Compare commits

..

288 Commits

Author SHA1 Message Date
Patrick Fic
f9a5e8485b Revert "Release/2022 11 25 (pull request #628)" 2022-11-22 01:31:09 +00:00
Patrick Fic
0fdb663d50 Merged in release/2022-11-25 (pull request #628)
Release/2022 11 25
2022-11-21 23:52:30 +00:00
Patrick Fic
d528a4b730 Resynced lock files. 2022-11-21 15:50:34 -08:00
Patrick Fic
e4692c2965 Remove uneeded package. 2022-11-21 15:00:48 -08:00
Patrick Fic
a600bd446c Merged in release/2022-11-10 (pull request #625)
Release/2022 11 10
2022-11-10 20:04:14 +00:00
Patrick Fic
9f28b80a5a IO-1532 Status Transition Tracking. 2022-11-10 09:42:03 -08:00
Patrick Fic
77e865c5c4 CI Test 2022-11-09 16:34:30 -08:00
Patrick Fic
d01a1aa0a0 CI Test 2022-11-09 16:29:32 -08:00
Patrick Fic
77bfa90b59 IO-2062 Update handling of negative EFT amounts for ICBC. 2022-11-09 09:28:56 -08:00
Patrick Fic
b8d3c5e36c IO-2097 Update query on job card details to include remove = true filter. 2022-11-09 09:18:20 -08:00
Patrick Fic
abac60a316 IO-2100 Update label for updated date range picker. 2022-11-09 08:59:51 -08:00
Patrick Fic
83626d83de IO-2100 Add last 3 months to date range picker. 2022-11-09 08:16:44 -08:00
Patrick Fic
928673691b Updated header to remove language selector. 2022-11-08 14:22:03 -08:00
Patrick Fic
818e0679d9 Update AP posting form validation. 2022-11-08 13:36:54 -08:00
Patrick Fic
18f56a93d0 Merged in circleci-editor/847/test (pull request #620)
Add .circleci/config.yml
2022-11-08 19:08:56 +00:00
Patrick Fic
d4a6c528d0 Add .circleci/config.yml 2022-11-08 19:06:22 +00:00
Patrick Fic
6410f868db Merged in release/2022-11-10 (pull request #619)
release/2022-11-10

Approved-by: Patrick Fic
2022-11-08 18:51:55 +00:00
Patrick Fic
058cd31784 CI file updates. 2022-11-08 10:51:20 -08:00
Patrick Fic
9a04c63f72 Merged in release/2022-11-10 (pull request #618)
release/2022-11-10

Approved-by: Patrick Fic
2022-11-08 18:35:58 +00:00
Patrick Fic
3fa38822f3 IO-2096 Fix grouping for CT report. 2022-11-08 10:35:26 -08:00
Patrick Fic
8d84ed8e00 Updated CI 2022-11-08 10:13:04 -08:00
Patrick Fic
60fa40f738 Merged in release/2022-11-10 (pull request #617)
Updated git ignore for updated CI.

Approved-by: Patrick Fic
2022-11-08 18:10:27 +00:00
Patrick Fic
22d075c89a Updated git ignore for updated CI. 2022-11-08 10:08:58 -08:00
Patrick Fic
ac1eead695 Merged in release/2022-11-10 (pull request #616)
release/2022-11-10

Approved-by: Patrick Fic
2022-11-08 18:02:10 +00:00
Patrick Fic
5a12738da5 IO-2096 Add Cycle Time Analysis report 2022-11-08 10:01:40 -08:00
Patrick Fic
480cdb9b28 updated GIT and circle CI messages. 2022-11-07 15:58:30 -08:00
Patrick Fic
f389dfdd94 Merged in release/2022-11-10 (pull request #615)
PBS AP: skip posting of bills/wip for AR
2022-11-07 22:23:14 +00:00
Patrick Fic
fb20cceebc Merged in release/2022-11-10 (pull request #614)
PBS AP: skip posting of bills/wip for AR
2022-11-07 22:05:40 +00:00
Patrick Fic
b549f45cde PBS AP: skip posting of bills/wip for AR 2022-11-07 13:24:54 -08:00
Patrick Fic
a4fc26c139 Merged in release/2022-11-04 (pull request #613)
Release/2022 11 04
2022-11-04 17:32:17 +00:00
Patrick Fic
8f9cecd6bf Merged in release/2022-11-04 (pull request #612)
Add title to email overlay to prevent close button overlap.
2022-11-04 17:31:07 +00:00
Patrick Fic
6c5d684218 Add title to email overlay to prevent close button overlap. 2022-11-03 16:37:09 -07:00
Patrick Fic
ee05f59bd3 Merged in release/2022-11-04 (pull request #611)
release/2022-11-04

Approved-by: Patrick Fic
2022-11-03 18:45:28 +00:00
Patrick Fic
a62796b12f ARMS Update 2022-11-03 11:44:37 -07:00
Patrick Fic
3a5508fc95 Merged in release/2022-11-04 (pull request #610)
Release/2022 11 04
2022-11-03 17:15:37 +00:00
Patrick Fic
6a49457382 CI fix. 2022-11-03 10:12:46 -07:00
Patrick Fic
24d71413c1 Revert "IO-2086 Use new grid gallery and lightbox for performance."
This reverts commit 089fc0b0f7.
2022-11-03 10:12:15 -07:00
Patrick Fic
00ae2cda5d Merged in release/2022-11-04 (pull request #609)
Release/2022 11 04
2022-11-03 17:02:46 +00:00
Patrick Fic
3354afe380 Merge branch 'feature/pbs-ap' into release/2022-11-04
* feature/pbs-ap:
  Minor bug fixes.
  Add notification of complete export.
  AP changes based on validation testing.
  WIP PBS AP.
  WIP PBS AP.
2022-11-03 10:01:39 -07:00
Patrick Fic
d0bd2aeaba Minor bug fixes. 2022-11-03 10:01:22 -07:00
Patrick Fic
b3ed9106f0 Add notification of complete export. 2022-11-03 08:25:44 -07:00
Patrick Fic
6354ccee87 AP changes based on validation testing. 2022-11-01 14:26:13 -07:00
Patrick Fic
089fc0b0f7 IO-2086 Use new grid gallery and lightbox for performance. 2022-11-01 09:04:41 -07:00
Patrick Fic
ede1cdb89b WIP PBS AP. 2022-10-31 15:59:00 -07:00
Patrick Fic
cd96de3a96 Merge branch 'release/2022-10-31' into feature/pbs-ap
* release/2022-10-31:
  Improved LR activiation.
  Improved LR tracking.
2022-10-31 11:34:56 -07:00
Patrick Fic
33dea8638c Merged in release/2022-10-31 (pull request #608)
Improved LR tracking.
2022-10-31 18:31:03 +00:00
Patrick Fic
790fd4611f Improved LR activiation. 2022-10-31 11:30:45 -07:00
Patrick Fic
c72fdb81af Improved LR tracking. 2022-10-31 11:27:33 -07:00
Patrick Fic
8d5202f46d WIP PBS AP. 2022-10-31 10:42:42 -07:00
Patrick Fic
7d81898a45 Merged in release/2022-10-28 (pull request #607)
Release/2022 10 28
2022-10-31 14:18:40 +00:00
Patrick Fic
821bc850db Merged in release/2022-10-28 (pull request #606)
Release/2022 10 28
2022-10-27 18:15:08 +00:00
Patrick Fic
107265c2fc IO-223 Remove LA1 from ARMS. 2022-10-27 11:11:29 -07:00
Patrick Fic
d6d75cacd8 Merged in release/2022-10-27 (pull request #605)
Resolve PBS query error.
2022-10-27 15:16:27 +00:00
Patrick Fic
bfefc99434 Resolve PBS query error. 2022-10-27 08:15:41 -07:00
Patrick Fic
c26cc9e908 Resolve PBS query error. 2022-10-27 08:13:57 -07:00
Patrick Fic
3e8af5fec7 Merged in release/2022-10-28 (pull request #604)
release/2022-10-28

Approved-by: Patrick Fic
2022-10-26 16:27:23 +00:00
Patrick Fic
21d05553ff Add ARMS logging. 2022-10-26 09:26:39 -07:00
Patrick Fic
fdcd97d195 Merged in release/2022-10-28 (pull request #603)
release/2022-10-28

Approved-by: Patrick Fic
2022-10-24 21:02:19 +00:00
Patrick Fic
15bf1937d3 Remove logging statement. 2022-10-24 14:01:37 -07:00
Patrick Fic
07dce11341 Merged in release/2022-10-28 (pull request #602)
release/2022-10-28

Approved-by: Patrick Fic
2022-10-24 20:50:20 +00:00
Patrick Fic
41b32a25d7 IO-2086 LMS Optimized imges bugfix. 2022-10-24 13:49:57 -07:00
Patrick Fic
ab242955f1 Merged in release/2022-10-28 (pull request #601)
IO-2086 Improved local media image display.
2022-10-24 19:52:10 +00:00
Patrick Fic
dc0d8526a3 IO-2086 Improved local media image display. 2022-10-24 12:50:52 -07:00
Patrick Fic
84fcca239a Merged in release/2022-10-21 (pull request #600)
Release/2022 10 21
2022-10-19 22:51:50 +00:00
Patrick Fic
7039eff354 Minimum bill quantity set to 1. 2022-10-19 15:20:06 -07:00
Patrick Fic
2c1bd448e7 Merged in release/2022-10-21 (pull request #599)
Release/2022 10 21
2022-10-18 17:46:18 +00:00
Patrick Fic
05bfd217be IO-2070 Credit memos no longer update line status. 2022-10-18 10:39:58 -07:00
Patrick Fic
c83ff03ae8 IO-2038 Resolve duplicate shift clock login. 2022-10-18 09:51:55 -07:00
Patrick Fic
b363b2f7e5 Resolve CI issue. 2022-10-18 09:22:21 -07:00
Patrick Fic
d14a3f77f1 IO-2082 Closing $0 RO. 2022-10-18 09:19:58 -07:00
Patrick Fic
04f4ce97ed IO-2059 Add invoice history to job line expander. 2022-10-18 09:00:21 -07:00
Patrick Fic
dc21c25c95 Merged in release/2022-10-21 (pull request #598)
IO-2072 Update template for parts label.
2022-10-18 00:40:49 +00:00
Patrick Fic
1d39e574cf IO-2072 Update template for parts label. 2022-10-17 17:30:30 -07:00
Patrick Fic
24c62f4905 Merged in release/2022-10-21 (pull request #597)
Release/2022 10 21
2022-10-18 00:07:33 +00:00
Patrick Fic
ac6903edcb IO-2072 add parts label to bill posting screen. 2022-10-17 16:58:03 -07:00
Patrick Fic
0129868bb0 IO-2058 Add RO/Name to resent parts order. 2022-10-17 16:30:45 -07:00
Patrick Fic
1012d4acda IO-2066 Update item used for adjustments. 2022-10-17 16:21:44 -07:00
Patrick Fic
2b5e8f9a81 IO-2013 Add IOU flag to job header in certain areas. 2022-10-17 16:07:16 -07:00
Patrick Fic
dbc83aab00 Merged in release/2022-10-14 (pull request #596)
Release/2022 10 14
2022-10-14 18:49:16 +00:00
Patrick Fic
f23868bbde Merged in release/2022-10-14 (pull request #595)
release/2022-10-14

Approved-by: Patrick Fic
2022-10-14 15:47:18 +00:00
Patrick Fic
ebcf27feea IO-2074 updated labels for reports. 2022-10-14 08:46:51 -07:00
Patrick Fic
7d84dcacee Merged in release/2022-10-14 (pull request #594)
release/2022-10-14

Approved-by: Patrick Fic
2022-10-13 20:22:18 +00:00
Patrick Fic
c391b7d90b IO-2067 IO-2074 Add new reports. 2022-10-13 13:21:53 -07:00
Patrick Fic
41b505748c Merged in release/2022-10-03 (pull request #593)
IO-2054 Resolve issue for multi AR.
2022-10-03 18:01:21 +00:00
Patrick Fic
54feb0b541 IO-2054 Resolve issue for multi AR. 2022-10-03 08:00:29 -10:00
Patrick Fic
2b36bcc56b Merged in release/2022-09-30 (pull request #592)
Release/2022 09 30
2022-09-28 23:26:19 +00:00
Patrick Fic
6aeeac15b6 Merged in release/2022-09-30 (pull request #591)
IO-223 Use NA for owner company name.
2022-09-28 19:45:32 +00:00
Patrick Fic
d699e49369 IO-223 Use NA for owner company name. 2022-09-28 12:44:09 -07:00
Patrick Fic
e69a218392 Merged in release/2022-09-30 (pull request #590)
Release/2022 09 30
2022-09-28 19:07:50 +00:00
Patrick Fic
1105431909 IO-2054 Improved spacing for QB Multi Payer 2022-09-28 12:07:27 -07:00
Patrick Fic
1c222db5a3 IO-2051 Mark appointment as arrived from checklist. 2022-09-28 11:55:46 -07:00
Patrick Fic
f6b72ab428 IO-2065 Resolve parts status filtering. 2022-09-28 11:47:48 -07:00
Patrick Fic
045a3660a3 IO-2054 Updated labels for multi AR. 2022-09-28 11:30:11 -07:00
Patrick Fic
477c7bb006 Merged in release/2022-09-30 (pull request #589)
IO-2054 Bug fix for payments.
2022-09-28 00:08:15 +00:00
Patrick Fic
45fca7206b IO-2054 Bug fix for payments. 2022-09-27 17:07:42 -07:00
Patrick Fic
0f75ca555d Merged in release/2022-09-30 (pull request #588)
Release/2022 09 30
2022-09-27 22:25:54 +00:00
Patrick Fic
e8fc29ea61 Autohouse Updates. 2022-09-27 15:07:26 -07:00
Patrick Fic
918bc402f6 IO-2054 Add Payments Export 2022-09-27 15:07:15 -07:00
Patrick Fic
e9c83e567a Merged in release/2022-09-30 (pull request #587)
Release/2022 09 30
2022-09-27 19:20:01 +00:00
Patrick Fic
c154e7be2e IO-223 ARMS bug fixes. 2022-09-27 08:41:45 -07:00
Patrick Fic
1f8edf764d IO-2054 Update responsibility center page. 2022-09-26 14:40:10 -07:00
Patrick Fic
acd3f545b3 IO-2054 RO Splitting for QBO 2022-09-26 11:33:58 -07:00
Patrick Fic
220e863482 Merged in release/2022-09-23 (pull request #586)
Prevent no reply loop
2022-09-24 18:05:24 +00:00
Patrick Fic
0fee89623c Prevent no reply loop 2022-09-24 11:04:08 -07:00
Patrick Fic
79c7ef327c Merged in release/2022-09-23 (pull request #585)
Updated recipieint for bounced emails since testing confirmed.
2022-09-23 21:10:48 +00:00
Patrick Fic
9249222fab Add missing query field. 2022-09-23 14:10:16 -07:00
Patrick Fic
4b6eea41c2 Updated recipieint for bounced emails since testing confirmed. 2022-09-23 14:08:45 -07:00
Patrick Fic
f7adbd9a20 Merged in release/2022-09-23 (pull request #584)
Updates to bounced email tracker.
2022-09-23 21:03:19 +00:00
Patrick Fic
ac270a608f Updates to bounced email tracker. 2022-09-23 13:59:58 -07:00
Patrick Fic
32110b13c2 Merged in release/2022-09-23 (pull request #583)
Update SNS handling
2022-09-23 20:49:06 +00:00
Patrick Fic
39efb52497 Update SNS handling 2022-09-23 13:48:45 -07:00
Patrick Fic
b4fd674535 Merged in release/2022-09-23 (pull request #582)
Release/2022 09 23
2022-09-23 20:36:42 +00:00
Patrick Fic
7aeac03b2c update formatting of bounced emails. 2022-09-23 13:35:33 -07:00
Patrick Fic
5ed8cef2f5 Merged in release/2022-09-23 (pull request #581)
Release/2022 09 23
2022-09-22 23:22:55 +00:00
Patrick Fic
c2dd122370 Merge branch 'release/2022-09-23' of https://bitbucket.org/snaptsoft/bodyshop into release/2022-09-23 2022-09-22 14:46:12 -07:00
Patrick Fic
7fc8cbcca4 IO-2061 Handled Bounced Emails. 2022-09-22 14:46:08 -07:00
Patrick Fic
ceafab55fa IO-2054 QBO Split AR 2022-09-22 11:21:08 -07:00
Patrick Fic
cccd025a24 Resolve PPG data pump issue. 2022-09-20 15:30:32 -07:00
Patrick Fic
78a04067c5 IO-2053 Confirm before marking as exported. 2022-09-19 19:21:44 -07:00
Patrick Fic
2db2c8edbf IO-2049 Document delete&move on server side 2022-09-19 14:48:06 -07:00
Patrick Fic
ff149c4464 Merged in release/2022-09-23 (pull request #580)
IO-233 Logging & Credentials
2022-09-19 18:00:44 +00:00
Patrick Fic
38efe03889 IO-233 Logging & Credentials 2022-09-19 11:00:04 -07:00
Patrick Fic
195e4a05db Merged in release/2022-09-23 (pull request #579)
IO-233 Update ARMS trigger & error logging.
2022-09-19 17:54:24 +00:00
Patrick Fic
2042ded99b IO-233 Update ARMS trigger & error logging. 2022-09-19 10:53:48 -07:00
Patrick Fic
d75a6328e8 Merged in release/2022-09-16 (pull request #578)
Release/2022 09 16
2022-09-19 15:50:43 +00:00
Patrick Fic
7194d2bba0 Merged in release/2022-09-16 (pull request #577)
Release/2022 09 16
2022-09-14 17:12:08 +00:00
Patrick Fic
3d9a24de4f IO-782 Parts Label Multi. 2022-09-13 15:43:05 -07:00
Patrick Fic
9792773cf0 IO-2043 Add LMS bill upload notification. 2022-09-13 15:26:55 -07:00
Patrick Fic
7c4ba416f7 IO-2028 LMS Deletion. 2022-09-13 15:13:43 -07:00
Patrick Fic
9dbb4b586f IO-233 ARMS event based updates. 2022-09-13 14:00:54 -07:00
Patrick Fic
169fdf6ae8 Merged in release/2022-09-09 (pull request #576)
Release/2022 09 09
2022-09-09 20:13:00 +00:00
Patrick Fic
9b92f83e52 Merged in release/2022-09-09 (pull request #575)
Release/2022 09 09
2022-09-08 17:04:23 +00:00
Patrick Fic
5f3c1fc95e IO-2039 LMS support for non media files. 2022-09-08 10:03:24 -07:00
Patrick Fic
ba9ea17805 Add QBO error for missing insurance company name. 2022-09-06 09:41:02 -07:00
Patrick Fic
d7c68441e8 Merged in releaase/2022-09-02 (pull request #574)
Releaase/2022 09 02
2022-09-02 15:03:09 +00:00
Patrick Fic
88a9ec1d3c Merged in releaase/2022-09-02 (pull request #573)
releaase/2022-09-02

Approved-by: Patrick Fic
2022-09-01 23:49:36 +00:00
Patrick Fic
c0973b6098 Remove unnecessary console log. 2022-09-01 16:48:29 -07:00
Patrick Fic
e10196bb62 IO-1325 Populate 3rd party payer modal correctly. 2022-09-01 16:39:05 -07:00
Patrick Fic
2e2e9ad7a9 Merged in releaase/2022-09-02 (pull request #572)
Releaase/2022 09 02
2022-09-01 15:54:46 +00:00
Patrick Fic
637a33e670 IO-1981 Disable order type for OEC orders. 2022-08-31 14:48:47 -07:00
Patrick Fic
eb52ccbc9d Merged in releaase/2022-09-02 (pull request #571)
Releaase/2022 09 02
2022-08-31 21:10:18 +00:00
Patrick Fic
8bcc903f2b IO-1325 Automatically populate 3rd party modal. 2022-08-31 13:37:01 -07:00
Patrick Fic
5ec5be0852 IO-1981 Add Sublet Order. 2022-08-31 13:04:36 -07:00
Patrick Fic
cb0c4d55df IO-1950 Parts Order Discounts 2022-08-31 10:32:41 -07:00
Patrick Fic
dea4d50821 IO-1949 Add additional buttons to email overlay. 2022-08-30 16:16:07 -07:00
Patrick Fic
df1adc34a2 IO-2033 Mark payment as exported. 2022-08-30 14:11:34 -07:00
Patrick Fic
e44e2bd7dd Merged in release/2022-08-26 (pull request #570)
IO-2032 Add psr by make template.

Approved-by: Patrick Fic
2022-08-29 23:50:53 +00:00
Patrick Fic
8f44b61a16 Merged in release/2022-08-26 (pull request #569)
release/2022-08-26

Approved-by: Patrick Fic
2022-08-29 23:22:05 +00:00
Patrick Fic
1f9abac599 IO-2032 Add psr by make template. 2022-08-29 16:21:46 -07:00
Patrick Fic
b557100fc6 Merged in release/2022-08-26 (pull request #568)
IO-1984 Resolve error on email audit tab.

Approved-by: Patrick Fic
2022-08-29 15:13:57 +00:00
Patrick Fic
7104af82d7 Merged in release/2022-08-26 (pull request #567)
release/2022-08-26

Approved-by: Patrick Fic
2022-08-29 15:13:31 +00:00
Patrick Fic
5adfef6ce0 IO-1984 Resolve error on email audit tab. 2022-08-29 08:13:12 -07:00
Patrick Fic
d9a8831eb3 Merged in release/2022-08-26 (pull request #566)
Minor release fixes.

Approved-by: Patrick Fic
2022-08-26 21:06:43 +00:00
Patrick Fic
511b7693c7 Merged in release/2022-08-26 (pull request #565)
release/2022-08-26

Approved-by: Patrick Fic
2022-08-26 21:06:12 +00:00
Patrick Fic
f5c9a7dfef Minor release fixes. 2022-08-26 13:59:44 -07:00
Patrick Fic
cdacc2befa Merged in release/2022-08-26 (pull request #564)
release/2022-08-26

Approved-by: Patrick Fic
2022-08-25 22:48:17 +00:00
Patrick Fic
79563a5cba IO-2030 Add totals to labor allocations table. 2022-08-25 14:56:59 -07:00
Patrick Fic
5264dfa49f Merge branch 'release/2022-08-26' of bitbucket.org:snaptsoft/bodyshop into release/2022-08-26 2022-08-23 15:16:27 -07:00
Patrick Fic
0810467689 IO-223 ARMS based updates. 2022-08-23 14:03:55 -07:00
Patrick Fic
2e069bf628 Merge branch 'hotfix/2022-08-23' into release/2022-08-26
* hotfix/2022-08-23:
  Query only active QBO tax codes.
2022-08-23 10:10:50 -07:00
Patrick Fic
137370812d Merged in hotfix/2022-08-23 (pull request #563)
Query only active QBO tax codes.
2022-08-23 17:01:03 +00:00
Patrick Fic
4f060ec447 Query only active QBO tax codes. 2022-08-23 09:59:38 -07:00
Patrick Fic
23971e23f2 IO-1998 Filter schedule by event type. 2022-08-23 09:31:04 -07:00
Patrick Fic
44e313d8e3 IO-1882 Add manual event from job detail actions menu. 2022-08-22 15:01:59 -07:00
Patrick Fic
3b9c44b0a8 IO-1984 Email Audit Trail 2022-08-22 13:02:02 -07:00
Patrick Fic
e438348e9b Update ARMS query 2022-08-19 14:10:13 -07:00
Patrick Fic
6122a24b80 Merged in release/2022-08-19 (pull request #562)
Release/2022 08 19
2022-08-19 18:25:50 +00:00
Patrick Fic
93cdbea17c Merged in release/2022-08-19 (pull request #561)
Release/2022 08 19
2022-08-18 21:47:16 +00:00
Patrick Fic
62d5c17de2 IO-2027 LMS Texting & Emails. 2022-08-18 14:46:40 -07:00
Patrick Fic
87c934c886 Add RBAC to tech jobs list. 2022-08-18 10:48:51 -07:00
Patrick Fic
e4b736d4e9 Resolve label typo. 2022-08-17 14:19:28 -07:00
Patrick Fic
d0673bfcba Updated third party notices. 2022-08-17 14:19:21 -07:00
Patrick Fic
18c182e573 Merged in release/2022-08-19 (pull request #560)
Release/2022 08 19
2022-08-17 16:30:52 +00:00
Patrick Fic
fe6e85e993 IO-2023 Resolve hours paid as part for MPI. 2022-08-16 16:18:50 -07:00
Patrick Fic
b744720efe IO-2009 Better handling of MPI discounts. 2022-08-16 14:29:28 -07:00
Patrick Fic
d5c27fc9ae IO-2015 Job Costing fix for lines with no part type but a dollar amount. 2022-08-15 12:39:09 -07:00
Patrick Fic
129c94f066 Check for blank string on owner name 2022-08-15 10:45:19 -07:00
Patrick Fic
503e901295 Merged in release/2022-08-05 (pull request #559)
IO-2008 Split local media into images and other as per cloudinary for better other support.
2022-08-04 16:06:52 +00:00
Patrick Fic
c2058bcec6 Merged in release/2022-08-05 (pull request #558)
Release/2022 08 05
2022-08-04 15:28:08 +00:00
Patrick Fic
85caf828ea IO-2008 Split local media into images and other as per cloudinary for better other support. 2022-08-03 13:27:45 -07:00
Patrick Fic
92b89af1c7 Merged in release/2022-08-05 (pull request #555)
Add null check to owner display.

Approved-by: Patrick Fic
2022-08-02 18:19:26 +00:00
Patrick Fic
b03d729af6 Add null check to owner display. 2022-08-02 11:18:36 -07:00
Patrick Fic
5c22ce188b Merged in release/2022-08-05 (pull request #554)
Updated antd package to fix drawer issues.

Approved-by: Patrick Fic
2022-08-02 18:11:31 +00:00
Patrick Fic
faef05a95b Updated antd package to fix drawer issues. 2022-08-02 11:11:01 -07:00
Patrick Fic
64aa8336b3 Merged in release/2022-07-29 (pull request #553)
Resolve production board loading issue for MAXWR.

Approved-by: Patrick Fic
2022-07-30 21:42:56 +00:00
Patrick Fic
d92f706d05 Merged in release/2022-07-29 (pull request #552)
release/2022-07-29

Approved-by: Patrick Fic
2022-07-29 23:05:08 +00:00
Patrick Fic
6031255d61 Merged in release/2022-07-29 (pull request #551)
release/2022-07-29

Approved-by: Patrick Fic
2022-07-29 18:08:36 +00:00
Patrick Fic
e9c229f307 Merged in release/2022-07-29 (pull request #550)
release/2022-07-29

Approved-by: Patrick Fic
2022-07-29 17:39:51 +00:00
Patrick Fic
26f6f63782 Merged in release/2022-07-29 (pull request #549)
IO-2000 Add parts_not_recieved_vendor to report center.

Approved-by: Patrick Fic
2022-07-26 21:30:26 +00:00
Patrick Fic
3eaf199322 Merged in release/2022-07-29 (pull request #548)
release/2022-07-29

Approved-by: Patrick Fic
2022-07-26 18:40:50 +00:00
Patrick Fic
9ded2fd5ba Merged in release/2022-07-29 (pull request #547)
release/2022-07-29

Approved-by: Patrick Fic
2022-07-25 20:05:51 +00:00
Patrick Fic
ce783fdc5a Merged in release/2022-07-29 (pull request #546)
release/2022-07-29

Approved-by: Patrick Fic
2022-07-25 18:15:43 +00:00
Patrick Fic
a1e35a137e Merged in release/2022-07-22 (pull request #544)
Remove logging statement.
2022-07-22 17:56:10 +00:00
Patrick Fic
fa9731369b Merged in release/2022-07-22 (pull request #543)
Release/2022 07 22
2022-07-22 15:34:30 +00:00
Patrick Fic
616f326a88 Merged in release/2022-07-22 (pull request #542)
release/2022-07-22

Approved-by: Patrick Fic
2022-07-20 22:19:46 +00:00
Patrick Fic
60950be9d5 Merged in release/2022-07-22 (pull request #541)
Release/2022 07 22
2022-07-19 21:53:12 +00:00
Patrick Fic
f479aeda54 Merged in release/2022-07-15 (pull request #539)
Release/2022 07 15
2022-07-15 22:40:04 +00:00
Patrick Fic
98bf700d1c Merged in release/2022-07-15 (pull request #538)
IO-1536 Add VIN Highlighting.
2022-07-15 17:51:21 +00:00
Patrick Fic
2a9609b917 Merged in release/2022-07-15 (pull request #537)
release/2022-07-15

Approved-by: Patrick Fic
2022-07-14 16:10:05 +00:00
Patrick Fic
423df9f9aa Merged in release/2022-07-08 (pull request #535)
release/2022-07-08

Approved-by: Patrick Fic
2022-07-07 16:30:17 +00:00
Patrick Fic
184ea58ec2 Merged in release/2022-06-30 (pull request #533)
release/2022-06-30

Approved-by: Patrick Fic
2022-06-30 21:17:08 +00:00
Patrick Fic
f9b6920eba Merged in release/2022-06-30 (pull request #532)
release/2022-06-30

Approved-by: Patrick Fic
2022-06-30 16:59:17 +00:00
Patrick Fic
9d8550e040 Merged in release/2022-06-30 (pull request #531)
release/2022-06-30

Approved-by: Patrick Fic
2022-06-30 16:04:37 +00:00
Patrick Fic
930321c885 Merged in release/2022-06-30 (pull request #530)
release/2022-06-30

Approved-by: Patrick Fic
2022-06-29 23:20:00 +00:00
Patrick Fic
5ff660d83d Merged in release/2022-06-30 (pull request #529)
release/2022-06-30

Approved-by: Patrick Fic
2022-06-29 22:20:53 +00:00
Patrick Fic
4b923acdf3 Merged in release/2022-06-30 (pull request #528)
release/2022-06-30

Approved-by: Patrick Fic
2022-06-29 22:05:24 +00:00
Patrick Fic
6337a961e9 Merged in release/2022-06-30 (pull request #525)
Release/2022 06 30
2022-06-28 22:50:16 +00:00
Patrick Fic
fb04742e5b Merged in release/2022-06-24 (pull request #522)
Add job reconciliation & autohouse filtering.

Approved-by: Patrick Fic
2022-06-24 16:48:05 +00:00
Patrick Fic
50f5be3174 Merged in release/2022-06-24 (pull request #520)
release/2022-06-24

Approved-by: Patrick Fic
2022-06-20 22:19:15 +00:00
Patrick Fic
63106487b9 Merged in release/2022-06-17 (pull request #518)
release/2022-06-17

Approved-by: Patrick Fic
2022-06-20 17:48:16 +00:00
Patrick Fic
a3a48c1b29 Merged in release/2022-06-10 (pull request #512)
release/2022-06-10

Approved-by: Patrick Fic
2022-06-10 22:37:40 +00:00
Patrick Fic
72d0a990ec Merged in release/2022-06-10 (pull request #511)
release/2022-06-10

Approved-by: Patrick Fic
2022-06-10 17:07:01 +00:00
Patrick Fic
e8d6871a20 Merged in release/2022-06-10 (pull request #510)
release/2022-06-10

Approved-by: Patrick Fic
2022-06-09 22:55:25 +00:00
Patrick Fic
5d4b2f308d Merged in release/2022-06-10 (pull request #509)
Resolved issues for job search select & updated packages.

Approved-by: Patrick Fic
2022-06-09 19:01:39 +00:00
Patrick Fic
84b274d0a5 Merged in release/2022-06-10 (pull request #508)
release/2022-06-10

Approved-by: Patrick Fic
2022-06-09 17:46:36 +00:00
Patrick Fic
271b2286ae Merged in release/2022-06-10 (pull request #507)
release/2022-06-10

Approved-by: Patrick Fic
2022-06-09 17:24:20 +00:00
Patrick Fic
660f851ee6 Merged in release/2022-06-10 (pull request #506)
Revert "Server Side CORS Updates."

Approved-by: Patrick Fic
2022-06-09 16:56:28 +00:00
Patrick Fic
5367e96aad Merged in release/2022-06-10 (pull request #505)
release/2022-06-10

Approved-by: Patrick Fic
2022-06-09 00:46:53 +00:00
Patrick Fic
9ac262fec2 Merged in release/2022-06-10 (pull request #504)
Resolve email PDFs not generating header.

Approved-by: Patrick Fic
2022-06-08 17:01:40 +00:00
Patrick Fic
62e044fbb2 Merged in release/2022-06-10 (pull request #503)
release/2022-06-10

Approved-by: Patrick Fic
2022-06-07 21:36:19 +00:00
Patrick Fic
d71155f350 Merged in release/2022-06-10 (pull request #502)
release/2022-06-10

Approved-by: Patrick Fic
2022-06-07 19:40:36 +00:00
Patrick Fic
c105c56acf Merged in release/2022-06-10 (pull request #501)
release/2022-06-10

Approved-by: Patrick Fic
2022-06-06 21:34:08 +00:00
Patrick Fic
9c7f2a6080 Merged in hotfix/2022-06-03 (pull request #497)
hotfix/2022-06-03

Approved-by: Patrick Fic
2022-06-03 17:15:51 +00:00
Patrick Fic
bcd6cc006f Merged in release/2022-05-27 (pull request #494)
release/2022-05-27

Approved-by: Patrick Fic
2022-05-27 21:44:45 +00:00
Patrick Fic
b2e14ed03b Merged in release/2022-05-27 (pull request #493)
release/2022-05-27

Approved-by: Patrick Fic
2022-05-27 21:34:31 +00:00
Patrick Fic
1d21dc3ee4 Merged in release/2022-05-27 (pull request #492)
release/2022-05-27

Approved-by: Patrick Fic
2022-05-26 22:21:32 +00:00
Patrick Fic
3c355a8227 Merged in release/2022-05-27 (pull request #491)
release/2022-05-27

Approved-by: Patrick Fic
2022-05-26 19:25:41 +00:00
Patrick Fic
546208d249 Merged in release/2022-05-20 (pull request #489)
Release/2022 05 20
2022-05-20 17:32:31 +00:00
Patrick Fic
012d59e5b7 Merged in release/2022-05-20 (pull request #488)
release/2022-05-20

Approved-by: Patrick Fic
2022-05-20 16:20:12 +00:00
Patrick Fic
8da2205a14 Merged in release/2022-05-20 (pull request #487)
release/2022-05-20

Approved-by: Patrick Fic
2022-05-20 15:54:17 +00:00
Patrick Fic
ad2bae00de Merged in release/2022-05-20 (pull request #486)
release/2022-05-20

Approved-by: Patrick Fic
2022-05-19 18:26:10 +00:00
Patrick Fic
8a21c24a5b Merged in release/2022-05-13 (pull request #480)
Updated CI config.

Approved-by: Patrick Fic
2022-05-13 23:40:32 +00:00
Patrick Fic
4004856cd6 Merged in release/2022-05-13 (pull request #479)
Local media server bugfixes.

Approved-by: Patrick Fic
2022-05-13 16:35:47 +00:00
Patrick Fic
23ace9f7be Merged in release/2022-05-13 (pull request #478)
IO-1875 Add CNR by Vendor
2022-05-12 23:59:10 +00:00
Patrick Fic
64f188dbdc Merged in release/2022-05-13 (pull request #476)
release/2022-05-13

Approved-by: Patrick Fic
2022-05-12 23:04:31 +00:00
Patrick Fic
ce15216764 Merged in release/2022-05-13 (pull request #475)
Release/2022 05 13
2022-05-12 19:41:19 +00:00
Patrick Fic
01253361b5 Merged in release/2022-05-13 (pull request #473)
Added IMS token changes.

Approved-by: Patrick Fic
2022-05-11 23:32:04 +00:00
Patrick Fic
fad667f2a4 Merged in release/2022-05-13 (pull request #471)
release/2022-05-13

Approved-by: Patrick Fic
2022-05-10 21:45:17 +00:00
Patrick Fic
00b982becc Merged in release/2022-05-13 (pull request #465)
Release/2022 05 13
2022-05-10 20:31:43 +00:00
Patrick Fic
c8fb685cb0 Merged in release/2022-05-06 (pull request #462)
release/2022-05-06

Approved-by: Patrick Fic
2022-05-06 22:50:44 +00:00
Patrick Fic
6a7b616037 Merged in release/2022-05-06 (pull request #461)
release/2022-05-06

Approved-by: Patrick Fic
2022-05-06 21:30:47 +00:00
Patrick Fic
6607258777 Merged in release/2022-05-06 (pull request #460)
release/2022-05-06

Approved-by: Patrick Fic
2022-05-06 17:20:13 +00:00
Patrick Fic
4ad9ccec47 Merged in release/2022-05-06 (pull request #458)
Add backwards compatibility for log generation.
2022-05-03 18:08:45 +00:00
Patrick Fic
896d82202b Merged in release/2022-05-06 (pull request #457)
Release/2022 05 06
2022-05-03 16:55:47 +00:00
Patrick Fic
2ad4035294 Merged in release/2022-04-29 (pull request #455)
release/2022-04-29

Approved-by: Patrick Fic
2022-04-28 20:41:34 +00:00
Patrick Fic
e65101b4a9 Merged in release/2022-04-22 (pull request #453)
Release/2022 04 22
2022-04-22 22:14:52 +00:00
Patrick Fic
cd25f079b5 Merged in release/2022-04-22 (pull request #452)
Release/2022 04 22
2022-04-22 19:04:11 +00:00
Patrick Fic
427c2b762b Merged in release/2022-04-22 (pull request #451)
release/2022-04-22

Approved-by: Patrick Fic
2022-04-21 19:39:36 +00:00
Patrick Fic
f50b7ccaee Merged in release/2022-04-22 (pull request #450)
release/2022-04-22

Approved-by: Patrick Fic
2022-04-21 19:12:03 +00:00
Patrick Fic
c67bcd2098 Merged in release/2022-04-22 (pull request #449)
release/2022-04-22

Approved-by: Patrick Fic
2022-04-21 17:05:20 +00:00
Patrick Fic
6c6cce7da7 Merged in release/2022-04-15 (pull request #447)
Resolve third party payer template issue.
2022-04-14 17:48:23 +00:00
Patrick Fic
e273481a25 Merged in release/2022-04-15 (pull request #446)
release/2022-04-15

Approved-by: Patrick Fic
2022-04-14 00:30:22 +00:00
Patrick Fic
4b34cd2a2e Merged in release/2022-04-15 (pull request #445)
release/2022-04-15

Approved-by: Patrick Fic
2022-04-13 22:13:29 +00:00
Patrick Fic
f54883164c Merged in release/2022-04-15 (pull request #444)
Release/2022 04 15
2022-04-12 22:51:22 +00:00
Patrick Fic
82e7e5d910 Merged in release/2022-04-08 (pull request #442)
release/2022-04-08

Approved-by: Patrick Fic
2022-04-08 00:40:14 +00:00
Patrick Fic
6831184e33 Merged in release/2022-04-02 (pull request #438)
release/2022-04-02

Approved-by: Patrick Fic
2022-03-31 21:44:14 +00:00
Patrick Fic
a39a79102d Merged in release/2022-04-02 (pull request #437)
release/2022-04-02

Approved-by: Patrick Fic
2022-03-31 21:26:41 +00:00
Patrick Fic
95fff2acc4 Merged in release/2022-04-02 (pull request #436)
Release/2022 04 02
2022-03-31 19:51:22 +00:00
Patrick Fic
64d1c87dbd Merged in release/2022-04-02 (pull request #435)
release/2022-04-02

Approved-by: Patrick Fic
2022-03-25 21:42:50 +00:00
Patrick Fic
47dc00ff6e Merged in release/2022-03-25 (pull request #432)
release/2022-03-25

Approved-by: Patrick Fic
2022-03-24 21:40:43 +00:00
Patrick Fic
a2fbdd6a32 Merged in release/2022-03-25 (pull request #431)
release/2022-03-25

Approved-by: Patrick Fic
2022-03-24 21:18:15 +00:00
Patrick Fic
e46240f543 Merged in release/2022-03-25 (pull request #430)
release/2022-03-25

Approved-by: Patrick Fic
2022-03-22 19:41:39 +00:00
Patrick Fic
8882481006 Merged in release/2022-03-25 (pull request #429)
release/2022-03-25

Approved-by: Patrick Fic
2022-03-22 17:12:08 +00:00
Patrick Fic
79f66294d9 Merged in release/2022-03-25 (pull request #428)
release/2022-03-25

Approved-by: Patrick Fic
2022-03-21 17:53:26 +00:00
Patrick Fic
f9f1dbaf24 Merged in release/2022-03-18 (pull request #425)
Release/2022 03 18
2022-03-17 22:48:36 +00:00
Patrick Fic
ac8ae34e1d Merged in release/2022-03-18 (pull request #423)
Release/2022 03 18
2022-03-16 21:01:48 +00:00
Patrick Fic
e72925e34e Merged in release/2022-03-18 (pull request #422)
Release/2022 03 18
2022-03-16 20:54:13 +00:00
Patrick Fic
9a6be8a71e Merged in release/2022-03-18 (pull request #421)
Release/2022 03 18
2022-03-14 20:49:42 +00:00
Patrick Fic
11b20c54c2 Merged in release/2022-03-11 (pull request #419)
IO-1776 Mechanical authorization added to print center.

Approved-by: Patrick Fic
2022-03-11 16:53:26 +00:00
Patrick Fic
ac7dcdd6a9 Merged in release/2022-03-11 (pull request #418)
release/2022-03-11

Approved-by: Patrick Fic
2022-03-10 19:02:58 +00:00
Patrick Fic
20abda83f3 Merged in release/2022-03-11 (pull request #414)
release/2022-03-11

Approved-by: Patrick Fic
2022-03-07 22:47:56 +00:00
Patrick Fic
826a57c012 Merged in release/2022-03-04 (pull request #412)
release/2022-03-04

Approved-by: Patrick Fic
2022-03-04 18:13:33 +00:00
Patrick Fic
1a0c5c44ba Merged in release/2022-03-04 (pull request #411)
release/2022-03-04

Approved-by: Patrick Fic
2022-03-03 20:39:40 +00:00
Patrick Fic
9e8eb35792 Merged in release/2022-03-04 (pull request #409)
release/2022-03-04

Approved-by: Patrick Fic
2022-03-03 17:07:03 +00:00
Patrick Fic
49618ac6ef Merged in release/2022-03-04 (pull request #408)
release/2022-03-04

Approved-by: Patrick Fic
2022-03-02 21:00:10 +00:00
Patrick Fic
ad8c13f346 Merged in release/2022-03-04 (pull request #406)
release/2022-03-04

Approved-by: Patrick Fic
2022-03-02 01:27:33 +00:00
Patrick Fic
e2f3d6ef83 Merged in release/2022-02-18 (pull request #404)
Updated task scheduler.

Approved-by: Patrick Fic
2022-02-18 20:59:32 +00:00
Patrick Fic
a859990110 Merged in release/2022-02-18 (pull request #403)
release/2022-02-18

Approved-by: Patrick Fic
2022-02-18 01:14:54 +00:00
Patrick Fic
1286b72f2c Merged in release/2022-02-18 (pull request #402)
release/2022-02-18

Approved-by: Patrick Fic
2022-02-18 01:08:43 +00:00
Patrick Fic
5a42cafe8a Merged in release/2022-02-18 (pull request #401)
release/2022-02-18

Approved-by: Patrick Fic
2022-02-18 00:30:33 +00:00
Patrick Fic
1fd9bfc34c Merged in release/2022-02-18 (pull request #400)
CI Fix.
2022-02-17 01:12:36 +00:00
Patrick Fic
8e470376fd Merged in release/2022-02-18 (pull request #399)
IO-1738 PVRT Update
2022-02-17 00:46:15 +00:00
Patrick Fic
171e11c9f0 Merged in release/2022-02-18 (pull request #398)
Release/2022 02 18
2022-02-16 23:31:51 +00:00
Patrick Fic
e56664bbc6 Merge branch 'release/2022-02-18' into test 2022-02-15 16:03:52 -08:00
Patrick Fic
0f16f616d4 Merged in release/2022-02-18 (pull request #396)
Release/2022 02 18
2022-02-15 21:47:22 +00:00
Patrick Fic
2fc2e0f02e Merged in release/2022-02-18 (pull request #394)
Resolve Date Picker Issues.
2022-02-15 17:31:05 +00:00
Patrick Fic
3cd2445098 Merged in release/2022-02-18 (pull request #391)
Release/2022 02 18
2022-02-15 03:35:19 +00:00
Patrick Fic
1742f66312 Merged in hotfix/2022-02-14 (pull request #387)
Resolve bill update error.
2022-02-14 21:03:44 +00:00
135 changed files with 65249 additions and 12061 deletions

180
.circleci/config.yml Normal file
View File

@@ -0,0 +1,180 @@
version: 2.1
orbs:
#snyk: snyk/snyk@0.0.8
#cypress: cypress-io/cypress@1.23.0
aws-s3: circleci/aws-s3@2.0.0
eb: circleci/aws-elastic-beanstalk@1.0.2
jira: circleci/jira@1.3.1
jobs:
api-deploy:
docker:
- image: "cimg/base:stable"
steps:
- checkout
- eb/setup
- run:
command: |
eb init imex-online-production-api -r ca-central-1 -p "Node.js 16 running on 64bit Amazon Linux 2"
eb status --verbose
eb deploy
eb status
- jira/notify
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.imex.online/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
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://imex-online-production/"
- jira/notify
test-hasura-migrate:
docker:
- image: cimg/node:16.15.0
parameters:
secret:
type: string
default: $HASURA_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.bodyshop.app/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
test-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://imex-online-test/"
- jira/notify
admin-app-build:
docker:
- image: cimg/node:16.15.0
working_directory: ~/repo/admin
steps:
- checkout:
path: ~/repo
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: npm i
- save_cache:
paths:
- node_modules
- ~/.npm
- ~/.cache
key: v1-dependencies-{{ checksum "package.json" }}
- run: npm run build
- aws-s3/sync:
from: build
to: "s3://adm.imex.online/"
workflows:
deploy_and_build:
jobs:
- api-deploy:
filters:
branches:
only: master
- app-build:
filters:
branches:
only: master
- hasura-migrate:
secret: ${HASURA_PROD_SECRET}
filters:
branches:
only: master
- test-app-build:
filters:
branches:
only: test
- test-hasura-migrate:
secret: ${HASURA_TEST_SECRET}
filters:
branches:
only: test
#- admin-app-build:
#filters:
#branches:
#only: master

View File

@@ -16,6 +16,6 @@
"rules": {
"no-console": "off"
},
"settings": {},
"plugins": ["cypress"]
"settings": {}
//"plugins": ["cypress"]
}

5
.gitignore vendored
View File

@@ -113,3 +113,8 @@ firebase/.env
!.elasticbeanstalk/*.cfg.yml
!.elasticbeanstalk/*.global.yml
logs/oAuthClient-log.log
.node-persist/**
/*.env.*

View File

@@ -19,3 +19,6 @@ npx deadfile ./src/index.js --exclude build templates
hasura migrate create "Init" --from-server --endpoint https://db.imex.online/ --admin-secret 'Production-ImEXOnline!@#'
hasura migrate apply --version "1620771761757" --skip-execution --endpoint https://db.imex.online/ --admin-secret 'Production-ImEXOnline!@#'
hasura migrate status --endpoint https://db.imex.online/ --admin-secret 'Production-ImEXOnline!@#'
Generate the license file:
$ generate-license-file --input package.json --output third-party-licenses.txt --overwrite

File diff suppressed because it is too large Load Diff

13
client/.env.development Normal file
View File

@@ -0,0 +1,13 @@
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_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_STRIPE_PUBLIC_KEY=pk_test_51GqB4TJl3nQjrZ0wCQWAxAhlNF8jKe0tipIa6ExBaxwJGitwvFsIZUEua4dUzaMIAuXp4qwYHXx7lgjyQSwP0Pe900vzm38C7g
REACT_APP_AXIOS_BASE_API_URL=https://api.imex.online/
REACT_APP_REPORTS_SERVER_URL=https://reports3.test.imex.online
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc

13
client/.env.production Normal file
View File

@@ -0,0 +1,13 @@
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/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_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_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_SPLIT_API=et9pjkik6bn67he5evpmpr1agoo7gactphgk

14
client/.env.test Normal file
View File

@@ -0,0 +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_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_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_IS_TEST=true
REACT_APP_SPLIT_API=ts615lqgnmk84thn72uk18uu5pgce6e0l4rc

View File

@@ -15,7 +15,7 @@
"@stripe/react-stripe-js": "^1.9.0",
"@stripe/stripe-js": "^1.32.0",
"@tanem/react-nprogress": "^5.0.8",
"antd": "^4.21.7",
"antd": "^4.22.3",
"apollo-link-logger": "^2.0.0",
"axios": "^0.27.2",
"craco-less": "^1.20.0",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
import { useTreatments } from "@splitsoftware/splitio-react";
import { useClient } from "@splitsoftware/splitio-react";
import { Button, Result } from "antd";
import LogRocket from "logrocket";
import React, { lazy, Suspense, useEffect } from "react";
@@ -51,11 +51,7 @@ export function App({
online,
setOnline,
}) {
const { LogRocket_Tracking } = useTreatments(
["LogRocket_Tracking"],
{},
bodyshop && bodyshop.imexshopid
);
const client = useClient();
useEffect(() => {
if (!navigator.onLine) {
@@ -78,15 +74,15 @@ export function App({
setOnline(true);
});
useEffect(() => {
if (currentUser.authorized) {
if (
process.env.NODE_ENV === "production" &&
LogRocket_Tracking.treatment === "on"
) {
if (currentUser.authorized && bodyshop) {
client.setAttribute("imexshopid", bodyshop.imexshopid);
if (client.getTreatment("LogRocket_Tracking") === "on") {
console.log("LR Start");
LogRocket.init("gvfvfw/bodyshopapp");
}
}
}, [currentUser.authorized, LogRocket_Tracking.treatment]);
}, [bodyshop, client, currentUser.authorized]);
if (currentUser.authorized === null) {
return <LoadingSpinner message={t("general.labels.loggingin")} />;

View File

@@ -1,19 +1,20 @@
import { Card, Input, Space, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
import PaymentMarkSelectedExported from "../payment-mark-selected-exported/payment-mark-selected-exported.component";
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -184,6 +185,13 @@ export function AccountingPayablesTableComponent({
<Card
extra={
<Space wrap>
<PaymentMarkSelectedExported
paymentIds={selectedPayments}
disabled={transInProgress || selectedPayments.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedPayments}
refetch={refetch}
/>
<PaymentsExportAllButton
paymentIds={selectedPayments}
disabled={transInProgress || selectedPayments.length === 0}

View File

@@ -4,6 +4,8 @@ import { useQuery } from "@apollo/client";
import { QUERY_AUDIT_TRAIL } from "../../graphql/audit_trail.queries";
import AlertComponent from "../alert/alert.component";
import { logImEXEvent } from "../../firebase/firebase.utils";
import EmailAuditTrailListComponent from "./email-audit-trail-list.component";
import { Card, Row } from "antd";
export default function AuditTrailListContainer({ recordId }) {
const { loading, error, data } = useQuery(QUERY_AUDIT_TRAIL, {
@@ -18,10 +20,20 @@ export default function AuditTrailListContainer({ recordId }) {
{error ? (
<AlertComponent type="error" message={error.message} />
) : (
<AuditTrailListComponent
loading={loading}
data={data ? data.audit_trail : null}
/>
<Row gutter={[16, 16]}>
<Card>
<AuditTrailListComponent
loading={loading}
data={data ? data.audit_trail : []}
/>
</Card>
<Card>
<EmailAuditTrailListComponent
loading={loading}
data={data ? data.audit_trail : []}
/>
</Card>
</Row>
)}
</div>
);

View File

@@ -0,0 +1,63 @@
import { Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
export default function EmailAuditTrailListComponent({ loading, data }) {
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: {},
});
const { t } = useTranslation();
const columns = [
{
title: t("audit.fields.created"),
dataIndex: " created",
key: " created",
width: "10%",
render: (text, record) => (
<DateTimeFormatter>{record.created}</DateTimeFormatter>
),
sorter: (a, b) => a.created - b.created,
sortOrder:
state.sortedInfo.columnKey === "created" && state.sortedInfo.order,
},
{
title: t("audit.fields.useremail"),
dataIndex: "useremail",
key: "useremail",
width: "10%",
sorter: (a, b) => alphaSort(a.useremail, b.useremail),
sortOrder:
state.sortedInfo.columnKey === "useremail" && state.sortedInfo.order,
},
];
const formItemLayout = {
labelCol: {
xs: { span: 12 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
},
};
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
return (
<Table
{...formItemLayout}
loading={loading}
pagination={{ position: "top", defaultPageSize: 25 }}
columns={columns}
rowKey="id"
dataSource={data}
onChange={handleTableChange}
/>
);
}

View File

@@ -1,5 +1,5 @@
import { useApolloClient, useMutation } from "@apollo/client";
import { Button, Form, Modal, notification, Space } from "antd";
import { Button, Checkbox, Form, Modal, notification, Space } from "antd";
import _ from "lodash";
import React, { useEffect, useState, useMemo } from "react";
import { useTranslation } from "react-i18next";
@@ -26,6 +26,9 @@ import BillFormContainer from "../bill-form/bill-form.container";
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
import { handleUpload } from "../documents-upload/documents-upload.utility";
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
import useLocalStorage from "../../utils/useLocalStorage";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
const mapStateToProps = createStructuredSelector({
billEnterModal: selectBillEnterModal,
@@ -38,6 +41,8 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(insertAuditTrail({ jobid, operation })),
});
const Templates = TemplateList("job_special");
function BillEnterModalContainer({
billEnterModal,
toggleModalVisible,
@@ -54,7 +59,10 @@ function BillEnterModalContainer({
const [updateInventoryLines] = useMutation(UPDATE_INVENTORY_LINES);
const [loading, setLoading] = useState(false);
const client = useApolloClient();
const [generateLabel, setGenerateLabel] = useLocalStorage(
"enter_bill_generate_label",
false
);
const formValues = useMemo(() => {
return {
...billEnterModal.context.bill,
@@ -221,23 +229,26 @@ function BillEnterModalContainer({
});
}
}
//If it's not a credit memo, update the statuses.
await Promise.all(
remainingValues.billlines
.filter((il) => il.joblineid !== "noline")
.map((li) => {
return updateJobLines({
variables: {
lineId: li.joblineid,
line: {
location: li.location || location,
status:
bodyshop.md_order_statuses.default_received || "Received*",
if (!values.is_credit_memo) {
await Promise.all(
remainingValues.billlines
.filter((il) => il.joblineid !== "noline")
.map((li) => {
return updateJobLines({
variables: {
lineId: li.joblineid,
line: {
location: li.location || location,
status:
bodyshop.md_order_statuses.default_received || "Received*",
},
},
},
});
})
);
});
})
);
}
/////////////////////////
if (upload && upload.length > 0) {
@@ -275,6 +286,20 @@ function BillEnterModalContainer({
notification["success"]({
message: t("bills.successes.created"),
});
if (generateLabel) {
GenerateDocument(
{
name: Templates.parts_invoice_label_single.key,
variables: {
id: billId,
},
},
{},
"p"
);
}
if (billEnterModal.actions.refetch) billEnterModal.actions.refetch();
insertAuditTrail({
@@ -330,6 +355,12 @@ function BillEnterModalContainer({
}}
footer={
<Space>
<Checkbox
checked={generateLabel}
onChange={(e) => setGenerateLabel(e.target.checked)}
>
{t("bills.labels.generatepartslabel")}
</Checkbox>
<Button onClick={handleCancel}>{t("general.actions.cancel")}</Button>
<Button loading={loading} onClick={() => form.submit()}>
{t("general.actions.save")}

View File

@@ -171,7 +171,7 @@ export function BillEnterModalLinesComponent({
};
},
formInput: (record, index) => (
<InputNumber precision={0} min={0} disabled={disabled} />
<InputNumber precision={0} min={1} disabled={disabled} />
),
},
{

View File

@@ -39,7 +39,13 @@ export function ContractsFindModalContainer({
if (!claim || !shortclaim) return;
const trimmedShortClaim = shortclaim.trim();
// const trimmedClaim = claim.trim();
claimNumbers.push({ claim: trimmedShortClaim, amount });
if (amount.slice(-1) === "-") {
}
claimNumbers.push({
claim: trimmedShortClaim,
amount: amount.slice(-1) === "-" ? parseFloat(amount) * -1 : amount,
});
});
await GenerateDocument(

View File

@@ -9,6 +9,7 @@ import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
import JobDocumentsLocalGalleryExternal from "../jobs-documents-local-gallery/jobs-documents-local-gallery.external.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({
@@ -58,17 +59,24 @@ export function ChatMediaSelector({
{selectedMedia.filter((s) => s.isSelected).length >= 10 ? (
<div style={{ color: "red" }}>{t("messaging.labels.maxtenimages")}</div>
) : null}
{data && (
{!bodyshop.uselocalmediaserver && data && (
<JobDocumentsGalleryExternal
data={data ? data.documents : []}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{bodyshop.uselocalmediaserver && visible && (
<JobDocumentsLocalGalleryExternal
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={
conversation.job_conversations[0] &&
conversation.job_conversations[0].jobid
}
/>
)}
</div>
);
if (bodyshop.uselocalmediaserver) return null;
return (
<Popover
content={

View File

@@ -0,0 +1,152 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Form, Input, Table } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(DmsAllocationsSummaryAp);
export function DmsAllocationsSummaryAp({ socket, bodyshop, billids, title }) {
const { t } = useTranslation();
const [allocationsSummary, setAllocationsSummary] = useState([]);
useEffect(() => {
socket.on("ap-export-success", (billid) => {
setAllocationsSummary((allocationsSummary) =>
allocationsSummary.map((a) => {
if (a.billid !== billid) return a;
return { ...a, status: "Successful" };
})
);
});
socket.on("ap-export-failure", ({ billid, error }) => {
allocationsSummary.map((a) => {
if (a.billid !== billid) return a;
return { ...a, status: error };
});
});
if (socket.disconnected) socket.connect();
return () => {
socket.removeListener("ap-export-success");
socket.removeListener("ap-export-failure");
//socket.disconnect();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
if (socket.connected) {
socket.emit("pbs-calculate-allocations-ap", billids, (ack) => {
setAllocationsSummary(ack);
socket.allocationsSummary = ack;
});
}
}, [socket, socket.connected, billids]);
console.log(allocationsSummary);
const columns = [
{
title: t("general.labels.status"),
dataIndex: "status",
key: "status",
},
{
title: t("jobs.fields.ro_number"),
dataIndex: ["Posting", "Reference"],
key: "reference",
},
{
title: t("jobs.fields.dms.lines"),
dataIndex: "Lines",
key: "Lines",
render: (text, record) => (
<table style={{ tableLayout: "auto", width: "100%" }}>
<tr>
<th>{t("bills.fields.invoice_number")}</th>
<th>{t("bodyshop.fields.dms.dms_acctnumber")}</th>
<th>{t("jobs.fields.dms.amount")}</th>
</tr>
{record.Posting.Lines.map((l, idx) => (
<tr key={idx}>
<td>{l.InvoiceNumber}</td>
<td>{l.Account}</td>
<td>{l.Amount}</td>
</tr>
))}
</table>
),
},
];
const handleFinish = async (values) => {
socket.emit(`pbs-export-ap`, {
billids,
txEnvelope: values,
});
};
return (
<Card
title={title}
extra={
<Button
onClick={() => {
socket.emit("pbs-calculate-allocations-ap", billids, (ack) =>
setAllocationsSummary(ack)
);
}}
>
<SyncOutlined />
</Button>
}
>
<Table
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}
rowKey={(record) => `${record.InvoiceNumber}${record.Account}`}
dataSource={allocationsSummary}
locale={{ emptyText: t("dms.labels.refreshallocations") }}
/>
<Form layout="vertical" onFinish={handleFinish}>
<Form.Item
name="journal"
label={t("jobs.fields.dms.journal")}
initialValue={
bodyshop.cdk_configuration &&
bodyshop.cdk_configuration.default_journal
}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Button
disabled={!allocationsSummary || allocationsSummary.length === 0}
htmlType="submit"
>
{t("jobs.actions.dms.post")}
</Button>
</Form>
</Card>
);
}

View File

@@ -1,4 +1,4 @@
import { Button, Card, Table, Typography } from "antd";
import { Alert, Button, Card, Table, Typography } from "antd";
import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -90,6 +90,9 @@ export function DmsAllocationsSummary({ socket, bodyshop, jobId, title }) {
</Button>
}
>
{bodyshop.pbs_configuration?.disablebillwip && (
<Alert type="warning" message={t("jobs.labels.dms.disablebillwip")} />
)}
<Table
pagination={{ position: "top", defaultPageSize: 50 }}
columns={columns}

View File

@@ -23,6 +23,7 @@ export function DocumentsLocalUploadComponent({
vendorid,
invoice_number,
callbackAfterUpload,
allowAllTypes,
}) {
const [fileList, setFileList] = useState([]);
@@ -52,7 +53,9 @@ export function DocumentsLocalUploadComponent({
},
})
}
accept="audio/*, video/*, image/*, .pdf, .doc, .docx, .xls, .xlsx"
{...(!allowAllTypes && {
accept: "audio/*, video/*, image/*, .pdf, .doc, .docx, .xls, .xlsx",
})}
>
{children || (
<>

View File

@@ -2,6 +2,8 @@ import cleanAxios from "../../utils/CleanAxios";
import { store } from "../../redux/store";
import { addMediaForJob } from "../../redux/media/media.actions";
import normalizeUrl from "normalize-url";
import { notification } from "antd";
import i18n from "i18next";
export const handleUpload = async ({ ev, context }) => {
const { onError, onSuccess, onProgress, file } = ev;
@@ -45,6 +47,11 @@ export const handleUpload = async ({ ev, context }) => {
}
} else {
onSuccess && onSuccess(file);
notification.open({
type: "success",
key: "docuploadsuccess",
message: i18n.t("documents.successes.insert"),
});
store.dispatch(
addMediaForJob({
jobid,
@@ -53,6 +60,11 @@ export const handleUpload = async ({ ev, context }) => {
...d,
selected: false,
src: normalizeUrl(`${bodyshop.localmediaserverhttp}/${d.src}`),
...(d.optimized && {
optimized: normalizeUrl(
`${bodyshop.localmediaserverhttp}/${d.optimized}`
),
}),
thumbnail: normalizeUrl(
`${bodyshop.localmediaserverhttp}/${d.thumbnail}`
),

View File

@@ -5,12 +5,15 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
import { selectEmailConfig } from "../../redux/email/email.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
import JobsDocumentsLocalGalleryExternalComponent from "../jobs-documents-local-gallery/jobs-documents-local-gallery.external.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
emailConfig: selectEmailConfig,
});
const mapDispatchToProps = (dispatch) => ({
@@ -25,6 +28,7 @@ export function EmailDocumentsComponent({
emailConfig,
form,
selectedMediaState,
bodyshop,
}) {
const { t } = useTranslation();
@@ -52,12 +56,18 @@ export function EmailDocumentsComponent({
10485760 - new Blob([form.getFieldValue("html")]).size ? (
<div style={{ color: "red" }}>{t("general.errors.sizelimit")}</div>
) : null}
{data && (
{!bodyshop.uselocalmediaserver && data && (
<JobDocumentsGalleryExternal
data={data ? data.documents : []}
externalMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
{bodyshop.uselocalmediaserver && (
<JobsDocumentsLocalGalleryExternalComponent
externalMediaState={[selectedMedia, setSelectedMedia]}
jobId={emailConfig.jobid}
/>
)}
</div>
);
}

View File

@@ -160,14 +160,13 @@ export function EmailOverlayComponent({
</Form.Item>
<Tabs>
{!bodyshop.uselocalmediaserver && (
<Tabs.TabPane tab={t("emails.labels.documents")} key="documents">
<EmailDocumentsComponent
selectedMediaState={selectedMediaState}
form={form}
/>
</Tabs.TabPane>
)}
<Tabs.TabPane tab={t("emails.labels.documents")} key="documents">
<EmailDocumentsComponent
selectedMediaState={selectedMediaState}
form={form}
/>
</Tabs.TabPane>
<Tabs.TabPane tab={t("emails.labels.attachments")} key="attachments">
{bodyshop.uselocalmediaserver && emailConfig.jobid && (
<a href={CreateExplorerLinkForJob({ jobid: emailConfig.jobid })}>

View File

@@ -1,4 +1,4 @@
import { Divider, Form, Modal, notification } from "antd";
import { Button, Divider, Form, Modal, notification, Space } from "antd";
import axios from "axios";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -77,6 +77,9 @@ export function EmailOverlayContainer({
setSending(true);
try {
await axios.post("/sendemail", {
bodyshopid: bodyshop.id,
jobid: emailConfig.jobid,
...defaultEmailFrom,
ReplyTo: {
Email: from,
@@ -172,36 +175,77 @@ export function EmailOverlayContainer({
<Modal
destroyOnClose={true}
visible={modalVisible}
maskClosable={false}
width={"80%"}
onOk={() => form.submit()}
title={t("emails.labels.emailpreview")}
onCancel={() => {
toggleEmailOverlayVisible();
}}
//closeIcon={() => null}
okText={t("general.actions.send")}
okButtonProps={{
loading: sending,
disabled:
selectedMedia &&
( (selectedMedia
(selectedMedia
.filter((s) => s.isSelected)
.reduce((acc, val) => (acc = acc + val.size), 0) >=
10485760 - new Blob([form.getFieldValue("html")]).size) || selectedMedia.filter((s) => s.isSelected).length > 10),
10485760 - new Blob([form.getFieldValue("html")]).size ||
selectedMedia.filter((s) => s.isSelected).length > 10),
}}
>
<Form layout="vertical" form={form} onFinish={handleFinish}>
{loading && (
<div>
<LoadingSkeleton />
<Divider>{t("emails.labels.preview")}</Divider>
<LoadingSpinner message={t("emails.labels.generatingemail")} />
</div>
)}
{!loading && (
<EmailOverlayComponent
form={form}
selectedMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
</Form>
<div>
<div
style={{
// marginTop: "3rem",
display: "flex",
justifyContent: "flex-end",
}}
>
<Space style={{ alignSelf: "flex-end" }} align="right">
<Button
onClick={() => {
toggleEmailOverlayVisible();
}}
>
{t("general.actions.cancel")}
</Button>
<Button
loading={sending}
onClick={() => form.submit()}
disabled={
selectedMedia &&
(selectedMedia
.filter((s) => s.isSelected)
.reduce((acc, val) => (acc = acc + val.size), 0) >=
10485760 - new Blob([form.getFieldValue("html")]).size ||
selectedMedia.filter((s) => s.isSelected).length > 10)
}
type="primary"
>
{t("general.actions.send")}
</Button>
</Space>
</div>
<Form layout="vertical" form={form} onFinish={handleFinish}>
{loading && (
<div>
<LoadingSkeleton />
<Divider>{t("emails.labels.preview")}</Divider>
<LoadingSpinner message={t("emails.labels.generatingemail")} />
</div>
)}
{!loading && (
<EmailOverlayComponent
form={form}
selectedMediaState={[selectedMedia, setSelectedMedia]}
/>
)}
</Form>
</div>
</Modal>
);
}

View File

@@ -12,7 +12,7 @@ import Icon, {
FileAddFilled,
FileAddOutlined,
FileFilled,
GlobalOutlined,
//GlobalOutlined,
HomeFilled,
ImportOutlined,
LineChartOutlined,
@@ -89,6 +89,11 @@ function Header({
{},
bodyshop && bodyshop.imexshopid
);
const { DmsAp } = useTreatments(
["DmsAp"],
{},
bodyshop && bodyshop.imexshopid
);
const { t } = useTranslation();
@@ -264,10 +269,11 @@ function Header({
{t("menus.header.accounting-receivables")}
</Link>
</Menu.Item>
{!(
{(!(
(bodyshop && bodyshop.cdk_dealerid) ||
(bodyshop && bodyshop.pbs_serialnumber)
) && (
) ||
DmsAp.treatment === "on") && (
<Menu.Item key="payables">
<Link to="/manage/accounting/payables">
{t("menus.header.accounting-payables")}
@@ -368,25 +374,27 @@ function Header({
<Menu.Item key="profile">
<Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
</Menu.Item>
<Menu.SubMenu
key="langselecter"
title={
<span>
<GlobalOutlined />
<span>{t("menus.currentuser.languageselector")}</span>
</span>
}
>
<Menu.Item actiontype="lang-select" key="en-US">
{t("general.languages.english")}
</Menu.Item>
<Menu.Item actiontype="lang-select" key="fr-CA">
{t("general.languages.french")}
</Menu.Item>
<Menu.Item actiontype="lang-select" key="es-MX">
{t("general.languages.spanish")}
</Menu.Item>
</Menu.SubMenu>
{
// <Menu.SubMenu
// key="langselecter"
// title={
// <span>
// <GlobalOutlined />
// <span>{t("menus.currentuser.languageselector")}</span>
// </span>
// }
// >
// <Menu.Item actiontype="lang-select" key="en-US">
// {t("general.languages.english")}
// </Menu.Item>
// <Menu.Item actiontype="lang-select" key="fr-CA">
// {t("general.languages.french")}
// </Menu.Item>
// <Menu.Item actiontype="lang-select" key="es-MX">
// {t("general.languages.spanish")}
// </Menu.Item>
// </Menu.SubMenu>
}
</Menu.SubMenu>
<Menu.SubMenu key="recent" title={<ClockCircleFilled />}>
{recentItems.map((i, idx) => (

View File

@@ -19,7 +19,7 @@ const mapDispatchToProps = (dispatch) => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(Jobd3RdPartyModal);
export function Jobd3RdPartyModal({ bodyshop, jobId }) {
export function Jobd3RdPartyModal({ bodyshop, jobId, job }) {
const [isModalVisible, setIsModalVisible] = useState(false);
const { t } = useTranslation();
const [form] = Form.useForm();
@@ -33,6 +33,11 @@ export function Jobd3RdPartyModal({ bodyshop, jobId }) {
);
const showModal = () => {
form.setFieldsValue({
ded_amt: job.ded_amt,
depreciation: job.depreciation_taxes,
custgst: job.ca_customer_gst,
});
setIsModalVisible(true);
};
@@ -42,6 +47,7 @@ export function Jobd3RdPartyModal({ bodyshop, jobId }) {
};
const handleCancel = () => {
form.resetFields();
setIsModalVisible(false);
};
const handleFinish = (values) => {

View File

@@ -1,13 +1,24 @@
import { useQuery } from "@apollo/client";
import { Card, Table } from "antd";
import { Button, Card, Col, Row, Table, Tag } from "antd";
import { SyncOutlined } from "@ant-design/icons";
import React from "react";
import { useTranslation } from "react-i18next";
import { QUERY_AUDIT_TRAIL } from "../../graphql/audit_trail.queries";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectCurrentUser } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(JobAuditTrail);
export default function JobAuditTrail({ jobId }) {
export function JobAuditTrail({ currentUser, jobId }) {
const { t } = useTranslation();
const { loading, data } = useQuery(QUERY_AUDIT_TRAIL, {
const { loading, data, refetch } = useQuery(QUERY_AUDIT_TRAIL, {
variables: { jobid: jobId },
skip: !jobId,
fetchPolicy: "network-only",
@@ -34,15 +45,109 @@ export default function JobAuditTrail({ jobId }) {
key: "operation",
},
];
const emailColumns = [
{
title: t("audit.fields.created"),
dataIndex: " created_at",
key: " created_at",
render: (text, record) => (
<DateTimeFormatter>{record.created_at}</DateTimeFormatter>
),
},
{
title: t("audit.fields.useremail"),
dataIndex: "useremail",
key: "useremail",
},
{
title: t("audit.fields.to"),
dataIndex: "to",
key: "to",
render: (text, record) =>
record.to &&
record.to.map((email, idx) => <Tag key={idx}>{email}</Tag>),
},
{
title: t("audit.fields.cc"),
dataIndex: "cc",
key: "cc",
render: (text, record) =>
record.cc &&
record.cc.map((email, idx) => <Tag key={idx}>{email}</Tag>),
},
{
title: t("audit.fields.subject"),
dataIndex: "subject",
key: "subject",
},
{
title: t("audit.fields.status"),
dataIndex: "status",
key: "status",
},
...(currentUser?.email.includes("@imex.")
? [
{
title: t("audit.fields.contents"),
dataIndex: "contents",
key: "contents",
width: "10%",
render: (text, record) => (
<Button
onClick={() => {
var win = window.open(
"",
"Title",
"toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=780,height=400,"
);
win.document.body.innerHTML = record.contents;
}}
>
Preview
</Button>
),
},
]
: []),
];
return (
<Card title={t("jobs.labels.audit")}>
<Table
loading={loading}
columns={columns}
rowKey="id"
dataSource={data ? data.audit_trail : []}
/>
</Card>
<Row gutter={[16, 16]}>
<Col span={24}>
<Card
title={t("jobs.labels.audit")}
extra={
<Button
onClick={() => {
refetch();
}}
>
<SyncOutlined />
</Button>
}
>
<Table
loading={loading}
columns={columns}
rowKey="id"
dataSource={data ? data.audit_trail : []}
/>
</Card>
</Col>
<Col span={24}>
<Card title={t("jobs.labels.emailaudit")}>
<Table
loading={loading}
columns={emailColumns}
rowKey="id"
dataSource={data ? data.email_audit_trail : []}
/>
</Card>
</Col>
</Row>
);
}

View File

@@ -7,7 +7,10 @@ import { connect } from "react-redux";
import { useHistory, useLocation, useParams } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../../../firebase/firebase.utils";
import { MARK_LATEST_APPOINTMENT_AS_ARRIVED } from "../../../../graphql/appointments.queries";
import {
MARK_APPOINTMENT_ARRIVED,
MARK_LATEST_APPOINTMENT_ARRIVED,
} from "../../../../graphql/appointments.queries";
import { UPDATE_JOB } from "../../../../graphql/jobs.queries";
import {
selectBodyshop,
@@ -41,7 +44,8 @@ export function JobChecklistForm({
const { t } = useTranslation();
const [intakeJob] = useMutation(UPDATE_JOB);
const [loading, setLoading] = useState(false);
const [markAptArrived] = useMutation(MARK_LATEST_APPOINTMENT_AS_ARRIVED);
const [markAptArrived] = useMutation(MARK_APPOINTMENT_ARRIVED);
const [markLatestAptArrived] = useMutation(MARK_LATEST_APPOINTMENT_ARRIVED);
const [updateOwner] = useMutation(UPDATE_OWNER);
const { jobId } = useParams();
@@ -125,6 +129,18 @@ export function JobChecklistForm({
variables: { appointmentId: search.appointmentId },
});
if (!!appUpdate.errors) {
notification["error"]({
message: t("checklist.errors.complete", {
error: JSON.stringify(result.errors),
}),
});
}
} else if (type === "intake" && !search.appointmentId) {
const appUpdate = await markLatestAptArrived({
variables: { jobId: jobId },
});
if (!!appUpdate.errors) {
notification["error"]({
message: t("checklist.errors.complete", {
@@ -133,6 +149,7 @@ export function JobChecklistForm({
});
}
}
if (type === "intake" && job.owner && job.owner.id) {
//Updae Owner Allow to Text
const updateOwnerResult = await updateOwner({
@@ -175,12 +192,7 @@ export function JobChecklistForm({
});
}
};
console.log(job, {
removeFromProduction: true,
actual_completion:
job && job.actual_completion && moment(job.actual_completion),
actual_delivery: job && job.actual_delivery && moment(job.actual_delivery),
});
return (
<Card
title={t("checklist.labels.checklist")}

View File

@@ -1,11 +1,12 @@
import { useQuery } from "@apollo/client";
import { Row, Col, Timeline, Typography, Space, Divider, Skeleton } from "antd";
import { Col, Divider, Row, Skeleton, Space, Timeline, Typography } from "antd";
import React from "react";
import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
import { useTranslation } from "react-i18next";
import AlertComponent from "../alert/alert.component";
import { DateFormatter } from "../../utils/DateFormatter";
import { Link } from "react-router-dom";
import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { DateFormatter } from "../../utils/DateFormatter";
import AlertComponent from "../alert/alert.component";
export default function JobLinesExpander({ jobline, jobid }) {
const { t } = useTranslation();
@@ -48,7 +49,46 @@ export default function JobLinesExpander({ jobline, jobid }) {
)}
</Timeline>
</Col>
<Col md={24} lg={12}></Col>
<Col md={24} lg={12}>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<Timeline>
{data.billlines.length > 0 ? (
data.billlines.map((line) => (
<Timeline.Item key={line.id}>
<Row wrap>
<Col span={4}>
<Link
to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}
>
{line.bill.invoice_number}
</Link>
</Col>
<Col span={4}>
<span>
{`${t("billlines.fields.actual_price")}: `}
<CurrencyFormatter>{line.actual_price}</CurrencyFormatter>
</span>
</Col>
<Col span={4}>
<span>
{`${t("billlines.fields.actual_cost")}: `}
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
</span>
</Col>
<Col span={4}>
<DateFormatter>{line.bill.date}</DateFormatter>
</Col>
<Col span={4}> {line.bill.vendor.name}</Col>
</Row>
</Timeline.Item>
))
) : (
<Timeline.Item>
{t("parts_orders.labels.notyetordered")}
</Timeline.Item>
)}
</Timeline>
</Col>
</Row>
);
}

View File

@@ -1,4 +1,5 @@
import { useMutation, useLazyQuery } from "@apollo/client";
import { CheckCircleOutlined } from "@ant-design/icons";
import {
Button,
Card,
@@ -141,6 +142,12 @@ export default function ScoreboardAddButton({
</Form>
)}
</div>
{entryData && entryData.scoreboard && entryData.scoreboard[0] && (
<Space>
<CheckCircleOutlined style={{ color: "green" }} />
<span>{t("jobs.labels.alreadyaddedtoscoreboard")}</span>
</Space>
)}
</Card>
);

View File

@@ -95,7 +95,7 @@ mutation UNVOID_JOB($jobId: uuid!) {
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_unvoicejob(),
operation: AuditTrailMapping.admin_jobunvoid(),
});
} else {
notification["error"]({

View File

@@ -205,7 +205,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(JobsCloseLines);
const HasBeenConvertedTolabor = ({ value }) => {
const { t } = useTranslation();
console.log(value);
if (!value) return null;
return (
<Tooltip title={t("joblines.labels.convertedtolabor")}>

View File

@@ -0,0 +1,163 @@
import { useMutation } from "@apollo/client";
import {
Button,
Card,
Form,
Input,
Menu,
notification,
Popover,
Select,
Space,
} from "antd";
import moment from "moment";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_MANUAL_APPT } from "../../graphql/appointments.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDetailHeaderAddEvent);
export function JobsDetailHeaderAddEvent({ bodyshop, jobid, ...props }) {
const { t } = useTranslation();
const [insertAppointment] = useMutation(INSERT_MANUAL_APPT);
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
const [visibility, setVisibility] = useState(false);
const handleFinish = async (values) => {
logImEXEvent("schedule_manual_event");
setLoading(true);
try {
insertAppointment({
variables: {
apt: { ...values, isintake: false, jobid, bodyshopid: bodyshop.id },
},
refetchQueries: ["QUERY_ALL_ACTIVE_APPOINTMENTS"],
});
notification.open({
type: "success",
message: t("appointments.successes.created"),
});
} catch (error) {
console.log(error);
} finally {
setLoading(false);
setVisibility(false);
}
};
const overlay = (
<Card>
<div>
<Form form={form} layout="vertical" onFinish={handleFinish}>
<Form.Item
label={t("appointments.fields.title")}
name="title"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item label={t("appointments.fields.note")} name="note">
<Input />
</Form.Item>
<Form.Item
label={t("appointments.fields.start")}
name="start"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<FormDateTimePickerComponent
onBlur={() => {
const start = form.getFieldValue("start");
form.setFieldsValue({ end: start.add(30, "minutes") });
}}
/>
</Form.Item>
<Form.Item
label={t("appointments.fields.end")}
name="end"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
async validator(rule, value) {
if (value) {
const { start } = form.getFieldsValue();
if (moment(start).isAfter(moment(value))) {
return Promise.reject(
t("employees.labels.endmustbeafterstart")
);
} else {
return Promise.resolve();
}
} else {
return Promise.resolve();
}
},
}),
]}
>
<FormDateTimePickerComponent />
</Form.Item>
<Form.Item label={t("appointments.fields.color")} name="color">
<Select>
{bodyshop.appt_colors.map((col, idx) => (
<Select.Option key={idx} value={col.color.hex}>
{col.label}
</Select.Option>
))}
</Select>
</Form.Item>
<Space wrap>
<Button type="primary" htmlType="submit" loading={loading}>
{t("general.actions.save")}
</Button>
<Button onClick={() => setVisibility(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</Form>
</div>
</Card>
);
const handleClick = (e) => {
setVisibility(true);
};
return (
<Popover content={overlay} visible={visibility}>
<Menu.Item {...props} onClick={handleClick}>
{t("appointments.labels.manualevent")}
</Menu.Item>
</Popover>
);
}

View File

@@ -15,6 +15,7 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import JobsDetailHeaderActionsAddevent from "./jobs-detail-header-actions.addevent";
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
import JobsDetaiLheaderCsi from "./jobs-detail-header-actions.csi.component";
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
@@ -421,6 +422,7 @@ export function JobsDetailHeaderActions({
</Popconfirm>
</Menu.Item>
)}
<JobsDetailHeaderActionsAddevent jobid={job.id} />
{!jobRO && job.converted && (
<Menu.Item>
<Popconfirm

View File

@@ -2,8 +2,9 @@ import {
ExclamationCircleFilled,
PauseCircleOutlined,
WarningFilled,
BranchesOutlined,
} from "@ant-design/icons";
import { Card, Col, Row, Space, Tag } from "antd";
import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -78,6 +79,13 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
{job.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
{job.iouparent && (
<Link to={`/manage/jobs/${job.iouparent}`}>
<Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} />
</Tooltip>
</Link>
)}
{job.production_vars && job.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" />
) : null}

View File

@@ -1,14 +1,11 @@
import { useApolloClient, useMutation } from "@apollo/client";
import { useApolloClient } from "@apollo/client";
import { Button, Form, notification, Popover, Space } from "antd";
import axios from "axios";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
GET_DOC_SIZE_BY_JOB,
UPDATE_DOCUMENT,
} from "../../graphql/documents.queries";
import { GET_DOC_SIZE_BY_JOB } from "../../graphql/documents.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import JobSearchSelect from "../job-search-select/job-search-select.component";
@@ -23,7 +20,11 @@ export default connect(
mapDispatchToProps
)(JobsDocumentsGalleryReassign);
export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) {
export function JobsDocumentsGalleryReassign({
bodyshop,
galleryImages,
callback,
}) {
const { t } = useTranslation();
const [form] = Form.useForm();
@@ -36,34 +37,33 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) {
const client = useApolloClient();
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const [updateDocument] = useMutation(UPDATE_DOCUMENT);
const updateImage = async (i, jobid) => {
//Move the cloudinary image
// const updateImage = async (i, jobid) => {
// //Move the cloudinary image
//Update it in the database.
const result = await updateDocument({
variables: {
id: i.id,
document: {
key: i.public_id,
jobid: jobid,
},
},
});
// //Update it in the database.
// const result = await updateDocument({
// variables: {
// id: i.id,
// document: {
// key: i.public_id,
// jobid: jobid,
// },
// },
// });
if (!!result.errors) {
notification["error"]({
message: t("documents.errors.updating", {
message: JSON.stringify(result.errors),
}),
});
} else {
notification["success"]({
message: t("documents.successes.updated"),
});
}
};
// if (!!result.errors) {
// notification["error"]({
// message: t("documents.errors.updating", {
// message: JSON.stringify(result.errors),
// }),
// });
// } else {
// notification["success"]({
// message: t("documents.successes.updated"),
// });
// }
// };
const handleFinish = async ({ jobid }) => {
setLoading(true);
@@ -96,6 +96,7 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) {
}
const res = await axios.post("/media/rename", {
tojobid: jobid,
documents: selectedImages.map((i) => {
//Need to check if the current key folder is null, or another job.
const currentKeys = i.key.split("/");
@@ -110,24 +111,21 @@ export function JobsDocumentsGalleryReassign({ bodyshop, galleryImages }) {
};
}),
});
//Add in confirmation & errors.
if (callback) callback();
res.data
.filter((d) => d.error)
.forEach((d) => {
notification["error"]({ message: t("documents.errors.updating") });
console.error("Error updating job document", d);
if (res.errors) {
notification["error"]({
message: t("documents.errors.updating", {
message: JSON.stringify(res.errors),
}),
});
const proms = [];
res.data
.filter((d) => !d.error)
.forEach((d) => {
proms.push(updateImage(d, jobid));
}
if (!res.mutationResult?.errors) {
notification["success"]({
message: t("documents.successes.updated"),
});
await Promise.all(proms);
}
setVisible(false);
setLoading(false);
};

View File

@@ -125,7 +125,10 @@ function JobsDocumentsComponent({
deletionCallback={billsCallback || refetch}
/>
{!billId && (
<JobsDocumentsGalleryReassign galleryImages={galleryImages} />
<JobsDocumentsGalleryReassign
galleryImages={galleryImages}
callback={refetch}
/>
)}
</Space>
</Col>

View File

@@ -1,11 +1,9 @@
import { QuestionCircleOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, notification, Popconfirm } from "antd";
import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { DELETE_DOCUMENTS } from "../../graphql/documents.queries";
//Context: currentUserEmail, bodyshop, jobid, invoiceid
export default function JobsDocumentsDeleteButton({
@@ -13,7 +11,7 @@ export default function JobsDocumentsDeleteButton({
deletionCallback,
}) {
const { t } = useTranslation();
const [deleteDocument] = useMutation(DELETE_DOCUMENTS);
const imagesToDelete = [
...galleryImages.images.filter((image) => image.isSelected),
...galleryImages.other.filter((image) => image.isSelected),
@@ -27,31 +25,10 @@ export default function JobsDocumentsDeleteButton({
ids: imagesToDelete,
});
const successfulDeletes = [];
res.data.forEach((resType) => {
Object.keys(resType.deleted).forEach((key) => {
if (resType.deleted[key] !== "deleted") {
notification["error"]({
message: t("documents.errors.deleting_cloudinary", {
message: JSON.stringify(resType.deleted[key]),
}),
});
} else {
successfulDeletes.push(key.replace(/\.[^/.]+$/, ""));
}
});
});
const delres = await deleteDocument({
variables: {
ids: imagesToDelete
.filter((i) => successfulDeletes.includes(i.key))
.map((i) => i.id),
},
});
if (delres.errors) {
if (res.data.error) {
notification["error"]({
message: t("documents.errors.deleting", {
message: JSON.stringify(delres.errors),
error: JSON.stringify(res.data.error.response.errors),
}),
});
} else {

View File

@@ -1,5 +1,5 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Space } from "antd";
import { SyncOutlined, FileExcelFilled } from "@ant-design/icons";
import { Alert, Button, Card, Space } from "antd";
import React, { useEffect } from "react";
import Gallery from "react-grid-gallery";
import { useTranslation } from "react-i18next";
@@ -14,6 +14,7 @@ import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
import DocumentsLocalUploadComponent from "../documents-local-upload/documents-local-upload.component";
import JobsDocumentsLocalDeleteButton from "./jobs-documents-local-gallery.delete.component";
import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.download";
import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component";
import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component";
@@ -57,6 +58,32 @@ export function JobsDocumentsLocalGallery({
}
}
}, [job, invoice_number, getJobMedia, getBillMedia]);
let optimized;
const jobMedia =
allMedia && allMedia[job.id]
? allMedia[job.id].reduce(
(acc, val) => {
if (
val.type &&
val.type.mime &&
val.type.mime.startsWith("image")
) {
acc.images.push({
...val,
...(val.optimized && { src: val.optimized, fullsize: val.src }),
});
if (val.optimized) optimized = true;
} else {
acc.other.push({
...val,
tags: [{ value: val.filename, title: val.filename }],
});
}
return acc;
},
{ images: [], other: [] }
)
: { images: [], other: [] };
return (
<div>
@@ -80,30 +107,70 @@ export function JobsDocumentsLocalGallery({
<JobsDocumentsLocalGalleryReassign jobid={job.id} />
<JobsDocumentsLocalGallerySelectAllComponent jobid={job.id} />
<JobsLocalGalleryDownloadButton job={job} />
<JobsDocumentsLocalDeleteButton jobid={job.id} />
</Space>
<Card>
<DocumentsLocalUploadComponent
job={job}
invoice_number={invoice_number}
vendorid={vendorid}
allowAllTypes
/>
</Card>
<Card title={t("jobs.labels.documents-images")}>
<Gallery
images={(allMedia && allMedia[job.id]) || []}
images={jobMedia.images}
backdropClosesModal={true}
onSelectImage={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
{...(optimized && {
customControls: [
<Alert
style={{ margin: "4px" }}
message={t("documents.labels.optimizedimage")}
type="success"
/>,
],
})}
onClickImage={(props) => {
const media = allMedia[job.id].find(
(m) => m.optimized === props.target.src
);
window.open(
props.target.src,
media ? media.src : props.target.src,
"_blank",
"toolbar=0,location=0,menubar=0"
);
}}
/>
</Card>
<Card title={t("jobs.labels.documents-other")}>
<Gallery
images={jobMedia.other}
backdropClosesModal={true}
enableLightbox={false}
thumbnailStyle={() => {
return {
backgroundImage: <FileExcelFilled />,
height: "100%",
width: "100%",
cursor: "pointer",
};
}}
onClickThumbnail={(index) => {
window.open(
jobMedia.other[index].src,
"_blank",
"toolbar=0,location=0,menubar=0"
);
}}
onSelectImage={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
/>
</Card>
</div>
);
}

View File

@@ -0,0 +1,81 @@
import { QuestionCircleOutlined } from "@ant-design/icons";
import { Button, notification, Popconfirm } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import cleanAxios from "../../utils/CleanAxios";
//Context: currentUserEmail, bodyshop, jobid, invoiceid
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { getJobMedia } from "../../redux/media/media.actions";
import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
allMedia: selectAllMedia,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
getJobMedia: (id) => dispatch(getJobMedia(id)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDocumentsLocalDeleteButton);
export function JobsDocumentsLocalDeleteButton({
bodyshop,
getJobMedia,
allMedia,
jobid,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleDelete = async () => {
logImEXEvent("job_documents_delete");
setLoading(true);
const delres = await cleanAxios.post(
`${bodyshop.localmediaserverhttp}/jobs/delete`,
{
jobid: jobid,
files: ((allMedia && allMedia[jobid]) || [])
.filter((i) => i.isSelected)
.map((i) => i.filename),
},
{ headers: { ims_token: bodyshop.localmediatoken } }
);
if (delres.errors) {
notification["error"]({
message: t("documents.errors.deleting", {
message: JSON.stringify(delres.errors),
}),
});
} else {
notification.open({
key: "docdeletedsuccesfully",
type: "success",
message: t("documents.successes.delete"),
});
}
getJobMedia(jobid);
setLoading(false);
};
return (
<Popconfirm
icon={<QuestionCircleOutlined style={{ color: "red" }} />}
onConfirm={handleDelete}
title={t("documents.labels.confirmdelete")}
okText={t("general.actions.delete")}
okButtonProps={{ type: "danger" }}
cancelText={t("general.actions.cancel")}
>
<Button loading={loading}>{t("documents.actions.delete")}</Button>
</Popconfirm>
);
}

View File

@@ -0,0 +1,79 @@
import React, { useEffect } from "react";
import Gallery from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
getJobMedia,
toggleMediaSelected,
} from "../../redux/media/media.actions";
import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
allMedia: selectAllMedia,
});
const mapDispatchToProps = (dispatch) => ({
getJobMedia: (id) => dispatch(getJobMedia(id)),
toggleMediaSelected: ({ jobid, filename }) =>
dispatch(toggleMediaSelected({ jobid, filename })),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobDocumentsLocalGalleryExternal);
function JobDocumentsLocalGalleryExternal({
jobId,
externalMediaState,
getJobMedia,
toggleMediaSelected,
allMedia,
}) {
const [galleryImages, setgalleryImages] = externalMediaState;
const { t } = useTranslation();
useEffect(() => {
if ( jobId) {
getJobMedia(jobId);
}
}, [jobId, getJobMedia]);
useEffect(() => {
let documents =
allMedia && allMedia[jobId]
? allMedia[jobId].reduce((acc, val) => {
if (
val.type &&
val.type.mime &&
val.type.mime.startsWith("image")
) {
acc.push(val);
}
return acc;
}, [])
: [];
setgalleryImages(documents);
}, [allMedia, jobId, setgalleryImages, t]);
return (
<div className="clearfix">
<Gallery
images={galleryImages}
backdropClosesModal={true}
onSelectImage={(index, image) => {
setgalleryImages(
galleryImages.map((g, idx) =>
index === idx ? { ...g, isSelected: !g.isSelected } : g
)
);
}}
/>
</div>
);
}

View File

@@ -2,9 +2,10 @@ import {
SyncOutlined,
ExclamationCircleFilled,
PauseCircleOutlined,
BranchesOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table } from "antd";
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -128,6 +129,11 @@ export function JobsList({ bodyshop }) {
{record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
{record.iouparent && (
<Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} />
</Tooltip>
)}
</Space>
</Link>
),

View File

@@ -2,9 +2,10 @@ import {
ExclamationCircleFilled,
PauseCircleOutlined,
SyncOutlined,
BranchesOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table } from "antd";
import { Button, Card, Grid, Input, Space, Table, Tooltip } from "antd";
import queryString from "query-string";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -140,6 +141,11 @@ export function JobsReadyList({ bodyshop }) {
{record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
{record.iouparent && (
<Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} />
</Tooltip>
)}
</Space>
</Link>
),

View File

@@ -1,5 +1,5 @@
import { EditFilled } from "@ant-design/icons";
import { Card, Col, Row, Space, Table } from "antd";
import { Card, Col, Row, Space, Table, Typography } from "antd";
import _ from "lodash";
import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -188,6 +188,19 @@ export function LaborAllocationsTable({
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const summary =
totals &&
totals.reduce(
(acc, val) => {
acc.hrs_total += val.total;
acc.hrs_claimed += val.claimed;
acc.adjustments += val.adjustments;
acc.difference += val.difference;
return acc;
},
{ hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 }
);
return (
<Row gutter={[16, 16]}>
<Col span={24}>
@@ -201,6 +214,27 @@ export function LaborAllocationsTable({
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>
{summary.hrs_total.toFixed(1)}
</Table.Summary.Cell>
<Table.Summary.Cell>
{summary.hrs_claimed.toFixed(1)}
</Table.Summary.Cell>
<Table.Summary.Cell>
{summary.adjustments.toFixed(1)}
</Table.Summary.Cell>
<Table.Summary.Cell>
{summary.difference.toFixed(1)}
</Table.Summary.Cell>
</Table.Summary.Row>
)}
/>
</Card>
</Col>

View File

@@ -39,7 +39,7 @@ export const CalculateAllocationsTotals = (
}, 0),
};
r.difference = (r.total + r.adjustments - r.claimed).toFixed(2);
r.difference = r.total + r.adjustments - r.claimed;
acc.push(r);
return acc;
}, []);

View File

@@ -12,24 +12,24 @@ export default connect(mapStateToProps, mapDispatchToProps)(OwnerNameDisplay);
export function OwnerNameDisplay({ bodyshop, ownerObject }) {
const emptyTest =
ownerObject.ownr_fn + ownerObject.ownr_ln + ownerObject.ownr_co_nm;
ownerObject?.ownr_fn + ownerObject?.ownr_ln + ownerObject?.ownr_co_nm;
if (!emptyTest || emptyTest === "null" || emptyTest.trim() === "")
return "N/A";
if (bodyshop.last_name_first)
return `${ownerObject.ownr_ln || ""}, ${ownerObject.ownr_fn || ""} ${
ownerObject.ownr_co_nm || ""
return `${ownerObject?.ownr_ln || ""}, ${ownerObject?.ownr_fn || ""} ${
ownerObject?.ownr_co_nm || ""
}`.trim();
return `${ownerObject.ownr_fn || ""} ${ownerObject.ownr_ln || ""} ${
return `${ownerObject?.ownr_fn || ""} ${ownerObject?.ownr_ln || ""} ${
ownerObject.ownr_co_nm || ""
}`.trim();
}
export function OwnerNameDisplayFunction(ownerObject, forceFirstLast = false) {
const emptyTest =
ownerObject.ownr_fn + ownerObject.ownr_ln + ownerObject.ownr_co_nm;
ownerObject?.ownr_fn + ownerObject?.ownr_ln + ownerObject?.ownr_co_nm;
if (!emptyTest || emptyTest === "null" || emptyTest.trim() === "")
return "N/A";
@@ -37,11 +37,11 @@ export function OwnerNameDisplayFunction(ownerObject, forceFirstLast = false) {
const rdxStore = store.getState();
if (rdxStore.user.bodyshop.last_name_first && !forceFirstLast)
return `${ownerObject.ownr_ln || ""}, ${ownerObject.ownr_fn || ""} ${
ownerObject.ownr_co_nm || ""
return `${ownerObject?.ownr_ln || ""}, ${ownerObject?.ownr_fn || ""} ${
ownerObject?.ownr_co_nm || ""
}`.trim();
return `${ownerObject.ownr_fn || ""} ${ownerObject.ownr_ln || ""} ${
ownerObject.ownr_co_nm || ""
return `${ownerObject?.ownr_fn || ""} ${ownerObject?.ownr_ln || ""} ${
ownerObject?.ownr_co_nm || ""
}`.trim();
}

View File

@@ -72,7 +72,8 @@ export function PartsOrderListTableComponent({
? bpoints[selectedBreakpoint[0]]
: "100%";
const responsibilityCenters = bodyshop.md_responsibility_centers;
const Templates = TemplateList("partsorder");
const Templates = TemplateList("partsorder", { job });
const { t } = useTranslation();
const [state, setState] = useState({
sortedInfo: {},

View File

@@ -0,0 +1,97 @@
import { DownOutlined } from "@ant-design/icons";
import { Dropdown, InputNumber, Menu, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
export default function PartsOrderModalPriceChange({ form, field }) {
const { t } = useTranslation();
const menu = (
<Menu
onClick={({ key }) => {
if (key === "custom") return;
const values = form.getFieldsValue();
const { parts_order_lines } = values;
form.setFieldsValue({
parts_order_lines: {
data: parts_order_lines.data.map((p, idx) => {
if (idx !== field.name) return p;
return {
...p,
act_price: (p.act_price || 0) * ((100 - key) / 100),
};
}),
},
});
}}
items={[
{
key: "5",
label: t("parts_orders.labels.discount", { percent: "5%" }),
},
{
key: "10",
label: t("parts_orders.labels.discount", { percent: "10%" }),
},
{
key: "15",
label: t("parts_orders.labels.discount", { percent: "15%" }),
},
{
key: "20",
label: t("parts_orders.labels.discount", { percent: "20%" }),
},
{
key: "25",
label: t("parts_orders.labels.discount", { percent: "25%" }),
},
{
key: "custom",
label: (
<InputNumber
onClick={(e) => e.stopPropagation()}
addonAfter="%"
onKeyUp={(e) => {
if (e.key === "Enter") {
const values = form.getFieldsValue();
const { parts_order_lines } = values;
form.setFieldsValue({
parts_order_lines: {
data: parts_order_lines.data.map((p, idx) => {
if (idx !== field.name) return p;
console.log(
p,
e.target.value,
(p.act_price || 0) *
((100 - (e.target.value || 0)) / 100)
);
return {
...p,
act_price:
(p.act_price || 0) *
((100 - (e.target.value || 0)) / 100),
};
}),
},
});
e.target.value = 0;
}
}}
min={0}
max={100}
/>
),
},
]}
/>
);
return (
<Dropdown overlay={menu} trigger="click">
<Space>
%
<DownOutlined />
</Space>
</Dropdown>
);
}

View File

@@ -23,6 +23,7 @@ import CurrencyInput from "../form-items-formatted/currency-form-item.component"
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component";
import PartsOrderModalPriceChange from "./parts-order-modal-price-change.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -133,6 +134,21 @@ export function PartsOrderModalComponent({
<Checkbox />
</Form.Item>
)}
<Form.Item
name="order_type"
initialValue="parts_order"
label={t("parts_orders.labels.order_type")}
>
<Radio.Group disabled={sendType === "oec"}>
<Radio value={"parts_order"}>
{t("parts_orders.labels.parts_order")}
</Radio>
<Radio value={"sublet"}>
{t("parts_orders.labels.sublet_order")}
</Radio>
</Radio.Group>
</Form.Item>
</LayoutFormRow>
<Divider orientation="left">
{t("parts_orders.labels.inthisorder")}
@@ -246,7 +262,14 @@ export function PartsOrderModalComponent({
key={`${index}act_price`}
name={[field.name, "act_price"]}
>
<CurrencyInput />
<CurrencyInput
addonBefore={
<PartsOrderModalPriceChange
form={form}
field={field}
/>
}
/>
</Form.Item>
{isReturn && (
<Form.Item

View File

@@ -94,6 +94,7 @@ export function PartsOrderModalContainer({
const [updateJob] = useMutation(UPDATE_JOB);
const handleFinish = async ({
order_type,
removefrompartsqueue,
is_quote,
...values
@@ -102,47 +103,46 @@ export function PartsOrderModalContainer({
setSaving(true);
let insertResult;
insertResult = await insertPartOrder({
variables: {
po: [
{
...values,
order_date: moment().format("YYYY-MM-DD"),
orderedby: currentUser.email,
jobid: jobId,
user_email: currentUser.email,
return: isReturn,
status: is_quote
? bodyshop.md_order_statuses.default_quote || "Quote"
: bodyshop.md_order_statuses.default_ordered || "Ordered*",
},
],
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
insertResult = await insertPartOrder({
variables: {
po: [
{
...values,
order_date: moment().format("YYYY-MM-DD"),
orderedby: currentUser.email,
jobid: jobId,
user_email: currentUser.email,
return: isReturn,
status: is_quote
? bodyshop.md_order_statuses.default_quote || "Quote"
: bodyshop.md_order_statuses.default_ordered || "Ordered*",
},
],
},
refetchQueries: ["QUERY_PARTS_BILLS_BY_JOBID"],
});
if (!!insertResult.errors) {
notification["error"]({
message: t("parts_orders.errors.creating"),
description: JSON.stringify(insertResult.errors),
});
if (!!insertResult.errors) {
notification["error"]({
message: t("parts_orders.errors.creating"),
description: JSON.stringify(insertResult.errors),
});
return;
}
notification["success"]({
message: values.isReturn
? t("parts_orders.successes.return_created")
: t("parts_orders.successes.created"),
});
insertAuditTrail({
jobid: jobId,
operation: isReturn
? AuditTrailMapping.jobspartsreturn(
insertResult.data.insert_parts_orders.returning[0].order_number
)
: AuditTrailMapping.jobspartsorder(
insertResult.data.insert_parts_orders.returning[0].order_number
),
});
return;
}
notification["success"]({
message: values.isReturn
? t("parts_orders.successes.return_created")
: t("parts_orders.successes.created"),
});
insertAuditTrail({
jobid: jobId,
operation: isReturn
? AuditTrailMapping.jobspartsreturn(
insertResult.data.insert_parts_orders.returning[0].order_number
)
: AuditTrailMapping.jobspartsorder(
insertResult.data.insert_parts_orders.returning[0].order_number
),
});
const jobLinesResult = await updateJobLines({
variables: {
@@ -228,7 +228,9 @@ export function PartsOrderModalContainer({
{
name: isReturn
? Templates.parts_return_slip.key
: Templates.parts_order.key,
: order_type === "parts_order"
? Templates.parts_order.key
: Templates.sublet_order.key,
variables: {
id: insertResult.data.insert_parts_orders.returning[0].id,
},
@@ -238,7 +240,9 @@ export function PartsOrderModalContainer({
replyTo: bodyshop.email,
subject: isReturn
? Templates.parts_return_slip.subject
: Templates.parts_order.subject,
: order_type === "parts_order"
? Templates.parts_order.subject
: Templates.sublet_order.subject,
},
"e",
jobId
@@ -248,7 +252,9 @@ export function PartsOrderModalContainer({
{
name: isReturn
? Templates.parts_return_slip.key
: Templates.parts_order.key,
: order_type === "parts_order"
? Templates.parts_order.key
: Templates.sublet_order.key,
variables: {
id: insertResult.data.insert_parts_orders.returning[0].id,
},

View File

@@ -14,6 +14,7 @@ import {
import { logImEXEvent } from "../../firebase/firebase.utils";
import _ from "lodash";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { Link } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -176,6 +177,13 @@ export function PayableExportAll({
setLoading(false);
};
if (bodyshop.pbs_serialnumber)
return (
<Link to={{ state: { billids }, pathname: `/manage/dmsap` }}>
<Button>{t("jobs.actions.export")}</Button>
</Link>
);
return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.exportselected")}

View File

@@ -13,6 +13,7 @@ import {
} from "../../redux/user/user.selectors";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { Link } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -179,6 +180,13 @@ export function PayableExportButton({
setLoading(false);
};
if (bodyshop.pbs_serialnumber)
return (
<Link to={{ state: { billids: [billId] }, pathname: `/manage/dmsap` }}>
<Button>{t("jobs.actions.export")}</Button>
</Link>
);
return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")}

View File

@@ -1,5 +1,5 @@
import { gql, useMutation } from "@apollo/client";
import { Button, notification } from "antd";
import { Button, notification, Popconfirm } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -34,6 +34,7 @@ export function BillMarkSelectedExported({
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [visible, setVisible] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [updateBill] = useMutation(gql`
mutation UPDATE_BILL($billIds: [uuid!]!) {
@@ -84,11 +85,24 @@ export function BillMarkSelectedExported({
completedCallback && completedCallback([]);
setLoading(false);
refetch && refetch();
setVisible(false);
};
return (
<Button loading={loading} disabled={disabled} onClick={handleUpdate}>
{t("bills.labels.markexported")}
</Button>
<Popconfirm
visible={visible}
title={t("general.labels.areyousure")}
onCancel={() => setVisible(false)}
onConfirm={handleUpdate}
disabled={disabled}
>
<Button
loading={loading}
disabled={disabled}
onClick={() => setVisible(true)}
>
{t("bills.labels.markexported")}
</Button>
</Popconfirm>
);
}

View File

@@ -1,3 +1,4 @@
import { useTreatments } from "@splitsoftware/splitio-react";
import { CardElement } from "@stripe/react-stripe-js";
import { Checkbox, Form, Input, Radio, Select } from "antd";
import React from "react";
@@ -23,6 +24,11 @@ export function PaymentFormComponent({
disabled,
}) {
const [stripeState, setStripeState] = stripeStateArr;
const { Qb_Multi_Ar } = useTreatments(
["Qb_Multi_Ar"],
{},
bodyshop && bodyshop.imexshopid
);
const { t } = useTranslation();
const handleStripeChange = (e) => {
@@ -106,9 +112,21 @@ export function PaymentFormComponent({
<Select.Option value={t("payments.labels.customer")}>
{t("payments.labels.customer")}
</Select.Option>
<Select.Option value={t("payments.labels.insurance")}>
{t("payments.labels.insurance")}
</Select.Option>
{Qb_Multi_Ar.treatment === "on" ? (
<>
<Select.OptGroup label={t("payments.labels.external")}>
{bodyshop.md_ins_cos.map((i, idx) => (
<Select.Option key={idx} value={i.name}>
{i.name}
</Select.Option>
))}
</Select.OptGroup>
</>
) : (
<Select.Option value={t("payments.labels.insurance")}>
{t("payments.labels.insurance")}
</Select.Option>
)}
</Select>
</Form.Item>

View File

@@ -0,0 +1,111 @@
import { gql, useMutation } from "@apollo/client";
import { Button, notification, Popconfirm } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(PaymentMarkSelectedExported);
export function PaymentMarkSelectedExported({
bodyshop,
currentUser,
paymentIds,
disabled,
loadingCallback,
completedCallback,
refetch,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [visible, setVisible] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [updatePayments] = useMutation(gql`
mutation UPDATE_PAYMENTS($paymentIds: [uuid!]!, $exportedat: timestamptz!) {
update_payments(
where: { id: { _in: $paymentIds } }
_set: { exportedat: $exportedat }
) {
returning {
id
exportedat
}
}
}
`);
const handleUpdate = async () => {
setLoading(true);
loadingCallback(true);
const result = await updatePayments({
variables: { paymentIds: paymentIds, exportedat: new Date() },
update(cache) {},
});
await insertExportLog({
variables: {
logs: paymentIds.map((id) => {
return {
bodyshopid: bodyshop.id,
paymentid: id,
successful: true,
message: JSON.stringify([t("general.labels.markedexported")]),
useremail: currentUser.email,
};
}),
},
});
if (!result.errors) {
notification["success"]({
message: t("payments.successes.markexported"),
});
} else {
notification["error"]({
message: t("bills.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
loadingCallback(false);
completedCallback && completedCallback([]);
setLoading(false);
refetch && refetch();
setVisible(false);
};
return (
<Popconfirm
visible={visible}
title={t("general.labels.areyousure")}
onCancel={() => setVisible(false)}
onConfirm={handleUpdate}
disabled={disabled}
>
<Button
loading={loading}
disabled={disabled}
onClick={() => setVisible(true)}
>
{t("bills.labels.markexported")}
</Button>
</Popconfirm>
);
}

View File

@@ -1,4 +1,4 @@
import { Button, Card, Form, InputNumber, Popover } from "antd";
import { Button, Card, Form, InputNumber, Popover, Radio } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -24,7 +24,8 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const handleOk = () => {
const handleOk = (e) => {
e.stopPropagation();
form.submit();
setIsModalVisible(false);
};
@@ -33,12 +34,12 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
setIsModalVisible(false);
setLoading(false);
};
const handleFinish = async (values) => {
const handleFinish = async ({ template, ...values }) => {
const { sendtype, ...restVals } = values;
setLoading(true);
await GenerateDocument(
{
name: TemplateList("job_special").folder_label_multiple.key,
name: TemplateList("job_special")[template].key,
variables: { id: jobId },
context: restVals,
},
@@ -48,6 +49,7 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
);
setLoading(false);
setIsModalVisible(false);
form.resetFields();
};
const content = (
@@ -58,13 +60,28 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
layout="vertical"
form={form}
>
<Form.Item required name="template">
<Radio.Group>
<Radio.Button value="parts_label_multiple">
{t("printcenter.jobs.parts_label_multiple")}
</Radio.Button>
<Radio.Button value="folder_label_multiple">
{t("printcenter.jobs.folder_label_multiple")}
</Radio.Button>
</Radio.Group>
</Form.Item>
<Form.Item
required
label={t("printcenter.jobs.labels.position")}
name="position"
>
<InputNumber min={1} precision={0} />
</Form.Item>
<Form.Item label={t("printcenter.jobs.labels.count")} name="count">
<Form.Item
required
label={t("printcenter.jobs.labels.count")}
name="count"
>
<InputNumber min={1} precision={0} max={99} />
</Form.Item>
<Button type="primary" loading={loading} onClick={handleOk}>

View File

@@ -20,7 +20,7 @@ const mapDispatchToProps = (dispatch) => ({});
export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
const [search, setSearch] = useState("");
const { id: jobId } = printCenterModal.context;
const { id: jobId, job } = printCenterModal.context;
const tempList = TemplateList("job", {});
const { t } = useTranslation();
const JobsReportsList = Object.keys(tempList)
@@ -54,7 +54,7 @@ export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
extra={
<Space wrap>
<PrintCenterJobsLabels jobId={jobId} />
<Jobd3RdPartyModal jobId={jobId} />
<Jobd3RdPartyModal jobId={jobId} job={job} />
<Input.Search
onChange={(e) => setSearch(e.target.value)}
value={search}

View File

@@ -3,8 +3,9 @@ import {
EyeFilled,
DownloadOutlined,
PauseCircleOutlined,
BranchesOutlined,
} from "@ant-design/icons";
import { Card, Col, Row, Space } from "antd";
import { Card, Col, Row, Space, Tooltip } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
@@ -63,6 +64,11 @@ export default function ProductionBoardCard(
{card.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
{card.iouparent && (
<Tooltip title={t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} />
</Tooltip>
)}
<span style={{ fontWeight: "bolder" }}>
<Link
to={

View File

@@ -1,5 +1,5 @@
import { PauseCircleOutlined } from "@ant-design/icons";
import { Space } from "antd";
import { PauseCircleOutlined, BranchesOutlined } from "@ant-design/icons";
import { Space, Tooltip } from "antd";
import i18n from "i18next";
import moment from "moment";
import { Link } from "react-router-dom";
@@ -61,6 +61,11 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
{record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
{record.iouparent && (
<Tooltip title={i18n.t("jobs.labels.iou")}>
<BranchesOutlined style={{ color: "orangered" }} />
</Tooltip>
)}
</Space>
</Link>
),

View File

@@ -40,7 +40,10 @@ export function ScheduleCalendarHeaderComponent({
if (!events) return [];
return _.groupBy(
events.filter(
(e) => !e.vacation && moment(date).isSame(moment(e.start), "day")
(e) =>
!e.vacation &&
e.isintake &&
moment(date).isSame(moment(e.start), "day")
),
"job.alt_transport"
);

View File

@@ -1,6 +1,8 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Col, PageHeader, Row, Space } from "antd";
import React from "react";
import { Button, Card, Checkbox, Col, PageHeader, Row, Space } from "antd";
import { t } from "i18next";
import React, { useMemo } from "react";
import useLocalStorage from "../../utils/useLocalStorage";
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
import ScheduleModal from "../schedule-job-modal/schedule-job-modal.container";
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
@@ -8,6 +10,23 @@ import ScheduleProductionList from "../schedule-production-list/schedule-product
import ScheduleVerifyIntegrity from "../schedule-verify-integrity/schedule-verify-integrity.component";
export default function ScheduleCalendarComponent({ data, refetch }) {
const [filter, setFilter] = useLocalStorage("filter_events", {
intake: true,
manual: true,
employeevacation: true,
});
const filteredData = useMemo(() => {
return data.filter(
(d) =>
d.block ||
(filter.intake && d.isintake) ||
(filter.manual && !d.isintake && d.block === false) ||
(d.__typename === "employee_vacation" &&
filter.employeevacation &&
!!d.employee)
);
}, [data, filter]);
return (
<Row gutter={[16, 16]}>
<ScheduleModal />
@@ -16,6 +35,30 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
<PageHeader
extra={
<Space wrap>
<Checkbox
checked={filter?.intake}
onChange={(e) => {
setFilter({ ...filter, intake: e.target.checked });
}}
>
{t("schedule.labels.intake")}
</Checkbox>
<Checkbox
checked={filter?.manual}
onChange={(e) => {
setFilter({ ...filter, manual: e.target.checked });
}}
>
{t("schedule.labels.manual")}
</Checkbox>
<Checkbox
checked={filter?.employeevacation}
onChange={(e) => {
setFilter({ ...filter, employeevacation: e.target.checked });
}}
>
{t("schedule.labels.employeevacation")}
</Checkbox>
<ScheduleVerifyIntegrity />
<Button
onClick={() => {
@@ -35,7 +78,7 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
<Col span={24}>
<Card>
<ScheduleCalendarWrapperComponent
data={data}
data={filteredData}
refetch={refetch}
style={{ height: "100rem" }}
/>

View File

@@ -16,6 +16,7 @@ import DataLabel from "../data-label/data-label.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { useTreatments } from "@splitsoftware/splitio-react";
const SelectorDiv = styled.div`
.ant-form-item .ant-select {
@@ -37,7 +38,16 @@ export default connect(
export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
const { t } = useTranslation();
const { Qb_Multi_Ar } = useTreatments(
["Qb_Multi_Ar"],
{},
bodyshop && bodyshop.imexshopid
);
const { DmsAp } = useTreatments(
["DmsAp"],
{},
bodyshop && bodyshop.imexshopid
);
const [costOptions, setCostOptions] = useState(
[
...((form.getFieldValue(["md_responsibility_centers", "costs"]) &&
@@ -138,6 +148,15 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Switch />
</Form.Item>
)}
{bodyshop.pbs_serialnumber && (
<Form.Item
label={t("bodyshop.fields.dms.disablebillwip")}
valuePropName="checked"
name={["pbs_configuration", "disablebillwip"]}
>
<Switch />
</Form.Item>
)}
</LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.dms.cdk.payers")}>
<Form.List name={["cdk_configuration", "payers"]}>
@@ -4153,6 +4172,121 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<InputNumber precision={2} />
</Form.Item>
</LayoutFormRow>
{DmsAp.treatment === "on" && (
<LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.federal_tax_itc")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "taxes", "federal_itc", "name"]}
>
<Input />
</Form.Item>
{/* <Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"accountnumber",
]}
>
<Input />
</Form.Item> */}
{/* <Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountname")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"accountname",
]}
>
<Input />
</Form.Item> */}
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"accountdesc",
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"accountitem",
]}
>
<Input />
</Form.Item>
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<Form.Item
label={t("bodyshop.fields.dms.dms_acctnumber")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"taxes",
"federal_itc",
"dms_acctnumber",
]}
>
<Input />
</Form.Item>
)}
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_rate")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "taxes", "federal_itc", "rate"]}
>
<InputNumber precision={2} />
</Form.Item>
</LayoutFormRow>
)}
<LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.state_tax")}
@@ -4411,68 +4545,71 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Input />
</Form.Item> */}
</LayoutFormRow>
{/* <LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.responsibilitycenters.ap")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "name"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "accountnumber"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountname")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "accountname"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "accountdesc"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "accountitem"]}
>
<Input />
</Form.Item>
</LayoutFormRow> */}
{DmsAp.treatment === "on" && (
<LayoutFormRow header={t("bodyshop.fields.responsibilitycenters.ap")}>
{/* <Form.Item
label={t("bodyshop.fields.responsibilitycenters.ap")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "name"]}
>
<Input />
</Form.Item> */}
{/* <Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountnumber")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "accountnumber"]}
>
<Input />
</Form.Item> */}
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountname")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "accountname"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountdesc")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "accountdesc"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.dms.dms_acctnumber")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_responsibility_centers", "ap", "dms_acctnumber"]}
>
<Input />
</Form.Item>
</LayoutFormRow>
)}
<LayoutFormRow header={<div>Refund</div>}>
{/* <Form.Item
label={t("bodyshop.fields.responsibilitycenters.refund")}
@@ -4535,6 +4672,26 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Input />
</Form.Item>
</LayoutFormRow>
{Qb_Multi_Ar.treatment === "on" && (
<LayoutFormRow header={<div>Multiple Payers Item</div>}>
<Form.Item
label={t("bodyshop.fields.responsibilitycenter_accountitem")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={[
"md_responsibility_centers",
"qb_multiple_payers",
"accountitem",
]}
>
<Input />
</Form.Item>
</LayoutFormRow>
)}
<Typography.Title level={4}>
{t("bodyshop.labels.responsibilitycenters.sales_tax_codes")}
</Typography.Title>

View File

@@ -27,6 +27,7 @@ export function TimeTicektShiftContainer({
technician,
currentUser,
isTechConsole,
checkIfAlreadyClocked,
}) {
const [form] = Form.useForm();
const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET);
@@ -43,36 +44,47 @@ export function TimeTicektShiftContainer({
const handleFinish = async (values) => {
setLoading(true);
const theTime = moment((await axios.post("/utils/time")).data);
const result = await insertTimeTicket({
variables: {
timeTicketInput: [
{
bodyshopid: bodyshop.id,
employeeid: isTechConsole ? technician.id : employeeId,
cost_center: "timetickets.labels.shift",
clockon: theTime,
date: theTime,
memo: values.memo,
},
],
},
awaitRefetchQueries: true,
refetchQueries: ["QUERY_ACTIVE_SHIFT_TIME_TICKETS"],
});
const alreadyClocked = await checkIfAlreadyClocked();
if (!!result.errors) {
if (alreadyClocked) {
//Show the error.
notification["error"]({
message: t("timetickets.errors.clockingin", {
message: JSON.stringify(result.errors),
}),
message: t("timetickets.errors.shiftalreadyclockedon"),
});
} else {
notification["success"]({
message: t("timetickets.successes.clockedin"),
const theTime = moment((await axios.post("/utils/time")).data);
const result = await insertTimeTicket({
variables: {
timeTicketInput: [
{
bodyshopid: bodyshop.id,
employeeid: isTechConsole ? technician.id : employeeId,
cost_center: "timetickets.labels.shift",
clockon: theTime,
date: theTime,
memo: values.memo,
},
],
},
awaitRefetchQueries: true,
refetchQueries: ["QUERY_ACTIVE_SHIFT_TIME_TICKETS"],
});
if (!!result.errors) {
notification["error"]({
message: t("timetickets.errors.clockingin", {
message: JSON.stringify(result.errors),
}),
});
} else {
notification["success"]({
message: t("timetickets.successes.clockedin"),
});
}
}
setLoading(false);
};

View File

@@ -64,6 +64,12 @@ export function TimeTicketShiftContainer({
</div>
);
const checkIfAlreadyClocked = async () => {
const { data } = await refetch();
return data.timetickets.length > 0;
};
return (
<div>
{data.timetickets.length > 0 ? (
@@ -72,7 +78,10 @@ export function TimeTicketShiftContainer({
refetch={refetch}
/>
) : (
<TimeTicketShiftFormContainer isTechConsole={isTechConsole} />
<TimeTicketShiftFormContainer
isTechConsole={isTechConsole}
checkIfAlreadyClocked={checkIfAlreadyClocked}
/>
)}
</div>
);

View File

@@ -1,5 +1,6 @@
import { DeleteFilled } from "@ant-design/icons";
import { useApolloClient } from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import {
Button,
Divider,
@@ -20,7 +21,23 @@ import PhoneFormItem, {
} from "../form-items-formatted/phone-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import VendorsPhonebookAdd from "../vendors-phonebook-add/vendors-phonebook-add.component";
export default function VendorsFormComponent({
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
)(VendorsFormComponent);
export function VendorsFormComponent({
bodyshop,
form,
formLoading,
handleDelete,
@@ -29,6 +46,12 @@ export default function VendorsFormComponent({
}) {
const { t } = useTranslation();
const client = useApolloClient();
const { DmsAp } = useTreatments(
["DmsAp"],
{},
bodyshop && bodyshop.imexshopid
);
const { getFieldValue } = form;
return (
<div>
@@ -184,6 +207,12 @@ export default function VendorsFormComponent({
// </Form.Item>
}
</LayoutFormRow>
{DmsAp.treatment === "on" && (
<Form.Item label={t("vendors.fields.dmsid")} name="dmsid">
<Input />
</Form.Item>
)}
<Divider align="left">{t("vendors.labels.preferredmakes")}</Divider>
<Form.List name="favorite">
{(fields, { add, remove }) => {

View File

@@ -53,7 +53,6 @@ export const requestForToken = () => {
})
.then((currentToken) => {
if (currentToken) {
console.log("current token for client: ", currentToken);
window.sessionStorage.setItem("fcmtoken", currentToken);
// Perform any other necessary action with the token
} else {

View File

@@ -1,77 +1,81 @@
import { gql } from "@apollo/client";
export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
query QUERY_ALL_ACTIVE_APPOINTMENTS(
$start: timestamptz!
$end: timestamptz!
$startd: date!
$endd: date!
) {
employee_vacation(
where: { _or: [{ start: { _gte: $startd } },
{ end: { _lte: $endd } },
{_and:[{start:{_lte: $startd}},{end:{_gte:$endd}}]}] }
query QUERY_ALL_ACTIVE_APPOINTMENTS(
$start: timestamptz!
$end: timestamptz!
$startd: date!
$endd: date!
) {
id
start
end
employee {
employee_vacation(
where: {
_or: [
{ start: { _gte: $startd } }
{ end: { _lte: $endd } }
{ _and: [{ start: { _lte: $startd } }, { end: { _gte: $endd } }] }
]
}
) {
id
last_name
first_name
start
end
employee {
id
last_name
first_name
}
}
}
appointments(
where: {
canceled: { _eq: false }
end: { _lte: $end }
start: { _gte: $start }
}
) {
start
id
end
arrived
title
isintake
block
color
note
job {
alt_transport
ro_number
ownr_ln
ownr_co_nm
ownr_fn
ownr_ph1
ownr_ph2
ownr_ea
clm_total
appointments(
where: {
canceled: { _eq: false }
end: { _lte: $end }
start: { _gte: $start }
}
) {
start
id
clm_no
ins_co_nm
v_model_yr
v_make_desc
v_model_desc
labhrs: joblines_aggregate(
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
) {
aggregate {
sum {
mod_lb_hrs
end
arrived
title
isintake
block
color
note
job {
alt_transport
ro_number
ownr_ln
ownr_co_nm
ownr_fn
ownr_ph1
ownr_ph2
ownr_ea
clm_total
id
clm_no
ins_co_nm
v_model_yr
v_make_desc
v_model_desc
labhrs: joblines_aggregate(
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
) {
aggregate {
sum {
mod_lb_hrs
}
}
}
larhrs: joblines_aggregate(
where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }
) {
aggregate {
sum {
mod_lb_hrs
}
}
}
}
larhrs: joblines_aggregate(
where: { mod_lbr_ty: { _eq: "LAR" }, removed: { _eq: false } }
) {
aggregate {
sum {
mod_lb_hrs
}
}
}
}
}
}
`;
@@ -382,8 +386,8 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
}
`;
export const MARK_LATEST_APPOINTMENT_AS_ARRIVED = gql`
mutation MARK_LATEST_APPOINTMENT_AS_ARRIVED($appointmentId: uuid!) {
export const MARK_APPOINTMENT_ARRIVED = gql`
mutation MARK_APPOINTMENT_ARRIVED($appointmentId: uuid!) {
update_appointments(
where: { id: { _eq: $appointmentId } }
_set: { arrived: true }
@@ -396,3 +400,21 @@ export const MARK_LATEST_APPOINTMENT_AS_ARRIVED = gql`
}
}
`;
export const MARK_LATEST_APPOINTMENT_ARRIVED = gql`
mutation MARK_LATEST_APPOINTMENT_ARRIVED($jobId: uuid!) {
update_appointments(
where: {
jobid: { _eq: $jobId }
canceled: { _eq: false }
isintake: { _eq: true }
}
_set: { arrived: true }
) {
affected_rows
returning {
id
arrived
}
}
}
`;

View File

@@ -13,6 +13,21 @@ export const QUERY_AUDIT_TRAIL = gql`
created
bodyshopid
}
email_audit_trail(
where: { jobid: { _eq: $jobid } }
order_by: { created_at: desc }
) {
cc
contents
created_at
id
jobid
noteid
subject
to
useremail
status
}
}
`;

View File

@@ -6,6 +6,7 @@ export const QUERY_ALL_ACTIVE_JOBS = gql`
where: { status: { _in: $statuses } }
order_by: { created_at: desc }
) {
iouparent
ownr_fn
ownr_ln
ownr_co_nm
@@ -109,6 +110,7 @@ export const QUERY_EXACT_JOB_IN_PRODUCTION = gql`
query QUERY_EXACT_JOB_IN_PRODUCTION($id: uuid!) {
jobs(where: { id: { _eq: $id } }) {
id
iouparent
status
ro_number
comment
@@ -187,6 +189,7 @@ export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql`
query QUERY_EXACT_JOBS_IN_PRODUCTION($ids: [uuid!]!) {
jobs(where: { id: { _in: $ids } }) {
id
iouparent
status
ro_number
comment
@@ -269,6 +272,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
comment
status
category
iouparent
ro_number
ownr_fn
ownr_ln
@@ -609,6 +613,7 @@ export const GET_JOB_BY_PK = gql`
materials
auto_add_ats
rate_ats
iouparent
owner {
id
ownr_fn
@@ -850,7 +855,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
count
status
}
joblines {
joblines(where: { removed: { _eq: false } }) {
id
mod_lbr_ty
mod_lb_hrs
@@ -1890,6 +1895,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
actual_in
kmin
kmout
qb_multiple_payers
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
id
removed
@@ -2114,6 +2120,22 @@ export const DELETE_RELATED_RO = gql`
`;
export const GET_JOB_LINE_ORDERS = gql`
query GET_JOB_LINE_ORDERS($joblineid: uuid!) {
billlines(where: { joblineid: { _eq: $joblineid } }) {
actual_cost
actual_price
billid
quantity
bill {
id
invoice_number
date
vendorid
vendor {
id
name
}
}
}
parts_order_lines(where: { job_line_id: { _eq: $joblineid } }) {
id
act_price

View File

@@ -18,6 +18,7 @@ export const QUERY_VENDOR_BY_ID = gql`
street1
active
phone
dmsid
}
}
`;

View File

@@ -0,0 +1,163 @@
import { Button, Card, Col, notification, Row, Select, Space } from "antd";
import React, { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import SocketIO from "socket.io-client";
import DmsAllocationsSummaryApComponent from "../../components/dms-allocations-summary-ap/dms-allocations-summary-ap.component";
import DmsLogEvents from "../../components/dms-log-events/dms-log-events.component";
import { auth } from "../../firebase/firebase.utils";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
});
export default connect(mapStateToProps, mapDispatchToProps)(DmsContainer);
export const socket = SocketIO(
process.env.NODE_ENV === "production"
? process.env.REACT_APP_AXIOS_BASE_API_URL
: window.location.origin,
{
path: "/ws",
withCredentials: true,
auth: async (callback) => {
const token = auth.currentUser && (await auth.currentUser.getIdToken());
callback({ token });
},
}
);
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
const [logLevel, setLogLevel] = useState("DEBUG");
const history = useHistory();
const [logs, setLogs] = useState([]);
const { state } = useLocation();
const logsRef = useRef(null);
useEffect(() => {
document.title = t("titles.dms");
setSelectedHeader("dms");
setBreadcrumbs([
{
link: "/manage/accounting/payables",
label: t("titles.bc.accounting-payables"),
},
{
link: "/manage/dms",
label: t("titles.bc.dms"),
},
]);
}, [t, setBreadcrumbs, setSelectedHeader]);
useEffect(() => {
socket.on("connect", () => socket.emit("set-log-level", logLevel));
socket.on("reconnect", () => {
setLogs((logs) => {
return [
...logs,
{
timestamp: new Date(),
level: "WARNING",
message: "Reconnected to CDK Export Service",
},
];
});
});
socket.on("log-event", (payload) => {
setLogs((logs) => {
return [...logs, payload];
});
});
socket.on("ap-export-complete", (payload) => {
notification.open({
type: "success",
message: t("jobs.labels.dms.apexported"),
});
});
if (socket.disconnected) socket.connect();
return () => {
socket.removeAllListeners();
socket.disconnect();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!state?.billids) {
history.push(`/manage/accounting/payables`);
}
return (
<Row gutter={[16, 16]}>
<Col md={24} lg={12}>
<DmsAllocationsSummaryApComponent
socket={socket}
billids={state?.billids}
/>
</Col>
<Col md={24} lg={12}>
<div ref={logsRef}>
<Card
title={t("jobs.labels.dms.logs")}
extra={
<Space wrap>
<Select
placeholder="Log Level"
value={logLevel}
onChange={(value) => {
setLogLevel(value);
socket.emit("set-log-level", value);
}}
>
<Select.Option key="TRACE">TRACE</Select.Option>
<Select.Option key="DEBUG">DEBUG</Select.Option>
<Select.Option key="INFO">INFO</Select.Option>
<Select.Option key="WARNING">WARNING</Select.Option>
<Select.Option key="ERROR">ERROR</Select.Option>
</Select>
<Button onClick={() => setLogs([])}>Clear Logs</Button>
<Button
onClick={() => {
setLogs([]);
socket.disconnect();
socket.connect();
}}
>
Reconnect
</Button>
</Space>
}
>
<DmsLogEvents socket={socket} logs={logs} />
</Card>
</div>
</Col>
</Row>
);
}
export const determineDmsType = (bodyshop) => {
if (bodyshop.cdk_dealerid) return "cdk";
else {
return "pbs";
}
};

View File

@@ -1,35 +1,43 @@
import { DeleteFilled } from "@ant-design/icons";
import { useApolloClient, useMutation } from "@apollo/client";
import {
Button,
Form,
notification,
Popconfirm,
Space,
Alert,
Button,
Col,
Divider,
PageHeader,
InputNumber,
Form,
Input,
InputNumber,
notification,
PageHeader,
Popconfirm,
Row,
Select,
Space,
Statistic,
Switch,
Typography,
} from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
//import { useHistory } from "react-router-dom";
import { useTreatments } from "@splitsoftware/splitio-react";
import moment from "moment";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import DateTimePicker from "../../components/form-date-time-picker/form-date-time-picker.component";
import FormsFieldChanged from "../../components/form-fields-changed-alert/form-fields-changed-alert.component";
import CurrencyInput from "../../components/form-items-formatted/currency-form-item.component";
import JobsScoreboardAdd from "../../components/job-scoreboard-add-button/job-scoreboard-add-button.component";
import JobsCloseAutoAllocate from "../../components/jobs-close-auto-allocate/jobs-close-auto-allocate.component";
import JobsCloseLines from "../../components/jobs-close-lines/jobs-close-lines.component";
import LayoutFormRow from "../../components/layout-form-row/layout-form-row.component";
import { generateJobLinesUpdatesForInvoicing } from "../../graphql/jobs-lines.queries";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import LayoutFormRow from "../../components/layout-form-row/layout-form-row.component";
import DateTimePicker from "../../components/form-date-time-picker/form-date-time-picker.component";
import moment from "moment";
import { Link } from "react-router-dom";
import Dinero from "dinero.js";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
jobRO: selectJobReadOnly,
@@ -42,6 +50,11 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
// const history = useHistory();
const [closeJob] = useMutation(UPDATE_JOB);
const [loading, setLoading] = useState(false);
const { Qb_Multi_Ar } = useTreatments(
["Qb_Multi_Ar"],
{},
bodyshop && bodyshop.imexshopid
);
const handleFinish = async ({ removefromproduction, ...values }) => {
setLoading(true);
@@ -65,6 +78,9 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
kmout: values.kmout,
dms_allocation: values.dms_allocation,
...(removefromproduction ? { inproduction: false } : {}),
...(values.qb_multiple_payers
? { qb_multiple_payers: values.qb_multiple_payers }
: {}),
},
},
refetchQueries: ["QUERY_JOB_CLOSE_DETAILS"],
@@ -127,6 +143,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
kmin: job.kmin,
kmout: job.kmout,
dms_allocation: job.dms_allocation,
qb_multiple_payers: job.qb_multiple_payers,
}}
scrollToFirstError
>
@@ -312,6 +329,165 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
</Form.Item>
)}
</LayoutFormRow>
<Divider>{t("jobs.labels.multipayers")}</Divider>
{Qb_Multi_Ar.treatment === "on" && (
<Row gutter={[16, 16]}>
<Col lg={8} md={24}>
<Form.List
name={["qb_multiple_payers"]}
rules={[
({ getFieldValue }) => ({
validator(_, value) {
let totalAllocated = Dinero();
const payers = form.getFieldValue("qb_multiple_payers");
payers &&
payers.forEach((payer) => {
totalAllocated = totalAllocated.add(
Dinero({
amount: Math.round((payer?.amount || 0) * 100),
})
);
});
const discrep = job.job_totals
? Dinero(job.job_totals.totals.total_repairs).subtract(
totalAllocated
)
: Dinero();
return discrep.getAmount() >= 0
? Promise.resolve()
: Promise.reject(
new Error(
t("jobs.labels.additionalpayeroverallocation")
)
);
},
}),
]}
>
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<Space>
<Form.Item
label={t("jobs.fields.qb_multiple_payers.name")}
key={`${index}name`}
name={[field.name, "name"]}
rules={[
{
required: true,
},
]}
>
<Select
style={{ minWidth: "12rem" }}
disabled={jobRO}
>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{s.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={t("jobs.fields.qb_multiple_payers.amount")}
key={`${index}amount`}
name={[field.name, "amount"]}
rules={[
{
required: true,
},
]}
>
<CurrencyInput min={0} disabled={jobRO} />
</Form.Item>
<DeleteFilled
disabled={jobRO}
onClick={() => {
remove(field.name);
}}
/>
</Space>
</Form.Item>
))}
<Form.Item>
<Button
disabled={jobRO}
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("jobs.actions.dms.addpayer")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</Col>
<Col lg={16} md={24}>
<Form.Item shouldUpdate>
{() => {
//Perform Calculation to determine discrepancy.
let totalAllocated = Dinero();
const payers = form.getFieldValue("qb_multiple_payers");
payers &&
payers.forEach((payer) => {
totalAllocated = totalAllocated.add(
Dinero({
amount: Math.round((payer?.amount || 0) * 100),
})
);
});
const discrep = job.job_totals
? Dinero(job.job_totals.totals.total_repairs).subtract(
totalAllocated
)
: Dinero();
return (
<Space size="large" wrap align="center">
<Statistic
title={t("jobs.labels.total_cust_payable")}
value={(job.job_totals
? Dinero(job.job_totals.totals.custPayable)
: Dinero()
).toFormat()}
/>
<Divider type="vertical" />
<Statistic
title={t("jobs.labels.total_repairs")}
value={(job.job_totals
? Dinero(job.job_totals.totals.total_repairs)
: Dinero()
).toFormat()}
/>
<Typography.Title>-</Typography.Title>
<Statistic
title={t("jobs.labels.dms.totalallocated")}
value={totalAllocated.toFormat()}
/>
<Typography.Title>=</Typography.Title>
<Statistic
title={t("jobs.labels.pimraryamountpayable")}
valueStyle={{
color: discrep.getAmount() > 0 ? "green" : "red",
}}
value={discrep.toFormat()}
/>
</Space>
);
}}
</Form.Item>
</Col>
</Row>
)}
<Divider />
<JobsCloseLines job={job} />
</Form>

View File

@@ -89,6 +89,7 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
let job = Object.assign(
{},
values,
{ date_open: new Date() },
{
vehicle:
state.vehicle.selectedid || state.vehicle.none

View File

@@ -164,6 +164,9 @@ const EmailTest = lazy(() =>
);
const Dashboard = lazy(() => import("../dashboard/dashboard.container"));
const Dms = lazy(() => import("../dms/dms.container"));
const DmsPayables = lazy(() =>
import("../dms-payables/dms-payables.container")
);
const { Content, Footer } = Layout;
@@ -391,6 +394,7 @@ export function Manage({ match, conflict, bodyshop }) {
<Route exact path={`${match.path}/emailtest`} component={EmailTest} />
<Route exact path={`${match.path}/dashboard`} component={Dashboard} />
<Route exact path={`${match.path}/dms`} component={Dms} />
<Route exact path={`${match.path}/dmsap`} component={DmsPayables} />
</Suspense>
);

View File

@@ -1,6 +1,7 @@
import { SyncOutlined } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Input, Space, Table } from "antd";
import _ from "lodash";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -14,7 +15,6 @@ import OwnerNameDisplay from "../../components/owner-name-display/owner-name-dis
import ProductionListColumnComment from "../../components/production-list-columns/production-list-columns.comment.component";
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
import { DateTimeFormatter, TimeAgoFormatter } from "../../utils/DateFormatter";
import { alphaSort, dateSort } from "../../utils/sorters";
import useLocalStorage from "../../utils/useLocalStorage";
@@ -94,6 +94,14 @@ export function PartsQueuePageComponent({ bodyshop }) {
// searchParams.page = pagination.current;
searchParams.sortcolumn = sorter.columnKey;
searchParams.sortorder = sorter.order;
if (filters.status) {
searchParams.statusFilters = JSON.stringify(
_.flattenDeep(filters.status)
);
} else {
delete searchParams.statusFilters;
}
setFilter(filters);
history.push({ search: queryString.stringify(searchParams) });
};
@@ -136,19 +144,14 @@ export function PartsQueuePageComponent({ bodyshop }) {
key: "status",
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder: sortcolumn === "status" && sortorder,
filteredValue: statusFilters ? JSON.parse(statusFilters) : null,
filters:
(jobs &&
jobs
.map((j) => j.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
bodyshop.md_ro_statuses.active_statuses.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
}) || [],
render: (text, record) => {
return record.status || t("general.labels.na");
},

View File

@@ -1,12 +1,15 @@
import React from "react";
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component";
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
export default function TechLookupContainer() {
return (
<div>
<TechLookupJobsList />
<TechLookupJobsDrawer />
<RbacWrapperComponent action="jobs:list-active">
<TechLookupJobsList />
<TechLookupJobsDrawer />
</RbacWrapperComponent>
</div>
);
}

View File

@@ -39,6 +39,11 @@ export function* getJobMedia({ payload: jobid }) {
thumbnail: normalizeUrl(
`${localmediaserverhttp}/${d.thumbnail}`
),
...(d.optimized && {
optimized: normalizeUrl(
`${localmediaserverhttp}/${d.optimized}`
),
}),
isSelected: false,
key: idx,
};
@@ -50,6 +55,7 @@ export function* getJobMedia({ payload: jobid }) {
thumbnail: normalizeUrl(
`${localmediaserverhttp}/${d.thumbnail}`
),
isSelected: false,
key: idx,
};
@@ -91,6 +97,11 @@ export function* getBillMedia({ payload: { jobid, invoice_number } }) {
thumbnail: normalizeUrl(
`${localmediaserverhttp}/${d.thumbnail}`
),
...(d.optimized && {
optimized: normalizeUrl(
`${localmediaserverhttp}/${d.optimized}`
),
}),
isSelected: false,
key: idx,
};

View File

@@ -82,8 +82,13 @@
},
"audit": {
"fields": {
"cc": "CC",
"contents": "Contents",
"created": "Time",
"operation": "Operation",
"status": "Status",
"subject": "Subject",
"to": "To",
"useremail": "User",
"values": "Values"
}
@@ -190,6 +195,7 @@
"entered_total": "Total of Entered Lines",
"enteringcreditmemo": "You are entering a credit memo. Please ensure you are also entering positive values.",
"federal_tax": "Federal Tax",
"generatepartslabel": "Generate Parts Labels after Saving?",
"iouexists": "An IOU exists that is associated to this RO.",
"local_tax": "Local Tax",
"markexported": "Mark Exported",
@@ -258,6 +264,7 @@
"dms": {
"cashierid": "Cashier ID",
"default_journal": "Default Journal",
"disablebillwip": "Disable bill WIP for A/P Posting",
"disablecontactvehiclecreation": "Disable Contact & Vehicle Updates/Creation",
"dms_acctnumber": "DMS Account #",
"dms_wip_acctnumber": "DMS W.I.P. Account #",
@@ -431,6 +438,7 @@
"ar": "Accounts Receivable",
"ats": "ATS",
"federal_tax": "Federal Tax",
"federal_tax_itc": "Federal Tax Credit",
"gst_override": "GST Override Account #",
"la1": "LA1",
"la2": "LA2",
@@ -831,6 +839,7 @@
},
"errors": {
"deletes3": "Error deleting document from storage. ",
"deleting": "Error deleting documents {{error}}",
"deleting_cloudinary": "Error deleting document from storage. {{message}}",
"getpresignurl": "Error obtaining presigned URL for document. {{message}}",
"insert": "Unable to upload file. {{message}}",
@@ -842,6 +851,7 @@
"doctype": "Document Type",
"newjobid": "Assign to Job",
"openinexplorer": "Open in Explorer",
"optimizedimage": "The below image is optimized. Click on the picture below to open in a new window and view it full size, or open it in explorer.",
"reassign_limitexceeded": "Reassigning all selected documents will exceed the job storage limit for your shop. ",
"reassign_limitexceeded_title": "Unable to reassign document(s)",
"storageexceeded": "You've exceeded your storage limit for this job. Please remove documents, or increase your storage plan.",
@@ -872,6 +882,7 @@
"labels": {
"attachments": "Attachments",
"documents": "Documents",
"emailpreview": "Email Preview",
"generatingemail": "Generating email...",
"pdfcopywillbeattached": "A PDF copy of this email will be attached when it is sent.",
"preview": "Email Preview"
@@ -960,6 +971,7 @@
"save": "Save",
"saveandnew": "Save and New",
"selectall": "Select All",
"send": "Send",
"senderrortosupport": "Send Error to Support",
"submit": "Submit",
"tryagain": "Try Again",
@@ -1029,6 +1041,7 @@
"sendby": "Send By",
"signin": "Sign In",
"sms": "SMS",
"status": "Status",
"sub_status": {
"expired": "The subscription for this shop has expired. Please contact technical support to reactivate the subscription. "
},
@@ -1345,6 +1358,7 @@
"depreciation_taxes": "Depreciation/Taxes",
"dms": {
"address": "Customer Address",
"amount": "Amount",
"center": "Center",
"cost": "Cost",
"cost_dms_acctnumber": "Cost DMS Acct #",
@@ -1354,6 +1368,7 @@
"id": "DMS ID",
"inservicedate": "In Service Date",
"journal": "Journal #",
"lines": "Posting Lines",
"name1": "Customer Name",
"payer": {
"amount": "Amount",
@@ -1462,6 +1477,10 @@
"production_vars": {
"note": "Production Note"
},
"qb_multiple_payers": {
"amount": "Amount",
"name": "Name"
},
"queued_for_parts": "Queued for Parts",
"rate_ats": "ATS Rate",
"rate_la1": "LA1",
@@ -1535,11 +1554,13 @@
"actual_completion_inferred": "$t(jobs.fields.actual_completion) inferred using $t(jobs.fields.scheduled_completion).",
"actual_delivery_inferred": "$t(jobs.fields.actual_delivery) inferred using $t(jobs.fields.scheduled_delivery).",
"actual_in_inferred": "$t(jobs.fields.actual_in) inferred using $t(jobs.fields.scheduled_in).",
"additionalpayeroverallocation": "You have allocated more than the sale of the Job to additional payers.",
"additionaltotal": "Additional Total",
"adjustmentrate": "Adjustment Rate",
"adjustments": "Adjustments",
"adminwarning": "Use the functionality on this page at your own risk. You are responsible for any and all changes to your data.",
"allocations": "Allocations",
"alreadyaddedtoscoreboard": "Job has already been added to scoreboard. Saving will update the previous entry.",
"alreadyclosed": "This job has already been closed.",
"appointmentconfirmation": "Send confirmation to customer?",
"associationwarning": "Any changes to associations will require updating the data from the new parent record to the job.",
@@ -1601,8 +1622,10 @@
"difference": "Difference",
"diskscan": "Scan Disk for Estimates",
"dms": {
"apexported": "AP export completed. See logs for details.",
"damageto": "Damage to $t(jobs.fields.area_of_damage_impact.{{area_of_damage}}).",
"defaultstory": "B/S RO: {{ro_number}}. Owner: {{ownr_nm}}. Insurance Co: {{ins_co_nm}}. Claim/PO #: {{clm_po}}",
"disablebillwip": "Cost and WIP for bills has been ignored per shop configuration.",
"invoicedatefuture": "Invoice date must be today or in the future for CDK posting.",
"kmoutnotgreaterthankmin": "Mileage out must be greater than mileage in.",
"logs": "Logs",
@@ -1614,6 +1637,7 @@
"documents-images": "Images",
"documents-other": "Other Documents",
"duplicateconfirm": "Are you sure you want to duplicate this job? Some elements of this job will not be duplicated.",
"emailaudit": "Email Audit Trail",
"employeeassignments": "Employee Assignments",
"estimatelines": "Estimate Lines",
"estimator": "Estimator",
@@ -1626,6 +1650,7 @@
"importnote": "The job was initially imported.",
"inproduction": "In Production",
"intakechecklist": "Intake Checklist",
"iou": "IOU",
"job": "Job Details",
"jobcosting": "Job Costing",
"jobtotals": "Job Totals",
@@ -1637,6 +1662,7 @@
"mapa": "Paint Materials",
"markforreexport": "Mark for Re-export",
"mash": "Shop Materials",
"multipayers": "Additional Payers",
"net_repairs": "Net Repairs",
"notes": "Notes",
"othertotal": "Other Totals",
@@ -1648,6 +1674,7 @@
"partsfilter": "Parts Only",
"partssubletstotal": "Parts & Sublets Total",
"partstotal": "Parts Total (ex. Taxes)",
"pimraryamountpayable": "Total Primary Payable",
"plitooltips": {
"billtotal": "The total amount of all bill lines that have been posted against this RO (not including credits, taxes, or labor adjustments).",
"calculatedcreditsnotreceived": "The calculated credits not received is derived by subtracting the amount of credit memos entered from the <b>retail</b> total of returns created. This does not take into account whether the credit was marked as received. You can find more information <a href=\"https://help.imex.online/en/article/credits-not-received-changes-1jy9snw\" target=\"_blank\">here</a>.",
@@ -2027,6 +2054,8 @@
"labels": {
"allpartsto": "All Parts Location",
"confirmdelete": "Are you sure you want to delete this item? It cannot be recovered. Job line statuses will not be updated and may require manual review. ",
"custompercent": "Custom %",
"discount": "Discount {{percent}}",
"email": "Send by Email",
"inthisorder": "Parts in this Order",
"is_quote": "Parts Quote?",
@@ -2034,12 +2063,15 @@
"newpartsorder": "New Parts Order",
"notyetordered": "This part has not yet been ordered.",
"oec": "Order via OEC",
"order_type": "Order Type",
"orderhistory": "Order History",
"parts_order": "Parts Order",
"parts_orders": "Parts Orders",
"print": "Show Printed Form",
"receive": "Receive Parts Order",
"removefrompartsqueue": "Remove from Parts Queue?",
"returnpartsorder": "Return Parts Order"
"returnpartsorder": "Return Parts Order",
"sublet_order": "Sublet Order"
},
"successes": {
"created": "Parts order created successfully. ",
@@ -2067,10 +2099,11 @@
},
"labels": {
"balance": "Balance",
"ca_bc_etf_table": "ICBC ETF Table Converter",
"ca_bc_etf_table": "ICBC EFT Table Converter",
"customer": "Customer",
"edit": "Edit Payment",
"electronicpayment": "Use Electronic Payment Processing?",
"external": "External",
"findermodal": "ICBC Payment Finder",
"insurance": "Insurance",
"new": "New Payment",
@@ -2080,6 +2113,7 @@
},
"successes": {
"exported": "Payment(s) exported successfully.",
"markexported": "Payment(s) marked exported.",
"payment": "Payment created successfully. ",
"stripe": "Credit card transaction charged successfully."
}
@@ -2169,7 +2203,7 @@
"filing_coversheet_portrait": "Filing Coversheet (Portrait)",
"final_invoice": "Final Invoice",
"fippa_authorization": "FIPPA Authorization",
"folder_label_multiple": "Folder Label Multiple",
"folder_label_multiple": "Folder Label - Multi",
"glass_express_checklist": "Glass Express Checklist",
"guarantee": "Repair Guarantee",
"individual_job_note": "Job Note RO # {{ro_number}}",
@@ -2190,6 +2224,8 @@
"mpi_eglass_auth": "MPI - eGlass Auth",
"mpi_final_acct_sheet": "MPI - Final Accounting Sheet",
"paint_grid": "Paint Grid",
"parts_invoice_label_single": "Parts Label Single",
"parts_label_multiple": "Parts Label - Multi",
"parts_label_single": "Parts Label - Single",
"parts_list": "Parts List",
"parts_order": "Parts Order Confirmation",
@@ -2208,6 +2244,7 @@
"sgi_certificate_of_repairs": "SGI - Certificate of Repairs",
"sgi_windshield_auth": "SGI - Windshield Authorization",
"stolen_recovery_checklist": "Stolen Recovery Checklist",
"sublet_order": "Sublet Order",
"supplement_request": "Supplement Request",
"thank_you_ro": "Thank You Letter",
"thirdpartypayer": "Third Party Payer",
@@ -2238,7 +2275,7 @@
"title": "Print Center"
},
"payments": {
"ca_bc_etf_table": "ICBC ETF Table",
"ca_bc_etf_table": "ICBC EFT Table",
"exported_payroll": "Payroll Table"
},
"special": {
@@ -2246,7 +2283,8 @@
},
"subjects": {
"jobs": {
"parts_order": "Parts Order PO: {{ro_number}} - {{name}}"
"parts_order": "Parts Order PO: {{ro_number}} - {{name}}",
"sublet_order": "Sublet Order PO: {{ro_number}} - {{name}}"
}
},
"vendors": {
@@ -2365,6 +2403,7 @@
"credits_not_received_date": "Credits not Received by Date",
"credits_not_received_date_vendorid": "Credits not Received by Vendor",
"csi": "CSI Responses",
"cycle_time_analysis": "Cycle Time Analysis",
"estimates_written_converted": "Estimates Written/Converted",
"estimator_detail": "Jobs by Estimator (Detail)",
"estimator_summary": "Jobs by Estimator (Summary)",
@@ -2383,15 +2422,19 @@
"hours_sold_detail_closed": "Hours Sold Detail - Closed",
"hours_sold_detail_closed_csr": "Hours Sold Detail - Closed by CSR",
"hours_sold_detail_closed_ins_co": "Hours Sold Detail - Closed by Source",
"hours_sold_detail_closed_status": "Hours Sold Detail - Closed by Status",
"hours_sold_detail_open": "Hours Sold Detail - Open",
"hours_sold_detail_open_csr": "Hours Sold Detail - Open by CSR",
"hours_sold_detail_open_ins_co": "Hours Sold Detail - Open by Source",
"hours_sold_detail_open_status": "Hours Sold Detail - Open by Status",
"hours_sold_summary_closed": "Hours Sold Summary - Closed",
"hours_sold_summary_closed_csr": "Hours Sold Summary - Closed by CSR",
"hours_sold_summary_closed_ins_co": "Hours Sold Summary - Closed by Source",
"hours_sold_summary_closed_status": "Hours Sold Summary - Closed by Status",
"hours_sold_summary_open": "Hours Sold Summary - Open",
"hours_sold_summary_open_csr": "Hours Sold Summary - Open CSR",
"hours_sold_summary_open_ins_co": "Hours Sold Summary - Open by Source",
"hours_sold_summary_open_status": "Hours Sold Summary - Open by Status",
"job_costing_ro_csr": "Job Costing by CSR",
"job_costing_ro_date_detail": "Job Costing by RO - Detail",
"job_costing_ro_date_summary": "Job Costing by RO - Summary",
@@ -2407,6 +2450,7 @@
"parts_backorder": "IOU Parts List",
"parts_not_recieved": "Parts Not Received",
"parts_not_recieved_vendor": "Parts Not Received by Vendor",
"parts_received_not_scheduled": "Parts Received for Jobs Not Scheduled",
"payments_by_date": "Payments by Date",
"payments_by_date_type": "Payments by Date and Type",
"production_by_category": "Production by Category",
@@ -2419,6 +2463,7 @@
"production_by_target_date": "Production by Target Date",
"production_by_technician": "Production by Technician",
"production_by_technician_one": "Production filtered by Technician",
"psr_by_make": "Percent of Sales by Vehicle Make",
"purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)",
"purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)",
"purchases_by_date_range_detail": "Purchases by Date - Detail",
@@ -2443,6 +2488,9 @@
},
"schedule": {
"labels": {
"employeevacation": "Employee Vacations",
"intake": "Intake Events",
"manual": "Manual Events",
"manualevent": "Add Manual Event"
}
},
@@ -2520,7 +2568,8 @@
"creating": "Error creating time ticket. {{message}}",
"deleting": "Error deleting time ticket. {{message}}",
"noemployeeforuser": "Unable to use Shift Clock",
"noemployeeforuser_sub": "An employee record has not been created for this user. Please create one before using the shift clock. "
"noemployeeforuser_sub": "An employee record has not been created for this user. Please create one before using the shift clock. ",
"shiftalreadyclockedon": "You are already clocked onto a shift. Unable to create shift entry."
},
"fields": {
"actualhrs": "Actual Hours",
@@ -2760,6 +2809,7 @@
"country": "Country",
"discount": "Discount % (as decimal)",
"display_name": "Display Name",
"dmsid": "DMS ID",
"due_date": "Payment Due Date (# of days)",
"email": "Contact Email",
"favorite": "Favorite?",

View File

@@ -82,8 +82,13 @@
},
"audit": {
"fields": {
"cc": "",
"contents": "",
"created": "",
"operation": "",
"status": "",
"subject": "",
"to": "",
"useremail": "",
"values": ""
}
@@ -190,6 +195,7 @@
"entered_total": "",
"enteringcreditmemo": "",
"federal_tax": "",
"generatepartslabel": "",
"iouexists": "",
"local_tax": "",
"markexported": "",
@@ -258,6 +264,7 @@
"dms": {
"cashierid": "",
"default_journal": "",
"disablebillwip": "",
"disablecontactvehiclecreation": "",
"dms_acctnumber": "",
"dms_wip_acctnumber": "",
@@ -431,6 +438,7 @@
"ar": "",
"ats": "",
"federal_tax": "",
"federal_tax_itc": "",
"gst_override": "",
"la1": "",
"la2": "",
@@ -831,6 +839,7 @@
},
"errors": {
"deletes3": "Error al eliminar el documento del almacenamiento.",
"deleting": "",
"deleting_cloudinary": "",
"getpresignurl": "Error al obtener la URL prescrita para el documento. {{message}}",
"insert": "Incapaz de cargar el archivo. {{message}}",
@@ -842,6 +851,7 @@
"doctype": "",
"newjobid": "",
"openinexplorer": "",
"optimizedimage": "",
"reassign_limitexceeded": "",
"reassign_limitexceeded_title": "",
"storageexceeded": "",
@@ -872,6 +882,7 @@
"labels": {
"attachments": "",
"documents": "",
"emailpreview": "",
"generatingemail": "",
"pdfcopywillbeattached": "",
"preview": ""
@@ -960,6 +971,7 @@
"save": "Salvar",
"saveandnew": "",
"selectall": "",
"send": "",
"senderrortosupport": "",
"submit": "",
"tryagain": "",
@@ -1029,6 +1041,7 @@
"sendby": "",
"signin": "",
"sms": "",
"status": "",
"sub_status": {
"expired": ""
},
@@ -1345,6 +1358,7 @@
"depreciation_taxes": "Depreciación / Impuestos",
"dms": {
"address": "",
"amount": "",
"center": "",
"cost": "",
"cost_dms_acctnumber": "",
@@ -1354,6 +1368,7 @@
"id": "",
"inservicedate": "",
"journal": "",
"lines": "",
"name1": "",
"payer": {
"amount": "",
@@ -1462,6 +1477,10 @@
"production_vars": {
"note": ""
},
"qb_multiple_payers": {
"amount": "",
"name": ""
},
"queued_for_parts": "",
"rate_ats": "",
"rate_la1": "Tarifa LA1",
@@ -1535,11 +1554,13 @@
"actual_completion_inferred": "",
"actual_delivery_inferred": "",
"actual_in_inferred": "",
"additionalpayeroverallocation": "",
"additionaltotal": "",
"adjustmentrate": "",
"adjustments": "",
"adminwarning": "",
"allocations": "",
"alreadyaddedtoscoreboard": "",
"alreadyclosed": "",
"appointmentconfirmation": "¿Enviar confirmación al cliente?",
"associationwarning": "",
@@ -1601,8 +1622,10 @@
"difference": "",
"diskscan": "",
"dms": {
"apexported": "",
"damageto": "",
"defaultstory": "",
"disablebillwip": "",
"invoicedatefuture": "",
"kmoutnotgreaterthankmin": "",
"logs": "",
@@ -1614,6 +1637,7 @@
"documents-images": "",
"documents-other": "",
"duplicateconfirm": "",
"emailaudit": "",
"employeeassignments": "",
"estimatelines": "",
"estimator": "",
@@ -1626,6 +1650,7 @@
"importnote": "",
"inproduction": "",
"intakechecklist": "",
"iou": "",
"job": "",
"jobcosting": "",
"jobtotals": "",
@@ -1637,6 +1662,7 @@
"mapa": "",
"markforreexport": "",
"mash": "",
"multipayers": "",
"net_repairs": "",
"notes": "Notas",
"othertotal": "",
@@ -1648,6 +1674,7 @@
"partsfilter": "",
"partssubletstotal": "",
"partstotal": "",
"pimraryamountpayable": "",
"plitooltips": {
"billtotal": "",
"calculatedcreditsnotreceived": "",
@@ -2027,6 +2054,8 @@
"labels": {
"allpartsto": "",
"confirmdelete": "",
"custompercent": "",
"discount": "",
"email": "Enviar por correo electrónico",
"inthisorder": "Partes en este pedido",
"is_quote": "",
@@ -2034,12 +2063,15 @@
"newpartsorder": "",
"notyetordered": "",
"oec": "",
"order_type": "",
"orderhistory": "Historial de pedidos",
"parts_order": "",
"parts_orders": "",
"print": "Mostrar formulario impreso",
"receive": "",
"removefrompartsqueue": "",
"returnpartsorder": ""
"returnpartsorder": "",
"sublet_order": ""
},
"successes": {
"created": "Pedido de piezas creado con éxito.",
@@ -2071,6 +2103,7 @@
"customer": "",
"edit": "",
"electronicpayment": "",
"external": "",
"findermodal": "",
"insurance": "",
"new": "",
@@ -2080,6 +2113,7 @@
},
"successes": {
"exported": "",
"markexported": "",
"payment": "",
"stripe": ""
}
@@ -2190,6 +2224,8 @@
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
"paint_grid": "",
"parts_invoice_label_single": "",
"parts_label_multiple": "",
"parts_label_single": "",
"parts_list": "",
"parts_order": "",
@@ -2208,6 +2244,7 @@
"sgi_certificate_of_repairs": "",
"sgi_windshield_auth": "",
"stolen_recovery_checklist": "",
"sublet_order": "",
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
@@ -2246,7 +2283,8 @@
},
"subjects": {
"jobs": {
"parts_order": ""
"parts_order": "",
"sublet_order": ""
}
},
"vendors": {
@@ -2365,6 +2403,7 @@
"credits_not_received_date": "",
"credits_not_received_date_vendorid": "",
"csi": "",
"cycle_time_analysis": "",
"estimates_written_converted": "",
"estimator_detail": "",
"estimator_summary": "",
@@ -2383,15 +2422,19 @@
"hours_sold_detail_closed": "",
"hours_sold_detail_closed_csr": "",
"hours_sold_detail_closed_ins_co": "",
"hours_sold_detail_closed_status": "",
"hours_sold_detail_open": "",
"hours_sold_detail_open_csr": "",
"hours_sold_detail_open_ins_co": "",
"hours_sold_detail_open_status": "",
"hours_sold_summary_closed": "",
"hours_sold_summary_closed_csr": "",
"hours_sold_summary_closed_ins_co": "",
"hours_sold_summary_closed_status": "",
"hours_sold_summary_open": "",
"hours_sold_summary_open_csr": "",
"hours_sold_summary_open_ins_co": "",
"hours_sold_summary_open_status": "",
"job_costing_ro_csr": "",
"job_costing_ro_date_detail": "",
"job_costing_ro_date_summary": "",
@@ -2407,6 +2450,7 @@
"parts_backorder": "",
"parts_not_recieved": "",
"parts_not_recieved_vendor": "",
"parts_received_not_scheduled": "",
"payments_by_date": "",
"payments_by_date_type": "",
"production_by_category": "",
@@ -2419,6 +2463,7 @@
"production_by_target_date": "",
"production_by_technician": "",
"production_by_technician_one": "",
"psr_by_make": "",
"purchases_by_cost_center_detail": "",
"purchases_by_cost_center_summary": "",
"purchases_by_date_range_detail": "",
@@ -2443,6 +2488,9 @@
},
"schedule": {
"labels": {
"employeevacation": "",
"intake": "",
"manual": "",
"manualevent": ""
}
},
@@ -2520,7 +2568,8 @@
"creating": "",
"deleting": "",
"noemployeeforuser": "",
"noemployeeforuser_sub": ""
"noemployeeforuser_sub": "",
"shiftalreadyclockedon": ""
},
"fields": {
"actualhrs": "",
@@ -2760,6 +2809,7 @@
"country": "País",
"discount": "% De descuento",
"display_name": "Nombre para mostrar",
"dmsid": "",
"due_date": "Fecha de vencimiento del pago",
"email": "Email de contacto",
"favorite": "¿Favorito?",

View File

@@ -82,8 +82,13 @@
},
"audit": {
"fields": {
"cc": "",
"contents": "",
"created": "",
"operation": "",
"status": "",
"subject": "",
"to": "",
"useremail": "",
"values": ""
}
@@ -190,6 +195,7 @@
"entered_total": "",
"enteringcreditmemo": "",
"federal_tax": "",
"generatepartslabel": "",
"iouexists": "",
"local_tax": "",
"markexported": "",
@@ -258,6 +264,7 @@
"dms": {
"cashierid": "",
"default_journal": "",
"disablebillwip": "",
"disablecontactvehiclecreation": "",
"dms_acctnumber": "",
"dms_wip_acctnumber": "",
@@ -431,6 +438,7 @@
"ar": "",
"ats": "",
"federal_tax": "",
"federal_tax_itc": "",
"gst_override": "",
"la1": "",
"la2": "",
@@ -831,6 +839,7 @@
},
"errors": {
"deletes3": "Erreur lors de la suppression du document du stockage.",
"deleting": "",
"deleting_cloudinary": "",
"getpresignurl": "Erreur lors de l'obtention de l'URL présignée pour le document. {{message}}",
"insert": "Incapable de télécharger le fichier. {{message}}",
@@ -842,6 +851,7 @@
"doctype": "",
"newjobid": "",
"openinexplorer": "",
"optimizedimage": "",
"reassign_limitexceeded": "",
"reassign_limitexceeded_title": "",
"storageexceeded": "",
@@ -872,6 +882,7 @@
"labels": {
"attachments": "",
"documents": "",
"emailpreview": "",
"generatingemail": "",
"pdfcopywillbeattached": "",
"preview": ""
@@ -960,6 +971,7 @@
"save": "sauvegarder",
"saveandnew": "",
"selectall": "",
"send": "",
"senderrortosupport": "",
"submit": "",
"tryagain": "",
@@ -1029,6 +1041,7 @@
"sendby": "",
"signin": "",
"sms": "",
"status": "",
"sub_status": {
"expired": ""
},
@@ -1345,6 +1358,7 @@
"depreciation_taxes": "Amortissement / taxes",
"dms": {
"address": "",
"amount": "",
"center": "",
"cost": "",
"cost_dms_acctnumber": "",
@@ -1354,6 +1368,7 @@
"id": "",
"inservicedate": "",
"journal": "",
"lines": "",
"name1": "",
"payer": {
"amount": "",
@@ -1462,6 +1477,10 @@
"production_vars": {
"note": ""
},
"qb_multiple_payers": {
"amount": "",
"name": ""
},
"queued_for_parts": "",
"rate_ats": "",
"rate_la1": "Taux LA1",
@@ -1535,11 +1554,13 @@
"actual_completion_inferred": "",
"actual_delivery_inferred": "",
"actual_in_inferred": "",
"additionalpayeroverallocation": "",
"additionaltotal": "",
"adjustmentrate": "",
"adjustments": "",
"adminwarning": "",
"allocations": "",
"alreadyaddedtoscoreboard": "",
"alreadyclosed": "",
"appointmentconfirmation": "Envoyer une confirmation au client?",
"associationwarning": "",
@@ -1601,8 +1622,10 @@
"difference": "",
"diskscan": "",
"dms": {
"apexported": "",
"damageto": "",
"defaultstory": "",
"disablebillwip": "",
"invoicedatefuture": "",
"kmoutnotgreaterthankmin": "",
"logs": "",
@@ -1614,6 +1637,7 @@
"documents-images": "",
"documents-other": "",
"duplicateconfirm": "",
"emailaudit": "",
"employeeassignments": "",
"estimatelines": "",
"estimator": "",
@@ -1626,6 +1650,7 @@
"importnote": "",
"inproduction": "",
"intakechecklist": "",
"iou": "",
"job": "",
"jobcosting": "",
"jobtotals": "",
@@ -1637,6 +1662,7 @@
"mapa": "",
"markforreexport": "",
"mash": "",
"multipayers": "",
"net_repairs": "",
"notes": "Remarques",
"othertotal": "",
@@ -1648,6 +1674,7 @@
"partsfilter": "",
"partssubletstotal": "",
"partstotal": "",
"pimraryamountpayable": "",
"plitooltips": {
"billtotal": "",
"calculatedcreditsnotreceived": "",
@@ -2027,6 +2054,8 @@
"labels": {
"allpartsto": "",
"confirmdelete": "",
"custompercent": "",
"discount": "",
"email": "Envoyé par email",
"inthisorder": "Pièces dans cette commande",
"is_quote": "",
@@ -2034,12 +2063,15 @@
"newpartsorder": "",
"notyetordered": "",
"oec": "",
"order_type": "",
"orderhistory": "Historique des commandes",
"parts_order": "",
"parts_orders": "",
"print": "Afficher le formulaire imprimé",
"receive": "",
"removefrompartsqueue": "",
"returnpartsorder": ""
"returnpartsorder": "",
"sublet_order": ""
},
"successes": {
"created": "Commande de pièces créée avec succès.",
@@ -2071,6 +2103,7 @@
"customer": "",
"edit": "",
"electronicpayment": "",
"external": "",
"findermodal": "",
"insurance": "",
"new": "",
@@ -2080,6 +2113,7 @@
},
"successes": {
"exported": "",
"markexported": "",
"payment": "",
"stripe": ""
}
@@ -2190,6 +2224,8 @@
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
"paint_grid": "",
"parts_invoice_label_single": "",
"parts_label_multiple": "",
"parts_label_single": "",
"parts_list": "",
"parts_order": "",
@@ -2208,6 +2244,7 @@
"sgi_certificate_of_repairs": "",
"sgi_windshield_auth": "",
"stolen_recovery_checklist": "",
"sublet_order": "",
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
@@ -2246,7 +2283,8 @@
},
"subjects": {
"jobs": {
"parts_order": ""
"parts_order": "",
"sublet_order": ""
}
},
"vendors": {
@@ -2365,6 +2403,7 @@
"credits_not_received_date": "",
"credits_not_received_date_vendorid": "",
"csi": "",
"cycle_time_analysis": "",
"estimates_written_converted": "",
"estimator_detail": "",
"estimator_summary": "",
@@ -2383,15 +2422,19 @@
"hours_sold_detail_closed": "",
"hours_sold_detail_closed_csr": "",
"hours_sold_detail_closed_ins_co": "",
"hours_sold_detail_closed_status": "",
"hours_sold_detail_open": "",
"hours_sold_detail_open_csr": "",
"hours_sold_detail_open_ins_co": "",
"hours_sold_detail_open_status": "",
"hours_sold_summary_closed": "",
"hours_sold_summary_closed_csr": "",
"hours_sold_summary_closed_ins_co": "",
"hours_sold_summary_closed_status": "",
"hours_sold_summary_open": "",
"hours_sold_summary_open_csr": "",
"hours_sold_summary_open_ins_co": "",
"hours_sold_summary_open_status": "",
"job_costing_ro_csr": "",
"job_costing_ro_date_detail": "",
"job_costing_ro_date_summary": "",
@@ -2407,6 +2450,7 @@
"parts_backorder": "",
"parts_not_recieved": "",
"parts_not_recieved_vendor": "",
"parts_received_not_scheduled": "",
"payments_by_date": "",
"payments_by_date_type": "",
"production_by_category": "",
@@ -2419,6 +2463,7 @@
"production_by_target_date": "",
"production_by_technician": "",
"production_by_technician_one": "",
"psr_by_make": "",
"purchases_by_cost_center_detail": "",
"purchases_by_cost_center_summary": "",
"purchases_by_date_range_detail": "",
@@ -2443,6 +2488,9 @@
},
"schedule": {
"labels": {
"employeevacation": "",
"intake": "",
"manual": "",
"manualevent": ""
}
},
@@ -2520,7 +2568,8 @@
"creating": "",
"deleting": "",
"noemployeeforuser": "",
"noemployeeforuser_sub": ""
"noemployeeforuser_sub": "",
"shiftalreadyclockedon": ""
},
"fields": {
"actualhrs": "",
@@ -2760,6 +2809,7 @@
"country": "Pays",
"discount": "Remise %",
"display_name": "Afficher un nom",
"dmsid": "",
"due_date": "Date limite de paiement",
"email": "Email du contact",
"favorite": "Préféré?",

View File

@@ -40,6 +40,7 @@ const AuditTrailMapping = {
i18n.t("audit_trail.messages.admin_jobmarkforreexport"),
admin_jobmarkexported: () =>
i18n.t("audit_trail.messages.admin_jobmarkexported"),
};
export default AuditTrailMapping;

View File

@@ -22,5 +22,6 @@ const range = {
moment().startOf("quarter"),
moment().startOf("quarter").add(1, "quarter").subtract(1, "day"),
],
"Last 90 Days": [moment().add(-90, "days"), moment()],
};
export default range;

View File

@@ -504,6 +504,19 @@ export const TemplateList = (type, context) => {
key: "folder_label_multiple",
disabled: false,
},
parts_label_multiple: {
title: i18n.t("printcenter.jobs.parts_label_multiple"),
description: "Parts Label Multiple",
key: "parts_label_multiple",
disabled: false,
},
parts_invoice_label_single: {
title: i18n.t("printcenter.jobs.parts_invoice_label_single"),
description: "Parts Label Multiple",
key: "parts_invoice_label_single",
disabled: false,
ignoreCustomMargins: true,
},
csi_invitation_action: {
title: i18n.t("printcenter.jobs.csi_invitation_action"),
description: "CSI invite",
@@ -551,6 +564,20 @@ export const TemplateList = (type, context) => {
}),
disabled: false,
},
sublet_order: {
title: i18n.t("printcenter.jobs.sublet_order"),
description: "Parts Order",
key: "sublet_order",
subject: i18n.t("printcenter.subjects.jobs.sublet_order", {
ro_number: context && context.job && context.job.ro_number,
name: (
(context && context.job && context.job.ownr_ln) ||
(context && context.job && context.job.ownr_co_nm) ||
""
).trim(),
}),
disabled: false,
},
parts_return_slip: {
title: i18n.t("printcenter.jobs.parts_return_slip"),
subject: i18n.t("printcenter.jobs.parts_return_slip"),
@@ -766,6 +793,74 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_summary_open_status: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_open_status"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_summary_open_status"
),
key: "hours_sold_summary_open_status",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
group: "sales",
},
hours_sold_summary_closed_status: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_closed_status"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_summary_closed_status"
),
key: "hours_sold_summary_closed_status",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
group: "sales",
},
hours_sold_detail_open_status: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_open_status"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_detail_open_status"
),
key: "hours_sold_detail_open_status",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
group: "sales",
},
hours_sold_detail_closed_status: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_status"
),
description: "",
subject: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_status"
),
key: "hours_sold_detail_closed_status",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
group: "sales",
},
purchases_by_date_range_detail: {
title: i18n.t(
"reportcenter.templates.purchases_by_date_range_detail"
@@ -1559,6 +1654,46 @@ export const TemplateList = (type, context) => {
},
group: "jobs",
},
parts_received_not_scheduled: {
title: i18n.t(
"reportcenter.templates.parts_received_not_scheduled"
),
subject: i18n.t(
"reportcenter.templates.parts_received_not_scheduled"
),
key: "parts_received_not_scheduled",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open"),
},
group: "jobs",
},
psr_by_make: {
title: i18n.t("reportcenter.templates.psr_by_make"),
subject: i18n.t("reportcenter.templates.psr_by_make"),
key: "psr_by_make",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
group: "sales",
},
cycle_time_analysis: {
title: i18n.t("reportcenter.templates.cycle_time_analysis"),
subject: i18n.t("reportcenter.templates.cycle_time_analysis"),
key: "cycle_time_analysis",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.actual_completion"),
},
group: "jobs",
},
}
: {}),
...(!type || type === "courtesycarcontract"

View File

@@ -3228,10 +3228,10 @@ ansi-styles@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
antd@^4.21.7:
version "4.22.0"
resolved "https://registry.yarnpkg.com/antd/-/antd-4.22.0.tgz#fef6aa2de347718bca4820b73c22e1cac843dd66"
integrity sha512-ZKEfRF+wT21PVWrd1NKG7CQNUtiGOt35OV+ZKumazvubLUXHyFnJvueaN7vgcbcBmexFXC6TZvIL+TXWxa0zzg==
antd@^4.22.3:
version "4.22.3"
resolved "https://registry.yarnpkg.com/antd/-/antd-4.22.3.tgz#c6d4ced17e9c21b55c6234595d962c5f9d6768db"
integrity sha512-Nay1tO+G5Z9szmshK9TinOWclJnxOtSe7cr15EX64NGkZZyRoHX2xXOFQoYtBt4qfVfFvLf97m9on7fwgy1Svg==
dependencies:
"@ant-design/colors" "^6.0.0"
"@ant-design/icons" "^4.7.0"
@@ -3247,7 +3247,7 @@ antd@^4.21.7:
rc-checkbox "~2.3.0"
rc-collapse "~3.3.0"
rc-dialog "~8.9.0"
rc-drawer "~5.0.0-alpha.7"
rc-drawer "~5.1.0-alpha.1"
rc-dropdown "~4.0.0"
rc-field-form "~1.27.0"
rc-image "~5.7.0"
@@ -11420,10 +11420,10 @@ rc-dialog@~8.9.0:
rc-motion "^2.3.0"
rc-util "^5.21.0"
rc-drawer@~5.0.0-alpha.7:
version "5.0.0-alpha.7"
resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-5.0.0-alpha.7.tgz#c0491780559a77f64df3c687caf56a7b6a180f6f"
integrity sha512-3AXWO7VTCz//Ys66iF+b9FsKfvcmdKaR0wFwNHNL3Vw+YeMji5WfC3wE4snEAqCqUrR2Cw2wsihK+gPt4qbpZA==
rc-drawer@~5.1.0-alpha.1:
version "5.1.0-alpha.3"
resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-5.1.0-alpha.3.tgz#a516131bd36f36b886c00ece027837050cbd3c68"
integrity sha512-8SR6VD3ms0KhfwGa0KIhCUr/3p8iIuxIqo8cqizZzQKUjSGU0IcB/fteDV3IVRGMqVHhSSQp4hzvYfZsOI+kOA==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "^2.2.6"

View File

@@ -1,3 +1,3 @@
Must set the environment variables using:
firebase functions:config:set auth.graphql_endpoint="https://db.development.bodyshop.app/v1/graphql" auth.hasura_secret_admin_key="Dev-BodyShopApp!"
firebase functions:config:set auth.graphql_endpoint="https://db.dev.bodyshop.app/v1/graphql" auth.hasura_secret_admin_key="Dev-BodyShopApp!"

View File

@@ -0,0 +1,24 @@
Issue when migrating events resolved by running SQL below. User needed updating when running in prod.
CREATE TABLE IF NOT EXISTS hdb_catalog.hdb_source_catalog_version
(
version text COLLATE pg_catalog."default" NOT NULL,
upgraded_on timestamp with time zone NOT NULL
)
TABLESPACE pg_default;
ALTER TABLE hdb_catalog.hdb_source_catalog_version
OWNER to postgres;
-- Index: hdb_source_catalog_version_one_row
-- DROP INDEX hdb_catalog.hdb_source_catalog_version_one_row;
CREATE UNIQUE INDEX hdb_source_catalog_version_one_row
ON hdb_catalog.hdb_source_catalog_version USING btree
((version IS NOT NULL) ASC NULLS LAST)
TABLESPACE pg_default;
INSERT INTO hdb_catalog.hdb_source_catalog_version (version, upgraded_on) VALUES ('2', NOW());
https://devscope.io/code/hasura/graphql-engine/issues/8694

View File

@@ -1,5 +1,5 @@
version: 2
endpoint: https://db.development.bodyshop.app
endpoint: https://db.dev.bodyshop.app
admin_secret: Dev-BodyShopApp!
metadata_directory: metadata
actions:

View File

@@ -1,30 +1,30 @@
- function:
schema: public
name: search_bills
- function:
schema: public
- function:
name: search_cccontracts
- function:
schema: public
- function:
name: search_dms_vehicles
- function:
schema: public
- function:
name: search_exportlog
- function:
schema: public
- function:
name: search_inventory
- function:
schema: public
- function:
name: search_jobs
- function:
schema: public
- function:
name: search_owners
- function:
schema: public
- function:
name: search_payments
- function:
schema: public
- function:
name: search_phonebook
- function:
schema: public
- function:
name: search_vehicles
schema: public

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
DROP TABLE "public"."email_audit_trail";

View File

@@ -0,0 +1,18 @@
CREATE TABLE "public"."email_audit_trail" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "bodyshopid" uuid NOT NULL, "jobid" uuid, "noteid" uuid, "to" jsonb NOT NULL DEFAULT jsonb_build_array(), "cc" jsonb NOT NULL DEFAULT jsonb_build_array(), "subject" text, "contents" text, "useremail" text NOT NULL, PRIMARY KEY ("id") );
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
RETURNS TRIGGER AS $$
DECLARE
_new record;
BEGIN
_new := NEW;
_new."updated_at" = NOW();
RETURN _new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "set_public_email_audit_trail_updated_at"
BEFORE UPDATE ON "public"."email_audit_trail"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_email_audit_trail_updated_at" ON "public"."email_audit_trail"
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
CREATE EXTENSION IF NOT EXISTS pgcrypto;

View File

@@ -0,0 +1 @@
DROP TABLE "public"."email_audit_trail";

View File

@@ -0,0 +1,18 @@
CREATE TABLE "public"."email_audit_trail" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "bodyshopid" uuid NOT NULL, "jobid" uuid, "noteid" uuid, "to" jsonb NOT NULL DEFAULT jsonb_build_array(), "cc" jsonb NOT NULL DEFAULT jsonb_build_array(), "subject" text, "contents" text, "useremail" text NOT NULL, PRIMARY KEY ("id") );
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
RETURNS TRIGGER AS $$
DECLARE
_new record;
BEGIN
_new := NEW;
_new."updated_at" = NOW();
RETURN _new;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER "set_public_email_audit_trail_updated_at"
BEFORE UPDATE ON "public"."email_audit_trail"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_email_audit_trail_updated_at" ON "public"."email_audit_trail"
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
CREATE EXTENSION IF NOT EXISTS pgcrypto;

View File

@@ -0,0 +1 @@
alter table "public"."email_audit_trail" drop constraint "email_audit_trail_bodyshopid_fkey";

View File

@@ -0,0 +1,5 @@
alter table "public"."email_audit_trail"
add constraint "email_audit_trail_bodyshopid_fkey"
foreign key ("bodyshopid")
references "public"."bodyshops"
("id") on update cascade on delete cascade;

View File

@@ -0,0 +1 @@
alter table "public"."email_audit_trail" drop constraint "email_audit_trail_jobid_fkey";

View File

@@ -0,0 +1,5 @@
alter table "public"."email_audit_trail"
add constraint "email_audit_trail_jobid_fkey"
foreign key ("jobid")
references "public"."jobs"
("id") on update cascade on delete cascade;

View File

@@ -0,0 +1 @@
alter table "public"."email_audit_trail" drop constraint "email_audit_trail_useremail_fkey";

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