Compare commits

..

249 Commits

Author SHA1 Message Date
Patrick Fic
76d907987a Remove unused destructuring. 2023-10-19 08:52:40 -07:00
Patrick Fic
e05b72615e Improve labor deduction for enhanced payroll. 2023-10-18 17:28:48 -07:00
Patrick Fic
b9a6a98fee Resolve deduct from labor calculations. 2023-10-18 09:51:17 -07:00
Patrick Fic
acc9145d52 Enhanced payroll deduct from labor correction. 2023-10-17 10:45:16 -07:00
Patrick Fic
f9c8f53474 Merge branch 'rome/master' into feature/payroll 2023-10-16 12:10:51 -07:00
Patrick Fic
004e96517f Merged in rome/test (pull request #1008)
Rome/test
2023-10-13 15:00:35 +00:00
Patrick Fic
957265b1c8 Merged in feature/ems-tax-calculation (pull request #1007)
Add warning for jobs missing profile information.
2023-10-13 14:57:38 +00:00
Patrick Fic
8807e282f4 Add warning for jobs missing profile information. 2023-10-11 14:44:07 -07:00
Patrick Fic
c394974dc8 Merged in feature/america (pull request #1001)
Add error handling when unable to caclulate job totals.
2023-10-11 18:14:13 +00:00
Patrick Fic
28386a4234 Merged in feature/ems-tax-calculation (pull request #1000)
Add error handling when unable to caclulate job totals.
2023-10-11 18:13:31 +00:00
Patrick Fic
6f16c47d4f Add error handling when unable to caclulate job totals. 2023-10-11 11:13:08 -07:00
Patrick Fic
25062e37f4 Merged in feature/america (pull request #999)
Add taxable overrides to shop configuration.
2023-10-11 17:59:49 +00:00
Patrick Fic
2b1f8e4335 Merged in feature/ems-tax-calculation (pull request #998)
Add taxable overrides to shop configuration.
2023-10-11 17:53:16 +00:00
Patrick Fic
02690b6796 Add taxable overrides to shop configuration. 2023-10-11 10:49:30 -07:00
Patrick Fic
3897281015 Merged in feature/ems-tax-calculation (pull request #997)
Resolved calculations for profile markups and discounts.
2023-10-06 14:58:29 +00:00
Patrick Fic
8d0e5b93ed Merged in feature/ems-tax-calculation (pull request #996)
Resolved calculations for profile markups and discounts.
2023-10-06 14:58:09 +00:00
Patrick Fic
ef146032df Resolved calculations for profile markups and discounts. 2023-10-06 07:57:26 -07:00
Patrick Fic
1dc025fb36 Merged in feature/america (pull request #995)
Remove GST related sections.
2023-10-03 17:47:05 +00:00
Patrick Fic
30c0c84b93 Merged in feature/ems-tax-calculation (pull request #994)
Remove GST related sections.
2023-10-03 17:46:40 +00:00
Patrick Fic
4bd139f93b Remove GST related sections. 2023-09-29 11:08:29 -07:00
Patrick Fic
f59787add0 Merged in feature/america (pull request #993)
Feature/america
2023-09-28 21:19:54 +00:00
Patrick Fic
20c304a2db Merged in feature/ems-tax-calculation (pull request #992)
Feature/ems tax calculation
2023-09-28 21:19:32 +00:00
Patrick Fic
53526d9c80 Add UI elements to Job to new cieca profile fields. 2023-09-28 13:32:01 -07:00
Patrick Fic
68b4bc66ff QB/O Exporting Adjustments 2023-09-28 09:31:32 -07:00
Patrick Fic
805149daea Final adjustments to job totals calculation for 99% accuracy. 2023-09-27 15:16:01 -07:00
Patrick Fic
b2529207e1 Merge branch 'release/2023-09-29' into feature/ems-tax-calculation 2023-09-26 12:59:42 -07:00
Patrick Fic
d2fe9b0590 Updates to QuickBooks and DMS export. 2023-09-26 12:35:49 -07:00
Patrick Fic
eff4f82ad7 Add tax rates to shop config, resolve rate population on job detail. 2023-09-19 15:23:47 -07:00
Patrick Fic
57327332c9 Merged in feature/america (pull request #982)
Feature/america
2023-09-15 19:58:23 +00:00
Patrick Fic
a2144ccb61 Merged in master (pull request #981)
Master
2023-09-15 19:57:46 +00:00
Patrick Fic
aa478fc510 Merge branch 'feature/america' of bitbucket.org:snaptsoft/bodyshop into feature/america 2023-09-15 12:56:45 -07:00
Patrick Fic
659a0bc0fd Updated test.env and circle CI for rome test. 2023-09-15 12:56:31 -07:00
Patrick Fic
f1ef28e544 Updated test.env and circle CI for rome test. 2023-09-15 12:53:53 -07:00
Patrick Fic
fef680fb99 Add labor to tax thresholds. 2023-09-14 15:18:59 -07:00
Patrick Fic
91a45a621a Merged in feature/america (pull request #974)
Feature/america
2023-09-14 18:20:26 +00:00
Patrick Fic
fe9512ca9c Merge branch 'feature/IO-2399-import-status' into feature/america 2023-09-14 11:19:13 -07:00
Patrick Fic
ff318599f5 Add regression check. 2023-09-13 14:55:20 -07:00
Patrick Fic
d1ba90408d Update hazard waste calculation. 2023-09-13 14:39:58 -07:00
Patrick Fic
536f8d9cb9 Add UI elements for part tax and improve calculations. 2023-09-12 14:16:01 -07:00
Patrick Fic
6a9e871b08 Add parts tax calculations. 2023-09-11 20:45:19 -07:00
Patrick Fic
d163d145d0 Merge remote-tracking branch 'origin/release/2023-09-15' into feature/ems-tax-calculation 2023-09-11 11:46:10 -07:00
Patrick Fic
fdf0506976 Merge remote-tracking branch 'origin/master' into feature/america 2023-09-11 11:29:55 -07:00
Patrick Fic
c9d8fc3072 Merged in feature/america (pull request #960)
Improve totals calculation.
2023-08-31 21:31:25 +00:00
Patrick Fic
0bcf67a5f5 Improve totals calculation. 2023-08-31 14:30:49 -07:00
Patrick Fic
3e48753329 Merged in feature/america (pull request #959)
Update US_NE parts calculation using PFL.
2023-08-31 15:31:35 +00:00
Patrick Fic
96441dbb33 Update US_NE parts calculation using PFL. 2023-08-31 08:30:30 -07:00
Patrick Fic
6a0b63b185 Merged in feature/america (pull request #958)
Add US_NE tax calc.
2023-08-31 15:25:21 +00:00
Patrick Fic
9dea43fd34 Add US_NE tax calc. 2023-08-31 08:24:52 -07:00
Patrick Fic
465980dd8c Merged in feature/america (pull request #957)
Feature/america
2023-08-30 16:40:13 +00:00
Patrick Fic
03d04fd8d1 Resolve enter again issue seen only on RO. 2023-08-30 08:49:50 -07:00
Patrick Fic
7d18c9b160 Merge branch 'master' into feature/america 2023-08-30 08:04:57 -07:00
Patrick Fic
c097f98959 Resolve issue when selecting lines. 2023-08-29 14:13:07 -07:00
Patrick Fic
213d4ad928 Add CIECA PFL info. 2023-08-28 12:06:13 -07:00
Patrick Fic
710b3b00f5 Remove parts scanning / critical item highlighting. 2023-08-28 11:18:10 -07:00
Patrick Fic
d041d03fbf Merged in feature/america (pull request #955)
Remove rescue link.
2023-08-28 16:47:23 +00:00
Patrick Fic
24b90e9888 Remove rescue link. 2023-08-28 09:46:02 -07:00
Patrick Fic
4b83e798ac Merged in feature/america (pull request #950)
Remove errant import
2023-08-24 21:50:05 +00:00
Patrick Fic
468f93f3df Remove errant import 2023-08-24 14:47:30 -07:00
Patrick Fic
78d9dd5acb Merged in feature/america (pull request #949)
Resolve UNK mileage from CCC.
2023-08-24 21:47:11 +00:00
Patrick Fic
6ef7d4653d Resolve UNK mileage from CCC. 2023-08-24 14:45:23 -07:00
Patrick Fic
645eb637f2 Merged in feature/america (pull request #948)
Remove Crisp from Rome.
2023-08-24 19:56:10 +00:00
Patrick Fic
268fdce5ac Remove Crisp from Rome. 2023-08-24 12:55:51 -07:00
Patrick Fic
f9f3a05a43 Merged in feature/america (pull request #947)
Feature/america
2023-08-24 18:19:26 +00:00
Patrick Fic
8a92919b2e Merge branch 'feature/payroll' into feature/america 2023-08-24 11:16:41 -07:00
Patrick Fic
dbcd675300 Clear selected lines on assignment. 2023-08-24 11:16:31 -07:00
Patrick Fic
4628af0e43 Merge branch 'feature/payroll' into feature/america 2023-08-24 11:05:22 -07:00
Patrick Fic
b1fedf5904 Resolve treatment misfire. 2023-08-24 11:05:10 -07:00
Patrick Fic
45ec03e615 Add crisp tagging for US users. 2023-08-23 11:20:45 -07:00
Patrick Fic
570d36b695 Remove QBO sandbox. 2023-08-21 16:01:45 -07:00
Patrick Fic
b36697054e Merged in feature/america (pull request #941)
Feature/america
2023-08-18 14:47:57 +00:00
Patrick Fic
17149fe853 Merge branch 'feature/payroll' into feature/america 2023-08-18 07:47:08 -07:00
Allan Carr
15c837f745 IO-2206 Filter Enhanced and Non-Enhanced Payroll Reports 2023-08-16 17:19:37 -07:00
Allan Carr
3f43ff05d0 IO-2206 Enhanced Payroll Reports 2023-08-16 12:12:22 -07:00
Patrick Fic
d4dee21383 Merged in feature/america (pull request #933)
Feature/america
2023-08-14 22:39:18 +00:00
Patrick Fic
79da904767 Merge remote-tracking branch 'origin/master' into feature/america 2023-08-14 15:38:49 -07:00
Patrick Fic
0821797044 Merge branch 'feature/payroll' into feature/america 2023-08-14 15:36:52 -07:00
Patrick Fic
ad7ff62b56 Shift employee cost centers to follow DMS standard. 2023-08-14 15:28:10 -07:00
Patrick Fic
f148d7d0d0 Merged in feature/america (pull request #930)
Resolve dispatch line error.
2023-08-11 17:31:03 +00:00
Patrick Fic
e19e3865e7 Resolve dispatch line error. 2023-08-11 10:30:17 -07:00
Patrick Fic
35f062d4e0 Merged in feature/america (pull request #923)
Remove Zoho.
2023-08-08 20:19:03 +00:00
Patrick Fic
01328ba33c Remove Zoho. 2023-08-08 13:18:39 -07:00
Patrick Fic
7caa138184 Merged in feature/america (pull request #922)
Resolve CI issue.
2023-08-08 19:15:02 +00:00
Patrick Fic
1b8c3f3081 Resolve CI issue. 2023-08-08 12:07:25 -07:00
Patrick Fic
3e4f36fb6f Merged in feature/america (pull request #921)
Feature/america
2023-08-04 20:58:05 +00:00
Patrick Fic
aedcb973f5 Merge branch 'feature/IO-2278-parts-dispatching' into feature/america 2023-08-04 13:57:25 -07:00
Patrick Fic
26dc720929 Add technician side to part dispatching. 2023-08-04 13:24:41 -07:00
Patrick Fic
b5cc1c06df Merge branch 'release/2023-08-11' into feature/IO-2278-parts-dispatching 2023-08-04 12:36:30 -07:00
Patrick Fic
786c790307 WIP Tech Claiming Ability 2023-08-04 12:28:08 -07:00
Patrick Fic
3e8660bb61 Validation and confirmation messages. 2023-08-03 14:14:01 -07:00
Patrick Fic
997aed4ab3 Update RO logos. 2023-08-03 08:42:15 -07:00
Patrick Fic
f64ea058b9 Merged in feature/america (pull request #915)
Feature/america
2023-08-01 22:30:47 +00:00
Patrick Fic
96485ba252 Merge branch 'master' into feature/america 2023-08-01 15:30:26 -07:00
Patrick Fic
a9bdcbcdd4 Merged in feature/payroll (pull request #914)
Add audit trail and resolve status update.
2023-08-01 22:29:49 +00:00
Patrick Fic
1d7f1cccba Add audit trail and resolve status update. 2023-08-01 15:29:19 -07:00
Patrick Fic
b002477c0d Merged in feature/america (pull request #910)
Feature/america
2023-07-28 16:42:01 +00:00
Patrick Fic
27f1447469 Merge branch 'feature/payroll' into feature/america 2023-07-28 09:39:05 -07:00
Patrick Fic
ff153cdd81 Add task name to queries. 2023-07-28 09:34:27 -07:00
Patrick Fic
fe16329443 Add claimed task name to tickets. 2023-07-28 08:22:38 -07:00
Patrick Fic
744e1cf2be Update QBO callback. 2023-07-27 11:41:08 -07:00
Patrick Fic
ab2cf8c8c7 Merge branch 'release/2023-07-28' into feature/payroll 2023-07-27 11:24:31 -07:00
Patrick Fic
fbd001b797 Merged in feature/america (pull request #909)
Feature/america
2023-07-27 15:12:41 +00:00
Patrick Fic
4610be6f15 Merged in master (pull request #908)
Master
2023-07-27 15:11:56 +00:00
Patrick Fic
d5e34b649f Redirect quickbooks to sandbox. 2023-07-27 08:10:22 -07:00
Patrick Fic
7b49a94edd Updated UI and added tech elements. 2023-07-21 13:40:39 -07:00
Patrick Fic
8dfcda6c5e WIP Payroll. 2023-07-21 10:04:44 -07:00
Patrick Fic
756d97a9cb Prevent previously claimed tasks. 2023-07-19 16:42:11 -07:00
Patrick Fic
c0887dbeb9 Merged in feature/america (pull request #897)
Remove Zoho code to fix CSS styling.
2023-07-19 22:45:50 +00:00
Patrick Fic
b8f0ff217f Remove Zoho code to fix CSS styling. 2023-07-19 15:45:07 -07:00
Patrick Fic
cc805781e3 Merged in feature/america (pull request #896)
Feature/america
2023-07-18 20:00:42 +00:00
Patrick Fic
9531eca7a7 Merge branch 'master' into feature/america 2023-07-18 12:59:26 -07:00
Patrick Fic
d7a1d5bbd2 Refactor task claiming and implement basic claim functionality. 2023-07-18 12:59:06 -07:00
Patrick Fic
c214168dcd Establish working version of pay all and labor calculations. 2023-07-17 14:58:28 -07:00
Patrick Fic
dd085be5c7 Updated pay all calculations and introduction of split. 2023-07-13 09:13:45 -07:00
Patrick Fic
bd7fbfff37 WIP Payroll. Added line assignment & begin server side calc. 2023-07-07 15:01:46 -07:00
Allan Carr
52a25fc720 Merged in master (pull request #884)
Master
2023-07-07 19:02:42 +00:00
Patrick Fic
384153d914 Merge branch 'feature/IO-2278-parts-dispatching' into feature/payroll 2023-07-06 15:07:44 -07:00
Patrick Fic
511ac5068b Merge branch 'release/2023-07-07' into feature/payroll 2023-07-06 13:51:31 -07:00
Patrick Fic
67e490ab53 Merge branch 'release/2023-07-07' into feature/payroll 2023-07-06 13:23:25 -07:00
Patrick Fic
b84cde1633 Merge branch 'master' into feature/payroll 2023-07-06 13:04:50 -07:00
Patrick Fic
3350f7bd56 Merge branch 'master' into feature/america 2023-07-06 13:02:29 -07:00
Allan Carr
7510419836 Merged in feature/IO-2302-California-Consumer-Law-Update-1 (pull request #860)
IO-2302 California Consumer Law Update 1

Approved-by: Patrick Fic
2023-06-12 23:37:27 +00:00
Allan Carr
7da3ef4e0c IO-2302 California Consumer Law Update 1
Change label for FIPPA switch
2023-06-12 15:00:17 -07:00
Patrick Fic
4c32171fbb Merged in feature/america (pull request #859)
Feature/america
2023-06-12 19:42:56 +00:00
Patrick Fic
470f178cde Remove "provincial" text references. 2023-06-12 12:42:11 -07:00
Patrick Fic
f9633505a2 Merge branch 'master' into feature/america 2023-06-12 10:05:03 -07:00
Patrick Fic
de102d9898 IO-2278 Parts Dispatching tables & buttons. 2023-06-07 08:25:49 -07:00
Patrick Fic
9d503b12e6 Merged in feature/america (pull request #847)
Feature/america
2023-06-06 23:04:34 +00:00
Patrick Fic
4dc3bc1532 Merge branch 'release/2023-06-09' into feature/IO-2278-parts-dispatching 2023-06-06 13:33:25 -07:00
Patrick Fic
50da8cfbc6 Merge branch 'release/2023-06-09' into feature/IO-2278-parts-dispatching 2023-06-06 13:20:10 -07:00
Patrick Fic
bccfc127f3 Resolve null line desc issue. 2023-06-06 12:26:26 -07:00
Patrick Fic
2332d8756d Resolve password reset issue. 2023-06-06 08:11:28 -07:00
Patrick Fic
a4abaed30a Merged in feature/america (pull request #841)
IO-2294 add parts summary list table.
2023-06-02 21:09:25 +00:00
Patrick Fic
d47e18df1f Merge branch 'feature/IO-2294-parts-summary-list' into feature/america 2023-06-02 14:08:24 -07:00
Patrick Fic
c2d094da35 IO-2294 add parts summary list table. 2023-06-02 14:08:01 -07:00
Patrick Fic
6e54b10239 Merged in feature/america (pull request #839)
Feature/america
2023-06-02 19:21:58 +00:00
Patrick Fic
e6c44ec52a Resolve CI issues. 2023-06-02 09:43:46 -07:00
Patrick Fic
39b8ecf785 IO-2313 Remove part type for secondary CCC lines. 2023-06-02 09:38:54 -07:00
Patrick Fic
d87f871334 Merged in feature/america (pull request #838)
Feature/america
2023-06-02 16:36:40 +00:00
Patrick Fic
07cea56e2b Merged in release/2023-06-02 (pull request #837)
Release/2023 06 02
2023-06-02 16:35:52 +00:00
Patrick Fic
dbf3226f97 Merge branch 'feature/payroll' into feature/america 2023-06-02 09:32:00 -07:00
Patrick Fic
4b926edf24 Merged in feature/america (pull request #831)
Feature/america
2023-06-02 02:04:14 +00:00
Patrick Fic
ce0fd42995 Merge branch 'feature/IO-2310-active-jobs-ro-sort' into feature/america 2023-06-01 18:30:00 -07:00
Patrick Fic
bdf91443e0 IO-2276 Change file handler to adjuster. 2023-06-01 18:13:36 -07:00
Patrick Fic
68f4237e15 IO-2278 Begin parts dispatching including basic items and schema. 2023-06-01 13:27:08 -07:00
Patrick Fic
81fc747c03 Update noticeable link to Rome Online. 2023-05-30 11:31:35 -07:00
Patrick Fic
3a25ed13b9 Merged in feature/america (pull request #825)
Feature/america
2023-05-30 18:19:30 +00:00
Patrick Fic
21c5eb6786 Increase Zoho waiting time. 2023-05-30 11:18:29 -07:00
Patrick Fic
e85203e429 Merge branch 'master' into feature/america 2023-05-29 14:49:13 -07:00
Patrick Fic
6b2d10d589 Zoho changes. 2023-05-26 11:15:50 -07:00
Patrick Fic
3d03de193e Add commit and uncommit functionality. 2023-05-25 15:23:14 -07:00
Patrick Fic
a56de72a6b Time Ticket Task Improvmenets. 2023-05-25 11:44:01 -07:00
Patrick Fic
0849bbbba6 Update translation laberl. 2023-05-25 10:14:17 -07:00
Patrick Fic
0cd41d2036 Merged in feature/america (pull request #792)
Feature/america
2023-05-18 22:00:12 +00:00
Patrick Fic
a93bcd6ab6 Resolve calculation issue for private jobs. 2023-05-17 15:19:11 -07:00
Patrick Fic
77b72e160b Update noticeable widget to Rome Online. 2023-05-17 14:47:06 -07:00
Patrick Fic
b32c37f4af Merged in feature/america (pull request #791)
feature/america

Approved-by: Patrick Fic
2023-05-17 20:49:37 +00:00
Patrick Fic
d0beea5cb3 Merge branch 'release/2023-05-19' into feature/america 2023-05-17 13:49:05 -07:00
Patrick Fic
30df6887c5 Resolve missing intellipay translations. 2023-05-17 12:27:27 -07:00
Patrick Fic
bc7823dda1 Merged in feature/america (pull request #790)
Feature/america
2023-05-17 14:52:41 +00:00
Patrick Fic
cbb195a313 Merge branch 'feature/payroll' into feature/america 2023-05-17 07:51:57 -07:00
Patrick Fic
097dc06c65 Merge branch 'release/2023-05-19' into feature/payroll 2023-05-17 07:51:23 -07:00
Patrick Fic
b06fbd25a0 Add shop config section. 2023-05-17 07:49:25 -07:00
Patrick Fic
89bbf274e5 Merged in release/2023-05-12 (pull request #787)
Release/2023 05 12
2023-05-12 18:18:28 +00:00
Patrick Fic
d854177e5a Merged in feature/america (pull request #774)
Resolve null coalescing when entering bill and add Zoho.
2023-05-10 17:55:26 +00:00
Patrick Fic
aa9f82f2d4 Resolve null coalescing when entering bill and add Zoho. 2023-05-10 10:54:35 -07:00
Patrick Fic
2b45264628 Merged in feature/america (pull request #772)
Feature/america
2023-05-08 15:54:39 +00:00
Patrick Fic
4da299f431 Merged in master (pull request #771)
Master
2023-05-08 15:53:29 +00:00
Patrick Fic
9f9aa447a6 Merged in feature/america (pull request #770)
Update to part price change for CCC.
2023-05-08 15:52:11 +00:00
Patrick Fic
6a9f1597cb Update to part price change for CCC. 2023-05-08 08:51:29 -07:00
Patrick Fic
cddc48b851 Merged in feature/america (pull request #769)
Remove filter on PPC button.
2023-05-08 15:17:35 +00:00
Patrick Fic
84ab6d300c Remove filter on PPC button. 2023-05-08 08:16:50 -07:00
Patrick Fic
3c5026c2fb Merged in feature/america (pull request #755)
Feature/america
2023-05-04 20:54:29 +00:00
Patrick Fic
c3c8959d2e Merge branch 'release/2023-05-05' into feature/america 2023-05-04 13:52:32 -07:00
Patrick Fic
2c459d2636 Merged in feature/america (pull request #747)
Default log rocket on in Rome Production.
2023-05-03 21:52:10 +00:00
Patrick Fic
a33c481dd5 Default log rocket on in Rome Production. 2023-05-03 14:51:51 -07:00
Patrick Fic
d6b2f93e54 Merged in feature/america (pull request #740)
Fresh Chat ID.
2023-05-01 22:40:33 +00:00
Patrick Fic
802e945829 Fresh Chat ID. 2023-05-01 15:39:51 -07:00
Patrick Fic
a4a885f33a Merged in feature/america (pull request #739)
Replace Crisp with FreshChat.
2023-05-01 22:30:35 +00:00
Patrick Fic
3380cebb28 Replace Crisp with FreshChat. 2023-05-01 15:29:37 -07:00
Patrick Fic
21ed415f4f Merged in feature/america (pull request #734)
Feature/america
2023-04-27 22:58:39 +00:00
Patrick Fic
51dd89d36a Merge branch 'feature/payroll' into feature/america 2023-04-27 15:52:37 -07:00
Patrick Fic
bad96f231c Code clean up prior to merge. 2023-04-27 15:51:39 -07:00
Patrick Fic
184d72c444 Merge branch 'release/2023-04-28' into feature/america 2023-04-27 15:21:42 -07:00
Patrick Fic
6ca773050f WIP Payroll Commit process. 2023-04-25 10:24:26 -07:00
Patrick Fic
648e8aaae0 QBO USA Changes for tax and non tax items. 2023-04-24 13:13:32 -07:00
Patrick Fic
ad9868b575 IO-2206 WIP Time Ticket Approval Queue. 2023-04-21 11:23:48 -07:00
Patrick Fic
d4d10998f8 Merge branch 'release/2023-04-21' into feature/payroll 2023-04-20 15:05:48 -07:00
Patrick Fic
ba22c31deb Merged in feature/america (pull request #709)
Feature/america
2023-04-18 20:28:08 +00:00
Patrick Fic
f6be133a78 Merge branch 'feature/payroll' into feature/america 2023-04-18 13:27:02 -07:00
Patrick Fic
3768164f1f IO-2206 Refine claim tasks modal to add validations and insertions. 2023-04-18 13:25:42 -07:00
Patrick Fic
8465e7539f Merge remote-tracking branch 'origin/master' into feature/payroll 2023-04-17 13:12:47 -07:00
Patrick Fic
542eca5867 Claim Tasks WIP. 2023-04-12 12:44:19 -07:00
Patrick Fic
27a67c5f4a Added team pay calculator. 2023-04-06 10:36:29 -07:00
Patrick Fic
e00c40f2d5 Merge branch 'release/2023-04-07' into feature/payroll 2023-04-06 08:10:57 -07:00
Patrick Fic
b664e231dc Merge branch 'release/2023-04-07' into feature/america 2023-04-05 17:23:15 -07:00
Patrick Fic
92d6977f9e Merge branch 'rome/master' of https://bitbucket.org/snaptsoft/bodyshop into rome/master 2023-04-05 15:30:10 -07:00
Patrick Fic
4059700468 Merge branch 'release/2023-04-07' into feature/america 2023-04-05 15:29:36 -07:00
Patrick Fic
196bdd83ba IO-2206 Payroll UI updates for discussions with Rome Clients. 2023-04-05 09:13:37 -07:00
Patrick Fic
fbd52bc14e QB USA Changes 2023-04-04 14:03:08 -07:00
Patrick Fic
8013c988d0 Merged in feature/america (pull request #699)
added the logic for aws secrets manager
2023-03-28 17:17:50 +00:00
Patrick Fic
460d6fcf12 Merge branch 'feature/intellipay' into feature/america 2023-03-28 10:16:51 -07:00
Patrick Fic
92353c4853 Merged in feature/america (pull request #698)
Feature/america
2023-03-28 16:55:36 +00:00
Patrick Fic
8ca2a89f0f Improved totals calculations 2023-03-28 09:54:47 -07:00
Patrick Fic
f54c2367f3 Merge branch 'release/2023-03-24' into feature/america 2023-03-24 08:39:36 -07:00
Patrick Fic
27452085e9 Merge remote-tracking branch 'origin/release/2023-03-24' into feature/america 2023-03-21 09:13:08 -07:00
Patrick Fic
2bff7d9567 Merge branch 'release/2023-03-21' into release/2023-03-24 2023-03-21 09:10:15 -07:00
Patrick Fic
c3749f62fe Updated supplementing mechanism. 2023-03-20 14:56:02 -07:00
Patrick Fic
b82c04a16a Merge branch 'feature/intellipay' into feature/america 2023-03-20 14:11:39 -07:00
Patrick Fic
0ec099cdf5 IO-2217 Part Price Changes for CCC. 2023-03-20 14:10:06 -07:00
Patrick Fic
d3f49094d8 Add ppc field to job lines. 2023-03-20 13:20:43 -07:00
Patrick Fic
4e2ad3bc62 IO-2222 Service Side Generation of part price changes. 2023-03-16 13:33:20 -07:00
Patrick Fic
05c287fcf7 Merged in feature/america (pull request #683)
Additional whitelabel changes.

Approved-by: Patrick Fic
2023-02-27 23:23:52 +00:00
Patrick Fic
c11cef4119 Additional whitelabel changes. 2023-02-27 15:23:14 -08:00
Patrick Fic
d6126cc5ce Merged in feature/america (pull request #681)
feature/america

Approved-by: Patrick Fic
2023-02-25 00:04:44 +00:00
Patrick Fic
d88c925a68 Environment setup and email white labeling. 2023-02-24 16:04:04 -08:00
Patrick Fic
372a572400 Merge branch 'master' into feature/america 2023-02-22 08:06:29 -08:00
Patrick Fic
623ee8fbb1 WIP TotalsUpdates. 2023-02-22 08:02:10 -08:00
Patrick Fic
729e0fc5ca Resolve CI issues. 2023-02-14 16:21:17 -08:00
Patrick Fic
d052cde6ca Add circle ci for Rome. 2023-02-14 16:08:31 -08:00
Patrick Fic
b52bbab903 Updates to landing pages. 2023-02-14 16:03:41 -08:00
Patrick Fic
9d42135ebf Merged in master (pull request #677)
Integrate master into USA branch.
2023-02-14 23:12:48 +00:00
Patrick Fic
9ce5aa2189 Rome Firebase Changes. 2023-02-13 10:09:43 -08:00
Patrick Fic
e277292194 Remove unnecesary firebase deployment items. 2023-02-10 14:29:22 -08:00
Patrick Fic
649961c831 Update API email. 2023-02-10 09:56:35 -08:00
Patrick Fic
e00cde2fbd Merge branch 'release/2023-01-27' into feature/america 2023-01-27 15:33:59 -08:00
Patrick Fic
25b89d191b Futher calculations refinments and QBD testing. 2023-01-26 17:01:12 -08:00
Patrick Fic
0a60f77bfc WIP America 2023-01-26 10:58:35 -08:00
Patrick Fic
66a80e439f Revert all package upgrades to unblock testing. 2023-01-25 11:20:51 -08:00
Patrick Fic
d8cc25a54f Rome whitelabelling. 2023-01-24 14:11:53 -08:00
Patrick Fic
39b9cfb348 Merge branch 'feature/major-package-upgrades' into feature/america 2023-01-24 13:22:10 -08:00
Patrick Fic
c6087574be Resolve master merge conflicts. 2023-01-24 10:56:28 -08:00
Patrick Fic
41c63cbfe9 Improve backwards compatibility for job totals calculations. 2023-01-20 07:59:47 -08:00
Patrick Fic
f1afc81e7d Resolve UI issue. 2023-01-20 07:59:18 -08:00
Patrick Fic
018f8a19bb Merge remote-tracking branch 'origin/feature/major-package-upgrades' into feature/america 2023-01-19 13:23:57 -08:00
Patrick Fic
cccd007ba6 Additional changes for CCC calculations. 2023-01-19 08:58:23 -08:00
Patrick Fic
6d5b8baadf Automatic code mods. 2023-01-17 20:22:03 -08:00
Patrick Fic
07e36415e4 Automatic code mods. 2023-01-17 20:21:49 -08:00
Patrick Fic
8dc480826b Antd package update. 2023-01-17 20:10:59 -08:00
Patrick Fic
533a5b87ec Changing of deprecated antd props. 2023-01-17 20:00:29 -08:00
Patrick Fic
7bd83557c1 Remainder of menu refactoring updates. 2023-01-17 19:48:37 -08:00
Patrick Fic
ef290e79b1 Antd 4.24.x compatibility updates => Menu fixes. 2023-01-17 19:14:31 -08:00
Patrick Fic
713b3f4f6d Documents Gallery Package Fixes 2023-01-17 13:59:44 -08:00
Patrick Fic
8fd9614f69 Package cleanup and upgrades. 2023-01-17 11:41:45 -08:00
Patrick Fic
6682a98770 Merge branch 'master' into feature/major-package-upgrades 2023-01-17 09:43:54 -08:00
Patrick Fic
392b405978 Add massaging of data to CCC import. 2023-01-16 15:54:23 -08:00
Patrick Fic
be61850d18 Merge in latest release. 2023-01-04 11:33:53 -08:00
Patrick Fic
f4208a2212 Client side package updates. 2022-12-22 15:06:10 -08:00
Patrick Fic
0b7278a2a8 Server side upgrades 2022-12-22 12:31:16 -08:00
Patrick Fic
bee078fe18 Add user definted rates to labor misc for autohouse. 2022-12-20 12:38:41 -08:00
231 changed files with 23987 additions and 38593 deletions

View File

@@ -8,13 +8,13 @@ orbs:
jobs:
api-deploy:
docker:
- image: cimg/node:18.18.2
- image: "cimg/base:stable"
steps:
- checkout
- eb/setup
- run:
command: |
eb init imex-online-production-api -r ca-central-1 -p "Node.js 18 running on 64bit Amazon Linux 2"
eb init imex-online-production-api -r ca-central-1 -p "Node.js 16 running on 64bit Amazon Linux 2"
eb status --verbose
eb deploy
eb status
@@ -68,6 +68,120 @@ jobs:
from: build
to: "s3://imex-online-production/"
- jira/notify
rome-api-deploy:
docker:
- image: "cimg/base:stable"
steps:
- checkout
- eb/setup
- run:
command: |
eb init romeonline-productionapi -r us-east-2 -p "Node.js 16 running on 64bit Amazon Linux 2"
eb status --verbose
eb deploy
eb status
- jira/notify
rome-hasura-migrate:
docker:
- image: cimg/node:16.15.0
parameters:
secret:
type: string
default: $HASURA_PROD_SECRET
working_directory: ~/repo/hasura
steps:
- checkout:
path: ~/repo
- run:
name: Execute migration
command: |
npm install hasura-cli -g
hasura migrate apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
rome-app-build:
docker:
- image: cimg/node:16.15.0
working_directory: ~/repo/client
steps:
- checkout:
path: ~/repo
- restore_cache:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
- save_cache:
name: Save Yarn Package Cache
key: yarn-packages-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
- run: yarn run build
- aws-s3/sync:
from: build
to: "s3://rome-online-production/"
- jira/notify
test-rome-hasura-migrate:
docker:
- image: cimg/node:16.15.0
parameters:
secret:
type: string
default: $HASURA_ROME_TEST_SECRET
working_directory: ~/repo/hasura
steps:
- checkout:
path: ~/repo
- run:
name: Execute migration
command: |
npm install hasura-cli -g
echo ${HASURA_TEST_SECRET}
hasura migrate apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
test-rome-app-build:
docker:
- image: cimg/node:16.15.0
working_directory: ~/repo/client
steps:
- checkout:
path: ~/repo
- restore_cache:
name: Restore Yarn Package Cache
keys:
- yarn-packages-{{ checksum "yarn.lock" }}
- run:
name: Install Dependencies
command: yarn install --frozen-lockfile --cache-folder ~/.cache/yarn
- save_cache:
name: Save Yarn Package Cache
key: yarn-packages-{{ checksum "yarn.lock" }}
paths:
- ~/.cache/yarn
- run: yarn run build:test
- aws-s3/sync:
from: build
to: "s3://rome-online-test/"
- jira/notify
test-hasura-migrate:
docker:
@@ -165,6 +279,19 @@ workflows:
filters:
branches:
only: master
- rome-api-deploy:
filters:
branches:
only: rome/master
- rome-app-build:
filters:
branches:
only: rome/master
- rome-hasura-migrate:
secret: ${HASURA_PROD_SECRET}
filters:
branches:
only: rome/master
- test-app-build:
filters:
branches:
@@ -174,6 +301,15 @@ workflows:
filters:
branches:
only: test
- test-rome-app-build:
filters:
branches:
only: rome/test
- test-rome-hasura-migrate:
secret: ${HASURA_ROME_TEST_SECRET}
filters:
branches:
only: rome/test
#- admin-app-build:
#filters:
#branches:

4
.gitignore vendored
View File

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

1
.npmrc
View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,14 @@
REACT_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
REACT_APP_GA_CODE=231099835
REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
REACT_APP_CLOUDINARY_API_KEY=957865933348715
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BG3tzU7L2BXlGZ_3VLK4PNaRceoEXEnmHfxcVbRMF5o5g05ejslhVPki9kBM9cBBT-08Ad9kN3HSpS6JmrWD6h4'
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
REACT_APP_AXIOS_BASE_API_URL=http://localhost:4000
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc
REACT_APP_COUNTRY=USA

View File

@@ -1,14 +1,13 @@
GENERATE_SOURCEMAP=false
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT=https://db.romeonline.io/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.romeonline.io/v1/graphql
REACT_APP_GA_CODE=231103507
REACT_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU","authDomain":"imex-prod.firebaseapp.com","databaseURL":"https://imex-prod.firebaseio.com","projectId":"imex-prod","storageBucket":"imex-prod.appspot.com","messagingSenderId":"253497221485","appId":"1:253497221485:web:3c81c483b94db84b227a64","measurementId":"G-NTWBKG2L0M"}
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
REACT_APP_CLOUDINARY_API_KEY=473322739956866
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BMgZT1NZztW2DsJl8Mg2L04hgY9FzAg6b8fbzgNAfww2VDzH3VE63Ot9EaP_U7KWS2JT-7HPHaw0T_Tw_5vkZc8'
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/
REACT_APP_REPORTS_SERVER_URL=https://reports.imex.online
REACT_APP_AXIOS_BASE_API_URL=https://api.romeonline.io/
REACT_APP_REPORTS_SERVER_URL=https://reports.romeonline.io
REACT_APP_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk

View File

@@ -1,14 +1,14 @@
REACT_APP_GRAPHQL_ENDPOINT=https://db.test.bodyshop.app/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.bodyshop.app/v1/graphql
REACT_APP_GA_CODE=231099835
REACT_APP_FIREBASE_CONFIG={ "apiKey":"AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c", "authDomain":"imex-test.firebaseapp.com", "projectId":"imex-test", "storageBucket":"imex-test.appspot.com", "messagingSenderId":"991923618608", "appId":"1:991923618608:web:633437569cdad78299bef5", "measurementId":"G-TW0XLZEH18"}
REACT_APP_GRAPHQL_ENDPOINT=https://db.test.romeonline.io/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.test.romeonline.io/v1/graphql
REACT_APP_GA_CODE=231103507
REACT_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
REACT_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/bodyshop
REACT_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/bodyshop
REACT_APP_CLOUDINARY_API_KEY=473322739956866
REACT_APP_CLOUDINARY_THUMB_TRANSFORMATIONS=c_fill,h_250,w_250
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BN2GcDPjipR5MTEosO5dT4CfQ3cmrdBIsI4juoOQrRijn_5aRiHlwj1mlq0W145mOusx6xynEKl_tvYJhpCc9lo'
REACT_APP_FIREBASE_PUBLIC_VAPID_KEY='BP1B7ZTYpn-KMt6nOxlld6aS8Skt3Q7ZLEqP0hAvGHxG4UojPYiXZ6kPlzZkUC5jH-EcWXomTLtmadAIxurfcHo'
REACT_APP_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
REACT_APP_AXIOS_BASE_API_URL=https://api.test.imex.online/
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
REACT_APP_AXIOS_BASE_API_URL=https://api.test.romeonline.io/
REACT_APP_REPORTS_SERVER_URL=https://reports.test.romeonline.io
REACT_APP_IS_TEST=true
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc

View File

@@ -12,7 +12,7 @@ module.exports = {
authToken:
"6b45b028a02342db97a9a2f92c0959058665443d379d4a3a876430009e744260",
org: "snapt-software",
project: "imexonline",
project: "rome-online",
release: process.env.REACT_APP_GIT_SHA,
// webpack-specific configuration
@@ -27,7 +27,7 @@ module.exports = {
lessOptions: {
modifyVars: {
...(process.env.NODE_ENV === "development"
? { "@primary-color": "#a51d1d" }
? { "@primary-color": "#B22234" }
: {
//"@primary-color": "#1DA57A"
}),

24522
client/package-lock.json generated

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.4.2",
"@fingerprintjs/fingerprintjs": "^3.3.3",
"@jsreport/browser-client": "^3.1.0",
"@sentry/react": "^7.40.0",
"@sentry/tracing": "^7.40.0",
@@ -89,8 +89,8 @@
"analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "craco start",
"build": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"build:test": "env-cmd -f .env.test npm run build",
"build-deploy:test": "npm run build:test && s3cmd sync build/* s3://imex-online-test && echo '🚀 TESTING Deployed!'",
"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!'",
"buildcra": "REACT_APP_GIT_SHA=`git rev-parse --short HEAD` craco build",
"test": "cypress open",
"eject": "react-scripts eject",

View File

@@ -28,6 +28,17 @@ switch (this.location.hostname) {
// measurementId: "${config.measurementId}",
};
break;
case "romeonline.io":
firebaseConfig = {
apiKey: "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE",
authDomain: "rome-prod-1.firebaseapp.com",
projectId: "rome-prod-1",
storageBucket: "rome-prod-1.appspot.com",
messagingSenderId: "147786367145",
appId: "1:147786367145:web:9d4cba68071c3f29a8a9b8",
measurementId: "G-G8Z9DRHTZS",
};
break;
case "imex.online":
default:
firebaseConfig = {

View File

@@ -2,23 +2,12 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
<link rel="icon" href="%PUBLIC_URL%/ro-favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#002366" />
<meta name="description" content="ImEX Online" />
<meta name="description" content="Rome Online" />
<!-- <link rel="apple-touch-icon" href="logo192.png" /> -->
<link rel="apple-touch-icon" href="logo192.png" />
<script type="text/javascript">
window.$crisp = [];
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
(function () {
d = document;
s = d.createElement("script");
s.src = "https://client.crisp.chat/l.js";
s.async = 1;
d.getElementsByTagName("head")[0].appendChild(s);
})();
</script>
<script>
!(function () {
"use strict";
@@ -77,7 +66,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>ImEX Online</title>
<title>Rome Online</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -1,6 +1,6 @@
{
"short_name": "ImEX Online",
"name": "ImEX Online",
"short_name": "Rome Online",
"name": "Rome Online",
"description": "The ultimate bodyshop management system.",
"icons": [
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

View File

@@ -27,6 +27,12 @@ export default function AppContainer() {
//componentSize="small"
input={{ autoComplete: "new-password" }}
locale={enLocale}
theme={{
token: {
colorPrimary: "#326ade",
colorInfo: "#326ade"
},
}}
form={{
validateMessages: {
// eslint-disable-next-line no-template-curly-in-string

View File

@@ -77,9 +77,9 @@ export function App({
if (currentUser.authorized && bodyshop) {
client.setAttribute("imexshopid", bodyshop.imexshopid);
LogRocket.init("rome-online/rome-online");
if (client.getTreatment("LogRocket_Tracking") === "on") {
console.log("LR Start");
LogRocket.init("gvfvfw/bodyshopapp");
LogRocket.init("rome-online/rome-online");
}
}
}, [bodyshop, client, currentUser.authorized]);
@@ -109,7 +109,7 @@ export function App({
return (
<Switch>
<Suspense fallback={<LoadingSpinner message="ImEX Online" />}>
<Suspense fallback={<LoadingSpinner />}>
<ErrorBoundary>
<Route exact path="/" component={LandingPage} />
</ErrorBoundary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -29,6 +29,7 @@ import BillFormContainer from "../bill-form/bill-form.container";
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
import { handleUpload } from "../documents-upload/documents-upload.utility";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
billEnterModal: selectBillEnterModal,
@@ -37,8 +38,8 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("billEnter")),
insertAuditTrail: ({ jobid, billid, operation }) =>
dispatch(insertAuditTrail({ jobid, billid, operation })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
const Templates = TemplateList("job_special");
@@ -63,6 +64,11 @@ function BillEnterModalContainer({
"enter_bill_generate_label",
false
);
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const formValues = useMemo(() => {
return {
...billEnterModal.context.bill,
@@ -98,6 +104,7 @@ function BillEnterModalContainer({
} = values;
let adjustmentsToInsert = {};
let payrollAdjustmentsToInsert = [];
const r1 = await insertBill({
variables: {
@@ -116,11 +123,28 @@ function BillEnterModalContainer({
...restI
} = i;
if (deductedfromlbr) {
adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
restI.actual_price / lbr_adjustment.rate;
if (Enhanced_Payroll.treatment === "on") {
if (
deductedfromlbr &&
true //payroll is on
) {
payrollAdjustmentsToInsert.push({
id: i.joblineid,
convertedtolbr: true,
convertedtolbr_data: {
mod_lb_hrs: lbr_adjustment.mod_lb_hrs * -1,
mod_lbr_ty: lbr_adjustment.mod_lbr_ty,
},
});
}
} else {
if (deductedfromlbr) {
adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] =
(adjustmentsToInsert[lbr_adjustment.mod_lbr_ty] || 0) -
restI.actual_price / lbr_adjustment.rate;
}
}
return {
...restI,
deductedfromlbr: deductedfromlbr,
@@ -143,9 +167,23 @@ function BillEnterModalContainer({
},
],
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID", "GET_JOB_BY_PK"],
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
});
await Promise.all(
payrollAdjustmentsToInsert.map((li) => {
return updateJobLines({
variables: {
lineId: li.id,
line: {
convertedtolbr: li.convertedtolbr,
convertedtolbr_data: li.convertedtolbr_data,
},
},
});
})
);
const adjKeys = Object.keys(adjustmentsToInsert);
if (adjKeys.length > 0) {
//Query the adjustments, merge, and update them.
@@ -316,18 +354,16 @@ function BillEnterModalContainer({
insertAuditTrail({
jobid: values.jobid,
billid: billId,
operation: AuditTrailMapping.billposted(
r1.data.insert_bills.returning[0].invoice_number
),
operation: AuditTrailMapping.billposted(remainingValues.invoice_number),
});
if (enterAgain) {
form.resetFields();
form.resetFields();
// form.resetFields();
form.setFieldsValue({
...formValues,
billlines: [],
});
form.resetFields();
} else {
toggleModalVisible();
}

View File

@@ -50,7 +50,6 @@ export function BillFormComponent({
job,
loadOutstandingReturns,
loadInventory,
preferredMake,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -186,7 +185,6 @@ export function BillFormComponent({
<VendorSearchSelect
disabled={disabled}
options={vendorAutoCompleteOptions}
preferredMake={preferredMake}
onSelect={handleVendorSelect}
/>
</Form.Item>
@@ -366,13 +364,15 @@ export function BillFormComponent({
)}
</LayoutFormRow>
<LayoutFormRow>
<Form.Item
span={3}
label={t("bills.fields.federal_tax_rate")}
name="federal_tax_rate"
>
<CurrencyInput min={0} disabled={disabled} />
</Form.Item>
{
// <Form.Item
// span={3}
// label={t("bills.fields.federal_tax_rate")}
// name="federal_tax_rate"
// >
// <CurrencyInput min={0} disabled={disabled} />
// </Form.Item>
}
<Form.Item
span={3}
label={t("bills.fields.state_tax_rate")}
@@ -380,13 +380,15 @@ export function BillFormComponent({
>
<CurrencyInput min={0} disabled={disabled} />
</Form.Item>
<Form.Item
span={3}
label={t("bills.fields.local_tax_rate")}
name="local_tax_rate"
>
<CurrencyInput min={0} />
</Form.Item>
{
// <Form.Item
// span={3}
// label={t("bills.fields.local_tax_rate")}
// name="local_tax_rate"
// >
// <CurrencyInput min={0} />
// </Form.Item>
}
<Form.Item shouldUpdate span={15}>
{() => {
const values = form.getFieldsValue([
@@ -412,21 +414,25 @@ export function BillFormComponent({
value={totals.subtotal.toFormat()}
precision={2}
/>
<Statistic
title={t("bills.labels.federal_tax")}
value={totals.federalTax.toFormat()}
precision={2}
/>
{
// <Statistic
// title={t("bills.labels.federal_tax")}
// value={totals.federalTax.toFormat()}
// precision={2}
// />
}
<Statistic
title={t("bills.labels.state_tax")}
value={totals.stateTax.toFormat()}
precision={2}
/>
<Statistic
title={t("bills.labels.local_tax")}
value={totals.localTax.toFormat()}
precision={2}
/>
{
// <Statistic
// title={t("bills.labels.local_tax")}
// value={totals.localTax.toFormat()}
// precision={2}
// />
}
<Statistic
title={t("bills.labels.entered_total")}
value={totals.enteredTotal.toFormat()}

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 BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
import BillInventoryTable from "../bill-inventory-table/bill-inventory-table.component";
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";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -59,7 +59,6 @@ export function BillFormContainer({
disableInvNumber={disableInvNumber}
loadOutstandingReturns={loadOutstandingReturns}
loadInventory={loadInventory}
preferredMake={lineData ? lineData.jobs_by_pk.v_make_desc : null}
/>
{!billEdit && (
<BillCmdReturnsTableComponent

View File

@@ -1,14 +1,15 @@
import { DeleteFilled, DollarCircleFilled } from "@ant-design/icons";
import { useTreatments } from "@splitsoftware/splitio-react";
import {
Button, Form,
Button,
Form,
Input,
InputNumber,
Select,
Space,
Switch,
Table,
Tooltip
Tooltip,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -45,6 +46,13 @@ export function BillEnterModalLinesComponent({
{},
bodyshop && bodyshop.imexshopid
);
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const columns = (remove) => {
return [
{
@@ -375,12 +383,31 @@ export function BillEnterModalLinesComponent({
"rate",
]);
const billline = getFieldValue(["billlines", record.name]);
const jobline = lineData.find(
(line) => line.id === billline?.joblineid
);
const employeeTeamName = bodyshop.employee_teams.find(
(team) => team.id === jobline?.assigned_team
);
if (getFieldValue(["billlines", record.name, "deductedfromlbr"]))
return (
<div>
<Space>
{t("joblines.fields.assigned_team", {
name: employeeTeamName?.name,
})}
{`${jobline.mod_lb_hrs} units/${t(
`joblines.fields.lbr_types.${jobline.mod_lbr_ty}`
)}`}
</Space>
<Form.Item
label={t("joblines.fields.mod_lbr_ty")}
key={`${index}modlbrty`}
initialValue={jobline ? jobline.mod_lbr_ty : null}
rules={[
{
required: true,
@@ -434,22 +461,44 @@ export function BillEnterModalLinesComponent({
</Select.Option>
</Select>
</Form.Item>
<Form.Item
label={t("jobs.labels.adjustmentrate")}
name={[record.name, "lbr_adjustment", "rate"]}
initialValue={bodyshop.default_adjustment_rate}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber precision={2} min={0.01} />
</Form.Item>
{price &&
adjustmentRate &&
`${(price / adjustmentRate).toFixed(1)} hrs`}
{Enhanced_Payroll.treatment === "on" ? (
<Form.Item
label={t("billlines.labels.mod_lbr_adjustment")}
name={[record.name, "lbr_adjustment", "mod_lb_hrs"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber
precision={5}
min={0.01}
max={jobline ? jobline.mod_lb_hrs : 0}
/>
</Form.Item>
) : (
<Form.Item
label={t("jobs.labels.adjustmentrate")}
name={[record.name, "lbr_adjustment", "rate"]}
initialValue={bodyshop.default_adjustment_rate}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber precision={2} min={0.01} />
</Form.Item>
)}
<Space>
{price &&
adjustmentRate &&
`${(price / adjustmentRate).toFixed(1)} hrs`}
</Space>
</div>
);
return <></>;
@@ -457,21 +506,21 @@ export function BillEnterModalLinesComponent({
</Form.Item>
),
},
{
title: t("billlines.fields.federal_tax_applicable"),
dataIndex: "applicable_taxes.federal",
editable: true,
// {
// title: t("billlines.fields.federal_tax_applicable"),
// dataIndex: "applicable_taxes.federal",
// editable: true,
formItemProps: (field) => {
return {
key: `${field.index}fedtax`,
valuePropName: "checked",
initialValue: true,
name: [field.name, "applicable_taxes", "federal"],
};
},
formInput: (record, index) => <Switch disabled={disabled} />,
},
// formItemProps: (field) => {
// return {
// key: `${field.index}fedtax`,
// valuePropName: "checked",
// // initialValue: true,
// name: [field.name, "applicable_taxes", "federal"],
// };
// },
// formInput: (record, index) => <Switch disabled={disabled} />,
// },
{
title: t("billlines.fields.state_tax_applicable"),
dataIndex: "applicable_taxes.state",
@@ -486,20 +535,20 @@ export function BillEnterModalLinesComponent({
},
formInput: (record, index) => <Switch disabled={disabled} />,
},
{
title: t("billlines.fields.local_tax_applicable"),
dataIndex: "applicable_taxes.local",
editable: true,
// {
// title: t("billlines.fields.local_tax_applicable"),
// dataIndex: "applicable_taxes.local",
// editable: true,
formItemProps: (field) => {
return {
key: `${field.index}localtax`,
valuePropName: "checked",
name: [field.name, "applicable_taxes", "local"],
};
},
formInput: (record, index) => <Switch disabled={disabled} />,
},
// formItemProps: (field) => {
// return {
// key: `${field.index}localtax`,
// valuePropName: "checked",
// name: [field.name, "applicable_taxes", "local"],
// };
// },
// formInput: (record, index) => <Switch disabled={disabled} />,
// },
{
title: t("general.labels.actions"),

View File

@@ -19,14 +19,14 @@ export const CalculateBillTotal = (invoice) => {
}).multiply(i.quantity || 1);
subtotal = subtotal.add(itemTotal);
if (i.applicable_taxes.federal) {
if (i.applicable_taxes?.federal) {
federalTax = federalTax.add(
itemTotal.percentage(federal_tax_rate || 0)
);
}
if (i.applicable_taxes.state)
if (i.applicable_taxes?.state)
stateTax = stateTax.add(itemTotal.percentage(state_tax_rate || 0));
if (i.applicable_taxes.local)
if (i.applicable_taxes?.local)
localTax = localTax.add(itemTotal.percentage(local_tax_rate || 0));
}
});

View File

@@ -63,6 +63,12 @@ const BillLineSearchSelect = (
item.oem_partno ? ` - ${item.oem_partno}` : ""
}${item.alt_partno ? ` (${item.alt_partno})` : ""}`.trim()}
</span>
{item.act_price === 0 && item.mod_lb_hrs > 0 && (
<span style={{ float: "right", paddingleft: "1rem" }}>
{`${item.mod_lb_hrs} units`}
</span>
)}
<span style={{ float: "right", paddingleft: "1rem" }}>
{item.act_price
? `$${item.act_price && item.act_price.toFixed(2)}`

View File

@@ -8,23 +8,15 @@ 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,
searchingForConversation,
phone,
jobid,
openChatByPhone,
}) {
export function ChatOpenButton({ bodyshop, phone, jobid, openChatByPhone }) {
const { t } = useTranslation();
if (!phone) return <></>;
@@ -37,7 +29,7 @@ export function ChatOpenButton({
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

@@ -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,7 +15,6 @@ 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,18 +34,6 @@ 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"
@@ -70,6 +58,18 @@ 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 min={0} precision={0} />
<InputNumber precision={0} />
</Form.Item>
<Form.Item
label={t("courtesycars.fields.fleetnumber")}
@@ -213,24 +213,49 @@ 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.nextservicekm")}
name="nextservicekm"
label={t("courtesycars.fields.nextservicedate")}
name="nextservicedate"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<InputNumber min={0} precision={0} />
<FormDatePicker />
</Form.Item>
<Form.Item
shouldUpdate={(p, c) =>
p.mileage !== c.mileage || p.nextservicekm !== c.nextservicekm
p.mileage !== c.mileage ||
p.nextservicedate !== c.nextservicedate ||
p.nextservicekm !== c.nextservicekm
}
>
{() => {
const nextservicedate = form.getFieldValue("nextservicedate");
const nextservicekm = form.getFieldValue("nextservicekm");
const mileageOver =
nextservicekm <= form.getFieldValue("mileage");
if (mileageOver)
const dueForService =
nextservicedate && moment(nextservicedate).isBefore(moment());
if (mileageOver || dueForService)
return (
<Space direction="vertical" style={{ color: "tomato" }}>
<span>
@@ -238,36 +263,6 @@ 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>
@@ -288,6 +283,12 @@ 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>
@@ -299,8 +300,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
{() => {
const expires = form.getFieldValue("registrationexpires");
const dateover =
expires && moment(expires).endOf("day").isBefore(moment());
const dateover = expires && moment(expires).isBefore(moment());
if (dateover)
return (
@@ -330,13 +330,14 @@ 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).endOf("day").isBefore(moment());
const dateover = expires && moment(expires).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 { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
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";
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
const [state, setState] = useState({
sortedInfo: {},
@@ -115,14 +115,6 @@ 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",
@@ -174,7 +166,6 @@ 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

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

View File

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

View File

@@ -1,10 +1,10 @@
import { Select, Space, Tag } from "antd";
import React, { forwardRef } from "react";
import React from "react";
import { useTranslation } from "react-i18next";
const { Option } = Select;
//To be used as a form element only.
const EmployeeSearchSelect = ({ options, ...props }, ref) => {
const EmployeeSearchSelect = ({ options, ...props }) => {
const { t } = useTranslation();
return (
@@ -39,4 +39,4 @@ const EmployeeSearchSelect = ({ options, ...props }, ref) => {
</Select>
);
};
export default forwardRef(EmployeeSearchSelect);
export default EmployeeSearchSelect;

View File

@@ -0,0 +1,33 @@
import { useQuery } from "@apollo/client";
import { Select } from "antd";
import React, { forwardRef } from "react";
import { QUERY_TEAMS } from "../../graphql/employee_teams.queries";
import AlertComponent from "../alert/alert.component";
//To be used as a form element only.
const EmployeeTeamSearchSelect = ({ ...props }, ref) => {
const { loading, error, data } = useQuery(QUERY_TEAMS);
if (error) return <AlertComponent message={JSON.stringify(error)} />;
return (
<Select
showSearch
allowClear
loading={loading}
style={{
width: 400,
}}
options={
data
? data.employee_teams.map((e) => ({
value: JSON.stringify(e),
label: e.name,
}))
: []
}
{...props}
/>
);
};
export default forwardRef(EmployeeTeamSearchSelect);

View File

@@ -40,22 +40,22 @@ class ErrorBoundary extends React.Component {
}
handleErrorSubmit = () => {
window.$crisp.push([
"do",
"message:send",
[
"text",
`I hit the following error: \n\n
${this.state.error.message}\n\n
${this.state.error.stack}\n\n
URL:${window.location} as ${this.props.currentUser.email} for ${
this.props.bodyshop && this.props.bodyshop.name
}
`,
],
]);
// window.$crisp.push([
// "do",
// "message:send",
// [
// "text",
// `I hit the following error: \n\n
// ${this.state.error.message}\n\n
// ${this.state.error.stack}\n\n
// URL:${window.location} as ${this.props.currentUser.email} for ${
// this.props.bodyshop && this.props.bodyshop.name
// }
// `,
// ],
// ]);
window.$crisp.push(["do", "chat:open"]);
// window.$crisp.push(["do", "chat:open"]);
// const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue**
// ----

View File

@@ -1,9 +1,26 @@
import Dinero from "dinero.js";
import React, { forwardRef } from "react";
const ReadOnlyFormItem = ({ value, type = "text", onChange }, ref) => {
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
const ReadOnlyFormItem = (
{ bodyshop, value, type = "text", onChange },
ref
) => {
if (!value) return null;
switch (type) {
case "employee":
const emp = bodyshop.employees.find((e) => e.id === value);
return `${emp?.first_name} ${emp?.last_name}`;
case "text":
return <div>{value}</div>;
case "currency":
@@ -14,4 +31,8 @@ const ReadOnlyFormItem = ({ value, type = "text", onChange }, ref) => {
return <div>{value}</div>;
}
};
export default forwardRef(ReadOnlyFormItem);
export default connect(
mapStateToProps,
mapDispatchToProps
)(forwardRef(ReadOnlyFormItem));

View File

@@ -54,7 +54,6 @@ 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

@@ -268,6 +268,13 @@ function Header({
{t("menus.header.timetickets")}
</Link>
</Menu.Item>
{bodyshop?.md_tasks_presets?.use_approvals && (
<Menu.Item key="ttapprovals" icon={<FieldTimeOutlined />}>
<Link to="/manage/ttapprovals">
{t("menus.header.ttapprovals")}
</Link>
</Menu.Item>
)}
<Menu.Item
key="entertimetickets"
icon={<Icon component={GiPlayerTime} />}
@@ -382,20 +389,12 @@ function Header({
<Menu.Item
key="help"
onClick={() => {
window.open("https://help.imex.online/", "_blank");
window.open("https://rometech.com/", "_blank");
}}
icon={<Icon component={QuestionCircleFilled} />}
>
{t("menus.header.help")}
</Menu.Item>
<Menu.Item
key="rescue"
onClick={() => {
window.open("https://imexrescue.com/", "_blank");
}}
>
{t("menus.header.rescueme")}
</Menu.Item>
<Menu.Item key="shiftclock">
<Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
</Menu.Item>

View File

@@ -4,7 +4,6 @@ import {
Divider,
Dropdown,
Form,
Input,
Menu,
notification,
Popover,
@@ -35,8 +34,6 @@ import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.
import ScheduleAtChange from "./job-at-change.component";
import ScheduleEventColor from "./schedule-event.color.component";
import ScheduleEventNote from "./schedule-event.note.component";
import { useMutation } from "@apollo/client";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -61,44 +58,16 @@ 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 = (
<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",
},
],
},
},
});
}}
/>
const blockContent = (
<div>
<Button
onClick={() => handleCancel({ id: event.id })}
disabled={event.arrived}
>
{t("appointments.actions.unblock")}
{t("appointments.actions.cancel")}
</Button>
</Space>
</div>
);
const popoverContent = (

View File

@@ -2,16 +2,12 @@ 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);
@@ -38,24 +34,16 @@ 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,47 +1,52 @@
import { Button, notification } from "antd";
import Axios from "axios";
import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import Dinero from "dinero.js";
export default function JobCalculateTotals({ job, disabled }) {
export default function JobCalculateTotals({ job, disabled, refetch }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [updateJob] = useMutation(UPDATE_JOB);
const handleCalculate = async () => {
setLoading(true);
const newTotals = (
await Axios.post("/job/totals", {
job: job,
})
).data;
try {
setLoading(true);
const result = await updateJob({
refetchQueries: ["GET_JOB_BY_PK"],
awaitRefetchQueries: true,
variables: {
jobId: job.id,
job: {
job_totals: newTotals,
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
"0.00"
),
},
},
});
if (!!!result.errors) {
notification["success"]({ message: t("jobs.successes.updated") });
} else {
await Axios.post("/job/totalsssu", {
id: job.id,
});
if (refetch) refetch();
// const result = await updateJob({
// refetchQueries: ["GET_JOB_BY_PK"],
// awaitRefetchQueries: true,
// variables: {
// jobId: job.id,
// job: {
// job_totals: newTotals,
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat(
// "0.00"
// ),
// },
// },
// });
// if (!!!result.errors) {
// notification["success"]({ message: t("jobs.successes.updated") });
// } else {
// notification["error"]({
// message: t("jobs.errors.updating", {
// error: JSON.stringify(result.errors),
// }),
// });
// }
} catch (error) {
notification["error"]({
message: t("jobs.errors.updating", {
error: JSON.stringify(result.errors),
error: JSON.stringify(error),
}),
});
} finally {
setLoading(false);
}
setLoading(false);
};
return (

View File

@@ -1,6 +1,5 @@
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";
@@ -13,15 +12,16 @@ 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,7 +230,6 @@ export function JobChecklistForm({
)),
scheduled_delivery:
job.scheduled_delivery && moment(job.scheduled_delivery),
production_vars: job.production_vars,
}),
...(type === "deliver" && {
removeFromProduction: true,

View File

@@ -32,9 +32,9 @@ const mapDispatchToProps = (dispatch) => ({
});
const span = {
sm: { span: 24 },
md: { span: 12 },
lg: { span: 8 },
lg: { span: 24 },
xl: { span: 12 },
xxl: { span: 8 },
};
export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
@@ -137,12 +137,6 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsPartsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsNotesComponent
loading={loading}
@@ -163,6 +157,12 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col span={24}>
<JobDetailCardsPartsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
</Row>
</Card>
) : null}

View File

@@ -1,16 +1,119 @@
import { Table } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
import PartsStatusPie from "../parts-status-pie/parts-status-pie.component";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsPartsComponent({ loading, data }) {
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
import { alphaSort } from "../../utils/sorters";
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
import JobLineStatusPopup from "../job-line-status-popup/job-line-status-popup.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
jobRO: selectJobReadOnly,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobDetailCardsPartsComponent);
export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
const { t } = useTranslation();
const { joblines_status } = data;
const columns = [
{
title: t("joblines.fields.line_desc"),
dataIndex: "line_desc",
fixed: "left",
key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
onCell: (record) => ({
className: record.manual_line && "job-line-manual",
style: {
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
},
}),
width: "30%",
ellipsis: true,
},
{
title: t("joblines.fields.part_type"),
dataIndex: "part_type",
key: "part_type",
width: "15%",
sorter: (a, b) =>
alphaSort(
t(`joblines.fields.part_types.${a.part_type}`),
t(`joblines.fields.part_types.${b.part_type}`)
),
render: (text, record) =>
record.part_type
? t(`joblines.fields.part_types.${record.part_type}`)
: null,
},
{
title: t("joblines.fields.part_qty"),
dataIndex: "part_qty",
key: "part_qty",
width: "10%",
},
{
title: t("joblines.fields.notes"),
dataIndex: "notes",
key: "notes",
render: (text, record) => (
<JobLineNotePopup disabled={jobRO} jobline={record} />
),
},
{
title: t("joblines.fields.location"),
dataIndex: "location",
key: "location",
sorter: (a, b) => alphaSort(a.location, b.location),
render: (text, record) => (
<JobLineLocationPopup jobline={record} disabled={jobRO} />
),
},
{
title: t("joblines.fields.status"),
dataIndex: "status",
key: "status",
sorter: (a, b) => alphaSort(a.status, b.status),
filters:
(data &&
data.joblines
?.map((l) => l.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
render: (text, record) => (
<JobLineStatusPopup jobline={record} disabled={jobRO} />
),
},
];
return (
<div>
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
<PartsStatusPie joblines_status={joblines_status} />
<Table
key="id"
columns={columns}
dataSource={data ? data.joblines : []}
/>
</CardTemplate>
</div>
);

View File

@@ -8,7 +8,18 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component";
export default function JobLinesExpander({ jobline, jobid }) {
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobLinesExpander);
export function JobLinesExpander({ jobline, jobid, bodyshop }) {
const { t } = useTranslation();
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
fetchPolicy: "network-only",
@@ -23,7 +34,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
return (
<Row>
<Col md={24} lg={12}>
<Col md={24} lg={8}>
<Typography.Title level={4}>
{t("parts_orders.labels.parts_orders")}
</Typography.Title>
@@ -49,7 +60,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
)}
</Timeline>
</Col>
<Col md={24} lg={12}>
<Col md={24} lg={8}>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<Timeline>
{data.billlines.length > 0 ? (
@@ -71,7 +82,7 @@ export default function JobLinesExpander({ jobline, jobid }) {
</Col>
<Col span={4}>
<span>
{`${t("billlines.fields.actual_cost")}: `}
{`${t("billlines.fields.actual_cost")}: `}
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
</span>
</Col>
@@ -89,6 +100,37 @@ export default function JobLinesExpander({ jobline, jobid }) {
)}
</Timeline>
</Col>
<Col md={24} lg={8}>
<Typography.Title level={4}>
{t("parts_dispatch.labels.parts_dispatch")}
</Typography.Title>
<Timeline>
{data.parts_dispatch_lines.length > 0 ? (
data.parts_dispatch_lines.map((line) => (
<Timeline.Item key={line.id}>
<Space split={<Divider type="vertical" />} wrap>
<Link to={`/manage/jobs/${jobid}?partsorderid=${line.id}`}>
{line.parts_dispatch.number}
</Link>
{
bodyshop.employees.find(
(e) => e.id === line.parts_dispatch.employeeid
)?.first_name
}
<Space>
{t("parts_dispatch_lines.fields.accepted_at")}
<DateFormatter>{line.accepted_at}</DateFormatter>
</Space>
</Space>
</Timeline.Item>
))
) : (
<Timeline.Item>
{t("parts_orders.labels.notyetordered")}
</Timeline.Item>
)}
</Timeline>
</Col>
</Row>
);
}

View File

@@ -0,0 +1,97 @@
import { useMutation } from "@apollo/client";
import { Button, Form, notification, Popover, Tooltip } from "antd";
import { t } from "i18next";
import React, { useState } from "react";
import { UPDATE_LINE_PPC } from "../../graphql/jobs-lines.queries";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
import axios from "axios";
export default function JobLinesPartPriceChange({ job, line, refetch }) {
const [loading, setLoading] = useState(false);
const [updatePartPrice] = useMutation(UPDATE_LINE_PPC);
const handleFinish = async (values) => {
try {
setLoading(true);
const result = await updatePartPrice({
variables: {
id: line.id,
jobline: {
act_price_before_ppc: line.act_price_before_ppc
? line.act_price_before_ppc
: line.act_price,
act_price: values.act_price,
},
},
});
await axios.post("/job/totalsssu", {
id: job.id,
});
if (result.errors) {
notification.open({
type: "error",
message: t("joblines.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
if (refetch) refetch();
} else {
notification.open({
type: "success",
message: t("joblines.successes.saved"),
});
}
} catch (error) {
notification.open({
type: "error",
message: t("joblines.errors.saving", { error: JSON.stringify(error) }),
});
} finally {
setLoading(false);
}
};
const popcontent = (
<Form layout="vertical" onFinish={handleFinish} initialValues={{ act_price: line.act_price }}>
<Form.Item
name="act_price"
label={t("jobs.labels.act_price_ppc")}
rules={[{ required: true }]}
>
<CurrencyFormItemComponent />
</Form.Item>
<Button loading={loading} htmlType="primary">
{t("general.actions.save")}
</Button>
</Form>
);
return (
<JobLineConvertToLabor jobline={line} job={job}>
<Popover trigger="click" disabled={line.manual_line} content={popcontent}>
<CurrencyFormatter>
{line.db_ref === "900510" || line.db_ref === "900511"
? line.prt_dsmk_m
: line.act_price}
</CurrencyFormatter>
{line.prt_dsmk_p && line.prt_dsmk_p !== 0 ? (
<span style={{ marginLeft: ".2rem" }}>{`(${line.prt_dsmk_p}%)`}</span>
) : (
<></>
)}
{line.act_price_before_ppc && line.act_price_before_ppc !== 0 ? (
<Tooltip title={t("jobs.labels.ppc")}>
<span style={{ marginLeft: ".2rem", color: "tomato" }}>
(
<CurrencyFormatter>{line.act_price_before_ppc}</CurrencyFormatter>
)
</span>
</Tooltip>
) : (
<></>
)}
</Popover>
</JobLineConvertToLabor>
);
}

View File

@@ -1,12 +1,12 @@
import {
DeleteFilled,
EditFilled,
FilterFilled,
HomeOutlined,
MinusCircleTwoTone,
PlusCircleTwoTone,
SyncOutlined,
WarningFilled,
EditFilled,
PlusCircleTwoTone,
MinusCircleTwoTone,
HomeOutlined,
} from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import {
@@ -29,7 +29,6 @@ import { selectJobReadOnly } from "../../redux/application/application.selectors
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters";
import JobLineLocationPopup from "../job-line-location-popup/job-line-location-popup.component";
import JobLineNotePopup from "../job-line-note-popup/job-line-note-popup.component";
@@ -38,13 +37,18 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
// import AllocationsAssignmentContainer from "../allocations-assignment/allocations-assignment.container";
// import AllocationsBulkAssignmentContainer from "../allocations-bulk-assignment/allocations-bulk-assignment.container";
// import AllocationsEmployeeLabelContainer from "../allocations-employee-label/allocations-employee-label.container";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import _ from "lodash";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobLinesExpander from "./job-lines-expander.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment";
import JobLineConvertToLabor from "../job-line-convert-to-labor/job-line-convert-to-labor.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobSendPartPriceChangeComponent from "../job-send-parts-price-change/job-send-parts-price-change.component";
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
import JobLinesExpander from "./job-lines-expander.component";
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
import JoblineTeamAssignment from "../job-line-team-assignment/job-line-team-assignmnent.component";
import JobLineDispatchButton from "../job-line-dispatch-button/job-line-dispatch-button.component";
import JobLineBulkAssignComponent from "../job-line-bulk-assign/job-line-bulk-assign.component";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -76,8 +80,16 @@ export function JobLinesComponent({
setBillEnterContext,
}) {
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const [selectedLines, setSelectedLines] = useState([]);
console.log(
"🚀 ~ file: job-lines.component.jsx:89 ~ selectedLines:",
selectedLines
);
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: {},
@@ -121,10 +133,21 @@ export function JobLinesComponent({
sortOrder:
state.sortedInfo.columnKey === "oem_partno" && state.sortedInfo.order,
ellipsis: true,
render: (text, record) =>
`${record.oem_partno || ""} ${
record.alt_partno ? `(${record.alt_partno})` : ""
}`.trim(),
onCell: (record) => ({
className: record.manual_line && "job-line-manual",
style: {
...(record.parts_dispatch_lines[0]?.accepted_at
? { boxShadow: " -.5em 0 0 #FFC107" }
: {}),
},
}),
render: (text, record) => (
<span class="ant-table-cell-content">
{`${record.oem_partno || ""} ${
record.alt_partno ? `(${record.alt_partno})` : ""
}`.trim()}
</span>
),
},
{
title: t("joblines.fields.op_code_desc"),
@@ -220,20 +243,7 @@ export function JobLinesComponent({
state.sortedInfo.columnKey === "act_price" && state.sortedInfo.order,
ellipsis: true,
render: (text, record) => (
<JobLineConvertToLabor jobline={record} job={job}>
<CurrencyFormatter>
{record.db_ref === "900510" || record.db_ref === "900511"
? record.prt_dsmk_m
: record.act_price}
</CurrencyFormatter>
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
<span
style={{ marginLeft: ".2rem" }}
>{`(${record.prt_dsmk_p}%)`}</span>
) : (
<></>
)}
</JobLineConvertToLabor>
<JobLinesPartPriceChange line={record} job={job} refetch={refetch} />
),
},
{
@@ -286,6 +296,23 @@ export function JobLinesComponent({
state.sortedInfo.columnKey === "line_ind" && state.sortedInfo.order,
responsive: ["md"],
},
...(Enhanced_Payroll.treatment === "on"
? [
{
title: t("joblines.fields.assigned_team"),
dataIndex: "assigned_team",
key: "assigned_team",
render: (text, record) => (
<JoblineTeamAssignment
disabled={jobRO}
jobline={record}
jobId={job.id}
/>
),
},
]
: []),
{
title: t("joblines.fields.notes"),
dataIndex: "notes",
@@ -404,7 +431,11 @@ export function JobLinesComponent({
setSelectedLines((selectedLines) =>
_.uniq([
...selectedLines,
...jobLines.filter((item) => markedTypes.includes(item.part_type)),
...jobLines.filter(
(item) =>
markedTypes.includes(item.part_type) ||
markedTypes.includes(item.mod_lbr_ty)
),
])
);
}
@@ -417,6 +448,21 @@ export function JobLinesComponent({
<Menu.Item key="PAL">{t("joblines.fields.part_types.PAL")}</Menu.Item>
<Menu.Item key="PAS">{t("joblines.fields.part_types.PAS")}</Menu.Item>
<Menu.Divider />
<Menu.Item key="LAA">{t("joblines.fields.lbr_types.LAA")}</Menu.Item>
<Menu.Item key="LAB">{t("joblines.fields.lbr_types.LAB")}</Menu.Item>
<Menu.Item key="LAD">{t("joblines.fields.lbr_types.LAD")}</Menu.Item>
<Menu.Item key="LAE">{t("joblines.fields.lbr_types.LAE")}</Menu.Item>
<Menu.Item key="LAF">{t("joblines.fields.lbr_types.LAF")}</Menu.Item>
<Menu.Item key="LAG">{t("joblines.fields.lbr_types.LAG")}</Menu.Item>
<Menu.Item key="LAM">{t("joblines.fields.lbr_types.LAM")}</Menu.Item>
<Menu.Item key="LAR">{t("joblines.fields.lbr_types.LAR")}</Menu.Item>
<Menu.Item key="LAS">{t("joblines.fields.lbr_types.LAS")}</Menu.Item>
<Menu.Item key="LAU">{t("joblines.fields.lbr_types.LAU")}</Menu.Item>
<Menu.Item key="LA1">{t("joblines.fields.lbr_types.LA1")}</Menu.Item>
<Menu.Item key="LA2">{t("joblines.fields.lbr_types.LA2")}</Menu.Item>
<Menu.Item key="LA3">{t("joblines.fields.lbr_types.LA3")}</Menu.Item>
<Menu.Item key="LA4">{t("joblines.fields.lbr_types.LA2")}</Menu.Item>
<Menu.Divider />
<Menu.Item key="clear">{t("general.labels.clear")}</Menu.Item>
</Menu>
);
@@ -440,6 +486,18 @@ export function JobLinesComponent({
</Space>
</Tag>
)}
<JobLineDispatchButton
selectedLines={selectedLines}
setSelectedLines={setSelectedLines}
job={job}
/>
{Enhanced_Payroll.treatment === "on" && (
<JobLineBulkAssignComponent
selectedLines={selectedLines}
setSelectedLines={setSelectedLines}
job={job}
/>
)}
<Button
disabled={
(job && !job.converted) ||
@@ -448,15 +506,6 @@ export function JobLinesComponent({
technician
}
onClick={() => {
// setPartsOrderContext({
// actions: { refetch: refetch },
// context: {
// jobId: job.id,
// job: job,
// linesToOrder: selectedLines,
// },
// });
setBillEnterContext({
actions: { refetch: refetch },
context: {
@@ -563,6 +612,9 @@ export function JobLinesComponent({
>
{t("joblines.actions.new")}
</Button>
{bodyshop.region_config.toLowerCase().startsWith("us") && (
<JobSendPartPriceChangeComponent job={job} />
)}
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
<Input.Search
placeholder={t("general.labels.search")}

View File

@@ -0,0 +1,149 @@
import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { Button, Form, Popover, Select, Space, notification } from "antd";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_LINE_BULK_ASSIGN } from "../../graphql/jobs-lines.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(mapStateToProps, mapDispatchToProps)(JoblineBulkAssign);
export function JoblineBulkAssign({
setSelectedLines,
selectedLines,
insertAuditTrail,
bodyshop,
jobRO,
job,
currentUser,
}) {
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const { t } = useTranslation();
const [assignLines] = useMutation(UPDATE_LINE_BULK_ASSIGN);
const handleConvert = async (values) => {
try {
setLoading(true);
const result = await assignLines({
variables: {
jobline: {
assigned_team: values.assigned_team,
},
ids: selectedLines.map((l) => l.id),
},
});
if (result.errors) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: JSON.stringify(result.errors),
}),
});
} else {
//Insert the audit trail here.
const teamName = bodyshop.employee_teams.find(
(et) => et.id === values.assigned_team
)?.name;
const hours = selectedLines.reduce(
(acc, val) => (acc += val.mod_lb_hrs),
0
);
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.assignedlinehours(
teamName,
hours.toFixed(1)
),
});
setSelectedLines([]);
setVisible(false);
}
} catch (error) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: error,
}),
});
} finally {
setLoading(false);
}
};
const popMenu = (
<div>
<Form layout="vertical" form={form} onFinish={handleConvert}>
<Form.Item
name={"assigned_team"}
label={t("joblines.fields.assigned_team")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
showSearch
style={{ width: 200 }}
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children
.toLowerCase()
.indexOf(input.toLowerCase()) >= 0
}
>
{bodyshop.employee_teams.map((team) => (
<Select.Option value={team.id} key={team.id} name={team.name}>
{team.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>
<Button type="danger" onClick={() => form.submit()} loading={loading}>
{t("general.actions.save")}
</Button>
<Button onClick={() => setVisible(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</Form>
</div>
);
return (
<Popover open={visible} content={popMenu}>
<Button
disabled={selectedLines.length === 0 || jobRO}
loading={loading}
onClick={() => setVisible(true)}
>
{t("joblines.actions.assign_team", { count: selectedLines.length })}
</Button>
</Popover>
);
}

View File

@@ -0,0 +1,167 @@
import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { Button, Form, Popover, Select, Space, notification } from "antd";
import moment from "moment";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_PARTS_DISPATCH } from "../../graphql/parts-dispatch.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobLineDispatchButton);
export function JobLineDispatchButton({
setSelectedLines,
selectedLines,
bodyshop,
jobRO,
job,
currentUser,
}) {
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const Templates = TemplateList("job_special", {
ro_number: job.ro_number,
});
const { t } = useTranslation();
const [dispatchLines] = useMutation(INSERT_PARTS_DISPATCH);
const handleConvert = async (values) => {
try {
setLoading(true);
//THIS HAS NOT YET BEEN TESTED. START BY FINISHING THIS FUNCTION.
const result = await dispatchLines({
variables: {
partsDispatch: {
dispatched_at: moment(),
employeeid: values.employeeid,
jobid: job.id,
dispatched_by: currentUser.email,
parts_dispatch_lines: {
data: selectedLines.map((l) => ({
joblineid: l.id,
quantity: l.part_qty,
})),
},
},
//joblineids: selectedLines.map((l) => l.id),
},
});
if (result.errors) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: result.errors,
}),
});
} else {
setSelectedLines([]);
await GenerateDocument(
{
name: Templates.parts_dispatch.key,
variables: {
id: result.data.insert_parts_dispatch_one.id,
},
},
{},
"p"
);
}
setVisible(false);
} catch (error) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.creating", {
error: error,
}),
});
} finally {
setLoading(false);
}
};
const popMenu = (
<div>
<Form layout="vertical" form={form} onFinish={handleConvert}>
<Form.Item
name={"employeeid"}
label={t("timetickets.fields.employee")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
showSearch
style={{ width: 200 }}
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children
.toLowerCase()
.indexOf(input.toLowerCase()) >= 0
}
>
{bodyshop.employees
.filter((emp) => emp.active)
.map((emp) => (
<Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>
<Button
type="danger"
onClick={() => form.submit()}
loading={loading}
disabled={selectedLines.length === 0}
>
{t("general.actions.save")}
</Button>
<Button onClick={() => setVisible(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</Form>
</div>
);
return (
<Popover open={visible} content={popMenu}>
<Button
disabled={selectedLines.length === 0 || jobRO}
loading={loading}
onClick={() => setVisible(true)}
>
{t("joblines.actions.dispatchparts", { count: selectedLines.length })}
</Button>
</Popover>
);
}

View File

@@ -1,6 +1,6 @@
import { useMutation } from "@apollo/client";
import { notification, Select } from "antd";
import { notification, Select, Space } from "antd";
import React, { useEffect, useState } from "react";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -56,10 +56,8 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
<LoadingSpinner loading={loading}>
<Select
autoFocus
allowClear
dropdownMatchSelectWidth={100}
value={location}
onClear={() => setLocation(null)}
onSelect={handleChange}
onBlur={handleSave}
>
@@ -77,7 +75,10 @@ export function JobLineLocationPopup({ bodyshop, jobline, disabled }) {
style={{ width: "100%", minHeight: "2rem", cursor: "pointer" }}
onClick={() => !disabled && setEditing(true)}
>
{jobline.location}
<Space wrap>
{jobline.location}
{jobline.parts_dispatch_lines?.length > 0 && "-Disp"}
</Space>
</div>
);
}

View File

@@ -0,0 +1,115 @@
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";
import { UPDATE_JOB_LINE } from "../../graphql/jobs-lines.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JoblineTeamAssignment({
bodyshop,
jobline,
disabled,
jobId,
insertAuditTrail,
}) {
const [editing, setEditing] = useState(false);
const [loading, setLoading] = useState(false);
const [assignedTeam, setAssignedTeam] = useState(jobline.assigned_team);
const [updateJob] = useMutation(UPDATE_JOB_LINE);
const { t } = useTranslation();
useEffect(() => {
if (editing) setAssignedTeam(jobline.assigned_team);
}, [editing, jobline.assigned_team]);
const handleChange = (e) => {
setAssignedTeam(e);
};
const handleSave = async (e) => {
setLoading(true);
const result = await updateJob({
variables: {
lineId: jobline.id,
line: { assigned_team: assignedTeam },
},
});
if (!!!result.errors) {
notification["success"]({ message: t("joblines.successes.saved") });
//insert the audit trail here.
const teamName = bodyshop.employee_teams.find(
(et) => et.id === assignedTeam
)?.name;
insertAuditTrail({
jobid: jobId,
operation: AuditTrailMapping.assignedlinehours(
teamName,
jobline.mod_lb_hrs
),
});
} else {
notification["error"]({
message: t("joblines.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
setEditing(false);
};
if (editing)
return (
<div>
<LoadingSpinner loading={loading}>
<Select
autoFocus
allowClear
dropdownMatchSelectWidth={100}
value={assignedTeam}
onSelect={handleChange}
onBlur={handleSave}
onClear={() => handleChange(null)}
>
{Object.values(bodyshop.employee_teams).map((s, idx) => (
<Select.Option key={idx} value={s.id}>
{s.name}
</Select.Option>
))}
</Select>
</LoadingSpinner>
</div>
);
const team = bodyshop.employee_teams.find(
(tm) => tm.id === jobline.assigned_team
);
return (
<div
style={{ width: "100%", minHeight: "1rem", cursor: "pointer" }}
onClick={() => !disabled && setEditing(true)}
>
{team?.name}
</div>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(JoblineTeamAssignment);

View File

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

View File

@@ -0,0 +1,18 @@
import { Alert } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
export default function JobProfileDataWarning({ job }) {
const { t } = useTranslation();
let missingProfileInfo =
Object.keys(job.cieca_pft).length === 0 ||
Object.keys(job.cieca_pfl).length === 0 ||
Object.keys(job.materials).length === 0;
if (missingProfileInfo)
return (
<Alert type="error" message={t("jobs.labels.missingprofileinfo")}></Alert>
);
return null;
}

View File

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

View File

@@ -0,0 +1,31 @@
import { Button, notification } from "antd";
import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
export default function JobSendPartPriceChangeComponent({ job }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
try {
const ppcData = await axios.post("/job/ppc", { jobid: job.id });
await axios.post("http://localhost:1337/ppc/", ppcData.data);
} catch (error) {
notification.open({
type: "error",
message: t("jobs.errors.partspricechange", {
error: JSON.stringify(error),
}),
});
} finally {
setLoading(false);
}
};
return (
<Button onClick={handleClick} loading={loading}>
{t("jobs.actions.sendpartspricechange")}
</Button>
);
}

View File

@@ -67,7 +67,8 @@ export function JobsTotalsTableComponent({ jobRO, currentUser, job }) {
<JobTotalsTableTotals job={job} />
</Card>
</Col>
{currentUser.email.includes("@imex.") && (
{(currentUser.email.includes("@imex.") ||
currentUser.email.includes("@rome.")) && (
<Col span={24}>
<Card title="DEVELOPMENT USE ONLY">
<JobCalculateTotals job={job} disabled={jobRO} />

View File

@@ -123,11 +123,10 @@ export default function JobTotalsTableLabor({ job }) {
<Space>
{t("jobs.labels.mapa")}
{job.materials &&
job.materials.mapa &&
job.materials.mapa.cal_maxdlr &&
job.materials.mapa.cal_maxdlr > 0 &&
job.materials.MAPA &&
job.materials.MAPA.cal_maxdlr !== undefined &&
t("jobs.labels.threshhold", {
amount: job.materials.mapa.cal_maxdlr,
amount: job.materials.MAPA.cal_maxdlr,
})}
</Space>
</Table.Summary.Cell>
@@ -148,11 +147,10 @@ export default function JobTotalsTableLabor({ job }) {
<Space wrap>
{t("jobs.labels.mash")}
{job.materials &&
job.materials.mash &&
job.materials.mash.cal_maxdlr &&
job.materials.mash.cal_maxdlr > 0 &&
job.materials.MASH &&
job.materials.MASH.cal_maxdlr !== undefined &&
t("jobs.labels.threshhold", {
amount: job.materials.mash.cal_maxdlr,
amount: job.materials.MASH.cal_maxdlr,
})}
</Space>
</Table.Summary.Cell>

View File

@@ -11,6 +11,22 @@ export default function JobTotalsTableParts({ job }) {
filteredInfo: {},
});
const insuranceAdjustments = useMemo(() => {
if (!job.job_totals) return [];
if (!job.job_totals?.parts?.adjustments) return [];
const adjs = [];
Object.keys(job.job_totals?.parts?.adjustments).forEach((key) => {
if (Dinero(job.job_totals?.parts?.adjustments[key]).getAmount() !== 0) {
adjs.push({
id: key,
amount: Dinero(job.job_totals.parts.adjustments[key]),
});
}
});
return adjs;
}, [job.job_totals]);
const data = useMemo(() => {
return Object.keys(job.job_totals.parts.parts.list)
.filter(
@@ -74,11 +90,11 @@ export default function JobTotalsTableParts({ job }) {
<Table.Summary.Cell>
{t("jobs.labels.prt_dsmk_total")}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{Dinero(job.job_totals.parts.parts.prt_dsmk_total).toFormat()}
</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell>
<strong>{t("jobs.labels.partstotal")}</strong>
@@ -90,6 +106,24 @@ export default function JobTotalsTableParts({ job }) {
</strong>
</Table.Summary.Cell>
</Table.Summary.Row>
{insuranceAdjustments.length > 0 && (
<Table.Summary.Row>
<Table.Summary.Cell colSpan={24}>
{t("jobs.labels.profileadjustments")}
</Table.Summary.Cell>
</Table.Summary.Row>
)}
{insuranceAdjustments.map((adj, idx) => (
<Table.Summary.Row key={idx}>
<Table.Summary.Cell>
{t(`jobs.fields.${adj.id.toLowerCase()}`)}
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
{adj.amount.toFormat()}
</Table.Summary.Cell>
</Table.Summary.Row>
))}
</>
)}
/>

View File

@@ -28,26 +28,46 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
total: job.job_totals.totals.subtotal,
bold: true,
},
{
key: t("jobs.labels.local_tax_amt"),
total: job.job_totals.totals.local_tax,
},
{
key: t("jobs.labels.state_tax_amt"),
total: job.job_totals.totals.state_tax,
},
...(bodyshop.region_config === "CA_BC"
...(job.job_totals.totals.us_sales_tax_breakdown
? [
{
key: t("jobs.fields.ca_bc_pvrt"),
total: job.job_totals.additional.pvrt,
key:
bodyshop.md_responsibility_centers.taxes.tax_ty1?.tax_type1 ||
"T1",
total: job.job_totals.totals.us_sales_tax_breakdown.ty1Tax,
},
{
key:
bodyshop.md_responsibility_centers.taxes.tax_ty2?.tax_type2 ||
"T2",
total: job.job_totals.totals.us_sales_tax_breakdown.ty2Tax,
},
{
key:
bodyshop.md_responsibility_centers.taxes.tax_ty3?.tax_type3 ||
"T3",
total: job.job_totals.totals.us_sales_tax_breakdown.ty3Tax,
},
{
key:
bodyshop.md_responsibility_centers.taxes.tax_ty4?.tax_type4 ||
"T4",
total: job.job_totals.totals.us_sales_tax_breakdown.ty4Tax,
},
{
key:
bodyshop.md_responsibility_centers.taxes.tax_ty5?.tax_type5 ||
"T5",
total: job.job_totals.totals.us_sales_tax_breakdown.ty5Tax,
},
]
: []),
{
key: t("jobs.labels.federal_tax_amt"),
total: job.job_totals.totals.federal_tax,
},
: [
{
key: t("jobs.labels.state_tax_amt"),
total: job.job_totals.totals.state_tax,
},
]),
{
key: t("jobs.labels.total_repairs"),
total: job.job_totals.totals.total_repairs,
@@ -57,10 +77,10 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
key: t("jobs.fields.ded_amt"),
total: job.job_totals.totals.custPayable.deductible,
},
{
key: t("jobs.fields.federal_tax_payable"),
total: job.job_totals.totals.custPayable.federal_tax,
},
// {
// key: t("jobs.fields.federal_tax_payable"),
// total: job.job_totals.totals.custPayable.federal_tax,
// },
{
key: t("jobs.fields.other_amount_payable"),
total: job.job_totals.totals.custPayable.other_customer_amount,
@@ -81,7 +101,7 @@ export function JobTotalsTableTotals({ bodyshop, job }) {
bold: true,
},
];
}, [job.job_totals, t, bodyshop.region_config]);
}, [job.job_totals, t, bodyshop.md_responsibility_centers]);
const columns = [
{

View File

@@ -13,7 +13,6 @@ 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
@@ -54,7 +53,7 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
operation: AuditTrailMapping.admin_jobfieldchange(
key,
changedAuditFields[key] instanceof moment
? DateTimeFormat(changedAuditFields[key])
? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a")
: changedAuditFields[key]
),
});
@@ -180,12 +179,6 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
<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

@@ -25,7 +25,7 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
//Found a relevant matching line. Add it to lines to update.
linesToUpdate.push({
id: existingLines[matchingIndex].id,
newData: { ...newLine, removed: false },
newData: { ...newLine, removed: false, act_price_before_ppc: null },
});
//Splice out item we found for performance.
@@ -50,7 +50,6 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
.reduce((acc, value, idx) => {
return acc + generateRemoveQuery(value, idx);
}, "");
console.log(insertQueries, updateQueries, removeQueries);
if ((insertQueries + updateQueries + removeQueries).trim() === "") {
return new Promise((resolve, reject) => {

View File

@@ -6,9 +6,9 @@ import {
useQuery,
} from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Col, notification, Row } from "antd";
import { Button, Col, Row, notification } from "antd";
import Axios from "axios";
import Dinero from "dinero.js";
import _ from "lodash";
import moment from "moment";
import queryString from "query-string";
import React, { useCallback, useEffect, useState } from "react";
@@ -30,7 +30,6 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import AlertComponent from "../alert/alert.component";
@@ -73,8 +72,6 @@ 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);
@@ -92,14 +89,14 @@ export function JobsAvailableContainer({
const modalSearchState = useState("");
//Import Scenario
const onOwnerFindModalOk = async () => {
const onOwnerFindModalOk = async (lazyData) => {
logImEXEvent("job_import_new");
setOwnerModalVisible(false);
setInsertLoading(true);
const estData = replaceEmpty(estDataRaw.data.available_jobs_by_pk);
const estData = replaceEmpty(
lazyData?.available_jobs_by_pk || estDataRaw.data.available_jobs_by_pk
);
if (!(estData && estData.est_data)) {
//We don't have the right data. Error!
@@ -109,21 +106,25 @@ export function JobsAvailableContainer({
});
return;
}
// if (process.env.REACT_APP_COUNTRY === "USA") {
//Massage the CCC file set to remove duplicate UNQ_SEQ.
await ResolveCCCLineIssues(estData.est_data, bodyshop);
// } else {
//IO-539 Check for Parts Rate on PAL for SGI use case.
await CheckTaxRates(estData.est_data, bodyshop);
const newTotals = (
await Axios.post("/job/totals", {
job: {
...estData.est_data,
joblines: estData.est_data.joblines.data,
},
})
).data;
// }
// const newTotals = (
// await Axios.post("/job/totals", {
// job: {
// ...estData.est_data,
// joblines: estData.est_data.joblines.data,
// },
// })
// ).data;
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: {
@@ -134,9 +135,9 @@ export function JobsAvailableContainer({
const newJob = {
...estData.est_data,
clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
job_totals: newTotals,
// clm_total: Dinero(newTotals.totals.total_repairs).toFormat("0.00"),
// owner_owing: Dinero(newTotals.totals.custPayable.total).toFormat("0.00"),
// job_totals: newTotals,
date_open: moment(),
status: bodyshop.md_ro_statuses.default_imported,
notes: {
@@ -146,7 +147,7 @@ export function JobsAvailableContainer({
text: t("jobs.labels.importnote"),
},
},
queued_for_parts: partsQueueToggle,
queued_for_parts: true,
...(existingVehicles && existingVehicles.data.vehicles.length > 0
? { vehicleid: existingVehicles.data.vehicles[0].id, vehicle: null }
: {}),
@@ -160,17 +161,23 @@ export function JobsAvailableContainer({
delete newJob.vehicle;
}
if (typeof newJob.kmin === "string") {
newJob.kmin = null;
}
try {
const r = await insertNewJob({
variables: {
job: newJob,
},
});
await Axios.post("/job/totalsssu", {
id: r.data.insert_jobs.returning[0].id,
});
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
}
notification["success"]({
message: t("jobs.successes.created"),
onClick: () => {
@@ -184,27 +191,23 @@ export function JobsAvailableContainer({
operation: AuditTrailMapping.jobimported(),
});
deleteJob({
await deleteJob({
variables: { id: estData.id },
}).then((r) => {
refetch();
setInsertLoading(false);
});
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
} catch (err) {
} catch (r) {
//error while inserting
notification["error"]({
message: t("jobs.errors.creating", { error: err.message }),
message: t("jobs.errors.creating", { error: r.message }),
});
refetch().catch(e => {console.error(`Something went wrong in jobs available table container - ${err.message || ''}`)});
refetch();
setInsertLoading(false);
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
}
};
//Supplement scenario
//Suplement scenario
const onJobFindModalOk = async () => {
logImEXEvent("job_import_supplement");
@@ -224,6 +227,7 @@ export function JobsAvailableContainer({
let supp = replaceEmpty({ ...estData.est_data });
//IO-539 Check for Parts Rate on PAL for SGI use case.
await CheckTaxRates(supp, bodyshop);
await ResolveCCCLineIssues(supp, bodyshop);
delete supp.owner;
delete supp.vehicle;
@@ -256,14 +260,11 @@ export function JobsAvailableContainer({
// "0.00"
// ),
// job_totals: newTotals,
queued_for_parts: partsQueueToggle,
// queued_for_parts: true,
},
},
});
setPartsQueueToggle(bodyshop.md_functionality_toggles.parts_queue_toggle);
if (CriticalPartsScanning.treatment === "on") {
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
}
if (updateResult.errors) {
@@ -338,14 +339,12 @@ 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) => {
@@ -366,8 +365,6 @@ export function JobsAvailableContainer({
}, [addJobAsSupp, availableJobId, clm_no]);
if (error) return <AlertComponent type="error" message={error.message} />;
return (
<LoadingSpinner
loading={insertLoading}
@@ -377,14 +374,11 @@ 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}
@@ -396,9 +390,26 @@ export function JobsAvailableContainer({
onOk={onJobFindModalOk}
onCancel={onJobModalCancel}
modalSearchState={modalSearchState}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
/>
{currentUser.email.includes("@rome.") ||
currentUser.email.includes("@imex.") ? (
<Button
onClick={async () => {
for (const record of data.available_jobs) {
//Query the data
console.log("Start Job", record.id);
const { data } = await loadEstData({
variables: { id: record.id },
});
console.log("Query has been awaited and is complete");
await onOwnerFindModalOk(data);
}
}}
>
Add all jobs as new.
</Button>
) : null}
<Row gutter={[16, 16]}>
<Col span={24}>
<JobsAvailableTableComponent
@@ -430,115 +441,158 @@ function replaceEmpty(someObj, replaceValue = null) {
}
async function CheckTaxRates(estData, bodyshop) {
//LKQ Check
if (
!estData.parts_tax_rates?.PAL ||
estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
) {
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
);
if (res) {
if (!estData.parts_tax_rates.PAL) {
estData.parts_tax_rates.PAL = {
prt_discp: 0,
prt_mktyp: true,
prt_mkupp: 0,
prt_type: "PAL",
};
}
estData.parts_tax_rates.PAL.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.parts_tax_rates.PAL.prt_tax_in = true;
}
}
//PAC Check
if (
!estData.parts_tax_rates?.PAC ||
estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
) {
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
);
if (res) {
if (!estData.parts_tax_rates.PAC) {
estData.parts_tax_rates.PAC = {
prt_discp: 0,
prt_mktyp: true,
prt_mkupp: 0,
prt_type: "PAC",
};
}
estData.parts_tax_rates.PAC.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.parts_tax_rates.PAC.prt_tax_in = true;
}
}
// //LKQ Check
// if (
// !estData.parts_tax_rates?.PAL ||
// estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
// estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
// ) {
// const res = await confirmDialog(
// `Rome Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
// );
// if (res) {
// if (!estData.parts_tax_rates.PAL) {
// estData.parts_tax_rates.PAL = {
// prt_discp: 0,
// prt_mktyp: true,
// prt_mkupp: 0,
// prt_type: "PAL",
// };
// }
// estData.parts_tax_rates.PAL.prt_tax_rt =
// bodyshop.bill_tax_rates.state_tax_rate / 100;
// estData.parts_tax_rates.PAL.prt_tax_in = true;
// }
// }
// //PAC Check
// if (
// !estData.parts_tax_rates?.PAC ||
// estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
// estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
// ) {
// const res = await confirmDialog(
// `Rome Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
// );
// if (res) {
// if (!estData.parts_tax_rates.PAC) {
// estData.parts_tax_rates.PAC = {
// prt_discp: 0,
// prt_mktyp: true,
// prt_mkupp: 0,
// prt_type: "PAC",
// };
// }
// estData.parts_tax_rates.PAC.prt_tax_rt =
// bodyshop.bill_tax_rates.state_tax_rate / 100;
// estData.parts_tax_rates.PAC.prt_tax_in = true;
// }
// }
//PAM Check
if (
!estData.parts_tax_rates?.PAM ||
estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
) {
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
);
if (res) {
if (!estData.parts_tax_rates.PAM) {
estData.parts_tax_rates.PAM = {
prt_discp: 0,
prt_mktyp: true,
prt_mkupp: 0,
prt_type: "PAM",
};
}
estData.parts_tax_rates.PAM.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.parts_tax_rates.PAM.prt_tax_in = true;
}
if (!estData.parts_tax_rates?.PAM) {
estData.parts_tax_rates.PAM = estData.parts_tax_rates.PAC;
}
if (
!estData.parts_tax_rates?.PAR ||
estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
) {
const res = await confirmDialog(
`ImEX Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
);
if (res) {
if (!estData.parts_tax_rates.PAR) {
estData.parts_tax_rates.PAR = {
prt_discp: 0,
prt_mktyp: true,
prt_mkupp: 0,
prt_type: "PAR",
};
}
estData.parts_tax_rates.PAR.prt_tax_rt =
bodyshop.bill_tax_rates.state_tax_rate / 100;
estData.parts_tax_rates.PAR.prt_tax_in = true;
}
}
// //PAM Check
// if (
// !estData.parts_tax_rates?.PAM ||
// estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
// estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
// ) {
// const res = await confirmDialog(
// `Rome Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
// );
// if (res) {
// if (!estData.parts_tax_rates.PAM) {
// estData.parts_tax_rates.PAM = {
// prt_discp: 0,
// prt_mktyp: true,
// prt_mkupp: 0,
// prt_type: "PAM",
// };
// }
// estData.parts_tax_rates.PAM.prt_tax_rt =
// bodyshop.bill_tax_rates.state_tax_rate / 100;
// estData.parts_tax_rates.PAM.prt_tax_in = true;
// }
// }
// if (
// !estData.parts_tax_rates?.PAR ||
// estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
// estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
// ) {
// const res = await confirmDialog(
// `Rome Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
// );
// if (res) {
// if (!estData.parts_tax_rates.PAR) {
// estData.parts_tax_rates.PAR = {
// prt_discp: 0,
// prt_mktyp: true,
// prt_mkupp: 0,
// prt_type: "PAR",
// };
// }
// estData.parts_tax_rates.PAR.prt_tax_rt =
// bodyshop.bill_tax_rates.state_tax_rate / 100;
// estData.parts_tax_rates.PAR.prt_tax_in = true;
// }
// }
//IO-1387 If a sublet line is NOT R&R, use the labor tax. If it is, use the sublet tax rate.
//Currently limited to SK shops only.
//if (bodyshop.region_config === "CA_SK") {
estData.joblines.data.forEach((jl, index) => {
if (
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
jl.lbr_op !== "OP11"
) {
estData.joblines.data[index].tax_part = jl.lbr_tax;
}
if (bodyshop.region_config === "CA_SK") {
estData.joblines.data.forEach((jl, index) => {
if (
(jl.part_type === "PASL" || jl.part_type === "PAS") &&
jl.lbr_op !== "OP11"
) {
estData.joblines.data[index].tax_part = jl.lbr_tax;
}
//Set markup lines and tax lines as taxable.
//900510 is a mark up. 900510 is a discount.
if (jl.db_ref === "900510") {
estData.joblines.data[index].tax_part = true;
//Set markup lines and tax lines as taxable.
//900510 is a mark up. 900510 is a discount.
if (jl.db_ref === "900510") {
estData.joblines.data[index].tax_part = true;
}
});
}
}
async function ResolveCCCLineIssues(estData, bodyshop) {
//Find all misc amounts, populate them to the act price.
//TODO Ensure that this doesnt get violated
//This needs to be done before cleansing unq_seq since some misc prices could move over.
estData.joblines.data.forEach((line) => {
if (line.misc_amt && line.misc_amt !== 0) {
line.act_price = line.act_price + line.misc_amt;
line.tax_part = !!line.misc_tax;
}
});
//}
//Generate the list of duplicated UNQ_SEQ that will feed into the next section to scrub the lines.
const unqSeqHash = _.groupBy(estData.joblines.data, "unq_seq");
const duplicatedUnqSeq = Object.keys(unqSeqHash).filter(
(key) => unqSeqHash[key].length > 1
);
duplicatedUnqSeq.forEach((unq_seq) => {
//Keys are strings, convert to int.
const int_unq_seq = parseInt(unq_seq);
//When line splitting, the first line is always the non-refinish line. We will keep it as is.
//We will cleanse the second line, which is always the next line.
const nonRefLineIndex = estData.joblines.data.findIndex(
(line) => line.unq_seq === int_unq_seq
);
estData.joblines.data[nonRefLineIndex + 1] = {
...estData.joblines.data[nonRefLineIndex + 1],
part_type: null,
act_price: 0,
db_price: 0,
prt_dsmk_p: 0,
prt_dsmk_m: 0,
};
});
}

View File

@@ -36,6 +36,8 @@ export function JobsCloseAutoAllocate({ bodyshop, joblines, form, disabled }) {
ret.profitcenter_part = defaults.profits["MAPA"];
} else if (lineDesc.includes("ats amount")) {
ret.profitcenter_part = defaults.profits["ATS"];
} else if (jl.act_price > 0) {
ret.profitcenter_part = defaults.profits["PAO"];
} else {
ret.profitcenter_part = null;
}

View File

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

View File

@@ -1,4 +1,4 @@
import { Collapse, Form, Input, InputNumber, Select, Switch } from "antd";
import { Collapse, Form, Input, Select, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -12,6 +12,12 @@ import FormItemPhone, {
} from "../form-items-formatted/phone-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
import JobsDetailRatesParts from "../jobs-detail-rates/jobs-detail-rates.parts.component";
import JobsDetailRatesLabor from "../jobs-detail-rates/jobs-detail-rates.labor.component";
import JobsDetailRatesMaterials from "../jobs-detail-rates/jobs-detail-rates.materials.component";
import JobsDetailRatesOther from "../jobs-detail-rates/jobs-detail-rates.other.component";
import JobsDetailRatesTaxes from "../jobs-detail-rates/jobs-detail-rates.taxes.component";
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
@@ -258,26 +264,28 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<CurrencyInput />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow>
<Form.Item
label={t("jobs.fields.federal_tax_rate")}
name="federal_tax_rate"
>
<InputNumber min={0} max={1} precision={2} />
</Form.Item>
<Form.Item
label={t("jobs.fields.state_tax_rate")}
name="state_tax_rate"
>
<InputNumber min={0} max={1} precision={2} />
</Form.Item>
<Form.Item
label={t("jobs.fields.local_tax_rate")}
name="local_tax_rate"
>
<InputNumber min={0} max={1} precision={2} />
</Form.Item>
</LayoutFormRow>
{
// <LayoutFormRow>
// <Form.Item
// label={t("jobs.fields.federal_tax_rate")}
// name="federal_tax_rate"
// >
// <InputNumber min={0} max={1} precision={2} />
// </Form.Item>
// <Form.Item
// label={t("jobs.fields.state_tax_rate")}
// name="state_tax_rate"
// >
// <InputNumber min={0} max={1} precision={2} />
// </Form.Item>
// <Form.Item
// label={t("jobs.fields.local_tax_rate")}
// name="local_tax_rate"
// >
// <InputNumber min={0} max={1} precision={2} />
// </Form.Item>
// </LayoutFormRow>
}
<LayoutFormRow>
<Form.Item label={t("jobs.fields.rate_lab")} name="rate_lab">
<CurrencyInput />
@@ -356,6 +364,10 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
required={selected && true}
form={form}
/>
<JobsDetailRatesLabor form={form} />
<JobsDetailRatesMaterials form={form} />
<JobsDetailRatesOther form={form} />
<JobsDetailRatesTaxes form={form} />
</div>
);
}

View File

@@ -145,13 +145,6 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
<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

@@ -18,14 +18,12 @@ 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";
@@ -50,10 +48,10 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setModalContext({ context: context, modal: "jobCosting" })),
setTimeTicketContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
setTimeTicketTaskContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicketTask" })),
setCardPaymentContext: (context) =>
dispatch(setModalContext({ context: context, modal: "cardPayment" })),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JobsDetailHeaderActions({
@@ -67,8 +65,8 @@ export function JobsDetailHeaderActions({
setJobCostingContext,
jobRO,
setTimeTicketContext,
setTimeTicketTaskContext,
setCardPaymentContext,
insertAuditTrail,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -163,7 +161,6 @@ export function JobsDetailHeaderActions({
scheduled_in: null,
scheduled_completion: null,
lost_sale_reason,
date_lost_sale: new Date(),
status: bodyshop.md_ro_statuses.default_imported,
},
},
@@ -172,11 +169,6 @@ export function JobsDetailHeaderActions({
notification["success"]({
message: t("appointments.successes.canceled"),
});
insertAuditTrail({
jobid: job.id,
operation:
AuditTrailMapping.appointmentcancel(lost_sale_reason),
});
return;
}
}}
@@ -268,6 +260,24 @@ export function JobsDetailHeaderActions({
>
{t("timetickets.actions.enter")}
</Menu.Item>
{bodyshop.md_tasks_presets.enable_tasks && (
<Menu.Item
key="claimtimetickettasks"
disabled={
!job.converted ||
(!bodyshop.tt_allow_post_to_invoiced && job.date_invoiced)
}
onClick={() => {
setTimeTicketTaskContext({
actions: {},
context: { jobid: job.id },
});
}}
>
{t("timetickets.actions.claimtasks")}
</Menu.Item>
)}
<Menu.Item
key="enterpayments"
disabled={!job.converted}

View File

@@ -27,8 +27,6 @@ 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,
@@ -83,8 +81,6 @@ 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

@@ -5,9 +5,13 @@ import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LaborAllocationsTableComponent from "../labor-allocations-table/labor-allocations-table.component";
import TimeTicketList from "../time-ticket-list/time-ticket-list.component";
import PayrollLaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.payroll.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
export default connect(mapStateToProps, null)(JobsDetailLaborContainer);
@@ -48,6 +52,7 @@ const adjSpan = {
};
export function JobsDetailLaborContainer({
bodyshop,
jobRO,
job,
jobId,
@@ -58,6 +63,12 @@ export function JobsDetailLaborContainer({
techConsole,
adjustments,
}) {
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
return (
<Row gutter={[16, 16]}>
<Col {...ticketSpan}>
@@ -70,14 +81,28 @@ export function JobsDetailLaborContainer({
jobId={jobId}
/>
</Col>
<Col {...adjSpan}>
<LaborAllocationsTableComponent
jobId={jobId}
joblines={joblines}
timetickets={timetickets}
adjustments={adjustments}
/>
</Col>
{Enhanced_Payroll.treatment === "on" ? (
<Col {...adjSpan}>
<PayrollLaborAllocationsTable
jobId={jobId}
joblines={joblines}
timetickets={timetickets}
refetch={refetch}
adjustments={adjustments}
/>
</Col>
) : (
<Col {...adjSpan}>
<LaborAllocationsTableComponent
jobId={jobId}
joblines={joblines}
timetickets={timetickets}
refetch={refetch}
adjustments={adjustments}
/>
</Col>
)}
</Row>
);
}

View File

@@ -6,12 +6,14 @@ import BillsListTable from "../bills-list-table/bills-list-table.component";
import JobBillsTotal from "../job-bills-total/job-bills-total.component";
import PartsOrderListTableComponent from "../parts-order-list-table/parts-order-list-table.component";
import PartsOrderModal from "../parts-order-modal/parts-order-modal.container";
import PartsDispatchTable from "../parts-dispatch-table/parts-dispatch-table.component";
export default function JobsDetailPliComponent({
job,
billsQuery,
handleBillOnRowClick,
handlePartsOrderOnRowClick,
handlePartsDispatchOnRowClick,
}) {
return (
<div>
@@ -43,6 +45,13 @@ export default function JobsDetailPliComponent({
billsQuery={billsQuery}
/>
</Col>
<Col span={24}>
<PartsDispatchTable
job={job}
handleOnRowClick={handlePartsDispatchOnRowClick}
billsQuery={billsQuery}
/>
</Col>
</Row>
</div>
);

View File

@@ -39,12 +39,24 @@ export default function JobsDetailPliContainer({ job }) {
}
};
const handlePartsDispatchOnRowClick = (record) => {
if (record) {
if (record.id) {
search.partsdispatchid = record.id;
history.push({ search: queryString.stringify(search) });
}
} else {
delete search.partsdispatchid;
history.push({ search: queryString.stringify(search) });
}
};
return (
<JobsDetailPliComponent
job={job}
billsQuery={billsQuery}
handleBillOnRowClick={handleBillOnRowClick}
handlePartsOrderOnRowClick={handlePartsOrderOnRowClick}
handlePartsDispatchOnRowClick={handlePartsDispatchOnRowClick}
/>
);
}

View File

@@ -1,25 +1,21 @@
import {
Divider,
Form,
Input,
InputNumber,
Select,
Space,
Switch,
Tooltip,
} from "antd";
import { Divider, Form, Input, Select, Space, Switch, Tooltip } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CABCpvrtCalculator from "../ca-bc-pvrt-calculator/ca-bc-pvrt-calculator.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobsDetailRatesChangeButton from "../jobs-detail-rates-change-button/jobs-detail-rates-change-button.component";
import JobsMarkPstExempt from "../jobs-mark-pst-exempt/jobs-mark-pst-exempt.component";
import FormRow from "../layout-form-row/layout-form-row.component";
import JobsDetailRatesLabor from "./jobs-detail-rates.labor.component";
import JobsDetailRatesMaterials from "./jobs-detail-rates.materials.component";
import JobsDetailRatesOther from "./jobs-detail-rates.other.component";
import JobsDetailRatesParts from "./jobs-detail-rates.parts.component";
import JobsDetailRatesTaxes from "./jobs-detail-rates.taxes.component";
import JobsDetailRatesProfileOVerride from "./jobs-detail-rates.profile-override.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
@@ -84,14 +80,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
>
<CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
</Form.Item>
{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"
@@ -120,41 +109,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
}}
</Form.Item>
</FormRow>
<FormRow>
<Form.Item
label={t("jobs.fields.federal_tax_rate")}
name="federal_tax_rate"
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.state_tax_rate")}
name="state_tax_rate"
>
<InputNumber
min={0}
max={1}
precision={2}
disabled={jobRO}
autoComplete="new-password"
/>
</Form.Item>
<Form.Item
label={t("jobs.fields.local_tax_rate")}
name="local_tax_rate"
>
<InputNumber min={0} max={1} precision={2} 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"
type="horizontal"
@@ -242,7 +197,15 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
</FormRow>
<Divider orientation="left">Tax Profile</Divider>
<JobsDetailRatesProfileOVerride form={form} />
<JobsDetailRatesParts form={form} />
<JobsDetailRatesLabor form={form} />
<JobsDetailRatesMaterials form={form} />
<JobsDetailRatesOther form={form} />
<JobsDetailRatesTaxes form={form} />
</div>
);
}

View File

@@ -0,0 +1,427 @@
import { Collapse, Form, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
});
export function JobsDetailRatesLabor({
jobRO,
expanded,
required = true,
form,
}) {
const { t } = useTranslation();
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel
forceRender
header={t("jobs.labels.cieca_pfl")}
key="cieca_pfl"
>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAB")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAB", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAB", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAB", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAB", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAB", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAB", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAD")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAD", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAD", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAD", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAD", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAD", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAD", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAE")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAE", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAE", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAE", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAE", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAE", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAE", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAF")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAF", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAF", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAF", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAF", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAF", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAF", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAG")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAG", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAG", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAG", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAG", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAG", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAG", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAM")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAM", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAM", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAM", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAM", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAM", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAM", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAR")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAR", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAR", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAR", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAR", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAR", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAR", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAS")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAS", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAS", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAS", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAS", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAS", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAS", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.lbr_types.LAU")}>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tax_in")}
name={["cieca_pfl", "LAU", "lbr_tax_in"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in1")}
name={["cieca_pfl", "LAU", "lbr_tx_in1"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in2")}
name={["cieca_pfl", "LAU", "lbr_tx_in2"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in3")}
name={["cieca_pfl", "LAU", "lbr_tx_in3"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in4")}
name={["cieca_pfl", "LAU", "lbr_tx_in4"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfl.lbr_tx_in5")}
name={["cieca_pfl", "LAU", "lbr_tx_in5"]}
valuePropName="checked"
>
<Switch disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>
</Collapse>
);
}
export default connect(mapStateToProps, null)(JobsDetailRatesLabor);

View File

@@ -0,0 +1,145 @@
import { Collapse, Form, Input, InputNumber, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
});
export function JobsDetailRatesMaterials({
jobRO,
expanded,
required = true,
form,
}) {
const { t } = useTranslation();
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel
forceRender
header={t("jobs.fields.materials.materials")}
key="materials"
>
<LayoutFormRow header={t("jobs.fields.materials.MAPA")}>
<Form.Item
label={t("jobs.fields.materials.cal_maxdlr")}
name={["materials", "MAPA", "cal_maxdlr"]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.cal_opcode")}
name={["materials", "MAPA", "cal_opcode"]}
>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.tax_ind")}
name={["materials", "MAPA", "tax_ind"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in1")}
name={["materials", "MAPA", "mat_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in2")}
name={["materials", "MAPA", "mat_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in3")}
name={["materials", "MAPA", "mat_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in4")}
name={["materials", "MAPA", "mat_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in5")}
name={["materials", "MAPA", "mat_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("jobs.fields.materials.MASH")}>
<Form.Item
label={t("jobs.fields.materials.cal_maxdlr")}
name={["materials", "MASH", "cal_maxdlr"]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.cal_opcode")}
name={["materials", "MASH", "cal_opcode"]}
>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.tax_ind")}
name={["materials", "MASH", "tax_ind"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in1")}
name={["materials", "MASH", "mat_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in2")}
name={["materials", "MASH", "mat_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in3")}
name={["materials", "MASH", "mat_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in4")}
name={["materials", "MASH", "mat_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.materials.mat_tx_in5")}
name={["materials", "MASH", "mat_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>
</Collapse>
);
}
export default connect(mapStateToProps, null)(JobsDetailRatesMaterials);

View File

@@ -0,0 +1,104 @@
import { Collapse, Form, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
});
export function JobsDetailRatesOther({
jobRO,
expanded,
required = true,
form,
}) {
const { t } = useTranslation();
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel
forceRender
header={t("jobs.labels.cieca_pfo")}
key="cieca_pfo"
>
<LayoutFormRow noDivider>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in1")}
name={["cieca_pfo", "tow_t_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in2")}
name={["cieca_pfo", "tow_t_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in3")}
name={["cieca_pfo", "tow_t_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in4")}
name={["cieca_pfo", "tow_t_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.tow_t_in5")}
name={["cieca_pfo", "tow_t_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in1")}
name={["cieca_pfo", "stor_t_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in2")}
name={["cieca_pfo", "stor_t_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in3")}
name={["cieca_pfo", "stor_t_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in4")}
name={["cieca_pfo", "stor_t_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.cieca_pfo.stor_t_in5")}
name={["cieca_pfo", "stor_t_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>
</Collapse>
);
}
export default connect(mapStateToProps, null)(JobsDetailRatesOther);

View File

@@ -29,7 +29,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAA", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -42,7 +42,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAA", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -68,18 +68,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAA", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAA", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAA", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAA", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAA", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAC")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAC", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -92,7 +132,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAC", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -118,18 +158,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAC", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAC", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAC", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAC", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAC", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAL")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAL", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -142,7 +222,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAL", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -168,18 +248,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAL", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAL", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAL", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAL", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAL", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAG")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAG", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -192,7 +312,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAG", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -218,18 +338,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAG", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAG", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAG", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAG", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAG", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAM")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAM", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -242,7 +402,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAM", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -268,18 +428,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAM", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAM", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAM", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAM", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAM", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAN")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAN", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -292,7 +492,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAN", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -318,18 +518,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAN", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAN", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAN", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAN", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAN", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAO")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAO", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -342,7 +582,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAO", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -368,18 +608,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAO", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAO", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAO", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAO", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAO", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAP")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAP", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -392,7 +672,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAP", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -418,18 +698,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAP", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAP", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAP", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAP", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAP", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAR")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAR", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -442,7 +762,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAR", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -468,18 +788,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAR", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAR", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAR", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAR", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAR", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PAS")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PAS", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -492,7 +852,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PAS", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -518,18 +878,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PAS", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PAS", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PAS", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PAS", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PAS", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.PASL")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "PASL", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -542,7 +942,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "PASL", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -568,18 +968,58 @@ export function JobsDetailRatesParts({
},
]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber
min={0}
max={100}
precision={4}
disabled={jobRO}
/>
</Form.Item>
);
}}
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in1")}
name={["parts_tax_rates", "PASL", "prt_tx_in1"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in2")}
name={["parts_tax_rates", "PASL", "prt_tx_in2"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in3")}
name={["parts_tax_rates", "PASL", "prt_tx_in3"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in4")}
name={["parts_tax_rates", "PASL", "prt_tx_in4"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tx_in5")}
name={["parts_tax_rates", "PASL", "prt_tx_in5"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCDR")}>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCDR", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -592,7 +1032,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCDR", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -605,7 +1045,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCDR", "prt_tax_rt"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCF")}>
@@ -613,7 +1053,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCF", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -626,7 +1066,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCF", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -639,7 +1079,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCF", "prt_tax_rt"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCM")}>
@@ -647,7 +1087,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCM", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -660,7 +1100,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCM", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -673,7 +1113,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCM", "prt_tax_rt"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCC")}>
@@ -681,7 +1121,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCC", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -694,7 +1134,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCC", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -707,7 +1147,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCC", "prt_tax_rt"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("joblines.fields.part_types.CCD")}>
@@ -715,7 +1155,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_discp")}
name={["parts_tax_rates", "CCD", "prt_discp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_mktyp")}
@@ -728,7 +1168,7 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_mkupp")}
name={["parts_tax_rates", "CCD", "prt_mkupp"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.parts_tax_rates.prt_tax_in")}
@@ -741,39 +1181,39 @@ export function JobsDetailRatesParts({
label={t("jobs.fields.parts_tax_rates.prt_tax_rt")}
name={["parts_tax_rates", "CCD", "prt_tax_rt"]}
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow>
<Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt">
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_str_rt")} name="tax_str_rt">
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.tax_paint_mat_rt")}
name="tax_paint_mat_rt"
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.tax_shop_mat_rt")}
name="tax_shop_mat_rt"
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_sub_rt")} name="tax_sub_rt">
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.tax_lbr_rt")} name="tax_lbr_rt">
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.tax_levies_rt")}
name="tax_levies_rt"
>
<InputNumber min={0} max={1} precision={2} disabled={jobRO} />
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>

View File

@@ -0,0 +1,42 @@
import { Button, Popconfirm } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDetailRatesProfileOVerride);
export function JobsDetailRatesProfileOVerride({ bodyshop, form }) {
const { t } = useTranslation();
return (
<Popconfirm
onConfirm={() => {
form.setFieldsValue({
cieca_pft: {
...bodyshop.md_responsibility_centers.taxes.tax_ty1,
...bodyshop.md_responsibility_centers.taxes.tax_ty2,
...bodyshop.md_responsibility_centers.taxes.tax_ty3,
...bodyshop.md_responsibility_centers.taxes.tax_ty4,
...bodyshop.md_responsibility_centers.taxes.tax_ty5,
},
materials: bodyshop.md_responsibility_centers.cieca_pfm,
cieca_pfl: bodyshop.md_responsibility_centers.cieca_pfl,
parts_tax_rates: bodyshop.md_responsibility_centers.parts_tax_rates,
});
}}
title={t("jobs.actions.taxprofileoverride_confirm")}
>
<Button type="link">{t("jobs.actions.taxprofileoverride")}</Button>
</Popconfirm>
);
}

View File

@@ -0,0 +1,155 @@
import { Collapse, Divider, Form, Input, InputNumber, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
export function JobsDetailRatesTaxes({
jobRO,
expanded,
bodyshop,
required = true,
form,
}) {
const { t } = useTranslation();
const formItems = [];
for (let tyCounter = 1; tyCounter <= 5; tyCounter++) {
const section = [];
section.push(
TaxFormItems({
typeNum: tyCounter,
rootElements: true,
bodyshop,
jobRO,
})
);
for (let iterator = 1; iterator <= 5; iterator++) {
section.push(
TaxFormItems({
typeNum: tyCounter,
typeNumIterator: iterator,
rootElements: false,
jobRO,
})
);
}
formItems.push(Space({ children: section, wrap: true }));
formItems.push(<Divider />);
}
return (
<Collapse defaultActiveKey={expanded && "rates"}>
<Collapse.Panel
forceRender
header={t("jobs.labels.cieca_pft")}
key="cieca_pft"
>
{formItems}
</Collapse.Panel>
</Collapse>
);
}
export default connect(mapStateToProps, null)(JobsDetailRatesTaxes);
function TaxFormItems({
typeNum,
typeNumIterator,
rootElements,
bodyshopjobRO,
jobRO,
}) {
const { t } = useTranslation();
if (rootElements)
return (
<>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_type", {
typeNum,
typeNumIterator,
})}
// rules={[
// {
// required: true,
// //message: t("general.validation.required"),
// },
// ]}
name={["cieca_pft", `tax_type${typeNum}`]}
>
<Input disabled={jobRO} />
</Form.Item>
</>
);
return (
<>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_tier", {
typeNum,
typeNumIterator,
})}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cieca_pft", `ty${typeNum}_tier${typeNumIterator}`]}
>
<InputNumber precision={0} min={0} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_thres", {
typeNum,
typeNumIterator,
})}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cieca_pft", `ty${typeNum}_thres${typeNumIterator}`]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_rate", {
typeNum,
typeNumIterator,
})}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cieca_pft", `ty${typeNum}_rate${typeNumIterator}`]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_tax_sur", {
typeNum,
typeNumIterator,
})}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["cieca_pft", `ty${typeNum}_sur${typeNumIterator}`]}
>
<InputNumber min={0} precision={2} disabled={jobRO} />
</Form.Item>
</>
);
}

View File

@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility";
import { 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({
fullsize: GenerateSrcUrl(value),
//src: 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, fullsize: val.src });
acc.push({ ...val, src: val.thumbnail });
}
return acc;
}, [])

View File

@@ -14,8 +14,6 @@ export default function JobsFindModalComponent({
importOptionsState,
modalSearchState,
jobsListRefetch,
partsQueueToggle,
setPartsQueueToggle,
}) {
const { t } = useTranslation();
const [modalSearch, setModalSearch] = modalSearchState;
@@ -201,12 +199,6 @@ 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,8 +24,6 @@ export default connect(
setSelectedJob,
importOptionsState,
modalSearchState,
partsQueueToggle,
setPartsQueueToggle,
...modalProps
}) {
const { t } = useTranslation();
@@ -93,8 +91,6 @@ export default connect(
jobsListRefetch={jobsList.refetch}
jobsList={jobsData}
modalSearchState={modalSearchState}
partsQueueToggle={partsQueueToggle}
setPartsQueueToggle={setPartsQueueToggle}
/>
) : null}
</Modal>

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 CurrencyFormatter from "../../utils/CurrencyFormatter";
import { onlyUnique } from "../../utils/arrayHelper";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters";
import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
@@ -46,7 +46,6 @@ 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

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

View File

@@ -0,0 +1,333 @@
import {
Button,
Card,
Col,
Row,
Space,
Table,
Typography,
notification,
} from "antd";
import { SyncOutlined } from "@ant-design/icons";
import axios from "axios";
import _ from "lodash";
import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import "./labor-allocations-table.styles.scss";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
technician: selectTechnician,
});
export function PayrollLaborAllocationsTable({
jobId,
joblines,
timetickets,
bodyshop,
adjustments,
technician,
refetch,
}) {
const { t } = useTranslation();
const [totals, setTotals] = useState([]);
const [state, setState] = useState({
sortedInfo: {
columnKey: "cost_center",
field: "cost_center",
order: "ascend",
},
filteredInfo: {},
});
useEffect(() => {
async function CalculateTotals() {
const { data } = await axios.post("/payroll/calculatelabor", {
jobid: jobId,
});
setTotals(data);
}
if (!!joblines && !!timetickets && !!bodyshop) {
CalculateTotals();
}
if (!jobId) setTotals([]);
}, [joblines, timetickets, bodyshop, adjustments, jobId]);
const convertedLines = useMemo(
() => joblines && joblines.filter((j) => j.convertedtolbr),
[joblines]
);
const columns = [
{
title: t("timetickets.fields.employee"),
dataIndex: "employeeid",
key: "employeeid",
render: (text, record) => {
if (record.employeeid === undefined) {
return (
<span style={{ color: "tomato", fontWeight: "bolder" }}>
{t("timetickets.labels.unassigned")}
</span>
);
}
const emp = bodyshop.employees.find((e) => e.id === record.employeeid);
return `${emp?.first_name} ${emp?.last_name}`;
},
},
{
title: t("joblines.fields.mod_lbr_ty"),
dataIndex: "mod_lbr_ty",
key: "mod_lbr_ty",
render: (text, record) =>
record.employeeid === undefined ? (
<span style={{ color: "tomato", fontWeight: "bolder" }}>
{t("timetickets.labels.unassigned")}
</span>
) : (
t(`joblines.fields.lbr_types.${record.mod_lbr_ty?.toUpperCase()}`)
),
},
// {
// title: t("timetickets.fields.rate"),
// dataIndex: "rate",
// key: "rate",
// },
{
title: t("jobs.labels.hrs_total"),
dataIndex: "expectedHours",
key: "expectedHours",
sorter: (a, b) => a.expectedHours - b.expectedHours,
sortOrder:
state.sortedInfo.columnKey === "expectedHours" &&
state.sortedInfo.order,
render: (text, record) => record.expectedHours.toFixed(5),
},
{
title: t("jobs.labels.hrs_claimed"),
dataIndex: "claimedHours",
key: "claimedHours",
sorter: (a, b) => a.claimedHours - b.claimedHours,
sortOrder:
state.sortedInfo.columnKey === "claimedHours" && state.sortedInfo.order,
render: (text, record) =>
record.claimedHours && record.claimedHours.toFixed(5),
},
{
title: t("jobs.labels.difference"),
dataIndex: "difference",
key: "difference",
sorter: (a, b) => a.difference - b.difference,
sortOrder:
state.sortedInfo.columnKey === "difference" && state.sortedInfo.order,
render: (text, record) => {
const difference = _.round(
record.expectedHours - record.claimedHours,
5
);
return (
<strong
style={{
color: difference >= 0 ? "green" : "red",
}}
>
{difference}
</strong>
);
},
},
];
const convertedTableCols = [
{
title: t("joblines.fields.line_desc"),
dataIndex: "line_desc",
key: "line_desc",
ellipsis: true,
},
{
title: t("joblines.fields.op_code_desc"),
dataIndex: "op_code_desc",
key: "op_code_desc",
ellipsis: true,
render: (text, record) =>
`${record.op_code_desc || ""}${
record.alt_partm ? ` ${record.alt_partm}` : ""
}`,
},
{
title: t("joblines.fields.act_price"),
dataIndex: "act_price",
key: "act_price",
ellipsis: true,
render: (text, record) => (
<>
<CurrencyFormatter>
{record.db_ref === "900510" || record.db_ref === "900511"
? record.prt_dsmk_m
: record.act_price}
</CurrencyFormatter>
{record.prt_dsmk_p && record.prt_dsmk_p !== 0 ? (
<span
style={{ marginLeft: ".2rem" }}
>{`(${record.prt_dsmk_p}%)`}</span>
) : (
<></>
)}
</>
),
},
{
title: t("joblines.fields.part_qty"),
dataIndex: "part_qty",
key: "part_qty",
},
{
title: t("joblines.fields.mod_lbr_ty"),
dataIndex: "conv_mod_lbr_ty",
key: "conv_mod_lbr_ty",
render: (text, record) =>
record.convertedtolbr_data && record.convertedtolbr_data.mod_lbr_ty,
},
{
title: t("joblines.fields.mod_lb_hrs"),
dataIndex: "conv_mod_lb_hrs",
key: "conv_mod_lb_hrs",
render: (text, record) =>
record.convertedtolbr_data &&
record.convertedtolbr_data.mod_lb_hrs &&
record.convertedtolbr_data.mod_lb_hrs.toFixed(5),
},
];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const summary =
totals &&
totals.reduce(
(acc, val) => {
acc.hrs_total += val.expectedHours;
acc.hrs_claimed += val.claimedHours;
// acc.adjustments += val.adjustments;
acc.difference += val.expectedHours - val.claimedHours;
return acc;
},
{ hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 }
);
return (
<Row gutter={[16, 16]}>
<Col span={24}>
<Card
title={t("jobs.labels.laborallocations")}
extra={
<Space>
<Button
onClick={async () => {
const response = await axios.post("/payroll/payall", {
jobid: jobId,
});
if (response.status === 200) {
if (response.data.success !== false) {
notification.open({
type: "success",
message: t("timetickets.successes.payall"),
});
} else {
notification.open({
type: "error",
message: t("timetickets.errors.payall", {
error: response.data.error,
}),
});
}
if (refetch) refetch();
} else {
notification.open({
type: "error",
message: t("timetickets.errors.payall", {
error: JSON.stringify(""),
}),
});
}
}}
>
{t("timetickets.actions.payall")}
</Button>
<Button
onClick={async () => {
const { data } = await axios.post("/payroll/calculatelabor", {
jobid: jobId,
});
setTotals(data);
refetch();
}}
>
<SyncOutlined />
</Button>
</Space>
}
>
<Table
columns={columns}
rowKey={(record) => `${record.employeeid} ${record.mod_lbr_ty}`}
pagination={false}
onChange={handleTableChange}
dataSource={totals}
scroll={{
x: true,
}}
summary={() => (
<Table.Summary.Row>
<Table.Summary.Cell>
<Typography.Title level={4}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell>
<Table.Summary.Cell></Table.Summary.Cell>
<Table.Summary.Cell>
{summary.hrs_total.toFixed(5)}
</Table.Summary.Cell>
<Table.Summary.Cell>
{summary.hrs_claimed.toFixed(5)}
</Table.Summary.Cell>
<Table.Summary.Cell>
{summary.difference.toFixed(5)}
</Table.Summary.Cell>
</Table.Summary.Row>
)}
/>
</Card>
</Col>
{convertedLines && convertedLines.length > 0 && (
<Col span={24}>
<Card title={t("jobs.labels.convertedtolabor")}>
<Table
columns={convertedTableCols}
rowKey="id"
pagination={false}
dataSource={convertedLines}
scroll={{
x: true,
}}
/>
</Card>
</Col>
)}
</Row>
);
}
export default connect(mapStateToProps, null)(PayrollLaborAllocationsTable);

View File

@@ -8,11 +8,10 @@ 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"),
@@ -110,12 +109,6 @@ 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,8 +14,6 @@ export default function OwnerFindModalContainer({
owner,
selectedOwner,
setSelectedOwner,
partsQueueToggle,
setPartsQueueToggle,
...modalProps
}) {
//use owner object to run query and find what possible owners there are.
@@ -61,8 +59,6 @@ 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,8 +21,7 @@ const OwnerSearchSelect = ({ value, onChange, onBlur, disabled }, ref) => {
useLazyQuery(SEARCH_OWNERS_BY_ID_FOR_AUTOCOMPLETE);
const executeSearch = (v) => {
if (v && v.variables?.search !== "" && v.variables.search.length >= 2)
callSearch(v);
callSearch(v);
};
const debouncedExecuteSearch = _.debounce(executeSearch, 500);

View File

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

View File

@@ -0,0 +1,81 @@
import { useMutation } from "@apollo/client";
import { Button, Card, Col, Row, Table, notification } from "antd";
import moment from "moment-business-days";
import React from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_PARTS_DISPATCH_LINE } from "../../graphql/parts-dispatch.queries";
import { DateTimeFormatter } from "../../utils/DateFormatter";
export default function PartsDispatchExpander({ dispatch, job }) {
const { t } = useTranslation();
const [updateDispatchLine] = useMutation(UPDATE_PARTS_DISPATCH_LINE);
const handleAccept = async ({ partsDispatchLineId }) => {
const accepted_at = moment();
const result = await updateDispatchLine({
variables: { id: partsDispatchLineId, line: { accepted_at } },
optimisticResponse: {
update_parts_dispatch_lines_by_pk: {
accepted_at,
id: partsDispatchLineId,
},
},
});
if (result.errors) {
notification.open({
type: "error",
message: t("parts_dispatch.errors.accepting", {
error: JSON.stringify(result.errors),
}),
});
}
};
const columns = [
{
title: t("joblines.fields.part_qty"),
dataIndex: "quantity",
key: "quantity",
width: "10%",
//sorter: (a, b) => alphaSort(a.number, b.number),
},
{
title: t("joblines.fields.line_desc"),
dataIndex: "joblineid",
key: "joblineid",
//sorter: (a, b) => alphaSort(a.number, b.number),
render: (text, record) => record.jobline.line_desc,
},
{
title: t("parts_dispatch_lines.fields.accepted_at"),
dataIndex: "accepted_at",
key: "accepted_at",
width: "20%",
//sorter: (a, b) => alphaSort(a.number, b.number),
render: (text, record) =>
record.accepted_at ? (
<DateTimeFormatter>{record.accepted_at}</DateTimeFormatter>
) : (
<Button
onClick={() => handleAccept({ partsDispatchLineId: record.id })}
>
{t("parts_dispatch.actions.accept")}
</Button>
),
},
];
return (
<Card>
<Row gutter={[16, 16]}>
<Col span={24}>
<Table
rowKey={"id"}
dataSource={dispatch.parts_dispatch_lines}
columns={columns}
/>
</Col>
</Row>
</Card>
);
}

View File

@@ -0,0 +1,155 @@
import {
MinusCircleTwoTone,
PlusCircleTwoTone,
SyncOutlined,
} from "@ant-design/icons";
import { Button, Card, Input, Space, Table } from "antd";
import React, { 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 { selectBodyshop } from "../../redux/user/user.selectors";
import { TemplateList } from "../../utils/TemplateConstants";
import { alphaSort } from "../../utils/sorters";
import PartsDispatchExpander from "../parts-dispatch-expander/parts-dispatch-expander.component";
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({});
export function PartDispatchTableComponent({
bodyshop,
jobRO,
job,
billsQuery,
handleOnRowClick,
}) {
const { t } = useTranslation();
const [state, setState] = useState({
sortedInfo: {},
});
// const search = queryString.parse(useLocation().search);
// const selectedBill = search.billid;
const [searchText, setSearchText] = useState("");
const Templates = TemplateList("job_special");
const { refetch } = billsQuery;
const recordActions = (record) => (
<Space wrap>
<PrintWrapperComponent
templateObject={{
name: Templates.parts_dispatch.key,
variables: { id: record.id },
}}
/>
</Space>
);
const columns = [
{
title: t("parts_dispatch.fields.number"),
dataIndex: "number",
key: "number",
sorter: (a, b) => alphaSort(a.number, b.number),
width: "10%",
sortOrder:
state.sortedInfo.columnKey === "number" && state.sortedInfo.order,
},
{
title: t("timetickets.fields.employee"),
dataIndex: "employeeid",
key: "employeeid",
sorter: (a, b) => alphaSort(a.employeeid, b.employeeid),
sortOrder:
state.sortedInfo.columnKey === "employeeid" && state.sortedInfo.order,
render: (text, record) => {
const e = bodyshop.employees.find((e) => e.id === record.employeeid);
return `${e?.first_name || ""} ${e?.last_name || ""}`.trim();
},
},
{
title: t("parts_dispatch.fields.percent_accepted"),
dataIndex: "percent_accepted",
key: "percent_accepted",
render: (text, record) =>
record.parts_dispatch_lines.length > 0
? `
${(
(record.parts_dispatch_lines.filter((l) => l.accepted_at)
.length /
record.parts_dispatch_lines.length) *
100
).toFixed(0)}%`
: "0%",
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
width: "10%",
render: (text, record) => recordActions(record, true),
},
];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
return (
<Card
title={t("parts_dispatch.labels.parts_dispatch")}
extra={
<Space wrap>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={t("general.labels.search")}
value={searchText}
onChange={(e) => {
e.preventDefault();
setSearchText(e.target.value);
}}
/>
</Space>
}
>
<Table
loading={billsQuery.loading}
scroll={{
x: true, // y: "50rem"
}}
expandable={{
expandedRowRender: (record) => (
<PartsDispatchExpander dispatch={record} job={job} />
),
rowExpandable: (record) => true,
expandIcon: ({ expanded, onExpand, record }) =>
expanded ? (
<MinusCircleTwoTone onClick={(e) => onExpand(record, e)} />
) : (
<PlusCircleTwoTone onClick={(e) => onExpand(record, e)} />
),
}}
columns={columns}
rowKey="id"
dataSource={billsQuery.data ? billsQuery.data.parts_dispatch : []}
onChange={handleTableChange}
/>
</Card>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(PartDispatchTableComponent);

View File

@@ -1,3 +1,4 @@
import { useTreatments } from "@splitsoftware/splitio-react";
import { Card, Col, Input, Row, Space, Typography } from "antd";
import _ from "lodash";
import React, { useState } from "react";
@@ -23,8 +24,13 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
const { id: jobId, job } = printCenterModal.context;
const tempList = TemplateList("job", {});
const { t } = useTranslation();
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const JobsReportsList =
const Templates =
bodyshop.cdk_dealerid === null && bodyshop.pbs_serialnumber === null
? Object.keys(tempList)
.map((key) => {
@@ -51,7 +57,26 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
bodyshop.region_config.includes(Object.keys(temp.regions)) ===
true)
);
const JobsReportsList =
Enhanced_Payroll.treatment === "on"
? Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === true
)
: Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === false
);
const filteredJobsReportsList =
search !== ""
? JobsReportsList.filter((r) =>

View File

@@ -9,9 +9,11 @@ export default function PrintWrapperComponent({
children,
id,
emailOnly = false,
disabled,
}) {
const [loading, setLoading] = useState(false);
const handlePrint = async (type) => {
if (disabled) return;
setLoading(true);
await GenerateDocument(templateObject, messageObject, type, id);
setLoading(false);
@@ -20,8 +22,18 @@ export default function PrintWrapperComponent({
return (
<Space>
{children || null}
{!emailOnly && <PrinterFilled onClick={() => handlePrint("p")} />}
<MailFilled onClick={() => handlePrint("e")} />
{!emailOnly && (
<PrinterFilled
disabled={disabled}
onClick={() => handlePrint("p")}
style={{ cursor: disabled ? "not-allowed" : null }}
/>
)}
<MailFilled
disabled={disabled}
onClick={() => handlePrint("e")}
style={{ cursor: disabled ? "not-allowed" : null }}
/>
{loading && <Spin />}
</Space>
);

View File

@@ -20,9 +20,9 @@ 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";
import CardColorLegend from "../production-board-kanban-card/production-board-kanban-card-color-legend.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
technician: selectTechnician,
@@ -153,18 +153,6 @@ 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];
@@ -248,14 +236,6 @@ 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}

View File

@@ -24,6 +24,8 @@ import ProductionListColumnNote from "./production-list-columns.productionnote.c
import ProductionListColumnCategory from "./production-list-columns.status.category";
import ProductionListColumnStatus from "./production-list-columns.status.component";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
import { store } from "../../redux/store";
import { setModalContext } from "../../redux/modals/modals.actions";
const r = ({ technician, state, activeStatuses, bodyshop }) => {
return [
@@ -38,6 +40,29 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
</Link>
),
},
{
title: i18n.t("timetickets.actions.claimtasks"),
dataIndex: "claimtasks",
key: "claimtasks",
ellipsis: true,
render: (text, record) => (
<div
onClick={() => {
store.dispatch(
setModalContext({
context: {
actions: {},
context: { jobid: record.id },
},
modal: "timeTicketTask",
})
);
}}
>
{i18n.t("timetickets.actions.claimtasks")}
</div>
),
},
{
title: i18n.t("jobs.fields.ro_number"),
dataIndex: "ro_number",
@@ -455,8 +480,8 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.order,
sorter: (a, b) =>
alphaSort(
bodyshop.employees?.find((e) => e.id === a.employee_body)?.first_name,
bodyshop.employees?.find((e) => e.id === b.employee_body)?.first_name
bodyshop.employees.find((e) => e.id === a.employee_body)?.first_name,
bodyshop.employees.find((e) => e.id === b.employee_body)?.first_name
),
render: (text, record) => (
<ProductionListEmployeeAssignment
@@ -474,8 +499,8 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.order,
sorter: (a, b) =>
alphaSort(
bodyshop.employees?.find((e) => e.id === a.employee_prep)?.first_name,
bodyshop.employees?.find((e) => e.id === b.employee_prep)?.first_name
bodyshop.employees.find((e) => e.id === a.employee_prep)?.first_name,
bodyshop.employees.find((e) => e.id === b.employee_prep)?.first_name
),
render: (text, record) => (
<ProductionListEmployeeAssignment
@@ -492,8 +517,8 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.columnKey === "employee_csr" && state.sortedInfo.order,
sorter: (a, b) =>
alphaSort(
bodyshop.employees?.find((e) => e.id === a.employee_csr)?.first_name,
bodyshop.employees?.find((e) => e.id === b.employee_csr)?.first_name
bodyshop.employees.find((e) => e.id === a.employee_csr)?.first_name,
bodyshop.employees.find((e) => e.id === b.employee_csr)?.first_name
),
render: (text, record) => (
<ProductionListEmployeeAssignment record={record} type="employee_csr" />
@@ -508,9 +533,9 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
state.sortedInfo.order,
sorter: (a, b) =>
alphaSort(
bodyshop.employees?.find((e) => e.id === a.employee_refinish)
bodyshop.employees.find((e) => e.id === a.employee_refinish)
?.first_name,
bodyshop.employees?.find((e) => e.id === b.employee_refinish)
bodyshop.employees.find((e) => e.id === b.employee_refinish)
?.first_name
),
render: (text, record) => (

View File

@@ -95,7 +95,7 @@ export function ProductionListDetail({
/>
}
placement="right"
width={"33%"}
width={"50%"}
onClose={handleClose}
visible={selected}
>

View File

@@ -55,27 +55,25 @@ export function ProductionListPrint({ bodyshop }) {
<Menu.SubMenu
title={t("reportcenter.templates.production_by_technician_one")}
>
{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>
))}
{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>
))}
</Menu.SubMenu>
<Menu.SubMenu
title={t("reportcenter.templates.production_by_category_one")}

View File

@@ -184,18 +184,6 @@ 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
@@ -205,14 +193,6 @@ 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}

View File

@@ -1,4 +1,5 @@
import { useLazyQuery } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import {
Button,
Card,
@@ -19,6 +20,7 @@ import { createStructuredSelector } from "reselect";
import { QUERY_ACTIVE_EMPLOYEES } from "../../graphql/employees.queries";
import { QUERY_ALL_VENDORS } from "../../graphql/vendors.queries";
import { selectReportCenter } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import DatePIckerRanges from "../../utils/DatePickerRanges";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
@@ -27,6 +29,7 @@ import VendorSearchSelect from "../vendor-search-select/vendor-search-select.com
import "./report-center-modal.styles.scss";
const mapStateToProps = createStructuredSelector({
reportCenterModal: selectReportCenter,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -36,16 +39,38 @@ export default connect(
mapDispatchToProps
)(ReportCenterModalComponent);
export function ReportCenterModalComponent({ reportCenterModal }) {
export function ReportCenterModalComponent({ reportCenterModal, bodyshop }) {
const [form] = Form.useForm();
const [search, setSearch] = useState("");
const { Enhanced_Payroll } = useTreatments(
["Enhanced_Payroll"],
{},
bodyshop.imexshopid
);
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const Templates = TemplateList("report_center");
const ReportsList = Object.keys(Templates).map((key) => {
return Templates[key];
});
const ReportsList =
Enhanced_Payroll.treatment === "on"
? Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === true
)
: Object.keys(Templates)
.map((key) => {
return Templates[key];
})
.filter(
(temp) =>
temp.enhanced_payroll === undefined ||
temp.enhanced_payroll === false
);
const { visible } = reportCenterModal;
const [callVendorQuery, { data: vendorData, called: vendorCalled }] =

View File

@@ -13,29 +13,20 @@ 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,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { DateTimeFormat } from "../../utils/DateFormatter";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { TemplateList } from "../../utils/TemplateConstants";
import ScheduleJobModalComponent from "./schedule-job-modal.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
scheduleModal: selectSchedule,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("schedule")),
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export function ScheduleJobModalContainer({
@@ -43,8 +34,6 @@ export function ScheduleJobModalContainer({
bodyshop,
toggleModalVisible,
setEmailOptions,
currentUser,
insertAuditTrail,
}) {
const { visible, context, actions } = scheduleModal;
const { jobId, job, previousEvent } = context;
@@ -133,22 +122,12 @@ 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", {
@@ -170,7 +149,6 @@ export function ScheduleJobModalContainer({
scheduled_in: values.start,
scheduled_completion: values.scheduled_completion,
lost_sale_reason: null,
date_lost_sale: null,
},
},
});

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