diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index f292acf4f..8de0a69f7 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -25469,6 +25469,27 @@ + + invoicedatefuture + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + kmoutnotgreaterthankmin false diff --git a/client/src/App/App.styles.scss b/client/src/App/App.styles.scss index c188f8a14..e7243044d 100644 --- a/client/src/App/App.styles.scss +++ b/client/src/App/App.styles.scss @@ -91,15 +91,14 @@ color: blue; } -.production-completion-1 { - color: rgba(207, 12, 12, 0.8); - -// animation: production-completion-1-blinker 1s linear infinite; +.production-completion-soon { + color: rgba(255, 140, 0, 0.8); + font-weight: bold; +} +.production-completion-past { + color: rgba(255, 0, 0, 0.8); + font-weight: bold; } -// @keyframes production-completion-1-blinker { -// 50% { -// } -// } .react-resizable { position: relative; diff --git a/client/src/components/job-bills-total/job-bills-total.component.jsx b/client/src/components/job-bills-total/job-bills-total.component.jsx index f4dbb80ea..8f8ba31e6 100644 --- a/client/src/components/job-bills-total/job-bills-total.component.jsx +++ b/client/src/components/job-bills-total/job-bills-total.component.jsx @@ -64,9 +64,9 @@ export default function JobBillsTotalComponent({ }) ); - const totalPartsSublet = Dinero(totals.parts.parts.total) - .add(Dinero(totals.parts.sublets.total)) - .add(Dinero(totals.additional.additionalCosts)); + const totalPartsSublet = Dinero(totals.parts.parts.total).add( + Dinero(totals.parts.sublets.total) + ); const discrepancy = totalPartsSublet.subtract(billTotals); diff --git a/client/src/components/job-reconciliation-modal/job-reconciliation-modal.component.jsx b/client/src/components/job-reconciliation-modal/job-reconciliation-modal.component.jsx index 39da47462..f817a4e73 100644 --- a/client/src/components/job-reconciliation-modal/job-reconciliation-modal.component.jsx +++ b/client/src/components/job-reconciliation-modal/job-reconciliation-modal.component.jsx @@ -18,8 +18,7 @@ export default function JobReconciliationModalComponent({ job, bills }) { .flat() || []; const jobLineData = job.joblines.filter( - (j) => - (j.part_type !== null && j.part_type !== "PAE") || IsAdditionalCost(j) + (j) => j.part_type !== null && j.part_type !== "PAE" ); return ( @@ -51,20 +50,3 @@ export default function JobReconciliationModalComponent({ job, bills }) { ); } - -function IsAdditionalCost(jobLine) { - //May be able to use db_ref here to help. - //936012 is Haz Waste Dispoal - //936008 is Paint/Materials - //936007 is Shop/Materials - - //Remove paint and shop mat lines. They're calculated under rates. - const isPaintOrShopMat = - jobLine.db_ref === "936008" || jobLine.db_ref === "936007"; - - return ( - (jobLine.lbr_op === "OP13" || //Added to resolve manual job lines coming into other totals because they have no reference. - (jobLine.db_ref && jobLine.db_ref.startsWith("9360"))) && - !isPaintOrShopMat - ); -} diff --git a/client/src/components/production-list-columns/production-list-columns.data.js b/client/src/components/production-list-columns/production-list-columns.data.js index 00f279a66..d8d547665 100644 --- a/client/src/components/production-list-columns/production-list-columns.data.js +++ b/client/src/components/production-list-columns/production-list-columns.data.js @@ -109,7 +109,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => { state.sortedInfo.columnKey === "scheduled_completion" && state.sortedInfo.order, render: (text, record) => ( - + ), }, { @@ -156,7 +156,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => { state.sortedInfo.columnKey === "scheduled_delivery" && state.sortedInfo.order, render: (text, record) => ( - + ), }, { diff --git a/client/src/components/production-list-columns/production-list-columns.date.component.jsx b/client/src/components/production-list-columns/production-list-columns.date.component.jsx index 4381d3b52..1b201032e 100644 --- a/client/src/components/production-list-columns/production-list-columns.date.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.date.component.jsx @@ -8,7 +8,12 @@ import { DateFormatter } from "../../utils/DateFormatter"; import { useTranslation } from "react-i18next"; -export default function ProductionListDate({ record, field, time }) { +export default function ProductionListDate({ + record, + field, + time, + pastIndicator, +}) { const [updateAlert] = useMutation(UPDATE_JOB); const [visible, setVisible] = useState(false); const { t } = useTranslation(); @@ -31,6 +36,15 @@ export default function ProductionListDate({ record, field, time }) { }); }; + let className = ""; + if (pastIndicator) { + className = + !!record[field] && + ((moment().isSameOrAfter(moment(record[field]), "day") && + "production-completion-past") || + (moment().add(1, "day").isSame(moment(record[field]), "day") && + "production-completion-soon")); + } return (
{record[field]}
diff --git a/client/src/components/tech-job-clock-in-form/tech-job-clock-in-form.container.jsx b/client/src/components/tech-job-clock-in-form/tech-job-clock-in-form.container.jsx index 499f40af1..eeff6d2de 100644 --- a/client/src/components/tech-job-clock-in-form/tech-job-clock-in-form.container.jsx +++ b/client/src/components/tech-job-clock-in-form/tech-job-clock-in-form.container.jsx @@ -1,5 +1,5 @@ import { useMutation } from "@apollo/client"; -import { Button, Card, Form, notification } from "antd"; +import { Button, Card, Form, notification, Space } from "antd"; import axios from "axios"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -9,6 +9,7 @@ import { INSERT_NEW_TIME_TICKET } from "../../graphql/timetickets.queries"; import { selectTechnician } from "../../redux/tech/tech.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors"; import TechClockInComponent from "./tech-job-clock-in-form.component"; +import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component"; const mapStateToProps = createStructuredSelector({ technician: selectTechnician, @@ -71,9 +72,16 @@ export function TechClockInContainer({ technician, bodyshop }) { form.submit()} loading={loading}> - {t("timetickets.actions.clockin")} - + + + + } >
diff --git a/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx b/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx new file mode 100644 index 000000000..e09691a65 --- /dev/null +++ b/client/src/components/tech-job-print-tickets/tech-job-print-tickets.component.jsx @@ -0,0 +1,125 @@ +import { Button, Card, DatePicker, Form, Popover, Space } from "antd"; +import moment from "moment"; +import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { logImEXEvent } from "../../firebase/firebase.utils"; +import { selectTechnician } from "../../redux/tech/tech.selectors"; +import DatePIckerRanges from "../../utils/DatePickerRanges"; +import { GenerateDocument } from "../../utils/RenderTemplate"; +import { TemplateList } from "../../utils/TemplateConstants"; + +const mapStateToProps = createStructuredSelector({ + technician: selectTechnician, +}); +const mapDispatchToProps = (dispatch) => ({ + //setUserLanguage: language => dispatch(setUserLanguage(language)) +}); +export default connect( + mapStateToProps, + mapDispatchToProps +)(TechJobPrintTickets); + +export function TechJobPrintTickets({ technician, event }) { + const { t } = useTranslation(); + + const [loading, setLoading] = useState(false); + const [form] = Form.useForm(); + const [visibility, setVisibility] = useState(false); + + useEffect(() => { + if (visibility && event) { + form.setFieldsValue(event); + } + }, [visibility, form, event]); + + const handleFinish = async (values) => { + logImEXEvent("schedule_manual_event"); + + setLoading(true); + const start = values.dates[0]; + const end = values.dates[1]; + + try { + await GenerateDocument( + { + name: TemplateList().timetickets_employee.key, + variables: { + ...(start + ? { start: moment(start).startOf("day").format("YYYY-MM-DD") } + : {}), + ...(end + ? { end: moment(end).endOf("day").format("YYYY-MM-DD") } + : {}), + ...(start ? { starttz: moment(start).startOf("day") } : {}), + ...(end ? { endtz: moment(end).endOf("day") } : {}), + + id: technician.id, + }, + }, + { + to: technician.email, + subject: TemplateList().timetickets_employee.subject, + }, + "p" + ); + } catch (error) { + console.log(error); + } finally { + setLoading(false); + setVisibility(false); + form.resetFields(); + } + }; + + const overlay = ( + +
+ + + + + + + + + + +
+
+ ); + + const handleClick = (e) => { + setVisibility(true); + }; + + return ( + + + + ); +} diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 999d56224..8f927d4ff 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -1878,6 +1878,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql` scheduled_delivery actual_delivery scheduled_in + date_invoiced actual_in kmin kmout diff --git a/client/src/pages/jobs-close/jobs-close.component.jsx b/client/src/pages/jobs-close/jobs-close.component.jsx index 71d961c99..697211ee6 100644 --- a/client/src/pages/jobs-close/jobs-close.component.jsx +++ b/client/src/pages/jobs-close/jobs-close.component.jsx @@ -56,7 +56,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { jobId: job.id, job: { status: bodyshop.md_ro_statuses.default_invoiced || "", - date_invoiced: new Date(), + date_invoiced: values.date_invoiced, actual_in: values.actual_in, actual_completion: values.actual_completion, actual_delivery: values.actual_delivery, @@ -119,6 +119,9 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { actual_delivery: job.actual_delivery ? moment(job.actual_delivery) : job.scheduled_delivery && moment(job.scheduled_delivery), + date_invoiced: job.date_invoiced + ? moment(job.date_invoiced) + : moment(), kmin: job.kmin, kmout: job.kmout, dms_allocation: job.dms_allocation, @@ -219,6 +222,32 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { > + ({ + validator(_, value) { + if (!bodyshop.cdk_dealerid) return Promise.resolve(); + if (!value || moment(value).isSameOrAfter(moment(), "day")) { + return Promise.resolve(); + } + + return Promise.reject( + new Error(t("jobs.labels.dms.invoicedatefuture")) + ); + }, + }), + ]} + > + + {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && ( a.useremail === userEmail + (a) => a.useremail.toLowerCase() === userEmail.toLowerCase() ); yield put(setAuthlevel(authRecord[0] ? authRecord[0].authlevel : 0)); diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 8e08327d7..4b885d167 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1507,6 +1507,7 @@ "diskscan": "Scan Disk for Estimates", "dms": { "defaultstory": "Bodyshop RO {{ro_number}}. Damage to $t(jobs.fields.area_of_damage_impact.{{area_of_damage}}).", + "invoicedatefuture": "Invoice date must be today or in the future for CDK posting.", "kmoutnotgreaterthankmin": "Mileage out must be greater than mileage in.", "logs": "Logs", "notallocated": "Not Allocated", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index e89b26bd5..ee0b10b4d 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1507,6 +1507,7 @@ "diskscan": "", "dms": { "defaultstory": "", + "invoicedatefuture": "", "kmoutnotgreaterthankmin": "", "logs": "", "notallocated": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 0f1c0c51c..9d50e5f37 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1507,6 +1507,7 @@ "diskscan": "", "dms": { "defaultstory": "", + "invoicedatefuture": "", "kmoutnotgreaterthankmin": "", "logs": "", "notallocated": "", diff --git a/server/data/arms.js b/server/data/arms.js index 7124c07ad..2d23ab8d3 100644 --- a/server/data/arms.js +++ b/server/data/arms.js @@ -2,6 +2,7 @@ const path = require("path"); const queries = require("../graphql-client/queries"); const Dinero = require("dinero.js"); const moment = require("moment"); +const fs = require("fs"); const _ = require("lodash"); const logger = require("../utils/logger"); @@ -28,7 +29,6 @@ exports.default = async (req, res) => { 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) { @@ -40,10 +40,12 @@ exports.default = async (req, res) => { const { jobs } = await client.request(queries.ENTEGRAL_EXPORT, { bodyshopid: bodyshop.id, }); - const ret = jobs.map((job) => { + const jobsToPush = []; + + jobs.forEach((job) => { const transId = uuid(); // Can this actually be the job id? - return { + let obj = { RqUID: transId, DocumentInfo: { BMSVer: "4.0.0", @@ -55,16 +57,18 @@ exports.default = async (req, res) => { TransmitDateTime: moment().format(momentFormat), // Omitted from ARMS docs }, EventInfo: { - AssignmentEvent: { - CreateDateTime: - job.asgn_date && moment(job.asgn_date).format(momentFormat), - }, - EstimateEvent: { - UploadDateTime: moment().format(momentFormat), - }, + // AssignmentEvent: { + // CreateDateTime: + // job.asgn_date && moment(job.asgn_date).format(momentFormat), + // }, + // EstimateEvent: { + // UploadDateTime: moment().format(momentFormat), + // }, RepairEvent: { - CreatedDateTime: - job.date_open && moment(job.date_open).format(momentFormat), + CreatedDateTime: (job.date_open + ? moment(job.date_open) + : moment() + ).format(momentFormat), ArrivalDateTime: job.actual_in && moment(job.actual_in).format(momentFormat), ArrivalOdometerReading: job.kmin, @@ -92,35 +96,35 @@ exports.default = async (req, res) => { 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, - }, + // 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: { @@ -141,16 +145,16 @@ exports.default = async (req, res) => { // }, // }, // }, - Insured: { - Party: { - PersonInfo: { - PersonName: { - FirstName: job.insd_fn, - LastName: job.insd_ln, - }, - }, - }, - }, + // Insured: { + // Party: { + // PersonInfo: { + // PersonName: { + // FirstName: job.insd_fn, + // LastName: job.insd_ln, + // }, + // }, + // }, + // }, Owner: { Party: { PersonInfo: { @@ -158,68 +162,70 @@ exports.default = async (req, res) => { FirstName: job.ownr_fn, LastName: job.ownr_ln, }, - Communications: [ - { - CommQualifier: "HA", - Address: { - Address1: job.ownr_addr1, + // 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, - // }, + // 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, + CompanyName: + process.env.NODE_ENV === "production" + ? bodyshop.shopname + : "IMEX Test Shop", IDInfo: { IDQualifierCode: "US", IDNum: bodyshop.entegral_id, @@ -233,7 +239,7 @@ exports.default = async (req, res) => { // VendorCode: "C", // EstimateDocumentID: "1223HJ76", }, - //RepairOrderType: "DRP", + RepairOrderType: "DirectRepairProgram", //Need to get from Entegral //ReferralSourceType: "Yellow Pages", VehicleInfo: { VINInfo: { @@ -241,9 +247,9 @@ exports.default = async (req, res) => { VINNum: job.v_vin, }, }, - License: { - LicensePlateNum: job.plate_no, - }, + // License: { + // LicensePlateNum: job.plate_no, + // }, VehicleDesc: { //ProductionDate: "2009-10", ModelYear: @@ -255,23 +261,23 @@ exports.default = async (req, res) => { MakeDesc: job.v_make_desc, ModelName: job.v_model_desc, }, - Paint: { - Exterior: { - Color: { - ColorName: job.v_color, - // OEMColorCode: "1M3", - }, - }, - }, + // Paint: { + // Exterior: { + // Color: { + // ColorName: job.v_color, + // // OEMColorCode: "1M3", + // }, + // }, + // }, // Body: { // BodyStyle: "2 Door Convertible", // Trim: { // TrimCode: "1B3", // }, // }, - Condition: { - DrivableInd: job.driveable ? "Y" : "N", - }, + // Condition: { + // DrivableInd: job.driveable ? "Y" : "N", + // }, }, ClaimInfo: { ClaimNum: job.clm_no, @@ -296,7 +302,7 @@ exports.default = async (req, res) => { }, }, ProfileInfo: { - //ProfileName: "Shop Standard Rates", + ProfileName: "ImEX", RateInfo: [ { RateType: "PA", @@ -306,7 +312,7 @@ exports.default = async (req, res) => { TaxableInd: true, TaxTierInfo: { TierNum: 1, - Percentage: 0, //TODO Find the best place to take the tax rates for parts. + Percentage: job.parts_tax_rates.PAN.prt_tax_rt * 100, //TODO Find the best place to take the tax rates for parts. }, }, }, @@ -318,7 +324,7 @@ exports.default = async (req, res) => { TaxableInd: true, TaxTierInfo: { TierNum: 1, - Percentage: 0, //TODO Find the best place to take the tax rates for labor. + Percentage: job.parts_tax_rates.PAN.prt_tax_rt * 100, //TODO Find the best place to take the tax rates for labor. }, }, }, @@ -495,56 +501,56 @@ exports.default = async (req, res) => { TotalType: "PAA", TotalTypeDesc: "Aftermarket Parts", TotalAmt: Dinero( - job.job_totals.parts.parts.list.paa && - job.job_totals.parts.parts.list.paa.total + job.job_totals.parts.parts.list.PAA && + job.job_totals.parts.parts.list.PAA.total ).toFormat("0.00"), }, { TotalType: "PAC", TotalTypeDesc: "Re-Chromed Parts", TotalAmt: Dinero( - job.job_totals.parts.parts.list.pac && - job.job_totals.parts.parts.list.pac.total + job.job_totals.parts.parts.list.PAC && + job.job_totals.parts.parts.list.PAC.total ).toFormat("0.00"), }, { TotalType: "PAG", TotalTypeDesc: "Glass Parts", TotalAmt: Dinero( - job.job_totals.parts.parts.list.pag && - job.job_totals.parts.parts.list.pag.total + job.job_totals.parts.parts.list.PAG && + job.job_totals.parts.parts.list.PAG.total ).toFormat("0.00"), }, { TotalType: "PAL", TotalTypeDesc: "LKQ/Used Parts", TotalAmt: Dinero( - job.job_totals.parts.parts.list.pal && - job.job_totals.parts.parts.list.pal.total + job.job_totals.parts.parts.list.PAL && + job.job_totals.parts.parts.list.PAL.total ).toFormat("0.00"), }, { TotalType: "PAM", TotalTypeDesc: "Remanufactured Parts", TotalAmt: Dinero( - job.job_totals.parts.parts.list.pam && - job.job_totals.parts.parts.list.pam.total + job.job_totals.parts.parts.list.PAM && + job.job_totals.parts.parts.list.PAM.total ).toFormat("0.00"), }, { TotalType: "PAN", TotalTypeDesc: "New Parts", TotalAmt: Dinero( - job.job_totals.parts.parts.list.pan && - job.job_totals.parts.parts.list.pan.total + job.job_totals.parts.parts.list.PAN && + job.job_totals.parts.parts.list.PAN.total ).toFormat("0.00"), }, { TotalType: "PAR", TotalTypeDesc: "Recored Parts", TotalAmt: Dinero( - job.job_totals.parts.parts.list.par && - job.job_totals.parts.parts.list.par.total + job.job_totals.parts.parts.list.PAR && + job.job_totals.parts.parts.list.PAR.total ).toFormat("0.00"), }, ], @@ -616,6 +622,14 @@ exports.default = async (req, res) => { "0.00" ), }, + { + TotalType: "TOT", + TotalSubType: "SM", + TotalTypeDesc: "Supplement Total", + TotalAmt: job.cieca_ttl + ? job.cieca_ttl.data.supp_amt + : Dinero().toFormat("0.00"), + }, { TotalType: "TOT", TotalSubType: "F7", @@ -632,12 +646,14 @@ exports.default = async (req, res) => { "0.00" ), }, - // { - // TotalType: "TOT", - // TotalSubType: "SM", - // TotalTypeDesc: "Supplement Total", - // TotalAmt: 0, - // }, + { + TotalType: "TOT", + TotalSubType: "D8", + TotalTypeDesc: "Bottom Line Discount", + TotalAmt: Dinero( + job.job_totals.additional.adjustments + ).toFormat("0.00"), + }, { TotalType: "TOT", TotalSubType: "D2", @@ -658,15 +674,13 @@ exports.default = async (req, res) => { TotalType: "TOT", TotalSubType: "AA", TotalTypeDesc: "Appearance Allowance", - TotalAmt: 0, + TotalAmt: Dinero().toFormat("0.00"), }, { TotalType: "TOT", - TotalSubType: "D8", - TotalTypeDesc: "Bottom Line Discount", - TotalAmt: Dinero( - job.job_totals.additional.adjustments - ).toFormat("0.00"), + TotalSubType: "DEPOSIT", + TotalTypeDesc: "Deposit", + TotalAmt: Dinero().toFormat("0.00"), }, { TotalType: "TOT", @@ -676,12 +690,6 @@ exports.default = async (req, res) => { .subtract(Dinero(job.job_totals.totals.custPayable.total)) .toFormat("0.00"), }, - // { - // TotalType: "TOT", - // TotalSubType: "DEPOSIT", - // TotalTypeDesc: "Deposit", - // TotalAmt: 0, - // }, { TotalType: "TOT", TotalSubType: "CUST", @@ -691,7 +699,7 @@ exports.default = async (req, res) => { ).toFormat("0.00"), }, ], - RepairTotalsType: 1, + // RepairTotalsType: 1, }, // RepairLabor: { // LaborAllocations: { @@ -741,7 +749,7 @@ exports.default = async (req, res) => { // }, ProductionStatus: { ProductionStage: { - ProductionStageCode: GetProductionStageCode(job), + ProductionStageCode: GetProductionStageCode(job, bodyshop), ProductionStageDateTime: moment().format(momentFormat), // ProductionStageStatusComment: // "Going to be painted this afternoon", @@ -777,6 +785,9 @@ exports.default = async (req, res) => { // }, // }, }; + + deleteNullKeys(obj); + jobsToPush.push(obj); }); if (erroredJobs.length > 0) { @@ -784,13 +795,12 @@ exports.default = async (req, res) => { count: erroredJobs.length, jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number)), }); + allErrors = [...allErrors, ...erroredJobs]; } logger.log("arms-end-shop-extract", "DEBUG", "api", bodyshop.id, { shopname: bodyshop.shopname, }); - const abc = ret[1]; - deleteNullKeys(abc); try { const entegralSoapClient = await soap.createClientAsync( @@ -798,7 +808,7 @@ exports.default = async (req, res) => { { ignoredNamespaces: true, wsdl_options: { - useEmptyTag: true, + // useEmptyTag: true, }, wsdl_headers: { Authorization: `Basic ${new Buffer.from( @@ -816,14 +826,20 @@ exports.default = async (req, res) => { ); const entegralResponse = - await entegralSoapClient.RepairOrderFolderAddRqAsync(abc); + await entegralSoapClient.RepairOrderFolderAddRqAsync( + jobsToPush, + function (err, result, rawResponse, soapHeader, rawRequest) { + fs.writeFileSync(`./logs/arms-request.xml`, rawRequest); + fs.writeFileSync(`./logs/arms-response.xml`, rawResponse); + + res.json(err || result); + } + ); const [result, rawResponse, , rawRequest] = entegralResponse; - console.log("🚀 ~ file: arms.js ~ line 806 ~ result", result); - res.json({ result, obj: abc }); } catch (error) { + fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml); console.log(error); - res.json(error); } } catch (error) { //Error at the shop level. @@ -846,15 +862,18 @@ exports.default = async (req, res) => { } } - // res.sendStatus(200); + res.sendStatus(200); } catch (error) { res.status(200).json(error); } }; function GetSupplementNumber(joblines) { - return 0; - return _.max(joblines.map((jl) => jl.line_ind)); + const max = _.max( + joblines.map((jl) => parseInt((jl.line_ind || "0").replace(/[^\d.-]/g, ""))) + ); + + return max || 0; } function GetDocumentstatus(job, bodyshop) { @@ -873,7 +892,9 @@ function GetDocumentstatus(job, bodyshop) { function GetRepairStatusCode(job) { return "25"; } -function GetProductionStageCode(job) { +function GetProductionStageCode(job, bodyshop) { + if (bodyshop.md_ro_statuses.post_production_statuses.includes(job.status)) + return "8D"; return "33"; } diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index e9ba2d511..1393e1b4d 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -783,6 +783,8 @@ query ENTEGRAL_EXPORT($bodyshopid: uuid!) { rate_matd job_totals ded_amt + cieca_ttl + adjustment_bottom_line } } `;