Compare commits

...

391 Commits

Author SHA1 Message Date
Patrick Fic
5db43dd065 IO-70 Add basic functions of OEC. 2021-11-29 15:04:34 -08:00
Patrick Fic
53be0bbc1a IO-70 OEC WIP. 2021-11-29 08:57:44 -08:00
Patrick Fic
bfcc03850b IO-70 Add OEC Through Partner Call. 2021-11-23 00:30:11 -08:00
Patrick Fic
65402c1420 Clean up console log statements. 2021-11-22 23:20:13 -08:00
Patrick Fic
b462b2fa03 IO-1485 Auto CC on parts order. 2021-11-22 15:39:12 -08:00
Patrick Fic
0c26b90591 IO-1533 Resolve board rerender. 2021-11-21 21:59:33 -08:00
Patrick Fic
f8f2384c54 Merged in release/2021-11-19 (pull request #272)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-19 20:08:53 +00:00
Patrick Fic
ef18cf0718 IO-233 CDK Updates. 2021-11-19 10:50:38 -08:00
Patrick Fic
9d35fc85ad Merged in release/2021-11-19 (pull request #271)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 18:39:14 +00:00
Patrick Fic
e98f9763fd Resolve QB Payments Refund Issue. 2021-11-18 10:38:04 -08:00
Patrick Fic
95e5385cd1 Merged in release/2021-11-19 (pull request #270)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 17:19:57 +00:00
Patrick Fic
46abf01366 IO-1533 Resolve defaults check issue for production board. 2021-11-18 09:19:34 -08:00
Patrick Fic
87f06425e1 Merged in release/2021-11-19 (pull request #269)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 06:19:53 +00:00
Patrick Fic
45ab7543d5 Resolve CI Issues. 2021-11-17 22:18:09 -08:00
Patrick Fic
120e4fc94c Merged in release/2021-11-19 (pull request #268)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 06:17:09 +00:00
Patrick Fic
fed16efd10 IO-1533 Added check for null settings on kanban. 2021-11-17 22:16:43 -08:00
Patrick Fic
ea6277c586 Merged in release/2021-11-19 (pull request #267)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 06:08:21 +00:00
Patrick Fic
49bf461c36 IO-1533 Kanban Customizations 2021-11-17 22:07:50 -08:00
Patrick Fic
696781c857 IO-1533 Kanban Customizations 2021-11-17 21:47:05 -08:00
Patrick Fic
5540872f62 IO-1531 CC Contract add to production. 2021-11-17 20:18:21 -08:00
Patrick Fic
dac53a56c3 Merged in release/2021-11-19 (pull request #266)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-18 01:30:23 +00:00
Patrick Fic
69690f0184 IO-1533 Kanban Customizations 2021-11-17 15:29:04 -08:00
Patrick Fic
f11a4c93ac IO-233 CDK Updates 2021-11-17 14:44:07 -08:00
Patrick Fic
d74870812e Merged in release/2021-11-19 (pull request #265)
release/2021-11-19

Approved-by: Patrick Fic
2021-11-16 01:17:31 +00:00
Patrick Fic
9eed33b5f2 Resolve CI issues with missing imports. 2021-11-15 17:15:51 -08:00
Patrick Fic
55b7715e1c IO-1529 Added gateway templates. 2021-11-15 17:14:12 -08:00
Patrick Fic
c129b5ba8c IO-1533 Kanban Customization 2021-11-15 13:53:54 -08:00
Patrick Fic
efc2844d99 IO-1530 Email individual note. 2021-11-15 11:06:10 -08:00
Patrick Fic
ab38f85d38 Package updates. 2021-11-15 10:36:41 -08:00
Patrick Fic
81eccba393 Merged in release/2021-11-12 (pull request #263)
release/2021-11-12

Approved-by: Patrick Fic
2021-11-12 21:46:33 +00:00
Patrick Fic
b3b49fd4ca Email test fix & Shop Config validation for resp centers. 2021-11-12 13:46:01 -08:00
Patrick Fic
b7a08db4e7 Merged in release/2021-11-12 (pull request #262)
IO-117 PBS WIP

Approved-by: Patrick Fic
2021-11-11 02:13:28 +00:00
Patrick Fic
2d63c3d576 IO-1528 Autohouse Updates. 2021-11-10 18:06:24 -08:00
Patrick Fic
5f4a2392af Costmetic updates & limit of conversations in subscription. 2021-11-10 17:09:09 -08:00
Patrick Fic
f6fe8be7c4 IO-1491 Simplify production query to use ignore employee relationships and match locally. 2021-11-10 16:54:02 -08:00
Patrick Fic
89af6d23e8 IO-1513 Added next contact date. 2021-11-10 16:22:52 -08:00
Patrick Fic
7e1431d65e IO-117 WIP PBS 2021-11-10 15:30:07 -08:00
Patrick Fic
d50e845ba0 IO-117 PBS WIP 2021-11-10 08:18:29 -08:00
Patrick Fic
6796c35e5b Merged in release/2021-11-12 (pull request #260)
release/2021-11-12

Approved-by: Patrick Fic
2021-11-10 01:06:57 +00:00
Patrick Fic
4a68a10005 IO-1486 Add dropdown for file handler. 2021-11-09 17:05:20 -08:00
Patrick Fic
17088b3025 IO-1188 Add AB proof of loss. 2021-11-09 16:33:20 -08:00
Patrick Fic
4e3c659b6d IO-1497 Ability to delete parts orders. 2021-11-09 16:31:57 -08:00
Patrick Fic
6afcf82cc4 IO-1505 Resolve PST exempt exporting to QBO. 2021-11-09 16:04:16 -08:00
Patrick Fic
f4ddb40bde IO-117 PBS WIP 2021-11-09 13:56:52 -08:00
Patrick Fic
6017e2172e IO-117 PBS WIP 2021-11-09 10:38:07 -08:00
Patrick Fic
bef9e64bf8 Resolve associations permissions to allow adjustments of RBAC. 2021-11-08 11:23:37 -08:00
Patrick Fic
e8d95bdb68 IO-1508 Add # of jobs in production. 2021-11-08 11:12:56 -08:00
Patrick Fic
10fb7d9d96 IO-1499 Reconciliation to consider quantity. 2021-11-08 10:50:21 -08:00
Patrick Fic
2ec196e664 IO-1510 Missing report templates 2021-11-08 10:29:16 -08:00
Patrick Fic
1f38b98bfd Package Updates 2021-11-08 10:27:38 -08:00
Patrick Fic
a7c76386bc Merged in release/2021-11-01 (pull request #258)
release/2021-11-01

Approved-by: Patrick Fic
2021-11-01 16:00:52 +00:00
Patrick Fic
042b67c531 IO-1438 Resolve issue with texting 2021-11-01 08:57:30 -07:00
Patrick Fic
2e72ed3698 IO-1438 Resolve Texting Issue 2021-11-01 08:40:53 -07:00
Patrick Fic
bc01f46388 Merge branch 'release/2021-10-29' into test 2021-10-29 13:48:27 -07:00
Patrick Fic
74f791541f IO-256 Resolve no invoice on CM QBO export. 2021-10-29 13:48:17 -07:00
Patrick Fic
84c7fdba5a Merge branch 'release/2021-10-29' into test 2021-10-29 11:30:53 -07:00
Patrick Fic
630fa23f6c Resolve uneeded import CI error. 2021-10-29 11:30:43 -07:00
Patrick Fic
8f58f09c8c Merge branch 'release/2021-10-29' into test 2021-10-29 11:19:36 -07:00
Patrick Fic
a0922f2944 IO-1476 Add Hrs to schedule modal. 2021-10-29 11:18:17 -07:00
Patrick Fic
b8f001625b IO-1502 PST on Adjustments for QBO 2021-10-29 10:55:35 -07:00
Patrick Fic
53394efebf IO-1503 QBO Credit Memos 2021-10-28 09:56:59 -07:00
Patrick Fic
d8296e1d01 Merge branch 'release/2021-10-29' into test 2021-10-27 11:37:04 -07:00
Patrick Fic
2c00e5ee79 IO-256 Resolve QBO Aposstroph Issues 2021-10-27 11:36:40 -07:00
Patrick Fic
34e220dcad IO-233 Update Regex for CDK. 2021-10-27 10:46:06 -07:00
Patrick Fic
fa1fffd8b9 Merge branch 'release/2021-10-29' into test 2021-10-27 08:39:20 -07:00
Patrick Fic
67eb430ff9 Add missing query field to CDK. 2021-10-27 08:39:05 -07:00
Patrick Fic
ff53355b4a Merge branch 'release/2021-10-29' into test 2021-10-27 08:30:03 -07:00
Patrick Fic
331b2c517b Update CDK Country Inclusion. 2021-10-27 08:29:43 -07:00
Patrick Fic
05d9f20a66 Mark Other RO field as Read Only 2021-10-26 15:38:05 -07:00
Patrick Fic
9e3a2e920d Merge branch 'release/2021-10-29' into test 2021-10-26 13:10:37 -07:00
Patrick Fic
1511b87959 IO-1487 MPI Totals Adjustments 2021-10-26 13:10:07 -07:00
Patrick Fic
9c3e4b7b83 IO-1489 Resolve payables memo for QBO. 2021-10-25 17:25:29 -07:00
Patrick Fic
b718f49071 IO-256 Resolve QBO Payable Vendor Insert. 2021-10-25 17:05:21 -07:00
Patrick Fic
e7157119ae Merged in release/2021-10-29 (pull request #256)
Release/2021 10 29
2021-10-25 21:58:53 +00:00
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
8bb8eee384 Remove open replay tracker. 2021-09-29 14:38:40 -07:00
Patrick Fic
85d79a8d7f Remove extra console logs. 2021-09-29 12:03:21 -07: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
e73d082eab IO-1349 Resolve manual job creation. 2021-09-29 08:49:55 -07:00
Patrick Fic
2637538d9a IO-256 QBO Payables 2021-09-29 08:43:15 -07: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
eb58274f90 IO-1391 Scoreboard Improvements 2021-09-28 13:19:02 -07:00
Patrick Fic
aa410d6847 IO-1283 IO-1371 Updates to add to scoreboard button. 2021-09-28 11:45:12 -07:00
Patrick Fic
97f1be9d6f IO-1390 Resolve breaking import changes from Hasura. 2021-09-28 10:59:18 -07: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
8ec5bc049b Resolve Qb Export not separating for QBO. 2021-09-27 16:50:37 -07:00
Patrick Fic
1f2bec06ef IO-1381 Updates to mark up calculations process. 2021-09-27 16:34:11 -07:00
Patrick Fic
1cad57bec2 Merge branch 'release/2021-10-01' of https://bitbucket.org/snaptsoft/bodyshop into release/2021-10-01 2021-09-27 15:44:21 -07:00
Patrick Fic
10f3c01677 IO-1375 SGI Add Sublet Labor Lines Calculation 2021-09-27 15:44:17 -07:00
Patrick Fic
fc68b669db Add GST to PVRT on QBO. 2021-09-27 14:49:33 -07:00
Patrick Fic
6e22091b81 IO-1382 Add ownr_co_nm to queries that were missing it. 2021-09-27 11:38:48 -07:00
Patrick Fic
545db54c14 IO-653 IO-654 Add SGI documents & regions filtering on print center. 2021-09-27 10:36:12 -07:00
Patrick Fic
aa69fef9ba IO-1379 Updated CSI key for actions menu 2021-09-27 10:22:47 -07:00
Patrick Fic
1e30642d28 Package updates for Tracker. 2021-09-27 09:49:08 -07:00
Patrick Fic
c1dfba949e IO-1349 Update Hasura to 2.0.9. 2021-09-27 09:17:34 -07:00
Patrick Fic
dfa9592755 IO-256 Add PVRT handling for QBO. 2021-09-24 17:17:23 -07:00
Patrick Fic
8f2b1f0f78 IO-256 Export receivables for QBO 2021-09-24 16:43:57 -07:00
Patrick Fic
cf91ec14c0 Merge branch 'feature/qbo' into test 2021-09-24 14:33:07 -07: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
dfe0f99bea Merged in release/2021-09-24 (pull request #225)
Revert Node Version

Approved-by: Patrick Fic
2021-09-24 17:44:20 +00:00
Patrick Fic
591022c097 Revert Node Version 2021-09-24 10:43:28 -07: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
c76da54b93 IO-256 First succesful basic post of receivables. 2021-09-23 16:53:41 -07:00
Patrick Fic
186f6101ff IO-256 Begin QBO Changes for Individual code mapping on receivables. 2021-09-23 16:22:59 -07:00
Patrick Fic
e95cf0a026 Merge branch 'test' into feature/qbo 2021-09-23 11:48:56 -07:00
Patrick Fic
3eaa3a0189 Merged in release/2021-09-24 (pull request #223)
release/2021-09-24

Approved-by: Patrick Fic
2021-09-23 17:42:34 +00:00
Patrick Fic
0ba445aad2 IO-1228 Report Center grouping change. 2021-09-23 10:38:22 -07:00
Patrick Fic
637c718b05 IO-1227 Add sold status to filter for CC 2021-09-23 10:36:27 -07:00
Patrick Fic
a01019b1b3 IO-1366 Additional Audit Trail Functionality 2021-09-23 10:29:35 -07:00
Patrick Fic
f6e2393a40 Merged in release/2021-09-24 (pull request #222)
release/2021-09-24

Approved-by: Patrick Fic
2021-09-22 23:11:31 +00:00
Patrick Fic
715857a587 IO-1370 Remove filtering on Other Part Type 2021-09-22 08:20:25 -07:00
Patrick Fic
186ad2d7a2 IO-1365 Update Messaging Issues. 2021-09-21 17:33:39 -07:00
Patrick Fic
c64c49ab6e IO-1103 Include removed lines for bill line select. 2021-09-21 15:56:07 -07:00
Patrick Fic
3e067e2103 Merged in release/2021-09-24 (pull request #221)
release/2021-09-24

Approved-by: Patrick Fic
2021-09-21 16:13:56 +00:00
Patrick Fic
4cc1f77b14 IO-1373 Resolve rounding issue on time tickets. 2021-09-21 08:29:16 -07:00
Patrick Fic
334dd5fe94 Merged in release/2021-09-24 (pull request #220)
release/2021-09-24

Approved-by: Patrick Fic
2021-09-21 00:22:47 +00:00
Patrick Fic
d73f37cacc IO-952 add confirm to bill enter with discrepancy. 2021-09-20 17:22:13 -07:00
Patrick Fic
c6a7e3dcb1 IO-1299 Resolve tax ratds not saving on bill enter save and new 2021-09-20 17:11:45 -07:00
Patrick Fic
4bdc02d22c IO-1358 IO-1372 Add Timestamp to report center. 2021-09-20 16:53:51 -07:00
Patrick Fic
afa8e04008 IO-1308 Remove pagination on job lines. 2021-09-20 16:41:59 -07:00
Patrick Fic
990c1bb2bf IO-1272 Improve reconciliation table UI 2021-09-20 16:39:17 -07:00
Patrick Fic
a5a59c526c IO-1369 Update jobs query to include name. 2021-09-20 16:18:39 -07:00
Patrick Fic
2d5bef4b7b IO-1370 Add glass to parts filter list. 2021-09-20 16:12:12 -07:00
Patrick Fic
5893b1e3b3 IO-1335 Add Subject with RO to Parts Order Email 2021-09-20 14:17:18 -07:00
Patrick Fic
0af452b8a4 IO-1292 Vendor Discount Step. 2021-09-20 14:09:27 -07:00
Patrick Fic
3d82198c90 IO-1227 Sold Courtesy Car Status. 2021-09-20 14:09:09 -07:00
Patrick Fic
01bca360c7 IO-1228 IO-1282 RC Categories & Searching 2021-09-20 12:48:37 -07:00
Patrick Fic
59e994ac29 IO-1367 Open Replay Tracking Improvements 2021-09-20 12:16:37 -07:00
Patrick Fic
d5f3105341 IO-1254 Add PO Number 2021-09-20 12:02:37 -07:00
Patrick Fic
92920f69d4 IO-1191 Add CSI to reports. 2021-09-20 11:34:05 -07:00
Patrick Fic
61ac520192 IO-1364 Update profile changes for new firebase package. 2021-09-20 11:31:13 -07:00
Patrick Fic
45176cc2e2 Merged in feature/cdk-cert (pull request #219)
IO-233 Add refetch.
2021-09-17 17:22:56 +00:00
Patrick Fic
6aacce5a58 IO-233 Add refetch. 2021-09-17 10:22:20 -07:00
Patrick Fic
7a515c35d2 Merge branch 'test' into feature/qbo 2021-09-16 17:04:57 -07:00
Patrick Fic
80dc04669c Merged in feature/cdk-cert (pull request #218)
feature/cdk-cert

Approved-by: Patrick Fic
2021-09-16 23:11:17 +00:00
Patrick Fic
a702c87986 io-233 Remote ITC Settings 2021-09-16 16:08:32 -07:00
Patrick Fic
1942103985 IO-256 Start add QBO Payables 2021-09-15 16:14:01 -07:00
Patrick Fic
fd161fa9eb IO-233 Add missing MAPA/MASH/Discount calulcations. 2021-09-15 08:28:54 -07:00
Patrick Fic
44a18f4ace Merged in feature/qbo (pull request #217)
feature/qbo

Approved-by: Patrick Fic
2021-09-14 21:10:15 +00:00
Patrick Fic
3269c4f602 Merged in feature/cdk-cert (pull request #216)
feature/cdk-cert

Approved-by: Patrick Fic
2021-09-14 21:09:10 +00:00
Patrick Fic
43cbf0084a Merged in release/2021-09-17 (pull request #215)
release/2021-09-17

Approved-by: Patrick Fic
2021-09-14 21:03:40 +00:00
Patrick Fic
28b1356f76 Merge branch 'release/2021-09-17' into feature/cdk-cert 2021-09-14 14:03:11 -07:00
Patrick Fic
38456cb213 Merge branch 'release/2021-09-17' into feature/qbo 2021-09-14 14:02:18 -07:00
Patrick Fic
8c826eaaed Update to latest node. 2021-09-14 14:01:55 -07:00
Patrick Fic
f047556ab5 Merge branch 'release/2021-09-17' into feature/qbo 2021-09-14 13:59:06 -07:00
Patrick Fic
057b335e82 Merge branch 'release/2021-09-17' into feature/cdk-cert 2021-09-14 13:58:48 -07: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
30c7da2bf9 Merged in hotfix-2021-09-14 (pull request #213)
IO-43 Resolve issue when no vehicle associated.

Approved-by: Patrick Fic
2021-09-14 16:41:07 +00:00
Patrick Fic
82fb4fc74c IO-43 Resolve issue when no vehicle associated. 2021-09-14 09:40:08 -07:00
Patrick Fic
151f122b8c Package updates. 2021-09-14 09:12:58 -07:00
Patrick Fic
b198ca5051 IO-233 Revert ITC Calculations. 2021-09-13 14:56:54 -07:00
Patrick Fic
d9abe84949 Merge release & add time ticket calculations. 2021-09-13 14:47:09 -07: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
17f4d69e30 Merged in release/2021-09-10 (pull request #211)
Resolve translation.

Approved-by: Patrick Fic
2021-09-13 17:09:49 +00:00
Patrick Fic
77d3fc359d Resolve translation. 2021-09-13 10:09:26 -07:00
Patrick Fic
86151f3337 Merge branch 'release/2021-09-10' into feature/cdk-cert 2021-09-13 10:08:15 -07:00
Patrick Fic
ae4b5bca33 Merged in release/2021-09-10 (pull request #210)
Add missing translations.

Approved-by: Patrick Fic
2021-09-13 16:50:13 +00:00
Patrick Fic
eb120a264e Add missing translations. 2021-09-13 09:49:38 -07:00
Patrick Fic
7e3f496ea1 IO-233 CDK WIP customer changes. 2021-09-13 09:43:41 -07:00
Patrick Fic
4d33f16f13 Merged in release/2021-09-10 (pull request #209)
release/2021-09-10

Approved-by: Patrick Fic
2021-09-13 15:09:19 +00:00
Patrick Fic
d7ebefe7ab Resolve missing query. 2021-09-13 08:08:43 -07:00
Patrick Fic
8dc2197677 IO-1353 Add missing default resp. centers. 2021-09-10 15:12:00 -07: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
Patrick Fic
fe993cba73 IO-233 CDK Reformatting. First successful post. 2021-09-10 14:58:09 -07:00
Patrick Fic
ef6cdf07d8 IO-1352 Remove positive hrs check on receivables export. 2021-09-10 13:09:21 -07:00
Patrick Fic
cdbde6f5fa Merged in release/2021-09-10 (pull request #207)
IO-43 Updated related ROs approach.

Approved-by: Patrick Fic
2021-09-09 22:35:46 +00:00
Patrick Fic
4059aa9875 IO-43 Updated related ROs approach. 2021-09-09 15:35:24 -07:00
Patrick Fic
22e30ae5cb IO-233 WIP CDK 2021-09-09 15:18:43 -07:00
Patrick Fic
78c883743f Merged in release/2021-09-10 (pull request #206)
IO-1350 Audatex import issues.
2021-09-09 20:10:20 +00:00
Patrick Fic
e46307e715 IO-1350 Audatex import issues. 2021-09-09 13:09:05 -07:00
Patrick Fic
f3e078b481 IO-233 Additional CDK Posting 2021-09-08 15:11:24 -07:00
Patrick Fic
c03f54b3eb Merge branch 'release/2021-09-10' into feature/cdk-cert 2021-09-08 08:28:54 -07:00
Patrick Fic
bb0234fb7d Merge branch 'test' into feature/cdk-cert 2021-09-08 08:28:27 -07:00
Patrick Fic
03f25c8c0e Merged in release/2021-09-10 (pull request #205)
release/2021-09-10

Approved-by: Patrick Fic
2021-09-07 23:53:40 +00:00
Patrick Fic
8e5005daa0 IO-1345 IO-43 Related Job linking 2021-09-07 16:27:34 -07:00
Patrick Fic
259458eec3 IO-1343 Updated tax rate check on import. 2021-09-07 09:53:55 -07:00
Patrick Fic
59e57aa274 Package updates. 2021-09-07 09:12:35 -07:00
Patrick Fic
de33bcd72b IO-1342 Revert IO-554 and add validation on jobline upsert. 2021-09-07 09:12:31 -07:00
Patrick Fic
d72472ccc3 Merged in hotifx/2021-09-06 (pull request #203)
hotifx/2021-09-06

Approved-by: Patrick Fic
2021-09-06 22:50:14 +00:00
Patrick Fic
91efd170c8 Merged in hotifx/2021-09-06 (pull request #202)
IO-1346 Resolve delivery checklist freezing.

Approved-by: Patrick Fic
2021-09-06 22:43:24 +00:00
Patrick Fic
6f1ddd51fd IO-1346 Resolve delivery checklist freezing. 2021-09-06 15:42:25 -07:00
Patrick Fic
9f48a91a29 Merged in release/2021-09-03 (pull request #201)
Remove assist tracker again.

Approved-by: Patrick Fic
2021-09-03 20:01:07 +00:00
Patrick Fic
a7e2548e14 Merged in release/2021-09-03 (pull request #200)
Remove assist tracker again.

Approved-by: Patrick Fic
2021-09-03 20:00:43 +00:00
Patrick Fic
3294faaeaa Remove assist tracker again. 2021-09-03 13:00:02 -07:00
Patrick Fic
06480159e3 Merge branch 'release/2021-09-03' into feature/qbo 2021-09-03 11:24:42 -07:00
Patrick Fic
6ce06ed5c0 Merge branch 'release/2021-09-03' into feature/cdk-cert 2021-09-03 11:24:16 -07:00
Patrick Fic
71e535388a Merged in release/2021-09-03 (pull request #199)
Release/2021 09 03
2021-09-03 18:19:02 +00:00
Patrick Fic
dbd265d368 Merged in release/2021-09-03 (pull request #198)
IO-1342 Resolve negative parts discount for negatives.

Approved-by: Patrick Fic
2021-09-02 23:31:38 +00:00
Patrick Fic
c11f182f83 IO-1342 Resolve negative parts discount for negatives. 2021-09-02 16:25:07 -07:00
Patrick Fic
5f70cfd585 Merged in release/2021-09-03 (pull request #197)
release/2021-09-03

Approved-by: Patrick Fic
2021-09-02 22:50:50 +00:00
Patrick Fic
6e0675f28b IO-1342 Resolve negative parts discount crash on job totals. 2021-09-02 15:48:54 -07:00
Patrick Fic
96f45d2c80 IO-233 Begin Trans Headers 2021-09-02 15:30:03 -07:00
Patrick Fic
091e44f471 Merge branch 'release/2021-09-03' into feature/cdk-cert 2021-09-02 15:27:22 -07:00
Patrick Fic
00549d6a88 Revert "Remove assist tracker."
This reverts commit d940e0ee78.
2021-09-01 18:10:44 -07:00
Patrick Fic
d940e0ee78 Remove assist tracker. 2021-09-01 15:06:52 -07:00
Patrick Fic
39a38d46ee Merged in release/2021-09-03 (pull request #196)
release/2021-09-03

Approved-by: Patrick Fic
2021-09-01 21:14:34 +00:00
Patrick Fic
71435ed75a Added patch for peerjs. 2021-09-01 14:14:20 -07:00
Patrick Fic
e7ec408b98 Merged in release/2021-09-03 (pull request #195)
Openrelay tracking change.

Approved-by: Patrick Fic
2021-09-01 20:39:45 +00:00
Patrick Fic
9306064420 Openrelay tracking change. 2021-09-01 13:38:35 -07:00
Patrick Fic
37a16edfb4 IO-233 Updated translations. 2021-09-01 13:24:19 -07:00
Patrick Fic
9ad5b9547f Merged in release/2021-09-03 (pull request #194)
release/2021-09-03

Approved-by: Patrick Fic
2021-09-01 16:19:58 +00:00
Patrick Fic
47edb0bdf4 IO-1332 Resolve checklist prod note. 2021-09-01 09:19:11 -07:00
Patrick Fic
5db47e879c IO-1224 Adjust scoreboard Entry 2021-09-01 08:43:37 -07:00
Patrick Fic
54b46dd25e IO-256 WIP QBO. 2021-09-01 08:31:24 -07:00
Patrick Fic
4cb92c8508 Merged in release/2021-09-03 (pull request #193)
IO-1336 Resolve towing/storage missing on export.

Approved-by: Patrick Fic
2021-08-31 00:04:22 +00:00
Patrick Fic
cb76e2dcde IO-1336 Resolve issues with QB Export of towing. 2021-08-30 16:59:15 -07:00
Patrick Fic
901c64ed85 IO-1336 Resolve towing/storage missing on export. 2021-08-30 16:38:45 -07:00
Patrick Fic
f6f90d68fa Merged in release/2021-09-03 (pull request #192)
release/2021-09-03

Approved-by: Patrick Fic
2021-08-30 22:43:40 +00:00
Patrick Fic
05e295fcac IO-1334 Added shop info RBAC. 2021-08-30 15:43:10 -07:00
Patrick Fic
c97df6dc61 IO-1333 Resolve dashboard component name issue. 2021-08-30 15:34:19 -07:00
Patrick Fic
61406aafa6 IO-1302 Remove 2nd message on job close. 2021-08-30 15:28:22 -07:00
Patrick Fic
03210db711 IO-1332 Resolve production note save on intake. 2021-08-30 15:25:04 -07:00
Patrick Fic
b3a34c109a IO-539 Fix hanging confirmation. 2021-08-30 15:20:08 -07:00
Patrick Fic
81daad35d8 Package updates and firebase refactor to SDK 9. 2021-08-30 15:09:11 -07:00
Patrick Fic
529eb24d76 Merge branch 'feature/qbo' into release/2021-09-03 2021-08-30 12:57:38 -07:00
Patrick Fic
e81bf7b561 Merge in CDK Branches WIP. 2021-08-30 12:57:13 -07:00
Patrick Fic
7a35dc9b38 IO-233 Add Vehicle History 2021-08-30 12:54:43 -07:00
Patrick Fic
c72ef97b82 IO-256 Further work on QBO Receivables. 2021-08-30 10:50:56 -07:00
Patrick Fic
5284ee2ef9 IO-256 Authorization and Basic Calls 2021-08-27 15:42:32 -07:00
Patrick Fic
724c097d52 IO-256 QBO Authorization Flow. 2021-08-26 15:48:10 -07:00
Patrick Fic
3c3da178ba Merged in release/2021-08-27 (pull request #191)
Release/2021 08 27
2021-08-25 21:59:23 +00:00
Patrick Fic
db4e5d48af Merge branch 'feature/cdk-cert' into feature/qbo 2021-08-25 12:08:06 -07:00
Patrick Fic
a7cf081ed5 IO-233 Begin Wip Header Creation 2021-08-24 18:48:02 -07:00
Patrick Fic
db5b11f6d3 IO-233 Vehicle and Customer posting unit testing completed. 2021-08-24 18:14:41 -07:00
Patrick Fic
8d3d52485f Merged in release/2021-08-27 (pull request #190)
IO-1330 Resolve postal code on qb export.

Approved-by: Patrick Fic
2021-08-24 23:40:02 +00:00
Patrick Fic
971b518e8f Merged in release/2021-08-27 (pull request #189)
IO-539 Resolve tax import for SGI.

Approved-by: Patrick Fic
2021-08-24 23:35:15 +00:00
Patrick Fic
748f8f472d IO-233 Insert DMS Vehicle 2021-08-24 15:56:10 -07:00
Patrick Fic
db2b4739c2 IO-233 Add insert Job. 2021-08-24 08:05:24 -07:00
Patrick Fic
6c12e5cb03 IO-233 Export Job Refactor 2021-08-23 19:02:05 -07:00
Patrick Fic
140e57a123 Merged in release/2021-08-27 (pull request #188)
IO-1330 Update QB Information

Approved-by: Patrick Fic
2021-08-23 21:17:06 +00:00
Patrick Fic
3ca6791939 IO-233 Added vehicle search & selection on form. 2021-08-23 10:40:10 -07:00
Patrick Fic
7e145bdec7 Merged in release/2021-08-27 (pull request #187)
Remove tracker assist for CI.

Approved-by: Patrick Fic
2021-08-23 15:43:39 +00:00
Patrick Fic
c493f6e31e Merged in release/2021-08-27 (pull request #186)
Release/2021 08 27

Approved-by: Patrick Fic
2021-08-23 15:34:50 +00:00
Patrick Fic
8bfd0a1c16 IO-233 Insert DMS Vehicles 2021-08-23 08:30:30 -07:00
Patrick Fic
834966ae96 Merged in release/2021-08-20 (pull request #185)
Release/2021 08 20
2021-08-20 21:01:17 +00:00
Patrick Fic
e7d813c3f3 Merged in test (pull request #184)
Merge WIP items back into CDK CErt.
2021-08-20 16:37:44 +00:00
Patrick Fic
db64d9b69f IO-233 WIP CDK 2021-08-17 16:22:10 -07:00
Patrick Fic
d6fbf16272 Merged in feature/2021-08-13 (pull request #180)
Feature/2021 08 13
2021-08-13 19:09:00 +00:00
Patrick Fic
2f9d025fbe Merged in hotfix/2021-08-12 (pull request #178)
hotfix/2021-08-12

Approved-by: Patrick Fic
2021-08-12 20:43:16 +00:00
Patrick Fic
9c1ffaba17 Merged in hotfix/2021-08-12 (pull request #177)
hotfix/2021-08-12
2021-08-12 20:42:55 +00:00
563 changed files with 92891 additions and 51733 deletions

1
.gitignore vendored
View File

@@ -112,3 +112,4 @@ firebase/.env
.elasticbeanstalk/*
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
logs/oAuthClient-log.log

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

43861
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,87 +2,90 @@
"name": "bodyshop",
"version": "0.1.1",
"private": true,
"proxy": "http://localhost:5000",
"proxy": "http://localhost:4000",
"dependencies": {
"@apollo/client": "^3.3.21",
"@craco/craco": "^6.2.0",
"@fingerprintjs/fingerprintjs": "^3.2.0",
"@lourenci/react-kanban": "^2.1.0",
"@openreplay/tracker": "^3.2.1",
"@openreplay/tracker-assist": "^3.0.3",
"@apollo/client": "^3.4.17",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^6.4.0",
"@fingerprintjs/fingerprintjs": "^3.3.0",
"@openreplay/tracker": "^3.4.7",
"@openreplay/tracker-assist": "^3.4.4",
"@openreplay/tracker-graphql": "^3.0.0",
"@openreplay/tracker-redux": "^3.0.0",
"@sentry/react": "^6.10.0",
"@sentry/tracing": "^6.10.0",
"@stripe/react-stripe-js": "^1.4.0",
"@stripe/stripe-js": "^1.16.0",
"@tanem/react-nprogress": "^3.0.74",
"antd": "^4.16.8",
"@sentry/react": "^6.14.3",
"@sentry/tracing": "^6.14.3",
"@splitsoftware/splitio-react": "^1.3.0",
"@stripe/react-stripe-js": "^1.6.0",
"@stripe/stripe-js": "^1.21.1",
"@tanem/react-nprogress": "^3.0.82",
"antd": "^4.16.13",
"apollo-link-logger": "^2.0.0",
"axios": "^0.21.1",
"craco-less": "^1.18.0",
"dinero.js": "^1.9.0",
"axios": "^0.24.0",
"craco-less": "^1.20.0",
"dinero.js": "^1.9.1",
"dotenv": "^10.0.0",
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.2",
"firebase": "^8.7.1",
"graphql": "^15.5.1",
"i18next": "^20.3.4",
"exifr": "^7.1.3",
"firebase": "^9.4.1",
"graphql": "^16.0.1",
"i18next": "^21.4.2",
"i18next-browser-languagedetector": "^6.1.2",
"jsoneditor": "^9.5.2",
"jsoneditor": "^9.5.7",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.22",
"logrocket": "^1.2.0",
"markerjs2": "^2.9.0",
"libphonenumber-js": "^1.9.42",
"logrocket": "^2.1.1",
"markerjs2": "^2.17.0",
"moment-business-days": "^1.2.0",
"phone": "^3.1.2",
"phone": "^3.1.9",
"preval.macro": "^5.0.0",
"prop-types": "^15.7.2",
"query-string": "^7.0.1",
"rc-queue-anim": "^2.0.0",
"rc-scroll-anim": "^2.7.6",
"react": "^17.0.1",
"react-big-calendar": "^0.33.2",
"react-big-calendar": "^0.38.1",
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.1",
"react-drag-listview": "^0.1.8",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.2.5",
"react-i18next": "^11.11.3",
"react-icons": "^4.2.0",
"react-number-format": "^4.6.4",
"react-redux": "^7.2.4",
"react-grid-layout": "^1.3.0",
"react-i18next": "^11.14.2",
"react-icons": "^4.3.1",
"react-number-format": "^4.8.0",
"react-redux": "^7.2.6",
"react-resizable": "^3.0.4",
"react-router-dom": "^5.2.0",
"react-router-dom": "^5.3.0",
"react-scripts": "^4.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.0.10",
"redux": "^4.1.0",
"recharts": "^2.1.6",
"redux": "^4.1.2",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.0.0",
"sass": "^1.35.2",
"socket.io-client": "^4.1.3",
"styled-components": "^5.3.0",
"subscriptions-transport-ws": "^0.9.18",
"web-vitals": "^2.1.0",
"workbox-background-sync": "^6.1.5",
"workbox-broadcast-update": "^6.1.5",
"workbox-cacheable-response": "^6.1.5",
"workbox-core": "^6.1.5",
"workbox-expiration": "^6.1.5",
"workbox-google-analytics": "^6.1.5",
"workbox-navigation-preload": "^6.1.5",
"workbox-precaching": "^6.1.5",
"workbox-range-requests": "^6.1.5",
"workbox-routing": "^6.1.5",
"workbox-strategies": "^6.1.5",
"workbox-streams": "^6.1.5"
"reselect": "^4.1.2",
"sass": "^1.43.4",
"socket.io-client": "^4.3.2",
"styled-components": "^5.3.3",
"subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.2",
"workbox-background-sync": "^6.3.0",
"workbox-broadcast-update": "^6.3.0",
"workbox-cacheable-response": "^6.3.0",
"workbox-core": "^6.3.0",
"workbox-expiration": "^6.3.0",
"workbox-google-analytics": "^6.3.0",
"workbox-navigation-preload": "^6.3.0",
"workbox-precaching": "^6.3.0",
"workbox-range-requests": "^6.3.0",
"workbox-routing": "^6.3.0",
"workbox-strategies": "^6.3.0",
"workbox-streams": "^6.3.0"
},
"scripts": {
"postinstall": "patch-package",
"analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "craco start",
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
@@ -112,7 +115,8 @@
]
},
"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"
}

File diff suppressed because one or more lines are too long

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,32 +10,40 @@ 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 trackerAssist from "@openreplay/tracker-assist";
import { getCurrentUser } from "../firebase/firebase.utils";
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";
moment.locale("en-US");
export const tracker = new Tracker({
projectKey: "FPjeYIbwJyvhrVVTTLHo",
ingestPoint: "https://replay.bodyshop.app/ingest",
// ...(process.env.NODE_ENV === null || process.env.NODE_ENV === "development"
// ? { __DISABLE_SECURE_MODE: true }
// : {}),
projectKey: "trDmOZlEXUpjGsMtHroA",
ingestPoint: "https://replay.imex.online/ingest",
...(process.env.NODE_ENV === null || process.env.NODE_ENV === "development"
? { __DISABLE_SECURE_MODE: true }
: {}),
// beaconSize: 10485760,
onStart: ({ sessionID }) =>
console.log(
"******** OpenReplay tracker started with session: ",
sessionID
),
onStart: async ({ sessionID }) => {
const user = await getCurrentUser();
if (user) tracker.setUserID(user.email);
},
});
//tracker.use(trackerAssist({ confirmText: "Confimr hep" })); // check the list of available options below
// tracker.use(
// trackerAssist({ confirmText: "Technical support is about to assist you." })
// ); // check the list of available options below
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();
@@ -49,7 +61,9 @@ export default function AppContainer() {
}}
>
<GlobalLoadingBar />
<App />
<SplitFactory factory={factory}>
<App />
</SplitFactory>
</ConfigProvider>
</ApolloProvider>
);

View File

@@ -53,7 +53,6 @@ export function App({ checkUserSession, currentUser, online, setOnline }) {
const { t } = useTranslation();
window.addEventListener("offline", function (e) {
console.log("Internet connection lost.");
setOnline(false);
});

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;
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -1,25 +1,9 @@
import Axios from "axios";
import React from "react";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
export default function Test() {
const handleQbSignIn = async () => {
const result = await Axios.post("/qbo/authorize", { userId: "1234" });
console.log("handleQbSignIn -> result", result.data);
// window.open(result.data, "_blank", "toolbar=0,location=0,menubar=0");
var parameters = "location=1,width=800,height=650";
parameters +=
",left=" +
(window.screen.width - 800) / 2 +
",top=" +
(window.screen.height - 650) / 2;
// Launch Popup
window.open(result.data, "connectPopup", parameters);
};
return (
<div>
<button onClick={handleQbSignIn}>Sign Into Qb.</button>
<QboAuthorizeComponent />
</div>
);
}

View File

@@ -9,8 +9,25 @@ import PayableExportAll from "../payable-export-all-button/payable-export-all-bu
import { DateFormatter } from "../../utils/DateFormatter";
import queryString from "query-string";
import { logImEXEvent } from "../../firebase/firebase.utils";
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({ loading, bills }) {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingPayablesTableComponent);
export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
const { t } = useTranslation();
const [selectedBills, setSelectedBills] = useState([]);
const [transInProgress, setTransInProgress] = useState(false);
@@ -166,6 +183,9 @@ export default function AccountingPayablesTableComponent({ loading, bills }) {
loadingCallback={setTransInProgress}
completedCallback={setSelectedBills}
/>
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />
)}
<Input
value={state.search}
onChange={handleSearch}

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

@@ -8,7 +8,26 @@ import { alphaSort } from "../../utils/sorters";
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
export default function AccountingReceivablesTableComponent({ loading, jobs }) {
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(AccountingReceivablesTableComponent);
export function AccountingReceivablesTableComponent({
bodyshop,
loading,
jobs,
}) {
const { t } = useTranslation();
const [selectedJobs, setSelectedJobs] = useState([]);
const [transInProgress, setTransInProgress] = useState(false);
@@ -180,12 +199,17 @@ export default function AccountingReceivablesTableComponent({ loading, jobs }) {
<Card
extra={
<Space wrap>
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
/>
{!bodyshop.cdk_dealerid && !bodyshop.pbs_serialnumber && (
<JobsExportAllButton
jobIds={selectedJobs}
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
/>
)}
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />
)}
<Input.Search
value={state.search}
onChange={handleSearch}

View File

@@ -18,8 +18,10 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import BillFormContainer from "../bill-form/bill-form.container";
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
import { handleUpload } from "../documents-upload/documents-upload.utility";
const mapStateToProps = createStructuredSelector({
@@ -48,7 +50,31 @@ function BillEnterModalContainer({
const [loading, setLoading] = useState(false);
const client = useApolloClient();
const formValues = useMemo(() => {
return {
...billEnterModal.context.bill,
jobid:
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
federal_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.federal_tax_rate) ||
0,
state_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.state_tax_rate) ||
0,
local_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.local_tax_rate) ||
0,
};
}, [billEnterModal, bodyshop]);
const handleFinish = async (values) => {
let totals = CalculateBillTotal(values);
if (totals.discrepancy.getAmount() !== 0) {
if (!(await confirmDialog(t("bills.labels.savewithdiscrepancy")))) {
return;
}
}
setLoading(true);
const { upload, location, ...remainingValues } = values;
@@ -190,7 +216,7 @@ function BillEnterModalContainer({
if (enterAgain) {
form.resetFields();
form.setFieldsValue({ billlines: [] });
form.setFieldsValue({ ...form.getFieldsValue(), billlines: [] });
} else {
toggleModalVisible();
}
@@ -208,23 +234,6 @@ function BillEnterModalContainer({
if (enterAgain) form.submit();
}, [enterAgain, form]);
const formValues = useMemo(() => {
return {
...billEnterModal.context.bill,
jobid:
(billEnterModal.context.job && billEnterModal.context.job.id) || null,
federal_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.federal_tax_rate) ||
0,
state_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.state_tax_rate) ||
0,
local_tax_rate:
(bodyshop.bill_tax_rates && bodyshop.bill_tax_rates.local_tax_rate) ||
0,
};
}, [billEnterModal, bodyshop]);
useEffect(() => {
if (billEnterModal.visible) {
form.setFieldsValue(formValues);

View File

@@ -156,18 +156,7 @@ export function BillEnterModalLinesComponent({
setFieldsValue({
billlines: getFieldsValue("billlines").billlines.map(
(item, idx) => {
console.log("Checking", index, idx);
if (idx === index) {
console.log(
"Found and setting.",
!!item.actual_cost
? item.actual_cost
: Math.round(
(parseFloat(e.target.value) * (1 - discount) +
Number.EPSILON) *
100
) / 100
);
return {
...item,
actual_cost: !!item.actual_cost
@@ -502,9 +491,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 +505,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) => {
return (
(option.line_desc &&
option.line_desc
.toLowerCase()
.includes(inputValue.toLowerCase())) ||
(option.oem_partno &&
option.oem_partno.toLowerCase().includes(inputValue.toLowerCase()))
);
}}
notFoundContent={"Removed."}
{...restProps}
>
<Select.Option key={null} value={"noline"} cost={0} line_desc={""}>
@@ -21,14 +32,19 @@ const BillLineSearchSelect = ({ options, disabled, ...restProps }, ref) => {
{options
? options.map((item) => (
<Option
disabled={item.removed}
key={item.id}
value={item.id}
cost={item.act_price ? item.act_price : 0}
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" } : {}),
}}
>
{`${item.line_desc}${
{`${item.removed ? `(REMOVED) ` : ""}${item.line_desc}${
item.oem_partno ? ` - ${item.oem_partno}` : ""
}`}
</Option>

View File

@@ -8,6 +8,7 @@ import { selectSelectedConversation } from "../../redux/messaging/messaging.sele
import PhoneFormatter from "../../utils/PhoneFormatter";
import "./chat-conversation-list.styles.scss";
import { useTranslation } from "react-i18next";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
@@ -60,13 +61,18 @@ export function ChatConversationListComponent({
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx} className="ro-number-tag">
{j.job.ro_number}
</Tag>
))
: null}
<div sryle={{ display: "inline-block" }}>
<div>
{item.job_conversations.length > 0
? item.job_conversations.map((j, idx) => (
<Tag key={idx} className="ro-number-tag">
{j.job.ro_number}
</Tag>
))
: null}
</div>
<TimeAgoFormatter>{item.updated_at}</TimeAgoFormatter>
</div>
<Badge count={item.messages_aggregate.aggregate.count || 0} />
</List.Item>
)}

View File

@@ -1,4 +1,5 @@
import Icon from "@ant-design/icons";
import { Tooltip } from "antd";
import i18n from "i18next";
import moment from "moment";
import React, { useEffect, useRef } from "react";
@@ -9,6 +10,7 @@ import {
CellMeasurerCache,
List,
} from "react-virtualized";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import "./chat-message-list.styles.scss";
export default function ChatMessageListComponent({ messages }) {
@@ -85,17 +87,22 @@ export default function ChatMessageListComponent({ messages }) {
const MessageRender = (message) => {
return (
<div>
{message.image_path &&
message.image_path.map((i, idx) => (
<div key={idx} style={{ display: "flex", justifyContent: "center" }}>
<a href={i} target="__blank">
<img alt="Received" className="message-img" src={i} />
</a>
</div>
))}
<div>{message.text}</div>
</div>
<Tooltip title={DateTimeFormatter({ children: message.created_at })}>
<div>
{message.image_path &&
message.image_path.map((i, idx) => (
<div
key={idx}
style={{ display: "flex", justifyContent: "center" }}
>
<a href={i} target="__blank">
<img alt="Received" className="message-img" src={i} />
</a>
</div>
))}
<div>{message.text}</div>
</div>
</Tooltip>
);
};

View File

@@ -20,7 +20,6 @@ export function ChatNewConversation({ openChatByPhone }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const handleFinish = (values) => {
console.log("values :>> ", values);
openChatByPhone({ phone_num: values.phoneNumber });
form.resetFields();
};

View File

@@ -1,5 +1,5 @@
import { ShrinkOutlined } from "@ant-design/icons";
import { Col, Row, Typography } from "antd";
import { ShrinkOutlined, InfoCircleOutlined } from "@ant-design/icons";
import { Col, Row, Tooltip, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -31,6 +31,9 @@ export function ChatPopupComponent({
{t("messaging.labels.messaging")}
</Typography.Title>
<ChatNewConversation />
<Tooltip title={t("messaging.labels.recentonly")}>
<InfoCircleOutlined />
</Tooltip>
</div>
<ShrinkOutlined
onClick={() => toggleChatVisible()}

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

@@ -42,7 +42,6 @@ export default function ContractFormComponent({
<ContractStatusSelector />
</Form.Item>
)}
<Form.Item
label={t("contracts.fields.start")}
name="start"

View File

@@ -31,6 +31,9 @@ const CourtesyCarStatusComponent = ({ value, onChange }, ref) => {
<Option value="courtesycars.status.out">
{t("courtesycars.status.out")}
</Option>
<Option value="courtesycars.status.sold">
{t("courtesycars.status.sold")}
</Option>
</Select>
);
};

View File

@@ -45,6 +45,10 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
text: t("courtesycars.status.out"),
value: "courtesycars.status.out",
},
{
text: t("courtesycars.status.sold"),
value: "courtesycars.status.sold",
},
],
onFilter: (value, record) => value.includes(record.status),
sortOrder:

View File

@@ -86,10 +86,7 @@ export function DashboardGridComponent({ currentUser, bodyshop }) {
const handleRemoveComponent = (key) => {
logImEXEvent("dashboard_remove_component", { name: key });
const idxToRemove = state.items.findIndex((i) => i.i === key);
console.log(
"🚀 ~ file: dashboard-grid.component.jsx ~ line 81 ~ idxToRemove",
idxToRemove
);
const items = _.cloneDeep(state.items);
items.splice(idxToRemove, 1);
@@ -244,7 +241,7 @@ const componentList = {
h: 3,
},
MonthlyPartsSales: {
label: i18next.t("dashboard.titles.productiondollars"),
label: i18next.t("dashboard.titles.monthlypartssales"),
component: DashboardMonthlyPartsSales,
gqlFragment: null,
minW: 2,
@@ -253,7 +250,7 @@ const componentList = {
h: 2,
},
MonthlyLaborSales: {
label: i18next.t("dashboard.titles.monthlypartssales"),
label: i18next.t("dashboard.titles.monthlylaborsales"),
component: DashboardMonthlyLaborSales,
gqlFragment: null,
minW: 2,

View File

@@ -1,25 +1,39 @@
import { Button, Table } from "antd";
import React, { useState } from "react";
import { Button, Card, Table, Typography } from "antd";
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import Dinero from "dinero.js";
import { SyncOutlined } from "@ant-design/icons";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DmsAllocationsSummary);
export function DmsAllocationsSummary({ socket, bodyshop, jobId }) {
export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
const { t } = useTranslation();
const [allocationsSummary, setAllocationsSummary] = useState([]);
useEffect(() => {
if (socket.connected) {
socket.emit("cdk-calculate-allocations", jobId, (ack) => {
setAllocationsSummary(ack);
socket.allocationsSummary = ack;
});
}
}, [socket, socket.connected, jobId]);
const columns = [
{
title: t("jobs.fields.dms.center"),
@@ -62,8 +76,9 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId }) {
];
return (
<Table
title={() => (
<Card
title={title}
extra={
<Button
onClick={() => {
socket.emit("cdk-calculate-allocations", jobId, (ack) =>
@@ -71,13 +86,50 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId }) {
);
}}
>
Get
<SyncOutlined />
</Button>
)}
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
rowKey="center"
dataSource={allocationsSummary}
/>
}
>
<Table
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
rowKey="center"
dataSource={allocationsSummary}
summary={() => {
const totals = allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
return (
<Table.Summary.Row>
<Table.Summary.Cell>
<Typography.Title level={4}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell>
{totals.totalSale.toFormat()}
</Table.Summary.Cell>
<Table.Summary.Cell>
{
// totals.totalCost.toFormat()
}
</Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
</Table.Summary.Row>
);
}}
/>
</Card>
);
}

View File

@@ -1,9 +1,12 @@
import { useLazyQuery } from "@apollo/client";
import { Button, Input, Modal, Table } from "antd";
import React, { useState } from "react";
import { Modal, Button, Table, Input } from "antd";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { SEARCH_DMS_VEHICLES } from "../../graphql/dms.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useTranslation } from "react-i18next";
import AlertComponent from "../alert/alert.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -12,83 +15,71 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkMakes);
export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkVehicles);
export function DmsCdkMakes({ bodyshop, form, socket }) {
const [makesList, setMakesList] = useState([]);
const [searchText, setSearchText] = useState("");
const [loading, setLoading] = useState(false);
export function DmsCdkVehicles({ bodyshop, form, socket, job }) {
const [visible, setVisible] = useState(false);
const [selectedModel, setSelectedModel] = useState(null);
const { t } = useTranslation();
const [callSearch, { loading, error, data }] =
useLazyQuery(SEARCH_DMS_VEHICLES);
const columns = [
{
title: t("jobs.fields.dms.makeFullName"),
dataIndex: "makeFullName",
key: "makeFullName",
title: t("vehicles.fields.v_make_desc"),
dataIndex: "make",
key: "make",
},
{
title: t("jobs.fields.dms.modelFullName"),
dataIndex: "modelFullName",
key: "modelFullName",
title: t("vehicles.fields.v_model_desc"),
dataIndex: "model",
key: "model",
},
{
title: t("jobs.fields.dms.makeCode"),
dataIndex: "makeCode",
key: "makeCode",
title: t("jobs.fields.dms.dms_make"),
dataIndex: "makecode",
key: "makecode",
},
{
title: t("jobs.fields.dms.modelCode"),
dataIndex: "modelCode",
key: "modelCode",
title: t("jobs.fields.dms.dms_model"),
dataIndex: "modelcode",
key: "modelcode",
},
];
const filteredMakes =
searchText !== "" && searchText
? makesList.filter(
(make) =>
searchText
.split(" ")
.some((v) =>
make.makeFullName.toLowerCase().includes(v.toLowerCase())
) ||
searchText
.split(" ")
.some((v) =>
make.modelFullName.toLowerCase().includes(v.toLowerCase())
)
)
: makesList;
return (
<div>
<Modal width={"90%"} visible={visible} onCancel={() => setVisible(false)}>
<Modal
width={"90%"}
visible={visible}
onCancel={() => setVisible(false)}
onOk={() => {
form.setFieldsValue({
dms_make: selectedModel.makecode,
dms_model: selectedModel.modelcode,
});
setVisible(false);
}}
>
{error && <AlertComponent error={error.message} />}
<Table
title={() => (
<Input.Search
onSearch={(val) => setSearchText(val)}
onSearch={(val) => callSearch({ variables: { search: val } })}
placeholder={t("general.labels.search")}
/>
)}
columns={columns}
loading={loading}
id="id"
dataSource={filteredMakes}
rowKey="id"
dataSource={data ? data.search_dms_vehicles : []}
onRow={(record) => {
return {
onClick: setSelectedModel(record),
onClick: () => setSelectedModel(record),
};
}}
rowSelection={{
onSelect: (record, selected, ...props) => {
console.log(
"🚀 ~ file: dms-cdk-makes.component.jsx ~ line 85 ~ record, selected, ...props",
record,
selected,
...props
);
onSelect: (record) => {
setSelectedModel(record);
},
@@ -100,15 +91,14 @@ export function DmsCdkMakes({ bodyshop, form, socket }) {
<Button
onClick={() => {
setVisible(true);
setLoading(true);
socket.emit("cdk-get-makes", bodyshop.cdk_dealerid, (makes) => {
console.log("Called back", makes);
setMakesList(makes);
setLoading(false);
callSearch({
variables: {
search: job && job.v_model_desc && job.v_model_desc.substr(0, 3),
},
});
}}
>
Get Makes
{t("jobs.actions.dms.findmakemodelcode")}
</Button>
</div>
);

View File

@@ -0,0 +1,34 @@
import { Button } from "antd";
import axios from "axios";
import React, { useState } 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({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsCdkMakesRefetch);
export function DmsCdkMakesRefetch({ bodyshop, form, socket }) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const handleRefetch = async () => {
setLoading(true);
await axios.post("/cdk/getvehicles", {
cdk_dealerid: bodyshop.cdk_dealerid,
bodyshopid: bodyshop.id,
});
setLoading(false);
};
return (
<Button loading={loading} onClick={handleRefetch}>
{t("jobs.actions.dms.refetchmakesmodels")}
</Button>
);
}

View File

@@ -1,77 +1,169 @@
import { Button, Table } from "antd";
import { Button, Table, Col, Checkbox } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { socket } from "../../pages/dms/dms.container";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { alphaSort } from "../../utils/sorters";
export default function DmsCustomerSelector() {
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DmsCustomerSelector);
export function DmsCustomerSelector({ bodyshop }) {
const { t } = useTranslation();
const [customerList, setcustomerList] = useState([]);
const [visible, setVisible] = useState(false);
const [selectedCustomer, setSelectedCustomer] = useState(null);
const [dmsType, setDmsType] = useState("cdk");
socket.on("cdk-select-customer", (customerList, callback) => {
setVisible(true);
setDmsType("cdk");
setcustomerList(customerList);
});
socket.on("pbs-select-customer", (customerList, callback) => {
setVisible(true);
setDmsType("pbs");
setcustomerList(customerList);
console.log(
"🚀 ~ file: dms-customer-selector.component.jsx ~ line 37 ~ socket.on ~ customerList",
customerList
);
});
const onOk = () => {
const onUseSelected = () => {
setVisible(false);
socket.emit("cdk-selected-customer", selectedCustomer);
socket.emit(`${dmsType}-selected-customer`, selectedCustomer);
setSelectedCustomer(null);
};
const columns = [
const onUseGeneric = () => {
setVisible(false);
socket.emit(
"cdk-selected-customer",
bodyshop.cdk_configuration.generic_customer_number
);
setSelectedCustomer(null);
};
const onCreateNew = () => {
setVisible(false);
socket.emit("cdk-selected-customer", null);
setSelectedCustomer(null);
};
const cdkColumns = [
{
title: t("dms.fields.name1"),
title: t("jobs.fields.dms.id"),
dataIndex: ["id", "value"],
key: "id",
},
{
title: t("jobs.fields.dms.vinowner"),
dataIndex: "vinOwner",
key: "vinOwner",
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />,
},
{
title: t("jobs.fields.dms.name1"),
dataIndex: ["name1", "fullName"],
key: "name1",
sorter: (a, b) => alphaSort(a.name1?.fullName, b.name1?.fullName),
sorter: (a, b) =>
alphaSort(a.name1 && a.name1.fullName, b.name1 && b.name1.fullName),
},
{
title: t("dms.fields.name2"),
dataIndex: ["name2", "fullName"],
key: "name2",
sorter: (a, b) => alphaSort(a.name2?.fullName, b.name2?.fullName),
},
{
title: t("dms.fields.phone"),
dataIndex: ["contactInfo", "mainTelephoneNumber", "value"],
key: "phone",
render: (record, value) => (
<PhoneFormatter>
{record.contactInfo?.mainTelephoneNumber?.value}
</PhoneFormatter>
),
},
{
title: t("dms.fields.address"),
title: t("jobs.fields.dms.address"),
//dataIndex: ["name2", "fullName"],
key: "address",
render: (record, value) =>
`${record.address?.addressLine[0]}, ${record.address?.city} ${record.address?.stateOrProvince} ${record.address?.postalCode}`,
`${record.address && record.address.addressLine[0]}, ${
record.address && record.address.city
} ${record.address && record.address.stateOrProvince} ${
record.address && record.address.postalCode
}`,
},
];
if (!visible) return <></>;
const pbsColumns = [
{
title: t("jobs.fields.dms.id"),
dataIndex: "ContactId",
key: "ContactId",
},
{
title: t("jobs.fields.dms.vinowner"),
dataIndex: "vinOwner",
key: "vinOwner",
render: (text, record) => <Checkbox disabled checked={record.vinOwner} />,
},
{
title: t("jobs.fields.dms.name1"),
key: "name1",
sorter: (a, b) => alphaSort(a.LastName, b.LastName),
render: (text, record) =>
`${record.FirstName || ""} ${record.LastName || ""}`,
},
{
title: t("jobs.fields.dms.address"),
key: "address",
render: (record, value) =>
`${record.Address}, ${record.City} ${record.State} ${record.ZipCode}`,
},
];
if (!visible) return null;
return (
<Table
title={() => (
<div>
<Button onClick={onOk}>Select</Button>
</div>
)}
pagination={{ position: "top" }}
columns={columns}
rowKey={(record) => record.id.value}
dataSource={customerList}
//onChange={handleTableChange}
rowSelection={{
onSelect: (props) => {
setSelectedCustomer(props.id.value);
},
type: "radio",
selectedRowKeys: [selectedCustomer],
}}
/>
<Col span={24}>
<Table
title={() => (
<div>
<Button onClick={onUseSelected} disabled={!selectedCustomer}>
{t("jobs.actions.dms.useselected")}
</Button>
<Button
onClick={onUseGeneric}
disabled={
!(
bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.generic_customer_number
)
}
>
{t("jobs.actions.dms.usegeneric")}
</Button>
<Button onClick={onCreateNew}>
{t("jobs.actions.dms.createnewcustomer")}
</Button>
</div>
)}
pagination={{ position: "top" }}
columns={dmsType === "cdk" ? cdkColumns : pbsColumns}
rowKey={(record) =>
dmsType === "cdk" ? record.id.value : record.ContactId
}
dataSource={customerList}
//onChange={handleTableChange}
rowSelection={{
onSelect: (record) => {
setSelectedCustomer(
dmsType === "cdk" ? record.id.value : record.ContactId
);
},
type: "radio",
selectedRowKeys: [selectedCustomer],
}}
/>
</Col>
);
}

View File

@@ -27,7 +27,7 @@ export function DmsLogEvents({ socket, logs, bodyshop }) {
<Timeline.Item key={idx} color={LogLevelHierarchy(log.level)}>
<Space wrap align="start" style={{}}>
<Tag color={LogLevelHierarchy(log.level)}>{log.level}</Tag>
<span>{moment(log.timestamp).format("MM/DD/YYYY HH:MM:ss")}</span>
<span>{moment(log.timestamp).format("MM/DD/YYYY HH:mm:ss")}</span>
<Divider type="vertical" />
<span>{log.message}</span>
</Space>

View File

@@ -1,13 +1,27 @@
import { DeleteFilled } from "@ant-design/icons";
import { Button, Form, Input } from "antd";
import {
Button,
Card,
Divider,
Form,
Input,
InputNumber,
Select,
Space,
Statistic,
Typography,
} from "antd";
import Dinero from "dinero.js";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { determineDmsType } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -17,134 +31,312 @@ const mapDispatchToProps = (dispatch) => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsPostForm);
export function DmsPostForm({ bodyshop, socket, jobId }) {
export function DmsPostForm({ bodyshop, socket, job }) {
const [form] = Form.useForm();
const { t } = useTranslation();
const handlePayerSelect = (value, index) => {
form.setFieldsValue({
payers: form.getFieldValue("payers").map((payer, mapIndex) => {
if (index !== mapIndex) return payer;
const cdkPayer =
bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.find((i) => i.name === value);
if (!cdkPayer) return payer;
return {
...cdkPayer,
dms_acctnumber: cdkPayer.dms_acctnumber,
controlnumber: job && job[cdkPayer.control_type],
};
}),
});
};
const handleFinish = (values) => {
socket.emit(`${determineDmsType(bodyshop)}-export-job`, {
jobid: job.id,
txEnvelope: values,
});
};
return (
<Form form={form} layout="vertical">
<LayoutFormRow>
<Form.Item
name="journal"
label={t("jobs.fields.dms.journal")}
initialValue={
bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.default_journal
}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="dms_make"
label={t("jobs.fields.dms.dms_make")}
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="dms_make"
label={t("jobs.fields.dms.dms_make")}
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<DmsCdkMakes form={form} socket={socket} />
</LayoutFormRow>
<Form.List name={["payers"]}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow>
<Form.Item
label={t("jobs.fields.dms.payer.name")}
key={`${index}name`}
name={[field.name, "name"]}
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.account")}
key={`${index}account`}
name={[field.name, "account"]}
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.amount")}
key={`${index}amount`}
name={[field.name, "amount"]}
rules={[
{
required: true,
},
]}
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.controlnumber")}
key={`${index}controlnumber`}
name={[field.name, "controlnumber"]}
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
</LayoutFormRow>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
<Card title={t("jobs.labels.dms.postingform")}>
<Form
form={form}
layout="vertical"
onFinish={handleFinish}
initialValues={{
story: t("jobs.labels.dms.defaultstory", {
ro_number: job.ro_number,
area_of_damage: job.area_of_damage && job.area_of_damage.impact1,
}).substr(0, 239),
}}
</Form.List>
</Form>
>
<LayoutFormRow grow>
<Form.Item
name="journal"
label={t("jobs.fields.dms.journal")}
initialValue={
bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.default_journal
}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="kmin"
label={t("jobs.fields.kmin")}
initialValue={job && job.kmin}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber disabled />
</Form.Item>
<Form.Item
name="kmout"
label={t("jobs.fields.kmout")}
initialValue={job && job.kmout}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber disabled />
</Form.Item>
</LayoutFormRow>
{bodyshop.cdk_dealerid && (
<LayoutFormRow style={{ justifyContent: "center" }} grow>
<Form.Item
name="dms_make"
label={t("jobs.fields.dms.dms_make")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
name="dms_model"
label={t("jobs.fields.dms.dms_model")}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<DmsCdkMakes form={form} socket={socket} job={job} />
<DmsCdkMakesRefetch />
</LayoutFormRow>
)}
<Form.Item
name="story"
label={t("jobs.fields.dms.story")}
rules={[
{
required: true,
},
]}
>
<Input.TextArea maxLength={240} />
</Form.Item>
<Divider />
<Form.List name={["payers"]}>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Space wrap>
<Form.Item
label={t("jobs.fields.dms.payer.name")}
key={`${index}name`}
name={[field.name, "name"]}
rules={[
{
required: true,
},
]}
>
<Select
style={{ minWidth: "15rem" }}
onSelect={(value) => handlePayerSelect(value, index)}
>
{bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.map((payer) => (
<Select.Option key={payer.name}>
{payer.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.dms_acctnumber")}
key={`${index}dms_acctnumber`}
name={[field.name, "dms_acctnumber"]}
rules={[
{
required: true,
},
]}
>
<Input disabled />
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.amount")}
key={`${index}amount`}
name={[field.name, "amount"]}
rules={[
{
required: true,
},
]}
>
<CurrencyInput min={0} />
</Form.Item>
<Form.Item
label={t("jobs.fields.dms.payer.controlnumber")}
key={`${index}controlnumber`}
name={[field.name, "controlnumber"]}
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item shouldUpdate>
{() => {
const payers = form.getFieldValue("payers");
const row = payers && payers[index];
const cdkPayer =
bodyshop.cdk_configuration.payers &&
bodyshop.cdk_configuration.payers.find(
(i) => i && row && i.name === row.name
);
return (
<div>
{cdkPayer &&
t(`jobs.fields.${cdkPayer.control_type}`)}
</div>
);
}}
</Form.Item>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
</Space>
</Form.Item>
))}
<Form.Item>
<Button
disabled={!(fields.length < 3)}
onClick={() => {
if (fields.length < 3) add();
}}
style={{ width: "100%" }}
>
{t("jobs.actions.dms.addpayer")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item shouldUpdate>
{() => {
//Perform Calculation to determine discrepancy.
let totalAllocated = Dinero();
const payers = form.getFieldValue("payers");
payers &&
payers.forEach((payer) => {
totalAllocated = totalAllocated.add(
Dinero({ amount: Math.round((payer?.amount || 0) * 100) })
);
});
const totals =
socket.allocationsSummary &&
socket.allocationsSummary.reduce(
(acc, val) => {
return {
totalSale: acc.totalSale.add(Dinero(val.sale)),
totalCost: acc.totalCost.add(Dinero(val.cost)),
};
},
{
totalSale: Dinero(),
totalCost: Dinero(),
}
);
const discrep = totals
? totals.totalSale.subtract(totalAllocated)
: Dinero();
return (
<Space size="large" wrap align="center">
<Statistic
title={t("jobs.labels.subtotal")}
value={(totals ? totals.totalSale : Dinero()).toFormat()}
/>
<Typography.Title>-</Typography.Title>
<Statistic
title={t("jobs.labels.dms.totalallocated")}
value={totalAllocated.toFormat()}
/>
<Typography.Title>=</Typography.Title>
<Statistic
title={t("jobs.labels.dms.notallocated")}
valueStyle={{
color: discrep.getAmount() === 0 ? "green" : "red",
}}
value={discrep.toFormat()}
/>
<Button
disabled={
!socket.allocationsSummary || discrep.getAmount() !== 0
}
htmlType="submit"
>
{t("jobs.actions.dms.post")}
</Button>
</Space>
);
}}
</Form.Item>
</Form>
</Card>
);
}

View File

@@ -58,11 +58,9 @@ export function DocumentEditorComponent({ currentUser, bodyshop, document }) {
if (imgRef.current !== null) {
// create a marker.js MarkerArea
markerArea.current = new markerjs2.MarkerArea(imgRef.current);
console.log(`markerArea.current`, markerArea.current);
// attach an event handler to assign annotated image back to our image element
markerArea.current.addCloseEventListener((closeEvent) => {
console.log("Close Event", closeEvent);
});
markerArea.current.addCloseEventListener((closeEvent) => {});
markerArea.current.addRenderEventListener((dataUrl) => {
imgRef.current.src = dataUrl;

View File

@@ -14,8 +14,6 @@ var cleanAxios = axios.create();
cleanAxios.interceptors.request.eject(axiosAuthInterceptorId);
export const handleUpload = (ev, context) => {
console.log("Handling Upload", ev);
logImEXEvent("document_upload", { filetype: ev.file.type });
const { onError, onSuccess, onProgress } = ev;
@@ -61,7 +59,7 @@ export const uploadToCloudinary = async (
// let eager = process.env.REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS;
//Get the signed url.
console.log("fileType", fileType);
const upload_preset = fileType.startsWith("video")
? "incoming_upload_video"
: "incoming_upload";
@@ -74,7 +72,6 @@ export const uploadToCloudinary = async (
});
if (signedURLResponse.status !== 200) {
console.log("Error Getting Signed URL", signedURLResponse.statusText);
if (!!onError) onError(signedURLResponse.statusText);
notification["error"]({
message: i18n.t("documents.errors.getpresignurl", {
@@ -113,13 +110,8 @@ export const uploadToCloudinary = async (
...options,
}
);
console.log("Upload Response", cloudinaryUploadResponse.data);
if (cloudinaryUploadResponse.status !== 200) {
console.log(
"Error uploading to cloudinary.",
cloudinaryUploadResponse.statusText
);
if (!!onError) onError(cloudinaryUploadResponse.statusText);
notification["error"]({
message: i18n.t("documents.errors.insert", {

View File

@@ -35,10 +35,6 @@ export function EmailDocumentsComponent({
},
skip: !emailConfig.jobid,
});
console.log(
"🚀 ~ file: email-documents.component.jsx ~ line 38 ~ emailConfig",
emailConfig
);
return (
<div>

View File

@@ -1,15 +1,71 @@
import { UploadOutlined } from "@ant-design/icons";
import { Divider, Form, Input, Select, Tabs, Upload } from "antd";
import { UploadOutlined, UserAddOutlined } from "@ant-design/icons";
import {
Divider,
Form,
Input,
Select,
Tabs,
Upload,
Space,
Menu,
Dropdown,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import EmailDocumentsComponent from "../email-documents/email-documents.component";
import _ from "lodash";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(EmailOverlayComponent);
export default function EmailOverlayComponent({ form, selectedMediaState }) {
export function EmailOverlayComponent({ form, selectedMediaState, bodyshop }) {
const { t } = useTranslation();
const handleClick = ({ item, key, keyPath }) => {
const email = item.props.value;
form.setFieldsValue({ to: _.uniq([...form.getFieldValue("to"), email]) });
};
const menu = (
<div>
<Menu onClick={handleClick}>
{bodyshop.employees
.filter((e) => e.user_email)
.map((e, idx) => (
<Menu.Item value={e.user_email} key={idx}>
{`${e.first_name} ${e.last_name}`}
</Menu.Item>
))}
</Menu>
</div>
);
return (
<div>
<Form.Item
label={t("emails.fields.to")}
label={
<Space>
{t("emails.fields.to")}
<Dropdown overlay={menu}>
<a
className="ant-dropdown-link"
href=" #"
onClick={(e) => e.preventDefault()}
>
<UserAddOutlined />
</a>
</Dropdown>
</Space>
}
name="to"
rules={[
{

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: {
@@ -109,7 +111,6 @@ export function EmailOverlayContainer({
notification["success"]({ message: t("emails.successes.sent") });
toggleEmailOverlayVisible();
} catch (error) {
console.log(JSON.stringify(error));
notification["error"]({
message: t("emails.errors.notsent", { message: error.message }),
});
@@ -147,6 +148,18 @@ export function EmailOverlayContainer({
html: response.data,
fileList: [],
});
if (
bodyshop.md_email_cc[emailConfig.template.name] &&
bodyshop.md_email_cc[emailConfig.template.name].length > 0
) {
form.setFieldsValue({
cc: [
...(form.getFieldValue("cc") || []),
...bodyshop.md_email_cc[emailConfig.template.name],
],
});
}
setLoading(false);
};

View File

@@ -7,10 +7,12 @@ import { selectCurrentUser } from "../../redux/user/user.selectors";
import { GenerateDocument } from "../../utils/RenderTemplate";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import moment from "moment";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
});
@@ -19,14 +21,24 @@ export function EmailTestComponent({ currentUser, setEmailOptions }) {
const [form] = Form.useForm();
const handleFinish = (values) => {
console.log("values", values);
GenerateDocument(
{
name: values.key,
variables: {
...(values.start
? {
start: moment(values.start).startOf("day").format("YYYY-MM-DD"),
}
: {}),
...(values.end
? { end: moment(values.end).endOf("day").format("YYYY-MM-DD") }
: {}),
...(values.start
? { starttz: moment(values.start).startOf("day") }
: {}),
...(values.end ? { endtz: moment(values.end).endOf("day") } : {}),
...(values.id ? { id: values.id } : {}),
...(values.start ? { start: values.start } : {}),
...(values.end ? { end: values.end } : {}),
},
},
{

View File

@@ -9,6 +9,7 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { tracker } from "../../App/App.container";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
@@ -36,6 +37,7 @@ class ErrorBoundary extends React.Component {
componentDidCatch(error, info) {
console.log("Exception Caught by Error Boundary.", error, info);
this.setState({ ...this.state, error, info });
tracker.event("error_boundary", error, true);
}
handleErrorSubmit = () => {

View File

@@ -2,7 +2,7 @@ import { withApollo } from "@apollo/client/react/hoc";
import React, { Component } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent, messaging } from "../../firebase/firebase.utils";
//import { logImEXEvent, messaging } from "../../firebase/firebase.utils";
import { selectCurrentUser } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
@@ -15,21 +15,20 @@ const mapDispatchToProps = (dispatch) => ({
class FcmNotificationComponent extends Component {
async componentDidMount() {
//const { client, currentUser } = this.props;
if (!!!messaging) return; //Skip all of the notification functionality if the firebase SDK could not start.
messaging
.requestPermission()
.then(async function () {
// const token = await messaging.getToken();
// client.mutate({
// mutation: UPDATE_FCM_TOKEN,
// variables: { authEmail: currentUser.email, token: { [token]: true } },
// });
})
.catch(function (err) {
console.log("Unable to get permission to notify.", err);
logImEXEvent("fcm_permission_denied", { message: err });
});
// if (!!!messaging) return; //Skip all of the notification functionality if the firebase SDK could not start.
// messaging
// .requestPermission()
// .then(async function () {
// // const token = await messaging.getToken();
// // client.mutate({
// // mutation: UPDATE_FCM_TOKEN,
// // variables: { authEmail: currentUser.email, token: { [token]: true } },
// // });
// })
// .catch(function (err) {
// console.log("Unable to get permission to notify.", err);
// logImEXEvent("fcm_permission_denied", { message: err });
// });
}
render() {

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>
),
@@ -168,7 +171,6 @@ export default function GlobalSearch() {
<AutoComplete
options={options}
onSearch={handleSearch}
allowClear
placeholder={t("general.labels.globalsearch")}
>
<Input.Search loading={loading} />

View File

@@ -46,12 +46,16 @@ import {
} from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import { signOutStart } from "../../redux/user/user.actions";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
recentItems: selectRecentItems,
selectedHeader: selectSelectedHeader,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
@@ -69,6 +73,7 @@ const mapDispatchToProps = (dispatch) => ({
function Header({
handleMenuClick,
currentUser,
bodyshop,
selectedHeader,
signOutStart,
setBillEnterContext,
@@ -237,16 +242,26 @@ function Header({
{t("menus.header.accounting-receivables")}
</Link>
</Menu.Item>
<Menu.Item key="payables">
<Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")}
</Link>
</Menu.Item>
<Menu.Item key="payments">
<Link to="/manage/accounting/payments">
{t("menus.header.accounting-payments")}
</Link>
</Menu.Item>
{!(
(bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber)
) && (
<Menu.Item key="payables">
<Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")}
</Link>
</Menu.Item>
)}
{!(
(bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber)
) && (
<Menu.Item key="payments">
<Link to="/manage/accounting/payments">
{t("menus.header.accounting-payments")}
</Link>
</Menu.Item>
)}
<Menu.Item key="export-logs">
<Link to="/manage/accounting/exportlogs">
{t("menus.header.export-logs")}

View File

@@ -10,15 +10,11 @@ export default function HelpRescue() {
var bodyFormData = new FormData();
bodyFormData.append("Code", code);
bodyFormData.append("hostederrorhandling", 1);
const res1 = await fetch(
"https://secure.logmeinrescue.com/Customer/Code.aspx",
{
mode: "no-cors",
method: "POST",
body: bodyFormData,
}
);
console.log("handleClick -> res1", await res1.text());
await fetch("https://secure.logmeinrescue.com/Customer/Code.aspx", {
mode: "no-cors",
method: "POST",
body: bodyFormData,
});
};
return (
@@ -40,7 +36,6 @@ export default function HelpRescue() {
method="post"
id="logmeinsupport"
onSubmit={(...props) => {
console.log(`props`, props);
alert();
}}
>

View File

@@ -44,7 +44,7 @@ export function Jobd3RdPartyModal({ bodyshop, jobId }) {
};
const handleFinish = (values) => {
const { sendtype, ...restVals } = values;
console.log(restVals);
GenerateDocument(
{
name: TemplateList("job_special").thirdpartypayer.key,

View File

@@ -56,7 +56,7 @@ export function ScheduleEventColor({ bodyshop, event }) {
<Menu.Item key={"null"}>{t("general.actions.clear")}</Menu.Item>
</Menu>
);
console.log(`event`, event);
return (
<Dropdown overlay={menu}>
<a href=" #" onClick={(e) => e.preventDefault()}>

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} />
@@ -170,7 +176,7 @@ export function ScheduleEventComponent({
t("appointments.labels.reminder", {
shopname: bodyshop.shopname,
date: moment(event.start).format("MM/DD/YYYY"),
time: moment(event.start).format("HH:MM a"),
time: moment(event.start).format("HH:mm a"),
})
);
setVisible(false);
@@ -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

@@ -55,7 +55,6 @@ export default function JobBillsTotalComponent({
);
}
if (il.deductedfromlbr) {
console.log(i, "Deducting from labor.");
lbrAdjustments = lbrAdjustments.add(
Dinero({
amount: Math.round((il.actual_price || 0) * 100),

View File

@@ -64,14 +64,16 @@ export function JobChecklistForm({
...(type === "intake" && { actual_in: new Date() }),
...(type === "intake" && {
production_vars: {
...job.production_vars,
...values.production_vars,
...(job ? job.production_vars : {}),
note:
values.production_vars &&
values.production_vars.note &&
values.production_vars.note !== ""
? job.production_vars && values.production_vars.note
: job.production_vars && job.production_vars.note,
? values &&
values.production_vars &&
values.production_vars.note
: job && job.production_vars && job.production_vars.note,
},
}),
...(type === "intake" && {
@@ -165,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>
)
@@ -180,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" && {
@@ -245,6 +253,7 @@ export function JobChecklistForm({
name={["production_vars", "note"]}
label={t("jobs.fields.production_vars.note")}
disabled={readOnly}
trigger="onChange"
>
<Input.TextArea rows={3} disabled={readOnly} />
</Form.Item>

View File

@@ -34,7 +34,7 @@ export default function JobIntakeTemplateList({ templates }) {
const renderAllTemplates = async () => {
logImEXEvent("checklist_render_all_templates");
setLoading(true);
console.log("templates :>> ", templates);
await GenerateDocuments(
templates.map((key) => {
return { name: key, variables: { id: jobId } };

View File

@@ -35,7 +35,7 @@ export function JobCostingModalContainer({
async function getData() {
if (jobId && visible) {
const { data } = await axios.post("/job/costing", { jobid: jobId });
console.log(data);
setCostingData(data);
}
}

View File

@@ -77,7 +77,6 @@ export default function JobCostingPartsTable({ data, summaryData }) {
.includes(searchText.toLowerCase())
);
console.log("data :>> ", data);
return (
<div>
<Table

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)),
])
);
}
};
@@ -400,7 +406,7 @@ export function JobLinesComponent({
setState({
...state,
filteredInfo: {
part_type: ["PAN,PAC,PAR,PAL,PAA,PAM,PAP,PAS,PASL"],
part_type: ["PAN,PAC,PAR,PAL,PAA,PAM,PAP,PAS,PASL,PAG"],
},
});
}}
@@ -435,7 +441,7 @@ export function JobLinesComponent({
columns={columns}
rowKey="id"
loading={loading}
pagination={{ position: "top", defaultPageSize: 50 }}
pagination={false}
dataSource={jobLines}
onChange={handleTableChange}
scroll={{

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";
@@ -115,18 +115,18 @@ export default function JobLinesUpsertModalComponent({
<Form.Item
label={t("joblines.fields.mod_lb_hrs")}
name="mod_lb_hrs"
// rules={[
// ({ getFieldValue }) => ({
// validator(rule, value) {
// if (!!getFieldValue("mod_lbr_ty") === !!value) {
// return Promise.resolve();
// }
// return Promise.reject(
// t("joblines.validations.hrsrequirediflbrtyp")
// );
// },
// }),
// ]}
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (!!getFieldValue("mod_lbr_ty") === !!value) {
return Promise.resolve();
}
return Promise.reject(
t("joblines.validations.hrsrequirediflbrtyp")
);
},
}),
]}
>
<InputNumber precision={1} />
</Form.Item>
@@ -169,49 +169,49 @@ export default function JobLinesUpsertModalComponent({
<Form.Item
label={t("joblines.fields.part_qty")}
name="part_qty"
// rules={[
// ({ getFieldValue }) => ({
// validator(rule, value) {
// if (!!getFieldValue("part_type") === !!value) {
// return Promise.resolve();
// }
// return Promise.reject(
// t("joblines.validations.requiredifparttype")
// );
// },
// }),
// ]}
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (!!getFieldValue("part_type") === !!value) {
return Promise.resolve();
}
return Promise.reject(
t("joblines.validations.requiredifparttype")
);
},
}),
]}
>
<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"
// rules={[
// ({ getFieldValue }) => ({
// validator(rule, value) {
// if (!value || getFieldValue("part_type") !== "PAE") {
// return Promise.resolve();
// }
// return Promise.reject(
// t("joblines.validations.zeropriceexistingpart")
// );
// },
// }),
// ({ getFieldValue }) => ({
// validator(rule, value) {
// if (!!getFieldValue("part_type") === !!value) {
// return Promise.resolve();
// }
// return Promise.reject(
// t("joblines.validations.requiredifparttype")
// );
// },
// }),
// ]}
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (!value || getFieldValue("part_type") !== "PAE") {
return Promise.resolve();
}
return Promise.reject(
t("joblines.validations.zeropriceexistingpart")
);
},
}),
({ getFieldValue }) => ({
validator(rule, value) {
if (!!getFieldValue("part_type") === !!value) {
return Promise.resolve();
}
return Promise.reject(
t("joblines.validations.requiredifparttype")
);
},
}),
]}
>
<InputCurrency precision={2} min={0} />
</Form.Item>
@@ -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

@@ -21,7 +21,8 @@ export default function JobReconciliationBillsTable({
title: t("billlines.fields.line_desc"),
dataIndex: "line_desc",
key: "line_desc",
width: "35%",
ellipsis: true,
minWidth: "65rem",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
@@ -30,7 +31,7 @@ export default function JobReconciliationBillsTable({
title: t("billlines.labels.from"),
dataIndex: "from",
key: "from",
width: "20%",
ellipsis: true,
render: (text, record) =>
`${record.bill.vendor && record.bill.vendor.name} / ${
@@ -42,6 +43,7 @@ export default function JobReconciliationBillsTable({
dataIndex: "actual_price",
key: "actual_price",
sorter: (a, b) => a.actual_price - b.actual_price,
width: "7rem",
sortOrder:
state.sortedInfo.columnKey === "actual_price" && state.sortedInfo.order,
render: (text, record) => (
@@ -53,6 +55,7 @@ export default function JobReconciliationBillsTable({
dataIndex: "actual_cost",
key: "actual_cost",
sorter: (a, b) => a.actual_cost - b.actual_cost,
width: "7rem",
sortOrder:
state.sortedInfo.columnKey === "actual_cost" && state.sortedInfo.order,
render: (text, record) => (
@@ -64,6 +67,7 @@ export default function JobReconciliationBillsTable({
dataIndex: "quantity",
key: "quantity",
sorter: (a, b) => a.quantity - b.quantity,
width: "4rem",
sortOrder:
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
},
@@ -72,9 +76,11 @@ export default function JobReconciliationBillsTable({
dataIndex: "is_credit_memo",
key: "is_credit_memo",
sorter: (a, b) => a.bill.is_credit_memo - b.bill.is_credit_memo,
width: "8rem",
sortOrder:
state.sortedInfo.columnKey === "is_credit_memo" &&
state.sortedInfo.order,
render: (text, record) => (
<Checkbox disabled checked={record.bill.is_credit_memo} />
),
@@ -94,7 +100,7 @@ export default function JobReconciliationBillsTable({
<Table
pagination={false}
size="small"
scroll={{ y: "80vh", x: true }}
scroll={{ y: "60vh" }}
columns={columns}
rowKey="id"
dataSource={invoiceLineData}
@@ -102,6 +108,9 @@ export default function JobReconciliationBillsTable({
rowSelection={{
onChange: handleOnRowClick,
selectedRowKeys: selectedLines,
getCheckboxProps: (record) => {
return { disabled: record.deductedfromlbr };
},
}}
/>
</div>

View File

@@ -1,12 +1,12 @@
.imex-reconciliation-modal {
top: 20px;
.ant-modal-content {
height: 95vh;
display: flex;
flex-direction: column;
.ant-modal-body {
display: flex;
flex: 1;
}
}
// .ant-modal-content {
// height: 95vh;
// display: flex;
// flex-direction: column;
// .ant-modal-body {
// display: flex;
// flex: 1;
// }
// }
}

View File

@@ -23,6 +23,7 @@ export default function JobReconcilitionPartsTable({
dataIndex: "line_desc",
key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
ellipses: true,
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
},
@@ -57,6 +58,7 @@ export default function JobReconcilitionPartsTable({
dataIndex: "act_price",
key: "act_price",
sorter: (a, b) => a.act_price - b.act_price,
width: "7rem",
sortOrder:
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
@@ -68,10 +70,12 @@ export default function JobReconcilitionPartsTable({
title: t("joblines.fields.part_qty"),
dataIndex: "part_qty",
key: "part_qty",
width: "4rem",
},
{
title: t("joblines.fields.total"),
dataIndex: "total",
width: "7rem",
key: "total",
sorter: (a, b) => a.act_price * a.part_qty - b.act_price * b.part_qty,
sortOrder:
@@ -89,6 +93,7 @@ export default function JobReconcilitionPartsTable({
dataIndex: "status",
key: "status",
sorter: (a, b) => alphaSort(a.status, b.status),
width: "6rem",
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
},
@@ -108,7 +113,7 @@ export default function JobReconcilitionPartsTable({
pagination={false}
columns={columns}
size="small"
scroll={{ y: "80vh", x: true }}
scroll={{ y: "60vh" }}
rowKey="id"
dataSource={jobLineData}
onChange={handleTableChange}

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(
@@ -73,7 +73,7 @@ export const reconcileByPrice = (
jobLines.forEach((jl) => {
const matchingBillLineIds = billLines
.filter((bl) => bl.actual_price === jl.act_price && !jl.removed)
.filter((bl) => bl.actual_price === jl.act_price && bl.quantity === jl.part_qty && !jl.removed)
.map((bl) => bl.id);
if (matchingBillLineIds.length > 1) {

View File

@@ -1,12 +1,24 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Form, notification, Popover } from "antd";
import { useMutation, useLazyQuery } from "@apollo/client";
import {
Button,
Card,
Form,
InputNumber,
notification,
Popover,
Space,
} from "antd";
import moment from "moment";
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_SCOREBOARD_ENTRY } from "../../graphql/scoreboard.queries";
import {
INSERT_SCOREBOARD_ENTRY,
QUERY_SCOREBOARD_ENTRY,
UPDATE_SCOREBOARD_ENTRY,
} from "../../graphql/scoreboard.queries";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import InputNumberCalculator from "../form-input-number-calculator/form-input-number-calculator.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
export default function ScoreboardAddButton({
job,
@@ -15,17 +27,44 @@ export default function ScoreboardAddButton({
}) {
const { t } = useTranslation();
const [insertScoreboardEntry] = useMutation(INSERT_SCOREBOARD_ENTRY);
const [updateScoreboardEntry] = useMutation(UPDATE_SCOREBOARD_ENTRY);
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) {
callQuery({ variables: { jobid: job.id } });
}
}, [visibility, job.id, callQuery]);
useEffect(() => {
if (entryData && entryData.scoreboard && entryData.scoreboard[0]) {
form.setFieldsValue(entryData.scoreboard[0]);
}
}, [entryData, form]);
const handleFinish = async (values) => {
logImEXEvent("job_close_add_to_scoreboard");
setLoading(true);
const result = await insertScoreboardEntry({
variables: { sbInput: [{ jobid: job.id, ...values }] },
});
let result;
if (entryData && entryData.scoreboard && entryData.scoreboard[0]) {
result = await updateScoreboardEntry({
variables: {
sbId: entryData.scoreboard[0].id,
sbInput: values,
},
});
} else {
result = await insertScoreboardEntry({
variables: { sbInput: [{ jobid: job.id, ...values }] },
});
}
if (!!result.errors) {
notification["error"]({
@@ -45,53 +84,62 @@ export default function ScoreboardAddButton({
const overlay = (
<Card>
<div>
<Form
form={form}
layout="vertical"
onFinish={handleFinish}
initialValues={{}}
>
<Form.Item
label={t("scoreboard.fields.date")}
name="date"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
{entryLoading ? (
<LoadingSpinner />
) : (
<Form
form={form}
layout="vertical"
onFinish={handleFinish}
initialValues={{}}
>
<FormDatePicker />
</Form.Item>
<Form.Item
label={t("scoreboard.fields.bodyhrs")}
name="bodyhrs"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumberCalculator precision={1} />
</Form.Item>
<Form.Item
label={t("scoreboard.fields.painthrs")}
name="painthrs"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumberCalculator precision={1} />
</Form.Item>
<Form.Item
label={t("scoreboard.fields.date")}
name="date"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<FormDatePicker />
</Form.Item>
<Form.Item
label={t("scoreboard.fields.bodyhrs")}
name="bodyhrs"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber precision={1} />
</Form.Item>
<Form.Item
label={t("scoreboard.fields.painthrs")}
name="painthrs"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber precision={1} />
</Form.Item>
<Button type="primary" htmlType="submit">
{t("general.actions.save")}
</Button>
</Form>
<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>
);
@@ -100,7 +148,7 @@ export default function ScoreboardAddButton({
setLoading(true);
const v = job.joblines.reduce(
(acc, val) => {
if (val.mod_lbr_ty === "LAB")
if (val.mod_lbr_ty !== "LAR")
acc = { ...acc, bodyhrs: acc.bodyhrs + val.mod_lb_hrs };
if (val.mod_lbr_ty === "LAR")
acc = { ...acc, painthrs: acc.painthrs + val.mod_lb_hrs };

View File

@@ -22,7 +22,6 @@ export function JobsAdminClass({ bodyshop, job }) {
const [updateJob] = useMutation(UPDATE_JOB);
const handleFinish = async (values) => {
console.log(values);
setLoading(true);
const result = await updateJob({
variables: { jobId: job.id, job: values },

View File

@@ -13,7 +13,6 @@ export default function JobsAdminDatesChange({ job }) {
const [updateJob] = useMutation(UPDATE_JOB);
const handleFinish = async (values) => {
console.log(values);
setLoading(true);
const result = await updateJob({
variables: { jobId: job.id, job: values },
@@ -74,7 +73,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

@@ -10,7 +10,6 @@ export default function JobAdminOwnerReassociate({ job }) {
const [form] = Form.useForm();
const [updateJob] = useMutation(UPDATE_JOB);
const handleFinish = async (values) => {
console.log(values);
setLoading(true);
const result = await updateJob({
variables: { jobId: job.id, job: { ownerid: values.ownerid } },

View File

@@ -10,7 +10,6 @@ export default function JobAdminOwnerReassociate({ job }) {
const [form] = Form.useForm();
const [updateJob] = useMutation(UPDATE_JOB);
const handleFinish = async (values) => {
console.log(values);
setLoading(true);
const result = await updateJob({
variables: { jobId: job.id, job: { vehicleid: values.vehicleid } },

View File

@@ -3,8 +3,6 @@ import { gql } from "@apollo/client";
import _ from "lodash";
export const GetSupplementDelta = async (client, jobId, newLines) => {
console.log("-----Begin Supplement-----");
const {
data: { joblines: existingLinesFromDb },
} = await client.query({

View File

@@ -29,6 +29,7 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import AlertComponent from "../alert/alert.component";
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
@@ -98,24 +99,8 @@ export function JobsAvailableContainer({
return;
}
//IO-539 Check for Parts Rate on PAL for SGI use case.
if (
estData.est_data.parts_tax_rates &&
estData.est_data.parts_tax_rates.PAL &&
(estData.est_data.parts_tax_rates.PAL.prt_tax_rt === null ||
estData.est_data.parts_tax_rates.PAL.prt_tax_rt === 0)
) {
console.log("checking");
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for used parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}%.`
);
if (res) {
estData.est_data.parts_tax_rates.PAL.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.est_data.parts_tax_rates.PAL.prt_tax_in = true;
}
}
await CheckTaxRates(estData.est_data, bodyshop);
console.log(estData);
const newTotals = (
await Axios.post("/job/totals", {
job: {
@@ -155,16 +140,17 @@ export function JobsAvailableContainer({
: {}),
};
if (selectedOwner) {
newJob.ownerid = selectedOwner;
delete newJob.owner;
}
if (newJob.vehicleid) {
delete newJob.vehicle;
}
insertNewJob({
variables: {
job: selectedOwner
? Object.assign(
{},
newJob,
{ owner: null },
{ ownerid: selectedOwner }
)
: newJob,
job: newJob,
},
})
.then((r) => {
@@ -214,26 +200,10 @@ export function JobsAvailableContainer({
message: t("jobs.errors.creating", { error: "No job data present." }),
});
} else {
//IO-539 Check for Parts Rate on PAL for SGI use case.
if (
estData.est_data.parts_tax_rates &&
estData.est_data.parts_tax_rates.PAL &&
(estData.est_data.parts_tax_rates.PAL.prt_tax_rt === null ||
estData.est_data.parts_tax_rates.PAL.prt_tax_rt === 0)
) {
console.log("checking");
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for used parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}%.`
);
if (res) {
estData.est_data.parts_tax_rates.PAL.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.est_data.parts_tax_rates.PAL.prt_tax_in = true;
}
}
//create upsert job
let supp = replaceEmpty({ ...estData.est_data });
//IO-539 Check for Parts Rate on PAL for SGI use case.
await CheckTaxRates(supp, bodyshop);
delete supp.owner;
delete supp.vehicle;
@@ -421,14 +391,119 @@ function replaceEmpty(someObj, replaceValue = null) {
value === "" ? replaceValue || null : value;
//^ because you seem to want to replace (strings) "null" or "undefined" too
const temp = JSON.stringify(someObj, replacer);
console.log("Parsed", JSON.parse(temp));
return JSON.parse(temp);
}
function confirmDialog(msg) {
return new Promise(function (resolve, reject) {
let confirmed = window.confirm(msg);
async function CheckTaxRates(estData, bodyshop) {
//LKQ Check
if (
!estData.parts_tax_rates?.PAL ||
estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
) {
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
);
if (res) {
if (!estData.parts_tax_rates.PAL) {
estData.parts_tax_rates.PAL = {
prt_discp: 0,
prt_mktyp: true,
prt_mkupp: 0,
prt_type: "PAL",
};
}
estData.parts_tax_rates.PAL.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.parts_tax_rates.PAL.prt_tax_in = true;
}
}
//PAC Check
if (
!estData.parts_tax_rates?.PAC ||
estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
) {
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
);
if (res) {
if (!estData.parts_tax_rates.PAC) {
estData.parts_tax_rates.PAC = {
prt_discp: 0,
prt_mktyp: true,
prt_mkupp: 0,
prt_type: "PAC",
};
}
estData.parts_tax_rates.PAC.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.parts_tax_rates.PAC.prt_tax_in = true;
}
}
//PAM Check
if (
!estData.parts_tax_rates?.PAM ||
estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
) {
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
);
if (res) {
if (!estData.parts_tax_rates.PAM) {
estData.parts_tax_rates.PAM = {
prt_discp: 0,
prt_mktyp: true,
prt_mkupp: 0,
prt_type: "PAM",
};
}
estData.parts_tax_rates.PAM.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.parts_tax_rates.PAM.prt_tax_in = true;
}
}
return confirmed ? resolve(true) : reject(false);
if (
!estData.parts_tax_rates?.PAR ||
estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
) {
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
);
if (res) {
if (!estData.parts_tax_rates.PAR) {
estData.parts_tax_rates.PAR = {
prt_discp: 0,
prt_mktyp: true,
prt_mkupp: 0,
prt_type: "PAR",
};
}
estData.parts_tax_rates.PAR.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
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

@@ -61,7 +61,7 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
);
};
const overlay = bodyshop.cdk_dealerid && (
const overlay = (bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<Menu onClick={handleMenuClick}>
{bodyshop.md_responsibility_centers.dms_defaults.map((mapping) => (
<Menu.Item key={mapping.name}>{mapping.name}</Menu.Item>
@@ -69,7 +69,7 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
</Menu>
);
return bodyshop.cdk_dealerid ? (
return bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber ? (
<Dropdown overlay={overlay}>
<Button disabled={disabled}>{t("jobs.actions.dmsautoallocate")}</Button>
</Dropdown>

View File

@@ -13,6 +13,8 @@ import {
} from "../../redux/user/user.selectors";
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,
@@ -26,56 +28,76 @@ export function JobsCloseExportButton({
disabled,
setSelectedJobs,
}) {
const history = useHistory();
const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOB);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [loading, setLoading] = useState(false);
const handleQbxml = async () => {
//Check if it's a CDK setup.
if (bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) {
history.push(`/manage/dms?jobId=${jobId}`);
return;
}
logImEXEvent("jobs_close_export");
setLoading(true);
let QbXmlResponse;
try {
QbXmlResponse = await axios.post(
"/accounting/qbxml/receivables",
{ jobIds: [jobId] },
{
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),
}),
});
setLoading(false);
return;
}
//Check if it's a QBO Setup.
let PartnerResponse;
try {
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(
"http://localhost:1337/qb/",
// "http://609feaeae986.ngrok.io/qb/",
QbXmlResponse.data,
`/qbo/receivables`,
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
},
}
jobIds: [jobId],
},
);
} catch (error) {
console.log("Error connecting to quickbooks or partner.", error);
notification["error"]({
message: t("jobs.errors.exporting-partner"),
});
setLoading(false);
return;
} else {
//Default is QBD
let QbXmlResponse;
try {
QbXmlResponse = await axios.post(
"/accounting/qbxml/receivables",
{ jobIds: [jobId] },
{
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),
}),
});
setLoading(false);
return;
}
try {
PartnerResponse = await axios.post(
"http://localhost:1337/qb/",
// "http://609feaeae986.ngrok.io/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"),
});
setLoading(false);
return;
}
}
console.log("PartnerResponse", PartnerResponse);
@@ -159,12 +181,7 @@ export function JobsCloseExportButton({
};
return (
<Button
onClick={handleQbxml}
loading={loading}
disabled={disabled}
type="dashed"
>
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}
</Button>
);

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,43 @@
import { DownOutlined } from "@ant-design/icons";
import { Dropdown, Menu } from "antd";
import React from "react";
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 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()}
>
<DownOutlined />
</a>
</Dropdown>
);
}
export default connect(mapStateToProps, null)(JobsDetailChangeEstimator);

View File

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

View File

@@ -69,6 +69,18 @@ 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.date_next_contact")}
name="date_next_contact"
>
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.scheduled_completion")}
name="scheduled_completion"
@@ -93,7 +105,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,14 @@
import { Col, Form, Input, InputNumber, Row, Select, Switch } from "antd";
import {
Col,
Divider,
Form,
Input,
InputNumber,
Row,
Select,
Space,
Switch,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -12,8 +22,9 @@ 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";
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
@@ -44,7 +55,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} />
@@ -68,7 +86,15 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
<Form.Item label={t("jobs.fields.ins_city")} name="ins_city">
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.ins_ct_ln")} name="ins_ct_ln">
<Form.Item
label={
<Space>
{t("jobs.fields.ins_ct_ln")}
<JobsDetailChangeFileHandler form={form} disabled={jobRO} />
</Space>
}
name="ins_ct_ln"
>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.ins_ct_fn")} name="ins_ct_fn">
@@ -114,6 +140,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 disabled={jobRO} />
</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 +165,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,12 +220,27 @@ 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")}>
<FormRow noDivider>
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.est_ct_fn")} name="est_ct_fn">
<Form.Item
label={
<Space>
{t("jobs.fields.est_ct_fn")}
<JobsDetailChangeEstimator form={form} disabled={jobRO} />
</Space>
}
name="est_ct_fn"
>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.est_ct_ln")} name="est_ct_ln">

View File

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

View File

@@ -104,7 +104,7 @@ export function JobsDetailHeaderCsi({
replyTo: bodyshop.email,
},
template: {
name: TemplateList("job").csi_invitation.key,
name: TemplateList("job_special").csi_invitation_action.key,
variables: {
id: result.data.insert_csi.returning[0].id,
},
@@ -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

@@ -19,7 +19,6 @@ export default async function DuplicateJob(
variables: { id: jobId },
});
console.log("res", res);
const { jobs_by_pk } = res.data;
const existingJob = _.cloneDeep(jobs_by_pk);
delete existingJob.__typename;

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

@@ -15,6 +15,8 @@ import JobAltTransportChange from "../job-at-change/job-at-change.component";
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
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,
@@ -69,17 +71,27 @@ 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")}>
{job.ins_co_nm}
</DataLabel>
<DataLabel label={t("jobs.fields.clm_no")}>{job.clm_no}</DataLabel>
<DataLabel label={t("jobs.fields.ponumber")} hideIfNull>
{job.po_number}
</DataLabel>
<DataLabel label={t("jobs.fields.repairtotal")}>
<CurrencyFormatter>{job.clm_total}</CurrencyFormatter>
<span style={{ margin: "0rem .5rem" }}>/</span>
<CurrencyFormatter>{job.owner_owing}</CurrencyFormatter>
</DataLabel>
<DataLabel label={t("jobs.fields.alt_transport")}>
{job.alt_transport}
<JobAltTransportChange job={job} />
@@ -136,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 || ""
@@ -177,6 +192,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<DataLabel key="4" label={t("vehicles.fields.v_vin")}>
{`${job.v_vin || t("general.labels.na")}`}
</DataLabel>
<DataLabel label={t("jobs.labels.relatedros")}>
<JobsRelatedRos jobid={job.id} job={job} />
</DataLabel>
</div>
</Card>
</Col>

View File

@@ -13,6 +13,7 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
@@ -22,9 +23,10 @@ import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
export function JobsDetailRates({ jobRO, form, job }) {
export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
const { t } = useTranslation();
return (
<div>
@@ -36,7 +38,7 @@ export function JobsDetailRates({ jobRO, form, job }) {
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
@@ -45,6 +47,7 @@ export function JobsDetailRates({ jobRO, form, job }) {
>
<CurrencyInput
disabled={jobRO}
min={0}
max={
Math.round(
(job.job_totals &&
@@ -59,29 +62,29 @@ export function JobsDetailRates({ jobRO, form, job }) {
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")}
name="adjustment_bottom_line"
>
<CurrencyInput disabled={jobRO} />
<CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
</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>
@@ -121,7 +124,6 @@ export function JobsDetailRates({ jobRO, form, job }) {
{t("jobs.forms.laborrates")}
</Divider>
<Space>
<div></div>
<JobsDetailRatesChangeButton form={form} disabled={jobRO} />
<JobsMarkPstExempt form={form} />
</Space>
@@ -133,72 +135,72 @@ export function JobsDetailRates({ jobRO, form, job }) {
<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} />

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