Merged in release/2021-08-20 (pull request #181)
release/2021-08-20 Approved-by: Patrick Fic
This commit is contained in:
6
_reference/Responsibility Center Setup.md
Normal file
6
_reference/Responsibility Center Setup.md
Normal file
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
|||||||
<babeledit_project version="1.2" be_version="2.7.1">
|
<babeledit_project be_version="2.7.1" version="1.2">
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
BabelEdit project file
|
BabelEdit project file
|
||||||
@@ -3575,6 +3575,95 @@
|
|||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>dms</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>default_journal</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>dms_acctnumber</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>dms_wip_acctnumber</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>mappingname</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>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>email</name>
|
<name>email</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -7458,6 +7547,53 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>dms</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>cdk_dealerid</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>title</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>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>employees</name>
|
<name>employees</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -12770,6 +12906,32 @@
|
|||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>labels</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>attempts</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>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
@@ -16799,6 +16961,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>dmsautoallocate</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>export</name>
|
<name>export</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -18384,6 +18567,137 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>dms</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>center</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>cost</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>cost_dms_acctnumber</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>dms_wip_acctnumber</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>sale</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>sale_dms_acctnumber</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>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>driveable</name>
|
<name>driveable</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -34880,6 +35194,37 @@
|
|||||||
</folder_node>
|
</folder_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>scoredboard</name>
|
||||||
|
<children>
|
||||||
|
<folder_node>
|
||||||
|
<name>successes</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>updated</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>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>tech</name>
|
<name>tech</name>
|
||||||
<children>
|
<children>
|
||||||
|
|||||||
@@ -109,6 +109,17 @@ export default function AccountingPayablesTableComponent({ loading, bills }) {
|
|||||||
<Checkbox disabled checked={record.is_credit_memo} />
|
<Checkbox disabled checked={record.is_credit_memo} />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("exportlogs.labels.attempts"),
|
||||||
|
dataIndex: "attempts",
|
||||||
|
key: "attempts",
|
||||||
|
|
||||||
|
render: (text, record) => {
|
||||||
|
const success = record.exportlogs.filter((e) => e.successful).length;
|
||||||
|
const attempts = record.exportlogs.length;
|
||||||
|
return `${success}/${attempts}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
|
|||||||
@@ -108,7 +108,17 @@ export default function AccountingPayablesTableComponent({
|
|||||||
<DateTimeFormatter>{record.exportedat}</DateTimeFormatter>
|
<DateTimeFormatter>{record.exportedat}</DateTimeFormatter>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("exportlogs.labels.attempts"),
|
||||||
|
dataIndex: "attempts",
|
||||||
|
key: "attempts",
|
||||||
|
|
||||||
|
render: (text, record) => {
|
||||||
|
const success = record.exportlogs.filter((e) => e.successful).length;
|
||||||
|
const attempts = record.exportlogs.length;
|
||||||
|
return `${success}/${attempts}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
|
|||||||
@@ -114,11 +114,21 @@ export default function AccountingReceivablesTableComponent({ loading, jobs }) {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("exportlogs.labels.attempts"),
|
||||||
|
dataIndex: "attempts",
|
||||||
|
key: "attempts",
|
||||||
|
|
||||||
|
render: (text, record) => {
|
||||||
|
const success = record.exportlogs.filter((e) => e.successful).length;
|
||||||
|
const attempts = record.exportlogs.length;
|
||||||
|
return `${success}/${attempts}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("general.labels.actions"),
|
title: t("general.labels.actions"),
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
key: "actions",
|
key: "actions",
|
||||||
sorter: (a, b) => a.clm_total - b.clm_total,
|
|
||||||
|
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
|
|||||||
@@ -72,9 +72,11 @@ export function BillEnterModalLinesComponent({
|
|||||||
quantity: opt.part_qty || 1,
|
quantity: opt.part_qty || 1,
|
||||||
actual_price: opt.cost,
|
actual_price: opt.cost,
|
||||||
cost_center: opt.part_type
|
cost_center: opt.part_type
|
||||||
? responsibilityCenters.defaults.costs[
|
? responsibilityCenters.defaults &&
|
||||||
|
(responsibilityCenters.defaults.costs[
|
||||||
opt.part_type
|
opt.part_type
|
||||||
] || null
|
] ||
|
||||||
|
null)
|
||||||
: null,
|
: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { Button, Table } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import Dinero from "dinero.js";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//currentUser: selectCurrentUser
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(DmsAllocationsSummary);
|
||||||
|
|
||||||
|
export function DmsAllocationsSummary({ socket, bodyshop, jobId }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [allocationsSummary, setAllocationsSummary] = useState([]);
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.center"),
|
||||||
|
dataIndex: "center",
|
||||||
|
key: "center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.sale"),
|
||||||
|
dataIndex: "sale",
|
||||||
|
key: "sale",
|
||||||
|
render: (text, record) => Dinero(record.sale).toFormat(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.cost"),
|
||||||
|
dataIndex: "cost",
|
||||||
|
key: "cost",
|
||||||
|
render: (text, record) => Dinero(record.cost).toFormat(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.sale_dms_acctnumber"),
|
||||||
|
dataIndex: "sale_dms_acctnumber",
|
||||||
|
key: "sale_dms_acctnumber",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.profitCenter && record.profitCenter.dms_acctnumber,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.cost_dms_acctnumber"),
|
||||||
|
dataIndex: "cost_dms_acctnumber",
|
||||||
|
key: "cost_dms_acctnumber",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.costCenter && record.costCenter.dms_acctnumber,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.dms_wip_acctnumber"),
|
||||||
|
dataIndex: "dms_wip_acctnumber",
|
||||||
|
key: "dms_wip_acctnumber",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.costCenter && record.costCenter.dms_wip_acctnumber,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
title={() => (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
socket.emit("cdk-calculate-allocations", jobId, (ack) =>
|
||||||
|
setAllocationsSummary(ack)
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Get
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
pagination={{ position: "top", defaultPageSize: 50 }}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="center"
|
||||||
|
dataSource={allocationsSummary}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
115
client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx
Normal file
115
client/src/components/dms-cdk-makes/dms-cdk-makes.component.jsx
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { Modal, Button, Table, Input } from "antd";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//currentUser: selectCurrentUser
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkMakes);
|
||||||
|
|
||||||
|
export function DmsCdkMakes({ bodyshop, form, socket }) {
|
||||||
|
const [makesList, setMakesList] = useState([]);
|
||||||
|
const [searchText, setSearchText] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [selectedModel, setSelectedModel] = useState(null);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.makeFullName"),
|
||||||
|
dataIndex: "makeFullName",
|
||||||
|
key: "makeFullName",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.modelFullName"),
|
||||||
|
dataIndex: "modelFullName",
|
||||||
|
key: "modelFullName",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.makeCode"),
|
||||||
|
dataIndex: "makeCode",
|
||||||
|
key: "makeCode",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.dms.modelCode"),
|
||||||
|
dataIndex: "modelCode",
|
||||||
|
key: "modelCode",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const filteredMakes =
|
||||||
|
searchText !== "" && searchText
|
||||||
|
? makesList.filter(
|
||||||
|
(make) =>
|
||||||
|
searchText
|
||||||
|
.split(" ")
|
||||||
|
.some((v) =>
|
||||||
|
make.makeFullName.toLowerCase().includes(v.toLowerCase())
|
||||||
|
) ||
|
||||||
|
searchText
|
||||||
|
.split(" ")
|
||||||
|
.some((v) =>
|
||||||
|
make.modelFullName.toLowerCase().includes(v.toLowerCase())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: makesList;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Modal width={"90%"} visible={visible} onCancel={() => setVisible(false)}>
|
||||||
|
<Table
|
||||||
|
title={() => (
|
||||||
|
<Input.Search
|
||||||
|
onSearch={(val) => setSearchText(val)}
|
||||||
|
placeholder={t("general.labels.search")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
columns={columns}
|
||||||
|
loading={loading}
|
||||||
|
id="id"
|
||||||
|
dataSource={filteredMakes}
|
||||||
|
onRow={(record) => {
|
||||||
|
return {
|
||||||
|
onClick: setSelectedModel(record),
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
rowSelection={{
|
||||||
|
onSelect: (record, selected, ...props) => {
|
||||||
|
console.log(
|
||||||
|
"🚀 ~ file: dms-cdk-makes.component.jsx ~ line 85 ~ record, selected, ...props",
|
||||||
|
record,
|
||||||
|
selected,
|
||||||
|
...props
|
||||||
|
);
|
||||||
|
|
||||||
|
setSelectedModel(record);
|
||||||
|
},
|
||||||
|
|
||||||
|
type: "radio",
|
||||||
|
selectedRowKeys: [selectedModel && selectedModel.id],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setVisible(true);
|
||||||
|
setLoading(true);
|
||||||
|
socket.emit("cdk-get-makes", bodyshop.cdk_dealerid, (makes) => {
|
||||||
|
console.log("Called back", makes);
|
||||||
|
setMakesList(makes);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Get Makes
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { Button, Table } from "antd";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { socket } from "../../pages/dms/dms.container";
|
||||||
|
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||||
|
import { alphaSort } from "../../utils/sorters";
|
||||||
|
export default function DmsCustomerSelector() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [customerList, setcustomerList] = useState([]);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [selectedCustomer, setSelectedCustomer] = useState(null);
|
||||||
|
|
||||||
|
socket.on("cdk-select-customer", (customerList, callback) => {
|
||||||
|
setVisible(true);
|
||||||
|
setcustomerList(customerList);
|
||||||
|
});
|
||||||
|
|
||||||
|
const onOk = () => {
|
||||||
|
setVisible(false);
|
||||||
|
socket.emit("cdk-selected-customer", selectedCustomer);
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("dms.fields.name1"),
|
||||||
|
dataIndex: ["name1", "fullName"],
|
||||||
|
key: "name1",
|
||||||
|
sorter: (a, b) => alphaSort(a.name1?.fullName, b.name1?.fullName),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("dms.fields.name2"),
|
||||||
|
dataIndex: ["name2", "fullName"],
|
||||||
|
key: "name2",
|
||||||
|
sorter: (a, b) => alphaSort(a.name2?.fullName, b.name2?.fullName),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("dms.fields.phone"),
|
||||||
|
dataIndex: ["contactInfo", "mainTelephoneNumber", "value"],
|
||||||
|
key: "phone",
|
||||||
|
render: (record, value) => (
|
||||||
|
<PhoneFormatter>
|
||||||
|
{record.contactInfo?.mainTelephoneNumber?.value}
|
||||||
|
</PhoneFormatter>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("dms.fields.address"),
|
||||||
|
//dataIndex: ["name2", "fullName"],
|
||||||
|
key: "address",
|
||||||
|
render: (record, value) =>
|
||||||
|
`${record.address?.addressLine[0]}, ${record.address?.city} ${record.address?.stateOrProvince} ${record.address?.postalCode}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!visible) return <></>;
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
title={() => (
|
||||||
|
<div>
|
||||||
|
<Button onClick={onOk}>Select</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
pagination={{ position: "top" }}
|
||||||
|
columns={columns}
|
||||||
|
rowKey={(record) => record.id.value}
|
||||||
|
dataSource={customerList}
|
||||||
|
//onChange={handleTableChange}
|
||||||
|
rowSelection={{
|
||||||
|
onSelect: (props) => {
|
||||||
|
setSelectedCustomer(props.id.value);
|
||||||
|
},
|
||||||
|
type: "radio",
|
||||||
|
selectedRowKeys: [selectedCustomer],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { Divider, Space, Tag, Timeline } from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import {
|
||||||
|
setBreadcrumbs,
|
||||||
|
setSelectedHeader,
|
||||||
|
} from "../../redux/application/application.actions";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(DmsLogEvents);
|
||||||
|
|
||||||
|
export function DmsLogEvents({ socket, logs, bodyshop }) {
|
||||||
|
return (
|
||||||
|
<Timeline pending reverse={true}>
|
||||||
|
{logs.map((log, idx) => (
|
||||||
|
<Timeline.Item key={idx} color={LogLevelHierarchy(log.level)}>
|
||||||
|
<Space wrap align="start" style={{}}>
|
||||||
|
<Tag color={LogLevelHierarchy(log.level)}>{log.level}</Tag>
|
||||||
|
<span>{moment(log.timestamp).format("MM/DD/YYYY HH:MM:ss")}</span>
|
||||||
|
<Divider type="vertical" />
|
||||||
|
<span>{log.message}</span>
|
||||||
|
</Space>
|
||||||
|
</Timeline.Item>
|
||||||
|
))}
|
||||||
|
</Timeline>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function LogLevelHierarchy(level) {
|
||||||
|
switch (level) {
|
||||||
|
case "TRACE":
|
||||||
|
return "pink";
|
||||||
|
case "DEBUG":
|
||||||
|
return "orange";
|
||||||
|
case "INFO":
|
||||||
|
return "blue";
|
||||||
|
case "WARNING":
|
||||||
|
return "yellow";
|
||||||
|
case "ERROR":
|
||||||
|
return "red";
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
150
client/src/components/dms-post-form/dms-post-form.component.jsx
Normal file
150
client/src/components/dms-post-form/dms-post-form.component.jsx
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
|
import { Button, Form, Input } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
|
||||||
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm);
|
||||||
|
|
||||||
|
export function DmsPostForm({ bodyshop, socket, jobId }) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Form form={form} layout="vertical">
|
||||||
|
<LayoutFormRow>
|
||||||
|
<Form.Item
|
||||||
|
name="journal"
|
||||||
|
label={t("jobs.fields.dms.journal")}
|
||||||
|
initialValue={
|
||||||
|
bodyshop.cdk_configuration &&
|
||||||
|
bodyshop.cdk_configuration.default_journal
|
||||||
|
}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="dms_make"
|
||||||
|
label={t("jobs.fields.dms.dms_make")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="dms_make"
|
||||||
|
label={t("jobs.fields.dms.dms_make")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<DmsCdkMakes form={form} socket={socket} />
|
||||||
|
</LayoutFormRow>
|
||||||
|
|
||||||
|
<Form.List name={["payers"]}>
|
||||||
|
{(fields, { add, remove }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<LayoutFormRow>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.dms.payer.name")}
|
||||||
|
key={`${index}name`}
|
||||||
|
name={[field.name, "name"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.dms.payer.account")}
|
||||||
|
key={`${index}account`}
|
||||||
|
name={[field.name, "account"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.dms.payer.amount")}
|
||||||
|
key={`${index}amount`}
|
||||||
|
name={[field.name, "amount"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CurrencyInput />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("jobs.fields.dms.payer.controlnumber")}
|
||||||
|
key={`${index}controlnumber`}
|
||||||
|
name={[field.name, "controlnumber"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<DeleteFilled
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -72,7 +72,7 @@ export function JobLineStatusPopup({ bodyshop, jobline, disabled }) {
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
|
style={{ width: "100%", minHeight: "1rem", cursor: "pointer" }}
|
||||||
onClick={() => !disabled && setEditing(true)}
|
onClick={() => !disabled && setEditing(true)}
|
||||||
>
|
>
|
||||||
{jobline.status}
|
{jobline.status}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button } from "antd";
|
import { Button, Dropdown, Menu } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -12,11 +12,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
|
|
||||||
export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
|
export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const handleAllocate = () => {
|
|
||||||
logImEXEvent("jobs_close_allocate_auto");
|
|
||||||
|
|
||||||
const { defaults } = bodyshop.md_responsibility_centers;
|
|
||||||
|
|
||||||
|
const handleAllocate = (defaults) => {
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
joblines: joblines.map((jl) => {
|
joblines: joblines.map((jl) => {
|
||||||
const ret = _.cloneDeep(jl);
|
const ret = _.cloneDeep(jl);
|
||||||
@@ -48,8 +45,36 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
const handleAutoAllocateClick = () => {
|
||||||
<Button onClick={handleAllocate} disabled={disabled}>
|
logImEXEvent("jobs_close_allocate_auto");
|
||||||
|
|
||||||
|
const { defaults } = bodyshop.md_responsibility_centers;
|
||||||
|
handleAllocate(defaults);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMenuClick = ({ item, key, keyPath, domEvent }) => {
|
||||||
|
logImEXEvent("jobs_close_allocate_auto_dms");
|
||||||
|
handleAllocate(
|
||||||
|
bodyshop.md_responsibility_centers.dms_defaults.find(
|
||||||
|
(x) => x.name === key
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const overlay = bodyshop.cdk_dealerid && (
|
||||||
|
<Menu onClick={handleMenuClick}>
|
||||||
|
{bodyshop.md_responsibility_centers.dms_defaults.map((mapping) => (
|
||||||
|
<Menu.Item key={mapping.name}>{mapping.name}</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
return bodyshop.cdk_dealerid ? (
|
||||||
|
<Dropdown overlay={overlay}>
|
||||||
|
<Button disabled={disabled}>{t("jobs.actions.dmsautoallocate")}</Button>
|
||||||
|
</Dropdown>
|
||||||
|
) : (
|
||||||
|
<Button onClick={handleAutoAllocateClick} disabled={disabled}>
|
||||||
{t("jobs.actions.autoallocate")}
|
{t("jobs.actions.autoallocate")}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { alphaSort } from "../../utils/sorters";
|
|||||||
import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component";
|
import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component";
|
||||||
import "./labor-allocations-table.styles.scss";
|
import "./labor-allocations-table.styles.scss";
|
||||||
import { CalculateAllocationsTotals } from "./labor-allocations-table.utility";
|
import { CalculateAllocationsTotals } from "./labor-allocations-table.utility";
|
||||||
|
import _ from "lodash";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
technician: selectTechnician,
|
technician: selectTechnician,
|
||||||
@@ -113,7 +113,7 @@ export function LaborAllocationsTable({
|
|||||||
color: record.difference >= 0 ? "green" : "red",
|
color: record.difference >= 0 ? "green" : "red",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{record.difference}
|
{_.round(record.difference, 1)}
|
||||||
</strong>
|
</strong>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
|
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
|
||||||
|
import _ from "lodash";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
@@ -52,17 +52,20 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
|
|||||||
|
|
||||||
const theValue = {
|
const theValue = {
|
||||||
date: moment(val).format("D dd"),
|
date: moment(val).format("D dd"),
|
||||||
paintHrs: dayhrs.painthrs,
|
paintHrs: _.round(dayhrs.painthrs, 1),
|
||||||
bodyHrs: dayhrs.bodyhrs,
|
bodyHrs: _.round(dayhrs.bodyhrs),
|
||||||
accTargetHrs: Utils.AsOfDateTargetHours(
|
accTargetHrs: _.round(
|
||||||
bodyshop.scoreboard_target.dailyBodyTarget +
|
Utils.AsOfDateTargetHours(
|
||||||
bodyshop.scoreboard_target.dailyPaintTarget,
|
bodyshop.scoreboard_target.dailyBodyTarget +
|
||||||
val
|
bodyshop.scoreboard_target.dailyPaintTarget,
|
||||||
|
val
|
||||||
|
)
|
||||||
),
|
),
|
||||||
accHrs:
|
accHrs: _.round(
|
||||||
acc.length > 0
|
acc.length > 0
|
||||||
? acc[acc.length - 1].accHrs + dayhrs.painthrs + dayhrs.bodyhrs
|
? acc[acc.length - 1].accHrs + dayhrs.painthrs + dayhrs.bodyhrs
|
||||||
: dayhrs.painthrs + dayhrs.bodyhrs,
|
: dayhrs.painthrs + dayhrs.bodyhrs
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
return [...acc, theValue];
|
return [...acc, theValue];
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { Button, Card, Tabs } from "antd";
|
import { Button, Card, Tabs } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import ShopInfoGeneral from "./shop-info.general.component";
|
import ShopInfoGeneral from "./shop-info.general.component";
|
||||||
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
||||||
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
||||||
@@ -11,7 +14,15 @@ import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
|||||||
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
||||||
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
||||||
|
|
||||||
export default function ShopInfoComponent({ form, saveLoading }) {
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
|
||||||
|
|
||||||
|
export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
@@ -53,6 +64,7 @@ export default function ShopInfoComponent({ form, saveLoading }) {
|
|||||||
>
|
>
|
||||||
<ShopInfoResponsibilityCenterComponent form={form} />
|
<ShopInfoResponsibilityCenterComponent form={form} />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
|
||||||
<Tabs.TabPane key="checklists" tab={t("bodyshop.labels.checklists")}>
|
<Tabs.TabPane key="checklists" tab={t("bodyshop.labels.checklists")}>
|
||||||
<ShopInfoIntakeChecklistComponent form={form} />
|
<ShopInfoIntakeChecklistComponent form={form} />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ import RbacWrapper, {
|
|||||||
} from "../rbac-wrapper/rbac-wrapper.component";
|
} from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
|
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import _ from "lodash";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
authLevel: selectAuthLevel,
|
authLevel: selectAuthLevel,
|
||||||
@@ -268,8 +268,12 @@ export function TimeTicketList({
|
|||||||
<Table.Summary.Cell />
|
<Table.Summary.Cell />
|
||||||
<Table.Summary.Cell />
|
<Table.Summary.Cell />
|
||||||
<Table.Summary.Cell />
|
<Table.Summary.Cell />
|
||||||
<Table.Summary.Cell>{totals.productivehrs}</Table.Summary.Cell>
|
<Table.Summary.Cell>
|
||||||
<Table.Summary.Cell>{totals.actualhrs}</Table.Summary.Cell>
|
{_.round(totals.productivehrs, 1)}
|
||||||
|
</Table.Summary.Cell>
|
||||||
|
<Table.Summary.Cell>
|
||||||
|
{_.round(totals.actualhrs, 1)}
|
||||||
|
</Table.Summary.Cell>
|
||||||
<Table.Summary.Cell>
|
<Table.Summary.Cell>
|
||||||
{totals.actualhrs === 0 || !totals.actualhrs
|
{totals.actualhrs === 0 || !totals.actualhrs
|
||||||
? "∞"
|
? "∞"
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ export function TimeTicketModalComponent({
|
|||||||
{emps &&
|
{emps &&
|
||||||
emps.rates.map((item) => (
|
emps.rates.map((item) => (
|
||||||
<Select.Option key={item.cost_center}>
|
<Select.Option key={item.cost_center}>
|
||||||
{item.cost_center}
|
{item.cost_center === "timetickets.labels.shift"
|
||||||
|
? t(item.cost_center)
|
||||||
|
: item.cost_center}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ export const QUERY_JOBS_FOR_EXPORT = gql`
|
|||||||
clm_total
|
clm_total
|
||||||
clm_no
|
clm_no
|
||||||
ins_co_nm
|
ins_co_nm
|
||||||
|
exportlogs {
|
||||||
|
id
|
||||||
|
successful
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -37,6 +41,10 @@ export const QUERY_BILLS_FOR_EXPORT = gql`
|
|||||||
invoice_number
|
invoice_number
|
||||||
is_credit_memo
|
is_credit_memo
|
||||||
total
|
total
|
||||||
|
exportlogs {
|
||||||
|
id
|
||||||
|
successful
|
||||||
|
}
|
||||||
job {
|
job {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
@@ -73,6 +81,10 @@ export const QUERY_PAYMENTS_FOR_EXPORT = gql`
|
|||||||
transactionid
|
transactionid
|
||||||
paymentnum
|
paymentnum
|
||||||
date
|
date
|
||||||
|
exportlogs {
|
||||||
|
id
|
||||||
|
successful
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export const QUERY_BODYSHOP = gql`
|
|||||||
features
|
features
|
||||||
attach_pdf_to_email
|
attach_pdf_to_email
|
||||||
tt_allow_post_to_invoiced
|
tt_allow_post_to_invoiced
|
||||||
|
cdk_configuration
|
||||||
employees {
|
employees {
|
||||||
id
|
id
|
||||||
active
|
active
|
||||||
@@ -182,6 +183,7 @@ export const UPDATE_SHOP = gql`
|
|||||||
cdk_dealerid
|
cdk_dealerid
|
||||||
attach_pdf_to_email
|
attach_pdf_to_email
|
||||||
tt_allow_post_to_invoiced
|
tt_allow_post_to_invoiced
|
||||||
|
cdk_configuration
|
||||||
employees {
|
employees {
|
||||||
id
|
id
|
||||||
first_name
|
first_name
|
||||||
|
|||||||
@@ -1875,3 +1875,100 @@ export const FIND_JOBS_BY_CLAIM = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_JOB_EXPORT_DMS = gql`
|
||||||
|
query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
ro_number
|
||||||
|
invoice_allocation
|
||||||
|
ins_co_id
|
||||||
|
id
|
||||||
|
ded_amt
|
||||||
|
ded_status
|
||||||
|
depreciation_taxes
|
||||||
|
other_amount_payable
|
||||||
|
towing_payable
|
||||||
|
storage_payable
|
||||||
|
adjustment_bottom_line
|
||||||
|
federal_tax_rate
|
||||||
|
state_tax_rate
|
||||||
|
local_tax_rate
|
||||||
|
tax_tow_rt
|
||||||
|
tax_str_rt
|
||||||
|
tax_paint_mat_rt
|
||||||
|
tax_sub_rt
|
||||||
|
tax_lbr_rt
|
||||||
|
tax_levies_rt
|
||||||
|
parts_tax_rates
|
||||||
|
job_totals
|
||||||
|
rate_la1
|
||||||
|
rate_la2
|
||||||
|
rate_la3
|
||||||
|
rate_la4
|
||||||
|
rate_laa
|
||||||
|
rate_lab
|
||||||
|
rate_lad
|
||||||
|
rate_lae
|
||||||
|
rate_laf
|
||||||
|
rate_lag
|
||||||
|
rate_lam
|
||||||
|
rate_lar
|
||||||
|
rate_las
|
||||||
|
rate_lau
|
||||||
|
rate_ma2s
|
||||||
|
rate_ma2t
|
||||||
|
rate_ma3s
|
||||||
|
rate_mabl
|
||||||
|
rate_macs
|
||||||
|
rate_mahw
|
||||||
|
rate_mapa
|
||||||
|
rate_mash
|
||||||
|
rate_matd
|
||||||
|
status
|
||||||
|
date_exported
|
||||||
|
date_invoiced
|
||||||
|
voided
|
||||||
|
scheduled_completion
|
||||||
|
actual_completion
|
||||||
|
scheduled_delivery
|
||||||
|
actual_delivery
|
||||||
|
scheduled_in
|
||||||
|
actual_in
|
||||||
|
bills {
|
||||||
|
id
|
||||||
|
federal_tax_rate
|
||||||
|
local_tax_rate
|
||||||
|
state_tax_rate
|
||||||
|
is_credit_memo
|
||||||
|
billlines {
|
||||||
|
actual_cost
|
||||||
|
cost_center
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
joblines(where: { removed: { _eq: false } }) {
|
||||||
|
id
|
||||||
|
removed
|
||||||
|
tax_part
|
||||||
|
line_desc
|
||||||
|
prt_dsmk_p
|
||||||
|
prt_dsmk_m
|
||||||
|
part_type
|
||||||
|
oem_partno
|
||||||
|
db_price
|
||||||
|
act_price
|
||||||
|
part_qty
|
||||||
|
mod_lbr_ty
|
||||||
|
db_hrs
|
||||||
|
mod_lb_hrs
|
||||||
|
lbr_op
|
||||||
|
lbr_amt
|
||||||
|
op_code_desc
|
||||||
|
profitcenter_labor
|
||||||
|
profitcenter_part
|
||||||
|
prt_dsmk_p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import { gql } from "@apollo/client";
|
|||||||
|
|
||||||
export const SUBSCRIPTION_SCOREBOARD = gql`
|
export const SUBSCRIPTION_SCOREBOARD = gql`
|
||||||
subscription SUBSCRIPTION_SCOREBOARD($start: date!, $end: date!) {
|
subscription SUBSCRIPTION_SCOREBOARD($start: date!, $end: date!) {
|
||||||
scoreboard(where: { _and: { date: { _gte: $start, _lte: $end } } }) {
|
scoreboard(
|
||||||
|
where: { _and: { date: { _gte: $start, _lte: $end } } }
|
||||||
|
order_by: { date: asc }
|
||||||
|
) {
|
||||||
id
|
id
|
||||||
painthrs
|
painthrs
|
||||||
bodyhrs
|
bodyhrs
|
||||||
|
|||||||
@@ -1,16 +1,25 @@
|
|||||||
import { Result, Timeline, Space, Tag, Divider, Button } from "antd";
|
//import { useQuery } from "@apollo/client";
|
||||||
|
import { Button, Col, Result, Row, Select, Space } from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import SocketIO from "socket.io-client";
|
||||||
|
//import AlertComponent from "../../components/alert/alert.component";
|
||||||
|
import DmsAllocationsSummary from "../../components/dms-allocations-summary/dms-allocations-summary.component";
|
||||||
|
import DmsCustomerSelector from "../../components/dms-customer-selector/dms-customer-selector.component";
|
||||||
|
import DmsLogEvents from "../../components/dms-log-events/dms-log-events.component";
|
||||||
|
import DmsPostForm from "../../components/dms-post-form/dms-post-form.component";
|
||||||
|
//import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||||
|
import { auth } from "../../firebase/firebase.utils";
|
||||||
|
//import { QUERY_JOB_EXPORT_DMS } from "../../graphql/jobs.queries";
|
||||||
import {
|
import {
|
||||||
setBreadcrumbs,
|
setBreadcrumbs,
|
||||||
setSelectedHeader,
|
setSelectedHeader,
|
||||||
} from "../../redux/application/application.actions";
|
} from "../../redux/application/application.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import SocketIO from "socket.io-client";
|
|
||||||
import { auth } from "../../firebase/firebase.utils";
|
|
||||||
import moment from "moment";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -38,7 +47,15 @@ export const socket = SocketIO(
|
|||||||
|
|
||||||
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [logLevel, setLogLevel] = useState("TRACE");
|
||||||
const [logs, setLogs] = useState([]);
|
const [logs, setLogs] = useState([]);
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
|
const { jobId } = search;
|
||||||
|
|
||||||
|
// const { loading, error } = useQuery(QUERY_JOB_EXPORT_DMS, {
|
||||||
|
// variables: { id: jobId },
|
||||||
|
// skip: true, //!jobId,
|
||||||
|
// });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("titles.dms");
|
document.title = t("titles.dms");
|
||||||
@@ -55,6 +72,19 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
socket.on("connected", () => {
|
socket.on("connected", () => {
|
||||||
console.log("Connected again.");
|
console.log("Connected again.");
|
||||||
});
|
});
|
||||||
|
socket.on("reconnect", () => {
|
||||||
|
console.log("Connected again.");
|
||||||
|
setLogs((logs) => {
|
||||||
|
return [
|
||||||
|
...logs,
|
||||||
|
{
|
||||||
|
timestamp: new Date(),
|
||||||
|
level: "WARNING",
|
||||||
|
message: "Reconnected to CDK Export Service",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("log-event", (payload) => {
|
socket.on("log-event", (payload) => {
|
||||||
setLogs((logs) => {
|
setLogs((logs) => {
|
||||||
@@ -63,74 +93,68 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.connect();
|
socket.connect();
|
||||||
socket.emit("set-log-level", "TRACE");
|
socket.emit("set-log-level", logLevel);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
socket.removeAllListeners();
|
socket.removeAllListeners();
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
};
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!bodyshop.cdk_dealerid) return <Result status="404" />;
|
if (!jobId || !bodyshop.cdk_dealerid) return <Result status="404" />;
|
||||||
|
|
||||||
const dmsType = determineDmsType(bodyshop);
|
const dmsType = determineDmsType(bodyshop);
|
||||||
|
|
||||||
|
// if (loading) return <LoadingSpinner />;
|
||||||
|
// if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Space>
|
||||||
onClick={() => {
|
<Button
|
||||||
socket.emit(
|
onClick={() => {
|
||||||
`${dmsType}-export-job`,
|
socket.emit(
|
||||||
"752a4f5f-22ab-414b-b182-98d4e62227ef"
|
`${dmsType}-export-job`,
|
||||||
);
|
"752a4f5f-22ab-414b-b182-98d4e62227ef"
|
||||||
}}
|
);
|
||||||
>
|
}}
|
||||||
Export
|
>
|
||||||
</Button>
|
Export
|
||||||
|
</Button>
|
||||||
|
<Select
|
||||||
|
placeholder="Log Level"
|
||||||
|
value={logLevel}
|
||||||
|
onChange={(value) => {
|
||||||
|
setLogLevel(value);
|
||||||
|
socket.emit("set-log-level", value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select.Option key="TRACE">TRACE</Select.Option>
|
||||||
|
<Select.Option key="DEBUG">DEBUG</Select.Option>
|
||||||
|
<Select.Option key="INFO">INFO</Select.Option>
|
||||||
|
<Select.Option key="WARNING">WARNING</Select.Option>
|
||||||
|
<Select.Option key="ERROR">ERROR</Select.Option>
|
||||||
|
</Select>
|
||||||
|
<Button onClick={() => setLogs([])}>Clear Logs</Button>
|
||||||
|
</Space>
|
||||||
|
<Row gutter={32}>
|
||||||
|
<Col span={18}>
|
||||||
|
<DmsAllocationsSummary socket={socket} jobId={jobId} />
|
||||||
|
<DmsPostForm socket={socket} jobId={jobId} />
|
||||||
|
</Col>
|
||||||
|
<Col span={6}>
|
||||||
|
<div style={{ maxHeight: "500px", overflowY: "auto" }}>
|
||||||
|
<DmsLogEvents socket={socket} logs={logs} />
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
<Button
|
<DmsCustomerSelector />
|
||||||
onClick={() => {
|
|
||||||
setLogs([]);
|
|
||||||
socket.disconnect();
|
|
||||||
socket.connect();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
reconnect
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Timeline pending={socket.connected && "Processing..."} reverse={true}>
|
|
||||||
{logs.map((log, idx) => (
|
|
||||||
<Timeline.Item key={idx} color={LogLevelHierarchy(log.level)}>
|
|
||||||
<Space wrap align="start" style={{}}>
|
|
||||||
<Tag color={LogLevelHierarchy(log.level)}>{log.level}</Tag>
|
|
||||||
<span>{moment(log.timestamp).format("MM/DD/YYYY HH:MM:ss")}</span>
|
|
||||||
<Divider type="vertical" />
|
|
||||||
<span>{log.message}</span>
|
|
||||||
</Space>
|
|
||||||
</Timeline.Item>
|
|
||||||
))}
|
|
||||||
</Timeline>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LogLevelHierarchy(level) {
|
|
||||||
switch (level) {
|
|
||||||
case "TRACE":
|
|
||||||
return "pink";
|
|
||||||
case "DEBUG":
|
|
||||||
return "orange";
|
|
||||||
case "INFO":
|
|
||||||
return "blue";
|
|
||||||
case "WARNING":
|
|
||||||
return "yellow";
|
|
||||||
case "ERROR":
|
|
||||||
return "red";
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const determineDmsType = (bodyshop) => {
|
const determineDmsType = (bodyshop) => {
|
||||||
if (bodyshop.cdk_dealerid) return "cdk";
|
if (bodyshop.cdk_dealerid) return "cdk";
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import DmsActions from "./dms.types";
|
|
||||||
|
|
||||||
export const endLoading = (options) => ({
|
|
||||||
// type: DmsActions.END_LOADING,
|
|
||||||
payload: options,
|
|
||||||
});
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import DmsActionTypes from "./dms.types";
|
|
||||||
|
|
||||||
const INITIAL_STATE = {
|
|
||||||
eventLog: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const dmsReducer = (state = INITIAL_STATE, action) => {
|
|
||||||
switch (action.type) {
|
|
||||||
// case ApplicationActionTypes.SET_SELECTED_HEADER:
|
|
||||||
// return {
|
|
||||||
// ...state,
|
|
||||||
// selectedHeader: action.payload,
|
|
||||||
// };
|
|
||||||
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default dmsReducer;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { all, call } from "redux-saga/effects";
|
|
||||||
//import DmsActionTypes from "./dms.types";
|
|
||||||
|
|
||||||
export function* onCalculateScheduleLoad() {
|
|
||||||
// yield takeLatest(
|
|
||||||
// DmsActionTypes.CALCULATE_SCHEDULE_LOAD,
|
|
||||||
// calculateScheduleLoad
|
|
||||||
// );
|
|
||||||
}
|
|
||||||
export function* calculateScheduleLoad({ payload: end }) {}
|
|
||||||
|
|
||||||
export function* dmsSagas() {
|
|
||||||
yield all([call()]);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { createSelector } from "reselect";
|
|
||||||
|
|
||||||
const selectDms = (state) => state.dms;
|
|
||||||
|
|
||||||
export const selectEventLog = createSelector(
|
|
||||||
[selectDms],
|
|
||||||
(dms) => dms.eventLog
|
|
||||||
);
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
const DmsActionTypes = {
|
|
||||||
ADD_EVENT: "ADD_EVENT",
|
|
||||||
};
|
|
||||||
export default DmsActionTypes;
|
|
||||||
@@ -231,6 +231,12 @@
|
|||||||
"deliver": {
|
"deliver": {
|
||||||
"templates": "Delivery Templates"
|
"templates": "Delivery Templates"
|
||||||
},
|
},
|
||||||
|
"dms": {
|
||||||
|
"default_journal": "Default Journal",
|
||||||
|
"dms_acctnumber": "DMS Account #",
|
||||||
|
"dms_wip_acctnumber": "DMS W.I.P. Account #",
|
||||||
|
"mappingname": "DMS Mapping Name"
|
||||||
|
},
|
||||||
"email": "General Shop Email",
|
"email": "General Shop Email",
|
||||||
"enforce_class": "Enforce Class on Conversion?",
|
"enforce_class": "Enforce Class on Conversion?",
|
||||||
"enforce_referral": "Enforce Referrals",
|
"enforce_referral": "Enforce Referrals",
|
||||||
@@ -467,6 +473,10 @@
|
|||||||
"defaultcostsmapping": "Default Costs Mapping",
|
"defaultcostsmapping": "Default Costs Mapping",
|
||||||
"defaultprofitsmapping": "Default Profits Mapping",
|
"defaultprofitsmapping": "Default Profits Mapping",
|
||||||
"deliverchecklist": "Delivery Checklist",
|
"deliverchecklist": "Delivery Checklist",
|
||||||
|
"dms": {
|
||||||
|
"cdk_dealerid": "CDK Dealer ID",
|
||||||
|
"title": "DMS"
|
||||||
|
},
|
||||||
"employees": "Employees",
|
"employees": "Employees",
|
||||||
"insurancecos": "Insurance Companies",
|
"insurancecos": "Insurance Companies",
|
||||||
"intakechecklist": "Intake Checklist",
|
"intakechecklist": "Intake Checklist",
|
||||||
@@ -815,6 +825,9 @@
|
|||||||
"exportlogs": {
|
"exportlogs": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"createdat": "Created At"
|
"createdat": "Created At"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"attempts": "Export Attempts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
@@ -1051,6 +1064,7 @@
|
|||||||
"changestatus": "Change Status",
|
"changestatus": "Change Status",
|
||||||
"convert": "Convert",
|
"convert": "Convert",
|
||||||
"deliver": "Deliver",
|
"deliver": "Deliver",
|
||||||
|
"dmsautoallocate": "DMS Auto Allocate",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"exportcustdata": "Export Customer Data",
|
"exportcustdata": "Export Customer Data",
|
||||||
"exportselected": "Export Selected",
|
"exportselected": "Export Selected",
|
||||||
@@ -1130,6 +1144,14 @@
|
|||||||
"ded_amt": "Deductible",
|
"ded_amt": "Deductible",
|
||||||
"ded_status": "Deductible Status",
|
"ded_status": "Deductible Status",
|
||||||
"depreciation_taxes": "Depreciation/Taxes",
|
"depreciation_taxes": "Depreciation/Taxes",
|
||||||
|
"dms": {
|
||||||
|
"center": "Center",
|
||||||
|
"cost": "Cost",
|
||||||
|
"cost_dms_acctnumber": "Cost DMS Acct #",
|
||||||
|
"dms_wip_acctnumber": "Cost WIP DMS Acct #",
|
||||||
|
"sale": "Sale",
|
||||||
|
"sale_dms_acctnumber": "Sale DMS Acct #"
|
||||||
|
},
|
||||||
"driveable": "Driveable",
|
"driveable": "Driveable",
|
||||||
"employee_body": "Body",
|
"employee_body": "Body",
|
||||||
"employee_csr": "Customer Service Rep.",
|
"employee_csr": "Customer Service Rep.",
|
||||||
@@ -2090,6 +2112,11 @@
|
|||||||
"updated": "Scoreboard updated."
|
"updated": "Scoreboard updated."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"scoredboard": {
|
||||||
|
"successes": {
|
||||||
|
"updated": "Scoreboard entry updated."
|
||||||
|
}
|
||||||
|
},
|
||||||
"tech": {
|
"tech": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"employeeid": "Employee ID",
|
"employeeid": "Employee ID",
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"deliver": {
|
"deliver": {
|
||||||
"templates": ""
|
"templates": ""
|
||||||
},
|
},
|
||||||
|
"dms": {
|
||||||
|
"default_journal": "",
|
||||||
|
"dms_acctnumber": "",
|
||||||
|
"dms_wip_acctnumber": "",
|
||||||
|
"mappingname": ""
|
||||||
|
},
|
||||||
"email": "",
|
"email": "",
|
||||||
"enforce_class": "",
|
"enforce_class": "",
|
||||||
"enforce_referral": "",
|
"enforce_referral": "",
|
||||||
@@ -467,6 +473,10 @@
|
|||||||
"defaultcostsmapping": "",
|
"defaultcostsmapping": "",
|
||||||
"defaultprofitsmapping": "",
|
"defaultprofitsmapping": "",
|
||||||
"deliverchecklist": "",
|
"deliverchecklist": "",
|
||||||
|
"dms": {
|
||||||
|
"cdk_dealerid": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
"employees": "",
|
"employees": "",
|
||||||
"insurancecos": "",
|
"insurancecos": "",
|
||||||
"intakechecklist": "",
|
"intakechecklist": "",
|
||||||
@@ -815,6 +825,9 @@
|
|||||||
"exportlogs": {
|
"exportlogs": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"createdat": ""
|
"createdat": ""
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"attempts": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
@@ -1051,6 +1064,7 @@
|
|||||||
"changestatus": "Cambiar Estado",
|
"changestatus": "Cambiar Estado",
|
||||||
"convert": "Convertir",
|
"convert": "Convertir",
|
||||||
"deliver": "",
|
"deliver": "",
|
||||||
|
"dmsautoallocate": "",
|
||||||
"export": "",
|
"export": "",
|
||||||
"exportcustdata": "",
|
"exportcustdata": "",
|
||||||
"exportselected": "",
|
"exportselected": "",
|
||||||
@@ -1130,6 +1144,14 @@
|
|||||||
"ded_amt": "Deducible",
|
"ded_amt": "Deducible",
|
||||||
"ded_status": "Estado deducible",
|
"ded_status": "Estado deducible",
|
||||||
"depreciation_taxes": "Depreciación / Impuestos",
|
"depreciation_taxes": "Depreciación / Impuestos",
|
||||||
|
"dms": {
|
||||||
|
"center": "",
|
||||||
|
"cost": "",
|
||||||
|
"cost_dms_acctnumber": "",
|
||||||
|
"dms_wip_acctnumber": "",
|
||||||
|
"sale": "",
|
||||||
|
"sale_dms_acctnumber": ""
|
||||||
|
},
|
||||||
"driveable": "",
|
"driveable": "",
|
||||||
"employee_body": "",
|
"employee_body": "",
|
||||||
"employee_csr": "Representante de servicio al cliente.",
|
"employee_csr": "Representante de servicio al cliente.",
|
||||||
@@ -2090,6 +2112,11 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"scoredboard": {
|
||||||
|
"successes": {
|
||||||
|
"updated": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"tech": {
|
"tech": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"employeeid": "",
|
"employeeid": "",
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"deliver": {
|
"deliver": {
|
||||||
"templates": ""
|
"templates": ""
|
||||||
},
|
},
|
||||||
|
"dms": {
|
||||||
|
"default_journal": "",
|
||||||
|
"dms_acctnumber": "",
|
||||||
|
"dms_wip_acctnumber": "",
|
||||||
|
"mappingname": ""
|
||||||
|
},
|
||||||
"email": "",
|
"email": "",
|
||||||
"enforce_class": "",
|
"enforce_class": "",
|
||||||
"enforce_referral": "",
|
"enforce_referral": "",
|
||||||
@@ -467,6 +473,10 @@
|
|||||||
"defaultcostsmapping": "",
|
"defaultcostsmapping": "",
|
||||||
"defaultprofitsmapping": "",
|
"defaultprofitsmapping": "",
|
||||||
"deliverchecklist": "",
|
"deliverchecklist": "",
|
||||||
|
"dms": {
|
||||||
|
"cdk_dealerid": "",
|
||||||
|
"title": ""
|
||||||
|
},
|
||||||
"employees": "",
|
"employees": "",
|
||||||
"insurancecos": "",
|
"insurancecos": "",
|
||||||
"intakechecklist": "",
|
"intakechecklist": "",
|
||||||
@@ -815,6 +825,9 @@
|
|||||||
"exportlogs": {
|
"exportlogs": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"createdat": ""
|
"createdat": ""
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"attempts": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
@@ -1051,6 +1064,7 @@
|
|||||||
"changestatus": "Changer le statut",
|
"changestatus": "Changer le statut",
|
||||||
"convert": "Convertir",
|
"convert": "Convertir",
|
||||||
"deliver": "",
|
"deliver": "",
|
||||||
|
"dmsautoallocate": "",
|
||||||
"export": "",
|
"export": "",
|
||||||
"exportcustdata": "",
|
"exportcustdata": "",
|
||||||
"exportselected": "",
|
"exportselected": "",
|
||||||
@@ -1130,6 +1144,14 @@
|
|||||||
"ded_amt": "Déductible",
|
"ded_amt": "Déductible",
|
||||||
"ded_status": "Statut de franchise",
|
"ded_status": "Statut de franchise",
|
||||||
"depreciation_taxes": "Amortissement / taxes",
|
"depreciation_taxes": "Amortissement / taxes",
|
||||||
|
"dms": {
|
||||||
|
"center": "",
|
||||||
|
"cost": "",
|
||||||
|
"cost_dms_acctnumber": "",
|
||||||
|
"dms_wip_acctnumber": "",
|
||||||
|
"sale": "",
|
||||||
|
"sale_dms_acctnumber": ""
|
||||||
|
},
|
||||||
"driveable": "",
|
"driveable": "",
|
||||||
"employee_body": "",
|
"employee_body": "",
|
||||||
"employee_csr": "représentant du service à la clientèle",
|
"employee_csr": "représentant du service à la clientèle",
|
||||||
@@ -2090,6 +2112,11 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"scoredboard": {
|
||||||
|
"successes": {
|
||||||
|
"updated": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"tech": {
|
"tech": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"employeeid": "",
|
"employeeid": "",
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "cdk_configuration";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "cdk_configuration" jsonb NULL;
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- attach_pdf_to_email
|
||||||
|
- bill_tax_rates
|
||||||
|
- cdk_dealerid
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- features
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- imexshopid
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- jobsizelimit
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_jobline_presets
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- messagingservicesid
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- region_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- stripe_acct_id
|
||||||
|
- sub_status
|
||||||
|
- target_touchtime
|
||||||
|
- template_header
|
||||||
|
- textid
|
||||||
|
- tt_allow_post_to_invoiced
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- attach_pdf_to_email
|
||||||
|
- bill_tax_rates
|
||||||
|
- cdk_configuration
|
||||||
|
- cdk_dealerid
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- features
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- imexshopid
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- jobsizelimit
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_jobline_presets
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- messagingservicesid
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- region_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- stripe_acct_id
|
||||||
|
- sub_status
|
||||||
|
- target_touchtime
|
||||||
|
- template_header
|
||||||
|
- textid
|
||||||
|
- tt_allow_post_to_invoiced
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- attach_pdf_to_email
|
||||||
|
- bill_tax_rates
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_jobline_presets
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- target_touchtime
|
||||||
|
- tt_allow_post_to_invoiced
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- attach_pdf_to_email
|
||||||
|
- bill_tax_rates
|
||||||
|
- cdk_configuration
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_jobline_presets
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- target_touchtime
|
||||||
|
- tt_allow_post_to_invoiced
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -796,6 +796,7 @@ tables:
|
|||||||
- appt_length
|
- appt_length
|
||||||
- attach_pdf_to_email
|
- attach_pdf_to_email
|
||||||
- bill_tax_rates
|
- bill_tax_rates
|
||||||
|
- cdk_configuration
|
||||||
- cdk_dealerid
|
- cdk_dealerid
|
||||||
- city
|
- city
|
||||||
- country
|
- country
|
||||||
@@ -873,6 +874,7 @@ tables:
|
|||||||
- appt_length
|
- appt_length
|
||||||
- attach_pdf_to_email
|
- attach_pdf_to_email
|
||||||
- bill_tax_rates
|
- bill_tax_rates
|
||||||
|
- cdk_configuration
|
||||||
- city
|
- city
|
||||||
- country
|
- country
|
||||||
- created_at
|
- created_at
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
"firebase-admin": "^9.11.0",
|
"firebase-admin": "^9.11.0",
|
||||||
"graphql": "^15.5.1",
|
"graphql": "^15.5.1",
|
||||||
"graphql-request": "^3.4.0",
|
"graphql-request": "^3.4.0",
|
||||||
|
"graylog2": "^0.2.1",
|
||||||
"inline-css": "^3.0.0",
|
"inline-css": "^3.0.0",
|
||||||
"intuit-oauth": "^4.0.0",
|
"intuit-oauth": "^4.0.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const bodyParser = require("body-parser");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const compression = require("compression");
|
const compression = require("compression");
|
||||||
const twilio = require("twilio");
|
const twilio = require("twilio");
|
||||||
|
const logger = require("./server/utils/logger");
|
||||||
global.fetch = require("node-fetch");
|
global.fetch = require("node-fetch");
|
||||||
var fb = require("./server/firebase/firebase-handler");
|
var fb = require("./server/firebase/firebase-handler");
|
||||||
|
|
||||||
@@ -33,7 +34,8 @@ app.post("/sendemail", fb.validateFirebaseIdToken, sendEmail.sendEmail);
|
|||||||
|
|
||||||
//Test route to ensure Express is responding.
|
//Test route to ensure Express is responding.
|
||||||
app.get("/test", async function (req, res) {
|
app.get("/test", async function (req, res) {
|
||||||
console.log("Incoming request verified.", req);
|
logger.log("test-api", null, null, null);
|
||||||
|
|
||||||
const Commit = require("child_process").execSync(
|
const Commit = require("child_process").execSync(
|
||||||
"git rev-parse --short HEAD"
|
"git rev-parse --short HEAD"
|
||||||
);
|
);
|
||||||
@@ -166,7 +168,7 @@ const io = new Server(server, {
|
|||||||
|
|
||||||
server.listen(port, (error) => {
|
server.listen(port, (error) => {
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
console.log(`[${process.env.NODE_ENV}] Server running on port ${port}`);
|
logger.log(`[${process.env.NODE_ENV}] Server running on port ${port}`);
|
||||||
});
|
});
|
||||||
exports.io = io;
|
exports.io = io;
|
||||||
require("./server/web-sockets/web-socket");
|
require("./server/web-sockets/web-socket");
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const Dinero = require("dinero.js");
|
|||||||
var builder = require("xmlbuilder2");
|
var builder = require("xmlbuilder2");
|
||||||
const QbXmlUtils = require("./qbxml-utils");
|
const QbXmlUtils = require("./qbxml-utils");
|
||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
|
const logger = require("../../utils/logger");
|
||||||
|
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
path: path.resolve(
|
path: path.resolve(
|
||||||
@@ -45,7 +46,13 @@ exports.default = async (req, res) => {
|
|||||||
|
|
||||||
res.status(200).json(QbXmlToExecute);
|
res.status(200).json(QbXmlToExecute);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("error", error);
|
logger.log(
|
||||||
|
"qbxml-payable-error",
|
||||||
|
"error",
|
||||||
|
req.body.user,
|
||||||
|
req.body.billsToQuery,
|
||||||
|
error
|
||||||
|
);
|
||||||
res.status(400).send(JSON.stringify(error));
|
res.status(400).send(JSON.stringify(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -89,7 +96,6 @@ const generateBill = (bill) => {
|
|||||||
.end({ pretty: true });
|
.end({ pretty: true });
|
||||||
|
|
||||||
const billQbxml_Full = QbXmlUtils.addQbxmlHeader(billQbxml_partial);
|
const billQbxml_Full = QbXmlUtils.addQbxmlHeader(billQbxml_partial);
|
||||||
console.log("generateBill -> billQbxml_Full", billQbxml_Full);
|
|
||||||
|
|
||||||
return billQbxml_Full;
|
return billQbxml_Full;
|
||||||
};
|
};
|
||||||
@@ -131,7 +137,6 @@ const findTaxCode = (billLine, taxcode) => {
|
|||||||
!!t.federal === !!federal
|
!!t.federal === !!federal
|
||||||
);
|
);
|
||||||
if (t.length === 1) {
|
if (t.length === 1) {
|
||||||
console.log(t);
|
|
||||||
return t[0].code;
|
return t[0].code;
|
||||||
} else if (t.length > 1) {
|
} else if (t.length > 1) {
|
||||||
return "Multiple Tax Codes Match";
|
return "Multiple Tax Codes Match";
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ var builder = require("xmlbuilder2");
|
|||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
const QbXmlUtils = require("./qbxml-utils");
|
const QbXmlUtils = require("./qbxml-utils");
|
||||||
const QbxmlReceivables = require("./qbxml-receivables");
|
const QbxmlReceivables = require("./qbxml-receivables");
|
||||||
|
const logger = require("../../utils/logger");
|
||||||
|
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
path: path.resolve(
|
path: path.resolve(
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
@@ -80,13 +82,18 @@ exports.default = async (req, res) => {
|
|||||||
|
|
||||||
res.status(200).json(QbXmlToExecute);
|
res.status(200).json(QbXmlToExecute);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("error", error);
|
logger.log(
|
||||||
|
"qbxml-payments-error",
|
||||||
|
"error",
|
||||||
|
req.body.user,
|
||||||
|
req.body.paymentsToQuery,
|
||||||
|
error
|
||||||
|
);
|
||||||
res.status(400).send(JSON.stringify(error));
|
res.status(400).send(JSON.stringify(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generatePayment = (payment, isThreeTier, twoTierPref) => {
|
const generatePayment = (payment, isThreeTier, twoTierPref) => {
|
||||||
console.log("generatePayment -> payment", payment);
|
|
||||||
let paymentQbxmlObj;
|
let paymentQbxmlObj;
|
||||||
if (payment.amount > 0) {
|
if (payment.amount > 0) {
|
||||||
paymentQbxmlObj = {
|
paymentQbxmlObj = {
|
||||||
@@ -194,7 +201,6 @@ const generatePayment = (payment, isThreeTier, twoTierPref) => {
|
|||||||
.end({ pretty: true });
|
.end({ pretty: true });
|
||||||
|
|
||||||
const paymentQbxmlFull = QbXmlUtils.addQbxmlHeader(paymentQbxmlPartial);
|
const paymentQbxmlFull = QbXmlUtils.addQbxmlHeader(paymentQbxmlPartial);
|
||||||
console.log("generateBill -> paymentQbxmlFull", paymentQbxmlFull);
|
|
||||||
|
|
||||||
return paymentQbxmlFull;
|
return paymentQbxmlFull;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ const Dinero = require("dinero.js");
|
|||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
var builder = require("xmlbuilder2");
|
var builder = require("xmlbuilder2");
|
||||||
const QbXmlUtils = require("./qbxml-utils");
|
const QbXmlUtils = require("./qbxml-utils");
|
||||||
|
const logger = require("../../utils/logger");
|
||||||
|
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
path: path.resolve(
|
path: path.resolve(
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
@@ -93,7 +95,13 @@ exports.default = async (req, res) => {
|
|||||||
|
|
||||||
res.status(200).json(QbXmlToExecute);
|
res.status(200).json(QbXmlToExecute);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("error", error);
|
logger.log(
|
||||||
|
"qbxml-payments-error",
|
||||||
|
"error",
|
||||||
|
req.body.user,
|
||||||
|
req.body.jobIds,
|
||||||
|
error
|
||||||
|
);
|
||||||
res.status(400).send(JSON.stringify(error));
|
res.status(400).send(JSON.stringify(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -220,7 +228,7 @@ const generateInvoiceQbxml = (
|
|||||||
}).multiply(jobline.part_qty || 1);
|
}).multiply(jobline.part_qty || 1);
|
||||||
|
|
||||||
if (jobline.prt_dsmk_p && jobline.prt_dsmk_p !== 0) {
|
if (jobline.prt_dsmk_p && jobline.prt_dsmk_p !== 0) {
|
||||||
console.log("Have a part discount", jobline);
|
// console.log("Have a part discount", jobline);
|
||||||
DineroAmount = DineroAmount.add(
|
DineroAmount = DineroAmount.add(
|
||||||
DineroAmount.percentage(jobline.prt_dsmk_p || 0)
|
DineroAmount.percentage(jobline.prt_dsmk_p || 0)
|
||||||
);
|
);
|
||||||
@@ -230,6 +238,13 @@ const generateInvoiceQbxml = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
|
logger.log(
|
||||||
|
"qbxml-receivables-no-account",
|
||||||
|
"warn",
|
||||||
|
null,
|
||||||
|
jobline.id,
|
||||||
|
null
|
||||||
|
);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`A matching account does not exist for the part allocation. Center: ${jobline.profitcenter_part}`
|
`A matching account does not exist for the part allocation. Center: ${jobline.profitcenter_part}`
|
||||||
);
|
);
|
||||||
@@ -309,7 +324,7 @@ const generateInvoiceQbxml = (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log("NO MAPA ACCOUNT FOUND!!");
|
//console.log("NO MAPA ACCOUNT FOUND!!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
167
server/cdk/cdk-calculate-allocations.js
Normal file
167
server/cdk/cdk-calculate-allocations.js
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
const path = require("path");
|
||||||
|
require("dotenv").config({
|
||||||
|
path: path.resolve(
|
||||||
|
process.cwd(),
|
||||||
|
`.env.${process.env.NODE_ENV || "development"}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||||
|
|
||||||
|
const queries = require("../graphql-client/queries");
|
||||||
|
const CdkBase = require("../web-sockets/web-socket");
|
||||||
|
|
||||||
|
const Dinero = require("dinero.js");
|
||||||
|
const _ = require("lodash");
|
||||||
|
|
||||||
|
exports.default = async function (socket, jobid) {
|
||||||
|
try {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`Received request to calculate allocations for ${jobid}`
|
||||||
|
);
|
||||||
|
const job = await QueryJobData(socket, jobid);
|
||||||
|
const { bodyshop } = job;
|
||||||
|
|
||||||
|
const taxAllocations = {
|
||||||
|
local: {
|
||||||
|
center: bodyshop.md_responsibility_centers.taxes.local.name,
|
||||||
|
sale: Dinero(job.job_totals.totals.local_tax),
|
||||||
|
cost: Dinero(),
|
||||||
|
profitCenter: bodyshop.md_responsibility_centers.taxes.local,
|
||||||
|
costCenter: bodyshop.md_responsibility_centers.taxes.local,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
center: bodyshop.md_responsibility_centers.taxes.state.name,
|
||||||
|
sale: Dinero(job.job_totals.totals.state_tax),
|
||||||
|
cost: Dinero(),
|
||||||
|
profitCenter: bodyshop.md_responsibility_centers.taxes.state,
|
||||||
|
costCenter: bodyshop.md_responsibility_centers.taxes.state,
|
||||||
|
},
|
||||||
|
federal: {
|
||||||
|
center: bodyshop.md_responsibility_centers.taxes.federal.name,
|
||||||
|
sale: Dinero(job.job_totals.totals.federal_tax),
|
||||||
|
cost: Dinero(),
|
||||||
|
profitCenter: bodyshop.md_responsibility_centers.taxes.federal,
|
||||||
|
costCenter: bodyshop.md_responsibility_centers.taxes.federal,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const profitCenterHash = job.joblines.reduce((acc, val) => {
|
||||||
|
//Check the Parts Assignment
|
||||||
|
if (val.profitcenter_part) {
|
||||||
|
if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero();
|
||||||
|
|
||||||
|
acc[val.profitcenter_part] = acc[val.profitcenter_part].add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round((val.act_price || 0) * 100),
|
||||||
|
}).multiply(val.part_qty || 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (val.profitcenter_labor) {
|
||||||
|
//Check the Labor Assignment.
|
||||||
|
|
||||||
|
if (!acc[val.profitcenter_labor])
|
||||||
|
acc[val.profitcenter_labor] = Dinero();
|
||||||
|
|
||||||
|
acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round(
|
||||||
|
job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100
|
||||||
|
),
|
||||||
|
}).multiply(val.mod_lb_hrs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const costCenterHash = job.bills.reduce((bill_acc, bill_val) => {
|
||||||
|
bill_val.billlines.map((line_val) => {
|
||||||
|
if (!bill_acc[line_val.cost_center])
|
||||||
|
bill_acc[line_val.cost_center] = Dinero();
|
||||||
|
const lineDinero = Dinero({
|
||||||
|
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||||
|
})
|
||||||
|
.multiply(line_val.quantity)
|
||||||
|
.multiply(bill_val.is_credit_memo ? -1 : 1);
|
||||||
|
|
||||||
|
bill_acc[line_val.cost_center] =
|
||||||
|
bill_acc[line_val.cost_center].add(lineDinero);
|
||||||
|
|
||||||
|
//Add appropriate tax amounts.
|
||||||
|
const {
|
||||||
|
applicable_taxes: { local, state, federal },
|
||||||
|
} = line_val;
|
||||||
|
if (local) {
|
||||||
|
taxAllocations.local.cost = taxAllocations.local.cost.add(
|
||||||
|
lineDinero.percentage(bill_val.local_tax_rate || 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (state) {
|
||||||
|
taxAllocations.state.cost = taxAllocations.state.cost.add(
|
||||||
|
lineDinero.percentage(bill_val.state_tax_rate || 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (federal) {
|
||||||
|
taxAllocations.federal.cost = taxAllocations.federal.cost.add(
|
||||||
|
lineDinero.percentage(bill_val.federal_tax_rate || 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
return bill_acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const jobAllocations = _.union(
|
||||||
|
Object.keys(profitCenterHash),
|
||||||
|
Object.keys(costCenterHash)
|
||||||
|
).map((key) => {
|
||||||
|
const profitCenter = bodyshop.md_responsibility_centers.profits.find(
|
||||||
|
(c) => c.name === key
|
||||||
|
);
|
||||||
|
const costCenter = bodyshop.md_responsibility_centers.costs.find(
|
||||||
|
(c) => c.name === key
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
center: key,
|
||||||
|
sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(),
|
||||||
|
cost: costCenterHash[key] ? costCenterHash[key] : Dinero(),
|
||||||
|
profitCenter,
|
||||||
|
costCenter,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
...jobAllocations,
|
||||||
|
...Object.keys(taxAllocations)
|
||||||
|
.filter(
|
||||||
|
(key) =>
|
||||||
|
taxAllocations[key].sale.getAmount() > 0 ||
|
||||||
|
taxAllocations[key].cost.getAmount() > 0
|
||||||
|
)
|
||||||
|
.map((key) => taxAllocations[key]),
|
||||||
|
];
|
||||||
|
} catch (error) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error encountered in CdkCalculateAllocations. ${error}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function QueryJobData(socket, jobid) {
|
||||||
|
CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
|
||||||
|
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
|
||||||
|
const result = await client
|
||||||
|
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
|
||||||
|
.request(queries.GET_CDK_ALLOCATIONS, { id: jobid });
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`Job data query result ${JSON.stringify(result, null, 2)}`
|
||||||
|
);
|
||||||
|
return result.jobs_by_pk;
|
||||||
|
}
|
||||||
78
server/cdk/cdk-get-makes.js
Normal file
78
server/cdk/cdk-get-makes.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
const path = require("path");
|
||||||
|
require("dotenv").config({
|
||||||
|
path: path.resolve(
|
||||||
|
process.cwd(),
|
||||||
|
`.env.${process.env.NODE_ENV || "development"}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||||
|
const soap = require("soap");
|
||||||
|
const queries = require("../graphql-client/queries");
|
||||||
|
const CdkBase = require("../web-sockets/web-socket");
|
||||||
|
const CdkWsdl = require("./cdk-wsdl").default;
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
const Dinero = require("dinero.js");
|
||||||
|
const _ = require("lodash");
|
||||||
|
const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
|
||||||
|
const { performance } = require("perf_hooks");
|
||||||
|
|
||||||
|
exports.default = async function (socket, cdk_dealerid) {
|
||||||
|
try {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`Getting makes and models list from CDK.`
|
||||||
|
);
|
||||||
|
return await GetCdkMakes(socket, cdk_dealerid);
|
||||||
|
} catch (error) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error encountered in CdkGetMakes. ${error}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function GetCdkMakes(socket, cdk_dealerid) {
|
||||||
|
CdkBase.createLogEvent(socket, "TRACE", `{1} Begin GetCDkMakes WSDL Call`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const soapClientVehicleInsert = await soap.createClientAsync(
|
||||||
|
CdkWsdl.VehicleInsert
|
||||||
|
);
|
||||||
|
const start = performance.now();
|
||||||
|
|
||||||
|
const soapResponseVehicleSearch =
|
||||||
|
await soapClientVehicleInsert.getMakeModelAsync(
|
||||||
|
{
|
||||||
|
arg0: CDK_CREDENTIALS,
|
||||||
|
arg1: { id: cdk_dealerid },
|
||||||
|
},
|
||||||
|
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
CheckCdkResponseForError(socket, soapResponseVehicleSearch);
|
||||||
|
const [
|
||||||
|
result, //rawResponse, soapheader, rawRequest
|
||||||
|
] = soapResponseVehicleSearch;
|
||||||
|
const end = performance.now();
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`soapClientVehicleInsert.getMakeModelAsync Result Length ${
|
||||||
|
result.return.length
|
||||||
|
} and took ${end - start}ms`
|
||||||
|
);
|
||||||
|
|
||||||
|
return result.return.map((element, index) => {
|
||||||
|
return { id: index, ...element };
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error in GetCdkMakes - ${JSON.stringify(error, null, 2)}`
|
||||||
|
);
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,40 +10,167 @@ const soap = require("soap");
|
|||||||
const queries = require("../graphql-client/queries");
|
const queries = require("../graphql-client/queries");
|
||||||
const CdkBase = require("../web-sockets/web-socket");
|
const CdkBase = require("../web-sockets/web-socket");
|
||||||
const CdkWsdl = require("./cdk-wsdl").default;
|
const CdkWsdl = require("./cdk-wsdl").default;
|
||||||
|
const logger = require("../utils/logger");
|
||||||
const IMEX_CDK_USER = process.env.IMEX_CDK_USER,
|
const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
|
||||||
IMEX_CDK_PASSWORD = process.env.IMEX_CDK_PASSWORD;
|
|
||||||
|
|
||||||
exports.default = async function (socket, jobid) {
|
exports.default = async function (socket, jobid) {
|
||||||
socket.logEvents = [];
|
socket.logEvents = [];
|
||||||
|
socket.recordid = jobid;
|
||||||
try {
|
try {
|
||||||
CdkBase.createLogEvent(
|
CdkBase.createLogEvent(
|
||||||
socket,
|
socket,
|
||||||
"DEBUG",
|
"DEBUG",
|
||||||
`Received Job export request for id ${jobid}`
|
`Received Job export request for id ${jobid}`
|
||||||
);
|
);
|
||||||
|
//The following values will be stored on the socket to allow callbacks.
|
||||||
|
//let clVFV, clADPV, clADPC;
|
||||||
const JobData = await QueryJobData(socket, jobid);
|
const JobData = await QueryJobData(socket, jobid);
|
||||||
console.log(JSON.stringify(JobData, null, 2));
|
|
||||||
const DealerId = JobData.bodyshop.cdk_dealerid;
|
const DealerId = JobData.bodyshop.cdk_dealerid;
|
||||||
|
|
||||||
CdkBase.createLogEvent(
|
CdkBase.createLogEvent(
|
||||||
socket,
|
socket,
|
||||||
"TRACE",
|
"TRACE",
|
||||||
`Dealer ID detected: ${JSON.stringify(DealerId)}`
|
`Dealer ID detected: ${JSON.stringify(DealerId)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Begin Calculate VID from DMS {1}
|
//{1} Begin Calculate DMS Vehicle Id
|
||||||
await DetermineDMSVid(socket, JobData);
|
socket.clVFV = await CalculateDmsVid(socket, JobData);
|
||||||
|
if (socket.clVFV.newId === "Y") {
|
||||||
|
//{1.2} This is a new Vehicle ID
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{1.2} clVFV DMSVid does *not* exist.`
|
||||||
|
);
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`{1.2} clVFV: ${JSON.stringify(socket.clVFV, null, 2)}`
|
||||||
|
);
|
||||||
|
//Check if DMSCustId is Empty - which it should always be?
|
||||||
|
//{6.6} Should check to see if a customer exists so that we can marry it to the new vehicle.
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{6.6} Trying to find customer ID in DMS.`
|
||||||
|
);
|
||||||
|
|
||||||
|
//Array
|
||||||
|
const strIDS = await FindCustomerIdFromDms(socket, JobData);
|
||||||
|
if (strIDS && strIDS.length > 0) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{8.2} ${strIDS.length} Customer ID(s) found.`
|
||||||
|
);
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`{8.2} strIDS: ${JSON.stringify(strIDS, null, 2)}`
|
||||||
|
);
|
||||||
|
if (strIDS.length > 1) {
|
||||||
|
//We have multiple IDs
|
||||||
|
//TODO: Do we need to let the person select it?
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"WARNING",
|
||||||
|
`{F} Mutliple customer ids have been found (${strIDS.length})`
|
||||||
|
);
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`Asking for user intervention to select customer.`
|
||||||
|
);
|
||||||
|
socket.emit("cdk-select-customer", strIDS);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{8.5} Customer ID(s) *not* found.`
|
||||||
|
);
|
||||||
|
|
||||||
|
//Create a customer number, then use that to insert the customer record.
|
||||||
|
const newCustomerNumber = await GenerateCustomerNumberFromDms(
|
||||||
|
socket,
|
||||||
|
JobData
|
||||||
|
);
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{10.1} New Customer number generated. newCustomerNumber: ${newCustomerNumber}`
|
||||||
|
);
|
||||||
|
|
||||||
|
//Use the new customer number to insert the customer record.
|
||||||
|
socket.clADPC = await CreateCustomerInDms(
|
||||||
|
socket,
|
||||||
|
JobData,
|
||||||
|
newCustomerNumber
|
||||||
|
);
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{11.1} New Customer inserted.`
|
||||||
|
);
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`{11.1} clADPC: ${JSON.stringify(socket.clADPC, null, 2)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CdkBase.createLogEvent(socket, "DEBUG", `{1.1} clVFV DMSVid does exist.`);
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`{1.1} clVFV: ${JSON.stringify(socket.clVFV, null, 2)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
//{2} Begin Find Vehicle in DMS
|
||||||
|
socket.clADPV = await FindVehicleInDms(socket, JobData, socket.clVFV); //TODO: Verify that this should always return a result. If an ID was found previously, it should be correct?
|
||||||
|
|
||||||
|
//{2.2} Check if the vehicle was found in the DMS.
|
||||||
|
if (socket.clADPV.AppErrorNo === "0") {
|
||||||
|
//Vehicle was found.
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{1.4} Vehicle was found in the DMS.`
|
||||||
|
);
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`{1.4} clADPV: ${JSON.stringify(socket.clADPV, null, 2)}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
//Vehicle was not found.
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{6.4} Vehicle does not exist in DMS. Will have to create one.`
|
||||||
|
);
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`{6.4} clVFV: ${JSON.stringify(socket.clVFV, null, 2)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CdkBase.createLogEvent(
|
CdkBase.createLogEvent(
|
||||||
socket,
|
socket,
|
||||||
"ERROR",
|
"ERROR",
|
||||||
`Error encountered in JobExport. ${error}`
|
`Error encountered in CdkJobExport. ${error}`
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
//Ensure we always insert logEvents
|
//Ensure we always insert logEvents
|
||||||
//GQL to insert logevents.
|
//GQL to insert logevents.
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`Capturing log events to database.`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -61,27 +188,267 @@ async function QueryJobData(socket, jobid) {
|
|||||||
return result.jobs_by_pk;
|
return result.jobs_by_pk;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function DetermineDMSVid(socket, JobData) {
|
async function CreateCustomerInDms(socket, JobData, newCustomerNumber) {
|
||||||
CdkBase.createLogEvent(socket, "TRACE", "{1} Begin Determine DMS VehicleID");
|
CdkBase.createLogEvent(socket, "DEBUG", `{11} Begin Create Customer in DMS`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//Create SOAP Request for <getVehIds/>
|
const soapClientCustomerInsertUpdate = await soap.createClientAsync(
|
||||||
const soapClient = await soap.createClientAsync(CdkWsdl.VehicleSearch);
|
CdkWsdl.CustomerInsertUpdate
|
||||||
const result = await soapClient.searchIDsByVINAsync(
|
|
||||||
{
|
|
||||||
arg0: { password: IMEX_CDK_PASSWORD, username: IMEX_CDK_USER },
|
|
||||||
arg1: { id: JobData.bodyshop.cdk_dealerid },
|
|
||||||
arg2: { VIN: JobData.v_vin },
|
|
||||||
},
|
|
||||||
|
|
||||||
{}
|
|
||||||
);
|
);
|
||||||
console.log(result);
|
const soapResponseCustomerInsertUpdate =
|
||||||
|
await soapClientCustomerInsertUpdate.insertAsync(
|
||||||
|
{
|
||||||
|
arg0: CDK_CREDENTIALS,
|
||||||
|
arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards.
|
||||||
|
arg2: { userId: null },
|
||||||
|
arg3: {
|
||||||
|
//Copied the required fields from the other integration.
|
||||||
|
//TODO: Verify whether we need to bring more information in.
|
||||||
|
id: { value: newCustomerNumber },
|
||||||
|
address: {
|
||||||
|
city: JobData.ownr_city,
|
||||||
|
country: null,
|
||||||
|
postalcode: JobData.ownr_zip,
|
||||||
|
stateOrProvince: JobData.ownr_st,
|
||||||
|
},
|
||||||
|
contactInfo: {
|
||||||
|
mainTelephoneNumber: { main: true, value: JobData.ownr_ph1 },
|
||||||
|
},
|
||||||
|
demographics: null,
|
||||||
|
name1: {
|
||||||
|
companyname: null,
|
||||||
|
firstName: JobData.ownr_fn,
|
||||||
|
fullname: null,
|
||||||
|
lastName: JobData.ownr_ln,
|
||||||
|
middleName: null,
|
||||||
|
nameType: "Person",
|
||||||
|
suffix: null,
|
||||||
|
title: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
|
||||||
|
const [
|
||||||
|
result, //rawResponse, soapheader, rawRequest
|
||||||
|
] = soapResponseCustomerInsertUpdate;
|
||||||
|
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`soapClientCustomerInsertUpdate.insertAsync Result ${JSON.stringify(
|
||||||
|
result,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
const customer = result && result.return;
|
||||||
|
return customer;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CdkBase.createLogEvent(
|
CdkBase.createLogEvent(
|
||||||
socket,
|
socket,
|
||||||
"ERROR",
|
"ERROR",
|
||||||
`Error in DetermineDMSVid - ${JSON.stringify(error, null, 2)}`
|
`Error in CreateCustomerInDms - ${JSON.stringify(error, null, 2)}`
|
||||||
);
|
);
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function GenerateCustomerNumberFromDms(socket, JobData) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{10} Begin Generate Customer Number from DMS`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const soapClientCustomerInsertUpdate = await soap.createClientAsync(
|
||||||
|
CdkWsdl.CustomerInsertUpdate
|
||||||
|
);
|
||||||
|
const soapResponseCustomerInsertUpdate =
|
||||||
|
await soapClientCustomerInsertUpdate.getCustomerNumberAsync(
|
||||||
|
{
|
||||||
|
arg0: CDK_CREDENTIALS,
|
||||||
|
arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards.
|
||||||
|
arg2: { userId: null },
|
||||||
|
},
|
||||||
|
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
CheckCdkResponseForError(socket, soapResponseCustomerInsertUpdate);
|
||||||
|
const [
|
||||||
|
result, //rawResponse, soapheader, rawRequest
|
||||||
|
] = soapResponseCustomerInsertUpdate;
|
||||||
|
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`soapClientCustomerInsertUpdate.getCustomerNumberAsync Result ${JSON.stringify(
|
||||||
|
result,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
const customerNumber =
|
||||||
|
result && result.return && result.return.customerNumber;
|
||||||
|
return customerNumber;
|
||||||
|
} catch (error) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error in GenerateCustomerNumberFromDms - ${JSON.stringify(
|
||||||
|
error,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function FindCustomerIdFromDms(socket, JobData) {
|
||||||
|
const ownerName = `${JobData.ownr_ln},${JobData.ownr_fn}`;
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{8} Begin Read Customer from DMS using OWNER NAME: ${ownerName}`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const soapClientCustomerSearch = await soap.createClientAsync(
|
||||||
|
CdkWsdl.CustomerSearch
|
||||||
|
);
|
||||||
|
const soapResponseCustomerSearch =
|
||||||
|
await soapClientCustomerSearch.executeSearchAsync(
|
||||||
|
{
|
||||||
|
arg0: CDK_CREDENTIALS,
|
||||||
|
arg1: { dealerId: JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards.
|
||||||
|
arg2: {
|
||||||
|
verb: "EXACT",
|
||||||
|
key: ownerName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
CheckCdkResponseForError(socket, soapResponseCustomerSearch);
|
||||||
|
const [
|
||||||
|
result, // rawResponse, soapheader, rawRequest
|
||||||
|
] = soapResponseCustomerSearch;
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`soapClientCustomerSearch.executeSearchBulkAsync Result ${JSON.stringify(
|
||||||
|
result,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
const CustomersFromDms = result && result.return;
|
||||||
|
return CustomersFromDms;
|
||||||
|
} catch (error) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error in FindCustomerIdFromDms - ${JSON.stringify(error, null, 2)}`
|
||||||
|
);
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function FindVehicleInDms(socket, JobData, clVFV) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`{2}/{6} Begin Find Vehicle In DMS using clVFV: ${clVFV}`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const soapClientVehicleInsertUpdate = await soap.createClientAsync(
|
||||||
|
CdkWsdl.VehicleInsertUpdate
|
||||||
|
);
|
||||||
|
const soapResponseVehicleInsertUpdate =
|
||||||
|
await soapClientVehicleInsertUpdate.readBulkAsync(
|
||||||
|
{
|
||||||
|
arg0: CDK_CREDENTIALS,
|
||||||
|
arg1: { id: JobData.bodyshop.cdk_dealerid },
|
||||||
|
arg2: {
|
||||||
|
fileType: "VEHICLES",
|
||||||
|
vehiclesVehicleId: clVFV.vehiclesVehId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
|
||||||
|
const [
|
||||||
|
result, //rawResponse, soapheader, rawRequest
|
||||||
|
] = soapResponseVehicleInsertUpdate;
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`soapClientVehicleInsertUpdate.readBulkAsync Result ${JSON.stringify(
|
||||||
|
result,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
const VehicleFromDMS = result && result.return && result.return[0];
|
||||||
|
return VehicleFromDMS;
|
||||||
|
} catch (error) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error in FindVehicleInDms - ${JSON.stringify(error, null, 2)}`
|
||||||
|
);
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function CalculateDmsVid(socket, JobData) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`{1} Begin Calculate DMS Vehicle ID using VIN: ${JobData.v_vin}`
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const soapClientVehicleInsertUpdate = await soap.createClientAsync(
|
||||||
|
CdkWsdl.VehicleInsertUpdate
|
||||||
|
);
|
||||||
|
const soapResponseVehicleInsertUpdate =
|
||||||
|
await soapClientVehicleInsertUpdate.getVehIdsAsync(
|
||||||
|
{
|
||||||
|
arg0: CDK_CREDENTIALS,
|
||||||
|
arg1: { id: JobData.bodyshop.cdk_dealerid },
|
||||||
|
arg2: { VIN: JobData.v_vin },
|
||||||
|
},
|
||||||
|
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
CheckCdkResponseForError(socket, soapResponseVehicleInsertUpdate);
|
||||||
|
const [
|
||||||
|
result, //rawResponse, soapheader, rawRequest
|
||||||
|
] = soapResponseVehicleInsertUpdate;
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`soapClientVehicleInsertUpdate.searchIDsByVINAsync Result ${JSON.stringify(
|
||||||
|
result,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
const DmsVehicle = result && result.return && result.return[0];
|
||||||
|
return DmsVehicle;
|
||||||
|
} catch (error) {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error in CalculateDmsVid - ${JSON.stringify(error, null, 2)}`
|
||||||
|
);
|
||||||
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,99 @@
|
|||||||
exports.default = {
|
const path = require("path");
|
||||||
VehicleSearch:
|
require("dotenv").config({
|
||||||
"https://uat-3pa.dmotorworks.com/pip-vehicle/services/VehicleSearch?wsdl",
|
path: path.resolve(
|
||||||
|
process.cwd(),
|
||||||
|
`.env.${process.env.NODE_ENV || "development"}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
const CdkBase = require("../web-sockets/web-socket");
|
||||||
|
|
||||||
|
const IMEX_CDK_USER = process.env.IMEX_CDK_USER,
|
||||||
|
IMEX_CDK_PASSWORD = process.env.IMEX_CDK_PASSWORD;
|
||||||
|
const CDK_CREDENTIALS = {
|
||||||
|
password: IMEX_CDK_PASSWORD,
|
||||||
|
username: IMEX_CDK_USER,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.CDK_CREDENTIALS = CDK_CREDENTIALS;
|
||||||
|
// const cdkDomain =
|
||||||
|
// process.env.NODE_ENV === "production"
|
||||||
|
// ? "https://3pa.dmotorworks.com"
|
||||||
|
// : "https://uat-3pa.dmotorworks.com";
|
||||||
|
|
||||||
|
function CheckCdkResponseForError(socket, soapResponse) {
|
||||||
|
if (!soapResponse[0]) {
|
||||||
|
//The response was null, this might be ok, it might not.
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"WARNING",
|
||||||
|
`Warning detected in CDK Response - it appears to be null. Stack: ${
|
||||||
|
new Error().stack
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResultToCheck = soapResponse[0].return;
|
||||||
|
|
||||||
|
if (Array.isArray(ResultToCheck)) {
|
||||||
|
ResultToCheck.forEach((result) => checkIndividualResult(socket, result));
|
||||||
|
} else {
|
||||||
|
checkIndividualResult(socket, ResultToCheck);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.CheckCdkResponseForError = CheckCdkResponseForError;
|
||||||
|
|
||||||
|
function checkIndividualResult(socket, ResultToCheck) {
|
||||||
|
if (
|
||||||
|
ResultToCheck.errorLevel === 0 ||
|
||||||
|
ResultToCheck.errorLevel === "0" ||
|
||||||
|
ResultToCheck.code === "success" ||
|
||||||
|
(!ResultToCheck.code && !ResultToCheck.errorLevel)
|
||||||
|
)
|
||||||
|
//TODO: Verify that this is the best way to detect errors.
|
||||||
|
return;
|
||||||
|
else {
|
||||||
|
CdkBase.createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error detected in CDK Response - ${JSON.stringify(
|
||||||
|
ResultToCheck,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Error found while validating CDK response for ${JSON.stringify(
|
||||||
|
ResultToCheck,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}:`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.checkIndividualResult = checkIndividualResult;
|
||||||
|
|
||||||
|
const cdkDomain = "https://uat-3pa.dmotorworks.com";
|
||||||
|
exports.default = {
|
||||||
|
// VehicleSearch: `${cdkDomain}/pip-vehicle/services/VehicleSearch?wsdl`,
|
||||||
|
VehicleInsertUpdate: `${cdkDomain}/pip-vehicle/services/VehicleInsertUpdate?wsdl`,
|
||||||
|
CustomerInsertUpdate: `${cdkDomain}/pip-customer/services/CustomerInsertUpdate?wsdl`,
|
||||||
|
CustomerSearch: `${cdkDomain}/pip-customer/services/CustomerSearch?wsdl`,
|
||||||
|
VehicleSearch: `${cdkDomain}/pip-vehicle/services/VehicleSearch?wsdl`,
|
||||||
|
VehicleInsert: `${cdkDomain}/pip-vehicle/services/VehicleInsertUpdate?wsdl`,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following login credentials will be used for all PIPs and all environments (User Acceptance Testing and Production).
|
||||||
|
// Only the URLs will change from https://uat-3pa.dmoto... to https://3pa.dmoto... or https://api-dit.connect... to https://api.connect...
|
||||||
|
// Accounting GL/Accounting GL WIP Update - https://uat-3pa.dmotorworks.com/pip-accounting-gl/services/AccountingGLInsertUpdate?wsdl
|
||||||
|
// Customer Insert Update - https://uat-3pa.dmotorworks.com/pip-customer/services/CustomerInsertUpdate?wsdl
|
||||||
|
// Help Database Location - https://uat-3pa.dmotorworks.com/pip-help-database-location/services/HelpDatabaseLocation?wsdl
|
||||||
|
// Parts Inventory Insert Update - https://uat-3pa.dmotorworks.com/pip-parts-inventory/services/PartsInventoryInsertUpdate?wsdl
|
||||||
|
// Purchase Order Insert - https://uat-3pa.dmotorworks.com/pip-purchase-order/services/PurchaseOrderInsert?wsdl
|
||||||
|
// Repair Order MLS Insert Update - https://uat-3pa.dmotorworks.com/pip-repair-order-mls/services/RepairOrderMLSInsertUpdate?wsdl
|
||||||
|
// Repair Order Parts Insert Update - https://uat-3pa.dmotorworks.com/pip-repair-order-parts/services/RepairOrderPartsInsertUpdate?wsdl
|
||||||
|
// Service History Insert - https://uat-3pa.dmotorworks.com/pip-service-history-insert/services/ServiceHistoryInsert?wsdl
|
||||||
|
// Service Repair Order Update - https://uat-3pa.dmotorworks.com/pip-service-repair-order/services/ServiceRepairOrderUpdate?wsdl
|
||||||
|
// Service Vehicle Insert Update - https://uat-3pa.dmotorworks.com/pip-vehicle/services/VehicleInsertUpdate?wsdl
|
||||||
|
|||||||
@@ -928,3 +928,105 @@ exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports.GET_CDK_ALLOCATIONS = `
|
||||||
|
query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
|
||||||
|
jobs_by_pk(id: $id) {
|
||||||
|
bodyshop{
|
||||||
|
id
|
||||||
|
md_responsibility_centers
|
||||||
|
}
|
||||||
|
ro_number
|
||||||
|
invoice_allocation
|
||||||
|
ins_co_id
|
||||||
|
id
|
||||||
|
ded_amt
|
||||||
|
ded_status
|
||||||
|
depreciation_taxes
|
||||||
|
other_amount_payable
|
||||||
|
towing_payable
|
||||||
|
storage_payable
|
||||||
|
adjustment_bottom_line
|
||||||
|
federal_tax_rate
|
||||||
|
state_tax_rate
|
||||||
|
local_tax_rate
|
||||||
|
tax_tow_rt
|
||||||
|
tax_str_rt
|
||||||
|
tax_paint_mat_rt
|
||||||
|
tax_sub_rt
|
||||||
|
tax_lbr_rt
|
||||||
|
tax_levies_rt
|
||||||
|
parts_tax_rates
|
||||||
|
job_totals
|
||||||
|
rate_la1
|
||||||
|
rate_la2
|
||||||
|
rate_la3
|
||||||
|
rate_la4
|
||||||
|
rate_laa
|
||||||
|
rate_lab
|
||||||
|
rate_lad
|
||||||
|
rate_lae
|
||||||
|
rate_laf
|
||||||
|
rate_lag
|
||||||
|
rate_lam
|
||||||
|
rate_lar
|
||||||
|
rate_las
|
||||||
|
rate_lau
|
||||||
|
rate_ma2s
|
||||||
|
rate_ma2t
|
||||||
|
rate_ma3s
|
||||||
|
rate_mabl
|
||||||
|
rate_macs
|
||||||
|
rate_mahw
|
||||||
|
rate_mapa
|
||||||
|
rate_mash
|
||||||
|
rate_matd
|
||||||
|
status
|
||||||
|
date_exported
|
||||||
|
date_invoiced
|
||||||
|
voided
|
||||||
|
scheduled_completion
|
||||||
|
actual_completion
|
||||||
|
scheduled_delivery
|
||||||
|
actual_delivery
|
||||||
|
scheduled_in
|
||||||
|
actual_in
|
||||||
|
bills {
|
||||||
|
id
|
||||||
|
federal_tax_rate
|
||||||
|
local_tax_rate
|
||||||
|
state_tax_rate
|
||||||
|
is_credit_memo
|
||||||
|
billlines {
|
||||||
|
actual_cost
|
||||||
|
cost_center
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
applicable_taxes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
joblines(where: { removed: { _eq: false } }) {
|
||||||
|
id
|
||||||
|
removed
|
||||||
|
tax_part
|
||||||
|
line_desc
|
||||||
|
prt_dsmk_p
|
||||||
|
prt_dsmk_m
|
||||||
|
part_type
|
||||||
|
oem_partno
|
||||||
|
db_price
|
||||||
|
act_price
|
||||||
|
part_qty
|
||||||
|
mod_lbr_ty
|
||||||
|
db_hrs
|
||||||
|
mod_lb_hrs
|
||||||
|
lbr_op
|
||||||
|
lbr_amt
|
||||||
|
op_code_desc
|
||||||
|
profitcenter_labor
|
||||||
|
profitcenter_part
|
||||||
|
prt_dsmk_p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
25
server/utils/logger.js
Normal file
25
server/utils/logger.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const graylog2 = require("graylog2");
|
||||||
|
|
||||||
|
const logger = new graylog2.graylog({
|
||||||
|
servers: [{ host: "logs.bodyshop.app", port: 12201 }],
|
||||||
|
});
|
||||||
|
|
||||||
|
function log(message, type, user, record, object) {
|
||||||
|
console.log(message, {
|
||||||
|
type,
|
||||||
|
env: process.env.NODE_ENV,
|
||||||
|
user,
|
||||||
|
record,
|
||||||
|
...object,
|
||||||
|
});
|
||||||
|
logger.log(message, {
|
||||||
|
type,
|
||||||
|
env: process.env.NODE_ENV || "development",
|
||||||
|
user,
|
||||||
|
record,
|
||||||
|
...object,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { log };
|
||||||
|
//const logger = require("./server/utils/logger");
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const _ = require("lodash");
|
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
path: path.resolve(
|
path: path.resolve(
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
@@ -10,7 +9,11 @@ require("dotenv").config({
|
|||||||
const { io } = require("../../server");
|
const { io } = require("../../server");
|
||||||
const { admin } = require("../firebase/firebase-handler");
|
const { admin } = require("../firebase/firebase-handler");
|
||||||
const CdkJobExport = require("../cdk/cdk-job-export").default;
|
const CdkJobExport = require("../cdk/cdk-job-export").default;
|
||||||
|
const CdkGetMakes = require("../cdk/cdk-get-makes").default;
|
||||||
|
const CdkCalculateAllocations =
|
||||||
|
require("../cdk/cdk-calculate-allocations").default;
|
||||||
const { isArray } = require("lodash");
|
const { isArray } = require("lodash");
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
|
||||||
io.use(function (socket, next) {
|
io.use(function (socket, next) {
|
||||||
try {
|
try {
|
||||||
@@ -30,6 +33,10 @@ io.use(function (socket, next) {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("Uncaught connection error:::", error);
|
console.log("Uncaught connection error:::", error);
|
||||||
|
logger.log("websocket-connection-error", "error", null, null, {
|
||||||
|
token: socket.handshake.auth.token,
|
||||||
|
...error,
|
||||||
|
});
|
||||||
next(new Error(`Authentication error ${error}`));
|
next(new Error(`Authentication error ${error}`));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -46,6 +53,40 @@ io.on("connection", (socket) => {
|
|||||||
socket.on("cdk-export-job", (jobid) => {
|
socket.on("cdk-export-job", (jobid) => {
|
||||||
CdkJobExport(socket, jobid);
|
CdkJobExport(socket, jobid);
|
||||||
});
|
});
|
||||||
|
socket.on("cdk-selected-customer", (selectedCustomerId) => {
|
||||||
|
createLogEvent(
|
||||||
|
socket,
|
||||||
|
"DEBUG",
|
||||||
|
`User selected customer ID ${selectedCustomerId}`
|
||||||
|
);
|
||||||
|
socket.selectedCustomerId = selectedCustomerId;
|
||||||
|
//CdkJobExport(socket, jobid);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("cdk-get-makes", async (cdk_dealerid, callback) => {
|
||||||
|
try {
|
||||||
|
const makes = await CdkGetMakes(socket, cdk_dealerid);
|
||||||
|
callback(makes);
|
||||||
|
} catch (error) {
|
||||||
|
createLogEvent(
|
||||||
|
socket,
|
||||||
|
"ERROR",
|
||||||
|
`Error in cdk-get-makes WS call. ${JSON.stringify(error, null, 2)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("cdk-calculate-allocations", async (jobid, callback) => {
|
||||||
|
const allocations = await CdkCalculateAllocations(socket, jobid);
|
||||||
|
createLogEvent(socket, "DEBUG", `Allocations calculated.`);
|
||||||
|
createLogEvent(
|
||||||
|
socket,
|
||||||
|
"TRACE",
|
||||||
|
`Allocations calculated. ${JSON.stringify(allocations, null, 2)}`
|
||||||
|
);
|
||||||
|
|
||||||
|
callback(allocations);
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
createLogEvent(socket, "DEBUG", `User disconnected.`);
|
createLogEvent(socket, "DEBUG", `User disconnected.`);
|
||||||
@@ -55,7 +96,7 @@ io.on("connection", (socket) => {
|
|||||||
function createLogEvent(socket, level, message) {
|
function createLogEvent(socket, level, message) {
|
||||||
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
|
if (LogLevelHierarchy(socket.log_level) >= LogLevelHierarchy(level)) {
|
||||||
console.log(
|
console.log(
|
||||||
`[CDK LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${
|
`[WS LOG EVENT] ${level} - ${new Date()} - ${socket.user.email} - ${
|
||||||
socket.id
|
socket.id
|
||||||
} - ${message}`
|
} - ${message}`
|
||||||
);
|
);
|
||||||
@@ -65,6 +106,10 @@ function createLogEvent(socket, level, message) {
|
|||||||
message,
|
message,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
logger.log("ws-log-event", level, socket.user.email, socket.recordid, {
|
||||||
|
wsmessage: message,
|
||||||
|
});
|
||||||
|
|
||||||
if (socket.logEvents && isArray(socket.logEvents)) {
|
if (socket.logEvents && isArray(socket.logEvents)) {
|
||||||
socket.logEvents.push({
|
socket.logEvents.push({
|
||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
@@ -72,6 +117,9 @@ function createLogEvent(socket, level, message) {
|
|||||||
message,
|
message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// if (level === "ERROR") {
|
||||||
|
// throw new Error(message);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1901,6 +1901,11 @@ graphql@^15.5.1:
|
|||||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad"
|
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad"
|
||||||
integrity sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw==
|
integrity sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw==
|
||||||
|
|
||||||
|
graylog2@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/graylog2/-/graylog2-0.2.1.tgz#fcb0775766fb6d7d6f5fef3ff873326b8b1d35a9"
|
||||||
|
integrity sha512-vjysakwOhrAqMeIvSK0WZcmzKvkpxY6pCfT9QqtdSVAidPFIynuin7adqbdFp9MCCTbTE402WIxvg8cph5OWTA==
|
||||||
|
|
||||||
gtoken@^5.0.4:
|
gtoken@^5.0.4:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.2.1.tgz#4dae1fea17270f457954b4a45234bba5fc796d16"
|
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.2.1.tgz#4dae1fea17270f457954b4a45234bba5fc796d16"
|
||||||
|
|||||||
Reference in New Issue
Block a user