Merged in release/2021-11-12 (pull request #262)

IO-117 PBS WIP

Approved-by: Patrick Fic
This commit is contained in:
Patrick Fic
2021-11-11 02:13:28 +00:00
25 changed files with 494 additions and 127 deletions

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
@@ -19779,6 +19779,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>date_next_contact</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>date_open</name>
<definition_loaded>false</definition_loaded>
@@ -29314,6 +29335,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>noattachedjobs</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
@@ -29466,6 +29508,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>recentonly</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>selectmedia</name>
<definition_loaded>false</definition_loaded>

View File

@@ -1,5 +1,5 @@
import { ShrinkOutlined } from "@ant-design/icons";
import { Col, Row, Typography } from "antd";
import { ShrinkOutlined, InfoCircleOutlined } from "@ant-design/icons";
import { Col, Row, Tooltip, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -31,6 +31,9 @@ export function ChatPopupComponent({
{t("messaging.labels.messaging")}
</Typography.Title>
<ChatNewConversation />
<Tooltip title={t("messaging.labels.recentonly")}>
<InfoCircleOutlined />
</Tooltip>
</div>
<ShrinkOutlined
onClick={() => toggleChatVisible()}

View File

@@ -1,4 +1,4 @@
import { Button, Table, Col , Checkbox} from "antd";
import { Button, Table, Col, Checkbox } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -23,15 +23,26 @@ export function DmsCustomerSelector({ bodyshop }) {
const [customerList, setcustomerList] = useState([]);
const [visible, setVisible] = useState(false);
const [selectedCustomer, setSelectedCustomer] = useState(null);
const [dmsType, setDmsType] = useState("cdk");
socket.on("cdk-select-customer", (customerList, callback) => {
setVisible(true);
setDmsType("cdk");
setcustomerList(customerList);
});
socket.on("pbs-select-customer", (customerList, callback) => {
setVisible(true);
setDmsType("pbs");
setcustomerList(customerList);
console.log(
"🚀 ~ file: dms-customer-selector.component.jsx ~ line 37 ~ socket.on ~ customerList",
customerList
);
});
const onUseSelected = () => {
setVisible(false);
socket.emit("cdk-selected-customer", selectedCustomer);
socket.emit(`${dmsType}-selected-customer`, selectedCustomer);
setSelectedCustomer(null);
};
@@ -50,7 +61,7 @@ export function DmsCustomerSelector({ bodyshop }) {
setSelectedCustomer(null);
};
const columns = [
const cdkColumns = [
{
title: t("jobs.fields.dms.id"),
dataIndex: ["id", "value"],
@@ -60,13 +71,14 @@ export function DmsCustomerSelector({ bodyshop }) {
title: t("jobs.fields.dms.vinowner"),
dataIndex: "vinOwner",
key: "vinOwner",
render: (text, record) => <Checkbox disabled checked={record.vinOwner}/>
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />,
},
{
title: t("jobs.fields.dms.name1"),
dataIndex: ["name1", "fullName"],
key: "name1",
sorter: (a, b) => alphaSort(a.name1?.fullName, b.name1?.fullName),
sorter: (a, b) =>
alphaSort(a.name1 && a.name1.fullName, b.name1 && b.name1.fullName),
},
{
@@ -74,11 +86,43 @@ export function DmsCustomerSelector({ bodyshop }) {
//dataIndex: ["name2", "fullName"],
key: "address",
render: (record, value) =>
`${record?.address?.addressLine[0]}, ${record.address?.city} ${record.address?.stateOrProvince} ${record.address?.postalCode}`,
`${record.address && record.address.addressLine[0]}, ${
record.address && record.address.city
} ${record.address && record.address.stateOrProvince} ${
record.address && record.address.postalCode
}`,
},
];
if (!visible) return <></>;
const pbsColumns = [
{
title: t("jobs.fields.dms.id"),
dataIndex: "ContactId",
key: "ContactId",
},
{
title: t("jobs.fields.dms.vinowner"),
dataIndex: "vinOwner",
key: "vinOwner",
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />,
},
{
title: t("jobs.fields.dms.name1"),
key: "name1",
sorter: (a, b) => alphaSort(a.LastName, b.LastName),
render: (text, record) =>
`${record.FirstName || ""} ${record.LastName || ""}`,
},
{
title: t("jobs.fields.dms.address"),
key: "address",
render: (record, value) =>
`${record.Address}, ${record.City} ${record.State} ${record.ZipCode}`,
},
];
if (!visible) return null;
return (
<Col span={24}>
<Table
@@ -104,13 +148,17 @@ export function DmsCustomerSelector({ bodyshop }) {
</div>
)}
pagination={{ position: "top" }}
columns={columns}
rowKey={(record) => record.id.value}
columns={dmsType === "cdk" ? cdkColumns : pbsColumns}
rowKey={(record) =>
dmsType === "cdk" ? record.id.value : record.ContactId
}
dataSource={customerList}
//onChange={handleTableChange}
rowSelection={{
onSelect: (props) => {
setSelectedCustomer(props.id.value);
onSelect: (record) => {
setSelectedCustomer(
dmsType === "cdk" ? record.id.value : record.ContactId
);
},
type: "radio",
selectedRowKeys: [selectedCustomer],

View File

@@ -82,7 +82,6 @@ function Header({
setReportCenterContext,
recentItems,
}) {
console.log("🚀 ~ file: header.component.jsx ~ line 85 ~ bodyshop", bodyshop);
const { t } = useTranslation();
return (

View File

@@ -75,6 +75,12 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
>
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.date_next_contact")}
name="date_next_contact"
>
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.scheduled_completion")}
name="scheduled_completion"

View File

@@ -10,7 +10,7 @@ import { useTranslation } from "react-i18next";
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
export default function ProductionBoardCard(technician, card) {
export default function ProductionBoardCard(technician, card, bodyshop) {
const { t } = useTranslation();
const menu = (
<div>
@@ -19,6 +19,21 @@ export default function ProductionBoardCard(technician, card) {
</Card>
</div>
);
let employee_body, employee_prep, employee_refinish, employee_csr;
if (card.employee_body) {
employee_body = bodyshop.employees.find((e) => e.id === card.employee_body);
}
if (card.employee_prep) {
employee_prep = bodyshop.employees.find((e) => e.id === card.employee_prep);
}
if (card.employee_refinish) {
employee_refinish = bodyshop.employees.find(
(e) => e.id === card.employee_refinish
);
}
if (card.employee_csr) {
employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
}
return (
<Dropdown overlay={menu} trigger={["contextMenu"]}>
@@ -53,23 +68,23 @@ export default function ProductionBoardCard(technician, card) {
</div>
<div className="mex-flex-row__margin">
<div>{`B: ${
card.employee_body_rel
? `${card.employee_body_rel.first_name} ${card.employee_body_rel.last_name}`
employee_body
? `${employee_body.first_name} ${employee_body.last_name}`
: ""
}`}</div>
<div>{`P: ${
card.employee_prep_rel
? `${card.employee_prep_rel.first_name} ${card.employee_prep_rel.last_name}`
employee_prep
? `${employee_prep.first_name} ${employee_prep.last_name}`
: ""
}`}</div>
<div>{`R: ${
card.employee_refinish_rel
? `${card.employee_refinish_rel.first_name} ${card.employee_refinish_rel.last_name}`
employee_refinish
? `${employee_refinish.first_name} ${employee_refinish.last_name}`
: ""
}`}</div>
<div>{`CSR: ${
card.employee_csr_rel
? `${card.employee_csr_rel.first_name} ${card.employee_csr_rel.last_name}`
employee_csr
? `${employee_csr.first_name} ${employee_csr.last_name}`
: ""
}`}</div>
</div>

View File

@@ -131,14 +131,14 @@ export function ProductionBoardKanbanComponent({
}
};
const totalHrs = data
.reduce(
(acc, val) =>
acc +
(val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) +
(val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
.reduce(
(acc, val) =>
acc +
(val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) +
(val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
return (
<div>
<IndefiniteLoading loading={isMoving} />
@@ -167,7 +167,7 @@ export function ProductionBoardKanbanComponent({
<Board
children={boardLanes}
disableCardDrag={isMoving}
renderCard={(card) => ProductionBoardCard(technician, card)}
renderCard={(card) => ProductionBoardCard(technician, card, bodyshop)}
onCardDragEnd={handleDragEnd}
/>
</div>

View File

@@ -1,4 +1,5 @@
import i18n from "i18next";
import moment from "moment";
import React from "react";
import { Link } from "react-router-dom";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
@@ -11,11 +12,11 @@ import ProductionListColumnBodyPriority from "./production-list-columns.bodyprio
import ProductionListDate from "./production-list-columns.date.component";
import ProductionListColumnDetailPriority from "./production-list-columns.detailpriority.component";
import ProductionListEmployeeAssignment from "./production-list-columns.empassignment.component";
import ProductionListLastContacted from "./production-list-columns.lastcontacted.component";
import ProductionListColumnPaintPriority from "./production-list-columns.paintpriority.component";
import ProductionListColumnNote from "./production-list-columns.productionnote.component";
import ProductionListColumnStatus from "./production-list-columns.status.component";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
import ProductionListLastContacted from "./production-list-columns.lastcontacted.component";
const r = ({ technician, state, activeStatuses }) => {
return [
@@ -109,6 +110,29 @@ const r = ({ technician, state, activeStatuses }) => {
state.sortedInfo.order,
render: (text, record) => <ProductionListLastContacted record={record} />,
},
{
title: i18n.t("jobs.fields.date_next_contact"),
dataIndex: "date_next_contact",
key: "date_next_contact",
ellipsis: true,
sorter: (a, b) => dateSort(a.date_next_contact, b.date_next_contact),
sortOrder:
state.sortedInfo.columnKey === "date_next_contact" &&
state.sortedInfo.order,
render: (text, record) => (
<span
style={{
color:
record.date_next_contact &&
moment(record.date_next_contact).isBefore(moment())
? "red"
: "",
}}
>
<ProductionListDate record={record} field="date_next_contact" time />
</span>
),
},
{
title: i18n.t("jobs.fields.scheduled_delivery"),
dataIndex: "scheduled_delivery",
@@ -332,7 +356,7 @@ const r = ({ technician, state, activeStatuses }) => {
render: (text, record) => (
<ProductionListEmployeeAssignment
record={record}
type="employee_body_rel"
type="employee_body"
/>
),
},
@@ -343,7 +367,7 @@ const r = ({ technician, state, activeStatuses }) => {
render: (text, record) => (
<ProductionListEmployeeAssignment
record={record}
type="employee_prep_rel"
type="employee_prep"
/>
),
},
@@ -352,10 +376,7 @@ const r = ({ technician, state, activeStatuses }) => {
dataIndex: "employee_csr",
key: "employee_csr",
render: (text, record) => (
<ProductionListEmployeeAssignment
record={record}
type="employee_csr_rel"
/>
<ProductionListEmployeeAssignment record={record} type="employee_csr" />
),
},
{
@@ -365,7 +386,7 @@ const r = ({ technician, state, activeStatuses }) => {
render: (text, record) => (
<ProductionListEmployeeAssignment
record={record}
type="employee_refinish_rel"
type="employee_refinish"
/>
),
},

View File

@@ -49,7 +49,7 @@ export function ProductionListEmpAssignment({
const result = await updateJob({
variables: { jobId: record.id, job: { [empAssignment]: employeeid } },
awaitRefetchQueries: true,
// awaitRefetchQueries: true,
});
insertAuditTrail({
@@ -145,13 +145,18 @@ export function ProductionListEmpAssignment({
</Row>
);
let theEmployee;
if (record[type])
theEmployee = bodyshop.employees.find((e) => e.id === record[type]);
return (
<Popover destroyTooltipOnHide content={popContent} visible={visibility}>
<Spin spinning={loading}>
{record[type] ? (
<div>
<span>{`${record[type].first_name || ""} ${
record[type].last_name || ""
<span>{`${theEmployee.first_name || ""} ${
theEmployee.last_name || ""
}`}</span>
<DeleteFilled
style={iconStyle}
@@ -174,13 +179,13 @@ export function ProductionListEmpAssignment({
const determineFieldName = (operation) => {
switch (operation) {
case "employee_body_rel":
case "employee_body":
return "employee_body";
case "employee_prep_rel":
case "employee_prep":
return "employee_prep";
case "employee_refinish_rel":
case "employee_refinish":
return "employee_refinish";
case "employee_csr_rel":
case "employee_csr":
return "employee_csr";
default:
return null;

View File

@@ -29,7 +29,11 @@ export function ProductionLastContacted({ currentUser, record }) {
const [visible, setVisible] = useState(false);
const { t } = useTranslation();
const [form] = Form.useForm();
const handleFinish = async ({ date_last_contacted, note }) => {
const handleFinish = async ({
date_last_contacted,
date_next_contact,
note,
}) => {
logImEXEvent("production_last_contacted");
//e.stopPropagation();
@@ -38,6 +42,7 @@ export function ProductionLastContacted({ currentUser, record }) {
jobId: record.id,
job: {
date_last_contacted,
...(date_next_contact ? { date_next_contact } : {}),
},
},
});
@@ -98,7 +103,16 @@ export function ProductionLastContacted({ currentUser, record }) {
onClick={(e) => e.stopPropagation()}
>
<Form form={form} onFinish={handleFinish} layout="vertical">
<Form.Item name="date_last_contacted">
<Form.Item
name="date_last_contacted"
label={t("jobs.fields.date_last_contacted")}
>
<FormDateTimePickerComponent />
</Form.Item>
<Form.Item
name="date_next_contact"
label={t("jobs.fields.date_next_contact")}
>
<FormDateTimePickerComponent />
</Form.Item>
<Form.Item label={t("notes.labels.notetoadd")} name="note">

View File

@@ -125,6 +125,7 @@ export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
scheduled_completion
scheduled_delivery
date_last_contacted
date_next_contact
ins_co_nm
clm_total
ownr_ph1
@@ -134,39 +135,10 @@ export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
production_vars
kanbanparent
alt_transport
joblines_status {
part_type
count
status
}
employee_body
employee_body_rel {
id
first_name
last_name
}
employee_refinish
employee_refinish_rel {
id
first_name
last_name
}
employee_prep
employee_prep_rel {
id
first_name
last_name
}
employee_csr_rel {
id
first_name
last_name
}
partcount: joblines_aggregate(where: { removed: { _eq: false } }) {
nodes {
status
}
}
employee_csr
labhrs: joblines_aggregate(
where: {
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
@@ -519,6 +491,7 @@ export const GET_JOB_BY_PK = gql`
date_scheduled
date_invoiced
date_last_contacted
date_next_contact
date_exported
status
owner_owing
@@ -762,6 +735,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
scheduled_delivery
date_invoiced
date_last_contacted
date_next_contact
date_open
date_exported
@@ -848,6 +822,7 @@ export const QUERY_TECH_JOB_DETAILS = gql`
scheduled_delivery
date_invoiced
date_last_contacted
date_next_contact
date_open
date_exported
voided

View File

@@ -1207,6 +1207,7 @@
"date_exported": "Exported",
"date_invoiced": "Invoiced",
"date_last_contacted": "Last Contacted Date",
"date_next_contact": "Next Contact Date",
"date_open": "Open",
"date_scheduled": "Scheduled",
"ded_amt": "Deductible",
@@ -1727,7 +1728,8 @@
"new": "New Conversation"
},
"errors": {
"invalidphone": "The phone number is invalid. Unable to open conversation. "
"invalidphone": "The phone number is invalid. Unable to open conversation. ",
"noattachedjobs": "No jobs have been associated to this conversation. "
},
"labels": {
"archive": "Archive",
@@ -1737,6 +1739,7 @@
"nojobs": "Not associated to any job.",
"phonenumber": "Phone #",
"presets": "Presets",
"recentonly": "Only your most recent 50 conversations will be shown here. If you are looking for an older conversation, find the related contact and click their phone number to view the conversation.",
"selectmedia": "Select Media",
"sentby": "Sent by {{by}} at {{time}}",
"typeamessage": "Send a message...",

View File

@@ -1207,6 +1207,7 @@
"date_exported": "Exportado",
"date_invoiced": "Facturado",
"date_last_contacted": "",
"date_next_contact": "",
"date_open": "Abierto",
"date_scheduled": "Programado",
"ded_amt": "Deducible",
@@ -1727,7 +1728,8 @@
"new": ""
},
"errors": {
"invalidphone": ""
"invalidphone": "",
"noattachedjobs": ""
},
"labels": {
"archive": "",
@@ -1737,6 +1739,7 @@
"nojobs": "",
"phonenumber": "",
"presets": "",
"recentonly": "",
"selectmedia": "",
"sentby": "",
"typeamessage": "Enviar un mensaje...",

View File

@@ -1207,6 +1207,7 @@
"date_exported": "Exportés",
"date_invoiced": "Facturé",
"date_last_contacted": "",
"date_next_contact": "",
"date_open": "Ouvrir",
"date_scheduled": "Prévu",
"ded_amt": "Déductible",
@@ -1727,7 +1728,8 @@
"new": ""
},
"errors": {
"invalidphone": ""
"invalidphone": "",
"noattachedjobs": ""
},
"labels": {
"archive": "",
@@ -1737,6 +1739,7 @@
"nojobs": "",
"phonenumber": "",
"presets": "",
"recentonly": "",
"selectmedia": "",
"sentby": "",
"typeamessage": "Envoyer un message...",

View File

@@ -2592,6 +2592,7 @@
- date_exported
- date_invoiced
- date_last_contacted
- date_next_contact
- date_open
- date_scheduled
- ded_amt
@@ -2843,6 +2844,7 @@
- date_exported
- date_invoiced
- date_last_contacted
- date_next_contact
- date_open
- date_scheduled
- ded_amt
@@ -3104,6 +3106,7 @@
- date_exported
- date_invoiced
- date_last_contacted
- date_next_contact
- date_open
- date_scheduled
- ded_amt

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" alter column "date_next_contact" drop not null;
alter table "public"."jobs" add column "date_next_contact" timestamptz;

View File

@@ -0,0 +1 @@
alter table "public"."jobs" drop column "date_next_contact" cascade;

View File

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

View File

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

View File

@@ -30,19 +30,58 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
const JobData = await QueryJobData(socket, jobid);
socket.JobData = JobData;
//Query for the Vehicle record to get the associated customer.
// socket.DmsVeh = await QueryVehicleFromDms(socket);
//Todo: Need to validate the lines and methods below.
if (socket.DmsVeh && socket.DmsVeh.CustomerRef) {
//Get the associated customer from the Vehicle Record.
socket.DMSVehCustomer = await QueryCustomerBycodeFromDms(
socket,
socket.DmsVeh.CustomerRef
);
}
socket.DMSCustList = await QueryCustomersFromDms(socket);
socket.emit("pbs-select-customer", [
...(socket.DMSVehCustomer
? [{ ...socket.DMSVehCustomer, vinOwner: true }]
: []),
...socket.DMSCustList,
]);
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error encountered in PbsJobExport. ${error}`
);
}
};
exports.PbsSelectedCustomer = async function PbsSelectedCustomer(
socket,
selectedCustomerId
) {
try {
CdkBase.createLogEvent(
socket,
"DEBUG",
`User selected customer ${selectedCustomerId || "NEW"}`
);
//Upsert the contact information as per Wafaa's Email.
CdkBase.createLogEvent(
socket,
"DEBUG",
`Upserting contact information to DMS for ${socket.JobData.ownr_fn} ${socket.JobData.ownr_ln} ${socket.JobData.ownr_co_nm}`
);
const ownerRef = await UpsertContactData(socket);
const ownerRef = await UpsertContactData(socket, selectedCustomerId);
CdkBase.createLogEvent(
socket,
"DEBUG",
`Upserting vehicle information to DMS for ${socket.JobData.v_vin}`
);
// await UpsertVehicleData(socket, ownerRef.ReferenceId);
await UpsertVehicleData(socket, ownerRef.ReferenceId);
CdkBase.createLogEvent(socket, "DEBUG", `Inserting account data.`);
await InsertAccountPostingData(socket);
CdkBase.createLogEvent(socket, "DEBUG", `Marking job as exported.`);
@@ -53,24 +92,29 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error encountered in PbsJobExport. ${error}`
`Error encountered in CdkSelectedCustomer. ${error}`
);
await InsertFailedExportLog(socket, error);
}
};
async function CheckForErrors(socket, response) {
if (response.WasSuccessful) {
return;
if (response.WasSuccessful === undefined || response.WasSuccessful === true) {
CdkBase.createLogEvent(
socket,
"DEBUG",
`Succesful response from DMS. ${response.Message || ""}`
);
} else {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error received from DMS:. ${response.Message}`
`Error received from DMS: ${response.Message}`
);
CdkBase.createLogEvent(
socket,
"TRACE",
`Error received from DMS:. ${JSON.stringify(response)}`
`Error received from DMS: ${JSON.stringify(response)}`
);
}
}
@@ -89,14 +133,127 @@ async function QueryJobData(socket, jobid) {
return result.jobs_by_pk;
}
async function UpsertContactData(socket) {
async function QueryVehicleFromDms(socket) {
try {
const { data: VehicleGetResponse } = await axios.post(
PBS_ENDPOINTS.VehicleGet,
{
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
// VehicleId: "00000000000000000000000000000000",
// Year: "String",
// Make: "String",
// Model: "String",
// Trim: "String",
// ModelNumber: "String",
// StockNumber: "String",
VIN: socket.JobData.v_vin,
// LicenseNumber: "String",
// Lot: "String",
// Status: "String",
// StatusList: ["String"],
// OwnerRef: "00000000000000000000000000000000",
// ModifiedSince: "0001-01-01T00:00:00.0000000Z",
// ModifiedUntil: "0001-01-01T00:00:00.0000000Z",
// LastSaleSince: "0001-01-01T00:00:00.0000000Z",
// VehicleIDList: ["00000000000000000000000000000000"],
// IncludeInactive: false,
// IncludeBuildVehicles: false,
// ShortVIN: "String",
},
{ auth: PBS_CREDENTIALS }
);
CheckForErrors(socket, VehicleGetResponse);
return VehicleGetResponse;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in QueryVehicleFromDms - ${error}`
);
throw new Error(error);
}
}
async function QueryCustomersFromDms(socket) {
try {
const { data: CustomerGetResponse } = await axios.post(
PBS_ENDPOINTS.ContactGet,
{
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
//ContactId: "00000000000000000000000000000000",
ContactCode: socket.JobData.owner.accountingid,
FirstName: socket.JobData.ownr_co_nm
? socket.JobData.ownr_co_nm
: socket.JobData.ownr_fn,
LastName: socket.JobData.ownr_ln,
PhoneNumber: socket.JobData.ownr_ph1,
// EmailAddress: "String",
// ModifiedSince: "0001-01-01T00:00:00.0000000Z",
// ModifiedUntil: "0001-01-01T00:00:00.0000000Z",
// ContactIdList: ["00000000000000000000000000000000"],
// IncludeInactive: false,
// PayableAccount: "String",
// ReceivableAccount: "String",
// DriverLicense: "String",
// ZipCode: "String",
},
{ auth: PBS_CREDENTIALS }
);
CheckForErrors(socket, CustomerGetResponse);
return CustomerGetResponse && CustomerGetResponse.Contacts;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in QueryCustomersFromDms - ${error}`
);
throw new Error(error);
}
}
async function QueryCustomerBycodeFromDms(socket, CustomerRef) {
try {
const { data: CustomerGetResponse } = await axios.post(
PBS_ENDPOINTS.ContactGet,
{
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
ContactId: CustomerRef,
//ContactCode: socket.JobData.owner.accountingid,
//FirstName: socket.JobData.ownr_co_nm
// ? socket.JobData.ownr_co_nm
// : socket.JobData.ownr_fn,
//LastName: socket.JobData.ownr_ln,
//PhoneNumber: socket.JobData.ownr_ph1,
// EmailAddress: "String",
// ModifiedSince: "0001-01-01T00:00:00.0000000Z",
// ModifiedUntil: "0001-01-01T00:00:00.0000000Z",
// ContactIdList: ["00000000000000000000000000000000"],
// IncludeInactive: false,
// PayableAccount: "String",
// ReceivableAccount: "String",
// DriverLicense: "String",
// ZipCode: "String",
},
{ auth: PBS_CREDENTIALS }
);
CheckForErrors(socket, CustomerGetResponse);
return CustomerGetResponse && CustomerGetResponse.Contacts;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in QueryCustomersFromDms - ${error}`
);
throw new Error(error);
}
}
async function UpsertContactData(socket, selectedCustomerId) {
try {
const { data: ContactChangeResponse } = await axios.post(
PBS_ENDPOINTS.ContactChange,
{
ContactInfo: {
// Id: socket.JobData.owner.id,
ContactId: socket.JobData.owner.id,
...(selectedCustomerId ? { ContactId: selectedCustomerId } : {}),
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
Code: socket.JobData.owner.accountingid,
...(socket.JobData.ownr_co_nm
@@ -393,20 +550,6 @@ async function InsertAccountPostingData(socket) {
}
});
console.log(
JSON.stringify({
SerialNumber: socket.JobData.bodyshop.pbs_serialnumber,
Posting: {
Reference: socket.JobData.ro_number,
JournalCode: socket.txEnvelope.journal,
TransactionDate: moment(socket.JobData.date_invoiced).toISOString(), //"0001-01-01T00:00:00.0000000Z",
Description: socket.txEnvelope.story,
//AdditionalInfo: "String",
Source: "ImEX Online",
Lines: wips,
},
})
);
const { data: AccountPostingChange } = await axios.post(
PBS_ENDPOINTS.AccountingPostingChange,
{
@@ -463,3 +606,20 @@ async function MarkJobExported(socket, jobid) {
return result;
}
async function InsertFailedExportLog(socket, error) {
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.INSERT_EXPORT_LOG, {
log: {
bodyshopid: socket.JobData.bodyshop.id,
jobid: socket.JobData.id,
successful: false,
message: [error],
useremail: socket.user.email,
},
});
return result;
}

View File

@@ -70,11 +70,15 @@ exports.default = async (req, res) => {
}
var ret = builder
.create(autoHouseObject, {
version: "1.0",
encoding: "UTF-8",
})
.end({ pretty: true, allowEmptyTags: true });
.create(
{
version: "1.0",
encoding: "UTF-8",
keepNullNodes: true,
},
autoHouseObject
)
.end({ allowEmptyTags: true });
allxmlsToUpload.push({
xml: ret,
@@ -107,6 +111,11 @@ exports.default = async (req, res) => {
}
}
if (process.env.NODE_ENV !== "production") {
res.sendStatus(200);
return;
}
let sftp = new Client();
sftp.on("error", (errors) =>
logger.log("autohouse-sftp-error", "ERROR", "api", null, {
@@ -239,7 +248,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
ReferralDate: null,
EstimateAppointmentDate: null,
SecondFollowUpDate: null,
AssignedDate: null,
AssignedDate:
job.asgn_date && moment(job.asgn_date).format(AhDateFormat),
EstComplete: null,
CustomerAuthorizationDate: null,
InsuranceAuthorizationDate: null,
@@ -248,7 +258,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
job.scheduled_in && moment(job.scheduled_in).format(AhDateFormat),
CarinShop: job.actual_in && moment(job.actual_in).format(AhDateFormat),
InsInspDate: null,
StartDate: null,
StartDate: job.actual_in && moment(job.actual_in).format(AhDateFormat),
PartsOrder: null,
TeardownHold: null,
SupplementSubmittedDate: null,
@@ -287,7 +297,10 @@ const CreateRepairOrderTag = (job, errorCallback) => {
StructuralRate: job.rate_las,
PMRate: job.rate_mapa,
BMRate: job.rate_mash,
TaxRate: null,
TaxRate:
job.parts_tax_rates &&
job.parts_tax_rates.PAN &&
job.parts_tax_rates.PAN.prt_tax_rt,
StorageRateperDay: null,
DaysStored: null,
},
@@ -389,39 +402,45 @@ const CreateRepairOrderTag = (job, errorCallback) => {
job.job_totals.parts.parts.list.PAA.total
).toFormat(AHDineroFormat),
PartsAMCost: repairCosts.PartsAMCost.toFormat(AHDineroFormat),
PartsReconditioned: null,
PartsReconditioned:
repairCosts.PartsReconditionedCost.toFormat(AHDineroFormat),
PartsReconditionedCost:
repairCosts.PartsReconditionedCost.toFormat(AHDineroFormat),
PartsRecycled: Dinero(
job.job_totals.parts.parts.list.PAR &&
job.job_totals.parts.parts.list.PAR.total
).toFormat(AHDineroFormat),
PartsRecycledCost: null,
PartsRecycledCost:
repairCosts.PartsRecycledCost.toFormat(AHDineroFormat),
PartsOther: Dinero(
job.job_totals.parts.parts.list.PAO &&
job.job_totals.parts.parts.list.PAO.total
).toFormat(AHDineroFormat),
PartsOtherCost: null,
PartsOtherCost: repairCosts.PartsOtherCost.toFormat(AHDineroFormat),
SubletTotal: Dinero(job.job_totals.parts.sublets.total).toFormat(
AHDineroFormat
),
SubletTotalCost: 0,
SubletTotalCost: repairCosts.SubletTotalCost.toFormat(AHDineroFormat),
BodyLaborTotal: Dinero(job.job_totals.rates.lab.total).toFormat(
AHDineroFormat
),
BodyLaborTotalCost: 0,
BodyLaborTotalCost:
repairCosts.BodyLaborTotalCost.toFormat(AHDineroFormat),
RefinishLaborTotal: Dinero(job.job_totals.rates.lar.total).toFormat(
AHDineroFormat
),
RefinishLaborTotalCost: 0,
RefinishLaborTotalCost:
repairCosts.RefinishLaborTotalCost.toFormat(AHDineroFormat),
MechanicalLaborTotal: Dinero(job.job_totals.rates.lam.total).toFormat(
AHDineroFormat
),
MechanicalLaborTotalCost: 0,
MechanicalLaborTotalCost:
repairCosts.MechanicalLaborTotalCost.toFormat(AHDineroFormat),
StructuralLaborTotal: Dinero(job.job_totals.rates.las.total).toFormat(
AHDineroFormat
),
StructuralLaborTotalCost: null,
StructuralLaborTotalCost:
repairCosts.StructuralLaborTotalCost.toFormat(AHDineroFormat),
MiscellaneousChargeTotal: null,
MiscellaneousChargeTotalCost: null,
PMTotal: Dinero(job.job_totals.rates.mapa.total).toFormat(
@@ -437,11 +456,11 @@ const CreateRepairOrderTag = (job, errorCallback) => {
TowingTotal: Dinero(job.job_totals.additional.towing).toFormat(
AHDineroFormat
),
TowingTotalCost: null,
TowingTotalCost: repairCosts.TowingTotalCost.toFormat(AHDineroFormat),
StorageTotal: Dinero(job.job_totals.additional.storage).toFormat(
AHDineroFormat
),
StorageTotalCost: null,
StorageTotalCost: repairCosts.StorageTotalCost.toFormat(AHDineroFormat),
DetailTotal: null,
DetailTotalCost: null,
SalesTaxTotal: Dinero(job.job_totals.totals.local_tax)
@@ -472,7 +491,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
Hub50Comment: null,
DateofChange: null,
BodyTechName: null,
TotalLossYN: null,
TotalLossYN: job.tlos_ind ? "Y" : "N",
InsScreenCommentsLine1: null,
InsScreenCommentsLine2: null,
AssignmentCaller: null,
@@ -483,7 +502,9 @@ const CreateRepairOrderTag = (job, errorCallback) => {
PaintTechName: null,
ImportType: null,
ImportFile: null,
GSTTax: null,
GSTTax: Dinero(job.job_totals.totals.federal_tax).toFormat(
AHDineroFormat
),
RepairDelayStatusCode: null,
RepairDelaycomment: null,
AgentMktgID: null,

View File

@@ -554,6 +554,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
rate_mash
job_totals
driveable
parts_tax_rates
bodyshop {
id
shopname

View File

@@ -17,7 +17,7 @@ const CdkCalculateAllocations =
require("../cdk/cdk-calculate-allocations").default;
const { isArray } = require("lodash");
const logger = require("../utils/logger");
const PbsExportJob = require("../accounting/pbs/pbs-job-export").default;
const {default: PbsExportJob, PbsSelectedCustomer} = require("../accounting/pbs/pbs-job-export");
io.use(function (socket, next) {
try {
@@ -113,6 +113,15 @@ io.on("connection", (socket) => {
socket.on("pbs-export-job", (jobid) => {
PbsExportJob(socket, jobid);
});
socket.on("pbs-selected-customer", (selectedCustomerId) => {
createLogEvent(
socket,
"DEBUG",
`User selected customer ID ${selectedCustomerId}`
);
socket.selectedCustomerId = selectedCustomerId;
PbsSelectedCustomer(socket, selectedCustomerId);
});
//End PBS
socket.on("disconnect", () => {