Compare commits

...

29 Commits

Author SHA1 Message Date
Patrick Fic
b8d2dbc2e1 Remove console log statmenets. 2022-05-27 14:38:45 -07:00
Patrick Fic
595ec72edd IO-1912 Resolve date for tech clock in. 2022-05-27 14:34:02 -07:00
Patrick Fic
885a861f1e IO-1910 Include totals of scoreboard components. 2022-05-27 10:39:16 -07:00
Patrick Fic
b213e5d54f Fix link on active jobs page. 2022-05-26 15:19:46 -07:00
Patrick Fic
653692b2a5 IO-1868 Prevent returns on invoiced RO. 2022-05-26 15:11:44 -07:00
Patrick Fic
e1e5dda710 IO-1891 Add owner Note. 2022-05-26 12:09:36 -07:00
Patrick Fic
7f3b1413d7 Remove files. 2022-05-26 12:04:56 -07:00
Patrick Fic
528c68695f IO-1885 Resolve UTC time for parts orders. 2022-05-26 11:31:09 -07:00
Patrick Fic
65a18acdc1 Added loading to parts receive modal. 2022-05-25 08:37:14 -07:00
Patrick Fic
930b2791f2 IO-1907 Prevent NaN PVRT. 2022-05-24 16:59:25 -07:00
Patrick Fic
1cf5a1fba8 IO-1864 Mark bills as exported in bulk. 2022-05-24 16:44:10 -07:00
Patrick Fic
83137b2d96 Merged in release/2022-05-20 (pull request #490)
Release/2022 05 20
2022-05-20 17:33:07 +00:00
Patrick Fic
5832a5f529 Add improved logging to export procedures. 2022-05-20 10:31:31 -07:00
Patrick Fic
f3ee20e89d Bug fixes for this weeks stories. 2022-05-20 09:19:54 -07:00
Patrick Fic
bcd062dffd Remove IO Event Tracking. 2022-05-20 09:01:37 -07:00
Patrick Fic
bd8b961bda IO-1894 Add scheduled return to all cc list. 2022-05-20 08:53:37 -07:00
Patrick Fic
43b6bdb024 IO-1896 Add remove from partst queue on parts order. 2022-05-19 11:08:12 -07:00
Patrick Fic
3dfc6eede2 Change scoreboard to query instead of subscription. 2022-05-19 11:07:51 -07:00
Patrick Fic
e70c11d4e6 IO-1890 Clear page when searching phonebook. 2022-05-19 10:46:36 -07:00
Patrick Fic
abc7262584 IO-1886 Handle large numbers of of menu items for notes and file handler. 2022-05-19 10:09:45 -07:00
Patrick Fic
e10ca9897c Merged in hotfix/2022-05-18 (pull request #485)
hotfix/2022-05-18

Approved-by: Patrick Fic
2022-05-18 19:00:28 +00:00
Patrick Fic
7e969e32b2 Merged in hotfix/2022-05-18 (pull request #484)
hotfix/2022-05-18

Approved-by: Patrick Fic
2022-05-18 19:00:12 +00:00
Patrick Fic
6f561e4caa Resolve job notes insert for ROs with no vehicle. 2022-05-18 11:59:46 -07:00
Patrick Fic
97beb14209 Merged in release/2022-05-13 (pull request #483)
release/2022-05-13

Approved-by: Patrick Fic
2022-05-17 18:24:42 +00:00
Patrick Fic
5568a434b9 Merged in release/2022-05-13 (pull request #482)
release/2022-05-13

Approved-by: Patrick Fic
2022-05-17 18:24:12 +00:00
Patrick Fic
ac890bd92b IO-1898 Add vehicle notes. 2022-05-17 08:24:04 -07:00
Patrick Fic
0eaf23841a IO-1893 Add scheduled in to parts queue. 2022-05-16 16:23:25 -07:00
Patrick Fic
708eb3c73f Merged in release/2022-05-13 (pull request #481)
Updated CI config.

Approved-by: Patrick Fic
2022-05-13 23:41:10 +00:00
Patrick Fic
9da126879e Merged in hotfix/2022-05-10 (pull request #470)
Update QBO export query for payments.

Approved-by: Patrick Fic
2022-05-10 21:25:52 +00:00
54 changed files with 4766 additions and 83846 deletions

View File

@@ -32076,6 +32076,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>note</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_addr1</name>
<definition_loaded>false</definition_loaded>
@@ -33533,6 +33554,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>removefrompartsqueue</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>returnpartsorder</name>
<definition_loaded>false</definition_loaded>
@@ -44249,6 +44291,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>notes</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>plate_no</name>
<definition_loaded>false</definition_loaded>

View File

@@ -4,54 +4,54 @@
"private": true,
"proxy": "http://localhost:4000",
"dependencies": {
"@apollo/client": "^3.5.10",
"@apollo/client": "^3.6.2",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^6.4.3",
"@fingerprintjs/fingerprintjs": "^3.3.3",
"@sentry/react": "^6.19.6",
"@sentry/tracing": "^6.19.6",
"@splitsoftware/splitio-react": "^1.4.0",
"@stripe/react-stripe-js": "^1.7.1",
"@stripe/stripe-js": "^1.27.0",
"@tanem/react-nprogress": "^4.0.12",
"antd": "^4.19.5",
"@sentry/react": "^6.19.7",
"@sentry/tracing": "^6.19.7",
"@splitsoftware/splitio-react": "^1.4.1",
"@stripe/react-stripe-js": "^1.8.0",
"@stripe/stripe-js": "^1.29.0",
"@tanem/react-nprogress": "^5.0.0",
"antd": "^4.20.5",
"apollo-link-logger": "^2.0.0",
"axios": "^0.26.1",
"axios": "^0.27.2",
"craco-less": "^1.20.0",
"dinero.js": "^1.9.1",
"dotenv": "^16.0.0",
"dotenv": "^16.0.1",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.6.11",
"graphql": "^16.3.0",
"i18next": "^21.6.16",
"firebase": "^9.8.1",
"graphql": "^16.5.0",
"i18next": "^21.8.2",
"i18next-browser-languagedetector": "^6.1.4",
"jsoneditor": "^9.7.4",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.51",
"logrocket": "^2.2.1",
"markerjs2": "^2.21.1",
"libphonenumber-js": "^1.9.53",
"logrocket": "^3.0.0",
"markerjs2": "^2.21.4",
"moment-business-days": "^1.2.0",
"moment-timezone": "^0.5.34",
"normalize-url": "^7.0.3",
"phone": "^3.1.15",
"phone": "^3.1.17",
"preval.macro": "^5.0.0",
"prop-types": "^15.8.1",
"query-string": "^7.1.1",
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"react": "^17.0.2",
"react-big-calendar": "^0.38.2",
"react-big-calendar": "^0.40.1",
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.2",
"react-drag-listview": "^0.1.9",
"react-drag-listview": "^0.2.0",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.3.4",
"react-i18next": "^11.16.6",
"react-i18next": "^11.16.9",
"react-icons": "^4.3.1",
"react-number-format": "^4.9.1",
"react-number-format": "^4.9.3",
"react-redux": "^7.2.8",
"react-resizable": "^3.0.4",
"react-router-dom": "^5.3.0",
@@ -60,28 +60,28 @@
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.1.9",
"redux": "^4.1.2",
"redux": "^4.2.0",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.1.5",
"sass": "^1.50.0",
"socket.io-client": "^4.4.1",
"sass": "^1.51.0",
"socket.io-client": "^4.5.0",
"styled-components": "^5.3.5",
"subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.4",
"workbox-background-sync": "^6.5.2",
"workbox-broadcast-update": "^6.5.2",
"workbox-cacheable-response": "^6.5.2",
"workbox-core": "^6.5.2",
"workbox-expiration": "^6.5.2",
"workbox-google-analytics": "^6.5.2",
"workbox-navigation-preload": "^6.5.2",
"workbox-precaching": "^6.5.2",
"workbox-range-requests": "^6.5.2",
"workbox-routing": "^6.5.2",
"workbox-strategies": "^6.5.2",
"workbox-streams": "^6.5.2",
"workbox-background-sync": "^6.5.3",
"workbox-broadcast-update": "^6.5.3",
"workbox-cacheable-response": "^6.5.3",
"workbox-core": "^6.5.3",
"workbox-expiration": "^6.5.3",
"workbox-google-analytics": "^6.5.3",
"workbox-navigation-preload": "^6.5.3",
"workbox-precaching": "^6.5.3",
"workbox-range-requests": "^6.5.3",
"workbox-routing": "^6.5.3",
"workbox-strategies": "^6.5.3",
"workbox-streams": "^6.5.3",
"yauzl": "^2.10.0"
},
"scripts": {
@@ -118,11 +118,11 @@
"react-error-overlay": "6.0.9"
},
"devDependencies": {
"@sentry/webpack-plugin": "^1.18.8",
"@sentry/webpack-plugin": "^1.18.9",
"@testing-library/cypress": "^8.0.2",
"cypress": "^9.5.3",
"cypress": "^9.6.1",
"eslint-plugin-cypress": "^2.12.1",
"react-error-overlay": "6.0.10",
"react-error-overlay": "6.0.11",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.2"
}

View File

@@ -14,6 +14,7 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
import BillMarkSelectedExported from "../payable-mark-selected-exported/payable-mark-selected-exported.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -28,7 +29,12 @@ export default connect(
mapDispatchToProps
)(AccountingPayablesTableComponent);
export function AccountingPayablesTableComponent({ bodyshop, loading, bills, refetch }) {
export function AccountingPayablesTableComponent({
bodyshop,
loading,
bills,
refetch,
}) {
const { t } = useTranslation();
const [selectedBills, setSelectedBills] = useState([]);
const [transInProgress, setTransInProgress] = useState(false);
@@ -143,15 +149,13 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
sorter: (a, b) => a.clm_total - b.clm_total,
render: (text, record) => (
<div>
<PayableExportButton
billId={record.id}
disabled={transInProgress || !!record.exported}
loadingCallback={setTransInProgress}
setSelectedBills={setSelectedBills}
refetch={refetch}
/>
</div>
<PayableExportButton
billId={record.id}
disabled={transInProgress || !!record.exported}
loadingCallback={setTransInProgress}
setSelectedBills={setSelectedBills}
refetch={refetch}
/>
),
},
];
@@ -177,6 +181,13 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref
<Card
extra={
<Space wrap>
<BillMarkSelectedExported
billids={selectedBills}
disabled={transInProgress || selectedBills.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedBills}
refetch={refetch}
/>
<PayableExportAll
billids={selectedBills}
disabled={transInProgress || selectedBills.length === 0}

View File

@@ -4,6 +4,7 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
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";
@@ -14,7 +15,7 @@ import BillDeleteButton from "../bill-delete-button/bill-delete-button.component
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
//jobRO: selectJobReadOnly,
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
@@ -29,6 +30,7 @@ const mapDispatchToProps = (dispatch) => ({
export function BillsListTableComponent({
bodyshop,
jobRO,
job,
billsQuery,
handleOnRowClick,
@@ -58,7 +60,9 @@ export function BillsListTableComponent({
<BillDeleteButton bill={record} />
<Button
disabled={
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
record.is_credit_memo ||
record.vendorid === bodyshop.inhousevendorid ||
jobRO
}
onClick={() => {
setPartsOrderContext({

View File

@@ -10,7 +10,7 @@ export default function CABCpvrtCalculator({ disabled, form }) {
const handleFinish = async (values) => {
logImEXEvent("job_ca_bc_pvrt_calculate");
form.setFieldsValue({ ca_bc_pvrt: (values.rate * values.days).toFixed(2) });
form.setFieldsValue({ ca_bc_pvrt: ((values.rate||0) * (values.days||0)).toFixed(2) });
setVisibility(false);
};

View File

@@ -3,6 +3,7 @@ import { Button, Card, Input, Space, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
@@ -105,6 +106,17 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
</Link>
) : null,
},
{
title: t("contracts.fields.scheduledreturn"),
dataIndex: "scheduledreturn",
key: "scheduledreturn",
render: (text, record) =>
record.cccontracts.length === 1 && (
<DateTimeFormatter>
{record.cccontracts[0].scheduledreturn}
</DateTimeFormatter>
),
},
];
const handleTableChange = (pagination, filters, sorter) => {

View File

@@ -16,15 +16,18 @@ export function JobsDetailChangeFilehandler({ disabled, form, bodyshop }) {
};
const menu = (
<div>
<Menu onClick={handleClick}>
{bodyshop.md_filehandlers.map((est, idx) => (
<Menu.Item value={est} key={idx}>
{`${est.ins_ct_fn} ${est.ins_ct_ln}`}
</Menu.Item>
))}
</Menu>
</div>
<Menu
onClick={handleClick}
style={{
columnCount: Math.floor(bodyshop.md_filehandlers.length / 10) + 1,
}}
>
{bodyshop.md_filehandlers.map((est, idx) => (
<Menu.Item value={est} key={idx} style={{ breakInside: "avoid" }}>
{`${est.ins_ct_fn} ${est.ins_ct_ln}`}
</Menu.Item>
))}
</Menu>
);
return (

View File

@@ -216,6 +216,22 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<DataLabel label={t("jobs.labels.relatedros")}>
<JobsRelatedRos jobid={job.id} job={job} />
</DataLabel>
{job.vehicle && job.vehicle.notes && (
<DataLabel label={t("vehicles.fields.notes")}>
<span style={{ whiteSpace: "pre" }}>{job.vehicle.notes}</span>
</DataLabel>
)}
{
// job.vehicle && job.vehicle.v_paint_codes && (
// <DataLabel label={t("vehicles.fields.v_paint_codes")}>
// <span style={{ whiteSpace: "pre" }}>
// {Object.keys(job.vehicle.v_paint_codes).map((key, idx) => (
// <Tag key={idx}>{job.vehicle.v_paint_codes[key]}</Tag>
// ))}
// </span>
// </DataLabel>
// )
}
</div>
</Card>
</Col>

View File

@@ -137,9 +137,9 @@ export function JobsList({ bodyshop }) {
sortOrder:
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => {
return record.owner ? (
return record.ownerid ? (
<Link
to={"/manage/owners/" + record.owner.id}
to={"/manage/owners/" + record.ownerid}
onClick={(e) => e.stopPropagation()}
>
<OwnerNameDisplay ownerObject={record} />

View File

@@ -61,7 +61,9 @@ export function JobNotesContainer({ jobId, insertAuditTrail }) {
jobId={jobId}
loading={loading}
data={data ? data.jobs_by_pk.notes : null}
relatedRos={data ? data.jobs_by_pk.vehicle.jobs : null}
relatedRos={
data ? data.jobs_by_pk.vehicle && data.jobs_by_pk.vehicle.jobs : null
}
refetch={refetch}
deleteLoading={deleteLoading}
handleNoteDelete={handleNoteDelete}

View File

@@ -22,9 +22,17 @@ export function NotesPresetButton({ bodyshop, form }) {
};
const menu = (
<Menu>
<Menu
style={{
columnCount: Math.floor(bodyshop.md_notes_presets.length / 10) + 1,
}}
>
{bodyshop.md_notes_presets.map((i, idx) => (
<Menu.Item onClick={() => handleSelect(i)} key={idx}>
<Menu.Item
onClick={() => handleSelect(i)}
key={idx}
style={{ breakInside: "avoid" }}
>
{i.label}
</Menu.Item>
))}

View File

@@ -14,7 +14,6 @@ export default function OwnerDetailFormComponent({ form, loading }) {
return (
<div>
<FormFieldsChanged form={form} />
<LayoutFormRow header={t("owners.forms.name")}>
<Form.Item label={t("owners.fields.ownr_title")} name="ownr_title">
<Input />
@@ -29,7 +28,6 @@ export default function OwnerDetailFormComponent({ form, loading }) {
<Input />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("owners.forms.address")}>
<Form.Item label={t("owners.fields.ownr_addr1")} name="ownr_addr1">
<Input />
@@ -50,7 +48,6 @@ export default function OwnerDetailFormComponent({ form, loading }) {
<Input />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("owners.forms.contact")}>
<Form.Item
label={t("owners.fields.allow_text_message")}
@@ -98,6 +95,9 @@ export default function OwnerDetailFormComponent({ form, loading }) {
<Input />
</Form.Item>
</LayoutFormRow>
<Form.Item label={t("owners.fields.note")} name="note">
<Input.TextArea rows={4} />
</Form.Item>
</div>
);
}

View File

@@ -59,6 +59,14 @@ export default function OwnerFindModalComponent({
<PhoneFormatter>{record.ownr_ph2}</PhoneFormatter>
),
},
{
title: t("owners.fields.note"),
dataIndex: "note",
key: "note",
render: (text, record) => (
<span style={{ whiteSpace: "pre" }}>{record.note}</span>
),
},
];
const handleOnRowClick = (record) => {

View File

@@ -11,6 +11,7 @@ import {
Select,
Menu,
Dropdown,
Checkbox,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -114,6 +115,15 @@ export function PartsOrderModalComponent({
</Space>
</Tag>
)}
{!isReturn && (
<Form.Item
name="removefrompartsqueue"
label={t("parts_orders.labels.removefrompartsqueue")}
valuePropName="checked"
>
<Checkbox />
</Form.Item>
)}
</LayoutFormRow>
<Divider orientation="left">
{t("parts_orders.labels.inthisorder")}
@@ -280,6 +290,7 @@ export function PartsOrderModalComponent({
>
<Input.TextArea rows={3} />
</Form.Item>
<Radio.Group
defaultValue={sendType}
onChange={(e) => setSendType(e.target.value)}

View File

@@ -32,6 +32,7 @@ import PartsOrderModalComponent from "./parts-order-modal.component";
import axios from "axios";
import { useTreatments } from "@splitsoftware/splitio-react";
import _ from "lodash";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
@@ -90,8 +91,9 @@ export function PartsOrderModalContainer({
const [insertPartOrder] = useMutation(INSERT_NEW_PARTS_ORDERS);
const [updateJobLines] = useMutation(UPDATE_JOB_LINE_STATUS);
const [updateJob] = useMutation(UPDATE_JOB);
const handleFinish = async (values) => {
const handleFinish = async ({ removefrompartsqueue, ...values }) => {
logImEXEvent("parts_order_insert");
setSaving(true);
const insertResult = await insertPartOrder({
@@ -99,6 +101,7 @@ export function PartsOrderModalContainer({
po: [
{
...values,
order_date: moment().format("YYYY-MM-DD"),
orderedby: currentUser.email,
jobid: jobId,
user_email: currentUser.email,
@@ -128,6 +131,17 @@ export function PartsOrderModalContainer({
},
});
if (!isReturn && removefrompartsqueue) {
await updateJob({
variables: {
jobId: jobId,
job: {
queued_for_parts: false,
},
},
});
}
insertAuditTrail({
jobid: jobId,
operation: isReturn

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client";
import { Form, Modal, notification } from "antd";
import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -31,7 +31,7 @@ export function PartsReceiveModalContainer({
bodyshop,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const { visible, context, actions } = partsReceiveModal;
const { partsorderlines } = context;
@@ -42,7 +42,7 @@ export function PartsReceiveModalContainer({
const handleFinish = async (values) => {
logImEXEvent("parts_order_receive");
setLoading(true);
const result = await Promise.all(
values.partsorderlines.map((li) => {
return receivePartsLine({
@@ -75,7 +75,7 @@ export function PartsReceiveModalContainer({
notification["success"]({
message: t("parts_orders.successes.received"),
});
setLoading(false);
if (refetch) refetch();
toggleModalVisible();
};
@@ -96,6 +96,7 @@ export function PartsReceiveModalContainer({
title={t("parts_orders.labels.receive")}
onCancel={() => toggleModalVisible()}
onOk={() => form.submit()}
okButtonProps={{ loading: loading }}
destroyOnClose
forceRender
width="50%"

View File

@@ -0,0 +1,79 @@
import { gql, useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
selectAuthLevel,
selectBodyshop,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
authLevel: selectAuthLevel,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(BillMarkSelectedExported);
export function BillMarkSelectedExported({
billids,
disabled,
loadingCallback,
completedCallback,
refetch,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [updateBill] = useMutation(gql`
mutation UPDATE_BILL($billIds: [uuid!]!) {
update_bills(where: { id: { _in: $billIds } }, _set: { exported: true }) {
returning {
id
exported
exported_at
}
}
}
`);
const handleUpdate = async () => {
setLoading(true);
loadingCallback(true);
const result = await updateBill({
variables: { billIds: billids },
update(cache){
}
});
if (!result.errors) {
notification["success"]({
message: t("bills.successes.markexported"),
});
} else {
notification["error"]({
message: t("bills.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
loadingCallback(false);
completedCallback && completedCallback([]);
setLoading(false);
refetch && refetch();
};
return (
<Button loading={loading} disabled={disabled} onClick={handleUpdate}>
{t("bills.labels.markexported")}
</Button>
);
}

View File

@@ -42,17 +42,24 @@ export function ProductionColumnsComponent({
};
const columnKeys = columns.map((i) => i.key);
const cols = dataSource({
technician,
state: tableState,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
});
const menu = (
<Menu onClick={handleAdd}>
{dataSource({
technician,
state: tableState,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
})
<Menu
onClick={handleAdd}
style={{
columnCount: Math.max(Math.floor(cols.length / 10), 1),
}}
>
{cols
.filter((i) => !columnKeys.includes(i.key))
.map((item) => (
<Menu.Item key={item.key}>{item.title}</Menu.Item>
<Menu.Item key={item.key} style={{ breakInside: "avoid" }}>
{item.title}
</Menu.Item>
))}
</Menu>
);

View File

@@ -8,8 +8,11 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment";
import { useApolloClient } from "@apollo/client";
import { GET_BLOCKED_DAYS } from "../../graphql/scoreboard.queries";
import { useApolloClient, useQuery } from "@apollo/client";
import {
GET_BLOCKED_DAYS,
QUERY_SCOREBOARD,
} from "../../graphql/scoreboard.queries";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
@@ -22,10 +25,15 @@ export default connect(
mapDispatchToProps
)(ScoreboardDisplayComponent);
export function ScoreboardDisplayComponent({
bodyshop,
scoreboardSubscription,
}) {
export function ScoreboardDisplayComponent({ bodyshop }) {
const scoreboardSubscription = useQuery(QUERY_SCOREBOARD, {
variables: {
start: moment().startOf("month"),
end: moment().endOf("month"),
},
pollInterval: 60000,
});
const { data } = scoreboardSubscription;
const client = useApolloClient();
const scoreBoardlist = (data && data.scoreboard) || [];

View File

@@ -177,6 +177,27 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
<Statistic value={values.toDatePaint.toFixed(1)} />
</Col>
</Row>
<Row>
<Col {...statSpans}></Col>
<Col {...statSpans}>
<Statistic
value={(values.todayPaint + values.todayBody).toFixed(1)}
/>
</Col>
<Col {...statSpans}></Col>
<Col {...statSpans}>
<Statistic
value={(values.weeklyPaint + values.weeklyBody).toFixed(1)}
/>
</Col>
<Col {...statSpans}></Col>
<Col {...statSpans}></Col>
<Col {...statSpans}>
<Statistic
value={(values.toDatePaint + values.toDateBody).toFixed(1)}
/>
</Col>
</Row>
</Col>
</Row>
</Card>

View File

@@ -10,6 +10,7 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import TechClockInComponent from "./tech-job-clock-in-form.component";
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
import moment from "moment";
const mapStateToProps = createStructuredSelector({
technician: selectTechnician,
@@ -27,14 +28,15 @@ export function TechClockInContainer({ technician, bodyshop }) {
const handleFinish = async (values) => {
setLoading(true);
const theTime = (await axios.post("/utils/time")).data;
const result = await insertTimeTicket({
variables: {
timeTicketInput: [
{
bodyshopid: bodyshop.id,
employeeid: technician.id,
date: theTime,
clockon: theTime,
date: moment(theTime).format("YYYY-MM-DD"),
clockon: moment(theTime),
jobid: values.jobid,
cost_center: values.cost_center,
ciecacode:

View File

@@ -55,9 +55,10 @@ export function TechClockOffButton({
timeticket: {
clockoff: (await axios.post("/utils/time")).data,
...values,
rate: emps && emps.rates.filter(
(r) => r.cost_center === values.cost_center
)[0]?.rate,
rate:
emps &&
emps.rates.filter((r) => r.cost_center === values.cost_center)[0]
?.rate,
ciecacode:
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? values.cost_center

View File

@@ -139,6 +139,9 @@ export default function VehicleDetailFormComponent({ form, loading }) {
<Input />
</Form.Item>
</LayoutFormRow>
<Form.Item label={t("vehicles.fields.notes")} name="notes">
<Input.TextArea rows={4} />
</Form.Item>
</div>
);
}

View File

@@ -86,6 +86,7 @@ export const QUERY_ALL_CC = gql`
limit: 1
) {
id
scheduledreturn
job {
id
ro_number

View File

@@ -12,6 +12,7 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
ownr_ph1
ownr_ph2
ownr_ea
ownerid
comment
plate_no
plate_st
@@ -75,13 +76,9 @@ export const QUERY_PARTS_QUEUE = gql`
v_make_desc
v_color
vehicleid
actual_completion
actual_delivery
actual_in
scheduled_in
id
clm_no
clm_total
owner_owing
ro_number
status
updated_at
@@ -534,6 +531,8 @@ export const GET_JOB_BY_PK = gql`
v_model_desc
v_make_desc
v_color
notes
v_paint_codes
jobs {
id
ro_number
@@ -870,6 +869,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
v_model_desc
v_color
plate_no
notes
jobs {
id
clm_no

View File

@@ -15,6 +15,7 @@ export const QUERY_SEARCH_OWNER_BY_IDX = gql`
ownr_st
ownr_zip
id
note
}
}
`;
@@ -65,6 +66,7 @@ export const QUERY_OWNER_BY_ID = gql`
ownr_title
ownr_zip
preferred_contact
note
jobs {
id
ro_number

View File

@@ -1,7 +1,7 @@
import { gql } from "@apollo/client";
export const SUBSCRIPTION_SCOREBOARD = gql`
subscription SUBSCRIPTION_SCOREBOARD($start: date!, $end: date!) {
export const QUERY_SCOREBOARD = gql`
query QUERY_SCOREBOARD($start: date!, $end: date!) {
scoreboard(
where: { _and: { date: { _gte: $start, _lte: $end } } }
order_by: { date: asc }

View File

@@ -27,6 +27,7 @@ export const QUERY_VEHICLE_BY_ID = gql`
v_bstyle
updated_at
trim_color
notes
jobs {
id
ro_number

View File

@@ -15,7 +15,7 @@ import OwnerNameDisplay from "../../components/owner-name-display/owner-name-dis
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
@@ -158,6 +158,17 @@ export function PartsQueuePageComponent({ bodyshop }) {
},
},
{
title: t("jobs.fields.scheduled_in"),
dataIndex: "scheduled_in",
key: "scheduled_in",
ellipsis: true,
sorter: (a, b) => dateSort(a.scheduled_in, b.scheduled_in),
sortOrder: sortcolumn === "scheduled_in" && sortorder,
render: (text, record) => (
<DateTimeFormatter>{record.scheduled_in}</DateTimeFormatter>
),
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
@@ -194,13 +205,6 @@ export function PartsQueuePageComponent({ bodyshop }) {
ellipsis: true,
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder: sortcolumn === "clm_no" && sortorder,
render: (text, record) => {
return record.clm_no ? (
<span>{record.clm_no}</span>
) : (
t("general.labels.unknown")
);
},
},
// {
// title: t("jobs.fields.clm_total"),

View File

@@ -160,6 +160,7 @@ export function PhonebookPageComponent({ bodyshop, authLevel }) {
<Button
onClick={() => {
delete searchParams.search;
searchParams.page = 1;
history.push({ search: queryString.stringify(searchParams) });
}}
>
@@ -177,6 +178,7 @@ export function PhonebookPageComponent({ bodyshop, authLevel }) {
placeholder={searchParams.search || t("general.labels.search")}
onSearch={(value) => {
searchParams.search = value;
searchParams.page = 1;
history.push({ search: queryString.stringify(searchParams) });
}}
/>

View File

@@ -2,17 +2,14 @@ import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
import { useSubscription } from "@apollo/client";
import { SUBSCRIPTION_SCOREBOARD } from "../../graphql/scoreboard.queries";
import moment from "moment";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -26,13 +23,6 @@ const mapDispatchToProps = (dispatch) => ({
export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
const scoreboardSubscription = useSubscription(SUBSCRIPTION_SCOREBOARD, {
variables: {
start: moment().startOf("month"),
end: moment().endOf("month"),
},
});
useEffect(() => {
document.title = t("titles.scoreboard");
setSelectedHeader("scoreboard");
@@ -47,7 +37,7 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
return (
<FeatureWrapper featureName="scoreboard">
<RbacWrapper action="scoreboard:view">
<ScoreboardDisplay scoreboardSubscription={scoreboardSubscription} />
<ScoreboardDisplay />
</RbacWrapper>
</FeatureWrapper>
);

View File

@@ -1893,6 +1893,7 @@
"address": "Address",
"allow_text_message": "Permission to Text?",
"name": "Name",
"note": "Owner Note",
"ownr_addr1": "Address",
"ownr_addr2": "Address 2",
"ownr_city": "City",
@@ -1980,6 +1981,7 @@
"parts_orders": "Parts Orders",
"print": "Show Printed Form",
"receive": "Receive Parts Order",
"removefrompartsqueue": "Remove from Parts Queue?",
"returnpartsorder": "Return Parts Order"
},
"successes": {
@@ -2633,6 +2635,7 @@
},
"fields": {
"description": "Vehicle Description",
"notes": "Vehicle Notes",
"plate_no": "License Plate",
"plate_st": "Plate Jurisdiction",
"trim_color": "Trim Color",

View File

@@ -1893,6 +1893,7 @@
"address": "Dirección",
"allow_text_message": "Permiso de texto?",
"name": "Nombre",
"note": "",
"ownr_addr1": "Dirección",
"ownr_addr2": "Dirección 2",
"ownr_city": "ciudad",
@@ -1980,6 +1981,7 @@
"parts_orders": "",
"print": "Mostrar formulario impreso",
"receive": "",
"removefrompartsqueue": "",
"returnpartsorder": ""
},
"successes": {
@@ -2633,6 +2635,7 @@
},
"fields": {
"description": "Descripcion del vehiculo",
"notes": "",
"plate_no": "Placa",
"plate_st": "Jurisdicción de placas",
"trim_color": "Recortar color",

View File

@@ -1893,6 +1893,7 @@
"address": "Adresse",
"allow_text_message": "Autorisation de texte?",
"name": "Prénom",
"note": "",
"ownr_addr1": "Adresse",
"ownr_addr2": "Adresse 2 ",
"ownr_city": "Ville",
@@ -1980,6 +1981,7 @@
"parts_orders": "",
"print": "Afficher le formulaire imprimé",
"receive": "",
"removefrompartsqueue": "",
"returnpartsorder": ""
},
"successes": {
@@ -2633,6 +2635,7 @@
},
"fields": {
"description": "Description du véhicule",
"notes": "",
"plate_no": "Plaque d'immatriculation",
"plate_st": "Juridiction de la plaque",
"trim_color": "Couleur de garniture",

View File

@@ -6,10 +6,10 @@ import { WebSocketLink } from "@apollo/client/link/ws";
import { getMainDefinition } from "@apollo/client/utilities";
//import { split } from "apollo-link";
import apolloLogger from "apollo-link-logger";
import axios from "axios";
//import axios from "axios";
import { auth } from "../firebase/firebase.utils";
import errorLink from "../graphql/apollo-error-handling";
import { store } from "../redux/store";
//import { store } from "../redux/store";
const httpLink = new HttpLink({
uri: process.env.REACT_APP_GRAPHQL_ENDPOINT,
});
@@ -48,25 +48,25 @@ const roundTripLink = new ApolloLink((operation, forward) => {
});
const TrackExecutionTime = async (operationName, time) => {
if (process.env.NODE_ENV === "development") return;
const rdxStore = store.getState();
try {
axios.post("/ioevent", {
operationName,
time,
dbevent: true,
user:
rdxStore.user &&
rdxStore.user.currentUser &&
rdxStore.user.currentUser.email,
imexshopid:
rdxStore.user &&
rdxStore.user.bodyshop &&
rdxStore.user.bodyshop.imexshopid,
});
} catch (error) {
console.log("IOEvent Error", error);
}
// if (process.env.NODE_ENV === "development") return;
// const rdxStore = store.getState();
// try {
// axios.post("/ioevent", {
// operationName,
// time,
// dbevent: true,
// user:
// rdxStore.user &&
// rdxStore.user.currentUser &&
// rdxStore.user.currentUser.email,
// imexshopid:
// rdxStore.user &&
// rdxStore.user.bodyshop &&
// rdxStore.user.bodyshop.imexshopid,
// });
// } catch (error) {
// console.log("IOEvent Error", error);
// }
};
const subscriptionMiddleware = {

File diff suppressed because it is too large Load Diff

View File

@@ -3823,32 +3823,36 @@
- active:
_eq: true
columns:
- id
- accountingid
- allow_text_message
- created_at
- updated_at
- ownr_fn
- ownr_ln
- id
- note
- ownr_addr1
- ownr_addr2
- ownr_city
- ownr_st
- ownr_zip
- ownr_co_nm
- ownr_ctry
- ownr_ea
- ownr_fn
- ownr_ln
- ownr_ph1
- preferred_contact
- allow_text_message
- shopid
- ownr_ph2
- ownr_co_nm
- ownr_st
- ownr_title
- accountingid
- ownr_zip
- preferred_contact
- shopid
- updated_at
select_permissions:
- role: user
permission:
columns:
- allow_text_message
- accountingid
- allow_text_message
- created_at
- id
- note
- ownr_addr1
- ownr_addr2
- ownr_city
@@ -3863,10 +3867,8 @@
- ownr_title
- ownr_zip
- preferred_contact
- created_at
- updated_at
- id
- shopid
- updated_at
filter:
bodyshop:
associations:
@@ -3881,8 +3883,11 @@
- role: user
permission:
columns:
- allow_text_message
- accountingid
- allow_text_message
- created_at
- id
- note
- ownr_addr1
- ownr_addr2
- ownr_city
@@ -3897,10 +3902,8 @@
- ownr_title
- ownr_zip
- preferred_contact
- created_at
- updated_at
- id
- shopid
- updated_at
filter:
bodyshop:
associations:
@@ -4925,60 +4928,62 @@
- active:
_eq: true
columns:
- id
- created_at
- updated_at
- v_vin
- v_make_desc
- v_model_desc
- v_model_yr
- v_color
- v_paint_codes
- v_bstyle
- v_engine
- shopid
- db_v_code
- id
- notes
- plate_no
- plate_st
- v_cond
- v_prod_dt
- v_type
- v_trimcode
- shopid
- trim_color
- v_mldgcode
- v_options
- v_tone
- v_stage
- updated_at
- v_bstyle
- v_color
- v_cond
- v_engine
- v_make_desc
- v_makecode
- v_mldgcode
- v_model_desc
- v_model_yr
- v_options
- v_paint_codes
- v_prod_dt
- v_stage
- v_tone
- v_trimcode
- v_type
- v_vin
select_permissions:
- role: user
permission:
columns:
- v_paint_codes
- created_at
- db_v_code
- id
- notes
- plate_no
- plate_st
- shopid
- trim_color
- updated_at
- v_bstyle
- v_color
- v_cond
- v_engine
- v_makecode
- v_make_desc
- v_makecode
- v_mldgcode
- v_model_desc
- v_model_yr
- v_options
- v_paint_codes
- v_prod_dt
- v_stage
- v_tone
- v_trimcode
- v_type
- v_vin
- created_at
- updated_at
- id
- shopid
filter:
bodyshop:
associations:
@@ -4993,31 +4998,32 @@
- role: user
permission:
columns:
- v_paint_codes
- created_at
- db_v_code
- id
- notes
- plate_no
- plate_st
- shopid
- trim_color
- updated_at
- v_bstyle
- v_color
- v_cond
- v_engine
- v_makecode
- v_make_desc
- v_makecode
- v_mldgcode
- v_model_desc
- v_model_yr
- v_options
- v_paint_codes
- v_prod_dt
- v_stage
- v_tone
- v_trimcode
- v_type
- v_vin
- created_at
- updated_at
- id
- shopid
filter:
bodyshop:
associations:

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."vehicles" add column "notes" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."vehicles" add column "notes" text
null;

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."owners" add column "note" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."owners" add column "note" text
null;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -17,21 +17,21 @@
"start": "node server.js"
},
"dependencies": {
"aws-sdk": "^2.1116.0",
"axios": "^0.24.0",
"aws-sdk": "^2.1136.0",
"axios": "^0.27.2",
"bluebird": "^3.7.2",
"body-parser": "^1.20.0",
"cloudinary": "^1.29.1",
"cloudinary": "^1.30.0",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",
"cors": "2.8.5",
"csrf": "^3.1.0",
"dinero.js": "^1.9.1",
"dotenv": "10.0.0",
"express": "^4.17.3",
"firebase-admin": "^10.0.2",
"graphql": "^16.3.0",
"graphql-request": "^3.7.0",
"dotenv": "16.0.1",
"express": "^4.18.1",
"firebase-admin": "^10.2.0",
"graphql": "^16.5.0",
"graphql-request": "^4.2.0",
"graylog2": "^0.2.1",
"inline-css": "^3.0.0",
"intuit-oauth": "^4.0.0",
@@ -40,16 +40,16 @@
"moment": "^2.29.3",
"moment-timezone": "^0.5.34",
"multer": "^1.4.4",
"node-mailjet": "^3.3.10",
"node-mailjet": "^3.4.1",
"node-quickbooks": "^2.0.39",
"nodemailer": "^6.7.3",
"phone": "^3.1.15",
"nodemailer": "^6.7.5",
"phone": "^3.1.17",
"query-string": "^7.1.1",
"soap": "^0.43.0",
"socket.io": "^4.4.1",
"socket.io": "^4.5.0",
"ssh2-sftp-client": "^8.0.0",
"stripe": "^8.217.0",
"twilio": "^3.76.1",
"stripe": "^9.1.0",
"twilio": "^3.77.0",
"uuid": "^8.3.2",
"xml2js": "^0.4.23",
"xmlbuilder2": "^3.0.2"

View File

@@ -147,7 +147,7 @@ exports.default = async (req, res) => {
res.status(200).json(ret);
} catch (error) {
console.log(error);
logger.log("qbo-payable-create-error", "ERROR", req.user.email, { error });
logger.log("qbo-payable-create-error", "ERROR", req.user.email, { error: error.message, stack: error.stack });
res.status(400).json(error);
}
};

View File

@@ -179,6 +179,7 @@ exports.default = async (req, res) => {
ret.push({ paymentid: payment.id, success: true });
} catch (error) {
logger.log("qbo-payment-create-error", "ERROR", req.user.email, {
error:
(error && error.authResponse && error.authResponse.body) ||
(error && error.message),
@@ -216,7 +217,7 @@ exports.default = async (req, res) => {
res.status(200).json(ret);
} catch (error) {
console.log(error);
logger.log("qbo-payment-create-error", "ERROR", req.user.email, { error });
logger.log("qbo-payment-create-error", "ERROR", req.user.email, { error: error.message, stack: error.stack });
res.status(400).json(error);
}
};

View File

@@ -197,7 +197,8 @@ exports.default = async (req, res) => {
} catch (error) {
console.log(error);
logger.log("qbo-receivable-create-error", "ERROR", req.user.email, {
error,
error: error.message,
stack: error.stack,
});
res.status(400).json(error);
}

View File

@@ -57,7 +57,7 @@ exports.default = async (req, res) => {
"ERROR",
req.user.email,
req.body.billsToQuery,
{ error }
{ error: error.message, stack: error.stack }
);
res.status(400).send(JSON.stringify(error));
}

View File

@@ -95,7 +95,7 @@ exports.default = async (req, res) => {
"error",
req.user.email,
req.body.paymentsToQuery,
error
{ error: error.message, stack: error.stack }
);
res.status(400).send(JSON.stringify(error));
}

View File

@@ -109,7 +109,7 @@ exports.default = async (req, res) => {
"error",
req.user.email,
req.body.jobIds,
{ error: error }
{ error: error.message, stack: error.stack }
);
res.status(400).send(JSON.stringify(error));
}

View File

@@ -800,7 +800,7 @@ const CreateCosts = (job) => {
};
const StatusMapping = (status, md_ro_statuses) => {
//EST, SCH, ARR, IPR, RDY, DEL, CLO, CAN, UNDEFINED.
//Possible return statuses EST, SCH, ARR, IPR, RDY, DEL, CLO, CAN, UNDEFINED.
const {
default_imported,
default_open,
@@ -823,8 +823,6 @@ const StatusMapping = (status, md_ro_statuses) => {
else if (status === default_void) return "VOID";
else if (md_ro_statuses.production_statuses.includes(status)) return "IPR";
else return "UNDEFINED";
// default: return "UNDEFINED"
};
const GenerateDetailLines = (job, line, statuses) => {

View File

@@ -917,7 +917,10 @@ exports.GET_JOB_BY_PK = ` query GET_JOB_BY_PK($id: uuid!) {
est_co_nm
est_ct_fn
est_ct_ln
vehicle{
id
notes
}
est_ph1
est_ea
selling_dealer

7266
yarn.lock

File diff suppressed because it is too large Load Diff