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

Release/2022 01 21
This commit is contained in:
Patrick Fic
2022-01-21 22:21:45 +00:00
29 changed files with 766 additions and 319 deletions

View File

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

View File

@@ -91,13 +91,13 @@
color: blue; color: blue;
} }
.production-completion-1 { .production-completion-soon {
animation: production-completion-1-blinker 5s linear infinite; color: rgba(255, 140, 0, 0.8);
font-weight: bold;
} }
@keyframes production-completion-1-blinker { .production-completion-past {
50% { color: rgba(255, 0, 0, 0.8);
background: rgba(207, 12, 12, 0.555); font-weight: bold;
}
} }
.react-resizable { .react-resizable {

View File

@@ -5,21 +5,25 @@ import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBreadcrumbs } from "../../redux/application/application.selectors"; import { selectBreadcrumbs } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import GlobalSearch from "../global-search/global-search.component"; import GlobalSearch from "../global-search/global-search.component";
import "./breadcrumbs.styles.scss"; import "./breadcrumbs.styles.scss";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
breadcrumbs: selectBreadcrumbs, breadcrumbs: selectBreadcrumbs,
bodyshop: selectBodyshop,
}); });
export function BreadCrumbs({ breadcrumbs }) { export function BreadCrumbs({ breadcrumbs, bodyshop }) {
return ( return (
<Row className="breadcrumb-container"> <Row className="breadcrumb-container">
<Col xs={24} sm={24} md={16}> <Col xs={24} sm={24} md={16}>
<Breadcrumb separator=">"> <Breadcrumb separator=">">
<Breadcrumb.Item> <Breadcrumb.Item>
<Link to={`/manage`}> <Link to={`/manage`}>
<HomeFilled /> <HomeFilled />{" "}
{(bodyshop && bodyshop.shopname && `(${bodyshop.shopname})`) ||
""}
</Link> </Link>
</Breadcrumb.Item> </Breadcrumb.Item>
{breadcrumbs.map((item) => {breadcrumbs.map((item) =>

View File

@@ -171,7 +171,12 @@ export function JobChecklistForm({
}); });
} }
}; };
console.log(job,{
removeFromProduction: true,
actual_completion:
job && job.actual_completion && moment(job.actual_completion),
actual_delivery: job && job.actual_delivery && moment(job.actual_delivery),
});
return ( return (
<Card <Card
title={t("checklist.labels.checklist")} title={t("checklist.labels.checklist")}
@@ -195,21 +200,26 @@ export function JobChecklistForm({
addToProduction: true, addToProduction: true,
allow_text_message: job.owner && job.owner.allow_text_message, allow_text_message: job.owner && job.owner.allow_text_message,
scheduled_completion: scheduled_completion:
(job && job.scheduled_completion) || (job &&
(job.labbrs && job.larhrs job.scheduled_completion &&
? moment().businessAdd( moment(job.scheduled_completion)) ||
(job.labhrs.aggregate.sum.mod_lb_hrs + (job &&
job.larhrs.aggregate.sum.mod_lb_hrs) / job.labhrs &&
bodyshop.target_touchtime, job.larhrs &&
"days" moment().businessAdd(
) (job.labhrs.aggregate.sum.mod_lb_hrs ||
: null), 0 + job.larhrs.aggregate.sum.mod_lb_hrs ||
scheduled_delivery: job && job.scheduled_delivery, 0) / bodyshop.target_touchtime,
"days"
)),
scheduled_delivery: job && moment(job.scheduled_delivery),
}), }),
...(type === "deliver" && { ...(type === "deliver" && {
removeFromProduction: true, removeFromProduction: true,
actual_completion: job && job.actual_completion, actual_completion:
actual_delivery: job && job.actual_delivery, job && job.actual_completion && moment(job.actual_completion),
actual_delivery:
job && job.actual_delivery && moment(job.actual_delivery),
}), }),
...formItems ...formItems
.filter((fi) => fi.value) .filter((fi) => fi.value)

View File

@@ -36,7 +36,7 @@ export function JobsDocumentsDownloadButton({
); );
const imagesToDownload = [ const imagesToDownload = [
...galleryImages.images.filter((image) => image.isSelected), ...galleryImages.images.filter((image) => image.isSelected),
// ...galleryImages.other.filter((image) => image.isSelected), ...galleryImages.other.filter((image) => image.isSelected),
]; ];
function downloadProgress(progressEvent) { function downloadProgress(progressEvent) {
@@ -123,6 +123,7 @@ export function JobsDocumentsDownloadButton({
a.click(); a.click();
} }
}; };
return ( return (
<> <>
<Button <Button

View File

@@ -12,6 +12,7 @@ import ProductionAlert from "../production-list-columns/production-list-columns.
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component"; import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component"; import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
import "./production-board-card.styles.scss"; import "./production-board-card.styles.scss";
import moment from "moment";
export default function ProductionBoardCard( export default function ProductionBoardCard(
technician, technician,
@@ -37,6 +38,15 @@ export default function ProductionBoardCard(
// employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr); // employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
// } // }
const pastDueAlert =
!!card.scheduled_completion &&
((moment().isSameOrAfter(moment(card.scheduled_completion), "day") &&
"production-completion-past") ||
(moment()
.add(1, "day")
.isSame(moment(card.scheduled_completion), "day") &&
"production-completion-soon"));
return ( return (
<Card <Card
className="react-kanban-card imex-kanban-card" className="react-kanban-card imex-kanban-card"
@@ -145,7 +155,7 @@ export default function ProductionBoardCard(
cardSettings.scheduled_completion && cardSettings.scheduled_completion &&
card.scheduled_completion && ( card.scheduled_completion && (
<Col span={cardSettings && cardSettings.compact ? 24 : 12}> <Col span={cardSettings && cardSettings.compact ? 24 : 12}>
<Space> <Space className={pastDueAlert}>
<CalendarOutlined /> <CalendarOutlined />
<DateTimeFormatter format="MM/DD"> <DateTimeFormatter format="MM/DD">
{card.scheduled_completion} {card.scheduled_completion}

View File

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

View File

@@ -8,10 +8,12 @@ import { DateFormatter } from "../../utils/DateFormatter";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
const OneCalendarDay = 60 * 60 * 24 * 1000; export default function ProductionListDate({
const Now = new Date(); record,
field,
export default function ProductionListDate({ record, field, time }) { time,
pastIndicator,
}) {
const [updateAlert] = useMutation(UPDATE_JOB); const [updateAlert] = useMutation(UPDATE_JOB);
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
@@ -34,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 ( return (
<div> <div>
<Dropdown <Dropdown
@@ -72,17 +83,9 @@ export default function ProductionListDate({ record, field, time }) {
style={{ style={{
height: "19px", height: "19px",
}} }}
className={className}
> >
<DateFormatter <DateFormatter bordered={false}>{record[field]}</DateFormatter>
bordered={false}
className={
!!record[field] && new Date(record[field]) - Now < OneCalendarDay
? "production-completion-1"
: ""
}
>
{record[field]}
</DateFormatter>
</div> </div>
</Dropdown> </Dropdown>
</div> </div>

View File

@@ -4,9 +4,24 @@ import { TemplateList } from "../../utils/TemplateConstants";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocument } from "../../utils/RenderTemplate";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const ProdTemplates = TemplateList("production"); const ProdTemplates = TemplateList("production");
const ProductionByTech = TemplateList("special").production_by_technician_one;
export default function ProductionListPrint() { const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionListPrint);
export function ProductionListPrint({ bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
return ( return (
@@ -33,6 +48,29 @@ export default function ProductionListPrint() {
{ProdTemplates[key].title} {ProdTemplates[key].title}
</Menu.Item> </Menu.Item>
))} ))}
<Menu.SubMenu
title={t("reportcenter.templates.production_by_technician_one")}
>
{bodyshop.employees.map((e) => (
<Menu.Item
key={e.id}
onClick={async () => {
setLoading(true);
await GenerateDocument(
{
name: ProductionByTech.key,
variables: { id: e.id },
},
{},
"p"
);
setLoading(false);
}}
>
{e.first_name} {e.last_name}
</Menu.Item>
))}
</Menu.SubMenu>
</Menu> </Menu>
} }
> >

View File

@@ -1,5 +1,5 @@
import { useMutation } from "@apollo/client"; 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 axios from "axios";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; 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 { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import TechClockInComponent from "./tech-job-clock-in-form.component"; import TechClockInComponent from "./tech-job-clock-in-form.component";
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
technician: selectTechnician, technician: selectTechnician,
@@ -71,9 +72,16 @@ export function TechClockInContainer({ technician, bodyshop }) {
<Card <Card
title={t("timetickets.labels.clockintojob")} title={t("timetickets.labels.clockintojob")}
extra={ extra={
<Button type="primary" onClick={() => form.submit()} loading={loading}> <Space wrap>
{t("timetickets.actions.clockin")} <TechJobPrintTickets />
</Button> <Button
type="primary"
onClick={() => form.submit()}
loading={loading}
>
{t("timetickets.actions.clockin")}
</Button>
</Space>
} }
> >
<Form form={form} layout="vertical" onFinish={handleFinish}> <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

@@ -262,6 +262,9 @@ export const QUERY_DELIVER_CHECKLIST = gql`
jobs_by_pk(id: $jobId) { jobs_by_pk(id: $jobId) {
id id
ro_number ro_number
actual_completion
actual_delivery
} }
} }
`; `;

View File

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

View File

@@ -56,7 +56,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
jobId: job.id, jobId: job.id,
job: { job: {
status: bodyshop.md_ro_statuses.default_invoiced || "", status: bodyshop.md_ro_statuses.default_invoiced || "",
date_invoiced: new Date(), date_invoiced: values.date_invoiced,
actual_in: values.actual_in, actual_in: values.actual_in,
actual_completion: values.actual_completion, actual_completion: values.actual_completion,
actual_delivery: values.actual_delivery, actual_delivery: values.actual_delivery,
@@ -119,6 +119,9 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
actual_delivery: job.actual_delivery actual_delivery: job.actual_delivery
? moment(job.actual_delivery) ? moment(job.actual_delivery)
: job.scheduled_delivery && moment(job.scheduled_delivery), : job.scheduled_delivery && moment(job.scheduled_delivery),
date_invoiced: job.date_invoiced
? moment(job.date_invoiced)
: moment(),
kmin: job.kmin, kmin: job.kmin,
kmout: job.kmout, kmout: job.kmout,
dms_allocation: job.dms_allocation, dms_allocation: job.dms_allocation,
@@ -219,6 +222,32 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
> >
<DateTimePicker disabled={jobRO} /> <DateTimePicker disabled={jobRO} />
</Form.Item> </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) && ( {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<Form.Item <Form.Item
label={t("jobs.fields.kmin")} label={t("jobs.fields.kmin")}

View File

@@ -69,6 +69,7 @@ export function JobsDeliverContainer({
checklistConfig={ checklistConfig={
(data && data.bodyshops_by_pk.deliverchecklist) || {} (data && data.bodyshops_by_pk.deliverchecklist) || {}
} }
job={data ? data.jobs_by_pk : {}}
/> />
</div> </div>
</RbacWrapper> </RbacWrapper>

View File

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

View File

@@ -1507,6 +1507,7 @@
"diskscan": "Scan Disk for Estimates", "diskscan": "Scan Disk for Estimates",
"dms": { "dms": {
"defaultstory": "Bodyshop RO {{ro_number}}. Damage to $t(jobs.fields.area_of_damage_impact.{{area_of_damage}}).", "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.", "kmoutnotgreaterthankmin": "Mileage out must be greater than mileage in.",
"logs": "Logs", "logs": "Logs",
"notallocated": "Not Allocated", "notallocated": "Not Allocated",
@@ -2267,6 +2268,7 @@
"production_by_ro": "Production by RO", "production_by_ro": "Production by RO",
"production_by_target_date": "Production by Target Date", "production_by_target_date": "Production by Target Date",
"production_by_technician": "Production by Technician", "production_by_technician": "Production by Technician",
"production_by_technician_one": "Production filtered by Technician",
"purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)", "purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)",
"purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)", "purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)",
"purchases_by_date_range_detail": "Purchases by Date - Detail", "purchases_by_date_range_detail": "Purchases by Date - Detail",

View File

@@ -1507,6 +1507,7 @@
"diskscan": "", "diskscan": "",
"dms": { "dms": {
"defaultstory": "", "defaultstory": "",
"invoicedatefuture": "",
"kmoutnotgreaterthankmin": "", "kmoutnotgreaterthankmin": "",
"logs": "", "logs": "",
"notallocated": "", "notallocated": "",
@@ -2267,6 +2268,7 @@
"production_by_ro": "", "production_by_ro": "",
"production_by_target_date": "", "production_by_target_date": "",
"production_by_technician": "", "production_by_technician": "",
"production_by_technician_one": "",
"purchases_by_cost_center_detail": "", "purchases_by_cost_center_detail": "",
"purchases_by_cost_center_summary": "", "purchases_by_cost_center_summary": "",
"purchases_by_date_range_detail": "", "purchases_by_date_range_detail": "",

View File

@@ -1507,6 +1507,7 @@
"diskscan": "", "diskscan": "",
"dms": { "dms": {
"defaultstory": "", "defaultstory": "",
"invoicedatefuture": "",
"kmoutnotgreaterthankmin": "", "kmoutnotgreaterthankmin": "",
"logs": "", "logs": "",
"notallocated": "", "notallocated": "",
@@ -2267,6 +2268,7 @@
"production_by_ro": "", "production_by_ro": "",
"production_by_target_date": "", "production_by_target_date": "",
"production_by_technician": "", "production_by_technician": "",
"production_by_technician_one": "",
"purchases_by_cost_center_detail": "", "purchases_by_cost_center_detail": "",
"purchases_by_cost_center_summary": "", "purchases_by_cost_center_summary": "",
"purchases_by_date_range_detail": "", "purchases_by_date_range_detail": "",

View File

@@ -1590,14 +1590,6 @@ export const TemplateList = (type, context) => {
//idtype: "vendor", //idtype: "vendor",
disabled: false, disabled: false,
}, },
production_by_technician: {
title: i18n.t("reportcenter.templates.production_by_technician"),
description: "",
subject: i18n.t("reportcenter.templates.production_by_technician"),
key: "production_by_technician",
//idtype: "vendor",
disabled: false,
},
production_by_category: { production_by_category: {
title: i18n.t("reportcenter.templates.production_by_category"), title: i18n.t("reportcenter.templates.production_by_category"),
description: "", description: "",
@@ -1606,6 +1598,14 @@ export const TemplateList = (type, context) => {
//idtype: "vendor", //idtype: "vendor",
disabled: false, disabled: false,
}, },
production_by_technician: {
title: i18n.t("reportcenter.templates.production_by_technician"),
description: "",
subject: i18n.t("reportcenter.templates.production_by_technician"),
key: "production_by_technician",
//idtype: "vendor",
disabled: false,
},
} }
: {}), : {}),
...(!type || type === "special" ...(!type || type === "special"
@@ -1617,6 +1617,18 @@ export const TemplateList = (type, context) => {
key: "ca_bc_etf_table", key: "ca_bc_etf_table",
disabled: false, disabled: false,
}, },
production_by_technician_one: {
title: i18n.t(
"reportcenter.templates.production_by_technician_one"
),
description: "",
subject: i18n.t(
"reportcenter.templates.production_by_technician_one"
),
key: "production_by_technician_one",
//idtype: "vendor",
disabled: false,
},
} }
: {}), : {}),
}; };

View File

@@ -30,7 +30,7 @@ axios.interceptors.request.use((x) => {
} | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`; } | ${JSON.stringify(x.data)} | ${JSON.stringify(headers)}`;
console.log(printable); console.log(printable);
CdkBase.createLogEvent(socket, "TRACE", `Raw Request: ${printable}`); CdkBase.createJsonEvent(socket, "TRACE", `Raw Request: ${printable}`, x.data);
return x; return x;
}); });
@@ -42,7 +42,12 @@ axios.interceptors.response.use((x) => {
x.data x.data
)}`; )}`;
console.log(printable); console.log(printable);
CdkBase.createLogEvent(socket, "TRACE", `Raw Response: ${printable}`); CdkBase.createJsonEvent(
socket,
"TRACE",
`Raw Response: ${printable}`,
x.data
);
return x; return x;
}); });

View File

@@ -38,7 +38,9 @@ exports.default = function ({
if ( if (
(jobline.prt_dsmk_p && jobline.prt_dsmk_p !== 0) || (jobline.prt_dsmk_p && jobline.prt_dsmk_p !== 0) ||
((jobline.db_ref === "900511" || jobline.db_ref === "900510") && ((jobline.db_ref === "900511" ||
jobline.db_ref === "900510" ||
jobline.db_ref === "900500") &&
jobline.prt_dsmk_m && jobline.prt_dsmk_m &&
jobline.prt_dsmk_m !== 0) jobline.prt_dsmk_m !== 0)
) { ) {

View File

@@ -72,7 +72,9 @@ exports.default = async function (socket, jobid) {
if ( if (
(val.prt_dsmk_p && val.prt_dsmk_p !== 0) || (val.prt_dsmk_p && val.prt_dsmk_p !== 0) ||
((val.db_ref === "900511" || val.db_ref === "900510") && ((val.db_ref === "900511" ||
val.db_ref === "900510" ||
val.db_ref === "900500") &&
val.prt_dsmk_m && val.prt_dsmk_m &&
val.prt_dsmk_m !== 0) val.prt_dsmk_m !== 0)
) { ) {

View File

@@ -2,6 +2,7 @@ const path = require("path");
const queries = require("../graphql-client/queries"); const queries = require("../graphql-client/queries");
const Dinero = require("dinero.js"); const Dinero = require("dinero.js");
const moment = require("moment"); const moment = require("moment");
const fs = require("fs");
const _ = require("lodash"); const _ = require("lodash");
const logger = require("../utils/logger"); const logger = require("../utils/logger");
@@ -28,7 +29,6 @@ exports.default = async (req, res) => {
logger.log("arms-start", "DEBUG", "api", null, null); logger.log("arms-start", "DEBUG", "api", null, null);
const { bodyshops } = await client.request(queries.GET_ENTEGRAL_SHOPS); const { bodyshops } = await client.request(queries.GET_ENTEGRAL_SHOPS);
const allxmlsToUpload = [];
const allErrors = []; const allErrors = [];
try { try {
for (const bodyshop of bodyshops) { for (const bodyshop of bodyshops) {
@@ -40,10 +40,12 @@ exports.default = async (req, res) => {
const { jobs } = await client.request(queries.ENTEGRAL_EXPORT, { const { jobs } = await client.request(queries.ENTEGRAL_EXPORT, {
bodyshopid: bodyshop.id, bodyshopid: bodyshop.id,
}); });
const ret = jobs.map((job) => { const jobsToPush = [];
jobs.forEach((job) => {
const transId = uuid(); // Can this actually be the job id? const transId = uuid(); // Can this actually be the job id?
return { let obj = {
RqUID: transId, RqUID: transId,
DocumentInfo: { DocumentInfo: {
BMSVer: "4.0.0", BMSVer: "4.0.0",
@@ -55,16 +57,18 @@ exports.default = async (req, res) => {
TransmitDateTime: moment().format(momentFormat), // Omitted from ARMS docs TransmitDateTime: moment().format(momentFormat), // Omitted from ARMS docs
}, },
EventInfo: { EventInfo: {
AssignmentEvent: { // AssignmentEvent: {
CreateDateTime: // CreateDateTime:
job.asgn_date && moment(job.asgn_date).format(momentFormat), // job.asgn_date && moment(job.asgn_date).format(momentFormat),
}, // },
EstimateEvent: { // EstimateEvent: {
UploadDateTime: moment().format(momentFormat), // UploadDateTime: moment().format(momentFormat),
}, // },
RepairEvent: { RepairEvent: {
CreatedDateTime: CreatedDateTime: (job.date_open
job.date_open && moment(job.date_open).format(momentFormat), ? moment(job.date_open)
: moment()
).format(momentFormat),
ArrivalDateTime: ArrivalDateTime:
job.actual_in && moment(job.actual_in).format(momentFormat), job.actual_in && moment(job.actual_in).format(momentFormat),
ArrivalOdometerReading: job.kmin, ArrivalOdometerReading: job.kmin,
@@ -92,35 +96,35 @@ exports.default = async (req, res) => {
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: [
{ // {
CommQualifier: "WA", // CommQualifier: "WA",
Address: { // Address: {
Address1: job.ins_addr1, // Address1: job.ins_addr1,
Address2: job.ins_addr2, // Address2: job.ins_addr2,
City: job.ins_city, // City: job.ins_city,
StateProvince: job.ins_st, // StateProvince: job.ins_st,
PostalCode: job.ins_zip, // PostalCode: job.ins_zip,
CountryCode: job.ins_ctry, // CountryCode: job.ins_ctry,
}, // },
}, // },
{ // {
CommQualifier: "WP", // CommQualifier: "WP",
CommPhone: job.ins_ph1, // CommPhone: job.ins_ph1,
}, // },
{ // {
CommQualifier: "WF", // CommQualifier: "WF",
CommPhone: job.ins_ph2, // CommPhone: job.ins_ph2,
}, // },
], // ],
},
ContactInfo: {
ContactJobTitle: "Adjuster",
ContactName: {
FirstName: job.est_ct_fn,
LastName: job.est_ct_ln,
},
}, },
// ContactInfo: {
// ContactJobTitle: "Adjuster",
// ContactName: {
// FirstName: job.est_ct_fn,
// LastName: job.est_ct_ln,
// },
// },
}, },
}, },
// InsuranceAgent: { // InsuranceAgent: {
@@ -141,16 +145,16 @@ exports.default = async (req, res) => {
// }, // },
// }, // },
// }, // },
Insured: { // Insured: {
Party: { // Party: {
PersonInfo: { // PersonInfo: {
PersonName: { // PersonName: {
FirstName: job.insd_fn, // FirstName: job.insd_fn,
LastName: job.insd_ln, // LastName: job.insd_ln,
}, // },
}, // },
}, // },
}, // },
Owner: { Owner: {
Party: { Party: {
PersonInfo: { PersonInfo: {
@@ -158,68 +162,70 @@ exports.default = async (req, res) => {
FirstName: job.ownr_fn, FirstName: job.ownr_fn,
LastName: job.ownr_ln, LastName: job.ownr_ln,
}, },
Communications: [ // Communications: [
{ // {
CommQualifier: "HA", // CommQualifier: "HA",
Address: { // Address: {
Address1: job.ownr_addr1, // Address1: job.ownr_addr1,
City: job.ownr_city, // City: job.ownr_city,
StateProvince: job.ownr_st, // StateProvince: job.ownr_st,
PostalCode: job.ownr_zip, // PostalCode: job.ownr_zip,
CountryCode: job.ownr_ctry, // CountryCode: job.ownr_ctry,
}, // },
}, // },
{ // {
CommQualifier: "HP", // CommQualifier: "HP",
CommPhone: job.ownr_ph1, // CommPhone: job.ownr_ph1,
}, // },
{ // {
CommQualifier: "WP", // CommQualifier: "WP",
CommPhone: job.ownr_ph2, // CommPhone: job.ownr_ph2,
}, // },
{ // {
CommQualifier: "CP", // CommQualifier: "CP",
CommPhone: job.ownr_ph1, // CommPhone: job.ownr_ph1,
}, // },
{ // {
CommQualifier: "EM", // CommQualifier: "EM",
CommEmail: job.ownr_ea, // 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,
// },
}, },
}, },
}, },
// 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: { RepairFacility: {
//This section not in documentation.
Party: { Party: {
OrgInfo: { OrgInfo: {
CompanyName: bodyshop.shopname, CompanyName:
process.env.NODE_ENV === "production"
? bodyshop.shopname
: "IMEX Test Shop",
IDInfo: { IDInfo: {
IDQualifierCode: "US", IDQualifierCode: "US",
IDNum: bodyshop.entegral_id, IDNum: bodyshop.entegral_id,
@@ -233,7 +239,7 @@ exports.default = async (req, res) => {
// VendorCode: "C", // VendorCode: "C",
// EstimateDocumentID: "1223HJ76", // EstimateDocumentID: "1223HJ76",
}, },
//RepairOrderType: "DRP", RepairOrderType: "DirectRepairProgram", //Need to get from Entegral
//ReferralSourceType: "Yellow Pages", //ReferralSourceType: "Yellow Pages",
VehicleInfo: { VehicleInfo: {
VINInfo: { VINInfo: {
@@ -241,9 +247,9 @@ exports.default = async (req, res) => {
VINNum: job.v_vin, VINNum: job.v_vin,
}, },
}, },
License: { // License: {
LicensePlateNum: job.plate_no, // LicensePlateNum: job.plate_no,
}, // },
VehicleDesc: { VehicleDesc: {
//ProductionDate: "2009-10", //ProductionDate: "2009-10",
ModelYear: ModelYear:
@@ -255,23 +261,23 @@ exports.default = async (req, res) => {
MakeDesc: job.v_make_desc, MakeDesc: job.v_make_desc,
ModelName: job.v_model_desc, ModelName: job.v_model_desc,
}, },
Paint: { // Paint: {
Exterior: { // Exterior: {
Color: { // Color: {
ColorName: job.v_color, // ColorName: job.v_color,
// OEMColorCode: "1M3", // // OEMColorCode: "1M3",
}, // },
}, // },
}, // },
// Body: { // Body: {
// BodyStyle: "2 Door Convertible", // BodyStyle: "2 Door Convertible",
// Trim: { // Trim: {
// TrimCode: "1B3", // TrimCode: "1B3",
// }, // },
// }, // },
Condition: { // Condition: {
DrivableInd: job.driveable ? "Y" : "N", // DrivableInd: job.driveable ? "Y" : "N",
}, // },
}, },
ClaimInfo: { ClaimInfo: {
ClaimNum: job.clm_no, ClaimNum: job.clm_no,
@@ -296,7 +302,7 @@ exports.default = async (req, res) => {
}, },
}, },
ProfileInfo: { ProfileInfo: {
//ProfileName: "Shop Standard Rates", ProfileName: "ImEX",
RateInfo: [ RateInfo: [
{ {
RateType: "PA", RateType: "PA",
@@ -306,7 +312,7 @@ exports.default = async (req, res) => {
TaxableInd: true, TaxableInd: true,
TaxTierInfo: { TaxTierInfo: {
TierNum: 1, 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, TaxableInd: true,
TaxTierInfo: { TaxTierInfo: {
TierNum: 1, 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", TotalType: "PAA",
TotalTypeDesc: "Aftermarket Parts", TotalTypeDesc: "Aftermarket Parts",
TotalAmt: Dinero( TotalAmt: Dinero(
job.job_totals.parts.parts.list.paa && job.job_totals.parts.parts.list.PAA &&
job.job_totals.parts.parts.list.paa.total job.job_totals.parts.parts.list.PAA.total
).toFormat("0.00"), ).toFormat("0.00"),
}, },
{ {
TotalType: "PAC", TotalType: "PAC",
TotalTypeDesc: "Re-Chromed Parts", TotalTypeDesc: "Re-Chromed Parts",
TotalAmt: Dinero( TotalAmt: Dinero(
job.job_totals.parts.parts.list.pac && job.job_totals.parts.parts.list.PAC &&
job.job_totals.parts.parts.list.pac.total job.job_totals.parts.parts.list.PAC.total
).toFormat("0.00"), ).toFormat("0.00"),
}, },
{ {
TotalType: "PAG", TotalType: "PAG",
TotalTypeDesc: "Glass Parts", TotalTypeDesc: "Glass Parts",
TotalAmt: Dinero( TotalAmt: Dinero(
job.job_totals.parts.parts.list.pag && job.job_totals.parts.parts.list.PAG &&
job.job_totals.parts.parts.list.pag.total job.job_totals.parts.parts.list.PAG.total
).toFormat("0.00"), ).toFormat("0.00"),
}, },
{ {
TotalType: "PAL", TotalType: "PAL",
TotalTypeDesc: "LKQ/Used Parts", TotalTypeDesc: "LKQ/Used Parts",
TotalAmt: Dinero( TotalAmt: Dinero(
job.job_totals.parts.parts.list.pal && job.job_totals.parts.parts.list.PAL &&
job.job_totals.parts.parts.list.pal.total job.job_totals.parts.parts.list.PAL.total
).toFormat("0.00"), ).toFormat("0.00"),
}, },
{ {
TotalType: "PAM", TotalType: "PAM",
TotalTypeDesc: "Remanufactured Parts", TotalTypeDesc: "Remanufactured Parts",
TotalAmt: Dinero( TotalAmt: Dinero(
job.job_totals.parts.parts.list.pam && job.job_totals.parts.parts.list.PAM &&
job.job_totals.parts.parts.list.pam.total job.job_totals.parts.parts.list.PAM.total
).toFormat("0.00"), ).toFormat("0.00"),
}, },
{ {
TotalType: "PAN", TotalType: "PAN",
TotalTypeDesc: "New Parts", TotalTypeDesc: "New Parts",
TotalAmt: Dinero( TotalAmt: Dinero(
job.job_totals.parts.parts.list.pan && job.job_totals.parts.parts.list.PAN &&
job.job_totals.parts.parts.list.pan.total job.job_totals.parts.parts.list.PAN.total
).toFormat("0.00"), ).toFormat("0.00"),
}, },
{ {
TotalType: "PAR", TotalType: "PAR",
TotalTypeDesc: "Recored Parts", TotalTypeDesc: "Recored Parts",
TotalAmt: Dinero( TotalAmt: Dinero(
job.job_totals.parts.parts.list.par && job.job_totals.parts.parts.list.PAR &&
job.job_totals.parts.parts.list.par.total job.job_totals.parts.parts.list.PAR.total
).toFormat("0.00"), ).toFormat("0.00"),
}, },
], ],
@@ -616,6 +622,14 @@ exports.default = async (req, res) => {
"0.00" "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", TotalType: "TOT",
TotalSubType: "F7", TotalSubType: "F7",
@@ -632,12 +646,14 @@ exports.default = async (req, res) => {
"0.00" "0.00"
), ),
}, },
// { {
// TotalType: "TOT", TotalType: "TOT",
// TotalSubType: "SM", TotalSubType: "D8",
// TotalTypeDesc: "Supplement Total", TotalTypeDesc: "Bottom Line Discount",
// TotalAmt: 0, TotalAmt: Dinero(
// }, job.job_totals.additional.adjustments
).toFormat("0.00"),
},
{ {
TotalType: "TOT", TotalType: "TOT",
TotalSubType: "D2", TotalSubType: "D2",
@@ -658,15 +674,13 @@ exports.default = async (req, res) => {
TotalType: "TOT", TotalType: "TOT",
TotalSubType: "AA", TotalSubType: "AA",
TotalTypeDesc: "Appearance Allowance", TotalTypeDesc: "Appearance Allowance",
TotalAmt: 0, TotalAmt: Dinero().toFormat("0.00"),
}, },
{ {
TotalType: "TOT", TotalType: "TOT",
TotalSubType: "D8", TotalSubType: "DEPOSIT",
TotalTypeDesc: "Bottom Line Discount", TotalTypeDesc: "Deposit",
TotalAmt: Dinero( TotalAmt: Dinero().toFormat("0.00"),
job.job_totals.additional.adjustments
).toFormat("0.00"),
}, },
{ {
TotalType: "TOT", TotalType: "TOT",
@@ -676,12 +690,6 @@ exports.default = async (req, res) => {
.subtract(Dinero(job.job_totals.totals.custPayable.total)) .subtract(Dinero(job.job_totals.totals.custPayable.total))
.toFormat("0.00"), .toFormat("0.00"),
}, },
// {
// TotalType: "TOT",
// TotalSubType: "DEPOSIT",
// TotalTypeDesc: "Deposit",
// TotalAmt: 0,
// },
{ {
TotalType: "TOT", TotalType: "TOT",
TotalSubType: "CUST", TotalSubType: "CUST",
@@ -691,7 +699,7 @@ exports.default = async (req, res) => {
).toFormat("0.00"), ).toFormat("0.00"),
}, },
], ],
RepairTotalsType: 1, // RepairTotalsType: 1,
}, },
// RepairLabor: { // RepairLabor: {
// LaborAllocations: { // LaborAllocations: {
@@ -741,7 +749,7 @@ exports.default = async (req, res) => {
// }, // },
ProductionStatus: { ProductionStatus: {
ProductionStage: { ProductionStage: {
ProductionStageCode: GetProductionStageCode(job), ProductionStageCode: GetProductionStageCode(job, bodyshop),
ProductionStageDateTime: moment().format(momentFormat), ProductionStageDateTime: moment().format(momentFormat),
// ProductionStageStatusComment: // ProductionStageStatusComment:
// "Going to be painted this afternoon", // "Going to be painted this afternoon",
@@ -777,6 +785,9 @@ exports.default = async (req, res) => {
// }, // },
// }, // },
}; };
deleteNullKeys(obj);
jobsToPush.push(obj);
}); });
if (erroredJobs.length > 0) { if (erroredJobs.length > 0) {
@@ -784,13 +795,12 @@ exports.default = async (req, res) => {
count: erroredJobs.length, count: erroredJobs.length,
jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number)), jobs: JSON.stringify(erroredJobs.map((j) => j.job.ro_number)),
}); });
allErrors = [...allErrors, ...erroredJobs];
} }
logger.log("arms-end-shop-extract", "DEBUG", "api", bodyshop.id, { logger.log("arms-end-shop-extract", "DEBUG", "api", bodyshop.id, {
shopname: bodyshop.shopname, shopname: bodyshop.shopname,
}); });
const abc = ret[1];
deleteNullKeys(abc);
try { try {
const entegralSoapClient = await soap.createClientAsync( const entegralSoapClient = await soap.createClientAsync(
@@ -798,7 +808,7 @@ exports.default = async (req, res) => {
{ {
ignoredNamespaces: true, ignoredNamespaces: true,
wsdl_options: { wsdl_options: {
useEmptyTag: true, // useEmptyTag: true,
}, },
wsdl_headers: { wsdl_headers: {
Authorization: `Basic ${new Buffer.from( Authorization: `Basic ${new Buffer.from(
@@ -816,14 +826,20 @@ exports.default = async (req, res) => {
); );
const entegralResponse = 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; const [result, rawResponse, , rawRequest] = entegralResponse;
console.log("🚀 ~ file: arms.js ~ line 806 ~ result", result);
res.json({ result, obj: abc });
} catch (error) { } catch (error) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
console.log(error); console.log(error);
res.json(error);
} }
} catch (error) { } catch (error) {
//Error at the shop level. //Error at the shop level.
@@ -846,15 +862,18 @@ exports.default = async (req, res) => {
} }
} }
// res.sendStatus(200); res.sendStatus(200);
} catch (error) { } catch (error) {
res.status(200).json(error); res.status(200).json(error);
} }
}; };
function GetSupplementNumber(joblines) { function GetSupplementNumber(joblines) {
return 0; const max = _.max(
return _.max(joblines.map((jl) => jl.line_ind)); joblines.map((jl) => parseInt((jl.line_ind || "0").replace(/[^\d.-]/g, "")))
);
return max || 0;
} }
function GetDocumentstatus(job, bodyshop) { function GetDocumentstatus(job, bodyshop) {
@@ -873,7 +892,9 @@ function GetDocumentstatus(job, bodyshop) {
function GetRepairStatusCode(job) { function GetRepairStatusCode(job) {
return "25"; return "25";
} }
function GetProductionStageCode(job) { function GetProductionStageCode(job, bodyshop) {
if (bodyshop.md_ro_statuses.post_production_statuses.includes(job.status))
return "8D";
return "33"; return "33";
} }

View File

@@ -37,6 +37,7 @@ exports.default = async (req, res) => {
//Query for the List of Bodyshop Clients. //Query for the List of Bodyshop Clients.
logger.log("autohouse-start", "DEBUG", "api", null, null); logger.log("autohouse-start", "DEBUG", "api", null, null);
const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS); const { bodyshops } = await client.request(queries.GET_AUTOHOUSE_SHOPS);
639;
const allxmlsToUpload = []; const allxmlsToUpload = [];
const allErrors = []; const allErrors = [];
@@ -47,16 +48,19 @@ exports.default = async (req, res) => {
}); });
const erroredJobs = []; const erroredJobs = [];
try { try {
const { jobs } = await client.request(queries.AUTOHOUSE_QUERY, { const { jobs, bodyshops_by_pk } = await client.request(
bodyshopid: bodyshop.id, queries.AUTOHOUSE_QUERY,
start: moment().subtract(3, "days").startOf("day"), {
}); bodyshopid: bodyshop.id,
start: moment().subtract(3, "days").startOf("day"),
}
);
const autoHouseObject = { const autoHouseObject = {
AutoHouseExport: { AutoHouseExport: {
RepairOrder: jobs.map((j) => RepairOrder: jobs.map((j) =>
CreateRepairOrderTag( CreateRepairOrderTag(
{ ...j, bodyshop }, { ...j, bodyshop: bodyshops_by_pk },
function ({ job, error }) { function ({ job, error }) {
erroredJobs.push({ job: job, error: error.toString() }); erroredJobs.push({ job: job, error: error.toString() });
} }
@@ -113,6 +117,14 @@ exports.default = async (req, res) => {
}); });
} }
} }
for (const xmlObj of allxmlsToUpload) {
fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
}
res.json(allxmlsToUpload);
return;
let sftp = new Client(); let sftp = new Client();
sftp.on("error", (errors) => sftp.on("error", (errors) =>
logger.log("autohouse-sftp-error", "ERROR", "api", null, { logger.log("autohouse-sftp-error", "ERROR", "api", null, {
@@ -395,21 +407,23 @@ const CreateRepairOrderTag = (job, errorCallback) => {
// AmountDue: null, // AmountDue: null,
// }, // },
RevisedTotals: { RevisedTotals: {
BodyHours: job.job_totals.rates.lab.hours, BodyHours: job.job_totals.rates.lab.hours.toFixed(2),
BodyRepairHours: job.joblines BodyRepairHours: job.joblines
.filter((line) => repairOpCodes.includes(line.lbr_op)) .filter((line) => repairOpCodes.includes(line.lbr_op))
.reduce((acc, val) => acc + val.mod_lb_hrs, 0), .reduce((acc, val) => acc + val.mod_lb_hrs, 0)
.toFixed(2),
BodyReplaceHours: job.joblines BodyReplaceHours: job.joblines
.filter((line) => replaceOpCodes.includes(line.lbr_op)) .filter((line) => replaceOpCodes.includes(line.lbr_op))
.reduce((acc, val) => acc + val.mod_lb_hrs, 0), .reduce((acc, val) => acc + val.mod_lb_hrs, 0)
RefinishHours: job.job_totals.rates.lar.hours, .toFixed(2),
MechanicalHours: job.job_totals.rates.lam.hours, RefinishHours: job.job_totals.rates.lar.hours.toFixed(2),
StructuralHours: job.job_totals.rates.las.hours, MechanicalHours: job.job_totals.rates.lam.hours.toFixed(2),
StructuralHours: job.job_totals.rates.las.hours.toFixed(2),
ElectricalHours: job.job_totals.rates.lae.hours, ElectricalHours: job.job_totals.rates.lae.hours.toFixed(2),
FrameHours: job.job_totals.rates.laf.hours, FrameHours: job.job_totals.rates.laf.hours.toFixed(2),
GlassHours: job.job_totals.rates.lag.hours, GlassHours: job.job_totals.rates.lag.hours.toFixed(2),
DetailHours: job.job_totals.rates.lad.hours, DetailHours: job.job_totals.rates.lad.hours.toFixed(2),
LaborMiscHours: 0, LaborMiscHours: 0,
PartsTotal: Dinero(job.job_totals.parts.parts.total).toFormat( PartsTotal: Dinero(job.job_totals.parts.parts.total).toFormat(
@@ -500,11 +514,11 @@ const CreateRepairOrderTag = (job, errorCallback) => {
PMTotal: Dinero(job.job_totals.rates.mapa.total).toFormat( PMTotal: Dinero(job.job_totals.rates.mapa.total).toFormat(
AHDineroFormat AHDineroFormat
), ),
PMTotalCost: 0, PMTotalCost: repairCosts.PMTotalCost.toFormat(AHDineroFormat),
BMTotal: Dinero(job.job_totals.rates.mash.total).toFormat( BMTotal: Dinero(job.job_totals.rates.mash.total).toFormat(
AHDineroFormat AHDineroFormat
), ),
BMTotalCost: 0, BMTotalCost: repairCosts.BMTotalCost.toFormat(AHDineroFormat),
MiscTotal: 0, MiscTotal: 0,
MiscTotalCost: 0, MiscTotalCost: 0,
TowingTotal: Dinero(job.job_totals.additional.towing).toFormat( TowingTotal: Dinero(job.job_totals.additional.towing).toFormat(
@@ -721,7 +735,7 @@ const StatusMapping = (status, md_ro_statuses) => {
else if (status === default_delivered) return "DEL"; else if (status === default_delivered) return "DEL";
else if (status === default_invoiced || status === default_exported) else if (status === default_invoiced || status === default_exported)
return "CLO"; return "CLO";
else if (status === default_void) return "CLO"; else if (status === default_void) return "VOID";
else if (md_ro_statuses.production_statuses.includes(status)) return "IPR"; else if (md_ro_statuses.production_statuses.includes(status)) return "IPR";
else return "UNDEFINED"; else return "UNDEFINED";
@@ -731,12 +745,7 @@ const StatusMapping = (status, md_ro_statuses) => {
const GenerateDetailLines = (line, statuses) => { const GenerateDetailLines = (line, statuses) => {
const ret = { const ret = {
BackOrdered: line.status === statuses.default_bo ? "1" : "0", BackOrdered: line.status === statuses.default_bo ? "1" : "0",
Cost: Cost: (line.billlines[0] && line.billlines[0].actual_cost.toFixed(2)) || 0,
(line.billlines[0] &&
(line.billlines[0].actual_cost * line.billlines[0].quantity).toFixed(
2
)) ||
0,
//Critical: null, //Critical: null,
Description: line.line_desc || "", Description: line.line_desc || "",
DiscountMarkup: line.prt_dsmk_m || 0, DiscountMarkup: line.prt_dsmk_m || 0,
@@ -744,27 +753,39 @@ const GenerateDetailLines = (line, statuses) => {
IOUPart: 0, IOUPart: 0,
LineNumber: line.line_no || 0, LineNumber: line.line_no || 0,
MarkUp: null, MarkUp: null,
OrderedOn: null, OrderedOn:
(line.parts_order_lines[0] &&
moment(line.parts_order_lines[0].parts_order.order_date).format(
AhDateFormat
)) ||
"",
OriginalCost: null, OriginalCost: null,
OriginalInvoiceNumber: null, OriginalInvoiceNumber: null,
PriceEach: (line.billlines[0] && line.billlines[0].retail_price) || 0, PriceEach: line.act_price || 0,
PartNumber: _.escape(line.oem_partno), PartNumber: _.escape(line.oem_partno),
ProfitPercent: null, ProfitPercent: null,
PurchaseOrderNumber: null, PurchaseOrderNumber: null,
Qty: line.part_qty || 0, Qty: line.part_qty || 0,
Status: line.status || "", Status: line.status || "",
SupplementNumber: line.line_ind || "", SupplementNumber: line.line_ind ? line.line_ind.replace(/[^\d.-]/g, "") : 0,
Type: line.part_type || "", Type: line.part_type || "",
Vendor: (line.billlines[0] && line.billlines[0].bill.vendor.name) || "", Vendor: (line.billlines[0] && line.billlines[0].bill.vendor.name) || "",
VendorPaid: null, VendorPaid: null,
VendorPrice: (line.billlines[0] && line.billlines[0].actual_price) || 0, VendorPrice:
(line.billlines[0] &&
(line.billlines[0].actual_price * line.billlines[0].quantity).toFixed(
2
)) ||
0,
Deleted: null, Deleted: null,
ExpectedOn: null, ExpectedOn: null,
ReceivedOn: null, ReceivedOn:
line.billlines[0] &&
moment(line.billlines[0].bill.date).format(AhDateFormat),
OrderedBy: null, OrderedBy: null,
ShipVia: null, ShipVia: null,
VendorContact: null, VendorContact: null,
EstimateAmount: line.act_price * line.part_qty || 0, //Rebecca EstimateAmount: (line.act_price * line.part_qty).toFixed(2) || 0, //Rebecca
}; };
return ret; return ret;
}; };
@@ -780,7 +801,7 @@ const generateNullDetailLine = () => {
IOUPart: 0, IOUPart: 0,
LineNumber: 0, LineNumber: 0,
MarkUp: null, MarkUp: null,
OrderedOn: null, OrderedOn: "",
OriginalCost: null, OriginalCost: null,
OriginalInvoiceNumber: null, OriginalInvoiceNumber: null,
PriceEach: 0, PriceEach: 0,

View File

@@ -543,6 +543,23 @@ exports.QUERY_EMPLOYEE_PIN = `query QUERY_EMPLOYEE_PIN($shopId: uuid!, $employee
}`; }`;
exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshopid: uuid!) { exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshopid: uuid!) {
bodyshops_by_pk(id: $bodyshopid){
id
shopname
address1
city
state
zip_post
country
phone
md_ro_statuses
md_order_statuses
autohouseid
md_responsibility_centers
jc_hourly_rates
}
jobs(where: {_and: [{converted: {_eq: true}}, {updated_at: {_gt: $start}}, {shopid: {_eq: $bodyshopid}}]}) { jobs(where: {_and: [{converted: {_eq: true}}, {updated_at: {_gt: $start}}, {shopid: {_eq: $bodyshopid}}]}) {
id id
ro_number ro_number
@@ -608,21 +625,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
job_totals job_totals
driveable driveable
parts_tax_rates parts_tax_rates
bodyshop {
id
shopname
address1
city
state
zip_post
country
phone
md_ro_statuses
md_order_statuses
autohouseid
md_responsibility_centers
jc_hourly_rates
}
joblines(where: {removed: {_eq: false}}) { joblines(where: {removed: {_eq: false}}) {
id id
line_no line_no
@@ -642,7 +645,13 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
lbr_op lbr_op
profitcenter_part profitcenter_part
profitcenter_labor profitcenter_labor
billlines(order_by: {bill: {date: desc_nulls_last}}) { parts_order_lines(order_by: {parts_order: {order_date: desc_nulls_last}} limit: 1){
parts_order{
id
order_date
}
}
billlines(order_by: {bill: {date: desc_nulls_last}} limit: 1) {
actual_cost actual_cost
actual_price actual_price
quantity quantity
@@ -651,6 +660,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
name name
} }
invoice_number invoice_number
date
} }
} }
} }
@@ -695,8 +705,6 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
} }
} }
} }
`; `;
exports.ENTEGRAL_EXPORT = ` exports.ENTEGRAL_EXPORT = `
@@ -783,6 +791,8 @@ query ENTEGRAL_EXPORT($bodyshopid: uuid!) {
rate_matd rate_matd
job_totals job_totals
ded_amt ded_amt
cieca_ttl
adjustment_bottom_line
} }
} }
`; `;
@@ -1030,6 +1040,7 @@ exports.QUERY_JOB_COSTING_DETAILS = ` query QUERY_JOB_COSTING_DETAILS($id: uuid!
status status
ca_bc_pvrt ca_bc_pvrt
ca_customer_gst ca_customer_gst
dms_allocation
joblines(where: { removed: { _eq: false } }) { joblines(where: { removed: { _eq: false } }) {
id id
db_ref db_ref
@@ -1073,11 +1084,14 @@ exports.QUERY_JOB_COSTING_DETAILS = ` query QUERY_JOB_COSTING_DETAILS($id: uuid!
actualhrs actualhrs
productivehrs productivehrs
flat_rate flat_rate
ciecacode
} }
bodyshop{ bodyshop{
id id
md_responsibility_centers md_responsibility_centers
jc_hourly_rates jc_hourly_rates
cdk_dealerid
pbs_serialnumber
} }
} }
}`; }`;
@@ -1133,6 +1147,7 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT
status status
ca_bc_pvrt ca_bc_pvrt
ca_customer_gst ca_customer_gst
dms_allocation
joblines(where: {removed: {_eq: false}}) { joblines(where: {removed: {_eq: false}}) {
id id
db_ref db_ref
@@ -1176,11 +1191,14 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT
actualhrs actualhrs
productivehrs productivehrs
flat_rate flat_rate
ciecacode
} }
bodyshop { bodyshop {
id id
md_responsibility_centers md_responsibility_centers
jc_hourly_rates jc_hourly_rates
cdk_dealerid
pbs_serialnumber
} }
} }
} }

View File

@@ -261,7 +261,7 @@ function GenerateCostingData(job) {
val.profitcenter_labor || defaultProfits[val.mod_lbr_ty] || "?"; val.profitcenter_labor || defaultProfits[val.mod_lbr_ty] || "?";
if (laborProfitCenter === "?") if (laborProfitCenter === "?")
console.log("Unknown type", val.mod_lbr_ty); console.log("Unknown type", val.line_desc, val.mod_lbr_ty);
const rateName = `rate_${(val.mod_lbr_ty || "").toLowerCase()}`; const rateName = `rate_${(val.mod_lbr_ty || "").toLowerCase()}`;
const laborAmount = Dinero({ const laborAmount = Dinero({
@@ -285,11 +285,12 @@ function GenerateCostingData(job) {
val.profitcenter_part || defaultProfits[val.part_type] || "?"; val.profitcenter_part || defaultProfits[val.part_type] || "?";
if (partsProfitCenter === "?") if (partsProfitCenter === "?")
console.log("Unknown type", val.part_type); console.log("Unknown type", val.line_desc, val.part_type);
if (!partsProfitCenter) if (!partsProfitCenter)
console.log( console.log(
"Unknown cost/profit center mapping for parts.", "Unknown cost/profit center mapping for parts.",
val.line_desc,
val.part_type val.part_type
); );
const partsAmount = Dinero({ const partsAmount = Dinero({
@@ -298,13 +299,13 @@ function GenerateCostingData(job) {
.multiply(val.part_qty || 1) .multiply(val.part_qty || 1)
.add( .add(
val.prt_dsmk_m && val.prt_dsmk_m !== 0 val.prt_dsmk_m && val.prt_dsmk_m !== 0
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
: Dinero({ : Dinero({
amount: Math.round(val.act_price * 100), amount: Math.round(val.act_price * 100),
}) })
.multiply(val.part_qty || 0) .multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0)) .percentage(Math.abs(val.prt_dsmk_p || 0))
.multiply(val.prt_dsmk_p > 0 ? 1 : -1) .multiply(val.prt_dsmk_p > 0 ? 1 : -1)
); );
if (!acc.parts[partsProfitCenter]) if (!acc.parts[partsProfitCenter])
acc.parts[partsProfitCenter] = Dinero(); acc.parts[partsProfitCenter] = Dinero();
@@ -322,7 +323,7 @@ function GenerateCostingData(job) {
"?"; "?";
if (partsProfitCenter === "?") { if (partsProfitCenter === "?") {
console.log("Unknown type", val.part_type); console.log("Unknown type", val.line_desc, val.part_type);
} else { } else {
const partsAmount = Dinero({ const partsAmount = Dinero({
amount: Math.round((val.act_price || 0) * 100), amount: Math.round((val.act_price || 0) * 100),
@@ -330,13 +331,13 @@ function GenerateCostingData(job) {
.multiply(val.part_qty || 1) .multiply(val.part_qty || 1)
.add( .add(
val.prt_dsmk_m && val.prt_dsmk_m !== 0 val.prt_dsmk_m && val.prt_dsmk_m !== 0
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
: Dinero({ : Dinero({
amount: Math.round(val.act_price * 100), amount: Math.round(val.act_price * 100),
}) })
.multiply(val.part_qty || 0) .multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0)) .percentage(Math.abs(val.prt_dsmk_p || 0))
.multiply(val.prt_dsmk_p > 0 ? 1 : -1) .multiply(val.prt_dsmk_p > 0 ? 1 : -1)
); );
if (!acc.parts[partsProfitCenter]) if (!acc.parts[partsProfitCenter])
@@ -372,21 +373,43 @@ function GenerateCostingData(job) {
); );
} }
//Is it a DMS Setup?
const selectedDmsAllocationConfig =
(job.bodyshop.md_responsibility_centers.dms_defaults &&
job.bodyshop.md_responsibility_centers.dms_defaults.find(
(d) => d.name === job.dms_allocation
)) ||
job.bodyshop.md_responsibility_centers.defaults;
const billTotalsByCostCenters = job.bills.reduce((bill_acc, bill_val) => { const billTotalsByCostCenters = job.bills.reduce((bill_acc, bill_val) => {
//At the bill level. //At the bill level.
bill_val.billlines.map((line_val) => { bill_val.billlines.map((line_val) => {
//At the bill line level. //At the bill line level.
//console.log("JobCostingPartsTable -> line_val", line_val); if (job.bodyshop.pbs_serialnumber || job.bodyshop.cdk_dealerid) {
if (!bill_acc[line_val.cost_center]) if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]])
bill_acc[line_val.cost_center] = Dinero(); bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
Dinero();
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add( bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
Dinero({ bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(
amount: Math.round((line_val.actual_cost || 0) * 100), Dinero({
}) amount: Math.round((line_val.actual_cost || 0) * 100),
.multiply(line_val.quantity) })
.multiply(bill_val.is_credit_memo ? -1 : 1) .multiply(line_val.quantity)
); .multiply(bill_val.is_credit_memo ? -1 : 1)
);
} else {
if (!bill_acc[line_val.cost_center])
bill_acc[line_val.cost_center] = Dinero();
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100),
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1)
);
}
return null; return null;
}); });
@@ -443,20 +466,42 @@ function GenerateCostingData(job) {
const ticketTotalsByCostCenter = job.timetickets.reduce( const ticketTotalsByCostCenter = job.timetickets.reduce(
(ticket_acc, ticket_val) => { (ticket_acc, ticket_val) => {
//At the invoice level. //At the invoice level.
if (!ticket_acc[ticket_val.cost_center])
ticket_acc[ticket_val.cost_center] = Dinero();
ticket_acc[ticket_val.cost_center] = ticket_acc[ if (job.bodyshop.pbs_serialnumber || job.bodyshop.cdk_dealerid) {
ticket_val.cost_center if (
].add( !ticket_acc[selectedDmsAllocationConfig.costs[ticket_val.ciecacode]]
Dinero({ )
amount: Math.round((ticket_val.rate || 0) * 100), ticket_acc[selectedDmsAllocationConfig.costs[ticket_val.ciecacode]] =
}).multiply( Dinero();
ticket_val.flat_rate
? ticket_val.productivehrs || ticket_val.actualhrs || 0 ticket_acc[selectedDmsAllocationConfig.costs[ticket_val.ciecacode]] =
: ticket_val.actualhrs || ticket_val.productivehrs || 0 ticket_acc[
) //Should base this on the employee. selectedDmsAllocationConfig.costs[ticket_val.ciecacode]
); ].add(
Dinero({
amount: Math.round((ticket_val.rate || 0) * 100),
}).multiply(
ticket_val.flat_rate
? ticket_val.productivehrs || ticket_val.actualhrs || 0
: ticket_val.actualhrs || ticket_val.productivehrs || 0
) //Should base this on the employee.
);
} else {
if (!ticket_acc[ticket_val.cost_center])
ticket_acc[ticket_val.cost_center] = Dinero();
ticket_acc[ticket_val.cost_center] = ticket_acc[
ticket_val.cost_center
].add(
Dinero({
amount: Math.round((ticket_val.rate || 0) * 100),
}).multiply(
ticket_val.flat_rate
? ticket_val.productivehrs || ticket_val.actualhrs || 0
: ticket_val.actualhrs || ticket_val.productivehrs || 0
) //Should base this on the employee.
);
}
return ticket_acc; return ticket_acc;
}, },

View File

@@ -271,7 +271,8 @@ function CalculatePartsTotals(jobLines) {
.multiply(value.part_qty || 0) .multiply(value.part_qty || 0)
.add( .add(
(value.db_ref === "900511" || (value.db_ref === "900511" ||
value.db_ref === "900510") && value.db_ref === "900510" ||
value.db_ref === "900500") &&
value.prt_dsmk_m && value.prt_dsmk_m &&
value.prt_dsmk_m !== 0 value.prt_dsmk_m !== 0
? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) }) ? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) })
@@ -298,7 +299,9 @@ function CalculatePartsTotals(jobLines) {
parts: { parts: {
...acc.parts, ...acc.parts,
prt_dsmk_total: acc.parts.prt_dsmk_total.add( prt_dsmk_total: acc.parts.prt_dsmk_total.add(
(value.db_ref === "900511" || value.db_ref === "900510") && (value.db_ref === "900511" ||
value.db_ref === "900510" ||
value.db_ref === "900500") &&
value.prt_dsmk_m && value.prt_dsmk_m &&
value.prt_dsmk_m !== 0 value.prt_dsmk_m !== 0
? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) }) ? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) })
@@ -344,7 +347,9 @@ function CalculatePartsTotals(jobLines) {
}).multiply(value.part_qty || 0) }).multiply(value.part_qty || 0)
) )
.add( .add(
(value.db_ref === "900511" || value.db_ref === "900510") && (value.db_ref === "900511" ||
value.db_ref === "900510" ||
value.db_ref === "900500") &&
value.prt_dsmk_m && value.prt_dsmk_m &&
value.prt_dsmk_m !== 0 value.prt_dsmk_m !== 0
? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) }) ? Dinero({ amount: Math.round(value.prt_dsmk_m * 100) })

View File

@@ -17,7 +17,10 @@ const CdkCalculateAllocations =
require("../cdk/cdk-calculate-allocations").default; require("../cdk/cdk-calculate-allocations").default;
const { isArray } = require("lodash"); const { isArray } = require("lodash");
const logger = require("../utils/logger"); const logger = require("../utils/logger");
const {default: PbsExportJob, PbsSelectedCustomer} = require("../accounting/pbs/pbs-job-export"); const {
default: PbsExportJob,
PbsSelectedCustomer,
} = require("../accounting/pbs/pbs-job-export");
io.use(function (socket, next) { io.use(function (socket, next) {
try { try {
@@ -159,6 +162,37 @@ function createLogEvent(socket, level, message) {
} }
} }
function createJsonEvent(socket, level, message, json) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
console.log(
`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${
socket.id
} - ${message}`
);
socket.emit("log-event", {
timestamp: new Date(),
level,
message,
});
logger.log("ws-log-event-json", level, socket.user.email, socket.recordid, {
wsmessage: message,
json
});
if (socket.logEvents && isArray(socket.logEvents)) {
socket.logEvents.push({
timestamp: new Date(),
level,
message,
});
}
// if (level === "ERROR") {
// throw new Error(message);
// }
}
}
function createXmlEvent(socket, xml, message, isError = false) { function createXmlEvent(socket, xml, message, isError = false) {
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("TRACE")) { if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy("TRACE")) {
socket.emit("log-event", { socket.emit("log-event", {
@@ -210,3 +244,4 @@ function LogLevelHierarchy(level) {
exports.createLogEvent = createLogEvent; exports.createLogEvent = createLogEvent;
exports.createXmlEvent = createXmlEvent; exports.createXmlEvent = createXmlEvent;
exports.createJsonEvent = createJsonEvent;