diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 7e79e45eb..67dd1b80b 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -7550,6 +7550,32 @@ dms + + cdk + + + payers + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + cdk_dealerid false diff --git a/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx b/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx index 79ba67527..142ca8671 100644 --- a/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx +++ b/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx @@ -1,17 +1,21 @@ -import { Button, Table } from "antd"; -import React, { useState } from "react"; +import { Button, Table, Typography } from "antd"; +import React, { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; import Dinero from "dinero.js"; +import { SyncOutlined } from "@ant-design/icons"; + const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser bodyshop: selectBodyshop, }); + const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) }); + export default connect( mapStateToProps, mapDispatchToProps @@ -20,6 +24,15 @@ export default connect( export function DmsAllocationsSummary({ socket, bodyshop, jobId }) { const { t } = useTranslation(); const [allocationsSummary, setAllocationsSummary] = useState([]); + + useEffect(() => { + if (socket.connected) { + socket.emit("cdk-calculate-allocations", jobId, (ack) => + setAllocationsSummary(ack) + ); + } + }, [socket, socket.connected, jobId]); + const columns = [ { title: t("jobs.fields.dms.center"), @@ -71,13 +84,45 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId }) { ); }} > - Get + )} pagination={{ position: "top", defaultPageSize: 50 }} columns={columns} rowKey="center" dataSource={allocationsSummary} + summary={() => { + const totals = allocationsSummary.reduce( + (acc, val) => { + return { + totalSale: acc.totalSale.add(Dinero(val.sale)), + totalCost: acc.totalCost.add(Dinero(val.cost)), + }; + }, + { + totalSale: Dinero(), + totalCost: Dinero(), + } + ); + + return ( + + + + {t("general.labels.totals")} + + + + {totals.totalSale.toFormat()} + + + {totals.totalCost.toFormat()} + + + + + ); + }} /> ); } diff --git a/client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx b/client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx index c972178b1..89ad19179 100644 --- a/client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx +++ b/client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx @@ -4,6 +4,9 @@ import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { useTranslation } from "react-i18next"; +import { useLazyQuery } from "@apollo/client"; +import { SEARCH_DMS_VEHICLES } from "../../graphql/dms.queries"; +import AlertComponent from "../alert/alert.component"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser @@ -12,83 +15,75 @@ const mapStateToProps = createStructuredSelector({ const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) }); -export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkMakes); +export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkVehicles); -export function DmsCdkMakes({ bodyshop, form, socket }) { - const [makesList, setMakesList] = useState([]); - const [searchText, setSearchText] = useState(""); - const [loading, setLoading] = useState(false); +export function DmsCdkVehicles({ bodyshop, form, socket, job }) { const [visible, setVisible] = useState(false); const [selectedModel, setSelectedModel] = useState(null); const { t } = useTranslation(); + + const [callSearch, { loading, error, data }] = + useLazyQuery(SEARCH_DMS_VEHICLES); const columns = [ { - title: t("jobs.fields.dms.makeFullName"), - dataIndex: "makeFullName", - key: "makeFullName", + title: t("jobs.fields.dms.make"), + dataIndex: "make", + key: "make", }, { - title: t("jobs.fields.dms.modelFullName"), - dataIndex: "modelFullName", - key: "modelFullName", + title: t("jobs.fields.dms.model"), + dataIndex: "model", + key: "model", }, { - title: t("jobs.fields.dms.makeCode"), - dataIndex: "makeCode", - key: "makeCode", + title: t("jobs.fields.dms.makecode"), + dataIndex: "makecode", + key: "makecode", }, { - title: t("jobs.fields.dms.modelCode"), - dataIndex: "modelCode", - key: "modelCode", + title: t("jobs.fields.dms.modelcode"), + dataIndex: "modelcode", + key: "modelcode", }, ]; - const filteredMakes = - searchText !== "" && searchText - ? makesList.filter( - (make) => - searchText - .split(" ") - .some((v) => - make.makeFullName.toLowerCase().includes(v.toLowerCase()) - ) || - searchText - .split(" ") - .some((v) => - make.modelFullName.toLowerCase().includes(v.toLowerCase()) - ) - ) - : makesList; - + console.log( + "🚀 ~ file: dms-cdk-makes.component.jsx ~ line 95 ~ selectedModel", + selectedModel + ); return (
- setVisible(false)}> + setVisible(false)} + onOk={() => { + form.setFieldsValue({ + dms_make: selectedModel.makecode, + dms_model: selectedModel.modelcode, + }); + setVisible(false); + }} + > + {error && } ( setSearchText(val)} + onSearch={(val) => callSearch({ variables: { search: val } })} placeholder={t("general.labels.search")} /> )} columns={columns} loading={loading} - id="id" - dataSource={filteredMakes} + rowKey="id" + dataSource={data ? data.search_dms_vehicles : []} onRow={(record) => { return { - onClick: setSelectedModel(record), + onClick: () => setSelectedModel(record), }; }} rowSelection={{ - onSelect: (record, selected, ...props) => { - console.log( - "🚀 ~ file: dms-cdk-makes.component.jsx ~ line 85 ~ record, selected, ...props", - record, - selected, - ...props - ); - + onSelect: (record) => { setSelectedModel(record); }, @@ -100,15 +95,14 @@ export function DmsCdkMakes({ bodyshop, form, socket }) { ); diff --git a/client/src/components/dms-cdk-makes/dms-cdk-makes.refetch.component.jsx b/client/src/components/dms-cdk-makes/dms-cdk-makes.refetch.component.jsx new file mode 100644 index 000000000..a154e819d --- /dev/null +++ b/client/src/components/dms-cdk-makes/dms-cdk-makes.refetch.component.jsx @@ -0,0 +1,32 @@ +import { Button } from "antd"; +import axios from "axios"; +import React, { useState } from "react"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +const mapStateToProps = createStructuredSelector({ + //currentUser: selectCurrentUser + bodyshop: selectBodyshop, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkMakesRefetch); + +export function DmsCdkMakesRefetch({ bodyshop, form, socket }) { + const [loading, setLoading] = useState(false); + const handleRefetch = async () => { + setLoading(true); + const response = await axios.post("/cdk/getvehicles", { + cdk_dealerid: bodyshop.cdk_dealerid, + bodyshopid: bodyshop.id, + }); + console.log(response); + setLoading(false); + }; + return ( + + ); +} diff --git a/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx b/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx index b85d1ff38..46be3d6d3 100644 --- a/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx +++ b/client/src/components/dms-customer-selector/dms-customer-selector.component.jsx @@ -1,10 +1,24 @@ import { Button, Table } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; import { socket } from "../../pages/dms/dms.container"; -import PhoneFormatter from "../../utils/PhoneFormatter"; +import { selectBodyshop } from "../../redux/user/user.selectors"; import { alphaSort } from "../../utils/sorters"; -export default function DmsCustomerSelector() { + +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect( + mapStateToProps, + mapDispatchToProps +)(DmsCustomerSelector); + +export function DmsCustomerSelector({ bodyshop }) { const { t } = useTranslation(); const [customerList, setcustomerList] = useState([]); const [visible, setVisible] = useState(false); @@ -15,11 +29,24 @@ export default function DmsCustomerSelector() { setcustomerList(customerList); }); - const onOk = () => { + const onUseSelected = () => { setVisible(false); socket.emit("cdk-selected-customer", selectedCustomer); }; + const onUseGeneric = () => { + setVisible(false); + socket.emit( + "cdk-selected-customer", + bodyshop.cdk_configuration.generic_customer_number + ); + }; + + const onCreateNew = () => { + setVisible(false); + socket.emit("cdk-selected-customer", null); + }; + const columns = [ { title: t("dms.fields.name1"), @@ -27,28 +54,13 @@ export default function DmsCustomerSelector() { key: "name1", sorter: (a, b) => alphaSort(a.name1?.fullName, b.name1?.fullName), }, - { - title: t("dms.fields.name2"), - dataIndex: ["name2", "fullName"], - key: "name2", - sorter: (a, b) => alphaSort(a.name2?.fullName, b.name2?.fullName), - }, - { - title: t("dms.fields.phone"), - dataIndex: ["contactInfo", "mainTelephoneNumber", "value"], - key: "phone", - render: (record, value) => ( - - {record.contactInfo?.mainTelephoneNumber?.value} - - ), - }, + { title: t("dms.fields.address"), //dataIndex: ["name2", "fullName"], key: "address", render: (record, value) => - `${record.address?.addressLine[0]}, ${record.address?.city} ${record.address?.stateOrProvince} ${record.address?.postalCode}`, + `${record?.address?.addressLine[0]}, ${record.address?.city} ${record.address?.stateOrProvince} ${record.address?.postalCode}`, }, ]; @@ -57,7 +69,23 @@ export default function DmsCustomerSelector() {
(
- + + +
)} pagination={{ position: "top" }} diff --git a/client/src/components/dms-post-form/dms-post-form.component.jsx b/client/src/components/dms-post-form/dms-post-form.component.jsx index 8bef25c4b..783f4749c 100644 --- a/client/src/components/dms-post-form/dms-post-form.component.jsx +++ b/client/src/components/dms-post-form/dms-post-form.component.jsx @@ -1,5 +1,13 @@ import { DeleteFilled } from "@ant-design/icons"; -import { Button, Form, Input } from "antd"; +import { + Button, + Form, + Input, + InputNumber, + Select, + Space, + Statistic, +} from "antd"; import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; @@ -8,7 +16,9 @@ import { selectBodyshop } from "../../redux/user/user.selectors"; import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component"; - +import Dinero from "dinero.js"; +import { determineDmsType } from "../../pages/dms/dms.container"; +import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, }); @@ -17,11 +27,38 @@ const mapDispatchToProps = (dispatch) => ({ }); export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm); -export function DmsPostForm({ bodyshop, socket, jobId }) { +export function DmsPostForm({ bodyshop, socket, job }) { const [form] = Form.useForm(); const { t } = useTranslation(); + + const handlePayerSelect = (value, index) => { + form.setFieldsValue({ + payers: form.getFieldValue("payers").map((payer, mapIndex) => { + if (index !== mapIndex) return payer; + const cdkPayer = + bodyshop.cdk_configuration.payers && + bodyshop.cdk_configuration.payers.find((i) => i.name === value); + + if (!cdkPayer) return payer; + + return { + ...cdkPayer, + dms_acctnumber: cdkPayer.dms_acctnumber, + controlnumber: job && job[cdkPayer.control_type], + }; + }), + }); + }; + + const handleFinish = (values) => { + socket.emit(`${determineDmsType(bodyshop)}-export-job`, { + jobid: job.id, + txEnvelope: values, + }); + }; + return ( - + - + + + + + + + - + - + + + + + @@ -81,20 +156,30 @@ export function DmsPostForm({ bodyshop, socket, jobId }) { }, ]} > - + - + - + + + + {() => { + const payers = form.getFieldValue("payers"); + + const row = payers && payers[index]; + + const cdkPayer = + bodyshop.cdk_configuration.payers && + bodyshop.cdk_configuration.payers.find( + (i) => i && row && i.name === row.name + ); + + return ( +
+ {cdkPayer && + t(`jobs.fields.${cdkPayer.control_type}`)} +
+ ); + }} +
+ { remove(field.name); @@ -133,18 +240,69 @@ export function DmsPostForm({ bodyshop, socket, jobId }) { ); }}
+ + {() => { + //Perform Calculation to determine discrepancy. + let totalAllocated = Dinero(); + + const payers = form.getFieldValue("payers"); + payers && + payers.forEach((payer) => { + totalAllocated = totalAllocated.add( + Dinero({ amount: Math.round((payer?.amount || 0) * 100) }) + ); + }); + const discrep = Dinero(job.job_totals.totals.total_repairs).subtract( + totalAllocated + ); + return ( + + + + + + + + ); + }} + ); } diff --git a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx index 37a9213fa..dc9bd066b 100644 --- a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx +++ b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx @@ -91,6 +91,93 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { > + + + + + + + {(fields, { add, remove }) => { + return ( +
+ {fields.map((field, index) => ( + + + + + + + + + + + + + { + remove(field.name); + }} + /> + + + ))} + + + +
+ ); + }} +
)} @@ -1335,926 +1422,802 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { )} - {!bodyshop.cdk_dealerid && ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - )} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { document.title = t("titles.dms"); @@ -73,7 +73,6 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) { console.log("Connected again."); }); socket.on("reconnect", () => { - console.log("Connected again."); setLogs((logs) => { return [ ...logs, @@ -104,46 +103,39 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) { if (!jobId || !bodyshop.cdk_dealerid) return ; - const dmsType = determineDmsType(bodyshop); - - // if (loading) return ; - // if (error) return ; + if (loading) return ; + if (error) return ; return (
- - - - -
+ {data && data.jobs_by_pk.ro_number} - + + + + +
@@ -155,7 +147,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) { ); } -const determineDmsType = (bodyshop) => { +export const determineDmsType = (bodyshop) => { if (bodyshop.cdk_dealerid) return "cdk"; else { return "pbs"; diff --git a/client/src/pages/jobs-close/jobs-close.component.jsx b/client/src/pages/jobs-close/jobs-close.component.jsx index bf57b554a..49b3d8e27 100644 --- a/client/src/pages/jobs-close/jobs-close.component.jsx +++ b/client/src/pages/jobs-close/jobs-close.component.jsx @@ -8,6 +8,7 @@ import { Alert, Divider, PageHeader, + InputNumber, } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -56,6 +57,8 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { actual_in: values.actual_in, actual_completion: values.actual_completion, actual_delivery: values.actual_delivery, + kmin: values.kmin, + kmout: values.kmout, }, }, refetchQueries: ["QUERY_JOB_CLOSE_DETAILS"], @@ -112,6 +115,8 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { actual_delivery: job.actual_delivery ? moment(job.actual_delivery) : job.scheduled_delivery && moment(job.scheduled_delivery), + kmin: job.kmin, + kmout: job.kmout, }} scrollToFirstError > @@ -203,6 +208,45 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { > + {bodyshop.cdk_dealerid && ( + + + + )} + {bodyshop.cdk_dealerid && ( + ({ + validator(_, value) { + if (!value || getFieldValue("kmin") <= value) { + return Promise.resolve(); + } + + return Promise.reject( + new Error(t("jobs.labels.dms.kmoutnotgreaterthankmin")) + ); + }, + }), + ]} + > + + + )} diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 2d24eb84d..c64713946 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -474,6 +474,9 @@ "defaultprofitsmapping": "Default Profits Mapping", "deliverchecklist": "Delivery Checklist", "dms": { + "cdk": { + "payers": "CDK Payers" + }, "cdk_dealerid": "CDK Dealer ID", "title": "DMS" }, diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index e519774c2..e539f407c 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -474,6 +474,9 @@ "defaultprofitsmapping": "", "deliverchecklist": "", "dms": { + "cdk": { + "payers": "" + }, "cdk_dealerid": "", "title": "" }, diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 170f304e5..30fd9a5d9 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -474,6 +474,9 @@ "defaultprofitsmapping": "", "deliverchecklist": "", "dms": { + "cdk": { + "payers": "" + }, "cdk_dealerid": "", "title": "" }, diff --git a/hasura/migrations/1629520416914_create_table_public_dms_vehicles/down.yaml b/hasura/migrations/1629520416914_create_table_public_dms_vehicles/down.yaml new file mode 100644 index 000000000..e8a69a7f3 --- /dev/null +++ b/hasura/migrations/1629520416914_create_table_public_dms_vehicles/down.yaml @@ -0,0 +1,5 @@ +- args: + cascade: false + read_only: false + sql: DROP TABLE "public"."dms_vehicles"; + type: run_sql diff --git a/hasura/migrations/1629520416914_create_table_public_dms_vehicles/up.yaml b/hasura/migrations/1629520416914_create_table_public_dms_vehicles/up.yaml new file mode 100644 index 000000000..eadd2690a --- /dev/null +++ b/hasura/migrations/1629520416914_create_table_public_dms_vehicles/up.yaml @@ -0,0 +1,18 @@ +- args: + cascade: false + read_only: false + sql: CREATE EXTENSION IF NOT EXISTS pgcrypto; + type: run_sql +- args: + cascade: false + read_only: false + sql: CREATE TABLE "public"."dms_vehicles"("id" uuid NOT NULL DEFAULT gen_random_uuid(), + "created_at" timestamptz NOT NULL DEFAULT now(), "makecode" text NOT NULL, "modelcode" + text NOT NULL, "make" text NOT NULL, "model" text NOT NULL, "bodyshopid" uuid + NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") + ON UPDATE cascade ON DELETE cascade); + type: run_sql +- args: + name: dms_vehicles + schema: public + type: add_existing_table_or_view diff --git a/hasura/migrations/1629520454038_track_all_relationships/down.yaml b/hasura/migrations/1629520454038_track_all_relationships/down.yaml new file mode 100644 index 000000000..d745605a5 --- /dev/null +++ b/hasura/migrations/1629520454038_track_all_relationships/down.yaml @@ -0,0 +1,12 @@ +- args: + relationship: dms_vehicles + table: + name: bodyshops + schema: public + type: drop_relationship +- args: + relationship: bodyshop + table: + name: dms_vehicles + schema: public + type: drop_relationship diff --git a/hasura/migrations/1629520454038_track_all_relationships/up.yaml b/hasura/migrations/1629520454038_track_all_relationships/up.yaml new file mode 100644 index 000000000..a6ec79cc2 --- /dev/null +++ b/hasura/migrations/1629520454038_track_all_relationships/up.yaml @@ -0,0 +1,20 @@ +- args: + name: dms_vehicles + table: + name: bodyshops + schema: public + using: + foreign_key_constraint_on: + column: bodyshopid + table: + name: dms_vehicles + schema: public + type: create_array_relationship +- args: + name: bodyshop + table: + name: dms_vehicles + schema: public + using: + foreign_key_constraint_on: bodyshopid + type: create_object_relationship diff --git a/hasura/migrations/1629520509486_update_permission_user_public_table_dms_vehicles/down.yaml b/hasura/migrations/1629520509486_update_permission_user_public_table_dms_vehicles/down.yaml new file mode 100644 index 000000000..972d7b719 --- /dev/null +++ b/hasura/migrations/1629520509486_update_permission_user_public_table_dms_vehicles/down.yaml @@ -0,0 +1,6 @@ +- args: + role: user + table: + name: dms_vehicles + schema: public + type: drop_select_permission diff --git a/hasura/migrations/1629520509486_update_permission_user_public_table_dms_vehicles/up.yaml b/hasura/migrations/1629520509486_update_permission_user_public_table_dms_vehicles/up.yaml new file mode 100644 index 000000000..892fb145f --- /dev/null +++ b/hasura/migrations/1629520509486_update_permission_user_public_table_dms_vehicles/up.yaml @@ -0,0 +1,28 @@ +- args: + permission: + allow_aggregations: false + backend_only: false + columns: + - id + - created_at + - makecode + - modelcode + - make + - model + - bodyshopid + computed_fields: [] + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + limit: null + role: user + table: + name: dms_vehicles + schema: public + type: create_select_permission diff --git a/hasura/migrations/1629520623978_update_permission_user_public_table_dms_vehicles/down.yaml b/hasura/migrations/1629520623978_update_permission_user_public_table_dms_vehicles/down.yaml new file mode 100644 index 000000000..1d905252b --- /dev/null +++ b/hasura/migrations/1629520623978_update_permission_user_public_table_dms_vehicles/down.yaml @@ -0,0 +1,6 @@ +- args: + role: user + table: + name: dms_vehicles + schema: public + type: drop_insert_permission diff --git a/hasura/migrations/1629520623978_update_permission_user_public_table_dms_vehicles/up.yaml b/hasura/migrations/1629520623978_update_permission_user_public_table_dms_vehicles/up.yaml new file mode 100644 index 000000000..13cb03c98 --- /dev/null +++ b/hasura/migrations/1629520623978_update_permission_user_public_table_dms_vehicles/up.yaml @@ -0,0 +1,27 @@ +- args: + permission: + allow_upsert: true + backend_only: false + check: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + columns: + - id + - created_at + - makecode + - modelcode + - make + - model + - bodyshopid + set: {} + role: user + table: + name: dms_vehicles + schema: public + type: create_insert_permission diff --git a/hasura/migrations/1629520632595_update_permission_user_public_table_dms_vehicles/down.yaml b/hasura/migrations/1629520632595_update_permission_user_public_table_dms_vehicles/down.yaml new file mode 100644 index 000000000..45e52f5d8 --- /dev/null +++ b/hasura/migrations/1629520632595_update_permission_user_public_table_dms_vehicles/down.yaml @@ -0,0 +1,6 @@ +- args: + role: user + table: + name: dms_vehicles + schema: public + type: drop_update_permission diff --git a/hasura/migrations/1629520632595_update_permission_user_public_table_dms_vehicles/up.yaml b/hasura/migrations/1629520632595_update_permission_user_public_table_dms_vehicles/up.yaml new file mode 100644 index 000000000..71a547c0e --- /dev/null +++ b/hasura/migrations/1629520632595_update_permission_user_public_table_dms_vehicles/up.yaml @@ -0,0 +1,26 @@ +- args: + permission: + backend_only: false + columns: + - make + - makecode + - model + - modelcode + - created_at + - bodyshopid + - id + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + set: {} + role: user + table: + name: dms_vehicles + schema: public + type: create_update_permission diff --git a/hasura/migrations/1629521860924_update_permission_user_public_table_dms_vehicles/down.yaml b/hasura/migrations/1629521860924_update_permission_user_public_table_dms_vehicles/down.yaml new file mode 100644 index 000000000..a4ff6bf77 --- /dev/null +++ b/hasura/migrations/1629521860924_update_permission_user_public_table_dms_vehicles/down.yaml @@ -0,0 +1,6 @@ +- args: + role: user + table: + name: dms_vehicles + schema: public + type: drop_delete_permission diff --git a/hasura/migrations/1629521860924_update_permission_user_public_table_dms_vehicles/up.yaml b/hasura/migrations/1629521860924_update_permission_user_public_table_dms_vehicles/up.yaml new file mode 100644 index 000000000..f1ee13adf --- /dev/null +++ b/hasura/migrations/1629521860924_update_permission_user_public_table_dms_vehicles/up.yaml @@ -0,0 +1,17 @@ +- args: + permission: + backend_only: false + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + role: user + table: + name: dms_vehicles + schema: public + type: create_delete_permission diff --git a/hasura/migrations/1629738990845_run_sql_migration/down.yaml b/hasura/migrations/1629738990845_run_sql_migration/down.yaml new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/hasura/migrations/1629738990845_run_sql_migration/down.yaml @@ -0,0 +1 @@ +[] diff --git a/hasura/migrations/1629738990845_run_sql_migration/up.yaml b/hasura/migrations/1629738990845_run_sql_migration/up.yaml new file mode 100644 index 000000000..75c3b0f55 --- /dev/null +++ b/hasura/migrations/1629738990845_run_sql_migration/up.yaml @@ -0,0 +1,13 @@ +- args: + cascade: true + read_only: false + sql: CREATE OR REPLACE FUNCTION public.search_dms_vehicles(search text)RETURNS + SETOF dms_vehicles LANGUAGE plpgsql STABLE AS $FUNCTION$ BEGIN IF search='' + THEN RETURN query SELECT*FROM dms_vehicles;ELSE RETURN query SELECT*FROM dms_vehicles + WHERE make ILIKE'%'||search||'%' OR model ILIKE'%'||search||'%' ORDER BY make + ILIKE'%'||search||'%' OR NULL,model ILIKE'%'||search||'%' OR NULL;END IF;END$FUNCTION$; + type: run_sql +- args: + name: search_dms_vehicles + schema: public + type: track_function diff --git a/hasura/migrations/metadata.yaml b/hasura/migrations/metadata.yaml index 6b280e543..943808d52 100644 --- a/hasura/migrations/metadata.yaml +++ b/hasura/migrations/metadata.yaml @@ -721,6 +721,13 @@ tables: table: schema: public name: csiquestions + - name: dms_vehicles + using: + foreign_key_constraint_on: + column: bodyshopid + table: + schema: public + name: dms_vehicles - name: documents using: foreign_key_constraint_on: @@ -1527,6 +1534,87 @@ tables: - active: _eq: true check: null +- table: + schema: public + name: dms_vehicles + object_relationships: + - name: bodyshop + using: + foreign_key_constraint_on: bodyshopid + insert_permissions: + - role: user + permission: + check: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + columns: + - id + - created_at + - makecode + - modelcode + - make + - model + - bodyshopid + backend_only: false + select_permissions: + - role: user + permission: + columns: + - id + - created_at + - makecode + - modelcode + - make + - model + - bodyshopid + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + update_permissions: + - role: user + permission: + columns: + - make + - makecode + - model + - modelcode + - created_at + - bodyshopid + - id + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true + check: null + delete_permissions: + - role: user + permission: + filter: + bodyshop: + associations: + _and: + - user: + authid: + _eq: X-Hasura-User-Id + - active: + _eq: true - table: schema: public name: documents @@ -4554,6 +4642,9 @@ functions: - function: schema: public name: search_cccontracts +- function: + schema: public + name: search_dms_vehicles - function: schema: public name: search_exportlog diff --git a/server.js b/server.js index 4b81464eb..e3beaa86f 100644 --- a/server.js +++ b/server.js @@ -21,6 +21,23 @@ const app = express(); const port = process.env.PORT || 5000; //const port = 5000; +const http = require("http"); +const server = http.createServer(app); +const { Server } = require("socket.io"); +const io = new Server(server, { + path: "/ws", + cors: { + origin: [ + "https://test.imex.online", + "http://localhost:3000", + "https://imex.online", + ], + methods: ["GET", "POST"], + }, +}); +exports.io = io; +require("./server/web-sockets/web-socket"); + //app.use(fb.validateFirebaseIdToken); app.use(compression()); app.use(bodyParser.json({ limit: "50mb" })); @@ -136,25 +153,13 @@ app.post("/data/ah", data.autohouse); var ioevent = require("./server/ioevent/ioevent"); app.post("/ioevent", ioevent.default); +var cdkGetMake = require("./server/cdk/cdk-get-makes"); +app.post("/cdk/getvehicles", fb.validateFirebaseIdToken, cdkGetMake.default); + app.get("/", async function (req, res) { res.status(200).send("Access Forbidden."); }); -const http = require("http"); -const server = http.createServer(app); -const { Server } = require("socket.io"); -const io = new Server(server, { - path: "/ws", - cors: { - origin: [ - "https://test.imex.online", - "http://localhost:3000", - "https://imex.online", - ], - methods: ["GET", "POST"], - }, -}); - server.listen(port, (error) => { if (error) throw error; logger.log( @@ -163,5 +168,3 @@ server.listen(port, (error) => { "api" ); }); -exports.io = io; -require("./server/web-sockets/web-socket"); diff --git a/server/cdk/cdk-get-makes.js b/server/cdk/cdk-get-makes.js index cdcb21eb8..da3343541 100644 --- a/server/cdk/cdk-get-makes.js +++ b/server/cdk/cdk-get-makes.js @@ -15,32 +15,101 @@ const Dinero = require("dinero.js"); const _ = require("lodash"); const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl"); const { performance } = require("perf_hooks"); +const apiGqlClient = require("../graphql-client/graphql-client").client; -exports.default = async function (socket, cdk_dealerid) { +// exports.default = async function (socket, cdk_dealerid) { +// try { +// CdkBase.createLogEvent( +// socket, +// "DEBUG", +// `Getting makes and models list from CDK.` +// ); +// return await GetCdkMakes(socket, cdk_dealerid); +// } catch (error) { +// CdkBase.createLogEvent( +// socket, +// "ERROR", +// `Error encountered in CdkGetMakes. ${error}` +// ); +// } +// }; + +exports.default = async function ReloadCdkMakes(req, res) { + const { bodyshopid, cdk_dealerid } = req.body; try { - CdkBase.createLogEvent( - socket, - "DEBUG", - `Getting makes and models list from CDK.` + const BearerToken = req.headers.authorization; + //Query all CDK Models + const newList = await GetCdkMakes(req, cdk_dealerid); + console.log("🚀 ~ file: cdk-get-makes.js ~ line 40 ~ newList", newList); + + //Clear out the existing records + const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { + headers: { + Authorization: BearerToken, + }, + }); + + const deleteResult = await client + .setHeaders({ Authorization: BearerToken }) + .request(queries.DELETE_ALL_DMS_VEHICLES, {}); + console.log( + "🚀 ~ file: cdk-get-makes.js ~ line 53 ~ deleteResult", + deleteResult + ); + + //Insert the new ones. + + const insertResult = await client + .setHeaders({ Authorization: BearerToken }) + .request(queries.INSERT_DMS_VEHICLES, { + vehicles: newList.map((i) => { + return { + bodyshopid, + makecode: i.makeCode, + modelcode: i.modelCode, + make: i.makeFullName, + model: i.modelFullName, + }; + }), + }); + console.log( + "🚀 ~ file: cdk-get-makes.js ~ line 66 ~ insertResult", + insertResult + ); + + logger.log( + "cdk-replace-makes-models-success", + "DEBUG", + req.user.email, + null, + { + cdk_dealerid, + count: newList.length, + } ); - return await GetCdkMakes(socket, cdk_dealerid); } catch (error) { - CdkBase.createLogEvent( - socket, + logger.log( + "cdk-replace-makes-models-error", "ERROR", - `Error encountered in CdkGetMakes. ${error}` + req.user.email, + null, + { + cdk_dealerid, + error, + } ); } }; -async function GetCdkMakes(socket, cdk_dealerid) { - CdkBase.createLogEvent(socket, "TRACE", `{1} Begin GetCDkMakes WSDL Call`); +async function GetCdkMakes(req, cdk_dealerid) { + logger.log("cdk-replace-makes-models", "DEBUG", req.user.email, null, { + cdk_dealerid, + }); try { const soapClientVehicleInsert = await soap.createClientAsync( CdkWsdl.VehicleInsert ); - const start = performance.now(); const soapResponseVehicleSearch = await soapClientVehicleInsert.getMakeModelAsync( @@ -51,28 +120,25 @@ async function GetCdkMakes(socket, cdk_dealerid) { {} ); - CheckCdkResponseForError(socket, soapResponseVehicleSearch); + + CheckCdkResponseForError(null, soapResponseVehicleSearch); const [ result, //rawResponse, soapheader, rawRequest ] = soapResponseVehicleSearch; - const end = performance.now(); - CdkBase.createLogEvent( - socket, - "TRACE", - `soapClientVehicleInsert.getMakeModelAsync Result Length ${ - result.return.length - } and took ${end - start}ms` + + return result.return; + } catch (error) { + logger.log( + "cdk-replace-makes-models-error", + "ERROR", + req.user.email, + null, + { + cdk_dealerid, + error, + } ); - return result.return.map((element, index) => { - return { id: index, ...element }; - }); - } catch (error) { - CdkBase.createLogEvent( - socket, - "ERROR", - `Error in GetCdkMakes - ${JSON.stringify(error, null, 2)}` - ); throw new Error(error); } } diff --git a/server/cdk/cdk-job-export.js b/server/cdk/cdk-job-export.js index e5aa5cdbf..b38ac0141 100644 --- a/server/cdk/cdk-job-export.js +++ b/server/cdk/cdk-job-export.js @@ -10,153 +10,72 @@ const soap = require("soap"); const queries = require("../graphql-client/queries"); const CdkBase = require("../web-sockets/web-socket"); const CdkWsdl = require("./cdk-wsdl").default; -const logger = require("../utils/logger"); const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl"); +const moment = require("moment"); -exports.default = async function (socket, jobid) { +exports.default = async function (socket, { txEnvelope, jobid }) { socket.logEvents = []; socket.recordid = jobid; + socket.txEnvelope = txEnvelope; try { CdkBase.createLogEvent( socket, "DEBUG", `Received Job export request for id ${jobid}` ); - //The following values will be stored on the socket to allow callbacks. - //let clVFV, clADPV, clADPC; + const JobData = await QueryJobData(socket, jobid); + socket.JobData = JobData; const DealerId = JobData.bodyshop.cdk_dealerid; CdkBase.createLogEvent( socket, - "TRACE", + "DEBUG", `Dealer ID detected: ${JSON.stringify(DealerId)}` ); - //{1} Begin Calculate DMS Vehicle Id - socket.clVFV = await CalculateDmsVid(socket, JobData); - if (socket.clVFV.newId === "Y") { - //{1.2} This is a new Vehicle ID + CdkBase.createLogEvent( + socket, + "DEBUG", + `{1} Begin Calculate DMS Vehicle ID using VIN: ${JobData.v_vin}` + ); + socket.DMSVid = await CalculateDmsVid(socket, JobData); + + if (socket.DMSVid.newId === "N") { CdkBase.createLogEvent( socket, "DEBUG", - `{1.2} clVFV DMSVid does *not* exist.` + `{2.1} Querying the Vehicle using the DMSVid: ${socket.DMSVid}` ); - CdkBase.createLogEvent( - socket, - "TRACE", - `{1.2} clVFV: ${JSON.stringify(socket.clVFV, null, 2)}` - ); - //Check if DMSCustId is Empty - which it should always be? - //{6.6} Should check to see if a customer exists so that we can marry it to the new vehicle. + socket.DMSVeh = await QueryDmsVehicleById(socket, JobData, socket.DMSVid); + + const DMSVehCustomer = + socket.DMSVeh && + socket.DMSVeh.owners.find((o) => o.id.assigningPartyId === "CURRENT"); + CdkBase.createLogEvent( socket, "DEBUG", - `{6.6} Trying to find customer ID in DMS.` + `{2.2} Querying the Customer using the ID from DMSVeh: ${DMSVehCustomer.id.value}` ); - - //Array - const strIDS = await FindCustomerIdFromDms(socket, JobData); - if (strIDS && strIDS.length > 0) { - CdkBase.createLogEvent( - socket, - "DEBUG", - `{8.2} ${strIDS.length} Customer ID(s) found.` - ); - CdkBase.createLogEvent( - socket, - "TRACE", - `{8.2} strIDS: ${JSON.stringify(strIDS, null, 2)}` - ); - if (strIDS.length > 1) { - //We have multiple IDs - //TODO: Do we need to let the person select it? - CdkBase.createLogEvent( - socket, - "WARNING", - `{F} Mutliple customer ids have been found (${strIDS.length})` - ); - CdkBase.createLogEvent( - socket, - "DEBUG", - `Asking for user intervention to select customer.` - ); - socket.emit("cdk-select-customer", strIDS); - - return; - } - } else { - CdkBase.createLogEvent( - socket, - "DEBUG", - `{8.5} Customer ID(s) *not* found.` - ); - - //Create a customer number, then use that to insert the customer record. - const newCustomerNumber = await GenerateCustomerNumberFromDms( - socket, - JobData - ); - CdkBase.createLogEvent( - socket, - "DEBUG", - `{10.1} New Customer number generated. newCustomerNumber: ${newCustomerNumber}` - ); - - //Use the new customer number to insert the customer record. - socket.clADPC = await CreateCustomerInDms( - socket, - JobData, - newCustomerNumber - ); - CdkBase.createLogEvent( - socket, - "DEBUG", - `{11.1} New Customer inserted.` - ); - CdkBase.createLogEvent( - socket, - "TRACE", - `{11.1} clADPC: ${JSON.stringify(socket.clADPC, null, 2)}` - ); - } - } else { - CdkBase.createLogEvent(socket, "DEBUG", `{1.1} clVFV DMSVid does exist.`); - CdkBase.createLogEvent( + socket.DMSVehCustomer = await QueryDmsCustomerById( socket, - "TRACE", - `{1.1} clVFV: ${JSON.stringify(socket.clVFV, null, 2)}` + JobData, + DMSVehCustomer.id.value ); - - //{2} Begin Find Vehicle in DMS - socket.clADPV = await FindVehicleInDms(socket, JobData, socket.clVFV); //TODO: Verify that this should always return a result. If an ID was found previously, it should be correct? - - //{2.2} Check if the vehicle was found in the DMS. - if (socket.clADPV.AppErrorNo === "0") { - //Vehicle was found. - CdkBase.createLogEvent( - socket, - "DEBUG", - `{1.4} Vehicle was found in the DMS.` - ); - CdkBase.createLogEvent( - socket, - "TRACE", - `{1.4} clADPV: ${JSON.stringify(socket.clADPV, null, 2)}` - ); - } else { - //Vehicle was not found. - CdkBase.createLogEvent( - socket, - "DEBUG", - `{6.4} Vehicle does not exist in DMS. Will have to create one.` - ); - CdkBase.createLogEvent( - socket, - "TRACE", - `{6.4} clVFV: ${JSON.stringify(socket.clVFV, null, 2)}` - ); - } } + + CdkBase.createLogEvent( + socket, + "DEBUG", + `{2.3} Querying the Customer using the name.` + ); + + socket.DMSCustList = await QueryDmsCustomerByName(socket, JobData); + + socket.emit("cdk-select-customer", [ + ...(socket.DMSVehCustomer ? [socket.DMSVehCustomer] : []), + ...socket.DMSCustList, + ]); } catch (error) { CdkBase.createLogEvent( socket, @@ -174,6 +93,79 @@ exports.default = async function (socket, jobid) { } }; +async function CdkSelectedCustomer(socket, selectedCustomerId) { + try { + if (selectedCustomerId) { + CdkBase.createLogEvent( + socket, + "DEBUG", + `{3.1} Querying the Customer using Customer ID: ${selectedCustomerId}` + ); + socket.DMSCust = await QueryDmsCustomerById(socket, selectedCustomerId); + } else { + CdkBase.createLogEvent( + socket, + "DEBUG", + `{3.2} Generating a new customer ID.` + ); + const newCustomerId = await GenerateDmsCustomerNumber(socket); + CdkBase.createLogEvent( + socket, + "DEBUG", + `{3.3} Inserting new customer with ID: ${newCustomerId}` + ); + socket.DMSCust = await InsertDmsCustomer(socket, newCustomerId); + } + + if (socket.DMSVid.newId === "Y") { + CdkBase.createLogEvent( + socket, + "DEBUG", + `{4.1} Inserting new vehicle with ID: ID ${socket.DMSVid.vehiclesVehId}` + ); + socket.DMSVeh = await InsertDmsVehicle(socket); + } else { + CdkBase.createLogEvent( + socket, + "DEBUG", + `{4.2} Querying Existing Vehicle using ID ${socket.DMSVid.vehiclesVehId}` + ); + socket.DMSVeh = await QueryDmsVehicleById( + socket, + socket.JobData, + socket.DMSVid + ); + CdkBase.createLogEvent( + socket, + "DEBUG", + `{4.3} Updating Existing Vehicle to associate to owner.` + ); + socket.DMSVeh = await UpdateDmsVehicle(socket); + } + CdkBase.createLogEvent( + socket, + "DEBUG", + `{5} **NOT DONE YET** Updating Service Vehicle History.` + ); + } catch (error) { + CdkBase.createLogEvent( + socket, + "ERROR", + `Error encountered in CdkSelectedCustomer. ${error}` + ); + } finally { + //Ensure we always insert logEvents + //GQL to insert logevents. + CdkBase.createLogEvent( + socket, + "DEBUG", + `Capturing log events to database.` + ); + } +} + +exports.CdkSelectedCustomer = CdkSelectedCustomer; + async function QueryJobData(socket, jobid) { CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`); const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {}); @@ -188,81 +180,255 @@ async function QueryJobData(socket, jobid) { return result.jobs_by_pk; } -async function CreateCustomerInDms(socket, JobData, newCustomerNumber) { - CdkBase.createLogEvent(socket, "DEBUG", `{11} Begin Create Customer in DMS`); - +async function CalculateDmsVid(socket, JobData) { try { - const soapClientCustomerInsertUpdate = await soap.createClientAsync( - CdkWsdl.CustomerInsertUpdate + const soapClientVehicleInsertUpdate = await soap.createClientAsync( + CdkWsdl.VehicleInsertUpdate ); - const soapResponseCustomerInsertUpdate = - await soapClientCustomerInsertUpdate.insertAsync( - { - arg0: CDK_CREDENTIALS, - arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. - arg2: { userId: null }, - arg3: { - //Copied the required fields from the other integration. - //TODO: Verify whether we need to bring more information in. - id: { value: newCustomerNumber }, - address: { - city: JobData.ownr_city, - country: null, - postalcode: JobData.ownr_zip, - stateOrProvince: JobData.ownr_st, - }, - contactInfo: { - mainTelephoneNumber: { main: true, value: JobData.ownr_ph1 }, - }, - demographics: null, - name1: { - companyname: null, - firstName: JobData.ownr_fn, - fullname: null, - lastName: JobData.ownr_ln, - middleName: null, - nameType: "Person", - suffix: null, - title: null, - }, - }, - }, + const soapResponseVehicleInsertUpdate = + await soapClientVehicleInsertUpdate.getVehIdsAsync({ + arg0: CDK_CREDENTIALS, + arg1: { id: JobData.bodyshop.cdk_dealerid }, + arg2: { VIN: JobData.v_vin }, + }); - {} - ); - CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); - const [ - result, //rawResponse, soapheader, rawRequest - ] = soapResponseCustomerInsertUpdate; + const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate; + CdkBase.createXmlEvent( + socket, + rawRequest, + `soapClientVehicleInsertUpdate.getVehIdsAsync request.` + ); + + CdkBase.createXmlEvent( + socket, + rawResponse, + `soapClientVehicleInsertUpdate.getVehIdsAsync response.` + ); CdkBase.createLogEvent( socket, "TRACE", - `soapClientCustomerInsertUpdate.insertAsync Result ${JSON.stringify( + `soapClientVehicleInsertUpdate.getVehIdsAsync Result ${JSON.stringify( result, null, 2 )}` ); - const customer = result && result.return; - return customer; + CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); + return result && result.return && result.return[0]; } catch (error) { + CdkBase.createXmlEvent( + socket, + error.request, + `soapClientVehicleInsertUpdate.getVehIdsAsync request.`, + true + ); + + CdkBase.createXmlEvent( + socket, + error.response && error.response.data, + `soapClientVehicleInsertUpdate.getVehIdsAsync response.`, + true + ); CdkBase.createLogEvent( socket, "ERROR", - `Error in CreateCustomerInDms - ${JSON.stringify(error, null, 2)}` + `{1} Error in CalculateDmsVid - ${error}` ); throw new Error(error); } } -async function GenerateCustomerNumberFromDms(socket, JobData) { +async function QueryDmsVehicleById(socket, JobData, DMSVid) { + try { + const soapClientVehicleInsertUpdate = await soap.createClientAsync( + CdkWsdl.VehicleInsertUpdate + ); + + const soapResponseVehicleInsertUpdate = + await soapClientVehicleInsertUpdate.readAsync({ + arg0: CDK_CREDENTIALS, + arg1: { id: JobData.bodyshop.cdk_dealerid }, + arg2: { + fileType: "VEHICLES", + vehiclesVehicleId: DMSVid.vehiclesVehId, + }, + }); + + const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate; + + CdkBase.createXmlEvent( + socket, + rawRequest, + `soapClientVehicleInsertUpdate.readAsync request.` + ); + + CdkBase.createLogEvent( + socket, + "TRACE", + `soapClientVehicleInsertUpdate.readAsync Result ${JSON.stringify( + result, + null, + 2 + )}` + ); + CdkBase.createXmlEvent( + socket, + rawResponse, + `soapClientVehicleInsertUpdate.readAsync response.` + ); + CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); + const VehicleFromDMS = result && result.return && result.return.vehicle; + return VehicleFromDMS; + } catch (error) { + CdkBase.createLogEvent( + socket, + "ERROR", + `Error in QueryDmsVehicleById - ${error}` + ); + throw new Error(error); + } +} + +async function QueryDmsCustomerById(socket, JobData, CustomerId) { + try { + const soapClientCustomerInsertUpdate = await soap.createClientAsync( + CdkWsdl.CustomerInsertUpdate + ); + const soapResponseCustomerInsertUpdate = + await soapClientCustomerInsertUpdate.readAsync({ + arg0: CDK_CREDENTIALS, + arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. + arg2: { + // userId: CustomerId, + }, + arg3: CustomerId, + }); + + const [result, rawResponse, , rawRequest] = + soapResponseCustomerInsertUpdate; + + CdkBase.createXmlEvent( + socket, + rawRequest, + `soapClientCustomerInsertUpdate.readAsync request.` + ); + + CdkBase.createXmlEvent( + socket, + rawResponse, + `soapClientCustomerInsertUpdate.readAsync response.` + ); + CdkBase.createLogEvent( + socket, + "TRACE", + `soapClientCustomerInsertUpdate.readAsync Result ${JSON.stringify( + result, + null, + 2 + )}` + ); + CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); + const CustomersFromDms = + result && result.return && result.return.customerParty; + return CustomersFromDms; + } catch (error) { + CdkBase.createXmlEvent( + socket, + error.request, + `soapClientCustomerInsertUpdate.readAsync request.`, + true + ); + + CdkBase.createXmlEvent( + socket, + error.response && error.response.data, + `soapClientCustomerInsertUpdate.readAsync response.`, + true + ); + + CdkBase.createLogEvent( + socket, + "ERROR", + `Error in QueryDmsCustomerById - ${error}` + ); + throw new Error(error); + } +} + +async function QueryDmsCustomerByName(socket, JobData) { + const ownerName = `${JobData.ownr_ln},${JobData.ownr_fn}`; CdkBase.createLogEvent( socket, "DEBUG", - `{10} Begin Generate Customer Number from DMS` + `Begin Query DMS Customer by Name using: ${ownerName}` ); + try { + const soapClientCustomerSearch = await soap.createClientAsync( + CdkWsdl.CustomerSearch + ); + const soapResponseCustomerSearch = + await soapClientCustomerSearch.executeSearchAsync({ + arg0: CDK_CREDENTIALS, + arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. + arg2: { + verb: "EXACT", + key: ownerName, + }, + }); + + const [result, rawResponse, , rawRequest] = soapResponseCustomerSearch; + + CdkBase.createXmlEvent( + socket, + rawRequest, + `soapClientCustomerSearch.executeSearchBulkAsync request.` + ); + + CdkBase.createXmlEvent( + socket, + rawResponse, + `soapClientCustomerSearch.executeSearchBulkAsync response.` + ); + + CdkBase.createLogEvent( + socket, + "TRACE", + `soapClientCustomerSearch.executeSearchBulkAsync Result ${JSON.stringify( + result, + null, + 2 + )}` + ); + CheckCdkResponseForError(socket, soapResponseCustomerSearch); + const CustomersFromDms = (result && result.return) || []; + return CustomersFromDms; + } catch (error) { + CdkBase.createXmlEvent( + socket, + error.request, + `soapClientCustomerSearch.executeSearchBulkAsync request.`, + true + ); + + CdkBase.createXmlEvent( + socket, + error.response && error.response.data, + `soapClientCustomerSearch.executeSearchBulkAsync response.`, + true + ); + + CdkBase.createLogEvent( + socket, + "ERROR", + `Error in QueryDmsCustomerByName - ${error}` + ); + throw new Error(error); + } +} + +async function GenerateDmsCustomerNumber(socket) { try { const soapClientCustomerInsertUpdate = await soap.createClientAsync( CdkWsdl.CustomerInsertUpdate @@ -271,16 +437,27 @@ async function GenerateCustomerNumberFromDms(socket, JobData) { await soapClientCustomerInsertUpdate.getCustomerNumberAsync( { arg0: CDK_CREDENTIALS, - arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. + arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. arg2: { userId: null }, }, {} ); - CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); - const [ - result, //rawResponse, soapheader, rawRequest - ] = soapResponseCustomerInsertUpdate; + + const [result, rawResponse, , rawRequest] = + soapResponseCustomerInsertUpdate; + + CdkBase.createXmlEvent( + socket, + rawRequest, + `soapClientCustomerInsertUpdate.getCustomerNumberAsync request.` + ); + + CdkBase.createXmlEvent( + socket, + rawResponse, + `soapClientCustomerInsertUpdate.getCustomerNumberAsync response.` + ); CdkBase.createLogEvent( socket, @@ -291,163 +468,320 @@ async function GenerateCustomerNumberFromDms(socket, JobData) { 2 )}` ); + CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); const customerNumber = result && result.return && result.return.customerNumber; return customerNumber; } catch (error) { + CdkBase.createXmlEvent( + socket, + error.request, + `soapClientCustomerInsertUpdate.getCustomerNumberAsync request.`, + true + ); + + CdkBase.createXmlEvent( + socket, + error.response && error.response.data, + `soapClientCustomerInsertUpdate.getCustomerNumberAsync response.`, + true + ); CdkBase.createLogEvent( socket, "ERROR", - `Error in GenerateCustomerNumberFromDms - ${JSON.stringify( - error, - null, - 2 - )}` + `Error in GenerateDmsCustomerNumber - ${error}` ); throw new Error(error); } } -async function FindCustomerIdFromDms(socket, JobData) { - const ownerName = `${JobData.ownr_ln},${JobData.ownr_fn}`; - CdkBase.createLogEvent( - socket, - "DEBUG", - `{8} Begin Read Customer from DMS using OWNER NAME: ${ownerName}` - ); - +async function InsertDmsCustomer(socket, newCustomerNumber) { try { - const soapClientCustomerSearch = await soap.createClientAsync( - CdkWsdl.CustomerSearch + const soapClientCustomerInsertUpdate = await soap.createClientAsync( + CdkWsdl.CustomerInsertUpdate ); - const soapResponseCustomerSearch = - await soapClientCustomerSearch.executeSearchAsync( + const soapResponseCustomerInsertUpdate = + await soapClientCustomerInsertUpdate.insertAsync( { arg0: CDK_CREDENTIALS, - arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. - arg2: { - verb: "EXACT", - key: ownerName, + arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. + arg2: { userId: null }, + arg3: { + //Copied the required fields from the other integration. + //TODO: Verify whether we need to bring more information in. + id: { value: newCustomerNumber }, + address: { + addressLine: socket.JobData.ownr_addr1, + city: socket.JobData.ownr_city, + country: null, + postalCode: socket.JobData.ownr_zip, + stateOrProvince: socket.JobData.ownr_st, + }, + contactInfo: { + mainTelephoneNumber: { + main: true, + value: socket.JobData.ownr_ph1, + }, + }, + demographics: null, + name1: { + companyname: socket.JobData.ownr_co_nm, + firstName: socket.JobData.ownr_fn, + fullname: null, + lastName: socket.JobData.ownr_ln, + middleName: null, + nameType: "Person", + suffix: null, + title: null, + }, }, }, {} ); - CheckCdkResponseForError(socket, soapResponseCustomerSearch); - const [ - result, // rawResponse, soapheader, rawRequest - ] = soapResponseCustomerSearch; + + const [result, rawResponse, , rawRequest] = + soapResponseCustomerInsertUpdate; + CdkBase.createXmlEvent( + socket, + rawRequest, + `soapClientCustomerInsertUpdate.insertAsync request.` + ); + + CdkBase.createXmlEvent( + socket, + rawResponse, + `soapClientCustomerInsertUpdate.insertAsync response.` + ); CdkBase.createLogEvent( socket, "TRACE", - `soapClientCustomerSearch.executeSearchBulkAsync Result ${JSON.stringify( + `soapClientCustomerInsertUpdate.insertAsync Result ${JSON.stringify( result, null, 2 )}` ); - const CustomersFromDms = result && result.return; - return CustomersFromDms; + CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate); + const customer = result && result.return && result.return.customerParty; + return customer; } catch (error) { + CdkBase.createXmlEvent( + socket, + error.request, + `soapClientCustomerInsertUpdate.insertAsync request.`, + true + ); + + CdkBase.createXmlEvent( + socket, + error.response && error.response.data, + `soapClientCustomerInsertUpdate.insertAsync response.`, + true + ); CdkBase.createLogEvent( socket, "ERROR", - `Error in FindCustomerIdFromDms - ${JSON.stringify(error, null, 2)}` + `Error in InsertDmsCustomer - ${error}` ); throw new Error(error); } } -async function FindVehicleInDms(socket, JobData, clVFV) { - CdkBase.createLogEvent( - socket, - "DEBUG", - `{2}/{6} Begin Find Vehicle In DMS using clVFV: ${clVFV}` - ); - +async function InsertDmsVehicle(socket) { try { const soapClientVehicleInsertUpdate = await soap.createClientAsync( CdkWsdl.VehicleInsertUpdate ); + const soapResponseVehicleInsertUpdate = - await soapClientVehicleInsertUpdate.readBulkAsync( - { - arg0: CDK_CREDENTIALS, - arg1: { id: JobData.bodyshop.cdk_dealerid }, - arg2: { - fileType: "VEHICLES", - vehiclesVehicleId: clVFV.vehiclesVehId, + await soapClientVehicleInsertUpdate.insertAsync({ + arg0: CDK_CREDENTIALS, + arg1: { id: socket.JobData.bodyshop.cdk_dealerid }, + arg2: { + dealer: { + dealerNumber: socket.JobData.bodyshop.cdk_dealerid, + inServiceDate: moment().startOf("day").toISOString(), + vehicleId: socket.DMSVid.vehiclesVehId, + }, + manufacturer: {}, + vehicle: { + deliveryDate: moment().format("YYYYMMDD"), + make: socket.txEnvelope.dms_make, + modelAbrev: socket.txEnvelope.dms_model, + modelYear: socket.JobData.v_model_yr, + odometerStatus: socket.txEnvelope.kmout, + saleClassValue: "MISC", + VIN: socket.JobData.v_vin, + }, + owners: { + id: { + assigningPartyId: "CURRENT", + value: socket.DMSCust.id.value, + }, }, }, + arg3: "VEHICLES", + }); + + const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate; + + CdkBase.createXmlEvent( + socket, + rawRequest, + `soapClientVehicleInsertUpdate.insertAsync request.` + ); - {} - ); - CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); - const [ - result, //rawResponse, soapheader, rawRequest - ] = soapResponseVehicleInsertUpdate; CdkBase.createLogEvent( socket, "TRACE", - `soapClientVehicleInsertUpdate.readBulkAsync Result ${JSON.stringify( + `soapClientVehicleInsertUpdate.insertAsync Result ${JSON.stringify( result, null, 2 )}` ); - const VehicleFromDMS = result && result.return && result.return[0]; + CdkBase.createXmlEvent( + socket, + rawResponse, + `soapClientVehicleInsertUpdate.insertAsync response.` + ); + CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); + const VehicleFromDMS = result && result.return && result.return.vehicle; return VehicleFromDMS; } catch (error) { CdkBase.createLogEvent( socket, "ERROR", - `Error in FindVehicleInDms - ${JSON.stringify(error, null, 2)}` + `Error in InsertDmsVehicle - ${error}` ); throw new Error(error); } } -async function CalculateDmsVid(socket, JobData) { - CdkBase.createLogEvent( - socket, - "TRACE", - `{1} Begin Calculate DMS Vehicle ID using VIN: ${JobData.v_vin}` - ); - +async function UpdateDmsVehicle(socket) { try { const soapClientVehicleInsertUpdate = await soap.createClientAsync( CdkWsdl.VehicleInsertUpdate ); - const soapResponseVehicleInsertUpdate = - await soapClientVehicleInsertUpdate.getVehIdsAsync( - { - arg0: CDK_CREDENTIALS, - arg1: { id: JobData.bodyshop.cdk_dealerid }, - arg2: { VIN: JobData.v_vin }, - }, - {} - ); - CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); - const [ - result, //rawResponse, soapheader, rawRequest - ] = soapResponseVehicleInsertUpdate; + const soapResponseVehicleInsertUpdate = + await soapClientVehicleInsertUpdate.updateAsync({ + arg0: CDK_CREDENTIALS, + arg1: { id: socket.JobData.bodyshop.cdk_dealerid }, + arg2: { + ...socket.DMSVeh, + dealer: { + ...socket.DMSVeh.dealer, + inServiceDate: moment( + socket.DMSVeh.dealer.inServiceDate + ).toISOString(), + }, + vehicle: { + ...socket.DMSVeh.vehicle, + deliveryDate: moment( + socket.DMSVeh.vehicle.deliveryDate + ).toISOString(), + }, + owners: { + id: { + assigningPartyId: "CURRENT", + value: socket.DMSCust.id.value, + }, + }, + }, + arg3: "VEHICLES", + }); + const [result, rawResponse, , rawRequest] = soapResponseVehicleInsertUpdate; + + CdkBase.createXmlEvent( + socket, + rawRequest, + `soapClientVehicleInsertUpdate.updateAsync request.` + ); + CdkBase.createLogEvent( socket, "TRACE", - `soapClientVehicleInsertUpdate.searchIDsByVINAsync Result ${JSON.stringify( + `soapClientVehicleInsertUpdate.updateAsync Result ${JSON.stringify( result, null, 2 )}` ); - const DmsVehicle = result && result.return && result.return[0]; - return DmsVehicle; + CdkBase.createXmlEvent( + socket, + rawResponse, + `soapClientVehicleInsertUpdate.updateAsync response.` + ); + CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate); + const VehicleFromDMS = result && result.return && result.return.vehicle; + return VehicleFromDMS; } catch (error) { CdkBase.createLogEvent( socket, "ERROR", - `Error in CalculateDmsVid - ${JSON.stringify(error, null, 2)}` + `Error in UpdateDmsVehicle - ${error}` + ); + throw new Error(error); + } +} + +async function InsertDmsStartWip(socket) { + try { + const soapClientAccountingGLInsertUpdate = await soap.createClientAsync( + CdkWsdl.AccountingGLInsertUpdate + ); + + const soapResponseAccountingGLInsertUpdate = + await soapClientAccountingGLInsertUpdate.startWIPAsync({ + arg0: CDK_CREDENTIALS, + arg1: { id: socket.JobData.bodyshop.cdk_dealerid }, + arg2: { + acctgDate: moment().toISOString(), + desc: socket.txEnvelope.story, + docType: 7 || 10, //Need to check what this usually would be? + //1 Cash Receipt , 2 Check, 3 Journal Voucher, 4 Parts invoice, 5 Payable Invoice, 6 Recurring Entry, 7 Repair Order Invoice, 8 Vehicle Purchase Invoice, 9 Vehicle Sale Invoice, 10 Other, 11 Payroll, 12 Finance Charge, 13 FMLR Invoice, 14 Parts Credit Memo, 15 Manufacturer Document, 16 FMLR Credit Memo + m13Flag: 0, + refer: socket.JobData.ro_number, + srcCo: socket.txEnvelope.journal, + srcJrnl: socket.txEnvelope.journal, + userID: "?", //Where is this coming from? + //userName: "IMEX", + }, + }); + + const [result, rawResponse, , rawRequest] = + soapResponseAccountingGLInsertUpdate; + + CdkBase.createXmlEvent( + socket, + rawRequest, + `soapClientAccountingGLInsertUpdate.startWIPAsync request.` + ); + + CdkBase.createLogEvent( + socket, + "TRACE", + `soapClientAccountingGLInsertUpdate.startWIPAsync Result ${JSON.stringify( + result, + null, + 2 + )}` + ); + CdkBase.createXmlEvent( + socket, + rawResponse, + `soapClientAccountingGLInsertUpdate.startWIPAsync response.` + ); + CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate); + const VehicleFromDMS = result && result.return && result.return.vehicle; + return VehicleFromDMS; + } catch (error) { + CdkBase.createLogEvent( + socket, + "ERROR", + `Error in QueryDmsVehicleById - ${error}` ); throw new Error(error); } diff --git a/server/cdk/cdk-wsdl.js b/server/cdk/cdk-wsdl.js index e020e3d89..8f0b1d3a6 100644 --- a/server/cdk/cdk-wsdl.js +++ b/server/cdk/cdk-wsdl.js @@ -78,11 +78,11 @@ exports.checkIndividualResult = checkIndividualResult; const cdkDomain = "https://uat-3pa.dmotorworks.com"; exports.default = { // VehicleSearch: `${cdkDomain}/pip-vehicle/services/VehicleSearch?wsdl`, + AccountingGLInsertUpdate: `${cdkDomain}/pip-accounting-gl/services/AccountingGLInsertUpdate?wsdl`, VehicleInsertUpdate: `${cdkDomain}/pip-vehicle/services/VehicleInsertUpdate?wsdl`, CustomerInsertUpdate: `${cdkDomain}/pip-customer/services/CustomerInsertUpdate?wsdl`, CustomerSearch: `${cdkDomain}/pip-customer/services/CustomerSearch?wsdl`, VehicleSearch: `${cdkDomain}/pip-vehicle/services/VehicleSearch?wsdl`, - VehicleInsert: `${cdkDomain}/pip-vehicle/services/VehicleInsertUpdate?wsdl`, }; // The following login credentials will be used for all PIPs and all environments (User Acceptance Testing and Production). diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 557f00dfa..10f5f545b 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -133,6 +133,7 @@ query QUERY_JOBS_FOR_CDK_EXPORT($id: uuid!) { ownr_fn ownr_addr1 ownr_addr2 + ownr_ph1 ownr_zip ownr_city ownr_st @@ -932,6 +933,19 @@ exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS { imexshopid } } +`; +exports.DELETE_ALL_DMS_VEHICLES = `mutation DELETE_ALL_DMS_VEHICLES{ + delete_dms_vehicles(where: {}) { + affected_rows + } +} +`; +exports.INSERT_DMS_VEHICLES = `mutation INSERT_DMS_VEHICLES($vehicles: [dms_vehicles_insert_input!]!) { + insert_dms_vehicles(objects: $vehicles) { +affected_rows + } +} + `; exports.GET_CDK_ALLOCATIONS = ` diff --git a/server/web-sockets/web-socket.js b/server/web-sockets/web-socket.js index 0b96ed27d..c822f42e7 100644 --- a/server/web-sockets/web-socket.js +++ b/server/web-sockets/web-socket.js @@ -8,7 +8,10 @@ require("dotenv").config({ const { io } = require("../../server"); const { admin } = require("../firebase/firebase-handler"); -const CdkJobExport = require("../cdk/cdk-job-export").default; +const { + default: CdkJobExport, + CdkSelectedCustomer, +} = require("../cdk/cdk-job-export"); const CdkGetMakes = require("../cdk/cdk-get-makes").default; const CdkCalculateAllocations = require("../cdk/cdk-calculate-allocations").default; @@ -60,7 +63,7 @@ io.on("connection", (socket) => { `User selected customer ID ${selectedCustomerId}` ); socket.selectedCustomerId = selectedCustomerId; - //CdkJobExport(socket, jobid); + CdkSelectedCustomer(socket, selectedCustomerId); }); socket.on("cdk-get-makes", async (cdk_dealerid, callback) => { @@ -123,8 +126,40 @@ function createLogEvent(socket, level, message) { } } +function createXmlEvent(socket, xml, message, isError = false) { + if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("TRACE")) { + socket.emit("log-event", { + timestamp: new Date(), + level: isError ? "ERROR" : "TRACE", + message: `${message}: ${xml}`, + }); + } + + logger.log( + isError ? "ws-log-event-xml-error" : "ws-log-event-xml", + isError ? "ERROR" : "TRACE", + socket.user.email, + socket.recordid, + { + wsmessage: message, + xml, + } + ); + + if (socket.logEvents && isArray(socket.logEvents)) { + socket.logEvents.push({ + timestamp: new Date(), + level: isError ? "ERROR" : "TRACE", + message, + xml, + }); + } +} + function LogLevelHierarchy(level) { switch (level) { + case "XML": + return 5; case "TRACE": return 5; case "DEBUG": @@ -141,3 +176,4 @@ function LogLevelHierarchy(level) { } exports.createLogEvent = createLogEvent; +exports.createXmlEvent = createXmlEvent;