From 25bc343027f14947a1b18d9e698f51c15e7866d8 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 17 Jan 2022 09:26:38 -0800 Subject: [PATCH 1/5] IO-1643 Add shop name to bread crumb. --- .../src/components/breadcrumbs/breadcrumbs.component.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/client/src/components/breadcrumbs/breadcrumbs.component.jsx b/client/src/components/breadcrumbs/breadcrumbs.component.jsx index ed3331da1..ee0d51bf6 100644 --- a/client/src/components/breadcrumbs/breadcrumbs.component.jsx +++ b/client/src/components/breadcrumbs/breadcrumbs.component.jsx @@ -5,21 +5,25 @@ import { connect } from "react-redux"; import { Link } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import { selectBreadcrumbs } from "../../redux/application/application.selectors"; +import { selectBodyshop } from "../../redux/user/user.selectors"; import GlobalSearch from "../global-search/global-search.component"; import "./breadcrumbs.styles.scss"; const mapStateToProps = createStructuredSelector({ breadcrumbs: selectBreadcrumbs, + bodyshop: selectBodyshop, }); -export function BreadCrumbs({ breadcrumbs }) { +export function BreadCrumbs({ breadcrumbs, bodyshop }) { return ( - + {" "} + {(bodyshop && bodyshop.shopname && `(${bodyshop.shopname})`) || + ""} {breadcrumbs.map((item) => From 933e0a62fb88d4d8d611901da1ece3d4ea177033 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 17 Jan 2022 10:18:07 -0800 Subject: [PATCH 2/5] IO-1573 CDK Job costing updates --- server/graphql-client/queries.js | 8 +++ server/job/job-costing.js | 119 ++++++++++++++++++++----------- 2 files changed, 87 insertions(+), 40 deletions(-) diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 3ed050f80..e9ba2d511 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -1030,6 +1030,7 @@ exports.QUERY_JOB_COSTING_DETAILS = ` query QUERY_JOB_COSTING_DETAILS($id: uuid! status ca_bc_pvrt ca_customer_gst + dms_allocation joblines(where: { removed: { _eq: false } }) { id db_ref @@ -1073,11 +1074,14 @@ exports.QUERY_JOB_COSTING_DETAILS = ` query QUERY_JOB_COSTING_DETAILS($id: uuid! actualhrs productivehrs flat_rate + ciecacode } bodyshop{ id md_responsibility_centers jc_hourly_rates + cdk_dealerid + pbs_serialnumber } } }`; @@ -1133,6 +1137,7 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT status ca_bc_pvrt ca_customer_gst + dms_allocation joblines(where: {removed: {_eq: false}}) { id db_ref @@ -1176,11 +1181,14 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT actualhrs productivehrs flat_rate + ciecacode } bodyshop { id md_responsibility_centers jc_hourly_rates + cdk_dealerid + pbs_serialnumber } } } diff --git a/server/job/job-costing.js b/server/job/job-costing.js index 7756810f6..4a7ebadb4 100644 --- a/server/job/job-costing.js +++ b/server/job/job-costing.js @@ -261,7 +261,7 @@ function GenerateCostingData(job) { val.profitcenter_labor || defaultProfits[val.mod_lbr_ty] || "?"; if (laborProfitCenter === "?") - console.log("Unknown type", val.mod_lbr_ty); + console.log("Unknown type", val.line_desc, val.mod_lbr_ty); const rateName = `rate_${(val.mod_lbr_ty || "").toLowerCase()}`; const laborAmount = Dinero({ @@ -285,11 +285,12 @@ function GenerateCostingData(job) { val.profitcenter_part || defaultProfits[val.part_type] || "?"; if (partsProfitCenter === "?") - console.log("Unknown type", val.part_type); + console.log("Unknown type", val.line_desc, val.part_type); if (!partsProfitCenter) console.log( "Unknown cost/profit center mapping for parts.", + val.line_desc, val.part_type ); const partsAmount = Dinero({ @@ -298,13 +299,13 @@ function GenerateCostingData(job) { .multiply(val.part_qty || 1) .add( val.prt_dsmk_m && val.prt_dsmk_m !== 0 - ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) - : Dinero({ - amount: Math.round(val.act_price * 100), - }) - .multiply(val.part_qty || 0) - .percentage(Math.abs(val.prt_dsmk_p || 0)) - .multiply(val.prt_dsmk_p > 0 ? 1 : -1) + ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) + : Dinero({ + amount: Math.round(val.act_price * 100), + }) + .multiply(val.part_qty || 0) + .percentage(Math.abs(val.prt_dsmk_p || 0)) + .multiply(val.prt_dsmk_p > 0 ? 1 : -1) ); if (!acc.parts[partsProfitCenter]) acc.parts[partsProfitCenter] = Dinero(); @@ -322,7 +323,7 @@ function GenerateCostingData(job) { "?"; if (partsProfitCenter === "?") { - console.log("Unknown type", val.part_type); + console.log("Unknown type", val.line_desc, val.part_type); } else { const partsAmount = Dinero({ amount: Math.round((val.act_price || 0) * 100), @@ -330,13 +331,13 @@ function GenerateCostingData(job) { .multiply(val.part_qty || 1) .add( val.prt_dsmk_m && val.prt_dsmk_m !== 0 - ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) - : Dinero({ - amount: Math.round(val.act_price * 100), - }) - .multiply(val.part_qty || 0) - .percentage(Math.abs(val.prt_dsmk_p || 0)) - .multiply(val.prt_dsmk_p > 0 ? 1 : -1) + ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) + : Dinero({ + amount: Math.round(val.act_price * 100), + }) + .multiply(val.part_qty || 0) + .percentage(Math.abs(val.prt_dsmk_p || 0)) + .multiply(val.prt_dsmk_p > 0 ? 1 : -1) ); if (!acc.parts[partsProfitCenter]) @@ -372,21 +373,41 @@ function GenerateCostingData(job) { ); } + //Is it a DMS Setup? + const selectedDmsAllocationConfig = + job.bodyshop.md_responsibility_centers.dms_defaults.find( + (d) => d.name === job.dms_allocation + ) || job.bodyshop.md_responsibility_centers.defaults; + const billTotalsByCostCenters = job.bills.reduce((bill_acc, bill_val) => { //At the bill level. bill_val.billlines.map((line_val) => { //At the bill line level. - //console.log("JobCostingPartsTable -> line_val", line_val); - if (!bill_acc[line_val.cost_center]) - bill_acc[line_val.cost_center] = Dinero(); + 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[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) - ); + bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = + 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 { + 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; }); @@ -443,20 +464,38 @@ function GenerateCostingData(job) { const ticketTotalsByCostCenter = job.timetickets.reduce( (ticket_acc, ticket_val) => { //At the invoice level. - if (!ticket_acc[ticket_val.cost_center]) - ticket_acc[ticket_val.cost_center] = Dinero(); - ticket_acc[ticket_val.cost_center] = ticket_acc[ - ticket_val.cost_center - ].add( - Dinero({ - amount: Math.round((ticket_val.rate || 0) * 100), - }).multiply( - ticket_val.flat_rate - ? ticket_val.productivehrs || ticket_val.actualhrs || 0 - : ticket_val.actualhrs || ticket_val.productivehrs || 0 - ) //Should base this on the employee. - ); + if (job.bodyshop.pbs_serialnumber || job.bodyshop.cdk_dealerid) { + if (!ticket_acc[selectedDmsAllocationConfig.costs[ticket_val.ciecacode]]) + ticket_acc[selectedDmsAllocationConfig.costs[ticket_val.ciecacode]] = + Dinero(); + + ticket_acc[selectedDmsAllocationConfig.costs[ticket_val.ciecacode]] = + ticket_acc[selectedDmsAllocationConfig.costs[ticket_val.ciecacode]].add( + Dinero({ + amount: Math.round((ticket_val.rate || 0) * 100), + }).multiply( + ticket_val.flat_rate + ? ticket_val.productivehrs || ticket_val.actualhrs || 0 + : ticket_val.actualhrs || ticket_val.productivehrs || 0 + ) //Should base this on the employee. + ); + } else { + if (!ticket_acc[ticket_val.cost_center]) + ticket_acc[ticket_val.cost_center] = Dinero(); + + ticket_acc[ticket_val.cost_center] = ticket_acc[ + ticket_val.cost_center + ].add( + Dinero({ + amount: Math.round((ticket_val.rate || 0) * 100), + }).multiply( + ticket_val.flat_rate + ? ticket_val.productivehrs || ticket_val.actualhrs || 0 + : ticket_val.actualhrs || ticket_val.productivehrs || 0 + ) //Should base this on the employee. + ); + } return ticket_acc; }, From 821b044515e8e1e050fd40ee21384687b41020c7 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 17 Jan 2022 11:48:12 -0800 Subject: [PATCH 3/5] IO-1606 Include additional costs in reconciliation. --- .../job-bills-total.component.jsx | 6 +++--- .../job-reconciliation-modal.component.jsx | 20 ++++++++++++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/client/src/components/job-bills-total/job-bills-total.component.jsx b/client/src/components/job-bills-total/job-bills-total.component.jsx index 8f8ba31e6..f4dbb80ea 100644 --- a/client/src/components/job-bills-total/job-bills-total.component.jsx +++ b/client/src/components/job-bills-total/job-bills-total.component.jsx @@ -64,9 +64,9 @@ export default function JobBillsTotalComponent({ }) ); - const totalPartsSublet = Dinero(totals.parts.parts.total).add( - Dinero(totals.parts.sublets.total) - ); + const totalPartsSublet = Dinero(totals.parts.parts.total) + .add(Dinero(totals.parts.sublets.total)) + .add(Dinero(totals.additional.additionalCosts)); const discrepancy = totalPartsSublet.subtract(billTotals); diff --git a/client/src/components/job-reconciliation-modal/job-reconciliation-modal.component.jsx b/client/src/components/job-reconciliation-modal/job-reconciliation-modal.component.jsx index f817a4e73..39da47462 100644 --- a/client/src/components/job-reconciliation-modal/job-reconciliation-modal.component.jsx +++ b/client/src/components/job-reconciliation-modal/job-reconciliation-modal.component.jsx @@ -18,7 +18,8 @@ export default function JobReconciliationModalComponent({ job, bills }) { .flat() || []; const jobLineData = job.joblines.filter( - (j) => j.part_type !== null && j.part_type !== "PAE" + (j) => + (j.part_type !== null && j.part_type !== "PAE") || IsAdditionalCost(j) ); return ( @@ -50,3 +51,20 @@ export default function JobReconciliationModalComponent({ job, bills }) { ); } + +function IsAdditionalCost(jobLine) { + //May be able to use db_ref here to help. + //936012 is Haz Waste Dispoal + //936008 is Paint/Materials + //936007 is Shop/Materials + + //Remove paint and shop mat lines. They're calculated under rates. + const isPaintOrShopMat = + jobLine.db_ref === "936008" || jobLine.db_ref === "936007"; + + return ( + (jobLine.lbr_op === "OP13" || //Added to resolve manual job lines coming into other totals because they have no reference. + (jobLine.db_ref && jobLine.db_ref.startsWith("9360"))) && + !isPaintOrShopMat + ); +} From e2941bfe847c3d0bb479f0f645cffbe9c8b10ee1 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 17 Jan 2022 12:08:09 -0800 Subject: [PATCH 4/5] IO-1558 Add color to dates that are today. --- client/src/App/App.styles.scss | 13 +++++++------ .../production-list-columns.date.component.jsx | 10 +++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/client/src/App/App.styles.scss b/client/src/App/App.styles.scss index ccaf3b89f..c188f8a14 100644 --- a/client/src/App/App.styles.scss +++ b/client/src/App/App.styles.scss @@ -92,13 +92,14 @@ } .production-completion-1 { - animation: production-completion-1-blinker 5s linear infinite; -} -@keyframes production-completion-1-blinker { - 50% { - background: rgba(207, 12, 12, 0.555); - } + color: rgba(207, 12, 12, 0.8); + +// animation: production-completion-1-blinker 1s linear infinite; } +// @keyframes production-completion-1-blinker { +// 50% { +// } +// } .react-resizable { position: relative; diff --git a/client/src/components/production-list-columns/production-list-columns.date.component.jsx b/client/src/components/production-list-columns/production-list-columns.date.component.jsx index 51999c288..59b59e420 100644 --- a/client/src/components/production-list-columns/production-list-columns.date.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.date.component.jsx @@ -72,14 +72,14 @@ export default function ProductionListDate({ record, field, time }) { style={{ height: "19px", }} + className={ + !!record[field] && moment().isSame(moment(record[field]), "day") + ? "production-completion-1" + : "" + } > {record[field]} From 34ae2c56b78e8fa6d977f0b9e84c4146737103a1 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Mon, 17 Jan 2022 12:35:43 -0800 Subject: [PATCH 5/5] IO-1642 Resolve delivery checklist not including dates. --- .../job-checklist-form.component.jsx | 36 ++++++++++++------- ...production-list-columns.date.component.jsx | 9 +---- client/src/graphql/bodyshop.queries.js | 3 ++ .../jobs-delivery.page.container.jsx | 1 + 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/client/src/components/job-checklist/components/job-checklist-form/job-checklist-form.component.jsx b/client/src/components/job-checklist/components/job-checklist-form/job-checklist-form.component.jsx index a08464db1..049fa58dc 100644 --- a/client/src/components/job-checklist/components/job-checklist-form/job-checklist-form.component.jsx +++ b/client/src/components/job-checklist/components/job-checklist-form/job-checklist-form.component.jsx @@ -171,7 +171,12 @@ export function JobChecklistForm({ }); } }; - + console.log(job,{ + removeFromProduction: true, + actual_completion: + job && job.actual_completion && moment(job.actual_completion), + actual_delivery: job && job.actual_delivery && moment(job.actual_delivery), + }); return ( fi.value) diff --git a/client/src/components/production-list-columns/production-list-columns.date.component.jsx b/client/src/components/production-list-columns/production-list-columns.date.component.jsx index 59b59e420..4381d3b52 100644 --- a/client/src/components/production-list-columns/production-list-columns.date.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.date.component.jsx @@ -8,9 +8,6 @@ import { DateFormatter } from "../../utils/DateFormatter"; import { useTranslation } from "react-i18next"; -const OneCalendarDay = 60 * 60 * 24 * 1000; -const Now = new Date(); - export default function ProductionListDate({ record, field, time }) { const [updateAlert] = useMutation(UPDATE_JOB); const [visible, setVisible] = useState(false); @@ -78,11 +75,7 @@ export default function ProductionListDate({ record, field, time }) { : "" } > - - {record[field]} - + {record[field]} diff --git a/client/src/graphql/bodyshop.queries.js b/client/src/graphql/bodyshop.queries.js index 878f3a3e9..23905296a 100644 --- a/client/src/graphql/bodyshop.queries.js +++ b/client/src/graphql/bodyshop.queries.js @@ -262,6 +262,9 @@ export const QUERY_DELIVER_CHECKLIST = gql` jobs_by_pk(id: $jobId) { id ro_number + actual_completion + actual_delivery + } } `; diff --git a/client/src/pages/jobs-deliver/jobs-delivery.page.container.jsx b/client/src/pages/jobs-deliver/jobs-delivery.page.container.jsx index ff81b90ba..a87038f58 100644 --- a/client/src/pages/jobs-deliver/jobs-delivery.page.container.jsx +++ b/client/src/pages/jobs-deliver/jobs-delivery.page.container.jsx @@ -69,6 +69,7 @@ export function JobsDeliverContainer({ checklistConfig={ (data && data.bodyshops_by_pk.deliverchecklist) || {} } + job={data ? data.jobs_by_pk : {}} />