Compare commits

...

97 Commits

Author SHA1 Message Date
Allan Carr
4a16df36dd IO-2848 UPDATE_JOB query
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-22 17:35:05 -07:00
Dave Richer
7ba3ed2b89 Merged in release/2024-07-19 (pull request #1531)
Release/2024 07 19
2024-07-19 20:02:39 +00:00
Dave Richer
2cc0b7d741 Merged in bugfix/ProductFruitsWrapper (pull request #1530)
Bugfix/ProductFruitsWrapper
2024-07-19 20:02:11 +00:00
Dave Richer
97693fbcff - Add Prop Types to ProductFruitsWrapper.jsx
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-07-19 16:01:40 -04:00
Dave Richer
5e94b1a71e - Improve product fruits wrapper for extra checks
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-07-19 15:50:09 -04:00
Allan Carr
970275e62a Merged in release/2024-07-19 (pull request #1529)
Release/2024 07 19
2024-07-19 19:02:15 +00:00
Dave Richer
aa076da255 Merged in bugfix/ProductFruitsWrapper (pull request #1527)
- Improve product fruits wrapper for extra checks

Approved-by: Allan Carr
2024-07-19 17:24:22 +00:00
Dave Richer
3dd154de79 - Improve product fruits wrapper for extra checks
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-07-19 13:23:43 -04:00
Dave Richer
72a2366abe - Improve product fruits wrapper for extra checks
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-07-19 13:18:10 -04:00
Allan Carr
36f517e8e1 Merged in feature/IO-2845-Payment-by-Date-Grouped-by-Payment-Type (pull request #1525)
IO-2845 Payments Grouped by Payment Type

Approved-by: Dave Richer
2024-07-19 17:16:26 +00:00
Allan Carr
981fb57d36 Merged in feature/IO-2847-Employee-Rate-Filter (pull request #1526)
IO-2847 Employee Rate Filter

Approved-by: Dave Richer
2024-07-19 17:15:04 +00:00
Allan Carr
a059c2b5a8 IO-2847 Employee Rate Filter
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-19 08:41:12 -07:00
Allan Carr
207bb39672 IO-2845 Payments Grouped by Payment Type
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-17 14:15:38 -07:00
Dave Richer
16e34e4ed9 Merged in bugfix/productfruits (pull request #1523)
- misc updates / clear stage
2024-07-17 16:30:53 +00:00
Dave Richer
cd04f2b2b2 Merged in bugfix/productfruits (pull request #1524)
- misc updates / clear stage
2024-07-17 16:30:40 +00:00
Dave Richer
26e164b4d1 - misc updates / clear stage
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-07-17 12:22:31 -04:00
Allan Carr
ae6b2fe4f5 Merged in feature/IO-2843-QBO_USA-Switch-for-IO (pull request #1521)
IO-2843 State Tax for QBO_USA and Region CA_
2024-07-15 23:53:59 +00:00
Allan Carr
4ca686126a IO-2843 State Tax for QBO_USA and Region CA_
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-15 16:53:48 -07:00
Allan Carr
c254a8abfe Merged in release/2024-07-12 (pull request #1520)
Release/2024 07 12
2024-07-12 20:15:53 +00:00
Allan Carr
f864e40a90 Merged in feature/IO-2836-Charts-Route (pull request #1518)
IO-2836 Charts Route

Approved-by: Dave Richer
2024-07-11 22:17:01 +00:00
Allan Carr
802f70dde8 IO-2836 Charts Route
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-11 11:25:27 -07:00
Allan Carr
6cb4fd6b93 Merged in feature/IO-2839-DMS-Allocation-Labels (pull request #1514)
IO-2839 DMS Allocation Labels

Approved-by: Dave Richer
2024-07-11 17:10:49 +00:00
Allan Carr
70ca6edcb2 Merged in feature/IO-2840-Area-of-Damage-Correction (pull request #1515)
IO-2840 Correct for Area of Damage that is only 1 Charater intead of 2

Approved-by: Dave Richer
2024-07-11 17:09:41 +00:00
Allan Carr
9dcc861740 Merged in IO-2841-Non-Parts-in-jobline_status (pull request #1516)
IO-2841 Non-Parts listed in Jobline_Status

Approved-by: Dave Richer
2024-07-11 17:09:04 +00:00
Allan Carr
fecb7fb24b Merged in feature/IO-2837-Job-Card-Add-to-Scoreboard (pull request #1517)
IO-2837 Add to Scoreboard from Job Drawer

Approved-by: Dave Richer
2024-07-11 17:06:37 +00:00
Allan Carr
c5b94db8af Merged in feature/IO-2520-Kaizen-Data-Pump (pull request #1513)
IO-2520 Kaizen Data Pump

Approved-by: Dave Richer
2024-07-11 17:06:23 +00:00
Allan Carr
6690c9c692 IO-2841 Non-Parts listed in Jobline_Status
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-11 09:15:18 -07:00
Allan Carr
506edcb3c6 IO-2837 Add to Scoreboard from Job Drawer
Query restricted necessary lines for proper labour calculations

Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-10 16:45:35 -07:00
Allan Carr
c45e53e38b IO-2840 Correct for Area of Damage that is only 1 Charater intead of 2
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-10 16:00:47 -07:00
Allan Carr
d8d8a4701e IO-2839 DMS Allocation Labels
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-10 14:54:04 -07:00
Allan Carr
970c4ee9e1 IO-2520 Kaizen Data Pump
Additional fields requested

Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-10 10:33:53 -07:00
Allan Carr
c2386f43ef Merge branch 'master-AIO' into feature/IO-2520-Kaizen-Data-Pump
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-10 10:32:21 -07:00
Allan Carr
0fd3e75862 Merged in feature/IO-2835-CDK-Calculate-Allocations-Route (pull request #1511)
IO-2835 CDK Calculate Allocation Route

Approved-by: Dave Richer
2024-07-09 15:54:21 +00:00
Allan Carr
fcd3234fd9 Merged in feature/IO-2838-JobCloseRoGuardSublet (pull request #1512)
IO-2838 JobCloseRoGuardSublet

Approved-by: Dave Richer
2024-07-09 15:52:45 +00:00
Allan Carr
2afa810e6c IO-2838 JobCloseRoGuardSublet
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-08 18:15:47 -07:00
Allan Carr
ff33b924b2 IO-2835 CDK Calculate Allocation Route
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-07-08 18:07:33 -07:00
Dave Richer
84bb25985b Merged in release/2024-06-28 (pull request #1510)
Release - 2024/06/28

Approved-by: Allan Carr
2024-06-28 23:02:23 +00:00
Allan Carr
e38a58550f Merged in feature/IO-2832-Purchases-by-RO-Date (pull request #1508)
IO-2832 Purchases by RO - Invoice Date bound

Approved-by: Dave Richer
2024-06-28 15:33:02 +00:00
Allan Carr
25ef4c6228 IO-2832 Purchases by RO - Invoice Date bound
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-27 18:57:43 -07:00
Allan Carr
277fbeebc8 Merged in release/2024-06-28 (pull request #1506)
Release/2024 06 28

Approved-by: Dave Richer
2024-06-26 18:09:45 +00:00
Allan Carr
399df78957 Merged in feature/IO-2793-QBO-State-Tax-Null (pull request #1504)
IO-2793 Insure Part Tax Type Exists
2024-06-26 16:31:40 +00:00
Allan Carr
294325343b IO-2793 Insure Part Tax Type Exists
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-26 09:32:04 -07:00
Allan Carr
ed17eec948 Merged in feature/IO-2829-Multiple-Towing-Lines (pull request #1500)
IO-2829 Multiple Towing Lines

Approved-by: Dave Richer
2024-06-26 00:51:52 +00:00
Allan Carr
f87c95079c Merged in feature/IO-2830-Bill-Line-Select-Search-Component (pull request #1501)
IO-2830 Bill Line Select Search Component

Approved-by: Dave Richer
2024-06-26 00:50:58 +00:00
Allan Carr
327149ffc9 IO-2830 Bill Line Select Search Component
remove unused variable

Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-25 13:32:01 -07:00
Allan Carr
f81b21b933 IO-2829 Multiple Towing Lines
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-25 13:26:56 -07:00
Allan Carr
a559b56983 Merged in feature/IO-2520-Kaizen-Data-Pump (pull request #1499)
IO-2520 Kaizen Data Pump

Approved-by: Dave Richer
2024-06-25 19:22:55 +00:00
Allan Carr
6a9030b653 IO-2520 Kaizen Data Pump
Add in Repair Line Details, Time Ticket Details, Void Date

Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-25 11:57:38 -07:00
Dave Richer
fc7da187f4 Merged in release/2024-06-21 (pull request #1498)
Release/2024 06 21
2024-06-21 22:47:20 +00:00
Allan Carr
f593f83ec1 Merged in feature/IO-2820-Adjust-to-Bottom-Line-State-Tax (pull request #1496)
IO-2820 Adjustment to Bottom Line
2024-06-21 22:32:03 +00:00
Allan Carr
5752f123ac IO-2820 Adjustment to Bottom Line
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-21 15:32:55 -07:00
Allan Carr
6396d68584 Merged in feature/IO-2828-Deliver-Checklist-Delete (pull request #1494)
IO-2828 Add InstanceManager and correct delete button for delivery checklist
2024-06-21 18:14:38 +00:00
Allan Carr
bfd29f25dd IO-2828 Add InstanceManager and correct delete button for delivery checklist
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-21 11:12:10 -07:00
Allan Carr
e75e35e4ee Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1493)
Feature/IO-2793 State Tax Null QBO
2024-06-20 21:39:24 +00:00
Allan Carr
d7ddbf7e8d IO-2793 Change Additional Costs tax item
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-20 14:15:10 -07:00
Allan Carr
1b0198af63 Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1491)
IO-2793 Change Additional Costs tax item
2024-06-20 21:14:35 +00:00
Allan Carr
ace16ba873 IO-2793 Adjustment for parts
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-20 13:46:54 -07:00
Allan Carr
f57a4bd948 Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1489)
IO-2793 Adjustment for parts
2024-06-20 20:45:44 +00:00
Allan Carr
fef4393f9c Merged in feature/IO-2795-Delivery-Checklist-Option (pull request #1487)
IO-2795 Delivery Date required on Delivery Checklist

Approved-by: Dave Richer
2024-06-20 18:43:20 +00:00
Allan Carr
47adb6d40a IO-2795 Delivery Date required on Delivery Checklist
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-20 11:26:05 -07:00
Allan Carr
4940b10910 IO-2793 Adjustmnet for OP14
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-20 08:30:58 -07:00
Allan Carr
9c8e241ef7 Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1486)
IO-2793 Adjustmnet for OP14
2024-06-20 15:30:14 +00:00
Allan Carr
c3d6b98c89 Merged in feature/IO-2820-Adjust-to-Bottom-Line-State-Tax (pull request #1483)
IO-2820 State Tax & Adjustment to Bottom Line

Approved-by: Dave Richer
2024-06-18 20:39:40 +00:00
Allan Carr
4b49654083 Merged in feature/IO-2814-Job-Costing-Correction (pull request #1484)
IO-2814 Job Costing Correction for OP14 Duplication

Approved-by: Dave Richer
2024-06-18 20:39:16 +00:00
Allan Carr
78223078f4 Merged in feature/IO-2816-Unsaved-Changes-Manual-Creation-of-Job (pull request #1485)
IO-2816 Unsaved Changes on Manual Job Creation

Approved-by: Dave Richer
2024-06-18 20:39:03 +00:00
Allan Carr
ca4b78d44c IO-2816 Unsaved Changes on Manual Job Creation
Correct Hardcoded button labels

Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-18 13:16:58 -07:00
Allan Carr
d7bfc789e2 IO-2814 Job Costing Correction for OP14 Duplication
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-18 08:44:35 -07:00
Allan Carr
b54d0ed62a Merge branch 'master-AIO' into feature/IO-2814-Job-Costing-Correction
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-18 08:43:51 -07:00
Allan Carr
8386443cb0 IO-2820 State Tax & Adjustment to Bottom Line
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-17 10:13:36 -07:00
Dave Richer
7959dc67ce Merged in release/AIO/2024-06-07 (pull request #1482)
Release/AIO/2024 06 07
2024-06-11 00:38:31 +00:00
Allan Carr
0d2cdec75c IO-2793 Correction for Sublet Part Tax
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-10 14:42:21 -07:00
Allan Carr
a4116b6c28 Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1480)
IO-2793 Correction for Sublet Part Tax
2024-06-10 21:41:17 +00:00
Allan Carr
269ef25ece IO-2793 Better tax handling
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-10 14:14:15 -07:00
Allan Carr
575fbd5357 Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1478)
IO-2793 Better tax handling
2024-06-10 21:13:20 +00:00
Allan Carr
2ad887fb82 Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1476)
IO-2793 Correct passed Variable
2024-06-10 18:25:27 +00:00
Allan Carr
40a1a86f72 IO-2793 Correct passed Variable
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-10 11:25:06 -07:00
Allan Carr
95d43d936c Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1474)
IO-2793 Correct variables
2024-06-10 18:03:12 +00:00
Allan Carr
9f56568680 IO-2793 Correct variables
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-10 10:58:01 -07:00
Allan Carr
3428940c72 Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1472)
IO-2793 Change to function for better clarity
2024-06-10 17:49:06 +00:00
Allan Carr
ea604a5e64 IO-2793 Change to function for better clarity
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-10 09:59:45 -07:00
Allan Carr
5b76473cbc IO-2793 Correct Parts Side for Taxes
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-07 15:59:51 -07:00
Allan Carr
db5359e086 Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1470)
Feature/IO-2793 State Tax Null QBO
2024-06-07 22:58:58 +00:00
Allan Carr
35046f11c2 IO-2793 Add Comment to see output for testing
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-07 14:31:33 -07:00
Allan Carr
f4c4005a2a IO-2814 Remove Comment as api.test.romeonline.io doesn't update w/ CICD
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-07 12:16:59 -07:00
Allan Carr
69d8d27ad3 Merged in feature/IO-2814-Job-Costing-Correction (pull request #1467)
IO-2814 Remove Comment as api.test.romeonline.io doesn't update w/ CICD
2024-06-07 19:16:01 +00:00
Allan Carr
755acd24f0 IO-2814 Correct Parts Price and add Console Log
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-07 11:40:39 -07:00
Allan Carr
079d6cfee6 Merged in feature/IO-2814-Job-Costing-Correction (pull request #1465)
IO-2814 Correct Parts Price and add Console Log
2024-06-07 18:39:43 +00:00
Allan Carr
3e3b3c269a Merged in feature/IO-2814-Job-Costing-Correction (pull request #1462)
IO-2814 Job Cost Correction
2024-06-07 03:18:49 +00:00
Allan Carr
39f1af7d4b Merged in feature/IO-2793-State-Tax-Null-QBO (pull request #1463)
IO-2793 State Tax Null QBO
2024-06-07 03:18:40 +00:00
Allan Carr
1ea4d616d7 IO-2793 State Tax Null QBO
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-06 15:00:58 -07:00
Allan Carr
fdf0ecf6f6 IO-2814 Job Cost Correction
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-06 14:51:44 -07:00
Allan Carr
e5f7285253 Merge branch 'feature/IO-2793-State-Tax-Null-QBO' into release/AIO/2024-06-07
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>

# Conflicts:
#	server/accounting/qb-receivables-lines.js
2024-06-03 17:28:18 -07:00
Allan Carr
e46c304f7c IO-2793 State Tax to QBO refactor
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-03 17:23:33 -07:00
Dave Richer
1aa570db90 Merged in hotfix/revert-tax-import-code (pull request #1459)
revert previous change due to problems with tax import.
2024-06-03 22:04:31 +00:00
Dave Richer
1c2be3c890 Merged in hotfix/revert-tax-import-code (pull request #1458)
revert previous change due to problems with tax import.
2024-06-03 22:04:05 +00:00
Dave Richer
05e4eacf34 revert previous change due to problems with tax import.
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-06-03 18:03:22 -04:00
Allan Carr
e1417b03f9 Merged in feature/IO-2801-os-loader-bills-data (pull request #1456)
IO-2801 os-loader bills data

Approved-by: Dave Richer
2024-06-03 18:59:08 +00:00
38 changed files with 3038 additions and 2243 deletions

View File

@@ -1,7 +1,7 @@
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
VITE_APP_GA_CODE=231099835
VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
VITE_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
VITE_APP_CLOUDINARY_API_KEY=957865933348715

View File

@@ -7,9 +7,7 @@ import { connect } from "react-redux";
import { Route, Routes } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
//Component Imports
import ErrorBoundary from "../components/error-boundary/error-boundary.component"; // Component Imports
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
import LandingPage from "../pages/landing/landing.page";
@@ -23,20 +21,21 @@ import "./App.styles.scss";
import handleBeta from "../utils/betaHandler";
import Eula from "../components/eula/eula.component";
import InstanceRenderMgr from "../utils/instanceRenderMgr";
import { ProductFruits } from "react-product-fruits";
import ProductFruitsWrapper from "./ProductFruitsWrapper.jsx";
const ResetPassword = lazy(() => import("../pages/reset-password/reset-password.component"));
const ManagePage = lazy(() => import("../pages/manage/manage.page.container"));
const SignInPage = lazy(() => import("../pages/sign-in/sign-in.page"));
const CsiPage = lazy(() => import("../pages/csi/csi.container.page"));
const MobilePaymentContainer = lazy(() => import("../pages/mobile-payment/mobile-payment.container"));
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
online: selectOnline,
bodyshop: selectBodyshop,
currentEula: selectCurrentEula
});
const mapDispatchToProps = (dispatch) => ({
checkUserSession: () => dispatch(checkUserSession()),
setOnline: (isOnline) => dispatch(setOnline(isOnline))
@@ -60,11 +59,11 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
// Associate event listeners, memoize to prevent multiple listeners being added
useEffect(() => {
const offlineListener = (e) => {
const offlineListener = () => {
setOnline(false);
};
const onlineListener = (e) => {
const onlineListener = () => {
setOnline(true);
};
@@ -98,7 +97,7 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
InstanceRenderMgr({
imex: "gvfvfw/bodyshopapp",
rome: "rome-online/rome-online",
promanager: "" //TODO:AIO Add in log rocket for promanager instances.
promanager: "" // TODO: AIO Add in log rocket for promanager instances.
})
);
}
@@ -111,24 +110,20 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
handleBeta();
if (!online)
if (!online) {
return (
<Result
status="warning"
title={t("general.labels.nointernet")}
subTitle={t("general.labels.nointernet_sub")}
extra={
<Button
type="primary"
onClick={() => {
window.location.reload();
}}
>
<Button type="primary" onClick={() => window.location.reload()}>
{t("general.actions.refresh")}
</Button>
}
/>
);
}
if (currentEula && !currentUser.eulaIsAccepted) {
return <Eula />;
@@ -147,18 +142,13 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
/>
}
>
<ProductFruits
<ProductFruitsWrapper
currentUser={currentUser}
workspaceCode={InstanceRenderMgr({
imex: null,
rome: "9BkbEseqNqxw8jUH",
promanager: "aoJoEifvezYI0Z0P"
})}
debug
language="en"
user={{
email: currentUser.email,
username: currentUser.email
}}
/>
<Routes>

View File

@@ -0,0 +1,32 @@
import React from "react";
import { ProductFruits } from "react-product-fruits";
import PropTypes from "prop-types";
const ProductFruitsWrapper = React.memo(({ currentUser, workspaceCode }) => {
return (
workspaceCode &&
currentUser?.authorized === true &&
currentUser?.email && (
<ProductFruits
lifeCycle="unmount"
workspaceCode={workspaceCode}
debug
language="en"
user={{
email: currentUser.email,
username: currentUser.email
}}
/>
)
);
});
export default ProductFruitsWrapper;
ProductFruitsWrapper.propTypes = {
currentUser: PropTypes.shape({
authorized: PropTypes.bool,
email: PropTypes.string
}).isRequired,
workspaceCode: PropTypes.string.isRequired
};

View File

@@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next";
import InstanceRenderMgr from "../../utils/instanceRenderMgr";
//To be used as a form element only.
const { Option } = Select;
const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps }, ref) => {
const { t } = useTranslation();

View File

@@ -14,7 +14,6 @@ import {
Typography
} from "antd";
import Dinero from "dinero.js";
import dayjs from "../../utils/day";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -22,6 +21,7 @@ import { createStructuredSelector } from "reselect";
import { determineDmsType } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
import i18n from "../../translations/i18n";
import dayjs from "../../utils/day";
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
@@ -89,7 +89,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
job.area_of_damage && job.area_of_damage.impact1
? " " +
t("jobs.labels.dms.damageto", {
area_of_damage: (job.area_of_damage && job.area_of_damage.impact1) || "UNKNOWN"
area_of_damage: (job.area_of_damage && job.area_of_damage.impact1.padStart(2, "0")) || "UNKNOWN"
})
: ""
}`.slice(0, 239),

View File

@@ -1,6 +1,5 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Form, Input, notification, Switch } from "antd";
import dayjs from "../../../../utils/day";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -14,6 +13,7 @@ import { UPDATE_OWNER } from "../../../../graphql/owners.queries";
import { insertAuditTrail } from "../../../../redux/application/application.actions";
import { selectBodyshop, selectCurrentUser } from "../../../../redux/user/user.selectors";
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
import dayjs from "../../../../utils/day";
import ConfigFormComponents from "../../../config-form-components/config-form-components.component";
import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component";
@@ -275,7 +275,19 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
>
<DateTimePicker disabled={readOnly} />
</Form.Item>
<Form.Item name="actual_delivery" label={t("jobs.fields.actual_delivery")} disabled={readOnly}>
<Form.Item
name="actual_delivery"
label={t("jobs.fields.actual_delivery")}
rules={[
{
required: bodyshop.deliverchecklist.actual_delivery
? bodyshop.deliverchecklist.actual_delivery
: false
//message: t("general.validation.required"),
}
]}
disabled={readOnly}
>
<DateTimePicker disabled={readOnly} />
</Form.Item>
<Form.Item

View File

@@ -1,6 +1,6 @@
import React, { useCallback, useState } from "react";
import { LockOutlined } from "@ant-design/icons";
import { Badge, Card, Col, Collapse, Form, Input, Row, Space, Tooltip } from "antd";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -12,9 +12,9 @@ import JobCloseRoGuardBills from "./job-close-ro-guard.bills";
import JobCloseRoGuardPpd from "./job-close-ro-guard.ppd";
import JobCloseRoGuardProfit from "./job-close-ro-guard.profit";
import "./job-close-ro-guard.styles.scss";
import JobCloseRoGuardSublet from "./job-close-ro-guard.sublet";
import JobCloseRoGuardTtLifecycle from "./job-close-ro-guard.tt-lifecycle";
// import JobCloseRoGuardSublet from "./job-close-ro-guard.sublet";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import JobCloseRoGuardTtLifecycle from "./job-close-ro-guard.tt-lifecycle";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser

View File

@@ -1,14 +1,21 @@
import React from "react";
import { useTranslation } from "react-i18next";
import CardTemplate from "./job-detail-cards.template.component";
import Car from "../job-damage-visual/job-damage-visual.component";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsDamageComponent({ loading, data }) {
const { t } = useTranslation();
const { area_of_damage } = data;
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.damage")}>
{area_of_damage ? <Car dmg1={area_of_damage.impact1} dmg2={area_of_damage.impact2} /> : t("jobs.errors.nodamage")}
{area_of_damage ? (
<Car
dmg1={area_of_damage.impact1 && area_of_damage.impact1.padStart(2, "0")}
dmg2={area_of_damage.impact2 && area_of_damage.impact2.padStart(2, "0")}
/>
) : (
t("jobs.errors.nodamage")
)}
</CardTemplate>
);
}

View File

@@ -26,6 +26,16 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
const { t } = useTranslation();
const { joblines_status } = data;
const filteredJobLines = data.joblines.filter(
(j) =>
j.part_type !== null &&
j.part_type !== "PAE" &&
j.part_type !== "PAS" &&
j.part_type !== "PASL" &&
j.part_qty !== 0 &&
j.act_price !== 0
);
//TODO: Correct jobline_statuses view by including the part_qty !== 0 and act_price !== 0
const columns = [
{
title: t("joblines.fields.line_desc"),
@@ -95,7 +105,7 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
<div>
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
<PartsStatusPie joblines_status={joblines_status} />
<Table key="id" columns={columns} dataSource={data ? data.joblines : []} />
<Table key="id" columns={columns} dataSource={filteredJobLines ? filteredJobLines : []} />
</CardTemplate>
</div>
);

View File

@@ -3,6 +3,7 @@ import { Button, Space, notification } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { DELETE_DELIVERY_CHECKLIST, DELETE_INTAKE_CHECKLIST } from "../../graphql/jobs.queries";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
export default function JobAdminDeleteIntake({ job }) {
const { t } = useTranslation();
@@ -47,16 +48,22 @@ export default function JobAdminDeleteIntake({ job }) {
setLoading(false);
};
return (
const InstanceRender = InstanceRenderManager({
imex: true,
rome: "USE_IMEX",
promanager: false
});
return InstanceRender ? (
<>
<Space wrap>
<Button loading={loading} onClick={handleDelete} disabled={!job.intakechecklist}>
{t("jobs.labels.deleteintake")}
</Button>
<Button loading={loading} onClick={handleDeleteDelivery} disabled={!job.deliverychecklist}>
<Button loading={loading} onClick={handleDeleteDelivery} disabled={!job.deliverchecklist}>
{t("jobs.labels.deletedelivery")}
</Button>
</Space>
</>
);
) : null;
}

View File

@@ -189,7 +189,10 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
</Col>
<Col {...lossColDamage}>
{job.area_of_damage ? (
<Car dmg1={job.area_of_damage.impact1} dmg2={job.area_of_damage.impact2} />
<Car
dmg1={job.area_of_damage.impact1 && job.area_of_damage.impact1.padStart(2, "0")}
dmg2={job.area_of_damage.impact2 && job.area_of_damage.impact2.padStart(2, "0")}
/>
) : (
t("jobs.errors.nodamage")
)}

View File

@@ -63,7 +63,7 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
value: false
}
],
onFilter: (value, record) => value === record.flate_rate,
onFilter: (value, record) => value === record.flat_rate,
render: (text, record) =>
record.flat_rate ? t("employees.labels.flat_rate") : t("employees.labels.straight_time")
},

View File

@@ -319,6 +319,18 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
))}
</Select>
</Form.Item>
<Form.Item
name={["deliverchecklist", "actual_delivery"]}
label={t("bodyshop.fields.deliver.require_actual_delivery_date")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Switch />
</Form.Item>
</SelectorDiv>
</div>
);

View File

@@ -905,7 +905,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
}
joblines(
where: { removed: { _eq: false }, part_type: { _is_null: false, _nin: ["PAE", "PAS", "PASL"] } }
where: { removed: { _eq: false } }
order_by: { line_no: asc }
) {
id
@@ -1149,6 +1149,7 @@ export const UPDATE_JOB = gql`
suspended
queued_for_parts
scheduled_completion
scheduled_delivery
actual_in
date_repairstarted
date_void

View File

@@ -1,5 +1,5 @@
import { Button, Result, Space, Steps } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
import { Button, Result, Space, Steps } from "antd";
import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -9,7 +9,6 @@ import JobsCreateJobsInfo from "../../components/jobs-create-jobs-info/jobs-crea
import JobsCreateOwnerInfoContainer from "../../components/jobs-create-owner-info/jobs-create-owner-info.container";
import JobsCreateVehicleInfoContainer from "../../components/jobs-create-vehicle-info/jobs-create-vehicle-info.container";
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
import FormsFieldChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
export default function JobsCreateComponent({ form }) {
const [pageIndex, setPageIndex] = useState(0);
@@ -41,10 +40,11 @@ export default function JobsCreateComponent({ form }) {
const next = () => {
setPageIndex(pageIndex + 1);
console.log("NExt");
console.log("Next");
};
const prev = () => {
setPageIndex(pageIndex - 1);
console.log("Previous");
};
const { Step } = Steps;
@@ -53,26 +53,26 @@ export default function JobsCreateComponent({ form }) {
<PageHeader
extra={
<Space wrap>
{pageIndex > 0 && <Button onClick={() => prev()}>Previous</Button>}
{pageIndex > 0 && <Button onClick={() => prev()}>{t("general.actions.previous")}</Button>}
{pageIndex < steps.length - 1 && (
<Button
type="primary"
onClick={() => {
next();
// form
// .validateFields()
// .then((r) => {
// if (steps[pageIndex].validation) {
// setErrorMessage(null);
// next();
// } else {
// setErrorMessage(steps[pageIndex].error);
// }
// })
// .catch((error) => console.log("error", error));
form
.validateFields()
.then((r) => {
if (steps[pageIndex].validation) {
setErrorMessage(null);
next();
} else {
setErrorMessage(steps[pageIndex].error);
}
})
.catch((error) => console.log("error", error));
}}
>
Next
{t("general.actions.next")}
</Button>
)}
{pageIndex === steps.length - 1 && (
@@ -104,17 +104,17 @@ export default function JobsCreateComponent({ form }) {
}}
onClick={() => {
setPageIndex(idx);
// form
// .validateFields()
// .then((r) => {
// if (steps[pageIndex].validation) {
// setErrorMessage(null);
// setPageIndex(idx);
// } else {
// setErrorMessage(steps[pageIndex].error);
// }
// })
// .catch((error) => console.log("error", error));
form
.validateFields()
.then((r) => {
if (steps[pageIndex].validation) {
setErrorMessage(null);
setPageIndex(idx);
} else {
setErrorMessage(steps[pageIndex].error);
}
})
.catch((error) => console.log("error", error));
}}
/>
))}
@@ -144,7 +144,7 @@ export default function JobsCreateComponent({ form }) {
) : (
<div>
<ProgressButtons top />
<FormsFieldChanged form={form} />
{errorMessage ? (
<div>
<AlertComponent message={errorMessage} type="error" />

View File

@@ -295,7 +295,8 @@
"dailypainttarget": "Scoreboard - Daily Paint Target",
"default_adjustment_rate": "Default Labor Deduction Adjustment Rate",
"deliver": {
"templates": "Delivery Templates"
"templates": "Delivery Templates",
"require_actual_delivery_date": "Require Actual Delivery"
},
"dms": {
"apcontrol": "AP Control Number",
@@ -646,7 +647,10 @@
"payers": "Payers"
},
"cdk_dealerid": "CDK Dealer ID",
"costsmapping": "Costs Mapping",
"dms_allocations": "DMS Allocations",
"pbs_serialnumber": "PBS Serial Number",
"profitsmapping": "Profits Mapping",
"title": "DMS"
},
"emaillater": "Email Later",
@@ -1139,6 +1143,8 @@
"download": "Download",
"edit": "Edit",
"login": "Login",
"next": "Next",
"previous": "Previous",
"print": "Print",
"refresh": "Refresh",
"remove": "Remove",
@@ -2914,7 +2920,8 @@
"parts_not_recieved_vendor": "Parts Not Received by Vendor",
"parts_received_not_scheduled": "Parts Received for Jobs Not Scheduled",
"payments_by_date": "Payments by Date",
"payments_by_date_type": "Payments by Date and Type",
"payments_by_date_payment": "Payments by Date and Payment Type",
"payments_by_date_type": "Payments by Date and Customer Type",
"production_by_category": "Production by Category",
"production_by_category_one": "Production filtered by Category",
"production_by_csr": "Production by CSR",
@@ -2933,6 +2940,8 @@
"purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)",
"purchases_by_date_range_detail": "Purchases by Date - Detail",
"purchases_by_date_range_summary": "Purchases by Date - Summary",
"purchases_by_ro_detail_date": "Purchases by RO - Detail",
"purchases_by_ro_summary_date": "Purchases by RO - Summary",
"purchases_by_vendor_detailed_date_range": "Purchases By Vendor - Detailed",
"purchases_by_vendor_summary_date_range": "Purchases by Vendor - Summary",
"purchases_grouped_by_vendor_detailed": "Purchases Grouped by Vendor - Detailed",

View File

@@ -295,7 +295,8 @@
"dailypainttarget": "",
"default_adjustment_rate": "",
"deliver": {
"templates": ""
"templates": "",
"require_actual_delivery_date": ""
},
"dms": {
"apcontrol": "",
@@ -646,7 +647,10 @@
"payers": ""
},
"cdk_dealerid": "",
"costsmapping": "",
"dms_allocations": "",
"pbs_serialnumber": "",
"profitsmapping": "",
"title": ""
},
"emaillater": "",
@@ -1139,6 +1143,8 @@
"download": "",
"edit": "Editar",
"login": "",
"next": "",
"previous": "",
"print": "",
"refresh": "",
"remove": "",
@@ -2914,6 +2920,7 @@
"parts_not_recieved_vendor": "",
"parts_received_not_scheduled": "",
"payments_by_date": "",
"payments_by_date_payment": "",
"payments_by_date_type": "",
"production_by_category": "",
"production_by_category_one": "",
@@ -2933,6 +2940,8 @@
"purchases_by_cost_center_summary": "",
"purchases_by_date_range_detail": "",
"purchases_by_date_range_summary": "",
"purchases_by_ro_detail_date": "",
"purchases_by_ro_summary_date": "",
"purchases_by_vendor_detailed_date_range": "",
"purchases_by_vendor_summary_date_range": "",
"purchases_grouped_by_vendor_detailed": "",

View File

@@ -295,7 +295,8 @@
"dailypainttarget": "",
"default_adjustment_rate": "",
"deliver": {
"templates": ""
"templates": "",
"require_actual_delivery_date": ""
},
"dms": {
"apcontrol": "",
@@ -646,7 +647,10 @@
"payers": ""
},
"cdk_dealerid": "",
"costsmapping": "",
"dms_allocations": "",
"pbs_serialnumber": "",
"profitsmapping": "",
"title": ""
},
"emaillater": "",
@@ -1139,6 +1143,8 @@
"download": "",
"edit": "modifier",
"login": "",
"next": "",
"previous": "",
"print": "",
"refresh": "",
"remove": "",
@@ -2914,6 +2920,7 @@
"parts_not_recieved_vendor": "",
"parts_received_not_scheduled": "",
"payments_by_date": "",
"payments_by_date_payment": "",
"payments_by_date_type": "",
"production_by_category": "",
"production_by_category_one": "",
@@ -2933,6 +2940,8 @@
"purchases_by_cost_center_summary": "",
"purchases_by_date_range_detail": "",
"purchases_by_date_range_summary": "",
"purchases_by_ro_detail_date": "",
"purchases_by_ro_summary_date": "",
"purchases_by_vendor_detailed_date_range": "",
"purchases_by_vendor_summary_date_range": "",
"purchases_grouped_by_vendor_detailed": "",

View File

@@ -1081,6 +1081,32 @@ export const TemplateList = (type, context) => {
},
group: "purchases"
},
purchases_by_ro_detail_date: {
title: i18n.t("reportcenter.templates.purchases_by_ro_detail_date"),
description: "",
subject: i18n.t("reportcenter.templates.purchases_by_ro_detail_date"),
key: "purchases_by_ro_detail_date",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced")
},
group: "purchases"
},
purchases_by_ro_summary_date: {
title: i18n.t("reportcenter.templates.purchases_by_ro_summary_date"),
description: "",
subject: i18n.t("reportcenter.templates.purchases_by_ro_summary_date"),
key: "purchases_by_ro_summary_date",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced")
},
group: "purchases"
},
job_costing_ro_date_summary: {
title: i18n.t("reportcenter.templates.job_costing_ro_date_summary"),
description: "",
@@ -1168,6 +1194,17 @@ export const TemplateList = (type, context) => {
},
group: "customers"
},
payments_by_date_payment: {
title: i18n.t("reportcenter.templates.payments_by_date_payment"),
subject: i18n.t("reportcenter.templates.payments_by_date_payment"),
key: "payments_by_date_payment",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.payments"),
field: i18n.t("payments.fields.date")
},
group: "customers"
},
schedule: {
title: i18n.t("reportcenter.templates.schedule"),
subject: i18n.t("reportcenter.templates.schedule"),

View File

@@ -0,0 +1,10 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE VIEW "public"."joblines_status" AS
-- SELECT j.jobid,
-- j.status,
-- count(1) AS count,
-- j.part_type
-- FROM joblines j
-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> 0) AND (j.act_price <> 0) AND (j.removed IS FALSE))
-- GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE VIEW "public"."joblines_status" AS
SELECT j.jobid,
j.status,
count(1) AS count,
j.part_type
FROM joblines j
WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> 0) AND (j.act_price <> 0) AND (j.removed IS FALSE))
GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -0,0 +1,10 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE VIEW "public"."joblines_status" AS
-- SELECT j.jobid,
-- j.status,
-- count(1) AS count,
-- j.part_type
-- FROM joblines j
-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> (0)::numeric) AND (j.act_price <> (0)::numeric) AND (j.removed IS FALSE))
-- GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE VIEW "public"."joblines_status" AS
SELECT j.jobid,
j.status,
count(1) AS count,
j.part_type
FROM joblines j
WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> (0)::numeric) AND (j.act_price <> (0)::numeric) AND (j.removed IS FALSE))
GROUP BY j.jobid, j.status, j.part_type;

480
package-lock.json generated
View File

@@ -19,6 +19,8 @@
"better-queue": "^3.8.12",
"bluebird": "^3.7.2",
"body-parser": "^1.20.2",
"canvas": "^2.11.2",
"chart.js": "^4.4.3",
"cloudinary": "^2.0.2",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
@@ -1687,6 +1689,45 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@kurkle/color": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
"dependencies": {
"detect-libc": "^2.0.0",
"https-proxy-agent": "^5.0.0",
"make-dir": "^3.1.0",
"node-fetch": "^2.6.7",
"nopt": "^5.0.0",
"npmlog": "^5.0.1",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.11"
},
"bin": {
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@oozcitak/dom": {
"version": "1.15.10",
"resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz",
@@ -2609,6 +2650,11 @@
"node": ">=10.0.0"
}
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@@ -2705,6 +2751,24 @@
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
},
"node_modules/aproba": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
},
"node_modules/are-we-there-yet": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
"deprecated": "This package is no longer supported.",
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -3006,6 +3070,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/canvas": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
"integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
"hasInstallScript": true,
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.0",
"nan": "^2.17.0",
"simple-get": "^3.0.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@@ -3039,6 +3117,17 @@
"node": ">=8"
}
},
"node_modules/chart.js": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz",
"integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
},
"node_modules/cheerio": {
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
@@ -3075,6 +3164,14 @@
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"engines": {
"node": ">=10"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -3135,6 +3232,14 @@
"simple-swizzle": "^0.2.2"
}
},
"node_modules/color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"bin": {
"color-support": "bin.js"
}
},
"node_modules/color/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -3228,8 +3333,7 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/concat-stream": {
"version": "1.6.2",
@@ -3294,6 +3398,11 @@
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -3540,6 +3649,17 @@
"node": ">=0.10"
}
},
"node_modules/decompress-response": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"dependencies": {
"mimic-response": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/deeks": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz",
@@ -3588,6 +3708,11 @@
"node": ">=0.4.0"
}
},
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -3614,6 +3739,14 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-libc": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
"engines": {
"node": ">=8"
}
},
"node_modules/dev-null": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz",
@@ -4286,11 +4419,32 @@
"node": ">=6 <7 || >=8"
}
},
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/fs-minipass/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/ftp": {
"version": "0.3.10",
@@ -4339,6 +4493,31 @@
"integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
"optional": true
},
"node_modules/gauge": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
"deprecated": "This package is no longer supported.",
"dependencies": {
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.2",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.1",
"object-assign": "^4.1.1",
"signal-exit": "^3.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/gauge/node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"node_modules/gaxios": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz",
@@ -4450,6 +4629,46 @@
"assert-plus": "^1.0.0"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/glob/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -4634,6 +4853,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
},
"node_modules/hasown": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
@@ -4761,7 +4985,6 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -5329,6 +5552,28 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
},
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-dir/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@@ -5402,6 +5647,17 @@
"node": ">= 0.6"
}
},
"node_modules/mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -5435,6 +5691,29 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/minizlib/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
@@ -5490,8 +5769,7 @@
"node_modules/nan": {
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
"optional": true
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w=="
},
"node_modules/negotiator": {
"version": "0.6.3",
@@ -5631,6 +5909,32 @@
"node": ">=6.0.0"
}
},
"node_modules/nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
"dependencies": {
"abbrev": "1"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/npmlog": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
"deprecated": "This package is no longer supported.",
"dependencies": {
"are-we-there-yet": "^2.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^3.0.0",
"set-blocking": "^2.0.0"
}
},
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -5824,7 +6128,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6491,6 +6794,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
@@ -6562,6 +6870,35 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/simple-get": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"dependencies": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -6722,16 +7059,6 @@
"node": ">=12"
}
},
"node_modules/source-map-explorer/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/source-map-explorer/node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@@ -6743,38 +7070,6 @@
"wrap-ansi": "^7.0.0"
}
},
"node_modules/source-map-explorer/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/source-map-explorer/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/source-map-explorer/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@@ -7158,6 +7453,41 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/tar": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^5.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/tar/node_modules/minipass": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/tar/node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/teeny-request": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
@@ -7210,48 +7540,6 @@
"node": ">=6.0.0"
}
},
"node_modules/temp/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/temp/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/temp/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/temp/node_modules/rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
@@ -7599,6 +7887,14 @@
"node": ">= 8"
}
},
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dependencies": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"node_modules/winston": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz",

View File

@@ -29,6 +29,8 @@
"better-queue": "^3.8.12",
"bluebird": "^3.7.2",
"body-parser": "^1.20.2",
"canvas": "^2.11.2",
"chart.js": "^4.4.3",
"cloudinary": "^2.0.2",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",

View File

@@ -71,16 +71,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
{
local: false,
federal: InstanceManager({ imex: true, rome: false }),
state:
!jobs_by_pk.state_tax_rate || jobs_by_pk.state_tax_rate === 0
? false
: jobline.db_ref === "900511" ||
jobline.db_ref === "900510" ||
(jobline.mod_lb_hrs === 0 && //Extending IO-1375 as a part of IO-2023
jobline.act_price > 0 &&
jobline.lbr_op === "OP14")
? true
: jobline.tax_part
state: checkStateTax(jobline, jobs_by_pk)
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -156,7 +147,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
{
local: false,
federal: InstanceManager({ imex: true, rome: false }),
state: !jobs_by_pk.state_tax_rate || jobs_by_pk.state_tax_rate === 0 ? false : true
state: jobs_by_pk.tax_lbr_rt === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -224,7 +215,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
{
local: false,
federal: InstanceManager({ imex: true, rome: false }),
state: !jobs_by_pk.state_tax_rate || jobs_by_pk.state_tax_rate === 0 ? false : true
state: jobs_by_pk.tax_paint_mat_rt === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -293,7 +284,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
{
local: false,
federal: InstanceManager({ imex: true, rome: false }),
state: !jobs_by_pk.state_tax_rate || jobs_by_pk.state_tax_rate === 0 ? false : true
state: jobs_by_pk.tax_shop_mat_rt === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -379,7 +370,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
{
local: false,
federal: InstanceManager({ imex: true, rome: false }),
state: !jobs_by_pk.state_tax_rate || jobs_by_pk.state_tax_rate === 0 ? false : true
state: jobs_by_pk.tax_tow_rt === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -439,7 +430,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
{
local: false,
federal: InstanceManager({ imex: true, rome: false }),
state: !jobs_by_pk.state_tax_rate || jobs_by_pk.state_tax_rate === 0 ? false : true
state: jobs_by_pk.tax_str_rt === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -499,7 +490,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
{
local: false,
federal: InstanceManager({ imex: true, rome: false }),
state: !jobs_by_pk.state_tax_rate || jobs_by_pk.state_tax_rate === 0 ? false : true
state: jobs_by_pk.tax_lbr_rt === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -641,7 +632,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
}
}
//QB USA with GST
//QB USA with GST and PST
//This was required for the No. 1 Collision Group.
if (
bodyshop.accountingconfig &&
@@ -660,6 +651,17 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
Qty: 1
}
});
InvoiceLineAdd.push({
DetailType: "SalesItemLineDetail",
Amount: Dinero(jobs_by_pk.job_totals.totals.state_tax).toFormat(DineroQbFormat),
SalesItemLineDetail: {
...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}),
ItemRef: {
value: items[bodyshop.md_responsibility_centers.taxes.state.accountitem]
},
Qty: 1
}
});
}
} else {
//Handle insurance profile adjustments
@@ -670,7 +672,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
{
local: false,
federal: process.env.COUNTRY === "USA" ? false : true,
state: !jobs_by_pk.state_tax_rate || jobs_by_pk.state_tax_rate === 0 ? false : true
state: jobs_by_pk.state_tax_rate === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
@@ -879,6 +881,64 @@ exports.createMultiQbPayerLines = function ({ bodyshop, jobs_by_pk, qbo = false,
return InvoiceLineAdd;
};
function checkStateTax(jobline, jobs_by_pk) {
const isPaintOrShopMat = jobline.db_ref === "936008" || jobline.db_ref === "936007";
if (isPaintOrShopMat) {
if (jobline.db_ref === "936008") {
if (jobs_by_pk.tax_paint_mat_rt === 0) {
return false;
} else {
return true;
}
}
if (jobline.db_ref === "936007") {
if (jobs_by_pk.tax_shop_mat_rt === 0) {
return false;
} else {
return true;
}
}
}
const isAdditionalCost =
(jobline.lbr_op === "OP13" ||
(jobline.lbr_op === "OP14" && jobline.act_price > 0 && jobline.mod_lb_hrs === 0) ||
(jobline.db_ref && jobline.db_ref.startsWith("9360")) ||
(jobline.db_ref && jobline.db_ref.startsWith("90051"))) &&
!isPaintOrShopMat;
if (!jobline.part_type && isAdditionalCost) {
if (jobs_by_pk.tax_lbr_rt === 0) {
return false;
} else {
return true;
}
}
if (jobline.tax_part === false) {
return false;
} else {
if (jobline.part_type) {
if (
!jobs_by_pk.parts_tax_rates[`${jobline.part_type.toUpperCase()}`] ||
jobs_by_pk.parts_tax_rates[`${jobline.part_type.toUpperCase()}`].prt_tax_in === false ||
jobs_by_pk.parts_tax_rates[`${jobline.part_type.toUpperCase()}`].prt_tax_rt === 0
) {
return false;
} else {
return true;
}
} else {
if (jobs_by_pk.tax_lbr_rt === 0) {
return false;
} else {
return true;
}
}
}
}
function CheckQBOUSATaxID({ jobline, job, type }) {
//Replacing this to be all non-taxable items with the refactor of parts tax rates.
return "NON";

View File

@@ -16,403 +16,402 @@ const { DiscountNotAlreadyCounted } = InstanceManager({
promanager: "USE_ROME"
});
exports.defaultRoute = async function (req, res) {
try {
CdkBase.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}`);
res.status(500).json({ error: `Error encountered in CdkCalculateAllocations. ${error}` });
}
};
exports.default = async function (socket, jobid) {
try {
CdkBase.createLogEvent(socket, "DEBUG", `Received request to calculate allocations for ${jobid}`);
const job = await QueryJobData(socket, jobid);
const { bodyshop } = job;
const taxAllocations = InstanceManager({
executeFunction: true,
deubg: true,
args: [],
imex: () => ({
local: {
center: bodyshop.md_responsibility_centers.taxes.local.name,
sale: Dinero(job.job_totals.totals.local_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.local,
costCenter: bodyshop.md_responsibility_centers.taxes.local
},
state: {
center: bodyshop.md_responsibility_centers.taxes.state.name,
sale: Dinero(job.job_totals.totals.state_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.state,
costCenter: bodyshop.md_responsibility_centers.taxes.state
},
federal: {
center: bodyshop.md_responsibility_centers.taxes.federal.name,
sale: Dinero(job.job_totals.totals.federal_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.federal,
costCenter: bodyshop.md_responsibility_centers.taxes.federal
}
}),
rome: () => ({
tax_ty1: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`]
},
tax_ty2: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`]
},
tax_ty3: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`]
},
tax_ty4: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`]
},
tax_ty5: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`]
}
})
});
//Determine if there are MAPA and MASH lines already on the estimate.
//If there are, don't do anything extra (mitchell estimate)
//Otherwise, calculate them and add them to the default MAPA and MASH centers.
let hasMapaLine = false;
let hasMashLine = false;
const profitCenterHash = job.joblines.reduce((acc, val) => {
//Check the Parts Assignment
if (val.db_ref === "936008") {
//If either of these DB REFs change, they also need to change in job-totals/job-costing calculations.
hasMapaLine = true;
}
if (val.db_ref === "936007") {
hasMashLine = true;
}
if (val.profitcenter_part) {
if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero();
let DineroAmount = Dinero({
amount: Math.round(val.act_price * 100)
}).multiply(val.part_qty || 1);
DineroAmount = DineroAmount.add(
((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) &&
DiscountNotAlreadyCounted(val, job.joblines)
? 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)
: Dinero()
);
acc[val.profitcenter_part] = acc[val.profitcenter_part].add(DineroAmount);
}
if (val.profitcenter_labor && val.mod_lbr_ty) {
//Check the Labor Assignment.
if (!acc[val.profitcenter_labor]) acc[val.profitcenter_labor] = Dinero();
acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add(
Dinero({
amount: Math.round(job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100)
}).multiply(val.mod_lb_hrs)
);
}
return acc;
}, {});
const selectedDmsAllocationConfig = bodyshop.md_responsibility_centers.dms_defaults.find(
(d) => d.name === job.dms_allocation
);
CdkBase.createLogEvent(
socket,
"DEBUG",
`Using DMS Allocation ${selectedDmsAllocationConfig && selectedDmsAllocationConfig.name} for cost export.`
);
let costCenterHash = {};
//Check whether to skip this if PBS and using AP module.
const disablebillwip = !!bodyshop?.pbs_configuration?.disablebillwip;
if (!disablebillwip) {
costCenterHash = job.bills.reduce((bill_acc, bill_val) => {
bill_val.billlines.map((line_val) => {
if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]])
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = Dinero();
let lineDinero = Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100)
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1);
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(lineDinero);
return null;
});
return bill_acc;
}, {});
}
job.timetickets.forEach((ticket) => {
//Get the total amount of the ticket.
let TicketTotal = Dinero({
amount: Math.round(
ticket.rate *
(ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) *
100
)
});
//Add it to the right cost center.
if (!costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]])
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = Dinero();
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] =
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]].add(TicketTotal);
});
if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
// console.log("Adding MAPA Line Manually.");
const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!profitCenterHash[mapaAccountName]) profitCenterHash[mapaAccountName] = Dinero();
profitCenterHash[mapaAccountName] = profitCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total)
);
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
}
if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) {
// console.log("Adding MASH Line Manually.");
const mashAccountName = selectedDmsAllocationConfig.profits.MASH;
const mashAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!profitCenterHash[mashAccountName]) profitCenterHash[mashAccountName] = Dinero();
profitCenterHash[mashAccountName] = profitCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
console.log(
Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting),
typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) {
//Manually send the percentage of the costing.
//Paint Mat
const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!costCenterHash[mapaAccountName])
costCenterHash[mapaAccountName] = Dinero();
if (job.bodyshop.use_paint_scale_data === true) {
if (job.mixdata.length > 0) {
costCenterHash[mapaAccountName] = costCenterHash[
mapaAccountName
].add(
Dinero({
amount: Math.round(
((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) *
100
),
})
);
} else {
costCenterHash[mapaAccountName] = costCenterHash[
mapaAccountName
].add(
Dinero(job.job_totals.rates.mapa.total).percentage(
bodyshop?.cdk_configuration?.sendmaterialscosting
)
);
}
} else {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(
bodyshop?.cdk_configuration?.sendmaterialscosting
)
);
}
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
//Shop Mat
const mashAccountName = selectedDmsAllocationConfig.costs.MASH;
const mashAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!costCenterHash[mashAccountName]) costCenterHash[mashAccountName] = Dinero();
costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
const { ca_bc_pvrt } = job;
if (ca_bc_pvrt) {
// const pvrtAccount = bodyshop.md_responsibility_centers.profits.find(
// (c) => c.name === mashAccountName
// );
taxAllocations.state.sale = taxAllocations.state.sale.add(
Dinero({ amount: Math.round((ca_bc_pvrt || 0) * 100) })
);
}
if (job.towing_payable && job.towing_payable !== 0) {
const towAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === towAccountName);
if (towAccount) {
if (!profitCenterHash[towAccountName]) profitCenterHash[towAccountName] = Dinero();
profitCenterHash[towAccountName] = profitCenterHash[towAccountName].add(
Dinero({
amount: Math.round((job.towing_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.storage_payable && job.storage_payable !== 0) {
const storageAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === storageAccountName);
if (towAccount) {
if (!profitCenterHash[storageAccountName]) profitCenterHash[storageAccountName] = Dinero();
profitCenterHash[storageAccountName] = profitCenterHash[storageAccountName].add(
Dinero({
amount: Math.round((job.storage_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) {
const otherAccountName = selectedDmsAllocationConfig.profits.PAO;
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === otherAccountName);
if (otherAccount) {
if (!profitCenterHash[otherAccountName]) profitCenterHash[otherAccountName] = Dinero();
profitCenterHash[otherAccountName] = profitCenterHash[otherAccountName].add(
Dinero({
amount: Math.round((job.adjustment_bottom_line || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (InstanceManager({ rome: true })) {
//profile level adjustments
Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
const accountName = selectedDmsAllocationConfig.profits[key];
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName);
if (otherAccount) {
if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero();
profitCenterHash[accountName] = profitCenterHash[accountName].add(
Dinero(job.job_totals.parts.adjustments[key])
);
} else {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
);
}
});
}
const jobAllocations = _.union(Object.keys(profitCenterHash), Object.keys(costCenterHash)).map((key) => {
const profitCenter = bodyshop.md_responsibility_centers.profits.find((c) => c.name === key);
const costCenter = bodyshop.md_responsibility_centers.costs.find((c) => c.name === key);
return {
center: key,
sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(),
cost: costCenterHash[key] ? costCenterHash[key] : Dinero(),
profitCenter,
costCenter
};
});
return [
...jobAllocations,
...Object.keys(taxAllocations)
.filter((key) => taxAllocations[key].sale.getAmount() > 0 || taxAllocations[key].cost.getAmount() > 0)
.map((key) => {
if (
key === "federal" &&
selectedDmsAllocationConfig.gst_override &&
selectedDmsAllocationConfig.gst_override !== ""
) {
const ret = { ...taxAllocations[key], tax: key };
ret.costCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
ret.profitCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
return ret;
} else {
return { ...taxAllocations[key], tax: key };
}
})
];
const jobData = await QueryJobData(socket, "Bearer " + socket.handshake.auth.token, jobid);
return calculateAllocations(socket, jobData);
} catch (error) {
console.log(error);
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
}
};
async function QueryJobData(socket, jobid) {
CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
async function QueryJobData(connectionData, token, jobid) {
CdkBase.createLogEvent(connectionData, "DEBUG", `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.GET_CDK_ALLOCATIONS, { id: jobid });
CdkBase.createLogEvent(socket, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`);
const result = await client.setHeaders({ Authorization: token }).request(queries.GET_CDK_ALLOCATIONS, { id: jobid });
CdkBase.createLogEvent(connectionData, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
function calculateAllocations(connectionData, job) {
const { bodyshop } = job;
const taxAllocations = InstanceManager({
executeFunction: true,
deubg: true,
args: [],
imex: () => ({
local: {
center: bodyshop.md_responsibility_centers.taxes.local.name,
sale: Dinero(job.job_totals.totals.local_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.local,
costCenter: bodyshop.md_responsibility_centers.taxes.local
},
state: {
center: bodyshop.md_responsibility_centers.taxes.state.name,
sale: Dinero(job.job_totals.totals.state_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.state,
costCenter: bodyshop.md_responsibility_centers.taxes.state
},
federal: {
center: bodyshop.md_responsibility_centers.taxes.federal.name,
sale: Dinero(job.job_totals.totals.federal_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.federal,
costCenter: bodyshop.md_responsibility_centers.taxes.federal
}
}),
rome: () => ({
tax_ty1: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`]
},
tax_ty2: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`]
},
tax_ty3: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`]
},
tax_ty4: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`]
},
tax_ty5: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`]
}
})
});
//Determine if there are MAPA and MASH lines already on the estimate.
//If there are, don't do anything extra (mitchell estimate)
//Otherwise, calculate them and add them to the default MAPA and MASH centers.
let hasMapaLine = false;
let hasMashLine = false;
const profitCenterHash = job.joblines.reduce((acc, val) => {
//Check the Parts Assignment
if (val.db_ref === "936008") {
//If either of these DB REFs change, they also need to change in job-totals/job-costing calculations.
hasMapaLine = true;
}
if (val.db_ref === "936007") {
hasMashLine = true;
}
if (val.profitcenter_part) {
if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero();
let DineroAmount = Dinero({
amount: Math.round(val.act_price * 100)
}).multiply(val.part_qty || 1);
DineroAmount = DineroAmount.add(
((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) &&
DiscountNotAlreadyCounted(val, job.joblines)
? 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)
: Dinero()
);
acc[val.profitcenter_part] = acc[val.profitcenter_part].add(DineroAmount);
}
if (val.profitcenter_labor && val.mod_lbr_ty) {
//Check the Labor Assignment.
if (!acc[val.profitcenter_labor]) acc[val.profitcenter_labor] = Dinero();
acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add(
Dinero({
amount: Math.round(job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100)
}).multiply(val.mod_lb_hrs)
);
}
return acc;
}, {});
const selectedDmsAllocationConfig = bodyshop.md_responsibility_centers.dms_defaults.find(
(d) => d.name === job.dms_allocation
);
CdkBase.createLogEvent(
connectionData,
"DEBUG",
`Using DMS Allocation ${selectedDmsAllocationConfig && selectedDmsAllocationConfig.name} for cost export.`
);
let costCenterHash = {};
//Check whether to skip this if PBS and using AP module.
const disablebillwip = !!bodyshop?.pbs_configuration?.disablebillwip;
if (!disablebillwip) {
costCenterHash = job.bills.reduce((bill_acc, bill_val) => {
bill_val.billlines.map((line_val) => {
if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]])
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = Dinero();
let lineDinero = Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100)
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1);
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(lineDinero);
return null;
});
return bill_acc;
}, {});
}
job.timetickets.forEach((ticket) => {
//Get the total amount of the ticket.
let TicketTotal = Dinero({
amount: Math.round(
ticket.rate *
(ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) *
100
)
});
//Add it to the right cost center.
if (!costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]])
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = Dinero();
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] =
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]].add(TicketTotal);
});
if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
// console.log("Adding MAPA Line Manually.");
const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!profitCenterHash[mapaAccountName]) profitCenterHash[mapaAccountName] = Dinero();
profitCenterHash[mapaAccountName] = profitCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total)
);
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
}
if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) {
// console.log("Adding MASH Line Manually.");
const mashAccountName = selectedDmsAllocationConfig.profits.MASH;
const mashAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!profitCenterHash[mashAccountName]) profitCenterHash[mashAccountName] = Dinero();
profitCenterHash[mashAccountName] = profitCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
// console.log(
// Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting),
// typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting)
// );
if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) {
//Manually send the percentage of the costing.
//Paint Mat
const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!costCenterHash[mapaAccountName]) costCenterHash[mapaAccountName] = Dinero();
if (job.bodyshop.use_paint_scale_data === true) {
if (job.mixdata.length > 0) {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero({
amount: Math.round(((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) * 100)
})
);
} else {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
}
} else {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
}
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
//Shop Mat
const mashAccountName = selectedDmsAllocationConfig.costs.MASH;
const mashAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!costCenterHash[mashAccountName]) costCenterHash[mashAccountName] = Dinero();
costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
const { ca_bc_pvrt } = job;
if (ca_bc_pvrt) {
// const pvrtAccount = bodyshop.md_responsibility_centers.profits.find(
// (c) => c.name === mashAccountName
// );
taxAllocations.state.sale = taxAllocations.state.sale.add(Dinero({ amount: Math.round((ca_bc_pvrt || 0) * 100) }));
}
if (job.towing_payable && job.towing_payable !== 0) {
const towAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === towAccountName);
if (towAccount) {
if (!profitCenterHash[towAccountName]) profitCenterHash[towAccountName] = Dinero();
profitCenterHash[towAccountName] = profitCenterHash[towAccountName].add(
Dinero({
amount: Math.round((job.towing_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.storage_payable && job.storage_payable !== 0) {
const storageAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === storageAccountName);
if (towAccount) {
if (!profitCenterHash[storageAccountName]) profitCenterHash[storageAccountName] = Dinero();
profitCenterHash[storageAccountName] = profitCenterHash[storageAccountName].add(
Dinero({
amount: Math.round((job.storage_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) {
const otherAccountName = selectedDmsAllocationConfig.profits.PAO;
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === otherAccountName);
if (otherAccount) {
if (!profitCenterHash[otherAccountName]) profitCenterHash[otherAccountName] = Dinero();
profitCenterHash[otherAccountName] = profitCenterHash[otherAccountName].add(
Dinero({
amount: Math.round((job.adjustment_bottom_line || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (InstanceManager({ rome: true })) {
//profile level adjustments
Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
const accountName = selectedDmsAllocationConfig.profits[key];
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName);
if (otherAccount) {
if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero();
profitCenterHash[accountName] = profitCenterHash[accountName].add(
Dinero(job.job_totals.parts.adjustments[key])
);
} else {
CdkBase.createLogEvent(
connectionData,
"ERROR",
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
);
}
});
}
const jobAllocations = _.union(Object.keys(profitCenterHash), Object.keys(costCenterHash)).map((key) => {
const profitCenter = bodyshop.md_responsibility_centers.profits.find((c) => c.name === key);
const costCenter = bodyshop.md_responsibility_centers.costs.find((c) => c.name === key);
return {
center: key,
sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(),
cost: costCenterHash[key] ? costCenterHash[key] : Dinero(),
profitCenter,
costCenter
};
});
return [
...jobAllocations,
...Object.keys(taxAllocations)
.filter((key) => taxAllocations[key].sale.getAmount() > 0 || taxAllocations[key].cost.getAmount() > 0)
.map((key) => {
if (
key === "federal" &&
selectedDmsAllocationConfig.gst_override &&
selectedDmsAllocationConfig.gst_override !== ""
) {
const ret = { ...taxAllocations[key], tax: key };
ret.costCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
ret.profitCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
return ret;
} else {
return { ...taxAllocations[key], tax: key };
}
})
];
}

View File

@@ -211,6 +211,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
}
const repairCosts = CreateCosts(job);
const jobline = CreateJobLines(job.joblines);
const timeticket = CreateTimeTickets(job.timetickets);
try {
const ret = {
@@ -242,6 +244,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
},
InsuranceCompany: job.ins_co_nm || "",
Claim: job.clm_no || "",
DMSAllocation: job.dms_allocation || "",
Contacts: {
CSR: job.employee_csr_rel
? `${
@@ -276,8 +279,11 @@ const CreateRepairOrderTag = (job, errorCallback) => {
DateInvoiced:
(job.date_invoiced && moment(job.date_invoiced).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateExported:
(job.date_exported && moment(job.date_exported).tz(job.bodyshop.timezone).format(DateFormat)) || ""
(job.date_exported && moment(job.date_exported).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateVoid: (job.date_void && moment(job.date_void).tz(job.bodyshop.timezone).format(DateFormat)) || ""
},
JobLineDetails: { jobline },
TimeTicketDetails: { timeticket },
Sales: {
Labour: {
Aluminum: Dinero(job.job_totals.rates.laa.total).toFormat(DineroFormat),
@@ -625,3 +631,42 @@ const CreateCosts = (job) => {
}, 0)
};
};
const CreateJobLines = (joblines) => {
const repairLines = [];
joblines.forEach((jobline) => {
repairLines.push({
line_description: jobline.line_desc,
oem_part_no: jobline.oem_partno,
alt_part_no: jobline.alt_partno,
op_code_desc: jobline.op_code_desc,
part_type: jobline.part_type,
part_qty: jobline.part_qty,
part_price: jobline.act_price,
labor_type: jobline.mod_lbr_ty,
labor_hours: jobline.mod_lb_hrs,
labor_sale: jobline.lbr_amt
});
});
return repairLines;
};
const CreateTimeTickets = (timetickets) => {
const timeTickets = [];
timetickets.forEach((ticket) => {
timeTickets.push({
date: ticket.date,
employee: ticket.employee.employee_number
.trim()
.concat(" - ", ticket.employee.first_name.trim(), " ", ticket.employee.last_name.trim())
.trim(),
productive_hrs: ticket.productivehrs,
actual_hrs: ticket.actualhrs,
cost_center: ticket.cost_center,
flat_rate: ticket.flat_rate,
rate: ticket.rate,
ticket_cost: ticket.flat_rate ? ticket.rate * ticket.productive_hrs : ticket.rate * ticket.actual_hrs
});
});
return timeTickets;
};

View File

@@ -206,8 +206,13 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
adjustment_bottom_line
state_tax_rate
qb_multiple_payers
parts_tax_rates
tax_paint_mat_rt
tax_lbr_rt
tax_shop_mat_rt
tax_sub_rt
tax_tow_rt
tax_str_rt
owner {
accountingid
}
@@ -1108,7 +1113,7 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
use_paint_scale_data
timezone
}
jobs(where: {_and: [{updated_at: {_gt: $start}}, {updated_at: {_lte: $end}}, {shopid: {_eq: $bodyshopid}}]}) {
jobs(where: {_and: [{updated_at: {_gt: $start}}, {updated_at: {_lte: $end}}, {converted: {_eq: true}}, {shopid: {_eq: $bodyshopid}}]}) {
actual_completion
actual_delivery
actual_in
@@ -1133,6 +1138,8 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
date_invoiced
date_open
date_repairstarted
date_void
dms_allocation
employee_body_rel {
first_name
last_name
@@ -1163,6 +1170,7 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
ins_co_nm
joblines(where: {removed: {_eq: false}}) {
act_price
alt_partno
billlines(order_by: {bill: {date: desc_nulls_last}} limit: 1) {
actual_cost
actual_price
@@ -1177,12 +1185,15 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
}
db_price
id
lbr_amt
lbr_op
line_desc
line_ind
line_no
mod_lb_hrs
mod_lbr_ty
oem_partno
op_code_desc
parts_order_lines(order_by: {parts_order: {order_date: desc_nulls_last}} limit: 1){
parts_order{
id
@@ -1195,7 +1206,6 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
profitcenter_labor
prt_dsmk_m
prt_dsmk_p
oem_partno
status
}
job_totals
@@ -1246,12 +1256,18 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
scheduled_in
status
timetickets {
id
rate
cost_center
actualhrs
productivehrs
cost_center
date
employee {
employee_number
first_name
last_name
}
flat_rate
id
productivehrs
rate
}
tlos_ind
v_color
@@ -1534,6 +1550,7 @@ exports.QUERY_JOB_COSTING_DETAILS = ` query QUERY_JOB_COSTING_DETAILS($id: uuid!
op_code_desc
profitcenter_part
profitcenter_labor
act_price_before_ppc
}
bills {
id
@@ -2475,7 +2492,6 @@ query QUERY_TASK_BY_ID($id: uuid!) {
`;
exports.GET_JOBS_BY_PKS = `query GET_JOBS_BY_PKS($ids: [uuid!]!) {
jobs(where: {id: {_in: $ids}}) {
id

View File

@@ -269,7 +269,7 @@ function GenerateCostingData(job) {
job &&
job.joblines.reduce(
(acc, val) => {
//Parts Lines
//Shop or Paint Material Flags
if (val.db_ref === "936008") {
//If either of these DB REFs change, they also need to change in job-totals/job-costing calculations.
hasMapaLine = true;
@@ -277,6 +277,8 @@ function GenerateCostingData(job) {
if (val.db_ref === "936007") {
hasMashLine = true;
}
//Labor Profit Center
if (val.mod_lbr_ty) {
const laborProfitCenter = val.profitcenter_labor || defaultProfits[val.mod_lbr_ty] || "Unknown";
@@ -307,6 +309,7 @@ function GenerateCostingData(job) {
}
}
// Part Profit Center
if (val.part_type && val.part_type !== "PAE" && val.part_type !== "PAS" && val.part_type !== "PASL") {
const partsProfitCenter = val.profitcenter_part || defaultProfits[val.part_type] || "Unknown";
@@ -315,7 +318,9 @@ function GenerateCostingData(job) {
if (!partsProfitCenter)
console.log("Unknown cost/profit center mapping for parts.", val.line_desc, val.part_type);
const partsAmount = Dinero({
amount: Math.round((val.act_price || 0) * 100)
amount: val.act_price_before_ppc
? Math.round(val.act_price_before_ppc * 100)
: Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 1)
.add(
@@ -324,7 +329,9 @@ function GenerateCostingData(job) {
? val.prt_dsmk_m
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
: Dinero({
amount: Math.round(val.act_price * 100)
amount: val.act_price_before_ppc
? Math.round(val.act_price_before_ppc * 100)
: Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0))
@@ -334,6 +341,8 @@ function GenerateCostingData(job) {
if (!acc.parts[partsProfitCenter]) acc.parts[partsProfitCenter] = Dinero();
acc.parts[partsProfitCenter] = acc.parts[partsProfitCenter].add(partsAmount);
}
//Sublet Profit Center
if (val.part_type && val.part_type !== "PAE" && (val.part_type === "PAS" || val.part_type === "PASL")) {
const partsProfitCenter = val.profitcenter_part || defaultProfits[val.part_type] || "Unknown";
@@ -362,8 +371,11 @@ function GenerateCostingData(job) {
acc.sublet[partsProfitCenter] = acc.sublet[partsProfitCenter].add(partsAmount);
}
//To deal with additional costs.
if (!val.part_type && !val.mod_lbr_ty) {
//Additional Profit Center
if (
(!val.part_type && !val.mod_lbr_ty) ||
(!val.part_type && val.mod_lbr_ty && val.act_price > 0 && val.lbr_op !== "OP14")
) {
//Does it already have a defined profit center?
//If so, use it, otherwise try to use the same from the auto-allocate logic in IO app jobs-close-auto-allocate.
const partsProfitCenter = val.profitcenter_part || getAdditionalCostCenter(val, defaultProfits) || "Unknown";

View File

@@ -643,7 +643,7 @@ function CalculateAdditional(job) {
additionalCosts: null,
additionalCostItems: [],
adjustments: null,
towing: null,
towing: Dinero(),
shipping: Dinero(),
storage: null,
pvrt: null,
@@ -668,7 +668,7 @@ function CalculateAdditional(job) {
}
if (val.line_desc.toLowerCase().includes("towing")) {
ret.towing = lineValue;
ret.towing = ret.towing.add(lineValue);
return acc;
} else {
ret.additionalCostItems.push({ key: val.line_desc, total: lineValue });
@@ -909,6 +909,25 @@ function CalculateTaxesTotals(job, otherTotals) {
}
});
if (job.adjustment_bottom_line) {
const subtotal_before_adjustment = subtotal.add(Dinero({ amount: Math.round(job.adjustment_bottom_line * -100) }));
const percent_of_adjustment =
Math.round(
subtotal_before_adjustment.toUnit() /
(job.adjustment_bottom_line > 0 ? job.adjustment_bottom_line : job.adjustment_bottom_line * -1)
) / 100;
Object.keys(taxableAmountsByTier).forEach((taxTierKey) => {
taxable_adjustment = taxableAmountsByTier[taxTierKey].multiply(percent_of_adjustment);
console.log("🚀 ~ taxableAmountsByTier ~ taxable_adjustment:", taxable_adjustment);
if (job.adjustment_bottom_line > 0) {
taxableAmountsByTier[taxTierKey] = taxableAmountsByTier[taxTierKey].add(taxable_adjustment);
} else {
taxableAmountsByTier[taxTierKey] = taxableAmountsByTier[taxTierKey].subtract(taxable_adjustment);
}
});
}
const remainingTaxableAmounts = taxableAmountsByTier;
console.log("*** Taxable Amounts by Tier***");
console.table(JSON.parse(JSON.stringify(taxableAmountsByTier)));

View File

@@ -489,7 +489,7 @@ function CalculateAdditional(job) {
additionalCosts: null,
additionalCostItems: [],
adjustments: null,
towing: null,
towing: Dinero(),
shipping: Dinero(),
storage: null,
pvrt: null,
@@ -512,7 +512,7 @@ function CalculateAdditional(job) {
}
if (val.line_desc.toLowerCase().includes("towing")) {
ret.towing = lineValue;
ret.towing = ret.towing.add(lineValue);
return acc;
} else {
ret.additionalCostItems.push({ key: val.line_desc, total: lineValue });

View File

@@ -0,0 +1,76 @@
const borderColors = [
"rgba(193, 0, 50, 1)",
"rgba(0, 101, 68, 1)",
"rgba(0, 45, 98, 1)",
"rgba(253, 184, 0, 1)",
"rgba(200, 112, 126, 1)",
"rgba(228, 142, 88, 1)",
"rgba(90, 160, 141, 1)",
"rgba(103, 143, 174, 1)",
"rgba(192, 136, 99, 1)",
"rgba(234, 3, 55, 1)",
"rgba(0, 149, 67, 1)",
"rgba(0, 81, 155, 1)",
"rgba(226, 143, 173, 1)",
"rgba(237, 170, 125, 1)",
"rgba(76, 146, 177, 1)",
"rgba(172, 153, 193, 1)",
"rgba(173, 167, 89, 1)",
"rgba(254, 197, 222, 1)",
"rgba(177, 231, 223, 1)",
"rgba(120, 199, 235, 1)",
"rgba(239, 180, 193, 1)",
"rgba(240, 199, 171, 1)",
"rgba(168, 200, 121, 1)",
"rgba(150, 177, 208, 1)",
"rgba(200, 194, 189, 1)",
"rgba(244, 244, 244, 1)",
"rgba(255, 99, 132, 1)",
"rgba(54, 162, 235, 1)",
"rgba(255, 206, 86, 1)",
"rgba(75, 192, 192, 1)",
"rgba(153, 102, 255, 1)",
"rgba(255, 159, 64, 1)",
"rgba(170, 183, 184, 1)",
];
const backgroundColors = [
'rgba(193, 0, 50, 0.2)',
'rgba(0, 101, 68, 0.2)',
'rgba(0, 45, 98, 0.2)',
'rgba(253, 184, 0, 0.2)',
'rgba(200, 112, 126, 0.2)',
'rgba(228, 142, 88, 0.2)',
'rgba(90, 160, 141, 0.2)',
'rgba(103, 143, 174, 0.2)',
'rgba(192, 136, 99, 0.2)',
'rgba(234, 3, 55, 0.2)',
'rgba(0, 149, 67, 0.2)',
'rgba(0, 81, 155, 0.2)',
'rgba(226, 143, 173, 0.2)',
'rgba(237, 170, 125, 0.2)',
'rgba(76, 146, 177, 0.2)',
'rgba(172, 153, 193, 0.2)',
'rgba(173, 167, 89, 0.2)',
'rgba(254, 197, 222, 0.2)',
'rgba(177, 231, 223, 0.2)',
'rgba(120, 199, 235, 0.2)',
'rgba(239, 180, 193, 0.2)',
'rgba(240, 199, 171, 0.2)',
'rgba(168, 200, 121, 0.2)',
'rgba(150, 177, 208, 0.2)',
'rgba(200, 194, 189, 0.2)',
'rgba(244, 244, 244, 0.2)',
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)',
'rgba(170, 183, 184, 0.2)',
];
module.exports = {
borderColors,
backgroundColors,
};

View File

@@ -0,0 +1,92 @@
const { createCanvas } = require("canvas");
const Chart = require("chart.js/auto");
const { backgroundColors, borderColors } = require("./canvas-colors");
const { isObject, defaultsDeep, isNumber } = require("lodash");
exports.canvastest = function (req, res) {
console.log("Incoming test request.", req);
res.status(200).send("OK");
};
exports.canvas = function (req, res) {
const { w, h, values, keys, override } = req.body;
console.log("Incoming Canvas Request:", w, h, values, keys, override);
// Gate required values
if (!values || !keys) {
res.status(400).send("Missing required data");
return;
}
// Override must be an object if it exists
if (override && !isObject(override)) {
res.status(400).send("Override must be an object");
return;
}
// Set the default Width and Height
let [width, height] = [500, 275];
// Allow for custom width and height
if (isNumber(w)) {
width = w;
}
if (isNumber(h)) {
height = h;
}
const configuration = {
type: "doughnut",
data: {
labels: keys,
datasets: [
{
data: values,
backgroundColor: backgroundColors,
borderColor: borderColors,
borderWidth: 1
}
]
},
options: {
devicePixelRatio: 4,
responsive: false,
maintainAspectRatio: true,
circumference: 180,
rotation: -90,
plugins: {
legend: {
labels: {
boxWidth: 20,
font: {
family: "'Montserrat'",
size: 10,
style: "normal",
weight: "normal"
}
},
position: "left"
}
}
}
};
// If we have a valid override object, merge it with the default configuration object.
// This allows for you to override the default configuration with a custom one.
const defaults = () => {
if (!override || !isObject(override)) {
return configuration;
}
return defaultsDeep(override, configuration);
};
res.status(200).send(
(() => {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext("2d");
new Chart(ctx, defaults());
return canvas.toDataURL();
})()
);
};

View File

@@ -1,11 +1,13 @@
const express = require("express");
const router = express.Router();
const cdkGetMake = require("../cdk/cdk-get-makes");
const cdkCalculateAllocations = require("../cdk/cdk-calculate-allocations");
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
router.use(validateFirebaseIdTokenMiddleware);
router.post("/getvehicles", withUserGraphQLClientMiddleware, cdkGetMake.default);
router.post("/calculate-allocations", withUserGraphQLClientMiddleware, cdkCalculateAllocations.defaultRoute);
module.exports = router;

View File

@@ -11,6 +11,7 @@ const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMI
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
const { taskAssignedEmail, tasksRemindEmail } = require("../email/tasksEmails");
const { canvastest } = require("../render/canvas-handler");
//Test route to ensure Express is responding.
router.get("/test", async function (req, res) {
@@ -49,4 +50,7 @@ router.post("/tasks-remind-handler", eventAuthorizationMiddleware, tasksRemindEm
router.post("/record-handler/arms", data.arms);
router.post("/taskHandler", validateFirebaseIdTokenMiddleware, taskHandler.taskHandler);
// Canvas Test
router.post("/canvastest", validateFirebaseIdTokenMiddleware, canvastest);
module.exports = router;

View File

@@ -2,8 +2,10 @@ const express = require("express");
const router = express.Router();
const { inlinecss } = require("../render/inlinecss");
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const { canvas } = require("../render/canvas-handler");
// Define the route for inline CSS rendering
router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss);
router.post("/canvas", validateFirebaseIdTokenMiddleware, canvas);
module.exports = router;