Compare commits

...

91 Commits

Author SHA1 Message Date
Allan Carr
c0dab92d0e IO-2522 Load Level Table Change 2024-01-05 12:01:47 -08:00
Allan Carr
1a4bc720c2 Merged in release/2023-12-29 (pull request #1127)
Release/2023 12 29
2023-12-29 22:08:52 +00:00
Allan Carr
18998c4dbe Merged in feature/IO-2489-Regi-#-in-Vehicle-Box (pull request #1125)
IO-2489 Registration # in Vehicle Info Box

Approved-by: Dave Richer
2023-12-28 18:56:27 +00:00
Allan Carr
e236d6e912 IO-2489 Registration # in Vehicle Info Box 2023-12-28 10:57:03 -08:00
Allan Carr
523df670df Merged in feature/IO-2512-Date-Estimated-Manual-Job (pull request #1122)
IO-2512 Date Esimated on Manual Created Jobs

Approved-by: Dave Richer
2023-12-28 18:22:43 +00:00
Allan Carr
ce58181fc3 Merged in feature/IO-2500-Courtesy-Car-Readiness-&-Fuel-Level (pull request #1123)
IO-2500 Readiness and Fuel Level

Approved-by: Dave Richer
2023-12-28 18:21:35 +00:00
Allan Carr
bed0669f73 Merged in feature/IO-2513-Fuel-Level-Tooltip (pull request #1124)
IO-2500 Courtesy Car Readiness

Approved-by: Dave Richer
2023-12-28 18:19:16 +00:00
Allan Carr
bdb2951330 IO-2500 Courtesy Car Readiness 2023-12-27 13:55:31 -08:00
Allan Carr
87a01208fb IO-2500 Readiness and Fuel Level 2023-12-27 13:35:29 -08:00
Allan Carr
2daee84fbf IO-2512 Date Esimated on Manual Created Jobs 2023-12-27 11:07:17 -08:00
Allan Carr
cdbf58f3ac Merged in feature/IO-2511-Bill-Label-Reprint (pull request #1121)
IO-2511 Bill Label Reprint

Approved-by: Dave Richer
2023-12-27 16:18:44 +00:00
Allan Carr
f6a59bdf55 IO-2511 Bill Label Reprint 2023-12-26 11:18:39 -08:00
Allan Carr
e12edd977e Merged in release/2023-12-15 (pull request #1120)
Release/2023 12 15

Approved-by: Dave Richer
2023-12-22 18:16:40 +00:00
Allan Carr
b925c991eb Merged in feature/IO-2501-Add-Jobs-Completed-Delivered-Not-Invoiced-Section (pull request #1118)
IO-2501 Correct for missing query variables

Approved-by: Dave Richer
2023-12-22 02:33:30 +00:00
Allan Carr
1e7f43fe3d IO-2501 Correct for missing query variables 2023-12-21 18:32:37 -08:00
Allan Carr
6f21de1901 Merged in feature/IO-2505-Conversation-List-Print (pull request #1116)
IO-2505 Conversation List Print

Approved-by: Dave Richer
2023-12-21 17:37:47 +00:00
Allan Carr
25e8eaa1d4 IO-2505 Conversation List Print 2023-12-21 09:24:25 -08:00
Allan Carr
6fe736ce06 Merged in feature/IO-1366-Audit-Logging (pull request #1114)
IO-1366 Invoice Job Audit Trail

Approved-by: Dave Richer
2023-12-20 20:44:11 +00:00
Allan Carr
482b03c2d1 IO-1366 Invoice Job Audit Trail 2023-12-20 11:52:04 -08:00
Allan Carr
ce3c72fc47 Merged in feature/IO-1366-Audit-Logging (pull request #1113)
IO-1366 Audit Logging for Production Alert

Approved-by: Dave Richer
2023-12-20 19:38:46 +00:00
Allan Carr
1ff5ed4141 IO-1366 Audit Logging for Production Alert 2023-12-20 11:36:42 -08:00
Allan Carr
10e3421572 Merged in feature/IO-2506-Federal-Tax-Exempt-on-Bill-Entry (pull request #1111)
IO-2506 Correct for variable immutibility and nested ifs
2023-12-19 17:16:48 +00:00
Allan Carr
0117237988 IO-2506 Correct for variable immutibility and nested ifs 2023-12-19 09:18:00 -08:00
Allan Carr
373fd817d0 Merged in feature/IO-2506-Federal-Tax-Exempt-on-Bill-Entry (pull request #1108)
IO-2506 Federal Tax Exempt on Bill Entry

Approved-by: Dave Richer
2023-12-19 16:34:38 +00:00
Allan Carr
0ef2d9646d Merged in feature/IO-2509-Report-Center-RBAC (pull request #1109)
IO-2509 Report Center RBAC

Approved-by: Dave Richer
2023-12-19 16:32:29 +00:00
Allan Carr
c8ac417200 IO-2509 Report Center RBAC 2023-12-18 14:12:21 -08:00
Allan Carr
661bedbe5b IO-2506 Federal Tax Exempt on Bill Entry
Will Toggle Federal Tax off on any new line or retroactively toggle it off on all lines when switch is enabled. Limited to PBS or CDK setups.
2023-12-18 12:36:46 -08:00
Allan Carr
2dd56590d3 Admin panel to force email addresses to be lowercase to conform with firebase 2023-12-14 08:57:36 -08:00
Allan Carr
98b760251c Merged in feature/IO-2501-Add-Jobs-Completed-Delivered-Not-Invoiced-Section (pull request #1107)
IO-2501 Add Jobs Complete Not Invoiced Section to Stats

Approved-by: Dave Richer
2023-12-13 18:47:04 +00:00
Allan Carr
b97de32a44 IO-2501 Add Jobs Complete Not Invoiced Section to Stats 2023-12-12 15:41:36 -08:00
Dave Richer
92c8b54f85 Merged in release/2023-12-01 (pull request #1103)
Reversion

Approved-by: Allan Carr
2023-12-04 16:50:13 +00:00
Dave Richer
d8420f472c Merged in feature/reversion-to-active-jobs-pagination (pull request #1102)
Reversion
2023-12-04 16:40:50 +00:00
Dave Richer
34d93c4de0 Reversion 2023-12-04 11:39:20 -05:00
Dave Richer
1c400cd456 Merged in release/2023-12-01 (pull request #1099)
Release/2023 12 01

Approved-by: Allan Carr
2023-12-01 18:18:23 +00:00
Allan Carr
a3cf97fcab Merged in feature/IO-2485-Fix-onlyFuture-Prop (pull request #1095)
IO-2485 Correct onlyFuture on typed values

Approved-by: Dave Richer
2023-11-30 16:56:57 +00:00
Allan Carr
1a9dc7a377 Merged in feature/IO-2484-Next-Service-KMs (pull request #1094)
IO-2484 Next Service KMs

Approved-by: Dave Richer
2023-11-30 16:55:55 +00:00
Allan Carr
806daebd3f IO-2485 Correct onlyFuture on typed values 2023-11-29 19:51:03 -08:00
Allan Carr
dfd8845864 IO-2484 Next Service KMs
Allow null and only display warning if not null and current milage is greater than service KMs
2023-11-29 17:32:09 -08:00
Allan Carr
170108b339 Merged in feature/IO-2465-Add-Vehicle-to-Override-Headers (pull request #1092)
IO-2465 Adjust Headerfile override and comment out line
2023-11-30 01:15:53 +00:00
Allan Carr
9f1f58a9c7 IO-2465 Adjust Headerfile override and comment out line 2023-11-29 17:14:39 -08:00
Dave Richer
4288e2d986 Merged in feature/IO-2403-Paginated-Active-Jobs (pull request #1088)
Fix issues with limits.
2023-11-29 22:27:49 +00:00
Dave Richer
4b289388bf Fix issues with limits. 2023-11-29 17:27:08 -05:00
Dave Richer
ed0136090c Merged in feature/IO-2403-Paginated-Active-Jobs (pull request #1086)
Fix order issue on all jobs
2023-11-29 21:10:56 +00:00
Dave Richer
0d70545b98 Fix order issue on all jobs 2023-11-29 16:10:30 -05:00
Dave Richer
2252091b53 Fix order issue on all jobs 2023-11-29 16:09:20 -05:00
Dave Richer
386531884b Merged in feature/IO-2403-Paginated-Active-Jobs (pull request #1068)
Paginated Active Jobs / Pagination refactor
2023-11-29 18:43:00 +00:00
Dave Richer
afbe328aa7 Merged release/2023-12-01 into feature/IO-2403-Paginated-Active-Jobs 2023-11-29 18:40:47 +00:00
Allan Carr
b7c0fba48b Merged in feature/IO-2483-Translation-Update (pull request #1084)
IO-2483 Update Transations

Approved-by: Dave Richer
2023-11-29 18:40:01 +00:00
Allan Carr
68635b1629 Merged in feature/IO-2465-Add-Vehicle-to-Override-Headers (pull request #1083)
IO-2465 Restrict Update of Vehicle on Supplement to Override Header

Approved-by: Dave Richer
2023-11-29 18:39:42 +00:00
Allan Carr
3bd0058dc8 Merged in feature/IO-2481-Parts-Queue-Query (pull request #1082)
Feature/IO-2481 Parts Queue Query

Approved-by: Dave Richer
2023-11-29 18:39:24 +00:00
Allan Carr
270a512585 Merged in feature/IO-2480-Unqueue-Parts-Queue-Label (pull request #1081)
IO-2480 Unqueue Parts Label instead of Remove

Approved-by: Dave Richer
2023-11-29 18:38:49 +00:00
Dave Richer
ec2519eae4 Move PageSize (PageLimit) to an external configuration file. 2023-11-29 13:37:02 -05:00
Dave Richer
d7f52d864a Add Global search to Active Jobs. 2023-11-28 16:05:23 -05:00
Allan Carr
5cb2b3940e IO-2483 Update Transations 2023-11-28 10:08:33 -08:00
Dave Richer
b5efaa944d Merge branch 'master' into feature/IO-2403-Paginated-Active-Jobs 2023-11-28 12:55:00 -05:00
Allan Carr
bbcfc420d2 IO-2465 Restrict Update of Vehicle on Supplement to Override Header 2023-11-27 14:34:36 -08:00
Allan Carr
8ebf7baa71 IO-2481 Parts Queue Query
prettyier
2023-11-27 10:16:10 -08:00
Allan Carr
742d2b5ff2 IO-2481 Parts Queue Query
Adjust query to only show converted Jobs
2023-11-27 10:13:43 -08:00
Allan Carr
f96fefbfdc IO-2480 Unqueue Parts Label instead of Remove
Remove is the uncorrect work as it doesn't actually remove just unqueue
2023-11-27 10:10:02 -08:00
Allan Carr
6570d38719 Merged in release/2023-11-24 (pull request #1080)
Release/2023 11 24
2023-11-24 21:12:02 +00:00
Dave Richer
547b58f05a Merged in release/2023-11-24 (pull request #1079)
Update translations, move configuration toggle for autopartsqueue

Approved-by: Allan Carr
2023-11-24 17:52:32 +00:00
Dave Richer
f299c685e2 Merged in feature/IO-2304-Auto-Parts-Toggle (pull request #1078)
Update translations, move configuration toggle for autopartsqueue
2023-11-24 17:46:30 +00:00
Dave Richer
59075ee610 Update translations, move configuration toggle for autopartsqueue 2023-11-24 12:43:55 -05:00
Dave Richer
432aa9f1e1 Progress 2023-11-24 12:34:17 -05:00
Dave Richer
c5bed4f36d Progress 2023-11-24 12:32:59 -05:00
Allan Carr
334306e3c9 Merged in release/2023-11-24 (pull request #1076)
IO-2438 Remove unneeded imports
2023-11-24 03:14:25 +00:00
Allan Carr
0716920dfc Merged in feature/IO-2438-All-Employee-Scoreboard-Summary (pull request #1075)
IO-2438 Remove unneeded imports
2023-11-24 03:13:54 +00:00
Allan Carr
07119e4e7e IO-2438 Remove unneeded imports 2023-11-23 19:14:00 -08:00
Allan Carr
2a7606836c Merged in release/2023-11-24 (pull request #1074)
IO-2438 Adjust Start & End dates for timetickets
2023-11-24 03:11:22 +00:00
Allan Carr
7dcdd64a17 IO-2438 Adjust Start & End dates for timetickets 2023-11-23 19:11:09 -08:00
Allan Carr
f26b045727 Merged in feature/IO-2438-All-Employee-Scoreboard-Summary (pull request #1073)
IO-2438 Adjust Start & End dates for timetickets
2023-11-24 03:10:49 +00:00
Allan Carr
79c966f9e4 Merged in release/2023-11-24 (pull request #1072)
Release/2023 11 24
2023-11-23 23:16:27 +00:00
Allan Carr
8eff1dfc4c Merged in feature/IO-2214-Lost-Sale-Date (pull request #1071)
IO-2214 Lost Sale Date
2023-11-23 23:05:12 +00:00
Dave Richer
65f960db00 Add Margin around the Messaging Icon 2023-11-23 16:58:56 -05:00
Allan Carr
74da3ec1ca IO-2214 Add new Lost Sales report to Report Center 2023-11-22 17:51:06 -08:00
Allan Carr
7e99a51495 IO-2214 Lost Sale Date
Add date_lost_sale to track date sale was lost, add in Audit Trail for both cancel and insertion of schedule, standardize date format on output.
2023-11-22 17:32:35 -08:00
Dave Richer
8d170a5bb4 Merged in feature/IO-2304-Auto-Parts-Toggle (pull request #1066)
Add toggles to two modals to allow for auto parts queue toggle

Approved-by: Patrick Fic
2023-11-22 23:02:09 +00:00
Dave Richer
8670f386dc refactors 2023-11-22 17:14:52 -05:00
Allan Carr
1f62108e57 Merged in feature/IO-2435-LAR&LAB-Production-Breakout (pull request #1067)
Feature/IO-2435 LAR&LAB Production Breakout
2023-11-22 01:12:01 +00:00
Allan Carr
8715ef4f24 IO-2435 Production Board Visual Breakout 2023-11-21 17:11:27 -08:00
Allan Carr
85b137f0d6 IO-2435 Total LAB & LAR in Production Board Header 2023-11-21 17:00:33 -08:00
Dave Richer
14ebb280a3 Add toggles to two modals to allow for auto parts queue toggle 2023-11-21 17:46:49 -05:00
Allan Carr
f1953eef29 Merged in feature/IO-2468-CC-Mileage-and-Service-KMs (pull request #1065)
IO-2468 CC Mileage and Service KMs
2023-11-21 22:01:39 +00:00
Allan Carr
55842faedd IO-2468 CC Mileage and Service KMs 2023-11-21 14:01:33 -08:00
Patrick Fic
9c50de85de Merged in feature/IO-2460-Node-18-Server (pull request #1063)
Update the node CI image
2023-11-21 20:39:47 +00:00
Patrick Fic
e6df079431 Merged in feature/IO-2460-Node-18-Server (pull request #1064)
Feature/IO-2460 Node 18 Server
2023-11-21 20:39:42 +00:00
Patrick Fic
fe55701079 Merged in release/2023-11-24 (pull request #1062)
Release/2023 11 24
2023-11-21 20:34:39 +00:00
Dave Richer
87b567e990 Merged in feature/IO-2460-Node-18-Server (pull request #1061)
Feature/IO-2460 Node 18 Server

Approved-by: Patrick Fic
2023-11-21 18:16:12 +00:00
Allan Carr
54e673176c Merged in release/2023-11-17 (pull request #1059)
IO-2470-CC-Year-b-Make-UI
2023-11-17 19:09:14 +00:00
Dave Richer
189c60a6d1 Merged in release/2023-11-17 (pull request #1056)
IO-2331 Remove required CC fields Next Service KMS and Next Service Date

Approved-by: Patrick Fic
2023-11-15 22:15:30 +00:00
Dave Richer
a718f2a012 Merged in release/2023-11-17 (pull request #1054)
Release/2023 11 17

Approved-by: Patrick Fic
2023-11-15 21:42:36 +00:00
109 changed files with 1811 additions and 998 deletions

View File

@@ -15,6 +15,7 @@ import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component"; import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
import BillMarkSelectedExported from "../payable-mark-selected-exported/payable-mark-selected-exported.component"; import BillMarkSelectedExported from "../payable-mark-selected-exported/payable-mark-selected-exported.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -210,7 +211,7 @@ export function AccountingPayablesTableComponent({
<Table <Table
loading={loading} loading={loading}
dataSource={dataSource} dataSource={dataSource}
pagination={{ position: "top", pageSize: 50 }} pagination={{ position: "top", pageSize: pageLimit }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
onChange={handleTableChange} onChange={handleTableChange}

View File

@@ -15,6 +15,7 @@ import PaymentExportButton from "../payment-export-button/payment-export-button.
import PaymentMarkSelectedExported from "../payment-mark-selected-exported/payment-mark-selected-exported.component"; import PaymentMarkSelectedExported from "../payment-mark-selected-exported/payment-mark-selected-exported.component";
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component"; import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component"; import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -209,7 +210,7 @@ export function AccountingPayablesTableComponent({
<Table <Table
loading={loading} loading={loading}
dataSource={dataSource} dataSource={dataSource}
pagination={{ position: "top", pageSize: 50 }} pagination={{ position: "top", pageSize: pageLimit }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
onChange={handleTableChange} onChange={handleTableChange}

View File

@@ -4,6 +4,7 @@ import { alphaSort } from "../../utils/sorters";
import { DateTimeFormatter } from "../../utils/DateFormatter"; import { DateTimeFormatter } from "../../utils/DateFormatter";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import AuditTrailValuesComponent from "../audit-trail-values/audit-trail-values.component"; import AuditTrailValuesComponent from "../audit-trail-values/audit-trail-values.component";
import {pageLimit} from "../../utils/config";
export default function AuditTrailListComponent({ loading, data }) { export default function AuditTrailListComponent({ loading, data }) {
const [state, setState] = useState({ const [state, setState] = useState({
@@ -74,7 +75,7 @@ export default function AuditTrailListComponent({ loading, data }) {
<Table <Table
{...formItemLayout} {...formItemLayout}
loading={loading} loading={loading}
pagination={{ position: "top", defaultPageSize: 25 }} pagination={{ position: "top", defaultPageSize: pageLimit }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={data} dataSource={data}

View File

@@ -3,6 +3,7 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { DateTimeFormatter } from "../../utils/DateFormatter"; import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import {pageLimit} from "../../utils/config";
export default function EmailAuditTrailListComponent({ loading, data }) { export default function EmailAuditTrailListComponent({ loading, data }) {
const [state, setState] = useState({ const [state, setState] = useState({
@@ -53,7 +54,7 @@ export default function EmailAuditTrailListComponent({ loading, data }) {
<Table <Table
{...formItemLayout} {...formItemLayout}
loading={loading} loading={loading}
pagination={{ position: "top", defaultPageSize: 25 }} pagination={{ position: "top", defaultPageSize: pageLimit }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={data} dataSource={data}

View File

@@ -10,7 +10,7 @@ import { createStructuredSelector } from "reselect";
import { import {
DELETE_BILL_LINE, DELETE_BILL_LINE,
INSERT_NEW_BILL_LINES, INSERT_NEW_BILL_LINES,
UPDATE_BILL_LINE UPDATE_BILL_LINE,
} from "../../graphql/bill-lines.queries"; } from "../../graphql/bill-lines.queries";
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries"; import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
import { insertAuditTrail } from "../../redux/application/application.actions"; import { insertAuditTrail } from "../../redux/application/application.actions";
@@ -20,6 +20,7 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import BillFormContainer from "../bill-form/bill-form.container"; import BillFormContainer from "../bill-form/bill-form.container";
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component"; import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
import BillPrintButton from "../bill-print-button/bill-print-button.component";
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component"; import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container"; import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container"; import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
@@ -176,7 +177,7 @@ export function BillDetailEditcontainer({
extra={ extra={
<Space> <Space>
<BillDetailEditReturn data={data} /> <BillDetailEditReturn data={data} />
<BillPrintButton billid={search.billid} />
<Popconfirm <Popconfirm
visible={visible} visible={visible}
onConfirm={() => form.submit()} onConfirm={() => form.submit()}

View File

@@ -79,6 +79,20 @@ export function BillFormComponent({
}); });
}; };
const handleFederalTaxExemptSwitchToggle = (checked) => {
// Early gate
if (!checked) return;
const values = form.getFieldsValue("billlines");
// Gate bill lines
if (!values?.billlines?.length) return;
const billlines = values.billlines.map((b) => {
b.applicable_taxes.federal = false;
return b;
});
form.setFieldsValue({ billlines });
};
useEffect(() => { useEffect(() => {
if (job) form.validateFields(["is_credit_memo"]); if (job) form.validateFields(["is_credit_memo"]);
}, [job, form]); }, [job, form]);
@@ -387,7 +401,16 @@ export function BillFormComponent({
> >
<CurrencyInput min={0} /> <CurrencyInput min={0} />
</Form.Item> </Form.Item>
<Form.Item shouldUpdate span={15}> {bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid ? (
<Form.Item
span={2}
label={t("bills.labels.federal_tax_exempt")}
name="federal_tax_exempt"
>
<Switch onChange={handleFederalTaxExemptSwitchToggle} />
</Form.Item>
) : null}
<Form.Item shouldUpdate span={13}>
{() => { {() => {
const values = form.getFieldsValue([ const values = form.getFieldsValue([
"billlines", "billlines",
@@ -405,7 +428,7 @@ export function BillFormComponent({
totals = CalculateBillTotal(values); totals = CalculateBillTotal(values);
if (!!totals) if (!!totals)
return ( return (
<div> <div align="right">
<Space wrap> <Space wrap>
<Statistic <Statistic
title={t("bills.labels.subtotal")} title={t("bills.labels.subtotal")}

View File

@@ -1,14 +1,15 @@
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons"; import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { import {
Button, Form, Button,
Form,
Input, Input,
InputNumber, InputNumber,
Select, Select,
Space, Space,
Switch, Switch,
Table, Table,
Tooltip Tooltip,
} from "antd"; } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -466,7 +467,8 @@ export function BillEnterModalLinesComponent({
return { return {
key: `${field.index}fedtax`, key: `${field.index}fedtax`,
valuePropName: "checked", valuePropName: "checked",
initialValue: true, initialValue:
form.getFieldValue("federal_tax_exempt") === true ? false : true,
name: [field.name, "applicable_taxes", "federal"], name: [field.name, "applicable_taxes", "federal"],
}; };
}, },

View File

@@ -0,0 +1,38 @@
import { Button, Space } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
export default function BillPrintButton({ billid }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const Templates = TemplateList("job_special");
const submitHandler = async () => {
setLoading(true);
try {
await GenerateDocument(
{
name: Templates.parts_invoice_label_single.key,
variables: {
id: billid,
},
},
{},
"p"
);
} catch (e) {
console.warn("Warning: Error generating a document.");
}
setLoading(false);
};
return (
<Space wrap>
<Button loading={loading} onClick={submitHandler}>
{t("bills.labels.printlabels")}
</Button>
</Space>
);
}

View File

@@ -4,6 +4,7 @@ import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component"; import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component";
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component"; import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
import ChatLabelComponent from "../chat-label/chat-label.component"; import ChatLabelComponent from "../chat-label/chat-label.component";
import ChatPrintButton from "../chat-print-button/chat-print-button.component";
import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container"; import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
export default function ChatConversationTitle({ conversation }) { export default function ChatConversationTitle({ conversation }) {
@@ -13,6 +14,7 @@ export default function ChatConversationTitle({ conversation }) {
{conversation && conversation.phone_num} {conversation && conversation.phone_num}
</PhoneNumberFormatter> </PhoneNumberFormatter>
<ChatLabelComponent conversation={conversation} /> <ChatLabelComponent conversation={conversation} />
<ChatPrintButton conversation={conversation} />
<ChatConversationTitleTags <ChatConversationTitleTags
jobConversations={ jobConversations={
(conversation && conversation.job_conversations) || [] (conversation && conversation.job_conversations) || []

View File

@@ -132,7 +132,7 @@ export function ChatPopupComponent({
onClick={() => toggleChatVisible()} onClick={() => toggleChatVisible()}
style={{ cursor: "pointer" }} style={{ cursor: "pointer" }}
> >
<MessageOutlined /> <MessageOutlined className="chat-popup-info-icon" />
<strong>{t("messaging.labels.messaging")}</strong> <strong>{t("messaging.labels.messaging")}</strong>
</div> </div>
)} )}

View File

@@ -13,6 +13,9 @@
height: 100%; height: 100%;
} }
} }
.chat-popup-info-icon {
margin-right: 5px;
}
@media only screen and (min-width: 992px) { @media only screen and (min-width: 992px) {
.chat-popup { .chat-popup {

View File

@@ -0,0 +1,59 @@
import { MailOutlined, PrinterOutlined } from "@ant-design/icons";
import { Space, Spin } from "antd";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setEmailOptions } from "../../redux/email/email.actions";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
});
export function ChatPrintButton({ conversation }) {
const [loading, setLoading] = useState(false);
return (
<Space wrap>
<PrinterOutlined
onClick={() => {
setLoading(true);
GenerateDocument(
{
name: TemplateList("messaging").conversation_list.key,
variables: { id: conversation.id },
},
{
subject: TemplateList("messaging").conversation_list.subject,
},
"p",
conversation.id
);
setLoading(false);
}}
/>
<MailOutlined
onClick={() => {
setLoading(true);
GenerateDocument(
{
name: TemplateList("messaging").conversation_list.key,
variables: { id: conversation.id },
},
{
subject: TemplateList("messaging").conversation_list.subject,
},
"e",
conversation.id
);
setLoading(false);
}}
/>
{loading && <Spin />}
</Space>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatPrintButton);

View File

@@ -35,6 +35,15 @@ export default function ContractsCarsComponent({
state.sortedInfo.columnKey === "status" && state.sortedInfo.order, state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
render: (text, record) => <div>{t(record.status)}</div>, render: (text, record) => <div>{t(record.status)}</div>,
}, },
{
title: t("courtesycars.fields.readiness"),
dataIndex: "readiness",
key: "readiness",
sorter: (a, b) => alphaSort(a.readiness, b.readiness),
sortOrder:
state.sortedInfo.columnKey === "readiness" && state.sortedInfo.order,
render: (text, record) => t(record.readiness),
},
{ {
title: t("courtesycars.fields.year"), title: t("courtesycars.fields.year"),
dataIndex: "year", dataIndex: "year",

View File

@@ -4,6 +4,7 @@ import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {pageLimit} from "../../utils/config";
export default function ContractsJobsComponent({ export default function ContractsJobsComponent({
loading, loading,
@@ -175,7 +176,7 @@ export default function ContractsJobsComponent({
loading={loading} loading={loading}
pagination={{ pagination={{
position: "top", position: "top",
defaultPageSize: 10, defaultPageSize: pageLimit,
defaultCurrent: defaultCurrent, defaultCurrent: defaultCurrent,
}} }}
columns={columns} columns={columns}

View File

@@ -13,6 +13,7 @@ import moment from "moment";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -209,7 +210,7 @@ export function ContractsList({
}} }}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: total, total: total,
}} }}

View File

@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import { Link, useHistory, useLocation } from "react-router-dom"; import { Link, useHistory, useLocation } from "react-router-dom";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import {pageLimit} from "../../utils/config";
export default function CourtesyCarContractListComponent({ export default function CourtesyCarContractListComponent({
contracts, contracts,
@@ -89,7 +90,7 @@ export default function CourtesyCarContractListComponent({
scroll={{ x: true }} scroll={{ x: true }}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: totalContracts, total: totalContracts,
}} }}

View File

@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
import { CHECK_CC_FLEET_NUMBER } from "../../graphql/courtesy-car.queries"; import { CHECK_CC_FLEET_NUMBER } from "../../graphql/courtesy-car.queries";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component"; import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
import CourtesyCarReadiness from "../courtesy-car-readiness-select/courtesy-car-readiness-select.component";
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component"; import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component"; import FormDatePicker from "../form-date-picker/form-date-picker.component";
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component"; //import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
@@ -34,7 +35,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
{/* <FormFieldsChanged form={form} /> */} {/* <FormFieldsChanged form={form} /> */}
<LayoutFormRow header={t("courtesycars.labels.vehicle")}> <LayoutFormRow header={t("courtesycars.labels.vehicle")}>
<Form.Item <Form.Item
label={t("courtesycars.fields.year")} label={t("courtesycars.fields.year")}
name="year" name="year"
rules={[ rules={[
@@ -118,7 +119,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
}, },
]} ]}
> >
<InputNumber precision={0} /> <InputNumber min={0} precision={0} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("courtesycars.fields.fleetnumber")} label={t("courtesycars.fields.fleetnumber")}
@@ -214,11 +215,42 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
<CourtesyCarStatus /> <CourtesyCarStatus />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("courtesycars.fields.nextservicekm")} label={t("courtesycars.fields.readiness")}
name="nextservicekm" name="readiness"
> >
<InputNumber /> <CourtesyCarReadiness />
</Form.Item> </Form.Item>
<div>
<Form.Item
label={t("courtesycars.fields.nextservicekm")}
name="nextservicekm"
>
<InputNumber min={0} precision={0} />
</Form.Item>
<Form.Item
shouldUpdate={(p, c) =>
p.mileage !== c.mileage || p.nextservicekm !== c.nextservicekm
}
>
{() => {
const nextservicekm = form.getFieldValue("nextservicekm");
const mileageOver =
nextservicekm && nextservicekm <= form.getFieldValue("mileage");
if (mileageOver)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<span>
<WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.cardueforservice")}
</span>
<span>{`${nextservicekm} km`}</span>
</Space>
);
return <></>;
}}
</Form.Item>
</div>
<div> <div>
<Form.Item <Form.Item
label={t("courtesycars.fields.nextservicedate")} label={t("courtesycars.fields.nextservicedate")}
@@ -227,30 +259,21 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
<FormDatePicker /> <FormDatePicker />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
shouldUpdate={(p, c) => shouldUpdate={(p, c) => p.nextservicedate !== c.nextservicedate}
p.mileage !== c.mileage ||
p.nextservicedate !== c.nextservicedate ||
p.nextservicekm !== c.nextservicekm
}
> >
{() => { {() => {
const nextservicedate = form.getFieldValue("nextservicedate"); const nextservicedate = form.getFieldValue("nextservicedate");
const nextservicekm = form.getFieldValue("nextservicekm");
const mileageOver =
nextservicekm <= form.getFieldValue("mileage");
const dueForService = const dueForService =
nextservicedate && moment(nextservicedate).isBefore(moment()); nextservicedate &&
moment(nextservicedate).endOf("day").isSameOrBefore(moment());
if (mileageOver || dueForService) if (dueForService)
return ( return (
<Space direction="vertical" style={{ color: "tomato" }}> <Space direction="vertical" style={{ color: "tomato" }}>
<span> <span>
<WarningFilled style={{ marginRight: ".3rem" }} /> <WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.cardueforservice")} {t("contracts.labels.cardueforservice")}
</span> </span>
<span>{`${nextservicekm} km`}</span>
<span> <span>
<DateFormatter>{nextservicedate}</DateFormatter> <DateFormatter>{nextservicedate}</DateFormatter>
</span> </span>
@@ -282,7 +305,8 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
{() => { {() => {
const expires = form.getFieldValue("registrationexpires"); const expires = form.getFieldValue("registrationexpires");
const dateover = expires && moment(expires).isBefore(moment()); const dateover =
expires && moment(expires).endOf("day").isBefore(moment());
if (dateover) if (dateover)
return ( return (
@@ -317,7 +341,8 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
{() => { {() => {
const expires = form.getFieldValue("insuranceexpires"); const expires = form.getFieldValue("insuranceexpires");
const dateover = expires && moment(expires).isBefore(moment()); const dateover =
expires && moment(expires).endOf("day").isBefore(moment());
if (dateover) if (dateover)
return ( return (

View File

@@ -34,6 +34,32 @@ const CourtesyCarFuelComponent = (props, ref) => {
step={null} step={null}
style={{ marginLeft: "2rem", marginRight: "2rem" }} style={{ marginLeft: "2rem", marginRight: "2rem" }}
{...props} {...props}
tooltip={{
formatter: (value) => {
switch (value) {
case 0:
return t("courtesycars.labels.fuel.empty");
case 13:
return t("courtesycars.labels.fuel.18");
case 25:
return t("courtesycars.labels.fuel.14");
case 38:
return t("courtesycars.labels.fuel.38");
case 50:
return t("courtesycars.labels.fuel.12");
case 63:
return t("courtesycars.labels.fuel.58");
case 75:
return t("courtesycars.labels.fuel.34");
case 88:
return t("courtesycars.labels.fuel.78");
case 100:
return t("courtesycars.labels.fuel.full");
default:
return value;
}
},
}}
/> />
); );
}; };

View File

@@ -0,0 +1,35 @@
import { Select } from "antd";
import React, { forwardRef, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
const { Option } = Select;
const CourtesyCarReadinessComponent = ({ value, onChange }, ref) => {
const [option, setOption] = useState(value);
const { t } = useTranslation();
useEffect(() => {
if (value !== option && onChange) {
onChange(option);
}
}, [value, option, onChange]);
return (
<Select
allowClear
ref={ref}
value={option}
style={{
width: 100,
}}
onChange={setOption}
>
<Option value="courtesycars.readiness.ready">
{t("courtesycars.readiness.ready")}
</Option>
<Option value="courtesycars.readiness.notready">
{t("courtesycars.readiness.notready")}
</Option>
</Select>
);
};
export default forwardRef(CourtesyCarReadinessComponent);

View File

@@ -91,6 +91,26 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
); );
}, },
}, },
{
title: t("courtesycars.fields.readiness"),
dataIndex: "readiness",
key: "readiness",
sorter: (a, b) => alphaSort(a.readiness, b.readiness),
filters: [
{
text: t("courtesycars.readiness.ready"),
value: "courtesycars.readiness.ready",
},
{
text: t("courtesycars.readiness.notready"),
value: "courtesycars.readiness.notready",
},
],
onFilter: (value, record) => value.includes(record.readiness),
sortOrder:
state.sortedInfo.columnKey === "readiness" && state.sortedInfo.order,
render: (text, record) => t(record.readiness),
},
{ {
title: t("courtesycars.fields.year"), title: t("courtesycars.fields.year"),
dataIndex: "year", dataIndex: "year",
@@ -131,6 +151,36 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
sortOrder: sortOrder:
state.sortedInfo.columnKey === "plate" && state.sortedInfo.order, state.sortedInfo.columnKey === "plate" && state.sortedInfo.order,
}, },
{
title: t("courtesycars.fields.fuel"),
dataIndex: "fuel",
key: "fuel",
sorter: (a, b) => alphaSort(a.fuel, b.fuel),
sortOrder:
state.sortedInfo.columnKey === "fuel" && state.sortedInfo.order,
render: (text, record) => {
switch (record.fuel) {
case 100:
return t("courtesycars.labels.fuel.full");
case 88:
return t("courtesycars.labels.fuel.78");
case 63:
return t("courtesycars.labels.fuel.58");
case 50:
return t("courtesycars.labels.fuel.12");
case 38:
return t("courtesycars.labels.fuel.34");
case 25:
return t("courtesycars.labels.fuel.14");
case 13:
return t("courtesycars.labels.fuel.18");
case 0:
return t("courtesycars.labels.fuel.empty");
default:
return record.fuel;
}
},
},
{ {
title: t("courtesycars.labels.outwith"), title: t("courtesycars.labels.outwith"),
dataIndex: "outwith", dataIndex: "outwith",

View File

@@ -7,6 +7,7 @@ import { Link, useHistory, useLocation } from "react-router-dom";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {pageLimit} from "../../utils/config";
export default function CsiResponseListPaginated({ export default function CsiResponseListPaginated({
refetch, refetch,
@@ -106,7 +107,7 @@ export default function CsiResponseListPaginated({
loading={loading} loading={loading}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: total, total: total,
}} }}

View File

@@ -6,6 +6,7 @@ import { alphaSort } from "../../../utils/sorters";
import LoadingSkeleton from "../../loading-skeleton/loading-skeleton.component"; import LoadingSkeleton from "../../loading-skeleton/loading-skeleton.component";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import DashboardRefreshRequired from "../refresh-required.component"; import DashboardRefreshRequired from "../refresh-required.component";
import {pageLimit} from "../../../utils/config";
export default function DashboardMonthlyJobCosting({ data, ...cardProps }) { export default function DashboardMonthlyJobCosting({ data, ...cardProps }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -118,7 +119,7 @@ export default function DashboardMonthlyJobCosting({ data, ...cardProps }) {
<div style={{ height: "100%" }}> <div style={{ height: "100%" }}>
<Table <Table
onChange={handleTableChange} onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: 50 }} pagination={{ position: "top", defaultPageSize: pageLimit }}
columns={columns} columns={columns}
scroll={{ x: true, y: "calc(100% - 4em)" }} scroll={{ x: true, y: "calc(100% - 4em)" }}
rowKey="id" rowKey="id"

View File

@@ -11,6 +11,7 @@ import { Link } from "react-router-dom";
import ChatOpenButton from "../../chat-open-button/chat-open-button.component"; import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
import DashboardRefreshRequired from "../refresh-required.component"; import DashboardRefreshRequired from "../refresh-required.component";
import {pageLimit} from "../../../utils/config";
export default function DashboardScheduledInToday({ data, ...cardProps }) { export default function DashboardScheduledInToday({ data, ...cardProps }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -195,7 +196,7 @@ export default function DashboardScheduledInToday({ data, ...cardProps }) {
<div style={{ height: "100%" }}> <div style={{ height: "100%" }}>
<Table <Table
onChange={handleTableChange} onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: 50 }} pagination={{ position: "top", defaultPageSize: pageLimit }}
columns={columns} columns={columns}
scroll={{ x: true, y: "calc(100% - 2em)" }} scroll={{ x: true, y: "calc(100% - 2em)" }}
rowKey="id" rowKey="id"

View File

@@ -11,6 +11,7 @@ import { Link } from "react-router-dom";
import ChatOpenButton from "../../chat-open-button/chat-open-button.component"; import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
import DashboardRefreshRequired from "../refresh-required.component"; import DashboardRefreshRequired from "../refresh-required.component";
import {pageLimit} from "../../../utils/config";
export default function DashboardScheduledOutToday({ data, ...cardProps }) { export default function DashboardScheduledOutToday({ data, ...cardProps }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -165,7 +166,7 @@ export default function DashboardScheduledOutToday({ data, ...cardProps }) {
<div style={{ height: "100%" }}> <div style={{ height: "100%" }}>
<Table <Table
onChange={handleTableChange} onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: 50 }} pagination={{ position: "top", defaultPageSize: pageLimit }}
columns={columns} columns={columns}
scroll={{ x: true, y: "calc(100% - 2em)" }} scroll={{ x: true, y: "calc(100% - 2em)" }}
rowKey="id" rowKey="id"

View File

@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
@@ -117,7 +118,7 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) {
} }
> >
<Table <Table
pagination={{ position: "top", defaultPageSize: 50 }} pagination={{ position: "top", defaultPageSize: pageLimit }}
columns={columns} columns={columns}
rowKey={(record) => `${record.InvoiceNumber}${record.Account}`} rowKey={(record) => `${record.InvoiceNumber}${record.Account}`}
dataSource={allocationsSummary} dataSource={allocationsSummary}

View File

@@ -6,6 +6,7 @@ import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
@@ -94,7 +95,7 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
<Alert type="warning" message={t("jobs.labels.dms.disablebillwip")} /> <Alert type="warning" message={t("jobs.labels.dms.disablebillwip")} />
)} )}
<Table <Table
pagination={{ position: "top", defaultPageSize: 50 }} pagination={{ position: "top", defaultPageSize: pageLimit }}
columns={columns} columns={columns}
rowKey="center" rowKey="center"
dataSource={allocationsSummary} dataSource={allocationsSummary}

View File

@@ -65,8 +65,17 @@ export function FormDatePicker({
}); });
} }
if (_a.isValid() && onChange) if (_a.isValid() && onChange) {
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a); if (onlyFuture) {
if (moment().subtract(1, "day").isBefore(_a)) {
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
} else {
onChange(isDateOnly ? moment().format("YYYY-MM-DD") : moment());
}
} else {
onChange(isDateOnly ? _a.format("YYYY-MM-DD") : _a);
}
}
}; };
return ( return (

View File

@@ -1,9 +1,9 @@
import React, { forwardRef } from "react"; import React, { forwardRef } from "react";
//import DatePicker from "react-datepicker"; //import DatePicker from "react-datepicker";
//import "react-datepicker/src/stylesheets/datepicker.scss"; //import "react-datepicker/src/stylesheets/datepicker.scss";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import { TimePicker } from "antd"; import { TimePicker } from "antd";
import moment from "moment"; import moment from "moment";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
//To be used as a form element only. //To be used as a form element only.
const DateTimePicker = ( const DateTimePicker = (
@@ -26,20 +26,21 @@ const DateTimePicker = (
value={value} value={value}
onBlur={onBlur} onBlur={onBlur}
onChange={onChange} onChange={onChange}
onlyFuture={onlyFuture}
isDateOnly={false} isDateOnly={false}
/> />
<TimePicker <TimePicker
value={value ? moment(value) : null} value={value ? moment(value) : null}
{...(onlyFuture && { {...(onlyFuture && {
disabledDate: (d) => moment().isAfter(d), disabledDate: (d) => moment().isAfter(d),
})} })}
onChange={onChange} onChange={onChange}
showSecond={false} showSecond={false}
minuteStep={15} minuteStep={15}
onBlur={onBlur} onBlur={onBlur}
format="hh:mm a" format="hh:mm a"
{...restProps} {...restProps}
/> />
</div> </div>
); );

View File

@@ -11,6 +11,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import InventoryBillRo from "../inventory-bill-ro/inventory-bill-ro.component"; import InventoryBillRo from "../inventory-bill-ro/inventory-bill-ro.component";
import InventoryLineDelete from "../inventory-line-delete/inventory-line-delete.component"; import InventoryLineDelete from "../inventory-line-delete/inventory-line-delete.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -213,7 +214,7 @@ export function JobsList({
loading={loading} loading={loading}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: total, total: total,
}} }}

View File

@@ -11,6 +11,7 @@ import {
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import InventoryListPaginated from "./inventory-list.component"; import InventoryListPaginated from "./inventory-list.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//bodyshop: selectBodyshop, //bodyshop: selectBodyshop,
@@ -32,8 +33,8 @@ export function InventoryList({ setBreadcrumbs, setSelectedHeader }) {
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
search: search || "", search: search || "",
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
consumedIsNull: showall === "true" ? null : true, consumedIsNull: showall === "true" ? null : true,
order: [ order: [
{ {

View File

@@ -2,12 +2,16 @@ import { useMutation } from "@apollo/client";
import { notification } from "antd"; import { notification } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { CANCEL_APPOINTMENT_BY_ID } from "../../graphql/appointments.queries"; import { CANCEL_APPOINTMENT_BY_ID } from "../../graphql/appointments.queries";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import ScheduleEventComponent from "./schedule-event.component"; import ScheduleEventComponent from "./schedule-event.component";
export default function ScheduleEventContainer({ bodyshop, event, refetch }) { export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
const dispatch = useDispatch();
const { t } = useTranslation(); const { t } = useTranslation();
const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID); const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID);
const [updateJob] = useMutation(UPDATE_JOB); const [updateJob] = useMutation(UPDATE_JOB);
@@ -34,16 +38,24 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
const jobUpdate = await updateJob({ const jobUpdate = await updateJob({
variables: { variables: {
jobId: event.job.id, jobId: event.job.id,
job: { job: {
date_scheduled: null, date_scheduled: null,
scheduled_in: null, scheduled_in: null,
scheduled_completion: null, scheduled_completion: null,
lost_sale_reason, lost_sale_reason,
date_lost_sale: new Date(),
status: bodyshop.md_ro_statuses.default_imported, status: bodyshop.md_ro_statuses.default_imported,
}, },
}, },
}); });
if (!jobUpdate.errors) {
dispatch(
insertAuditTrail({
jobid: event.job.id,
operation: AuditTrailMapping.appointmentcancel(lost_sale_reason),
})
);
}
if (!!jobUpdate.errors) { if (!!jobUpdate.errors) {
notification["error"]({ notification["error"]({
message: t("jobs.errors.updating", { message: t("jobs.errors.updating", {

View File

@@ -3,6 +3,7 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import Dinero from "dinero.js"; import Dinero from "dinero.js";
import {pageLimit} from "../../utils/config";
export default function JobCostingPartsTable({ data, summaryData }) { export default function JobCostingPartsTable({ data, summaryData }) {
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
const [state, setState] = useState({ const [state, setState] = useState({
@@ -98,7 +99,7 @@ export default function JobCostingPartsTable({ data, summaryData }) {
x: "50%", //y: "40rem" x: "50%", //y: "40rem"
}} }}
onChange={handleTableChange} onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: 50 }} pagination={{ position: "top", defaultPageSize: pageLimit }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={filteredData} dataSource={filteredData}

View File

@@ -13,6 +13,7 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { insertAuditTrail } from "../../redux/application/application.actions"; import { insertAuditTrail } from "../../redux/application/application.actions";
import { DateTimeFormat } from "./../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
@@ -53,7 +54,7 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
operation: AuditTrailMapping.admin_jobfieldchange( operation: AuditTrailMapping.admin_jobfieldchange(
key, key,
changedAuditFields[key] instanceof moment changedAuditFields[key] instanceof moment
? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a") ? DateTimeFormat(changedAuditFields[key])
: changedAuditFields[key] : changedAuditFields[key]
), ),
}); });
@@ -179,6 +180,12 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
<Form.Item label={t("jobs.fields.date_void")} name="date_void"> <Form.Item label={t("jobs.fields.date_void")} name="date_void">
<DateTimePicker /> <DateTimePicker />
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.date_lost_sale")}
name="date_lost_sale"
>
<DateTimePicker />
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
</Form> </Form>

View File

@@ -1,6 +1,6 @@
import { GET_ALL_JOBLINES_BY_PK } from "../../graphql/jobs-lines.queries";
import { gql } from "@apollo/client"; import { gql } from "@apollo/client";
import _ from "lodash"; import _ from "lodash";
import { GET_ALL_JOBLINES_BY_PK } from "../../graphql/jobs-lines.queries";
export const GetSupplementDelta = async (client, jobId, newLines) => { export const GetSupplementDelta = async (client, jobId, newLines) => {
const { const {
@@ -50,7 +50,7 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
.reduce((acc, value, idx) => { .reduce((acc, value, idx) => {
return acc + generateRemoveQuery(value, idx); return acc + generateRemoveQuery(value, idx);
}, ""); }, "");
console.log(insertQueries, updateQueries, removeQueries); //console.log(insertQueries, updateQueries, removeQueries);
if ((insertQueries + updateQueries + removeQueries).trim() === "") { if ((insertQueries + updateQueries + removeQueries).trim() === "") {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@@ -114,7 +114,7 @@ const headerFields = [
"ins_ct_ph", "ins_ct_ph",
"ins_ct_phx", "ins_ct_phx",
"loss_cat", "loss_cat",
//ad2 //AD2
"clmt_ln", "clmt_ln",
"clmt_fn", "clmt_fn",
"clmt_title", "clmt_title",
@@ -219,7 +219,16 @@ const headerFields = [
"loc_title", "loc_title",
"loc_ph", "loc_ph",
"loc_phx", "loc_phx",
"loc_ea" "loc_ea",
//VEH
"plate_no",
"plate_st",
"v_vin",
"v_model_yr",
"v_make_desc",
"v_model_desc",
"v_options",
"v_color",
]; ];
export default headerFields; export default headerFields;

View File

@@ -73,6 +73,8 @@ export function JobsAvailableContainer({
const [selectedJob, setSelectedJob] = useState(null); const [selectedJob, setSelectedJob] = useState(null);
const [selectedOwner, setSelectedOwner] = useState(null); const [selectedOwner, setSelectedOwner] = useState(null);
const [partsQueueToggle, setPartsQueueToggle] = useState(bodyshop.md_functionality_toggles.parts_queue_toggle);
const [insertLoading, setInsertLoading] = useState(false); const [insertLoading, setInsertLoading] = useState(false);
const [insertNote] = useMutation(INSERT_NEW_NOTE); const [insertNote] = useMutation(INSERT_NEW_NOTE);
@@ -94,6 +96,7 @@ export function JobsAvailableContainer({
logImEXEvent("job_import_new"); logImEXEvent("job_import_new");
setOwnerModalVisible(false); setOwnerModalVisible(false);
setInsertLoading(true); setInsertLoading(true);
const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk); const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
@@ -120,7 +123,7 @@ export function JobsAvailableContainer({
let existingVehicles; let existingVehicles;
if (estData.est_data.v_vin) { if (estData.est_data.v_vin) {
//There's vehicle data, need to double check the VIN. //There's vehicle data, need to double-check the VIN.
existingVehicles = await client.query({ existingVehicles = await client.query({
query: SEARCH_VEHICLE_BY_VIN, query: SEARCH_VEHICLE_BY_VIN,
variables: { variables: {
@@ -143,7 +146,7 @@ export function JobsAvailableContainer({
text: t("jobs.labels.importnote"), text: t("jobs.labels.importnote"),
}, },
}, },
queued_for_parts: true, queued_for_parts: partsQueueToggle,
...(existingVehicles && existingVehicles.data.vehicles.length > 0 ...(existingVehicles && existingVehicles.data.vehicles.length > 0
? { vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null } ? { vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null }
: {}), : {}),
@@ -157,46 +160,51 @@ export function JobsAvailableContainer({
delete newJob.vehicle; delete newJob.vehicle;
} }
insertNewJob({ try {
variables: { const r = await insertNewJob({
job: newJob, variables: {
}, job: newJob,
}) },
.then((r) => { });
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
}
notification["success"]({
message: t("jobs.successes.created"),
onClick: () => {
history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
},
});
//Job has been inserted. Clean up the available jobs record.
insertAuditTrail({ if (CriticalPartsScanning.treatment === "on") {
jobid: r.data.insert_jobs.returning[0].id, CriticalPartsScan(r.data.insert_jobs.returning[0].id);
operation: AuditTrailMapping.jobimported(), }
});
deleteJob({ notification["success"]({
variables: { id: estData.id }, message: t("jobs.successes.created"),
}).then((r) => { onClick: () => {
refetch(); history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
setInsertLoading(false); },
}); });
}) //Job has been inserted. Clean up the available jobs record.
.catch((r) => {
//error while inserting insertAuditTrail({
notification["error"]({ jobid: r.data.insert_jobs.returning[0].id,
message: t("jobs.errors.creating", { error: r.message }), operation: AuditTrailMapping.jobimported(),
}); });
deleteJob({
variables: { id: estData.id },
}).then((r) => {
refetch(); refetch();
setInsertLoading(false); setInsertLoading(false);
}); });
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
} catch (err) {
//error while inserting
notification["error"]({
message: t("jobs.errors.creating", { error: err.message }),
});
refetch().catch(e => {console.error(`Something went wrong in jobs available table container - ${err.message || ''}`)});
setInsertLoading(false);
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
}
}; };
//Suplement scenario //Supplement scenario
const onJobFindModalOk = async () => { const onJobFindModalOk = async () => {
logImEXEvent("job_import_supplement"); logImEXEvent("job_import_supplement");
@@ -248,11 +256,14 @@ export function JobsAvailableContainer({
// "0.00" // "0.00"
// ), // ),
// job_totals: newTotals, // job_totals: newTotals,
// queued_for_parts: true, queued_for_parts: partsQueueToggle,
}, },
}, },
}); });
if (CriticalPartsScanning.treatment === "on") {
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id); CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
} }
if (updateResult.errors) { if (updateResult.errors) {
@@ -327,12 +338,14 @@ export function JobsAvailableContainer({
const onOwnerModalCancel = () => { const onOwnerModalCancel = () => {
setOwnerModalVisible(false); setOwnerModalVisible(false);
setSelectedOwner(null); setSelectedOwner(null);
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
}; };
const onJobModalCancel = () => { const onJobModalCancel = () => {
setJobModalVisible(false); setJobModalVisible(false);
modalSearchState[1](""); modalSearchState[1]("");
setSelectedJob(null); setSelectedJob(null);
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
}; };
const addJobAsNew = (record) => { const addJobAsNew = (record) => {
@@ -353,6 +366,8 @@ export function JobsAvailableContainer({
}, [addJobAsSupp, availableJobId, clm_no]); }, [addJobAsSupp, availableJobId, clm_no]);
if (error) return <AlertComponent type="error" message={error.message} />; if (error) return <AlertComponent type="error" message={error.message} />;
return ( return (
<LoadingSpinner <LoadingSpinner
loading={insertLoading} loading={insertLoading}
@@ -362,11 +377,14 @@ export function JobsAvailableContainer({
loading={estDataRaw.loading} loading={estDataRaw.loading}
error={estDataRaw.error} error={estDataRaw.error}
owner={owner} owner={owner}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
selectedOwner={selectedOwner} selectedOwner={selectedOwner}
setSelectedOwner={setSelectedOwner} setSelectedOwner={setSelectedOwner}
visible={ownerModalVisible} visible={ownerModalVisible}
onOk={onOwnerFindModalOk} onOk={onOwnerFindModalOk}
onCancel={onOwnerModalCancel} onCancel={onOwnerModalCancel}
/> />
<JobsFindModalContainer <JobsFindModalContainer
loading={estDataRaw.loading} loading={estDataRaw.loading}
@@ -378,6 +396,8 @@ export function JobsAvailableContainer({
onOk={onJobFindModalOk} onOk={onJobFindModalOk}
onCancel={onJobModalCancel} onCancel={onJobModalCancel}
modalSearchState={modalSearchState} modalSearchState={modalSearchState}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
/> />
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={24}> <Col span={24}>

View File

@@ -145,6 +145,13 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
<Form.Item label={t("jobs.fields.date_void")} name="date_void"> <Form.Item label={t("jobs.fields.date_void")} name="date_void">
<DateTimePicker disabled={true || jobRO} /> <DateTimePicker disabled={true || jobRO} />
</Form.Item> </Form.Item>
<Form.Item
label={t("jobs.fields.date_lost_sale")}
name="date_lost_sale"
>
<DateTimePicker disabled={true || jobRO} />
</Form.Item>
</FormRow> </FormRow>
</div> </div>
); );

View File

@@ -18,12 +18,14 @@ import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { CANCEL_APPOINTMENTS_BY_JOB_ID } from "../../graphql/appointments.queries"; import { CANCEL_APPOINTMENTS_BY_JOB_ID } from "../../graphql/appointments.queries";
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries"; import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component"; import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent"; import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent";
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util"; import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
@@ -50,6 +52,8 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setModalContext({ context: context, modal: "timeTicket" })), dispatch(setModalContext({ context: context, modal: "timeTicket" })),
setCardPaymentContext: (context) => setCardPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "cardPayment" })), dispatch(setModalContext({ context: context, modal: "cardPayment" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
}); });
export function JobsDetailHeaderActions({ export function JobsDetailHeaderActions({
@@ -64,6 +68,7 @@ export function JobsDetailHeaderActions({
jobRO, jobRO,
setTimeTicketContext, setTimeTicketContext,
setCardPaymentContext, setCardPaymentContext,
insertAuditTrail,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const client = useApolloClient(); const client = useApolloClient();
@@ -103,6 +108,14 @@ export function JobsDetailHeaderActions({
}, },
}, },
}); });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.alertToggle(
!!job.production_vars && !!job.production_vars.alert
? !job.production_vars.alert
: true
),
});
}; };
const handleSuspend = (e) => { const handleSuspend = (e) => {
@@ -158,6 +171,7 @@ export function JobsDetailHeaderActions({
scheduled_in: null, scheduled_in: null,
scheduled_completion: null, scheduled_completion: null,
lost_sale_reason, lost_sale_reason,
date_lost_sale: new Date(),
status: bodyshop.md_ro_statuses.default_imported, status: bodyshop.md_ro_statuses.default_imported,
}, },
}, },
@@ -166,6 +180,11 @@ export function JobsDetailHeaderActions({
notification["success"]({ notification["success"]({
message: t("appointments.successes.canceled"), message: t("appointments.successes.canceled"),
}); });
insertAuditTrail({
jobid: job.id,
operation:
AuditTrailMapping.appointmentcancel(lost_sale_reason),
});
return; return;
} }
}} }}

View File

@@ -1,8 +1,8 @@
import { import {
BranchesOutlined,
ExclamationCircleFilled, ExclamationCircleFilled,
PauseCircleOutlined, PauseCircleOutlined,
WarningFilled, WarningFilled,
BranchesOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Card, Col, Row, Space, Tag, Tooltip } from "antd"; import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
import React, { useState } from "react"; import React, { useState } from "react";
@@ -222,6 +222,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
{`${job.v_vin || t("general.labels.na")}`} {`${job.v_vin || t("general.labels.na")}`}
</VehicleVinDisplay> </VehicleVinDisplay>
</DataLabel> </DataLabel>
<DataLabel label={t("jobs.fields.regie_number")}>
{job.regie_number || t("general.labels.na")}
</DataLabel>
<DataLabel label={t("jobs.labels.relatedros")}> <DataLabel label={t("jobs.labels.relatedros")}>
<JobsRelatedRos jobid={job.id} job={job} /> <JobsRelatedRos jobid={job.id} job={job} />
</DataLabel> </DataLabel>

View File

@@ -14,6 +14,8 @@ export default function JobsFindModalComponent({
importOptionsState, importOptionsState,
modalSearchState, modalSearchState,
jobsListRefetch, jobsListRefetch,
partsQueueToggle,
setPartsQueueToggle,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [modalSearch, setModalSearch] = modalSearchState; const [modalSearch, setModalSearch] = modalSearchState;
@@ -199,6 +201,12 @@ export default function JobsFindModalComponent({
> >
{t("jobs.labels.override_header")} {t("jobs.labels.override_header")}
</Checkbox> </Checkbox>
<Checkbox
checked={partsQueueToggle}
onChange={(e) => setPartsQueueToggle(e.target.checked)}
>
{t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
</Checkbox>
</div> </div>
); );
} }

View File

@@ -24,6 +24,8 @@ export default connect(
setSelectedJob, setSelectedJob,
importOptionsState, importOptionsState,
modalSearchState, modalSearchState,
partsQueueToggle,
setPartsQueueToggle,
...modalProps ...modalProps
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -91,6 +93,8 @@ export default connect(
jobsListRefetch={jobsList.refetch} jobsListRefetch={jobsList.refetch}
jobsList={jobsData} jobsList={jobsData}
modalSearchState={modalSearchState} modalSearchState={modalSearchState}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
/> />
) : null} ) : null}
</Modal> </Modal>

View File

@@ -12,6 +12,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import StartChatButton from "../chat-open-button/chat-open-button.component"; import StartChatButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -33,17 +34,14 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
title: t("jobs.fields.ro_number"), title: t("jobs.fields.ro_number"),
dataIndex: "ro_number", dataIndex: "ro_number",
key: "ro_number", key: "ro_number",
sorter: true, //(a, b) => alphaSort(a.ro_number, b.ro_number), sorter: true, //(a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder, sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) => ( render: (text, record) => (
<Link to={"/manage/jobs/" + record.id}> <Link to={"/manage/jobs/" + record.id}>
{record.ro_number || t("general.labels.na")} {record.ro_number || t("general.labels.na")}
</Link> </Link>
), ),
}, },
{ {
title: t("jobs.fields.owner"), title: t("jobs.fields.owner"),
dataIndex: "ownr_ln", dataIndex: "ownr_ln",
@@ -125,7 +123,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
title: t("vehicles.fields.plate_no"), title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no", dataIndex: "plate_no",
key: "plate_no", key: "plate_no",
ellipsis: true, ellipsis: true,
sorter: true, //(a, b) => alphaSort(a.plate_no, b.plate_no), sorter: true, //(a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder: sortcolumn === "plate_no" && sortorder, sortOrder: sortcolumn === "plate_no" && sortorder,
@@ -137,7 +134,6 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
title: t("jobs.fields.clm_no"), title: t("jobs.fields.clm_no"),
dataIndex: "clm_no", dataIndex: "clm_no",
key: "clm_no", key: "clm_no",
ellipsis: true, ellipsis: true,
sorter: true, //(a, b) => alphaSort(a.clm_no, b.clm_no), sorter: true, //(a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder: sortcolumn === "clm_no" && sortorder, sortOrder: sortcolumn === "clm_no" && sortorder,
@@ -259,11 +255,11 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
pagination={ pagination={
search?.search search?.search
? { ? {
pageSize: 25, pageSize: pageLimit,
showSizeChanger: false, showSizeChanger: false,
} }
: { : {
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: total, total: total,
showSizeChanger: false, showSizeChanger: false,

View File

@@ -1,8 +1,8 @@
import { import {
SyncOutlined, SyncOutlined,
ExclamationCircleFilled, ExclamationCircleFilled,
PauseCircleOutlined, PauseCircleOutlined,
BranchesOutlined, BranchesOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { useQuery } from "@apollo/client"; import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd"; import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
@@ -22,374 +22,374 @@ import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function JobsList({ bodyshop }) { export function JobsList({ bodyshop }) {
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
const { selected } = searchParams; const { selected } = searchParams;
const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1]) .filter((screen) => !!screen[1])
.slice(-1)[0]; .slice(-1)[0];
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, { const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: { variables: {
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"], statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
}, },
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
}); });
const [state, setState] = useState({ const [state, setState] = useState({
sortedInfo: {}, sortedInfo: {},
filteredInfo: { text: "" }, filteredInfo: { text: "" },
}); });
const { t } = useTranslation(); const { t } = useTranslation();
const history = useHistory(); const history = useHistory();
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
if (error) return <AlertComponent message={error.message} type="error" />; if (error) return <AlertComponent message={error.message} type="error" />;
const jobs = data const jobs = data
? searchText === "" ? searchText === ""
? data.jobs ? data.jobs
: data.jobs.filter( : data.jobs.filter(
(j) => (j) =>
(j.ro_number || "") (j.ro_number || "")
.toString() .toString()
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.ownr_co_nm || "") (j.ownr_co_nm || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.comments || "") (j.comments || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.ownr_fn || "") (j.ownr_fn || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.ownr_ln || "") (j.ownr_ln || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) || (j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
(j.plate_no || "") (j.plate_no || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.v_model_desc || "") (j.v_model_desc || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.est_ct_fn || "") (j.est_ct_fn || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.est_ct_ln || "") (j.est_ct_ln || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) || .includes(searchText.toLowerCase()) ||
(j.v_make_desc || "") (j.v_make_desc || "")
.toLowerCase() .toLowerCase()
.includes(searchText.toLowerCase()) .includes(searchText.toLowerCase())
) )
: []; : [];
const handleTableChange = (pagination, filters, sorter) => { const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
}; };
const handleOnRowClick = (record) => { const handleOnRowClick = (record) => {
if (record) { if (record) {
if (record.id) { if (record.id) {
history.push({ history.push({
search: queryString.stringify({ search: queryString.stringify({
...searchParams, ...searchParams,
selected: record.id, selected: record.id,
}), }),
}); });
} }
} }
}; };
const columns = [ const columns = [
{ {
title: t("jobs.fields.ro_number"), title: t("jobs.fields.ro_number"),
dataIndex: "ro_number", dataIndex: "ro_number",
key: "ro_number", key: "ro_number",
sorter: (a, b) => sorter: (a, b) =>
parseInt((a.ro_number || "0").replace(/\D/g, "")) - parseInt((a.ro_number || "0").replace(/\D/g, "")) -
parseInt((b.ro_number || "0").replace(/\D/g, "")), parseInt((b.ro_number || "0").replace(/\D/g, "")),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) => ( render: (text, record) => (
<Link <Link
to={"/manage/jobs/" + record.id} to={"/manage/jobs/" + record.id}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<Space> <Space>
{record.ro_number || t("general.labels.na")} {record.ro_number || t("general.labels.na")}
{record.production_vars && record.production_vars.alert ? ( {record.production_vars && record.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" /> <ExclamationCircleFilled className="production-alert" />
) : null} ) : null}
{record.suspended && ( {record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} /> <PauseCircleOutlined style={{ color: "orangered" }} />
)} )}
{record.iouparent && ( {record.iouparent && (
<Tooltip title={t("jobs.labels.iou")}> <Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} /> <BranchesOutlined style={{ color: "orangered" }} />
</Tooltip> </Tooltip>
)} )}
</Space> </Space>
</Link> </Link>
), ),
}, },
{ {
title: t("jobs.fields.owner"), title: t("jobs.fields.owner"),
dataIndex: "owner", dataIndex: "owner",
key: "owner", key: "owner",
ellipsis: true, ellipsis: true,
responsive: ["md"], responsive: ["md"],
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln), sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder: sortOrder:
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => { render: (text, record) => {
return record.ownerid ? ( return record.ownerid ? (
<Link <Link
to={"/manage/owners/" + record.ownerid} to={"/manage/owners/" + record.ownerid}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record} />
</Link> </Link>
) : ( ) : (
<span> <span>
<OwnerNameDisplay ownerObject={record} /> <OwnerNameDisplay ownerObject={record} />
</span> </span>
); );
},
},
{
title: t("jobs.fields.ownr_ph1"),
dataIndex: "ownr_ph1",
key: "ownr_ph1",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
),
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status",
ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
filters:
(jobs &&
jobs
.map((j) => j.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<Link
to={"/manage/vehicles/" + record.vehicleid}
onClick={(e) => e.stopPropagation()}
>
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}
</Link>
) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}</span>
);
},
},
{
title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
render: (text, record) =>
`${record.clm_no || ""}${
record.po_number ? ` (PO: ${record.po_number})` : ""
}`,
},
{
title: t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
filters:
(jobs &&
jobs
.map((j) => j.ins_co_nm)
.filter(onlyUnique)
.map((s) => {
return {
text: s,
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.ins_co_nm),
responsive: ["md"],
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
responsive: ["md"],
ellipsis: true,
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
),
},
{
title: t("jobs.labels.estimator"),
dataIndex: "jobs.labels.estimator",
key: "jobs.labels.estimator",
ellipsis: true,
responsive: ["xl"],
filterSearch: true,
filters:
(jobs &&
jobs
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
.filter(onlyUnique)
.map((s) => {
return {
text: s || "N/A",
value: [s],
};
})) ||
[],
onFilter: (value, record) =>
value.includes(
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
),
render: (text, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
},
{
title: t("jobs.fields.comment"),
dataIndex: "comment",
key: "comment",
ellipsis: true,
responsive: ["md"],
},
// {
// title: t("jobs.fields.owner_owing"),
// dataIndex: "owner_owing",
// key: "owner_owing",
// responsive: ["md"],
// render: (text, record) => (
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
// ),
// },
];
const scrollMapper = {
xs: true,
sm: true,
md: true,
lg: "100%",
xl: "100%",
xxl: "100%",
};
return (
<Card
title={t("titles.bc.jobs-active")}
extra={
<Space wrap>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
setSearchText(e.target.value);
}}
value={searchText}
enterButton
/>
</Space>
}
>
<Table
loading={loading}
pagination={{ defaultPageSize: 50 }}
columns={columns}
rowKey="id"
dataSource={jobs}
scroll={{
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
}}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selected],
type: "radio",
}}
onChange={handleTableChange}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
}, },
}; },
}} {
/> title: t("jobs.fields.ownr_ph1"),
</Card> dataIndex: "ownr_ph1",
); key: "ownr_ph1",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
),
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status",
ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
filters:
(jobs &&
jobs
.map((j) => j.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<Link
to={"/manage/vehicles/" + record.vehicleid}
onClick={(e) => e.stopPropagation()}
>
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}
</Link>
) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}</span>
);
},
},
{
title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
render: (text, record) =>
`${record.clm_no || ""}${
record.po_number ? ` (PO: ${record.po_number})` : ""
}`,
},
{
title: t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
filters:
(jobs &&
jobs
.map((j) => j.ins_co_nm)
.filter(onlyUnique)
.map((s) => {
return {
text: s,
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.ins_co_nm),
responsive: ["md"],
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
responsive: ["md"],
ellipsis: true,
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
),
},
{
title: t("jobs.labels.estimator"),
dataIndex: "jobs.labels.estimator",
key: "jobs.labels.estimator",
ellipsis: true,
responsive: ["xl"],
filterSearch: true,
filters:
(jobs &&
jobs
.map((j) => `${j.est_ct_fn || ""} ${j.est_ct_ln || ""}`.trim())
.filter(onlyUnique)
.map((s) => {
return {
text: s || "N/A",
value: [s],
};
})) ||
[],
onFilter: (value, record) =>
value.includes(
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim()
),
render: (text, record) =>
`${record.est_ct_fn || ""} ${record.est_ct_ln || ""}`.trim(),
},
{
title: t("jobs.fields.comment"),
dataIndex: "comment",
key: "comment",
ellipsis: true,
responsive: ["md"],
},
// {
// title: t("jobs.fields.owner_owing"),
// dataIndex: "owner_owing",
// key: "owner_owing",
// responsive: ["md"],
// render: (text, record) => (
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
// ),
// },
];
const scrollMapper = {
xs: true,
sm: true,
md: true,
lg: "100%",
xl: "100%",
xxl: "100%",
};
return (
<Card
title={t("titles.bc.jobs-active")}
extra={
<Space wrap>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
setSearchText(e.target.value);
}}
value={searchText}
enterButton
/>
</Space>
}
>
<Table
loading={loading}
pagination={{ defaultPageSize: 50 }}
columns={columns}
rowKey="id"
dataSource={jobs}
scroll={{
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
}}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selected],
type: "radio",
}}
onChange={handleTableChange}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
},
};
}}
/>
</Card>
);
} }
export default connect(mapStateToProps, null)(JobsList); export default connect(mapStateToProps, null)(JobsList);

View File

@@ -20,6 +20,7 @@ import { alphaSort } from "../../utils/sorters";
import AlertComponent from "../alert/alert.component"; import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.component"; import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -377,7 +378,7 @@ export function JobsReadyList({ bodyshop }) {
> >
<Table <Table
loading={loading} loading={loading}
pagination={{ defaultPageSize: 50 }} pagination={{ defaultPageSize: pageLimit }}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
dataSource={jobs} dataSource={jobs}

View File

@@ -8,10 +8,11 @@ export default function OwnerFindModalComponent({
setSelectedOwner, setSelectedOwner,
ownersListLoading, ownersListLoading,
ownersList, ownersList,
partsQueueToggle,
setPartsQueueToggle,
}) { }) {
//setSelectedOwner is used to set the record id of the owner to use for adding the job. //setSelectedOwner is used to set the record id of the owner to use for adding the job.
const { t } = useTranslation(); const { t } = useTranslation();
const columns = [ const columns = [
{ {
title: t("owners.fields.ownr_ln"), title: t("owners.fields.ownr_ln"),
@@ -109,6 +110,12 @@ export default function OwnerFindModalComponent({
> >
{t("owners.labels.create_new")} {t("owners.labels.create_new")}
</Checkbox> </Checkbox>
<Checkbox
checked={partsQueueToggle}
onChange={(e) => setPartsQueueToggle(e.target.checked)}
>
{t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
</Checkbox>
</div> </div>
); );
} }

View File

@@ -14,6 +14,8 @@ export default function OwnerFindModalContainer({
owner, owner,
selectedOwner, selectedOwner,
setSelectedOwner, setSelectedOwner,
partsQueueToggle,
setPartsQueueToggle,
...modalProps ...modalProps
}) { }) {
//use owner object to run query and find what possible owners there are. //use owner object to run query and find what possible owners there are.
@@ -59,6 +61,8 @@ export default function OwnerFindModalContainer({
selectedOwner={selectedOwner} selectedOwner={selectedOwner}
setSelectedOwner={setSelectedOwner} setSelectedOwner={setSelectedOwner}
ownersListLoading={ownersList.loading} ownersListLoading={ownersList.loading}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
ownersList={ ownersList={
ownersList.data && ownersList.data.search_owners ownersList.data && ownersList.data.search_owners
? ownersList.data.search_owners ? ownersList.data.search_owners

View File

@@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next";
import { Link, useHistory, useLocation } from "react-router-dom"; import { Link, useHistory, useLocation } from "react-router-dom";
import PhoneFormatter from "../../utils/PhoneFormatter"; import PhoneFormatter from "../../utils/PhoneFormatter";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import {pageLimit} from "../../utils/config";
export default function OwnersListComponent({ export default function OwnersListComponent({
loading, loading,
@@ -122,7 +123,7 @@ export default function OwnersListComponent({
loading={loading} loading={loading}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: total, total: total,
}} }}

View File

@@ -5,6 +5,7 @@ import AlertComponent from "../alert/alert.component";
import OwnersListComponent from "./owners-list.component"; import OwnersListComponent from "./owners-list.component";
import queryString from "query-string"; import queryString from "query-string";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import {pageLimit} from "../../utils/config";
export default function OwnersListContainer() { export default function OwnersListContainer() {
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
@@ -16,8 +17,8 @@ export default function OwnersListContainer() {
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
search: search || "", search: search || "",
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
order: [ order: [
{ {
[sortcolumn || "created_at"]: sortorder [sortcolumn || "created_at"]: sortorder

View File

@@ -18,6 +18,7 @@ import { alphaSort } from "../../utils/sorters";
import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container"; import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component"; import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
@@ -282,11 +283,11 @@ export function PaymentsListPaginated({
pagination={ pagination={
search?.search search?.search
? { ? {
pageSize: 25, pageSize: pageLimit,
showSizeChanger: false, showSizeChanger: false,
} }
: { : {
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: total, total: total,
showSizeChanger: false, showSizeChanger: false,

View File

@@ -20,9 +20,9 @@ import ProductionBoardCard from "../production-board-kanban-card/production-boar
import ProductionListDetailComponent from "../production-list-detail/production-list-detail.component"; import ProductionListDetailComponent from "../production-list-detail/production-list-detail.component";
import ProductionBoardKanbanCardSettings from "./production-board-kanban.card-settings.component"; import ProductionBoardKanbanCardSettings from "./production-board-kanban.card-settings.component";
//import "@asseinfo/react-kanban/dist/styles.css"; //import "@asseinfo/react-kanban/dist/styles.css";
import CardColorLegend from "../production-board-kanban-card/production-board-kanban-card-color-legend.component";
import "./production-board-kanban.styles.scss"; import "./production-board-kanban.styles.scss";
import { createBoardData } from "./production-board-kanban.utils.js"; import { createBoardData } from "./production-board-kanban.utils.js";
import CardColorLegend from "../production-board-kanban-card/production-board-kanban-card-color-legend.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
technician: selectTechnician, technician: selectTechnician,
@@ -153,6 +153,18 @@ export function ProductionBoardKanbanComponent({
0 0
) )
.toFixed(1); .toFixed(1);
const totalLAB = data
.reduce(
(acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
const totalLAR = data
.reduce(
(acc, val) => acc + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
const selectedBreakpoint = Object.entries(Grid.useBreakpoint()) const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1]) .filter((screen) => !!screen[1])
.slice(-1)[0]; .slice(-1)[0];
@@ -236,6 +248,14 @@ export function ProductionBoardKanbanComponent({
title={t("dashboard.titles.productionhours")} title={t("dashboard.titles.productionhours")}
value={totalHrs} value={totalHrs}
/> />
<Statistic
title={t("dashboard.titles.labhours")}
value={totalLAB}
/>
<Statistic
title={t("dashboard.titles.larhours")}
value={totalLAR}
/>
<Statistic <Statistic
title={t("appointments.labels.inproduction")} title={t("appointments.labels.inproduction")}
value={data && data.length} value={data && data.length}

View File

@@ -1,12 +1,23 @@
import { ExclamationCircleFilled } from "@ant-design/icons"; import { ExclamationCircleFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Dropdown, Menu } from "antd"; import { Dropdown, Menu } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useMutation } from "@apollo/client"; import { connect } from "react-redux";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
export default function ProductionListColumnAlert({ record }) { const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function ProductionListColumnAlert({ record, insertAuditTrail }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [updateAlert] = useMutation(UPDATE_JOB); const [updateAlert] = useMutation(UPDATE_JOB);
@@ -27,6 +38,14 @@ export default function ProductionListColumnAlert({ record }) {
}, },
}, },
}, },
});
insertAuditTrail({
jobid: record.id,
operation: AuditTrailMapping.alertToggle(
!!record.production_vars && !!record.production_vars.alert
? !record.production_vars.alert
: true
),
}).then(() => { }).then(() => {
if (record.refetch) record.refetch(); if (record.refetch) record.refetch();
}); });
@@ -58,3 +77,8 @@ export default function ProductionListColumnAlert({ record }) {
</Dropdown> </Dropdown>
); );
} }
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionListColumnAlert);

View File

@@ -184,6 +184,18 @@ export function ProductionListTable({
0 0
) )
.toFixed(1); .toFixed(1);
const totalLAB = data
.reduce(
(acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
const totalLAR = data
.reduce(
(acc, val) => acc + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
return ( return (
<div> <div>
<PageHeader <PageHeader
@@ -193,6 +205,14 @@ export function ProductionListTable({
title={t("dashboard.titles.productionhours")} title={t("dashboard.titles.productionhours")}
value={totalHrs} value={totalHrs}
/> />
<Statistic
title={t("dashboard.titles.labhours")}
value={totalLAB}
/>
<Statistic
title={t("dashboard.titles.larhours")}
value={totalLAR}
/>
<Statistic <Statistic
title={t("appointments.labels.inproduction")} title={t("appointments.labels.inproduction")}
value={dataSource && dataSource.length} value={dataSource && dataSource.length}

View File

@@ -55,10 +55,11 @@ const ret = {
"shiftclock:view": 2, "shiftclock:view": 2,
"shop:config": 4, "shop:config": 4,
"shop:rbac": 5,
"shop:vendors": 2,
"shop:dashboard": 3, "shop:dashboard": 3,
"shop:rbac": 5,
"shop:reportcenter": 2,
"shop:templates": 4, "shop:templates": 4,
"shop:vendors": 2,
"temporarydocs:view": 2, "temporarydocs:view": 2,

View File

@@ -5,6 +5,7 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectReportCenter } from "../../redux/modals/modals.selectors"; import { selectReportCenter } from "../../redux/modals/modals.selectors";
import RbacWrapperComponent from "../rbac-wrapper/rbac-wrapper.component";
import ReportCenterModalComponent from "./report-center-modal.component"; import ReportCenterModalComponent from "./report-center-modal.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -33,7 +34,9 @@ export function ReportCenterModalContainer({
destroyOnClose destroyOnClose
width="80%" width="80%"
> >
<ReportCenterModalComponent /> <RbacWrapperComponent action="shop:reportcenter">
<ReportCenterModalComponent />
</RbacWrapperComponent>
</Modal> </Modal>
); );
} }

View File

@@ -13,6 +13,7 @@ import {
QUERY_APPOINTMENTS_BY_JOBID, QUERY_APPOINTMENTS_BY_JOBID,
} from "../../graphql/appointments.queries"; } from "../../graphql/appointments.queries";
import { QUERY_LBR_HRS_BY_PK, UPDATE_JOBS } from "../../graphql/jobs.queries"; import { QUERY_LBR_HRS_BY_PK, UPDATE_JOBS } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { setEmailOptions } from "../../redux/email/email.actions"; import { setEmailOptions } from "../../redux/email/email.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectSchedule } from "../../redux/modals/modals.selectors"; import { selectSchedule } from "../../redux/modals/modals.selectors";
@@ -20,6 +21,8 @@ import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser,
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { DateTimeFormat } from "../../utils/DateFormatter";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import ScheduleJobModalComponent from "./schedule-job-modal.component"; import ScheduleJobModalComponent from "./schedule-job-modal.component";
@@ -31,6 +34,8 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("schedule")), toggleModalVisible: () => dispatch(toggleModalVisible("schedule")),
setEmailOptions: (e) => dispatch(setEmailOptions(e)), setEmailOptions: (e) => dispatch(setEmailOptions(e)),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
}); });
export function ScheduleJobModalContainer({ export function ScheduleJobModalContainer({
@@ -39,6 +44,7 @@ export function ScheduleJobModalContainer({
toggleModalVisible, toggleModalVisible,
setEmailOptions, setEmailOptions,
currentUser, currentUser,
insertAuditTrail,
}) { }) {
const { visible, context, actions } = scheduleModal; const { visible, context, actions } = scheduleModal;
const { jobId, job, previousEvent } = context; const { jobId, job, previousEvent } = context;
@@ -134,6 +140,15 @@ export function ScheduleJobModalContainer({
}, },
}); });
if (!appt.errors) {
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.appointmentinsert(
DateTimeFormat(values.start)
),
});
}
if (!!appt.errors) { if (!!appt.errors) {
notification["error"]({ notification["error"]({
message: t("appointments.errors.saving", { message: t("appointments.errors.saving", {
@@ -155,6 +170,7 @@ export function ScheduleJobModalContainer({
scheduled_in: values.start, scheduled_in: values.start,
scheduled_completion: values.scheduled_completion, scheduled_completion: values.scheduled_completion,
lost_sale_reason: null, lost_sale_reason: null,
date_lost_sale: null,
}, },
}, },
}); });

View File

@@ -10,13 +10,14 @@ import OwnerNameDisplay from "../owner-name-display/owner-name-display.component
import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component"; import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component";
import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component"; import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component";
import { SyncOutlined } from "@ant-design/icons"; import { SyncOutlined } from "@ant-design/icons";
import {pageLimit} from "../../utils/config";
export default function ScoreboardJobsList({ scoreBoardlist }) { export default function ScoreboardJobsList({ scoreBoardlist }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [state, setState] = useState({ const [state, setState] = useState({
visible: false, visible: false,
search: "", search: "",
current: 1, current: 1,
pageSize: 10, pageSize: pageLimit,
}); });
const { loading, error, data, refetch } = useQuery( const { loading, error, data, refetch } = useQuery(
@@ -148,7 +149,7 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
} }
pagination={{ pagination={{
position: "top", position: "top",
pageSize: state.pageSize || 10, pageSize: state.pageSize || pageLimit,
current: state.current || 1, current: state.current || 1,
total: data ? data.scoreboard_aggregate.aggregate.count : 0, total: data ? data.scoreboard_aggregate.aggregate.count : 0,
}} }}

View File

@@ -2,11 +2,9 @@ import { useQuery } from "@apollo/client";
import { Col, Row } from "antd"; import { Col, Row } from "antd";
import _ from "lodash"; import _ from "lodash";
import moment from "moment"; import moment from "moment";
import queryString from "query-string";
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { QUERY_TIME_TICKETS_IN_RANGE_SB } from "../../graphql/timetickets.queries"; import { QUERY_TIME_TICKETS_IN_RANGE_SB } from "../../graphql/timetickets.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
@@ -31,12 +29,8 @@ export default connect(
export function ScoreboardTimeTicketsStats({ bodyshop }) { export function ScoreboardTimeTicketsStats({ bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const searchParams = queryString.parse(useLocation().search); const startDate = moment().startOf("month");
const { start, end } = searchParams; const endDate = moment().endOf("month");
const startDate = start
? moment(start)
: moment().startOf("week").subtract(7, "days");
const endDate = end ? moment(end) : moment().endOf("week");
const fixedPeriods = useMemo(() => { const fixedPeriods = useMemo(() => {
const endOfThisMonth = moment().endOf("month"); const endOfThisMonth = moment().endOf("month");
@@ -90,6 +84,8 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
end: endDate.format("YYYY-MM-DD"), end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"), fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"), fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
jobStart: startDate,
jobEnd: endDate,
}, },
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
@@ -346,11 +342,21 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
larData.push({ ...r, ...lar }); larData.push({ ...r, ...lar });
}); });
const jobData = {};
data.jobs.forEach((job) => {
job.tthrs = job.joblines.reduce((acc, val) => acc + val.mod_lb_hrs, 0);
});
jobData.tthrs = data.jobs
.reduce((acc, val) => acc + val.tthrs, 0)
.toFixed(1);
jobData.count = data.jobs.length.toFixed(0);
return { return {
fixed: ret, fixed: ret,
combinedData: combinedData, combinedData: combinedData,
labData: labData, labData: labData,
larData: larData, larData: larData,
jobData: jobData,
}; };
}, [fixedPeriods, data, bodyshop]); }, [fixedPeriods, data, bodyshop]);
@@ -362,7 +368,10 @@ export function ScoreboardTimeTicketsStats({ bodyshop }) {
<ScoreboardTimeticketsTargetsTable /> <ScoreboardTimeticketsTargetsTable />
</Col> </Col>
<Col span={24}> <Col span={24}>
<ScoreboardTicketsStats data={calculatedData.fixed} /> <ScoreboardTicketsStats
data={calculatedData.fixed}
jobData={calculatedData.jobData}
/>
</Col> </Col>
<Col span={24}> <Col span={24}>
<ScoreboardTimeTicketsChart <ScoreboardTimeTicketsChart

View File

@@ -41,7 +41,7 @@ function useLocalStorage(key, initialValue) {
return [storedValue, setStoredValue]; return [storedValue, setStoredValue];
} }
export function ScoreboardTicketsStats({ data, bodyshop }) { export function ScoreboardTicketsStats({ data, jobData, bodyshop }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [isLarge, setIsLarge] = useLocalStorage("isLargeStatistic", false); const [isLarge, setIsLarge] = useLocalStorage("isLargeStatistic", false);
@@ -408,7 +408,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
{/* Monthly Stats */} {/* Monthly Stats */}
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
{/* This Month */} {/* This Month */}
<Col span={8} align="center"> <Col span={7} align="center">
<Card size="small" title={t("scoreboard.labels.thismonth")}> <Card size="small" title={t("scoreboard.labels.thismonth")}>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={24}> <Col span={24}>
@@ -482,7 +482,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
</Card> </Card>
</Col> </Col>
{/* Last Month */} {/* Last Month */}
<Col span={8} align="center"> <Col span={7} align="center">
<Card size="small" title={t("scoreboard.labels.lastmonth")}> <Card size="small" title={t("scoreboard.labels.lastmonth")}>
<Row gutter={[16, 16]}> <Row gutter={[16, 16]}>
<Col span={24}> <Col span={24}>
@@ -556,7 +556,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
</Card> </Card>
</Col> </Col>
{/* Efficiency Over Period */} {/* Efficiency Over Period */}
<Col span={8} align="center"> <Col span={7} align="center">
<Card <Card
size="small" size="small"
title={t("scoreboard.labels.efficiencyoverperiod")} title={t("scoreboard.labels.efficiencyoverperiod")}
@@ -604,6 +604,40 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
</Row> </Row>
</Card> </Card>
</Col> </Col>
<Col span={3} align="center">
<Card
size="small"
title={t("scoreboard.labels.jobscompletednotinvoiced")}
>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
value={jobData.count}
valueStyle={{
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.totalhrs")}
</Typography.Text>
}
value={jobData.tthrs}
valueStyle={{
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
</Row> </Row>
</Space> </Space>
{/* Disclaimer */} {/* Disclaimer */}

View File

@@ -65,6 +65,8 @@ export default function ScoreboardTimeTickets() {
end: endDate.format("YYYY-MM-DD"), end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"), fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"), fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
jobStart: startDate,
jobEnd: endDate,
}, },
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",

View File

@@ -42,6 +42,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
bodyshop && bodyshop.imexshopid bodyshop && bodyshop.imexshopid
); );
return ( return (
<div> <div>
<LayoutFormRow <LayoutFormRow
@@ -680,6 +681,13 @@ export function ShopInfoGeneral({ form, bodyshop }) {
> >
<Select mode="tags" /> <Select mode="tags" />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.md_functionality_toggles.parts_queue_toggle")}
name={["md_functionality_toggles","parts_queue_toggle"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item <Form.Item
name={["last_name_first"]} name={["last_name_first"]}
label={t("bodyshop.fields.last_name_first")} label={t("bodyshop.fields.last_name_first")}

View File

@@ -28,18 +28,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
return ( return (
<RbacWrapper action="shop:rbac"> <RbacWrapper action="shop:rbac">
<LayoutFormRow> <LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.rbac.accounting.payables")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "accounting:payables"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.accounting.exportlog")} label={t("bodyshop.fields.rbac.accounting.exportlog")}
rules={[ rules={[
@@ -52,6 +40,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.accounting.payables")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "accounting:payables"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.accounting.payments")} label={t("bodyshop.fields.rbac.accounting.payments")}
rules={[ rules={[
@@ -77,26 +77,62 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.csi.page")} label={t("bodyshop.fields.rbac.bills.delete")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "csi:page"]} name={["md_rbac", "bills:delete"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.csi.export")} label={t("bodyshop.fields.rbac.bills.enter")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "csi:export"]} name={["md_rbac", "bills:enter"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.list")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:list"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.reexport")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:reexport"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:view"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -173,26 +209,38 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.list-active")} label={t("bodyshop.fields.rbac.csi.export")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "jobs:list-active"]} name={["md_rbac", "csi:export"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.list-ready")} label={t("bodyshop.fields.rbac.csi.page")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "jobs:list-ready"]} name={["md_rbac", "csi:page"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.employees.page")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "employees:page"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -208,30 +256,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.partsqueue")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:partsqueue"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-all")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-all"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.available-list")} label={t("bodyshop.fields.rbac.jobs.available-list")}
rules={[ rules={[
@@ -245,26 +269,14 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.create")} label={t("bodyshop.fields.rbac.jobs.checklist-view")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "jobs:create"]} name={["md_rbac", "jobs:checklist-view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.intake")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:intake"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -280,6 +292,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.create")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:create"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.deliver")} label={t("bodyshop.fields.rbac.jobs.deliver")}
rules={[ rules={[
@@ -305,14 +329,62 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.jobs.checklist-view")} label={t("bodyshop.fields.rbac.jobs.intake")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "jobs:checklist-view"]} name={["md_rbac", "jobs:intake"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-active")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-active"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-all")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-all"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-ready")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-ready"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.partsqueue")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:partsqueue"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -329,74 +401,14 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.bills.enter")} label={t("bodyshop.fields.rbac.owners.detail")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "bills:enter"]} name={["md_rbac", "owners:detail"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.delete")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:delete"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.reexport")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:reexport"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.bills.list")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "bills:list"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.employees.page")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "employees:page"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -412,18 +424,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.owners.detail")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "owners:detail"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.payments.enter")} label={t("bodyshop.fields.rbac.payments.enter")}
rules={[ rules={[
@@ -448,6 +448,30 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.phonebook.edit")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "phonebook:edit"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.phonebook.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "phonebook:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.production.board")} label={t("bodyshop.fields.rbac.production.board")}
rules={[ rules={[
@@ -509,38 +533,14 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.timetickets.edit")} label={t("bodyshop.fields.rbac.shop.config")}
rules={[ rules={[
{ {
required: true, required: true,
//message: t("general.validation.required"), //message: t("general.validation.required"),
}, },
]} ]}
name={["md_rbac", "timetickets:edit"]} name={["md_rbac", "shop:config"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "timetickets:shiftedit"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.shop.vendors")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "shop:vendors"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
@@ -556,18 +556,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.shop.config")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "shop:config"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.shop.rbac")} label={t("bodyshop.fields.rbac.shop.rbac")}
rules={[ rules={[
@@ -580,6 +568,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.shop.reportcenter")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "shop:reportcenter"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.shop.templates")} label={t("bodyshop.fields.rbac.shop.templates")}
rules={[ rules={[
@@ -592,6 +592,42 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.shop.vendors")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "shop:vendors"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.temporarydocs.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "temporarydocs:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.edit")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "timetickets:edit"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.timetickets.enter")} label={t("bodyshop.fields.rbac.timetickets.enter")}
rules={[ rules={[
@@ -616,6 +652,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "timetickets:shiftedit"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.users.editaccess")} label={t("bodyshop.fields.rbac.users.editaccess")}
rules={[ rules={[
@@ -628,42 +676,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.temporarydocs.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "temporarydocs:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.phonebook.view")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "phonebook:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.phonebook.edit")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "phonebook:edit"]}
>
<InputNumber />
</Form.Item>
{Simple_Inventory.treatment === "on" && ( {Simple_Inventory.treatment === "on" && (
<> <>
<Form.Item <Form.Item

View File

@@ -5,6 +5,7 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link, useHistory, useLocation } from "react-router-dom"; import { Link, useHistory, useLocation } from "react-router-dom";
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component"; import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
import {pageLimit} from "../../utils/config";
export default function VehiclesListComponent({ export default function VehiclesListComponent({
loading, loading,
vehicles, vehicles,
@@ -106,7 +107,7 @@ export default function VehiclesListComponent({
loading={loading} loading={loading}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: total, total: total,
}} }}

View File

@@ -5,6 +5,7 @@ import AlertComponent from "../alert/alert.component";
import { QUERY_ALL_VEHICLES_PAGINATED } from "../../graphql/vehicles.queries"; import { QUERY_ALL_VEHICLES_PAGINATED } from "../../graphql/vehicles.queries";
import queryString from "query-string"; import queryString from "query-string";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import {pageLimit} from "../../utils/config";
export default function VehiclesListContainer() { export default function VehiclesListContainer() {
const searchParams = queryString.parse(useLocation().search); const searchParams = queryString.parse(useLocation().search);
@@ -15,8 +16,8 @@ export default function VehiclesListContainer() {
{ {
variables: { variables: {
search: search || "", search: search || "",
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
order: [ order: [
{ {
[sortcolumn || "created_at"]: sortorder [sortcolumn || "created_at"]: sortorder

View File

@@ -271,6 +271,7 @@ export const CANCEL_APPOINTMENTS_BY_JOB_ID = gql`
scheduled_completion scheduled_completion
status status
lost_sale_reason lost_sale_reason
date_lost_sale
} }
} }
`; `;

View File

@@ -39,6 +39,7 @@ export const QUERY_BODYSHOP = gql`
logo_img_path logo_img_path
md_ro_statuses md_ro_statuses
md_order_statuses md_order_statuses
md_functionality_toggles
shopname shopname
state state
state_tax_id state_tax_id
@@ -158,6 +159,7 @@ export const UPDATE_SHOP = gql`
logo_img_path logo_img_path
md_ro_statuses md_ro_statuses
md_order_statuses md_order_statuses
md_functionality_toggles
shopname shopname
state state
state_tax_id state_tax_id

View File

@@ -30,15 +30,15 @@ export const QUERY_AVAILABLE_CC = gql`
fuel fuel
id id
make make
model
plate
status
year
dailycost
mileage mileage
model
notes notes
nextservicekm nextservicekm
nextservicedate nextservicedate
plate
readiness
status
year
} }
} }
`; `;
@@ -68,19 +68,20 @@ export const QUERY_ALL_CC = gql`
insuranceexpires insuranceexpires
leaseenddate leaseenddate
make make
mileage
model model
nextservicedate nextservicedate
nextservicekm nextservicekm
notes notes
plate plate
purchasedate purchasedate
readiness
registrationexpires registrationexpires
serviceenddate serviceenddate
servicestartdate servicestartdate
status status
vin vin
year year
mileage
cccontracts( cccontracts(
where: { status: { _eq: "contracts.status.out" } } where: { status: { _eq: "contracts.status.out" } }
order_by: { contract_date: desc } order_by: { contract_date: desc }
@@ -90,10 +91,10 @@ export const QUERY_ALL_CC = gql`
scheduledreturn scheduledreturn
job { job {
id id
ro_number
ownr_fn ownr_fn
ownr_ln ownr_ln
ownr_co_nm ownr_co_nm
ro_number
} }
} }
} }
@@ -119,19 +120,20 @@ export const QUERY_CC_BY_PK = gql`
insuranceexpires insuranceexpires
leaseenddate leaseenddate
make make
mileage
model model
nextservicedate nextservicedate
nextservicekm nextservicekm
notes notes
plate plate
purchasedate purchasedate
readiness
registrationexpires registrationexpires
serviceenddate serviceenddate
servicestartdate servicestartdate
status status
vin vin
year year
mileage
cccontracts_aggregate { cccontracts_aggregate {
aggregate { aggregate {
count(distinct: true) count(distinct: true)
@@ -139,21 +141,20 @@ export const QUERY_CC_BY_PK = gql`
} }
cccontracts(offset: $offset, limit: $limit, order_by: $order) { cccontracts(offset: $offset, limit: $limit, order_by: $order) {
agreementnumber agreementnumber
driver_fn
driver_ln
id id
status
start
scheduledreturn
kmstart kmstart
kmend kmend
driver_ln scheduledreturn
driver_fn start
status
job { job {
ro_number id
ownr_ln ownr_ln
ownr_fn ownr_fn
ownr_co_nm ownr_co_nm
id ro_number
} }
} }
} }

View File

@@ -1,5 +1,65 @@
import { gql } from "@apollo/client"; import { gql } from "@apollo/client";
export const QUERY_ALL_ACTIVE_JOBS_PAGINATED = gql`
query QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED(
$offset: Int
$limit: Int
$order: [jobs_order_by!]
$statuses: [String!]!,
$isConverted: Boolean
) {
jobs(
offset: $offset
limit: $limit
where: { status: { _in: $statuses }, converted: { _eq: $isConverted } }
order_by: $order
) {
iouparent
ownr_fn
ownr_ln
ownr_co_nm
ownr_ph1
ownr_ph2
ownr_ea
ownerid
comment
plate_no
plate_st
v_vin
v_model_yr
v_model_desc
v_make_desc
v_color
vehicleid
actual_completion
actual_delivery
actual_in
production_vars
id
ins_co_nm
clm_no
po_number
clm_total
owner_owing
ro_number
scheduled_completion
scheduled_in
scheduled_delivery
status
updated_at
ded_amt
suspended
est_ct_fn
est_ct_ln
}
jobs_aggregate(where: { status: { _in: $statuses } }) {
aggregate {
count(distinct: true)
}
}
}
`;
export const QUERY_ALL_ACTIVE_JOBS = gql` export const QUERY_ALL_ACTIVE_JOBS = gql`
query QUERY_ALL_ACTIVE_JOBS($statuses: [String!]!, $isConverted: Boolean) { query QUERY_ALL_ACTIVE_JOBS($statuses: [String!]!, $isConverted: Boolean) {
jobs( jobs(
@@ -60,7 +120,7 @@ export const QUERY_PARTS_QUEUE = gql`
} }
} }
jobs( jobs(
where: { _and: [{ status: { _in: $statuses } }] } where: { _and: [{ status: { _in: $statuses }, converted: { _eq: true } }] }
offset: $offset offset: $offset
limit: $limit limit: $limit
order_by: $order order_by: $order
@@ -675,6 +735,7 @@ export const GET_JOB_BY_PK = gql`
date_scheduled date_scheduled
date_invoiced date_invoiced
date_last_contacted date_last_contacted
date_lost_sale
date_next_contact date_next_contact
date_towin date_towin
date_rentalresp date_rentalresp
@@ -1077,6 +1138,7 @@ export const UPDATE_JOB = gql`
actual_in actual_in
date_repairstarted date_repairstarted
date_void date_void
date_lost_sale
} }
} }
} }

View File

@@ -143,9 +143,14 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
$end: date! $end: date!
$fixedStart: date! $fixedStart: date!
$fixedEnd: date! $fixedEnd: date!
$jobStart: timestamptz!
$jobEnd: timestamptz!
) { ) {
timetickets( timetickets(
where: { date: { _gte: $start, _lte: $end }, cost_center: {_neq: "timetickets.labels.shift"} } where: {
date: { _gte: $start, _lte: $end }
cost_center: { _neq: "timetickets.labels.shift" }
}
order_by: { date: desc_nulls_first } order_by: { date: desc_nulls_first }
) { ) {
actualhrs actualhrs
@@ -176,7 +181,10 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
} }
} }
fixedperiod: timetickets( fixedperiod: timetickets(
where: { date: { _gte: $fixedStart, _lte: $fixedEnd }, cost_center: {_neq: "timetickets.labels.shift"} } where: {
date: { _gte: $fixedStart, _lte: $fixedEnd }
cost_center: { _neq: "timetickets.labels.shift" }
}
order_by: { date: desc_nulls_first } order_by: { date: desc_nulls_first }
) { ) {
actualhrs actualhrs
@@ -205,6 +213,25 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
last_name last_name
} }
} }
jobs(
where: {
date_invoiced: { _is_null: true }
ro_number: { _is_null: false }
voided: { _eq: false }
_or: [
{ actual_completion: { _gte: $jobStart, _lte: $jobEnd } }
{ actual_delivery: { _gte: $jobStart, _lte: $jobEnd } }
]
}
) {
id
joblines(order_by: { line_no: asc }, where: { removed: { _eq: false } }) {
convertedtolbr
convertedtolbr_data
mod_lb_hrs
mod_lbr_ty
}
}
} }
`; `;

View File

@@ -14,6 +14,7 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter"; import { DateFormatter } from "../../utils/DateFormatter";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort, dateSort } from "../../utils/sorters"; import { alphaSort, dateSort } from "../../utils/sorters";
import {pageLimit} from "../../utils/config";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) => setPartsOrderContext: (context) =>
@@ -295,11 +296,11 @@ export function BillsListPage({
pagination={ pagination={
search?.search search?.search
? { ? {
pageSize: 25, pageSize: pageLimit,
showSizeChanger: false, showSizeChanger: false,
} }
: { : {
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: total, total: total,
showSizeChanger: false, showSizeChanger: false,

View File

@@ -13,6 +13,7 @@ import {
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import BillsPageComponent from "./bills.page.component"; import BillsPageComponent from "./bills.page.component";
import {pageLimit} from "../../utils/config";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
@@ -38,8 +39,8 @@ export function BillsPageContainer({ setBreadcrumbs, setSelectedHeader }) {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
order: [ order: [
searchObj searchObj
? JSON.parse(searchObj) ? JSON.parse(searchObj)

View File

@@ -12,6 +12,7 @@ import {
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import ContractsPageComponent from "./contracts.page.component"; import ContractsPageComponent from "./contracts.page.component";
import {pageLimit} from "../../utils/config";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
@@ -29,8 +30,8 @@ export function ContractsPageContainer({ setBreadcrumbs, setSelectedHeader }) {
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
search: search || "", search: search || "",
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
order: [ order: [
{ {
[sortcolumn || "start"]: sortorder [sortcolumn || "start"]: sortorder

View File

@@ -1,11 +1,14 @@
import { useMutation, useQuery } from "@apollo/client"; import { useMutation, useQuery } from "@apollo/client";
import { Form, notification } from "antd"; import { Form, notification } from "antd";
import moment from "moment"; import moment from "moment";
import queryString from "query-string";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useParams } from "react-router-dom"; import { useLocation, useParams } from "react-router-dom";
import AlertComponent from "../../components/alert/alert.component"; import AlertComponent from "../../components/alert/alert.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import NotFound from "../../components/not-found/not-found.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries"; import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries";
import { import {
@@ -13,12 +16,10 @@ import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import { pageLimit } from "../../utils/config";
import { CreateRecentItem } from "../../utils/create-recent-item"; import { CreateRecentItem } from "../../utils/create-recent-item";
import UndefinedToNull from "./../../utils/undefinedtonull";
import CourtesyCarDetailPageComponent from "./courtesy-car-detail.page.component"; import CourtesyCarDetailPageComponent from "./courtesy-car-detail.page.component";
import NotFound from "../../components/not-found/not-found.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import queryString from "query-string";
import { useLocation } from "react-router-dom";
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)), setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
@@ -41,8 +42,8 @@ export function CourtesyCarDetailPageContainer({
const { loading, error, data } = useQuery(QUERY_CC_BY_PK, { const { loading, error, data } = useQuery(QUERY_CC_BY_PK, {
variables: { variables: {
id: ccId, id: ccId,
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
order: [ order: [
{ {
[sortcolumn || "start"]: sortorder [sortcolumn || "start"]: sortorder
@@ -111,7 +112,10 @@ export function CourtesyCarDetailPageContainer({
setSaveLoading(true); setSaveLoading(true);
const result = await updateCourtesyCar({ const result = await updateCourtesyCar({
variables: { cc: { ...values }, ccId: ccId }, variables: {
cc: { ...UndefinedToNull(values, ["readiness"]) },
ccId: ccId,
},
refetchQueries: ["QUERY_CC_BY_PK"], refetchQueries: ["QUERY_CC_BY_PK"],
awaitRefetchQueries: true, awaitRefetchQueries: true,
}); });

View File

@@ -12,6 +12,7 @@ import AlertComponent from "../../components/alert/alert.component";
import { QUERY_EXPORT_LOG_PAGINATED } from "../../graphql/accounting.queries"; import { QUERY_EXPORT_LOG_PAGINATED } from "../../graphql/accounting.queries";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { DateTimeFormatter } from "../../utils/DateFormatter"; import { DateTimeFormatter } from "../../utils/DateFormatter";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -29,8 +30,8 @@ export function ExportLogsPageComponent({ bodyshop }) {
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
search: search || "", search: search || "",
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
order: [ order: [
{ {
[sortcolumn || "created_at"]: sortorder [sortcolumn || "created_at"]: sortorder
@@ -178,7 +179,7 @@ export function ExportLogsPageComponent({ bodyshop }) {
loading={loading} loading={loading}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: data && data.search_exportlog_aggregate.aggregate.count, total: data && data.search_exportlog_aggregate.aggregate.count,
}} }}

View File

@@ -13,6 +13,7 @@ import {
setBreadcrumbs, setBreadcrumbs,
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//bodyshop: selectBodyshop, //bodyshop: selectBodyshop,
@@ -33,16 +34,16 @@ export function AllJobs({ setBreadcrumbs, setSelectedHeader }) {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
...(statusFilters ? { statusList: JSON.parse(statusFilters) } : {}), ...(statusFilters ? { statusList: JSON.parse(statusFilters) } : {}),
order: [ order: [
{ {
[sortcolumn || "ro_number"]: [sortcolumn || "ro_number"]:
sortorder && sortorder !== "false" sortorder && sortorder !== "false"
? sortorder === "descend" ? (sortorder === "descend"
? "desc" ? "desc"
: "asc" : "asc")
: "desc", : "desc",
}, },
], ],

View File

@@ -36,14 +36,22 @@ import JobsCloseLines from "../../components/jobs-close-lines/jobs-close-lines.c
import LayoutFormRow from "../../components/layout-form-row/layout-form-row.component"; import LayoutFormRow from "../../components/layout-form-row/layout-form-row.component";
import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.queries"; import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.queries";
import { UPDATE_JOB } from "../../graphql/jobs.queries"; import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
jobRO: selectJobReadOnly, jobRO: selectJobReadOnly,
}); });
export function JobsCloseComponent({ job, bodyshop, jobRO }) { const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JobsCloseComponent({ job, bodyshop, jobRO, insertAuditTrail, }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const client = useApolloClient(); const client = useApolloClient();
@@ -110,6 +118,10 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
notification["success"]({ notification["success"]({
message: t("jobs.successes.closed"), message: t("jobs.successes.closed"),
}); });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.jobinvoiced(),
});
// history.push(`/manage/jobs/${job.id}`); // history.push(`/manage/jobs/${job.id}`);
} else { } else {
setLoading(false); setLoading(false);
@@ -527,4 +539,4 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
</div> </div>
); );
} }
export default connect(mapStateToProps, null)(JobsCloseComponent); export default connect(mapStateToProps, mapDispatchToProps)(JobsCloseComponent);

View File

@@ -1,6 +1,6 @@
import _ from "lodash";
import { useLazyQuery, useMutation } from "@apollo/client"; import { useLazyQuery, useMutation } from "@apollo/client";
import { Form, notification } from "antd"; import { Form, notification } from "antd";
import _ from "lodash";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -90,6 +90,7 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
{}, {},
values, values,
{ date_open: new Date() }, { date_open: new Date() },
{ date_estimated: new Date() },
{ {
vehicle: vehicle:
state.vehicle.selectedid || state.vehicle.none state.vehicle.selectedid || state.vehicle.none

View File

@@ -3,19 +3,19 @@ import Icon, {
CalendarFilled, CalendarFilled,
DollarCircleOutlined, DollarCircleOutlined,
FileImageFilled, FileImageFilled,
PrinterFilled,
ToolFilled,
HistoryOutlined, HistoryOutlined,
PrinterFilled,
SyncOutlined, SyncOutlined,
ToolFilled,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { import {
Button, Button,
Divider, Divider,
Form, Form,
notification,
PageHeader, PageHeader,
Space, Space,
Tabs, Tabs,
notification,
} from "antd"; } from "antd";
import Axios from "axios"; import Axios from "axios";
import moment from "moment"; import moment from "moment";
@@ -27,6 +27,7 @@ import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom"; import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import FormFieldsChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.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 JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container"; import JobLineUpsertModalContainer from "../../components/job-lines-upsert-modal/job-lines-upsert-modal.container";
import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container"; import JobReconciliationModal from "../../components/job-reconciliation-modal/job-reconciliation.modal.container";
@@ -42,17 +43,17 @@ import JobsDetailPliContainer from "../../components/jobs-detail-pli/jobs-detail
import JobsDetailRates from "../../components/jobs-detail-rates/jobs-detail-rates.component"; import JobsDetailRates from "../../components/jobs-detail-rates/jobs-detail-rates.component";
import JobsDetailTotals from "../../components/jobs-detail-totals/jobs-detail-totals.component"; import JobsDetailTotals from "../../components/jobs-detail-totals/jobs-detail-totals.component";
import JobsDocumentsGalleryContainer from "../../components/jobs-documents-gallery/jobs-documents-gallery.container"; import JobsDocumentsGalleryContainer from "../../components/jobs-documents-gallery/jobs-documents-gallery.container";
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container"; 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 ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
import AuditTrailMapping from "../../utils/AuditTrailMappings"; import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { insertAuditTrail } from "../../redux/application/application.actions";
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
import UndefinedToNull from "../../utils/undefinedtonull"; import UndefinedToNull from "../../utils/undefinedtonull";
import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container"; import { DateTimeFormat } from "./../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -172,7 +173,7 @@ export function JobsDetailPage({
operation: AuditTrailMapping.jobfieldchange( operation: AuditTrailMapping.jobfieldchange(
key, key,
changedAuditFields[key] instanceof moment changedAuditFields[key] instanceof moment
? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a") ? DateTimeFormat(changedAuditFields[key])
: changedAuditFields[key] : changedAuditFields[key]
), ),
}); });

View File

@@ -18,6 +18,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter"; import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters"; import { alphaSort, dateSort } from "../../utils/sorters";
import useLocalStorage from "../../utils/useLocalStorage"; import useLocalStorage from "../../utils/useLocalStorage";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -296,7 +297,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
loading={loading} loading={loading}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 50, pageSize: pageLimit,
// current: parseInt(page || 1), // current: parseInt(page || 1),
// total: data && data.jobs_aggregate.aggregate.count, // total: data && data.jobs_aggregate.aggregate.count,
}} }}

View File

@@ -14,6 +14,7 @@ import {
setSelectedHeader, setSelectedHeader,
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -34,8 +35,8 @@ export function AllJobs({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
fetchPolicy: "network-only", fetchPolicy: "network-only",
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
order: [ order: [
searchObj searchObj
? JSON.parse(searchObj) ? JSON.parse(searchObj)

View File

@@ -17,6 +17,7 @@ import {
import ChatOpenButton from "../../components/chat-open-button/chat-open-button.component"; import ChatOpenButton from "../../components/chat-open-button/chat-open-button.component";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import { HasRbacAccess } from "../../components/rbac-wrapper/rbac-wrapper.component"; import { HasRbacAccess } from "../../components/rbac-wrapper/rbac-wrapper.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -35,8 +36,8 @@ export function PhonebookPageComponent({ bodyshop, authLevel }) {
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
search: search || "", search: search || "",
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
order: [ order: [
{ {
[sortcolumn || "lastname"]: sortorder [sortcolumn || "lastname"]: sortorder
@@ -189,7 +190,7 @@ export function PhonebookPageComponent({ bodyshop, authLevel }) {
loading={loading} loading={loading}
pagination={{ pagination={{
position: "top", position: "top",
pageSize: 25, pageSize: pageLimit,
current: parseInt(page || 1), current: parseInt(page || 1),
total: data && data.search_phonebook_aggregate.aggregate.count, total: data && data.search_phonebook_aggregate.aggregate.count,
}} }}

View File

@@ -16,6 +16,7 @@ import {
} from "../../redux/application/application.actions"; } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component"; import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import {pageLimit} from "../../utils/config";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
@@ -42,8 +43,8 @@ export function ShopCsiContainer({
nextFetchPolicy: "network-only", nextFetchPolicy: "network-only",
variables: { variables: {
//search: search || "", //search: search || "",
offset: page ? (page - 1) * 25 : 0, offset: page ? (page - 1) * pageLimit : 0,
limit: 25, limit: pageLimit,
order: [ order: [
{ {
[sortcolumn || "completedon"]: sortorder [sortcolumn || "completedon"]: sortorder

View File

@@ -56,13 +56,13 @@
"history": "History", "history": "History",
"inproduction": "Jobs In Production", "inproduction": "Jobs In Production",
"manualevent": "Add Manual Appointment", "manualevent": "Add Manual Appointment",
"noarrivingjobs": "No jobs are arriving.", "noarrivingjobs": "No Jobs are arriving.",
"nocompletingjobs": "No jobs scheduled for completion.", "nocompletingjobs": "No Jobs scheduled for completion.",
"nodateselected": "No date has been selected.", "nodateselected": "No date has been selected.",
"priorappointments": "Previous Appointments", "priorappointments": "Previous Appointments",
"reminder": "This is {{shopname}} reminding you about an appointment on {{date}} at {{time}}. Please let us know if you are not able to make the appointment. We look forward to seeing you soon. ", "reminder": "This is {{shopname}} reminding you about an appointment on {{date}} at {{time}}. Please let us know if you are not able to make the appointment. We look forward to seeing you soon. ",
"scheduledfor": "Scheduled appointment for: ", "scheduledfor": "Scheduled appointment for: ",
"severalerrorsfound": "Several jobs have issues which may prevent accurate smart scheduling. Click to expand.", "severalerrorsfound": "Several Jobs have issues which may prevent accurate smart scheduling. Click to expand.",
"smartscheduling": "Smart Scheduling", "smartscheduling": "Smart Scheduling",
"suggesteddates": "Suggested Dates" "suggesteddates": "Suggested Dates"
}, },
@@ -103,23 +103,27 @@
"admin_jobmarkforreexport": "ADMIN: Job marked for re-export.", "admin_jobmarkforreexport": "ADMIN: Job marked for re-export.",
"admin_jobuninvoice": "ADMIN: Job has been uninvoiced.", "admin_jobuninvoice": "ADMIN: Job has been uninvoiced.",
"admin_jobunvoid": "ADMIN: Job has been unvoided.", "admin_jobunvoid": "ADMIN: Job has been unvoided.",
"alerttoggle": "Alert Toggle set to {{status}}",
"appointmentcancel": "Appointment canceled. Lost Reason: {{lost_sale_reason}}.",
"appointmentinsert": "Appointment created. Appointment Date: {{start}}.",
"billposted": "Bill with invoice number {{invoice_number}} posted.", "billposted": "Bill with invoice number {{invoice_number}} posted.",
"billupdated": "Bill with invoice number {{invoice_number}} updated.", "billupdated": "Bill with invoice number {{invoice_number}} updated.",
"failedpayment": "Failed payment", "failedpayment": "Failed payment",
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}", "jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
"jobassignmentremoved": "Employee assignment removed for {{operation}}", "jobassignmentremoved": "Employee assignment removed for {{operation}}",
"jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.", "jobchecklist": "Checklist type \"{{type}}\" completed. In production set to {{inproduction}}. Status set to {{status}}.",
"jobinvoiced": "Job has been invoiced.",
"jobconverted": "Job converted and assigned number {{ro_number}}.", "jobconverted": "Job converted and assigned number {{ro_number}}.",
"jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.", "jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.",
"jobimported": "Job imported.", "jobimported": "Job imported.",
"jobinproductionchange": "Job production status set to {{inproduction}}", "jobinproductionchange": "Job production status set to {{inproduction}}",
"jobioucreated": "IOU Created.", "jobioucreated": "IOU Created.",
"jobmodifylbradj": "Labor adjustments modified {{mod_lbr_ty}} / {{hours}}.", "jobmodifylbradj": "Labor adjustments modified {{mod_lbr_ty}} / {{hours}}.",
"jobnoteadded": "Note added to job.", "jobnoteadded": "Note added to Job.",
"jobnotedeleted": "Note deleted from job.", "jobnotedeleted": "Note deleted from Job.",
"jobnoteupdated": "Note updated on job.", "jobnoteupdated": "Note updated on Job.",
"jobspartsorder": "Parts order {{order_number}} added to job.", "jobspartsorder": "Parts order {{order_number}} added to Job.",
"jobspartsreturn": "Parts return {{order_number}} added to job.", "jobspartsreturn": "Parts return {{order_number}} added to Job.",
"jobstatuschange": "Job status changed to {{status}}.", "jobstatuschange": "Job status changed to {{status}}.",
"jobsupplement": "Job supplement imported." "jobsupplement": "Job supplement imported."
} }
@@ -201,6 +205,7 @@
"entered_total": "Total of Entered Lines", "entered_total": "Total of Entered Lines",
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.", "enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
"federal_tax": "Federal Tax", "federal_tax": "Federal Tax",
"federal_tax_exempt": "Federal Tax Exempt?",
"generatepartslabel": "Generate Parts Labels after Saving?", "generatepartslabel": "Generate Parts Labels after Saving?",
"iouexists": "An IOU exists that is associated to this RO.", "iouexists": "An IOU exists that is associated to this RO.",
"local_tax": "Local Tax", "local_tax": "Local Tax",
@@ -208,7 +213,8 @@
"markforreexport": "Mark for Re-export", "markforreexport": "Mark for Re-export",
"new": "New Bill", "new": "New Bill",
"noneselected": "No bill selected.", "noneselected": "No bill selected.",
"onlycmforinvoiced": "Only credit memos can be entered for any job that has been invoiced, exported, or voided.", "onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
"printlabels": "Print Labels",
"retailtotal": "Bills Retail Total", "retailtotal": "Bills Retail Total",
"savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.", "savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.",
"state_tax": "Provincial/State Tax", "state_tax": "Provincial/State Tax",
@@ -347,6 +353,9 @@
}, },
"md_payment_types": "Payment Types", "md_payment_types": "Payment Types",
"md_referral_sources": "Referral Sources", "md_referral_sources": "Referral Sources",
"md_functionality_toggles": {
"parts_queue_toggle": "Auto Add Imported/Supplemented Jobs to Parts Queue"
},
"md_tasks_presets": { "md_tasks_presets": {
"hourstype": "", "hourstype": "",
"memo": "", "memo": "",
@@ -442,6 +451,7 @@
"config": "Shop -> Config", "config": "Shop -> Config",
"dashboard": "Shop -> Dashboard", "dashboard": "Shop -> Dashboard",
"rbac": "Shop -> RBAC", "rbac": "Shop -> RBAC",
"reportcenter": "Shop -> Report Center",
"templates": "Shop -> Templates", "templates": "Shop -> Templates",
"vendors": "Shop -> Vendors" "vendors": "Shop -> Vendors"
}, },
@@ -653,7 +663,7 @@
"printall": "Print All Documents" "printall": "Print All Documents"
}, },
"errors": { "errors": {
"complete": "Error during job checklist completion. {{error}}", "complete": "Error during Job checklist completion. {{error}}",
"nochecklist": "No checklist has been configured for your shop. " "nochecklist": "No checklist has been configured for your shop. "
}, },
"labels": { "labels": {
@@ -661,7 +671,7 @@
"allow_text_message": "Permission to Text?", "allow_text_message": "Permission to Text?",
"checklist": "Checklist", "checklist": "Checklist",
"printpack": "Job Intake Print Pack", "printpack": "Job Intake Print Pack",
"removefromproduction": "Remove job from production?" "removefromproduction": "Remove Job from Production?"
}, },
"successes": { "successes": {
"completed": "Job checklist completed." "completed": "Job checklist completed."
@@ -677,9 +687,9 @@
"senddltoform": "Insert Driver's License Information" "senddltoform": "Insert Driver's License Information"
}, },
"errors": { "errors": {
"fetchingjobinfo": "Error fetching job info. {{error}}.", "fetchingjobinfo": "Error fetching Job Info. {{error}}.",
"returning": "Error returning courtesy car. {{error}}", "returning": "Error returning Courtesy Car. {{error}}",
"saving": "Error saving contract. {{error}}", "saving": "Error saving Contract. {{error}}",
"selectjobandcar": "Please ensure both a car and job are selected." "selectjobandcar": "Please ensure both a car and job are selected."
}, },
"fields": { "fields": {
@@ -736,7 +746,7 @@
"driverinformation": "Driver's Information", "driverinformation": "Driver's Information",
"findcontract": "Find Contract", "findcontract": "Find Contract",
"findermodal": "Contract Finder", "findermodal": "Contract Finder",
"noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.", "noteconvertedfrom": "R.O. created from converted Courtesy Car Contract {{agreementnumber}}.",
"populatefromjob": "Populate from Job", "populatefromjob": "Populate from Job",
"rates": "Contract Rates", "rates": "Contract Rates",
"time": "Time", "time": "Time",
@@ -758,7 +768,7 @@
"return": "Return Car" "return": "Return Car"
}, },
"errors": { "errors": {
"saving": "Error saving courtesy card. {{error}}" "saving": "Error saving Courtesy Car. {{error}}"
}, },
"fields": { "fields": {
"color": "Color", "color": "Color",
@@ -776,6 +786,7 @@
"notes": "Notes", "notes": "Notes",
"plate": "Plate Number", "plate": "Plate Number",
"purchasedate": "Purchase Date", "purchasedate": "Purchase Date",
"readiness": "Readiness",
"registrationexpires": "Registration Expires On", "registrationexpires": "Registration Expires On",
"serviceenddate": "Usage End Date", "serviceenddate": "Usage End Date",
"servicestartdate": "Usage Start Date", "servicestartdate": "Usage Start Date",
@@ -812,6 +823,10 @@
}, },
"successes": { "successes": {
"saved": "Courtesy Car saved successfully." "saved": "Courtesy Car saved successfully."
},
"readiness": {
"notready": "Not Ready",
"ready": "Ready"
} }
}, },
"csi": { "csi": {
@@ -855,14 +870,16 @@
"refhrs": "Refinish Hrs" "refhrs": "Refinish Hrs"
}, },
"titles": { "titles": {
"labhours": "Total Body Hours",
"larhours": "Total Refinish Hours",
"monthlyemployeeefficiency": "Monthly Employee Efficiency", "monthlyemployeeefficiency": "Monthly Employee Efficiency",
"monthlyjobcosting": "Monthly Job Costing ", "monthlyjobcosting": "Monthly Job Costing ",
"monthlylaborsales": "Monthly Labor Sales", "monthlylaborsales": "Monthly Labor Sales",
"monthlypartssales": "Monthly Parts Sales", "monthlypartssales": "Monthly Parts Sales",
"monthlyrevenuegraph": "Monthly Revenue Graph", "monthlyrevenuegraph": "Monthly Revenue Graph",
"prodhrssummary": "Production Hours Summary", "prodhrssummary": "Production Hours Summary",
"productiondollars": "Total dollars in Production", "productiondollars": "Total Dollars in Production",
"productionhours": "Total hours in Production", "productionhours": "Total Hours in Production",
"projectedmonthlysales": "Projected Monthly Sales", "projectedmonthlysales": "Projected Monthly Sales",
"scheduledintoday": "Sheduled In Today: {{date}}", "scheduledintoday": "Sheduled In Today: {{date}}",
"scheduledouttoday": "Sheduled Out Today: {{date}}" "scheduledouttoday": "Sheduled Out Today: {{date}}"
@@ -907,7 +924,7 @@
"upload_limitexceeded": "Uploading all selected documents will exceed the job storage limit for your shop. ", "upload_limitexceeded": "Uploading all selected documents will exceed the job storage limit for your shop. ",
"upload_limitexceeded_title": "Unable to upload document(s)", "upload_limitexceeded_title": "Unable to upload document(s)",
"uploading": "Uploading...", "uploading": "Uploading...",
"usage": "of job storage used. ({{used}} / {{total}})" "usage": "of Job storage used. ({{used}} / {{total}})"
}, },
"successes": { "successes": {
"delete": "Document(s) deleted successfully.", "delete": "Document(s) deleted successfully.",
@@ -1368,28 +1385,28 @@
}, },
"errors": { "errors": {
"addingtoproduction": "Error adding to production. {{error}}", "addingtoproduction": "Error adding to production. {{error}}",
"cannotintake": "Intake cannot be completed for this job. It has either already been completed or the job is already here.", "cannotintake": "Intake cannot be completed for this Job. It has either already been completed or the Job is already here.",
"closing": "Error closing job. {{error}}", "closing": "Error closing Job. {{error}}",
"creating": "Error encountered while creating job. {{error}}", "creating": "Error encountered while creating Job. {{error}}",
"deleted": "Error deleting job. {{error}}", "deleted": "Error deleting Job. {{error}}",
"exporting": "Error exporting job. {{error}}", "exporting": "Error exporting Job. {{error}}",
"exporting-partner": "Unable to connect to ImEX Partner. Please ensure it is running and logged in.", "exporting-partner": "Unable to connect to ImEX Partner. Please ensure it is running and logged in.",
"invoicing": "Error invoicing job. {{error}}", "invoicing": "Error invoicing Job. {{error}}",
"noaccess": "This job does not exist or you do not have access to it.", "noaccess": "This Job does not exist or you do not have access to it.",
"nodamage": "No damage points on estimate.", "nodamage": "No damage points on estimate.",
"nodates": "No dates specified for this job.", "nodates": "No dates specified for this Job.",
"nofinancial": "No financial data has been calculated yet for this job. Please save it again.", "nofinancial": "No financial data has been calculated yet for this job. Please save it again.",
"nojobselected": "No job is selected.", "nojobselected": "No Job is selected.",
"noowner": "No owner associated.", "noowner": "No owner associated.",
"novehicle": "No vehicle associated.", "novehicle": "No vehicle associated.",
"partspricechange": "", "partspricechange": "",
"saving": "Error encountered while saving record.", "saving": "Error encountered while saving record.",
"scanimport": "Error importing job. {{message}}", "scanimport": "Error importing Job. {{message}}",
"totalscalc": "Error while calculating new job totals.", "totalscalc": "Error while calculating new Job totals.",
"updating": "Error while updating job(s). {{error}}", "updating": "Error while updating Job(s). {{error}}",
"validation": "Please ensure all fields are entered correctly.", "validation": "Please ensure all fields are entered correctly.",
"validationtitle": "Validation Error", "validationtitle": "Validation Error",
"voiding": "Error voiding job. {{error}}" "voiding": "Error voiding Job. {{error}}"
}, },
"fields": { "fields": {
"actual_completion": "Actual Completion", "actual_completion": "Actual Completion",
@@ -1442,6 +1459,7 @@
"date_exported": "Exported", "date_exported": "Exported",
"date_invoiced": "Invoiced", "date_invoiced": "Invoiced",
"date_last_contacted": "Last Contacted Date", "date_last_contacted": "Last Contacted Date",
"date_lost_sale": "Lost Sale",
"date_next_contact": "Next Contact Date", "date_next_contact": "Next Contact Date",
"date_open": "Open", "date_open": "Open",
"date_rentalresp": "Shop Rental Responsibility Start", "date_rentalresp": "Shop Rental Responsibility Start",
@@ -1665,9 +1683,9 @@
"adminwarning": "Use the functionality on this page at your own risk. You are responsible for any and all changes to your data.", "adminwarning": "Use the functionality on this page at your own risk. You are responsible for any and all changes to your data.",
"allocations": "Allocations", "allocations": "Allocations",
"alreadyaddedtoscoreboard": "Job has already been added to scoreboard. Saving will update the previous entry.", "alreadyaddedtoscoreboard": "Job has already been added to scoreboard. Saving will update the previous entry.",
"alreadyclosed": "This job has already been closed.", "alreadyclosed": "This Job has already been closed.",
"appointmentconfirmation": "Send confirmation to customer?", "appointmentconfirmation": "Send confirmation to customer?",
"associationwarning": "Any changes to associations will require updating the data from the new parent record to the job.", "associationwarning": "Any changes to associations will require updating the data from the new parent record to the Job.",
"audit": "Audit Trail", "audit": "Audit Trail",
"available": "Available", "available": "Available",
"availablejobs": "Available Jobs", "availablejobs": "Available Jobs",
@@ -1675,7 +1693,7 @@
"days": "Days", "days": "Days",
"rate": "PVRT Rate" "rate": "PVRT Rate"
}, },
"ca_gst_all_if_null": "If the job is marked as a \"GST Registrant\" and this value is set to $0, the customer will be responsible for paying all of the GST by default. ", "ca_gst_all_if_null": "If the Job is marked as a \"GST Registrant\" and this value is set to $0, the customer will be responsible for paying all of the GST by default. ",
"calc_repair_days": "Calculated Repair Days", "calc_repair_days": "Calculated Repair Days",
"calc_repair_days_tt": "This is the approximate number of days required to complete the repair according to the target touch time in your shop configuration (current set to {{target_touchtime}}).", "calc_repair_days_tt": "This is the approximate number of days required to complete the repair according to the target touch time in your shop configuration (current set to {{target_touchtime}}).",
"cards": { "cards": {
@@ -1691,7 +1709,7 @@
"totals": "Totals", "totals": "Totals",
"vehicle": "Vehicle" "vehicle": "Vehicle"
}, },
"changeclass": "Changing the job's class can have fundamental impacts to already exported accounting items. Are you sure you want to do this?", "changeclass": "Changing the Job's class can have fundamental impacts to already exported accounting items. Are you sure you want to do this?",
"checklistcompletedby": "Checklist completed by {{by}} at {{at}}", "checklistcompletedby": "Checklist completed by {{by}} at {{at}}",
"checklistdocuments": "Checklist Documents", "checklistdocuments": "Checklist Documents",
"checklists": "Checklists", "checklists": "Checklists",
@@ -1715,12 +1733,12 @@
"vehicleinfo": "Vehicle Info" "vehicleinfo": "Vehicle Info"
}, },
"createiouwarning": "Are you sure you want to create an IOU for these lines? A new RO will be created based on those lines for this customer.", "createiouwarning": "Are you sure you want to create an IOU for these lines? A new RO will be created based on those lines for this customer.",
"creating_new_job": "Creating new job...", "creating_new_job": "Creating new Job...",
"deductible": { "deductible": {
"stands": "Stands", "stands": "Stands",
"waived": "Waived" "waived": "Waived"
}, },
"deleteconfirm": "Are you sure you want to delete this job? This cannot be undone. ", "deleteconfirm": "Are you sure you want to delete this Job? This cannot be undone. ",
"deletedelivery": "Delete Delivery Checklist", "deletedelivery": "Delete Delivery Checklist",
"deleteintake": "Delete Intake Checklist", "deleteintake": "Delete Intake Checklist",
"deliverchecklist": "Deliver Checklist", "deliverchecklist": "Deliver Checklist",
@@ -1741,7 +1759,7 @@
"documents": "Documents", "documents": "Documents",
"documents-images": "Images", "documents-images": "Images",
"documents-other": "Other Documents", "documents-other": "Other Documents",
"duplicateconfirm": "Are you sure you want to duplicate this job? Some elements of this job will not be duplicated.", "duplicateconfirm": "Are you sure you want to duplicate this Job? Some elements of this Job will not be duplicated.",
"emailaudit": "Email Audit Trail", "emailaudit": "Email Audit Trail",
"employeeassignments": "Employee Assignments", "employeeassignments": "Employee Assignments",
"estimatelines": "Estimate Lines", "estimatelines": "Estimate Lines",
@@ -1752,7 +1770,7 @@
"gppercent": "% G.P.", "gppercent": "% G.P.",
"hrs_claimed": "Hours Claimed", "hrs_claimed": "Hours Claimed",
"hrs_total": "Hours Total", "hrs_total": "Hours Total",
"importnote": "The job was initially imported.", "importnote": "The Job was initially imported.",
"inproduction": "In Production", "inproduction": "In Production",
"intakechecklist": "Intake Checklist", "intakechecklist": "Intake Checklist",
"iou": "IOU", "iou": "IOU",
@@ -1785,9 +1803,9 @@
"calculatedcreditsnotreceived": "The calculated credits not received is derived by subtracting the amount of credit memos entered from the <b>retail</b> total of returns created. This does not take into account whether the credit was marked as received. You can find more information <a href=\"https://help.imex.online/en/article/credits-not-received-changes-1jy9snw\" target=\"_blank\">here</a>.", "calculatedcreditsnotreceived": "The calculated credits not received is derived by subtracting the amount of credit memos entered from the <b>retail</b> total of returns created. This does not take into account whether the credit was marked as received. You can find more information <a href=\"https://help.imex.online/en/article/credits-not-received-changes-1jy9snw\" target=\"_blank\">here</a>.",
"creditmemos": "The total <b>retail</b> amount of all returns created. This amount does not reflect credit memos that have been posted.", "creditmemos": "The total <b>retail</b> amount of all returns created. This amount does not reflect credit memos that have been posted.",
"creditsnotreceived": "This total reflects the total <b>retail</b> of parts returns lines that have not been explicitly marked as returned when posting a credit memo. You can learn more about this here <a href=\"https://help.imex.online/en/article/credits-not-received-changes-1jy9snw\" target=\"_blank\">here</a>. ", "creditsnotreceived": "This total reflects the total <b>retail</b> of parts returns lines that have not been explicitly marked as returned when posting a credit memo. You can learn more about this here <a href=\"https://help.imex.online/en/article/credits-not-received-changes-1jy9snw\" target=\"_blank\">here</a>. ",
"discrep1": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>Too many bills/bill lines that have been posted against this RO. Check to make sure every bill posted on this RO is correctly posted and assigned.</li>\n<li>You do not have the latest supplement imported, or, a supplement must be submitted and then imported.</li>\n<li>You have posted a bill line to labor.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this job from reconciling.</i>", "discrep1": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>Too many bills/bill lines that have been posted against this RO. Check to make sure every bill posted on this RO is correctly posted and assigned.</li>\n<li>You do not have the latest supplement imported, or, a supplement must be submitted and then imported.</li>\n<li>You have posted a bill line to labor.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this Job from reconciling.</i>",
"discrep2": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>Used an incorrect rate when deducting from labor.</li>\n<li>An outstanding imbalance higher in the reconciliation process.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this job from reconciling.</i>", "discrep2": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>Used an incorrect rate when deducting from labor.</li>\n<li>An outstanding imbalance higher in the reconciliation process.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this Job from reconciling.</i>",
"discrep3": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>A parts order return has not been created.</li>\n<li>An outstanding imbalance higher in the reconciliation process.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this job from reconciling.</i>", "discrep3": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>A parts order return has not been created.</li>\n<li>An outstanding imbalance higher in the reconciliation process.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this Job from reconciling.</i>",
"laboradj": "The sum of all bill lines that deducted from labor hours, rather than part prices.", "laboradj": "The sum of all bill lines that deducted from labor hours, rather than part prices.",
"partstotal": "This is the total of all parts and sublet amounts on the vehicle (some of these may require an in-house invoice).<br/>\nItems such as shop and paint materials, labor online lines, etc. are not included in this total.", "partstotal": "This is the total of all parts and sublet amounts on the vehicle (some of these may require an in-house invoice).<br/>\nItems such as shop and paint materials, labor online lines, etc. are not included in this total.",
"totalreturns": "The total <b>retail</b> amount of returns created for this job." "totalreturns": "The total <b>retail</b> amount of returns created for this job."
@@ -1818,13 +1836,13 @@
"sale_parts": "Sales - Parts", "sale_parts": "Sales - Parts",
"sale_sublet": "Sales - Sublet", "sale_sublet": "Sales - Sublet",
"sales": "Sales", "sales": "Sales",
"savebeforeconversion": "You have unsaved changes on the job. Please save them before converting it. ", "savebeforeconversion": "You have unsaved changes on the Job. Please save them before converting it. ",
"scheduledinchange": "The scheduled in is based off the latest appointment. To change this date, please schedule or reschedule the job. ", "scheduledinchange": "The scheduled in is based off the latest appointment. To change this date, please schedule or reschedule the Job. ",
"specialcoveragepolicy": "Special Coverage Policy Applies", "specialcoveragepolicy": "Special Coverage Policy Applies",
"state_tax_amt": "Provincial/State Taxes", "state_tax_amt": "Provincial/State Taxes",
"subletstotal": "Sublets Total", "subletstotal": "Sublets Total",
"subtotal": "Subtotal", "subtotal": "Subtotal",
"supplementnote": "The job had a supplement imported.", "supplementnote": "The Job had a supplement imported.",
"suspended": "SUSPENDED", "suspended": "SUSPENDED",
"suspense": "Suspense", "suspense": "Suspense",
"threshhold": "Max Threshold: ${{amount}}", "threshhold": "Max Threshold: ${{amount}}",
@@ -1833,16 +1851,16 @@
"total_repairs": "Total Repairs", "total_repairs": "Total Repairs",
"total_sales": "Total Sales", "total_sales": "Total Sales",
"totals": "Totals", "totals": "Totals",
"unvoidnote": "This job was unvoided.", "unvoidnote": "This Job was unvoided.",
"vehicle_info": "Vehicle", "vehicle_info": "Vehicle",
"vehicleassociation": "Vehicle Association", "vehicleassociation": "Vehicle Association",
"viewallocations": "View Allocations", "viewallocations": "View Allocations",
"voidjob": "Are you sure you want to void this job? This cannot be easily undone. ", "voidjob": "Are you sure you want to void this Job? This cannot be easily undone. ",
"voidnote": "This job was voided." "voidnote": "This Job was voided."
}, },
"successes": { "successes": {
"addedtoproduction": "Job added to production board.", "addedtoproduction": "Job added to production board.",
"all_deleted": "{{count}} jobs deleted successfully.", "all_deleted": "{{count}} Jobs deleted successfully.",
"closed": "Job closed successfully.", "closed": "Job closed successfully.",
"converted": "Job converted successfully.", "converted": "Job converted successfully.",
"created": "Job created successfully. Click to view.", "created": "Job created successfully. Click to view.",
@@ -2018,7 +2036,7 @@
}, },
"errors": { "errors": {
"invalidphone": "The phone number is invalid. Unable to open conversation. ", "invalidphone": "The phone number is invalid. Unable to open conversation. ",
"noattachedjobs": "No jobs have been associated to this conversation. ", "noattachedjobs": "No Jobs have been associated to this conversation. ",
"updatinglabel": "Error updating label. {{error}}" "updatinglabel": "Error updating label. {{error}}"
}, },
"labels": { "labels": {
@@ -2027,7 +2045,7 @@
"maxtenimages": "You can only select up to a maximum of 10 images at a time.", "maxtenimages": "You can only select up to a maximum of 10 images at a time.",
"messaging": "Messaging", "messaging": "Messaging",
"noallowtxt": "This customer has not indicated their permission to be messaged.", "noallowtxt": "This customer has not indicated their permission to be messaged.",
"nojobs": "Not associated to any job.", "nojobs": "Not associated to any Job.",
"nopush": "Polling Mode Enabled", "nopush": "Polling Mode Enabled",
"phonenumber": "Phone #", "phonenumber": "Phone #",
"presets": "Presets", "presets": "Presets",
@@ -2036,6 +2054,9 @@
"sentby": "Sent by {{by}} at {{time}}", "sentby": "Sent by {{by}} at {{time}}",
"typeamessage": "Send a message...", "typeamessage": "Send a message...",
"unarchive": "Unarchive" "unarchive": "Unarchive"
},
"render": {
"conversation_list": "Conversation List"
} }
}, },
"notes": { "notes": {
@@ -2194,7 +2215,7 @@
"parts_orders": "Parts Orders", "parts_orders": "Parts Orders",
"print": "Show Printed Form", "print": "Show Printed Form",
"receive": "Receive Parts Order", "receive": "Receive Parts Order",
"removefrompartsqueue": "Remove from Parts Queue?", "removefrompartsqueue": "Unqueue from Parts Queue?",
"returnpartsorder": "Return Parts Order", "returnpartsorder": "Return Parts Order",
"sublet_order": "Sublet Order" "sublet_order": "Sublet Order"
}, },
@@ -2447,15 +2468,15 @@
"unsuspend": "Unsuspend" "unsuspend": "Unsuspend"
}, },
"errors": { "errors": {
"boardupdate": "Error encountered updating job. {{message}}", "boardupdate": "Error encountered updating Job. {{message}}",
"removing": "Error removing from production board. {{error}}", "removing": "Error removing from production board. {{error}}",
"settings": "Error saving board settings: {{error}}" "settings": "Error saving board settings: {{error}}"
}, },
"labels": { "labels": {
"actual_in": "Actual In", "actual_in": "Actual In",
"alert": "Alert", "alert": "Alert",
"alertoff": "Remove alert from job", "alertoff": "Remove alert from Job",
"alerton": "Add alert to job", "alerton": "Add alert to Job",
"ats": "Alternative Transportation", "ats": "Alternative Transportation",
"bodyhours": "B", "bodyhours": "B",
"bodypriority": "B/P", "bodypriority": "B/P",
@@ -2596,6 +2617,7 @@
"jobs_reconcile": "Parts/Sublet/Labor Reconciliation", "jobs_reconcile": "Parts/Sublet/Labor Reconciliation",
"jobs_scheduled_completion": "Jobs Scheduled Completion", "jobs_scheduled_completion": "Jobs Scheduled Completion",
"lag_time": "Lag Time", "lag_time": "Lag Time",
"lost_sales": "Lost Sales",
"open_orders": "Open Orders by Date", "open_orders": "Open Orders by Date",
"open_orders_csr": "Open Orders by CSR", "open_orders_csr": "Open Orders by CSR",
"open_orders_estimator": "Open Orders by Estimator", "open_orders_estimator": "Open Orders by Estimator",
@@ -2665,9 +2687,9 @@
"edit": "Edit" "edit": "Edit"
}, },
"errors": { "errors": {
"adding": "Error adding job to scoreboard. {{message}}", "adding": "Error adding Job to Scoreboard. {{message}}",
"removing": "Error removing job from scoreboard. {{message}}", "removing": "Error removing Job from Scoreboard. {{message}}",
"updating": "Error updating scoreboard. {{message}}" "updating": "Error updating Scoreboard. {{message}}"
}, },
"fields": { "fields": {
"bodyhrs": "Body Hours", "bodyhrs": "Body Hours",
@@ -2686,6 +2708,7 @@
"efficiencyoverperiod": "Efficiency over Selected Dates", "efficiencyoverperiod": "Efficiency over Selected Dates",
"entries": "Scoreboard Entries", "entries": "Scoreboard Entries",
"jobs": "Jobs", "jobs": "Jobs",
"jobscompletednotinvoiced": "Completed Not Invoiced",
"lastmonth": "Last Month", "lastmonth": "Last Month",
"lastweek": "Last Week", "lastweek": "Last Week",
"monthlytarget": "Monthly", "monthlytarget": "Monthly",
@@ -2700,6 +2723,7 @@
"timetickets": "Time Tickets", "timetickets": "Time Tickets",
"timeticketsemployee": "Time Tickets by Employee", "timeticketsemployee": "Time Tickets by Employee",
"todateactual": "Actual (MTD)", "todateactual": "Actual (MTD)",
"totalhrs": "Total Hours",
"totaloverperiod": "Total over Selected Dates", "totaloverperiod": "Total over Selected Dates",
"weeklyactual": "Actual (W)", "weeklyactual": "Actual (W)",
"weeklytarget": "Weekly", "weeklytarget": "Weekly",
@@ -2765,7 +2789,7 @@
"ro_number": "Job to Post Against" "ro_number": "Job to Post Against"
}, },
"labels": { "labels": {
"alreadyclockedon": "You are already clocked in to the following job(s):", "alreadyclockedon": "You are already clocked in to the following Job(s):",
"ambreak": "AM Break", "ambreak": "AM Break",
"amshift": "AM Shift", "amshift": "AM Shift",
"clockhours": "Shift Clock Hours Summary", "clockhours": "Shift Clock Hours Summary",

View File

@@ -103,12 +103,16 @@
"admin_jobmarkforreexport": "", "admin_jobmarkforreexport": "",
"admin_jobuninvoice": "", "admin_jobuninvoice": "",
"admin_jobunvoid": "", "admin_jobunvoid": "",
"alerttoggle": "",
"appointmentcancel": "",
"appointmentinsert": "",
"billposted": "", "billposted": "",
"billupdated": "", "billupdated": "",
"failedpayment": "", "failedpayment": "",
"jobassignmentchange": "", "jobassignmentchange": "",
"jobassignmentremoved": "", "jobassignmentremoved": "",
"jobchecklist": "", "jobchecklist": "",
"jobinvoiced": "",
"jobconverted": "", "jobconverted": "",
"jobfieldchanged": "", "jobfieldchanged": "",
"jobimported": "", "jobimported": "",
@@ -201,6 +205,7 @@
"entered_total": "", "entered_total": "",
"enteringcreditmemo": "", "enteringcreditmemo": "",
"federal_tax": "", "federal_tax": "",
"federal_tax_exempt": "",
"generatepartslabel": "", "generatepartslabel": "",
"iouexists": "", "iouexists": "",
"local_tax": "", "local_tax": "",
@@ -209,6 +214,7 @@
"new": "", "new": "",
"noneselected": "", "noneselected": "",
"onlycmforinvoiced": "", "onlycmforinvoiced": "",
"printlabels": "",
"retailtotal": "", "retailtotal": "",
"savewithdiscrepancy": "", "savewithdiscrepancy": "",
"state_tax": "", "state_tax": "",
@@ -252,6 +258,9 @@
"address1": "", "address1": "",
"address2": "", "address2": "",
"appt_alt_transport": "", "appt_alt_transport": "",
"md_functionality_toggles": {
"parts_queue_toggle": ""
},
"appt_colors": { "appt_colors": {
"color": "", "color": "",
"label": "" "label": ""
@@ -442,6 +451,7 @@
"config": "", "config": "",
"dashboard": "", "dashboard": "",
"rbac": "", "rbac": "",
"reportcenter": "",
"templates": "", "templates": "",
"vendors": "" "vendors": ""
}, },
@@ -776,6 +786,7 @@
"notes": "", "notes": "",
"plate": "", "plate": "",
"purchasedate": "", "purchasedate": "",
"readiness": "",
"registrationexpires": "", "registrationexpires": "",
"serviceenddate": "", "serviceenddate": "",
"servicestartdate": "", "servicestartdate": "",
@@ -812,6 +823,10 @@
}, },
"successes": { "successes": {
"saved": "" "saved": ""
},
"readiness": {
"notready": "",
"ready": ""
} }
}, },
"csi": { "csi": {
@@ -855,6 +870,8 @@
"refhrs": "" "refhrs": ""
}, },
"titles": { "titles": {
"labhours": "",
"larhours": "",
"monthlyemployeeefficiency": "", "monthlyemployeeefficiency": "",
"monthlyjobcosting": "", "monthlyjobcosting": "",
"monthlylaborsales": "", "monthlylaborsales": "",
@@ -1442,6 +1459,7 @@
"date_exported": "Exportado", "date_exported": "Exportado",
"date_invoiced": "Facturado", "date_invoiced": "Facturado",
"date_last_contacted": "", "date_last_contacted": "",
"date_lost_sale": "",
"date_next_contact": "", "date_next_contact": "",
"date_open": "Abierto", "date_open": "Abierto",
"date_rentalresp": "", "date_rentalresp": "",
@@ -2036,6 +2054,9 @@
"sentby": "", "sentby": "",
"typeamessage": "Enviar un mensaje...", "typeamessage": "Enviar un mensaje...",
"unarchive": "" "unarchive": ""
},
"render": {
"conversation_list": ""
} }
}, },
"notes": { "notes": {
@@ -2596,6 +2617,7 @@
"jobs_reconcile": "", "jobs_reconcile": "",
"jobs_scheduled_completion": "", "jobs_scheduled_completion": "",
"lag_time": "", "lag_time": "",
"lost_sales": "",
"open_orders": "", "open_orders": "",
"open_orders_csr": "", "open_orders_csr": "",
"open_orders_estimator": "", "open_orders_estimator": "",
@@ -2686,6 +2708,7 @@
"efficiencyoverperiod": "", "efficiencyoverperiod": "",
"entries": "", "entries": "",
"jobs": "", "jobs": "",
"jobscompletednotinvoiced": "",
"lastmonth": "", "lastmonth": "",
"lastweek": "", "lastweek": "",
"monthlytarget": "", "monthlytarget": "",
@@ -2700,6 +2723,7 @@
"timetickets": "", "timetickets": "",
"timeticketsemployee": "", "timeticketsemployee": "",
"todateactual": "", "todateactual": "",
"totalhrs": "",
"totaloverperiod": "", "totaloverperiod": "",
"weeklyactual": "", "weeklyactual": "",
"weeklytarget": "", "weeklytarget": "",

View File

@@ -103,12 +103,16 @@
"admin_jobmarkforreexport": "", "admin_jobmarkforreexport": "",
"admin_jobuninvoice": "", "admin_jobuninvoice": "",
"admin_jobunvoid": "", "admin_jobunvoid": "",
"alerttoggle": "",
"appointmentcancel": "",
"appointmentinsert": "",
"billposted": "", "billposted": "",
"billupdated": "", "billupdated": "",
"failedpayment": "", "failedpayment": "",
"jobassignmentchange": "", "jobassignmentchange": "",
"jobassignmentremoved": "", "jobassignmentremoved": "",
"jobchecklist": "", "jobchecklist": "",
"jobinvoiced": "",
"jobconverted": "", "jobconverted": "",
"jobfieldchanged": "", "jobfieldchanged": "",
"jobimported": "", "jobimported": "",
@@ -201,6 +205,7 @@
"entered_total": "", "entered_total": "",
"enteringcreditmemo": "", "enteringcreditmemo": "",
"federal_tax": "", "federal_tax": "",
"federal_tax_exempt": "",
"generatepartslabel": "", "generatepartslabel": "",
"iouexists": "", "iouexists": "",
"local_tax": "", "local_tax": "",
@@ -209,6 +214,7 @@
"new": "", "new": "",
"noneselected": "", "noneselected": "",
"onlycmforinvoiced": "", "onlycmforinvoiced": "",
"printlabels": "",
"retailtotal": "", "retailtotal": "",
"savewithdiscrepancy": "", "savewithdiscrepancy": "",
"state_tax": "", "state_tax": "",
@@ -329,6 +335,9 @@
"paint": "", "paint": "",
"prep": "" "prep": ""
}, },
"md_functionality_toggles": {
"parts_queue_toggle": ""
},
"md_ins_co": { "md_ins_co": {
"city": "", "city": "",
"name": "", "name": "",
@@ -442,6 +451,7 @@
"config": "", "config": "",
"dashboard": "", "dashboard": "",
"rbac": "", "rbac": "",
"reportcenter": "",
"templates": "", "templates": "",
"vendors": "" "vendors": ""
}, },
@@ -776,6 +786,7 @@
"notes": "", "notes": "",
"plate": "", "plate": "",
"purchasedate": "", "purchasedate": "",
"readiness": "",
"registrationexpires": "", "registrationexpires": "",
"serviceenddate": "", "serviceenddate": "",
"servicestartdate": "", "servicestartdate": "",
@@ -812,6 +823,10 @@
}, },
"successes": { "successes": {
"saved": "" "saved": ""
},
"readiness": {
"notready": "",
"ready": ""
} }
}, },
"csi": { "csi": {
@@ -855,6 +870,8 @@
"refhrs": "" "refhrs": ""
}, },
"titles": { "titles": {
"labhours": "",
"larhours": "",
"monthlyemployeeefficiency": "", "monthlyemployeeefficiency": "",
"monthlyjobcosting": "", "monthlyjobcosting": "",
"monthlylaborsales": "", "monthlylaborsales": "",
@@ -1442,6 +1459,7 @@
"date_exported": "Exportés", "date_exported": "Exportés",
"date_invoiced": "Facturé", "date_invoiced": "Facturé",
"date_last_contacted": "", "date_last_contacted": "",
"date_lost_sale": "",
"date_next_contact": "", "date_next_contact": "",
"date_open": "Ouvrir", "date_open": "Ouvrir",
"date_rentalresp": "", "date_rentalresp": "",
@@ -2036,6 +2054,9 @@
"sentby": "", "sentby": "",
"typeamessage": "Envoyer un message...", "typeamessage": "Envoyer un message...",
"unarchive": "" "unarchive": ""
},
"render": {
"conversation_list": ""
} }
}, },
"notes": { "notes": {
@@ -2596,6 +2617,7 @@
"jobs_reconcile": "", "jobs_reconcile": "",
"jobs_scheduled_completion": "", "jobs_scheduled_completion": "",
"lag_time": "", "lag_time": "",
"lost_sales": "",
"open_orders": "", "open_orders": "",
"open_orders_csr": "", "open_orders_csr": "",
"open_orders_estimator": "", "open_orders_estimator": "",
@@ -2686,6 +2708,7 @@
"efficiencyoverperiod": "", "efficiencyoverperiod": "",
"entries": "", "entries": "",
"jobs": "", "jobs": "",
"jobscompletednotinvoiced": "",
"lastmonth": "", "lastmonth": "",
"lastweek": "", "lastweek": "",
"monthlytarget": "", "monthlytarget": "",
@@ -2700,6 +2723,7 @@
"timetickets": "", "timetickets": "",
"timeticketsemployee": "", "timeticketsemployee": "",
"todateactual": "", "todateactual": "",
"totalhrs": "",
"totaloverperiod": "", "totaloverperiod": "",
"weeklyactual": "", "weeklyactual": "",
"weeklytarget": "", "weeklytarget": "",

View File

@@ -1,12 +1,19 @@
import i18n from "i18next"; import i18n from "i18next";
const AuditTrailMapping = { const AuditTrailMapping = {
alertToggle: (status) => i18n.t("audit_trail.messages.alerttoggle", { status }),
appointmentcancel: (lost_sale_reason) =>
i18n.t("audit_trail.messages.appointmentcancel", { lost_sale_reason }),
appointmentinsert: (start) =>
i18n.t("audit_trail.messages.appointmentinsert", { start }),
jobstatuschange: (status) => jobstatuschange: (status) =>
i18n.t("audit_trail.messages.jobstatuschange", { status }), i18n.t("audit_trail.messages.jobstatuschange", { status }),
admin_jobstatuschange: (status) => admin_jobstatuschange: (status) =>
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }), "ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"), jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
jobimported: () => i18n.t("audit_trail.messages.jobimported"), jobimported: () => i18n.t("audit_trail.messages.jobimported"),
jobinvoiced: () =>
i18n.t("audit_trail.messages.jobinvoiced"),
jobconverted: (ro_number) => jobconverted: (ro_number) =>
i18n.t("audit_trail.messages.jobconverted", { ro_number }), i18n.t("audit_trail.messages.jobconverted", { ro_number }),
jobfieldchange: (field, value) => jobfieldchange: (field, value) =>

View File

@@ -31,3 +31,7 @@ export function TimeAgoFormatter(props) {
</Tooltip> </Tooltip>
) : null; ) : null;
} }
export function DateTimeFormat(value) {
return moment(value).format("MM/DD/YYYY hh:mm A");
}

View File

@@ -2014,6 +2014,18 @@ export const TemplateList = (type, context) => {
}, },
group: "jobs", group: "jobs",
}, },
lost_sales: {
title: i18n.t("reportcenter.templates.lost_sales"),
subject: i18n.t("reportcenter.templates.lost_sales"),
key: "lost_sales",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_lost_sale"),
},
group: "customers",
},
} }
: {}), : {}),
...(!type || type === "courtesycarcontract" ...(!type || type === "courtesycarcontract"
@@ -2090,6 +2102,17 @@ export const TemplateList = (type, context) => {
// }, // },
} }
: {}), : {}),
...(!type || type === "messaging"
? {
conversation_list: {
title: i18n.t("messaging.render.conversation_list"),
description: "",
subject: i18n.t("messaging.render.conversation_list"),
key: "conversation_list",
disabled: false,
},
}
: {}),
...(!type || type === "vendor" ...(!type || type === "vendor"
? { ? {
purchases_by_vendor_detailed: { purchases_by_vendor_detailed: {

View File

@@ -0,0 +1,4 @@
// Sometimes referred to as PageSize, this variable controls the amount of records
// to show on one page during pagination.
export const pageLimit = 50;

View File

@@ -890,11 +890,13 @@
- appt_colors - appt_colors
- appt_length - appt_length
- attach_pdf_to_email - attach_pdf_to_email
- autohouseid
- bill_allow_post_to_closed - bill_allow_post_to_closed
- bill_tax_rates - bill_tax_rates
- cdk_configuration - cdk_configuration
- cdk_dealerid - cdk_dealerid
- city - city
- claimscorpid
- country - country
- created_at - created_at
- default_adjustment_rate - default_adjustment_rate
@@ -928,6 +930,7 @@
- md_estimators - md_estimators
- md_filehandlers - md_filehandlers
- md_from_emails - md_from_emails
- md_functionality_toggles
- md_hour_split - md_hour_split
- md_ins_cos - md_ins_cos
- md_jobline_presets - md_jobline_presets
@@ -1026,6 +1029,7 @@
- md_estimators - md_estimators
- md_filehandlers - md_filehandlers
- md_from_emails - md_from_emails
- md_functionality_toggles
- md_hour_split - md_hour_split
- md_ins_cos - md_ins_cos
- md_jobline_presets - md_jobline_presets
@@ -1384,60 +1388,62 @@
- active: - active:
_eq: true _eq: true
columns: columns:
- id
- created_at
- updated_at
- bodyshopid - bodyshopid
- make
- model
- year
- plate
- color - color
- vin - created_at
- fleetnumber
- purchasedate
- servicestartdate
- serviceenddate
- leaseenddate
- status
- nextservicekm
- nextservicedate
- damage
- notes
- fuel
- registrationexpires
- insuranceexpires
- dailycost - dailycost
- damage
- fleetnumber
- fuel
- id
- insuranceexpires
- leaseenddate
- make
- mileage - mileage
- model
- nextservicedate
- nextservicekm
- notes
- plate
- purchasedate
- readiness
- registrationexpires
- serviceenddate
- servicestartdate
- status
- updated_at
- vin
- year
select_permissions: select_permissions:
- role: user - role: user
permission: permission:
columns: columns:
- bodyshopid
- color
- created_at
- dailycost
- damage
- fleetnumber
- fuel
- id
- insuranceexpires - insuranceexpires
- leaseenddate - leaseenddate
- make
- mileage
- model
- nextservicedate - nextservicedate
- nextservicekm
- notes
- plate
- purchasedate - purchasedate
- readiness
- registrationexpires - registrationexpires
- serviceenddate - serviceenddate
- servicestartdate - servicestartdate
- dailycost
- fuel
- mileage
- nextservicekm
- color
- damage
- fleetnumber
- make
- model
- notes
- plate
- status - status
- updated_at
- vin - vin
- year - year
- created_at
- updated_at
- bodyshopid
- id
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -1452,31 +1458,32 @@
- role: user - role: user
permission: permission:
columns: columns:
- bodyshopid
- color
- created_at
- dailycost
- damage
- fleetnumber
- fuel
- id
- insuranceexpires - insuranceexpires
- leaseenddate - leaseenddate
- make
- mileage
- model
- nextservicedate - nextservicedate
- nextservicekm
- notes
- plate
- purchasedate - purchasedate
- readiness
- registrationexpires - registrationexpires
- serviceenddate - serviceenddate
- servicestartdate - servicestartdate
- dailycost
- fuel
- mileage
- nextservicekm
- color
- damage
- fleetnumber
- make
- model
- notes
- plate
- status - status
- updated_at
- vin - vin
- year - year
- created_at
- updated_at
- bodyshopid
- id
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -2016,24 +2023,24 @@
- active: - active:
_eq: true _eq: true
columns: columns:
- labor_rates
- percentage
- created_at - created_at
- updated_at
- employeeid - employeeid
- id - id
- labor_rates
- percentage
- teamid - teamid
- updated_at
select_permissions: select_permissions:
- role: user - role: user
permission: permission:
columns: columns:
- labor_rates
- percentage
- created_at - created_at
- updated_at
- employeeid - employeeid
- id - id
- labor_rates
- percentage
- teamid - teamid
- updated_at
filter: filter:
employee_team: employee_team:
bodyshop: bodyshop:
@@ -2048,13 +2055,13 @@
- role: user - role: user
permission: permission:
columns: columns:
- labor_rates
- percentage
- created_at - created_at
- updated_at
- employeeid - employeeid
- id - id
- labor_rates
- percentage
- teamid - teamid
- updated_at
filter: filter:
employee_team: employee_team:
bodyshop: bodyshop:
@@ -2116,21 +2123,23 @@
_eq: true _eq: true
columns: columns:
- active - active
- name
- created_at
- updated_at
- bodyshopid - bodyshopid
- created_at
- id - id
- max_load
- name
- updated_at
select_permissions: select_permissions:
- role: user - role: user
permission: permission:
columns: columns:
- active - active
- name
- created_at
- updated_at
- bodyshopid - bodyshopid
- created_at
- id - id
- max_load
- name
- updated_at
filter: filter:
bodyshop: bodyshop:
associations: associations:
@@ -2146,6 +2155,7 @@
columns: columns:
- active - active
- bodyshopid - bodyshopid
- max_load
- name - name
- updated_at - updated_at
filter: filter:
@@ -3583,6 +3593,7 @@
- date_exported - date_exported
- date_invoiced - date_invoiced
- date_last_contacted - date_last_contacted
- date_lost_sale
- date_next_contact - date_next_contact
- date_open - date_open
- date_rentalresp - date_rentalresp
@@ -3863,6 +3874,7 @@
- date_exported - date_exported
- date_invoiced - date_invoiced
- date_last_contacted - date_last_contacted
- date_lost_sale
- date_next_contact - date_next_contact
- date_open - date_open
- date_rentalresp - date_rentalresp

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"."bodyshops" add column "md_functionality_toggles" jsonb
-- null default jsonb_build_object();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "md_functionality_toggles" jsonb
null default jsonb_build_object();

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"."jobs" add column "date_lost_sale" timestamp with time zone
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "date_lost_sale" timestamp with time zone
null;

View File

@@ -0,0 +1 @@
ALTER TABLE "public"."jobs" ALTER COLUMN "date_lost_sale" TYPE timestamp with time zone;

View File

@@ -0,0 +1 @@
ALTER TABLE "public"."jobs" ALTER COLUMN "date_lost_sale" TYPE timestamp with time zone;

View File

@@ -0,0 +1,2 @@
alter table "public"."courtesycars" alter column "nextservicekm" set not null;
alter table "public"."courtesycars" alter column "nextservicekm" set default '0';

View File

@@ -0,0 +1,2 @@
ALTER TABLE "public"."courtesycars" ALTER COLUMN "nextservicekm" drop default;
alter table "public"."courtesycars" alter column "nextservicekm" drop not 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"."courtesycars" add column "readiness" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."courtesycars" add column "readiness" 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"."employee_team_members" add column "max_load" numeric
-- not null default '10000';

Some files were not shown because too many files have changed in this diff Show More