diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 8d18dce03..61ce03247 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 @@ -28293,6 +28335,27 @@ + + pimraryamountpayable + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + plitooltips @@ -35283,6 +35346,27 @@ + + external + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + findermodal false 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 70c4703ff..956cb4761 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 @@ -7,7 +7,10 @@ import { connect } from "react-redux"; import { useHistory, useLocation, useParams } from "react-router-dom"; import { createStructuredSelector } from "reselect"; import { logImEXEvent } from "../../../../firebase/firebase.utils"; -import { MARK_LATEST_APPOINTMENT_AS_ARRIVED } from "../../../../graphql/appointments.queries"; +import { + MARK_APPOINTMENT_ARRIVED, + MARK_LATEST_APPOINTMENT_ARRIVED, +} from "../../../../graphql/appointments.queries"; import { UPDATE_JOB } from "../../../../graphql/jobs.queries"; import { selectBodyshop, @@ -41,7 +44,8 @@ export function JobChecklistForm({ const { t } = useTranslation(); const [intakeJob] = useMutation(UPDATE_JOB); const [loading, setLoading] = useState(false); - const [markAptArrived] = useMutation(MARK_LATEST_APPOINTMENT_AS_ARRIVED); + const [markAptArrived] = useMutation(MARK_APPOINTMENT_ARRIVED); + const [markLatestAptArrived] = useMutation(MARK_LATEST_APPOINTMENT_ARRIVED); const [updateOwner] = useMutation(UPDATE_OWNER); const { jobId } = useParams(); @@ -125,6 +129,18 @@ export function JobChecklistForm({ variables: { appointmentId: search.appointmentId }, }); + if (!!appUpdate.errors) { + notification["error"]({ + message: t("checklist.errors.complete", { + error: JSON.stringify(result.errors), + }), + }); + } + } else if (type === "intake" && !search.appointmentId) { + const appUpdate = await markLatestAptArrived({ + variables: { jobId: jobId }, + }); + if (!!appUpdate.errors) { notification["error"]({ message: t("checklist.errors.complete", { @@ -133,6 +149,7 @@ export function JobChecklistForm({ }); } } + if (type === "intake" && job.owner && job.owner.id) { //Updae Owner Allow to Text const updateOwnerResult = await updateOwner({ @@ -175,12 +192,7 @@ 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 ( { @@ -106,9 +112,21 @@ export function PaymentFormComponent({ {t("payments.labels.customer")} - - {t("payments.labels.insurance")} - + {Qb_Multi_Ar.treatment === "on" ? ( + <> + + {bodyshop.md_ins_cos.map((i, idx) => ( + + {i.name} + + ))} + + + ) : ( + + {t("payments.labels.insurance")} + + )} diff --git a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx index 73da49047..d0746a92b 100644 --- a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx +++ b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx @@ -16,6 +16,7 @@ import DataLabel from "../data-label/data-label.component"; import { selectBodyshop } from "../../redux/user/user.selectors"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; +import { useTreatments } from "@splitsoftware/splitio-react"; const SelectorDiv = styled.div` .ant-form-item .ant-select { @@ -37,6 +38,11 @@ export default connect( export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { const { t } = useTranslation(); + const { Qb_Multi_Ar } = useTreatments( + ["Qb_Multi_Ar"], + {}, + bodyshop && bodyshop.imexshopid + ); const [costOptions, setCostOptions] = useState( [ @@ -4535,24 +4541,26 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { - Multiple Payers Item}> - - - - + {Qb_Multi_Ar.treatment === "on" && ( + Multiple Payers Item}> + + + + + )} {t("bodyshop.labels.responsibilitycenters.sales_tax_codes")} diff --git a/client/src/graphql/appointments.queries.js b/client/src/graphql/appointments.queries.js index e508e274e..d5942caca 100644 --- a/client/src/graphql/appointments.queries.js +++ b/client/src/graphql/appointments.queries.js @@ -1,77 +1,81 @@ import { gql } from "@apollo/client"; export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql` -query QUERY_ALL_ACTIVE_APPOINTMENTS( - $start: timestamptz! - $end: timestamptz! - $startd: date! - $endd: date! -) { - employee_vacation( - where: { _or: [{ start: { _gte: $startd } }, - { end: { _lte: $endd } }, - {_and:[{start:{_lte: $startd}},{end:{_gte:$endd}}]}] } + query QUERY_ALL_ACTIVE_APPOINTMENTS( + $start: timestamptz! + $end: timestamptz! + $startd: date! + $endd: date! ) { - id - start - end - employee { + employee_vacation( + where: { + _or: [ + { start: { _gte: $startd } } + { end: { _lte: $endd } } + { _and: [{ start: { _lte: $startd } }, { end: { _gte: $endd } }] } + ] + } + ) { id - last_name - first_name + start + end + employee { + id + last_name + first_name + } } - } - appointments( - where: { - canceled: { _eq: false } - end: { _lte: $end } - start: { _gte: $start } - } - ) { - start - id - end - arrived - title - isintake - block - color - note - job { - alt_transport - ro_number - ownr_ln - ownr_co_nm - ownr_fn - ownr_ph1 - ownr_ph2 - ownr_ea - clm_total + appointments( + where: { + canceled: { _eq: false } + end: { _lte: $end } + start: { _gte: $start } + } + ) { + start id - clm_no - ins_co_nm - v_model_yr - v_make_desc - v_model_desc - labhrs: joblines_aggregate( - where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } } - ) { - aggregate { - sum { - mod_lb_hrs + end + arrived + title + isintake + block + color + note + job { + alt_transport + ro_number + ownr_ln + ownr_co_nm + ownr_fn + ownr_ph1 + ownr_ph2 + ownr_ea + clm_total + id + clm_no + ins_co_nm + v_model_yr + v_make_desc + v_model_desc + labhrs: joblines_aggregate( + where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } } + ) { + aggregate { + sum { + mod_lb_hrs + } + } + } + larhrs: joblines_aggregate( + where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } } + ) { + aggregate { + sum { + mod_lb_hrs + } } } } - larhrs: joblines_aggregate( - where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } } - ) { - aggregate { - sum { - mod_lb_hrs - } - } - } - } } } `; @@ -382,8 +386,8 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql` } `; -export const MARK_LATEST_APPOINTMENT_AS_ARRIVED = gql` - mutation MARK_LATEST_APPOINTMENT_AS_ARRIVED($appointmentId: uuid!) { +export const MARK_APPOINTMENT_ARRIVED = gql` + mutation MARK_APPOINTMENT_ARRIVED($appointmentId: uuid!) { update_appointments( where: { id: { _eq: $appointmentId } } _set: { arrived: true } @@ -396,3 +400,21 @@ export const MARK_LATEST_APPOINTMENT_AS_ARRIVED = gql` } } `; +export const MARK_LATEST_APPOINTMENT_ARRIVED = gql` + mutation MARK_LATEST_APPOINTMENT_ARRIVED($jobId: uuid!) { + update_appointments( + where: { + jobid: { _eq: $jobId } + canceled: { _eq: false } + isintake: { _eq: true } + } + _set: { arrived: true } + ) { + affected_rows + returning { + id + arrived + } + } + } +`; diff --git a/client/src/pages/jobs-close/jobs-close.component.jsx b/client/src/pages/jobs-close/jobs-close.component.jsx index b8f73c144..f7e6d3795 100644 --- a/client/src/pages/jobs-close/jobs-close.component.jsx +++ b/client/src/pages/jobs-close/jobs-close.component.jsx @@ -3,6 +3,7 @@ import { useApolloClient, useMutation } from "@apollo/client"; import { Alert, Button, + Col, Divider, Form, Input, @@ -10,9 +11,12 @@ import { notification, PageHeader, Popconfirm, + Row, Select, Space, + Statistic, Switch, + Typography, } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -33,7 +37,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,75 +329,164 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) { )} + {t("jobs.labels.multipayers")} {Qb_Multi_Ar.treatment === "on" && ( - <> - - {(fields, { add, remove }) => { - return ( -
- {fields.map((field, index) => ( - - - - - + + - - - + + + - { - remove(field.name); - }} - /> - + { + remove(field.name); + }} + /> + + + ))} + + - ))} - -
+ ); + }} +
+ + + + {() => { + //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", }} - style={{ width: "100%" }} - > - {t("jobs.actions.dms.addpayer")} - - - - ); - }} - - + value={discrep.toFormat()} + /> + + ); + }} + + + )} diff --git a/client/src/pages/jobs-create/jobs-create.container.jsx b/client/src/pages/jobs-create/jobs-create.container.jsx index b7ffad7b2..15af524ed 100644 --- a/client/src/pages/jobs-create/jobs-create.container.jsx +++ b/client/src/pages/jobs-create/jobs-create.container.jsx @@ -89,6 +89,7 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) { let job = Object.assign( {}, values, + { date_open: new Date() }, { vehicle: state.vehicle.selectedid || state.vehicle.none diff --git a/client/src/pages/parts-queue/parts-queue.page.component.jsx b/client/src/pages/parts-queue/parts-queue.page.component.jsx index c9725a905..5d6226bdb 100644 --- a/client/src/pages/parts-queue/parts-queue.page.component.jsx +++ b/client/src/pages/parts-queue/parts-queue.page.component.jsx @@ -1,6 +1,7 @@ import { SyncOutlined } from "@ant-design/icons"; import { useQuery } from "@apollo/client"; import { Button, Card, Input, Space, Table } from "antd"; +import _ from "lodash"; import queryString from "query-string"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -14,7 +15,6 @@ import OwnerNameDisplay from "../../components/owner-name-display/owner-name-dis import ProductionListColumnComment from "../../components/production-list-columns/production-list-columns.comment.component"; import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries"; import { selectBodyshop } from "../../redux/user/user.selectors"; -import { onlyUnique } from "../../utils/arrayHelper"; import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter"; import { alphaSort, dateSort } from "../../utils/sorters"; import useLocalStorage from "../../utils/useLocalStorage"; @@ -94,6 +94,14 @@ export function PartsQueuePageComponent({ bodyshop }) { // searchParams.page = pagination.current; searchParams.sortcolumn = sorter.columnKey; searchParams.sortorder = sorter.order; + + if (filters.status) { + searchParams.statusFilters = JSON.stringify( + _.flattenDeep(filters.status) + ); + } else { + delete searchParams.statusFilters; + } setFilter(filters); history.push({ search: queryString.stringify(searchParams) }); }; @@ -136,19 +144,14 @@ export function PartsQueuePageComponent({ bodyshop }) { key: "status", sorter: (a, b) => alphaSort(a.status, b.status), sortOrder: sortcolumn === "status" && sortorder, + filteredValue: statusFilters ? JSON.parse(statusFilters) : null, filters: - (jobs && - jobs - .map((j) => j.status) - .filter(onlyUnique) - .map((s) => { - return { - text: s || "No Status*", - value: [s], - }; - })) || - [], - onFilter: (value, record) => value.includes(record.status), + bodyshop.md_ro_statuses.active_statuses.map((s) => { + return { + text: s || "No Status*", + value: [s], + }; + }) || [], render: (text, record) => { return record.status || t("general.labels.na"); }, diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 3a63d470a..ea078dada 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", @@ -1660,6 +1662,7 @@ "partsfilter": "Parts Only", "partssubletstotal": "Parts & Sublets Total", "partstotal": "Parts Total (ex. Taxes)", + "pimraryamountpayable": "Total Primary Payable", "plitooltips": { "billtotal": "The total amount of all bill lines that have been posted against this RO (not including credits, taxes, or labor adjustments).", "calculatedcreditsnotreceived": "The calculated credits not received is derived by subtracting the amount of credit memos entered from the retail total of returns created. This does not take into account whether the credit was marked as received. You can find more information here.", @@ -2088,6 +2091,7 @@ "customer": "Customer", "edit": "Edit Payment", "electronicpayment": "Use Electronic Payment Processing?", + "external": "External", "findermodal": "ICBC Payment Finder", "insurance": "Insurance", "new": "New Payment", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index e1dcf6d16..a479d3208 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": "", @@ -1660,6 +1662,7 @@ "partsfilter": "", "partssubletstotal": "", "partstotal": "", + "pimraryamountpayable": "", "plitooltips": { "billtotal": "", "calculatedcreditsnotreceived": "", @@ -2088,6 +2091,7 @@ "customer": "", "edit": "", "electronicpayment": "", + "external": "", "findermodal": "", "insurance": "", "new": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 673f93cf4..7f8077f2e 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": "", @@ -1660,6 +1662,7 @@ "partsfilter": "", "partssubletstotal": "", "partstotal": "", + "pimraryamountpayable": "", "plitooltips": { "billtotal": "", "calculatedcreditsnotreceived": "", @@ -2088,6 +2091,7 @@ "customer": "", "edit": "", "electronicpayment": "", + "external": "", "findermodal": "", "insurance": "", "new": "", diff --git a/server/accounting/qbo/qbo-payments.js b/server/accounting/qbo/qbo-payments.js index c903c0a43..3f729d80d 100644 --- a/server/accounting/qbo/qbo-payments.js +++ b/server/accounting/qbo/qbo-payments.js @@ -73,11 +73,19 @@ exports.default = async (req, res) => { for (const payment of payments) { try { - const isThreeTier = bodyshop.accountingconfig.tiers === 3; - const twoTierPref = bodyshop.accountingconfig.twotierpref; + let isThreeTier = bodyshop.accountingconfig.tiers === 3; + let twoTierPref = bodyshop.accountingconfig.twotierpref; //Replace this with a for-each loop to check every single Job that's included in the list. + //QB Multi AR - If it is in this scenario, overwrite whatever defaults are set since multi AR + //will always go Source => RO + if (payment.payer !== "Customer") { + payment.job.ins_co_nm = payment.payer; + twoTierPref = "source"; + isThreeTier = false; + } + let insCoCustomerTier, ownerCustomerTier, jobTier; if (isThreeTier || (!isThreeTier && twoTierPref === "source")) { //Insert the insurance company tier. @@ -106,7 +114,9 @@ exports.default = async (req, res) => { oauthClient, qbo_realmId, req, - payment.job + payment.job, + isThreeTier, + insCoCustomerTier ); //Query for the owner itself. if (!ownerCustomerTier) { @@ -122,7 +132,17 @@ exports.default = async (req, res) => { } //Query for the Job or Create it. - jobTier = await QueryJob(oauthClient, qbo_realmId, req, payment.job); + jobTier = await QueryJob( + oauthClient, + qbo_realmId, + req, + payment.job, + isThreeTier + ? ownerCustomerTier + : twoTierPref === "source" + ? insCoCustomerTier + : ownerCustomerTier + ); // Need to validate that the job tier is associated to the right individual? @@ -237,7 +257,8 @@ async function InsertPayment( qbo_realmId, req, payment.job.ro_number, - false + false, + parentRef ); if (invoices && invoices.length !== 1) { @@ -260,7 +281,7 @@ async function InsertPayment( PaymentMethodRef: { value: paymentMethods[payment.type], }, - ...(invoices && invoices.length === 1 + ...(invoices && invoices.length === 1 && invoices[0] ? { Line: [ { @@ -305,13 +326,14 @@ async function QueryMetaData( qbo_realmId, req, ro_number, - isCreditMemo + isCreditMemo, + parentTierRef ) { const invoice = await oauthClient.makeApiCall({ url: urlBuilder( qbo_realmId, "query", - `select * From Invoice where DocNumber = '${ro_number}'` + `select * From Invoice where DocNumber like '${ro_number}%'` ), method: "POST", headers: { @@ -407,7 +429,14 @@ async function QueryMetaData( invoices: invoice.json && invoice.json.QueryResponse && - invoice.json.QueryResponse.Invoice, + invoice.json.QueryResponse.Invoice && + (parentTierRef + ? [ + invoice.json.QueryResponse.Invoice.find( + (x) => x.CustomerRef.value === parentTierRef.Id + ), + ] + : [invoice.json.QueryResponse.Invoice[0]]), }; } async function InsertCreditMemo( @@ -423,7 +452,8 @@ async function InsertCreditMemo( qbo_realmId, req, payment.job.ro_number, - true + true, + parentRef ); if (invoices && invoices.length !== 1) { 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; diff --git a/server/data/arms.js b/server/data/arms.js index 7e3cd9643..a95887f05 100644 --- a/server/data/arms.js +++ b/server/data/arms.js @@ -205,8 +205,8 @@ exports.default = async (req, res) => { Party: { PersonInfo: { PersonName: { - FirstName: job.ownr_fn, - LastName: job.ownr_ln, + FirstName: job.ownr_co_nm ? "N/A" : job.ownr_fn, + LastName: job.ownr_co_nm ? job.ownr_co_nm : job.ownr_ln, }, // Communications: [ // { @@ -336,7 +336,7 @@ exports.default = async (req, res) => { LossDateTime: job.loss_date && moment(job.loss_date) - .tz(bodyshop.timezone) + //.tz(bodyshop.timezone) .format(momentFormat), LossDescCode: "Collision", PrimaryPOI: { @@ -515,8 +515,11 @@ exports.default = async (req, res) => { { TotalType: "LAB", TotalTypeDesc: "Body Labor", - TotalHours: job.job_totals.rates.lab.hours, - TotalAmt: Dinero(job.job_totals.rates.lab.total).toFormat("0.00"), + TotalHours: + job.job_totals.rates.lab.hours + job.job_totals.rates.la1.hours, + TotalAmt: Dinero(job.job_totals.rates.lab.total) + .add(Dinero(job.job_totals.rates.la1.total)) + .toFormat("0.00"), }, { TotalType: "LAF", @@ -635,9 +638,9 @@ exports.default = async (req, res) => { { TotalType: "OTAC", TotalTypeDesc: "Additional Charges", - TotalAmt: Dinero( - job.job_totals.additional.additionalCosts - ).toFormat("0.00"), + TotalAmt: Dinero(job.job_totals.additional.additionalCosts) + .add(Dinero(job.job_totals.additional.pvrt)) + .toFormat("0.00"), }, ], SummaryTotalsInfo: [ diff --git a/server/data/autohouse.js b/server/data/autohouse.js index 265187796..679a455b9 100644 --- a/server/data/autohouse.js +++ b/server/data/autohouse.js @@ -57,7 +57,7 @@ exports.default = async (req, res) => { bodyshopid: bodyshop.id, start: start ? moment(start).startOf("day") - : moment().subtract(3, "days").startOf("day"), + : moment().subtract(5, "days").startOf("day"), ...(end && { end: moment(end).startOf("day") }), } ); @@ -316,6 +316,10 @@ const CreateRepairOrderTag = (job, errorCallback) => { moment(job.date_open) .tz(job.bodyshop.timezone) .format(AhDateFormat)) || + (job.created_at && + moment(job.created_at) + .tz(job.bodyshop.timezone) + .format(AhDateFormat)) || "", ScheduledArrivalDate: (job.scheduled_in && diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 1f984cde5..fdae6eb5e 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -623,6 +623,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop } jobs(where: {_and: [{converted: {_eq: true}}, {updated_at: {_gt: $start}}, {updated_at: {_lte: $end}}, {shopid: {_eq: $bodyshopid}}]}) { id + created_at ro_number status est_ct_fn