IO-3515 additional cleanup, translations
This commit is contained in:
@@ -3015,6 +3015,48 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>errors</name>
|
<name>errors</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>calculating_totals</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>calculating_totals_generic</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>creating</name>
|
<name>creating</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -3642,6 +3684,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>generic_failure</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>multipage</name>
|
<name>multipage</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ function BillEnterAiScan({
|
|||||||
formdata.append("jobid", billEnterModal.context.job?.id);
|
formdata.append("jobid", billEnterModal.context.job?.id);
|
||||||
formdata.append("bodyshopid", bodyshop.id);
|
formdata.append("bodyshopid", bodyshop.id);
|
||||||
formdata.append("partsorderid", billEnterModal.context.parts_order?.id);
|
formdata.append("partsorderid", billEnterModal.context.parts_order?.id);
|
||||||
//formdata.append("skipTextract", "true"); // For testing purposes
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data, status } = await axios.post("/ai/bill-ocr", formdata);
|
const { data, status } = await axios.post("/ai/bill-ocr", formdata);
|
||||||
@@ -147,7 +146,7 @@ function BillEnterAiScan({
|
|||||||
setScanLoading(false);
|
setScanLoading(false);
|
||||||
notification.error({
|
notification.error({
|
||||||
title: t("bills.labels.ai.scanfailed"),
|
title: t("bills.labels.ai.scanfailed"),
|
||||||
message: error.response?.data?.message || error.message || "Failed to process invoice"
|
message: error.response?.data?.message || error.message || t("bills.labels.ai.generic_failure")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
create_ppc,
|
create_ppc,
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
original_actual_price,
|
original_actual_price,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
confidence,
|
||||||
...restI
|
...restI
|
||||||
} = i;
|
} = i;
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ export function BillFormComponent({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log("*** Form Watch - jobid changed:", jobIdFormWatch);
|
// When the jobid is set by AI scan, we need to reload the lines. This prevents having to hoist the apollo query.
|
||||||
if (jobIdFormWatch !== null) {
|
if (jobIdFormWatch !== null) {
|
||||||
if (form.getFieldValue("jobid") !== null && form.getFieldValue("jobid") !== undefined) {
|
if (form.getFieldValue("jobid") !== null && form.getFieldValue("jobid") !== undefined) {
|
||||||
loadLines({ variables: { id: form.getFieldValue("jobid") } });
|
loadLines({ variables: { id: form.getFieldValue("jobid") } });
|
||||||
@@ -399,8 +399,8 @@ export function BillFormComponent({
|
|||||||
totals = CalculateBillTotal(values);
|
totals = CalculateBillTotal(values);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notification.error({
|
notification.error({
|
||||||
title: "Error calculating totals",
|
title: t("bills.errors.calculating_totals"),
|
||||||
message: error.message || "An error occurred while calculating bill totals.",
|
message: error.message || t("bills.errors.calculating_totals_generic"),
|
||||||
key: "bill_totals_calculation_error"
|
key: "bill_totals_calculation_error"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,6 +192,8 @@
|
|||||||
"return": "Return Items"
|
"return": "Return Items"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"calculating_totals": "Error Calculating Totals",
|
||||||
|
"calculating_totals_generic": "Please ensure all fields are properly completed. ",
|
||||||
"creating": "Error adding bill. {{error}}",
|
"creating": "Error adding bill. {{error}}",
|
||||||
"deleting": "Error deleting bill. {{error}}",
|
"deleting": "Error deleting bill. {{error}}",
|
||||||
"existinginventoryline": "This bill cannot be deleted as it is tied to items in inventory.",
|
"existinginventoryline": "This bill cannot be deleted as it is tied to items in inventory.",
|
||||||
@@ -228,6 +230,7 @@
|
|||||||
"overall": "Overall"
|
"overall": "Overall"
|
||||||
},
|
},
|
||||||
"disclaimer_title": "AI Scan Beta Disclaimer",
|
"disclaimer_title": "AI Scan Beta Disclaimer",
|
||||||
|
"generic_failure": "Failed to process invoice.",
|
||||||
"multipage": "The is a multi-page document. Processing will take a few moments.",
|
"multipage": "The is a multi-page document. Processing will take a few moments.",
|
||||||
"processing": "Analyzing Bill",
|
"processing": "Analyzing Bill",
|
||||||
"scan": "AI Bill Scanner",
|
"scan": "AI Bill Scanner",
|
||||||
|
|||||||
@@ -192,6 +192,8 @@
|
|||||||
"return": ""
|
"return": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"calculating_totals": "",
|
||||||
|
"calculating_totals_generic": "",
|
||||||
"creating": "",
|
"creating": "",
|
||||||
"deleting": "",
|
"deleting": "",
|
||||||
"existinginventoryline": "",
|
"existinginventoryline": "",
|
||||||
@@ -228,6 +230,7 @@
|
|||||||
"overall": ""
|
"overall": ""
|
||||||
},
|
},
|
||||||
"disclaimer_title": "",
|
"disclaimer_title": "",
|
||||||
|
"generic_failure": "",
|
||||||
"multipage": "",
|
"multipage": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"scan": "",
|
"scan": "",
|
||||||
|
|||||||
@@ -192,6 +192,8 @@
|
|||||||
"return": ""
|
"return": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"calculating_totals": "",
|
||||||
|
"calculating_totals_generic": "",
|
||||||
"creating": "",
|
"creating": "",
|
||||||
"deleting": "",
|
"deleting": "",
|
||||||
"existinginventoryline": "",
|
"existinginventoryline": "",
|
||||||
@@ -228,6 +230,7 @@
|
|||||||
"overall": ""
|
"overall": ""
|
||||||
},
|
},
|
||||||
"disclaimer_title": "",
|
"disclaimer_title": "",
|
||||||
|
"generic_failure": "",
|
||||||
"multipage": "",
|
"multipage": "",
|
||||||
"processing": "",
|
"processing": "",
|
||||||
"scan": "",
|
"scan": "",
|
||||||
|
|||||||
@@ -19,93 +19,97 @@ const normalizePrice = (str) => {
|
|||||||
if (typeof str !== 'string') return str;
|
if (typeof str !== 'string') return str;
|
||||||
return str.replace(/[^0-9.-]+/g, "");
|
return str.replace(/[^0-9.-]+/g, "");
|
||||||
};
|
};
|
||||||
const normalizePriceFinal = (str) => {
|
|
||||||
if (typeof str !== 'string') {
|
|
||||||
// If it's already a number, format to 2 decimals
|
|
||||||
const num = parseFloat(str);
|
|
||||||
return isNaN(num) ? 0 : num;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, try to extract valid decimal number patterns (e.g., "123.45")
|
//More complex function. Not necessary at the moment, keeping for reference.
|
||||||
const decimalPattern = /\d+\.\d{1,2}/g;
|
// const normalizePriceFinal = (str) => {
|
||||||
const decimalMatches = str.match(decimalPattern);
|
// if (typeof str !== 'string') {
|
||||||
|
// // If it's already a number, format to 2 decimals
|
||||||
|
// const num = parseFloat(str);
|
||||||
|
// return isNaN(num) ? 0 : num;
|
||||||
|
// }
|
||||||
|
|
||||||
if (decimalMatches && decimalMatches.length > 0) {
|
// // First, try to extract valid decimal number patterns (e.g., "123.45")
|
||||||
// Found valid decimal number(s)
|
// const decimalPattern = /\d+\.\d{1,2}/g;
|
||||||
const numbers = decimalMatches.map(m => parseFloat(m)).filter(n => !isNaN(n) && n > 0);
|
// const decimalMatches = str.match(decimalPattern);
|
||||||
|
|
||||||
if (numbers.length === 1) {
|
// if (decimalMatches && decimalMatches.length > 0) {
|
||||||
return numbers[0];
|
// // Found valid decimal number(s)
|
||||||
}
|
// const numbers = decimalMatches.map(m => parseFloat(m)).filter(n => !isNaN(n) && n > 0);
|
||||||
|
|
||||||
if (numbers.length > 1) {
|
// if (numbers.length === 1) {
|
||||||
// Check if all numbers are the same (e.g., "47.57.47.57" -> [47.57, 47.57])
|
// return numbers[0];
|
||||||
const uniqueNumbers = [...new Set(numbers)];
|
// }
|
||||||
if (uniqueNumbers.length === 1) {
|
|
||||||
return uniqueNumbers[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if numbers are very close (within 1% tolerance)
|
// if (numbers.length > 1) {
|
||||||
const avg = numbers.reduce((a, b) => a + b, 0) / numbers.length;
|
// // Check if all numbers are the same (e.g., "47.57.47.57" -> [47.57, 47.57])
|
||||||
const allClose = numbers.every(num => Math.abs(num - avg) / avg < 0.01);
|
// const uniqueNumbers = [...new Set(numbers)];
|
||||||
|
// if (uniqueNumbers.length === 1) {
|
||||||
|
// return uniqueNumbers[0];
|
||||||
|
// }
|
||||||
|
|
||||||
if (allClose) {
|
// // Check if numbers are very close (within 1% tolerance)
|
||||||
return avg;
|
// const avg = numbers.reduce((a, b) => a + b, 0) / numbers.length;
|
||||||
}
|
// const allClose = numbers.every(num => Math.abs(num - avg) / avg < 0.01);
|
||||||
|
|
||||||
// Return the first number (most likely correct)
|
// if (allClose) {
|
||||||
return numbers[0];
|
// return avg;
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback: Split on common delimiters and extract all potential numbers
|
// // Return the first number (most likely correct)
|
||||||
const parts = str.split(/[\/|\\,;]/).map(part => part.trim()).filter(part => part.length > 0);
|
// return numbers[0];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
if (parts.length > 1) {
|
// // Fallback: Split on common delimiters and extract all potential numbers
|
||||||
// Multiple values detected - extract and parse all valid numbers
|
// const parts = str.split(/[\/|\\,;]/).map(part => part.trim()).filter(part => part.length > 0);
|
||||||
const numbers = parts
|
|
||||||
.map(part => {
|
|
||||||
const cleaned = part.replace(/[^0-9.-]+/g, "");
|
|
||||||
const parsed = parseFloat(cleaned);
|
|
||||||
return isNaN(parsed) ? null : parsed;
|
|
||||||
})
|
|
||||||
.filter(num => num !== null && num > 0);
|
|
||||||
|
|
||||||
if (numbers.length === 0) {
|
// if (parts.length > 1) {
|
||||||
// No valid numbers found, try fallback to basic cleaning
|
// // Multiple values detected - extract and parse all valid numbers
|
||||||
const cleaned = str.replace(/[^0-9.-]+/g, "");
|
// const numbers = parts
|
||||||
const parsed = parseFloat(cleaned);
|
// .map(part => {
|
||||||
return isNaN(parsed) ? 0 : parsed;
|
// const cleaned = part.replace(/[^0-9.-]+/g, "");
|
||||||
}
|
// const parsed = parseFloat(cleaned);
|
||||||
|
// return isNaN(parsed) ? null : parsed;
|
||||||
|
// })
|
||||||
|
// .filter(num => num !== null && num > 0);
|
||||||
|
|
||||||
if (numbers.length === 1) {
|
// if (numbers.length === 0) {
|
||||||
return numbers[0];
|
// // No valid numbers found, try fallback to basic cleaning
|
||||||
}
|
// const cleaned = str.replace(/[^0-9.-]+/g, "");
|
||||||
|
// const parsed = parseFloat(cleaned);
|
||||||
|
// return isNaN(parsed) ? 0 : parsed;
|
||||||
|
// }
|
||||||
|
|
||||||
// Multiple valid numbers
|
// if (numbers.length === 1) {
|
||||||
const uniqueNumbers = [...new Set(numbers)];
|
// return numbers[0];
|
||||||
|
// }
|
||||||
|
|
||||||
if (uniqueNumbers.length === 1) {
|
// // Multiple valid numbers
|
||||||
return uniqueNumbers[0];
|
// const uniqueNumbers = [...new Set(numbers)];
|
||||||
}
|
|
||||||
|
|
||||||
// Check if numbers are very close (within 1% tolerance)
|
// if (uniqueNumbers.length === 1) {
|
||||||
const avg = numbers.reduce((a, b) => a + b, 0) / numbers.length;
|
// return uniqueNumbers[0];
|
||||||
const allClose = numbers.every(num => Math.abs(num - avg) / avg < 0.01);
|
// }
|
||||||
|
|
||||||
if (allClose) {
|
// // Check if numbers are very close (within 1% tolerance)
|
||||||
return avg;
|
// const avg = numbers.reduce((a, b) => a + b, 0) / numbers.length;
|
||||||
}
|
// const allClose = numbers.every(num => Math.abs(num - avg) / avg < 0.01);
|
||||||
|
|
||||||
|
// if (allClose) {
|
||||||
|
// return avg;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Return the first valid number
|
||||||
|
// return numbers[0];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Single value or no delimiters, clean normally
|
||||||
|
// const cleaned = str.replace(/[^0-9.-]+/g, "");
|
||||||
|
// const parsed = parseFloat(cleaned);
|
||||||
|
// return isNaN(parsed) ? 0 : parsed;
|
||||||
|
// };
|
||||||
|
|
||||||
// Return the first valid number
|
|
||||||
return numbers[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single value or no delimiters, clean normally
|
|
||||||
const cleaned = str.replace(/[^0-9.-]+/g, "");
|
|
||||||
const parsed = parseFloat(cleaned);
|
|
||||||
return isNaN(parsed) ? 0 : parsed;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper function to calculate Textract OCR confidence (0-100%)
|
// Helper function to calculate Textract OCR confidence (0-100%)
|
||||||
const calculateTextractConfidence = (textractLineItem) => {
|
const calculateTextractConfidence = (textractLineItem) => {
|
||||||
@@ -149,6 +153,7 @@ const calculateTextractConfidence = (textractLineItem) => {
|
|||||||
else if (field.normalizedLabel === standardizedFieldsnames.quantity) {
|
else if (field.normalizedLabel === standardizedFieldsnames.quantity) {
|
||||||
weight = 3.5;
|
weight = 3.5;
|
||||||
}
|
}
|
||||||
|
// We generally ignore the key from textract. Keeping for future reference.
|
||||||
// else if (key === 'ITEM' || key === 'PRODUCT_CODE') {
|
// else if (key === 'ITEM' || key === 'PRODUCT_CODE') {
|
||||||
// weight = 3; // Description and part number are most important
|
// weight = 3; // Description and part number are most important
|
||||||
// } else if (key === 'PRICE' || key === 'UNIT_PRICE' || key === 'QUANTITY') {
|
// } else if (key === 'PRICE' || key === 'UNIT_PRICE' || key === 'QUANTITY') {
|
||||||
@@ -179,7 +184,6 @@ const calculateTextractConfidence = (textractLineItem) => {
|
|||||||
return Math.round(avgConfidence * 100) / 100; // Round to 2 decimal places
|
return Math.round(avgConfidence * 100) / 100; // Round to 2 decimal places
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to calculate match confidence score (0-100%)
|
|
||||||
const calculateMatchConfidence = (matches, bestMatch) => {
|
const calculateMatchConfidence = (matches, bestMatch) => {
|
||||||
if (!matches || matches.length === 0 || !bestMatch) {
|
if (!matches || matches.length === 0 || !bestMatch) {
|
||||||
return 0; // No match = 0% confidence
|
return 0; // No match = 0% confidence
|
||||||
@@ -217,7 +221,6 @@ const calculateMatchConfidence = (matches, bestMatch) => {
|
|||||||
return Math.max(matchConfidence, 1);
|
return Math.max(matchConfidence, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function to calculate overall confidence combining OCR and match confidence
|
|
||||||
const calculateOverallConfidence = (ocrConfidence, matchConfidence) => {
|
const calculateOverallConfidence = (ocrConfidence, matchConfidence) => {
|
||||||
// If there's no match, OCR confidence doesn't matter much
|
// If there's no match, OCR confidence doesn't matter much
|
||||||
if (matchConfidence === 0) {
|
if (matchConfidence === 0) {
|
||||||
@@ -318,7 +321,7 @@ async function generateBillFormData({ processedData, jobid: jobidFromProps, body
|
|||||||
|
|
||||||
}
|
}
|
||||||
`, {
|
`, {
|
||||||
jobid, // TODO: Refactor back in parts orders
|
jobid, // TODO: Parts order IDs are currently ignore. If receving a parts order, it could be used to more precisely match to joblines.
|
||||||
});
|
});
|
||||||
|
|
||||||
//Create fuses of line descriptions for matching.
|
//Create fuses of line descriptions for matching.
|
||||||
@@ -378,10 +381,8 @@ async function generateBillFormData({ processedData, jobid: jobidFromProps, body
|
|||||||
if (!job) {
|
if (!job) {
|
||||||
throw new Error('Job not found for bill form data generation.');
|
throw new Error('Job not found for bill form data generation.');
|
||||||
}
|
}
|
||||||
//Figure out which lines have a match and which don't.
|
|
||||||
|
|
||||||
//TODO: How do we handle freight lines and core charges?
|
//TODO: How do we handle freight lines and core charges?
|
||||||
|
|
||||||
//Create the form data structure for the bill posting screen.
|
//Create the form data structure for the bill posting screen.
|
||||||
const billFormData = {
|
const billFormData = {
|
||||||
"jobid": jobid,
|
"jobid": jobid,
|
||||||
@@ -392,10 +393,10 @@ async function generateBillFormData({ processedData, jobid: jobidFromProps, body
|
|||||||
"total": normalizePrice(processedData.summary?.INVOICE_TOTAL?.value || processedData.summary?.TOTAL?.value),
|
"total": normalizePrice(processedData.summary?.INVOICE_TOTAL?.value || processedData.summary?.TOTAL?.value),
|
||||||
"billlines": joblineMatches.map(jlMatchLine => {
|
"billlines": joblineMatches.map(jlMatchLine => {
|
||||||
const { matches, textractLineItem, } = jlMatchLine
|
const { matches, textractLineItem, } = jlMatchLine
|
||||||
//Matches should be prioritized, take the first one.
|
//Matches should be pre-sorted, take the first one.
|
||||||
const matchToUse = matches.length > 0 ? matches[0] : null;
|
const matchToUse = matches.length > 0 ? matches[0] : null;
|
||||||
|
|
||||||
// Calculate confidence scores (0-100%)
|
// Calculate confidence scores
|
||||||
const ocrConfidence = calculateTextractConfidence(textractLineItem);
|
const ocrConfidence = calculateTextractConfidence(textractLineItem);
|
||||||
const matchConfidence = calculateMatchConfidence(matches, matchToUse);
|
const matchConfidence = calculateMatchConfidence(matches, matchToUse);
|
||||||
const overallConfidence = calculateOverallConfidence(ocrConfidence, matchConfidence);
|
const overallConfidence = calculateOverallConfidence(ocrConfidence, matchConfidence);
|
||||||
@@ -452,7 +453,7 @@ async function generateBillFormData({ processedData, jobid: jobidFromProps, body
|
|||||||
//TODO: Do we need to verify the lines to see if it is a unit price or total price (i.e. quantity * price)
|
//TODO: Do we need to verify the lines to see if it is a unit price or total price (i.e. quantity * price)
|
||||||
const lineObject = {
|
const lineObject = {
|
||||||
"line_desc": matchToUse?.item?.line_desc || textractLineItem.ITEM?.value || "NO DESCRIPTION",
|
"line_desc": matchToUse?.item?.line_desc || textractLineItem.ITEM?.value || "NO DESCRIPTION",
|
||||||
"quantity": textractLineItem.QUANTITY?.value, // convert to integer?
|
"quantity": textractLineItem.QUANTITY?.value,
|
||||||
"actual_price": normalizePrice(actualPrice),
|
"actual_price": normalizePrice(actualPrice),
|
||||||
"actual_cost": normalizePrice(actualCost),
|
"actual_cost": normalizePrice(actualCost),
|
||||||
"cost_center": matchToUse?.item?.part_type
|
"cost_center": matchToUse?.item?.part_type
|
||||||
@@ -470,7 +471,6 @@ async function generateBillFormData({ processedData, jobid: jobidFromProps, body
|
|||||||
},
|
},
|
||||||
"joblineid": matchToUse?.item?.id || "noline",
|
"joblineid": matchToUse?.item?.id || "noline",
|
||||||
"confidence": `T${overallConfidence} - O${ocrConfidence} - J${matchConfidence}`
|
"confidence": `T${overallConfidence} - O${ocrConfidence} - J${matchConfidence}`
|
||||||
|
|
||||||
}
|
}
|
||||||
return lineObject
|
return lineObject
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const PDFDocument = require('pdf-lib').PDFDocument;
|
const PDFDocument = require('pdf-lib').PDFDocument;
|
||||||
const TEXTRACT_REDIS_PREFIX = `textract:${process.env?.NODE_ENV === "production" ? "PROD" : "TEST"}`
|
const TEXTRACT_REDIS_PREFIX = `textract:${process.env?.NODE_ENV}`
|
||||||
const TEXTRACT_JOB_TTL = 10 * 60;
|
const TEXTRACT_JOB_TTL = 10 * 60;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,4 @@ Required Infrastructure setup
|
|||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
* Create a rome bucket for uploads, or move to the regular spot.
|
* Create a rome bucket for uploads, or move to the regular spot.
|
||||||
* How to implement this across environments.
|
* Add environment variables.
|
||||||
* How to prevent polling for a job that may have errored.
|
|
||||||
* Handling of HEIC files on upload.
|
|
||||||
Reference in New Issue
Block a user