import { DeleteFilled, DownOutlined } from "@ant-design/icons"; import { Button, Card, Col, Divider, Dropdown, Form, Input, InputNumber, Row, Select, Space, Statistic, Switch, Tooltip, Typography } from "antd"; import Dinero from "dinero.js"; import { useTranslation } from "react-i18next"; import { useMemo, useState } from "react"; import i18n from "../../translations/i18n"; import dayjs from "../../utils/day"; import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component"; import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component"; import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component.jsx"; import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import { DMS_MAP } from "../../utils/dmsUtils"; import { useTreatmentsWithConfig } from "@splitsoftware/splitio-react"; /** * CDK-like DMS post form: * - CDK / Fortellis / PBS * - CDK vehicle details + make/model selection * - Payer list with discrepancy gating * - Submit: "{mode}-export-job" * @param bodyshop * @param socket * @param job * @param logsRef * @param mode * @param allocationsSummary * @returns {JSX.Element} * @constructor */ export default function CdkLikePostForm({ bodyshop, socket, job, logsRef, mode, allocationsSummary }) { const [form] = Form.useForm(); const { t } = useTranslation(); const [, /*unused*/ setTick] = useState(0); // handy if you need a forceUpdate later const { treatments: { Fortellis } } = useTreatmentsWithConfig({ attributes: {}, names: ["Fortellis"], splitKey: bodyshop.imexshopid }); const initialValues = useMemo( () => ({ story: `${t("jobs.labels.dms.defaultstory", { ro_number: job.ro_number, ownr_nm: `${job.ownr_fn || ""} ${job.ownr_ln || ""} ${job.ownr_co_nm || ""}`.trim(), ins_co_nm: job.ins_co_nm || "N/A", clm_po: `${job.clm_no ? `${job.clm_no} ` : ""}${job.po_number || ""}` }).trim()}.${ job.area_of_damage?.impact1 ? " " + t("jobs.labels.dms.damageto", { area_of_damage: (job.area_of_damage && job.area_of_damage.impact1.padStart(2, "0")) || "UNKNOWN" }) : "" }`.slice(0, 239), inservicedate: dayjs( `${ (job.v_model_yr && (job.v_model_yr < 100 ? job.v_model_yr >= (dayjs().year() + 1) % 100 ? 1900 + parseInt(job.v_model_yr, 10) : 2000 + parseInt(job.v_model_yr, 10) : job.v_model_yr)) || 2019 }-01-01` ), journal: bodyshop.cdk_configuration?.default_journal }), [job, bodyshop, t] ); // Payers helpers const handlePayerSelect = (value, index) => { form.setFieldsValue({ payers: (form.getFieldValue("payers") || []).map((payer, mapIndex) => { if (index !== mapIndex) return payer; const cdkPayer = bodyshop.cdk_configuration.payers && bodyshop.cdk_configuration.payers.find((i) => i.name === value); if (!cdkPayer) return payer; return { ...cdkPayer, dms_acctnumber: cdkPayer.dms_acctnumber, controlnumber: job?.[cdkPayer.control_type] }; }) }); setTick((n) => n + 1); }; const handleFinish = (values) => { if (!socket) return; if (mode === DMS_MAP.fortellis) { socket.emit("fortellis-export-job", { jobid: job.id, txEnvelope: { ...values, SubscriptionID: bodyshop.cdk_dealerid } }); } else { socket.emit(`${mode}-export-job`, { jobid: job.id, txEnvelope: values }); } logsRef?.current?.scrollIntoView({ behavior: "smooth" }); }; // Totals & discrepancy const totals = useMemo(() => { if (!allocationsSummary || allocationsSummary.length === 0) { return { totalSale: Dinero(), totalCost: Dinero() }; } return allocationsSummary.reduce( (acc, val) => ({ totalSale: acc.totalSale.add(Dinero(val.sale)), totalCost: acc.totalCost.add(Dinero(val.cost)) }), { totalSale: Dinero(), totalCost: Dinero() } ); }, [allocationsSummary]); return (
{/* TOP ROW */} {/* CDK vehicle details (kept for CDK/Fortellis paths when dealer id exists) */} {bodyshop.cdk_dealerid && ( <> )} {/* Totals */} {/* Payers list */} {(fields, { add, remove }) => (
{fields.map((field, index) => (
} name={[field.name, "controlnumber"]} rules={[{ required: true }]} > {() => { const payers = form.getFieldValue("payers"); const row = payers?.[index]; const cdkPayer = bodyshop.cdk_configuration.payers && bodyshop.cdk_configuration.payers.find((i) => i && row && i.name === row.name); if (i18n.exists(`jobs.fields.${cdkPayer?.control_type}`)) return
{cdkPayer && t(`jobs.fields.${cdkPayer?.control_type}`)}
; else if (i18n.exists(`jobs.fields.dms.control_type.${cdkPayer?.control_type}`)) { return
{cdkPayer && t(`jobs.fields.dms.control_type.${cdkPayer?.control_type}`)}
; } else { return null; } }}
))} )}
{/* Validation gates & summary */} {() => { let totalAllocated = Dinero(); const payers = form.getFieldValue("payers") || []; payers.forEach((payer) => { totalAllocated = totalAllocated.add(Dinero({ amount: Math.round((payer?.amount || 0) * 100) })); }); const discrep = totals ? totals.totalSale.subtract(totalAllocated) : Dinero(); // gate: must have payers filled + zero discrepancy when we have a summary const payersOk = payers.length > 0 && payers.every((p) => p?.name && p.dms_acctnumber && (p.amount ?? "") !== "" && p.controlnumber); const hasAllocations = allocationsSummary && allocationsSummary.length > 0; const nonRrDiscrepancyGate = hasAllocations ? discrep.getAmount() !== 0 : true; const disablePost = !payersOk || nonRrDiscrepancyGate; return ( - = ); }}
); }