Compare commits

...

50 Commits

Author SHA1 Message Date
Patrick Fic
daf9f197eb IO-2776 Re-disable make fields. 2025-11-14 11:00:08 -08:00
Patrick Fic
5848daef72 IO-2776 Remove ref file. 2025-11-14 10:58:37 -08:00
Patrick Fic
c655badae2 Fortellis cleanup. 2025-11-14 10:55:47 -08:00
Patrick Fic
7af70f7512 Additional Fortellis functionality on delete. 2025-11-14 08:36:42 -08:00
Patrick Fic
a8dcc542cc Merge remote-tracking branch 'origin/master-AIO' into feature/IO-2776-cdk-fortellis 2025-11-13 14:27:43 -08:00
Patrick Fic
a16c680d04 Add department overrides. 2025-11-05 13:23:24 -08:00
Patrick Fic
c6d083ce02 Additional fortellis changes. 2025-10-15 15:20:14 -07:00
Patrick Fic
ec30e73b3e WIP Get Makes 2025-10-07 13:23:42 -07:00
Patrick Fic
92b05a290e IO-2776 Remove issue checking revoked token causing log issues on wss. 2025-10-06 11:20:52 -07:00
Dave
0d9a7dda53 feature/IO-2776-cdk-fortellis - Rename missing WSS loggers 2025-10-06 12:53:21 -04:00
Dave
601eed6db8 feature/IO-2776-cdk-fortellis - Rename missing WSS loggers 2025-10-06 12:29:29 -04:00
Dave
9b708d5e8e Merge remote-tracking branch 'origin/release/2025-10-17' into feature/IO-2776-cdk-fortellis 2025-10-06 12:26:57 -04:00
Dave
6b4bc27205 feature/IO-2776-cdk-fortellis - Remove socket auth , was supposed to be old sockets. 2025-10-06 12:16:31 -04:00
Dave
0eb0e335fe Merge remote-tracking branch 'origin/master-AIO' into feature/IO-2776-cdk-fortellis 2025-10-02 14:13:03 -04:00
Patrick Fic
cd1e8b0b15 Add delete DMS wip. 2025-09-25 14:34:31 -07:00
Dave
f7799ffd03 feature/IO-2776-cdk-fortellis - Cleanup + Auth Socket stuff 2025-09-24 18:12:57 -04:00
Dave
57baa3d9fd Merge remote-tracking branch 'origin/release/2025-09-26' into feature/IO-2776-cdk-fortellis 2025-09-24 17:27:08 -04:00
Patrick Fic
f796dd0f89 Add DMS error WIP query. 2025-09-22 11:44:46 -07:00
Patrick Fic
517c30787d Improved error logging. 2025-09-18 11:32:48 -07:00
Patrick Fic
c8771275ce WIP and improved error handling 2025-09-15 12:39:35 -07:00
Patrick Fic
de26979e44 Merge branch 'master-AIO' into feature/IO-2776-cdk-fortellis 2025-09-11 14:40:20 -07:00
Patrick Fic
661678eb1c Additional WIP Fortellis. 2025-09-11 08:45:34 -07:00
Dave
1728982b2b deps 2025-09-08 12:17:17 -04:00
Dave
8aa747edc5 Merge remote-tracking branch 'origin/release/2025-09-12' into feature/IO-2776-cdk-fortellis 2025-09-08 12:16:14 -04:00
Dave Richer
544494ce0d Merged in release/2025-09-12 (pull request #2536)
Release/2025 09 12
2025-09-03 18:54:16 +00:00
Patrick Fic
2e25324ae9 IO-2776 Calculate allocations on load using Fortellis connection. 2025-08-27 16:56:46 -07:00
Patrick Fic
f525ec6fb8 IO-2776 Resolve circular dependancy for legacy WS preventing run time. 2025-08-27 16:52:47 -07:00
Patrick Fic
8296d914c5 IO-2776 Add posting transactions for Fortellis. 2025-08-27 16:37:31 -07:00
Dave Richer
3d691568ff Merged in release/2025-08-29 (pull request #2518)
Release/2025 08 29
2025-08-27 19:27:22 +00:00
Dave
7ab070e2bc feature/IO-2776-cdk-fortellis - Fix linting errors preventing build of front end 2025-08-21 12:23:02 -04:00
Dave
47a974e3cb Merge branch 'feature/IO-3255-simplified-part-management' into feature/IO-2776-cdk-fortellis 2025-08-21 12:04:36 -04:00
Dave
eac81cdffb Merge branch 'feature/IO-3255-simplified-part-management' into feature/IO-2776-cdk-fortellis 2025-08-21 11:45:58 -04:00
Dave
77c3e6f7e7 feature/IO-2776-cdk-fortellis - Update to the latest from simplified Parts Management 2025-08-21 11:28:23 -04:00
Dave
7f07da4360 Merge branch 'feature/IO-3255-simplified-part-management' into feature/IO-2776-cdk-fortellis
# Conflicts:
#	client/package-lock.json
#	client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx
#	client/src/components/dms-customer-selector/dms-customer-selector.component.jsx
#	client/src/pages/dms/dms.container.jsx
#	package-lock.json
#	package.json
2025-08-21 11:13:27 -04:00
Dave
c47144df72 Merge remote-tracking branch 'origin/master-AIO' into feature/IO-2776-cdk-fortellis
# Conflicts:
#	client/src/components/dms-post-form/dms-post-form.component.jsx
#	package-lock.json
#	package.json
#	server/web-sockets/redisSocketEvents.js
2025-08-21 11:05:03 -04:00
Patrick Fic
163eaac110 Fortellis bug fixes. 2025-05-14 17:48:28 -07:00
Patrick Fic
19ce1c66ad WIP Fortellis. 2025-04-25 11:58:38 -07:00
Patrick Fic
e2b4b408ed Basic posting up to fortellis-select-customer. 2025-04-16 15:55:22 -07:00
Patrick Fic
567171c722 Resolve use socket. 2025-04-14 14:59:36 -07:00
Patrick Fic
03acb78ab2 Merge branch 'master-AIO' into feature/IO-2776-cdk-fortellis 2025-04-14 12:51:43 -07:00
Patrick Fic
e7c4797fef IO-2776 Add additional redis helpers, restructure some fortellis calls. 2025-03-17 10:49:02 -07:00
Patrick Fic
88c35e8c48 Merge branch 'release/2025-03-14' into feature/IO-2776-cdk-fortellis 2025-03-14 09:21:09 -07:00
Patrick Fic
8623172aa1 WIP Fortellis work. 2025-03-13 14:06:07 -07:00
Patrick Fic
6ecc67184d Merge branch 'master-AIO' into feature/IO-2776-cdk-fortellis 2025-03-11 13:08:54 -07:00
Patrick Fic
09973ecb3b Merge branch 'release/2025-02-14' into feature/IO-2776-cdk-fortellis 2025-02-12 08:18:27 -08:00
Patrick Fic
db9e86e4c8 Succesful header creation. 2024-08-15 12:37:05 -07:00
Patrick Fic
85c446bc57 Refactor testing js file and made more modular. 2024-08-15 10:17:11 -07:00
Patrick Fic
5cf6f47bdc Merge remote-tracking branch 'origin/master-AIO' into feature/AIO/IO-2776-cdk-fortellis 2024-08-14 08:18:49 -07:00
Patrick Fic
1db4cbeeb8 Fortellis WIP 2024-05-22 08:18:10 -07:00
Patrick Fic
c88bf4065e Fortellis WIP 2024-05-14 08:15:40 -07:00
24 changed files with 3279 additions and 1318 deletions

346
Fortellis Notes.md Normal file
View File

@@ -0,0 +1,346 @@
Fortellis Notes
Subscription ID
- Appears to give us a list of all dealerships we have access to, and `apiDmsInfo` contains the integrations that are enabled for that dealership.
- Will likely need to filter based on the DMS ID or something?
- Should store the whole subscription object. Contains department information needed in subsequent calls.
Department ID
- May have multiple departments. Appears that financial stuff goes to Accounting, History will go to Service.
- TODO: How do we handle the multiple departments that may come up.
###Internal Questions
* Overview of the redis storing mechanism to cache this data.
*
# GL Wip Posting
## Org Helper Return Data
```json
[
{
"acctgLgnID": "DEVWB-A",
"applCode": "V",
"coID": "77",
"companyName": "TEST SYS C187092 DEVWB",
"lgnDesc": "DEV WRITE BACK VMS",
"logon": "DEVWB-V"
},
{
"acctgLgnID": "DEVWB-A",
"applCode": "F",
"coID": "77",
"companyName": "TEST SYS C187092 DEVWB",
"lgnDesc": "DEV WRITE BACK F&I SALES",
"logon": "DEVWB-FI"
},
{
"acctgLgnID": "DEVWB-A",
"applCode": "CS",
"coID": "77",
"companyName": "TEST SYS C187092 DEVWB",
"lgnDesc": "DEV WRITE BACK SERVICE",
"logon": "DEVWB-S"
},
{
"acctgLgnID": "DEVWB-A",
"applCode": "A",
"coID": "77",
"companyName": "TEST SYS C187092 DEVWB",
"lgnDesc": "DEV WRITE BACK ACCTG",
"logon": "DEVWB-A"
},
{
"acctgLgnID": "DEVWB-A",
"applCode": "SL",
"coID": "77",
"companyName": "TEST SYS C187092 DEVWB",
"lgnDesc": "DEV WRTIE BACK SLS MGMT",
"logon": "DEVWB-SL"
},
{
"acctgLgnID": "DEVWB-A",
"applCode": "O",
"coID": "77",
"companyName": "TEST SYS C187092 DEVWB",
"lgnDesc": "DEV WRITE BACK PARTS",
"logon": "DEVWB-I"
}
]
```
## Journal Helper Return Data
```json
[
{
"companyNo": "77",
"jrnlID": "32",
"jrnlName": "PARTS SALES",
"jrnlType": "S",
"intercoFlag": "0",
"defaultDocType": "4",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "92",
"jrnlName": "YTD ADJUSTMENTS",
"jrnlType": "Y",
"intercoFlag": "0",
"defaultDocType": "3",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "12",
"jrnlName": "FLEET SALES",
"jrnlType": "S",
"intercoFlag": "0",
"defaultDocType": "9",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "57",
"jrnlName": "CASH RECEIPTS (OPEN-ITEM)",
"jrnlType": "R",
"intercoFlag": "0",
"defaultDocType": "1",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "93",
"jrnlName": "SET UP HISTORY",
"jrnlType": "H",
"intercoFlag": "0",
"defaultDocType": "10",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "88",
"jrnlName": "F/S STATISCAL DATA",
"jrnlType": "F",
"intercoFlag": "0",
"defaultDocType": "10",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "58",
"jrnlName": "WARRANTY CREDITS",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "3",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "FC",
"jrnlName": "FINANCE CHARGE",
"jrnlType": "A",
"intercoFlag": "0",
"defaultDocType": "12",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "94",
"jrnlName": "SET UP SCHEDULES",
"jrnlType": "C",
"intercoFlag": "0",
"defaultDocType": "3",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "95",
"jrnlName": "SET UP GENERAL LEDGER",
"jrnlType": "B",
"intercoFlag": "0",
"defaultDocType": "3",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "20",
"jrnlName": "USED VEHICLE SALES",
"jrnlType": "S",
"intercoFlag": "0",
"defaultDocType": "9",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "60",
"jrnlName": "CASH DISBURSEMENTS",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "2",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "30",
"jrnlName": "SERVICE SALES",
"jrnlType": "S",
"intercoFlag": "0",
"defaultDocType": "7",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "40",
"jrnlName": "PAYROLL",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "11",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "15",
"jrnlName": "DEALER TRADES",
"jrnlType": "S",
"intercoFlag": "0",
"defaultDocType": "9",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "70",
"jrnlName": "NEW VEHICLE PURCHASES",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "8",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "25",
"jrnlName": "USED WHOLESALE",
"jrnlType": "S",
"intercoFlag": "0",
"defaultDocType": "9",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "75",
"jrnlName": "GENERAL PURCHASES",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "5",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "10",
"jrnlName": "NEW VEHICLE SALES",
"jrnlType": "S",
"intercoFlag": "0",
"defaultDocType": "9",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "80",
"jrnlName": "GENERAL JOURNAL",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "3",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "11",
"jrnlName": "WORK IN PROGRESS",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "10",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "56",
"jrnlName": "CASH RECEIPTS (BALANCE FWD)",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "1",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "81",
"jrnlName": "STANDARD ENTRIES",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "6",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "51",
"jrnlName": "CASH RECEIPTS JOURNAL - EFT",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "10",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "61",
"jrnlName": "CASH DISBURSMENTS -EFT",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "10",
"errCode": "",
"errMsg": ""
},
{
"companyNo": "77",
"jrnlID": "71",
"jrnlName": "USED VEHICLE PURCHASES",
"jrnlType": "G",
"intercoFlag": "0",
"defaultDocType": "8",
"errCode": "",
"errMsg": ""
}
]
```
# Feedback
- Receiving bad request errors, with no details. API errors page doesn't indicate what's wrong for certain types of error codes.
- API Error page works on a several minute delay.

661
client/package-lock.json generated
View File

@@ -2620,431 +2620,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
"integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
"integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
"integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
"integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
"integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
"integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
"integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
"integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
"integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
"integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
"integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
"integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
"integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
"integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
"integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
"integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
"integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
"integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
"integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
"integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
"integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
"integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
"integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
"integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
"integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
@@ -3776,166 +3351,6 @@
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
@@ -3976,46 +3391,6 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
@@ -9260,9 +8635,9 @@
"license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -13132,6 +12507,21 @@
"node": ">=18"
}
},
"node_modules/playwright/node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@@ -17443,21 +16833,6 @@
"sourcemap-codec": "^1.4.8"
}
},
"node_modules/vite/node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/vite/node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",

View File

@@ -7,6 +7,8 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import Dinero from "dinero.js";
import { SyncOutlined } from "@ant-design/icons";
import { pageLimit } from "../../utils/config";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { useSocket } from "../../contexts/SocketIO/useSocket";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -22,13 +24,28 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsAllocationsSummar
export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
const { t } = useTranslation();
const [allocationsSummary, setAllocationsSummary] = useState([]);
const {
treatments: { Fortellis }
} = useSplitTreatments({
attributes: {},
names: ["Fortellis"],
splitKey: bodyshop.imexshopid
});
const { socket: wsssocket } = useSocket();
useEffect(() => {
if (socket.connected) {
socket.emit("cdk-calculate-allocations", jobId, (ack) => {
if (Fortellis.treatment === "on") {
wsssocket.emit("fortellis-calculate-allocations", jobId, (ack) => {
setAllocationsSummary(ack);
socket.allocationsSummary = ack;
});
} else {
if (socket.connected) {
socket.emit("cdk-calculate-allocations", jobId, (ack) => {
setAllocationsSummary(ack);
socket.allocationsSummary = ack;
});
}
}
}, [socket, socket.connected, jobId]);
@@ -76,8 +93,12 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
extra={
<Button
onClick={() => {
socket.emit("cdk-calculate-allocations", jobId, (ack) => setAllocationsSummary(ack));
}}
if (Fortellis.treatment === "on") {
socket.emit("fortellis-calculate-allocations", jobId, (ack) => setAllocationsSummary(ack));
} else {
socket.emit("cdk-calculate-allocations", jobId, (ack) => setAllocationsSummary(ack));
}
}}
>
<SyncOutlined />
</Button>

View File

@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
@@ -18,15 +19,26 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkMakesRefetch);
export function DmsCdkMakesRefetch({ currentUser, bodyshop }) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const {
treatments: { Fortellis }
} = useSplitTreatments({
attributes: {},
names: ["Fortellis"],
splitKey: bodyshop.imexshopid
});
if (!currentUser.email.includes("@imex.")) return null;
const handleRefetch = async () => {
setLoading(true);
await axios.post("/cdk/getvehicles", {
cdk_dealerid: bodyshop.cdk_dealerid,
bodyshopid: bodyshop.id
});
try {
setLoading(true);
await axios.post(`cdk${Fortellis.treatment === "on" ? "/fortellis" : ""}/getvehicles`, {
cdk_dealerid: bodyshop.cdk_dealerid,
bodyshopid: bodyshop.id
});
} catch (error) {
console.error(error);
}
setLoading(false);
};

View File

@@ -1,8 +1,10 @@
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { Button, Checkbox, Col, Table } from "antd";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { useSocket } from "../../contexts/SocketIO/useSocket";
import { socket } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { alphaSort } from "../../utils/sorters";
@@ -15,42 +17,125 @@ const mapDispatchToProps = () => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsCustomerSelector);
export function DmsCustomerSelector({ bodyshop }) {
export function DmsCustomerSelector({ bodyshop, jobid }) {
const { t } = useTranslation();
const [customerList, setcustomerList] = useState([]);
const [open, setOpen] = useState(false);
const [selectedCustomer, setSelectedCustomer] = useState(null);
const [dmsType, setDmsType] = useState("cdk");
socket.on("cdk-select-customer", (customerList) => {
setOpen(true);
setDmsType("cdk");
setcustomerList(customerList);
});
socket.on("pbs-select-customer", (customerList) => {
setOpen(true);
setDmsType("pbs");
setcustomerList(customerList);
const {
treatments: { Fortellis }
} = useSplitTreatments({
attributes: {},
names: ["Fortellis"],
splitKey: bodyshop.imexshopid
});
const { socket: wsssocket } = useSocket();
useEffect(() => {
if (Fortellis.treatment === "on") {
const handleFortellisSelectCustomer = (customerList) => {
setOpen(true);
setDmsType("cdk");
setcustomerList(customerList);
};
wsssocket.on("fortellis-select-customer", handleFortellisSelectCustomer);
return () => {
wsssocket.off("fortellis-select-customer", handleFortellisSelectCustomer);
};
} else {
const handleCdkSelectCustomer = (customerList) => {
setOpen(true);
setDmsType("cdk");
setcustomerList(customerList);
};
const handlePbsSelectCustomer = (customerList) => {
setOpen(true);
setDmsType("pbs");
setcustomerList(customerList);
};
socket.on("cdk-select-customer", handleCdkSelectCustomer);
socket.on("pbs-select-customer", handlePbsSelectCustomer);
return () => {
socket.off("cdk-select-customer", handleCdkSelectCustomer);
socket.off("pbs-select-customer", handlePbsSelectCustomer);
};
}
}, []);
const onUseSelected = () => {
setOpen(false);
socket.emit(`${dmsType}-selected-customer`, selectedCustomer);
if (Fortellis.treatment === "on") {
wsssocket.emit(`fortellis-selected-customer`, { selectedCustomerId: selectedCustomer, jobid });
} else {
socket.emit(`${dmsType}-selected-customer`, selectedCustomer);
}
setSelectedCustomer(null);
};
const onUseGeneric = () => {
setOpen(false);
socket.emit(`${dmsType}-selected-customer`, bodyshop.cdk_configuration.generic_customer_number);
if (Fortellis.treatment === "on") {
wsssocket.emit(`fortellis-selected-customer`, {
selectedCustomerId: bodyshop.cdk_configuration.generic_customer_number,
jobid
});
} else {
socket.emit(`${dmsType}-selected-customer`, bodyshop.cdk_configuration.generic_customer_number);
}
setSelectedCustomer(null);
};
const onCreateNew = () => {
setOpen(false);
socket.emit(`${dmsType}-selected-customer`, null);
if (Fortellis.treatment === "on") {
wsssocket.emit(`fortellis-selected-customer`, { selectedCustomerId: null, jobid });
} else {
socket.emit(`${dmsType}-selected-customer`, null);
}
setSelectedCustomer(null);
};
const fortellisColumns = [
{
title: t("jobs.fields.dms.id"),
dataIndex: "customerId",
key: "id"
},
{
title: t("jobs.fields.dms.vinowner"),
dataIndex: "vinOwner",
key: "vinOwner",
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />
},
{
title: t("jobs.fields.dms.name1"),
dataIndex: ["customerName", "firstName"],
key: "firstName",
sorter: (a, b) => alphaSort(a.customerName?.firstName, b.customerName?.firstName)
},
{
title: t("jobs.fields.dms.name1"),
dataIndex: ["customerName", "lastName"],
key: "lastName",
sorter: (a, b) => alphaSort(a.customerName?.lastName, b.customerName?.lastName)
},
{
title: t("jobs.fields.dms.address"),
key: "address",
render: (record) =>
`${record.postalAddress?.addressLine1} ${record.postalAddress?.addressLine2 ? `, ${record.postalAddress?.addressLine2}` : ""},
${record.postalAddress?.city} ${record.postalAddress?.state} ${record.postalAddress?.postalCode} ${
record.postalAddress?.country
}`
}
];
const cdkColumns = [
{
title: t("jobs.fields.dms.id"),
@@ -117,13 +202,13 @@ export function DmsCustomerSelector({ bodyshop }) {
</div>
)}
pagination={{ position: "top" }}
columns={dmsType === "cdk" ? cdkColumns : pbsColumns}
rowKey={(record) => (dmsType === "cdk" ? record.id.value : record.ContactId)}
columns={dmsType === "cdk" ? (Fortellis.treatment === "on" ? fortellisColumns : cdkColumns) : pbsColumns}
rowKey={(record) => (dmsType === "cdk" ? record.id?.value || record.customerId : record.ContactId)}
dataSource={customerList}
//onChange={handleTableChange}
rowSelection={{
onSelect: (record) => {
setSelectedCustomer(dmsType === "cdk" ? record.id.value : record.ContactId);
setSelectedCustomer(dmsType === "cdk" ? record.id?.value || record.customerId : record.ContactId);
},
type: "radio",
selectedRowKeys: [selectedCustomer]

View File

@@ -26,6 +26,8 @@ import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { useSocket } from "../../contexts/SocketIO/useSocket";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -36,8 +38,17 @@ const mapDispatchToProps = () => ({
export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm);
export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
const {
treatments: { Fortellis }
} = useSplitTreatments({
attributes: {},
names: ["Fortellis"],
splitKey: bodyshop.imexshopid
});
const [form] = Form.useForm();
const { t } = useTranslation();
const { socket: wsssocket } = useSocket();
const handlePayerSelect = (value, index) => {
form.setFieldsValue({
@@ -58,13 +69,22 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
};
const handleFinish = (values) => {
socket.emit(`${determineDmsType(bodyshop)}-export-job`, {
jobid: job.id,
txEnvelope: values
});
console.log(logsRef);
if (Fortellis.treatment === "on") {
wsssocket.emit("fortellis-export-job", {
jobid: job.id,
txEnvelope: {
...values,
SubscriptionID: bodyshop.cdk_dealerid
}
});
} else {
socket.emit(`${determineDmsType(bodyshop)}-export-job`, {
jobid: job.id,
txEnvelope: values
});
}
if (logsRef) {
console.log("executing", logsRef);
logsRef.curent &&
logsRef.current.scrollIntoView({
behavior: "smooth"
@@ -169,7 +189,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
</Form.Item>
</LayoutFormRow>
<Space>
<DmsCdkMakes form={form} socket={socket} job={job} />
<DmsCdkMakes form={form} job={job} />
<DmsCdkMakesRefetch />
<Form.Item name="dms_unsold" label={t("jobs.fields.dms.dms_unsold")} initialValue={false}>
<Switch />

View File

@@ -21,6 +21,8 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -54,6 +56,14 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
const search = queryString.parse(useLocation().search);
const { jobId } = search;
const notification = useNotification();
const {
treatments: { Fortellis }
} = useSplitTreatments({
attributes: {},
names: ["Fortellis"],
splitKey: bodyshop.imexshopid
});
const { socket: wsssocket } = useSocket();
const { loading, error, data } = useQuery(QUERY_JOB_EXPORT_DMS, {
variables: { id: jobId },
@@ -84,45 +94,75 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
}, [t, setBreadcrumbs, setSelectedHeader]);
useEffect(() => {
socket.on("connect", () => socket.emit("set-log-level", logLevel));
socket.on("reconnect", () => {
setLogs((logs) => {
return [
...logs,
{
timestamp: new Date(),
level: "warn",
message: "Reconnected to CDK Export Service"
}
];
});
});
socket.on("connect_error", (err) => {
console.log(`connect_error due to ${err}`, err);
notification.error({ message: err.message });
});
socket.on("log-event", (payload) => {
setLogs((logs) => {
return [...logs, payload];
});
});
socket.on("export-success", (payload) => {
notification.success({
message: t("jobs.successes.exported")
});
insertAuditTrail({
jobid: payload,
operation: AuditTrailMapping.jobexported(),
type: "jobexported"
});
history("/manage/accounting/receivables");
});
if (Fortellis.treatment === "on") {
wsssocket.emit("set-log-level", logLevel);
if (socket.disconnected) socket.connect();
return () => {
socket.removeAllListeners();
socket.disconnect();
};
const handleLogEvent = (payload) => {
setLogs((logs) => {
return [...logs, payload];
});
};
const handleExportSuccess = (payload) => {
notification.success({
message: t("jobs.successes.exported")
});
insertAuditTrail({
jobid: payload,
operation: AuditTrailMapping.jobexported(),
type: "jobexported"
});
history("/manage/accounting/receivables");
};
wsssocket.on("fortellis-log-event", handleLogEvent);
wsssocket.on("export-success", handleExportSuccess);
return () => {
wsssocket.off("fortellis-log-event", handleLogEvent);
wsssocket.off("export-success", handleExportSuccess);
};
} else {
socket.on("connect", () => socket.emit("set-log-level", logLevel));
socket.on("reconnect", () => {
setLogs((logs) => {
return [
...logs,
{
timestamp: new Date(),
level: "warn",
message: "Reconnected to CDK Export Service"
}
];
});
});
socket.on("connect_error", (err) => {
console.log(`connect_error due to ${err}`, err);
notification.error({ message: err.message });
});
socket.on("log-event", (payload) => {
setLogs((logs) => {
return [...logs, payload];
});
});
socket.on("export-success", (payload) => {
notification.success({
message: t("jobs.successes.exported")
});
insertAuditTrail({
jobid: payload,
operation: AuditTrailMapping.jobexported(),
type: "jobexported"
});
history("/manage/accounting/receivables");
});
if (socket.disconnected) socket.connect();
return () => {
socket.removeAllListeners();
socket.disconnect();
};
}
}, []);
if (loading) return <LoadingSpinner />;
@@ -136,6 +176,9 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
return (
<div>
{Fortellis.treatment === "on" && (
<AlertComponent message="Posting to Fortellis" type="warning" showIcon closable />
)}
<Row gutter={[16, 16]}>
<Col md={24} lg={10}>
<DmsAllocationsSummary
@@ -157,7 +200,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
<DmsPostForm socket={socket} jobId={jobId} job={data && data.jobs_by_pk} logsRef={logsRef} />
</Col>
<DmsCustomerSelector />
<DmsCustomerSelector jobid={jobId} />
<Col span={24}>
<div ref={logsRef}>
@@ -182,8 +225,13 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
<Button
onClick={() => {
setLogs([]);
socket.disconnect();
socket.connect();
if (Fortellis.treatment === "on") {
wsssocket.disconnect();
wsssocket.connect();
} else {
socket.disconnect();
socket.connect();
}
}}
>
Reconnect

7
package-lock.json generated
View File

@@ -23,6 +23,7 @@
"archiver": "^7.0.1",
"aws4": "^1.13.2",
"axios": "^1.12.2",
"axios-curlirize": "^2.0.0",
"better-queue": "^3.8.12",
"bullmq": "^5.61.0",
"chart.js": "^4.5.0",
@@ -3586,6 +3587,12 @@
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios-curlirize": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/axios-curlirize/-/axios-curlirize-2.0.0.tgz",
"integrity": "sha512-TrQBa8MfIwaYsrCoYhfCr7NDRXLuGm+Rqh/PtAuO64b8PCCOJWn37BWQvpN4/mzzig3uHb4qXzvpxJmALHaiwA==",
"license": "MIT"
},
"node_modules/axios-ntlm": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/axios-ntlm/-/axios-ntlm-1.4.6.tgz",

View File

@@ -32,6 +32,7 @@
"archiver": "^7.0.1",
"aws4": "^1.13.2",
"axios": "^1.12.2",
"axios-curlirize": "^2.0.0",
"better-queue": "^3.8.12",
"bullmq": "^5.61.0",
"chart.js": "^4.5.0",

View File

@@ -38,7 +38,7 @@ const { registerCleanupTask, initializeCleanupManager } = require("./server/util
const { loadEmailQueue } = require("./server/notifications/queues/emailQueue");
const { loadAppQueue } = require("./server/notifications/queues/appQueue");
const { SetLegacyWebsocketHandlers } = require("./server/web-sockets/web-socket");
const CLUSTER_RETRY_BASE_DELAY = 100;
const CLUSTER_RETRY_MAX_DELAY = 5000;
const CLUSTER_RETRY_JITTER = 100;
@@ -324,6 +324,9 @@ const applySocketIO = async ({ server, app }) => {
}
});
// Legacy Socket Events
SetLegacyWebsocketHandlers(io)
const api = {
pubClient,
io,
@@ -387,8 +390,6 @@ const main = async () => {
const redisHelpers = applyRedisHelpers({ pubClient, app, logger });
const ioHelpers = applyIOHelpers({ app, redisHelpers, ioRedis, logger });
// Legacy Socket Events
require("./server/web-sockets/web-socket");
// Initialize Queues
await loadQueues({ pubClient: pubClient, logger, redisHelpers, ioRedis });

View File

@@ -1,7 +1,7 @@
const GraphQLClient = require("graphql-request").GraphQLClient;
const queries = require("../../graphql-client/queries");
const CdkBase = require("../../web-sockets/web-socket");
const WsLogger = require("../../web-sockets/createLogEvent");
const moment = require("moment");
const Dinero = require("dinero.js");
const AxiosLib = require("axios").default;
@@ -23,7 +23,7 @@ axios.interceptors.request.use((x) => {
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
//console.log(printable);
CdkBase.createJsonEvent(socket, "SILLY", `Raw Request: ${printable}`, x.data);
WsLogger.createJsonEvent(socket, "SILLY", `Raw Request: ${printable}`, x.data);
return x;
});
@@ -33,14 +33,14 @@ axios.interceptors.response.use((x) => {
const printable = `${new Date()} | Response: ${x.status} | ${JSON.stringify(x.data)}`;
//console.log(printable);
CdkBase.createJsonEvent(socket, "SILLY", `Raw Response: ${printable}`, x.data);
WsLogger.createJsonEvent(socket, "SILLY", `Raw Response: ${printable}`, x.data);
return x;
});
async function PbsCalculateAllocationsAp(socket, billids) {
try {
CdkBase.createLogEvent(socket, "DEBUG", `Received request to calculate allocations for ${billids}`);
WsLogger.createLogEvent(socket, "DEBUG", `Received request to calculate allocations for ${billids}`);
const { bills, bodyshops } = await QueryBillData(socket, billids);
const bodyshop = bodyshops[0];
socket.bodyshop = bodyshop;
@@ -50,7 +50,7 @@ async function PbsCalculateAllocationsAp(socket, billids) {
const transactionlist = [];
if (bills.length === 0) {
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"ERROR",
`No bills found for export. Ensure they have not already been exported and try again.`
@@ -147,7 +147,9 @@ async function PbsCalculateAllocationsAp(socket, billids) {
...billHash[key],
Amount: billHash[key].Amount.toFormat("0.00")
});
APAmount = APAmount.add(billHash[key].Amount); //Calculate the total expense for the bill iteratively to create the corresponding credit to AP.
//Calculate the total expense for the bill iteratively to
// create the corresponding credit to AP.
APAmount = APAmount.add(billHash[key].Amount);
}
});
@@ -166,19 +168,21 @@ async function PbsCalculateAllocationsAp(socket, billids) {
return transactionlist;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in PbsCalculateAllocationsAp. ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error encountered in PbsCalculateAllocationsAp. ${error}`);
}
}
exports.PbsCalculateAllocationsAp = PbsCalculateAllocationsAp;
async function QueryBillData(socket, billids) {
CdkBase.createLogEvent(socket, "DEBUG", `Querying bill data for id(s) ${billids}`);
WsLogger.createLogEvent(socket, "DEBUG", `Querying bill data for id(s) ${billids}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.GET_PBS_AP_ALLOCATIONS, { billids: billids });
CdkBase.createLogEvent(socket, "SILLY", `Bill data query result ${JSON.stringify(result, null, 2)}`);
WsLogger.createLogEvent(socket, "SILLY", `Bill data query result ${JSON.stringify(result, null, 2)}`);
return result;
}
@@ -192,40 +196,10 @@ function getCostAccount(billline, respcenters) {
return respcenters.costs.find((c) => c.name === acctName);
}
exports.PbsExportAp = async function (socket, { billids, txEnvelope }) {
CdkBase.createLogEvent(socket, "DEBUG", `Exporting selected AP.`);
//apAllocations has the same shap as the lines key for the accounting posting to PBS.
socket.apAllocations = await PbsCalculateAllocationsAp(socket, billids);
socket.txEnvelope = txEnvelope;
for (const allocation of socket.apAllocations) {
const { billid, ...restAllocation } = allocation;
const { data: AccountPostingChange } = await axios.post(PBS_ENDPOINTS.AccountingPostingChange, restAllocation, {
auth: PBS_CREDENTIALS,
socket
});
CheckForErrors(socket, AccountPostingChange);
if (AccountPostingChange.WasSuccessful) {
CdkBase.createLogEvent(socket, "DEBUG", `Marking bill as exported.`);
await MarkApExported(socket, [billid]);
socket.emit("ap-export-success", billid);
} else {
CdkBase.createLogEvent(socket, "ERROR", `Export was not successful.`);
socket.emit("ap-export-failure", {
billid,
error: AccountPostingChange.Message
});
}
}
socket.emit("ap-export-complete");
};
async function MarkApExported(socket, billids) {
CdkBase.createLogEvent(socket, "DEBUG", `Marking bills as exported for id ${billids}`);
WsLogger.createLogEvent(socket, "DEBUG", `Marking bills as exported for id ${billids}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.MARK_BILLS_EXPORTED, {
@@ -244,3 +218,36 @@ async function MarkApExported(socket, billids) {
return result;
}
const defaultHandler = async (socket, { billids, txEnvelope }) => {
WsLogger.createLogEvent(socket, "DEBUG", `Exporting selected AP.`);
//apAllocations has the same shap as the lines key for the accounting posting to PBS.
socket.apAllocations = await PbsCalculateAllocationsAp(socket, billids);
socket.txEnvelope = txEnvelope;
for (const allocation of socket.apAllocations) {
const { billid, ...restAllocation } = allocation;
const { data: AccountPostingChange } = await axios.post(PBS_ENDPOINTS.AccountingPostingChange, restAllocation, {
auth: PBS_CREDENTIALS,
socket
});
CheckForErrors(socket, AccountPostingChange);
if (AccountPostingChange.WasSuccessful) {
WsLogger.createLogEvent(socket, "DEBUG", `Marking bill as exported.`);
await MarkApExported(socket, [billid]);
socket.emit("ap-export-success", billid);
} else {
WsLogger.createLogEvent(socket, "ERROR", `Export was not successful.`);
socket.emit("ap-export-failure", {
billid,
error: AccountPostingChange.Message
});
}
}
socket.emit("ap-export-complete");
};
exports.PbsExportAp = defaultHandler;

View File

@@ -2,10 +2,12 @@ const GraphQLClient = require("graphql-request").GraphQLClient;
const AxiosLib = require("axios").default;
const queries = require("../../graphql-client/queries");
const { PBS_ENDPOINTS, PBS_CREDENTIALS } = require("./pbs-constants");
const WsLogger = require("../../web-sockets/createLogEvent");
const fs = require("fs");
const path = require("path");
//const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
const CalculateAllocations = require("../../cdk/cdk-calculate-allocations").default;
const CdkBase = require("../../web-sockets/web-socket");
const moment = require("moment-timezone");
const Dinero = require("dinero.js");
const InstanceManager = require("../../utils/instanceMgr").default;
@@ -19,11 +21,12 @@ axios.interceptors.request.use((x) => {
...x.headers[x.method],
...x.headers
};
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
const printable = `${new Date()} | Request: ${x.method.toUpperCase()} | ${
x.url
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
//logRequestToFile(printable);
CdkBase.createJsonEvent(socket, "DEBUG", `Raw Request: ${printable}`, x.data);
WsLogger.createJsonEvent(socket, "DEBUG", `Raw Request: ${printable}`, x.data);
return x;
});
@@ -33,13 +36,11 @@ axios.interceptors.response.use((x) => {
const printable = `${new Date()} | Response: ${x.status} ${x.statusText} |${JSON.stringify(x.data)}`;
//logRequestToFile(printable);
CdkBase.createJsonEvent(socket, "DEBUG", `Raw Response: ${printable}`, x.data);
WsLogger.createJsonEvent(socket, "DEBUG", `Raw Response: ${printable}`, x.data);
return x;
});
const fs = require('fs');
const path = require("path");
function logRequestToFile(printable) {
try {
const logDir = path.join(process.cwd(), "logs");
@@ -53,21 +54,20 @@ function logRequestToFile(printable) {
}
}
exports.default = async function (socket, { txEnvelope, jobid }) {
const defaultHandler = async (socket, { txEnvelope, jobid }) => {
socket.logEvents = [];
socket.recordid = jobid;
socket.txEnvelope = txEnvelope;
try {
CdkBase.createLogEvent(socket, "INFO", `Received Job export request for id ${jobid}`);
WsLogger.createLogEvent(socket, "INFO", `Received Job export request for id ${jobid}`);
const JobData = await QueryJobData(socket, jobid);
socket.JobData = JobData;
CdkBase.createLogEvent(socket, "INFO", `Querying the DMS for the Vehicle Record.`);
WsLogger.createLogEvent(socket, "INFO", `Querying the DMS for the Vehicle Record.`);
//Query for the Vehicle record to get the associated customer.
socket.DmsVeh = await QueryVehicleFromDms(socket);
//Todo: Need to validate the lines and methods below.
if (socket.DmsVeh && socket.DmsVeh.CustomerRef) {
if (socket.DmsVeh?.CustomerRef) {
//Get the associated customer from the Vehicle Record.
socket.DMSVehCustomer = await QueryCustomerBycodeFromDms(socket, socket.DmsVeh.CustomerRef);
}
@@ -78,35 +78,39 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
...socket.DMSCustList
]);
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in PbsJobExport. ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error encountered in PbsJobExport. ${error}`);
}
};
exports.default = defaultHandler;
exports.PbsSelectedCustomer = async function PbsSelectedCustomer(socket, selectedCustomerId) {
try {
socket.selectedCustomerId = selectedCustomerId;
if (socket.JobData.bodyshop.pbs_configuration.disablecontactvehicle !== true) {
CdkBase.createLogEvent(socket, "INFO", `User selected customer ${selectedCustomerId || "NEW"}`);
WsLogger.createLogEvent(socket, "INFO", `User selected customer ${selectedCustomerId || "NEW"}`);
//Upsert the contact information as per Wafaa's Email.
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"INFO",
`Upserting contact information to DMS for ${socket.JobData.ownr_fn || ""
`Upserting contact information to DMS for ${
socket.JobData.ownr_fn || ""
} ${socket.JobData.ownr_ln || ""} ${socket.JobData.ownr_co_nm || ""}`
);
const ownerRef = await UpsertContactData(socket, selectedCustomerId);
socket.ownerRef = ownerRef;
CdkBase.createLogEvent(socket, "INFO", `Upserting vehicle information to DMS for ${socket.JobData.v_vin}`);
WsLogger.createLogEvent(socket, "INFO", `Upserting vehicle information to DMS for ${socket.JobData.v_vin}`);
const vehicleRef = await UpsertVehicleData(socket, ownerRef.ReferenceId);
socket.vehicleRef = vehicleRef;
} else {
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"INFO",
`Contact and Vehicle updates disabled. Querying data and skipping to accounting data insert.`
);
//Must query for records to insert $0 RO.
//Must query for records to insert $0 RO.
if (!socket.ownerRef) {
const ownerRef = (await QueryCustomerBycodeFromDms(socket, selectedCustomerId))?.[0];
socket.ownerRef = ownerRef;
@@ -114,22 +118,21 @@ exports.PbsSelectedCustomer = async function PbsSelectedCustomer(socket, selecte
const vehicleRef = await GetVehicleData(socket, socket.ownerRef?.ReferenceId || socket.selectedCustomerId);
socket.vehicleRef = vehicleRef;
}
CdkBase.createLogEvent(socket, "INFO", `Inserting accounting posting data..`);
WsLogger.createLogEvent(socket, "INFO", `Inserting account posting data...`);
const insertResponse = await InsertAccountPostingData(socket);
if (insertResponse.WasSuccessful) {
if (socket.JobData.bodyshop.pbs_configuration.ro_posting) {
await CreateRepairOrderInPBS(socket, socket.ownerRef, socket.vehicleRef)
await CreateRepairOrderInPBS(socket, socket.ownerRef, socket.vehicleRef);
}
CdkBase.createLogEvent(socket, "INFO", `Marking job as exported.`);
WsLogger.createLogEvent(socket, "INFO", `Marking job as exported.`);
await MarkJobExported(socket, socket.JobData.id);
socket.emit("export-success", socket.JobData.id);
} else {
CdkBase.createLogEvent(socket, "ERROR", `Export was not successful.`);
WsLogger.createLogEvent(socket, "ERROR", `Export was not successful.`);
}
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in PbsSelectedCustomer. ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error encountered in PbsSelectedCustomer. ${error}`);
await InsertFailedExportLog(socket, error);
}
};
@@ -137,22 +140,24 @@ exports.PbsSelectedCustomer = async function PbsSelectedCustomer(socket, selecte
// Was Successful
async function CheckForErrors(socket, response) {
if (response.WasSuccessful === undefined || response.WasSuccessful === true) {
CdkBase.createLogEvent(socket, "INFO", `Successful response from DMS. ${response.Message || ""}`);
WsLogger.createLogEvent(socket, "INFO", `Successful response from DMS. ${response.Message || ""}`);
} else {
CdkBase.createLogEvent(socket, "ERROR", `Error received from DMS: ${response.Message}`);
CdkBase.createLogEvent(socket, "DEBUG", `Error received from DMS: ${JSON.stringify(response)}`);
WsLogger.createLogEvent(socket, "ERROR", `Error received from DMS: ${response.Message}`);
WsLogger.createLogEvent(socket, "DEBUG", `Error received from DMS: ${JSON.stringify(response)}`);
}
}
exports.CheckForErrors = CheckForErrors;
async function QueryJobData(socket, jobid) {
CdkBase.createLogEvent(socket, "INFO", `Querying job data for id ${jobid}`);
WsLogger.createLogEvent(socket, "INFO", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.QUERY_JOBS_FOR_PBS_EXPORT, { id: jobid });
CdkBase.createLogEvent(socket, "DEBUG", `Job data query result ${JSON.stringify(result, null, 2)}`);
//WsLogger.createLogEvent(socket, "DEBUG", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
@@ -191,7 +196,7 @@ async function QueryVehicleFromDms(socket) {
CheckForErrors(socket, VehicleGetResponse);
return VehicleGetResponse;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in QueryVehicleFromDms - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryVehicleFromDms - ${error}`);
throw new Error(error);
}
}
@@ -220,9 +225,9 @@ async function QueryCustomersFromDms(socket) {
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, CustomerGetResponse);
return CustomerGetResponse && CustomerGetResponse.Contacts;
return CustomerGetResponse?.Contacts;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in QueryCustomersFromDms - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryCustomersFromDms - ${error}`);
throw new Error(error);
}
}
@@ -253,9 +258,9 @@ async function QueryCustomerBycodeFromDms(socket, CustomerRef) {
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, CustomerGetResponse);
return CustomerGetResponse && CustomerGetResponse.Contacts;
return CustomerGetResponse?.Contacts;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in QueryCustomersFromDms - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryCustomersFromDms - ${error}`);
throw new Error(error);
}
}
@@ -272,15 +277,15 @@ async function UpsertContactData(socket, selectedCustomerId) {
Code: socket.JobData.owner.accountingid,
...(socket.JobData.ownr_co_nm
? {
//LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_co_nm,
IsBusiness: true
}
//LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_co_nm,
IsBusiness: true
}
: {
LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_fn,
IsBusiness: false
}),
LastName: socket.JobData.ownr_ln,
FirstName: socket.JobData.ownr_fn,
IsBusiness: false
}),
//Salutation: "String",
//MiddleName: "String",
@@ -337,7 +342,7 @@ async function UpsertContactData(socket, selectedCustomerId) {
CheckForErrors(socket, ContactChangeResponse);
return ContactChangeResponse;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in UpsertContactData - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in UpsertContactData - ${error}`);
throw new Error(error);
}
}
@@ -357,10 +362,10 @@ async function UpsertVehicleData(socket, ownerRef) {
//FleetNumber: "String",
//Status: "String",
OwnerRef: ownerRef, // "00000000000000000000000000000000",
// ModelNumber: socket.JobData.vehicle && socket.JobData.vehicle.v_makecode,
// ModelNumber: socket.JobData.vehicle?.v_makecode,
Make: socket.JobData.v_make_desc,
Model: socket.JobData.v_model_desc,
Trim: socket.JobData.vehicle && socket.JobData.vehicle.v_trimcode,
Trim: socket.JobData.vehicle?.v_trimcode,
//VehicleType: "String",
Year: socket.JobData.v_model_yr,
Odometer: socket.JobData.kmout,
@@ -490,14 +495,16 @@ async function UpsertVehicleData(socket, ownerRef) {
CheckForErrors(socket, VehicleChangeResponse);
return VehicleChangeResponse;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in UpsertVehicleData - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in UpsertVehicleData - ${error}`);
throw new Error(error);
}
}
async function GetVehicleData(socket, ownerRef) {
try {
const { data: { Vehicles } } = await axios.post(
const {
data: { Vehicles }
} = await axios.post(
PBS_ENDPOINTS.VehicleGet,
{
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
@@ -510,7 +517,7 @@ async function GetVehicleData(socket, ownerRef) {
// "Trim": "String",
// "ModelNumber": "String",
// "StockNumber": "String",
VIN: socket.JobData.v_vin,
VIN: socket.JobData.v_vin
// "LicenseNumber": "String",
// "Lot": "String",
// "Status": "String",
@@ -528,24 +535,21 @@ async function GetVehicleData(socket, ownerRef) {
// "LotAccessDivisions": [0],
// "OdometerTo": 0,
// "OdometerFrom": 0
}
,
},
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, Vehicles);
if (Vehicles.length === 1) {
return Vehicles[0];
} else {
CdkBase.createLogEvent(socket, "ERROR", `Error in Getting Vehicle Data - ${Vehicles.length} vehicle(s) found`);
WsLogger.createLogEvent(socket, "ERROR", `Error in Getting Vehicle Data - ${Vehicles.length} vehicle(s) found`);
}
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in UpsertVehicleData - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in UpsertVehicleData - ${error}`);
throw new Error(error);
}
}
async function InsertAccountPostingData(socket) {
try {
const allocations = await CalculateAllocations(socket, socket.JobData.id);
@@ -629,7 +633,8 @@ async function InsertAccountPostingData(socket) {
Posting: {
Reference: socket.JobData.ro_number,
JournalCode: socket.txEnvelope.journal,
TransactionDate: moment(socket.JobData.date_invoiced).tz(socket.JobData.bodyshop.timezone).toISOString(), //"0001-01-01T00:00:00.0000000Z",
//Sample TransactionDate: "0001-01-01T00:00:00.0000000Z",
TransactionDate: moment(socket.JobData.date_invoiced).tz(socket.JobData.bodyshop.timezone).toISOString(),
Description: socket.txEnvelope.story,
//AdditionalInfo: "String",
Source: InstanceManager({ imex: "ImEX Online", rome: "Rome Online" }),
@@ -642,14 +647,15 @@ async function InsertAccountPostingData(socket) {
CheckForErrors(socket, AccountPostingChange);
return AccountPostingChange;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in InsertAccountPostingData - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertAccountPostingData - ${error}`);
throw new Error(error);
}
}
async function MarkJobExported(socket, jobid) {
CdkBase.createLogEvent(socket, "INFO", `Marking job as exported for id ${jobid}`);
WsLogger.createLogEvent(socket, "INFO", `Marking job as exported for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.MARK_JOB_EXPORTED, {
@@ -677,47 +683,52 @@ async function MarkJobExported(socket, jobid) {
async function InsertFailedExportLog(socket, error) {
try {
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.INSERT_EXPORT_LOG, {
log: {
logs: [{
bodyshopid: socket.JobData.bodyshop.id,
jobid: socket.JobData.id,
successful: false,
message: JSON.stringify(error),
useremail: socket.user.email
}
}]
});
return result;
} catch (error2) {
CdkBase.createLogEvent(socket, "ERROR", `Error in InsertFailedExportLog - ${error} - ${JSON.stringify(error2)}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertFailedExportLog - ${error} - ${JSON.stringify(error2)}`);
}
}
async function CreateRepairOrderInPBS(socket) {
try {
const { RepairOrders } = await RepairOrderGet(socket);
if (RepairOrders.length === 0) {
const InsertedRepairOrder = await RepairOrderChange(socket)
const InsertedRepairOrder = await RepairOrderChange(socket);
socket.InsertedRepairOrder = InsertedRepairOrder;
CdkBase.createLogEvent(socket, "INFO", `No repair orders found for vehicle. Inserting record.`);
WsLogger.createLogEvent(socket, "INFO", `No repair orders found for vehicle. Inserting record.`);
} else if (RepairOrders.length > 0) {
//Find out if it's a matching RO.
//This logic is used because the integration will simply add another line to an open RO if it exists.
const matchingRo = RepairOrders.find(ro => ro.Memo?.toLowerCase()?.includes(socket.JobData.ro_number.toLowerCase()))
//Find out if it's a matching RO.
//This logic is used because the integration will simply add another line to an open RO if it exists.
const matchingRo = RepairOrders.find((ro) =>
ro.Memo?.toLowerCase()?.includes(socket.JobData.ro_number.toLowerCase())
);
if (!matchingRo) {
CdkBase.createLogEvent(socket, "INFO", `ROs found for vehicle, but none match. Inserting record.`);
const InsertedRepairOrder = await RepairOrderChange(socket)
WsLogger.createLogEvent(socket, "INFO", `ROs found for vehicle, but none match. Inserting record.`);
const InsertedRepairOrder = await RepairOrderChange(socket);
socket.InsertedRepairOrder = InsertedRepairOrder;
} else {
CdkBase.createLogEvent(socket, "WARN", `Repair order appears to already exist in PBS. ${matchingRo.RepairOrderNumber}`);
WsLogger.createLogEvent(
socket,
"WARN",
`Repair order appears to already exist in PBS. ${matchingRo.RepairOrderNumber}`
);
}
}
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in CreateRepairOrderInPBS - ${error} - ${JSON.stringify(error)}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in CreateRepairOrderInPBS - ${error} - ${JSON.stringify(error)}`);
}
}
@@ -733,7 +744,7 @@ async function RepairOrderGet(socket) {
// "Tag": "String",
//"ContactRef": socket.contactRef,
// "ContactRefList": ["00000000000000000000000000000000"],
"VehicleRef": socket.vehicleRef?.ReferenceId || socket.vehicleRef?.VehicleId,
VehicleRef: socket.vehicleRef?.ReferenceId || socket.vehicleRef?.VehicleId
// "VehicleRefList": ["00000000000000000000000000000000"],
// "Status": "String",
// "CashieredSince": "0001-01-01T00:00:00.0000000Z",
@@ -749,7 +760,7 @@ async function RepairOrderGet(socket) {
CheckForErrors(socket, RepairOrderGet);
return RepairOrderGet;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in RepairOrderChange - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in RepairOrderChange - ${error}`);
throw new Error(error);
}
}
@@ -758,31 +769,32 @@ async function RepairOrderChange(socket) {
try {
const { data: RepairOrderChangeResponse } = await axios.post(
PBS_ENDPOINTS.RepairOrderChange,
{ //Additional details at https://partnerhub.pbsdealers.com/json/metadata?op=RepairOrderChange
"RepairOrderInfo": {
{
//Additional details at https://partnerhub.pbsdealers.com/json/metadata?op=RepairOrderChange
RepairOrderInfo: {
//"Id": "string/00000000-0000-0000-0000-000000000000",
//"RepairOrderId": "00000000000000000000000000000000",
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
"RepairOrderNumber": "00000000000000000000000000000000", //This helps force a new RO.
"RawRepairOrderNumber": "00000000000000000000000000000000",
RepairOrderNumber: "00000000000000000000000000000000", //This helps force a new RO.
RawRepairOrderNumber: "00000000000000000000000000000000",
// "RepairOrderNumber": socket.JobData.ro_number, //These 2 values are ignored as confirmed by PBS.
// "RawRepairOrderNumber": socket.JobData.ro_number,
"DateOpened": moment(),
DateOpened: moment(),
// "DateOpenedUTC": "0001-01-01T00:00:00.0000000Z",
// "DateCashiered": "0001-01-01T00:00:00.0000000Z",
// "DateCashieredUTC": "0001-01-01T00:00:00.0000000Z",
"DatePromised": socket.JobData.scheduled_completion,
DatePromised: socket.JobData.scheduled_completion,
// "DatePromisedUTC": "0001-01-01T00:00:00.0000000Z",
"DateVehicleCompleted": socket.JobData.actual_completion,
DateVehicleCompleted: socket.JobData.actual_completion,
// "DateCustomerNotified": "0001-01-01T00:00:00.0000000Z",
// "CSR": "String",
// "CSRRef": "00000000000000000000000000000000",
// "BookingUser": "String",
// "BookingUserRef": "00000000000000000000000000000000",
"ContactRef": socket.ownerRef?.ReferenceId || socket.ownerRef?.ContactId,
"VehicleRef": socket.vehicleRef?.ReferenceId || socket.vehicleRef?.VehicleId,
"MileageIn": socket.JobData.km_in,
"Tag": "BODYSHOP",
ContactRef: socket.ownerRef?.ReferenceId || socket.ownerRef?.ContactId,
VehicleRef: socket.vehicleRef?.ReferenceId || socket.vehicleRef?.VehicleId,
MileageIn: socket.JobData.km_in,
Tag: "BODYSHOP",
//"Status": "CLOSED", //Values here do not impact the status. Confirmed by PBS support.
Requests: [
{
@@ -791,61 +803,59 @@ async function RepairOrderChange(socket) {
// "CSR": "PBS",
// "CSRRef": "1ce12ac692564e94bda955d529ee911a",
// "Skill": "GEN",
"RequestCode": "MISC",
"RequestDescription": `VEHICLE REPAIRED AT BODYSHOP. PLEASE REFERENCE IMEX SHOP MANAGEMENT SYSTEM. ${socket.txEnvelope.story}`,
"Status": "Completed",
RequestCode: "MISC",
RequestDescription: `VEHICLE REPAIRED AT BODYSHOP. PLEASE REFERENCE IMEX SHOP MANAGEMENT SYSTEM. ${socket.txEnvelope.story}`,
Status: "Completed",
// "TechRef": "00000000000000000000000000000000",
"AllowedHours": 0,
"EstimateLabour": 0,
"EstimateParts": 0,
"ComeBack": false,
"AddedOperation": true,
"PartLines": [],
"PartRequestLines": [],
"LabourLines": [],
"SubletLines": [],
"TimePunches": [],
"Summary": {
"Labour": 0,
"Parts": 0,
"OilGas": 0,
"SubletTow": 0,
"Misc": 0,
"Environment": 0,
"ShopSupplies": 0,
"Freight": 0,
"WarrantyDeductible": 0,
"Discount": 0,
"SubTotal": 0,
"Tax1": 0,
"Tax2": 0,
"InvoiceTotal": 0,
"CustomerDeductible": 0,
"GrandTotal": 0,
"LabourDiscount": 0,
"PartDiscount": 0,
"ServiceFeeTotal": 0,
"OEMDiscount": 0
AllowedHours: 0,
EstimateLabour: 0,
EstimateParts: 0,
ComeBack: false,
AddedOperation: true,
PartLines: [],
PartRequestLines: [],
LabourLines: [],
SubletLines: [],
TimePunches: [],
Summary: {
Labour: 0,
Parts: 0,
OilGas: 0,
SubletTow: 0,
Misc: 0,
Environment: 0,
ShopSupplies: 0,
Freight: 0,
WarrantyDeductible: 0,
Discount: 0,
SubTotal: 0,
Tax1: 0,
Tax2: 0,
InvoiceTotal: 0,
CustomerDeductible: 0,
GrandTotal: 0,
LabourDiscount: 0,
PartDiscount: 0,
ServiceFeeTotal: 0,
OEMDiscount: 0
},
"LineType": "RequestLine",
},
LineType: "RequestLine"
}
],
"Memo": socket.txEnvelope.story,
Memo: socket.txEnvelope.story
},
"IsAsynchronous": false,
IsAsynchronous: false
// "UserRequest": "String",
// "UserRef": "00000000000000000000000000000000"
}
},
,
{ auth: PBS_CREDENTIALS, socket }
);
CheckForErrors(socket, RepairOrderChangeResponse);
return RepairOrderChangeResponse;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in RepairOrderChange - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in RepairOrderChange - ${error}`);
throw new Error(error);
}
}
}

View File

@@ -1,10 +1,11 @@
const GraphQLClient = require("graphql-request").GraphQLClient;
const queries = require("../graphql-client/queries");
const CdkBase = require("../web-sockets/web-socket");
const CreateFortellisLogEvent = require("../fortellis/fortellis-logger");
const Dinero = require("dinero.js");
const _ = require("lodash");
const WsLogger = require("../web-sockets/createLogEvent")
const InstanceManager = require("../utils/instanceMgr").default;
const { DiscountNotAlreadyCounted } = InstanceManager({
imex: require("../job/job-totals"),
@@ -13,37 +14,40 @@ const { DiscountNotAlreadyCounted } = InstanceManager({
exports.defaultRoute = async function (req, res) {
try {
CdkBase.createLogEvent(req, "DEBUG", `Received request to calculate allocations for ${req.body.jobid}`);
WsLogger.createLogEvent(req, "DEBUG", `Received request to calculate allocations for ${req.body.jobid}`);
const jobData = await QueryJobData(req, req.BearerToken, req.body.jobid);
return res.status(200).json({ data: calculateAllocations(req, jobData) });
} catch (error) {
////console.log(error);
CdkBase.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
WsLogger.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
WsLogger.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error.stack}`);
res.status(500).json({ error: `Error encountered in CdkCalculateAllocations. ${error}` });
}
};
exports.default = async function (socket, jobid) {
exports.default = async function (socket, jobid, isFortellis = false) {
try {
const jobData = await QueryJobData(socket, "Bearer " + socket.handshake.auth.token, jobid);
return calculateAllocations(socket, jobData);
const jobData = await QueryJobData(socket, "Bearer " + socket.handshake.auth.token, jobid, isFortellis);
return calculateAllocations(socket, jobData, isFortellis);
} catch (error) {
////console.log(error);
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
const loggingFunction = isFortellis ? CreateFortellisLogEvent : WsLogger.createLogEvent;
loggingFunction(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
loggingFunction(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error.stack}`);
}
};
async function QueryJobData(connectionData, token, jobid) {
CdkBase.createLogEvent(connectionData, "DEBUG", `Querying job data for id ${jobid}`);
async function QueryJobData(connectionData, token, jobid, isFortellis) {
const loggingFunction = isFortellis ? CreateFortellisLogEvent : WsLogger.createLogEvent;
loggingFunction(connectionData, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client.setHeaders({ Authorization: token }).request(queries.GET_CDK_ALLOCATIONS, { id: jobid });
CdkBase.createLogEvent(connectionData, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`);
//loggingFunction(connectionData, "DEBUG", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
function calculateAllocations(connectionData, job) {
function calculateAllocations(connectionData, job, isFortellis) {
const { bodyshop } = job;
const loggingFunction = isFortellis ? CreateFortellisLogEvent : WsLogger.createLogEvent;
const taxAllocations = InstanceManager({
executeFunction: true,
deubg: true,
@@ -132,11 +136,11 @@ function calculateAllocations(connectionData, job) {
? val.prt_dsmk_m
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
: Dinero({
amount: Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0))
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
amount: Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0))
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
: Dinero()
);
@@ -159,7 +163,7 @@ function calculateAllocations(connectionData, job) {
const selectedDmsAllocationConfig = bodyshop.md_responsibility_centers.dms_defaults.find(
(d) => d.name === job.dms_allocation
);
CdkBase.createLogEvent(
loggingFunction(
connectionData,
"DEBUG",
`Using DMS Allocation ${selectedDmsAllocationConfig && selectedDmsAllocationConfig.name} for cost export.`
@@ -194,8 +198,8 @@ function calculateAllocations(connectionData, job) {
let TicketTotal = Dinero({
amount: Math.round(
ticket.rate *
(ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) *
100
(ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) *
100
)
});
//Add it to the right cost center.
@@ -360,10 +364,10 @@ function calculateAllocations(connectionData, job) {
Dinero(job.job_totals.parts.adjustments[key])
);
} else {
CdkBase.createLogEvent(
loggingFunction(
connectionData,
"ERROR",
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account: ${accountName}`
);
}
});
@@ -383,10 +387,10 @@ function calculateAllocations(connectionData, job) {
Dinero(job.job_totals.rates[key].adjustments)
);
} else {
CdkBase.createLogEvent(
loggingFunction(
connectionData,
"ERROR",
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account: ${accountName}`
);
}
}
@@ -427,37 +431,37 @@ function calculateAllocations(connectionData, job) {
...(job.job_totals.totals.ttl_adjustment
? [
{
center: "SUB ADJ",
sale: Dinero(job.job_totals.totals.ttl_adjustment),
cost: Dinero(),
profitCenter: {
name: "SUB ADJ",
accountdesc: "SUB ADJ",
accountitem: "SUB ADJ",
accountname: "SUB ADJ",
dms_acctnumber: bodyshop.md_responsibility_centers.ttl_adjustment.dms_acctnumber
},
costCenter: {}
}
]
{
center: "SUB ADJ",
sale: Dinero(job.job_totals.totals.ttl_adjustment),
cost: Dinero(),
profitCenter: {
name: "SUB ADJ",
accountdesc: "SUB ADJ",
accountitem: "SUB ADJ",
accountname: "SUB ADJ",
dms_acctnumber: bodyshop.md_responsibility_centers.ttl_adjustment.dms_acctnumber
},
costCenter: {}
}
]
: []),
...(job.job_totals.totals.ttl_tax_adjustment
? [
{
center: "TAX ADJ",
sale: Dinero(job.job_totals.totals.ttl_tax_adjustment),
cost: Dinero(),
profitCenter: {
name: "TAX ADJ",
accountdesc: "TAX ADJ",
accountitem: "TAX ADJ",
accountname: "TAX ADJ",
dms_acctnumber: bodyshop.md_responsibility_centers.ttl_tax_adjustment.dms_acctnumber
},
costCenter: {}
}
]
{
center: "TAX ADJ",
sale: Dinero(job.job_totals.totals.ttl_tax_adjustment),
cost: Dinero(),
profitCenter: {
name: "TAX ADJ",
accountdesc: "TAX ADJ",
accountitem: "TAX ADJ",
accountname: "TAX ADJ",
dms_acctnumber: bodyshop.md_responsibility_centers.ttl_tax_adjustment.dms_acctnumber
},
costCenter: {}
}
]
: [])
];
}

View File

@@ -5,6 +5,12 @@ const CdkWsdl = require("./cdk-wsdl").default;
const logger = require("../utils/logger");
const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
const {
MakeFortellisCall,
FortellisActions,
GetAuthToken,
GetDepartmentId
} = require("../fortellis/fortellis-helpers");
// exports.default = async function (socket, cdk_dealerid) {
// try {
@@ -105,3 +111,85 @@ async function GetCdkMakes(req, cdk_dealerid) {
throw new Error(error);
}
}
async function GetFortellisMakes(req, cdk_dealerid) {
logger.log("fortellis-replace-makes-models", "DEBUG", req.user.email, null, {
cdk_dealerid
});
try {
const result = await MakeFortellisCall({
...FortellisActions.GetMakeModel,
headers: {},
redisHelpers: {
setSessionTransactionData: () => {
return null;
},
getSessionTransactionData: () => {
return null;
}
},
socket: { emit: () => null },
jobid: null,
body: {},
SubscriptionObject: {
SubscriptionID: cdk_dealerid
}
});
logger.log("fortellis-replace-makes-models-response", "ERROR", req.user.email, null, {
cdk_dealerid,
xml: result
});
return result.data;
} catch (error) {
logger.log("fortellis-replace-makes-models-error", "ERROR", req.user.email, null, {
cdk_dealerid,
error
});
throw new Error(error);
}
}
exports.fortellis = async function ReloadFortellisMakes(req, res) {
const { bodyshopid, cdk_dealerid } = req.body;
try {
//Query all CDK Models
const newList = await GetFortellisMakes(req, cdk_dealerid);
const BearerToken = req.BearerToken;
const client = req.userGraphQLClient;
const deleteResult = await client
.setHeaders({ Authorization: BearerToken })
.request(queries.DELETE_ALL_DMS_VEHICLES, {});
//Insert the new ones.
const insertResult = await client.setHeaders({ Authorization: BearerToken }).request(queries.INSERT_DMS_VEHICLES, {
vehicles: newList.map((i) => {
return {
bodyshopid,
makecode: i.makeCode,
modelcode: i.modelCode,
make: i.makeFullName,
model: i.modelFullName
};
})
});
logger.log("fortellis-replace-makes-models-success", "DEBUG", req.user.email, null, {
cdk_dealerid,
count: newList.length
});
res.sendStatus(200);
} catch (error) {
logger.log("fortellis-replace-makes-models-error", "ERROR", req.user.email, null, {
cdk_dealerid,
error: error.message,
stack: error.stack
});
res.status(500).json(error);
}
};

View File

@@ -1,33 +1,36 @@
const GraphQLClient = require("graphql-request").GraphQLClient;
const soap = require("soap");
const queries = require("../graphql-client/queries");
const CdkBase = require("../web-sockets/web-socket");
const CdkWsdl = require("./cdk-wsdl").default;
const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
const CalcualteAllocations = require("./cdk-calculate-allocations").default;
const InstanceMgr = require("../utils/instanceMgr").default;
const WsLogger = require("../web-sockets/createLogEvent");
const moment = require("moment-timezone");
const replaceSpecialRegex = /[^a-zA-Z0-9 .,\n #]+/g;
exports.default = async function (socket, { txEnvelope, jobid }) {
const defaultHandler = async (socket, { txEnvelope, jobid }) => {
////Store the following information into the redis store for this transaction.
socket.logEvents = [];
socket.recordid = jobid;
socket.txEnvelope = txEnvelope;
////
try {
CdkBase.createLogEvent(socket, "DEBUG", `Received Job export request for id ${jobid}`);
WsLogger.createLogEvent(socket, "DEBUG", `Received Job export request for id ${jobid}`);
const JobData = await QueryJobData(socket, jobid);
socket.JobData = JobData;
const DealerId = JobData.bodyshop.cdk_dealerid;
CdkBase.createLogEvent(socket, "DEBUG", `Dealer ID detected: ${JSON.stringify(DealerId)}`);
WsLogger.createLogEvent(socket, "DEBUG", `Dealer ID detected: ${JSON.stringify(DealerId)}`);
CdkBase.createLogEvent(socket, "DEBUG", `{1} Begin Calculate DMS Vehicle ID using VIN: ${JobData.v_vin}`);
WsLogger.createLogEvent(socket, "DEBUG", `{1} Begin Calculate DMS Vehicle ID using VIN: ${JobData.v_vin}`);
socket.DMSVid = await CalculateDmsVid(socket, JobData);
if (socket.DMSVid.newId === "N") {
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"DEBUG",
`{2.1} Querying the Vehicle using the DMSVid: ${socket.DMSVid.vehiclesVehId}`
@@ -35,10 +38,10 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
socket.DMSVeh = await QueryDmsVehicleById(socket, JobData, socket.DMSVid);
const DMSVehCustomer =
socket.DMSVeh && socket.DMSVeh.owners && socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
socket.DMSVeh?.owners && socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
if (DMSVehCustomer && DMSVehCustomer.id && DMSVehCustomer.id.value) {
CdkBase.createLogEvent(
if (DMSVehCustomer?.id && DMSVehCustomer.id.value) {
WsLogger.createLogEvent(
socket,
"DEBUG",
`{2.2} Querying the Customer using the ID from DMSVeh: ${DMSVehCustomer.id.value}`
@@ -47,7 +50,7 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
}
}
CdkBase.createLogEvent(socket, "DEBUG", `{2.3} Querying the Customer using the name.`);
WsLogger.createLogEvent(socket, "DEBUG", `{2.3} Querying the Customer using the name.`);
socket.DMSCustList = await QueryDmsCustomerByName(socket, JobData);
@@ -56,43 +59,48 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
...socket.DMSCustList
]);
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkJobExport. ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error encountered in CdkJobExport. ${error}`);
}
};
exports.default = defaultHandler;
async function CdkSelectedCustomer(socket, selectedCustomerId) {
try {
socket.selectedCustomerId = selectedCustomerId;
if (selectedCustomerId) {
CdkBase.createLogEvent(socket, "DEBUG", `{3.1} Querying the Customer using Customer ID: ${selectedCustomerId}`);
WsLogger.createLogEvent(socket, "DEBUG", `{3.1} Querying the Customer using Customer ID: ${selectedCustomerId}`);
socket.DMSCust = await QueryDmsCustomerById(socket, socket.JobData, selectedCustomerId);
} else {
CdkBase.createLogEvent(socket, "DEBUG", `{3.2} Generating a new customer ID.`);
WsLogger.createLogEvent(socket, "DEBUG", `{3.2} Generating a new customer ID.`);
const newCustomerId = await GenerateDmsCustomerNumber(socket);
CdkBase.createLogEvent(socket, "DEBUG", `{3.3} Inserting new customer with ID: ${newCustomerId}`);
WsLogger.createLogEvent(socket, "DEBUG", `{3.3} Inserting new customer with ID: ${newCustomerId}`);
socket.DMSCust = await InsertDmsCustomer(socket, newCustomerId);
}
if (socket.DMSVid.newId === "Y") {
CdkBase.createLogEvent(socket, "DEBUG", `{4.1} Inserting new vehicle with ID: ID ${socket.DMSVid.vehiclesVehId}`);
WsLogger.createLogEvent(
socket,
"DEBUG",
`{4.1} Inserting new vehicle with ID: ID ${socket.DMSVid.vehiclesVehId}`
);
socket.DMSVeh = await InsertDmsVehicle(socket);
} else {
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"DEBUG",
`{4.2} Querying Existing Vehicle using ID ${socket.DMSVid.vehiclesVehId}`
);
socket.DMSVeh = await QueryDmsVehicleById(socket, socket.JobData, socket.DMSVid);
CdkBase.createLogEvent(socket, "DEBUG", `{4.3} Updating Existing Vehicle to associate to owner.`);
WsLogger.createLogEvent(socket, "DEBUG", `{4.3} Updating Existing Vehicle to associate to owner.`);
socket.DMSVeh = await UpdateDmsVehicle(socket);
}
CdkBase.createLogEvent(socket, "DEBUG", `{5} Creating Transaction header with Dms Start WIP`);
WsLogger.createLogEvent(socket, "DEBUG", `{5} Creating Transaction header with Dms Start WIP`);
socket.DMSTransHeader = await InsertDmsStartWip(socket);
CdkBase.createLogEvent(socket, "DEBUG", `{5.1} Creating Transaction with ID ${socket.DMSTransHeader.transID}`);
WsLogger.createLogEvent(socket, "DEBUG", `{5.1} Creating Transaction with ID ${socket.DMSTransHeader.transID}`);
socket.DMSBatchTxn = await InsertDmsBatchWip(socket);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"DEBUG",
`{6} Attempting to post Transaction with ID ${socket.DMSTransHeader.transID}`
@@ -100,23 +108,23 @@ async function CdkSelectedCustomer(socket, selectedCustomerId) {
socket.DmsBatchTxnPost = await PostDmsBatchWip(socket);
if (socket.DmsBatchTxnPost.code === "success") {
//something
CdkBase.createLogEvent(socket, "DEBUG", `{6} Successfully posted sransaction to DMS.`);
WsLogger.createLogEvent(socket, "DEBUG", `{6} Successfully posted sransaction to DMS.`);
await MarkJobExported(socket, socket.JobData.id);
CdkBase.createLogEvent(socket, "DEBUG", `{5} Updating Service Vehicle History.`);
WsLogger.createLogEvent(socket, "DEBUG", `{5} Updating Service Vehicle History.`);
socket.DMSVehHistory = await InsertServiceVehicleHistory(socket);
socket.emit("export-success", socket.JobData.id);
} else {
//Get the error code
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"DEBUG",
`{6.1} Getting errors for Transaction ID ${socket.DMSTransHeader.transID}`
);
socket.DmsError = await QueryDmsErrWip(socket);
//Delete the transaction
CdkBase.createLogEvent(socket, "DEBUG", `{6.2} Deleting Transaction ID ${socket.DMSTransHeader.transID}`);
WsLogger.createLogEvent(socket, "DEBUG", `{6.2} Deleting Transaction ID ${socket.DMSTransHeader.transID}`);
socket.DmsBatchTxnPost = await DeleteDmsWip(socket);
socket.DmsError.errMsg
@@ -125,29 +133,33 @@ async function CdkSelectedCustomer(socket, selectedCustomerId) {
(e) =>
e !== null &&
e !== "" &&
CdkBase.createLogEvent(socket, "ERROR", `Error(s) encountered in posting transaction. ${e}`)
WsLogger.createLogEvent(socket, "ERROR", `Error(s) encountered in posting transaction. ${e}`)
);
}
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkSelectedCustomer. ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error encountered in CdkSelectedCustomer. ${error}`);
await InsertFailedExportLog(socket, error);
} finally {
//Ensure we always insert logEvents
//GQL to insert logevents.
CdkBase.createLogEvent(socket, "DEBUG", `Capturing log events to database.`);
WsLogger.createLogEvent(socket, "DEBUG", `Capturing log events to database.`);
}
}
exports.CdkSelectedCustomer = CdkSelectedCustomer;
async function QueryJobData(socket, jobid) {
CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
WsLogger.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.QUERY_JOBS_FOR_CDK_EXPORT, { id: jobid });
CdkBase.createLogEvent(socket, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`);
//WsLogger.createLogEvent(socket, "SILLY", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
@@ -161,11 +173,11 @@ async function CalculateDmsVid(socket, JobData) {
});
const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.getVehIdsAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.getVehIdsAsync request.`);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.getVehIdsAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.getVehIdsAsync response.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientVehicleInsertUpdate.getVehIdsAsync Result ${JSON.stringify(result, null, 2)}`
@@ -178,15 +190,15 @@ async function CalculateDmsVid(socket, JobData) {
//return result && result.return && result.return[0];
} catch (error) {
CdkBase.createXmlEvent(socket, error.request, `soapClientVehicleInsertUpdate.getVehIdsAsync request.`, true);
WsLogger.createXmlEvent(socket, error.request, `soapClientVehicleInsertUpdate.getVehIdsAsync request.`, true);
CdkBase.createXmlEvent(
WsLogger.createXmlEvent(
socket,
error.response && error.response.data,
error.response?.data,
`soapClientVehicleInsertUpdate.getVehIdsAsync response.`,
true
);
CdkBase.createLogEvent(socket, "ERROR", `{1} Error in CalculateDmsVid - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `{1} Error in CalculateDmsVid - ${error}`);
throw new Error(error);
}
}
@@ -206,19 +218,19 @@ async function QueryDmsVehicleById(socket, JobData, DMSVid) {
const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.readAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.readAsync request.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientVehicleInsertUpdate.readAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.readAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.readAsync response.`);
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
const VehicleFromDMS = result && result.return && result.return.vehicle;
const VehicleFromDMS = result?.return && result.return.vehicle;
return VehicleFromDMS;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in QueryDmsVehicleById - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryDmsVehicleById - ${error}`);
throw new Error(error);
}
}
@@ -237,28 +249,23 @@ async function QueryDmsCustomerById(socket, JobData, CustomerId) {
const [result, rawResponse, , rawRequest] = soapResponseCustomerInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientCustomerInsertUpdate.readAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientCustomerInsertUpdate.readAsync request.`);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.readAsync response.`);
CdkBase.createLogEvent(
WsLogger.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.readAsync response.`);
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientCustomerInsertUpdate.readAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
const CustomersFromDms = result && result.return && result.return.customerParty;
const CustomersFromDms = result?.return && result.return.customerParty;
return CustomersFromDms;
} catch (error) {
CdkBase.createXmlEvent(socket, error.request, `soapClientCustomerInsertUpdate.readAsync request.`, true);
WsLogger.createXmlEvent(socket, error.request, `soapClientCustomerInsertUpdate.readAsync request.`, true);
CdkBase.createXmlEvent(
socket,
error.response && error.response.data,
`soapClientCustomerInsertUpdate.readAsync response.`,
true
);
WsLogger.createXmlEvent(socket, error.response?.data, `soapClientCustomerInsertUpdate.readAsync response.`, true);
CdkBase.createLogEvent(socket, "ERROR", `Error in QueryDmsCustomerById - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryDmsCustomerById - ${error}`);
throw new Error(error);
}
}
@@ -270,7 +277,7 @@ async function QueryDmsCustomerByName(socket, JobData) {
: `${JobData.ownr_ln},${JobData.ownr_fn}`
).replace(replaceSpecialRegex, "");
CdkBase.createLogEvent(socket, "DEBUG", `Begin Query DMS Customer by Name using: ${ownerName}`);
WsLogger.createLogEvent(socket, "DEBUG", `Begin Query DMS Customer by Name using: ${ownerName}`);
try {
const soapClientCustomerSearch = await soap.createClientAsync(CdkWsdl.CustomerSearch);
@@ -285,29 +292,29 @@ async function QueryDmsCustomerByName(socket, JobData) {
const [result, rawResponse, , rawRequest] = soapResponseCustomerSearch;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientCustomerSearch.executeSearchBulkAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientCustomerSearch.executeSearchBulkAsync request.`);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerSearch.executeSearchBulkAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientCustomerSearch.executeSearchBulkAsync response.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientCustomerSearch.executeSearchBulkAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerSearch);
const CustomersFromDms = (result && result.return) || [];
const CustomersFromDms = result?.return || [];
return CustomersFromDms;
} catch (error) {
CdkBase.createXmlEvent(socket, error.request, `soapClientCustomerSearch.executeSearchBulkAsync request.`, true);
WsLogger.createXmlEvent(socket, error.request, `soapClientCustomerSearch.executeSearchBulkAsync request.`, true);
CdkBase.createXmlEvent(
WsLogger.createXmlEvent(
socket,
error.response && error.response.data,
error.response?.data,
`soapClientCustomerSearch.executeSearchBulkAsync response.`,
true
);
CdkBase.createLogEvent(socket, "ERROR", `Error in QueryDmsCustomerByName - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryDmsCustomerByName - ${error}`);
throw new Error(error);
}
}
@@ -318,7 +325,8 @@ async function GenerateDmsCustomerNumber(socket) {
const soapResponseCustomerInsertUpdate = await soapClientCustomerInsertUpdate.getCustomerNumberAsync(
{
arg0: CDK_CREDENTIALS,
arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards.
//TODO: Verify why this does not follow the other standards.
arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid },
arg2: { userId: null }
},
@@ -327,33 +335,33 @@ async function GenerateDmsCustomerNumber(socket) {
const [result, rawResponse, , rawRequest] = soapResponseCustomerInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientCustomerInsertUpdate.getCustomerNumberAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientCustomerInsertUpdate.getCustomerNumberAsync request.`);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.getCustomerNumberAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.getCustomerNumberAsync response.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientCustomerInsertUpdate.getCustomerNumberAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
const customerNumber = result && result.return && result.return.customerNumber;
const customerNumber = result?.return && result.return.customerNumber;
return customerNumber;
} catch (error) {
CdkBase.createXmlEvent(
WsLogger.createXmlEvent(
socket,
error.request,
`soapClientCustomerInsertUpdate.getCustomerNumberAsync request.`,
true
);
CdkBase.createXmlEvent(
WsLogger.createXmlEvent(
socket,
error.response && error.response.data,
error.response?.data,
`soapClientCustomerInsertUpdate.getCustomerNumberAsync response.`,
true
);
CdkBase.createLogEvent(socket, "ERROR", `Error in GenerateDmsCustomerNumber - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in GenerateDmsCustomerNumber - ${error}`);
throw new Error(error);
}
}
@@ -416,27 +424,22 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
);
const [result, rawResponse, , rawRequest] = soapResponseCustomerInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientCustomerInsertUpdate.insertAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientCustomerInsertUpdate.insertAsync request.`);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.insertAsync response.`);
CdkBase.createLogEvent(
WsLogger.createXmlEvent(socket, rawResponse, `soapClientCustomerInsertUpdate.insertAsync response.`);
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientCustomerInsertUpdate.insertAsync Result ${JSON.stringify(result, null, 2)}`
);
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
const customer = result && result.return && result.return.customerParty;
const customer = result?.return && result.return.customerParty;
return customer;
} catch (error) {
CdkBase.createXmlEvent(socket, error.request, `soapClientCustomerInsertUpdate.insertAsync request.`, true);
WsLogger.createXmlEvent(socket, error.request, `soapClientCustomerInsertUpdate.insertAsync request.`, true);
CdkBase.createXmlEvent(
socket,
error.response && error.response.data,
`soapClientCustomerInsertUpdate.insertAsync response.`,
true
);
CdkBase.createLogEvent(socket, "ERROR", `Error in InsertDmsCustomer - ${error}`);
WsLogger.createXmlEvent(socket, error.response?.data, `soapClientCustomerInsertUpdate.insertAsync response.`, true);
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertDmsCustomer - ${error}`);
throw new Error(error);
}
}
@@ -497,19 +500,19 @@ async function InsertDmsVehicle(socket) {
const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.insertAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.insertAsync request.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientVehicleInsertUpdate.insertAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.insertAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.insertAsync response.`);
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
const VehicleFromDMS = result && result.return && result.return.vehicle;
const VehicleFromDMS = result?.return && result.return.vehicle;
return VehicleFromDMS;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in InsertDmsVehicle - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertDmsVehicle - ${error}`);
throw new Error(error);
}
}
@@ -523,12 +526,10 @@ async function UpdateDmsVehicle(socket) {
//if it's a generic customer, don't update the vehicle owners.
if (socket.selectedCustomerId === socket.JobData.bodyshop.cdk_configuration.generic_customer_number) {
ids = socket.DMSVeh && socket.DMSVeh.owners && socket.DMSVeh.owners;
ids = socket.DMSVeh?.owners && socket.DMSVeh.owners;
} else {
const existingOwnerinVeh =
socket.DMSVeh &&
socket.DMSVeh.owners &&
socket.DMSVeh.owners.find((o) => o.id.value === socket.DMSCust.id.value);
socket.DMSVeh?.owners && socket.DMSVeh.owners.find((o) => o.id.value === socket.DMSCust.id.value);
if (existingOwnerinVeh) {
ids = socket.DMSVeh.owners.map((o) => {
@@ -540,10 +541,7 @@ async function UpdateDmsVehicle(socket) {
};
});
} else {
const oldOwner =
socket.DMSVeh &&
socket.DMSVeh.owners &&
socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
const oldOwner = socket.DMSVeh?.owners && socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT");
ids = [
{
@@ -603,19 +601,19 @@ async function UpdateDmsVehicle(socket) {
});
const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.updateAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientVehicleInsertUpdate.updateAsync request.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"DEBUG",
`soapClientVehicleInsertUpdate.updateAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.updateAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientVehicleInsertUpdate.updateAsync response.`);
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
const VehicleFromDMS = result && result.return && result.return.vehicle;
const VehicleFromDMS = result?.return && result.return.vehicle;
return VehicleFromDMS;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in UpdateDmsVehicle - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in UpdateDmsVehicle - ${error}`);
throw new Error(error);
}
}
@@ -642,18 +640,18 @@ async function InsertServiceVehicleHistory(socket) {
const [result, rawResponse, , rawRequest] = soapResponseServiceHistoryInsert;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert request.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientServiceHistoryInsert.serviceHistoryHeaderInsert Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert response.`);
CheckCdkResponseForError(socket, soapResponseServiceHistoryInsert);
return result && result.return;
return result?.return;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in InsertServiceVehicleHistory - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertServiceVehicleHistory - ${error}`);
throw new Error(error);
}
}
@@ -669,8 +667,10 @@ async function InsertDmsStartWip(socket) {
acctgDate: moment().tz(socket.JobData.bodyshop.timezone).format("YYYY-MM-DD"),
//socket.JobData.invoice_date
desc: socket.txEnvelope.story && socket.txEnvelope.story.replace(replaceSpecialRegex, ""),
docType: 10 || 7, //Need to check what this usually would be? Apparently it is almost always 10 or 7.
//1 Cash Receipt , 2 Check, 3 Journal Voucher, 4 Parts invoice, 5 Payable Invoice, 6 Recurring Entry, 7 Repair Order Invoice, 8 Vehicle Purchase Invoice, 9 Vehicle Sale Invoice, 10 Other, 11 Payroll, 12 Finance Charge, 13 FMLR Invoice, 14 Parts Credit Memo, 15 Manufacturer Document, 16 FMLR Credit Memo
docType: 10, // Need to check what this usually would be? Apparently it is almost always 10 or 7.
//1 Cash Receipt , 2 Check, 3 Journal Voucher, 4 Parts invoice, 5 Payable Invoice, 6 Recurring Entry, 7 Repair
// Order Invoice, 8 Vehicle Purchase Invoice, 9 Vehicle Sale Invoice, 10 Other, 11 Payroll, 12 Finance Charge,
// 13 FMLR Invoice, 14 Parts Credit Memo, 15 Manufacturer Document, 16 FMLR Credit Memo
m13Flag: 0,
refer: socket.JobData.ro_number,
srcCo: socket.JobData.bodyshop.cdk_configuration.srcco,
@@ -682,19 +682,19 @@ async function InsertDmsStartWip(socket) {
const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doStartWIPAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doStartWIPAsync request.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientAccountingGLInsertUpdate.doStartWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doStartWIPAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doStartWIPAsync response.`);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const TransactionHeader = result && result.return;
const TransactionHeader = result?.return;
return TransactionHeader;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in InsertDmsStartWip - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertDmsStartWip - ${error}`);
throw new Error(error);
}
}
@@ -713,19 +713,19 @@ async function InsertDmsBatchWip(socket) {
const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync request.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync response.`);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const BatchWipResult = result && result.return;
const BatchWipResult = result?.return;
return BatchWipResult;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in InsertDmsBatchWip - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertDmsBatchWip - ${error}`);
throw new Error(error);
}
}
@@ -877,19 +877,19 @@ async function PostDmsBatchWip(socket) {
const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync request.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`);
// CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const PostResult = result && result.return;
const PostResult = result?.return;
return PostResult;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in PostDmsBatchWip - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in PostDmsBatchWip - ${error}`);
throw new Error(error);
}
}
@@ -906,19 +906,19 @@ async function QueryDmsErrWip(socket) {
const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doErrWIPAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doErrWIPAsync request.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"DEBUG",
`soapClientAccountingGLInsertUpdate.doErrWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doErrWIPAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doErrWIPAsync response.`);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const PostResult = result && result.return;
const PostResult = result?.return;
return PostResult;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in QueryDmsErrWip - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in QueryDmsErrWip - ${error}`);
throw new Error(error);
}
}
@@ -937,28 +937,31 @@ async function DeleteDmsWip(socket) {
const [result, rawResponse, , rawRequest] = soapResponseAccountingGLInsertUpdate;
CdkBase.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync request.`);
WsLogger.createXmlEvent(socket, rawRequest, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync request.`);
CdkBase.createLogEvent(
WsLogger.createLogEvent(
socket,
"SILLY",
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(result, null, 2)}`
);
CdkBase.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`);
WsLogger.createXmlEvent(socket, rawResponse, `soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const PostResult = result && result.return;
const PostResult = result?.return;
return PostResult;
} catch (error) {
CdkBase.createLogEvent(socket, "ERROR", `Error in PostDmsBatchWip - ${error}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in PostDmsBatchWip - ${error}`);
throw new Error(error);
}
}
async function MarkJobExported(socket, jobid) {
CdkBase.createLogEvent(socket, "DEBUG", `Marking job as exported for id ${jobid}`);
WsLogger.createLogEvent(socket, "DEBUG", `Marking job as exported for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.MARK_JOB_EXPORTED, {
jobId: jobid,
job: {
@@ -984,20 +987,23 @@ async function MarkJobExported(socket, jobid) {
async function InsertFailedExportLog(socket, error) {
try {
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const currentToken =
(socket?.data && socket.data.authToken) || (socket?.handshake?.auth && socket.handshake.auth.token);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.setHeaders({ Authorization: `Bearer ${currentToken}` })
.request(queries.INSERT_EXPORT_LOG, {
log: {
logs: [{
bodyshopid: socket.JobData.bodyshop.id,
jobid: socket.JobData.id,
successful: false,
message: JSON.stringify(error),
useremail: socket.user.email
}
}]
});
return result;
} catch (error2) {
CdkBase.createLogEvent(socket, "ERROR", `Error in InsertFailedExportLog - ${error} - ${JSON.stringify(error2)}`);
WsLogger.createLogEvent(socket, "ERROR", `Error in InsertFailedExportLog - ${error} - ${JSON.stringify(error2)}`);
}
}

View File

@@ -0,0 +1,460 @@
const path = require("path");
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
// const CalcualteAllocations = require("../cdk/cdk-calculate-allocations").default;
const CreateFortellisLogEvent = require("./fortellis-logger");
const logger = require("../utils/logger");
const uuid = require("uuid").v4;
const AxiosLib = require("axios").default;
const axios = AxiosLib.create();
const axiosCurlirize = require("axios-curlirize").default;
// Custom error class for Fortellis API errors
class FortellisApiError extends Error {
constructor(message, details) {
super(message);
this.name = "FortellisApiError";
this.reqId = details.reqId;
this.url = details.url;
this.apiName = details.apiName;
this.errorData = details.errorData;
this.errorStatus = details.errorStatus;
this.errorStatusText = details.errorStatusText;
this.originalError = details.originalError;
}
}
axiosCurlirize(axios, (_result, _err) => {
//Left intentionally blank. We don't want to console.log. We handle logging the cURL in MakeFortellisCall once completed.
});
const getTransactionType = (jobid) => `fortellis:${jobid}`;
const defaultFortellisTTL = 60 * 60;
async function GetAuthToken() {
//Done with Authorization Code Flow
//https://docs.fortellis.io/docs/tutorials/solution-integration/authorization-code-flow/
//TODO: This should get stored in the redis cache and only be refreshed when it expires.
const {
data: { access_token, expires_in, token_type }
} = await axios.post(
process.env.FORTELLIS_AUTH_URL,
{},
{
auth: {
username: process.env.FORTELLIS_KEY,
password: process.env.FORTELLIS_SECRET
},
params: {
grant_type: "client_credentials",
scope: "anonymous"
}
}
);
return access_token;
}
async function FetchSubscriptions({ redisHelpers, socket, jobid, SubscriptionObject }) {
try {
const { setSessionTransactionData, getSessionTransactionData } = redisHelpers;
//Get Subscription ID from Transaction Envelope
const { SubscriptionID } = SubscriptionObject
? SubscriptionObject
: await getSessionTransactionData(socket.id, getTransactionType(jobid), `txEnvelope`);
if (!SubscriptionID) {
throw new Error("Subscription ID not found in transaction envelope.");
}
//Check to See if the subscription meta is in the Redis Cache.
const SubscriptionMetaFromCache = await getSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.SubscriptionMeta
);
// If it is, return it.
if (SubscriptionMetaFromCache) {
return SubscriptionMetaFromCache;
} else {
const access_token = await GetAuthToken();
const subscriptions = await axios.get(FortellisActions.GetSubscription.url, {
headers: { Authorization: `Bearer ${access_token}` },
logRequest: false
});
const SubscriptionMeta = subscriptions.data.subscriptions.find((s) => s.subscriptionId === SubscriptionID);
if (setSessionTransactionData) {
await setSessionTransactionData(
socket.id,
getTransactionType(jobid),
FortellisCacheEnums.SubscriptionMeta,
SubscriptionMeta,
defaultFortellisTTL
);
}
return SubscriptionMeta;
}
} catch (error) {
CreateFortellisLogEvent(socket, "ERROR", `Error fetching subscription metadata.`, {
error: error.message,
stack: error.stack
});
}
}
async function GetDepartmentId({ apiName, debug = false, SubscriptionMeta, overrideDepartmentId }) {
if (!apiName) throw new Error("apiName not provided. Unable to get department without apiName.");
if (debug) {
console.log("API Names & Departments ");
console.log("===========");
console.log(JSON.stringify(SubscriptionMeta.apiDmsInfo, null, 4));
console.log("===========");
}
const departmentIds = SubscriptionMeta.apiDmsInfo //Get the subscription object.
.find((info) => info.name === apiName)?.departments; //Departments are categorized by API name and have an array of departments.
if (overrideDepartmentId) {
return departmentIds && departmentIds.find(d => d.id === overrideDepartmentId)?.id
} else {
return departmentIds && departmentIds[0] && departmentIds[0].id; //TODO: This makes the assumption that there is only 1 department.
}
}
//Highest level function call to make a call to fortellis. This should be the only call required, and it will handle all the logic for making the call.
async function MakeFortellisCall({
apiName,
url,
headers = {},
body = {},
type = "post",
debug = false,
requestPathParams,
requestSearchParams = [], //Array of key/value strings like [["key", "value"]]
jobid,
redisHelpers,
socket,
SubscriptionObject, //This is used because of the get make models to bypass all of the redis calls.
overrideDepartmentId
}) {
//const { setSessionTransactionData, getSessionTransactionData } = redisHelpers;
const fullUrl = constructFullUrl({ url, pathParams: requestPathParams, requestSearchParams });
if (debug) console.log(`Executing ${type} to ${fullUrl}`);
const ReqId = uuid();
const access_token = await GetAuthToken();
const SubscriptionMeta = await FetchSubscriptions({ redisHelpers, socket, jobid, SubscriptionObject });
const DepartmentId = await GetDepartmentId({ apiName, debug, SubscriptionMeta, overrideDepartmentId });
if (debug) {
console.log(
`ReqID: ${ReqId} | SubscriptionID: ${SubscriptionMeta.subscriptionId} | DepartmentId: ${DepartmentId}`
);
console.log(`Body Contents: ${JSON.stringify(body, null, 4)}`);
}
try {
let result;
switch (type) {
case "post":
default:
result = await axios.post(fullUrl, body, {
headers: {
Authorization: `Bearer ${access_token}`,
"Subscription-Id": SubscriptionMeta.subscriptionId,
"Request-Id": ReqId,
"Content-Type": "application/json",
Accept: "application/json",
...(DepartmentId && { "Department-Id": DepartmentId }),
...headers
}
});
break;
case "get":
result = await axios.get(fullUrl, {
headers: {
Authorization: `Bearer ${access_token}`,
"Subscription-Id": SubscriptionMeta.subscriptionId,
"Request-Id": ReqId,
Accept: "application/json",
"Department-Id": DepartmentId,
...headers
}
});
break;
case "put":
result = await axios.put(fullUrl, body, {
headers: {
Authorization: `Bearer ${access_token}`,
"Subscription-Id": SubscriptionMeta.subscriptionId,
"Request-Id": ReqId,
Accept: "application/json",
"Content-Type": "application/json",
"Department-Id": DepartmentId,
...headers
}
});
break;
}
if (debug) {
console.log(`ReqID: ${ReqId} Data`);
console.log(JSON.stringify(result.data, null, 4));
}
if (result.data.checkStatusAfterSeconds) {
return DelayedCallback({
delayMeta: result.data,
access_token,
SubscriptionID: SubscriptionMeta.subscriptionId,
ReqId,
departmentIds: DepartmentId
});
}
logger.log(
"fortellis-log-event-json",
"DEBUG",
socket?.user?.email,
jobid,
{
requestcurl: result.config.curlCommand,
reqid: result.config.headers["Request-Id"] || null,
subscriptionId: result.config.headers["Subscription-Id"] || null,
resultdata: result.data,
resultStatus: result.status
},
);
return result.data;
} catch (error) {
const errorDetails = {
reqId: ReqId,
url: fullUrl,
apiName,
errorData: error.response?.data,
errorStatus: error.response?.status,
errorStatusText: error.response?.statusText,
originalError: error
};
logger.log(
"fortellis-log-event-error",
"ERROR",
socket?.user?.email,
socket?.recordid,
{
wsmessage: "",//message,
curl: error.config.curl.curlCommand,
reqid: error.request.headers["Request-Id"] || null,
subscriptionId: error.request.headers["Subscription-Id"] || null,
},
true
);
throw new FortellisApiError(`Fortellis API call failed for ${apiName}: ${error.message}`, errorDetails);
}
}
//Some Fortellis calls return a batch result that isn't ready immediately.
//This function will check the status of the call and wait until it is ready.
//It will try 5 times before giving up.
async function DelayedCallback({ delayMeta, access_token, SubscriptionID, ReqId, departmentIds }) {
for (let index = 0; index < 5; index++) {
await sleep(delayMeta.checkStatusAfterSeconds * 1000);
//Check to see if the call is ready.
const statusResult = await axios.get(delayMeta._links.status.href, {
headers: {
Authorization: `Bearer ${access_token}`,
"Subscription-Id": SubscriptionID,
"Request-Id": ReqId,
"Department-Id": departmentIds[0].id
}
});
//TODO: Add a check if the status result is not ready, to try again.
if (statusResult.data.status === "complete") {
//This may have to check again if it isn't ready.
const batchResult = await axios.get(statusResult.data._links.result.href, {
headers: {
Authorization: `Bearer ${access_token}`,
"Subscription-Id": SubscriptionID,
"Request-Id": ReqId
//"Department-Id": departmentIds[0].id
}
});
return batchResult;
} else {
return "Error!!! Still need to implement batch waiting.";
}
}
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const isProduction = process.env.NODE_ENV === "production";
//Get requests should have the trailing slash as they are used that way in the calls.
const FortellisActions = {
GetSubscription: {
url: isProduction
? "https://subscriptions.fortellis.io/v1/solution/subscriptions"
: "https://subscriptions.fortellis.io/v1/solution/subscriptions",
type: "get",
apiName: "Fortellis Get Subscriptions"
},
QueryVehicles: {
url: isProduction
? "https://api.fortellis.io/cdkdrive/service/v1/vehicles/"
: "https://api.fortellis.io/cdk-test/cdkdrive/service/v1/vehicles/",
type: "get",
apiName: "Service Vehicle - Query Vehicles"
},
GetMakeModel: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/makemodel/v2/bulk"
: "https://api.fortellis.io/cdk-test/drive/makemodel/v2",
type: "get",
apiName: "CDK Drive Get Make Model Lite"
},
GetVehicleId: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/service-vehicle-mgmt/v2/vehicle-ids/" //Request path params of vins
: "https://api.fortellis.io/cdk-test/drive/service-vehicle-mgmt/v2/vehicle-ids/",
type: "get",
apiName: "CDK Drive Post Service Vehicle"
},
GetVehicleById: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/service-vehicle-mgmt/v2/" //Request path params of vehicleId
: "https://api.fortellis.io/cdk-test/drive/service-vehicle-mgmt/v2/",
type: "get",
apiName: "CDK Drive Post Service Vehicle"
},
QueryCustomerByName: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/customerpost/v1/search"
: "https://api.fortellis.io/cdk-test/drive/customerpost/v1/search",
type: "get",
apiName: "CDK Drive Post Customer"
},
ReadCustomer: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/customerpost/v1/" //Customer ID is request param.
: "https://api.fortellis.io/cdk-test/drive/customerpost/v1/",
type: "get",
apiName: "CDK Drive Post Customer"
},
CreateCustomer: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/customerpost/v1/"
: "https://api.fortellis.io/cdk-test/drive/customerpost/v1/",
type: "post",
apiName: "CDK Drive Post Customer"
},
InsertVehicle: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/service-vehicle-mgmt/v2/"
: "https://api.fortellis.io/cdk-test/drive/service-vehicle-mgmt/v2/",
type: "post",
apiName: "CDK Drive Post Service Vehicle"
},
UpdateVehicle: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/service-vehicle-mgmt/v2/"
: "https://api.fortellis.io/cdk-test/drive/service-vehicle-mgmt/v2/",
type: "put",
apiName: "CDK Drive Post Service Vehicle"
},
GetCOA: {
type: "get",
apiName: "CDK Drive Post Accounts GL WIP",
url: `https://api.fortellis.io/cdk-test/drive/chartofaccounts/v2/bulk/`,
waitForResult: true
},
StartWip: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/glpost/startWIP"
: "https://api.fortellis.io/cdk-test/drive/glpost/startWIP",
type: "post",
apiName: "CDK Drive Post Accounts GL"
},
TranBatchWip: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/glpost/transBatchWIP"
: "https://api.fortellis.io/cdk-test/drive/glpost/transBatchWIP",
type: "post",
apiName: "CDK Drive Post Accounts GL"
},
PostBatchWip: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/glpost/postBatchWIP"
: "https://api.fortellis.io/cdk-test/drive/glpost/postBatchWIP",
type: "post",
apiName: "CDK Drive Post Accounts GL"
},
DeleteTranWip: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/glpost/postWIP"
: "https://api.fortellis.io/cdk-test/drive/glpost/postWIP",
type: "post",
apiName: "CDK Drive Post Accounts GL"
},
QueryErrorWip: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/glpost/errWIP/"
: "https://api.fortellis.io/cdk-test/drive/glpost/errWIP/",
type: "get",
apiName: "CDK Drive Post Accounts GL"
},
ServiceHistoryInsert: {
url: isProduction
? "https://api.fortellis.io/cdk/drive/post/service-vehicle-history-mgmt/v2/"
: "https://api.fortellis.io/cdk-test/drive/post/service-vehicle-history-mgmt/v2/",
type: "post",
apiName: "CDK Drive Post Service Vehicle History"
}
};
const FortellisCacheEnums = {
txEnvelope: "txEnvelope",
DMSBatchTxn: "DMSBatchTxn",
SubscriptionMeta: "SubscriptionMeta",
DepartmentId: "DepartmentId",
JobData: "JobData",
DMSVid: "DMSVid",
DMSVeh: "DMSVeh",
DMSVehCustomer: "DMSVehCustomer",
DMSCustList: "DMSCustList",
DMSCust: "DMSCust",
selectedCustomerId: "selectedCustomerId",
DMSTransHeader: "DMSTransHeader",
transWips: "transWips",
DmsBatchTxnPost: "DmsBatchTxnPost",
DMSVehHistory: "DMSVehHistory"
};
function constructFullUrl({ url, pathParams = "", requestSearchParams = [] }) {
// Ensure the base URL ends with a single "/"
url = url.replace(/\/+$/, "/");
const fullPath = pathParams ? `${url}${pathParams}` : url;
const searchParams = new URLSearchParams(requestSearchParams).toString();
const fullUrl = searchParams ? `${fullPath}?${searchParams}` : fullPath;
return fullUrl;
}
module.exports = {
GetAuthToken,
FortellisCacheEnums,
MakeFortellisCall,
FortellisActions,
getTransactionType,
defaultFortellisTTL,
FortellisApiError,
GetDepartmentId
};

View File

@@ -0,0 +1,8 @@
const logger = require("../utils/logger");
const CreateFortellisLogEvent = (socket, level, message, txnDetails) => {
logger.log("fortellis-log-event", level, socket?.user?.email, null, { wsmessage: message, txnDetails });
socket.emit("fortellis-log-event", { level, message, txnDetails });
};
module.exports = CreateFortellisLogEvent;

File diff suppressed because it is too large Load Diff

View File

@@ -2173,19 +2173,7 @@ mutation UPDATE_BILLS($billids: [uuid!]!, $bill: bills_set_input!, $logs: [expor
}
}`;
exports.INSERT_EXPORT_LOG = `
mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) {
insert_exportlog_one(object: $log) {
id
}
}`;
exports.QUERY_EXISTING_TRANSITION = `
mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) {
insert_exportlog_one(object: $log) {
id
}
}`;
exports.UPDATE_OLD_TRANSITION = `mutation UPDATE_OLD_TRANSITION($jobid: uuid!, $existingTransition: transitions_set_input!){
update_transitions(where:{jobid:{_eq:$jobid}, end:{_is_null:true

View File

@@ -8,6 +8,7 @@ const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLCl
router.use(validateFirebaseIdTokenMiddleware);
router.post("/getvehicles", withUserGraphQLClientMiddleware, cdkGetMake.default);
router.post("/fortellis/getvehicles", withUserGraphQLClientMiddleware, cdkGetMake.fortellis);
router.post("/calculate-allocations", withUserGraphQLClientMiddleware, cdkCalculateAllocations.defaultRoute);
module.exports = router;

View File

@@ -19,6 +19,7 @@ const getBodyshopCacheKey = (bodyshopId) => `bodyshop-cache:${bodyshopId}`;
const getUserSocketMappingKey = (email) =>
`user:${process.env?.NODE_ENV === "production" ? "prod" : "dev"}:${email}:socketMapping`;
const getSocketTransactionkey = ({ socketId, transactionType }) => `socket:${socketId}:${transactionType}`;
/**
* Fetch bodyshop data from the database
* @param bodyshopId
@@ -51,9 +52,12 @@ const fetchBodyshopFromDB = async (bodyshopId, logger) => {
*/
const applyRedisHelpers = ({ pubClient, app, logger }) => {
// Store session data in Redis
const setSessionData = async (socketId, key, value) => {
const setSessionData = async (socketId, key, value, ttl) => {
try {
await pubClient.hset(`socket:${socketId}`, key, JSON.stringify(value)); // Use Redis pubClient
await pubClient.hset(`socket:${socketId}`, key, JSON.stringify(value), ttl); // Use Redis pubClient
if (ttl && typeof ttl === "number") {
await pubClient.expire(`socket:${socketId}`, ttl);
}
} catch (error) {
logger.log(`Error Setting Session Data for socket ${socketId}: ${error}`, "ERROR", "redis");
}
@@ -69,6 +73,35 @@ const applyRedisHelpers = ({ pubClient, app, logger }) => {
}
};
const setSessionTransactionData = async (socketId, transactionType, key, value, ttl) => {
try {
await pubClient.hset(getSocketTransactionkey({ socketId, transactionType }), key, JSON.stringify(value)); // Use Redis pubClient
if (ttl && typeof ttl === "number") {
await pubClient.expire(getSocketTransactionkey({ socketId, transactionType }), ttl);
}
} catch (error) {
logger.log(
`Error Setting Session Data for socket transaction ${socketId}:${transactionType}: ${error}`,
"ERROR",
"redis"
);
}
};
// Retrieve session transaction data from Redis
const getSessionTransactionData = async (socketId, transactionType, key) => {
try {
const data = await pubClient.hget(getSocketTransactionkey({ socketId, transactionType }), key);
return data ? JSON.parse(data) : null;
} catch (error) {
logger.log(
`Error Getting Session Data for socket transaction ${socketId}:${transactionType}: ${error}`,
"ERROR",
"redis"
);
}
};
// Clear session data from Redis
const clearSessionData = async (socketId) => {
try {
@@ -77,6 +110,18 @@ const applyRedisHelpers = ({ pubClient, app, logger }) => {
logger.log(`Error Clearing Session Data for socket ${socketId}: ${error}`, "ERROR", "redis");
}
};
// Clear session data from Redis
const clearSessionTransactionData = async (socketId, transactionType) => {
try {
await pubClient.del(getSocketTransactionkey({ socketId, transactionType }));
} catch (error) {
logger.log(
`Error Clearing Session Transaction Data for socket ${socketId}:${transactionType}: ${error}`,
"ERROR",
"redis"
);
}
};
/**
* Add a socket mapping for a user
@@ -394,7 +439,10 @@ const applyRedisHelpers = ({ pubClient, app, logger }) => {
getUserSocketMapping,
refreshUserSocketTTL,
getBodyshopFromRedis,
updateOrInvalidateBodyshopFromRedis
updateOrInvalidateBodyshopFromRedis,
setSessionTransactionData,
getSessionTransactionData,
clearSessionTransactionData
// setMultipleSessionData,
// getMultipleSessionData,
// setMultipleFromArraySessionData,

View File

@@ -0,0 +1,116 @@
const { isArray } = require("lodash");
const logger = require("../utils/logger");
function createLogEvent(socket, level, message) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
// console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`);
socket.emit("log-event", {
timestamp: new Date(),
level,
message
});
logger.log("ws-log-event", level, socket.user.email, socket.recordid, {
wsmessage: message
});
if (socket.logEvents && isArray(socket.logEvents)) {
socket.logEvents.push({
timestamp: new Date(),
level,
message
});
}
// if (level === "ERROR") {
// throw new Error(message);
// }
}
}
function createJsonEvent(socket, level, message, json) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
//console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`);
socket.emit("log-event", {
timestamp: new Date(),
level,
message
});
}
logger.log(
"ws-log-event-json",
level,
socket.user.email,
socket.recordid,
{
wsmessage: message,
json
},
true
);
if (socket.logEvents && isArray(socket.logEvents)) {
socket.logEvents.push({
timestamp: new Date(),
level,
message
});
}
// if (level === "ERROR") {
// throw new Error(message);
// }
}
function createXmlEvent(socket, xml, message, isError = false) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("SILLY")) {
socket.emit("log-event", {
timestamp: new Date(),
level: isError ? "ERROR" : "SILLY",
message: `${message}: ${xml}`
});
}
logger.log(
isError ? "ws-log-event-xml-error" : "ws-log-event-xml",
isError ? "ERROR" : "SILLY",
socket.user.email,
socket.recordid,
{
wsmessage: message,
xml
},
true
);
if (socket.logEvents && isArray(socket.logEvents)) {
socket.logEvents.push({
timestamp: new Date(),
level: isError ? "ERROR" : "SILLY",
message,
xml
});
}
}
function LogLevelHierarchy(level) {
switch (level) {
case "XML":
return 5;
case "SILLY":
return 5;
case "DEBUG":
return 4;
case "INFO":
return 3;
case "WARN":
return 2;
case "ERROR":
return 1;
default:
return 3;
}
}
exports.createLogEvent = createLogEvent;
exports.createXmlEvent = createXmlEvent;
exports.createJsonEvent = createJsonEvent;

View File

@@ -1,8 +1,20 @@
const { admin } = require("../firebase/firebase-handler");
const { FortellisJobExport, FortellisSelectedCustomer } = require("../fortellis/fortellis");
const FortellisLogger = require("../fortellis/fortellis-logger");
const CdkCalculateAllocations = require("../cdk/cdk-calculate-allocations").default;
const redisSocketEvents = ({
io,
redisHelpers: { addUserSocketMapping, removeUserSocketMapping, refreshUserSocketTTL, getUserSocketMappingByBodyshop },
redisHelpers: {
setSessionData,
getSessionData,
addUserSocketMapping,
removeUserSocketMapping,
refreshUserSocketTTL,
getUserSocketMappingByBodyshop,
setSessionTransactionData,
getSessionTransactionData,
clearSessionTransactionData
},
ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom },
logger
}) => {
@@ -27,6 +39,8 @@ const redisSocketEvents = ({
const user = await admin.auth().verifyIdToken(token);
socket.user = user;
socket.bodyshopId = bodyshopId;
socket.data = socket.data || {};
socket.data.authToken = token;
await addUserSocketMapping(user.email, socket.id, bodyshopId);
next();
} catch (error) {
@@ -50,13 +64,21 @@ const redisSocketEvents = ({
}
try {
const user = await admin.auth().verifyIdToken(token, true);
const user = await admin.auth().verifyIdToken(token);
if (currentTimestamp < latestTokenTimestamp) {
createLogEvent(socket, "warn", "Outdated token validation skipped.");
return;
}
socket.user = user;
socket.bodyshopId = bodyshopId;
// 🔑 keep the live token in a mutable place used by downstream code
socket.data = socket.data || {};
socket.data.authToken = token;
// also keep handshake in sync for any legacy reads
if (socket.handshake?.auth) {
socket.handshake.auth.token = token;
socket.handshake.auth.bodyshopId = bodyshopId;
}
await refreshUserSocketTTL(user.email, bodyshopId);
socket.emit("token-updated", { success: true });
} catch (error) {
@@ -217,6 +239,76 @@ const redisSocketEvents = ({
});
};
//Fortellis/CDK Handlers
const registerFortellisEvents = (socket) => {
socket.on("fortellis-export-job", async ({ jobid, txEnvelope }) => {
try {
await FortellisJobExport({
socket,
redisHelpers: {
setSessionData,
getSessionData,
addUserSocketMapping,
removeUserSocketMapping,
refreshUserSocketTTL,
getUserSocketMappingByBodyshop,
setSessionTransactionData,
getSessionTransactionData,
clearSessionTransactionData
},
ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom },
jobid,
txEnvelope
});
} catch (error) {
FortellisLogger(socket, "error", `Error during Fortellis export : ${error.message}`);
logger.log("fortellis-job-export-error", "error", socket.user?.email, jobid, {
message: error.message,
stack: error.stack
});
}
});
socket.on("fortellis-selected-customer", async ({ jobid, selectedCustomerId }) => {
try {
await FortellisSelectedCustomer({
socket,
redisHelpers: {
setSessionData,
getSessionData,
addUserSocketMapping,
removeUserSocketMapping,
refreshUserSocketTTL,
getUserSocketMappingByBodyshop,
setSessionTransactionData,
getSessionTransactionData,
clearSessionTransactionData
},
ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom },
jobid,
selectedCustomerId
});
} catch (error) {
FortellisLogger(socket, "error", `Error during Fortellis export : ${error.message}`);
logger.log("fortellis-selectd-customer-error", "error", socket.user?.email, jobid, {
message: error.message,
stack: error.stack
});
}
});
socket.on("fortellis-calculate-allocations", async (jobid, callback) => {
try {
const allocations = await CdkCalculateAllocations(socket, jobid);
callback(allocations);
} catch (error) {
FortellisLogger(socket, "error", `Error during Fortellis export : ${error.message}`);
logger.log("fortellis-selectd-customer-error", "error", socket.user?.email, jobid, {
message: error.message,
stack: error.stack
});
}
});
};
// Task Events
const registerTaskEvents = (socket) => {
socket.on("task-created", (payload) => {
@@ -245,6 +337,7 @@ const redisSocketEvents = ({
registerDisconnectEvents(socket);
registerSyncEvents(socket);
registerTaskEvents(socket);
registerFortellisEvents(socket);
};
// Associate Middleware and Handlers

View File

@@ -1,231 +1,121 @@
const path = require("path");
const { io } = require("../../server");
const { admin } = require("../firebase/firebase-handler");
const { default: CdkJobExport, CdkSelectedCustomer } = require("../cdk/cdk-job-export");
const CdkGetMakes = require("../cdk/cdk-get-makes").default;
const CdkCalculateAllocations = require("../cdk/cdk-calculate-allocations").default;
const { isArray } = require("lodash");
const logger = require("../utils/logger");
const { default: PbsExportJob, PbsSelectedCustomer } = require("../accounting/pbs/pbs-job-export");
const { PbsCalculateAllocationsAp, PbsExportAp } = require("../accounting/pbs/pbs-ap-allocations");
const { createLogEvent } = require("./createLogEvent");
io.use(function (socket, next) {
try {
if (socket.handshake.auth.token) {
admin
.auth()
.verifyIdToken(socket.handshake.auth.token)
.then((user) => {
socket.user = user;
next();
})
.catch((error) => {
next(new Error("Authentication error", JSON.stringify(error)));
});
} else {
next(new Error("Authentication error - no authorization token."));
}
} catch (error) {
//console.log("Uncaught connection error:::", error);
logger.log("websocket-connection-error", "error", null, null, {
token: socket.handshake.auth.token,
...error
});
next(new Error(`Authentication error ${error}`));
}
});
io.on("connection", (socket) => {
socket.log_level = "DEBUG";
createLogEvent(socket, "DEBUG", `Connected and Authenticated.`);
socket.on("set-log-level", (level) => {
socket.log_level = level;
socket.emit("log-event", {
timestamp: new Date(),
level: "INFO",
message: `Updated log level to ${level}`
});
});
///CDK
socket.on("cdk-export-job", (jobid) => {
CdkJobExport(socket, jobid);
});
socket.on("cdk-selected-customer", (selectedCustomerId) => {
createLogEvent(socket, "DEBUG", `User selected customer ID ${selectedCustomerId}`);
socket.selectedCustomerId = selectedCustomerId;
CdkSelectedCustomer(socket, selectedCustomerId);
});
socket.on("cdk-get-makes", async (cdk_dealerid, callback) => {
function SetLegacyWebsocketHandlers(io) {
io.use(function (socket, next) {
try {
const makes = await CdkGetMakes(socket, cdk_dealerid);
callback(makes);
if (socket.handshake.auth.token) {
admin
.auth()
.verifyIdToken(socket.handshake.auth.token)
.then((user) => {
socket.user = user;
next();
})
.catch((error) => {
next(new Error("Authentication error", JSON.stringify(error)));
});
} else {
next(new Error("Authentication error - no authorization token."));
}
} catch (error) {
createLogEvent(socket, "ERROR", `Error in cdk-get-makes WS call. ${JSON.stringify(error, null, 2)}`);
}
});
socket.on("cdk-calculate-allocations", async (jobid, callback) => {
const allocations = await CdkCalculateAllocations(socket, jobid);
createLogEvent(socket, "DEBUG", `Allocations calculated.`);
createLogEvent(socket, "SILLY", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
callback(allocations);
});
//END CDK
//PBS AR
socket.on("pbs-calculate-allocations", async (jobid, callback) => {
const allocations = await CdkCalculateAllocations(socket, jobid);
createLogEvent(socket, "DEBUG", `Allocations calculated.`);
createLogEvent(socket, "SILLY", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
callback(allocations);
});
socket.on("pbs-export-job", (jobid) => {
PbsExportJob(socket, jobid);
});
socket.on("pbs-selected-customer", (selectedCustomerId) => {
createLogEvent(socket, "DEBUG", `User selected customer ID ${selectedCustomerId}`);
socket.selectedCustomerId = selectedCustomerId;
PbsSelectedCustomer(socket, selectedCustomerId);
});
//End PBS AR
//PBS AP
socket.on("pbs-calculate-allocations-ap", async (billids, callback) => {
const allocations = await PbsCalculateAllocationsAp(socket, billids);
createLogEvent(socket, "DEBUG", `AP Allocations calculated.`);
createLogEvent(socket, "DEBUG", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
socket.apAllocations = allocations;
callback(allocations);
});
socket.on("pbs-export-ap", ({ billids, txEnvelope }) => {
socket.txEnvelope = txEnvelope;
PbsExportAp(socket, { billids, txEnvelope });
});
//END PBS AP
socket.on("disconnect", () => {
createLogEvent(socket, "DEBUG", `User disconnected.`);
});
});
function createLogEvent(socket, level, message) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
// console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`);
socket.emit("log-event", {
timestamp: new Date(),
level,
message
});
logger.log("ws-log-event", level, socket.user.email, socket.recordid, {
wsmessage: message
});
if (socket.logEvents && isArray(socket.logEvents)) {
socket.logEvents.push({
timestamp: new Date(),
level,
message
//console.log("Uncaught connection error:::", error);
logger.log("websocket-connection-error", "error", null, null, {
token: socket.handshake.auth.token,
...error
});
next(new Error(`Authentication error ${error}`));
}
// if (level === "ERROR") {
// throw new Error(message);
// }
}
});
io.on("connection", (socket) => {
socket.log_level = "DEBUG";
createLogEvent(socket, "DEBUG", `Connected and Authenticated.`);
socket.on("set-log-level", (level) => {
socket.log_level = level;
socket.emit("log-event", {
timestamp: new Date(),
level: "INFO",
message: `Updated log level to ${level}`
});
});
///CDK
socket.on("cdk-export-job", (jobid) => {
CdkJobExport(socket, jobid);
});
socket.on("cdk-selected-customer", (selectedCustomerId) => {
createLogEvent(socket, "DEBUG", `User selected customer ID ${selectedCustomerId}`);
socket.selectedCustomerId = selectedCustomerId;
CdkSelectedCustomer(socket, selectedCustomerId);
});
socket.on("cdk-get-makes", async (cdk_dealerid, callback) => {
try {
const makes = await CdkGetMakes(socket, cdk_dealerid);
callback(makes);
} catch (error) {
createLogEvent(socket, "ERROR", `Error in cdk-get-makes WS call. ${JSON.stringify(error, null, 2)}`);
}
});
socket.on("cdk-calculate-allocations", async (jobid, callback) => {
console.log("cdk-calculate-allocations called", typeof CdkCalculateAllocations);
const allocations = await CdkCalculateAllocations(socket, jobid);
createLogEvent(socket, "DEBUG", `Allocations calculated.`);
createLogEvent(socket, "SILLY", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
callback(allocations);
});
//END CDK
//PBS AR
socket.on("pbs-calculate-allocations", async (jobid, callback) => {
const allocations = await CdkCalculateAllocations(socket, jobid);
createLogEvent(socket, "DEBUG", `Allocations calculated.`);
createLogEvent(socket, "SILLY", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
callback(allocations);
});
socket.on("pbs-export-job", (jobid) => {
PbsExportJob(socket, jobid);
});
socket.on("pbs-selected-customer", (selectedCustomerId) => {
createLogEvent(socket, "DEBUG", `User selected customer ID ${selectedCustomerId}`);
socket.selectedCustomerId = selectedCustomerId;
PbsSelectedCustomer(socket, selectedCustomerId);
});
//End PBS AR
//PBS AP
socket.on("pbs-calculate-allocations-ap", async (billids, callback) => {
const allocations = await PbsCalculateAllocationsAp(socket, billids);
createLogEvent(socket, "DEBUG", `AP Allocations calculated.`);
createLogEvent(socket, "DEBUG", `Allocations calculated. ${JSON.stringify(allocations, null, 2)}`);
socket.apAllocations = allocations;
callback(allocations);
});
socket.on("pbs-export-ap", ({ billids, txEnvelope }) => {
socket.txEnvelope = txEnvelope;
PbsExportAp(socket, { billids, txEnvelope });
});
//END PBS AP
socket.on("disconnect", () => {
createLogEvent(socket, "DEBUG", `User disconnected.`);
});
});
}
function createJsonEvent(socket, level, message, json) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
//console.log(`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${socket.id} - ${message}`);
socket.emit("log-event", {
timestamp: new Date(),
level,
message
});
}
logger.log(
"ws-log-event-json",
level,
socket.user.email,
socket.recordid,
{
wsmessage: message,
json
},
true
);
if (socket.logEvents && isArray(socket.logEvents)) {
socket.logEvents.push({
timestamp: new Date(),
level,
message
});
}
// if (level === "ERROR") {
// throw new Error(message);
// }
}
function createXmlEvent(socket, xml, message, isError = false) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("SILLY")) {
socket.emit("log-event", {
timestamp: new Date(),
level: isError ? "ERROR" : "SILLY",
message: `${message}: ${xml}`
});
}
logger.log(
isError ? "ws-log-event-xml-error" : "ws-log-event-xml",
isError ? "ERROR" : "SILLY",
socket.user.email,
socket.recordid,
{
wsmessage: message,
xml
},
true
);
if (socket.logEvents && isArray(socket.logEvents)) {
socket.logEvents.push({
timestamp: new Date(),
level: isError ? "ERROR" : "SILLY",
message,
xml
});
}
}
function LogLevelHierarchy(level) {
switch (level) {
case "XML":
return 5;
case "SILLY":
return 5;
case "DEBUG":
return 4;
case "INFO":
return 3;
case "WARN":
return 2;
case "ERROR":
return 1;
default:
return 3;
}
}
exports.createLogEvent = createLogEvent;
exports.createXmlEvent = createXmlEvent;
exports.createJsonEvent = createJsonEvent;
exports.SetLegacyWebsocketHandlers = SetLegacyWebsocketHandlers;