Compare commits
139 Commits
hotfix/202
...
feature/no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5a2be9392 | ||
|
|
df1e15be60 | ||
|
|
274b572915 | ||
|
|
fa4aa5ca8f | ||
|
|
2b537b65dd | ||
|
|
75b2cb18ca | ||
|
|
5167668958 | ||
|
|
1eacf43669 | ||
|
|
77ed64969e | ||
|
|
44ff032acc | ||
|
|
542637edb2 | ||
|
|
f7971ed60c | ||
|
|
ca71b0479a | ||
|
|
515de38fe6 | ||
|
|
113c62b36f | ||
|
|
c0ea5a9818 | ||
|
|
6b13dffe68 | ||
|
|
51d49be16e | ||
|
|
04c3abd65f | ||
|
|
29dab7312b | ||
|
|
01eb68fda1 | ||
|
|
a4c4329253 | ||
|
|
b738fc9007 | ||
|
|
5484358b32 | ||
|
|
b48d7512a2 | ||
|
|
ae3226efb8 | ||
|
|
da9ddfc419 | ||
|
|
901902c1d1 | ||
|
|
79bfb12063 | ||
|
|
c1bf112aac | ||
|
|
ecfb3e91cd | ||
|
|
e25606070b | ||
|
|
28f0d9a4b2 | ||
|
|
c125cd8ca2 | ||
|
|
123f94a0f5 | ||
|
|
d601617819 | ||
|
|
80bd2dc6d8 | ||
|
|
45bd2d3281 | ||
|
|
b4304c743e | ||
|
|
faf9fbb9d8 | ||
|
|
6d1581a4e1 | ||
|
|
28f6c72de1 | ||
|
|
54577ac680 | ||
|
|
c912681793 | ||
|
|
09f909142b | ||
|
|
89b515fff4 | ||
|
|
fb380a5b31 | ||
|
|
3896a0b03d | ||
|
|
832674662d | ||
|
|
f2b2011900 | ||
|
|
15c305317a | ||
|
|
d6924c2292 | ||
|
|
7ccd356f0a | ||
|
|
592b47b5d4 | ||
|
|
d9beee7f2f | ||
|
|
75743f44e7 | ||
|
|
3d68a7099b | ||
|
|
71f161ec27 | ||
|
|
9b1f24926f | ||
|
|
bcc153caa5 | ||
|
|
f59911d5ab | ||
|
|
417958e1e8 | ||
|
|
ccf2f0ad47 | ||
|
|
bb993ab1fb | ||
|
|
9c39c8d59b | ||
|
|
3439f09d9a | ||
|
|
279e93f0c3 | ||
|
|
92f14d6fa5 | ||
|
|
148c645f18 | ||
|
|
9a65b6a1ce | ||
|
|
839c82abb9 | ||
|
|
471756d7ad | ||
|
|
2e2a4920ca | ||
|
|
f775e09391 | ||
|
|
c0b0bcd55e | ||
|
|
1bc5493f3f | ||
|
|
2579558090 | ||
|
|
bc9a3a21a8 | ||
|
|
f11eb6406d | ||
|
|
3d6bad9e7d | ||
|
|
12a5f17351 | ||
|
|
a2032553d9 | ||
|
|
d22979dadc | ||
|
|
c1068ec92b | ||
|
|
a318f3e74b | ||
|
|
e5a5cb4e85 | ||
|
|
f8151e387e | ||
|
|
b98bfe566a | ||
|
|
c0220f0ca2 | ||
|
|
3e121a1a25 | ||
|
|
a2a8868223 | ||
|
|
80d16b4651 | ||
|
|
ce3fbab1dc | ||
|
|
4c1a333514 | ||
|
|
0660b79c01 | ||
|
|
63ec578b6a | ||
|
|
dcf388ff7c | ||
|
|
0cd1b41ed9 | ||
|
|
79124daa9a | ||
|
|
76ec55d709 | ||
|
|
375b8ba050 | ||
|
|
2192cb1e7c | ||
|
|
65b505035a | ||
|
|
191f3f96a2 | ||
|
|
14d873f795 | ||
|
|
55ad75df8a | ||
|
|
5f112e797d | ||
|
|
b842cee076 | ||
|
|
2c1c11828d | ||
|
|
e7c380d780 | ||
|
|
0958ea5ba6 | ||
|
|
8031f2b2ed | ||
|
|
066f395a40 | ||
|
|
51c5d163a5 | ||
|
|
d8ec6dd997 | ||
|
|
a3e8f56728 | ||
|
|
a6f2cfba0f | ||
|
|
da51aeb135 | ||
|
|
680dd98f6d | ||
|
|
22e6d596e6 | ||
|
|
1ba904d082 | ||
|
|
dc77930950 | ||
|
|
520b61706f | ||
|
|
09a87dd2a7 | ||
|
|
f884d2e23f | ||
|
|
b84935efdc | ||
|
|
b3aeee4f45 | ||
|
|
06f266a292 | ||
|
|
b34694f3c4 | ||
|
|
0dbb3a446a | ||
|
|
2409042450 | ||
|
|
f509ea07c0 | ||
|
|
e263c32d83 | ||
|
|
4a023faf67 | ||
|
|
90f0232ff0 | ||
|
|
e0e2183d86 | ||
|
|
bc504d2a78 | ||
|
|
7c66e5cb90 | ||
|
|
8745ffd08f |
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "off"
|
||||
},
|
||||
"settings": {},
|
||||
"plugins": ["cypress"]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<babeledit_project version="1.2" be_version="2.7.1">
|
||||
<babeledit_project be_version="2.7.1" version="1.2">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -2354,6 +2354,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>is_credit_memo_short</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>local_tax_rate</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -3633,6 +3654,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>bill_allow_post_to_closed</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>bill_federal_tax_rate</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -4367,6 +4409,27 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>last_name_first</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>lastnumberworkingdays</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -4388,6 +4451,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>logo_img_footer_margin</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_header_margin</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_path</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -4556,6 +4661,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>md_from_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>
|
||||
<folder_node>
|
||||
<name>md_hour_split</name>
|
||||
<children>
|
||||
@@ -4648,6 +4774,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>private</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>state</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -4755,6 +4902,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>md_parts_order_comment</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_payment_types</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -7238,6 +7406,32 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>ss_configuration</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>dailyhrslimit</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>ssbuckets</name>
|
||||
<children>
|
||||
@@ -8733,6 +8927,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>qbo_departmentid</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>qbo_usa</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>rbac</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -13163,6 +13399,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>from</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>subject</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -20119,6 +20376,27 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>auto_add_ats</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>ca_bc_pvrt</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -20581,6 +20859,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>date_rentalresp</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>date_scheduled</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -20602,6 +20901,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>date_towin</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>ded_amt</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -22979,6 +23299,27 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>rate_ats</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_la1</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -25204,6 +25545,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cost_sublet</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>costs</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -26416,6 +26778,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>parts_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>parts_tax_rates</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -27119,6 +27502,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>sale_sublet</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>sales</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -27329,6 +27733,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>threshhold</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>total_cost</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -29666,6 +30091,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>cancelallappointments</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>closejob</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -30974,6 +31420,37 @@
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>owner</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>labels</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>noownerinfo</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>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>owners</name>
|
||||
<children>
|
||||
@@ -32342,6 +32819,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>notyetordered</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>oec</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -34710,6 +35208,48 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>lag_time_ro</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>mechanical_authorization</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>mpi_animal_checklist</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -35691,6 +36231,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>exported_payroll</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>
|
||||
@@ -36230,6 +36791,58 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<folder_node>
|
||||
<name>bodyshop</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>labels</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>qbo_departmentid</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>qbo_usa</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>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>cardsettings</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -36587,6 +37200,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>stickyheader</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>sublets</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -38364,6 +38998,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>production_by_repair_status_one</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_by_ro</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -42660,6 +43315,63 @@
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>users</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>errors</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>signinerror</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>auth/user-not-found</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>auth/wrong-password</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>
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>vehicles</name>
|
||||
<children>
|
||||
@@ -43290,6 +44002,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>novehinfo</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>relatedjobs</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -3,83 +3,89 @@
|
||||
"version": "0.2.1",
|
||||
"private": true,
|
||||
"proxy": "http://localhost:4000",
|
||||
"browser": {
|
||||
"fs": false,
|
||||
"path": false,
|
||||
"os": false
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.5.6",
|
||||
"@apollo/client": "^3.5.10",
|
||||
"@asseinfo/react-kanban": "^2.2.0",
|
||||
"@craco/craco": "^6.4.3",
|
||||
"@fingerprintjs/fingerprintjs": "^3.3.1",
|
||||
"@sentry/react": "^6.16.1",
|
||||
"@sentry/tracing": "^6.16.1",
|
||||
"@splitsoftware/splitio-react": "^1.3.0",
|
||||
"@stripe/react-stripe-js": "^1.7.0",
|
||||
"@stripe/stripe-js": "^1.22.0",
|
||||
"@tanem/react-nprogress": "^3.0.82",
|
||||
"antd": "^4.17.4",
|
||||
"@fingerprintjs/fingerprintjs": "^3.3.3",
|
||||
"@jsreport/browser-client": "^3.1.0",
|
||||
"@sentry/react": "^6.19.6",
|
||||
"@sentry/tracing": "^6.19.6",
|
||||
"@splitsoftware/splitio-react": "^1.4.0",
|
||||
"@stripe/react-stripe-js": "^1.7.1",
|
||||
"@stripe/stripe-js": "^1.27.0",
|
||||
"@tanem/react-nprogress": "^5.0.0",
|
||||
"antd": "^4.19.5",
|
||||
"apollo-link-logger": "^2.0.0",
|
||||
"axios": "^0.24.0",
|
||||
"craco-less": "^1.20.0",
|
||||
"axios": "^0.26.1",
|
||||
"craco-less": "^2.0.0",
|
||||
"dinero.js": "^1.9.1",
|
||||
"dotenv": "^10.0.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"enquire-js": "^0.2.1",
|
||||
"env-cmd": "^10.1.0",
|
||||
"exifr": "^7.1.3",
|
||||
"firebase": "^9.6.1",
|
||||
"graphql": "^16.2.0",
|
||||
"i18next": "^21.6.3",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"jsoneditor": "^9.5.8",
|
||||
"jsreport-browser-client-dist": "^1.3.0",
|
||||
"libphonenumber-js": "^1.9.44",
|
||||
"logrocket": "^2.1.2",
|
||||
"markerjs2": "^2.17.2",
|
||||
"firebase": "^9.6.10",
|
||||
"graphql": "^16.3.0",
|
||||
"i18next": "^21.6.16",
|
||||
"i18next-browser-languagedetector": "^6.1.4",
|
||||
"jsoneditor": "^9.7.4",
|
||||
"libphonenumber-js": "^1.9.51",
|
||||
"logrocket": "^2.2.1",
|
||||
"markerjs2": "^2.21.0",
|
||||
"moment-business-days": "^1.2.0",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"phone": "^3.1.10",
|
||||
"phone": "^3.1.15",
|
||||
"preval.macro": "^5.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"query-string": "^7.0.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"query-string": "^7.1.1",
|
||||
"rc-queue-anim": "^2.0.0",
|
||||
"rc-scroll-anim": "^2.7.6",
|
||||
"react": "^17.0.2",
|
||||
"react-big-calendar": "^0.38.2",
|
||||
"react": "^18.0.0",
|
||||
"react-big-calendar": "^0.40.0",
|
||||
"react-color": "^2.19.3",
|
||||
"react-cookie": "^4.1.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-drag-listview": "^0.1.8",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-drag-listview": "^0.1.9",
|
||||
"react-grid-gallery": "^0.5.5",
|
||||
"react-grid-layout": "^1.3.0",
|
||||
"react-i18next": "^11.15.1",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
"react-i18next": "^11.16.6",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-number-format": "^4.9.0",
|
||||
"react-redux": "^7.2.6",
|
||||
"react-number-format": "^4.9.1",
|
||||
"react-redux": "^7.2.8",
|
||||
"react-resizable": "^3.0.4",
|
||||
"react-router-dom": "^5.3.0",
|
||||
"react-scripts": "^4.0.3",
|
||||
"react-sticky": "^6.0.3",
|
||||
"react-sublime-video": "^0.2.5",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"recharts": "^2.1.8",
|
||||
"recharts": "^2.1.9",
|
||||
"redux": "^4.1.2",
|
||||
"redux-persist": "^6.0.0",
|
||||
"redux-saga": "^1.1.3",
|
||||
"redux-state-sync": "^3.1.2",
|
||||
"reselect": "^4.1.5",
|
||||
"sass": "^1.45.0",
|
||||
"socket.io-client": "^4.4.0",
|
||||
"styled-components": "^5.3.3",
|
||||
"sass": "^1.50.0",
|
||||
"socket.io-client": "^4.4.1",
|
||||
"styled-components": "^5.3.5",
|
||||
"subscriptions-transport-ws": "^0.11.0",
|
||||
"web-vitals": "^2.1.2",
|
||||
"workbox-background-sync": "^6.4.2",
|
||||
"workbox-broadcast-update": "^6.4.2",
|
||||
"workbox-cacheable-response": "^6.4.2",
|
||||
"workbox-core": "^6.4.2",
|
||||
"workbox-expiration": "^6.4.2",
|
||||
"workbox-google-analytics": "^6.4.2",
|
||||
"workbox-navigation-preload": "^6.4.2",
|
||||
"workbox-precaching": "^6.4.2",
|
||||
"workbox-range-requests": "^6.4.2",
|
||||
"workbox-routing": "^6.4.2",
|
||||
"workbox-strategies": "^6.4.2",
|
||||
"workbox-streams": "^6.4.2",
|
||||
"web-vitals": "^2.1.4",
|
||||
"workbox-background-sync": "^6.5.3",
|
||||
"workbox-broadcast-update": "^6.5.3",
|
||||
"workbox-cacheable-response": "^6.5.3",
|
||||
"workbox-core": "^6.5.3",
|
||||
"workbox-expiration": "^6.5.3",
|
||||
"workbox-google-analytics": "^6.5.3",
|
||||
"workbox-navigation-preload": "^6.5.3",
|
||||
"workbox-precaching": "^6.5.3",
|
||||
"workbox-range-requests": "^6.5.3",
|
||||
"workbox-routing": "^6.5.3",
|
||||
"workbox-strategies": "^6.5.3",
|
||||
"workbox-streams": "^6.5.3",
|
||||
"yauzl": "^2.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -116,11 +122,11 @@
|
||||
"react-error-overlay": "6.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sentry/webpack-plugin": "^1.18.3",
|
||||
"@sentry/webpack-plugin": "^1.18.8",
|
||||
"@testing-library/cypress": "^8.0.2",
|
||||
"cypress": "^9.1.1",
|
||||
"cypress": "^9.5.4",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"react-error-overlay": "6.0.9",
|
||||
"react-error-overlay": "6.0.11",
|
||||
"redux-logger": "^3.0.6",
|
||||
"source-map-explorer": "^2.5.2"
|
||||
}
|
||||
|
||||
@@ -12,6 +12,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 OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -76,14 +77,12 @@ export function AccountingPayablesTableComponent({
|
||||
render: (text, record) => {
|
||||
return record.job.owner ? (
|
||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record.job} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record.job} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -162,10 +161,19 @@ export function AccountingPayablesTableComponent({
|
||||
const dataSource = state.search
|
||||
? payments.filter(
|
||||
(v) =>
|
||||
(v.vendor.name || "")
|
||||
(v.paymentnum || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
(v.invoice_number || "")
|
||||
((v.job && v.job.ro_number) || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
((v.job && v.job.ownr_fn) || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
((v.job && v.job.ownr_ln) || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase()) ||
|
||||
((v.job && v.job.ownr_co_nm) || "")
|
||||
.toLowerCase()
|
||||
.includes(state.search.toLowerCase())
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
|
||||
import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-button.component";
|
||||
|
||||
@@ -12,6 +12,9 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
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";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
@@ -62,6 +65,18 @@ export function AccountingReceivablesTableComponent({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.date_invoiced"),
|
||||
dataIndex: "date_invoiced",
|
||||
key: "date_invoiced",
|
||||
sorter: (a, b) => dateSort(a.date_invoiced, b.date_invoiced),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "date_invoiced" &&
|
||||
state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<DateFormatter>{record.date_invoiced}</DateFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.owner"),
|
||||
dataIndex: "owner",
|
||||
@@ -72,14 +87,12 @@ export function AccountingReceivablesTableComponent({
|
||||
render: (text, record) => {
|
||||
return record.owner ? (
|
||||
<Link to={"/manage/owners/" + record.owner.id}>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLocation, useHistory } from "react-router-dom";
|
||||
import {
|
||||
DELETE_BILL_LINE,
|
||||
INSERT_NEW_BILL_LINES,
|
||||
UPDATE_BILL_LINE,
|
||||
} from "../../graphql/bill-lines.queries";
|
||||
@@ -58,6 +59,7 @@ export function BillDetailEditcontainer({
|
||||
const [update_bill] = useMutation(UPDATE_BILL);
|
||||
const [insertBillLine] = useMutation(INSERT_NEW_BILL_LINES);
|
||||
const [updateBillLine] = useMutation(UPDATE_BILL_LINE);
|
||||
const [deleteBillLine] = useMutation(DELETE_BILL_LINE);
|
||||
|
||||
const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
|
||||
.filter((screen) => !!screen[1])
|
||||
@@ -107,6 +109,20 @@ export function BillDetailEditcontainer({
|
||||
})
|
||||
);
|
||||
|
||||
//Find bill lines that were deleted.
|
||||
const deletedJobLines = [];
|
||||
|
||||
data.bills_by_pk.billlines.forEach((a) => {
|
||||
const matchingRecord = billlines.find((b) => b.id === a.id);
|
||||
if (!matchingRecord) {
|
||||
deletedJobLines.push(a);
|
||||
}
|
||||
});
|
||||
|
||||
deletedJobLines.forEach((d) => {
|
||||
updates.push(deleteBillLine({ variables: { id: d.id } }));
|
||||
});
|
||||
|
||||
billlines.forEach((billline) => {
|
||||
const { deductedfromlbr, jobline, ...il } = billline;
|
||||
delete il.__typename;
|
||||
@@ -142,6 +158,7 @@ export function BillDetailEditcontainer({
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(updates);
|
||||
|
||||
insertAuditTrail({
|
||||
|
||||
@@ -114,7 +114,7 @@ export function BillFormComponent({
|
||||
<Form.Item
|
||||
label={t("bills.fields.vendor")}
|
||||
name="vendorid"
|
||||
style={{ display: billEdit ? "none" : null }}
|
||||
// style={{ display: billEdit ? "none" : null }}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
@@ -229,6 +229,7 @@ export function BillFormComponent({
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
if (
|
||||
!bodyshop.bill_allow_post_to_closed &&
|
||||
(job.status === bodyshop.md_ro_statuses.default_invoiced ||
|
||||
job.status === bodyshop.md_ro_statuses.default_exported ||
|
||||
job.status === bodyshop.md_ro_statuses.default_void) &&
|
||||
|
||||
@@ -7,6 +7,7 @@ import { selectSelectedConversation } from "../../redux/messaging/messaging.sele
|
||||
import { TimeAgoFormatter } from "../../utils/DateFormatter";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import "./chat-conversation-list.styles.scss";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
selectedConversation: selectSelectedConversation,
|
||||
@@ -40,9 +41,9 @@ export function ChatConversationListComponent({
|
||||
{item.job_conversations.length > 0 ? (
|
||||
<div className="chat-name">
|
||||
{item.job_conversations.map((j, idx) => (
|
||||
<div key={idx}>{`${j.job.ownr_fn || ""} ${
|
||||
j.job.ownr_ln || ""
|
||||
} ${j.job.ownr_co_nm || ""} `}</div>
|
||||
<div key={idx}>
|
||||
<OwnerNameDisplay ownerObject={j.job} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -4,6 +4,7 @@ import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { REMOVE_CONVERSATION_TAG } from "../../graphql/job-conversations.queries";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function ChatConversationTitleTags({ jobConversations }) {
|
||||
const [removeJobConversation] = useMutation(REMOVE_CONVERSATION_TAG);
|
||||
@@ -45,9 +46,8 @@ export default function ChatConversationTitleTags({ jobConversations }) {
|
||||
onClose={() => handleRemoveTag(item.job.id)}
|
||||
>
|
||||
<Link to={`/manage/jobs/${item.job.id}`}>
|
||||
{`${item.job.ro_number || "?"} | ${item.job.ownr_fn || ""} ${
|
||||
item.job.ownr_ln || ""
|
||||
} ${item.job.ownr_co_nm || ""}`}
|
||||
{`${item.job.ro_number || "?"} | `}
|
||||
<OwnerNameDisplay ownerObject={item.job} />
|
||||
</Link>
|
||||
</Tag>
|
||||
))}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { CloseCircleOutlined, LoadingOutlined } from "@ant-design/icons";
|
||||
import { Select, Empty, Space } from "antd";
|
||||
import { Empty, Select, Space } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function ChatTagRoComponent({
|
||||
roOptions,
|
||||
@@ -27,9 +28,7 @@ export default function ChatTagRoComponent({
|
||||
>
|
||||
{roOptions.map((item, idx) => (
|
||||
<Select.Option key={item.id || idx}>
|
||||
{` ${item.ro_number || ""} | ${item.ownr_fn || ""} ${
|
||||
item.ownr_ln || ""
|
||||
} ${item.ownr_co_nm || ""}`}
|
||||
{` ${item.ro_number || ""} | ${OwnerNameDisplayFunction(item)}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import DataLabel from "../data-label/data-label.component";
|
||||
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
export default function ContractJobBlock({ job }) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
@@ -23,9 +23,7 @@ export default function ContractJobBlock({ job }) {
|
||||
} ${(job && job.v_model_desc) || ""}`}
|
||||
</DataLabel>
|
||||
<DataLabel label={t("jobs.fields.owner")}>
|
||||
{`${(job && job.ownr_fn) || ""} ${(job && job.ownr_ln) || ""} ${
|
||||
(job && job.ownr_co_nm) || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={job} />
|
||||
</DataLabel>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function ContractsJobsComponent({
|
||||
loading,
|
||||
@@ -43,17 +44,7 @@ export default function ContractsJobsComponent({
|
||||
width: "25%",
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||
render: (text, record) => {
|
||||
return record.owner ? (
|
||||
<span>
|
||||
{record.ownr_fn} {record.ownr_ln} {record.ownr_co_nm || ""}
|
||||
</span>
|
||||
) : (
|
||||
<span>{`${record.ownr_fn} ${record.ownr_ln} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
);
|
||||
},
|
||||
render: (text, record) => <OwnerNameDisplay ownerObject={record} />,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
|
||||
@@ -12,17 +12,22 @@ import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import moment from "moment";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
setContractFinderContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "contractFinder" })),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ContractsList);
|
||||
|
||||
export function ContractsList({
|
||||
bodyshop,
|
||||
loading,
|
||||
contracts,
|
||||
refetch,
|
||||
@@ -72,7 +77,9 @@ export function ContractsList({
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "driver_ln" && state.sortedInfo.order,
|
||||
render: (text, record) =>
|
||||
`${record.driver_fn || ""} ${record.driver_ln || ""}`,
|
||||
bodyshop.last_name_first
|
||||
? `${record.driver_ln || ""}, ${record.driver_fn || ""}`
|
||||
: `${record.driver_fn || ""} ${record.driver_ln || ""}`,
|
||||
},
|
||||
{
|
||||
title: t("contracts.labels.vehicle"),
|
||||
|
||||
@@ -9,7 +9,7 @@ import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
|
||||
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
|
||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
|
||||
}
|
||||
/>
|
||||
|
||||
<FormFieldsChanged form={form} />
|
||||
{/* <FormFieldsChanged form={form} /> */}
|
||||
<LayoutFormRow header={t("courtesycars.labels.vehicle")}>
|
||||
<Form.Item
|
||||
label={t("courtesycars.fields.make")}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Table, Button, Input, Card, Space } from "antd";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, Input, Space, Table } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
@@ -97,7 +99,9 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||
render: (text, record) =>
|
||||
record.cccontracts.length === 1 ? (
|
||||
<Link to={`/manage/jobs/${record.cccontracts[0].job.id}`}>
|
||||
{record.cccontracts[0].job.ro_number}
|
||||
{`${
|
||||
record.cccontracts[0].job.ro_number
|
||||
} - ${OwnerNameDisplayFunction(record.cccontracts[0].job)}`}
|
||||
</Link>
|
||||
) : null,
|
||||
},
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function CsiResponseListPaginated({
|
||||
refetch,
|
||||
@@ -48,14 +49,12 @@ export default function CsiResponseListPaginated({
|
||||
render: (text, record) => {
|
||||
return record.job.owner ? (
|
||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record.job} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record.job} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -16,9 +16,14 @@ import EmailDocumentsComponent from "../email-documents/email-documents.componen
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
@@ -28,7 +33,12 @@ export default connect(
|
||||
mapDispatchToProps
|
||||
)(EmailOverlayComponent);
|
||||
|
||||
export function EmailOverlayComponent({ form, selectedMediaState, bodyshop }) {
|
||||
export function EmailOverlayComponent({
|
||||
form,
|
||||
selectedMediaState,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const handleClick = ({ item, key, keyPath }) => {
|
||||
const email = item.props.value;
|
||||
@@ -51,6 +61,27 @@ export function EmailOverlayComponent({ form, selectedMediaState, bodyshop }) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
label={t("emails.fields.from")}
|
||||
name="from"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
<Select.Option key={currentUser.email}>
|
||||
{currentUser.email}
|
||||
</Select.Option>
|
||||
<Select.Option key={bodyshop.email}>{bodyshop.email}</Select.Option>
|
||||
{bodyshop.md_from_emails &&
|
||||
bodyshop.md_from_emails.map((e) => (
|
||||
<Select.Option key={e}>{e}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={
|
||||
<Space>
|
||||
|
||||
@@ -56,13 +56,9 @@ export function EmailOverlayContainer({
|
||||
: bodyshop.shopname,
|
||||
address: EmailSettings.fromAddress,
|
||||
},
|
||||
ReplyTo: {
|
||||
Email: currentUser.validemail ? currentUser.email : bodyshop.email,
|
||||
Name: currentUser.displayName,
|
||||
},
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
const handleFinish = async (allValues) => {
|
||||
logImEXEvent("email_send_from_modal");
|
||||
|
||||
//const attachments = [];
|
||||
@@ -77,10 +73,15 @@ export function EmailOverlayContainer({
|
||||
// attachments.push(t);
|
||||
// });
|
||||
|
||||
const { from, ...values } = allValues;
|
||||
setSending(true);
|
||||
try {
|
||||
await axios.post("/sendemail", {
|
||||
...defaultEmailFrom,
|
||||
ReplyTo: {
|
||||
Email: from,
|
||||
Name: currentUser.displayName,
|
||||
},
|
||||
...values,
|
||||
html: rawHtml,
|
||||
attachments: [
|
||||
@@ -138,6 +139,7 @@ export function EmailOverlayContainer({
|
||||
}
|
||||
|
||||
form.setFieldsValue({
|
||||
from: currentUser.validemail ? currentUser.email : bodyshop.email,
|
||||
...emailConfig.messageOptions,
|
||||
cc:
|
||||
emailConfig.messageOptions.cc &&
|
||||
|
||||
@@ -30,16 +30,16 @@ const DateTimePicker = (
|
||||
/>
|
||||
|
||||
<TimePicker
|
||||
{...restProps}
|
||||
value={value ? moment(value) : null}
|
||||
{...(onlyFuture && {
|
||||
disabledDate: (d) => moment().isAfter(d),
|
||||
})}
|
||||
onChange={onChange}
|
||||
showSecond={false}
|
||||
minuteStep={15}
|
||||
onBlur={onBlur}
|
||||
format="hh:mm a"
|
||||
value={value ? moment(value) : null}
|
||||
{...(onlyFuture && {
|
||||
disabledDate: (d) => moment().isAfter(d),
|
||||
})}
|
||||
onChange={onChange}
|
||||
showSecond={false}
|
||||
minuteStep={15}
|
||||
onBlur={onBlur}
|
||||
format="hh:mm a"
|
||||
{...restProps}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import { useLazyQuery } from "@apollo/client";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
import { AutoComplete, Divider, Space } from "antd";
|
||||
import _ from "lodash";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import { GLOBAL_SEARCH_QUERY } from "../../graphql/search.queries";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
export default function GlobalSearch() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [callSearch, { error, data }] = useLazyQuery(GLOBAL_SEARCH_QUERY);
|
||||
const history = useHistory();
|
||||
const [callSearch, { loading, error, data }] =
|
||||
useLazyQuery(GLOBAL_SEARCH_QUERY);
|
||||
|
||||
const executeSearch = (v) => {
|
||||
if (v && v.variables.search && v.variables.search !== "") callSearch(v);
|
||||
@@ -39,9 +43,9 @@ export default function GlobalSearch() {
|
||||
<Space size="small" split={<Divider type="vertical" />}>
|
||||
<strong>{job.ro_number || t("general.labels.na")}</strong>
|
||||
<span>{`${job.status || ""}`}</span>
|
||||
<span>{`${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
||||
job.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={job} />
|
||||
</span>
|
||||
<span>{`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${
|
||||
job.v_model_desc || ""
|
||||
}`}</span>
|
||||
@@ -57,15 +61,13 @@ export default function GlobalSearch() {
|
||||
options: data.search_owners.map((owner) => {
|
||||
return {
|
||||
key: owner.id,
|
||||
value: `${owner.ownr_fn || ""} ${owner.ownr_ln || ""} ${
|
||||
owner.ownr_co_nm || ""
|
||||
}`,
|
||||
value: OwnerNameDisplayFunction(owner),
|
||||
label: (
|
||||
<Link to={`/manage/owners/${owner.id}`}>
|
||||
<Space size="small" split={<Divider type="vertical" />} wrap>
|
||||
<span>{`${owner.ownr_fn || ""} ${owner.ownr_ln || ""} ${
|
||||
owner.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={owner} />
|
||||
</span>
|
||||
<PhoneNumberFormatter>
|
||||
{owner.ownr_ph1}
|
||||
</PhoneNumberFormatter>
|
||||
@@ -171,8 +173,13 @@ export default function GlobalSearch() {
|
||||
<AutoComplete
|
||||
options={options}
|
||||
onSearch={handleSearch}
|
||||
suffixIcon={loading && <LoadingOutlined spin />}
|
||||
defaultActiveFirstOption
|
||||
placeholder={t("general.labels.globalsearch")}
|
||||
allowClear
|
||||
onSelect={(val, opt) => {
|
||||
history.push(opt.label.props.to);
|
||||
}}
|
||||
></AutoComplete>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.
|
||||
import ScheduleAtChange from "./job-at-change.component";
|
||||
import ScheduleEventColor from "./schedule-event.color.component";
|
||||
import ScheduleEventNote from "./schedule-event.note.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -73,9 +74,9 @@ export function ScheduleEventComponent({
|
||||
</Space>
|
||||
) : (
|
||||
<Space>
|
||||
<strong>{`${(event.job && event.job.ownr_fn) || ""} ${
|
||||
(event.job && event.job.ownr_ln) || ""
|
||||
}`}</strong>
|
||||
<strong>
|
||||
<OwnerNameDisplay ownerObject={event.job} />
|
||||
</strong>
|
||||
<span style={{ margin: 4 }}>
|
||||
{`${(event.job && event.job.v_model_yr) || ""} ${
|
||||
(event.job && event.job.v_make_desc) || ""
|
||||
@@ -256,9 +257,9 @@ export function ScheduleEventComponent({
|
||||
<Space>
|
||||
{event.note && <AlertFilled className="production-alert" />}
|
||||
<strong>{`${event.job.ro_number || t("general.labels.na")}`}</strong>
|
||||
<span>{`${(event.job && event.job.ownr_fn) || ""} ${
|
||||
(event.job && event.job.ownr_ln) || ""
|
||||
} ${(event.job && event.job.ownr_co_nm) || ""}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={event.job} />
|
||||
</span>
|
||||
</Space>
|
||||
<Space>
|
||||
<span>
|
||||
|
||||
@@ -64,9 +64,9 @@ export default function JobBillsTotalComponent({
|
||||
})
|
||||
);
|
||||
|
||||
const totalPartsSublet = Dinero(totals.parts.parts.total).add(
|
||||
Dinero(totals.parts.sublets.total)
|
||||
);
|
||||
const totalPartsSublet = Dinero(totals.parts.parts.total)
|
||||
.add(Dinero(totals.parts.sublets.total))
|
||||
.add(Dinero(totals.additional.towing));
|
||||
|
||||
const discrepancy = totalPartsSublet.subtract(billTotals);
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ export default function JobCostingStatistics({ summaryData }) {
|
||||
value={Dinero(summaryData.totalPartsSales).toFormat()}
|
||||
title={t("jobs.labels.sale_parts")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalSubletSales).toFormat()}
|
||||
title={t("jobs.labels.sale_sublet")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalAdditionalSales).toFormat()}
|
||||
title={t("jobs.labels.sale_additional")}
|
||||
@@ -32,6 +36,10 @@ export default function JobCostingStatistics({ summaryData }) {
|
||||
value={Dinero(summaryData.totalPartsCost).toFormat()}
|
||||
title={t("jobs.labels.cost_parts")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalSubletCost).toFormat()}
|
||||
title={t("jobs.labels.cost_sublet")}
|
||||
/>
|
||||
<Statistic
|
||||
value={Dinero(summaryData.totalAdditionalCost).toFormat()}
|
||||
title={t("jobs.labels.cost_Additional")}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Row, Col, Timeline, Typography, Space, Divider, Skeleton } from "antd";
|
||||
import React from "react";
|
||||
import { GET_JOB_LINE_ORDERS } from "../../graphql/jobs.queries";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import { DateFormatter } from "../../utils/DateFormatter";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function JobLinesExpander({ jobline, jobid }) {
|
||||
const { t } = useTranslation();
|
||||
const { loading, error, data } = useQuery(GET_JOB_LINE_ORDERS, {
|
||||
fetchPolicy: "network-only",
|
||||
nextFetchPolicy: "network-only",
|
||||
variables: {
|
||||
joblineid: jobline.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (loading) return <Skeleton />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
return (
|
||||
<Row>
|
||||
<Col md={24} lg={12}>
|
||||
<Typography.Title level={4}>
|
||||
{t("parts_orders.labels.parts_orders")}
|
||||
</Typography.Title>
|
||||
<Timeline>
|
||||
{data.parts_order_lines.length > 0 ? (
|
||||
data.parts_order_lines.map((line) => (
|
||||
<Timeline.Item key={line.id}>
|
||||
<Space split={<Divider type="vertical" />} wrap>
|
||||
<Link
|
||||
to={`/manage/jobs/${jobid}?partsorderid=${line.parts_order.id}`}
|
||||
>
|
||||
{line.parts_order.order_number}
|
||||
</Link>
|
||||
<DateFormatter>{line.parts_order.order_date}</DateFormatter>
|
||||
{line.parts_order.vendor.name}
|
||||
</Space>
|
||||
</Timeline.Item>
|
||||
))
|
||||
) : (
|
||||
<Timeline.Item>
|
||||
{t("parts_orders.labels.notyetordered")}
|
||||
</Timeline.Item>
|
||||
)}
|
||||
</Timeline>
|
||||
</Col>
|
||||
<Col md={24} lg={12}></Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
SyncOutlined,
|
||||
WarningFilled,
|
||||
EditFilled,
|
||||
PlusCircleTwoTone,
|
||||
MinusCircleTwoTone,
|
||||
} from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import {
|
||||
@@ -38,9 +40,11 @@ import JobLinesBillRefernece from "../job-lines-bill-reference/job-lines-bill-re
|
||||
import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.container";
|
||||
import _ from "lodash";
|
||||
import JobCreateIOU from "../job-create-iou/job-create-iou.component";
|
||||
import JobLinesExpander from "./job-lines-expander.component";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
jobRO: selectJobReadOnly,
|
||||
technician: selectTechnician,
|
||||
});
|
||||
@@ -53,6 +57,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
|
||||
export function JobLinesComponent({
|
||||
bodyshop,
|
||||
jobRO,
|
||||
technician,
|
||||
setPartsOrderContext,
|
||||
@@ -72,6 +77,9 @@ export function JobLinesComponent({
|
||||
filteredInfo: {},
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const jobIsPrivate = bodyshop.md_ins_cos.find(
|
||||
(c) => c.name === job.ins_co_nm
|
||||
)?.private;
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@@ -283,7 +291,7 @@ export function JobLinesComponent({
|
||||
key: "actions",
|
||||
render: (text, record) => (
|
||||
<div>
|
||||
{record.manual_line && (
|
||||
{(record.manual_line || jobIsPrivate) && (
|
||||
<Space>
|
||||
<Button
|
||||
disabled={jobRO}
|
||||
@@ -449,6 +457,19 @@ export function JobLinesComponent({
|
||||
scroll={{
|
||||
x: true,
|
||||
}}
|
||||
expandable={{
|
||||
expandedRowRender: (record) => (
|
||||
<JobLinesExpander jobline={record} jobid={job.id} />
|
||||
),
|
||||
rowExpandable: (record) => true,
|
||||
//expandRowByClick: true,
|
||||
expandIcon: ({ expanded, onExpand, record }) =>
|
||||
expanded ? (
|
||||
<MinusCircleTwoTone onClick={(e) => onExpand(record, e)} />
|
||||
) : (
|
||||
<PlusCircleTwoTone onClick={(e) => onExpand(record, e)} />
|
||||
),
|
||||
}}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onDoubleClick: (event) => {
|
||||
@@ -462,7 +483,7 @@ export function JobLinesComponent({
|
||||
};
|
||||
}}
|
||||
rowSelection={{
|
||||
selectedRowKeys: selectedLines.map((item) => item.id),
|
||||
selectedRowKeys: selectedLines.map((item) => item && item.id),
|
||||
onSelectAll: (selected, selectedRows, changeRows) => {
|
||||
setSelectedLines(selectedRows);
|
||||
},
|
||||
|
||||
@@ -55,15 +55,17 @@ export function JobEmployeeAssignments({
|
||||
0
|
||||
}
|
||||
>
|
||||
{bodyshop.employees.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
{bodyshop.employees
|
||||
.filter((emp) => emp.active)
|
||||
.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function JobLinesBillRefernece({ jobline }) {
|
||||
{subletRequired && <WarningFilled />}
|
||||
{`${(billLine.actual_price * billLine.quantity).toFixed(2)} (${
|
||||
billLine.bill.vendor.name
|
||||
})`}
|
||||
} #${billLine.bill.invoice_number})`}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -216,6 +216,7 @@ export function JobLinesUpsertModalComponent({
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
console.log(value);
|
||||
if (!value || getFieldValue("part_type") !== "PAE") {
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -226,7 +227,10 @@ export function JobLinesUpsertModalComponent({
|
||||
}),
|
||||
({ getFieldValue }) => ({
|
||||
validator(rule, value) {
|
||||
if (!!getFieldValue("part_type") === !!value) {
|
||||
console.log(value, !!value);
|
||||
if (
|
||||
!!getFieldValue("part_type") === (!!value || value === 0)
|
||||
) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject(
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function JobReconciliationBillsTable({
|
||||
dataIndex: "line_desc",
|
||||
key: "line_desc",
|
||||
ellipsis: true,
|
||||
minWidth: "65rem",
|
||||
width: "10rem",
|
||||
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
|
||||
@@ -72,11 +72,11 @@ export default function JobReconciliationBillsTable({
|
||||
state.sortedInfo.columnKey === "quantity" && state.sortedInfo.order,
|
||||
},
|
||||
{
|
||||
title: t("bills.fields.is_credit_memo"),
|
||||
title: t("bills.fields.is_credit_memo_short"),
|
||||
dataIndex: "is_credit_memo",
|
||||
key: "is_credit_memo",
|
||||
sorter: (a, b) => a.bill.is_credit_memo - b.bill.is_credit_memo,
|
||||
width: "8rem",
|
||||
width: "3rem",
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "is_credit_memo" &&
|
||||
state.sortedInfo.order,
|
||||
|
||||
@@ -18,7 +18,11 @@ export default function JobReconciliationModalComponent({ job, bills }) {
|
||||
.flat() || [];
|
||||
|
||||
const jobLineData = job.joblines.filter(
|
||||
(j) => j.part_type !== null && j.part_type !== "PAE"
|
||||
(j) =>
|
||||
(j.part_type !== null && j.part_type !== "PAE") ||
|
||||
(j.line_desc &&
|
||||
j.line_desc.toLowerCase().includes("towing") &&
|
||||
j.lbr_op === "OP13")
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SEARCH_JOBS_FOR_AUTOCOMPLETE,
|
||||
} from "../../graphql/jobs.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
const { Option } = Select;
|
||||
|
||||
const JobSearchSelect = (
|
||||
@@ -86,11 +87,9 @@ const JobSearchSelect = (
|
||||
<span>
|
||||
{`${clm_no && o.clm_no ? `${o.clm_no} | ` : ""}${
|
||||
o.ro_number || t("general.labels.na")
|
||||
} | ${o.ownr_ln || ""} ${o.ownr_fn || ""} ${
|
||||
o.ownr_co_nm ? ` ${o.ownr_co_num}` : ""
|
||||
}| ${o.v_model_yr || ""} ${o.v_make_desc || ""} ${
|
||||
o.v_model_desc || ""
|
||||
}`}
|
||||
} | ${OwnerNameDisplayFunction(o)} | ${
|
||||
o.v_model_yr || ""
|
||||
} ${o.v_make_desc || ""} ${o.v_model_desc || ""}`}
|
||||
</span>
|
||||
<Tag>
|
||||
<strong>{o.status}</strong>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Table } from "antd";
|
||||
import { Space, Table } from "antd";
|
||||
import Dinero from "dinero.js";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -119,7 +119,18 @@ export default function JobTotalsTableLabor({ job }) {
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
<Table.Summary.Row>
|
||||
<Table.Summary.Cell>{t("jobs.labels.mapa")}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell>
|
||||
<Space>
|
||||
{t("jobs.labels.mapa")}
|
||||
{job.materials &&
|
||||
job.materials.mapa &&
|
||||
job.materials.mapa.cal_maxdlr &&
|
||||
job.materials.mapa.cal_maxdlr > 0 &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.mapa.cal_maxdlr,
|
||||
})}
|
||||
</Space>
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell align="right">
|
||||
<CurrencyFormatter>
|
||||
{job.job_totals.rates.mapa.rate}
|
||||
@@ -133,7 +144,18 @@ export default function JobTotalsTableLabor({ job }) {
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
<Table.Summary.Row>
|
||||
<Table.Summary.Cell>{t("jobs.labels.mash")}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell>
|
||||
<Space wrap>
|
||||
{t("jobs.labels.mash")}
|
||||
{job.materials &&
|
||||
job.materials.mash &&
|
||||
job.materials.mash.cal_maxdlr &&
|
||||
job.materials.mash.cal_maxdlr > 0 &&
|
||||
t("jobs.labels.threshhold", {
|
||||
amount: job.materials.mash.cal_maxdlr,
|
||||
})}
|
||||
</Space>
|
||||
</Table.Summary.Cell>
|
||||
<Table.Summary.Cell align="right">
|
||||
<CurrencyFormatter>
|
||||
{job.job_totals.rates.mash.rate}
|
||||
|
||||
@@ -33,10 +33,10 @@ export default function JobTotalsTableOther({ job }) {
|
||||
key: t("jobs.fields.storage_payable"),
|
||||
total: job.job_totals.additional.storage,
|
||||
},
|
||||
{
|
||||
key: t("jobs.fields.ca_bc_pvrt"),
|
||||
total: job.job_totals.additional.pvrt,
|
||||
},
|
||||
// {
|
||||
// key: t("jobs.fields.ca_bc_pvrt"),
|
||||
// total: job.job_totals.additional.pvrt,
|
||||
// },
|
||||
];
|
||||
}, [job.job_totals, t]);
|
||||
|
||||
|
||||
@@ -3,7 +3,22 @@ import Dinero from "dinero.js";
|
||||
import React, { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function JobTotalsTableTotals({ job }) {
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobTotalsTableTotals);
|
||||
|
||||
export function JobTotalsTableTotals({ bodyshop, job }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const data = useMemo(() => {
|
||||
@@ -21,6 +36,14 @@ export default function JobTotalsTableTotals({ job }) {
|
||||
key: t("jobs.labels.state_tax_amt"),
|
||||
total: job.job_totals.totals.state_tax,
|
||||
},
|
||||
...(bodyshop.region_config === "CA_BC"
|
||||
? [
|
||||
{
|
||||
key: t("jobs.fields.ca_bc_pvrt"),
|
||||
total: job.job_totals.additional.pvrt,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: t("jobs.labels.federal_tax_amt"),
|
||||
total: job.job_totals.totals.federal_tax,
|
||||
@@ -58,7 +81,7 @@ export default function JobTotalsTableTotals({ job }) {
|
||||
bold: true,
|
||||
},
|
||||
];
|
||||
}, [job.job_totals, t]);
|
||||
}, [job.job_totals, t, bodyshop.region_config]);
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
||||
@@ -58,6 +58,15 @@ export default function JobsAdminDatesChange({ job }) {
|
||||
>
|
||||
<FormDatePicker format="MM/DD/YYYY" />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_towin")} name="date_towin">
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.date_rentalresp")}
|
||||
name="date_rentalresp"
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_open")} name="date_open">
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
|
||||
@@ -37,7 +37,6 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
|
||||
});
|
||||
|
||||
//Wahtever is left in the existing lines, are lines that should be removed.
|
||||
|
||||
const insertQueries = linesToInsert.reduce((acc, value, idx) => {
|
||||
return acc + generateInsertQuery(value, idx, jobId);
|
||||
}, "");
|
||||
@@ -49,6 +48,13 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
|
||||
const removeQueries = existingLines.reduce((acc, value, idx) => {
|
||||
return acc + generateRemoveQuery(value, idx);
|
||||
}, "");
|
||||
console.log(insertQueries, updateQueries, removeQueries);
|
||||
|
||||
if ((insertQueries + updateQueries + removeQueries).trim() === "") {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(gql`
|
||||
|
||||
@@ -220,12 +220,13 @@ export function JobsAvailableContainer({
|
||||
);
|
||||
|
||||
delete supp.joblines;
|
||||
await client.mutate({
|
||||
mutation: gql`
|
||||
${suppDelta}
|
||||
`,
|
||||
});
|
||||
|
||||
if (suppDelta !== null) {
|
||||
await client.mutate({
|
||||
mutation: gql`
|
||||
${suppDelta}
|
||||
`,
|
||||
});
|
||||
}
|
||||
const updateResult = await updateJob({
|
||||
variables: {
|
||||
jobId: selectedJob,
|
||||
|
||||
@@ -18,7 +18,7 @@ import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
|
||||
import axios from "axios";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -53,6 +53,12 @@ export function JobsConvertButton({
|
||||
variables: { jobId: job.id, ...values },
|
||||
});
|
||||
|
||||
if (values.ca_gst_registrant) {
|
||||
await axios.post("/job/totalsssu", {
|
||||
id: job.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (!res.errors) {
|
||||
refetch();
|
||||
notification["success"]({
|
||||
|
||||
@@ -39,6 +39,12 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
|
||||
<Form.Item label={t("jobs.fields.date_open")} name="date_open">
|
||||
<DateTimePicker disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_towin")} name="date_towin">
|
||||
<DateTimePicker disabled={jobRO} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.date_rentalresp")} name="date_rentalresp">
|
||||
<DateTimePicker disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
|
||||
<FormRow header={t("jobs.forms.scheddates")}>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
Col,
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
@@ -23,8 +22,8 @@ import FormItemPhone, {
|
||||
} from "../form-items-formatted/phone-form-item.component";
|
||||
import Car from "../job-damage-visual/job-damage-visual.component";
|
||||
import JobsDetailChangeEstimator from "../jobs-detail-change-estimator/jobs-detail-change-estimator.component";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
import JobsDetailChangeFileHandler from "../jobs-detail-change-filehandler/jobs-detail-change-filehandler.component";
|
||||
import FormRow from "../layout-form-row/layout-form-row.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -220,15 +219,8 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider
|
||||
orientation="left"
|
||||
type="horizontal"
|
||||
style={{ marginTop: ".8rem", float: "right" }}
|
||||
>
|
||||
{t("jobs.forms.appraiserinfo")}
|
||||
</Divider>
|
||||
|
||||
<FormRow noDivider>
|
||||
<FormRow header={t("jobs.forms.appraiserinfo")}>
|
||||
<Form.Item label={t("jobs.fields.est_co_nm")} name="est_co_nm">
|
||||
<Input disabled={jobRO} />
|
||||
</Form.Item>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { connect } from "react-redux";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { CANCEL_APPOINTMENTS_BY_JOB_ID } from "../../graphql/appointments.queries";
|
||||
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
|
||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
@@ -56,6 +57,7 @@ export function JobsDetailHeaderActions({
|
||||
const [deleteJob] = useMutation(DELETE_JOB);
|
||||
const [updateJob] = useMutation(UPDATE_JOB);
|
||||
const [voidJob] = useMutation(VOID_JOB);
|
||||
const [cancelAllAppointments] = useMutation(CANCEL_APPOINTMENTS_BY_JOB_ID);
|
||||
const jobInProduction = useMemo(() => {
|
||||
return bodyshop.md_ro_statuses.production_statuses.includes(job.status);
|
||||
}, [job, bodyshop.md_ro_statuses.production_statuses]);
|
||||
@@ -121,6 +123,39 @@ export function JobsDetailHeaderActions({
|
||||
>
|
||||
{t("jobs.actions.schedule")}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
>
|
||||
<Popconfirm
|
||||
title={t("general.labels.areyousure")}
|
||||
okText="Yes"
|
||||
cancelText="No"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
disabled={job.status !== bodyshop.md_ro_statuses.default_scheduled}
|
||||
onConfirm={async () => {
|
||||
const jobUpdate = await cancelAllAppointments({
|
||||
variables: {
|
||||
jobid: job.id,
|
||||
job: {
|
||||
date_scheduled: null,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
status: bodyshop.md_ro_statuses.default_imported,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!jobUpdate.errors) {
|
||||
notification["success"]({
|
||||
message: t("appointments.successes.canceled"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
getPopupContainer={(trigger) => trigger.parentNode}
|
||||
>
|
||||
{t("menus.jobsactions.cancelallappointments")}
|
||||
</Popconfirm>
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
disabled={
|
||||
!!job.intakechecklist ||
|
||||
@@ -401,6 +436,9 @@ export function JobsDetailHeaderActions({
|
||||
job: {
|
||||
status: bodyshop.md_ro_statuses.default_void,
|
||||
voided: true,
|
||||
scheduled_in: null,
|
||||
scheduled_completion: null,
|
||||
inproduction: false,
|
||||
},
|
||||
note: [
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@ import "./jobs-detail-header.styles.scss";
|
||||
import JobsRelatedRos from "../jobs-related-ros/jobs-related-ros.component";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly,
|
||||
@@ -60,6 +61,12 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
);
|
||||
}, [job.status, bodyshop.md_ro_statuses.post_production_statuses]);
|
||||
|
||||
const vehicleTitle = `${job.v_model_yr || ""} ${job.v_color || ""}
|
||||
${job.v_make_desc || ""}
|
||||
${job.v_model_desc || ""}`.trim();
|
||||
|
||||
const ownerTitle = OwnerNameDisplayFunction(job).trim();
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]} style={{ alignItems: "stretch" }}>
|
||||
<Col {...colSpan}>
|
||||
@@ -152,9 +159,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
style={{ height: "100%" }}
|
||||
title={
|
||||
<Link to={disabled ? "#" : `/manage/owners/${job.owner.id}`}>
|
||||
{`${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
|
||||
job.ownr_co_nm || ""
|
||||
}`}
|
||||
{ownerTitle.length > 0
|
||||
? ownerTitle
|
||||
: t("owner.labels.noownerinfo")}
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
@@ -188,9 +195,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
|
||||
: job.vehicle && `/manage/vehicles/${job.vehicle.id}`
|
||||
}
|
||||
>
|
||||
{`${job.v_model_yr || ""} ${job.v_color || ""}
|
||||
${job.v_make_desc || ""}
|
||||
${job.v_model_desc || ""}`}
|
||||
{vehicleTitle.length > 0
|
||||
? vehicleTitle
|
||||
: t("vehicles.labels.novehinfo")}
|
||||
</Link>
|
||||
) : (
|
||||
<span></span>
|
||||
|
||||
@@ -88,6 +88,33 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
||||
</Form.Item>
|
||||
<CABCpvrtCalculator form={form} disabled={jobRO} />
|
||||
</Space>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.auto_add_ats")}
|
||||
name="auto_add_ats"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch disabled={jobRO} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
nostyle
|
||||
shouldUpdate={(prev, cur) => prev.auto_add_ats !== cur.auto_add_ats}
|
||||
>
|
||||
{() => {
|
||||
if (form.getFieldValue("auto_add_ats"))
|
||||
return (
|
||||
<Form.Item
|
||||
label={t("jobs.fields.rate_ats")}
|
||||
name="rate_ats"
|
||||
initialValue={bodyshop.shoprates.rate_atp}
|
||||
>
|
||||
<CurrencyInput disabled={jobRO} />
|
||||
</Form.Item>
|
||||
);
|
||||
|
||||
return null;
|
||||
}}
|
||||
</Form.Item>
|
||||
</FormRow>
|
||||
<FormRow>
|
||||
<Form.Item
|
||||
@@ -100,7 +127,13 @@ export function JobsDetailRates({ jobRO, form, job, bodyshop }) {
|
||||
label={t("jobs.fields.state_tax_rate")}
|
||||
name="state_tax_rate"
|
||||
>
|
||||
<InputNumber min={0} max={1} precision={2} disabled={jobRO} autoComplete="new-password"/>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={1}
|
||||
precision={2}
|
||||
disabled={jobRO}
|
||||
autoComplete="new-password"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("jobs.fields.local_tax_rate")}
|
||||
|
||||
@@ -4,6 +4,7 @@ import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function JobsFindModalComponent({
|
||||
selectedJob,
|
||||
@@ -43,15 +44,12 @@ export default function JobsFindModalComponent({
|
||||
render: (text, record) => {
|
||||
return record.owner ? (
|
||||
<Link to={"/manage/owners/" + record.owner.id}>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
// t("jobs.errors.noowner")
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import StartChatButton from "../chat-open-button/chat-open-button.component";
|
||||
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -52,14 +52,12 @@ export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link to={"/manage/owners/" + record.ownerid}>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -18,6 +18,7 @@ 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,
|
||||
@@ -141,14 +142,12 @@ export function JobsList({ bodyshop }) {
|
||||
to={"/manage/owners/" + record.owner.id}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -115,7 +115,6 @@ export function JobNotesComponent({
|
||||
<EditFilled />
|
||||
</Button>
|
||||
<PrintWrapperComponent
|
||||
emailOnly
|
||||
templateObject={{
|
||||
name: Templates.individual_job_note.key,
|
||||
|
||||
@@ -124,7 +123,7 @@ export function JobNotesComponent({
|
||||
messageObject={{
|
||||
subject: Templates.individual_job_note.subject,
|
||||
}}
|
||||
id={record.id}
|
||||
id={jobId}
|
||||
/>
|
||||
</Space>
|
||||
),
|
||||
|
||||
@@ -6,6 +6,7 @@ import { QUERY_SEARCH_OWNER_BY_IDX } from "../../graphql/owners.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import OwnerFindModalComponent from "./owner-find-modal.component";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function OwnerFindModalContainer({
|
||||
loading,
|
||||
@@ -30,9 +31,7 @@ export default function OwnerFindModalContainer({
|
||||
|
||||
useEffect(() => {
|
||||
if (modalProps.visible && owner) {
|
||||
const s = `${owner.ownr_fn || ""} ${owner.ownr_ln || ""} ${
|
||||
owner.ownr_co_nm || ""
|
||||
}`;
|
||||
const s = OwnerNameDisplayFunction(owner);
|
||||
|
||||
setSearchText(s.trim());
|
||||
callSearchowners({ variables: { search: s.trim() } });
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { store } from "../../redux/store";
|
||||
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)(OwnerNameDisplay);
|
||||
|
||||
export function OwnerNameDisplay({ bodyshop, ownerObject }) {
|
||||
const emptyTest =
|
||||
ownerObject.ownr_fn + ownerObject.ownr_ln + ownerObject.ownr_co_nm;
|
||||
|
||||
if (!emptyTest || emptyTest === "null" || emptyTest.trim() === "")
|
||||
return "N/A";
|
||||
|
||||
if (bodyshop.last_name_first)
|
||||
return `${ownerObject.ownr_ln || ""}, ${ownerObject.ownr_fn || ""} ${
|
||||
ownerObject.ownr_co_nm || ""
|
||||
}`.trim();
|
||||
|
||||
return `${ownerObject.ownr_fn || ""} ${ownerObject.ownr_ln || ""} ${
|
||||
ownerObject.ownr_co_nm || ""
|
||||
}`.trim();
|
||||
}
|
||||
|
||||
export function OwnerNameDisplayFunction(ownerObject) {
|
||||
const emptyTest =
|
||||
ownerObject.ownr_fn + ownerObject.ownr_ln + ownerObject.ownr_co_nm;
|
||||
|
||||
if (!emptyTest || emptyTest === "null" || emptyTest.trim() === "")
|
||||
return "N/A";
|
||||
|
||||
const rdxStore = store.getState();
|
||||
|
||||
if (rdxStore.user.bodyshop.last_name_first)
|
||||
return `${ownerObject.ownr_ln || ""}, ${ownerObject.ownr_fn || ""} ${
|
||||
ownerObject.ownr_co_nm || ""
|
||||
}`.trim();
|
||||
|
||||
return `${ownerObject.ownr_fn || ""} ${ownerObject.ownr_ln || ""} ${
|
||||
ownerObject.ownr_co_nm || ""
|
||||
}`.trim();
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
SEARCH_OWNERS_FOR_AUTOCOMPLETE,
|
||||
} from "../../graphql/owners.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
@@ -16,10 +17,8 @@ const OwnerSearchSelect = ({ value, onChange, onBlur, disabled }, ref) => {
|
||||
SEARCH_OWNERS_FOR_AUTOCOMPLETE
|
||||
);
|
||||
|
||||
const [
|
||||
callIdSearch,
|
||||
{ loading: idLoading, error: idError, data: idData },
|
||||
] = useLazyQuery(SEARCH_OWNERS_BY_ID_FOR_AUTOCOMPLETE);
|
||||
const [callIdSearch, { loading: idLoading, error: idError, data: idData }] =
|
||||
useLazyQuery(SEARCH_OWNERS_BY_ID_FOR_AUTOCOMPLETE);
|
||||
|
||||
const executeSearch = (v) => {
|
||||
callSearch(v);
|
||||
@@ -78,9 +77,7 @@ const OwnerSearchSelect = ({ value, onChange, onBlur, disabled }, ref) => {
|
||||
{theOptions
|
||||
? theOptions.map((o) => (
|
||||
<Option key={o.id} value={o.id}>
|
||||
{`${o.ownr_ln || ""} ${o.ownr_fn || ""} ${
|
||||
o.ownr_co_nm ? ` ${o.ownr_co_num}` : ""
|
||||
}| ${o.ownr_addr1 || ""} `}
|
||||
{`${OwnerNameDisplayFunction(o)} | ${o.ownr_addr1 || ""} `}
|
||||
</Option>
|
||||
))
|
||||
: null}
|
||||
|
||||
@@ -3,6 +3,10 @@ import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link } from "react-router-dom";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import OwnerNameDisplay, {
|
||||
OwnerNameDisplayFunction,
|
||||
} from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function OwnerTagPopoverComponent({ job }) {
|
||||
const { t } = useTranslation();
|
||||
const content = (
|
||||
@@ -10,9 +14,9 @@ export default function OwnerTagPopoverComponent({ job }) {
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Descriptions title={t("owners.labels.fromclaim")} column={1}>
|
||||
<Descriptions.Item key="1" label={t("jobs.fields.owner")}>{`${
|
||||
job.ownr_fn || ""
|
||||
} ${job.ownr_ln || ""} ${job.ownr_co_nm || ""}`}</Descriptions.Item>
|
||||
<Descriptions.Item key="1" label={t("jobs.fields.owner")}>
|
||||
<OwnerNameDisplay ownerObject={job} />
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item key="2" label={t("jobs.fields.ownr_ph1")}>
|
||||
<PhoneFormatter>{job.ownr_ph1 || ""}</PhoneFormatter>
|
||||
</Descriptions.Item>
|
||||
@@ -31,11 +35,9 @@ export default function OwnerTagPopoverComponent({ job }) {
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Descriptions title={t("owners.labels.fromowner")} column={1}>
|
||||
<Descriptions.Item key="1" label={t("jobs.fields.owner")}>{`${
|
||||
job.owner.ownr_fn || ""
|
||||
} ${job.owner.ownr_ln || ""} ${
|
||||
job.owner.ownr_co_nm || ""
|
||||
}`}</Descriptions.Item>
|
||||
<Descriptions.Item key="1" label={t("jobs.fields.owner")}>
|
||||
<OwnerNameDisplay ownerObject={job.owner} />
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item key="2" label={t("jobs.fields.ownr_ph1")}>
|
||||
<PhoneFormatter>{job.owner.ownr_ph1 || ""}</PhoneFormatter>
|
||||
</Descriptions.Item>
|
||||
@@ -68,9 +70,7 @@ export default function OwnerTagPopoverComponent({ job }) {
|
||||
<Popover placement="bottom" content={content}>
|
||||
<Tag color="cyan">
|
||||
<Link to={`/manage/owners/${job.owner.id}`}>
|
||||
{job.owner
|
||||
? `${job.ownr_co_nm || ""}${job.ownr_fn || ""} ${job.ownr_ln || ""}`
|
||||
: t("jobs.errors.noowner")}
|
||||
{job.owner ? OwnerNameDisplayFunction(job) : t("jobs.errors.noowner")}
|
||||
</Link>
|
||||
</Tag>
|
||||
</Popover>
|
||||
|
||||
@@ -5,6 +5,7 @@ import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Link, useHistory, useLocation } from "react-router-dom";
|
||||
import PhoneFormatter from "../../utils/PhoneFormatter";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function OwnersListComponent({
|
||||
loading,
|
||||
@@ -33,9 +34,7 @@ export default function OwnersListComponent({
|
||||
key: "name",
|
||||
render: (text, record) => (
|
||||
<Link to={"/manage/owners/" + record.id}>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { notification } from "antd";
|
||||
import axios from "axios";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setPartnerVersion } from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
import {store} from '../../redux/store'
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -21,45 +18,48 @@ export default connect(
|
||||
mapDispatchToProps
|
||||
)(PartnerPingComponent);
|
||||
|
||||
export function PartnerPingComponent({ bodyshop, setPartnerVersion }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
export function PartnerPingComponent({ bodyshop, }) {
|
||||
useEffect(() => {
|
||||
// Create an scoped async function in the hook
|
||||
async function checkPartnerStatus() {
|
||||
if (!bodyshop) return;
|
||||
try {
|
||||
//if (process.env.NODE_ENV === "development") return;
|
||||
const PartnerResponse = await axios.post("http://localhost:1337/ping/");
|
||||
const { appver, qbpath } = PartnerResponse.data;
|
||||
setPartnerVersion(appver);
|
||||
console.log({ appver, qbpath });
|
||||
if (
|
||||
!qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
)
|
||||
) {
|
||||
notification["error"]({
|
||||
title: "",
|
||||
message: t("general.messages.noacctfilepath"),
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
notification["error"]({
|
||||
title: "",
|
||||
message: t("general.messages.partnernotrunning"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the created function directly
|
||||
checkPartnerStatus();
|
||||
checkPartnerStatus(bodyshop);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bodyshop]);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
export async function checkPartnerStatus(bodyshop) {
|
||||
if (!bodyshop) return;
|
||||
try {
|
||||
//if (process.env.NODE_ENV === "development") return;
|
||||
const PartnerResponse = await axios.post("http://localhost:1337/ping/");
|
||||
// const {
|
||||
// appver, //qbpath
|
||||
// } = PartnerResponse.data;
|
||||
console.log(PartnerResponse.data)
|
||||
store.dispatch(setPartnerVersion(PartnerResponse.data));
|
||||
// if (
|
||||
// checkAcctPath &&
|
||||
// !qbpath &&
|
||||
// !(
|
||||
// bodyshop &&
|
||||
// (bodyshop.cdk_dealerid ||
|
||||
// bodyshop.pbs_serialnumber ||
|
||||
// bodyshop.accountingconfig.qbo)
|
||||
// )
|
||||
// ) {
|
||||
// notification["error"]({
|
||||
// title: "",
|
||||
// message: i18n.t("general.messages.noacctfilepath"),
|
||||
// });
|
||||
// }
|
||||
} catch (error) {
|
||||
console.log("ImEX Online Partner is not running.", error);
|
||||
// notification["error"]({
|
||||
// title: "",
|
||||
// message: i18n.t("general.messages.partnernotrunning"),
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,8 +425,6 @@ export function PartsOrderListTableComponent({
|
||||
placement="right"
|
||||
onClose={() => handleOnRowClick(null)}
|
||||
visible={selectedpartsorder}
|
||||
//getContainer={false}
|
||||
style={{ position: "absolute" }}
|
||||
closable
|
||||
width={drawerPercentage}
|
||||
>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DeleteFilled, WarningFilled } from "@ant-design/icons";
|
||||
import { DeleteFilled, WarningFilled, DownOutlined } from "@ant-design/icons";
|
||||
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||
import {
|
||||
Divider,
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
Space,
|
||||
Tag,
|
||||
Select,
|
||||
Menu,
|
||||
Dropdown,
|
||||
} from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -39,6 +41,7 @@ export function PartsOrderModalComponent({
|
||||
isReturn,
|
||||
preferredMake,
|
||||
job,
|
||||
form,
|
||||
}) {
|
||||
const [sendType, setSendType] = sendTypeState;
|
||||
const { OEConnection } = useTreatments(
|
||||
@@ -52,6 +55,21 @@ export function PartsOrderModalComponent({
|
||||
bodyshop.imexshopid
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
const handleClick = ({ item, key, keyPath }) => {
|
||||
form.setFieldsValue({ comments: item.props.value });
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<div>
|
||||
<Menu onClick={handleClick}>
|
||||
{bodyshop.md_parts_order_comment.map((comment, idx) => (
|
||||
<Menu.Item value={comment.comment} key={idx}>
|
||||
{comment.label}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -73,6 +91,7 @@ export function PartsOrderModalComponent({
|
||||
options={vendorList}
|
||||
disabled={isReturn}
|
||||
preferredMake={preferredMake}
|
||||
showPhone
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
@@ -242,7 +261,23 @@ export function PartsOrderModalComponent({
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
<Form.Item name="comments" label={t("parts_orders.fields.comments")}>
|
||||
<Form.Item
|
||||
name="comments"
|
||||
label={
|
||||
<Space>
|
||||
{t("parts_orders.fields.comments")}
|
||||
<Dropdown overlay={menu}>
|
||||
<a
|
||||
className="ant-dropdown-link"
|
||||
href=" #"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
>
|
||||
<DownOutlined />
|
||||
</a>
|
||||
</Dropdown>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Input.TextArea rows={3} />
|
||||
</Form.Item>
|
||||
<Radio.Group
|
||||
|
||||
@@ -196,6 +196,11 @@ export function PartsOrderModalContainer({
|
||||
(item) => item.id === values.vendorid
|
||||
)[0];
|
||||
|
||||
let vendorEmails =
|
||||
matchingVendor &&
|
||||
matchingVendor.email &&
|
||||
matchingVendor.email.split(RegExp("[;,]"));
|
||||
|
||||
GenerateDocument(
|
||||
{
|
||||
name: isReturn
|
||||
@@ -206,7 +211,7 @@ export function PartsOrderModalContainer({
|
||||
},
|
||||
},
|
||||
{
|
||||
to: matchingVendor ? [matchingVendor.email] : null,
|
||||
to: matchingVendor ? vendorEmails : null,
|
||||
replyTo: bodyshop.email,
|
||||
subject: isReturn
|
||||
? Templates.parts_return_slip.subject
|
||||
@@ -341,6 +346,7 @@ export function PartsOrderModalContainer({
|
||||
<LoadingSpinner />
|
||||
) : (
|
||||
<PartsOrderModalComponent
|
||||
form={form}
|
||||
vendorList={(data && data.vendors) || []}
|
||||
sendTypeState={sendTypeState}
|
||||
isReturn={isReturn}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { alphaSort } from "../../utils/sorters";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table-modal.container";
|
||||
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const stripeTestEnv = process.env.REACT_APP_STRIPE_PUBLIC_KEY; //.includes("test");
|
||||
|
||||
@@ -78,14 +79,12 @@ export function PaymentsListPaginated({
|
||||
render: (text, record) => {
|
||||
return record.job.owner ? (
|
||||
<Link to={"/manage/owners/" + record.job.owner.id}>
|
||||
{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.job.ownr_fn || ""} ${record.job.ownr_ln || ""} ${
|
||||
record.job.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import ProductionListColumnProductionNote from "../production-list-columns/produ
|
||||
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
|
||||
import "./production-board-card.styles.scss";
|
||||
import moment from "moment";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
export default function ProductionBoardCard(
|
||||
technician,
|
||||
@@ -22,7 +23,7 @@ export default function ProductionBoardCard(
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
let employee_body, employee_prep, employee_refinish; //employee_csr;
|
||||
let employee_body, employee_prep, employee_refinish, employee_csr;
|
||||
if (card.employee_body) {
|
||||
employee_body = bodyshop.employees.find((e) => e.id === card.employee_body);
|
||||
}
|
||||
@@ -34,6 +35,9 @@ export default function ProductionBoardCard(
|
||||
(e) => e.id === card.employee_refinish
|
||||
);
|
||||
}
|
||||
if (card.employee_csr) {
|
||||
employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
|
||||
}
|
||||
// if (card.employee_csr) {
|
||||
// employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
|
||||
// }
|
||||
@@ -58,20 +62,22 @@ export default function ProductionBoardCard(
|
||||
<PauseCircleOutlined style={{ color: "orangered" }} />
|
||||
)}
|
||||
<span style={{ fontWeight: "bolder" }}>
|
||||
{card.ro_number || t("general.labels.na")}
|
||||
<Link
|
||||
to={
|
||||
technician
|
||||
? `/tech/joblookup?selected=${card.id}`
|
||||
: `/manage/jobs/${card.id}`
|
||||
}
|
||||
>
|
||||
{card.ro_number || t("general.labels.na")}
|
||||
</Link>
|
||||
</span>
|
||||
</Space>
|
||||
}
|
||||
extra={
|
||||
technician ? (
|
||||
<Link to={`/tech/joblookup?selected=${card.id}`}>
|
||||
<EyeFilled />
|
||||
</Link>
|
||||
) : (
|
||||
<Link to={`/manage/jobs/${card.id}`}>
|
||||
<EyeFilled />
|
||||
</Link>
|
||||
)
|
||||
<Link to={{ search: `?selected=${card.id}` }}>
|
||||
<EyeFilled />
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
<Row>
|
||||
@@ -82,9 +88,9 @@ export default function ProductionBoardCard(
|
||||
card.ownr_co_nm || ""
|
||||
}`}</div>
|
||||
) : (
|
||||
<div className="ellipses">{`${card.ownr_ln || ""}, ${
|
||||
card.ownr_fn || ""
|
||||
} ${card.ownr_co_nm || ""}`}</div>
|
||||
<div className="ellipses">
|
||||
<OwnerNameDisplay ownerObject={card} />
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
)}
|
||||
@@ -131,11 +137,11 @@ export default function ProductionBoardCard(
|
||||
)} ${employee_refinish.last_name.charAt(0)}`
|
||||
: ""
|
||||
} ${card.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
|
||||
{/* <Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
|
||||
employee_csr
|
||||
? `${employee_csr.first_name} ${employee_csr.last_name}`
|
||||
: ""
|
||||
}`}</Col> */}
|
||||
}`}</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
@@ -131,6 +131,13 @@ export default function ProductionBoardKanbanCardSettings({
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
valuePropName="checked"
|
||||
label={t("production.labels.stickyheader")}
|
||||
name="stickyheader"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { useApolloClient } from "@apollo/client";
|
||||
import Board, { moveCard } from "@asseinfo/react-kanban";
|
||||
//import "@asseinfo/react-kanban/dist/styles.css";
|
||||
import "./production-board-kanban.styles.scss";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { Grid, notification, Button, PageHeader, Space, Statistic } from "antd";
|
||||
import { Button, Grid, notification, PageHeader, Space, Statistic } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Sticky, StickyContainer } from "react-sticky";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import ProductionBoardCard from "../production-board-kanban-card/production-board-kanban-card.component";
|
||||
import { createBoardData } from "./production-board-kanban.utils.js";
|
||||
import IndefiniteLoading from "../indefinite-loading/indefinite-loading.component";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import ProductionBoardKanbanCardSettings from "./production-board-kanban.card-settings.component";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { generate_UPDATE_JOB_KANBAN } from "../../graphql/jobs.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import IndefiniteLoading from "../indefinite-loading/indefinite-loading.component";
|
||||
import ProductionBoardFilters from "../production-board-filters/production-board-filters.component";
|
||||
import ProductionBoardCard from "../production-board-kanban-card/production-board-kanban-card.component";
|
||||
import ProductionListDetailComponent from "../production-list-detail/production-list-detail.component";
|
||||
import ProductionBoardKanbanCardSettings from "./production-board-kanban.card-settings.component";
|
||||
//import "@asseinfo/react-kanban/dist/styles.css";
|
||||
import "./production-board-kanban.styles.scss";
|
||||
import { createBoardData } from "./production-board-kanban.utils.js";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician,
|
||||
@@ -183,9 +184,52 @@ export function ProductionBoardKanbanComponent({
|
||||
: standardSizes[selectedBreakpoint[0]]
|
||||
: "250";
|
||||
|
||||
const stickyHeader = {
|
||||
renderColumnHeader: ({ title }) => (
|
||||
<Sticky>
|
||||
{({
|
||||
style,
|
||||
|
||||
// the following are also available but unused in this example
|
||||
isSticky,
|
||||
wasSticky,
|
||||
distanceFromTop,
|
||||
distanceFromBottom,
|
||||
calculatedHeight,
|
||||
}) => (
|
||||
<div
|
||||
className="react-kanban-column-header"
|
||||
style={{ ...style, zIndex: "99", backgroundColor: "#ddd" }}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
)}
|
||||
</Sticky>
|
||||
),
|
||||
};
|
||||
|
||||
const cardSettings =
|
||||
associationSettings &&
|
||||
associationSettings.kanban_settings &&
|
||||
Object.keys(associationSettings.kanban_settings).length > 0
|
||||
? associationSettings.kanban_settings
|
||||
: {
|
||||
ats: true,
|
||||
clm_no: true,
|
||||
compact: false,
|
||||
ownr_nm: true,
|
||||
sublets: true,
|
||||
ins_co_nm: true,
|
||||
production_note: true,
|
||||
employeeassignments: true,
|
||||
scheduled_completion: true,
|
||||
stickyheader: false,
|
||||
};
|
||||
|
||||
return (
|
||||
<Container width={width}>
|
||||
<IndefiniteLoading loading={isMoving} />
|
||||
|
||||
<PageHeader
|
||||
title={
|
||||
<Space>
|
||||
@@ -215,34 +259,19 @@ export function ProductionBoardKanbanComponent({
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
|
||||
<Board
|
||||
children={boardLanes}
|
||||
disableCardDrag={isMoving}
|
||||
renderCard={(card) =>
|
||||
ProductionBoardCard(
|
||||
technician,
|
||||
card,
|
||||
bodyshop,
|
||||
associationSettings &&
|
||||
associationSettings.kanban_settings &&
|
||||
Object.keys(associationSettings.kanban_settings).length > 0
|
||||
? associationSettings.kanban_settings
|
||||
: {
|
||||
ats: true,
|
||||
clm_no: true,
|
||||
compact: false,
|
||||
ownr_nm: true,
|
||||
sublets: true,
|
||||
ins_co_nm: true,
|
||||
production_note: true,
|
||||
employeeassignments: true,
|
||||
scheduled_completion: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
onCardDragEnd={handleDragEnd}
|
||||
/>
|
||||
<ProductionListDetailComponent jobs={data} />
|
||||
<StickyContainer>
|
||||
<Board
|
||||
style={{ height: "100%" }}
|
||||
children={boardLanes}
|
||||
disableCardDrag={isMoving}
|
||||
{...(cardSettings.stickyheader && stickyHeader)}
|
||||
renderCard={(card) =>
|
||||
ProductionBoardCard(technician, card, bodyshop, cardSettings)
|
||||
}
|
||||
onCardDragEnd={handleDragEnd}
|
||||
/>
|
||||
</StickyContainer>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ export const createBoardData = (AllStatuses, Jobs, filter) => {
|
||||
include ||
|
||||
j.employee_body === employeeId ||
|
||||
j.employee_prep === employeeId ||
|
||||
j.employee_csr === employeeId ||
|
||||
j.employee_refinish === employeeId;
|
||||
}
|
||||
|
||||
@@ -76,9 +77,8 @@ export const createBoardData = (AllStatuses, Jobs, filter) => {
|
||||
|
||||
Object.keys(DataGroupedByStatus).map((statusGroupKey) => {
|
||||
try {
|
||||
boardLanes.columns.find(
|
||||
(l) => l.id === statusGroupKey
|
||||
).cards = sortByParentId(DataGroupedByStatus[statusGroupKey]);
|
||||
boardLanes.columns.find((l) => l.id === statusGroupKey).cards =
|
||||
sortByParentId(DataGroupedByStatus[statusGroupKey]);
|
||||
} catch (error) {
|
||||
console.log("Error while creating board card", error);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import ProductionListColumnStatus from "./production-list-columns.status.compone
|
||||
import ProductionListColumnCategory from "./production-list-columns.status.category";
|
||||
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||
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";
|
||||
|
||||
const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
return [
|
||||
@@ -67,11 +69,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
dataIndex: "ownr",
|
||||
key: "ownr",
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
),
|
||||
render: (text, record) => <OwnerNameDisplay ownerObject={record} />,
|
||||
sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
|
||||
@@ -81,6 +79,13 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
dataIndex: "vehicle",
|
||||
key: "vehicle",
|
||||
ellipsis: true,
|
||||
sorter: (a, b) =>
|
||||
alphaSort(
|
||||
a.v_make_desc + a.v_model_desc,
|
||||
b.v_make_desc + b.v_model_desc
|
||||
),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "ownr" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<span>{`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${
|
||||
record.v_model_desc || ""
|
||||
@@ -96,7 +101,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "actual_in" && state.sortedInfo.order,
|
||||
render: (text, record) => (
|
||||
<ProductionListDate record={record} field="actual_in" time/>
|
||||
<ProductionListDate record={record} field="actual_in" time />
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -477,6 +482,14 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: i18n.t("jobs.labels.parts_received"),
|
||||
dataIndex: "parts_received",
|
||||
key: "parts_received",
|
||||
render: (text, record) => (
|
||||
<ProductionListColumnPartsReceived record={record} />
|
||||
),
|
||||
},
|
||||
];
|
||||
};
|
||||
export default r;
|
||||
|
||||
@@ -74,6 +74,7 @@ export default function ProductionListDate({
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
value={(record[field] && moment(record[field])) || null}
|
||||
onChange={handleChange}
|
||||
minuteStep={15}
|
||||
format="hh:mm a"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -116,15 +116,17 @@ export function ProductionListEmpAssignment({
|
||||
0
|
||||
}
|
||||
>
|
||||
{bodyshop.employees.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
{bodyshop.employees
|
||||
.filter((emp) => emp.active)
|
||||
.map((emp) => (
|
||||
<Select.Option
|
||||
value={emp.id}
|
||||
key={emp.id}
|
||||
name={`${emp.first_name} ${emp.last_name}`}
|
||||
>
|
||||
{`${emp.first_name} ${emp.last_name}`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { useMemo } from "react";
|
||||
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
|
||||
)(ProductionListColumnPartsReceived);
|
||||
|
||||
export function ProductionListColumnPartsReceived({ bodyshop, record }) {
|
||||
const amount = useMemo(() => {
|
||||
const amount = record.joblines_status.reduce(
|
||||
(acc, val) => {
|
||||
acc.total += val.count;
|
||||
acc.received =
|
||||
val.status === bodyshop.md_order_statuses.default_received
|
||||
? acc.received + val.count
|
||||
: acc.received;
|
||||
return acc;
|
||||
},
|
||||
{ total: 0, received: 0 }
|
||||
);
|
||||
|
||||
return {
|
||||
...amount,
|
||||
percent:
|
||||
amount.total !== 0
|
||||
? ((amount.received / amount.total) * 100).toFixed(0) + "%"
|
||||
: "N/A",
|
||||
};
|
||||
}, [record, bodyshop.md_order_statuses]);
|
||||
|
||||
return `${amount.percent} (${amount.received}/${amount.total})`;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@apollo/client";
|
||||
import { Descriptions, Drawer, Space } from "antd";
|
||||
import { Descriptions, Drawer, Space, PageHeader, Button } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -16,8 +16,25 @@ import JobEmployeeAssignments from "../job-employee-assignments/job-employee-ass
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import ProductionRemoveButton from "../production-remove-button/production-remove-button.component";
|
||||
import JobAtChange from "../job-at-change/job-at-change.component";
|
||||
import { PrinterFilled } from "@ant-design/icons";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||
|
||||
export default function ProductionListDetail({ jobs }) {
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setPrintCenterContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "printCenter" })),
|
||||
});
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ProductionListDetail);
|
||||
|
||||
export function ProductionListDetail({ jobs, setPrintCenterContext }) {
|
||||
const search = queryString.parse(useLocation().search);
|
||||
const history = useHistory();
|
||||
const { selected } = search;
|
||||
@@ -39,11 +56,29 @@ export default function ProductionListDetail({ jobs }) {
|
||||
return (
|
||||
<Drawer
|
||||
title={
|
||||
<Space>
|
||||
<span>{t("production.labels.jobdetail")}</span>
|
||||
<span>{theJob.ro_number}</span>
|
||||
<ProductionRemoveButton jobId={theJob.id} />
|
||||
</Space>
|
||||
<PageHeader
|
||||
title={theJob.ro_number}
|
||||
extra={
|
||||
<Space>
|
||||
<ProductionRemoveButton jobId={theJob.id} />{" "}
|
||||
<Button
|
||||
onClick={() => {
|
||||
setPrintCenterContext({
|
||||
actions: { refetch: refetch },
|
||||
context: {
|
||||
id: theJob.id,
|
||||
job: theJob,
|
||||
type: "job",
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
<PrinterFilled />
|
||||
{t("jobs.actions.printCenter")}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
}
|
||||
placement="right"
|
||||
width={"33%"}
|
||||
@@ -72,9 +107,7 @@ export default function ProductionListDetail({ jobs }) {
|
||||
{theJob.ins_co_nm || ""}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t("jobs.fields.owner")}>
|
||||
{`${theJob.ownr_fn || ""} ${theJob.ownr_ln || ""} ${
|
||||
theJob.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={theJob} />
|
||||
<StartChatButton
|
||||
phone={data.jobs_by_pk.ownr_ph1}
|
||||
jobid={data.jobs_by_pk.id}
|
||||
|
||||
@@ -8,8 +8,11 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
const ProdTemplates = TemplateList("production");
|
||||
const { production_by_technician_one, production_by_category_one } =
|
||||
TemplateList("special");
|
||||
const {
|
||||
production_by_technician_one,
|
||||
production_by_category_one,
|
||||
production_by_repair_status_one,
|
||||
} = TemplateList("special");
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -95,6 +98,29 @@ export function ProductionListPrint({ bodyshop }) {
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu
|
||||
title={t("reportcenter.templates.production_by_repair_status_one")}
|
||||
>
|
||||
{bodyshop.md_ro_statuses.production_statuses.map((e) => (
|
||||
<Menu.Item
|
||||
key={e}
|
||||
onClick={async () => {
|
||||
setLoading(true);
|
||||
await GenerateDocument(
|
||||
{
|
||||
name: production_by_repair_status_one.key,
|
||||
variables: { status: e },
|
||||
},
|
||||
{},
|
||||
"p"
|
||||
);
|
||||
setLoading(false);
|
||||
}}
|
||||
>
|
||||
{e}
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu.SubMenu>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -88,12 +88,6 @@ export function ProductionListTable({
|
||||
);
|
||||
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
console.log(
|
||||
"🚀 ~ file: production-list-table.component.jsx ~ line 91 ~ pagination, filters, sorter",
|
||||
pagination,
|
||||
filters,
|
||||
sorter
|
||||
);
|
||||
setState({
|
||||
...state,
|
||||
filteredInfo: filters,
|
||||
|
||||
@@ -9,7 +9,7 @@ import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
|
||||
import "./schedule-production-list.styles.scss";
|
||||
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
export default function ScheduleProductionList() {
|
||||
const { t } = useTranslation();
|
||||
const [callQuery, { loading, error, data }] = useLazyQuery(
|
||||
@@ -36,9 +36,7 @@ export default function ScheduleProductionList() {
|
||||
<td>
|
||||
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
||||
</td>
|
||||
<td>{`${j.ownr_fn || ""} ${j.ownr_ln || ""} ${
|
||||
j.ownr_co_nm || ""
|
||||
}`}</td>
|
||||
<td><OwnerNameDisplay ownerObject={j} /></td>
|
||||
<td>{`${j.v_model_yr || ""} ${j.v_make_desc || ""} ${
|
||||
j.v_model_desc || ""
|
||||
}`}</td>
|
||||
|
||||
@@ -101,7 +101,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.dailyactual")}
|
||||
value={values.todayBody}
|
||||
value={values.todayBody.toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
@@ -116,7 +116,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.weeklyactual")}
|
||||
value={values.weeklyBody}
|
||||
value={values.weeklyBody.toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
@@ -140,7 +140,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
title={t("scoreboard.labels.todateactual")}
|
||||
value={values.toDateBody}
|
||||
value={values.toDateBody.toFixed(1)}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -152,7 +152,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic value={values.todayPaint} />
|
||||
<Statistic value={values.todayPaint.toFixed(1)} />
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
@@ -163,7 +163,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic value={values.weeklyPaint} />
|
||||
<Statistic value={values.weeklyPaint.toFixed(1)} />
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic
|
||||
@@ -182,7 +182,7 @@ export function ScoreboardTargetsTable({ bodyshop, scoreBoardlist }) {
|
||||
/>
|
||||
</Col>
|
||||
<Col {...statSpans}>
|
||||
<Statistic value={values.toDatePaint} />
|
||||
<Statistic value={values.toDatePaint.toFixed(1)} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
@@ -142,6 +142,18 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.logo_img_header_margin")}
|
||||
name={["logo_img_path", "headerMargin"]}
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.logo_img_footer_margin")}
|
||||
name={["logo_img_path", "footerMargin"]}
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")}>
|
||||
<Form.Item
|
||||
@@ -151,6 +163,27 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{() => (
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.qbo_usa")}
|
||||
shouldUpdate
|
||||
valuePropName="checked"
|
||||
name={["accountingconfig", "qbo_usa"]}
|
||||
>
|
||||
<Switch
|
||||
disabled={!form.getFieldValue(["accountingconfig", "qbo"])}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.qbo_departmentid")}
|
||||
name={["accountingconfig", "qbo_departmentid"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.labels.accountingtiers")}
|
||||
rules={[
|
||||
@@ -487,7 +520,6 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name={["attach_pdf_to_email"]}
|
||||
label={t("bodyshop.fields.attach_pdf_to_email")}
|
||||
@@ -495,6 +527,18 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_from_emails"]}
|
||||
label={t("bodyshop.fields.md_from_emails")}
|
||||
// rules={[
|
||||
// {
|
||||
// //message: t("general.validation.required"),
|
||||
// type: "array",
|
||||
// },
|
||||
// ]}
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_email_cc", "parts_order"]}
|
||||
label={t("bodyshop.fields.md_email_cc", { template: "parts_order" })}
|
||||
@@ -514,6 +558,13 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["bill_allow_post_to_closed"]}
|
||||
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_ded_notes"]}
|
||||
label={t("bodyshop.fields.md_ded_notes")}
|
||||
@@ -526,6 +577,13 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["last_name_first"]}
|
||||
label={t("bodyshop.fields.last_name_first")}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.labels.messagingpresets")}>
|
||||
<Form.List name={["md_messaging_presets"]}>
|
||||
@@ -760,14 +818,23 @@ export default function ShopInfoGeneral({ form }) {
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Space>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_ins_co.zip")}
|
||||
key={`${index}zip`}
|
||||
name={[field.name, "zip"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_ins_co.zip")}
|
||||
key={`${index}zip`}
|
||||
name={[field.name, "zip"]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("bodyshop.fields.md_ins_co.private")}
|
||||
key={`${index}private`}
|
||||
name={[field.name, "private"]}
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
@@ -1260,6 +1327,72 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow header={t("bodyshop.fields.md_parts_order_comment")}>
|
||||
<Form.List name={["md_parts_order_comment"]}>
|
||||
{(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"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("parts_orders.fields.comments")}
|
||||
key={`${index}comment`}
|
||||
name={[field.name, "comment"]}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
//message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Space wrap>
|
||||
<DeleteFilled
|
||||
onClick={() => {
|
||||
remove(field.name);
|
||||
}}
|
||||
/>
|
||||
<FormListMoveArrows
|
||||
move={move}
|
||||
index={index}
|
||||
total={fields.length}
|
||||
/>
|
||||
</Space>
|
||||
</LayoutFormRow>
|
||||
</Form.Item>
|
||||
))}
|
||||
<Form.Item>
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
}}
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
{t("general.actions.add")}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Form.List>
|
||||
</LayoutFormRow>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,6 +70,12 @@ export default function ShopInfoSchedulingComponent({ form }) {
|
||||
>
|
||||
<Select mode="tags" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["ss_configuration", "dailyhrslimit"]}
|
||||
label={t("bodyshop.fields.ss_configuration.dailyhrslimit")}
|
||||
>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider>
|
||||
<Space wrap size="large">
|
||||
|
||||
@@ -81,7 +81,10 @@ export function SignInComponent({
|
||||
/>
|
||||
</Form.Item>
|
||||
{signInError ? (
|
||||
<AlertComponent type="error" message={signInError.message} />
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t(`users.errors.signinerror.${signInError.code}`)}
|
||||
/>
|
||||
) : null}
|
||||
<Button
|
||||
className="login-btn"
|
||||
|
||||
@@ -12,6 +12,7 @@ import AlertComponent from "../alert/alert.component";
|
||||
import DataLabel from "../data-label/data-label.component";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import TechClockOffButton from "../tech-job-clock-out-button/tech-job-clock-out-button.component";
|
||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
technician: selectTechnician,
|
||||
@@ -60,11 +61,9 @@ export function TechClockedInList({ technician }) {
|
||||
<Card
|
||||
title={
|
||||
<Link to={`/tech/joblookup?selected=${ticket.job.id}`}>
|
||||
{`${ticket.job.ro_number || t("general.labels.na")} ${
|
||||
ticket.job.ownr_fn || ""
|
||||
} ${ticket.job.ownr_ln || ""} ${
|
||||
ticket.job.ownr_co_nm || ""
|
||||
}`}
|
||||
{`${
|
||||
ticket.job.ro_number || t("general.labels.na")
|
||||
} ${OwnerNameDisplayFunction(ticket.job)}`}
|
||||
</Link>
|
||||
}
|
||||
actions={[
|
||||
|
||||
@@ -12,6 +12,7 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { onlyUnique } from "../../utils/arrayHelper";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -108,9 +109,9 @@ export function TechLookupJobsList({ bodyshop }) {
|
||||
state.sortedInfo.columnKey === "owner" && state.sortedInfo.order,
|
||||
ellipsis: true,
|
||||
render: (text, record) => (
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
@@ -76,6 +76,21 @@ export function TimeTicketList({
|
||||
state.sortedInfo.columnKey === "employee" && state.sortedInfo.order,
|
||||
render: (text, record) =>
|
||||
`${record.employee.first_name} ${record.employee.last_name}`,
|
||||
filters:
|
||||
timetickets
|
||||
.map((l) => l.employeeid)
|
||||
.filter(onlyUnique)
|
||||
.map((s) => {
|
||||
return {
|
||||
text: (() => {
|
||||
const emp = bodyshop.employees.find((e) => e.id === s);
|
||||
|
||||
return `${emp.first_name} ${emp.last_name}`;
|
||||
})(), //
|
||||
value: [s],
|
||||
};
|
||||
}) || [],
|
||||
onFilter: (value, record) => value.includes(record.employeeid),
|
||||
},
|
||||
{
|
||||
title: t("timetickets.fields.cost_center"),
|
||||
|
||||
@@ -159,8 +159,10 @@ export function TimeTicketModalComponent({
|
||||
name="flat_rate"
|
||||
label={t("timetickets.fields.flat_rate")}
|
||||
valuePropName="checked"
|
||||
noStyle
|
||||
style={{ display: "none" }}
|
||||
>
|
||||
<Switch />
|
||||
<Switch style={{ display: "none" }} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
|
||||
@@ -212,6 +214,7 @@ export function TimeTicketModalComponent({
|
||||
<>
|
||||
<Form.Item label={t("timetickets.fields.clockon")} name="clockon">
|
||||
<FormDateTimePicker
|
||||
minuteStep={5}
|
||||
disabled={
|
||||
!HasRbacAccess({
|
||||
bodyshop,
|
||||
@@ -234,7 +237,7 @@ export function TimeTicketModalComponent({
|
||||
return Promise.reject(
|
||||
t("timetickets.validation.clockoffwithoutclockon")
|
||||
);
|
||||
if (!value.isSameOrAfter(clockon))
|
||||
if (value && !value.isSameOrAfter(clockon))
|
||||
return Promise.reject(
|
||||
t("timetickets.validation.clockoffmustbeafterclockon")
|
||||
);
|
||||
@@ -245,6 +248,7 @@ export function TimeTicketModalComponent({
|
||||
]}
|
||||
>
|
||||
<FormDateTimePicker
|
||||
minuteStep={5}
|
||||
disabled={
|
||||
!HasRbacAccess({
|
||||
bodyshop,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
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 PayrollTemplate = TemplateList("special").exported_payroll;
|
||||
export default function TimeTicketsPayrollTable() {
|
||||
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: PayrollTemplate.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"),
|
||||
},
|
||||
},
|
||||
{},
|
||||
"x"
|
||||
);
|
||||
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button loading={loading} onClick={handleClick}>
|
||||
{t("printcenter.payments.exported_payroll")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import VehicleDetailUpdateJobsComponent from "../vehicle-detail-update-jobs/vehicle-detail-update-jobs.component";
|
||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -34,9 +35,7 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
||||
key: "owner",
|
||||
render: (text, record) => (
|
||||
<Link to={`/manage/owners/${record.owner.id}`}>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { HeartOutlined } from "@ant-design/icons";
|
||||
import { Select, Tag } from "antd";
|
||||
import { Select, Space, Tag } from "antd";
|
||||
import React, { forwardRef, useEffect, useState } from "react";
|
||||
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
|
||||
const { Option } = Select;
|
||||
|
||||
//To be used as a form element only.
|
||||
|
||||
const VendorSearchSelect = (
|
||||
{ value, onChange, options, onSelect, disabled, preferredMake },
|
||||
{ value, onChange, options, onSelect, disabled, preferredMake, showPhone },
|
||||
ref
|
||||
) => {
|
||||
const [option, setOption] = useState(value);
|
||||
@@ -35,6 +36,7 @@ const VendorSearchSelect = (
|
||||
style={{
|
||||
width: "100%",
|
||||
}}
|
||||
dropdownMatchSelectWidth={false}
|
||||
onChange={setOption}
|
||||
optionFilterProp="name"
|
||||
onSelect={onSelect}
|
||||
@@ -50,10 +52,15 @@ const VendorSearchSelect = (
|
||||
>
|
||||
<div className="imex-flex-row">
|
||||
<div style={{ flex: 1 }}>{o.name}</div>
|
||||
<HeartOutlined />
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
<Space style={{ marginLeft: "1rem" }}>
|
||||
<HeartOutlined style={{ color: "red" }} />
|
||||
{o.phone && showPhone && (
|
||||
<PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>
|
||||
)}
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
</Option>
|
||||
))
|
||||
@@ -64,9 +71,14 @@ const VendorSearchSelect = (
|
||||
<div className="imex-flex-row" style={{ width: "100%" }}>
|
||||
<div style={{ flex: 1 }}>{o.name}</div>
|
||||
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
<Space style={{ marginLeft: "1rem" }}>
|
||||
{o.phone && showPhone && (
|
||||
<PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>
|
||||
)}
|
||||
{o.discount && o.discount !== 0 ? (
|
||||
<Tag color="green">{`${o.discount * 100}%`}</Tag>
|
||||
) : null}
|
||||
</Space>
|
||||
</div>
|
||||
</Option>
|
||||
))
|
||||
|
||||
@@ -25,6 +25,7 @@ export default function VendorsFormComponent({
|
||||
formLoading,
|
||||
handleDelete,
|
||||
responsibilityCenters,
|
||||
selectedvendor,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const client = useApolloClient();
|
||||
@@ -52,7 +53,12 @@ export default function VendorsFormComponent({
|
||||
>
|
||||
{t("general.actions.save")}
|
||||
</Button>
|
||||
<Button type="danger" onClick={handleDelete} loading={formLoading}>
|
||||
<Button
|
||||
type="danger"
|
||||
disabled={selectedvendor === "new"}
|
||||
onClick={handleDelete}
|
||||
loading={formLoading}
|
||||
>
|
||||
{t("general.actions.delete")}
|
||||
</Button>
|
||||
|
||||
@@ -109,12 +115,14 @@ export default function VendorsFormComponent({
|
||||
|
||||
<Form.Item
|
||||
label={t("vendors.fields.email")}
|
||||
rules={[
|
||||
{
|
||||
type: "email",
|
||||
message: t("general.validation.invalidemail"),
|
||||
},
|
||||
]}
|
||||
rules={
|
||||
[
|
||||
// {
|
||||
// type: "email",
|
||||
// message: t("general.validation.invalidemail"),
|
||||
// },
|
||||
]
|
||||
}
|
||||
name="email"
|
||||
>
|
||||
<FormItemEmail email={getFieldValue("email")} />
|
||||
|
||||
@@ -39,30 +39,30 @@ function VendorsFormContainer({ refetch, bodyshop }) {
|
||||
const [insertvendor] = useMutation(INSERT_NEW_VENDOR);
|
||||
const [deleteVendor] = useMutation(DELETE_VENDOR);
|
||||
|
||||
const handleDelete = () => {
|
||||
const handleDelete = async () => {
|
||||
setFormLoading(true);
|
||||
deleteVendor({
|
||||
const result = await deleteVendor({
|
||||
variables: { id: selectedvendor },
|
||||
refetchQueries: ["QUERY_ALL_VENDORS"],
|
||||
})
|
||||
.then((r) => {
|
||||
notification["success"]({
|
||||
message: t("vendors.successes.deleted"),
|
||||
});
|
||||
delete search.selectedvendor;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
if (refetch)
|
||||
refetch().then((r) => {
|
||||
form.resetFields();
|
||||
});
|
||||
setFormLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
notification["error"]({
|
||||
message: t("vendors.errors.deleting"),
|
||||
});
|
||||
setFormLoading(false);
|
||||
});
|
||||
console.log(result);
|
||||
if (result.errors) {
|
||||
notification["error"]({
|
||||
message: t("vendors.errors.deleting"),
|
||||
});
|
||||
} else {
|
||||
notification["success"]({
|
||||
message: t("vendors.successes.deleted"),
|
||||
});
|
||||
delete search.selectedvendor;
|
||||
history.push({ search: queryString.stringify(search) });
|
||||
if (refetch)
|
||||
refetch().then((r) => {
|
||||
form.resetFields();
|
||||
});
|
||||
}
|
||||
|
||||
setFormLoading(false);
|
||||
};
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
@@ -139,6 +139,7 @@ function VendorsFormContainer({ refetch, bodyshop }) {
|
||||
formLoading={formLoading}
|
||||
handleDelete={handleDelete}
|
||||
responsibilityCenters={bodyshop.md_responsibility_centers || null}
|
||||
selectedvendor={selectedvendor}
|
||||
/>
|
||||
) : (
|
||||
t("vendors.labels.noneselected")
|
||||
|
||||
@@ -245,6 +245,27 @@ export const CANCEL_APPOINTMENT_BY_ID = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const CANCEL_APPOINTMENTS_BY_JOB_ID = gql`
|
||||
mutation CANCEL_APPOINTMENTS_BY_JOB_ID($jobid: uuid!, $job: jobs_set_input) {
|
||||
update_appointments(
|
||||
where: { _and: { jobid: { _eq: $jobid }, arrived: { _eq: false } } }
|
||||
_set: { canceled: true }
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
canceled
|
||||
}
|
||||
}
|
||||
update_jobs_by_pk(pk_columns: { id: $jobid }, _set: $job) {
|
||||
date_scheduled
|
||||
id
|
||||
scheduled_in
|
||||
scheduled_completion
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const QUERY_APPOINTMENTS_BY_JOBID = gql`
|
||||
query QUERY_APPOINTMENTS_BY_JOBID($jobid: uuid!) {
|
||||
appointments(where: { jobid: { _eq: $jobid } }, order_by: { start: desc }) {
|
||||
|
||||
@@ -12,6 +12,13 @@ export const UPDATE_BILL_LINE = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const DELETE_BILL_LINE = gql`
|
||||
mutation DELETE_BILL_LINE($id: uuid!) {
|
||||
delete_billlines_by_pk(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const INSERT_NEW_BILL_LINES = gql`
|
||||
mutation INSERT_NEW_BILL_LINES($billLines: [billlines_insert_input!]!) {
|
||||
|
||||
@@ -101,6 +101,11 @@ export const QUERY_BODYSHOP = gql`
|
||||
md_filehandlers
|
||||
md_email_cc
|
||||
timezone
|
||||
ss_configuration
|
||||
md_from_emails
|
||||
last_name_first
|
||||
md_parts_order_comment
|
||||
bill_allow_post_to_closed
|
||||
employees {
|
||||
user_email
|
||||
id
|
||||
@@ -199,6 +204,11 @@ export const UPDATE_SHOP = gql`
|
||||
md_filehandlers
|
||||
md_email_cc
|
||||
timezone
|
||||
ss_configuration
|
||||
md_from_emails
|
||||
last_name_first
|
||||
md_parts_order_comment
|
||||
bill_allow_post_to_closed
|
||||
employees {
|
||||
id
|
||||
first_name
|
||||
@@ -266,7 +276,6 @@ export const QUERY_DELIVER_CHECKLIST = gql`
|
||||
ro_number
|
||||
actual_completion
|
||||
actual_delivery
|
||||
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -89,6 +89,9 @@ export const QUERY_ALL_CC = gql`
|
||||
job {
|
||||
id
|
||||
ro_number
|
||||
ownr_fn
|
||||
ownr_ln
|
||||
ownr_co_nm
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ export const GET_LINE_TICKET_BY_PK = gql`
|
||||
employeeid
|
||||
memo
|
||||
flat_rate
|
||||
clockon
|
||||
clockoff
|
||||
employee {
|
||||
id
|
||||
first_name
|
||||
|
||||
@@ -146,6 +146,11 @@ export const QUERY_EXACT_JOB_IN_PRODUCTION = gql`
|
||||
employee_refinish
|
||||
employee_prep
|
||||
employee_csr
|
||||
joblines_status {
|
||||
part_type
|
||||
status
|
||||
count
|
||||
}
|
||||
labhrs: joblines_aggregate(
|
||||
where: {
|
||||
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
|
||||
@@ -219,6 +224,11 @@ export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql`
|
||||
employee_refinish
|
||||
employee_prep
|
||||
employee_csr
|
||||
joblines_status {
|
||||
part_type
|
||||
status
|
||||
count
|
||||
}
|
||||
labhrs: joblines_aggregate(
|
||||
where: {
|
||||
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
|
||||
@@ -294,6 +304,11 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
|
||||
employee_prep
|
||||
employee_csr
|
||||
suspended
|
||||
joblines_status {
|
||||
part_type
|
||||
status
|
||||
count
|
||||
}
|
||||
labhrs: joblines_aggregate(
|
||||
where: {
|
||||
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
|
||||
@@ -595,6 +610,9 @@ export const GET_JOB_BY_PK = gql`
|
||||
ca_gst_registrant
|
||||
ownerid
|
||||
ded_note
|
||||
materials
|
||||
auto_add_ats
|
||||
rate_ats
|
||||
owner {
|
||||
id
|
||||
ownr_fn
|
||||
@@ -649,6 +667,8 @@ export const GET_JOB_BY_PK = gql`
|
||||
date_invoiced
|
||||
date_last_contacted
|
||||
date_next_contact
|
||||
date_towin
|
||||
date_rentalresp
|
||||
date_exported
|
||||
status
|
||||
owner_owing
|
||||
@@ -696,20 +716,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
joblineid
|
||||
bill {
|
||||
id
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
parts_order_lines {
|
||||
id
|
||||
parts_order {
|
||||
id
|
||||
order_number
|
||||
comments
|
||||
order_date
|
||||
user_email
|
||||
invoice_number
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
@@ -1096,6 +1103,15 @@ export const VOID_JOB = gql`
|
||||
insert_notes(objects: $note) {
|
||||
affected_rows
|
||||
}
|
||||
update_appointments(
|
||||
where: { jobid: { _eq: $jobId } }
|
||||
_set: { canceled: true }
|
||||
) {
|
||||
returning {
|
||||
id
|
||||
canceled
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -2086,3 +2102,23 @@ export const DELETE_RELATED_RO = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const GET_JOB_LINE_ORDERS = gql`
|
||||
query GET_JOB_LINE_ORDERS($joblineid: uuid!) {
|
||||
parts_order_lines(where: { job_line_id: { _eq: $joblineid } }) {
|
||||
id
|
||||
act_price
|
||||
parts_order {
|
||||
id
|
||||
order_date
|
||||
order_number
|
||||
orderedby
|
||||
return
|
||||
comments
|
||||
vendor {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -87,6 +87,7 @@ export const QUERY_ALL_VENDORS_FOR_ORDER = gql`
|
||||
discount
|
||||
email
|
||||
active
|
||||
phone
|
||||
}
|
||||
jobs(where: { id: { _eq: $jobId } }) {
|
||||
v_make_desc
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as Sentry from "@sentry/react";
|
||||
import "antd/dist/antd.less";
|
||||
import Dinero from "dinero.js";
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { Provider } from "react-redux";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { PersistGate } from "redux-persist/integration/react";
|
||||
@@ -39,7 +39,9 @@ if (process.env.NODE_ENV !== "development") {
|
||||
});
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
const container = document.getElementById("root");
|
||||
const root = createRoot(container); // createRoot(container!) if you use TypeScript
|
||||
root.render(
|
||||
<Provider store={store}>
|
||||
<BrowserRouter>
|
||||
<PersistGate
|
||||
@@ -49,48 +51,7 @@ ReactDOM.render(
|
||||
<AppContainer />
|
||||
</PersistGate>
|
||||
</BrowserRouter>
|
||||
</Provider>,
|
||||
document.getElementById("root")
|
||||
</Provider>
|
||||
);
|
||||
|
||||
// const onServiceWorkerUpdate = (registration) => {
|
||||
// console.log("onServiceWorkerUpdate", registration);
|
||||
|
||||
// const btn = (
|
||||
// <Space flex>
|
||||
// <Button
|
||||
// onClick={async () => {
|
||||
// window.open("https://imex-online.noticeable.news/", "_blank");
|
||||
// }}
|
||||
// >
|
||||
// {i18n.t("general.actions.viewreleasenotes")}
|
||||
// </Button>
|
||||
// <Button
|
||||
// type="primary"
|
||||
// onClick={async () => {
|
||||
// if (registration && registration.waiting) {
|
||||
// await registration.unregister();
|
||||
// // Makes Workbox call skipWaiting()
|
||||
// registration.waiting.postMessage({ type: "SKIP_WAITING" });
|
||||
// // Once the service worker is unregistered, we can reload the page to let
|
||||
// // the browser download a fresh copy of our app (invalidating the cache)
|
||||
// window.location.reload();
|
||||
// }
|
||||
// }}
|
||||
// >
|
||||
// {i18n.t("general.actions.refresh")}
|
||||
// </Button>
|
||||
// </Space>
|
||||
// );
|
||||
// notification.open({
|
||||
// icon: <AlertOutlined />,
|
||||
// message: i18n.t("general.messages.newversiontitle"),
|
||||
// description: i18n.t("general.messages.newversionmessage"),
|
||||
// duration: 0,
|
||||
// btn,
|
||||
// key: "updateavailable",
|
||||
// });
|
||||
// };
|
||||
|
||||
// serviceWorkerRegistration.register({ onUpdate: onServiceWorkerUpdate });
|
||||
reportWebVitals();
|
||||
|
||||
@@ -5,16 +5,19 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AccountingPayablesTable from "../../components/accounting-payables-table/accounting-payables-table.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { checkPartnerStatus } from "../../components/partner-ping/partner-ping.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { QUERY_BILLS_FOR_EXPORT } from "../../graphql/accounting.queries";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
@@ -26,6 +29,7 @@ export function AccountingPayablesContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
partnerVersion,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -38,7 +42,8 @@ export function AccountingPayablesContainer({
|
||||
label: t("titles.bc.accounting-payables"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
checkPartnerStatus(bodyshop, true);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_BILLS_FOR_EXPORT, {
|
||||
fetchPolicy: "network-only",
|
||||
@@ -47,9 +52,24 @@ export function AccountingPayablesContainer({
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
const noPath =
|
||||
!partnerVersion?.qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="accounting:payables">
|
||||
{noPath && (
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t("general.messages.noacctfilepath")}
|
||||
/>
|
||||
)}
|
||||
<AccountingPayablesTable
|
||||
loadaing={loading}
|
||||
bills={data ? data.bills : []}
|
||||
|
||||
@@ -12,9 +12,12 @@ import {
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { checkPartnerStatus } from "../../components/partner-ping/partner-ping.component";
|
||||
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
@@ -25,6 +28,7 @@ export function AccountingPaymentsContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
partnerVersion,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -37,7 +41,8 @@ export function AccountingPaymentsContainer({
|
||||
label: t("titles.bc.accounting-payments"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
checkPartnerStatus(bodyshop, true);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_PAYMENTS_FOR_EXPORT, {
|
||||
fetchPolicy: "network-only",
|
||||
@@ -45,10 +50,23 @@ export function AccountingPaymentsContainer({
|
||||
});
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
const noPath =
|
||||
!partnerVersion?.qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="accounting:payments">
|
||||
{noPath && (
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t("general.messages.noacctfilepath")}
|
||||
/>
|
||||
)}
|
||||
<AccountingPaymentsTable
|
||||
loadaing={loading}
|
||||
payments={data ? data.payments : []}
|
||||
|
||||
@@ -12,9 +12,12 @@ import {
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { checkPartnerStatus } from "../../components/partner-ping/partner-ping.component";
|
||||
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
partnerVersion: selectPartnerVersion,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
@@ -25,6 +28,7 @@ export function AccountingReceivablesContainer({
|
||||
bodyshop,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
partnerVersion,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -37,7 +41,8 @@ export function AccountingReceivablesContainer({
|
||||
label: t("titles.bc.accounting-receivables"),
|
||||
},
|
||||
]);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||
checkPartnerStatus(bodyshop, true);
|
||||
}, [t, setBreadcrumbs, setSelectedHeader, bodyshop]);
|
||||
|
||||
const { loading, error, data } = useQuery(QUERY_JOBS_FOR_EXPORT, {
|
||||
variables: {
|
||||
@@ -48,9 +53,25 @@ export function AccountingReceivablesContainer({
|
||||
});
|
||||
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
const noPath =
|
||||
!partnerVersion?.qbpath &&
|
||||
!(
|
||||
bodyshop &&
|
||||
(bodyshop.cdk_dealerid ||
|
||||
bodyshop.pbs_serialnumber ||
|
||||
bodyshop.accountingconfig.qbo)
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RbacWrapper action="accounting:receivables">
|
||||
{noPath && (
|
||||
<AlertComponent
|
||||
type="error"
|
||||
message={t("general.messages.noacctfilepath")}
|
||||
/>
|
||||
)}
|
||||
<AccountingReceivablesTable
|
||||
loadaing={loading}
|
||||
jobs={data ? data.jobs : []}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
import { SyncOutlined, EditFilled } from "@ant-design/icons";
|
||||
import { Button, Card, Checkbox, Input, Space, Table, Typography } from "antd";
|
||||
import queryString from "query-string";
|
||||
import React, { useState } from "react";
|
||||
@@ -141,7 +141,9 @@ export function BillsListPage({
|
||||
render: (text, record) => (
|
||||
<Space wrap>
|
||||
<Link to={`/manage/bills?billid=${record.id}`}>
|
||||
<Button>{t("bills.actions.edit")}</Button>
|
||||
<Button>
|
||||
<EditFilled />
|
||||
</Button>
|
||||
</Link>
|
||||
{
|
||||
// <Button
|
||||
|
||||
@@ -70,7 +70,12 @@ export function CourtesyCarCreateContainer({
|
||||
|
||||
return (
|
||||
<RbacWrapper action="courtesycar:create">
|
||||
<Form form={form} autoComplete="new-password" onFinish={handleFinish}>
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete="new-password"
|
||||
onFinish={handleFinish}
|
||||
layout="vertical"
|
||||
>
|
||||
<CourtesyCarFormComponent form={form} saveLoading={loading} />
|
||||
</Form>
|
||||
</RbacWrapper>
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { OwnerNameDisplayFunction } from "../../components/owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -143,9 +144,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
||||
<Link to={`/manage/jobs/${data && data.jobs_by_pk.id}`}>{`${
|
||||
data && data.jobs_by_pk && data.jobs_by_pk.ro_number
|
||||
}`}</Link>
|
||||
{` | ${data.jobs_by_pk.ownr_fn || ""} ${
|
||||
data.jobs_by_pk.ownr_ln || ""
|
||||
} ${data.jobs_by_pk.ownr_co_nm || ""} | ${
|
||||
{` | ${OwnerNameDisplayFunction(data.jobs_by_pk)} | ${
|
||||
data.jobs_by_pk.v_model_yr || ""
|
||||
} ${data.jobs_by_pk.v_make_desc || ""} ${
|
||||
data.jobs_by_pk.v_model_desc || ""
|
||||
|
||||
@@ -3,12 +3,19 @@ import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import JobsAvailableTableContainer from "../../components/jobs-available-table/jobs-available-table.container";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { selectPartnerVersion } from "../../redux/application/application.selectors";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
partnerVersion: selectPartnerVersion,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -16,6 +23,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
});
|
||||
|
||||
export function JobsAvailablePageContainer({
|
||||
partnerVersion,
|
||||
setBreadcrumbs,
|
||||
setSelectedHeader,
|
||||
}) {
|
||||
@@ -40,9 +48,18 @@ export function JobsAvailablePageContainer({
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
{!partnerVersion && (
|
||||
<AlertComponent
|
||||
type="warning"
|
||||
message={t("general.messages.partnernotrunning")}
|
||||
/>
|
||||
)}
|
||||
<JobsAvailableTableContainer />
|
||||
</div>
|
||||
</RbacWrapper>
|
||||
);
|
||||
}
|
||||
export default connect(null, mapDispatchToProps)(JobsAvailablePageContainer);
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(JobsAvailablePageContainer);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import SpinComponent from "../../components/loading-spinner/loading-spinner.component";
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
import { OwnerNameDisplayFunction } from "../../components/owner-name-display/owner-name-display.component";
|
||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||
import { GET_JOB_BY_PK, UPDATE_JOB } from "../../graphql/jobs.queries";
|
||||
import {
|
||||
@@ -78,11 +79,10 @@ function JobsDetailPageContainer({
|
||||
CreateRecentItem(
|
||||
jobId,
|
||||
"job",
|
||||
`${data.jobs_by_pk.ro_number || t("general.labels.na")} | ${
|
||||
data.jobs_by_pk.ownr_fn || ""
|
||||
} ${data.jobs_by_pk.ownr_ln || ""} ${
|
||||
data.jobs_by_pk.ownr_co_nm || ""
|
||||
}`,
|
||||
|
||||
`${
|
||||
data.jobs_by_pk.ro_number || t("general.labels.na")
|
||||
} | ${OwnerNameDisplayFunction(data.jobs_by_pk)}`,
|
||||
`/manage/jobs/${jobId}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import OwnersDetailComponent from "./owners-detail.page.component";
|
||||
import NotFound from "../../components/not-found/not-found.component";
|
||||
import { OwnerNameDisplayFunction } from "../../components/owner-name-display/owner-name-display.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -38,11 +39,7 @@ export function OwnersDetailContainer({
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.owners-detail", {
|
||||
name: data
|
||||
? `${(data.owners_by_pk && data.owners_by_pk.ownr_fn) || ""} ${
|
||||
(data.owners_by_pk && data.owners_by_pk.ownr_ln) || ""
|
||||
} ${(data.owners_by_pk && data.owners_by_pk.ownr_co_nm) || ""}`
|
||||
: "",
|
||||
name: data ? OwnerNameDisplayFunction(data.owners_by_pk) : "",
|
||||
});
|
||||
setSelectedHeader("owners");
|
||||
setBreadcrumbs([
|
||||
@@ -50,11 +47,7 @@ export function OwnersDetailContainer({
|
||||
{
|
||||
link: `/manage/owners/${ownerId}`,
|
||||
label: t("titles.bc.owner-detail", {
|
||||
name: data
|
||||
? `${(data.owners_by_pk && data.owners_by_pk.ownr_fn) || ""} ${
|
||||
(data.owners_by_pk && data.owners_by_pk.ownr_ln) || ""
|
||||
} ${(data.owners_by_pk && data.owners_by_pk.ownr_co_nm) || ""}`
|
||||
: "",
|
||||
name: data ? OwnerNameDisplayFunction(data.owners_by_pk) : "",
|
||||
}),
|
||||
},
|
||||
]);
|
||||
@@ -64,9 +57,7 @@ export function OwnersDetailContainer({
|
||||
CreateRecentItem(
|
||||
ownerId,
|
||||
"owner",
|
||||
`${data.owners_by_pk.ownr_fn || ""} ${
|
||||
data.owners_by_pk.ownr_ln || ""
|
||||
} ${data.owners_by_pk.ownr_co_nm || ""}`,
|
||||
OwnerNameDisplayFunction(data.owners_by_pk),
|
||||
`/manage/owners/${ownerId}`
|
||||
)
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ 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";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -120,14 +121,12 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
||||
render: (text, record) => {
|
||||
return record.ownerid ? (
|
||||
<Link to={"/manage/owners/" + record.ownerid}>
|
||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</Link>
|
||||
) : (
|
||||
<span>{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||
record.ownr_co_nm || ""
|
||||
}`}</span>
|
||||
<span>
|
||||
<OwnerNameDisplay ownerObject={record} />
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user