Compare commits

..

1 Commits

Author SHA1 Message Date
Allan Carr
98bff6d8f6 IO-2824 Dev Server Instance Switch
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-06-20 10:56:07 -07:00
56 changed files with 13029 additions and 14896 deletions

View File

@@ -1,6 +1,7 @@
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
VITE_APP_GA_CODE=231099835
# VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
VITE_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test

View File

@@ -1,7 +1,8 @@
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
VITE_APP_GA_CODE=231099835
VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
# VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
VITE_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
VITE_APP_CLOUDINARY_API_KEY=957865933348715

View File

@@ -7,7 +7,9 @@ import { connect } from "react-redux";
import { Route, Routes } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
import ErrorBoundary from "../components/error-boundary/error-boundary.component"; // Component Imports
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
//Component Imports
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
import LandingPage from "../pages/landing/landing.page";
@@ -21,21 +23,20 @@ import "./App.styles.scss";
import handleBeta from "../utils/betaHandler";
import Eula from "../components/eula/eula.component";
import InstanceRenderMgr from "../utils/instanceRenderMgr";
import ProductFruitsWrapper from "./ProductFruitsWrapper.jsx";
import { ProductFruits } from "react-product-fruits";
const ResetPassword = lazy(() => import("../pages/reset-password/reset-password.component"));
const ManagePage = lazy(() => import("../pages/manage/manage.page.container"));
const SignInPage = lazy(() => import("../pages/sign-in/sign-in.page"));
const CsiPage = lazy(() => import("../pages/csi/csi.container.page"));
const MobilePaymentContainer = lazy(() => import("../pages/mobile-payment/mobile-payment.container"));
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
online: selectOnline,
bodyshop: selectBodyshop,
currentEula: selectCurrentEula
});
const mapDispatchToProps = (dispatch) => ({
checkUserSession: () => dispatch(checkUserSession()),
setOnline: (isOnline) => dispatch(setOnline(isOnline))
@@ -59,11 +60,11 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
// Associate event listeners, memoize to prevent multiple listeners being added
useEffect(() => {
const offlineListener = () => {
const offlineListener = (e) => {
setOnline(false);
};
const onlineListener = () => {
const onlineListener = (e) => {
setOnline(true);
};
@@ -97,7 +98,7 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
InstanceRenderMgr({
imex: "gvfvfw/bodyshopapp",
rome: "rome-online/rome-online",
promanager: "" // TODO: AIO Add in log rocket for promanager instances.
promanager: "" //TODO:AIO Add in log rocket for promanager instances.
})
);
}
@@ -110,20 +111,24 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
handleBeta();
if (!online) {
if (!online)
return (
<Result
status="warning"
title={t("general.labels.nointernet")}
subTitle={t("general.labels.nointernet_sub")}
extra={
<Button type="primary" onClick={() => window.location.reload()}>
<Button
type="primary"
onClick={() => {
window.location.reload();
}}
>
{t("general.actions.refresh")}
</Button>
}
/>
);
}
if (currentEula && !currentUser.eulaIsAccepted) {
return <Eula />;
@@ -142,13 +147,18 @@ export function App({ bodyshop, checkUserSession, currentUser, online, setOnline
/>
}
>
<ProductFruitsWrapper
currentUser={currentUser}
<ProductFruits
workspaceCode={InstanceRenderMgr({
imex: null,
rome: "9BkbEseqNqxw8jUH",
promanager: "aoJoEifvezYI0Z0P"
})}
debug
language="en"
user={{
email: currentUser.email,
username: currentUser.email
}}
/>
<Routes>

View File

@@ -1,32 +0,0 @@
import React from "react";
import { ProductFruits } from "react-product-fruits";
import PropTypes from "prop-types";
const ProductFruitsWrapper = React.memo(({ currentUser, workspaceCode }) => {
return (
workspaceCode &&
currentUser?.authorized === true &&
currentUser?.email && (
<ProductFruits
lifeCycle="unmount"
workspaceCode={workspaceCode}
debug
language="en"
user={{
email: currentUser.email,
username: currentUser.email
}}
/>
)
);
});
export default ProductFruitsWrapper;
ProductFruitsWrapper.propTypes = {
currentUser: PropTypes.shape({
authorized: PropTypes.bool,
email: PropTypes.string
}).isRequired,
workspaceCode: PropTypes.string.isRequired
};

View File

@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import InstanceRenderMgr from "../../utils/instanceRenderMgr";
//To be used as a form element only.
const { Option } = Select;
const BillLineSearchSelect = ({ options, disabled, allowRemoved, ...restProps }, ref) => {
const { t } = useTranslation();

View File

@@ -14,6 +14,7 @@ import {
Typography
} from "antd";
import Dinero from "dinero.js";
import dayjs from "../../utils/day";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -21,7 +22,6 @@ import { createStructuredSelector } from "reselect";
import { determineDmsType } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
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 FormDatePicker from "../form-date-picker/form-date-picker.component";
@@ -89,7 +89,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
job.area_of_damage && 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"
area_of_damage: (job.area_of_damage && job.area_of_damage.impact1) || "UNKNOWN"
})
: ""
}`.slice(0, 239),

View File

@@ -1,5 +1,6 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Form, Input, notification, Switch } from "antd";
import dayjs from "../../../../utils/day";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -13,7 +14,6 @@ import { UPDATE_OWNER } from "../../../../graphql/owners.queries";
import { insertAuditTrail } from "../../../../redux/application/application.actions";
import { selectBodyshop, selectCurrentUser } from "../../../../redux/user/user.selectors";
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
import dayjs from "../../../../utils/day";
import ConfigFormComponents from "../../../config-form-components/config-form-components.component";
import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component";
@@ -275,19 +275,7 @@ export function JobChecklistForm({ insertAuditTrail, formItems, bodyshop, curren
>
<DateTimePicker disabled={readOnly} />
</Form.Item>
<Form.Item
name="actual_delivery"
label={t("jobs.fields.actual_delivery")}
rules={[
{
required: bodyshop.deliverchecklist.actual_delivery
? bodyshop.deliverchecklist.actual_delivery
: false
//message: t("general.validation.required"),
}
]}
disabled={readOnly}
>
<Form.Item name="actual_delivery" label={t("jobs.fields.actual_delivery")} disabled={readOnly}>
<DateTimePicker disabled={readOnly} />
</Form.Item>
<Form.Item

View File

@@ -3,7 +3,7 @@ import React from "react";
import { useQuery } from "@apollo/client";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_PARTS_BILLS_BY_JOBID } from "../../graphql/bills.queries";
import { QUERY_BILLS_BY_JOBID } from "../../graphql/bills.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
@@ -19,7 +19,7 @@ const mapDispatchToProps = (dispatch) => ({
export default connect(mapStateToProps, mapDispatchToProps)(JobCloseRoGuardBills);
export function JobCloseRoGuardBills({ job, jobRO, bodyshop, form, warningCallback }) {
const { loading, error, data } = useQuery(QUERY_PARTS_BILLS_BY_JOBID, {
const { loading, error, data } = useQuery(QUERY_BILLS_BY_JOBID, {
variables: { jobid: job.id },
fetchPolicy: "network-only",
nextFetchPolicy: "network-only"

View File

@@ -1,6 +1,6 @@
import React, { useCallback, useState } from "react";
import { LockOutlined } from "@ant-design/icons";
import { Badge, Card, Col, Collapse, Form, Input, Row, Space, Tooltip } from "antd";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -12,9 +12,9 @@ import JobCloseRoGuardBills from "./job-close-ro-guard.bills";
import JobCloseRoGuardPpd from "./job-close-ro-guard.ppd";
import JobCloseRoGuardProfit from "./job-close-ro-guard.profit";
import "./job-close-ro-guard.styles.scss";
// import JobCloseRoGuardSublet from "./job-close-ro-guard.sublet";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import JobCloseRoGuardSublet from "./job-close-ro-guard.sublet";
import JobCloseRoGuardTtLifecycle from "./job-close-ro-guard.tt-lifecycle";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser

View File

@@ -1,21 +1,14 @@
import React from "react";
import { useTranslation } from "react-i18next";
import Car from "../job-damage-visual/job-damage-visual.component";
import CardTemplate from "./job-detail-cards.template.component";
import Car from "../job-damage-visual/job-damage-visual.component";
export default function JobDetailCardsDamageComponent({ loading, data }) {
const { t } = useTranslation();
const { area_of_damage } = data;
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.damage")}>
{area_of_damage ? (
<Car
dmg1={area_of_damage.impact1 && area_of_damage.impact1.padStart(2, "0")}
dmg2={area_of_damage.impact2 && area_of_damage.impact2.padStart(2, "0")}
/>
) : (
t("jobs.errors.nodamage")
)}
{area_of_damage ? <Car dmg1={area_of_damage.impact1} dmg2={area_of_damage.impact2} /> : t("jobs.errors.nodamage")}
</CardTemplate>
);
}

View File

@@ -26,16 +26,6 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
const { t } = useTranslation();
const { joblines_status } = data;
const filteredJobLines = data.joblines.filter(
(j) =>
j.part_type !== null &&
j.part_type !== "PAE" &&
j.part_type !== "PAS" &&
j.part_type !== "PASL" &&
j.part_qty !== 0 &&
j.act_price !== 0
);
//TODO: Correct jobline_statuses view by including the part_qty !== 0 and act_price !== 0
const columns = [
{
title: t("joblines.fields.line_desc"),
@@ -105,7 +95,7 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
<div>
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
<PartsStatusPie joblines_status={joblines_status} />
<Table key="id" columns={columns} dataSource={filteredJobLines ? filteredJobLines : []} />
<Table key="id" columns={columns} dataSource={data ? data.joblines : []} />
</CardTemplate>
</div>
);

View File

@@ -2,18 +2,17 @@ import { useQuery } from "@apollo/client";
import { Col, Row, Skeleton, Space, Timeline, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
import { QUERY_JOBLINE_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component";
import BillDetailEditcontainer from "../bill-detail-edit/bill-detail-edit.container.jsx";
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component.jsx";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { QUERY_JOBLINE_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
import TaskListContainer from "../task-list/task-list.container.jsx";
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component.jsx";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -85,17 +84,17 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
key: line.id,
children: (
<Row>
<Col span={8}>{line.parts_dispatch.number}</Col>
<Col span={8}>
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>{line.parts_dispatch.number}</Link>
</Col>
<Col span={8}>
{bodyshop.employees.find((e) => e.id === line.parts_dispatch.employeeid)?.first_name}
</Col>
<Col span={8}>
{line.accepted_at ? (
<Space>
{t("parts_dispatch_lines.fields.accepted_at")}
<DateFormatter>{line.accepted_at}</DateFormatter>
</Space>
) : null}
<Space>
{t("parts_dispatch_lines.fields.accepted_at")}
<DateFormatter>{line.accepted_at}</DateFormatter>
</Space>
</Col>
</Row>
)
@@ -112,7 +111,6 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
<FeatureWrapper featureName="bills" noauth={() => null}>
<Col md={24} lg={8}>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<BillDetailEditcontainer />
<Timeline
items={
data.billlines.length > 0

View File

@@ -31,20 +31,19 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
import { useSplitTreatments } from "@splitsoftware/splitio-react";
import _ from "lodash";
import { FaTasks } from "react-icons/fa";
import { selectBodyshop } from "../../redux/user/user.selectors";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobLineBulkAssignComponent from "../job-line-bulk-assign/job-line-bulk-assign.component";
import JobLineDispatchButton from "../job-line-dispatch-button/job-line-dispatch-button.component";
import JoblineTeamAssignment from "../job-line-team-assignment/job-line-team-assignmnent.component";
import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-send-parts-price-change.component";
import PartsOrderDrawer from "../parts-order-list-table/parts-order-list-table-drawer.component";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import JobLinesExpander from "./job-lines-expander.component";
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
import { FaTasks } from "react-icons/fa";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -55,7 +54,6 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
setJobLineEditContext: (context) => dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
setPartsOrderContext: (context) => dispatch(setModalContext({ context: context, modal: "partsOrder" })),
setPartsReceiveContext: (context) => dispatch(setModalContext({ context: context, modal: "partsReceive" })),
setBillEnterContext: (context) => dispatch(setModalContext({ context: context, modal: "billEnter" })),
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
});
@@ -65,7 +63,6 @@ export function JobLinesComponent({
jobRO,
technician,
setPartsOrderContext,
setPartsReceiveContext,
loading,
refetch,
jobLines,
@@ -74,11 +71,7 @@ export function JobLinesComponent({
setJobLineEditContext,
form,
setBillEnterContext,
setTaskUpsertContext,
billsQuery,
handleBillOnRowClick,
handlePartsOrderOnRowClick,
handlePartsDispatchOnRowClick
setTaskUpsertContext
}) {
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
const {
@@ -444,13 +437,6 @@ export function JobLinesComponent({
return (
<div>
<PartsOrderModalContainer />
<PartsOrderDrawer
job={job}
billsQuery={billsQuery}
handleOnRowClick={handlePartsOrderOnRowClick}
setPartsReceiveContext={setPartsReceiveContext}
setTaskUpsertContext={setTaskUpsertContext}
/>
<PageHeader
title={t("jobs.labels.estimatelines")}
extra={

View File

@@ -1,17 +1,7 @@
import React, { useMemo, useState } from "react";
import JobLinesComponent from "./job-lines.component";
function JobLinesContainer({
job,
joblines,
billsQuery,
handleBillOnRowClick,
handlePartsOrderOnRowClick,
handlePartsDispatchOnRowClick,
refetch,
form,
...rest
}) {
function JobLinesContainer({ job, joblines, refetch, form, ...rest }) {
const [searchText, setSearchText] = useState("");
const jobLines = useMemo(() => {
@@ -32,19 +22,7 @@ function JobLinesContainer({
}, [joblines, searchText]);
return (
<div>
<JobLinesComponent
refetch={refetch}
jobLines={jobLines}
billsQuery={billsQuery}
handleBillOnRowClick={handleBillOnRowClick}
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
setSearchText={setSearchText}
job={job}
form={form}
/>
</div>
<JobLinesComponent refetch={refetch} jobLines={jobLines} setSearchText={setSearchText} job={job} form={form} />
);
}

View File

@@ -3,8 +3,8 @@ import Dinero from "dinero.js";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { alphaSort } from "../../utils/sorters";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
export default function JobTotalsTableLabor({ job }) {
const { t } = useTranslation();
@@ -56,47 +56,16 @@ export default function JobTotalsTableLabor({ job }) {
sortOrder: state.sortedInfo.columnKey === "mod_lb_hrs" && state.sortedInfo.order,
render: (text, record) => record.hours.toFixed(1)
},
...InstanceRenderManager({
imex: {
title: t("joblines.fields.total"),
dataIndex: "total",
key: "total",
align: "right",
sorter: (a, b) => a.total.amount - b.total.amount,
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
render: (text, record) => Dinero(record.total).toFormat()
},
rome: [
{
title: t("joblines.fields.amount"),
dataIndex: "base",
key: "base",
align: "right",
sorter: (a, b) => a.base.amount - b.base.amount,
sortOrder: state.sortedInfo.columnKey === "base" && state.sortedInfo.order,
render: (text, record) => Dinero(record.base).toFormat()
},
{
title: t("joblines.fields.adjustment"),
dataIndex: "adjustment",
key: "adjustment",
align: "right",
sorter: (a, b) => a.adjustment.amount - b.adjustment.amount,
sortOrder: state.sortedInfo.columnKey === "adjustment" && state.sortedInfo.order,
render: (text, record) => Dinero(record.adjustment).toFormat()
},
{
title: t("joblines.fields.total"),
dataIndex: "total",
key: "total",
align: "right",
sorter: (a, b) => a.total.amount - b.total.amount,
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
render: (text, record) => Dinero(record.total).toFormat()
}
],
promanager: "USE_ROME"
})
{
title: t("joblines.fields.total"),
dataIndex: "total",
key: "total",
align: "right",
sorter: (a, b) => a.total.amount - b.total.amount,
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
render: (text, record) => Dinero(record.total).toFormat()
}
];
const handleTableChange = (pagination, filters, sorter) => {
@@ -122,16 +91,6 @@ export default function JobTotalsTableLabor({ job }) {
<Table.Summary.Cell>
{(job.job_totals.rates.mapa.hours + job.job_totals.rates.mash.hours).toFixed(1)}
</Table.Summary.Cell>
{InstanceRenderManager({
imex: null,
rome: (
<>
<Table.Summary.Cell />
<Table.Summary.Cell />
</>
),
promanager: "USE_ROME"
})}
<Table.Summary.Cell align="right">
<strong>{Dinero(job.job_totals.rates.rates_subtotal).toFormat()}</strong>
</Table.Summary.Cell>
@@ -163,29 +122,7 @@ export default function JobTotalsTableLabor({ job }) {
<CurrencyFormatter>{job.job_totals.rates.mapa.rate}</CurrencyFormatter>
</Table.Summary.Cell>
<Table.Summary.Cell>{job.job_totals.rates.mapa.hours.toFixed(1)}</Table.Summary.Cell>
{InstanceRenderManager({
imex: (
<>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mapa.total).toFormat()}
</Table.Summary.Cell>
</>
),
rome: (
<>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mapa.base).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mapa.adjustment).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mapa.total).toFormat()}
</Table.Summary.Cell>
</>
),
promanager: "USE_ROME"
})}
<Table.Summary.Cell align="right">{Dinero(job.job_totals.rates.mapa.total).toFormat()}</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell>
@@ -214,29 +151,7 @@ export default function JobTotalsTableLabor({ job }) {
<CurrencyFormatter>{job.job_totals.rates.mash.rate}</CurrencyFormatter>
</Table.Summary.Cell>
<Table.Summary.Cell>{job.job_totals.rates.mash.hours.toFixed(1)}</Table.Summary.Cell>
{InstanceRenderManager({
imex: (
<>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mash.total).toFormat()}
</Table.Summary.Cell>
</>
),
rome: (
<>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mash.base).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mash.adjustment).toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.rates.mash.total).toFormat()}
</Table.Summary.Cell>
</>
),
promanager: "USE_ROME"
})}
<Table.Summary.Cell align="right">{Dinero(job.job_totals.rates.mash.total).toFormat()}</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell>
@@ -244,16 +159,6 @@ export default function JobTotalsTableLabor({ job }) {
</Table.Summary.Cell>
<Table.Summary.Cell />
<Table.Summary.Cell />
{InstanceRenderManager({
imex: null,
rome: (
<>
<Table.Summary.Cell />
<Table.Summary.Cell />
</>
),
promanager: "USE_ROME"
})}
<Table.Summary.Cell align="right">
<strong>{Dinero(job.job_totals.rates.subtotal).toFormat()}</strong>
</Table.Summary.Cell>

View File

@@ -3,7 +3,6 @@ import { Button, Space, notification } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { DELETE_DELIVERY_CHECKLIST, DELETE_INTAKE_CHECKLIST } from "../../graphql/jobs.queries";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
export default function JobAdminDeleteIntake({ job }) {
const { t } = useTranslation();
@@ -48,22 +47,16 @@ export default function JobAdminDeleteIntake({ job }) {
setLoading(false);
};
const InstanceRender = InstanceRenderManager({
imex: true,
rome: "USE_IMEX",
promanager: false
});
return InstanceRender ? (
return (
<>
<Space wrap>
<Button loading={loading} onClick={handleDelete} disabled={!job.intakechecklist}>
{t("jobs.labels.deleteintake")}
</Button>
<Button loading={loading} onClick={handleDeleteDelivery} disabled={!job.deliverchecklist}>
<Button loading={loading} onClick={handleDeleteDelivery} disabled={!job.deliverychecklist}>
{t("jobs.labels.deletedelivery")}
</Button>
</Space>
</>
) : null;
);
}

View File

@@ -189,10 +189,7 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
</Col>
<Col {...lossColDamage}>
{job.area_of_damage ? (
<Car
dmg1={job.area_of_damage.impact1 && job.area_of_damage.impact1.padStart(2, "0")}
dmg2={job.area_of_damage.impact2 && job.area_of_damage.impact2.padStart(2, "0")}
/>
<Car dmg1={job.area_of_damage.impact1} dmg2={job.area_of_damage.impact2} />
) : (
t("jobs.errors.nodamage")
)}

View File

@@ -1,13 +1,55 @@
import { useQuery } from "@apollo/client";
import queryString from "query-string";
import React from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { QUERY_BILLS_BY_JOBID } from "../../graphql/bills.queries";
import JobsDetailPliComponent from "./jobs-detail-pli.component";
export default function JobsDetailPliContainer({
job,
billsQuery,
handleBillOnRowClick,
handlePartsOrderOnRowClick,
handlePartsDispatchOnRowClick
}) {
export default function JobsDetailPliContainer({ job }) {
const billsQuery = useQuery(QUERY_BILLS_BY_JOBID, {
variables: { jobid: job.id },
fetchPolicy: "network-only",
nextFetchPolicy: "network-only"
});
const search = queryString.parse(useLocation().search);
const history = useNavigate();
const handleBillOnRowClick = (record) => {
if (record) {
if (record.id) {
search.billid = record.id;
history({ search: queryString.stringify(search) });
}
} else {
delete search.billid;
history({ search: queryString.stringify(search) });
}
};
const handlePartsOrderOnRowClick = (record) => {
if (record) {
if (record.id) {
search.partsorderid = record.id;
history({ search: queryString.stringify(search) });
}
} else {
delete search.partsorderid;
history({ search: queryString.stringify(search) });
}
};
const handlePartsDispatchOnRowClick = (record) => {
if (record) {
if (record.id) {
search.partsdispatchid = record.id;
history.push({ search: queryString.stringify(search) });
}
} else {
delete search.partsdispatchid;
history.push({ search: queryString.stringify(search) });
}
};
return (
<JobsDetailPliComponent
job={job}

View File

@@ -1,4 +1,4 @@
import { Collapse, Form, InputNumber, Switch } from "antd";
import { Collapse, Form, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -17,9 +17,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel forceRender header={t("jobs.labels.cieca_pfl")} key="cieca_pfl">
<LayoutFormRow header={t("joblines.fields.lbr_types.LAB")}>
<Form.Item label={t("jobs.fields.cieca_pfl.lbr_adjp")} name={["cieca_pfl", "LAB", "lbr_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAB", "lbr_tax_in"]}
@@ -27,24 +24,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["cieca_pfl", "LAB", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["cieca_pfl", "LAB", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAB", "lbr_tx_in1"]}
@@ -82,9 +61,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAD")}>
<Form.Item label={t("jobs.fields.cieca_pfl.lbr_adjp")} name={["cieca_pfl", "LAD", "lbr_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAD", "lbr_tax_in"]}
@@ -92,24 +68,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["cieca_pfl", "LAD", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["cieca_pfl", "LAD", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAD", "lbr_tx_in1"]}
@@ -147,9 +105,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAE")}>
<Form.Item label={t("jobs.fields.cieca_pfl.lbr_adjp")} name={["cieca_pfl", "LAE", "lbr_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAE", "lbr_tax_in"]}
@@ -157,24 +112,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["cieca_pfl", "LAE", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["cieca_pfl", "LAE", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAE", "lbr_tx_in1"]}
@@ -212,9 +149,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAF")}>
<Form.Item label={t("jobs.fields.cieca_pfl.lbr_adjp")} name={["cieca_pfl", "LAF", "lbr_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAF", "lbr_tax_in"]}
@@ -222,24 +156,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["cieca_pfl", "LAF", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["cieca_pfl", "LAF", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAF", "lbr_tx_in1"]}
@@ -277,9 +193,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAG")}>
<Form.Item label={t("jobs.fields.cieca_pfl.lbr_adjp")} name={["cieca_pfl", "LAG", "lbr_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAG", "lbr_tax_in"]}
@@ -287,24 +200,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["cieca_pfl", "LAG", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["cieca_pfl", "LAG", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAG", "lbr_tx_in1"]}
@@ -342,9 +237,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAM")}>
<Form.Item label={t("jobs.fields.cieca_pfl.lbr_adjp")} name={["cieca_pfl", "LAM", "lbr_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAM", "lbr_tax_in"]}
@@ -352,24 +244,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["cieca_pfl", "LAM", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["cieca_pfl", "LAM", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAM", "lbr_tx_in1"]}
@@ -407,9 +281,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAR")}>
<Form.Item label={t("jobs.fields.cieca_pfl.lbr_adjp")} name={["cieca_pfl", "LAR", "lbr_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAR", "lbr_tax_in"]}
@@ -417,24 +288,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["cieca_pfl", "LAR", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["cieca_pfl", "LAR", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAR", "lbr_tx_in1"]}
@@ -472,9 +325,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAS")}>
<Form.Item label={t("jobs.fields.cieca_pfl.lbr_adjp")} name={["cieca_pfl", "LAS", "lbr_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAS", "lbr_tax_in"]}
@@ -482,24 +332,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["cieca_pfl", "LAS", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["cieca_pfl", "LAS", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAS", "lbr_tx_in1"]}
@@ -537,9 +369,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAU")}>
<Form.Item label={t("jobs.fields.cieca_pfl.lbr_adjp")} name={["cieca_pfl", "LAU", "lbr_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAU", "lbr_tax_in"]}
@@ -547,24 +376,6 @@ export function JobsDetailRatesLabor({ jobRO, expanded, required = true, form })
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["cieca_pfl", "LAU", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["cieca_pfl", "LAU", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAU", "lbr_tx_in1"]}

View File

@@ -23,9 +23,7 @@ export function JobsDetailRatesMaterials({ jobRO, expanded, required = true, for
<Form.Item label={t("jobs.fields.materials.cal_opcode")} name={["materials", "MAPA", "cal_opcode"]}>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.materials.mat_adjp")} name={["materials", "MAPA", "mat_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.tax_ind")}
name={["materials", "MAPA", "tax_ind"]}
@@ -33,24 +31,6 @@ export function JobsDetailRatesMaterials({ jobRO, expanded, required = true, for
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.materials.mat_taxp")}
name={["materials", "MAPA", "mat_taxp"]}
rules={[
{
required: form.getFieldValue(["materials", "MAPA", "tax_ind"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in1")}
name={["materials", "MAPA", "mat_tx_in1"]}
@@ -94,9 +74,7 @@ export function JobsDetailRatesMaterials({ jobRO, expanded, required = true, for
<Form.Item label={t("jobs.fields.materials.cal_opcode")} name={["materials", "MASH", "cal_opcode"]}>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.materials.mat_adjp")} name={["materials", "MAPA", "mat_adjp"]}>
<InputNumber min={-100} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.tax_ind")}
name={["materials", "MASH", "tax_ind"]}
@@ -104,24 +82,6 @@ export function JobsDetailRatesMaterials({ jobRO, expanded, required = true, for
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.materials.mat_taxp")}
name={["materials", "MASH", "mat_taxp"]}
rules={[
{
required: form.getFieldValue(["materials", "MASH", "tax_ind"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in1")}
name={["materials", "MASH", "mat_tx_in1"]}

View File

@@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
@@ -989,24 +988,18 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true, form })
<Form.Item label={t("jobs.fields.tax_str_rt")} name="tax_str_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
{InstanceRenderManager({ imex: true, rome: false, promanager: "USE_ROME" }) ? (
<>
<Form.Item label={t("jobs.fields.tax_paint_mat_rt")} name="tax_paint_mat_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_shop_mat_rt")} name="tax_shop_mat_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>{" "}
</>
) : null}
<Form.Item label={t("jobs.fields.tax_paint_mat_rt")} name="tax_paint_mat_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_shop_mat_rt")} name="tax_shop_mat_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_sub_rt")} name="tax_sub_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
{InstanceRenderManager({ imex: true, rome: false, promanager: "USE_ROME" }) ? (
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
) : null}
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_levies_rt")} name="tax_levies_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>

View File

@@ -1,411 +0,0 @@
import { DeleteFilled, EyeFilled } from "@ant-design/icons";
import { PageHeader } from "@ant-design/pro-layout";
import { useLazyQuery, useMutation } from "@apollo/client";
import { Button, Drawer, Grid, Popconfirm, Space, Table } from "antd";
import queryString from "query-string";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { FaTasks } from "react-icons/fa";
import { connect } from "react-redux";
import { useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { QUERY_BILL_BY_PK } from "../../graphql/bills.queries";
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort } from "../../utils/sorters";
import DataLabel from "../data-label/data-label.component";
import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component";
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
import PartsOrderDeleteLine from "../parts-order-delete-line/parts-order-delete-line.component";
import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
import PrintWrapper from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop
});
const mapDispatchToProps = (dispatch) => ({
setBillEnterContext: (context) =>
dispatch(
setModalContext({
context: context,
modal: "billEnter"
})
),
setPartsReceiveContext: (context) =>
dispatch(
setModalContext({
context: context,
modal: "partsReceive"
})
),
setTaskUpsertContext: (context) => dispatch(setModalContext({ context, modal: "taskUpsert" }))
});
export function PartsOrderListTableDrawerComponent({
setBillEnterContext,
bodyshop,
jobRO,
job,
billsQuery,
handleOnRowClick,
setPartsReceiveContext,
setTaskUpsertContext
}) {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const bpoints = {
xs: "100%",
sm: "100%",
md: "100%",
lg: "75%",
xl: "75%",
xxl: "65%"
};
const drawerPercentage = selectedBreakpoint ? bpoints[selectedBreakpoint[0]] : "100%";
const responsibilityCenters = bodyshop.md_responsibility_centers;
const Templates = TemplateList("partsorder", { job });
const { t } = useTranslation();
const [state, setState] = useState({
sortedInfo: {}
});
const [returnfrombill, setReturnFromBill] = useState();
const [billData, setBillData] = useState();
const search = queryString.parse(useLocation().search);
const selectedpartsorder = search.partsorderid;
const [billQuery] = useLazyQuery(QUERY_BILL_BY_PK);
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
const { refetch } = billsQuery;
useEffect(() => {
if (returnfrombill === null) {
setBillData(null);
} else {
const fetchData = async () => {
const result = await billQuery({
variables: { billid: returnfrombill }
});
setBillData(result.data);
};
fetchData();
}
}, [returnfrombill, billQuery]);
const recordActions = (record, showView = false) => (
<Space direction="horizontal" wrap>
{showView && (
<Button
onClick={() => {
if (record.returnfrombill) {
setReturnFromBill(record.returnfrombill);
} else {
setReturnFromBill(null);
}
handleOnRowClick(record);
}}
>
<EyeFilled />
</Button>
)}
<Button
disabled={jobRO || record.return || record.vendor.id === bodyshop.inhousevendorid}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
setPartsReceiveContext({
actions: { refetch: refetch },
context: {
jobId: job.id,
job: job,
partsorderlines: record.parts_order_lines.map((pol) => {
return {
joblineid: pol.job_line_id,
id: pol.id,
line_desc: pol.line_desc,
quantity: pol.quantity,
act_price: pol.act_price,
oem_partno: pol.oem_partno
};
})
}
});
}}
>
{t("parts_orders.actions.receive")}
</Button>
<Button
title={t("tasks.buttons.create")}
onClick={() => {
setTaskUpsertContext({
context: {
jobid: job.id,
partsorderid: record.id
}
});
}}
>
<FaTasks />
</Button>
<Popconfirm
title={t("parts_orders.labels.confirmdelete")}
disabled={jobRO}
onConfirm={async () => {
//Delete the parts return.!
await deletePartsOrder({
variables: { partsOrderId: record.id },
update(cache) {
cache.modify({
fields: {
parts_orders(existingPartsOrders, { readField }) {
return existingPartsOrders.filter((billref) => record.id !== readField("id", billref));
}
}
});
}
});
}}
>
<Button disabled={jobRO}>
<DeleteFilled />
</Button>
</Popconfirm>
<FeatureWrapperComponent featureName="bills" noauth={() => null}>
<Button
disabled={(jobRO ? !record.return : jobRO) || record.vendor.id === bodyshop.inhousevendorid}
onClick={() => {
logImEXEvent("parts_order_receive_bill");
setBillEnterContext({
actions: { refetch: refetch },
context: {
job: job,
bill: {
vendorid: record.vendor.id,
is_credit_memo: record.return,
billlines: record.parts_order_lines.map((pol) => {
return {
joblineid: pol.job_line_id || "noline",
line_desc: pol.line_desc,
quantity: pol.quantity,
actual_price: pol.act_price,
cost_center: pol.jobline?.part_type
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
? pol.jobline.part_type !== "PAE"
? pol.jobline.part_type
: null
: responsibilityCenters.defaults &&
(responsibilityCenters.defaults.costs[pol.jobline.part_type] || null)
: null
};
})
}
}
});
}}
>
{t("parts_orders.actions.receivebill")}
</Button>
</FeatureWrapperComponent>
<PrintWrapper
templateObject={{
name: record.return ? Templates.parts_return_slip.key : Templates.parts_order.key,
variables: { id: record.id }
}}
messageObject={{
subject: record.return ? Templates.parts_return_slip.subject : Templates.parts_order.subject,
to: record.vendor.email
}}
id={job.id}
/>
</Space>
);
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const selectedPartsOrderRecord = parts_orders.find((r) => r.id === selectedpartsorder);
const rowExpander = (record) => {
const columns = [
{
title: t("parts_orders.fields.line_desc"),
dataIndex: "line_desc",
key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order
},
{
title: t("parts_orders.fields.quantity"),
dataIndex: "quantity",
key: "quantity",
sorter: (a, b) => a.quantity - b.quantity,
sortOrder: state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order
},
{
title: t("parts_orders.fields.act_price"),
dataIndex: "act_price",
key: "act_price",
sorter: (a, b) => a.act_price - b.act_price,
sortOrder: state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
render: (text, record) => <CurrencyFormatter>{record.act_price}</CurrencyFormatter>
},
...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
? [
{
title: t("parts_orders.fields.cost"),
dataIndex: "cost",
key: "cost",
sorter: (a, b) => a.cost - b.cost,
sortOrder: state.sortedInfo.columnKey === "cost" && state.sortedInfo.order,
render: (text, record) => <CurrencyFormatter>{record.cost}</CurrencyFormatter>
}
]
: []),
{
title: t("parts_orders.fields.part_type"),
dataIndex: "part_type",
key: "part_type",
render: (text, record) => (record.part_type ? t(`joblines.fields.part_types.${record.part_type}`) : null)
},
{
title: t("parts_orders.fields.oem_partno"),
dataIndex: "oem_partno",
key: "oem_partno",
sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno),
sortOrder: state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order
},
{
title: t("parts_orders.fields.line_remarks"),
dataIndex: "line_remarks",
key: "line_remarks"
},
{
title: t("parts_orders.fields.status"),
dataIndex: "status",
key: "status"
},
...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
? [
{
title: t("parts_orders.fields.cm_received"),
dataIndex: "cm_received",
key: "cm_received",
render: (text, record) => (
<PartsOrderCmReceived
orderLineId={record.id}
checked={record.cm_received}
partsorderid={selectedPartsOrderRecord.id}
/>
)
}
]
: []),
{
title: t("parts_orders.fields.backordered_on"),
dataIndex: "backordered_on",
key: "backordered_on",
render: (text, record) => <DateFormatter>{text}</DateFormatter>
},
{
title: t("parts_orders.fields.backordered_eta"),
dataIndex: "backordered_eta",
key: "backordered_eta",
render: (text, record) => (
<PartsOrderBackorderEta
backordered_eta={record.backordered_eta}
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
jobLineId={record.job_line_id}
/>
)
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
render: (text, record) => (
<Space wrap>
<PartsOrderDeleteLine
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
partsOrderId={selectedpartsorder}
jobLineId={record.job_line_id}
/>
<PartsOrderLineBackorderButton
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
jobLineId={record.job_line_id}
/>
</Space>
)
}
];
return (
<div>
<PageHeader
title={
billData
? `${record.vendor.name} - ${record.order_number} - ${t("bills.labels.returnfrombill")}: ${billData.bills_by_pk.invoice_number}`
: `${record.vendor.name} - ${record.order_number}`
}
extra={recordActions(record)}
/>
<Table
scroll={{
x: true //y: "50rem"
}}
columns={columns}
rowKey="id"
dataSource={record.parts_order_lines}
onChange={handleTableChange}
/>
<DataLabel label={t("parts_orders.fields.comments")}>
<div style={{ whiteSpace: "pre" }}>{record.comments}</div>
</DataLabel>
</div>
);
};
return (
<div>
<PartsReceiveModalContainer />
<Drawer
placement="right"
onClose={() => handleOnRowClick(null)}
open={selectedpartsorder}
closable
width={drawerPercentage}
>
{selectedPartsOrderRecord && rowExpander(selectedPartsOrderRecord)}
</Drawer>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(PartsOrderListTableDrawerComponent);

View File

@@ -1,23 +1,33 @@
import { DeleteFilled, EyeFilled, SyncOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, Card, Checkbox, Input, Popconfirm, Space, Table } from "antd";
import React, { useState } from "react";
import { useLazyQuery, useMutation } from "@apollo/client";
import { Button, Card, Checkbox, Drawer, Grid, Input, Popconfirm, Space, Table } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
import queryString from "query-string";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { FaTasks } from "react-icons/fa";
import { connect } from "react-redux";
import { useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { QUERY_BILL_BY_PK } from "../../graphql/bills.queries";
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort } from "../../utils/sorters";
import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component";
import DataLabel from "../data-label/data-label.component";
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
import PartsOrderDeleteLine from "../parts-order-delete-line/parts-order-delete-line.component";
import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
import PrintWrapper from "../print-wrapper/print-wrapper.component";
import PartsOrderDrawer from "./parts-order-list-table-drawer.component";
import FeatureWrapperComponent from "../feature-wrapper/feature-wrapper.component";
import { FaTasks } from "react-icons/fa";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -52,6 +62,19 @@ export function PartsOrderListTableComponent({
setPartsReceiveContext,
setTaskUpsertContext
}) {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const bpoints = {
xs: "100%",
sm: "100%",
md: "100%",
lg: "75%",
xl: "75%",
xxl: "65%"
};
const drawerPercentage = selectedBreakpoint ? bpoints[selectedBreakpoint[0]] : "100%";
const responsibilityCenters = bodyshop.md_responsibility_centers;
const Templates = TemplateList("partsorder", { job });
@@ -60,17 +83,42 @@ export function PartsOrderListTableComponent({
sortedInfo: {}
});
const [returnfrombill, setReturnFromBill] = useState();
const [billData, setBillData] = useState();
const search = queryString.parse(useLocation().search);
const selectedpartsorder = search.partsorderid;
const [searchText, setSearchText] = useState("");
const [billQuery] = useLazyQuery(QUERY_BILL_BY_PK);
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
const { refetch } = billsQuery;
useEffect(() => {
if (returnfrombill === null) {
setBillData(null);
} else {
const fetchData = async () => {
const result = await billQuery({
variables: { billid: returnfrombill }
});
setBillData(result.data);
};
fetchData();
}
}, [returnfrombill, billQuery]);
const recordActions = (record, showView = false) => (
<Space direction="horizontal" wrap>
{showView && (
<Button
onClick={() => {
if (record.returnfrombill) {
setReturnFromBill(record.returnfrombill);
} else {
setReturnFromBill(null);
}
handleOnRowClick(record);
}}
>
@@ -250,6 +298,154 @@ export function PartsOrderListTableComponent({
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const selectedPartsOrderRecord = parts_orders.find((r) => r.id === selectedpartsorder);
const rowExpander = (record) => {
const columns = [
{
title: t("parts_orders.fields.line_desc"),
dataIndex: "line_desc",
key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
sortOrder: state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order
},
{
title: t("parts_orders.fields.quantity"),
dataIndex: "quantity",
key: "quantity",
sorter: (a, b) => a.quantity - b.quantity,
sortOrder: state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order
},
{
title: t("parts_orders.fields.act_price"),
dataIndex: "act_price",
key: "act_price",
sorter: (a, b) => a.act_price - b.act_price,
sortOrder: state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
render: (text, record) => <CurrencyFormatter>{record.act_price}</CurrencyFormatter>
},
...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
? [
{
title: t("parts_orders.fields.cost"),
dataIndex: "cost",
key: "cost",
sorter: (a, b) => a.cost - b.cost,
sortOrder: state.sortedInfo.columnKey === "cost" && state.sortedInfo.order,
render: (text, record) => <CurrencyFormatter>{record.cost}</CurrencyFormatter>
}
]
: []),
{
title: t("parts_orders.fields.part_type"),
dataIndex: "part_type",
key: "part_type",
render: (text, record) => (record.part_type ? t(`joblines.fields.part_types.${record.part_type}`) : null)
},
{
title: t("parts_orders.fields.oem_partno"),
dataIndex: "oem_partno",
key: "oem_partno",
sorter: (a, b) => alphaSort(a.oem_partno, b.oem_partno),
sortOrder: state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order
},
{
title: t("parts_orders.fields.line_remarks"),
dataIndex: "line_remarks",
key: "line_remarks"
},
{
title: t("parts_orders.fields.status"),
dataIndex: "status",
key: "status"
},
...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
? [
{
title: t("parts_orders.fields.cm_received"),
dataIndex: "cm_received",
key: "cm_received",
render: (text, record) => (
<PartsOrderCmReceived
orderLineId={record.id}
checked={record.cm_received}
partsorderid={selectedPartsOrderRecord.id}
/>
)
}
]
: []),
{
title: t("parts_orders.fields.backordered_on"),
dataIndex: "backordered_on",
key: "backordered_on",
render: (text, record) => <DateFormatter>{text}</DateFormatter>
},
{
title: t("parts_orders.fields.backordered_eta"),
dataIndex: "backordered_eta",
key: "backordered_eta",
render: (text, record) => (
<PartsOrderBackorderEta
backordered_eta={record.backordered_eta}
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
jobLineId={record.job_line_id}
/>
)
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
render: (text, record) => (
<Space wrap>
<PartsOrderDeleteLine
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
partsOrderId={selectedpartsorder}
jobLineId={record.job_line_id}
/>
<PartsOrderLineBackorderButton
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
jobLineId={record.job_line_id}
/>
</Space>
)
}
];
return (
<div>
<PageHeader
title={
billData
? `${record.vendor.name} - ${record.order_number} - ${t("bills.labels.returnfrombill")}: ${billData.bills_by_pk.invoice_number}`
: `${record.vendor.name} - ${record.order_number}`
}
extra={recordActions(record)}
/>
<Table
scroll={{
x: true //y: "50rem"
}}
columns={columns}
rowKey="id"
dataSource={record.parts_order_lines}
/>
<DataLabel label={t("parts_orders.fields.comments")}>
<div style={{ whiteSpace: "pre" }}>{record.comments}</div>
</DataLabel>
</div>
);
};
const filteredPartsOrders = parts_orders
? searchText === ""
? parts_orders
@@ -280,13 +476,15 @@ export function PartsOrderListTableComponent({
}
>
<PartsReceiveModalContainer />
<PartsOrderDrawer
job={job}
billsQuery={billsQuery}
handleOnRowClick={handleOnRowClick}
setPartsReceiveContext={setPartsReceiveContext}
setTaskUpsertContext={setTaskUpsertContext}
/>
<Drawer
placement="right"
onClose={() => handleOnRowClick(null)}
open={selectedpartsorder}
closable
width={drawerPercentage}
>
{selectedPartsOrderRecord && rowExpander(selectedPartsOrderRecord)}
</Drawer>
<Table
loading={billsQuery.loading}
scroll={{

View File

@@ -1,11 +1,11 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Dropdown, Space, TimePicker } from "antd";
import { Button, Card, Dropdown, TimePicker } from "antd";
import dayjs from "../../utils/day";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { DateFormatter } from "../../utils/DateFormatter";
import dayjs from "../../utils/day";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
export default function ProductionListDate({ record, field, time, pastIndicator }) {
@@ -56,25 +56,23 @@ export default function ProductionListDate({ record, field, time, pastIndicator
key: "overlayItem1",
label: (
<Card style={{ padding: "1rem" }} onClick={(e) => e.stopPropagation()}>
<Space direction={"vertical"}>
<FormDatePicker
<FormDatePicker
onClick={(e) => e.stopPropagation()}
value={(record[field] && dayjs(record[field])) || null}
onChange={handleChange}
format="MM/DD/YYYY"
isDateOnly={!time}
/>
{time && (
<TimePicker
onClick={(e) => e.stopPropagation()}
value={(record[field] && dayjs(record[field])) || null}
onChange={handleChange}
format="MM/DD/YYYY"
isDateOnly={!time}
minuteStep={15}
format="hh:mm a"
/>
{time && (
<TimePicker
onClick={(e) => e.stopPropagation()}
value={(record[field] && dayjs(record[field])) || null}
onChange={handleChange}
minuteStep={15}
format="hh:mm a"
/>
)}
<Button onClick={() => setOpen(false)}>{t("general.actions.close")}</Button>
</Space>
)}
<Button onClick={() => setOpen(false)}>{t("general.actions.close")}</Button>
</Card>
)
}

View File

@@ -63,7 +63,7 @@ export default function ShopEmployeesListComponent({ loading, employees }) {
value: false
}
],
onFilter: (value, record) => value === record.flat_rate,
onFilter: (value, record) => value === record.flate_rate,
render: (text, record) =>
record.flat_rate ? t("employees.labels.flat_rate") : t("employees.labels.straight_time")
},

View File

@@ -319,18 +319,6 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
))}
</Select>
</Form.Item>
<Form.Item
name={["deliverchecklist", "actual_delivery"]}
label={t("bodyshop.fields.deliver.require_actual_delivery_date")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
>
<Switch />
</Form.Item>
</SelectorDiv>
</div>
);

View File

@@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
@@ -52,12 +51,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
<Collapse>
<Collapse.Panel forceRender header={t("jobs.labels.cieca_pfl")} key="cieca_pfl">
<LayoutFormRow header={t("joblines.fields.lbr_types.LAB")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_adjp")}
name={["md_responsibility_centers", "cieca_pfl", "LAB", "lbr_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["md_responsibility_centers", "cieca_pfl", "LAB", "lbr_tax_in"]}
@@ -65,24 +58,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["md_responsibility_centers", "cieca_pfl", "LAB", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfl", "LAB", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["md_responsibility_centers", "cieca_pfl", "LAB", "lbr_tx_in1"]}
@@ -120,12 +95,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAD")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_adjp")}
name={["md_responsibility_centers", "cieca_pfl", "LAD", "lbr_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["md_responsibility_centers", "cieca_pfl", "LAD", "lbr_tax_in"]}
@@ -133,24 +102,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["md_responsibility_centers", "cieca_pfl", "LAD", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfl", "LAD", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["md_responsibility_centers", "cieca_pfl", "LAD", "lbr_tx_in1"]}
@@ -188,12 +139,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAE")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_adjp")}
name={["md_responsibility_centers", "cieca_pfl", "LAE", "lbr_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["md_responsibility_centers", "cieca_pfl", "LAE", "lbr_tax_in"]}
@@ -201,24 +146,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["md_responsibility_centers", "cieca_pfl", "LAE", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfl", "LAE", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["md_responsibility_centers", "cieca_pfl", "LAE", "lbr_tx_in1"]}
@@ -256,12 +183,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAF")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_adjp")}
name={["md_responsibility_centers", "cieca_pfl", "LAF", "lbr_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["md_responsibility_centers", "cieca_pfl", "LAF", "lbr_tax_in"]}
@@ -269,24 +190,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["md_responsibility_centers", "cieca_pfl", "LAF", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfl", "LAF", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["md_responsibility_centers", "cieca_pfl", "LAF", "lbr_tx_in1"]}
@@ -324,12 +227,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAG")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_adjp")}
name={["md_responsibility_centers", "cieca_pfl", "LAG", "lbr_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["md_responsibility_centers", "cieca_pfl", "LAG", "lbr_tax_in"]}
@@ -337,24 +234,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["md_responsibility_centers", "cieca_pfl", "LAG", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfl", "LAG", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["md_responsibility_centers", "cieca_pfl", "LAG", "lbr_tx_in1"]}
@@ -392,12 +271,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAM")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_adjp")}
name={["md_responsibility_centers", "cieca_pfl", "LAM", "lbr_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["md_responsibility_centers", "cieca_pfl", "LAM", "lbr_tax_in"]}
@@ -405,24 +278,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["md_responsibility_centers", "cieca_pfl", "LAM", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfl", "LAM", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["md_responsibility_centers", "cieca_pfl", "LAM", "lbr_tx_in1"]}
@@ -460,12 +315,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAR")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_adjp")}
name={["md_responsibility_centers", "cieca_pfl", "LAR", "lbr_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["md_responsibility_centers", "cieca_pfl", "LAR", "lbr_tax_in"]}
@@ -473,24 +322,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["md_responsibility_centers", "cieca_pfl", "LAR", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfl", "LAR", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["md_responsibility_centers", "cieca_pfl", "LAR", "lbr_tx_in1"]}
@@ -528,12 +359,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAS")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_adjp")}
name={["md_responsibility_centers", "cieca_pfl", "LAS", "lbr_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["md_responsibility_centers", "cieca_pfl", "LAS", "lbr_tax_in"]}
@@ -541,24 +366,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["md_responsibility_centers", "cieca_pfl", "LAS", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfl", "LAS", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["md_responsibility_centers", "cieca_pfl", "LAS", "lbr_tx_in1"]}
@@ -596,12 +403,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAU")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_adjp")}
name={["md_responsibility_centers", "cieca_pfl", "LAU", "lbr_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["md_responsibility_centers", "cieca_pfl", "LAU", "lbr_tax_in"]}
@@ -609,24 +410,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_taxp")}
name={["md_responsibility_centers", "cieca_pfl", "LAU", "lbr_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfl", "LAU", "lbr_tax_in"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={2} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["md_responsibility_centers", "cieca_pfl", "LAU", "lbr_tx_in1"]}
@@ -679,12 +462,7 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_adjp")}
name={["md_responsibility_centers", "cieca_pfm", "MAPA", "mat_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.tax_ind")}
name={["md_responsibility_centers", "cieca_pfm", "MAPA", "tax_ind"]}
@@ -692,24 +470,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.materials.mat_taxp")}
name={["md_responsibility_centers", "cieca_pfm", "MAPA", "mat_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfm", "MAPA", "tax_ind"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in1")}
name={["md_responsibility_centers", "cieca_pfm", "MAPA", "mat_tx_in1"]}
@@ -759,12 +519,7 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_adjp")}
name={["md_responsibility_centers", "cieca_pfm", "MASH", "mat_adjp"]}
>
<InputNumber min={-100} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.tax_ind")}
name={["md_responsibility_centers", "cieca_pfm", "MASH", "tax_ind"]}
@@ -772,24 +527,6 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
return (
<Form.Item
label={t("jobs.fields.materials.mat_taxp")}
name={["md_responsibility_centers", "cieca_pfm", "MASH", "mat_taxp"]}
rules={[
{
required: form.getFieldValue(["md_responsibility_centers", "cieca_pfm", "MASH", "tax_ind"])
//message: t("general.validation.required"),
}
]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in1")}
name={["md_responsibility_centers", "cieca_pfm", "MASH", "mat_tx_in1"]}
@@ -2028,37 +1765,25 @@ export function ShopInfoResponsibilityCenters({ bodyshop, form }) {
</Form.Item>
</LayoutFormRow>
<LayoutFormRow>
<Form.Item label={t("jobs.fields.tax_tow_rt")} name={["md_responsibility_centers", "tax_tow_rt"]}>
<Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt">
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_str_rt")} name={["md_responsibility_centers", "tax_str_rt"]}>
<Form.Item label={t("jobs.fields.tax_str_rt")} name="tax_str_rt">
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
{InstanceRenderManager({ imex: true, rome: false, promanager: "USE_ROME" }) ? (
<>
<Form.Item
label={t("jobs.fields.tax_paint_mat_rt")}
name={["md_responsibility_centers", "tax_paint_mat_rt"]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
<Form.Item
label={t("jobs.fields.tax_shop_mat_rt")}
name={["md_responsibility_centers", "tax_shop_mat_rt"]}
>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
</>
) : null}
<Form.Item label={t("jobs.fields.tax_sub_rt")} name={["md_responsibility_centers", "tax_sub_rt"]}>
<Form.Item label={t("jobs.fields.tax_paint_mat_rt")} name="tax_paint_mat_rt">
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
{InstanceRenderManager({ imex: true, rome: false, promanager: "USE_ROME" }) ? (
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name={["md_responsibility_centers", "tax_lbr_rt"]}>
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
) : null}
<Form.Item label={t("jobs.fields.tax_levies_rt")} name={["md_responsibility_centers", "tax_levies_rt"]}>
<Form.Item label={t("jobs.fields.tax_shop_mat_rt")} name="tax_shop_mat_rt">
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_sub_rt")} name="tax_sub_rt">
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt">
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_levies_rt")} name="tax_levies_rt">
<InputNumber min={0} max={100} precision={4} />
</Form.Item>
</LayoutFormRow>

View File

@@ -50,7 +50,7 @@ export const QUERY_ALL_BILLS_PAGINATED = gql`
}
`;
export const QUERY_PARTS_BILLS_BY_JOBID = gql`
export const QUERY_BILLS_BY_JOBID = gql`
query QUERY_PARTS_BILLS_BY_JOBID($jobid: uuid!) {
parts_orders(where: { jobid: { _eq: $jobid } }, order_by: { order_date: desc }) {
id

View File

@@ -905,7 +905,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
}
joblines(
where: { removed: { _eq: false } }
where: { removed: { _eq: false }, part_type: { _is_null: false, _nin: ["PAE", "PAS", "PASL"] } }
order_by: { line_no: asc }
) {
id
@@ -1149,7 +1149,6 @@ export const UPDATE_JOB = gql`
suspended
queued_for_parts
scheduled_completion
scheduled_delivery
actual_in
date_repairstarted
date_void

View File

@@ -1,5 +1,5 @@
import { PageHeader } from "@ant-design/pro-layout";
import { Button, Result, Space, Steps } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -9,6 +9,7 @@ import JobsCreateJobsInfo from "../../components/jobs-create-jobs-info/jobs-crea
import JobsCreateOwnerInfoContainer from "../../components/jobs-create-owner-info/jobs-create-owner-info.container";
import JobsCreateVehicleInfoContainer from "../../components/jobs-create-vehicle-info/jobs-create-vehicle-info.container";
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
import FormsFieldChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
export default function JobsCreateComponent({ form }) {
const [pageIndex, setPageIndex] = useState(0);
@@ -40,11 +41,10 @@ export default function JobsCreateComponent({ form }) {
const next = () => {
setPageIndex(pageIndex + 1);
console.log("Next");
console.log("NExt");
};
const prev = () => {
setPageIndex(pageIndex - 1);
console.log("Previous");
};
const { Step } = Steps;
@@ -53,26 +53,26 @@ export default function JobsCreateComponent({ form }) {
<PageHeader
extra={
<Space wrap>
{pageIndex > 0 && <Button onClick={() => prev()}>{t("general.actions.previous")}</Button>}
{pageIndex > 0 && <Button onClick={() => prev()}>Previous</Button>}
{pageIndex < steps.length - 1 && (
<Button
type="primary"
onClick={() => {
next();
form
.validateFields()
.then((r) => {
if (steps[pageIndex].validation) {
setErrorMessage(null);
next();
} else {
setErrorMessage(steps[pageIndex].error);
}
})
.catch((error) => console.log("error", error));
// form
// .validateFields()
// .then((r) => {
// if (steps[pageIndex].validation) {
// setErrorMessage(null);
// next();
// } else {
// setErrorMessage(steps[pageIndex].error);
// }
// })
// .catch((error) => console.log("error", error));
}}
>
{t("general.actions.next")}
Next
</Button>
)}
{pageIndex === steps.length - 1 && (
@@ -104,17 +104,17 @@ export default function JobsCreateComponent({ form }) {
}}
onClick={() => {
setPageIndex(idx);
form
.validateFields()
.then((r) => {
if (steps[pageIndex].validation) {
setErrorMessage(null);
setPageIndex(idx);
} else {
setErrorMessage(steps[pageIndex].error);
}
})
.catch((error) => console.log("error", error));
// form
// .validateFields()
// .then((r) => {
// if (steps[pageIndex].validation) {
// setErrorMessage(null);
// setPageIndex(idx);
// } else {
// setErrorMessage(steps[pageIndex].error);
// }
// })
// .catch((error) => console.log("error", error));
}}
/>
))}
@@ -144,7 +144,7 @@ export default function JobsCreateComponent({ form }) {
) : (
<div>
<ProgressButtons top />
<FormsFieldChanged form={form} />
{errorMessage ? (
<div>
<AlertComponent message={errorMessage} type="error" />

View File

@@ -10,9 +10,9 @@ import { INSERT_NEW_JOB } from "../../graphql/jobs.queries";
import { QUERY_OWNER_FOR_JOB_CREATION } from "../../graphql/owners.queries";
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import JobsCreateComponent from "./jobs-create.component";
import JobCreateContext from "./jobs-create.context";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
@@ -159,6 +159,13 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
layout="vertical"
autoComplete={"off"}
initialValues={{
tax_tow_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_str_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_paint_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_shop_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_sub_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_lbr_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_levies_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate / 100,
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate / 100,
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate / 100,
@@ -254,34 +261,19 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
prt_tax_rt: bodyshop.bill_tax_rates.state_tax_rate / 100
}
},
tax_tow_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_str_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_paint_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_shop_mat_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_sub_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_lbr_rt: bodyshop.bill_tax_rates.state_tax_rate / 100,
tax_levies_rt: bodyshop.bill_tax_rates.state_tax_rate / 100
},
rome: {
cieca_pft: {
...bodyshop.md_responsibility_centers.taxes.tax_ty1,
...bodyshop.md_responsibility_centers.taxes.tax_ty2,
...bodyshop.md_responsibility_centers.taxes.tax_ty3,
...bodyshop.md_responsibility_centers.taxes.tax_ty4,
...bodyshop.md_responsibility_centers.taxes.tax_ty5
},
materials: bodyshop.md_responsibility_centers.cieca_pfm,
cieca_pfl: bodyshop.md_responsibility_centers.cieca_pfl,
parts_tax_rates: bodyshop.md_responsibility_centers.parts_tax_rates,
tax_tow_rt: bodyshop.md_responsibility_centers.tax_tow_rt,
tax_str_rt: bodyshop.md_responsibility_centers.tax_str_rt,
tax_paint_mat_rt: bodyshop.md_responsibility_centers.tax_paint_mat_rt,
tax_shop_mat_rt: bodyshop.md_responsibility_centers.tax_shop_mat_rt,
tax_sub_rt: bodyshop.md_responsibility_centers.tax_sub_rt,
tax_lbr_rt: bodyshop.md_responsibility_centers.tax_lbr_rt,
tax_levies_rt: bodyshop.md_responsibility_centers.tax_levies_rt
},
promanager: "USE_ROME"
rome: {
cieca_pft: {
...bodyshop.md_responsibility_centers.taxes.tax_ty1,
...bodyshop.md_responsibility_centers.taxes.tax_ty2,
...bodyshop.md_responsibility_centers.taxes.tax_ty3,
...bodyshop.md_responsibility_centers.taxes.tax_ty4,
...bodyshop.md_responsibility_centers.taxes.tax_ty5
},
materials: bodyshop.md_responsibility_centers.cieca_pfm,
cieca_pfl: bodyshop.md_responsibility_centers.cieca_pfl,
parts_tax_rates: bodyshop.md_responsibility_centers.parts_tax_rates
}
}
})
}}
>

View File

@@ -8,11 +8,11 @@ import Icon, {
SyncOutlined,
ToolFilled
} from "@ant-design/icons";
import { PageHeader } from "@ant-design/pro-layout";
import { useQuery } from "@apollo/client";
import { Badge, Button, Divider, Form, notification, Space, Tabs } from "antd";
import { PageHeader } from "@ant-design/pro-layout";
import Axios from "axios";
import _ from "lodash";
import dayjs from "../../utils/day";
import queryString from "query-string";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -20,13 +20,11 @@ import { FaHardHat, FaRegStickyNote, FaShieldAlt, FaTasks } from "react-icons/fa
import { connect } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
import FormFieldsChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
import JobLifecycleComponent from "../../components/job-lifecycle/job-lifecycle.component";
import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component";
import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
import JobSyncButton from "../../components/job-sync-button/job-sync-button.component";
import JobsChangeStatus from "../../components/jobs-change-status/jobs-change-status.component";
@@ -44,18 +42,19 @@ import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gal
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container";
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
import TaskListContainer from "../../components/task-list/task-list.container.jsx";
import { QUERY_PARTS_BILLS_BY_JOBID } from "../../graphql/bills.queries.js";
import { QUERY_JOB_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { DateTimeFormat } from "../../utils/DateFormatter";
import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import UndefinedToNull from "../../utils/undefinedtonull";
import _ from "lodash";
import JobProfileDataWarning from "../../components/job-profile-data-warning/job-profile-data-warning.component";
import { DateTimeFormat } from "../../utils/DateFormatter";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
import TaskListContainer from "../../components/task-list/task-list.container.jsx";
import { QUERY_JOB_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -96,11 +95,6 @@ export function JobsDetailPage({
const formItemLayout = {
layout: "vertical"
};
const billsQuery = useQuery(QUERY_PARTS_BILLS_BY_JOBID, {
variables: { jobid: job.id },
fetchPolicy: "network-only",
nextFetchPolicy: "network-only"
});
useEffect(() => {
//form.setFieldsValue(transormJobToForm(job));
@@ -109,42 +103,6 @@ export function JobsDetailPage({
//useKeyboardSaveShortcut(form.submit);
const handleBillOnRowClick = (record) => {
if (record) {
if (record.id) {
search.billid = record.id;
history({ search: queryString.stringify(search) });
}
} else {
delete search.billid;
history({ search: queryString.stringify(search) });
}
};
const handlePartsOrderOnRowClick = (record) => {
if (record) {
if (record.id) {
search.partsorderid = record.id;
history({ search: queryString.stringify(search) });
}
} else {
delete search.partsorderid;
history({ search: queryString.stringify(search) });
}
};
const handlePartsDispatchOnRowClick = (record) => {
if (record) {
if (record.id) {
search.partsdispatchid = record.id;
history.push({ search: queryString.stringify(search) });
}
} else {
delete search.partsdispatchid;
history.push({ search: queryString.stringify(search) });
}
};
const handleFinish = async (values) => {
setLoading(true);
@@ -344,18 +302,7 @@ export function JobsDetailPage({
id: "job-details-repairdata",
label: t("menus.jobsdetail.repairdata"),
forceRender: true,
children: (
<JobsLinesContainer
job={job}
joblines={job.joblines}
billsQuery={billsQuery}
handleBillOnRowClick={handleBillOnRowClick}
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
refetch={refetch}
form={form}
/>
)
children: <JobsLinesContainer job={job} joblines={job.joblines} refetch={refetch} form={form} />
},
{
key: "rates",
@@ -379,15 +326,7 @@ export function JobsDetailPage({
label: HasFeatureAccess({ featureName: "bills", bodyshop })
? t("menus.jobsdetail.partssublet")
: t("menus.jobsdetail.parts"),
children: (
<JobsDetailPliContainer
job={job}
billsQuery={billsQuery}
handleBillOnRowClick={handleBillOnRowClick}
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
/>
)
children: <JobsDetailPliContainer job={job} />
},
...(InstanceRenderManager({
imex: true,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1081,32 +1081,6 @@ export const TemplateList = (type, context) => {
},
group: "purchases"
},
purchases_by_ro_detail_date: {
title: i18n.t("reportcenter.templates.purchases_by_ro_detail_date"),
description: "",
subject: i18n.t("reportcenter.templates.purchases_by_ro_detail_date"),
key: "purchases_by_ro_detail_date",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced")
},
group: "purchases"
},
purchases_by_ro_summary_date: {
title: i18n.t("reportcenter.templates.purchases_by_ro_summary_date"),
description: "",
subject: i18n.t("reportcenter.templates.purchases_by_ro_summary_date"),
key: "purchases_by_ro_summary_date",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced")
},
group: "purchases"
},
job_costing_ro_date_summary: {
title: i18n.t("reportcenter.templates.job_costing_ro_date_summary"),
description: "",
@@ -1194,17 +1168,6 @@ export const TemplateList = (type, context) => {
},
group: "customers"
},
payments_by_date_payment: {
title: i18n.t("reportcenter.templates.payments_by_date_payment"),
subject: i18n.t("reportcenter.templates.payments_by_date_payment"),
key: "payments_by_date_payment",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.payments"),
field: i18n.t("payments.fields.date")
},
group: "customers"
},
schedule: {
title: i18n.t("reportcenter.templates.schedule"),
subject: i18n.t("reportcenter.templates.schedule"),

View File

@@ -1,10 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE VIEW "public"."joblines_status" AS
-- SELECT j.jobid,
-- j.status,
-- count(1) AS count,
-- j.part_type
-- FROM joblines j
-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> 0) AND (j.act_price <> 0) AND (j.removed IS FALSE))
-- GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -1,8 +0,0 @@
CREATE OR REPLACE VIEW "public"."joblines_status" AS
SELECT j.jobid,
j.status,
count(1) AS count,
j.part_type
FROM joblines j
WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> 0) AND (j.act_price <> 0) AND (j.removed IS FALSE))
GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -1,10 +0,0 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE VIEW "public"."joblines_status" AS
-- SELECT j.jobid,
-- j.status,
-- count(1) AS count,
-- j.part_type
-- FROM joblines j
-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> (0)::numeric) AND (j.act_price <> (0)::numeric) AND (j.removed IS FALSE))
-- GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -1,8 +0,0 @@
CREATE OR REPLACE VIEW "public"."joblines_status" AS
SELECT j.jobid,
j.status,
count(1) AS count,
j.part_type
FROM joblines j
WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> (0)::numeric) AND (j.act_price <> (0)::numeric) AND (j.removed IS FALSE))
GROUP BY j.jobid, j.status, j.part_type;

480
package-lock.json generated
View File

@@ -19,8 +19,6 @@
"better-queue": "^3.8.12",
"bluebird": "^3.7.2",
"body-parser": "^1.20.2",
"canvas": "^2.11.2",
"chart.js": "^4.4.3",
"cloudinary": "^2.0.2",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
@@ -1689,45 +1687,6 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@kurkle/color": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
"integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
"dependencies": {
"detect-libc": "^2.0.0",
"https-proxy-agent": "^5.0.0",
"make-dir": "^3.1.0",
"node-fetch": "^2.6.7",
"nopt": "^5.0.0",
"npmlog": "^5.0.1",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.11"
},
"bin": {
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/@oozcitak/dom": {
"version": "1.15.10",
"resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-1.15.10.tgz",
@@ -2650,11 +2609,6 @@
"node": ">=10.0.0"
}
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@@ -2751,24 +2705,6 @@
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
},
"node_modules/aproba": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
},
"node_modules/are-we-there-yet": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
"deprecated": "This package is no longer supported.",
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -3070,20 +3006,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/canvas": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
"integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
"hasInstallScript": true,
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.0",
"nan": "^2.17.0",
"simple-get": "^3.0.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@@ -3117,17 +3039,6 @@
"node": ">=8"
}
},
"node_modules/chart.js": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz",
"integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==",
"dependencies": {
"@kurkle/color": "^0.3.0"
},
"engines": {
"pnpm": ">=8"
}
},
"node_modules/cheerio": {
"version": "1.0.0-rc.12",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
@@ -3164,14 +3075,6 @@
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"engines": {
"node": ">=10"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -3232,14 +3135,6 @@
"simple-swizzle": "^0.2.2"
}
},
"node_modules/color-support": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"bin": {
"color-support": "bin.js"
}
},
"node_modules/color/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -3333,7 +3228,8 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
"node_modules/concat-stream": {
"version": "1.6.2",
@@ -3398,11 +3294,6 @@
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -3649,17 +3540,6 @@
"node": ">=0.10"
}
},
"node_modules/decompress-response": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"dependencies": {
"mimic-response": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/deeks": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/deeks/-/deeks-3.1.0.tgz",
@@ -3708,11 +3588,6 @@
"node": ">=0.4.0"
}
},
"node_modules/delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -3739,14 +3614,6 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/detect-libc": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
"engines": {
"node": ">=8"
}
},
"node_modules/dev-null": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz",
@@ -4419,32 +4286,11 @@
"node": ">=6 <7 || >=8"
}
},
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/fs-minipass/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"node_modules/ftp": {
"version": "0.3.10",
@@ -4493,31 +4339,6 @@
"integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
"optional": true
},
"node_modules/gauge": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
"deprecated": "This package is no longer supported.",
"dependencies": {
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.2",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.1",
"object-assign": "^4.1.1",
"signal-exit": "^3.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/gauge/node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
},
"node_modules/gaxios": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz",
@@ -4629,46 +4450,6 @@
"assert-plus": "^1.0.0"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/glob/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -4853,11 +4634,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
},
"node_modules/hasown": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
@@ -4985,6 +4761,7 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -5552,28 +5329,6 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
},
"node_modules/make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-dir/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
@@ -5647,17 +5402,6 @@
"node": ">= 0.6"
}
},
"node_modules/mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -5691,29 +5435,6 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/minizlib/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
@@ -5769,7 +5490,8 @@
"node_modules/nan": {
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w=="
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
"optional": true
},
"node_modules/negotiator": {
"version": "0.6.3",
@@ -5909,32 +5631,6 @@
"node": ">=6.0.0"
}
},
"node_modules/nopt": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
"dependencies": {
"abbrev": "1"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/npmlog": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
"deprecated": "This package is no longer supported.",
"dependencies": {
"are-we-there-yet": "^2.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^3.0.0",
"set-blocking": "^2.0.0"
}
},
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -6128,6 +5824,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6794,11 +6491,6 @@
"node": ">= 0.8.0"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
},
"node_modules/set-function-length": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
@@ -6870,35 +6562,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/simple-get": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"dependencies": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
@@ -7059,6 +6722,16 @@
"node": ">=12"
}
},
"node_modules/source-map-explorer/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/source-map-explorer/node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
@@ -7070,6 +6743,38 @@
"wrap-ansi": "^7.0.0"
}
},
"node_modules/source-map-explorer/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/source-map-explorer/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/source-map-explorer/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
@@ -7453,41 +7158,6 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/tar": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^5.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/tar/node_modules/minipass": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/tar/node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/teeny-request": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
@@ -7540,6 +7210,48 @@
"node": ">=6.0.0"
}
},
"node_modules/temp/node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/temp/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/temp/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/temp/node_modules/rimraf": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
@@ -7887,14 +7599,6 @@
"node": ">= 8"
}
},
"node_modules/wide-align": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
"dependencies": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"node_modules/winston": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz",

View File

@@ -12,6 +12,9 @@
"admin": "cd admin && npm start",
"client": "cd client && npm start",
"server": "nodemon server.js",
"server:imex": "nodemon server.js imex",
"server:rome": "nodemon server.js rome",
"server:promanager": "nodemon server.js promanager",
"build": "cd client && npm run build",
"dev": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\"",
"deva": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\" \"npm run admin\"",
@@ -29,8 +32,6 @@
"better-queue": "^3.8.12",
"bluebird": "^3.7.2",
"body-parser": "^1.20.2",
"canvas": "^2.11.2",
"chart.js": "^4.4.3",
"cloudinary": "^2.0.2",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",

View File

@@ -8,9 +8,11 @@ const cookieParser = require("cookie-parser");
const http = require("http");
const { Server } = require("socket.io");
const instanceName = process.argv[2];
// Load environment variables
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}${instanceName ? `.${instanceName}` : ''}`)
});
// Import custom utilities and handlers

View File

@@ -632,7 +632,7 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
}
}
//QB USA with GST and PST
//QB USA with GST
//This was required for the No. 1 Collision Group.
if (
bodyshop.accountingconfig &&
@@ -651,20 +651,9 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
Qty: 1
}
});
InvoiceLineAdd.push({
DetailType: "SalesItemLineDetail",
Amount: Dinero(jobs_by_pk.job_totals.totals.state_tax).toFormat(DineroQbFormat),
SalesItemLineDetail: {
...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}),
ItemRef: {
value: items[bodyshop.md_responsibility_centers.taxes.state.accountitem]
},
Qty: 1
}
});
}
} else {
//Handle insurance profile adjustments for Parts
//Handle insurance profile adjustments
Object.keys(job_totals.parts.adjustments).forEach((key) => {
if (qbo) {
//Going to always assume that we need to apply GST and PST for labor.
@@ -718,67 +707,6 @@ exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes
}
});
//Handle insurance profile adjustments for Labor and Materials
Object.keys(job_totals.rates).forEach((key) => {
if (
job_totals.rates[key] &&
job_totals.rates[key].adjustment &&
Dinero(job_totals.rates[key].adjustment).isZero() === false
) {
if (qbo) {
//Going to always assume that we need to apply GST and PST for labor.
const taxAccountCode = findTaxCode(
{
local: false,
federal: process.env.COUNTRY === "USA" ? false : true,
state: jobs_by_pk.state_tax_rate === 0 ? false : true
},
bodyshop.md_responsibility_centers.sales_tax_codes
);
const account = responsibilityCenters.profits.find(
(c) => c.name === responsibilityCenters.defaults.profits[key.toUpperCase()]
);
const QboTaxId =
process.env.COUNTRY === "USA"
? CheckQBOUSATaxID({
// jobline: jobline,
job: jobs_by_pk,
type: "storage"
})
: taxCodes[taxAccountCode];
InvoiceLineAdd.push({
DetailType: "SalesItemLineDetail",
Amount: Dinero(job_totals.rates[key].adjustment).toFormat(DineroQbFormat),
Description: `${account.accountdesc} - Adjustment`,
SalesItemLineDetail: {
...(jobs_by_pk.class ? { ClassRef: { value: classes[jobs_by_pk.class] } } : {}),
ItemRef: {
value: items[account.accountitem]
},
TaxCodeRef: {
value: QboTaxId
},
Qty: 1
}
});
} else {
InvoiceLineAdd.push({
ItemRef: {
FullName: responsibilityCenters.profits.find(
(c) => c.name === responsibilityCenters.defaults.profits[key.toUpperCase()]
).accountitem
},
Desc: "Storage",
Quantity: 1,
Amount: Dinero(job_totals.rates[key].adjustment).toFormat(DineroQbFormat),
SalesTaxCodeRef: {
FullName: bodyshop.md_responsibility_centers.taxes.itemexemptcode || "NON"
}
});
}
}
});
const QboTaxId =
process.env.COUNTRY === "USA"
? CheckQBOUSATaxID({
@@ -963,11 +891,7 @@ function checkStateTax(jobline, jobs_by_pk) {
}
const isAdditionalCost =
(jobline.lbr_op === "OP13" ||
(jobline.lbr_op === "OP14" && jobline.act_price > 0 && jobline.mod_lb_hrs === 0) ||
(jobline.db_ref && jobline.db_ref.startsWith("9360")) ||
(jobline.db_ref && jobline.db_ref.startsWith("90051"))) &&
!isPaintOrShopMat;
(jobline.lbr_op === "OP13" || (jobline.db_ref && jobline.db_ref.startsWith("9360"))) && !isPaintOrShopMat;
if (!jobline.part_type && isAdditionalCost) {
if (jobs_by_pk.tax_lbr_rt === 0) {
@@ -977,12 +901,18 @@ function checkStateTax(jobline, jobs_by_pk) {
}
}
if (
jobline.db_ref === "900511" ||
jobline.db_ref === "900510" ||
(jobline.mod_lb_hrs === 0 && jobline.act_price > 0 && jobline.lbr_op === "OP14")
)
return true; //Extending IO-1375 as a part of IO-2023
if (jobline.tax_part === false) {
return false;
} else {
if (jobline.part_type) {
if (
!jobs_by_pk.parts_tax_rates[`${jobline.part_type.toUpperCase()}`] ||
jobs_by_pk.parts_tax_rates[`${jobline.part_type.toUpperCase()}`].prt_tax_in === false ||
jobs_by_pk.parts_tax_rates[`${jobline.part_type.toUpperCase()}`].prt_tax_rt === 0
) {

View File

@@ -16,418 +16,403 @@ const { DiscountNotAlreadyCounted } = InstanceManager({
promanager: "USE_ROME"
});
exports.defaultRoute = async function (req, res) {
try {
CdkBase.createLogEvent(req, "DEBUG", `Received request to calculate allocations for ${req.body.jobid}`);
const jobData = await QueryJobData(req, req.BearerToken, req.body.jobid);
return res.status(200).json({ data: calculateAllocations(req, jobData) });
} catch (error) {
console.log(error);
CdkBase.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
res.status(500).json({ error: `Error encountered in CdkCalculateAllocations. ${error}` });
}
};
exports.default = async function (socket, jobid) {
try {
const jobData = await QueryJobData(socket, "Bearer " + socket.handshake.auth.token, jobid);
return calculateAllocations(socket, jobData);
CdkBase.createLogEvent(socket, "DEBUG", `Received request to calculate allocations for ${jobid}`);
const job = await QueryJobData(socket, jobid);
const { bodyshop } = job;
const taxAllocations = InstanceManager({
executeFunction: true,
deubg: true,
args: [],
imex: () => ({
local: {
center: bodyshop.md_responsibility_centers.taxes.local.name,
sale: Dinero(job.job_totals.totals.local_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.local,
costCenter: bodyshop.md_responsibility_centers.taxes.local
},
state: {
center: bodyshop.md_responsibility_centers.taxes.state.name,
sale: Dinero(job.job_totals.totals.state_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.state,
costCenter: bodyshop.md_responsibility_centers.taxes.state
},
federal: {
center: bodyshop.md_responsibility_centers.taxes.federal.name,
sale: Dinero(job.job_totals.totals.federal_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.federal,
costCenter: bodyshop.md_responsibility_centers.taxes.federal
}
}),
rome: () => ({
tax_ty1: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`]
},
tax_ty2: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`]
},
tax_ty3: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`]
},
tax_ty4: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`]
},
tax_ty5: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`]
}
})
});
//Determine if there are MAPA and MASH lines already on the estimate.
//If there are, don't do anything extra (mitchell estimate)
//Otherwise, calculate them and add them to the default MAPA and MASH centers.
let hasMapaLine = false;
let hasMashLine = false;
const profitCenterHash = job.joblines.reduce((acc, val) => {
//Check the Parts Assignment
if (val.db_ref === "936008") {
//If either of these DB REFs change, they also need to change in job-totals/job-costing calculations.
hasMapaLine = true;
}
if (val.db_ref === "936007") {
hasMashLine = true;
}
if (val.profitcenter_part) {
if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero();
let DineroAmount = Dinero({
amount: Math.round(val.act_price * 100)
}).multiply(val.part_qty || 1);
DineroAmount = DineroAmount.add(
((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) &&
DiscountNotAlreadyCounted(val, job.joblines)
? val.prt_dsmk_m
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
: Dinero({
amount: Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0))
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
: Dinero()
);
acc[val.profitcenter_part] = acc[val.profitcenter_part].add(DineroAmount);
}
if (val.profitcenter_labor && val.mod_lbr_ty) {
//Check the Labor Assignment.
if (!acc[val.profitcenter_labor]) acc[val.profitcenter_labor] = Dinero();
acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add(
Dinero({
amount: Math.round(job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100)
}).multiply(val.mod_lb_hrs)
);
}
return acc;
}, {});
const selectedDmsAllocationConfig = bodyshop.md_responsibility_centers.dms_defaults.find(
(d) => d.name === job.dms_allocation
);
CdkBase.createLogEvent(
socket,
"DEBUG",
`Using DMS Allocation ${selectedDmsAllocationConfig && selectedDmsAllocationConfig.name} for cost export.`
);
let costCenterHash = {};
//Check whether to skip this if PBS and using AP module.
const disablebillwip = !!bodyshop?.pbs_configuration?.disablebillwip;
if (!disablebillwip) {
costCenterHash = job.bills.reduce((bill_acc, bill_val) => {
bill_val.billlines.map((line_val) => {
if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]])
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = Dinero();
let lineDinero = Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100)
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1);
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(lineDinero);
return null;
});
return bill_acc;
}, {});
}
job.timetickets.forEach((ticket) => {
//Get the total amount of the ticket.
let TicketTotal = Dinero({
amount: Math.round(
ticket.rate *
(ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) *
100
)
});
//Add it to the right cost center.
if (!costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]])
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = Dinero();
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] =
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]].add(TicketTotal);
});
if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
// console.log("Adding MAPA Line Manually.");
const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!profitCenterHash[mapaAccountName]) profitCenterHash[mapaAccountName] = Dinero();
profitCenterHash[mapaAccountName] = profitCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total)
);
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
}
if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) {
// console.log("Adding MASH Line Manually.");
const mashAccountName = selectedDmsAllocationConfig.profits.MASH;
const mashAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!profitCenterHash[mashAccountName]) profitCenterHash[mashAccountName] = Dinero();
profitCenterHash[mashAccountName] = profitCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
console.log(
Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting),
typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) {
//Manually send the percentage of the costing.
//Paint Mat
const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!costCenterHash[mapaAccountName])
costCenterHash[mapaAccountName] = Dinero();
if (job.bodyshop.use_paint_scale_data === true) {
if (job.mixdata.length > 0) {
costCenterHash[mapaAccountName] = costCenterHash[
mapaAccountName
].add(
Dinero({
amount: Math.round(
((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) *
100
),
})
);
} else {
costCenterHash[mapaAccountName] = costCenterHash[
mapaAccountName
].add(
Dinero(job.job_totals.rates.mapa.total).percentage(
bodyshop?.cdk_configuration?.sendmaterialscosting
)
);
}
} else {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(
bodyshop?.cdk_configuration?.sendmaterialscosting
)
);
}
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
//Shop Mat
const mashAccountName = selectedDmsAllocationConfig.costs.MASH;
const mashAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!costCenterHash[mashAccountName]) costCenterHash[mashAccountName] = Dinero();
costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
const { ca_bc_pvrt } = job;
if (ca_bc_pvrt) {
// const pvrtAccount = bodyshop.md_responsibility_centers.profits.find(
// (c) => c.name === mashAccountName
// );
taxAllocations.state.sale = taxAllocations.state.sale.add(
Dinero({ amount: Math.round((ca_bc_pvrt || 0) * 100) })
);
}
if (job.towing_payable && job.towing_payable !== 0) {
const towAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === towAccountName);
if (towAccount) {
if (!profitCenterHash[towAccountName]) profitCenterHash[towAccountName] = Dinero();
profitCenterHash[towAccountName] = profitCenterHash[towAccountName].add(
Dinero({
amount: Math.round((job.towing_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.storage_payable && job.storage_payable !== 0) {
const storageAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === storageAccountName);
if (towAccount) {
if (!profitCenterHash[storageAccountName]) profitCenterHash[storageAccountName] = Dinero();
profitCenterHash[storageAccountName] = profitCenterHash[storageAccountName].add(
Dinero({
amount: Math.round((job.storage_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) {
const otherAccountName = selectedDmsAllocationConfig.profits.PAO;
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === otherAccountName);
if (otherAccount) {
if (!profitCenterHash[otherAccountName]) profitCenterHash[otherAccountName] = Dinero();
profitCenterHash[otherAccountName] = profitCenterHash[otherAccountName].add(
Dinero({
amount: Math.round((job.adjustment_bottom_line || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (InstanceManager({ rome: true })) {
//profile level adjustments
Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
const accountName = selectedDmsAllocationConfig.profits[key];
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName);
if (otherAccount) {
if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero();
profitCenterHash[accountName] = profitCenterHash[accountName].add(
Dinero(job.job_totals.parts.adjustments[key])
);
} else {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
);
}
});
}
const jobAllocations = _.union(Object.keys(profitCenterHash), Object.keys(costCenterHash)).map((key) => {
const profitCenter = bodyshop.md_responsibility_centers.profits.find((c) => c.name === key);
const costCenter = bodyshop.md_responsibility_centers.costs.find((c) => c.name === key);
return {
center: key,
sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(),
cost: costCenterHash[key] ? costCenterHash[key] : Dinero(),
profitCenter,
costCenter
};
});
return [
...jobAllocations,
...Object.keys(taxAllocations)
.filter((key) => taxAllocations[key].sale.getAmount() > 0 || taxAllocations[key].cost.getAmount() > 0)
.map((key) => {
if (
key === "federal" &&
selectedDmsAllocationConfig.gst_override &&
selectedDmsAllocationConfig.gst_override !== ""
) {
const ret = { ...taxAllocations[key], tax: key };
ret.costCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
ret.profitCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
return ret;
} else {
return { ...taxAllocations[key], tax: key };
}
})
];
} catch (error) {
console.log(error);
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
}
};
async function QueryJobData(connectionData, token, jobid) {
CdkBase.createLogEvent(connectionData, "DEBUG", `Querying job data for id ${jobid}`);
async function QueryJobData(socket, jobid) {
CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client.setHeaders({ Authorization: token }).request(queries.GET_CDK_ALLOCATIONS, { id: jobid });
CdkBase.createLogEvent(connectionData, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`);
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.GET_CDK_ALLOCATIONS, { id: jobid });
CdkBase.createLogEvent(socket, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
function calculateAllocations(connectionData, job) {
const { bodyshop } = job;
const taxAllocations = InstanceManager({
executeFunction: true,
deubg: true,
args: [],
imex: () => ({
local: {
center: bodyshop.md_responsibility_centers.taxes.local.name,
sale: Dinero(job.job_totals.totals.local_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.local,
costCenter: bodyshop.md_responsibility_centers.taxes.local
},
state: {
center: bodyshop.md_responsibility_centers.taxes.state.name,
sale: Dinero(job.job_totals.totals.state_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.state,
costCenter: bodyshop.md_responsibility_centers.taxes.state
},
federal: {
center: bodyshop.md_responsibility_centers.taxes.federal.name,
sale: Dinero(job.job_totals.totals.federal_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.federal,
costCenter: bodyshop.md_responsibility_centers.taxes.federal
}
}),
rome: () => ({
tax_ty1: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`]
},
tax_ty2: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`]
},
tax_ty3: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`]
},
tax_ty4: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`]
},
tax_ty5: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`]
}
})
});
//Determine if there are MAPA and MASH lines already on the estimate.
//If there are, don't do anything extra (mitchell estimate)
//Otherwise, calculate them and add them to the default MAPA and MASH centers.
let hasMapaLine = false;
let hasMashLine = false;
const profitCenterHash = job.joblines.reduce((acc, val) => {
//Check the Parts Assignment
if (val.db_ref === "936008") {
//If either of these DB REFs change, they also need to change in job-totals/job-costing calculations.
hasMapaLine = true;
}
if (val.db_ref === "936007") {
hasMashLine = true;
}
if (val.profitcenter_part) {
if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero();
let DineroAmount = Dinero({
amount: Math.round(val.act_price * 100)
}).multiply(val.part_qty || 1);
DineroAmount = DineroAmount.add(
((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) &&
DiscountNotAlreadyCounted(val, job.joblines)
? val.prt_dsmk_m
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
: Dinero({
amount: Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0))
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
: Dinero()
);
acc[val.profitcenter_part] = acc[val.profitcenter_part].add(DineroAmount);
}
if (val.profitcenter_labor && val.mod_lbr_ty) {
//Check the Labor Assignment.
if (!acc[val.profitcenter_labor]) acc[val.profitcenter_labor] = Dinero();
acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add(
Dinero({
amount: Math.round(job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100)
}).multiply(val.mod_lb_hrs)
);
}
return acc;
}, {});
const selectedDmsAllocationConfig = bodyshop.md_responsibility_centers.dms_defaults.find(
(d) => d.name === job.dms_allocation
);
CdkBase.createLogEvent(
connectionData,
"DEBUG",
`Using DMS Allocation ${selectedDmsAllocationConfig && selectedDmsAllocationConfig.name} for cost export.`
);
let costCenterHash = {};
//Check whether to skip this if PBS and using AP module.
const disablebillwip = !!bodyshop?.pbs_configuration?.disablebillwip;
if (!disablebillwip) {
costCenterHash = job.bills.reduce((bill_acc, bill_val) => {
bill_val.billlines.map((line_val) => {
if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]])
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = Dinero();
let lineDinero = Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100)
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1);
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(lineDinero);
return null;
});
return bill_acc;
}, {});
}
job.timetickets.forEach((ticket) => {
//Get the total amount of the ticket.
let TicketTotal = Dinero({
amount: Math.round(
ticket.rate *
(ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) *
100
)
});
//Add it to the right cost center.
if (!costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]])
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = Dinero();
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] =
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]].add(TicketTotal);
});
if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
// console.log("Adding MAPA Line Manually.");
const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!profitCenterHash[mapaAccountName]) profitCenterHash[mapaAccountName] = Dinero();
profitCenterHash[mapaAccountName] = profitCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total)
);
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
}
if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) {
// console.log("Adding MASH Line Manually.");
const mashAccountName = selectedDmsAllocationConfig.profits.MASH;
const mashAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!profitCenterHash[mashAccountName]) profitCenterHash[mashAccountName] = Dinero();
profitCenterHash[mashAccountName] = profitCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
// console.log(
// Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting),
// typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting)
// );
if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) {
//Manually send the percentage of the costing.
//Paint Mat
const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!costCenterHash[mapaAccountName]) costCenterHash[mapaAccountName] = Dinero();
if (job.bodyshop.use_paint_scale_data === true) {
if (job.mixdata.length > 0) {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero({
amount: Math.round(((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) * 100)
})
);
} else {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
}
} else {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
}
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
//Shop Mat
const mashAccountName = selectedDmsAllocationConfig.costs.MASH;
const mashAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!costCenterHash[mashAccountName]) costCenterHash[mashAccountName] = Dinero();
costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
const { ca_bc_pvrt } = job;
if (ca_bc_pvrt) {
// const pvrtAccount = bodyshop.md_responsibility_centers.profits.find(
// (c) => c.name === mashAccountName
// );
taxAllocations.state.sale = taxAllocations.state.sale.add(Dinero({ amount: Math.round((ca_bc_pvrt || 0) * 100) }));
}
if (job.towing_payable && job.towing_payable !== 0) {
const towAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === towAccountName);
if (towAccount) {
if (!profitCenterHash[towAccountName]) profitCenterHash[towAccountName] = Dinero();
profitCenterHash[towAccountName] = profitCenterHash[towAccountName].add(
Dinero({
amount: Math.round((job.towing_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.storage_payable && job.storage_payable !== 0) {
const storageAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === storageAccountName);
if (towAccount) {
if (!profitCenterHash[storageAccountName]) profitCenterHash[storageAccountName] = Dinero();
profitCenterHash[storageAccountName] = profitCenterHash[storageAccountName].add(
Dinero({
amount: Math.round((job.storage_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) {
const otherAccountName = selectedDmsAllocationConfig.profits.PAO;
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === otherAccountName);
if (otherAccount) {
if (!profitCenterHash[otherAccountName]) profitCenterHash[otherAccountName] = Dinero();
profitCenterHash[otherAccountName] = profitCenterHash[otherAccountName].add(
Dinero({
amount: Math.round((job.adjustment_bottom_line || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (InstanceManager({ rome: true })) {
//profile level adjustments for parts
Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
const accountName = selectedDmsAllocationConfig.profits[key];
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName);
if (otherAccount) {
if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero();
profitCenterHash[accountName] = profitCenterHash[accountName].add(
Dinero(job.job_totals.parts.adjustments[key])
);
} else {
CdkBase.createLogEvent(
connectionData,
"ERROR",
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
);
}
});
//profile level adjustments for labor and materials
Object.keys(job.job_totals.rates).forEach((key) => {
if (job.job_totals.rates[key] && job.job_totals.rates[key].adjustment && Dinero(job.job_totals.rates[key].adjustment).isZero() === false) {
const accountName = selectedDmsAllocationConfig.profits[key.toUpperCase()];
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName);
if (otherAccount) {
if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero();
profitCenterHash[accountName] = profitCenterHash[accountName].add(Dinero(job.job_totals.rates[key].adjustments));
} else {
CdkBase.createLogEvent(
connectionData,
"ERROR",
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
);
}
}
});
}
const jobAllocations = _.union(Object.keys(profitCenterHash), Object.keys(costCenterHash)).map((key) => {
const profitCenter = bodyshop.md_responsibility_centers.profits.find((c) => c.name === key);
const costCenter = bodyshop.md_responsibility_centers.costs.find((c) => c.name === key);
return {
center: key,
sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(),
cost: costCenterHash[key] ? costCenterHash[key] : Dinero(),
profitCenter,
costCenter
};
});
return [
...jobAllocations,
...Object.keys(taxAllocations)
.filter((key) => taxAllocations[key].sale.getAmount() > 0 || taxAllocations[key].cost.getAmount() > 0)
.map((key) => {
if (
key === "federal" &&
selectedDmsAllocationConfig.gst_override &&
selectedDmsAllocationConfig.gst_override !== ""
) {
const ret = { ...taxAllocations[key], tax: key };
ret.costCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
ret.profitCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
return ret;
} else {
return { ...taxAllocations[key], tax: key };
}
})
];
}

View File

@@ -211,8 +211,6 @@ const CreateRepairOrderTag = (job, errorCallback) => {
}
const repairCosts = CreateCosts(job);
const jobline = CreateJobLines(job.joblines);
const timeticket = CreateTimeTickets(job.timetickets);
try {
const ret = {
@@ -244,7 +242,6 @@ const CreateRepairOrderTag = (job, errorCallback) => {
},
InsuranceCompany: job.ins_co_nm || "",
Claim: job.clm_no || "",
DMSAllocation: job.dms_allocation || "",
Contacts: {
CSR: job.employee_csr_rel
? `${
@@ -279,11 +276,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
DateInvoiced:
(job.date_invoiced && moment(job.date_invoiced).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateExported:
(job.date_exported && moment(job.date_exported).tz(job.bodyshop.timezone).format(DateFormat)) || "",
DateVoid: (job.date_void && moment(job.date_void).tz(job.bodyshop.timezone).format(DateFormat)) || ""
(job.date_exported && moment(job.date_exported).tz(job.bodyshop.timezone).format(DateFormat)) || ""
},
JobLineDetails: { jobline },
TimeTicketDetails: { timeticket },
Sales: {
Labour: {
Aluminum: Dinero(job.job_totals.rates.laa.total).toFormat(DineroFormat),
@@ -631,42 +625,3 @@ const CreateCosts = (job) => {
}, 0)
};
};
const CreateJobLines = (joblines) => {
const repairLines = [];
joblines.forEach((jobline) => {
repairLines.push({
line_description: jobline.line_desc,
oem_part_no: jobline.oem_partno,
alt_part_no: jobline.alt_partno,
op_code_desc: jobline.op_code_desc,
part_type: jobline.part_type,
part_qty: jobline.part_qty,
part_price: jobline.act_price,
labor_type: jobline.mod_lbr_ty,
labor_hours: jobline.mod_lb_hrs,
labor_sale: jobline.lbr_amt
});
});
return repairLines;
};
const CreateTimeTickets = (timetickets) => {
const timeTickets = [];
timetickets.forEach((ticket) => {
timeTickets.push({
date: ticket.date,
employee: ticket.employee.employee_number
.trim()
.concat(" - ", ticket.employee.first_name.trim(), " ", ticket.employee.last_name.trim())
.trim(),
productive_hrs: ticket.productivehrs,
actual_hrs: ticket.actualhrs,
cost_center: ticket.cost_center,
flat_rate: ticket.flat_rate,
rate: ticket.rate,
ticket_cost: ticket.flat_rate ? ticket.rate * ticket.productive_hrs : ticket.rate * ticket.actual_hrs
});
});
return timeTickets;
};

View File

@@ -1113,7 +1113,7 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
use_paint_scale_data
timezone
}
jobs(where: {_and: [{updated_at: {_gt: $start}}, {updated_at: {_lte: $end}}, {converted: {_eq: true}}, {shopid: {_eq: $bodyshopid}}]}) {
jobs(where: {_and: [{updated_at: {_gt: $start}}, {updated_at: {_lte: $end}}, {shopid: {_eq: $bodyshopid}}]}) {
actual_completion
actual_delivery
actual_in
@@ -1138,8 +1138,6 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
date_invoiced
date_open
date_repairstarted
date_void
dms_allocation
employee_body_rel {
first_name
last_name
@@ -1170,7 +1168,6 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
ins_co_nm
joblines(where: {removed: {_eq: false}}) {
act_price
alt_partno
billlines(order_by: {bill: {date: desc_nulls_last}} limit: 1) {
actual_cost
actual_price
@@ -1185,15 +1182,12 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
}
db_price
id
lbr_amt
lbr_op
line_desc
line_ind
line_no
mod_lb_hrs
mod_lbr_ty
oem_partno
op_code_desc
parts_order_lines(order_by: {parts_order: {order_date: desc_nulls_last}} limit: 1){
parts_order{
id
@@ -1206,6 +1200,7 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
profitcenter_labor
prt_dsmk_m
prt_dsmk_p
oem_partno
status
}
job_totals
@@ -1256,18 +1251,12 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
scheduled_in
status
timetickets {
actualhrs
cost_center
date
employee {
employee_number
first_name
last_name
}
flat_rate
id
productivehrs
rate
cost_center
actualhrs
productivehrs
flat_rate
}
tlos_ind
v_color
@@ -1527,8 +1516,6 @@ exports.QUERY_JOB_COSTING_DETAILS = ` query QUERY_JOB_COSTING_DETAILS($id: uuid!
ca_bc_pvrt
ca_customer_gst
dms_allocation
cieca_pfl
materials
joblines(where: { removed: { _eq: false } }) {
id
db_ref
@@ -1643,8 +1630,6 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT
ca_bc_pvrt
ca_customer_gst
dms_allocation
cieca_pfl
materials
joblines(where: {removed: {_eq: false}}) {
id
db_ref
@@ -1870,8 +1855,6 @@ exports.GET_CDK_ALLOCATIONS = `query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
scheduled_in
actual_in
ca_bc_pvrt
cieca_pfl
materials
timetickets {
id
actualhrs
@@ -2498,6 +2481,7 @@ query QUERY_TASK_BY_ID($id: uuid!) {
`;
exports.GET_JOBS_BY_PKS = `query GET_JOBS_BY_PKS($ids: [uuid!]!) {
jobs(where: {id: {_in: $ids}}) {
id

View File

@@ -286,45 +286,9 @@ function GenerateCostingData(job) {
const rateName = `rate_${(val.mod_lbr_ty || "").toLowerCase()}`;
let laborAmount = Dinero();
laborAmount = Dinero({
const laborAmount = Dinero({
amount: Math.round((job[rateName] || 0) * 100)
}).multiply(val.mod_lb_hrs || 0);
if (
job.cieca_pfl &&
job.cieca_pfl[val.mod_lbr_ty.toUpperCase()] &&
job.cieca_pfl[val.mod_lbr_ty.toUpperCase()].lbr_adjp !== 0
) {
let adjp = 0;
if (
val.mod_lbr_ty === "la1" ||
val.mod_lbr_ty === "la2" ||
val.mod_lbr_ty === "la3" ||
val.mod_lbr_ty === "la4"
) {
adjp =
Math.abs(job.cieca_pfl["LAU"].lbr_adjp) > 1
? job.cieca_pfl["LAU"].lbr_adjp
: job.cieca_pfl["LAU"].lbr_adjp * 100; //Adjust lbr_adjp to whole number
} else {
if (job.cieca_pfl[val.mod_lbr_ty.toUpperCase()]) {
adjp =
Math.abs(job.cieca_pfl[val.mod_lbr_ty.toUpperCase()].lbr_adjp) > 1
? job.cieca_pfl[val.mod_lbr_ty.toUpperCase()].lbr_adjp
: job.cieca_pfl[val.mod_lbr_ty.toUpperCase()].lbr_adjp * 100; //Adjust lbr_adjp to whole number
} else {
adjp =
Math.abs(job.cieca_pfl["LAB"].lbr_adjp) > 1
? job.cieca_pfl["LAB"].lbr_adjp
: job.cieca_pfl["LAB"].lbr_adjp * 100; //Adjust lbr_adjp to whole number
}
}
laborAmount = laborAmount.add(
laborAmount.percentage(adjp < 0 ? adjp * -1 : adjp).multiply(adjp < 0 ? -1 : 1)
);
}
if (!acc.labor[laborProfitCenter]) acc.labor[laborProfitCenter] = Dinero();
acc.labor[laborProfitCenter] = acc.labor[laborProfitCenter].add(laborAmount);
@@ -353,10 +317,8 @@ function GenerateCostingData(job) {
if (!partsProfitCenter)
console.log("Unknown cost/profit center mapping for parts.", val.line_desc, val.part_type);
let partsAmount = Dinero({
amount: val.act_price_before_ppc
? Math.round(val.act_price_before_ppc * 100)
: Math.round(val.act_price * 100)
const partsAmount = Dinero({
amount: val.act_price_before_ppc ? Math.round(val.act_price_before_ppc * 100) : Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 1)
.add(
@@ -365,42 +327,13 @@ function GenerateCostingData(job) {
? val.prt_dsmk_m
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
: Dinero({
amount: val.act_price_before_ppc
? Math.round(val.act_price_before_ppc * 100)
: Math.round(val.act_price * 100)
amount: val.act_price_before_ppc ? Math.round(val.act_price_before_ppc * 100) : Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0))
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
: Dinero()
);
// Profile Discount for Parts
if (job.parts_tax_rates && job.parts_tax_rates[val.part_type.toUpperCase()]) {
if (
job.parts_tax_rates[val.part_type.toUpperCase()].prt_discp !== undefined &&
job.parts_tax_rates[val.part_type.toUpperCase()].prt_discp >= 0
) {
const discountRate =
Math.abs(job.parts_tax_rates[val.part_type.toUpperCase()].prt_discp) > 1
? parts_tajob.parts_tax_rates_rates[val.part_type.toUpperCase()].prt_discp
: job.parts_tax_rates[val.part_type.toUpperCase()].prt_discp * 100;
const disc = partsAmount.percentage(discountRate).multiply(-1);
partsAmount = partsAmount.add(disc);
}
if (
job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp !== undefined &&
job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp >= 0
) {
const markupRate =
Math.abs(job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp) > 1
? job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp
: job.parts_tax_rates[val.part_type.toUpperCase()].prt_mkupp * 100;
const markup = partsAmount.percentage(markupRate);
partsAmount = partsAmount.add(markup);
}
}
if (!acc.parts[partsProfitCenter]) acc.parts[partsProfitCenter] = Dinero();
acc.parts[partsProfitCenter] = acc.parts[partsProfitCenter].add(partsAmount);
}
@@ -435,10 +368,7 @@ function GenerateCostingData(job) {
}
//Additional Profit Center
if (
(!val.part_type && !val.mod_lbr_ty) ||
(!val.part_type && val.mod_lbr_ty && val.act_price > 0 && val.lbr_op !== "OP14")
) {
if ((!val.part_type && !val.mod_lbr_ty) || (!val.part_type && val.mod_lbr_ty)) {
//Does it already have a defined profit center?
//If so, use it, otherwise try to use the same from the auto-allocate logic in IO app jobs-close-auto-allocate.
const partsProfitCenter = val.profitcenter_part || getAdditionalCostCenter(val, defaultProfits) || "Unknown";
@@ -476,7 +406,6 @@ function GenerateCostingData(job) {
if (!hasMapaLine) {
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]])
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = Dinero();
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = jobLineTotalsByProfitCenter.additional[
defaultProfits["MAPA"]
].add(
@@ -484,26 +413,10 @@ function GenerateCostingData(job) {
amount: Math.round((job.rate_mapa || 0) * 100)
}).multiply(materialsHours.mapaHrs || 0)
);
let adjp = 0;
if (job.materials["MAPA"] && job.materials["MAPA"].mat_adjp) {
adjp =
Math.abs(job.materials["MAPA"].mat_adjp) > 1
? job.materials["MAPA"].mat_adjp
: job.materials["MAPA"].mat_adjp * 100; //Adjust mat_adjp to whole number
}
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]] = jobLineTotalsByProfitCenter.additional[
defaultProfits["MAPA"]
].add(
jobLineTotalsByProfitCenter.additional[defaultProfits["MAPA"]]
.percentage(adjp < 0 ? adjp * -1 : adjp)
.multiply(adjp < 0 ? -1 : 1)
);
}
if (!hasMashLine) {
if (!jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]])
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] = Dinero();
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] = jobLineTotalsByProfitCenter.additional[
defaultProfits["MASH"]
].add(
@@ -511,21 +424,6 @@ function GenerateCostingData(job) {
amount: Math.round((job.rate_mash || 0) * 100)
}).multiply(materialsHours.mashHrs || 0)
);
let adjp = 0;
if (job.materials["MASH"] && job.materials["MASH"].mat_adjp) {
adjp =
Math.abs(job.materials["MASH"].mat_adjp) > 1
? job.materials["MASH"].mat_adjp
: job.materials["MASH"].mat_adjp * 100; //Adjust mat_adjp to whole number
}
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]] = jobLineTotalsByProfitCenter.additional[
defaultProfits["MASH"]
].add(
jobLineTotalsByProfitCenter.additional[defaultProfits["MASH"]]
.percentage(adjp < 0 ? adjp * -1 : adjp)
.multiply(adjp < 0 ? -1 : 1)
);
}
//Is it a DMS Setup?

View File

@@ -331,8 +331,6 @@ async function CalculateRatesTotals({ job, client }) {
//Skip calculating mapa and mash if we got the amounts.
if (!((property === "mapa" && hasMapaLine) || (property === "mash" && hasMashLine))) {
if (!ret[property].total) {
ret[property].base = Dinero();
ret[property].adjustment = Dinero();
ret[property].total = Dinero();
}
let threshold;
@@ -351,50 +349,13 @@ async function CalculateRatesTotals({ job, client }) {
}
}
const base = Dinero({
const total = Dinero({
amount: Math.round((ret[property].rate || 0) * 100)
}).multiply(ret[property].hours);
let adjp = 0;
if (property === "mapa" || property === "mash") {
if (job.materials[property.toUpperCase()] && job.materials[property.toUpperCase()].mat_adjp) {
adjp =
Math.abs(job.materials[property.toUpperCase()].mat_adjp) > 1
? job.materials[property.toUpperCase()].mat_adjp
: job.materials[property.toUpperCase()].mat_adjp * 100; //Adjust mat_adjp to whole number
}
} else {
if (property === "la1" || property === "la2" || property === "la3" || property === "la4") {
if (job.cieca_pfl["LAU"] && job.cieca_pfl["LAU"].lbr_adjp) {
adjp =
Math.abs(job.cieca_pfl["LAU"].lbr_adjp) > 1
? job.cieca_pfl["LAU"].lbr_adjp
: job.cieca_pfl["LAU"].lbr_adjp * 100; //Adjust lbr_adjp to whole number
}
} else {
if (job.cieca_pfl[property.toUpperCase()] && job.cieca_pfl[property.toUpperCase()].lbr_adjp) {
adjp =
Math.abs(job.cieca_pfl[property.toUpperCase()].lbr_adjp) > 1
? job.cieca_pfl[property.toUpperCase()].lbr_adjp
: job.cieca_pfl[property.toUpperCase()].lbr_adjp * 100; //Adjust lbr_adjp to whole number
} else {
if (job.cieca_pfl["LAB"].lbr_adjp) {
adjp =
Math.abs(job.cieca_pfl["LAB"].lbr_adjp) > 1
? job.cieca_pfl["LAB"].lbr_adjp
: job.cieca_pfl["LAB"].lbr_adjp * 100; //Adjust lbr_adjp to whole number
}
}
}
}
const adjustment = base.percentage(adjp < 0 ? adjp * -1 : adjp).multiply(adjp < 0 ? -1 : 1);
const total = base.add(adjustment);
if (threshold && total.greaterThanOrEqual(threshold)) {
ret[property].total = ret[property].total.add(threshold);
} else {
ret[property].base = ret[property].base.add(base);
ret[property].adjustment = ret[property].adjustment.add(adjustment);
ret[property].total = ret[property].total.add(total);
}
}
@@ -682,7 +643,7 @@ function CalculateAdditional(job) {
additionalCosts: null,
additionalCostItems: [],
adjustments: null,
towing: Dinero(),
towing: null,
shipping: Dinero(),
storage: null,
pvrt: null,
@@ -707,7 +668,7 @@ function CalculateAdditional(job) {
}
if (val.line_desc.toLowerCase().includes("towing")) {
ret.towing = ret.towing.add(lineValue);
ret.towing = lineValue;
return acc;
} else {
ret.additionalCostItems.push({ key: val.line_desc, total: lineValue });
@@ -742,19 +703,18 @@ function CalculateTaxesTotals(job, otherTotals) {
//Potential issue here with Sublet Calculation. Sublets are calculated under labor in Mitchell, but it's done in IO
//Under the parts rates.
let stateTax = Dinero();
// let additionalItemsTax = Dinero(); //This is not used.
let statePartsTax = Dinero();
let additionalItemsTax = Dinero();
let us_sales_tax_breakdown;
// This is not referenced in the code base.
//Audatex sends additional glass part types. IO-774
// const BackupGlassTax =
// job.parts_tax_rates &&
// (job.parts_tax_rates.PAGD ||
// job.parts_tax_rates.PAGF ||
// job.parts_tax_rates.PAGP ||
// job.parts_tax_rates.PAGQ ||
// job.parts_tax_rates.PAGR);
const BackupGlassTax =
job.parts_tax_rates &&
(job.parts_tax_rates.PAGD ||
job.parts_tax_rates.PAGF ||
job.parts_tax_rates.PAGP ||
job.parts_tax_rates.PAGQ ||
job.parts_tax_rates.PAGR);
const taxableAmounts = {
PAA: Dinero(),
@@ -918,27 +878,11 @@ function CalculateTaxesTotals(job, otherTotals) {
} else if (key.startsWith("LA")) {
//Labor.
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
if (key === "LA1" || key === "LA2" || key === "LA3" || key === "LA4") {
if (IsTrueOrYes(pfl["LAU"][`lbr_tx_in${tyCounter}`])) {
//This amount is taxable for this type.
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[`ty${tyCounter}Tax`].add(
taxableAmounts[key]
);
}
} else if (key === "LAA" && !pfl[key]) {
if (IsTrueOrYes(pfl["LAB"][`lbr_tx_in${tyCounter}`])) {
//This amount is taxable for this type.
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[`ty${tyCounter}Tax`].add(
taxableAmounts[key]
);
}
} else {
if (IsTrueOrYes(pfl[key][`lbr_tx_in${tyCounter}`])) {
//This amount is taxable for this type.
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[`ty${tyCounter}Tax`].add(
taxableAmounts[key]
);
}
if (IsTrueOrYes(pfl[key][`lbr_tx_in${tyCounter}`])) {
//This amount is taxable for this type.
taxableAmountsByTier[`ty${tyCounter}Tax`] = taxableAmountsByTier[`ty${tyCounter}Tax`].add(
taxableAmounts[key]
);
}
}
} else if (key === "TOW") {
@@ -965,24 +909,6 @@ function CalculateTaxesTotals(job, otherTotals) {
}
});
if (job.adjustment_bottom_line) {
const subtotal_before_adjustment = subtotal.add(Dinero({ amount: Math.round(job.adjustment_bottom_line * -100) }));
const percent_of_adjustment =
Math.round(
subtotal_before_adjustment.toUnit() /
(job.adjustment_bottom_line > 0 ? job.adjustment_bottom_line : job.adjustment_bottom_line * -1)
) / 100;
Object.keys(taxableAmountsByTier).forEach((taxTierKey) => {
taxable_adjustment = taxableAmountsByTier[taxTierKey].multiply(percent_of_adjustment);
if (job.adjustment_bottom_line > 0) {
taxableAmountsByTier[taxTierKey] = taxableAmountsByTier[taxTierKey].add(taxable_adjustment);
} else {
taxableAmountsByTier[taxTierKey] = taxableAmountsByTier[taxTierKey].subtract(taxable_adjustment);
}
});
}
const remainingTaxableAmounts = taxableAmountsByTier;
console.log("*** Taxable Amounts by Tier***");
console.table(JSON.parse(JSON.stringify(taxableAmountsByTier)));
@@ -992,8 +918,8 @@ function CalculateTaxesTotals(job, otherTotals) {
let tyCounter = taxTierKey[2]; //Get the number from the key.
//i represents the tax number. If we got here, this type of tax is applicable. Now we need to add based on the thresholds.
for (let threshCounter = 1; threshCounter <= 5; threshCounter++) {
const thresholdAmount = parseFloat(job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`]) || 0;
const thresholdTaxRate = parseFloat(job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`]) || 0;
const thresholdAmount = parseFloat(job.cieca_pft[`ty${tyCounter}_thres${threshCounter}`]);
const thresholdTaxRate = parseFloat(job.cieca_pft[`ty${tyCounter}_rate${threshCounter}`]);
let taxableAmountInThisThreshold;
if (
@@ -1001,7 +927,7 @@ function CalculateTaxesTotals(job, otherTotals) {
InstanceMgr({
imex: false,
rome: thresholdAmount === 0 && parseInt(tyCounter) === 1,
promanager: "USE_ROME"
promanager: thresholdAmount === 0 && parseInt(tyCounter) === 1
})
) {
//
@@ -1035,10 +961,10 @@ function CalculateTaxesTotals(job, otherTotals) {
}
});
console.log("*** Total Tax by Tier Amounts***");
console.table(JSON.parse(JSON.stringify(totalTaxByTier)));
// console.log("*** Total Tax by Tier Amounts***");
// console.table(JSON.parse(JSON.stringify(totalTaxByTier)));
stateTax = stateTax
statePartsTax = statePartsTax
.add(totalTaxByTier.ty1Tax)
.add(totalTaxByTier.ty2Tax)
.add(totalTaxByTier.ty3Tax)
@@ -1046,18 +972,17 @@ function CalculateTaxesTotals(job, otherTotals) {
.add(totalTaxByTier.ty5Tax)
.add(totalTaxByTier.ty6Tax);
us_sales_tax_breakdown = totalTaxByTier;
//console.log("Tiered Taxes Total for Parts/Labor", stateTax.toFormat());
//console.log("Tiered Taxes Total for Parts/Labor", statePartsTax.toFormat());
// This is not in use as such commented out.
// let laborTaxTotal = Dinero();
let laborTaxTotal = Dinero();
// if (Object.keys(job.cieca_pfl).length > 0) {
// //Ignore it now, we have calculated it above.
// //This was previously used for JCS before parts were also calculated at a different rate.
// } else {
// //We don't have it, just add in how it was before.
// laborTaxTotal = otherTotals.rates.subtotal.percentage((job.tax_lbr_rt || 0) * 100); // THis is currently using the lbr tax rate from PFH not PFL.
// }
if (Object.keys(job.cieca_pfl).length > 0) {
//Ignore it now, we have calculated it above.
//This was previously used for JCS before parts were also calculated at a different rate.
} else {
//We don't have it, just add in how it was before.
laborTaxTotal = otherTotals.rates.subtotal.percentage((job.tax_lbr_rt || 0) * 100); // THis is currently using the lbr tax rate from PFH not PFL.
}
//console.log("Labor Tax Total", laborTaxTotal.toFormat());
@@ -1066,9 +991,9 @@ function CalculateTaxesTotals(job, otherTotals) {
federal_tax: subtotal
.percentage((job.federal_tax_rate || 0) * 100)
.add(otherTotals.additional.pvrt.percentage((job.federal_tax_rate || 0) * 100)),
stateTax,
statePartsTax,
us_sales_tax_breakdown,
state_tax: stateTax,
state_tax: statePartsTax,
local_tax: subtotal.percentage((job.local_tax_rate || 0) * 100)
};
ret.total_repairs = ret.subtotal

View File

@@ -489,7 +489,7 @@ function CalculateAdditional(job) {
additionalCosts: null,
additionalCostItems: [],
adjustments: null,
towing: Dinero(),
towing: null,
shipping: Dinero(),
storage: null,
pvrt: null,
@@ -512,7 +512,7 @@ function CalculateAdditional(job) {
}
if (val.line_desc.toLowerCase().includes("towing")) {
ret.towing = ret.towing.add(lineValue);
ret.towing = lineValue;
return acc;
} else {
ret.additionalCostItems.push({ key: val.line_desc, total: lineValue });

View File

@@ -1,76 +0,0 @@
const borderColors = [
"rgba(193, 0, 50, 1)",
"rgba(0, 101, 68, 1)",
"rgba(0, 45, 98, 1)",
"rgba(253, 184, 0, 1)",
"rgba(200, 112, 126, 1)",
"rgba(228, 142, 88, 1)",
"rgba(90, 160, 141, 1)",
"rgba(103, 143, 174, 1)",
"rgba(192, 136, 99, 1)",
"rgba(234, 3, 55, 1)",
"rgba(0, 149, 67, 1)",
"rgba(0, 81, 155, 1)",
"rgba(226, 143, 173, 1)",
"rgba(237, 170, 125, 1)",
"rgba(76, 146, 177, 1)",
"rgba(172, 153, 193, 1)",
"rgba(173, 167, 89, 1)",
"rgba(254, 197, 222, 1)",
"rgba(177, 231, 223, 1)",
"rgba(120, 199, 235, 1)",
"rgba(239, 180, 193, 1)",
"rgba(240, 199, 171, 1)",
"rgba(168, 200, 121, 1)",
"rgba(150, 177, 208, 1)",
"rgba(200, 194, 189, 1)",
"rgba(244, 244, 244, 1)",
"rgba(255, 99, 132, 1)",
"rgba(54, 162, 235, 1)",
"rgba(255, 206, 86, 1)",
"rgba(75, 192, 192, 1)",
"rgba(153, 102, 255, 1)",
"rgba(255, 159, 64, 1)",
"rgba(170, 183, 184, 1)",
];
const backgroundColors = [
'rgba(193, 0, 50, 0.2)',
'rgba(0, 101, 68, 0.2)',
'rgba(0, 45, 98, 0.2)',
'rgba(253, 184, 0, 0.2)',
'rgba(200, 112, 126, 0.2)',
'rgba(228, 142, 88, 0.2)',
'rgba(90, 160, 141, 0.2)',
'rgba(103, 143, 174, 0.2)',
'rgba(192, 136, 99, 0.2)',
'rgba(234, 3, 55, 0.2)',
'rgba(0, 149, 67, 0.2)',
'rgba(0, 81, 155, 0.2)',
'rgba(226, 143, 173, 0.2)',
'rgba(237, 170, 125, 0.2)',
'rgba(76, 146, 177, 0.2)',
'rgba(172, 153, 193, 0.2)',
'rgba(173, 167, 89, 0.2)',
'rgba(254, 197, 222, 0.2)',
'rgba(177, 231, 223, 0.2)',
'rgba(120, 199, 235, 0.2)',
'rgba(239, 180, 193, 0.2)',
'rgba(240, 199, 171, 0.2)',
'rgba(168, 200, 121, 0.2)',
'rgba(150, 177, 208, 0.2)',
'rgba(200, 194, 189, 0.2)',
'rgba(244, 244, 244, 0.2)',
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)',
'rgba(170, 183, 184, 0.2)',
];
module.exports = {
borderColors,
backgroundColors,
};

View File

@@ -1,92 +0,0 @@
const { createCanvas } = require("canvas");
const Chart = require("chart.js/auto");
const { backgroundColors, borderColors } = require("./canvas-colors");
const { isObject, defaultsDeep, isNumber } = require("lodash");
exports.canvastest = function (req, res) {
console.log("Incoming test request.", req);
res.status(200).send("OK");
};
exports.canvas = function (req, res) {
const { w, h, values, keys, override } = req.body;
console.log("Incoming Canvas Request:", w, h, values, keys, override);
// Gate required values
if (!values || !keys) {
res.status(400).send("Missing required data");
return;
}
// Override must be an object if it exists
if (override && !isObject(override)) {
res.status(400).send("Override must be an object");
return;
}
// Set the default Width and Height
let [width, height] = [500, 275];
// Allow for custom width and height
if (isNumber(w)) {
width = w;
}
if (isNumber(h)) {
height = h;
}
const configuration = {
type: "doughnut",
data: {
labels: keys,
datasets: [
{
data: values,
backgroundColor: backgroundColors,
borderColor: borderColors,
borderWidth: 1
}
]
},
options: {
devicePixelRatio: 4,
responsive: false,
maintainAspectRatio: true,
circumference: 180,
rotation: -90,
plugins: {
legend: {
labels: {
boxWidth: 20,
font: {
family: "'Montserrat'",
size: 10,
style: "normal",
weight: "normal"
}
},
position: "left"
}
}
}
};
// If we have a valid override object, merge it with the default configuration object.
// This allows for you to override the default configuration with a custom one.
const defaults = () => {
if (!override || !isObject(override)) {
return configuration;
}
return defaultsDeep(override, configuration);
};
res.status(200).send(
(() => {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext("2d");
new Chart(ctx, defaults());
return canvas.toDataURL();
})()
);
};

View File

@@ -1,13 +1,11 @@
const express = require("express");
const router = express.Router();
const cdkGetMake = require("../cdk/cdk-get-makes");
const cdkCalculateAllocations = require("../cdk/cdk-calculate-allocations");
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
router.use(validateFirebaseIdTokenMiddleware);
router.post("/getvehicles", withUserGraphQLClientMiddleware, cdkGetMake.default);
router.post("/calculate-allocations", withUserGraphQLClientMiddleware, cdkCalculateAllocations.defaultRoute);
module.exports = router;

View File

@@ -11,7 +11,6 @@ const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMI
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
const { taskAssignedEmail, tasksRemindEmail } = require("../email/tasksEmails");
const { canvastest } = require("../render/canvas-handler");
//Test route to ensure Express is responding.
router.get("/test", async function (req, res) {
@@ -50,7 +49,4 @@ router.post("/tasks-remind-handler", eventAuthorizationMiddleware, tasksRemindEm
router.post("/record-handler/arms", data.arms);
router.post("/taskHandler", validateFirebaseIdTokenMiddleware, taskHandler.taskHandler);
// Canvas Test
router.post("/canvastest", validateFirebaseIdTokenMiddleware, canvastest);
module.exports = router;

View File

@@ -2,10 +2,8 @@ const express = require("express");
const router = express.Router();
const { inlinecss } = require("../render/inlinecss");
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const { canvas } = require("../render/canvas-handler");
// Define the route for inline CSS rendering
router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss);
router.post("/canvas", validateFirebaseIdTokenMiddleware, canvas);
module.exports = router;