Compare commits

...

65 Commits

Author SHA1 Message Date
Patrick Fic
09187bdb7f Remove manual scheduling for CI. 2021-10-25 14:57:27 -07:00
Patrick Fic
2aed392fbe IO-658 IO-652 IO-657 add MPI Templates. 2021-10-25 14:46:24 -07:00
Patrick Fic
1a0054a911 IO-1438 Resolve inbound conversation creation. 2021-10-25 11:00:31 -07:00
Patrick Fic
6e6f3d3d3e Resolve user email naming if null. 2021-10-25 10:46:30 -07:00
Patrick Fic
29df140680 IO-1402 Respect blocked day today for scheduling. 2021-10-25 10:41:22 -07:00
Patrick Fic
f37c67a122 Merge branch 'master' into release/2021-10-29 2021-10-25 10:31:49 -07:00
Patrick Fic
e53c9aab72 IO-1379 CSI invite Key update. 2021-10-25 10:24:18 -07:00
Patrick Fic
385ad06adc IO-1375 Special characters in CDK 2021-10-25 10:12:43 -07:00
Patrick Fic
d1e2d943a9 Package updates. 2021-10-25 09:41:54 -07:00
Patrick Fic
90aa3557b7 Merged in release/2021-10-22 (pull request #255)
Change Nod Version.

Approved-by: Patrick Fic
2021-10-22 21:38:53 +00:00
Patrick Fic
8891167183 Merged in release/2021-10-22 (pull request #254)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-22 21:38:35 +00:00
Patrick Fic
6631e645df Change Nod Version. 2021-10-22 14:38:05 -07:00
Patrick Fic
b7f202969b Merged in release/2021-10-22 (pull request #253)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-22 21:29:49 +00:00
Patrick Fic
1dc3353ecc Merge branch 'release/2021-10-22' into test 2021-10-22 13:01:41 -07:00
Patrick Fic
b1a3f1a7b8 Finish revert stripe removal. 2021-10-22 13:01:26 -07:00
Patrick Fic
89f3a26635 Revert "Disable stripe promise."
This reverts commit 3ece5e0ba2.
2021-10-22 12:59:45 -07:00
Patrick Fic
506fe9b1af Merge branch 'release/2021-10-22' into test 2021-10-22 10:47:12 -07:00
Patrick Fic
37c898d3ce IO-256 Resolve QBO issues. 2021-10-22 10:46:03 -07:00
Patrick Fic
e4eac5714a Merge branch 'release/2021-10-22' into test 2021-10-22 10:00:42 -07:00
Patrick Fic
9d4a59ca16 IO-256 Remove credentials with Axios calls for QBO. 2021-10-22 10:00:23 -07:00
Patrick Fic
8ec524061a Merge branch 'release/2021-10-22' into test 2021-10-21 14:32:06 -07:00
Patrick Fic
434ed46b5a IO-1469 Add keep schedule details to job detail reschedule. 2021-10-21 14:30:50 -07:00
Patrick Fic
3ece5e0ba2 Disable stripe promise. 2021-10-21 14:16:18 -07:00
Patrick Fic
52a383ffb7 IO-1454 Disable production colors & add feature flags. 2021-10-21 14:03:48 -07:00
Patrick Fic
8ad1d5929a Reduce Sentry Logging. 2021-10-21 12:02:02 -07:00
Patrick Fic
445c01499b Merge branch 'release/2021-10-22' into test 2021-10-21 11:42:33 -07:00
Patrick Fic
0b05be841d Merge branch 'release/2021-10-22' of bitbucket.org:snaptsoft/bodyshop into release/2021-10-22 2021-10-21 11:42:10 -07:00
Patrick Fic
6bcb5f2af5 IO-1446 Smart Schedule Blocked Day Fixes 2021-10-21 11:42:05 -07:00
Patrick Fic
7482751c5b IO-1478 Make CC Contract RO lines editable. 2021-10-21 09:43:14 -07:00
Patrick Fic
7a025fff42 Merge branch 'release/2021-10-22' into test 2021-10-21 08:59:49 -07:00
Patrick Fic
602fe36638 QBO Cleanup. 2021-10-21 08:58:24 -07:00
Patrick Fic
da08fc74f1 IO-1475 Totals update for Mitchell Cloud discounts. 2021-10-21 08:53:15 -07:00
Patrick Fic
14af45baf0 IO-256 QBO SS Realm ID 2021-10-20 14:33:31 -07:00
Patrick Fic
420a88c505 Merged in release/2021-10-22 (pull request #252)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-19 05:12:44 +00:00
Patrick Fic
f3c44f8dd1 Merge branch 'feature/pbs' into release/2021-10-22 2021-10-18 22:12:16 -07:00
Patrick Fic
c72111e18b Merged in release/2021-10-22 (pull request #251)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-19 05:04:35 +00:00
Patrick Fic
1ca2870912 IO-1437 Add deductible Note 2021-10-18 22:02:45 -07:00
Patrick Fic
db9744e1e5 IO-1454 Production List Visual Indicators. 2021-10-18 21:54:24 -07:00
Patrick Fic
e700095551 IO-1492 Add estimator Presets 2021-10-18 20:26:57 -07:00
Patrick Fic
99196a77ed IO-1433 Add Loss of Use Field 2021-10-18 19:56:12 -07:00
Patrick Fic
289a8222a0 IO-223 ARM development 2021-10-18 19:37:27 -07:00
Patrick Fic
dc10f8d35b IO-1469 Retain event detals when rescheduling. 2021-10-18 13:56:48 -07:00
Patrick Fic
f448232fe7 IO-1435 Missed additional query. 2021-10-18 13:52:30 -07:00
Patrick Fic
4baf4b4afa IO-1435 Add scheduled time to job header if scheduled. 2021-10-18 13:48:23 -07:00
Patrick Fic
1785093023 IO-1449 Adjust display of time in schedulign setup. 2021-10-18 13:43:15 -07:00
Patrick Fic
0d65f8d894 IO-1407 Resolve bill not clearing on enter again 2021-10-18 13:40:31 -07:00
Patrick Fic
3d8c390291 IO-1408 Resolve bill form index issue. 2021-10-18 13:29:35 -07:00
Patrick Fic
f0a13856bc IO-1439 Trim QB name fields on export. 2021-10-18 12:16:12 -07:00
Patrick Fic
ad6394783d IO-1443 Expand Priority selection to 15. 2021-10-18 11:22:01 -07:00
Patrick Fic
16e9843298 Package Updates. 2021-10-18 11:21:51 -07:00
Patrick Fic
a4c949c376 Merged in release/2021-10-22 (pull request #250)
Release/2021 10 22
2021-10-18 17:01:18 +00:00
Patrick Fic
0e0d5316b7 Undo Quickbooks CORS to resolve prod error. 2021-10-18 07:55:36 -07:00
Patrick Fic
60cb6ee8fb IO-223 Begin ARMS Integration 2021-10-15 17:55:55 -07:00
Patrick Fic
7d3279d21a Merged in release/2021-10-15 (pull request #249)
Release/2021 10 15
2021-10-15 21:33:24 +00:00
Patrick Fic
3989d0f1e2 Merge branch 'test' into feature/pbs 2021-10-14 17:20:15 -07:00
Patrick Fic
c4e59c1a5e IO-117 Begin PBS Structure. 2021-10-13 11:45:24 -07:00
Patrick Fic
ff049ad3e8 Merged in release/2021-10-08 (pull request #246)
release/2021-10-08
2021-10-09 15:29:49 +00:00
Patrick Fic
fd43d7d56d Merged in release/2021-10-08 (pull request #237)
JSR CHange

Approved-by: Patrick Fic
2021-10-04 14:57:42 +00:00
Patrick Fic
e899e4545f Merged in release/2021-10-08 (pull request #236)
Release/2021 10 08
2021-10-02 06:19:45 +00:00
Patrick Fic
2e28a4a790 Merged in release/2021-10-01 (pull request #231)
Release/2021 10 01
2021-09-30 01:50:34 +00:00
Patrick Fic
891aa649e8 Merged in release/2021-09-24 (pull request #226)
release/2021-09-24
2021-09-24 17:44:53 +00:00
Patrick Fic
a8a0167123 Merged in release/2021-09-24 (pull request #224)
Release/2021 09 24
2021-09-24 17:20:52 +00:00
Patrick Fic
4b55719f86 Merged in hotfix-2021-09-14 (pull request #214)
hotfix-2021-09-14

Approved-by: Patrick Fic
2021-09-14 16:52:50 +00:00
Patrick Fic
5387ff207c Merged in release/2021-09-10 (pull request #212)
Release/2021 09 10
2021-09-13 19:02:26 +00:00
Patrick Fic
e3804b103b Merged in release/2021-09-10 (pull request #208)
Release/2021 09 10
2021-09-10 22:08:15 +00:00
90 changed files with 29540 additions and 1317 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project be_version="2.7.1" version="1.2">
<babeledit_project version="1.2" be_version="2.7.1">
<!--
BabelEdit project file
@@ -4283,6 +4283,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>md_ded_notes</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<folder_node>
<name>md_hour_split</name>
<children>
@@ -7120,6 +7141,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>color</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>default_arrived</name>
<definition_loaded>false</definition_loaded>
@@ -7456,6 +7498,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>production_colors</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>production_statuses</name>
<definition_loaded>false</definition_loaded>
@@ -8019,6 +8082,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>estimators</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>insurancecos</name>
<definition_loaded>false</definition_loaded>
@@ -17449,6 +17533,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>changestimator</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>convert</name>
<definition_loaded>false</definition_loaded>
@@ -19695,6 +19800,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ded_note</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>ded_status</name>
<definition_loaded>false</definition_loaded>
@@ -21159,6 +21285,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>loss_of_use</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>ma2s</name>
<definition_loaded>false</definition_loaded>
@@ -33210,6 +33357,69 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>mpi_animal_checklist</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>mpi_eglass_auth</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>mpi_final_acct_sheet</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>paint_grid</name>
<definition_loaded>false</definition_loaded>
@@ -36868,6 +37078,37 @@
</folder_node>
</children>
</folder_node>
<folder_node>
<name>schedule</name>
<children>
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>manualevent</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
</children>
</folder_node>
<folder_node>
<name>scoreboard</name>
<children>

View File

@@ -5,18 +5,19 @@
"proxy": "http://localhost:5000",
"dependencies": {
"@apollo/client": "^3.4.16",
"@craco/craco": "^6.3.0",
"@craco/craco": "^6.4.0",
"@fingerprintjs/fingerprintjs": "^3.3.0",
"@lourenci/react-kanban": "^2.1.0",
"@openreplay/tracker": "^3.4.4",
"@openreplay/tracker-assist": "^3.4.3",
"@openreplay/tracker-assist": "^3.4.4",
"@openreplay/tracker-graphql": "^3.0.0",
"@openreplay/tracker-redux": "^3.0.0",
"@sentry/react": "^6.13.3",
"@sentry/tracing": "^6.13.3",
"@splitsoftware/splitio-react": "^1.3.0",
"@stripe/react-stripe-js": "^1.6.0",
"@stripe/stripe-js": "^1.19.1",
"@tanem/react-nprogress": "^3.0.80",
"@stripe/stripe-js": "^1.20.2",
"@tanem/react-nprogress": "^3.0.81",
"antd": "^4.16.13",
"apollo-link-logger": "^2.0.0",
"axios": "^0.23.0",
@@ -26,15 +27,15 @@
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.1.2",
"firebase": "^9.1.3",
"graphql": "^15.6.1",
"i18next": "^21.3.0",
"i18next": "^21.3.3",
"i18next-browser-languagedetector": "^6.1.2",
"jsoneditor": "^9.5.6",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.37",
"logrocket": "^2.1.0",
"markerjs2": "^2.14.0",
"libphonenumber-js": "^1.9.38",
"logrocket": "^2.1.1",
"markerjs2": "^2.15.0",
"moment-business-days": "^1.2.0",
"phone": "^3.1.8",
"preval.macro": "^5.0.0",
@@ -43,7 +44,7 @@
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"react": "^17.0.1",
"react-big-calendar": "^0.36.1",
"react-big-calendar": "^0.38.0",
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.1",
@@ -59,15 +60,15 @@
"react-scripts": "^4.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.1.4",
"recharts": "^2.1.5",
"redux": "^4.1.1",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.0.0",
"sass": "^1.42.1",
"socket.io-client": "^4.2.0",
"styled-components": "^5.3.1",
"sass": "^1.43.3",
"socket.io-client": "^4.3.2",
"styled-components": "^5.3.3",
"subscriptions-transport-ws": "^0.9.18",
"web-vitals": "^2.1.2",
"workbox-background-sync": "^6.3.0",
@@ -114,7 +115,7 @@
]
},
"devDependencies": {
"@sentry/webpack-plugin": "^1.18.0",
"@sentry/webpack-plugin": "^1.18.3",
"patch-package": "^6.4.7",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.2"

View File

@@ -1,4 +1,8 @@
import { ApolloProvider } from "@apollo/client";
//import trackerRedux from "@openreplay/tracker-redux";
import Tracker from "@openreplay/tracker";
import trackerGraphQL from "@openreplay/tracker-graphql";
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
import { ConfigProvider } from "antd";
import enLocale from "antd/es/locale/en_US";
import LogRocket from "logrocket";
@@ -6,13 +10,11 @@ import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
import client from "../utils/GraphQLClient";
import App from "./App";
import trackerGraphQL from "@openreplay/tracker-graphql";
//import trackerRedux from "@openreplay/tracker-redux";
import Tracker from "@openreplay/tracker";
//import trackerAssist from "@openreplay/tracker-assist";
import { getCurrentUser } from "../firebase/firebase.utils";
import client from "../utils/GraphQLClient";
import App from "./App";
moment.locale("en-US");
export const tracker = new Tracker({
@@ -36,6 +38,13 @@ export const recordGraphQL = tracker.use(trackerGraphQL());
tracker.start();
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
export const factory = SplitSdk({
core: {
authorizationKey: process.env.REACT_APP_SPLIT_API,
key: "anon",
},
});
export default function AppContainer() {
const { t } = useTranslation();
@@ -53,7 +62,9 @@ export default function AppContainer() {
}}
>
<GlobalLoadingBar />
<App />
<SplitFactory factory={factory}>
<App />
</SplitFactory>
</ConfigProvider>
</ApolloProvider>
);

View File

@@ -128,3 +128,9 @@
.react-kanban-column {
background-color: #ddd !important;
}
.production-list-table {
td.ant-table-column-sort {
background: unset;
}
}

View File

@@ -199,7 +199,7 @@ export function AccountingReceivablesTableComponent({
<Card
extra={
<Space wrap>
{!bodyshop.cdk_dealerid && (
{!bodyshop.cdk_dealerid && !bodyshop.pbs_serialnumber && (
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}

View File

@@ -216,7 +216,7 @@ function BillEnterModalContainer({
if (enterAgain) {
form.resetFields();
form.setFieldsValue(formValues);
form.setFieldsValue({ ...form.getFieldsValue(), billlines: [] });
} else {
toggleModalVisible();
}

View File

@@ -156,7 +156,6 @@ export function BillEnterModalLinesComponent({
setFieldsValue({
billlines: getFieldsValue("billlines").billlines.map(
(item, idx) => {
console.log("Checking", index, idx);
if (idx === index) {
console.log(
"Found and setting.",
@@ -502,9 +501,9 @@ const EditableCell = ({
labelCol={{ span: 0 }}
{...(formItemProps && formItemProps(record))}
>
{(formInput && formInput(record, record.key)) || children}
{(formInput && formInput(record, record.name)) || children}
</Form.Item>
{additional && additional(record, record.key)}
{additional && additional(record, record.name)}
</Space>
</td>
);
@@ -516,7 +515,7 @@ const EditableCell = ({
name={dataIndex}
{...(formItemProps && formItemProps(record))}
>
{(formInput && formInput(record, record.key)) || children}
{(formInput && formInput(record, record.name)) || children}
</Form.Item>
</td>
);

View File

@@ -53,6 +53,7 @@ export function ContractConvertToRo({
const billingLines = [];
if (contractLength > 0)
billingLines.push({
manual_line:true,
unq_seq: 1,
line_no: 1,
line_ref: 1,
@@ -70,6 +71,7 @@ export function ContractConvertToRo({
contract.kmend - contract.kmstart - contract.dailyfreekm * contractLength;
if (mileageDiff > 0) {
billingLines.push({
manual_line:true,
unq_seq: 2,
line_no: 2,
line_ref: 2,
@@ -86,6 +88,7 @@ export function ContractConvertToRo({
if (values.refuelqty > 0) {
billingLines.push({
manual_line:true,
unq_seq: 3,
line_no: 3,
line_ref: 3,
@@ -101,6 +104,7 @@ export function ContractConvertToRo({
}
if (values.applyCleanupCharge) {
billingLines.push({
manual_line:true,
unq_seq: 4,
line_no: 4,
line_ref: 4,
@@ -117,6 +121,7 @@ export function ContractConvertToRo({
if (contract.damagewaiver) {
//Add for cleanup fee.
billingLines.push({
manual_line:true,
unq_seq: 5,
line_no: 5,
line_ref: 5,

View File

@@ -51,7 +51,9 @@ export function EmailOverlayContainer({
const defaultEmailFrom = {
from: {
name: `${currentUser.displayName} @ ${bodyshop.shopname}`,
name: currentUser.displayName
? `${currentUser.displayName} @ ${bodyshop.shopname}`
: bodyshop.shopname,
address: EmailSettings.fromAddress,
},
ReplyTo: {

View File

@@ -21,7 +21,7 @@ export const PhoneItemFormatterValidation = (getFieldValue, name) => ({
return Promise.resolve();
} else {
const p = parsePhoneNumber(value, "CA");
if (p.isValid()) {
if (p && p.isValid()) {
return Promise.resolve();
} else {
return Promise.reject(i18n.t("general.validation.invalidphone"));

View File

@@ -209,6 +209,9 @@ export function ScheduleEventComponent({
jobId: event.job.id,
job: event.job,
previousEvent: event.id,
color: event.color,
alt_transport: event.job && event.job.alt_transport,
note: event.note,
},
});
}}

View File

@@ -14,7 +14,7 @@ import {
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { useHistory } from "react-router-dom";
import { useCookies } from "react-cookie";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -33,11 +33,10 @@ export function JobsCloseExportButton({
const [updateJob] = useMutation(UPDATE_JOB);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [loading, setLoading] = useState(false);
const [cookies] = useCookies();
const handleQbxml = async () => {
//Check if it's a CDK setup.
if (bodyshop.cdk_dealerid) {
if (bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) {
history.push(`/manage/dms?jobId=${jobId}`);
return;
}
@@ -52,7 +51,7 @@ export function JobsCloseExportButton({
{
jobIds: [jobId],
},
{ withCredentials: true }
);
} else {
//Default is QBD
@@ -182,16 +181,7 @@ export function JobsCloseExportButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={
disabled ||
(bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo &&
!cookies.qbo_realmId)
}
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}
</Button>
);

View File

@@ -150,6 +150,9 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<Form.Item label={t("jobs.fields.loss_desc")} name="loss_desc">
<Input />
</Form.Item>
<Form.Item label={t("jobs.fields.loss_of_use")} name="loss_of_use">
<Input />
</Form.Item>
<Form.Item label={t("jobs.fields.ponumber")} name="po_number">
<Input />
</Form.Item>

View File

@@ -0,0 +1,46 @@
import { DownOutlined } from "@ant-design/icons";
import { Dropdown, Menu } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
export function JobsDetailChangeEstimator({ disabled, form, bodyshop }) {
const { t } = useTranslation();
const handleClick = ({ item, key, keyPath }) => {
const est = item.props.value;
form.setFieldsValue(est);
};
const menu = (
<div>
<Menu onClick={handleClick}>
{bodyshop.md_estimators.map((est, idx) => (
<Menu.Item value={est} key={idx}>
{`${est.est_ct_fn} ${est.est_ct_ln}`}
</Menu.Item>
))}
</Menu>
</div>
);
return (
<Dropdown overlay={menu} disabled={disabled}>
<a
className="ant-dropdown-link"
href=" #"
onClick={(e) => e.preventDefault()}
>
{t("jobs.actions.changestimator")} <DownOutlined />
</a>
</Dropdown>
);
}
export default connect(mapStateToProps, null)(JobsDetailChangeEstimator);

View File

@@ -1,4 +1,13 @@
import { Col, Form, Input, InputNumber, Row, Select, Switch } from "antd";
import {
Col,
Divider,
Form,
Input,
InputNumber,
Row,
Select,
Switch,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -12,6 +21,7 @@ import FormItemPhone, {
PhoneItemFormatterValidation,
} from "../form-items-formatted/phone-form-item.component";
import Car from "../job-damage-visual/job-damage-visual.component";
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
import FormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
@@ -46,6 +56,13 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<Form.Item label={t("jobs.fields.ded_note")} name="ded_note">
<Select disabled={jobRO}>
{bodyshop.md_ded_notes.map((n, index) => (
<Select.Option key={index}>{n}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
<Input disabled={jobRO} />
</Form.Item>
@@ -139,6 +156,9 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
<FormDatePicker disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.loss_of_use")} name="loss_of_use">
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
<InputNumber precision={0} min={0} disabled={jobRO} />
</Form.Item>
@@ -191,8 +211,16 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
)}
</Col>
</Row>
<Divider
orientation="left"
type="horizontal"
style={{ marginTop: ".8rem", float: "right" }}
>
{t("jobs.forms.appraiserinfo")}
</Divider>
<FormRow header={t("jobs.forms.appraiserinfo")}>
<JobsDetailChangeEstimator form={form} disabled={jobRO} />
<FormRow noDivider>
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
<Input disabled={jobRO} />
</Form.Item>

View File

@@ -101,6 +101,7 @@ export function JobsDetailHeaderActions({
context: {
jobId: job.id,
job: job,
alt_transport: job.alt_transport,
},
});
}}

View File

@@ -147,7 +147,7 @@ export function JobsDetailHeaderCsi({
replyTo: bodyshop.email,
},
template: {
name: TemplateList("job").csi_invitation.key,
name: TemplateList("job_special").csi_invitation_action.key,
variables: {
id: job.csiinvites[0].id,
},

View File

@@ -23,14 +23,10 @@ export function JobsDetailHeaderActionexportCustomerData({
logImEXEvent("job_export_cust_data");
let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(
`/qbo/receivables`,
{
jobIds: [job.id],
custDataOnly: true,
},
{ withCredentials: true }
);
PartnerResponse = await axios.post(`/qbo/receivables`, {
jobIds: [job.id],
custDataOnly: true,
});
} else {
//Default is QBD

View File

@@ -16,6 +16,7 @@ import JobEmployeeAssignments from "../job-employee-assignments/job-employee-ass
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
import "./jobs-detail-header.styles.scss";
import JobsRelatedRos from "../jobs-related-ros/jobs-related-ros.component";
import { DateTimeFormatter } from "../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -70,6 +71,12 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
{job.production_vars && job.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" />
) : null}
{job.status === bodyshop.md_ro_statuses.default_scheduled &&
job.scheduled_in ? (
<Tag>
<DateTimeFormatter>{job.scheduled_in}</DateTimeFormatter>
</Tag>
) : null}
</Space>
</DataLabel>
<DataLabel label={t("jobs.fields.ins_co_nm_short")}>

View File

@@ -124,7 +124,6 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
{t("jobs.forms.laborrates")}
</Divider>
<Space>
<div></div>
<JobsDetailRatesChangeButton form={form} disabled={jobRO} />
<JobsMarkPstExempt form={form} />
</Space>

View File

@@ -13,7 +13,6 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { useCookies } from "react-cookie";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -31,7 +30,6 @@ export function JobsExportAllButton({
const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOBS);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [cookies] = useCookies();
const [loading, setLoading] = useState(false);
const handleQbxml = async () => {
@@ -39,13 +37,9 @@ export function JobsExportAllButton({
let PartnerResponse;
setLoading(true);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(
`/qbo/receivables`,
{
jobIds: jobIds,
},
{ withCredentials: true }
);
PartnerResponse = await axios.post(`/qbo/receivables`, {
jobIds: jobIds,
});
} else {
let QbXmlResponse;
try {
@@ -175,16 +169,7 @@ export function JobsExportAllButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={
disabled ||
(bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo &&
!cookies.qbo_realmId)
}
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}
</Button>
);

View File

@@ -14,7 +14,6 @@ import {
import { logImEXEvent } from "../../firebase/firebase.utils";
import _ from "lodash";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { useCookies } from "react-cookie";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -33,7 +32,6 @@ export function PayableExportAll({
const [updateBill] = useMutation(UPDATE_BILLS);
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [cookies] = useCookies();
const handleQbxml = async () => {
logImEXEvent("accounting_payables_export_all");
@@ -42,13 +40,9 @@ export function PayableExportAll({
setLoading(true);
if (!!loadingCallback) loadingCallback(true);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(
`/qbo/receivables`,
{
bills: billids,
},
{ withCredentials: true }
);
PartnerResponse = await axios.post(`/qbo/payables`, {
bills: billids,
});
} else {
let QbXmlResponse;
try {
@@ -174,16 +168,7 @@ export function PayableExportAll({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={
disabled ||
(bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo &&
!cookies.qbo_realmId)
}
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.exportselected")}
</Button>
);

View File

@@ -13,7 +13,6 @@ import {
} from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { useCookies } from "react-cookie";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -32,7 +31,6 @@ export function PayableExportButton({
const [updateBill] = useMutation(UPDATE_BILLS);
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [cookies] = useCookies();
const handleQbxml = async () => {
logImEXEvent("accounting_export_payable");
@@ -43,13 +41,9 @@ export function PayableExportButton({
//Check if it's a QBO Setup.
let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(
`/qbo/payables`,
{
bills: [billId],
},
{ withCredentials: true }
);
PartnerResponse = await axios.post(`/qbo/payables`, {
bills: [billId],
});
} else {
//Default is QBD
@@ -176,16 +170,7 @@ export function PayableExportButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={
disabled ||
(bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo &&
!cookies.qbo_realmId)
}
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}
</Button>
);

View File

@@ -8,7 +8,6 @@ import { createStructuredSelector } from "reselect";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
import { useCookies } from "react-cookie";
import {
selectBodyshop,
@@ -32,7 +31,6 @@ export function PaymentExportButton({
const [updatePayment] = useMutation(UPDATE_PAYMENTS);
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [cookies] = useCookies();
const handleQbxml = async () => {
logImEXEvent("accounting_payment_export");
@@ -40,13 +38,9 @@ export function PaymentExportButton({
//Check if it's a QBO Setup.
let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(
`/qbo/payments`,
{
payments: [paymentId],
},
{ withCredentials: true }
);
PartnerResponse = await axios.post(`/qbo/payments`, {
payments: [paymentId],
});
} else {
//Default is QBD
@@ -177,16 +171,7 @@ export function PaymentExportButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={
disabled ||
(bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo &&
!cookies.qbo_realmId)
}
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}
</Button>
);

View File

@@ -8,7 +8,6 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
import { useCookies } from "react-cookie";
import {
selectBodyshop,
@@ -31,20 +30,15 @@ export function PaymentsExportAllButton({
const [updatePayments] = useMutation(UPDATE_PAYMENTS);
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [cookies] = useCookies();
const handleQbxml = async () => {
setLoading(true);
if (!!loadingCallback) loadingCallback(true);
let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(
`/qbo/payments`,
{
payments: paymentIds,
},
{ withCredentials: true }
);
PartnerResponse = await axios.post(`/qbo/payments`, {
payments: paymentIds,
});
} else {
let QbXmlResponse;
try {
@@ -158,16 +152,7 @@ export function PaymentsExportAllButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={
disabled ||
(bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo &&
!cookies.qbo_realmId)
}
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.exportselected")}
</Button>
);

View File

@@ -40,7 +40,7 @@ export default function ProductionListColumnBodyPriority({ record }) {
key="set"
title={t("production.actions.bodypriority-set")}
>
{new Array(9).fill().map((value, index) => (
{new Array(15).fill().map((value, index) => (
<Menu.Item key={index + 1}>{index + 1}</Menu.Item>
))}
</Menu.SubMenu>

View File

@@ -40,7 +40,7 @@ export default function ProductionListColumnDetailPriority({ record }) {
key="set"
title={t("production.actions.detailpriority-set")}
>
{new Array(9).fill().map((value, index) => (
{new Array(15).fill().map((value, index) => (
<Menu.Item key={index + 1}>{index + 1}</Menu.Item>
))}
</Menu.SubMenu>

View File

@@ -40,7 +40,7 @@ export default function ProductionListColumnPaintPriority({ record }) {
key="set"
title={t("production.actions.paintpriority-set")}
>
{new Array(9).fill().map((value, index) => (
{new Array(15).fill().map((value, index) => (
<Menu.Item key={index + 1}>{index + 1}</Menu.Item>
))}
</Menu.SubMenu>

View File

@@ -24,6 +24,7 @@ import ProductionListSaveConfigButton from "../production-list-save-config-butto
import ProductionListPrint from "./production-list-print.component";
import ProductionListTableViewSelect from "./production-list-table-view-select.component";
import ResizeableTitle from "./production-list-table.resizeable.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -39,7 +40,9 @@ export function ProductionListTable({
currentUser,
}) {
const [searchText, setSearchText] = useState("");
const { Production_List_Status_Colors } = useTreatments([
"Production_List_Status_Colors",
]);
const assoc = bodyshop.associations.find(
(a) => a.useremail === currentUser.email
);
@@ -165,7 +168,6 @@ export function ProductionListTable({
// };
if (!!!columns) return <div>No columns found.</div>;
console.log(data);
const totalHrs = data
.reduce(
@@ -218,8 +220,28 @@ export function ProductionListTable({
handleSelector=".prod-header-dropdown"
>
<Table
sticky
pagination={false}
size="small"
className="production-list-table"
onRow={
Production_List_Status_Colors.treatment === "on" &&
((record, index) => {
if (!bodyshop.md_ro_statuses.production_colors) return null;
const color = bodyshop.md_ro_statuses.production_colors.find(
(x) => x.status === record.status
);
if (!color) return null;
return {
style: {
backgroundColor: `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`,
},
};
})
}
components={{
header: {
cell: ResizeableTitle,

View File

@@ -1,4 +1,4 @@
import { Space, Tag } from "antd";
import { Space } from "antd";
import Axios from "axios";
import queryString from "query-string";
import React, { useEffect } from "react";
@@ -9,7 +9,7 @@ import QboSignIn from "../../assets/qbo/C2QB_green_btn_med_default.svg";
export default function QboAuthorizeComponent() {
const location = useLocation();
const history = useHistory();
const [cookies, setCookie] = useCookies(["access_token", "refresh_token"]);
const [, setCookie] = useCookies(["access_token", "refresh_token"]);
const handleQbSignIn = async () => {
const result = await Axios.post("/qbo/authorize");
@@ -24,20 +24,20 @@ export default function QboAuthorizeComponent() {
const hasBeenCalledBack = code && realmId && state;
if (hasBeenCalledBack) {
setCookie("qbo_code", code, { path: "/" });
setCookie("qbo_state", state, { path: "/" });
// setCookie("qbo_code", code, { path: "/" });
// setCookie("qbo_state", state, { path: "/" });
let expires = new Date();
expires.setTime(expires.getTime() + 8726400 * 1000);
// let expires = new Date();
// expires.setTime(expires.getTime() + 8726400 * 1000);
setCookie("qbo_realmId", realmId, {
path: "/",
expires,
// setCookie("qbo_realmId", realmId, {
// path: "/",
// expires,
...(process.env.NODE_ENV !== "development"
? { domain: `.${window.location.host}` }
: {}),
});
// ...(process.env.NODE_ENV !== "development"
// ? { domain: `.${window.location.host}` }
// : {}),
// });
history.push({ pathname: `/manage/accounting/receivables` });
}
@@ -52,9 +52,7 @@ export default function QboAuthorizeComponent() {
src={QboSignIn}
style={{ cursor: "pointer" }}
/>
{!cookies.qbo_realmId && (
<Tag color="red">No QuickBooks company has been connected.</Tag>
)}
{error && JSON.parse(decodeURIComponent(error)).error_description}
</Space>
);

View File

@@ -3,6 +3,7 @@ import { Button, Card, Col, PageHeader, Row, Space } from "antd";
import React from "react";
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
import ScheduleModal from "../schedule-job-modal/schedule-job-modal.container";
//import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
import ScheduleProductionList from "../schedule-production-list/schedule-production-list.component";
export default function ScheduleCalendarComponent({ data, refetch }) {
@@ -21,8 +22,10 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
>
<SyncOutlined />
</Button>
<ScheduleProductionList />
{
// <ScheduleManualEvent />
}
</Space>
}
/>

View File

@@ -63,6 +63,20 @@ export function ScheduleJobModalContainer({
skip: !visible || !!!jobId,
});
useEffect(() => {
if (
existingAppointments.data &&
existingAppointments.data.appointments.length > 0 &&
!existingAppointments.data.appointments[0].canceled
) {
form.setFieldsValue({
color: existingAppointments.data.appointments[0].color,
note: existingAppointments.data.appointments[0].note,
});
}
}, [existingAppointments.data, form]);
const handleFinish = async (values) => {
logImEXEvent("schedule_new_appointment");
@@ -105,7 +119,7 @@ export function ScheduleJobModalContainer({
start: moment(values.start),
end: moment(values.start).add(bodyshop.appt_length || 60, "minutes"),
color: values.color,
note:values.note
note: values.note,
},
jobId: jobId,
altTransport: values.alt_transport,
@@ -188,6 +202,9 @@ export function ScheduleJobModalContainer({
start: null,
// smartDates: [],
scheduled_completion: null,
color: context.color,
alt_transport: context.alt_transport,
note: context.note,
}}
>
<ScheduleJobModalComponent

View File

@@ -0,0 +1,119 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Form, Input, Popover, Space } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import {
INSERT_APPOINTMENT,
UPDATE_APPOINTMENT,
} from "../../graphql/appointments.queries";
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
export default function ScheduleManualEvent({ event }) {
const { t } = useTranslation();
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
const [updateAppointment] = useMutation(UPDATE_APPOINTMENT);
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const [visibility, setVisibility] = useState(false);
// const [callQuery, { loading: entryLoading, data: entryData }] = useLazyQuery(
// QUERY_SCOREBOARD_ENTRY
// );
useEffect(() => {
if (visibility && event) {
form.setFieldsValue({ event });
}
}, [visibility, form, event]);
useEffect(() => {
// if (entryData && entryData.scoreboard && entryData.scoreboard[0]) {
// console.log("Setting FOrm");
// // form.setFieldsValue(entryData.scoreboard[0]);
// }
}, [form]);
const handleFinish = async (values) => {
logImEXEvent("job_close_add_to_scoreboard");
setLoading(true);
try {
if (event && event.id) {
updateAppointment();
} else {
insertAppointment();
}
} catch (error) {
console.log(error);
} finally {
setLoading(false);
setVisibility(false);
}
};
const overlay = (
<Card>
<div>
<Form form={form} layout="vertical" onFinish={handleFinish}>
<Form.Item
label={t("schedule.fields.note")}
name="note"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("schedule.fields.start")}
name="start"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<FormDateTimePickerComponent />
</Form.Item>
<Form.Item
label={t("schedule.fields.end")}
name="end"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<FormDateTimePickerComponent />
</Form.Item>
<Space wrap>
<Button type="primary" htmlType="submit">
{t("general.actions.save")}
</Button>
<Button onClick={() => setVisibility(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</Form>
</div>
</Card>
);
const handleClick = (e) => {
setVisibility(true);
};
return (
<Popover content={overlay} visible={visibility}>
<Button loading={loading} onClick={handleClick}>
{t("schedule.labels.manualevent")}
</Button>
</Popover>
);
}

View File

@@ -17,6 +17,8 @@ import PhoneFormItem, {
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
export default function ShopInfoGeneral({ form }) {
const { t } = useTranslation();
return (
@@ -472,6 +474,19 @@ export default function ShopInfoGeneral({ form }) {
>
<Switch />
</Form.Item>
<Form.Item
name={["md_ded_notes"]}
label={t("bodyshop.fields.md_ded_notes")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array",
},
]}
>
<Select mode="tags" />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
<Form.List name={["md_messaging_presets"]}>
@@ -706,7 +721,7 @@ export default function ShopInfoGeneral({ form }) {
>
<Input />
</Form.Item>
<Space wrap>
<Space>
<Form.Item
label={t("bodyshop.fields.md_ins_co.zip")}
key={`${index}zip`}
@@ -744,6 +759,95 @@ export default function ShopInfoGeneral({ form }) {
}}
</Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.estimators")}>
<Form.List name={["md_estimators"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item
label={t("jobs.fields.est_co_nm")}
key={`${index}est_co_nm`}
name={[field.name, "est_co_nm"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.est_ct_fn")}
key={`${index}est_ct_fn`}
name={[field.name, "est_ct_fn"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.est_ct_ln")}
key={`${index}est_ct_ln`}
name={[field.name, "est_ct_ln"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.est_ph1")}
key={`${index}est_ph1`}
name={[field.name, "est_ph1"]}
rules={[
({ getFieldValue }) =>
PhoneItemFormatterValidation(getFieldValue, [
field.name,
"est_ph",
]),
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.est_ea")}
key={`${index}est_ea`}
name={[field.name, "est_ea"]}
rules={[
{
type: "email",
message: "This is not a valid email address.",
},
]}
>
<FormItemEmail
email={form.getFieldValue([field.name, "est_ea"])}
/>
</Form.Item>
<Space>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
/>
</Space>
</LayoutFormRow>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")}>
<Form.List name={["md_ccc_rates"]}>
{(fields, { add, remove, move }) => {

View File

@@ -203,6 +203,14 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow>
</>
)}
{bodyshop.pbs_serialnumber && (
<>
<DataLabel label={t("bodyshop.labels.dms.pbs_serialnumber")}>
{form.getFieldValue("pbs_serialnumber")}
</DataLabel>
</>
)}
<LayoutFormRow header={t("bodyshop.labels.responsibilitycenters.costs")}>
<Form.List name={["md_responsibility_centers", "costs"]}>
{(fields, { add, remove }) => {

View File

@@ -1,8 +1,12 @@
import { Form, Select } from "antd";
import { DeleteFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Button, Form, Select, Space } from "antd";
import React, { useState } from "react";
import { ChromePicker } from "react-color";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const SelectorDiv = styled.div`
.ant-form-item .ant-select {
width: 200px;
@@ -11,6 +15,9 @@ const SelectorDiv = styled.div`
export default function ShopInfoROStatusComponent({ form }) {
const { t } = useTranslation();
const { Production_List_Status_Colors } = useTreatments([
"Production_List_Status_Colors",
]);
const [options, setOptions] = useState(
form.getFieldValue(["md_ro_statuses", "statuses"]) || []
@@ -257,6 +264,97 @@ export default function ShopInfoROStatusComponent({ form }) {
</Select>
</Form.Item>
</LayoutFormRow>
{Production_List_Status_Colors.treatment === "on" && (
<LayoutFormRow
grow
header={t("bodyshop.fields.statuses.production_colors")}
>
<Form.List name={["md_ro_statuses", "production_colors"]}>
{(fields, { add, remove, move }) => {
return (
<div>
<LayoutFormRow>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Space direction="vertical">
<div style={{ display: "flex" }}>
<Form.Item
style={{ flex: 1 }}
label={t("jobs.fields.status")}
key={`${index}status`}
name={[field.name, "status"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
</div>
<Form.Item
label={t("bodyshop.fields.statuses.color")}
key={`${index}color`}
name={[field.name, "color"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<ColorPicker />
</Form.Item>
</Space>
</Form.Item>
))}
</LayoutFormRow>
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
)}
</SelectorDiv>
);
}
const ColorPicker = ({ value, onChange, style, ...restProps }) => {
const handleChange = (color) => {
console.log(
"🚀 ~ file: shop-info.rostatus.component.jsx ~ line 345 ~ color",
color
);
if (onChange) onChange(color.rgb);
};
return (
<ChromePicker
{...restProps}
color={value}
onChangeComplete={handleChange}
/>
);
};

View File

@@ -44,7 +44,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
},
]}
>
<TimePicker showSecond={false} format="hh:mm" />
<TimePicker showSecond={false} format="HH:mm" />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.schedule_end_time")}
@@ -56,7 +56,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
},
]}
>
<TimePicker showSecond={false} format="hh:mm" />
<TimePicker showSecond={false} format="HH:mm" />
</Form.Item>
<Form.Item
name={["appt_alt_transport"]}

View File

@@ -196,10 +196,11 @@ export const CANCEL_APPOINTMENT_BY_ID = gql`
export const QUERY_APPOINTMENTS_BY_JOBID = gql`
query QUERY_APPOINTMENTS_BY_JOBID($jobid: uuid!) {
appointments(where: { jobid: { _eq: $jobid } }) {
appointments(where: { jobid: { _eq: $jobid } }, order_by: { start: desc }) {
start
id
end
color
isintake
arrived
canceled

View File

@@ -94,6 +94,10 @@ export const QUERY_BODYSHOP = gql`
attach_pdf_to_email
tt_allow_post_to_invoiced
cdk_configuration
md_estimators
md_ded_notes
pbs_configuration
pbs_serialnumber
employees {
id
active
@@ -184,6 +188,10 @@ export const UPDATE_SHOP = gql`
attach_pdf_to_email
tt_allow_post_to_invoiced
cdk_configuration
md_estimators
md_ded_notes
pbs_configuration
pbs_serialnumber
employees {
id
first_name

View File

@@ -385,6 +385,7 @@ export const GET_JOB_BY_PK = gql`
vehicleid
driveable
towin
loss_of_use
vehicle {
id
plate_no
@@ -463,6 +464,7 @@ export const GET_JOB_BY_PK = gql`
production_vars
ca_gst_registrant
ownerid
ded_note
owner {
id
ownr_fn
@@ -735,6 +737,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
actual_completion
actual_delivery
actual_in
scheduled_in
po_number
id
ins_co_nm

View File

@@ -23,6 +23,11 @@ Dinero.globalRoundingMode = "HALF_EVEN";
if (process.env.NODE_ENV !== "development") {
Sentry.init({
dsn: "https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
ignoreErrors: [
"ResizeObserver loop",
"Module specifier, 'fs' does not start",
"Module specifier, 'zlib' does not start with",
],
integrations: [
// new Integrations.BrowserTracing(),
// new Sentry.Integrations.Breadcrumbs({ console: true }),

View File

@@ -5,12 +5,12 @@ import preval from "preval.macro";
import React, { lazy, Suspense, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
import { Link, Route, Switch } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
import ConflictComponent from "../../components/conflict/conflict.component";
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
import FcmNotification from "../../components/fcm-notification/fcm-notification.component";
//import FooterComponent from "../../components/footer/footer.component";
//Component Imports

View File

@@ -6,8 +6,8 @@ import AlertComponent from "../../components/alert/alert.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
import { setBodyshop } from "../../redux/user/user.actions";
import ManagePage from "./manage.page.component";
import "../../utils/RegisterSw";
import ManagePage from "./manage.page.component";
const mapDispatchToProps = (dispatch) => ({
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
@@ -21,7 +21,9 @@ function ManagePageContainer({ match, setBodyshop }) {
const { t } = useTranslation();
useEffect(() => {
if (data) setBodyshop(data.bodyshops[0] || { notfound: true });
if (data) {
setBodyshop(data.bodyshops[0] || { notfound: true });
}
}, [data, setBodyshop]);
if (loading)

View File

@@ -13,7 +13,7 @@ import { doc } from "firebase/firestore";
import i18next from "i18next";
import LogRocket from "logrocket";
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
import { tracker } from "../../App/App.container";
import { factory, tracker } from "../../App/App.container";
import {
getCurrentUser,
logImEXEvent,
@@ -250,6 +250,8 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
try {
const userEmail = yield select((state) => state.user.currentUser.email);
factory.client(payload.imexshopid);
const authRecord = payload.associations.filter(
(a) => a.useremail === userEmail
);

View File

@@ -270,6 +270,7 @@
"md_categories": "Categories",
"md_ccc_rates": "Courtesy Car Contract Rate Presets",
"md_classes": "Classes",
"md_ded_notes": "Deductible Notes",
"md_hour_split": {
"paint": "Paint Hour Split",
"prep": "Prep Hour Split"
@@ -450,6 +451,7 @@
"status": "Status Label",
"statuses": {
"active_statuses": "Active Statuses (Filtering for Active Jobs throughout system)",
"color": "Color",
"default_arrived": "Default Arrived Status (Transition to Production)",
"default_bo": "Default Backordered Status",
"default_canceled": "Default Canceled Status",
@@ -466,6 +468,7 @@
"open_statuses": "Open Statuses",
"post_production_statuses": "Post-Production Statuses",
"pre_production_statuses": "Pre-Production Statuses",
"production_colors": "Production Status Colors",
"production_statuses": "Production Statuses"
},
"target_touchtime": "Target Touch Time",
@@ -499,6 +502,7 @@
},
"emaillater": "Email Later",
"employees": "Employees",
"estimators": "Estimators",
"insurancecos": "Insurance Companies",
"intakechecklist": "Intake Checklist",
"jobstatuses": "Job Statuses",
@@ -1089,6 +1093,7 @@
"autoallocate": "Auto Allocate",
"changelaborrate": "Change Labor Rate",
"changestatus": "Change Status",
"changestimator": "Change Estimator",
"convert": "Convert",
"deliver": "Deliver",
"dms": {
@@ -1203,6 +1208,7 @@
"date_open": "Open",
"date_scheduled": "Scheduled",
"ded_amt": "Deductible",
"ded_note": "Deductible Note",
"ded_status": "Deductible Status",
"depreciation_taxes": "Depreciation/Taxes",
"dms": {
@@ -1278,6 +1284,7 @@
"local_tax_rate": "Local Tax Rate",
"loss_date": "Loss Date",
"loss_desc": "Loss Description",
"loss_of_use": "Loss of Use",
"ma2s": "2 Stage Paint",
"ma3s": "3 Stage Pain",
"mabl": "MABL?",
@@ -1984,6 +1991,9 @@
"job_costing_ro": "Job Costing",
"job_notes": "Job Notes",
"key_tag": "Key Tag",
"mpi_animal_checklist": "MPI - Animal Checklist",
"mpi_eglass_auth": "MPI - eGlass Auth",
"mpi_final_acct_sheet": "MPI - Final Accounting Sheet",
"paint_grid": "Paint Grid",
"parts_label_single": "Parts Label - Single",
"parts_list": "Parts List",
@@ -2197,6 +2207,11 @@
"work_in_progress_payables": "Work in Progress - Payables"
}
},
"schedule": {
"labels": {
"manualevent": "Add Manual Event"
}
},
"scoreboard": {
"actions": {
"edit": "Edit"

View File

@@ -270,6 +270,7 @@
"md_categories": "",
"md_ccc_rates": "",
"md_classes": "",
"md_ded_notes": "",
"md_hour_split": {
"paint": "",
"prep": ""
@@ -450,6 +451,7 @@
"status": "",
"statuses": {
"active_statuses": "",
"color": "",
"default_arrived": "",
"default_bo": "",
"default_canceled": "",
@@ -466,6 +468,7 @@
"open_statuses": "",
"post_production_statuses": "",
"pre_production_statuses": "",
"production_colors": "",
"production_statuses": ""
},
"target_touchtime": "",
@@ -499,6 +502,7 @@
},
"emaillater": "",
"employees": "",
"estimators": "",
"insurancecos": "",
"intakechecklist": "",
"jobstatuses": "",
@@ -1089,6 +1093,7 @@
"autoallocate": "",
"changelaborrate": "",
"changestatus": "Cambiar Estado",
"changestimator": "",
"convert": "Convertir",
"deliver": "",
"dms": {
@@ -1203,6 +1208,7 @@
"date_open": "Abierto",
"date_scheduled": "Programado",
"ded_amt": "Deducible",
"ded_note": "",
"ded_status": "Estado deducible",
"depreciation_taxes": "Depreciación / Impuestos",
"dms": {
@@ -1278,6 +1284,7 @@
"local_tax_rate": "",
"loss_date": "Fecha de pérdida",
"loss_desc": "",
"loss_of_use": "",
"ma2s": "",
"ma3s": "",
"mabl": "",
@@ -1984,6 +1991,9 @@
"job_costing_ro": "",
"job_notes": "",
"key_tag": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
"paint_grid": "",
"parts_label_single": "",
"parts_list": "",
@@ -2197,6 +2207,11 @@
"work_in_progress_payables": ""
}
},
"schedule": {
"labels": {
"manualevent": ""
}
},
"scoreboard": {
"actions": {
"edit": ""

View File

@@ -270,6 +270,7 @@
"md_categories": "",
"md_ccc_rates": "",
"md_classes": "",
"md_ded_notes": "",
"md_hour_split": {
"paint": "",
"prep": ""
@@ -450,6 +451,7 @@
"status": "",
"statuses": {
"active_statuses": "",
"color": "",
"default_arrived": "",
"default_bo": "",
"default_canceled": "",
@@ -466,6 +468,7 @@
"open_statuses": "",
"post_production_statuses": "",
"pre_production_statuses": "",
"production_colors": "",
"production_statuses": ""
},
"target_touchtime": "",
@@ -499,6 +502,7 @@
},
"emaillater": "",
"employees": "",
"estimators": "",
"insurancecos": "",
"intakechecklist": "",
"jobstatuses": "",
@@ -1089,6 +1093,7 @@
"autoallocate": "",
"changelaborrate": "",
"changestatus": "Changer le statut",
"changestimator": "",
"convert": "Convertir",
"deliver": "",
"dms": {
@@ -1203,6 +1208,7 @@
"date_open": "Ouvrir",
"date_scheduled": "Prévu",
"ded_amt": "Déductible",
"ded_note": "",
"ded_status": "Statut de franchise",
"depreciation_taxes": "Amortissement / taxes",
"dms": {
@@ -1278,6 +1284,7 @@
"local_tax_rate": "",
"loss_date": "Date de perte",
"loss_desc": "",
"loss_of_use": "",
"ma2s": "",
"ma3s": "",
"mabl": "",
@@ -1984,6 +1991,9 @@
"job_costing_ro": "",
"job_notes": "",
"key_tag": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
"paint_grid": "",
"parts_label_single": "",
"parts_list": "",
@@ -2197,6 +2207,11 @@
"work_in_progress_payables": ""
}
},
"schedule": {
"labels": {
"manualevent": ""
}
},
"scoreboard": {
"actions": {
"edit": ""

View File

@@ -374,6 +374,39 @@ export const TemplateList = (type, context) => {
CA_SK: true,
},
},
mpi_final_acct_sheet: {
title: i18n.t("printcenter.jobs.mpi_final_acct_sheet"),
description: "Thank You Letter by RO",
key: "mpi_final_acct_sheet",
subject: i18n.t("printcenter.jobs.mpi_final_acct_sheet"),
disabled: false,
group: "post",
regions: {
CA_MB: true,
},
},
mpi_eglass_auth: {
title: i18n.t("printcenter.jobs.mpi_eglass_auth"),
description: "Thank You Letter by RO",
key: "mpi_eglass_auth",
subject: i18n.t("printcenter.jobs.mpi_eglass_auth"),
disabled: false,
group: "pre",
regions: {
CA_MB: true,
},
},
mpi_animal_checklist: {
title: i18n.t("printcenter.jobs.mpi_animal_checklist"),
description: "Thank You Letter by RO",
key: "mpi_animal_checklist",
subject: i18n.t("printcenter.jobs.mpi_animal_checklist"),
disabled: false,
group: "pre",
regions: {
CA_MB: true,
},
},
// parts_label_multi: {
// title: i18n.t("printcenter.jobs.parts_label_multi"),
// description: "Thank You Letter by RO",

File diff suppressed because it is too large Load Diff

View File

@@ -203,6 +203,7 @@
- authlevel
- default_prod_list_view
- id
- qbo_realmId
- shopid
- useremail
filter:
@@ -216,6 +217,7 @@
- active
- authlevel
- default_prod_list_view
- qbo_realmId
filter:
bodyshop:
associations:
@@ -824,6 +826,8 @@
- md_categories
- md_ccc_rates
- md_classes
- md_ded_notes
- md_estimators
- md_hour_split
- md_ins_cos
- md_jobline_presets
@@ -838,6 +842,8 @@
- md_responsibility_centers
- md_ro_statuses
- messagingservicesid
- pbs_configuration
- pbs_serialnumber
- phone
- prodtargethrs
- production_config
@@ -898,6 +904,8 @@
- md_categories
- md_ccc_rates
- md_classes
- md_ded_notes
- md_estimators
- md_hour_split
- md_ins_cos
- md_jobline_presets
@@ -2585,6 +2593,7 @@
- date_open
- date_scheduled
- ded_amt
- ded_note
- ded_status
- deliverchecklist
- depreciation_taxes
@@ -2660,6 +2669,7 @@
- loss_cat
- loss_date
- loss_desc
- loss_of_use
- loss_type
- other_amount_payable
- owner_owing
@@ -2834,6 +2844,7 @@
- date_open
- date_scheduled
- ded_amt
- ded_note
- ded_status
- deliverchecklist
- depreciation_taxes
@@ -2909,6 +2920,7 @@
- loss_cat
- loss_date
- loss_desc
- loss_of_use
- loss_type
- other_amount_payable
- owner_owing
@@ -3093,6 +3105,7 @@
- date_open
- date_scheduled
- ded_amt
- ded_note
- ded_status
- deliverchecklist
- depreciation_taxes
@@ -3168,6 +3181,7 @@
- loss_cat
- loss_date
- loss_desc
- loss_of_use
- loss_type
- other_amount_payable
- owner_owing

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "pbs_configuration" jsonb
-- null default jsonb_build_object();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "pbs_configuration" jsonb
null default jsonb_build_object();

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "pbs_serialnumber" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "pbs_serialnumber" text
null;

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "entegral_id" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "entegral_id" text
null;

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."jobs" add column "loss_of_use" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "loss_of_use" text
null;

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "md_estimators" jsonb
-- null default jsonb_build_array();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "md_estimators" jsonb
null default jsonb_build_array();

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."jobs" add column "ded_note" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "ded_note" text
null;

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "md_ded_notes" jsonb
-- null default jsonb_build_array();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "md_ded_notes" jsonb
null default jsonb_build_array();

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."associations" add column "qbo_realmId" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."associations" add column "qbo_realmId" text
null;

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
"version": "0.0.1",
"license": "UNLICENSED",
"engines": {
"node": "12.18.3",
"node": "12.22.6",
"npm": "7.17.0"
},
"scripts": {
@@ -17,7 +17,7 @@
"start": "node server.js"
},
"dependencies": {
"aws-sdk": "^2.1006.0",
"aws-sdk": "^2.1013.0",
"bluebird": "^3.7.2",
"body-parser": "^1.18.3",
"cloudinary": "^1.27.1",
@@ -30,7 +30,7 @@
"express": "^4.16.4",
"firebase-admin": "^9.12.0",
"graphql": "^15.6.1",
"graphql-request": "^3.4.0",
"graphql-request": "^3.6.1",
"graylog2": "^0.2.1",
"inline-css": "^3.0.0",
"intuit-oauth": "^4.0.0",
@@ -43,10 +43,11 @@
"phone": "^3.1.8",
"query-string": "^7.0.1",
"soap": "^0.42.0",
"socket.io": "^4.2.0",
"ssh2-sftp-client": "^7.0.4",
"stripe": "^8.181.0",
"twilio": "^3.69.0",
"socket.io": "^4.3.1",
"ssh2-sftp-client": "^7.1.0",
"stripe": "^8.184.0",
"twilio": "^3.70.0",
"uuid": "^8.3.2",
"xmlbuilder2": "^3.0.2"
},
"devDependencies": {

View File

@@ -46,14 +46,15 @@ app.use(bodyParser.json({ limit: "50mb" }));
app.use(bodyParser.urlencoded({ limit: "50mb", extended: true }));
//app.use(enforce.HTTPS({ trustProtoHeader: true }));
app.use(
cors({
credentials: true,
origin: [
"https://test.imex.online",
"http://localhost:3000",
"https://imex.online",
],
})
cors()
// cors({
// credentials: true,
// origin: [
// "https://test.imex.online",
// "http://localhost:3000",
// "https://imex.online",
// ],
// })
);
//Email Based Paths.
@@ -163,6 +164,7 @@ app.post("/qbo/payments", fb.validateFirebaseIdToken, qbo.payments);
var data = require("./server/data/data");
app.post("/data/ah", data.autohouse);
app.post("/data/arms", data.arms);
var ioevent = require("./server/ioevent/ioevent");
app.post("/ioevent", ioevent.default);

View File

@@ -0,0 +1,21 @@
const path = require("path");
require("dotenv").config({
path: path.resolve(
process.cwd(),
`.env.${process.env.NODE_ENV || "development"}`
),
});
const CdkBase = require("../web-sockets/web-socket");
const IMEX_PBS_USER = process.env.IMEX_PBS_USER,
IMEX_PBS_PASSWORD = process.env.IMEX_PBS_PASSWORD;
const PBS_CREDENTIALS = {
password: IMEX_PBS_PASSWORD,
username: IMEX_PBS_USER,
};
exports.PBS_CREDENTIALS = PBS_CREDENTIALS;
// const cdkDomain =
// process.env.NODE_ENV === "production"
// ? "https://3pa.dmotorworks.com"
// : "https://uat-3pa.dmotorworks.com";

View File

@@ -0,0 +1,35 @@
const path = require("path");
require("dotenv").config({
path: path.resolve(
process.cwd(),
`.env.${process.env.NODE_ENV || "development"}`
),
});
const GraphQLClient = require("graphql-request").GraphQLClient;
const soap = require("soap");
const queries = require("../../graphql-client/queries");
const CdkBase = require("../../web-sockets/web-socket");
//const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
//const CalcualteAllocations = require("./cdk-calculate-allocations").default;
const moment = require("moment");
exports.default = async function (socket, jobid) {
socket.logEvents = [];
socket.recordid = jobid;
try {
CdkBase.createLogEvent(
socket,
"DEBUG",
`Received Job export request for id ${jobid}`
);
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error encountered in PbsJobExport. ${error}`
);
}
};

View File

@@ -38,7 +38,9 @@ exports.default = function ({
if (
(jobline.prt_dsmk_p && jobline.prt_dsmk_p !== 0) ||
jobline.prt_dsmk_m !== 0
((jobline.db_ref === "900511" || jobline.db_ref === "900510") &&
jobline.prt_dsmk_m &&
jobline.prt_dsmk_m !== 0)
) {
// console.log("Have a part discount", jobline);
DineroAmount = DineroAmount.add(

View File

@@ -44,9 +44,10 @@ exports.default = async (req, res) => {
)}`
);
} else {
await client.request(queries.SET_QBO_AUTH, {
await client.request(queries.SET_QBO_AUTH_WITH_REALM, {
email: params.state,
qbo_auth: { ...authResponse.json, createdAt: Date.now() },
qbo_realmId: params.realmId,
});
logger.log(
"qbo-callback-create-token-success",

View File

@@ -34,9 +34,13 @@ exports.default = async (req, res) => {
const response = await apiGqlClient.request(queries.GET_QBO_AUTH, {
email: req.user.email,
});
const { qbo_realmId } = response.associations[0];
oauthClient.setToken(response.associations[0].qbo_auth);
if (!qbo_realmId) {
res.status(401).json({ error: "No company associated." });
return;
}
await refreshOauthToken(oauthClient, req);
const BearerToken = req.headers.authorization;
@@ -60,14 +64,25 @@ exports.default = async (req, res) => {
for (const bill of bills) {
try {
let vendorRecord;
vendorRecord = await QueryVendorRecord(oauthClient, req, bill);
vendorRecord = await QueryVendorRecord(
oauthClient,
qbo_realmId,
req,
bill
);
if (!vendorRecord) {
vendorRecord = await InsertVendorRecord(oauthClient, req, bill);
vendorRecord = await InsertVendorRecord(
oauthClient,
qbo_realmId,
req,
bill
);
}
const insertResults = await InsertBill(
oauthClient,
qbo_realmId,
req,
bill,
vendorRecord
@@ -80,7 +95,7 @@ exports.default = async (req, res) => {
success: false,
errorMessage:
(error && error.authResponse && error.authResponse.body) ||
JSON.stringify(error),
(error && error.message),
});
}
}
@@ -93,11 +108,11 @@ exports.default = async (req, res) => {
}
};
async function QueryVendorRecord(oauthClient, req, bill) {
async function QueryVendorRecord(oauthClient, qbo_realmId, req, bill) {
try {
const result = await oauthClient.makeApiCall({
url: urlBuilder(
req.cookies.qbo_realmId,
qbo_realmId,
"query",
`select * From vendor where DisplayName = '${bill.vendor.name}'`
),
@@ -117,19 +132,19 @@ async function QueryVendorRecord(oauthClient, req, bill) {
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
error:
(error && error.authResponse && error.authResponse.body) ||
JSON.stringify(error),
(error && error.message),
method: "QueryVendorRecord",
});
throw error;
}
}
async function InsertVendorRecord(oauthClient, req, bill) {
async function InsertVendorRecord(oauthClient, qbo_realmId, req, bill) {
const Vendor = {
DisplayName: bill.vendor.name,
};
try {
const result = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "vendor"),
url: urlBuilder(qbo_realmId, "vendor"),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -142,15 +157,19 @@ async function InsertVendorRecord(oauthClient, req, bill) {
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
error:
(error && error.authResponse && error.authResponse.body) ||
JSON.stringify(error),
(error && error.message),
method: "InsertVendorRecord",
});
throw error;
}
}
async function InsertBill(oauthClient, req, bill, vendor) {
const { accounts, taxCodes, classes } = await QueryMetaData(oauthClient, req);
async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
const { accounts, taxCodes, classes } = await QueryMetaData(
oauthClient,
qbo_realmId,
req
);
const billQbo = {
VendorRef: {
@@ -182,7 +201,7 @@ async function InsertBill(oauthClient, req, bill, vendor) {
try {
const result = await oauthClient.makeApiCall({
url: urlBuilder(
req.cookies.qbo_realmId,
qbo_realmId,
bill.is_credit_memo ? "vendorcredit" : "bill"
),
method: "POST",
@@ -197,7 +216,7 @@ async function InsertBill(oauthClient, req, bill, vendor) {
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
error:
(error && error.authResponse && error.authResponse.body) ||
JSON.stringify(error),
(error && error.message),
method: "InsertBill",
});
throw error;
@@ -248,10 +267,10 @@ const generateBillLine = (
};
};
async function QueryMetaData(oauthClient, req) {
async function QueryMetaData(oauthClient, qbo_realmId, req) {
const accounts = await oauthClient.makeApiCall({
url: urlBuilder(
req.cookies.qbo_realmId,
qbo_realmId,
"query",
`select * From Account where AccountType = 'Cost of Goods Sold'`
),
@@ -262,7 +281,7 @@ async function QueryMetaData(oauthClient, req) {
});
setNewRefreshToken(req.user.email, accounts);
const taxCodes = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "query", `select * From TaxCode`),
url: urlBuilder(qbo_realmId, "query", `select * From TaxCode`),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -270,7 +289,7 @@ async function QueryMetaData(oauthClient, req) {
});
const classes = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "query", `select * From Class`),
url: urlBuilder(qbo_realmId, "query", `select * From Class`),
method: "POST",
headers: {
"Content-Type": "application/json",

View File

@@ -42,9 +42,12 @@ exports.default = async (req, res) => {
const response = await apiGqlClient.request(queries.GET_QBO_AUTH, {
email: req.user.email,
});
const { qbo_realmId } = response.associations[0];
oauthClient.setToken(response.associations[0].qbo_auth);
if (!qbo_realmId) {
res.status(401).json({ error: "No company associated." });
return;
}
await refreshOauthToken(oauthClient, req);
const BearerToken = req.headers.authorization;
@@ -80,6 +83,7 @@ exports.default = async (req, res) => {
//Query for top level customer, the insurance company name.
insCoCustomerTier = await QueryInsuranceCo(
oauthClient,
qbo_realmId,
req,
payment.job
);
@@ -87,6 +91,7 @@ exports.default = async (req, res) => {
//Creating the Insurance Customer.
insCoCustomerTier = await InsertInsuranceCo(
oauthClient,
qbo_realmId,
req,
payment.job,
bodyshop
@@ -96,11 +101,17 @@ exports.default = async (req, res) => {
if (isThreeTier || (!isThreeTier && twoTierPref === "name")) {
//Insert the name/owner and account for whether the source should be the ins co in 3 tier..
ownerCustomerTier = await QueryOwner(oauthClient, req, payment.job);
ownerCustomerTier = await QueryOwner(
oauthClient,
qbo_realmId,
req,
payment.job
);
//Query for the owner itself.
if (!ownerCustomerTier) {
ownerCustomerTier = await InsertOwner(
oauthClient,
qbo_realmId,
req,
payment.job,
isThreeTier,
@@ -110,26 +121,27 @@ exports.default = async (req, res) => {
}
//Query for the Job or Create it.
jobTier = await QueryJob(oauthClient, req, payment.job);
jobTier = await QueryJob(oauthClient, qbo_realmId, req, payment.job);
// Need to validate that the job tier is associated to the right individual?
if (!jobTier) {
jobTier = await InsertJob(
oauthClient,
qbo_realmId,
req,
payment.job,
ownerCustomerTier || insCoCustomerTier
);
}
await InsertPayment(oauthClient, req, payment, jobTier);
await InsertPayment(oauthClient, qbo_realmId, req, payment, jobTier);
ret.push({ paymentid: payment.id, success: true });
} catch (error) {
logger.log("qbo-payment-create-error", "ERROR", req.user.email, {
error:
(error && error.authResponse && error.authResponse.body) ||
JSON.stringify(error),
(error && error.message),
});
ret.push({
@@ -137,7 +149,7 @@ exports.default = async (req, res) => {
success: false,
errorMessage:
(error && error.authResponse && error.authResponse.body) ||
JSON.stringify(error),
(error && error.message),
});
}
}
@@ -150,9 +162,16 @@ exports.default = async (req, res) => {
}
};
async function InsertPayment(oauthClient, req, payment, parentRef) {
async function InsertPayment(
oauthClient,
qbo_realmId,
req,
payment,
parentRef
) {
const { paymentMethods, invoices } = await QueryMetaData(
oauthClient,
qbo_realmId,
req,
payment.job.ro_number
);
@@ -199,7 +218,7 @@ async function InsertPayment(oauthClient, req, payment, parentRef) {
});
try {
const result = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "payment"),
url: urlBuilder(qbo_realmId, "payment"),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -210,16 +229,16 @@ async function InsertPayment(oauthClient, req, payment, parentRef) {
return result && result.Bill;
} catch (error) {
logger.log("qbo-payables-error", "DEBUG", req.user.email, payment.id, {
error: JSON.stringify(error),
error: error && error.message,
method: "InsertPayment",
});
throw error;
}
}
async function QueryMetaData(oauthClient, req, ro_number) {
async function QueryMetaData(oauthClient, qbo_realmId, req, ro_number) {
const invoice = await oauthClient.makeApiCall({
url: urlBuilder(
req.cookies.qbo_realmId,
qbo_realmId,
"query",
`select * From Invoice where DocNumber = '${ro_number}'`
),
@@ -230,11 +249,7 @@ async function QueryMetaData(oauthClient, req, ro_number) {
});
const paymentMethods = await oauthClient.makeApiCall({
url: urlBuilder(
req.cookies.qbo_realmId,
"query",
`select * From PaymentMethod`
),
url: urlBuilder(qbo_realmId, "query", `select * From PaymentMethod`),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -243,7 +258,7 @@ async function QueryMetaData(oauthClient, req, ro_number) {
setNewRefreshToken(req.user.email, paymentMethods);
// const classes = await oauthClient.makeApiCall({
// url: urlBuilder(req.cookies.qbo_realmId, "query", `select * From Class`),
// url: urlBuilder(qbo_realmId, "query", `select * From Class`),
// method: "POST",
// headers: {
// "Content-Type": "application/json",

View File

@@ -34,7 +34,11 @@ exports.default = async (req, res) => {
const response = await apiGqlClient.request(queries.GET_QBO_AUTH, {
email: req.user.email,
});
const { qbo_realmId } = response.associations[0];
if (!qbo_realmId) {
res.status(401).json({ error: "No company associated." });
return;
}
oauthClient.setToken(response.associations[0].qbo_auth);
await refreshOauthToken(oauthClient, req);
@@ -69,11 +73,17 @@ exports.default = async (req, res) => {
if (isThreeTier || (!isThreeTier && twoTierPref === "source")) {
//Insert the insurance company tier.
//Query for top level customer, the insurance company name.
insCoCustomerTier = await QueryInsuranceCo(oauthClient, req, job);
insCoCustomerTier = await QueryInsuranceCo(
oauthClient,
qbo_realmId,
req,
job
);
if (!insCoCustomerTier) {
//Creating the Insurance Customer.
insCoCustomerTier = await InsertInsuranceCo(
oauthClient,
qbo_realmId,
req,
job,
bodyshop
@@ -83,11 +93,17 @@ exports.default = async (req, res) => {
if (isThreeTier || (!isThreeTier && twoTierPref === "name")) {
//Insert the name/owner and account for whether the source should be the ins co in 3 tier..
ownerCustomerTier = await QueryOwner(oauthClient, req, job);
ownerCustomerTier = await QueryOwner(
oauthClient,
qbo_realmId,
req,
job
);
//Query for the owner itself.
if (!ownerCustomerTier) {
ownerCustomerTier = await InsertOwner(
oauthClient,
qbo_realmId,
req,
job,
isThreeTier,
@@ -97,13 +113,14 @@ exports.default = async (req, res) => {
}
//Query for the Job or Create it.
jobTier = await QueryJob(oauthClient, req, job);
jobTier = await QueryJob(oauthClient, qbo_realmId, req, job);
// Need to validate that the job tier is associated to the right individual?
if (!jobTier) {
jobTier = await InsertJob(
oauthClient,
qbo_realmId,
req,
job,
@@ -112,7 +129,14 @@ exports.default = async (req, res) => {
}
if (!req.body.custDataOnly) {
await InsertInvoice(oauthClient, req, job, bodyshop, jobTier);
await InsertInvoice(
oauthClient,
qbo_realmId,
req,
job,
bodyshop,
jobTier
);
}
ret.push({ jobid: job.id, success: true });
} catch (error) {
@@ -121,7 +145,7 @@ exports.default = async (req, res) => {
success: false,
errorMessage:
(error && error.authResponse && error.authResponse.body) ||
JSON.stringify(error),
(error && error.message),
});
}
}
@@ -136,11 +160,11 @@ exports.default = async (req, res) => {
}
};
async function QueryInsuranceCo(oauthClient, req, job) {
async function QueryInsuranceCo(oauthClient, qbo_realmId, req, job) {
try {
const result = await oauthClient.makeApiCall({
url: urlBuilder(
req.cookies.qbo_realmId,
qbo_realmId,
"query",
`select * From Customer where DisplayName = '${job.ins_co_nm}'`
),
@@ -165,7 +189,7 @@ async function QueryInsuranceCo(oauthClient, req, job) {
}
}
exports.QueryInsuranceCo = QueryInsuranceCo;
async function InsertInsuranceCo(oauthClient, req, job, bodyshop) {
async function InsertInsuranceCo(oauthClient, qbo_realmId, req, job, bodyshop) {
const insCo = bodyshop.md_ins_cos.find((i) => i.name === job.ins_co_nm);
const Customer = {
@@ -180,7 +204,7 @@ async function InsertInsuranceCo(oauthClient, req, job, bodyshop) {
};
try {
const result = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "customer"),
url: urlBuilder(qbo_realmId, "customer"),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -198,11 +222,11 @@ async function InsertInsuranceCo(oauthClient, req, job, bodyshop) {
}
}
exports.InsertInsuranceCo = InsertInsuranceCo;
async function QueryOwner(oauthClient, req, job) {
async function QueryOwner(oauthClient, qbo_realmId, req, job) {
const ownerName = generateOwnerTier(job, true, null);
const result = await oauthClient.makeApiCall({
url: urlBuilder(
req.cookies.qbo_realmId,
qbo_realmId,
"query",
`select * From Customer where DisplayName = '${ownerName}'`
),
@@ -220,7 +244,14 @@ async function QueryOwner(oauthClient, req, job) {
);
}
exports.QueryOwner = QueryOwner;
async function InsertOwner(oauthClient, req, job, isThreeTier, parentTierRef) {
async function InsertOwner(
oauthClient,
qbo_realmId,
req,
job,
isThreeTier,
parentTierRef
) {
const ownerName = generateOwnerTier(job, true, null);
const Customer = {
DisplayName: ownerName,
@@ -242,7 +273,7 @@ async function InsertOwner(oauthClient, req, job, isThreeTier, parentTierRef) {
};
try {
const result = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "customer"),
url: urlBuilder(qbo_realmId, "customer"),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -260,10 +291,10 @@ async function InsertOwner(oauthClient, req, job, isThreeTier, parentTierRef) {
}
}
exports.InsertOwner = InsertOwner;
async function QueryJob(oauthClient, req, job) {
async function QueryJob(oauthClient, qbo_realmId, req, job) {
const result = await oauthClient.makeApiCall({
url: urlBuilder(
req.cookies.qbo_realmId,
qbo_realmId,
"query",
`select * From Customer where DisplayName = '${job.ro_number}'`
),
@@ -281,7 +312,7 @@ async function QueryJob(oauthClient, req, job) {
);
}
exports.QueryJob = QueryJob;
async function InsertJob(oauthClient, req, job, parentTierRef) {
async function InsertJob(oauthClient, qbo_realmId, req, job, parentTierRef) {
const Customer = {
DisplayName: job.ro_number,
BillAddr: {
@@ -299,7 +330,7 @@ async function InsertJob(oauthClient, req, job, parentTierRef) {
};
try {
const result = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "customer"),
url: urlBuilder(qbo_realmId, "customer"),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -317,9 +348,9 @@ async function InsertJob(oauthClient, req, job, parentTierRef) {
}
}
exports.InsertJob = InsertJob;
async function QueryMetaData(oauthClient, req) {
async function QueryMetaData(oauthClient, qbo_realmId, req) {
const items = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "query", `select * From Item`),
url: urlBuilder(qbo_realmId, "query", `select * From Item`),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -327,7 +358,7 @@ async function QueryMetaData(oauthClient, req) {
});
setNewRefreshToken(req.user.email, items);
const taxCodes = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "query", `select * From TaxCode`),
url: urlBuilder(qbo_realmId, "query", `select * From TaxCode`),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -335,7 +366,7 @@ async function QueryMetaData(oauthClient, req) {
});
const classes = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "query", `select * From Class`),
url: urlBuilder(qbo_realmId, "query", `select * From Class`),
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -375,8 +406,19 @@ async function QueryMetaData(oauthClient, req) {
};
}
async function InsertInvoice(oauthClient, req, job, bodyshop, parentTierRef) {
const { items, taxCodes, classes } = await QueryMetaData(oauthClient, req);
async function InsertInvoice(
oauthClient,
qbo_realmId,
req,
job,
bodyshop,
parentTierRef
) {
const { items, taxCodes, classes } = await QueryMetaData(
oauthClient,
qbo_realmId,
req
);
const InvoiceLineAdd = CreateInvoiceLines({
bodyshop,
jobs_by_pk: job,
@@ -407,7 +449,7 @@ async function InsertInvoice(oauthClient, req, job, bodyshop, parentTierRef) {
try {
const result = await oauthClient.makeApiCall({
url: urlBuilder(req.cookies.qbo_realmId, "invoice"),
url: urlBuilder(qbo_realmId, "invoice"),
method: "POST",
headers: {

View File

@@ -111,18 +111,18 @@ const generatePayment = (payment, isThreeTier, twoTierPref) => {
ReceivePaymentAddRq: {
ReceivePaymentAdd: {
CustomerRef: {
FullName:
payment.job.bodyshop.accountingconfig.tiers === 3
? `${generateSourceTier(payment.job)}:${generateOwnerTier(
payment.job,
isThreeTier,
twoTierPref
)}:${generateJobTier(payment.job)}`
: `${generateOwnerTier(
payment.job,
isThreeTier,
twoTierPref
)}:${generateJobTier(payment.job)}`,
FullName: (payment.job.bodyshop.accountingconfig.tiers === 3
? `${generateSourceTier(payment.job)}:${generateOwnerTier(
payment.job,
isThreeTier,
twoTierPref
)}:${generateJobTier(payment.job)}`
: `${generateOwnerTier(
payment.job,
isThreeTier,
twoTierPref
)}:${generateJobTier(payment.job)}`
).trim(),
},
ARAccountRef: {
FullName:
@@ -155,18 +155,18 @@ const generatePayment = (payment, isThreeTier, twoTierPref) => {
CreditMemoAddRq: {
CreditMemoAdd: {
CustomerRef: {
FullName:
payment.job.bodyshop.accountingconfig.tiers === 3
? `${generateSourceTier(payment.job)}:${generateOwnerTier(
payment.job,
isThreeTier,
twoTierPref
)}:${generateJobTier(payment.job)}`
: `${generateOwnerTier(
payment.job,
isThreeTier,
twoTierPref
)}:${generateJobTier(payment.job)}`,
FullName: (payment.job.bodyshop.accountingconfig.tiers === 3
? `${generateSourceTier(payment.job)}:${generateOwnerTier(
payment.job,
isThreeTier,
twoTierPref
)}:${generateJobTier(payment.job)}`
: `${generateOwnerTier(
payment.job,
isThreeTier,
twoTierPref
)}:${generateJobTier(payment.job)}`
).trim(),
},
ARAccountRef: {
FullName:

View File

@@ -122,7 +122,7 @@ const generateSourceCustomerQbxml = (jobs_by_pk, bodyshop) => {
"@onError": "continueOnError",
CustomerAddRq: {
CustomerAdd: {
Name: jobs_by_pk.ins_co_nm,
Name: jobs_by_pk.ins_co_nm.trim(),
// BillAddress: {
// Addr1: jobs_by_pk.ownr_addr1,
// Addr2: jobs_by_pk.ownr_addr2,
@@ -238,16 +238,16 @@ const generateInvoiceQbxml = (
InvoiceAddRq: {
InvoiceAdd: {
CustomerRef: {
FullName:
bodyshop.accountingconfig.tiers === 3
? `${generateSourceTier(jobs_by_pk)}:${generateOwnerTier(
jobs_by_pk
)}:${generateJobTier(jobs_by_pk)}`
: `${generateOwnerTier(
jobs_by_pk,
isThreeTier,
twoTierPref
)}:${generateJobTier(jobs_by_pk)}`,
FullName: (bodyshop.accountingconfig.tiers === 3
? `${generateSourceTier(jobs_by_pk)}:${generateOwnerTier(
jobs_by_pk
)}:${generateJobTier(jobs_by_pk)}`
: `${generateOwnerTier(
jobs_by_pk,
isThreeTier,
twoTierPref
)}:${generateJobTier(jobs_by_pk)}`
).trim(),
},
...(jobs_by_pk.class

View File

@@ -6,24 +6,25 @@ exports.addQbxmlHeader = addQbxmlHeader = (xml) => {
};
exports.generateSourceTier = (jobs_by_pk) => {
return jobs_by_pk.ins_co_nm;
return jobs_by_pk.ins_co_nm && jobs_by_pk.ins_co_nm.trim();
};
exports.generateJobTier = (jobs_by_pk) => {
return jobs_by_pk.ro_number;
return jobs_by_pk.ro_number && jobs_by_pk.ro_number.trim();
};
exports.generateOwnerTier = (jobs_by_pk, isThreeTier, twotierpref) => {
if (isThreeTier) {
//It's always gonna be the owner now. Same as 2 tier by name
return jobs_by_pk.ownr_co_nm
? `${jobs_by_pk.ownr_co_nm.substring(0, 30)} #${
jobs_by_pk.owner.accountingid || ""
}`
: `${`${jobs_by_pk.ownr_ln || ""} ${jobs_by_pk.ownr_fn || ""}`.substring(
0,
30
)} #${jobs_by_pk.owner.accountingid || ""}`;
return (
jobs_by_pk.ownr_co_nm
? `${jobs_by_pk.ownr_co_nm.substring(0, 30)} #${
jobs_by_pk.owner.accountingid || ""
}`
: `${`${jobs_by_pk.ownr_ln || ""} ${
jobs_by_pk.ownr_fn || ""
}`.substring(0, 30)} #${jobs_by_pk.owner.accountingid || ""}`
).trim();
} else {
//What's the 2 tier pref?
if (twotierpref === "source") {
@@ -31,13 +32,15 @@ exports.generateOwnerTier = (jobs_by_pk, isThreeTier, twotierpref) => {
//It should be the insurance co.
} else {
//Same as 3 tier
return jobs_by_pk.ownr_co_nm
? `${jobs_by_pk.ownr_co_nm.substring(0, 30)} #${
jobs_by_pk.owner.accountingid || ""
}`
: `${`${jobs_by_pk.ownr_ln || ""} ${
jobs_by_pk.ownr_fn || ""
}`.substring(0, 30)} #${jobs_by_pk.owner.accountingid || ""}`;
return (
jobs_by_pk.ownr_co_nm
? `${jobs_by_pk.ownr_co_nm.substring(0, 30)} #${
jobs_by_pk.owner.accountingid || ""
}`
: `${`${jobs_by_pk.ownr_ln || ""} ${
jobs_by_pk.ownr_fn || ""
}`.substring(0, 30)} #${jobs_by_pk.owner.accountingid || ""}`
).trim();
}
}
};

View File

@@ -70,7 +70,12 @@ exports.default = async function (socket, jobid) {
amount: Math.round(val.act_price * 100),
}).multiply(val.part_qty || 1);
if ((val.prt_dsmk_p && val.prt_dsmk_p !== 0) || val.prt_dsmk_m !== 0) {
if (
(val.prt_dsmk_p && val.prt_dsmk_p !== 0) ||
((val.db_ref === "900511" || val.db_ref === "900510") &&
val.prt_dsmk_m &&
val.prt_dsmk_m !== 0)
) {
// console.log("Have a part discount", val);
DineroAmount = DineroAmount.add(
val.prt_dsmk_m && val.prt_dsmk_m !== 0

View File

@@ -584,7 +584,7 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
addressLine:
socket.JobData.ownr_addr1 &&
socket.JobData.ownr_addr1.replace(replaceSpecialRegex, ""),
city: socket.JobData.ownr_city.replace(replaceSpecialRegex, ""),
city: socket.JobData.ownr_city&& socket.JobData.ownr_city.replace(replaceSpecialRegex, ""),
country: null,
postalCode:
socket.JobData.ownr_zip &&
@@ -592,7 +592,7 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
.toUpperCase()
.replace(/\W/g, "")
.replace(/(...)/, "$1 "),
stateOrProvince: socket.JobData.ownr_st,
stateOrProvince: socket.JobData.ownr_st&&socket.JobData.ownr_st.replace(replaceSpecialRegex, ""),
},
contactInfo: {
mainTelephoneNumber: {
@@ -1090,18 +1090,6 @@ async function GenerateTransWips(socket) {
wips.push(item);
});
//should validate that the wips = 0
console.log(
"WIPS TOTAL",
wips.reduce((acc, val) => {
console.log(val);
console.log(acc + val.postAmt);
return acc + val.postAmt;
}, 0)
);
return wips;
}

651
server/data/SampleArms.json Normal file
View File

@@ -0,0 +1,651 @@
{
"RepairOrderFolderAddRq": {
"RqUID": "426cce3a-efa7-44d9-b76e-50b9102c4198",
"DocumentInfo": {
"BMSVer": "4.0.0",
"DocumentType": "Repair Order",
"DocumentVerCode": "EM",
"DocumentVerNum": 0,
"DocumentStatus": "O",
"CreateDateTime": "2009-03-11T11:58:31.0404914-07:00",
"TransmitDateTime": "2009-03-11T11:58:31.0404914-07:00"
},
"EventInfo": {
"AssignmentEvent": {
"CreateDateTime": "2009-03-02T17:00:00.0000000-08:00"
},
"EstimateEvent": {
"UploadDateTime": "2009-03-02T17:00:00.0000000-08:00"
},
"RepairEvent": {
"ArrivalDateTime": "2009-03-02T17:00:00.0000000-08:00",
"ArrivalOdometerReading": 12540,
"TargetCompletionDateTime": "2009-03-09T17:00:00.0000000-07:00",
"ActualCompletionDateTime": "2009-03-05T17:00:00.0000000-08:00",
"ActualPickUpDateTime": "2009-03-06T17:00:00.0000000-08:00",
"CreatedDateTime": "2009-03-02T17:00:00.0000000-08:00",
"CloseDateTime": "2009-03-06T17:00:00.0000000-08:00"
}
},
"RepairOrderHeader": {
"AdminInfo": {
"InsuranceCompany": {
"Party": {
"OrgInfo": {
"CompanyName": "Nationwide Insurance",
"IDInfo": {
"IDQualifierCode": "US",
"IDNum": 44
},
"Communications": [
{
"CommQualifier": "WA",
"Address": {
"Address1": "1245 Central Street",
"Address2": "#310",
"City": "Anaheim",
"StateProvince": "CA",
"PostalCode": 92808,
"CountryCode": "US"
}
},
{
"CommQualifier": "WP",
"CommPhone": "714-5551212"
},
{
"CommQualifier": "WF",
"CommPhone": "714-5551414"
}
]
},
"ContactInfo": {
"ContactJobTitle": "Adjuster",
"ContactName": {
"FirstName": "Jim",
"LastName": "Smith"
}
}
}
},
"InsuranceAgent": {
"Party": {
"OrgInfo": {
"CompanyName": "Nationwide Insurance",
"Communications": {
"CommQualifier": "WP",
"CommPhone": "714-5551212"
}
},
"ContactInfo": {
"ContactJobTitle": "Insurance Agent",
"ContactName": {
"FirstName": "Paul",
"LastName": "White"
}
}
}
},
"Insured": {
"Party": {
"PersonInfo": {
"PersonName": {
"FirstName": "Jim",
"LastName": "Smith"
}
}
}
},
"Owner": {
"Party": {
"PersonInfo": {
"PersonName": {
"FirstName": "Cathy",
"LastName": "Jones"
},
"Communications": [
{
"CommQualifier": "HA",
"Address": {
"Address1": "2571 Elm Street",
"City": "Anaheim",
"StateProvince": "CA",
"PostalCode": 92808,
"CountryCode": "US"
}
},
{
"CommQualifier": "HP",
"CommPhone": "714-5551212"
},
{
"CommQualifier": "WP",
"CommPhone": "714-5551414"
},
{
"CommQualifier": "CP",
"CommPhone": "714-5551616"
},
{
"CommQualifier": "EM",
"CommEmail": "cjones@cox.net"
}
]
}
}
},
"Claimant": {
"Party": {
"PersonInfo": {
"PersonName": {
"FirstName": "Jim",
"LastName": "Smith"
}
}
},
"OwnerInd": true
},
"Estimator": {
"Party": {
"PersonInfo": {
"PersonName": {
"FirstName": "Jim",
"LastName": "Smith"
},
"IDInfo": {
"IDQualifierCode": "US",
"IDNum": 2941
}
}
}
},
"RepairFacility": {
"Party": {
"OrgInfo": {
"CompanyName": "Mikes Auto Collision",
"IDInfo": {
"IDQualifierCode": "US",
"IDNum": 2207
}
}
}
}
},
"RepairOrderIDs": {
"RepairOrderNum": 10245,
"VendorCode": "C",
"EstimateDocumentID": "1223HJ76"
},
"RepairOrderType": "DRP",
"ReferralSourceType": "Yellow Pages",
"VehicleInfo": {
"VINInfo": {
"VIN": {
"VINNum": "Z13838383"
}
},
"License": {
"LicensePlateNum": "2H6781"
},
"VehicleDesc": {
"ProductionDate": "2009-10",
"ModelYear": 2006,
"MakeDesc": "Ford",
"ModelName": "Mustang Convertible"
},
"Paint": {
"Exterior": {
"Color": {
"ColorName": "Red",
"OEMColorCode": "1M3"
}
}
},
"Body": {
"BodyStyle": "2 Door Convertible",
"Trim": {
"TrimCode": "1B3"
}
},
"Condition": {
"DrivableInd": "Y"
}
},
"ClaimInfo": {
"ClaimNum": "C03062009-01",
"PolicyInfo": {
"PolicyNum": "P29438484"
},
"LossInfo": {
"Facts": {
"LossDateTime": "2009-03-09T17:00:00.0000000-07:00",
"LossDescCode": "Collision",
"PrimaryPOI": {
"POICode": 3
},
"SecondaryPOI": {
"POICode": 2
}
},
"TotalLossInd": "N"
}
}
},
"ProfileInfo": {
"ProfileName": "Shop Standard Rates",
"RateInfo": [
{
"RateType": "PA",
"RateDesc": "Parts Tax",
"TaxInfo": {
"TaxType": "LS",
"TaxableInd": true,
"TaxTierInfo": {
"TierNum": 1,
"Percentage": 0
}
}
},
{
"RateType": "LA",
"RateDesc": "Labor Tax",
"TaxInfo": {
"TaxType": "LS",
"TaxableInd": true,
"TaxTierInfo": {
"TierNum": 1,
"Percentage": 0
}
}
},
{
"RateType": "LAB",
"RateDesc": "Body Labor",
"RateTierInfo": {
"TierNum": 1,
"Rate": 38
}
},
{
"RateType": "LAS",
"RateDesc": "Structural Labor",
"RateTierInfo": {
"TierNum": 1,
"Rate": 38
}
},
{
"RateType": "LAR",
"RateDesc": "Refinish Labor",
"RateTierInfo": {
"TierNum": 1,
"Rate": 38
}
},
{
"RateType": "LAG",
"RateDesc": "Glass Labor",
"RateTierInfo": {
"TierNum": 1,
"Rate": 38
}
},
{
"RateType": "LAF",
"RateDesc": "Frame Labor",
"RateTierInfo": {
"TierNum": 1,
"Rate": 38
}
},
{
"RateType": "LAM",
"RateDesc": "Mechancial Labor",
"RateTierInfo": {
"TierNum": 1,
"Rate": 38
}
},
{
"RateType": "LAU",
"RateDesc": "User Defined Labor",
"RateTierInfo": {
"TierNum": 1,
"Rate": 0
}
},
{
"RateType": "MAPA",
"RateDesc": "Paint Materials",
"RateTierInfo": {
"TierNum": 1,
"Rate": 21,
"ThresholdAmt": 0
},
"MaterialCalcSettings": {
"CalcMethodCode": 2,
"CalcMaxAmt": 9999.99
}
},
{
"RateType": "MASH",
"RateDesc": "Shop Materials",
"RateTierInfo": {
"TierNum": 1,
"Rate": 0
},
"MaterialCalcSettings": {
"CalcMethodCode": 4,
"CalcMaxAmt": 9999.99
}
},
{
"RateType": "MAHW",
"RateDesc": "Hazardous Wastes Removal",
"RateTierInfo": {
"TierNum": 1,
"Rate": 10
},
"MaterialCalcSettings": {
"CalcMethodCode": 2,
"CalcMaxAmt": 10
}
},
{
"RateType": "MA2S",
"RateDesc": "Two Stage Paint",
"RateTierInfo": {
"TierNum": 1,
"Rate": 0
},
"MaterialCalcSettings": {
"CalcMethodCode": 1,
"CalcMaxAmt": 999999.99
}
},
{
"RateType": "MA2T",
"RateDesc": "Two Tone Paint",
"RateTierInfo": {
"TierNum": 1,
"Rate": 0
},
"MaterialCalcSettings": {
"CalcMethodCode": 1,
"CalcMaxAmt": 999999.99
}
},
{
"RateType": "MA3S",
"RateDesc": "Three Stage Paint",
"RateTierInfo": {
"TierNum": 1,
"Rate": 0
},
"MaterialCalcSettings": {
"CalcMethodCode": 1,
"CalcMaxAmt": 999999.99
}
}
]
},
"StorageDuration": 17,
"RepairTotalsInfo": {
"LaborTotalsInfo": [
{
"TotalType": "LAB",
"TotalTypeDesc": "Body Labor",
"TotalHours": 5.7,
"TotalAmt": 216.6
},
{
"TotalType": "LAF",
"TotalTypeDesc": "Frame Labor",
"TotalHours": 2.5,
"TotalAmt": 255.45
},
{
"TotalType": "LAM",
"TotalTypeDesc": "Mechanical Labor",
"TotalHours": 4.7,
"TotalAmt": 489.45
},
{
"TotalType": "LAR",
"TotalTypeDesc": "Refinish Labor",
"TotalHours": 3.7,
"TotalAmt": 140.6
}
],
"PartsTotalsInfo": [
{
"TotalType": "PAA",
"TotalTypeDesc": "Aftermarket Parts",
"TotalAmt": 26.4
},
{
"TotalType": "PAC",
"TotalTypeDesc": "Re-Chromed Parts",
"TotalAmt": 156.45
},
{
"TotalType": "PAG",
"TotalTypeDesc": "Glass Parts",
"TotalAmt": 740
},
{
"TotalType": "PAL",
"TotalTypeDesc": "LKQ/Used Parts",
"TotalAmt": 326.4
},
{
"TotalType": "PAM",
"TotalTypeDesc": "Remanufactured Parts",
"TotalAmt": 50.94
},
{
"TotalType": "PAN",
"TotalTypeDesc": "New Parts",
"TotalAmt": 4526.4
},
{
"TotalType": "PAR",
"TotalTypeDesc": "Recored Parts",
"TotalAmt": 209.45
}
],
"OtherChargesTotalsInfo": [
{
"TotalType": "OTSL",
"TotalTypeDesc": "Sublet",
"TotalAmt": 0
},
{
"TotalType": "MAPA",
"TotalTypeDesc": "Paint Materials",
"TotalAmt": 77.7
},
{
"TotalType": "MASH",
"TotalTypeDesc": "Shop Materials",
"TotalAmt": 0
},
{
"TotalType": "MAHW",
"TotalTypeDesc": "Hazardous Wastes Removal",
"TotalAmt": 10
},
{
"TotalType": "OTST",
"TotalTypeDesc": "Storage",
"TotalAmt": 0
},
{
"TotalType": "OTTW",
"TotalTypeDesc": "Towing",
"TotalAmt": 0
},
{
"TotalType": "OTAC",
"TotalTypeDesc": "Additional Charges",
"TotalAmt": 0
}
],
"SummaryTotalsInfo": [
{
"TotalType": "TOT",
"TotalSubType": "T2",
"TotalTypeDesc": "Net Total",
"TotalAmt": 471.3
},
{
"TotalType": "TOT",
"TotalSubType": "F7",
"TotalTypeDesc": "Sales Tax",
"TotalAmt": 0
},
{
"TotalType": "TOT",
"TotalSubType": "GST",
"TotalTypeDesc": "GST Tax",
"TotalAmt": 0
},
{
"TotalType": "TOT",
"TotalSubType": "TT",
"TotalTypeDesc": "Gross Total",
"TotalAmt": 471.3
},
{
"TotalType": "TOT",
"TotalSubType": "SM",
"TotalTypeDesc": "Supplement Total",
"TotalAmt": 0
},
{
"TotalType": "TOT",
"TotalSubType": "D2",
"TotalTypeDesc": "Deductible",
"TotalAmt": 0
},
{
"TotalType": "TOT",
"TotalSubType": "BTR",
"TotalTypeDesc": "Betterment",
"TotalAmt": 0
},
{
"TotalType": "TOT",
"TotalSubType": "AA",
"TotalTypeDesc": "Appearance Allowance",
"TotalAmt": 0
},
{
"TotalType": "TOT",
"TotalSubType": "D8",
"TotalTypeDesc": "Bottom Line Discount",
"TotalAmt": 0
},
{
"TotalType": "TOT",
"TotalSubType": "INS",
"TotalTypeDesc": "Insurance Pay",
"TotalAmt": 471.3
},
{
"TotalType": "TOT",
"TotalSubType": "DEPOSIT",
"TotalTypeDesc": "Deposit",
"TotalAmt": 0
},
{
"TotalType": "TOT",
"TotalSubType": "CUST",
"TotalTypeDesc": "Customer Pay",
"TotalAmt": 0
}
],
"RepairTotalsType": 1
},
"RepairLabor": {
"LaborAllocations": {
"LaborAllocation": [
{
"LaborAllocationUUID": "426cce3a-efa7-44d9-b76e-50b9102c4198",
"LaborType": "LAB",
"Technician": {
"Employee": {
"PersonInfo": {
"PersonName": {
"FirstName": "Jose",
"LastName": "Gonzalez"
},
"IDInfo": {
"IDQualifierCode": "US",
"IDNum": 2987
}
}
}
},
"AllocatedHours": 3.5
},
{
"LaborAllocationUUID": "426cce3a-efa7-44d9-b76e-50b9102c4199",
"LaborType": "LAR",
"Technician": {
"Employee": {
"PersonInfo": {
"PersonName": {
"FirstName": "Rcardo",
"LastName": "Himenez"
},
"IDInfo": {
"IDQualifierCode": "US",
"IDNum": 2989
}
}
}
},
"AllocatedHours": 5.5
}
]
}
},
"ProductionStatus": {
"ProductionStage": {
"ProductionStageCode": 4,
"ProductionStageDateTime": "2009-03-11T11:58:32.1898319-07:00",
"ProductionStageStatusComment": "Going to be painted this afternoon"
},
"RepairStatus": {
"RepairStatusCode": 2,
"RepairStatusDateTime": "2009-03-11T11:58:32.1898319-07:00",
"RepairStatusMemo": "Waiting on back ordered parts"
}
},
"RepairOrderNotes": {
"RepairOrderNote": {
"LineSequenceNum": 1,
"Note": "Revision Requested : approved--but needs est separated.8/22/2008 11:58:53 AM",
"CreateDateTime": "2008-08-22T11:58:53",
"AuthoredBy": {
"FirstName": {
"#text": "Elizabeth/FirstName>",
"LastName": "Unis"
}
},
"RepairOrderNote": {
"LineSequenceNum": 2,
"Note": "Approved : 8/26/2008 12:21:08 PM",
"CreateDateTime": "2008-08-26T12:21:08",
"AuthoredBy": {
"FirstName": {
"#text": "Elizabeth/FirstName>",
"LastName": "Unis"
}
}
}
}
}
}
}

817
server/data/arms.js Normal file
View File

@@ -0,0 +1,817 @@
const path = require("path");
const queries = require("../graphql-client/queries");
const Dinero = require("dinero.js");
const moment = require("moment");
var builder = require("xmlbuilder2");
const _ = require("lodash");
const logger = require("../utils/logger");
require("dotenv").config({
path: path.resolve(
process.cwd(),
`.env.${process.env.NODE_ENV || "development"}`
),
});
const client = require("../graphql-client/graphql-client").client;
const uuid = require("uuid").v4;
exports.default = async (req, res) => {
//Query for the List of Bodyshop Clients.
logger.log("arms-start", "DEBUG", "api", null, null);
const { bodyshops } = await client.request(queries.GET_ENTEGRAL_SHOPS);
const allxmlsToUpload = [];
const allErrors = [];
try {
for (const bodyshop of bodyshops) {
logger.log("arms-start-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname,
});
const erroredJobs = [];
try {
const { jobs } = await client.request(queries.ENTEGRAL_EXPORT, {
bodyshopid: bodyshop.id,
});
const ret = jobs.map((job) => {
const transId = uuid(); // Can this actually be the job id?
return {
RepairOrderFolderAddRq: {
RqUID: transId,
DocumentInfo: {
BMSVer: "4.0.0",
DocumentType: "Repair Order",
DocumentVerCode: "EM",
DocumentVerNum: GetSupplementNumber(job.joblines), //TODO Get Supplement Number
DocumentStatus: GetDocumentstatus(job, bodyshop),
CreateDateTime: moment().format(),
// TransmitDateTime: "2009-03-11T11:58:31.0404914-07:00", Omitted from ARMS docs
},
EventInfo: {
AssignmentEvent: {
CreateDateTime:
job.asgn_date && moment(job.asgn_date).format(),
},
EstimateEvent: {
UploadDateTime: "2009-03-02T17:00:00.0000000-08:00", //TODO Figure out what this actually is. 'Date Estimate was uploaded'
},
RepairEvent: {
ArrivalDateTime:
job.date_open && moment(job.date_open).format(),
ArrivalOdometerReading: job.kmin,
TargetCompletionDateTime:
job.scheduled_completion &&
moment(job.scheduled_completion).format(),
ActualCompletionDateTime:
job.actual_completion &&
moment(job.actual_completion).format(),
ActualPickUpDateTime:
job.actual_delivery && moment(job.actual_delivery).format(),
CloseDateTime:
job.date_exported && moment(job.date_exported).format(),
},
},
RepairOrderHeader: {
AdminInfo: {
InsuranceCompany: {
Party: {
OrgInfo: {
CompanyName: job.ins_co_nm,
IDInfo: {
IDQualifierCode: "US",
IDNum: 44, // ** Not sure where to get this entegral ID from?
},
Communications: [
{
CommQualifier: "WA",
Address: {
Address1: job.ins_addr1,
Address2: job.ins_addr2,
City: job.ins_city,
StateProvince: job.ins_st,
PostalCode: job.ins_zip,
CountryCode: job.ins_ctry,
},
},
{
CommQualifier: "WP",
CommPhone: job.ins_ph1,
},
{
CommQualifier: "WF",
CommPhone: job.ins_ph2,
},
],
},
ContactInfo: {
ContactJobTitle: "Adjuster",
ContactName: {
FirstName: job.est_ct_fn,
LastName: job.est_ct_ln,
},
},
},
},
// InsuranceAgent: {
// Party: {
// OrgInfo: {
// CompanyName: "Nationwide Insurance",
// Communications: {
// CommQualifier: "WP",
// CommPhone: "714-5551212",
// },
// },
// ContactInfo: {
// ContactJobTitle: "Insurance Agent",
// ContactName: {
// FirstName: "Paul",
// LastName: "White",
// },
// },
// },
// },
Insured: {
Party: {
PersonInfo: {
PersonName: {
FirstName: job.insd_fn,
LastName: job.insd_ln,
},
},
},
},
Owner: {
Party: {
PersonInfo: {
PersonName: {
FirstName: job.ownr_fn,
LastName: job.ownr_ln,
},
Communications: [
{
CommQualifier: "HA",
Address: {
Address1: job.ownr_addr1,
City: job.ownr_city,
StateProvince: job.ownr_st,
PostalCode: job.ownr_zip,
CountryCode: job.ownr_ctry,
},
},
{
CommQualifier: "HP",
CommPhone: job.ownr_ph1,
},
{
CommQualifier: "WP",
CommPhone: job.ownr_ph2,
},
{
CommQualifier: "CP",
CommPhone: job.ownr_ph1,
},
{
CommQualifier: "EM",
CommEmail: job.ownr_ea,
},
],
},
},
},
Claimant: {
Party: {
PersonInfo: {
PersonName: {
FirstName: job.clm_ct_fn,
LastName: job.clm_ct_ln,
},
},
},
OwnerInd: true,
},
Estimator: {
Party: {
PersonInfo: {
PersonName: {
FirstName: job.est_ct_fn,
LastName: job.est_ct_ln,
},
// IDInfo: {
// IDQualifierCode: "US",
// IDNum: 2941,
// },
},
},
},
RepairFacility: {
//This section not in documentation.
Party: {
OrgInfo: {
CompanyName: bodyshop.shopname,
IDInfo: {
IDQualifierCode: "US",
IDNum: bodyshop.entegral_id,
},
},
},
},
},
RepairOrderIDs: {
RepairOrderNum: job.ro_number,
// VendorCode: "C",
// EstimateDocumentID: "1223HJ76",
},
//RepairOrderType: "DRP",
//ReferralSourceType: "Yellow Pages",
VehicleInfo: {
VINInfo: {
VIN: {
VINNum: job.v_vin,
},
},
License: {
LicensePlateNum: job.plate_no,
},
VehicleDesc: {
//ProductionDate: "2009-10",
ModelYear: job.v_model_yr,
MakeDesc: job.v_make_desc,
ModelName: job.v_model_desc,
},
Paint: {
Exterior: {
Color: {
ColorName: job.v_color,
// OEMColorCode: "1M3",
},
},
},
// Body: {
// BodyStyle: "2 Door Convertible",
// Trim: {
// TrimCode: "1B3",
// },
// },
Condition: {
DrivableInd: job.driveable ? "Y" : "N",
},
},
ClaimInfo: {
ClaimNum: job.clm_no,
PolicyInfo: {
PolicyNum: job.policy_no,
},
LossInfo: {
Facts: {
LossDateTime:
job.loss_date && moment(job.loss_date).format(),
LossDescCode: "Collision",
PrimaryPOI: {
POICode: job.area_of_damage.impact1,
},
SecondaryPOI: {
POICode: job.area_of_damage.impact2,
},
},
TotalLossInd: job.tlos_ind,
},
},
},
ProfileInfo: {
//ProfileName: "Shop Standard Rates",
RateInfo: [
{
RateType: "PA",
RateDesc: "Parts Tax",
TaxInfo: {
TaxType: "LS",
TaxableInd: true,
TaxTierInfo: {
TierNum: 1,
Percentage: 0, //TODO Find the best place to take the tax rates for parts.
},
},
},
{
RateType: "LA",
RateDesc: "Labor Tax",
TaxInfo: {
TaxType: "LS",
TaxableInd: true,
TaxTierInfo: {
TierNum: 1,
Percentage: 0, //TODO Find the best place to take the tax rates for labor.
},
},
},
{
RateType: "LAB",
RateDesc: "Body Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lab,
},
},
{
RateType: "LAS",
RateDesc: "Structural Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_las,
},
},
{
RateType: "LAR",
RateDesc: "Refinish Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lar,
},
},
{
RateType: "LAG",
RateDesc: "Glass Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lag,
},
},
{
RateType: "LAF",
RateDesc: "Frame Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_laf,
},
},
{
RateType: "LAM",
RateDesc: "Mechancial Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lam,
},
},
{
RateType: "LAU",
RateDesc: "User Defined Labor",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_lau,
},
},
{
RateType: "MAPA",
RateDesc: "Paint Materials",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_mapa,
ThresholdAmt: 0,
},
MaterialCalcSettings: {
CalcMethodCode: 2,
CalcMaxAmt: 9999.99, //TODO Find threshold amts.
},
},
{
RateType: "MASH",
RateDesc: "Shop Materials",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_mash,
},
MaterialCalcSettings: {
CalcMethodCode: 4,
CalcMaxAmt: 9999.99, //TODO Find threshold amounts.
},
},
{
RateType: "MAHW",
RateDesc: "Hazardous Wastes Removal",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_mahw,
},
MaterialCalcSettings: {
//Todo Capture Calc Settings
CalcMethodCode: 2,
CalcMaxAmt: 10,
},
},
{
RateType: "MA2S",
RateDesc: "Two Stage Paint",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_ma2s,
},
MaterialCalcSettings: {
CalcMethodCode: 1,
CalcMaxAmt: 999999.99,
},
},
{
RateType: "MA2T",
RateDesc: "Two Tone Paint",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_ma2t,
},
MaterialCalcSettings: {
CalcMethodCode: 1,
CalcMaxAmt: 999999.99,
},
},
{
RateType: "MA3S",
RateDesc: "Three Stage Paint",
RateTierInfo: {
TierNum: 1,
Rate: job.rate_ma3s,
},
MaterialCalcSettings: {
CalcMethodCode: 1,
CalcMaxAmt: 999999.99,
},
},
],
},
//StorageDuration: 17,
RepairTotalsInfo: {
LaborTotalsInfo: [
{
TotalType: "LAB",
TotalTypeDesc: "Body Labor",
TotalHours: job.job_totals.rates.lab.hours,
TotalAmt: Dinero(job.job_totals.rates.lab.total).toFormat(
0.0
),
},
{
TotalType: "LAF",
TotalTypeDesc: "Frame Labor",
TotalHours: job.job_totals.rates.laf.hours,
TotalAmt: Dinero(job.job_totals.rates.laf.total).toFormat(
0.0
),
},
{
TotalType: "LAM",
TotalTypeDesc: "Mechanical Labor",
TotalHours: job.job_totals.rates.lam.hours,
TotalAmt: Dinero(job.job_totals.rates.lam.total).toFormat(
0.0
),
},
{
TotalType: "LAR",
TotalTypeDesc: "Refinish Labor",
TotalHours: job.job_totals.rates.lar.hours,
TotalAmt: Dinero(job.job_totals.rates.lar.total).toFormat(
0.0
),
},
],
PartsTotalsInfo: [
{
TotalType: "PAA",
TotalTypeDesc: "Aftermarket Parts",
TotalAmt: Dinero(
job.job_totals.parts.parts.list.paa &&
job.job_totals.parts.parts.list.paa.total
).toFormat("0.0"),
},
{
TotalType: "PAC",
TotalTypeDesc: "Re-Chromed Parts",
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pac &&
job.job_totals.parts.parts.list.pac.total
).toFormat("0.0"),
},
{
TotalType: "PAG",
TotalTypeDesc: "Glass Parts",
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pag &&
job.job_totals.parts.parts.list.pag.total
).toFormat("0.0"),
},
{
TotalType: "PAL",
TotalTypeDesc: "LKQ/Used Parts",
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pal &&
job.job_totals.parts.parts.list.pal.total
).toFormat("0.0"),
},
{
TotalType: "PAM",
TotalTypeDesc: "Remanufactured Parts",
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pam &&
job.job_totals.parts.parts.list.pam.total
).toFormat("0.0"),
},
{
TotalType: "PAN",
TotalTypeDesc: "New Parts",
TotalAmt: Dinero(
job.job_totals.parts.parts.list.pan &&
job.job_totals.parts.parts.list.pan.total
).toFormat("0.0"),
},
{
TotalType: "PAR",
TotalTypeDesc: "Recored Parts",
TotalAmt: Dinero(
job.job_totals.parts.parts.list.par &&
job.job_totals.parts.parts.list.par.total
).toFormat("0.0"),
},
],
OtherChargesTotalsInfo: [
{
TotalType: "OTSL",
TotalTypeDesc: "Sublet",
TotalAmt: Dinero(
job.job_totals.parts.sublets.total
).toFormat("0.0"),
},
{
TotalType: "MAPA",
TotalTypeDesc: "Paint Materials",
TotalAmt: Dinero(job.job_totals.rates.mapa.total).toFormat(
0.0
),
},
{
TotalType: "MASH",
TotalTypeDesc: "Shop Materials",
TotalAmt: Dinero(job.job_totals.rates.mash.total).toFormat(
0.0
),
},
// {
// TotalType: "MAHW",
// TotalTypeDesc: "Hazardous Wastes Removal",
// TotalAmt: Dinero(job.job_totals.rates.mahw.total).toFormat(
// 0.0
// ),
// },
{
TotalType: "OTST",
TotalTypeDesc: "Storage",
TotalAmt: Dinero(
job.job_totals.additional.storage
).toFormat("0.0"),
},
{
TotalType: "OTTW",
TotalTypeDesc: "Towing",
TotalAmt: Dinero(job.job_totals.additional.towing).toFormat(
0.0
),
},
{
TotalType: "OTAC",
TotalTypeDesc: "Additional Charges",
TotalAmt: Dinero(
job.job_totals.additional.additionalCosts
).toFormat("0.0"),
},
],
SummaryTotalsInfo: [
{
TotalType: "TOT",
TotalSubType: "T2",
TotalTypeDesc: "Net Total",
TotalAmt: Dinero(job.job_totals.totals.subtotal).toFormat(
0.0
),
},
{
TotalType: "TOT",
TotalSubType: "F7",
TotalTypeDesc: "Sales Tax",
TotalAmt: Dinero(job.job_totals.totals.state_tax).toFormat(
0.0
),
},
{
TotalType: "TOT",
TotalSubType: "GST",
TotalTypeDesc: "GST Tax",
TotalAmt: Dinero(
job.job_totals.totals.federal_tax
).toFormat("0.0"),
},
{
TotalType: "TOT",
TotalSubType: "TT",
TotalTypeDesc: "Gross Total",
TotalAmt: Dinero(
job.job_totals.totals.total_repairs
).toFormat("0.0"),
},
// {
// TotalType: "TOT",
// TotalSubType: "SM",
// TotalTypeDesc: "Supplement Total",
// TotalAmt: 0,
// },
{
TotalType: "TOT",
TotalSubType: "D2",
TotalTypeDesc: "Deductible",
TotalAmt: Dinero({
amount: Math.round((job.ded_amt || 0) * 100),
}).toFormat("0.0"),
},
{
TotalType: "TOT",
TotalSubType: "BTR",
TotalTypeDesc: "Betterment",
TotalAmt: Dinero(
job.job_totals.totals.custPayable.dep_taxes
).toFormat("0.0"),
},
{
TotalType: "TOT",
TotalSubType: "AA",
TotalTypeDesc: "Appearance Allowance",
TotalAmt: 0,
},
{
TotalType: "TOT",
TotalSubType: "D8",
TotalTypeDesc: "Bottom Line Discount",
TotalAmt: Dinero(
job.job_totals.additional.adjustments
).toFormat("0.0"),
},
{
TotalType: "TOT",
TotalSubType: "INS",
TotalTypeDesc: "Insurance Pay",
TotalAmt: Dinero(job.job_totals.totals.total_repairs)
.subtract(Dinero(job.job_totals.totals.custPayable.total))
.toFormat("0.0"),
},
// {
// TotalType: "TOT",
// TotalSubType: "DEPOSIT",
// TotalTypeDesc: "Deposit",
// TotalAmt: 0,
// },
{
TotalType: "TOT",
TotalSubType: "CUST",
TotalTypeDesc: "Customer Pay",
TotalAmt: Dinero(
job.job_totals.totals.custPayable.total
).toFormat("0.0"),
},
],
RepairTotalsType: 1,
},
// RepairLabor: {
// LaborAllocations: {
// LaborAllocation: [
// {
// LaborAllocationUUID:
// "426cce3a-efa7-44d9-b76e-50b9102c4198",
// LaborType: "LAB",
// Technician: {
// Employee: {
// PersonInfo: {
// PersonName: {
// FirstName: "Jose",
// LastName: "Gonzalez",
// },
// IDInfo: {
// IDQualifierCode: "US",
// IDNum: 2987,
// },
// },
// },
// },
// AllocatedHours: 3.5,
// },
// {
// LaborAllocationUUID:
// "426cce3a-efa7-44d9-b76e-50b9102c4199",
// LaborType: "LAR",
// Technician: {
// Employee: {
// PersonInfo: {
// PersonName: {
// FirstName: "Rcardo",
// LastName: "Himenez",
// },
// IDInfo: {
// IDQualifierCode: "US",
// IDNum: 2989,
// },
// },
// },
// },
// AllocatedHours: 5.5,
// },
// ],
// },
// },
ProductionStatus: {
ProductionStage: {
ProductionStageCode: 4,
ProductionStageDateTime: "2009-03-11T11:58:32.1898319-07:00",
ProductionStageStatusComment:
"Going to be painted this afternoon",
},
RepairStatus: {
RepairStatusCode: 2,
RepairStatusDateTime: "2009-03-11T11:58:32.1898319-07:00",
RepairStatusMemo: "Waiting on back ordered parts",
},
},
// RepairOrderNotes: {
// RepairOrderNote: {
// LineSequenceNum: 1,
// Note: "Revision Requested : approved--but needs est separated.8/22/2008 11:58:53 AM",
// CreateDateTime: "2008-08-22T11:58:53",
// AuthoredBy: {
// FirstName: {
// "#text": "Elizabeth/FirstName>",
// LastName: "Unis",
// },
// },
// RepairOrderNote: {
// LineSequenceNum: 2,
// Note: "Approved : 8/26/2008 12:21:08 PM",
// CreateDateTime: "2008-08-26T12:21:08",
// AuthoredBy: {
// FirstName: {
// "#text": "Elizabeth/FirstName>",
// LastName: "Unis",
// },
// },
// },
// },
// },
},
};
});
if (erroredJobs.length > 0) {
logger.log("arms-failed-jobs", "ERROR", "api", bodyshop.id, {
count: erroredJobs.length,
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number)),
});
}
logger.log("arms-end-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname,
});
res.json(ret);
} catch (error) {
//Error at the shop level.
logger.log("arms-error-shop", "ERROR", "api", bodyshop.id, {
error,
});
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
fatal: true,
errors: [error.toString()],
});
} finally {
allErrors.push({
bodyshopid: bodyshop.id,
imexshopid: bodyshop.imexshopid,
errors: erroredJobs,
});
}
}
res.sendStatus(200);
} catch (error) {
res.status(200).json(error);
}
};
function GetSupplementNumber(joblines) {
return _.max(joblines.map((jl) => jl.line_ind));
}
function GetDocumentstatus(job, bodyshop) {
switch (job.status) {
case bodyshop.md_ro_statuses.default_void:
return "V";
case bodyshop.md_ro_statuses.default_invoiced:
case bodyshop.md_ro_statuses.default_exported:
return "V";
default:
return "0";
}
}

View File

@@ -1 +1,2 @@
exports.autohouse = require("./autohouse").default;
exports.arms = require("./arms").default;

View File

@@ -20,6 +20,28 @@ mutation UNARCHIVE_CONVERSATION($id: uuid!) {
}
`;
exports.RECEIVE_MESSAGE = `
mutation RECEIVE_MESSAGE($msg: [messages_insert_input!]!) {
insert_messages(objects: $msg) {
returning {
conversation {
id
archived
bodyshop {
associations(where: {active: {_eq: true}}) {
user {
fcmtokens
}
}
}
}
}
}
}
`;
exports.INSERT_MESSAGE = `
mutation INSERT_MESSAGE($msg: [messages_insert_input!]!, $conversationid: uuid!) {
update_conversations_by_pk(pk_columns: {id: $conversationid}, _set: {archived: false}) {
@@ -531,6 +553,94 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
`;
exports.ENTEGRAL_EXPORT = `
query ENTEGRAL_EXPORT($bodyshopid: uuid!) {
jobs(where: {_and: [{converted: {_eq: true}}, {shopid: {_eq: $bodyshopid}}]}) {
joblines {
id
line_ind
}
id
ro_number
status
asgn_date
date_open
kmin
scheduled_completion
actual_completion
actual_delivery
date_exported
ins_co_nm
ins_addr1
ins_addr2
ins_city
ins_st
ins_zip
ins_ctry
ins_ph1
ins_ph2
est_ct_ln
est_ct_fn
insd_fn
insd_ln
ownr_fn
ownr_ln
ownr_co_nm
ownr_addr1
ownr_addr2
ownr_city
ownr_st
ownr_zip
ownr_ctry
ownr_ph1
ownr_ph2
ownr_ea
clm_ct_fn
clm_ct_ln
v_vin
plate_no
v_model_yr
v_make_desc
v_model_desc
v_color
driveable
clm_no
policy_no
loss_date
area_of_damage
tlos_ind
parts_tax_rates
federal_tax_rate
state_tax_rate
rate_la1
rate_la2
rate_la3
rate_la4
rate_laa
rate_lab
rate_lad
rate_lae
rate_laf
rate_lag
rate_lam
rate_lar
rate_las
rate_lau
rate_ma2s
rate_ma2t
rate_ma3s
rate_mabl
rate_macs
rate_mahw
rate_mapa
rate_mash
rate_matd
job_totals
ded_amt
}
}
`;
exports.UPDATE_JOB = `
mutation UPDATE_JOB($jobId: uuid!, $job: jobs_set_input!) {
update_jobs(where: { id: { _eq: $jobId } }, _set: $job) {
@@ -938,7 +1048,6 @@ exports.INSERT_IOEVENT = ` mutation INSERT_IOEVENT($event: ioevents_insert_input
exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS {
bodyshops(where: {autohouseid: {_is_null: false}}){
id
shopname
address1
@@ -956,6 +1065,26 @@ exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS {
}
}
`;
exports.GET_ENTEGRAL_SHOPS = `query GET_AUTOHOUSE_SHOPS {
bodyshops(where: {entegral_id: {_is_null: false}}){
id
shopname
address1
city
state
zip_post
country
phone
md_ro_statuses
md_order_statuses
entegral_id
md_responsibility_centers
imexshopid
}
}
`;
exports.DELETE_ALL_DMS_VEHICLES = `mutation DELETE_ALL_DMS_VEHICLES{
delete_dms_vehicles(where: {}) {
affected_rows
@@ -1087,9 +1216,17 @@ exports.GET_QBO_AUTH = `query GET_QBO_AUTH($email: String!) {
associations(where: {_and: {active: {_eq: true}, useremail: {_eq: $email}}}){
id
qbo_auth
qbo_realmId
}
}`;
exports.SET_QBO_AUTH_WITH_REALM = `mutation SET_QBO_AUTH($email: String!, $qbo_auth: jsonb!, $qbo_realmId: String) {
update_associations(_set: {qbo_auth: $qbo_auth, qbo_realmId: $qbo_realmId}, where: {_and: {active: {_eq: true}, useremail: {_eq: $email}}}){
affected_rows
}
}
`;
exports.SET_QBO_AUTH = `mutation SET_QBO_AUTH($email: String!, $qbo_auth: jsonb!) {
update_associations(_set: {qbo_auth: $qbo_auth}, where: {_and: {active: {_eq: true}, useremail: {_eq: $email}}}){
affected_rows

View File

@@ -270,7 +270,10 @@ function CalculatePartsTotals(jobLines) {
})
.multiply(value.part_qty || 0)
.add(
value.prt_dsmk_m && value.prt_dsmk_m !== 0
(value.db_ref === "900511" ||
value.db_ref === "900510") &&
value.prt_dsmk_m &&
value.prt_dsmk_m !== 0
? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) })
: Dinero({
amount: Math.round(value.act_price * 100),
@@ -295,7 +298,9 @@ function CalculatePartsTotals(jobLines) {
parts: {
...acc.parts,
prt_dsmk_total: acc.parts.prt_dsmk_total.add(
value.prt_dsmk_m && value.prt_dsmk_m !== 0
(value.db_ref === "900511" || value.db_ref === "900510") &&
value.prt_dsmk_m &&
value.prt_dsmk_m !== 0
? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) })
: Dinero({
amount: Math.round(value.act_price * 100),
@@ -339,7 +344,9 @@ function CalculatePartsTotals(jobLines) {
}).multiply(value.part_qty || 0)
)
.add(
value.prt_dsmk_m && value.prt_dsmk_m !== 0
(value.db_ref === "900511" || value.db_ref === "900510") &&
value.prt_dsmk_m &&
value.prt_dsmk_m !== 0
? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) })
: Dinero({
amount: Math.round(value.act_price * 100),

View File

@@ -26,7 +26,7 @@ exports.job = async (req, res) => {
const result = await client
.setHeaders({ Authorization: BearerToken })
.request(queries.QUERY_UPCOMING_APPOINTMENTS, {
now: new Date(),
now: moment().startOf("day"),
jobId: jobId,
});
@@ -42,6 +42,7 @@ exports.job = async (req, res) => {
const bucketMatrix = {};
const yesterday = moment().subtract(1, "day");
//Get latest date + add 5 days to allow for back end adding..
const totalMatrixDays = moment
.max([
...appointments.map((a) => moment(a.start)),
@@ -53,7 +54,7 @@ exports.job = async (req, res) => {
.diff(moment(), "days");
//Initialize the bucket matrix
for (i = 0; i < totalMatrixDays; i++) {
for (var i = 0; i < totalMatrixDays; i++) {
const theDate = moment().add(i, "days").format("yyyy-MM-DD");
//Only need to create a matrix for jobs of the same bucket.
bucketMatrix[theDate] = { in: 0, out: 0 };
@@ -87,7 +88,10 @@ exports.job = async (req, res) => {
} else {
//remove the date from the possible list.
const appDate = moment(appointment.start).format("yyyy-MM-DD");
delete bucketMatrix[appDate];
bucketMatrix[appDate] = {
...bucketMatrix[appDate],
blocked: true,
};
}
});
@@ -125,7 +129,9 @@ exports.job = async (req, res) => {
const possibleDates = [];
const bucketMatrixKeys = Object.keys(bucketMatrix);
bucketMatrixKeys.forEach((bmkey) => {
const isShopOpen = workingdays[dayOfWeekMapper(moment(bmkey).day())];
const isShopOpen =
workingdays[dayOfWeekMapper(moment(bmkey).day())] &&
!bucketMatrix[bmkey].blocked;
if (
JobBucket.target > bucketMatrix[bmkey].in - bucketMatrix[bmkey].out &&
@@ -134,10 +140,14 @@ exports.job = async (req, res) => {
possibleDates.push(new Date(bmkey).toISOString().substr(0, 10));
});
res.json(possibleDates);
if (possibleDates.length < 6) {
res.json(possibleDates);
} else {
res.json(possibleDates.slice(0, 5));
}
} catch (error) {
logger.log("smart-scheduling-error", "ERROR", req.user.email, jobId, {
error,
error: JSON.stringify(error),
});
res.status(400).send(error);
}

View File

@@ -11,7 +11,7 @@ const queries = require("../graphql-client/queries");
const { phone } = require("phone");
const admin = require("../firebase/firebase-handler").admin;
const logger = require("../utils/logger");
exports.receive = (req, res) => {
exports.receive = async (req, res) => {
//Perform request validation
logger.log("sms-inbound", "DEBUG", "api", null, {
@@ -36,114 +36,80 @@ exports.receive = (req, res) => {
res.status(400);
res.json({ success: false, error: "Malformed Request" });
} else {
client
.request(queries.FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID, {
mssid: req.body.MessagingServiceSid,
phone: phone(req.body.From).phoneNumber,
})
.then((response) => {
let newMessage = {
msid: req.body.SmsMessageSid,
text: req.body.Body,
image: !!req.body.MediaUrl0,
image_path: generateMediaArray(req.body),
};
if (response.bodyshops[0]) {
//Found a bodyshop - should always happen.
if (response.bodyshops[0].conversations.length === 0) {
//No conversation Found, create one.
console.log("[SMS Receive] No conversation found. Creating one.");
newMessage.conversation = {
data: {
bodyshopid: response.bodyshops[0].id,
phone_num: phone(req.body.From).phoneNumber,
},
};
} else if (response.bodyshops[0].conversations.length === 1) {
//Just add it to the conversation
console.log("[SMS Receive] Conversation found. Added ID.");
newMessage.conversationid =
response.bodyshops[0].conversations[0].id;
} else {
//We should never get here.
logger.log("sms-inbound-error", "ERROR", "api", null, {
msid: req.body.SmsMessageSid,
text: req.body.Body,
image: !!req.body.MediaUrl0,
image_path: generateMediaArray(req.body),
messagingServiceSid: req.body.MessagingServiceSid,
type: "duplicate-phone",
try {
const response = await client.request(
queries.FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID,
{
mssid: req.body.MessagingServiceSid,
phone: phone(req.body.From).phoneNumber,
}
);
let newMessage = {
msid: req.body.SmsMessageSid,
text: req.body.Body,
image: !!req.body.MediaUrl0,
image_path: generateMediaArray(req.body),
};
if (response.bodyshops[0]) {
//Found a bodyshop - should always happen.
if (response.bodyshops[0].conversations.length === 0) {
//No conversation Found, create one.
console.log("[SMS Receive] No conversation found. Creating one.");
newMessage.conversation = {
data: {
bodyshopid: response.bodyshops[0].id,
phone_num: phone(req.body.From).phoneNumber,
},
};
} else if (response.bodyshops[0].conversations.length === 1) {
//Just add it to the conversation
console.log("[SMS Receive] Conversation found. Added ID.");
newMessage.conversationid = response.bodyshops[0].conversations[0].id;
} else {
//We should never get here.
logger.log("sms-inbound-error", "ERROR", "api", null, {
msid: req.body.SmsMessageSid,
text: req.body.Body,
image: !!req.body.MediaUrl0,
image_path: generateMediaArray(req.body),
messagingServiceSid: req.body.MessagingServiceSid,
type: "duplicate-phone",
});
}
try {
const r2 = await client.request(queries.RECEIVE_MESSAGE, {
msg: newMessage,
});
if (response.bodyshops[0].conversations[0]) {
const r3 = await client.request(queries.INSERT_MESSAGE, {
id:
response.bodyshops[0].conversations[0] &&
response.bodyshops[0].conversations[0].id,
});
}
client
.request(queries.INSERT_MESSAGE, {
msg: newMessage,
conversationid: response.bodyshops[0].conversations[0].id,
})
.then((r2) => {
logger.log("sms-inbound-success", "DEBUG", "api", null, {
newMessage,
});
res.status(200).send("");
logger.log("sms-inbound-success", "DEBUG", "api", null, {
newMessage,
});
res.status(200).send("");
} catch (e2) {
logger.log("sms-inbound-error", "ERROR", "api", null, {
msid: req.body.SmsMessageSid,
text: req.body.Body,
image: !!req.body.MediaUrl0,
image_path: generateMediaArray(req.body),
messagingServiceSid: req.body.MessagingServiceSid,
error: e2,
});
const arrayOfAllUserFcmTokens =
r2.insert_messages.returning[0].conversation.bodyshop.associations.map(
(a) => a.user.fcmtokens
);
const allTokens = [];
arrayOfAllUserFcmTokens.map((i) =>
Object.keys(i).map((k) => allTokens.push(k))
);
const uniqueTokens = [...new Set(allTokens)];
var message = {
notification: {
title: `SMS - ${phone(req.body.From).phoneNumber}`,
body: req.body.Body,
click_action: "TEST CLICK ACTION",
},
data: {
jobid: "1234",
title: `New SMS From ${phone(req.body.From).phoneNumber}`,
body: req.body.Body,
},
tokens: uniqueTokens,
};
// Send a message to the device corresponding to the provided
// registration token.
// admin
// .messaging()
// .sendMulticast(message)
// .then((response) => {
// // Response is a message ID string.
// console.log(
// "[SMS Receive] Successfully sent FCM Broadcast.:"
// //JSON.stringify(response)
// );
// })
// .catch((error) => {
// console.log("Error sending message:", error);
// });
})
.catch((e2) => {
logger.log("sms-inbound-error", "ERROR", "api", null, {
msid: req.body.SmsMessageSid,
text: req.body.Body,
image: !!req.body.MediaUrl0,
image_path: generateMediaArray(req.body),
messagingServiceSid: req.body.MessagingServiceSid,
error: e2,
});
res.sendStatus(500).json(e2);
});
res.sendStatus(500).json(e2);
}
})
.catch((e1) => {
console.log("e1", e1);
res.sendStatus(500).json(e1);
});
}
} catch (e1) {
console.log("e1", e1);
res.sendStatus(500).json(e1);
}
}
};

View File

@@ -17,6 +17,7 @@ const CdkCalculateAllocations =
require("../cdk/cdk-calculate-allocations").default;
const { isArray } = require("lodash");
const logger = require("../utils/logger");
const PbsExportJob = require("../accounting/pbs/pbs-job-export");
io.use(function (socket, next) {
try {
@@ -57,6 +58,7 @@ io.on("connection", (socket) => {
});
});
///CDK
socket.on("cdk-export-job", (jobid) => {
CdkJobExport(socket, jobid);
});
@@ -94,6 +96,13 @@ io.on("connection", (socket) => {
callback(allocations);
});
//END CDK
//PBS
socket.on("pbs-export-job", (jobid) => {
PbsExportJob(socket, jobid);
});
//End PBS
socket.on("disconnect", () => {
createLogEvent(socket, "DEBUG", `User disconnected.`);

170
yarn.lock
View File

@@ -10,9 +10,9 @@
"@babel/highlight" "^7.10.4"
"@babel/helper-validator-identifier@^7.14.5":
version "7.14.9"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48"
integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==
version "7.15.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==
"@babel/highlight@^7.10.4":
version "7.14.5"
@@ -489,9 +489,9 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4:
uri-js "^4.2.2"
ajv@^8.0.1:
version "8.6.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.2.tgz#2fb45e0e5fcbc0813326c1c3da535d1881bb0571"
integrity sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==
version "8.6.3"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764"
integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
@@ -508,6 +508,11 @@ ansi-regex@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@@ -595,10 +600,10 @@ atob@2.1.2:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
aws-sdk@^2.1006.0:
version "2.1006.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1006.0.tgz#fc2f7e267d19a6297f732e19449461bb944682af"
integrity sha512-lwXAy706+1HVQqMnHaahdeBZZbdu6TWrtTY0ydeG0qanwldTFNMLczwnETTZWYsqNAU+wjl1VzmFdMO4gePLNQ==
aws-sdk@^2.1013.0:
version "2.1013.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1013.0.tgz#85babc473b0bc90cc1160eb48baf616ddb86e346"
integrity sha512-TXxkp/meAdofpC15goFpNuur7fvh/mcMRfHJoP1jYzTtD0wcoB4FK16GLcny0uDYgkQgZuiO9QYv3Rq5bhGCqQ==
dependencies:
buffer "4.9.2"
events "1.1.1"
@@ -634,15 +639,22 @@ axios@^0.21.1:
dependencies:
follow-redirects "^1.10.0"
axios@^0.21.4:
version "0.21.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
dependencies:
follow-redirects "^1.14.0"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-arraybuffer@0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812"
integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=
base64-arraybuffer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz#87bd13525626db4a9838e00a508c2b73efcf348c"
integrity sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==
base64-js@^1.0.2, base64-js@^1.3.0:
version "1.5.1"
@@ -1326,25 +1338,28 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
dependencies:
once "^1.4.0"
engine.io-parser@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-4.0.3.tgz#83d3a17acfd4226f19e721bb22a1ee8f7662d2f6"
integrity sha512-xEAAY0msNnESNPc00e19y5heTPX4y/TJ36gr8t1voOaNmTojP9b3oK3BbJLFufW2XFPQaaijpFewm2g2Um3uqA==
engine.io-parser@~5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.1.tgz#6695fc0f1e6d76ad4a48300ff80db5f6b3654939"
integrity sha512-j4p3WwJrG2k92VISM0op7wiq60vO92MlF3CRGxhKHy9ywG1/Dkc72g0dXeDQ+//hrcDn8gqQzoEkdO9FN0d9AA==
dependencies:
base64-arraybuffer "0.1.4"
base64-arraybuffer "~1.0.1"
engine.io@~5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-5.2.0.tgz#554cdd0230d89de7b1a49a809d7ee5a129d36809"
integrity sha512-d1DexkQx87IFr1FLuV+0f5kAm1Hk1uOVijLOb+D1sDO2QMb7YjE02VHtZtxo7xIXMgcWLb+vl3HRT0rI9tr4jQ==
engine.io@~6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.0.0.tgz#2b993fcd73e6b3a6abb52b40b803651cd5747cf0"
integrity sha512-Ui7yl3JajEIaACg8MOUwWvuuwU7jepZqX3BKs1ho7NQRuP4LhN4XIykXhp8bEy+x/DhA0LBZZXYSCkZDqrwMMg==
dependencies:
"@types/cookie" "^0.4.1"
"@types/cors" "^2.8.12"
"@types/node" ">=10.0.0"
accepts "~1.3.4"
base64id "2.0.0"
cookie "~0.4.1"
cors "~2.8.5"
debug "~4.3.1"
engine.io-parser "~4.0.0"
ws "~7.4.2"
engine.io-parser "~5.0.0"
ws "~8.2.3"
enquirer@^2.3.5:
version "2.3.6"
@@ -1723,6 +1738,11 @@ follow-redirects@^1.10.0:
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.3.tgz#6ada78118d8d24caee595595accdc0ac6abd022e"
integrity sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw==
follow-redirects@^1.14.0:
version "1.14.4"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379"
integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -1944,10 +1964,10 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
graphql-request@^3.4.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.5.0.tgz#7e69574e15875fb3f660a4b4be3996ecd0bbc8b7"
integrity sha512-Io89QpfU4rqiMbqM/KwMBzKaDLOppi8FU8sEccCE4JqCgz95W9Q8bvxQ4NfPALLSMvg9nafgg8AkYRmgKSlukA==
graphql-request@^3.6.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.6.1.tgz#689cce1da990131b40b05651f9f32bff506a1d8e"
integrity sha512-Nm1EasrAQVZllyNTlHDLnLZjlhC6eRWnWP6KH//ytnAL08pjlLkdI2K+s6OV92p45hn5b/kUlLbDwACmRoLwrQ==
dependencies:
cross-fetch "^3.0.6"
extract-files "^9.0.0"
@@ -2218,13 +2238,20 @@ is-fullwidth-code-point@^3.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
is-glob@^4.0.0, is-glob@^4.0.1:
is-glob@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
dependencies:
is-extglob "^2.1.1"
is-glob@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-obj@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
@@ -3514,18 +3541,15 @@ socket.io-parser@~4.0.4:
component-emitter "~1.3.0"
debug "~4.3.1"
socket.io@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.2.0.tgz#9e1c09d3ea647e24963a2e7ba8ea5c847778e2ed"
integrity sha512-sjlGfMmnaWvTRVxGRGWyhd9ctpg4APxWAxu85O/SxekkxHhfxmePWZbaYCkeX5QQX0z1YEnKOlNt6w82E4Nzug==
socket.io@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.3.1.tgz#c0aa14f3f916a8ab713e83a5bd20c16600245763"
integrity sha512-HC5w5Olv2XZ0XJ4gOLGzzHEuOCfj3G0SmoW3jLHYYh34EVsIr3EkW9h6kgfW+K3TFEcmYy8JcPWe//KUkBp5jA==
dependencies:
"@types/cookie" "^0.4.1"
"@types/cors" "^2.8.12"
"@types/node" ">=10.0.0"
accepts "~1.3.4"
base64id "~2.0.0"
debug "~4.3.2"
engine.io "~5.2.0"
engine.io "~6.0.0"
socket.io-adapter "~2.3.2"
socket.io-parser "~4.0.4"
@@ -3594,19 +3618,19 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
ssh2-sftp-client@^7.0.4:
version "7.0.4"
resolved "https://registry.yarnpkg.com/ssh2-sftp-client/-/ssh2-sftp-client-7.0.4.tgz#1e3182fa6a123f5d65429b2f62a76f1e9780907d"
integrity sha512-4fFSTgoYlzcAtGfEjiXN6N41s1jSUmPlI00f7uD7pQOjt9yK9susminINKTRvPp35dkrATrlNZVhUxNCt3z5+w==
ssh2-sftp-client@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/ssh2-sftp-client/-/ssh2-sftp-client-7.1.0.tgz#1a717b6818dfcae6c7c4eb1b0fd8580dd701502b"
integrity sha512-RyeBnutDAbIwmQrGO+MafKuXHkg2F6AMrdZtB7fbQdGm2c8AhPEY6hMwc41DKJlNtDcQCr2vaZlrBriu6xC5PA==
dependencies:
concat-stream "^2.0.0"
promise-retry "^2.0.1"
ssh2 "^1.4.0"
ssh2 "^1.5.0"
ssh2@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.4.0.tgz#e32e8343394364c922bad915a5a7fecd67d0f5c5"
integrity sha512-XvXwcXKvS452DyQvCa6Ct+chpucwc/UyxgliYz+rWXJ3jDHdtBb9xgmxJdMmnIn5bpgGAEV3KaEsH98ZGPHqwg==
ssh2@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.5.0.tgz#4dc559ba98a1cbb420e8d42998dfe35d0eda92bc"
integrity sha512-iUmRkhH9KGeszQwDW7YyyqjsMTf4z+0o48Cp4xOwlY5LjtbIAvyd3fwnsoUZW/hXmTCRA3yt7S/Jb9uVjErVlA==
dependencies:
asn1 "^0.2.4"
bcrypt-pbkdf "^1.0.2"
@@ -3670,6 +3694,15 @@ string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -3701,6 +3734,13 @@ strip-ansi@^6.0.0:
dependencies:
ansi-regex "^5.0.0"
strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-bom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@@ -3711,10 +3751,10 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
stripe@^8.181.0:
version "8.181.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.181.0.tgz#e49514b10c54d146cc5306204410658c2d689386"
integrity sha512-yEKT+/a/5OMYqGPhI/4jy/kiKHKeew1n9BvNtHbOA9lQDM8yVIYDx0nbQMj5rMowivAMqY67mejDJeSBlcPASA==
stripe@^8.184.0:
version "8.184.0"
resolved "https://registry.yarnpkg.com/stripe/-/stripe-8.184.0.tgz#ea68470ca6045bb47516c4fea3b775fd6ce15a36"
integrity sha512-ZUdvyX+sizTxXLEbUjgTShrulSWSkMIt7hIKdAkhnajYrHdzVtdmhBJl8sQbR9chMVox3Ig5ohilyeIrvcCE2g==
dependencies:
"@types/node" ">=8.1.0"
qs "^6.6.0"
@@ -3780,16 +3820,16 @@ supports-color@^8.1.0:
has-flag "^4.0.0"
table@^6.0.9:
version "6.7.1"
resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2"
integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==
version "6.7.2"
resolved "https://registry.yarnpkg.com/table/-/table-6.7.2.tgz#a8d39b9f5966693ca8b0feba270a78722cbaf3b0"
integrity sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g==
dependencies:
ajv "^8.0.1"
lodash.clonedeep "^4.5.0"
lodash.truncate "^4.4.2"
slice-ansi "^4.0.0"
string-width "^4.2.0"
strip-ansi "^6.0.0"
string-width "^4.2.3"
strip-ansi "^6.0.1"
teeny-request@^7.0.0:
version "7.1.1"
@@ -3878,12 +3918,12 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
twilio@^3.69.0:
version "3.69.0"
resolved "https://registry.yarnpkg.com/twilio/-/twilio-3.69.0.tgz#641d1c16f647f5f091e253345e7983c7306bffdd"
integrity sha512-mm330UFTlFh6GyLZUPVSLO0uVCigW7JdX/wyyV3VuBJ4Z8ie/aNmgztd3xWQr6RBB98gCwJ+UtumqIfixVUm8A==
twilio@^3.70.0:
version "3.70.0"
resolved "https://registry.yarnpkg.com/twilio/-/twilio-3.70.0.tgz#9e6a96f0c46e8256d74bce918baeb7e678c4c1aa"
integrity sha512-GhohvQfP3aHEwiCb6MWqDJ/KeSyFmFwCQtoSuHEwevE7GCxCq6spK36HlCNg3UyTTZNvfdIhN9Sf1wDWeDIbOg==
dependencies:
axios "^0.21.1"
axios "^0.21.4"
dayjs "^1.8.29"
https-proxy-agent "^5.0.0"
jsonwebtoken "^8.5.1"
@@ -4115,10 +4155,10 @@ write-file-atomic@^3.0.0:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
ws@~7.4.2:
version "7.4.6"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
ws@~8.2.3:
version "8.2.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
xdg-basedir@^4.0.0:
version "4.0.0"