Compare commits

...

50 Commits

Author SHA1 Message Date
Patrick Fic
70e2fd000c Add local media setup to shop config. 2022-05-10 13:05:06 -07:00
Patrick Fic
f9fdd95491 Remove documents components that do not support local media. 2022-05-09 09:56:46 -07:00
Patrick Fic
45354417d0 Merge master into feature/local-images 2022-05-09 08:26:43 -07:00
Patrick Fic
70749cdef5 Merged in release/2022-05-06 (pull request #463)
Release/2022 05 06
2022-05-06 23:46:20 +00:00
Patrick Fic
6c3d29ba91 IO-1853 CSV Generation. 2022-05-06 15:50:05 -07:00
Patrick Fic
eca5e8241a QBO Bill parent by default. 2022-05-06 15:35:34 -07:00
Patrick Fic
cf23266831 IO-1853 Change attendance recipe type to text. 2022-05-06 14:30:26 -07:00
Patrick Fic
09d54722f0 IO-1863 Delete parts return and order line. 2022-05-06 14:25:02 -07:00
Patrick Fic
d78955a8fd IO-1853 Add Attendance Table excel creation. 2022-05-06 10:19:49 -07:00
Patrick Fic
467841bea2 IO-1865 Add employee external ID 2022-05-06 09:46:26 -07:00
Patrick Fic
e56424c9b3 IO-1858 Email Groupings 2022-05-06 09:30:29 -07:00
Patrick Fic
a1e4f3827d Uploads and viewing from bills. 2022-05-05 15:46:58 -07:00
Patrick Fic
5461aae6f6 Base changes to job upload screen. 2022-05-04 18:13:58 -07:00
Patrick Fic
55fa2a9b8d Merged in release/2022-05-06 (pull request #459)
Release/2022 05 06
2022-05-03 18:47:30 +00:00
Patrick Fic
e348110bdd IO-1857 Resolve time ticket update fix. 2022-05-03 11:33:31 -07:00
Patrick Fic
d533423fb6 Add backwards compatibility for log generation. 2022-05-03 11:08:08 -07:00
Patrick Fic
9b4e83705b Add department to QBO Payables. 2022-05-03 09:51:54 -07:00
Patrick Fic
4f6d1d27d5 IO-1855 Change QBO changes to server side. 2022-05-02 15:52:05 -07:00
Patrick Fic
2d9de4703b Merged in release/2022-04-29 (pull request #456)
Test
2022-05-02 01:00:23 +00:00
Patrick Fic
865f4776d0 IO-233 Mixdata schema updates and API. 2022-04-28 09:54:15 -07:00
Patrick Fic
ad6d1202f2 IO-227 Begin PPG 2 way communication. 2022-04-27 16:03:53 -07:00
Patrick Fic
3db613da7f IO-1847 Allow $0 labor line with type selected. 2022-04-26 16:01:50 -07:00
Patrick Fic
c48b0d7b99 IO-1847 Add blank line if no sales lines for QBD export. 2022-04-26 15:56:03 -07:00
Patrick Fic
15cdcdfbea IO-1846 add prior succesful export indicator. 2022-04-26 15:25:51 -07:00
Patrick Fic
39b7280595 Allow clear of deductible status. 2022-04-26 13:38:16 -07:00
Patrick Fic
273542f93b IO-1532 Add job transition tracking server method. 2022-04-26 13:38:07 -07:00
Patrick Fic
6d01199185 IO-1831 Skip PBS vehicle search with no vin 2022-04-25 14:54:36 -07:00
Patrick Fic
db0ade9791 IO-1680 Order as In House from Job View 2022-04-25 14:40:23 -07:00
Patrick Fic
cbad157ded Merged in release/2022-04-22 (pull request #454)
Release/2022 04 22
2022-04-24 20:29:31 +00:00
Patrick Fic
a1f6f2fe4c IO-1477 Update to joblines_status view. 2022-04-22 15:14:02 -07:00
Patrick Fic
f72169d98f Header-Footer template render updates. 2022-04-22 15:12:20 -07:00
Patrick Fic
478e03cbe7 Resolve PO and Bills search on PLI screen. 2022-04-22 11:00:35 -07:00
Patrick Fic
c7389cc093 Updated footer minimum. 2022-04-22 10:37:33 -07:00
Patrick Fic
e659204e8f CNR Modifications. 2022-04-22 10:36:00 -07:00
Patrick Fic
07e6344812 IO-1839 Add filing coversheet landscape. 2022-04-21 12:36:04 -07:00
Patrick Fic
73e50df21b Added missing translations. 2022-04-21 12:13:07 -07:00
Patrick Fic
80f92203ca IO-1834 Add more job info and filtering to scoreboard jobs display. 2022-04-21 12:10:39 -07:00
Patrick Fic
eab9aca3d4 IO-1840 Add to scoreboard from production detail view. 2022-04-21 11:51:47 -07:00
Patrick Fic
49818cc043 Modification to Credits not Received. 2022-04-21 11:45:41 -07:00
Patrick Fic
51843f364b Add footer path. 2022-04-21 10:04:39 -07:00
Patrick Fic
7d8bbe69bd IO-1366 Add additional audit trail items. 2022-04-18 16:18:57 -07:00
Patrick Fic
6998a11a3a Add refresh button to job detail page. 2022-04-18 15:34:12 -07:00
Patrick Fic
65bf81b349 IO-1829 Add additional columns to visual board. 2022-04-18 14:48:56 -07:00
Patrick Fic
65783cde07 Update parts status counter to exclude sublets. 2022-04-18 14:34:36 -07:00
Patrick Fic
74297052d4 IO-1830 Add Parts Status to Production Boards. 2022-04-18 14:28:53 -07:00
Patrick Fic
988c3a9f22 IO-1477 Updated parts queue page. 2022-04-18 13:53:21 -07:00
Patrick Fic
c08713bfbe IO-1650 Added ready jobs screen. 2022-04-18 13:07:52 -07:00
Patrick Fic
a10b5a2ee0 IO-1816 Refactor main page. 2022-04-18 11:37:56 -07:00
Patrick Fic
920ebdde42 Merged in release/2022-04-15 (pull request #448)
Release/2022 04 15
2022-04-14 17:48:45 +00:00
Patrick Fic
e31a7eb65f Resolve third party payer template issue. 2022-04-14 10:23:09 -07:00
138 changed files with 6614 additions and 1115 deletions

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
@@ -1243,6 +1243,69 @@
<folder_node>
<name>messages</name>
<children>
<concept_node>
<name>admin_jobmarkexported</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>admin_jobmarkforreexport</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>admin_jobunvoid</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>billposted</name>
<definition_loaded>false</definition_loaded>
@@ -2611,6 +2674,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>calculatedcreditsnotreceived</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>creditsnotreceived</name>
<definition_loaded>false</definition_loaded>
@@ -4451,6 +4535,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>localmediaserverhttp</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>localmediaservernetwork</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>logo_img_footer_margin</name>
<definition_loaded>false</definition_loaded>
@@ -5736,6 +5862,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>list-ready</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>partsqueue</name>
<definition_loaded>false</definition_loaded>
@@ -7629,6 +7776,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>additional_board_statuses</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>color</name>
<definition_loaded>false</definition_loaded>
@@ -8028,6 +8196,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>ready_statuses</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>
@@ -8114,6 +8303,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>uselocalmediaserver</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>website</name>
<definition_loaded>false</definition_loaded>
@@ -8780,6 +8990,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>md_to_emails</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_to_emails_emails</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>messagingpresets</name>
<definition_loaded>false</definition_loaded>
@@ -13064,6 +13316,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>openinexplorer</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>reassign_limitexceeded</name>
<definition_loaded>false</definition_loaded>
@@ -13849,6 +14122,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>external_id</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>first_name</name>
<definition_loaded>false</definition_loaded>
@@ -14346,6 +14640,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>priorsuccesfulexport</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>
@@ -15862,6 +16177,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>refresh</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>required</name>
<definition_loaded>false</definition_loaded>
@@ -23147,6 +23483,27 @@
</concept_node>
</children>
</folder_node>
<concept_node>
<name>partsstatus</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>pas</name>
<definition_loaded>false</definition_loaded>
@@ -23299,6 +23656,27 @@
</concept_node>
</children>
</folder_node>
<concept_node>
<name>queued_for_parts</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>rate_ats</name>
<definition_loaded>false</definition_loaded>
@@ -26907,6 +27285,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>calculatedcreditsnotreceived</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>creditmemos</name>
<definition_loaded>false</definition_loaded>
@@ -29640,6 +30039,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>readyjobs</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>recent</name>
<definition_loaded>false</definition_loaded>
@@ -32143,6 +32563,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>orderinhouse</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>
@@ -32305,6 +32746,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>saving</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>updating</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>
@@ -32373,6 +32856,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>cm_received</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>comments</name>
<definition_loaded>false</definition_loaded>
@@ -32798,6 +33302,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>mark_as_received</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>newpartsorder</name>
<definition_loaded>false</definition_loaded>
@@ -32992,6 +33517,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>line_updated</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>received</name>
<definition_loaded>false</definition_loaded>
@@ -34867,6 +35413,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>filing_coversheet_landscape</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>filing_coversheet_portrait</name>
<definition_loaded>false</definition_loaded>
@@ -36254,6 +36821,32 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>special</name>
<children>
<concept_node>
<name>attendance_detail_csv</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>
<name>subjects</name>
<children>
@@ -36665,6 +37258,27 @@
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>actual_in</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>alert</name>
<definition_loaded>false</definition_loaded>
@@ -37116,6 +37730,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>partsstatus</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>production_note</name>
<definition_loaded>false</definition_loaded>
@@ -41811,6 +42446,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobs-ready</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>owner-detail</name>
<definition_loaded>false</definition_loaded>
@@ -42821,6 +43477,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>readyjobs</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>resetpassword</name>
<definition_loaded>false</definition_loaded>

View File

@@ -10,7 +10,7 @@
"@fingerprintjs/fingerprintjs": "^3.3.3",
"@sentry/react": "^6.19.6",
"@sentry/tracing": "^6.19.6",
"@splitsoftware/splitio-react": "^1.3.1-rc.1",
"@splitsoftware/splitio-react": "^1.4.0",
"@stripe/react-stripe-js": "^1.7.1",
"@stripe/stripe-js": "^1.27.0",
"@tanem/react-nprogress": "^4.0.12",
@@ -23,7 +23,7 @@
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.6.10",
"firebase": "^9.6.11",
"graphql": "^16.3.0",
"i18next": "^21.6.16",
"i18next-browser-languagedetector": "^6.1.4",
@@ -31,9 +31,10 @@
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.51",
"logrocket": "^2.2.1",
"markerjs2": "^2.21.0",
"markerjs2": "^2.21.1",
"moment-business-days": "^1.2.0",
"moment-timezone": "^0.5.34",
"normalize-url": "^7.0.3",
"phone": "^3.1.15",
"preval.macro": "^5.0.0",
"prop-types": "^15.8.1",
@@ -48,7 +49,7 @@
"react-drag-listview": "^0.1.9",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.3.4",
"react-i18next": "^11.16.5",
"react-i18next": "^11.16.6",
"react-icons": "^4.3.1",
"react-number-format": "^4.9.1",
"react-redux": "^7.2.8",

BIN
client/src/assets/banner4.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

View File

@@ -13,6 +13,7 @@ import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -27,7 +28,7 @@ export default connect(
mapDispatchToProps
)(AccountingPayablesTableComponent);
export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
export function AccountingPayablesTableComponent({ bodyshop, loading, bills, refetch }) {
const { t } = useTranslation();
const [selectedBills, setSelectedBills] = useState([]);
const [transInProgress, setTransInProgress] = useState(false);
@@ -131,11 +132,9 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
dataIndex: "attempts",
key: "attempts",
render: (text, record) => {
const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
render: (text, record) => (
<ExportLogsCountDisplay logs={record.exportlogs} />
),
},
{
title: t("general.labels.actions"),
@@ -150,6 +149,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
disabled={transInProgress || !!record.exported}
loadingCallback={setTransInProgress}
setSelectedBills={setSelectedBills}
refetch={refetch}
/>
</div>
),
@@ -182,6 +182,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
disabled={transInProgress || selectedBills.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedBills}
refetch={refetch}
/>
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />

View File

@@ -13,6 +13,7 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -31,6 +32,7 @@ export function AccountingPayablesTableComponent({
bodyshop,
loading,
payments,
refetch,
}) {
const { t } = useTranslation();
const [selectedPayments, setSelectedPayments] = useState([]);
@@ -130,11 +132,9 @@ export function AccountingPayablesTableComponent({
dataIndex: "attempts",
key: "attempts",
render: (text, record) => {
const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
render: (text, record) => (
<ExportLogsCountDisplay logs={record.exportlogs} />
),
},
{
title: t("general.labels.actions"),
@@ -148,6 +148,7 @@ export function AccountingPayablesTableComponent({
disabled={transInProgress || !!record.exportedat}
loadingCallback={setTransInProgress}
setSelectedPayments={setSelectedPayments}
refetch={refetch}
/>
),
},
@@ -188,6 +189,7 @@ export function AccountingPayablesTableComponent({
disabled={transInProgress || selectedPayments.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedPayments}
refetch={refetch}
/>
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
<QboAuthorizeComponent />

View File

@@ -14,6 +14,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
import { DateFormatter } from "../../utils/DateFormatter";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -30,6 +31,7 @@ export function AccountingReceivablesTableComponent({
bodyshop,
loading,
jobs,
refetch,
}) {
const { t } = useTranslation();
const [selectedJobs, setSelectedJobs] = useState([]);
@@ -139,12 +141,9 @@ export function AccountingReceivablesTableComponent({
title: t("exportlogs.labels.attempts"),
dataIndex: "attempts",
key: "attempts",
render: (text, record) => {
const success = record.exportlogs.filter((e) => e.successful).length;
const attempts = record.exportlogs.length;
return `${success}/${attempts}`;
},
render: (text, record) => (
<ExportLogsCountDisplay logs={record.exportlogs} />
),
},
{
title: t("general.labels.actions"),
@@ -157,6 +156,7 @@ export function AccountingReceivablesTableComponent({
jobId={record.id}
disabled={!!record.date_exported}
setSelectedJobs={setSelectedJobs}
refetch={refetch}
/>
<Link to={`/manage/jobs/${record.id}/close`}>
<Button>{t("jobs.labels.viewallocations")}</Button>
@@ -207,6 +207,7 @@ export function AccountingReceivablesTableComponent({
disabled={transInProgress || selectedJobs.length === 0}
loadingCallback={setTransInProgress}
completedCallback={setSelectedJobs}
refetch={refetch}
/>
)}
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (

View File

@@ -0,0 +1,136 @@
import { Checkbox, Form, Skeleton, Typography } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
import "./bill-cm-returns-table.styles.scss";
export default function BillCmdReturnsTableComponent({
form,
loadOutstandingReturns,
returnLoading,
returnData,
}) {
const { t } = useTranslation();
useEffect(() => {
if (returnData) {
form.setFieldsValue({
outstanding_returns: returnData.parts_order_lines,
});
}
}, [returnData, form]);
return (
<Form.Item
shouldUpdate={(prev, cur) =>
prev.jobid !== cur.jobid ||
prev.is_credit_memo !== cur.is_credit_memo ||
prev.vendorid !== cur.vendorid
}
noStyle
>
{() => {
const isReturn = form.getFieldValue("is_credit_memo");
if (!isReturn) {
return null;
}
if (returnLoading) return <Skeleton />;
return (
<Form.List name="outstanding_returns">
{(fields, { add, remove, move }) => {
return (
<>
<Typography.Title level={4}>
{t("bills.labels.creditsnotreceived")}
</Typography.Title>
<table className="bill-cm-returns-table">
<thead>
<tr>
<th>{t("parts_orders.fields.line_desc")}</th>
<th>{t("parts_orders.fields.part_type")}</th>
<th>{t("parts_orders.fields.quantity")}</th>
<th>{t("parts_orders.fields.act_price")}</th>
<th>{t("parts_orders.fields.cost")}</th>
<th>{t("parts_orders.labels.mark_as_received")}</th>
</tr>
</thead>
<tbody>
{fields.map((field, index) => (
<tr key={field.key}>
<td>
<Form.Item
// label={t("joblines.fields.line_desc")}
key={`${index}line_desc`}
name={[field.name, "line_desc"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}part_type`}
name={[field.name, "part_type"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}quantity`}
name={[field.name, "quantity"]}
>
<ReadOnlyFormItemComponent />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}act_price`}
name={[field.name, "act_price"]}
>
<ReadOnlyFormItemComponent type="currency" />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}cost`}
name={[field.name, "cost"]}
>
<ReadOnlyFormItemComponent type="currency" />
</Form.Item>
</td>
<td>
<Form.Item
span={2}
//label={t("joblines.fields.mod_lb_hrs")}
key={`${index}cm_received`}
name={[field.name, "cm_received"]}
valuePropName="checked"
>
<Checkbox />
</Form.Item>
</td>
</tr>
))}
</tbody>
</table>
</>
);
}}
</Form.List>
);
}}
</Form.Item>
);
}

View File

@@ -0,0 +1,19 @@
.bill-cm-returns-table {
table-layout: fixed;
width: 100%;
th,
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
.ant-form-item {
margin-bottom: 0px !important;
}
}
tr:hover {
background-color: #f5f5f5;
}
}

View File

@@ -12,27 +12,29 @@ import moment from "moment";
import queryString from "query-string";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useLocation, useHistory } from "react-router-dom";
import { connect } from "react-redux";
import { useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import {
DELETE_BILL_LINE,
INSERT_NEW_BILL_LINES,
UPDATE_BILL_LINE,
} from "../../graphql/bill-lines.queries";
import { QUERY_BILL_BY_PK, UPDATE_BILL } from "../../graphql/bills.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import AlertComponent from "../alert/alert.component";
import BillFormContainer from "../bill-form/bill-form.container";
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import BillMarkExportedButton from "../bill-mark-exported-button/bill-mark-exported-button.component";
import BillReeportButtonComponent from "../bill-reexport-button/bill-reexport-button.component";
import JobDocumentsGallery from "../jobs-documents-gallery/jobs-documents-gallery.container";
import JobsDocumentsLocalGallery from "../jobs-documents-local-gallery/jobs-documents-local-gallery.container";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setPartsOrderContext: (context) =>
@@ -49,6 +51,7 @@ export default connect(
export function BillDetailEditcontainer({
setPartsOrderContext,
insertAuditTrail,
bodyshop,
}) {
const search = queryString.parse(useLocation().search);
const history = useHistory();
@@ -265,12 +268,21 @@ export function BillDetailEditcontainer({
layout="vertical"
>
<BillFormContainer form={form} billEdit disabled={exported} />
<JobDocumentsGallery
jobId={data ? data.bills_by_pk.jobid : null}
billId={search.billid}
documentsList={data ? data.bills_by_pk.documents : []}
billsCallback={refetch}
/>
{bodyshop.uselocalmediaserver ? (
<JobsDocumentsLocalGallery
job={{ id: data ? data.bills_by_pk.jobid : null }}
invoice_number={data ? data.bills_by_pk.invoice_number : null}
vendorid={data ? data.bills_by_pk.vendorid : null}
/>
) : (
<JobDocumentsGallery
jobId={data ? data.bills_by_pk.jobid : null}
billId={search.billid}
documentsList={data ? data.bills_by_pk.documents : []}
billsCallback={refetch}
/>
)}
</Form>
</>
)}

View File

@@ -11,6 +11,7 @@ import {
QUERY_JOB_LBR_ADJUSTMENTS,
UPDATE_JOB,
} from "../../graphql/jobs.queries";
import { MUTATION_MARK_RETURN_RECEIVED } from "../../graphql/parts-orders.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
@@ -23,6 +24,7 @@ import AuditTrailMapping from "../../utils/AuditTrailMappings";
import BillFormContainer from "../bill-form/bill-form.container";
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
import { handleUpload } from "../documents-upload/documents-upload.utility";
import { handleUpload as handleLocalUpload } from "../documents-local-upload/documents-local-upload.utility";
const mapStateToProps = createStructuredSelector({
billEnterModal: selectBillEnterModal,
@@ -47,6 +49,7 @@ function BillEnterModalContainer({
const [enterAgain, setEnterAgain] = useState(false);
const [insertBill] = useMutation(INSERT_NEW_BILL);
const [updateJobLines] = useMutation(UPDATE_JOB_LINE);
const [updatePartsOrderLines] = useMutation(MUTATION_MARK_RETURN_RECEIVED);
const [loading, setLoading] = useState(false);
const client = useApolloClient();
@@ -76,7 +79,8 @@ function BillEnterModalContainer({
}
setLoading(true);
const { upload, location, ...remainingValues } = values;
const { upload, location, outstanding_returns, ...remainingValues } =
values;
let adjustmentsToInsert = {};
@@ -156,6 +160,25 @@ function BillEnterModalContainer({
});
}
const markPolReceived =
outstanding_returns &&
outstanding_returns.filter((o) => o.cm_received === true);
if (markPolReceived && markPolReceived.length > 0) {
const r2 = await updatePartsOrderLines({
variables: { partsLineIds: markPolReceived.map((p) => p.id) },
});
if (!!r2.errors) {
setLoading(false);
setEnterAgain(false);
notification["error"]({
message: t("parts_orders.errors.updating", {
message: JSON.stringify(r2.errors),
}),
});
}
}
if (!!r1.errors) {
setLoading(false);
setEnterAgain(false);
@@ -188,19 +211,33 @@ function BillEnterModalContainer({
/////////////////////////
if (upload && upload.length > 0) {
//insert Each of the documents?
upload.forEach((u) => {
handleUpload(
{ file: u.originFileObj },
{
bodyshop: bodyshop,
uploaded_by: currentUser.email,
jobId: values.jobid,
billId: billId,
tagsArray: null,
callback: null,
}
);
});
if (bodyshop.uselocalmediaserver) {
upload.forEach((u) => {
handleLocalUpload({
ev: { file: u.originFileObj },
context: {
jobid: values.jobid,
invoice_number: remainingValues.invoice_number,
vendorid: remainingValues.vendorid,
},
});
});
} else {
upload.forEach((u) => {
handleUpload(
{ file: u.originFileObj },
{
bodyshop: bodyshop,
uploaded_by: currentUser.email,
jobId: values.jobid,
billId: billId,
tagsArray: null,
callback: null,
}
);
});
}
}
///////////////////////////
setLoading(false);

View File

@@ -47,6 +47,7 @@ export function BillFormComponent({
billEdit,
disableInvNumber,
job,
loadOutstandingReturns,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -58,6 +59,14 @@ export function BillFormComponent({
);
const handleVendorSelect = (props, opt) => {
setDiscount(opt.discount);
opt &&
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: opt.value,
},
});
};
useEffect(() => {
@@ -65,8 +74,8 @@ export function BillFormComponent({
}, [job, form]);
useEffect(() => {
if (form.getFieldValue("vendorid") && vendorAutoCompleteOptions) {
const vendorId = form.getFieldValue("vendorid");
const vendorId = form.getFieldValue("vendorid");
if (vendorId && vendorAutoCompleteOptions) {
const matchingVendors = vendorAutoCompleteOptions.filter(
(v) => v.id === vendorId
);
@@ -74,10 +83,25 @@ export function BillFormComponent({
setDiscount(matchingVendors[0].discount);
}
}
if (form.getFieldValue("jobid")) {
loadLines({ variables: { id: form.getFieldValue("jobid") } });
const jobId = form.getFieldValue("jobid");
if (jobId) {
loadLines({ variables: { id: jobId } });
if (form.getFieldValue("is_credit_memo") && vendorId) {
loadOutstandingReturns({
variables: {
jobId: jobId,
vendorId: vendorId,
},
});
}
}
}, [form, setDiscount, vendorAutoCompleteOptions, loadLines]);
}, [
form,
loadOutstandingReturns,
setDiscount,
vendorAutoCompleteOptions,
loadLines,
]);
return (
<div>
@@ -107,6 +131,13 @@ export function BillFormComponent({
onBlur={() => {
if (form.getFieldValue("jobid") !== null) {
loadLines({ variables: { id: form.getFieldValue("jobid") } });
if (form.getFieldValue("vendorid") !== null)
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: form.getFieldValue("vendorid"),
},
});
}
}}
/>
@@ -228,8 +259,22 @@ export function BillFormComponent({
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (
value === true &&
getFieldValue("jobid") &&
getFieldValue("vendorid")
) {
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
vendorId: form.getFieldValue("vendorid"),
},
});
}
if (
!bodyshop.bill_allow_post_to_closed &&
job &&
(job.status === bodyshop.md_ro_statuses.default_invoiced ||
job.status === bodyshop.md_ro_statuses.default_exported ||
job.status === bodyshop.md_ro_statuses.default_void) &&

View File

@@ -6,6 +6,8 @@ import { GET_JOB_LINES_TO_ENTER_BILL } from "../../graphql/jobs-lines.queries";
import { SEARCH_VENDOR_AUTOCOMPLETE } from "../../graphql/vendors.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import BillFormComponent from "./bill-form.component";
import BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -27,20 +29,34 @@ export function BillFormContainer({
GET_JOB_LINES_TO_ENTER_BILL
);
const [loadOutstandingReturns, { loading: returnLoading, data: returnData }] =
useLazyQuery(QUERY_UNRECEIVED_LINES);
return (
<BillFormComponent
disabled={disabled}
form={form}
billEdit={billEdit}
vendorAutoCompleteOptions={
VendorAutoCompleteData && VendorAutoCompleteData.vendors
}
loadLines={loadLines}
lineData={lineData ? lineData.joblines : []}
job={lineData ? lineData.jobs_by_pk : null}
responsibilityCenters={bodyshop.md_responsibility_centers || null}
disableInvNumber={disableInvNumber}
/>
<>
<BillFormComponent
disabled={disabled}
form={form}
billEdit={billEdit}
vendorAutoCompleteOptions={
VendorAutoCompleteData && VendorAutoCompleteData.vendors
}
loadLines={loadLines}
lineData={lineData ? lineData.joblines : []}
job={lineData ? lineData.jobs_by_pk : null}
responsibilityCenters={bodyshop.md_responsibility_centers || null}
disableInvNumber={disableInvNumber}
loadOutstandingReturns={loadOutstandingReturns}
/>
{!billEdit && (
<BillCmdReturnsTableComponent
form={form}
loadOutstandingReturns={loadOutstandingReturns}
returnLoading={returnLoading}
returnData={returnData}
/>
)}
</>
);
}
export default connect(mapStateToProps, null)(BillFormContainer);

View File

@@ -43,6 +43,8 @@ export function BillsListTableComponent({
});
// const search = queryString.parse(useLocation().search);
// const selectedBill = search.billid;
const [searchText, setSearchText] = useState("");
const Templates = TemplateList("bill");
const bills = billsQuery.data ? billsQuery.data.bills : [];
const { refetch } = billsQuery;
@@ -59,7 +61,6 @@ export function BillsListTableComponent({
record.is_credit_memo || record.vendorid === bodyshop.inhousevendorid
}
onClick={() => {
console.log(record);
setPartsOrderContext({
actions: {},
context: {
@@ -167,6 +168,24 @@ export function BillsListTableComponent({
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const filteredBills = bills
? searchText === ""
? bills
: bills.filter(
(b) =>
(b.invoice_number || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(b.vendor.name || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(b.total || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
return (
<Card
title={t("bills.labels.bills")}
@@ -207,8 +226,10 @@ export function BillsListTableComponent({
<Input.Search
placeholder={t("general.labels.search")}
value={searchText}
onChange={(e) => {
e.preventDefault();
setSearchText(e.target.value);
}}
/>
</Space>
@@ -221,7 +242,7 @@ export function BillsListTableComponent({
}}
columns={columns}
rowKey="id"
dataSource={bills}
dataSource={filteredBills}
onChange={handleTableChange}
/>
</Card>

View File

@@ -6,12 +6,13 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import JobDocumentsGalleryExternal from "../jobs-documents-gallery/jobs-documents-gallery.external.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -19,6 +20,7 @@ const mapDispatchToProps = (dispatch) => ({
export default connect(mapStateToProps, mapDispatchToProps)(ChatMediaSelector);
export function ChatMediaSelector({
bodyshop,
selectedMedia,
setSelectedMedia,
conversation,
@@ -27,7 +29,6 @@ export function ChatMediaSelector({
const [visible, setVisible] = useState(false);
const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
variables: {
@@ -66,6 +67,8 @@ export function ChatMediaSelector({
</div>
);
if (bodyshop.uselocalmediaserver) return null;
return (
<Popover
content={

View File

@@ -19,7 +19,7 @@ export function ChatPresetsComponent({ bodyshop, setMessage, className }) {
const menu = (
<Menu>
{bodyshop.md_messaging_presets.map((i, idx) => (
<Menu.Item onClick={() => setMessage(i.text)} onItemHover key={idx}>
<Menu.Item onClick={() => setMessage(i.text)} key={idx}>
{i.label}
</Menu.Item>
))}

View File

@@ -0,0 +1,70 @@
import { UploadOutlined } from "@ant-design/icons";
import { Upload } from "antd";
import React, { useState } from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { handleUpload } from "./documents-local-upload.utility";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
});
export function DocumentsLocalUploadComponent({
children,
currentUser,
bodyshop,
job,
vendorid,
invoice_number,
callbackAfterUpload,
}) {
const [fileList, setFileList] = useState([]);
const handleDone = (uid) => {
setTimeout(() => {
setFileList((fileList) => fileList.filter((x) => x.uid !== uid));
}, 2000);
};
return (
<Upload.Dragger
multiple={true}
fileList={fileList}
onChange={(f) => {
if (f.event && f.event.percent === 100) handleDone(f.file.uid);
setFileList(f.fileList);
}}
customRequest={(ev) =>
handleUpload({
ev,
context: {
jobid: job.id,
vendorid,
invoice_number,
callback: callbackAfterUpload,
},
})
}
accept="audio/*, video/*, image/*, .pdf, .doc, .docx, .xls, .xlsx"
>
{children || (
<>
<p className="ant-upload-drag-icon">
<UploadOutlined />
</p>
<p className="ant-upload-text">
Click or drag files to this area to upload.
</p>
</>
)}
</Upload.Dragger>
);
}
export default connect(mapStateToProps, null)(DocumentsLocalUploadComponent);

View File

@@ -0,0 +1,65 @@
import cleanAxios from "../../utils/CleanAxios";
import { store } from "../../redux/store";
import { addMediaForJob } from "../../redux/media/media.actions";
import normalizeUrl from "normalize-url";
export const handleUpload = async ({ ev, context }) => {
const { onError, onSuccess, onProgress, file } = ev;
const { jobid, invoice_number, vendorid, callbackAfterUpload } = context;
var options = {
headers: { "X-Requested-With": "XMLHttpRequest" },
onUploadProgress: (e) => {
if (!!onProgress) onProgress({ percent: (e.loaded / e.total) * 100 });
},
};
const formData = new FormData();
formData.append("jobid", jobid);
if (invoice_number) {
formData.append("invoice_number", invoice_number);
formData.append("vendorid", vendorid);
}
formData.append("file", file);
const bodyshop = store.getState().user.bodyshop;
const imexMediaServerResponse = await cleanAxios.post(
normalizeUrl(
`${bodyshop.localmediaserverhttp}/${
invoice_number ? "bills" : "jobs"
}/upload`
),
formData,
{
...options,
}
);
if (imexMediaServerResponse.status !== 200) {
if (!!onError) {
onError(imexMediaServerResponse.statusText);
}
} else {
onSuccess && onSuccess(file);
store.dispatch(
addMediaForJob({
jobid,
media: imexMediaServerResponse.data.map((d) => {
return {
...d,
selected: false,
src: normalizeUrl(`${bodyshop.localmediaserverhttp}/${d.src}`),
thumbnail: normalizeUrl(
`${bodyshop.localmediaserverhttp}/${d.thumbnail}`
),
};
}),
})
);
}
if (callbackAfterUpload) {
callbackAfterUpload();
}
};

View File

@@ -9,6 +9,7 @@ import {
Space,
Menu,
Dropdown,
Button,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -20,10 +21,13 @@ import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
import { selectEmailConfig } from "../../redux/email/email.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
emailConfig: selectEmailConfig,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -34,6 +38,7 @@ export default connect(
)(EmailOverlayComponent);
export function EmailOverlayComponent({
emailConfig,
form,
selectedMediaState,
bodyshop,
@@ -42,7 +47,12 @@ export function EmailOverlayComponent({
const { t } = useTranslation();
const handleClick = ({ item, key, keyPath }) => {
const email = item.props.value;
form.setFieldsValue({ to: _.uniq([...form.getFieldValue("to"), email]) });
form.setFieldsValue({
to: _.uniq([
...form.getFieldValue("to"),
...(typeof email === "string" ? [email] : email),
]),
});
};
const menu = (
@@ -55,6 +65,11 @@ export function EmailOverlayComponent({
{`${e.first_name} ${e.last_name}`}
</Menu.Item>
))}
{bodyshop.md_to_emails.map((e, idx) => (
<Menu.Item value={e.emails} key={idx + "group"}>
{e.label}
</Menu.Item>
))}
</Menu>
</div>
);
@@ -143,10 +158,17 @@ export function EmailOverlayComponent({
</Form.Item>
<Tabs>
<Tabs.TabPane tab={t("emails.labels.documents")} key="documents">
<EmailDocumentsComponent selectedMediaState={selectedMediaState} />
</Tabs.TabPane>
{!bodyshop.uselocalmediaserver && (
<Tabs.TabPane tab={t("emails.labels.documents")} key="documents">
<EmailDocumentsComponent selectedMediaState={selectedMediaState} />
</Tabs.TabPane>
)}
<Tabs.TabPane tab={t("emails.labels.attachments")} key="attachments">
{bodyshop.uselocalmediaserver && emailConfig.jobid && (
<a href={CreateExplorerLinkForJob({ jobid: emailConfig.jobid })}>
<Button>{t("documents.labels.openinexplorer")}</Button>
</a>
)}
<Form.Item
name="fileList"
valuePropName="fileList"

View File

@@ -0,0 +1,25 @@
import React from "react";
import { WarningOutlined } from "@ant-design/icons";
import { Space, Tooltip } from "antd";
import { useTranslation } from "react-i18next";
const style = {
fontWeight: "bold",
color: "green",
};
export default function ExportLogsCountDisplay({ logs }) {
const success = logs.filter((e) => e.successful).length;
const attempts = logs.length;
const { t } = useTranslation();
return (
<Space style={success > 0 ? style : {}}>
{`${success}/${attempts}`}
{success > 0 && (
<Tooltip title={t("exportlogs.labels.priorsuccesfulexport")}>
<WarningOutlined />
</Tooltip>
)}
</Space>
);
}

View File

@@ -3,6 +3,7 @@ import Icon, {
BarChartOutlined,
CarFilled,
ClockCircleFilled,
CheckCircleOutlined,
DashboardFilled,
DollarCircleFilled,
ExportOutlined,
@@ -108,6 +109,9 @@ function Header({
<Menu.Item key="activejobs" icon={<FileFilled />}>
<Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
</Menu.Item>
<Menu.Item key="readyjobs" icon={<CheckCircleOutlined />}>
<Link to="/manage/jobs/ready">{t("menus.header.readyjobs")}</Link>
</Menu.Item>
<Menu.Item key="parts-queue" icon={<ToolFilled />}>
<Link to="/manage/partsqueue">{t("menus.header.parts-queue")}</Link>
</Menu.Item>

View File

@@ -49,11 +49,11 @@ export function Jobd3RdPartyModal({ bodyshop, jobId }) {
GenerateDocument(
{
name: TemplateList("job_special").thirdpartypayer.key,
name: TemplateList("job_special").special_thirdpartypayer.key,
variables: { id: jobId },
context: restVals,
},
{ subject: TemplateList("job_special").thirdpartypayer.subject },
{ subject: TemplateList("job_special").special_thirdpartypayer.subject },
sendtype
);
};

View File

@@ -26,6 +26,8 @@ export default function JobBillsTotalComponent({
let billCms = Dinero();
let lbrAdjustments = Dinero();
let totalReturns = Dinero();
let totalReturnsMarkedNotReceived = Dinero();
let totalReturnsMarkedReceived = Dinero();
partsOrders.forEach((p) =>
p.parts_order_lines.forEach((pol) => {
@@ -35,6 +37,24 @@ export default function JobBillsTotalComponent({
amount: Math.round((pol.act_price || 0) * 100),
}).multiply(pol.quantity)
);
if (pol.cm_received === null) {
return; // Skip this calculation for bills posted prior to the CNR change.
} else {
if (pol.cm_received === false) {
totalReturnsMarkedNotReceived = totalReturnsMarkedNotReceived.add(
Dinero({
amount: Math.round((pol.act_price || 0) * 100),
}).multiply(pol.quantity)
);
} else {
totalReturnsMarkedReceived = totalReturnsMarkedReceived.add(
Dinero({
amount: Math.round((pol.act_price || 0) * 100),
}).multiply(pol.quantity)
);
}
}
}
})
);
@@ -73,7 +93,7 @@ export default function JobBillsTotalComponent({
const discrepWithLbrAdj = discrepancy.add(lbrAdjustments);
const discrepWithCms = discrepWithLbrAdj.add(totalReturns);
const creditsNotReceived = totalReturns.subtract(billCms); //billCms is tracked as a negative number.
const calculatedCreditsNotReceived = totalReturns.subtract(billCms); //billCms is tracked as a negative number.
return (
<Row gutter={16}>
@@ -213,6 +233,32 @@ export default function JobBillsTotalComponent({
value={totalReturns.toFormat()}
/>
</Tooltip>
<Tooltip
title={
<div
dangerouslySetInnerHTML={{
__html: t(
"jobs.labels.plitooltips.calculatedcreditsnotreceived"
),
}}
/>
}
>
<Statistic
title={t("bills.labels.calculatedcreditsnotreceived")}
valueStyle={{
color:
calculatedCreditsNotReceived.getAmount() <= 0
? "green"
: "red",
}}
value={
calculatedCreditsNotReceived.getAmount() >= 0
? calculatedCreditsNotReceived.toFormat()
: Dinero().toFormat()
}
/>
</Tooltip>
<Tooltip
title={
<div
@@ -225,11 +271,14 @@ export default function JobBillsTotalComponent({
<Statistic
title={t("bills.labels.creditsnotreceived")}
valueStyle={{
color: creditsNotReceived.getAmount() <= 0 ? "green" : "red",
color:
totalReturnsMarkedNotReceived.getAmount() <= 0
? "green"
: "red",
}}
value={
creditsNotReceived.getAmount() >= 0
? creditsNotReceived.toFormat()
totalReturnsMarkedNotReceived.getAmount() >= 0
? totalReturnsMarkedNotReceived.toFormat()
: Dinero().toFormat()
}
/>

View File

@@ -6,8 +6,10 @@ import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_JOB_CARD_DETAILS } from "../../graphql/jobs.queries";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import JobSyncButton from "../job-sync-button/job-sync-button.component";
import JobsDetailHeader from "../jobs-detail-header/jobs-detail-header.component";
@@ -20,6 +22,10 @@ import JobDetailCardsNotesComponent from "./job-detail-cards.notes.component";
import JobDetailCardsPartsComponent from "./job-detail-cards.parts.component";
import JobDetailCardsTotalsComponent from "./job-detail-cards.totals.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "printCenter" })),
@@ -31,7 +37,7 @@ const span = {
lg: { span: 8 },
};
export function JobDetailCards({ setPrintCenterContext }) {
export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
@@ -143,12 +149,14 @@ export function JobDetailCards({ setPrintCenterContext }) {
data={data ? data.jobs_by_pk : null}
/>
</Col>
<Col {...span}>
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
{!bodyshop.uselocalmediaserver && (
<Col {...span}>
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
</Col>
)}
<Col {...span}>
<JobDetailCardsDamageComponent
loading={loading}
@@ -161,4 +169,4 @@ export function JobDetailCards({ setPrintCenterContext }) {
</Drawer>
);
}
export default connect(null, mapDispatchToProps)(JobDetailCards);
export default connect(mapStateToProps, mapDispatchToProps)(JobDetailCards);

View File

@@ -6,6 +6,7 @@ import {
EditFilled,
PlusCircleTwoTone,
MinusCircleTwoTone,
HomeOutlined,
} from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import {
@@ -42,6 +43,7 @@ import _ from "lodash";
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
import JobLinesExpander from "./job-lines-expander.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -54,6 +56,8 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setModalContext({ context: context, modal: "jobLineEdit" })),
setPartsOrderContext: (context) =>
dispatch(setModalContext({ context: context, modal: "partsOrder" })),
setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "billEnter" })),
});
export function JobLinesComponent({
@@ -68,6 +72,7 @@ export function JobLinesComponent({
job,
setJobLineEditContext,
form,
setBillEnterContext,
}) {
const [deleteJobLine] = useMutation(DELETE_JOB_LINE_BY_PK);
@@ -386,6 +391,62 @@ export function JobLinesComponent({
</Space>
</Tag>
)}
<Button
disabled={
(job && !job.converted) ||
(selectedLines.length > 0 ? false : true) ||
jobRO ||
technician
}
onClick={() => {
// setPartsOrderContext({
// actions: { refetch: refetch },
// context: {
// jobId: job.id,
// job: job,
// linesToOrder: selectedLines,
// },
// });
setBillEnterContext({
actions: { refetch: refetch },
context: {
disableInvNumber: true,
job: { id: job.id },
bill: {
vendorid: bodyshop.inhousevendorid,
invoice_number: "ih",
isinhouse: true,
date: new moment(),
total: 0,
billlines: selectedLines.map((p) => {
return {
joblineid: p.id,
actual_price: p.act_price,
actual_cost: 0, //p.act_price,
line_desc: p.line_desc,
line_remarks: p.line_remarks,
part_type: p.part_type,
quantity: p.quantity || 1,
applicable_taxes: {
local: false,
state: false,
federal: false,
},
};
}),
},
},
});
//Clear out the selected lines. IO-785
setSelectedLines([]);
}}
>
<HomeOutlined />
{t("parts.actions.orderinhouse")}
{selectedLines.length > 0 && ` (${selectedLines.length})`}
</Button>
<Button
disabled={
(job && !job.converted) ||

View File

@@ -24,7 +24,7 @@ export function JoblinePresetButton({ bodyshop, form }) {
const menu = (
<Menu>
{bodyshop.md_jobline_presets.map((i, idx) => (
<Menu.Item onClick={() => handleSelect(i)} onItemHover key={idx}>
<Menu.Item onClick={() => handleSelect(i)} key={idx}>
{i.label}
</Menu.Item>
))}

View File

@@ -141,7 +141,9 @@ export function JobLinesUpsertModalComponent({
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
if (!!getFieldValue("mod_lbr_ty") === !!value) {
if (
!!getFieldValue("mod_lbr_ty") === (!!value || value === 0)
) {
return Promise.resolve();
}
return Promise.reject(

View File

@@ -0,0 +1,80 @@
import React, { useMemo } from "react";
import { Row, Col, Tag, Tooltip } from "antd";
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)(JobPartsQueueCount);
export function JobPartsQueueCount({ bodyshop, parts }) {
const partsStatus = useMemo(() => {
if (!parts) return null;
return parts.reduce(
(acc, val) => {
if (val.part_type === "PAS" || val.part_type === "PASL") return acc;
acc.total = acc.total + val.count;
acc[val.status] = acc[val.status] + val.count;
return acc;
},
{
total: 0,
null: 0,
[bodyshop.md_order_statuses.default_bo]: 0,
[bodyshop.md_order_statuses.default_ordered]: 0,
[bodyshop.md_order_statuses.default_received]: 0,
[bodyshop.md_order_statuses.default_returned]: 0,
}
);
}, [bodyshop, parts]);
if (!parts) return null;
return (
<Row>
<Col span={4}>
<Tooltip title="Total">
<Tag>{partsStatus.total}</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title="No Status">
<Tag color="gold">{partsStatus["null"]}</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title={bodyshop.md_order_statuses.default_ordered}>
<Tag color="blue">
{partsStatus[bodyshop.md_order_statuses.default_ordered]}
</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title={bodyshop.md_order_statuses.default_received}>
<Tag color="green">
{partsStatus[bodyshop.md_order_statuses.default_received]}
</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title={bodyshop.md_order_statuses.default_returned}>
<Tag color="orange">
{partsStatus[bodyshop.md_order_statuses.default_returned]}
</Tag>
</Tooltip>
</Col>
<Col span={4}>
<Tooltip title={bodyshop.md_order_statuses.default_bo}>
<Tag color="red">
{partsStatus[bodyshop.md_order_statuses.default_bo]}
</Tag>
</Tooltip>
</Col>
</Row>
);
}

View File

@@ -1,24 +1,23 @@
import { Button, notification } from "antd";
import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
import { Checkbox, notification, Space, Spin } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_JOB } from "../../graphql/jobs.queries";
export default function JobRemoveFromPartsQueue({ jobId, refetch }) {
export default function JobRemoveFromPartsQueue({ checked, jobId }) {
const [updateJob] = useMutation(UPDATE_JOB);
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleClick = async (e) => {
const handleChange = async (e) => {
setLoading(true);
const result = await updateJob({
variables: { jobId: jobId, job: { queued_for_parts: false } },
variables: { jobId: jobId, job: { queued_for_parts: e.target.checked } },
});
if (!!!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
if (refetch) refetch();
} else {
notification["error"]({
message: t("jobs.errors.saving", {
@@ -30,8 +29,9 @@ export default function JobRemoveFromPartsQueue({ jobId, refetch }) {
};
return (
<Button onClick={handleClick} loading={loading}>
{t("general.actions.remove")}
</Button>
<Space>
<Checkbox checked={checked} onChange={handleChange} />
{loading && <Spin size="small" />}
</Space>
);
}

View File

@@ -6,17 +6,20 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminStatus);
export function JobsAdminStatus({ bodyshop, job }) {
export function JobsAdminStatus({ insertAuditTrail, bodyshop, job }) {
const { t } = useTranslation();
const [mutationUpdateJobstatus] = useMutation(UPDATE_JOB_STATUS);
@@ -26,6 +29,10 @@ export function JobsAdminStatus({ bodyshop, job }) {
})
.then((r) => {
notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobstatuschange(status),
});
// refetch();
})
.catch((error) => {

View File

@@ -7,8 +7,27 @@ import DateTimePicker from "../form-date-time-picker/form-date-time-picker.compo
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import moment from "moment";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
export default function JobsAdminDatesChange({ job }) {
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { insertAuditTrail } from "../../redux/application/application.actions";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsAdminDatesChange);
export function JobsAdminDatesChange({ insertAuditTrail, job }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [form] = Form.useForm();
@@ -20,6 +39,23 @@ export default function JobsAdminDatesChange({ job }) {
variables: { jobId: job.id, job: values },
});
const changedAuditFields = form.getFieldsValue(
true,
(meta) => meta && meta.touched
);
Object.keys(changedAuditFields).forEach((key) => {
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobfieldchange(
key,
changedAuditFields[key] instanceof moment
? moment(changedAuditFields[key]).format("MM/DD/YYYY hh:mm a")
: changedAuditFields[key]
),
});
});
if (!!!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
} else {

View File

@@ -8,18 +8,21 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { insertAuditTrail } from "../../redux/application/application.actions";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobAdminMarkReexport);
export function JobAdminMarkReexport({ bodyshop, job }) {
export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [markJobForReexport] = useMutation(gql`
@@ -78,6 +81,10 @@ export function JobAdminMarkReexport({ bodyshop, job }) {
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobmarkforreexport(),
});
} else {
notification["error"]({
message: t("jobs.errors.saving", {
@@ -96,6 +103,10 @@ export function JobAdminMarkReexport({ bodyshop, job }) {
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_jobmarkexported(),
});
} else {
notification["error"]({
message: t("jobs.errors.saving", {

View File

@@ -4,21 +4,29 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { insertAuditTrail } from "../../redux/application/application.actions";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
insertAuditTrail: ({ jobid, operation }) =>
dispatch(insertAuditTrail({ jobid, operation })),
});
export default connect(mapStateToProps, mapDispatchToProps)(JobsAdminUnvoid);
export function JobsAdminUnvoid({ bodyshop, job, currentUser }) {
export function JobsAdminUnvoid({
insertAuditTrail,
bodyshop,
job,
currentUser,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [updateJob] = useMutation(gql`
@@ -84,6 +92,11 @@ mutation UNVOID_JOB($jobId: uuid!) {
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({
jobid: job.id,
operation: AuditTrailMapping.admin_unvoicejob(),
});
} else {
notification["error"]({
message: t("jobs.errors.saving", {

View File

@@ -15,7 +15,6 @@ import { logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { useHistory } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
@@ -27,6 +26,7 @@ export function JobsCloseExportButton({
jobId,
disabled,
setSelectedJobs,
refetch,
}) {
const history = useHistory();
const { t } = useTranslation();
@@ -46,13 +46,10 @@ export function JobsCloseExportButton({
//Check if it's a QBO Setup.
let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(
`/qbo/receivables`,
{
jobIds: [jobId],
},
);
PartnerResponse = await axios.post(`/qbo/receivables`, {
jobIds: [jobId],
elgen: true,
});
} else {
//Default is QBD
@@ -117,58 +114,64 @@ export function JobsCloseExportButton({
});
});
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: jobId,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: jobId,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
}
} else {
//Insert success export log.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: jobId,
successful: true,
useremail: currentUser.email,
},
],
},
});
const jobUpdateResponse = await updateJob({
variables: {
jobId: jobId,
job: {
status: bodyshop.md_ro_statuses.default_exported || "Exported*",
date_exported: new Date(),
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: jobId,
successful: true,
useremail: currentUser.email,
},
],
},
},
});
});
if (!jobUpdateResponse.errors) {
notification.open({
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
error: JSON.stringify(jobUpdateResponse.error),
}),
const jobUpdateResponse = await updateJob({
variables: {
jobId: jobId,
job: {
status: bodyshop.md_ro_statuses.default_exported || "Exported*",
date_exported: new Date(),
},
},
});
if (!jobUpdateResponse.errors) {
notification.open({
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
error: JSON.stringify(jobUpdateResponse.error),
}),
});
}
}
if (setSelectedJobs) {
setSelectedJobs((selectedJobs) => {
@@ -176,7 +179,7 @@ export function JobsCloseExportButton({
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
setLoading(false);
};

View File

@@ -146,7 +146,11 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>
<Collapse.Panel forceRender key="claim" header={t("menus.jobsdetail.claimdetail")}>
<Collapse.Panel
forceRender
key="claim"
header={t("menus.jobsdetail.claimdetail")}
>
<LayoutFormRow>
<Form.Item label={t("jobs.fields.loss_desc")} name="loss_desc">
<Input />
@@ -193,7 +197,8 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
</Form.Item>
</LayoutFormRow>
</Collapse.Panel>
<Collapse.Panel forceRender
<Collapse.Panel
forceRender
key="financial"
header={t("menus.jobsdetail.financials")}
>
@@ -204,7 +209,7 @@ export function JobsCreateJobsInfo({ bodyshop, form, selected }) {
<CurrencyInput min={0} />
</Form.Item>
<Form.Item label={t("jobs.fields.ded_status")} name="ded_status">
<Select>
<Select allowClear>
<Select.Option value="W">
{t("jobs.labels.deductible.waived")}
</Select.Option>

View File

@@ -97,7 +97,7 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
</Form.Item>
<Form.Item
nostyle
noStyle
shouldUpdate={(prev, cur) => prev.auto_add_ats !== cur.auto_add_ats}
>
{() => {

View File

@@ -1,5 +1,5 @@
import { FileExcelFilled, EditFilled, SyncOutlined } from "@ant-design/icons";
import { Card, Col, Row, Space, Button } from "antd";
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 { useTranslation } from "react-i18next";

View File

@@ -0,0 +1,105 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Space } from "antd";
import React, { useEffect } from "react";
import Gallery from "react-grid-gallery";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import {
getBillMedia,
getJobMedia,
toggleMediaSelected,
} from "../../redux/media/media.actions";
import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { CreateExplorerLinkForJob } from "../../utils/localmedia";
import DocumentsLocalUploadComponent from "../documents-local-upload/documents-local-upload.component";
import JobsDocumentsLocalGalleryReassign from "./jobs-documents-local-gallery.reassign.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
allMedia: selectAllMedia,
});
const mapDispatchToProps = (dispatch) => ({
getJobMedia: (id) => dispatch(getJobMedia(id)),
getBillMedia: ({ jobid, invoice_number }) => {
dispatch(getBillMedia({ jobid, invoice_number }));
},
toggleMediaSelected: ({ jobid, filename }) =>
dispatch(toggleMediaSelected({ jobid, filename })),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDocumentsLocalGallery);
export function JobsDocumentsLocalGallery({
bodyshop,
toggleMediaSelected,
getJobMedia,
getBillMedia,
allMedia,
job,
invoice_number,
vendorid,
}) {
const { t } = useTranslation();
useEffect(() => {
if (job) {
if (invoice_number) {
getBillMedia({ jobid: job.id, invoice_number });
} else {
getJobMedia(job.id);
}
}
}, [job, invoice_number, getJobMedia, getBillMedia]);
return (
<div>
<Space wrap>
<Button
onClick={() => {
if (job) {
if (invoice_number) {
getBillMedia({ jobid: job.id, invoice_number });
} else {
getJobMedia(job.id);
}
}
}}
>
<SyncOutlined />
</Button>
<a href={CreateExplorerLinkForJob({ jobid: job.id })}>
<Button>{t("documents.labels.openinexplorer")}</Button>
</a>
<JobsDocumentsLocalGalleryReassign jobid={job.id} />
</Space>
<Card>
<DocumentsLocalUploadComponent
job={job}
invoice_number={invoice_number}
vendorid={vendorid}
/>
</Card>
<Card title={t("jobs.labels.documents-images")}>
<Gallery
images={(allMedia && allMedia[job.id]) || []}
backdropClosesModal={true}
onSelectImage={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
onClickImage={(props) => {
window.open(
props.target.src,
"_blank",
"toolbar=0,location=0,menubar=0"
);
}}
/>
</Card>
</div>
);
}

View File

@@ -0,0 +1,92 @@
import { Button, Form, Popover, Space } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { getJobMedia } from "../../redux/media/media.actions";
import { selectAllMedia } from "../../redux/media/media.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import cleanAxios from "../../utils/CleanAxios";
import JobSearchSelect from "../job-search-select/job-search-select.component";
const mapStateToProps = createStructuredSelector({
allMedia: selectAllMedia,
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
getJobMedia: (id) => dispatch(getJobMedia(id)),
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(JobsDocumentsLocalGalleryReassign);
export function JobsDocumentsLocalGalleryReassign({
bodyshop,
jobid,
allMedia,
getJobMedia,
}) {
const { t } = useTranslation();
const [form] = Form.useForm();
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const handleFinish = async ({ jobid: newJobid }) => {
setLoading(true);
const selectedDocuments = allMedia[jobid].filter((m) => m.isSelected);
await cleanAxios.post(`${bodyshop.localmediaserverhttp}/jobs/move`, {
from_jobid: jobid,
jobid: newJobid,
files: selectedDocuments.map((f) => f.filename),
});
getJobMedia(jobid);
setVisible(false);
setLoading(false);
};
const popContent = (
<div>
<Form onFinish={handleFinish} layout="vertical" form={form}>
<Form.Item
label={t("documents.labels.newjobid")}
style={{ width: "20rem" }}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={"jobid"}
>
<JobSearchSelect />
</Form.Item>
</Form>
<Space>
<Button type="primary" onClick={() => form.submit()}>
{t("general.actions.submit")}
</Button>
<Button onClick={() => setVisible(false)}>
{t("general.actions.cancel")}
</Button>
</Space>
</div>
);
return (
<Popover content={popContent} visible={visible}>
<Button
//disabled={selectedImages.length < 1}
onClick={() => setVisible(true)}
loading={loading}
>
{t("documents.actions.reassign")}
</Button>
</Popover>
);
}

View File

@@ -26,6 +26,7 @@ export function JobsExportAllButton({
disabled,
loadingCallback,
completedCallback,
refetch,
}) {
const { t } = useTranslation();
const [updateJob] = useMutation(UPDATE_JOBS);
@@ -39,6 +40,7 @@ export function JobsExportAllButton({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/receivables`, {
jobIds: jobIds,
elgen: true,
});
} else {
let QbXmlResponse;
@@ -83,6 +85,7 @@ export function JobsExportAllButton({
return;
}
}
console.log("PartnerResponse", PartnerResponse);
const groupedData = _.groupBy(
PartnerResponse.data,
@@ -106,61 +109,70 @@ export function JobsExportAllButton({
});
//Call is not awaited as it is not critical to finish before proceeding.
});
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const jobUpdateResponse = await updateJob({
variables: {
jobIds: [key],
fields: {
status: bodyshop.md_ro_statuses.default_exported || "Exported*",
date_exported: new Date(),
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
},
});
});
}
} else {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
if (!jobUpdateResponse.errors) {
notification.open({
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
error: JSON.stringify(jobUpdateResponse.error),
}),
const jobUpdateResponse = await updateJob({
variables: {
jobIds: [key],
fields: {
status:
bodyshop.md_ro_statuses.default_exported || "Exported*",
date_exported: new Date(),
},
},
});
if (!jobUpdateResponse.errors) {
notification.open({
type: "success",
key: "jobsuccessexport",
message: t("jobs.successes.exported"),
});
} else {
notification["error"]({
message: t("jobs.errors.exporting", {
error: JSON.stringify(jobUpdateResponse.error),
}),
});
}
}
}
})
);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false);

View File

@@ -0,0 +1,355 @@
import {
ExclamationCircleFilled,
PauseCircleOutlined,
SyncOutlined,
} from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Grid, Input, Space, Table } from "antd";
import queryString from "query-string";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { QUERY_ALL_ACTIVE_JOBS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters";
import AlertComponent from "../alert/alert.component";
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
export function JobsReadyList({ bodyshop }) {
const searchParams = queryString.parse(useLocation().search);
const { selected } = searchParams;
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
.filter((screen) => !!screen[1])
.slice(-1)[0];
const readyStatuses = useMemo(() => {
if (bodyshop.md_ro_statuses.ready_statuses)
return bodyshop.md_ro_statuses.ready_statuses;
return bodyshop.md_ro_statuses.post_production_statuses.filter(
(s) =>
s !== bodyshop.md_ro_statuses.default_invoiced &&
s !== bodyshop.md_ro_statuses.default_exported
);
}, [bodyshop.md_ro_statuses]);
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ACTIVE_JOBS, {
variables: {
statuses: readyStatuses,
},
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const [state, setState] = useState({
sortedInfo: {},
filteredInfo: { text: "" },
});
const { t } = useTranslation();
const history = useHistory();
const [searchText, setSearchText] = useState("");
if (error) return <AlertComponent message={error.message} type="error" />;
const jobs = data
? searchText === ""
? data.jobs
: data.jobs.filter(
(j) =>
(j.ro_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_co_nm || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.comments || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.ownr_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.clm_no || "").toLowerCase().includes(searchText.toLowerCase()) ||
(j.plate_no || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_model_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(j.v_make_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
const handleTableChange = (pagination, filters, sorter) => {
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
};
const handleOnRowClick = (record) => {
if (record) {
if (record.id) {
history.push({
search: queryString.stringify({
...searchParams,
selected: record.id,
}),
});
}
}
};
const columns = [
{
title: t("jobs.fields.ro_number"),
dataIndex: "ro_number",
key: "ro_number",
sorter: (a, b) => alphaSort(a.ro_number, b.ro_number),
sortOrder:
state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order,
render: (text, record) => (
<Link
to={"/manage/jobs/" + record.id}
onClick={(e) => e.stopPropagation()}
>
<Space>
{record.ro_number || t("general.labels.na")}
{record.production_vars && record.production_vars.alert ? (
<ExclamationCircleFilled className="production-alert" />
) : null}
{record.suspended && (
<PauseCircleOutlined style={{ color: "orangered" }} />
)}
</Space>
</Link>
),
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder:
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
render: (text, record) => {
return record.owner ? (
<Link
to={"/manage/owners/" + record.owner.id}
onClick={(e) => e.stopPropagation()}
>
<OwnerNameDisplay ownerObject={record} />
</Link>
) : (
<span>
<OwnerNameDisplay ownerObject={record} />
</span>
);
},
},
{
title: t("jobs.fields.ownr_ph1"),
dataIndex: "ownr_ph1",
key: "ownr_ph1",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph1} jobid={record.id} />
),
},
{
title: t("jobs.fields.ownr_ph2"),
dataIndex: "ownr_ph2",
key: "ownr_ph2",
ellipsis: true,
responsive: ["md"],
render: (text, record) => (
<ChatOpenButton phone={record.ownr_ph2} jobid={record.id} />
),
},
{
title: t("jobs.fields.status"),
dataIndex: "status",
key: "status",
ellipsis: true,
sorter: (a, b) => alphaSort(a.status, b.status),
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
filters:
(jobs &&
jobs
.map((j) => j.status)
.filter(onlyUnique)
.map((s) => {
return {
text: s || "No Status*",
value: [s],
};
})) ||
[],
onFilter: (value, record) => value.includes(record.status),
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => {
return record.vehicleid ? (
<Link
to={"/manage/vehicles/" + record.vehicleid}
onClick={(e) => e.stopPropagation()}
>
{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}
</Link>
) : (
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
record.v_model_desc || ""
}`}</span>
);
},
},
{
title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder:
state.sortedInfo.columnKey === "plate_no" && state.sortedInfo.order,
},
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
key: "clm_no",
ellipsis: true,
responsive: ["md"],
sorter: (a, b) => alphaSort(a.clm_no, b.clm_no),
sortOrder:
state.sortedInfo.columnKey === "clm_no" && state.sortedInfo.order,
render: (text, record) =>
`${record.clm_no || ""}${
record.po_number ? ` (PO: ${record.po_number})` : ""
}`,
},
{
title: t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm",
key: "ins_co_nm",
ellipsis: true,
responsive: ["md"],
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
responsive: ["md"],
ellipsis: true,
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
render: (text, record) => (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
),
},
{
title: t("jobs.fields.comment"),
dataIndex: "comment",
key: "comment",
ellipsis: true,
responsive: ["md"],
},
// {
// title: t("jobs.fields.owner_owing"),
// dataIndex: "owner_owing",
// key: "owner_owing",
// responsive: ["md"],
// render: (text, record) => (
// <CurrencyFormatter>{record.owner_owing}</CurrencyFormatter>
// ),
// },
];
const scrollMapper = {
xs: true,
sm: true,
md: true,
lg: "100%",
xl: "100%",
xxl: "100%",
};
return (
<Card
title={t("titles.bc.jobs-ready")}
extra={
<Space wrap>
<span>({readyStatuses && readyStatuses.join(", ")})</span>
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
setSearchText(e.target.value);
}}
value={searchText}
enterButton
/>
</Space>
}
>
<Table
loading={loading}
pagination={{ defaultPageSize: 50 }}
columns={columns}
rowKey="id"
dataSource={jobs}
scroll={{
x: selectedBreakpoint ? scrollMapper[selectedBreakpoint[0]] : "100%",
}}
rowSelection={{
onSelect: (record) => {
handleOnRowClick(record);
},
selectedRowKeys: [selected],
type: "radio",
}}
onChange={handleTableChange}
onRow={(record, rowIndex) => {
return {
onClick: (event) => {
handleOnRowClick(record);
},
};
}}
/>
</Card>
);
}
export default connect(mapStateToProps, null)(JobsReadyList);

View File

@@ -24,7 +24,7 @@ export function NotesPresetButton({ bodyshop, form }) {
const menu = (
<Menu>
{bodyshop.md_notes_presets.map((i, idx) => (
<Menu.Item onClick={() => handleSelect(i)} onItemHover key={idx}>
<Menu.Item onClick={() => handleSelect(i)} key={idx}>
{i.label}
</Menu.Item>
))}

View File

@@ -0,0 +1,66 @@
import { useMutation } from "@apollo/client";
import { Checkbox, notification, Space, Spin } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { MUTATION_UPDATE_PO_CM_REECEIVED } from "../../graphql/parts-orders.queries";
export default function PartsOrderCmReceived({
checked,
orderLineId,
partsOrderId,
}) {
const [updateLine] = useMutation(MUTATION_UPDATE_PO_CM_REECEIVED);
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleChange = async (e) => {
setLoading(true);
const result = await updateLine({
variables: {
partsLineId: orderLineId,
partsOrder: { cm_received: e.target.checked },
},
update(cache) {
cache.modify({
id: cache.identify({
id: partsOrderId,
__typename: "parts_orders",
}),
fields: {
parts_order_lines(ex, { readField }) {
console.log(ex);
return ex.map((lineref) => {
if (orderLineId.id !== readField("id", lineref)) {
lineref.cm_received = e.target.checked;
}
return lineref;
});
},
},
});
},
});
if (!!!result.errors) {
notification["success"]({
message: t("parts_orders.successes.line_updated"),
});
} else {
notification["error"]({
message: t("parts_orders.errors.saving", {
error: JSON.stringify(result.errors),
}),
});
}
setLoading(false);
};
return (
<Space>
<Checkbox checked={checked} onChange={handleChange} />
{loading && <Spin size="small" />}
</Space>
);
}

View File

@@ -0,0 +1,47 @@
import React from "react";
import { Button, Popconfirm } from "antd";
import { DeleteFilled } from "@ant-design/icons";
import { useTranslation } from "react-i18next";
import { DELETE_PARTS_ORDER_LINE } from "../../graphql/parts-orders.queries";
import { useMutation } from "@apollo/client";
export default function PartsOrderDeleteLine({
disabled,
partsLineId,
partsOrderId,
}) {
const { t } = useTranslation();
const [deletePartsOrderLine] = useMutation(DELETE_PARTS_ORDER_LINE);
return (
<Popconfirm
title={t("parts_orders.labels.confirmdelete")}
disabled={disabled}
onConfirm={async () => {
//Delete the parts return.!
await deletePartsOrderLine({
variables: { partsOrderLineId: partsLineId },
update(cache) {
cache.modify({
id: cache.identify({
__typename: "parts_orders",
id: partsOrderId,
}),
fields: {
parts_order_lines(cached, { readField }) {
return cached.filter((c) => {
return readField("id", c) !== partsLineId;
});
},
},
});
},
});
}}
>
<Button disabled={disabled}>
<DeleteFilled />
</Button>
</Popconfirm>
);
}

View File

@@ -29,6 +29,8 @@ import { alphaSort } from "../../utils/sorters";
import { TemplateList } from "../../utils/TemplateConstants";
import DataLabel from "../data-label/data-label.component";
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
import PartsOrderDeleteLine from "../parts-order-delete-line/parts-order-delete-line.component";
import PartsOrderLineBackorderButton from "../parts-order-line-backorder-button/parts-order-line-backorder-button.component";
import PartsReceiveModalContainer from "../parts-receive-modal/parts-receive-modal.container";
import PrintWrapper from "../print-wrapper/print-wrapper.component";
@@ -77,6 +79,7 @@ export function PartsOrderListTableComponent({
});
const search = queryString.parse(useLocation().search);
const selectedpartsorder = search.partsorderid;
const [searchText, setSearchText] = useState("");
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
@@ -346,6 +349,23 @@ export function PartsOrderListTableComponent({
dataIndex: "status",
key: "status",
},
...(selectedPartsOrderRecord && selectedPartsOrderRecord.return
? [
{
title: t("parts_orders.fields.cm_received"),
dataIndex: "cm_received",
key: "cm_received",
render: (text, record) => (
<PartsOrderCmReceived
orderLineId={record.id}
checked={record.cm_received}
partsorderid={selectedPartsOrderRecord.id}
/>
),
},
]
: []),
{
title: t("parts_orders.fields.backordered_on"),
dataIndex: "backordered_on",
@@ -372,12 +392,21 @@ export function PartsOrderListTableComponent({
dataIndex: "actions",
key: "actions",
render: (text, record) => (
<PartsOrderLineBackorderButton
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
jobLineId={record.job_line_id}
/>
<Space wrap>
<PartsOrderDeleteLine
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
partsOrderId={selectedpartsorder}
jobLineId={record.job_line_id}
/>
<PartsOrderLineBackorderButton
disabled={jobRO}
partsOrderStatus={record.status}
partsLineId={record.id}
jobLineId={record.job_line_id}
/>
</Space>
),
},
];
@@ -403,6 +432,21 @@ export function PartsOrderListTableComponent({
);
};
const filteredPartsOrders = parts_orders
? searchText === ""
? parts_orders
: parts_orders.filter(
(b) =>
(b.order_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(b.vendor.name || "")
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
return (
<Card
title={t("parts_orders.labels.parts_orders")}
@@ -413,8 +457,10 @@ export function PartsOrderListTableComponent({
</Button>
<Input.Search
placeholder={t("general.labels.search")}
value={searchText}
onChange={(e) => {
e.preventDefault();
setSearchText(e.target.value);
}}
/>
</Space>
@@ -438,7 +484,7 @@ export function PartsOrderListTableComponent({
}}
columns={columns}
rowKey="id"
dataSource={parts_orders}
dataSource={filteredPartsOrders}
onChange={handleTableChange}
/>
</Card>

View File

@@ -305,6 +305,7 @@ export function PartsOrderModalContainer({
quantity: value.part_qty,
job_line_id: isReturn ? value.joblineid : value.id,
part_type: value.part_type,
...(isReturn && { cm_received: false }),
});
return acc;
}, [])

View File

@@ -27,6 +27,7 @@ export function PayableExportAll({
disabled,
loadingCallback,
completedCallback,
refetch,
}) {
const { t } = useTranslation();
const [updateBill] = useMutation(UPDATE_BILLS);
@@ -42,6 +43,7 @@ export function PayableExportAll({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payables`, {
bills: billids,
elgen: true,
});
} else {
let QbXmlResponse;
@@ -104,57 +106,62 @@ export function PayableExportAll({
}),
})
);
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const billUpdateResponse = await updateBill({
variables: {
billIdList: [key],
bill: {
exported: true,
exported_at: new Date(),
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
},
});
if (!!!billUpdateResponse.errors) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {
notification["error"]({
message: t("bills.errors.exporting", {
error: JSON.stringify(billUpdateResponse.error),
}),
}
} else {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const billUpdateResponse = await updateBill({
variables: {
billIdList: [key],
bill: {
exported: true,
exported_at: new Date(),
},
},
});
if (!!!billUpdateResponse.errors) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {
notification["error"]({
message: t("bills.errors.exporting", {
error: JSON.stringify(billUpdateResponse.error),
}),
});
}
}
}
})()
@@ -164,6 +171,8 @@ export function PayableExportAll({
await Promise.all(proms);
if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
setLoading(false);
};

View File

@@ -26,6 +26,7 @@ export function PayableExportButton({
disabled,
loadingCallback,
setSelectedBills,
refetch,
}) {
const { t } = useTranslation();
const [updateBill] = useMutation(UPDATE_BILLS);
@@ -43,6 +44,7 @@ export function PayableExportButton({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payables`, {
bills: [billId],
elgen: true,
});
} else {
//Default is QBD
@@ -100,64 +102,72 @@ export function PayableExportButton({
}),
})
);
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: billId,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
}
if (successfulTransactions.length > 0) {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: billId,
successful: true,
useremail: currentUser.email,
},
],
},
});
const billUpdateResponse = await updateBill({
variables: {
billIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "billid"
: "id"
]
),
bill: {
exported: true,
exported_at: new Date(),
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: billId,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
},
});
if (!!!billUpdateResponse.errors) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {
notification["error"]({
message: t("bills.errors.exporting", {
error: JSON.stringify(billUpdateResponse.error),
}),
});
}
}
if (successfulTransactions.length > 0) {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: billId,
successful: true,
useremail: currentUser.email,
},
],
},
});
const billUpdateResponse = await updateBill({
variables: {
billIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "billid"
: "id"
]
),
bill: {
exported: true,
exported_at: new Date(),
},
},
});
if (!!!billUpdateResponse.errors) {
notification.open({
type: "success",
key: "billsuccessexport",
message: t("bills.successes.exported"),
});
} else {
notification["error"]({
message: t("bills.errors.exporting", {
error: JSON.stringify(billUpdateResponse.error),
}),
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
if (setSelectedBills) {
setSelectedBills((selectedBills) => {
return selectedBills.filter((i) => i !== billId);

View File

@@ -26,6 +26,7 @@ export function PaymentExportButton({
disabled,
loadingCallback,
setSelectedPayments,
refetch,
}) {
const { t } = useTranslation();
const [updatePayment] = useMutation(UPDATE_PAYMENTS);
@@ -40,6 +41,7 @@ export function PaymentExportButton({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payments`, {
payments: [paymentId],
elgen: true,
});
} else {
//Default is QBD
@@ -100,63 +102,68 @@ export function PaymentExportButton({
}),
})
);
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: paymentId,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: paymentId,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayment({
variables: {
paymentIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "paymentid"
: "id"
]
),
payment: {
exportedat: new Date(),
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: paymentId,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
}
} else {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: paymentId,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayment({
variables: {
paymentIdList: successfulTransactions.map(
(st) =>
st[
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
? "paymentid"
: "id"
]
),
payment: {
exportedat: new Date(),
},
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
});
}
}
if (setSelectedPayments) {
@@ -165,7 +172,7 @@ export function PaymentExportButton({
});
}
}
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
if (!!loadingCallback) loadingCallback(false);
setLoading(false);
};

View File

@@ -25,6 +25,7 @@ export function PaymentsExportAllButton({
disabled,
loadingCallback,
completedCallback,
refetch
}) {
const { t } = useTranslation();
const [updatePayments] = useMutation(UPDATE_PAYMENTS);
@@ -38,6 +39,7 @@ export function PaymentsExportAllButton({
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payments`, {
payments: paymentIds,
elgen: true,
});
} else {
let QbXmlResponse;
@@ -92,54 +94,61 @@ export function PaymentsExportAllButton({
}),
})
);
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
});
} else {
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayments({
variables: {
paymentIdList: [key],
payment: {
exportedat: new Date(),
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: key,
successful: false,
message: JSON.stringify(
failedTransactions.map((ft) => ft.errorMessage)
),
useremail: currentUser.email,
},
],
},
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
}
} else {
if (!(bodyshop.accountingconfig && bodyshop.accountingconfig.qbo)) {
//QBO Logs are handled server side.
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
paymentid: key,
successful: true,
useremail: currentUser.email,
},
],
},
});
const paymentUpdateResponse = await updatePayments({
variables: {
paymentIdList: [key],
payment: {
exportedat: new Date(),
},
},
});
if (!!!paymentUpdateResponse.errors) {
notification.open({
type: "success",
key: "paymentsuccessexport",
message: t("payments.successes.exported"),
});
} else {
notification["error"]({
message: t("payments.errors.exporting", {
error: JSON.stringify(paymentUpdateResponse.error),
}),
});
}
}
}
})()
@@ -148,6 +157,7 @@ export function PaymentsExportAllButton({
await Promise.all(proms);
if (!!completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) refetch();
setLoading(false);
};

View File

@@ -1,6 +1,7 @@
import {
CalendarOutlined,
EyeFilled,
DownloadOutlined,
PauseCircleOutlined,
} from "@ant-design/icons";
import { Card, Col, Row, Space } from "antd";
@@ -14,6 +15,7 @@ import ProductionSubletsManageComponent from "../production-sublets-manage/produ
import "./production-board-card.styles.scss";
import moment from "moment";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
export default function ProductionBoardCard(
technician,
@@ -157,6 +159,16 @@ export default function ProductionBoardCard(
</Row>
</Col>
)} */}
{cardSettings && cardSettings.actual_in && card.actual_in && (
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
<Space>
<DownloadOutlined />
<DateTimeFormatter format="MM/DD">
{card.actual_in}
</DateTimeFormatter>
</Space>
</Col>
)}
{cardSettings &&
cardSettings.scheduled_completion &&
card.scheduled_completion && (
@@ -188,6 +200,11 @@ export default function ProductionBoardCard(
)}
</Col>
)}
{cardSettings && cardSettings.partsstatus && (
<Col span={24}>
<JobPartsQueueCount parts={card.joblines_status} />
</Col>
)}
</Row>
</Card>
);

View File

@@ -97,6 +97,13 @@ export default function ProductionBoardKanbanCardSettings({
>
<Switch />
</Form.Item>
<Form.Item
valuePropName="checked"
label={t("production.labels.actual_in")}
name="actual_in"
>
<Switch />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
@@ -131,6 +138,13 @@ export default function ProductionBoardKanbanCardSettings({
>
<Switch />
</Form.Item>
<Form.Item
valuePropName="checked"
label={t("production.labels.partsstatus")}
name="partsstatus"
>
<Switch />
</Form.Item>
<Form.Item
valuePropName="checked"
label={t("production.labels.stickyheader")}

View File

@@ -51,7 +51,10 @@ export function ProductionBoardKanbanComponent({
const { t } = useTranslation();
useEffect(() => {
const boardData = createBoardData(
bodyshop.md_ro_statuses.production_statuses,
[
...bodyshop.md_ro_statuses.production_statuses,
...(bodyshop.md_ro_statuses.additional_board_statuses || []),
],
data,
filter
);
@@ -61,13 +64,7 @@ export function ProductionBoardKanbanComponent({
});
setBoardLanes(boardData);
setIsMoving(false);
}, [
data,
setBoardLanes,
setIsMoving,
bodyshop.md_ro_statuses.production_statuses,
filter,
]);
}, [data, setBoardLanes, setIsMoving, bodyshop.md_ro_statuses, filter]);
const client = useApolloClient();

View File

@@ -23,6 +23,7 @@ import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.co
import ProductionListColumnComment from "./production-list-columns.comment.component";
import ProductionListColumnPartsReceived from "./production-list-columns.partsreceived.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
const r = ({ technician, state, activeStatuses, bodyshop }) => {
return [
@@ -490,6 +491,14 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
<ProductionListColumnPartsReceived record={record} />
),
},
{
title: i18n.t("jobs.fields.partsstatus"),
dataIndex: "partsstatus",
key: "partsstatus",
render: (text, record) => (
<JobPartsQueueCount parts={record.joblines_status} record={record} />
),
},
];
};
export default r;

View File

@@ -21,9 +21,11 @@ import OwnerNameDisplay from "../owner-name-display/owner-name-display.component
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import ScoreboardAddButton from "../job-scoreboard-add-button/job-scoreboard-add-button.component";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setPrintCenterContext: (context) =>
@@ -34,7 +36,11 @@ export default connect(
mapDispatchToProps
)(ProductionListDetail);
export function ProductionListDetail({ jobs, setPrintCenterContext }) {
export function ProductionListDetail({
bodyshop,
jobs,
setPrintCenterContext,
}) {
const search = queryString.parse(useLocation().search);
const history = useHistory();
const { selected } = search;
@@ -59,7 +65,7 @@ export function ProductionListDetail({ jobs, setPrintCenterContext }) {
<PageHeader
title={theJob.ro_number}
extra={
<Space>
<Space wrap>
<ProductionRemoveButton jobId={theJob.id} />{" "}
<Button
onClick={() => {
@@ -76,6 +82,7 @@ export function ProductionListDetail({ jobs, setPrintCenterContext }) {
<PrinterFilled />
{t("jobs.actions.printCenter")}
</Button>
<ScoreboardAddButton job={data ? data.jobs_by_pk : {}} />
</Space>
}
/>
@@ -142,11 +149,12 @@ export function ProductionListDetail({ jobs, setPrintCenterContext }) {
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
{!bodyshop.uselocalmediaserver && (
<JobDetailCardsDocumentsComponent
loading={loading}
data={data ? data.jobs_by_pk : null}
/>
)}
</div>
)}
</Drawer>

View File

@@ -1,13 +1,42 @@
import React from "react";
import { Dropdown, Button, Table, Space } from "antd";
import React, { useState } from "react";
import { Dropdown, Button, Table, Space, Card, Input } from "antd";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import ScoreboardRemoveButton from "../scoreboard-remove-button/scorebard-remove-button.component";
import { DateFormatter } from "../../utils/DateFormatter";
import ScoreboardEntryEdit from "../scoreboard-entry-edit/scoreboard-entry-edit.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
export default function ScoreboardJobsList({ scoreBoardlist }) {
const { t } = useTranslation();
const [searchText, setSearchText] = useState("");
const jobs = scoreBoardlist
? searchText === ""
? scoreBoardlist
: scoreBoardlist.filter(
(sb) =>
(sb.job.ro_number || "")
.toString()
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.ownr_co_nm || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.ownr_fn || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.ownr_ln || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.v_model_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase()) ||
(sb.job.v_make_desc || "")
.toLowerCase()
.includes(searchText.toLowerCase())
)
: [];
const columns = [
{
@@ -20,7 +49,25 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
</Link>
),
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
ellipsis: true,
render: (text, record) => <OwnerNameDisplay ownerObject={record.job} />,
},
{
title: t("jobs.fields.vehicle"),
dataIndex: "vehicle",
key: "vehicle",
ellipsis: true,
render: (text, record) => (
<span>{`${record.job.v_model_yr || ""} ${
record.job.v_make_desc || ""
} ${record.job.v_model_desc || ""}`}</span>
),
},
{
title: t("scoreboard.fields.date"),
dataIndex: "date",
@@ -51,17 +98,29 @@ export default function ScoreboardJobsList({ scoreBoardlist }) {
];
const overlay = (
<div style={{ width: "50vw", padding: "1rem" }}>
<Card
style={{ maxWidth: "90vw", padding: "1rem" }}
onClick={(e) => e.stopPropagation()}
extra={
<Input.Search
placeholder={t("general.labels.search")}
onChange={(e) => {
setSearchText(e.target.value);
}}
value={searchText}
enterButton
onClick={(e) => e.stopPropagation()}
/>
}
>
<Table
pagination={false}
columns={columns}
rowKey="id"
dataSource={scoreBoardlist}
scroll={{ x: true, y: "15rem" }}
style={{ padding: "1rem" }}
dataSource={jobs}
onClick={(e) => e.stopPropagation()}
/>
</div>
</Card>
);
return (

View File

@@ -25,10 +25,6 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
const values = useMemo(() => {
const dateHash = _.groupBy(scoreBoardlist, "date");
console.log(
"🚀 ~ file: scoreboard-targets-table.component.jsx ~ line 31 ~ values ~ dateHash",
dateHash
);
let ret = {
todayBody: 0,
@@ -71,10 +67,6 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
return ret;
}, [scoreBoardlist]);
console.log(
"🚀 ~ file: scoreboard-targets-table.component.jsx ~ line 51 ~ values ~ values",
values
);
return (
<Card

View File

@@ -328,6 +328,12 @@ export function ShopEmployeesFormComponent({ bodyshop }) {
>
<Input />
</Form.Item>
<Form.Item
label={t("employees.fields.external_id")}
name="external_id"
>
<Input />
</Form.Item>
</LayoutFormRow>
<Form.List name={["rates"]}>
{(fields, { add, remove, move }) => {

View File

@@ -584,6 +584,25 @@ export default function ShopInfoGeneral({ form }) {
>
<Switch />
</Form.Item>
<Form.Item
name={["uselocalmediaserver"]}
label={t("bodyshop.fields.uselocalmediaserver")}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name={["localmediaserverhttp"]}
label={t("bodyshop.fields.localmediaserverhttp")}
>
<Input />
</Form.Item>
<Form.Item
name={["localmediaservernetwork"]}
label={t("bodyshop.fields.localmediaservernetwork")}
>
<Input />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
<Form.List name={["md_messaging_presets"]}>
@@ -1393,6 +1412,60 @@ export default function ShopInfoGeneral({ form }) {
}}
</Form.List>
</LayoutFormRow>
<LayoutFormRow grow header={t("bodyshop.labels.md_to_emails")}>
<Form.List name={["md_to_emails"]}>
{(fields, { add, remove, move }) => {
return (
<div>
{fields.map((field, index) => (
<Form.Item key={field.key}>
<LayoutFormRow noDivider>
<Form.Item
label={t("general.labels.label")}
key={`${index}label`}
name={[field.name, "label"]}
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.labels.md_to_emails_emails")}
key={`${index}emails`}
name={[field.name, "emails"]}
>
<Select mode="tags" tokenSeparators={[",", ";"]} />
</Form.Item>
<Space>
<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("general.actions.add")}
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
</LayoutFormRow>
</div>
);
}

View File

@@ -165,6 +165,18 @@ export default function ShopInfoRbacComponent({ form }) {
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.list-ready")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "jobs:list-ready"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.admin")}
rules={[

View File

@@ -134,6 +134,44 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
))}
</Select>
</Form.Item>
<Form.Item
name={["md_ro_statuses", "ready_statuses"]}
label={t("bodyshop.fields.statuses.ready_statuses")}
rules={[
{
//required: true,
//message: t("general.validation.required"),
type: "array",
},
]}
>
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
name={["md_ro_statuses", "additional_board_statuses"]}
label={t("bodyshop.fields.statuses.additional_board_statuses")}
rules={[
{
//required: true,
//message: t("general.validation.required"),
type: "array",
},
]}
>
<Select mode="multiple">
{options.map((item, idx) => (
<Select.Option key={idx} value={item}>
{item}
</Select.Option>
))}
</Select>
</Form.Item>
<LayoutFormRow noDivider>
<Form.Item
label={t("bodyshop.fields.statuses.default_scheduled")}

View File

@@ -237,7 +237,11 @@ export function TimeTicketModalComponent({
return Promise.reject(
t("timetickets.validation.clockoffwithoutclockon")
);
if (value && !value.isSameOrAfter(clockon))
if (
value &&
value.isSameOrAfter &&
!value.isSameOrAfter(clockon)
)
return Promise.reject(
t("timetickets.validation.clockoffmustbeafterclockon")
);

View File

@@ -0,0 +1,42 @@
import { Button } from "antd";
import queryString from "query-string";
import React, { useState } from "react";
import { useLocation } from "react-router-dom";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import { useTranslation } from "react-i18next";
import moment from "moment";
const AttendanceCsv = TemplateList("special").attendance_detail_csv;
export default function TimeTicketsAttendanceTable() {
const searchParams = queryString.parse(useLocation().search);
const { start, end } = searchParams;
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
await GenerateDocument(
{
name: AttendanceCsv.key,
variables: {
start: start
? start
: moment().startOf("week").subtract(7, "days").format("YYYY-MM-DD"),
end: end ? end : moment().endOf("week").format("YYYY-MM-DD"),
},
},
{},
"text"
);
setLoading(false);
};
return (
<Button loading={loading} onClick={handleClick}>
{t("printcenter.special.attendance_detail_csv")}
</Button>
);
}

View File

@@ -86,6 +86,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
job_line_id
part_type
cost
cm_received
jobline {
id
part_type
@@ -124,7 +125,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
applicable_taxes
deductedfromlbr
lbr_adjustment
jobline{
jobline {
oem_partno
part_type
}
@@ -164,7 +165,7 @@ export const QUERY_BILL_BY_PK = gql`
cost_center
quantity
joblineid
jobline{
jobline {
oem_partno
part_type
}

View File

@@ -106,6 +106,10 @@ export const QUERY_BODYSHOP = gql`
last_name_first
md_parts_order_comment
bill_allow_post_to_closed
md_to_emails
uselocalmediaserver
localmediaserverhttp
localmediaservernetwork
employees {
user_email
id
@@ -114,6 +118,7 @@ export const QUERY_BODYSHOP = gql`
last_name
employee_number
rates
external_id
}
}
}
@@ -209,6 +214,10 @@ export const UPDATE_SHOP = gql`
last_name_first
md_parts_order_comment
bill_allow_post_to_closed
md_to_emails
uselocalmediaserver
localmediaserverhttp
localmediaservernetwork
employees {
id
first_name
@@ -217,6 +226,7 @@ export const UPDATE_SHOP = gql`
employee_number
rates
user_email
external_id
}
}
}

View File

@@ -25,6 +25,7 @@ export const QUERY_EMPLOYEE_BY_ID = gql`
rates
pin
user_email
external_id
employee_vacations(order_by: { start: desc }) {
id
start

View File

@@ -50,25 +50,13 @@ export const QUERY_PARTS_QUEUE = gql`
$limit: Int
$order: [jobs_order_by!]
) {
jobs_aggregate(
where: {
_and: [
{ status: { _in: $statuses } }
{ queued_for_parts: { _eq: true } }
]
}
) {
jobs_aggregate(where: { _and: [{ status: { _in: $statuses } }] }) {
aggregate {
count(distinct: true)
}
}
jobs(
where: {
_and: [
{ status: { _in: $statuses } }
{ queued_for_parts: { _eq: true } }
]
}
where: { _and: [{ status: { _in: $statuses } }] }
offset: $offset
limit: $limit
order_by: $order
@@ -99,6 +87,12 @@ export const QUERY_PARTS_QUEUE = gql`
updated_at
vehicleid
ownerid
queued_for_parts
joblines_status {
count
part_type
status
}
}
}
`;
@@ -851,6 +845,11 @@ export const QUERY_JOB_CARD_DETAILS = gql`
count
status
}
joblines {
id
mod_lbr_ty
mod_lb_hrs
}
owner {
id
allow_text_message
@@ -1050,6 +1049,7 @@ export const UPDATE_JOB = gql`
production_vars
lbr_adjustments
suspended
queued_for_parts
}
}
}

View File

@@ -72,7 +72,7 @@ export const QUERY_PARTS_ORDER_OEC = gql`
part_type
}
job {
bodyshop{
bodyshop {
shopname
bill_tax_rates
}
@@ -292,6 +292,30 @@ export const DELETE_PARTS_ORDER = gql`
}
`;
export const DELETE_PARTS_ORDER_LINE = gql`
mutation DELETE_PARTS_ORDER_LINE($partsOrderLineId: uuid!) {
delete_parts_order_lines_by_pk(id: $partsOrderLineId) {
id
}
}
`;
export const MUTATION_UPDATE_PO_CM_REECEIVED = gql`
mutation MUTATION_UPDATE_PO_CM_REECEIVED(
$partsLineId: uuid!
$partsOrder: parts_order_lines_set_input
) {
update_parts_order_lines(
where: { id: { _eq: $partsLineId } }
_set: $partsOrder
) {
returning {
id
cm_received
}
}
}
`;
export const MUTATION_UPDATE_BO_ETA = gql`
mutation MUTATION_UPDATE_BO_ETA(
$partsLineId: uuid!
@@ -339,3 +363,36 @@ export const MUTATION_BACKORDER_PART_LINE = gql`
}
}
`;
export const QUERY_UNRECEIVED_LINES = gql`
query QUERY_UNRECEIVED_LINES($jobId: uuid!, $vendorId: uuid!) {
parts_order_lines(
where: {
parts_order: { jobid: { _eq: $jobId }, vendorid: { _eq: $vendorId } }
cm_received: { _neq: true }
}
) {
cm_received
id
line_desc
quantity
act_price
cost
oem_partno
}
}
`;
export const MUTATION_MARK_RETURN_RECEIVED = gql`
mutation MUTATION_MARK_RETURN_RECEIVED($partsLineIds: [uuid!]!) {
update_parts_order_lines(
where: { id: { _in: $partsLineIds } }
_set: { cm_received: true }
) {
returning {
id
cm_received
}
}
}
`;

View File

@@ -13,6 +13,12 @@ export const SUBSCRIPTION_SCOREBOARD = gql`
job {
id
ro_number
ownr_fn
ownr_ln
ownr_co_nm
v_make_desc
v_model_desc
v_model_yr
}
}
}

View File

@@ -1,9 +1,9 @@
import React from 'react';
import { Button } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import QueueAnim from 'rc-queue-anim';
import TweenOne from 'rc-tween-one';
import { isImg } from './utils';
import React from "react";
import { Button, Space } from "antd";
import { DownOutlined } from "@ant-design/icons";
import QueueAnim from "rc-queue-anim";
import TweenOne from "rc-tween-one";
import { isImg } from "./utils";
class Banner extends React.PureComponent {
render() {
@@ -15,12 +15,12 @@ class Banner extends React.PureComponent {
<div {...currentProps} {...dataSource.wrapper}>
<QueueAnim
key="QueueAnim"
type={['bottom', 'top']}
type={["bottom", "top"]}
delay={200}
{...dataSource.textWrapper}
>
<div key="title" {...dataSource.title}>
{typeof dataSource.title.children === 'string' &&
{typeof dataSource.title.children === "string" &&
dataSource.title.children.match(isImg) ? (
<img src={dataSource.title.children} width="100%" alt="img" />
) : (
@@ -30,13 +30,18 @@ class Banner extends React.PureComponent {
<div key="content" {...dataSource.content}>
{dataSource.content.children}
</div>
<Button ghost key="button" {...dataSource.button}>
{dataSource.button.children}
</Button>
<Space wrap>
<Button ghost key="button" {...dataSource.button}>
{dataSource.button.children}
</Button>
<Button type="primary" key="button2" {...dataSource.button2}>
{dataSource.button2.children}
</Button>
</Space>
</QueueAnim>
<TweenOne
animation={{
y: '-=20',
y: "-=20",
yoyo: true,
repeat: -1,
duration: 1000,

View File

@@ -74,26 +74,26 @@ export const Nav00DataSource = {
// },
// ],
// },
{
name: "item1",
className: "header0-item",
children: {
href: "#",
children: [
{ children: i18n.t("landing.labels.features"), name: "text" },
],
},
},
{
name: "item2",
className: "header0-item",
children: {
href: "#",
children: [
{ children: i18n.t("landing.labels.pricing"), name: "text" },
],
},
},
// {
// name: "item1",
// className: "header0-item",
// children: {
// href: "#",
// children: [
// { children: i18n.t("landing.labels.features"), name: "text" },
// ],
// },
// },
// {
// name: "item2",
// className: "header0-item",
// children: {
// href: "#",
// children: [
// { children: i18n.t("landing.labels.pricing"), name: "text" },
// ],
// },
// },
{
name: "item3",
className: "header0-item",
@@ -124,7 +124,12 @@ export const Banner00DataSource = {
button: {
className: "banner0-button",
children: i18n.t("landing.hero.button"),
href: "https://imexsystems.ca",
href: "https://imexsystems.ca/products/imex-online",
},
button2: {
className: "banner0-button2",
children: i18n.t("landing.labels.managemyshop"),
href: "/signin",
},
};
export const Content40DataSource = {
@@ -1033,22 +1038,22 @@ export const Footer10DataSource = {
childWrapper: {
children: [
{
href: "/privacy",
href: "https://imexsystems.ca/privacy",
name: "link0",
children: i18n.t("landing.footer.company.privacypolicy"),
},
{
href: "/about",
name: "link1",
children: i18n.t("landing.footer.company.about"),
},
// {
// href: "/about",
// name: "link1",
// children: i18n.t("landing.footer.company.about"),
// },
{
href: "/disclaimer",
name: "link2",
children: i18n.t("landing.footer.company.disclaimers"),
},
{
href: "https://thinkimex.com",
href: "https://imexsystems.ca/schedule-a-demo/",
name: "link3",
children: i18n.t("landing.footer.company.contact"),
},
@@ -1063,7 +1068,7 @@ export const Footer10DataSource = {
className: "copyright",
children: (
<span>
©2021 <a href="http://imexsystems.ca">ImEX Systems</a> used under
©2022 <a href="http://imexsystems.ca">ImEX Systems</a> used under
license.
</span>
),

View File

@@ -4,27 +4,27 @@ import { enquireScreen } from "enquire-js";
import React from "react";
import Banner0 from "./Banner0";
// import Content4 from "./Content4";
import Content0 from "./Content0";
import Content1 from "./Content1";
//import Content0 from "./Content0";
//import Content1 from "./Content1";
import {
Banner00DataSource,
// Content40DataSource,
Content00DataSource,
Content10DataSource,
//Content00DataSource,
//Content10DataSource,
// Pricing11DataSource,
// Content30DataSource,
// Content120DataSource,
Footer10DataSource,
Nav00DataSource,
Pricing20DataSource,
// Nav00DataSource,
//Pricing20DataSource,
} from "./data.source";
// import Pricing1 from "./Pricing1";
// import Content3 from "./Content3";
// import Content12 from "./Content12";
import Footer1 from "./Footer1";
import "./less/antMotionStyle.less";
import Nav0 from "./Nav0";
import Pricing2 from "./Pricing2";
// import Nav0 from "./Nav0";
//import Pricing2 from "./Pricing2";
let isMobile;
enquireScreen((b) => {
@@ -62,64 +62,64 @@ export default class Home extends React.Component {
render() {
const children = [
<Nav0
id="Nav0_0"
key="Nav0_0"
dataSource={Nav00DataSource}
isMobile={this.state.isMobile}
/>,
// <Nav0
// id="Nav0_0"
// key="Nav0_0"
// dataSource={Nav00DataSource}
// isMobile={this.state.isMobile}
// />,
<Banner0
id="Banner0_0"
key="Banner0_0"
dataSource={Banner00DataSource}
isMobile={this.state.isMobile}
/>,
...(process.env.NODE_ENV !== "production"
? [
// <Content4
// id="Content4_0"
// key="Content4_0"
// dataSource={Content40DataSource}
// isMobile={this.state.isMobile}
// />,
<Content1
id="Content1_0"
key="Content1_0"
dataSource={Content10DataSource}
isMobile={this.state.isMobile}
/>,
<Content0
id="Content0_0"
key="Content0_0"
dataSource={Content00DataSource}
isMobile={this.state.isMobile}
/>,
<Pricing2
id="Pricing2_0"
key="Pricing2_0"
dataSource={Pricing20DataSource}
isMobile={this.state.isMobile}
/>,
// <Pricing1
// id="Pricing1_1"
// key="Pricing1_1"
// dataSource={Pricing11DataSource}
// isMobile={this.state.isMobile}
// />,
// <Content3
// id="Content3_0"
// key="Content3_0"
// dataSource={Content30DataSource}
// isMobile={this.state.isMobile}
// />,
// <Content12
// id="Content12_0"
// key="Content12_0"
// dataSource={Content120DataSource}
// isMobile={this.state.isMobile}
// />,
]
: []),
// ...(process.env.NODE_ENV !== "production"
// ? [
// // <Content4
// // id="Content4_0"
// // key="Content4_0"
// // dataSource={Content40DataSource}
// // isMobile={this.state.isMobile}
// // />,
// <Content1
// id="Content1_0"
// key="Content1_0"
// dataSource={Content10DataSource}
// isMobile={this.state.isMobile}
// />,
// <Content0
// id="Content0_0"
// key="Content0_0"
// dataSource={Content00DataSource}
// isMobile={this.state.isMobile}
// />,
// <Pricing2
// id="Pricing2_0"
// key="Pricing2_0"
// dataSource={Pricing20DataSource}
// isMobile={this.state.isMobile}
// />,
// // <Pricing1
// // id="Pricing1_1"
// // key="Pricing1_1"
// // dataSource={Pricing11DataSource}
// // isMobile={this.state.isMobile}
// // />,
// // <Content3
// // id="Content3_0"
// // key="Content3_0"
// // dataSource={Content30DataSource}
// // isMobile={this.state.isMobile}
// // />,
// // <Content12
// // id="Content12_0"
// // key="Content12_0"
// // dataSource={Content120DataSource}
// // isMobile={this.state.isMobile}
// // />,
// ]
// : []),
<Footer1
id="Footer1_0"
key="Footer1_0"

View File

@@ -6,7 +6,7 @@
position: relative;
text-align: center;
border-color: #666;
background-image: url("../../assets/banner1.jpeg");
background-image: url("../../assets/banner4.jpg");
background-size: cover;
background-attachment: fixed;
background-position: center;
@@ -60,6 +60,28 @@
width: auto;
}
}
& &-button2 {
border: 1px solid #fff;
color: #fff;
// background: transparent;
box-shadow: 0 0 0 transparent;
font-size: 16px;
height: 40px;
transition: background 0.45s @ease-out, box-shadow 0.45s @ease-out;
&:hover {
color: #fff;
border-color: #fff;
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 0 10px rgba(50, 250, 255, 0.75);
}
&:focus {
color: #fff;
border-color: #fff;
}
&.queue-anim-leaving {
width: auto;
}
}
& &-icon {
bottom: 20px;
font-size: 24px;

View File

@@ -45,7 +45,7 @@ export function AccountingPayablesContainer({
checkPartnerStatus(bodyshop, true);
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
const { loading, error, data } = useQuery(QUERY_BILLS_FOR_EXPORT, {
const { loading, error, data, refetch } = useQuery(QUERY_BILLS_FOR_EXPORT, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
@@ -73,6 +73,7 @@ export function AccountingPayablesContainer({
<AccountingPayablesTable
loadaing={loading}
bills={data ? data.bills : []}
refetch={refetch}
/>
</RbacWrapper>
</div>

View File

@@ -44,10 +44,13 @@ export function AccountingPaymentsContainer({
checkPartnerStatus(bodyshop, true);
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
const { loading, error, data } = useQuery(QUERY_PAYMENTS_FOR_EXPORT, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
});
const { loading, error, data, refetch } = useQuery(
QUERY_PAYMENTS_FOR_EXPORT,
{
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
}
);
if (error) return <AlertComponent message={error.message} type="error" />;
const noPath =
@@ -70,6 +73,7 @@ export function AccountingPaymentsContainer({
<AccountingPaymentsTable
loadaing={loading}
payments={data ? data.payments : []}
refetch={refetch}
/>
</RbacWrapper>
</div>

View File

@@ -44,7 +44,7 @@ export function AccountingReceivablesContainer({
checkPartnerStatus(bodyshop, true);
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
const { loading, error, data } = useQuery(QUERY_JOBS_FOR_EXPORT, {
const { loading, error, data, refetch } = useQuery(QUERY_JOBS_FOR_EXPORT, {
variables: {
invoicedStatus: bodyshop.md_ro_statuses.default_invoiced || "Invoiced*",
},
@@ -75,6 +75,7 @@ export function AccountingReceivablesContainer({
<AccountingReceivablesTable
loadaing={loading}
jobs={data ? data.jobs : []}
refetch={refetch}
/>
</RbacWrapper>
</div>

View File

@@ -6,6 +6,7 @@ import Icon, {
PrinterFilled,
ToolFilled,
HistoryOutlined,
SyncOutlined,
} from "@ant-design/icons";
import {
Button,
@@ -49,6 +50,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import JobAuditTrail from "../../components/job-audit-trail/job-audit-trail.component";
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";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -61,6 +63,7 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(insertAuditTrail({ jobid, operation })),
});
export function JobsDetailPage({
bodyshop,
setPrintCenterContext,
jobRO,
job,
@@ -178,6 +181,15 @@ export function JobsDetailPage({
const menuExtra = (
<Space wrap>
<Button
onClick={() => {
refetch();
}}
key="refresh"
>
<SyncOutlined />
{t("general.labels.refresh")}
</Button>
<JobsChangeStatus job={job} />
<JobSyncButton job={job} />
<Button
@@ -334,7 +346,11 @@ export function JobsDetailPage({
}
key="documents"
>
<JobsDocumentsGalleryContainer jobId={job.id} />
{bodyshop.uselocalmediaserver ? (
<JobsDocumentsLocalGallery job={job} />
) : (
<JobsDocumentsGalleryContainer jobId={job.id} />
)}
</Tabs.TabPane>
<Tabs.TabPane
tab={

View File

@@ -0,0 +1,36 @@
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import JobDetailCards from "../../components/job-detail-cards/job-detail-cards.component";
import JobsReadyList from "../../components/jobs-ready-list/jobs-ready-list.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
const mapDispatchToProps = (dispatch) => ({
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
});
export function JobsReadyPage({ setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.readyjobs");
setSelectedHeader("readyjobs");
setBreadcrumbs([
{ link: "/manage/jobs", label: t("titles.bc.jobs-ready") },
]);
}, [t, setBreadcrumbs, setSelectedHeader]);
return (
<RbacWrapper action="jobs:list-ready">
<JobsReadyList />
<JobDetailCards />
</RbacWrapper>
);
}
export default connect(null, mapDispatchToProps)(JobsReadyPage);

View File

@@ -127,6 +127,7 @@ const AccountingPayments = lazy(() =>
import("../accounting-payments/accounting-payments.container")
);
const AllJobs = lazy(() => import("../jobs-all/jobs-all.container"));
const ReadyJobs = lazy(() => import("../jobs-ready/jobs-ready.page"));
const JobsClose = lazy(() => import("../jobs-close/jobs-close.container"));
const JobsAdmin = lazy(() => import("../jobs-admin/jobs-admin.page"));
const TempDocs = lazy(() =>
@@ -240,6 +241,7 @@ export function Manage({ match, conflict, bodyshop }) {
component={JobsAdmin}
/>
<Route exact path={`${match.path}/jobs/all`} component={AllJobs} />
<Route exact path={`${match.path}/jobs/ready`} component={ReadyJobs} />
<Route
exact
path={`${match.path}/jobs/new`}

View File

@@ -1,23 +1,22 @@
import { SyncOutlined } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Input, Space, Table } from "antd";
import _ from "lodash";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link, useHistory } from "react-router-dom";
import { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import AlertComponent from "../../components/alert/alert.component";
import JobPartsQueueCount from "../../components/job-parts-queue-count/job-parts-queue-count.component";
import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { useLocation } from "react-router-dom";
import queryString from "query-string";
import _ from "lodash";
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
import { alphaSort, dateSort } from "../../utils/sorters";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -25,20 +24,25 @@ const mapStateToProps = createStructuredSelector({
export function PartsQueuePageComponent({ bodyshop }) {
const searchParams = queryString.parse(useLocation().search);
const { page, sortcolumn, sortorder, statusFilters } = searchParams;
const {
//page,
sortcolumn,
sortorder,
statusFilters,
} = searchParams;
const history = useHistory();
const { loading, error, data, refetch } = useQuery(QUERY_PARTS_QUEUE, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
variables: {
offset: page ? (page - 1) * 25 : 0,
limit: 25,
// offset: page ? (page - 1) * 25 : 0,
// limit: 25,
statuses: (statusFilters && JSON.parse(statusFilters)) ||
bodyshop.md_ro_statuses.active_statuses || ["Open", "Open*"],
order: [
{
[sortcolumn || "updated_at"]: sortorder
[sortcolumn || "ro_number"]: sortorder
? sortorder === "descend"
? "desc"
: "asc"
@@ -85,7 +89,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
: [];
const handleTableChange = (pagination, filters, sorter) => {
searchParams.page = pagination.current;
// searchParams.page = pagination.current;
searchParams.sortcolumn = sorter.columnKey;
searchParams.sortorder = sorter.order;
if (filters.status) {
@@ -114,10 +118,10 @@ export function PartsQueuePageComponent({ bodyshop }) {
},
{
title: t("jobs.fields.owner"),
dataIndex: "owner",
key: "owner",
// sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
// sortOrder: sortcolumn === "owner" && sortorder,
dataIndex: "ownr_ln",
key: "ownr_ln",
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
sortOrder: sortcolumn === "ownr_ln" && sortorder,
render: (text, record) => {
return record.ownerid ? (
<Link to={"/manage/owners/" + record.ownerid}>
@@ -173,16 +177,16 @@ export function PartsQueuePageComponent({ bodyshop }) {
);
},
},
{
title: t("vehicles.fields.plate_no"),
dataIndex: "plate_no",
key: "plate_no",
sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
sortOrder: sortcolumn === "plate_no" && sortorder,
render: (text, record) => {
return record.plate_no ? record.plate_no : "";
},
},
// {
// title: t("vehicles.fields.plate_no"),
// dataIndex: "plate_no",
// key: "plate_no",
// sorter: (a, b) => alphaSort(a.plate_no, b.plate_no),
// sortOrder: sortcolumn === "plate_no" && sortorder,
// render: (text, record) => {
// return record.plate_no ? record.plate_no : "";
// },
// },
{
title: t("jobs.fields.clm_no"),
dataIndex: "clm_no",
@@ -198,34 +202,49 @@ export function PartsQueuePageComponent({ bodyshop }) {
);
},
},
{
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder: sortcolumn === "clm_total" && sortorder,
render: (text, record) => {
return record.clm_total ? (
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
) : (
t("general.labels.unknown")
);
},
},
// {
// title: t("jobs.fields.clm_total"),
// dataIndex: "clm_total",
// key: "clm_total",
// sorter: (a, b) => a.clm_total - b.clm_total,
// sortOrder: sortcolumn === "clm_total" && sortorder,
// render: (text, record) => {
// return record.clm_total ? (
// <CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
// ) : (
// t("general.labels.unknown")
// );
// },
// },
{
title: t("jobs.fields.updated_at"),
dataIndex: "updated_at",
key: "updated_at",
sorter: (a, b) => dateSort(a.updated_at, b.updated_at),
sortOrder: sortcolumn === "updated_at" && sortorder,
render: (text, record) => (
<TimeAgoFormatter>{record.updated_at}</TimeAgoFormatter>
),
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
title: t("jobs.fields.partsstatus"),
dataIndex: "partsstatus",
key: "partsstatus",
render: (text, record) => (
<JobRemoveFromPartsQueue jobId={record.id} refetch={refetch} />
<JobPartsQueueCount parts={record.joblines_status} />
),
},
{
title: t("jobs.fields.queued_for_parts"),
dataIndex: "queued_for_parts",
key: "queued_for_parts",
sorter: (a, b) => a.queued_for_parts - b.queued_for_parts,
sortOrder: sortcolumn === "queued_for_parts" && sortorder,
render: (text, record) => (
<JobRemoveFromPartsQueue
checked={record.queued_for_parts}
jobId={record.id}
/>
),
},
];
@@ -253,9 +272,9 @@ export function PartsQueuePageComponent({ bodyshop }) {
loading={loading}
pagination={{
position: "top",
pageSize: 25,
current: parseInt(page || 1),
total: data && data.jobs_aggregate.aggregate.count,
pageSize: 50,
// current: parseInt(page || 1),
// total: data && data.jobs_aggregate.aggregate.count,
}}
columns={columns}
rowKey="id"

View File

@@ -5,15 +5,34 @@ import JobsDocumentsComponent from "../../components/jobs-documents-gallery/jobs
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import { QUERY_TEMPORARY_DOCS } from "../../graphql/documents.queries";
export default function TemporaryDocsComponent() {
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import JobsDocumentsLocalGallery from "../../components/jobs-documents-local-gallery/jobs-documents-local-gallery.container";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(TemporaryDocsComponent);
export function TemporaryDocsComponent({ bodyshop }) {
const { loading, error, data, refetch } = useQuery(QUERY_TEMPORARY_DOCS, {
fetchPolicy: "network-only",
nextFetchPolicy: "network-only",
skip: bodyshop.uselocalmediaserver,
});
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
if (bodyshop.uselocalmediaserver) {
return <JobsDocumentsLocalGallery job={{ id: "temporary" }} />;
}
return (
<JobsDocumentsComponent
data={data ? data.documents : []}

View File

@@ -14,6 +14,7 @@ import TimeTicketList from "../../components/time-ticket-list/time-ticket-list.c
import TimeTicketsPayrollTable from "../../components/time-tickets-payroll-table/time-tickets-payroll-table.component";
import TimeTicketsSummaryEmployees from "../../components/time-tickets-summary-employees/time-tickets-summary-employees.component";
import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries";
import TimeTicketsAttendanceTable from "../../components/time-tickets-attendance-table/time-tickets-attendance-table.component";
import {
setBreadcrumbs,
setSelectedHeader,
@@ -71,6 +72,7 @@ export function TimeTicketsContainer({
timetickets={data ? data.timetickets : []}
extra={
<Space wrap>
<TimeTicketsAttendanceTable />
<TimeTicketsPayrollTable />
<TimeTicketsDatesSelector />
</Space>

View File

@@ -0,0 +1,34 @@
import MediaActionTypes from "./media.types";
export const getJobMedia = (jobid) => ({
type: MediaActionTypes.GET_MEDIA_FOR_JOB,
payload: jobid,
});
export const getBillMedia = ({ jobid, invoice_number }) => {
console.log("in the action");
return {
type: MediaActionTypes.GET_MEDIA_FOR_BILL,
payload: { jobid, invoice_number },
};
};
export const setJobMedia = ({ jobid, media }) => ({
type: MediaActionTypes.SET_MEDIA_FOR_JOB,
payload: { jobid, media },
});
export const addMediaForJob = ({ jobid, media }) => ({
type: MediaActionTypes.ADD_MEDIA_FOR_JOB,
payload: { jobid, media },
});
export const getJobMediaError = ({ error, message }) => ({
type: MediaActionTypes.GET_MEDIA_FOR_JOB_ERROR,
payload: { error, message },
});
export const toggleMediaSelected = ({ jobid, filename }) => ({
type: MediaActionTypes.TOGGLE_MEDIA_SELECTED,
payload: { jobid, filename },
});

View File

@@ -0,0 +1,34 @@
import MediaActionTypes from "./media.types";
const INITIAL_STATE = { error: null };
const mediaReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case MediaActionTypes.SET_MEDIA_FOR_JOB:
return { ...state, [action.payload.jobid]: action.payload.media };
case MediaActionTypes.GET_MEDIA_FOR_JOB_ERROR:
return { ...state, error: action.payload };
case MediaActionTypes.ADD_MEDIA_FOR_JOB:
return {
...state,
[action.payload.jobid]: [
...(state[action.payload.jobid] ? state[action.payload.jobid] : []),
...(action.payload.media || []),
],
};
case MediaActionTypes.TOGGLE_MEDIA_SELECTED:
return {
...state,
[action.payload.jobid]: state[action.payload.jobid].map((p) => {
if (p.filename === action.payload.filename) {
p.isSelected = !p.isSelected;
}
return p;
}),
};
default:
return state;
}
};
export default mediaReducer;

View File

@@ -0,0 +1,108 @@
import { all, call, takeLatest, put, select } from "redux-saga/effects";
import { getJobMediaError, setJobMedia } from "./media.actions";
import MediaActionTypes from "./media.types";
import cleanAxios from "../../utils/CleanAxios";
import normalizeUrl from "normalize-url";
export function* onSetJobMedia() {
yield takeLatest(MediaActionTypes.GET_MEDIA_FOR_JOB, getJobMedia);
}
export function* getJobMedia({ payload: jobid }) {
try {
const localmediaserverhttp = (yield select(
(state) => state.user.bodyshop.localmediaserverhttp
)).trim();
if (localmediaserverhttp && localmediaserverhttp !== "") {
const imagesFetch = yield cleanAxios.post(
`${localmediaserverhttp}/jobs/list`,
{
jobid,
}
);
const documentsFetch = yield cleanAxios.post(
`${localmediaserverhttp}/bills/list`,
{
jobid,
}
);
yield put(
setJobMedia({
jobid,
media: [
...imagesFetch.data.map((d, idx) => {
return {
...d,
src: normalizeUrl(`${localmediaserverhttp}/${d.src}`),
thumbnail: normalizeUrl(
`${localmediaserverhttp}/${d.thumbnail}`
),
isSelected: false,
key: idx,
};
}),
...documentsFetch.data.map((d, idx) => {
return {
...d,
src: normalizeUrl(`${localmediaserverhttp}/${d.src}`),
thumbnail: normalizeUrl(
`${localmediaserverhttp}/${d.thumbnail}`
),
isSelected: false,
key: idx,
};
}),
],
})
);
}
} catch (error) {
yield put(getJobMediaError(error));
}
}
export function* onSetBillMedia() {
yield takeLatest(MediaActionTypes.GET_MEDIA_FOR_BILL, getBillMedia);
}
export function* getBillMedia({ payload: { jobid, invoice_number } }) {
try {
const localmediaserverhttp = (yield select(
(state) => state.user.bodyshop.localmediaserverhttp
)).trim();
if (localmediaserverhttp && localmediaserverhttp !== "") {
const documentsFetch = yield cleanAxios.post(
`${localmediaserverhttp}/bills/list`,
{
jobid,
invoice_number,
}
);
yield put(
setJobMedia({
jobid,
media: [
...documentsFetch.data.map((d, idx) => {
return {
...d,
src: normalizeUrl(`${localmediaserverhttp}/${d.src}`),
thumbnail: normalizeUrl(
`${localmediaserverhttp}/${d.thumbnail}`
),
isSelected: false,
key: idx,
};
}),
],
})
);
}
} catch (error) {
yield put(getJobMediaError(error));
}
}
export function* mediaSagas() {
yield all([call(onSetJobMedia), call(onSetBillMedia)]);
}

View File

@@ -0,0 +1,5 @@
import { createSelector } from "reselect";
const selectMedia = (state) => state.media;
export const selectAllMedia = createSelector([selectMedia], (media) => media);

View File

@@ -0,0 +1,12 @@
const MediaActionTypes = {
SET_MEDIA_FOR_JOB: "SET_MEDIA_FOR_JOB",
GET_MEDIA_FOR_JOB: "GET_MEDIA_FOR_JOB",
GET_MEDIA_FOR_JOB_ERROR: "GET_MEDIA_FOR_JOB_ERROR",
ADD_MEDIA_FOR_JOB: "ADD_MEDIA_FOR_JOB",
TOGGLE_MEDIA_SELECTED: "TOGGLE_MEDIA_SELECTED",
POST_MEDIA_FOR_JOB: "POST_MEDIA_FOR_JOB",
POST_MEDIA_FOR_JOB_SUCCESS: "POST_MEDIA_FOR_JOB_SUCCESS",
POST_MEDIA_FOR_JOB_ERROR: "POST_MEDIA_FOR_JOB_ERROR",
GET_MEDIA_FOR_BILL: "GET_MEDIA_FOR_BILL",
};
export default MediaActionTypes;

View File

@@ -4,6 +4,7 @@ import storage from "redux-persist/lib/storage";
import { withReduxStateSync } from "redux-state-sync";
import applicationReducer from "./application/application.reducer";
import emailReducer from "./email/email.reducer";
import mediaReducer from "./media/media.reducer";
import messagingReducer from "./messaging/messaging.reducer";
import modalsReducer from "./modals/modals.reducer";
import techReducer from "./tech/tech.reducer";
@@ -29,6 +30,7 @@ const rootReducer = combineReducers({
modals: modalsReducer,
application: persistReducer(applicationPersistConfig, applicationReducer),
tech: techReducer,
media: mediaReducer,
});
export default withReduxStateSync(

View File

@@ -6,6 +6,7 @@ import { emailSagas } from "./email/email.sagas";
import { modalsSagas } from "./modals/modals.sagas";
import { applicationSagas } from "./application/application.sagas";
import { techSagas } from "./tech/tech.sagas";
import { mediaSagas } from "./media/media.sagas";
export default function* rootSaga() {
yield all([
@@ -15,5 +16,6 @@ export default function* rootSaga() {
call(modalsSagas),
call(applicationSagas),
call(techSagas),
call(mediaSagas),
]);
}

View File

@@ -97,7 +97,10 @@ const userReducer = (state = INITIAL_STATE, action) => {
};
case UserActionTypes.SET_SHOP_DETAILS:
return { ...state, bodyshop: action.payload };
return {
...state,
bodyshop: action.payload,
};
case UserActionTypes.SIGN_IN_FAILURE:
case UserActionTypes.SIGN_OUT_FAILURE:
case UserActionTypes.EMAIL_SIGN_UP_FAILURE:

View File

@@ -90,6 +90,9 @@
},
"audit_trail": {
"messages": {
"admin_jobmarkexported": "ADMIN: Job marked as exported.",
"admin_jobmarkforreexport": "ADMIN: Job marked for re-export.",
"admin_jobunvoid": "ADMIN: Job has been unvoided.",
"billposted": "Bill with invoice number {{invoice_number}} posted.",
"billupdated": "Bill with invoice number {{invoice_number}} updated.",
"jobassignmentchange": "Employee {{name}} assigned to {{operation}}",
@@ -171,7 +174,8 @@
"bill_total": "Bill Total Amount",
"billcmtotal": "Credit Memos",
"bills": "Bills",
"creditsnotreceived": "Credits Not Received",
"calculatedcreditsnotreceived": "Calculated CNR",
"creditsnotreceived": "Credits Not Marked Received",
"creditsreceived": "Credits Received",
"dedfromlbr": "Labor Adjustments",
"deleteconfirm": "Are you sure you want to delete this bill? It cannot be undone. If this bill has deductions from labors, manual changes may be required.",
@@ -278,6 +282,8 @@
},
"last_name_first": "Display Owner Info as <Last>, <First>",
"lastnumberworkingdays": "Scoreboard - Last Number of Working Days",
"localmediaserverhttp": "Local Media Server - HTTP Path",
"localmediaservernetwork": "Local Media Server - Network Path",
"logo_img_footer_margin": "Footer Margin (px)",
"logo_img_header_margin": "Header Margin (px)",
"logo_img_path": "Shop Logo",
@@ -355,6 +361,7 @@
"intake": "Jobs -> Intake",
"list-active": "Jobs -> List Active",
"list-all": "Jobs -> List All",
"list-ready": "Jobs -> List Ready",
"partsqueue": "Jobs -> Parts Queue"
},
"owners": {
@@ -476,6 +483,7 @@
"status": "Status Label",
"statuses": {
"active_statuses": "Active Statuses (Filtering for Active Jobs throughout system)",
"additional_board_statuses": "Additional Status to Display on Production Board",
"color": "Color",
"default_arrived": "Default Arrived Status (Transition to Production)",
"default_bo": "Default Backordered Status",
@@ -494,12 +502,14 @@
"post_production_statuses": "Post-Production Statuses",
"pre_production_statuses": "Pre-Production Statuses",
"production_colors": "Production Status Colors",
"production_statuses": "Production Statuses"
"production_statuses": "Production Statuses",
"ready_statuses": "Ready Statuses"
},
"target_touchtime": "Target Touch Time",
"timezone": "Timezone",
"tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs",
"use_fippa": "Use FIPPA for Names on Generated Documents?",
"uselocalmediaserver": "Use Local Media Server?",
"website": "Website",
"zip_post": "Zip/Postal Code"
},
@@ -537,6 +547,8 @@
"jobstatuses": "Job Statuses",
"laborrates": "Labor Rates",
"licensing": "Licensing",
"md_to_emails": "Preset To Emails",
"md_to_emails_emails": "Emails",
"messagingpresets": "Messaging Presets",
"notemplatesavailable": "No templates available to add.",
"notespresets": "Notes Presets",
@@ -815,6 +827,7 @@
"confirmdelete": "Are you sure you want to delete these documents. This CANNOT be undone.",
"doctype": "Document Type",
"newjobid": "Assign to Job",
"openinexplorer": "Open in Explorer",
"reassign_limitexceeded": "Reassigning all selected documents will exceed the job storage limit for your shop. ",
"reassign_limitexceeded_title": "Unable to reassign document(s)",
"storageexceeded": "You've exceeded your storage limit for this job. Please remove documents, or increase your storage plan.",
@@ -870,6 +883,7 @@
"base_rate": "Base Rate",
"cost_center": "Cost Center",
"employee_number": "Employee Number",
"external_id": "External Employee ID",
"first_name": "First Name",
"flat_rate": "Flat Rate (Disabled is Straight Time)",
"hire_date": "Hire Date",
@@ -905,7 +919,8 @@
"createdat": "Created At"
},
"labels": {
"attempts": "Export Attempts"
"attempts": "Export Attempts",
"priorsuccesfulexport": "This record has previously been exported successfully. Please make sure it has already been deleted in the target system."
}
},
"general": {
@@ -987,6 +1002,7 @@
"passwordresetvalidatesuccess_sub": "You may now sign in again using your new password. ",
"passwordsdonotmatch": "The passwords you have entered do not match.",
"print": "Print",
"refresh": "Refresh",
"required": "Required",
"saturday": "Saturday",
"search": "Search...",
@@ -1385,6 +1401,7 @@
"prt_tax_rt": "Part Tax Rate",
"prt_type": "Part Type"
},
"partsstatus": "Parts Status",
"pas": "Sublet",
"pay_date": "Pay Date",
"phoneshort": "PH",
@@ -1394,6 +1411,7 @@
"production_vars": {
"note": "Production Note"
},
"queued_for_parts": "Queued for Parts",
"rate_ats": "ATS Rate",
"rate_la1": "LA1",
"rate_la2": "LA2",
@@ -1579,14 +1597,15 @@
"partstotal": "Parts Total (ex. Taxes)",
"plitooltips": {
"billtotal": "The total amount of all bill lines that have been posted against this RO (not including credits, taxes, or labor adjustments).",
"creditmemos": "The total amount of all returns created. This amount does not reflect credit memos that have been posted.",
"creditsnotreceived": "The total amount of returns created for this job that do not have a corresponding credit memo posted. An amount greater than $0 indicates that vendors have not provided requested credit memos.",
"calculatedcreditsnotreceived": "The calculated credits not received is derived by subtracting the amount of credit memos entered from the <b>retail</b> total of returns created. This does not take into account whether the credit was marked as received. You can find more information <a href=\"https://help.imex.online/en/article/credits-not-received-changes-1jy9snw\" target=\"_blank\">here</a>.",
"creditmemos": "The total <b>retail</b> amount of all returns created. This amount does not reflect credit memos that have been posted.",
"creditsnotreceived": "This total reflects the total <b>retail</b> of parts returns lines that have not been explicitly marked as returned when posting a credit memo. You can learn more about this here <a href=\"https://help.imex.online/en/article/credits-not-received-changes-1jy9snw\" target=\"_blank\">here</a>. ",
"discrep1": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>Too many bills/bill lines that have been posted against this RO. Check to make sure every bill posted on this RO is correctly posted and assigned.</li>\n<li>You do not have the latest supplement imported, or, a supplement must be submitted and then imported.</li>\n<li>You have posted a bill line to labor.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this job from reconciling.</i>",
"discrep2": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>Used an incorrect rate when deducting from labor.</li>\n<li>An outstanding imbalance higher in the reconciliation process.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this job from reconciling.</i>",
"discrep3": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>Credit memos that have not been received or posted.</li>\n<li>An outstanding imbalance higher in the reconciliation process.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this job from reconciling.</i>",
"discrep3": "If the discrepancy is not $0, you may have one of the following: <br/><br/>\n\n<ul>\n<li>A parts order return has not been created.</li>\n<li>An outstanding imbalance higher in the reconciliation process.</li>\n</ul>\n<br/>\n<i>There may be additional issues not listed above that prevent this job from reconciling.</i>",
"laboradj": "The sum of all bill lines that deducted from labor hours, rather than part prices.",
"partstotal": "This is the total of all parts and sublet amounts on the vehicle (some of these may require an in-house invoice).<br/>\nItems such as shop and paint materials, labor online lines, etc. are not included in this total.",
"totalreturns": "The total amount of returns created for this job."
"totalreturns": "The total <b>retail</b> amount of returns created for this job."
},
"prt_dsmk_total": "Line Item Adjustment",
"rates": "Rates",
@@ -1673,15 +1692,15 @@
"name": "ImEX Online",
"status": "System Status"
},
"slogan": "A whole new kind of shop management system."
"slogan": "ImEX Systems Inc. is a technology leader in the collision repair industry. We specialize in creating collision repair management systems and bodyshop management systems that lower cycle times and promote efficiency."
},
"hero": {
"button": "Learn More",
"title": "A whole new kind of shop management system."
"title": "Shop management reimagined."
},
"labels": {
"features": "Features",
"managemyshop": "Manage my Shop",
"managemyshop": "Sign In",
"pricing": "Pricing"
},
"pricing": {
@@ -1740,6 +1759,7 @@
"phonebook": "Phonebook",
"productionboard": "Production Board - Visual",
"productionlist": "Production Board - List",
"readyjobs": "Ready Jobs",
"recent": "Recent Items",
"reportcenter": "Report Center",
"rescueme": "Rescue me!",
@@ -1904,7 +1924,8 @@
},
"parts": {
"actions": {
"order": "Order Parts"
"order": "Order Parts",
"orderinhouse": "Order as In House"
}
},
"parts_orders": {
@@ -1917,12 +1938,15 @@
"associatedbills": "This parts order cannot",
"backordering": "Error backordering part {{message}}.",
"creating": "Error encountered when creating parts order. ",
"oec": "Error creating EMS files for OEC. {{error}}"
"oec": "Error creating EMS files for OEC. {{error}}",
"saving": "Error saving parts order. {{error}}.",
"updating": "Error updating parts order/parts order line. {{error}}."
},
"fields": {
"act_price": "Price",
"backordered_eta": "B.O. ETA",
"backordered_on": "B.O. On",
"cm_received": "CM Received?",
"comments": "Comments",
"cost": "Cost",
"db_price": "List Price",
@@ -1945,6 +1969,7 @@
"confirmdelete": "Are you sure you want to delete this item? It cannot be recovered. Job line statuses will not be updated and may require manual review. ",
"email": "Send by Email",
"inthisorder": "Parts in this Order",
"mark_as_received": "Mark as Received?",
"newpartsorder": "New Parts Order",
"notyetordered": "This part has not yet been ordered.",
"oec": "Order via OEC",
@@ -1956,6 +1981,7 @@
},
"successes": {
"created": "Parts order created successfully. ",
"line_updated": "Parts return line updated.",
"received": "Parts order received.",
"return_created": "Parts return created successfully."
}
@@ -2077,6 +2103,7 @@
"estimate_detail": "Estimate Details",
"estimate_followup": "Estimate Followup",
"express_repair_checklist": "Express Repair Checklist",
"filing_coversheet_landscape": "Filing Coversheet (Landscape)",
"filing_coversheet_portrait": "Filing Coversheet (Portrait)",
"final_invoice": "Final Invoice",
"fippa_authorization": "FIPPA Authorization",
@@ -2151,6 +2178,9 @@
"ca_bc_etf_table": "ICBC ETF Table",
"exported_payroll": "Payroll Table"
},
"special": {
"attendance_detail_csv": "Attendance Table"
},
"subjects": {
"jobs": {
"parts_order": "Parts Order PO: {{ro_number}} - {{name}}"
@@ -2182,6 +2212,7 @@
"settings": "Error saving board settings: {{error}}"
},
"labels": {
"actual_in": "Actual In",
"alert": "Alert",
"alertoff": "Remove alert from job",
"alerton": "Add alert to job",
@@ -2207,6 +2238,7 @@
"note": "Production Note",
"ownr_nm": "Owner Name",
"paintpriority": "P/P",
"partsstatus": "Parts Status",
"production_note": "Production Note",
"refinishhours": "R",
"scheduled_completion": "Scheduled Completion",
@@ -2494,6 +2526,7 @@
"jobs-detail": "Job {{number}}",
"jobs-intake": "Intake",
"jobs-new": "Create a New Job",
"jobs-ready": "Ready Jobs",
"owner-detail": "{{name}}",
"owners": "Owners",
"parts-queue": "Parts Queue",
@@ -2543,6 +2576,7 @@
"productionboard": "Production - Board",
"productionlist": "Production Board - List | $t(titles.app)",
"profile": "My Profile | $t(titles.app)",
"readyjobs": "Ready Jobs | $t(titles.app)",
"resetpassword": "Reset Password",
"resetpasswordvalidate": "Enter New Password",
"schedule": "Schedule | $t(titles.app)",

View File

@@ -90,6 +90,9 @@
},
"audit_trail": {
"messages": {
"admin_jobmarkexported": "",
"admin_jobmarkforreexport": "",
"admin_jobunvoid": "",
"billposted": "",
"billupdated": "",
"jobassignmentchange": "",
@@ -171,6 +174,7 @@
"bill_total": "",
"billcmtotal": "",
"bills": "",
"calculatedcreditsnotreceived": "",
"creditsnotreceived": "",
"creditsreceived": "",
"dedfromlbr": "",
@@ -278,6 +282,8 @@
},
"last_name_first": "",
"lastnumberworkingdays": "",
"localmediaserverhttp": "",
"localmediaservernetwork": "",
"logo_img_footer_margin": "",
"logo_img_header_margin": "",
"logo_img_path": "",
@@ -355,6 +361,7 @@
"intake": "",
"list-active": "",
"list-all": "",
"list-ready": "",
"partsqueue": ""
},
"owners": {
@@ -476,6 +483,7 @@
"status": "",
"statuses": {
"active_statuses": "",
"additional_board_statuses": "",
"color": "",
"default_arrived": "",
"default_bo": "",
@@ -494,12 +502,14 @@
"post_production_statuses": "",
"pre_production_statuses": "",
"production_colors": "",
"production_statuses": ""
"production_statuses": "",
"ready_statuses": ""
},
"target_touchtime": "",
"timezone": "",
"tt_allow_post_to_invoiced": "",
"use_fippa": "",
"uselocalmediaserver": "",
"website": "",
"zip_post": ""
},
@@ -537,6 +547,8 @@
"jobstatuses": "",
"laborrates": "",
"licensing": "",
"md_to_emails": "",
"md_to_emails_emails": "",
"messagingpresets": "",
"notemplatesavailable": "",
"notespresets": "",
@@ -815,6 +827,7 @@
"confirmdelete": "",
"doctype": "",
"newjobid": "",
"openinexplorer": "",
"reassign_limitexceeded": "",
"reassign_limitexceeded_title": "",
"storageexceeded": "",
@@ -870,6 +883,7 @@
"base_rate": "Tasa básica",
"cost_center": "Centro de costos",
"employee_number": "Numero de empleado",
"external_id": "",
"first_name": "Nombre de pila",
"flat_rate": "Tarifa plana (deshabilitado es tiempo recto)",
"hire_date": "Fecha de contratación",
@@ -905,7 +919,8 @@
"createdat": ""
},
"labels": {
"attempts": ""
"attempts": "",
"priorsuccesfulexport": ""
}
},
"general": {
@@ -987,6 +1002,7 @@
"passwordresetvalidatesuccess_sub": "",
"passwordsdonotmatch": "",
"print": "",
"refresh": "",
"required": "",
"saturday": "",
"search": "Buscar...",
@@ -1385,6 +1401,7 @@
"prt_tax_rt": "",
"prt_type": ""
},
"partsstatus": "",
"pas": "",
"pay_date": "Fecha de Pay",
"phoneshort": "PH",
@@ -1394,6 +1411,7 @@
"production_vars": {
"note": ""
},
"queued_for_parts": "",
"rate_ats": "",
"rate_la1": "Tarifa LA1",
"rate_la2": "Tarifa LA2",
@@ -1579,6 +1597,7 @@
"partstotal": "",
"plitooltips": {
"billtotal": "",
"calculatedcreditsnotreceived": "",
"creditmemos": "",
"creditsnotreceived": "",
"discrep1": "",
@@ -1740,6 +1759,7 @@
"phonebook": "",
"productionboard": "",
"productionlist": "",
"readyjobs": "",
"recent": "",
"reportcenter": "",
"rescueme": "",
@@ -1904,7 +1924,8 @@
},
"parts": {
"actions": {
"order": "Pedido de piezas"
"order": "Pedido de piezas",
"orderinhouse": ""
}
},
"parts_orders": {
@@ -1917,12 +1938,15 @@
"associatedbills": "",
"backordering": "",
"creating": "Se encontró un error al crear el pedido de piezas.",
"oec": ""
"oec": "",
"saving": "",
"updating": ""
},
"fields": {
"act_price": "",
"backordered_eta": "",
"backordered_on": "",
"cm_received": "",
"comments": "",
"cost": "",
"db_price": "",
@@ -1945,6 +1969,7 @@
"confirmdelete": "",
"email": "Enviar por correo electrónico",
"inthisorder": "Partes en este pedido",
"mark_as_received": "",
"newpartsorder": "",
"notyetordered": "",
"oec": "",
@@ -1956,6 +1981,7 @@
},
"successes": {
"created": "Pedido de piezas creado con éxito.",
"line_updated": "",
"received": "",
"return_created": ""
}
@@ -2077,6 +2103,7 @@
"estimate_detail": "",
"estimate_followup": "",
"express_repair_checklist": "",
"filing_coversheet_landscape": "",
"filing_coversheet_portrait": "",
"final_invoice": "",
"fippa_authorization": "",
@@ -2151,6 +2178,9 @@
"ca_bc_etf_table": "",
"exported_payroll": ""
},
"special": {
"attendance_detail_csv": ""
},
"subjects": {
"jobs": {
"parts_order": ""
@@ -2182,6 +2212,7 @@
"settings": ""
},
"labels": {
"actual_in": "",
"alert": "",
"alertoff": "",
"alerton": "",
@@ -2207,6 +2238,7 @@
"note": "",
"ownr_nm": "",
"paintpriority": "",
"partsstatus": "",
"production_note": "",
"refinishhours": "",
"scheduled_completion": "",
@@ -2494,6 +2526,7 @@
"jobs-detail": "",
"jobs-intake": "",
"jobs-new": "",
"jobs-ready": "",
"owner-detail": "",
"owners": "",
"parts-queue": "",
@@ -2543,6 +2576,7 @@
"productionboard": "",
"productionlist": "",
"profile": "Mi perfil | $t(titles.app)",
"readyjobs": "",
"resetpassword": "",
"resetpasswordvalidate": "",
"schedule": "Horario | $t(titles.app)",

View File

@@ -90,6 +90,9 @@
},
"audit_trail": {
"messages": {
"admin_jobmarkexported": "",
"admin_jobmarkforreexport": "",
"admin_jobunvoid": "",
"billposted": "",
"billupdated": "",
"jobassignmentchange": "",
@@ -171,6 +174,7 @@
"bill_total": "",
"billcmtotal": "",
"bills": "",
"calculatedcreditsnotreceived": "",
"creditsnotreceived": "",
"creditsreceived": "",
"dedfromlbr": "",
@@ -278,6 +282,8 @@
},
"last_name_first": "",
"lastnumberworkingdays": "",
"localmediaserverhttp": "",
"localmediaservernetwork": "",
"logo_img_footer_margin": "",
"logo_img_header_margin": "",
"logo_img_path": "",
@@ -355,6 +361,7 @@
"intake": "",
"list-active": "",
"list-all": "",
"list-ready": "",
"partsqueue": ""
},
"owners": {
@@ -476,6 +483,7 @@
"status": "",
"statuses": {
"active_statuses": "",
"additional_board_statuses": "",
"color": "",
"default_arrived": "",
"default_bo": "",
@@ -494,12 +502,14 @@
"post_production_statuses": "",
"pre_production_statuses": "",
"production_colors": "",
"production_statuses": ""
"production_statuses": "",
"ready_statuses": ""
},
"target_touchtime": "",
"timezone": "",
"tt_allow_post_to_invoiced": "",
"use_fippa": "",
"uselocalmediaserver": "",
"website": "",
"zip_post": ""
},
@@ -537,6 +547,8 @@
"jobstatuses": "",
"laborrates": "",
"licensing": "",
"md_to_emails": "",
"md_to_emails_emails": "",
"messagingpresets": "",
"notemplatesavailable": "",
"notespresets": "",
@@ -815,6 +827,7 @@
"confirmdelete": "",
"doctype": "",
"newjobid": "",
"openinexplorer": "",
"reassign_limitexceeded": "",
"reassign_limitexceeded_title": "",
"storageexceeded": "",
@@ -870,6 +883,7 @@
"base_rate": "Taux de base",
"cost_center": "Centre de coûts",
"employee_number": "Numéro d'employé",
"external_id": "",
"first_name": "Prénom",
"flat_rate": "Taux fixe (désactivé est le temps normal)",
"hire_date": "Date d'embauche",
@@ -905,7 +919,8 @@
"createdat": ""
},
"labels": {
"attempts": ""
"attempts": "",
"priorsuccesfulexport": ""
}
},
"general": {
@@ -987,6 +1002,7 @@
"passwordresetvalidatesuccess_sub": "",
"passwordsdonotmatch": "",
"print": "",
"refresh": "",
"required": "",
"saturday": "",
"search": "Chercher...",
@@ -1385,6 +1401,7 @@
"prt_tax_rt": "",
"prt_type": ""
},
"partsstatus": "",
"pas": "",
"pay_date": "Date d'Pay",
"phoneshort": "PH",
@@ -1394,6 +1411,7 @@
"production_vars": {
"note": ""
},
"queued_for_parts": "",
"rate_ats": "",
"rate_la1": "Taux LA1",
"rate_la2": "Taux LA2",
@@ -1579,6 +1597,7 @@
"partstotal": "",
"plitooltips": {
"billtotal": "",
"calculatedcreditsnotreceived": "",
"creditmemos": "",
"creditsnotreceived": "",
"discrep1": "",
@@ -1740,6 +1759,7 @@
"phonebook": "",
"productionboard": "",
"productionlist": "",
"readyjobs": "",
"recent": "",
"reportcenter": "",
"rescueme": "",
@@ -1904,7 +1924,8 @@
},
"parts": {
"actions": {
"order": "Commander des pièces"
"order": "Commander des pièces",
"orderinhouse": ""
}
},
"parts_orders": {
@@ -1917,12 +1938,15 @@
"associatedbills": "",
"backordering": "",
"creating": "Erreur rencontrée lors de la création de la commande de pièces.",
"oec": ""
"oec": "",
"saving": "",
"updating": ""
},
"fields": {
"act_price": "",
"backordered_eta": "",
"backordered_on": "",
"cm_received": "",
"comments": "",
"cost": "",
"db_price": "",
@@ -1945,6 +1969,7 @@
"confirmdelete": "",
"email": "Envoyé par email",
"inthisorder": "Pièces dans cette commande",
"mark_as_received": "",
"newpartsorder": "",
"notyetordered": "",
"oec": "",
@@ -1956,6 +1981,7 @@
},
"successes": {
"created": "Commande de pièces créée avec succès.",
"line_updated": "",
"received": "",
"return_created": ""
}
@@ -2077,6 +2103,7 @@
"estimate_detail": "",
"estimate_followup": "",
"express_repair_checklist": "",
"filing_coversheet_landscape": "",
"filing_coversheet_portrait": "",
"final_invoice": "",
"fippa_authorization": "",
@@ -2151,6 +2178,9 @@
"ca_bc_etf_table": "",
"exported_payroll": ""
},
"special": {
"attendance_detail_csv": ""
},
"subjects": {
"jobs": {
"parts_order": ""
@@ -2182,6 +2212,7 @@
"settings": ""
},
"labels": {
"actual_in": "",
"alert": "",
"alertoff": "",
"alerton": "",
@@ -2207,6 +2238,7 @@
"note": "",
"ownr_nm": "",
"paintpriority": "",
"partsstatus": "",
"production_note": "",
"refinishhours": "",
"scheduled_completion": "",
@@ -2494,6 +2526,7 @@
"jobs-detail": "",
"jobs-intake": "",
"jobs-new": "",
"jobs-ready": "",
"owner-detail": "",
"owners": "",
"parts-queue": "",
@@ -2543,6 +2576,7 @@
"productionboard": "",
"productionlist": "",
"profile": "Mon profil | $t(titles.app)",
"readyjobs": "",
"resetpassword": "",
"resetpasswordvalidate": "",
"schedule": "Horaire | $t(titles.app)",

View File

@@ -3,12 +3,17 @@ import i18n from "i18next";
const AuditTrailMapping = {
jobstatuschange: (status) =>
i18n.t("audit_trail.messages.jobstatuschange", { status }),
admin_jobstatuschange: (status) =>
"ADMIN: " + i18n.t("audit_trail.messages.jobstatuschange", { status }),
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
jobimported: () => i18n.t("audit_trail.messages.jobimported"),
jobconverted: (ro_number) =>
i18n.t("audit_trail.messages.jobconverted", { ro_number }),
jobfieldchange: (field, value) =>
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
admin_jobfieldchange: (field, value) =>
"ADMIN: " +
i18n.t("audit_trail.messages.jobfieldchanged", { field, value }),
jobspartsorder: (order_number) =>
i18n.t("audit_trail.messages.jobspartsorder", { order_number }),
jobspartsreturn: (order_number) =>
@@ -29,6 +34,11 @@ const AuditTrailMapping = {
jobnoteadded: () => i18n.t("audit_trail.messages.jobnoteadded"),
jobnoteupdated: () => i18n.t("audit_trail.messages.jobnoteupdated"),
jobnotedeleted: () => i18n.t("audit_trail.messages.jobnotedeleted"),
admin_jobunvoid: () => i18n.t("audit_trail.messages.admin_jobunvoid"),
admin_jobmarkforreexport: () =>
i18n.t("audit_trail.messages.admin_jobmarkforreexport"),
admin_jobmarkexported: () =>
i18n.t("audit_trail.messages.admin_jobmarkexported"),
};
export default AuditTrailMapping;

View File

@@ -18,12 +18,14 @@ export default async function RenderTemplate(
templateObject,
bodyshop,
renderAsHtml = false,
renderAsExcel = false
renderAsExcel = false,
renderAsText = false
) {
//Query assets that match the template name. Must be in format <<templateName>>.query
let { contextData, useShopSpecificTemplate } = await fetchContextData(
templateObject
);
const { ignoreCustomMargins } = Templates[templateObject.name];
let reportRequest = {
@@ -46,19 +48,21 @@ export default async function RenderTemplate(
marginBottom:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.footerMargin &&
bodyshop.logo_img_path.footerMargin > 36
bodyshop.logo_img_path.footerMargin > 50
? bodyshop.logo_img_path.footerMargin
: "36px",
: "50px",
},
}),
}),
...(renderAsExcel ? { recipe: "html-to-xlsx" } : {}),
...(renderAsText ? { recipe: "text" } : {}),
},
data: {
...contextData,
...templateObject.variables,
...templateObject.context,
headerpath: `/${bodyshop.imexshopid}/header.html`,
footerpath: `/${bodyshop.imexshopid}/footer.html`,
bodyshop: bodyshop,
offset: bodyshop.timezone, //moment().utcOffset(),
},
@@ -156,24 +160,49 @@ export async function RenderTemplates(
marginBottom:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.footerMargin &&
bodyshop.logo_img_path.footerMargin > 36
bodyshop.logo_img_path.footerMargin > 50
? bodyshop.logo_img_path.footerMargin
: "36px",
: "50px",
},
}),
pdfOperations: templateAndData.map((template) => {
return {
pdfOperations: [
{
template: {
name: template.useShopSpecificTemplate
? `/${bodyshop.imexshopid}/${template.templateObject.name}`
: `/${template.templateObject.name}`,
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
name: "/components/Header-Footer",
recipe: "chrome-pdf",
engine: "handlebars",
},
type: "append",
mergeWholeDocument: true,
renderForEveryPage: true,
};
}),
type: "merge",
},
...templateAndData.map((template) => {
return {
template: {
chrome: {
marginTop:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.headerMargin &&
bodyshop.logo_img_path.headerMargin > 36
? bodyshop.logo_img_path.headerMargin
: "36px",
marginBottom:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.footerMargin &&
bodyshop.logo_img_path.footerMargin > 50
? bodyshop.logo_img_path.footerMargin
: "50px",
},
name: template.useShopSpecificTemplate
? `/${bodyshop.imexshopid}/${template.templateObject.name}`
: `/${template.templateObject.name}`,
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
},
type: "append",
// mergeWholeDocument: true,
// renderForEveryPage: true,
};
}),
],
},
data: {
...extend(
@@ -184,6 +213,7 @@ export async function RenderTemplates(
// ...rootTemplate.templateObject.variables,
// ...rootTemplate.templateObject.context,
headerpath: `/${bodyshop.imexshopid}/header.html`,
footerpath: `/${bodyshop.imexshopid}/footer.html`,
bodyshop: bodyshop,
offset: moment().utcOffset(),
},
@@ -226,6 +256,8 @@ export const GenerateDocument = async (
} else if (sendType === "x") {
console.log("excel");
await RenderTemplate(template, bodyshop, false, true);
} else if (sendType === "text") {
await RenderTemplate(template, bodyshop, false, false, true);
} else {
await RenderTemplate(template, bodyshop);
}

View File

@@ -314,6 +314,14 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "post",
},
filing_coversheet_landscape: {
title: i18n.t("printcenter.jobs.filing_coversheet_landscape"),
description: "CASL Authorization",
subject: i18n.t("printcenter.jobs.filing_coversheet_landscape"),
key: "filing_coversheet_landscape",
disabled: false,
group: "post",
},
qc_sheet: {
title: i18n.t("printcenter.jobs.qc_sheet"),
description: "All Jobs Notes",
@@ -476,7 +484,7 @@ export const TemplateList = (type, context) => {
: {}),
...(!type || type === "job_special"
? {
thirdpartypayer: {
special_thirdpartypayer: {
title: i18n.t("printcenter.jobs.thirdpartypayer"),
description: "CSI invite",
key: "special_thirdpartypayer",
@@ -1660,6 +1668,13 @@ export const TemplateList = (type, context) => {
key: "exported_payroll",
disabled: false,
},
attendance_detail_csv: {
title: i18n.t("printcenter.special.attendance_detail_csv"),
description: "Est Detail",
subject: i18n.t("printcenter.special.attendance_detail_csv"),
key: "attendance_detail_csv",
disabled: false,
},
production_by_technician_one: {
title: i18n.t(
"reportcenter.templates.production_by_technician_one"

View File

@@ -0,0 +1,6 @@
import { store } from "../redux/store";
export function CreateExplorerLinkForJob({ jobid }) {
const bodyshop = store.getState().user.bodyshop;
return `imexmedia://${bodyshop.localmediaservernetwork}/Jobs/${jobid}`;
}

View File

@@ -1266,15 +1266,15 @@
dependencies:
tslib "^2.0.1"
"@firebase/analytics-compat@0.1.8":
version "0.1.8"
resolved "https://registry.yarnpkg.com/@firebase/analytics-compat/-/analytics-compat-0.1.8.tgz#37a96ad8609c5492c9e21040359efdafd5bb6503"
integrity sha512-2XBPTFD4DPVZUJZ2tPOib7BfyHB/Ucfq3DfYMNRZFUrgctABTO/lRwjm3oqv4EsoqEOlbxRurvfFN8BQEe28LA==
"@firebase/analytics-compat@0.1.9":
version "0.1.9"
resolved "https://registry.yarnpkg.com/@firebase/analytics-compat/-/analytics-compat-0.1.9.tgz#d4a724f78a7333abe8ee8b00f4a4d8b8c392b46c"
integrity sha512-HYKMAZvfU589WVvK5XKY9Pl+axXFISabouAFw2VHpJm/TO1mAXAy0+eIjqQ3j8z3L1OEfCeOV/oY9eh8rpJZ5w==
dependencies:
"@firebase/analytics" "0.7.7"
"@firebase/analytics" "0.7.8"
"@firebase/analytics-types" "0.7.0"
"@firebase/component" "0.5.12"
"@firebase/util" "1.5.1"
"@firebase/component" "0.5.13"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/analytics-types@0.7.0":
@@ -1282,27 +1282,27 @@
resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.7.0.tgz#91960e7c87ce8bf18cf8dd9e55ccbf5dc3989b5d"
integrity sha512-DNE2Waiwy5+zZnCfintkDtBfaW6MjIG883474v6Z0K1XZIvl76cLND4iv0YUb48leyF+PJK1KO2XrgHb/KpmhQ==
"@firebase/analytics@0.7.7":
version "0.7.7"
resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.7.7.tgz#a7f50fd2b5ef9f3ca23e1ce4a2326fa6a659d907"
integrity sha512-duzzyDmBSF2ql6lp0m2jxmOH5PIgw0utqSXRjSgoYzD7lURncMAZVC2u3IgsAoUNTW/lcBSHpv+t4lnxTCNMCw==
"@firebase/analytics@0.7.8":
version "0.7.8"
resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.7.8.tgz#8f163437adb9b6b3f866e3744410aae931b97453"
integrity sha512-W38Zy/jf64LKpPi+mGNNETIjz4eq/YXBE0Uu2bzstqUwlhvFn1WlRBK4vzgtZMRaGW04CQp9FXYv6ZTRo/Xbyw==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/installations" "0.5.7"
"@firebase/component" "0.5.13"
"@firebase/installations" "0.5.8"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/app-check-compat@0.2.5":
version "0.2.5"
resolved "https://registry.yarnpkg.com/@firebase/app-check-compat/-/app-check-compat-0.2.5.tgz#ece1b5aa0553933e953f33f824573cfbf0eac89d"
integrity sha512-W1jGGcXXw1sdFia3qqnhlkg/5aQGiV6lAurq0FxfMXiV+qUxVuQ2roRbkJrcuJ3jxth8OGOir/yxOk/4YwkfHA==
"@firebase/app-check-compat@0.2.6":
version "0.2.6"
resolved "https://registry.yarnpkg.com/@firebase/app-check-compat/-/app-check-compat-0.2.6.tgz#34c4bd20d385909789a83c40815925176cc15fb1"
integrity sha512-DBzLHg/uuoNhDdwPEj8zQcqPaZSBFn8I0hATKyoX6SiAQKCi+4ugqeyQ6qGCyDpfNOyxL4PPxPMisXRhPzV2jw==
dependencies:
"@firebase/app-check" "0.5.5"
"@firebase/app-check" "0.5.6"
"@firebase/app-check-types" "0.4.0"
"@firebase/component" "0.5.12"
"@firebase/component" "0.5.13"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/app-check-interop-types@0.1.0":
@@ -1315,25 +1315,25 @@
resolved "https://registry.yarnpkg.com/@firebase/app-check-types/-/app-check-types-0.4.0.tgz#7007a9d1d720db20bcf466fe6785c96feaa0a82d"
integrity sha512-SsWafqMABIOu7zLgWbmwvHGOeQQVQlwm42kwwubsmfLmL4Sf5uGpBfDhQ0CAkpi7bkJ/NwNFKafNDL9prRNP0Q==
"@firebase/app-check@0.5.5":
version "0.5.5"
resolved "https://registry.yarnpkg.com/@firebase/app-check/-/app-check-0.5.5.tgz#836e031688c7c9e73119776e0dd71ed6775231b1"
integrity sha512-thGjl8Ys2VFKitUUM/xqkXXV8GS56yUdOBMpsjpTSH5iwa49dSBUTGzbwmUMARSkiSF+AzcjNonO6LQczTU/AA==
"@firebase/app-check@0.5.6":
version "0.5.6"
resolved "https://registry.yarnpkg.com/@firebase/app-check/-/app-check-0.5.6.tgz#e3b6d4d352875078ee32757d0adb125f3cc13a26"
integrity sha512-wdR/DCSdSDM0ka4nvMlGSiaknFxJO/gBuwn7G0iHO2vwj/2oSqjyG+QdJnoiIe1P1vOdqGNLxb1j10LPkR3TQQ==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/component" "0.5.13"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/app-compat@0.1.21":
version "0.1.21"
resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.1.21.tgz#bd9e33be3d6fb2a10d2a40e6b78779d6dca246a3"
integrity sha512-zKRjOt6JXZ6gBdl3ELdjvEQ7cdrsrCjLEaLttBxioqW9VxXZfBOgP38uUb0baJk2XNEUA6YWM+H/bg1y+FGFYA==
"@firebase/app-compat@0.1.22":
version "0.1.22"
resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.1.22.tgz#7190c50f3fd5d794e508bfcebe42a9b159f73890"
integrity sha512-InzQWdIKXsioZb6Ll/uynvopFbq9k3Qpi3gEUq+f8q0yr8/KQVuH2lIDmN70z11LRKXlsziU49qRwtV9tcEYhA==
dependencies:
"@firebase/app" "0.7.20"
"@firebase/component" "0.5.12"
"@firebase/app" "0.7.21"
"@firebase/component" "0.5.13"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/app-types@0.7.0":
@@ -1341,25 +1341,25 @@
resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.7.0.tgz#c9e16d1b8bed1a991840b8d2a725fb58d0b5899f"
integrity sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==
"@firebase/app@0.7.20":
version "0.7.20"
resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.7.20.tgz#919bcf199def75a8c2beb0658757cb83eec7302c"
integrity sha512-tTVrEYCbEKBcMp/bj5rUa35iM32W5z9l3jbLAqDh0ZM2yO4JvF08a3hHacZ32XDh9Av/yCgla0QmVPp/Z2klNg==
"@firebase/app@0.7.21":
version "0.7.21"
resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.7.21.tgz#c31077bd4c61f130eb00b6546835ed9cf8da69ab"
integrity sha512-b1COyw4HwajJ4zQCtL7w+d4GCQDmEaVO957eLLlBwz4QuDlx3eQIirpQhzkkPH17BJFZ6x0qyYEt6Wbhakn0kg==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/component" "0.5.13"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/auth-compat@0.2.11":
version "0.2.11"
resolved "https://registry.yarnpkg.com/@firebase/auth-compat/-/auth-compat-0.2.11.tgz#bf41ab5cc5657bbed32620d07972b24fb3c48990"
integrity sha512-6C42yXevri3F7H1LS3h524UsQsUlzGuszlIL3YsDuS+WJFqBe8I5AHOEM+Opi/VtIpWaXxPhWsp75TQndaCjKA==
"@firebase/auth-compat@0.2.12":
version "0.2.12"
resolved "https://registry.yarnpkg.com/@firebase/auth-compat/-/auth-compat-0.2.12.tgz#a13bd42c4ee36ddbf764ae24958cf4c64ddba5b5"
integrity sha512-LKeKylktRj03xgW5ilSOW1c4AsMig15ogf5hDKa820t6Bp6MNabj8yq2TV0/Q4SP4Ox/yrTISJGVvk+TJuBecQ==
dependencies:
"@firebase/auth" "0.19.11"
"@firebase/auth" "0.19.12"
"@firebase/auth-types" "0.11.0"
"@firebase/component" "0.5.12"
"@firebase/util" "1.5.1"
"@firebase/component" "0.5.13"
"@firebase/util" "1.5.2"
node-fetch "2.6.7"
selenium-webdriver "^4.0.0-beta.2"
tslib "^2.1.0"
@@ -1374,67 +1374,67 @@
resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.11.0.tgz#b9c73c60ca07945b3bbd7a097633e5f78fa9e886"
integrity sha512-q7Bt6cx+ySj9elQHTsKulwk3+qDezhzRBFC9zlQ1BjgMueUOnGMcvqmU0zuKlQ4RhLSH7MNAdBV2znVaoN3Vxw==
"@firebase/auth@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.19.11.tgz#38a1907e5a9117008616972295cb8be54abc06ac"
integrity sha512-9bctXmQA5pRhLL03wkbg6ibmhoTMa8QRHm3uDnb+iyMcHTJ5AyILRc5AVPS9FsnpWPDOLiVjtuMC28D6iC+zew==
"@firebase/auth@0.19.12":
version "0.19.12"
resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.19.12.tgz#df201b456bfb2c846b22513fc5798476e0730adc"
integrity sha512-39/eJBmq5Ne+HoCJuQXlhaOH2e8qySxYUa5Z25mhcam8nmAMrBh7Ph1yZjUeSfLsSJiSXANMHK5dnVE+1TROXw==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/component" "0.5.13"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
node-fetch "2.6.7"
selenium-webdriver "4.0.0-rc-1"
tslib "^2.1.0"
"@firebase/component@0.5.12":
version "0.5.12"
resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.5.12.tgz#901f59255fce8ee98f2772de6c5a0f8097cbdcef"
integrity sha512-gAKwxo0Ev+rp7Px+Yr71WbcC0CM9Tevhv0g38ORp2p57HqGjY65D3MD+jTKGZl58N/0nmX6MRRKym3bq/3k1gw==
"@firebase/component@0.5.13":
version "0.5.13"
resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.5.13.tgz#65a382e83bddd109380c9aa1f280791b1b4567c4"
integrity sha512-hxhJtpD8Ppf/VU2Rlos6KFCEV77TGIGD5bJlkPK1+B/WUe0mC6dTjW7KhZtXTc+qRBp9nFHWcsIORnT8liHP9w==
dependencies:
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/database-compat@0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@firebase/database-compat/-/database-compat-0.1.7.tgz#326f14532c2ec00842aa5e5f387aeaf941748551"
integrity sha512-T1mleRv2A8wyTV/jUuOdkN9Tl2lz0RGauqGc9nxP3AUzS9m3gIDN7u4CahZSdJlkR6tSU/MEWlfs5Q/oZStqxg==
"@firebase/database-compat@0.1.8":
version "0.1.8"
resolved "https://registry.yarnpkg.com/@firebase/database-compat/-/database-compat-0.1.8.tgz#ab627f2bdbe94367f515d5bded880c86886bbd28"
integrity sha512-dhXr5CSieBuKNdU96HgeewMQCT9EgOIkfF1GNy+iRrdl7BWLxmlKuvLfK319rmIytSs/vnCzcD9uqyxTeU/A3A==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/database" "0.12.7"
"@firebase/database-types" "0.9.6"
"@firebase/component" "0.5.13"
"@firebase/database" "0.12.8"
"@firebase/database-types" "0.9.7"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/database-types@0.9.6":
version "0.9.6"
resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.9.6.tgz#dcea2564ada9afe522d2520d889008f13ad8d2a1"
integrity sha512-E7U28X+FtVtug7EkIkaOXbdP8ghCPno21WWgEiDKsneY28N5WOwccfXqSzHgAAezkR40ht/ZqXlCsUhEpv6JXw==
"@firebase/database-types@0.9.7":
version "0.9.7"
resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.9.7.tgz#c5ee0ea9bb2703a13c1c47fe880fc577d5ce7f33"
integrity sha512-EFhgL89Fz6DY3kkB8TzdHvdu8XaqqvzcF2DLVOXEnQ3Ms7L755p5EO42LfxXoJqb9jKFvgLpFmKicyJG25WFWw==
dependencies:
"@firebase/app-types" "0.7.0"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
"@firebase/database@0.12.7":
version "0.12.7"
resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.12.7.tgz#49b9c326b9b86da6f5238627176a1a21fc2cc768"
integrity sha512-HL2NMQ3Ce5YNM2MdEuACHmU9NQEwq2F64R0XK+CReph40skxp+A7TvlJDO5bTAC0s3l3ebgCA9VmxfJu5R6UAA==
"@firebase/database@0.12.8":
version "0.12.8"
resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.12.8.tgz#11a1b6752ba0614892af15c71958e00ce16f5824"
integrity sha512-JBQVfFLzfhxlQbl4OU6ov9fdsddkytBQdtSSR49cz48homj38ccltAhK6seum+BI7f28cV2LFHF9672lcN+qxA==
dependencies:
"@firebase/auth-interop-types" "0.1.6"
"@firebase/component" "0.5.12"
"@firebase/component" "0.5.13"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
faye-websocket "0.11.4"
tslib "^2.1.0"
"@firebase/firestore-compat@0.1.16":
version "0.1.16"
resolved "https://registry.yarnpkg.com/@firebase/firestore-compat/-/firestore-compat-0.1.16.tgz#aa8eb38dfb5bbe828d76887eb1d302c95e766fd1"
integrity sha512-nLSBYGTkcGDbNFHBBMl74e7gwNo++BDTALlWNBaYRjTNj0TctYt8q4lggNy+MxXRQhP+dZjq/mrClS9T8RJZ4g==
"@firebase/firestore-compat@0.1.17":
version "0.1.17"
resolved "https://registry.yarnpkg.com/@firebase/firestore-compat/-/firestore-compat-0.1.17.tgz#8851d52717ac468b242fd9752706c2be12d15c30"
integrity sha512-hTLgq2WXUE6bb3/IqYlwY0Q6FdbZB2JwDoZHexIQmK69XuuK3j+JbE/NixV3mBo232tNSU+QeamfbAd6A1Agfw==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/firestore" "3.4.7"
"@firebase/component" "0.5.13"
"@firebase/firestore" "3.4.8"
"@firebase/firestore-types" "2.5.0"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/firestore-types@2.5.0":
@@ -1442,29 +1442,29 @@
resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-2.5.0.tgz#16fca40b6980fdb000de86042d7a96635f2bcdd7"
integrity sha512-I6c2m1zUhZ5SH0cWPmINabDyH5w0PPFHk2UHsjBpKdZllzJZ2TwTkXbDtpHUZNmnc/zAa0WNMNMvcvbb/xJLKA==
"@firebase/firestore@3.4.7":
version "3.4.7"
resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-3.4.7.tgz#aa31d8dd4fdb527fd3cc8ca57706a3137be06bf4"
integrity sha512-bE8k72cw0zJKOsKTOeRp2As0nNZIv3To9qQe0mTtHu4O8dSdst6izSXixZEXDOuMf/dp7VOY8+VBW/ZqeqWQsA==
"@firebase/firestore@3.4.8":
version "3.4.8"
resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-3.4.8.tgz#ca7395c81929c79e30a28ff5d19a567713e27f77"
integrity sha512-qjrI22TrqSGsOVBkYpRcpY48eSFj+hvleWEaFn3bDxy+QNUiZS08cicSlBOxdosKi5LRMQVGyHKcqHExup02+A==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/component" "0.5.13"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
"@firebase/webchannel-wrapper" "0.6.1"
"@grpc/grpc-js" "^1.3.2"
"@grpc/proto-loader" "^0.6.0"
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/functions-compat@0.1.11":
version "0.1.11"
resolved "https://registry.yarnpkg.com/@firebase/functions-compat/-/functions-compat-0.1.11.tgz#b005ee3750437e0a28e39891d57c195939e2e2d2"
integrity sha512-4U3BU4hOPAhG5YKpLAY/qKhNxsEn4yWSJHLc83H81Y75o2jSsyVklFR2bZNHl0qfq6A7KwUbL8HU48qLbhPlGg==
"@firebase/functions-compat@0.1.12":
version "0.1.12"
resolved "https://registry.yarnpkg.com/@firebase/functions-compat/-/functions-compat-0.1.12.tgz#b1e53630bf56816355f775216f34e24a2371ca8b"
integrity sha512-pKianAWF9vv3u9DazbRExYQFjEu/b9gxTWVCPjq+FiLK39xULT01dZz4Zrr2KzFnb54wHHbRmU1BAWNAkQTmmQ==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/functions" "0.7.10"
"@firebase/component" "0.5.13"
"@firebase/functions" "0.7.11"
"@firebase/functions-types" "0.5.0"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/functions-types@0.5.0":
@@ -1472,26 +1472,26 @@
resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.5.0.tgz#b50ba95ccce9e96f7cda453228ffe1684645625b"
integrity sha512-qza0M5EwX+Ocrl1cYI14zoipUX4gI/Shwqv0C1nB864INAD42Dgv4v94BCyxGHBg2kzlWy8PNafdP7zPO8aJQA==
"@firebase/functions@0.7.10":
version "0.7.10"
resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.7.10.tgz#ef849f449af87ce2a168d0af3b1077706ee973de"
integrity sha512-d+8M6RENmrF2/Y2J+V3fuad6Vq8aiwEzeJCl5sdUA4TydAnAPmGPaEz7uEyaxp8gIx/j373CZy2jTuI3PCxEyw==
"@firebase/functions@0.7.11":
version "0.7.11"
resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.7.11.tgz#ab65a20503d7a4bfeb4bc571c976cc37dc781184"
integrity sha512-o9pmN1TWHDEpmB6IYbqeIIG6Wllcfw6jSNm8UZYnOYM8oDay1FW6OeN/fA0GlGmwF4cPdxA3oKXbLn3ObYFxXQ==
dependencies:
"@firebase/app-check-interop-types" "0.1.0"
"@firebase/auth-interop-types" "0.1.6"
"@firebase/component" "0.5.12"
"@firebase/component" "0.5.13"
"@firebase/messaging-interop-types" "0.1.0"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/installations@0.5.7":
version "0.5.7"
resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.5.7.tgz#7298064662ac4e50fdfe07a16633bc64d951601d"
integrity sha512-qt6crxYyzUc19OYmNunOilcPUrVd+g0VJ42C3+clFJXsYzHey9xfr4q+5vALTJKLXbsX3QPzqEFEpe37sGSRRQ==
"@firebase/installations@0.5.8":
version "0.5.8"
resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.5.8.tgz#7a12c4367bc932303d4651f46196262e38aaae58"
integrity sha512-u/lAOVhgYFg1e38rNrVzFrWxdKzTOIromx574Hi2AccFA230hSlXFY7pRaCpgs11VDzmpt4lhhOrQOX7886cKw==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/util" "1.5.1"
"@firebase/component" "0.5.13"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/logger@0.3.2":
@@ -1501,14 +1501,14 @@
dependencies:
tslib "^2.1.0"
"@firebase/messaging-compat@0.1.11":
version "0.1.11"
resolved "https://registry.yarnpkg.com/@firebase/messaging-compat/-/messaging-compat-0.1.11.tgz#dd8e1b77f6d88f9fd3614d7c1265e9663272e623"
integrity sha512-icqaH66Fdn1704WRnF3NxPuDYA7SQTJv1klDuN2/LZi029w2jd9j7Tkld2egcwDgF2IrIIgxdCTRcwFZQIG2kQ==
"@firebase/messaging-compat@0.1.12":
version "0.1.12"
resolved "https://registry.yarnpkg.com/@firebase/messaging-compat/-/messaging-compat-0.1.12.tgz#239c1148f1cd5bf507613c431ff6fe7fc096ab3c"
integrity sha512-Cfv4ZQaxiMx4DcpDkFX1yKHFGQtnyMA6pcLplcC3uHkSVCyNRW6pFYSoO0/Uae03ixxIYNwle1ZVaVUZ2L5ddA==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/messaging" "0.9.11"
"@firebase/util" "1.5.1"
"@firebase/component" "0.5.13"
"@firebase/messaging" "0.9.12"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/messaging-interop-types@0.1.0":
@@ -1516,27 +1516,27 @@
resolved "https://registry.yarnpkg.com/@firebase/messaging-interop-types/-/messaging-interop-types-0.1.0.tgz#bdac02dd31edd5cb9eec37b1db698ea5e2c1a631"
integrity sha512-DbvUl/rXAZpQeKBnwz0NYY5OCqr2nFA0Bj28Fmr3NXGqR4PAkfTOHuQlVtLO1Nudo3q0HxAYLa68ZDAcuv2uKQ==
"@firebase/messaging@0.9.11":
version "0.9.11"
resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.9.11.tgz#4c72fe87bc3211121436d777131bdf2c91430e27"
integrity sha512-AdUvAz3S6fAhGlaP0gAT83vuuLtieXzr8TKjEeREbz/cJHx3DKZuDPK1RVElQos0ACc5kFtSJrKlEa8N2rdYjQ==
"@firebase/messaging@0.9.12":
version "0.9.12"
resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.9.12.tgz#8ef7a76de17921eac68e79952006604d01dda138"
integrity sha512-qfLW7SZRZVKscI1GSyWc3WPtjAUDUk3gcEfPkdz9fzzQwj98V8xF++g4wL+9cuEuRzYf8ki2kCN/aqKRYUrxag==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/installations" "0.5.7"
"@firebase/component" "0.5.13"
"@firebase/installations" "0.5.8"
"@firebase/messaging-interop-types" "0.1.0"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/performance-compat@0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@firebase/performance-compat/-/performance-compat-0.1.7.tgz#225101bf1da4b892e3d669f3b1da1348aeba5e0f"
integrity sha512-U0kxTs5mhzLM4tEkLCnlThgdJUh43dZQsk3Q9/fhuI4FrI8I2uUhx6BMVnyeiT7O9G4lEmhV3ag3QSmzdw4hzQ==
"@firebase/performance-compat@0.1.8":
version "0.1.8"
resolved "https://registry.yarnpkg.com/@firebase/performance-compat/-/performance-compat-0.1.8.tgz#9a22286ee20b421b871ac2534223e01327df689a"
integrity sha512-lMLKFcOB99+tb6dVHJlJ8s19JFjxqpAqPGXCG8evTODPUW3BluBbfG4YS7JRESVA7wc/6kkuQIOx9q7l+bBZtQ==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/component" "0.5.13"
"@firebase/logger" "0.3.2"
"@firebase/performance" "0.5.7"
"@firebase/performance" "0.5.8"
"@firebase/performance-types" "0.1.0"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/performance-types@0.1.0":
@@ -1544,15 +1544,15 @@
resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.1.0.tgz#5e6efa9dc81860aee2cb7121b39ae8fa137e69fc"
integrity sha512-6p1HxrH0mpx+622Ql6fcxFxfkYSBpE3LSuwM7iTtYU2nw91Hj6THC8Bc8z4nboIq7WvgsT/kOTYVVZzCSlXl8w==
"@firebase/performance@0.5.7":
version "0.5.7"
resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.5.7.tgz#56d06e019561d4f7ffbcc2bf4a78af474f52584c"
integrity sha512-V8dIYqCr+STiwY09JRPlRo1X59nwpT+s5gy34pdF4DNe/9Ck2XN0WkzDRb1oYwRtMl1cvWwLktbjtFICp4VYfA==
"@firebase/performance@0.5.8":
version "0.5.8"
resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.5.8.tgz#c7e1c73122975d3364203829839a78d9371d9530"
integrity sha512-IN5MWdGRn0jglSdv1UHEDMklm1SOfF1IZ1pGNxVyO5CpF3a08I54I60fuwEfMUcsU6OAfzMl3zI+bnW5IgKdPg==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/installations" "0.5.7"
"@firebase/component" "0.5.13"
"@firebase/installations" "0.5.8"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/polyfill@0.3.36":
@@ -1564,16 +1564,16 @@
promise-polyfill "8.1.3"
whatwg-fetch "2.0.4"
"@firebase/remote-config-compat@0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@firebase/remote-config-compat/-/remote-config-compat-0.1.7.tgz#d1c275c82a879c627321f8596d08beed1b223712"
integrity sha512-i3qfpqNL1iYRP8JSxorV9vQuReTst/xJ6voEodIiTR/JbTM0pXlQ4E+zlS5Nw+6GzsZUW21cmXwk1Yzz5a0AFQ==
"@firebase/remote-config-compat@0.1.8":
version "0.1.8"
resolved "https://registry.yarnpkg.com/@firebase/remote-config-compat/-/remote-config-compat-0.1.8.tgz#25c079fa8737d824add05337049dca17e078358f"
integrity sha512-lU9t7PMVpgE6q1vG8AuFenFhfUnx0H+eeiIQTi4dtuLDMx9BsI14c9VuiVjRIi7xC2DCDRNQCRL1kRD8bzgJNg==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/component" "0.5.13"
"@firebase/logger" "0.3.2"
"@firebase/remote-config" "0.3.6"
"@firebase/remote-config" "0.3.7"
"@firebase/remote-config-types" "0.2.0"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/remote-config-types@0.2.0":
@@ -1581,26 +1581,26 @@
resolved "https://registry.yarnpkg.com/@firebase/remote-config-types/-/remote-config-types-0.2.0.tgz#1e2759fc01f20b58c564db42196f075844c3d1fd"
integrity sha512-hqK5sCPeZvcHQ1D6VjJZdW6EexLTXNMJfPdTwbD8NrXUw6UjWC4KWhLK/TSlL0QPsQtcKRkaaoP+9QCgKfMFPw==
"@firebase/remote-config@0.3.6":
version "0.3.6"
resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.3.6.tgz#c7622e8f0d65bb55c03bc5529a49292814bd4165"
integrity sha512-eec1gbwFWslZZKdwC/tAC1cM1cUQSr+beS/uNkevrrC0cOjAJ1xqFPayvd0e2kbjAO2+pbp1Oe0yreORe28Cvg==
"@firebase/remote-config@0.3.7":
version "0.3.7"
resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.3.7.tgz#743fcb00501b9eca24728cf4caabea974ba3396b"
integrity sha512-gQaGzQCBOkS35b/aXC5Y9/zsPenqs6+axnChYYyfU7CqMG5FGfNbVi2rezYwB4G3+fH4rGO1s6xqcI535Fvy/A==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/installations" "0.5.7"
"@firebase/component" "0.5.13"
"@firebase/installations" "0.5.8"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/storage-compat@0.1.12":
version "0.1.12"
resolved "https://registry.yarnpkg.com/@firebase/storage-compat/-/storage-compat-0.1.12.tgz#47f7ff7d428296b73aab7bded7d942384d015f69"
integrity sha512-Qa21YWM5AZvRSH8/lmT4r//xdlEP01azt0GHgcX+MkKLi18qpPjgXpxCstVALH4XCKBP0RMaNiIToWIRTdRSxw==
"@firebase/storage-compat@0.1.13":
version "0.1.13"
resolved "https://registry.yarnpkg.com/@firebase/storage-compat/-/storage-compat-0.1.13.tgz#e7a985ee01336df40682add37a2d0055e83c9a3a"
integrity sha512-MdubKh+xe3Xpi34WaXBKtim8H2aauO5sqqmATTc2WgSmSAqTmNSjQfNqIdf139Mp9ZCnpZAxiwiwzQtfckLYWg==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/storage" "0.9.4"
"@firebase/component" "0.5.13"
"@firebase/storage" "0.9.5"
"@firebase/storage-types" "0.6.0"
"@firebase/util" "1.5.1"
"@firebase/util" "1.5.2"
tslib "^2.1.0"
"@firebase/storage-types@0.6.0":
@@ -1608,20 +1608,20 @@
resolved "https://registry.yarnpkg.com/@firebase/storage-types/-/storage-types-0.6.0.tgz#0b1af64a2965af46fca138e5b70700e9b7e6312a"
integrity sha512-1LpWhcCb1ftpkP/akhzjzeFxgVefs6eMD2QeKiJJUGH1qOiows2w5o0sKCUSQrvrRQS1lz3SFGvNR1Ck/gqxeA==
"@firebase/storage@0.9.4":
version "0.9.4"
resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.9.4.tgz#67fef67f50b1dd672202bc5be811ab45db0d224c"
integrity sha512-oDqIIxoGMuchwTDHugDE/ul0h+hFVh7eAmvIWHrchaj0R2BTivb+H5qgszWjlCs2pjHS6waGsnSGJgpBTW4AeA==
"@firebase/storage@0.9.5":
version "0.9.5"
resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.9.5.tgz#f74c905c7fbc40c1fef02c9191d2bffcf2898b4e"
integrity sha512-+nCDNIT2pNovlHnLOQPofn8jdOyJ4akUWPGn4ydAoFrfVt1/lINx5Qe+jS3/tKLROfYabqBYxfFUjHQKZBYwvg==
dependencies:
"@firebase/component" "0.5.12"
"@firebase/util" "1.5.1"
"@firebase/component" "0.5.13"
"@firebase/util" "1.5.2"
node-fetch "2.6.7"
tslib "^2.1.0"
"@firebase/util@1.5.1":
version "1.5.1"
resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.5.1.tgz#80ee586c78ed444eb7b96406e00513f279ef5a82"
integrity sha512-ojwPg8sKVcoU/kC1QdTrD+eUDyjQkZyiH9tlouXeZdAeDddCYNvHgIeBQhZt62WIcjlNhy1zro/xdV5nUUU38A==
"@firebase/util@1.5.2":
version "1.5.2"
resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.5.2.tgz#bdd2bc11c956a8a6a0fa25fbd752a13e033558bc"
integrity sha512-YvBH2UxFcdWG2HdFnhxZptPl2eVFlpOyTH66iDo13JPEYraWzWToZ5AMTtkyRHVmu7sssUpQlU9igy1KET7TOw==
dependencies:
tslib "^2.1.0"
@@ -2213,18 +2213,24 @@
resolved "https://registry.yarnpkg.com/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz#03ecc29279e3c0c832f6185a5bfa3497858ac8ca"
integrity sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==
"@splitsoftware/splitio-react@^1.3.1-rc.1":
version "1.3.1-rc.1"
resolved "https://registry.yarnpkg.com/@splitsoftware/splitio-react/-/splitio-react-1.3.1-rc.1.tgz#6ce939cb4438469fc5ce0a278ced80eddf3a3d0c"
integrity sha512-1DucZ/nrgnvNS7ohG31iWIcWJWsYxCl/IJvbcBW7TCXN71VfV8B5MV5jgNNB9qCmzxD2OpucTm+1UTal5dxFFg==
"@splitsoftware/splitio-commons@1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@splitsoftware/splitio-commons/-/splitio-commons-1.3.0.tgz#d9b10614fc5bcdd6a94748f53b7da8d83edaf8de"
integrity sha512-jwthiLCgN4DOiLjxn7RIGoEArTonpLUMcHgKMhka1rIzu9R8z+4qkvYPC+Eb5zLK/EIBViyf+sZJFUmBny3Ckw==
dependencies:
"@babel/runtime" "^7.10.2"
tslib "^2.3.1"
"@splitsoftware/splitio-react@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@splitsoftware/splitio-react/-/splitio-react-1.4.0.tgz#d23c2dea2fd82744a4540889bb2fbd15e55cf83a"
integrity sha512-zHcTLn8G08Kk+xCYpQLu8E0hpzMxrglcTdXUpC3zBCois2DVg9rh8QFp135b9eaTOJLSjsNXQMmVoeqKWNTJkQ==
dependencies:
"@splitsoftware/splitio-commons" "1.3.0"
"@types/google.analytics" "0.0.40"
events "3.1.0"
"@types/ioredis" "^4.28.0"
memoize-one "^5.1.1"
object-assign "^4.1.1"
shallowequal "^1.1.0"
unfetch "^4.1.0"
unfetch "^4.2.0"
"@stripe/react-stripe-js@^1.7.1":
version "1.7.1"
@@ -2535,6 +2541,13 @@
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57"
integrity sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==
"@types/ioredis@^4.28.0":
version "4.28.10"
resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.10.tgz#40ceb157a4141088d1394bb87c98ed09a75a06ff"
integrity sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==
dependencies:
"@types/node" "*"
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
@@ -6240,11 +6253,6 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.1:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
events@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59"
integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==
events@^3.0.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
@@ -6621,37 +6629,37 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
firebase@^9.6.10:
version "9.6.10"
resolved "https://registry.yarnpkg.com/firebase/-/firebase-9.6.10.tgz#542f899780780e85a7aa2a7bcaa8e76c1daea13a"
integrity sha512-pnWXhH2J/O5To/TXEp7Dx1nSKkDRspl1bo4f4p6f2noVrn3lvz+cbQNM7DTT0xyA0RsNM/V7aSv3ZbAQj8Od3Q==
firebase@^9.6.11:
version "9.6.11"
resolved "https://registry.yarnpkg.com/firebase/-/firebase-9.6.11.tgz#ec198b3bd646d0028b5d6240261cd89b200a590d"
integrity sha512-Zdmag/wGNkA4IAek+2yQoWrF2vyqIowu+2eOcSaE6jE2hDZYA3nHNutsQ+jquSxE3SeJk3Dh1OEsffqgunBy/w==
dependencies:
"@firebase/analytics" "0.7.7"
"@firebase/analytics-compat" "0.1.8"
"@firebase/app" "0.7.20"
"@firebase/app-check" "0.5.5"
"@firebase/app-check-compat" "0.2.5"
"@firebase/app-compat" "0.1.21"
"@firebase/analytics" "0.7.8"
"@firebase/analytics-compat" "0.1.9"
"@firebase/app" "0.7.21"
"@firebase/app-check" "0.5.6"
"@firebase/app-check-compat" "0.2.6"
"@firebase/app-compat" "0.1.22"
"@firebase/app-types" "0.7.0"
"@firebase/auth" "0.19.11"
"@firebase/auth-compat" "0.2.11"
"@firebase/database" "0.12.7"
"@firebase/database-compat" "0.1.7"
"@firebase/firestore" "3.4.7"
"@firebase/firestore-compat" "0.1.16"
"@firebase/functions" "0.7.10"
"@firebase/functions-compat" "0.1.11"
"@firebase/installations" "0.5.7"
"@firebase/messaging" "0.9.11"
"@firebase/messaging-compat" "0.1.11"
"@firebase/performance" "0.5.7"
"@firebase/performance-compat" "0.1.7"
"@firebase/auth" "0.19.12"
"@firebase/auth-compat" "0.2.12"
"@firebase/database" "0.12.8"
"@firebase/database-compat" "0.1.8"
"@firebase/firestore" "3.4.8"
"@firebase/firestore-compat" "0.1.17"
"@firebase/functions" "0.7.11"
"@firebase/functions-compat" "0.1.12"
"@firebase/installations" "0.5.8"
"@firebase/messaging" "0.9.12"
"@firebase/messaging-compat" "0.1.12"
"@firebase/performance" "0.5.8"
"@firebase/performance-compat" "0.1.8"
"@firebase/polyfill" "0.3.36"
"@firebase/remote-config" "0.3.6"
"@firebase/remote-config-compat" "0.1.7"
"@firebase/storage" "0.9.4"
"@firebase/storage-compat" "0.1.12"
"@firebase/util" "1.5.1"
"@firebase/remote-config" "0.3.7"
"@firebase/remote-config-compat" "0.1.8"
"@firebase/storage" "0.9.5"
"@firebase/storage-compat" "0.1.13"
"@firebase/util" "1.5.2"
flat-cache@^3.0.4:
version "3.0.4"
@@ -9165,10 +9173,10 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
markerjs2@^2.21.0:
version "2.21.0"
resolved "https://registry.yarnpkg.com/markerjs2/-/markerjs2-2.21.0.tgz#f2b4c524c058300709b54c0db2086569d00d9b1a"
integrity sha512-SC0/6VfT52laKWmnDQDKRqMREKhGzltJYWHaGFhoJza36IplFfrVRA1IYSPqHa5xKPApQmEn7fyNiSgfCi2VRA==
markerjs2@^2.21.1:
version "2.21.1"
resolved "https://registry.yarnpkg.com/markerjs2/-/markerjs2-2.21.1.tgz#a85d76ed00c56023e359e4b99d9c1a0e30f0eda4"
integrity sha512-JnN+NJMeZElfGrxb8+ZmSN3TCYrs2uVumW7WC/db4iDTgLhePMLn7vXumrYFyyoe6x0NhliOoDfKmCh63EucxQ==
material-colors@^1.2.1:
version "1.2.6"
@@ -9694,6 +9702,11 @@ normalize-url@^3.0.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
normalize-url@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-7.0.3.tgz#12e56889f7a54b2d5b09616f36c442a9063f61af"
integrity sha512-RiCOdwdPnzvwcBFJE4iI1ss3dMVRIrEzFpn8ftje6iBfzBInqlnRrNhxcLwBEKjPPXQKzm1Ptlxtaiv9wdcj5w==
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
@@ -11900,10 +11913,10 @@ react-grid-layout@^1.3.4:
react-draggable "^4.0.0"
react-resizable "^3.0.4"
react-i18next@^11.16.5:
version "11.16.5"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.16.5.tgz#5258784b9b617c443811307072a434ec7447c70a"
integrity sha512-dkDN9suVU/jaD9A0xM4qNU9++LwgueWZn5XGTDEKTZeBgsd3q17ssTQXlSbEb0Gl0iLDdUKU7MrdjQjDc33ztA==
react-i18next@^11.16.6:
version "11.16.6"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.16.6.tgz#e8a07802c391a55e1528673201a2727994787641"
integrity sha512-qa76GnvAPafNSxKNN/XMhdCkVN/9Lm+BpzW5+6FE2ctYUemhbglP7oklGmYiJXlG24p9itqzlJDbCi3SNd3jzA==
dependencies:
"@babel/runtime" "^7.14.5"
html-escaper "^2.0.2"
@@ -13987,7 +14000,7 @@ tslib@^1.8.1, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0:
tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
@@ -14151,7 +14164,7 @@ uncontrollable@^7.0.0:
invariant "^2.2.4"
react-lifecycles-compat "^3.0.4"
unfetch@^4.1.0:
unfetch@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==

View File

@@ -779,6 +779,13 @@
table:
schema: public
name: timetickets
- name: transitions
using:
foreign_key_constraint_on:
column: bodyshopid
table:
schema: public
name: transitions
- name: vehicles
using:
foreign_key_constraint_on:
@@ -828,6 +835,8 @@
- jc_hourly_rates
- jobsizelimit
- last_name_first
- localmediaserverhttp
- localmediaservernetwork
- logo_img_path
- md_categories
- md_ccc_rates
@@ -851,6 +860,7 @@
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- md_to_emails
- messagingservicesid
- pbs_configuration
- pbs_serialnumber
@@ -877,6 +887,7 @@
- tt_allow_post_to_invoiced
- updated_at
- use_fippa
- uselocalmediaserver
- website
- workingdays
- zip_post
@@ -914,6 +925,8 @@
- intakechecklist
- jc_hourly_rates
- last_name_first
- localmediaserverhttp
- localmediaservernetwork
- logo_img_path
- md_categories
- md_ccc_rates
@@ -937,6 +950,7 @@
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- md_to_emails
- pbs_configuration
- phone
- prodtargethrs
@@ -956,6 +970,7 @@
- tt_allow_post_to_invoiced
- updated_at
- use_fippa
- uselocalmediaserver
- website
- workingdays
- zip_post
@@ -1946,6 +1961,7 @@
- active
- created_at
- employee_number
- external_id
- first_name
- flat_rate
- hire_date
@@ -1964,6 +1980,7 @@
- active
- created_at
- employee_number
- external_id
- first_name
- flat_rate
- hire_date
@@ -1992,6 +2009,7 @@
- active
- created_at
- employee_number
- external_id
- first_name
- flat_rate
- hire_date
@@ -2596,6 +2614,13 @@
insertion_order: null
column_mapping:
id: jobid
- name: mixdata
using:
foreign_key_constraint_on:
column: jobid
table:
schema: public
name: mixdata
- name: notes
using:
foreign_key_constraint_on:
@@ -2645,6 +2670,13 @@
table:
schema: public
name: timetickets
- name: transitions
using:
foreign_key_constraint_on:
column: jobid
table:
schema: public
name: transitions
insert_permissions:
- role: user
permission:
@@ -3590,6 +3622,84 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
- table:
schema: public
name: mixdata
object_relationships:
- name: job
using:
foreign_key_constraint_on: jobid
insert_permissions:
- role: user
permission:
check:
job:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
columns:
- mixdata
- totalliquidcost
- totalsundrycost
- company
- version
- created_at
- updated_at
- id
- jobid
backend_only: false
select_permissions:
- role: user
permission:
columns:
- mixdata
- totalliquidcost
- totalsundrycost
- company
- version
- created_at
- updated_at
- id
- jobid
filter:
job:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
update_permissions:
- role: user
permission:
columns:
- mixdata
- totalliquidcost
- totalsundrycost
- company
- version
- created_at
- updated_at
- id
- jobid
filter:
job:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
check: null
- table:
schema: public
name: notes
@@ -3839,6 +3949,7 @@
- act_price
- backordered_eta
- backordered_on
- cm_received
- cost
- created_at
- db_price
@@ -3859,6 +3970,7 @@
- act_price
- backordered_eta
- backordered_on
- cm_received
- cost
- created_at
- db_price
@@ -3890,6 +4002,7 @@
- act_price
- backordered_eta
- backordered_on
- cm_received
- cost
- created_at
- db_price
@@ -4594,6 +4707,93 @@
_eq: X-Hasura-User-Id
- active:
_eq: true
- table:
schema: public
name: transitions
object_relationships:
- name: bodyshop
using:
foreign_key_constraint_on: bodyshopid
- name: job
using:
foreign_key_constraint_on: jobid
insert_permissions:
- role: user
permission:
check:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
columns:
- bodyshopid
- created_at
- duration
- end
- id
- jobid
- next_value
- prev_value
- start
- type
- updated_at
- value
backend_only: false
select_permissions:
- role: user
permission:
columns:
- duration
- next_value
- prev_value
- type
- value
- created_at
- end
- start
- updated_at
- bodyshopid
- id
- jobid
filter:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
update_permissions:
- role: user
permission:
columns:
- duration
- next_value
- prev_value
- type
- value
- created_at
- end
- start
- updated_at
- bodyshopid
- id
- jobid
filter:
bodyshop:
associations:
_and:
- active:
_eq: true
- user:
authid:
_eq: X-Hasura-User-Id
check: {}
- table:
schema: public
name: users

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"."parts_order_lines" add column "cm_received" boolean
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."parts_order_lines" add column "cm_received" boolean
null;

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