Revert "Release/2026 02 27 (pull request #3070)"
This commit is contained in:
@@ -8,15 +8,12 @@ import { MdOpenInNew } from "react-icons/md";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||
import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import dayjs from "../../utils/day";
|
||||
import { bodyshopHasDmsKey } from "../../utils/dmsUtils.js";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||
@@ -24,6 +21,8 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
|
||||
import BillFormLines from "./bill-form.lines.component";
|
||||
import { CalculateBillTotal } from "./bill-form.totals.utility";
|
||||
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx";
|
||||
import { bodyshopHasDmsKey } from "../../utils/dmsUtils.js";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -44,14 +43,11 @@ export function BillFormComponent({
|
||||
loadOutstandingReturns,
|
||||
loadInventory,
|
||||
preferredMake,
|
||||
disableInHouse,
|
||||
isAiScan
|
||||
disableInHouse
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const client = useApolloClient();
|
||||
const [discount, setDiscount] = useState(0);
|
||||
const notification = useNotification();
|
||||
const jobIdFormWatch = Form.useWatch("jobid", form);
|
||||
|
||||
const {
|
||||
treatments: { Extended_Bill_Posting, ClosingPeriod }
|
||||
@@ -127,23 +123,6 @@ export function BillFormComponent({
|
||||
bodyshop.inhousevendorid
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
// 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 (form.getFieldValue("jobid") !== null && form.getFieldValue("jobid") !== undefined) {
|
||||
loadLines({ variables: { id: form.getFieldValue("jobid") } });
|
||||
if (form.getFieldValue("vendorid") !== null && form.getFieldValue("vendorid") !== undefined) {
|
||||
loadOutstandingReturns({
|
||||
variables: {
|
||||
jobId: form.getFieldValue("jobid"),
|
||||
vendorId: form.getFieldValue("vendorid")
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [jobIdFormWatch, form]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormFieldsChanged form={form} />
|
||||
@@ -349,12 +328,13 @@ export function BillFormComponent({
|
||||
</Form.Item>
|
||||
{!billEdit && (
|
||||
<Form.Item label={t("bills.fields.allpartslocation")} name="location">
|
||||
<Select
|
||||
style={{ width: "10rem" }}
|
||||
disabled={disabled}
|
||||
allowClear
|
||||
options={bodyshop.md_parts_locations.map((loc) => ({ value: loc, label: loc }))}
|
||||
/>
|
||||
<Select style={{ width: "10rem" }} disabled={disabled} allowClear>
|
||||
{bodyshop.md_parts_locations.map((loc, idx) => (
|
||||
<Select.Option key={idx} value={loc}>
|
||||
{loc}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
)}
|
||||
</LayoutFormRow>
|
||||
@@ -394,15 +374,7 @@ export function BillFormComponent({
|
||||
]);
|
||||
let totals;
|
||||
if (!!values.total && !!values.billlines && values.billlines.length > 0) {
|
||||
try {
|
||||
totals = CalculateBillTotal(values);
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
title: t("bills.errors.calculating_totals"),
|
||||
message: error.message || t("bills.errors.calculating_totals_generic"),
|
||||
key: "bill_totals_calculation_error"
|
||||
});
|
||||
}
|
||||
totals = CalculateBillTotal(values);
|
||||
}
|
||||
|
||||
if (totals) {
|
||||
@@ -480,7 +452,6 @@ export function BillFormComponent({
|
||||
responsibilityCenters={responsibilityCenters}
|
||||
disabled={disabled}
|
||||
billEdit={billEdit}
|
||||
isAiScan={isAiScan}
|
||||
/>
|
||||
)}
|
||||
<Divider titlePlacement="left" style={{ display: billEdit ? "none" : null }}>
|
||||
|
||||
@@ -15,7 +15,7 @@ const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop
|
||||
});
|
||||
|
||||
export function BillFormContainer({ bodyshop, form, billEdit, disabled, disableInvNumber, disableInHouse,isAiScan }) {
|
||||
export function BillFormContainer({ bodyshop, form, billEdit, disabled, disableInvNumber, disableInHouse }) {
|
||||
const {
|
||||
treatments: { Simple_Inventory }
|
||||
} = useTreatmentsWithConfig({
|
||||
@@ -50,7 +50,6 @@ export function BillFormContainer({ bodyshop, form, billEdit, disabled, disableI
|
||||
loadOutstandingReturns={loadOutstandingReturns}
|
||||
loadInventory={loadInventory}
|
||||
preferredMake={lineData ? lineData.jobs_by_pk.v_make_desc : null}
|
||||
isAiScan={isAiScan}
|
||||
/>
|
||||
{!billEdit && <BillCmdReturnsTableComponent form={form} returnLoading={returnLoading} returnData={returnData} />}
|
||||
{Simple_Inventory.treatment === "on" && (
|
||||
|
||||
@@ -5,15 +5,14 @@ import { useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectDarkMode } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { selectDarkMode } from "../../redux/application/application.selectors";
|
||||
import CiecaSelect from "../../utils/Ciecaselect";
|
||||
import { bodyshopHasDmsKey } from "../../utils/dmsUtils.js";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
|
||||
import BilllineAddInventory from "../billline-add-inventory/billline-add-inventory.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import ConfidenceDisplay from "./bill-form.lines.confidence.component.jsx";
|
||||
import { bodyshopHasDmsKey } from "../../utils/dmsUtils.js";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -30,8 +29,7 @@ export function BillEnterModalLinesComponent({
|
||||
discount,
|
||||
form,
|
||||
responsibilityCenters,
|
||||
billEdit,
|
||||
isAiScan
|
||||
billEdit
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { setFieldsValue, getFieldsValue, getFieldValue } = form;
|
||||
@@ -141,29 +139,6 @@ export function BillEnterModalLinesComponent({
|
||||
|
||||
const columns = (remove) => {
|
||||
return [
|
||||
...(isAiScan
|
||||
? [
|
||||
{
|
||||
title: t("billlines.fields.confidence"),
|
||||
dataIndex: "confidence",
|
||||
editable: true,
|
||||
width: "5rem",
|
||||
formItemProps: (field) => ({
|
||||
key: `${field.index}confidence`,
|
||||
name: [field.name, "confidence"],
|
||||
label: t("billlines.fields.confidence")
|
||||
}),
|
||||
formInput: (record) => {
|
||||
const rowValue = getFieldValue(["billlines", record.name]);
|
||||
return (
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
|
||||
<ConfidenceDisplay rowValue={rowValue} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: t("billlines.fields.jobline"),
|
||||
dataIndex: "joblineid",
|
||||
@@ -237,7 +212,6 @@ export function BillEnterModalLinesComponent({
|
||||
}),
|
||||
formInput: () => <Input.TextArea disabled={disabled} autoSize tabIndex={0} />
|
||||
},
|
||||
|
||||
{
|
||||
title: t("billlines.fields.quantity"),
|
||||
dataIndex: "quantity",
|
||||
@@ -276,16 +250,7 @@ export function BillEnterModalLinesComponent({
|
||||
key: `${field.name}actual_price`,
|
||||
name: [field.name, "actual_price"],
|
||||
label: t("billlines.fields.actual_price"),
|
||||
rules: [
|
||||
{ required: true },
|
||||
{
|
||||
validator: (_, value) => {
|
||||
return Math.abs(parseFloat(value)) < 0.01 ? Promise.reject() : Promise.resolve();
|
||||
},
|
||||
warningOnly: true
|
||||
}
|
||||
],
|
||||
hasFeedback: true
|
||||
rules: [{ required: true }]
|
||||
}),
|
||||
formInput: (record, index) => (
|
||||
<CurrencyInput
|
||||
@@ -434,17 +399,11 @@ export function BillEnterModalLinesComponent({
|
||||
rules: [{ required: true }]
|
||||
}),
|
||||
formInput: () => (
|
||||
<Select
|
||||
showSearch
|
||||
style={{ minWidth: "3rem" }}
|
||||
disabled={disabled}
|
||||
tabIndex={0}
|
||||
options={
|
||||
bodyshopHasDmsKey(bodyshop)
|
||||
? CiecaSelect(true, false)
|
||||
: responsibilityCenters.costs.map((item) => ({ value: item.name, label: item.name }))
|
||||
}
|
||||
/>
|
||||
<Select showSearch style={{ minWidth: "3rem" }} disabled={disabled} tabIndex={0}>
|
||||
{bodyshopHasDmsKey(bodyshop)
|
||||
? CiecaSelect(true, false)
|
||||
: responsibilityCenters.costs.map((item) => <Select.Option key={item.name}>{item.name}</Select.Option>)}
|
||||
</Select>
|
||||
)
|
||||
},
|
||||
...(billEdit
|
||||
@@ -460,11 +419,13 @@ export function BillEnterModalLinesComponent({
|
||||
name: [field.name, "location"]
|
||||
}),
|
||||
formInput: () => (
|
||||
<Select
|
||||
disabled={disabled}
|
||||
tabIndex={0}
|
||||
options={bodyshop.md_parts_locations.map((loc) => ({ value: loc, label: loc }))}
|
||||
/>
|
||||
<Select disabled={disabled} tabIndex={0}>
|
||||
{bodyshop.md_parts_locations.map((loc, idx) => (
|
||||
<Select.Option key={idx} value={loc}>
|
||||
{loc}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
]),
|
||||
@@ -505,10 +466,22 @@ export function BillEnterModalLinesComponent({
|
||||
rules={[{ required: true }]}
|
||||
name={[record.name, "lbr_adjustment", "mod_lbr_ty"]}
|
||||
>
|
||||
<Select
|
||||
allowClear
|
||||
options={CiecaSelect(false, true)}
|
||||
/>
|
||||
<Select allowClear>
|
||||
<Select.Option value="LAA">{t("joblines.fields.lbr_types.LAA")}</Select.Option>
|
||||
<Select.Option value="LAB">{t("joblines.fields.lbr_types.LAB")}</Select.Option>
|
||||
<Select.Option value="LAD">{t("joblines.fields.lbr_types.LAD")}</Select.Option>
|
||||
<Select.Option value="LAE">{t("joblines.fields.lbr_types.LAE")}</Select.Option>
|
||||
<Select.Option value="LAF">{t("joblines.fields.lbr_types.LAF")}</Select.Option>
|
||||
<Select.Option value="LAG">{t("joblines.fields.lbr_types.LAG")}</Select.Option>
|
||||
<Select.Option value="LAM">{t("joblines.fields.lbr_types.LAM")}</Select.Option>
|
||||
<Select.Option value="LAR">{t("joblines.fields.lbr_types.LAR")}</Select.Option>
|
||||
<Select.Option value="LAS">{t("joblines.fields.lbr_types.LAS")}</Select.Option>
|
||||
<Select.Option value="LAU">{t("joblines.fields.lbr_types.LAU")}</Select.Option>
|
||||
<Select.Option value="LA1">{t("joblines.fields.lbr_types.LA1")}</Select.Option>
|
||||
<Select.Option value="LA2">{t("joblines.fields.lbr_types.LA2")}</Select.Option>
|
||||
<Select.Option value="LA3">{t("joblines.fields.lbr_types.LA3")}</Select.Option>
|
||||
<Select.Option value="LA4">{t("joblines.fields.lbr_types.LA4")}</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{Enhanced_Payroll.treatment === "on" ? (
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
import { Progress, Space, Tag, Tooltip } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
const parseConfidence = (confidenceStr) => {
|
||||
if (!confidenceStr || typeof confidenceStr !== "string") return null;
|
||||
|
||||
const match = confidenceStr.match(/T([\d.]+)\s*-\s*O([\d.]+)\s*-\s*J([\d.]+)/);
|
||||
if (!match) return null;
|
||||
|
||||
return {
|
||||
total: parseFloat(match[1]),
|
||||
ocr: parseFloat(match[2]),
|
||||
jobMatch: parseFloat(match[3])
|
||||
};
|
||||
};
|
||||
|
||||
const getConfidenceColor = (value) => {
|
||||
if (value >= 80) return "green";
|
||||
if (value >= 60) return "orange";
|
||||
if (value >= 40) return "gold";
|
||||
return "red";
|
||||
};
|
||||
|
||||
const ConfidenceDisplay = ({ rowValue: { confidence, actual_price, actual_cost } }) => {
|
||||
const { t } = useTranslation();
|
||||
const parsed = parseConfidence(confidence);
|
||||
const parsed_actual_price = parseFloat(actual_price);
|
||||
const parsed_actual_cost = parseFloat(actual_cost);
|
||||
if (!parsed) {
|
||||
return <span style={{ color: "#959595", fontSize: "0.85em" }}>N/A</span>;
|
||||
}
|
||||
|
||||
const { total, ocr, jobMatch } = parsed;
|
||||
const color = getConfidenceColor(total);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
title={
|
||||
<div style={{ padding: "4px 0" }}>
|
||||
<div style={{ marginBottom: 8, fontWeight: 600 }}>{t("bills.labels.ai.confidence.breakdown")}</div>
|
||||
<div style={{ marginBottom: 4 }}>
|
||||
<strong>{t("bills.labels.ai.confidence.overall")}:</strong> {total.toFixed(1)}%
|
||||
<Progress
|
||||
percent={total}
|
||||
size="small"
|
||||
strokeColor={getConfidenceColor(total)}
|
||||
showInfo={false}
|
||||
style={{ marginTop: 2 }}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginBottom: 4 }}>
|
||||
<strong>{t("bills.labels.ai.confidence.ocr")}:</strong> {ocr.toFixed(1)}%
|
||||
<Progress
|
||||
percent={ocr}
|
||||
size="small"
|
||||
strokeColor={getConfidenceColor(ocr)}
|
||||
showInfo={false}
|
||||
style={{ marginTop: 2 }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{t("bills.labels.ai.confidence.match")}:</strong> {jobMatch.toFixed(1)}%
|
||||
<Progress
|
||||
percent={jobMatch}
|
||||
size="small"
|
||||
strokeColor={getConfidenceColor(jobMatch)}
|
||||
showInfo={false}
|
||||
style={{ marginTop: 2 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Space size="small">
|
||||
{!parsed_actual_cost || !parsed_actual_price || parsed_actual_cost === 0 || parsed_actual_price === 0 ? (
|
||||
<Tag color="red" style={{ margin: 0, cursor: "help", userSelect: "none" }}>
|
||||
{t("bills.labels.ai.confidence.missing_data")}
|
||||
</Tag>
|
||||
) : null}
|
||||
<Tag color={color} style={{ margin: 0, cursor: "help", userSelect: "none" }}>
|
||||
{total.toFixed(0)}%
|
||||
</Tag>
|
||||
</Space>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfidenceDisplay;
|
||||
Reference in New Issue
Block a user