Compare commits

..

160 Commits

Author SHA1 Message Date
Patrick Fic
09187bdb7f Remove manual scheduling for CI. 2021-10-25 14:57:27 -07:00
Patrick Fic
2aed392fbe IO-658 IO-652 IO-657 add MPI Templates. 2021-10-25 14:46:24 -07:00
Patrick Fic
1a0054a911 IO-1438 Resolve inbound conversation creation. 2021-10-25 11:00:31 -07:00
Patrick Fic
6e6f3d3d3e Resolve user email naming if null. 2021-10-25 10:46:30 -07:00
Patrick Fic
29df140680 IO-1402 Respect blocked day today for scheduling. 2021-10-25 10:41:22 -07:00
Patrick Fic
f37c67a122 Merge branch 'master' into release/2021-10-29 2021-10-25 10:31:49 -07:00
Patrick Fic
e53c9aab72 IO-1379 CSI invite Key update. 2021-10-25 10:24:18 -07:00
Patrick Fic
385ad06adc IO-1375 Special characters in CDK 2021-10-25 10:12:43 -07:00
Patrick Fic
d1e2d943a9 Package updates. 2021-10-25 09:41:54 -07:00
Patrick Fic
90aa3557b7 Merged in release/2021-10-22 (pull request #255)
Change Nod Version.

Approved-by: Patrick Fic
2021-10-22 21:38:53 +00:00
Patrick Fic
8891167183 Merged in release/2021-10-22 (pull request #254)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-22 21:38:35 +00:00
Patrick Fic
6631e645df Change Nod Version. 2021-10-22 14:38:05 -07:00
Patrick Fic
b7f202969b Merged in release/2021-10-22 (pull request #253)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-22 21:29:49 +00:00
Patrick Fic
1dc3353ecc Merge branch 'release/2021-10-22' into test 2021-10-22 13:01:41 -07:00
Patrick Fic
b1a3f1a7b8 Finish revert stripe removal. 2021-10-22 13:01:26 -07:00
Patrick Fic
89f3a26635 Revert "Disable stripe promise."
This reverts commit 3ece5e0ba2.
2021-10-22 12:59:45 -07:00
Patrick Fic
506fe9b1af Merge branch 'release/2021-10-22' into test 2021-10-22 10:47:12 -07:00
Patrick Fic
37c898d3ce IO-256 Resolve QBO issues. 2021-10-22 10:46:03 -07:00
Patrick Fic
e4eac5714a Merge branch 'release/2021-10-22' into test 2021-10-22 10:00:42 -07:00
Patrick Fic
9d4a59ca16 IO-256 Remove credentials with Axios calls for QBO. 2021-10-22 10:00:23 -07:00
Patrick Fic
8ec524061a Merge branch 'release/2021-10-22' into test 2021-10-21 14:32:06 -07:00
Patrick Fic
434ed46b5a IO-1469 Add keep schedule details to job detail reschedule. 2021-10-21 14:30:50 -07:00
Patrick Fic
3ece5e0ba2 Disable stripe promise. 2021-10-21 14:16:18 -07:00
Patrick Fic
52a383ffb7 IO-1454 Disable production colors & add feature flags. 2021-10-21 14:03:48 -07:00
Patrick Fic
8ad1d5929a Reduce Sentry Logging. 2021-10-21 12:02:02 -07:00
Patrick Fic
445c01499b Merge branch 'release/2021-10-22' into test 2021-10-21 11:42:33 -07:00
Patrick Fic
0b05be841d Merge branch 'release/2021-10-22' of bitbucket.org:snaptsoft/bodyshop into release/2021-10-22 2021-10-21 11:42:10 -07:00
Patrick Fic
6bcb5f2af5 IO-1446 Smart Schedule Blocked Day Fixes 2021-10-21 11:42:05 -07:00
Patrick Fic
7482751c5b IO-1478 Make CC Contract RO lines editable. 2021-10-21 09:43:14 -07:00
Patrick Fic
7a025fff42 Merge branch 'release/2021-10-22' into test 2021-10-21 08:59:49 -07:00
Patrick Fic
602fe36638 QBO Cleanup. 2021-10-21 08:58:24 -07:00
Patrick Fic
da08fc74f1 IO-1475 Totals update for Mitchell Cloud discounts. 2021-10-21 08:53:15 -07:00
Patrick Fic
14af45baf0 IO-256 QBO SS Realm ID 2021-10-20 14:33:31 -07:00
Patrick Fic
420a88c505 Merged in release/2021-10-22 (pull request #252)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-19 05:12:44 +00:00
Patrick Fic
f3c44f8dd1 Merge branch 'feature/pbs' into release/2021-10-22 2021-10-18 22:12:16 -07:00
Patrick Fic
c72111e18b Merged in release/2021-10-22 (pull request #251)
release/2021-10-22

Approved-by: Patrick Fic
2021-10-19 05:04:35 +00:00
Patrick Fic
1ca2870912 IO-1437 Add deductible Note 2021-10-18 22:02:45 -07:00
Patrick Fic
db9744e1e5 IO-1454 Production List Visual Indicators. 2021-10-18 21:54:24 -07:00
Patrick Fic
e700095551 IO-1492 Add estimator Presets 2021-10-18 20:26:57 -07:00
Patrick Fic
99196a77ed IO-1433 Add Loss of Use Field 2021-10-18 19:56:12 -07:00
Patrick Fic
289a8222a0 IO-223 ARM development 2021-10-18 19:37:27 -07:00
Patrick Fic
dc10f8d35b IO-1469 Retain event detals when rescheduling. 2021-10-18 13:56:48 -07:00
Patrick Fic
f448232fe7 IO-1435 Missed additional query. 2021-10-18 13:52:30 -07:00
Patrick Fic
4baf4b4afa IO-1435 Add scheduled time to job header if scheduled. 2021-10-18 13:48:23 -07:00
Patrick Fic
1785093023 IO-1449 Adjust display of time in schedulign setup. 2021-10-18 13:43:15 -07:00
Patrick Fic
0d65f8d894 IO-1407 Resolve bill not clearing on enter again 2021-10-18 13:40:31 -07:00
Patrick Fic
3d8c390291 IO-1408 Resolve bill form index issue. 2021-10-18 13:29:35 -07:00
Patrick Fic
f0a13856bc IO-1439 Trim QB name fields on export. 2021-10-18 12:16:12 -07:00
Patrick Fic
ad6394783d IO-1443 Expand Priority selection to 15. 2021-10-18 11:22:01 -07:00
Patrick Fic
16e9843298 Package Updates. 2021-10-18 11:21:51 -07:00
Patrick Fic
a4c949c376 Merged in release/2021-10-22 (pull request #250)
Release/2021 10 22
2021-10-18 17:01:18 +00:00
Patrick Fic
0e0d5316b7 Undo Quickbooks CORS to resolve prod error. 2021-10-18 07:55:36 -07:00
Patrick Fic
60cb6ee8fb IO-223 Begin ARMS Integration 2021-10-15 17:55:55 -07:00
Patrick Fic
7d3279d21a Merged in release/2021-10-15 (pull request #249)
Release/2021 10 15
2021-10-15 21:33:24 +00:00
Patrick Fic
402d13ad99 Merge branch 'test' into release/2021-10-15 2021-10-15 10:53:50 -07:00
Patrick Fic
e803f5a2d4 IO-1471 Total Hrs in produciton. 2021-10-15 10:53:24 -07:00
Patrick Fic
3989d0f1e2 Merge branch 'test' into feature/pbs 2021-10-14 17:20:15 -07:00
Patrick Fic
161d476ab3 IO-256 Export payments with no matching invoice & export cust data. 2021-10-14 15:07:31 -07:00
Patrick Fic
ce84a89cf3 Merge branch 'release/2021-10-15' into test 2021-10-14 11:22:14 -07:00
Patrick Fic
1d210a9e52 IO-1468 QB and CDK Updates for MCE Markup 2021-10-14 11:21:06 -07:00
Patrick Fic
660f463aea IO-1468 Resolve line markup for MCE. 2021-10-14 10:52:08 -07:00
Patrick Fic
ad9f01111c Merge branch 'release/2021-10-15' into test 2021-10-13 21:22:55 -07:00
Patrick Fic
1b885e4114 IO-256 Add description to receivables export. 2021-10-13 21:22:38 -07:00
Patrick Fic
b56742bcb2 IO-256 QBO Improvements. 2021-10-13 21:12:02 -07:00
Patrick Fic
40d4d69a9a Merge branch 'release/2021-10-15' into test 2021-10-13 17:03:00 -07:00
Patrick Fic
b54d5beb76 IO-256 Resolve Bill Center reference 2021-10-13 17:02:39 -07:00
Patrick Fic
5cbcd440f5 Merge branch 'feature/qbo' into test 2021-10-13 15:49:02 -07:00
Patrick Fic
1cdc34249a IO-256 Fix null handling for missing metadata. 2021-10-13 15:48:43 -07:00
Patrick Fic
8bbb218777 Merge branch 'feature/qbo' into test
# Conflicts:
#	server/accounting/qbo/qbo-payables.js
#	server/accounting/qbo/qbo-payments.js
#	server/accounting/qbo/qbo-receivables.js
2021-10-13 15:25:02 -07:00
Patrick Fic
755ac7f657 IO-256 Improved Logging 2021-10-13 15:24:14 -07:00
Patrick Fic
d7b884ff86 Revert "IO-256 Genericize email."
This reverts commit fda3620ed0.
2021-10-13 15:14:26 -07:00
Patrick Fic
fda3620ed0 IO-256 Genericize email. 2021-10-13 15:10:36 -07:00
Patrick Fic
61ad9f0d58 IO-256 Add CORS back for QBO. 2021-10-13 15:01:59 -07:00
Patrick Fic
804c8ad40a IO-256 Add cookie location 2021-10-13 14:49:47 -07:00
Patrick Fic
0ddf009f8f Reverse CORS & Fix Cookie Setting. 2021-10-13 14:35:39 -07:00
Patrick Fic
14309b5c96 QBO CORS Updates 2021-10-13 14:06:25 -07:00
Patrick Fic
57d9de469a Add CORS to Server. 2021-10-13 14:01:59 -07:00
Patrick Fic
404ade396c QB URL Updates. 2021-10-13 13:39:04 -07:00
Patrick Fic
bdad6da6d9 QB URL Fix. 2021-10-13 13:35:42 -07:00
Patrick Fic
e7ef3b94c1 QB Env update. 2021-10-13 13:34:56 -07:00
Patrick Fic
c19c92ab7e QBO port fix. 2021-10-13 13:25:15 -07:00
Patrick Fic
944229bae3 Update process env. 2021-10-13 13:22:56 -07:00
Patrick Fic
661b05d9e3 Merge branch 'feature/qbo' into test 2021-10-13 13:15:48 -07:00
Patrick Fic
4d1d471a66 Merge branch 'release/2021-10-15' into test 2021-10-13 13:15:35 -07:00
Patrick Fic
3e84fbbaf4 IO-1458 IO-1465 Sorting Fixes. 2021-10-13 11:52:11 -07:00
Patrick Fic
c4e59c1a5e IO-117 Begin PBS Structure. 2021-10-13 11:45:24 -07:00
Patrick Fic
9ee8e9007a IO-256 Add Payables Posting. 2021-10-12 20:18:06 -07:00
Patrick Fic
4d52a5c44a IO-256 Exporting of payments 2021-10-12 19:07:43 -07:00
Patrick Fic
fff9073f9d IO-256 Add vendor credits. 2021-10-12 16:54:28 -07:00
Patrick Fic
71f0b8a005 Merge branch 'release/2021-10-15' into test 2021-10-12 16:41:01 -07:00
Patrick Fic
d2b965f79e Resolve CI Issue. 2021-10-12 16:40:27 -07:00
Patrick Fic
a02aa71a95 Merged in release/2021-10-15 (pull request #248)
release/2021-10-15

Approved-by: Patrick Fic
2021-10-12 23:33:17 +00:00
Patrick Fic
7562bf5c95 IO-1418 Allow owner re-search on import. 2021-10-12 16:17:31 -07:00
Patrick Fic
f9521483e2 IO-1448 Disable negative deductible amounts. 2021-10-12 14:39:20 -07:00
Patrick Fic
ca85858885 IO-1453 Allow null scheduled completion dates on cehcklist view. 2021-10-12 14:27:22 -07:00
Patrick Fic
c7b3a94533 IO-1441 Conversion safety check. 2021-10-12 14:18:09 -07:00
Patrick Fic
b010c9ecb0 IO-1458 Production Board Sort ORder 2021-10-12 13:50:25 -07:00
Patrick Fic
0b98d04bac IO-1458 Production Board Sort Order 2021-10-12 13:49:32 -07:00
Patrick Fic
e25e388e59 Merged in release/2021-10-15 (pull request #247)
IO-233 Replace unknown CDK function.

Approved-by: Patrick Fic
2021-10-12 19:06:35 +00:00
Patrick Fic
10654b7916 IO-233 Replace unknown CDK function. 2021-10-12 12:04:50 -07:00
Patrick Fic
ff049ad3e8 Merged in release/2021-10-08 (pull request #246)
release/2021-10-08
2021-10-09 15:29:49 +00:00
Patrick Fic
3572fff2c1 Merged in release/2021-10-08 (pull request #245)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-07 23:38:03 +00:00
Patrick Fic
774e4cdf94 IO-1430 Add note on last contacted update. 2021-10-07 16:26:48 -07:00
Patrick Fic
65ade5cab8 IO-1428 add ph2 to global search. 2021-10-07 16:00:35 -07:00
Patrick Fic
bf21a073fb IO-1415 Update cache on line delete. 2021-10-07 15:48:29 -07:00
Patrick Fic
851f1c265f Merged in release/2021-10-08 (pull request #244)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-07 16:51:36 +00:00
Patrick Fic
51f3b5927b IO-1428 IO-1338 Add ownr_ph2. 2021-10-07 09:47:10 -07:00
Patrick Fic
42c779f368 IO-1415 Default tax part to true. 2021-10-07 09:19:37 -07:00
Patrick Fic
c104ee4fd9 Merged in release/2021-10-08 (pull request #243)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-06 20:37:34 +00:00
Patrick Fic
0b2efa31b5 IO-1375 IO-1383 CDK Tax Addition 2021-10-06 13:37:14 -07:00
Patrick Fic
17baa8fcb2 IO-1404 Rates in shop info minimums 2021-10-05 16:36:53 -07:00
Patrick Fic
e550baf59d Merged in release/2021-10-08 (pull request #242)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-05 23:20:08 +00:00
Patrick Fic
fd53eb92e6 Merge branch 'test' into release/2021-10-08 2021-10-05 16:19:41 -07:00
Patrick Fic
6521c0bfb8 IO-1413 Change color of kanban background 2021-10-05 14:22:45 -07:00
Patrick Fic
50489dd682 IO-1415 Add tax part & refresh line ticket on line edit 2021-10-05 14:15:21 -07:00
Patrick Fic
220afa5add IO-1405 resolve shift label issues. 2021-10-05 14:05:16 -07:00
Patrick Fic
9d549b02fe IO-1410 Remove ded from lbr lines on reconciliation. 2021-10-05 13:59:43 -07:00
Patrick Fic
ca2ded047b Merged in release/2021-10-08 (pull request #241)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-05 18:48:13 +00:00
Patrick Fic
c423e61ce8 IO-1422 Resolve send email with attachments. 2021-10-05 11:47:37 -07:00
Patrick Fic
65550c7bf4 IO-1436 App tax part fix to all regions. 2021-10-05 11:36:21 -07:00
Patrick Fic
cdb4da9e5f IO-1412 Resolve production list saving and loading. 2021-10-05 09:40:25 -07:00
Patrick Fic
b59a5303c6 IO-1430 add Last Contacted date to job. 2021-10-05 09:12:44 -07:00
Patrick Fic
9a72abbed0 IO-1426 Add time to date detail card. 2021-10-05 08:53:20 -07:00
Patrick Fic
762a5ff01b IO-1432 Add offset for speed print. 2021-10-05 08:49:38 -07:00
Patrick Fic
2f6571e703 Package updates. 2021-10-05 07:59:51 -07:00
Patrick Fic
80c90f1819 IO-1399 Parts Order Modal Updates 2021-10-04 16:13:33 -07:00
Patrick Fic
4b0c2c60a2 IO-1398 Parts Order Mark Duplication 2021-10-04 16:00:05 -07:00
Patrick Fic
ee7d7d2f6a IO-1397 Preview cards consistency updates. 2021-10-04 15:55:43 -07:00
Patrick Fic
0ad670013b IO-1395 Add missing info to job preview. 2021-10-04 15:48:28 -07:00
Patrick Fic
064a66aa66 IO-1329 Filtering on bill line posting 2021-10-04 14:37:37 -07:00
Patrick Fic
f46c19b152 Merged in release/2021-10-08 (pull request #239)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-04 20:35:21 +00:00
Patrick Fic
ea2c583b26 IO-1419 Resolve hsaura migration issues with new field. 2021-10-04 13:20:35 -07:00
Patrick Fic
7bd2a90141 Merged in release/2021-10-08 (pull request #238)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-04 19:11:34 +00:00
Patrick Fic
632549dd9d IO-1406 Parts Order label update 2021-10-04 12:08:47 -07:00
Patrick Fic
70ef274821 IO-1404 IO-1403 Remove negative value options for 2021-10-04 12:08:29 -07:00
Patrick Fic
7d9759fda4 IO-1419 Added comments to parts orders. 2021-10-04 11:59:33 -07:00
Patrick Fic
0fd06d5e4e IO-1421 Add day of week to smart scheduling buttons. 2021-10-04 11:48:55 -07:00
Patrick Fic
4995e44e06 IO-1424 Add other referral source 2021-10-04 11:43:33 -07:00
Patrick Fic
fd43d7d56d Merged in release/2021-10-08 (pull request #237)
JSR CHange

Approved-by: Patrick Fic
2021-10-04 14:57:42 +00:00
Patrick Fic
96f292f61c JSR CHange 2021-10-04 07:57:14 -07:00
Patrick Fic
e899e4545f Merged in release/2021-10-08 (pull request #236)
Release/2021 10 08
2021-10-02 06:19:45 +00:00
Patrick Fic
c62f5e3911 Merged in release/2021-10-08 (pull request #235)
Resolve CI.

Approved-by: Patrick Fic
2021-10-01 22:56:48 +00:00
Patrick Fic
9f1d184081 Resolve CI. 2021-10-01 15:56:24 -07:00
Patrick Fic
29e596eedb Merged in release/2021-10-08 (pull request #234)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-01 22:41:36 +00:00
Patrick Fic
6e28afda67 IO-1417 Resolve custom templates printing. 2021-10-01 15:41:17 -07:00
Patrick Fic
eca7e9fba1 Merged in release/2021-10-08 (pull request #233)
IO-1392 Add IOU Form to Print Center

Approved-by: Patrick Fic
2021-10-01 16:25:04 +00:00
Patrick Fic
aa2ac2b296 IO-1392 Add IOU Form to Print Center 2021-10-01 09:24:37 -07:00
Patrick Fic
ce57752b95 Merged in release/2021-10-08 (pull request #232)
release/2021-10-08

Approved-by: Patrick Fic
2021-10-01 00:49:16 +00:00
Patrick Fic
63163c6459 IO1387 Add tax part override for non-R&R lines & Package Updates 2021-09-30 17:48:31 -07:00
Patrick Fic
2712ee5c0b IO-1416 Add missing LAA to resp center setup. 2021-09-30 09:44:15 -07:00
Patrick Fic
2e28a4a790 Merged in release/2021-10-01 (pull request #231)
Release/2021 10 01
2021-09-30 01:50:34 +00:00
Patrick Fic
4179943df6 Merged in release/2021-10-01 (pull request #230)
Release/2021 10 01
2021-09-29 22:55:42 +00:00
Patrick Fic
d38dd67c2f Merged in release/2021-10-01 (pull request #229)
release/2021-10-01

Approved-by: Patrick Fic
2021-09-29 15:50:34 +00:00
Patrick Fic
0eacf9a840 Merged in release/2021-10-01 (pull request #228)
release/2021-10-01

Approved-by: Patrick Fic
2021-09-28 20:19:33 +00:00
Patrick Fic
a7c9dde5e3 Merged in release/2021-10-01 (pull request #227)
release/2021-10-01

Approved-by: Patrick Fic
2021-09-27 23:52:21 +00:00
Patrick Fic
891aa649e8 Merged in release/2021-09-24 (pull request #226)
release/2021-09-24
2021-09-24 17:44:53 +00:00
Patrick Fic
a8a0167123 Merged in release/2021-09-24 (pull request #224)
Release/2021 09 24
2021-09-24 17:20:52 +00:00
Patrick Fic
4b55719f86 Merged in hotfix-2021-09-14 (pull request #214)
hotfix-2021-09-14

Approved-by: Patrick Fic
2021-09-14 16:52:50 +00:00
Patrick Fic
5387ff207c Merged in release/2021-09-10 (pull request #212)
Release/2021 09 10
2021-09-13 19:02:26 +00:00
Patrick Fic
e3804b103b Merged in release/2021-09-10 (pull request #208)
Release/2021 09 10
2021-09-10 22:08:15 +00:00
164 changed files with 34346 additions and 2011 deletions

View File

@@ -4283,6 +4283,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>md_ded_notes</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<folder_node>
<name>md_hour_split</name>
<children>
@@ -6134,6 +6155,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>laa</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>lab</name>
<definition_loaded>false</definition_loaded>
@@ -7099,6 +7141,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>color</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>default_arrived</name>
<definition_loaded>false</definition_loaded>
@@ -7435,6 +7498,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>production_colors</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>production_statuses</name>
<definition_loaded>false</definition_loaded>
@@ -7998,6 +8082,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>estimators</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>insurancecos</name>
<definition_loaded>false</definition_loaded>
@@ -16857,6 +16962,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>prt_dsmk_m</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>prt_dsmk_p</name>
<definition_loaded>false</definition_loaded>
@@ -16899,6 +17025,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>tax_part</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>total</name>
<definition_loaded>false</definition_loaded>
@@ -17386,6 +17533,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>changestimator</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>convert</name>
<definition_loaded>false</definition_loaded>
@@ -19548,6 +19716,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>date_last_contacted</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>date_open</name>
<definition_loaded>false</definition_loaded>
@@ -19611,6 +19800,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ded_note</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>ded_status</name>
<definition_loaded>false</definition_loaded>
@@ -21075,6 +21285,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>loss_of_use</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>ma2s</name>
<definition_loaded>false</definition_loaded>
@@ -21348,6 +21579,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_ph2</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>paa</name>
<definition_loaded>false</definition_loaded>
@@ -22282,6 +22534,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>referral_source_extra</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>referral_source_other</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>referralsource</name>
<definition_loaded>false</definition_loaded>
@@ -25834,6 +26128,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>savebeforeconversion</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>scheduledinchange</name>
<definition_loaded>false</definition_loaded>
@@ -29289,6 +29604,32 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>inserting</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>
</children>
</folder_node>
<folder_node>
<name>fields</name>
<children>
@@ -29423,6 +29764,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>notetoadd</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>
</children>
</folder_node>
<folder_node>
@@ -29826,6 +30188,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_ph2</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>ownr_st</name>
<definition_loaded>false</definition_loaded>
@@ -30354,6 +30737,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>comments</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>cost</name>
<definition_loaded>false</definition_loaded>
@@ -32869,6 +33273,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>iou_form</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>job_costing_ro</name>
<definition_loaded>false</definition_loaded>
@@ -32932,6 +33357,69 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>mpi_animal_checklist</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>mpi_eglass_auth</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>mpi_final_acct_sheet</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>paint_grid</name>
<definition_loaded>false</definition_loaded>
@@ -36590,6 +37078,37 @@
</folder_node>
</children>
</folder_node>
<folder_node>
<name>schedule</name>
<children>
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>manualevent</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>
</children>
</folder_node>
</children>
</folder_node>
<folder_node>
<name>scoreboard</name>
<children>

View File

@@ -4,37 +4,38 @@
"private": true,
"proxy": "http://localhost:5000",
"dependencies": {
"@apollo/client": "^3.4.14",
"@craco/craco": "^6.3.0",
"@apollo/client": "^3.4.16",
"@craco/craco": "^6.4.0",
"@fingerprintjs/fingerprintjs": "^3.3.0",
"@lourenci/react-kanban": "^2.1.0",
"@openreplay/tracker": "^3.4.0",
"@openreplay/tracker-assist": "^3.4.0",
"@openreplay/tracker": "^3.4.4",
"@openreplay/tracker-assist": "^3.4.4",
"@openreplay/tracker-graphql": "^3.0.0",
"@openreplay/tracker-redux": "^3.0.0",
"@sentry/react": "^6.13.2",
"@sentry/tracing": "^6.13.2",
"@stripe/react-stripe-js": "^1.5.0",
"@stripe/stripe-js": "^1.18.0",
"@tanem/react-nprogress": "^3.0.79",
"@sentry/react": "^6.13.3",
"@sentry/tracing": "^6.13.3",
"@splitsoftware/splitio-react": "^1.3.0",
"@stripe/react-stripe-js": "^1.6.0",
"@stripe/stripe-js": "^1.20.2",
"@tanem/react-nprogress": "^3.0.81",
"antd": "^4.16.13",
"apollo-link-logger": "^2.0.0",
"axios": "^0.21.4",
"axios": "^0.23.0",
"craco-less": "^1.20.0",
"dinero.js": "^1.9.0",
"dinero.js": "^1.9.1",
"dotenv": "^10.0.0",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.1.0",
"graphql": "^15.6.0",
"i18next": "^21.1.1",
"firebase": "^9.1.3",
"graphql": "^15.6.1",
"i18next": "^21.3.3",
"i18next-browser-languagedetector": "^6.1.2",
"jsoneditor": "^9.5.6",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.34",
"logrocket": "^2.0.0",
"markerjs2": "^2.12.0",
"libphonenumber-js": "^1.9.38",
"logrocket": "^2.1.1",
"markerjs2": "^2.15.0",
"moment-business-days": "^1.2.0",
"phone": "^3.1.8",
"preval.macro": "^5.0.0",
@@ -43,7 +44,7 @@
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"react": "^17.0.1",
"react-big-calendar": "^0.35.0",
"react-big-calendar": "^0.38.0",
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.1",
@@ -51,7 +52,7 @@
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.3.0",
"react-i18next": "^11.12.0",
"react-icons": "^4.2.0",
"react-icons": "^4.3.1",
"react-number-format": "^4.7.3",
"react-redux": "^7.2.5",
"react-resizable": "^3.0.4",
@@ -59,17 +60,17 @@
"react-scripts": "^4.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.1.4",
"recharts": "^2.1.5",
"redux": "^4.1.1",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.0.0",
"sass": "^1.42.1",
"socket.io-client": "^4.2.0",
"styled-components": "^5.3.1",
"sass": "^1.43.3",
"socket.io-client": "^4.3.2",
"styled-components": "^5.3.3",
"subscriptions-transport-ws": "^0.9.18",
"web-vitals": "^2.1.0",
"web-vitals": "^2.1.2",
"workbox-background-sync": "^6.3.0",
"workbox-broadcast-update": "^6.3.0",
"workbox-cacheable-response": "^6.3.0",
@@ -114,7 +115,7 @@
]
},
"devDependencies": {
"@sentry/webpack-plugin": "^1.17.1",
"@sentry/webpack-plugin": "^1.18.3",
"patch-package": "^6.4.7",
"redux-logger": "^3.0.6",
"source-map-explorer": "^2.5.2"

View File

@@ -1,4 +1,8 @@
import { ApolloProvider } from "@apollo/client";
//import trackerRedux from "@openreplay/tracker-redux";
import Tracker from "@openreplay/tracker";
import trackerGraphQL from "@openreplay/tracker-graphql";
import { SplitFactory, SplitSdk } from "@splitsoftware/splitio-react";
import { ConfigProvider } from "antd";
import enLocale from "antd/es/locale/en_US";
import LogRocket from "logrocket";
@@ -6,13 +10,11 @@ import moment from "moment";
import React from "react";
import { useTranslation } from "react-i18next";
import GlobalLoadingBar from "../components/global-loading-bar/global-loading-bar.component";
import client from "../utils/GraphQLClient";
import App from "./App";
import trackerGraphQL from "@openreplay/tracker-graphql";
//import trackerRedux from "@openreplay/tracker-redux";
import Tracker from "@openreplay/tracker";
//import trackerAssist from "@openreplay/tracker-assist";
import { getCurrentUser } from "../firebase/firebase.utils";
import client from "../utils/GraphQLClient";
import App from "./App";
moment.locale("en-US");
export const tracker = new Tracker({
@@ -24,8 +26,8 @@ export const tracker = new Tracker({
// beaconSize: 10485760,
onStart: async ({ sessionID }) => {
const user = await getCurrentUser();
tracker.setUserID(user.email);
console.log("ORS SESSION ", sessionID, user.email);
if (user) tracker.setUserID(user.email);
console.log("ORS SESSION ", sessionID, user && user.email);
},
});
@@ -36,6 +38,13 @@ export const recordGraphQL = tracker.use(trackerGraphQL());
tracker.start();
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
export const factory = SplitSdk({
core: {
authorizationKey: process.env.REACT_APP_SPLIT_API,
key: "anon",
},
});
export default function AppContainer() {
const { t } = useTranslation();
@@ -53,7 +62,9 @@ export default function AppContainer() {
}}
>
<GlobalLoadingBar />
<App />
<SplitFactory factory={factory}>
<App />
</SplitFactory>
</ConfigProvider>
</ApolloProvider>
);

View File

@@ -124,3 +124,13 @@
z-index: 2 !important;
}
}
.react-kanban-column {
background-color: #ddd !important;
}
.production-list-table {
td.ant-table-column-sort {
background: unset;
}
}

View File

@@ -8,8 +8,26 @@ import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
export default function AccountingPayablesTableComponent({
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingPayablesTableComponent);
export function AccountingPayablesTableComponent({
bodyshop,
loading,
payments,
}) {
@@ -163,6 +181,9 @@ export default function AccountingPayablesTableComponent({
loadingCallback={setTransInProgress}
completedCallback={setSelectedPayments}
/>
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />
)}
<Input
value={state.search}
onChange={handleSearch}

View File

@@ -199,7 +199,7 @@ export function AccountingReceivablesTableComponent({
<Card
extra={
<Space wrap>
{!bodyshop.cdk_dealerid && (
{!bodyshop.cdk_dealerid && !bodyshop.pbs_serialnumber && (
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}

View File

@@ -216,7 +216,7 @@ function BillEnterModalContainer({
if (enterAgain) {
form.resetFields();
form.setFieldsValue(formValues);
form.setFieldsValue({ ...form.getFieldsValue(), billlines: [] });
} else {
toggleModalVisible();
}

View File

@@ -156,7 +156,6 @@ export function BillEnterModalLinesComponent({
setFieldsValue({
billlines: getFieldsValue("billlines").billlines.map(
(item, idx) => {
console.log("Checking", index, idx);
if (idx === index) {
console.log(
"Found and setting.",
@@ -502,9 +501,9 @@ const EditableCell = ({
labelCol={{ span: 0 }}
{...(formItemProps && formItemProps(record))}
>
{(formInput && formInput(record, record.key)) || children}
{(formInput && formInput(record, record.name)) || children}
</Form.Item>
{additional && additional(record, record.key)}
{additional && additional(record, record.name)}
</Space>
</td>
);
@@ -516,7 +515,7 @@ const EditableCell = ({
name={dataIndex}
{...(formItemProps && formItemProps(record))}
>
{(formInput && formInput(record, record.key)) || children}
{(formInput && formInput(record, record.name)) || children}
</Form.Item>
</td>
);

View File

@@ -12,7 +12,18 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
disabled={disabled}
ref={ref}
showSearch
optionFilterProp="line_desc"
// optionFilterProp="line_desc"
filterOption={(inputValue, option) => {
console.log(inputValue);
return (
(option.line_desc &&
option.line_desc
.toLowerCase()
.includes(inputValue.toLowerCase())) ||
(option.oem_partno &&
option.oem_partno.toLowerCase().includes(inputValue.toLowerCase()))
);
}}
notFoundContent={"Removed."}
{...restProps}
>
@@ -29,6 +40,7 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
part_type={item.part_type}
line_desc={item.line_desc}
part_qty={item.part_qty}
oem_partno={item.oem_partno}
style={{
...(item.removed ? { textDecoration: "line-through" } : {}),
}}

View File

@@ -53,6 +53,7 @@ export function ContractConvertToRo({
const billingLines = [];
if (contractLength > 0)
billingLines.push({
manual_line:true,
unq_seq: 1,
line_no: 1,
line_ref: 1,
@@ -70,6 +71,7 @@ export function ContractConvertToRo({
contract.kmend - contract.kmstart - contract.dailyfreekm * contractLength;
if (mileageDiff > 0) {
billingLines.push({
manual_line:true,
unq_seq: 2,
line_no: 2,
line_ref: 2,
@@ -86,6 +88,7 @@ export function ContractConvertToRo({
if (values.refuelqty > 0) {
billingLines.push({
manual_line:true,
unq_seq: 3,
line_no: 3,
line_ref: 3,
@@ -101,6 +104,7 @@ export function ContractConvertToRo({
}
if (values.applyCleanupCharge) {
billingLines.push({
manual_line:true,
unq_seq: 4,
line_no: 4,
line_ref: 4,
@@ -117,6 +121,7 @@ export function ContractConvertToRo({
if (contract.damagewaiver) {
//Add for cleanup fee.
billingLines.push({
manual_line:true,
unq_seq: 5,
line_no: 5,
line_ref: 5,

View File

@@ -259,7 +259,6 @@ export function DmsPostForm({ bodyshop, socket, job }) {
))}
<Form.Item>
<Button
type="dashed"
disabled={!(fields.length < 3)}
onClick={() => {
if (fields.length < 3) add();

View File

@@ -51,7 +51,9 @@ export function EmailOverlayContainer({
const defaultEmailFrom = {
from: {
name: `${currentUser.displayName} @ ${bodyshop.shopname}`,
name: currentUser.displayName
? `${currentUser.displayName} @ ${bodyshop.shopname}`
: bodyshop.shopname,
address: EmailSettings.fromAddress,
},
ReplyTo: {

View File

@@ -21,7 +21,7 @@ export const PhoneItemFormatterValidation = (getFieldValue, name) => ({
return Promise.resolve();
} else {
const p = parsePhoneNumber(value, "CA");
if (p.isValid()) {
if (p && p.isValid()) {
return Promise.resolve();
} else {
return Promise.reject(i18n.t("general.validation.invalidphone"));

View File

@@ -62,13 +62,16 @@ export default function GlobalSearch() {
}`,
label: (
<Link to={`/manage/owners/${owner.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<Space size="small" split={<Divider type="vertical" />} wrap>
<span>{`${owner.ownr_fn || ""} ${owner.ownr_ln || ""} ${
owner.ownr_co_nm || ""
}`}</span>
<PhoneNumberFormatter>
{owner.ownr_ph1}
</PhoneNumberFormatter>
<PhoneNumberFormatter>
{owner.ownr_ph2}
</PhoneNumberFormatter>
</Space>
</Link>
),

View File

@@ -106,6 +106,12 @@ export function ScheduleEventComponent({
jobid={event.job.id}
/>
</DataLabel>
<DataLabel label={t("jobs.fields.ownr_ph2")}>
<ChatOpenButton
phone={event.job && event.job.ownr_ph2}
jobid={event.job.id}
/>
</DataLabel>
<DataLabel label={t("jobs.fields.alt_transport")}>
{(event.job && event.job.alt_transport) || ""}
<ScheduleAtChange job={event && event.job} />
@@ -203,6 +209,9 @@ export function ScheduleEventComponent({
jobId: event.job.id,
job: event.job,
previousEvent: event.id,
color: event.color,
alt_transport: event.job && event.job.alt_transport,
note: event.note,
},
});
}}

View File

@@ -167,7 +167,11 @@ export function JobChecklistForm({
title={t("checklist.labels.checklist")}
extra={
!readOnly && (
<Button loading={loading} onClick={() => form.submit()}>
<Button
loading={loading}
type="primary"
onClick={() => form.submit()}
>
{t("general.actions.submit")}
</Button>
)
@@ -182,12 +186,14 @@ export function JobChecklistForm({
allow_text_message: job.owner && job.owner.allow_text_message,
scheduled_completion:
(job && job.scheduled_completion) ||
moment().businessAdd(
(job.labhrs.aggregate.sum.mod_lb_hrs +
job.larhrs.aggregate.sum.mod_lb_hrs) /
bodyshop.target_touchtime,
"days"
),
(job.labbrs && job.larhrs
? moment().businessAdd(
(job.labhrs.aggregate.sum.mod_lb_hrs +
job.larhrs.aggregate.sum.mod_lb_hrs) /
bodyshop.target_touchtime,
"days"
)
: null),
scheduled_delivery: job && job.scheduled_delivery,
}),
...(type === "deliver" && {

View File

@@ -112,60 +112,46 @@ export function JobDetailCards({ setPrintCenterContext }) {
<Divider type="horizontal" />
<Row gutter={[16, 16]}>
<Col {...span}>
<Card.Grid style={{ width: "100%", height: "100%" }}>
<JobDetailCardsInsuranceComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Card.Grid>
<JobDetailCardsInsuranceComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<Card.Grid style={{ width: "100%", height: "100%" }}>
<JobDetailCardsTotalsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Card.Grid>
<JobDetailCardsTotalsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<Card.Grid style={{ width: "100%", height: "100%" }}>
<JobDetailCardsDatesComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Card.Grid>
<JobDetailCardsDatesComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<Card.Grid style={{ width: "100%", height: "100%" }}>
<JobDetailCardsPartsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Card.Grid>
<JobDetailCardsPartsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<Card.Grid style={{ width: "100%", height: "100%" }}>
<JobDetailCardsNotesComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Card.Grid>
<JobDetailCardsNotesComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<Card.Grid style={{ width: "100%", height: "100%" }}>
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Card.Grid>
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<Card.Grid style={{ width: "100%", height: "100%" }}>
<JobDetailCardsDamageComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Card.Grid>
<JobDetailCardsDamageComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
</Row>
</Card>

View File

@@ -1,7 +1,7 @@
import { Timeline } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { DateFormatter } from "../../utils/DateFormatter";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsDatesComponent({ loading, data }) {
@@ -26,80 +26,86 @@ export default function JobDetailCardsDatesComponent({ loading, data }) {
) ? (
<div>{t("jobs.errors.nodates")}</div>
) : null}
{data.date_last_contacted ? (
<Timeline.Item>
<label>{t("jobs.fields.date_last_contacted")}: </label>
<DateTimeFormatter>{data.date_last_contacted}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.date_open ? (
<Timeline.Item>
<label>{t("jobs.fields.date_open")}: </label>
<DateFormatter>{data.date_open}</DateFormatter>
<DateTimeFormatter>{data.date_open}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.date_estimated ? (
<Timeline.Item>
<label>{t("jobs.fields.date_estimated")}: </label>
<DateFormatter>{data.date_estimated}</DateFormatter>
<DateTimeFormatter>{data.date_estimated}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.date_scheduled ? (
<Timeline.Item>
<label>{t("jobs.fields.date_scheduled")}: </label>
<DateFormatter>{data.date_scheduled}</DateFormatter>
<DateTimeFormatter>{data.date_scheduled}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.scheduled_in ? (
<Timeline.Item>
<label>{t("jobs.fields.scheduled_in")}: </label>
<DateFormatter>{data.scheduled_in}</DateFormatter>
<DateTimeFormatter>{data.scheduled_in}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.actual_in ? (
<Timeline.Item>
<label>{t("jobs.fields.actual_in")}: </label>
<DateFormatter>{data.actual_in}</DateFormatter>
<DateTimeFormatter>{data.actual_in}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.scheduled_completion ? (
<Timeline.Item>
<label>{t("jobs.fields.scheduled_completion")}: </label>
<DateFormatter>{data.scheduled_completion}</DateFormatter>
<DateTimeFormatter>{data.scheduled_completion}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.actual_completion ? (
<Timeline.Item>
<label>{t("jobs.fields.actual_completion")}: </label>
<DateFormatter>{data.actual_completion}</DateFormatter>
<DateTimeFormatter>{data.actual_completion}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.scheduled_delivery ? (
<Timeline.Item>
<label>{t("jobs.fields.scheduled_delivery")}: </label>
<DateFormatter>{data.scheduled_delivery}</DateFormatter>
<DateTimeFormatter>{data.scheduled_delivery}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.actual_delivery ? (
<Timeline.Item>
<label>{t("jobs.fields.actual_delivery")}: </label>
<DateFormatter>{data.actual_delivery}</DateFormatter>
<DateTimeFormatter>{data.actual_delivery}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.date_invoiced ? (
<Timeline.Item>
<label>{t("jobs.fields.date_invoiced")}: </label>
<DateFormatter>{data.date_invoiced}</DateFormatter>
<DateTimeFormatter>{data.date_invoiced}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.date_exported ? (
<Timeline.Item>
<label>{t("jobs.fields.date_exported")}: </label>
<DateFormatter>{data.date_exported}</DateFormatter>
<DateTimeFormatter>{data.date_exported}</DateTimeFormatter>
</Timeline.Item>
) : null}
</Timeline>

View File

@@ -1,8 +1,8 @@
import { Carousel } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { GenerateThumbUrl } from "../jobs-documents-gallery/job-documents.utility";
import CardTemplate from "./job-detail-cards.template.component";
import { DetermineFileType } from "../documents-upload/documents-upload.utility";
export default function JobDetailCardsDocumentsComponent({ loading, data }) {
const { t } = useTranslation();
@@ -22,15 +22,7 @@ export default function JobDetailCardsDocumentsComponent({ loading, data }) {
{data.documents.length > 0 ? (
<Carousel autoplay>
{data.documents.map((item) => (
<img
key={item.id}
src={`${
process.env.REACT_APP_CLOUDINARY_ENDPOINT
}/${DetermineFileType(item.type)}/upload/${
process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS
}/${item.key}`}
alt={item.name}
/>
<img key={item.id} src={GenerateThumbUrl(item)} alt={item.name} />
))}
</Carousel>
) : (

View File

@@ -12,11 +12,13 @@ export default function JobDetailCardTemplate({
if (extraLink) extra = { extra: <Link to={extraLink}>More</Link> };
return (
<Card
size='small'
className='job-card'
size="small"
className="job-card"
title={title}
loading={loading}
{...extra}>
style={{ height: "100%" }}
{...extra}
>
{otherProps.children}
</Card>
);

View File

@@ -36,6 +36,7 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
// import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container";
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import _ from "lodash";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -161,7 +162,11 @@ export function JobLinesComponent({
ellipsis: true,
render: (text, record) => (
<>
<CurrencyFormatter>{record.act_price}</CurrencyFormatter>
<CurrencyFormatter>
{record.db_ref === "900510" || record.db_ref === "900511"
? record.prt_dsmk_m
: record.act_price}
</CurrencyFormatter>
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
<span
style={{ marginLeft: ".2rem" }}
@@ -295,18 +300,17 @@ export function JobLinesComponent({
onClick={async () => {
await deleteJobLine({
variables: { joblineId: record.id },
// update(cache) {
// cache.modify({
// id: cache.identify(job),
// fields: {
// joblines(existingJobLines, { readField }) {
// return existingJobLines.filter(
// (jlRef) => record.id !== readField("id", jlRef)
// );
// },
// },
// });
// },
update(cache) {
cache.modify({
fields: {
joblines(existingJobLines, { readField }) {
return existingJobLines.filter(
(jlRef) => record.id !== readField("id", jlRef)
);
},
},
});
},
});
await axios.post("/job/totalsssu", {
id: job.id,
@@ -334,10 +338,12 @@ export function JobLinesComponent({
const markedTypes = [e.key];
if (e.key === "PAN") markedTypes.push("PAP");
if (e.key === "PAS") markedTypes.push("PASL");
setSelectedLines([
...selectedLines,
...jobLines.filter((item) => markedTypes.includes(item.part_type)),
]);
setSelectedLines(
_.uniq([
...selectedLines,
...jobLines.filter((item) => markedTypes.includes(item.part_type)),
])
);
}
};

View File

@@ -1,4 +1,4 @@
import { Form, Input, InputNumber, Modal, Select } from "antd";
import { Form, Input, InputNumber, Modal, Select, Switch } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import InputCurrency from "../form-items-formatted/currency-form-item.component";
@@ -184,9 +184,9 @@ export default function JobLinesUpsertModalComponent({
>
<InputNumber precision={0} min={0} />
</Form.Item>
<Form.Item label={t("joblines.fields.db_price")} name="db_price">
{/* <Form.Item label={t("joblines.fields.db_price")} name="db_price">
<InputCurrency precision={2} min={0} />
</Form.Item>
</Form.Item> */}
<Form.Item
label={t("joblines.fields.act_price")}
name="act_price"
@@ -222,6 +222,14 @@ export default function JobLinesUpsertModalComponent({
>
<InputNumber precision={0} min={0} max={100} />
</Form.Item>
<Form.Item
label={t("joblines.fields.tax_part")}
name="tax_part"
valuePropName="checked"
initialValue={true}
>
<Switch />
</Form.Item>
</LayoutFormRow>
</Form>
</Modal>

View File

@@ -44,6 +44,7 @@ function JobLinesUpsertModalContainer({
},
],
},
refetchQueries: ["GET_LINE_TICKET_BY_PK"],
});
if (!r.errors) {
await Axios.post("/job/totalsssu", {
@@ -69,6 +70,7 @@ function JobLinesUpsertModalContainer({
lineId: jobLineEditModal.context.id,
line: values,
},
refetchQueries: ["GET_LINE_TICKET_BY_PK"],
});
if (!r.errors) {
notification["success"]({

View File

@@ -108,6 +108,9 @@ export default function JobReconciliationBillsTable({
rowSelection={{
onChange: handleOnRowClick,
selectedRowKeys: selectedLines,
getCheckboxProps: (record) => {
return { disabled: record.deductedfromlbr };
},
}}
/>
</div>

View File

@@ -11,7 +11,7 @@ export const reconcileByAssocLine = (
const [selectedJobLines, setSelectedJobLines] = jobLineState;
const allJoblinesFromBills = billLines
.filter((bl) => bl.joblineid && !(bl.jobline && bl.jobline.removed))
.filter((bl) => bl.joblineid && bl.jobline && !bl.jobline.removed)
.map((bl) => bl.joblineid);
const duplicatedJobLinesbyInvoiceId = _.filter(

View File

@@ -74,7 +74,12 @@ export default function JobsAdminDatesChange({ job }) {
<Form.Item label={t("jobs.fields.actual_in")} name="actual_in">
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.date_last_contacted")}
name="date_last_contacted"
>
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.scheduled_completion")}
name="scheduled_completion"

View File

@@ -100,7 +100,7 @@ export function JobsAvailableContainer({
}
//IO-539 Check for Parts Rate on PAL for SGI use case.
await CheckTaxRates(estData.est_data, bodyshop);
console.log(estData);
const newTotals = (
await Axios.post("/job/totals", {
job: {
@@ -395,10 +395,6 @@ function replaceEmpty(someObj, replaceValue = null) {
}
async function CheckTaxRates(estData, bodyshop) {
console.log(
"🚀 ~ file: jobs-available-table.container.jsx ~ line 398 ~ estData",
estData
);
//LKQ Check
if (
!estData.parts_tax_rates?.PAL ||
@@ -491,4 +487,23 @@ async function CheckTaxRates(estData, bodyshop) {
estData.parts_tax_rates.PAR.prt_tax_in = true;
}
}
//IO-1387 If a sublet line is NOT R&R, use the labor tax. If it is, use the sublet tax rate.
//Currently limited to SK shops only.
//if (bodyshop.region_config === "CA_SK") {
estData.joblines.data.forEach((jl, index) => {
if (
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
jl.lbr_op !== "OP11"
) {
estData.joblines.data[index].tax_part = jl.lbr_tax;
}
//Set markup lines and tax lines as taxable.
//900510 is a mark up. 900510 is a discount.
if (jl.db_ref === "900510") {
estData.joblines.data[index].tax_part = true;
}
});
//}
}

View File

@@ -15,6 +15,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { useHistory } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
@@ -35,7 +36,7 @@ export function JobsCloseExportButton({
const handleQbxml = async () => {
//Check if it's a CDK setup.
if (bodyshop.cdk_dealerid) {
if (bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) {
history.push(`/manage/dms?jobId=${jobId}`);
return;
}
@@ -45,10 +46,13 @@ export function JobsCloseExportButton({
//Check if it's a QBO Setup.
let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/receivables`, {
withCredentials: true,
jobIds: [jobId],
});
PartnerResponse = await axios.post(
`/qbo/receivables`,
{
jobIds: [jobId],
},
);
} else {
//Default is QBD

View File

@@ -31,6 +31,7 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
<th>{t("joblines.fields.line_desc")}</th>
<th>{t("joblines.fields.part_type")}</th>
<th>{t("joblines.fields.act_price")}</th>
<th>{t("joblines.fields.prt_dsmk_m")}</th>
<th>{t("joblines.fields.op_code_desc")}</th>
<th>{t("joblines.fields.mod_lbr_ty")}</th>
<th>{t("joblines.fields.mod_lb_hrs")}</th>
@@ -70,6 +71,16 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
<ReadOnlyFormItem type="currency" />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
// label={t("joblines.fields.prt_dsmk_m")}
key={`${index}prt_dsmk_m`}
name={[field.name, "prt_dsmk_m"]}
>
<ReadOnlyFormItem type="currency" />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
@@ -108,7 +119,9 @@ export function JobsCloseLines({ bodyshop, job, jobRO }) {
labelCol={{ span: 0 }}
rules={[
{
required: !!job.joblines[index].act_price,
required:
!!job.joblines[index].act_price ||
!!job.joblines[index].prt_dsmk_m,
//message: t("general.validation.required"),
},
]}

View File

@@ -2,6 +2,7 @@ import { useMutation } from "@apollo/client";
import {
Button,
Form,
Input,
notification,
Popover,
Select,
@@ -34,6 +35,7 @@ export function JobsConvertButton({
refetch,
jobRO,
insertAuditTrail,
parentFormIsFieldsTouched,
}) {
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
@@ -42,6 +44,10 @@ export function JobsConvertButton({
const [form] = Form.useForm();
const handleConvert = async (values) => {
if (parentFormIsFieldsTouched()) {
alert(t("jobs.labels.savebeforeconversion"));
return;
}
setLoading(true);
const res = await mutationConvertJob({
variables: { jobId: job.id, ...values },
@@ -112,24 +118,32 @@ export function JobsConvertButton({
</Form.Item>
)}
{bodyshop.enforce_referral && (
<Form.Item
name={"referral_source"}
label={t("jobs.fields.referralsource")}
rules={[
{
required: bodyshop.enforce_referral,
//message: t("general.validation.required"),
},
]}
>
<Select>
{bodyshop.md_referral_sources.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
<>
<Form.Item
name={"referral_source"}
label={t("jobs.fields.referralsource")}
rules={[
{
required: bodyshop.enforce_referral,
//message: t("general.validation.required"),
},
]}
>
<Select>
{bodyshop.md_referral_sources.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("jobs.fields.referral_source_extra")}
name="referral_source_extra"
>
<Input />
</Form.Item>
</>
)}
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}

View File

@@ -150,6 +150,9 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<Form.Item label={t("jobs.fields.loss_desc")} name="loss_desc">
<Input />
</Form.Item>
<Form.Item label={t("jobs.fields.loss_of_use")} name="loss_of_use">
<Input />
</Form.Item>
<Form.Item label={t("jobs.fields.ponumber")} name="po_number">
<Input />
</Form.Item>
@@ -181,6 +184,12 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
))}
</Select>
</Form.Item>
<Form.Item
label={t("jobs.fields.referral_source_extra")}
name="referral_source_extra"
>
<Input />
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>
<Collapse.Panel
@@ -191,7 +200,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<JobsMarkPstExempt form={form} />
<LayoutFormRow>
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item label={t("jobs.fields.ded_status")} name="ded_status">
<Select>

View File

@@ -129,6 +129,19 @@ export default function JobsCreateOwnerInfoNewComponent() {
>
<FormItemPhone disabled={!state.owner.new} />
</Form.Item>
<Form.Item
label={t("owners.fields.ownr_ph2")}
name={["owner", "data", "ownr_ph2"]}
rules={[
({ getFieldValue }) =>
PhoneItemFormatterValidation(
getFieldValue,
"owner.data.ownr_ph2"
),
]}
>
<FormItemPhone disabled={!state.owner.new} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow>
<Form.Item

View File

@@ -84,6 +84,18 @@ export default function JobsCreateOwnerInfoSearchComponent({
tableState.sortedInfo.columnKey === "ownr_ph1" &&
tableState.sortedInfo.order,
},
{
title: t("owners.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
render: (text, record) => (
<PhoneFormatter>{record.ownr_ph2}</PhoneFormatter>
),
sorter: (a, b) => alphaSort(a.ownr_ph2, b.ownr_ph2),
sortOrder:
tableState.sortedInfo.columnKey === "ownr_ph2" &&
tableState.sortedInfo.order,
},
];
const handleTableChange = (pagination, filters, sorter) => {

View File

@@ -0,0 +1,46 @@
import { DownOutlined } from "@ant-design/icons";
import { Dropdown, Menu } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
export function JobsDetailChangeEstimator({ disabled, form, bodyshop }) {
const { t } = useTranslation();
const handleClick = ({ item, key, keyPath }) => {
const est = item.props.value;
form.setFieldsValue(est);
};
const menu = (
<div>
<Menu onClick={handleClick}>
{bodyshop.md_estimators.map((est, idx) => (
<Menu.Item value={est} key={idx}>
{`${est.est_ct_fn} ${est.est_ct_ln}`}
</Menu.Item>
))}
</Menu>
</div>
);
return (
<Dropdown overlay={menu} disabled={disabled}>
<a
className="ant-dropdown-link"
href=" #"
onClick={(e) => e.preventDefault()}
>
{t("jobs.actions.changestimator")} <DownOutlined />
</a>
</Dropdown>
);
}
export default connect(mapStateToProps, null)(JobsDetailChangeEstimator);

View File

@@ -69,6 +69,12 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
</Tooltip>
</FormRow>
<FormRow header={t("jobs.forms.repairdates")}>
<Form.Item
label={t("jobs.fields.date_last_contacted")}
name="date_last_contacted"
>
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.scheduled_completion")}
name="scheduled_completion"
@@ -93,7 +99,6 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.scheduled_delivery")}
name="scheduled_delivery"

View File

@@ -1,4 +1,13 @@
import { Col, Form, Input, InputNumber, Row, Select, Switch } from "antd";
import {
Col,
Divider,
Form,
Input,
InputNumber,
Row,
Select,
Switch,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -12,6 +21,7 @@ import FormItemPhone, {
PhoneItemFormatterValidation,
} from "../form-items-formatted/phone-form-item.component";
import Car from "../job-damage-visual/job-damage-visual.component";
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
import FormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
@@ -44,7 +54,14 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.ded_amt")} name="ded_amt">
<CurrencyInput disabled={jobRO} />
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<Form.Item label={t("jobs.fields.ded_note")} name="ded_note">
<Select disabled={jobRO}>
{bodyshop.md_ded_notes.map((n, index) => (
<Select.Option key={index}>{n}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item label={t("jobs.fields.policy_no")} name="policy_no">
<Input disabled={jobRO} />
@@ -114,6 +131,12 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
))}
</Select>
</Form.Item>
<Form.Item
label={t("jobs.fields.referral_source_extra")}
name="referral_source_extra"
>
<Input />
</Form.Item>
<Form.Item label={t("jobs.fields.alt_transport")} name="alt_transport">
<Select disabled={jobRO} allowClear>
{bodyshop.appt_alt_transport.map((s) => (
@@ -133,6 +156,9 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
<Form.Item label={t("jobs.fields.loss_date")} name="loss_date">
<FormDatePicker disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.loss_of_use")} name="loss_of_use">
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.kmin")} name="kmin">
<InputNumber precision={0} min={0} disabled={jobRO} />
</Form.Item>
@@ -185,8 +211,16 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
)}
</Col>
</Row>
<Divider
orientation="left"
type="horizontal"
style={{ marginTop: ".8rem", float: "right" }}
>
{t("jobs.forms.appraiserinfo")}
</Divider>
<FormRow header={t("jobs.forms.appraiserinfo")}>
<JobsDetailChangeEstimator form={form} disabled={jobRO} />
<FormRow noDivider>
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
<Input disabled={jobRO} />
</Form.Item>

View File

@@ -101,6 +101,7 @@ export function JobsDetailHeaderActions({
context: {
jobId: job.id,
job: job,
alt_transport: job.alt_transport,
},
});
}}

View File

@@ -147,7 +147,7 @@ export function JobsDetailHeaderCsi({
replyTo: bodyshop.email,
},
template: {
name: TemplateList("job").csi_invitation.key,
name: TemplateList("job_special").csi_invitation_action.key,
variables: {
id: job.csiinvites[0].id,
},

View File

@@ -21,47 +21,57 @@ export function JobsDetailHeaderActionexportCustomerData({
const handleExportCustData = async (e) => {
logImEXEvent("job_export_cust_data");
let QbXmlResponse;
try {
QbXmlResponse = await axios.post(
"/accounting/qbxml/receivables",
{ jobIds: [job.id], custDataOnly: true },
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
},
}
);
console.log("handle -> XML", QbXmlResponse);
} catch (error) {
console.log("Error getting QBXML from Server.", error);
notification["error"]({
message: t("jobs.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
}),
});
return;
}
let PartnerResponse;
try {
PartnerResponse = await axios.post(
"http://localhost:1337/qb/",
QbXmlResponse.data,
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
},
}
);
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({
message: t("jobs.errors.exporting-partner"),
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/receivables`, {
jobIds: [job.id],
custDataOnly: true,
});
} else {
//Default is QBD
return;
let QbXmlResponse;
try {
QbXmlResponse = await axios.post(
"/accounting/qbxml/receivables",
{ jobIds: [job.id], custDataOnly: true },
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
},
}
);
console.log("handle -> XML", QbXmlResponse);
} catch (error) {
console.log("Error getting QBXML from Server.", error);
notification["error"]({
message: t("jobs.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
}),
});
return;
}
//let PartnerResponse;
try {
PartnerResponse = await axios.post(
"http://localhost:1337/qb/",
QbXmlResponse.data,
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
},
}
);
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({
message: t("jobs.errors.exporting-partner"),
});
return;
}
}
//Check to see if any of them failed. If they didn't don't execute the update.
const failedTransactions = PartnerResponse.data.filter((r) => !r.success);

View File

@@ -16,6 +16,7 @@ import JobEmployeeAssignments from "../job-employee-assignments/job-employee-ass
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
import "./jobs-detail-header.styles.scss";
import JobsRelatedRos from "../jobs-related-ros/jobs-related-ros.component";
import { DateTimeFormatter } from "../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -70,6 +71,12 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
{job.production_vars && job.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" />
) : null}
{job.status === bodyshop.md_ro_statuses.default_scheduled &&
job.scheduled_in ? (
<Tag>
<DateTimeFormatter>{job.scheduled_in}</DateTimeFormatter>
</Tag>
) : null}
</Space>
</DataLabel>
<DataLabel label={t("jobs.fields.ins_co_nm_short")}>
@@ -141,6 +148,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<DataLabel key="2" label={t("jobs.fields.ownr_ph1")}>
<ChatOpenButton phone={job.ownr_ph1} jobid={job.id} />
</DataLabel>
<DataLabel key="22" label={t("jobs.fields.ownr_ph2")}>
<ChatOpenButton phone={job.ownr_ph2} jobid={job.id} />
</DataLabel>
<DataLabel key="3" label={t("owners.fields.address")}>
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
job.ownr_city || ""

View File

@@ -38,7 +38,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
label={t("jobs.fields.depreciation_taxes")}
name="depreciation_taxes"
>
<CurrencyInput disabled={jobRO} />
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<Tooltip title={t("jobs.labels.ca_gst_all_if_null")}>
<Form.Item
@@ -47,6 +47,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
>
<CurrencyInput
disabled={jobRO}
min={0}
max={
Math.round(
(job.job_totals &&
@@ -61,19 +62,19 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
label={t("jobs.fields.other_amount_payable")}
name="other_amount_payable"
>
<CurrencyInput disabled={jobRO} />
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.towing_payable")}
name="towing_payable"
>
<CurrencyInput disabled={jobRO} />
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.storage_payable")}
name="storage_payable"
>
<CurrencyInput disabled={jobRO} />
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.adjustment_bottom_line")}
@@ -83,7 +84,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
</Form.Item>
<Space align="end">
<Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt">
<CurrencyInput disabled={jobRO} />
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<CABCpvrtCalculator form={form} disabled={jobRO} />
</Space>
@@ -123,7 +124,6 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
{t("jobs.forms.laborrates")}
</Divider>
<Space>
<div></div>
<JobsDetailRatesChangeButton form={form} disabled={jobRO} />
<JobsMarkPstExempt form={form} />
</Space>
@@ -135,72 +135,72 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_laa")} name="rate_laa">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_lad")} name="rate_lad">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_lae")} name="rate_lae">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_lar")} name="rate_lar">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_las")} name="rate_las">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_laf")} name="rate_laf">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_lam")} name="rate_lam">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_lag")} name="rate_lag">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_la1")} name="rate_la1">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_la2")} name="rate_la2">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_la3")} name="rate_la3">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_la4")} name="rate_la4">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_lau")} name="rate_lau">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_mapa")} name="rate_mapa">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_mash")} name="rate_mash">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_mahw")} name="rate_mahw">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_ma2s")} name="rate_ma2s">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.rate_ma3s")} name="rate_ma3s">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
{
// <Form.Item label={t("jobs.fields.rate_mabl")} name="rate_mabl">
// <CurrencyInput disabled={jobRO} />
// <CurrencyInput min={0}disabled={jobRO} />
// </Form.Item>
// <Form.Item label={t("jobs.fields.rate_macs")} name="rate_macs">
// <CurrencyInput disabled={jobRO} />
// <CurrencyInput min={0}disabled={jobRO} />
// </Form.Item>
}
<Form.Item label={t("jobs.fields.rate_matd")} name="rate_matd">
<CurrencyInput disabled={jobRO} />
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
</FormRow>
<JobsDetailRatesParts form={form} />

View File

@@ -29,20 +29,6 @@ function JobsDocumentsComponent({
setIndex(index);
};
// useEffect(() => {
// console.log("Added event listening for reteching.");
// window.addEventListener("storage", (ev) => {
// if (ev.key === "refetch" && ev.newValue === true) {
// refetch && refetch();
// localStorage.setItem("refetch", false);
// }
// });
// return () => {
// window.removeEventListener("storage");
// };
// }, [refetch]);
useEffect(() => {
let documents = data.reduce(
(acc, value) => {

View File

@@ -38,7 +38,6 @@ export function JobsExportAllButton({
setLoading(true);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/receivables`, {
withCredentials: true,
jobIds: jobIds,
});
} else {
@@ -170,12 +169,7 @@ export function JobsExportAllButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={disabled}
type="dashed"
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}
</Button>
);

View File

@@ -69,6 +69,20 @@ export default function JobsFindModalComponent({
);
},
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
width: "12%",
ellipsis: true,
render: (text, record) => {
return record.ownr_ph2 ? (
<PhoneFormatter>{record.ownr_ph2}</PhoneFormatter>
) : (
t("general.labels.unknown")
);
},
},
{
title: t("jobs.fields.status"),
dataIndex: "status",

View File

@@ -73,6 +73,16 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
<StartChatButton phone={record.ownr_ph1} jobid={record.id} />
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
width: "12%",
ellipsis: true,
render: (text, record) => (
<StartChatButton phone={record.ownr_ph2} jobid={record.id} />
),
},
{
title: t("jobs.fields.status"),
dataIndex: "status",

View File

@@ -145,6 +145,16 @@ export function JobsList({ bodyshop }) {
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
),
},
{
title: t("jobs.fields.status"),

View File

@@ -81,6 +81,16 @@ export default function OwnerDetailFormComponent({ form, loading }) {
>
<FormItemPhone />
</Form.Item>
<Form.Item
label={t("owners.fields.ownr_ph2")}
name="ownr_ph2"
rules={[
({ getFieldValue }) =>
PhoneItemFormatterValidation(getFieldValue, "ownr_ph2"),
]}
>
<FormItemPhone />
</Form.Item>
<Form.Item
label={t("owners.fields.preferred_contact")}
name="preferred_contact"

View File

@@ -51,6 +51,14 @@ export default function OwnerFindModalComponent({
<PhoneFormatter>{record.ownr_ph1}</PhoneFormatter>
),
},
{
title: t("owners.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
render: (text, record) => (
<PhoneFormatter>{record.ownr_ph2}</PhoneFormatter>
),
},
];
const handleOnRowClick = (record) => {
@@ -66,7 +74,6 @@ export default function OwnerFindModalComponent({
return (
<div>
<Table
title={() => t("owners.labels.existing_owners")}
pagination={{ position: "bottom" }}
columns={columns}
rowKey="id"

View File

@@ -1,6 +1,6 @@
import { Modal } from "antd";
import React from "react";
import { useQuery } from "@apollo/client";
import { useLazyQuery } from "@apollo/client";
import { Input, Modal } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { QUERY_SEARCH_OWNER_BY_IDX } from "../../graphql/owners.queries";
import AlertComponent from "../alert/alert.component";
@@ -17,14 +17,27 @@ export default function OwnerFindModalContainer({
}) {
//use owner object to run query and find what possible owners there are.
const { t } = useTranslation();
const [searchText, setSearchText] = useState(null);
const ownersList = useQuery(QUERY_SEARCH_OWNER_BY_IDX, {
variables: {
search: owner ? `${owner.ownr_fn || ""} ${owner.ownr_ln || ""}` : null,
},
skip: !owner,
fetchPolicy: "network-only",
});
const [callSearchowners, ownersList] = useLazyQuery(
QUERY_SEARCH_OWNER_BY_IDX,
{
variables: {
search: searchText,
},
}
);
useEffect(() => {
if (modalProps.visible && owner) {
const s = `${owner.ownr_fn || ""} ${owner.ownr_ln || ""} ${
owner.ownr_co_nm || ""
}`;
setSearchText(s.trim());
callSearchowners({ variables: { search: s.trim() } });
}
}, [callSearchowners, modalProps.visible, owner]);
return (
<Modal
@@ -35,16 +48,25 @@ export default function OwnerFindModalContainer({
{loading ? <LoadingSpinner /> : null}
{error ? <AlertComponent message={error.message} type="error" /> : null}
{owner ? (
<OwnerFindModalComponent
selectedOwner={selectedOwner}
setSelectedOwner={setSelectedOwner}
ownersListLoading={ownersList.loading}
ownersList={
ownersList.data && ownersList.data.search_owners
? ownersList.data.search_owners
: null
}
/>
<>
<Input.Search
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
onSearch={(val) =>
callSearchowners({ variables: { search: val.trim() } })
}
/>
<OwnerFindModalComponent
selectedOwner={selectedOwner}
setSelectedOwner={setSelectedOwner}
ownersListLoading={ownersList.loading}
ownersList={
ownersList.data && ownersList.data.search_owners
? ownersList.data.search_owners
: null
}
/>
</>
) : null}
</Modal>
);

View File

@@ -16,6 +16,9 @@ export default function OwnerTagPopoverComponent({ job }) {
<Descriptions.Item key="2" label={t("jobs.fields.ownr_ph1")}>
<PhoneFormatter>{job.ownr_ph1 || ""}</PhoneFormatter>
</Descriptions.Item>
<Descriptions.Item key="22" label={t("jobs.fields.ownr_ph2")}>
<PhoneFormatter>{job.ownr_ph2 || ""}</PhoneFormatter>
</Descriptions.Item>
<Descriptions.Item key="3" label={t("owners.fields.address")}>
{`${job.ownr_addr1 || ""} ${job.ownr_addr2 || ""} ${
job.ownr_city || ""
@@ -36,6 +39,12 @@ export default function OwnerTagPopoverComponent({ job }) {
<Descriptions.Item key="2" label={t("jobs.fields.ownr_ph1")}>
<PhoneFormatter>{job.owner.ownr_ph1 || ""}</PhoneFormatter>
</Descriptions.Item>
<Descriptions.Item key="22" label={t("jobs.fields.ownr_ph2")}>
<PhoneFormatter>{job.owner.ownr_ph2 || ""}</PhoneFormatter>
</Descriptions.Item>
<Descriptions.Item key="2" label={t("jobs.fields.ownr_ph2")}>
<PhoneFormatter>{job.owner.ownr_ph2 || ""}</PhoneFormatter>
</Descriptions.Item>
<Descriptions.Item key="3" label={t("owners.fields.address")}>
{`${job.owner.ownr_addr1 || ""} ${job.owner.ownr_addr2 || ""} ${
job.owner.ownr_city || ""

View File

@@ -47,6 +47,14 @@ export default function OwnersListComponent({
return <PhoneFormatter>{record.ownr_ph1}</PhoneFormatter>;
},
},
{
title: t("owners.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
render: (text, record) => {
return <PhoneFormatter>{record.ownr_ph2}</PhoneFormatter>;
},
},
{
title: t("owners.fields.ownr_ea"),
dataIndex: "ownr_ea",

View File

@@ -27,6 +27,7 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants";
import DataLabel from "../data-label/data-label.component";
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
@@ -379,6 +380,9 @@ export function PartsOrderListTableComponent({
rowKey="id"
dataSource={record.parts_order_lines}
/>
<DataLabel label={t("parts_orders.fields.comments")}>
<div style={{ whiteSpace: "pre" }}>{record.comments}</div>
</DataLabel>
</div>
);
};

View File

@@ -73,7 +73,7 @@ export default function PartsOrderModalComponent({
<Form.Item required={false} key={field.key}>
<LayoutFormRow grow noDivider>
<Form.Item
span={8}
//span={8}
label={t("parts_orders.fields.line_desc")}
key={`${index}line_desc`}
name={[field.name, "line_desc"]}
@@ -132,19 +132,21 @@ export default function PartsOrderModalComponent({
</Form.Item>
)}
<div>
<DeleteFilled
style={{ margin: "1rem" }}
onClick={() => {
remove(field.name);
}}
/>
<Space wrap align="center">
<div>
<DeleteFilled
style={{ margin: "1rem" }}
onClick={() => {
remove(field.name);
}}
/>
</div>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
/>
</div>
</Space>
</LayoutFormRow>
</Form.Item>
))}
@@ -152,6 +154,9 @@ export default function PartsOrderModalComponent({
);
}}
</Form.List>
<Form.Item name="comments" label={t("parts_orders.fields.comments")}>
<Input.TextArea rows={3} />
</Form.Item>
<Radio.Group
defaultValue={sendType}
onChange={(e) => setSendType(e.target.value)}

View File

@@ -260,7 +260,7 @@ export function PartsOrderModalContainer({
onCancel={() => toggleModalVisible()}
onOk={() => form.submit()}
destroyOnClose
width="50%"
width="75%"
forceRender
>
{error ? <AlertComponent message={error.message} type="error" /> : null}

View File

@@ -40,8 +40,7 @@ export function PayableExportAll({
setLoading(true);
if (!!loadingCallback) loadingCallback(true);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/receivables`, {
withCredentials: true,
PartnerResponse = await axios.post(`/qbo/payables`, {
bills: billids,
});
} else {
@@ -169,12 +168,7 @@ export function PayableExportAll({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={disabled}
type="dashed"
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.exportselected")}
</Button>
);

View File

@@ -42,7 +42,6 @@ export function PayableExportButton({
let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payables`, {
withCredentials: true,
bills: [billId],
});
} else {
@@ -171,12 +170,7 @@ export function PayableExportButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={disabled}
type="dashed"
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}
</Button>
);

View File

@@ -8,6 +8,7 @@ import { createStructuredSelector } from "reselect";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
import {
selectBodyshop,
selectCurrentUser,
@@ -33,55 +34,63 @@ export function PaymentExportButton({
const handleQbxml = async () => {
logImEXEvent("accounting_payment_export");
setLoading(true);
if (!!loadingCallback) loadingCallback(true);
let QbXmlResponse;
try {
QbXmlResponse = await axios.post(
"/accounting/qbxml/payments",
{ payments: [paymentId] },
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
},
}
);
console.log("handle -> XML", QbXmlResponse);
} catch (error) {
console.log("Error getting QBXML from Server.", error);
notification["error"]({
message: t("payments.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
}),
});
if (loadingCallback) loadingCallback(false);
setLoading(false);
return;
}
//Check if it's a QBO Setup.
let PartnerResponse;
try {
PartnerResponse = await axios.post(
"http://localhost:1337/qb/",
//"http://609feaeae986.ngrok.io/qb/",
QbXmlResponse.data
);
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({
message: t("payments.errors.exporting-partner"),
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payments`, {
payments: [paymentId],
});
if (!!loadingCallback) loadingCallback(false);
setLoading(false);
return;
} else {
//Default is QBD
if (!!loadingCallback) loadingCallback(true);
let QbXmlResponse;
try {
QbXmlResponse = await axios.post(
"/accounting/qbxml/payments",
{ payments: [paymentId] },
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
},
}
);
console.log("handle -> XML", QbXmlResponse);
} catch (error) {
console.log("Error getting QBXML from Server.", error);
notification["error"]({
message: t("payments.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
}),
});
if (loadingCallback) loadingCallback(false);
setLoading(false);
return;
}
try {
PartnerResponse = await axios.post(
"http://localhost:1337/qb/",
QbXmlResponse.data
);
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({
message: t("payments.errors.exporting-partner"),
});
if (!!loadingCallback) loadingCallback(false);
setLoading(false);
return;
}
}
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
const failedTransactions = PartnerResponse.data.filter((r) => !r.success);
const successfulTransactions = PartnerResponse.data.filter(
(r) => r.success
);
if (failedTransactions.length > 0) {
//Uh oh. At least one was no good.
failedTransactions.map((ft) =>
@@ -123,7 +132,14 @@ export function PaymentExportButton({
const paymentUpdateResponse = await updatePayment({
variables: {
paymentIdList: [paymentId],
paymentIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "paymentid"
: "id"
]
),
payment: {
exportedat: new Date(),
},
@@ -155,12 +171,7 @@ export function PaymentExportButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={disabled}
type="dashed"
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}
</Button>
);

View File

@@ -8,6 +8,7 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_PAYMENTS } from "../../graphql/payments.queries";
import {
selectBodyshop,
selectCurrentUser,
@@ -33,42 +34,49 @@ export function PaymentsExportAllButton({
const handleQbxml = async () => {
setLoading(true);
if (!!loadingCallback) loadingCallback(true);
let QbXmlResponse;
try {
QbXmlResponse = await axios.post("/accounting/qbxml/payments", {
let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payments`, {
payments: paymentIds,
});
} catch (error) {
console.log("Error getting QBXML from Server.", error);
notification["error"]({
message: t("payments.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
}),
});
if (loadingCallback) loadingCallback(false);
setLoading(false);
return;
} else {
let QbXmlResponse;
try {
QbXmlResponse = await axios.post("/accounting/qbxml/payments", {
payments: paymentIds,
});
} catch (error) {
console.log("Error getting QBXML from Server.", error);
notification["error"]({
message: t("payments.errors.exporting", {
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
}),
});
if (loadingCallback) loadingCallback(false);
setLoading(false);
return;
}
try {
PartnerResponse = await axios.post(
"http://localhost:1337/qb/",
QbXmlResponse.data
);
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({
message: t("payments.errors.exporting-partner"),
});
if (!!loadingCallback) loadingCallback(false);
setLoading(false);
return;
}
}
let PartnerResponse;
try {
PartnerResponse = await axios.post(
"http://localhost:1337/qb/",
QbXmlResponse.data
);
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({
message: t("payments.errors.exporting-partner"),
});
if (!!loadingCallback) loadingCallback(false);
setLoading(false);
return;
}
const groupedData = _.groupBy(PartnerResponse.data, "id");
const groupedData = _.groupBy(
PartnerResponse.data,
bodyshop.accountingconfig.qbo ? "paymentid" : "id"
);
const proms = [];
Object.keys(groupedData).forEach((key) => {
proms.push(
@@ -144,12 +152,7 @@ export function PaymentsExportAllButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={disabled}
type="dashed"
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.exportselected")}
</Button>
);

View File

@@ -38,6 +38,7 @@ export function ProductionBoardFilters({
}}
/>
<EmployeeSearchSelectComponent
style={{ minWidth: "20rem" }}
options={bodyshop.employees.filter((e) => e.active)}
value={filter.employeeId}
placeholder={t("production.labels.employeesearch")}

View File

@@ -6,9 +6,11 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
technician: selectTechnician,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -21,6 +23,7 @@ export default connect(
export function ProductionColumnsComponent({
columnState,
technician,
bodyshop,
tableState,
}) {
const [columns, setColumns] = columnState;
@@ -29,9 +32,11 @@ export function ProductionColumnsComponent({
const handleAdd = (e) => {
setColumns([
...columns,
...dataSource({ technician, state: tableState }).filter(
(i) => i.key === e.key
),
...dataSource({
technician,
state: tableState,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).filter((i) => i.key === e.key),
]);
};
@@ -39,7 +44,11 @@ export function ProductionColumnsComponent({
const menu = (
<Menu onClick={handleAdd}>
{dataSource({ technician, state: tableState })
{dataSource({
technician,
state: tableState,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
})
.filter((i) => !columnKeys.includes(i.key))
.map((item) => (
<Menu.Item key={item.key}>{item.title}</Menu.Item>

View File

@@ -40,7 +40,7 @@ export default function ProductionListColumnBodyPriority({ record }) {
key="set"
title={t("production.actions.bodypriority-set")}
>
{new Array(9).fill().map((value, index) => (
{new Array(15).fill().map((value, index) => (
<Menu.Item key={index + 1}>{index + 1}</Menu.Item>
))}
</Menu.SubMenu>

View File

@@ -3,7 +3,7 @@ import React from "react";
import { Link } from "react-router-dom";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
import JobAltTransportChange from "../job-at-change/job-at-change.component";
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
import ProductionListColumnAlert from "./production-list-columns.alert.component";
@@ -15,8 +15,9 @@ import ProductionListColumnPaintPriority from "./production-list-columns.paintpr
import ProductionListColumnNote from "./production-list-columns.productionnote.component";
import ProductionListColumnStatus from "./production-list-columns.status.component";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
import ProductionListLastContacted from "./production-list-columns.lastcontacted.component";
const r = ({ technician, state }) => {
const r = ({ technician, state, activeStatuses }) => {
return [
{
title: i18n.t("jobs.actions.viewdetail"),
@@ -97,6 +98,17 @@ const r = ({ technician, state }) => {
<ProductionListDate record={record} field="scheduled_completion" />
),
},
{
title: i18n.t("jobs.fields.date_last_contacted"),
dataIndex: "date_last_contacted",
key: "date_last_contacted",
ellipsis: true,
sorter: (a, b) => dateSort(a.date_last_contacted, b.date_last_contacted),
sortOrder:
state.sortedInfo.columnKey === "date_last_contacted" &&
state.sortedInfo.order,
render: (text, record) => <ProductionListLastContacted record={record} />,
},
{
title: i18n.t("jobs.fields.scheduled_delivery"),
dataIndex: "scheduled_delivery",
@@ -161,6 +173,15 @@ const r = ({ technician, state }) => {
<PhoneFormatter>{record.ownr_ph1}</PhoneFormatter>
),
},
{
title: i18n.t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
render: (text, record) => (
<PhoneFormatter>{record.ownr_ph2}</PhoneFormatter>
),
},
{
title: i18n.t("jobs.fields.specialcoveragepolicy"),
dataIndex: "special_coverage_policy",
@@ -189,7 +210,7 @@ const r = ({ technician, state }) => {
dataIndex: "status",
key: "status",
ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sorter: (a, b) => statusSort(a.status, b.status, activeStatuses),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
render: (text, record) => <ProductionListColumnStatus record={record} />,

View File

@@ -1,17 +1,20 @@
import { useMutation } from "@apollo/client";
import { DatePicker, Dropdown } from "antd";
import { DatePicker, Dropdown, TimePicker, Button, Card } from "antd";
import moment from "moment";
import React from "react";
import React, { useState } from "react";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { DateFormatter } from "../../utils/DateFormatter";
import { useTranslation } from "react-i18next";
const OneCalendarDay = 60 * 60 * 24 * 1000;
const Now = new Date();
export default function ProductionListDate({ record, field }) {
export default function ProductionListDate({ record, field, time }) {
const [updateAlert] = useMutation(UPDATE_JOB);
const [visible, setVisible] = useState(false);
const { t } = useTranslation();
const handleChange = (date) => {
logImEXEvent("product_toggle_date", { field });
@@ -25,27 +28,47 @@ export default function ProductionListDate({ record, field }) {
},
}).then(() => {
if (record.refetch) record.refetch();
if (!time) {
setVisible(false);
}
});
};
return (
<div>
<Dropdown
trigger={["click"]}
//trigger={["click"]}
visible={visible}
style={{
height: "19px",
}}
overlay={
<div onClick={(e) => e.stopPropagation()}>
<Card
style={{ padding: "1rem" }}
onClick={(e) => e.stopPropagation()}
>
<DatePicker
onClick={(e) => e.stopPropagation()}
value={(record[field] && moment(record[field])) || null}
onChange={handleChange}
format="MM/DD/YYYY"
/>
</div>
{time && (
<TimePicker
onClick={(e) => e.stopPropagation()}
value={(record[field] && moment(record[field])) || null}
onChange={handleChange}
format="hh:mm a"
/>
)}
<Button onClick={() => setVisible(false)}>
{t("general.actions.close")}
</Button>
</Card>
}
>
<div
onClick={() => setVisible(true)}
style={{
height: "19px",
}}

View File

@@ -40,7 +40,7 @@ export default function ProductionListColumnDetailPriority({ record }) {
key="set"
title={t("production.actions.detailpriority-set")}
>
{new Array(9).fill().map((value, index) => (
{new Array(15).fill().map((value, index) => (
<Menu.Item key={index + 1}>{index + 1}</Menu.Item>
))}
</Menu.SubMenu>

View File

@@ -0,0 +1,132 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Dropdown, Form, Input, notification, Space } from "antd";
import moment from "moment";
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import { DateFormatter } from "../../utils/DateFormatter";
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionLastContacted);
export function ProductionLastContacted({ currentUser, record }) {
const [updateAlert] = useMutation(UPDATE_JOB);
const [insertNote] = useMutation(INSERT_NEW_NOTE);
const [visible, setVisible] = useState(false);
const { t } = useTranslation();
const [form] = Form.useForm();
const handleFinish = async ({ date_last_contacted, note }) => {
logImEXEvent("production_last_contacted");
//e.stopPropagation();
const res = await updateAlert({
variables: {
jobId: record.id,
job: {
date_last_contacted,
},
},
});
if (res.errors) {
notification.open({
type: "error",
message: t("jobs.errors.saving", {
error: JSON.stringify(res.errors),
}),
});
}
if (note && note.trim() !== "") {
//Insert a note.
const res2 = await insertNote({
variables: {
noteInput: {
jobid: record.id,
text: note,
created_by: currentUser.email,
},
},
});
if (res2.errors) {
notification.open({
type: "error",
message: t("notes.errors.inserting", {
error: JSON.stringify(res.errors),
}),
});
}
}
if (record.refetch) record.refetch();
setVisible(false);
};
useEffect(() => {
if (visible) {
form.setFieldsValue({
note: null,
date_last_contacted:
record.date_last_contacted && moment(record.date_last_contacted),
});
}
}, [visible, form, record.date_last_contacted]);
return (
<div>
<Dropdown
//trigger={["click"]}
visible={visible}
style={{
height: "19px",
}}
overlay={
<Card
style={{ padding: "1rem" }}
onClick={(e) => e.stopPropagation()}
>
<Form form={form} onFinish={handleFinish} layout="vertical">
<Form.Item name="date_last_contacted">
<FormDateTimePickerComponent />
</Form.Item>
<Form.Item label={t("notes.labels.notetoadd")} name="note">
<Input.TextArea rows={4} />
</Form.Item>
<Space>
<Button type="primary" htmlType="submit">
{t("general.actions.save")}
</Button>
<Button onClick={() => setVisible(false)}>
{t("general.actions.close")}
</Button>
</Space>
</Form>
</Card>
}
>
<div
onClick={() => setVisible(true)}
style={{
height: "19px",
}}
>
<DateFormatter bordered={false}>
{record.date_last_contacted}
</DateFormatter>
</div>
</Dropdown>
</div>
);
}

View File

@@ -40,7 +40,7 @@ export default function ProductionListColumnPaintPriority({ record }) {
key="set"
title={t("production.actions.paintpriority-set")}
>
{new Array(9).fill().map((value, index) => (
{new Array(15).fill().map((value, index) => (
<Menu.Item key={index + 1}>{index + 1}</Menu.Item>
))}
</Menu.SubMenu>

View File

@@ -77,6 +77,10 @@ export default function ProductionListDetail({ jobs }) {
phone={data.jobs_by_pk.ownr_ph1}
jobid={data.jobs_by_pk.id}
/>
<StartChatButton
phone={data.jobs_by_pk.ownr_ph2}
jobid={data.jobs_by_pk.id}
/>
</Descriptions.Item>
<Descriptions.Item label={t("jobs.fields.vehicle")}>
{`${theJob.v_model_yr || ""} ${theJob.v_color || ""} ${

View File

@@ -66,12 +66,7 @@ export function ProductionListSaveConfigButton({
};
const popMenu = (
<div>
<Form
layout="vertical"
form={form}
onFinish={handleSaveConfig}
initialValues={{ driveable: true, towin: false }}
>
<Form layout="vertical" form={form} onFinish={handleSaveConfig}>
<Form.Item
label={t("production.labels.viewname")}
name="name"

View File

@@ -37,9 +37,11 @@ export function ProductionListTable({
.filter((pc) => pc.name === value)[0]
.columns.columnKeys.map((k) => {
return {
...ProductionListColumns({ technician, state }).find(
(e) => e.key === k.key
),
...ProductionListColumns({
technician,
state,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).find((e) => e.key === k.key),
width: k.width,
};
})
@@ -55,6 +57,19 @@ export function ProductionListTable({
if (assoc) {
await updateDefaultProdView({
variables: { assocId: assoc.id, view: value },
update(cache) {
cache.modify({
id: cache.identify(bodyshop),
fields: {
associations(existingAssociations, { readField }) {
return existingAssociations.map((a) => {
if (a.useremail !== currentUser.email) return a;
return { ...a, default_prod_list_view: value };
});
},
},
});
},
});
}
};
@@ -75,9 +90,11 @@ export function ProductionListTable({
setColumns(
bodyshop.production_config[0].columns.columnKeys.map((k) => {
return {
...ProductionListColumns({ technician, state }).find(
(e) => e.key === k.key
),
...ProductionListColumns({
technician,
state,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).find((e) => e.key === k.key),
width: k.width,
};
})
@@ -85,13 +102,18 @@ export function ProductionListTable({
setState(bodyshop.production_config[0].columns.tableState);
};
const assoc = bodyshop.associations.find(
(a) => a.useremail === currentUser.email
);
const defaultView = assoc && assoc.default_prod_list_view;
return (
<div style={{ width: "10rem" }}>
<Select
onSelect={handleSelect}
placeholder={t("production.labels.selectview")}
optionLabelProp="label"
defaultValue={defaultView}
>
{bodyshop.production_config.map((config) => (
<Select.Option key={config.name} label={config.name}>

View File

@@ -1,4 +1,12 @@
import { Dropdown, Input, Menu, PageHeader, Space, Table } from "antd";
import {
Dropdown,
Input,
Menu,
PageHeader,
Space,
Statistic,
Table,
} from "antd";
import React, { useMemo, useState } from "react";
import ReactDragListView from "react-drag-listview";
import { useTranslation } from "react-i18next";
@@ -16,6 +24,7 @@ import ProductionListSaveConfigButton from "../production-list-save-config-butto
import ProductionListPrint from "./production-list-print.component";
import ProductionListTableViewSelect from "./production-list-table-view-select.component";
import ResizeableTitle from "./production-list-table.resizeable.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -31,13 +40,14 @@ export function ProductionListTable({
currentUser,
}) {
const [searchText, setSearchText] = useState("");
const { Production_List_Status_Colors } = useTreatments([
"Production_List_Status_Colors",
]);
const assoc = bodyshop.associations.find(
(a) => a.useremail === currentUser.email
);
const defaultView = useMemo(() => {
const assoc = bodyshop.associations.find(
(a) => a.useremail === currentUser.email
);
return assoc && assoc.default_prod_list_view;
}, [bodyshop.associations, currentUser.email]);
const defaultView = assoc && assoc.default_prod_list_view;
const [state, setState] = useState(
(bodyshop.production_config &&
@@ -60,9 +70,11 @@ export function ProductionListTable({
matchingColumnConfig &&
matchingColumnConfig.columns.columnKeys.map((k) => {
return {
...ProductionListColumns({ technician, state }).find(
(e) => e.key === k.key
),
...ProductionListColumns({
technician,
state,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).find((e) => e.key === k.key),
width: k.width,
};
})) ||
@@ -157,9 +169,24 @@ export function ProductionListTable({
if (!!!columns) return <div>No columns found.</div>;
const totalHrs = data
.reduce(
(acc, val) =>
acc +
(val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0) +
(val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
return (
<div>
<PageHeader
title={
<Statistic
title={t("dashboard.titles.productionhours")}
value={totalHrs}
/>
}
extra={
<Space wrap>
<ProductionListColumnsAdd
@@ -193,8 +220,28 @@ export function ProductionListTable({
handleSelector=".prod-header-dropdown"
>
<Table
sticky
pagination={false}
size="small"
className="production-list-table"
onRow={
Production_List_Status_Colors.treatment === "on" &&
((record, index) => {
if (!bodyshop.md_ro_statuses.production_colors) return null;
const color = bodyshop.md_ro_statuses.production_colors.find(
(x) => x.status === record.status
);
if (!color) return null;
return {
style: {
backgroundColor: `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`,
},
};
})
}
components={{
header: {
cell: ResizeableTitle,

View File

@@ -1,4 +1,4 @@
import { Space, Tag } from "antd";
import { Space } from "antd";
import Axios from "axios";
import queryString from "query-string";
import React, { useEffect } from "react";
@@ -9,7 +9,7 @@ import QboSignIn from "../../assets/qbo/C2QB_green_btn_med_default.svg";
export default function QboAuthorizeComponent() {
const location = useLocation();
const history = useHistory();
const [cookies, setCookie] = useCookies(["access_token", "refresh_token"]);
const [, setCookie] = useCookies(["access_token", "refresh_token"]);
const handleQbSignIn = async () => {
const result = await Axios.post("/qbo/authorize");
@@ -24,16 +24,20 @@ export default function QboAuthorizeComponent() {
const hasBeenCalledBack = code && realmId && state;
if (hasBeenCalledBack) {
setCookie("qbo_code", code, { path: "/" });
setCookie("qbo_state", state, { path: "/" });
// setCookie("qbo_code", code, { path: "/" });
// setCookie("qbo_state", state, { path: "/" });
let expires = new Date();
expires.setTime(expires.getTime() + 8726400 * 1000);
// let expires = new Date();
// expires.setTime(expires.getTime() + 8726400 * 1000);
setCookie("qbo_realmId", realmId, {
path: "/",
expires,
});
// setCookie("qbo_realmId", realmId, {
// path: "/",
// expires,
// ...(process.env.NODE_ENV !== "development"
// ? { domain: `.${window.location.host}` }
// : {}),
// });
history.push({ pathname: `/manage/accounting/receivables` });
}
@@ -48,9 +52,7 @@ export default function QboAuthorizeComponent() {
src={QboSignIn}
style={{ cursor: "pointer" }}
/>
{!cookies.qbo_realmId && (
<Tag color="red">No QuickBooks company has been connected.</Tag>
)}
{error && JSON.parse(decodeURIComponent(error)).error_description}
</Space>
);

View File

@@ -3,6 +3,7 @@ import { Button, Card, Col, PageHeader, Row, Space } from "antd";
import React from "react";
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
import ScheduleModal from "../schedule-job-modal/schedule-job-modal.container";
//import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
import ScheduleProductionList from "../schedule-production-list/schedule-production-list.component";
export default function ScheduleCalendarComponent({ data, refetch }) {
@@ -21,8 +22,10 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
>
<SyncOutlined />
</Button>
<ScheduleProductionList />
{
// <ScheduleManualEvent />
}
</Space>
}
/>

View File

@@ -124,7 +124,7 @@ export function ScheduleJobModalComponent({
handleDateBlur();
}}
>
<DateFormatter>{d}</DateFormatter>
<DateFormatter includeDay>{d}</DateFormatter>
</Button>
))}
</Space>

View File

@@ -63,6 +63,20 @@ export function ScheduleJobModalContainer({
skip: !visible || !!!jobId,
});
useEffect(() => {
if (
existingAppointments.data &&
existingAppointments.data.appointments.length > 0 &&
!existingAppointments.data.appointments[0].canceled
) {
form.setFieldsValue({
color: existingAppointments.data.appointments[0].color,
note: existingAppointments.data.appointments[0].note,
});
}
}, [existingAppointments.data, form]);
const handleFinish = async (values) => {
logImEXEvent("schedule_new_appointment");
@@ -105,7 +119,7 @@ export function ScheduleJobModalContainer({
start: moment(values.start),
end: moment(values.start).add(bodyshop.appt_length || 60, "minutes"),
color: values.color,
note:values.note
note: values.note,
},
jobId: jobId,
altTransport: values.alt_transport,
@@ -188,6 +202,9 @@ export function ScheduleJobModalContainer({
start: null,
// smartDates: [],
scheduled_completion: null,
color: context.color,
alt_transport: context.alt_transport,
note: context.note,
}}
>
<ScheduleJobModalComponent

View File

@@ -0,0 +1,119 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Form, Input, Popover, Space } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import {
INSERT_APPOINTMENT,
UPDATE_APPOINTMENT,
} from "../../graphql/appointments.queries";
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
export default function ScheduleManualEvent({ event }) {
const { t } = useTranslation();
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
const [updateAppointment] = useMutation(UPDATE_APPOINTMENT);
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const [visibility, setVisibility] = useState(false);
// const [callQuery, { loading: entryLoading, data: entryData }] = useLazyQuery(
// QUERY_SCOREBOARD_ENTRY
// );
useEffect(() => {
if (visibility && event) {
form.setFieldsValue({ event });
}
}, [visibility, form, event]);
useEffect(() => {
// if (entryData && entryData.scoreboard && entryData.scoreboard[0]) {
// console.log("Setting FOrm");
// // form.setFieldsValue(entryData.scoreboard[0]);
// }
}, [form]);
const handleFinish = async (values) => {
logImEXEvent("job_close_add_to_scoreboard");
setLoading(true);
try {
if (event && event.id) {
updateAppointment();
} else {
insertAppointment();
}
} catch (error) {
console.log(error);
} finally {
setLoading(false);
setVisibility(false);
}
};
const overlay = (
<Card>
<div>
<Form form={form} layout="vertical" onFinish={handleFinish}>
<Form.Item
label={t("schedule.fields.note")}
name="note"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("schedule.fields.start")}
name="start"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<FormDateTimePickerComponent />
</Form.Item>
<Form.Item
label={t("schedule.fields.end")}
name="end"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<FormDateTimePickerComponent />
</Form.Item>
<Space wrap>
<Button type="primary" htmlType="submit">
{t("general.actions.save")}
</Button>
<Button onClick={() => setVisibility(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</Form>
</div>
</Card>
);
const handleClick = (e) => {
setVisibility(true);
};
return (
<Popover content={overlay} visible={visibility}>
<Button loading={loading} onClick={handleClick}>
{t("schedule.labels.manualevent")}
</Button>
</Popover>
);
}

View File

@@ -17,6 +17,8 @@ import PhoneFormItem, {
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
export default function ShopInfoGeneral({ form }) {
const { t } = useTranslation();
return (
@@ -472,6 +474,19 @@ export default function ShopInfoGeneral({ form }) {
>
<Switch />
</Form.Item>
<Form.Item
name={["md_ded_notes"]}
label={t("bodyshop.fields.md_ded_notes")}
rules={[
{
required: true,
//message: t("general.validation.required"),
type: "array",
},
]}
>
<Select mode="tags" />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
<Form.List name={["md_messaging_presets"]}>
@@ -706,7 +721,7 @@ export default function ShopInfoGeneral({ form }) {
>
<Input />
</Form.Item>
<Space wrap>
<Space>
<Form.Item
label={t("bodyshop.fields.md_ins_co.zip")}
key={`${index}zip`}
@@ -744,6 +759,95 @@ export default function ShopInfoGeneral({ form }) {
}}
</Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.estimators")}>
<Form.List name={["md_estimators"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item
label={t("jobs.fields.est_co_nm")}
key={`${index}est_co_nm`}
name={[field.name, "est_co_nm"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.est_ct_fn")}
key={`${index}est_ct_fn`}
name={[field.name, "est_ct_fn"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.est_ct_ln")}
key={`${index}est_ct_ln`}
name={[field.name, "est_ct_ln"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.est_ph1")}
key={`${index}est_ph1`}
name={[field.name, "est_ph1"]}
rules={[
({ getFieldValue }) =>
PhoneItemFormatterValidation(getFieldValue, [
field.name,
"est_ph",
]),
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.est_ea")}
key={`${index}est_ea`}
name={[field.name, "est_ea"]}
rules={[
{
type: "email",
message: "This is not a valid email address.",
},
]}
>
<FormItemEmail
email={form.getFieldValue([field.name, "est_ea"])}
/>
</Form.Item>
<Space>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
/>
</Space>
</LayoutFormRow>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")}>
<Form.List name={["md_ccc_rates"]}>
{(fields, { add, remove, move }) => {

View File

@@ -42,7 +42,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_lab")}
@@ -55,7 +55,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_lad")}
@@ -68,7 +68,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_lae")}
@@ -81,7 +81,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_laf")}
@@ -94,7 +94,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_lag")}
@@ -107,7 +107,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_lam")}
@@ -120,7 +120,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_lar")}
@@ -133,7 +133,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_las")}
@@ -146,7 +146,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_la1")}
@@ -159,7 +159,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_la2")}
@@ -172,7 +172,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_la3")}
@@ -185,7 +185,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_la4")}
@@ -198,7 +198,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_mash")}
@@ -211,7 +211,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_mapa")}
@@ -224,7 +224,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_ma2s")}
@@ -237,7 +237,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_ma3s")}
@@ -250,7 +250,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
{
// <Form.Item
@@ -264,7 +264,7 @@ export default function ShopInfoLaborRates({ form }) {
// },
// ]}
// >
// <CurrencyInput />
// <CurrencyInput min={0} />
// </Form.Item>
// <Form.Item
// label={t("jobs.fields.rate_macs")}
@@ -277,7 +277,7 @@ export default function ShopInfoLaborRates({ form }) {
// },
// ]}
// >
// <CurrencyInput />
// <CurrencyInput min={0} />
// </Form.Item>
}
<Form.Item
@@ -291,7 +291,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.rate_mahw")}
@@ -304,7 +304,7 @@ export default function ShopInfoLaborRates({ form }) {
},
]}
>
<CurrencyInput />
<CurrencyInput min={0} />
</Form.Item>
<DeleteFilled
onClick={() => {

View File

@@ -203,6 +203,14 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
</LayoutFormRow>
</>
)}
{bodyshop.pbs_serialnumber && (
<>
<DataLabel label={t("bodyshop.labels.dms.pbs_serialnumber")}>
{form.getFieldValue("pbs_serialnumber")}
</DataLabel>
</>
)}
<LayoutFormRow header={t("bodyshop.labels.responsibilitycenters.costs")}>
<Form.List name={["md_responsibility_centers", "costs"]}>
{(fields, { add, remove }) => {
@@ -515,6 +523,27 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.laa"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}costs-LAA`}
name={[field.name, "costs", "LAA"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.lab"
@@ -1086,6 +1115,27 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.laa"
)}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
key={`${index}profits-LAA`}
name={[field.name, "profits", "LAA"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t(
"bodyshop.fields.responsibilitycenters.lab"
@@ -1673,6 +1723,24 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.laa")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "costs", "LAA"]}
>
<Select>
{costOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.lab")}
rules={[
@@ -2161,6 +2229,24 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.laa")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "defaults", "profits", "LAA"]}
>
<Select>
{profitOptions.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.lab")}
rules={[

View File

@@ -1,8 +1,12 @@
import { Form, Select } from "antd";
import { DeleteFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Button, Form, Select, Space } from "antd";
import React, { useState } from "react";
import { ChromePicker } from "react-color";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const SelectorDiv = styled.div`
.ant-form-item .ant-select {
width: 200px;
@@ -11,6 +15,9 @@ const SelectorDiv = styled.div`
export default function ShopInfoROStatusComponent({ form }) {
const { t } = useTranslation();
const { Production_List_Status_Colors } = useTreatments([
"Production_List_Status_Colors",
]);
const [options, setOptions] = useState(
form.getFieldValue(["md_ro_statuses", "statuses"]) || []
@@ -257,6 +264,97 @@ export default function ShopInfoROStatusComponent({ form }) {
</Select>
</Form.Item>
</LayoutFormRow>
{Production_List_Status_Colors.treatment === "on" && (
<LayoutFormRow
grow
header={t("bodyshop.fields.statuses.production_colors")}
>
<Form.List name={["md_ro_statuses", "production_colors"]}>
{(fields, { add, remove, move }) => {
return (
<div>
<LayoutFormRow>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Space direction="vertical">
<div style={{ display: "flex" }}>
<Form.Item
style={{ flex: 1 }}
label={t("jobs.fields.status")}
key={`${index}status`}
name={[field.name, "status"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select>
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
</div>
<Form.Item
label={t("bodyshop.fields.statuses.color")}
key={`${index}color`}
name={[field.name, "color"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<ColorPicker />
</Form.Item>
</Space>
</Form.Item>
))}
</LayoutFormRow>
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
)}
</SelectorDiv>
);
}
const ColorPicker = ({ value, onChange, style, ...restProps }) => {
const handleChange = (color) => {
console.log(
"🚀 ~ file: shop-info.rostatus.component.jsx ~ line 345 ~ color",
color
);
if (onChange) onChange(color.rgb);
};
return (
<ChromePicker
{...restProps}
color={value}
onChangeComplete={handleChange}
/>
);
};

View File

@@ -44,7 +44,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
},
]}
>
<TimePicker showSecond={false} format="hh:mm" />
<TimePicker showSecond={false} format="HH:mm" />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.schedule_end_time")}
@@ -56,7 +56,7 @@ export default function ShopInfoSchedulingComponent({ form }) {
},
]}
>
<TimePicker showSecond={false} format="hh:mm" />
<TimePicker showSecond={false} format="HH:mm" />
</Form.Item>
<Form.Item
name={["appt_alt_transport"]}

View File

@@ -50,7 +50,9 @@ export function TechClockInComponent({ form, bodyshop, technician }) {
{emps &&
emps.rates.map((item) => (
<Select.Option key={item.cost_center}>
{item.cost_center}
{item.cost_center === "timetickets.labels.shift"
? t(item.cost_center)
: item.cost_center}
</Select.Option>
))}
</Select>

View File

@@ -139,7 +139,9 @@ export function TechClockOffButton({
emps &&
emps.rates.map((item) => (
<Select.Option key={item.cost_center}>
{item.cost_center}
{item.cost_center === "timetickets.labels.shift"
? t(item.cost_center)
: item.cost_center}
</Select.Option>
))
)}

View File

@@ -83,7 +83,9 @@ export function TechClockedInList({ technician }) {
<DateTimeFormatter>{ticket.clockon}</DateTimeFormatter>
</DataLabel>
<DataLabel label={t("timetickets.fields.cost_center")}>
{ticket.cost_center}
{ticket.cost_center === "timetickets.labels.shift"
? t(ticket.cost_center)
: ticket.cost_center}
</DataLabel>
</Card>
</List.Item>

View File

@@ -28,6 +28,7 @@ export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
ownr_co_nm
ownr_fn
ownr_ph1
ownr_ph2
ownr_ea
clm_total
id
@@ -126,6 +127,7 @@ export const QUERY_APPOINTMENT_BY_DATE = gql`
ownr_ln
ownr_fn
ownr_ph1
ownr_ph2
ownr_ea
clm_total
id
@@ -194,10 +196,11 @@ export const CANCEL_APPOINTMENT_BY_ID = gql`
export const QUERY_APPOINTMENTS_BY_JOBID = gql`
query QUERY_APPOINTMENTS_BY_JOBID($jobid: uuid!) {
appointments(where: { jobid: { _eq: $jobid } }) {
appointments(where: { jobid: { _eq: $jobid } }, order_by: { start: desc }) {
start
id
end
color
isintake
arrived
canceled

View File

@@ -93,6 +93,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
backordered_on
}
order_number
comments
user_email
}
bills(where: { jobid: { _eq: $jobid } }, order_by: { date: desc }) {

View File

@@ -94,6 +94,10 @@ export const QUERY_BODYSHOP = gql`
attach_pdf_to_email
tt_allow_post_to_invoiced
cdk_configuration
md_estimators
md_ded_notes
pbs_configuration
pbs_serialnumber
employees {
id
active
@@ -184,6 +188,10 @@ export const UPDATE_SHOP = gql`
attach_pdf_to_email
tt_allow_post_to_invoiced
cdk_configuration
md_estimators
md_ded_notes
pbs_configuration
pbs_serialnumber
employees {
id
first_name

View File

@@ -10,6 +10,7 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
ownr_ln
ownr_co_nm
ownr_ph1
ownr_ph2
ownr_ea
owner {
id
@@ -79,6 +80,7 @@ export const QUERY_PARTS_QUEUE = gql`
ownr_ln
ownr_co_nm
ownr_ph1
ownr_ph2
ownr_ea
plate_no
plate_st
@@ -122,9 +124,11 @@ export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
actual_in
scheduled_completion
scheduled_delivery
date_last_contacted
ins_co_nm
clm_total
ownr_ph1
ownr_ph2
special_coverage_policy
owner_owing
production_vars
@@ -359,6 +363,7 @@ export const GET_JOB_BY_PK = gql`
kmin
kmout
referral_source
referral_source_extra
unit_number
po_number
special_coverage_policy
@@ -380,6 +385,7 @@ export const GET_JOB_BY_PK = gql`
vehicleid
driveable
towin
loss_of_use
vehicle {
id
plate_no
@@ -454,9 +460,11 @@ export const GET_JOB_BY_PK = gql`
ownr_zip
ownr_ctry
ownr_ph1
ownr_ph2
production_vars
ca_gst_registrant
ownerid
ded_note
owner {
id
ownr_fn
@@ -470,6 +478,7 @@ export const GET_JOB_BY_PK = gql`
ownr_zip
ownr_ctry
ownr_ph1
ownr_ph2
}
labor_rate_desc
rate_la1
@@ -508,6 +517,7 @@ export const GET_JOB_BY_PK = gql`
date_open
date_scheduled
date_invoiced
date_last_contacted
date_exported
status
owner_owing
@@ -563,6 +573,7 @@ export const GET_JOB_BY_PK = gql`
parts_order {
id
order_number
comments
order_date
user_email
vendor {
@@ -626,7 +637,7 @@ export const GET_JOB_RECONCILIATION_BY_PK = gql`
is_credit_memo
isinhouse
exported
billlines {
billlines(where: { deductedfromlbr: { _eq: false } }) {
actual_price
quantity
actual_cost
@@ -684,8 +695,10 @@ export const QUERY_JOB_CARD_DETAILS = gql`
ownr_ln
ownr_co_nm
ownr_ph1
ownr_ph2
ownr_ea
ca_gst_registrant
owner_owing
special_coverage_policy
available_jobs {
id
@@ -724,6 +737,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
actual_completion
actual_delivery
actual_in
scheduled_in
po_number
id
ins_co_nm
@@ -745,6 +759,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
scheduled_in
scheduled_delivery
date_invoiced
date_last_contacted
date_open
date_exported
@@ -830,6 +845,7 @@ export const QUERY_TECH_JOB_DETAILS = gql`
scheduled_in
scheduled_delivery
date_invoiced
date_last_contacted
date_open
date_exported
voided
@@ -964,6 +980,7 @@ export const CONVERT_JOB_TO_RO = gql`
$driveable: Boolean
$towin: Boolean
$referral_source: String
$referral_source_extra: String
) {
update_jobs(
where: { id: { _eq: $jobId } }
@@ -975,6 +992,7 @@ export const CONVERT_JOB_TO_RO = gql`
towin: $towin
driveable: $driveable
referral_source: $referral_source
referral_source_extra: $referral_source_extra
}
) {
returning {
@@ -984,6 +1002,7 @@ export const CONVERT_JOB_TO_RO = gql`
class
ins_co_nm
referral_source
referral_source_extra
}
}
}
@@ -1008,6 +1027,7 @@ export const GET_JOB_INFO_FOR_STRIPE = gql`
ownr_ln
ownr_co_nm
ownr_ph1
ownr_ph2
ownr_ea
}
}
@@ -1497,6 +1517,7 @@ export const QUERY_ALL_JOB_FIELDS = gql`
rate_mash
rate_matd
referral_source
referral_source_extra
regie_number
selling_dealer
selling_dealer_contact
@@ -1643,6 +1664,7 @@ export const QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED = gql`
ownr_co_nm
ownerid
ownr_ph1
ownr_ph2
ownr_ea
plate_no
plate_st
@@ -1750,7 +1772,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
actual_in
kmin
kmout
joblines(where: { removed: { _eq: false } }) {
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
id
removed
tax_part
@@ -1858,6 +1880,7 @@ export const GET_JOB_FOR_CC_CONTRACT = gql`
ownr_city
ownr_zip
ownr_ph1
ownr_ph2
}
}
`;

View File

@@ -2,7 +2,7 @@ import { gql } from "@apollo/client";
export const QUERY_SEARCH_OWNER_BY_IDX = gql`
query QUERY_SEARCH_OWNER_BY_IDX($search: String!) {
search_owners(args: { search: $search }) {
search_owners(args: { search: $search }, limit: 100) {
ownr_fn
ownr_ln
ownr_ph1

View File

@@ -23,6 +23,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
ownr_ln
ownr_co_nm
ownr_ph1
ownr_ph2
}
search_vehicles(args: { search: $search }) {
id

View File

@@ -23,6 +23,11 @@ Dinero.globalRoundingMode = "HALF_EVEN";
if (process.env.NODE_ENV !== "development") {
Sentry.init({
dsn: "https://fd7e89369b6b4bdc9c6c4c9f22fa4ee4@o492140.ingest.sentry.io/5651027",
ignoreErrors: [
"ResizeObserver loop",
"Module specifier, 'fs' does not start",
"Module specifier, 'zlib' does not start with",
],
integrations: [
// new Integrations.BrowserTracing(),
// new Sentry.Integrations.Breadcrumbs({ console: true }),

View File

@@ -612,7 +612,7 @@ export const Pricing11DataSource = {
<br /> 14500GB流量包
<br /> 14国内按峰值宽带账单
<br /> 14弹性计算
<br /> 14云服务器 ECS{" "}
<br /> 14云服务器 ECS
</span>
),
},

View File

@@ -196,7 +196,11 @@ export function JobsDetailPage({
<PrinterFilled />
{t("jobs.actions.printCenter")}
</Button>
<JobsConvertButton job={job} refetch={refetch} />
<JobsConvertButton
job={job}
refetch={refetch}
parentFormIsFieldsTouched={form.isFieldsTouched}
/>
<JobsDetailHeaderActions key="actions" job={job} refetch={refetch} />
<Button
type="primary"

View File

@@ -5,12 +5,12 @@ import preval from "preval.macro";
import React, { lazy, Suspense, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
import { Link, Route, Switch } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import BreadCrumbs from "../../components/breadcrumbs/breadcrumbs.component";
import ChatAffixContainer from "../../components/chat-affix/chat-affix.container";
import ConflictComponent from "../../components/conflict/conflict.component";
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
import FcmNotification from "../../components/fcm-notification/fcm-notification.component";
//import FooterComponent from "../../components/footer/footer.component";
//Component Imports

View File

@@ -6,8 +6,8 @@ import AlertComponent from "../../components/alert/alert.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
import { setBodyshop } from "../../redux/user/user.actions";
import ManagePage from "./manage.page.component";
import "../../utils/RegisterSw";
import ManagePage from "./manage.page.component";
const mapDispatchToProps = (dispatch) => ({
setBodyshop: (bs) => dispatch(setBodyshop(bs)),
@@ -21,7 +21,9 @@ function ManagePageContainer({ match, setBodyshop }) {
const { t } = useTranslation();
useEffect(() => {
if (data) setBodyshop(data.bodyshops[0] || { notfound: true });
if (data) {
setBodyshop(data.bodyshops[0] || { notfound: true });
}
}, [data, setBodyshop]);
if (loading)

View File

@@ -13,7 +13,7 @@ import { doc } from "firebase/firestore";
import i18next from "i18next";
import LogRocket from "logrocket";
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
import { tracker } from "../../App/App.container";
import { factory, tracker } from "../../App/App.container";
import {
getCurrentUser,
logImEXEvent,
@@ -250,6 +250,8 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
try {
const userEmail = yield select((state) => state.user.currentUser.email);
factory.client(payload.imexshopid);
const authRecord = payload.associations.filter(
(a) => a.useremail === userEmail
);

View File

@@ -270,6 +270,7 @@
"md_categories": "Categories",
"md_ccc_rates": "Courtesy Car Contract Rate Presets",
"md_classes": "Classes",
"md_ded_notes": "Deductible Notes",
"md_hour_split": {
"paint": "Paint Hour Split",
"prep": "Prep Hour Split"
@@ -396,6 +397,7 @@
"la2": "LA2",
"la3": "LA3",
"la4": "LA4",
"laa": "Aluminum",
"lab": "Body",
"lad": "Diagnostic",
"lae": "Electrical",
@@ -449,6 +451,7 @@
"status": "Status Label",
"statuses": {
"active_statuses": "Active Statuses (Filtering for Active Jobs throughout system)",
"color": "Color",
"default_arrived": "Default Arrived Status (Transition to Production)",
"default_bo": "Default Backordered Status",
"default_canceled": "Default Canceled Status",
@@ -465,6 +468,7 @@
"open_statuses": "Open Statuses",
"post_production_statuses": "Post-Production Statuses",
"pre_production_statuses": "Pre-Production Statuses",
"production_colors": "Production Status Colors",
"production_statuses": "Production Statuses"
},
"target_touchtime": "Target Touch Time",
@@ -498,6 +502,7 @@
},
"emaillater": "Email Later",
"employees": "Employees",
"estimators": "Estimators",
"insurancecos": "Insurance Companies",
"intakechecklist": "Intake Checklist",
"jobstatuses": "Job Statuses",
@@ -1052,8 +1057,10 @@
},
"profitcenter_labor": "Profit Center: Labor",
"profitcenter_part": "Profit Center: Part",
"prt_dsmk_p": "Line Markup %",
"prt_dsmk_m": "Line Discount/Markup $",
"prt_dsmk_p": "Line Discount/Markup %",
"status": "Status",
"tax_part": "Tax Part",
"total": "Total",
"unq_seq": "Seq #"
},
@@ -1086,6 +1093,7 @@
"autoallocate": "Auto Allocate",
"changelaborrate": "Change Labor Rate",
"changestatus": "Change Status",
"changestimator": "Change Estimator",
"convert": "Convert",
"deliver": "Deliver",
"dms": {
@@ -1196,9 +1204,11 @@
"date_estimated": "Date Estimated",
"date_exported": "Exported",
"date_invoiced": "Invoiced",
"date_last_contacted": "Last Contacted Date",
"date_open": "Open",
"date_scheduled": "Scheduled",
"ded_amt": "Deductible",
"ded_note": "Deductible Note",
"ded_status": "Deductible Status",
"depreciation_taxes": "Depreciation/Taxes",
"dms": {
@@ -1274,6 +1284,7 @@
"local_tax_rate": "Local Tax Rate",
"loss_date": "Loss Date",
"loss_desc": "Loss Description",
"loss_of_use": "Loss of Use",
"ma2s": "2 Stage Paint",
"ma3s": "3 Stage Pain",
"mabl": "MABL?",
@@ -1287,6 +1298,7 @@
"owner_owing": "Cust. Owes",
"ownr_ea": "Email",
"ownr_ph1": "Phone 1",
"ownr_ph2": "Phone 2",
"paa": "Aftermarket",
"pac": "Rechromed",
"pae": "Existing",
@@ -1335,6 +1347,8 @@
"rate_mapa": "Paint Materials",
"rate_mash": "Shop Material",
"rate_matd": "Tire Disposal",
"referral_source_extra": "Other Referral Source",
"referral_source_other": "",
"referralsource": "Referral Source",
"regie_number": "Registration #",
"repairtotal": "Repair Total",
@@ -1520,6 +1534,7 @@
"sale_labor": "Sales - Labor",
"sale_parts": "Sales - Parts & Sublet",
"sales": "Sales",
"savebeforeconversion": "You have unsaved changes on the job. Please save them before converting it. ",
"scheduledinchange": "The scheduled in is based off the latest appointment. To change this date, please schedule or reschedule the job. ",
"specialcoveragepolicy": "Special Coverage Policy Applies",
"state_tax_amt": "Provincial/State Taxes",
@@ -1733,6 +1748,9 @@
"edit": "Edit Note",
"new": "New Note"
},
"errors": {
"inserting": "Error inserting note. {{error}}"
},
"fields": {
"createdby": "Created By",
"critical": "Critical",
@@ -1741,7 +1759,8 @@
"updatedat": "Updated At"
},
"labels": {
"newnoteplaceholder": "Add a note..."
"newnoteplaceholder": "Add a note...",
"notetoadd": "Note to Add"
},
"successes": {
"create": "Note created successfully.",
@@ -1770,6 +1789,7 @@
"ownr_fn": "First Name",
"ownr_ln": "Last Name",
"ownr_ph1": "Phone 1",
"ownr_ph2": "Phone 2",
"ownr_st": "Province/State",
"ownr_title": "Title",
"ownr_zip": "Zip/Postal Code",
@@ -1811,9 +1831,10 @@
"act_price": "Price",
"backordered_eta": "B.O. ETA",
"backordered_on": "B.O. On",
"comments": "Comments",
"cost": "Cost",
"db_price": "List Price",
"deliver_by": "Date",
"deliver_by": "Deliver By",
"job_line_id": "Job Line Id",
"line_desc": "Line Description",
"line_remarks": "Remarks",
@@ -1966,9 +1987,13 @@
"guarantee": "Repair Guarantee",
"invoice_customer_payable": "Invoice (Customer Payable)",
"invoice_total_payable": "Invoice (Total Payable)",
"iou_form": "IOU Form",
"job_costing_ro": "Job Costing",
"job_notes": "Job Notes",
"key_tag": "Key Tag",
"mpi_animal_checklist": "MPI - Animal Checklist",
"mpi_eglass_auth": "MPI - eGlass Auth",
"mpi_final_acct_sheet": "MPI - Final Accounting Sheet",
"paint_grid": "Paint Grid",
"parts_label_single": "Parts Label - Single",
"parts_list": "Parts List",
@@ -2020,7 +2045,7 @@
},
"subjects": {
"jobs": {
"parts_order": "$t(printcenter.jobs.parts_order) PO: {{ro_number}}"
"parts_order": "Parts Order PO: {{ro_number}}"
}
},
"vendors": {
@@ -2182,6 +2207,11 @@
"work_in_progress_payables": "Work in Progress - Payables"
}
},
"schedule": {
"labels": {
"manualevent": "Add Manual Event"
}
},
"scoreboard": {
"actions": {
"edit": "Edit"

View File

@@ -270,6 +270,7 @@
"md_categories": "",
"md_ccc_rates": "",
"md_classes": "",
"md_ded_notes": "",
"md_hour_split": {
"paint": "",
"prep": ""
@@ -396,6 +397,7 @@
"la2": "",
"la3": "",
"la4": "",
"laa": "",
"lab": "",
"lad": "",
"lae": "",
@@ -449,6 +451,7 @@
"status": "",
"statuses": {
"active_statuses": "",
"color": "",
"default_arrived": "",
"default_bo": "",
"default_canceled": "",
@@ -465,6 +468,7 @@
"open_statuses": "",
"post_production_statuses": "",
"pre_production_statuses": "",
"production_colors": "",
"production_statuses": ""
},
"target_touchtime": "",
@@ -498,6 +502,7 @@
},
"emaillater": "",
"employees": "",
"estimators": "",
"insurancecos": "",
"intakechecklist": "",
"jobstatuses": "",
@@ -1052,8 +1057,10 @@
},
"profitcenter_labor": "",
"profitcenter_part": "",
"prt_dsmk_m": "",
"prt_dsmk_p": "",
"status": "Estado",
"tax_part": "",
"total": "",
"unq_seq": "Seq #"
},
@@ -1086,6 +1093,7 @@
"autoallocate": "",
"changelaborrate": "",
"changestatus": "Cambiar Estado",
"changestimator": "",
"convert": "Convertir",
"deliver": "",
"dms": {
@@ -1196,9 +1204,11 @@
"date_estimated": "Fecha estimada",
"date_exported": "Exportado",
"date_invoiced": "Facturado",
"date_last_contacted": "",
"date_open": "Abierto",
"date_scheduled": "Programado",
"ded_amt": "Deducible",
"ded_note": "",
"ded_status": "Estado deducible",
"depreciation_taxes": "Depreciación / Impuestos",
"dms": {
@@ -1274,6 +1284,7 @@
"local_tax_rate": "",
"loss_date": "Fecha de pérdida",
"loss_desc": "",
"loss_of_use": "",
"ma2s": "",
"ma3s": "",
"mabl": "",
@@ -1287,6 +1298,7 @@
"owner_owing": "Cust. Debe",
"ownr_ea": "Email",
"ownr_ph1": "Teléfono 1",
"ownr_ph2": "",
"paa": "",
"pac": "",
"pae": "",
@@ -1335,6 +1347,8 @@
"rate_mapa": "Tasa de materiales de pintura",
"rate_mash": "Comprar material de tarifa",
"rate_matd": "Tasa de eliminación de neumáticos",
"referral_source_extra": "",
"referral_source_other": "",
"referralsource": "Fuente de referencia",
"regie_number": "N. ° de registro",
"repairtotal": "Reparación total",
@@ -1520,6 +1534,7 @@
"sale_labor": "",
"sale_parts": "",
"sales": "",
"savebeforeconversion": "",
"scheduledinchange": "",
"specialcoveragepolicy": "",
"state_tax_amt": "",
@@ -1733,6 +1748,9 @@
"edit": "Editar nota",
"new": "Nueva nota"
},
"errors": {
"inserting": ""
},
"fields": {
"createdby": "Creado por",
"critical": "Crítico",
@@ -1741,7 +1759,8 @@
"updatedat": "Actualizado en"
},
"labels": {
"newnoteplaceholder": "Agrega una nota..."
"newnoteplaceholder": "Agrega una nota...",
"notetoadd": ""
},
"successes": {
"create": "Nota creada con éxito.",
@@ -1770,6 +1789,7 @@
"ownr_fn": "Nombre de pila",
"ownr_ln": "Apellido",
"ownr_ph1": "Teléfono 1",
"ownr_ph2": "",
"ownr_st": "Provincia del estado",
"ownr_title": "Título",
"ownr_zip": "código postal",
@@ -1811,6 +1831,7 @@
"act_price": "",
"backordered_eta": "",
"backordered_on": "",
"comments": "",
"cost": "",
"db_price": "",
"deliver_by": "",
@@ -1966,9 +1987,13 @@
"guarantee": "",
"invoice_customer_payable": "",
"invoice_total_payable": "",
"iou_form": "",
"job_costing_ro": "",
"job_notes": "",
"key_tag": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
"paint_grid": "",
"parts_label_single": "",
"parts_list": "",
@@ -2182,6 +2207,11 @@
"work_in_progress_payables": ""
}
},
"schedule": {
"labels": {
"manualevent": ""
}
},
"scoreboard": {
"actions": {
"edit": ""

View File

@@ -270,6 +270,7 @@
"md_categories": "",
"md_ccc_rates": "",
"md_classes": "",
"md_ded_notes": "",
"md_hour_split": {
"paint": "",
"prep": ""
@@ -396,6 +397,7 @@
"la2": "",
"la3": "",
"la4": "",
"laa": "",
"lab": "",
"lad": "",
"lae": "",
@@ -449,6 +451,7 @@
"status": "",
"statuses": {
"active_statuses": "",
"color": "",
"default_arrived": "",
"default_bo": "",
"default_canceled": "",
@@ -465,6 +468,7 @@
"open_statuses": "",
"post_production_statuses": "",
"pre_production_statuses": "",
"production_colors": "",
"production_statuses": ""
},
"target_touchtime": "",
@@ -498,6 +502,7 @@
},
"emaillater": "",
"employees": "",
"estimators": "",
"insurancecos": "",
"intakechecklist": "",
"jobstatuses": "",
@@ -1052,8 +1057,10 @@
},
"profitcenter_labor": "",
"profitcenter_part": "",
"prt_dsmk_m": "",
"prt_dsmk_p": "",
"status": "Statut",
"tax_part": "",
"total": "",
"unq_seq": "Seq #"
},
@@ -1086,6 +1093,7 @@
"autoallocate": "",
"changelaborrate": "",
"changestatus": "Changer le statut",
"changestimator": "",
"convert": "Convertir",
"deliver": "",
"dms": {
@@ -1196,9 +1204,11 @@
"date_estimated": "Date estimée",
"date_exported": "Exportés",
"date_invoiced": "Facturé",
"date_last_contacted": "",
"date_open": "Ouvrir",
"date_scheduled": "Prévu",
"ded_amt": "Déductible",
"ded_note": "",
"ded_status": "Statut de franchise",
"depreciation_taxes": "Amortissement / taxes",
"dms": {
@@ -1274,6 +1284,7 @@
"local_tax_rate": "",
"loss_date": "Date de perte",
"loss_desc": "",
"loss_of_use": "",
"ma2s": "",
"ma3s": "",
"mabl": "",
@@ -1287,6 +1298,7 @@
"owner_owing": "Cust. Owes",
"ownr_ea": "Email",
"ownr_ph1": "Téléphone 1",
"ownr_ph2": "",
"paa": "",
"pac": "",
"pae": "",
@@ -1335,6 +1347,8 @@
"rate_mapa": "Taux de matériaux de peinture",
"rate_mash": "Tarif du matériel de la boutique",
"rate_matd": "Taux d'élimination des pneus",
"referral_source_extra": "",
"referral_source_other": "",
"referralsource": "Source de référence",
"regie_number": "Enregistrement #",
"repairtotal": "Réparation totale",
@@ -1520,6 +1534,7 @@
"sale_labor": "",
"sale_parts": "",
"sales": "",
"savebeforeconversion": "",
"scheduledinchange": "",
"specialcoveragepolicy": "",
"state_tax_amt": "",
@@ -1733,6 +1748,9 @@
"edit": "Note éditée",
"new": "Nouvelle note"
},
"errors": {
"inserting": ""
},
"fields": {
"createdby": "Créé par",
"critical": "Critique",
@@ -1741,7 +1759,8 @@
"updatedat": "Mis à jour à"
},
"labels": {
"newnoteplaceholder": "Ajouter une note..."
"newnoteplaceholder": "Ajouter une note...",
"notetoadd": ""
},
"successes": {
"create": "Remarque créée avec succès.",
@@ -1770,6 +1789,7 @@
"ownr_fn": "Prénom",
"ownr_ln": "Nom de famille",
"ownr_ph1": "Téléphone 1",
"ownr_ph2": "",
"ownr_st": "Etat / Province",
"ownr_title": "Titre",
"ownr_zip": "Zip / code postal",
@@ -1811,6 +1831,7 @@
"act_price": "",
"backordered_eta": "",
"backordered_on": "",
"comments": "",
"cost": "",
"db_price": "",
"deliver_by": "",
@@ -1966,9 +1987,13 @@
"guarantee": "",
"invoice_customer_payable": "",
"invoice_total_payable": "",
"iou_form": "",
"job_costing_ro": "",
"job_notes": "",
"key_tag": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
"paint_grid": "",
"parts_label_single": "",
"parts_list": "",
@@ -2182,6 +2207,11 @@
"work_in_progress_payables": ""
}
},
"schedule": {
"labels": {
"manualevent": ""
}
},
"scoreboard": {
"actions": {
"edit": ""

View File

@@ -3,7 +3,11 @@ import moment from "moment";
import React from "react";
export function DateFormatter(props) {
return props.children ? moment(props.children).format("MM/DD/YYYY") : null;
return props.children
? moment(props.children).format(
props.includeDay ? "ddd MM/DD/YYYY" : "MM/DD/YYYY"
)
: null;
}
export function DateTimeFormatter(props) {

View File

@@ -2,13 +2,13 @@ import { gql } from "@apollo/client";
import { notification } from "antd";
import axios from "axios";
import jsreport from "jsreport-browser-client-dist";
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 _ from "lodash";
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
jsreport.serverUrl = server;
@@ -144,6 +144,7 @@ export async function RenderTemplates(
// ...rootTemplate.templateObject.context,
headerpath: `/${bodyshop.imexshopid}/header.html`,
bodyshop: bodyshop,
offset: moment().utcOffset(),
},
};
@@ -195,35 +196,40 @@ export const GenerateDocuments = async (templates) => {
const fetchContextData = async (templateObject) => {
console.log("Fetching context data", templateObject);
const bodyshop = store.getState().user.bodyshop;
jsreport.headers["Authorization"] =
"Bearer " + (await auth.currentUser.getIdToken());
const folders = await axios.get(`${server}/odata/folders`);
const shopSpecificFolder = folders.data.value.find(
(f) => f.name === bodyshop.imexshopid
);
const jsReportQueries = await axios.get(
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`
);
let templateQueryToExecute,
useShopSpecificTemplate = false;
if (jsReportQueries.data.value.length === 0) {
//We have no query to execute. Just render the template.
} else if (jsReportQueries.data.value.length === 1) {
//We're using the default template. Get the query and execute.
templateQueryToExecute = atob(jsReportQueries.data.value[0].content);
} else if (jsReportQueries.data.value.length === 2) {
//There's a custom template. Use that query instead and execute. We find it because it has a parent folder.
templateQueryToExecute = atob(
jsReportQueries.data.value.filter((v) => !!v.folder)[0].content
);
useShopSpecificTemplate = true;
} else {
//We have too many queries to choose from. Throw an error.
alert(
"There are too many queries to choose from. Please ensure there are no conflicting keys."
);
throw new Error(
"There are too many queries to choose from. Please ensure there are no conflicting keys."
let templateQueryToExecute;
let useShopSpecificTemplate = false;
// let shopSpecificTemplate;
if (shopSpecificFolder) {
let shopSpecificTemplate = jsReportQueries.data.value.find(
(f) => f?.folder?.shortid === shopSpecificFolder.shortid
);
if (shopSpecificTemplate) {
useShopSpecificTemplate = true;
templateQueryToExecute = atob(shopSpecificTemplate.content);
}
}
if (!templateQueryToExecute) {
const generalTemplate = jsReportQueries.data.value.find((f) => !f.folder);
useShopSpecificTemplate = false;
templateQueryToExecute = atob(generalTemplate.content);
}
let contextData = {};
if (templateQueryToExecute) {
const { data } = await client.query({

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