import { useMutation, useQuery, useApolloClient } from "@apollo/client"; import { Form, Modal, notification } from "antd"; import moment from "moment"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; import { logImEXEvent, auth } from "../../firebase/firebase.utils"; import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries"; import { INSERT_NEW_PARTS_ORDERS, QUERY_PARTS_ORDER_OEC, } from "../../graphql/parts-orders.queries"; import { QUERY_ALL_VENDORS_FOR_ORDER } from "../../graphql/vendors.queries"; import { insertAuditTrail } from "../../redux/application/application.actions"; import { setEmailOptions } from "../../redux/email/email.actions"; import { setModalContext, toggleModalVisible, } from "../../redux/modals/modals.actions"; import { selectPartsOrder } from "../../redux/modals/modals.selectors"; import { selectBodyshop, selectCurrentUser, } from "../../redux/user/user.selectors"; import AuditTrailMapping from "../../utils/AuditTrailMappings"; import { GenerateDocument } from "../../utils/RenderTemplate"; import { TemplateList } from "../../utils/TemplateConstants"; import AlertComponent from "../alert/alert.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import PartsOrderModalComponent from "./parts-order-modal.component"; import axios from "axios"; import { useTreatments } from "@splitsoftware/splitio-react"; import _ from "lodash"; const mapStateToProps = createStructuredSelector({ currentUser: selectCurrentUser, bodyshop: selectBodyshop, partsOrderModal: selectPartsOrder, }); const mapDispatchToProps = (dispatch) => ({ setEmailOptions: (e) => dispatch(setEmailOptions(e)), toggleModalVisible: () => dispatch(toggleModalVisible("partsOrder")), setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })), insertAuditTrail: ({ jobid, operation }) => dispatch(insertAuditTrail({ jobid, operation })), }); export function PartsOrderModalContainer({ partsOrderModal, toggleModalVisible, currentUser, bodyshop, setEmailOptions, setBillEnterContext, insertAuditTrail, }) { const { t } = useTranslation(); const client = useApolloClient(); const { OEConnection_PriceChange } = useTreatments( ["OEConnection_PriceChange"], {}, bodyshop.imexshopid ); const { visible, context, actions } = partsOrderModal; const { jobId, linesToOrder, isReturn, vendorId, returnFromBill, invoiceNumber, job, } = context; const { refetch } = actions; const [form] = Form.useForm(); const [saving, setSaving] = useState(false); const sendTypeState = useState("e"); const sendType = sendTypeState[0]; const { loading, error, data } = useQuery(QUERY_ALL_VENDORS_FOR_ORDER, { skip: !visible, variables: { jobId: jobId }, fetchPolicy: "network-only", nextFetchPolicy: "network-only", }); const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS); const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS); const handleFinish = async (values) => { logImEXEvent("parts_order_insert"); setSaving(true); const insertResult = await insertPartOrder({ variables: { po: [ { ...values, orderedby: currentUser.email, jobid: jobId, user_email: currentUser.email, return: isReturn, status: bodyshop.md_order_statuses.default_ordered || "Ordered*", }, ], }, refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"], }); if (!!insertResult.error) { notification["error"]({ message: t("parts_orders.errors.creating"), description: JSON.stringify(insertResult.error), }); return; } const jobLinesResult = await updateJobLines({ variables: { ids: values.parts_order_lines.data .filter((item) => item.job_line_id) .map((item) => item.job_line_id), status: isReturn ? bodyshop.md_order_statuses.default_returned || "Returned*" : bodyshop.md_order_statuses.default_ordered || "Ordered*", }, }); insertAuditTrail({ jobid: jobId, operation: isReturn ? AuditTrailMapping.jobspartsreturn( insertResult.data.insert_parts_orders.returning[0].order_number ) : AuditTrailMapping.jobspartsorder( insertResult.data.insert_parts_orders.returning[0].order_number ), }); if (!!jobLinesResult.errors) { notification["error"]({ message: t("parts_orders.errors.creating"), description: JSON.stringify(jobLinesResult.errors), }); } notification["success"]({ message: values.isReturn ? t("parts_orders.successes.return_created") : t("parts_orders.successes.created"), }); if (values.vendorid === bodyshop.inhousevendorid) { setBillEnterContext({ actions: { refetch: refetch }, context: { disableInvNumber: true, job: { id: jobId }, bill: { vendorid: bodyshop.inhousevendorid, invoice_number: "ih", isinhouse: true, date: new moment(), total: 0, billlines: values.parts_order_lines.data.map((p) => { return { joblineid: p.job_line_id, actual_price: p.act_price, actual_cost: 0, //p.act_price, line_desc: p.line_desc, line_remarks: p.line_remarks, part_type: p.part_type, quantity: p.quantity || 1, applicable_taxes: { local: false, state: false, federal: false, }, }; }), }, }, }); toggleModalVisible(); return; } if (refetch) refetch(); const Templates = TemplateList("partsorder", context); if (sendType === "e") { const matchingVendor = data.vendors.filter( (item) => item.id === values.vendorid )[0]; let vendorEmails = matchingVendor && matchingVendor.email && matchingVendor.email.split(RegExp("[;,]")); GenerateDocument( { name: isReturn ? Templates.parts_return_slip.key : Templates.parts_order.key, variables: { id: insertResult.data.insert_parts_orders.returning[0].id, }, }, { to: matchingVendor ? vendorEmails : null, replyTo: bodyshop.email, subject: isReturn ? Templates.parts_return_slip.subject : Templates.parts_order.subject, }, "e", jobId ); } else if (sendType === "p") { GenerateDocument( { name: isReturn ? Templates.parts_return_slip.key : Templates.parts_order.key, variables: { id: insertResult.data.insert_parts_orders.returning[0].id, }, }, {}, "p" ); } else if (sendType === "oec") { //Send to Partner OEC. try { const partsOrder = await client.query({ query: QUERY_PARTS_ORDER_OEC, variables: { id: insertResult.data.insert_parts_orders.returning[0].id, }, }); let po; //Massage the data based on the split. Should they be able to overwrite OEC pricing? if (OEConnection_PriceChange.treatment === "on") { //Set the flag to include the override. po = _.cloneDeep(partsOrder.data.parts_orders_by_pk); po.parts_order_lines.forEach((pol) => { pol.priceChange = true; }); } const oecResponse = await axios.post( "http://localhost:1337/oec/", po || partsOrder.data.parts_orders_by_pk, { headers: { Authorization: `Bearer ${await auth.currentUser.getIdToken()}`, }, } ); if (oecResponse.data && oecResponse.data.success === false) { notification.open({ type: "error", message: t("parts_orders.errors.oec", { error: oecResponse.data.error, }), }); } } catch (error) { console.log("Error OEC.", error); notification["error"]({ message: t("parts_orders.errors.oec", { error: JSON.stringify(error.message), }), }); setSaving(false); return; } } setSaving(false); toggleModalVisible(); }; const initialValues = { jobid: jobId, return: isReturn, deliver_by: isReturn ? moment(new Date()) : null, vendorid: vendorId, returnfrombill: returnFromBill, parts_order_lines: { data: linesToOrder ? linesToOrder.reduce((acc, value) => { acc.push({ line_desc: value.line_desc, oem_partno: value.oem_partno, db_price: value.db_price, act_price: value.act_price, cost: value.cost, quantity: value.part_qty, job_line_id: isReturn ? value.joblineid : value.id, part_type: value.part_type, }); return acc; }, []) : [], }, }; useEffect(() => { if (visible && !!linesToOrder) { form.resetFields(); } }, [visible, linesToOrder, form]); return ( toggleModalVisible()} onOk={() => form.submit()} okButtonProps={{ loading: saving }} cancelButtonProps={{ loading: saving }} destroyOnClose width="75%" forceRender > {error ? : null}
{loading ? ( ) : ( )}
); } export default connect( mapStateToProps, mapDispatchToProps )(PartsOrderModalContainer);