Merged in feature/cdk-cert (pull request #216)

feature/cdk-cert

Approved-by: Patrick Fic
This commit is contained in:
Patrick Fic
2021-09-14 21:09:10 +00:00
25 changed files with 19047 additions and 605 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
<babeledit_project be_version="2.7.1" version="1.2"> <babeledit_project version="1.2" be_version="2.7.1">
<!-- <!--
BabelEdit project file BabelEdit project file
@@ -3683,6 +3683,69 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>itc_federal</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>itc_local</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>itc_state</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>mappingname</name> <name>mappingname</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -8,7 +8,25 @@ import { alphaSort } from "../../utils/sorters";
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component"; import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component"; import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
export default function AccountingReceivablesTableComponent({ loading, jobs }) { import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingReceivablesTableComponent);
export function AccountingReceivablesTableComponent({
bodyshop,
loading,
jobs,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
const [selectedJobs, setSelectedJobs] = useState([]); const [selectedJobs, setSelectedJobs] = useState([]);
const [transInProgress, setTransInProgress] = useState(false); const [transInProgress, setTransInProgress] = useState(false);
@@ -180,12 +198,14 @@ export default function AccountingReceivablesTableComponent({ loading, jobs }) {
<Card <Card
extra={ extra={
<Space wrap> <Space wrap>
<JobsExportAllButton {!bodyshop.cdk_dealerid && (
jobIds={selectedJobs} <JobsExportAllButton
disabled={transInProgress || selectedJobs.length === 0} jobIds={selectedJobs}
loadingCallback={setTransInProgress} disabled={transInProgress || selectedJobs.length === 0}
completedCallback={setSelectedJobs} loadingCallback={setTransInProgress}
/> completedCallback={setSelectedJobs}
/>
)}
<Input.Search <Input.Search
value={state.search} value={state.search}
onChange={handleSearch} onChange={handleSearch}

View File

@@ -1,4 +1,4 @@
import { Button, Table, Typography } from "antd"; import { Button, Card, Table, Typography } from "antd";
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -21,15 +21,16 @@ export default connect(
mapDispatchToProps mapDispatchToProps
)(DmsAllocationsSummary); )(DmsAllocationsSummary);
export function DmsAllocationsSummary({ socket, bodyshop, jobId }) { export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [allocationsSummary, setAllocationsSummary] = useState([]); const [allocationsSummary, setAllocationsSummary] = useState([]);
useEffect(() => { useEffect(() => {
if (socket.connected) { if (socket.connected) {
socket.emit("cdk-calculate-allocations", jobId, (ack) => socket.emit("cdk-calculate-allocations", jobId, (ack) => {
setAllocationsSummary(ack) setAllocationsSummary(ack);
); socket.allocationsSummary = ack;
});
} }
}, [socket, socket.connected, jobId]); }, [socket, socket.connected, jobId]);
@@ -75,8 +76,9 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId }) {
]; ];
return ( return (
<Table <Card
title={() => ( title={title}
extra={
<Button <Button
onClick={() => { onClick={() => {
socket.emit("cdk-calculate-allocations", jobId, (ack) => socket.emit("cdk-calculate-allocations", jobId, (ack) =>
@@ -86,43 +88,48 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId }) {
> >
<SyncOutlined /> <SyncOutlined />
</Button> </Button>
)} }
pagination={{ position: "top", defaultPageSize: 50 }} >
columns={columns} <Table
rowKey="center" pagination={{ position: "top", defaultPageSize: 50 }}
dataSource={allocationsSummary} columns={columns}
summary={() => { rowKey="center"
const totals = allocationsSummary.reduce( dataSource={allocationsSummary}
(acc, val) => { summary={() => {
return { const totals = allocationsSummary.reduce(
totalSale: acc.totalSale.add(Dinero(val.sale)), (acc, val) => {
totalCost: acc.totalCost.add(Dinero(val.cost)), return {
}; totalSale: acc.totalSale.add(Dinero(val.sale)),
}, totalCost: acc.totalCost.add(Dinero(val.cost)),
{ };
totalSale: Dinero(), },
totalCost: Dinero(), {
} totalSale: Dinero(),
); totalCost: Dinero(),
}
);
return ( return (
<Table.Summary.Row> <Table.Summary.Row>
<Table.Summary.Cell> <Table.Summary.Cell>
<Typography.Title level={4}> <Typography.Title level={4}>
{t("general.labels.totals")} {t("general.labels.totals")}
</Typography.Title> </Typography.Title>
</Table.Summary.Cell> </Table.Summary.Cell>
<Table.Summary.Cell> <Table.Summary.Cell>
{totals.totalSale.toFormat()} {totals.totalSale.toFormat()}
</Table.Summary.Cell> </Table.Summary.Cell>
<Table.Summary.Cell> <Table.Summary.Cell>
{totals.totalCost.toFormat()} {
</Table.Summary.Cell> // totals.totalCost.toFormat()
<Table.Summary.Cell></Table.Summary.Cell> }
<Table.Summary.Cell></Table.Summary.Cell> </Table.Summary.Cell>
</Table.Summary.Row> <Table.Summary.Cell></Table.Summary.Cell>
); <Table.Summary.Cell></Table.Summary.Cell>
}} </Table.Summary.Row>
/> );
}}
/>
</Card>
); );
} }

View File

@@ -1,11 +1,11 @@
import { useLazyQuery } from "@apollo/client";
import { Button, Input, Modal, Table } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { Modal, Button, Table, Input } from "antd"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useTranslation } from "react-i18next";
import { useLazyQuery } from "@apollo/client";
import { SEARCH_DMS_VEHICLES } from "../../graphql/dms.queries"; import { SEARCH_DMS_VEHICLES } from "../../graphql/dms.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -26,31 +26,27 @@ export function DmsCdkVehicles({ bodyshop, form, socket, job }) {
useLazyQuery(SEARCH_DMS_VEHICLES); useLazyQuery(SEARCH_DMS_VEHICLES);
const columns = [ const columns = [
{ {
title: t("jobs.fields.dms.make"), title: t("vehicles.fields.v_make_desc"),
dataIndex: "make", dataIndex: "make",
key: "make", key: "make",
}, },
{ {
title: t("jobs.fields.dms.model"), title: t("vehicles.fields.v_model_desc"),
dataIndex: "model", dataIndex: "model",
key: "model", key: "model",
}, },
{ {
title: t("jobs.fields.dms.makecode"), title: t("jobs.fields.dms.dms_make"),
dataIndex: "makecode", dataIndex: "makecode",
key: "makecode", key: "makecode",
}, },
{ {
title: t("jobs.fields.dms.modelcode"), title: t("jobs.fields.dms.dms_model"),
dataIndex: "modelcode", dataIndex: "modelcode",
key: "modelcode", key: "modelcode",
}, },
]; ];
console.log(
"🚀 ~ file: dms-cdk-makes.component.jsx ~ line 95 ~ selectedModel",
selectedModel
);
return ( return (
<div> <div>
<Modal <Modal
@@ -102,7 +98,7 @@ export function DmsCdkVehicles({ bodyshop, form, socket, job }) {
}); });
}} }}
> >
{t("jobs.actions.dms.getmakes")} {t("jobs.actions.dms.findmakemodelcode")}
</Button> </Button>
</div> </div>
); );

View File

@@ -4,6 +4,7 @@ import React, { useState } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { useTranslation } from "react-i18next";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -15,6 +16,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkMakesRefetch);
export function DmsCdkMakesRefetch({ bodyshop, form, socket }) { export function DmsCdkMakesRefetch({ bodyshop, form, socket }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const handleRefetch = async () => { const handleRefetch = async () => {
setLoading(true); setLoading(true);
const response = await axios.post("/cdk/getvehicles", { const response = await axios.post("/cdk/getvehicles", {
@@ -26,7 +28,7 @@ export function DmsCdkMakesRefetch({ bodyshop, form, socket }) {
}; };
return ( return (
<Button loading={loading} onClick={handleRefetch}> <Button loading={loading} onClick={handleRefetch}>
Refetch Models {t("jobs.actions.dms.refetchmakesmodels")}
</Button> </Button>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Button, Table } from "antd"; import { Button, Table, Col , Checkbox} from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -32,6 +32,7 @@ export function DmsCustomerSelector({ bodyshop }) {
const onUseSelected = () => { const onUseSelected = () => {
setVisible(false); setVisible(false);
socket.emit("cdk-selected-customer", selectedCustomer); socket.emit("cdk-selected-customer", selectedCustomer);
setSelectedCustomer(null);
}; };
const onUseGeneric = () => { const onUseGeneric = () => {
@@ -40,23 +41,36 @@ export function DmsCustomerSelector({ bodyshop }) {
"cdk-selected-customer", "cdk-selected-customer",
bodyshop.cdk_configuration.generic_customer_number bodyshop.cdk_configuration.generic_customer_number
); );
setSelectedCustomer(null);
}; };
const onCreateNew = () => { const onCreateNew = () => {
setVisible(false); setVisible(false);
socket.emit("cdk-selected-customer", null); socket.emit("cdk-selected-customer", null);
setSelectedCustomer(null);
}; };
const columns = [ const columns = [
{ {
title: t("dms.fields.name1"), title: t("jobs.fields.dms.id"),
dataIndex: ["id", "value"],
key: "id",
},
{
title: t("jobs.fields.dms.vinowner"),
dataIndex: "vinOwner",
key: "vinOwner",
render: (text, record) => <Checkbox disabled checked={record.vinOwner}/>
},
{
title: t("jobs.fields.dms.name1"),
dataIndex: ["name1", "fullName"], dataIndex: ["name1", "fullName"],
key: "name1", key: "name1",
sorter: (a, b) => alphaSort(a.name1?.fullName, b.name1?.fullName), sorter: (a, b) => alphaSort(a.name1?.fullName, b.name1?.fullName),
}, },
{ {
title: t("dms.fields.address"), title: t("jobs.fields.dms.address"),
//dataIndex: ["name2", "fullName"], //dataIndex: ["name2", "fullName"],
key: "address", key: "address",
render: (record, value) => render: (record, value) =>
@@ -66,40 +80,42 @@ export function DmsCustomerSelector({ bodyshop }) {
if (!visible) return <></>; if (!visible) return <></>;
return ( return (
<Table <Col span={24}>
title={() => ( <Table
<div> title={() => (
<Button onClick={onUseSelected} disabled={!selectedCustomer}> <div>
{t("jobs.actions.dms.useselected")} <Button onClick={onUseSelected} disabled={!selectedCustomer}>
</Button> {t("jobs.actions.dms.useselected")}
<Button </Button>
onClick={onUseGeneric} <Button
disabled={ onClick={onUseGeneric}
!( disabled={
bodyshop.cdk_configuration && !(
bodyshop.cdk_configuration.generic_customer_number bodyshop.cdk_configuration &&
) bodyshop.cdk_configuration.generic_customer_number
} )
> }
{t("jobs.actions.dms.usegeneric")} >
</Button> {t("jobs.actions.dms.usegeneric")}
<Button onClick={onCreateNew}> </Button>
{t("jobs.actions.dms.createnewcustomer")} <Button onClick={onCreateNew}>
</Button> {t("jobs.actions.dms.createnewcustomer")}
</div> </Button>
)} </div>
pagination={{ position: "top" }} )}
columns={columns} pagination={{ position: "top" }}
rowKey={(record) => record.id.value} columns={columns}
dataSource={customerList} rowKey={(record) => record.id.value}
//onChange={handleTableChange} dataSource={customerList}
rowSelection={{ //onChange={handleTableChange}
onSelect: (props) => { rowSelection={{
setSelectedCustomer(props.id.value); onSelect: (props) => {
}, setSelectedCustomer(props.id.value);
type: "radio", },
selectedRowKeys: [selectedCustomer], type: "radio",
}} selectedRowKeys: [selectedCustomer],
/> }}
/>
</Col>
); );
} }

View File

@@ -1,24 +1,26 @@
import { DeleteFilled } from "@ant-design/icons"; import { DeleteFilled } from "@ant-design/icons";
import { import {
Button, Button,
Card,
Divider,
Form, Form,
Input, Input,
InputNumber, InputNumber,
Select, Select,
Space, Space,
Statistic, Statistic,
Typography,
} from "antd"; } from "antd";
import Dinero from "dinero.js";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { determineDmsType } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component"; import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component"; import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import Dinero from "dinero.js";
import { determineDmsType } from "../../pages/dms/dms.container";
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
@@ -58,24 +60,90 @@ export function DmsPostForm({ bodyshop, socket, job }) {
}; };
return ( return (
<Form form={form} layout="vertical" onFinish={handleFinish}> <Card title={t("jobs.labels.dms.postingform")}>
<LayoutFormRow> <Form
<Form.Item form={form}
name="journal" layout="vertical"
label={t("jobs.fields.dms.journal")} onFinish={handleFinish}
initialValue={ initialValues={{
bodyshop.cdk_configuration && story: t("jobs.labels.dms.defaultstory", {
bodyshop.cdk_configuration.default_journal ro_number: job.ro_number,
} area_of_damage: job.area_of_damage && job.area_of_damage.impact1,
rules={[ }).substr(0, 239),
{ }}
required: true, >
//message: t("general.validation.required"), <LayoutFormRow grow>
}, <Form.Item
]} name="journal"
> label={t("jobs.fields.dms.journal")}
<Input /> initialValue={
</Form.Item> bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.default_journal
}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="kmin"
label={t("jobs.fields.kmin")}
initialValue={job && job.kmin}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber disabled />
</Form.Item>
<Form.Item
name="kmout"
label={t("jobs.fields.kmout")}
initialValue={job && job.kmout}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber disabled />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow style={{ justifyContent: "center" }} grow>
<Form.Item
name="dms_make"
label={t("jobs.fields.dms.dms_make")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
name="dms_model"
label={t("jobs.fields.dms.dms_model")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<DmsCdkMakes form={form} socket={socket} job={job} />
{/* <DmsCdkMakesRefetch /> */}
</LayoutFormRow>
<Form.Item <Form.Item
name="story" name="story"
label={t("jobs.fields.dms.story")} label={t("jobs.fields.dms.story")}
@@ -85,224 +153,186 @@ export function DmsPostForm({ bodyshop, socket, job }) {
}, },
]} ]}
> >
<Input.TextArea /> <Input.TextArea maxLength={240} />
</Form.Item> </Form.Item>
<Form.Item <Divider />
name="kmin" <Form.List name={["payers"]}>
label={t("jobs.fields.kmin")} {(fields, { add, remove }) => {
initialValue={job && job.kmin} return (
rules={[ <div>
{ {fields.map((field, index) => (
required: true, <Form.Item key={field.key}>
//message: t("general.validation.required"), <Space wrap>
}, <Form.Item
]} label={t("jobs.fields.dms.payer.name")}
> key={`${index}name`}
<InputNumber disabled /> name={[field.name, "name"]}
</Form.Item> rules={[
<Form.Item {
name="kmout" required: true,
label={t("jobs.fields.kmout")} },
initialValue={job && job.kmout} ]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber disabled />
</Form.Item>
<Form.Item
name="dms_make"
label={t("jobs.fields.dms.dms_make")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
name="dms_model"
label={t("jobs.fields.dms.dms_model")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<DmsCdkMakes form={form} socket={socket} job={job} />
<DmsCdkMakesRefetch />
</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,
},
]}
>
<Select
onSelect={(value) => handlePayerSelect(value, index)}
> >
{bodyshop.cdk_configuration && <Select
bodyshop.cdk_configuration.payers && style={{ minWidth: "15rem" }}
bodyshop.cdk_configuration.payers.map((payer) => ( onSelect={(value) => handlePayerSelect(value, index)}
<Select.Option key={payer.name}> >
{payer.name} {bodyshop.cdk_configuration &&
</Select.Option> bodyshop.cdk_configuration.payers &&
))} bodyshop.cdk_configuration.payers.map((payer) => (
</Select> <Select.Option key={payer.name}>
</Form.Item> {payer.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.dms.payer.dms_acctnumber")} label={t("jobs.fields.dms.payer.dms_acctnumber")}
key={`${index}dms_acctnumber`} key={`${index}dms_acctnumber`}
name={[field.name, "dms_acctnumber"]} name={[field.name, "dms_acctnumber"]}
rules={[ rules={[
{ {
required: true, required: true,
}, },
]} ]}
> >
<Input disabled /> <Input disabled />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.dms.payer.amount")} label={t("jobs.fields.dms.payer.amount")}
key={`${index}amount`} key={`${index}amount`}
name={[field.name, "amount"]} name={[field.name, "amount"]}
rules={[ rules={[
{ {
required: true, required: true,
}, },
]} ]}
> >
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("jobs.fields.dms.payer.controlnumber")} label={t("jobs.fields.dms.payer.controlnumber")}
key={`${index}controlnumber`} key={`${index}controlnumber`}
name={[field.name, "controlnumber"]} name={[field.name, "controlnumber"]}
rules={[ rules={[
{ {
required: true, required: true,
}, },
]} ]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item shouldUpdate> <Form.Item shouldUpdate>
{() => { {() => {
const payers = form.getFieldValue("payers"); const payers = form.getFieldValue("payers");
const row = payers && payers[index]; const row = payers && payers[index];
const cdkPayer = const cdkPayer =
bodyshop.cdk_configuration.payers && bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.find( bodyshop.cdk_configuration.payers.find(
(i) => i && row && i.name === row.name (i) => i && row && i.name === row.name
);
return (
<div>
{cdkPayer &&
t(`jobs.fields.${cdkPayer.control_type}`)}
</div>
); );
}}
</Form.Item>
return ( <DeleteFilled
<div> onClick={() => {
{cdkPayer && remove(field.name);
t(`jobs.fields.${cdkPayer.control_type}`)} }}
</div> />
); </Space>
}} </Form.Item>
</Form.Item> ))}
<Form.Item>
<DeleteFilled <Button
onClick={() => { type="dashed"
remove(field.name); disabled={!(fields.length < 3)}
}} onClick={() => {
/> if (fields.length < 3) add();
</LayoutFormRow> }}
style={{ width: "100%" }}
>
{t("jobs.actions.dms.addpayer")}
</Button>
</Form.Item> </Form.Item>
))} </div>
<Form.Item> );
<Button }}
type="dashed" </Form.List>
disabled={!(fields.length < 3)} <Form.Item shouldUpdate>
onClick={() => { {() => {
if (fields.length < 3) add(); //Perform Calculation to determine discrepancy.
}} let totalAllocated = Dinero();
style={{ width: "100%" }}
>
{t("dms.actions.addpayer")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item shouldUpdate>
{() => {
//Perform Calculation to determine discrepancy.
let totalAllocated = Dinero();
const payers = form.getFieldValue("payers"); const payers = form.getFieldValue("payers");
payers && payers &&
payers.forEach((payer) => { payers.forEach((payer) => {
totalAllocated = totalAllocated.add( totalAllocated = totalAllocated.add(
Dinero({ amount: Math.round((payer?.amount || 0) * 100) }) Dinero({ amount: Math.round((payer?.amount || 0) * 100) })
);
});
const totals =
socket.allocationsSummary &&
socket.allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
); );
}); const discrep = totals
const discrep = Dinero(job.job_totals.totals.total_repairs).subtract( ? totals.totalSale.subtract(totalAllocated)
totalAllocated : Dinero();
); return (
return ( <Space size="large" wrap align="center">
<Space> <Statistic
<Statistic title={t("jobs.labels.subtotal")}
title={t("jobs.labels.dms.totalallocated")} value={(totals ? totals.totalSale : Dinero()).toFormat()}
value={totalAllocated.toFormat()} />
/> <Typography.Title>-</Typography.Title>
<Statistic <Statistic
title={t("jobs.fields.subtotal")} title={t("jobs.labels.dms.totalallocated")}
value={Dinero(job.job_totals.totals.total_repairs).toFormat()} value={totalAllocated.toFormat()}
/> />
<Statistic <Typography.Title>=</Typography.Title>
title={t("jobs.labels.dms.notallocated")} <Statistic
valueStyle={{ title={t("jobs.labels.dms.notallocated")}
color: discrep.getAmount() === 0 ? "green" : "red", valueStyle={{
}} color: discrep.getAmount() === 0 ? "green" : "red",
value={discrep.toFormat()} }}
/> value={discrep.toFormat()}
<Button //disabled={discrep.getAmount() !== 0} //TODO: REMOVE THIS COMMENT. />
htmlType="submit" <Button
> disabled={
{t("jobs.actions.dms.post")} !socket.allocationsSummary || discrep.getAmount() !== 0
</Button> }
<Button htmlType="submit"
onClick={() => { >
socket.emit(`${determineDmsType(bodyshop)}-export-job`, { {t("jobs.actions.dms.post")}
jobid: job.id, </Button>
}); </Space>
}} );
> }}
Bypass </Form.Item>
</Button> </Form>
</Space> </Card>
);
}}
</Form.Item>
</Form>
); );
} }

View File

@@ -13,6 +13,7 @@ import {
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { useHistory } from "react-router-dom";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -26,11 +27,17 @@ export function JobsCloseExportButton({
disabled, disabled,
setSelectedJobs, setSelectedJobs,
}) { }) {
const history = useHistory();
const { t } = useTranslation(); const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOB); const [updateJob] = useMutation(UPDATE_JOB);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG); const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleQbxml = async () => { const handleQbxml = async () => {
if (bodyshop.cdk_dealerid) {
history.push(`/manage/dms?jobId=${jobId}`);
return;
}
logImEXEvent("jobs_close_export"); logImEXEvent("jobs_close_export");
setLoading(true); setLoading(true);
@@ -159,12 +166,7 @@ export function JobsCloseExportButton({
}; };
return ( return (
<Button <Button onClick={handleQbxml} loading={loading} disabled={disabled}>
onClick={handleQbxml}
loading={loading}
disabled={disabled}
type="dashed"
>
{t("jobs.actions.export")} {t("jobs.actions.export")}
</Button> </Button>
); );

View File

@@ -13,6 +13,7 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component"; import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component"; import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
@@ -22,9 +23,10 @@ import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
}); });
export function JobsDetailRates({ jobRO, form, job }) { export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div> <div>
@@ -77,7 +79,7 @@ export function JobsDetailRates({ jobRO, form, job }) {
label={t("jobs.fields.adjustment_bottom_line")} label={t("jobs.fields.adjustment_bottom_line")}
name="adjustment_bottom_line" name="adjustment_bottom_line"
> >
<CurrencyInput disabled={jobRO} /> <CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
</Form.Item> </Form.Item>
<Space align="end"> <Space align="end">
<Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt"> <Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt">

View File

@@ -57,6 +57,7 @@ export function LaborAllocationsTable({
sorter: (a, b) => alphaSort(a.cost_center, b.cost_center), sorter: (a, b) => alphaSort(a.cost_center, b.cost_center),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order, state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order,
render: (text, record) => `${record.cost_center} (${record.mod_lbr_ty})`,
}, },
{ {
title: t("jobs.labels.hrs_total"), title: t("jobs.labels.hrs_total"),

View File

@@ -16,6 +16,7 @@ export const CalculateAllocationsTotals = (
const r = { const r = {
opcode: value, opcode: value,
cost_center: responsibilitycenters.defaults.costs[value], cost_center: responsibilitycenters.defaults.costs[value],
mod_lbr_ty: value,
total: joblines.reduce((acc2, val2) => { total: joblines.reduce((acc2, val2) => {
return val2.mod_lbr_ty === value ? acc2 + val2.mod_lb_hrs : acc2; return val2.mod_lbr_ty === value ? acc2 + val2.mod_lb_hrs : acc2;
}, 0), }, 0),

View File

@@ -91,6 +91,18 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.dms.srcco")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cdk_configuration", "srcco"]}
>
<Input />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.dms.generic_customer_number")} label={t("bodyshop.fields.dms.generic_customer_number")}
name={["cdk_configuration", "generic_customer_number"]} name={["cdk_configuration", "generic_customer_number"]}
@@ -99,10 +111,37 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.dms.cashierid")} label={t("bodyshop.fields.dms.cashierid")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cdk_configuration", "cashierid"]} name={["cdk_configuration", "cashierid"]}
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.dms.itc_federal")}
valuePropName="checked"
name={["cdk_configuration", "itc_federal"]}
>
<Switch />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.dms.itc_state")}
valuePropName="checked"
name={["cdk_configuration", "itc_state"]}
>
<Switch />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.dms.itc_local")}
valuePropName="checked"
name={["cdk_configuration", "itc_local"]}
>
<Switch />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.dms.cdk.payers")}> <LayoutFormRow header={t("bodyshop.labels.dms.cdk.payers")}>
<Form.List name={["cdk_configuration", "payers"]}> <Form.List name={["cdk_configuration", "payers"]}>
@@ -113,7 +152,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Form.Item key={field.key}> <Form.Item key={field.key}>
<LayoutFormRow noDivider> <LayoutFormRow noDivider>
<Form.Item <Form.Item
label={t("bodyshop.fields.dms.payer.name")} label={t("jobs.fields.dms.payer.name")}
key={`${index}name`} key={`${index}name`}
name={[field.name, "name"]} name={[field.name, "name"]}
rules={[ rules={[
@@ -125,9 +164,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t( label={t("jobs.fields.dms.payer.dms_acctnumber")}
"bodyshop.fields.dms.payer.dms_acctnumber"
)}
key={`${index}dms_acctnumber`} key={`${index}dms_acctnumber`}
name={[field.name, "dms_acctnumber"]} name={[field.name, "dms_acctnumber"]}
rules={[ rules={[
@@ -139,7 +176,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.dms.payer.control_type")} label={t("jobs.fields.dms.payer.control_type")}
key={`${index}control_type`} key={`${index}control_type`}
name={[field.name, "control_type"]} name={[field.name, "control_type"]}
rules={[ rules={[
@@ -455,20 +492,26 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
{fields.map((field, index) => ( {fields.map((field, index) => (
<Form.Item key={field.key}> <Form.Item key={field.key}>
<div> <div>
<Form.Item <LayoutFormRow>
label={t("bodyshop.fields.dms.mappingname")} <Form.Item
key={`${index}name`} label={t("bodyshop.fields.dms.mappingname")}
name={[field.name, "name"]} key={`${index}name`}
rules={[ name={[field.name, "name"]}
{ rules={[
required: true, {
//message: t("general.validation.required"), required: true,
}, //message: t("general.validation.required"),
]} },
> ]}
<Input /> >
</Form.Item> <Input />
</Form.Item>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
</LayoutFormRow>
<LayoutFormRow <LayoutFormRow
header={t("bodyshop.labels.defaultcostsmapping")} header={t("bodyshop.labels.defaultcostsmapping")}
> >
@@ -1611,12 +1654,6 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Select> </Select>
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
</div> </div>
</Form.Item> </Form.Item>
))} ))}
@@ -2701,7 +2738,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
{bodyshop.cdk_dealerid && ( {bodyshop.cdk_dealerid && (
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenter_dms_acctnumber")} label={t("bodyshop.fields.dms.dms_acctnumber")}
rules={[ rules={[
{ {
required: true, required: true,
@@ -2799,7 +2836,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</Form.Item> </Form.Item>
{bodyshop.cdk_dealerid && ( {bodyshop.cdk_dealerid && (
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenter_dms_acctnumber")} label={t("bodyshop.fields.dms.dms_acctnumber")}
rules={[ rules={[
{ {
required: true, required: true,
@@ -2895,21 +2932,9 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_rate")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "taxes", "local", "rate"]}
>
<InputNumber precision={2} />
</Form.Item>
{bodyshop.cdk_dealerid && ( {bodyshop.cdk_dealerid && (
<Form.Item <Form.Item
label={t("bodyshop.fields.responsibilitycenter_dms_acctnumber")} label={t("bodyshop.fields.dms.dms_acctnumber")}
rules={[ rules={[
{ {
required: true, required: true,
@@ -2926,6 +2951,18 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Input /> <Input />
</Form.Item> </Form.Item>
)} )}
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_rate")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "taxes", "local", "rate"]}
>
<InputNumber precision={2} />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow header={<div>AR</div>}> <LayoutFormRow header={<div>AR</div>}>
{/* <Form.Item {/* <Form.Item

View File

@@ -1898,9 +1898,15 @@ export const QUERY_JOB_EXPORT_DMS = gql`
po_number po_number
clm_no clm_no
job_totals job_totals
ownr_fn
ownr_ln
ownr_co_nm
kmin kmin
kmout kmout
v_make_desc
v_model_yr
v_model_desc v_model_desc
area_of_damage
} }
} }
`; `;

View File

@@ -1,10 +1,19 @@
import { useQuery } from "@apollo/client"; import { useQuery } from "@apollo/client";
import { Button, Col, Result, Row, Select, Space } from "antd"; import {
Button,
Card,
Col,
notification,
Result,
Row,
Select,
Space,
} from "antd";
import queryString from "query-string"; import queryString from "query-string";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useLocation } from "react-router-dom"; import { useLocation, useHistory } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import SocketIO from "socket.io-client"; import SocketIO from "socket.io-client";
import AlertComponent from "../../components/alert/alert.component"; import AlertComponent from "../../components/alert/alert.component";
@@ -47,7 +56,8 @@ 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 [logLevel, setLogLevel] = useState("DEBUG");
const history = useHistory();
const [logs, setLogs] = useState([]); const [logs, setLogs] = useState([]);
const search = queryString.parse(useLocation().search); const search = queryString.parse(useLocation().search);
const { jobId } = search; const { jobId } = search;
@@ -61,6 +71,10 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
document.title = t("titles.dms"); document.title = t("titles.dms");
setSelectedHeader("dms"); setSelectedHeader("dms");
setBreadcrumbs([ setBreadcrumbs([
{
link: "/manage/accounting/receivables",
label: t("titles.bc.accounting-receivables"),
},
{ {
link: "/manage/dms", link: "/manage/dms",
label: t("titles.bc.dms"), label: t("titles.bc.dms"),
@@ -69,9 +83,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
}, [t, setBreadcrumbs, setSelectedHeader]); }, [t, setBreadcrumbs, setSelectedHeader]);
useEffect(() => { useEffect(() => {
socket.on("connected", () => { socket.on("connect", () => socket.emit("set-log-level", logLevel));
console.log("Connected again.");
});
socket.on("reconnect", () => { socket.on("reconnect", () => {
setLogs((logs) => { setLogs((logs) => {
return [ return [
@@ -90,10 +102,14 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
return [...logs, payload]; return [...logs, payload];
}); });
}); });
socket.on("export-success", (payload) => {
notification.success({
message: t("jobs.successes.exported"),
});
history.push("/manage/accounting/receivables");
});
socket.connect(); if (socket.disconnected) socket.connect();
socket.emit("set-log-level", logLevel);
return () => { return () => {
socket.removeAllListeners(); socket.removeAllListeners();
socket.disconnect(); socket.disconnect();
@@ -101,49 +117,74 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
if (!jobId || !bodyshop.cdk_dealerid || !(data && data.jobs_by_pk))
return <Result status="404" />;
if (loading) return <LoadingSpinner />; if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
if (!jobId || !bodyshop.cdk_dealerid || !(data && data.jobs_by_pk))
return <Result status="404" />;
return ( return (
<div> <div>
<Row gutter={32}> <Row gutter={[16, 16]}>
<Col span={18}> <Col span={10}>
{data && data.jobs_by_pk && data.jobs_by_pk.ro_number} <DmsAllocationsSummary
<DmsAllocationsSummary socket={socket} jobId={jobId} /> title={`${data && data.jobs_by_pk && data.jobs_by_pk.ro_number} | ${
data.jobs_by_pk.ownr_fn || ""
} ${data.jobs_by_pk.ownr_ln || ""} ${
data.jobs_by_pk.ownr_co_nm || ""
} | ${data.jobs_by_pk.v_model_yr || ""} ${
data.jobs_by_pk.v_make_desc || ""
} ${data.jobs_by_pk.v_model_desc || ""}`}
socket={socket}
jobId={jobId}
/>
</Col>
<Col span={14}>
<DmsPostForm <DmsPostForm
socket={socket} socket={socket}
jobId={jobId} jobId={jobId}
job={data && data.jobs_by_pk} job={data && data.jobs_by_pk}
/> />
</Col> </Col>
<Col span={6}>
<Space> <DmsCustomerSelector />
<Select
placeholder="Log Level" <Col span={24}>
value={logLevel} <Card
onChange={(value) => { title={t("jobs.labels.dms.logs")}
setLogLevel(value); extra={
socket.emit("set-log-level", value); <Space wrap>
}} <Select
> placeholder="Log Level"
<Select.Option key="TRACE">TRACE</Select.Option> value={logLevel}
<Select.Option key="DEBUG">DEBUG</Select.Option> onChange={(value) => {
<Select.Option key="INFO">INFO</Select.Option> setLogLevel(value);
<Select.Option key="WARNING">WARNING</Select.Option> socket.emit("set-log-level", value);
<Select.Option key="ERROR">ERROR</Select.Option> }}
</Select> >
<Button onClick={() => setLogs([])}>Clear Logs</Button> <Select.Option key="TRACE">TRACE</Select.Option>
</Space> <Select.Option key="DEBUG">DEBUG</Select.Option>
<div style={{ maxHeight: "500px", overflowY: "auto" }}> <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>
<Button
onClick={() => {
setLogs([]);
socket.disconnect();
socket.connect();
}}
>
Reconnect
</Button>
</Space>
}
>
<DmsLogEvents socket={socket} logs={logs} /> <DmsLogEvents socket={socket} logs={logs} />
</div> </Card>
</Col> </Col>
</Row> </Row>
<DmsCustomerSelector />
</div> </div>
); );
} }

View File

@@ -218,7 +218,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
}, },
]} ]}
> >
<InputNumber disabled={jobRO} /> <InputNumber precision={0} disabled={jobRO} />
</Form.Item> </Form.Item>
)} )}
{bodyshop.cdk_dealerid && ( {bodyshop.cdk_dealerid && (
@@ -244,7 +244,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
}), }),
]} ]}
> >
<InputNumber disabled={jobRO} /> <InputNumber precision={0} disabled={jobRO} />
</Form.Item> </Form.Item>
)} )}
</LayoutFormRow> </LayoutFormRow>

View File

@@ -237,6 +237,9 @@
"dms_acctnumber": "DMS Account #", "dms_acctnumber": "DMS Account #",
"dms_wip_acctnumber": "DMS W.I.P. Account #", "dms_wip_acctnumber": "DMS W.I.P. Account #",
"generic_customer_number": "Generic Customer Number", "generic_customer_number": "Generic Customer Number",
"itc_federal": "Federal Tax is ITC?",
"itc_local": "Local Tax is ITC?",
"itc_state": "State Tax is ITC?",
"mappingname": "DMS Mapping Name", "mappingname": "DMS Mapping Name",
"srcco": "Source Company #/Dealer #" "srcco": "Source Company #/Dealer #"
}, },
@@ -1081,10 +1084,10 @@
"dms": { "dms": {
"addpayer": "Add Payer", "addpayer": "Add Payer",
"createnewcustomer": "Create New Customer", "createnewcustomer": "Create New Customer",
"findmakemodelcode": "", "findmakemodelcode": "Find Make/Model Code",
"getmakes": "Get Makes", "getmakes": "",
"post": "Post", "post": "Post",
"refetchmakesmodels": "", "refetchmakesmodels": "Refetch Make and Model Codes",
"usegeneric": "Use Generic Customer", "usegeneric": "Use Generic Customer",
"useselected": "Use Selected Customer" "useselected": "Use Selected Customer"
}, },
@@ -1146,27 +1149,27 @@
"adjustmenthours": "Adjustment Hours", "adjustmenthours": "Adjustment Hours",
"alt_transport": "Alt. Trans.", "alt_transport": "Alt. Trans.",
"area_of_damage_impact": { "area_of_damage_impact": {
"10": "", "10": "Left Front Side",
"11": "", "11": "Left Front Corner",
"12": "", "12": "Front",
"13": "", "13": "Rollover",
"14": "", "14": "Unknown",
"15": "", "15": "Total Loss",
"16": "", "16": "Non-collision",
"25": "", "25": "Hood",
"26": "", "26": "Deck-lid",
"27": "", "27": "Roof",
"28": "", "28": "Undercarriage",
"34": "", "34": "All Over",
"01": "", "01": "Right Front Corner",
"02": "", "02": "Right Front Side",
"03": "", "03": "Right Side",
"04": "", "04": "Right Rear Side",
"05": "", "05": "Right Rear Corner",
"06": "", "06": "Rear",
"07": "", "07": "Left Rear Corner",
"08": "", "08": "Left Rear Side",
"09": "" "09": "Left Side"
}, },
"ca_bc_pvrt": "PVRT", "ca_bc_pvrt": "PVRT",
"ca_customer_gst": "Customer Portion of GST", "ca_customer_gst": "Customer Portion of GST",
@@ -1199,7 +1202,7 @@
"dms_make": "DMS Make", "dms_make": "DMS Make",
"dms_model": "DMS Model", "dms_model": "DMS Model",
"dms_wip_acctnumber": "Cost WIP DMS Acct #", "dms_wip_acctnumber": "Cost WIP DMS Acct #",
"id": "", "id": "DMS ID",
"journal": "Journal #", "journal": "Journal #",
"name1": "Customer Name", "name1": "Customer Name",
"payer": { "payer": {
@@ -1212,7 +1215,7 @@
"sale": "Sale", "sale": "Sale",
"sale_dms_acctnumber": "Sale DMS Acct #", "sale_dms_acctnumber": "Sale DMS Acct #",
"story": "Story", "story": "Story",
"vinowner": "" "vinowner": "VIN Owner"
}, },
"driveable": "Driveable", "driveable": "Driveable",
"employee_body": "Body", "employee_body": "Body",
@@ -1434,11 +1437,11 @@
"difference": "Difference", "difference": "Difference",
"diskscan": "Scan Disk for Estimates", "diskscan": "Scan Disk for Estimates",
"dms": { "dms": {
"defaultstory": "", "defaultstory": "Bodyshop RO {{ro_number}}. Damage to $t(jobs.fields.area_of_damage_impact.{{area_of_damage}}).",
"kmoutnotgreaterthankmin": "Mileage out must be greater than mileage in.", "kmoutnotgreaterthankmin": "Mileage out must be greater than mileage in.",
"logs": "", "logs": "Logs",
"notallocated": "Not Allocated", "notallocated": "Not Allocated",
"postingform": "", "postingform": "Posting Form",
"totalallocated": "Total Amount Allocated" "totalallocated": "Total Amount Allocated"
}, },
"documents": "Documents", "documents": "Documents",
@@ -2282,7 +2285,7 @@
"courtesycars-detail": "Courtesy Car {{number}}", "courtesycars-detail": "Courtesy Car {{number}}",
"courtesycars-new": "New Courtesy Car", "courtesycars-new": "New Courtesy Car",
"dashboard": "Dashboard", "dashboard": "Dashboard",
"dms": "", "dms": "DMS Export",
"export-logs": "Export Logs", "export-logs": "Export Logs",
"jobs": "Jobs", "jobs": "Jobs",
"jobs-active": "Active Jobs", "jobs-active": "Active Jobs",
@@ -2321,7 +2324,7 @@
"courtesycars-create": "New Courtesy Car | $t(titles.app)", "courtesycars-create": "New Courtesy Car | $t(titles.app)",
"courtesycars-detail": "Courtesy Car {{id}} | $t(titles.app)", "courtesycars-detail": "Courtesy Car {{id}} | $t(titles.app)",
"dashboard": "Dashboard | $t(titles.app)", "dashboard": "Dashboard | $t(titles.app)",
"dms": "", "dms": "DMS Export | $t(titles.app)",
"export-logs": "Export Logs | $t(titles.app)", "export-logs": "Export Logs | $t(titles.app)",
"jobs": "Active Jobs | $t(titles.app)", "jobs": "Active Jobs | $t(titles.app)",
"jobs-admin": "Job {{ro_number}} - Admin | $t(titles.app)", "jobs-admin": "Job {{ro_number}} - Admin | $t(titles.app)",

View File

@@ -237,6 +237,9 @@
"dms_acctnumber": "", "dms_acctnumber": "",
"dms_wip_acctnumber": "", "dms_wip_acctnumber": "",
"generic_customer_number": "", "generic_customer_number": "",
"itc_federal": "",
"itc_local": "",
"itc_state": "",
"mappingname": "", "mappingname": "",
"srcco": "" "srcco": ""
}, },

View File

@@ -237,6 +237,9 @@
"dms_acctnumber": "", "dms_acctnumber": "",
"dms_wip_acctnumber": "", "dms_wip_acctnumber": "",
"generic_customer_number": "", "generic_customer_number": "",
"itc_federal": "",
"itc_local": "",
"itc_state": "",
"mappingname": "", "mappingname": "",
"srcco": "" "srcco": ""
}, },

View File

@@ -79,40 +79,78 @@ exports.default = async function (socket, jobid) {
bill_val.billlines.map((line_val) => { bill_val.billlines.map((line_val) => {
if (!bill_acc[line_val.cost_center]) if (!bill_acc[line_val.cost_center])
bill_acc[line_val.cost_center] = Dinero(); bill_acc[line_val.cost_center] = Dinero();
const lineDinero = Dinero({
let lineDinero = Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100), amount: Math.round((line_val.actual_cost || 0) * 100),
}) })
.multiply(line_val.quantity) .multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1); .multiply(bill_val.is_credit_memo ? -1 : 1);
// //Add appropriate tax amounts.
// const {
// applicable_taxes: { local, state, federal },
// } = line_val;
// if (local) {
// if (bodyshop.cdk_configuration.itc_local)
// taxAllocations.local.cost = taxAllocations.local.cost.add(
// lineDinero.percentage(bill_val.local_tax_rate || 0)
// );
// lineDinero = lineDinero.add(
// lineDinero.percentage(bill_val.local_tax_rate || 0)
// );
// }
// if (state) {
// if (bodyshop.cdk_configuration.itc_state)
// taxAllocations.state.cost = taxAllocations.state.cost.add(
// lineDinero.percentage(bill_val.state_tax_rate || 0)
// );
// lineDinero = lineDinero.add(
// lineDinero.percentage(bill_val.state_tax_rate || 0)
// );
// }
// if (federal) {
// //If it's an ITC, add it as a negative cost, otherwise add it to the item cost.
// if (bodyshop.cdk_configuration.itc_federal)
// taxAllocations.federal.cost = taxAllocations.federal.cost.add(
// lineDinero.percentage(bill_val.federal_tax_rate || 0)
// );
// lineDinero = lineDinero.add(
// lineDinero.percentage(bill_val.federal_tax_rate || 0)
// );
// // bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
// // lineDinero.percentage(bill_val.federal_tax_rate || 0)
// // );
// }
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center] =
bill_acc[line_val.cost_center].add(lineDinero); 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 null;
}); });
return bill_acc; return bill_acc;
}, {}); }, {});
job.timetickets.forEach((ticket) => {
//Get the total amount of the ticket.
let TicketTotal = Dinero({
amount: Math.round(
ticket.rate *
(ticket.employee && ticket.employee.flat_rate
? ticket.productivehrs || 0
: ticket.actualhrs || 0) *
100
),
});
//Add it to the right cost center.
if (!costCenterHash[ticket.cost_center])
costCenterHash[ticket.cost_center] = Dinero();
costCenterHash[ticket.cost_center] =
costCenterHash[ticket.cost_center].add(TicketTotal);
});
const jobAllocations = _.union( const jobAllocations = _.union(
Object.keys(profitCenterHash), Object.keys(profitCenterHash),
Object.keys(costCenterHash) Object.keys(costCenterHash)
@@ -141,7 +179,9 @@ exports.default = async function (socket, jobid) {
taxAllocations[key].sale.getAmount() > 0 || taxAllocations[key].sale.getAmount() > 0 ||
taxAllocations[key].cost.getAmount() > 0 taxAllocations[key].cost.getAmount() > 0
) )
.map((key) => taxAllocations[key]), .map((key) => {
return { ...taxAllocations[key], tax: key };
}),
]; ];
} catch (error) { } catch (error) {
CdkBase.createLogEvent( CdkBase.createLogEvent(

View File

@@ -8,14 +8,11 @@ require("dotenv").config({
const GraphQLClient = require("graphql-request").GraphQLClient; const GraphQLClient = require("graphql-request").GraphQLClient;
const soap = require("soap"); const soap = require("soap");
const queries = require("../graphql-client/queries"); const queries = require("../graphql-client/queries");
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 logger = require("../utils/logger");
const Dinero = require("dinero.js");
const _ = require("lodash");
const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl"); const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
const { performance } = require("perf_hooks");
const apiGqlClient = require("../graphql-client/graphql-client").client;
// exports.default = async function (socket, cdk_dealerid) { // exports.default = async function (socket, cdk_dealerid) {
// try { // try {
@@ -87,6 +84,7 @@ exports.default = async function ReloadCdkMakes(req, res) {
count: newList.length, count: newList.length,
} }
); );
res.sendStatus(200);
} catch (error) { } catch (error) {
logger.log( logger.log(
"cdk-replace-makes-models-error", "cdk-replace-makes-models-error",
@@ -98,6 +96,7 @@ exports.default = async function ReloadCdkMakes(req, res) {
error, error,
} }
); );
res.status(500).json(error);
} }
}; };
@@ -108,7 +107,7 @@ async function GetCdkMakes(req, cdk_dealerid) {
try { try {
const soapClientVehicleInsert = await soap.createClientAsync( const soapClientVehicleInsert = await soap.createClientAsync(
CdkWsdl.VehicleInsert CdkWsdl.VehicleInsertUpdate
); );
const soapResponseVehicleSearch = const soapResponseVehicleSearch =

View File

@@ -11,6 +11,8 @@ 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 { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl"); const { CDK_CREDENTIALS, CheckCdkResponseForError } = require("./cdk-wsdl");
const CalcualteAllocations = require("./cdk-calculate-allocations").default;
const moment = require("moment"); const moment = require("moment");
exports.default = async function (socket, { txEnvelope, jobid }) { exports.default = async function (socket, { txEnvelope, jobid }) {
@@ -44,7 +46,7 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
CdkBase.createLogEvent( CdkBase.createLogEvent(
socket, socket,
"DEBUG", "DEBUG",
`{2.1} Querying the Vehicle using the DMSVid: ${socket.DMSVid}` `{2.1} Querying the Vehicle using the DMSVid: ${socket.DMSVid.vehiclesVehId}`
); );
socket.DMSVeh = await QueryDmsVehicleById(socket, JobData, socket.DMSVid); socket.DMSVeh = await QueryDmsVehicleById(socket, JobData, socket.DMSVid);
@@ -73,7 +75,9 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
socket.DMSCustList = await QueryDmsCustomerByName(socket, JobData); socket.DMSCustList = await QueryDmsCustomerByName(socket, JobData);
socket.emit("cdk-select-customer", [ socket.emit("cdk-select-customer", [
...(socket.DMSVehCustomer ? [socket.DMSVehCustomer] : []), ...(socket.DMSVehCustomer
? [{ ...socket.DMSVehCustomer, vinOwner: true }]
: []),
...socket.DMSCustList, ...socket.DMSCustList,
]); ]);
} catch (error) { } catch (error) {
@@ -82,14 +86,6 @@ exports.default = async function (socket, { txEnvelope, jobid }) {
"ERROR", "ERROR",
`Error encountered in CdkJobExport. ${error}` `Error encountered in CdkJobExport. ${error}`
); );
} finally {
//Ensure we always insert logEvents
//GQL to insert logevents.
CdkBase.createLogEvent(
socket,
"DEBUG",
`Capturing log events to database.`
);
} }
}; };
@@ -146,18 +142,80 @@ async function CdkSelectedCustomer(socket, selectedCustomerId) {
); );
socket.DMSVeh = await UpdateDmsVehicle(socket); socket.DMSVeh = await UpdateDmsVehicle(socket);
} }
CdkBase.createLogEvent( CdkBase.createLogEvent(
socket, socket,
"DEBUG", "DEBUG",
`{5}Updating Service Vehicle History.` `{5} Creating Transaction header with Dms Start WIP`
); );
await InsertServiceVehicleHistory(socket); socket.DMSTransHeader = await InsertDmsStartWip(socket);
CdkBase.createLogEvent(
socket,
"DEBUG",
`{5.1} Creating Transaction with ID ${socket.DMSTransHeader.transID}`
);
socket.DMSBatchTxn = await InsertDmsBatchWip(socket);
CdkBase.createLogEvent(
socket,
"DEBUG",
`{6} Attempting to post Transaction with ID ${socket.DMSTransHeader.transID}`
);
socket.DmsBatchTxnPost = await PostDmsBatchWip(socket);
if (socket.DmsBatchTxnPost.code === "success") {
//something
CdkBase.createLogEvent(
socket,
"DEBUG",
`{6} Successfully posted sransaction to DMS.`
);
await MarkJobExported(socket, socket.JobData.id);
CdkBase.createLogEvent(
socket,
"DEBUG",
`{5} Updating Service Vehicle History. ***SKIPPING FOR NOW TO PRESERVE RO NUMBERS ***`
);
//socket.DMSVehHistory = await InsertServiceVehicleHistory(socket);
socket.emit("export-success", socket.JobData.id);
} else {
//Get the error code
CdkBase.createLogEvent(
socket,
"DEBUG",
`{6.1} Getting errors for Transaction ID ${socket.DMSTransHeader.transID}`
);
socket.DmsError = await QueryDmsErrWip(socket);
//Delete the transaction
CdkBase.createLogEvent(
socket,
"DEBUG",
`{6.2} Deleting Transaction ID ${socket.DMSTransHeader.transID}`
);
socket.DmsBatchTxnPost = await DeleteDmsWip(socket);
//Emit the error in a nice way .
socket.DmsError.errMsg
.split("|")
.map(
(e) =>
e !== null &&
e !== "" &&
CdkBase.createLogEvent(
socket,
"ERROR",
`Error(s) encountered in posting transaction. ${e}`
)
);
}
} catch (error) { } catch (error) {
CdkBase.createLogEvent( CdkBase.createLogEvent(
socket, socket,
"ERROR", "ERROR",
`Error encountered in CdkSelectedCustomer. ${error}` `Error encountered in CdkSelectedCustomer. ${error}`
); );
await InsertFailedExportLog(socket, error);
} finally { } finally {
//Ensure we always insert logEvents //Ensure we always insert logEvents
//GQL to insert logevents. //GQL to insert logevents.
@@ -509,7 +567,7 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
await soapClientCustomerInsertUpdate.insertAsync( await soapClientCustomerInsertUpdate.insertAsync(
{ {
arg0: CDK_CREDENTIALS, arg0: CDK_CREDENTIALS,
arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid }, //TODO: Verify why this does not follow the other standards. arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid },
arg2: { userId: null }, arg2: { userId: null },
arg3: { arg3: {
//Copied the required fields from the other integration. //Copied the required fields from the other integration.
@@ -519,7 +577,12 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
addressLine: socket.JobData.ownr_addr1, addressLine: socket.JobData.ownr_addr1,
city: socket.JobData.ownr_city, city: socket.JobData.ownr_city,
country: null, country: null,
postalCode: socket.JobData.ownr_zip, postalCode:
socket.JobData.ownr_zip &&
socket.JobData.ownr_zip //TODO Need to remove for US Based customers.
.toUpperCase()
.replace(/\W/g, "")
.replace(/(...)/, "$1 "),
stateOrProvince: socket.JobData.ownr_st, stateOrProvince: socket.JobData.ownr_st,
}, },
contactInfo: { contactInfo: {
@@ -527,6 +590,10 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
main: true, main: true,
value: socket.JobData.ownr_ph1, value: socket.JobData.ownr_ph1,
}, },
email: {
desc: socket.JobData.ownr_ea ? "Other" : "CustomerDeclined",
value: socket.JobData.ownr_ea ? "Other" : null,
},
}, },
demographics: null, demographics: null,
name1: { name1: {
@@ -539,6 +606,10 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
suffix: null, suffix: null,
title: null, title: null,
}, },
//TODO - REMOVE THIS AFTER TESTING.
...(process.env.NODE_ENV !== "production"
? { arStatus: { dealerField1: "Testing" } }
: {}),
}, },
}, },
@@ -751,7 +822,7 @@ async function InsertServiceVehicleHistory(socket) {
closeDate: moment(socket.JobData.invoice_date).format("YYYY-MM-DD"), closeDate: moment(socket.JobData.invoice_date).format("YYYY-MM-DD"),
closeTime: moment(socket.JobData.invoice_date).format("HH:MM:SS"), closeTime: moment(socket.JobData.invoice_date).format("HH:MM:SS"),
comments: socket.txEnvelope.story, comments: socket.txEnvelope.story,
cashierID: socket.JobData.bodyshop.cdk_configuration.cashierid, //NEEDS TO BE PROVIDED BY DEALER. cashierID: socket.JobData.bodyshop.cdk_configuration.cashierid,
}, },
}); });
@@ -779,8 +850,7 @@ async function InsertServiceVehicleHistory(socket) {
`soapClientServiceHistoryInsert.serviceHistoryHeaderInsert response.` `soapClientServiceHistoryInsert.serviceHistoryHeaderInsert response.`
); );
CheckCdkResponseForError(socket, soapResponseServiceHistoryInsert); CheckCdkResponseForError(socket, soapResponseServiceHistoryInsert);
const VehicleFromDMS = result && result.return && result.return.vehicle; return result && result.return;
return VehicleFromDMS;
} catch (error) { } catch (error) {
CdkBase.createLogEvent( CdkBase.createLogEvent(
socket, socket,
@@ -797,19 +867,20 @@ async function InsertDmsStartWip(socket) {
); );
const soapResponseAccountingGLInsertUpdate = const soapResponseAccountingGLInsertUpdate =
await soapClientAccountingGLInsertUpdate.startWIPAsync({ await soapClientAccountingGLInsertUpdate.doStartWIPAsync({
arg0: CDK_CREDENTIALS, arg0: CDK_CREDENTIALS,
arg1: { id: socket.JobData.bodyshop.cdk_dealerid }, arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid },
arg2: { arg2: {
acctgDate: moment().toISOString(), acctgDate: moment().format("YYYY-MM-DD"),
//socket.JobData.invoice_date
desc: socket.txEnvelope.story, desc: socket.txEnvelope.story,
docType: 7 || 10, //Need to check what this usually would be? docType: 10 || 7, //Need to check what this usually would be? Apparently it is almost always 10 or 7.
//1 Cash Receipt , 2 Check, 3 Journal Voucher, 4 Parts invoice, 5 Payable Invoice, 6 Recurring Entry, 7 Repair Order Invoice, 8 Vehicle Purchase Invoice, 9 Vehicle Sale Invoice, 10 Other, 11 Payroll, 12 Finance Charge, 13 FMLR Invoice, 14 Parts Credit Memo, 15 Manufacturer Document, 16 FMLR Credit Memo //1 Cash Receipt , 2 Check, 3 Journal Voucher, 4 Parts invoice, 5 Payable Invoice, 6 Recurring Entry, 7 Repair Order Invoice, 8 Vehicle Purchase Invoice, 9 Vehicle Sale Invoice, 10 Other, 11 Payroll, 12 Finance Charge, 13 FMLR Invoice, 14 Parts Credit Memo, 15 Manufacturer Document, 16 FMLR Credit Memo
m13Flag: 0, m13Flag: 0,
refer: socket.JobData.ro_number, refer: socket.JobData.ro_number,
srcCo: socket.txEnvelope.journal, srcCo: socket.JobData.bodyshop.cdk_configuration.srcco,
srcJrnl: socket.txEnvelope.journal, srcJrnl: socket.txEnvelope.journal,
userID: "?", //Where is this coming from? userID: socket.JobData.bodyshop.cdk_configuration.cashierid, //Where is this coming from?
//userName: "IMEX", //userName: "IMEX",
}, },
}); });
@@ -820,13 +891,13 @@ async function InsertDmsStartWip(socket) {
CdkBase.createXmlEvent( CdkBase.createXmlEvent(
socket, socket,
rawRequest, rawRequest,
`soapClientAccountingGLInsertUpdate.startWIPAsync request.` `soapClientAccountingGLInsertUpdate.doStartWIPAsync request.`
); );
CdkBase.createLogEvent( CdkBase.createLogEvent(
socket, socket,
"TRACE", "TRACE",
`soapClientAccountingGLInsertUpdate.startWIPAsync Result ${JSON.stringify( `soapClientAccountingGLInsertUpdate.doStartWIPAsync Result ${JSON.stringify(
result, result,
null, null,
2 2
@@ -835,17 +906,383 @@ async function InsertDmsStartWip(socket) {
CdkBase.createXmlEvent( CdkBase.createXmlEvent(
socket, socket,
rawResponse, rawResponse,
`soapClientAccountingGLInsertUpdate.startWIPAsync response.` `soapClientAccountingGLInsertUpdate.doStartWIPAsync response.`
); );
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate); CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const VehicleFromDMS = result && result.return && result.return.vehicle; const TransactionHeader = result && result.return;
return VehicleFromDMS; return TransactionHeader;
} catch (error) { } catch (error) {
CdkBase.createLogEvent( CdkBase.createLogEvent(
socket, socket,
"ERROR", "ERROR",
`Error in QueryDmsVehicleById - ${error}` `Error in InsertDmsStartWip - ${error}`
); );
throw new Error(error); throw new Error(error);
} }
} }
async function InsertDmsBatchWip(socket) {
try {
const soapClientAccountingGLInsertUpdate = await soap.createClientAsync(
CdkWsdl.AccountingGLInsertUpdate
);
const soapResponseAccountingGLInsertUpdate =
await soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync({
arg0: CDK_CREDENTIALS,
arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid },
arg2: {
transWIPs: await GenerateTransWips(socket),
},
});
const [result, rawResponse, , rawRequest] =
soapResponseAccountingGLInsertUpdate;
CdkBase.createXmlEvent(
socket,
rawRequest,
`soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync request.`
);
CdkBase.createLogEvent(
socket,
"TRACE",
`soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync Result ${JSON.stringify(
result,
null,
2
)}`
);
CdkBase.createXmlEvent(
socket,
rawResponse,
`soapClientAccountingGLInsertUpdate.doTransBatchWIPAsync response.`
);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const BatchWipResult = result && result.return;
return BatchWipResult;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in InsertDmsBatchWip - ${error}`
);
throw new Error(error);
}
}
async function GenerateTransWips(socket) {
const allocations = await CalcualteAllocations(socket, socket.JobData.id);
const wips = [];
allocations.forEach((alloc) => {
//Add the sale item from each allocation.
if (alloc.sale.getAmount() > 0 && !alloc.tax) {
const item = {
acct: alloc.profitCenter.dms_acctnumber,
cntl: socket.JobData.ro_number,
cntl2: null,
credtMemoNo: null,
postAmt: alloc.sale.multiply(-1).getAmount(),
postDesc: null,
prod: null,
statCnt: 1,
transID: socket.DMSTransHeader.transID,
trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco,
};
wips.push(item);
}
//Add the cost Item.
if (alloc.cost.getAmount() > 0 && !alloc.tax) {
const item = {
acct: alloc.costCenter.dms_acctnumber,
cntl: socket.JobData.ro_number,
cntl2: null,
credtMemoNo: null,
postAmt: alloc.cost.getAmount(),
postDesc: null,
prod: null,
statCnt: 1,
transID: socket.DMSTransHeader.transID,
trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco,
};
wips.push(item);
const itemWip = {
acct: alloc.costCenter.dms_wip_acctnumber,
cntl: socket.JobData.ro_number,
cntl2: null,
credtMemoNo: null,
postAmt: alloc.cost.multiply(-1).getAmount(),
postDesc: null,
prod: null,
statCnt: 1,
transID: socket.DMSTransHeader.transID,
trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco,
};
wips.push(itemWip);
//Add to the WIP account.
}
if (alloc.tax) {
// if (alloc.cost.getAmount() > 0) {
// const item = {
// acct: alloc.costCenter.dms_acctnumber,
// cntl: socket.JobData.ro_number,
// cntl2: null,
// credtMemoNo: null,
// postAmt: alloc.cost.getAmount(),
// postDesc: null,
// prod: null,
// statCnt: 1,
// transID: socket.DMSTransHeader.transID,
// trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco,
// };
// wips.push(item);
// }
if (alloc.sale.getAmount() > 0) {
const item2 = {
acct: alloc.profitCenter.dms_acctnumber,
cntl: socket.JobData.ro_number,
cntl2: null,
credtMemoNo: null,
postAmt: alloc.sale.multiply(-1).getAmount(),
postDesc: null,
prod: null,
statCnt: 1,
transID: socket.DMSTransHeader.transID,
trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco,
};
wips.push(item2);
}
}
});
socket.txEnvelope.payers.forEach((payer) => {
const item = {
acct: payer.dms_acctnumber,
cntl: payer.controlnumber,
cntl2: null,
credtMemoNo: null,
postAmt: Math.round(payer.amount * 100),
postDesc: null,
prod: null,
statCnt: 1,
transID: socket.DMSTransHeader.transID,
trgtCoID: socket.JobData.bodyshop.cdk_configuration.srcco,
};
wips.push(item);
});
//should validate that the wips = 0
console.log(
"WIPS TOTAL",
wips.reduce((acc, val) => {
console.log(val);
console.log(acc + val.postAmt);
return acc + val.postAmt;
}, 0)
);
return wips;
}
async function PostDmsBatchWip(socket) {
try {
const soapClientAccountingGLInsertUpdate = await soap.createClientAsync(
CdkWsdl.AccountingGLInsertUpdate
);
const soapResponseAccountingGLInsertUpdate =
await soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync({
arg0: CDK_CREDENTIALS,
arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid },
arg2: {
postWIP: { opCode: "P", transID: socket.DMSTransHeader.transID },
},
});
const [result, rawResponse, , rawRequest] =
soapResponseAccountingGLInsertUpdate;
CdkBase.createXmlEvent(
socket,
rawRequest,
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync request.`
);
CdkBase.createLogEvent(
socket,
"TRACE",
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(
result,
null,
2
)}`
);
CdkBase.createXmlEvent(
socket,
rawResponse,
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`
);
// CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const PostResult = result && result.return;
return PostResult;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in PostDmsBatchWip - ${error}`
);
throw new Error(error);
}
}
async function QueryDmsErrWip(socket) {
try {
const soapClientAccountingGLInsertUpdate = await soap.createClientAsync(
CdkWsdl.AccountingGLInsertUpdate
);
const soapResponseAccountingGLInsertUpdate =
await soapClientAccountingGLInsertUpdate.doErrWIPAsync({
arg0: CDK_CREDENTIALS,
arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid },
arg2: socket.DMSTransHeader.transID,
});
const [result, rawResponse, , rawRequest] =
soapResponseAccountingGLInsertUpdate;
CdkBase.createXmlEvent(
socket,
rawRequest,
`soapClientAccountingGLInsertUpdate.doErrWIPAsync request.`
);
CdkBase.createLogEvent(
socket,
"TRACE",
`soapClientAccountingGLInsertUpdate.doErrWIPAsync Result ${JSON.stringify(
result,
null,
2
)}`
);
CdkBase.createXmlEvent(
socket,
rawResponse,
`soapClientAccountingGLInsertUpdate.doErrWIPAsync response.`
);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const PostResult = result && result.return;
return PostResult;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in QueryDmsErrWip - ${error}`
);
throw new Error(error);
}
}
async function DeleteDmsWip(socket) {
try {
const soapClientAccountingGLInsertUpdate = await soap.createClientAsync(
CdkWsdl.AccountingGLInsertUpdate
);
const soapResponseAccountingGLInsertUpdate =
await soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync({
arg0: CDK_CREDENTIALS,
arg1: { dealerId: socket.JobData.bodyshop.cdk_dealerid },
arg2: {
postWIP: { opCode: "D", transID: socket.DMSTransHeader.transID },
},
});
const [result, rawResponse, , rawRequest] =
soapResponseAccountingGLInsertUpdate;
CdkBase.createXmlEvent(
socket,
rawRequest,
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync request.`
);
CdkBase.createLogEvent(
socket,
"TRACE",
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync Result ${JSON.stringify(
result,
null,
2
)}`
);
CdkBase.createXmlEvent(
socket,
rawResponse,
`soapClientAccountingGLInsertUpdate.doPostBatchWIPAsync response.`
);
CheckCdkResponseForError(socket, soapResponseAccountingGLInsertUpdate);
const PostResult = result && result.return;
return PostResult;
} catch (error) {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error in PostDmsBatchWip - ${error}`
);
throw new Error(error);
}
}
async function MarkJobExported(socket, jobid) {
CdkBase.createLogEvent(
socket,
"DEBUG",
`Marking job as exported for id ${jobid}`
);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.MARK_JOB_EXPORTED, {
jobId: jobid,
job: {
status:
socket.JobData.bodyshop.md_ro_statuses.default_exported ||
"Exported*",
date_exported: new Date(),
},
log: {
bodyshopid: socket.JobData.bodyshop.id,
jobid: jobid,
successful: true,
useremail: socket.user.email,
},
});
return result;
}
async function InsertFailedExportLog(socket, error) {
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.INSERT_EXPORT_LOG, {
log: {
bodyshopid: socket.JobData.bodyshop.id,
jobid: socket.JobData.id,
successful: false,
message: [error],
useremail: socket.user.email,
},
});
return result;
}

View File

@@ -78,6 +78,7 @@ exports.checkIndividualResult = checkIndividualResult;
const cdkDomain = "https://uat-3pa.dmotorworks.com"; const cdkDomain = "https://uat-3pa.dmotorworks.com";
exports.default = { exports.default = {
// VehicleSearch: `${cdkDomain}/pip-vehicle/services/VehicleSearch?wsdl`, // VehicleSearch: `${cdkDomain}/pip-vehicle/services/VehicleSearch?wsdl`,
HelpDataBase: `${cdkDomain}/pip-help-database-location/services/HelpDatabaseLocation?wsdl`,
AccountingGLInsertUpdate: `${cdkDomain}/pip-accounting-gl/services/AccountingGLInsertUpdate?wsdl`, AccountingGLInsertUpdate: `${cdkDomain}/pip-accounting-gl/services/AccountingGLInsertUpdate?wsdl`,
VehicleInsertUpdate: `${cdkDomain}/pip-vehicle/services/VehicleInsertUpdate?wsdl`, VehicleInsertUpdate: `${cdkDomain}/pip-vehicle/services/VehicleInsertUpdate?wsdl`,
CustomerInsertUpdate: `${cdkDomain}/pip-customer/services/CustomerInsertUpdate?wsdl`, CustomerInsertUpdate: `${cdkDomain}/pip-customer/services/CustomerInsertUpdate?wsdl`,

View File

@@ -140,6 +140,7 @@ query QUERY_JOBS_FOR_CDK_EXPORT($id: uuid!) {
ownr_zip ownr_zip
ownr_city ownr_city
ownr_st ownr_st
ownr_ea
ins_co_nm ins_co_nm
job_totals job_totals
rate_la1 rate_la1
@@ -177,9 +178,11 @@ query QUERY_JOBS_FOR_CDK_EXPORT($id: uuid!) {
ca_customer_gst ca_customer_gst
bodyshop { bodyshop {
id id
md_ro_statuses
md_responsibility_centers md_responsibility_centers
accountingconfig accountingconfig
cdk_dealerid cdk_dealerid
cdk_configuration
} }
owner { owner {
accountingid accountingid
@@ -951,106 +954,116 @@ affected_rows
`; `;
exports.GET_CDK_ALLOCATIONS = ` exports.GET_CDK_ALLOCATIONS = `query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
query QUERY_JOB_CLOSE_DETAILS($id: uuid!) { jobs_by_pk(id: $id) {
jobs_by_pk(id: $id) { bodyshop {
bodyshop{
id
md_responsibility_centers
}
ro_number
invoice_allocation
ins_co_id
id id
ded_amt md_responsibility_centers
ded_status cdk_configuration
depreciation_taxes }
other_amount_payable ro_number
towing_payable invoice_allocation
storage_payable ins_co_id
adjustment_bottom_line id
federal_tax_rate ded_amt
state_tax_rate ded_status
local_tax_rate depreciation_taxes
tax_tow_rt other_amount_payable
tax_str_rt towing_payable
tax_paint_mat_rt storage_payable
tax_sub_rt adjustment_bottom_line
tax_lbr_rt federal_tax_rate
tax_levies_rt state_tax_rate
parts_tax_rates local_tax_rate
job_totals tax_tow_rt
rate_la1 tax_str_rt
rate_la2 tax_paint_mat_rt
rate_la3 tax_sub_rt
rate_la4 tax_lbr_rt
rate_laa tax_levies_rt
rate_lab parts_tax_rates
rate_lad job_totals
rate_lae rate_la1
rate_laf rate_la2
rate_lag rate_la3
rate_lam rate_la4
rate_lar rate_laa
rate_las rate_lab
rate_lau rate_lad
rate_ma2s rate_lae
rate_ma2t rate_laf
rate_ma3s rate_lag
rate_mabl rate_lam
rate_macs rate_lar
rate_mahw rate_las
rate_mapa rate_lau
rate_mash rate_ma2s
rate_matd rate_ma2t
status rate_ma3s
date_exported rate_mabl
date_invoiced rate_macs
voided rate_mahw
scheduled_completion rate_mapa
actual_completion rate_mash
scheduled_delivery rate_matd
actual_delivery status
scheduled_in date_exported
actual_in date_invoiced
bills { voided
id scheduled_completion
federal_tax_rate actual_completion
local_tax_rate scheduled_delivery
state_tax_rate actual_delivery
is_credit_memo scheduled_in
billlines { actual_in
actual_cost timetickets {
cost_center id
id actualhrs
quantity cost_center
applicable_taxes productivehrs
} rate
} employee {
joblines(where: { removed: { _eq: false } }) { flat_rate
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
} }
} }
bills(where: {isinhouse: {_eq: false}}) {
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
}
} }
}
`; `;
exports.GET_QBO_AUTH = `query GET_QBO_AUTH($email: String!) { exports.GET_QBO_AUTH = `query GET_QBO_AUTH($email: String!) {
@@ -1066,3 +1079,29 @@ exports.SET_QBO_AUTH = `mutation SET_QBO_AUTH($email: String!, $qbo_auth: jsonb!
} }
} }
`; `;
exports.MARK_JOB_EXPORTED = `
mutation MARK_JOB_EXPORTED($jobId: uuid!, $job: jobs_set_input!, $log: exportlog_insert_input!) {
update_jobs(where: {id: {_eq: $jobId}}, _set: $job) {
returning {
id
date_exported
status
alt_transport
ro_number
production_vars
lbr_adjustments
}
}
insert_exportlog_one(object: $log) {
id
}
}
`;
exports.INSERT_EXPORT_LOG = `
mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) {
insert_exportlog_one(object: $log) {
id
}
}
`;

View File

@@ -50,7 +50,11 @@ io.on("connection", (socket) => {
socket.on("set-log-level", (level) => { socket.on("set-log-level", (level) => {
socket.log_level = level; socket.log_level = level;
createLogEvent(socket, "DEBUG", `Updated log level to ${level}`); socket.emit("log-event", {
timestamp: new Date(),
level: "INFO",
message: `Updated log level to ${level}`,
});
}); });
socket.on("cdk-export-job", (jobid) => { socket.on("cdk-export-job", (jobid) => {