diff --git a/admin/package-lock.json b/admin/package-lock.json index 1c9c766bc..c4ada889a 100644 --- a/admin/package-lock.json +++ b/admin/package-lock.json @@ -4040,6 +4040,15 @@ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", "optional": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -7153,6 +7162,12 @@ } } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", @@ -10752,6 +10767,12 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "optional": true + }, "nanoid": { "version": "3.1.16", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.16.tgz", @@ -16724,7 +16745,11 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", @@ -17326,7 +17351,11 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index bd8a11b7b..104262d2b 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -18265,6 +18265,27 @@ + + sales + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + state_tax_amt false diff --git a/client/src/components/job-costing-modal/job-costing-modal.component.jsx b/client/src/components/job-costing-modal/job-costing-modal.component.jsx index 4cb133fab..d15d7057a 100644 --- a/client/src/components/job-costing-modal/job-costing-modal.component.jsx +++ b/client/src/components/job-costing-modal/job-costing-modal.component.jsx @@ -1,10 +1,13 @@ +import { Typography } from "antd"; import Dinero from "dinero.js"; import React from "react"; +import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { selectBodyshop } from "../../redux/user/user.selectors"; import JobCostingPartsTable from "../job-costing-parts-table/job-costing-parts-table.component"; import JobCostingStatistics from "../job-costing-statistics/job-costing-statistics.component"; +import JobCostingPie from "./job-costing-modal.pie.component"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, @@ -16,7 +19,7 @@ const mapDispatchToProps = (dispatch) => ({ export function JobCostingModalComponent({ bodyshop, job }) { const defaultProfits = bodyshop.md_responsibility_centers.defaults.profits; // const defaultCosts = bodyshop.md_responsibility_centers.defaults.costs; - + const { t } = useTranslation(); const jobLineTotalsByProfitCenter = job && job.joblines.reduce( @@ -115,11 +118,11 @@ export function JobCostingModalComponent({ bodyshop, job }) { ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 }); const cost_parts = billTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 }); - const cost = (billTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })).add( - ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 }) - ); + const costs = ( + billTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 }) + ).add(ticketTotalsByProfitCenter[ccVal] || Dinero({ amount: 0 })); const totalSales = sale_labor.add(sale_parts); - const gpdollars = totalSales.subtract(cost); + const gpdollars = totalSales.subtract(costs); const gppercent = ( (gpdollars.getAmount() / totalSales.getAmount()) * 100 @@ -139,16 +142,19 @@ export function JobCostingModalComponent({ bodyshop, job }) { .add(sale_parts); summaryData.totalLaborCost = summaryData.totalLaborCost.add(cost_labor); summaryData.totalPartsCost = summaryData.totalPartsCost.add(cost_parts); - summaryData.totalCost = summaryData.totalCost.add(cost); + summaryData.totalCost = summaryData.totalCost.add(costs); return { id: idx, cost_center: ccVal, sale_labor: sale_labor && sale_labor.toFormat(), sale_parts: sale_parts && sale_parts.toFormat(), + sales: sale_labor.add(sale_parts).toFormat(), + sales_dinero: sale_labor.add(sale_parts), cost_parts: cost_parts && cost_parts.toFormat(), cost_labor: cost_labor && cost_labor.toFormat(), - cost: cost && cost.toFormat(), + costs: cost_parts.add(cost_labor).toFormat(), + costs_dinero: cost_parts.add(cost_labor), gpdollars: gpdollars.toFormat(), gppercent: gppercentFormatted, }; @@ -173,6 +179,18 @@ export function JobCostingModalComponent({ bodyshop, job }) { + + + + {t("jobs.labels.sales")} + + + + + {t("jobs.labels.cost")} + + + ); } diff --git a/client/src/components/job-costing-modal/job-costing-modal.pie.component.jsx b/client/src/components/job-costing-modal/job-costing-modal.pie.component.jsx new file mode 100644 index 000000000..8215e5eb1 --- /dev/null +++ b/client/src/components/job-costing-modal/job-costing-modal.pie.component.jsx @@ -0,0 +1,69 @@ +import React, { useCallback, useMemo } from "react"; +import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts"; + +export default function JobCostingPieComponent({ + type = "sales", + costCenterData, +}) { + const Calculatedata = useCallback( + (data) => { + if (data && data.length > 0) { + return data.reduce((acc, i) => { + const value = + type === "sales" + ? i.sales_dinero.getAmount() + : i.costs_dinero.getAmount(); + + if (value > 0) { + acc.push({ + name: i.cost_center, + color: "#" + Math.floor(Math.random() * 16777215).toString(16), + + label: `${i.cost_center} - ${ + type === "sales" + ? i.sales_dinero.toFormat() + : i.costs_dinero.toFormat() + }`, + value: + type === "sales" + ? i.sales_dinero.getAmount() + : i.costs_dinero.getAmount(), + }); + } + return acc; + }, []); + } else { + return []; + } + }, + [type] + ); + + const memoizedData = useMemo(() => Calculatedata(costCenterData), [ + costCenterData, + Calculatedata, + ]); + + console.log(type, memoizedData); + + return ( + + + entry.label} + labelLine + > + {memoizedData.map((entry, index) => ( + + ))} + + + + ); +} diff --git a/client/src/components/job-costing-parts-table/job-costing-parts-table.component.jsx b/client/src/components/job-costing-parts-table/job-costing-parts-table.component.jsx index fb81eb59a..894c427f5 100644 --- a/client/src/components/job-costing-parts-table/job-costing-parts-table.component.jsx +++ b/client/src/components/job-costing-parts-table/job-costing-parts-table.component.jsx @@ -24,37 +24,23 @@ export default function JobCostingPartsTable({ job, data }) { state.sortedInfo.columnKey === "cost_center" && state.sortedInfo.order, }, { - title: t("jobs.labels.sale_labor"), - dataIndex: "sale_labor", - key: "sale_labor", - sorter: (a, b) => alphaSort(a.sale_labor, b.sale_labor), + title: t("jobs.labels.sales"), + dataIndex: "sales", + key: "sales", + sorter: (a, b) => alphaSort(a.sales, b.sales), sortOrder: - state.sortedInfo.columnKey === "sale_labor" && state.sortedInfo.order, + state.sortedInfo.columnKey === "sales" && state.sortedInfo.order, }, + { - title: t("jobs.labels.sale_parts"), - dataIndex: "sale_parts", - key: "sale_parts", - sorter: (a, b) => alphaSort(a.sale_parts, b.sale_parts), + title: t("jobs.labels.costs"), + dataIndex: "costs", + key: "costs", + sorter: (a, b) => a.costs - b.costs, sortOrder: - state.sortedInfo.columnKey === "sale_parts" && state.sortedInfo.order, - }, - { - title: t("jobs.labels.cost_labor"), - dataIndex: "cost_labor", - key: "cost_labor", - sorter: (a, b) => a.cost_labor - b.cost_labor, - sortOrder: - state.sortedInfo.columnKey === "cost_labor" && state.sortedInfo.order, - }, - { - title: t("jobs.labels.cost_parts"), - dataIndex: "cost_parts", - key: "cost_parts", - sorter: (a, b) => a.cost_parts - b.cost_parts, - sortOrder: - state.sortedInfo.columnKey === "cost_parts" && state.sortedInfo.order, + state.sortedInfo.columnKey === "costs" && state.sortedInfo.order, }, + { title: t("jobs.labels.gpdollars"), dataIndex: "gpdollars", diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 0630d604a..0eb3b13f6 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1112,6 +1112,7 @@ "reconciliationheader": "Parts & Sublet Reconciliation", "sale_labor": "Sales - Labor", "sale_parts": "Sales - Parts", + "sales": "Sales", "state_tax_amt": "State/Provincial Taxes", "subletstotal": "Sublets Total", "subtotal": "Subtotal", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 9b713b47a..28b230099 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1112,6 +1112,7 @@ "reconciliationheader": "", "sale_labor": "", "sale_parts": "", + "sales": "", "state_tax_amt": "", "subletstotal": "", "subtotal": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 37aa460c7..8d28dca11 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1112,6 +1112,7 @@ "reconciliationheader": "", "sale_labor": "", "sale_parts": "", + "sales": "", "state_tax_amt": "", "subletstotal": "", "subtotal": "",