diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index f8b337366..2d2352892 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -18520,6 +18520,137 @@ + + dms + + + center + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + cost + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + cost_dms_acctnumber + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + dms_wip_acctnumber + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + sale + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + sale_dms_acctnumber + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + + + driveable false diff --git a/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx b/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx index 4bd0050e2..65b9434b6 100644 --- a/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx +++ b/client/src/components/dms-allocations-summary/dms-allocations-summary.component.jsx @@ -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 ( ( + + )} pagination={{ position: "top", defaultPageSize: 50 }} columns={columns} rowKey="center" diff --git a/client/src/pages/dms/dms.container.jsx b/client/src/pages/dms/dms.container.jsx index ebc5100be..37c67fda9 100644 --- a/client/src/pages/dms/dms.container.jsx +++ b/client/src/pages/dms/dms.container.jsx @@ -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); }} > - TRACE - DEBUG - INFO - WARNING - ERROR + TRACE + DEBUG + INFO + WARNING + ERROR -
- - -
+ + + + + +
+ +
+ + diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 8f02fb73a..5d160c0d7 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -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.", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 841470e69..b66911cad 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -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.", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 0dd6abcba..435a10e71 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -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", diff --git a/server/cdk/cdk-calculate-allocations.js b/server/cdk/cdk-calculate-allocations.js new file mode 100644 index 000000000..f17682876 --- /dev/null +++ b/server/cdk/cdk-calculate-allocations.js @@ -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; +} diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index d9d66aeca..40ec9d851 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -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 + } + } + } +`; diff --git a/server/web-sockets/web-socket.js b/server/web-sockets/web-socket.js index 724fd7c73..61ef31557 100644 --- a/server/web-sockets/web-socket.js +++ b/server/web-sockets/web-socket.js @@ -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.`); });