diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 8d18dce03..c905978c4 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -26084,6 +26084,27 @@ + + additionalpayeroverallocation + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + additionaltotal false @@ -28062,6 +28083,27 @@ + + multipayers + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + net_repairs false diff --git a/client/src/pages/jobs-close/jobs-close.component.jsx b/client/src/pages/jobs-close/jobs-close.component.jsx index b8f73c144..3df8aafc6 100644 --- a/client/src/pages/jobs-close/jobs-close.component.jsx +++ b/client/src/pages/jobs-close/jobs-close.component.jsx @@ -12,7 +12,9 @@ import { Popconfirm, Select, Space, + Statistic, Switch, + Typography, } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -33,7 +35,7 @@ import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.qu import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors"; - +import Dinero from "dinero.js"; const mapStateToProps = createStructuredSelector({ bodyshop: selectBodyshop, jobRO: selectJobReadOnly, @@ -325,9 +327,41 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { )} + {t("jobs.labels.multipayers")} {Qb_Multi_Ar.treatment === "on" && ( - <> - + + ({ + validator(_, value) { + let totalAllocated = Dinero(); + + const payers = form.getFieldValue("qb_multiple_payers"); + payers && + payers.forEach((payer) => { + totalAllocated = totalAllocated.add( + Dinero({ + amount: Math.round((payer?.amount || 0) * 100), + }) + ); + }); + const discrep = job.job_totals + ? Dinero(job.job_totals.totals.total_repairs).subtract( + totalAllocated + ) + : Dinero(); + return discrep.getAmount() > 0 + ? Promise.resolve() + : Promise.reject( + new Error( + t("jobs.labels.additionalpayeroverallocation") + ) + ); + }, + }), + ]} + > {(fields, { add, remove }) => { return ( @@ -382,7 +416,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { { - if (fields.length < 3) add(); + add(); }} style={{ width: "100%" }} > @@ -393,7 +427,50 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { ); }} - > + + {() => { + //Perform Calculation to determine discrepancy. + let totalAllocated = Dinero(); + + const payers = form.getFieldValue("qb_multiple_payers"); + payers && + payers.forEach((payer) => { + totalAllocated = totalAllocated.add( + Dinero({ amount: Math.round((payer?.amount || 0) * 100) }) + ); + }); + const discrep = job.job_totals + ? Dinero(job.job_totals.totals.total_repairs).subtract( + totalAllocated + ) + : Dinero(); + return ( + + + - + + = + 0 ? "green" : "red", + }} + value={discrep.toFormat()} + /> + + ); + }} + + )} diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 3a63d470a..3570e04dc 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -1546,6 +1546,7 @@ "actual_completion_inferred": "$t(jobs.fields.actual_completion) inferred using $t(jobs.fields.scheduled_completion).", "actual_delivery_inferred": "$t(jobs.fields.actual_delivery) inferred using $t(jobs.fields.scheduled_delivery).", "actual_in_inferred": "$t(jobs.fields.actual_in) inferred using $t(jobs.fields.scheduled_in).", + "additionalpayeroverallocation": "You have allocated more than the sale of the Job to additional payers.", "additionaltotal": "Additional Total", "adjustmentrate": "Adjustment Rate", "adjustments": "Adjustments", @@ -1649,6 +1650,7 @@ "mapa": "Paint Materials", "markforreexport": "Mark for Re-export", "mash": "Shop Materials", + "multipayers": "Additional Payers", "net_repairs": "Net Repairs", "notes": "Notes", "othertotal": "Other Totals", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index e1dcf6d16..919e650e9 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -1546,6 +1546,7 @@ "actual_completion_inferred": "", "actual_delivery_inferred": "", "actual_in_inferred": "", + "additionalpayeroverallocation": "", "additionaltotal": "", "adjustmentrate": "", "adjustments": "", @@ -1649,6 +1650,7 @@ "mapa": "", "markforreexport": "", "mash": "", + "multipayers": "", "net_repairs": "", "notes": "Notas", "othertotal": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 673f93cf4..82294d431 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -1546,6 +1546,7 @@ "actual_completion_inferred": "", "actual_delivery_inferred": "", "actual_in_inferred": "", + "additionalpayeroverallocation": "", "additionaltotal": "", "adjustmentrate": "", "adjustments": "", @@ -1649,6 +1650,7 @@ "mapa": "", "markforreexport": "", "mash": "", + "multipayers": "", "net_repairs": "", "notes": "Remarques", "othertotal": "", diff --git a/server/accounting/qbo/qbo-receivables.js b/server/accounting/qbo/qbo-receivables.js index 057c6c042..a09c22d80 100644 --- a/server/accounting/qbo/qbo-receivables.js +++ b/server/accounting/qbo/qbo-receivables.js @@ -100,7 +100,9 @@ exports.default = async (req, res) => { oauthClient, qbo_realmId, req, - job + job, + isThreeTier, + insCoCustomerTier ); //Query for the owner itself. if (!ownerCustomerTier) { @@ -121,7 +123,11 @@ exports.default = async (req, res) => { qbo_realmId, req, job, - isThreeTier ? ownerCustomerTier : null // ownerCustomerTier || insCoCustomerTier + isThreeTier + ? ownerCustomerTier + : twoTierPref === "source" + ? insCoCustomerTier + : ownerCustomerTier ); // Need to validate that the job tier is associated to the right individual? @@ -342,7 +348,14 @@ async function InsertInsuranceCo(oauthClient, qbo_realmId, req, job, bodyshop) { } exports.InsertInsuranceCo = InsertInsuranceCo; -async function QueryOwner(oauthClient, qbo_realmId, req, job) { +async function QueryOwner( + oauthClient, + qbo_realmId, + req, + job, + isThreeTier, + parentTierRef +) { const ownerName = generateOwnerTier(job, true, null); const result = await oauthClient.makeApiCall({ url: urlBuilder( @@ -362,7 +375,9 @@ async function QueryOwner(oauthClient, qbo_realmId, req, job) { result.json && result.json.QueryResponse && result.json.QueryResponse.Customer && - result.json.QueryResponse.Customer[0] + result.json.QueryResponse.Customer.find( + (x) => x.ParentRef?.value === parentTierRef?.Id + ) ); } exports.QueryOwner = QueryOwner;