Merged in release/2022-01-21 (pull request #351)

Release/2022 01 21
This commit is contained in:
Patrick Fic
2022-01-20 00:30:05 +00:00
16 changed files with 413 additions and 212 deletions

View File

@@ -25469,6 +25469,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>invoicedatefuture</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>kmoutnotgreaterthankmin</name>
<definition_loaded>false</definition_loaded>

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 }) {
</div>
);
}
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
);
}

View File

@@ -109,7 +109,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.columnKey === "scheduled_completion" &&
state.sortedInfo.order,
render: (text, record) => (
<ProductionListDate record={record} field="scheduled_completion" />
<ProductionListDate record={record} field="scheduled_completion" pastIndicator />
),
},
{
@@ -156,7 +156,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.columnKey === "scheduled_delivery" &&
state.sortedInfo.order,
render: (text, record) => (
<ProductionListDate record={record} field="scheduled_delivery" />
<ProductionListDate record={record} field="scheduled_delivery" pastIndicator/>
),
},
{

View File

@@ -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 (
<div>
<Dropdown
@@ -69,11 +83,7 @@ export default function ProductionListDate({ record, field, time }) {
style={{
height: "19px",
}}
className={
!!record[field] && moment().isSame(moment(record[field]), "day")
? "production-completion-1"
: ""
}
className={className}
>
<DateFormatter bordered={false}>{record[field]}</DateFormatter>
</div>

View File

@@ -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 }) {
<Card
title={t("timetickets.labels.clockintojob")}
extra={
<Button type="primary" onClick={() => form.submit()} loading={loading}>
{t("timetickets.actions.clockin")}
</Button>
<Space wrap>
<TechJobPrintTickets />
<Button
type="primary"
onClick={() => form.submit()}
loading={loading}
>
{t("timetickets.actions.clockin")}
</Button>
</Space>
}
>
<Form form={form} layout="vertical" onFinish={handleFinish}>

View File

@@ -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 = (
<Card>
<div>
<Form form={form} layout="vertical" onFinish={handleFinish}>
<Form.Item
label={t("reportcenter.labels.dates")}
name="dates"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<DatePicker.RangePicker
ranges={DatePIckerRanges}
format={"MM/DD/YYYY"}
/>
</Form.Item>
<Space wrap>
<Button type="primary" onClick={() => form.submit()}>
{t("general.actions.print")}
</Button>
<Button
onClick={() => {
setVisibility(false);
form.resetFields();
}}
>
{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("general.actions.print")}
</Button>
</Popover>
);
}

View File

@@ -1878,6 +1878,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
scheduled_delivery
actual_delivery
scheduled_in
date_invoiced
actual_in
kmin
kmout

View File

@@ -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 }) {
>
<DateTimePicker disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.date_invoiced")}
name="date_invoiced"
rules={[
{
required: true,
},
({ getFieldValue }) => ({
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"))
);
},
}),
]}
>
<DateTimePicker
disabled={jobRO}
onlyFuture={!!bodyshop.cdk_dealerid}
/>
</Form.Item>
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<Form.Item
label={t("jobs.fields.kmin")}

View File

@@ -270,7 +270,7 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
factory.client(payload.imexshopid);
const authRecord = payload.associations.filter(
(a) => a.useremail === userEmail
(a) => a.useremail.toLowerCase() === userEmail.toLowerCase()
);
yield put(setAuthlevel(authRecord[0] ? authRecord[0].authlevel : 0));

View File

@@ -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",

View File

@@ -1507,6 +1507,7 @@
"diskscan": "",
"dms": {
"defaultstory": "",
"invoicedatefuture": "",
"kmoutnotgreaterthankmin": "",
"logs": "",
"notallocated": "",

View File

@@ -1507,6 +1507,7 @@
"diskscan": "",
"dms": {
"defaultstory": "",
"invoicedatefuture": "",
"kmoutnotgreaterthankmin": "",
"logs": "",
"notallocated": "",

View File

@@ -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";
}

View File

@@ -783,6 +783,8 @@ query ENTEGRAL_EXPORT($bodyshopid: uuid!) {
rate_matd
job_totals
ded_amt
cieca_ttl
adjustment_bottom_line
}
}
`;