@@ -11149,6 +11149,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>dateinpast</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>dlexpirebeforereturn</name>
|
<name>dlexpirebeforereturn</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -12370,6 +12391,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>leasereturn</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>out</name>
|
<name>out</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -14735,6 +14777,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>vacationadded</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>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
@@ -32699,6 +32762,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>saving</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>selectexistingornew</name>
|
<name>selectexistingornew</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -33103,6 +33187,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>tax_number</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>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
@@ -41346,6 +41451,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>efficiencyoverperiod</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>jobs</name>
|
<name>jobs</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -142,3 +142,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Update row highlighting on production board.
|
||||||
|
.ant-table-tbody > tr.ant-table-row:hover > td {
|
||||||
|
background: #eaeaea !important;
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
import { useMutation, useQuery } from "@apollo/client";
|
||||||
|
import { Button, Form, PageHeader, Popconfirm, Space } from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import {
|
||||||
|
DELETE_BILL_LINE,
|
||||||
|
INSERT_NEW_BILL_LINES,
|
||||||
|
UPDATE_BILL_LINE
|
||||||
|
} from "../../graphql/bill-lines.queries";
|
||||||
|
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import BillFormContainer from "../bill-form/bill-form.container";
|
||||||
|
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
|
||||||
|
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
|
||||||
|
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
|
||||||
|
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
||||||
|
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||||
|
import BillDetailEditReturn from "./bill-detail-edit-return.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setPartsOrderContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||||
|
insertAuditTrail: ({ jobid, operation }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(BillDetailEditcontainer);
|
||||||
|
|
||||||
|
export function BillDetailEditcontainer({
|
||||||
|
setPartsOrderContext,
|
||||||
|
insertAuditTrail,
|
||||||
|
bodyshop,
|
||||||
|
}) {
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [updateLoading, setUpdateLoading] = useState(false);
|
||||||
|
const [update_bill] = useMutation(UPDATE_BILL);
|
||||||
|
const [insertBillLine] = useMutation(INSERT_NEW_BILL_LINES);
|
||||||
|
const [updateBillLine] = useMutation(UPDATE_BILL_LINE);
|
||||||
|
const [deleteBillLine] = useMutation(DELETE_BILL_LINE);
|
||||||
|
|
||||||
|
const { loading, error, data, refetch } = useQuery(QUERY_BILL_BY_PK, {
|
||||||
|
variables: { billid: search.billid },
|
||||||
|
skip: !!!search.billid,
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
//It's got a previously deducted bill line!
|
||||||
|
if (
|
||||||
|
data.bills_by_pk.billlines.filter((b) => b.deductedfromlbr).length > 0 ||
|
||||||
|
form.getFieldValue("billlines").filter((b) => b.deductedfromlbr).length >
|
||||||
|
0
|
||||||
|
)
|
||||||
|
setVisible(true);
|
||||||
|
else {
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFinish = async (values) => {
|
||||||
|
setUpdateLoading(true);
|
||||||
|
//let adjustmentsToInsert = {};
|
||||||
|
|
||||||
|
const { billlines, upload, ...bill } = values;
|
||||||
|
const updates = [];
|
||||||
|
updates.push(
|
||||||
|
update_bill({
|
||||||
|
variables: { billId: search.billid, bill: bill },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
billlines.forEach((l) => {
|
||||||
|
delete l.selected;
|
||||||
|
});
|
||||||
|
|
||||||
|
//Find bill lines that were deleted.
|
||||||
|
const deletedJobLines = [];
|
||||||
|
|
||||||
|
data.bills_by_pk.billlines.forEach((a) => {
|
||||||
|
const matchingRecord = billlines.find((b) => b.id === a.id);
|
||||||
|
if (!matchingRecord) {
|
||||||
|
deletedJobLines.push(a);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
deletedJobLines.forEach((d) => {
|
||||||
|
updates.push(deleteBillLine({ variables: { id: d.id } }));
|
||||||
|
});
|
||||||
|
|
||||||
|
billlines.forEach((billline) => {
|
||||||
|
const { deductedfromlbr, inventories, jobline, ...il } = billline;
|
||||||
|
delete il.__typename;
|
||||||
|
|
||||||
|
if (il.id) {
|
||||||
|
updates.push(
|
||||||
|
updateBillLine({
|
||||||
|
variables: {
|
||||||
|
billLineId: il.id,
|
||||||
|
billLine: {
|
||||||
|
...il,
|
||||||
|
deductedfromlbr: deductedfromlbr,
|
||||||
|
joblineid: il.joblineid === "noline" ? null : il.joblineid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
//It's a new line, have to insert it.
|
||||||
|
updates.push(
|
||||||
|
insertBillLine({
|
||||||
|
variables: {
|
||||||
|
billLines: [
|
||||||
|
{
|
||||||
|
...il,
|
||||||
|
deductedfromlbr: deductedfromlbr,
|
||||||
|
billid: search.billid,
|
||||||
|
joblineid: il.joblineid === "noline" ? null : il.joblineid,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(updates);
|
||||||
|
|
||||||
|
insertAuditTrail({
|
||||||
|
jobid: bill.jobid,
|
||||||
|
billid: search.billid,
|
||||||
|
operation: AuditTrailMapping.billupdated(bill.invoice_number),
|
||||||
|
});
|
||||||
|
|
||||||
|
await refetch();
|
||||||
|
form.setFieldsValue(transformData(data));
|
||||||
|
form.resetFields();
|
||||||
|
setVisible(false);
|
||||||
|
setUpdateLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
if (!search.billid) return <></>; //<div>{t("bills.labels.noneselected")}</div>;
|
||||||
|
|
||||||
|
const exported = data && data.bills_by_pk && data.bills_by_pk.exported;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{loading && <LoadingSkeleton />}
|
||||||
|
{data && (
|
||||||
|
<>
|
||||||
|
<PageHeader
|
||||||
|
title={
|
||||||
|
data &&
|
||||||
|
`${data.bills_by_pk.invoice_number} - ${data.bills_by_pk.vendor.name}`
|
||||||
|
}
|
||||||
|
extra={
|
||||||
|
<Space>
|
||||||
|
<BillDetailEditReturn data={data} />
|
||||||
|
|
||||||
|
<Popconfirm
|
||||||
|
visible={visible}
|
||||||
|
onConfirm={() => form.submit()}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
okButtonProps={{ loading: updateLoading }}
|
||||||
|
title={t("bills.labels.editadjwarning")}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
htmlType="submit"
|
||||||
|
disabled={exported}
|
||||||
|
onClick={handleSave}
|
||||||
|
loading={updateLoading}
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
|
{t("general.actions.save")}
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
<BillReeportButtonComponent bill={data && data.bills_by_pk} />
|
||||||
|
<BillMarkExportedButton bill={data && data.bills_by_pk} />
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
onFinish={handleFinish}
|
||||||
|
initialValues={transformData(data)}
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<BillFormContainer form={form} billEdit disabled={exported} />
|
||||||
|
|
||||||
|
{bodyshop.uselocalmediaserver ? (
|
||||||
|
<JobsDocumentsLocalGallery
|
||||||
|
job={{ id: data ? data.bills_by_pk.jobid : null }}
|
||||||
|
invoice_number={data ? data.bills_by_pk.invoice_number : null}
|
||||||
|
vendorid={data ? data.bills_by_pk.vendorid : null}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<JobDocumentsGallery
|
||||||
|
jobId={data ? data.bills_by_pk.jobid : null}
|
||||||
|
billId={search.billid}
|
||||||
|
documentsList={data ? data.bills_by_pk.documents : []}
|
||||||
|
billsCallback={refetch}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const transformData = (data) => {
|
||||||
|
return data
|
||||||
|
? {
|
||||||
|
...data.bills_by_pk,
|
||||||
|
|
||||||
|
billlines: data.bills_by_pk.billlines.map((i) => {
|
||||||
|
return {
|
||||||
|
...i,
|
||||||
|
joblineid: !!i.joblineid ? i.joblineid : "noline",
|
||||||
|
applicable_taxes: {
|
||||||
|
federal:
|
||||||
|
(i.applicable_taxes && i.applicable_taxes.federal) || false,
|
||||||
|
state: (i.applicable_taxes && i.applicable_taxes.state) || false,
|
||||||
|
local: (i.applicable_taxes && i.applicable_taxes.local) || false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
date: data.bills_by_pk ? moment(data.bills_by_pk.date) : null,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
};
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
import { Button, Checkbox, Form, Modal } from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setPartsOrderContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
||||||
|
insertAuditTrail: ({ jobid, operation }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, operation })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(BillDetailEditReturn);
|
||||||
|
|
||||||
|
export function BillDetailEditReturn({
|
||||||
|
setPartsOrderContext,
|
||||||
|
insertAuditTrail,
|
||||||
|
bodyshop,
|
||||||
|
data,
|
||||||
|
disabled,
|
||||||
|
}) {
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
|
const history = useHistory();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
const handleFinish = ({ billlines }) => {
|
||||||
|
const selectedLines = billlines.filter((l) => l.selected).map((l) => l.id);
|
||||||
|
|
||||||
|
setPartsOrderContext({
|
||||||
|
actions: {},
|
||||||
|
context: {
|
||||||
|
jobId: data.bills_by_pk.jobid,
|
||||||
|
vendorId: data.bills_by_pk.vendorid,
|
||||||
|
returnFromBill: data.bills_by_pk.id,
|
||||||
|
invoiceNumber: data.bills_by_pk.invoice_number,
|
||||||
|
linesToOrder: data.bills_by_pk.billlines
|
||||||
|
.filter((l) => selectedLines.includes(l.id))
|
||||||
|
.map((i) => {
|
||||||
|
return {
|
||||||
|
line_desc: i.line_desc,
|
||||||
|
// db_price: i.actual_price,
|
||||||
|
act_price: i.actual_price,
|
||||||
|
cost: i.actual_cost,
|
||||||
|
quantity: i.quantity,
|
||||||
|
joblineid: i.joblineid,
|
||||||
|
oem_partno: i.jobline && i.jobline.oem_partno,
|
||||||
|
part_type: i.jobline && i.jobline.part_type,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
isReturn: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
delete search.billid;
|
||||||
|
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
setVisible(false);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible === false) form.resetFields();
|
||||||
|
}, [visible, form]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
visible={visible}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
destroyOnClose
|
||||||
|
title={t("bills.actions.return")}
|
||||||
|
onOk={() => form.submit()}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
initialValues={data && data.bills_by_pk}
|
||||||
|
onFinish={handleFinish}
|
||||||
|
form={form}
|
||||||
|
>
|
||||||
|
<Form.List name={["billlines"]}>
|
||||||
|
{(fields, { add, remove, move }) => {
|
||||||
|
return (
|
||||||
|
<table style={{ tableLayout: "auto", width: "100%" }}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>{t("billlines.fields.line_desc")}</td>
|
||||||
|
<td>{t("billlines.fields.quantity")}</td>
|
||||||
|
<td>{t("billlines.fields.actual_price")}</td>
|
||||||
|
<td>{t("billlines.fields.actual_cost")}</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<tr key={field.key}>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
// label={t("joblines.fields.selected")}
|
||||||
|
key={`${index}selected`}
|
||||||
|
name={[field.name, "selected"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Checkbox />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
// label={t("joblines.fields.line_desc")}
|
||||||
|
key={`${index}line_desc`}
|
||||||
|
name={[field.name, "line_desc"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
// label={t("joblines.fields.quantity")}
|
||||||
|
key={`${index}quantity`}
|
||||||
|
name={[field.name, "quantity"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
// label={t("joblines.fields.actual_price")}
|
||||||
|
key={`${index}actual_price`}
|
||||||
|
name={[field.name, "actual_price"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent type="currency" />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
// label={t("joblines.fields.actual_cost")}
|
||||||
|
key={`${index}actual_cost`}
|
||||||
|
name={[field.name, "actual_cost"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent type="currency" />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
<Button
|
||||||
|
disabled={data.bills_by_pk.is_credit_memo || disabled}
|
||||||
|
onClick={() => {
|
||||||
|
setVisible(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("bills.actions.return")}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,68 +1,12 @@
|
|||||||
import { useMutation, useQuery } from "@apollo/client";
|
import { Drawer, Grid } from "antd";
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Drawer,
|
|
||||||
Form,
|
|
||||||
Grid,
|
|
||||||
PageHeader,
|
|
||||||
Popconfirm,
|
|
||||||
Space,
|
|
||||||
} from "antd";
|
|
||||||
import moment from "moment";
|
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { useHistory, useLocation } from "react-router-dom";
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import BillDetailEditComponent from "./bill-detail-edit-component";
|
||||||
import {
|
|
||||||
DELETE_BILL_LINE,
|
|
||||||
INSERT_NEW_BILL_LINES,
|
|
||||||
UPDATE_BILL_LINE,
|
|
||||||
} from "../../graphql/bill-lines.queries";
|
|
||||||
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
|
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
|
||||||
import AlertComponent from "../alert/alert.component";
|
|
||||||
import BillFormContainer from "../bill-form/bill-form.container";
|
|
||||||
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
|
|
||||||
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
|
|
||||||
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
|
|
||||||
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
|
|
||||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
export default function BillDetailEditcontainer() {
|
||||||
bodyshop: selectBodyshop,
|
|
||||||
});
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
|
||||||
setPartsOrderContext: (context) =>
|
|
||||||
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
|
|
||||||
insertAuditTrail: ({ jobid, operation }) =>
|
|
||||||
dispatch(insertAuditTrail({ jobid, operation })),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(
|
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(BillDetailEditcontainer);
|
|
||||||
|
|
||||||
export function BillDetailEditcontainer({
|
|
||||||
setPartsOrderContext,
|
|
||||||
insertAuditTrail,
|
|
||||||
bodyshop,
|
|
||||||
}) {
|
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { t } = useTranslation();
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const [updateLoading, setUpdateLoading] = useState(false);
|
|
||||||
const [update_bill] = useMutation(UPDATE_BILL);
|
|
||||||
const [insertBillLine] = useMutation(INSERT_NEW_BILL_LINES);
|
|
||||||
const [updateBillLine] = useMutation(UPDATE_BILL_LINE);
|
|
||||||
const [deleteBillLine] = useMutation(DELETE_BILL_LINE);
|
|
||||||
|
|
||||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||||
.filter((screen) => !!screen[1])
|
.filter((screen) => !!screen[1])
|
||||||
@@ -80,114 +24,6 @@ export function BillDetailEditcontainer({
|
|||||||
? bpoints[selectedBreakpoint[0]]
|
? bpoints[selectedBreakpoint[0]]
|
||||||
: "100%";
|
: "100%";
|
||||||
|
|
||||||
const { loading, error, data, refetch } = useQuery(QUERY_BILL_BY_PK, {
|
|
||||||
variables: { billid: search.billid },
|
|
||||||
skip: !!!search.billid,
|
|
||||||
fetchPolicy: "network-only",
|
|
||||||
nextFetchPolicy: "network-only",
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleSave = () => {
|
|
||||||
//It's got a previously deducted bill line!
|
|
||||||
if (
|
|
||||||
data.bills_by_pk.billlines.filter((b) => b.deductedfromlbr).length > 0 ||
|
|
||||||
form.getFieldValue("billlines").filter((b) => b.deductedfromlbr).length >
|
|
||||||
0
|
|
||||||
)
|
|
||||||
setVisible(true);
|
|
||||||
else {
|
|
||||||
form.submit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
|
||||||
setUpdateLoading(true);
|
|
||||||
//let adjustmentsToInsert = {};
|
|
||||||
|
|
||||||
const { billlines, upload, ...bill } = values;
|
|
||||||
const updates = [];
|
|
||||||
updates.push(
|
|
||||||
update_bill({
|
|
||||||
variables: { billId: search.billid, bill: bill },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
//Find bill lines that were deleted.
|
|
||||||
const deletedJobLines = [];
|
|
||||||
|
|
||||||
data.bills_by_pk.billlines.forEach((a) => {
|
|
||||||
const matchingRecord = billlines.find((b) => b.id === a.id);
|
|
||||||
if (!matchingRecord) {
|
|
||||||
deletedJobLines.push(a);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
deletedJobLines.forEach((d) => {
|
|
||||||
updates.push(deleteBillLine({ variables: { id: d.id } }));
|
|
||||||
});
|
|
||||||
|
|
||||||
billlines.forEach((billline) => {
|
|
||||||
const { deductedfromlbr, inventories, jobline, ...il } = billline;
|
|
||||||
delete il.__typename;
|
|
||||||
|
|
||||||
if (il.id) {
|
|
||||||
updates.push(
|
|
||||||
updateBillLine({
|
|
||||||
variables: {
|
|
||||||
billLineId: il.id,
|
|
||||||
billLine: {
|
|
||||||
...il,
|
|
||||||
deductedfromlbr: deductedfromlbr,
|
|
||||||
joblineid: il.joblineid === "noline" ? null : il.joblineid,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
//It's a new line, have to insert it.
|
|
||||||
updates.push(
|
|
||||||
insertBillLine({
|
|
||||||
variables: {
|
|
||||||
billLines: [
|
|
||||||
{
|
|
||||||
...il,
|
|
||||||
deductedfromlbr: deductedfromlbr,
|
|
||||||
billid: search.billid,
|
|
||||||
joblineid: il.joblineid === "noline" ? null : il.joblineid,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(updates);
|
|
||||||
|
|
||||||
insertAuditTrail({
|
|
||||||
jobid: bill.jobid,
|
|
||||||
billid: search.billid,
|
|
||||||
operation: AuditTrailMapping.billupdated(bill.invoice_number),
|
|
||||||
});
|
|
||||||
|
|
||||||
await refetch();
|
|
||||||
form.setFieldsValue(transformData(data));
|
|
||||||
form.resetFields();
|
|
||||||
setVisible(false);
|
|
||||||
setUpdateLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (search.billid && data) {
|
|
||||||
form.resetFields();
|
|
||||||
}
|
|
||||||
}, [form, search.billid, data]);
|
|
||||||
|
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
|
||||||
if (!search.billid) return <></>; //<div>{t("bills.labels.noneselected")}</div>;
|
|
||||||
|
|
||||||
const exported = data && data.bills_by_pk && data.bills_by_pk.exported;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
width={drawerPercentage}
|
width={drawerPercentage}
|
||||||
@@ -195,119 +31,10 @@ export function BillDetailEditcontainer({
|
|||||||
delete search.billid;
|
delete search.billid;
|
||||||
history.push({ search: queryString.stringify(search) });
|
history.push({ search: queryString.stringify(search) });
|
||||||
}}
|
}}
|
||||||
|
destroyOnClose
|
||||||
visible={search.billid}
|
visible={search.billid}
|
||||||
>
|
>
|
||||||
{loading && <LoadingSkeleton />}
|
<BillDetailEditComponent />
|
||||||
{!loading && (
|
|
||||||
<>
|
|
||||||
<PageHeader
|
|
||||||
title={
|
|
||||||
data &&
|
|
||||||
`${data.bills_by_pk.invoice_number} - ${data.bills_by_pk.vendor.name}`
|
|
||||||
}
|
|
||||||
extra={
|
|
||||||
<Space>
|
|
||||||
<Button
|
|
||||||
disabled={data.bills_by_pk.is_credit_memo}
|
|
||||||
onClick={() => {
|
|
||||||
delete search.billid;
|
|
||||||
history.push({ search: queryString.stringify(search) });
|
|
||||||
setPartsOrderContext({
|
|
||||||
actions: {},
|
|
||||||
context: {
|
|
||||||
jobId: data.bills_by_pk.jobid,
|
|
||||||
vendorId: data.bills_by_pk.vendorid,
|
|
||||||
returnFromBill: data.bills_by_pk.id,
|
|
||||||
invoiceNumber: data.bills_by_pk.invoice_number,
|
|
||||||
linesToOrder: data.bills_by_pk.billlines.map((i) => {
|
|
||||||
return {
|
|
||||||
line_desc: i.line_desc,
|
|
||||||
// db_price: i.actual_price,
|
|
||||||
act_price: i.actual_price,
|
|
||||||
cost: i.actual_cost,
|
|
||||||
quantity: i.quantity,
|
|
||||||
joblineid: i.joblineid,
|
|
||||||
oem_partno: i.jobline && i.jobline.oem_partno,
|
|
||||||
part_type: i.jobline && i.jobline.part_type,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
isReturn: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("bills.actions.return")}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Popconfirm
|
|
||||||
visible={visible}
|
|
||||||
onConfirm={() => form.submit()}
|
|
||||||
onCancel={() => setVisible(false)}
|
|
||||||
okButtonProps={{ loading: updateLoading }}
|
|
||||||
title={t("bills.labels.editadjwarning")}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
htmlType="submit"
|
|
||||||
disabled={exported}
|
|
||||||
onClick={handleSave}
|
|
||||||
loading={updateLoading}
|
|
||||||
type="primary"
|
|
||||||
>
|
|
||||||
{t("general.actions.save")}
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>
|
|
||||||
<BillReeportButtonComponent bill={data && data.bills_by_pk} />
|
|
||||||
<BillMarkExportedButton bill={data && data.bills_by_pk} />
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
onFinish={handleFinish}
|
|
||||||
initialValues={transformData(data)}
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<BillFormContainer form={form} billEdit disabled={exported} />
|
|
||||||
|
|
||||||
{bodyshop.uselocalmediaserver ? (
|
|
||||||
<JobsDocumentsLocalGallery
|
|
||||||
job={{ id: data ? data.bills_by_pk.jobid : null }}
|
|
||||||
invoice_number={data ? data.bills_by_pk.invoice_number : null}
|
|
||||||
vendorid={data ? data.bills_by_pk.vendorid : null}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<JobDocumentsGallery
|
|
||||||
jobId={data ? data.bills_by_pk.jobid : null}
|
|
||||||
billId={search.billid}
|
|
||||||
documentsList={data ? data.bills_by_pk.documents : []}
|
|
||||||
billsCallback={refetch}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const transformData = (data) => {
|
|
||||||
return data
|
|
||||||
? {
|
|
||||||
...data.bills_by_pk,
|
|
||||||
|
|
||||||
billlines: data.bills_by_pk.billlines.map((i) => {
|
|
||||||
return {
|
|
||||||
...i,
|
|
||||||
joblineid: !!i.joblineid ? i.joblineid : "noline",
|
|
||||||
applicable_taxes: {
|
|
||||||
federal:
|
|
||||||
(i.applicable_taxes && i.applicable_taxes.federal) || false,
|
|
||||||
state: (i.applicable_taxes && i.applicable_taxes.state) || false,
|
|
||||||
local: (i.applicable_taxes && i.applicable_taxes.local) || false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
date: data.bills_by_pk ? moment(data.bills_by_pk.date) : null,
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button, Form,
|
||||||
Form,
|
|
||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
Tooltip,
|
Tooltip
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -17,9 +17,8 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CiecaSelect from "../../utils/Ciecaselect";
|
import CiecaSelect from "../../utils/Ciecaselect";
|
||||||
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
|
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
|
||||||
import BilllineAddInventory from "../billline-add-inventory/billline-add-inventory.component";
|
import BilllineAddInventory from "../billline-add-inventory/billline-add-inventory.component";
|
||||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { DateFormatter } from "../../utils/DateFormatter";
|
|||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
|
import BillDeleteButton from "../bill-delete-button/bill-delete-button.component";
|
||||||
|
import BillDetailEditReturnComponent from "../bill-detail-edit/bill-detail-edit-return.component";
|
||||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -58,6 +59,14 @@ export function BillsListTableComponent({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<BillDeleteButton bill={record} />
|
<BillDeleteButton bill={record} />
|
||||||
|
<BillDetailEditReturnComponent
|
||||||
|
data={{ bills_by_pk: { ...record, jobid: job.id } }}
|
||||||
|
disabled={
|
||||||
|
record.is_credit_memo ||
|
||||||
|
record.vendorid === bodyshop.inhousevendorid ||
|
||||||
|
jobRO
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
disabled={
|
disabled={
|
||||||
record.is_credit_memo ||
|
record.is_credit_memo ||
|
||||||
|
|||||||
@@ -279,31 +279,80 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
|||||||
<Form.Item label={t("courtesycars.fields.notes")} name="notes">
|
<Form.Item label={t("courtesycars.fields.notes")} name="notes">
|
||||||
<Input.TextArea />
|
<Input.TextArea />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<div>
|
||||||
|
<Form.Item
|
||||||
|
label={t("courtesycars.fields.registrationexpires")}
|
||||||
|
name="registrationexpires"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<FormDatePicker />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
shouldUpdate={(p, c) =>
|
||||||
|
p.registrationexpires !== c.registrationexpires
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
const expires = form.getFieldValue("registrationexpires");
|
||||||
|
|
||||||
<Form.Item
|
const dateover = expires && moment(expires).isBefore(moment());
|
||||||
label={t("courtesycars.fields.registrationexpires")}
|
|
||||||
name="registrationexpires"
|
if (dateover)
|
||||||
rules={[
|
return (
|
||||||
{
|
<Space direction="vertical" style={{ color: "tomato" }}>
|
||||||
required: true,
|
<span>
|
||||||
//message: t("general.validation.required"),
|
<WarningFilled style={{ marginRight: ".3rem" }} />
|
||||||
},
|
{t("contracts.labels.dateinpast")}
|
||||||
]}
|
</span>
|
||||||
>
|
</Space>
|
||||||
<FormDatePicker />
|
);
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
return <></>;
|
||||||
label={t("courtesycars.fields.insuranceexpires")}
|
}}
|
||||||
name="insuranceexpires"
|
</Form.Item>
|
||||||
rules={[
|
</div>
|
||||||
{
|
<div>
|
||||||
required: true,
|
<Form.Item
|
||||||
//message: t("general.validation.required"),
|
label={t("courtesycars.fields.insuranceexpires")}
|
||||||
},
|
name="insuranceexpires"
|
||||||
]}
|
rules={[
|
||||||
>
|
{
|
||||||
<FormDatePicker />
|
required: true,
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<FormDatePicker />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
shouldUpdate={(p, c) =>
|
||||||
|
p.insuranceexpires !== c.insuranceexpires
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
const expires = form.getFieldValue("insuranceexpires");
|
||||||
|
|
||||||
|
const dateover = expires && moment(expires).isBefore(moment());
|
||||||
|
|
||||||
|
if (dateover)
|
||||||
|
return (
|
||||||
|
<Space direction="vertical" style={{ color: "tomato" }}>
|
||||||
|
<span>
|
||||||
|
<WarningFilled style={{ marginRight: ".3rem" }} />
|
||||||
|
{t("contracts.labels.dateinpast")}
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
<Form.Item label={t("courtesycars.fields.dailycost")} name="dailycost">
|
<Form.Item label={t("courtesycars.fields.dailycost")} name="dailycost">
|
||||||
<CurrencyInput />
|
<CurrencyInput />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ const CourtesyCarStatusComponent = ({ value, onChange }, ref) => {
|
|||||||
<Option value="courtesycars.status.sold">
|
<Option value="courtesycars.status.sold">
|
||||||
{t("courtesycars.status.sold")}
|
{t("courtesycars.status.sold")}
|
||||||
</Option>
|
</Option>
|
||||||
|
<Option value="courtesycars.status.leasereturn">
|
||||||
|
{t("courtesycars.status.leasereturn")}
|
||||||
|
</Option>
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
text: t("courtesycars.status.sold"),
|
text: t("courtesycars.status.sold"),
|
||||||
value: "courtesycars.status.sold",
|
value: "courtesycars.status.sold",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: t("courtesycars.status.leasereturn"),
|
||||||
|
value: "courtesycars.status.leasereturn",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
onFilter: (value, record) => value.includes(record.status),
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import AlertComponent from "../alert/alert.component";
|
|||||||
import { Prompt, useLocation } from "react-router-dom";
|
import { Prompt, useLocation } from "react-router-dom";
|
||||||
import "./form-fields-changed.styles.scss";
|
import "./form-fields-changed.styles.scss";
|
||||||
|
|
||||||
export default function FormsFieldChanged({ form }) {
|
export default function FormsFieldChanged({ form, skipPrompt }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
@@ -25,7 +25,7 @@ export default function FormsFieldChanged({ form }) {
|
|||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: "100%" }}>
|
<Space direction="vertical" style={{ width: "100%" }}>
|
||||||
<Prompt
|
<Prompt
|
||||||
when={true}
|
when={skipPrompt ? false : true}
|
||||||
message={(location) => {
|
message={(location) => {
|
||||||
if (loc.pathname === location.pathname) return false;
|
if (loc.pathname === location.pathname) return false;
|
||||||
return t("general.messages.unsavedchangespopup");
|
return t("general.messages.unsavedchangespopup");
|
||||||
|
|||||||
@@ -477,7 +477,7 @@ export function JobLinesComponent({
|
|||||||
setState({
|
setState({
|
||||||
...state,
|
...state,
|
||||||
filteredInfo: {
|
filteredInfo: {
|
||||||
part_type: ["PAN,PAC,PAR,PAL,PAA,PAM,PAP,PAS,PASL,PAG"],
|
part_type: ["PAN","PAC","PAR","PAL","PAA","PAM","PAP","PAS","PASL","PAG"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -66,10 +66,8 @@ export function JobLineConvertToLabor({
|
|||||||
const newAdjustments = _.cloneDeep(
|
const newAdjustments = _.cloneDeep(
|
||||||
existingAdjustments.data.jobs_by_pk.lbr_adjustments
|
existingAdjustments.data.jobs_by_pk.lbr_adjustments
|
||||||
);
|
);
|
||||||
|
const adjustment = calculateAdjustment({ mod_lbr_ty, job, jobline });
|
||||||
newAdjustments[mod_lbr_ty] =
|
newAdjustments[mod_lbr_ty] = (newAdjustments[mod_lbr_ty] || 0) + adjustment;
|
||||||
(newAdjustments[mod_lbr_ty] || 0) +
|
|
||||||
calculateAdjustment({ mod_lbr_ty, job, jobline });
|
|
||||||
|
|
||||||
const jobUpdate = client.mutate({
|
const jobUpdate = client.mutate({
|
||||||
mutation: UPDATE_JOB,
|
mutation: UPDATE_JOB,
|
||||||
@@ -83,7 +81,13 @@ export function JobLineConvertToLabor({
|
|||||||
mutation: UPDATE_JOB_LINE,
|
mutation: UPDATE_JOB_LINE,
|
||||||
variables: {
|
variables: {
|
||||||
lineId: jobline.id,
|
lineId: jobline.id,
|
||||||
line: { convertedtolbr: true },
|
line: {
|
||||||
|
convertedtolbr: true,
|
||||||
|
convertedtolbr_data: {
|
||||||
|
mod_lbr_ty: mod_lbr_ty,
|
||||||
|
mod_lb_hrs: adjustment,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useMutation } from "@apollo/client";
|
||||||
import { Button, Form, notification } from "antd";
|
import { Button, Form, notification } from "antd";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
@@ -87,6 +88,7 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
|
|||||||
: null,
|
: null,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<FormFieldsChanged form={form} />
|
||||||
<LayoutFormRow header={t("jobs.forms.estdates")}>
|
<LayoutFormRow header={t("jobs.forms.estdates")}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("jobs.fields.date_estimated")}
|
label={t("jobs.fields.date_estimated")}
|
||||||
|
|||||||
@@ -181,6 +181,11 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
|||||||
<DataLabel key="4" label={t("owners.fields.ownr_ea")}>
|
<DataLabel key="4" label={t("owners.fields.ownr_ea")}>
|
||||||
{job.ownr_ea || ""}
|
{job.ownr_ea || ""}
|
||||||
</DataLabel>
|
</DataLabel>
|
||||||
|
{job.owner?.tax_number && (
|
||||||
|
<DataLabel key="5" label={t("owners.fields.tax_number")}>
|
||||||
|
{job.owner?.tax_number || ""}
|
||||||
|
</DataLabel>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { EditFilled } from "@ant-design/icons";
|
import { EditFilled } from "@ant-design/icons";
|
||||||
import { Card, Col, Row, Space, Table } from "antd";
|
import { Card, Col, Row, Space, Table } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useMemo, 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";
|
||||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component";
|
import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component";
|
||||||
import "./labor-allocations-table.styles.scss";
|
import "./labor-allocations-table.styles.scss";
|
||||||
@@ -44,10 +45,14 @@ export function LaborAllocationsTable({
|
|||||||
if (!jobId) setTotals([]);
|
if (!jobId) setTotals([]);
|
||||||
}, [joblines, timetickets, bodyshop, adjustments, jobId]);
|
}, [joblines, timetickets, bodyshop, adjustments, jobId]);
|
||||||
|
|
||||||
// const convertedLines = useMemo(
|
const convertedLines = useMemo(
|
||||||
// () => joblines && joblines.filter((j) => j.convertedtolbr),
|
() => joblines && joblines.filter((j) => j.convertedtolbr),
|
||||||
// [joblines]
|
[joblines]
|
||||||
// );
|
);
|
||||||
|
console.log(
|
||||||
|
"🚀 ~ file: labor-allocations-table.component.jsx ~ line 52 ~ convertedLines",
|
||||||
|
convertedLines
|
||||||
|
);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@@ -120,52 +125,68 @@ export function LaborAllocationsTable({
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
// const convertedTableCols = [
|
const convertedTableCols = [
|
||||||
// {
|
{
|
||||||
// title: t("joblines.fields.line_desc"),
|
title: t("joblines.fields.line_desc"),
|
||||||
// dataIndex: "line_desc",
|
dataIndex: "line_desc",
|
||||||
// key: "line_desc",
|
key: "line_desc",
|
||||||
// ellipsis: true,
|
ellipsis: true,
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// title: t("joblines.fields.op_code_desc"),
|
title: t("joblines.fields.op_code_desc"),
|
||||||
// dataIndex: "op_code_desc",
|
dataIndex: "op_code_desc",
|
||||||
// key: "op_code_desc",
|
key: "op_code_desc",
|
||||||
// ellipsis: true,
|
ellipsis: true,
|
||||||
// render: (text, record) =>
|
render: (text, record) =>
|
||||||
// `${record.op_code_desc || ""}${
|
`${record.op_code_desc || ""}${
|
||||||
// record.alt_partm ? ` ${record.alt_partm}` : ""
|
record.alt_partm ? ` ${record.alt_partm}` : ""
|
||||||
// }`,
|
}`,
|
||||||
// },
|
},
|
||||||
|
|
||||||
// {
|
{
|
||||||
// title: t("joblines.fields.act_price"),
|
title: t("joblines.fields.act_price"),
|
||||||
// dataIndex: "act_price",
|
dataIndex: "act_price",
|
||||||
// key: "act_price",
|
key: "act_price",
|
||||||
// ellipsis: true,
|
ellipsis: true,
|
||||||
// render: (text, record) => (
|
render: (text, record) => (
|
||||||
// <>
|
<>
|
||||||
// <CurrencyFormatter>
|
<CurrencyFormatter>
|
||||||
// {record.db_ref === "900510" || record.db_ref === "900511"
|
{record.db_ref === "900510" || record.db_ref === "900511"
|
||||||
// ? record.prt_dsmk_m
|
? record.prt_dsmk_m
|
||||||
// : record.act_price}
|
: record.act_price}
|
||||||
// </CurrencyFormatter>
|
</CurrencyFormatter>
|
||||||
// {record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
|
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
|
||||||
// <span
|
<span
|
||||||
// style={{ marginLeft: ".2rem" }}
|
style={{ marginLeft: ".2rem" }}
|
||||||
// >{`(${record.prt_dsmk_p}%)`}</span>
|
>{`(${record.prt_dsmk_p}%)`}</span>
|
||||||
// ) : (
|
) : (
|
||||||
// <></>
|
<></>
|
||||||
// )}
|
)}
|
||||||
// </>
|
</>
|
||||||
// ),
|
),
|
||||||
// },
|
},
|
||||||
// {
|
{
|
||||||
// title: t("joblines.fields.part_qty"),
|
title: t("joblines.fields.part_qty"),
|
||||||
// dataIndex: "part_qty",
|
dataIndex: "part_qty",
|
||||||
// key: "part_qty",
|
key: "part_qty",
|
||||||
// },
|
},
|
||||||
// ];
|
{
|
||||||
|
title: t("joblines.fields.mod_lbr_ty"),
|
||||||
|
dataIndex: "conv_mod_lbr_ty",
|
||||||
|
key: "conv_mod_lbr_ty",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.convertedtolbr_data && record.convertedtolbr_data.mod_lbr_ty,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("joblines.fields.mod_lb_hrs"),
|
||||||
|
dataIndex: "conv_mod_lb_hrs",
|
||||||
|
key: "conv_mod_lb_hrs",
|
||||||
|
render: (text, record) =>
|
||||||
|
record.convertedtolbr_data &&
|
||||||
|
record.convertedtolbr_data.mod_lb_hrs &&
|
||||||
|
record.convertedtolbr_data.mod_lb_hrs.toFixed(1),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||||
@@ -187,23 +208,21 @@ export function LaborAllocationsTable({
|
|||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
{
|
{convertedLines && convertedLines.length > 0 && (
|
||||||
// convertedLines && convertedLines.length > 0 && (
|
<Col span={24}>
|
||||||
// <Col span={24}>
|
<Card title={t("jobs.labels.convertedtolabor")}>
|
||||||
// <Card title={t("jobs.labels.convertedtolabor")}>
|
<Table
|
||||||
// <Table
|
columns={convertedTableCols}
|
||||||
// columns={convertedTableCols}
|
rowKey="id"
|
||||||
// rowKey="id"
|
pagination={false}
|
||||||
// pagination={false}
|
dataSource={convertedLines}
|
||||||
// dataSource={convertedLines}
|
scroll={{
|
||||||
// scroll={{
|
x: true,
|
||||||
// x: true,
|
}}
|
||||||
// }}
|
/>
|
||||||
// />
|
</Card>
|
||||||
// </Card>
|
</Col>
|
||||||
// </Col>
|
)}
|
||||||
// )
|
|
||||||
}
|
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,12 @@ export default function OwnerDetailFormComponent({ form, loading }) {
|
|||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("owners.fields.tax_number")}
|
||||||
|
name="tax_number"
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<Form.Item label={t("owners.fields.note")} name="note">
|
<Form.Item label={t("owners.fields.note")} name="note">
|
||||||
<Input.TextArea rows={4} />
|
<Input.TextArea rows={4} />
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ function OwnerDetailFormContainer({ owner, refetch }) {
|
|||||||
if (!!result.errors) {
|
if (!!result.errors) {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("owners.errors.saving", {
|
message: t("owners.errors.saving", {
|
||||||
message: JSON.stringify(result.errors),
|
error: JSON.stringify(result.errors),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
setLoading(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ export function ScheduleCalendarHeaderComponent({
|
|||||||
const ATSToday = useMemo(() => {
|
const ATSToday = useMemo(() => {
|
||||||
if (!events) return [];
|
if (!events) return [];
|
||||||
return _.groupBy(
|
return _.groupBy(
|
||||||
events.filter((e) => moment(date).isSame(moment(e.start), "day")),
|
events.filter(
|
||||||
|
(e) => !e.vacation && moment(date).isSame(moment(e.start), "day")
|
||||||
|
),
|
||||||
"job.alt_transport"
|
"job.alt_transport"
|
||||||
);
|
);
|
||||||
}, [events, date]);
|
}, [events, date]);
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export default function ScoreboardTimeTickets() {
|
|||||||
totalThisMonth: 0,
|
totalThisMonth: 0,
|
||||||
totalLastMonth: 0,
|
totalLastMonth: 0,
|
||||||
totalOverPeriod: 0,
|
totalOverPeriod: 0,
|
||||||
|
actualTotalOverPeriod: 0,
|
||||||
employees: {},
|
employees: {},
|
||||||
};
|
};
|
||||||
data.fixedperiod.forEach((ticket) => {
|
data.fixedperiod.forEach((ticket) => {
|
||||||
@@ -92,6 +93,7 @@ export default function ScoreboardTimeTickets() {
|
|||||||
totalThisMonth: 0,
|
totalThisMonth: 0,
|
||||||
totalLastMonth: 0,
|
totalLastMonth: 0,
|
||||||
totalOverPeriod: 0,
|
totalOverPeriod: 0,
|
||||||
|
actualTotalOverPeriod: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +184,9 @@ export default function ScoreboardTimeTickets() {
|
|||||||
ret.employees[ticket.employee.employee_number].totalOverPeriod =
|
ret.employees[ticket.employee.employee_number].totalOverPeriod =
|
||||||
ret.employees[ticket.employee.employee_number].totalOverPeriod +
|
ret.employees[ticket.employee.employee_number].totalOverPeriod +
|
||||||
ticket.productivehrs;
|
ticket.productivehrs;
|
||||||
|
ret.employees[ticket.employee.employee_number].actualTotalOverPeriod =
|
||||||
|
ret.employees[ticket.employee.employee_number].actualTotalOverPeriod +
|
||||||
|
(ticket.actualhrs || 0);
|
||||||
|
|
||||||
if (!totals.employees[ticket.employee.employee_number])
|
if (!totals.employees[ticket.employee.employee_number])
|
||||||
totals.employees[ticket.employee.employee_number] = {
|
totals.employees[ticket.employee.employee_number] = {
|
||||||
@@ -219,7 +224,7 @@ export default function ScoreboardTimeTickets() {
|
|||||||
roundObject(ret);
|
roundObject(ret);
|
||||||
roundObject(totals);
|
roundObject(totals);
|
||||||
roundObject(ret2);
|
roundObject(ret2);
|
||||||
console.log(ret);
|
|
||||||
return {
|
return {
|
||||||
fixed: ret,
|
fixed: ret,
|
||||||
timeperiod: {
|
timeperiod: {
|
||||||
@@ -231,6 +236,8 @@ export default function ScoreboardTimeTickets() {
|
|||||||
};
|
};
|
||||||
}, [fixedPeriods, data, startDate, endDate]);
|
}, [fixedPeriods, data, startDate, endDate]);
|
||||||
|
|
||||||
|
console.log(calculatedData);
|
||||||
|
|
||||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
if (loading) return <LoadingSpinner />;
|
if (loading) return <LoadingSpinner />;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export default connect(
|
|||||||
|
|
||||||
export function ScoreboardTicketsStats({ data, bodyshop }) {
|
export function ScoreboardTicketsStats({ data, bodyshop }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
console.log(data);
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t("employees.fields.employee_number"),
|
title: t("employees.fields.employee_number"),
|
||||||
@@ -57,6 +56,16 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
|||||||
key: "totalOverPeriod",
|
key: "totalOverPeriod",
|
||||||
sorter: (a, b) => a.totalOverPeriod - b.totalOverPeriod,
|
sorter: (a, b) => a.totalOverPeriod - b.totalOverPeriod,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("scoreboard.labels.efficiencyoverperiod"),
|
||||||
|
dataIndex: "efficiencyoverperiod",
|
||||||
|
key: "efficiencyoverperiod",
|
||||||
|
render: (text, record) =>
|
||||||
|
`${(
|
||||||
|
(record.totalOverPeriod / (record.actualTotalOverPeriod || 1)) *
|
||||||
|
100
|
||||||
|
).toFixed(1)} %`,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const tableData = data
|
const tableData = data
|
||||||
@@ -112,6 +121,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
|
|||||||
<Col md={24} lg={20}>
|
<Col md={24} lg={20}>
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
rowKey='employee_number'
|
||||||
dataSource={tableData}
|
dataSource={tableData}
|
||||||
id="employee_number"
|
id="employee_number"
|
||||||
scroll={{ y: "300px" }}
|
scroll={{ y: "300px" }}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export default function ShopEmployeeAddVacation({ employee }) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("employees.successes.added"),
|
message: t("employees.successes.vacationadded"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -1494,7 +1494,7 @@ export default function ShopInfoGeneral({ form }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ReceivableCustomFieldSelect = (
|
const ReceivableCustomFieldSelect = (
|
||||||
<Select>
|
<Select allowClear>
|
||||||
<Select.Option value="v_vin">VIN</Select.Option>
|
<Select.Option value="v_vin">VIN</Select.Option>
|
||||||
<Select.Option value="clm_no">Claim No.</Select.Option>
|
<Select.Option value="clm_no">Claim No.</Select.Option>
|
||||||
<Select.Option value="ded_amt">Deductible Amount</Select.Option>
|
<Select.Option value="ded_amt">Deductible Amount</Select.Option>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
|
|
||||||
export default function VehicleVinDisplay({ children }) {
|
export default function VehicleVinDisplay({ children }) {
|
||||||
if (!children) return null;
|
if (!children) return null;
|
||||||
console.log(children);
|
|
||||||
if (typeof children !== "string" || children.length !== 17) return children;
|
if (typeof children !== "string" || children.length !== 17) return children;
|
||||||
const vin = children.trim();
|
const vin = children.trim();
|
||||||
|
|
||||||
|
|||||||
@@ -1,75 +1,77 @@
|
|||||||
import { gql } from "@apollo/client";
|
import { gql } from "@apollo/client";
|
||||||
|
|
||||||
export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
|
export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
|
||||||
query QUERY_ALL_ACTIVE_APPOINTMENTS(
|
query QUERY_ALL_ACTIVE_APPOINTMENTS(
|
||||||
$start: timestamptz!
|
$start: timestamptz!
|
||||||
$end: timestamptz!
|
$end: timestamptz!
|
||||||
$startd: date!
|
$startd: date!
|
||||||
$endd: date!
|
$endd: date!
|
||||||
|
) {
|
||||||
|
employee_vacation(
|
||||||
|
where: { _or: [{ start: { _gte: $startd } },
|
||||||
|
{ end: { _lte: $endd } },
|
||||||
|
{_and:[{start:{_lte: $startd}},{end:{_gte:$endd}}]}] }
|
||||||
) {
|
) {
|
||||||
employee_vacation(
|
id
|
||||||
where: { _or: [{ start: { _gte: $startd } }, { end: { _lte: $endd } }] }
|
start
|
||||||
) {
|
end
|
||||||
|
employee {
|
||||||
id
|
id
|
||||||
start
|
last_name
|
||||||
end
|
first_name
|
||||||
employee {
|
}
|
||||||
id
|
}
|
||||||
last_name
|
appointments(
|
||||||
first_name
|
where: {
|
||||||
|
canceled: { _eq: false }
|
||||||
|
end: { _lte: $end }
|
||||||
|
start: { _gte: $start }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
start
|
||||||
|
id
|
||||||
|
end
|
||||||
|
arrived
|
||||||
|
title
|
||||||
|
isintake
|
||||||
|
block
|
||||||
|
color
|
||||||
|
note
|
||||||
|
job {
|
||||||
|
alt_transport
|
||||||
|
ro_number
|
||||||
|
ownr_ln
|
||||||
|
ownr_co_nm
|
||||||
|
ownr_fn
|
||||||
|
ownr_ph1
|
||||||
|
ownr_ph2
|
||||||
|
ownr_ea
|
||||||
|
clm_total
|
||||||
|
id
|
||||||
|
clm_no
|
||||||
|
ins_co_nm
|
||||||
|
v_model_yr
|
||||||
|
v_make_desc
|
||||||
|
v_model_desc
|
||||||
|
labhrs: joblines_aggregate(
|
||||||
|
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
sum {
|
||||||
|
mod_lb_hrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
larhrs: joblines_aggregate(
|
||||||
|
where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
sum {
|
||||||
|
mod_lb_hrs
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appointments(
|
|
||||||
where: {
|
|
||||||
canceled: { _eq: false }
|
|
||||||
end: { _lte: $end }
|
|
||||||
start: { _gte: $start }
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
start
|
|
||||||
id
|
|
||||||
end
|
|
||||||
arrived
|
|
||||||
title
|
|
||||||
isintake
|
|
||||||
block
|
|
||||||
color
|
|
||||||
note
|
|
||||||
job {
|
|
||||||
alt_transport
|
|
||||||
ro_number
|
|
||||||
ownr_ln
|
|
||||||
ownr_co_nm
|
|
||||||
ownr_fn
|
|
||||||
ownr_ph1
|
|
||||||
ownr_ph2
|
|
||||||
ownr_ea
|
|
||||||
clm_total
|
|
||||||
id
|
|
||||||
clm_no
|
|
||||||
ins_co_nm
|
|
||||||
v_model_yr
|
|
||||||
v_make_desc
|
|
||||||
v_model_desc
|
|
||||||
labhrs: joblines_aggregate(
|
|
||||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
|
||||||
) {
|
|
||||||
aggregate {
|
|
||||||
sum {
|
|
||||||
mod_lb_hrs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
larhrs: joblines_aggregate(
|
|
||||||
where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }
|
|
||||||
) {
|
|
||||||
aggregate {
|
|
||||||
sum {
|
|
||||||
mod_lb_hrs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export const GET_LINE_TICKET_BY_PK = gql`
|
|||||||
lbr_amt
|
lbr_amt
|
||||||
op_code_desc
|
op_code_desc
|
||||||
convertedtolbr
|
convertedtolbr
|
||||||
|
convertedtolbr_data
|
||||||
}
|
}
|
||||||
timetickets(where: { jobid: { _eq: $id } }) {
|
timetickets(where: { jobid: { _eq: $id } }) {
|
||||||
actualhrs
|
actualhrs
|
||||||
@@ -179,6 +180,7 @@ export const UPDATE_JOB_LINE = gql`
|
|||||||
status
|
status
|
||||||
removed
|
removed
|
||||||
convertedtolbr
|
convertedtolbr
|
||||||
|
convertedtolbr_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -621,6 +621,7 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
ownr_ctry
|
ownr_ctry
|
||||||
ownr_ph1
|
ownr_ph1
|
||||||
ownr_ph2
|
ownr_ph2
|
||||||
|
tax_number
|
||||||
}
|
}
|
||||||
labor_rate_desc
|
labor_rate_desc
|
||||||
rate_la1
|
rate_la1
|
||||||
@@ -855,6 +856,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
|
|||||||
id
|
id
|
||||||
allow_text_message
|
allow_text_message
|
||||||
preferred_contact
|
preferred_contact
|
||||||
|
tax_number
|
||||||
}
|
}
|
||||||
vehicleid
|
vehicleid
|
||||||
v_model_yr
|
v_model_yr
|
||||||
@@ -1907,6 +1909,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
|
|||||||
profitcenter_part
|
profitcenter_part
|
||||||
prt_dsmk_p
|
prt_dsmk_p
|
||||||
convertedtolbr
|
convertedtolbr
|
||||||
|
convertedtolbr_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export const QUERY_OWNER_BY_ID = gql`
|
|||||||
ownr_zip
|
ownr_zip
|
||||||
preferred_contact
|
preferred_contact
|
||||||
note
|
note
|
||||||
|
tax_number
|
||||||
jobs {
|
jobs {
|
||||||
id
|
id
|
||||||
ro_number
|
ro_number
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
|
|||||||
date
|
date
|
||||||
id
|
id
|
||||||
rate
|
rate
|
||||||
|
actualhrs
|
||||||
productivehrs
|
productivehrs
|
||||||
memo
|
memo
|
||||||
jobid
|
jobid
|
||||||
|
|||||||
@@ -678,6 +678,7 @@
|
|||||||
"refuelqty": "Refuel qty.?"
|
"refuelqty": "Refuel qty.?"
|
||||||
},
|
},
|
||||||
"correctdataonform": "Please review the information above. If any of it is not correct, you can fix it later.",
|
"correctdataonform": "Please review the information above. If any of it is not correct, you can fix it later.",
|
||||||
|
"dateinpast": "Date is in the past.",
|
||||||
"dlexpirebeforereturn": "The driver's license expires before the car is expected to return. ",
|
"dlexpirebeforereturn": "The driver's license expires before the car is expected to return. ",
|
||||||
"driverinformation": "Driver's Information",
|
"driverinformation": "Driver's Information",
|
||||||
"findcontract": "Find Contract",
|
"findcontract": "Find Contract",
|
||||||
@@ -752,6 +753,7 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"in": "Available",
|
"in": "Available",
|
||||||
"inservice": "In Service",
|
"inservice": "In Service",
|
||||||
|
"leasereturn": "Lease Returned",
|
||||||
"out": "Rented",
|
"out": "Rented",
|
||||||
"sold": "Sold"
|
"sold": "Sold"
|
||||||
},
|
},
|
||||||
@@ -920,7 +922,8 @@
|
|||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"delete": "Employee deleted successfully.",
|
"delete": "Employee deleted successfully.",
|
||||||
"save": "Employee saved successfully."
|
"save": "Employee saved successfully.",
|
||||||
|
"vacationadded": "Employee vacation added."
|
||||||
},
|
},
|
||||||
"validation": {
|
"validation": {
|
||||||
"unique_employee_number": "You must enter a unique employee number."
|
"unique_employee_number": "You must enter a unique employee number."
|
||||||
@@ -1935,6 +1938,7 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"noaccess": "The record does not exist or you do not have access to it. ",
|
"noaccess": "The record does not exist or you do not have access to it. ",
|
||||||
|
"saving": "Error saving owner. {{error}}.",
|
||||||
"selectexistingornew": "Select an existing owner record or create a new one. "
|
"selectexistingornew": "Select an existing owner record or create a new one. "
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
@@ -1955,7 +1959,8 @@
|
|||||||
"ownr_st": "Province/State",
|
"ownr_st": "Province/State",
|
||||||
"ownr_title": "Title",
|
"ownr_title": "Title",
|
||||||
"ownr_zip": "Zip/Postal Code",
|
"ownr_zip": "Zip/Postal Code",
|
||||||
"preferred_contact": "Preferred Contact Method"
|
"preferred_contact": "Preferred Contact Method",
|
||||||
|
"tax_number": "Tax Number"
|
||||||
},
|
},
|
||||||
"forms": {
|
"forms": {
|
||||||
"address": "Address",
|
"address": "Address",
|
||||||
@@ -2456,6 +2461,7 @@
|
|||||||
"calendarperiod": "Periods based on calendar weeks/months.",
|
"calendarperiod": "Periods based on calendar weeks/months.",
|
||||||
"dailyactual": "Actual (D)",
|
"dailyactual": "Actual (D)",
|
||||||
"dailytarget": "Daily",
|
"dailytarget": "Daily",
|
||||||
|
"efficiencyoverperiod": "Efficiency over Selected Dates",
|
||||||
"jobs": "Jobs",
|
"jobs": "Jobs",
|
||||||
"lastmonth": "Last Month",
|
"lastmonth": "Last Month",
|
||||||
"lastweek": "Last Week",
|
"lastweek": "Last Week",
|
||||||
|
|||||||
@@ -678,6 +678,7 @@
|
|||||||
"refuelqty": ""
|
"refuelqty": ""
|
||||||
},
|
},
|
||||||
"correctdataonform": "",
|
"correctdataonform": "",
|
||||||
|
"dateinpast": "",
|
||||||
"dlexpirebeforereturn": "",
|
"dlexpirebeforereturn": "",
|
||||||
"driverinformation": "",
|
"driverinformation": "",
|
||||||
"findcontract": "",
|
"findcontract": "",
|
||||||
@@ -752,6 +753,7 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"in": "",
|
"in": "",
|
||||||
"inservice": "",
|
"inservice": "",
|
||||||
|
"leasereturn": "",
|
||||||
"out": "",
|
"out": "",
|
||||||
"sold": ""
|
"sold": ""
|
||||||
},
|
},
|
||||||
@@ -920,7 +922,8 @@
|
|||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"delete": "Empleado eliminado con éxito.",
|
"delete": "Empleado eliminado con éxito.",
|
||||||
"save": "Empleado guardado con éxito."
|
"save": "Empleado guardado con éxito.",
|
||||||
|
"vacationadded": ""
|
||||||
},
|
},
|
||||||
"validation": {
|
"validation": {
|
||||||
"unique_employee_number": ""
|
"unique_employee_number": ""
|
||||||
@@ -1935,6 +1938,7 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"noaccess": "El registro no existe o no tiene acceso a él.",
|
"noaccess": "El registro no existe o no tiene acceso a él.",
|
||||||
|
"saving": "",
|
||||||
"selectexistingornew": ""
|
"selectexistingornew": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
@@ -1955,7 +1959,8 @@
|
|||||||
"ownr_st": "Provincia del estado",
|
"ownr_st": "Provincia del estado",
|
||||||
"ownr_title": "Título",
|
"ownr_title": "Título",
|
||||||
"ownr_zip": "código postal",
|
"ownr_zip": "código postal",
|
||||||
"preferred_contact": "Método de Contacto Preferido"
|
"preferred_contact": "Método de Contacto Preferido",
|
||||||
|
"tax_number": ""
|
||||||
},
|
},
|
||||||
"forms": {
|
"forms": {
|
||||||
"address": "",
|
"address": "",
|
||||||
@@ -2456,6 +2461,7 @@
|
|||||||
"calendarperiod": "",
|
"calendarperiod": "",
|
||||||
"dailyactual": "",
|
"dailyactual": "",
|
||||||
"dailytarget": "",
|
"dailytarget": "",
|
||||||
|
"efficiencyoverperiod": "",
|
||||||
"jobs": "",
|
"jobs": "",
|
||||||
"lastmonth": "",
|
"lastmonth": "",
|
||||||
"lastweek": "",
|
"lastweek": "",
|
||||||
|
|||||||
@@ -678,6 +678,7 @@
|
|||||||
"refuelqty": ""
|
"refuelqty": ""
|
||||||
},
|
},
|
||||||
"correctdataonform": "",
|
"correctdataonform": "",
|
||||||
|
"dateinpast": "",
|
||||||
"dlexpirebeforereturn": "",
|
"dlexpirebeforereturn": "",
|
||||||
"driverinformation": "",
|
"driverinformation": "",
|
||||||
"findcontract": "",
|
"findcontract": "",
|
||||||
@@ -752,6 +753,7 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"in": "",
|
"in": "",
|
||||||
"inservice": "",
|
"inservice": "",
|
||||||
|
"leasereturn": "",
|
||||||
"out": "",
|
"out": "",
|
||||||
"sold": ""
|
"sold": ""
|
||||||
},
|
},
|
||||||
@@ -920,7 +922,8 @@
|
|||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"delete": "L'employé a bien été supprimé.",
|
"delete": "L'employé a bien été supprimé.",
|
||||||
"save": "L'employé a enregistré avec succès."
|
"save": "L'employé a enregistré avec succès.",
|
||||||
|
"vacationadded": ""
|
||||||
},
|
},
|
||||||
"validation": {
|
"validation": {
|
||||||
"unique_employee_number": ""
|
"unique_employee_number": ""
|
||||||
@@ -1935,6 +1938,7 @@
|
|||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.",
|
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.",
|
||||||
|
"saving": "",
|
||||||
"selectexistingornew": ""
|
"selectexistingornew": ""
|
||||||
},
|
},
|
||||||
"fields": {
|
"fields": {
|
||||||
@@ -1955,7 +1959,8 @@
|
|||||||
"ownr_st": "Etat / Province",
|
"ownr_st": "Etat / Province",
|
||||||
"ownr_title": "Titre",
|
"ownr_title": "Titre",
|
||||||
"ownr_zip": "Zip / code postal",
|
"ownr_zip": "Zip / code postal",
|
||||||
"preferred_contact": "Méthode de contact préférée"
|
"preferred_contact": "Méthode de contact préférée",
|
||||||
|
"tax_number": ""
|
||||||
},
|
},
|
||||||
"forms": {
|
"forms": {
|
||||||
"address": "",
|
"address": "",
|
||||||
@@ -2456,6 +2461,7 @@
|
|||||||
"calendarperiod": "",
|
"calendarperiod": "",
|
||||||
"dailyactual": "",
|
"dailyactual": "",
|
||||||
"dailytarget": "",
|
"dailytarget": "",
|
||||||
|
"efficiencyoverperiod": "",
|
||||||
"jobs": "",
|
"jobs": "",
|
||||||
"lastmonth": "",
|
"lastmonth": "",
|
||||||
"lastweek": "",
|
"lastweek": "",
|
||||||
|
|||||||
@@ -2385,6 +2385,7 @@
|
|||||||
- bett_type
|
- bett_type
|
||||||
- cert_part
|
- cert_part
|
||||||
- convertedtolbr
|
- convertedtolbr
|
||||||
|
- convertedtolbr_data
|
||||||
- created_at
|
- created_at
|
||||||
- db_hrs
|
- db_hrs
|
||||||
- db_price
|
- db_price
|
||||||
@@ -2449,6 +2450,7 @@
|
|||||||
- bett_type
|
- bett_type
|
||||||
- cert_part
|
- cert_part
|
||||||
- convertedtolbr
|
- convertedtolbr
|
||||||
|
- convertedtolbr_data
|
||||||
- created_at
|
- created_at
|
||||||
- db_hrs
|
- db_hrs
|
||||||
- db_price
|
- db_price
|
||||||
@@ -2524,6 +2526,7 @@
|
|||||||
- bett_type
|
- bett_type
|
||||||
- cert_part
|
- cert_part
|
||||||
- convertedtolbr
|
- convertedtolbr
|
||||||
|
- convertedtolbr_data
|
||||||
- created_at
|
- created_at
|
||||||
- db_hrs
|
- db_hrs
|
||||||
- db_price
|
- db_price
|
||||||
@@ -4012,6 +4015,7 @@
|
|||||||
- ownr_zip
|
- ownr_zip
|
||||||
- preferred_contact
|
- preferred_contact
|
||||||
- shopid
|
- shopid
|
||||||
|
- tax_number
|
||||||
- updated_at
|
- updated_at
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
@@ -4047,6 +4051,7 @@
|
|||||||
- ownr_zip
|
- ownr_zip
|
||||||
- preferred_contact
|
- preferred_contact
|
||||||
- shopid
|
- shopid
|
||||||
|
- tax_number
|
||||||
- updated_at
|
- updated_at
|
||||||
filter:
|
filter:
|
||||||
bodyshop:
|
bodyshop:
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."joblines" add column "convertedtolbr_data" jsonb
|
||||||
|
-- not null default jsonb_build_object();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."joblines" add column "convertedtolbr_data" jsonb
|
||||||
|
not null default jsonb_build_object();
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."owners" add column "tax_number" text
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."owners" add column "tax_number" text
|
||||||
|
null;
|
||||||
@@ -218,7 +218,7 @@ async function JobCostingMulti(req, res) {
|
|||||||
(multiSummary.summaryData.totalLaborGp.getAmount() /
|
(multiSummary.summaryData.totalLaborGp.getAmount() /
|
||||||
multiSummary.summaryData.totalLaborSales.getAmount()) *
|
multiSummary.summaryData.totalLaborSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
multiSummary.summaryData.totalLaborGppercentFormatted = formatGpPercent(
|
multiSummary.summaryData.totalLaborGppercentFormatted = formatGpPercent(
|
||||||
multiSummary.summaryData.totalLaborGppercent
|
multiSummary.summaryData.totalLaborGppercent
|
||||||
);
|
);
|
||||||
@@ -227,7 +227,7 @@ async function JobCostingMulti(req, res) {
|
|||||||
(multiSummary.summaryData.totalPartsGp.getAmount() /
|
(multiSummary.summaryData.totalPartsGp.getAmount() /
|
||||||
multiSummary.summaryData.totalPartsSales.getAmount()) *
|
multiSummary.summaryData.totalPartsSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
|
|
||||||
multiSummary.summaryData.totalPartsGppercentFormatted = formatGpPercent(
|
multiSummary.summaryData.totalPartsGppercentFormatted = formatGpPercent(
|
||||||
multiSummary.summaryData.totalPartsGppercent
|
multiSummary.summaryData.totalPartsGppercent
|
||||||
@@ -237,7 +237,7 @@ async function JobCostingMulti(req, res) {
|
|||||||
(multiSummary.summaryData.totalAdditionalGp.getAmount() /
|
(multiSummary.summaryData.totalAdditionalGp.getAmount() /
|
||||||
multiSummary.summaryData.totalAdditionalSales.getAmount()) *
|
multiSummary.summaryData.totalAdditionalSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
|
|
||||||
multiSummary.summaryData.totalAdditionalGppercentFormatted =
|
multiSummary.summaryData.totalAdditionalGppercentFormatted =
|
||||||
formatGpPercent(multiSummary.summaryData.totalAdditionalGppercent);
|
formatGpPercent(multiSummary.summaryData.totalAdditionalGppercent);
|
||||||
@@ -246,7 +246,7 @@ async function JobCostingMulti(req, res) {
|
|||||||
(multiSummary.summaryData.totalSubletGp.getAmount() /
|
(multiSummary.summaryData.totalSubletGp.getAmount() /
|
||||||
multiSummary.summaryData.totalSubletSales.getAmount()) *
|
multiSummary.summaryData.totalSubletSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
|
|
||||||
multiSummary.summaryData.totalSubletGppercentFormatted = formatGpPercent(
|
multiSummary.summaryData.totalSubletGppercentFormatted = formatGpPercent(
|
||||||
multiSummary.summaryData.totalSubletGppercent
|
multiSummary.summaryData.totalSubletGppercent
|
||||||
@@ -256,7 +256,7 @@ async function JobCostingMulti(req, res) {
|
|||||||
(multiSummary.summaryData.gpdollars.getAmount() /
|
(multiSummary.summaryData.gpdollars.getAmount() /
|
||||||
multiSummary.summaryData.totalSales.getAmount()) *
|
multiSummary.summaryData.totalSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
|
|
||||||
multiSummary.summaryData.gppercentFormatted = formatGpPercent(
|
multiSummary.summaryData.gppercentFormatted = formatGpPercent(
|
||||||
multiSummary.summaryData.gppercent
|
multiSummary.summaryData.gppercent
|
||||||
@@ -282,7 +282,7 @@ async function JobCostingMulti(req, res) {
|
|||||||
(
|
(
|
||||||
(c.gpdollars_dinero.getAmount() / c.sales_dinero.getAmount()) *
|
(c.gpdollars_dinero.getAmount() / c.sales_dinero.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2)
|
).toFixed(1)
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -730,9 +730,9 @@ function GenerateCostingData(job) {
|
|||||||
.add(sale_sublet);
|
.add(sale_sublet);
|
||||||
const gpdollars = totalSales.subtract(costs);
|
const gpdollars = totalSales.subtract(costs);
|
||||||
const gppercent = (
|
const gppercent = (
|
||||||
(gpdollars.getAmount() / totalSales.getAmount()) *
|
(gpdollars.getAmount() / Math.abs(totalSales.getAmount())) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
|
|
||||||
//Push summary data to avoid extra loop.
|
//Push summary data to avoid extra loop.
|
||||||
summaryData.totalLaborSales = summaryData.totalLaborSales.add(sale_labor);
|
summaryData.totalLaborSales = summaryData.totalLaborSales.add(sale_labor);
|
||||||
@@ -823,7 +823,7 @@ function GenerateCostingData(job) {
|
|||||||
(summaryData.totalLaborGp.getAmount() /
|
(summaryData.totalLaborGp.getAmount() /
|
||||||
summaryData.totalLaborSales.getAmount()) *
|
summaryData.totalLaborSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
summaryData.totalLaborGppercentFormatted = formatGpPercent(
|
summaryData.totalLaborGppercentFormatted = formatGpPercent(
|
||||||
summaryData.totalLaborGppercent
|
summaryData.totalLaborGppercent
|
||||||
);
|
);
|
||||||
@@ -835,7 +835,7 @@ function GenerateCostingData(job) {
|
|||||||
(summaryData.totalPartsGp.getAmount() /
|
(summaryData.totalPartsGp.getAmount() /
|
||||||
summaryData.totalPartsSales.getAmount()) *
|
summaryData.totalPartsSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
summaryData.totalPartsGppercentFormatted = formatGpPercent(
|
summaryData.totalPartsGppercentFormatted = formatGpPercent(
|
||||||
summaryData.totalPartsGppercent
|
summaryData.totalPartsGppercent
|
||||||
);
|
);
|
||||||
@@ -846,7 +846,7 @@ function GenerateCostingData(job) {
|
|||||||
(summaryData.totalAdditionalGp.getAmount() /
|
(summaryData.totalAdditionalGp.getAmount() /
|
||||||
summaryData.totalAdditionalSales.getAmount()) *
|
summaryData.totalAdditionalSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
summaryData.totalAdditionalGppercentFormatted = formatGpPercent(
|
summaryData.totalAdditionalGppercentFormatted = formatGpPercent(
|
||||||
summaryData.totalAdditionalGppercent
|
summaryData.totalAdditionalGppercent
|
||||||
);
|
);
|
||||||
@@ -857,7 +857,7 @@ function GenerateCostingData(job) {
|
|||||||
(summaryData.totalSubletGp.getAmount() /
|
(summaryData.totalSubletGp.getAmount() /
|
||||||
summaryData.totalSubletSales.getAmount()) *
|
summaryData.totalSubletSales.getAmount()) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
summaryData.totalSubletGppercentFormatted = formatGpPercent(
|
summaryData.totalSubletGppercentFormatted = formatGpPercent(
|
||||||
summaryData.totalSubletGppercent
|
summaryData.totalSubletGppercent
|
||||||
);
|
);
|
||||||
@@ -866,9 +866,10 @@ function GenerateCostingData(job) {
|
|||||||
summaryData.totalCost
|
summaryData.totalCost
|
||||||
);
|
);
|
||||||
summaryData.gppercent = (
|
summaryData.gppercent = (
|
||||||
(summaryData.gpdollars.getAmount() / summaryData.totalSales.getAmount()) *
|
(summaryData.gpdollars.getAmount() /
|
||||||
|
Math.abs(summaryData.totalSales.getAmount())) *
|
||||||
100
|
100
|
||||||
).toFixed(2);
|
).toFixed(1);
|
||||||
|
|
||||||
if (isNaN(summaryData.gppercent)) summaryData.gppercentFormatted = 0;
|
if (isNaN(summaryData.gppercent)) summaryData.gppercentFormatted = 0;
|
||||||
else if (!isFinite(summaryData.gppercent))
|
else if (!isFinite(summaryData.gppercent))
|
||||||
|
|||||||
Reference in New Issue
Block a user