IO-233 Resolv merge issues & move allocations to ws

This commit is contained in:
Patrick Fic
2021-08-10 14:30:08 -07:00
parent e78e628bec
commit 6b64499e24
9 changed files with 431 additions and 104 deletions

View File

@@ -18520,6 +18520,137 @@
</translation>
</translations>
</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>
<name>driveable</name>
<definition_loaded>false</definition_loaded>

View File

@@ -1,11 +1,9 @@
import { Table } from "antd";
import React, { useMemo } from "react";
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";
import { useTranslation } from "react-i18next";
import _ from "lodash";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
@@ -18,117 +16,41 @@ export default connect(
mapDispatchToProps
)(DmsAllocationsSummary);
export function DmsAllocationsSummary({ bodyshop, job }) {
export function DmsAllocationsSummary({ socket, bodyshop, jobId }) {
const { t } = useTranslation();
const allocationsSummary = useMemo(() => {
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();
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100),
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1)
);
return null;
});
return bill_acc;
}, {});
console.log(
"🚀 ~ file: dms-allocations-summary.component.jsx ~ line 69 ~ costCenterHash",
costCenterHash
);
return _.union(
Object.keys(profitCenterHash),
Object.keys(costCenterHash)
).map((key) => {
console.log("Key", 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].toFormat()
: Dinero().toFormat(),
cost: costCenterHash[key]
? costCenterHash[key].toFormat()
: Dinero().toFormat(),
profitCenter,
costCenter,
};
});
}, [job, bodyshop.md_responsibility_centers]);
const [allocationsSummary, setAllocationsSummary] = useState([]);
const columns = [
{
title: t("job.fields.dms.center"),
title: t("jobs.fields.dms.center"),
dataIndex: "center",
key: "center",
},
{
title: t("job.fields.dms.sale"),
title: t("jobs.fields.dms.sale"),
dataIndex: "sale",
key: "sale",
},
{
title: t("job.fields.dms.cost"),
title: t("jobs.fields.dms.cost"),
dataIndex: "cost",
key: "cost",
},
{
title: t("job.fields.dms.sale_dms_acctnumber"),
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("job.fields.dms.cost_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("job.fields.dms.dms_wip_acctnumber"),
title: t("jobs.fields.dms.dms_wip_acctnumber"),
dataIndex: "dms_wip_acctnumber",
key: "dms_wip_acctnumber",
render: (text, record) =>
@@ -138,6 +60,17 @@ export function DmsAllocationsSummary({ bodyshop, job }) {
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"

View File

@@ -1,5 +1,5 @@
import { useQuery } from "@apollo/client";
import { Button, Result, Select, Space } from "antd";
import { Button, Col, Result, Row, Select, Space } from "antd";
import queryString from "query-string";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -46,14 +46,14 @@ export const socket = SocketIO(
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
const [logLevel, setLogLevel] = useState("DEBUG");
const [logLevel, setLogLevel] = useState("TRACE");
const [logs, setLogs] = useState([]);
const search = queryString.parse(useLocation().search);
const { jobId } = search;
const { loading, error, data } = useQuery(QUERY_JOB_EXPORT_DMS, {
variables: { id: jobId },
skip: !jobId,
skip: true, //!jobId,
});
useEffect(() => {
@@ -129,21 +129,24 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
socket.emit("set-log-level", value);
}}
>
<Select.Option>TRACE</Select.Option>
<Select.Option>DEBUG</Select.Option>
<Select.Option>INFO</Select.Option>
<Select.Option>WARNING</Select.Option>
<Select.Option>ERROR</Select.Option>
<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>
<div>
<DmsAllocationsSummary
socket={socket}
job={data ? data.jobs_by_pk : null}
/>
<DmsLogEvents socket={socket} logs={logs} />
</div>
<Row gutter={32}>
<Col span={18}>
<DmsAllocationsSummary socket={socket} jobId={jobId} />
</Col>
<Col span={6}>
<div style={{ maxHeight: "500px", overflowY: "auto" }}>
<DmsLogEvents socket={socket} logs={logs} />
</div>
</Col>
</Row>
<DmsCustomerSelector />
</div>

View File

@@ -1140,6 +1140,14 @@
"ded_amt": "Deductible",
"ded_status": "Deductible Status",
"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",
"employee_body": "Body",
"employee_csr": "Customer Service Rep.",

View File

@@ -1140,6 +1140,14 @@
"ded_amt": "Deducible",
"ded_status": "Estado deducible",
"depreciation_taxes": "Depreciación / Impuestos",
"dms": {
"center": "",
"cost": "",
"cost_dms_acctnumber": "",
"dms_wip_acctnumber": "",
"sale": "",
"sale_dms_acctnumber": ""
},
"driveable": "",
"employee_body": "",
"employee_csr": "Representante de servicio al cliente.",

View File

@@ -1140,6 +1140,14 @@
"ded_amt": "Déductible",
"ded_status": "Statut de franchise",
"depreciation_taxes": "Amortissement / taxes",
"dms": {
"center": "",
"cost": "",
"cost_dms_acctnumber": "",
"dms_wip_acctnumber": "",
"sale": "",
"sale_dms_acctnumber": ""
},
"driveable": "",
"employee_body": "",
"employee_csr": "représentant du service à la clientèle",

View File

@@ -0,0 +1,121 @@
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");
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 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();
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100),
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1)
);
return null;
});
return bill_acc;
}, {});
console.log(
"🚀 ~ file: dms-allocations-summary.component.jsx ~ line 69 ~ costCenterHash",
costCenterHash
);
return _.union(
Object.keys(profitCenterHash),
Object.keys(costCenterHash)
).map((key) => {
console.log("Key", 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].toFormat()
: Dinero().toFormat(),
cost: costCenterHash[key]
? costCenterHash[key].toFormat()
: Dinero().toFormat(),
profitCenter,
costCenter,
};
});
} 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;
}

View File

@@ -928,3 +928,104 @@ 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
}
}
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
}
}
}
`;

View File

@@ -9,6 +9,8 @@ require("dotenv").config({
const { io } = require("../../server");
const { admin } = require("../firebase/firebase-handler");
const CdkJobExport = require("../cdk/cdk-job-export").default;
const CdkCalculateAllocations =
require("../cdk/cdk-calculate-allocations").default;
const { isArray } = require("lodash");
const logger = require("../utils/logger");
@@ -60,6 +62,18 @@ io.on("connection", (socket) => {
//CdkJobExport(socket, jobid);
});
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", () => {
createLogEvent(socket, "DEBUG", `User disconnected.`);
});