From 83be45a40bfc01a7d19eb4634ce6b30a65c5b42c Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 28 Jan 2026 16:20:27 -0800 Subject: [PATCH] IO-3515 Checkin. Crude form update with some correct values. Pricing still significantly out. --- .../bill-enter-modal.container.jsx | 42 +- server/ai/bill-ocr/bill-ocr-generator.js | 2968 ++--------------- server/ai/bill-ocr/bill-ocr-normalize.js | 12 +- server/ai/bill-ocr/bill-ocr.js | 30 +- 4 files changed, 280 insertions(+), 2772 deletions(-) diff --git a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx index f3bfe6fab..4338f9bea 100644 --- a/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx +++ b/client/src/components/bill-enter-modal/bill-enter-modal.container.jsx @@ -2,7 +2,7 @@ import { useApolloClient, useMutation } from "@apollo/client/react"; import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react"; import { Button, Checkbox, Form, Modal, Space } from "antd"; import _ from "lodash"; -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -27,6 +27,7 @@ import { handleUpload as handleLocalUpload } from "../documents-local-upload/doc import { handleUpload } from "../documents-upload/documents-upload.utility"; import { handleUpload as handleUploadToImageProxy } from "../documents-upload-imgproxy/documents-upload-imgproxy.utility"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; +import axios from "axios"; const mapStateToProps = createStructuredSelector({ billEnterModal: selectBillEnterModal, @@ -53,6 +54,7 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop, const client = useApolloClient(); const [generateLabel, setGenerateLabel] = useLocalStorage("enter_bill_generate_label", false); const notification = useNotification(); + const fileInputRef = useRef(null); const { treatments: { Enhanced_Payroll, Imgproxy } @@ -419,6 +421,44 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop, }} footer={ + { + const file = e.target.files?.[0]; + if (file) { + const formdata = new FormData(); + formdata.append("billScan", file); + formdata.append("jobid", billEnterModal.context.job.id); + formdata.append("bodyshopid", bodyshop.id); + formdata.append("partsorderid", "3dd26419-a139-4399-af4e-43eeb6f0dbad"); + // formdata.append("skipTextract", "true"); // For testing purposes + axios + .post("/ai/bill-ocr", formdata) + .then(({ data }) => { + console.log("*** ~ BillEnterModalContainer ~ response:", data.data.billForm); + //Stored in data.data + + form.setFieldsValue(data.data.billForm); + }) + .catch((error) => { + console.error("*** ~ BillEnterModalContainer ~ error:", error); + }); + } + // Reset the input so the same file can be selected again + e.target.value = ""; + }} + /> + setGenerateLabel(e.target.checked)}> {t("bills.labels.generatepartslabel")} diff --git a/server/ai/bill-ocr/bill-ocr-generator.js b/server/ai/bill-ocr/bill-ocr-generator.js index 34f50baa3..c35ca3076 100644 --- a/server/ai/bill-ocr/bill-ocr-generator.js +++ b/server/ai/bill-ocr/bill-ocr-generator.js @@ -1,6 +1,9 @@ const client = require("../../graphql-client/graphql-client").client; const Fuse = require('fuse.js'); +const { has } = require("lodash"); + +const PRICE_PERCENT_MARGIN_TOLERANCE = 0.5; //Used to make sure prices and costs are likely. // Helper function to normalize fields const normalizePartNumber = (str) => { @@ -12,6 +15,7 @@ const normalizeText = (str) => { }; const normalizePrice = (str) => { + if (typeof str !== 'string') return str; return str.replace(/[^0-9.-]+/g, ""); }; @@ -47,50 +51,53 @@ const mergeResults = (resultsArray, weights = []) => { }; async function generateBillFormData({ processedData, jobid, bodyshopid, partsorderid }) { - processedData = processedDataFromCoache; - const jobData = { jobs_by_pk: tempJobObjectFromCoache }; - //Look up the job information - // const jobData = await client.request(` - // query QUERY_BILL_OCR_DATA($jobid: uuid!, $partsorderid: uuid!) { - // jobs_by_pk(id: $jobid) { - // id - // joblines { - // id - // line_desc - // removed - // act_price - // db_price - // oem_partno - // alt_partno - // } - // } - // parts_orders_by_pk(id: $partsorderid) { - // id - // parts_order_lines { - // id - // line_desc - // act_price - // cost - // jobline { - // id - // line_desc - // act_price - // oem_partno - // alt_partno - // } - // } - // } - // } + //TODO: Should this be using the client auth token to limit results? Most likely. + //TODO: Add in vendor data. + const jobData = await client.request(` + query QUERY_BILL_OCR_DATA($jobid: uuid!, $partsorderid: uuid!) { + vendors{ + id + name + } + jobs_by_pk(id: $jobid) { + id + joblines { + id + line_desc + removed + act_price + db_price + oem_partno + alt_partno + } + } + parts_orders_by_pk(id: $partsorderid) { + id + parts_order_lines { + id + line_desc + act_price + cost + jobline { + id + line_desc + act_price + oem_partno + alt_partno + } + } + } + } - // `, { - // jobid, partsorderid // this may fail if null? - // }); + `, { + jobid, partsorderid // this may fail if null? + }); - //Get a list of vendors and the ID. + //TODO: Need to find a vendor ID. Create a fuse for it, and fuzzy search for it using the textract vendor info. //Create fuses of line descriptions for matching. const jobLineDescFuse = new Fuse( - jobData.jobs_by_pk.joblines, + jobData.jobs_by_pk.joblines, { keys: [{ name: 'line_desc', @@ -118,18 +125,99 @@ async function generateBillFormData({ processedData, jobid, bodyshopid, partsord if (!job) { throw new Error('Job not found for bill form data generation.'); } - //Try to match the recognized bill lines with a job line if there is one. + //Figure out which lines have a match and which don't. + + //TODO: How do we handle freight lines and core charges? //Create the form data structure for the bill posting screen. + const billFormData = { + "jobid": jobid, + "vendorid": null, + "invoice_number": processedData.summary?.INVOICE_RECEIPT_ID?.value, + "date": processedData.summary?.INVOICE_RECEIPT_DATE?.value, + "is_credit_memo": false, + "total": normalizePrice(processedData.summary?.INVOICE_TOTAL?.value || processedData.summary?.TOTAL?.value), + "billlines": joblineMatches.map(jlMatchLine => { + const { matches, textractLineItem, } = jlMatchLine + //Matches should be prioritized, take the first one. + const matchToUse = matches.length > 0 ? matches[0] : null; + //TODO: Should be using the textract if there is an exact match on the normalized label. + //if there isn't then we can do the below. + let actualPrice, actualCost; + + const hasNormalizedActualPrice = Object.keys(textractLineItem).find(key => textractLineItem[key].normalizedLabel === 'actual_price'); + const hasNormalizedActualCost = Object.keys(textractLineItem).find(key => textractLineItem[key].normalizedLabel === 'actual_cost'); + + if (hasNormalizedActualPrice) { + actualPrice = textractLineItem[hasNormalizedActualPrice].value; + } + if (hasNormalizedActualCost) { + actualCost = textractLineItem[hasNormalizedActualCost].value; + } + + if (!hasNormalizedActualPrice || !hasNormalizedActualCost) { + //This is if there was no match found for normalized labels. + //Check all prices, and generally the higher one will be the actual price and the lower one will be the cost. + //Need to make sure that other random items are excluded. This should be within a reasonable range of the matched jobline at matchToUse.item.act_price + //Iterate over all of the text values, and check out which of them are currencies. + //They'll be in the format starting with a $ sign usually. + const currencyTextractLineItems = [] // {key, value} + Object.keys(textractLineItem).forEach(key => { + const currencyValue = textractLineItem[key].value?.startsWith('$') ? textractLineItem[key].value : null; + if (currencyValue) { + //Clean it and parse it + const cleanValue = parseFloat(currencyValue.replace(/[^0-9.-]/g, '')) || 0; + currencyTextractLineItems.push({ key, value: cleanValue }) + } + }) + + //Sort them descending + currencyTextractLineItems.sort((a, b) => b.value - a.value); + //Most expensive should be the actual price, second most expensive should be the cost. + if (!actualPrice) actualPrice = currencyTextractLineItems.length > 0 ? currencyTextractLineItems[0].value : 0; + if (!actualCost) actualCost = currencyTextractLineItems.length > 1 ? currencyTextractLineItems[1].value : 0; + + if (matchToUse) { + //Double check that they're within 50% of the matched jobline price if there is one. + const joblinePrice = parseFloat(matchToUse.item.act_price) || 0; + if (!hasNormalizedActualPrice && actualPrice > 0 && (actualPrice < joblinePrice * (1 - PRICE_PERCENT_MARGIN_TOLERANCE) || actualPrice > joblinePrice * (1 + PRICE_PERCENT_MARGIN_TOLERANCE))) { + actualPrice = joblinePrice; //Set to the jobline as a fallback. + } + if (!hasNormalizedActualCost && actualCost > 0 && (actualCost < joblinePrice * (1 - PRICE_PERCENT_MARGIN_TOLERANCE) || actualCost > joblinePrice * (1 + PRICE_PERCENT_MARGIN_TOLERANCE))) { + actualCost = null //Blank it out if it's not likely. + } + } + } + + const lineObject = { + "line_desc": matchToUse.item?.line_desc, + "quantity": textractLineItem.QUANTITY?.value, // convert to integer? + "actual_price": normalizePrice(actualPrice), + "actual_cost": normalizePrice(actualCost), + "cost_center": "SETBYCLIENT", //Needs to get set by client side. + "applicable_taxes": { //Not sure what to do with these? + "federal": false, + "state": false, + "local": false + }, + "joblineid": matchToUse.item?.id || "noline", + } + return lineObject + }) + } + + return billFormData } function joblineFuzzySearch({ fuseToSearch, processedData }) { const matches = [] - processedData.result.lineItems.forEach(lineItem => { + processedData.lineItems.forEach(lineItem => { // Refined ITEM search (multi-word description) const refinedItemResults = (() => { + if (!lineItem.ITEM?.value) return []; + const itemValue = lineItem.ITEM.value; const normalized = normalizeText(itemValue); @@ -152,6 +240,8 @@ function joblineFuzzySearch({ fuseToSearch, processedData }) { // Refined PRODUCT_CODE search (part numbers) const refinedProductCodeResults = (() => { + if (!lineItem.PRODUCT_CODE?.value) return []; + const productCode = lineItem.PRODUCT_CODE.value; const normalized = normalizePartNumber(productCode); @@ -174,6 +264,8 @@ function joblineFuzzySearch({ fuseToSearch, processedData }) { // Refined PRICE search const refinedPriceResults = (() => { + if (!lineItem.PRICE?.value) return []; + const price = normalizePrice(lineItem.PRICE.value); // 1: Exact price match @@ -193,6 +285,8 @@ function joblineFuzzySearch({ fuseToSearch, processedData }) { // Refined UNIT_PRICE search const refinedUnitPriceResults = (() => { + if (!lineItem.UNIT_PRICE?.value) return []; + const unitPrice = normalizePrice(lineItem.UNIT_PRICE.value); // 1: Exact price match @@ -269,8 +363,13 @@ function joblineFuzzySearch({ fuseToSearch, processedData }) { .sort((a, b) => b.finalScore - a.finalScore) .slice(0, 5); - - matches.push({ matches: finalMatches, textractLineItem: lineItem }); + // Always push the textract line item, even if no matches found + // This ensures all invoice lines are processed + matches.push({ + matches: finalMatches, + textractLineItem: lineItem, + hasMatch: finalMatches.length > 0 + }); }) return matches @@ -280,64 +379,6 @@ module.exports = { generateBillFormData } - -//Desired Output - -const output = { - "jobid": "3977b88e-68b8-482c-b10e-ca5e44222543", - "vendorid": "9261069e-4579-47d0-ad01-cdbed45c8655", - "invoice_number": "1234", - "date": "2026-01-28T19:50:13.598Z", - "is_credit_memo": false, - "total": 609.61, - "federal_tax_rate": 5, - "state_tax_rate": 7, - "local_tax_rate": 0, - "billlines": { - "data": [ - { - "line_desc": "R Fender Panel", - "quantity": 1, - "actual_price": 724.01, - "actual_cost": 579.21, - "cost_center": "Aftermarket", - "applicable_taxes": { - "federal": false, - "state": false, - "local": false - }, - "joblineid": "a18e0cc4-87b4-4089-a837-5e89f9ab2f77" - }, - { - "line_desc": "R Fender Stone Protective Film", - "quantity": 1, - "actual_price": 15, - "actual_cost": 12, - "cost_center": "Aftermarket", - "applicable_taxes": { - "federal": false, - "state": false, - "local": false - }, - "joblineid": "dded9d43-e981-42fc-82b6-c6b15740c240" - }, - { - "line_desc": "R Frt Door Adhesive Nameplate", - "quantity": 1, - "actual_price": 23, - "actual_cost": 18.4, - "cost_center": "Aftermarket", - "applicable_taxes": { - "federal": false, - "state": false, - "local": false - }, - "joblineid": "578c7825-4f01-46d2-a403-527613711e2c" - } - ] - } -} - const processedDataFromCoache = { "result": { "summary": { @@ -521,10 +562,10 @@ const processedDataFromCoache = { }, { "OTHER": { - "value": "$246.00", - "label": "38", - "normalizedLabel": "UNKNOWN_38", - "confidence": 90.83607482910156 + "value": "2", + "label": "1", + "normalizedLabel": "UNKNOWN_1", + "confidence": 75.68085479736328 }, "PRODUCT_CODE": { "value": "80A-807-647-C-9B9", @@ -556,6 +597,18 @@ const processedDataFromCoache = { "normalizedLabel": "UNKNOWN_38", "confidence": 93.8568344116211 }, + "OTHER_2": { + "value": "SP-ORD", + "label": "38", + "normalizedLabel": "UNKNOWN_38", + "confidence": 90.64925384521484 + }, + "OTHER_3": { + "value": "$246.00", + "label": "38", + "normalizedLabel": "UNKNOWN_38", + "confidence": 90.83607482910156 + }, "EXPENSE_ROW": { "value": "2 80A-807-647-C-9B9 GRILLE 1 SP-ORD $246.00 $191.88 $191.88\n-", "label": "", @@ -565,10 +618,10 @@ const processedDataFromCoache = { }, { "OTHER": { - "value": "$286.00", - "label": "38", - "normalizedLabel": "UNKNOWN_38", - "confidence": 88.31619262695312 + "value": "3", + "label": "1", + "normalizedLabel": "UNKNOWN_1", + "confidence": 73.59772491455078 }, "PRODUCT_CODE": { "value": "80A-807-661-A-GRU", @@ -600,6 +653,18 @@ const processedDataFromCoache = { "normalizedLabel": "UNKNOWN_38", "confidence": 91.8134765625 }, + "OTHER_2": { + "value": "SP-ORD", + "label": "38", + "normalizedLabel": "UNKNOWN_38", + "confidence": 88.12962341308594 + }, + "OTHER_3": { + "value": "$286.00", + "label": "38", + "normalizedLabel": "UNKNOWN_38", + "confidence": 88.31619262695312 + }, "EXPENSE_ROW": { "value": "3 80A-807-661-A-GRU CONN PIECE 1 SP-ORD $286.00 $223.08 $223.08\n-", "label": "", @@ -609,10 +674,10 @@ const processedDataFromCoache = { }, { "OTHER": { - "value": "$1,275.00", - "label": "38", - "normalizedLabel": "UNKNOWN_38", - "confidence": 89.5396499633789 + "value": "4", + "label": "1", + "normalizedLabel": "UNKNOWN_1", + "confidence": 74.5914306640625 }, "PRODUCT_CODE": { "value": "80A-853-765--3Q7", @@ -644,6 +709,18 @@ const processedDataFromCoache = { "normalizedLabel": "UNKNOWN_38", "confidence": 96.28019714355469 }, + "OTHER_2": { + "value": "SP-ORD", + "label": "38", + "normalizedLabel": "UNKNOWN_38", + "confidence": 89.3599853515625 + }, + "OTHER_3": { + "value": "$1,275.00", + "label": "38", + "normalizedLabel": "UNKNOWN_38", + "confidence": 89.5396499633789 + }, "EXPENSE_ROW": { "value": "4 80A-853-765--3Q7 MOLDING 1 SP-ORD $1,275.00 $994.50 $994.50\n-", "label": "", @@ -653,10 +730,10 @@ const processedDataFromCoache = { }, { "OTHER": { - "value": "$2,690.00", - "label": "38", - "normalizedLabel": "UNKNOWN_38", - "confidence": 91.14651489257812 + "value": "5", + "label": "1", + "normalizedLabel": "UNKNOWN_1", + "confidence": 75.91088104248047 }, "ITEM": { "value": "GRILLE", @@ -688,6 +765,18 @@ const processedDataFromCoache = { "normalizedLabel": "UNKNOWN_38", "confidence": 86.12691497802734 }, + "OTHER_2": { + "value": "SP-ORD", + "label": "38", + "normalizedLabel": "UNKNOWN_38", + "confidence": 90.96830749511719 + }, + "OTHER_3": { + "value": "$2,690.00", + "label": "38", + "normalizedLabel": "UNKNOWN_38", + "confidence": 91.14651489257812 + }, "EXPENSE_ROW": { "value": "5 80A-853-651-L-RP5 GRILLE 1 SP-ORD $2,690.00 $2,098.20 $2,098.20\n-", "label": "", @@ -698,2649 +787,4 @@ const processedDataFromCoache = { ], } -} - -const tempJobObjectFromCoache = { - "tasks_aggregate": { - "aggregate": { - "count": 0, - "__typename": "tasks_aggregate_fields" - }, - "__typename": "tasks_aggregate" - }, - "actual_completion": "2026-01-21T22:50:00+00:00", - "actual_delivery": "2026-01-22T00:50:00+00:00", - "actual_in": "2026-01-09T22:23:41.562+00:00", - "acv_amount": null, - "admin_clerk": "bianca@raydarcollisiongroup.com", - "adjustment_bottom_line": null, - "alt_transport": "Rental", - "area_of_damage": { - "impact1": "12", - "impact2": null - }, - "auto_add_ats": false, - "available_jobs": [], - "ca_bc_pvrt": null, - "ca_customer_gst": 0, - "ca_gst_registrant": false, - "category": "Hit & Run", - "cccontracts": [], - "cieca_pfl": {}, - "cieca_pfo": {}, - "cieca_pft": {}, - "cieca_ttl": { - "data": { - "g_tax": 620.74, - "gst_amt": 443.39, - "g_aa_amt": 0, - "prev_net": 0, - "supp_amt": -697.45, - "g_ded_amt": 300, - "g_rpd_amt": 0, - "g_ttl_amt": 9931.82, - "g_upd_amt": 0, - "n_ttl_amt": 9631.82, - "g_bett_amt": 0, - "g_cust_amt": 300, - "g_ttl_disc": 0 - } - }, - "class": null, - "clm_no": "DA37868-0-A", - "clm_total": 9931.81, - "comment": "IN 01/09/2026 02:23 pm\nCOMP 01/21/2026 02:50 pm\nDEL 01/21/2026 04:50 pm", - "converted": true, - "csiinvites": [], - "date_estimated": "2026-01-07", - "date_exported": "2026-01-26T19:19:13.41+00:00", - "date_invoiced": "2026-01-26T19:07:11.473+00:00", - "date_last_contacted": null, - "date_lost_sale": null, - "date_next_contact": null, - "date_open": "2026-01-07T18:50:36.171+00:00", - "date_rentalresp": null, - "date_repairstarted": null, - "date_scheduled": null, - "date_towin": null, - "date_void": null, - "ded_amt": 300, - "ded_note": null, - "ded_status": "Y", - "deliverchecklist": { - "form": [ - { - "name": "Detailed", - "type": "checkbox", - "label": "Detailed?", - "required": false - }, - { - "name": "Buffed", - "type": "checkbox", - "label": "Buffed?", - "required": false - }, - { - "name": "Comments", - "type": "text", - "label": "Additional Comments?", - "required": false - } - ], - "completed_at": "2026-01-26T17:57:21.864Z", - "completed_by": "reception@coachecollision.ca", - "actual_delivery": "2026-01-22T00:50:00.000Z", - "actual_completion": "2026-01-21T22:50:00.000Z", - "removeFromProduction": true - }, - "depreciation_taxes": 0, - "driveable": true, - "employee_body": "f7cbeb13-f193-45bf-ba79-0ad11551b775", - "employee_body_rel": { - "id": "f7cbeb13-f193-45bf-ba79-0ad11551b775", - "first_name": "Steve", - "last_name": "Steele", - "__typename": "employees" - }, - "employee_csr": null, - "employee_csr_rel": null, - "employee_prep": null, - "employee_prep_rel": null, - "employee_refinish": "8535a3bc-0162-4fd8-8efd-48e99f437154", - "employee_refinish_rel": { - "id": "8535a3bc-0162-4fd8-8efd-48e99f437154", - "first_name": "David", - "last_name": "Lam", - "__typename": "employees" - }, - "est_co_nm": null, - "est_ct_fn": "Nuben", - "est_ct_ln": "Suthatharan", - "est_ea": "Nuben@coachecollision.ca", - "est_ph1": null, - "flat_rate_ats": false, - "federal_tax_rate": 0.05, - "hit_and_run": true, - "id": "62fcba91-b8df-4076-b5f5-14f8827c8f92", - "inproduction": false, - "ins_addr1": null, - "ins_city": null, - "ins_co_id": null, - "ins_co_nm": "ICBC", - "ins_ct_fn": null, - "ins_ct_ln": null, - "ins_ea": null, - "ins_ph1": null, - "intakechecklist": { - "ICBC": true, - "form": [ - { - "name": "ICBC", - "type": "checkbox", - "label": "ICBC", - "value": true - }, - { - "name": "PRIVATE", - "type": "checkbox", - "label": "PRIVATE" - }, - { - "name": "TOW-IN", - "type": "checkbox", - "label": "TOW-IN" - }, - { - "name": "How did you hear about us?", - "type": "textarea", - "label": "How did you hear about us?", - "required": false - }, - { - "name": "Were you referred to us?", - "type": "checkbox", - "label": "Were you referred to us?", - "required": false - }, - { - "name": "If YES, By who?", - "type": "textarea", - "label": "If YES, By who?", - "required": false - }, - { - "name": "CUSTOMER INFORMATION", - "type": "text", - "label": "CUSTOMER INFORMATION", - "required": false - }, - { - "name": "Phone Numbers:", - "type": "textarea", - "label": "Phone Numbers:", - "required": false - }, - { - "name": "Cell:", - "type": "textarea", - "label": "Cell:", - "required": false - }, - { - "name": "Text OK?", - "type": "checkbox", - "label": "Text OK?", - "required": false - }, - { - "name": "Email Address:", - "type": "textarea", - "label": "Email Address:", - "required": false - }, - { - "name": "Carry loss of use?", - "type": "checkbox", - "label": "Carry loss of use?", - "required": false - }, - { - "name": "Carry OEM Replacement Cost Policy?", - "type": "checkbox", - "label": "Carry OEM Replacement Cost Policy?", - "required": false - }, - { - "name": "GST Recipiant", - "type": "checkbox", - "label": "GST Recipiant", - "required": false - }, - { - "name": "VEHICLE INFORMATION", - "type": "text", - "label": "VEHICLE INFORMATION", - "required": false - }, - { - "name": "Canapy", - "type": "checkbox", - "label": "Canapy", - "required": false - }, - { - "name": "Keys", - "type": "checkbox", - "label": "Keys", - "required": false - }, - { - "name": "Wheel Locks", - "type": "checkbox", - "label": "Wheel Locks", - "required": false - }, - { - "name": "Alarm", - "type": "checkbox", - "label": "Alarm", - "required": false - }, - { - "name": "Fob", - "type": "checkbox", - "label": "Fob", - "required": false - }, - { - "name": "Dash board waring lights on?", - "type": "textarea", - "label": "Dash board waring lights on?", - "required": false - }, - { - "name": "Passenger in car?", - "type": "textarea", - "label": "Passenger in car?", - "required": false - }, - { - "name": "Interior Damage?", - "type": "textarea", - "label": "Interior Damage?", - "required": false - }, - { - "name": "Any power options not working?", - "type": "textarea", - "label": "Any power options not working?", - "required": false - }, - { - "name": "Did the car hit a curb?", - "type": "textarea", - "label": "Did the car hit a curb?", - "required": false - }, - { - "name": "Anything in the Truck or box of the Truck?", - "type": "textarea", - "label": "Anything in the Truck on box of the Truck?", - "required": false - }, - { - "name": "Old damage discussed with RO", - "type": "textarea", - "label": "Old damage discussed with RO", - "required": false - }, - { - "name": "If we find any dents or dings in repair panels would you like to be notified and given an estimate?", - "type": "checkbox", - "label": "If we find any dents or dings in repair panels would you like to be notified and given an estimate?", - "required": false - }, - { - "name": "Condition of Windshield", - "type": "textarea", - "label": "Condition of Windshield", - "required": false - }, - { - "name": "Notes", - "type": "textarea", - "label": "Notes" - } - ], - "completed_at": "2026-01-09T22:23:41.562Z", - "completed_by": "nuben@coachecollision.ca", - "addToProduction": true, - "production_vars": {}, - "scheduled_delivery": null, - "scheduled_completion": "2026-01-13T22:23:39.311Z" - }, - "invoice_final_note": null, - "iouparent": null, - "job_totals": { - "parts": { - "parts": { - "list": { - "PAA": { - "total": { - "amount": 58102, - "currency": "USD", - "precision": 2 - } - }, - "PAE": { - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "PAN": { - "total": { - "amount": 701000, - "currency": "USD", - "precision": 2 - } - } - }, - "total": { - "amount": 759102, - "currency": "USD", - "precision": 2 - }, - "subtotal": { - "amount": 759102, - "currency": "USD", - "precision": 2 - }, - "prt_dsmk_total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "sublets": { - "total": { - "amount": 35000, - "currency": "USD", - "precision": 2 - }, - "subtotal": { - "amount": 35000, - "currency": "USD", - "precision": 2 - } - } - }, - "rates": { - "la1": { - "rate": 97.8, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "la2": { - "rate": 0, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "la3": { - "rate": 107.35, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "la4": { - "rate": 0, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "laa": { - "rate": 107.35, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "lab": { - "rate": 89.46, - "hours": 5.800000000000001, - "total": { - "amount": 51887, - "currency": "USD", - "precision": 2 - } - }, - "lad": { - "rate": 0, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "lae": { - "rate": 0, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "laf": { - "rate": 102.27, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "lag": { - "rate": 89.46, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "lam": { - "rate": 115.05, - "hours": 0.8, - "total": { - "amount": 9204, - "currency": "USD", - "precision": 2 - } - }, - "lar": { - "rate": 89.46, - "hours": 1.8, - "total": { - "amount": 16103, - "currency": "USD", - "precision": 2 - } - }, - "las": { - "rate": 89.46, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "lau": { - "rate": 0, - "hours": 0, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "mapa": { - "rate": 60.11, - "hours": 1.8, - "total": { - "amount": 10820, - "currency": "USD", - "precision": 2 - } - }, - "mash": { - "rate": 7.05, - "hours": 6.6000000000000005, - "total": { - "amount": 4653, - "currency": "USD", - "precision": 2 - } - }, - "subtotal": { - "amount": 92667, - "currency": "USD", - "precision": 2 - }, - "rates_subtotal": { - "amount": 77194, - "currency": "USD", - "precision": 2 - } - }, - "totals": { - "subtotal": { - "amount": 886769, - "currency": "USD", - "precision": 2 - }, - "local_tax": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "state_tax": { - "amount": 62074, - "currency": "USD", - "precision": 2 - }, - "custPayable": { - "total": { - "amount": 30000, - "currency": "USD", - "precision": 2 - }, - "dep_taxes": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "deductible": { - "amount": 30000, - "currency": "USD", - "precision": 2 - }, - "federal_tax": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "other_customer_amount": { - "amount": 0, - "currency": "USD", - "precision": 2 - } - }, - "federal_tax": { - "amount": 44338, - "currency": "USD", - "precision": 2 - }, - "net_repairs": { - "amount": 963181, - "currency": "USD", - "precision": 2 - }, - "statePartsTax": { - "amount": 55587, - "currency": "USD", - "precision": 2 - }, - "total_repairs": { - "amount": 993181, - "currency": "USD", - "precision": 2 - } - }, - "additional": { - "pvrt": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "total": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "towing": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "storage": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "shipping": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "adjustments": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "additionalCosts": { - "amount": 0, - "currency": "USD", - "precision": 2 - }, - "additionalCostItems": [] - } - }, - "job_watchers": [], - "joblines": [ - { - "act_price": 1172, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 914.17, - "actual_price": 1172, - "bill": { - "id": "a9dda427-a344-4f72-b7a0-3702b7f8c1b6", - "invoice_number": "72821", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "65a1bf5d-a520-4b0a-a1d3-3518de994bc6", - "joblineid": "e64ab0ba-03b1-4b16-991d-b533caa57ef1", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 1.6, - "db_price": 1419, - "db_ref": "202199", - "id": "e64ab0ba-03b1-4b16-991d-b533caa57ef1", - "ioucreated": false, - "lbr_amt": 143.14, - "lbr_op": "OP11", - "line_desc": "L Frt Bumper Side Cover", - "line_ind": "S1", - "line_no": 1, - "line_ref": 14, - "location": null, - "manual_line": false, - "mod_lb_hrs": 1.6, - "mod_lbr_ty": "LAB", - "notes": "ORD OEM JAN 07 ETA 1 - 2 WEEKS", - "oem_partno": "80A 807 107 M GRU", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAN", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 14, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "9c7744db-7f5a-4d16-aa29-3c65d1044fe5", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "CRACKED", - "line_ind": "E", - "line_no": 2, - "line_ref": 14, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 18, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 1.1, - "db_price": 0, - "db_ref": "201285", - "id": "07b44893-092c-4687-a24b-970fc96ff4f3", - "ioucreated": false, - "lbr_amt": 98.41, - "lbr_op": "OP6", - "line_desc": "L Frt Bumper Side Cover", - "line_ind": "S1", - "line_no": 3, - "line_ref": 14, - "location": null, - "manual_line": false, - "mod_lb_hrs": 1.1, - "mod_lbr_ty": "LAR", - "notes": null, - "oem_partno": null, - "op_code_desc": "REFINISH", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 15, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.1, - "db_price": 0, - "db_ref": "201299", - "id": "a6b9007d-d45c-4e7c-9dad-9e141c97a5d2", - "ioucreated": false, - "lbr_amt": 8.95, - "lbr_op": "OP11", - "line_desc": "L Frt Add W/Parallel Park Assist Sensor", - "line_ind": "S1", - "line_no": 4, - "line_ref": 14, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.1, - "mod_lbr_ty": "LAB", - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 17, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 246, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 191.88, - "actual_price": 246, - "bill": { - "id": "a9dda427-a344-4f72-b7a0-3702b7f8c1b6", - "invoice_number": "72821", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "fe9d8dd3-74a0-4e51-bb3a-0f2529613d4d", - "joblineid": "5703a154-9cd8-41b2-b9c6-0e5665948052", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.3, - "db_price": 246, - "db_ref": "200194", - "id": "5703a154-9cd8-41b2-b9c6-0e5665948052", - "ioucreated": false, - "lbr_amt": 26.84, - "lbr_op": "OP11", - "line_desc": "Frt Lwr Bumper Grille", - "line_ind": "E", - "line_no": 5, - "line_ref": 7, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.3, - "mod_lbr_ty": "LAB", - "notes": "ORD OEM JAN 07 ETA LOCAL", - "oem_partno": "80A 807 647 C 9B9", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAN", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 7, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "f1cb3534-949c-4f50-8d48-184483847b90", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "CRACKED", - "line_ind": "E", - "line_no": 6, - "line_ref": 7, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 9, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 1.2, - "db_price": 0, - "db_ref": "201294", - "id": "966d17a3-4575-413c-9e81-3b5aed0c2034", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP2", - "line_desc": "Frt Bumper Cover", - "line_ind": "E", - "line_no": 7, - "line_ref": 7, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": "LAB", - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / INSTALL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 8, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 223.08, - "actual_price": 286, - "bill": { - "id": "92120eaf-8a04-4629-8e6c-082ce2fef717", - "invoice_number": "CM-72821-1", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "4dff060e-54c7-4a46-9349-0d482fdc1196", - "joblineid": "a89761f4-97e2-474b-b981-fa9cede17005", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.3, - "db_price": 286, - "db_ref": "200195", - "id": "a89761f4-97e2-474b-b981-fa9cede17005", - "ioucreated": false, - "lbr_amt": 26.84, - "lbr_op": "OP2", - "line_desc": "Frt Lwr Bumper Plate", - "line_ind": "S1", - "line_no": 8, - "line_ref": 23, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.3, - "mod_lbr_ty": "LAB", - "notes": "ORD OEM JAN 07 ETA LOCAL", - "oem_partno": "80A 807 661 A GRU", - "op_code_desc": "REMOVE / INSTALL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAE", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Returned", - "tax_part": false, - "unq_seq": 23, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 1275, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 994.5, - "actual_price": 1275, - "bill": { - "id": "a9dda427-a344-4f72-b7a0-3702b7f8c1b6", - "invoice_number": "72821", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "c9cec9ef-7b5a-4232-9b69-5ee8e5089e23", - "joblineid": "9a531740-43f4-4181-8406-1c6e64046fa3", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.2, - "db_price": 1275, - "db_ref": "200197", - "id": "9a531740-43f4-4181-8406-1c6e64046fa3", - "ioucreated": false, - "lbr_amt": 17.89, - "lbr_op": "OP11", - "line_desc": "Frt Lwr Bumper Moulding", - "line_ind": "E", - "line_no": 9, - "line_ref": 27, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.2, - "mod_lbr_ty": "LAB", - "notes": "ORD OEM JAN 07 ETA LOCAL", - "oem_partno": "80A 853 765 3Q7", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAN", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 27, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "f7e70ed4-de45-4532-bc8a-d3e541be83d4", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "CRACKED", - "line_ind": "E", - "line_no": 10, - "line_ref": 27, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 28, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 120, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": "AQ518.264", - "assigned_team": null, - "billlines": [ - { - "actual_cost": 96, - "actual_price": 120, - "bill": { - "id": "c2c1850b-494d-4cbf-8083-dff15eadf745", - "invoice_number": "041105", - "vendor": { - "id": "72634cde-8dfa-457c-8c04-08621e712d67", - "name": "KWANTLEN ENTERPRISES LTD", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "42cbbc6e-e8ea-4efb-aa5a-7f92cdff448f", - "joblineid": "176052dd-4c5f-4d64-a520-74194a87fea2", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.2, - "db_price": 163, - "db_ref": "200200", - "id": "176052dd-4c5f-4d64-a520-74194a87fea2", - "ioucreated": false, - "lbr_amt": 17.89, - "lbr_op": "OP11", - "line_desc": "L Frt Bumper Reflector", - "line_ind": "E", - "line_no": 11, - "line_ref": 42, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.2, - "mod_lbr_ty": "LAB", - "notes": "ORD KW JAN 09 ETA JAN 13", - "oem_partno": "80A 945 071 A", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAA", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 42, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "16c263a7-c12d-4b1e-851a-532843ad87ac", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "INSIDE BROEKN -- GLUED ON", - "line_ind": "E", - "line_no": 12, - "line_ref": 42, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 47, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 306.01, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": "AU38-000W-0C", - "assigned_team": null, - "billlines": [ - { - "actual_cost": 174.43, - "actual_price": 306.01, - "bill": { - "id": "a57ce241-e00a-4ba3-9f26-e9e0e5eba1d2", - "invoice_number": "IS2595145", - "vendor": { - "id": "e54b45b4-55ab-4379-8d82-e4007e71622c", - "name": "A.P.T. AUTO PARTS TRADING CO LTD", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "5c9571e2-5abf-45e9-9dc7-896019e538b0", - "joblineid": "bde7f2b2-d2dc-480b-9398-233ada3ebae4", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 407, - "db_ref": "200173", - "id": "bde7f2b2-d2dc-480b-9398-233ada3ebae4", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP11", - "line_desc": "Frt Bumper Impact Absorber", - "line_ind": "E", - "line_no": 13, - "line_ref": 32, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": "LAB", - "notes": "ORD APT JAN 09 ETA JAN 12", - "oem_partno": "80A 807 550 C", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAA", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 32, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "1e82e200-5654-4521-9b1f-5930d24d3a3c", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "CRACKED", - "line_ind": "E", - "line_no": 14, - "line_ref": 32, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 33, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 2690, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 2098.2, - "actual_price": 2690, - "bill": { - "id": "a9dda427-a344-4f72-b7a0-3702b7f8c1b6", - "invoice_number": "72821", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "5420de33-09fd-4ed3-9e4e-604403d88e17", - "joblineid": "78c719ec-dbeb-445d-8d45-ba15d3099edb", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 1, - "db_price": 2690, - "db_ref": "202371", - "id": "78c719ec-dbeb-445d-8d45-ba15d3099edb", - "ioucreated": false, - "lbr_amt": 89.46, - "lbr_op": "OP11", - "line_desc": "Grille Assembly", - "line_ind": "E", - "line_no": 15, - "line_ref": 10, - "location": null, - "manual_line": false, - "mod_lb_hrs": 1, - "mod_lbr_ty": "LAB", - "notes": "ORD OEM JAN 07 ETA LOCAL", - "oem_partno": "80A 853 651 L RP5", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAN", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 10, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "20a9e040-74ad-47a2-993d-dd36f58311a0", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "CRACKED", - "line_ind": "E", - "line_no": 16, - "line_ref": 10, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 13, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.2, - "db_price": 0, - "db_ref": "201373", - "id": "4525e99d-c06a-47df-aa6a-1b14d27697b5", - "ioucreated": false, - "lbr_amt": 17.89, - "lbr_op": "OP11", - "line_desc": "Frt Inr Add w/Parking Sensor", - "line_ind": "E", - "line_no": 17, - "line_ref": 10, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.2, - "mod_lbr_ty": "LAB", - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 12, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 447, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 348.66, - "actual_price": 447, - "bill": { - "id": "9a87a941-da0b-468f-9c99-9a8856a9a2e9", - "invoice_number": "72865", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "1ac66d08-afc3-48c1-a3cf-6ed5611f6558", - "joblineid": "2679e440-4c79-4d70-ac20-6e6dc8ae8770", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.4, - "db_price": 447, - "db_ref": "202383", - "id": "2679e440-4c79-4d70-ac20-6e6dc8ae8770", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP11", - "line_desc": "Grille Plate", - "line_ind": "E", - "line_no": 18, - "line_ref": 29, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": "LAB", - "notes": "ORD OEM JAN 09 ETA JAN 12", - "oem_partno": "80A 853 692 A", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAN", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 29, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "b0a41a8a-f332-409c-bf2b-1924431629a2", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "CRACKED", - "line_ind": "E", - "line_no": 19, - "line_ref": 29, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 31, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 149, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 116.22, - "actual_price": 149, - "bill": { - "id": "9a87a941-da0b-468f-9c99-9a8856a9a2e9", - "invoice_number": "72865", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "39e324fa-06da-487a-9d23-288eb476d103", - "joblineid": "7ba6a3cc-3291-40d6-a258-cf60573fe63f", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.4, - "db_price": 149, - "db_ref": "201241", - "id": "7ba6a3cc-3291-40d6-a258-cf60573fe63f", - "ioucreated": false, - "lbr_amt": 35.78, - "lbr_op": "OP11", - "line_desc": "L Cooling Air Deflector", - "line_ind": "E", - "line_no": 20, - "line_ref": 34, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.4, - "mod_lbr_ty": "LAB", - "notes": "ORD OEM JAN 09 ETA JAN 12", - "oem_partno": "80A 121 283 K", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAN", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 34, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "989d0abc-05ab-409d-9cb2-2ea0ebccb13f", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "CRACKED", - "line_ind": "E", - "line_no": 21, - "line_ref": 34, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 35, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 597, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 465.66, - "actual_price": 597, - "bill": { - "id": "f0cedaf5-e8ac-4fe5-bfd6-3bc38d430d98", - "invoice_number": "72868", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "34baeff0-8481-478b-acf4-4a0772d0ee5e", - "joblineid": "60d8f84e-af42-4013-a967-5bad275844d3", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.8, - "db_price": 597, - "db_ref": "202630", - "id": "60d8f84e-af42-4013-a967-5bad275844d3", - "ioucreated": false, - "lbr_amt": 92.04, - "lbr_op": "OP11", - "line_desc": "L Cooling Auxiliary Radiator -M", - "line_ind": "E", - "line_no": 22, - "line_ref": 36, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.8, - "mod_lbr_ty": "LAM", - "notes": "ORD PERFORMANCE JAN 09 ETA NO STOCK ORD OEM JAN 09 ETA", - "oem_partno": "80A 121 212 E", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAN", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 36, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "9f7a1b49-468f-4156-803c-6cd6083ceec3", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "DAMAGED -- ALPI NO STOCK / PERFORMANCE N", - "line_ind": "E", - "line_no": 23, - "line_ref": 36, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 37, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 149, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 116.22, - "actual_price": 149, - "bill": { - "id": "9a87a941-da0b-468f-9c99-9a8856a9a2e9", - "invoice_number": "72865", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "71ed0c79-3cce-4245-90c3-2869880a785e", - "joblineid": "bb6d3570-b61a-49be-9c33-9df9dc9827be", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 149, - "db_ref": "204303", - "id": "bb6d3570-b61a-49be-9c33-9df9dc9827be", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP11", - "line_desc": "L Cooling Air Duct", - "line_ind": "E", - "line_no": 24, - "line_ref": 38, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": "LAB", - "notes": "ORD OEM JAN 09 ETA JAN 12", - "oem_partno": "80A 121 333 D", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAN", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 38, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "27b2668e-874f-483d-a63a-504aa7633777", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "CRACKED", - "line_ind": "E", - "line_no": 25, - "line_ref": 38, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 39, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 285, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [ - { - "actual_cost": 222.3, - "actual_price": 285, - "bill": { - "id": "9a87a941-da0b-468f-9c99-9a8856a9a2e9", - "invoice_number": "72865", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "d1399be3-a660-4ac6-9eb7-cbcc4e304c78", - "joblineid": "4584e129-aeb7-4ea0-96de-73f0a56c26c7", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.5, - "db_price": 285, - "db_ref": "201577", - "id": "4584e129-aeb7-4ea0-96de-73f0a56c26c7", - "ioucreated": false, - "lbr_amt": 44.73, - "lbr_op": "OP11", - "line_desc": "L Fender Splash Shield", - "line_ind": "E", - "line_no": 26, - "line_ref": 40, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.5, - "mod_lbr_ty": "LAB", - "notes": "ORD OEM JAN 09 ETA JAN 12", - "oem_partno": "80A 853 887 K", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAN", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 40, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "45b16056-b19c-41f0-82ba-5c6ab0404fbd", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "TABS TORN / MISSING PIECES", - "line_ind": "E", - "line_no": 27, - "line_ref": 40, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 41, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 155.01, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": "AU38-219B-2C", - "assigned_team": null, - "billlines": [ - { - "actual_cost": 88.36, - "actual_price": 155.01, - "bill": { - "id": "a57ce241-e00a-4ba3-9f26-e9e0e5eba1d2", - "invoice_number": "IS2595145", - "vendor": { - "id": "e54b45b4-55ab-4379-8d82-e4007e71622c", - "name": "A.P.T. AUTO PARTS TRADING CO LTD", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "4863b4ce-5c1c-4429-89ee-4b08c7c4cdd0", - "joblineid": "d5cc63f7-346d-4246-840d-fe4d1cdb217c", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.3, - "db_price": 241, - "db_ref": "201599", - "id": "d5cc63f7-346d-4246-840d-fe4d1cdb217c", - "ioucreated": false, - "lbr_amt": 26.84, - "lbr_op": "OP11", - "line_desc": "L Frt Body Headlamp Mtg Brkt", - "line_ind": "E", - "line_no": 28, - "line_ref": 44, - "location": "", - "manual_line": false, - "mod_lb_hrs": 0.3, - "mod_lbr_ty": "LAB", - "notes": "ORD APT JAN 09 ETA JAN 12", - "oem_partno": "80A 805 607", - "op_code_desc": "REMOVE / REPLACE", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAA", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 44, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "6df2b618-68ce-4138-a93d-99dbd755612c", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "CRACKED", - "line_ind": "E", - "line_no": 29, - "line_ref": 44, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 46, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0.4, - "db_price": 0, - "db_ref": "201304", - "id": "6281b001-55af-43f2-bb67-75154f396053", - "ioucreated": false, - "lbr_amt": 35.78, - "lbr_op": "OP2", - "line_desc": "L Front Combination Lamp", - "line_ind": "E", - "line_no": 30, - "line_ref": 44, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.4, - "mod_lbr_ty": "LAB", - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / INSTALL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 45, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 46.53, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "936007", - "id": "6efcef5e-6697-426f-998d-9db0208ed164", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP13", - "line_desc": "Shop Materials", - "line_ind": null, - "line_no": 31, - "line_ref": 4, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "ADDITIONAL COSTS", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": true, - "unq_seq": 4, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 108.2, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "936008", - "id": "e29b7c4f-d12c-44f4-84a1-414bb7a0bad5", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP13", - "line_desc": "Paint/Materials", - "line_ind": null, - "line_no": 32, - "line_ref": 6, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "ADDITIONAL COSTS", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": true, - "unq_seq": 6, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "933002", - "id": "4c35520b-9538-4101-b40e-2ec2a52285fc", - "ioucreated": false, - "lbr_amt": 35.78, - "lbr_op": "OP14", - "line_desc": "Clear Coat", - "line_ind": "E", - "line_no": 33, - "line_ref": 19, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.4, - "mod_lbr_ty": "LAR", - "notes": null, - "oem_partno": null, - "op_code_desc": "ADDITIONAL OPERATIONS", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 19, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900500", - "id": "1210db03-3a0d-4893-88ca-411de3d7484c", - "ioucreated": false, - "lbr_amt": 26.84, - "lbr_op": "OP9", - "line_desc": "ICBC POST REPAIR SCAN", - "line_ind": "E", - "line_no": 34, - "line_ref": 2, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.3, - "mod_lbr_ty": "LAB", - "notes": null, - "oem_partno": null, - "op_code_desc": "REPAIR", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAE", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 2, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 50, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": "Sublet", - "assigned_team": null, - "billlines": [ - { - "actual_cost": 0, - "actual_price": 50, - "bill": { - "id": "1a9798cb-1f37-4198-b2f7-a53d3b97eab2", - "invoice_number": "3077", - "vendor": { - "id": "6793335c-98f6-4472-86c5-278bb1459462", - "name": "In House", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "8a0184b4-34e4-4552-93d7-fec5fbda2f9f", - "joblineid": "ea2002b9-41f2-4a50-8757-371e333406b4", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900500", - "id": "ea2002b9-41f2-4a50-8757-371e333406b4", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP26", - "line_desc": "REPAIR PLANNING & DOCUMENTATION", - "line_ind": "E", - "line_no": 35, - "line_ref": 3, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": "LAB", - "notes": null, - "oem_partno": null, - "op_code_desc": "PAINTLESS DENT REPAIR", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAS", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 3, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900500", - "id": "a3954c53-5d55-4163-8d91-373c89453633", - "ioucreated": false, - "lbr_amt": 26.84, - "lbr_op": "OP6", - "line_desc": "TINT COLOUR", - "line_ind": "E", - "line_no": 36, - "line_ref": 5, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0.3, - "mod_lbr_ty": "LAR", - "notes": null, - "oem_partno": null, - "op_code_desc": "REFINISH", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAE", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 5, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 300, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": "Sublet", - "assigned_team": null, - "billlines": [ - { - "actual_cost": 240, - "actual_price": 300, - "bill": { - "id": "b244fa89-c8e8-49ed-bccd-e5bc1ffb891b", - "invoice_number": "169291", - "vendor": { - "id": "377a732f-6285-4937-b110-bc11984541c4", - "name": "CAPILANO AUDI INC.", - "__typename": "vendors" - }, - "__typename": "bills" - }, - "id": "225ad893-6285-46a5-a2cb-82735a4edf21", - "joblineid": "7a9fb964-807a-4b4c-8b6a-715462b2ac45", - "quantity": 1, - "__typename": "billlines" - } - ], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900500", - "id": "7a9fb964-807a-4b4c-8b6a-715462b2ac45", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP26", - "line_desc": "ADAS CALIBRATION STATIC", - "line_ind": "E", - "line_no": 37, - "line_ref": 21, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": "LAB", - "notes": null, - "oem_partno": null, - "op_code_desc": "PAINTLESS DENT REPAIR", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": "PAS", - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": "Received", - "tax_part": true, - "unq_seq": 21, - "include_in_part_cnt": false, - "__typename": "joblines" - }, - { - "act_price": 0, - "act_price_before_ppc": null, - "ah_detail_line": false, - "alt_partm": null, - "alt_partno": null, - "assigned_team": null, - "billlines": [], - "convertedtolbr": false, - "critical": false, - "db_hrs": 0, - "db_price": 0, - "db_ref": "900501", - "id": "5eafbf12-5502-40a0-8b23-e6376acbfc17", - "ioucreated": false, - "lbr_amt": 0, - "lbr_op": "OP0", - "line_desc": "Adaptive Cruise Control -- ADAS OEM REQU", - "line_ind": "E", - "line_no": 38, - "line_ref": 21, - "location": null, - "manual_line": false, - "mod_lb_hrs": 0, - "mod_lbr_ty": null, - "notes": null, - "oem_partno": null, - "op_code_desc": "REMOVE / REPLACE PARTIAL", - "parts_dispatch_lines": [], - "part_qty": 1, - "part_type": null, - "prt_dsmk_m": 0, - "prt_dsmk_p": 0, - "status": null, - "tax_part": false, - "unq_seq": 22, - "include_in_part_cnt": false, - "__typename": "joblines" - } - ], - "kmin": 23959, - "kmout": null, - "labor_rate_desc": "EST", - "lbr_adjustments": {}, - "local_tax_rate": null, - "loss_date": "2025-12-23", - "loss_desc": "Other", - "loss_of_use": "RS+ ", - "lost_sale_reason": null, - "materials": { - "mapa": { - "cal_maxdlr": 9999.99, - "cal_opcode": "OP13" - }, - "mash": { - "cal_maxdlr": 9999.99, - "cal_opcode": "OP13" - } - }, - "other_amount_payable": null, - "owner": { - "id": "20615805-d911-4998-aa38-d0fcd08e49b2", - "note": null, - "ownr_addr1": "6500 CHATSWORTH RD", - "ownr_addr2": null, - "ownr_city": "RICHMOND", - "ownr_co_nm": null, - "ownr_ctry": null, - "ownr_ea": "fengwenilucy@gmail.com", - "ownr_fn": "KUN", - "ownr_ln": "LIN", - "ownr_ph1": "6043796180", - "ownr_ph2": "7789556180", - "ownr_ph1_ty": null, - "ownr_ph2_ty": null, - "ownr_st": "BC", - "ownr_zip": "V7C 3S3", - "tax_number": null, - "__typename": "owners" - }, - "ownerid": "20615805-d911-4998-aa38-d0fcd08e49b2", - "owner_owing": 300, - "ownr_addr1": "6500 CHATSWORTH RD", - "ownr_addr2": null, - "ownr_city": "RICHMOND", - "ownr_co_nm": null, - "ownr_ctry": null, - "ownr_ea": "fengwenilucy@gmail.com", - "ownr_fn": "KUN", - "ownr_ln": "LIN", - "ownr_ph1": "6043796180", - "ownr_ph2": "7789556180", - "ownr_ph1_ty": null, - "ownr_ph2_ty": null, - "ownr_st": "BC", - "ownr_zip": "V7C 3S3", - "parts_tax_rates": { - "CCC": {}, - "CCD": {}, - "CCF": {}, - "CCM": {}, - "PAA": { - "prt_type": "PAA", - "prt_discp": 0, - "prt_mktyp": false, - "prt_mkupp": 0, - "prt_tax_in": true, - "prt_tax_rt": 0.07 - }, - "PAC": { - "prt_type": "PAC", - "prt_discp": 0, - "prt_mktyp": false, - "prt_mkupp": 0, - "prt_tax_in": true, - "prt_tax_rt": 0.07 - }, - "PAG": {}, - "PAL": { - "prt_type": "PAL", - "prt_discp": 0, - "prt_mktyp": false, - "prt_mkupp": 0, - "prt_tax_in": true, - "prt_tax_rt": 0.07 - }, - "PAM": { - "prt_type": "PAM", - "prt_discp": 0, - "prt_mktyp": false, - "prt_mkupp": 0, - "prt_tax_in": true, - "prt_tax_rt": 0.07 - }, - "PAN": { - "prt_type": "PAN", - "prt_discp": 0, - "prt_mktyp": false, - "prt_mkupp": 0, - "prt_tax_in": true, - "prt_tax_rt": 0.07 - }, - "PAO": {}, - "PAP": {}, - "PAR": { - "prt_type": "PAR", - "prt_discp": 0, - "prt_mktyp": false, - "prt_mkupp": 0, - "prt_tax_in": true, - "prt_tax_rt": 0.07 - }, - "PAS": { - "prt_type": "PAS", - "prt_discp": 0, - "prt_mktyp": false, - "prt_mkupp": 0, - "prt_tax_in": true, - "prt_tax_rt": 0.07 - }, - "CCDR": {}, - "PASL": {} - }, - "payments": [ - { - "amount": 300, - "created_at": "2026-01-22T00:53:01.352735+00:00", - "date": "2026-01-21", - "exportedat": "2026-01-22T23:29:19.294+00:00", - "id": "98088180-cc9f-469e-b1a1-586775aae73a", - "jobid": "62fcba91-b8df-4076-b5f5-14f8827c8f92", - "memo": null, - "payer": "Customer", - "paymentnum": "2489", - "transactionid": "075921", - "type": "Visa", - "__typename": "payments" - } - ], - "plate_no": "HW815N", - "plate_st": "BC", - "po_number": null, - "policy_no": "5L.LHS", - "production_vars": { - "note": null - }, - "rate_ats": null, - "rate_ats_flat": null, - "rate_la1": 97.8, - "rate_la2": 0, - "rate_la3": 107.35, - "rate_la4": 0, - "rate_laa": 107.35, - "rate_lab": 89.46, - "rate_lad": null, - "rate_lae": null, - "rate_laf": 102.27, - "rate_lag": 89.46, - "rate_lam": 115.05, - "rate_lar": 89.46, - "rate_las": 89.46, - "rate_lau": 0, - "rate_ma2s": 0, - "rate_ma2t": 0, - "rate_ma3s": 0, - "rate_mabl": null, - "rate_macs": 0, - "rate_mahw": 0, - "rate_mapa": 60.11, - "rate_mash": 7.05, - "rate_matd": null, - "referral_source": null, - "referral_source_extra": null, - "regie_number": "12984170", - "remove_from_ar": false, - "ro_number": "58117", - "scheduled_completion": "2026-01-21T22:15:39.3+00:00", - "scheduled_delivery": "2026-01-21T08:00:00+00:00", - "scheduled_in": null, - "selling_dealer": null, - "estimate_approved": "2026-01-20T19:33:34.829+00:00", - "estimate_sent_approval": "2026-01-09T23:17:39.023+00:00", - "selling_dealer_contact": null, - "servicing_dealer": null, - "servicing_dealer_contact": null, - "special_coverage_policy": false, - "state_tax_rate": null, - "status": "Exported", - "storage_payable": null, - "suspended": false, - "tax_lbr_rt": 0.07, - "tax_levies_rt": 0.07, - "tax_paint_mat_rt": 0.07, - "tax_registration_number": null, - "tax_shop_mat_rt": 0.07, - "tax_str_rt": 0.07, - "tax_sub_rt": 0.07, - "tax_tow_rt": 0.07, - "tlos_ind": false, - "towin": false, - "towing_payable": null, - "unit_number": null, - "updated_at": "2026-01-26T19:19:12.944388+00:00", - "v_color": "Navarra Blue", - "v_make_desc": "Audi", - "v_model_yr": "18", - "v_model_desc": "SQ5", - "v_vin": "WA1A4AFY5J2082044", - "notes": [], - "vehicle": { - "id": "5a834070-9cf4-420d-90a4-08df96303af4", - "jobs": [ - { - "clm_no": "DA37868-0-A", - "id": "62fcba91-b8df-4076-b5f5-14f8827c8f92", - "ro_number": "58117", - "status": "Exported", - "__typename": "jobs" - } - ], - "notes": null, - "plate_no": "HW815N", - "plate_st": "BC", - "v_color": "LX5H Navarra Blue", - "v_make_desc": "Audi", - "v_model_yr": "18", - "v_model_desc": "SQ5", - "v_paint_codes": { - "paint_cd1": "LX5H Navarra Blue", - "paint_cd2": null - }, - "v_vin": "WA1A4AFY5J2082044", - "__typename": "vehicles" - }, - "vehicleid": "5a834070-9cf4-420d-90a4-08df96303af4", - "voided": false, - "__typename": "jobs" } \ No newline at end of file diff --git a/server/ai/bill-ocr/bill-ocr-normalize.js b/server/ai/bill-ocr/bill-ocr-normalize.js index c519f654a..7e39949a6 100644 --- a/server/ai/bill-ocr/bill-ocr-normalize.js +++ b/server/ai/bill-ocr/bill-ocr-normalize.js @@ -39,9 +39,9 @@ function normalizeLabelName(labelText) { 'part_num': standardizedFieldsnames.part_no, 'part_number': standardizedFieldsnames.part_no, 'price': standardizedFieldsnames.actual_price, - 'unit_price': standardizedFieldsnames.actual_price, 'amount': standardizedFieldsnames.actual_price, 'list_price': standardizedFieldsnames.actual_price, + 'unit_price': standardizedFieldsnames.actual_price, 'list': standardizedFieldsnames.actual_price, 'retail_price': standardizedFieldsnames.actual_price, 'net': standardizedFieldsnames.actual_cost, @@ -145,6 +145,7 @@ function extractInvoiceData(textractResponse) { if (lineItemGroup.LineItems) { lineItemGroup.LineItems.forEach(lineItem => { const item = {}; + const fieldNameCounts = {}; // Track field name occurrences if (lineItem.LineItemExpenseFields) { lineItem.LineItemExpenseFields.forEach(field => { @@ -155,7 +156,14 @@ function extractInvoiceData(textractResponse) { if (fieldType && fieldValue) { // Normalize field names - const normalizedField = normalizeFieldName(fieldType); + let normalizedField = normalizeFieldName(fieldType); + + // Ensure uniqueness by appending a counter if the field already exists + if (item.hasOwnProperty(normalizedField)) { + fieldNameCounts[normalizedField] = (fieldNameCounts[normalizedField] || 1) + 1; + normalizedField = `${normalizedField}_${fieldNameCounts[normalizedField]}`; + } + item[normalizedField] = { value: fieldValue, label: fieldLabel, diff --git a/server/ai/bill-ocr/bill-ocr.js b/server/ai/bill-ocr/bill-ocr.js index cba3af558..ce8f9cae9 100644 --- a/server/ai/bill-ocr/bill-ocr.js +++ b/server/ai/bill-ocr/bill-ocr.js @@ -62,7 +62,19 @@ async function handleBillOcr(request, response) { // The uploaded file is available in request.file const uploadedFile = request.file; - const { jobid, bodyshopid, parts_orderid } = request.body; + const { jobid, bodyshopid, partsorderid, skipTextract } = request.body; + + + if (skipTextract === 'true') { + console.log('Skipping Textract processing as per request'); + response.status(200).send({ + success: true, + status: 'COMPLETED', + data: await generateBillFormData({ processedData: null, jobid, bodyshopid, partsorderid }), //This is broken if the processedData is not overwritten in the function for testing. + message: 'Invoice processing completed' + }); + return; + } try { const fileType = getFileType(uploadedFile); @@ -71,12 +83,12 @@ async function handleBillOcr(request, response) { // Images are always processed synchronously (single page) if (fileType === 'image') { console.log('Image => 1 page, processing synchronously'); - const result = await processSinglePageDocument(uploadedFile.buffer); - + const processedData = await processSinglePageDocument(uploadedFile.buffer); + const billForm = await generateBillFormData({ processedData: processedData, jobid, bodyshopid, partsorderid }); response.status(200).send({ success: true, status: 'COMPLETED', - data: result, + data: { ...processedData, billForm }, message: 'Invoice processing completed' }); } else if (fileType === 'pdf') { @@ -87,13 +99,13 @@ async function handleBillOcr(request, response) { if (pageCount === 1) { // Process synchronously for single-page documents console.log('PDF => 1 page, processing synchronously'); - const result = await processSinglePageDocument(uploadedFile.buffer); - + const processedData = await processSinglePageDocument(uploadedFile.buffer); + const billForm = await generateBillFormData({ processedData: processedData, jobid, bodyshopid, partsorderid }); //const billResult = await generateBillFormData({ result, }); response.status(200).send({ success: true, status: 'COMPLETED', - data: { result, }, + data: { ...processedData, billForm }, message: 'Invoice processing completed' }); } else { @@ -142,9 +154,13 @@ async function handleBillOcrStatus(request, response) { } if (jobStatus.status === 'COMPLETED') { + //TODO: This needs to be stored in the redis cache and pulled when it's processed. + //const billForm = await generateBillFormData({ jobid, bodyshopid, partsorderid }); + response.status(200).send({ status: 'COMPLETED', data: jobStatus.data + // data: { ...jobStatus.data, billForm } }); } else if (jobStatus.status === 'FAILED') { response.status(500).send({