Merged in feature/IO-3066-1-scaffolding (pull request #2)
Major v1 release.
This commit is contained in:
65
.circleci/config.yml
Normal file
65
.circleci/config.yml
Normal file
@@ -0,0 +1,65 @@
|
||||
version: 2.1
|
||||
orbs:
|
||||
win: circleci/windows@5.1.0
|
||||
jobs:
|
||||
partner-win-build:
|
||||
executor:
|
||||
name: win/default
|
||||
parameters:
|
||||
tenantID:
|
||||
type: string
|
||||
default: $AZURE_TENANT_ID
|
||||
clientID:
|
||||
type: string
|
||||
default: $AZURE_CLIENT_ID
|
||||
clientSecret:
|
||||
type: string
|
||||
default: $AZURE_CLIENT_SECRET
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Set up Node.js
|
||||
command: |
|
||||
nvm install 22
|
||||
nvm use 22
|
||||
- run:
|
||||
name: Install Build Dependencies
|
||||
command: |
|
||||
choco install python visualstudio2022-workload-vctools -y
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
|
||||
# look for existing cache and restore if found
|
||||
- restore_cache:
|
||||
key: bsp-win-deps-{{ checksum "package-lock.json" }}
|
||||
# install dependencies
|
||||
- run:
|
||||
name: install dependencies
|
||||
command: npm install
|
||||
- run:
|
||||
name: Install NPM Packages
|
||||
command: npm install
|
||||
- save_cache:
|
||||
key: bsp-win-deps-{{ checksum "package-lock.json" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- run:
|
||||
name: Build Electron App for Windows
|
||||
environment:
|
||||
AZURE_TENANT_ID: << parameters.tenantID >>
|
||||
AZURE_CLIENT_ID: << parameters.clientID >>
|
||||
AZURE_CLIENT_SECRET: << parameters.clientSecret >>
|
||||
command: |
|
||||
$env:AZURE_TENANT_ID = << parameters.tenantID >>
|
||||
$env:AZURE_CLIENT_ID = << parameters.clientID >>
|
||||
$env:AZURE_CLIENT_SECRET = << parameters.clientSecret >>
|
||||
npm run build:win
|
||||
# - store_artifacts:
|
||||
# path: .\dist
|
||||
|
||||
workflows:
|
||||
deploy_and_build:
|
||||
jobs:
|
||||
- partner-win-build:
|
||||
filters:
|
||||
branches:
|
||||
only: main
|
||||
14
.env.imex
Normal file
14
.env.imex
Normal file
@@ -0,0 +1,14 @@
|
||||
VITE_COMPANY=IMEX
|
||||
|
||||
# Fire Base Config
|
||||
VITE_FIREBASE_CONFIG={"apiKey":"AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU","authDomain":"imex-prod.firebaseapp.com","databaseURL":"https://imex-prod.firebaseio.com","projectId":"imex-prod","storageBucket":"imex-prod.appspot.com","messagingSenderId":"253497221485","appId":"1:253497221485:web:3c81c483b94db84b227a64","measurementId":"G-NTWBKG2L0M"}
|
||||
VITE_FIREBASE_CONFIG_TEST={ "apiKey":"AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c", "authDomain":"imex-test.firebaseapp.com", "projectId":"imex-test", "storageBucket":"imex-test.appspot.com", "messagingSenderId":"991923618608", "appId":"1:991923618608:web:633437569cdad78299bef5", "measurementId":"G-TW0XLZEH18"}
|
||||
# GraphQL Config
|
||||
VITE_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
|
||||
VITE_GRAPHQL_ENDPOINT_TEST=https://db.test.bodyshop.app/v1/graphql
|
||||
# Front End URL
|
||||
VITE_FE_URL=https://imex.online
|
||||
VITE_FE_URL_TEST=https://test.imex.online
|
||||
# API Url
|
||||
VITE_API_URL="https://api.imex.online"
|
||||
VITE_API_TEST_URL="https://test.api.imex.online"
|
||||
9
.env.local
Normal file
9
.env.local
Normal file
@@ -0,0 +1,9 @@
|
||||
VITE_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
|
||||
VITE_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
|
||||
VITE_FIREBASE_CONFIG_TEST={ "apiKey":"AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c", "authDomain":"imex-test.firebaseapp.com", "projectId":"imex-test", "storageBucket":"imex-test.appspot.com", "messagingSenderId":"991923618608", "appId":"1:991923618608:web:633437569cdad78299bef5", "measurementId":"G-TW0XLZEH18"}
|
||||
VITE_GRAPHQL_ENDPOINT_TEST=https://db.test.bodyshop.app/v1/graphql
|
||||
VITE_COMPANY=IMEX
|
||||
VITE_FE_URL=https://imex.online
|
||||
VITE_FE_URL_TEST=https://test.imex.online
|
||||
VITE_API_URL="http://localhost:4000"
|
||||
VITE_API_TEST_URL="https://api.test.imex.online"
|
||||
14
.env.rome
Normal file
14
.env.rome
Normal file
@@ -0,0 +1,14 @@
|
||||
VITE_COMPANY=ROME
|
||||
|
||||
# Fire Base Config
|
||||
VITE_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
|
||||
VITE_FIREBASE_CONFIG_TEST={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
|
||||
# GraphQL Config
|
||||
VITE_GRAPHQL_ENDPOINT=https://db.romeonline.io/v1/graphql
|
||||
VITE_GRAPHQL_ENDPOINT_TEST=https://db.test.romeonline.io/v1/graphql
|
||||
# Front End URL
|
||||
VITE_FE_URL=https://romeonline.io
|
||||
VITE_FE_URL_TEST=https://test.romeonline.io
|
||||
# API Url
|
||||
VITE_API_URL="https://api.romeonline.io"
|
||||
VITE_API_TEST_URL="https://test.api.romeonline.io"
|
||||
1
.eslintcache
Normal file
1
.eslintcache
Normal file
File diff suppressed because one or more lines are too long
15
.gitignore
vendored
15
.gitignore
vendored
@@ -2,4 +2,17 @@ node_modules
|
||||
dist
|
||||
out
|
||||
.DS_Store
|
||||
*.log*
|
||||
*.log*
|
||||
/logs
|
||||
|
||||
# Playwright
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
|
||||
# Build Files
|
||||
macbuild.sh
|
||||
# Sentry Config File
|
||||
.env.sentry-build-plugin
|
||||
|
||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
12
.idea/bodyshop-desktop.iml
generated
Normal file
12
.idea/bodyshop-desktop.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/jsLibraryMappings.xml
generated
Normal file
6
.idea/jsLibraryMappings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/material_theme_project_new.xml
generated
Normal file
12
.idea/material_theme_project_new.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MaterialThemeProjectNewConfig">
|
||||
<option name="metadata">
|
||||
<MTProjectMetadataState>
|
||||
<option name="migrated" value="true" />
|
||||
<option name="pristineConfig" value="false" />
|
||||
<option name="userId" value="-4002d172:18ee315e3ba:-7ffe" />
|
||||
</MTProjectMetadataState>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/bodyshop-desktop.iml" filepath="$PROJECT_DIR$/.idea/bodyshop-desktop.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/prettier.xml
generated
Normal file
6
.idea/prettier.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
65
.vscode/launch.json
vendored
Normal file
65
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Main Process",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
|
||||
},
|
||||
"runtimeArgs": ["--sourcemap"],
|
||||
"env": {
|
||||
"REMOTE_DEBUGGING_PORT": "9222"
|
||||
},
|
||||
"experimentalNetworking": "off"
|
||||
},
|
||||
{
|
||||
"name": "Debug Renderer Process",
|
||||
"port": 9222,
|
||||
"request": "attach",
|
||||
"type": "chrome",
|
||||
"webRoot": "${workspaceFolder}/src/renderer",
|
||||
"timeout": 60000,
|
||||
"presentation": {
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Debug Main Process w/ Hot Reloading",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd"
|
||||
},
|
||||
"runtimeArgs": ["--sourcemap", "--watch"],
|
||||
"env": {
|
||||
"REMOTE_DEBUGGING_PORT": "9222"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Debug All",
|
||||
"configurations": ["Debug Main Process", "Debug Renderer Process"],
|
||||
"presentation": {
|
||||
"order": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Debug All (Hot Reload)",
|
||||
"configurations": [
|
||||
"Debug Main Process w/ Hot Reloading",
|
||||
"Debug Renderer Process"
|
||||
],
|
||||
"presentation": {
|
||||
"order": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"prettier.trailingComma": "all",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
BIN
PartnerCert.p12
Normal file
BIN
PartnerCert.p12
Normal file
Binary file not shown.
44
README.md
44
README.md
@@ -1,34 +1,18 @@
|
||||
# bodyshop-desktop
|
||||
# Shop Partner
|
||||
An electron app that is replacing the existing Bodyshop Partner that was a C#/WPF Application.
|
||||
|
||||
An Electron application with React and TypeScript
|
||||
The purpose of this application is to:
|
||||
* Parse EMS files, and upload them to the IO back end.
|
||||
* Receive requests for EMS file parsing
|
||||
|
||||
The following functionality will be coming:
|
||||
* Interact with QuickBooks desktop (Windows Only)
|
||||
* Paint scale integrations
|
||||
* Parts Price Changes for CCC
|
||||
|
||||
## Recommended IDE Setup
|
||||
Toggling between the Production and Test servers can be done by pressing `CTRL/CMD + SHIFT + T`, and then going to the application menu, and enabling test. The application will restart automatically.
|
||||
|
||||
- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
|
||||
## Dev and Build Notes
|
||||
Unlike the main app, the dev mode will only connect to ImEX Online test data.
|
||||
|
||||
## Project Setup
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
$ npm run dev
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
# For windows
|
||||
$ npm run build:win
|
||||
|
||||
# For macOS
|
||||
$ npm run build:mac
|
||||
|
||||
# For Linux
|
||||
$ npm run build:linux
|
||||
```
|
||||
Building the app will require specifying the company to build for. Those details are captured in their respective ENV and YAML files.
|
||||
|
||||
BIN
_reference/ems/MPI_1/3698420.ENV
Normal file
BIN
_reference/ems/MPI_1/3698420.ENV
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.LIN
Normal file
BIN
_reference/ems/MPI_1/3698420.LIN
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.PFH
Normal file
BIN
_reference/ems/MPI_1/3698420.PFH
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.PFL
Normal file
BIN
_reference/ems/MPI_1/3698420.PFL
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.PFM
Normal file
BIN
_reference/ems/MPI_1/3698420.PFM
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.PFO
Normal file
BIN
_reference/ems/MPI_1/3698420.PFO
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.PFP
Normal file
BIN
_reference/ems/MPI_1/3698420.PFP
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.PFT
Normal file
BIN
_reference/ems/MPI_1/3698420.PFT
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.STL
Normal file
BIN
_reference/ems/MPI_1/3698420.STL
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.TTL
Normal file
BIN
_reference/ems/MPI_1/3698420.TTL
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420.VEN
Normal file
BIN
_reference/ems/MPI_1/3698420.VEN
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420A.AD1
Normal file
BIN
_reference/ems/MPI_1/3698420A.AD1
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420A.dbt
Normal file
BIN
_reference/ems/MPI_1/3698420A.dbt
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420B.AD2
Normal file
BIN
_reference/ems/MPI_1/3698420B.AD2
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420B.dbt
Normal file
BIN
_reference/ems/MPI_1/3698420B.dbt
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420V.VEH
Normal file
BIN
_reference/ems/MPI_1/3698420V.VEH
Normal file
Binary file not shown.
BIN
_reference/ems/MPI_1/3698420V.dbt
Normal file
BIN
_reference/ems/MPI_1/3698420V.dbt
Normal file
Binary file not shown.
17
build/com.convenient-brands.bodyshop-desktop.keepalive.plist
Normal file
17
build/com.convenient-brands.bodyshop-desktop.keepalive.plist
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>com.convenientbrands.bodyshop-desktop.keepalive</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>Shop Partner Keep Alive</string>
|
||||
<string>imexmedia://keep-alive</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StartInterval</key>
|
||||
<integer>${KEEP_ALIVE_INTERVAL_SECONDS}</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
build/icon.icns
BIN
build/icon.icns
Binary file not shown.
BIN
build/icon.ico
BIN
build/icon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 121 KiB |
BIN
build/icon.png
BIN
build/icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 320 KiB |
@@ -1,3 +1,3 @@
|
||||
provider: generic
|
||||
url: https://example.com/auto-updates
|
||||
updaterCacheDirName: bodyshop-desktop-updater
|
||||
provider: s3
|
||||
bucket: imex-partner
|
||||
region: ca-central-1
|
||||
|
||||
67
electron-builder.imex.yml
Normal file
67
electron-builder.imex.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
appId: com.convenientbrands.bodyshop-desktop-imex
|
||||
copyright: Convenient Brands, LLC.
|
||||
productName: ImEX Shop Partner
|
||||
generateUpdatesFilesForAllChannels: true
|
||||
|
||||
directories:
|
||||
buildResources: build
|
||||
files:
|
||||
- "!**/.vscode/*"
|
||||
- "!**/.idea/*"
|
||||
- "!src/*"
|
||||
- "!electron.vite.config.{js,ts,mjs,cjs}"
|
||||
- "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}"
|
||||
- "!{.env,.env.*,.npmrc,pnpm-lock.yaml}"
|
||||
- "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}"
|
||||
asarUnpack:
|
||||
- resources/**
|
||||
win:
|
||||
executableName: ShopPartner
|
||||
icon: resources/icon.png
|
||||
azureSignOptions:
|
||||
endpoint: https://eus.codesigning.azure.net
|
||||
certificateProfileName: ImEXRPS
|
||||
codeSigningAccountName: ImEX
|
||||
nsis:
|
||||
artifactName: ${name}-${arch}.${ext}
|
||||
shortcutName: ${productName}
|
||||
uninstallDisplayName: ${productName}
|
||||
createDesktopShortcut: always
|
||||
include: "scripts/installer.nsh" # Reference NSIS script from scripts directory
|
||||
mac:
|
||||
entitlementsInherit: build/entitlements.mac.plist
|
||||
category: public.app-category.business
|
||||
extendInfo:
|
||||
- NSCameraUsageDescription: Application requests access to the device's camera.
|
||||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
||||
- CFBundleURLTypes:
|
||||
- CFBundleTypeRole: Viewer # More specific role for protocol handling
|
||||
CFBundleURLName: com.convenientbrands.bodyshop-desktop-imex
|
||||
CFBundleURLSchemes:
|
||||
- imexmedia
|
||||
target:
|
||||
- target: default
|
||||
arch:
|
||||
- arm64
|
||||
- target: default
|
||||
arch:
|
||||
- x64
|
||||
dmg:
|
||||
artifactName: ${name}-${arch}.${ext}
|
||||
linux:
|
||||
target:
|
||||
- AppImage
|
||||
- snap
|
||||
- deb
|
||||
maintainer: electronjs.org
|
||||
category: Utility
|
||||
desktop: scripts/imex-shop-partner.desktop
|
||||
appImage:
|
||||
artifactName: ${name}-${arch}.${ext}
|
||||
npmRebuild: false
|
||||
publish:
|
||||
provider: s3
|
||||
bucket: imex-partner
|
||||
region: ca-central-1
|
||||
67
electron-builder.rome.yml
Normal file
67
electron-builder.rome.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
appId: com.convenientbrands.bodyshop-desktop-rome
|
||||
copyright: Convenient Brands, LLC.
|
||||
productName: Rome Shop Partner
|
||||
generateUpdatesFilesForAllChannels: true
|
||||
|
||||
directories:
|
||||
buildResources: build
|
||||
files:
|
||||
- "!**/.vscode/*"
|
||||
- "!**/.idea/*"
|
||||
- "!src/*"
|
||||
- "!electron.vite.config.{js,ts,mjs,cjs}"
|
||||
- "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}"
|
||||
- "!{.env,.env.*,.npmrc,pnpm-lock.yaml}"
|
||||
- "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}"
|
||||
asarUnpack:
|
||||
- resources/**
|
||||
win:
|
||||
executableName: ShopPartner
|
||||
icon: resources/icon.png
|
||||
azureSignOptions:
|
||||
endpoint: https://eus.codesigning.azure.net
|
||||
certificateProfileName: ImEXRPS
|
||||
codeSigningAccountName: ImEX
|
||||
nsis:
|
||||
artifactName: ${name}-${arch}.${ext}
|
||||
shortcutName: ${productName}
|
||||
uninstallDisplayName: ${productName}
|
||||
createDesktopShortcut: always
|
||||
include: "scripts/installer.nsh" # Reference NSIS script from scripts directory
|
||||
mac:
|
||||
entitlementsInherit: build/entitlements.mac.plist
|
||||
category: public.app-category.business
|
||||
extendInfo:
|
||||
- NSCameraUsageDescription: Application requests access to the device's camera.
|
||||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
||||
- CFBundleURLTypes:
|
||||
- CFBundleTypeRole: Viewer # More specific role for protocol handling
|
||||
CFBundleURLName: com.convenientbrands.bodyshop-desktop-rome
|
||||
CFBundleURLSchemes:
|
||||
- imexmedia
|
||||
target:
|
||||
- target: default
|
||||
arch:
|
||||
- arm64
|
||||
- target: default
|
||||
arch:
|
||||
- x64
|
||||
dmg:
|
||||
artifactName: ${name}-${arch}.${ext}
|
||||
linux:
|
||||
target:
|
||||
- AppImage
|
||||
- snap
|
||||
- deb
|
||||
maintainer: electronjs.org
|
||||
category: Utility
|
||||
desktop: scripts/rome-shop-partner.desktop
|
||||
appImage:
|
||||
artifactName: ${name}-${arch}.${ext}
|
||||
npmRebuild: false
|
||||
publish:
|
||||
provider: s3
|
||||
bucket: rome-partner
|
||||
region: us-east-2
|
||||
@@ -1,45 +0,0 @@
|
||||
appId: com.convenientbrands.bodyshop-desktop
|
||||
productName: Shop Partner
|
||||
directories:
|
||||
buildResources: build
|
||||
files:
|
||||
- '!**/.vscode/*'
|
||||
- '!src/*'
|
||||
- '!electron.vite.config.{js,ts,mjs,cjs}'
|
||||
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
|
||||
- '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
|
||||
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
|
||||
asarUnpack:
|
||||
- resources/**
|
||||
win:
|
||||
executableName: bodyshop-desktop
|
||||
nsis:
|
||||
artifactName: ${name}-${version}-setup.${ext}
|
||||
shortcutName: ${productName}
|
||||
uninstallDisplayName: ${productName}
|
||||
createDesktopShortcut: always
|
||||
mac:
|
||||
entitlementsInherit: build/entitlements.mac.plist
|
||||
extendInfo:
|
||||
- NSCameraUsageDescription: Application requests access to the device's camera.
|
||||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone.
|
||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
||||
notarize: false
|
||||
dmg:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
linux:
|
||||
target:
|
||||
- AppImage
|
||||
- snap
|
||||
- deb
|
||||
maintainer: electronjs.org
|
||||
category: Utility
|
||||
appImage:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
npmRebuild: false
|
||||
publish:
|
||||
provider: generic
|
||||
url: https://example.com/auto-updates
|
||||
electronDownload:
|
||||
mirror: https://npmmirror.com/mirrors/electron/
|
||||
@@ -1,20 +1,41 @@
|
||||
import { resolve } from 'path'
|
||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
||||
import { resolve } from "path";
|
||||
import { defineConfig, externalizeDepsPlugin } from "electron-vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
|
||||
export default defineConfig({
|
||||
main: {
|
||||
plugins: [externalizeDepsPlugin()]
|
||||
plugins: [
|
||||
externalizeDepsPlugin(),
|
||||
sentryVitePlugin({
|
||||
org: "imex",
|
||||
project: "imex-partner",
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
sourcemap: true,
|
||||
},
|
||||
},
|
||||
|
||||
preload: {
|
||||
plugins: [externalizeDepsPlugin()]
|
||||
plugins: [externalizeDepsPlugin()],
|
||||
},
|
||||
|
||||
renderer: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@renderer': resolve('src/renderer/src')
|
||||
}
|
||||
"@renderer": resolve("src/renderer/src"),
|
||||
},
|
||||
},
|
||||
plugins: [react()]
|
||||
}
|
||||
})
|
||||
plugins: [
|
||||
react(),
|
||||
sentryVitePlugin({
|
||||
org: "imex",
|
||||
project: "imex-partner",
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
sourcemap: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,31 +1,39 @@
|
||||
import tseslint from '@electron-toolkit/eslint-config-ts'
|
||||
import eslintConfigPrettier from '@electron-toolkit/eslint-config-prettier'
|
||||
import eslintPluginReact from 'eslint-plugin-react'
|
||||
import eslintPluginReactHooks from 'eslint-plugin-react-hooks'
|
||||
import eslintPluginReactRefresh from 'eslint-plugin-react-refresh'
|
||||
import eslintConfigPrettier from "@electron-toolkit/eslint-config-prettier";
|
||||
import tseslint from "@electron-toolkit/eslint-config-ts";
|
||||
import eslintPluginReact from "eslint-plugin-react";
|
||||
import eslintPluginReactHooks from "eslint-plugin-react-hooks";
|
||||
import eslintPluginReactRefresh from "eslint-plugin-react-refresh";
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['**/node_modules', '**/dist', '**/out'] },
|
||||
tseslint.configs.recommended,
|
||||
eslintPluginReact.configs.flat.recommended,
|
||||
eslintPluginReact.configs.flat['jsx-runtime'],
|
||||
{
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
plugins: {
|
||||
'react-hooks': eslintPluginReactHooks,
|
||||
'react-refresh': eslintPluginReactRefresh
|
||||
{ignores: ["**/node_modules", "**/dist", "**/out"]},
|
||||
tseslint.configs.recommended,
|
||||
eslintPluginReact.configs.flat.recommended,
|
||||
eslintPluginReact.configs.flat["jsx-runtime"],
|
||||
{
|
||||
settings: {
|
||||
react: {
|
||||
version: "detect",
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...eslintPluginReactHooks.configs.recommended.rules,
|
||||
...eslintPluginReactRefresh.configs.vite.rules
|
||||
}
|
||||
},
|
||||
eslintConfigPrettier
|
||||
{
|
||||
files: ["**/*.{ts,tsx}"],
|
||||
plugins: {
|
||||
"react-hooks": eslintPluginReactHooks,
|
||||
"react-refresh": eslintPluginReactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...eslintPluginReactHooks.configs.recommended.rules,
|
||||
...eslintPluginReactRefresh.configs.vite.rules,
|
||||
},
|
||||
|
||||
},
|
||||
{
|
||||
files: ["**/*.{js,mjs,ts,tsx,jsx,tsx}"],
|
||||
rules: {
|
||||
"prettier/prettier": ["error", {"endOfLine": "off"}]
|
||||
}
|
||||
},
|
||||
eslintConfigPrettier,
|
||||
)
|
||||
|
||||
|
||||
7770
package-lock.json
generated
7770
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
82
package.json
82
package.json
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "bodyshop-desktop",
|
||||
"version": "1.0.0",
|
||||
"description": "An Electron application with React and TypeScript",
|
||||
"description": "Shop Management System Partner",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "example.com",
|
||||
"homepage": "https://electron-vite.org",
|
||||
"author": "Convenient Brands, LLC",
|
||||
"homepage": "https://convenient-brands.com",
|
||||
"scripts": {
|
||||
"format": "prettier --write .",
|
||||
"lint": "eslint --cache .",
|
||||
@@ -13,37 +13,79 @@
|
||||
"typecheck": "npm run typecheck:node && npm run typecheck:web",
|
||||
"start": "electron-vite preview",
|
||||
"dev": "electron-vite dev",
|
||||
"build": "npm run typecheck && electron-vite build",
|
||||
"build:imex": "electron-vite build --mode imex && electron-builder --config electron-builder.imex.yml",
|
||||
"build:rome": "electron-vite build --mode rome && electron-builder --config electron-builder.rome.yml",
|
||||
"build:imex:publish": "electron-vite build --mode imex && electron-builder --config electron-builder.imex.yml --publish always",
|
||||
"build:rome:publish": "electron-vite build --mode rome && electron-builder --config electron-builder.rome.yml --publish always",
|
||||
"build:imex:linux": "electron-vite build --mode imex && electron-builder --config electron-builder.imex.yml --linux",
|
||||
"build:rome:linux": "electron-vite build --mode rome && electron-builder --config electron-builder.rome.yml --linux",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"build:unpack": "npm run build && electron-builder --dir",
|
||||
"build:win": "npm run build && electron-builder --win",
|
||||
"build:mac": "electron-vite build && electron-builder --mac",
|
||||
"build:linux": "electron-vite build && electron-builder --linux"
|
||||
"build:unpack": "electron-vite build --mode imex && electron-builder --dir",
|
||||
"build:win": "electron-vite build --mode imex && electron-builder --win",
|
||||
"build:mac": "electron-vite build --mode imex && electron-builder --mac",
|
||||
"build:linux": "electron-vite build --mode imex && electron-builder --linux"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.13.6",
|
||||
"@electron-toolkit/preload": "^3.0.1",
|
||||
"@electron-toolkit/utils": "^4.0.0",
|
||||
"electron-updater": "^6.3.9"
|
||||
"@sentry/electron": "^6.5.0",
|
||||
"@sentry/vite-plugin": "^3.3.1",
|
||||
"axios": "^1.9.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"electron-log": "^5.3.3",
|
||||
"electron-store": "^8.2.0",
|
||||
"electron-updater": "^6.6.2",
|
||||
"winax": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ant-design/v5-patch-for-react-19": "^1.0.3",
|
||||
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
|
||||
"@electron-toolkit/eslint-config-ts": "^3.0.0",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@reduxjs/toolkit": "^2.6.1",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/node": "^22.14.0",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@types/xml2js": "^0.4.14",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"electron": "^35.0.1",
|
||||
"antd": "^5.24.6",
|
||||
"archiver": "^7.0.1",
|
||||
"chokidar": "^4.0.3",
|
||||
"cors": "^2.8.5",
|
||||
"dbffile": "^1.12.0",
|
||||
"electron": "^35.1.5",
|
||||
"electron-builder": "^25.1.8",
|
||||
"electron-vite": "^3.0.0",
|
||||
"eslint": "^9.22.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"electron-store": "^8.2.0",
|
||||
"electron-vite": "^3.1.0",
|
||||
"eslint": "^9.24.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"express": "^5.1.0",
|
||||
"firebase": "^11.6.0",
|
||||
"graphql": "^16.10.0",
|
||||
"graphql-request": "^7.1.2",
|
||||
"i18next": "^24.2.3",
|
||||
"lodash": "^4.17.21",
|
||||
"node-cron": "^3.0.3",
|
||||
"playwright": "^1.51.1",
|
||||
"prettier": "^3.5.3",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.1"
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-error-boundary": "^5.0.0",
|
||||
"react-i18next": "^15.4.1",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router": "^7.5.0",
|
||||
"redux-logger": "^3.0.6",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "6.2.6",
|
||||
"xml2js": "^0.6.2",
|
||||
"xmlbuilder2": "^3.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
80
playwright.config.ts
Normal file
80
playwright.config.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// import dotenv from 'dotenv';
|
||||
// import path from 'path';
|
||||
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
//testDir: './tests/**/*',
|
||||
//testMatch: '**/*.test.ts',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: "html",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
// baseURL: 'http://127.0.0.1:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: "on-first-retry",
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "firefox",
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
},
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: { ...devices['iPhone 12'] },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
// command: "npm run start",
|
||||
// url: "http://127.0.0.1:3000",
|
||||
// reuseExistingServer: !process.env.CI,
|
||||
// },
|
||||
});
|
||||
BIN
resources/Interop.QBFC16.dll
Normal file
BIN
resources/Interop.QBFC16.dll
Normal file
Binary file not shown.
BIN
resources/Interop.QBXMLRP2.dll
Normal file
BIN
resources/Interop.QBXMLRP2.dll
Normal file
Binary file not shown.
BIN
resources/QBFC16.dll
Normal file
BIN
resources/QBFC16.dll
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 320 KiB |
8
scripts/imex-shop-partner.desktop
Normal file
8
scripts/imex-shop-partner.desktop
Normal file
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Name=ImEX Shop Partner
|
||||
Exec=/opt/ImEX%20Shop%20Partner/ShopPartner %u
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Icon=imex-shop-partner
|
||||
Categories=Utility;
|
||||
MimeType=x-scheme-handler/imexmedia;
|
||||
13
scripts/installer.nsh
Normal file
13
scripts/installer.nsh
Normal file
@@ -0,0 +1,13 @@
|
||||
!macro customInstall
|
||||
; Register imexmedia protocol
|
||||
WriteRegStr HKCR "imexmedia" "" "URL:ImEX Shop Partner Protocol"
|
||||
WriteRegStr HKCR "imexmedia" "URL Protocol" ""
|
||||
WriteRegStr HKCR "imexmedia\\shell" "" ""
|
||||
WriteRegStr HKCR "imexmedia\\shell\\open" "" ""
|
||||
WriteRegStr HKCR "imexmedia\\shell\\open\\command" "" '"$INSTDIR\ShopPartner.exe" "%1"'
|
||||
!macroend
|
||||
|
||||
!macro customUnInstall
|
||||
; Remove imexmedia protocol
|
||||
DeleteRegKey HKCR "imexmedia"
|
||||
!macroend
|
||||
8
scripts/rome-shop-partner.desktop
Normal file
8
scripts/rome-shop-partner.desktop
Normal file
@@ -0,0 +1,8 @@
|
||||
[Desktop Entry]
|
||||
Name=Rome Shop Partner
|
||||
Exec=/opt/Rome%20Shop%20Partner/ShopPartner %u
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Icon=rome-shop-partner
|
||||
Categories=Utility;
|
||||
MimeType=x-scheme-handler/imexmedia;
|
||||
12
src/env.d.ts
vendored
Normal file
12
src/env.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
export interface ImportMetaEnv {
|
||||
readonly VITE_FIREBASE_CONFIG: string;
|
||||
readonly VITE_GRAPHQL_ENDPOINT: string;
|
||||
readonly VITE_FIREBASE_CONFIG_TEST: string;
|
||||
readonly VITE_GRAPHQL_ENDPOINT_TEST: string;
|
||||
}
|
||||
|
||||
export interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
149
src/main/decoder/decode-ad1.interface.ts
Normal file
149
src/main/decoder/decode-ad1.interface.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
import { UUID } from "crypto";
|
||||
|
||||
export interface DecodedAd1 {
|
||||
// Insurance company information
|
||||
ins_co_id?: string;
|
||||
ins_co_nm?: string;
|
||||
ins_addr1?: string;
|
||||
ins_addr2?: string;
|
||||
ins_city?: string;
|
||||
ins_st?: string;
|
||||
ins_zip?: string;
|
||||
ins_ctry?: string;
|
||||
ins_ea?: string;
|
||||
ins_ph1?: string;
|
||||
ins_ph1x?: string;
|
||||
ins_ph2?: string;
|
||||
ins_ph2x?: string;
|
||||
ins_fax?: string;
|
||||
ins_faxx?: string;
|
||||
ins_ct_ln?: string;
|
||||
ins_ct_fn?: string;
|
||||
ins_title?: string;
|
||||
ins_ct_ph?: string;
|
||||
ins_ct_phx?: string;
|
||||
|
||||
// Policy information
|
||||
policy_no?: string;
|
||||
ded_amt?: string;
|
||||
ded_status?: string;
|
||||
asgn_no?: string;
|
||||
asgn_date?: Date | string;
|
||||
asgn_type?: string;
|
||||
|
||||
// Claim information
|
||||
clm_no?: string;
|
||||
clm_ofc_id?: string;
|
||||
clm_ofc_nm?: string;
|
||||
clm_addr1?: string;
|
||||
clm_addr2?: string;
|
||||
clm_city?: string;
|
||||
clm_st?: string;
|
||||
clm_zip?: string;
|
||||
clm_ctry?: string;
|
||||
clm_ph1?: string;
|
||||
clm_ph1x?: string;
|
||||
clm_ph2?: string;
|
||||
clm_ph2x?: string;
|
||||
clm_fax?: string;
|
||||
clm_faxx?: string;
|
||||
clm_ct_ln?: string;
|
||||
clm_ct_fn?: string;
|
||||
clm_title?: string;
|
||||
clm_ct_ph?: string;
|
||||
clm_ct_phx?: string;
|
||||
clm_ea?: string;
|
||||
|
||||
// Payment information
|
||||
payee_nms?: string;
|
||||
pay_type?: string;
|
||||
pay_date?: string;
|
||||
pay_chknm?: string;
|
||||
pay_amt?: string;
|
||||
|
||||
// Agent information
|
||||
agt_co_id?: string;
|
||||
agt_co_nm?: string;
|
||||
agt_addr1?: string;
|
||||
agt_addr2?: string;
|
||||
agt_city?: string;
|
||||
agt_st?: string;
|
||||
agt_zip?: string;
|
||||
agt_ctry?: string;
|
||||
agt_ph1?: string;
|
||||
agt_ph1x?: string;
|
||||
agt_ph2?: string;
|
||||
agt_ph2x?: string;
|
||||
agt_fax?: string;
|
||||
agt_faxx?: string;
|
||||
agt_ct_ln?: string;
|
||||
agt_ct_fn?: string;
|
||||
agt_ct_ph?: string;
|
||||
agt_ct_phx?: string;
|
||||
agt_ea?: string;
|
||||
agt_lic_no?: string;
|
||||
|
||||
// Loss information
|
||||
loss_date?: string;
|
||||
loss_type?: string;
|
||||
loss_desc?: string;
|
||||
theft_ind?: string;
|
||||
cat_no?: string;
|
||||
tlos_ind?: string;
|
||||
cust_pr?: string;
|
||||
loss_cat?: string;
|
||||
|
||||
// Insured information
|
||||
insd_ln?: string;
|
||||
insd_fn?: string;
|
||||
insd_title?: string;
|
||||
insd_co_nm?: string;
|
||||
insd_addr1?: string;
|
||||
insd_addr2?: string;
|
||||
insd_city?: string;
|
||||
insd_st?: string;
|
||||
insd_zip?: string;
|
||||
insd_ctry?: string;
|
||||
insd_ph1?: string;
|
||||
insd_ph2?: string;
|
||||
insd_fax?: string;
|
||||
insd_faxx?: string;
|
||||
insd_ea?: string;
|
||||
|
||||
// Owner information
|
||||
ownr_ln?: string;
|
||||
ownr_fn?: string;
|
||||
ownr_title?: string;
|
||||
ownr_co_nm?: string;
|
||||
ownr_addr1?: string;
|
||||
ownr_addr2?: string;
|
||||
ownr_city?: string;
|
||||
ownr_st?: string;
|
||||
ownr_zip?: string;
|
||||
ownr_ctry?: string;
|
||||
ownr_ph1?: string;
|
||||
ownr_ph2?: string;
|
||||
ownr_ea?: string;
|
||||
|
||||
// Owner data object - referenced in the code
|
||||
owner: {
|
||||
data: OwnerRecordInterface;
|
||||
};
|
||||
}
|
||||
|
||||
export interface OwnerRecordInterface {
|
||||
ownr_ln?: string;
|
||||
ownr_fn?: string;
|
||||
ownr_title?: string;
|
||||
ownr_co_nm?: string;
|
||||
ownr_addr1?: string;
|
||||
ownr_addr2?: string;
|
||||
ownr_city?: string;
|
||||
ownr_st?: string;
|
||||
ownr_zip?: string;
|
||||
ownr_ctry?: string;
|
||||
ownr_ph1?: string;
|
||||
ownr_ph2?: string;
|
||||
ownr_ea?: string;
|
||||
shopid: UUID;
|
||||
}
|
||||
235
src/main/decoder/decode-ad1.ts
Normal file
235
src/main/decoder/decode-ad1.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import store from "../store/store";
|
||||
import { DecodedAd1, OwnerRecordInterface } from "./decode-ad1.interface";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodeAD1 = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedAd1> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}A.AD1`);
|
||||
} catch {
|
||||
// log.debug("Error opening AD1 File.", errorTypeCheck(error));
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.AD1`);
|
||||
// log.debug("Trying to find AD1 file using regular CIECA Id.");
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any AD1 files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any AD1 files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = ["a.ad1", ".ad1"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any AD1 files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any AD1 files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening AD1 File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const rawDBFRecord = await dbf.readRecords(1);
|
||||
|
||||
//AD1 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
const rawAd1Data: DecodedAd1 = deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"INS_CO_ID",
|
||||
"INS_CO_NM",
|
||||
"INS_ADDR1",
|
||||
"INS_ADDR2",
|
||||
"INS_CITY",
|
||||
"INS_ST",
|
||||
"INS_ZIP",
|
||||
"INS_CTRY",
|
||||
"INS_EA",
|
||||
"POLICY_NO",
|
||||
"DED_AMT",
|
||||
"DED_STATUS",
|
||||
"ASGN_NO",
|
||||
"ASGN_DATE",
|
||||
"ASGN_TYPE",
|
||||
"CLM_NO",
|
||||
"CLM_OFC_ID",
|
||||
"CLM_OFC_NM",
|
||||
"CLM_ADDR1",
|
||||
"CLM_ADDR2",
|
||||
"CLM_CITY",
|
||||
"CLM_ST",
|
||||
"CLM_ZIP",
|
||||
"CLM_CTRY",
|
||||
"CLM_PH1",
|
||||
"CLM_PH1X",
|
||||
"CLM_PH2",
|
||||
"CLM_PH2X",
|
||||
"CLM_FAX",
|
||||
"CLM_FAXX",
|
||||
"CLM_CT_LN",
|
||||
"CLM_CT_FN",
|
||||
"CLM_TITLE",
|
||||
"CLM_CT_PH",
|
||||
"CLM_CT_PHX",
|
||||
"CLM_EA",
|
||||
"PAYEE_NMS",
|
||||
"PAY_TYPE",
|
||||
"PAY_DATE",
|
||||
"PAY_CHKNM",
|
||||
"PAY_AMT",
|
||||
"AGT_CO_ID",
|
||||
"AGT_CO_NM",
|
||||
"AGT_ADDR1",
|
||||
"AGT_ADDR2",
|
||||
"AGT_CITY",
|
||||
"AGT_ST",
|
||||
"AGT_ZIP",
|
||||
"AGT_CTRY",
|
||||
"AGT_PH1",
|
||||
"AGT_PH1X",
|
||||
"AGT_PH2",
|
||||
"AGT_PH2X",
|
||||
"AGT_FAX",
|
||||
"AGT_FAXX",
|
||||
"AGT_CT_LN",
|
||||
"AGT_CT_FN",
|
||||
"AGT_CT_PH",
|
||||
"AGT_CT_PHX",
|
||||
"AGT_EA",
|
||||
"AGT_LIC_NO",
|
||||
"LOSS_DATE",
|
||||
"LOSS_TYPE",
|
||||
"LOSS_DESC",
|
||||
"THEFT_IND",
|
||||
"CAT_NO",
|
||||
"TLOS_IND",
|
||||
"CUST_PR",
|
||||
"INSD_LN",
|
||||
"INSD_FN",
|
||||
"INSD_TITLE",
|
||||
"INSD_CO_NM",
|
||||
"INSD_ADDR1",
|
||||
"INSD_ADDR2",
|
||||
"INSD_CITY",
|
||||
"INSD_ST",
|
||||
"INSD_ZIP",
|
||||
"INSD_CTRY",
|
||||
"INSD_PH1",
|
||||
//"INSD_PH1X",
|
||||
"INSD_PH2",
|
||||
//"INSD_PH2X",
|
||||
"INSD_FAX",
|
||||
"INSD_FAXX",
|
||||
"INSD_EA",
|
||||
"OWNR_LN",
|
||||
"OWNR_FN",
|
||||
"OWNR_TITLE",
|
||||
"OWNR_CO_NM",
|
||||
"OWNR_ADDR1",
|
||||
"OWNR_ADDR2",
|
||||
"OWNR_CITY",
|
||||
"OWNR_ST",
|
||||
"OWNR_ZIP",
|
||||
"OWNR_CTRY",
|
||||
"OWNR_PH1",
|
||||
//"OWNR_PH1X",
|
||||
"OWNR_PH2",
|
||||
//"OWNR_PH2X",
|
||||
//"OWNR_FAX",
|
||||
//"OWNR_FAXX",
|
||||
"OWNR_EA",
|
||||
"INS_PH1",
|
||||
"INS_PH1X",
|
||||
"INS_PH2",
|
||||
"INS_PH2X",
|
||||
"INS_FAX",
|
||||
"INS_FAXX",
|
||||
"INS_CT_LN",
|
||||
"INS_CT_FN",
|
||||
"INS_TITLE",
|
||||
"INS_CT_PH",
|
||||
"INS_CT_PHX",
|
||||
"LOSS_CAT",
|
||||
]),
|
||||
);
|
||||
|
||||
//Copy specific logic for manipulation.
|
||||
//If ownr_ph1 is missing, use ownr_ph2
|
||||
if (rawAd1Data.asgn_date) {
|
||||
const newAsgnDate = new Date(rawAd1Data.asgn_date);
|
||||
rawAd1Data.asgn_date = newAsgnDate.toISOString().split("T")[0];
|
||||
}
|
||||
|
||||
if (!rawAd1Data.ownr_ph1 || _.isEmpty(rawAd1Data.ownr_ph1)) {
|
||||
rawAd1Data.ownr_ph1 = rawAd1Data.ownr_ph2;
|
||||
}
|
||||
|
||||
let ownerRecord: OwnerRecordInterface;
|
||||
//Check if the owner information is there. If not, use the insured information as a fallback.
|
||||
if (
|
||||
_.isEmpty(rawAd1Data.ownr_ln) &&
|
||||
_.isEmpty(rawAd1Data.ownr_fn) &&
|
||||
_.isEmpty(rawAd1Data.ownr_co_nm)
|
||||
) {
|
||||
//They're all empty. Using the insured information as a fallback.
|
||||
// Build up the owner record to insert it alongside the job.
|
||||
//TODO: Verify that this should be the insured, and not the claimant.
|
||||
ownerRecord = {
|
||||
ownr_ln: rawAd1Data.insd_ln,
|
||||
ownr_fn: rawAd1Data.insd_fn,
|
||||
ownr_title: rawAd1Data.insd_title,
|
||||
ownr_co_nm: rawAd1Data.insd_co_nm,
|
||||
ownr_addr1: rawAd1Data.insd_addr1,
|
||||
ownr_addr2: rawAd1Data.insd_addr2,
|
||||
ownr_city: rawAd1Data.insd_city,
|
||||
ownr_st: rawAd1Data.insd_st,
|
||||
ownr_zip: rawAd1Data.insd_zip,
|
||||
ownr_ctry: rawAd1Data.insd_ctry,
|
||||
ownr_ph1: rawAd1Data.insd_ph1,
|
||||
ownr_ph2: rawAd1Data.insd_ph2,
|
||||
ownr_ea: rawAd1Data.insd_ea,
|
||||
shopid: store.get("app.bodyshop.id"),
|
||||
};
|
||||
} else {
|
||||
//Use the owner information.
|
||||
ownerRecord = {
|
||||
ownr_ln: rawAd1Data.ownr_ln,
|
||||
ownr_fn: rawAd1Data.ownr_fn,
|
||||
ownr_title: rawAd1Data.ownr_title,
|
||||
ownr_co_nm: rawAd1Data.ownr_co_nm,
|
||||
ownr_addr1: rawAd1Data.ownr_addr1,
|
||||
ownr_addr2: rawAd1Data.ownr_addr2,
|
||||
ownr_city: rawAd1Data.ownr_city,
|
||||
ownr_st: rawAd1Data.ownr_st,
|
||||
ownr_zip: rawAd1Data.ownr_zip,
|
||||
ownr_ctry: rawAd1Data.ownr_ctry,
|
||||
ownr_ph1: rawAd1Data.ownr_ph1,
|
||||
ownr_ph2: rawAd1Data.ownr_ph2,
|
||||
ownr_ea: rawAd1Data.ownr_ea,
|
||||
shopid: store.get("app.bodyshop.id"),
|
||||
};
|
||||
}
|
||||
|
||||
return { ...rawAd1Data, owner: { data: ownerRecord } };
|
||||
};
|
||||
|
||||
export default DecodeAD1;
|
||||
29
src/main/decoder/decode-ad2.interface.ts
Normal file
29
src/main/decoder/decode-ad2.interface.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
export interface DecodedAD2 {
|
||||
clmt_ln?: string;
|
||||
clmt_fn?: string;
|
||||
clmt_title?: string;
|
||||
clmt_co_nm?: string;
|
||||
clmt_addr1?: string;
|
||||
clmt_addr2?: string;
|
||||
clmt_city?: string;
|
||||
clmt_st?: string;
|
||||
clmt_zip?: string;
|
||||
clmt_ctry?: string;
|
||||
clmt_ph1?: string;
|
||||
clmt_ph2?: string;
|
||||
clmt_ea?: string;
|
||||
est_co_id?: string;
|
||||
est_co_nm?: string;
|
||||
est_addr1?: string;
|
||||
est_addr2?: string;
|
||||
est_city?: string;
|
||||
est_st?: string;
|
||||
est_zip?: string;
|
||||
est_ctry?: string;
|
||||
est_ph1?: string;
|
||||
est_ct_ln?: string;
|
||||
est_ct_fn?: string;
|
||||
est_ea?: string;
|
||||
date_estimated?: Date; // This is transformed from insp_date
|
||||
insp_date?: Date; // This exists initially but gets deleted
|
||||
}
|
||||
170
src/main/decoder/decode-ad2.ts
Normal file
170
src/main/decoder/decode-ad2.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import { DecodedAD2 } from "./decode-ad2.interface";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodeAD2 = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedAD2> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}B.AD2`);
|
||||
} catch {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.AD2`);
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any AD2 files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any AD2 files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = ["b.ad2", ".ad2"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any AD2 files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any AD2 files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening AD2 File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const rawDBFRecord = await dbf.readRecords(1);
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const rawAd2Data: DecodedAD2 = deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"CLMT_LN", //TODO: This claimant info shouldnt be passed back. Just for the owner info.
|
||||
"CLMT_FN",
|
||||
"CLMT_TITLE",
|
||||
"CLMT_CO_NM",
|
||||
"CLMT_ADDR1",
|
||||
"CLMT_ADDR2",
|
||||
"CLMT_CITY",
|
||||
"CLMT_ST",
|
||||
"CLMT_ZIP",
|
||||
"CLMT_CTRY",
|
||||
"CLMT_PH1",
|
||||
//"CLMT_PH1X",
|
||||
"CLMT_PH2",
|
||||
//"CLMT_PH2X",
|
||||
//"CLMT_FAX",
|
||||
//"CLMT_FAXX",
|
||||
"CLMT_EA",
|
||||
//"EST_CO_ID",
|
||||
"EST_CO_NM",
|
||||
"EST_ADDR1",
|
||||
"EST_ADDR2",
|
||||
"EST_CITY",
|
||||
"EST_ST",
|
||||
"EST_ZIP",
|
||||
"EST_CTRY",
|
||||
"EST_PH1",
|
||||
//"EST_PH1X",
|
||||
//"EST_PH2",
|
||||
//"EST_PH2X",
|
||||
//"EST_FAX",
|
||||
//"EST_FAXX",
|
||||
"EST_CT_LN",
|
||||
"EST_CT_FN",
|
||||
"EST_EA",
|
||||
//"EST_LIC_NO",
|
||||
//"EST_FILENO",
|
||||
//"INSP_CT_LN",
|
||||
//"INSP_CT_FN",
|
||||
//"INSP_ADDR1",
|
||||
//"INSP_ADDR2",
|
||||
//"INSP_CITY",
|
||||
//"INSP_ST",
|
||||
//"INSP_ZIP",
|
||||
//"INSP_CTRY",
|
||||
//"INSP_PH1",
|
||||
//"INSP_PH1X",
|
||||
//"INSP_PH2",
|
||||
//"INSP_PH2X",
|
||||
//"INSP_FAX",
|
||||
//"INSP_FAXX",
|
||||
//"INSP_EA",
|
||||
//"INSP_CODE",
|
||||
//"INSP_DESC",
|
||||
"INSP_DATE", //RENAME TO date_estimated
|
||||
//"INSP_TIME",
|
||||
//"RF_CO_ID",
|
||||
//"RF_CO_NM",
|
||||
//"RF_ADDR1",
|
||||
//"RF_ADDR2",
|
||||
//"RF_CITY",
|
||||
//"RF_ST",
|
||||
//"RF_ZIP",
|
||||
//"RF_CTRY",
|
||||
//"RF_PH1",
|
||||
//"RF_PH1X",
|
||||
//"RF_PH2",
|
||||
//"RF_PH2X",
|
||||
//"RF_FAX",
|
||||
//"RF_FAXX",
|
||||
//"RF_CT_LN",
|
||||
//"RF_CT_FN",
|
||||
//"RF_EA",
|
||||
//"RF_TAX_ID",
|
||||
//"RF_LIC_NO",
|
||||
//"RF_BAR_NO",
|
||||
//"RO_IN_DATE",
|
||||
//"RO_IN_TIME",
|
||||
//"TAR_DATE",
|
||||
//"TAR_TIME",
|
||||
//"RO_CMPDATE",
|
||||
//"RO_CMPTIME",
|
||||
//"DATE_OUT",
|
||||
//"TIME_OUT",
|
||||
//"RF_ESTIMTR",
|
||||
//"MKTG_TYPE",
|
||||
//"MKTG_SRC",
|
||||
//"LOC_NM",
|
||||
//"LOC_ADDR1",
|
||||
//"LOC_ADDR2",
|
||||
//"LOC_CITY",
|
||||
//"LOC_ST",
|
||||
//"LOC_ZIP",
|
||||
//"LOC_CTRY",
|
||||
//"LOC_PH1",
|
||||
//"LOC_PH1X",
|
||||
//"LOC_PH2",
|
||||
//"LOC_PH2X",
|
||||
//"LOC_FAX",
|
||||
//"LOC_FAXX",
|
||||
//"LOC_CT_LN",
|
||||
//"LOC_CT_FN",
|
||||
//"LOC_TITLE",
|
||||
//"LOC_PH",
|
||||
//"LOC_PHX",
|
||||
//"LOC_EA",
|
||||
]),
|
||||
);
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
//We don't have an inspection date, we instead have `date_estimated`
|
||||
rawAd2Data.date_estimated = rawAd2Data.insp_date;
|
||||
delete rawAd2Data.insp_date;
|
||||
|
||||
return rawAd2Data;
|
||||
};
|
||||
export default DecodeAD2;
|
||||
5
src/main/decoder/decode-env.interface.ts
Normal file
5
src/main/decoder/decode-env.interface.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface DecodedEnv {
|
||||
est_system?: string;
|
||||
estfile_id?: string;
|
||||
ciecaid?: string;
|
||||
}
|
||||
68
src/main/decoder/decode-env.ts
Normal file
68
src/main/decoder/decode-env.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedEnv } from "./decode-env.interface";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodeEnv = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedEnv> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.ENV`);
|
||||
} catch (error) {
|
||||
log.error("Error opening ENV File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any ENV files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any ENV files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = [".env"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any ENV files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any ENV files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening ENV File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const rawDBFRecord = await dbf.readRecords(1);
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
|
||||
//TODO: Determine if there's any value to capture the whole ENV file.
|
||||
|
||||
const rawEnvData: DecodedEnv = deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
//TODO: Several of these fields will fail. Should extend schema to capture them.
|
||||
//"EST_SYSTEM",
|
||||
"ESTFILE_ID",
|
||||
]),
|
||||
);
|
||||
rawEnvData.ciecaid = rawEnvData.estfile_id;
|
||||
delete rawEnvData.estfile_id;
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
|
||||
return rawEnvData;
|
||||
};
|
||||
export default DecodeEnv;
|
||||
54
src/main/decoder/decode-lin.interface.ts
Normal file
54
src/main/decoder/decode-lin.interface.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
export interface DecodedLinLine {
|
||||
line_no?: string;
|
||||
line_ind?: string;
|
||||
line_ref?: string;
|
||||
tran_code?: string;
|
||||
db_ref?: string;
|
||||
unq_seq?: string;
|
||||
//who_pays?: string;
|
||||
line_desc?: string;
|
||||
part_type?: string;
|
||||
//part_desc_j?: boolean;
|
||||
glass_flag?: boolean;
|
||||
oem_partno?: string;
|
||||
price_inc?: boolean;
|
||||
alt_part_i?: boolean;
|
||||
tax_part?: boolean;
|
||||
db_price?: number;
|
||||
act_price?: number;
|
||||
price_j?: boolean;
|
||||
cert_part?: boolean;
|
||||
part_qty?: number;
|
||||
alt_co_id?: string;
|
||||
alt_partno?: string;
|
||||
alt_overrd?: boolean;
|
||||
alt_partm?: string;
|
||||
prt_dsmk_p?: string;
|
||||
prt_dsmk_m?: string;
|
||||
mod_lbr_ty?: string;
|
||||
db_hrs?: number;
|
||||
mod_lb_hrs?: number;
|
||||
lbr_inc?: boolean;
|
||||
lbr_op?: string;
|
||||
lbr_hrs_j?: boolean;
|
||||
lbr_typ_j?: boolean;
|
||||
lbr_op_j?: boolean;
|
||||
paint_stg?: string;
|
||||
paint_tone?: string;
|
||||
lbr_tax?: boolean;
|
||||
lbr_amt?: number;
|
||||
misc_amt?: number;
|
||||
misc_sublt?: string;
|
||||
misc_tax?: boolean;
|
||||
bett_type?: string;
|
||||
bett_pctg?: string | number;
|
||||
bett_amt?: number;
|
||||
bett_tax?: boolean;
|
||||
op_code_desc?: string;
|
||||
}
|
||||
|
||||
export interface DecodedLin {
|
||||
joblines: {
|
||||
data: DecodedLinLine[];
|
||||
};
|
||||
}
|
||||
120
src/main/decoder/decode-lin.ts
Normal file
120
src/main/decoder/decode-lin.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import store from "../store/store";
|
||||
import { DecodedLin, DecodedLinLine } from "./decode-lin.interface";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodeLin = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedLin> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.LIN`);
|
||||
} catch (error) {
|
||||
//LIN File only has 1 location.
|
||||
log.error("Error opening LIN File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any LIN files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any LIN files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = ["lin"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any LIN files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any LIN files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening LIN File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const rawDBFRecord = await dbf.readRecords();
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
const opCodeData = store.get("app.masterdata.opcodes"); //TODO: Type the op codes
|
||||
|
||||
const rawLinData: DecodedLinLine[] = rawDBFRecord.map((record) => {
|
||||
const singleLineData: DecodedLinLine = deepLowerCaseKeys(
|
||||
_.pick(record, [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"LINE_NO",
|
||||
"LINE_IND",
|
||||
"LINE_REF",
|
||||
"TRAN_CODE",
|
||||
"DB_REF",
|
||||
"UNQ_SEQ",
|
||||
// "WHO_PAYS",
|
||||
"LINE_DESC",
|
||||
"PART_TYPE",
|
||||
//TODO: Believe this was previously broken in partner. Need to confirm.
|
||||
// system == "M" ? "PART_DESCJ" : "PART_DES_J",
|
||||
//"PART_DESC_J",
|
||||
//End Check
|
||||
"GLASS_FLAG",
|
||||
"OEM_PARTNO",
|
||||
"PRICE_INC",
|
||||
"ALT_PART_I",
|
||||
"TAX_PART",
|
||||
"DB_PRICE",
|
||||
"ACT_PRICE",
|
||||
"PRICE_J",
|
||||
"CERT_PART",
|
||||
"PART_QTY",
|
||||
"ALT_CO_ID",
|
||||
"ALT_PARTNO",
|
||||
"ALT_OVERRD",
|
||||
"ALT_PARTM",
|
||||
"PRT_DSMK_P",
|
||||
"PRT_DSMK_M",
|
||||
"MOD_LBR_TY",
|
||||
"DB_HRS",
|
||||
"MOD_LB_HRS",
|
||||
"LBR_INC",
|
||||
"LBR_OP",
|
||||
"LBR_HRS_J",
|
||||
"LBR_TYP_J",
|
||||
"LBR_OP_J",
|
||||
"PAINT_STG",
|
||||
"PAINT_TONE",
|
||||
"LBR_TAX",
|
||||
"LBR_AMT",
|
||||
"MISC_AMT",
|
||||
"MISC_SUBLT",
|
||||
"MISC_TAX",
|
||||
"BETT_TYPE",
|
||||
"BETT_PCTG",
|
||||
"BETT_AMT",
|
||||
"BETT_TAX",
|
||||
]),
|
||||
);
|
||||
//Apply line by line adjustments.
|
||||
singleLineData.op_code_desc = opCodeData[singleLineData.lbr_op]?.desc;
|
||||
|
||||
return singleLineData;
|
||||
});
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
//We don't have an inspection date, we instead have `date_estimated`
|
||||
|
||||
return { joblines: { data: rawLinData } };
|
||||
};
|
||||
export default DecodeLin;
|
||||
15
src/main/decoder/decode-pfh.interface.ts
Normal file
15
src/main/decoder/decode-pfh.interface.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export interface DecodedPfh {
|
||||
tax_prethr: number;
|
||||
tax_thr_amt?: number;
|
||||
tax_pstthr?: number;
|
||||
tax_tow_rt: number;
|
||||
tax_str_rt: number;
|
||||
tax_sub_rt: number;
|
||||
tax_lbr_rt: number;
|
||||
federal_tax_rate: number;
|
||||
adj_g_disc?: number;
|
||||
adj_towdis?: number;
|
||||
adj_strdis?: number;
|
||||
tax_predis?: number;
|
||||
tax_gst_rt?: number;
|
||||
}
|
||||
94
src/main/decoder/decode-pfh.ts
Normal file
94
src/main/decoder/decode-pfh.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedPfh } from "./decode-pfh.interface";
|
||||
import { platform } from "os";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodePfh = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedPfh> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.PFH`);
|
||||
} catch (error) {
|
||||
log.error("Error opening PFH File.", errorTypeCheck(error));
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.PFH`);
|
||||
log.log("Trying to find PFH file using regular CIECA Id.");
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any PFH files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFH files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = [".pfh"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any PFH files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFH files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening PFH File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const rawDBFRecord = await dbf.readRecords(1);
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const rawPfhData: DecodedPfh = deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
//TODO: Several of these fields will fail. Should extend schema to capture them.
|
||||
//"ID_PRO_NAM", //Remove
|
||||
"TAX_PRETHR",
|
||||
"TAX_THRAMT",
|
||||
"TAX_PSTTHR",
|
||||
//"TAX_TOW_IN", //Remove
|
||||
"TAX_TOW_RT",
|
||||
//"TAX_STR_IN", //Remove
|
||||
"TAX_STR_RT",
|
||||
//"TAX_SUB_IN", //Remove
|
||||
"TAX_SUB_RT",
|
||||
//"TAX_BTR_IN", //Remove
|
||||
"TAX_LBR_RT",
|
||||
"TAX_GST_RT",
|
||||
//"TAX_GST_IN", //Remove
|
||||
"ADJ_G_DISC",
|
||||
"ADJ_TOWDIS",
|
||||
"ADJ_STRDIS",
|
||||
//"ADJ_BTR_IN", //Remove
|
||||
"TAX_PREDIS",
|
||||
]),
|
||||
);
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
|
||||
//Standardize some of the numbers and divide by 100.
|
||||
|
||||
rawPfhData.tax_prethr = (rawPfhData.tax_prethr ?? 0) / 100;
|
||||
rawPfhData.tax_pstthr = (rawPfhData.tax_pstthr ?? 0) / 100;
|
||||
rawPfhData.tax_tow_rt = (rawPfhData.tax_tow_rt ?? 0) / 100;
|
||||
rawPfhData.tax_str_rt = (rawPfhData.tax_str_rt ?? 0) / 100;
|
||||
rawPfhData.tax_sub_rt = (rawPfhData.tax_sub_rt ?? 0) / 100;
|
||||
rawPfhData.tax_lbr_rt = (rawPfhData.tax_lbr_rt ?? 0) / 100;
|
||||
rawPfhData.federal_tax_rate = (rawPfhData.tax_gst_rt ?? 0) / 100;
|
||||
delete rawPfhData.tax_gst_rt;
|
||||
|
||||
return rawPfhData;
|
||||
};
|
||||
export default DecodePfh;
|
||||
57
src/main/decoder/decode-pfl.interface.ts
Normal file
57
src/main/decoder/decode-pfl.interface.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
//TODO: Clean up this interface. A bit messy as we store the data in very different ways.
|
||||
|
||||
export interface DecodedPflLine {
|
||||
lbr_type: string;
|
||||
lbr_desc: string;
|
||||
lbr_rate: number;
|
||||
lbr_tax_in: boolean;
|
||||
lbr_taxp: number;
|
||||
lbr_adjP: number;
|
||||
lbr_tx_ty1: string;
|
||||
lbr_tx_in1: boolean;
|
||||
lbr_tx_ty2: string;
|
||||
lbr_tx_in2: boolean;
|
||||
lbr_tx_ty3: string;
|
||||
lbr_tx_in3: boolean;
|
||||
lbr_tx_ty4: string;
|
||||
lbr_tx_in4: boolean;
|
||||
lbr_tx_ty5: string;
|
||||
lbr_tx_in5: boolean;
|
||||
}
|
||||
|
||||
export interface JobLaborRateFields {
|
||||
rate_laa: number;
|
||||
rate_lab: number;
|
||||
rate_lad: number;
|
||||
rate_las: number;
|
||||
rate_lar: number;
|
||||
rate_lae: number;
|
||||
rate_lag: number;
|
||||
rate_laf: number;
|
||||
rate_lam: number;
|
||||
rate_lau: number;
|
||||
rate_la1: number;
|
||||
rate_la2: number;
|
||||
rate_la3: number;
|
||||
rate_la4: number;
|
||||
}
|
||||
export interface CiecaPfl {
|
||||
LAA?: DecodedPflLine;
|
||||
LAB?: DecodedPflLine;
|
||||
LAD?: DecodedPflLine;
|
||||
LAS?: DecodedPflLine;
|
||||
LAR?: DecodedPflLine;
|
||||
LAE?: DecodedPflLine;
|
||||
LAG?: DecodedPflLine;
|
||||
LAF?: DecodedPflLine;
|
||||
LAM?: DecodedPflLine;
|
||||
LAU?: DecodedPflLine;
|
||||
LA1?: DecodedPflLine;
|
||||
LA2?: DecodedPflLine;
|
||||
LA3?: DecodedPflLine;
|
||||
LA4?: DecodedPflLine;
|
||||
}
|
||||
|
||||
export interface DecodedPfl extends JobLaborRateFields {
|
||||
cieca_pfl: CiecaPfl;
|
||||
}
|
||||
122
src/main/decoder/decode-pfl.ts
Normal file
122
src/main/decoder/decode-pfl.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import {
|
||||
DecodedPfl,
|
||||
JobLaborRateFields,
|
||||
DecodedPflLine,
|
||||
} from "./decode-pfl.interface";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodePfl = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedPfl> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.PFL`);
|
||||
} catch (error) {
|
||||
//PFL File only has 1 location.
|
||||
log.error("Error opening PFL File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any PFL files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFL files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = [".pfl"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any PFL files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFL files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening PFL File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const rawDBFRecord = await dbf.readRecords();
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const jobLaborRates: JobLaborRateFields = {
|
||||
rate_laa: 0,
|
||||
rate_lab: 0,
|
||||
rate_lad: 0,
|
||||
rate_las: 0,
|
||||
rate_lar: 0,
|
||||
rate_lae: 0,
|
||||
rate_lag: 0,
|
||||
rate_laf: 0,
|
||||
rate_lam: 0,
|
||||
rate_lau: 0,
|
||||
rate_la1: 0,
|
||||
rate_la2: 0,
|
||||
rate_la3: 0,
|
||||
rate_la4: 0,
|
||||
};
|
||||
|
||||
const rawPflData: DecodedPflLine[] = rawDBFRecord.map((record) => {
|
||||
const singleLineData: DecodedPflLine = deepLowerCaseKeys(
|
||||
_.pick(record, [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"LBR_TYPE",
|
||||
"LBR_DESC",
|
||||
"LBR_RATE",
|
||||
"LBR_TAX_IN",
|
||||
"LBR_TAXP",
|
||||
"LBR_ADJP",
|
||||
"LBR_TX_TY1",
|
||||
"LBR_TX_IN1",
|
||||
"LBR_TX_TY2",
|
||||
"LBR_TX_IN2",
|
||||
"LBR_TX_TY3",
|
||||
"LBR_TX_IN3",
|
||||
"LBR_TX_TY4",
|
||||
"LBR_TX_IN4",
|
||||
"LBR_TX_TY5",
|
||||
"LBR_TX_IN5",
|
||||
]),
|
||||
);
|
||||
//Apply line by line adjustments.
|
||||
//Set the job.rate_<CIECA_TYPE> field based on the value.
|
||||
jobLaborRates[`rate_${singleLineData.lbr_type.toLowerCase()}`] =
|
||||
singleLineData.lbr_rate;
|
||||
|
||||
//For Mitchell, Alum is stored under LA3 instead of LAA. Shift it back over.
|
||||
//The old partner had a check for this, but it always was true. Matching that logic.
|
||||
if (singleLineData.lbr_type === "LA3") {
|
||||
jobLaborRates[`rate_laa`] = singleLineData.lbr_rate;
|
||||
}
|
||||
|
||||
//Also capture the whole object.
|
||||
//This is segmented because the whole object was not previously captured for ImEX as it wasn't needed.
|
||||
//Rome needs the whole object to accurately calculate the tax rates.
|
||||
|
||||
return singleLineData;
|
||||
});
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
//We don't have an inspection date, we instead have `date_estimated`
|
||||
|
||||
const pflObj = _.keyBy(rawPflData, "lbr_type");
|
||||
|
||||
return { ...jobLaborRates, cieca_pfl: pflObj };
|
||||
};
|
||||
|
||||
export default DecodePfl;
|
||||
50
src/main/decoder/decode-pfm.interface.ts
Normal file
50
src/main/decoder/decode-pfm.interface.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
export interface DecodedPfmLine {
|
||||
matl_type?: string;
|
||||
cal_code?: number;
|
||||
cal_desc?: string;
|
||||
cal_maxdlr?: number;
|
||||
cal_prip?: number;
|
||||
cal_secp?: number;
|
||||
mat_calp?: number;
|
||||
cal_prethr?: number;
|
||||
cal_pstthr?: number;
|
||||
cal_thramt?: number;
|
||||
cal_lbrmin?: number;
|
||||
cal_lbrrte?: number;
|
||||
cal_opcode?: string;
|
||||
tax_ind?: boolean;
|
||||
mat_taxp?: number;
|
||||
mat_adjp?: number;
|
||||
mat_tx_ty1?: string;
|
||||
mat_tx_in1?: boolean;
|
||||
mat_tx_ty2?: string;
|
||||
mat_tx_in2?: boolean;
|
||||
mat_tx_ty3?: string;
|
||||
mat_tx_in3?: boolean;
|
||||
mat_tx_ty4?: string;
|
||||
mat_tx_in4?: boolean;
|
||||
mat_tx_ty5?: string;
|
||||
mat_tx_in5?: boolean;
|
||||
}
|
||||
|
||||
export interface JobMaterialRateFields {
|
||||
rate_mapa: number;
|
||||
tax_paint_mat_rt: number;
|
||||
rate_mash: number;
|
||||
tax_shop_mat_rt: number;
|
||||
rate_mahw: number;
|
||||
tax_levies_rt: number;
|
||||
rate_ma2s: number;
|
||||
rate_ma2t: number;
|
||||
rate_ma3s: number;
|
||||
rate_macs: number;
|
||||
rate_mabl: number;
|
||||
}
|
||||
|
||||
export interface DecodedPfm extends JobMaterialRateFields {
|
||||
materials: {
|
||||
MAPA?: DecodedPfmLine;
|
||||
MASH?: DecodedPfmLine;
|
||||
};
|
||||
cieca_pfm?: DecodedPfmLine[];
|
||||
}
|
||||
169
src/main/decoder/decode-pfm.ts
Normal file
169
src/main/decoder/decode-pfm.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import YNBoolConverter from "../../util/ynBoolConverter";
|
||||
import {
|
||||
DecodedPfm,
|
||||
DecodedPfmLine,
|
||||
JobMaterialRateFields,
|
||||
} from "./decode-pfm.interface";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodePfm = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedPfm> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.PFM`);
|
||||
} catch (error) {
|
||||
//PFM File only has 1 location.
|
||||
log.error("Error opening PFM File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any PFM files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFM files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = [".pfm"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any PFM files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFM files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening PFM File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const rawDBFRecord = await dbf.readRecords();
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const jobMaterialRates: JobMaterialRateFields = {
|
||||
rate_mapa: 0,
|
||||
tax_paint_mat_rt: 0,
|
||||
rate_mash: 0,
|
||||
tax_shop_mat_rt: 0,
|
||||
rate_mahw: 0,
|
||||
tax_levies_rt: 0,
|
||||
rate_ma2s: 0,
|
||||
rate_ma2t: 0,
|
||||
rate_ma3s: 0,
|
||||
rate_macs: 0,
|
||||
rate_mabl: 0,
|
||||
};
|
||||
|
||||
const rawPfmData: DecodedPfmLine[] = rawDBFRecord.map((record) => {
|
||||
const singleLineData: DecodedPfmLine = YNBoolConverter(
|
||||
deepLowerCaseKeys(
|
||||
_.pick(record, [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"MATL_TYPE",
|
||||
"CAL_CODE",
|
||||
"CAL_DESC",
|
||||
"CAL_MAXDLR",
|
||||
"CAL_PRIP",
|
||||
|
||||
"CAL_SECP",
|
||||
"MAT_CALP",
|
||||
"CAL_PRETHR", //Mitchell here
|
||||
"CAL_PSTTHR",
|
||||
"CAL_THRAMT",
|
||||
|
||||
"CAL_LBRMIN",
|
||||
|
||||
"CAL_LBRRTE", //Audatex puts it here
|
||||
"CAL_OPCODE",
|
||||
|
||||
"TAX_IND",
|
||||
"MAT_TAXP",
|
||||
"MAT_ADJP",
|
||||
"MAT_TX_TY1",
|
||||
"MAT_TX_IN1",
|
||||
"MAT_TX_TY2",
|
||||
"MAT_TX_IN2",
|
||||
"MAT_TX_TY3",
|
||||
"MAT_TX_IN3",
|
||||
"MAT_TX_TY4",
|
||||
"MAT_TX_IN4",
|
||||
"MAT_TX_TY5",
|
||||
"MAT_TX_IN5",
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
//Also capture the whole object.
|
||||
//This is segmented because the whole object was not previously captured for ImEX as it wasn't needed.
|
||||
//Rome needs the whole object to accurately calculate the tax rates.
|
||||
|
||||
return singleLineData;
|
||||
});
|
||||
|
||||
//Apply line by line adjustments.
|
||||
const mapaLine: DecodedPfmLine | undefined = rawPfmData.find(
|
||||
(line) => line.matl_type === "MAPA",
|
||||
);
|
||||
if (mapaLine) {
|
||||
jobMaterialRates.rate_mapa =
|
||||
mapaLine.cal_lbrrte || mapaLine.cal_prethr || 0;
|
||||
jobMaterialRates.tax_paint_mat_rt = (mapaLine.mat_taxp ?? 0) / 100;
|
||||
}
|
||||
|
||||
const mashLine: DecodedPfmLine | undefined = rawPfmData.find(
|
||||
(line) => line.matl_type === "MASH",
|
||||
);
|
||||
if (mashLine) {
|
||||
jobMaterialRates.rate_mash =
|
||||
mashLine.cal_lbrrte || mashLine.cal_prethr || 0;
|
||||
jobMaterialRates.tax_shop_mat_rt = (mashLine.mat_taxp ?? 0) / 100;
|
||||
}
|
||||
|
||||
const mahwLine: DecodedPfmLine | undefined = rawPfmData.find(
|
||||
(line) => line.matl_type === "MAHW",
|
||||
);
|
||||
if (mahwLine) {
|
||||
jobMaterialRates.rate_mahw =
|
||||
mahwLine.cal_lbrrte || mahwLine.cal_prethr || 0;
|
||||
jobMaterialRates.tax_levies_rt = (mahwLine.mat_taxp ?? 0) / 100;
|
||||
}
|
||||
|
||||
const additionalMaterials = ["MA2S", "MA2T", "MA3S", "MACS", "MABL"];
|
||||
additionalMaterials.forEach((type) => {
|
||||
const line: DecodedPfmLine | undefined = rawPfmData.find(
|
||||
(line) => line.matl_type === type,
|
||||
);
|
||||
if (line) {
|
||||
jobMaterialRates[`rate_${type.toLowerCase()}`] =
|
||||
line.cal_lbrrte || line.cal_prethr || 0;
|
||||
}
|
||||
});
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
//We don't have an inspection date, we instead have `date_estimated`
|
||||
|
||||
return {
|
||||
...jobMaterialRates,
|
||||
materials: {
|
||||
MASH: mashLine,
|
||||
MAPA: mapaLine, //TODO: Need to verify if more fields are to come in here.
|
||||
},
|
||||
//cieca_pfm: rawPfmData, //TODO: Not currently captured. This may have valu in the future.
|
||||
};
|
||||
};
|
||||
|
||||
export default DecodePfm;
|
||||
32
src/main/decoder/decode-pfo.interface.ts
Normal file
32
src/main/decoder/decode-pfo.interface.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
export interface DecodedPfoLine {
|
||||
tx_tow_ty?: string;
|
||||
tow_t_ty1?: string;
|
||||
tow_t_in1?: boolean;
|
||||
tow_t_ty2?: string;
|
||||
tow_t_in2?: boolean;
|
||||
tow_t_ty3?: string;
|
||||
tow_t_in3?: boolean;
|
||||
tow_t_ty4?: string;
|
||||
tow_t_in4?: boolean;
|
||||
tow_t_ty5?: string;
|
||||
tow_t_in5?: boolean;
|
||||
tow_t_ty6?: string;
|
||||
tow_t_in6?: boolean;
|
||||
tx_stor_ty?: string;
|
||||
stor_t_ty1?: string;
|
||||
stor_t_in1?: boolean;
|
||||
stor_t_ty2?: string;
|
||||
stor_t_in2?: boolean;
|
||||
stor_t_ty3?: string;
|
||||
stor_t_in3?: boolean;
|
||||
stor_t_ty4?: string;
|
||||
stor_t_in4?: boolean;
|
||||
stor_t_ty5?: string;
|
||||
stor_t_in5?: boolean;
|
||||
stor_t_ty6?: string;
|
||||
stor_t_in6?: boolean;
|
||||
}
|
||||
|
||||
export interface DecodedPfo {
|
||||
cieca_pfo: DecodedPfoLine;
|
||||
}
|
||||
93
src/main/decoder/decode-pfo.ts
Normal file
93
src/main/decoder/decode-pfo.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import YNBoolConverter from "../../util/ynBoolConverter";
|
||||
import { DecodedPfo, DecodedPfoLine } from "./decode-pfo.interface";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodePfo = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedPfo> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.PFO`);
|
||||
} catch (error) {
|
||||
log.error("Error opening PFO File.", errorTypeCheck(error));
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.PFO`);
|
||||
log.log("Trying to find PFO file using regular CIECA Id.");
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any PFO files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFO files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = [".pfo"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any PFO files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFO files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening PFO File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const rawDBFRecord = await dbf.readRecords(1);
|
||||
|
||||
//PFO will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const rawPfoData: DecodedPfoLine = YNBoolConverter(
|
||||
deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"TX_TOW_TY",
|
||||
"TOW_T_TY1",
|
||||
"TOW_T_IN1",
|
||||
"TOW_T_TY2",
|
||||
"TOW_T_IN2",
|
||||
"TOW_T_TY3",
|
||||
"TOW_T_IN3",
|
||||
"TOW_T_TY4",
|
||||
"TOW_T_IN4",
|
||||
"TOW_T_TY5",
|
||||
"TOW_T_IN5",
|
||||
"TOW_T_TY6",
|
||||
"TOW_T_IN6",
|
||||
"TX_STOR_TY",
|
||||
"STOR_T_TY1",
|
||||
"STOR_T_IN1",
|
||||
"STOR_T_TY2",
|
||||
"STOR_T_IN2",
|
||||
"STOR_T_TY3",
|
||||
"STOR_T_IN3",
|
||||
"STOR_T_TY4",
|
||||
"STOR_T_IN4",
|
||||
"STOR_T_TY5",
|
||||
"STOR_T_IN5",
|
||||
"STOR_T_TY6",
|
||||
"STOR_T_IN6",
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
|
||||
return { cieca_pfo: rawPfoData };
|
||||
};
|
||||
export default DecodePfo;
|
||||
37
src/main/decoder/decode-pfp.interface.ts
Normal file
37
src/main/decoder/decode-pfp.interface.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export interface DecodedPfpLine {
|
||||
prt_type: string;
|
||||
prt_tax_in: boolean;
|
||||
prt_tax_rt: number;
|
||||
prt_mkupp: number;
|
||||
prt_mktyp: string;
|
||||
prt_discp: number;
|
||||
prt_tx_ty1: string;
|
||||
prt_tx_in1: boolean;
|
||||
prt_tx_ty2: string;
|
||||
prt_tx_in2: boolean;
|
||||
prt_tx_ty3: string;
|
||||
prt_tx_in3: boolean;
|
||||
prt_tx_ty4: string;
|
||||
prt_tx_in4: boolean;
|
||||
prt_tx_ty5: string;
|
||||
prt_tx_in5: boolean;
|
||||
}
|
||||
|
||||
export interface DecodedPfpLinesByType {
|
||||
PAA: DecodedPfpLine;
|
||||
PAC: DecodedPfpLine;
|
||||
PAL: DecodedPfpLine;
|
||||
PAG: DecodedPfpLine;
|
||||
PAM: DecodedPfpLine;
|
||||
PAP: DecodedPfpLine;
|
||||
PAN: DecodedPfpLine;
|
||||
PAO: DecodedPfpLine;
|
||||
PAR: DecodedPfpLine;
|
||||
PAS: DecodedPfpLine;
|
||||
PASL: DecodedPfpLine;
|
||||
PAT: DecodedPfpLine;
|
||||
}
|
||||
|
||||
export interface DecodedPfp {
|
||||
parts_tax_rates: DecodedPfpLinesByType;
|
||||
}
|
||||
98
src/main/decoder/decode-pfp.ts
Normal file
98
src/main/decoder/decode-pfp.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import YNBoolConverter from "../../util/ynBoolConverter";
|
||||
import {
|
||||
DecodedPfp,
|
||||
DecodedPfpLine,
|
||||
DecodedPfpLinesByType,
|
||||
} from "./decode-pfp.interface";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodePfp = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedPfp> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.PFP`);
|
||||
} catch (error) {
|
||||
//PFP File only has 1 location.
|
||||
log.error("Error opening PFP File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any PFP files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFP files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = [".pfp"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any PFP files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFP files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening PFP File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const rawDBFRecord = await dbf.readRecords();
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const rawPfpData: DecodedPfpLine[] = rawDBFRecord.map((record) => {
|
||||
const singleLineData: DecodedPfpLine = deepLowerCaseKeys(
|
||||
_.pick(record, [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"PRT_TYPE",
|
||||
"PRT_TAX_IN",
|
||||
"PRT_TAX_RT",
|
||||
"PRT_MKUPP",
|
||||
"PRT_MKTYP",
|
||||
"PRT_DISCP",
|
||||
"PRT_TX_TY1",
|
||||
"PRT_TX_IN1",
|
||||
"PRT_TX_TY2",
|
||||
"PRT_TX_IN2",
|
||||
"PRT_TX_TY3",
|
||||
"PRT_TX_IN3",
|
||||
"PRT_TX_TY4",
|
||||
"PRT_TX_IN4",
|
||||
"PRT_TX_TY5",
|
||||
"PRT_TX_IN5",
|
||||
]),
|
||||
);
|
||||
|
||||
singleLineData.prt_tax_rt = singleLineData.prt_tax_rt / 100;
|
||||
return YNBoolConverter(singleLineData);
|
||||
});
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
|
||||
//Convert array of lines to a hash object.
|
||||
const parsedPfpFile: DecodedPfpLinesByType = rawPfpData.reduce(
|
||||
(acc: DecodedPfpLinesByType, line: DecodedPfpLine) => {
|
||||
acc[line.prt_type] = line;
|
||||
return acc;
|
||||
},
|
||||
{} as DecodedPfpLinesByType,
|
||||
);
|
||||
|
||||
return { parts_tax_rates: parsedPfpFile };
|
||||
};
|
||||
|
||||
export default DecodePfp;
|
||||
147
src/main/decoder/decode-pft.interface.ts
Normal file
147
src/main/decoder/decode-pft.interface.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Interface representing decoded data from a PFT file
|
||||
* Contains tax type information with up to 6 tax types and 5 tiers each
|
||||
*/
|
||||
export interface DecodedPftLine {
|
||||
// Tax Type 1
|
||||
tax_type1?: string;
|
||||
ty1_tier1?: number;
|
||||
ty1_thres1?: number;
|
||||
ty1_rate1?: number;
|
||||
ty1_sur1?: number;
|
||||
ty1_tier2?: number;
|
||||
ty1_thres2?: number;
|
||||
ty1_rate2?: number;
|
||||
ty1_sur2?: number;
|
||||
ty1_tier3?: number;
|
||||
ty1_thres3?: number;
|
||||
ty1_rate3?: number;
|
||||
ty1_sur3?: number;
|
||||
ty1_tier4?: number;
|
||||
ty1_thres4?: number;
|
||||
ty1_rate4?: number;
|
||||
ty1_sur4?: number;
|
||||
ty1_tier5?: number;
|
||||
ty1_thres5?: number;
|
||||
ty1_rate5?: number;
|
||||
ty1_sur5?: number;
|
||||
|
||||
// Tax Type 2
|
||||
tax_type2?: string;
|
||||
ty2_tier1?: number;
|
||||
ty2_thres1?: number;
|
||||
ty2_rate1?: number;
|
||||
ty2_sur1?: number;
|
||||
ty2_tier2?: number;
|
||||
ty2_thres2?: number;
|
||||
ty2_rate2?: number;
|
||||
ty2_sur2?: number;
|
||||
ty2_tier3?: number;
|
||||
ty2_thres3?: number;
|
||||
ty2_rate3?: number;
|
||||
ty2_sur3?: number;
|
||||
ty2_tier4?: number;
|
||||
ty2_thres4?: number;
|
||||
ty2_rate4?: number;
|
||||
ty2_sur4?: number;
|
||||
ty2_tier5?: number;
|
||||
ty2_thres5?: number;
|
||||
ty2_rate5?: number;
|
||||
ty2_sur5?: number;
|
||||
|
||||
// Tax Type 3
|
||||
tax_type3?: string;
|
||||
ty3_tier1?: number;
|
||||
ty3_thres1?: number;
|
||||
ty3_rate1?: number;
|
||||
ty3_sur1?: number;
|
||||
ty3_tier2?: number;
|
||||
ty3_thres2?: number;
|
||||
ty3_rate2?: number;
|
||||
ty3_sur2?: number;
|
||||
ty3_tier3?: number;
|
||||
ty3_thres3?: number;
|
||||
ty3_rate3?: number;
|
||||
ty3_sur3?: number;
|
||||
ty3_tier4?: number;
|
||||
ty3_thres4?: number;
|
||||
ty3_rate4?: number;
|
||||
ty3_sur4?: number;
|
||||
ty3_tier5?: number;
|
||||
ty3_thres5?: number;
|
||||
ty3_rate5?: number;
|
||||
ty3_sur5?: number;
|
||||
|
||||
// Tax Type 4
|
||||
tax_type4?: string;
|
||||
ty4_tier1?: number;
|
||||
ty4_thres1?: number;
|
||||
ty4_rate1?: number;
|
||||
ty4_sur1?: number;
|
||||
ty4_tier2?: number;
|
||||
ty4_thres2?: number;
|
||||
ty4_rate2?: number;
|
||||
ty4_sur2?: number;
|
||||
ty4_tier3?: number;
|
||||
ty4_thres3?: number;
|
||||
ty4_rate3?: number;
|
||||
ty4_sur3?: number;
|
||||
ty4_tier4?: number;
|
||||
ty4_thres4?: number;
|
||||
ty4_rate4?: number;
|
||||
ty4_sur4?: number;
|
||||
ty4_tier5?: number;
|
||||
ty4_thres5?: number;
|
||||
ty4_rate5?: number;
|
||||
ty4_sur5?: number;
|
||||
|
||||
// Tax Type 5
|
||||
tax_type5?: string;
|
||||
ty5_tier1?: number;
|
||||
ty5_thres1?: number;
|
||||
ty5_rate1?: number;
|
||||
ty5_sur1?: number;
|
||||
ty5_tier2?: number;
|
||||
ty5_thres2?: number;
|
||||
ty5_rate2?: number;
|
||||
ty5_sur2?: number;
|
||||
ty5_tier3?: number;
|
||||
ty5_thres3?: number;
|
||||
ty5_rate3?: number;
|
||||
ty5_sur3?: number;
|
||||
ty5_tier4?: number;
|
||||
ty5_thres4?: number;
|
||||
ty5_rate4?: number;
|
||||
ty5_sur4?: number;
|
||||
ty5_tier5?: number;
|
||||
ty5_thres5?: number;
|
||||
ty5_rate5?: number;
|
||||
ty5_sur5?: number;
|
||||
|
||||
// Tax Type 6
|
||||
tax_type6?: string;
|
||||
ty6_tier1?: number;
|
||||
ty6_thres1?: number;
|
||||
ty6_rate1?: number;
|
||||
ty6_sur1?: number;
|
||||
ty6_tier2?: number;
|
||||
ty6_thres2?: number;
|
||||
ty6_rate2?: number;
|
||||
ty6_sur2?: number;
|
||||
ty6_tier3?: number;
|
||||
ty6_thres3?: number;
|
||||
ty6_rate3?: number;
|
||||
ty6_sur3?: number;
|
||||
ty6_tier4?: number;
|
||||
ty6_thres4?: number;
|
||||
ty6_rate4?: number;
|
||||
ty6_sur4?: number;
|
||||
ty6_tier5?: number;
|
||||
ty6_thres5?: number;
|
||||
ty6_rate5?: number;
|
||||
ty6_sur5?: number;
|
||||
}
|
||||
|
||||
export interface DecodedPft {
|
||||
cieca_pft: DecodedPftLine;
|
||||
}
|
||||
189
src/main/decoder/decode-pft.ts
Normal file
189
src/main/decoder/decode-pft.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedPft, DecodedPftLine } from "./decode-pft.interface";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodePft = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedPft> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.PFT`);
|
||||
} catch (error) {
|
||||
log.error("Error opening PFH File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any PFT files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFT files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = ["pft"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any PFT files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any PFT files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening PFT File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const rawDBFRecord = await dbf.readRecords(1);
|
||||
|
||||
//PFT will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const rawPftData: DecodedPftLine = deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"TAX_TYPE1", //The below is is taken from a CCC estimate. Will require validation to ensure it is also accurate for Audatex/Mitchell
|
||||
"TY1_TIER1",
|
||||
"TY1_THRES1",
|
||||
"TY1_RATE1",
|
||||
"TY1_SUR1",
|
||||
"TY1_TIER2",
|
||||
"TY1_THRES2",
|
||||
"TY1_RATE2",
|
||||
"TY1_SUR2",
|
||||
"TY1_TIER3",
|
||||
"TY1_THRES3",
|
||||
"TY1_RATE3",
|
||||
"TY1_SUR3",
|
||||
"TY1_TIER4",
|
||||
"TY1_THRES4",
|
||||
"TY1_RATE4",
|
||||
"TY1_SUR4",
|
||||
"TY1_TIER5",
|
||||
"TY1_THRES5",
|
||||
"TY1_RATE5",
|
||||
"TY1_SUR5",
|
||||
"TAX_TYPE2",
|
||||
"TY2_TIER1",
|
||||
"TY2_THRES1",
|
||||
"TY2_RATE1",
|
||||
"TY2_SUR1",
|
||||
"TY2_TIER2",
|
||||
"TY2_THRES2",
|
||||
"TY2_RATE2",
|
||||
"TY2_SUR2",
|
||||
"TY2_TIER3",
|
||||
"TY2_THRES3",
|
||||
"TY2_RATE3",
|
||||
"TY2_SUR3",
|
||||
"TY2_TIER4",
|
||||
"TY2_THRES4",
|
||||
"TY2_RATE4",
|
||||
"TY2_SUR4",
|
||||
"TY2_TIER5",
|
||||
"TY2_THRES5",
|
||||
"TY2_RATE5",
|
||||
"TY2_SUR5",
|
||||
"TAX_TYPE3",
|
||||
"TY3_TIER1",
|
||||
"TY3_THRES1",
|
||||
"TY3_RATE1",
|
||||
"TY3_SUR1",
|
||||
"TY3_TIER2",
|
||||
"TY3_THRES2",
|
||||
"TY3_RATE2",
|
||||
"TY3_SUR2",
|
||||
"TY3_TIER3",
|
||||
"TY3_THRES3",
|
||||
"TY3_RATE3",
|
||||
"TY3_SUR3",
|
||||
"TY3_TIER4",
|
||||
"TY3_THRES4",
|
||||
"TY3_RATE4",
|
||||
"TY3_SUR4",
|
||||
"TY3_TIER5",
|
||||
"TY3_THRES5",
|
||||
"TY3_RATE5",
|
||||
"TY3_SUR5",
|
||||
"TAX_TYPE4",
|
||||
"TY4_TIER1",
|
||||
"TY4_THRES1",
|
||||
"TY4_RATE1",
|
||||
"TY4_SUR1",
|
||||
"TY4_TIER2",
|
||||
"TY4_THRES2",
|
||||
"TY4_RATE2",
|
||||
"TY4_SUR2",
|
||||
"TY4_TIER3",
|
||||
"TY4_THRES3",
|
||||
"TY4_RATE3",
|
||||
"TY4_SUR3",
|
||||
"TY4_TIER4",
|
||||
"TY4_THRES4",
|
||||
"TY4_RATE4",
|
||||
"TY4_SUR4",
|
||||
"TY4_TIER5",
|
||||
"TY4_THRES5",
|
||||
"TY4_RATE5",
|
||||
"TY4_SUR5",
|
||||
"TAX_TYPE5",
|
||||
"TY5_TIER1",
|
||||
"TY5_THRES1",
|
||||
"TY5_RATE1",
|
||||
"TY5_SUR1",
|
||||
"TY5_TIER2",
|
||||
"TY5_THRES2",
|
||||
"TY5_RATE2",
|
||||
"TY5_SUR2",
|
||||
"TY5_TIER3",
|
||||
"TY5_THRES3",
|
||||
"TY5_RATE3",
|
||||
"TY5_SUR3",
|
||||
"TY5_TIER4",
|
||||
"TY5_THRES4",
|
||||
"TY5_RATE4",
|
||||
"TY5_SUR4",
|
||||
"TY5_TIER5",
|
||||
"TY5_THRES5",
|
||||
"TY5_RATE5",
|
||||
"TY5_SUR5",
|
||||
"TAX_TYPE6",
|
||||
"TY6_TIER1",
|
||||
"TY6_THRES1",
|
||||
"TY6_RATE1",
|
||||
"TY6_SUR1",
|
||||
"TY6_TIER2",
|
||||
"TY6_THRES2",
|
||||
"TY6_RATE2",
|
||||
"TY6_SUR2",
|
||||
"TY6_TIER3",
|
||||
"TY6_THRES3",
|
||||
"TY6_RATE3",
|
||||
"TY6_SUR3",
|
||||
"TY6_TIER4",
|
||||
"TY6_THRES4",
|
||||
"TY6_RATE4",
|
||||
"TY6_SUR4",
|
||||
"TY6_TIER5",
|
||||
"TY6_THRES5",
|
||||
"TY6_RATE5",
|
||||
"TY6_SUR5",
|
||||
]),
|
||||
);
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
//We don't have an inspection date, we instead have `date_estimated`
|
||||
|
||||
return { cieca_pft: rawPftData };
|
||||
};
|
||||
export default DecodePft;
|
||||
23
src/main/decoder/decode-stl.interface.ts
Normal file
23
src/main/decoder/decode-stl.interface.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export interface DecodedStlLine {
|
||||
ttl_type?: string;
|
||||
ttl_typecd?: string;
|
||||
t_amt?: number;
|
||||
t_hrs?: number;
|
||||
t_addlbr?: number;
|
||||
t_discamt?: number;
|
||||
t_mkupamt?: number;
|
||||
t_gdiscamt?: number;
|
||||
tax_amt?: number;
|
||||
nt_amt?: number;
|
||||
nt_hrs?: number;
|
||||
nt_addlbr?: number;
|
||||
nt_disc?: number;
|
||||
nt_mkup?: number;
|
||||
nt_gdis?: number;
|
||||
ttl_typamt?: number;
|
||||
ttl_hrs?: number;
|
||||
ttl_amt?: number;
|
||||
}
|
||||
export interface DecodedStl {
|
||||
cieca_stl: { data: DecodedStlLine[] };
|
||||
}
|
||||
85
src/main/decoder/decode-stl.ts
Normal file
85
src/main/decoder/decode-stl.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedStl, DecodedStlLine } from "./decode-stl.interface";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodeStl = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedStl> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.STL`);
|
||||
} catch (error) {
|
||||
log.error("Error opening STL File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any STL files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any STL files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = ["stl"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any STL files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any STL files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening STL File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const rawDBFRecord = await dbf.readRecords();
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const rawStlData: DecodedStlLine[] = rawDBFRecord.map((record) => {
|
||||
const singleLineData: DecodedStlLine = deepLowerCaseKeys(
|
||||
_.pick(record, [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"TTL_TYPE",
|
||||
"TTL_TYPECD",
|
||||
"T_AMT",
|
||||
"T_HRS",
|
||||
"T_ADDLBR",
|
||||
"T_DISCAMT",
|
||||
"T_MKUPAMT",
|
||||
"T_GDISCAMT",
|
||||
"TAX_AMT",
|
||||
"NT_AMT",
|
||||
"NT_HRS",
|
||||
"NT_ADDLBR",
|
||||
"NT_DISC",
|
||||
"NT_MKUP",
|
||||
"NT_GDIS",
|
||||
"TTL_TYPAMT",
|
||||
"TTL_HRS",
|
||||
"TTL_AMT",
|
||||
]),
|
||||
);
|
||||
//Apply line by line adjustments.
|
||||
|
||||
return singleLineData;
|
||||
});
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
//We don't have an inspection date, we instead have `date_estimated`
|
||||
|
||||
return { cieca_stl: { data: rawStlData } };
|
||||
};
|
||||
export default DecodeStl;
|
||||
22
src/main/decoder/decode-ttl.interface.ts
Normal file
22
src/main/decoder/decode-ttl.interface.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export interface DecodedTtl {
|
||||
clm_total: number;
|
||||
depreciation_taxes: number;
|
||||
cieca_ttl: { data: DecodedTtlLine };
|
||||
}
|
||||
|
||||
export interface DecodedTtlLine {
|
||||
g_ttl_amt?: number;
|
||||
g_bett_amt?: number;
|
||||
g_rpd_amt?: number;
|
||||
g_ded_amt?: number;
|
||||
g_cust_amt?: number;
|
||||
g_aa_amt?: number;
|
||||
n_ttl_amt?: number;
|
||||
prev_net?: number;
|
||||
supp_amt?: number;
|
||||
n_supp_amt?: number;
|
||||
g_upd_amt?: number;
|
||||
g_ttl_disc?: number;
|
||||
g_tax?: number;
|
||||
gst_amt?: number;
|
||||
}
|
||||
80
src/main/decoder/decode-ttl.ts
Normal file
80
src/main/decoder/decode-ttl.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedTtl, DecodedTtlLine } from "./decode-ttl.interface";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodeTtl = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedTtl> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.TTL`);
|
||||
} catch (error) {
|
||||
log.error("Error opening TTL File.", errorTypeCheck(error));
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any TTL files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any TTL files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = ["ttl"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any TTL files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any TTL files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath);
|
||||
} catch (error) {
|
||||
log.error("Error opening TTL File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const rawDBFRecord = await dbf.readRecords(1);
|
||||
|
||||
//PFT will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
const rawTtlData: DecodedTtlLine = deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"G_TTL_AMT",
|
||||
"G_BETT_AMT",
|
||||
"G_RPD_AMT",
|
||||
"G_DED_AMT",
|
||||
"G_CUST_AMT",
|
||||
"G_AA_AMT",
|
||||
"N_TTL_AMT",
|
||||
"PREV_NET",
|
||||
"SUPP_AMT",
|
||||
"N_SUPP_AMT", //Previously commented. Possible issue.
|
||||
"G_UPD_AMT",
|
||||
"G_TTL_DISC",
|
||||
"G_TAX",
|
||||
"GST_AMT",
|
||||
]),
|
||||
);
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
|
||||
return {
|
||||
clm_total: rawTtlData.g_ttl_amt || 0,
|
||||
depreciation_taxes: rawTtlData.g_bett_amt || 0, //TODO: Find where this needs to be filled from
|
||||
cieca_ttl: { data: rawTtlData },
|
||||
};
|
||||
};
|
||||
export default DecodeTtl;
|
||||
64
src/main/decoder/decode-veh.interface.ts
Normal file
64
src/main/decoder/decode-veh.interface.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { UUID } from "crypto";
|
||||
|
||||
export interface DecodedVeh {
|
||||
// Basic vehicle information
|
||||
plate_no?: string;
|
||||
plate_st?: string;
|
||||
v_vin?: string;
|
||||
v_model_yr?: string;
|
||||
v_make_desc?: string;
|
||||
v_model_desc?: string;
|
||||
v_color?: string;
|
||||
kmin?: number;
|
||||
area_of_damage?: {
|
||||
impact1?: string;
|
||||
impact2?: string;
|
||||
};
|
||||
// Complete vehicle data object
|
||||
vehicle?: { data: VehicleRecordInterface };
|
||||
}
|
||||
|
||||
export interface VehicleRecordInterface {
|
||||
// Area of damage information
|
||||
area_of_damage?: {
|
||||
impact1?: string;
|
||||
impact2?: string;
|
||||
};
|
||||
// Paint code information
|
||||
v_paint_codes: {
|
||||
paint_cd1: string;
|
||||
paint_cd2: string;
|
||||
paint_cd3: string;
|
||||
};
|
||||
// Vehicle information from DBF file
|
||||
db_v_code?: string;
|
||||
plate_no?: string;
|
||||
plate_st?: string;
|
||||
v_vin?: string;
|
||||
v_cond: string;
|
||||
v_prod_dt?: Date;
|
||||
v_model_yr: string;
|
||||
v_makecode: string;
|
||||
v_make_desc?: string;
|
||||
v_model?: string;
|
||||
v_model_desc?: string;
|
||||
v_type: string;
|
||||
v_bstyle?: string;
|
||||
v_trimcode?: string;
|
||||
trim_color?: string;
|
||||
v_mldgcode?: string;
|
||||
v_engine?: string;
|
||||
v_mileage?: number; //TODO: This can sometimes come in as UNK.
|
||||
v_color?: string;
|
||||
v_tone?: string;
|
||||
v_stage?: string;
|
||||
shopid: UUID;
|
||||
|
||||
//These are removed during business logic processing.
|
||||
v_makedesc?: string;
|
||||
impact_1?: string;
|
||||
impact_2?: string;
|
||||
paint_cd1?: string;
|
||||
paint_cd2?: string;
|
||||
paint_cd3?: string;
|
||||
}
|
||||
145
src/main/decoder/decode-veh.ts
Normal file
145
src/main/decoder/decode-veh.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import log from "electron-log/main";
|
||||
import _ from "lodash";
|
||||
import deepLowerCaseKeys from "../../util/deepLowercaseKeys";
|
||||
import { DecodedVeh, VehicleRecordInterface } from "./decode-veh.interface";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import store from "../store/store";
|
||||
import typeCaster from "../../util/typeCaster";
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { findFileCaseInsensitive } from "./decoder-utils";
|
||||
|
||||
const DecodeVeh = async (
|
||||
extensionlessFilePath: string,
|
||||
): Promise<DecodedVeh> => {
|
||||
let dbf: DBFFile | null = null;
|
||||
if (platform.isWindows) {
|
||||
try {
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}V.VEH`);
|
||||
} catch (error) {
|
||||
log.error("Error opening VEH File.", errorTypeCheck(error));
|
||||
dbf = await DBFFile.open(`${extensionlessFilePath}.VEH`);
|
||||
log.log("Found VEH file using regular CIECA Id.");
|
||||
}
|
||||
|
||||
if (!dbf) {
|
||||
log.error(`Could not find any VEH files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any VEH files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const possibleExtensions: string[] = ["v.veh", ".veh"];
|
||||
const filePath = await findFileCaseInsensitive(
|
||||
extensionlessFilePath,
|
||||
possibleExtensions,
|
||||
);
|
||||
try {
|
||||
if (!filePath) {
|
||||
log.error(`Could not find any VEH files at ${extensionlessFilePath}`);
|
||||
throw new Error(
|
||||
`Could not find any VEH files at ${extensionlessFilePath}`,
|
||||
);
|
||||
}
|
||||
dbf = await DBFFile.open(filePath, { readMode: "loose" });
|
||||
} catch (error) {
|
||||
log.error("Error opening VEH File.", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const rawDBFRecord = await dbf.readRecords(1);
|
||||
|
||||
//AD2 will always have only 1 row.
|
||||
//Commented lines have been cross referenced with existing partner fields.
|
||||
|
||||
//typeCaster is required as the previous partner sent some of these values toString, and the database was made accordingly rather than keeping their original type.
|
||||
//Alternative is to change the database schema to match the original type.
|
||||
const rawVehData: VehicleRecordInterface = typeCaster(
|
||||
deepLowerCaseKeys(
|
||||
_.pick(rawDBFRecord[0], [
|
||||
//TODO: Add typings for EMS File Formats.
|
||||
"IMPACT_1",
|
||||
"IMPACT_2",
|
||||
"DB_V_CODE",
|
||||
"PLATE_NO",
|
||||
"PLATE_ST",
|
||||
"V_VIN",
|
||||
"V_COND",
|
||||
"V_PROD_DT",
|
||||
"V_MODEL_YR",
|
||||
"V_MAKECODE",
|
||||
"V_MAKEDESC",
|
||||
"V_MODEL",
|
||||
"V_TYPE",
|
||||
"V_BSTYLE",
|
||||
"V_TRIMCODE",
|
||||
"TRIM_COLOR",
|
||||
"V_MLDGCODE",
|
||||
"V_ENGINE",
|
||||
"V_MILEAGE",
|
||||
"V_COLOR",
|
||||
"V_TONE",
|
||||
"V_STAGE",
|
||||
"PAINT_CD1",
|
||||
"PAINT_CD2",
|
||||
"PAINT_CD3",
|
||||
]),
|
||||
),
|
||||
{
|
||||
v_tone: "string",
|
||||
v_stage: "string",
|
||||
},
|
||||
);
|
||||
|
||||
//Apply business logic transfomrations.
|
||||
|
||||
//An old error where the column had an extra underscore.
|
||||
rawVehData.v_make_desc = rawVehData.v_makedesc || rawVehData.v_makecode; //Fallback for US.
|
||||
delete rawVehData.v_makedesc;
|
||||
//An old error where the column had an extra underscore.
|
||||
rawVehData.v_model_desc = rawVehData.v_model;
|
||||
delete rawVehData.v_model;
|
||||
|
||||
//Consolidate Area of Damage.
|
||||
const area_of_damage = {
|
||||
impact1: rawVehData.impact_1 ?? "",
|
||||
impact2: rawVehData.impact_2 ?? "",
|
||||
};
|
||||
delete rawVehData.impact_1;
|
||||
delete rawVehData.impact_2;
|
||||
|
||||
const kmin = rawVehData.v_mileage ?? 0;
|
||||
delete rawVehData.v_mileage;
|
||||
|
||||
//Consolidate Paint Code information.
|
||||
rawVehData.v_paint_codes = {
|
||||
paint_cd1: rawVehData.paint_cd1 ?? "",
|
||||
paint_cd2: rawVehData.paint_cd2 ?? "",
|
||||
paint_cd3: rawVehData.paint_cd3 ?? "",
|
||||
};
|
||||
delete rawVehData.paint_cd1;
|
||||
delete rawVehData.paint_cd2;
|
||||
delete rawVehData.paint_cd3;
|
||||
|
||||
rawVehData.shopid = store.get("app.bodyshop.id");
|
||||
|
||||
//Aggregate the vehicle data to be stamped onto the job record.
|
||||
const jobVehicleData: DecodedVeh = {
|
||||
plate_no: rawVehData.plate_no,
|
||||
plate_st: rawVehData.plate_st,
|
||||
v_vin: rawVehData.v_vin,
|
||||
v_model_yr: rawVehData.v_model_yr,
|
||||
v_make_desc: rawVehData.v_make_desc,
|
||||
v_model_desc: rawVehData.v_model_desc,
|
||||
v_color: rawVehData.v_color,
|
||||
kmin: kmin,
|
||||
area_of_damage: area_of_damage,
|
||||
vehicle: {
|
||||
data: rawVehData,
|
||||
},
|
||||
};
|
||||
|
||||
return jobVehicleData;
|
||||
};
|
||||
|
||||
export default DecodeVeh;
|
||||
47
src/main/decoder/decoder-utils.ts
Normal file
47
src/main/decoder/decoder-utils.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import log from "electron-log/main";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
const findFileCaseInsensitive = async (
|
||||
extensionlessFilePath: string,
|
||||
extensions: string[],
|
||||
): Promise<string | null> => {
|
||||
const directory: string = path.dirname(extensionlessFilePath);
|
||||
try {
|
||||
const matchingFiles = fs.readdirSync(directory).filter((file: string) => {
|
||||
return (
|
||||
extensions.some((ext) =>
|
||||
file.toLowerCase().endsWith(ext.toLowerCase()),
|
||||
) &&
|
||||
path
|
||||
.basename(file, path.extname(file))
|
||||
.toLowerCase()
|
||||
.startsWith(path.basename(extensionlessFilePath).toLowerCase())
|
||||
);
|
||||
});
|
||||
const files: string[] = [];
|
||||
matchingFiles.forEach((file) => {
|
||||
const fullPath = path.join(directory, file);
|
||||
files.push(fullPath);
|
||||
});
|
||||
|
||||
// Return the first matching file if needed
|
||||
if (files.length > 0) {
|
||||
return files[0];
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Failed to read directory ${directory}:`, error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const getFilePathWithoutExtension = (filePath: string): string => {
|
||||
return path.join(
|
||||
path.dirname(filePath),
|
||||
path.basename(filePath, path.extname(filePath)),
|
||||
);
|
||||
};
|
||||
|
||||
export { findFileCaseInsensitive };
|
||||
415
src/main/decoder/decoder.ts
Normal file
415
src/main/decoder/decoder.ts
Normal file
@@ -0,0 +1,415 @@
|
||||
import { platform } from "@electron-toolkit/utils";
|
||||
import { UUID } from "crypto";
|
||||
import { Notification, shell } from "electron";
|
||||
import log from "electron-log/main";
|
||||
import fs from "fs";
|
||||
import _ from "lodash";
|
||||
import path from "path";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import client from "../graphql/graphql-client";
|
||||
import {
|
||||
INSERT_AVAILABLE_JOB_TYPED,
|
||||
InsertAvailableJobResult,
|
||||
QUERY_JOB_BY_CLM_NO_TYPED,
|
||||
QUERY_VEHICLE_BY_VIN_TYPED,
|
||||
QueryJobByClmNoResult,
|
||||
VehicleQueryResult,
|
||||
} from "../graphql/queries";
|
||||
import store from "../store/store";
|
||||
import DecodeAD1 from "./decode-ad1";
|
||||
import { DecodedAd1 } from "./decode-ad1.interface";
|
||||
import DecodeAD2 from "./decode-ad2";
|
||||
import { DecodedAD2 } from "./decode-ad2.interface";
|
||||
import DecodeEnv from "./decode-env";
|
||||
import { DecodedEnv } from "./decode-env.interface";
|
||||
import DecodeLin from "./decode-lin";
|
||||
import { DecodedLin } from "./decode-lin.interface";
|
||||
import DecodePfh from "./decode-pfh";
|
||||
import { DecodedPfh } from "./decode-pfh.interface";
|
||||
import DecodePfl from "./decode-pfl";
|
||||
import { DecodedPfl } from "./decode-pfl.interface";
|
||||
import DecodePfm from "./decode-pfm";
|
||||
import { DecodedPfm } from "./decode-pfm.interface";
|
||||
import DecodePfo from "./decode-pfo";
|
||||
import { DecodedPfo } from "./decode-pfo.interface";
|
||||
import DecodePfp from "./decode-pfp";
|
||||
import { DecodedPfp } from "./decode-pfp.interface";
|
||||
import DecodePft from "./decode-pft";
|
||||
import { DecodedPft } from "./decode-pft.interface";
|
||||
import DecodeStl from "./decode-stl";
|
||||
import { DecodedStl } from "./decode-stl.interface";
|
||||
import DecodeTtl from "./decode-ttl";
|
||||
import { DecodedTtl } from "./decode-ttl.interface";
|
||||
import DecodeVeh from "./decode-veh";
|
||||
import { DecodedVeh } from "./decode-veh.interface";
|
||||
import setAppProgressbar from "../util/setAppProgressBar";
|
||||
import UploadEmsToS3 from "./emsbackup";
|
||||
|
||||
async function ImportJob(filepath: string): Promise<void> {
|
||||
const parsedFilePath = path.parse(filepath);
|
||||
const extensionlessFilePath = path.join(
|
||||
parsedFilePath.dir,
|
||||
parsedFilePath.name,
|
||||
);
|
||||
log.debug("Importing Job", extensionlessFilePath);
|
||||
|
||||
try {
|
||||
await WaitForAllFiles(extensionlessFilePath, requiredExtensions);
|
||||
|
||||
//The below all end up returning parts of the job object.
|
||||
//Some of them return additional info - e.g. owner or vehicle record data at both the job and corresponding table level.
|
||||
setAppProgressbar(0.1);
|
||||
const env: DecodedEnv = await DecodeEnv(extensionlessFilePath);
|
||||
setAppProgressbar(0.15);
|
||||
const ad1: DecodedAd1 = await DecodeAD1(extensionlessFilePath);
|
||||
setAppProgressbar(0.2);
|
||||
const ad2: DecodedAD2 = await DecodeAD2(extensionlessFilePath);
|
||||
setAppProgressbar(0.25);
|
||||
const veh: DecodedVeh = await DecodeVeh(extensionlessFilePath);
|
||||
setAppProgressbar(0.3);
|
||||
const lin: DecodedLin = await DecodeLin(extensionlessFilePath);
|
||||
setAppProgressbar(0.35);
|
||||
const pfh: DecodedPfh = await DecodePfh(extensionlessFilePath);
|
||||
setAppProgressbar(0.4);
|
||||
const pfl: DecodedPfl = await DecodePfl(extensionlessFilePath);
|
||||
setAppProgressbar(0.45);
|
||||
const pft: DecodedPft = await DecodePft(extensionlessFilePath);
|
||||
setAppProgressbar(0.5);
|
||||
const pfm: DecodedPfm = await DecodePfm(extensionlessFilePath);
|
||||
setAppProgressbar(0.55);
|
||||
const pfo: DecodedPfo = await DecodePfo(extensionlessFilePath); // TODO: This will be the `cieca_pfo` object
|
||||
setAppProgressbar(0.6);
|
||||
const stl: DecodedStl = await DecodeStl(extensionlessFilePath); // TODO: This will be the `cieca_stl` object
|
||||
setAppProgressbar(0.65);
|
||||
const ttl: DecodedTtl = await DecodeTtl(extensionlessFilePath);
|
||||
setAppProgressbar(0.7);
|
||||
const pfp: DecodedPfp = await DecodePfp(extensionlessFilePath);
|
||||
setAppProgressbar(0.75);
|
||||
|
||||
const jobObjectUncleaned: RawJobDataObject = {
|
||||
...env,
|
||||
...ad1,
|
||||
...ad2,
|
||||
...veh,
|
||||
...lin,
|
||||
...pfh,
|
||||
...pfl,
|
||||
...pft,
|
||||
...pfm,
|
||||
...pfo,
|
||||
...stl,
|
||||
...ttl,
|
||||
...pfp,
|
||||
shopid: store.get("app.bodyshop.id") as UUID,
|
||||
};
|
||||
|
||||
// Replace owner information with claimant information if necessary
|
||||
const jobObject = ReplaceOwnerInfoWithClaimant(jobObjectUncleaned);
|
||||
setAppProgressbar(0.8);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
// Save jobObject to a timestamped JSON file
|
||||
const timestamp = new Date()
|
||||
.toISOString()
|
||||
.replace(/:/g, "-")
|
||||
.replace(/\..+/, "");
|
||||
const fileName = `job_${timestamp}_${parsedFilePath.name}.json`;
|
||||
const logsDir = path.join(process.cwd(), "logs");
|
||||
|
||||
// Create logs directory if it doesn't exist
|
||||
if (!fs.existsSync(logsDir)) {
|
||||
fs.mkdirSync(logsDir, { recursive: true });
|
||||
}
|
||||
|
||||
const filePath = path.join(logsDir, fileName);
|
||||
fs.writeFileSync(filePath, JSON.stringify(jobObject, null, 2), "utf8");
|
||||
log.info(`Job data saved to: ${filePath}`);
|
||||
}
|
||||
|
||||
const newAvailableJob: AvailableJobSchema = {
|
||||
uploaded_by: store.get("user.email"),
|
||||
bodyshopid: store.get("app.bodyshop.id"),
|
||||
cieca_id: jobObject.ciecaid,
|
||||
est_data: jobObject,
|
||||
ownr_name: `${jobObject.ownr_fn} ${jobObject.ownr_ln} ${jobObject.ownr_co_nm}`,
|
||||
ins_co_nm: jobObject.ins_co_nm,
|
||||
vehicle_info: `${jobObject.v_model_yr} ${jobObject.v_make_desc} ${jobObject.v_model_desc}`,
|
||||
clm_no: jobObject.clm_no,
|
||||
clm_amt: jobObject.clm_total,
|
||||
// source_system: jobObject.source_system, //TODO: Add back source system if needed.
|
||||
issupplement: false,
|
||||
jobid: null,
|
||||
};
|
||||
setAppProgressbar(0.85);
|
||||
|
||||
const existingVehicleRecord: VehicleQueryResult = await client.request(
|
||||
QUERY_VEHICLE_BY_VIN_TYPED,
|
||||
{
|
||||
vin: jobObject.v_vin,
|
||||
},
|
||||
);
|
||||
|
||||
if (existingVehicleRecord.vehicles.length > 0) {
|
||||
delete newAvailableJob.est_data.vehicle;
|
||||
newAvailableJob.est_data.vehicleid = existingVehicleRecord.vehicles[0].id;
|
||||
}
|
||||
|
||||
console.log("Available Job record to upload;", newAvailableJob);
|
||||
|
||||
setAppProgressbar(0.95);
|
||||
const existingJobRecord: QueryJobByClmNoResult = await client.request(
|
||||
QUERY_JOB_BY_CLM_NO_TYPED,
|
||||
{ clm_no: jobObject.clm_no },
|
||||
);
|
||||
|
||||
if (existingJobRecord.jobs.length > 0) {
|
||||
newAvailableJob.issupplement = true;
|
||||
newAvailableJob.jobid = existingJobRecord.jobs[0].id;
|
||||
}
|
||||
|
||||
const insertRecordResult: InsertAvailableJobResult = await client.request(
|
||||
INSERT_AVAILABLE_JOB_TYPED,
|
||||
{
|
||||
jobInput: [newAvailableJob],
|
||||
},
|
||||
);
|
||||
setAppProgressbar(-1);
|
||||
const uploadNotification = new Notification({
|
||||
title: "Job Imported",
|
||||
//subtitle: `${newAvailableJob.ownr_name} - ${newAvailableJob.vehicle_info}`,
|
||||
body: `${newAvailableJob.ownr_name} - ${newAvailableJob.vehicle_info}. Click to view.`,
|
||||
actions: [{ text: "View Job", type: "button" }],
|
||||
});
|
||||
uploadNotification.on("click", () => {
|
||||
shell.openExternal(
|
||||
`${
|
||||
store.get("app.isTest")
|
||||
? import.meta.env.VITE_FE_URL_TEST
|
||||
: import.meta.env.VITE_FE_URL
|
||||
}/manage/available`,
|
||||
);
|
||||
});
|
||||
uploadNotification.show();
|
||||
|
||||
log.debug("Job inserted", insertRecordResult);
|
||||
|
||||
UploadEmsToS3({
|
||||
extensionlessFilePath,
|
||||
bodyshopid: newAvailableJob.bodyshopid,
|
||||
ciecaid: jobObject.ciecaid ?? "",
|
||||
clm_no: jobObject.clm_no ?? "",
|
||||
ownr_ln: jobObject.ownr_ln ?? "",
|
||||
});
|
||||
} catch (error) {
|
||||
log.error("Error encountered while decoding job. ", errorTypeCheck(error));
|
||||
const uploadNotificationFailure = new Notification({
|
||||
title: "Job Upload Failure",
|
||||
body: errorTypeCheck(error).message, //TODO: Remove after debug.
|
||||
});
|
||||
|
||||
uploadNotificationFailure.show();
|
||||
}
|
||||
}
|
||||
|
||||
export default ImportJob;
|
||||
|
||||
export interface RawJobDataObject
|
||||
extends DecodedEnv,
|
||||
DecodedAd1,
|
||||
DecodedAD2,
|
||||
DecodedVeh,
|
||||
DecodedLin,
|
||||
DecodedPfh,
|
||||
DecodedPfl,
|
||||
DecodedPft,
|
||||
DecodedPfm,
|
||||
DecodedPfo,
|
||||
DecodedStl,
|
||||
DecodedTtl,
|
||||
DecodedPfp {
|
||||
vehicleid?: UUID;
|
||||
shopid: UUID;
|
||||
}
|
||||
|
||||
export interface AvailableJobSchema {
|
||||
uploaded_by: string;
|
||||
bodyshopid: UUID;
|
||||
cieca_id?: string;
|
||||
est_data: RawJobDataObject;
|
||||
ownr_name: string;
|
||||
ins_co_nm?: string;
|
||||
vehicle_info: string;
|
||||
clm_no?: string;
|
||||
clm_amt: number;
|
||||
source_system?: string | null;
|
||||
issupplement: boolean;
|
||||
jobid: UUID | null;
|
||||
}
|
||||
|
||||
async function WaitForAllFiles(
|
||||
baseFilePath: string,
|
||||
requiredExtensions: string[],
|
||||
maxRetries: number = 5,
|
||||
backoffMs: number = 1000,
|
||||
): Promise<void> {
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
//Get all files in directory if Mac.
|
||||
let filesInDir: string[] = [];
|
||||
if (platform.isMacOS) {
|
||||
const dir: string = path.dirname(baseFilePath);
|
||||
filesInDir = fs.readdirSync(dir).map((file) => file.toLowerCase());
|
||||
}
|
||||
|
||||
const missingFiles = requiredExtensions.filter((ext) => {
|
||||
const filePath: string = `${baseFilePath}.${ext}`;
|
||||
const filePathA: string = `${baseFilePath}A.${ext}`;
|
||||
const filePathB: string = `${baseFilePath}B.${ext}`;
|
||||
const filePathV: string = `${baseFilePath}V.${ext}`;
|
||||
|
||||
if (!platform.isWindows) {
|
||||
// Case-insensitive check for macOS/Linux
|
||||
const baseName: string = path.basename(baseFilePath);
|
||||
|
||||
return !(
|
||||
filesInDir.includes(`${baseName}.${ext}`.toLowerCase()) ||
|
||||
filesInDir.includes(`${baseName}A.${ext}`.toLowerCase()) ||
|
||||
filesInDir.includes(`${baseName}B.${ext}`.toLowerCase()) ||
|
||||
filesInDir.includes(`${baseName}V.${ext}`.toLowerCase())
|
||||
);
|
||||
} else {
|
||||
// Case-sensitive check for other platforms
|
||||
return !(
|
||||
fs.existsSync(filePath) ||
|
||||
fs.existsSync(filePathA) ||
|
||||
fs.existsSync(filePathB) ||
|
||||
fs.existsSync(filePathV)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (missingFiles.length === 0) {
|
||||
return; // All files are present
|
||||
}
|
||||
|
||||
log.debug(
|
||||
`Attempt ${attempt}: Missing files: ${missingFiles.join(", ")}. Retrying in ${backoffMs}ms...`,
|
||||
);
|
||||
|
||||
if (attempt < maxRetries) {
|
||||
await new Promise((resolve) => setTimeout(resolve, backoffMs));
|
||||
backoffMs *= 2; // Exponential backoff
|
||||
} else {
|
||||
throw new Error(
|
||||
`The set of files is not valid. Missing files for CIECA ID ${baseFilePath}: ${missingFiles.join(", ")}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const requiredExtensions = [
|
||||
"env",
|
||||
"ad1",
|
||||
"ad2",
|
||||
"veh",
|
||||
"lin",
|
||||
"pfh",
|
||||
"pfl",
|
||||
"pft",
|
||||
"pfm",
|
||||
"pfo",
|
||||
"stl",
|
||||
"ttl",
|
||||
"pfp",
|
||||
];
|
||||
|
||||
export function ReplaceOwnerInfoWithClaimant<
|
||||
T extends Partial<
|
||||
Pick<
|
||||
RawJobDataObject,
|
||||
| "ownr_ln"
|
||||
| "ownr_fn"
|
||||
| "ownr_co_nm"
|
||||
| "ownr_title"
|
||||
| "ownr_co_nm"
|
||||
| "ownr_addr1"
|
||||
| "ownr_addr2"
|
||||
| "ownr_city"
|
||||
| "ownr_st"
|
||||
| "ownr_zip"
|
||||
| "ownr_ctry"
|
||||
| "ownr_ph1"
|
||||
| "ownr_ph2"
|
||||
| "ownr_ea"
|
||||
| "clmt_ln"
|
||||
| "clmt_fn"
|
||||
| "clmt_title"
|
||||
| "clmt_co_nm"
|
||||
| "clmt_addr1"
|
||||
| "clmt_addr2"
|
||||
| "clmt_city"
|
||||
| "clmt_st"
|
||||
| "clmt_zip"
|
||||
| "clmt_ctry"
|
||||
| "clmt_ph1"
|
||||
| "clmt_ph2"
|
||||
| "clmt_ea"
|
||||
| "owner"
|
||||
>
|
||||
>,
|
||||
>(jobObject: T): T {
|
||||
// In some scenarios, the owner information is missing. So we use the claimant instead.
|
||||
// We pull the claimant info for this, but we don't store it in our system, so it needs to be deleted regardless.
|
||||
if (
|
||||
_.isEmpty(jobObject.ownr_ln) &&
|
||||
_.isEmpty(jobObject.ownr_fn) &&
|
||||
_.isEmpty(jobObject.ownr_co_nm)
|
||||
) {
|
||||
jobObject.ownr_ln = jobObject.clmt_ln;
|
||||
jobObject.ownr_fn = jobObject.clmt_fn;
|
||||
jobObject.ownr_title = jobObject.clmt_title;
|
||||
jobObject.ownr_co_nm = jobObject.clmt_co_nm;
|
||||
jobObject.ownr_addr1 = jobObject.clmt_addr1;
|
||||
jobObject.ownr_addr2 = jobObject.clmt_addr2;
|
||||
jobObject.ownr_city = jobObject.clmt_city;
|
||||
jobObject.ownr_st = jobObject.clmt_st;
|
||||
jobObject.ownr_zip = jobObject.clmt_zip;
|
||||
jobObject.ownr_ctry = jobObject.clmt_ctry;
|
||||
jobObject.ownr_ph1 = jobObject.clmt_ph1;
|
||||
jobObject.ownr_ph2 = jobObject.clmt_ph2;
|
||||
jobObject.ownr_ea = jobObject.clmt_ea;
|
||||
|
||||
// Ensure the owner and owner.data fields exist before assigning values
|
||||
if (jobObject.owner?.data) {
|
||||
jobObject.owner.data.ownr_ln = jobObject.clmt_ln;
|
||||
jobObject.owner.data.ownr_fn = jobObject.clmt_fn;
|
||||
jobObject.owner.data.ownr_title = jobObject.clmt_title;
|
||||
jobObject.owner.data.ownr_co_nm = jobObject.clmt_co_nm;
|
||||
jobObject.owner.data.ownr_addr1 = jobObject.clmt_addr1;
|
||||
jobObject.owner.data.ownr_addr2 = jobObject.clmt_addr2;
|
||||
jobObject.owner.data.ownr_city = jobObject.clmt_city;
|
||||
jobObject.owner.data.ownr_st = jobObject.clmt_st;
|
||||
jobObject.owner.data.ownr_zip = jobObject.clmt_zip;
|
||||
jobObject.owner.data.ownr_ctry = jobObject.clmt_ctry;
|
||||
jobObject.owner.data.ownr_ph1 = jobObject.clmt_ph1;
|
||||
jobObject.owner.data.ownr_ph2 = jobObject.clmt_ph2;
|
||||
jobObject.owner.data.ownr_ea = jobObject.clmt_ea;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the claimant info as it's not needed.
|
||||
delete jobObject.clmt_ln;
|
||||
delete jobObject.clmt_fn;
|
||||
delete jobObject.clmt_title;
|
||||
delete jobObject.clmt_co_nm;
|
||||
delete jobObject.clmt_addr1;
|
||||
delete jobObject.clmt_addr2;
|
||||
delete jobObject.clmt_city;
|
||||
delete jobObject.clmt_st;
|
||||
delete jobObject.clmt_zip;
|
||||
delete jobObject.clmt_ctry;
|
||||
delete jobObject.clmt_ph1;
|
||||
delete jobObject.clmt_ph2;
|
||||
delete jobObject.clmt_ea;
|
||||
|
||||
return jobObject;
|
||||
}
|
||||
104
src/main/decoder/emsbackup.ts
Normal file
104
src/main/decoder/emsbackup.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import axios from "axios";
|
||||
import archiver from "archiver";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { UUID } from "crypto";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import stream from "stream";
|
||||
import { getTokenFromRenderer } from "../graphql/graphql-client";
|
||||
import store from "../store/store";
|
||||
|
||||
async function UploadEmsToS3({
|
||||
extensionlessFilePath,
|
||||
bodyshopid,
|
||||
clm_no,
|
||||
ciecaid,
|
||||
ownr_ln,
|
||||
}: {
|
||||
extensionlessFilePath: string;
|
||||
bodyshopid: UUID;
|
||||
clm_no: string;
|
||||
ciecaid: string;
|
||||
ownr_ln: string;
|
||||
}): Promise<boolean> {
|
||||
// This function is a placeholder for the actual upload logic
|
||||
try {
|
||||
const directory = path.dirname(extensionlessFilePath);
|
||||
const baseFilename = path.basename(extensionlessFilePath);
|
||||
|
||||
// Find all files in the directory that start with the base filename
|
||||
const filesToZip = fs
|
||||
.readdirSync(directory)
|
||||
.filter((file) => file.startsWith(baseFilename))
|
||||
.map((file) => path.join(directory, file));
|
||||
|
||||
if (filesToZip.length === 0) {
|
||||
console.error("No files found to zip.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a zip archive in memory
|
||||
const archive = archiver("zip", { zlib: { level: 9 } });
|
||||
const zipBuffer = await new Promise<Buffer>((resolve, reject) => {
|
||||
const buffers: Buffer[] = [];
|
||||
const writableStream = new stream.Writable({
|
||||
write(chunk, _encoding, callback) {
|
||||
buffers.push(chunk);
|
||||
callback();
|
||||
},
|
||||
});
|
||||
|
||||
writableStream.on("finish", () => resolve(Buffer.concat(buffers)));
|
||||
writableStream.on("error", reject);
|
||||
|
||||
archive.pipe(writableStream);
|
||||
|
||||
// Append files to the archive
|
||||
filesToZip.forEach((file) => {
|
||||
archive.file(file, { name: path.basename(file) });
|
||||
});
|
||||
|
||||
archive.finalize();
|
||||
});
|
||||
|
||||
// Get the presigned URL from the server
|
||||
const presignedUrlResponse = await axios.post(
|
||||
`${
|
||||
store.get("app.isTest")
|
||||
? import.meta.env.VITE_API_TEST_URL
|
||||
: import.meta.env.VITE_API_URL
|
||||
}/emsupload`,
|
||||
{
|
||||
bodyshopid,
|
||||
ciecaid,
|
||||
clm_no,
|
||||
ownr_ln,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${await getTokenFromRenderer()}`,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const presignedUrl = presignedUrlResponse.data?.presignedUrl;
|
||||
if (!presignedUrl) {
|
||||
console.error("Failed to retrieve presigned URL.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upload the zip file to S3 using the presigned URL
|
||||
await axios.put(presignedUrl, zipBuffer, {
|
||||
headers: {
|
||||
"Content-Type": "application/zip",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error uploading EMS to S3:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // Return true if the upload is successful
|
||||
}
|
||||
|
||||
export default UploadEmsToS3;
|
||||
57
src/main/decoder/folder-scan.ts
Normal file
57
src/main/decoder/folder-scan.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import path from "path";
|
||||
import { GetAllEnvFiles } from "../watcher/watcher";
|
||||
import DecodeAD1 from "./decode-ad1";
|
||||
import DecodeAD2 from "./decode-ad2";
|
||||
import DecodeEnv from "./decode-env";
|
||||
import DecodeVeh from "./decode-veh";
|
||||
import { ReplaceOwnerInfoWithClaimant } from "./decoder";
|
||||
|
||||
const folderScan = async (): Promise<FolderScanResult[]> => {
|
||||
//Get all ENV files for watched paths.
|
||||
const allEnvFiles = GetAllEnvFiles();
|
||||
//Run a simplified decode on them
|
||||
const returnedFiles: FolderScanResult[] = [];
|
||||
|
||||
for (const filepath of allEnvFiles) {
|
||||
const parsedFilePath = path.parse(filepath);
|
||||
const extensionlessFilePath = path.join(
|
||||
parsedFilePath.dir,
|
||||
parsedFilePath.name,
|
||||
);
|
||||
|
||||
const rawJob = {
|
||||
...(await DecodeEnv(extensionlessFilePath)),
|
||||
...(await DecodeAD1(extensionlessFilePath)),
|
||||
...(await DecodeAD2(extensionlessFilePath)),
|
||||
...(await DecodeVeh(extensionlessFilePath)),
|
||||
};
|
||||
const job = ReplaceOwnerInfoWithClaimant(rawJob);
|
||||
|
||||
const scanResult: FolderScanResult = {
|
||||
id: job.ciecaid,
|
||||
filepath: filepath,
|
||||
cieca_id: job.ciecaid,
|
||||
clm_no: job.clm_no,
|
||||
owner: `${job.ownr_fn} ${job.ownr_ln} ${job.ownr_co_nm}`.trim(),
|
||||
vehicle:
|
||||
`${job.vehicle?.data.v_model_yr} ${job.vehicle?.data.v_make_desc} ${job.vehicle?.data.v_model_desc}`.trim(),
|
||||
ins_co_nm: job.ins_co_nm,
|
||||
};
|
||||
|
||||
returnedFiles.push(scanResult);
|
||||
}
|
||||
//Build up the object and return it
|
||||
return returnedFiles;
|
||||
};
|
||||
|
||||
export interface FolderScanResult {
|
||||
id?: string;
|
||||
filepath: string;
|
||||
cieca_id?: string;
|
||||
clm_no?: string;
|
||||
owner: string;
|
||||
ins_co_nm?: string;
|
||||
vehicle: string;
|
||||
}
|
||||
|
||||
export default folderScan;
|
||||
158
src/main/ems-parts-order/ems-parts-order-generate-ad1.ts
Normal file
158
src/main/ems-parts-order/ems-parts-order-generate-ad1.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { ad1FieldLineDescriptors } from "../util/ems-interface/fielddescriptors/ad1-field-descriptors";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGenerateAd1File = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const records = [
|
||||
{
|
||||
INS_CO_ID: partsOrder.job.ins_co_nm,
|
||||
INS_CO_NM: partsOrder.job.ins_co_nm,
|
||||
INS_ADDR1: partsOrder.job.ins_addr1,
|
||||
INS_ADDR2: partsOrder.job.ins_addr2,
|
||||
INS_CITY: partsOrder.job.ins_city,
|
||||
INS_ST: partsOrder.job.ins_st,
|
||||
INS_ZIP: partsOrder.job.ins_zip,
|
||||
INS_CTRY: partsOrder.job.ins_ctry,
|
||||
INS_PH1: partsOrder.job.ins_ph1,
|
||||
INS_PH1X: partsOrder.job.ins_ph1x,
|
||||
INS_PH2: partsOrder.job.ins_ph2,
|
||||
INS_PH2X: partsOrder.job.ins_ph2x,
|
||||
INS_FAX: partsOrder.job.ins_fax,
|
||||
INS_FAXX: partsOrder.job.ins_faxx,
|
||||
INS_CT_LN: partsOrder.job.ins_ct_ln,
|
||||
INS_CT_FN: partsOrder.job.ins_ct_fn,
|
||||
INS_TITLE: partsOrder.job.ins_title,
|
||||
INS_CT_PH: partsOrder.job.ins_ct_ph,
|
||||
INS_CT_PHX: partsOrder.job.ins_ct_phx,
|
||||
INS_EA: partsOrder.job.ins_ea,
|
||||
INS_MEMO: partsOrder.job.ins_memo,
|
||||
POLICY_NO: partsOrder.job.policy_no,
|
||||
DED_AMT: partsOrder.job.ded_amt,
|
||||
DED_STATUS: partsOrder.job.ded_status,
|
||||
ASGN_NO: partsOrder.job.asgn_no,
|
||||
ASGN_DATE: partsOrder.job.asgn_date
|
||||
? new Date(partsOrder.job.asgn_date)
|
||||
: null,
|
||||
ASGN_TYPE: partsOrder.job.asgn_type,
|
||||
CLM_NO: partsOrder.job.clm_no,
|
||||
CLM_OFC_ID: partsOrder.job.clm_ofc_id,
|
||||
CLM_OFC_NM: partsOrder.job.clm_ofc_nm,
|
||||
CLM_ADDR1: partsOrder.job.clm_addr1,
|
||||
CLM_ADDR2: partsOrder.job.clm_addr2,
|
||||
CLM_CITY: partsOrder.job.clm_city,
|
||||
CLM_ST: partsOrder.job.clm_st,
|
||||
CLM_ZIP: partsOrder.job.clm_zip,
|
||||
CLM_CTRY: partsOrder.job.clm_ctry,
|
||||
CLM_PH1: partsOrder.job.clm_ph1,
|
||||
CLM_PH1X: partsOrder.job.clm_ph1x,
|
||||
CLM_PH2: partsOrder.job.clm_ph2,
|
||||
CLM_PH2X: partsOrder.job.clm_ph2x,
|
||||
CLM_FAX: partsOrder.job.clm_fax,
|
||||
CLM_FAXX: partsOrder.job.clm_faxx,
|
||||
CLM_CT_LN: partsOrder.job.clm_ct_ln,
|
||||
CLM_CT_FN: partsOrder.job.clm_ct_fn,
|
||||
CLM_TITLE: partsOrder.job.clm_title,
|
||||
CLM_CT_PH: partsOrder.job.clm_ct_ph,
|
||||
CLM_CT_PHX: partsOrder.job.clm_ct_phx,
|
||||
CLM_EA: partsOrder.job.clm_ea,
|
||||
PAYEE_NMS: partsOrder.job.payee_nms,
|
||||
PAY_TYPE: partsOrder.job.pay_type,
|
||||
PAY_DATE: partsOrder.job.pay_date,
|
||||
PAY_CHKNM: null, // Explicitly set to null as in original code
|
||||
PAY_AMT: null, // Explicitly set to null as in original code
|
||||
PAY_MEMO: partsOrder.job.pay_memo,
|
||||
AGT_CO_ID: partsOrder.job.agt_co_id,
|
||||
AGT_CO_NM: partsOrder.job.agt_co_nm,
|
||||
AGT_ADDR1: partsOrder.job.agt_addr1,
|
||||
AGT_ADDR2: partsOrder.job.agt_addr2,
|
||||
AGT_CITY: partsOrder.job.agt_city,
|
||||
AGT_ST: partsOrder.job.agt_st,
|
||||
AGT_ZIP: partsOrder.job.agt_zip,
|
||||
AGT_CTRY: partsOrder.job.agt_ctry,
|
||||
AGT_PH1: partsOrder.job.agt_ph1,
|
||||
AGT_PH1X: partsOrder.job.agt_ph1x,
|
||||
AGT_PH2: partsOrder.job.agt_ph2,
|
||||
AGT_PH2X: partsOrder.job.agt_ph2x,
|
||||
AGT_FAX: partsOrder.job.agt_fax,
|
||||
AGT_FAXX: partsOrder.job.agt_faxx,
|
||||
AGT_CT_LN: partsOrder.job.agt_ct_ln,
|
||||
AGT_CT_FN: partsOrder.job.agt_ct_fn,
|
||||
AGT_CT_PH: partsOrder.job.agt_ct_ph,
|
||||
AGT_CT_PHX: partsOrder.job.agt_ct_phx,
|
||||
AGT_EA: partsOrder.job.agt_ea,
|
||||
AGT_LIC_NO: partsOrder.job.agt_lic_no,
|
||||
LOSS_DATE: partsOrder.job.loss_date
|
||||
? new Date(partsOrder.job.loss_date)
|
||||
: null,
|
||||
LOSS_CAT: null, // Explicitly set to null as in original code
|
||||
LOSS_TYPE: null, // Explicitly set to null as in original code
|
||||
LOSS_DESC: partsOrder.job.loss_desc,
|
||||
THEFT_IND: null, // Explicitly set to null as in original code
|
||||
CAT_NO: partsOrder.job.cat_no,
|
||||
TLOS_IND: null, // Explicitly set to null as in original code
|
||||
LOSS_MEMO: partsOrder.job.loss_memo,
|
||||
CUST_PR: partsOrder.job.cust_pr,
|
||||
INSD_LN: partsOrder.job.insd_ln,
|
||||
INSD_FN: partsOrder.job.insd_fn,
|
||||
INSD_TITLE: partsOrder.job.insd_title,
|
||||
INSD_CO_NM: partsOrder.job.insd_co_nm,
|
||||
INSD_ADDR1: partsOrder.job.insd_addr1,
|
||||
INSD_ADDR2: partsOrder.job.insd_addr2,
|
||||
INSD_CITY: partsOrder.job.insd_city,
|
||||
INSD_ST: partsOrder.job.insd_st,
|
||||
INSD_ZIP: partsOrder.job.insd_zip,
|
||||
INSD_CTRY: partsOrder.job.insd_ctry,
|
||||
INSD_PH1: partsOrder.job.insd_ph1,
|
||||
INSD_PH1X: partsOrder.job.insd_ph1x,
|
||||
INSD_PH2: partsOrder.job.insd_ph2,
|
||||
INSD_PH2X: partsOrder.job.insd_ph2x,
|
||||
INSD_FAX: partsOrder.job.insd_fax,
|
||||
INSD_FAXX: partsOrder.job.insd_faxx,
|
||||
INSD_EA: partsOrder.job.insd_ea,
|
||||
OWNR_LN: partsOrder.job.ownr_ln,
|
||||
OWNR_FN: partsOrder.job.ownr_fn,
|
||||
OWNR_TITLE: partsOrder.job.ownr_title,
|
||||
OWNR_CO_NM: partsOrder.job.ownr_co_nm,
|
||||
OWNR_ADDR1: partsOrder.job.ownr_addr1,
|
||||
OWNR_ADDR2: partsOrder.job.ownr_addr2,
|
||||
OWNR_CITY: partsOrder.job.ownr_city,
|
||||
OWNR_ST: partsOrder.job.ownr_st,
|
||||
OWNR_ZIP: partsOrder.job.ownr_zip,
|
||||
OWNR_CTRY: partsOrder.job.ownr_ctry,
|
||||
OWNR_PH1: partsOrder.job.ownr_ph1,
|
||||
OWNR_PH1X: partsOrder.job.ownr_ph1x,
|
||||
OWNR_PH2: partsOrder.job.ownr_ph2,
|
||||
OWNR_PH2X: partsOrder.job.ownr_ph2x,
|
||||
OWNR_FAX: partsOrder.job.ownr_fax,
|
||||
OWNR_FAXX: partsOrder.job.ownr_faxx,
|
||||
OWNR_EA: partsOrder.job.ownr_ea,
|
||||
},
|
||||
];
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.AD1`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.AD1`),
|
||||
ad1FieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} AD1 file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating AD1 file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGenerateAd1File;
|
||||
158
src/main/ems-parts-order/ems-parts-order-generate-ad2.ts
Normal file
158
src/main/ems-parts-order/ems-parts-order-generate-ad2.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { ad2FieldLineDescriptors } from "../util/ems-interface/fielddescriptors/ad2-field-descriptors";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGenerateAd2File = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const records = [
|
||||
{
|
||||
INS_CO_ID: partsOrder.job.ins_co_nm,
|
||||
INS_CO_NM: partsOrder.job.ins_co_nm,
|
||||
INS_ADDR1: partsOrder.job.ins_addr1,
|
||||
INS_ADDR2: partsOrder.job.ins_addr2,
|
||||
INS_CITY: partsOrder.job.ins_city,
|
||||
INS_ST: partsOrder.job.ins_st,
|
||||
INS_ZIP: partsOrder.job.ins_zip,
|
||||
INS_CTRY: partsOrder.job.ins_ctry,
|
||||
INS_PH1: partsOrder.job.ins_ph1,
|
||||
INS_PH1X: partsOrder.job.ins_ph1x,
|
||||
INS_PH2: partsOrder.job.ins_ph2,
|
||||
INS_PH2X: partsOrder.job.ins_ph2x,
|
||||
INS_FAX: partsOrder.job.ins_fax,
|
||||
INS_FAXX: partsOrder.job.ins_faxx,
|
||||
INS_CT_LN: partsOrder.job.ins_ct_ln,
|
||||
INS_CT_FN: partsOrder.job.ins_ct_fn,
|
||||
INS_TITLE: partsOrder.job.ins_title,
|
||||
INS_CT_PH: partsOrder.job.ins_ct_ph,
|
||||
INS_CT_PHX: partsOrder.job.ins_ct_phx,
|
||||
INS_EA: partsOrder.job.ins_ea,
|
||||
INS_MEMO: partsOrder.job.ins_memo,
|
||||
POLICY_NO: partsOrder.job.policy_no,
|
||||
DED_AMT: partsOrder.job.ded_amt,
|
||||
DED_STATUS: partsOrder.job.ded_status,
|
||||
ASGN_NO: partsOrder.job.asgn_no,
|
||||
ASGN_DATE: partsOrder.job.asgn_date
|
||||
? new Date(partsOrder.job.asgn_date)
|
||||
: null,
|
||||
ASGN_TYPE: partsOrder.job.asgn_type,
|
||||
CLM_NO: partsOrder.job.clm_no,
|
||||
CLM_OFC_ID: partsOrder.job.clm_ofc_id,
|
||||
CLM_OFC_NM: partsOrder.job.clm_ofc_nm,
|
||||
CLM_ADDR1: partsOrder.job.clm_addr1,
|
||||
CLM_ADDR2: partsOrder.job.clm_addr2,
|
||||
CLM_CITY: partsOrder.job.clm_city,
|
||||
CLM_ST: partsOrder.job.clm_st,
|
||||
CLM_ZIP: partsOrder.job.clm_zip,
|
||||
CLM_CTRY: partsOrder.job.clm_ctry,
|
||||
CLM_PH1: partsOrder.job.clm_ph1,
|
||||
CLM_PH1X: partsOrder.job.clm_ph1x,
|
||||
CLM_PH2: partsOrder.job.clm_ph2,
|
||||
CLM_PH2X: partsOrder.job.clm_ph2x,
|
||||
CLM_FAX: partsOrder.job.clm_fax,
|
||||
CLM_FAXX: partsOrder.job.clm_faxx,
|
||||
CLM_CT_LN: partsOrder.job.clm_ct_ln,
|
||||
CLM_CT_FN: partsOrder.job.clm_ct_fn,
|
||||
CLM_TITLE: partsOrder.job.clm_title,
|
||||
CLM_CT_PH: partsOrder.job.clm_ct_ph,
|
||||
CLM_CT_PHX: partsOrder.job.clm_ct_phx,
|
||||
CLM_EA: partsOrder.job.clm_ea,
|
||||
PAYEE_NMS: partsOrder.job.payee_nms,
|
||||
PAY_TYPE: partsOrder.job.pay_type,
|
||||
PAY_DATE: partsOrder.job.pay_date,
|
||||
PAY_CHKNM: null, // Explicitly set to null as in original code
|
||||
PAY_AMT: null, // Explicitly set to null as in original code
|
||||
PAY_MEMO: partsOrder.job.pay_memo,
|
||||
AGT_CO_ID: partsOrder.job.agt_co_id,
|
||||
AGT_CO_NM: partsOrder.job.agt_co_nm,
|
||||
AGT_ADDR1: partsOrder.job.agt_addr1,
|
||||
AGT_ADDR2: partsOrder.job.agt_addr2,
|
||||
AGT_CITY: partsOrder.job.agt_city,
|
||||
AGT_ST: partsOrder.job.agt_st,
|
||||
AGT_ZIP: partsOrder.job.agt_zip,
|
||||
AGT_CTRY: partsOrder.job.agt_ctry,
|
||||
AGT_PH1: partsOrder.job.agt_ph1,
|
||||
AGT_PH1X: partsOrder.job.agt_ph1x,
|
||||
AGT_PH2: partsOrder.job.agt_ph2,
|
||||
AGT_PH2X: partsOrder.job.agt_ph2x,
|
||||
AGT_FAX: partsOrder.job.agt_fax,
|
||||
AGT_FAXX: partsOrder.job.agt_faxx,
|
||||
AGT_CT_LN: partsOrder.job.agt_ct_ln,
|
||||
AGT_CT_FN: partsOrder.job.agt_ct_fn,
|
||||
AGT_CT_PH: partsOrder.job.agt_ct_ph,
|
||||
AGT_CT_PHX: partsOrder.job.agt_ct_phx,
|
||||
AGT_EA: partsOrder.job.agt_ea,
|
||||
AGT_LIC_NO: partsOrder.job.agt_lic_no,
|
||||
LOSS_DATE: partsOrder.job.loss_date
|
||||
? new Date(partsOrder.job.loss_date)
|
||||
: null,
|
||||
LOSS_CAT: null, // Explicitly set to null as in original code
|
||||
LOSS_TYPE: null, // Explicitly set to null as in original code
|
||||
LOSS_DESC: partsOrder.job.loss_desc,
|
||||
THEFT_IND: null, // Explicitly set to null as in original code
|
||||
CAT_NO: partsOrder.job.cat_no,
|
||||
TLOS_IND: null, // Explicitly set to null as in original code
|
||||
LOSS_MEMO: partsOrder.job.loss_memo,
|
||||
CUST_PR: partsOrder.job.cust_pr,
|
||||
INSD_LN: partsOrder.job.insd_ln,
|
||||
INSD_FN: partsOrder.job.insd_fn,
|
||||
INSD_TITLE: partsOrder.job.insd_title,
|
||||
INSD_CO_NM: partsOrder.job.insd_co_nm,
|
||||
INSD_ADDR1: partsOrder.job.insd_addr1,
|
||||
INSD_ADDR2: partsOrder.job.insd_addr2,
|
||||
INSD_CITY: partsOrder.job.insd_city,
|
||||
INSD_ST: partsOrder.job.insd_st,
|
||||
INSD_ZIP: partsOrder.job.insd_zip,
|
||||
INSD_CTRY: partsOrder.job.insd_ctry,
|
||||
INSD_PH1: partsOrder.job.insd_ph1,
|
||||
INSD_PH1X: partsOrder.job.insd_ph1x,
|
||||
INSD_PH2: partsOrder.job.insd_ph2,
|
||||
INSD_PH2X: partsOrder.job.insd_ph2x,
|
||||
INSD_FAX: partsOrder.job.insd_fax,
|
||||
INSD_FAXX: partsOrder.job.insd_faxx,
|
||||
INSD_EA: partsOrder.job.insd_ea,
|
||||
OWNR_LN: partsOrder.job.ownr_ln,
|
||||
OWNR_FN: partsOrder.job.ownr_fn,
|
||||
OWNR_TITLE: partsOrder.job.ownr_title,
|
||||
OWNR_CO_NM: partsOrder.job.ownr_co_nm,
|
||||
OWNR_ADDR1: partsOrder.job.ownr_addr1,
|
||||
OWNR_ADDR2: partsOrder.job.ownr_addr2,
|
||||
OWNR_CITY: partsOrder.job.ownr_city,
|
||||
OWNR_ST: partsOrder.job.ownr_st,
|
||||
OWNR_ZIP: partsOrder.job.ownr_zip,
|
||||
OWNR_CTRY: partsOrder.job.ownr_ctry,
|
||||
OWNR_PH1: partsOrder.job.ownr_ph1,
|
||||
OWNR_PH1X: partsOrder.job.ownr_ph1x,
|
||||
OWNR_PH2: partsOrder.job.ownr_ph2,
|
||||
OWNR_PH2X: partsOrder.job.ownr_ph2x,
|
||||
OWNR_FAX: partsOrder.job.ownr_fax,
|
||||
OWNR_FAXX: partsOrder.job.ownr_faxx,
|
||||
OWNR_EA: partsOrder.job.ownr_ea,
|
||||
},
|
||||
];
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.AD2`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.AD2`),
|
||||
ad2FieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} AD2 file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating AD2 file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGenerateAd2File;
|
||||
47
src/main/ems-parts-order/ems-parts-order-generate-env.ts
Normal file
47
src/main/ems-parts-order/ems-parts-order-generate-env.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { envFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/env-field-descriptor";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGenerateEnvFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const records = [
|
||||
{
|
||||
EST_SYSTEM: "C",
|
||||
RO_ID: partsOrder.job.ro_number,
|
||||
ESTFILE_ID: partsOrder.job.ciecaid,
|
||||
STATUS: false,
|
||||
INCL_ADMIN: true,
|
||||
INCL_VEH: true,
|
||||
INCL_EST: true,
|
||||
INCL_PROFL: true,
|
||||
INCL_TOTAL: true,
|
||||
INCL_VENDR: false,
|
||||
},
|
||||
];
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.ENV`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.ENV`),
|
||||
envFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} ENV file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating ENV file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGenerateEnvFile;
|
||||
87
src/main/ems-parts-order/ems-parts-order-generate-lin.ts
Normal file
87
src/main/ems-parts-order/ems-parts-order-generate-lin.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
import { linFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/lin-field-descriptors";
|
||||
|
||||
const EmsPartsOrderGenerateLinFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const records = partsOrder.parts_order_lines.map((partsOrderLine) => ({
|
||||
LINE_NO: partsOrderLine.jobline?.line_no,
|
||||
LINE_IND: partsOrderLine.jobline?.line_ind,
|
||||
LINE_REF: partsOrderLine.jobline?.line_ref,
|
||||
TRAN_CODE: partsOrderLine.jobline?.tran_code ?? "1",
|
||||
DB_REF: partsOrderLine.jobline?.db_ref,
|
||||
UNQ_SEQ: partsOrderLine.jobline?.unq_seq,
|
||||
WHO_PAYS: partsOrderLine.jobline?.who_pays,
|
||||
PART_DESCJ: partsOrderLine.jobline?.part_descj,
|
||||
|
||||
LINE_DESC: partsOrderLine.jobline?.line_desc,
|
||||
PART_TYPE:
|
||||
partsOrderLine.priceChange === true
|
||||
? partsOrderLine.part_type
|
||||
: partsOrderLine.jobline?.part_type,
|
||||
GLASS_FLAG: partsOrderLine.jobline?.glass_flag,
|
||||
OEM_PARTNO: partsOrderLine.jobline?.oem_partno,
|
||||
PRICE_INC: partsOrderLine.jobline?.price_inc,
|
||||
ALT_PART_I: partsOrderLine.jobline?.alt_part_i,
|
||||
TAX_PART: partsOrderLine.jobline?.tax_part,
|
||||
DB_PRICE: partsOrderLine.jobline?.db_price,
|
||||
ACT_PRICE:
|
||||
partsOrderLine.priceChange === true
|
||||
? partsOrderLine.act_price
|
||||
: partsOrderLine.jobline?.act_price,
|
||||
PRICE_J: partsOrderLine.jobline?.price_j,
|
||||
CERT_PART: partsOrderLine.jobline?.cert_part,
|
||||
PART_QTY: partsOrderLine.jobline?.part_qty,
|
||||
ALT_CO_ID: partsOrderLine.jobline?.alt_co_id,
|
||||
ALT_PARTNO: partsOrderLine.jobline?.alt_partno,
|
||||
ALT_OVERRD: partsOrderLine.jobline?.alt_overrd,
|
||||
ALT_PARTM: partsOrderLine.jobline?.alt_partm,
|
||||
PRT_DSMK_P: partsOrderLine.jobline?.prt_dsmk_p,
|
||||
PRT_DSMK_M: partsOrderLine.jobline?.prt_dsmk_m,
|
||||
MOD_LBR_TY: partsOrderLine.jobline?.mod_lbr_ty,
|
||||
DB_HRS: partsOrderLine.jobline?.db_hrs,
|
||||
MOD_LB_HRS: partsOrderLine.jobline?.mod_lb_hrs,
|
||||
LBR_INC: partsOrderLine.jobline?.lbr_inc,
|
||||
LBR_OP: partsOrderLine.jobline?.lbr_op,
|
||||
LBR_HRS_J: partsOrderLine.jobline?.lbr_hrs_j,
|
||||
LBR_TYP_J: partsOrderLine.jobline?.lbr_typ_j,
|
||||
LBR_OP_J: partsOrderLine.jobline?.lbr_op_j,
|
||||
PAINT_STG: partsOrderLine.jobline?.paint_stg,
|
||||
PAINT_TONE: partsOrderLine.jobline?.paint_tone,
|
||||
LBR_TAX: partsOrderLine.jobline?.lbr_tax,
|
||||
LBR_AMT: partsOrderLine.jobline?.lbr_amt,
|
||||
MISC_AMT: partsOrderLine.jobline?.misc_amt,
|
||||
MISC_SUBLT: partsOrderLine.jobline?.misc_sublt,
|
||||
MISC_TAX: partsOrderLine.jobline?.misc_tax,
|
||||
BETT_TYPE: partsOrderLine.jobline?.bett_type,
|
||||
BETT_PCTG: partsOrderLine.jobline?.bett_pctg,
|
||||
BETT_AMT: partsOrderLine.jobline?.bett_amt,
|
||||
BETT_TAX: partsOrderLine.jobline?.bett_tax,
|
||||
}));
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.LIN`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.LIN`),
|
||||
linFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} LIN file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating LIN file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGenerateLinFile;
|
||||
59
src/main/ems-parts-order/ems-parts-order-generate-pfh.ts
Normal file
59
src/main/ems-parts-order/ems-parts-order-generate-pfh.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
import { pfhFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfh-field-descriptors";
|
||||
|
||||
const EmsPartsOrderGeneratePfhFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const records = [
|
||||
{
|
||||
ID_PRO_NAM: "REPAIR FACILITY", // Job.id_pro_nam?.Value
|
||||
TAX_PRETHR: (partsOrder.job.tax_prethr || 0) * 100,
|
||||
TAX_THRAMT: (partsOrder.job.tax_thramt || 0) * 100,
|
||||
TAX_PSTTHR: (partsOrder.job.tax_pstthr || 0) * 100,
|
||||
TAX_TOW_IN: true, // Job.tax_tow_in?.Value
|
||||
TAX_TOW_RT: (partsOrder.job.tax_tow_rt || 0) * 100,
|
||||
TAX_STR_IN: true, // Job.tax_str_in?.Value
|
||||
TAX_STR_RT: (partsOrder.job.tax_str_rt || 0) * 100,
|
||||
TAX_SUB_IN: true, // Job.tax_sub_in?.Value
|
||||
TAX_SUB_RT: (partsOrder.job.tax_sub_rt || 0) * 100,
|
||||
TAX_BTR_IN: true, // Job.tax_btr_in?.Value
|
||||
TAX_LBR_RT:
|
||||
(partsOrder.job.bodyshop?.bill_tax_rates?.state_tax_rate || 0) * 100,
|
||||
TAX_GST_RT:
|
||||
(partsOrder.job.bodyshop?.bill_tax_rates?.federal_tax_rate || 0) *
|
||||
100,
|
||||
TAX_GST_IN: true, // Job.tax_gst_in?.Value
|
||||
ADJ_G_DISC: (partsOrder.job.adj_g_disc || 0) * 100,
|
||||
ADJ_TOWDIS: (partsOrder.job.adj_towdis || 0) * 100,
|
||||
ADJ_STRDIS: (partsOrder.job.adj_strdis || 0) * 100,
|
||||
ADJ_BTR_IN: null, // Job.adj_btr_in?.Value
|
||||
TAX_PREDIS: (partsOrder.job.tax_predis || 0) * 100,
|
||||
},
|
||||
];
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFH`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFH`),
|
||||
pfhFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} PFH file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating PFH file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGeneratePfhFile;
|
||||
302
src/main/ems-parts-order/ems-parts-order-generate-pfl.ts
Normal file
302
src/main/ems-parts-order/ems-parts-order-generate-pfl.ts
Normal file
@@ -0,0 +1,302 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedPflLine } from "../decoder/decode-pfl.interface";
|
||||
import { pflFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfl-field-descriptors";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
import _ from "lodash";
|
||||
|
||||
const EmsPartsOrderGeneratePflFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
let records;
|
||||
|
||||
if (partsOrder.job.cieca_pfl && !_.isEmpty(partsOrder.job.cieca_pfl)) {
|
||||
Object.keys(partsOrder.job.cieca_pfl).map((key) => {
|
||||
const record: DecodedPflLine = partsOrder.job.cieca_pfl[key];
|
||||
return uppercaseObjectKeys(record);
|
||||
});
|
||||
} else {
|
||||
//We don't have the PFL data for an old job, so make it manually.
|
||||
|
||||
records = [
|
||||
{
|
||||
LBR_TYPE: "LAA",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_laa,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LAB",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_lab,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
|
||||
{
|
||||
LBR_TYPE: "LAD",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_lad,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LAE",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_lae,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LAF",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_laf,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LAG",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_lag,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LAM",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_lam,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LAR",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_lar,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LAS",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_las,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LAU",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_lau,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LA1",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_la1,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LA2",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_la2,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LA3",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_la3,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
{
|
||||
LBR_TYPE: "LA4",
|
||||
LBR_DESC: "",
|
||||
LBR_RATE: partsOrder.job.rate_la4,
|
||||
LBR_TAX_IN: true,
|
||||
LBR_TAXP: null, // Job.bodyshop.bill_tax_rates.state_tax_rate?.Value ?? 0,
|
||||
LBR_ADJP: 0,
|
||||
LBR_TX_TY1: null, //partsOrder.job.lbr_tx_ty1,
|
||||
LBR_TX_IN1: null, //partsOrder.job.lbr_tx_in1,
|
||||
LBR_TX_TY2: null, //partsOrder.job.lbr_tx_ty2,
|
||||
LBR_TX_IN2: null, //partsOrder.job.lbr_tx_in2,
|
||||
LBR_TX_TY3: null, //partsOrder.job.lbr_tx_ty3,
|
||||
LBR_TX_IN3: null, //partsOrder.job.lbr_tx_in3,
|
||||
LBR_TX_TY4: null, //partsOrder.job.lbr_tx_ty4,
|
||||
LBR_TX_IN4: null, //partsOrder.job.lbr_tx_in4,
|
||||
LBR_TX_TY5: null, //partsOrder.job.lbr_tx_ty5,
|
||||
LBR_TX_IN5: null, //partsOrder.job.lbr_tx_in5,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFL`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFL`),
|
||||
pflFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} PFL file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating PFL file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGeneratePflFile;
|
||||
105
src/main/ems-parts-order/ems-parts-order-generate-pfm.ts
Normal file
105
src/main/ems-parts-order/ems-parts-order-generate-pfm.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import _ from "lodash";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedPfmLine } from "../decoder/decode-pfm.interface";
|
||||
import { pfmFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfm-field-descriptors";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGeneratePfmFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
let records;
|
||||
if (partsOrder.job.materials && !_.isEmpty(partsOrder.job.materials)) {
|
||||
records = Object.keys(partsOrder.job.materials).map((key) => {
|
||||
const record: DecodedPfmLine = partsOrder.job.materials[key];
|
||||
return uppercaseObjectKeys(record);
|
||||
});
|
||||
} else {
|
||||
//Older records may not have materials, especially for ImEX.
|
||||
records = [
|
||||
{
|
||||
MATL_TYPE: "MAPA",
|
||||
CAL_CODE: null,
|
||||
CAL_DESC: null,
|
||||
CAL_MAXDLR: 0,
|
||||
CAL_PRIP: 0,
|
||||
CAL_SECP: 0,
|
||||
MAT_CALP: 0,
|
||||
CAL_PRETHR: 0,
|
||||
CAL_PSTTHR: 0,
|
||||
CAL_THRAMT: 0,
|
||||
CAL_LBRMIN: 0,
|
||||
CAL_LBRMAX: 0,
|
||||
CAL_LBRRTE: partsOrder.job.rate_mapa,
|
||||
CAL_OPCODE: null,
|
||||
TAX_IND: true,
|
||||
MAT_TAXP: null,
|
||||
MAT_ADJP: null,
|
||||
MAT_TX_TY1: null,
|
||||
MAT_TX_IN1: null,
|
||||
MAT_TX_TY2: null,
|
||||
MAT_TX_IN2: null,
|
||||
MAT_TX_TY3: null,
|
||||
MAT_TX_IN3: null,
|
||||
MAT_TX_TY4: null,
|
||||
MAT_TX_IN4: null,
|
||||
MAT_TX_TY5: null,
|
||||
MAT_TX_IN5: null,
|
||||
},
|
||||
{
|
||||
MATL_TYPE: "MASH",
|
||||
CAL_CODE: null,
|
||||
CAL_DESC: null,
|
||||
CAL_MAXDLR: 0,
|
||||
CAL_PRIP: 0,
|
||||
CAL_SECP: 0,
|
||||
MAT_CALP: 0,
|
||||
CAL_PRETHR: 0,
|
||||
CAL_PSTTHR: 0,
|
||||
CAL_THRAMT: 0,
|
||||
CAL_LBRMIN: 0,
|
||||
CAL_LBRMAX: 0,
|
||||
CAL_LBRRTE: partsOrder.job.rate_mash,
|
||||
CAL_OPCODE: null,
|
||||
TAX_IND: true,
|
||||
MAT_TAXP: null,
|
||||
MAT_ADJP: null,
|
||||
MAT_TX_TY1: null,
|
||||
MAT_TX_IN1: null,
|
||||
MAT_TX_TY2: null,
|
||||
MAT_TX_IN2: null,
|
||||
MAT_TX_TY3: null,
|
||||
MAT_TX_IN3: null,
|
||||
MAT_TX_TY4: null,
|
||||
MAT_TX_IN4: null,
|
||||
MAT_TX_TY5: null,
|
||||
MAT_TX_IN5: null,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFM`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFM`),
|
||||
pfmFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} PFM file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating PFM file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGeneratePfmFile;
|
||||
34
src/main/ems-parts-order/ems-parts-order-generate-pfo.ts
Normal file
34
src/main/ems-parts-order/ems-parts-order-generate-pfo.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { pfoFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfo-field-descriptors";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGeneratePfoFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const records = []; //This was kept blank previously as well.
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFO`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFO`),
|
||||
pfoFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} PFO file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating PFO file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGeneratePfoFile;
|
||||
39
src/main/ems-parts-order/ems-parts-order-generate-pfp.ts
Normal file
39
src/main/ems-parts-order/ems-parts-order-generate-pfp.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedPfpLine } from "../decoder/decode-pfp.interface";
|
||||
import { pfpFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pfp-field-descriptors";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGeneratePfpFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const records = Object.keys(partsOrder.job.parts_tax_rates).map((key) => {
|
||||
const record: DecodedPfpLine = partsOrder.job.parts_tax_rates[key];
|
||||
return uppercaseObjectKeys(record);
|
||||
});
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFP`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFP`),
|
||||
pfpFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} PFP file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating PFP file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGeneratePfpFile;
|
||||
34
src/main/ems-parts-order/ems-parts-order-generate-pft.ts
Normal file
34
src/main/ems-parts-order/ems-parts-order-generate-pft.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { pftFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/pft-field-descriptor";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGeneratePftFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const records = []; //Left blank intentionally as per previous code.
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFT`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.PFT`),
|
||||
pftFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} PFT file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating PFT file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGeneratePftFile;
|
||||
40
src/main/ems-parts-order/ems-parts-order-generate-stl.ts
Normal file
40
src/main/ems-parts-order/ems-parts-order-generate-stl.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { DecodedStlLine } from "../decoder/decode-stl.interface";
|
||||
import { stlFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/stl-field-descriptors";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGenerateStlFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
//TODO: Add CIECA STL to parts order.
|
||||
const records = Object.keys(partsOrder.job.cieca_stl?.data).map((key) => {
|
||||
const record: DecodedStlLine = partsOrder.job.cieca_stl.data[key];
|
||||
return uppercaseObjectKeys(record);
|
||||
});
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.STL`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.STL`),
|
||||
stlFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} STL file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating STL file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGenerateStlFile;
|
||||
36
src/main/ems-parts-order/ems-parts-order-generate-ttl.ts
Normal file
36
src/main/ems-parts-order/ems-parts-order-generate-ttl.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { ttlFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/ttl-field-descriptors";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import uppercaseObjectKeys from "../util/uppercaseObjectKeys";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGenerateTtlFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
//TODO: Add CIECA STL to parts order.
|
||||
const records = uppercaseObjectKeys(partsOrder.job.cieca_ttl?.data);
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.TTL`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.TTL`),
|
||||
ttlFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords([records]);
|
||||
console.log(`${records.length} TTL file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating TTL file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGenerateTtlFile;
|
||||
65
src/main/ems-parts-order/ems-parts-order-generate-veh.ts
Normal file
65
src/main/ems-parts-order/ems-parts-order-generate-veh.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { DBFFile } from "dbffile";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import { vehFieldLineDescriptors } from "../util/ems-interface/fielddescriptors/veh-field-descriptors";
|
||||
import {
|
||||
deleteEmsFileIfExists,
|
||||
generateEmsOutFilePath,
|
||||
} from "../util/ems-util";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const EmsPartsOrderGenerateVehFile = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<boolean> => {
|
||||
try {
|
||||
const records = [
|
||||
{
|
||||
IMPACT_1: partsOrder.job.area_of_damage?.impact1 || null,
|
||||
IMPACT_2: partsOrder.job.area_of_damage?.impact2 || null,
|
||||
DMG_MEMO: null,
|
||||
DB_V_CODE: "",
|
||||
PLATE_NO: partsOrder.job.plate_no || null,
|
||||
PLATE_ST: partsOrder.job.plate_st || null,
|
||||
V_VIN: partsOrder.job.v_vin || null,
|
||||
V_COND: "",
|
||||
V_PROD_DT: "",
|
||||
V_MODEL_YR: partsOrder.job.v_model_yr || null,
|
||||
V_MAKECODE: "",
|
||||
V_MAKEDESC: partsOrder.job.v_make_desc || null,
|
||||
V_MODEL: partsOrder.job.v_model_desc || null,
|
||||
V_TYPE: partsOrder.job.vehicle?.v_type || null,
|
||||
V_BSTYLE: partsOrder.job.vehicle?.v_bstyle || null,
|
||||
V_TRIMCODE: partsOrder.job.vehicle?.v_trimcode || null,
|
||||
TRIM_COLOR: partsOrder.job.vehicle?.trim_color || null,
|
||||
V_MLDGCODE: partsOrder.job.vehicle?.v_mldgcode || null,
|
||||
V_ENGINE: partsOrder.job.vehicle?.v_engine || null,
|
||||
V_MILEAGE: partsOrder.job.vehicle?.v_mileage || null,
|
||||
V_OPTIONS: null,
|
||||
V_COLOR: partsOrder.job.vehicle?.v_color || null,
|
||||
V_TONE: Number(partsOrder.job.vehicle?.v_tone) || null,
|
||||
V_STAGE: null,
|
||||
PAINT_CD1: partsOrder.job.vehicle?.v_paint_codes?.paint_cd1 || "",
|
||||
PAINT_CD2: partsOrder.job.vehicle?.v_paint_codes?.paint_cd2 || "",
|
||||
PAINT_CD3: partsOrder.job.vehicle?.v_paint_codes?.paint_cd3 || "",
|
||||
V_MEMO: null,
|
||||
},
|
||||
];
|
||||
|
||||
await deleteEmsFileIfExists(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.VEH`),
|
||||
);
|
||||
|
||||
const dbf: DBFFile = await DBFFile.create(
|
||||
generateEmsOutFilePath(`${partsOrder.job.ciecaid}.VEH`),
|
||||
vehFieldLineDescriptors,
|
||||
);
|
||||
|
||||
await dbf.appendRecords(records);
|
||||
console.log(`${records.length} VEH file records added.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error generating VEH file:", errorTypeCheck(error));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export default EmsPartsOrderGenerateVehFile;
|
||||
83
src/main/ems-parts-order/ems-parts-order-handler.ts
Normal file
83
src/main/ems-parts-order/ems-parts-order-handler.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import log from "electron-log/main";
|
||||
import express from "express";
|
||||
import _ from "lodash";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import store from "../store/store";
|
||||
import createdDirectoryIfNotExist from "../util/createDirectoryIfNotExist";
|
||||
import EmsPartsOrderGenerateAd1File from "./ems-parts-order-generate-ad1";
|
||||
import EmsPartsOrderGenerateAd2File from "./ems-parts-order-generate-ad2";
|
||||
import EmsPartsOrderGenerateEnvFile from "./ems-parts-order-generate-env";
|
||||
import EmsPartsOrderGenerateLinFile from "./ems-parts-order-generate-lin";
|
||||
import EmsPartsOrderGeneratePfhFile from "./ems-parts-order-generate-pfh";
|
||||
import EmsPartsOrderGeneratePflFile from "./ems-parts-order-generate-pfl";
|
||||
import EmsPartsOrderGeneratePfmFile from "./ems-parts-order-generate-pfm";
|
||||
import EmsPartsOrderGeneratePfoFile from "./ems-parts-order-generate-pfo";
|
||||
import EmsPartsOrderGeneratePfpFile from "./ems-parts-order-generate-pfp";
|
||||
import EmsPartsOrderGeneratePftFile from "./ems-parts-order-generate-pft";
|
||||
import EmsPartsOrderGenerateStlFile from "./ems-parts-order-generate-stl";
|
||||
import EmsPartsOrderGenerateTtlFile from "./ems-parts-order-generate-ttl";
|
||||
import EmsPartsOrderGenerateVehFile from "./ems-parts-order-generate-veh";
|
||||
import { EmsPartsOrder } from "./ems-parts-order-interfaces";
|
||||
|
||||
const handleEMSPartsOrder = async (
|
||||
req: express.Request,
|
||||
res: express.Response,
|
||||
): Promise<void> => {
|
||||
//Route handler here only.
|
||||
|
||||
const partsOrderBody = req.body as EmsPartsOrder;
|
||||
try {
|
||||
await generateEMSPartsOrder(partsOrderBody);
|
||||
res.status(200).json({ success: true });
|
||||
} catch (error) {
|
||||
log.error("Error generating parts price change", errorTypeCheck(error));
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: "Error generating parts price change.",
|
||||
...errorTypeCheck(error),
|
||||
});
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
const generateEMSPartsOrder = async (
|
||||
partsOrder: EmsPartsOrder,
|
||||
): Promise<void> => {
|
||||
log.debug(" Generating parts price change");
|
||||
//Check to make sure that the EMS Output file path exists. If it doesn't, create it. If it's not set, abandon ship.
|
||||
|
||||
const emsOutFilePath: string | null = store.get("settings.emsOutFilePath");
|
||||
if (_.isEmpty(emsOutFilePath) || emsOutFilePath === null) {
|
||||
log.error("EMS Out file path is not set");
|
||||
throw new Error("EMS Out file path is not set");
|
||||
}
|
||||
try {
|
||||
createdDirectoryIfNotExist(emsOutFilePath);
|
||||
|
||||
//Generate all required files: ad1, ad2, veh, lin, pfh, pfl, pfm,pfo, pfp, pft, stl, ttl
|
||||
await EmsPartsOrderGenerateAd1File(partsOrder);
|
||||
await EmsPartsOrderGenerateAd2File(partsOrder);
|
||||
await EmsPartsOrderGenerateVehFile(partsOrder);
|
||||
await EmsPartsOrderGenerateLinFile(partsOrder);
|
||||
await EmsPartsOrderGeneratePfhFile(partsOrder);
|
||||
await EmsPartsOrderGeneratePflFile(partsOrder);
|
||||
await EmsPartsOrderGeneratePfmFile(partsOrder);
|
||||
await EmsPartsOrderGeneratePfoFile(partsOrder);
|
||||
await EmsPartsOrderGeneratePfpFile(partsOrder);
|
||||
await EmsPartsOrderGeneratePftFile(partsOrder);
|
||||
await EmsPartsOrderGenerateStlFile(partsOrder);
|
||||
await EmsPartsOrderGenerateTtlFile(partsOrder);
|
||||
|
||||
await EmsPartsOrderGenerateEnvFile(partsOrder);
|
||||
|
||||
log.info(
|
||||
"EMS Parts Order files generated successfully for " +
|
||||
partsOrder.job.ciecaid,
|
||||
);
|
||||
} catch (error) {
|
||||
log.error("Error generating parts price change", errorTypeCheck(error));
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { handleEMSPartsOrder };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user