Compare commits

...

133 Commits

Author SHA1 Message Date
swtmply
3c3f9521f6 IO-2266 fixed key issue causing option to duplicate 2023-05-05 23:43:48 +08:00
Patrick Fic
6155b8bf24 Merged in release/2023-05-05 (pull request #754)
Add firebase auth to JSR call.
2023-05-04 21:04:16 +00:00
Patrick Fic
2586855f11 Add firebase auth to JSR call. 2023-05-04 13:33:16 -07:00
Patrick Fic
b3b3c4c737 Merged in release/2023-05-05 (pull request #752)
Resolve health check
2023-05-04 20:26:04 +00:00
Patrick Fic
abe5fadeea Resolve health check 2023-05-04 13:25:45 -07:00
Patrick Fic
f1a10e0df4 Merged in release/2023-05-05 (pull request #750)
Release/2023 05 05
2023-05-04 19:57:22 +00:00
Patrick Fic
7dc3c00628 Revert "Merged in feature/IO-1722-job-size-color (pull request #746)"
This reverts commit 7594f53e88, reversing
changes made to b861957342.
2023-05-04 12:50:28 -07:00
John Allen Delos Reyes
7594f53e88 Merged in feature/IO-1722-job-size-color (pull request #746)
Feature/IO-1722 job size color

Approved-by: Patrick Fic
2023-05-04 19:03:30 +00:00
Patrick Fic
b861957342 Additional security hardening. 2023-05-04 11:59:39 -07:00
swtmply
2bf24ff5a1 IO-1722 refactor color function 2023-05-04 23:00:37 +08:00
swtmply
833baca9cc Merge branch 'feature/IO-1722-job-size-color' of https://bitbucket.org/snaptsoft/bodyshop into feature/IO-1722-job-size-color 2023-05-04 01:27:24 +08:00
swtmply
889ef61185 IO-1722 fixed spacing on translations 2023-05-04 01:25:17 +08:00
swtmply
add1eddbc1 IO-1722 added translations 2023-05-04 01:18:16 +08:00
swtmply
0cabd80b94 IO-1722 job size color in production card 2023-05-04 00:39:50 +08:00
Patrick Fic
7f756bab88 Merged in feature/IO-2190-Autohouse (pull request #743)
IO-2190 Autohouse & Job Costing
2023-05-02 21:53:40 +00:00
Allan Carr
99b847822f Merged in feature/IO-2190-Autohouse (pull request #742)
IO-2190 Autohouse & Job Costing

Approved-by: Patrick Fic
2023-05-02 21:52:50 +00:00
Allan Carr
f66d9b8c09 IO-2190 Autohouse & Job Costing
Insure that amount going into Dinero is Integer
2023-05-02 14:51:47 -07:00
Patrick Fic
5660de42af Add support email to server side emails. 2023-05-02 12:31:18 -07:00
Patrick Fic
ccbe92c275 Merged in feature/IO-2190-Autohouse (pull request #741)
IO-2190 Correct lookup location for MAPA Hrs
2023-05-02 17:06:12 +00:00
Allan Carr
b0ea8a71fb Merged in feature/IO-2190-Autohouse (pull request #738)
IO-2190 Correct lookup location for MAPA Hrs

Approved-by: Patrick Fic
2023-05-01 22:30:13 +00:00
Allan Carr
060871306f Merged in feature/IO-2257-PBS-EXPORT-CREDIT (pull request #737)
IO-2257 PBS Export of Credits

Approved-by: Patrick Fic
2023-05-01 22:30:00 +00:00
Allan Carr
2eb4e142ff IO-2190 Correct lookup location for MAPA Hrs 2023-05-01 13:48:18 -07:00
Patrick Fic
adf8cf9e8d Additional hasura indexes. 2023-05-01 13:05:59 -07:00
Patrick Fic
e921f28105 Merged in release/2023-04-28 (pull request #736)
Additional database indexing.
2023-04-28 16:51:46 +00:00
Patrick Fic
51ebfd86e7 Additional database indexing. 2023-04-28 09:37:49 -07:00
Allan Carr
79a90bb9ee IO-2257 PBS Export of Credits
Amounts less then 0 (ie all credits) would not get pushed to transactionObject. Expand to be both sides of zero to allow for credits within transactionObject
2023-04-27 17:13:37 -07:00
Patrick Fic
1664f9c935 Merged in release/2023-04-28 (pull request #733)
Resolve Loader script issue.
2023-04-27 22:21:15 +00:00
Patrick Fic
ea7c22daec Resolve Loader script issue. 2023-04-27 15:20:41 -07:00
Patrick Fic
10ffb33ec9 Merged in release/2023-04-28 (pull request #731)
Remove unneeded imports.
2023-04-27 21:33:39 +00:00
Patrick Fic
f9e023f922 Remove unneeded imports. 2023-04-27 14:19:27 -07:00
Patrick Fic
c7832bdd82 Merged in release/2023-04-28 (pull request #730)
Release/2023 04 28
2023-04-27 21:13:54 +00:00
Patrick Fic
2df046c39d IO-1053 Fix issues with scoreboard updates. 2023-04-27 13:52:01 -07:00
Patrick Fic
46065f1986 IO-1933 Resolve tech efficency component. 2023-04-27 13:13:16 -07:00
swtmply
5eda224393 IO-1053 Added Job Total to graph 2023-04-28 01:42:22 +08:00
Patrick Fic
e387abcd14 IO-1741 IO-2147 IO-1933 Bug fixes for multiple tickets. 2023-04-27 10:20:46 -07:00
Allan Carr
4c2d4e20a6 IO-2190 Factor Paint Costs from Scale over to Job Costing & Autohouse
Add use_paint_scale_data to bodyshop query
2023-04-27 09:40:12 -07:00
swtmply
d9e2ef9300 IO-1933 Added statistics on tech page 2023-04-27 22:19:32 +08:00
swtmply
b00fdadc1b IO-1741 Added touched property on calculate button 2023-04-27 22:19:00 +08:00
swtmply
4d5e06b9fc IO-2157 Added save to job notes button 2023-04-27 22:18:45 +08:00
Patrick Fic
1f4c1c9e92 IO-2210 Remove unused state. 2023-04-26 16:51:53 -07:00
Patrick Fic
2227acab3a IO-2210 Search Improvements 2023-04-26 16:45:20 -07:00
Patrick Fic
081165b6f5 Resolve missing items from merge conflicts. 2023-04-26 12:46:41 -07:00
Allan Carr
7e717c0b1f Merge branch 'release/2023-04-28' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-28
# Conflicts:
#	yarn.lock
2023-04-26 12:23:50 -07:00
Allan Carr
e242aaa9f5 IO-2190 Factor Paint Costs from Scale over to Job Costing & Autohouse
If Paint Scale Data exist in MixData then use the LiquidCost is use_paint_scale_data is set to true else fall back to cost calculations
2023-04-26 12:05:14 -07:00
Patrick Fic
2db88f57df Resolve missing items from merge conflicts. 2023-04-26 10:20:24 -07:00
Patrick Fic
cd0b7a4e56 Add additional indexes. 2023-04-26 09:42:43 -07:00
Allan Carr
2a3b4e89ab Merge branch 'release/2023-04-28' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-28
# Conflicts:
#	hasura/metadata/tables.yaml
2023-04-26 09:13:34 -07:00
Allan Carr
1d77eda3e2 IO-2190 Add extra field in for this item 2023-04-26 09:02:36 -07:00
swtmply
490e1f696a IO-1741 Added touched property on calculate button 2023-04-26 22:33:30 +08:00
swtmply
b96c618f54 IO-2157 Added save to job notes button 2023-04-26 22:32:46 +08:00
Patrick Fic
b9d11580d4 IO-2210 Opensearch implementation. 2023-04-25 18:47:11 -07:00
Patrick Fic
27fadb9ae2 Additional payroll schema tracking. 2023-04-25 10:16:56 -07:00
Patrick Fic
4c5a2cefe9 Payroll schema updates. 2023-04-25 09:33:17 -07:00
Patrick Fic
175692559c Merged in release/2023-04-28 (pull request #720)
Release/2023 04 28
2023-04-24 21:45:57 +00:00
Patrick Fic
57b27f73c3 Added a search filter for greater than 3 characters. 2023-04-24 14:45:08 -07:00
Patrick Fic
7badb09ba1 Timeticket Approval Queue schema updates for payroll updates. 2023-04-24 13:39:59 -07:00
Patrick Fic
c39f1d824a Merged in release/2023-04-21 (pull request #719)
Release/2023 04 21
2023-04-21 20:18:25 +00:00
Patrick Fic
132cf98a37 IO-2086 Resolve local media server pulling full size image for thumbnail. 2023-04-21 12:21:28 -07:00
Patrick Fic
f4b3a990d7 Additional schema changes for payroll. 2023-04-21 08:42:47 -07:00
swtmply
20371ea00d Merge branch 'release/2023-04-21' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-21 2023-04-21 22:28:00 +08:00
swtmply
3086a654a1 IO-1461 made the sider sticky 2023-04-21 22:27:48 +08:00
Patrick Fic
60768c8847 Timeticket approval queue aggregation permissions. 2023-04-20 15:28:14 -07:00
Patrick Fic
a68f8d6880 Payroll schema changes. 2023-04-20 15:05:23 -07:00
Allan Carr
522665256e IO-2335 Exported GSR to Excel
Change idtype to reporttype to prevent issues later on down the road if we extend more reports to excel format
2023-04-20 12:43:06 -07:00
swtmply
d3fe2c9d06 IO-2011 removed column background color 2023-04-21 03:37:36 +08:00
swtmply
f080e84985 IO-2242 added email on vendor query 2023-04-21 02:56:11 +08:00
Allan Carr
2a0ad46eea IO-2235
Changes in Prettier
2023-04-20 09:46:21 -07:00
Allan Carr
7725080a11 IO-2235 Exported GSR to Excel
For Morrey Body Shop
2023-04-20 09:33:22 -07:00
Allan Carr
1932795f55 Merge branch 'release/2023-04-21' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-21 2023-04-19 15:44:41 -07:00
Allan Carr
cc7bd1c792 IO-2243 DMS Posting Sheet Report
Requested by Morrey Auto Body
2023-04-19 15:44:09 -07:00
Patrick Fic
7799d93f3d Merged in release/2023-04-21 (pull request #714)
Disable job status transition tracking.
2023-04-19 21:26:37 +00:00
Patrick Fic
a7cf36d5f8 Disable job status transition tracking. 2023-04-19 14:26:12 -07:00
Patrick Fic
54cc02068c Merged in release/2023-04-21 (pull request #713)
Release/2023 04 21
2023-04-19 21:11:45 +00:00
Patrick Fic
f575870685 Added missed limit on global search. 2023-04-19 14:10:33 -07:00
Patrick Fic
171b61b92f Included a limit on global search query to limit performance impact. 2023-04-19 14:08:57 -07:00
Patrick Fic
de44116940 Merged in release/2023-04-21 (pull request #712)
Resolve status transition error message.
2023-04-19 20:08:39 +00:00
Patrick Fic
3c0a883326 Resolve status transition error message. 2023-04-19 13:08:18 -07:00
Patrick Fic
14d6cc94dd Merged in release/2023-04-21 (pull request #711)
IO-2086 Resolve cloudinary based thumbnail retrieval.
2023-04-19 18:45:22 +00:00
Patrick Fic
d787821345 IO-2086 Resolve cloudinary based thumbnail retrieval. 2023-04-19 11:43:24 -07:00
Patrick Fic
ed6eab4c38 Merged in release/2023-04-14 (pull request #708)
Remove erroneous console log.
2023-04-14 20:05:43 +00:00
Patrick Fic
b12c9407d9 Remove erroneous console log. 2023-04-14 13:05:09 -07:00
Patrick Fic
300aee5b02 Merged in release/2023-04-14 (pull request #707)
Release/2023 04 14
2023-04-14 20:04:15 +00:00
Patrick Fic
0a93551db4 Merge branch 'release/2023-04-14' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-14
# Conflicts:
#	client/src/components/scoreboard-timetickets/scoreboard-timetickets.component.jsx
2023-04-14 12:30:22 -07:00
Patrick Fic
8602ccbb8a IO-2240 Resolve efficiency calculation issue. 2023-04-14 12:29:11 -07:00
swtmply
d7b0e3046b IO-2240 Adjusted calculation for total efficiency 2023-04-15 03:26:56 +08:00
swtmply
0e06b449cb IO-2240 Adjusted the location of the calculation 2023-04-15 01:18:29 +08:00
swtmply
8e8208dd9a IO-2240 Moved the calculation within the function 2023-04-15 01:11:44 +08:00
swtmply
79e2fecb24 IO-2239 Removed total title 2023-04-14 23:33:14 +08:00
swtmply
86e909e4e9 Merge branch 'release/2023-04-14' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-14 2023-04-14 02:53:14 +08:00
swtmply
c7ff893397 IO-1412 Fixed column resize drag speed 2023-04-14 02:53:04 +08:00
Patrick Fic
3981b8684c IO-2244 Round difference calculation to 1 decimal point for productive hours claiming enforcement. 2023-04-13 10:11:15 -07:00
swtmply
1fb856f95f Merge branch 'release/2023-04-14' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-14 2023-04-14 00:41:00 +08:00
swtmply
c62c3fa938 IO-2240 total for productive hours in summary 2023-04-14 00:40:50 +08:00
Patrick Fic
be4feca990 Clean up unneeded import. 2023-04-13 09:26:24 -07:00
Patrick Fic
ec45454b3d IO-2244 Restrict claimable productive hours based on remaining hours 2023-04-13 09:25:52 -07:00
swtmply
0e78cb47f9 Merge branch 'release/2023-04-14' of https://bitbucket.org/snaptsoft/bodyshop into release/2023-04-14 2023-04-13 22:04:01 +08:00
Patrick Fic
e5b8d003ec IO-2137 Adjust PBS company name logic for posting. 2023-04-13 22:03:35 +08:00
Patrick Fic
2eb81dde37 Add ppc field to job lines. 2023-04-13 22:03:35 +08:00
Patrick Fic
614549a545 Include employee team changes from Payroll Based Changes. 2023-04-13 22:03:35 +08:00
Patrick Fic
5717727d2a IO-2243 Capture successful CDK posting details. 2023-04-13 22:03:35 +08:00
swtmply
f3714cea1e IO-2239 Added totals on cards in job scoreboards 2023-04-13 22:03:35 +08:00
swtmply
a3375e6152 IO-2238 Changed vehicle column to be a hyperlink 2023-04-13 22:03:24 +08:00
Patrick Fic
f48fb7130e IO-2137 Adjust PBS company name logic for posting. 2023-04-12 13:16:18 -07:00
Patrick Fic
1460fa6fd7 Add ppc field to job lines. 2023-04-12 13:13:51 -07:00
Patrick Fic
3d9a07bd39 Include employee team changes from Payroll Based Changes. 2023-04-12 13:08:39 -07:00
Patrick Fic
bd4aa4027a IO-2243 Capture successful CDK posting details. 2023-04-12 13:02:45 -07:00
swtmply
9fa995f002 IO-2239 Added totals on cards in job scoreboards 2023-04-12 03:13:19 +08:00
swtmply
3ed48b26f1 IO-2338 Changed vehicle column to be a hyperlink 2023-04-12 01:44:45 +08:00
swtmply
d585cacdfc IO-1412 Fixed column resize 2023-04-12 00:57:13 +08:00
Patrick Fic
55ddaca328 Merged in release/2023-04-07 (pull request #703)
Release/2023 04 07
2023-04-06 00:22:52 +00:00
Patrick Fic
146bf95e51 IO-2086 Resolve additional gallery import issue & tech login issue. 2023-04-05 17:22:18 -07:00
Patrick Fic
58defad2ea IO-2086 Resolve imports for additional classes with gallery. 2023-04-05 17:21:47 -07:00
Patrick Fic
9c40a03a06 Merged in release/2023-04-07 (pull request #701)
Release/2023 04 07
2023-04-05 22:09:28 +00:00
Patrick Fic
9b4247d6f6 Revert "fixed column resize"
This reverts commit 6e7d1abd70.
2023-04-05 14:20:15 -07:00
Patrick Fic
8a6d94f193 Resolve time ticket posting issue. 2023-04-05 14:02:34 -07:00
swtmply
6e7d1abd70 fixed column resize 2023-04-05 02:35:07 +08:00
swtmply
6bd74aae87 document galleries 2023-04-01 03:26:08 +08:00
Patrick Fic
f21caa10fc Merged in release/2023-03-21 (pull request #697)
IO-2213 Resolve issue with filtering on schedule screen.
2023-03-21 16:10:03 +00:00
Patrick Fic
3d164eb070 IO-2213 Resolve issue with filtering on schedule screen. 2023-03-21 09:09:03 -07:00
Patrick Fic
88ab3a21e2 Merged in release/2023-03-17 (pull request #695)
Release/2023 03 17
2023-03-17 18:44:06 +00:00
Patrick Fic
45bc1893a0 Language updates & default for lost sale reason. 2023-03-17 11:23:29 -07:00
Allan Carr
d053e682d7 IO-2193 Delete Owner & Vehicle
Pass error message on deleting to translation
2023-03-16 17:58:12 -07:00
Allan Carr
984a4a4cf6 IO-2193 Delete Owner & Vehicle 2023-03-16 16:44:12 -07:00
Patrick Fic
9438ef9683 IO-2177 Add enforce category on conversion. 2023-03-16 14:15:29 -07:00
Allan Carr
10a354e479 IO-2220 Customer List Report
Customer List report for Loewen Body Shop
2023-03-16 11:04:12 -07:00
Patrick Fic
31092c20a9 Updated hasura metadata 2023-03-16 07:59:40 -07:00
Patrick Fic
86beaf049c IO-2219 Resolve employees not needing job to post time ticket. 2023-03-14 14:41:14 -07:00
Patrick Fic
3bc0653230 IO-2215 Critical Parts scanning.
IO-2215 Critical Parts Scanning

IO-2215 critical parts scanning
2023-03-14 12:30:55 -07:00
Patrick Fic
b950b3f825 IO-2215 Critical Parts Scanning 2023-03-14 08:41:35 -07:00
Patrick Fic
3891fbefdf IO-2215 Critical parts scanning 2023-03-14 08:41:26 -07:00
Patrick Fic
73bcc72fc3 IO-2213 Add Ins Co filtering to several pages. 2023-03-13 14:37:05 -07:00
Patrick Fic
f3911859c7 IO-2214 Add lost sale reason tracking. 2023-03-13 12:07:25 -07:00
Patrick Fic
12e3d61cfb Merged in release/2023-03-10 (pull request #689)
Release/2023 03 10
2023-03-10 22:55:07 +00:00
164 changed files with 5514 additions and 1356 deletions

View File

@@ -1,5 +1,4 @@
{
"eslint.workingDirectories": ["./client", "./"],
"xml.fileAssociations": [
{
"pattern": "**/Test.xml",

View File

@@ -1,4 +1,4 @@
<babeledit_project be_version="2.7.1" version="1.2">
<babeledit_project version="1.2" be_version="2.7.1">
<!--
BabelEdit project file
@@ -3665,6 +3665,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>addpartsrule</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>addspeedprint</name>
<definition_loaded>false</definition_loaded>
@@ -4572,6 +4593,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>enforce_conversion_category</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>enforce_conversion_csr</name>
<definition_loaded>false</definition_loaded>
@@ -5411,6 +5453,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>md_lost_sale_reasons</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>md_parts_order_comment</name>
<definition_loaded>false</definition_loaded>
@@ -5432,6 +5495,53 @@
</translation>
</translations>
</concept_node>
<folder_node>
<name>md_parts_scan</name>
<children>
<concept_node>
<name>expression</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>flags</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<concept_node>
<name>md_payment_types</name>
<definition_loaded>false</definition_loaded>
@@ -8754,6 +8864,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>tt_enforce_hours_for_tech_console</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>use_fippa</name>
<definition_loaded>false</definition_loaded>
@@ -8775,6 +8906,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>use_paint_scale_data</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>uselocalmediaserver</name>
<definition_loaded>false</definition_loaded>
@@ -9609,6 +9761,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>partsscan</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>printlater</name>
<definition_loaded>false</definition_loaded>
@@ -16187,6 +16360,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>excel</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>exceptiontitle</name>
<definition_loaded>false</definition_loaded>
@@ -17179,6 +17373,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>total</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>totals</name>
<definition_loaded>false</definition_loaded>
@@ -20758,6 +20973,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>sendpartspricechange</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>sendtodms</name>
<definition_loaded>false</definition_loaded>
@@ -21204,6 +21440,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>partspricechange</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>saving</name>
<definition_loaded>false</definition_loaded>
@@ -24153,6 +24410,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>lost_sale_reason</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>ma2s</name>
<definition_loaded>false</definition_loaded>
@@ -33231,6 +33509,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>savetojobnotes</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
@@ -33612,6 +33911,27 @@
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>deleting</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>noaccess</name>
<definition_loaded>false</definition_loaded>
@@ -34173,6 +34493,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>deleteconfirm</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>existing_owners</name>
<definition_loaded>false</definition_loaded>
@@ -34283,6 +34624,27 @@
<folder_node>
<name>successes</name>
<children>
<concept_node>
<name>delete</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>save</name>
<definition_loaded>false</definition_loaded>
@@ -37290,6 +37652,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>dms_posting_sheet</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>envelope_return_address</name>
<definition_loaded>false</definition_loaded>
@@ -40775,6 +41158,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>customer_list</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>cycle_time_analysis</name>
<definition_loaded>false</definition_loaded>
@@ -40922,6 +41326,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>exported_gsr_by_ro</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>exported_gsr_by_ro_labor</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>gsr_by_atp</name>
<definition_loaded>false</definition_loaded>
@@ -42780,6 +43226,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ins_co_nm_filter</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>intake</name>
<definition_loaded>false</definition_loaded>
@@ -44744,6 +45211,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>hoursenteredmorethanavailable</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
</children>
@@ -47056,6 +47544,27 @@
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>deleting</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>noaccess</name>
<definition_loaded>false</definition_loaded>
@@ -47680,6 +48189,27 @@
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>deleteconfirm</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>fromvehicle</name>
<definition_loaded>false</definition_loaded>
@@ -47769,6 +48299,27 @@
<folder_node>
<name>successes</name>
<children>
<concept_node>
<name>delete</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>save</name>
<definition_loaded>false</definition_loaded>

View File

@@ -1,3 +1,4 @@
GENERATE_SOURCEMAP=false
REACT_APP_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql
REACT_APP_GRAPHQL_ENDPOINT_WS=wss://db.imex.online/v1/graphql
REACT_APP_GA_CODE=231103507

View File

@@ -46,10 +46,11 @@
"react-cookie": "^4.1.1",
"react-dom": "^17.0.2",
"react-drag-listview": "^0.2.1",
"react-grid-gallery": "^0.5.5",
"react-grid-gallery": "^1.0.0",
"react-grid-layout": "^1.3.4",
"react-i18next": "^12.2.0",
"react-icons": "^4.7.1",
"react-image-lightbox": "^5.1.4",
"react-number-format": "^5.1.3",
"react-redux": "^8.0.5",
"react-resizable": "^3.0.4",

View File

@@ -143,13 +143,16 @@
}
}
//Update row highlighting on production board.
//Update row highlighting on production board.
.ant-table-tbody > tr.ant-table-row:hover > td {
background: #eaeaea !important;
}
.job-line-manual{
.job-line-manual {
color: tomato;
font-style: italic;
}
td.ant-table-column-sort {
background-color: transparent;
}

View File

@@ -7,7 +7,9 @@ import { createStructuredSelector } from "reselect";
import { selectBreadcrumbs } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import GlobalSearch from "../global-search/global-search.component";
import GlobalSearchOs from "../global-search/global-search-os.component";
import "./breadcrumbs.styles.scss";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
breadcrumbs: selectBreadcrumbs,
@@ -15,6 +17,12 @@ const mapStateToProps = createStructuredSelector({
});
export function BreadCrumbs({ breadcrumbs, bodyshop }) {
const { OpenSearch } = useTreatments(
["OpenSearch"],
{},
bodyshop && bodyshop.imexshopid
);
return (
<Row className="breadcrumb-container">
<Col xs={24} sm={24} md={16}>
@@ -38,7 +46,7 @@ export function BreadCrumbs({ breadcrumbs, bodyshop }) {
</Breadcrumb>
</Col>
<Col xs={24} sm={24} md={8}>
<GlobalSearch />
{OpenSearch.treatment === "on" ? <GlobalSearchOs /> : <GlobalSearch />}
</Col>
</Row>
);

View File

@@ -10,7 +10,10 @@ export default function CABCpvrtCalculator({ disabled, form }) {
const handleFinish = async (values) => {
logImEXEvent("job_ca_bc_pvrt_calculate");
form.setFieldsValue({ ca_bc_pvrt: ((values.rate||0) * (values.days||0)).toFixed(2) });
form.setFieldsValue({
ca_bc_pvrt: ((values.rate || 0) * (values.days || 0)).toFixed(2),
});
form.setFields([{ name: "ca_bc_pvrt", touched: true }]);
setVisibility(false);
};

View File

@@ -0,0 +1,216 @@
import { AutoComplete, Divider, Input, Space } from "antd";
import axios from "axios";
import _ from "lodash";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link, useHistory } from "react-router-dom";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import OwnerNameDisplay, {
OwnerNameDisplayFunction,
} from "../owner-name-display/owner-name-display.component";
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
export default function GlobalSearchOs() {
const { t } = useTranslation();
const history = useHistory();
const [loading, setLoading] = useState(false);
const [data, setData] = useState(false);
const executeSearch = async (v) => {
if (v && v && v !== "" && v.length >= 3) {
try {
setLoading(true);
const searchData = await axios.post("/search", {
search: v,
});
const resultsByType = {
payments: [],
jobs: [],
bills: [],
owners: [],
vehicles: [],
};
searchData.data.hits.hits.forEach((hit) => {
resultsByType[hit._index].push(hit._source);
});
setData([
{
label: renderTitle(t("menus.header.search.jobs")),
options: resultsByType.jobs.map((job) => {
return {
key: job.id,
value: job.ro_number || "N/A",
label: (
<Link to={`/manage/jobs/${job.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<strong>{job.ro_number || t("general.labels.na")}</strong>
<span>{`${job.status || ""}`}</span>
<span>
<OwnerNameDisplay ownerObject={job} />
</span>
<span>{`${job.v_model_yr || ""} ${
job.v_make_desc || ""
} ${job.v_model_desc || ""}`}</span>
<span>{`${job.clm_no || ""}`}</span>
</Space>
</Link>
),
};
}),
},
{
label: renderTitle(t("menus.header.search.owners")),
options: resultsByType.owners.map((owner) => {
return {
key: owner.id,
value: OwnerNameDisplayFunction(owner),
label: (
<Link to={`/manage/owners/${owner.id}`}>
<Space
size="small"
split={<Divider type="vertical" />}
wrap
>
<span>
<OwnerNameDisplay ownerObject={owner} />
</span>
<PhoneNumberFormatter>
{owner.ownr_ph1}
</PhoneNumberFormatter>
<PhoneNumberFormatter>
{owner.ownr_ph2}
</PhoneNumberFormatter>
</Space>
</Link>
),
};
}),
},
{
label: renderTitle(t("menus.header.search.vehicles")),
options: resultsByType.vehicles.map((vehicle) => {
return {
key: vehicle.id,
value: `${vehicle.v_model_yr || ""} ${
vehicle.v_make_desc || ""
} ${vehicle.v_model_desc || ""}`,
label: (
<Link to={`/manage/vehicles/${vehicle.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<span>
{`${vehicle.v_model_yr || ""} ${
vehicle.v_make_desc || ""
} ${vehicle.v_model_desc || ""}`}
</span>
<span>{vehicle.plate_no || ""}</span>
<span>
<VehicleVinDisplay>
{vehicle.v_vin || ""}
</VehicleVinDisplay>
</span>
</Space>
</Link>
),
};
}),
},
{
label: renderTitle(t("menus.header.search.payments")),
options: resultsByType.payments.map((payment) => {
return {
key: payment.id,
value: `${payment.job?.ro_number} ${payment.amount}`,
label: (
<Link to={`/manage/jobs/${payment.job?.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<span>{payment.paymentnum}</span>
<span>{payment.job?.ro_number}</span>
<span>{payment.memo || ""}</span>
<span>{payment.amount || ""}</span>
<span>{payment.transactionid || ""}</span>
</Space>
</Link>
),
};
}),
},
{
label: renderTitle(t("menus.header.search.bills")),
options: resultsByType.bills.map((bill) => {
return {
key: bill.id,
value: `${bill.invoice_number} - ${bill.vendor.name}`,
label: (
<Link to={`/manage/bills?billid=${bill.id}`}>
<Space size="small" split={<Divider type="vertical" />}>
<span>{bill.invoice_number}</span>
<span>{bill.vendor.name}</span>
<span>{bill.date}</span>
</Space>
</Link>
),
};
}),
},
// {
// label: renderTitle(t("menus.header.search.phonebook")),
// options: resultsByType.search_phonebook.map((pb) => {
// return {
// key: pb.id,
// value: `${pb.firstname || ""} ${pb.lastname || ""} ${
// pb.company || ""
// }`,
// label: (
// <Link to={`/manage/phonebook?phonebookentry=${pb.id}`}>
// <Space size="small" split={<Divider type="vertical" />}>
// <span>{`${pb.firstname || ""} ${pb.lastname || ""} ${
// pb.company || ""
// }`}</span>
// <PhoneNumberFormatter>{pb.phone1}</PhoneNumberFormatter>
// <span>{pb.email}</span>
// </Space>
// </Link>
// ),
// };
// }),
// },
]);
} catch (error) {
console.log("Error while fetching search results", error);
} finally {
setLoading(false);
}
}
};
const debouncedExecuteSearch = _.debounce(executeSearch, 750);
const handleSearch = (value) => {
debouncedExecuteSearch(value);
};
const renderTitle = (title) => {
return <span>{title}</span>;
};
return (
<AutoComplete
options={data}
onSearch={handleSearch}
defaultActiveFirstOption
onSelect={(val, opt) => {
history.push(opt.label.props.to);
}}
onClear={() => setData([])}
>
<Input.Search
size="large"
placeholder={t("general.labels.globalsearch")}
enterButton
allowClear
loading={loading}
/>
</AutoComplete>
);
}

View File

@@ -8,7 +8,7 @@ import { GLOBAL_SEARCH_QUERY } from "../../graphql/search.queries";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import AlertComponent from "../alert/alert.component";
import OwnerNameDisplay, {
OwnerNameDisplayFunction
OwnerNameDisplayFunction,
} from "../owner-name-display/owner-name-display.component";
import VehicleVinDisplay from "../vehicle-vin-display/vehicle-vin-display.component";
export default function GlobalSearch() {
@@ -18,11 +18,18 @@ export default function GlobalSearch() {
useLazyQuery(GLOBAL_SEARCH_QUERY);
const executeSearch = (v) => {
if (v && v.variables.search && v.variables.search !== "") callSearch(v);
if (
v &&
v.variables.search &&
v.variables.search !== "" &&
v.variables.search.length >= 3
)
callSearch(v);
};
const debouncedExecuteSearch = _.debounce(executeSearch, 750);
const handleSearch = (value) => {
console.log("Handle Search");
debouncedExecuteSearch({ variables: { search: value } });
};
@@ -37,7 +44,7 @@ export default function GlobalSearch() {
options: data.search_jobs.map((job) => {
return {
key: job.id,
value: job.ro_number,
value: job.ro_number || "N/A",
label: (
<Link to={`/manage/jobs/${job.id}`}>
<Space size="small" split={<Divider type="vertical" />}>

View File

@@ -3,9 +3,11 @@ import {
Button,
Divider,
Dropdown,
Form,
Menu,
notification,
Popover,
Select,
Space,
} from "antd";
import parsePhoneNumber from "libphonenumber-js";
@@ -59,7 +61,10 @@ export function ScheduleEventComponent({
const blockContent = (
<div>
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
<Button
onClick={() => handleCancel({ id: event.id })}
disabled={event.arrived}
>
{t("appointments.actions.cancel")}
</Button>
</div>
@@ -203,10 +208,46 @@ export function ScheduleEventComponent({
<Button>{t("appointments.actions.sendreminder")}</Button>
</Dropdown>
) : null}
<Button onClick={() => handleCancel(event.id)} disabled={event.arrived}>
{t("appointments.actions.cancel")}
</Button>
<Popover
trigger="click"
disabled={event.arrived}
content={
<Form
layout="vertical"
onFinish={({ lost_sale_reason }) => {
handleCancel({ id: event.id, lost_sale_reason });
}}
>
<Form.Item
name="lost_sale_reason"
label={t("jobs.fields.lost_sale_reason")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
label: lsr,
value: lsr,
}))}
/>
</Form.Item>
<Button htmlType="submit">
{t("appointments.actions.cancel")}
</Button>
</Form>
}
>
<Button
// onClick={() => handleCancel(event.id)}
disabled={event.arrived}
>
{t("appointments.actions.cancel")}
</Button>
</Popover>
{event.isintake ? (
<Button
disabled={event.arrived}
@@ -249,7 +290,7 @@ export function ScheduleEventComponent({
const RegularEvent = event.isintake ? (
<Space
wrap
size='small'
size="small"
style={{
backgroundColor:
event.color && event.color.hex ? event.color.hex : event.color,

View File

@@ -11,7 +11,7 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
const { t } = useTranslation();
const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID);
const [updateJob] = useMutation(UPDATE_JOB);
const handleCancel = async (id) => {
const handleCancel = async ({ id, lost_sale_reason }) => {
logImEXEvent("schedule_cancel_appt");
const cancelAppt = await cancelAppointment({
@@ -38,7 +38,8 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
job: {
date_scheduled: null,
scheduled_in: null,
scheduled_completion:null,
scheduled_completion: null,
lost_sale_reason,
status: bodyshop.md_ro_statuses.default_imported,
},
},

View File

@@ -103,7 +103,12 @@ export function JobLinesComponent({
fixed: "left",
key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
onCell: (record) => ({ className: record.manual_line && "job-line-manual" }),
onCell: (record) => ({
className: record.manual_line && "job-line-manual",
style: {
...(record.critical ? { boxShadow: " -.5em 0 0 #FFC107" } : {}),
},
}),
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
ellipsis: true,
@@ -343,7 +348,7 @@ export function JobLinesComponent({
onClick={() => {
setJobLineEditContext({
actions: { refetch: refetch, submit: form && form.submit },
context: record,
context: { ...record, jobid: job.id },
});
}}
>

View File

@@ -14,8 +14,12 @@ import UndefinedToNull from "../../utils/undefinedtonull";
import JobLinesUpdsertModal from "./job-lines-upsert-modal.component";
import Axios from "axios";
import Dinero from "dinero.js";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { useTreatments } from "@splitsoftware/splitio-react";
const mapStateToProps = createStructuredSelector({
jobLineEditModal: selectJobLineEditModal,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("jobLineEdit")),
@@ -24,7 +28,13 @@ const mapDispatchToProps = (dispatch) => ({
function JobLinesUpsertModalContainer({
jobLineEditModal,
toggleModalVisible,
bodyshop,
}) {
const { CriticalPartsScanning } = useTreatments(
["CriticalPartsScanning"],
{},
bodyshop.imexshopid
);
const { t } = useTranslation();
const [insertJobLine] = useMutation(INSERT_NEW_JOB_LINE);
const [updateJobLine] = useMutation(UPDATE_JOB_LINE);
@@ -109,6 +119,9 @@ function JobLinesUpsertModalContainer({
}
toggleModalVisible();
}
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(jobLineEditModal.context.jobid);
}
setLoading(false);
};

View File

@@ -3,8 +3,9 @@ import {
useApolloClient,
useLazyQuery,
useMutation,
useQuery,
useQuery
} from "@apollo/client";
import { useTreatments } from "@splitsoftware/splitio-react";
import { Col, notification, Row } from "antd";
import Axios from "axios";
import Dinero from "dinero.js";
@@ -19,7 +20,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import {
DELETE_AVAILABLE_JOB,
QUERY_AVAILABLE_JOBS,
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK,
QUERY_AVAILABLE_NEW_JOBS_EST_DATA_BY_PK
} from "../../graphql/available-jobs.queries";
import { INSERT_NEW_JOB, UPDATE_JOB } from "../../graphql/jobs.queries";
import { INSERT_NEW_NOTE } from "../../graphql/notes.queries";
@@ -27,10 +28,11 @@ import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import {
selectBodyshop,
selectCurrentUser,
selectCurrentUser
} from "../../redux/user/user.selectors";
import confirmDialog from "../../utils/asyncConfirm";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import CriticalPartsScan from "../../utils/criticalPartsScan";
import AlertComponent from "../alert/alert.component";
import JobsAvailableScan from "../jobs-available-scan/jobs-available-scan.component";
import JobsFindModalContainer from "../jobs-find-modal/jobs-find-modal.container";
@@ -53,6 +55,11 @@ export function JobsAvailableContainer({
currentUser,
insertAuditTrail,
}) {
const { CriticalPartsScanning } = useTreatments(
["CriticalPartsScanning"],
{},
bodyshop.imexshopid
);
const { loading, error, data, refetch } = useQuery(QUERY_AVAILABLE_JOBS, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
@@ -155,6 +162,9 @@ export function JobsAvailableContainer({
},
})
.then((r) => {
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(r.data.insert_jobs.returning[0].id);
}
notification["success"]({
message: t("jobs.successes.created"),
onClick: () => {
@@ -241,7 +251,9 @@ export function JobsAvailableContainer({
},
},
});
if (CriticalPartsScanning.treatment === "on") {
CriticalPartsScan(updateResult.data.update_jobs.returning[0].id);
}
if (updateResult.errors) {
//error while inserting
notification["error"]({

View File

@@ -43,7 +43,7 @@ export function JobsConvertButton({
const { t } = useTranslation();
const [form] = Form.useForm();
const handleConvert = async ({ employee_csr, ...values }) => {
const handleConvert = async ({ employee_csr, category, ...values }) => {
if (parentFormIsFieldsTouched()) {
alert(t("jobs.labels.savebeforeconversion"));
return;
@@ -55,6 +55,7 @@ export function JobsConvertButton({
job: {
converted: true,
...(bodyshop.enforce_conversion_csr ? { employee_csr } : {}),
...(bodyshop.enforce_conversion_category ? { category } : {}),
...values,
},
},
@@ -94,6 +95,7 @@ export function JobsConvertButton({
driveable: true,
towin: false,
employee_csr: job.employee_csr,
category: job.category,
}}
>
<Form.Item
@@ -107,8 +109,8 @@ export function JobsConvertButton({
]}
>
<Select>
{bodyshop.md_ins_cos.map((s) => (
<Select.Option key={s.name} value={s.name}>
{bodyshop.md_ins_cos.map((s, i) => (
<Select.Option key={i} value={s.name}>
{s.name}
</Select.Option>
))}
@@ -197,6 +199,26 @@ export function JobsConvertButton({
</Select>
</Form.Item>
)}
{bodyshop.enforce_conversion_category && (
<Form.Item
name={"category"}
label={t("jobs.fields.category")}
rules={[
{
required: bodyshop.enforce_conversion_category,
//message: t("general.validation.required"),
},
]}
>
<Select allowClear>
{bodyshop.md_categories.map((s) => (
<Select.Option key={s} value={s}>
{s}
</Select.Option>
))}
</Select>
</Form.Item>
)}
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant"

View File

@@ -289,6 +289,12 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
>
<Input disabled={jobRO} />
</Form.Item>
<Form.Item
label={t("jobs.fields.lost_sale_reason")}
name="lost_sale_reason"
>
<Input disabled={jobRO} allowClear />
</Form.Item>
</FormRow>
</div>
);

View File

@@ -1,6 +1,15 @@
import { DownCircleFilled } from "@ant-design/icons";
import { useApolloClient, useMutation } from "@apollo/client";
import { Button, Dropdown, Menu, notification, Popconfirm } from "antd";
import {
Button,
Dropdown,
Form,
Menu,
notification,
Popconfirm,
Popover,
Select,
} from "antd";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -127,35 +136,63 @@ export function JobsDetailHeaderActions({
<Menu.Item
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
>
<Popconfirm
title={t("general.labels.areyousure")}
okText="Yes"
cancelText="No"
onClick={(e) => e.stopPropagation()}
<Popover
trigger="click"
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
onConfirm={async () => {
const jobUpdate = await cancelAllAppointments({
variables: {
jobid: job.id,
job: {
date_scheduled: null,
scheduled_in: null,
scheduled_completion: null,
status: bodyshop.md_ro_statuses.default_imported,
},
},
});
if (!jobUpdate.errors) {
notification["success"]({
message: t("appointments.successes.canceled"),
});
return;
}
}}
getPopupContainer={(trigger) => trigger.parentNode}
content={
<Form
layout="vertical"
onFinish={async ({ lost_sale_reason }) => {
const jobUpdate = await cancelAllAppointments({
variables: {
jobid: job.id,
job: {
date_scheduled: null,
scheduled_in: null,
scheduled_completion: null,
lost_sale_reason,
status: bodyshop.md_ro_statuses.default_imported,
},
},
});
if (!jobUpdate.errors) {
notification["success"]({
message: t("appointments.successes.canceled"),
});
return;
}
}}
>
<Form.Item
name="lost_sale_reason"
label={t("jobs.fields.lost_sale_reason")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select
options={bodyshop.md_lost_sale_reasons.map((lsr) => ({
label: lsr,
value: lsr,
}))}
/>
</Form.Item>
<Button
htmlType="submit"
disabled={
job.status !== bodyshop.md_ro_statuses.default_scheduled
}
>
{t("appointments.actions.cancel")}
</Button>
</Form>
}
>
{t("menus.jobsactions.cancelallappointments")}
</Popconfirm>
</Popover>
</Menu.Item>
<Menu.Item
disabled={

View File

@@ -82,7 +82,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
>
<CurrencyInput disabled={jobRO || bodyshop.cdk_dealerid} />
</Form.Item>
<Space align="end">
<Space align="center">
<Form.Item label={t("jobs.fields.ca_bc_pvrt")} name="ca_bc_pvrt">
<CurrencyInput disabled={jobRO} min={0} />
</Form.Item>

View File

@@ -1,7 +1,7 @@
import { EditFilled, FileExcelFilled, SyncOutlined } from "@ant-design/icons";
import { Button, Card, Col, Row, Space } from "antd";
import React, { useEffect, useState } from "react";
import Gallery from "react-grid-gallery";
import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import DocumentsUploadComponent from "../documents-upload/documents-upload.component";
import { DetermineFileType } from "../documents-upload/documents-upload.utility";
@@ -11,6 +11,9 @@ import JobsDocumentsGalleryReassign from "./jobs-document-gallery.reassign.compo
import JobsDocumentsDeleteButton from "./jobs-documents-gallery.delete.component";
import JobsDocumentsGallerySelectAllComponent from "./jobs-documents-gallery.selectall.component";
import Lightbox from "react-image-lightbox";
import "react-image-lightbox/style.css";
function JobsDocumentsComponent({
data,
jobId,
@@ -23,11 +26,7 @@ function JobsDocumentsComponent({
}) {
const [galleryImages, setgalleryImages] = useState({ images: [], other: [] });
const { t } = useTranslation();
const [index, setIndex] = useState(0);
const onCurrentImageChange = (index) => {
setIndex(index);
};
const [modalState, setModalState] = useState({ open: false, index: 0 });
useEffect(() => {
let documents = data.reduce(
@@ -35,14 +34,16 @@ function JobsDocumentsComponent({
const fileType = DetermineFileType(value.type);
if (value.type.startsWith("image")) {
acc.images.push({
src: GenerateSrcUrl(value),
thumbnail: GenerateThumbUrl(value),
thumbnailHeight: 225,
thumbnailWidth: 225,
// src: GenerateSrcUrl(value),
src: GenerateThumbUrl(value),
// src: GenerateSrcUrl(value),
// thumbnail: GenerateThumbUrl(value),
fullsize: GenerateSrcUrl(value),
height: 225,
width: 225,
isSelected: false,
key: value.key,
extension: value.extension,
id: value.id,
type: value.type,
size: value.size,
@@ -62,7 +63,7 @@ function JobsDocumentsComponent({
const fileName = value.key.split("/").pop();
acc.other.push({
source: GenerateSrcUrl(value),
src: "",
src: thumb,
thumbnail: thumb,
tags: [
{
@@ -85,10 +86,9 @@ function JobsDocumentsComponent({
]
: []),
],
thumbnailHeight: 225,
thumbnailWidth: 225,
height: 225,
width: 225,
isSelected: false,
extension: value.extension,
key: value.key,
id: value.id,
@@ -148,35 +148,15 @@ function JobsDocumentsComponent({
<Card title={t("jobs.labels.documents-images")}>
<Gallery
images={galleryImages.images}
backdropClosesModal={true}
currentImageWillChange={onCurrentImageChange}
customControls={[
<Button
key="edit-button"
style={{
float: "right",
zIndex: "5",
}}
onClick={() => {
const newWindow = window.open(
`${window.location.protocol}//${window.location.host}/edit?documentId=${galleryImages.images[index].id}`,
"_blank",
"noopener,noreferrer"
);
if (newWindow) newWindow.opener = null;
}}
>
<EditFilled />
</Button>,
]}
onClickImage={(props) => {
window.open(
props.target.src,
"_blank",
"toolbar=0,location=0,menubar=0"
);
onClick={(index, item) => {
setModalState({ open: true, index: index });
// window.open(
// item.fullsize,
// "_blank",
// "toolbar=0,location=0,menubar=0"
// );
}}
onSelectImage={(index, image) => {
onSelect={(index, image) => {
setgalleryImages({
...galleryImages,
images: galleryImages.images.map((g, idx) =>
@@ -191,8 +171,6 @@ function JobsDocumentsComponent({
<Card title={t("jobs.labels.documents-other")}>
<Gallery
images={galleryImages.other}
backdropClosesModal={true}
enableLightbox={false}
thumbnailStyle={() => {
return {
backgroundImage: <FileExcelFilled />,
@@ -201,14 +179,14 @@ function JobsDocumentsComponent({
cursor: "pointer",
};
}}
onClickThumbnail={(index) => {
onClick={(index) => {
window.open(
galleryImages.other[index].source,
"_blank",
"toolbar=0,location=0,menubar=0"
);
}}
onSelectImage={(index) => {
onSelect={(index) => {
setgalleryImages({
...galleryImages,
other: galleryImages.other.map((g, idx) =>
@@ -219,6 +197,53 @@ function JobsDocumentsComponent({
/>
</Card>
</Col>
{modalState.open && (
<Lightbox
toolbarButtons={[
<EditFilled
onClick={() => {
const newWindow = window.open(
`${window.location.protocol}//${
window.location.host
}/edit?documentId=${
galleryImages.images[modalState.index].id
}`,
"_blank",
"noopener,noreferrer"
);
if (newWindow) newWindow.opener = null;
}}
/>,
]}
mainSrc={galleryImages.images[modalState.index].fullsize}
nextSrc={
galleryImages.images[
(modalState.index + 1) % galleryImages.images.length
].fullsize
}
prevSrc={
galleryImages.images[
(modalState.index + galleryImages.images.length - 1) %
galleryImages.images.length
].fullsize
}
onCloseRequest={() => setModalState({ open: false, index: 0 })}
onMovePrevRequest={() =>
setModalState({
...modalState,
index:
(modalState.index + galleryImages.images.length - 1) %
galleryImages.images.length,
})
}
onMoveNextRequest={() =>
setModalState({
...modalState,
index: (modalState.index + 1) % galleryImages.images.length,
})
}
/>
)}
</Row>
</div>
);

View File

@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import Gallery from "react-grid-gallery";
import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { GenerateSrcUrl, GenerateThumbUrl } from "./job-documents.utility";
import { GenerateThumbUrl } from "./job-documents.utility";
function JobsDocumentGalleryExternal({
data,
@@ -15,8 +15,8 @@ function JobsDocumentGalleryExternal({
let documents = data.reduce((acc, value) => {
if (value.type.startsWith("image")) {
acc.push({
src: GenerateSrcUrl(value),
thumbnail: GenerateThumbUrl(value),
//src: GenerateSrcUrl(value),
src: GenerateThumbUrl(value),
thumbnailHeight: 225,
thumbnailWidth: 225,
isSelected: false,
@@ -39,7 +39,7 @@ function JobsDocumentGalleryExternal({
<Gallery
images={galleryImages}
backdropClosesModal={true}
onSelectImage={(index, image) => {
onSelect={(index, image) => {
setgalleryImages(
galleryImages.map((g, idx) =>
index === idx ? { ...g, isSelected: !g.isSelected } : g

View File

@@ -1,7 +1,7 @@
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 React, { useEffect, useState } from "react";
import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -19,6 +19,9 @@ import JobsLocalGalleryDownloadButton from "./jobs-documents-local-gallery.downl
import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component";
import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-gallery.selectall.component";
import Lightbox from "react-image-lightbox";
import "react-image-lightbox/style.css";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
allMedia: selectAllMedia,
@@ -49,6 +52,7 @@ export function JobsDocumentsLocalGallery({
vendorid,
}) {
const { t } = useTranslation();
const [modalState, setModalState] = useState({ open: false, index: 0 });
useEffect(() => {
if (job) {
if (invoice_number) {
@@ -70,12 +74,20 @@ export function JobsDocumentsLocalGallery({
) {
acc.images.push({
...val,
fullsize: val.src,
src: val.thumbnail,
height: val.thumbnailHeight,
width: val.thumbnailWidth,
...(val.optimized && { src: val.optimized, fullsize: val.src }),
});
if (val.optimized) optimized = true;
} else {
acc.other.push({
...val,
fullsize: val.src,
src: val.thumbnail,
height: val.thumbnailHeight,
width: val.thumbnailWidth,
tags: [{ value: val.filename, title: val.filename }],
});
}
@@ -120,8 +132,7 @@ export function JobsDocumentsLocalGallery({
<Card title={t("jobs.labels.documents-images")}>
<Gallery
images={jobMedia.images}
backdropClosesModal={true}
onSelectImage={(index, image) => {
onSelect={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
{...(optimized && {
@@ -133,24 +144,23 @@ export function JobsDocumentsLocalGallery({
/>,
],
})}
onClickImage={(props) => {
const media = allMedia[job.id].find(
(m) => m.optimized === props.target.src
);
onClick={(index) => {
setModalState({ open: true, index: index });
// const media = allMedia[job.id].find(
// (m) => m.optimized === item.src
// );
window.open(
media ? media.src : props.target.src,
"_blank",
"toolbar=0,location=0,menubar=0"
);
// window.open(
// media ? media.fullsize : item.fullsize,
// "_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 />,
@@ -159,18 +169,48 @@ export function JobsDocumentsLocalGallery({
cursor: "pointer",
};
}}
onClickThumbnail={(index) => {
onClick={(index) => {
window.open(
jobMedia.other[index].src,
jobMedia.other[index].fullsize,
"_blank",
"toolbar=0,location=0,menubar=0"
);
}}
onSelectImage={(index, image) => {
onSelect={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
/>
</Card>
{modalState.open && (
<Lightbox
mainSrc={jobMedia.images[modalState.index].fullsize}
nextSrc={
jobMedia.images[(modalState.index + 1) % jobMedia.images.length]
.fullsize
}
prevSrc={
jobMedia.images[
(modalState.index + jobMedia.images.length - 1) %
jobMedia.images.length
].fullsize
}
onCloseRequest={() => setModalState({ open: false, index: 0 })}
onMovePrevRequest={() =>
setModalState({
...modalState,
index:
(modalState.index + jobMedia.images.length - 1) %
jobMedia.images.length,
})
}
onMoveNextRequest={() =>
setModalState({
...modalState,
index: (modalState.index + 1) % jobMedia.images.length,
})
}
/>
)}
</div>
);
}

View File

@@ -1,5 +1,5 @@
import React, { useEffect } from "react";
import Gallery from "react-grid-gallery";
import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -38,7 +38,7 @@ function JobDocumentsLocalGalleryExternal({
const { t } = useTranslation();
useEffect(() => {
if ( jobId) {
if (jobId) {
getJobMedia(jobId);
}
}, [jobId, getJobMedia]);
@@ -52,11 +52,15 @@ function JobDocumentsLocalGalleryExternal({
val.type.mime &&
val.type.mime.startsWith("image")
) {
acc.push(val);
acc.push({ ...val, src: val.thumbnail });
}
return acc;
}, [])
: [];
console.log(
"🚀 ~ file: jobs-documents-local-gallery.external.component.jsx:48 ~ useEffect ~ documents:",
documents
);
setgalleryImages(documents);
}, [allMedia, jobId, setgalleryImages, t]);
@@ -65,8 +69,7 @@ function JobDocumentsLocalGalleryExternal({
<div className="clearfix">
<Gallery
images={galleryImages}
backdropClosesModal={true}
onSelectImage={(index, image) => {
onSelect={(index, image) => {
setgalleryImages(
galleryImages.map((g, idx) =>
index === idx ? { ...g, isSelected: !g.isSelected } : g

View File

@@ -260,6 +260,19 @@ export function JobsList({ bodyshop }) {
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
filters:
(jobs &&
jobs
.map((j) => j.ins_co_nm)
.filter(onlyUnique)
.map((s) => {
return {
text: s,
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.ins_co_nm),
responsive: ["md"],
},
{

View File

@@ -272,6 +272,19 @@ export function JobsReadyList({ bodyshop }) {
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
filters:
(jobs &&
jobs
.map((j) => j.ins_co_nm)
.filter(onlyUnique)
.map((s) => {
return {
text: s,
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.ins_co_nm),
responsive: ["md"],
},
{

View File

@@ -6,10 +6,6 @@ export const CalculateAllocationsTotals = (
timetickets,
adjustments = []
) => {
console.log(
"🚀 ~ file: labor-allocations-table.utility.js ~ line 9 ~ adjustments",
adjustments
);
const responsibilitycenters = bodyshop.md_responsibility_centers;
const jobCodes = joblines.map((item) => item.mod_lbr_ty);
//.filter((value, index, self) => self.indexOf(value) === index && !!value);

View File

@@ -34,7 +34,7 @@ export function NoteUpsertModalContainer({
const [updateNote] = useMutation(UPDATE_NOTE);
const { visible, context, actions } = noteUpsertModal;
const { jobId, existingNote } = context;
const { jobId, existingNote, text } = context;
const { refetch } = actions;
const [form] = Form.useForm();
@@ -45,8 +45,12 @@ export function NoteUpsertModalContainer({
form.setFieldsValue(existingNote);
} else if (!existingNote && visible) {
form.resetFields();
if (text) {
form.setFieldValue("text", text);
}
}
}, [existingNote, form, visible]);
}, [existingNote, form, visible, text]);
const handleFinish = async (formValues) => {
const { relatedros, ...values } = formValues;
@@ -82,6 +86,7 @@ export function NoteUpsertModalContainer({
{ ...values, jobid: jobId, created_by: currentUser.email },
],
},
refetchQueries: ["QUERY_NOTES_BY_JOB_PK"],
});
if (AdditionalNoteInserts.length > 0) {

View File

@@ -1,15 +1,40 @@
import { Button, Form, notification, PageHeader } from "antd";
import { Button, Form, notification, PageHeader, Popconfirm } from "antd";
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import { useMutation } from "@apollo/client";
import { useTranslation } from "react-i18next";
import { UPDATE_OWNER } from "../../graphql/owners.queries";
import { DELETE_OWNER, UPDATE_OWNER } from "../../graphql/owners.queries";
import OwnerDetailFormComponent from "./owner-detail-form.component";
function OwnerDetailFormContainer({ owner, refetch }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const history = useHistory();
const [loading, setLoading] = useState(false);
const [updateOwner] = useMutation(UPDATE_OWNER);
const [deleteOwner] = useMutation(DELETE_OWNER);
const handleDelete = async () => {
setLoading(true);
const result = await deleteOwner({
variables: { id: owner.id },
});
console.log(result);
if (result.errors) {
notification["error"]({
message: t("owners.errors.deleting", {
error: JSON.stringify(result.errors),
}),
});
setLoading(false);
} else {
notification["success"]({
message: t("owners.successes.delete"),
});
setLoading(false);
history.push(`/manage/owners`);
}
};
const handleFinish = async (values) => {
setLoading(true);
@@ -41,15 +66,29 @@ function OwnerDetailFormContainer({ owner, refetch }) {
<>
<PageHeader
title={t("menus.header.owners")}
extra={
extra={[
<Popconfirm
trigger="click"
onConfirm={handleDelete}
disabled={owner.jobs.length !== 0}
title={t("owners.labels.deleteconfirm")}
>
<Button
type="danger"
loading={loading}
disabled={owner.jobs.length !== 0}
>
{t("general.actions.delete")}
</Button>
</Popconfirm>,
<Button
type="primary"
loading={loading}
onClick={() => form.submit()}
>
{t("general.actions.save")}
</Button>
}
</Button>,
]}
/>
<Form
form={form}

View File

@@ -201,6 +201,7 @@ export function PartsOrderListTableComponent({
subject: record.return
? Templates.parts_return_slip.subject
: Templates.parts_order.subject,
to: record.vendor.email,
}}
id={job.id}
/>
@@ -296,7 +297,6 @@ export function PartsOrderListTableComponent({
sortOrder:
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
},
{
title: t("parts_orders.fields.act_price"),
dataIndex: "act_price",

View File

@@ -91,11 +91,13 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
b.v_make_desc + b.v_model_desc
),
sortOrder:
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order,
render: (text, record) => (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
} ${record.v_color || ""} ${record.plate_no || ""}`}</span>
<Link to={`/manage/vehicles/${record.vehicleid}`}>{`${
record.v_model_yr || ""
} ${record.v_make_desc || ""} ${record.v_model_desc || ""} ${
record.v_color || ""
} ${record.plate_no || ""}`}</Link>
),
},
{

View File

@@ -1,12 +1,23 @@
import Icon from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, Input, Popover } from "antd";
import { Button, Input, Popover, Space } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { FaRegStickyNote } from "react-icons/fa";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
export default function ProductionListColumnProductionNote({ record }) {
import { setModalContext } from "../../redux/modals/modals.actions";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
const mapStateToProps = createStructuredSelector({});
const mapDispatchToProps = (dispatch) => ({
setNoteUpsertContext: (context) =>
dispatch(setModalContext({ context: context, modal: "noteUpsert" })),
});
function ProductionListColumnProductionNote({ record, setNoteUpsertContext }) {
const { t } = useTranslation();
const [note, setNote] = useState(
@@ -60,12 +71,26 @@ export default function ProductionListColumnProductionNote({ record }) {
// onPressEnter={handleSaveNote}
autoFocus
allowClear
style={{ marginBottom: "1em" }}
/>
<div>
<Button onClick={handleSaveNote}>
<Space>
<Button onClick={handleSaveNote} type="primary">
{t("general.actions.save")}
</Button>
</div>
<Button
onClick={() => {
setVisible(false);
setNoteUpsertContext({
context: {
jobId: record.id,
text: note,
},
});
}}
>
{t("notes.actions.savetojobnotes")}
</Button>
</Space>
</div>
}
trigger={["click"]}
@@ -85,3 +110,8 @@ export default function ProductionListColumnProductionNote({ record }) {
</Popover>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductionListColumnProductionNote);

View File

@@ -81,7 +81,7 @@ export function ProductionListTable({
state,
activeStatuses: bodyshop.md_ro_statuses.active_statuses,
}).find((e) => e.key === k.key),
width: k.width,
width: k.width ?? 100,
};
})) ||
[]
@@ -267,6 +267,8 @@ export function ProductionListTable({
sortOrder:
state.sortedInfo.columnKey === c.key && state.sortedInfo.order,
title: headerItem(c),
ellipsis: true,
width: c.width ?? 100,
onHeaderCell: (column) => ({
width: column.width,
onResize: handleResize(index),
@@ -276,11 +278,12 @@ export function ProductionListTable({
rowKey="id"
loading={loading}
dataSource={dataSource}
// scroll={{ x: true }}
scroll={{ x: 1000 }}
onChange={handleTableChange}
/>
</ReactDragListView.DragColumn>
</div>
);
}
export default connect(mapStateToProps, null)(ProductionListTable);

View File

@@ -3,8 +3,26 @@ import { Resizable } from "react-resizable";
export default function ResizableComponent(props) {
const { onResize, width, ...restProps } = props;
if (!width) {
return <th {...restProps} />;
}
return (
<Resizable width={width || 200} height={0} onResize={onResize}>
<Resizable
width={width || 200}
height={0}
onResize={onResize}
draggableOpts={{ enableUserSelectHack: false }}
handle={
<span
className="react-resizable-handle"
onClick={(e) => {
e.stopPropagation();
}}
/>
}
>
<th {...restProps} />
</Resizable>
);

View File

@@ -92,7 +92,11 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
to: values.to,
subject: Templates[values.key]?.subject,
},
values.sendby === "email" ? "e" : "p",
values.sendbyexcel === "excel"
? "x"
: values.sendby === "email"
? "e"
: "p",
id
);
setLoading(false);
@@ -250,15 +254,38 @@ export function ReportCenterModalComponent({ reportCenterModal }) {
ranges={DatePIckerRanges}
/>
</Form.Item>
<Form.Item
label={t("general.labels.sendby")}
name="sendby"
initialValue="print"
>
<Radio.Group>
<Radio value="email">{t("general.labels.email")}</Radio>
<Radio value="print">{t("general.labels.print")}</Radio>
</Radio.Group>
<Form.Item style={{ margin: 0, padding: 0 }} dependencies={["key"]}>
{() => {
const key = form.getFieldValue("key");
//Kind of Id
const reporttype = Templates[key] && Templates[key].reporttype;
if (reporttype === "excel")
return (
<Form.Item
label={t("general.labels.sendby")}
name="sendbyexcel"
initialValue="excel"
>
<Radio.Group>
<Radio value="excel">{t("general.labels.excel")}</Radio>
</Radio.Group>
</Form.Item>
);
if (reporttype !== "excel")
return (
<Form.Item
label={t("general.labels.sendby")}
name="sendby"
initialValue="print"
>
<Radio.Group>
<Radio value="email">{t("general.labels.email")}</Radio>
<Radio value="print">{t("general.labels.print")}</Radio>
</Radio.Group>
</Form.Item>
);
}}
</Form.Item>
<div

View File

@@ -1,5 +1,14 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Checkbox, Col, PageHeader, Row, Space } from "antd";
import {
Button,
Card,
Checkbox,
Col,
PageHeader,
Row,
Select,
Space,
} from "antd";
import { t } from "i18next";
import React, { useMemo } from "react";
import useLocalStorage from "../../utils/useLocalStorage";
@@ -9,22 +18,39 @@ import ScheduleModal from "../schedule-job-modal/schedule-job-modal.container";
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
import ScheduleProductionList from "../schedule-production-list/schedule-production-list.component";
import ScheduleVerifyIntegrity from "../schedule-verify-integrity/schedule-verify-integrity.component";
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
)(ScheduleCalendarComponent);
export default function ScheduleCalendarComponent({ data, refetch }) {
export function ScheduleCalendarComponent({ data, refetch, bodyshop }) {
const [filter, setFilter] = useLocalStorage("filter_events", {
intake: true,
manual: true,
employeevacation: true,
ins_co_nm: null,
});
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)
(d.block ||
(filter.intake && d.isintake) ||
(filter.manual && !d.isintake && d.block === false) ||
(d.__typename === "employee_vacation" &&
filter.employeevacation &&
!!d.employee)) &&
(filter.ins_co_nm && filter.ins_co_nm.length > 0
? filter.ins_co_nm.includes(d.job?.ins_co_nm)
: true)
);
}, [data, filter]);
@@ -37,6 +63,21 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
extra={
<Space wrap>
<ScheduleAtsSummary appointments={filteredData} />
<Select
style={{ minWidth: "15rem" }}
mode="multiple"
placeholder={t("schedule.labels.ins_co_nm_filter")}
allowClear
onClear={() => setFilter({ ...filter, ins_co_nm: [] })}
value={filter?.ins_co_nm ? filter.ins_co_nm : []}
onChange={(e) => {
setFilter({ ...filter, ins_co_nm: e });
}}
options={bodyshop.md_ins_cos.map((i) => ({
label: i.name,
value: i.name,
}))}
/>
<Checkbox
checked={filter?.intake}
onChange={(e) => {

View File

@@ -148,6 +148,7 @@ export function ScheduleJobModalContainer({
date_scheduled: new Date(),
scheduled_in: values.start,
scheduled_completion: values.scheduled_completion,
lost_sale_reason: null,
},
},
});

View File

@@ -0,0 +1,39 @@
import Dinero from "dinero.js";
const CustomTooltip = ({ active, payload, label }) => {
if (active && payload && payload.length) {
return (
<div
style={{
backgroundColor: "white",
border: "1px solid gray",
padding: "0.5rem",
}}
>
<p style={{ margin: "0" }}>{label}</p>
{payload.map((data, index) => {
if (data.dataKey === "sales" || data.dataKey === "accSales")
return (
<p
style={{ margin: "10px 0", color: data.color }}
key={index}
>{`${data.name} : ${Dinero({
amount: Math.round(data.value * 100),
}).toFormat()}`}</p>
);
return (
<p
style={{ margin: "10px 0", color: data.color }}
key={index}
>{`${data.name} : ${data.value}`}</p>
);
})}
</div>
);
}
return null;
};
export default CustomTooltip;

View File

@@ -1,3 +1,4 @@
import Dinero from "dinero.js";
import { Card } from "antd";
import moment from "moment";
import React from "react";
@@ -18,6 +19,7 @@ import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
import _ from "lodash";
import CustomTooltip from "./chart-custom-tooltip";
const graphProps = {
strokeWidth: 3,
@@ -44,14 +46,19 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
return {
bodyhrs: dayAcc.bodyhrs + dayVal.bodyhrs,
painthrs: dayAcc.painthrs + dayVal.painthrs,
sales:
dayAcc.painthrs +
dayVal.job.job_totals.totals.subtotal.amount / 100 +
2500,
};
},
{ bodyhrs: 0, painthrs: 0 }
{ bodyhrs: 0, painthrs: 0, sales: 0 }
);
} else {
dayhrs = {
bodyhrs: 0,
painthrs: 0,
sales: 0,
};
}
@@ -73,6 +80,13 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
: dayhrs.painthrs + dayhrs.bodyhrs,
1
),
sales: _.round(dayhrs.sales, 2),
accSales: _.round(
acc.length > 0
? acc[acc.length - 1].accSales + dayhrs.sales
: dayhrs.sales,
2
),
};
return [...acc, theValue];
@@ -87,22 +101,27 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
>
<CartesianGrid stroke="#f5f5f5" />
<XAxis dataKey="date" strokeWidth={graphProps.strokeWidth} />
<YAxis strokeWidth={graphProps.strokeWidth} />
<Tooltip />
<Legend />
<Area
type="monotone"
name="Accumulated Hours"
dataKey="accHrs"
fill="lightgreen"
stroke="green"
<YAxis
strokeWidth={graphProps.strokeWidth}
// allowDataOverflow
dataKey="sales"
yAxisId="right"
tickFormatter={(value) =>
Dinero({ amount: Math.round(value * 100) }).toFormat()
}
orientation="right"
/>
<YAxis yAxisId="left" strokeWidth={graphProps.strokeWidth} />
<Tooltip content={<CustomTooltip />} />
<Legend />
<Bar
name="Body Hours"
dataKey="bodyHrs"
stackId="day"
barSize={20}
fill="darkblue"
yAxisId="left"
/>
<Bar
name="Paint Hours"
@@ -110,12 +129,42 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
stackId="day"
barSize={20}
fill="darkred"
yAxisId="left"
/>
<Line
name="Target Hours"
type="monotone"
dataKey="accTargetHrs"
stroke="#ff7300"
yAxisId="left"
strokeWidth={graphProps.strokeWidth}
/>
<Area
type="monotone"
name="MTD Hours"
dataKey="accHrs"
fill="lightblue"
stroke="blue"
yAxisId="left"
/>
{
// <Area
// type="monotone"
// name="MTD Sales"
// dataKey="accSales"
// fill="lightgreen"
// stroke="green"
// yAxisId="right"
// />
}
<Bar
name="Sales"
dataKey="sales"
stackId="day"
barSize={20}
fill="darkgreen"
yAxisId="right"
strokeWidth={graphProps.strokeWidth}
/>
</ComposedChart>

View File

@@ -1,4 +1,4 @@
import { Card, Statistic } from "antd";
import { Card, Divider, Statistic } from "antd";
import moment from "moment";
import React from "react";
import { connect } from "react-redux";
@@ -41,6 +41,9 @@ export function ScoreboardDayStats({ bodyshop, date, entries }) {
label="P"
value={paintHrs.toFixed(1)}
/>
<Divider style={{ margin: 0 }} />
<Statistic value={(bodyHrs + paintHrs).toFixed(1)} />
</Card>
);
}

View File

@@ -1,5 +1,5 @@
import { CalendarOutlined } from "@ant-design/icons";
import { Card, Col, Row, Statistic } from "antd";
import { Card, Col, Divider, Row, Statistic } from "antd";
import _ from "lodash";
import moment from "moment";
import React, { useMemo } from "react";
@@ -177,6 +177,9 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
<Statistic value={values.toDatePaint.toFixed(1)} />
</Col>
</Row>
<Row>
<Divider style={{ margin: 5 }} />
</Row>
<Row>
<Col {...statSpans}></Col>
<Col {...statSpans}>
@@ -184,14 +187,53 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
value={(values.todayPaint + values.todayBody).toFixed(1)}
/>
</Col>
<Col {...statSpans}></Col>
<Col {...statSpans}>
<Statistic
value={(
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.WeeklyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(values.weeklyPaint + values.weeklyBody).toFixed(1)}
/>
</Col>
<Col {...statSpans}></Col>
<Col {...statSpans}></Col>
<Col {...statSpans}>
<Statistic
value={(
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.MonthlyTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(
Util.AsOfTodayTargetHrs(
bodyshop.scoreboard_target.dailyBodyTarget,
bodyshop
) +
Util.AsOfTodayTargetHrs(
bodyshop.scoreboard_target.dailyPaintTarget,
bodyshop
)
).toFixed(1)}
/>
</Col>
<Col {...statSpans}>
<Statistic
value={(values.toDatePaint + values.toDateBody).toFixed(1)}

View File

@@ -81,6 +81,7 @@ export default function ScoreboardTimeTickets() {
totalLastMonth: 0,
totalOverPeriod: 0,
actualTotalOverPeriod: 0,
totalEffieciencyOverPeriod: 0,
employees: {},
};
data.fixedperiod.forEach((ticket) => {
@@ -94,6 +95,7 @@ export default function ScoreboardTimeTickets() {
totalLastMonth: 0,
totalOverPeriod: 0,
actualTotalOverPeriod: 0,
totalEffieciencyOverPeriod: 0,
};
}
@@ -221,6 +223,30 @@ export default function ScoreboardTimeTickets() {
ret2.push(r);
});
// Add total efficiency of employees
const totalActualAndProductive = Object.keys(ret.employees)
.map((key) => {
return { employee_number: key, ...ret.employees[key] };
})
.reduce(
(acc, e) => {
return {
totalOverPeriod: acc.totalOverPeriod + e.totalOverPeriod,
actualTotalOverPeriod:
acc.actualTotalOverPeriod + e.actualTotalOverPeriod,
};
},
{ totalOverPeriod: 0, actualTotalOverPeriod: 0 }
);
ret.totalEffieciencyOverPeriod =
totalActualAndProductive.actualTotalOverPeriod
? (totalActualAndProductive.totalOverPeriod /
totalActualAndProductive.actualTotalOverPeriod) *
100
: 0;
roundObject(ret);
roundObject(totals);
roundObject(ret2);

View File

@@ -62,7 +62,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
key: "efficiencyoverperiod",
render: (text, record) =>
`${(
(record.totalOverPeriod / (record.actualTotalOverPeriod || .1)) *
(record.totalOverPeriod / (record.actualTotalOverPeriod || 0.1)) *
100
).toFixed(1)} %`,
},
@@ -113,6 +113,12 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
value={data.totalOverPeriod}
/>
</Col>
<Col span={12}>
<Statistic
title={t("scoreboard.labels.efficiencyoverperiod")}
value={`${data.totalEffieciencyOverPeriod || 0}%`}
/>
</Col>
</Row>
<Typography.Text type="secondary">
{t("scoreboard.labels.calendarperiod")}
@@ -121,7 +127,7 @@ export function ScoreboardTicketsStats({ data, bodyshop }) {
<Col md={24} lg={20}>
<Table
columns={columns}
rowKey='employee_number'
rowKey="employee_number"
dataSource={tableData}
id="employee_number"
scroll={{ y: "300px" }}

View File

@@ -1,3 +1,4 @@
import { useTreatments } from "@splitsoftware/splitio-react";
import { Button, Card, Tabs } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -8,6 +9,7 @@ import ShopInfoGeneral from "./shop-info.general.component";
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
import ShopInfoLaborRates from "./shop-info.laborrates.component";
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
import ShopInfoPartsScan from "./shop-info.parts-scan";
import ShopInfoRbacComponent from "./shop-info.rbac.component";
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
@@ -23,6 +25,11 @@ const mapDispatchToProps = (dispatch) => ({
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoComponent);
export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
const { CriticalPartsScanning } = useTreatments(
["CriticalPartsScanning"],
{},
bodyshop.imexshopid
);
const { t } = useTranslation();
return (
<Card
@@ -71,6 +78,11 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
<Tabs.TabPane key="laborrates" tab={t("bodyshop.labels.laborrates")}>
<ShopInfoLaborRates form={form} />
</Tabs.TabPane>
{CriticalPartsScanning.treatment === "on" && (
<Tabs.TabPane key="partsscan" tab={t("bodyshop.labels.partsscan")}>
<ShopInfoPartsScan form={form} />
</Tabs.TabPane>
)}
</Tabs>
</Card>
);

View File

@@ -11,13 +11,13 @@ import {
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
import PhoneFormItem, {
PhoneItemFormatterValidation,
} from "../form-items-formatted/phone-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 CurrencyInput from "../form-items-formatted/currency-form-item.component";
import FormItemEmail from "../form-items-formatted/email-form-item.component";
import momentTZ from "moment-timezone";
const timeZonesList = momentTZ.tz.names();
@@ -473,6 +473,13 @@ export default function ShopInfoGeneral({ form }) {
>
<Switch />
</Form.Item>
<Form.Item
name={["enforce_conversion_category"]}
label={t("bodyshop.fields.enforce_conversion_category")}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name={["target_touchtime"]}
label={t("bodyshop.fields.target_touchtime")}
@@ -544,6 +551,13 @@ export default function ShopInfoGeneral({ form }) {
>
<CurrencyInput />
</Form.Item>
<Form.Item
name={["use_paint_scale_data"]}
label={t("bodyshop.fields.use_paint_scale_data")}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name={["attach_pdf_to_email"]}
label={t("bodyshop.fields.attach_pdf_to_email")}
@@ -582,6 +596,13 @@ export default function ShopInfoGeneral({ form }) {
>
<Switch />
</Form.Item>
<Form.Item
name={["tt_enforce_hours_for_tech_console"]}
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name={["bill_allow_post_to_closed"]}
label={t("bodyshop.fields.bill_allow_post_to_closed")}

View File

@@ -0,0 +1,81 @@
import { DeleteFilled } from "@ant-design/icons";
import { Button, Form, Input, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
export default function ShopInfoPartsScan({ form }) {
const { t } = useTranslation();
return (
<div>
<LayoutFormRow header={t("bodyshop.labels.md_parts_scan")}>
<Form.List name={["md_parts_scan"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item
label={t("bodyshop.fields.md_parts_scan.expression")}
key={`${index}expression`}
name={[field.name, "expression"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.md_parts_scan.flags")}
key={`${index}flags`}
name={[field.name, "flags"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Input />
</Form.Item>
<Space wrap>
<DeleteFilled
onClick={() => {
remove(field.name);
}}
/>
<FormListMoveArrows
move={move}
index={index}
total={fields.length}
/>
</Space>
</LayoutFormRow>
</Form.Item>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "100%" }}
>
{t("bodyshop.actions.addpartsrule")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
</div>
);
}

View File

@@ -76,6 +76,19 @@ export default function ShopInfoSchedulingComponent({ form }) {
>
<InputNumber min={0} />
</Form.Item>
<Form.Item
name={["md_lost_sale_reasons"]}
label={t("bodyshop.fields.md_lost_sale_reasons")}
rules={[
{
// required: true,
//message: t("general.validation.required"),
type: "array",
},
]}
>
<Select mode="tags" />
</Form.Item>
</LayoutFormRow>
<Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider>
<Space wrap size="large">

View File

@@ -26,6 +26,10 @@ export function TechClockInContainer({
technician,
bodyshop,
}) {
console.log(
"🚀 ~ file: tech-job-clock-in-form.container.jsx:29 ~ technician:",
technician
);
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET, {
@@ -33,6 +37,10 @@ export function TechClockInContainer({
});
const { t } = useTranslation();
const emps = bodyshop.employees.filter(
(e) => e.id === (technician && technician.id)
)[0];
const handleFinish = async (values) => {
setLoading(true);
const theTime = (await axios.post("/utils/time")).data;
@@ -87,7 +95,12 @@ export function TechClockInContainer({
onClick={() => {
setTimeTicketContext({
actions: {},
context: { timeticket: { employeeid: technician.id } },
context: {
timeticket: {
employeeid: technician.id,
flat_rate: emps.flat_rate,
},
},
});
}}
>

View File

@@ -1,4 +1,4 @@
import { useMutation } from "@apollo/client";
import { useMutation, useQuery } from "@apollo/client";
import {
Button,
Card,
@@ -21,6 +21,8 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import TechJobClockoutDelete from "../tech-job-clock-out-delete/tech-job-clock-out-delete.component";
import { LaborAllocationContainer } from "../time-ticket-modal/time-ticket-modal.component";
import { GET_LINE_TICKET_BY_PK } from "../../graphql/jobs-lines.queries";
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -39,7 +41,17 @@ export function TechClockOffButton({
const [loading, setLoading] = useState(false);
const [updateTimeticket] = useMutation(UPDATE_TIME_TICKET);
const [form] = Form.useForm();
const { queryLoading, data: lineTicketData } = useQuery(
GET_LINE_TICKET_BY_PK,
{
variables: {
id: jobId,
},
skip: !jobId,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
}
);
const { t } = useTranslation();
const emps = bodyshop.employees.filter(
(e) => e.id === (technician && technician.id)
@@ -59,6 +71,7 @@ export function TechClockOffButton({
emps &&
emps.rates.filter((r) => r.cost_center === values.cost_center)[0]
?.rate,
flat_rate: emps && emps.flat_rate,
ciecacode:
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? values.cost_center
@@ -128,6 +141,54 @@ export function TechClockOffButton({
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
console.log(
bodyshop.tt_enforce_hours_for_tech_console
);
if (!bodyshop.tt_enforce_hours_for_tech_console) {
return Promise.resolve();
}
if (
!value ||
getFieldValue("cost_center") === null ||
!lineTicketData
)
return Promise.resolve();
//Check the cost center,
const totals = CalculateAllocationsTotals(
bodyshop,
lineTicketData.joblines,
lineTicketData.timetickets,
lineTicketData.jobs_by_pk.lbr_adjustments
);
const fieldTypeToCheck =
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? "mod_lbr_ty"
: "cost_center";
const costCenterDiff =
Math.round(
totals.find(
(total) =>
total[fieldTypeToCheck] ===
getFieldValue("cost_center")
)?.difference * 10
) / 10;
if (value > costCenterDiff)
return Promise.reject(
t(
"timetickets.validation.hoursenteredmorethanavailable"
)
);
else {
return Promise.resolve();
}
},
}),
]}
>
<InputNumber min={0} precision={1} />
@@ -177,7 +238,11 @@ export function TechClockOffButton({
</Col>
{!isShiftTicket && (
<Col span={16}>
<LaborAllocationContainer jobid={jobId} />
<LaborAllocationContainer
jobid={jobId || null}
loading={queryLoading}
lineTicketData={lineTicketData}
/>
</Col>
)}
</Row>

View File

@@ -0,0 +1,131 @@
import { useQuery } from "@apollo/client";
import { Card, Col, Space, Statistic, Typography } from "antd";
import moment from "moment";
import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE } from "../../graphql/timetickets.queries";
import { selectTechnician } from "../../redux/tech/tech.selectors";
import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const { Title } = Typography;
const mapStateToProps = createStructuredSelector({
technician: selectTechnician,
});
const mapDispatchToProps = (dispatch) => ({});
const TechJobStatistics = ({ technician }) => {
const { t } = useTranslation();
const startDate = moment().startOf("week");
const endDate = moment().endOf("week");
const { loading, error, data } = useQuery(
QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE,
{
variables: {
start: startDate.format("YYYY-MM-DD"),
end: endDate.format("YYYY-MM-DD"),
fixedStart: moment().startOf("month").format("YYYY-MM-DD"),
fixedEnd: moment().endOf("month").format("YYYY-MM-DD"),
employeeid: technician.id,
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
}
);
const totals = useMemo(() => {
if (data && data.timetickets && data.fixedperiod) {
const week = data.timetickets.reduce(
(acc, val) => {
acc.productivehrs = acc.productivehrs + val.productivehrs;
acc.actualhrs = acc.actualhrs + val.actualhrs;
return acc;
},
{ productivehrs: 0, actualhrs: 0 }
);
const month = data.fixedperiod.reduce(
(acc, val) => {
acc.productivehrs = acc.productivehrs + val.productivehrs;
acc.actualhrs = acc.actualhrs + val.actualhrs;
return acc;
},
{ productivehrs: 0, actualhrs: 0 }
);
return {
week,
month,
};
}
return {
week: { productivehrs: 0, actualhrs: 0 },
month: { productivehrs: 0, actualhrs: 0 },
};
}, [data]);
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<Card title={t("scoreboard.labels.productivestatistics")}>
<Space size={100}>
<Col>
<Title level={5}>{t("scoreboard.labels.thisweek")}</Title>
<Space size={20}>
<Statistic
title={t("timetickets.fields.productivehrs")}
value={totals.week.productivehrs.toFixed(2)}
/>
<Statistic
title={t("timetickets.fields.actualhrs")}
value={totals.week.actualhrs.toFixed(2)}
/>
<Statistic
title={t("timetickets.labels.efficiency")}
value={
totals.week.actualhrs
? `${(
(totals.week.productivehrs / totals.week.actualhrs) *
100
).toFixed(2)}%`
: "0%"
}
/>
</Space>
</Col>
<Col>
<Title level={5}>{t("scoreboard.labels.thismonth")}</Title>
<Space size={20}>
<Statistic
title={t("timetickets.fields.productivehrs")}
value={totals.month.productivehrs.toFixed(2)}
/>
<Statistic
title={t("timetickets.fields.actualhrs")}
value={totals.month.actualhrs.toFixed(2)}
/>
<Statistic
title={t("timetickets.labels.efficiency")}
value={
totals.month.actualhrs
? `${(
(totals.month.productivehrs / totals.month.actualhrs) *
100
).toFixed(2)}%`
: "0%"
}
/>
</Space>
</Col>
</Space>
</Card>
);
};
export default connect(mapStateToProps, mapDispatchToProps)(TechJobStatistics);

View File

@@ -29,7 +29,17 @@ export function TechSider({ technician, techLogout }) {
};
return (
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse}>
<Sider
style={{
height: "100vh",
position: "sticky",
top: 0,
left: 0,
}}
collapsible
collapsed={collapsed}
onCollapse={onCollapse}
>
<Menu theme="dark" defaultSelectedKeys={["1"]} mode="inline">
<Menu.Item
key="1"

View File

@@ -1,4 +1,4 @@
import { useQuery } from "@apollo/client";
import { useLazyQuery } from "@apollo/client";
import { Form, Input, InputNumber, Select, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -14,6 +14,7 @@ import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormDateTimePicker from "../form-date-time-picker/form-date-time-picker.component";
import JobSearchSelect from "../job-search-select/job-search-select.component";
import LaborAllocationsTable from "../labor-allocations-table/labor-allocations-table.component";
import { CalculateAllocationsTotals } from "../labor-allocations-table/labor-allocations-table.utility";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
@@ -38,7 +39,11 @@ export function TimeTicketModalComponent({
employeeSelectDisabled,
}) {
const { t } = useTranslation();
const [loadLineTicketData, { called, loading, data: lineTicketData }] =
useLazyQuery(GET_LINE_TICKET_BY_PK, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const CostCenterSelect = ({ emps, value, ...props }) => {
return (
<Select
@@ -82,9 +87,10 @@ export function TimeTicketModalComponent({
label={t("timetickets.fields.ro_number")}
rules={[
{
required:
!form.getFieldValue("cost_center") ===
"timetickets.labels.shift",
required: !(
form.getFieldValue("cost_center") ===
"timetickets.labels.shift"
),
//message: t("general.validation.required"),
},
]}
@@ -175,6 +181,51 @@ export function TimeTicketModalComponent({
label={t("timetickets.fields.productivehrs")}
name="productivehrs"
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (!bodyshop.tt_enforce_hours_for_tech_console) {
return Promise.resolve();
}
if (
!value ||
getFieldValue("cost_center") === null ||
!lineTicketData
)
return Promise.resolve();
//Check the cost center,
const totals = CalculateAllocationsTotals(
bodyshop,
lineTicketData.joblines,
lineTicketData.timetickets,
lineTicketData.jobs_by_pk.lbr_adjustments
);
const fieldTypeToCheck =
bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber
? "mod_lbr_ty"
: "cost_center";
const costCenterDiff =
Math.round(
totals.find(
(total) =>
total[fieldTypeToCheck] ===
getFieldValue("cost_center")
)?.difference * 10
) / 10;
if (value > costCenterDiff)
return Promise.reject(
t(
"timetickets.validation.hoursenteredmorethanavailable"
)
);
else {
return Promise.resolve();
}
},
}),
{
required:
form.getFieldValue("cost_center") !==
@@ -290,23 +341,28 @@ export function TimeTicketModalComponent({
</Form.Item>
</LayoutFormRow>
<Form.Item dependencies={["jobid"]}>
{() => (
<LaborAllocationContainer
jobid={form.getFieldValue("jobid") || null}
/>
)}
{() => {
const jobid = form.getFieldValue("jobid");
if (
(!called && jobid) ||
(jobid && lineTicketData?.jobs_by_pk?.id !== jobid && !loading)
) {
loadLineTicketData({ variables: { id: jobid } });
}
return (
<LaborAllocationContainer
jobid={jobid || null}
loading={loading}
lineTicketData={lineTicketData}
/>
);
}}
</Form.Item>
</div>
);
}
export function LaborAllocationContainer({ jobid }) {
const { loading, data: lineTicketData } = useQuery(GET_LINE_TICKET_BY_PK, {
variables: { id: jobid },
skip: !jobid,
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
export function LaborAllocationContainer({ jobid, loading, lineTicketData }) {
if (loading) return <LoadingSkeleton />;
if (!lineTicketData) return null;
return (

View File

@@ -1,16 +1,41 @@
import React, { useState } from "react";
import { Button, Form, notification, PageHeader } from "antd";
import { Button, Form, notification, PageHeader, Popconfirm } from "antd";
import { useMutation } from "@apollo/client";
import VehicleDetailFormComponent from "./vehicle-detail-form.component";
import { useTranslation } from "react-i18next";
import moment from "moment";
import { UPDATE_VEHICLE } from "../../graphql/vehicles.queries";
import { DELETE_VEHICLE, UPDATE_VEHICLE } from "../../graphql/vehicles.queries";
import { useHistory } from "react-router-dom";
function VehicleDetailFormContainer({ vehicle, refetch }) {
const { t } = useTranslation();
const [updateVehicle] = useMutation(UPDATE_VEHICLE);
const [deleteVehicle] = useMutation(DELETE_VEHICLE);
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const history = useHistory();
const handleDelete = async () => {
setLoading(true);
const result = await deleteVehicle({
variables: { id: vehicle.id },
});
console.log(result);
if (result.errors) {
notification["error"]({
message: t("vehicles.errors.deleting", {
error: JSON.stringify(result.errors),
}),
});
setLoading(false);
} else {
notification["success"]({
message: t("vehicles.successes.delete"),
});
setLoading(false);
history.push(`/manage/vehicles`);
}
};
const handleFinish = async (values) => {
setLoading(true);
@@ -40,15 +65,29 @@ function VehicleDetailFormContainer({ vehicle, refetch }) {
<>
<PageHeader
title={t("menus.header.vehicles")}
extra={
extra={[
<Popconfirm
trigger="click"
onConfirm={handleDelete}
disabled={vehicle.jobs.length !== 0}
title={t("vehicles.labels.deleteconfirm")}
>
<Button
type="danger"
loading={loading}
disabled={vehicle.jobs.length !== 0}
>
{t("general.actions.delete")}
</Button>
</Popconfirm>,
<Button
type="primary"
loading={loading}
onClick={() => form.submit()}
>
{t("general.actions.save")}
</Button>
}
</Button>,
]}
/>
<Form
onFinish={handleFinish}

View File

@@ -268,6 +268,7 @@ export const CANCEL_APPOINTMENTS_BY_JOB_ID = gql`
scheduled_in
scheduled_completion
status
lost_sale_reason
}
}
`;

View File

@@ -69,6 +69,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
vendor {
id
name
email
}
order_date
deliver_by
@@ -104,6 +105,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
vendor {
id
name
email
}
total
invoice_number

View File

@@ -113,6 +113,11 @@ export const QUERY_BODYSHOP = gql`
localmediaservernetwork
localmediatoken
enforce_conversion_csr
md_lost_sale_reasons
md_parts_scan
enforce_conversion_category
tt_enforce_hours_for_tech_console
use_paint_scale_data
employees {
user_email
id
@@ -122,6 +127,7 @@ export const QUERY_BODYSHOP = gql`
employee_number
rates
external_id
flat_rate
}
}
}
@@ -223,6 +229,10 @@ export const UPDATE_SHOP = gql`
localmediaservernetwork
localmediatoken
enforce_conversion_csr
md_lost_sale_reasons
md_parts_scan
enforce_conversion_category
tt_enforce_hours_for_tech_console
employees {
id
first_name

View File

@@ -284,6 +284,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
clm_no
v_make_desc
v_color
vehicleid
plate_no
actual_in
scheduled_completion
@@ -536,6 +537,7 @@ export const GET_JOB_BY_PK = gql`
driveable
towin
loss_of_use
lost_sale_reason
vehicle {
id
plate_no
@@ -721,6 +723,7 @@ export const GET_JOB_BY_PK = gql`
ioucreated
convertedtolbr
ah_detail_line
critical
billlines(limit: 1, order_by: { bill: { date: desc } }) {
id
quantity
@@ -1275,7 +1278,7 @@ export const SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE = gql`
export const SEARCH_FOR_JOBS = gql`
query SEARCH_FOR_JOBS($search: String!) {
search_jobs(args: { search: $search }) {
search_jobs(args: { search: $search }, limit: 25) {
id
ro_number
ownr_fn

View File

@@ -4,7 +4,15 @@ export const INSERT_NEW_NOTE = gql`
mutation INSERT_NEW_NOTE($noteInput: [notes_insert_input!]!) {
insert_notes(objects: $noteInput) {
returning {
created_at
created_by
critical
id
jobid
private
text
updated_at
audit
}
}
}
@@ -15,8 +23,8 @@ export const QUERY_NOTES_BY_JOB_PK = gql`
jobs_by_pk(id: $id) {
id
ro_number
vehicle{
jobs{
vehicle {
jobs {
id
ro_number
status

View File

@@ -94,6 +94,14 @@ export const UPDATE_OWNER = gql`
}
`;
export const DELETE_OWNER = gql`
mutation DELETE_OWNER($id: uuid!) {
delete_owners_by_pk(id: $id) {
id
}
}
`;
export const QUERY_ALL_OWNERS = gql`
query QUERY_ALL_OWNERS {
owners {

View File

@@ -19,6 +19,7 @@ export const QUERY_SCOREBOARD = gql`
v_make_desc
v_model_desc
v_model_yr
job_totals
}
}
}

View File

@@ -2,22 +2,20 @@ import { gql } from "@apollo/client";
export const GLOBAL_SEARCH_QUERY = gql`
query GLOBAL_SEARCH_QUERY($search: String) {
search_jobs(args: { search: $search }) {
search_jobs(args: { search: $search }, limit: 25) {
id
ro_number
status
clm_no
v_model_yr
v_model_desc
v_make_desc
v_color
ownr_fn
ownr_ln
ownr_co_nm
}
search_owners(args: { search: $search }) {
search_owners(args: { search: $search }, limit: 25) {
id
ownr_fn
ownr_ln
@@ -25,7 +23,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
ownr_ph1
ownr_ph2
}
search_vehicles(args: { search: $search }) {
search_vehicles(args: { search: $search }, limit: 25) {
id
v_model_yr
v_model_desc
@@ -34,7 +32,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
v_vin
plate_no
}
search_payments(args: { search: $search }) {
search_payments(args: { search: $search }, limit: 25) {
id
amount
paymentnum
@@ -45,7 +43,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
memo
transactionid
}
search_bills(args: { search: $search }) {
search_bills(args: { search: $search }, limit: 25) {
id
date
invoice_number
@@ -54,7 +52,7 @@ export const GLOBAL_SEARCH_QUERY = gql`
name
}
}
search_phonebook(args: { search: $search }) {
search_phonebook(args: { search: $search }, limit: 25) {
id
firstname
lastname

View File

@@ -59,6 +59,81 @@ export const QUERY_TIME_TICKETS_IN_RANGE = gql`
}
`;
export const QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE = gql`
query QUERY_TIME_TICKETS_TECHNICIAN_IN_RANGE(
$employeeid: uuid!
$start: date!
$end: date!
$fixedStart: date!
$fixedEnd: date!
) {
timetickets(
where: {
date: { _gte: $start, _lte: $end }
employeeid: { _eq: $employeeid }
}
order_by: { date: desc_nulls_first }
) {
actualhrs
ciecacode
clockoff
clockon
cost_center
created_at
date
id
rate
productivehrs
memo
jobid
flat_rate
job {
id
ro_number
}
employeeid
employee {
id
employee_number
first_name
last_name
}
}
fixedperiod: timetickets(
where: {
date: { _gte: $fixedStart, _lte: $fixedEnd }
employeeid: { _eq: $employeeid }
}
order_by: { date: desc_nulls_first }
) {
actualhrs
ciecacode
clockoff
clockon
cost_center
created_at
date
id
rate
productivehrs
memo
jobid
flat_rate
job {
id
ro_number
}
employeeid
employee {
id
employee_number
first_name
last_name
}
}
}
`;
export const QUERY_TIME_TICKETS_IN_RANGE_SB = gql`
query QUERY_TIME_TICKETS_IN_RANGE_SB(
$start: date!

View File

@@ -55,6 +55,14 @@ export const UPDATE_VEHICLE = gql`
}
`;
export const DELETE_VEHICLE = gql`
mutation DELETE_VEHICLE($id: uuid!) {
delete_vehicles_by_pk(id: $id) {
id
}
}
`;
export const QUERY_ALL_VEHICLES = gql`
query QUERY_ALL_VEHICLES {
vehicles {

View File

@@ -39,7 +39,7 @@ export function AllJobs({ setBreadcrumbs, setSelectedHeader }) {
...(statusFilters ? { statusList: JSON.parse(statusFilters) } : {}),
order: [
{
[sortcolumn || "created_at"]:
[sortcolumn || "ro_number"]:
sortorder && sortorder !== "false"
? sortorder === "descend"
? "desc"

View File

@@ -52,6 +52,7 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { insertAuditTrail } from "../../redux/application/application.actions";
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
import UndefinedToNull from "../../utils/undefinedtonull";
import NoteUpsertModalComponent from "../../components/note-upsert-modal/note-upsert-modal.container";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -97,7 +98,11 @@ export function JobsDetailPage({
variables: {
jobId: job.id,
job: {
...UndefinedToNull(values, ["alt_transport", "category", "referral_source"]),
...UndefinedToNull(values, [
"alt_transport",
"category",
"referral_source",
]),
parts_tax_rates: {
...job.parts_tax_rates,
...values.parts_tax_rates,
@@ -231,6 +236,7 @@ export function JobsDetailPage({
<ScheduleJobModalContainer />
<JobReconciliationModal />
<JobLineUpsertModalContainer />
<NoteUpsertModalComponent />
<Form
form={form}
name="JobDetailForm"

View File

@@ -2,10 +2,12 @@ import { Divider } from "antd";
import React from "react";
import TechClockInFormContainer from "../../components/tech-job-clock-in-form/tech-job-clock-in-form.container";
import TechClockedInList from "../../components/tech-job-clocked-in-list/tech-job-clocked-in-list.component";
import TechJobStatistics from "../../components/tech-job-statistics/tech-job-statistics.component";
export default function TechClockComponent() {
return (
<div>
<TechJobStatistics />
<TechClockInFormContainer />
<Divider />
<TechClockedInList />

View File

@@ -1,9 +1,10 @@
.tech-content-container {
overflow-y: auto;
overflow-y: visible;
padding: 1rem;
background: #fff;
}
.tech-layout-container {
height: 100vh;
position: relative;
min-height: 100vh;
}

View File

@@ -230,6 +230,7 @@
"addapptcolor": "Add Appointment Color",
"addbucket": "Add Definition",
"addpartslocation": "Add Parts Location",
"addpartsrule": "Add Parts Scan Rule",
"addspeedprint": "Add Speed Print",
"addtemplate": "Add Template",
"newlaborrate": "New Labor Rate",
@@ -282,6 +283,7 @@
},
"email": "General Shop Email",
"enforce_class": "Enforce Class on Conversion?",
"enforce_conversion_category": "Enforce Category on Conversion?",
"enforce_conversion_csr": "Enforce CSR on Conversion?",
"enforce_referral": "Enforce Referrals",
"federal_tax_id": "Federal Tax ID (GST/HST)",
@@ -329,7 +331,12 @@
"zip": "Zip/Postal Code"
},
"md_jobline_presets": "Jobline Presets",
"md_lost_sale_reasons": "Lost Sale Reasons",
"md_parts_order_comment": "Parts Orders Comments",
"md_parts_scan": {
"expression": "RegEX Expression",
"flags": "Flags"
},
"md_payment_types": "Payment Types",
"md_referral_sources": "Referral Sources",
"messaginglabel": "Messaging Preset Label",
@@ -534,7 +541,9 @@
"target_touchtime": "Target Touch Time",
"timezone": "Timezone",
"tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs",
"tt_enforce_hours_for_tech_console": "Restrict Claimable hours from Tech Console",
"use_fippa": "Use FIPPA for Names on Generated Documents?",
"use_paint_scale_data": "Use Paint Scale Data for Job Costing?",
"uselocalmediaserver": "Use Local Media Server?",
"website": "Website",
"zip_post": "Zip/Postal Code"
@@ -580,6 +589,7 @@
"notespresets": "Notes Presets",
"orderstatuses": "Order Statuses",
"partslocations": "Parts Locations",
"partsscan": "Critical Parts Scanning",
"printlater": "Print Later",
"qbo": "Use QuickBooks Online?",
"qbo_departmentid": "QBO Department ID",
@@ -1006,6 +1016,7 @@
"created_at": "Created At",
"email": "Email",
"errors": "Errors",
"excel": "Excel",
"exceptiontitle": "An error has occurred.",
"friday": "Friday",
"globalsearch": "Global Search",
@@ -1055,6 +1066,7 @@
"sunday": "Sunday",
"text": "Text",
"thursday": "Thursday",
"total": "Total",
"totals": "Totals",
"tuesday": "Tuesday",
"unknown": "Unknown",
@@ -1273,6 +1285,7 @@
"removefromproduction": "Remove from Production",
"schedule": "Schedule",
"sendcsi": "Send CSI",
"sendpartspricechange": "",
"sendtodms": "Send to DMS",
"sync": "Sync",
"uninvoice": "Uninvoice",
@@ -1296,6 +1309,7 @@
"nojobselected": "No job is selected.",
"noowner": "No owner associated.",
"novehicle": "No vehicle associated.",
"partspricechange": "",
"saving": "Error encountered while saving record.",
"scanimport": "Error importing job. {{message}}",
"totalscalc": "Error while calculating new job totals.",
@@ -1447,6 +1461,7 @@
"loss_date": "Loss Date",
"loss_desc": "Loss Description",
"loss_of_use": "Loss of Use",
"lost_sale_reason": "Lost Sale Reason",
"ma2s": "2 Stage Paint",
"ma3s": "3 Stage Pain",
"mabl": "MABL?",
@@ -1947,7 +1962,8 @@
"actions": "Actions",
"deletenote": "Delete Note",
"edit": "Edit Note",
"new": "New Note"
"new": "New Note",
"savetojobnotes": "Save to Job Notes"
},
"errors": {
"inserting": "Error inserting note. {{error}}"
@@ -1982,6 +1998,7 @@
"update": "Update Selected Records"
},
"errors": {
"deleting": "Error deleting owner. {{error}}.",
"noaccess": "The record does not exist or you do not have access to it. ",
"saving": "Error saving owner. {{error}}.",
"selectexistingornew": "Select an existing owner record or create a new one. "
@@ -2014,6 +2031,7 @@
},
"labels": {
"create_new": "Create a new owner record.",
"deleteconfirm": "Are you sure you want to delete this owner? This cannot be undone.",
"existing_owners": "Existing Owners",
"fromclaim": "Current Claim",
"fromowner": "Historical Owner Record",
@@ -2021,6 +2039,7 @@
"updateowner": "Update Owner"
},
"successes": {
"delete": "Owner deleted successfully.",
"save": "Owner saved successfully."
}
},
@@ -2210,6 +2229,7 @@
"csi_invitation": "CSI Invitation",
"csi_invitation_action": "CSI Invite",
"diagnostic_authorization": "Diagnostic Authorization",
"dms_posting_sheet": "DMS Posting Sheet",
"envelope_return_address": "#10 Envelope Return Address Label",
"estimate": "Estimate Only",
"estimate_detail": "Estimate Details",
@@ -2420,6 +2440,7 @@
"credits_not_received_date": "Credits not Received by Date",
"credits_not_received_date_vendorid": "Credits not Received by Vendor",
"csi": "CSI Responses",
"customer_list": "Customer List",
"cycle_time_analysis": "Cycle Time Analysis",
"estimates_written_converted": "Estimates Written/Converted",
"estimator_detail": "Jobs by Estimator (Detail)",
@@ -2427,6 +2448,8 @@
"export_payables": "Export Log - Payables",
"export_payments": "Export Log - Payments",
"export_receivables": "Export Log - Receivables",
"exported_gsr_by_ro": "Exported Gross Sales - Excel",
"exported_gsr_by_ro_labor": "Exported Gross Sales (Labor) - Excel",
"gsr_by_atp": "",
"gsr_by_ats": "Gross Sales by ATS",
"gsr_by_category": "Gross Sales by Category",
@@ -2519,6 +2542,7 @@
"labels": {
"atssummary": "ATS Summary",
"employeevacation": "Employee Vacations",
"ins_co_nm_filter": "Filter by Insurance Company",
"intake": "Intake Events",
"manual": "Manual Events",
"manualevent": "Add Manual Event"
@@ -2645,7 +2669,8 @@
},
"validation": {
"clockoffmustbeafterclockon": "Clock off time must be the same or after clock in time.",
"clockoffwithoutclockon": "Clock off time cannot be set without a clock in time."
"clockoffwithoutclockon": "Clock off time cannot be set without a clock in time.",
"hoursenteredmorethanavailable": "The number of hours entered is more than what is available for this cost center."
}
},
"titles": {
@@ -2779,6 +2804,7 @@
},
"vehicles": {
"errors": {
"deleting": "Error deleting vehicle. {{error}}.",
"noaccess": "The vehicle does not exist or you do not have access to it.",
"selectexistingornew": "Select an existing vehicle record or create a new one. ",
"validation": "Please ensure all fields are entered correctly.",
@@ -2814,12 +2840,14 @@
"registration": "Registration"
},
"labels": {
"deleteconfirm": "Are you sure you want to delete this vehicle? This cannot be undone.",
"fromvehicle": "Historical Vehicle Record",
"novehinfo": "No Vehicle Information",
"relatedjobs": "Related Jobs",
"updatevehicle": "Update Vehicle Information"
},
"successes": {
"delete": "Vehicle deleted successfully.",
"save": "Vehicle saved successfully."
}
},

View File

@@ -230,6 +230,7 @@
"addapptcolor": "",
"addbucket": "",
"addpartslocation": "",
"addpartsrule": "",
"addspeedprint": "",
"addtemplate": "",
"newlaborrate": "",
@@ -282,6 +283,7 @@
},
"email": "",
"enforce_class": "",
"enforce_conversion_category": "",
"enforce_conversion_csr": "",
"enforce_referral": "",
"federal_tax_id": "",
@@ -329,7 +331,12 @@
"zip": ""
},
"md_jobline_presets": "",
"md_lost_sale_reasons": "",
"md_parts_order_comment": "",
"md_parts_scan": {
"expression": "",
"flags": ""
},
"md_payment_types": "",
"md_referral_sources": "",
"messaginglabel": "",
@@ -534,7 +541,9 @@
"target_touchtime": "",
"timezone": "",
"tt_allow_post_to_invoiced": "",
"tt_enforce_hours_for_tech_console": "",
"use_fippa": "",
"use_paint_scale_data": "",
"uselocalmediaserver": "",
"website": "",
"zip_post": ""
@@ -580,6 +589,7 @@
"notespresets": "",
"orderstatuses": "",
"partslocations": "",
"partsscan": "",
"printlater": "",
"qbo": "",
"qbo_departmentid": "",
@@ -1006,6 +1016,7 @@
"created_at": "",
"email": "",
"errors": "",
"excel": "",
"exceptiontitle": "",
"friday": "",
"globalsearch": "",
@@ -1055,6 +1066,7 @@
"sunday": "",
"text": "",
"thursday": "",
"total": "",
"totals": "",
"tuesday": "",
"unknown": "Desconocido",
@@ -1273,6 +1285,7 @@
"removefromproduction": "",
"schedule": "Programar",
"sendcsi": "",
"sendpartspricechange": "",
"sendtodms": "",
"sync": "",
"uninvoice": "",
@@ -1296,6 +1309,7 @@
"nojobselected": "No hay trabajo seleccionado.",
"noowner": "Ningún propietario asociado.",
"novehicle": "No hay vehículo asociado.",
"partspricechange": "",
"saving": "Se encontró un error al guardar el registro.",
"scanimport": "",
"totalscalc": "",
@@ -1447,6 +1461,7 @@
"loss_date": "Fecha de pérdida",
"loss_desc": "",
"loss_of_use": "",
"lost_sale_reason": "",
"ma2s": "",
"ma3s": "",
"mabl": "",
@@ -1947,7 +1962,8 @@
"actions": "Comportamiento",
"deletenote": "Borrar nota",
"edit": "Editar nota",
"new": "Nueva nota"
"new": "Nueva nota",
"savetojobnotes": ""
},
"errors": {
"inserting": ""
@@ -1982,6 +1998,7 @@
"update": ""
},
"errors": {
"deleting": "",
"noaccess": "El registro no existe o no tiene acceso a él.",
"saving": "",
"selectexistingornew": ""
@@ -2014,6 +2031,7 @@
},
"labels": {
"create_new": "Crea un nuevo registro de propietario.",
"deleteconfirm": "",
"existing_owners": "Propietarios existentes",
"fromclaim": "",
"fromowner": "",
@@ -2021,6 +2039,7 @@
"updateowner": ""
},
"successes": {
"delete": "",
"save": "Propietario guardado con éxito."
}
},
@@ -2210,6 +2229,7 @@
"csi_invitation": "",
"csi_invitation_action": "",
"diagnostic_authorization": "",
"dms_posting_sheet": "",
"envelope_return_address": "",
"estimate": "",
"estimate_detail": "",
@@ -2420,6 +2440,7 @@
"credits_not_received_date": "",
"credits_not_received_date_vendorid": "",
"csi": "",
"customer_list": "",
"cycle_time_analysis": "",
"estimates_written_converted": "",
"estimator_detail": "",
@@ -2427,6 +2448,8 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"exported_gsr_by_ro": "",
"exported_gsr_by_ro_labor": "",
"gsr_by_atp": "",
"gsr_by_ats": "",
"gsr_by_category": "",
@@ -2519,6 +2542,7 @@
"labels": {
"atssummary": "",
"employeevacation": "",
"ins_co_nm_filter": "",
"intake": "",
"manual": "",
"manualevent": ""
@@ -2645,7 +2669,8 @@
},
"validation": {
"clockoffmustbeafterclockon": "",
"clockoffwithoutclockon": ""
"clockoffwithoutclockon": "",
"hoursenteredmorethanavailable": ""
}
},
"titles": {
@@ -2779,6 +2804,7 @@
},
"vehicles": {
"errors": {
"deleting": "",
"noaccess": "El vehículo no existe o usted no tiene acceso a él.",
"selectexistingornew": "",
"validation": "Asegúrese de que todos los campos se ingresen correctamente.",
@@ -2814,12 +2840,14 @@
"registration": ""
},
"labels": {
"deleteconfirm": "",
"fromvehicle": "",
"novehinfo": "",
"relatedjobs": "",
"updatevehicle": ""
},
"successes": {
"delete": "",
"save": "Vehículo guardado con éxito."
}
},

View File

@@ -230,6 +230,7 @@
"addapptcolor": "",
"addbucket": "",
"addpartslocation": "",
"addpartsrule": "",
"addspeedprint": "",
"addtemplate": "",
"newlaborrate": "",
@@ -282,6 +283,7 @@
},
"email": "",
"enforce_class": "",
"enforce_conversion_category": "",
"enforce_conversion_csr": "",
"enforce_referral": "",
"federal_tax_id": "",
@@ -329,7 +331,12 @@
"zip": ""
},
"md_jobline_presets": "",
"md_lost_sale_reasons": "",
"md_parts_order_comment": "",
"md_parts_scan": {
"expression": "",
"flags": ""
},
"md_payment_types": "",
"md_referral_sources": "",
"messaginglabel": "",
@@ -534,7 +541,9 @@
"target_touchtime": "",
"timezone": "",
"tt_allow_post_to_invoiced": "",
"tt_enforce_hours_for_tech_console": "",
"use_fippa": "",
"use_paint_scale_data": "",
"uselocalmediaserver": "",
"website": "",
"zip_post": ""
@@ -580,6 +589,7 @@
"notespresets": "",
"orderstatuses": "",
"partslocations": "",
"partsscan": "",
"printlater": "",
"qbo": "",
"qbo_departmentid": "",
@@ -1006,6 +1016,7 @@
"created_at": "",
"email": "",
"errors": "",
"excel": "",
"exceptiontitle": "",
"friday": "",
"globalsearch": "",
@@ -1055,6 +1066,7 @@
"sunday": "",
"text": "",
"thursday": "",
"total": "",
"totals": "",
"tuesday": "",
"unknown": "Inconnu",
@@ -1273,6 +1285,7 @@
"removefromproduction": "",
"schedule": "Programme",
"sendcsi": "",
"sendpartspricechange": "",
"sendtodms": "",
"sync": "",
"uninvoice": "",
@@ -1296,6 +1309,7 @@
"nojobselected": "Aucun travail n'est sélectionné.",
"noowner": "Aucun propriétaire associé.",
"novehicle": "Aucun véhicule associé.",
"partspricechange": "",
"saving": "Erreur rencontrée lors de la sauvegarde de l'enregistrement.",
"scanimport": "",
"totalscalc": "",
@@ -1447,6 +1461,7 @@
"loss_date": "Date de perte",
"loss_desc": "",
"loss_of_use": "",
"lost_sale_reason": "",
"ma2s": "",
"ma3s": "",
"mabl": "",
@@ -1947,7 +1962,8 @@
"actions": "actes",
"deletenote": "Supprimer la note",
"edit": "Note éditée",
"new": "Nouvelle note"
"new": "Nouvelle note",
"savetojobnotes": ""
},
"errors": {
"inserting": ""
@@ -1982,6 +1998,7 @@
"update": ""
},
"errors": {
"deleting": "",
"noaccess": "L'enregistrement n'existe pas ou vous n'y avez pas accès.",
"saving": "",
"selectexistingornew": ""
@@ -2014,6 +2031,7 @@
},
"labels": {
"create_new": "Créez un nouvel enregistrement de propriétaire.",
"deleteconfirm": "",
"existing_owners": "Propriétaires existants",
"fromclaim": "",
"fromowner": "",
@@ -2021,6 +2039,7 @@
"updateowner": ""
},
"successes": {
"delete": "",
"save": "Le propriétaire a bien enregistré."
}
},
@@ -2210,6 +2229,7 @@
"csi_invitation": "",
"csi_invitation_action": "",
"diagnostic_authorization": "",
"dms_posting_sheet": "",
"envelope_return_address": "",
"estimate": "",
"estimate_detail": "",
@@ -2420,6 +2440,7 @@
"credits_not_received_date": "",
"credits_not_received_date_vendorid": "",
"csi": "",
"customer_list": "",
"cycle_time_analysis": "",
"estimates_written_converted": "",
"estimator_detail": "",
@@ -2427,6 +2448,8 @@
"export_payables": "",
"export_payments": "",
"export_receivables": "",
"exported_gsr_by_ro": "",
"exported_gsr_by_ro_labor": "",
"gsr_by_atp": "",
"gsr_by_ats": "",
"gsr_by_category": "",
@@ -2519,6 +2542,7 @@
"labels": {
"atssummary": "",
"employeevacation": "",
"ins_co_nm_filter": "",
"intake": "",
"manual": "",
"manualevent": ""
@@ -2645,7 +2669,8 @@
},
"validation": {
"clockoffmustbeafterclockon": "",
"clockoffwithoutclockon": ""
"clockoffwithoutclockon": "",
"hoursenteredmorethanavailable": ""
}
},
"titles": {
@@ -2779,6 +2804,7 @@
},
"vehicles": {
"errors": {
"deleting": "",
"noaccess": "Le véhicule n'existe pas ou vous n'y avez pas accès.",
"selectexistingornew": "",
"validation": "Veuillez vous assurer que tous les champs sont correctement entrés.",
@@ -2814,12 +2840,14 @@
"registration": ""
},
"labels": {
"deleteconfirm": "",
"fromvehicle": "",
"novehinfo": "",
"relatedjobs": "",
"updatevehicle": ""
},
"successes": {
"delete": "",
"save": "Le véhicule a été enregistré avec succès."
}
},

View File

@@ -1,6 +1,5 @@
import { gql } from "@apollo/client";
import { notification } from "antd";
import axios from "axios";
import jsreport from "@jsreport/browser-client";
import _ from "lodash";
import moment from "moment";
@@ -9,7 +8,8 @@ import { setEmailOptions } from "../redux/email/email.actions";
import { store } from "../redux/store";
import client from "../utils/GraphQLClient";
import { TemplateList } from "./TemplateConstants";
import cleanAxios from "./CleanAxios";
import axios from "axios";
const server = process.env.REACT_APP_REPORTS_SERVER_URL;
jsreport.serverUrl = server;
@@ -26,10 +26,14 @@ export default async function RenderTemplate(
if (window.jsr3) {
jsreport.serverUrl = "https://reports3.test.imex.online/";
}
const jsrAuth = (await axios.post("/utils/jsr")).data;
jsreport.headers["Authorization"] = jsrAuth;
//Query assets that match the template name. Must be in format <<templateName>>.query
let { contextData, useShopSpecificTemplate } = await fetchContextData(
templateObject
templateObject,
jsrAuth
);
const { ignoreCustomMargins } = Templates[templateObject.name];
@@ -137,11 +141,15 @@ export async function RenderTemplates(
//Query assets that match the template name. Must be in format <<templateName>>.query
let unsortedTemplatesAndData = [];
let proms = [];
const jsrAuth = (await axios.post("/utils/jsr")).data;
jsreport.headers["Authorization"] = jsrAuth;
templateObjects.forEach((template) => {
proms.push(
(async () => {
let { contextData, useShopSpecificTemplate } = await fetchContextData(
template
template,
jsrAuth
);
unsortedTemplatesAndData.push({
templateObject: template,
@@ -298,19 +306,22 @@ export const GenerateDocuments = async (templates) => {
await RenderTemplates(templates, bodyshop);
};
const fetchContextData = async (templateObject) => {
const fetchContextData = async (templateObject, jsrAuth) => {
const bodyshop = store.getState().user.bodyshop;
jsreport.headers["Authorization"] =
jsreport.headers["FirebaseAuthorization"] =
"Bearer " + (await auth.currentUser.getIdToken());
const folders = await axios.get(`${server}/odata/folders`);
const folders = await cleanAxios.get(`${server}/odata/folders`, {
headers: { Authorization: jsrAuth },
});
const shopSpecificFolder = folders.data.value.find(
(f) => f.name === bodyshop.imexshopid
);
const jsReportQueries = await axios.get(
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`
const jsReportQueries = await cleanAxios.get(
`${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`,
{ headers: { Authorization: jsrAuth } }
);
let templateQueryToExecute;

View File

@@ -20,7 +20,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "authorization",
},
fippa_authorization: {
title: i18n.t("printcenter.jobs.fippa_authorization"),
description: "CASL Authorization",
@@ -101,7 +100,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "ro",
},
job_notes: {
title: i18n.t("printcenter.jobs.job_notes"),
description: "All Jobs Notes",
@@ -330,7 +328,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "post",
},
vehicle_delivery_check: {
title: i18n.t("printcenter.jobs.vehicle_delivery_check"),
description: "All Jobs Notes",
@@ -339,7 +336,6 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "post",
},
guarantee: {
title: i18n.t("printcenter.jobs.guarantee"),
description: "All Jobs Notes",
@@ -496,6 +492,14 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "financial",
},
dms_posting_sheet: {
title: i18n.t("printcenter.jobs.dms_posting_sheet"),
description: "DMS Posting Sheet",
subject: i18n.t("printcenter.jobs.dms_posting_sheet"),
key: "dms_posting_sheet",
disabled: false,
group: "financial",
},
}
: {}),
...(!type || type === "job_special"
@@ -622,7 +626,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_detail_closed_ins_co: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_ins_co"
@@ -640,7 +643,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_summary_closed: {
title: i18n.t("reportcenter.templates.hours_sold_summary_closed"),
description: "",
@@ -654,7 +656,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_summary_closed_ins_co: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_closed_ins_co"
@@ -672,7 +673,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_detail_open: {
title: i18n.t("reportcenter.templates.hours_sold_detail_open"),
description: "",
@@ -686,7 +686,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_detail_open_ins_co: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_open_ins_co"
@@ -704,7 +703,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_summary_open: {
title: i18n.t("reportcenter.templates.hours_sold_summary_open"),
description: "",
@@ -718,7 +716,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_summary_open_ins_co: {
title: i18n.t(
"reportcenter.templates.hours_sold_summary_open_ins_co"
@@ -736,7 +733,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
hours_sold_detail_closed_csr: {
title: i18n.t(
"reportcenter.templates.hours_sold_detail_closed_csr"
@@ -1090,7 +1086,6 @@ export const TemplateList = (type, context) => {
},
group: "customers",
},
schedule: {
title: i18n.t("reportcenter.templates.schedule"),
subject: i18n.t("reportcenter.templates.schedule"),
@@ -1102,7 +1097,6 @@ export const TemplateList = (type, context) => {
},
group: "customers",
},
timetickets: {
title: i18n.t("reportcenter.templates.timetickets"),
subject: i18n.t("reportcenter.templates.timetickets"),
@@ -1126,7 +1120,6 @@ export const TemplateList = (type, context) => {
title: i18n.t("reportcenter.templates.attendance_detail"),
subject: i18n.t("reportcenter.templates.attendance_detail"),
key: "attendance_detail",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.timetickets"),
@@ -1138,7 +1131,6 @@ export const TemplateList = (type, context) => {
title: i18n.t("reportcenter.templates.attendance_summary"),
subject: i18n.t("reportcenter.templates.attendance_summary"),
key: "attendance_summary",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.timetickets"),
@@ -1158,7 +1150,6 @@ export const TemplateList = (type, context) => {
},
group: "payroll",
},
timetickets_summary: {
title: i18n.t("reportcenter.templates.timetickets_summary"),
subject: i18n.t("reportcenter.templates.timetickets_summary"),
@@ -1171,7 +1162,6 @@ export const TemplateList = (type, context) => {
},
group: "payroll",
},
estimator_detail: {
title: i18n.t("reportcenter.templates.estimator_detail"),
description: "",
@@ -1224,7 +1214,6 @@ export const TemplateList = (type, context) => {
},
group: "purchases",
},
void_ros: {
title: i18n.t("reportcenter.templates.void_ros"),
description: "",
@@ -1329,7 +1318,6 @@ export const TemplateList = (type, context) => {
},
group: "sales",
},
gsr_by_estimator: {
title: i18n.t("reportcenter.templates.gsr_by_estimator"),
description: "",
@@ -1814,7 +1802,9 @@ export const TemplateList = (type, context) => {
group: "jobs",
},
purchase_return_ratio_grouped_by_vendor_detail: {
title: i18n.t("reportcenter.templates.purchase_return_ratio_grouped_by_vendor_detail"),
title: i18n.t(
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_detail"
),
subject: i18n.t(
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_detail"
),
@@ -1828,7 +1818,9 @@ export const TemplateList = (type, context) => {
group: "purchases",
},
purchase_return_ratio_grouped_by_vendor_summary: {
title: i18n.t("reportcenter.templates.purchase_return_ratio_grouped_by_vendor_summary"),
title: i18n.t(
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_summary"
),
subject: i18n.t(
"reportcenter.templates.purchase_return_ratio_grouped_by_vendor_summary"
),
@@ -1843,9 +1835,7 @@ export const TemplateList = (type, context) => {
},
production_over_time: {
title: i18n.t("reportcenter.templates.production_over_time"),
subject: i18n.t(
"reportcenter.templates.production_over_time"
),
subject: i18n.t("reportcenter.templates.production_over_time"),
key: "production_over_time",
//idtype: "vendor",
disabled: false,
@@ -1855,6 +1845,44 @@ export const TemplateList = (type, context) => {
},
group: "jobs",
},
customer_list: {
title: i18n.t("reportcenter.templates.customer_list"),
subject: i18n.t("reportcenter.templates.customer_list"),
key: "customer_list",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced"),
},
group: "customers",
},
exported_gsr_by_ro: {
title: i18n.t("reportcenter.templates.exported_gsr_by_ro"),
subject: i18n.t("reportcenter.templates.exported_gsr_by_ro"),
key: "exported_gsr_by_ro",
//idtype: "vendor",
reporttype: "excel",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_exported"),
},
group: "sales",
},
exported_gsr_by_ro_labor: {
title: i18n.t("reportcenter.templates.exported_gsr_by_ro_labor"),
subject: i18n.t("reportcenter.templates.exported_gsr_by_ro_labor"),
key: "exported_gsr_by_ro_labor",
//idtype: "vendor",
reporttype: "excel",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_exported"),
},
group: "sales",
},
}
: {}),
...(!type || type === "courtesycarcontract"
@@ -1949,7 +1977,6 @@ export const TemplateList = (type, context) => {
},
}
: {}),
...(!type || type === "production"
? {
production_by_last_name: {

View File

@@ -0,0 +1,12 @@
import axios from "axios";
import { notification } from "antd";
async function CriticalPartsScan(jobid) {
try {
await axios.post("/job/partsscan", { jobid });
} catch (error) {
notification.open({ type: "error", message: JSON.stringify(error) });
}
}
export default CriticalPartsScan;

View File

@@ -3924,14 +3924,6 @@ anymatch@^3.0.3, anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
aphrodite@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/aphrodite/-/aphrodite-0.5.0.tgz#a4b9a8902662395d2702e70ac7a2b4ca66f25703"
integrity sha512-bPcR/68OdLg+16XDYAaKnlGJufxrbOw86ucElS7qHm7Y6ZNvMT/D/VDEEfodjFgfyRyoTeKmSaikULG92kSYvQ==
dependencies:
asap "^2.0.3"
inline-style-prefixer "^2.0.0"
apollo-link-logger@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/apollo-link-logger/-/apollo-link-logger-2.0.1.tgz#63d86b4fe550c147c5a52108710075675694905c"
@@ -4073,7 +4065,7 @@ array.prototype.tosorted@^1.1.1:
es-shim-unscopables "^1.0.0"
get-intrinsic "^1.1.3"
asap@^2.0.3, asap@~2.0.6:
asap@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
@@ -4484,11 +4476,6 @@ boolbase@^1.0.0, boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
bowser@^1.0.0:
version "1.9.4"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a"
integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -6504,7 +6491,7 @@ executable@^4.1.1:
dependencies:
pify "^2.2.0"
exenv@^1.2.2:
exenv@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d"
integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==
@@ -7404,11 +7391,6 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
hyphenate-style-name@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
i18next-browser-languagedetector@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz#ead34592edc96c6c3a618a51cb57ad027c5b5d87"
@@ -7543,14 +7525,6 @@ ini@^1.3.5:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
inline-style-prefixer@^2.0.0:
version "2.0.5"
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-2.0.5.tgz#c153c7e88fd84fef5c602e95a8168b2770671fe7"
integrity sha512-rlgoApaAAv66JeDFqGYaXwglnlxjyIr7RMaFZqJw4opT0WKnZVXhXMC9SAfuMpN7NELPaImUhJmOO3hAamSlfw==
dependencies:
bowser "^1.0.0"
hyphenate-style-name "^1.0.1"
internal-slot@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
@@ -11157,13 +11131,10 @@ react-error-overlay@6.0.9, react-error-overlay@^6.0.11:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
react-grid-gallery@^0.5.5:
version "0.5.5"
resolved "https://registry.yarnpkg.com/react-grid-gallery/-/react-grid-gallery-0.5.5.tgz#1b3f3c23a190834e587ab613c96d53ec3af4f0a2"
integrity sha512-DkKg2/Am+VZPDG39fazelTcsZSQrfM/YllnIcWToyUEfOZcrzHxUoqCziCkuTPmCuMbHnrjidBFuDbAFgvSnvQ==
dependencies:
prop-types "^15.5.8"
react-images "^0.5.16"
react-grid-gallery@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/react-grid-gallery/-/react-grid-gallery-1.0.0.tgz#31604b9488dfa75a899aa39bc8884138f0409a12"
integrity sha512-S1gr6WXBlPFVrE0x2BHuu7jhyaB61mabcMQyx+8KCgAyKzGza0WF67AfAnS9Q00aurFEq4IP8eqb2Bk5F4RAmQ==
react-grid-layout@^1.3.4:
version "1.3.4"
@@ -11189,15 +11160,13 @@ react-icons@^4.7.1:
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.7.1.tgz#0f4b25a5694e6972677cb189d2a72eabea7a8345"
integrity sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==
react-images@^0.5.16:
version "0.5.19"
resolved "https://registry.yarnpkg.com/react-images/-/react-images-0.5.19.tgz#9339570029e065f9f28a19f03fdb5d9d5aa109d3"
integrity sha512-B3d4W1uFJj+m17K8S65iAyEJShKGBjPk7n7N1YsPiAydEm8mIq9a6CoeQFMY1d7N2QMs6FBCjT9vELyc5jP5JA==
react-image-lightbox@^5.1.4:
version "5.1.4"
resolved "https://registry.yarnpkg.com/react-image-lightbox/-/react-image-lightbox-5.1.4.tgz#5b847dcb79e9efdf9d7cd5621a92e0f156d2cf30"
integrity sha512-kTiAODz091bgT7SlWNHab0LSMZAPJtlNWDGKv7pLlLY1krmf7FuG1zxE0wyPpeA8gPdwfr3cu6sPwZRqWsc3Eg==
dependencies:
aphrodite "^0.5.0"
prop-types "^15.6.0"
react-scrolllock "^2.0.1"
react-transition-group "2"
prop-types "^15.7.2"
react-modal "^3.11.1"
react-is@^16.10.2, react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
version "16.13.1"
@@ -11214,11 +11183,21 @@ react-is@^18.0.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-lifecycles-compat@^3.0.4:
react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
react-modal@^3.11.1:
version "3.16.1"
resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.1.tgz#34018528fc206561b1a5467fc3beeaddafb39b2b"
integrity sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==
dependencies:
exenv "^1.2.0"
prop-types "^15.7.2"
react-lifecycles-compat "^3.0.0"
warning "^4.0.3"
react-number-format@^5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.1.3.tgz#5534f5141cea29e0fe889f76c73dfad11ddf2e17"
@@ -11240,11 +11219,6 @@ react-overlays@^5.2.1:
uncontrollable "^7.2.1"
warning "^4.0.3"
react-prop-toggle@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/react-prop-toggle/-/react-prop-toggle-1.0.2.tgz#8b0b7e74653606b1427cfcf6c4eaa9198330568e"
integrity sha512-JmerjAXs7qJ959+d0Ygt7Cb2+4fG+n3I2VXO6JO0AcAY1vkRN/JpZKAN67CMXY889xEJcfylmMPhzvf6nWO68Q==
react-redux@^7.2.0:
version "7.2.8"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.8.tgz#a894068315e65de5b1b68899f9c6ee0923dd28de"
@@ -11373,14 +11347,6 @@ react-scripts@^5.0.1:
optionalDependencies:
fsevents "^2.3.2"
react-scrolllock@^2.0.1:
version "2.0.7"
resolved "https://registry.yarnpkg.com/react-scrolllock/-/react-scrolllock-2.0.7.tgz#3b879e1fe308fc900ab76e226e9be594c41226fd"
integrity sha512-Gzpu8+ulxdYcybAgJOFTXc70xs7SBZDQbZNpKzchZUgLCJKjz6lrgESx6LHHZgfELx1xYL4yHu3kYQGQPFas/g==
dependencies:
exenv "^1.2.2"
react-prop-toggle "^1.0.2"
react-smooth@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-2.0.1.tgz#74c7309916d6ccca182c4b30c8992f179e6c5a05"
@@ -11405,7 +11371,7 @@ react-sublime-video@^0.2.5:
prop-types "^15.5.10"
rc-tween-one "^1.2.5"
react-transition-group@2, react-transition-group@2.9.0:
react-transition-group@2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==

View File

@@ -673,6 +673,36 @@
_eq: true
- exported:
_eq: false
event_triggers:
- name: os_bills
definition:
delete:
columns: '*'
enable_manual: false
insert:
columns: '*'
update:
columns:
- jobid
- invoice_number
- due_date
- vendorid
- id
- date
retry_conf:
interval_sec: 10
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
headers:
- name: event-secret
value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2
- table:
name: bodyshops
schema: public
@@ -761,6 +791,13 @@
table:
name: email_audit_trail
schema: public
- name: employee_teams
using:
foreign_key_constraint_on:
column: bodyshopid
table:
name: employee_teams
schema: public
- name: employees
using:
foreign_key_constraint_on:
@@ -796,6 +833,13 @@
table:
name: owners
schema: public
- name: payment_responses
using:
foreign_key_constraint_on:
column: bodyshopid
table:
name: payment_response
schema: public
- name: phonebooks
using:
foreign_key_constraint_on:
@@ -817,6 +861,13 @@
table:
name: transitions
schema: public
- name: tt_approval_queues
using:
foreign_key_constraint_on:
column: bodyshopid
table:
name: tt_approval_queue
schema: public
- name: vehicles
using:
foreign_key_constraint_on:
@@ -853,6 +904,7 @@
- deliverchecklist
- email
- enforce_class
- enforce_conversion_category
- enforce_conversion_csr
- enforce_referral
- entegral_configuration
@@ -883,11 +935,13 @@
- md_ins_cos
- md_jobline_presets
- md_labor_rates
- md_lost_sale_reasons
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_parts_order_comment
- md_parts_scan
- md_payment_types
- md_rbac
- md_referral_sources
@@ -918,8 +972,10 @@
- textid
- timezone
- tt_allow_post_to_invoiced
- tt_enforce_hours_for_tech_console
- updated_at
- use_fippa
- use_paint_scale_data
- uselocalmediaserver
- website
- workingdays
@@ -950,6 +1006,7 @@
- deliverchecklist
- email
- enforce_class
- enforce_conversion_category
- enforce_conversion_csr
- enforce_referral
- federal_tax_id
@@ -975,11 +1032,13 @@
- md_ins_cos
- md_jobline_presets
- md_labor_rates
- md_lost_sale_reasons
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_parts_order_comment
- md_parts_scan
- md_payment_types
- md_rbac
- md_referral_sources
@@ -1003,8 +1062,10 @@
- target_touchtime
- timezone
- tt_allow_post_to_invoiced
- tt_enforce_hours_for_tech_console
- updated_at
- use_fippa
- use_paint_scale_data
- uselocalmediaserver
- website
- workingdays
@@ -1932,6 +1993,165 @@
- active:
_eq: true
check: null
- table:
name: employee_team_members
schema: public
object_relationships:
- name: employee
using:
foreign_key_constraint_on: employeeid
- name: employee_team
using:
foreign_key_constraint_on: teamid
insert_permissions:
- role: user
permission:
check:
employee_team:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- labor_rates
- percentage
- created_at
- updated_at
- employeeid
- id
- teamid
select_permissions:
- role: user
permission:
columns:
- labor_rates
- percentage
- created_at
- updated_at
- employeeid
- id
- teamid
filter:
employee_team:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
update_permissions:
- role: user
permission:
columns:
- labor_rates
- percentage
- created_at
- updated_at
- employeeid
- id
- teamid
filter:
employee_team:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
check: null
delete_permissions:
- role: user
permission:
backend_only: false
filter:
employee_team:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
- table:
name: employee_teams
schema: public
object_relationships:
- name: bodyshop
using:
foreign_key_constraint_on: bodyshopid
array_relationships:
- name: employee_team_members
using:
foreign_key_constraint_on:
column: teamid
table:
name: employee_team_members
schema: public
insert_permissions:
- role: user
permission:
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- active
- name
- created_at
- updated_at
- bodyshopid
- id
select_permissions:
- role: user
permission:
columns:
- active
- name
- created_at
- updated_at
- bodyshopid
- id
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
update_permissions:
- role: user
permission:
columns:
- active
- bodyshopid
- name
- updated_at
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
check: null
- table:
name: employee_vacation
schema: public
@@ -2032,6 +2252,13 @@
table:
name: allocations
schema: public
- name: employee_team_members
using:
foreign_key_constraint_on:
column: employeeid
table:
name: employee_team_members
schema: public
- name: employee_vacations
using:
foreign_key_constraint_on:
@@ -2074,6 +2301,13 @@
table:
name: timetickets
schema: public
- name: tt_approval_queues
using:
foreign_key_constraint_on:
column: employeeid
table:
name: tt_approval_queue
schema: public
insert_permissions:
- role: user
permission:
@@ -2205,30 +2439,32 @@
- active:
_eq: true
columns:
- id
- created_at
- updated_at
- jobid
- billid
- bodyshopid
- created_at
- id
- jobid
- message
- metadata
- paymentid
- successful
- message
- bodyshopid
- updated_at
- useremail
select_permissions:
- role: user
permission:
columns:
- successful
- message
- useremail
- created_at
- updated_at
- billid
- bodyshopid
- created_at
- id
- jobid
- message
- metadata
- paymentid
- successful
- updated_at
- useremail
filter:
bodyshop:
associations:
@@ -2480,6 +2716,7 @@
_eq: true
columns:
- act_price
- act_price_before_ppc
- ah_detail_line
- alt_co_id
- alt_overrd
@@ -2546,6 +2783,7 @@
permission:
columns:
- act_price
- act_price_before_ppc
- ah_detail_line
- alt_co_id
- alt_overrd
@@ -2560,6 +2798,7 @@
- convertedtolbr
- convertedtolbr_data
- created_at
- critical
- db_hrs
- db_price
- db_ref
@@ -2623,6 +2862,7 @@
permission:
columns:
- act_price
- act_price_before_ppc
- ah_detail_line
- alt_co_id
- alt_overrd
@@ -2637,6 +2877,7 @@
- convertedtolbr
- convertedtolbr_data
- created_at
- critical
- db_hrs
- db_price
- db_ref
@@ -2901,6 +3142,13 @@
table:
name: parts_orders
schema: public
- name: payment_responses
using:
foreign_key_constraint_on:
column: jobid
table:
name: payment_response
schema: public
- name: payments
using:
foreign_key_constraint_on:
@@ -2943,6 +3191,13 @@
table:
name: transitions
schema: public
- name: tt_approval_queues
using:
foreign_key_constraint_on:
column: jobid
table:
name: tt_approval_queue
schema: public
insert_permissions:
- role: user
permission:
@@ -3380,6 +3635,7 @@
- loss_desc
- loss_of_use
- loss_type
- lost_sale_reason
- materials
- other_amount_payable
- owner_owing
@@ -3654,6 +3910,7 @@
- loss_desc
- loss_of_use
- loss_type
- lost_sale_reason
- materials
- other_amount_payable
- owner_owing
@@ -3829,6 +4086,44 @@
template_engine: Kriti
url: '{{$base_url}}/record-handler/arms'
version: 2
- name: os_jobs
definition:
delete:
columns: '*'
enable_manual: false
insert:
columns: '*'
update:
columns:
- v_color
- ownerid
- ownr_fn
- v_model_desc
- ownr_ln
- id
- v_make_desc
- ownr_st
- clm_no
- voided
- status
- ownr_co_nm
- v_model_yr
- v_vin
- converted
retry_conf:
interval_sec: 10
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
headers:
- name: event-secret
value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2
- table:
name: masterdata
schema: public
@@ -4253,6 +4548,35 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
event_triggers:
- name: os_owners
definition:
delete:
columns: '*'
enable_manual: false
insert:
columns: '*'
update:
columns:
- shopid
- ownr_fn
- id
- ownr_co_nm
- ownr_ln
retry_conf:
interval_sec: 10
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
headers:
- name: event-secret
value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2
- table:
name: parts_order_lines
schema: public
@@ -4556,6 +4880,7 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
allow_aggregations: true
- table:
name: payments
schema: public
@@ -4571,6 +4896,13 @@
table:
name: exportlog
schema: public
- name: payment_responses
using:
foreign_key_constraint_on:
column: paymentid
table:
name: payment_response
schema: public
insert_permissions:
- role: user
permission:
@@ -4668,6 +5000,39 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
event_triggers:
- name: os_payments
definition:
delete:
columns: '*'
enable_manual: false
insert:
columns: '*'
update:
columns:
- paymentnum
- type
- amount
- date
- transactionid
- memo
- payer
- id
- jobid
retry_conf:
interval_sec: 10
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
headers:
- name: event-secret
value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2
- table:
name: phonebook
schema: public
@@ -4998,6 +5363,12 @@
- name: job
using:
foreign_key_constraint_on: jobid
- name: tt_approval_queue
using:
foreign_key_constraint_on: ttapprovalqueueid
- name: user
using:
foreign_key_constraint_on: commited_by
insert_permissions:
- role: user
permission:
@@ -5016,6 +5387,8 @@
- ciecacode
- clockoff
- clockon
- commited_by
- committed_at
- cost_center
- created_at
- date
@@ -5026,6 +5399,7 @@
- memo
- productivehrs
- rate
- ttapprovalqueueid
- updated_at
select_permissions:
- role: user
@@ -5036,6 +5410,8 @@
- ciecacode
- clockoff
- clockon
- commited_by
- committed_at
- cost_center
- created_at
- date
@@ -5046,6 +5422,7 @@
- memo
- productivehrs
- rate
- ttapprovalqueueid
- updated_at
filter:
bodyshop:
@@ -5065,6 +5442,8 @@
- ciecacode
- clockoff
- clockon
- commited_by
- committed_at
- cost_center
- created_at
- date
@@ -5075,6 +5454,7 @@
- memo
- productivehrs
- rate
- ttapprovalqueueid
- updated_at
filter:
bodyshop:
@@ -5185,6 +5565,119 @@
authid:
_eq: X-Hasura-User-Id
check: {}
- table:
name: tt_approval_queue
schema: public
object_relationships:
- name: bodyshop
using:
foreign_key_constraint_on: bodyshopid
- name: employee
using:
foreign_key_constraint_on: employeeid
- name: job
using:
foreign_key_constraint_on: jobid
- name: user
using:
foreign_key_constraint_on: approved_by
array_relationships:
- name: timetickets
using:
foreign_key_constraint_on:
column: ttapprovalqueueid
table:
name: timetickets
schema: public
insert_permissions:
- role: user
permission:
check:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
columns:
- actualhrs
- approved_at
- approved_by
- bodyshopid
- ciecacode
- cost_center
- created_at
- date
- employeeid
- flat_rate
- id
- jobid
- memo
- productivehrs
- rate
- updated_at
select_permissions:
- role: user
permission:
columns:
- actualhrs
- approved_at
- approved_by
- bodyshopid
- ciecacode
- cost_center
- created_at
- date
- employeeid
- flat_rate
- id
- jobid
- memo
- productivehrs
- rate
- updated_at
filter:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
allow_aggregations: true
update_permissions:
- role: user
permission:
columns:
- actualhrs
- approved_at
- approved_by
- bodyshopid
- ciecacode
- cost_center
- created_at
- date
- employeeid
- flat_rate
- id
- jobid
- memo
- productivehrs
- rate
- updated_at
filter:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
check: null
- table:
name: users
schema: public
@@ -5255,6 +5748,20 @@
table:
name: parts_orders
schema: public
- name: timetickets
using:
foreign_key_constraint_on:
column: commited_by
table:
name: timetickets
schema: public
- name: tt_approval_queues
using:
foreign_key_constraint_on:
column: approved_by
table:
name: tt_approval_queue
schema: public
insert_permissions:
- role: user
permission:
@@ -5440,6 +5947,37 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
event_triggers:
- name: os_vehicles
definition:
delete:
columns: '*'
enable_manual: false
insert:
columns: '*'
update:
columns:
- v_model_yr
- plate_no
- id
- v_vin
- v_model_desc
- plate_st
- shopid
retry_conf:
interval_sec: 10
num_retries: 3
timeout_sec: 60
webhook_from_env: HASURA_API_URL
headers:
- name: event-secret
value_from_env: EVENT_SECRET
request_transform:
method: POST
query_params: {}
template_engine: Kriti
url: '{{$base_url}}/opensearch'
version: 2
- table:
name: vendors
schema: public

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "md_lost_sale_reasons" jsonb
-- not null default jsonb_build_array();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "md_lost_sale_reasons" jsonb
not null default jsonb_build_array();

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."jobs" add column "lost_sale_reason" text
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "lost_sale_reason" text
null;

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "md_parts_scan" jsonb
-- not null default jsonb_build_array();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "md_parts_scan" jsonb
not null default jsonb_build_array();

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."joblines" add column "critical" boolean
-- not null default 'false';

View File

@@ -0,0 +1,2 @@
alter table "public"."joblines" add column "critical" boolean
not null default 'false';

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "enforce_conversion_category" boolean
-- not null default 'false';

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "enforce_conversion_category" boolean
not null default 'false';

View File

@@ -0,0 +1 @@
alter table "public"."bodyshops" alter column "md_lost_sale_reasons" set default jsonb_build_array();

View File

@@ -0,0 +1 @@
alter table "public"."bodyshops" alter column "md_lost_sale_reasons" set default '["Scheduling Delay", "Backordered Parts", "Price", "Unknown"]';

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."joblines" add column "act_price_before_ppc" numeric
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."joblines" add column "act_price_before_ppc" numeric
null;

View File

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

View File

@@ -0,0 +1,18 @@
CREATE TABLE "public"."employee_teams" ("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, "name" text NOT NULL, "active" boolean NOT NULL DEFAULT true, PRIMARY KEY ("id") , FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE cascade ON DELETE cascade);
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_employee_teams_updated_at"
BEFORE UPDATE ON "public"."employee_teams"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_employee_teams_updated_at" ON "public"."employee_teams"
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"."employee_team_members";

View File

@@ -0,0 +1,18 @@
CREATE TABLE "public"."employee_team_members" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "teamid" uuid NOT NULL, "employeeid" uuid NOT NULL, "labor_rates" jsonb NOT NULL DEFAULT jsonb_build_object(), "percentage" numeric NOT NULL DEFAULT 0, PRIMARY KEY ("id") , FOREIGN KEY ("teamid") REFERENCES "public"."employee_teams"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("employeeid") REFERENCES "public"."employees"("id") ON UPDATE cascade ON DELETE cascade);
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_employee_team_members_updated_at"
BEFORE UPDATE ON "public"."employee_team_members"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_employee_team_members_updated_at" ON "public"."employee_team_members"
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,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."exportlog" add column "metadata" jsonb
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."exportlog" add column "metadata" jsonb
null;

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."bodyshops" add column "tt_enforce_hours_for_tech_console" boolean
-- not null default 'false';

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "tt_enforce_hours_for_tech_console" boolean
not null default 'false';

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."timetickets" add column "committed_at" timestamptz
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."timetickets" add column "committed_at" timestamptz
null;

View File

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

View File

@@ -0,0 +1,18 @@
CREATE TABLE "public"."tt_approval_queue" ("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 NOT NULL, "employeeid" uuid NOT NULL, "timeticketid" uuid, "approved_by" text, "approved_at" timestamptz NOT NULL, "actualhrs" numeric NOT NULL DEFAULT 0, "productivehrs" numeric NOT NULL DEFAULT 0, "rate" numeric NOT NULL DEFAULT 0, "flat_rate" boolean NOT NULL DEFAULT true, "ciecacode" text, "cost_center" text NOT NULL, "date" date NOT NULL DEFAULT now(), "memo" text NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("bodyshopid") REFERENCES "public"."bodyshops"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("jobid") REFERENCES "public"."jobs"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("employeeid") REFERENCES "public"."employees"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("timeticketid") REFERENCES "public"."timetickets"("id") ON UPDATE cascade ON DELETE cascade, FOREIGN KEY ("approved_by") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict);
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_tt_approval_queue_updated_at"
BEFORE UPDATE ON "public"."tt_approval_queue"
FOR EACH ROW
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
COMMENT ON TRIGGER "set_public_tt_approval_queue_updated_at" ON "public"."tt_approval_queue"
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"."tt_approval_queue" alter column "approved_at" set not null;

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