IO-3515 Checkin. Crude form update with some correct values. Pricing still significantly out.
This commit is contained in:
@@ -2,7 +2,7 @@ import { useApolloClient, useMutation } from "@apollo/client/react";
|
|||||||
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
|
import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Checkbox, Form, Modal, Space } from "antd";
|
import { Button, Checkbox, Form, Modal, Space } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
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 } from "../documents-upload/documents-upload.utility";
|
||||||
import { handleUpload as handleUploadToImageProxy } from "../documents-upload-imgproxy/documents-upload-imgproxy.utility";
|
import { handleUpload as handleUploadToImageProxy } from "../documents-upload-imgproxy/documents-upload-imgproxy.utility";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
billEnterModal: selectBillEnterModal,
|
billEnterModal: selectBillEnterModal,
|
||||||
@@ -53,6 +54,7 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
const [generateLabel, setGenerateLabel] = useLocalStorage("enter_bill_generate_label", false);
|
const [generateLabel, setGenerateLabel] = useLocalStorage("enter_bill_generate_label", false);
|
||||||
const notification = useNotification();
|
const notification = useNotification();
|
||||||
|
const fileInputRef = useRef(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
treatments: { Enhanced_Payroll, Imgproxy }
|
treatments: { Enhanced_Payroll, Imgproxy }
|
||||||
@@ -419,6 +421,44 @@ function BillEnterModalContainer({ billEnterModal, toggleModalVisible, bodyshop,
|
|||||||
}}
|
}}
|
||||||
footer={
|
footer={
|
||||||
<Space>
|
<Space>
|
||||||
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
|
type="file"
|
||||||
|
accept="image/*,application/pdf"
|
||||||
|
style={{ display: "none" }}
|
||||||
|
onChange={(e) => {
|
||||||
|
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 = "";
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
console.log("Fields Object", form.getFieldsValue());
|
||||||
|
fileInputRef.current?.click();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
AI Scan (1 page only for now)
|
||||||
|
</Button>
|
||||||
<Checkbox checked={generateLabel} onChange={(e) => setGenerateLabel(e.target.checked)}>
|
<Checkbox checked={generateLabel} onChange={(e) => setGenerateLabel(e.target.checked)}>
|
||||||
{t("bills.labels.generatepartslabel")}
|
{t("bills.labels.generatepartslabel")}
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -39,9 +39,9 @@ function normalizeLabelName(labelText) {
|
|||||||
'part_num': standardizedFieldsnames.part_no,
|
'part_num': standardizedFieldsnames.part_no,
|
||||||
'part_number': standardizedFieldsnames.part_no,
|
'part_number': standardizedFieldsnames.part_no,
|
||||||
'price': standardizedFieldsnames.actual_price,
|
'price': standardizedFieldsnames.actual_price,
|
||||||
'unit_price': standardizedFieldsnames.actual_price,
|
|
||||||
'amount': standardizedFieldsnames.actual_price,
|
'amount': standardizedFieldsnames.actual_price,
|
||||||
'list_price': standardizedFieldsnames.actual_price,
|
'list_price': standardizedFieldsnames.actual_price,
|
||||||
|
'unit_price': standardizedFieldsnames.actual_price,
|
||||||
'list': standardizedFieldsnames.actual_price,
|
'list': standardizedFieldsnames.actual_price,
|
||||||
'retail_price': standardizedFieldsnames.actual_price,
|
'retail_price': standardizedFieldsnames.actual_price,
|
||||||
'net': standardizedFieldsnames.actual_cost,
|
'net': standardizedFieldsnames.actual_cost,
|
||||||
@@ -145,6 +145,7 @@ function extractInvoiceData(textractResponse) {
|
|||||||
if (lineItemGroup.LineItems) {
|
if (lineItemGroup.LineItems) {
|
||||||
lineItemGroup.LineItems.forEach(lineItem => {
|
lineItemGroup.LineItems.forEach(lineItem => {
|
||||||
const item = {};
|
const item = {};
|
||||||
|
const fieldNameCounts = {}; // Track field name occurrences
|
||||||
|
|
||||||
if (lineItem.LineItemExpenseFields) {
|
if (lineItem.LineItemExpenseFields) {
|
||||||
lineItem.LineItemExpenseFields.forEach(field => {
|
lineItem.LineItemExpenseFields.forEach(field => {
|
||||||
@@ -155,7 +156,14 @@ function extractInvoiceData(textractResponse) {
|
|||||||
|
|
||||||
if (fieldType && fieldValue) {
|
if (fieldType && fieldValue) {
|
||||||
// Normalize field names
|
// 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] = {
|
item[normalizedField] = {
|
||||||
value: fieldValue,
|
value: fieldValue,
|
||||||
label: fieldLabel,
|
label: fieldLabel,
|
||||||
|
|||||||
@@ -62,7 +62,19 @@ async function handleBillOcr(request, response) {
|
|||||||
|
|
||||||
// The uploaded file is available in request.file
|
// The uploaded file is available in request.file
|
||||||
const uploadedFile = 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 {
|
try {
|
||||||
const fileType = getFileType(uploadedFile);
|
const fileType = getFileType(uploadedFile);
|
||||||
@@ -71,12 +83,12 @@ async function handleBillOcr(request, response) {
|
|||||||
// Images are always processed synchronously (single page)
|
// Images are always processed synchronously (single page)
|
||||||
if (fileType === 'image') {
|
if (fileType === 'image') {
|
||||||
console.log('Image => 1 page, processing synchronously');
|
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({
|
response.status(200).send({
|
||||||
success: true,
|
success: true,
|
||||||
status: 'COMPLETED',
|
status: 'COMPLETED',
|
||||||
data: result,
|
data: { ...processedData, billForm },
|
||||||
message: 'Invoice processing completed'
|
message: 'Invoice processing completed'
|
||||||
});
|
});
|
||||||
} else if (fileType === 'pdf') {
|
} else if (fileType === 'pdf') {
|
||||||
@@ -87,13 +99,13 @@ async function handleBillOcr(request, response) {
|
|||||||
if (pageCount === 1) {
|
if (pageCount === 1) {
|
||||||
// Process synchronously for single-page documents
|
// Process synchronously for single-page documents
|
||||||
console.log('PDF => 1 page, processing synchronously');
|
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, });
|
//const billResult = await generateBillFormData({ result, });
|
||||||
response.status(200).send({
|
response.status(200).send({
|
||||||
success: true,
|
success: true,
|
||||||
status: 'COMPLETED',
|
status: 'COMPLETED',
|
||||||
data: { result, },
|
data: { ...processedData, billForm },
|
||||||
message: 'Invoice processing completed'
|
message: 'Invoice processing completed'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -142,9 +154,13 @@ async function handleBillOcrStatus(request, response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jobStatus.status === 'COMPLETED') {
|
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({
|
response.status(200).send({
|
||||||
status: 'COMPLETED',
|
status: 'COMPLETED',
|
||||||
data: jobStatus.data
|
data: jobStatus.data
|
||||||
|
// data: { ...jobStatus.data, billForm }
|
||||||
});
|
});
|
||||||
} else if (jobStatus.status === 'FAILED') {
|
} else if (jobStatus.status === 'FAILED') {
|
||||||
response.status(500).send({
|
response.status(500).send({
|
||||||
|
|||||||
Reference in New Issue
Block a user