Compare commits
163 Commits
feature/pa
...
rome/relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f14dc497cf | ||
|
|
89339e1033 | ||
|
|
146a61c9cf | ||
|
|
5250d97935 | ||
|
|
18dce9795d | ||
|
|
7add2bb12e | ||
|
|
36ce547579 | ||
|
|
ae5fef435a | ||
|
|
c2b3905c8e | ||
|
|
32072f1d6c | ||
|
|
e84d3bf53a | ||
|
|
67a7e4b865 | ||
|
|
3d7da0b28a | ||
|
|
9320587595 | ||
|
|
7bceba7ed5 | ||
|
|
0db72cd9e4 | ||
|
|
53fc5e361f | ||
|
|
54af163ddf | ||
|
|
c0d756fa38 | ||
|
|
fc16190ec4 | ||
|
|
cf7f4f1b46 | ||
|
|
756fc07193 | ||
|
|
ba95a636cf | ||
|
|
2478fedf1a | ||
|
|
bc6c889afc | ||
|
|
33bcf250d0 | ||
|
|
bad32e069b | ||
|
|
9acf20d4f3 | ||
|
|
26f1ee0d89 | ||
|
|
9c86be8250 | ||
|
|
4a06f9a686 | ||
|
|
98700f54b4 | ||
|
|
9a37cb5cb8 | ||
|
|
673d0bb7c5 | ||
|
|
04c7bc445b | ||
|
|
41782fe120 | ||
|
|
71a5722b0e | ||
|
|
4507135a1b | ||
|
|
4e8e25a336 | ||
|
|
3c2d3156cb | ||
|
|
0dac15391f | ||
|
|
106534b59b | ||
|
|
19c0553746 | ||
|
|
5f1475d2ec | ||
|
|
84f4d5956a | ||
|
|
4330ddd926 | ||
|
|
0711210512 | ||
|
|
458ec76835 | ||
|
|
682ea860fb | ||
|
|
99977934e7 | ||
|
|
3e05b21c90 | ||
|
|
4e1dd52bea | ||
|
|
f6bcc743d8 | ||
|
|
7f2479f597 | ||
|
|
66b97be9d2 | ||
|
|
7472285641 | ||
|
|
d747594e39 | ||
|
|
67ff9f30c6 | ||
|
|
a27092dbcc | ||
|
|
ca41bff446 | ||
|
|
cf8280590c | ||
|
|
b649ca1f00 | ||
|
|
b441301007 | ||
|
|
2e93238b5c | ||
|
|
ce4fe84536 | ||
|
|
9b7c0af025 | ||
|
|
dfdaf36ed1 | ||
|
|
57909ebc61 | ||
|
|
cc8d1b3793 | ||
|
|
a33bfedbb8 | ||
|
|
d1090670a8 | ||
|
|
f4bcad1b06 | ||
|
|
eb359d83c5 | ||
|
|
ae13e9e36a | ||
|
|
4a62ac2a11 | ||
|
|
6656708c8e | ||
|
|
bfe3fb2ed0 | ||
|
|
ec21521281 | ||
|
|
016a62b6d5 | ||
|
|
3e226b50ab | ||
|
|
ab84cb5ada | ||
|
|
7825aa4122 | ||
|
|
e71e671d7d | ||
|
|
8d43fbfcd9 | ||
|
|
47a01628d3 | ||
|
|
c008660023 | ||
|
|
5da34cbeac | ||
|
|
5b29aec14b | ||
|
|
ca6aa682f6 | ||
|
|
eb3786cebf | ||
|
|
53843e22a4 | ||
|
|
e1693674ca | ||
|
|
2d2190e4fa | ||
|
|
02fd8097a8 | ||
|
|
fcfbc85683 | ||
|
|
802dd696f4 | ||
|
|
df6b0fae21 | ||
|
|
69723a8bf4 | ||
|
|
241caa6132 | ||
|
|
13b93f6d9d | ||
|
|
77555366f4 | ||
|
|
f3e8132bfc | ||
|
|
7510385cf4 | ||
|
|
60a0222dd0 | ||
|
|
9114abd3ef | ||
|
|
b3f69d4088 | ||
|
|
8796c8fe06 | ||
|
|
f7fc0e6a6d | ||
|
|
ffebbe3b2a | ||
|
|
25b8c1b1eb | ||
|
|
17f8625108 | ||
|
|
e3c21f0373 | ||
|
|
859ff00277 | ||
|
|
e7c3be5231 | ||
|
|
85e3c5a433 | ||
|
|
d8d5cde3f1 | ||
|
|
6efa08fee3 | ||
|
|
636c13373c | ||
|
|
3659fbec84 | ||
|
|
05f1a9b280 | ||
|
|
34b4baac3d | ||
|
|
5884d5eba0 | ||
|
|
6b4709b76b | ||
|
|
4dd2137006 | ||
|
|
03315836a6 | ||
|
|
f703ba2cf9 | ||
|
|
4513acc640 | ||
|
|
612e359d4c | ||
|
|
c8fb1ce302 | ||
|
|
b29d8e1912 | ||
|
|
f1aa7944a3 | ||
|
|
74b6c2b6b5 | ||
|
|
4313cf471f | ||
|
|
fce8039dad | ||
|
|
085ae141ae | ||
|
|
fbc9ccc018 | ||
|
|
f2ede519d7 | ||
|
|
21c53473d3 | ||
|
|
fbc622fa04 | ||
|
|
6319fd20fa | ||
|
|
c998e4901f | ||
|
|
06c35a4ff8 | ||
|
|
171c0b2b5a | ||
|
|
fd4820336f | ||
|
|
46840266ee | ||
|
|
82a0d287d6 | ||
|
|
4de604ef7c | ||
|
|
ec2c26ca69 | ||
|
|
026ce853e2 | ||
|
|
720b7f891b | ||
|
|
b5332458ec | ||
|
|
3c7b16412b | ||
|
|
a454c57bc9 | ||
|
|
86ff7cbc69 | ||
|
|
fbfdbc903c | ||
|
|
9e64fdc985 | ||
|
|
d7c23297ab | ||
|
|
f6e095e0a6 | ||
|
|
d7ec2e717c | ||
|
|
35fd74d3fe | ||
|
|
8325e2d9cf | ||
|
|
cc3c1242f5 | ||
|
|
b2f4a5539c |
@@ -8,13 +8,13 @@ orbs:
|
||||
jobs:
|
||||
api-deploy:
|
||||
docker:
|
||||
- image: "cimg/base:stable"
|
||||
- image: cimg/node:18.18.2
|
||||
steps:
|
||||
- checkout
|
||||
- eb/setup
|
||||
- run:
|
||||
command: |
|
||||
eb init imex-online-production-api -r ca-central-1 -p "Node.js 16 running on 64bit Amazon Linux 2"
|
||||
eb init imex-online-production-api -r ca-central-1 -p "Node.js 18 running on 64bit Amazon Linux 2"
|
||||
eb status --verbose
|
||||
eb deploy
|
||||
eb status
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
- eb/setup
|
||||
- run:
|
||||
command: |
|
||||
eb init romeonline-productionapi -r us-east-2 -p "Node.js 16 running on 64bit Amazon Linux 2"
|
||||
eb init romeonline-productionapi -r us-east-2 -p "Node.js 18 running on 64bit Amazon Linux 2"
|
||||
eb status --verbose
|
||||
eb deploy
|
||||
eb status
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -117,4 +117,6 @@ logs/oAuthClient-log.log
|
||||
|
||||
.node-persist/**
|
||||
|
||||
/*.env.*
|
||||
/*.env.*
|
||||
.idea/*
|
||||
.idea
|
||||
@@ -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>
|
||||
@@ -18432,6 +18453,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>tvmode</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>unknown</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -20059,6 +20101,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>create_ppc</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>db_price</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -24841,6 +24904,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>
|
||||
@@ -32462,6 +32546,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>total_sales_tax</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>totals</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -44869,6 +44974,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 +45079,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 +45184,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 +45289,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 +45646,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>
|
||||
@@ -46773,6 +46983,27 @@
|
||||
<folder_node>
|
||||
<name>labels</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>allemployeetimetickets</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>asoftodaytarget</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -46794,6 +47025,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>body</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>bodycharttitle</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>calendarperiod</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -46815,6 +47088,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>combinedcharttitle</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>dailyactual</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -46983,6 +47277,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>priorweek</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>productivestatistics</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -47025,6 +47340,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>refinish</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>refinishcharttitle</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>targets</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -47109,6 +47466,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>timeticketsemployee</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>todateactual</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
24522
client/package-lock.json
generated
Normal file
24522
client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
@@ -89,8 +89,8 @@
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "craco start",
|
||||
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
||||
"build:test": "env-cmd -f .env.test yarn run build",
|
||||
"build-deploy:test": "yarn run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
|
||||
"build:test": "env-cmd -f .env.test npm run build",
|
||||
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
|
||||
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
|
||||
"test": "cypress open",
|
||||
"eject": "react-scripts eject",
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useApolloClient, useMutation } from "@apollo/client";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import { Button, Checkbox, Form, Modal, Space, notification } from "antd";
|
||||
import _ from "lodash";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
@@ -29,7 +30,6 @@ import BillFormContainer from "../bill-form/bill-form.container";
|
||||
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
||||
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
|
||||
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
billEnterModal: selectBillEnterModal,
|
||||
@@ -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");
|
||||
@@ -72,6 +72,11 @@ function BillEnterModalContainer({
|
||||
const formValues = useMemo(() => {
|
||||
return {
|
||||
...billEnterModal.context.bill,
|
||||
//Added as a part of IO-2436 for capturing parts price changes.
|
||||
billlines: billEnterModal.context?.bill?.billlines?.map((line) => ({
|
||||
...line,
|
||||
original_actual_price: line.actual_price,
|
||||
})),
|
||||
jobid:
|
||||
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
|
||||
federal_tax_rate:
|
||||
@@ -120,6 +125,8 @@ function BillEnterModalContainer({
|
||||
lbr_adjustment,
|
||||
location: lineLocation,
|
||||
part_type,
|
||||
create_ppc,
|
||||
original_actual_price,
|
||||
...restI
|
||||
} = i;
|
||||
|
||||
@@ -167,7 +174,7 @@ function BillEnterModalContainer({
|
||||
},
|
||||
],
|
||||
},
|
||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
|
||||
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"],
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
@@ -292,6 +299,14 @@ function BillEnterModalContainer({
|
||||
location: li.location || location,
|
||||
status:
|
||||
bodyshop.md_order_statuses.default_received || "Received*",
|
||||
//Added parts price changes.
|
||||
...(li.create_ppc &&
|
||||
li.original_actual_price !== li.actual_price
|
||||
? {
|
||||
act_price_before_ppc: li.original_actual_price,
|
||||
act_price: li.actual_price,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -354,7 +369,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) {
|
||||
@@ -438,6 +455,9 @@ function BillEnterModalContainer({
|
||||
setEnterAgain(false);
|
||||
}}
|
||||
>
|
||||
<button onClick={() => console.log(form.getFieldsValue("billlines"))}>
|
||||
get billlines
|
||||
</button>
|
||||
<BillFormContainer
|
||||
form={form}
|
||||
disableInvNumber={billEnterModal.context.disableInvNumber}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,6 +2,7 @@ import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
@@ -101,6 +102,7 @@ export function BillEnterModalLinesComponent({
|
||||
line_desc: opt.line_desc,
|
||||
quantity: opt.part_qty || 1,
|
||||
actual_price: opt.cost,
|
||||
original_actual_price: opt.cost,
|
||||
cost_center: opt.part_type
|
||||
? bodyshop.pbs_serialnumber || bodyshop.cdk_dealerid
|
||||
? opt.part_type !== "PAE"
|
||||
@@ -227,6 +229,43 @@ export function BillEnterModalLinesComponent({
|
||||
}}
|
||||
/>
|
||||
),
|
||||
additional: (record, index) => (
|
||||
<Form.Item
|
||||
dependencies={["billlines", record.name, "actual_price"]}
|
||||
noStyle
|
||||
>
|
||||
{() => {
|
||||
const billLine = getFieldValue(["billlines", record.name]);
|
||||
const jobLine = lineData.find(
|
||||
(line) => line.id === billLine?.joblineid
|
||||
);
|
||||
|
||||
if (
|
||||
!billEdit &&
|
||||
billLine &&
|
||||
jobLine &&
|
||||
billLine?.actual_price !== jobLine?.act_price
|
||||
) {
|
||||
return (
|
||||
<Space size="small">
|
||||
<Form.Item
|
||||
noStyle
|
||||
label={t("joblines.fields.create_ppc")}
|
||||
key={`${index}ppc`}
|
||||
valuePropName="checked"
|
||||
name={[record.name, "create_ppc"]}
|
||||
>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
{t("joblines.fields.create_ppc")}
|
||||
</Space>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}}
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("billlines.fields.actual_cost"),
|
||||
@@ -368,7 +407,7 @@ export function BillEnterModalLinesComponent({
|
||||
},
|
||||
formInput: (record, index) => <Switch disabled={disabled} />,
|
||||
additional: (record, index) => (
|
||||
<Form.Item shouldUpdate style={{ display: "inline-block" }}>
|
||||
<Form.Item shouldUpdate noStyle style={{ display: "inline-block" }}>
|
||||
{() => {
|
||||
const price = getFieldValue([
|
||||
"billlines",
|
||||
@@ -673,7 +712,7 @@ const EditableCell = ({
|
||||
if (additional)
|
||||
return (
|
||||
<td {...restProps}>
|
||||
<Space size="small">
|
||||
<div size="small">
|
||||
<Form.Item
|
||||
name={dataIndex}
|
||||
labelCol={{ span: 0 }}
|
||||
@@ -682,7 +721,7 @@ const EditableCell = ({
|
||||
{(formInput && formInput(record, record.name)) || children}
|
||||
</Form.Item>
|
||||
{additional && additional(record, record.name)}
|
||||
</Space>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
if (wrapper)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -2,10 +2,10 @@ import { useQuery } from "@apollo/client";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import ContractJobsComponent from "./contract-jobs.component";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
@@ -15,6 +15,7 @@ export function ContractJobsContainer({ selectedJobState, bodyshop }) {
|
||||
const { loading, error, data } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
variables: {
|
||||
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open"],
|
||||
isConverted: true,
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
|
||||
@@ -4,14 +4,14 @@ import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import ContractsFindModalContainer from "../contracts-find-modal/contracts-find-modal.container";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
|
||||
import moment from "moment";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import moment from "moment";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
|
||||
@@ -34,6 +34,18 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
|
||||
{/* <FormFieldsChanged form={form} /> */}
|
||||
<LayoutFormRow header={t("courtesycars.labels.vehicle")}>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.year")}
|
||||
name="year"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.make")}
|
||||
name="make"
|
||||
@@ -58,18 +70,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.year")}
|
||||
name="year"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.plate")}
|
||||
name="plate"
|
||||
@@ -216,12 +216,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.nextservicekm")}
|
||||
name="nextservicekm"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
@@ -229,12 +223,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.nextservicedate")}
|
||||
name="nextservicedate"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
@@ -283,12 +271,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.registrationexpires")}
|
||||
name="registrationexpires"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
@@ -330,9 +312,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
<FormDatePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
shouldUpdate={(p, c) =>
|
||||
p.insuranceexpires !== c.insuranceexpires
|
||||
}
|
||||
shouldUpdate={(p, c) => p.insuranceexpires !== c.insuranceexpires}
|
||||
>
|
||||
{() => {
|
||||
const expires = form.getFieldValue("insuranceexpires");
|
||||
|
||||
@@ -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",
|
||||
@@ -166,6 +174,7 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
(c.year || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(c.make || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(c.model || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(c.plate || "").toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(t(c.status) || "").toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
: courtesycars;
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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") });
|
||||
|
||||
@@ -54,6 +54,7 @@ export default function GlobalSearchOs() {
|
||||
job.v_make_desc || ""
|
||||
} ${job.v_model_desc || ""}`}</span>
|
||||
<span>{`${job.clm_no || ""}`}</span>
|
||||
<span>{`${job.plate_no || ""}`}</span>
|
||||
</Space>
|
||||
</Link>
|
||||
),
|
||||
|
||||
@@ -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 = (
|
||||
|
||||
@@ -87,7 +87,8 @@ export default function JobBillsTotalComponent({
|
||||
const totalPartsSublet = Dinero(totals.parts.parts.total)
|
||||
.add(Dinero(totals.parts.sublets.total))
|
||||
.add(Dinero(totals.additional.shipping))
|
||||
.add(Dinero(totals.additional.towing));
|
||||
.add(Dinero(totals.additional.towing))
|
||||
.add(Dinero(totals.additional.additionalCosts));
|
||||
|
||||
const discrepancy = totalPartsSublet.subtract(billTotals);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { notification, Select, Space } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -56,8 +56,10 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
|
||||
<LoadingSpinner loading={loading}>
|
||||
<Select
|
||||
autoFocus
|
||||
allowClear
|
||||
dropdownMatchSelectWidth={100}
|
||||
value={location}
|
||||
onClear={() => setLocation(null)}
|
||||
onSelect={handleChange}
|
||||
onBlur={handleSave}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -19,7 +19,8 @@ export default function JobReconciliationModalComponent({ job, bills }) {
|
||||
|
||||
const jobLineData = job.joblines.filter(
|
||||
(j) =>
|
||||
(j.part_type !== null && j.part_type !== "PAE") ||
|
||||
(j.part_type !== "PAE" && j.act_price !== 0 && j.part_qty !== 0) ||
|
||||
j.misc_amt !== 0 ||
|
||||
(j.line_desc &&
|
||||
j.line_desc.toLowerCase().includes("towing") &&
|
||||
j.lbr_op === "OP13") ||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -32,42 +32,105 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
...(job.job_totals.totals.us_sales_tax_breakdown
|
||||
? [
|
||||
{
|
||||
key:
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty1?.tax_type1 ||
|
||||
"T1",
|
||||
"T1"
|
||||
} - ${[
|
||||
job.cieca_pft.ty1_rate1,
|
||||
job.cieca_pft.ty1_rate2,
|
||||
job.cieca_pft.ty1_rate3,
|
||||
job.cieca_pft.ty1_rate4,
|
||||
job.cieca_pft.ty1_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty1Tax,
|
||||
},
|
||||
{
|
||||
key:
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty2?.tax_type2 ||
|
||||
"T2",
|
||||
"T2"
|
||||
} - ${[
|
||||
job.cieca_pft.ty2_rate1,
|
||||
job.cieca_pft.ty2_rate2,
|
||||
job.cieca_pft.ty2_rate3,
|
||||
job.cieca_pft.ty2_rate4,
|
||||
job.cieca_pft.ty2_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty2Tax,
|
||||
},
|
||||
{
|
||||
key:
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty3?.tax_type3 ||
|
||||
"T3",
|
||||
"T3"
|
||||
} - ${[
|
||||
job.cieca_pft.ty3_rate1,
|
||||
job.cieca_pft.ty3_rate2,
|
||||
job.cieca_pft.ty3_rate3,
|
||||
job.cieca_pft.ty3_rate4,
|
||||
job.cieca_pft.ty3_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty3Tax,
|
||||
},
|
||||
{
|
||||
key:
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty4?.tax_type4 ||
|
||||
"T4",
|
||||
"T4"
|
||||
} - ${[
|
||||
job.cieca_pft.ty4_rate1,
|
||||
job.cieca_pft.ty4_rate2,
|
||||
job.cieca_pft.ty4_rate3,
|
||||
job.cieca_pft.ty4_rate4,
|
||||
job.cieca_pft.ty4_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty4Tax,
|
||||
},
|
||||
{
|
||||
key:
|
||||
key: `${
|
||||
bodyshop.md_responsibility_centers.taxes.tax_ty5?.tax_type5 ||
|
||||
"T5",
|
||||
"TT"
|
||||
} - ${[
|
||||
job.cieca_pft.ty5_rate1,
|
||||
job.cieca_pft.ty5_rate2,
|
||||
job.cieca_pft.ty5_rate3,
|
||||
job.cieca_pft.ty5_rate4,
|
||||
job.cieca_pft.ty5_rate5,
|
||||
]
|
||||
.filter((i) => i > 0)
|
||||
.join(", ")}%`,
|
||||
total: job.job_totals.totals.us_sales_tax_breakdown.ty5Tax,
|
||||
},
|
||||
]
|
||||
{
|
||||
key: t("jobs.labels.total_sales_tax"),
|
||||
bold: true,
|
||||
total: Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty1Tax)
|
||||
.add(
|
||||
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty2Tax)
|
||||
)
|
||||
.add(
|
||||
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty3Tax)
|
||||
)
|
||||
.add(
|
||||
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty4Tax)
|
||||
)
|
||||
.add(
|
||||
Dinero(job.job_totals.totals.us_sales_tax_breakdown.ty5Tax)
|
||||
).toJSON(),
|
||||
},
|
||||
].filter((item) => item.total.amount !== 0)
|
||||
: [
|
||||
{
|
||||
key: t("jobs.labels.state_tax_amt"),
|
||||
total: job.job_totals.totals.state_tax,
|
||||
},
|
||||
]),
|
||||
|
||||
{
|
||||
key: t("jobs.labels.total_repairs"),
|
||||
total: job.job_totals.totals.total_repairs,
|
||||
@@ -101,7 +164,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
bold: true,
|
||||
},
|
||||
];
|
||||
}, [job.job_totals, t, bodyshop.md_responsibility_centers]);
|
||||
}, [job.job_totals, job.cieca_pft, t, bodyshop.md_responsibility_centers]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}, [])
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
BranchesOutlined,
|
||||
ExclamationCircleFilled,
|
||||
PauseCircleOutlined,
|
||||
SyncOutlined,
|
||||
BranchesOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
|
||||
@@ -14,8 +14,8 @@ import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||
@@ -46,6 +46,7 @@ export function JobsReadyList({ bodyshop }) {
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
variables: {
|
||||
statuses: readyStatuses,
|
||||
isConverted: true,
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) => (
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -25,6 +25,7 @@ export function TechLookupJobsList({ bodyshop }) {
|
||||
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
|
||||
variables: {
|
||||
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
|
||||
isConverted: true,
|
||||
},
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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"),
|
||||
]}
|
||||
|
||||
@@ -5,6 +5,7 @@ export const INSERT_NEW_BILL = gql`
|
||||
insert_bills(objects: $bill) {
|
||||
returning {
|
||||
id
|
||||
invoice_number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +283,7 @@ export const GET_JOB_LINES_TO_ENTER_BILL = gql`
|
||||
id
|
||||
ro_number
|
||||
}
|
||||
v_make_desc
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { gql } from "@apollo/client";
|
||||
|
||||
export const QUERY_ALL_ACTIVE_JOBS = gql`
|
||||
query QUERY_ALL_ACTIVE_JOBS($statuses: [String!]!) {
|
||||
query QUERY_ALL_ACTIVE_JOBS($statuses: [String!]!, $isConverted: Boolean) {
|
||||
jobs(
|
||||
where: { status: { _in: $statuses } }
|
||||
where: { status: { _in: $statuses }, converted: { _eq: $isConverted } }
|
||||
order_by: { created_at: desc }
|
||||
) {
|
||||
iouparent
|
||||
@@ -432,7 +432,6 @@ export const QUERY_JOB_COSTING_DETAILS = gql`
|
||||
status
|
||||
ca_bc_pvrt
|
||||
ca_customer_gst
|
||||
|
||||
joblines(where: { removed: { _eq: false } }) {
|
||||
id
|
||||
unq_seq
|
||||
@@ -850,6 +849,7 @@ export const GET_JOB_RECONCILIATION_BY_PK = gql`
|
||||
tax_part
|
||||
db_ref
|
||||
manual_line
|
||||
misc_amt
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1057,7 +1057,6 @@ export const QUERY_TECH_JOB_DETAILS = gql`
|
||||
actual_completion
|
||||
actual_delivery
|
||||
actual_in
|
||||
|
||||
id
|
||||
ins_co_nm
|
||||
clm_no
|
||||
@@ -1298,7 +1297,7 @@ export const SEARCH_JOBS_FOR_AUTOCOMPLETE = gql`
|
||||
) {
|
||||
search_jobs(
|
||||
args: { search: $search }
|
||||
limit: 50
|
||||
limit: 25
|
||||
where: {
|
||||
_and: {
|
||||
converted: { _eq: $isConverted }
|
||||
@@ -1642,7 +1641,6 @@ export const QUERY_ALL_JOB_FIELDS = gql`
|
||||
clm_title
|
||||
clm_total
|
||||
clm_zip
|
||||
|
||||
cust_pr
|
||||
ded_amt
|
||||
ded_status
|
||||
@@ -1725,7 +1723,6 @@ export const QUERY_ALL_JOB_FIELDS = gql`
|
||||
parts_tax_rates
|
||||
pay_amt
|
||||
pay_chknm
|
||||
|
||||
pay_type
|
||||
payee_nms
|
||||
plate_no
|
||||
@@ -2133,6 +2130,7 @@ export const QUERY_JOB_EXPORT_DMS = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_RELATED_ROS = gql`
|
||||
query QUERY_RELATED_ROS($jobid: uuid!) {
|
||||
relatedjobs(
|
||||
@@ -2154,6 +2152,7 @@ export const QUERY_RELATED_ROS = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const INSERT_RELATED_ROS = gql`
|
||||
mutation INSERT_RELATED_ROS($relationship: relatedjobs_insert_input!) {
|
||||
insert_relatedjobs_one(object: $relationship) {
|
||||
@@ -2171,6 +2170,7 @@ export const INSERT_RELATED_ROS = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_RELATED_RO = gql`
|
||||
mutation DELETE_RELATED_RO($relationshipid: uuid!) {
|
||||
delete_relatedjobs_by_pk(id: $relationshipid) {
|
||||
@@ -2178,6 +2178,7 @@ export const DELETE_RELATED_RO = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_JOB_LINE_ORDERS = gql`
|
||||
query GET_JOB_LINE_ORDERS($joblineid: uuid!) {
|
||||
billlines(where: { joblineid: { _eq: $joblineid } }) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -104,6 +104,7 @@ export const SEARCH_VENDOR_AUTOCOMPLETE = gql`
|
||||
id
|
||||
cost_center
|
||||
active
|
||||
favorite
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -26,3 +26,8 @@ export const selectMessage = createSelector(
|
||||
[selectMessaging],
|
||||
(messaging) => messaging.message
|
||||
);
|
||||
|
||||
export const searchingForConversation = createSelector(
|
||||
[selectMessaging],
|
||||
(messaging) => messaging.searchingForConversation
|
||||
);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
@@ -1245,6 +1247,7 @@
|
||||
"act_price": "Retail Price",
|
||||
"ah_detail_line": "Mark as Detail Labor Line (Autohouse Only)",
|
||||
"assigned_team": "Team {{name}}",
|
||||
"create_ppc": "Create PPC?",
|
||||
"db_price": "List Price",
|
||||
"lbr_types": {
|
||||
"LA1": "LA1",
|
||||
@@ -1500,6 +1503,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",
|
||||
@@ -1892,6 +1896,7 @@
|
||||
"total_cust_payable": "Total Customer Amount Payable",
|
||||
"total_repairs": "Total Repairs",
|
||||
"total_sales": "Total Sales",
|
||||
"total_sales_tax": "Total Sales Tax",
|
||||
"totals": "Totals",
|
||||
"unvoidnote": "This job was unvoided.",
|
||||
"vehicle_info": "Vehicle",
|
||||
@@ -2659,18 +2664,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 +2696,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 +2772,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 +2786,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)",
|
||||
|
||||
@@ -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": "",
|
||||
@@ -1245,6 +1247,7 @@
|
||||
"act_price": "Precio actual",
|
||||
"ah_detail_line": "",
|
||||
"assigned_team": "",
|
||||
"create_ppc": "",
|
||||
"db_price": "Precio de base de datos",
|
||||
"lbr_types": {
|
||||
"LA1": "",
|
||||
@@ -1500,6 +1503,7 @@
|
||||
"cost_dms_acctnumber": "",
|
||||
"dms_make": "",
|
||||
"dms_model": "",
|
||||
"dms_model_override": "",
|
||||
"dms_unsold": "",
|
||||
"dms_wip_acctnumber": "",
|
||||
"id": "",
|
||||
@@ -1892,6 +1896,7 @@
|
||||
"total_cust_payable": "",
|
||||
"total_repairs": "",
|
||||
"total_sales": "",
|
||||
"total_sales_tax": "",
|
||||
"totals": "",
|
||||
"unvoidnote": "",
|
||||
"vehicle_info": "Vehículo",
|
||||
@@ -2659,18 +2664,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 +2696,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 +2772,12 @@
|
||||
"painthrs": ""
|
||||
},
|
||||
"labels": {
|
||||
"allemployeetimetickets": "",
|
||||
"asoftodaytarget": "",
|
||||
"body": "",
|
||||
"bodycharttitle": "",
|
||||
"calendarperiod": "",
|
||||
"combinedcharttitle": "",
|
||||
"dailyactual": "",
|
||||
"dailytarget": "",
|
||||
"efficiencyoverperiod": "",
|
||||
@@ -2772,12 +2786,16 @@
|
||||
"lastmonth": "",
|
||||
"lastweek": "",
|
||||
"monthlytarget": "",
|
||||
"priorweek": "",
|
||||
"productivestatistics": "",
|
||||
"productivetimeticketsoverdate": "",
|
||||
"refinish": "",
|
||||
"refinishcharttitle": "",
|
||||
"targets": "",
|
||||
"thismonth": "",
|
||||
"thisweek": "",
|
||||
"timetickets": "",
|
||||
"timeticketsemployee": "",
|
||||
"todateactual": "",
|
||||
"totaloverperiod": "",
|
||||
"weeklyactual": "",
|
||||
|
||||
@@ -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": "",
|
||||
@@ -1245,6 +1247,7 @@
|
||||
"act_price": "Prix actuel",
|
||||
"ah_detail_line": "",
|
||||
"assigned_team": "",
|
||||
"create_ppc": "",
|
||||
"db_price": "Prix de la base de données",
|
||||
"lbr_types": {
|
||||
"LA1": "",
|
||||
@@ -1500,6 +1503,7 @@
|
||||
"cost_dms_acctnumber": "",
|
||||
"dms_make": "",
|
||||
"dms_model": "",
|
||||
"dms_model_override": "",
|
||||
"dms_unsold": "",
|
||||
"dms_wip_acctnumber": "",
|
||||
"id": "",
|
||||
@@ -1892,6 +1896,7 @@
|
||||
"total_cust_payable": "",
|
||||
"total_repairs": "",
|
||||
"total_sales": "",
|
||||
"total_sales_tax": "",
|
||||
"totals": "",
|
||||
"unvoidnote": "",
|
||||
"vehicle_info": "Véhicule",
|
||||
@@ -2659,18 +2664,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 +2696,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 +2772,12 @@
|
||||
"painthrs": ""
|
||||
},
|
||||
"labels": {
|
||||
"allemployeetimetickets": "",
|
||||
"asoftodaytarget": "",
|
||||
"body": "",
|
||||
"bodycharttitle": "",
|
||||
"calendarperiod": "",
|
||||
"combinedcharttitle": "",
|
||||
"dailyactual": "",
|
||||
"dailytarget": "",
|
||||
"efficiencyoverperiod": "",
|
||||
@@ -2772,12 +2786,16 @@
|
||||
"lastmonth": "",
|
||||
"lastweek": "",
|
||||
"monthlytarget": "",
|
||||
"priorweek": "",
|
||||
"productivestatistics": "",
|
||||
"productivetimeticketsoverdate": "",
|
||||
"refinish": "",
|
||||
"refinishcharttitle": "",
|
||||
"targets": "",
|
||||
"thismonth": "",
|
||||
"thisweek": "",
|
||||
"timetickets": "",
|
||||
"timeticketsemployee": "",
|
||||
"todateactual": "",
|
||||
"totaloverperiod": "",
|
||||
"weeklyactual": "",
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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.");
|
||||
|
||||
4642
client/yarn.lock
4642
client/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -4091,29 +4089,14 @@
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/job/statustransition'
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."appointments" add column "created_by" text
|
||||
null;
|
||||
38
hasura/migrations/1699662574943_run_sql_migration/down.sql
Normal file
38
hasura/migrations/1699662574943_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,38 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- CREATE OR REPLACE FUNCTION public.search_cccontracts(search text)
|
||||
-- RETURNS SETOF cccontracts
|
||||
-- LANGUAGE plpgsql
|
||||
-- STABLE
|
||||
-- AS $function$
|
||||
-- BEGIN
|
||||
-- IF search = '' THEN
|
||||
-- RETURN query
|
||||
-- SELECT
|
||||
-- *
|
||||
-- FROM
|
||||
-- cccontracts c;
|
||||
-- ELSE
|
||||
-- RETURN query
|
||||
-- SELECT
|
||||
-- contracts.*
|
||||
-- FROM
|
||||
-- courtesycars cars,
|
||||
-- jobs jobs,
|
||||
-- cccontracts contracts
|
||||
-- WHERE (jobs.ro_number ILIKE '%' || search || '%'
|
||||
-- OR jobs.ownr_fn ILIKE '%' || search || '%'
|
||||
-- OR jobs.ownr_ln ILIKE '%' || search || '%'
|
||||
-- OR jobs.ownr_co_nm ILIKE '%' || search || '%'
|
||||
-- OR (cast(contracts.agreementnumber as text)) ILIKE '%' || search || '%'
|
||||
-- OR contracts.driver_fn ILIKE '%' || search || '%'
|
||||
-- OR contracts.driver_ln ILIKE '%' || search || '%'
|
||||
-- OR cars.fleetnumber ILIKE '%' || search || '%'
|
||||
-- OR cars.make ILIKE '%' || search || '%'
|
||||
-- OR cars.model ILIKE '%' || search || '%'
|
||||
-- OR cars.plate ILIKE '%' || search || '%')
|
||||
-- AND contracts.jobid = jobs.id
|
||||
-- AND contracts.courtesycarid = cars.id;
|
||||
-- END IF;
|
||||
-- END
|
||||
-- $function$;
|
||||
36
hasura/migrations/1699662574943_run_sql_migration/up.sql
Normal file
36
hasura/migrations/1699662574943_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
CREATE OR REPLACE FUNCTION public.search_cccontracts(search text)
|
||||
RETURNS SETOF cccontracts
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
BEGIN
|
||||
IF search = '' THEN
|
||||
RETURN query
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
cccontracts c;
|
||||
ELSE
|
||||
RETURN query
|
||||
SELECT
|
||||
contracts.*
|
||||
FROM
|
||||
courtesycars cars,
|
||||
jobs jobs,
|
||||
cccontracts contracts
|
||||
WHERE (jobs.ro_number ILIKE '%' || search || '%'
|
||||
OR jobs.ownr_fn ILIKE '%' || search || '%'
|
||||
OR jobs.ownr_ln ILIKE '%' || search || '%'
|
||||
OR jobs.ownr_co_nm ILIKE '%' || search || '%'
|
||||
OR (cast(contracts.agreementnumber as text)) ILIKE '%' || search || '%'
|
||||
OR contracts.driver_fn ILIKE '%' || search || '%'
|
||||
OR contracts.driver_ln ILIKE '%' || search || '%'
|
||||
OR cars.fleetnumber ILIKE '%' || search || '%'
|
||||
OR cars.make ILIKE '%' || search || '%'
|
||||
OR cars.model ILIKE '%' || search || '%'
|
||||
OR cars.plate ILIKE '%' || search || '%')
|
||||
AND contracts.jobid = jobs.id
|
||||
AND contracts.courtesycarid = cars.id;
|
||||
END IF;
|
||||
END
|
||||
$function$;
|
||||
7156
package-lock.json
generated
Normal file
7156
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
61
package.json
61
package.json
@@ -3,65 +3,64 @@
|
||||
"version": "0.0.1",
|
||||
"license": "UNLICENSED",
|
||||
"engines": {
|
||||
"node": ">=16.0.0",
|
||||
"node": ">=18.0.0",
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"setup": "rm -rf node_modules && yarn && cd client && rm -rf node_modules && yarn",
|
||||
"admin": "cd admin && yarn start",
|
||||
"client": "cd client && yarn start",
|
||||
"setup": "rm -rf node_modules && npm i && cd client && rm -rf node_modules && npm i",
|
||||
"admin": "cd admin && npm start",
|
||||
"client": "cd client && npm start",
|
||||
"server": "nodemon server.js",
|
||||
"build": "cd client && yarn run build",
|
||||
"dev": "concurrently --kill-others-on-fail \"yarn run server\" \"yarn run client\"",
|
||||
"deva": "concurrently --kill-others-on-fail \"yarn run server\" \"yarn run client\" \"yarn run admin\"",
|
||||
"build": "cd client && npm run build",
|
||||
"dev": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\"",
|
||||
"deva": "concurrently --kill-others-on-fail \"npm run server\" \"npm run client\" \"npm run admin\"",
|
||||
"start": "node server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.388.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.319.0",
|
||||
"@opensearch-project/opensearch": "^2.2.1",
|
||||
"aws-sdk": "^2.1326.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.454.0",
|
||||
"@aws-sdk/client-ses": "^3.454.0",
|
||||
"@aws-sdk/credential-provider-node": "^3.451.0",
|
||||
"@opensearch-project/opensearch": "^2.4.0",
|
||||
"aws4": "^1.12.0",
|
||||
"axios": "^0.27.2",
|
||||
"axios": "^1.6.2",
|
||||
"bluebird": "^3.7.2",
|
||||
"body-parser": "^1.20.2",
|
||||
"cloudinary": "^1.34.0",
|
||||
"cloudinary": "^1.41.0",
|
||||
"compression": "^1.7.4",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "2.8.5",
|
||||
"csrf": "^3.1.0",
|
||||
"dinero.js": "^1.9.1",
|
||||
"dotenv": "16.0.3",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"firebase-admin": "^11.5.0",
|
||||
"graphql": "^16.6.0",
|
||||
"graphql-request": "^4.2.0",
|
||||
"firebase-admin": "^11.11.0",
|
||||
"graphql": "^16.8.1",
|
||||
"graphql-request": "^6.1.0",
|
||||
"graylog2": "^0.2.1",
|
||||
"inline-css": "^4.0.2",
|
||||
"intuit-oauth": "^4.0.0",
|
||||
"json-2-csv": "^3.19.0",
|
||||
"json-2-csv": "^5.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.41",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"node-mailjet": "^6.0.2",
|
||||
"node-mailjet": "^6.0.4",
|
||||
"node-persist": "^3.1.3",
|
||||
"node-quickbooks": "^2.0.41",
|
||||
"nodemailer": "^6.9.1",
|
||||
"phone": "^3.1.35",
|
||||
"query-string": "^7.1.1",
|
||||
"node-quickbooks": "^2.0.43",
|
||||
"nodemailer": "^6.9.7",
|
||||
"phone": "^3.1.41",
|
||||
"recursive-diff": "^1.0.9",
|
||||
"soap": "^1.0.0",
|
||||
"socket.io": "^4.6.1",
|
||||
"ssh2-sftp-client": "^9.0.4",
|
||||
"stripe": "^9.15.0",
|
||||
"twilio": "^4.8.0",
|
||||
"uuid": "^9.0.0",
|
||||
"xml2js": "^0.4.23",
|
||||
"xmlbuilder2": "^3.0.2"
|
||||
"socket.io": "^4.7.2",
|
||||
"ssh2-sftp-client": "^9.1.0",
|
||||
"stripe": "^14.5.0",
|
||||
"twilio": "^4.19.0",
|
||||
"uuid": "^9.0.1",
|
||||
"xml2js": "^0.6.2",
|
||||
"xmlbuilder2": "^3.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^7.3.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"source-map-explorer": "^2.5.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ const path = require("path");
|
||||
const compression = require("compression");
|
||||
const twilio = require("twilio");
|
||||
const logger = require("./server/utils/logger");
|
||||
var fb = require("./server/firebase/firebase-handler");
|
||||
var cookieParser = require("cookie-parser");
|
||||
const fb = require("./server/firebase/firebase-handler");
|
||||
const cookieParser = require("cookie-parser");
|
||||
const multer = require("multer");
|
||||
const upload = multer();
|
||||
//var enforce = require("express-sslify");
|
||||
|
||||
@@ -9,7 +9,7 @@ const logger = require("../../utils/logger");
|
||||
const OAuthClient = require("intuit-oauth");
|
||||
const client = require("../../graphql-client/graphql-client").client;
|
||||
const queries = require("../../graphql-client/queries");
|
||||
const queryString = require("query-string");
|
||||
const {parse, stringify} = require("querystring");
|
||||
|
||||
const oauthClient = new OAuthClient({
|
||||
clientId: process.env.QBO_CLIENT_ID,
|
||||
@@ -30,7 +30,8 @@ if (process.env.NODE_ENV === "production") {
|
||||
}
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
const params = queryString.parse(req.url.split("?").reverse()[0]);
|
||||
const queryString = req.url.split("?").reverse()[0];
|
||||
const params = parse(queryString);
|
||||
try {
|
||||
logger.log("qbo-callback-create-token", "DEBUG", params.state, null, null);
|
||||
const authResponse = await oauthClient.createToken(req.url);
|
||||
@@ -58,7 +59,7 @@ exports.default = async (req, res) => {
|
||||
);
|
||||
|
||||
res.redirect(
|
||||
`${url}/manage/accounting/qbo?${queryString.stringify(params)}`
|
||||
`${url}/manage/accounting/qbo?${stringify(params)}`
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
@@ -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
|
||||
? ""
|
||||
|
||||
@@ -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") }),
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -7,12 +7,16 @@ require("dotenv").config({
|
||||
});
|
||||
const axios = require("axios");
|
||||
let nodemailer = require("nodemailer");
|
||||
let aws = require("aws-sdk");
|
||||
let aws = require("@aws-sdk/client-ses");
|
||||
let { defaultProvider } = require("@aws-sdk/credential-provider-node");
|
||||
|
||||
const logger = require("../utils/logger");
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
const queries = require("../graphql-client/queries");
|
||||
|
||||
const ses = new aws.SES({
|
||||
// The key apiVersion is no longer supported in v3, and can be removed.
|
||||
// @deprecated The client uses the "latest" apiVersion.
|
||||
apiVersion: "latest",
|
||||
|
||||
region: "us-east-2",
|
||||
@@ -48,7 +52,6 @@ exports.sendServerEmail = async function ({ subject, text }) {
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("server-email-failure", "error", null, null, error);
|
||||
res.status(500).json(error);
|
||||
}
|
||||
};
|
||||
exports.sendTaskEmail = async function ({ to, subject, text, attachments }) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
const AWS = require("aws-sdk");
|
||||
const awsSecretManager = require("@aws-sdk/client-secrets-manager");
|
||||
|
||||
class SecretsManager {
|
||||
/**
|
||||
@@ -8,11 +8,10 @@ class SecretsManager {
|
||||
*/
|
||||
static async getSecret(secretName, region) {
|
||||
const config = { region: region };
|
||||
let secretsManager = new AWS.SecretsManager(config);
|
||||
let secretsManager = new awsSecretManager.SecretsManager(config);
|
||||
try {
|
||||
let secretValue = await secretsManager
|
||||
.getSecretValue({ SecretId: secretName })
|
||||
.promise();
|
||||
.getSecretValue({ SecretId: secretName });
|
||||
if ("SecretString" in secretValue) {
|
||||
return secretValue.SecretString;
|
||||
} else {
|
||||
|
||||
@@ -1,121 +1,122 @@
|
||||
const Dinero = require("dinero.js");
|
||||
const queries = require("../graphql-client/queries");
|
||||
//const client = require("../graphql-client/graphql-client").client;
|
||||
const _ = require("lodash");
|
||||
const {pick} = require("lodash");
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const logger = require("../utils/logger");
|
||||
//const client = require("../graphql-client/graphql-client").client;
|
||||
|
||||
const path = require("path");
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const { Client, Connection } = require("@opensearch-project/opensearch");
|
||||
const { defaultProvider } = require("@aws-sdk/credential-provider-node");
|
||||
const {Client, Connection} = require("@opensearch-project/opensearch");
|
||||
const {defaultProvider} = require("@aws-sdk/credential-provider-node");
|
||||
const aws4 = require("aws4");
|
||||
const { gql } = require("graphql-request");
|
||||
const {gql} = require("graphql-request");
|
||||
|
||||
var host = process.env.OPEN_SEARCH_HOST;
|
||||
const createAwsConnector = (credentials, region) => {
|
||||
class AmazonConnection extends Connection {
|
||||
buildRequestObject(params) {
|
||||
const request = super.buildRequestObject(params);
|
||||
request.service = "es";
|
||||
request.region = region;
|
||||
request.headers = request.headers || {};
|
||||
request.headers["host"] = request.hostname;
|
||||
|
||||
return aws4.sign(request, credentials);
|
||||
const createAwsConnector = (credentials, region) => {
|
||||
class AmazonConnection extends Connection {
|
||||
buildRequestObject(params) {
|
||||
const request = super.buildRequestObject(params);
|
||||
request.service = "es";
|
||||
request.region = region;
|
||||
request.headers = request.headers || {};
|
||||
request.headers["host"] = request.hostname;
|
||||
|
||||
return aws4.sign(request, credentials);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
Connection: AmazonConnection,
|
||||
};
|
||||
|
||||
return {
|
||||
Connection: AmazonConnection,
|
||||
};
|
||||
};
|
||||
|
||||
const getClient = async () => {
|
||||
const credentials = await defaultProvider()();
|
||||
return new Client({
|
||||
...createAwsConnector(credentials, "ca-central-1"),
|
||||
node: host,
|
||||
});
|
||||
const credentials = await defaultProvider()();
|
||||
return new Client({
|
||||
...createAwsConnector(credentials, "ca-central-1"),
|
||||
node: host,
|
||||
});
|
||||
};
|
||||
|
||||
async function OpenSearchUpdateHandler(req, res) {
|
||||
if (req.headers["event-secret"] !== process.env.EVENT_SECRET) {
|
||||
res.status(401).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var osClient = await getClient();
|
||||
// const osClient = new Client({
|
||||
// node: `https://imex:<password>@search-imexonline-search-ixp2stfvwp6qocjsowzjzyreoy.ca-central-1.es.amazonaws.com/`,
|
||||
// });
|
||||
if (req.headers["event-secret"] !== process.env.EVENT_SECRET) {
|
||||
res.status(401).send("Unauthorized");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var osClient = await getClient();
|
||||
// const osClient = new Client({
|
||||
// node: `https://imex:<password>@search-imexonline-search-ixp2stfvwp6qocjsowzjzyreoy.ca-central-1.es.amazonaws.com/`,
|
||||
// });
|
||||
|
||||
if (req.body.event.op === "DELETE") {
|
||||
let response;
|
||||
response = await osClient.delete({
|
||||
id: req.body.event.data.old.id,
|
||||
index: req.body.table.name,
|
||||
});
|
||||
res.status(200).json(response.body);
|
||||
} else {
|
||||
let document;
|
||||
if (req.body.event.op === "DELETE") {
|
||||
let response;
|
||||
response = await osClient.delete({
|
||||
id: req.body.event.data.old.id,
|
||||
index: req.body.table.name,
|
||||
});
|
||||
res.status(200).json(response.body);
|
||||
} else {
|
||||
let document;
|
||||
|
||||
switch (req.body.table.name) {
|
||||
case "jobs":
|
||||
document = _.pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"bodyshopid",
|
||||
"clm_no",
|
||||
"clm_total",
|
||||
"comment",
|
||||
"ins_co_nm",
|
||||
"owner_owing",
|
||||
"ownr_co_nm",
|
||||
"ownr_fn",
|
||||
"ownr_ln",
|
||||
"ownr_ph1",
|
||||
"ownr_ph2",
|
||||
"plate_no",
|
||||
"ro_number",
|
||||
"status",
|
||||
"v_model_yr",
|
||||
"v_make_desc",
|
||||
"v_model_desc",
|
||||
"v_vin",
|
||||
]);
|
||||
document.bodyshopid = req.body.event.data.new.shopid;
|
||||
break;
|
||||
case "vehicles":
|
||||
document = _.pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"v_model_yr",
|
||||
"v_model_desc",
|
||||
"v_make_desc",
|
||||
"v_color",
|
||||
"v_vin",
|
||||
"plate_no",
|
||||
]);
|
||||
document.bodyshopid = req.body.event.data.new.shopid;
|
||||
break;
|
||||
case "owners":
|
||||
document = _.pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"ownr_fn",
|
||||
"ownr_ln",
|
||||
"ownr_co_nm",
|
||||
"ownr_ph1",
|
||||
"ownr_ph2",
|
||||
]);
|
||||
document.bodyshopid = req.body.event.data.new.shopid;
|
||||
break;
|
||||
case "bills":
|
||||
const bill = await client.request(
|
||||
`query ADMIN_GET_BILL_BY_ID($billId: uuid!) {
|
||||
switch (req.body.table.name) {
|
||||
case "jobs":
|
||||
document = pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"bodyshopid",
|
||||
"clm_no",
|
||||
"clm_total",
|
||||
"comment",
|
||||
"ins_co_nm",
|
||||
"owner_owing",
|
||||
"ownr_co_nm",
|
||||
"ownr_fn",
|
||||
"ownr_ln",
|
||||
"ownr_ph1",
|
||||
"ownr_ph2",
|
||||
"plate_no",
|
||||
"ro_number",
|
||||
"status",
|
||||
"v_model_yr",
|
||||
"v_make_desc",
|
||||
"v_model_desc",
|
||||
"v_vin",
|
||||
]);
|
||||
document.bodyshopid = req.body.event.data.new.shopid;
|
||||
break;
|
||||
case "vehicles":
|
||||
document = pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"v_model_yr",
|
||||
"v_model_desc",
|
||||
"v_make_desc",
|
||||
"v_color",
|
||||
"v_vin",
|
||||
"plate_no",
|
||||
]);
|
||||
document.bodyshopid = req.body.event.data.new.shopid;
|
||||
break;
|
||||
case "owners":
|
||||
document = pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"ownr_fn",
|
||||
"ownr_ln",
|
||||
"ownr_co_nm",
|
||||
"ownr_ph1",
|
||||
"ownr_ph2",
|
||||
]);
|
||||
document.bodyshopid = req.body.event.data.new.shopid;
|
||||
break;
|
||||
case "bills":
|
||||
const bill = await client.request(
|
||||
`query ADMIN_GET_BILL_BY_ID($billId: uuid!) {
|
||||
bills_by_pk(id: $billId) {
|
||||
id
|
||||
job {
|
||||
@@ -130,26 +131,26 @@ async function OpenSearchUpdateHandler(req, res) {
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ billId: req.body.event.data.new.id }
|
||||
);
|
||||
document = {
|
||||
..._.pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"date",
|
||||
"exported",
|
||||
"exported_at",
|
||||
"invoice_number",
|
||||
"is_credit_memo",
|
||||
"total",
|
||||
]),
|
||||
...bill.bills_by_pk,
|
||||
bodyshopid: bill.bills_by_pk.job.shopid,
|
||||
};
|
||||
break;
|
||||
case "payments":
|
||||
//Query to get the job and RO number
|
||||
const payment = await client.request(
|
||||
`query ADMIN_GET_PAYMENT_BY_ID($paymentId: uuid!) {
|
||||
{billId: req.body.event.data.new.id}
|
||||
);
|
||||
document = {
|
||||
...pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"date",
|
||||
"exported",
|
||||
"exported_at",
|
||||
"invoice_number",
|
||||
"is_credit_memo",
|
||||
"total",
|
||||
]),
|
||||
...bill.bills_by_pk,
|
||||
bodyshopid: bill.bills_by_pk.job.shopid,
|
||||
};
|
||||
break;
|
||||
case "payments":
|
||||
//Query to get the job and RO number
|
||||
const payment = await client.request(
|
||||
`query ADMIN_GET_PAYMENT_BY_ID($paymentId: uuid!) {
|
||||
payments_by_pk(id: $paymentId) {
|
||||
id
|
||||
job {
|
||||
@@ -170,154 +171,156 @@ async function OpenSearchUpdateHandler(req, res) {
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ paymentId: req.body.event.data.new.id }
|
||||
);
|
||||
document = {
|
||||
..._.pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"amount",
|
||||
"created_at",
|
||||
"date",
|
||||
"exportedat",
|
||||
"memo",
|
||||
"payer",
|
||||
"paymentnum",
|
||||
"transactionid",
|
||||
"type",
|
||||
]),
|
||||
...payment.payments_by_pk,
|
||||
bodyshopid: payment.payments_by_pk.job.shopid,
|
||||
};
|
||||
break;
|
||||
}
|
||||
const payload = {
|
||||
id: req.body.event.data.new.id,
|
||||
index: req.body.table.name,
|
||||
body: document,
|
||||
};
|
||||
{paymentId: req.body.event.data.new.id}
|
||||
);
|
||||
document = {
|
||||
...pick(req.body.event.data.new, [
|
||||
"id",
|
||||
"amount",
|
||||
"created_at",
|
||||
"date",
|
||||
"exportedat",
|
||||
"memo",
|
||||
"payer",
|
||||
"paymentnum",
|
||||
"transactionid",
|
||||
"type",
|
||||
]),
|
||||
...payment.payments_by_pk,
|
||||
bodyshopid: payment.payments_by_pk.job.shopid,
|
||||
};
|
||||
break;
|
||||
}
|
||||
const payload = {
|
||||
id: req.body.event.data.new.id,
|
||||
index: req.body.table.name,
|
||||
body: document,
|
||||
};
|
||||
|
||||
let response;
|
||||
response = await osClient.index(payload);
|
||||
console.log(response.body);
|
||||
res.status(200).json(response.body);
|
||||
let response;
|
||||
response = await osClient.index(payload);
|
||||
console.log(response.body);
|
||||
res.status(200).json(response.body);
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(400).json(JSON.stringify(error));
|
||||
} finally {
|
||||
}
|
||||
} catch (error) {
|
||||
res.status(400).json(JSON.stringify(error));
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
async function OpensearchSearchHandler(req, res) {
|
||||
try {
|
||||
const { search, bodyshopid, index } = req.body;
|
||||
if (!req.user) {
|
||||
res.sendStatus(401);
|
||||
return;
|
||||
}
|
||||
logger.log("os-search", "DEBUG", req.user.email, null, {
|
||||
search,
|
||||
});
|
||||
async function OpenSearchSearchHandler(req, res) {
|
||||
try {
|
||||
const {search, bodyshopid, index} = req.body;
|
||||
|
||||
const BearerToken = req.headers.authorization;
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
});
|
||||
if (!req.user) {
|
||||
res.sendStatus(401);
|
||||
return;
|
||||
}
|
||||
|
||||
const assocs = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
.request(queries.ACTIVE_SHOP_BY_USER, {
|
||||
user: req.user.email,
|
||||
});
|
||||
logger.log("os-search", "DEBUG", req.user.email, null, {
|
||||
search,
|
||||
});
|
||||
|
||||
if (assocs.length === 0) {
|
||||
res.sendStatus(401);
|
||||
}
|
||||
|
||||
var osClient = await getClient();
|
||||
|
||||
const { body } = await osClient.search({
|
||||
...(index ? { index } : {}),
|
||||
body: {
|
||||
size: 100,
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
match: {
|
||||
bodyshopid: assocs.associations[0].shopid,
|
||||
},
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
multi_match: {
|
||||
query: search,
|
||||
type: "cross_fields",
|
||||
fields: ["*ownr_fn", "*ownr_ln"],
|
||||
},
|
||||
},
|
||||
{
|
||||
multi_match: {
|
||||
query: search,
|
||||
type: "most_fields",
|
||||
fields: [
|
||||
"*v_model_yr",
|
||||
"*v_make_desc^2",
|
||||
"*v_model_desc^3",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
query_string: {
|
||||
query: `*${search}*`,
|
||||
|
||||
fields: [
|
||||
"*ro_number^20",
|
||||
"*clm_no^14",
|
||||
"*v_vin^12",
|
||||
"*plate_no^12",
|
||||
"*ownr_ln^10",
|
||||
"transactionid^10",
|
||||
"paymentnum^10",
|
||||
"invoice_number^10",
|
||||
"*ownr_fn^8",
|
||||
"*ownr_co_nm^8",
|
||||
"*ownr_ph1^8",
|
||||
"*ownr_ph2^8",
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
sort: [
|
||||
{
|
||||
_score: {
|
||||
order: "desc",
|
||||
const BearerToken = req.headers.authorization;
|
||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||
headers: {
|
||||
Authorization: BearerToken,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
res.json(body);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("os-search-error", "ERROR", req.user.email, null, {
|
||||
error: JSON.stringify(error),
|
||||
});
|
||||
res.status(400).json(error);
|
||||
} finally {
|
||||
}
|
||||
const assocs = await client
|
||||
.setHeaders({Authorization: BearerToken})
|
||||
.request(queries.ACTIVE_SHOP_BY_USER, {
|
||||
user: req.user.email,
|
||||
});
|
||||
|
||||
if (assocs.length === 0) {
|
||||
res.sendStatus(401);
|
||||
}
|
||||
|
||||
const osClient = await getClient();
|
||||
|
||||
const {body} = await osClient.search({
|
||||
...(index ? {index} : {}),
|
||||
body: {
|
||||
size: 100,
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
match: {
|
||||
bodyshopid: assocs.associations[0].shopid,
|
||||
},
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
multi_match: {
|
||||
query: search,
|
||||
type: "cross_fields",
|
||||
fields: ["*ownr_fn", "*ownr_ln"],
|
||||
},
|
||||
},
|
||||
{
|
||||
multi_match: {
|
||||
query: search,
|
||||
type: "most_fields",
|
||||
fields: [
|
||||
"*v_model_yr",
|
||||
"*v_make_desc^2",
|
||||
"*v_model_desc^3",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
query_string: {
|
||||
query: `*${search}*`,
|
||||
// Weighted Fields
|
||||
fields: [
|
||||
"*ro_number^20",
|
||||
"*clm_no^14",
|
||||
"*v_vin^12",
|
||||
"*plate_no^12",
|
||||
"*ownr_ln^10",
|
||||
"transactionid^10",
|
||||
"paymentnum^10",
|
||||
"invoice_number^10",
|
||||
"*ownr_fn^8",
|
||||
"*ownr_co_nm^8",
|
||||
"*ownr_ph1^8",
|
||||
"*ownr_ph2^8",
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
minimum_should_match: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
sort: [
|
||||
{
|
||||
_score: {
|
||||
order: "desc",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
res.json(body);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("os-search-error", "ERROR", req.user.email, null, {
|
||||
error: JSON.stringify(error),
|
||||
});
|
||||
res.status(400).json(error);
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
exports.handler = OpenSearchUpdateHandler;
|
||||
exports.search = OpensearchSearchHandler;
|
||||
exports.search = OpenSearchSearchHandler;
|
||||
|
||||
@@ -250,8 +250,7 @@ function CalculateExpectedHoursForJob(job, filterToLbrTypes) {
|
||||
.forEach((jobline) => {
|
||||
if (jobline.convertedtolbr) {
|
||||
// Line has been converte to labor. Temporarily re-assign the hours.
|
||||
jobline.mod_lbr_ty =
|
||||
jobline.mod_lbr_ty || jobline.convertedtolbr_data.mod_lbr_ty;
|
||||
jobline.mod_lbr_ty = jobline.convertedtolbr_data.mod_lbr_ty;
|
||||
jobline.mod_lb_hrs += jobline.convertedtolbr_data.mod_lb_hrs;
|
||||
}
|
||||
if (jobline.mod_lb_hrs != 0) {
|
||||
|
||||
@@ -1,79 +1,46 @@
|
||||
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||
const path = require("path");
|
||||
const queries = require("../graphql-client/queries");
|
||||
const Dinero = require("dinero.js");
|
||||
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
|
||||
|
||||
exports.payment = async (req, res) => {
|
||||
const { amount, stripe_acct_id } = req.body;
|
||||
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
|
||||
|
||||
try {
|
||||
await stripe.paymentIntents
|
||||
.create(
|
||||
{
|
||||
payment_method_types: ["card"],
|
||||
amount: amount,
|
||||
currency: "cad",
|
||||
application_fee_amount: 50,
|
||||
},
|
||||
{
|
||||
stripeAccount: stripe_acct_id,
|
||||
}
|
||||
)
|
||||
.then(function (paymentIntent) {
|
||||
try {
|
||||
return res.send({
|
||||
clientSecret: paymentIntent.client_secret,
|
||||
});
|
||||
} catch (err) {
|
||||
return res.status(500).send({
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
res.status(400).send(error);
|
||||
}
|
||||
const processor = async (req, res) => {
|
||||
const {amount, stripe_acct_id} = req.body;
|
||||
|
||||
try {
|
||||
await stripe.paymentIntents
|
||||
.create(
|
||||
{
|
||||
payment_method_types: ["card"],
|
||||
amount: amount,
|
||||
currency: "cad",
|
||||
application_fee_amount: 50,
|
||||
},
|
||||
{
|
||||
stripeAccount: stripe_acct_id,
|
||||
}
|
||||
)
|
||||
.then(function (paymentIntent) {
|
||||
try {
|
||||
return res.send({
|
||||
clientSecret: paymentIntent.client_secret,
|
||||
});
|
||||
} catch (err) {
|
||||
return res.status(500).send({
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
res.status(400).send(error);
|
||||
}
|
||||
};
|
||||
|
||||
exports.mobile_payment = async (req, res) => {
|
||||
const { amount, stripe_acct_id } = req.body;
|
||||
|
||||
try {
|
||||
await stripe.paymentIntents
|
||||
.create(
|
||||
{
|
||||
//Pull the amounts from the payment request.
|
||||
payment_method_types: ["card"],
|
||||
amount: amount,
|
||||
currency: "cad",
|
||||
application_fee_amount: 50,
|
||||
},
|
||||
{
|
||||
stripeAccount: stripe_acct_id,
|
||||
}
|
||||
)
|
||||
.then(function (paymentIntent) {
|
||||
try {
|
||||
return res.send({
|
||||
clientSecret: paymentIntent.client_secret,
|
||||
});
|
||||
} catch (err) {
|
||||
return res.status(500).send({
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
res.status(400).send(error);
|
||||
}
|
||||
};
|
||||
exports.payment = processor;
|
||||
exports.mobile_payment = processor;
|
||||
|
||||
@@ -1,92 +1,84 @@
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const axios = require("axios");
|
||||
const client = require("../graphql-client/graphql-client").client;
|
||||
const emailer = require("../email/sendemail");
|
||||
const logger = require("../utils/logger");
|
||||
const moment = require("moment-timezone");
|
||||
const converter = require("json-2-csv");
|
||||
|
||||
exports.taskHandler = async (req, res) => {
|
||||
try {
|
||||
const { bodyshopid, query, variables, text, to, subject, timezone } =
|
||||
req.body;
|
||||
//Run the query
|
||||
try {
|
||||
const {bodyshopid, query, variables, text, to, subject, timezone} = req.body;
|
||||
|
||||
//Check the variables to see if they are an object.
|
||||
Object.keys(variables).forEach((key) => {
|
||||
if (typeof variables[key] === "object") {
|
||||
if (variables[key].function) {
|
||||
variables[key] = functionMapper(variables[key].function, timezone);
|
||||
}
|
||||
}
|
||||
});
|
||||
//Check the variables to see if they are an object.
|
||||
Object.keys(variables).forEach((key) => {
|
||||
if (typeof variables[key] === "object") {
|
||||
if (variables[key].function) {
|
||||
variables[key] = functionMapper(variables[key].function, timezone);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const response = await client.request(query, variables);
|
||||
//Massage the data
|
||||
//Send the email
|
||||
const rootElement = response[Object.keys(response)[0]]; //This element shoudl always be an array.
|
||||
let converter = require("json-2-csv");
|
||||
converter.json2csv(
|
||||
rootElement,
|
||||
(err, csv) => {
|
||||
if (err) {
|
||||
res.status(500).json(err);
|
||||
}
|
||||
const response = await client.request(query, variables);
|
||||
const rootElement = response[Object.keys(response)[0]]; //This element should always be an array.
|
||||
|
||||
const csv = converter.json2csv(rootElement, {emptyFieldValue: ""});
|
||||
|
||||
emailer.sendTaskEmail({
|
||||
to,
|
||||
subject,
|
||||
text,
|
||||
attachments: [{ filename: "query.csv", content: csv }],
|
||||
to,
|
||||
subject,
|
||||
text,
|
||||
attachments: [{filename: "query.csv", content: csv}],
|
||||
}).catch(err => {
|
||||
console.error('Errors sending CSV Email.')
|
||||
});
|
||||
res.status(200).send(csv);
|
||||
},
|
||||
{ emptyFieldValue: "" }
|
||||
);
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message, stack: error.stackTrace });
|
||||
}
|
||||
|
||||
return res.status(200).send(csv);
|
||||
} catch (error) {
|
||||
res.status(500).json({error: error.message, stack: error.stackTrace});
|
||||
}
|
||||
};
|
||||
|
||||
const isoformat = "YYYY-MM-DD";
|
||||
function functionMapper(f, timezone) {
|
||||
switch (f) {
|
||||
case "date.today":
|
||||
return moment().tz(timezone).format(isoformat);
|
||||
case "date.now":
|
||||
return moment().tz(timezone);
|
||||
case "date.yesterday":
|
||||
return moment().tz(timezone).subtract(1, "day").format(isoformat);
|
||||
case "date.3daysago":
|
||||
return moment().tz(timezone).subtract(3, "days").format(isoformat);
|
||||
case "date.7daysago":
|
||||
return moment().tz(timezone).subtract(7, "days").format(isoformat);
|
||||
case "date.tomorrow":
|
||||
return moment().tz(timezone).add(1, "day").format(isoformat);
|
||||
case "date.3daysfromnow":
|
||||
return moment().tz(timezone).add(3, "days").format(isoformat);
|
||||
case "date.7daysfromnow":
|
||||
return moment().tz(timezone).add(7, "days").format(isoformat);
|
||||
case "date.yesterdaytz":
|
||||
return moment().tz(timezone).subtract(1, "day");
|
||||
case "date.3daysagotz":
|
||||
return moment().tz(timezone).subtract(3, "days");
|
||||
case "date.7daysagotz":
|
||||
return moment().tz(timezone).subtract(7, "days");
|
||||
case "date.tomorrowtz":
|
||||
return moment().tz(timezone).add(1, "day");
|
||||
case "date.3daysfromnowtz":
|
||||
return moment().tz(timezone).add(3, "days");
|
||||
case "date.7daysfromnowtz":
|
||||
return moment().tz(timezone).add(7, "days");
|
||||
const isoFormat = "YYYY-MM-DD";
|
||||
|
||||
case "date.now":
|
||||
return moment().tz(timezone);
|
||||
default:
|
||||
return f;
|
||||
}
|
||||
function functionMapper(f, timezone) {
|
||||
switch (f) {
|
||||
case "date.today":
|
||||
return moment().tz(timezone).format(isoFormat);
|
||||
case "date.now":
|
||||
return moment().tz(timezone);
|
||||
case "date.yesterday":
|
||||
return moment().tz(timezone).subtract(1, "day").format(isoFormat);
|
||||
case "date.3daysago":
|
||||
return moment().tz(timezone).subtract(3, "days").format(isoFormat);
|
||||
case "date.7daysago":
|
||||
return moment().tz(timezone).subtract(7, "days").format(isoFormat);
|
||||
case "date.tomorrow":
|
||||
return moment().tz(timezone).add(1, "day").format(isoFormat);
|
||||
case "date.3daysfromnow":
|
||||
return moment().tz(timezone).add(3, "days").format(isoFormat);
|
||||
case "date.7daysfromnow":
|
||||
return moment().tz(timezone).add(7, "days").format(isoFormat);
|
||||
case "date.yesterdaytz":
|
||||
return moment().tz(timezone).subtract(1, "day");
|
||||
case "date.3daysagotz":
|
||||
return moment().tz(timezone).subtract(3, "days");
|
||||
case "date.7daysagotz":
|
||||
return moment().tz(timezone).subtract(7, "days");
|
||||
case "date.tomorrowtz":
|
||||
return moment().tz(timezone).add(1, "day");
|
||||
case "date.3daysfromnowtz":
|
||||
return moment().tz(timezone).add(3, "days");
|
||||
case "date.7daysfromnowtz":
|
||||
return moment().tz(timezone).add(7, "days");
|
||||
|
||||
case "date.now":
|
||||
return moment().tz(timezone);
|
||||
default:
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user