Merged in release/2022-02-11 (pull request #376)

Release/2022 02 11
This commit is contained in:
Patrick Fic
2022-02-09 23:52:27 +00:00
24 changed files with 343 additions and 100 deletions

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
@@ -25120,6 +25120,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>cost_Additional</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>cost_labor</name> <name>cost_labor</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -27014,6 +27035,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>sale_additional</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>sale_labor</name> <name>sale_labor</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -32085,6 +32127,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>part_type</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>quantity</name> <name>quantity</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -207,6 +207,8 @@ export function BillDetailEditcontainer({
cost: i.actual_cost, cost: i.actual_cost,
quantity: i.quantity, quantity: i.quantity,
joblineid: i.joblineid, joblineid: i.joblineid,
oem_partno: i.jobline && i.jobline.oem_partno,
part_type: i.jobline && i.jobline.part_type,
}; };
}), }),
isReturn: true, isReturn: true,

View File

@@ -216,7 +216,11 @@ function BillEnterModalContainer({
if (enterAgain) { if (enterAgain) {
form.resetFields(); form.resetFields();
form.setFieldsValue({ ...form.getFieldsValue(), billlines: [] }); form.resetFields();
form.setFieldsValue({
...formValues,
billlines: [],
});
} else { } else {
toggleModalVisible(); toggleModalVisible();
} }

View File

@@ -76,6 +76,7 @@ export function BillsListTableComponent({
quantity: i.quantity, quantity: i.quantity,
joblineid: i.joblineid, joblineid: i.joblineid,
oem_partno: i.jobline && i.jobline.oem_partno, oem_partno: i.jobline && i.jobline.oem_partno,
part_type: i.jobline && i.jobline.part_type,
}; };
}), }),
isReturn: true, isReturn: true,

View File

@@ -26,11 +26,6 @@ export function JobCostingModalContainer({
const { visible, context } = jobCostingModal; const { visible, context } = jobCostingModal;
const { jobId } = context; const { jobId } = context;
// const { loading, error, data } = useQuery(QUERY_JOB_COSTING_DETAILS, {
// variables: { id: jobId },
// skip: !jobId,
// });
useEffect(() => { useEffect(() => {
async function getData() { async function getData() {
if (jobId && visible) { if (jobId && visible) {
@@ -46,8 +41,14 @@ export function JobCostingModalContainer({
<Modal <Modal
visible={visible} visible={visible}
title={t("jobs.labels.jobcosting")} title={t("jobs.labels.jobcosting")}
onOk={() => toggleModalVisible()} onOk={() => {
onCancel={() => toggleModalVisible()} toggleModalVisible();
setCostingData(null);
}}
onCancel={() => {
toggleModalVisible();
setCostingData(null);
}}
cancelButtonProps={{ style: { display: "none" } }} cancelButtonProps={{ style: { display: "none" } }}
width="90%" width="90%"
destroyOnClose destroyOnClose

View File

@@ -16,6 +16,10 @@ export default function JobCostingStatistics({ summaryData }) {
value={Dinero(summaryData.totalPartsSales).toFormat()} value={Dinero(summaryData.totalPartsSales).toFormat()}
title={t("jobs.labels.sale_parts")} title={t("jobs.labels.sale_parts")}
/> />
<Statistic
value={Dinero(summaryData.totalAdditionalSales).toFormat()}
title={t("jobs.labels.sale_additional")}
/>
<Statistic <Statistic
value={Dinero(summaryData.totalSales).toFormat()} value={Dinero(summaryData.totalSales).toFormat()}
title={t("jobs.labels.total_sales")} title={t("jobs.labels.total_sales")}
@@ -28,6 +32,10 @@ export default function JobCostingStatistics({ summaryData }) {
value={Dinero(summaryData.totalPartsCost).toFormat()} value={Dinero(summaryData.totalPartsCost).toFormat()}
title={t("jobs.labels.cost_parts")} title={t("jobs.labels.cost_parts")}
/> />
<Statistic
value={Dinero(summaryData.totalAdditionalCost).toFormat()}
title={t("jobs.labels.cost_Additional")}
/>
<Statistic <Statistic
value={Dinero(summaryData.totalCost).toFormat()} value={Dinero(summaryData.totalCost).toFormat()}
title={t("jobs.labels.total_cost")} title={t("jobs.labels.total_cost")}

View File

@@ -319,6 +319,15 @@ export function PartsOrderListTableComponent({
}, },
] ]
: []), : []),
{
title: t("parts_orders.fields.part_type"),
dataIndex: "part_type",
key: "part_type",
render: (text, record) =>
record.part_type
? t(`joblines.fields.part_types.${record.part_type}`)
: null,
},
{ {
title: t("parts_orders.fields.oem_partno"), title: t("parts_orders.fields.oem_partno"),
dataIndex: "oem_partno", dataIndex: "oem_partno",

View File

@@ -1,6 +1,15 @@
import { DeleteFilled, WarningFilled } from "@ant-design/icons"; import { DeleteFilled, WarningFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { Divider, Form, Input, InputNumber, Radio, Space, Tag } from "antd"; import {
Divider,
Form,
Input,
InputNumber,
Radio,
Space,
Tag,
Select,
} from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -114,6 +123,45 @@ export function PartsOrderModalComponent({
> >
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item
label={t("parts_orders.fields.part_type")}
key={`${index}part_type`}
name={[field.name, "part_type"]}
>
<Select>
<Select.Option value="PAA">
{t("joblines.fields.part_types.PAA")}
</Select.Option>
<Select.Option value="PAC">
{t("joblines.fields.part_types.PAC")}
</Select.Option>
<Select.Option value="PAL">
{t("joblines.fields.part_types.PAL")}
</Select.Option>
<Select.Option value="PAG">
{t("joblines.fields.part_types.PAG")}
</Select.Option>
<Select.Option value="PAM">
{t("joblines.fields.part_types.PAM")}
</Select.Option>
<Select.Option value="PAP">
{t("joblines.fields.part_types.PAP")}
</Select.Option>
<Select.Option value="PAN">
{t("joblines.fields.part_types.PAN")}
</Select.Option>
<Select.Option value="PAO">
{t("joblines.fields.part_types.PAO")}
</Select.Option>
<Select.Option value="PAR">
{t("joblines.fields.part_types.PAR")}
</Select.Option>
<Select.Option value="PAS">
{t("joblines.fields.part_types.PAS")}
</Select.Option>
</Select>
</Form.Item>
<Form.Item <Form.Item
label={t("parts_orders.fields.oem_partno")} label={t("parts_orders.fields.oem_partno")}
key={`${index}oem_partno`} key={`${index}oem_partno`}

View File

@@ -77,7 +77,7 @@ export function PartsOrderModalContainer({
const { refetch } = actions; const { refetch } = actions;
const [form] = Form.useForm(); const [form] = Form.useForm();
const [saving, setSaving] = useState(false);
const sendTypeState = useState("e"); const sendTypeState = useState("e");
const sendType = sendTypeState[0]; const sendType = sendTypeState[0];
@@ -93,7 +93,7 @@ export function PartsOrderModalContainer({
const handleFinish = async (values) => { const handleFinish = async (values) => {
logImEXEvent("parts_order_insert"); logImEXEvent("parts_order_insert");
setSaving(true);
const insertResult = await insertPartOrder({ const insertResult = await insertPartOrder({
variables: { variables: {
po: [ po: [
@@ -247,8 +247,6 @@ export function PartsOrderModalContainer({
}); });
} }
console.log(partsOrder.data.parts_orders_by_pk);
const oecResponse = await axios.post( const oecResponse = await axios.post(
"http://localhost:1337/oec/", "http://localhost:1337/oec/",
@@ -275,11 +273,11 @@ export function PartsOrderModalContainer({
error: JSON.stringify(error.message), error: JSON.stringify(error.message),
}), }),
}); });
setSaving(false);
return; return;
} }
} }
setSaving(false);
toggleModalVisible(); toggleModalVisible();
}; };
@@ -301,6 +299,7 @@ export function PartsOrderModalContainer({
cost: value.cost, cost: value.cost,
quantity: value.part_qty, quantity: value.part_qty,
job_line_id: isReturn ? value.joblineid : value.id, job_line_id: isReturn ? value.joblineid : value.id,
part_type: value.part_type,
}); });
return acc; return acc;
}, []) }, [])
@@ -324,6 +323,8 @@ export function PartsOrderModalContainer({
} }
onCancel={() => toggleModalVisible()} onCancel={() => toggleModalVisible()}
onOk={() => form.submit()} onOk={() => form.submit()}
okButtonProps={{ loading: saving }}
cancelButtonProps={{ loading: saving }}
destroyOnClose destroyOnClose
width="75%" width="75%"
forceRender forceRender

View File

@@ -84,6 +84,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
line_remarks line_remarks
quantity quantity
job_line_id job_line_id
part_type
cost cost
jobline { jobline {
id id
@@ -123,6 +124,10 @@ export const QUERY_BILLS_BY_JOBID = gql`
applicable_taxes applicable_taxes
deductedfromlbr deductedfromlbr
lbr_adjustment lbr_adjustment
jobline{
oem_partno
part_type
}
} }
} }
} }
@@ -159,6 +164,10 @@ export const QUERY_BILL_BY_PK = gql`
cost_center cost_center
quantity quantity
joblineid joblineid
jobline{
oem_partno
part_type
}
applicable_taxes applicable_taxes
deductedfromlbr deductedfromlbr
lbr_adjustment lbr_adjustment

View File

@@ -69,6 +69,7 @@ export const QUERY_PARTS_ORDER_OEC = gql`
db_price db_price
line_desc line_desc
quantity quantity
part_type
} }
job { job {
bodyshop{ bodyshop{

View File

@@ -1486,6 +1486,7 @@
"closejob": "Close Job {{ro_number}}", "closejob": "Close Job {{ro_number}}",
"contracts": "CC Contracts", "contracts": "CC Contracts",
"cost": "Cost", "cost": "Cost",
"cost_Additional": "Cost - Additional",
"cost_labor": "Cost - Labor", "cost_labor": "Cost - Labor",
"cost_parts": "Cost - Parts", "cost_parts": "Cost - Parts",
"costs": "Costs", "costs": "Costs",
@@ -1585,6 +1586,7 @@
"relatedros": "Related ROs", "relatedros": "Related ROs",
"returntotals": "Return Totals", "returntotals": "Return Totals",
"rosaletotal": "RO Parts Total", "rosaletotal": "RO Parts Total",
"sale_additional": "Sales - Additional",
"sale_labor": "Sales - Labor", "sale_labor": "Sales - Labor",
"sale_parts": "Sales - Parts & Sublet", "sale_parts": "Sales - Parts & Sublet",
"sales": "Sales", "sales": "Sales",
@@ -1904,6 +1906,7 @@
"order_date": "Order Date", "order_date": "Order Date",
"order_number": "Order Number", "order_number": "Order Number",
"orderedby": "Ordered By", "orderedby": "Ordered By",
"part_type": "Type",
"quantity": "Qty.", "quantity": "Qty.",
"return": "Return", "return": "Return",
"status": "Status" "status": "Status"

View File

@@ -1486,6 +1486,7 @@
"closejob": "", "closejob": "",
"contracts": "", "contracts": "",
"cost": "", "cost": "",
"cost_Additional": "",
"cost_labor": "", "cost_labor": "",
"cost_parts": "", "cost_parts": "",
"costs": "", "costs": "",
@@ -1585,6 +1586,7 @@
"relatedros": "", "relatedros": "",
"returntotals": "", "returntotals": "",
"rosaletotal": "", "rosaletotal": "",
"sale_additional": "",
"sale_labor": "", "sale_labor": "",
"sale_parts": "", "sale_parts": "",
"sales": "", "sales": "",
@@ -1904,6 +1906,7 @@
"order_date": "", "order_date": "",
"order_number": "", "order_number": "",
"orderedby": "", "orderedby": "",
"part_type": "",
"quantity": "", "quantity": "",
"return": "", "return": "",
"status": "" "status": ""

View File

@@ -1486,6 +1486,7 @@
"closejob": "", "closejob": "",
"contracts": "", "contracts": "",
"cost": "", "cost": "",
"cost_Additional": "",
"cost_labor": "", "cost_labor": "",
"cost_parts": "", "cost_parts": "",
"costs": "", "costs": "",
@@ -1585,6 +1586,7 @@
"relatedros": "", "relatedros": "",
"returntotals": "", "returntotals": "",
"rosaletotal": "", "rosaletotal": "",
"sale_additional": "",
"sale_labor": "", "sale_labor": "",
"sale_parts": "", "sale_parts": "",
"sales": "", "sales": "",
@@ -1904,6 +1906,7 @@
"order_date": "", "order_date": "",
"order_number": "", "order_number": "",
"orderedby": "", "orderedby": "",
"part_type": "",
"quantity": "", "quantity": "",
"return": "", "return": "",
"status": "" "status": ""

View File

@@ -815,6 +815,7 @@
- email - email
- enforce_class - enforce_class
- enforce_referral - enforce_referral
- entegral_configuration
- entegral_id - entegral_id
- features - features
- federal_tax_id - federal_tax_id

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "entegral_configuration" jsonb
-- null default jsonb_build_array();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "entegral_configuration" jsonb
null default jsonb_build_array();

View File

@@ -0,0 +1 @@
alter table "public"."bodyshops" alter column "entegral_configuration" set default jsonb_build_array();

View File

@@ -0,0 +1 @@
alter table "public"."bodyshops" alter column "entegral_configuration" set default jsonb_build_object();

View File

@@ -0,0 +1 @@
ALTER TABLE "public"."bodyshops" ALTER COLUMN "timezone" drop default;

View File

@@ -0,0 +1 @@
alter table "public"."bodyshops" alter column "timezone" set default 'America/Vancouver';

View File

@@ -123,12 +123,12 @@ exports.default = async (req, res) => {
} }
} }
// for (const xmlObj of allxmlsToUpload) { for (const xmlObj of allxmlsToUpload) {
// fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml); fs.writeFileSync(`./logs/${xmlObj.filename}`, xmlObj.xml);
// } }
// res.json(allxmlsToUpload); res.json(allxmlsToUpload);
// return; return;
let sftp = new Client(); let sftp = new Client();
sftp.on("error", (errors) => sftp.on("error", (errors) =>
@@ -227,7 +227,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
InsuredorClaimantFlag: null, InsuredorClaimantFlag: null,
}, },
VehicleInformation: { VehicleInformation: {
Year: job.v_model_yr || "", Year: parseInt(job.v_model_yr.match(/\d/g).join(""), 10) || "",
Make: job.v_make_desc || "", Make: job.v_make_desc || "",
Model: job.v_model_desc || "", Model: job.v_model_desc || "",
VIN: job.v_vin || "", VIN: job.v_vin || "",
@@ -575,10 +575,12 @@ const CreateRepairOrderTag = (job, errorCallback) => {
.add(Dinero(job.job_totals.totals.federal_tax)) .add(Dinero(job.job_totals.totals.federal_tax))
.toFormat(AHDineroFormat), .toFormat(AHDineroFormat),
SalesTaxTotalCost: 0, SalesTaxTotalCost: 0,
GrossTotal: Dinero(job.job_totals.totals.net_repairs).toFormat( GrossTotal: Dinero(job.job_totals.totals.total_repairs).toFormat(
AHDineroFormat AHDineroFormat
), ),
DeductibleTotal: job.ded_amt || 0, DeductibleTotal: Dinero({
amount: Math.round((job.ded_amt || 0) * 100),
}).toFormat(AHDineroFormat),
DepreciationTotal: Dinero( DepreciationTotal: Dinero(
job.job_totals.totals.custPayable.dep_taxes job.job_totals.totals.custPayable.dep_taxes
).toFormat(AHDineroFormat), ).toFormat(AHDineroFormat),
@@ -588,7 +590,9 @@ const CreateRepairOrderTag = (job, errorCallback) => {
CustomerPay: Dinero(job.job_totals.totals.custPayable.total).toFormat( CustomerPay: Dinero(job.job_totals.totals.custPayable.total).toFormat(
AHDineroFormat AHDineroFormat
), ),
InsurancePay: 0, InsurancePay: Dinero(job.job_totals.totals.total_repairs)
.subtract(Dinero(job.job_totals.totals.custPayable))
.toFormat(AHDineroFormat),
Deposit: 0, Deposit: 0,
AmountDue: 0, AmountDue: 0,
}, },
@@ -634,7 +638,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
DetailLine: DetailLine:
job.joblines.length > 0 job.joblines.length > 0
? job.joblines.map((jl) => ? job.joblines.map((jl) =>
GenerateDetailLines(jl, job.bodyshop.md_order_statuses) GenerateDetailLines(job, jl, job.bodyshop.md_order_statuses)
) )
: [generateNullDetailLine()], : [generateNullDetailLine()],
}, },
@@ -718,7 +722,10 @@ const CreateCosts = (job) => {
return { return {
PartsTotalCost: Object.keys(billTotalsByCostCenters).reduce((acc, key) => { PartsTotalCost: Object.keys(billTotalsByCostCenters).reduce((acc, key) => {
return acc.add(billTotalsByCostCenters[key]); if (key !== defaultCosts.PAS && key !== defaultCosts.PASL)
return acc.add(billTotalsByCostCenters[key]);
return acc;
}, Dinero()), }, Dinero()),
PartsOemCost: (billTotalsByCostCenters[defaultCosts.PAN] || Dinero()).add( PartsOemCost: (billTotalsByCostCenters[defaultCosts.PAN] || Dinero()).add(
billTotalsByCostCenters[defaultCosts.PAP] || Dinero() billTotalsByCostCenters[defaultCosts.PAP] || Dinero()
@@ -728,7 +735,9 @@ const CreateCosts = (job) => {
billTotalsByCostCenters[defaultCosts.PAM] || Dinero(), billTotalsByCostCenters[defaultCosts.PAM] || Dinero(),
PartsRecycledCost: billTotalsByCostCenters[defaultCosts.PAL] || Dinero(), PartsRecycledCost: billTotalsByCostCenters[defaultCosts.PAL] || Dinero(),
PartsOtherCost: billTotalsByCostCenters[defaultCosts.PAO] || Dinero(), PartsOtherCost: billTotalsByCostCenters[defaultCosts.PAO] || Dinero(),
SubletTotalCost: billTotalsByCostCenters[defaultCosts.PAS] || Dinero(), SubletTotalCost:
billTotalsByCostCenters[defaultCosts.PAS] ||
Dinero(billTotalsByCostCenters[defaultCosts.PASL] || Dinero()),
BodyLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAB] || Dinero(), BodyLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAB] || Dinero(),
RefinishLaborTotalCost: RefinishLaborTotalCost:
ticketTotalsByCostCenter[defaultCosts.LAR] || Dinero(), ticketTotalsByCostCenter[defaultCosts.LAR] || Dinero(),
@@ -781,10 +790,15 @@ const StatusMapping = (status, md_ro_statuses) => {
// default: return "UNDEFINED" // default: return "UNDEFINED"
}; };
const GenerateDetailLines = (line, statuses) => { const GenerateDetailLines = (job, line, statuses) => {
const ret = { const ret = {
BackOrdered: line.status === statuses.default_bo ? "1" : "0", BackOrdered: line.status === statuses.default_bo ? "1" : "0",
Cost: (line.billlines[0] && line.billlines[0].actual_cost.toFixed(2)) || 0, Cost:
(line.billlines[0] &&
(line.billlines[0].actual_cost * line.billlines[0].quantity).toFixed(
2
)) ||
0,
//Critical: null, //Critical: null,
Description: line.line_desc || "", Description: line.line_desc || "",
DiscountMarkup: line.prt_dsmk_m || 0, DiscountMarkup: line.prt_dsmk_m || 0,
@@ -811,11 +825,7 @@ const GenerateDetailLines = (line, statuses) => {
Vendor: (line.billlines[0] && line.billlines[0].bill.vendor.name) || "", Vendor: (line.billlines[0] && line.billlines[0].bill.vendor.name) || "",
VendorPaid: null, VendorPaid: null,
VendorPrice: VendorPrice:
(line.billlines[0] && (line.billlines[0] && line.billlines[0].actual_price.toFixed(2)) || 0,
(line.billlines[0].actual_price * line.billlines[0].quantity).toFixed(
2
)) ||
0,
Deleted: null, Deleted: null,
ExpectedOn: null, ExpectedOn: null,
ReceivedOn: ReceivedOn:

View File

@@ -639,7 +639,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
job_totals job_totals
driveable driveable
parts_tax_rates parts_tax_rates
ded_amt
joblines(where: {removed: {_eq: false}}) { joblines(where: {removed: {_eq: false}}) {
id id
line_no line_no

View File

@@ -62,9 +62,11 @@ async function JobCostingMulti(req, res) {
summaryData: { summaryData: {
totalLaborSales: Dinero({ amount: 0 }), totalLaborSales: Dinero({ amount: 0 }),
totalPartsSales: Dinero({ amount: 0 }), totalPartsSales: Dinero({ amount: 0 }),
totalAdditionalSales: Dinero({ amount: 0 }),
totalSales: Dinero({ amount: 0 }), totalSales: Dinero({ amount: 0 }),
totalLaborCost: Dinero({ amount: 0 }), totalLaborCost: Dinero({ amount: 0 }),
totalPartsCost: Dinero({ amount: 0 }), totalPartsCost: Dinero({ amount: 0 }),
totalAdditionalCost: Dinero({ amount: 0 }),
totalCost: Dinero({ amount: 0 }), totalCost: Dinero({ amount: 0 }),
gpdollars: Dinero({ amount: 0 }), gpdollars: Dinero({ amount: 0 }),
gppercent: null, gppercent: null,
@@ -102,12 +104,18 @@ async function JobCostingMulti(req, res) {
sale_parts_dinero: multiSummary.costCenterData[ sale_parts_dinero: multiSummary.costCenterData[
CostCenterIndex CostCenterIndex
].sale_parts_dinero.add(c.sale_parts_dinero), ].sale_parts_dinero.add(c.sale_parts_dinero),
sale_additional_dinero: multiSummary.costCenterData[
CostCenterIndex
].sale_additional_dinero.add(c.sale_additional_dinero),
cost_labor_dinero: multiSummary.costCenterData[ cost_labor_dinero: multiSummary.costCenterData[
CostCenterIndex CostCenterIndex
].cost_labor_dinero.add(c.cost_labor_dinero), ].cost_labor_dinero.add(c.cost_labor_dinero),
cost_parts_dinero: multiSummary.costCenterData[ cost_parts_dinero: multiSummary.costCenterData[
CostCenterIndex CostCenterIndex
].cost_parts_dinero.add(c.cost_parts_dinero), ].cost_parts_dinero.add(c.cost_parts_dinero),
cost_additional_dinero: multiSummary.costCenterData[
CostCenterIndex
].cost_additional_dinero.add(c.cost_additional_dinero),
gpdollars_dinero: multiSummary.costCenterData[ gpdollars_dinero: multiSummary.costCenterData[
CostCenterIndex CostCenterIndex
].gpdollars_dinero.add(c.gpdollars_dinero), ].gpdollars_dinero.add(c.gpdollars_dinero),
@@ -129,6 +137,10 @@ async function JobCostingMulti(req, res) {
multiSummary.summaryData.totalPartsSales.add( multiSummary.summaryData.totalPartsSales.add(
costingData.summaryData.totalPartsSales costingData.summaryData.totalPartsSales
); );
multiSummary.summaryData.totalAdditionalSales =
multiSummary.summaryData.totalAdditionalSales.add(
costingData.summaryData.totalAdditionalSales
);
multiSummary.summaryData.totalSales = multiSummary.summaryData.totalSales =
multiSummary.summaryData.totalSales.add( multiSummary.summaryData.totalSales.add(
costingData.summaryData.totalSales costingData.summaryData.totalSales
@@ -145,6 +157,10 @@ async function JobCostingMulti(req, res) {
multiSummary.summaryData.totalPartsCost.add( multiSummary.summaryData.totalPartsCost.add(
costingData.summaryData.totalPartsCost costingData.summaryData.totalPartsCost
); );
multiSummary.summaryData.totalAdditionalCost =
multiSummary.summaryData.totalAdditionalCost.add(
costingData.summaryData.totalAdditionalCost
);
multiSummary.summaryData.totalCost = multiSummary.summaryData.totalCost =
multiSummary.summaryData.totalCost.add( multiSummary.summaryData.totalCost.add(
costingData.summaryData.totalCost costingData.summaryData.totalCost
@@ -201,10 +217,14 @@ async function JobCostingMulti(req, res) {
...c, ...c,
sale_labor: c.sale_labor_dinero && c.sale_labor_dinero.toFormat(), sale_labor: c.sale_labor_dinero && c.sale_labor_dinero.toFormat(),
sale_parts: c.sale_parts_dinero && c.sale_parts_dinero.toFormat(), sale_parts: c.sale_parts_dinero && c.sale_parts_dinero.toFormat(),
sales: c.sale_labor_dinero.add(c.sale_parts_dinero).toFormat(), sale_additional:
c.sale_additional_dinero && c.sale_additional_dinero.toFormat(),
sales: c.sales_dinero.toFormat(),
cost_parts: c.cost_parts_dinero && c.cost_parts_dinero.toFormat(), cost_parts: c.cost_parts_dinero && c.cost_parts_dinero.toFormat(),
cost_labor: c.cost_labor_dinero && c.cost_labor_dinero.toFormat(), cost_labor: c.cost_labor_dinero && c.cost_labor_dinero.toFormat(),
costs: c.cost_parts_dinero.add(c.cost_labor_dinero).toFormat(), cost_additional:
c.cost_additional_dinero && c.cost_additional_dinero.toFormat(),
costs: c.costs_dinero.toFormat(),
gpdollars: c.gpdollars_dinero.toFormat(), gpdollars: c.gpdollars_dinero.toFormat(),
gppercent: formatGpPercent( gppercent: formatGpPercent(
( (
@@ -340,33 +360,33 @@ function GenerateCostingData(job) {
.multiply(val.prt_dsmk_p > 0 ? 1 : -1) .multiply(val.prt_dsmk_p > 0 ? 1 : -1)
); );
if (!acc.parts[partsProfitCenter]) if (!acc.additional[partsProfitCenter])
acc.parts[partsProfitCenter] = Dinero(); acc.additional[partsProfitCenter] = Dinero();
acc.parts[partsProfitCenter] = acc.additional[partsProfitCenter] =
acc.parts[partsProfitCenter].add(partsAmount); acc.additional[partsProfitCenter].add(partsAmount);
} }
} }
return acc; return acc;
}, },
{ parts: {}, labor: {} } { parts: {}, labor: {}, additional: {} }
); );
if (!hasMapaLine) { if (!hasMapaLine) {
if (!jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]]) if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]])
jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]] = Dinero(); jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = Dinero();
jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]] = jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] =
jobLineTotalsByProfitCenter.parts[defaultProfits["MAPA"]].add( jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]].add(
Dinero({ Dinero({
amount: Math.round((job.rate_mapa || 0) * 100), amount: Math.round((job.rate_mapa || 0) * 100),
}).multiply(materialsHours.mapaHrs || 0) }).multiply(materialsHours.mapaHrs || 0)
); );
} }
if (!hasMashLine) { if (!hasMashLine) {
if (!jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]]) if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]])
jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]] = Dinero(); jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] = Dinero();
jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]] = jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] =
jobLineTotalsByProfitCenter.parts[defaultProfits["MASH"]].add( jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]].add(
Dinero({ Dinero({
amount: Math.round((job.rate_mash || 0) * 100), amount: Math.round((job.rate_mash || 0) * 100),
}).multiply(materialsHours.mashHrs || 0) }).multiply(materialsHours.mashHrs || 0)
@@ -381,54 +401,88 @@ function GenerateCostingData(job) {
)) || )) ||
job.bodyshop.md_responsibility_centers.defaults; job.bodyshop.md_responsibility_centers.defaults;
const billTotalsByCostCenters = job.bills.reduce((bill_acc, bill_val) => { const billTotalsByCostCenters = job.bills.reduce(
//At the bill level. (bill_acc, bill_val) => {
bill_val.billlines.map((line_val) => { //At the bill level.
//At the bill line level. bill_val.billlines.map((line_val) => {
if (job.bodyshop.pbs_serialnumber || job.bodyshop.cdk_dealerid) { //At the bill line level.
if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]]) if (job.bodyshop.pbs_serialnumber || job.bodyshop.cdk_dealerid) {
if (
!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]]
)
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
Dinero();
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
Dinero(); bill_acc[
selectedDmsAllocationConfig.costs[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)
);
} else {
const isAdditionalCostCenter =
// line_val.cost_center ===
// job.bodyshop.md_responsibility_centers.defaults.costs.PAS ||
// line_val.cost_center ===
// job.bodyshop.md_responsibility_centers.defaults.costs.PASL ||
line_val.cost_center ===
job.bodyshop.md_responsibility_centers.defaults.costs.TOW ||
line_val.cost_center ===
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA ||
line_val.cost_center ===
job.bodyshop.md_responsibility_centers.defaults.costs.MASH;
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = if (isAdditionalCostCenter) {
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add( if (!bill_acc.additionalCosts[line_val.cost_center])
Dinero({ bill_acc.additionalCosts[line_val.cost_center] = Dinero();
amount: Math.round((line_val.actual_cost || 0) * 100),
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1)
);
} else {
if (!bill_acc[line_val.cost_center])
bill_acc[line_val.cost_center] = Dinero();
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add( bill_acc.additionalCosts[line_val.cost_center] =
Dinero({ bill_acc.additionalCosts[line_val.cost_center].add(
amount: Math.round((line_val.actual_cost || 0) * 100), Dinero({
}) amount: Math.round((line_val.actual_cost || 0) * 100),
.multiply(line_val.quantity) })
.multiply(bill_val.is_credit_memo ? -1 : 1) .multiply(line_val.quantity)
); .multiply(bill_val.is_credit_memo ? -1 : 1)
} );
} else {
if (!bill_acc[line_val.cost_center])
bill_acc[line_val.cost_center] = Dinero();
return null; bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
}); Dinero({
return bill_acc; 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;
},
{ additionalCosts: {} }
);
//If the hourly rates for job costing are set, add them in. //If the hourly rates for job costing are set, add them in.
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mapa) { if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mapa) {
if ( if (
!billTotalsByCostCenters[ !billTotalsByCostCenters.additionalCosts[
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
] ]
) )
billTotalsByCostCenters[ billTotalsByCostCenters.additionalCosts[
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
] = Dinero(); ] = Dinero();
billTotalsByCostCenters[ billTotalsByCostCenters.additionalCosts[
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
] = billTotalsByCostCenters[ ] = billTotalsByCostCenters.additionalCosts[
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
].add( ].add(
Dinero({ Dinero({
@@ -442,16 +496,16 @@ function GenerateCostingData(job) {
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mash) { if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mash) {
if ( if (
!billTotalsByCostCenters[ !billTotalsByCostCenters.additionalCosts[
job.bodyshop.md_responsibility_centers.defaults.costs.MASH job.bodyshop.md_responsibility_centers.defaults.costs.MASH
] ]
) )
billTotalsByCostCenters[ billTotalsByCostCenters.additionalCosts[
job.bodyshop.md_responsibility_centers.defaults.costs.MASH job.bodyshop.md_responsibility_centers.defaults.costs.MASH
] = Dinero(); ] = Dinero();
billTotalsByCostCenters[ billTotalsByCostCenters.additionalCosts[
job.bodyshop.md_responsibility_centers.defaults.costs.MASH job.bodyshop.md_responsibility_centers.defaults.costs.MASH
] = billTotalsByCostCenters[ ] = billTotalsByCostCenters.additionalCosts[
job.bodyshop.md_responsibility_centers.defaults.costs.MASH job.bodyshop.md_responsibility_centers.defaults.costs.MASH
].add( ].add(
Dinero({ Dinero({
@@ -511,9 +565,11 @@ function GenerateCostingData(job) {
const summaryData = { const summaryData = {
totalLaborSales: Dinero({ amount: 0 }), totalLaborSales: Dinero({ amount: 0 }),
totalPartsSales: Dinero({ amount: 0 }), totalPartsSales: Dinero({ amount: 0 }),
totalAdditionalSales: Dinero({ amount: 0 }),
totalSales: Dinero({ amount: 0 }), totalSales: Dinero({ amount: 0 }),
totalLaborCost: Dinero({ amount: 0 }), totalLaborCost: Dinero({ amount: 0 }),
totalPartsCost: Dinero({ amount: 0 }), totalPartsCost: Dinero({ amount: 0 }),
totalAdditionalCost: Dinero({ amount: 0 }),
totalCost: Dinero({ amount: 0 }), totalCost: Dinero({ amount: 0 }),
totalLaborGp: Dinero({ amount: 0 }), totalLaborGp: Dinero({ amount: 0 }),
totalPartsGp: Dinero({ amount: 0 }), totalPartsGp: Dinero({ amount: 0 }),
@@ -533,14 +589,16 @@ function GenerateCostingData(job) {
jobLineTotalsByProfitCenter.labor[ccVal] || Dinero({ amount: 0 }); jobLineTotalsByProfitCenter.labor[ccVal] || Dinero({ amount: 0 });
const sale_parts = const sale_parts =
jobLineTotalsByProfitCenter.parts[ccVal] || Dinero({ amount: 0 }); jobLineTotalsByProfitCenter.parts[ccVal] || Dinero({ amount: 0 });
const sale_additional =
jobLineTotalsByProfitCenter.additional[ccVal] || Dinero({ amount: 0 });
const cost_labor = ticketTotalsByCostCenter[ccVal] || Dinero({ amount: 0 }); const cost_labor = ticketTotalsByCostCenter[ccVal] || Dinero({ amount: 0 });
const cost_parts = billTotalsByCostCenters[ccVal] || Dinero({ amount: 0 }); const cost_parts = billTotalsByCostCenters[ccVal] || Dinero({ amount: 0 });
const cost_additional =
billTotalsByCostCenters.additionalCosts[ccVal] || Dinero({ amount: 0 });
const costs = (billTotalsByCostCenters[ccVal] || Dinero({ amount: 0 })).add( const costs = cost_labor.add(cost_parts).add(cost_additional);
ticketTotalsByCostCenter[ccVal] || Dinero({ amount: 0 }) const totalSales = sale_labor.add(sale_parts).add(sale_additional);
);
const totalSales = sale_labor.add(sale_parts);
const gpdollars = totalSales.subtract(costs); const gpdollars = totalSales.subtract(costs);
const gppercent = ( const gppercent = (
(gpdollars.getAmount() / totalSales.getAmount()) * (gpdollars.getAmount() / totalSales.getAmount()) *
@@ -550,11 +608,13 @@ function GenerateCostingData(job) {
//Push summary data to avoid extra loop. //Push summary data to avoid extra loop.
summaryData.totalLaborSales = summaryData.totalLaborSales.add(sale_labor); summaryData.totalLaborSales = summaryData.totalLaborSales.add(sale_labor);
summaryData.totalPartsSales = summaryData.totalPartsSales.add(sale_parts); summaryData.totalPartsSales = summaryData.totalPartsSales.add(sale_parts);
summaryData.totalSales = summaryData.totalSales summaryData.totalAdditionalSales =
.add(sale_labor) summaryData.totalAdditionalSales.add(sale_additional);
.add(sale_parts); summaryData.totalSales = summaryData.totalSales.add(totalSales);
summaryData.totalLaborCost = summaryData.totalLaborCost.add(cost_labor); summaryData.totalLaborCost = summaryData.totalLaborCost.add(cost_labor);
summaryData.totalPartsCost = summaryData.totalPartsCost.add(cost_parts); summaryData.totalPartsCost = summaryData.totalPartsCost.add(cost_parts);
summaryData.totalAdditionalCost =
summaryData.totalAdditionalCost.add(cost_additional);
summaryData.totalCost = summaryData.totalCost.add(costs); summaryData.totalCost = summaryData.totalCost.add(costs);
return { return {
@@ -564,14 +624,18 @@ function GenerateCostingData(job) {
sale_labor_dinero: sale_labor, sale_labor_dinero: sale_labor,
sale_parts: sale_parts && sale_parts.toFormat(), sale_parts: sale_parts && sale_parts.toFormat(),
sale_parts_dinero: sale_parts, sale_parts_dinero: sale_parts,
sales: sale_labor.add(sale_parts).toFormat(), sale_additional: sale_additional && sale_additional.toFormat(),
sales_dinero: sale_labor.add(sale_parts), sale_additional_dinero: sale_additional,
sales: totalSales.toFormat(),
sales_dinero: totalSales,
cost_parts: cost_parts && cost_parts.toFormat(), cost_parts: cost_parts && cost_parts.toFormat(),
cost_parts_dinero: cost_parts, cost_parts_dinero: cost_parts,
cost_labor: cost_labor && cost_labor.toFormat(), cost_labor: cost_labor && cost_labor.toFormat(),
cost_labor_dinero: cost_labor, cost_labor_dinero: cost_labor,
costs: cost_parts.add(cost_labor).toFormat(), cost_additional: cost_additional && cost_additional.toFormat(),
costs_dinero: cost_parts.add(cost_labor), cost_additional_dinero: cost_additional,
costs: costs.toFormat(),
costs_dinero: costs,
gpdollars_dinero: gpdollars, gpdollars_dinero: gpdollars,
gpdollars: gpdollars.toFormat(), gpdollars: gpdollars.toFormat(),
gppercent: formatGpPercent(gppercent), gppercent: formatGpPercent(gppercent),
@@ -675,6 +739,8 @@ const getAdditionalCostCenter = (jl, profitCenters) => {
return profitCenters["MAPA"]; return profitCenters["MAPA"];
} else if (lineDesc.includes("ats amount")) { } else if (lineDesc.includes("ats amount")) {
return profitCenters["ATS"]; return profitCenters["ATS"];
} else if (lineDesc.includes("towing")) {
return profitCenters["TOW"];
} else { } else {
return null; return null;
} }