Compare commits

...

81 Commits

Author SHA1 Message Date
Patrick Fic
4507135a1b Merge branch 'master' into feature/america 2023-11-10 15:25:47 -08:00
Patrick Fic
4e8e25a336 Merged in release/2023-11-10 (pull request #1048)
Release/2023 11 10
2023-11-10 19:21:15 +00:00
Patrick Fic
0dac15391f Merge branch 'feature/IO-2445-block-types' into release/2023-11-10 2023-11-09 13:39:21 -08:00
Patrick Fic
106534b59b IO-2445 Updated blocked day types. 2023-11-09 13:39:05 -08:00
Patrick Fic
5f1475d2ec Merge branch 'feature/IO-2444-single-device-login' into release/2023-11-10 2023-11-08 12:08:32 -08:00
Patrick Fic
84f4d5956a IO-2444 update fingerprint JS and re-enable using shop flag. 2023-11-08 12:08:21 -08:00
Allan Carr
4330ddd926 Merged in feature/IO-2438-All-Employee-Scoreboard-Summary (pull request #1046)
IO-2438 All Employee Scoreboard
2023-11-08 17:15:37 +00:00
Allan Carr
0711210512 IO-2438 All Employee Scoreboard
A-G Autobody request
2023-11-08 09:13:16 -08:00
Patrick Fic
458ec76835 Merge branch 'feature/IO-2437-CDK-make-override' into release/2023-11-10 2023-11-07 12:36:40 -08:00
Patrick Fic
682ea860fb IO-2437 override CDK make model on update. 2023-11-07 12:36:30 -08:00
Patrick Fic
99977934e7 IO-2429 Remove CIECA information on duplication. 2023-11-07 09:24:21 -08:00
Patrick Fic
3e05b21c90 IO-2426 Add FCM Cache update for conversation aggregate count. 2023-11-07 08:55:59 -08:00
Patrick Fic
4e1dd52bea IO-2332 filter insurance company name on convert. 2023-11-06 15:07:26 -08:00
Patrick Fic
f6bcc743d8 IO-2330 Remove phone validation for vendor sav.e 2023-11-06 15:04:01 -08:00
Patrick Fic
66b97be9d2 Merge branch 'master' into feature/america 2023-11-06 14:48:49 -08:00
Allan Carr
7472285641 Merged in release/2023-11-03 (pull request #1043)
Release/2023 11 03
2023-11-03 16:45:23 +00:00
Allan Carr
67ff9f30c6 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1042)
IO-1559 Change File name as per Koyel
2023-11-01 17:28:30 +00:00
Allan Carr
a27092dbcc IO-1559 Change File name as per Koyel 2023-11-01 10:28:10 -07:00
Allan Carr
ca41bff446 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1041)
Feature/IO-1559 ClaimsCorp Datapump
2023-11-01 16:58:32 +00:00
Allan Carr
cf8280590c IO-1559 Prettyier 2023-10-31 12:36:39 -07:00
Allan Carr
b649ca1f00 IO-1559 Additional XML Tag adjustments 2023-10-31 12:35:18 -07:00
Allan Carr
b441301007 Merged in feature/IO-2430-Open-Orders-by-Referral (pull request #1040)
IO-2430 Open Orders by Referral
2023-10-31 00:08:53 +00:00
Allan Carr
2e93238b5c IO-2430 Open Orders by Referral 2023-10-30 17:08:22 -07:00
Allan Carr
ce4fe84536 Merged in feature/IO-2431-Created-By-for-Appointments (pull request #1038)
IO-2431 Created By for Appointments
2023-10-30 23:52:36 +00:00
Allan Carr
9b7c0af025 IO-2431 Created By for Appointments 2023-10-30 16:51:26 -07:00
Allan Carr
dfdaf36ed1 Merged in feature/2023-10-27 (pull request #1037)
Feature/2023 10 27
2023-10-27 17:38:10 +00:00
Allan Carr
cc8d1b3793 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1035)
IO-1559 Requested changes to tags from ClaimsCorp
2023-10-27 16:13:30 +00:00
Allan Carr
eb359d83c5 IO-1559 Requested changes to tags from ClaimsCorp 2023-10-25 17:01:03 -07:00
Allan Carr
ae13e9e36a Merged in feature/IO-2421-Production-Note-to-Initial-Value-for-Intake (pull request #1032)
IO-2421 Add Production Vars to the initial values on CheckList
2023-10-25 15:52:49 +00:00
Allan Carr
4a62ac2a11 IO-2421 Add Production Vars to the initial values on CheckList 2023-10-25 08:52:03 -07:00
Patrick Fic
ec21521281 Add Rome Support Chat Widget 2023-10-23 15:22:33 -07:00
Allan Carr
016a62b6d5 Merged in feature/IO-2420-Preferred-Vendor-for-Bills (pull request #1029)
IO-2420 Prefered Vendor for Bill Entry
2023-10-23 19:30:25 +00:00
Allan Carr
3e226b50ab Merged in feature/IO-2419-Thumbnail-Emailing (pull request #1030)
IO-2419 Correct for Cloudinary to include fullsize on send
2023-10-23 19:30:11 +00:00
Allan Carr
ab84cb5ada IO-2419 Correct for Cloudinary to include fullsize on send 2023-10-23 12:32:32 -07:00
Allan Carr
7825aa4122 IO-2420 Prefered Vendor for Bill Entry 2023-10-20 13:55:21 -07:00
Patrick Fic
8d43fbfcd9 Merged in release/2023-10-20 (pull request #1028)
Release/2023 10 20
2023-10-20 18:51:19 +00:00
Patrick Fic
47a01628d3 Merge branch 'feature/IO-2401-duplicate-chat-creation' into release/2023-10-20 2023-10-20 08:50:52 -07:00
Patrick Fic
c008660023 Resolve error on multiple conversations found saga. 2023-10-20 08:50:31 -07:00
Patrick Fic
5b29aec14b Resolve vehicle search select issue. 2023-10-19 15:17:22 -07:00
Allan Carr
ca6aa682f6 Merged in feature/IO-2416-Autohouse_ClaimsCorp-Query (pull request #1017)
IO-2416 Filter out "" from ClaimsCorp and Autohouse get shop queries
2023-10-19 19:45:23 +00:00
Allan Carr
eb3786cebf Merged in feature/IO-2419-Thumbnail-Emailing (pull request #1025)
IO-2419 Thumbnails being emailed instead of Full Size Image
2023-10-19 19:45:11 +00:00
Allan Carr
53843e22a4 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1016)
IO-1559 Add in extra required tag
2023-10-19 19:44:58 +00:00
Patrick Fic
e1693674ca Merged in feature/IO-2418-jobline-bill-amount (pull request #1026)
Add refetch of job when posting bill.
2023-10-19 16:34:56 +00:00
Patrick Fic
2d2190e4fa Add refetch of job when posting bill. 2023-10-19 09:34:35 -07:00
Allan Carr
02fd8097a8 IO-2419 Thumbnails being emailed instead of Full Size Image 2023-10-19 09:30:29 -07:00
Patrick Fic
fcfbc85683 Merged in feature/IO-2418-jobline-bill-amount (pull request #1024)
IO-2418 Adjust latest bill amount
2023-10-19 16:23:43 +00:00
Patrick Fic
802dd696f4 IO-2418 Adjust latest bill amount 2023-10-19 09:20:29 -07:00
Patrick Fic
7510385cf4 Merged in feature/payroll (pull request #1018)
Improve labor deduction for enhanced payroll.
2023-10-19 00:29:15 +00:00
Allan Carr
60a0222dd0 IO-2416 Filter out "" from ClaimsCorp and Autohouse get shop queries 2023-10-18 10:55:08 -07:00
Allan Carr
9114abd3ef IO-1559 Add in extra required tag 2023-10-18 10:48:47 -07:00
Patrick Fic
8796c8fe06 Merged in feature/payroll (pull request #1014)
Feature/payroll
2023-10-18 16:51:50 +00:00
Patrick Fic
f7fc0e6a6d Merged in feature/IO-2401-duplicate-chat-creation (pull request #1013)
IO-2401 Add prevention of duplicate chat creation by adding loading tracking.
2023-10-16 19:03:42 +00:00
Patrick Fic
ffebbe3b2a IO-2401 Add prevention of duplicate chat creation by adding loading tracking. 2023-10-16 12:00:11 -07:00
Patrick Fic
25b8c1b1eb Merged in feature/IO-2414-prod-list-delete (pull request #1012)
IO-2414 add null handling in case employee records are null.
2023-10-16 18:40:50 +00:00
Patrick Fic
17f8625108 IO-2414 add null handling in case employee records are null. 2023-10-16 11:40:23 -07:00
Patrick Fic
e3c21f0373 Merge branch 'release/2023-10-20' of bitbucket.org:snaptsoft/bodyshop into release/2023-10-20 2023-10-16 10:33:47 -07:00
Patrick Fic
859ff00277 Merge remote-tracking branch 'origin/master' into release/2023-10-20 2023-10-16 10:33:32 -07:00
Patrick Fic
e7c3be5231 Merged in release/2023-09-23 (pull request #1011)
Fix search select issues.
2023-10-16 17:24:46 +00:00
Patrick Fic
85e3c5a433 Fix search select issues. 2023-10-16 10:24:16 -07:00
Patrick Fic
d8d5cde3f1 Merged in feature/IO-2411-active-employee-filtering (pull request #1010)
IO-2411 filter inactive employees on prod. print button
2023-10-16 16:35:44 +00:00
Patrick Fic
6efa08fee3 IO-2411 filter inactive employees on prod. print button 2023-10-16 09:34:53 -07:00
Patrick Fic
636c13373c Merged in release/2023-09-23 (pull request #1009)
Release/2023 09 23
2023-10-16 16:19:04 +00:00
Patrick Fic
3659fbec84 Merged in 2023-09-29 (pull request #1004)
2023 09 29
2023-10-13 14:53:24 +00:00
Allan Carr
05f1a9b280 IO-1559 Adjust count object to new field tag 2023-10-12 14:54:58 -07:00
Allan Carr
5884d5eba0 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1005)
IO-1559 Adjust count object to new field tag
2023-10-12 21:52:54 +00:00
Allan Carr
4dd2137006 IO-1559 Change xml label as per Claimscorp 2023-10-12 11:24:24 -07:00
Allan Carr
03315836a6 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1002)
IO-1559 Change xml label as per Claimscorp
2023-10-12 18:23:39 +00:00
Patrick Fic
f703ba2cf9 Disable unnecessary events for Hasura. 2023-09-29 12:36:29 -07:00
Allan Carr
612e359d4c Merged in feature/IO-2408-Speedprint-TZ (pull request #990)
IO-2408 Remove unneeded library
2023-09-22 18:50:13 +00:00
Allan Carr
c8fb1ce302 IO-2408 Remove unneeded library 2023-09-22 11:51:41 -07:00
Allan Carr
b29d8e1912 IO-2408 Use bodyshop.timezone instead of UTCOffset in Speedprint 2023-09-22 11:34:09 -07:00
Allan Carr
74b6c2b6b5 Merged in feature/IO-2408-Speedprint-TZ (pull request #988)
IO-2408 Use bodyshop.timezone instead of UTCOffset in Speedprint
2023-09-22 18:33:25 +00:00
Allan Carr
fce8039dad Merged in feature/IO-2400-Courtesy-Car-Color-All-Cars (pull request #986)
IO-2400 Add Color column to All Courtesy Cars
2023-09-22 15:27:13 +00:00
Allan Carr
085ae141ae Merged in feature/IO-2407-InHouse-Audit-Log (pull request #984)
IO-2407 In House Invoice Audit Log correction for Invoice Number
2023-09-22 15:27:02 +00:00
Allan Carr
fbc9ccc018 Merged in feature/IO-2363-Labour-Allocation-Summary-Color-Coding (pull request #985)
IO-2363 Make Summary.Difference follow Color logic
2023-09-22 15:26:47 +00:00
Allan Carr
f2ede519d7 Merged in feature/IO-2398-Hours-Sold-By-Estimator (pull request #983)
IO-2398 Hours Sold by Estimator
2023-09-22 15:26:39 +00:00
Allan Carr
21c53473d3 IO-2400 Add Color column to All Courtesy Cars 2023-09-20 15:58:48 -07:00
Allan Carr
fbc622fa04 IO-2363 Make Summary.Difference follow Color logic 2023-09-20 14:30:53 -07:00
Allan Carr
6319fd20fa IO-2407 In House Invoice Audit Log correction for Invoice Number 2023-09-20 12:16:03 -07:00
Allan Carr
c998e4901f IO-2398 Hours Sold by Estimator 2023-09-18 18:25:05 -07:00
Patrick Fic
06c35a4ff8 Add additonal query limits and restrictors for performance. 2023-09-18 16:08:20 -07:00
59 changed files with 29019 additions and 2628 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project be_version="2.7.1" version="1.2">
<babeledit_project version="1.2" be_version="2.7.1">
<!--
BabelEdit project file
@@ -364,6 +364,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>unblock</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>viewjob</name>
<definition_loaded>false</definition_loaded>
@@ -24841,6 +24862,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>dms_model_override</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>dms_unsold</name>
<definition_loaded>false</definition_loaded>
@@ -44869,6 +44911,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_detail_closed_estimator</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_detail_closed_ins_co</name>
<definition_loaded>false</definition_loaded>
@@ -44953,6 +45016,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_detail_open_estimator</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_detail_open_ins_co</name>
<definition_loaded>false</definition_loaded>
@@ -45037,6 +45121,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_summary_closed_estimator</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_summary_closed_ins_co</name>
<definition_loaded>false</definition_loaded>
@@ -45121,6 +45226,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_summary_open_estimator</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>hours_sold_summary_open_ins_co</name>
<definition_loaded>false</definition_loaded>
@@ -45457,6 +45583,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>open_orders_referral</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>open_orders_specific_csr</name>
<definition_loaded>false</definition_loaded>

24522
client/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
"@apollo/client": "^3.7.9",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^7.0.0",
"@fingerprintjs/fingerprintjs": "^3.3.3",
"@fingerprintjs/fingerprintjs": "^3.4.2",
"@jsreport/browser-client": "^3.1.0",
"@sentry/react": "^7.40.0",
"@sentry/tracing": "^7.40.0",

View File

@@ -7,6 +7,75 @@
<meta name="theme-color" content="#002366" />
<meta name="description" content="Rome Online" />
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
<!--Use the below code snippet to provide real time updates to the live chat plugin without the need of copying and paste each time to your website when changes are made via PBX-->
<call-us-selector phonesystem-url=https://rometech.east.3cx.us:5001 party="LiveChat528346"></call-us-selector>
<!--Incase you don't want real time updates to the live chat plugin when options are changed, use the below code snippet. Please note that each time you change the settings you will need to copy and paste the snippet code to your website-->
<!--<call-us
phonesystem-url=https://rometech.east.3cx.us:5001
style="position:fixed;font-size:16px;line-height:17px;z-index: 99999;right: 20px; bottom: 20px;"
id="wp-live-chat-by-3CX"
minimized="true"
animation-style="noanimation"
party="LiveChat528346"
minimized-style="bubbleright"
allow-call="true"
allow-video="false"
allow-soundnotifications="true"
enable-mute="true"
enable-onmobile="true"
offline-enabled="true"
enable="true"
ignore-queueownership="false"
authentication="both"
show-operator-actual-name="true"
aknowledge-received="true"
gdpr-enabled="false"
message-userinfo-format="name"
message-dateformat="both"
lang="browser"
button-icon-type="default"
greeting-visibility="none"
greeting-offline-visibility="none"
chat-delay="2000"
enable-direct-call="true"
enable-ga="false"
></call-us>-->
<script defer src=https://downloads-global.3cx.com/downloads/livechatandtalk/v1/callus.js id="tcx-callus-js" charset="utf-8"></script>
<link rel="apple-touch-icon" href="logo192.png" />
<script>
!(function () {

View File

@@ -38,8 +38,8 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
insertAuditTrail: ({ jobid, billid, operation }) =>
dispatch(insertAuditTrail({ jobid, billid, operation })),
});
const Templates = TemplateList("job_special");
@@ -167,7 +167,7 @@ function BillEnterModalContainer({
},
],
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"],
});
await Promise.all(
@@ -354,7 +354,9 @@ function BillEnterModalContainer({
insertAuditTrail({
jobid: values.jobid,
billid: billId,
operation: AuditTrailMapping.billposted(remainingValues.invoice_number),
operation: AuditTrailMapping.billposted(
r1.data.insert_bills.returning[0].invoice_number
),
});
if (enterAgain) {

View File

@@ -50,6 +50,7 @@ export function BillFormComponent({
job,
loadOutstandingReturns,
loadInventory,
preferredMake,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -185,6 +186,7 @@ export function BillFormComponent({
<VendorSearchSelect
disabled={disabled}
options={vendorAutoCompleteOptions}
preferredMake={preferredMake}
onSelect={handleVendorSelect}
/>
</Form.Item>

View File

@@ -1,16 +1,16 @@
import { useLazyQuery, useQuery } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_OUTSTANDING_INVENTORY } from "../../graphql/inventory.queries";
import { GET_JOB_LINES_TO_ENTER_BILL } from "../../graphql/jobs-lines.queries";
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import BillFormComponent from "./bill-form.component";
import BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
import BillInventoryTable from "../bill-inventory-table/bill-inventory-table.component";
import { QUERY_OUTSTANDING_INVENTORY } from "../../graphql/inventory.queries";
import { useTreatments } from "@splitsoftware/splitio-react";
import BillFormComponent from "./bill-form.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -59,6 +59,7 @@ export function BillFormContainer({
disableInvNumber={disableInvNumber}
loadOutstandingReturns={loadOutstandingReturns}
loadInventory={loadInventory}
preferredMake={lineData ? lineData.jobs_by_pk.v_make_desc : null}
/>
{!billEdit && (
<BillCmdReturnsTableComponent

View File

@@ -8,15 +8,23 @@ import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { searchingForConversation } from "../../redux/messaging/messaging.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
searchingForConversation: searchingForConversation,
});
const mapDispatchToProps = (dispatch) => ({
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
});
export function ChatOpenButton({ bodyshop, phone, jobid, openChatByPhone }) {
export function ChatOpenButton({
bodyshop,
searchingForConversation,
phone,
jobid,
openChatByPhone,
}) {
const { t } = useTranslation();
if (!phone) return <></>;
@@ -29,7 +37,7 @@ export function ChatOpenButton({ bodyshop, phone, jobid, openChatByPhone }) {
onClick={(e) => {
e.stopPropagation();
const p = parsePhoneNumber(phone, "CA");
if (searchingForConversation) return; //This is to prevent finding the same thing twice.
if (p && p.isValid()) {
openChatByPhone({ phone_num: p.formatInternational(), jobid: jobid });
} else {

View File

@@ -9,15 +9,15 @@ import {
Table,
Tooltip,
} from "antd";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
import moment from "moment";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort } from "../../utils/sorters";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
const [state, setState] = useState({
sortedInfo: {},
@@ -115,6 +115,14 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
sortOrder:
state.sortedInfo.columnKey === "model" && state.sortedInfo.order,
},
{
title: t("courtesycars.fields.color"),
dataIndex: "color",
key: "color",
sorter: (a, b) => alphaSort(a.color, b.color),
sortOrder:
state.sortedInfo.columnKey === "color" && state.sortedInfo.order,
},
{
title: t("courtesycars.fields.plate"),
dataIndex: "plate",

View File

@@ -191,6 +191,13 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
>
<Switch />
</Form.Item>
<Form.Item
name="dms_model_override"
label={t("jobs.fields.dms.dms_model_override")}
initialValue={false}
>
<Switch />
</Form.Item>
</Space>
</div>
)}

View File

@@ -109,7 +109,7 @@ export function EmailOverlayContainer({
]
: []),
],
media: selectedMedia.filter((m) => m.isSelected).map((m) => m.src),
media: selectedMedia.filter((m) => m.isSelected).map((m) => m.fullsize),
//attachments,
});
notification["success"]({ message: t("emails.successes.sent") });

View File

@@ -4,6 +4,7 @@ import {
Divider,
Dropdown,
Form,
Input,
Menu,
notification,
Popover,
@@ -34,6 +35,8 @@ import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.
import ScheduleAtChange from "./job-at-change.component";
import ScheduleEventColor from "./schedule-event.color.component";
import ScheduleEventNote from "./schedule-event.note.component";
import { useMutation } from "@apollo/client";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -58,16 +61,44 @@ export function ScheduleEventComponent({
const [visible, setVisible] = useState(false);
const history = useHistory();
const searchParams = queryString.parse(useLocation().search);
const [updateAppointment] = useMutation(UPDATE_APPOINTMENT);
const [title, setTitle] = useState(event.title);
const blockContent = (
<div>
<Space direction="vertical" wrap>
<Input
value={title}
onChange={(e) => setTitle(e.currentTarget.value)}
onBlur={async () => {
await updateAppointment({
variables: {
appid: event.id,
app: {
title: title,
},
},
optimisticResponse: {
update_appointments: {
__typename: "appointments_mutation_response",
returning: [
{
...event,
title: title,
__typename: "appointments",
},
],
},
},
});
}}
/>
<Button
onClick={() => handleCancel({ id: event.id })}
disabled={event.arrived}
>
{t("appointments.actions.cancel")}
{t("appointments.actions.unblock")}
</Button>
</div>
</Space>
);
const popoverContent = (

View File

@@ -1,5 +1,6 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Form, Input, notification, Switch } from "antd";
import moment from "moment-business-days";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -12,16 +13,15 @@ import {
MARK_LATEST_APPOINTMENT_ARRIVED,
} from "../../../../graphql/appointments.queries";
import { UPDATE_JOB } from "../../../../graphql/jobs.queries";
import { UPDATE_OWNER } from "../../../../graphql/owners.queries";
import { insertAuditTrail } from "../../../../redux/application/application.actions";
import {
selectBodyshop,
selectCurrentUser,
} from "../../../../redux/user/user.selectors";
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
import ConfigFormComponents from "../../../config-form-components/config-form-components.component";
import DateTimePicker from "../../../form-date-time-picker/form-date-time-picker.component";
import moment from "moment-business-days";
import { insertAuditTrail } from "../../../../redux/application/application.actions";
import AuditTrailMapping from "../../../../utils/AuditTrailMappings";
import { UPDATE_OWNER } from "../../../../graphql/owners.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -230,6 +230,7 @@ export function JobChecklistForm({
)),
scheduled_delivery:
job.scheduled_delivery && moment(job.scheduled_delivery),
production_vars: job.production_vars,
}),
...(type === "deliver" && {
removeFromProduction: true,

View File

@@ -8,7 +8,7 @@ export default function JobLinesBillRefernece({ jobline }) {
return (
<div style={{ color: subletRequired && "tomato" }}>
{subletRequired && <WarningFilled />}
{`${(billLine.actual_price * billLine.quantity).toFixed(2)} (${
{`${billLine.actual_price.toFixed(2)} x ${billLine.quantity} (${
billLine.bill.vendor.name
} #${billLine.bill.invoice_number})`}
</div>

View File

@@ -33,7 +33,9 @@ const JobSearchSelect = (
useLazyQuery(SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE);
const executeSearch = (v) => {
if (v && v !== "") callSearch(v);
console.log(v);
if (v && v.variables?.search !== "" && v.variables.search.length >= 2)
callSearch(v);
};
const debouncedExecuteSearch = _.debounce(executeSearch, 500);

View File

@@ -108,7 +108,7 @@ export function JobsConvertButton({
},
]}
>
<Select>
<Select showSearch>
{bodyshop.md_ins_cos.map((s, i) => (
<Select.Option key={i} value={s.name}>
{s.name}

View File

@@ -27,6 +27,8 @@ export default async function DuplicateJob(
delete existingJob.id;
delete existingJob.createdat;
delete existingJob.updatedat;
delete existingJob.cieca_stl;
delete existingJob.cieca_ttl;
const newJob = {
...existingJob,
@@ -81,6 +83,8 @@ export async function CreateIouForJob(
delete existingJob.id;
delete existingJob.createdat;
delete existingJob.updatedat;
delete existingJob.cieca_stl;
delete existingJob.cieca_ttl;
const newJob = {
...existingJob,

View File

@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { GenerateThumbUrl } from "./job-documents.utility";
import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility";
function JobsDocumentGalleryExternal({
data,
@@ -15,7 +15,7 @@ function JobsDocumentGalleryExternal({
let documents = data.reduce((acc, value) => {
if (value.type.startsWith("image")) {
acc.push({
//src: GenerateSrcUrl(value),
fullsize: GenerateSrcUrl(value),
src: GenerateThumbUrl(value),
thumbnailHeight: 225,
thumbnailWidth: 225,

View File

@@ -52,7 +52,7 @@ function JobDocumentsLocalGalleryExternal({
val.type.mime &&
val.type.mime.startsWith("image")
) {
acc.push({ ...val, src: val.thumbnail });
acc.push({ ...val, src: val.thumbnail, fullsize: val.src });
}
return acc;
}, [])

View File

@@ -231,7 +231,14 @@ export function LaborAllocationsTable({
{summary.adjustments.toFixed(1)}
</Table.Summary.Cell>
<Table.Summary.Cell>
{summary.difference.toFixed(1)}
<Typography.Text
style={{
fontWeight: "bold",
color: summary.difference >= 0 ? "green" : "red",
}}
>
{summary.difference.toFixed(1)}
</Typography.Text>
</Table.Summary.Cell>
</Table.Summary.Row>
)}

View File

@@ -21,7 +21,8 @@ const OwnerSearchSelect = ({ value, onChange, onBlur, disabled }, ref) => {
useLazyQuery(SEARCH_OWNERS_BY_ID_FOR_AUTOCOMPLETE);
const executeSearch = (v) => {
callSearch(v);
if (v && v.variables?.search !== "" && v.variables.search.length >= 2)
callSearch(v);
};
const debouncedExecuteSearch = _.debounce(executeSearch, 500);

View File

@@ -106,7 +106,11 @@ export default function OwnersListComponent({
<Input.Search
placeholder={search.search || t("general.labels.search")}
onSearch={(value) => {
search.search = value;
if (value?.length >= 3) {
search.search = value;
} else {
delete search.search;
}
history.push({ search: queryString.stringify(search) });
}}
enterButton

View File

@@ -480,8 +480,8 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.order,
sorter: (a, b) =>
alphaSort(
bodyshop.employees.find((e) => e.id === a.employee_body)?.first_name,
bodyshop.employees.find((e) => e.id === b.employee_body)?.first_name
bodyshop.employees?.find((e) => e.id === a.employee_body)?.first_name,
bodyshop.employees?.find((e) => e.id === b.employee_body)?.first_name
),
render: (text, record) => (
<ProductionListEmployeeAssignment
@@ -499,8 +499,8 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.order,
sorter: (a, b) =>
alphaSort(
bodyshop.employees.find((e) => e.id === a.employee_prep)?.first_name,
bodyshop.employees.find((e) => e.id === b.employee_prep)?.first_name
bodyshop.employees?.find((e) => e.id === a.employee_prep)?.first_name,
bodyshop.employees?.find((e) => e.id === b.employee_prep)?.first_name
),
render: (text, record) => (
<ProductionListEmployeeAssignment
@@ -517,8 +517,8 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.columnKey === "employee_csr" && state.sortedInfo.order,
sorter: (a, b) =>
alphaSort(
bodyshop.employees.find((e) => e.id === a.employee_csr)?.first_name,
bodyshop.employees.find((e) => e.id === b.employee_csr)?.first_name
bodyshop.employees?.find((e) => e.id === a.employee_csr)?.first_name,
bodyshop.employees?.find((e) => e.id === b.employee_csr)?.first_name
),
render: (text, record) => (
<ProductionListEmployeeAssignment record={record} type="employee_csr" />
@@ -533,9 +533,9 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.order,
sorter: (a, b) =>
alphaSort(
bodyshop.employees.find((e) => e.id === a.employee_refinish)
bodyshop.employees?.find((e) => e.id === a.employee_refinish)
?.first_name,
bodyshop.employees.find((e) => e.id === b.employee_refinish)
bodyshop.employees?.find((e) => e.id === b.employee_refinish)
?.first_name
),
render: (text, record) => (

View File

@@ -55,25 +55,27 @@ export function ProductionListPrint({ bodyshop }) {
<Menu.SubMenu
title={t("reportcenter.templates.production_by_technician_one")}
>
{bodyshop.employees.map((e) => (
<Menu.Item
key={e.id}
onClick={async () => {
setLoading(true);
await GenerateDocument(
{
name: production_by_technician_one.key,
variables: { id: e.id },
},
{},
"p"
);
setLoading(false);
}}
>
{e.first_name} {e.last_name}
</Menu.Item>
))}
{bodyshop.employees
.filter((e) => e.active)
.map((e) => (
<Menu.Item
key={e.id}
onClick={async () => {
setLoading(true);
await GenerateDocument(
{
name: production_by_technician_one.key,
variables: { id: e.id },
},
{},
"p"
);
setLoading(false);
}}
>
{e.first_name} {e.last_name}
</Menu.Item>
))}
</Menu.SubMenu>
<Menu.SubMenu
title={t("reportcenter.templates.production_by_category_one")}

View File

@@ -16,13 +16,17 @@ import { QUERY_LBR_HRS_BY_PK, UPDATE_JOBS } from "../../graphql/jobs.queries";
import { setEmailOptions } from "../../redux/email/email.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectSchedule } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { TemplateList } from "../../utils/TemplateConstants";
import ScheduleJobModalComponent from "./schedule-job-modal.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
scheduleModal: selectSchedule,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("schedule")),
@@ -34,6 +38,7 @@ export function ScheduleJobModalContainer({
bodyshop,
toggleModalVisible,
setEmailOptions,
currentUser,
}) {
const { visible, context, actions } = scheduleModal;
const { jobId, job, previousEvent } = context;
@@ -122,6 +127,7 @@ export function ScheduleJobModalContainer({
end: moment(values.start).add(bodyshop.appt_length || 60, "minutes"),
color: values.color,
note: values.note,
created_by: currentUser.email,
},
jobId: jobId,
altTransport: values.alt_transport,

View File

@@ -8,18 +8,45 @@ export const CalculateWorkingDaysThisMonth = () => {
return moment().endOf("month").businessDaysIntoMonth();
};
export const CalculateWorkingDaysInPeriod = (start, end) => {
return moment(start).businessDiff(moment(end));
};
export const CalculateWorkingDaysAsOfToday = () => {
return moment().businessDaysIntoMonth();
};
export const CalculateWorkingDaysLastMonth = () => {
return moment().subtract(1, "month").endOf("month").businessDaysIntoMonth();
};
export const WeeklyTargetHrs = (dailyTargetHrs, bodyshop) => {
return dailyTargetHrs * 5;
return (
dailyTargetHrs *
CalculateWorkingDaysInPeriod(
moment().startOf("week"),
moment().endOf("week")
)
);
};
export const WeeklyTargetHrsInPeriod = (
dailyTargetHrs,
start,
end,
bodyshop
) => {
return dailyTargetHrs * CalculateWorkingDaysInPeriod(start, end);
};
export const MonthlyTargetHrs = (dailyTargetHrs, bodyshop) => {
return dailyTargetHrs * CalculateWorkingDaysThisMonth();
};
export const LastMonthTargetHrs = (dailyTargetHrs, bodyshop) => {
return dailyTargetHrs * CalculateWorkingDaysLastMonth();
};
export const AsOfTodayTargetHrs = (dailyTargetHrs, bodyshop) => {
return dailyTargetHrs * CalculateWorkingDaysAsOfToday();
};

View File

@@ -0,0 +1,26 @@
const CustomTooltip = ({ active, payload, label }) => {
if (active && payload && payload.length) {
return (
<div
style={{
backgroundColor: "white",
border: "1px solid gray",
padding: "0.5rem",
}}
>
<p style={{ margin: "0" }}>{label}</p>
{payload.map((data, index) => {
return (
<p style={{ margin: "10px 0", color: data.color }} key={index}>{`${
data.name
} : ${data.value.toFixed(1)}`}</p>
);
})}
</div>
);
}
return null;
};
export default CustomTooltip;

View File

@@ -0,0 +1,54 @@
import { Card } from "antd";
import React from "react";
import {
Area,
CartesianGrid,
ComposedChart,
Legend,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import CustomTooltip from "./chart-custom-tooltip";
const graphProps = {
strokeWidth: 3,
};
export default function ScoreboardTimeTicketsChart({ data, chartTitle }) {
return (
<Card title={chartTitle}>
<ResponsiveContainer width="100%" height={275}>
<ComposedChart
data={data}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" strokeWidth={graphProps.strokeWidth} />
<YAxis yAxisId="left" strokeWidth={graphProps.strokeWidth} />
<Tooltip content={<CustomTooltip />} />
<Legend />
<Line
name="Target Hours"
type="monotone"
dataKey="accTargetHrs"
stroke="#ff7300"
yAxisId="left"
strokeWidth={graphProps.strokeWidth}
/>
<Area
type="monotone"
name="MTD Hours"
dataKey="accHrs"
fill="lightblue"
stroke="blue"
yAxisId="left"
/>
</ComposedChart>
</ResponsiveContainer>
</Card>
);
}

View File

@@ -0,0 +1,399 @@
import { useQuery } from "@apollo/client";
import { Col, Row } from "antd";
import _ from "lodash";
import moment from "moment";
import queryString from "query-string";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_TIME_TICKETS_IN_RANGE_SB } from "../../graphql/timetickets.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
import ScoreboardTimeTicketsChart from "./scoreboard-timetickets.chart.component";
import ScoreboardTicketsStats from "./scoreboard-timetickets.stats.component";
import ScoreboardTimeticketsTargetsTable from "./scoreboard-timetickets.targets-table.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ScoreboardTimeTicketsStats);
export function ScoreboardTimeTicketsStats({ bodyshop }) {
const { t } = useTranslation();
const searchParams = queryString.parse(useLocation().search);
const { start, end } = searchParams;
const startDate = start
? moment(start)
: moment().startOf("week").subtract(7, "days");
const endDate = end ? moment(end) : moment().endOf("week");
const fixedPeriods = useMemo(() => {
const endOfThisMonth = moment().endOf("month");
const startofthisMonth = moment().startOf("month");
const endOfLastmonth = moment().subtract(1, "month").endOf("month");
const startOfLastmonth = moment().subtract(1, "month").startOf("month");
const endOfThisWeek = moment().endOf("week");
const startOfThisWeek = moment().startOf("week");
const endOfLastWeek = moment().subtract(1, "week").endOf("week");
const startOfLastWeek = moment().subtract(1, "week").startOf("week");
const endOfPriorWeek = moment().subtract(2, "week").endOf("week");
const startOfPriorWeek = moment().subtract(2, "week").startOf("week");
const allDates = [
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
endOfPriorWeek,
startOfPriorWeek,
];
const start = moment.min(allDates);
const end = moment.max(allDates);
return {
start,
end,
endOfThisMonth,
startofthisMonth,
endOfLastmonth,
startOfLastmonth,
endOfThisWeek,
startOfThisWeek,
endOfLastWeek,
startOfLastWeek,
endOfPriorWeek,
startOfPriorWeek,
};
}, []);
const { loading, error, data } = useQuery(QUERY_TIME_TICKETS_IN_RANGE_SB, {
variables: {
start: startDate.format("YYYY-MM-DD"),
end: endDate.format("YYYY-MM-DD"),
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
pollInterval: 60000,
skip: !fixedPeriods,
});
const calculatedData = useMemo(() => {
if (!data) return [];
const ret = {
totalThisWeek: 0,
totalThisWeekLAB: 0,
totalThisWeekLAR: 0,
totalLastWeek: 0,
totalLastWeekLAB: 0,
totalLastWeekLAR: 0,
totalPriorWeek: 0,
totalPriorWeekLAB: 0,
totalPriorWeekLAR: 0,
totalThisMonth: 0,
totalThisMonthLAB: 0,
totalThisMonthLAR: 0,
totalLastMonth: 0,
totalLastMonthLAB: 0,
totalLastMonthLAR: 0,
actualTotalOverPeriod: 0,
actualTotalOverPeriodLAB: 0,
actualTotalOverPeriodLAR: 0,
totalEffieciencyOverPeriod: 0,
totalEffieciencyOverPeriodLAB: 0,
totalEffieciencyOverPeriodLAR: 0,
seperatedThisWeek: {
sunday: {
total: 0,
lab: 0,
lar: 0,
},
monday: {
total: 0,
lab: 0,
lar: 0,
},
tuesday: {
total: 0,
lab: 0,
lar: 0,
},
wednesday: {
total: 0,
lab: 0,
lar: 0,
},
thursday: {
total: 0,
lab: 0,
lar: 0,
},
friday: {
total: 0,
lab: 0,
lar: 0,
},
saturday: {
total: 0,
lab: 0,
lar: 0,
},
},
};
data.fixedperiod.forEach((ticket) => {
const ticketDate = moment(ticket.date);
if (
ticketDate.isBetween(
fixedPeriods.startOfThisWeek,
fixedPeriods.endOfThisWeek,
undefined,
"[]"
)
) {
ret.totalThisWeek = ret.totalThisWeek + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalThisWeekLAB = ret.totalThisWeekLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalThisWeekLAR = ret.totalThisWeekLAR + ticket.productivehrs;
//Seperate out to Day of Week
ret.seperatedThisWeek[
moment(ticket.date).format("dddd").toLowerCase()
].total =
ret.seperatedThisWeek[
moment(ticket.date).format("dddd").toLowerCase()
].total + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.seperatedThisWeek[
moment(ticket.date).format("dddd").toLowerCase()
].lab =
ret.seperatedThisWeek[
moment(ticket.date).format("dddd").toLowerCase()
].lab + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.seperatedThisWeek[
moment(ticket.date).format("dddd").toLowerCase()
].lar =
ret.seperatedThisWeek[
moment(ticket.date).format("dddd").toLowerCase()
].lar + ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastWeek,
fixedPeriods.endOfLastWeek,
undefined,
"[]"
)
) {
ret.totalLastWeek = ret.totalLastWeek + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalLastWeekLAB = ret.totalLastWeekLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalLastWeekLAR = ret.totalLastWeekLAR + ticket.productivehrs;
} else if (
ticketDate.isBetween(
fixedPeriods.startOfPriorWeek,
fixedPeriods.endOfPriorWeek,
undefined,
"[]"
)
) {
ret.totalPriorWeek = ret.totalPriorWeek + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalPriorWeekLAB = ret.totalPriorWeekLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalPriorWeekLAR = ret.totalPriorWeekLAR + ticket.productivehrs;
}
if (
ticketDate.isBetween(
fixedPeriods.startofthisMonth,
fixedPeriods.endOfThisMonth,
undefined,
"[]"
)
) {
ret.totalThisMonth = ret.totalThisMonth + ticket.productivehrs;
ret.actualTotalOverPeriod =
ret.actualTotalOverPeriod + (ticket.actualhrs || 0);
if (ticket.ciecacode !== "LAR") {
ret.totalThisMonthLAB = ret.totalThisMonthLAB + ticket.productivehrs;
ret.actualTotalOverPeriodLAB =
ret.actualTotalOverPeriodLAB + (ticket.actualhrs || 0);
}
if (ticket.ciecacode === "LAR") {
ret.totalThisMonthLAR = ret.totalThisMonthLAR + ticket.productivehrs;
ret.actualTotalOverPeriodLAR =
ret.actualTotalOverPeriodLAR + (ticket.actualhrs || 0);
}
} else if (
ticketDate.isBetween(
fixedPeriods.startOfLastmonth,
fixedPeriods.endOfLastmonth,
undefined,
"[]"
)
) {
ret.totalLastMonth = ret.totalLastMonth + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
ret.totalLastMonthLAB = ret.totalLastMonthLAB + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
ret.totalLastMonthLAR = ret.totalLastMonthLAR + ticket.productivehrs;
}
});
ret.totalEffieciencyOverPeriod = ret.actualTotalOverPeriod
? (ret.totalThisMonth / ret.actualTotalOverPeriod) * 100
: 0;
ret.totalEffieciencyOverPeriodLAB = ret.actualTotalOverPeriodLAB
? (ret.totalThisMonthLAB / ret.actualTotalOverPeriodLAB) * 100
: 0;
ret.totalEffieciencyOverPeriodLAR = ret.actualTotalOverPeriodLAR
? (ret.totalThisMonthLAR / ret.actualTotalOverPeriodLAR) * 100
: 0;
roundObject(ret);
const ticketsGroupedByDate = _.groupBy(data.timetickets, "date");
const listOfDays = Utils.ListOfDaysInCurrentMonth();
const combinedData = [],
labData = [],
larData = [];
var acc_comb = 0;
var acc_lab = 0;
var acc_lar = 0;
listOfDays.forEach((day) => {
const r = {
date: moment(day).format("MM/DD"),
actualhrs: 0,
productivehrs: 0,
};
const combined = {
accTargetHrs: _.round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyBodyTarget +
bodyshop.scoreboard_target.dailyPaintTarget,
day
) +
(bodyshop.scoreboard_target.dailyBodyTarget +
bodyshop.scoreboard_target.dailyPaintTarget),
1
),
accHrs: 0,
};
const lab = {
accTargetHrs: _.round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyBodyTarget,
day
) + bodyshop.scoreboard_target.dailyBodyTarget,
1
),
accHrs: 0,
};
const lar = {
accTargetHrs: _.round(
Utils.AsOfDateTargetHours(
bodyshop.scoreboard_target.dailyPaintTarget,
day
) + bodyshop.scoreboard_target.dailyPaintTarget,
1
),
accHrs: 0,
};
if (ticketsGroupedByDate[day]) {
ticketsGroupedByDate[day].forEach((ticket) => {
r.actualhrs = r.actualhrs + ticket.actualhrs;
r.productivehrs = r.productivehrs + ticket.productivehrs;
acc_comb = acc_comb + ticket.productivehrs;
if (ticket.ciecacode !== "LAR")
acc_lab = acc_lab + ticket.productivehrs;
if (ticket.ciecacode === "LAR")
acc_lar = acc_lar + ticket.productivehrs;
});
}
combined.accHrs = acc_comb;
lab.accHrs = acc_lab;
lar.accHrs = acc_lar;
combinedData.push({ ...r, ...combined });
labData.push({ ...r, ...lab });
larData.push({ ...r, ...lar });
});
return {
fixed: ret,
combinedData: combinedData,
labData: labData,
larData: larData,
};
}, [fixedPeriods, data, bodyshop]);
if (error) return <AlertComponent message={error.message} type="error" />;
if (loading) return <LoadingSpinner />;
return (
<Row gutter={[16, 16]}>
<Col span={24}>
<ScoreboardTimeticketsTargetsTable />
</Col>
<Col span={24}>
<ScoreboardTicketsStats data={calculatedData.fixed} />
</Col>
<Col span={24}>
<ScoreboardTimeTicketsChart
data={calculatedData.combinedData}
chartTitle={t("scoreboard.labels.combinedcharttitle")}
/>
</Col>
<Col span={12}>
<ScoreboardTimeTicketsChart
data={calculatedData.labData}
chartTitle={t("scoreboard.labels.bodycharttitle")}
/>
</Col>
<Col span={12}>
<ScoreboardTimeTicketsChart
data={calculatedData.larData}
chartTitle={t("scoreboard.labels.refinishcharttitle")}
/>
</Col>
</Row>
);
}
function roundObject(inputObj) {
for (var key of Object.keys(inputObj)) {
if (typeof inputObj[key] === "number") {
inputObj[key] = inputObj[key].toFixed(1);
} else if (Array.isArray(inputObj[key])) {
inputObj[key].forEach((item) => roundObject(item));
} else if (typeof inputObj[key] === "object") {
roundObject(inputObj[key]);
}
}
}

View File

@@ -0,0 +1,617 @@
import {
Card,
Col,
Form,
Row,
Space,
Statistic,
Switch,
Typography,
} from "antd";
import moment from "moment";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import * as Util from "../scoreboard-targets-table/scoreboard-targets-table.util";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ScoreboardTicketsStats);
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(storedValue));
}, [key, storedValue]);
return [storedValue, setStoredValue];
}
export function ScoreboardTicketsStats({ data, bodyshop }) {
const { t } = useTranslation();
const [isLarge, setIsLarge] = useLocalStorage("isLargeStatistic", false);
const statisticSize = isLarge ? 36 : 24;
const statisticWeight = isLarge ? 550 : "normal";
const daySpan =
Util.CalculateWorkingDaysInPeriod(
moment().startOf("week"),
moment().endOf("week")
) > 5
? 3
: 4;
return (
<Card
title={t("scoreboard.labels.productivestatistics")}
extra={
<Form.Item
label={t("general.labels.tvmode")}
valuePropName="checked"
name={["tvmode"]}
>
<Switch
onClick={() => setIsLarge(!isLarge)}
defaultChecked={isLarge}
/>
</Form.Item>
}
>
<Row gutter={[16, 16]}>
<Col md={24}>
{/* Daily Stats */}
<Space direction="vertical" size="middle" style={{ display: "flex" }}>
<Row gutter={[16, 16]} align="center">
{[
"sunday",
"monday",
"tuesday",
"wednesday",
"thursday",
"friday",
"saturday",
].map((day) => {
if (bodyshop.workingdays[day] === true) {
return (
<Col key={day} span={daySpan} align="center">
<Card size="small" title={t("general.labels." + day)}>
<Row gutter={[8, 8]}>
<Col span={24}>
<Statistic
value={data.seperatedThisWeek[day].total}
valueStyle={{
color:
parseFloat(
data.seperatedThisWeek[day].total
) >=
bodyshop.scoreboard_target.dailyBodyTarget +
bodyshop.scoreboard_target.dailyPaintTarget
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.body")}
</Typography.Text>
}
value={data.seperatedThisWeek[day].lab}
valueStyle={{
color:
parseFloat(data.seperatedThisWeek[day].lab) >=
bodyshop.scoreboard_target.dailyBodyTarget
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.refinish")}
</Typography.Text>
}
value={data.seperatedThisWeek[day].lar}
valueStyle={{
color:
parseFloat(data.seperatedThisWeek[day].lar) >=
bodyshop.scoreboard_target.dailyPaintTarget
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
);
} else {
return null;
}
})}
</Row>
{/* Weekly Stats */}
<Row gutter={[16, 16]}>
{/* This Week */}
<Col span={8} align="center">
<Card size="small" title={t("scoreboard.labels.thisweek")}>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
value={data.totalThisWeek}
valueStyle={{
color:
parseFloat(data.totalThisWeek) >=
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().startOf("week"),
moment().endOf("week"),
bodyshop
) +
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().startOf("week"),
moment().endOf("week"),
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.body")}
</Typography.Text>
}
value={data.totalThisWeekLAB}
valueStyle={{
color:
parseFloat(data.totalThisWeekLAB) >=
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().startOf("week"),
moment().endOf("week"),
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.refinish")}
</Typography.Text>
}
value={data.totalThisWeekLAR}
valueStyle={{
color:
parseFloat(data.totalThisWeekLAR) >=
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().startOf("week"),
moment().endOf("week"),
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
{/* Last Week */}
<Col span={8} align="center">
<Card size="small" title={t("scoreboard.labels.lastweek")}>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
value={data.totalLastWeek}
valueStyle={{
color:
parseFloat(data.totalLastWeek) >=
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().subtract(1, "week").startOf("week"),
moment().subtract(1, "week").endOf("week"),
bodyshop
) +
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().subtract(1, "week").startOf("week"),
moment().subtract(1, "week").endOf("week"),
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.body")}
</Typography.Text>
}
value={data.totalLastWeekLAB}
valueStyle={{
color:
parseFloat(data.totalLastWeekLAB) >=
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().subtract(1, "week").startOf("week"),
moment().subtract(1, "week").endOf("week"),
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.refinish")}
</Typography.Text>
}
value={data.totalLastWeekLAR}
valueStyle={{
color:
parseFloat(data.totalLastWeekLAR) >=
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().subtract(1, "week").startOf("week"),
moment().subtract(1, "week").endOf("week"),
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
{/* Prior Week */}
<Col span={8} align="center">
<Card size="small" title={t("scoreboard.labels.priorweek")}>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
value={data.totalPriorWeek}
valueStyle={{
color:
parseFloat(data.totalPriorWeek) >=
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().subtract(2, "week").startOf("week"),
moment().subtract(2, "week").endOf("week"),
bodyshop
) +
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().subtract(2, "week").startOf("week"),
moment().subtract(2, "week").endOf("week"),
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.body")}
</Typography.Text>
}
value={data.totalPriorWeekLAB}
valueStyle={{
color:
parseFloat(data.totalPriorWeekLAB) >=
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().subtract(2, "week").startOf("week"),
moment().subtract(2, "week").endOf("week"),
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.refinish")}
</Typography.Text>
}
value={data.totalPriorWeekLAR}
valueStyle={{
color:
parseFloat(data.totalPriorWeekLAR) >=
Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().subtract(2, "week").startOf("week"),
moment().subtract(2, "week").endOf("week"),
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
</Row>
{/* Monthly Stats */}
<Row gutter={[16, 16]}>
{/* This Month */}
<Col span={8} align="center">
<Card size="small" title={t("scoreboard.labels.thismonth")}>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
value={data.totalThisMonth}
valueStyle={{
color:
parseFloat(data.totalThisMonth) >=
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.body")}
</Typography.Text>
}
value={data.totalThisMonthLAB}
valueStyle={{
color:
parseFloat(data.totalThisMonthLAB) >=
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.refinish")}
</Typography.Text>
}
value={data.totalThisMonthLAR}
valueStyle={{
color:
parseFloat(data.totalThisMonthLAR) >=
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
{/* Last Month */}
<Col span={8} align="center">
<Card size="small" title={t("scoreboard.labels.lastmonth")}>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
value={data.totalLastMonth}
valueStyle={{
color:
parseFloat(data.totalLastMonth) >=
Util.LastMonthTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.LastMonthTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.body")}
</Typography.Text>
}
value={data.totalLastMonthLAB}
valueStyle={{
color:
parseFloat(data.totalLastMonthLAB) >=
Util.LastMonthTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.refinish")}
</Typography.Text>
}
value={data.totalLastMonthLAR}
valueStyle={{
color:
parseFloat(data.totalLastMonthLAR) >=
Util.LastMonthTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
? "green"
: "red",
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
{/* Efficiency Over Period */}
<Col span={8} align="center">
<Card
size="small"
title={t("scoreboard.labels.efficiencyoverperiod")}
>
<Row gutter={[16, 16]}>
<Col span={24}>
<Statistic
value={`${data.totalEffieciencyOverPeriod || 0}%`}
valueStyle={{
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
<Row gutter={[16, 16]}>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.body")}
</Typography.Text>
}
value={`${data.totalEffieciencyOverPeriodLAB || 0}%`}
valueStyle={{
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
<Col span={12}>
<Statistic
title={
<Typography.Text strong>
{t("scoreboard.labels.refinish")}
</Typography.Text>
}
value={`${data.totalEffieciencyOverPeriodLAR || 0}%`}
valueStyle={{
fontSize: statisticSize,
fontWeight: statisticWeight,
}}
/>
</Col>
</Row>
</Card>
</Col>
</Row>
</Space>
{/* Disclaimer */}
<Typography.Text type="secondary">
*{t("scoreboard.labels.calendarperiod")}
</Typography.Text>
</Col>
</Row>
</Card>
);
}

View File

@@ -0,0 +1,285 @@
import { CalendarOutlined } from "@ant-design/icons";
import { Card, Col, Divider, Row, Statistic } from "antd";
import moment from "moment";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import * as Util from "../scoreboard-targets-table/scoreboard-targets-table.util";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
const rowGutter = [16, 16];
const statSpans = { xs: 24, sm: 3 };
export function ScoreboardTimeticketsTargetsTable({ bodyshop }) {
const { t } = useTranslation();
return (
<Card title={t("scoreboard.labels.targets")}>
<Row gutter={rowGutter}>
<Col xs={24} sm={{ offset: 0, span: 3 }} lg={{ span: 3 }}>
<Statistic
title={t("scoreboard.labels.workingdays")}
value={Util.CalculateWorkingDaysThisMonth()}
prefix={<CalendarOutlined />}
/>
</Col>
<Col xs={24} sm={{ offset: 0, span: 20 }} lg={{ offset: 0, span: 20 }}>
<Row>
<Col {...statSpans}>
<Statistic title="Type" value={t("scoreboard.labels.body")} />
</Col>
<Col {...statSpans}>
<Statistic
title={t("scoreboard.labels.dailytarget")}
value={bodyshop.scoreboard_target.dailyBodyTarget}
/>
</Col>
<Col {...statSpans}>
<Statistic
title={t("scoreboard.labels.thisweek")}
value={Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().startOf("week"),
moment().endOf("week"),
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
title={t("scoreboard.labels.lastweek")}
value={Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().subtract(1, "week").startOf("week"),
moment().subtract(1, "week").endOf("week"),
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
title={t("scoreboard.labels.priorweek")}
value={Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().subtract(2, "week").startOf("week"),
moment().subtract(2, "week").endOf("week"),
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
title={t("scoreboard.labels.thismonth")}
value={Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
title={t("scoreboard.labels.lastmonth")}
value={Util.LastMonthTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
title={t("scoreboard.labels.asoftodaytarget")}
value={Util.AsOfTodayTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
)}
/>
</Col>
</Row>
<Row>
<Col {...statSpans}>
<Statistic value={t("scoreboard.labels.refinish")} />
</Col>
<Col {...statSpans}>
<Statistic value={bodyshop.scoreboard_target.dailyPaintTarget} />
</Col>
<Col {...statSpans}>
<Statistic
value={Util.WeeklyTargetHrsInPeriod(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().startOf("week"),
moment().endOf("week"),
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().subtract(1, "week").startOf("week"),
moment().subtract(1, "week").endOf("week"),
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().subtract(2, "week").startOf("week"),
moment().subtract(2, "week").endOf("week"),
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={Util.LastMonthTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={Util.AsOfTodayTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)}
/>
</Col>
</Row>
<Row>
<Divider style={{ margin: 5 }} />
</Row>
<Row>
<Col {...statSpans}></Col>
<Col {...statSpans}>
<Statistic
value={(
bodyshop.scoreboard_target.dailyBodyTarget +
bodyshop.scoreboard_target.dailyPaintTarget
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().startOf("week"),
moment().endOf("week"),
bodyshop
) +
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().startOf("week"),
moment().endOf("week"),
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().subtract(1, "week").startOf("week"),
moment().subtract(1, "week").endOf("week"),
bodyshop
) +
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().subtract(1, "week").startOf("week"),
moment().subtract(1, "week").endOf("week"),
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
moment().subtract(2, "week").startOf("week"),
moment().subtract(2, "week").endOf("week"),
bodyshop
) +
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
moment().subtract(2, "week").startOf("week"),
moment().subtract(2, "week").endOf("week"),
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(
Util.LastMonthTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.LastMonthTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(
Util.AsOfTodayTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.AsOfTodayTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
).toFixed(1)}
/>
</Col>
</Row>
</Col>
</Row>
</Card>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ScoreboardTimeticketsTargetsTable);

View File

@@ -16,13 +16,12 @@ const VehicleSearchSelect = ({ value, onChange, onBlur, disabled }, ref) => {
SEARCH_VEHICLES_FOR_AUTOCOMPLETE
);
const [
callIdSearch,
{ loading: idLoading, error: idError, data: idData },
] = useLazyQuery(SEARCH_VEHICLES_BY_ID_FOR_AUTOCOMPLETE);
const [callIdSearch, { loading: idLoading, error: idError, data: idData }] =
useLazyQuery(SEARCH_VEHICLES_BY_ID_FOR_AUTOCOMPLETE);
const executeSearch = (v) => {
callSearch(v);
if (v && v.variables?.search !== "" && v.variables.search.length >= 2)
callSearch(v);
};
const debouncedExecuteSearch = _.debounce(executeSearch, 500);

View File

@@ -154,7 +154,6 @@ export function VendorsFormComponent({
label={t("vendors.fields.phone")}
name="phone"
rules={[
{ required: true, message: t("general.validation.required") },
({ getFieldValue }) =>
PhoneItemFormatterValidation(getFieldValue, "phone"),
]}

View File

@@ -5,6 +5,7 @@ export const INSERT_NEW_BILL = gql`
insert_bills(objects: $bill) {
returning {
id
invoice_number
}
}
}

View File

@@ -283,6 +283,7 @@ export const GET_JOB_LINES_TO_ENTER_BILL = gql`
id
ro_number
}
v_make_desc
}
}
`;

View File

@@ -1298,7 +1298,7 @@ export const SEARCH_JOBS_FOR_AUTOCOMPLETE = gql`
) {
search_jobs(
args: { search: $search }
limit: 50
limit: 25
where: {
_and: {
converted: { _eq: $isConverted }

View File

@@ -36,7 +36,7 @@ export const SEARCH_OWNERS_FOR_AUTOCOMPLETE = gql`
query SEARCH_OWNERS_FOR_AUTOCOMPLETE($search: String) {
search_owners(
args: { search: $search }
limit: 50
limit: 25
order_by: { ownr_ln: desc_nulls_last }
) {
id

View File

@@ -154,7 +154,7 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
$fixedEnd: date!
) {
timetickets(
where: { date: { _gte: $start, _lte: $end } }
where: { date: { _gte: $start, _lte: $end }, cost_center: {_neq: "timetickets.labels.shift"} }
order_by: { date: desc_nulls_first }
) {
actualhrs
@@ -189,7 +189,7 @@ export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
}
}
fixedperiod: timetickets(
where: { date: { _gte: $fixedStart, _lte: $fixedEnd } }
where: { date: { _gte: $fixedStart, _lte: $fixedEnd }, cost_center: {_neq: "timetickets.labels.shift"} }
order_by: { date: desc_nulls_first }
) {
actualhrs

View File

@@ -184,7 +184,7 @@ export const SEARCH_VEHICLES_BY_ID_FOR_AUTOCOMPLETE = gql`
`;
export const SEARCH_VEHICLES_FOR_AUTOCOMPLETE = gql`
query SEARCH_VEHICLES_FOR_AUTOCOMPLETE($search: String) {
search_vehicles(args: { search: $search }, limit: 50) {
search_vehicles(args: { search: $search }, limit: 25) {
id
v_vin
v_model_yr

View File

@@ -104,6 +104,7 @@ export const SEARCH_VENDOR_AUTOCOMPLETE = gql`
id
cost_center
active
favorite
}
}
`;

View File

@@ -1,21 +1,22 @@
import Icon, { FieldTimeOutlined } from "@ant-design/icons";
import { Tabs } from "antd";
import queryString from "query-string";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { FaShieldAlt } from "react-icons/fa";
import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
import ScoreboardTimeTicketsStats from "../../components/scoreboard-timetickets-stats/scoreboard-timetickets.component";
import ScoreboardTimeTickets from "../../components/scoreboard-timetickets/scoreboard-timetickets.component";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import queryString from "query-string";
import { useHistory, useLocation } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -71,7 +72,7 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
tab={
<span>
<FieldTimeOutlined />
{t("scoreboard.labels.timetickets")}
{t("scoreboard.labels.timeticketsemployee")}
</span>
}
destroyInactiveTabPane
@@ -79,6 +80,18 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
>
<ScoreboardTimeTickets />
</Tabs.TabPane>
<Tabs.TabPane
tab={
<span>
<FieldTimeOutlined />
{t("scoreboard.labels.allemployeetimetickets")}
</span>
}
destroyInactiveTabPane
key="ticketsstats"
>
<ScoreboardTimeTicketsStats />
</Tabs.TabPane>
</Tabs>
</RbacWrapper>
</FeatureWrapper>

View File

@@ -6,6 +6,7 @@ const INITIAL_STATE = {
isSending: false,
error: null,
message: null,
searchingForConversation: false,
};
const messagingReducer = (state = INITIAL_STATE, action) => {
@@ -17,10 +18,16 @@ const messagingReducer = (state = INITIAL_STATE, action) => {
...state,
visible: !state.visible,
};
case MessagingActionTypes.OPEN_CHAT_BY_PHONE:
return {
...state,
searchingForConversation: true,
};
case MessagingActionTypes.SET_SELECTED_CONVERSATION:
return {
...state,
visible: true,
searchingForConversation: false,
selectedConversationId: action.payload,
};
case MessagingActionTypes.SEND_MESSAGE:

View File

@@ -4,7 +4,7 @@ import { all, call, put, select, takeLatest } from "redux-saga/effects";
import { logImEXEvent } from "../../firebase/firebase.utils";
import {
CONVERSATION_ID_BY_PHONE,
CREATE_CONVERSATION
CREATE_CONVERSATION,
} from "../../graphql/conversations.queries";
import { INSERT_CONVERSATION_TAG } from "../../graphql/job-conversations.queries";
import client from "../../utils/GraphQLClient";
@@ -12,7 +12,7 @@ import { selectBodyshop } from "../user/user.selectors";
import {
sendMessageFailure,
sendMessageSuccess,
setSelectedConversation
setSelectedConversation,
} from "./messaging.actions";
import MessagingActionTypes from "./messaging.types";
@@ -79,6 +79,7 @@ export function* openChatByPhone({ payload }) {
});
} else {
console.log("ERROR: Multiple conversations found. ");
yield put(setSelectedConversation(null));
}
} catch (error) {
console.log("Error in sendMessage saga.", error);

View File

@@ -26,3 +26,8 @@ export const selectMessage = createSelector(
[selectMessaging],
(messaging) => messaging.message
);
export const searchingForConversation = createSelector(
[selectMessaging],
(messaging) => messaging.searchingForConversation
);

View File

@@ -1,15 +1,17 @@
import Fingerprint2 from "@fingerprintjs/fingerprintjs";
import FingerprintJS from "@fingerprintjs/fingerprintjs";
import * as Sentry from "@sentry/browser";
import { notification } from "antd";
import axios from "axios";
import { setUserId, setUserProperties } from "firebase/analytics";
import {
checkActionCode,
confirmPasswordReset,
sendPasswordResetEmail,
signInWithEmailAndPassword,
signOut,
sendPasswordResetEmail,
} from "firebase/auth";
import { doc } from "firebase/firestore";
import { doc, getDoc, setDoc } from "firebase/firestore";
import { getToken } from "firebase/messaging";
import i18next from "i18next";
import LogRocket from "logrocket";
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
@@ -20,6 +22,7 @@ import {
firestore,
getCurrentUser,
logImEXEvent,
messaging,
updateCurrentUser,
} from "../../firebase/firebase.utils";
import {
@@ -28,6 +31,7 @@ import {
sendPasswordResetSuccess,
setAuthlevel,
setInstanceConflict,
setInstanceId,
setLocalFingerprint,
signInFailure,
signInSuccess,
@@ -39,9 +43,8 @@ import {
validatePasswordResetSuccess,
} from "./user.actions";
import UserActionTypes from "./user.types";
import axios from "axios";
import { messaging } from "../../firebase/firebase.utils";
import { getToken } from "firebase/messaging";
const fpPromise = FingerprintJS.load();
export function* onEmailSignInStart() {
yield takeLatest(UserActionTypes.EMAIL_SIGN_IN_START, signInWithEmail);
@@ -148,17 +151,15 @@ export function* setInstanceIdSaga({ payload: uid }) {
try {
const userInstanceRef = doc(firestore, `userInstance/${uid}`);
const fingerprint = Fingerprint2.x64hash128(
(yield Fingerprint2.getPromise({})).map((c) => c.value).join(""),
31
);
yield userInstanceRef.set({
// Get the visitor identifier when you need it.
const fp = yield fpPromise;
const result = yield fp.get();
yield setDoc(userInstanceRef, {
timestamp: new Date(),
fingerprint,
fingerprint: result.visitorId,
});
yield put(setLocalFingerprint(fingerprint));
yield put(setLocalFingerprint(result.visitorId));
yield delay(5 * 60 * 1000);
if (process.env.NODE_ENV === "production") yield put(checkInstanceId(uid));
} catch (error) {
@@ -171,11 +172,9 @@ export function* onCheckInstanceId() {
}
export function* checkInstanceIdSaga({ payload: uid }) {
try {
const userInstanceRef = doc(firestore, `userInstance/${uid}`);
const snapshot = yield userInstanceRef.get();
const snapshot = yield getDoc(doc(firestore, `userInstance/${uid}`));
let fingerprint = yield select((state) => state.user.fingerprint);
yield put(setInstanceConflict());
if (snapshot.data().fingerprint === fingerprint) {
yield delay(5 * 60 * 1000);
yield put(checkInstanceId(uid));
@@ -213,7 +212,6 @@ export function* signInSuccessSaga({ payload }) {
console.log("Error updating Crisp settings.", error);
}
// if (!payload.email.includes("@imex.")) yield put(setInstanceId(payload.uid));
setUserId(analytics, payload.email);
setUserProperties(analytics, payload);
yield logImEXEvent("redux_sign_in_success");
@@ -287,6 +285,14 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
: { validemail: false }
)
);
if (payload.features.singleDeviceOnly) {
const user = yield select((state) => state.user.currentUser);
if (!(user.email.includes("@imex.") || user.email.includes("@rome.")))
yield put(setInstanceId(user.uid));
}
try {
// window.$crisp.push(["set", "user:company", [payload.shopname]]);
// if (authRecord[0] && authRecord[0].user.validemail) {

View File

@@ -27,6 +27,7 @@
"preview": "Preview",
"reschedule": "Reschedule",
"sendreminder": "Send Reminder",
"unblock": "Unblock",
"viewjob": "View Job"
},
"errors": {
@@ -1127,6 +1128,7 @@
"total": "Total",
"totals": "Totals",
"tuesday": "Tuesday",
"tvmode": "TV Mode",
"unknown": "Unknown",
"username": "Username",
"view": "View",
@@ -1500,6 +1502,7 @@
"cost_dms_acctnumber": "Cost DMS Acct #",
"dms_make": "DMS Make",
"dms_model": "DMS Model",
"dms_model_override": "Override DMS Make/Model",
"dms_unsold": "New, Unsold Vehicle",
"dms_wip_acctnumber": "Cost WIP DMS Acct #",
"id": "DMS ID",
@@ -2659,18 +2662,22 @@
"gsr_labor_only": "Gross Sales - Labor Only",
"hours_sold_detail_closed": "Hours Sold Detail - Closed",
"hours_sold_detail_closed_csr": "Hours Sold Detail - Closed by CSR",
"hours_sold_detail_closed_estimator": "Hours Sold Detail - Closed by Estimator",
"hours_sold_detail_closed_ins_co": "Hours Sold Detail - Closed by Source",
"hours_sold_detail_closed_status": "Hours Sold Detail - Closed by Status",
"hours_sold_detail_open": "Hours Sold Detail - Open",
"hours_sold_detail_open_csr": "Hours Sold Detail - Open by CSR",
"hours_sold_detail_open_estimator": "Hours Sold Detail - Open by Estimator",
"hours_sold_detail_open_ins_co": "Hours Sold Detail - Open by Source",
"hours_sold_detail_open_status": "Hours Sold Detail - Open by Status",
"hours_sold_summary_closed": "Hours Sold Summary - Closed",
"hours_sold_summary_closed_csr": "Hours Sold Summary - Closed by CSR",
"hours_sold_summary_closed_estimator": "Hours Sold Summary - Closed by Estimator",
"hours_sold_summary_closed_ins_co": "Hours Sold Summary - Closed by Source",
"hours_sold_summary_closed_status": "Hours Sold Summary - Closed by Status",
"hours_sold_summary_open": "Hours Sold Summary - Open",
"hours_sold_summary_open_csr": "Hours Sold Summary - Open CSR",
"hours_sold_summary_open_estimator": "Hours Sold Summary - Open Estimator",
"hours_sold_summary_open_ins_co": "Hours Sold Summary - Open by Source",
"hours_sold_summary_open_status": "Hours Sold Summary - Open by Status",
"job_costing_ro_csr": "Job Costing by CSR",
@@ -2687,6 +2694,7 @@
"open_orders_csr": "Open Orders by CSR",
"open_orders_estimator": "Open Orders by Estimator",
"open_orders_ins_co": "Open Orders by Insurance Company",
"open_orders_referral": "Open Orders by Referral Source",
"open_orders_specific_csr": "Open Orders filtered by CSR",
"open_orders_status": "Open Orders by Status",
"parts_backorder": "IOU Parts List",
@@ -2762,8 +2770,12 @@
"painthrs": "Paint Hours"
},
"labels": {
"allemployeetimetickets": "All Employee Time Tickets",
"asoftodaytarget": "As of Today",
"body": "Body",
"bodycharttitle": "Body Targets vs Actual",
"calendarperiod": "Periods based on calendar weeks/months.",
"combinedcharttitle": "Combined Targets vs Actual",
"dailyactual": "Actual (D)",
"dailytarget": "Daily",
"efficiencyoverperiod": "Efficiency over Selected Dates",
@@ -2772,12 +2784,16 @@
"lastmonth": "Last Month",
"lastweek": "Last Week",
"monthlytarget": "Monthly",
"priorweek": "Prior Week",
"productivestatistics": "Productive Hours Statistics",
"productivetimeticketsoverdate": "Productive Hours over Selected Dates",
"refinish": "Refinish",
"refinishcharttitle": "Refinish Targets vs Actual",
"targets": "Targets",
"thismonth": "This Month",
"thisweek": "This Week",
"timetickets": "Timetickets",
"timetickets": "Time Tickets",
"timeticketsemployee": "Time Tickets by Employee",
"todateactual": "Actual (MTD)",
"totaloverperiod": "Total over Selected Dates",
"weeklyactual": "Actual (W)",

View File

@@ -27,6 +27,7 @@
"preview": "",
"reschedule": "Reprogramar",
"sendreminder": "",
"unblock": "",
"viewjob": "Ver trabajo"
},
"errors": {
@@ -1127,6 +1128,7 @@
"total": "",
"totals": "",
"tuesday": "",
"tvmode": "",
"unknown": "Desconocido",
"username": "",
"view": "",
@@ -1500,6 +1502,7 @@
"cost_dms_acctnumber": "",
"dms_make": "",
"dms_model": "",
"dms_model_override": "",
"dms_unsold": "",
"dms_wip_acctnumber": "",
"id": "",
@@ -2659,18 +2662,22 @@
"gsr_labor_only": "",
"hours_sold_detail_closed": "",
"hours_sold_detail_closed_csr": "",
"hours_sold_detail_closed_estimator": "",
"hours_sold_detail_closed_ins_co": "",
"hours_sold_detail_closed_status": "",
"hours_sold_detail_open": "",
"hours_sold_detail_open_csr": "",
"hours_sold_detail_open_estimator": "",
"hours_sold_detail_open_ins_co": "",
"hours_sold_detail_open_status": "",
"hours_sold_summary_closed": "",
"hours_sold_summary_closed_csr": "",
"hours_sold_summary_closed_estimator": "",
"hours_sold_summary_closed_ins_co": "",
"hours_sold_summary_closed_status": "",
"hours_sold_summary_open": "",
"hours_sold_summary_open_csr": "",
"hours_sold_summary_open_estimator": "",
"hours_sold_summary_open_ins_co": "",
"hours_sold_summary_open_status": "",
"job_costing_ro_csr": "",
@@ -2687,6 +2694,7 @@
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"open_orders_referral": "",
"open_orders_specific_csr": "",
"open_orders_status": "",
"parts_backorder": "",
@@ -2762,8 +2770,12 @@
"painthrs": ""
},
"labels": {
"allemployeetimetickets": "",
"asoftodaytarget": "",
"body": "",
"bodycharttitle": "",
"calendarperiod": "",
"combinedcharttitle": "",
"dailyactual": "",
"dailytarget": "",
"efficiencyoverperiod": "",
@@ -2772,12 +2784,16 @@
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
"priorweek": "",
"productivestatistics": "",
"productivetimeticketsoverdate": "",
"refinish": "",
"refinishcharttitle": "",
"targets": "",
"thismonth": "",
"thisweek": "",
"timetickets": "",
"timeticketsemployee": "",
"todateactual": "",
"totaloverperiod": "",
"weeklyactual": "",

View File

@@ -27,6 +27,7 @@
"preview": "",
"reschedule": "Replanifier",
"sendreminder": "",
"unblock": "",
"viewjob": "Voir le travail"
},
"errors": {
@@ -1127,6 +1128,7 @@
"total": "",
"totals": "",
"tuesday": "",
"tvmode": "",
"unknown": "Inconnu",
"username": "",
"view": "",
@@ -1500,6 +1502,7 @@
"cost_dms_acctnumber": "",
"dms_make": "",
"dms_model": "",
"dms_model_override": "",
"dms_unsold": "",
"dms_wip_acctnumber": "",
"id": "",
@@ -2659,18 +2662,22 @@
"gsr_labor_only": "",
"hours_sold_detail_closed": "",
"hours_sold_detail_closed_csr": "",
"hours_sold_detail_closed_estimator": "",
"hours_sold_detail_closed_ins_co": "",
"hours_sold_detail_closed_status": "",
"hours_sold_detail_open": "",
"hours_sold_detail_open_csr": "",
"hours_sold_detail_open_estimator": "",
"hours_sold_detail_open_ins_co": "",
"hours_sold_detail_open_status": "",
"hours_sold_summary_closed": "",
"hours_sold_summary_closed_csr": "",
"hours_sold_summary_closed_estimator": "",
"hours_sold_summary_closed_ins_co": "",
"hours_sold_summary_closed_status": "",
"hours_sold_summary_open": "",
"hours_sold_summary_open_csr": "",
"hours_sold_summary_open_estimator": "",
"hours_sold_summary_open_ins_co": "",
"hours_sold_summary_open_status": "",
"job_costing_ro_csr": "",
@@ -2687,6 +2694,7 @@
"open_orders_csr": "",
"open_orders_estimator": "",
"open_orders_ins_co": "",
"open_orders_referral": "",
"open_orders_specific_csr": "",
"open_orders_status": "",
"parts_backorder": "",
@@ -2762,8 +2770,12 @@
"painthrs": ""
},
"labels": {
"allemployeetimetickets": "",
"asoftodaytarget": "",
"body": "",
"bodycharttitle": "",
"calendarperiod": "",
"combinedcharttitle": "",
"dailyactual": "",
"dailytarget": "",
"efficiencyoverperiod": "",
@@ -2772,12 +2784,16 @@
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
"priorweek": "",
"productivestatistics": "",
"productivetimeticketsoverdate": "",
"refinish": "",
"refinishcharttitle": "",
"targets": "",
"thismonth": "",
"thisweek": "",
"timetickets": "",
"timeticketsemployee": "",
"todateactual": "",
"totaloverperiod": "",
"weeklyactual": "",

View File

@@ -1,15 +1,14 @@
import { gql } from "@apollo/client";
import { notification } from "antd";
import jsreport from "@jsreport/browser-client";
import { notification } from "antd";
import axios from "axios";
import _ from "lodash";
import moment from "moment";
import { auth } from "../firebase/firebase.utils";
import { setEmailOptions } from "../redux/email/email.actions";
import { store } from "../redux/store";
import client from "../utils/GraphQLClient";
import { TemplateList } from "./TemplateConstants";
import cleanAxios from "./CleanAxios";
import axios from "axios";
import { TemplateList } from "./TemplateConstants";
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
jsreport.serverUrl = server;
@@ -255,7 +254,7 @@ export async function RenderTemplates(
headerpath: `/${bodyshop.imexshopid}/header.html`,
footerpath: `/${bodyshop.imexshopid}/footer.html`,
bodyshop: bodyshop,
offset: moment().utcOffset(),
offset: bodyshop.timezone,
},
};

View File

@@ -907,6 +907,74 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_detail_closed_estimator: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_estimator"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_estimator"
),
key: "hours_sold_detail_closed_estimator",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
group: "sales",
},
hours_sold_detail_open_estimator: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_open_estimator"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_detail_open_estimator"
),
key: "hours_sold_detail_open_estimator",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
group: "sales",
},
hours_sold_summary_closed_estimator: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_closed_estimator"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_summary_closed_estimator"
),
key: "hours_sold_summary_closed_estimator",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
group: "sales",
},
hours_sold_summary_open_estimator: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_open_estimator"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_summary_open_estimator"
),
key: "hours_sold_summary_open_estimator",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
group: "sales",
},
hours_sold_summary_open_status: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_open_status"
@@ -1523,6 +1591,19 @@ export const TemplateList = (type, context) => {
},
group: "jobs",
},
open_orders_specific_csr: {
title: i18n.t("reportcenter.templates.open_orders_specific_csr"),
description: "",
subject: i18n.t("reportcenter.templates.open_orders_specific_csr"),
key: "open_orders_specific_csr",
idtype: "employee",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
group: "jobs",
},
open_orders_estimator: {
title: i18n.t("reportcenter.templates.open_orders_estimator"),
description: "",
@@ -1549,12 +1630,12 @@ export const TemplateList = (type, context) => {
},
group: "jobs",
},
open_orders_specific_csr: {
title: i18n.t("reportcenter.templates.open_orders_specific_csr"),
open_orders_referral: {
title: i18n.t("reportcenter.templates.open_orders_referral"),
description: "",
subject: i18n.t("reportcenter.templates.open_orders_specific_csr"),
key: "open_orders_specific_csr",
idtype: "employee",
subject: i18n.t("reportcenter.templates.open_orders_referral"),
key: "open_orders_referral",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),

View File

@@ -12,6 +12,13 @@ export default async function FcmHandler({ client, payload }) {
},
},
});
client.cache.modify({
fields: {
messages_aggregate(cached) {
return { aggregate: { count: cached.aggregate.count + 1 } };
},
},
});
break;
case "messaging-outbound":
client.cache.modify({
@@ -30,6 +37,7 @@ export default async function FcmHandler({ client, payload }) {
});
break;
case "messaging-mark-conversation-read":
let previousUnreadCount = 0;
client.cache.modify({
id: client.cache.identify({
__typename: "conversations",
@@ -37,10 +45,22 @@ export default async function FcmHandler({ client, payload }) {
}),
fields: {
messages_aggregate(cached) {
previousUnreadCount = cached.aggregate.count;
return { aggregate: { count: 0 } };
},
},
});
client.cache.modify({
fields: {
messages_aggregate(cached) {
return {
aggregate: {
count: cached.aggregate.count - previousUnreadCount,
},
};
},
},
});
break;
default:
console.log("No payload type set.");

File diff suppressed because it is too large Load Diff

View File

@@ -110,6 +110,7 @@
- canceled
- color
- created_at
- created_by
- end
- id
- isintake
@@ -128,6 +129,7 @@
- canceled
- color
- created_at
- created_by
- end
- id
- isintake
@@ -156,6 +158,7 @@
- canceled
- color
- created_at
- created_by
- end
- id
- isintake
@@ -4076,12 +4079,7 @@
event_triggers:
- name: job_status_transition
definition:
enable_manual: false
insert:
columns: '*'
update:
columns:
- status
enable_manual: true
retry_conf:
interval_sec: 10
num_retries: 0
@@ -4097,23 +4095,7 @@
version: 2
- name: jobs_arms
definition:
enable_manual: false
update:
columns:
- actual_delivery
- scheduled_delivery
- scheduled_completion
- actual_completion
- date_scheduled
- inproduction
- clm_total
- suspended
- job_totals
- converted
- employee_body
- ro_number
- actual_in
- scheduled_in
enable_manual: true
retry_conf:
interval_sec: 10
num_retries: 0

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"."appointments" add column "created_by" text
-- null;

View File

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

View File

@@ -879,6 +879,12 @@ async function UpdateDmsVehicle(socket) {
},
vehicle: {
...socket.DMSVeh.vehicle,
...(socket.txEnvelope.dms_model_override
? {
make: socket.txEnvelope.dms_make,
modelAbrev: socket.txEnvelope.dms_model,
}
: {}),
deliveryDate:
socket.txEnvelope.dms_unsold === true
? ""

View File

@@ -62,7 +62,7 @@ exports.default = async (req, res) => {
start: start
? moment(start).startOf("day")
: moment().subtract(5, "days").startOf("day"),
...(end && { end: moment(end).startOf("day") }),
...(end && { end: moment(end).endOf("day") }),
}
);

View File

@@ -17,7 +17,7 @@ let Client = require("ssh2-sftp-client");
const client = require("../graphql-client/graphql-client").client;
const { sendServerEmail } = require("../email/sendemail");
const CCDineroFormat = "0,0.00";
const AhDateFormat = "MMDDYYYY";
const AhDateFormat = "MM/DD/YYYY";
const repairOpCodes = ["OP4", "OP9", "OP10"];
const replaceOpCodes = ["OP2", "OP5", "OP11", "OP12"];
@@ -62,22 +62,24 @@ exports.default = async (req, res) => {
start: start
? moment(start).startOf("day")
: moment().subtract(5, "days").startOf("day"),
...(end && { end: moment(end).startOf("day") }),
...(end && { end: moment(end).endOf("day") }),
}
);
const claimsCorpObject = {
ClaimsCorpExport: {
ShopID: bodyshops_by_pk.claimscorpid,
ShopName: bodyshops_by_pk.shopname,
RO: jobs.map((j) =>
CreateRepairOrderTag(
{ ...j, bodyshop: bodyshops_by_pk },
function ({ job, error }) {
erroredJobs.push({ job: job, error: error.toString() });
}
)
),
DataFeed: {
ShopInfo: {
ShopID: bodyshops_by_pk.claimscorpid,
ShopName: bodyshops_by_pk.shopname,
RO: jobs.map((j) =>
CreateRepairOrderTag(
{ ...j, bodyshop: bodyshops_by_pk },
function ({ job, error }) {
erroredJobs.push({ job: job, error: error.toString() });
}
)
),
},
},
};
@@ -100,9 +102,9 @@ exports.default = async (req, res) => {
.end({ allowEmptyTags: true });
allxmlsToUpload.push({
count: claimsCorpObject.ClaimsCorpExport.RO.length,
count: claimsCorpObject.DataFeed.ShopInfo.RO.length,
xml: ret,
filename: `${bodyshop.claimscorpid}-MIS-${moment().format(
filename: `${bodyshop.claimscorpid}-${moment().format(
"YYYYMMDDTHHMMss"
)}.xml`,
});
@@ -243,7 +245,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
try {
const ret = {
RONumber: job.ro_number,
RoNumber: job.ro_number,
Customer: {
CustomerZip: (job.ownr_zip && job.ownr_zip.substring(0, 3)) || "",
CustomerState: job.ownr_st || "",
@@ -261,8 +263,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
VIN: job.v_vin || "",
},
Carrier: {
InsuranceCo: job.ins_co_nm || "",
CompanyName: job.ins_co_nm || "",
UniqueID: job.ins_co_nm || "",
InsuranceCompany: job.ins_co_nm || "",
},
Claim: job.clm_no || "",
Contacts: {
@@ -279,7 +281,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
: "",
Phone1: "",
Phone2: "",
EstimatorName: `${job.est_ct_ln ? job.est_ct_ln : ""}${
Estimator: `${job.est_ct_ln ? job.est_ct_ln : ""}${
job.est_ct_ln ? ", " : ""
}${job.est_ct_fn ? job.est_ct_fn : ""}`,
BodyTechnician: job.employee_body_rel
@@ -310,7 +312,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
(job.date_estimated &&
moment(job.date_estimated).format(AhDateFormat)) ||
"",
DateofLoss:
DateLoss:
(job.loss_date && moment(job.loss_date).format(AhDateFormat)) || "",
DateFNOL: "",
DateContact: "",
@@ -337,7 +339,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
.tz(job.bodyshop.timezone)
.format(AhDateFormat)) ||
"",
StartDate: job.date_repairstarted
DateStart: job.date_repairstarted
? (job.date_repairstarted &&
moment(job.date_repairstarted)
.tz(job.bodyshop.timezone)
@@ -382,7 +384,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
"",
BilledDate: "",
PaidInFullDate: "",
RoStatus: job.tlos_ind
ROStatus: job.tlos_ind
? "TOT"
: StatusMapping(job.status, job.bodyshop.md_ro_statuses),
},
@@ -392,14 +394,10 @@ const CreateRepairOrderTag = (job, errorCallback) => {
.add(Dinero(job.job_totals.rates.lad.total))
.add(Dinero(job.job_totals.rates.las.total))
.toFormat(CCDineroFormat),
Refinish: Dinero(job.job_totals.rates.lar.total).toFormat(
CCDineroFormat
),
Paint: Dinero(job.job_totals.rates.lar.total).toFormat(CCDineroFormat),
Prep: Dinero().toFormat(CCDineroFormat),
Frame: Dinero(job.job_totals.rates.laf.total).toFormat(CCDineroFormat),
Mechanical: Dinero(job.job_totals.rates.lam.total).toFormat(
CCDineroFormat
),
Mech: Dinero(job.job_totals.rates.lam.total).toFormat(CCDineroFormat),
Glass: Dinero(job.job_totals.rates.lag.total).toFormat(CCDineroFormat),
Elec: Dinero(job.job_totals.rates.lae.total).toFormat(CCDineroFormat),
Detail: detailAdjustments.amount.toFormat(CCDineroFormat),
@@ -446,6 +444,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
Towing: Dinero(job.job_totals.additional.towing).toFormat(
CCDineroFormat
),
Storage: "0.00",
Rental:
job.job_totals.additional.additionalCostItems.includes(
"ATS Amount"
@@ -480,17 +479,17 @@ const CreateRepairOrderTag = (job, errorCallback) => {
.filter((line) => repairOpCodes.includes(line.lbr_op))
.reduce((acc, val) => acc + val.mod_lb_hrs, 0)
.toFixed(2),
BodyReplaceHours: job.joblines
BodyReplacehours: job.joblines
.filter((line) => replaceOpCodes.includes(line.lbr_op))
.reduce((acc, val) => acc + val.mod_lb_hrs, 0)
.toFixed(2),
Paint: job.job_totals.rates.lar.hours.toFixed(2),
Prep: "0.00",
FrameHours: job.job_totals.rates.laf.hours.toFixed(2),
MechanicalHours: job.job_totals.rates.lam.hours.toFixed(2),
GlassHours: job.job_totals.rates.lag.hours.toFixed(2),
ElectricalHours: job.job_totals.rates.lae.hours.toFixed(2),
DetailHours: detailAdjustments.hours,
Frame: job.job_totals.rates.laf.hours.toFixed(2),
Mech: job.job_totals.rates.lam.hours.toFixed(2),
Glass: job.job_totals.rates.lag.hours.toFixed(2),
Elec: job.job_totals.rates.lae.hours.toFixed(2),
Detail: detailAdjustments.hours,
Reassem: "0.00",
Other: (
job.job_totals.rates.la1.hours +
@@ -522,7 +521,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
AM: repairCosts.PartsAMCost.toFormat(CCDineroFormat),
MechParts: Dinero().toFormat(CCDineroFormat),
OtherParts: Dinero().toFormat(CCDineroFormat), //Check Synergy
OtherCosts: repairCosts.PartsOtherCost.toFormat(CCDineroFormat),
OtherCost: repairCosts.PartsOtherCost.toFormat(CCDineroFormat),
Sublet: repairCosts.SubletTotalCost.toFormat(CCDineroFormat),
Towing: repairCosts.TowingTotalCost.toFormat(CCDineroFormat),
Storage: repairCosts.StorageTotalCost.toFormat(CCDineroFormat),

View File

@@ -1,16 +1,12 @@
exports.FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID = `
query FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID(
$mssid: String!
$phone: String!
) {
query FIND_BODYSHOP_BY_MESSAGING_SERVICE_SID($mssid: String!, $phone: String!) {
bodyshops(where: { messagingservicesid: { _eq: $mssid } }) {
id
conversations(where: { phone_num: { _eq: $phone } }) {
id
}
}
}
`;
}`;
exports.GET_JOB_BY_RO_NUMBER = `
query GET_JOB_BY_RO_NUMBER($ro_number: String!) {
@@ -20,8 +16,7 @@ exports.GET_JOB_BY_RO_NUMBER = `
id
}
}
}
`;
}`;
exports.INSERT_NEW_PAYMENT = `
mutation INSERT_NEW_PAYMENT($paymentInput: [payments_insert_input!]!) {
@@ -30,28 +25,23 @@ exports.INSERT_NEW_PAYMENT = `
id
}
}
}
`;
}`;
exports.INSERT_PAYMENT_RESPONSE = `
mutation INSERT_PAYMENT_RESPONSE(
$paymentResponse: [payment_response_insert_input!]!
) {
mutation INSERT_PAYMENT_RESPONSE($paymentResponse: [payment_response_insert_input!]!) {
insert_payment_response(objects: $paymentResponse) {
returning {
id
}
}
}
`;
}`;
exports.UNARCHIVE_CONVERSATION = `
mutation UNARCHIVE_CONVERSATION($id: uuid!) {
update_conversations_by_pk(pk_columns: {id: $id}, _set: {archived: false}) {
id
}
}
`;
}`;
exports.INSERT_NEW_JOB_LINE = `
mutation INSERT_NEW_JOB_LINE($lineInput: [joblines_insert_input!]!) {
@@ -60,8 +50,7 @@ exports.INSERT_NEW_JOB_LINE = `
id
}
}
}
`;
}`;
exports.UPDATE_JOB_LINE = `
mutation UPDATE_JOB_LINE($lineId: uuid!, $line: joblines_set_input!) {
@@ -82,8 +71,7 @@ exports.UPDATE_JOB_LINE = `
removed
}
}
}
`;
}`;
exports.RECEIVE_MESSAGE = `
mutation RECEIVE_MESSAGE($msg: [messages_insert_input!]!) {
@@ -92,7 +80,7 @@ mutation RECEIVE_MESSAGE($msg: [messages_insert_input!]!) {
conversation {
id
archived
bodyshop{
bodyshop {
imexshopid
}
created_at
@@ -114,10 +102,7 @@ mutation RECEIVE_MESSAGE($msg: [messages_insert_input!]!) {
userid
}
}
}
`;
}`;
exports.INSERT_MESSAGE = `
mutation INSERT_MESSAGE($msg: [messages_insert_input!]!, $conversationid: uuid!) {
@@ -130,7 +115,7 @@ mutation INSERT_MESSAGE($msg: [messages_insert_input!]!, $conversationid: uuid!)
conversation {
id
archived
bodyshop{
bodyshop {
imexshopid
}
created_at
@@ -152,10 +137,7 @@ mutation INSERT_MESSAGE($msg: [messages_insert_input!]!, $conversationid: uuid!)
userid
}
}
}
`;
}`;
exports.UPDATE_MESSAGE_STATUS = `
mutation UPDATE_MESSAGE($msid: String!, $fields: messages_set_input!) {
@@ -164,8 +146,7 @@ mutation UPDATE_MESSAGE($msid: String!, $fields: messages_set_input!) {
id
}
}
}
`;
}`;
exports.QUERY_JOBS_FOR_RECEIVABLES_EXPORT = `
query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
@@ -230,7 +211,7 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
owner {
accountingid
}
joblines(where:{removed: {_eq:false}}) {
joblines(where:{removed: {_eq:false}}) {
id
line_desc
part_type
@@ -260,8 +241,7 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
timezone
md_ro_statuses
}
}
`;
}`;
exports.QUERY_JOBS_FOR_CDK_EXPORT = `
query QUERY_JOBS_FOR_CDK_EXPORT($id: uuid!) {
@@ -321,19 +301,19 @@ query QUERY_JOBS_FOR_CDK_EXPORT($id: uuid!) {
v_make_desc
v_color
ca_customer_gst
bodyshop {
id
md_ro_statuses
md_responsibility_centers
accountingconfig
cdk_dealerid
cdk_configuration
timezone
}
bodyshop {
id
md_ro_statuses
md_responsibility_centers
accountingconfig
cdk_dealerid
cdk_configuration
timezone
}
owner {
accountingid
}
joblines(where:{removed: {_eq:false}}) {
joblines(where:{removed: {_eq:false}}) {
id
line_desc
part_type
@@ -348,9 +328,7 @@ query QUERY_JOBS_FOR_CDK_EXPORT($id: uuid!) {
prt_dsmk_p
}
}
}
`;
}`;
exports.QUERY_JOBS_FOR_PBS_EXPORT = `
query QUERY_JOBS_FOR_PBS_EXPORT($id: uuid!) {
@@ -413,24 +391,24 @@ query QUERY_JOBS_FOR_PBS_EXPORT($id: uuid!) {
v_make_desc
v_color
ca_customer_gst
vehicle{
vehicle {
v_trimcode
v_makecode
}
bodyshop {
id
md_ro_statuses
md_responsibility_centers
accountingconfig
pbs_serialnumber
pbs_configuration
timezone
}
bodyshop {
id
md_ro_statuses
md_responsibility_centers
accountingconfig
pbs_serialnumber
pbs_configuration
timezone
}
owner {
id
accountingid
}
joblines(where:{removed: {_eq:false}}) {
joblines(where:{removed: {_eq:false}}) {
id
line_desc
part_type
@@ -447,9 +425,7 @@ query QUERY_JOBS_FOR_PBS_EXPORT($id: uuid!) {
line_ref
}
}
}
`;
}`;
exports.QUERY_BILLS_FOR_PAYABLES_EXPORT = `
query QUERY_BILLS_FOR_PAYABLES_EXPORT($bills: [uuid!]!) {
@@ -493,18 +469,17 @@ query QUERY_BILLS_FOR_PAYABLES_EXPORT($bills: [uuid!]!) {
due_date
}
}
}
`;
}`;
exports.QUERY_PAYMENTS_FOR_EXPORT = `
query QUERY_PAYMENTS_FOR_EXPORT($payments: [uuid!]!)
{ bodyshops(where: {associations: {active: {_eq: true}}}) {
id
md_responsibility_centers
accountingconfig
timezone
md_ins_cos
}
query QUERY_PAYMENTS_FOR_EXPORT($payments: [uuid!]!) {
bodyshops(where: {associations: {active: {_eq: true}}}) {
id
md_responsibility_centers
accountingconfig
timezone
md_ins_cos
}
payments(where: {id: {_in: $payments}}) {
id
created_at
@@ -512,15 +487,14 @@ exports.QUERY_PAYMENTS_FOR_EXPORT = `
job {
id
ro_number
ownerid
ownr_ln
ownr_fn
ownr_addr1
ownr_addr2
ownr_zip
ownr_city
ownr_st
ownerid
ownr_ln
ownr_fn
ownr_addr1
ownr_addr2
ownr_zip
ownr_city
ownr_st
ins_co_nm
owner{
accountingid
@@ -528,7 +502,7 @@ exports.QUERY_PAYMENTS_FOR_EXPORT = `
ownr_fn
ownr_ln
ownr_co_nm
bodyshop{
bodyshop {
accountingconfig
md_responsibility_centers
md_ins_cos
@@ -545,8 +519,7 @@ exports.QUERY_PAYMENTS_FOR_EXPORT = `
paymentnum
date
}
}
`;
}`;
exports.QUERY_UPCOMING_APPOINTMENTS = `query QUERY_UPCOMING_APPOINTMENTS($now: timestamptz!, $jobId: uuid!) {
jobs_by_pk(id: $jobId) {
@@ -646,8 +619,7 @@ exports.QUERY_UPCOMING_APPOINTMENTS = `query QUERY_UPCOMING_APPOINTMENTS($now: t
}
}
}
}
`;
}`;
exports.QUERY_EMPLOYEE_PIN = `query QUERY_EMPLOYEE_PIN($shopId: uuid!, $employeeId: String!) {
employees(where: {_and: {shopid: {_eq: $shopId}, employee_number: {_eq: $employeeId}}}) {
@@ -838,8 +810,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
totalliquidcost
}
}
}
`;
}`;
exports.CLAIMSCORP_QUERY = `query CLAIMSCORP_EXPORT($start: timestamptz, $bodyshopid: uuid!, $end: timestamptz) {
bodyshops_by_pk(id: $bodyshopid){
@@ -1011,8 +982,7 @@ exports.CLAIMSCORP_QUERY = `query CLAIMSCORP_EXPORT($start: timestamptz, $bodysh
totalliquidcost
}
}
}
`;
}`;
exports.ENTEGRAL_EXPORT = `
query ENTEGRAL_EXPORT($bodyshopid: uuid!) {
@@ -1101,8 +1071,7 @@ query ENTEGRAL_EXPORT($bodyshopid: uuid!) {
cieca_ttl
adjustment_bottom_line
}
}
`;
}`;
exports.UPDATE_JOB = `
mutation UPDATE_JOB($jobId: uuid!, $job: jobs_set_input!) {
@@ -1117,8 +1086,7 @@ exports.UPDATE_JOB = `
lbr_adjustments
}
}
}
`;
}`;
exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) {
jobs_by_pk(id: $id) {
@@ -1131,7 +1099,7 @@ exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) {
kmout
comment
referral_source
referral_source_extra
referral_source_extra
unit_number
po_number
special_coverage_policy
@@ -1170,6 +1138,10 @@ exports.GET_JOB_BY_PK = `query GET_JOB_BY_PK($id: uuid!) {
cieca_pfl
cieca_pft
cieca_pfo
vehicle {
id
notes
}
est_ph1
est_ea
selling_dealer
@@ -1518,19 +1490,17 @@ exports.QUERY_JOB_COSTING_DETAILS_MULTI = ` query QUERY_JOB_COSTING_DETAILS_MULT
use_paint_scale_data
}
}
}
`;
}`;
exports.INSERT_IOEVENT = ` mutation INSERT_IOEVENT($event: ioevents_insert_input!) {
insert_ioevents_one(object: $event) {
id
}
}
`;
}`;
exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS {
bodyshops(where: {autohouseid: {_is_null: false}}){
id
bodyshops(where: {autohouseid: {_is_null: false}, _or: {autohouseid: {_neq: ""}}}){
id
shopname
address1
city
@@ -1546,11 +1516,10 @@ exports.GET_AUTOHOUSE_SHOPS = `query GET_AUTOHOUSE_SHOPS {
imexshopid
timezone
}
}
`;
}`;
exports.GET_CLAIMSCORP_SHOPS = `query GET_CLAIMSCORP_SHOPS {
bodyshops(where: {claimscorpid: {_is_null: false}}){
bodyshops(where: {claimscorpid: {_is_null: false}, _or: {claimscorpid: {_neq: ""}}}){
id
shopname
address1
@@ -1567,12 +1536,11 @@ exports.GET_CLAIMSCORP_SHOPS = `query GET_CLAIMSCORP_SHOPS {
imexshopid
timezone
}
}
`;
}`;
exports.GET_ENTEGRAL_SHOPS = `query GET_AUTOHOUSE_SHOPS {
bodyshops(where: {entegral_id: {_is_null: false}}){
id
bodyshops(where: {entegral_id: {_is_null: false}, _or: {entegral_id: {_neq: ""}}}){
id
shopname
address1
city
@@ -1588,22 +1556,19 @@ exports.GET_ENTEGRAL_SHOPS = `query GET_AUTOHOUSE_SHOPS {
timezone
features
}
}
`;
}`;
exports.DELETE_ALL_DMS_VEHICLES = `mutation DELETE_ALL_DMS_VEHICLES{
delete_dms_vehicles(where: {}) {
affected_rows
}
}
`;
}`;
exports.INSERT_DMS_VEHICLES = `mutation INSERT_DMS_VEHICLES($vehicles: [dms_vehicles_insert_input!]!) {
insert_dms_vehicles(objects: $vehicles) {
affected_rows
}
}
`;
}`;
exports.GET_CDK_ALLOCATIONS = `query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
jobs_by_pk(id: $id) {
@@ -1720,8 +1685,7 @@ exports.GET_CDK_ALLOCATIONS = `query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
unq_seq
}
}
}
`;
}`;
exports.GET_QBO_AUTH = `query GET_QBO_AUTH($email: String!) {
associations(where: {_and: {active: {_eq: true}, useremail: {_eq: $email}}}){
@@ -1735,15 +1699,13 @@ exports.SET_QBO_AUTH_WITH_REALM = `mutation SET_QBO_AUTH($email: String!, $qbo_a
update_associations(_set: {qbo_auth: $qbo_auth, qbo_realmId: $qbo_realmId}, where: {_and: {active: {_eq: true}, useremail: {_eq: $email}}}){
affected_rows
}
}
`;
}`;
exports.SET_QBO_AUTH = `mutation SET_QBO_AUTH($email: String!, $qbo_auth: jsonb!) {
update_associations(_set: {qbo_auth: $qbo_auth}, where: {_and: {active: {_eq: true}, useremail: {_eq: $email}}}){
affected_rows
}
}
`;
}`;
exports.MARK_JOB_EXPORTED = `
mutation MARK_JOB_EXPORTED($jobId: uuid!, $job: jobs_set_input!, $log: exportlog_insert_input!, $bill: bills_set_input!) {
@@ -1762,10 +1724,9 @@ mutation MARK_JOB_EXPORTED($jobId: uuid!, $job: jobs_set_input!, $log: exportlog
id
}
update_bills(where:{jobid:{_eq :$jobId}}, _set:$bill){
affected_rows
affected_rows
}
}
`;
}`;
exports.MARK_BILLS_EXPORTED = `
mutation UPDATE_BILLS($billids: [uuid!]!, $bill: bills_set_input!, $logs: [exportlog_insert_input!]!) {
@@ -1777,28 +1738,25 @@ mutation UPDATE_BILLS($billids: [uuid!]!, $bill: bills_set_input!, $logs: [expor
}
}
insert_exportlog(objects: $logs) {
returning{
id
}
returning{
id
}
}
}
`;
}`;
exports.INSERT_EXPORT_LOG = `
mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) {
insert_exportlog_one(object: $log) {
id
}
}
`;
}`;
exports.QUERY_EXISTING_TRANSITION = `
mutation INSERT_EXPORT_LOG($log: exportlog_insert_input!) {
insert_exportlog_one(object: $log) {
id
}
}
`;
}`;
exports.UPDATE_OLD_TRANSITION = `mutation UPDATE_OLD_TRANSITION($jobid: uuid!, $existingTransition: transitions_set_input!){
update_transitions(where:{jobid:{_eq:$jobid}, end:{_is_null:true
@@ -1822,9 +1780,7 @@ exports.INSERT_NEW_TRANSITION = `mutation INSERT_NEW_TRANSITION($newTransition:
update_transitions(where: {id: {_eq: $oldTransitionId}}, _set: {duration: $duration}) {
affected_rows
}
}
`;
}`;
exports.QUERY_JOB_ID_MIXDATA = `query QUERY_JOB_ID_MIXDATA($roNumbers: [String!]!) {
jobs(where: {ro_number: {_in: $roNumbers}}) {
@@ -1834,9 +1790,7 @@ exports.QUERY_JOB_ID_MIXDATA = `query QUERY_JOB_ID_MIXDATA($roNumbers: [String!]
id
}
}
}
`;
}`;
exports.QBO_MARK_JOB_EXPORTED = `
mutation QBO_MARK_JOB_EXPORTED($jobId: uuid!, $job: jobs_set_input!, $logs: [exportlog_insert_input!]!) {
@@ -1849,9 +1803,8 @@ mutation QBO_MARK_JOB_EXPORTED($jobId: uuid!, $job: jobs_set_input!, $logs: [exp
}
}
}
}`;
`;
exports.QBO_MARK_BILL_EXPORTED = `
mutation QBO_MARK_BILL_EXPORTED($billId: uuid!, $bill: bills_set_input!, $logs: [exportlog_insert_input!]!) {
insert_exportlog(objects: $logs) {
@@ -1862,9 +1815,7 @@ mutation QBO_MARK_BILL_EXPORTED($billId: uuid!, $bill: bills_set_input!, $logs:
id
}
}
}
`;
}`;
exports.QBO_MARK_PAYMENT_EXPORTED = `
mutation QBO_MARK_PAYMENT_EXPORTED($paymentId: uuid!, $payment: payments_set_input!, $logs: [exportlog_insert_input!]!) {
@@ -1879,19 +1830,17 @@ mutation QBO_MARK_PAYMENT_EXPORTED($paymentId: uuid!, $payment: payments_set_inp
}`;
exports.INSERT_EXPORT_LOG = `
mutation INSERT_EXPORT_LOG($logs: [exportlog_insert_input!]!) {
insert_exportlog(objects: $logs) {
affected_rows
}
mutation INSERT_EXPORT_LOG($logs: [exportlog_insert_input!]!) {
insert_exportlog(objects: $logs) {
affected_rows
}
`;
}`;
exports.INSERT_EMAIL_AUDIT = `mutation INSERT_EMAIL_AUDIT($email: email_audit_trail_insert_input!) {
insert_email_audit_trail_one(object: $email) {
id
}
}
`;
}`;
exports.DELETE_MEDIA_DOCUMENTS = `
mutation DELETE_DOCUMENTS($ids: [uuid!]!) {
@@ -1900,8 +1849,7 @@ mutation DELETE_DOCUMENTS($ids: [uuid!]!) {
id
}
}
}
`;
}`;
exports.UPDATE_EMAIL_AUDIT = `
mutation ($sesid: String!, $status: String, $context: jsonb) {
@@ -1953,8 +1901,7 @@ query GET_PBS_AP_ALLOCATIONS($billids: [uuid!]) {
quantity
}
}
}
`;
}`;
exports.GET_JOB_FOR_PPC = `query GET_JOB_FOR_PPC($jobid: uuid!) {
jobs_by_pk(id: $jobid) {
@@ -1986,8 +1933,7 @@ exports.QUERY_PARTS_SCAN = `query QUERY_PARTS_SCAN ($id: uuid!) {
critical
}
}
}
`;
}`;
exports.UPDATE_PARTS_CRITICAL = `mutation UPDATE_PARTS_CRITICAL ($IdsToMarkCritical:[uuid!]!, $jobid: uuid!){
critical: update_joblines(where:{id:{_in:$IdsToMarkCritical}}, _set:{critical: true}){
@@ -2003,8 +1949,7 @@ exports.ACTIVE_SHOP_BY_USER = `query ACTIVE_SHOP_BY_USER($user: String) {
id
shopid
}
}
`;
}`;
exports.QUERY_JOB_PAYROLL_DATA = `query QUERY_JOB_PAYROLL_DATA($id: uuid!) {
jobs_by_pk(id: $id) {