Compare commits

...

568 Commits

Author SHA1 Message Date
Allan Carr
8ebf7baa71 IO-2481 Parts Queue Query
prettyier
2023-11-27 10:16:10 -08:00
Allan Carr
742d2b5ff2 IO-2481 Parts Queue Query
Adjust query to only show converted Jobs
2023-11-27 10:13:43 -08:00
Allan Carr
6570d38719 Merged in release/2023-11-24 (pull request #1080)
Release/2023 11 24
2023-11-24 21:12:02 +00:00
Dave Richer
f299c685e2 Merged in feature/IO-2304-Auto-Parts-Toggle (pull request #1078)
Update translations, move configuration toggle for autopartsqueue
2023-11-24 17:46:30 +00:00
Dave Richer
59075ee610 Update translations, move configuration toggle for autopartsqueue 2023-11-24 12:43:55 -05:00
Allan Carr
0716920dfc Merged in feature/IO-2438-All-Employee-Scoreboard-Summary (pull request #1075)
IO-2438 Remove unneeded imports
2023-11-24 03:13:54 +00:00
Allan Carr
07119e4e7e IO-2438 Remove unneeded imports 2023-11-23 19:14:00 -08:00
Allan Carr
7dcdd64a17 IO-2438 Adjust Start & End dates for timetickets 2023-11-23 19:11:09 -08:00
Allan Carr
f26b045727 Merged in feature/IO-2438-All-Employee-Scoreboard-Summary (pull request #1073)
IO-2438 Adjust Start & End dates for timetickets
2023-11-24 03:10:49 +00:00
Allan Carr
8eff1dfc4c Merged in feature/IO-2214-Lost-Sale-Date (pull request #1071)
IO-2214 Lost Sale Date
2023-11-23 23:05:12 +00:00
Allan Carr
74da3ec1ca IO-2214 Add new Lost Sales report to Report Center 2023-11-22 17:51:06 -08:00
Allan Carr
7e99a51495 IO-2214 Lost Sale Date
Add date_lost_sale to track date sale was lost, add in Audit Trail for both cancel and insertion of schedule, standardize date format on output.
2023-11-22 17:32:35 -08:00
Dave Richer
8d170a5bb4 Merged in feature/IO-2304-Auto-Parts-Toggle (pull request #1066)
Add toggles to two modals to allow for auto parts queue toggle

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

Approved-by: Patrick Fic
2023-11-21 18:16:12 +00:00
Patrick Fic
c2b3905c8e Remove references to Yarn in packages files. 2023-11-20 14:43:07 -08:00
Patrick Fic
32072f1d6c Update package node ver to >=18. 2023-11-20 14:02:31 -08:00
Dave Richer
e84d3bf53a Changes 2023-11-20 16:46:27 -05:00
Dave Richer
67a7e4b865 Fix Circle CI Config 2023-11-20 15:57:06 -05:00
Dave Richer
3d7da0b28a Update Circle CI configuration 2023-11-20 15:45:46 -05:00
Dave Richer
9320587595 progress 2023-11-20 15:41:24 -05:00
Dave Richer
7bceba7ed5 Progress 2023-11-20 15:28:47 -05:00
Dave Richer
0db72cd9e4 Progress 2023-11-20 15:14:31 -05:00
Dave Richer
53fc5e361f Progress 2023-11-20 15:02:46 -05:00
Dave Richer
54af163ddf Progress 2023-11-20 14:46:11 -05:00
Allan Carr
c0d756fa38 Merged in release/2023-11-17 (pull request #1060)
Release/2023 11 17
2023-11-17 21:46:20 +00:00
Allan Carr
fc16190ec4 Merged in feature/IO-2470-CC-Year-b-Make-UI (pull request #1058)
IO-2470-CC-Year-b-Make-UI
2023-11-17 19:08:43 +00:00
Allan Carr
cf7f4f1b46 IO-2470-CC-Year-b-Make-UI 2023-11-17 11:08:17 -08:00
Dave Richer
ba95a636cf Merged in feature/IO-2331-Remove-Required-CC-Fields (pull request #1055)
IO-2331 Remove required CC fields Next Service KMS and Next Service Date
2023-11-15 22:13:56 +00:00
Dave Richer
2478fedf1a IO-2331 Remove required CC fields Next Service KMS and Next Service Date 2023-11-15 17:10:52 -05:00
Dave Richer
bc6c889afc Merged in feature/io-2283-Add-Plate-Number-To-Global-Search (pull request #1053)
IO-2283 - Add Plate Number to global search

Approved-by: Patrick Fic
2023-11-15 21:42:09 +00:00
Dave Richer
33bcf250d0 Merged release/2023-11-17 into feature/io-2283-Add-Plate-Number-To-Global-Search 2023-11-15 21:32:04 +00:00
Dave Richer
bad32e069b IO-2283 - Add Plate Number to global search 2023-11-15 16:29:04 -05:00
Allan Carr
9acf20d4f3 Merged in feature/IO-1016-Registration-Expiry-for-CCs (pull request #1052)
IO-1016 Remove required rule for Registration Expiry date
2023-11-15 18:36:53 +00:00
Allan Carr
26f1ee0d89 Merged in feature/IO-2453-Clear-Bin-Location (pull request #1051)
IO-2453 Clear Bin Location
2023-11-15 18:36:36 +00:00
Allan Carr
9c86be8250 Merged in feature/IO-2448-CC-Search-by-Plate (pull request #1050)
IO-2448 Search by C/C Plate number
2023-11-15 18:36:20 +00:00
Allan Carr
4a06f9a686 Merged in feature/IO-2451-Non-Converted-Jobs-in-List (pull request #1049)
IO-2451 Non Converted Jobs in Lists
2023-11-15 18:36:04 +00:00
Allan Carr
98700f54b4 IO-1016 Remove required rule for Registration Expiry date 2023-11-14 17:55:57 -08:00
Patrick Fic
9a37cb5cb8 IO-1532 Re-enable hasura event. 2023-11-14 13:53:28 -08:00
Allan Carr
673d0bb7c5 IO-2453 Clear Bin Location 2023-11-14 12:15:09 -08:00
Allan Carr
04c7bc445b IO-2448 Search by C/C Plate number 2023-11-10 16:32:38 -08:00
Allan Carr
41782fe120 IO-2451 Non Converted Jobs in Lists
These lists should be converted jobs only
2023-11-10 16:01:17 -08:00
Patrick Fic
4e8e25a336 Merged in release/2023-11-10 (pull request #1048)
Release/2023 11 10
2023-11-10 19:21:15 +00:00
Patrick Fic
3c2d3156cb Merge branch 'release/2023-11-10' into test 2023-11-09 13:39:46 -08:00
Patrick Fic
0dac15391f Merge branch 'feature/IO-2445-block-types' into release/2023-11-10 2023-11-09 13:39:21 -08:00
Patrick Fic
106534b59b IO-2445 Updated blocked day types. 2023-11-09 13:39:05 -08:00
Allan Carr
19c0553746 Merged in release/2023-11-10 (pull request #1047)
Release/2023 11 10
2023-11-09 21:23:41 +00:00
Patrick Fic
5f1475d2ec Merge branch 'feature/IO-2444-single-device-login' into release/2023-11-10 2023-11-08 12:08:32 -08:00
Patrick Fic
84f4d5956a IO-2444 update fingerprint JS and re-enable using shop flag. 2023-11-08 12:08:21 -08:00
Allan Carr
4330ddd926 Merged in feature/IO-2438-All-Employee-Scoreboard-Summary (pull request #1046)
IO-2438 All Employee Scoreboard
2023-11-08 17:15:37 +00:00
Allan Carr
0711210512 IO-2438 All Employee Scoreboard
A-G Autobody request
2023-11-08 09:13:16 -08:00
Patrick Fic
458ec76835 Merge branch 'feature/IO-2437-CDK-make-override' into release/2023-11-10 2023-11-07 12:36:40 -08:00
Patrick Fic
682ea860fb IO-2437 override CDK make model on update. 2023-11-07 12:36:30 -08:00
Patrick Fic
99977934e7 IO-2429 Remove CIECA information on duplication. 2023-11-07 09:24:21 -08:00
Patrick Fic
3e05b21c90 IO-2426 Add FCM Cache update for conversation aggregate count. 2023-11-07 08:55:59 -08:00
Patrick Fic
4e1dd52bea IO-2332 filter insurance company name on convert. 2023-11-06 15:07:26 -08:00
Patrick Fic
f6bcc743d8 IO-2330 Remove phone validation for vendor sav.e 2023-11-06 15:04:01 -08:00
Allan Carr
7472285641 Merged in release/2023-11-03 (pull request #1043)
Release/2023 11 03
2023-11-03 16:45:23 +00:00
Allan Carr
d747594e39 Merged in release/2023-11-03 (pull request #1039)
Release/2023 11 03
2023-11-02 22:47:29 +00:00
Allan Carr
67ff9f30c6 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1042)
IO-1559 Change File name as per Koyel
2023-11-01 17:28:30 +00:00
Allan Carr
a27092dbcc IO-1559 Change File name as per Koyel 2023-11-01 10:28:10 -07:00
Allan Carr
ca41bff446 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1041)
Feature/IO-1559 ClaimsCorp Datapump
2023-11-01 16:58:32 +00:00
Allan Carr
cf8280590c IO-1559 Prettyier 2023-10-31 12:36:39 -07:00
Allan Carr
b649ca1f00 IO-1559 Additional XML Tag adjustments 2023-10-31 12:35:18 -07:00
Allan Carr
b441301007 Merged in feature/IO-2430-Open-Orders-by-Referral (pull request #1040)
IO-2430 Open Orders by Referral
2023-10-31 00:08:53 +00:00
Allan Carr
2e93238b5c IO-2430 Open Orders by Referral 2023-10-30 17:08:22 -07:00
Allan Carr
ce4fe84536 Merged in feature/IO-2431-Created-By-for-Appointments (pull request #1038)
IO-2431 Created By for Appointments
2023-10-30 23:52:36 +00:00
Allan Carr
9b7c0af025 IO-2431 Created By for Appointments 2023-10-30 16:51:26 -07:00
Allan Carr
dfdaf36ed1 Merged in feature/2023-10-27 (pull request #1037)
Feature/2023 10 27
2023-10-27 17:38:10 +00:00
Allan Carr
cc8d1b3793 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1035)
IO-1559 Requested changes to tags from ClaimsCorp
2023-10-27 16:13:30 +00:00
Allan Carr
a33bfedbb8 Merged in feature/2023-10-27 (pull request #1034)
Feature/2023 10 27
2023-10-27 15:24:30 +00:00
Allan Carr
eb359d83c5 IO-1559 Requested changes to tags from ClaimsCorp 2023-10-25 17:01:03 -07:00
Allan Carr
ae13e9e36a Merged in feature/IO-2421-Production-Note-to-Initial-Value-for-Intake (pull request #1032)
IO-2421 Add Production Vars to the initial values on CheckList
2023-10-25 15:52:49 +00:00
Allan Carr
4a62ac2a11 IO-2421 Add Production Vars to the initial values on CheckList 2023-10-25 08:52:03 -07:00
Allan Carr
016a62b6d5 Merged in feature/IO-2420-Preferred-Vendor-for-Bills (pull request #1029)
IO-2420 Prefered Vendor for Bill Entry
2023-10-23 19:30:25 +00:00
Allan Carr
3e226b50ab Merged in feature/IO-2419-Thumbnail-Emailing (pull request #1030)
IO-2419 Correct for Cloudinary to include fullsize on send
2023-10-23 19:30:11 +00:00
Allan Carr
ab84cb5ada IO-2419 Correct for Cloudinary to include fullsize on send 2023-10-23 12:32:32 -07:00
Allan Carr
7825aa4122 IO-2420 Prefered Vendor for Bill Entry 2023-10-20 13:55:21 -07:00
Patrick Fic
8d43fbfcd9 Merged in release/2023-10-20 (pull request #1028)
Release/2023 10 20
2023-10-20 18:51:19 +00:00
Patrick Fic
47a01628d3 Merge branch 'feature/IO-2401-duplicate-chat-creation' into release/2023-10-20 2023-10-20 08:50:52 -07:00
Patrick Fic
c008660023 Resolve error on multiple conversations found saga. 2023-10-20 08:50:31 -07:00
Allan Carr
5da34cbeac Merged in release/2023-10-20 (pull request #1027)
Release/2023 10 20

Approved-by: Patrick Fic
2023-10-20 15:37:26 +00:00
Patrick Fic
5b29aec14b Resolve vehicle search select issue. 2023-10-19 15:17:22 -07:00
Allan Carr
ca6aa682f6 Merged in feature/IO-2416-Autohouse_ClaimsCorp-Query (pull request #1017)
IO-2416 Filter out "" from ClaimsCorp and Autohouse get shop queries
2023-10-19 19:45:23 +00:00
Allan Carr
eb3786cebf Merged in feature/IO-2419-Thumbnail-Emailing (pull request #1025)
IO-2419 Thumbnails being emailed instead of Full Size Image
2023-10-19 19:45:11 +00:00
Allan Carr
53843e22a4 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1016)
IO-1559 Add in extra required tag
2023-10-19 19:44:58 +00:00
Patrick Fic
e1693674ca Merged in feature/IO-2418-jobline-bill-amount (pull request #1026)
Add refetch of job when posting bill.
2023-10-19 16:34:56 +00:00
Patrick Fic
2d2190e4fa Add refetch of job when posting bill. 2023-10-19 09:34:35 -07:00
Allan Carr
02fd8097a8 IO-2419 Thumbnails being emailed instead of Full Size Image 2023-10-19 09:30:29 -07:00
Patrick Fic
fcfbc85683 Merged in feature/IO-2418-jobline-bill-amount (pull request #1024)
IO-2418 Adjust latest bill amount
2023-10-19 16:23:43 +00:00
Patrick Fic
802dd696f4 IO-2418 Adjust latest bill amount 2023-10-19 09:20:29 -07:00
Allan Carr
60a0222dd0 IO-2416 Filter out "" from ClaimsCorp and Autohouse get shop queries 2023-10-18 10:55:08 -07:00
Allan Carr
9114abd3ef IO-1559 Add in extra required tag 2023-10-18 10:48:47 -07:00
Patrick Fic
f7fc0e6a6d Merged in feature/IO-2401-duplicate-chat-creation (pull request #1013)
IO-2401 Add prevention of duplicate chat creation by adding loading tracking.
2023-10-16 19:03:42 +00:00
Patrick Fic
ffebbe3b2a IO-2401 Add prevention of duplicate chat creation by adding loading tracking. 2023-10-16 12:00:11 -07:00
Patrick Fic
25b8c1b1eb Merged in feature/IO-2414-prod-list-delete (pull request #1012)
IO-2414 add null handling in case employee records are null.
2023-10-16 18:40:50 +00:00
Patrick Fic
17f8625108 IO-2414 add null handling in case employee records are null. 2023-10-16 11:40:23 -07:00
Patrick Fic
e3c21f0373 Merge branch 'release/2023-10-20' of bitbucket.org:snaptsoft/bodyshop into release/2023-10-20 2023-10-16 10:33:47 -07:00
Patrick Fic
859ff00277 Merge remote-tracking branch 'origin/master' into release/2023-10-20 2023-10-16 10:33:32 -07:00
Patrick Fic
e7c3be5231 Merged in release/2023-09-23 (pull request #1011)
Fix search select issues.
2023-10-16 17:24:46 +00:00
Patrick Fic
85e3c5a433 Fix search select issues. 2023-10-16 10:24:16 -07:00
Patrick Fic
d8d5cde3f1 Merged in feature/IO-2411-active-employee-filtering (pull request #1010)
IO-2411 filter inactive employees on prod. print button
2023-10-16 16:35:44 +00:00
Patrick Fic
6efa08fee3 IO-2411 filter inactive employees on prod. print button 2023-10-16 09:34:53 -07:00
Patrick Fic
636c13373c Merged in release/2023-09-23 (pull request #1009)
Release/2023 09 23
2023-10-16 16:19:04 +00:00
Patrick Fic
3659fbec84 Merged in 2023-09-29 (pull request #1004)
2023 09 29
2023-10-13 14:53:24 +00:00
Allan Carr
05f1a9b280 IO-1559 Adjust count object to new field tag 2023-10-12 14:54:58 -07:00
Allan Carr
34b4baac3d Merged in 2023-09-29 (pull request #1006)
IO-1559 Adjust count object to new field tag
2023-10-12 21:53:29 +00:00
Allan Carr
5884d5eba0 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1005)
IO-1559 Adjust count object to new field tag
2023-10-12 21:52:54 +00:00
Allan Carr
6b4709b76b Merged in 2023-09-29 (pull request #1003)
2023 09 29

Approved-by: Patrick Fic
2023-10-12 19:41:37 +00:00
Allan Carr
4dd2137006 IO-1559 Change xml label as per Claimscorp 2023-10-12 11:24:24 -07:00
Allan Carr
03315836a6 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #1002)
IO-1559 Change xml label as per Claimscorp
2023-10-12 18:23:39 +00:00
Patrick Fic
f703ba2cf9 Disable unnecessary events for Hasura. 2023-09-29 12:36:29 -07:00
Patrick Fic
dc05e4e166 Add CIECA PFO object to job. 2023-09-26 12:58:33 -07:00
Allan Carr
4513acc640 Merged in release/2023-09-23 (pull request #991)
IO-2408 Remove unneeded library
2023-09-22 18:50:44 +00:00
Allan Carr
612e359d4c Merged in feature/IO-2408-Speedprint-TZ (pull request #990)
IO-2408 Remove unneeded library
2023-09-22 18:50:13 +00:00
Allan Carr
c8fb1ce302 IO-2408 Remove unneeded library 2023-09-22 11:51:41 -07:00
Allan Carr
b29d8e1912 IO-2408 Use bodyshop.timezone instead of UTCOffset in Speedprint 2023-09-22 11:34:09 -07:00
Allan Carr
f1aa7944a3 Merged in release/2023-09-23 (pull request #989)
IO-2408 Use bodyshop.timezone instead of UTCOffset in Speedprint
2023-09-22 18:33:50 +00:00
Allan Carr
74b6c2b6b5 Merged in feature/IO-2408-Speedprint-TZ (pull request #988)
IO-2408 Use bodyshop.timezone instead of UTCOffset in Speedprint
2023-09-22 18:33:25 +00:00
Allan Carr
4313cf471f Merged in release/2023-09-23 (pull request #987)
Release/2023 09 23
2023-09-22 15:27:51 +00:00
Allan Carr
fce8039dad Merged in feature/IO-2400-Courtesy-Car-Color-All-Cars (pull request #986)
IO-2400 Add Color column to All Courtesy Cars
2023-09-22 15:27:13 +00:00
Allan Carr
085ae141ae Merged in feature/IO-2407-InHouse-Audit-Log (pull request #984)
IO-2407 In House Invoice Audit Log correction for Invoice Number
2023-09-22 15:27:02 +00:00
Allan Carr
fbc9ccc018 Merged in feature/IO-2363-Labour-Allocation-Summary-Color-Coding (pull request #985)
IO-2363 Make Summary.Difference follow Color logic
2023-09-22 15:26:47 +00:00
Allan Carr
f2ede519d7 Merged in feature/IO-2398-Hours-Sold-By-Estimator (pull request #983)
IO-2398 Hours Sold by Estimator
2023-09-22 15:26:39 +00:00
Allan Carr
21c53473d3 IO-2400 Add Color column to All Courtesy Cars 2023-09-20 15:58:48 -07:00
Allan Carr
fbc622fa04 IO-2363 Make Summary.Difference follow Color logic 2023-09-20 14:30:53 -07:00
Allan Carr
6319fd20fa IO-2407 In House Invoice Audit Log correction for Invoice Number 2023-09-20 12:16:03 -07:00
Allan Carr
c998e4901f IO-2398 Hours Sold by Estimator 2023-09-18 18:25:05 -07:00
Patrick Fic
06c35a4ff8 Add additonal query limits and restrictors for performance. 2023-09-18 16:08:20 -07:00
Patrick Fic
64851047bf Merged in release/2023-09-15 (pull request #980)
Release/2023 09 15
2023-09-15 17:40:34 +00:00
Patrick Fic
171c0b2b5a Merged in release/2023-09-15 (pull request #979)
Refactor payments for intellipay.
2023-09-15 17:39:16 +00:00
Patrick Fic
73fac34ef4 Merge branch 'feature/intellipay' into release/2023-09-15 2023-09-15 10:38:51 -07:00
Patrick Fic
5ca34105ef Refactor payments for intellipay. 2023-09-15 10:37:09 -07:00
Allan Carr
fd4820336f Merged in release/2023-09-15 (pull request #977)
IO-2395 Adjust Payment Number Label
2023-09-15 16:14:40 +00:00
Allan Carr
fcfa1a9be8 Merged in feature/IO-2395-Payment-Expansion-Formating (pull request #976)
IO-2395 Adjust Payment Number Label
2023-09-15 16:14:16 +00:00
Allan Carr
41849644f3 IO-2395 Adjust Payment Number Label 2023-09-15 09:15:34 -07:00
Allan Carr
46840266ee Merged in release/2023-09-15 (pull request #975)
Release/2023 09 15
2023-09-15 16:05:02 +00:00
Allan Carr
f36fb06dd6 Merged in feature/IO-2368-QBO-Successful-Export (pull request #972)
IO-2368 Confirm that there are successful transaction
2023-09-15 16:04:15 +00:00
Allan Carr
af4c4a4fa3 Merged in feature/IO-2395-Payment-Expansion-Formating (pull request #973)
IO-2395 Payment Expansion Formatting
2023-09-15 16:03:59 +00:00
Patrick Fic
d6045a9334 Merge branch 'feature/IO-2399-import-status' into release/2023-09-15 2023-09-14 11:19:05 -07:00
Patrick Fic
dcc29f23d4 Respect default import status on new creation. 2023-09-14 11:18:40 -07:00
Patrick Fic
638a9fc76b Add CIECA PFT to job. 2023-09-11 11:45:35 -07:00
Allan Carr
53e3b3fa03 IO-2395 Payment Expansion Formatting 2023-09-11 11:15:50 -07:00
Allan Carr
56c1b6f992 IO-2368 Confirm that there are successful transaction 2023-09-11 09:07:38 -07:00
Allan Carr
22f9a7ee3d Merged in release/2023-09-01 (pull request #971)
Release/2023 09 01
2023-09-08 20:50:50 +00:00
Allan Carr
82a0d287d6 Merged in release/2023-09-01 (pull request #970)
IO-2391 Remote IP comment out till proxy for X-Forwarded-For can be figured out from AWS Load balancer
2023-09-08 20:17:48 +00:00
Allan Carr
cfcad472fd Merged in feature/IO-2391-IP-Address-for-Check (pull request #969)
IO-2391 Remote IP comment out till proxy for X-Forwarded-For can be figured out from AWS Load balancer
2023-09-08 20:17:19 +00:00
Allan Carr
1a622f1b2c IO-2391 Remote IP comment out till proxy for X-Forwarded-For can be figured out from AWS Load balancer 2023-09-08 13:18:20 -07:00
Allan Carr
4de604ef7c Merged in release/2023-09-01 (pull request #968)
IO-2368 Move Cache update to function and just pass in keys array
2023-09-08 17:18:06 +00:00
Allan Carr
87e3adf579 IO-2368 Move Cache update to function and just pass in keys array 2023-09-08 10:17:42 -07:00
Allan Carr
aa7a4ccdd0 Merged in feature/IO-2368-QBO-Successful-Export (pull request #967)
IO-2368 Move Cache update to function and just pass in keys array
2023-09-08 17:16:50 +00:00
Allan Carr
ec2c26ca69 Merged in release/2023-09-01 (pull request #962)
Release/2023 09 01

Approved-by: Patrick Fic
2023-09-08 01:35:06 +00:00
Allan Carr
28dc10f5a1 Merged in feature/IO-2368-QBO-Successful-Export (pull request #966)
IO-2368 Update Cache on success
2023-09-08 00:23:57 +00:00
Allan Carr
b2d615b9c1 Merged in feature/IO-2392-Ticket-Date-UTCOffset (pull request #965)
IO-2392 Ticket Date UTCOffset
2023-09-08 00:22:28 +00:00
Allan Carr
1e40a22762 IO-2392 Ticket Date UTCOffset 2023-09-07 17:23:08 -07:00
Allan Carr
d1407162d9 IO-2368 Update Cache on success 2023-09-07 17:21:21 -07:00
Allan Carr
7a1984d037 IO-2368 Update Cache on QBD posting success 2023-09-06 16:23:58 -07:00
Allan Carr
9bcc449f20 Merged in feature/IO-2368-QBO-Successful-Export (pull request #964)
IO-2368 Update Cache on QBD posting success
2023-09-06 23:23:53 +00:00
Allan Carr
bc7d0ef171 IO-2391 Add IP address to Server API Check 2023-09-06 09:06:05 -07:00
Allan Carr
3e9b046476 Merged in feature/IO-2391-IP-Address-for-Check (pull request #963)
IO-2391 Add IP address to Server API Check
2023-09-06 16:05:57 +00:00
Allan Carr
e0ccd62c82 Merged in feature/IO-1559-ClaimsCorp-Datapump (pull request #956)
IO-1559 ClaimsCorp Datapump
2023-09-01 15:47:25 +00:00
Allan Carr
fd0970aef2 Merged in feature/IO-2365-Correct-LAU-Transation (pull request #961)
IO-2365 LAU Translation adjust from Detail to User Defined
2023-09-01 15:47:08 +00:00
Allan Carr
b673bcae7a IO-2365 LAU Translation adjust from Detail to User Defined 2023-09-01 08:46:52 -07:00
Allan Carr
63673548a0 IO-1559 ClaimsCorp Datapump 2023-08-29 17:51:34 -07:00
Patrick Fic
29b74a8c0e Resolve issue when selecting lines. 2023-08-29 14:12:38 -07:00
Patrick Fic
2658626c7e Add CIECA PFL info. 2023-08-28 12:05:36 -07:00
Allan Carr
763b199646 Merged in release/2023-08-25 (pull request #954)
Release/2023 08 25
2023-08-25 19:49:35 +00:00
Allan Carr
026ce853e2 Merged in release/2023-08-25 (pull request #952)
Release/2023 08 25
2023-08-25 18:09:40 +00:00
Allan Carr
17905fa844 Merged in feature/IO-2389-Add-Metadata-of-Transaction-Wip (pull request #953)
IO-2389 Metadata to ExportLog
2023-08-25 18:05:01 +00:00
Allan Carr
74a0b78a71 IO-2389 Metadata to ExportLog 2023-08-25 11:05:16 -07:00
Allan Carr
797a423702 Merged in feature/IO-2368-QBO-Successful-Export (pull request #945)
IO-2368 Successful Export Notification for QBO
2023-08-25 17:24:38 +00:00
Allan Carr
2fce8c9644 Merged in feature/IO-2385-Tech-Console-Attendance-Report (pull request #946)
IO-2385 Enabled Emailing of TimeTicket & Attendance from Tech Console
2023-08-25 17:24:23 +00:00
Patrick Fic
9cd39c1c3e Resolve manual appointment old data after submission. 2023-08-25 10:23:22 -07:00
Patrick Fic
720b7f891b Merged in release/2023-08-25 (pull request #951)
Release/2023 08 25
2023-08-25 16:46:10 +00:00
Patrick Fic
6264a2f45c Merge branch 'feature/intellipay' into release/2023-08-25 2023-08-23 13:54:39 -07:00
Patrick Fic
94e47d14ad Add server side logging for intellipay. 2023-08-23 13:26:17 -07:00
Allan Carr
9319f492dd IO-2385 Enabled Emailing of TimeTicket & Attendance from Tech Console
Modify print center so that it will/wont display email option if technician is set and production board detail so that it wont display the remove from production / add to scoreboard if technician is set
2023-08-22 13:29:28 -07:00
Allan Carr
8f04c5a12c IO-2368 Successful Export Notification for QBO 2023-08-22 09:15:10 -07:00
Allan Carr
436a41405d Merged in release/2023-08-18 (pull request #944)
Release/2023 08 18
2023-08-18 21:41:04 +00:00
Allan Carr
b5332458ec Merged in release/2023-08-18 (pull request #936)
Release/2023 08 18
2023-08-18 21:00:11 +00:00
Allan Carr
a2150009db IO-2385 Tech Console to print Attendance Report from Shift Clock 2023-08-18 13:59:42 -07:00
Allan Carr
e1c785322f Merged in feature/IO-2385-Tech-Console-Attendance-Report (pull request #943)
IO-2385 Tech Console to print Attendance Report from Shift Clock
2023-08-18 20:59:23 +00:00
John Allen Delos Reyes
c1d71720ab Merged in feature/IO-2380-parts-order-receive-modal (pull request #934)
Feature/IO-2380 parts order receive modal

Approved-by: Patrick Fic
2023-08-18 18:16:04 +00:00
Allan Carr
89ff7740e2 Merged in feature/IO-2325-Ticket-Create-By (pull request #942)
IO-2325 Add in Created by from Action menu
2023-08-18 16:02:56 +00:00
Allan Carr
4e69fe819e IO-2325 Add in Created by from Action menu 2023-08-18 09:03:13 -07:00
Allan Carr
a8cc3fa190 Merged in feature/IO-2384-Owner-Related-Job-Default-Sort-Order (pull request #939)
IO-2384 Default Sort Order for Related Jobs
2023-08-17 23:19:03 +00:00
Allan Carr
10fceb7ddf IO-2384 Extend to Vehicle Related ROs 2023-08-17 16:18:56 -07:00
Allan Carr
6c1a0cff8d IO-2384 Default Sort Order for Related Jobs 2023-08-17 16:05:33 -07:00
Allan Carr
d92d2cca9a Merged in feature/IO-2247-Dashboard-Components (pull request #938)
IO-2247 Don't push manual appointments into component if they don't have a job associated.
2023-08-17 16:32:37 +00:00
Allan Carr
ea54820bc0 IO-2247 Don't push manual appointments into component if they don't have a job associated. 2023-08-17 09:32:32 -07:00
Allan Carr
62a800a2c0 Merged in feature/IO-2381-Disabled-Cancel-Appt-Button (pull request #937)
IO-2381 Disabled Cancel Appt Button
2023-08-17 01:37:20 +00:00
Allan Carr
9c408d8bf5 IO-2381 Disabled Cancel Appt Button 2023-08-16 18:37:19 -07:00
Allan Carr
45ad09c100 Merged in feature/IO-2383-Timeticket-Employee-Rangefilter (pull request #935)
IO-2383 TimeTicket Employee Range Filter
2023-08-17 00:25:11 +00:00
Allan Carr
dd5cafcd42 IO-2383 TimeTicket Employee Range Filter 2023-08-16 17:25:32 -07:00
Patrick Fic
eb48b56f47 Updates to SMS payments. 2023-08-16 16:14:34 -07:00
Patrick Fic
a879e99e77 Fix postback handling. 2023-08-16 15:28:05 -07:00
swtmply
ddd816e7ca IO-2380 added translation to fields 2023-08-17 06:15:46 +08:00
swtmply
d646e5f285 IO-2380 added part number and price 2023-08-16 11:43:21 +08:00
Patrick Fic
c10517a11b Intellipay improvements with Allan 2023-08-15 14:59:35 -07:00
Allan Carr
ba683a2e8a Merged in release/2023-08-11 (pull request #931)
Release/2023 08 11
2023-08-11 20:36:11 +00:00
Patrick Fic
6b66b76f84 Cleanup imports. 2023-08-11 13:13:26 -07:00
Patrick Fic
c3fe763261 Improvements to intellipay script. 2023-08-11 09:48:39 -07:00
Allan Carr
3c7b16412b Merged in release/2023-08-11 (pull request #929)
IO-2325 Shift Clock Created By
2023-08-11 16:05:39 +00:00
Allan Carr
5209c12b89 Merged in feature/IO-2325-Ticket-Create-By (pull request #928)
IO-2325 Shift Clock Created By
2023-08-11 15:36:06 +00:00
Allan Carr
bf7aa17f65 IO-2325 Shift Clock Created By 2023-08-11 08:35:57 -07:00
Allan Carr
a454c57bc9 Merged in release/2023-08-11 (pull request #927)
Release/2023 08 11
2023-08-10 19:07:49 +00:00
Allan Carr
cd6e0dcde3 Merged in feature/IO-2325-Ticket-Create-By (pull request #926)
IO-2325 Time Ticket Creation Date
2023-08-10 19:06:58 +00:00
Allan Carr
a2822f5592 Merged in feature/IO-2376-Inactive-Employee-Login (pull request #925)
IO-2376 Prevent Inactive Employee from logging in
2023-08-10 19:06:49 +00:00
Allan Carr
ca129fa4a0 IO-2325 Time Ticket Creation Date 2023-08-10 12:06:47 -07:00
Patrick Fic
cbe0c78553 Update to secrets retrieval. 2023-08-09 20:42:37 -07:00
Patrick Fic
2e763f1dd5 Minor updates to intellipay processing. 2023-08-09 20:14:44 -07:00
Patrick Fic
b8942c320e Merge branch 'master' into feature/intellipay 2023-08-09 19:48:33 -07:00
Allan Carr
eee135f4ef IO-2376 Prevent Inactive Employee from logging in 2023-08-09 18:01:28 -07:00
Allan Carr
de92b2d47e Merged in feature/IO-2375-Job-Costing-by-CSR-Filter-Label (pull request #924)
IO-2375 Filter label for Job Costing by CSR correction
2023-08-09 22:26:09 +00:00
Allan Carr
5d7384aa8b IO-2375 Filter label for Job Costing by CSR correction 2023-08-09 15:25:58 -07:00
Patrick Fic
bf18e687da Schema updates for parts dispatch. 2023-08-04 12:36:08 -07:00
Allan Carr
e69e844568 Merged in release/2023-08-04 (pull request #920)
Release/2023 08 04
2023-08-04 18:06:05 +00:00
Allan Carr
86ff7cbc69 Merged in release/2023-08-04 (pull request #919)
Release/2023 08 04
2023-08-04 17:36:26 +00:00
Allan Carr
2b5268fb77 Merged in feature/IO-2371-Closing-Period (pull request #918)
IO-2371 Closing Period
2023-08-04 17:35:45 +00:00
Allan Carr
eebe7edba8 IO-2371 Closing period timezone adjustments 2023-08-04 10:36:01 -07:00
Allan Carr
1a5c74dc79 IO-2371 Closing Period 2023-08-04 09:05:03 -07:00
Allan Carr
fbfdbc903c Merged in release/2023-08-04 (pull request #917)
IO-2370 Work In Progress Jobs
2023-08-02 23:38:22 +00:00
Allan Carr
be62ab5ff9 Merged in feature/IO-2370-Work-In-Progress-Jobs (pull request #916)
IO-2370 Work In Progress Jobs
2023-08-02 23:37:51 +00:00
Allan Carr
85497eb815 IO-2370 Work In Progress Jobs 2023-08-02 16:36:47 -07:00
Allan Carr
5724d0129c Merged in release/2023-07-28 (pull request #913)
Release/2023 07 28
2023-07-29 01:10:53 +00:00
Allan Carr
9e64fdc985 Merged in release/2023-07-28 (pull request #912)
Release/2023 07 28
2023-07-28 20:01:26 +00:00
Allan Carr
fd579fc509 Merged in feature/IO-2366-Scoreboard-Sales-Calculation (pull request #911)
IO-2366 Scoreboard Sales Calculation correction of formula
2023-07-28 20:00:15 +00:00
Allan Carr
6e21b1bdf6 IO-2366 Scoreboard Sales Calculation correction of formula 2023-07-28 12:59:10 -07:00
Patrick Fic
a7ad18fae2 Timeticket schema update to include task_name 2023-07-27 11:23:44 -07:00
Allan Carr
d7c23297ab Merged in release/2023-07-28 (pull request #907)
Release/2023 07 28
2023-07-26 23:45:22 +00:00
Allan Carr
8bfa879485 Merged in feature/IO-2364-UnInvoice-Audit-Trail (pull request #906)
Feature/IO-2364 UnInvoice Audit Trail
2023-07-26 23:44:44 +00:00
Allan Carr
ea774ff22b IO-2364 UnInvoice Audit Trail 2023-07-26 16:45:00 -07:00
Allan Carr
88101b0252 IO-2364 UnInvoice Audit Trail 2023-07-26 16:36:51 -07:00
Allan Carr
f6e095e0a6 Merged in release/2023-07-28 (pull request #905)
IO-2356 Auto CC for Parts Return
2023-07-24 22:44:46 +00:00
Allan Carr
60ec76701d Merged in feature/IO-2356-Auto-CC-for-Parts-Return (pull request #904)
IO-2356 Auto CC for Parts Return
2023-07-24 22:43:47 +00:00
Allan Carr
6b52723ba9 IO-2356 Auto CC for Parts Return 2023-07-24 15:43:34 -07:00
Allan Carr
d7ec2e717c Merged in release/2023-07-28 (pull request #903)
Release/2023 07 28
2023-07-24 20:02:19 +00:00
Allan Carr
910c2a0f9b Merged in feature/IO-2117-Void-Date (pull request #902)
IO-2117 Void Date Add to table
2023-07-21 23:02:06 +00:00
Allan Carr
6c93e600c4 Merge branch 'release/2023-07-28' into feature/IO-2117-Void-Date 2023-07-21 15:58:30 -07:00
Allan Carr
e70edaec7c IO-2117 Void Date 2023-07-21 15:50:49 -07:00
Allan Carr
acaba96e3b IO-2117 Void Date Add to table 2023-07-21 14:08:42 -07:00
Allan Carr
12d1613b04 Merged in release/2023-07-21 (pull request #901)
Release/2023 07 21
2023-07-21 17:16:26 +00:00
Allan Carr
35fd74d3fe Merged in release/2023-07-21 (pull request #900)
Release/2023 07 21
2023-07-21 17:09:31 +00:00
Allan Carr
df878672fc Merged in feature/IO-2354-Available-Cars-Table (pull request #899)
IO-2354 Add Color to Available C/C Table
2023-07-21 17:07:38 +00:00
Patrick Fic
076115253f Merged in master (pull request #898)
Merged in feature/IO-2170-Tech-Console-Job-Clock-Out-Status (pull request #894)
2023-07-21 17:06:47 +00:00
Allan Carr
6f58528de2 IO-2354 Add Color to Available C/C Table 2023-07-21 09:54:20 -07:00
Patrick Fic
3defe7201f Add compelted tasks to jobs table. 2023-07-19 16:11:26 -07:00
Patrick Fic
4ce75ead52 Merged in feature/IO-2170-Tech-Console-Job-Clock-Out-Status (pull request #895)
IO-2170 Correct for Shift clock so Job Status is only on Job Clock
2023-07-17 17:11:03 +00:00
Patrick Fic
6de7ec00fe Merged in feature/IO-2170-Tech-Console-Job-Clock-Out-Status (pull request #894)
IO-2170 Correct for Shift clock so Job Status is only on Job Clock
2023-07-17 17:10:42 +00:00
Allan Carr
90ea2cd699 IO-2170 Correct for Shift clock so Job Status is only on Job Clock 2023-07-17 09:56:21 -07:00
Patrick Fic
800552210b Merged in release/2023-07-14 (pull request #893)
IO-2170 Job Status change on Job Clock Out
2023-07-14 23:14:51 +00:00
Patrick Fic
8325e2d9cf Merged in release/2023-07-14 (pull request #892)
IO-2349 Change control number for AP to allow RO.
2023-07-14 23:14:34 +00:00
Patrick Fic
80abea56b4 Merged in feature/IO-2349-pbs-ap-cogs-wip (pull request #891)
IO-2349 Change control number for AP to allow RO.
2023-07-13 16:28:30 +00:00
Patrick Fic
480f081c40 Merged in feature/IO-2349-pbs-ap-cogs-wip (pull request #890)
IO-2349 Change control number for AP to allow RO.
2023-07-13 16:28:05 +00:00
Patrick Fic
9529335c96 IO-2349 Change control number for AP to allow RO. 2023-07-13 09:27:37 -07:00
Allan Carr
cc3c1242f5 Merged in release/2023-07-14 (pull request #889)
IO-2170 Job Status change on Job Clock Out
2023-07-12 17:40:32 +00:00
Allan Carr
4334b3f419 Merged in feature/IO-2170-Tech-Console-Job-Clock-Out-Status (pull request #888)
IO-2170 Job Status change on Job Clock Out

Approved-by: Patrick Fic
2023-07-12 17:29:49 +00:00
Allan Carr
94353bb342 IO-2170 Job Status change on Job Clock Out 2023-07-12 09:42:03 -07:00
Patrick Fic
5aad7acdd5 Merged in feature/IO-2349-pbs-ap-cogs-wip (pull request #887)
IO-2349 Allow PBS AP Posting to WIP
2023-07-12 16:39:28 +00:00
Patrick Fic
b2f4a5539c Merged in feature/IO-2349-pbs-ap-cogs-wip (pull request #886)
IO-2349 Allow PBS AP Posting to WIP
2023-07-12 16:39:01 +00:00
Patrick Fic
cd4f7ffb9c Merged in feature/IO-2349-pbs-ap-cogs-wip (pull request #885)
IO-2349 Allow PBS AP Posting to WIP
2023-07-11 16:16:27 +00:00
Patrick Fic
400dc79ed6 IO-2349 Allow PBS AP Posting to WIP 2023-07-11 09:10:26 -07:00
Allan Carr
1dfb309223 Merged in test (pull request #883)
Test
2023-07-07 18:53:53 +00:00
Allan Carr
29c9fb37a1 Merged in release/2023-07-07 (pull request #882)
Release/2023 07 07
2023-07-07 16:55:39 +00:00
Allan Carr
41d6f0a4bc Merged in feature/IO-2337-billlines-applicable_taxes (pull request #881)
IO-2337 Spread in billLines
2023-07-07 16:54:48 +00:00
Allan Carr
af70c80e09 IO-2337 Spread in billLines 2023-07-07 09:54:19 -07:00
Patrick Fic
b02d4e0fdd IO-2206 Missed schema update. 2023-07-06 13:51:23 -07:00
Patrick Fic
27bf8d9ed6 IO-2206 Payroll schema changes to assign lines to teams. 2023-07-06 13:22:22 -07:00
Allan Carr
582ad03e05 Merged in release/2023-07-07 (pull request #880)
Release/2023 07 07
2023-07-04 17:34:47 +00:00
Allan Carr
babdfe4cc5 Merged in feature/IO-2247-Dashboard-Components (pull request #879)
Feature/IO-2247 Dashboard Components
2023-07-04 17:33:13 +00:00
Allan Carr
8a7a94dd70 Merged in feature/IO-2337-billlines-applicable_taxes (pull request #878)
IO-2337 Billline.applicible_taxes=null and billline.applicible_taxes on entry data cleanup
2023-07-04 17:32:22 +00:00
Allan Carr
2654519277 Merged in release/2023-07-07 (pull request #877)
IO-2342 Trim Comany Name after SubString
2023-06-29 17:46:21 +00:00
Allan Carr
02eddcbbf4 Merged in feature/IO-2342-QBD-GenerateOwnerName-Trim (pull request #876)
IO-2342 Trim Comany Name after SubString
2023-06-29 17:45:08 +00:00
Allan Carr
b967bb6d4e IO-2342 Trim Comany Name after SubString 2023-06-29 10:45:52 -07:00
Allan Carr
f60870a087 Merged in release/2023-07-07 (pull request #875)
IO-2342 Billing and Shipping Addr1 Trim
2023-06-29 16:58:40 +00:00
Allan Carr
39aa21d985 IO-2342 Billing and Shipping Addr1 Trim 2023-06-29 09:58:20 -07:00
Allan Carr
512bb5e013 Merged in feature/IO-2342-QBD-GenerateOwnerName-Trim (pull request #874)
IO-2342 Billing and Shipping Addr1 Trim
2023-06-29 16:58:12 +00:00
Allan Carr
7581b8634e IO-2247 Scheduled Out Today 2023-06-27 08:35:13 -07:00
Allan Carr
78f041a34f Merged in release/2023-07-07 (pull request #873)
Release/2023 07 07
2023-06-26 15:27:53 +00:00
Allan Carr
2c456cbf03 Merged in feature/IO-2342-QBD-GenerateOwnerName-Trim (pull request #872)
IO-2342 QBD Owner Name with just LN Trim
2023-06-26 15:26:56 +00:00
Allan Carr
da76021802 Merged in feature/IO-2341-Jobs-Schedule-Completion (pull request #868)
IO-2341 Jobs Schedule Completion Report
2023-06-26 15:26:15 +00:00
Allan Carr
6de06e084b IO-2342 QBD Owner Name with just LN Trim
Removes extra space if there is just a OWNR_LN before the ACCT #
2023-06-23 08:53:14 -07:00
Allan Carr
cb49c91983 Merged in test (pull request #871)
IO-2340 CDK New Unsold Vehicle adjustments
2023-06-23 02:46:58 +00:00
Allan Carr
b0df5fa91c Merged in release/2023-07-07 (pull request #870)
IO-2340 CDK New Unsold Vehicle adjustments
2023-06-22 22:46:52 +00:00
Allan Carr
0652404334 Merged in feature/IO-2340-CDK-New-Unsold-Vehicle (pull request #869)
IO-2340 CDK New Unsold Vehicle adjustments
2023-06-22 22:43:26 +00:00
Allan Carr
bd7d8068df IO-2340 CDK New Unsold Vehicle adjustments 2023-06-22 15:38:18 -07:00
Allan Carr
4dd868130c IO-2341 Jobs Schedule Completion Report 2023-06-21 08:31:30 -07:00
Allan Carr
71860cf899 Merged in test (pull request #867)
Test
2023-06-20 17:49:27 +00:00
Allan Carr
3512905264 Merged in release/2023-07-07 (pull request #866)
IO-2340 CDK New Unsold Vehicle Option
2023-06-20 17:11:58 +00:00
Allan Carr
2c072a9e7a Merged in feature/IO-2340-CDK-New-Unsold-Vehicle (pull request #865)
IO-2340 CDK New Unsold Vehicle Option
2023-06-20 17:10:12 +00:00
Allan Carr
fee5bee569 IO-2340 CDK New Unsold Vehicle Option
Exports with no Delivery Date or In-Service Date
2023-06-20 10:03:49 -07:00
Allan Carr
0a1cdbdfe3 Merged in release/2023-07-07 (pull request #864)
IO-2338 Selection Color
2023-06-16 15:49:54 +00:00
Allan Carr
8af79989ff Merged in feature/IO-2338-Selection-Color (pull request #863)
IO-2338 Selection Color
2023-06-16 15:47:43 +00:00
Allan Carr
5d2bdc7ee1 IO-2338 Selection Color
Overrides nth-child color
2023-06-15 15:24:24 -07:00
Allan Carr
255d65e47d IO-2337 Billline.applicible_taxes=null and billline.applicible_taxes on entry data cleanup 2023-06-15 09:06:38 -07:00
Allan Carr
f0805e0a79 Merged in release/2023-07-07 (pull request #862)
IO-2336 Job Search Query and Autocomplete Query
2023-06-14 17:17:09 +00:00
Allan Carr
c875ade35c Merged in feature/IO-2336-Job-Selector-Misses-ownr_co_nm (pull request #861)
IO-2336 Job Search Query and Autocomplete Query
2023-06-14 17:13:05 +00:00
Allan Carr
31b4f4e561 IO-2336 Job Search Query and Autocomplete Query
Update queries to include ownr_co_nm
2023-06-13 16:23:23 -07:00
Allan Carr
9b485bfe45 IO-2247 Schedule In Today Component 2023-06-13 15:07:10 -07:00
Patrick Fic
91279c27fe Merged in release/2023-06-09 (pull request #858)
Release/2023 06 09
2023-06-09 22:35:38 +00:00
Patrick Fic
2c1844fb13 Merged in release/2023-06-09 (pull request #857)
Release/2023 06 09
2023-06-09 22:11:52 +00:00
Patrick Fic
3812a0650e Global search improvements. 2023-06-09 15:11:14 -07:00
Patrick Fic
b1c5bbb01f Change FK relaitonship. 2023-06-09 11:28:22 -07:00
Patrick Fic
bd59e40761 Merged in release/2023-06-09 (pull request #856)
Release/2023 06 09
2023-06-08 23:19:02 +00:00
Patrick Fic
f440a2b022 Improve update alert on mobile devices. 2023-06-08 16:18:12 -07:00
Patrick Fic
6262b3ff83 Revert changes for IO-2311 IO-2317. 2023-06-08 16:00:47 -07:00
Patrick Fic
0652114013 Merged in release/2023-06-09 (pull request #855)
Remove failing CI components.
2023-06-08 22:43:49 +00:00
Patrick Fic
307c77b30c Remove failing CI components. 2023-06-08 15:43:21 -07:00
Patrick Fic
3150647ff6 Merged in release/2023-06-09 (pull request #854)
Release/2023 06 09
2023-06-08 18:20:48 +00:00
Patrick Fic
f1d7a98fe8 Merge branch 'feature/IO-2208-chat-affix' into release/2023-06-09 2023-06-08 11:20:10 -07:00
Patrick Fic
be259317f9 Update payables posting label. 2023-06-08 11:13:35 -07:00
swtmply
046d104bfa IO-2208 removed subscriptions to the message 2023-06-09 01:04:59 +08:00
Patrick Fic
e2258bb91f Merged in release/2023-06-09 (pull request #852)
IO-2329 Remove testing statement.
2023-06-07 19:06:48 +00:00
Patrick Fic
9c693a2b74 Merged in feature/IO-2329-update-alert (pull request #851)
IO-2329 Remove testing statement.
2023-06-07 19:06:22 +00:00
Patrick Fic
2be0f3de09 IO-2329 Remove testing statement. 2023-06-07 12:05:55 -07:00
Patrick Fic
3dd4b3dd77 Merged in release/2023-06-09 (pull request #850)
Release/2023 06 09
2023-06-07 19:04:36 +00:00
Patrick Fic
de8c2cd5a2 Merged in feature/IO-2329-update-alert (pull request #849)
IO-2329 Change update alert to be permanent.
2023-06-07 19:03:48 +00:00
Patrick Fic
b791f9846f IO-2329 Change update alert to be permanent. 2023-06-07 12:03:17 -07:00
Patrick Fic
daa7631056 Integration IO-2278 schema changes made on wrong branch. 2023-06-07 11:23:00 -07:00
Patrick Fic
d2f7585ea5 Merged in release/2023-06-09 (pull request #848)
Release/2023 06 09
2023-06-07 18:16:35 +00:00
John Allen Delos Reyes
14b8a2daef Merged in feature/IO-2322-insurance-dropdown (pull request #844)
IO-2322 added search function to insurance dropdown

Approved-by: Patrick Fic
2023-06-07 18:14:31 +00:00
John Allen Delos Reyes
90b38d817d Merged in feature/IO-2311-messaging-tile-height (pull request #845)
IO-2311 fixed tile height

Approved-by: Patrick Fic
2023-06-07 18:14:25 +00:00
John Allen Delos Reyes
ace48e2890 Merged in feature/IO-2280-config-query-params (pull request #843)
IO-2280 fixed query params in shop config

Approved-by: Patrick Fic
2023-06-07 18:14:18 +00:00
John Allen Delos Reyes
fb810be5d5 Merged in feature/IO-2317-messaging-screen (pull request #846)
IO-2317 Messaging screen on mobile

Approved-by: Patrick Fic
2023-06-07 18:13:45 +00:00
Patrick Fic
50230e9f50 IO-2278 adjust schema for parts dispatch. 2023-06-06 13:41:38 -07:00
Patrick Fic
86e14967ca IO-2278 Additional permission for dispatch lines. 2023-06-06 13:38:51 -07:00
Patrick Fic
c45c3b4037 IO-2278 Add permissions for parts dispatch. 2023-06-06 13:33:03 -07:00
Patrick Fic
c4c11528b9 IO-2278 Add relationship tracking to parts dispatch. 2023-06-06 13:22:51 -07:00
Patrick Fic
1f9c4e92f1 IO-2278 Add parts dispatch relationship. 2023-06-06 13:21:40 -07:00
Patrick Fic
371e148e09 IO-2278 Track parts dispatch relationships. 2023-06-06 13:18:44 -07:00
swtmply
33af544ded IO-2322 added search function to insurance dropdown 2023-06-06 22:47:20 +08:00
swtmply
6b8d0ec91c IO-2311 fixed tile height 2023-06-06 22:39:39 +08:00
swtmply
5a3ddfad0f IO-2280 fixed query params in shop config 2023-06-06 22:20:22 +08:00
Allan Carr
043c44ed51 Merged in feature/IO-2281-table-colors (pull request #842)
IO-2281 Hover Row Color

Approved-by: Patrick Fic
2023-06-06 00:17:39 +00:00
Allan Carr
6d5dbf3145 IO-2281 Hover Row Color 2023-06-05 17:13:31 -07:00
swtmply
6d8463265c IO-2317 Messaging screen on mobile 2023-06-06 02:07:30 +08:00
Patrick Fic
0669282432 Merged in release/2023-06-02 (pull request #840)
Release/2023 06 02
2023-06-02 20:08:34 +00:00
Patrick Fic
1af511be2f Merged in release/2023-06-02 (pull request #836)
IO-2314 Added hyperlink to ro number
2023-06-02 15:58:35 +00:00
John Allen Delos Reyes
bf4dc7e158 Merged in feature/IO-2314-problem-job-hyperlink (pull request #835)
IO-2314 Added hyperlink to ro number

Approved-by: Patrick Fic
2023-06-02 15:55:13 +00:00
Patrick Fic
14b38604a3 Merged in release/2023-06-02 (pull request #834)
Release/2023 06 02
2023-06-02 14:37:51 +00:00
Allan Carr
5de2036fdb Merged in feature/IO-2316-Predefined-Email-Address-for-CC (pull request #833)
IO-2316 Predefined Email address for CC field

Approved-by: Patrick Fic
2023-06-02 14:37:28 +00:00
Allan Carr
1629663e15 Merged in feature/IO-2315-DMS-Posting-Sheet-Restriction (pull request #832)
IO-2315 DMS Posting Sheet

Approved-by: Patrick Fic
2023-06-02 14:36:32 +00:00
swtmply
e25f2db2b1 IO-2314 Added hyperlink to ro number 2023-06-02 22:27:53 +08:00
Allan Carr
cbf5d268ea IO-2315 DMS Posting Sheet
Restrict to DMS setups only
2023-06-01 20:32:30 -07:00
Patrick Fic
a92a95a9fa Merge branch 'release/2023-06-02' of bitbucket.org:snaptsoft/bodyshop into release/2023-06-02 2023-06-01 18:31:12 -07:00
Patrick Fic
0be7bf2c8e Merge branch 'feature/IO-2310-active-jobs-ro-sort' into release/2023-06-02 2023-06-01 18:29:53 -07:00
Patrick Fic
56b810dd40 IO-2310 Change RO sort. 2023-06-01 18:29:41 -07:00
Allan Carr
33cfa531b8 IO-2316 Predefined Email address for CC field 2023-06-01 17:34:55 -07:00
Patrick Fic
40c7b706aa Merged in release/2023-06-02 (pull request #830)
IO-2281 Added striped table colors
2023-06-01 22:33:44 +00:00
John Allen Delos Reyes
000ded6649 Merged in feature/IO-2281-table-colors (pull request #829)
IO-2281 Added striped table colors

Approved-by: Patrick Fic
2023-06-01 22:28:30 +00:00
Patrick Fic
72181e1ff7 Add in missing filter for job search select on job clock on. 2023-05-31 14:33:59 -07:00
swtmply
d73b1d2220 IO-2281 Added striped table colors 2023-06-01 02:34:18 +08:00
Patrick Fic
88c03ce655 Merged in release/2023-06-02 (pull request #828)
Release/2023 06 02
2023-05-31 15:30:33 +00:00
Allan Carr
8645b434c8 Merged in feature/IO-2299-RBAC-for-Voiding-File (pull request #827)
IO-2299 RBAC for Voiding File

Approved-by: Patrick Fic
2023-05-31 15:30:15 +00:00
Allan Carr
38a13bd082 IO-2299 RBAC for Voiding File 2023-05-30 20:14:33 -07:00
Allan Carr
3bc5f5d626 Merged in feature/IO-2233-CDK-VEHICLE-LICENSE-PLATE (pull request #826)
IO-2233 Check for null and if stripped string is size 0 send null instead

Approved-by: Patrick Fic
2023-05-31 00:33:20 +00:00
Allan Carr
86a2351316 IO-2233 Check for null and if stripped string is size 0 send null instead 2023-05-30 17:23:22 -07:00
Patrick Fic
de62e994bd Merged in release/2023-05-26 (pull request #823)
Resolve error thrown when entering payment from accounting menu.
2023-05-26 22:11:09 +00:00
Patrick Fic
d5b1496898 Merged in release/2023-05-26 (pull request #822)
Resolve error thrown when entering payment from accounting menu.
2023-05-26 21:57:56 +00:00
Patrick Fic
d0d8354395 Merged in feature/IO-2298-payment-export (pull request #821)
Resolve error thrown when entering payment from accounting menu.
2023-05-26 21:57:39 +00:00
Patrick Fic
6d2c3c81c7 Resolve error thrown when entering payment from accounting menu. 2023-05-26 14:57:04 -07:00
Patrick Fic
3486e16d4e Merged in release/2023-05-26 (pull request #819)
Release/2023 05 26
2023-05-26 19:06:01 +00:00
Patrick Fic
e2e02945cc Merged in release/2023-05-26 (pull request #820)
Brought in shop estimator & made list of estimators unique.
2023-05-26 19:04:20 +00:00
Patrick Fic
404efcaf05 Merged in feature/IO-2264-schedule-filter (pull request #818)
Brought in shop estimator & made list of estimators unique.
2023-05-26 19:03:54 +00:00
Patrick Fic
9b96460e4f Brought in shop estimator & made list of estimators unique. 2023-05-26 12:03:33 -07:00
Patrick Fic
618eff4973 Merged in release/2023-05-26 (pull request #817)
Add manual appointment handling.
2023-05-26 18:53:21 +00:00
Patrick Fic
109c34bebd Merged in feature/IO-2264-schedule-filter (pull request #816)
Add manual appointment handling.
2023-05-26 18:53:09 +00:00
Patrick Fic
1aab5aa740 Add manual appointment handling. 2023-05-26 11:52:43 -07:00
Patrick Fic
484cb8e39e Merged in release/2023-05-26 (pull request #815)
Release/2023 05 26
2023-05-26 18:27:21 +00:00
Patrick Fic
1ca483d4b0 Merged in feature/IO-2298-payment-export (pull request #814)
Update labels.
2023-05-26 18:22:23 +00:00
Patrick Fic
3641363d3d Merged in feature/IO-2298-payment-export (pull request #813)
Update labels.
2023-05-26 18:21:39 +00:00
Patrick Fic
094160ebf3 Update labels. 2023-05-26 11:18:59 -07:00
Patrick Fic
fde13436c9 Merged in release/2023-05-26 (pull request #812)
Release/2023 05 26
2023-05-26 18:09:17 +00:00
John Allen Delos Reyes
e7be4c6e61 Merged in feature/IO-2298-payment-export (pull request #809)
IO-2298 added mark for export button on payment

Approved-by: Patrick Fic
2023-05-26 18:08:52 +00:00
John Allen Delos Reyes
708fe63852 Merged in feature/IO-2208-chat-affix (pull request #810)
IO-2208 added more conversations on initial load

Approved-by: Patrick Fic
2023-05-26 18:01:00 +00:00
John Allen Delos Reyes
3f579f49b9 Merged in feature/IO-2264-schedule-filter (pull request #811)
IO-2264 removed filter function for estimators

Approved-by: Patrick Fic
2023-05-26 18:00:52 +00:00
swtmply
c15e69f079 IO-2298 added mark for export button on payment 2023-05-27 00:17:44 +08:00
swtmply
ddb919e2cc IO-2264 removed filter function for estimators 2023-05-26 22:56:36 +08:00
swtmply
27e0f497bb IO-2208 added more conversations on initial load 2023-05-26 20:12:58 +08:00
Patrick Fic
b9a9f07d7b Merged in release/2023-05-26 (pull request #807)
Release/2023 05 26
2023-05-25 23:42:28 +00:00
John Allen Delos Reyes
3a06e813a8 Merged in feature/IO-2280-config-anchors (pull request #808)
IO-2280 added config anchors

Approved-by: Patrick Fic
2023-05-25 23:41:50 +00:00
swtmply
c2aaf8844f IO-2280 added config anchors 2023-05-26 02:24:34 +08:00
John Allen Delos Reyes
488f79ddc8 Merged in feature/IO-2264-schedule-filter (pull request #806)
Feature/IO-2264 schedule filter

Approved-by: Patrick Fic
2023-05-25 17:44:11 +00:00
John Allen Delos Reyes
5704e54e48 Merged in feature/IO-2298-payment-export (pull request #802)
IO-2298 added export buttons on payments

Approved-by: Patrick Fic
2023-05-25 17:41:54 +00:00
swtmply
3c4902f71f IO-2298 removed import destructure and added context reload 2023-05-26 00:48:28 +08:00
swtmply
5fb62aa16b IO-2264 added filter by estimators for the schedule 2023-05-26 00:15:35 +08:00
Patrick Fic
f4473d11a8 Merged in release/2023-05-26 (pull request #805)
Release/2023 05 26
2023-05-24 21:09:06 +00:00
Allan Carr
dd469bad12 Merged in feature/IO-2233-CDK-VEHICLE-LICENSE-PLATE (pull request #803)
IO-2233 CDK Vehicle License Plate Standardization

Approved-by: Patrick Fic
2023-05-24 21:05:21 +00:00
Allan Carr
a716535795 Merged in feature/IO-2259-Company-Name-with-only-Space (pull request #804)
IO-2259 Trim Company Name before seeing if ""

Approved-by: Patrick Fic
2023-05-24 21:05:00 +00:00
Allan Carr
c2ec476324 IO-2259 Trim Company Name before seeing if "" 2023-05-24 11:15:55 -07:00
Patrick Fic
3ecd29c640 Add missing filter on job search select. 2023-05-24 11:13:55 -07:00
Allan Carr
01443c478d IO-2233 CDK Vehicle License Plate Standardization
Remove All special character and spaces and covert to Upper Case
2023-05-24 10:34:05 -07:00
swtmply
209245187f IO-2298 added export buttons on payments 2023-05-25 01:28:22 +08:00
John Allen Delos Reyes
3a9e989d70 Merged in feature/IO-2208-chat-affix (pull request #801)
IO-2208 replaced cache first to network only

Approved-by: Patrick Fic
2023-05-24 15:24:57 +00:00
swtmply
8839fc0293 IO-2208 replaced cache first to network only 2023-05-24 23:07:49 +08:00
Allan Carr
028bf3c7a0 Merged in feature/IO-2292-MPI-Signature-Sheets (pull request #800)
IO-2292 MPI Signature Sheets

Approved-by: Patrick Fic
2023-05-24 14:58:04 +00:00
Allan Carr
ed05754368 IO-2292 MPI Signature Sheets 2023-05-23 17:56:48 -07:00
Allan Carr
7c3043988b Merged in feature/IO-2295-Individual-Note-Print (pull request #799)
IO-2295 Individual Note Print File Name contained {{ro_number}}

Approved-by: Patrick Fic
2023-05-23 23:32:33 +00:00
John Allen Delos Reyes
a16f0df7de Merged in feature/IO-2297-datetime-improvement (pull request #798)
IO-2297 added optimistic response and click outside function

Approved-by: Patrick Fic
2023-05-23 23:32:11 +00:00
John Allen Delos Reyes
2562151f6e Merged in feature/IO-2208-chat-affix (pull request #797)
Feature/IO-2208 chat affix

Approved-by: Patrick Fic
2023-05-23 23:28:31 +00:00
Allan Carr
44f2287b07 IO-2295 Individual Note Print File Name contained {{ro_number}} 2023-05-23 13:38:55 -07:00
swtmply
5e96ccdd99 IO-2297 added optimistic response and click outside function 2023-05-24 03:17:41 +08:00
Patrick Fic
8f91416623 Merged in release/2023-05-19 (pull request #796)
Release/2023 05 19
2023-05-19 17:17:47 +00:00
Patrick Fic
965af6da5f Merged in release/2023-05-19 (pull request #795)
IO-2293 Autohouse & Job Costing Dinero Type Casting
2023-05-19 17:02:28 +00:00
Allan Carr
a18dbbb6c4 Merged in feature/IO-2293-Autohouse-Job-Cost-Dinero-Type-Cast (pull request #794)
IO-2293 Autohouse & Job Costing Dinero Type Casting

Approved-by: Patrick Fic
2023-05-19 17:02:05 +00:00
Allan Carr
57d8ca5829 IO-2293 Autohouse & Job Costing Dinero Type Casting
All type cast to Dinero round to integer to prevent cast errors
2023-05-19 09:39:37 -07:00
Patrick Fic
4c6a2d6d63 Merged in release/2023-05-19 (pull request #793)
Release/2023 05 19
2023-05-18 22:05:08 +00:00
swtmply
c5c47e9bfc IO-2208 remove unused variables and imports 2023-05-19 00:14:28 +08:00
swtmply
d66fdfb2e0 IO-2208 added virtualization to conversation list 2023-05-18 23:04:10 +08:00
Patrick Fic
5861d0e9b6 IO-2289 Add betterment description to translation 2023-05-17 13:46:37 -07:00
Patrick Fic
e36904794b IO-2274 Add typing to notes. 2023-05-17 13:31:15 -07:00
Patrick Fic
1c89d12034 IO-2286 Resolve duplication of cost centers when sorting labor allocations. 2023-05-17 12:36:15 -07:00
Patrick Fic
fb5c5561e9 Resolve translations issues. 2023-05-17 12:21:14 -07:00
Patrick Fic
5e36a4ae89 IO-2275 Remove PVRT and GST References in USA App. 2023-05-17 12:09:21 -07:00
Patrick Fic
f553307587 Added rounding to job costing for adjustments. 2023-05-17 10:11:27 -07:00
Patrick Fic
2c80c81197 Added tasks presets to database schema. 2023-05-17 07:39:23 -07:00
Patrick Fic
9d865cf130 Merged in release/2023-05-19 (pull request #789)
IO-2258 Scoreboard Chart
2023-05-16 18:34:13 +00:00
Allan Carr
8e119ce0dd Merged in feature/IO-2258-Scoreboard-Chart-Inconsistencies (pull request #788)
IO-2258 Scoreboard Chart

Approved-by: Patrick Fic
2023-05-16 18:33:34 +00:00
Allan Carr
fe49161718 IO-2258 Scoreboard Chart
Add in target paint and body to the overall calculation to bump the start value up to the target instead of zero.
2023-05-15 15:18:24 -07:00
Patrick Fic
040e366335 Merged in release/2023-05-12 (pull request #786)
Release/2023 05 12
2023-05-12 18:18:04 +00:00
Patrick Fic
4655663dd8 Merged in release/2023-05-12 (pull request #785)
Release/2023 05 12
2023-05-12 17:14:31 +00:00
Allan Carr
6e1fbda79b Merged in feature/IO-2261-opensearch-replacements (pull request #784)
IO-2261 Add missing fields to return querys

Approved-by: Patrick Fic
2023-05-12 17:14:04 +00:00
Allan Carr
b2f616f1eb IO-2261 Add missing fields to return querys
Customer info missing on return
2023-05-12 10:12:49 -07:00
swtmply
4bc8ff26d2 IO-2208 Added pagination and subscription to chat 2023-05-13 00:32:51 +08:00
Patrick Fic
76eec7bebc Merged in release/2023-05-12 (pull request #783)
IO-2261 Remove duplicate fields and add created_at into return querys
2023-05-12 16:32:51 +00:00
Allan Carr
aa5f405e1b Merged in feature/IO-2261-opensearch-replacements (pull request #782)
IO-2261 Remove duplicate fields and add created_at into return querys

Approved-by: Patrick Fic
2023-05-12 15:26:05 +00:00
Allan Carr
ca9752d119 IO-2261 Remove duplicate fields and add created_at into return querys 2023-05-12 08:23:16 -07:00
Patrick Fic
45b4af5225 Merged in release/2023-05-12 (pull request #781)
Release/2023 05 12
2023-05-11 23:39:04 +00:00
Allan Carr
d2d310cf57 Merged in feature/IO-2190-Autohouse (pull request #780)
IO-2190 Paint Costs for JC if Cost/Hr is 0

Approved-by: Patrick Fic
2023-05-11 23:38:42 +00:00
Allan Carr
5d1a7657a9 Merged in feature/IO-2265-Autohouse-Extract (pull request #779)
IO-2190 Allow for 0 in JC/Hr rate

Approved-by: Patrick Fic
2023-05-11 23:38:25 +00:00
Allan Carr
5cb17994cd IO-2190 Allow for 0 in JC/Hr rate 2023-05-11 16:09:30 -07:00
Allan Carr
dab78e3dc9 IO-2190 Paint Costs for JC if Cost/Hr is 0 2023-05-11 16:07:28 -07:00
Patrick Fic
1232f28b3d Merged in release/2023-05-12 (pull request #778)
IO-2261 Modify Opensearch Data after payment update
2023-05-11 21:42:57 +00:00
Allan Carr
8e8d40d4b0 Merged in feature/IO-2261-opensearch-replacements (pull request #777)
IO-2261 Modify Opensearch Data after payment update

Approved-by: Patrick Fic
2023-05-11 21:42:24 +00:00
Allan Carr
7fae408454 IO-2261 Modify Opensearch Data after payment update 2023-05-11 14:38:09 -07:00
Patrick Fic
1cdafaa2cc Merged in release/2023-05-12 (pull request #776)
Release/2023 05 12
2023-05-11 14:50:33 +00:00
Allan Carr
b9ca7ef2e3 Merged in feature/IO-2271-Country-Region (pull request #775)
Feature/IO-2271 Country Region

Approved-by: Patrick Fic
2023-05-11 14:50:07 +00:00
Allan Carr
60867ae4dc IO-2271 Country Region Print Center Modification
Allow items to be hidden by Country Code only from region
2023-05-10 18:03:42 -07:00
Allan Carr
b0ddb62ac0 Merged in feature/IO-2265-Autohouse-Extract (pull request #773)
IO-2265 Autohouse Extract Remove Customer Info

Approved-by: Patrick Fic
2023-05-10 15:24:27 +00:00
Allan Carr
39a4646339 IO-2271 Country Region for Print Center
Hide reports based on Country Code for Region
2023-05-09 18:11:06 -07:00
Allan Carr
584322819f IO-2265 Autohouse Extract Remove Customer Info 2023-05-09 16:51:55 -07:00
Patrick Fic
051ee347a9 Merged in release/2023-05-05 (pull request #768)
Release/2023 05 05
2023-05-05 22:51:16 +00:00
Patrick Fic
acf1b387de Merged in release/2023-05-05 (pull request #767)
Release/2023 05 05
2023-05-05 22:03:21 +00:00
John Allen Delos Reyes
d350515c90 Merged in feature/IO-1722-job-size-color (pull request #763)
Feature/IO-1722 job size color

Approved-by: Patrick Fic
2023-05-05 22:02:55 +00:00
Allan Carr
121e579388 Merged in feature/IO-2261-opensearch-replacements (pull request #766)
IO-2261 Missing loader handler for owner_owing, correct payment

Approved-by: Patrick Fic
2023-05-05 22:02:03 +00:00
Allan Carr
cf5ebb8130 IO-2261 Missing loader for owner_owing 2023-05-05 14:24:00 -07:00
Allan Carr
2dabf3c811 IO-2261 Missing loader handler for owner_owing, correct payment 2023-05-05 14:03:05 -07:00
swtmply
04509fa587 IO-1722 adjusted logic for card color 2023-05-06 04:56:03 +08:00
swtmply
56fef0f43c IO-1722 Added reset button 2023-05-06 04:55:21 +08:00
Patrick Fic
42702ef015 Merged in release/2023-05-05 (pull request #765)
Release/2023 05 05
2023-05-05 20:19:03 +00:00
Allan Carr
25bee3cfdf Merged in feature/IO-2261-opensearch-replacements (pull request #764)
Feature/IO-2261 opensearch replacements

Approved-by: Patrick Fic
2023-05-05 20:14:02 +00:00
Allan Carr
baf06fee6c Merge branch 'feature/IO-2261-opensearch-replacements' of https://bitbucket.org/snaptsoft/bodyshop into feature/IO-2261-opensearch-replacements 2023-05-05 13:10:39 -07:00
Allan Carr
a3557bbc86 IO-2261 Add Delete function, Correct Payment edit, add Totals to Bills handler 2023-05-05 13:10:07 -07:00
swtmply
3e00e7981d IO-1722 added placement to popover 2023-05-06 03:34:55 +08:00
swtmply
12fa270a1a IO-1722 adjusted background colors 2023-05-06 03:34:22 +08:00
Patrick Fic
de250b152a Change all open search events to trigger on all fields. 2023-05-05 12:20:25 -07:00
Patrick Fic
c45741257f Add export trigger for os_bills event. 2023-05-05 12:16:39 -07:00
swtmply
3988386c79 IO-1722 replaced hex to rgba 2023-05-06 02:56:02 +08:00
Patrick Fic
492032c1e2 Merged in release/2023-05-05 (pull request #762)
Release/2023 05 05
2023-05-05 17:56:36 +00:00
Allan Carr
8222e56485 Merged in feature/IO-2261-opensearch-replacements (pull request #761)
IO-2261 Add in missing field "date" for Payments

Approved-by: Patrick Fic
2023-05-05 17:56:14 +00:00
Patrick Fic
49a61e1564 Merge branch 'feature/IO-2261-opensearch-replacements' of https://bitbucket.org/snaptsoft/bodyshop into feature/IO-2261-opensearch-replacements 2023-05-05 10:55:59 -07:00
Patrick Fic
eed18aa1c5 Add event triggers on export fields 2023-05-05 10:55:42 -07:00
Allan Carr
2de3f8b022 IO-2261 Add in missing field "date" for Payments 2023-05-05 10:54:41 -07:00
Patrick Fic
91476c7ad3 Merged in release/2023-05-05 (pull request #760)
Release/2023 05 05
2023-05-05 17:44:50 +00:00
John Allen Delos Reyes
00eb7926f9 Merged in feature/IO-2266-duplicate-option (pull request #759)
IO-2266 fixed key issue causing option to duplicate

Approved-by: Patrick Fic
2023-05-05 17:44:19 +00:00
Patrick Fic
854ad21b20 Update autohouse header auth. 2023-05-05 10:16:41 -07:00
Patrick Fic
561bcf10d9 Merged in release/2023-05-05 (pull request #758)
IO-2261 All Jobs, Bills, Payments
2023-05-05 14:49:55 +00:00
Allan Carr
75d9faa05b Merged in feature/IO-2261-opensearch-replacements (pull request #757)
IO-2261 All Jobs, Bills, Payments

Approved-by: Patrick Fic
2023-05-05 14:49:23 +00:00
Allan Carr
ac72177fbb IO-2261 All Jobs, Bills, Payments
Adjust Pagination settings and on clear delete search.page to reset. Adjust payments for bodyshopid as it was pointed at bill instead of payment
2023-05-04 18:27:47 -07:00
Allan Carr
088faf152c Merged in release/2023-05-05 (pull request #756)
Release/2023 05 05

Approved-by: Patrick Fic
2023-05-04 22:33:12 +00:00
Patrick Fic
90cba9ed24 Merge branch 'feature/IO-1722-job-size-color' into release/2023-05-05 2023-05-04 15:32:30 -07:00
Patrick Fic
a0702785c5 Revert "Revert "Merged in feature/IO-1722-job-size-color (pull request #746)""
This reverts commit 7dc3c00628.
2023-05-04 15:32:21 -07:00
Allan Carr
6898d609fe IO-2261 Add Loading effect 2023-05-04 15:17:37 -07:00
Allan Carr
d70893e2ba Merged in feature/IO-2261-opensearch-replacements (pull request #748)
Feature/IO-2261 opensearch replacements
2023-05-04 22:16:49 +00:00
Patrick Fic
239dc5c62d Merged in release/2023-05-05 (pull request #753)
Release/2023 05 05
2023-05-04 20:36:18 +00:00
swtmply
08e5543536 IO-1722 adjusted condition for text color 2023-05-05 04:00:44 +08:00
Patrick Fic
1c5c403d65 Merged in release/2023-05-05 (pull request #749)
Release/2023 05 05
2023-05-04 19:34:56 +00:00
Allan Carr
6e5fcbfdbd IO-2267 Remove Sort for Linked Table fields 2023-05-03 16:30:48 -07:00
Allan Carr
759a8ac58c IO-2261 Opensearch for Payments, Bills and All Jobs 2023-05-03 16:29:01 -07:00
Allan Carr
1c63aa39c4 Merged in release/2023-05-05 (pull request #744)
Release/2023 05 05
2023-05-03 17:10:48 +00:00
Patrick Fic
852fd9c388 IO-2261 Sample open search replacement. 2023-05-01 13:04:10 -07:00
Patrick Fic
6db68b76db Merged in release/2023-04-28 (pull request #735)
Additional database indexing.
2023-04-28 16:51:31 +00:00
Patrick Fic
23fcdd6375 Merged in release/2023-04-28 (pull request #732)
Release/2023 04 28
2023-04-27 22:21:03 +00:00
Patrick Fic
329bdbe22d Merged in release/2023-04-28 (pull request #729)
Release/2023 04 28
2023-04-27 20:54:32 +00:00
Allan Carr
66b3fb6988 Merged in release/2023-04-28 (pull request #728)
IO-2190 Factor Paint Costs from Scale over to Job Costing & Autohouse

Approved-by: Patrick Fic
2023-04-27 19:47:13 +00:00
Patrick Fic
3d26c2e94e Merged in release/2023-04-28 (pull request #727)
Release/2023 04 28
2023-04-27 16:32:43 +00:00
Patrick Fic
ad42dd1295 Merged in release/2023-04-28 (pull request #726)
IO-2210 Remove unused state.
2023-04-26 23:52:34 +00:00
Patrick Fic
2938b9c94c Merged in release/2023-04-28 (pull request #725)
IO-2210 Search Improvements
2023-04-26 23:45:43 +00:00
Patrick Fic
bde17446ad Merged in release/2023-04-28 (pull request #724)
Release/2023 04 28
2023-04-26 19:49:54 +00:00
Patrick Fic
fe60538acf Merged in release/2023-04-28 (pull request #723)
Resolve missing items from merge conflicts.
2023-04-26 17:21:20 +00:00
Patrick Fic
7461e58000 Merged in release/2023-04-28 (pull request #722)
Release/2023 04 28
2023-04-26 16:44:29 +00:00
Patrick Fic
1afda01d34 Merged in release/2023-04-28 (pull request #721)
Release/2023 04 28
2023-04-26 02:22:48 +00:00
Patrick Fic
59f71d53cd Merged in release/2023-04-21 (pull request #718)
Release/2023 04 21
2023-04-21 18:42:34 +00:00
Allan Carr
0cad64ff6d Merged in release/2023-04-21 (pull request #717)
IO-2335 Exported GSR to Excel

Approved-by: Patrick Fic
2023-04-20 19:58:58 +00:00
Allan Carr
3a83160b33 Merged in release/2023-04-21 (pull request #716)
IO-2235 Exported GSR to Excel

Approved-by: Patrick Fic
2023-04-20 16:45:28 +00:00
Allan Carr
701c532e48 Merged in release/2023-04-21 (pull request #715)
Release/2023 04 21

Approved-by: Patrick Fic
2023-04-19 23:14:52 +00:00
Patrick Fic
c0d9bacf1d Merged in release/2023-04-21 (pull request #710)
Release/2023 04 21
2023-04-19 18:44:50 +00:00
Patrick Fic
357f40bdc2 Merged in release/2023-04-14 (pull request #706)
Release/2023 04 14
2023-04-14 20:03:58 +00:00
Patrick Fic
30689a8ca6 Merged in release/2023-04-14 (pull request #705)
Release/2023 04 14
2023-04-14 18:39:19 +00:00
Patrick Fic
554ec37ace Merged in release/2023-04-14 (pull request #704)
Release/2023 04 14
2023-04-13 16:36:16 +00:00
Patrick Fic
3fc7af9780 Merged in release/2023-04-07 (pull request #702)
Release/2023 04 07
2023-04-06 00:22:37 +00:00
John Allen Delos Reyes
a1b6ccc23d Merged in release/2023-04-07 (pull request #700)
Release/2023 04 07

Approved-by: Patrick Fic
2023-04-05 21:21:39 +00:00
swtmply
a5e3985745 added the logic for aws secrets manager 2023-03-27 23:21:39 +08:00
swtmply
88ee4f13e1 code cleanup and translations 2023-03-18 02:45:00 +08:00
Patrick Fic
dde6f17029 Merged in release/2023-03-17 (pull request #694)
Language updates & default for lost sale reason.
2023-03-17 18:43:52 +00:00
Allan Carr
1aceef9153 Merged in release/2023-03-17 (pull request #693)
IO-2193 Delete Owner & Vehicle

Approved-by: Patrick Fic
2023-03-17 01:06:10 +00:00
Patrick Fic
678892d134 Merged in release/2023-03-17 (pull request #692)
IO-2177 Add enforce category on conversion.
2023-03-16 21:51:01 +00:00
Allan Carr
dc187bbf24 Merged in release/2023-03-17 (pull request #691)
Release/2023 03 17

Approved-by: Patrick Fic
2023-03-16 18:51:11 +00:00
swtmply
fa05d0b401 added refund and sms feature 2023-03-17 01:05:51 +08:00
Patrick Fic
09aae78715 Merged in release/2023-03-17 (pull request #690)
Release/2023 03 17
2023-03-15 18:53:49 +00:00
swtmply
cf017fb80b fixed the duplicate non-approval call from intellipay 2023-03-15 03:45:08 +08:00
swtmply
56c366e9e8 added dummy function for dynamic bodyshop 2023-03-15 03:44:33 +08:00
swtmply
07b7394fec fixed the import path from payment response queries 2023-03-14 23:24:44 +08:00
swtmply
0617d79d19 some cleanup and translation 2023-03-14 02:37:04 +08:00
swtmply
885e9c6958 added the card payment option on actions and menu 2023-03-14 02:36:41 +08:00
swtmply
6bf5f2fe77 added expandable row in payments table 2023-03-14 02:36:22 +08:00
swtmply
a3cc5c2324 restructured card payment modal and added audit trailing 2023-03-14 02:35:56 +08:00
Patrick Fic
c42276ab3a Merged in release/2023-03-10 (pull request #688)
Release/2023 03 10
2023-03-10 18:51:03 +00:00
swtmply
a44ed3c406 removed console log and added the mutation for failed payment attempts 2023-03-06 21:49:14 +08:00
swtmply
aa5110ae13 transfered test files to separate component 2023-03-04 04:05:28 +08:00
Patrick Fic
41d25cbc52 Merged in release/2023-03-03 (pull request #686)
release/2023-03-03

Approved-by: Patrick Fic
2023-03-03 17:28:32 +00:00
Patrick Fic
085c27ad20 Merged in release/2023-03-03 (pull request #685)
release/2023-03-03

Approved-by: Patrick Fic
2023-03-03 17:08:45 +00:00
Patrick Fic
38e6b5010e Merged in release/2023-03-03 (pull request #684)
release/2023-03-03

Approved-by: Patrick Fic
2023-03-03 04:05:30 +00:00
swtmply
c8ee9ca5a7 initial intellipay implementation
Co-authored-by: Patrick Fic <patrick@thinkimex.com>
2023-03-01 00:31:44 +08:00
Allan Carr
242c275e7d Merged in release/2023-02-24 (pull request #680)
Release/2023 02 24

Approved-by: Patrick Fic
2023-02-23 19:44:40 +00:00
Allan Carr
1dc6130fdf Merged in release/2023-02-17 (pull request #678)
Release/2023 02 17

Approved-by: Patrick Fic
2023-02-17 18:37:20 +00:00
Allan Carr
910afbf48d Merged in release/2023-02-17 (pull request #676)
Release/2023 02 17

Approved-by: Patrick Fic
2023-02-14 22:03:01 +00:00
Patrick Fic
9d9edfd674 Merged in release/2023-02-10 (pull request #674)
Improve display of job lines preset display.
2023-02-10 23:22:47 +00:00
Allan Carr
62a5b49836 Merged in release/2023-02-10 (pull request #673)
Release/2023 02 10

Approved-by: Patrick Fic
2023-02-09 17:56:24 +00:00
Patrick Fic
e93e138f78 Merged in release/2023-02-03 (pull request #671)
release/2023-02-03

Approved-by: Patrick Fic
2023-02-06 18:09:03 +00:00
Patrick Fic
4639e31e55 Merged in release/2023-02-03 (pull request #669)
release/2023-02-03

Approved-by: Patrick Fic
2023-02-04 01:19:46 +00:00
Patrick Fic
e8fde14f9b Merged in release/2023-02-03 (pull request #668)
release/2023-02-03

Approved-by: Patrick Fic
2023-02-03 22:58:21 +00:00
Patrick Fic
90e87adc34 Merged in release/2023-02-03 (pull request #667)
release/2023-02-03

Approved-by: Patrick Fic
2023-02-03 20:34:27 +00:00
Patrick Fic
81053b3cbf Merged in release/2023-02-03 (pull request #666)
release/2023-02-03

Approved-by: Patrick Fic
2023-02-02 21:47:46 +00:00
Patrick Fic
f4290bf20c Merged in release/2023-02-03 (pull request #665)
release/2023-02-03

Approved-by: Patrick Fic
2023-02-02 19:06:20 +00:00
Patrick Fic
5371657aa4 Merged in release/2023-02-03 (pull request #664)
release/2023-02-03

Approved-by: Patrick Fic
2023-02-02 18:42:40 +00:00
Patrick Fic
599f4e143c Merged in release/2023-02-03 (pull request #663)
release/2023-02-03

Approved-by: Patrick Fic
2023-02-02 18:10:12 +00:00
Patrick Fic
94440e5c48 Merged in master (pull request #662)
Merged in release/2023-01-27 (pull request #661)
2023-01-27 23:14:44 +00:00
Patrick Fic
aa5d6f2090 Merged in release/2023-01-27 (pull request #660)
IO-2156 Resolve AH detail line validation issue.
2023-01-27 21:21:21 +00:00
Patrick Fic
6bde1b1baf Merged in release/2023-01-27 (pull request #659)
Release/2023 01 27
2023-01-27 17:02:10 +00:00
Patrick Fic
fc404b1f3b Merged in release/2022-01-20 (pull request #655)
Release/2022 01 20
2023-01-19 21:11:58 +00:00
Patrick Fic
c6ba3fd8f0 Merged in release/2022-01-13 (pull request #653)
Release/2022 01 13
2023-01-09 17:36:25 +00:00
Patrick Fic
119904ca2b Merged in release/2023-01-06 (pull request #651)
Updates to Sentry & removal of Stripe.
2023-01-04 16:33:10 +00:00
Patrick Fic
f7e1b023df Merged in release/2023-01-06 (pull request #650)
Release/2023 01 06
2023-01-02 22:56:05 +00:00
Patrick Fic
5855569194 Merged in release/2022-12-30 (pull request #648)
IO-2133 Manual Line Highlighting
2022-12-28 18:05:20 +00:00
Patrick Fic
4f852e7493 Merged in release/2022-12-30 (pull request #647)
release/2022-12-30

Approved-by: Patrick Fic
2022-12-26 18:34:28 +00:00
Patrick Fic
deec40a89c Merged in release/2022-12-16 (pull request #642)
Remove unneeded import to fix CI.
2022-12-16 01:09:42 +00:00
Patrick Fic
145dd9bec6 Merged in release/2022-12-16 (pull request #641)
IO-2131 Resolve job search select filtering issue.
2022-12-16 01:02:45 +00:00
Patrick Fic
b8e5d4412f Merged in release/2022-12-16 (pull request #640)
Release/2022 12 16
2022-12-15 18:20:28 +00:00
Patrick Fic
277fb8f839 Merged in release/2022-12-09 (pull request #637)
IO-2123 Add cc inventory print.
2022-12-09 19:35:49 +00:00
Patrick Fic
d3d5485846 Merged in release/2022-12-09 (pull request #636)
Release/2022 12 09
2022-12-09 16:19:58 +00:00
Patrick Fic
55091d61d6 Merged in release/2022-12-02 (pull request #634)
Release/2022 12 02
2022-11-30 18:26:32 +00:00
Patrick Fic
5b5df8a3a1 Merged in release/2022-11-25 (pull request #630)
Release/2022 11 25
2022-11-25 16:42:19 +00:00
Patrick Fic
ccf48cfcf1 Merged in release/2022-11-10 (pull request #624)
release/2022-11-10

Approved-by: Patrick Fic
2022-11-10 18:47:48 +00:00
Patrick Fic
c89342b6ef Merge branch 'release/2022-11-10' into test
* release/2022-11-10:
  CI Test
2022-11-09 16:35:48 -08:00
Patrick Fic
e97ceb7cbe Merged in release/2022-11-10 (pull request #623)
CI Test
2022-11-10 00:30:20 +00:00
Patrick Fic
de34cbd937 Merged in release/2022-11-10 (pull request #622)
Release/2022 11 10
2022-11-10 00:23:10 +00:00
Patrick Fic
cf7a1b0168 Merged in release/2022-11-10 (pull request #621)
Add .circleci/config.yml
2022-11-08 19:10:04 +00:00
256 changed files with 40455 additions and 15274 deletions

View File

@@ -8,13 +8,13 @@ orbs:
jobs:
api-deploy:
docker:
- image: "cimg/base:stable"
- image: cimg/node:18.18.2
steps:
- checkout
- eb/setup
- run:
command: |
eb init imex-online-production-api -r ca-central-1 -p "Node.js 16 running on 64bit Amazon Linux 2"
eb init imex-online-production-api -r ca-central-1 -p "Node.js 18 running on 64bit Amazon Linux 2"
eb status --verbose
eb deploy
eb status

4
.gitignore vendored
View File

@@ -117,4 +117,6 @@ logs/oAuthClient-log.log
.node-persist/**
/*.env.*
/*.env.*
.idea/*
.idea

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
legacy-peer-deps=true

File diff suppressed because it is too large Load Diff

24522
client/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
"@apollo/client": "^3.7.9",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^7.0.0",
"@fingerprintjs/fingerprintjs": "^3.3.3",
"@fingerprintjs/fingerprintjs": "^3.4.2",
"@jsreport/browser-client": "^3.1.0",
"@sentry/react": "^7.40.0",
"@sentry/tracing": "^7.40.0",
@@ -51,6 +51,7 @@
"react-i18next": "^12.2.0",
"react-icons": "^4.7.1",
"react-image-lightbox": "^5.1.4",
"react-intersection-observer": "^9.4.3",
"react-number-format": "^5.1.3",
"react-redux": "^8.0.5",
"react-resizable": "^3.0.4",
@@ -88,8 +89,8 @@
"analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "craco start",
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"build:test": "env-cmd -f .env.test yarn run build",
"build-deploy:test": "yarn run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"build:test": "env-cmd -f .env.test npm run build",
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"test": "cypress open",
"eject": "react-scripts eject",

View File

@@ -145,7 +145,11 @@
//Update row highlighting on production board.
.ant-table-tbody > tr.ant-table-row:hover > td {
background: #eaeaea !important;
background: #e7f3ff !important;
}
.ant-table-tbody > tr.ant-table-row-selected > td {
background: #e6f7ff !important;
}
.job-line-manual {
@@ -156,3 +160,11 @@
td.ant-table-column-sort {
background-color: transparent;
}
.ant-table-tbody > tr.ant-table-row:nth-child(2n) > td {
background-color: #f4f4f4;
}
.rowWithColor > td {
background-color: var(--bgColor) !important;
}

View File

@@ -1,49 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { setEmailOptions } from "../../redux/email/email.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { TemplateList } from "../../utils/TemplateConstants";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
});
function Test({ bodyshop, setEmailOptions }) {
return (
<div>
<button
onClick={() => {
setEmailOptions({
messageOptions: {
to: ["patrickwf@gmail.com"],
replyTo: bodyshop.email,
},
template: {
name: TemplateList().parts_order.key,
variables: {
id: "a7c2d4e1-f519-42a9-a071-c48cf0f22979",
},
},
});
}}
>
send email
</button>
<button
onClick={() => {
logImEXEvent("IMEXEVENT", { somethignArThare: 5 });
}}
>
Log an ImEX Event.
</button>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(Test);

View File

@@ -0,0 +1,63 @@
{
"status": 24201299,
"custid": 19607899,
"paymentid": 24201299,
"response": "A",
"authcode": "498680",
"declinereason": "Approved",
"fee": 0,
"invoice": "",
"account": "john",
"amount": 1000,
"amountincludesfee": false,
"total": 1000,
"paymenttype": "C",
"methodhint": "VI ***1111",
"cardbrand": "Visa",
"cardnumdisplay": "***1111",
"receiptelements": {
"authcode": "498680",
"cust_srv_ph_num": "1-555-555-5555",
"rcpt_pg_ftr_txt": "Thank You\nPlease Come Again",
"rcpt_currency": "USD",
"responsecode": "A",
"rcpt_pay_mthd": "Visa",
"transid": "C00 915799",
"merch_disp_nm": "CP Devel Test",
"rcpt_input_mthd": "Keyed",
"rcpt_pg_hdr_txt": "Welcome!",
"rcpt_tran_time": "Thursday February 23 2023, 11:25:36 pm +08",
"rcpt_trans_type": "Normal Transaction (Sale)",
"message": "Approved",
"rcpt_dba_addr": "1234 Storefront Ave\nSome City, UT 84111",
"avsdata": "N",
"receiptrequirements": "S",
"rcpt_cardnum": "************1111",
"cv2result": "M",
"rfnd_policy_txt": "<b>No Refunds</b>\nStore Credit Only",
"labels": {
"tranref": "REF#",
"tid": "TID",
"validationcode": "ValCode",
"emvapplicationid": "AID",
"emvatc": "ATC",
"rcpt_pay_mthd": "Pay Method",
"transid": "TransID",
"rcpt_input_mthd": "IMode",
"emvtsi": "TSI",
"emvac": "AC",
"rcpt_trans_type": "TranType",
"emvapplicationname": "PApp",
"visarewards": "RewardsProg"
}
},
"receipttoken": "H4sIAAAAAAAAACXMTQuCMBgA4P/ynh3tw_3dBI/ipQ8NOtRN53QiblpBRfTfCzo/8LwhxGAdZCCwFYoJJFQjI2kvHdGu74lVkgmrWyWNhASW5jW7cB87yHjKKePGJODnxrrnMl7dDTKmEJlSOqV/_N30XPpyj2Eddq57_KKZ8FLzmh_G1VQnVfhjiXGK1XYTc/h8AVOkf4qUAAAA",
"call": "card_payment",
"nonce": "488b5568-b5c1-4f38-8b2f-3b050f3abb11P",
"hmac": "JyPAJ9Yx0SlYBTtqns1OxAFRt+xF3l2UiLPO5zTDRBE=",
"paymentreferenceid": "C19607899P24201299",
"cardnum": "...1111",
"email": "",
"nameOnCard": "John Allen",
"cardType": "visa"
}

View File

@@ -1,9 +0,0 @@
import React from "react";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
export default function Test() {
return (
<div>
<QboAuthorizeComponent />
</div>
);
}

View File

@@ -0,0 +1,31 @@
import { Button } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
setRefundPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "refund_payment" })),
});
function Test({ setRefundPaymentContext, refundPaymentModal }) {
console.log("refundPaymentModal", refundPaymentModal);
return (
<div>
<Button
onClick={() =>
setRefundPaymentContext({
context: {},
})
}
>
Open Modal
</Button>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(Test);

View File

@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
import { DELETE_BILL } from "../../graphql/bills.queries";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
export default function BillDeleteButton({ bill }) {
export default function BillDeleteButton({ bill, callback }) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const [deleteBill] = useMutation(DELETE_BILL);
@@ -36,6 +36,8 @@ export default function BillDeleteButton({ bill }) {
if (!!!result.errors) {
notification["success"]({ message: t("bills.successes.deleted") });
if (callback && typeof callback === "function") callback(bill.id);
} else {
//Check if it's an fkey violation.
const error = JSON.stringify(result.errors);

View File

@@ -1,18 +1,18 @@
import { useApolloClient, useMutation } from "@apollo/client";
import { Button, Checkbox, Form, Modal, notification, Space } from "antd";
import { Button, Checkbox, Form, Modal, Space, notification } from "antd";
import _ from "lodash";
import React, { useEffect, useState, useMemo } from "react";
import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_NEW_BILL } from "../../graphql/bills.queries";
import { UPDATE_INVENTORY_LINES } from "../../graphql/inventory.queries";
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
import {
QUERY_JOB_LBR_ADJUSTMENTS,
UPDATE_JOB,
} from "../../graphql/jobs.queries";
import { MUTATION_MARK_RETURN_RECEIVED } from "../../graphql/parts-orders.queries";
import { UPDATE_INVENTORY_LINES } from "../../graphql/inventory.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
@@ -20,15 +20,15 @@ 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";
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
import useLocalStorage from "../../utils/useLocalStorage";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import confirmDialog from "../../utils/asyncConfirm";
import useLocalStorage from "../../utils/useLocalStorage";
import BillFormContainer from "../bill-form/bill-form.container";
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
import { handleUpload } from "../documents-upload/documents-upload.utility";
const mapStateToProps = createStructuredSelector({
billEnterModal: selectBillEnterModal,
@@ -37,8 +37,8 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
insertAuditTrail: ({ jobid, billid, operation }) =>
dispatch(insertAuditTrail({ jobid, billid, operation })),
});
const Templates = TemplateList("job_special");
@@ -126,13 +126,24 @@ function BillEnterModalContainer({
deductedfromlbr: deductedfromlbr,
lbr_adjustment,
joblineid: i.joblineid === "noline" ? null : i.joblineid,
applicable_taxes: {
federal:
(i.applicable_taxes && i.applicable_taxes.federal) ||
false,
state:
(i.applicable_taxes && i.applicable_taxes.state) ||
false,
local:
(i.applicable_taxes && i.applicable_taxes.local) ||
false,
},
};
}),
},
},
],
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"],
});
const adjKeys = Object.keys(adjustmentsToInsert);
@@ -305,7 +316,9 @@ function BillEnterModalContainer({
insertAuditTrail({
jobid: values.jobid,
billid: billId,
operation: AuditTrailMapping.billposted(remainingValues.invoice_number),
operation: AuditTrailMapping.billposted(
r1.data.insert_bills.returning[0].invoice_number
),
});
if (enterAgain) {

View File

@@ -1,6 +1,6 @@
import Icon, { UploadOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { MdOpenInNew } from "react-icons/md";
import { useTreatments } from "@splitsoftware/splitio-react";
import {
Alert,
Divider,
@@ -12,14 +12,17 @@ import {
Switch,
Upload,
} from "antd";
import moment from "moment";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { MdOpenInNew } from "react-icons/md";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { CHECK_BILL_INVOICE_NUMBER } from "../../graphql/bills.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
@@ -28,8 +31,6 @@ import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import BillFormLines from "./bill-form.lines.component";
import { CalculateBillTotal } from "./bill-form.totals.utility";
import { useTreatments } from "@splitsoftware/splitio-react";
import BillFormLinesExtended from "../bill-form-lines-extended/bill-form-lines-extended.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -49,6 +50,7 @@ export function BillFormComponent({
job,
loadOutstandingReturns,
loadInventory,
preferredMake,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -58,6 +60,11 @@ export function BillFormComponent({
{},
bodyshop.imexshopid
);
const { ClosingPeriod } = useTreatments(
["ClosingPeriod"],
{},
bodyshop.imexshopid
);
const handleVendorSelect = (props, opt) => {
setDiscount(opt.discount);
@@ -179,6 +186,7 @@ export function BillFormComponent({
<VendorSearchSelect
disabled={disabled}
options={vendorAutoCompleteOptions}
preferredMake={preferredMake}
onSelect={handleVendorSelect}
/>
</Form.Item>
@@ -259,6 +267,37 @@ export function BillFormComponent({
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
if (
ClosingPeriod.treatment === "on" &&
bodyshop.accountingconfig.ClosingPeriod
) {
if (
moment(value)
.startOf("day")
.isSameOrAfter(
moment(
bodyshop.accountingconfig.ClosingPeriod[0]
).startOf("day")
) &&
moment(value)
.startOf("day")
.isSameOrBefore(
moment(
bodyshop.accountingconfig.ClosingPeriod[1]
).endOf("day")
)
) {
return Promise.resolve();
} else {
return Promise.reject(t("bills.validation.closingperiod"));
}
} else {
return Promise.resolve();
}
},
}),
]}
>
<FormDatePicker disabled={disabled} />

View File

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

View File

@@ -0,0 +1,374 @@
import { DeleteFilled } from "@ant-design/icons";
import { useLazyQuery, useMutation } from "@apollo/client";
import {
Button,
Card,
Col,
Form,
Input,
Row,
Space,
Spin,
Statistic,
notification,
} from "antd";
import axios from "axios";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
INSERT_PAYMENT_RESPONSE,
QUERY_RO_AND_OWNER_BY_JOB_PKS,
} from "../../graphql/payment_response.queries";
import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectCardPayment } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
const mapStateToProps = createStructuredSelector({
cardPaymentModal: selectCardPayment,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")),
});
const CardPaymentModalComponent = ({
bodyshop,
cardPaymentModal,
toggleModalVisible,
insertAuditTrail,
}) => {
const { context } = cardPaymentModal;
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
const { t } = useTranslation();
const [, { data, refetch, queryLoading }] = useLazyQuery(
QUERY_RO_AND_OWNER_BY_JOB_PKS,
{
variables: { jobids: [context.jobid] },
skip: true,
}
);
console.log("🚀 ~ file: card-payment-modal.component..jsx:61 ~ data:", data);
//Initialize the intellipay window.
const SetIntellipayCallbackFunctions = () => {
console.log("*** Set IntelliPay callback functions.");
window.intellipay.runOnClose(() => {
//window.intellipay.initialize();
});
window.intellipay.runOnApproval(async function (response) {
console.warn("*** Running On Approval Script ***");
form.setFieldValue("paymentResponse", response);
form.submit();
});
window.intellipay.runOnNonApproval(async function (response) {
// Mutate unsuccessful payment
const { payments } = form.getFieldsValue();
await insertPaymentResponse({
variables: {
paymentResponse: payments.map((payment) => ({
amount: payment.amount,
bodyshopid: bodyshop.id,
jobid: payment.jobid,
declinereason: response.declinereason,
ext_paymentid: response.paymentid.toString(),
successful: false,
response,
})),
},
});
payments.forEach((payment) =>
insertAuditTrail({
jobid: payment.jobid,
operation: AuditTrailMapping.failedpayment(),
})
);
});
};
const handleFinish = async (values) => {
try {
await insertPayment({
variables: {
paymentInput: values.payments.map((payment) => ({
amount: payment.amount,
transactionid: (values.paymentResponse.paymentid || "").toString(),
payer: t("payments.labels.customer"),
type: values.paymentResponse.cardbrand,
jobid: payment.jobid,
date: moment(Date.now()),
payment_responses: {
data: [
{
amount: payment.amount,
bodyshopid: bodyshop.id,
jobid: payment.jobid,
declinereason: values.paymentResponse.declinereason,
ext_paymentid: values.paymentResponse.paymentid.toString(),
successful: true,
response: values.paymentResponse,
},
],
},
})),
},
refetchQueries: ["GET_JOB_BY_PK"],
});
toggleModalVisible();
} catch (error) {
console.error(error);
notification.open({
type: "error",
message: t("payments.errors.inserting", { error: error.message }),
});
} finally {
setLoading(false);
}
};
const handleIntelliPayCharge = async () => {
setLoading(true);
//Validate
try {
await form.validateFields();
} catch (error) {
setLoading(false);
return;
}
try {
const response = await axios.post("/intellipay/lightbox_credentials", {
bodyshop,
refresh: !!window.intellipay,
});
if (window.intellipay) {
// eslint-disable-next-line no-eval
eval(response.data);
SetIntellipayCallbackFunctions();
window.intellipay.autoOpen();
} else {
var rg = document.createRange();
let node = rg.createContextualFragment(response.data);
document.documentElement.appendChild(node);
SetIntellipayCallbackFunctions();
window.intellipay.isAutoOpen = true;
window.intellipay.initialize();
}
} catch (error) {
notification.open({
type: "error",
message: t("job_payments.notifications.error.openingip"),
});
setLoading(false);
}
};
return (
<Card title="Card Payment">
<Spin spinning={loading}>
<Form
onFinish={handleFinish}
form={form}
layout="vertical"
initialValues={{
payments: context.jobid ? [{ jobid: context.jobid }] : [],
}}
>
<Form.List name={["payments"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Row gutter={[16, 16]}>
<Col span={16}>
<Form.Item
key={`${index}jobid`}
label={t("jobs.fields.ro_number")}
name={[field.name, "jobid"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<JobSearchSelectComponent
notExported={false}
clm_no
/>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
key={`${index}amount`}
label={t("payments.fields.amount")}
name={[field.name, "amount"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<CurrencyFormItemComponent />
</Form.Item>
</Col>
<Col span={2}>
<DeleteFilled
style={{ margin: "1rem" }}
onClick={() => {
remove(field.name);
}}
/>
</Col>
</Row>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item
shouldUpdate={(prevValues, curValues) =>
prevValues.payments?.map((p) => p?.jobid).join() !==
curValues.payments?.map((p) => p?.jobid).join()
}
>
{() => {
console.log("Updating the owner info section.");
//If all of the job ids have been fileld in, then query and update the IP field.
const { payments } = form.getFieldsValue();
if (
payments?.length > 0 &&
payments?.filter((p) => p?.jobid).length === payments?.length
) {
console.log("**Calling refetch.");
refetch({ jobids: payments.map((p) => p.jobid) });
}
console.log(
"Acc info",
data,
payments && data && data.jobs.length > 0
? data.jobs.map((j) => j.ro_number).join(", ")
: null
);
return (
<>
<Input
className="ipayfield"
data-ipayname="account"
//type="hidden"
value={
payments && data && data.jobs.length > 0
? data.jobs.map((j) => j.ro_number).join(", ")
: null
}
hidden
/>
<Input
className="ipayfield"
data-ipayname="email"
// type="hidden"
value={
payments && data && data.jobs.length > 0
? data.jobs.filter((j) => j.ownr_ea)[0]?.ownr_ea
: null
}
hidden
/>
</>
);
}}
</Form.Item>
<Form.Item
shouldUpdate={(prevValues, curValues) =>
prevValues.payments?.map((p) => p?.amount).join() !==
curValues.payments?.map((p) => p?.amount).join()
}
>
{() => {
const { payments } = form.getFieldsValue();
const totalAmountToCharge = payments?.reduce((acc, val) => {
return acc + (val?.amount || 0);
}, 0);
return (
<Space style={{ float: "right" }}>
<Statistic
title="Amount To Charge"
value={totalAmountToCharge}
precision={2}
/>
<Input
className="ipayfield"
data-ipayname="amount"
//type="hidden"
value={totalAmountToCharge?.toFixed(2)}
hidden
/>
<Button
type="primary"
// data-ipayname="submit"
className="ipayfield"
loading={queryLoading || loading}
disabled={!(totalAmountToCharge > 0)}
onClick={handleIntelliPayCharge}
>
{t("job_payments.buttons.proceedtopayment")}
</Button>
</Space>
);
}}
</Form.Item>
{/* Lightbox payment response when it is completed */}
<Form.Item name="paymentResponse" hidden>
<Input type="hidden" />
</Form.Item>
</Form>
</Spin>
</Card>
);
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(CardPaymentModalComponent);

View File

@@ -0,0 +1,57 @@
import { Button, Modal } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectCardPayment } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CardPaymentModalComponent from "./card-payment-modal.component.";
const mapStateToProps = createStructuredSelector({
cardPaymentModal: selectCardPayment,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment")),
});
function CardPaymentModalContainer({
cardPaymentModal,
toggleModalVisible,
bodyshop,
}) {
const { visible } = cardPaymentModal;
const { t } = useTranslation();
const handleCancel = () => {
toggleModalVisible();
};
const handleOK = () => {
toggleModalVisible();
};
return (
<Modal
open={visible}
onOk={handleOK}
onCancel={handleCancel}
footer={[
<Button key="back" onClick={handleCancel}>
{t("job_payments.buttons.goback")}
</Button>,
]}
width="80%"
destroyOnClose
>
<CardPaymentModalComponent />
</Modal>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(CardPaymentModalContainer);

View File

@@ -1,12 +1,19 @@
import { Badge, List, Tag } from "antd";
import React from "react";
import { connect } from "react-redux";
import {
AutoSizer,
CellMeasurer,
CellMeasurerCache,
List as VirtualizedList,
} from "react-virtualized";
import { createStructuredSelector } from "reselect";
import { setSelectedConversation } from "../../redux/messaging/messaging.actions";
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import "./chat-conversation-list.styles.scss";
const mapStateToProps = createStructuredSelector({
@@ -18,59 +25,95 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setSelectedConversation(conversationId)),
});
export function ChatConversationListComponent({
function ChatConversationListComponent({
conversationList,
selectedConversation,
setSelectedConversation,
loadMoreConversations,
}) {
const cache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 60,
});
const rowRenderer = ({ index, key, style, parent }) => {
const item = conversationList[index];
return (
<CellMeasurer
key={key}
cache={cache}
parent={parent}
columnIndex={0}
rowIndex={index}
>
<List.Item
onClick={() => setSelectedConversation(item.id)}
className={`chat-list-item ${
item.id === selectedConversation
? "chat-list-selected-conversation"
: null
}`}
style={style}
>
<div
style={{
display: "inline-block",
}}
>
{item.label && <div className="chat-name">{item.label}</div>}
{item.job_conversations.length > 0 ? (
<div className="chat-name">
{item.job_conversations.map((j, idx) => (
<div key={idx}>
<OwnerNameDisplay ownerObject={j.job} />
</div>
))}
</div>
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
</div>
<div style={{ 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>
</CellMeasurer>
);
};
return (
<div className="chat-list-container">
<List
bordered
dataSource={conversationList}
renderItem={(item) => (
<List.Item
key={item.id}
onClick={() => setSelectedConversation(item.id)}
className={`chat-list-item ${
item.id === selectedConversation
? "chat-list-selected-conversation"
: null
}`}
>
<div sryle={{ display: "inline-block" }}>
{item.label && <div className="chat-name">{item.label}</div>}
{item.job_conversations.length > 0 ? (
<div className="chat-name">
{item.job_conversations.map((j, idx) => (
<div key={idx}>
<OwnerNameDisplay ownerObject={j.job} />
</div>
))}
</div>
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
</div>
<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>
<AutoSizer>
{({ height, width }) => (
<VirtualizedList
height={height}
width={width}
rowCount={conversationList.length}
rowHeight={cache.rowHeight}
rowRenderer={rowRenderer}
onScroll={({ scrollTop, scrollHeight, clientHeight }) => {
if (scrollTop + clientHeight === scrollHeight) {
loadMoreConversations();
}
}}
/>
)}
/>
</AutoSizer>
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps

View File

@@ -3,8 +3,9 @@
}
.chat-list-container {
flex: 1;
overflow: auto;
overflow: hidden;
height: 100%;
border: 1px solid gainsboro;
}
.chat-list-item {
@@ -21,4 +22,6 @@
.ro-number-tag {
align-self: baseline;
}
padding: 12px 24px;
border-bottom: 1px solid gainsboro;
}

View File

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

View File

@@ -4,9 +4,9 @@ import {
ShrinkOutlined,
SyncOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { useLazyQuery, useQuery } from "@apollo/client";
import { Badge, Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd";
import React, { useEffect, useState } from "react";
import React, { useCallback, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -47,12 +47,13 @@ export function ChatPopupComponent({
...(pollInterval > 0 ? { pollInterval } : {}),
});
const { loading, data, refetch, called } = useQuery(CONVERSATION_LIST_QUERY, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
skip: !chatVisible,
...(pollInterval > 0 ? { pollInterval } : {}),
});
const [getConversations, { loading, data, refetch, fetchMore }] =
useLazyQuery(CONVERSATION_LIST_QUERY, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
skip: !chatVisible,
...(pollInterval > 0 ? { pollInterval } : {}),
});
const fcmToken = sessionStorage.getItem("fcmtoken");
@@ -65,15 +66,22 @@ export function ChatPopupComponent({
}, [fcmToken]);
useEffect(() => {
if (called && chatVisible) refetch();
}, [chatVisible, called, refetch]);
if (chatVisible)
getConversations({
variables: {
offset: 0,
},
});
}, [chatVisible, getConversations]);
// const unreadCount = data
// ? data.conversations.reduce(
// (acc, val) => val.messages_aggregate.aggregate.count + acc,
// 0
// )
// : 0;
const loadMoreConversations = useCallback(() => {
if (data)
fetchMore({
variables: {
offset: data.conversations.length,
},
});
}, [data, fetchMore]);
const unreadCount = unreadData?.messages_aggregate.aggregate.count || 0;
@@ -110,6 +118,7 @@ export function ChatPopupComponent({
) : (
<ChatConversationListComponent
conversationList={data ? data.conversations : []}
loadMoreConversations={loadMoreConversations}
/>
)}
</Col>

View File

@@ -59,6 +59,14 @@ export default function ContractsCarsComponent({
sortOrder:
state.sortedInfo.columnKey === "model" && state.sortedInfo.order,
},
{
title: t("courtesycars.fields.color"),
dataIndex: "color",
key: "color",
sorter: (a, b) => alphaSort(a.color, b.color),
sortOrder:
state.sortedInfo.columnKey === "color" && state.sortedInfo.order,
},
{
title: t("courtesycars.fields.plate"),
dataIndex: "plate",
@@ -93,6 +101,9 @@ export default function ContractsCarsComponent({
(cc.model || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(cc.color || "")
.toLowerCase()
.includes(state.search.toLowerCase()) ||
(cc.plate || "").toLowerCase().includes(state.search.toLowerCase())
);

View File

@@ -2,10 +2,10 @@ import { useQuery } from "@apollo/client";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import ContractJobsComponent from "./contract-jobs.component";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -15,6 +15,7 @@ export function ContractJobsContainer({ selectedJobState, bodyshop }) {
const { loading, error, data } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: {
statuses: bodyshop.md_ro_statuses.active_statuses || ["Open"],
isConverted: true,
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",

View File

@@ -4,14 +4,14 @@ import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link, useHistory, useLocation } from "react-router-dom";
import { setModalContext } from "../../redux/modals/modals.actions";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import ContractsFindModalContainer from "../contracts-find-modal/contracts-find-modal.container";
import { setModalContext } from "../../redux/modals/modals.actions";
import moment from "moment";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import moment from "moment";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({

View File

@@ -34,6 +34,18 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
{/* <FormFieldsChanged form={form} /> */}
<LayoutFormRow header={t("courtesycars.labels.vehicle")}>
<Form.Item
label={t("courtesycars.fields.year")}
name="year"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("courtesycars.fields.make")}
name="make"
@@ -58,18 +70,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
>
<Input />
</Form.Item>
<Form.Item
label={t("courtesycars.fields.year")}
name="year"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("courtesycars.fields.plate")}
name="plate"
@@ -118,7 +118,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
},
]}
>
<InputNumber precision={0} />
<InputNumber min={0} precision={0} />
</Form.Item>
<Form.Item
label={t("courtesycars.fields.fleetnumber")}
@@ -213,49 +213,24 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
>
<CourtesyCarStatus />
</Form.Item>
<Form.Item
label={t("courtesycars.fields.nextservicekm")}
name="nextservicekm"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber />
</Form.Item>
<div>
<Form.Item
label={t("courtesycars.fields.nextservicedate")}
name="nextservicedate"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
label={t("courtesycars.fields.nextservicekm")}
name="nextservicekm"
>
<FormDatePicker />
<InputNumber min={0} precision={0} />
</Form.Item>
<Form.Item
shouldUpdate={(p, c) =>
p.mileage !== c.mileage ||
p.nextservicedate !== c.nextservicedate ||
p.nextservicekm !== c.nextservicekm
p.mileage !== c.mileage || p.nextservicekm !== c.nextservicekm
}
>
{() => {
const nextservicedate = form.getFieldValue("nextservicedate");
const nextservicekm = form.getFieldValue("nextservicekm");
const mileageOver =
nextservicekm <= form.getFieldValue("mileage");
const dueForService =
nextservicedate && moment(nextservicedate).isBefore(moment());
if (mileageOver || dueForService)
if (mileageOver)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<span>
@@ -263,6 +238,36 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
{t("contracts.labels.cardueforservice")}
</span>
<span>{`${nextservicekm} km`}</span>
</Space>
);
return <></>;
}}
</Form.Item>
</div>
<div>
<Form.Item
label={t("courtesycars.fields.nextservicedate")}
name="nextservicedate"
>
<FormDatePicker />
</Form.Item>
<Form.Item
shouldUpdate={(p, c) => p.nextservicedate !== c.nextservicedate}
>
{() => {
const nextservicedate = form.getFieldValue("nextservicedate");
const dueForService =
nextservicedate &&
moment(nextservicedate).endOf("day").isSameOrBefore(moment());
if (dueForService)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<span>
<WarningFilled style={{ marginRight: ".3rem" }} />
{t("contracts.labels.cardueforservice")}
</span>
<span>
<DateFormatter>{nextservicedate}</DateFormatter>
</span>
@@ -283,12 +288,6 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
<Form.Item
label={t("courtesycars.fields.registrationexpires")}
name="registrationexpires"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<FormDatePicker />
</Form.Item>
@@ -300,7 +299,8 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
{() => {
const expires = form.getFieldValue("registrationexpires");
const dateover = expires && moment(expires).isBefore(moment());
const dateover =
expires && moment(expires).endOf("day").isBefore(moment());
if (dateover)
return (
@@ -330,14 +330,13 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
<FormDatePicker />
</Form.Item>
<Form.Item
shouldUpdate={(p, c) =>
p.insuranceexpires !== c.insuranceexpires
}
shouldUpdate={(p, c) => p.insuranceexpires !== c.insuranceexpires}
>
{() => {
const expires = form.getFieldValue("insuranceexpires");
const dateover = expires && moment(expires).isBefore(moment());
const dateover =
expires && moment(expires).endOf("day").isBefore(moment());
if (dateover)
return (

View File

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

View File

@@ -0,0 +1,244 @@
import {
BranchesOutlined,
ExclamationCircleFilled,
PauseCircleOutlined,
} from "@ant-design/icons";
import { Card, Space, Table, Tooltip } from "antd";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardScheduledInToday({ data, ...cardProps }) {
const { t } = useTranslation();
const [state, setState] = useState({
sortedInfo: {},
});
if (!data) return null;
if (!data.scheduled_in_today)
return <DashboardRefreshRequired {...cardProps} />;
const appt = []; // Flatten Data
data.scheduled_in_today.forEach((item) => {
if (item.job) {
var i = {
canceled: item.canceled,
id: item.id,
alt_transport: item.job.alt_transport,
clm_no: item.job.clm_no,
jobid: item.job.jobid,
ins_co_nm: item.job.ins_co_nm,
iouparent: item.job.iouparent,
ownerid: item.job.ownerid,
ownr_co_nm: item.job.ownr_co_nm,
ownr_ea: item.job.ownr_ea,
ownr_fn: item.job.ownr_fn,
ownr_ln: item.job.ownr_ln,
ownr_ph1: item.job.ownr_ph1,
ownr_ph2: item.job.ownr_ph2,
production_vars: item.job.production_vars,
ro_number: item.job.ro_number,
suspended: item.job.suspended,
v_make_desc: item.job.v_make_desc,
v_model_desc: item.job.v_model_desc,
v_model_yr: item.job.v_model_yr,
v_vin: item.job.v_vin,
vehicleid: item.job.vehicleid,
note: item.note,
start: moment(item.start).format("hh:mm a"),
title: item.title,
};
appt.push(i);
}
});
appt.sort(function (a, b) {
return new moment(a.start) - new moment(b.start);
});
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
render: (text, record) => (
<Link
to={"/manage/jobs/" + record.jobid}
onClick={(e) => e.stopPropagation()}
>
<Space>
{record.ro_number || t("general.labels.na")}
{record.production_vars && record.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" />
) : null}
{record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
{record.iouparent && (
<Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} />
</Tooltip>
)}
</Space>
</Link>
),
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
ellipsis: true,
responsive: ["md"],
render: (text, record) => {
return record.ownerid ? (
<Link
to={"/manage/owners/" + record.ownerid}
onClick={(e) => e.stopPropagation()}
>
<OwnerNameDisplay ownerObject={record} />
</Link>
) : (
<span>
<OwnerNameDisplay ownerObject={record} />
</span>
);
},
},
{
title: t("jobs.fields.ownr_ph1"),
dataIndex: "ownr_ph1",
key: "ownr_ph1",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
),
},
{
title: t("jobs.fields.ownr_ea"),
dataIndex: "ownr_ea",
key: "ownr_ea",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
),
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<Link
to={"/manage/vehicles/" + record.vehicleid}
onClick={(e) => e.stopPropagation()}
>
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}
</Link>
) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}</span>
);
},
},
{
title: t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
responsive: ["md"],
},
{
title: t("appointments.fields.time"),
dataIndex: "start",
key: "start",
ellipsis: true,
responsive: ["md"],
},
{
title: t("appointments.fields.alt_transport"),
dataIndex: "alt_transport",
key: "alt_transport",
ellipsis: true,
responsive: ["md"],
},
];
const handleTableChange = (sorter) => {
setState({ ...state, sortedInfo: sorter });
};
return (
<Card
title={t("dashboard.titles.scheduledintoday", {
date: moment().startOf("day").format("MM/DD/YYYY"),
})}
{...cardProps}
>
<div style={{ height: "100%" }}>
<Table
onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
scroll={{ x: true, y: "calc(100% - 2em)" }}
rowKey="id"
style={{ height: "85%" }}
dataSource={appt}
/>
</div>
</Card>
);
}
export const DashboardScheduledInTodayGql = `
scheduled_in_today: appointments(where: {start: {_gte: "${moment()
.startOf("day")
.toISOString()}", _lte: "${moment()
.endOf("day")
.toISOString()}"}, canceled: {_eq: false}, block: {_neq: true}}) {
canceled
id
job {
alt_transport
clm_no
jobid: id
ins_co_nm
iouparent
ownerid
ownr_co_nm
ownr_ea
ownr_fn
ownr_ln
ownr_ph1
ownr_ph2
production_vars
ro_number
suspended
v_make_desc
v_model_desc
v_model_yr
v_vin
vehicleid
}
note
start
title
}
`;

View File

@@ -0,0 +1,210 @@
import {
BranchesOutlined,
ExclamationCircleFilled,
PauseCircleOutlined,
} from "@ant-design/icons";
import { Card, Space, Table, Tooltip } from "antd";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import ChatOpenButton from "../../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component";
import DashboardRefreshRequired from "../refresh-required.component";
export default function DashboardScheduledOutToday({ data, ...cardProps }) {
const { t } = useTranslation();
const [state, setState] = useState({
sortedInfo: {},
});
if (!data) return null;
if (!data.scheduled_out_today)
return <DashboardRefreshRequired {...cardProps} />;
data.scheduled_out_today.forEach((item) => {
item.scheduled_completion= moment(item.scheduled_completion).format("hh:mm a")
});
data.scheduled_out_today.sort(function (a, b) {
return new Date(a.scheduled_completion) - new Date(b.scheduled_completion);
});
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
render: (text, record) => (
<Link
to={"/manage/jobs/" + record.jobid}
onClick={(e) => e.stopPropagation()}
>
<Space>
{record.ro_number || t("general.labels.na")}
{record.production_vars && record.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" />
) : null}
{record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
{record.iouparent && (
<Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} />
</Tooltip>
)}
</Space>
</Link>
),
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
ellipsis: true,
responsive: ["md"],
render: (text, record) => {
return record.ownerid ? (
<Link
to={"/manage/owners/" + record.ownerid}
onClick={(e) => e.stopPropagation()}
>
<OwnerNameDisplay ownerObject={record} />
</Link>
) : (
<span>
<OwnerNameDisplay ownerObject={record} />
</span>
);
},
},
{
title: t("jobs.fields.ownr_ph1"),
dataIndex: "ownr_ph1",
key: "ownr_ph1",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.jobid} />
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.jobid} />
),
},
{
title: t("jobs.fields.ownr_ea"),
dataIndex: "ownr_ea",
key: "ownr_ea",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ea} jobid={record.jobid} />
),
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<Link
to={"/manage/vehicles/" + record.vehicleid}
onClick={(e) => e.stopPropagation()}
>
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}
</Link>
) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}</span>
);
},
},
{
title: t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
responsive: ["md"],
},
{
title: t("jobs.fields.scheduled_completion"),
dataIndex: "scheduled_completion",
key: "scheduled_completion",
ellipsis: true,
responsive: ["md"],
},
{
title: t("appointments.fields.alt_transport"),
dataIndex: "alt_transport",
key: "alt_transport",
ellipsis: true,
responsive: ["md"],
},
];
const handleTableChange = (sorter) => {
setState({ ...state, sortedInfo: sorter });
};
return (
<Card
title={t("dashboard.titles.scheduledouttoday", {
date: moment().startOf("day").format("MM/DD/YYYY"),
})}
{...cardProps}
>
<div style={{ height: "100%" }}>
<Table
onChange={handleTableChange}
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
scroll={{ x: true, y: "calc(100% - 2em)" }}
rowKey="id"
style={{ height: "85%" }}
dataSource={data.scheduled_out_today}
/>
</div>
</Card>
);
}
export const DashboardScheduledOutTodayGql = `
scheduled_out_today: jobs(where: {
date_invoiced: {_is_null: true},
ro_number: {_is_null: false},
voided: {_eq: false},
scheduled_completion: {_gte: "${moment().startOf("day").toISOString()}",
_lte: "${moment().endOf("day").toISOString()}"}}) {
alt_transport
clm_no
jobid: id
ins_co_nm
iouparent
ownerid
ownr_co_nm
ownr_ea
ownr_fn
ownr_ln
ownr_ph1
ownr_ph2
production_vars
ro_number
scheduled_completion
suspended
v_make_desc
v_model_desc
v_model_yr
v_vin
vehicleid
}
`;

View File

@@ -1,6 +1,6 @@
import Icon, { SyncOutlined } from "@ant-design/icons";
import { gql, useMutation, useQuery } from "@apollo/client";
import { Button, Dropdown, Menu, notification, PageHeader, Space } from "antd";
import { Button, Dropdown, Menu, PageHeader, Space, notification } from "antd";
import i18next from "i18next";
import _ from "lodash";
import moment from "moment";
@@ -37,6 +37,12 @@ import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
//Combination of the following:
// /node_modules/react-grid-layout/css/styles.css
// /node_modules/react-resizable/css/styles.css
import DashboardScheduledInToday, {
DashboardScheduledInTodayGql,
} from "../dashboard-components/scheduled-in-today/scheduled-in-today.component";
import DashboardScheduledOutToday, {
DashboardScheduledOutTodayGql,
} from "../dashboard-components/scheduled-out-today/scheduled-out-today.component";
import "./dashboard-grid.styles.scss";
import { GenerateDashboardData } from "./dashboard-grid.utils";
@@ -268,6 +274,28 @@ const componentList = {
w: 2,
h: 2,
},
ScheduleInToday: {
label: i18next.t("dashboard.titles.scheduledintoday", {
date: moment().startOf("day").format("MM/DD/YYYY"),
}),
component: DashboardScheduledInToday,
gqlFragment: DashboardScheduledInTodayGql,
minW: 10,
minH: 2,
w: 10,
h: 2,
},
ScheduleOutToday: {
label: i18next.t("dashboard.titles.scheduledouttoday", {
date: moment().startOf("day").format("MM/DD/YYYY"),
}),
component: DashboardScheduledOutToday,
gqlFragment: DashboardScheduledOutTodayGql,
minW: 10,
minH: 2,
w: 10,
h: 2,
},
};
const createDashboardQuery = (state) => {
@@ -283,8 +311,12 @@ const createDashboardQuery = (state) => {
monthly_sales: jobs(where: {_and: [
{ voided: {_eq: false}},
{date_invoiced: {_gte: "${moment()
.startOf("month").startOf('day').toISOString()}"}}, {date_invoiced: {_lte: "${moment()
.endOf("month").endOf('day').toISOString()}"}}]}) {
.startOf("month")
.startOf("day")
.toISOString()}"}}, {date_invoiced: {_lte: "${moment()
.endOf("month")
.endOf("day")
.toISOString()}"}}]}) {
id
ro_number
date_invoiced

View File

@@ -18,7 +18,7 @@ export default function DataLabel({
<div {...props} style={{ display: "flex" }}>
<div
style={{
flex: 2,
// flex: 2,
marginRight: ".2rem",
}}
>

View File

@@ -66,7 +66,7 @@ export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) {
key: "status",
},
{
title: t("jobs.fields.ro_number"),
title: t("bills.fields.invoice_number"),
dataIndex: ["Posting", "Reference"],
key: "reference",
},

View File

@@ -11,6 +11,7 @@ import {
Select,
Space,
Statistic,
Switch,
Typography,
} from "antd";
import Dinero from "dinero.js";
@@ -183,6 +184,20 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
<Space>
<DmsCdkMakes form={form} socket={socket} job={job} />
<DmsCdkMakesRefetch />
<Form.Item
name="dms_unsold"
label={t("jobs.fields.dms.dms_unsold")}
initialValue={false}
>
<Switch />
</Form.Item>
<Form.Item
name="dms_model_override"
label={t("jobs.fields.dms.dms_model_override")}
initialValue={false}
>
<Switch />
</Form.Item>
</Space>
</div>
)}

View File

@@ -1,28 +1,28 @@
import { UploadOutlined, UserAddOutlined } from "@ant-design/icons";
import {
Button,
Divider,
Dropdown,
Form,
Input,
Menu,
Select,
Space,
Tabs,
Upload,
Space,
Menu,
Dropdown,
Button,
} from "antd";
import _ from "lodash";
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 { selectEmailConfig } from "../../redux/email/email.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
import { selectEmailConfig } from "../../redux/email/email.selectors";
import EmailDocumentsComponent from "../email-documents/email-documents.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -54,6 +54,15 @@ export function EmailOverlayComponent({
]),
});
};
const handle_CC_Click = ({ item, key, keyPath }) => {
const email = item.props.value;
form.setFieldsValue({
cc: _.uniq([
...(form.getFieldValue("cc") || ""),
...(typeof email === "string" ? [email] : email),
]),
});
};
const menu = (
<div>
@@ -74,6 +83,25 @@ export function EmailOverlayComponent({
</div>
);
const menuCC = (
<div>
<Menu onClick={handle_CC_Click}>
{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>
))}
{bodyshop.md_to_emails.map((e, idx) => (
<Menu.Item value={e.emails} key={idx + "group"}>
{e.label}
</Menu.Item>
))}
</Menu>
</div>
);
return (
<div>
<Form.Item
@@ -122,7 +150,23 @@ export function EmailOverlayComponent({
>
<Select mode="tags" tokenSeparators={[",", ";"]} />
</Form.Item>
<Form.Item label={t("emails.fields.cc")} name="cc">
<Form.Item
label={
<Space>
{t("emails.fields.cc")}
<Dropdown overlay={menuCC}>
<a
className="ant-dropdown-link"
href=" #"
onClick={(e) => e.preventDefault()}
>
<UserAddOutlined />
</a>
</Dropdown>
</Space>
}
name="cc"
>
<Select mode="tags" tokenSeparators={[",", ";"]} />
</Form.Item>
<Form.Item

View File

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

View File

@@ -54,6 +54,7 @@ export default function GlobalSearchOs() {
job.v_make_desc || ""
} ${job.v_model_desc || ""}`}</span>
<span>{`${job.clm_no || ""}`}</span>
<span>{`${job.plate_no || ""}`}</span>
</Space>
</Link>
),

View File

@@ -1,10 +1,9 @@
import { useTreatments } from "@splitsoftware/splitio-react";
import Icon, {
BankFilled,
BarChartOutlined,
CarFilled,
ClockCircleFilled,
CheckCircleOutlined,
ClockCircleFilled,
DashboardFilled,
DollarCircleFilled,
ExportOutlined,
@@ -26,6 +25,7 @@ import Icon, {
UnorderedListOutlined,
UserOutlined,
} from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Layout, Menu } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -70,6 +70,8 @@ const mapDispatchToProps = (dispatch) => ({
setReportCenterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "reportCenter" })),
signOutStart: () => dispatch(signOutStart()),
setCardPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
});
function Header({
@@ -83,6 +85,7 @@ function Header({
setPaymentContext,
setReportCenterContext,
recentItems,
setCardPaymentContext,
}) {
const { Simple_Inventory } = useTreatments(
["Simple_Inventory"],
@@ -94,6 +97,11 @@ function Header({
{},
bodyshop && bodyshop.imexshopid
);
const { ImEXPay } = useTreatments(
["ImEXPay"],
{},
bodyshop && bodyshop.imexshopid
);
const { t } = useTranslation();
@@ -240,6 +248,20 @@ function Header({
>
{t("menus.header.enterpayment")}
</Menu.Item>
{ImEXPay.treatment === "on" && (
<Menu.Item
key="entercardpayments"
onClick={() => {
setCardPaymentContext({
actions: {},
context: {},
});
}}
icon={<Icon component={FaCreditCard} />}
>
{t("menus.header.entercardpayment")}
</Menu.Item>
)}
<Menu.Divider key="div5" />
<Menu.Item key="timetickets" icon={<FieldTimeOutlined />}>
<Link to="/manage/timetickets">
@@ -252,7 +274,11 @@ function Header({
onClick={() => {
setTimeTicketContext({
actions: {},
context: {},
context: {
created_by: currentUser.displayName
? currentUser.email.concat(" | ", currentUser.displayName)
: currentUser.email,
},
});
}}
>
@@ -311,7 +337,9 @@ function Header({
icon={<SettingOutlined />}
>
<Menu.Item key="shop" icon={<Icon component={GiSettingsKnobs} />}>
<Link to="/manage/shop">{t("menus.header.shop_config")}</Link>
<Link to="/manage/shop?tab=info">
{t("menus.header.shop_config")}
</Link>
</Menu.Item>
<Menu.Item key="dashboard" icon={<DashboardFilled />}>
<Link to="/manage/dashboard">{t("menus.header.dashboard")}</Link>

View File

@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { SEARCH_VENDOR_AUTOCOMPLETE_WITH_ADDR } from "../../graphql/vendors.queries";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
@@ -13,13 +14,14 @@ import VendorSearchSelect from "../vendor-search-select/vendor-search-select.com
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(Jobd3RdPartyModal);
export function Jobd3RdPartyModal({ bodyshop, jobId, job }) {
export function Jobd3RdPartyModal({ bodyshop, jobId, job, technician }) {
const [isModalVisible, setIsModalVisible] = useState(false);
const { t } = useTranslation();
const [form] = Form.useForm();
@@ -212,7 +214,9 @@ export function Jobd3RdPartyModal({ bodyshop, jobId, job }) {
]}
>
<Radio.Group>
<Radio value={"e"}>{t("parts_orders.labels.email")}</Radio>
{!technician ? (
<Radio value={"e"}>{t("parts_orders.labels.email")}</Radio>
) : null}
<Radio value={"p"}>{t("parts_orders.labels.print")}</Radio>
</Radio.Group>
</Form.Item>

View File

@@ -4,6 +4,7 @@ import {
Divider,
Dropdown,
Form,
Input,
Menu,
notification,
Popover,
@@ -29,11 +30,13 @@ import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import DataLabel from "../data-label/data-label.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
import ScheduleAtChange from "./job-at-change.component";
import ScheduleEventColor from "./schedule-event.color.component";
import ScheduleEventNote from "./schedule-event.note.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import { useMutation } from "@apollo/client";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -58,16 +61,44 @@ export function ScheduleEventComponent({
const [visible, setVisible] = useState(false);
const history = useHistory();
const searchParams = queryString.parse(useLocation().search);
const [updateAppointment] = useMutation(UPDATE_APPOINTMENT);
const [title, setTitle] = useState(event.title);
const blockContent = (
<div>
<Space direction="vertical" wrap>
<Input
value={title}
onChange={(e) => setTitle(e.currentTarget.value)}
onBlur={async () => {
await updateAppointment({
variables: {
appid: event.id,
app: {
title: title,
},
},
optimisticResponse: {
update_appointments: {
__typename: "appointments_mutation_response",
returning: [
{
...event,
title: title,
__typename: "appointments",
},
],
},
},
});
}}
/>
<Button
onClick={() => handleCancel({ id: event.id })}
disabled={event.arrived}
>
{t("appointments.actions.cancel")}
{t("appointments.actions.unblock")}
</Button>
</div>
</Space>
);
const popoverContent = (
@@ -208,46 +239,56 @@ export function ScheduleEventComponent({
<Button>{t("appointments.actions.sendreminder")}</Button>
</Dropdown>
) : null}
<Popover
trigger="click"
disabled={event.arrived}
content={
<Form
layout="vertical"
onFinish={({ lost_sale_reason }) => {
handleCancel({ id: event.id, lost_sale_reason });
}}
>
<Form.Item
name="lost_sale_reason"
label={t("jobs.fields.lost_sale_reason")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
label: lsr,
value: lsr,
}))}
/>
</Form.Item>
<Button htmlType="submit">
{t("appointments.actions.cancel")}
</Button>
</Form>
}
>
{event.arrived ? (
<Button
// onClick={() => handleCancel(event.id)}
disabled={event.arrived}
>
{t("appointments.actions.cancel")}
</Button>
</Popover>
) : (
<Popover
trigger="click"
disabled={event.arrived}
content={
<Form
layout="vertical"
onFinish={({ lost_sale_reason }) => {
handleCancel({ id: event.id, lost_sale_reason });
}}
>
<Form.Item
name="lost_sale_reason"
label={t("jobs.fields.lost_sale_reason")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
label: lsr,
value: lsr,
}))}
/>
</Form.Item>
<Button htmlType="submit">
{t("appointments.actions.cancel")}
</Button>
</Form>
}
>
<Button
// onClick={() => handleCancel(event.id)}
disabled={event.arrived}
>
{t("appointments.actions.cancel")}
</Button>
</Popover>
)}
{event.isintake ? (
<Button
disabled={event.arrived}

View File

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

View File

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

View File

@@ -401,7 +401,7 @@ export function JobLinesComponent({
const markedTypes = [e.key];
if (e.key === "PAN") markedTypes.push("PAP");
if (e.key === "PAS") markedTypes.push("PASL");
setSelectedLines(
setSelectedLines((selectedLines) =>
_.uniq([
...selectedLines,
...jobLines.filter((item) => markedTypes.includes(item.part_type)),
@@ -614,8 +614,17 @@ export function JobLinesComponent({
onSelectAll: (selected, selectedRows, changeRows) => {
setSelectedLines(selectedRows);
},
onSelect: (record, selected, selectedRows, nativeEvent) =>
setSelectedLines(selectedRows),
onSelect: (record, selected, selectedRows, nativeEvent) => {
if (selected) {
setSelectedLines((selectedLines) =>
_.uniqBy([...selectedLines, record], "id")
);
} else {
setSelectedLines((selectedLines) =>
selectedLines.filter((l) => l.id !== record.id)
);
}
},
}}
/>
</div>

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client";
import { notification, Select } from "antd";
import React, { useEffect, useState } from "react";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -56,8 +56,10 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
<LoadingSpinner loading={loading}>
<Select
autoFocus
allowClear
dropdownMatchSelectWidth={100}
value={location}
onClear={() => setLocation(null)}
onSelect={handleChange}
onBlur={handleSave}
>

View File

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

View File

@@ -1,19 +1,26 @@
import { Button, Card, Space, Table } from "antd";
import { EditFilled } from "@ant-design/icons";
import { Button, Card, Space, Table } from "antd";
import Dinero from "dinero.js";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import {
openChatByPhone,
setMessage,
} from "../../redux/messaging/messaging.actions";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort, dateSort } from "../../utils/sorters";
import DataLabel from "../data-label/data-label.component";
import PaymentExpandedRowComponent from "../payment-expanded-row/payment-expanded-row.component";
import PaymentsGenerateLink from "../payments-generate-link/payments-generate-link.component";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -23,20 +30,34 @@ const mapStateToProps = createStructuredSelector({
const mapDispatchToProps = (dispatch) => ({
setPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "payment" })),
setCardPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
setMessage: (text) => dispatch(setMessage(text)),
});
export function JobPayments({
job,
jobRO,
bodyshop,
setMessage,
openChatByPhone,
setPaymentContext,
setCardPaymentContext,
refetch,
}) {
const { ImEXPay } = useTreatments(
["ImEXPay"],
{},
bodyshop && bodyshop.imexshopid
);
const { t } = useTranslation();
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: {},
});
const columns = [
{
title: t("payments.fields.date"),
@@ -99,7 +120,7 @@ export function JobPayments({
render: (text, record) => (
<Space wrap>
<Button
disabled={record.exportedat}
// disabled={record.exportedat}
onClick={() => {
setPaymentContext({
actions: { refetch: refetch },
@@ -149,6 +170,21 @@ export function JobPayments({
title={t("payments.labels.title")}
extra={
<Space wrap>
{ImEXPay.treatment === "on" && (
<>
<Button
onClick={() =>
setCardPaymentContext({
actions: { refetch },
context: { jobid: job.id, balance },
})
}
>
{t("menus.header.entercardpayment")}
</Button>
<PaymentsGenerateLink job={job} />
</>
)}
<Button
disabled={!job.converted}
onClick={() =>
@@ -160,6 +196,7 @@ export function JobPayments({
>
{t("menus.header.enterpayment")}
</Button>
<DataLabel
valueStyle={{ color: balance.getAmount() !== 0 ? "red" : "green" }}
label={t("payments.labels.balance")}
@@ -178,6 +215,11 @@ export function JobPayments({
scroll={{
x: true,
}}
expandable={{
expandedRowRender: (record) => (
<PaymentExpandedRowComponent record={record} bodyshop={bodyshop} />
),
}}
summary={() => (
<>
<Table.Summary.Row>

View File

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

View File

@@ -1,18 +1,19 @@
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import { useMutation } from "@apollo/client";
import { Button, Form, notification } from "antd";
import moment from "moment";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
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";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import DateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { DateTimeFormat } from "./../../utils/DateFormatter";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -38,8 +39,8 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
setLoading(true);
const result = await updateJob({
variables: { jobId: job.id, job: values },
refetchQueries: ['GET_JOB_BY_PK'],
awaitRefetchQueries:true
refetchQueries: ["GET_JOB_BY_PK"],
awaitRefetchQueries: true,
});
const changedAuditFields = form.getFieldsValue(
@@ -53,7 +54,7 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
operation: AuditTrailMapping.admin_jobfieldchange(
key,
changedAuditFields[key] instanceof moment
? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a")
? DateTimeFormat(changedAuditFields[key])
: changedAuditFields[key]
),
});
@@ -126,7 +127,10 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
<Form.Item label={t("jobs.fields.actual_in")} name="actual_in">
<DateTimePicker />
</Form.Item>
<Form.Item label={t("jobs.fields.date_repairstarted")} name="date_repairstarted">
<Form.Item
label={t("jobs.fields.date_repairstarted")}
name="date_repairstarted"
>
<DateTimePicker />
</Form.Item>
<Form.Item
@@ -173,6 +177,15 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
>
<DateTimePicker />
</Form.Item>
<Form.Item label={t("jobs.fields.date_void")} name="date_void">
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.date_lost_sale")}
name="date_lost_sale"
>
<DateTimePicker />
</Form.Item>
</LayoutFormRow>
</Form>

View File

@@ -1,19 +1,18 @@
import { useMutation } from "@apollo/client";
import { gql, useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import { gql } from "@apollo/client";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import moment from "moment";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import moment from "moment";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
@@ -150,6 +149,10 @@ export function JobAdminMarkReexport({
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobuninvoice(),
});
} else {
notification["error"]({
message: t("jobs.errors.saving", {

View File

@@ -33,8 +33,9 @@ export function JobsAdminUnvoid({
mutation UNVOID_JOB($jobId: uuid!) {
update_jobs_by_pk(pk_columns: {id: $jobId}, _set: {voided: false, status: "${
bodyshop.md_ro_statuses.default_imported
}"}) {
}", date_void: null}) {
id
date_void
voided
status
}

View File

@@ -3,7 +3,7 @@ import {
useApolloClient,
useLazyQuery,
useMutation,
useQuery
useQuery,
} from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Col, notification, Row } from "antd";
@@ -20,7 +20,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import {
DELETE_AVAILABLE_JOB,
QUERY_AVAILABLE_JOBS,
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK,
} from "../../graphql/available-jobs.queries";
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
@@ -28,7 +28,7 @@ import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import {
selectBodyshop,
selectCurrentUser
selectCurrentUser,
} from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
@@ -73,6 +73,8 @@ export function JobsAvailableContainer({
const [selectedJob, setSelectedJob] = useState(null);
const [selectedOwner, setSelectedOwner] = useState(null);
const [partsQueueToggle, setPartsQueueToggle] = useState(bodyshop.md_functionality_toggles.parts_queue_toggle);
const [insertLoading, setInsertLoading] = useState(false);
const [insertNote] = useMutation(INSERT_NEW_NOTE);
@@ -94,6 +96,7 @@ export function JobsAvailableContainer({
logImEXEvent("job_import_new");
setOwnerModalVisible(false);
setInsertLoading(true);
const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
@@ -120,7 +123,7 @@ export function JobsAvailableContainer({
let existingVehicles;
if (estData.est_data.v_vin) {
//There's vehicle data, need to double check the VIN.
//There's vehicle data, need to double-check the VIN.
existingVehicles = await client.query({
query: SEARCH_VEHICLE_BY_VIN,
variables: {
@@ -135,6 +138,7 @@ export function JobsAvailableContainer({
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
job_totals: newTotals,
date_open: moment(),
status: bodyshop.md_ro_statuses.default_imported,
notes: {
data: {
created_by: currentUser.email,
@@ -142,7 +146,7 @@ export function JobsAvailableContainer({
text: t("jobs.labels.importnote"),
},
},
queued_for_parts: true,
queued_for_parts: partsQueueToggle,
...(existingVehicles && existingVehicles.data.vehicles.length > 0
? { vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null }
: {}),
@@ -156,46 +160,51 @@ export function JobsAvailableContainer({
delete newJob.vehicle;
}
insertNewJob({
variables: {
job: newJob,
},
})
.then((r) => {
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
}
notification["success"]({
message: t("jobs.successes.created"),
onClick: () => {
history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
},
});
//Job has been inserted. Clean up the available jobs record.
try {
const r = await insertNewJob({
variables: {
job: newJob,
},
});
insertAuditTrail({
jobid: r.data.insert_jobs.returning[0].id,
operation: AuditTrailMapping.jobimported(),
});
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
}
deleteJob({
variables: { id: estData.id },
}).then((r) => {
refetch();
setInsertLoading(false);
});
})
.catch((r) => {
//error while inserting
notification["error"]({
message: t("jobs.errors.creating", { error: r.message }),
});
notification["success"]({
message: t("jobs.successes.created"),
onClick: () => {
history.push(`/manage/jobs/${r.data.insert_jobs.returning[0].id}`);
},
});
//Job has been inserted. Clean up the available jobs record.
insertAuditTrail({
jobid: r.data.insert_jobs.returning[0].id,
operation: AuditTrailMapping.jobimported(),
});
deleteJob({
variables: { id: estData.id },
}).then((r) => {
refetch();
setInsertLoading(false);
});
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
} catch (err) {
//error while inserting
notification["error"]({
message: t("jobs.errors.creating", { error: err.message }),
});
refetch().catch(e => {console.error(`Something went wrong in jobs available table container - ${err.message || ''}`)});
setInsertLoading(false);
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
}
};
//Suplement scenario
//Supplement scenario
const onJobFindModalOk = async () => {
logImEXEvent("job_import_supplement");
@@ -247,11 +256,14 @@ export function JobsAvailableContainer({
// "0.00"
// ),
// job_totals: newTotals,
// queued_for_parts: true,
queued_for_parts: partsQueueToggle,
},
},
});
if (CriticalPartsScanning.treatment === "on") {
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
}
if (updateResult.errors) {
@@ -326,12 +338,14 @@ export function JobsAvailableContainer({
const onOwnerModalCancel = () => {
setOwnerModalVisible(false);
setSelectedOwner(null);
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
};
const onJobModalCancel = () => {
setJobModalVisible(false);
modalSearchState[1]("");
setSelectedJob(null);
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
};
const addJobAsNew = (record) => {
@@ -352,6 +366,8 @@ export function JobsAvailableContainer({
}, [addJobAsSupp, availableJobId, clm_no]);
if (error) return <AlertComponent type="error" message={error.message} />;
return (
<LoadingSpinner
loading={insertLoading}
@@ -361,11 +377,14 @@ export function JobsAvailableContainer({
loading={estDataRaw.loading}
error={estDataRaw.error}
owner={owner}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
selectedOwner={selectedOwner}
setSelectedOwner={setSelectedOwner}
visible={ownerModalVisible}
onOk={onOwnerFindModalOk}
onCancel={onOwnerModalCancel}
/>
<JobsFindModalContainer
loading={estDataRaw.loading}
@@ -377,6 +396,8 @@ export function JobsAvailableContainer({
onOk={onJobFindModalOk}
onCancel={onJobModalCancel}
modalSearchState={modalSearchState}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
/>
<Row gutter={[16, 16]}>
<Col span={24}>

View File

@@ -4,22 +4,35 @@ import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useHistory } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import {
selectBodyshop,
selectCurrentUser,
} 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";
import client from "../../utils/GraphQLClient";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
function updateJobCache(items) {
client.cache.modify({
id: "ROOT_QUERY",
fields: {
jobs(existingJobs = []) {
return existingJobs.filter(
(jobRef) => jobRef.__ref.includes(items) === false
);
},
},
});
}
export function JobsCloseExportButton({
bodyshop,
currentUser,
@@ -101,6 +114,9 @@ export function JobsCloseExportButton({
//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);
const successfulTransactions = PartnerResponse.data.filter(
(r) => r.success
);
if (failedTransactions.length > 0) {
//Uh oh. At least one was no good.
failedTransactions.forEach((ft) => {
@@ -159,12 +175,15 @@ export function JobsCloseExportButton({
},
});
if (!jobUpdateResponse.errors) {
if (!!!jobUpdateResponse.errors) {
notification.open({
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
updateJobCache(
jobUpdateResponse.data.update_jobs.returning.map((job) => job.id)
);
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
@@ -173,13 +192,31 @@ export function JobsCloseExportButton({
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
notification.open({
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
updateJobCache([
...new Set(
successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "jobid"
: "id"
]
)
),
]);
}
if (setSelectedJobs) {
setSelectedJobs((selectedJobs) => {
return selectedJobs.filter((i) => i !== jobId);
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
setLoading(false);
};

View File

@@ -108,7 +108,7 @@ export function JobsConvertButton({
},
]}
>
<Select>
<Select showSearch>
{bodyshop.md_ins_cos.map((s, i) => (
<Select.Option key={i} value={s.name}>
{s.name}
@@ -219,13 +219,15 @@ export function JobsConvertButton({
</Select>
</Form.Item>
)}
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant"
valuePropName="checked"
>
<Switch />
</Form.Item>
{bodyshop.region_config.toLowerCase().startsWith("ca") && (
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant"
valuePropName="checked"
>
<Switch />
</Form.Item>
)}
<Form.Item
label={t("jobs.fields.driveable")}
name="driveable"

View File

@@ -224,13 +224,15 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
>
<CurrencyInput />
</Form.Item>
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant"
valuePropName="checked"
>
<Switch />
</Form.Item>
{bodyshop.region_config.toLowerCase().startsWith("ca") && (
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant"
valuePropName="checked"
>
<Switch />
</Form.Item>
)}
<Form.Item
label={t("jobs.fields.other_amount_payable")}
name="other_amount_payable"

View File

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

View File

@@ -5,10 +5,10 @@ import {
Dropdown,
Form,
Menu,
notification,
Popconfirm,
Popover,
Select,
notification,
} from "antd";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
@@ -18,12 +18,15 @@ import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { CANCEL_APPOINTMENTS_BY_JOB_ID } from "../../graphql/appointments.queries";
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { setModalContext } from "../../redux/modals/modals.actions";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent";
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component";
@@ -47,6 +50,10 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setModalContext({ context: context, modal: "jobCosting" })),
setTimeTicketContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
setCardPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JobsDetailHeaderActions({
@@ -60,6 +67,8 @@ export function JobsDetailHeaderActions({
setJobCostingContext,
jobRO,
setTimeTicketContext,
setCardPaymentContext,
insertAuditTrail,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -136,63 +145,73 @@ export function JobsDetailHeaderActions({
<Menu.Item
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
>
<Popover
trigger="click"
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
content={
<Form
layout="vertical"
onFinish={async ({ lost_sale_reason }) => {
const jobUpdate = await cancelAllAppointments({
variables: {
jobid: job.id,
job: {
date_scheduled: null,
scheduled_in: null,
scheduled_completion: null,
lost_sale_reason,
status: bodyshop.md_ro_statuses.default_imported,
{job.status !== bodyshop.md_ro_statuses.default_scheduled ? (
t("menus.jobsactions.cancelallappointments")
) : (
<Popover
trigger="click"
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
content={
<Form
layout="vertical"
onFinish={async ({ lost_sale_reason }) => {
const jobUpdate = await cancelAllAppointments({
variables: {
jobid: job.id,
job: {
date_scheduled: null,
scheduled_in: null,
scheduled_completion: null,
lost_sale_reason,
date_lost_sale: new Date(),
status: bodyshop.md_ro_statuses.default_imported,
},
},
},
});
if (!jobUpdate.errors) {
notification["success"]({
message: t("appointments.successes.canceled"),
});
return;
}
}}
>
<Form.Item
name="lost_sale_reason"
label={t("jobs.fields.lost_sale_reason")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
if (!jobUpdate.errors) {
notification["success"]({
message: t("appointments.successes.canceled"),
});
insertAuditTrail({
jobid: job.id,
operation:
AuditTrailMapping.appointmentcancel(lost_sale_reason),
});
return;
}
}}
>
<Select
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
label: lsr,
value: lsr,
}))}
/>
</Form.Item>
<Button
htmlType="submit"
disabled={
job.status !== bodyshop.md_ro_statuses.default_scheduled
}
>
{t("appointments.actions.cancel")}
</Button>
</Form>
}
>
{t("menus.jobsactions.cancelallappointments")}
</Popover>
<Form.Item
name="lost_sale_reason"
label={t("jobs.fields.lost_sale_reason")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
label: lsr,
value: lsr,
}))}
/>
</Form.Item>
<Button
htmlType="submit"
disabled={
job.status !== bodyshop.md_ro_statuses.default_scheduled
}
>
{t("appointments.actions.cancel")}
</Button>
</Form>
}
>
{t("menus.jobsactions.cancelallappointments")}
</Popover>
)}
</Menu.Item>
<Menu.Item
disabled={
@@ -238,7 +257,12 @@ export function JobsDetailHeaderActions({
setTimeTicketContext({
actions: {},
context: { jobId: job.id },
context: {
jobId: job.id,
created_by: currentUser.displayName
? currentUser.email.concat(" | ", currentUser.displayName)
: currentUser.email,
},
});
}}
>
@@ -258,6 +282,18 @@ export function JobsDetailHeaderActions({
>
{t("menus.header.enterpayment")}
</Menu.Item>
<Menu.Item
key="entercardpayments"
disabled={!job.converted}
onClick={() => {
setCardPaymentContext({
actions: {},
context: { jobid: job.id },
});
}}
>
{t("menus.header.entercardpayment")}
</Menu.Item>
<Menu.Item key="cccontract" disabled={jobRO || !job.converted}>
<Link
to={{
@@ -461,54 +497,57 @@ export function JobsDetailHeaderActions({
)}
<JobsDetailHeaderActionsAddevent jobid={job.id} />
{!jobRO && job.converted && (
<Menu.Item>
<Popconfirm
title={t("jobs.labels.voidjob")}
okText="Yes"
cancelText="No"
onClick={(e) => e.stopPropagation()}
onConfirm={async () => {
//delete the job.
const result = await voidJob({
variables: {
jobId: job.id,
job: {
status: bodyshop.md_ro_statuses.default_void,
voided: true,
scheduled_in: null,
scheduled_completion: null,
inproduction: false,
},
note: [
{
jobid: job.id,
created_by: currentUser.email,
audit: true,
text: t("jobs.labels.voidnote"),
<RbacWrapper action="jobs:void" noauth>
<Menu.Item>
<Popconfirm
title={t("jobs.labels.voidjob")}
okText="Yes"
cancelText="No"
onClick={(e) => e.stopPropagation()}
onConfirm={async () => {
//delete the job.
const result = await voidJob({
variables: {
jobId: job.id,
job: {
status: bodyshop.md_ro_statuses.default_void,
voided: true,
scheduled_in: null,
scheduled_completion: null,
inproduction: false,
date_void: new Date(),
},
],
},
});
note: [
{
jobid: job.id,
created_by: currentUser.email,
audit: true,
text: t("jobs.labels.voidnote"),
},
],
},
});
if (!!!result.errors) {
notification["success"]({
message: t("jobs.successes.voided"),
});
//go back to jobs list.
history.push(`/manage/`);
} else {
notification["error"]({
message: t("jobs.errors.voiding", {
error: JSON.stringify(result.errors),
}),
});
}
}}
getPopupContainer={(trigger) => trigger.parentNode}
>
{t("menus.jobsactions.void")}
</Popconfirm>
</Menu.Item>
if (!!!result.errors) {
notification["success"]({
message: t("jobs.successes.voided"),
});
//go back to jobs list.
history.push(`/manage/`);
} else {
notification["error"]({
message: t("jobs.errors.voiding", {
error: JSON.stringify(result.errors),
}),
});
}
}}
getPopupContainer={(trigger) => trigger.parentNode}
>
{t("menus.jobsactions.void")}
</Popconfirm>
</Menu.Item>
</RbacWrapper>
)}
</Menu>
);

View File

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

View File

@@ -40,24 +40,26 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
>
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<Tooltip title={t("jobs.labels.ca_gst_all_if_null")}>
<Form.Item
label={t("jobs.fields.ca_customer_gst")}
name="ca_customer_gst"
>
<CurrencyInput
disabled={jobRO}
min={0}
max={
Math.round(
(job.job_totals &&
job.job_totals.totals.federal_tax.amount) ||
0
) / 100
}
/>
</Form.Item>
</Tooltip>
{bodyshop.region_config.toLowerCase().startsWith("ca") && (
<Tooltip title={t("jobs.labels.ca_gst_all_if_null")}>
<Form.Item
label={t("jobs.fields.ca_customer_gst")}
name="ca_customer_gst"
>
<CurrencyInput
disabled={jobRO}
min={0}
max={
Math.round(
(job.job_totals &&
job.job_totals.totals.federal_tax.amount) ||
0
) / 100
}
/>
</Form.Item>
</Tooltip>
)}
<Form.Item
label={t("jobs.fields.other_amount_payable")}
name="other_amount_payable"
@@ -82,12 +84,14 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
>
<CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
</Form.Item>
<Space align="center">
<Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt">
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<CABCpvrtCalculator form={form} disabled={jobRO} />
</Space>
{bodyshop.region_config === "CA_BC" && (
<Space align="center">
<Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt">
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>
<CABCpvrtCalculator form={form} disabled={jobRO} />
</Space>
)}
<Form.Item
label={t("jobs.fields.auto_add_ats")}
name="auto_add_ats"
@@ -141,13 +145,15 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant"
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
{bodyshop.region_config.toLowerCase().startsWith("ca") && (
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant"
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
)}
</FormRow>
<Divider
orientation="left"

View File

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

View File

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

View File

@@ -13,12 +13,26 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import client from "../../utils/GraphQLClient";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
function updateJobCache(items) {
client.cache.modify({
id: "ROOT_QUERY",
fields: {
jobs(existingJobs = []) {
return existingJobs.filter(
(jobRef) => jobRef.__ref.includes(items) === false
);
},
},
});
}
export function JobsExportAllButton({
bodyshop,
currentUser,
@@ -96,7 +110,9 @@ export function JobsExportAllButton({
Object.keys(groupedData).map(async (key) => {
//Check to see if any of them failed. If they didn't don't execute the update.
const failedTransactions = groupedData[key].filter((r) => !r.success);
const successfulTransactions = groupedData[key].filter(
(r) => r.success
);
if (failedTransactions.length > 0) {
//Uh oh. At least one was no good.
failedTransactions.forEach((ft) => {
@@ -155,12 +171,17 @@ export function JobsExportAllButton({
},
});
if (!jobUpdateResponse.errors) {
if (!!!jobUpdateResponse.errors) {
notification.open({
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
updateJobCache(
jobUpdateResponse.data.update_jobs.returning.map(
(job) => job.id
)
);
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
@@ -169,14 +190,31 @@ export function JobsExportAllButton({
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
notification.open({
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
updateJobCache([
...new Set(
successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "jobid"
: "id"
]
)
),
]);
}
}
})
);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false);
setLoading(false);
};

View File

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

View File

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

View File

@@ -1,8 +1,9 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Input, Space, Table, Typography } from "antd";
import axios from "axios";
import _ from "lodash";
import queryString from "query-string";
import React from "react";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom";
@@ -21,6 +22,8 @@ const mapDispatchToProps = (dispatch) => ({
export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
const search = queryString.parse(useLocation().search);
const [openSearchResults, setOpenSearchResults] = useState([]);
const [searchLoading, setSearchLoading] = useState(false);
const { page, sortcolumn, sortorder } = search;
const history = useHistory();
@@ -193,6 +196,28 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
history.push({ search: queryString.stringify(search) });
};
useEffect(() => {
if (search.search && search.search.trim() !== "") {
searchJobs();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function searchJobs(value) {
try {
setSearchLoading(true);
const searchData = await axios.post("/search", {
search: value || search.search,
index: "jobs",
});
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
} catch (error) {
console.log("Error while fetching search results", error);
} finally {
setSearchLoading(false);
}
}
return (
<Card
extra={
@@ -205,6 +230,7 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
<Button
onClick={() => {
delete search.search;
delete search.page;
history.push({ search: queryString.stringify(search) });
}}
>
@@ -220,24 +246,32 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
onSearch={(value) => {
search.search = value;
history.push({ search: queryString.stringify(search) });
searchJobs(value);
}}
loading={loading || searchLoading}
enterButton
/>
</Space>
}
>
<Table
loading={loading}
pagination={{
position: "top",
pageSize: 25,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}}
loading={loading || searchLoading}
pagination={
search?.search
? {
pageSize: 25,
showSizeChanger: false,
}
: {
pageSize: 25,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}
}
columns={columns}
rowKey="id"
dataSource={jobs}
dataSource={search?.search ? openSearchResults : jobs}
onChange={handleTableChange}
/>
</Card>

View File

@@ -112,7 +112,9 @@ export function JobsList({ bodyshop }) {
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sorter: (a, b) =>
parseInt((a.ro_number || "0").replace(/\D/g, "")) -
parseInt((b.ro_number || "0").replace(/\D/g, "")),
sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,

View File

@@ -75,6 +75,27 @@ export function JobNotesComponent({
</span>
),
},
{
title: t("notes.fields.type"),
dataIndex: "type",
key: "type",
width: 120,
filteredValue: filter?.type || null,
filters: [
{ value: "general", text: t("notes.fields.types.general") },
{ value: "customer", text: t("notes.fields.types.customer") },
{ value: "shop", text: t("notes.fields.types.shop") },
{ value: "office", text: t("notes.fields.types.office") },
{ value: "parts", text: t("notes.fields.types.parts") },
{ value: "paint", text: t("notes.fields.types.paint") },
{
value: "supplement",
text: t("notes.fields.types.supplement"),
},
],
onFilter: (value, record) => value.includes(record.type),
render: (text, record) => t(`notes.fields.types.${record.type}`),
},
{
title: t("notes.fields.text"),
dataIndex: "text",
@@ -106,7 +127,7 @@ export function JobNotesComponent({
title: t("notes.actions.actions"),
dataIndex: "actions",
key: "actions",
width: 150,
width: 200,
render: (text, record) => (
<Space wrap>
<Button

View File

@@ -1,8 +1,8 @@
import {
BranchesOutlined,
ExclamationCircleFilled,
PauseCircleOutlined,
SyncOutlined,
BranchesOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
@@ -14,8 +14,8 @@ import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { onlyUnique } from "../../utils/arrayHelper";
import { alphaSort } from "../../utils/sorters";
import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
@@ -46,6 +46,7 @@ export function JobsReadyList({ bodyshop }) {
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: {
statuses: readyStatuses,
isConverted: true,
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",

View File

@@ -207,7 +207,7 @@ export function LaborAllocationsTable({
<Card title={t("jobs.labels.laborallocations")}>
<Table
columns={columns}
rowKey="cost_center"
rowKey={(record) => `${record.cost_center} ${record.mod_lbr_ty}`}
pagination={false}
onChange={handleTableChange}
dataSource={totals}
@@ -231,7 +231,14 @@ export function LaborAllocationsTable({
{summary.adjustments.toFixed(1)}
</Table.Summary.Cell>
<Table.Summary.Cell>
{summary.difference.toFixed(1)}
<Typography.Text
style={{
fontWeight: "bold",
color: summary.difference >= 0 ? "green" : "red",
}}
>
{summary.difference.toFixed(1)}
</Typography.Text>
</Table.Summary.Cell>
</Table.Summary.Row>
)}

View File

@@ -1,4 +1,14 @@
import { Checkbox, Col, Form, Input, Row, Space, Switch, Tag } from "antd";
import {
Checkbox,
Col,
Form,
Input,
Row,
Select,
Space,
Switch,
Tag,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -46,6 +56,28 @@ export function NoteUpsertModalComponent({ form, noteUpsertModal }) {
<Switch />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item
label={t("notes.fields.type")}
name="type"
initialValue="general"
>
<Select
options={[
{ value: "general", label: t("notes.fields.types.general") },
{ value: "customer", label: t("notes.fields.types.customer") },
{ value: "shop", label: t("notes.fields.types.shop") },
{ value: "office", label: t("notes.fields.types.office") },
{ value: "parts", label: t("notes.fields.types.parts") },
{ value: "paint", label: t("notes.fields.types.paint") },
{
value: "supplement",
label: t("notes.fields.types.supplement"),
},
]}
/>
</Form.Item>
</Col>
<Col span={8}>
<NotesPresetButton form={form} />
</Col>

View File

@@ -6,6 +6,7 @@ import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort, statusSort } from "../../utils/sorters";
import OwnerDetailUpdateJobsComponent from "../owner-detail-update-jobs/owner-detail-update-jobs.component";
const mapStateToProps = createStructuredSelector({
@@ -15,6 +16,15 @@ const mapStateToProps = createStructuredSelector({
function OwnerDetailJobsComponent({ bodyshop, owner }) {
const { t } = useTranslation();
const [selectedJobs, setSelectedJobs] = useState([]);
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: { text: "" },
});
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const columns = [
{
title: t("jobs.fields.ro_number"),
@@ -26,6 +36,9 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
{record.ro_number || t("general.labels.na")}
</Link>
),
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
},
{
title: t("jobs.fields.vehicle"),
@@ -46,11 +59,17 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status",
sorter: (a, b) => statusSort(a.status, b.status, bodyshop.md_ro_statuses.statuses),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
},
{
@@ -60,6 +79,9 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
),
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
},
];
@@ -80,6 +102,7 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
scroll={{ x: true }}
rowKey="id"
dataSource={owner.jobs}
onChange={handleTableChange}
rowSelection={{
onSelect: (record, selected, selectedRows) => {
setSelectedJobs(selectedRows ? selectedRows.map((i) => i.id) : []);

View File

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

View File

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

View File

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

View File

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

View File

@@ -113,6 +113,8 @@ export function PartsOrderListTableComponent({
id: pol.id,
line_desc: pol.line_desc,
quantity: pol.quantity,
act_price: pol.act_price,
oem_partno: pol.oem_partno,
};
}),
},

View File

@@ -79,6 +79,20 @@ export function PartsReceiveModalComponent({ bodyshop, form }) {
>
<Input />
</Form.Item>
<Form.Item
label={t("joblines.fields.oem_partno")}
key={`${index}oem_partno`}
name={[field.name, "oem_partno"]}
>
<Input disabled />
</Form.Item>
<Form.Item
label={t("joblines.fields.act_price")}
key={`${index}act_price`}
name={[field.name, "act_price"]}
>
<Input disabled />
</Form.Item>
<Form.Item
label={t("joblines.fields.location")}
key={`${index}location`}

View File

@@ -1,26 +1,39 @@
import { useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import axios from "axios";
import _ from "lodash";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_BILLS } from "../../graphql/bills.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils";
import _ from "lodash";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { Link } from "react-router-dom";
import client from "../../utils/GraphQLClient";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
function updateBillCache(items) {
client.cache.modify({
id: "ROOT_QUERY",
fields: {
bills(existingJobs = []) {
return existingJobs.filter(
(billRef) => billRef.__ref.includes(items) === false
);
},
},
});
}
export function PayableExportAll({
bodyshop,
currentUser,
@@ -97,7 +110,9 @@ export function PayableExportAll({
proms.push(
(async () => {
const failedTransactions = groupedData[key].filter((r) => !r.success);
const successfulTransactions = groupedData[key].filter(
(r) => r.success
);
if (failedTransactions.length > 0) {
//Uh oh. At least one was no good.
failedTransactions.map((ft) =>
@@ -143,7 +158,15 @@ export function PayableExportAll({
const billUpdateResponse = await updateBill({
variables: {
billIdList: [key],
billIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo
? "billid"
: "id"
]
),
bill: {
exported: true,
exported_at: new Date(),
@@ -156,6 +179,11 @@ export function PayableExportAll({
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
updateBillCache(
billUpdateResponse.data.update_bills.returning.map(
(bill) => bill.id
)
);
} else {
notification["error"]({
message: t("bills.errors.exporting", {
@@ -164,6 +192,26 @@ export function PayableExportAll({
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
updateBillCache([
...new Set(
successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo
? "billid"
: "id"
]
)
),
]);
}
}
})()
);
@@ -172,8 +220,6 @@ export function PayableExportAll({
await Promise.all(proms);
if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
setLoading(false);
};

View File

@@ -4,22 +4,35 @@ import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils";
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_BILLS } from "../../graphql/bills.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { Link } from "react-router-dom";
import client from "../../utils/GraphQLClient";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
function updateBillCache(items) {
client.cache.modify({
id: "ROOT_QUERY",
fields: {
bills(existingJobs = []) {
return existingJobs.filter(
(billRef) => billRef.__ref.includes(items) === false
);
},
},
});
}
export function PayableExportButton({
bodyshop,
currentUser,
@@ -159,6 +172,11 @@ export function PayableExportButton({
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
updateBillCache(
billUpdateResponse.data.update_bills.returning.map(
(bill) => bill.id
)
);
} else {
notification["error"]({
message: t("bills.errors.exporting", {
@@ -167,7 +185,25 @@ export function PayableExportButton({
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
updateBillCache([
...new Set(
successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "billid"
: "id"
]
)
),
]);
}
if (setSelectedBills) {
setSelectedBills((selectedBills) => {

View File

@@ -0,0 +1,186 @@
import { useMutation, useQuery } from "@apollo/client";
import {
Button,
Descriptions,
InputNumber,
Modal,
Space,
notification,
} from "antd";
import axios from "axios";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import {
GET_REFUNDABLE_AMOUNT_BY_JOBID,
INSERT_PAYMENT_RESPONSE,
QUERY_PAYMENT_RESPONSE_BY_PAYMENT_ID,
} from "../../graphql/payment_response.queries";
import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateTimeFormatter } from "../../utils/DateFormatter";
const { confirm } = Modal;
const openNotificationWithIcon = (type, t) => {
notification[type]({
message: t("job_payments.notifications.error.title"),
description: t("job_payments.notifications.error.description"),
});
};
const PaymentExpandedRowComponent = ({ record, bodyshop }) => {
const [refundAmount, setRefundAmount] = useState(0);
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
const { t } = useTranslation();
const { loading, error, data } = useQuery(
QUERY_PAYMENT_RESPONSE_BY_PAYMENT_ID,
{
variables: {
paymentid: record.id,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
},
}
);
const { data: refundable_amount, refetch } = useQuery(
GET_REFUNDABLE_AMOUNT_BY_JOBID,
{
variables: {
jobid: record.jobid,
},
}
);
const insertPayments = async (payment_response, refund_response) => {
await insertPayment({
variables: {
paymentInput: {
amount: -refund_response.data.amount,
transactionid: payment_response.response.receiptelements.transid,
payer: record.payer,
type: "Refund",
jobid: payment_response.jobid,
date: moment(Date.now()),
},
},
update(cache, { data }) {
cache.modify({
id: cache.identify({
id: payment_response.jobid,
__typename: "jobs",
}),
fields: {
payments(payments) {
return [...data.insert_payments.returning, ...payments];
},
},
});
},
});
await insertPaymentResponse({
variables: {
paymentResponse: {
amount: -refund_response.data.amount,
bodyshopid: payment_response.bodyshopid,
paymentid: payment_response.paymentid,
jobid: payment_response.jobid,
declinereason: "Refund",
ext_paymentid: payment_response.ext_paymentid,
successful: true,
response: refund_response.data,
},
},
});
};
const showConfirm = (payment_response) => {
confirm({
title: "Do you want to refund payment?",
content:
"The payment will be refunded. Click OK to confirm and Cancel to dismiss.",
async onOk() {
const refundResponse = await axios.post("/intellipay/payment_refund", {
bodyshop,
amount: refundAmount,
paymentid: payment_response.ext_paymentid,
});
if (refundResponse.data.status < 0) {
openNotificationWithIcon("error", t);
return;
}
insertPayments(payment_response, refundResponse);
// refetch refundable amount
refetch();
},
onCancel() {},
});
};
if (loading) return null;
if (error) return <p>Error loading data. Please Reload</p>;
const payment_response = data.payment_response[0];
const max_refundable_amount =
refundable_amount?.payment_response_aggregate.aggregate.sum.amount;
return (
<div>
<Descriptions
title={t("job_payments.titles.descriptions")}
contentStyle={{ fontWeight: "600" }}
column={4}
>
<Descriptions.Item label={t("job_payments.titles.payer")}>
{record.payer}
</Descriptions.Item>
<Descriptions.Item label={t("job_payments.titles.payername")}>
{payment_response?.response?.nameOnCard ?? ""}
</Descriptions.Item>
<Descriptions.Item label={t("job_payments.titles.amount")}>
<CurrencyFormatter>{record.amount}</CurrencyFormatter>
</Descriptions.Item>
<Descriptions.Item label={t("job_payments.titles.dateOfPayment")}>
{<DateTimeFormatter>{record.created_at}</DateTimeFormatter>}
</Descriptions.Item>
<Descriptions.Item label={t("job_payments.titles.transactionid")}>
{record.transactionid}
</Descriptions.Item>
<Descriptions.Item label={t("job_payments.titles.paymentid")}>
{payment_response?.response?.paymentreferenceid ?? ""}
</Descriptions.Item>
<Descriptions.Item label={t("job_payments.titles.paymenttype")}>
{record.type}
</Descriptions.Item>
<Descriptions.Item label={t("job_payments.titles.paymentnum")}>
{record.paymentnum}
</Descriptions.Item>
{payment_response && (
<Descriptions.Item label={t("job_payments.titles.refundamount")}>
<Space>
<InputNumber
onChange={setRefundAmount}
max={max_refundable_amount}
min={0}
/>
<Button onClick={() => showConfirm(payment_response)}>
{t("job_payments.buttons.refundpayment")}
</Button>
</Space>
</Descriptions.Item>
)}
</Descriptions>
</div>
);
};
export default PaymentExpandedRowComponent;

View File

@@ -13,12 +13,26 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import client from "../../utils/GraphQLClient";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
function updatePaymentCache(items) {
client.cache.modify({
id: "ROOT_QUERY",
fields: {
payments(existingJobs = []) {
return existingJobs.filter(
(paymentRef) => paymentRef.__ref.includes(items) === false
);
},
},
});
}
export function PaymentExportButton({
bodyshop,
currentUser,
@@ -157,6 +171,11 @@ export function PaymentExportButton({
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
updatePaymentCache(
paymentUpdateResponse.data.update_payments.returning.map(
(payment) => payment.id
)
);
} else {
notification["error"]({
message: t("payments.errors.exporting", {
@@ -172,7 +191,25 @@ export function PaymentExportButton({
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
updatePaymentCache([
...new Set(
successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "paymentid"
: "id"
]
)
),
]);
}
if (!!loadingCallback) loadingCallback(false);
setLoading(false);
};

View File

@@ -0,0 +1,100 @@
import React from "react";
import { Button, notification } from "antd";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { setModalContext } from "../../redux/modals/modals.actions";
import { connect } from "react-redux";
import { UPDATE_PAYMENT } from "../../graphql/payments.queries";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import { createStructuredSelector } from "reselect";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
setPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "payment" })),
});
const PaymentMarkForExportButton = ({
bodyshop,
payment,
refetch,
setPaymentContext,
currentUser,
}) => {
const { t } = useTranslation();
const [insertExportLog, { loading: exportLogLoading }] =
useMutation(INSERT_EXPORT_LOG);
const [updatePayment, { loading: updatePaymentLoading }] =
useMutation(UPDATE_PAYMENT);
const handleClick = async () => {
const today = new Date();
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: payment.id,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayment({
variables: {
paymentId: payment.id,
payment: {
exportedat: today,
},
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessmarkforexport",
message: t("payments.successes.markexported"),
});
if (refetch) refetch();
setPaymentContext({
actions: {
refetch,
},
context: {
...payment,
exportedat: today,
},
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
});
}
};
return (
<Button
onClick={handleClick}
loading={exportLogLoading || updatePaymentLoading}
disabled={!!payment.exportedat}
>
{t("payments.labels.markexported")}
</Button>
);
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PaymentMarkForExportButton);

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client";
import { Button, Form, Modal, notification } from "antd";
import { Button, Form, Modal, notification, Space } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -19,6 +19,8 @@ import {
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import PaymentForm from "../payment-form/payment-form.component";
import PaymentReexportButton from "../payment-reexport-button/payment-reexport-button.component";
import PaymentMarkForExportButton from "../payment-mark-export-button/payment-mark-export-button-component";
const mapStateToProps = createStructuredSelector({
paymentModal: selectPayment,
@@ -52,7 +54,7 @@ function PaymentModalContainer({
const { useStripe, sendby, ...paymentObj } = values;
setLoading(true);
let updatedPayment; //Moved up from if statement for greater scope.
try {
if (!context || (context && !context.id)) {
const newPayment = await insertPayment({
@@ -87,7 +89,7 @@ function PaymentModalContainer({
);
}
} else {
const updatedPayment = await updatePayment({
updatedPayment = await updatePayment({
variables: {
paymentId: context.id,
payment: paymentObj,
@@ -101,7 +103,11 @@ function PaymentModalContainer({
}
}
if (actions.refetch) actions.refetch();
if (actions.refetch)
actions.refetch(
updatedPayment && updatedPayment.data.update_payments.returning[0]
);
if (enterAgain) {
const prev = form.getFieldsValue(["date"]);
@@ -172,12 +178,24 @@ function PaymentModalContainer({
</span>
}
>
{!context || (context && !context.id) ? null : (
<Space>
<PaymentReexportButton payment={context} refetch={actions.refetch} />
<PaymentMarkForExportButton
bodyshop={bodyshop}
payment={context}
refetch={actions.refetch}
/>
</Space>
)}
<Form
onFinish={handleFinish}
autoComplete={"off"}
form={form}
layout="vertical"
initialValues={context || {}}
disabled={context?.exportedat}
>
<PaymentForm form={form} />
</Form>

View File

@@ -0,0 +1,66 @@
import React from "react";
import { Button, notification } from "antd";
import { useTranslation } from "react-i18next";
import { UPDATE_PAYMENT } from "../../graphql/payments.queries";
import { useMutation } from "@apollo/client";
import { setModalContext } from "../../redux/modals/modals.actions";
import { connect } from "react-redux";
const mapDispatchToProps = (dispatch) => ({
setPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "payment" })),
});
const PaymentReexportButton = ({ payment, refetch, setPaymentContext }) => {
const { t } = useTranslation();
const [updatePayment, { loading }] = useMutation(UPDATE_PAYMENT);
const handleClick = async () => {
const paymentUpdateResponse = await updatePayment({
variables: {
paymentId: payment.id,
payment: {
exportedat: null,
},
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.markreexported"),
});
if (refetch) refetch();
setPaymentContext({
actions: {
refetch,
},
context: {
...payment,
exportedat: null,
},
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
});
}
};
return (
<Button
onClick={handleClick}
loading={loading}
disabled={!payment.exportedat}
>
{t("payments.labels.markforreexport")}
</Button>
);
};
export default connect(null, mapDispatchToProps)(PaymentReexportButton);

View File

@@ -13,11 +13,25 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import client from "../../utils/GraphQLClient";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
function updatePaymentCache(items) {
client.cache.modify({
id: "ROOT_QUERY",
fields: {
payments(existingJobs = []) {
return existingJobs.filter(
(paymentRef) => paymentRef.__ref.includes(items) === false
);
},
},
});
}
export function PaymentsExportAllButton({
bodyshop,
currentUser,
@@ -25,7 +39,7 @@ export function PaymentsExportAllButton({
disabled,
loadingCallback,
completedCallback,
refetch
refetch,
}) {
const { t } = useTranslation();
const [updatePayments] = useMutation(UPDATE_PAYMENTS);
@@ -84,7 +98,9 @@ export function PaymentsExportAllButton({
proms.push(
(async () => {
const failedTransactions = groupedData[key].filter((r) => !r.success);
const successfulTransactions = groupedData[key].filter(
(r) => r.success
);
if (failedTransactions.length > 0) {
//Uh oh. At least one was no good.
failedTransactions.map((ft) =>
@@ -130,7 +146,15 @@ export function PaymentsExportAllButton({
});
const paymentUpdateResponse = await updatePayments({
variables: {
paymentIdList: [key],
paymentIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo
? "paymentid"
: "id"
]
),
payment: {
exportedat: new Date(),
},
@@ -142,6 +166,11 @@ export function PaymentsExportAllButton({
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
updatePaymentCache(
paymentUpdateResponse.data.update_payments.returning.map(
(payment) => payment.id
)
);
} else {
notification["error"]({
message: t("payments.errors.exporting", {
@@ -150,6 +179,26 @@ export function PaymentsExportAllButton({
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && successfulTransactions.length > 0) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
updatePaymentCache([
...new Set(
successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig &&
bodyshop.accountingconfig.qbo
? "paymentid"
: "id"
]
)
),
]);
}
}
})()
);
@@ -157,7 +206,6 @@ export function PaymentsExportAllButton({
await Promise.all(proms);
if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
setLoading(false);
};

View File

@@ -0,0 +1,156 @@
import { CopyFilled } from "@ant-design/icons";
import { Button, Form, Popover, Space, message } from "antd";
import axios from "axios";
import Dinero from "dinero.js";
import { parsePhoneNumber } from "libphonenumber-js";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
openChatByPhone,
setMessage,
} from "../../redux/messaging/messaging.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
setMessage: (text) => dispatch(setMessage(text)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(PaymentsGenerateLink);
export function PaymentsGenerateLink({
bodyshop,
callback,
job,
openChatByPhone,
setMessage,
}) {
const { t } = useTranslation();
const [form] = Form.useForm();
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const [paymentLink, setPaymentLink] = useState(null);
const handleFinish = async ({ amount }) => {
setLoading(true);
const p = parsePhoneNumber(job.ownr_ph1, "CA");
setLoading(true);
const response = await axios.post("/intellipay/generate_payment_url", {
bodyshop,
amount: amount,
account: job.ro_number,
invoice: job.id,
});
setLoading(false);
setPaymentLink(response.data.shorUrl);
openChatByPhone({
phone_num: p.formatInternational(),
jobid: job.id,
});
setMessage(
t("payments.labels.smspaymentreminder", {
shopname: bodyshop.shopname,
amount: amount,
payment_link: response.data.shorUrl,
})
);
//Add in confirmation & errors.
if (callback) callback();
// setVisible(false);
setLoading(false);
};
const popContent = (
<div>
<Form onFinish={handleFinish} layout="vertical" form={form}>
<Form.Item
label={t("payments.fields.amount")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name="amount"
>
<CurrencyFormItemComponent />
</Form.Item>
{paymentLink && (
<Space direction="vertical">
<Space
style={{ cursor: "pointer" }}
onClick={() => {
navigator.clipboard.writeText(paymentLink);
message.success(t("general.actions.copied"));
}}
>
<div
onClick={() => {
//Copy the link.
}}
>
{paymentLink}
</div>{" "}
<CopyFilled />
</Space>
<Button
onClick={() => {
const p = parsePhoneNumber(job.ownr_ph1, "CA");
openChatByPhone({
phone_num: p.formatInternational(),
jobid: job.id,
});
setMessage(
t("payments.labels.smspaymentreminder", {
shopname: bodyshop.shopname,
amount: Dinero({
amount: Math.round(form.getFieldValue("amount") * 100),
}).toFormat(),
payment_link: paymentLink,
})
);
}}
>
{t("general.actions.sendbysms")}
</Button>
</Space>
)}
</Form>
<Space>
<Button type="primary" onClick={() => form.submit()}>
{t("general.actions.submit")}
</Button>
<Button
onClick={() => {
form.resetFields();
setPaymentLink(null);
setVisible(false);
}}
>
{t("general.actions.cancel")}
</Button>
</Space>
</div>
);
return (
<Popover content={popContent} visible={visible}>
<Button onClick={() => setVisible(true)} loading={loading}>
{t("payments.actions.generatepaymentlink")}
</Button>
</Popover>
);
}

View File

@@ -1,20 +1,23 @@
import { EditFilled, SyncOutlined } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { Button, Card, Input, Space, Table, Typography } from "antd";
import axios from "axios";
import queryString from "query-string";
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_PAYMENT_BY_ID } from "../../graphql/payments.queries";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort } from "../../utils/sorters";
import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -39,7 +42,10 @@ export function PaymentsListPaginated({
bodyshop,
}) {
const search = queryString.parse(useLocation().search);
const [openSearchResults, setOpenSearchResults] = useState([]);
const [searchLoading, setSearchLoading] = useState(false);
const { page, sortcolumn, sortorder } = search;
const client = useApolloClient();
const history = useHistory();
const [state, setState] = useState({
sortedInfo: {},
@@ -52,13 +58,17 @@ export function PaymentsListPaginated({
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) => (
<Link to={"/manage/jobs/" + record.job.id}>
{record.job.ro_number || t("general.labels.na")}
</Link>
),
// sorter: (a, b) => alphaSort(a.job.ro_number, b.job.ro_number),
// sortOrder: sortcolumn === "ro_number" && sortorder,
render: (text, record) => {
return record.job ? (
<Link to={"/manage/jobs/" + record.job.id}>
{record.job.ro_number || t("general.labels.na")}
</Link>
) : (
<span>{t("general.labels.na")}</span>
);
},
},
{
title: t("payments.fields.paymentnum"),
@@ -72,16 +82,16 @@ export function PaymentsListPaginated({
dataIndex: "owner",
key: "owner",
ellipsis: true,
sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
sortOrder: sortcolumn === "owner" && sortorder,
// sorter: (a, b) => alphaSort(a.job.ownr_ln, b.job.ownr_ln),
// sortOrder: sortcolumn === "owner" && sortorder,
render: (text, record) => {
return record.job.owner ? (
<Link to={"/manage/owners/" + record.job.owner.id}>
<OwnerNameDisplay ownerObject={record} />
return record.job?.owner ? (
<Link to={"/manage/owners/" + record.job?.owner?.id}>
<OwnerNameDisplay ownerObject={record.job} />
</Link>
) : (
<span>
<OwnerNameDisplay ownerObject={record} />
<OwnerNameDisplay ownerObject={record.job} />
</span>
);
},
@@ -146,11 +156,34 @@ export function PaymentsListPaginated({
render: (text, record) => (
<Space>
<Button
disabled={record.exportedat}
onClick={() => {
// disabled={record.exportedat}
onClick={async () => {
let apolloResults;
if (search.search) {
const { data } = await client.query({
query: QUERY_PAYMENT_BY_ID,
variables: {
paymentId: record.id,
},
});
apolloResults = data.payments_by_pk;
}
setPaymentContext({
actions: { refetch: refetch },
context: record,
actions: {
refetch: apolloResults
? (updatedRecord) => {
setOpenSearchResults((results) =>
results.map((result) => {
if (result.id !== record.id) {
return result;
}
return updatedRecord;
})
);
}
: refetch,
},
context: apolloResults ? apolloResults : record,
});
}}
>
@@ -177,6 +210,28 @@ export function PaymentsListPaginated({
history.push({ search: queryString.stringify(search) });
};
useEffect(() => {
if (search.search && search.search.trim() !== "") {
searchPayments();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
async function searchPayments(value) {
try {
setSearchLoading(true);
const searchData = await axios.post("/search", {
search: value || search.search,
index: "payments",
});
setOpenSearchResults(searchData.data.hits.hits.map((s) => s._source));
} catch (error) {
console.log("Error while fetching search results", error);
} finally {
setSearchLoading(false);
}
}
return (
<Card
extra={
@@ -189,6 +244,7 @@ export function PaymentsListPaginated({
<Button
onClick={() => {
delete search.search;
delete search.page;
history.push({ search: queryString.stringify(search) });
}}
>
@@ -212,24 +268,33 @@ export function PaymentsListPaginated({
onSearch={(value) => {
search.search = value;
history.push({ search: queryString.stringify(search) });
searchPayments(value);
}}
loading={loading || searchLoading}
enterButton
/>
</Space>
}
>
<Table
loading={loading}
loading={loading || searchLoading}
scroll={{ x: true }}
pagination={{
position: "top",
pageSize: 25,
current: parseInt(page || 1),
total: total,
}}
pagination={
search?.search
? {
pageSize: 25,
showSizeChanger: false,
}
: {
pageSize: 25,
current: parseInt(page || 1),
total: total,
showSizeChanger: false,
}
}
columns={columns}
rowKey="id"
dataSource={payments}
dataSource={search?.search ? openSearchResults : payments}
onChange={handleTableChange}
/>
</Card>

View File

@@ -5,11 +5,13 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setEmailOptions } from "../../redux/email/email.actions";
import { selectPrintCenter } from "../../redux/modals/modals.selectors";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { GenerateDocument } from "../../utils/RenderTemplate";
const mapStateToProps = createStructuredSelector({
printCenterModal: selectPrintCenter,
bodyshop: selectBodyshop,
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
@@ -22,6 +24,7 @@ export function PrintCenterItemComponent({
id,
bodyshop,
disabled,
technician,
}) {
const [loading, setLoading] = useState(false);
const { context } = printCenterModal;
@@ -44,19 +47,24 @@ export function PrintCenterItemComponent({
<Space wrap>
{item.title}
<PrinterOutlined onClick={renderToNewWindow} />
<MailOutlined
onClick={() => {
GenerateDocument(
{
name: item.key,
variables: { id: id },
},
{ to: context.job && context.job.ownr_ea, subject: item.subject },
"e",
id
);
}}
/>
{!technician ? (
<MailOutlined
onClick={() => {
GenerateDocument(
{
name: item.key,
variables: { id: id },
},
{
to: context.job && context.job.ownr_ea,
subject: item.subject,
},
"e",
id
);
}}
/>
) : null}
{loading && <Spin />}
</Space>
</li>

View File

@@ -23,14 +23,34 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
const { id: jobId, job } = printCenterModal.context;
const tempList = TemplateList("job", {});
const { t } = useTranslation();
const JobsReportsList = Object.keys(tempList)
.map((key) => {
return tempList[key];
})
.filter(
(temp) =>
!temp.regions || (temp.regions && temp.regions[bodyshop.region_config])
);
const JobsReportsList =
bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null
? Object.keys(tempList)
.map((key) => {
return tempList[key];
})
.filter(
(temp) =>
(!temp.regions ||
(temp.regions && temp.regions[bodyshop.region_config]) ||
(temp.regions &&
bodyshop.region_config.includes(Object.keys(temp.regions)) ===
true)) &&
(!temp.dms || temp.dms === false)
)
: Object.keys(tempList)
.map((key) => {
return tempList[key];
})
.filter(
(temp) =>
!temp.regions ||
(temp.regions && temp.regions[bodyshop.region_config]) ||
(temp.regions &&
bodyshop.region_config.includes(Object.keys(temp.regions)) ===
true)
);
const filteredJobsReportsList =
search !== ""

View File

@@ -0,0 +1,51 @@
import { Col, List, Space, Typography } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
const CardColorLegend = ({ bodyshop }) => {
const { t } = useTranslation();
const data = bodyshop.ssbuckets.map((bucket) => {
let color = { r: 255, g: 255, b: 255 };
if (bucket.color) {
color = bucket.color;
if (bucket.color.rgb) {
color = bucket.color.rgb;
}
}
return {
label: bucket.label,
color,
};
});
return (
<Col>
<Typography>{t("production.labels.legend")}</Typography>
<List
grid={{
gutter: 16,
}}
dataSource={data}
renderItem={(item) => (
<List.Item>
<Space>
<div
style={{
width: "1.5rem",
aspectRatio: "1/1",
backgroundColor: `rgba(${item.color.r},${item.color.g},${item.color.b},${item.color.a})`,
}}
></div>
<div>{item.label}</div>
</Space>
</List.Item>
)}
/>
</Col>
);
};
export default CardColorLegend;

View File

@@ -18,6 +18,31 @@ import moment from "moment";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
const cardColor = (ssbuckets, totalHrs) => {
const bucket = ssbuckets.filter(
(bucket) =>
bucket.gte <= totalHrs && (!!bucket.lt ? bucket.lt > totalHrs : true)
)[0];
let color = { r: 255, g: 255, b: 255 };
if (bucket && bucket.color) {
color = bucket.color;
if (bucket.color.rgb) {
color = bucket.color.rgb;
}
}
return color;
};
function getContrastYIQ(bgColor) {
const yiq = (bgColor.r * 299 + bgColor.g * 587 + bgColor.b * 114) / 1000;
return yiq >= 128 ? "black" : "white";
}
export default function ProductionBoardCard(
technician,
card,
@@ -54,10 +79,22 @@ export default function ProductionBoardCard(
.isSame(moment(card.scheduled_completion), "day") &&
"production-completion-soon"));
const totalHrs =
card.labhrs.aggregate.sum.mod_lb_hrs + card.larhrs.aggregate.sum.mod_lb_hrs;
const bgColor = cardColor(bodyshop.ssbuckets, totalHrs);
return (
<Card
className="react-kanban-card imex-kanban-card"
size="small"
style={{
backgroundColor:
cardSettings &&
cardSettings.cardcolor &&
`rgba(${bgColor.r},${bgColor.g},${bgColor.b},${bgColor.a})`,
color:
cardSettings && cardSettings.cardcolor && getContrastYIQ(bgColor),
}}
title={
<Space>
<ProductionAlert record={card} key="alert" />

View File

@@ -104,6 +104,13 @@ export default function ProductionBoardKanbanCardSettings({
>
<Switch />
</Form.Item>
<Form.Item
valuePropName="checked"
label={t("production.labels.cardcolor")}
name="cardcolor"
>
<Switch />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
@@ -166,7 +173,7 @@ export default function ProductionBoardKanbanCardSettings({
</div>
);
return (
<Popover content={overlay} visible={visible}>
<Popover content={overlay} visible={visible} placement="topRight">
<Button loading={loading} onClick={() => setVisible(true)}>
{t("production.labels.cardsettings")}
</Button>

View File

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

View File

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

View File

@@ -25,6 +25,7 @@ export default function ProductionListDate({
// }
//e.stopPropagation();
updateAlert({
variables: {
jobId: record.id,
@@ -32,6 +33,11 @@ export default function ProductionListDate({
[field]: date,
},
},
optimisticResponse: {
update_jobs: {
[field]: date,
},
},
}).then(() => {
if (record.refetch) record.refetch();
if (!time) {
@@ -49,9 +55,11 @@ export default function ProductionListDate({
(moment().add(1, "day").isSame(moment(record[field]), "day") &&
"production-completion-soon"));
}
return (
<Dropdown
//trigger={["click"]}
trigger={["click"]}
onVisibleChange={(v) => setVisible(v)}
visible={visible}
style={{
height: "19px",

View File

@@ -1,31 +1,33 @@
import { PrinterFilled } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Descriptions, Drawer, Space, PageHeader, Button } from "antd";
import { Button, Descriptions, Drawer, PageHeader, Space } from "antd";
import queryString from "query-string";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_JOB_CARD_DETAILS } from "../../graphql/jobs.queries";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component";
import StartChatButton from "../chat-open-button/chat-open-button.component";
import JobAtChange from "../job-at-change/job-at-change.component";
import JobDetailCardsDocumentsComponent from "../job-detail-cards/job-detail-cards.documents.component";
import JobDetailCardsNotesComponent from "../job-detail-cards/job-detail-cards.notes.component";
import JobDetailCardsPartsComponent from "../job-detail-cards/job-detail-cards.parts.component";
import JobEmployeeAssignments from "../job-employee-assignments/job-employee-assignments.container";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
import JobAtChange from "../job-at-change/job-at-change.component";
import { PrinterFilled } from "@ant-design/icons";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) =>
@@ -40,6 +42,7 @@ export function ProductionListDetail({
bodyshop,
jobs,
setPrintCenterContext,
technician,
}) {
const search = queryString.parse(useLocation().search);
const history = useHistory();
@@ -66,7 +69,9 @@ export function ProductionListDetail({
title={theJob.ro_number}
extra={
<Space wrap>
<ProductionRemoveButton jobId={theJob.id} />{" "}
{!technician ? (
<ProductionRemoveButton jobId={theJob.id} />
) : null}
<Button
onClick={() => {
setPrintCenterContext({
@@ -82,7 +87,9 @@ export function ProductionListDetail({
<PrinterFilled />
{t("jobs.actions.printCenter")}
</Button>
<ScoreboardAddButton job={data ? data.jobs_by_pk : {}} />
{!technician ? (
<ScoreboardAddButton job={data ? data.jobs_by_pk : {}} />
) : null}
</Space>
}
/>

View File

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

View File

@@ -184,6 +184,18 @@ export function ProductionListTable({
0
)
.toFixed(1);
const totalLAB = data
.reduce(
(acc, val) => acc + (val.labhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
const totalLAR = data
.reduce(
(acc, val) => acc + (val.larhrs?.aggregate?.sum?.mod_lb_hrs || 0),
0
)
.toFixed(1);
return (
<div>
<PageHeader
@@ -193,6 +205,14 @@ export function ProductionListTable({
title={t("dashboard.titles.productionhours")}
value={totalHrs}
/>
<Statistic
title={t("dashboard.titles.labhours")}
value={totalLAB}
/>
<Statistic
title={t("dashboard.titles.larhours")}
value={totalLAR}
/>
<Statistic
title={t("appointments.labels.inproduction")}
value={dataSource && dataSource.length}
@@ -246,11 +266,21 @@ export function ProductionListTable({
(x) => x.status === record.status
);
if (!color) return null;
if (!color) {
if (index % 2 === 0)
return {
style: {
backgroundColor: `rgb(236, 236, 236)`,
},
};
return null;
}
return {
className: "rowWithColor",
style: {
backgroundColor: `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`,
"--bgColor": `rgb(${color.color.r},${color.color.g},${color.color.b},${color.color.a})`,
},
};
},

View File

@@ -26,6 +26,8 @@ const ret = {
"jobs:partsqueue": 4,
"jobs:checklist-view": 2,
"jobs:list-ready": 1,
"jobs:void": 5,
"bills:enter": 2,
"bills:view": 2,
"bills:list": 2,

View File

@@ -12,7 +12,8 @@ import "./schedule-calendar.styles.scss";
import JobDetailCards from "../job-detail-cards/job-detail-cards.component";
import { selectProblemJobs } from "../../redux/application/application.selectors";
import { Alert, Collapse } from "antd";
import { useTranslation } from "react-i18next";
import { useTranslation, Trans } from "react-i18next";
import { Link } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -66,10 +67,21 @@ export function ScheduleCalendarWrapperComponent({
<Alert
key={problem.id}
type="error"
message={t("appointments.labels.dataconsistency", {
ro_number: problem.ro_number,
code: problem.code,
})}
message={
<Trans
i18nKey="appointments.labels.dataconsistency"
components={[
<Link
to={`/manage/jobs/${problem.id}`}
target="_blank"
/>,
]}
values={{
ro_number: problem.ro_number,
code: problem.code,
}}
/>
}
/>
))}
</Collapse.Panel>
@@ -79,10 +91,18 @@ export function ScheduleCalendarWrapperComponent({
<Alert
key={problem.id}
type="error"
message={t("appointments.labels.dataconsistency", {
ro_number: problem.ro_number,
code: problem.code,
})}
message={
<Trans
i18nKey="appointments.labels.dataconsistency"
components={[
<Link to={`/manage/jobs/${problem.id}`} target="_blank" />,
]}
values={{
ro_number: problem.ro_number,
code: problem.code,
}}
/>
}
/>
))
)}

View File

@@ -21,6 +21,7 @@ import ScheduleVerifyIntegrity from "../schedule-verify-integrity/schedule-verif
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import _ from "lodash";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
@@ -39,9 +40,39 @@ export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
employeevacation: true,
ins_co_nm: null,
});
const [estimatorsFilter, setEstimatiorsFilter] = useLocalStorage(
"estimators",
[]
);
const estimators = useMemo(() => {
return _.uniq([
...data
.filter((d) => d.__typename === "appointments")
.map((app) =>
`${app.job?.est_ct_fn || ""} ${app.job?.est_ct_ln || ""}`.trim()
)
.filter((e) => e.length > 0),
...bodyshop.md_estimators.map((e) =>
`${e.est_ct_fn || ""} ${e.est_ct_ln || ""}`.trim()
),
]);
}, [data, bodyshop.md_estimators]);
const filteredData = useMemo(() => {
return data.filter(
(d) =>
return data.filter((d) => {
const estFilter =
d.__typename === "appointments"
? estimatorsFilter.length === 0
? true
: !!estimatorsFilter.find(
(e) =>
e ===
`${d.job?.est_ct_fn || ""} ${d.job?.est_ct_ln || ""}`.trim()
)
: true;
return (
(d.block ||
(filter.intake && d.isintake) ||
(filter.manual && !d.isintake && d.block === false) ||
@@ -50,9 +81,11 @@ export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
!!d.employee)) &&
(filter.ins_co_nm && filter.ins_co_nm.length > 0
? filter.ins_co_nm.includes(d.job?.ins_co_nm)
: true)
);
}, [data, filter]);
: true) &&
estFilter
);
});
}, [data, filter, estimatorsFilter]);
return (
<Row gutter={[16, 16]}>
@@ -63,6 +96,21 @@ export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
extra={
<Space wrap>
<ScheduleAtsSummary appointments={filteredData} />
<Select
style={{ minWidth: "15rem" }}
mode="multiple"
placeholder={t("schedule.labels.estimators")}
allowClear
onClear={() => setEstimatiorsFilter([])}
value={[...estimatorsFilter]}
onChange={(e) => {
setEstimatiorsFilter(e);
}}
options={estimators.map((e) => ({
label: e,
value: e,
}))}
/>
<Select
style={{ minWidth: "15rem" }}
mode="multiple"

View File

@@ -13,20 +13,29 @@ import {
QUERY_APPOINTMENTS_BY_JOBID,
} from "../../graphql/appointments.queries";
import { QUERY_LBR_HRS_BY_PK, UPDATE_JOBS } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { setEmailOptions } from "../../redux/email/email.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectSchedule } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { DateTimeFormat } from "../../utils/DateFormatter";
import { TemplateList } from "../../utils/TemplateConstants";
import ScheduleJobModalComponent from "./schedule-job-modal.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
scheduleModal: selectSchedule,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("schedule")),
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function ScheduleJobModalContainer({
@@ -34,6 +43,8 @@ export function ScheduleJobModalContainer({
bodyshop,
toggleModalVisible,
setEmailOptions,
currentUser,
insertAuditTrail,
}) {
const { visible, context, actions } = scheduleModal;
const { jobId, job, previousEvent } = context;
@@ -122,12 +133,22 @@ export function ScheduleJobModalContainer({
end: moment(values.start).add(bodyshop.appt_length || 60, "minutes"),
color: values.color,
note: values.note,
created_by: currentUser.email,
},
jobId: jobId,
altTransport: values.alt_transport,
},
});
if (!appt.errors) {
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.appointmentinsert(
DateTimeFormat(values.start)
),
});
}
if (!!appt.errors) {
notification["error"]({
message: t("appointments.errors.saving", {
@@ -149,6 +170,7 @@ export function ScheduleJobModalContainer({
scheduled_in: values.start,
scheduled_completion: values.scheduled_completion,
lost_sale_reason: null,
date_lost_sale: null,
},
},
});

View File

@@ -59,11 +59,12 @@ export function ScheduleManualEvent({ bodyshop, event }) {
refetchQueries: ["QUERY_ALL_ACTIVE_APPOINTMENTS"],
});
}
form.resetFields();
setVisibility(false);
} catch (error) {
console.log(error);
} finally {
setLoading(false);
setVisibility(false);
}
};

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