Merged in release/2022-09-16 (pull request #578)

Release/2022 09 16
This commit is contained in:
Patrick Fic
2022-09-19 15:50:43 +00:00
19 changed files with 1217 additions and 938 deletions

3
.gitignore vendored
View File

@@ -113,3 +113,6 @@ firebase/.env
!.elasticbeanstalk/*.cfg.yml !.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml !.elasticbeanstalk/*.global.yml
logs/oAuthClient-log.log logs/oAuthClient-log.log
.node-persist/**

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.7.1"> <babeledit_project be_version="2.7.1" version="1.2">
<!-- <!--
BabelEdit project file BabelEdit project file
@@ -37159,6 +37159,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>parts_label_multiple</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> <concept_node>
<name>parts_label_single</name> <name>parts_label_single</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -2,6 +2,8 @@ import cleanAxios from "../../utils/CleanAxios";
import { store } from "../../redux/store"; import { store } from "../../redux/store";
import { addMediaForJob } from "../../redux/media/media.actions"; import { addMediaForJob } from "../../redux/media/media.actions";
import normalizeUrl from "normalize-url"; import normalizeUrl from "normalize-url";
import { notification } from "antd";
import i18n from "i18next";
export const handleUpload = async ({ ev, context }) => { export const handleUpload = async ({ ev, context }) => {
const { onError, onSuccess, onProgress, file } = ev; const { onError, onSuccess, onProgress, file } = ev;
@@ -45,6 +47,11 @@ export const handleUpload = async ({ ev, context }) => {
} }
} else { } else {
onSuccess && onSuccess(file); onSuccess && onSuccess(file);
notification.open({
type: "success",
key: "docuploadsuccess",
message: i18n.t("documents.successes.insert"),
});
store.dispatch( store.dispatch(
addMediaForJob({ addMediaForJob({
jobid, jobid,

View File

@@ -14,6 +14,7 @@ import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { CreateExplorerLinkForJob } from "../../utils/localmedia"; import { CreateExplorerLinkForJob } from "../../utils/localmedia";
import DocumentsLocalUploadComponent from "../documents-local-upload/documents-local-upload.component"; import DocumentsLocalUploadComponent from "../documents-local-upload/documents-local-upload.component";
import JobsDocumentsLocalDeleteButton from "./jobs-documents-local-gallery.delete.component";
import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.download"; import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.download";
import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component"; import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component";
import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component"; import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component";
@@ -101,6 +102,7 @@ export function JobsDocumentsLocalGallery({
<JobsDocumentsLocalGalleryReassign jobid={job.id} /> <JobsDocumentsLocalGalleryReassign jobid={job.id} />
<JobsDocumentsLocalGallerySelectAllComponent jobid={job.id} /> <JobsDocumentsLocalGallerySelectAllComponent jobid={job.id} />
<JobsLocalGalleryDownloadButton job={job} /> <JobsLocalGalleryDownloadButton job={job} />
<JobsDocumentsLocalDeleteButton jobid={job.id} />
</Space> </Space>
<Card> <Card>
<DocumentsLocalUploadComponent <DocumentsLocalUploadComponent

View File

@@ -0,0 +1,81 @@
import { QuestionCircleOutlined } from "@ant-design/icons";
import { Button, notification, Popconfirm } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import cleanAxios from "../../utils/CleanAxios";
//Context: currentUserEmail, bodyshop, jobid, invoiceid
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { getJobMedia } from "../../redux/media/media.actions";
import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
allMedia: selectAllMedia,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
getJobMedia: (id) => dispatch(getJobMedia(id)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDocumentsLocalDeleteButton);
export function JobsDocumentsLocalDeleteButton({
bodyshop,
getJobMedia,
allMedia,
jobid,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleDelete = async () => {
logImEXEvent("job_documents_delete");
setLoading(true);
const delres = await cleanAxios.post(
`${bodyshop.localmediaserverhttp}/jobs/delete`,
{
jobid: jobid,
files: ((allMedia && allMedia[jobid]) || [])
.filter((i) => i.isSelected)
.map((i) => i.filename),
},
{ headers: { ims_token: bodyshop.localmediatoken } }
);
if (delres.errors) {
notification["error"]({
message: t("documents.errors.deleting", {
message: JSON.stringify(delres.errors),
}),
});
} else {
notification.open({
key: "docdeletedsuccesfully",
type: "success",
message: t("documents.successes.delete"),
});
}
getJobMedia(jobid);
setLoading(false);
};
return (
<Popconfirm
icon={<QuestionCircleOutlined style={{ color: "red" }} />}
onConfirm={handleDelete}
title={t("documents.labels.confirmdelete")}
okText={t("general.actions.delete")}
okButtonProps={{ type: "danger" }}
cancelText={t("general.actions.cancel")}
>
<Button loading={loading}>{t("documents.actions.delete")}</Button>
</Popconfirm>
);
}

View File

@@ -1,4 +1,4 @@
import { Button, Card, Form, InputNumber, Popover } from "antd"; import { Button, Card, Form, InputNumber, Popover, Radio } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -24,7 +24,8 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const handleOk = () => { const handleOk = (e) => {
e.stopPropagation();
form.submit(); form.submit();
setIsModalVisible(false); setIsModalVisible(false);
}; };
@@ -33,12 +34,12 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
setIsModalVisible(false); setIsModalVisible(false);
setLoading(false); setLoading(false);
}; };
const handleFinish = async (values) => { const handleFinish = async ({ template, ...values }) => {
const { sendtype, ...restVals } = values; const { sendtype, ...restVals } = values;
setLoading(true); setLoading(true);
await GenerateDocument( await GenerateDocument(
{ {
name: TemplateList("job_special").folder_label_multiple.key, name: TemplateList("job_special")[template].key,
variables: { id: jobId }, variables: { id: jobId },
context: restVals, context: restVals,
}, },
@@ -48,6 +49,7 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
); );
setLoading(false); setLoading(false);
setIsModalVisible(false); setIsModalVisible(false);
form.resetFields();
}; };
const content = ( const content = (
@@ -58,13 +60,28 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
layout="vertical" layout="vertical"
form={form} form={form}
> >
<Form.Item required name="template">
<Radio.Group>
<Radio.Button value="parts_label_multiple">
{t("printcenter.jobs.parts_label_multiple")}
</Radio.Button>
<Radio.Button value="folder_label_multiple">
{t("printcenter.jobs.folder_label_multiple")}
</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item <Form.Item
required
label={t("printcenter.jobs.labels.position")} label={t("printcenter.jobs.labels.position")}
name="position" name="position"
> >
<InputNumber min={1} precision={0} /> <InputNumber min={1} precision={0} />
</Form.Item> </Form.Item>
<Form.Item label={t("printcenter.jobs.labels.count")} name="count"> <Form.Item
required
label={t("printcenter.jobs.labels.count")}
name="count"
>
<InputNumber min={1} precision={0} max={99} /> <InputNumber min={1} precision={0} max={99} />
</Form.Item> </Form.Item>
<Button type="primary" loading={loading} onClick={handleOk}> <Button type="primary" loading={loading} onClick={handleOk}>

View File

@@ -2181,7 +2181,7 @@
"filing_coversheet_portrait": "Filing Coversheet (Portrait)", "filing_coversheet_portrait": "Filing Coversheet (Portrait)",
"final_invoice": "Final Invoice", "final_invoice": "Final Invoice",
"fippa_authorization": "FIPPA Authorization", "fippa_authorization": "FIPPA Authorization",
"folder_label_multiple": "Folder Label Multiple", "folder_label_multiple": "Folder Label - Multi",
"glass_express_checklist": "Glass Express Checklist", "glass_express_checklist": "Glass Express Checklist",
"guarantee": "Repair Guarantee", "guarantee": "Repair Guarantee",
"individual_job_note": "Job Note RO # {{ro_number}}", "individual_job_note": "Job Note RO # {{ro_number}}",
@@ -2202,6 +2202,7 @@
"mpi_eglass_auth": "MPI - eGlass Auth", "mpi_eglass_auth": "MPI - eGlass Auth",
"mpi_final_acct_sheet": "MPI - Final Accounting Sheet", "mpi_final_acct_sheet": "MPI - Final Accounting Sheet",
"paint_grid": "Paint Grid", "paint_grid": "Paint Grid",
"parts_label_multiple": "Parts Label - Multi",
"parts_label_single": "Parts Label - Single", "parts_label_single": "Parts Label - Single",
"parts_list": "Parts List", "parts_list": "Parts List",
"parts_order": "Parts Order Confirmation", "parts_order": "Parts Order Confirmation",

View File

@@ -2202,6 +2202,7 @@
"mpi_eglass_auth": "", "mpi_eglass_auth": "",
"mpi_final_acct_sheet": "", "mpi_final_acct_sheet": "",
"paint_grid": "", "paint_grid": "",
"parts_label_multiple": "",
"parts_label_single": "", "parts_label_single": "",
"parts_list": "", "parts_list": "",
"parts_order": "", "parts_order": "",

View File

@@ -2202,6 +2202,7 @@
"mpi_eglass_auth": "", "mpi_eglass_auth": "",
"mpi_final_acct_sheet": "", "mpi_final_acct_sheet": "",
"paint_grid": "", "paint_grid": "",
"parts_label_multiple": "",
"parts_label_single": "", "parts_label_single": "",
"parts_list": "", "parts_list": "",
"parts_order": "", "parts_order": "",

View File

@@ -504,6 +504,12 @@ export const TemplateList = (type, context) => {
key: "folder_label_multiple", key: "folder_label_multiple",
disabled: false, disabled: false,
}, },
parts_label_multiple: {
title: i18n.t("printcenter.jobs.parts_label_multiple"),
description: "Parts Label Multiple",
key: "parts_label_multiple",
disabled: false,
},
csi_invitation_action: { csi_invitation_action: {
title: i18n.t("printcenter.jobs.csi_invitation_action"), title: i18n.t("printcenter.jobs.csi_invitation_action"),
description: "CSI invite", description: "CSI invite",

View File

@@ -1,3 +1,3 @@
Must set the environment variables using: Must set the environment variables using:
firebase functions:config:set auth.graphql_endpoint="https://db.development.bodyshop.app/v1/graphql" auth.hasura_secret_admin_key="Dev-BodyShopApp!" firebase functions:config:set auth.graphql_endpoint="https://db.dev.bodyshop.app/v1/graphql" auth.hasura_secret_admin_key="Dev-BodyShopApp!"

View File

@@ -1,5 +1,5 @@
version: 2 version: 2
endpoint: https://db.development.bodyshop.app endpoint: https://db.dev.bodyshop.app
admin_secret: Dev-BodyShopApp! admin_secret: Dev-BodyShopApp!
metadata_directory: metadata metadata_directory: metadata
actions: actions:

View File

@@ -1,30 +1,30 @@
- function: - function:
schema: public
name: search_bills name: search_bills
- function:
schema: public schema: public
- function:
name: search_cccontracts name: search_cccontracts
- function:
schema: public schema: public
- function:
name: search_dms_vehicles name: search_dms_vehicles
- function:
schema: public schema: public
- function:
name: search_exportlog name: search_exportlog
- function:
schema: public schema: public
- function:
name: search_inventory name: search_inventory
- function:
schema: public schema: public
- function:
name: search_jobs name: search_jobs
- function:
schema: public schema: public
- function:
name: search_owners name: search_owners
- function:
schema: public schema: public
- function:
name: search_payments name: search_payments
- function:
schema: public schema: public
- function:
name: search_phonebook name: search_phonebook
- function:
schema: public schema: public
- function:
name: search_vehicles name: search_vehicles
schema: public

File diff suppressed because it is too large Load Diff

View File

@@ -41,6 +41,7 @@
"moment-timezone": "^0.5.34", "moment-timezone": "^0.5.34",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"node-mailjet": "^5.1.0", "node-mailjet": "^5.1.0",
"node-persist": "^3.1.0",
"node-quickbooks": "^2.0.39", "node-quickbooks": "^2.0.39",
"nodemailer": "^6.7.7", "nodemailer": "^6.7.7",
"phone": "^3.1.23", "phone": "^3.1.23",

View File

@@ -210,7 +210,7 @@ app.post("/qbo/payments", fb.validateFirebaseIdToken, qbo.payments);
var data = require("./server/data/data"); var data = require("./server/data/data");
app.post("/data/ah", data.autohouse); app.post("/data/ah", data.autohouse);
app.post("/data/arms", data.arms); app.post("/record-handler/arms", data.arms);
var taskHandler = require("./server/tasks/tasks"); var taskHandler = require("./server/tasks/tasks");
app.post("/taskHandler", taskHandler.taskHandler); app.post("/taskHandler", taskHandler.taskHandler);
@@ -238,6 +238,7 @@ app.get("/", async function (req, res) {
res.status(200).send("Access Forbidden."); res.status(200).send("Access Forbidden.");
}); });
server.listen(port, (error) => { server.listen(port, (error) => {
if (error) throw error; if (error) throw error;
logger.log( logger.log(

View File

@@ -3,7 +3,7 @@ const queries = require("../graphql-client/queries");
const Dinero = require("dinero.js"); const Dinero = require("dinero.js");
const moment = require("moment-timezone"); const moment = require("moment-timezone");
const fs = require("fs"); const fs = require("fs");
const storage = require("node-persist");
const _ = require("lodash"); const _ = require("lodash");
const logger = require("../utils/logger"); const logger = require("../utils/logger");
require("dotenv").config({ require("dotenv").config({
@@ -13,6 +13,7 @@ require("dotenv").config({
), ),
}); });
const soap = require("soap"); const soap = require("soap");
const { sendServerEmail } = require("../email/sendemail");
const entegralEndpoint = const entegralEndpoint =
process.env.NODE_ENV === "production" process.env.NODE_ENV === "production"
@@ -24,27 +25,63 @@ const uuid = require("uuid").v4;
const momentFormat = "yyyy-MM-DDTHH:mm:ss.SSS"; const momentFormat = "yyyy-MM-DDTHH:mm:ss.SSS";
function pollFunc(fn, timeout, interval) {
var startTime = new Date().getTime();
(interval = interval || 1000), (canPoll = true);
(function p() {
canPoll =
timeout === 0 ? true : new Date().getTime() - startTime <= timeout;
if (fn() && canPoll) {
// ensures the function exucutes
setTimeout(p, interval);
}
})();
}
pollFunc(getEntegralShopData, 0, 5 * 60 * 1000); //Set the metadata to refresh every 5 minutes.
async function getEntegralShopData() {
const { bodyshops } = await client.request(queries.GET_ENTEGRAL_SHOPS);
await storage.init({ logging: true });
logger.log("set-entegral-shops-local-storage", "DEBUG", "API", null, null);
await storage.setItem("entegralShops", bodyshops);
return true; //Continue execution.
}
exports.default = async (req, res) => { exports.default = async (req, res) => {
//Query for the List of Bodyshop Clients. //Query for the List of Bodyshop Clients.
logger.log("arms-start", "DEBUG", "api", null, null); const job = req.body.event.data.new;
const { bodyshops } = await client.request(queries.GET_ENTEGRAL_SHOPS); logger.log("arms-job-update", "DEBUG", "api", job.id, null);
const allErrors = []; let allEntegralShops = await storage.getItem("entegralShops");
try {
for (const bodyshop of bodyshops) {
logger.log("arms-start-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname,
});
const erroredJobs = [];
try {
const { jobs } = await client.request(queries.ENTEGRAL_EXPORT, {
bodyshopid: bodyshop.id,
});
const jobsToPush = [];
jobs.forEach((job) => { if (!allEntegralShops) {
await getEntegralShopData();
allEntegralShops = await storage.getItem("entegralShops");
}
//Is this job part of an entegral shop?
const bodyshop = allEntegralShops.find((b) => b.id === job.shopid);
if (!bodyshop) {
//This job is not for entegral based shops.
res.sendStatus(200);
return;
}
if (process.env.NODE_ENV === "PRODUCTION") {
res.sendStatus(200);
return;
}
//TODO: Check if an update should even be sent.
if (false) {
res.sendStatus(200);
return;
}
try {
const transId = uuid(); // Can this actually be the job id? const transId = uuid(); // Can this actually be the job id?
let obj = { let obj = {
RqUID: transId, RqUID: transId,
DocumentInfo: { DocumentInfo: {
@@ -71,9 +108,7 @@ exports.default = async (req, res) => {
).format(momentFormat), ).format(momentFormat),
ArrivalDateTime: ArrivalDateTime:
job.actual_in && job.actual_in &&
moment(job.actual_in) moment(job.actual_in).tz(bodyshop.timezone).format(momentFormat),
.tz(bodyshop.timezone)
.format(momentFormat),
ArrivalOdometerReading: job.kmin, ArrivalOdometerReading: job.kmin,
TargetCompletionDateTime: TargetCompletionDateTime:
job.scheduled_completion && job.scheduled_completion &&
@@ -105,7 +140,7 @@ exports.default = async (req, res) => {
CompanyName: job.ins_co_nm, CompanyName: job.ins_co_nm,
IDInfo: { IDInfo: {
IDQualifierCode: "US", IDQualifierCode: "US",
IDNum: 44, // ** Not sure where to get this entegral ID from? //IDNum: 44, // ** Not sure where to get this entegral ID from?
}, },
// Communications: [ // Communications: [
// { // {
@@ -481,33 +516,25 @@ exports.default = async (req, res) => {
TotalType: "LAB", TotalType: "LAB",
TotalTypeDesc: "Body Labor", TotalTypeDesc: "Body Labor",
TotalHours: job.job_totals.rates.lab.hours, TotalHours: job.job_totals.rates.lab.hours,
TotalAmt: Dinero(job.job_totals.rates.lab.total).toFormat( TotalAmt: Dinero(job.job_totals.rates.lab.total).toFormat("0.00"),
"0.00"
),
}, },
{ {
TotalType: "LAF", TotalType: "LAF",
TotalTypeDesc: "Frame Labor", TotalTypeDesc: "Frame Labor",
TotalHours: job.job_totals.rates.laf.hours, TotalHours: job.job_totals.rates.laf.hours,
TotalAmt: Dinero(job.job_totals.rates.laf.total).toFormat( TotalAmt: Dinero(job.job_totals.rates.laf.total).toFormat("0.00"),
"0.00"
),
}, },
{ {
TotalType: "LAM", TotalType: "LAM",
TotalTypeDesc: "Mechanical Labor", TotalTypeDesc: "Mechanical Labor",
TotalHours: job.job_totals.rates.lam.hours, TotalHours: job.job_totals.rates.lam.hours,
TotalAmt: Dinero(job.job_totals.rates.lam.total).toFormat( TotalAmt: Dinero(job.job_totals.rates.lam.total).toFormat("0.00"),
"0.00"
),
}, },
{ {
TotalType: "LAR", TotalType: "LAR",
TotalTypeDesc: "Refinish Labor", TotalTypeDesc: "Refinish Labor",
TotalHours: job.job_totals.rates.lar.hours, TotalHours: job.job_totals.rates.lar.hours,
TotalAmt: Dinero(job.job_totals.rates.lar.total).toFormat( TotalAmt: Dinero(job.job_totals.rates.lar.total).toFormat("0.00"),
"0.00"
),
}, },
], ],
PartsTotalsInfo: [ PartsTotalsInfo: [
@@ -579,16 +606,12 @@ exports.default = async (req, res) => {
{ {
TotalType: "MAPA", TotalType: "MAPA",
TotalTypeDesc: "Paint Materials", TotalTypeDesc: "Paint Materials",
TotalAmt: Dinero(job.job_totals.rates.mapa.total).toFormat( TotalAmt: Dinero(job.job_totals.rates.mapa.total).toFormat("0.00"),
"0.00"
),
}, },
{ {
TotalType: "MASH", TotalType: "MASH",
TotalTypeDesc: "Shop Materials", TotalTypeDesc: "Shop Materials",
TotalAmt: Dinero(job.job_totals.rates.mash.total).toFormat( TotalAmt: Dinero(job.job_totals.rates.mash.total).toFormat("0.00"),
"0.00"
),
}, },
// { // {
// TotalType: "MAHW", // TotalType: "MAHW",
@@ -607,9 +630,7 @@ exports.default = async (req, res) => {
{ {
TotalType: "OTTW", TotalType: "OTTW",
TotalTypeDesc: "Towing", TotalTypeDesc: "Towing",
TotalAmt: Dinero(job.job_totals.additional.towing).toFormat( TotalAmt: Dinero(job.job_totals.additional.towing).toFormat("0.00"),
"0.00"
),
}, },
{ {
TotalType: "OTAC", TotalType: "OTAC",
@@ -624,17 +645,15 @@ exports.default = async (req, res) => {
TotalType: "TOT", TotalType: "TOT",
TotalSubType: "TT", TotalSubType: "TT",
TotalTypeDesc: "Gross Total", TotalTypeDesc: "Gross Total",
TotalAmt: Dinero( TotalAmt: Dinero(job.job_totals.totals.total_repairs).toFormat(
job.job_totals.totals.total_repairs "0.00"
).toFormat("0.00"), ),
}, },
{ {
TotalType: "TOT", TotalType: "TOT",
TotalSubType: "T2", TotalSubType: "T2",
TotalTypeDesc: "Net Total", TotalTypeDesc: "Net Total",
TotalAmt: Dinero(job.job_totals.totals.subtotal).toFormat( TotalAmt: Dinero(job.job_totals.totals.subtotal).toFormat("0.00"),
"0.00"
),
}, },
{ {
TotalType: "TOT", TotalType: "TOT",
@@ -648,9 +667,7 @@ exports.default = async (req, res) => {
TotalType: "TOT", TotalType: "TOT",
TotalSubType: "F7", TotalSubType: "F7",
TotalTypeDesc: "Sales Tax", TotalTypeDesc: "Sales Tax",
TotalAmt: Dinero(job.job_totals.totals.state_tax).toFormat( TotalAmt: Dinero(job.job_totals.totals.state_tax).toFormat("0.00"),
"0.00"
),
}, },
{ {
TotalType: "TOT", TotalType: "TOT",
@@ -664,9 +681,9 @@ exports.default = async (req, res) => {
TotalType: "TOT", TotalType: "TOT",
TotalSubType: "D8", TotalSubType: "D8",
TotalTypeDesc: "Bottom Line Discount", TotalTypeDesc: "Bottom Line Discount",
TotalAmt: Dinero( TotalAmt: Dinero(job.job_totals.additional.adjustments).toFormat(
job.job_totals.additional.adjustments "0.00"
).toFormat("0.00"), ),
}, },
{ {
TotalType: "TOT", TotalType: "TOT",
@@ -708,9 +725,9 @@ exports.default = async (req, res) => {
TotalType: "TOT", TotalType: "TOT",
TotalSubType: "CUST", TotalSubType: "CUST",
TotalTypeDesc: "Customer Pay", TotalTypeDesc: "Customer Pay",
TotalAmt: Dinero( TotalAmt: Dinero(job.job_totals.totals.custPayable.total).toFormat(
job.job_totals.totals.custPayable.total "0.00"
).toFormat("0.00"), ),
}, },
], ],
// RepairTotalsType: 1, // RepairTotalsType: 1,
@@ -805,8 +822,73 @@ exports.default = async (req, res) => {
}; };
deleteNullKeys(obj); deleteNullKeys(obj);
jobsToPush.push(obj);
try {
const entegralSoapClient = await soap.createClientAsync(
entegralEndpoint,
{
ignoredNamespaces: true,
wsdl_options: {
// useEmptyTag: true,
},
wsdl_headers: {
Authorization: `Basic ${new Buffer.from(
`${process.env.ENTEGRAL_USER}:${process.env.ENTEGRAL_PASSWORD}`
).toString("base64")}`,
},
}
);
entegralSoapClient.setSecurity(
new soap.BasicAuthSecurity(
process.env.ENTEGRAL_USER,
process.env.ENTEGRAL_PASSWORD
)
);
const entegralResponse =
await entegralSoapClient.RepairOrderFolderAddRqAsync(
obj,
function (err, result, rawResponse, soapHeader, rawRequest) {
fs.writeFileSync(`./logs/arms-request.xml`, rawRequest);
fs.writeFileSync(`./logs/arms-response.xml`, rawResponse);
if (err) {
sendServerEmail({
subject: `ARMS Update Failed: ${bodyshop.shopname} - ${job.ro_number}`,
text: `Error: ${JSON.stringify(error)}`,
}); });
}
res.status(200).json(err || result);
}
);
const [result, rawResponse, , rawRequest] = entegralResponse;
} catch (error) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
console.log(error);
}
} catch (error) {
logger.log("arms-failed-job", "ERROR", "api", job.shopid, {
job: JSON.stringify({ id: job.id, ro_number: job.ro_number }),
});
}
res.sendStatus(200);
return;
const allErrors = [];
try {
for (const bodyshop of bodyshops) {
logger.log("arms-start-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname,
});
const erroredJobs = [];
try {
const { jobs } = await client.request(queries.ENTEGRAL_EXPORT, {
bodyshopid: bodyshop.id,
});
const jobsToPush = [];
if (erroredJobs.length > 0) { if (erroredJobs.length > 0) {
logger.log("arms-failed-jobs", "ERROR", "api", bodyshop.id, { logger.log("arms-failed-jobs", "ERROR", "api", bodyshop.id, {
@@ -887,6 +969,7 @@ exports.default = async (req, res) => {
}; };
function GetSupplementNumber(joblines) { function GetSupplementNumber(joblines) {
if (!joblines) return 0;
const max = _.max( const max = _.max(
joblines.map((jl) => parseInt((jl.line_ind || "0").replace(/[^\d.-]/g, ""))) joblines.map((jl) => parseInt((jl.line_ind || "0").replace(/[^\d.-]/g, "")))
); );

View File

@@ -5,7 +5,7 @@ const logger = new graylog2.graylog({
}); });
function log(message, type, user, record, object) { function log(message, type, user, record, object) {
if (type !== "ioevent" && type !== "DEBUG") if (type !== "ioevent")
console.log(message, { console.log(message, {
type, type,
env: process.env.NODE_ENV || "development", env: process.env.NODE_ENV || "development",

View File

@@ -2715,6 +2715,11 @@ node-mailjet@^5.1.0:
json-bigint "^1.0.0" json-bigint "^1.0.0"
url-join "^4.0.0" url-join "^4.0.0"
node-persist@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/node-persist/-/node-persist-3.1.0.tgz#9d4b03950bba70d37d13d3d3551840e25fd17e09"
integrity sha512-/j+fd/u71wNgKf3V2bx4tnDm+3GvLnlCuvf2MXbJ3wern+67IAb6zN9Leu1tCWPlPNZ+v1hLSibVukkPK2HqJw==
node-quickbooks@^2.0.39: node-quickbooks@^2.0.39:
version "2.0.39" version "2.0.39"
resolved "https://registry.yarnpkg.com/node-quickbooks/-/node-quickbooks-2.0.39.tgz#a2534d24063e8a0cea5bb80c66b0727c1c942081" resolved "https://registry.yarnpkg.com/node-quickbooks/-/node-quickbooks-2.0.39.tgz#a2534d24063e8a0cea5bb80c66b0727c1c942081"