Merged in release/2022-06-10 (pull request #501)
release/2022-06-10 Approved-by: Patrick Fic
This commit is contained in:
@@ -5691,6 +5691,32 @@
|
|||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>inventory</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>list</name>
|
||||||
|
<definition_loaded>false</definition_loaded>
|
||||||
|
<description></description>
|
||||||
|
<comment></comment>
|
||||||
|
<default_text></default_text>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>es-MX</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-CA</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>jobs</name>
|
<name>jobs</name>
|
||||||
<children>
|
<children>
|
||||||
@@ -17213,6 +17239,241 @@
|
|||||||
</folder_node>
|
</folder_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>inventory</name>
|
||||||
|
<children>
|
||||||
|
<folder_node>
|
||||||
|
<name>actions</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>addtoinventory</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>consumefrominventory</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>errors</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>inserting</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>labels</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>consumedbyjob</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>frombillinvoicenumber</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>fromvendor</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>inventory</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>showall</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>showavailable</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>successes</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>inserted</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>
|
<folder_node>
|
||||||
<name>joblines</name>
|
<name>joblines</name>
|
||||||
<children>
|
<children>
|
||||||
@@ -29934,6 +30195,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>inventory</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>
|
<concept_node>
|
||||||
<name>jobs</name>
|
<name>jobs</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -40529,6 +40811,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>calendarperiod</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>
|
<concept_node>
|
||||||
<name>dailyactual</name>
|
<name>dailyactual</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -40571,6 +40874,69 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>jobs</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>lastmonth</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>lastweek</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>
|
<concept_node>
|
||||||
<name>monthlytarget</name>
|
<name>monthlytarget</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -40592,6 +40958,48 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>productivestatistics</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>productivetimeticketsoverdate</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>
|
<concept_node>
|
||||||
<name>targets</name>
|
<name>targets</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -40613,6 +41021,69 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>thismonth</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>thisweek</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>timetickets</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>
|
<concept_node>
|
||||||
<name>todateactual</name>
|
<name>todateactual</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -40634,6 +41105,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>totaloverperiod</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>
|
<concept_node>
|
||||||
<name>weeklyactual</name>
|
<name>weeklyactual</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -40769,37 +41261,6 @@
|
|||||||
</folder_node>
|
</folder_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
|
||||||
<name>scoredboard</name>
|
|
||||||
<children>
|
|
||||||
<folder_node>
|
|
||||||
<name>successes</name>
|
|
||||||
<children>
|
|
||||||
<concept_node>
|
|
||||||
<name>updated</name>
|
|
||||||
<definition_loaded>false</definition_loaded>
|
|
||||||
<description></description>
|
|
||||||
<comment></comment>
|
|
||||||
<default_text></default_text>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>es-MX</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-CA</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
</children>
|
|
||||||
</folder_node>
|
|
||||||
</children>
|
|
||||||
</folder_node>
|
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>tech</name>
|
<name>tech</name>
|
||||||
<children>
|
<children>
|
||||||
@@ -42383,6 +42844,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>inventory</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>
|
<concept_node>
|
||||||
<name>jobs</name>
|
<name>jobs</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -43204,6 +43686,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>inventory</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>
|
<concept_node>
|
||||||
<name>jobs</name>
|
<name>jobs</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ export function BillDetailEditcontainer({
|
|||||||
sm: "100%",
|
sm: "100%",
|
||||||
md: "100%",
|
md: "100%",
|
||||||
lg: "100%",
|
lg: "100%",
|
||||||
xl: "80%",
|
xl: "90%",
|
||||||
xxl: "80%",
|
xxl: "90%",
|
||||||
};
|
};
|
||||||
const drawerPercentage = selectedBreakpoint
|
const drawerPercentage = selectedBreakpoint
|
||||||
? bpoints[selectedBreakpoint[0]]
|
? bpoints[selectedBreakpoint[0]]
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
UPDATE_JOB,
|
UPDATE_JOB,
|
||||||
} from "../../graphql/jobs.queries";
|
} from "../../graphql/jobs.queries";
|
||||||
import { MUTATION_MARK_RETURN_RECEIVED } from "../../graphql/parts-orders.queries";
|
import { MUTATION_MARK_RETURN_RECEIVED } from "../../graphql/parts-orders.queries";
|
||||||
|
import { UPDATE_INVENTORY_LINES } from "../../graphql/inventory.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||||
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
|
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
|
||||||
@@ -50,6 +51,7 @@ function BillEnterModalContainer({
|
|||||||
const [insertBill] = useMutation(INSERT_NEW_BILL);
|
const [insertBill] = useMutation(INSERT_NEW_BILL);
|
||||||
const [updateJobLines] = useMutation(UPDATE_JOB_LINE);
|
const [updateJobLines] = useMutation(UPDATE_JOB_LINE);
|
||||||
const [updatePartsOrderLines] = useMutation(MUTATION_MARK_RETURN_RECEIVED);
|
const [updatePartsOrderLines] = useMutation(MUTATION_MARK_RETURN_RECEIVED);
|
||||||
|
const [updateInventoryLines] = useMutation(UPDATE_INVENTORY_LINES);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
|
|
||||||
@@ -79,8 +81,13 @@ function BillEnterModalContainer({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const { upload, location, outstanding_returns, ...remainingValues } =
|
const {
|
||||||
values;
|
upload,
|
||||||
|
location,
|
||||||
|
outstanding_returns,
|
||||||
|
inventory,
|
||||||
|
...remainingValues
|
||||||
|
} = values;
|
||||||
|
|
||||||
let adjustmentsToInsert = {};
|
let adjustmentsToInsert = {};
|
||||||
|
|
||||||
@@ -190,6 +197,26 @@ function BillEnterModalContainer({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const billId = r1.data.insert_bills.returning[0].id;
|
const billId = r1.data.insert_bills.returning[0].id;
|
||||||
|
const markInventoryConsumed =
|
||||||
|
inventory && inventory.filter((i) => i.consumefrominventory);
|
||||||
|
|
||||||
|
if (markInventoryConsumed && markInventoryConsumed.length > 0) {
|
||||||
|
const r2 = await updateInventoryLines({
|
||||||
|
variables: {
|
||||||
|
InventoryIds: markInventoryConsumed.map((p) => p.id),
|
||||||
|
consumedbybillid: billId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!!r2.errors) {
|
||||||
|
setLoading(false);
|
||||||
|
setEnterAgain(false);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("inventory.errors.updating", {
|
||||||
|
message: JSON.stringify(r2.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
remainingValues.billlines
|
remainingValues.billlines
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export function BillFormComponent({
|
|||||||
disableInvNumber,
|
disableInvNumber,
|
||||||
job,
|
job,
|
||||||
loadOutstandingReturns,
|
loadOutstandingReturns,
|
||||||
|
loadInventory,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
@@ -61,6 +62,7 @@ export function BillFormComponent({
|
|||||||
setDiscount(opt.discount);
|
setDiscount(opt.discount);
|
||||||
|
|
||||||
opt &&
|
opt &&
|
||||||
|
!billEdit &&
|
||||||
loadOutstandingReturns({
|
loadOutstandingReturns({
|
||||||
variables: {
|
variables: {
|
||||||
jobId: form.getFieldValue("jobid"),
|
jobId: form.getFieldValue("jobid"),
|
||||||
@@ -86,7 +88,7 @@ export function BillFormComponent({
|
|||||||
const jobId = form.getFieldValue("jobid");
|
const jobId = form.getFieldValue("jobid");
|
||||||
if (jobId) {
|
if (jobId) {
|
||||||
loadLines({ variables: { id: jobId } });
|
loadLines({ variables: { id: jobId } });
|
||||||
if (form.getFieldValue("is_credit_memo") && vendorId) {
|
if (form.getFieldValue("is_credit_memo") && vendorId && !billEdit) {
|
||||||
loadOutstandingReturns({
|
loadOutstandingReturns({
|
||||||
variables: {
|
variables: {
|
||||||
jobId: jobId,
|
jobId: jobId,
|
||||||
@@ -95,12 +97,19 @@ export function BillFormComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vendorId === bodyshop.inhousevendorid && !billEdit) {
|
||||||
|
loadInventory();
|
||||||
|
}
|
||||||
}, [
|
}, [
|
||||||
form,
|
form,
|
||||||
|
billEdit,
|
||||||
loadOutstandingReturns,
|
loadOutstandingReturns,
|
||||||
|
loadInventory,
|
||||||
setDiscount,
|
setDiscount,
|
||||||
vendorAutoCompleteOptions,
|
vendorAutoCompleteOptions,
|
||||||
loadLines,
|
loadLines,
|
||||||
|
bodyshop.inhousevendorid,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -425,6 +434,7 @@ export function BillFormComponent({
|
|||||||
form={form}
|
form={form}
|
||||||
responsibilityCenters={responsibilityCenters}
|
responsibilityCenters={responsibilityCenters}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
billEdit={billEdit}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import BillFormComponent from "./bill-form.component";
|
import BillFormComponent from "./bill-form.component";
|
||||||
import BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
|
import BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
|
||||||
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
|
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
|
||||||
|
import BillInventoryTable from "../bill-inventory-table/bill-inventory-table.component";
|
||||||
|
import { QUERY_OUTSTANDING_INVENTORY } from "../../graphql/inventory.queries";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -20,6 +23,12 @@ export function BillFormContainer({
|
|||||||
disabled,
|
disabled,
|
||||||
disableInvNumber,
|
disableInvNumber,
|
||||||
}) {
|
}) {
|
||||||
|
const { Simple_Inventory } = useTreatments(
|
||||||
|
["Simple_Inventory"],
|
||||||
|
{},
|
||||||
|
bodyshop && bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
const { data: VendorAutoCompleteData } = useQuery(
|
const { data: VendorAutoCompleteData } = useQuery(
|
||||||
SEARCH_VENDOR_AUTOCOMPLETE,
|
SEARCH_VENDOR_AUTOCOMPLETE,
|
||||||
{ fetchPolicy: "network-only", nextFetchPolicy: "network-only" }
|
{ fetchPolicy: "network-only", nextFetchPolicy: "network-only" }
|
||||||
@@ -31,6 +40,8 @@ export function BillFormContainer({
|
|||||||
|
|
||||||
const [loadOutstandingReturns, { loading: returnLoading, data: returnData }] =
|
const [loadOutstandingReturns, { loading: returnLoading, data: returnData }] =
|
||||||
useLazyQuery(QUERY_UNRECEIVED_LINES);
|
useLazyQuery(QUERY_UNRECEIVED_LINES);
|
||||||
|
const [loadInventory, { loading: inventoryLoading, data: inventoryData }] =
|
||||||
|
useLazyQuery(QUERY_OUTSTANDING_INVENTORY);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -47,6 +58,7 @@ export function BillFormContainer({
|
|||||||
responsibilityCenters={bodyshop.md_responsibility_centers || null}
|
responsibilityCenters={bodyshop.md_responsibility_centers || null}
|
||||||
disableInvNumber={disableInvNumber}
|
disableInvNumber={disableInvNumber}
|
||||||
loadOutstandingReturns={loadOutstandingReturns}
|
loadOutstandingReturns={loadOutstandingReturns}
|
||||||
|
loadInventory={loadInventory}
|
||||||
/>
|
/>
|
||||||
{!billEdit && (
|
{!billEdit && (
|
||||||
<BillCmdReturnsTableComponent
|
<BillCmdReturnsTableComponent
|
||||||
@@ -56,6 +68,14 @@ export function BillFormContainer({
|
|||||||
returnData={returnData}
|
returnData={returnData}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{Simple_Inventory.treatment === "on" && (
|
||||||
|
<BillInventoryTable
|
||||||
|
form={form}
|
||||||
|
inventoryLoading={inventoryLoading}
|
||||||
|
inventoryData={billEdit ? [] : inventoryData}
|
||||||
|
billEdit={billEdit}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
Tooltip
|
Tooltip,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -18,6 +18,8 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import CiecaSelect from "../../utils/Ciecaselect";
|
import CiecaSelect from "../../utils/Ciecaselect";
|
||||||
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
|
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.component";
|
||||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||||
|
import BilllineAddInventory from "../billline-add-inventory/billline-add-inventory.component";
|
||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
//currentUser: selectCurrentUser
|
//currentUser: selectCurrentUser
|
||||||
@@ -34,10 +36,16 @@ export function BillEnterModalLinesComponent({
|
|||||||
discount,
|
discount,
|
||||||
form,
|
form,
|
||||||
responsibilityCenters,
|
responsibilityCenters,
|
||||||
|
billEdit,
|
||||||
|
billid,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { setFieldsValue, getFieldsValue, getFieldValue } = form;
|
const { setFieldsValue, getFieldsValue, getFieldValue } = form;
|
||||||
|
const { Simple_Inventory } = useTreatments(
|
||||||
|
["Simple_Inventory"],
|
||||||
|
{},
|
||||||
|
bodyshop && bodyshop.imexshopid
|
||||||
|
);
|
||||||
const columns = (remove) => {
|
const columns = (remove) => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -477,9 +485,22 @@ export function BillEnterModalLinesComponent({
|
|||||||
|
|
||||||
dataIndex: "actions",
|
dataIndex: "actions",
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Button disabled={disabled} onClick={() => remove(record.name)}>
|
<Space wrap>
|
||||||
<DeleteFilled />
|
<Button disabled={disabled} onClick={() => remove(record.name)}>
|
||||||
</Button>
|
<DeleteFilled />
|
||||||
|
</Button>
|
||||||
|
<Form.Item shouldUpdate noStyle>
|
||||||
|
{() =>
|
||||||
|
Simple_Inventory.treatment === "on" && (
|
||||||
|
<BilllineAddInventory
|
||||||
|
disabled={!billEdit || form.isFieldsTouched()}
|
||||||
|
billline={getFieldValue("billlines")[record.fieldKey]}
|
||||||
|
jobid={getFieldValue("jobid")}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Form.Item>
|
||||||
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
import { Checkbox, Form, Skeleton, Typography } from "antd";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import ReadOnlyFormItemComponent from "../form-items-formatted/read-only-form-item.component";
|
||||||
|
import "./bill-inventory-table.styles.scss";
|
||||||
|
|
||||||
|
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)(BillInventoryTable);
|
||||||
|
|
||||||
|
export function BillInventoryTable({
|
||||||
|
bodyshop,
|
||||||
|
form,
|
||||||
|
billEdit,
|
||||||
|
inventoryLoading,
|
||||||
|
inventoryData,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (inventoryData) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
inventory: inventoryData.inventory,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [inventoryData, form]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form.Item
|
||||||
|
shouldUpdate={(prev, cur) => prev.vendorid !== cur.vendorid}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
{() => {
|
||||||
|
const is_inhouse =
|
||||||
|
form.getFieldValue("vendorid") === bodyshop.inhousevendorid;
|
||||||
|
|
||||||
|
if (!is_inhouse || billEdit) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inventoryLoading) return <Skeleton />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form.List name="inventory">
|
||||||
|
{(fields, { add, remove, move }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Typography.Title level={4}>
|
||||||
|
{t("inventory.labels.inventory")}
|
||||||
|
</Typography.Title>
|
||||||
|
<table className="bill-inventory-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{t("billlines.fields.line_desc")}</th>
|
||||||
|
<th>{t("vendors.fields.name")}</th>
|
||||||
|
<th>{t("billlines.fields.quantity")}</th>
|
||||||
|
<th>{t("billlines.fields.actual_price")}</th>
|
||||||
|
<th>{t("billlines.fields.actual_cost")}</th>
|
||||||
|
<th>{t("inventory.actions.consumefrominventory")}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<tr key={field.key}>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
// label={t("joblines.fields.line_desc")}
|
||||||
|
key={`${index}line_desc`}
|
||||||
|
name={[field.name, "line_desc"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
span={2}
|
||||||
|
//label={t("joblines.fields.mod_lb_hrs")}
|
||||||
|
key={`${index}part_type`}
|
||||||
|
name={[
|
||||||
|
field.name,
|
||||||
|
"billline",
|
||||||
|
"bill",
|
||||||
|
"vendor",
|
||||||
|
"name",
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
span={2}
|
||||||
|
//label={t("joblines.fields.mod_lb_hrs")}
|
||||||
|
key={`${index}quantity`}
|
||||||
|
name={[field.name, "quantity"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
span={2}
|
||||||
|
//label={t("joblines.fields.mod_lb_hrs")}
|
||||||
|
key={`${index}act_price`}
|
||||||
|
name={[field.name, "actual_price"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent type="currency" />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
span={2}
|
||||||
|
//label={t("joblines.fields.mod_lb_hrs")}
|
||||||
|
key={`${index}cost`}
|
||||||
|
name={[field.name, "actual_cost"]}
|
||||||
|
>
|
||||||
|
<ReadOnlyFormItemComponent type="currency" />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<Form.Item
|
||||||
|
span={2}
|
||||||
|
//label={t("joblines.fields.mod_lb_hrs")}
|
||||||
|
key={`${index}consumefrominventory`}
|
||||||
|
name={[field.name, "consumefrominventory"]}
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Checkbox />
|
||||||
|
</Form.Item>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
.bill-inventory-table {
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: 0px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
import { FileAddFilled } from "@ant-design/icons";
|
||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { Button, notification, Tooltip } from "antd";
|
||||||
|
import { t } from "i18next";
|
||||||
|
import moment from "moment";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { INSERT_INVENTORY_AND_CREDIT } from "../../graphql/inventory.queries";
|
||||||
|
import {
|
||||||
|
selectBodyshop,
|
||||||
|
selectCurrentUser,
|
||||||
|
} from "../../redux/user/user.selectors";
|
||||||
|
import { CalculateBillTotal } from "../bill-form/bill-form.totals.utility";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(BilllineAddInventory);
|
||||||
|
|
||||||
|
export function BilllineAddInventory({
|
||||||
|
currentUser,
|
||||||
|
bodyshop,
|
||||||
|
billline,
|
||||||
|
disabled,
|
||||||
|
jobid,
|
||||||
|
}) {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const { billid } = queryString.parse(useLocation().search);
|
||||||
|
|
||||||
|
const [insertInventoryLine] = useMutation(INSERT_INVENTORY_AND_CREDIT);
|
||||||
|
|
||||||
|
const addToInventory = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
//Check to make sure there are no existing items already in the inventory.
|
||||||
|
|
||||||
|
const cm = {
|
||||||
|
vendorid: bodyshop.inhousevendorid,
|
||||||
|
invoice_number: "ih",
|
||||||
|
jobid: jobid,
|
||||||
|
isinhouse: true,
|
||||||
|
is_credit_memo: true,
|
||||||
|
date: moment().format("YYYY-MM-DD"),
|
||||||
|
federal_tax_rate: bodyshop.bill_tax_rates.federal_tax_rate,
|
||||||
|
state_tax_rate: bodyshop.bill_tax_rates.state_tax_rate,
|
||||||
|
local_tax_rate: bodyshop.bill_tax_rates.local_tax_rate,
|
||||||
|
total: 0,
|
||||||
|
billlines: [
|
||||||
|
{
|
||||||
|
actual_price: billline.actual_price,
|
||||||
|
actual_cost: billline.actual_cost,
|
||||||
|
quantity: billline.quantity,
|
||||||
|
line_desc: billline.line_desc,
|
||||||
|
cost_center: billline.cost_center,
|
||||||
|
deductedfromlbr: billline.deductedfromlbr,
|
||||||
|
applicable_taxes: {
|
||||||
|
local: false, //billline.applicable_taxes.local,
|
||||||
|
state: false, //billline.applicable_taxes.state,
|
||||||
|
federal: false, // billline.applicable_taxes.federal,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
cm.total = CalculateBillTotal(cm).enteredTotal.getAmount() / 100;
|
||||||
|
|
||||||
|
const insertResult = await insertInventoryLine({
|
||||||
|
variables: {
|
||||||
|
joblineId: billline.joblineid,
|
||||||
|
joblineStatus: bodyshop.md_order_statuses.default_returned,
|
||||||
|
inv: {
|
||||||
|
shopid: bodyshop.id,
|
||||||
|
billlineid: billline.id,
|
||||||
|
actual_price: billline.actual_price,
|
||||||
|
actual_cost: billline.actual_cost,
|
||||||
|
quantity: billline.quantity,
|
||||||
|
line_desc: billline.line_desc,
|
||||||
|
},
|
||||||
|
cm: { ...cm, billlines: { data: cm.billlines } }, //Fix structure for apollo insert.
|
||||||
|
pol: {
|
||||||
|
returnfrombill: billid,
|
||||||
|
vendorid: bodyshop.inhousevendorid,
|
||||||
|
deliver_by: moment().format("YYYY-MM-DD"),
|
||||||
|
parts_order_lines: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
line_desc: billline.line_desc,
|
||||||
|
|
||||||
|
act_price: billline.actual_price,
|
||||||
|
cost: billline.actual_cost,
|
||||||
|
quantity: billline.quantity,
|
||||||
|
job_line_id: billline.joblineid,
|
||||||
|
part_type: billline.jobline.part_type,
|
||||||
|
cm_received: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
order_date: "2022-06-01",
|
||||||
|
orderedby: currentUser.email,
|
||||||
|
jobid: jobid,
|
||||||
|
user_email: currentUser.email,
|
||||||
|
return: true,
|
||||||
|
status: "Ordered",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
refetchQueries: ["QUERY_BILL_BY_PK"],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!insertResult.errors) {
|
||||||
|
notification.open({
|
||||||
|
type: "success",
|
||||||
|
message: t("inventory.successes.inserted"),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("inventory.errors.inserting", {
|
||||||
|
error: JSON.stringify(insertResult.errors),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip title={t("inventory.actions.addtoinventory")}>
|
||||||
|
<Button
|
||||||
|
loading={loading}
|
||||||
|
disabled={disabled || billline?.inventories?.length > 0}
|
||||||
|
onClick={addToInventory}
|
||||||
|
>
|
||||||
|
<FileAddFilled />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { SyncOutlined } from "@ant-design/icons";
|
import { SyncOutlined, WarningFilled } from "@ant-design/icons";
|
||||||
import { Button, Card, Input, Space, Table } from "antd";
|
import { Button, Card, Input, Space, Table, Tooltip } from "antd";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
|
import moment from "moment";
|
||||||
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
@@ -56,7 +56,25 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
onFilter: (value, record) => value.includes(record.status),
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
sortOrder:
|
sortOrder:
|
||||||
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
render: (text, record) => t(record.status),
|
render: (text, record) => {
|
||||||
|
const { nextservicedate, nextservicekm, mileage } = record;
|
||||||
|
|
||||||
|
const mileageOver = nextservicekm <= mileage;
|
||||||
|
|
||||||
|
const dueForService =
|
||||||
|
nextservicedate && moment(nextservicedate).isBefore(moment());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
{t(record.status)}
|
||||||
|
{(mileageOver || dueForService) && (
|
||||||
|
<Tooltip title={t("contracts.labels.cardueforservice")}>
|
||||||
|
<WarningFilled style={{ color: "tomato" }} />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("courtesycars.fields.year"),
|
title: t("courtesycars.fields.year"),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useTreatments } from "@splitsoftware/splitio-react";
|
||||||
import Icon, {
|
import Icon, {
|
||||||
BankFilled,
|
BankFilled,
|
||||||
BarChartOutlined,
|
BarChartOutlined,
|
||||||
@@ -83,6 +84,12 @@ function Header({
|
|||||||
setReportCenterContext,
|
setReportCenterContext,
|
||||||
recentItems,
|
recentItems,
|
||||||
}) {
|
}) {
|
||||||
|
const { Simple_Inventory } = useTreatments(
|
||||||
|
["Simple_Inventory"],
|
||||||
|
{},
|
||||||
|
bodyshop && bodyshop.imexshopid
|
||||||
|
);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -199,7 +206,20 @@ function Header({
|
|||||||
>
|
>
|
||||||
{t("menus.header.enterbills")}
|
{t("menus.header.enterbills")}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Divider key="div4" />
|
{Simple_Inventory.treatment === "on" && (
|
||||||
|
<>
|
||||||
|
<Menu.Divider key="div4" />
|
||||||
|
<Menu.Item
|
||||||
|
key="inventory"
|
||||||
|
icon={<Icon component={FaFileInvoiceDollar} />}
|
||||||
|
>
|
||||||
|
<Link to="/manage/inventory">
|
||||||
|
{t("menus.header.inventory")}
|
||||||
|
</Link>
|
||||||
|
</Menu.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<Menu.Divider key="div7" />
|
||||||
<Menu.Item key="allpayments" icon={<BankFilled />}>
|
<Menu.Item key="allpayments" icon={<BankFilled />}>
|
||||||
<Link to="/manage/payments">{t("menus.header.allpayments")}</Link>
|
<Link to="/manage/payments">{t("menus.header.allpayments")}</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
@@ -216,7 +236,6 @@ function Header({
|
|||||||
{t("menus.header.enterpayment")}
|
{t("menus.header.enterpayment")}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Divider key="div5" />
|
<Menu.Divider key="div5" />
|
||||||
|
|
||||||
<Menu.Item key="timetickets" icon={<FieldTimeOutlined />}>
|
<Menu.Item key="timetickets" icon={<FieldTimeOutlined />}>
|
||||||
<Link to="/manage/timetickets">
|
<Link to="/manage/timetickets">
|
||||||
{t("menus.header.timetickets")}
|
{t("menus.header.timetickets")}
|
||||||
@@ -235,7 +254,6 @@ function Header({
|
|||||||
{t("menus.header.entertimeticket")}
|
{t("menus.header.entertimeticket")}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Divider key="div6" />
|
<Menu.Divider key="div6" />
|
||||||
|
|
||||||
<Menu.SubMenu
|
<Menu.SubMenu
|
||||||
key="accountingexport"
|
key="accountingexport"
|
||||||
title={t("menus.header.export")}
|
title={t("menus.header.export")}
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
|
import { Button, Card, Input, Space, Table, Typography } from "antd";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//currentUser: selectCurrentUser
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
|
||||||
|
export function JobsList({ bodyshop, refetch, loading, jobs, total }) {
|
||||||
|
const search = queryString.parse(useLocation().search);
|
||||||
|
const { page, sortcolumn, sortorder } = search;
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("billlines.fields.line_desc"),
|
||||||
|
dataIndex: "line_desc",
|
||||||
|
key: "line_desc",
|
||||||
|
|
||||||
|
sorter: true, //(a, b) => alphaSort(a.line_desc, b.line_desc),
|
||||||
|
sortOrder: sortcolumn === "line_desc" && sortorder,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("inventory.labels.frombillinvoicenumber"),
|
||||||
|
dataIndex: "vendorname",
|
||||||
|
key: "vendorname",
|
||||||
|
ellipsis: true,
|
||||||
|
//sorter: true, // (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||||
|
|
||||||
|
//sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
||||||
|
render: (text, record) => record.billline?.bill?.invoice_number,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("inventory.labels.fromvendor"),
|
||||||
|
dataIndex: "vendorname",
|
||||||
|
key: "vendorname",
|
||||||
|
ellipsis: true,
|
||||||
|
//sorter: true, // (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||||
|
|
||||||
|
//sortOrder: sortcolumn === "ownr_ln" && sortorder,
|
||||||
|
render: (text, record) => record.billline?.bill?.vendor?.name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("billlines.fields.actual_price"),
|
||||||
|
dataIndex: "actual_price",
|
||||||
|
key: "actual_price",
|
||||||
|
|
||||||
|
render: (text, record) => (
|
||||||
|
<CurrencyFormatter>{record.actual_price}</CurrencyFormatter>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("billlines.fields.actual_cost"),
|
||||||
|
dataIndex: "actual_cost",
|
||||||
|
key: "actual_cost",
|
||||||
|
|
||||||
|
render: (text, record) => (
|
||||||
|
<CurrencyFormatter>{record.actual_cost}</CurrencyFormatter>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("inventory.labels.consumedbyjob"),
|
||||||
|
dataIndex: "consumedbyjob",
|
||||||
|
key: "consumedbyjob",
|
||||||
|
|
||||||
|
ellipsis: true,
|
||||||
|
render: (text, record) => record.bill?.job?.ro_number,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
|
search.page = pagination.current;
|
||||||
|
search.sortcolumn = sorter.column && sorter.column.key;
|
||||||
|
search.sortorder = sorter.order;
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
extra={
|
||||||
|
<Space wrap>
|
||||||
|
{search.search && (
|
||||||
|
<>
|
||||||
|
<Typography.Title level={4}>
|
||||||
|
{t("general.labels.searchresults", { search: search.search })}
|
||||||
|
</Typography.Title>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
delete search.search;
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("general.actions.clear")}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (search.showall) delete search.showall;
|
||||||
|
else {
|
||||||
|
search.showall = true;
|
||||||
|
}
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{search.showall
|
||||||
|
? t("inventory.labels.showavailable")
|
||||||
|
: t("inventory.labels.showall")}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button onClick={() => refetch()}>
|
||||||
|
<SyncOutlined />
|
||||||
|
</Button>
|
||||||
|
<Input.Search
|
||||||
|
placeholder={search.search || t("general.labels.search")}
|
||||||
|
onSearch={(value) => {
|
||||||
|
search.search = value;
|
||||||
|
history.push({ search: queryString.stringify(search) });
|
||||||
|
}}
|
||||||
|
enterButton
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
loading={loading}
|
||||||
|
pagination={{
|
||||||
|
position: "top",
|
||||||
|
pageSize: 25,
|
||||||
|
current: parseInt(page || 1),
|
||||||
|
total: total,
|
||||||
|
}}
|
||||||
|
columns={columns}
|
||||||
|
rowKey="id"
|
||||||
|
dataSource={jobs}
|
||||||
|
onChange={handleTableChange}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(JobsList);
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { QUERY_INVENTORY_PAGINATED } from "../../graphql/inventory.queries";
|
||||||
|
import {
|
||||||
|
setBreadcrumbs,
|
||||||
|
setSelectedHeader,
|
||||||
|
} from "../../redux/application/application.actions";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import InventoryListPaginated from "./inventory-list.component";
|
||||||
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function InventoryList({ setBreadcrumbs, setSelectedHeader }) {
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
const { page, sortcolumn, sortorder, search, showall } = searchParams;
|
||||||
|
|
||||||
|
const { loading, error, data, refetch } = useQuery(
|
||||||
|
QUERY_INVENTORY_PAGINATED,
|
||||||
|
{
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
variables: {
|
||||||
|
search: search || "",
|
||||||
|
offset: page ? (page - 1) * 25 : 0,
|
||||||
|
limit: 25,
|
||||||
|
consumedIsNull: showall === "true" ? null : true,
|
||||||
|
order: [
|
||||||
|
{
|
||||||
|
[sortcolumn || "created_at"]:
|
||||||
|
sortorder && sortorder !== "false"
|
||||||
|
? sortorder === "descend"
|
||||||
|
? "desc"
|
||||||
|
: "asc"
|
||||||
|
: "desc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
return (
|
||||||
|
<RbacWrapper action="jobs:list-all">
|
||||||
|
<InventoryListPaginated
|
||||||
|
refetch={refetch}
|
||||||
|
loading={loading}
|
||||||
|
searchParams={searchParams}
|
||||||
|
total={data ? data.search_inventory_aggregate.aggregate.count : 0}
|
||||||
|
jobs={data ? data.search_inventory : []}
|
||||||
|
/>
|
||||||
|
</RbacWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(InventoryList);
|
||||||
@@ -86,6 +86,7 @@ export default function JobBillsTotalComponent({
|
|||||||
|
|
||||||
const totalPartsSublet = Dinero(totals.parts.parts.total)
|
const totalPartsSublet = Dinero(totals.parts.parts.total)
|
||||||
.add(Dinero(totals.parts.sublets.total))
|
.add(Dinero(totals.parts.sublets.total))
|
||||||
|
.add(Dinero(totals.additional.shipping))
|
||||||
.add(Dinero(totals.additional.towing));
|
.add(Dinero(totals.additional.towing));
|
||||||
|
|
||||||
const discrepancy = totalPartsSublet.subtract(billTotals);
|
const discrepancy = totalPartsSublet.subtract(billTotals);
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ export default function JobReconciliationModalComponent({ job, bills }) {
|
|||||||
(j.part_type !== null && j.part_type !== "PAE") ||
|
(j.part_type !== null && j.part_type !== "PAE") ||
|
||||||
(j.line_desc &&
|
(j.line_desc &&
|
||||||
j.line_desc.toLowerCase().includes("towing") &&
|
j.line_desc.toLowerCase().includes("towing") &&
|
||||||
j.lbr_op === "OP13")
|
j.lbr_op === "OP13") ||
|
||||||
|
j.db_ref === "936004" //ADD SHIPPING LINE.
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
export const reconcileByAssocLine = (
|
export const reconcileByAssocLine = (
|
||||||
jobLines,
|
jobLines,
|
||||||
jobLineState,
|
jobLineState,
|
||||||
@@ -73,7 +74,12 @@ export const reconcileByPrice = (
|
|||||||
|
|
||||||
jobLines.forEach((jl) => {
|
jobLines.forEach((jl) => {
|
||||||
const matchingBillLineIds = billLines
|
const matchingBillLineIds = billLines
|
||||||
.filter((bl) => bl.actual_price === jl.act_price && bl.quantity === jl.part_qty && !jl.removed)
|
.filter(
|
||||||
|
(bl) =>
|
||||||
|
bl.actual_price === jl.act_price &&
|
||||||
|
bl.quantity === jl.part_qty &&
|
||||||
|
!jl.removed
|
||||||
|
)
|
||||||
.map((bl) => bl.id);
|
.map((bl) => bl.id);
|
||||||
|
|
||||||
if (matchingBillLineIds.length > 1) {
|
if (matchingBillLineIds.length > 1) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const ret = {
|
|||||||
"jobs:detail": 1,
|
"jobs:detail": 1,
|
||||||
"jobs:partsqueue": 4,
|
"jobs:partsqueue": 4,
|
||||||
"jobs:checklist-view": 2,
|
"jobs:checklist-view": 2,
|
||||||
|
"jobs:list-ready": 1,
|
||||||
"bills:enter": 2,
|
"bills:enter": 2,
|
||||||
"bills:view": 2,
|
"bills:view": 2,
|
||||||
"bills:list": 2,
|
"bills:list": 2,
|
||||||
@@ -66,5 +66,7 @@ const ret = {
|
|||||||
"timetickets:shiftedit": 5,
|
"timetickets:shiftedit": 5,
|
||||||
|
|
||||||
"users:editaccess": 4,
|
"users:editaccess": 4,
|
||||||
|
|
||||||
|
"inventory:list": 1,
|
||||||
};
|
};
|
||||||
export default ret;
|
export default ret;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function ScoreboardEntryEdit({ entry }) {
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("scoredboard.successes.updated"),
|
message: t("scoreboard.successes.updated"),
|
||||||
});
|
});
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,3 +47,15 @@ export const ListOfDaysInCurrentMonth = () => {
|
|||||||
days.push(dateEnd.format("YYYY-MM-DD"));
|
days.push(dateEnd.format("YYYY-MM-DD"));
|
||||||
return days;
|
return days;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ListDaysBetween = ({ start, end }) => {
|
||||||
|
const days = [];
|
||||||
|
const dateStart = moment(start);
|
||||||
|
const dateEnd = moment(end);
|
||||||
|
while (dateEnd.diff(dateStart, "days") > 0) {
|
||||||
|
days.push(dateStart.format("YYYY-MM-DD"));
|
||||||
|
dateStart.add(1, "days");
|
||||||
|
}
|
||||||
|
days.push(dateEnd.format("YYYY-MM-DD"));
|
||||||
|
return days;
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import { Card } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import {
|
||||||
|
Bar,
|
||||||
|
CartesianGrid,
|
||||||
|
ComposedChart,
|
||||||
|
Legend,
|
||||||
|
ResponsiveContainer,
|
||||||
|
Tooltip,
|
||||||
|
XAxis,
|
||||||
|
YAxis,
|
||||||
|
} from "recharts";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import TimeTicketsDatesSelector from "../ticket-tickets-dates-selector/time-tickets-dates-selector.component";
|
||||||
|
const graphProps = {
|
||||||
|
strokeWidth: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ScoreboardTicketsBar);
|
||||||
|
|
||||||
|
export function ScoreboardTicketsBar({ data, bodyshop }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
title={t("scoreboard.labels.productivetimeticketsoverdate")}
|
||||||
|
extra={<TimeTicketsDatesSelector />}
|
||||||
|
>
|
||||||
|
<ResponsiveContainer width="100%" height={475}>
|
||||||
|
<ComposedChart
|
||||||
|
data={data.chartData}
|
||||||
|
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
|
||||||
|
>
|
||||||
|
<CartesianGrid stroke="#f5f5f5" />
|
||||||
|
<XAxis dataKey="date" strokeWidth={graphProps.strokeWidth} />
|
||||||
|
<YAxis strokeWidth={graphProps.strokeWidth} />
|
||||||
|
<Tooltip />
|
||||||
|
<Legend />
|
||||||
|
{/* <Area
|
||||||
|
type="monotone"
|
||||||
|
name="Accumulated Hours"
|
||||||
|
dataKey="accHrs"
|
||||||
|
fill="lightgreen"
|
||||||
|
stroke="green"
|
||||||
|
/> */}
|
||||||
|
{data &&
|
||||||
|
data.employees.map((e, idx) => (
|
||||||
|
<Bar
|
||||||
|
key={`${e}productive`}
|
||||||
|
name={e}
|
||||||
|
dataKey={`employees.${e}.productive`}
|
||||||
|
stackId="productive"
|
||||||
|
// barSize={20}
|
||||||
|
fill={data.colors[idx]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* <Line
|
||||||
|
name="Target Hours"
|
||||||
|
type="monotone"
|
||||||
|
dataKey="accTargetHrs"
|
||||||
|
stroke="#ff7300"
|
||||||
|
strokeWidth={graphProps.strokeWidth}
|
||||||
|
/> */}
|
||||||
|
</ComposedChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,301 @@
|
|||||||
|
import { useQuery } from "@apollo/client";
|
||||||
|
import { Col, Row } from "antd";
|
||||||
|
import _ from "lodash";
|
||||||
|
import moment from "moment";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import React, { useMemo } from "react";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
import { QUERY_TIME_TICKETS_IN_RANGE } from "../../graphql/timetickets.queries";
|
||||||
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
|
||||||
|
import ScoreboardTicketsBar from "./scoreboard-timetickets.bar.component";
|
||||||
|
import ScoreboardTicketsStats from "./scoreboard-timetickets.stats.component";
|
||||||
|
|
||||||
|
export default function ScoreboardTimeTickets() {
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
const { start, end } = searchParams;
|
||||||
|
const startDate = start
|
||||||
|
? moment(start)
|
||||||
|
: moment().startOf("week").subtract(7, "days");
|
||||||
|
const endDate = end ? moment(end) : moment().endOf("week");
|
||||||
|
|
||||||
|
const fixedPeriods = useMemo(() => {
|
||||||
|
const endOfThisMonth = moment().endOf("month");
|
||||||
|
const startofthisMonth = moment().startOf("month");
|
||||||
|
|
||||||
|
const endOfLastmonth = moment().subtract(1, "month").endOf("month");
|
||||||
|
const startOfLastmonth = moment().subtract(1, "month").startOf("month");
|
||||||
|
|
||||||
|
const endOfThisWeek = moment().endOf("week");
|
||||||
|
const startOfThisWeek = moment().startOf("week");
|
||||||
|
|
||||||
|
const endOfLastWeek = moment().subtract(1, "week").endOf("week");
|
||||||
|
const startOfLastWeek = moment().subtract(1, "week").startOf("week");
|
||||||
|
|
||||||
|
const allDates = [
|
||||||
|
endOfThisMonth,
|
||||||
|
startofthisMonth,
|
||||||
|
endOfLastmonth,
|
||||||
|
startOfLastmonth,
|
||||||
|
endOfThisWeek,
|
||||||
|
startOfThisWeek,
|
||||||
|
endOfLastWeek,
|
||||||
|
startOfLastWeek,
|
||||||
|
];
|
||||||
|
const start = moment.min(allDates);
|
||||||
|
const end = moment.max(allDates);
|
||||||
|
return {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
endOfThisMonth,
|
||||||
|
startofthisMonth,
|
||||||
|
endOfLastmonth,
|
||||||
|
startOfLastmonth,
|
||||||
|
endOfThisWeek,
|
||||||
|
startOfThisWeek,
|
||||||
|
endOfLastWeek,
|
||||||
|
startOfLastWeek,
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const { loading, error, data } = useQuery(QUERY_TIME_TICKETS_IN_RANGE, {
|
||||||
|
variables: {
|
||||||
|
start: startDate.format("YYYY-MM-DD"),
|
||||||
|
end: endDate.format("YYYY-MM-DD"),
|
||||||
|
fixedStart: fixedPeriods.start.format("YYYY-MM-DD"),
|
||||||
|
fixedEnd: fixedPeriods.end.format("YYYY-MM-DD"),
|
||||||
|
},
|
||||||
|
fetchPolicy: "network-only",
|
||||||
|
nextFetchPolicy: "network-only",
|
||||||
|
pollInterval: 60000,
|
||||||
|
skip: !fixedPeriods,
|
||||||
|
});
|
||||||
|
|
||||||
|
const calculatedData = useMemo(() => {
|
||||||
|
if (!data) return [];
|
||||||
|
const ret = {
|
||||||
|
totalThisWeek: 0,
|
||||||
|
totalLastWeek: 0,
|
||||||
|
totalThisMonth: 0,
|
||||||
|
totalLastMonth: 0,
|
||||||
|
totalOverPeriod: 0,
|
||||||
|
employees: {},
|
||||||
|
};
|
||||||
|
data.fixedperiod.forEach((ticket) => {
|
||||||
|
const ticketDate = moment(ticket.date);
|
||||||
|
|
||||||
|
if (!ret.employees[ticket.employee.employee_number]) {
|
||||||
|
ret.employees[ticket.employee.employee_number] = {
|
||||||
|
totalThisWeek: 0,
|
||||||
|
totalLastWeek: 0,
|
||||||
|
totalThisMonth: 0,
|
||||||
|
totalLastMonth: 0,
|
||||||
|
totalOverPeriod: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
ticketDate.isBetween(
|
||||||
|
fixedPeriods.startOfThisWeek,
|
||||||
|
fixedPeriods.endOfThisWeek,
|
||||||
|
undefined,
|
||||||
|
"[]"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ret.totalThisWeek = ret.totalThisWeek + ticket.productivehrs;
|
||||||
|
ret.employees[ticket.employee.employee_number].totalThisWeek =
|
||||||
|
ret.employees[ticket.employee.employee_number].totalThisWeek +
|
||||||
|
ticket.productivehrs;
|
||||||
|
} else if (
|
||||||
|
ticketDate.isBetween(
|
||||||
|
fixedPeriods.startOfLastWeek,
|
||||||
|
fixedPeriods.endOfLastWeek,
|
||||||
|
undefined,
|
||||||
|
"[]"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ret.totalLastWeek = ret.totalLastWeek + ticket.productivehrs;
|
||||||
|
ret.employees[ticket.employee.employee_number].totalLastWeek =
|
||||||
|
ret.employees[ticket.employee.employee_number].totalLastWeek +
|
||||||
|
ticket.productivehrs;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
ticketDate.isBetween(
|
||||||
|
fixedPeriods.startofthisMonth,
|
||||||
|
fixedPeriods.endOfThisMonth,
|
||||||
|
undefined,
|
||||||
|
"[]"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ret.totalThisMonth = ret.totalThisMonth + ticket.productivehrs;
|
||||||
|
ret.employees[ticket.employee.employee_number].totalThisMonth =
|
||||||
|
ret.employees[ticket.employee.employee_number].totalThisMonth +
|
||||||
|
ticket.productivehrs;
|
||||||
|
} else if (
|
||||||
|
ticketDate.isBetween(
|
||||||
|
fixedPeriods.startOfLastmonth,
|
||||||
|
fixedPeriods.endOfLastmonth,
|
||||||
|
undefined,
|
||||||
|
"[]"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ret.totalLastMonth = ret.totalLastMonth + ticket.productivehrs;
|
||||||
|
ret.employees[ticket.employee.employee_number].totalLastMonth =
|
||||||
|
ret.employees[ticket.employee.employee_number].totalLastMonth +
|
||||||
|
ticket.productivehrs;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const ticketsGroupedByDate = _.groupBy(data.timetickets, "date");
|
||||||
|
const listOfDays = Utils.ListDaysBetween({
|
||||||
|
start: startDate,
|
||||||
|
end: endDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
const employees = [];
|
||||||
|
const ret2 = [];
|
||||||
|
let totals = {
|
||||||
|
totalproductive: 0,
|
||||||
|
totalactual: 0,
|
||||||
|
employees: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
listOfDays.forEach((day) => {
|
||||||
|
const r = {
|
||||||
|
date: moment(day).format("MM/DD"),
|
||||||
|
actualtotal: 0,
|
||||||
|
productivetotal: 0,
|
||||||
|
employees: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ticketsGroupedByDate[day]) {
|
||||||
|
ticketsGroupedByDate[day].forEach((ticket) => {
|
||||||
|
r.actualtotal = r.actualtotal + ticket.actualhrs;
|
||||||
|
r.productivetotal = r.productivetotal + ticket.productivehrs;
|
||||||
|
totals.totalactual = totals.totalactual + ticket.actualhrs;
|
||||||
|
totals.totalproductive =
|
||||||
|
totals.totalproductive + ticket.productivehrs;
|
||||||
|
|
||||||
|
employees.push(ticket.employee.employee_number);
|
||||||
|
//Add to table data.
|
||||||
|
ret.employees[ticket.employee.employee_number].totalOverPeriod =
|
||||||
|
ret.employees[ticket.employee.employee_number].totalOverPeriod +
|
||||||
|
ticket.productivehrs;
|
||||||
|
|
||||||
|
if (!totals.employees[ticket.employee.employee_number])
|
||||||
|
totals.employees[ticket.employee.employee_number] = {
|
||||||
|
totalactual: 0,
|
||||||
|
totalproductive: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!r.employees[ticket.employee.employee_number])
|
||||||
|
r.employees[ticket.employee.employee_number] = {
|
||||||
|
actual: 0,
|
||||||
|
productive: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
//Add to totals.
|
||||||
|
totals.employees[ticket.employee.employee_number].totalproductive =
|
||||||
|
totals.employees[ticket.employee.employee_number].totalproductive +
|
||||||
|
ticket.productivehrs;
|
||||||
|
|
||||||
|
totals.employees[ticket.employee.employee_number].totalactual =
|
||||||
|
totals.employees[ticket.employee.employee_number].totalactual +
|
||||||
|
ticket.actualhrs;
|
||||||
|
//Add to dailys.
|
||||||
|
r.employees[ticket.employee.employee_number].productive =
|
||||||
|
r.employees[ticket.employee.employee_number].productive +
|
||||||
|
ticket.productivehrs;
|
||||||
|
|
||||||
|
r.employees[ticket.employee.employee_number].actual =
|
||||||
|
r.employees[ticket.employee.employee_number].actual +
|
||||||
|
ticket.actualhrs;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ret2.push(r);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
fixed: ret,
|
||||||
|
timeperiod: {
|
||||||
|
totals,
|
||||||
|
chartData: ret2,
|
||||||
|
employees: _.uniq(employees),
|
||||||
|
colors: getColorArray(employees.length),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, [fixedPeriods, data, startDate, endDate]);
|
||||||
|
|
||||||
|
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||||
|
if (loading) return <LoadingSpinner />;
|
||||||
|
return (
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={24}>
|
||||||
|
<ScoreboardTicketsStats data={calculatedData.fixed} />
|
||||||
|
</Col>
|
||||||
|
<Col span={24}>
|
||||||
|
<ScoreboardTicketsBar
|
||||||
|
start={startDate}
|
||||||
|
end={endDate}
|
||||||
|
data={calculatedData.timeperiod}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Include a filter by employee.
|
||||||
|
|
||||||
|
//Hours produced today.
|
||||||
|
//Hours produced in last 7 days
|
||||||
|
//Hours produced for time period by day
|
||||||
|
//Hours produced by employee by day for time period.
|
||||||
|
|
||||||
|
function getColorArray(num) {
|
||||||
|
return [
|
||||||
|
"#3366cc",
|
||||||
|
"#dc3912",
|
||||||
|
"#ff9900",
|
||||||
|
"#109618",
|
||||||
|
"#990099",
|
||||||
|
"#0099c6",
|
||||||
|
"#dd4477",
|
||||||
|
"#66aa00",
|
||||||
|
"#b82e2e",
|
||||||
|
"#316395",
|
||||||
|
"#3366cc",
|
||||||
|
"#994499",
|
||||||
|
"#22aa99",
|
||||||
|
"#aaaa11",
|
||||||
|
"#6633cc",
|
||||||
|
"#e67300",
|
||||||
|
"#8b0707",
|
||||||
|
"#651067",
|
||||||
|
"#329262",
|
||||||
|
"#5574a6",
|
||||||
|
"#3b3eac",
|
||||||
|
"#b77322",
|
||||||
|
"#16d620",
|
||||||
|
"#b91383",
|
||||||
|
"#f4359e",
|
||||||
|
"#9c5935",
|
||||||
|
"#a9c413",
|
||||||
|
"#2a778d",
|
||||||
|
"#668d1c",
|
||||||
|
"#bea413",
|
||||||
|
"#0c5922",
|
||||||
|
"#743411",
|
||||||
|
];
|
||||||
|
// var result = [];
|
||||||
|
// for (var i = 0; i < num; i += 1) {
|
||||||
|
// var letters = "0123456789ABCDEF".split("");
|
||||||
|
// var color = "#";
|
||||||
|
// for (var j = 0; j < 6; j += 1) {
|
||||||
|
// color += letters[Math.floor(Math.random() * 16)];
|
||||||
|
// }
|
||||||
|
// result.push(color);
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import { Card, Col, Row, Statistic, Table, Typography } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ScoreboardTicketsStats);
|
||||||
|
|
||||||
|
export function ScoreboardTicketsStats({ data, bodyshop }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t("employees.fields.employee_number"),
|
||||||
|
dataIndex: "employee_number",
|
||||||
|
key: "employee_number",
|
||||||
|
sorter: (a, b) => a.employee_number - b.employee_number,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("scoreboard.labels.thisweek"),
|
||||||
|
dataIndex: "totalThisWeek",
|
||||||
|
key: "totalThisWeek",
|
||||||
|
sorter: (a, b) => a.totalThisWeek - b.totalThisWeek,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("scoreboard.labels.lastweek"),
|
||||||
|
dataIndex: "totalLastWeek",
|
||||||
|
key: "totalLastWeek",
|
||||||
|
sorter: (a, b) => a.totalLastWeek - b.totalLastWeek,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("scoreboard.labels.thismonth"),
|
||||||
|
dataIndex: "totalThisMonth",
|
||||||
|
key: "totalThisMonth",
|
||||||
|
sorter: (a, b) => a.totalThisMonth - b.totalThisMonth,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("scoreboard.labels.lastmonth"),
|
||||||
|
dataIndex: "totalLastMonth",
|
||||||
|
key: "totalLastMonth",
|
||||||
|
sorter: (a, b) => a.totalLastMonth - b.totalLastMonth,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("scoreboard.labels.totaloverperiod"),
|
||||||
|
dataIndex: "totalOverPeriod",
|
||||||
|
key: "totalOverPeriod",
|
||||||
|
sorter: (a, b) => a.totalOverPeriod - b.totalOverPeriod,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const tableData = data
|
||||||
|
? Object.keys(data.employees).map((key) => {
|
||||||
|
return { employee_number: key, ...data.employees[key] };
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card title={t("scoreboard.labels.productivestatistics")}>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col md={24} lg={4}>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Statistic
|
||||||
|
title={t("scoreboard.labels.lastweek")}
|
||||||
|
value={data.totalLastWeek.toFixed(1)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Statistic
|
||||||
|
title={t("scoreboard.labels.lastmonth")}
|
||||||
|
value={data.totalLastMonth.toFixed(1)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={[16, 16]}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Statistic
|
||||||
|
title={t("scoreboard.labels.thisweek")}
|
||||||
|
value={data.totalThisWeek.toFixed(1)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Statistic
|
||||||
|
title={t("scoreboard.labels.thismonth")}
|
||||||
|
value={data.totalThisMonth.toFixed(1)}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Typography.Text type="secondary">
|
||||||
|
{t("scoreboard.labels.calendarperiod")}
|
||||||
|
</Typography.Text>
|
||||||
|
</Col>
|
||||||
|
<Col md={24} lg={20}>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={tableData}
|
||||||
|
id="employee_number"
|
||||||
|
scroll={{ y: "300px" }}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -633,6 +633,18 @@ export default function ShopInfoRbacComponent({ form }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.inventory.list")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "inventory:list"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
</RbacWrapper>
|
</RbacWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,13 +11,21 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import TechClockInComponent from "./tech-job-clock-in-form.component";
|
import TechClockInComponent from "./tech-job-clock-in-form.component";
|
||||||
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
|
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
technician: selectTechnician,
|
technician: selectTechnician,
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
export function TechClockInContainer({ technician, bodyshop }) {
|
setTimeTicketContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||||
|
});
|
||||||
|
export function TechClockInContainer({
|
||||||
|
setTimeTicketContext,
|
||||||
|
technician,
|
||||||
|
bodyshop,
|
||||||
|
}) {
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET, {
|
const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET, {
|
||||||
@@ -75,6 +83,16 @@ export function TechClockInContainer({ technician, bodyshop }) {
|
|||||||
title={t("timetickets.labels.clockintojob")}
|
title={t("timetickets.labels.clockintojob")}
|
||||||
extra={
|
extra={
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setTimeTicketContext({
|
||||||
|
actions: {},
|
||||||
|
context: { timeticket: { employeeid: technician.id } },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("timetickets.actions.enter")}
|
||||||
|
</Button>
|
||||||
<TechJobPrintTickets />
|
<TechJobPrintTickets />
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
@@ -92,4 +110,7 @@ export function TechClockInContainer({ technician, bodyshop }) {
|
|||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default connect(mapStateToProps, null)(TechClockInContainer);
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(TechClockInContainer);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export default function TimeTicketsDatesSelector() {
|
|||||||
if (!!start && !!end) {
|
if (!!start && !!end) {
|
||||||
history.push({
|
history.push({
|
||||||
search: queryString.stringify({
|
search: queryString.stringify({
|
||||||
|
...searchParams,
|
||||||
start: start.format("YYYY-MM-DD"),
|
start: start.format("YYYY-MM-DD"),
|
||||||
end: end.format("YYYY-MM-DD"),
|
end: end.format("YYYY-MM-DD"),
|
||||||
}),
|
}),
|
||||||
@@ -25,6 +26,7 @@ export default function TimeTicketsDatesSelector() {
|
|||||||
} else {
|
} else {
|
||||||
history.push({
|
history.push({
|
||||||
search: queryString.stringify({
|
search: queryString.stringify({
|
||||||
|
...searchParams,
|
||||||
start: null,
|
start: null,
|
||||||
end: null,
|
end: null,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export function TimeTicketModalComponent({
|
|||||||
authLevel,
|
authLevel,
|
||||||
employeeAutoCompleteOptions,
|
employeeAutoCompleteOptions,
|
||||||
isEdit,
|
isEdit,
|
||||||
|
employeeSelectDisabled,
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -118,6 +119,7 @@ export function TimeTicketModalComponent({
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<EmployeeSearchSelect
|
<EmployeeSearchSelect
|
||||||
|
disabled={employeeSelectDisabled}
|
||||||
options={employeeAutoCompleteOptions}
|
options={employeeAutoCompleteOptions}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
const emps =
|
const emps =
|
||||||
|
|||||||
@@ -244,6 +244,12 @@ export function TimeTicketModalContainer({
|
|||||||
employeeAutoCompleteOptions={
|
employeeAutoCompleteOptions={
|
||||||
EmployeeAutoCompleteData && EmployeeAutoCompleteData.employees
|
EmployeeAutoCompleteData && EmployeeAutoCompleteData.employees
|
||||||
}
|
}
|
||||||
|
employeeSelectDisabled={
|
||||||
|
timeTicketModal.context?.timeticket?.employeeid &&
|
||||||
|
!timeTicketModal.context.id
|
||||||
|
? true
|
||||||
|
: false
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ export const QUERY_BILL_BY_PK = gql`
|
|||||||
state_tax_rate
|
state_tax_rate
|
||||||
federal_tax_rate
|
federal_tax_rate
|
||||||
isinhouse
|
isinhouse
|
||||||
|
inventories {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
}
|
||||||
vendor {
|
vendor {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@@ -165,6 +169,9 @@ export const QUERY_BILL_BY_PK = gql`
|
|||||||
cost_center
|
cost_center
|
||||||
quantity
|
quantity
|
||||||
joblineid
|
joblineid
|
||||||
|
inventories {
|
||||||
|
id
|
||||||
|
}
|
||||||
jobline {
|
jobline {
|
||||||
oem_partno
|
oem_partno
|
||||||
part_type
|
part_type
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export const QUERY_ALL_CC = gql`
|
|||||||
status
|
status
|
||||||
vin
|
vin
|
||||||
year
|
year
|
||||||
|
mileage
|
||||||
cccontracts(
|
cccontracts(
|
||||||
where: { status: { _eq: "contracts.status.out" } }
|
where: { status: { _eq: "contracts.status.out" } }
|
||||||
order_by: { contract_date: desc }
|
order_by: { contract_date: desc }
|
||||||
|
|||||||
112
client/src/graphql/inventory.queries.js
Normal file
112
client/src/graphql/inventory.queries.js
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { gql } from "@apollo/client";
|
||||||
|
|
||||||
|
export const INSERT_INVENTORY_AND_CREDIT = gql`
|
||||||
|
mutation INSERT_INVENTORY_AND_CREDIT(
|
||||||
|
$inv: inventory_insert_input!
|
||||||
|
$cm: bills_insert_input!
|
||||||
|
$pol: parts_orders_insert_input!
|
||||||
|
$joblineId: uuid!
|
||||||
|
$joblineStatus: String
|
||||||
|
) {
|
||||||
|
insert_inventory_one(object: $inv) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
insert_bills_one(object: $cm) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
insert_parts_orders_one(object: $pol) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
update_joblines_by_pk(
|
||||||
|
pk_columns: { id: $joblineId }
|
||||||
|
_set: { status: $joblineStatus }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const UPDATE_INVENTORY_LINES = gql`
|
||||||
|
mutation UPDATE_INVENTORY_LINES(
|
||||||
|
$InventoryIds: [uuid!]!
|
||||||
|
$consumedbybillid: uuid!
|
||||||
|
) {
|
||||||
|
update_inventory(
|
||||||
|
where: { id: { _in: $InventoryIds } }
|
||||||
|
_set: { consumedbybillid: $consumedbybillid }
|
||||||
|
) {
|
||||||
|
affected_rows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const QUERY_OUTSTANDING_INVENTORY = gql`
|
||||||
|
query QUERY_OUTSTANDING_INVENTORY {
|
||||||
|
inventory(where: { consumedbybillid: { _is_null: true } }) {
|
||||||
|
id
|
||||||
|
actual_cost
|
||||||
|
actual_price
|
||||||
|
quantity
|
||||||
|
billlineid
|
||||||
|
line_desc
|
||||||
|
billline {
|
||||||
|
bill {
|
||||||
|
invoice_number
|
||||||
|
vendor {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const QUERY_INVENTORY_PAGINATED = gql`
|
||||||
|
query QUERY_INVENTORY_PAGINATED(
|
||||||
|
$search: String
|
||||||
|
$offset: Int
|
||||||
|
$limit: Int
|
||||||
|
$order: [inventory_order_by!]
|
||||||
|
$consumedIsNull: Boolean
|
||||||
|
) {
|
||||||
|
search_inventory(
|
||||||
|
args: { search: $search }
|
||||||
|
offset: $offset
|
||||||
|
limit: $limit
|
||||||
|
order_by: $order
|
||||||
|
where: { consumedbybillid: { _is_null: $consumedIsNull } }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
line_desc
|
||||||
|
actual_price
|
||||||
|
actual_cost
|
||||||
|
bill {
|
||||||
|
id
|
||||||
|
invoice_number
|
||||||
|
job {
|
||||||
|
ro_number
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
billline {
|
||||||
|
id
|
||||||
|
bill {
|
||||||
|
id
|
||||||
|
invoice_number
|
||||||
|
vendor {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
search_inventory_aggregate(
|
||||||
|
args: { search: $search }
|
||||||
|
where: { consumedbybillid: { _is_null: $consumedIsNull } }
|
||||||
|
) {
|
||||||
|
aggregate {
|
||||||
|
count(distinct: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
@@ -61,6 +61,7 @@ export const GET_LINE_TICKET_BY_PK = gql`
|
|||||||
flat_rate
|
flat_rate
|
||||||
clockon
|
clockon
|
||||||
clockoff
|
clockoff
|
||||||
|
rate
|
||||||
employee {
|
employee {
|
||||||
id
|
id
|
||||||
first_name
|
first_name
|
||||||
|
|||||||
@@ -26,7 +26,12 @@ export const QUERY_TICKETS_BY_JOBID = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const QUERY_TIME_TICKETS_IN_RANGE = gql`
|
export const QUERY_TIME_TICKETS_IN_RANGE = gql`
|
||||||
query QUERY_TIME_TICKETS_IN_RANGE($start: date!, $end: date!) {
|
query QUERY_TIME_TICKETS_IN_RANGE(
|
||||||
|
$start: date!
|
||||||
|
$end: date!
|
||||||
|
$fixedStart: date!
|
||||||
|
$fixedEnd: date!
|
||||||
|
) {
|
||||||
timetickets(
|
timetickets(
|
||||||
where: { date: { _gte: $start, _lte: $end } }
|
where: { date: { _gte: $start, _lte: $end } }
|
||||||
order_by: { date: desc_nulls_first }
|
order_by: { date: desc_nulls_first }
|
||||||
@@ -56,6 +61,35 @@ export const QUERY_TIME_TICKETS_IN_RANGE = gql`
|
|||||||
last_name
|
last_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fixedperiod: timetickets(
|
||||||
|
where: { date: { _gte: $fixedStart, _lte: $fixedEnd } }
|
||||||
|
order_by: { date: desc_nulls_first }
|
||||||
|
) {
|
||||||
|
actualhrs
|
||||||
|
ciecacode
|
||||||
|
clockoff
|
||||||
|
clockon
|
||||||
|
cost_center
|
||||||
|
created_at
|
||||||
|
date
|
||||||
|
id
|
||||||
|
rate
|
||||||
|
productivehrs
|
||||||
|
memo
|
||||||
|
jobid
|
||||||
|
flat_rate
|
||||||
|
job {
|
||||||
|
id
|
||||||
|
ro_number
|
||||||
|
}
|
||||||
|
employeeid
|
||||||
|
employee {
|
||||||
|
id
|
||||||
|
employee_number
|
||||||
|
first_name
|
||||||
|
last_name
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
32
client/src/pages/inventory/inventory.page.jsx
Normal file
32
client/src/pages/inventory/inventory.page.jsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
|
import {
|
||||||
|
setBreadcrumbs,
|
||||||
|
setSelectedHeader,
|
||||||
|
} from "../../redux/application/application.actions";
|
||||||
|
import InventoryList from "../../components/inventory-list/inventory-list.container";
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||||
|
setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export function InventoryPage({ setBreadcrumbs, setSelectedHeader }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.title = t("titles.inventory");
|
||||||
|
setSelectedHeader("inventory");
|
||||||
|
setBreadcrumbs([{ link: "/manage/jobs", label: t("titles.bc.inventory") }]);
|
||||||
|
}, [t, setBreadcrumbs, setSelectedHeader]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RbacWrapper action="inventory:list">
|
||||||
|
<InventoryList />
|
||||||
|
</RbacWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps)(InventoryPage);
|
||||||
@@ -34,6 +34,7 @@ const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
|||||||
const JobsDetailPage = lazy(() =>
|
const JobsDetailPage = lazy(() =>
|
||||||
import("../jobs-detail/jobs-detail.page.container")
|
import("../jobs-detail/jobs-detail.page.container")
|
||||||
);
|
);
|
||||||
|
const InventoryListPage = lazy(() => import("../inventory/inventory.page"));
|
||||||
const ProfilePage = lazy(() => import("../profile/profile.container.page"));
|
const ProfilePage = lazy(() => import("../profile/profile.container.page"));
|
||||||
const JobsAvailablePage = lazy(() =>
|
const JobsAvailablePage = lazy(() =>
|
||||||
import("../jobs-available/jobs-available.page.container")
|
import("../jobs-available/jobs-available.page.container")
|
||||||
@@ -250,6 +251,11 @@ export function Manage({ match, conflict, bodyshop }) {
|
|||||||
<Route path={`${match.path}/jobs/:jobId`} component={JobsDetailPage} />
|
<Route path={`${match.path}/jobs/:jobId`} component={JobsDetailPage} />
|
||||||
</Switch>
|
</Switch>
|
||||||
<Route exact path={`${match.path}/temporarydocs/`} component={TempDocs} />
|
<Route exact path={`${match.path}/temporarydocs/`} component={TempDocs} />
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path={`${match.path}/inventory/`}
|
||||||
|
component={InventoryListPage}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path={`${match.path}/courtesycars/`}
|
path={`${match.path}/courtesycars/`}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { SyncOutlined } from "@ant-design/icons";
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
import { useQuery } from "@apollo/client";
|
import { useQuery } from "@apollo/client";
|
||||||
import { Button, Card, Input, Space, Table } from "antd";
|
import { Button, Card, Input, Space, Table } from "antd";
|
||||||
import _ from "lodash";
|
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -92,13 +91,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
// searchParams.page = pagination.current;
|
// searchParams.page = pagination.current;
|
||||||
searchParams.sortcolumn = sorter.columnKey;
|
searchParams.sortcolumn = sorter.columnKey;
|
||||||
searchParams.sortorder = sorter.order;
|
searchParams.sortorder = sorter.order;
|
||||||
if (filters.status) {
|
|
||||||
searchParams.statusFilters = JSON.stringify(
|
|
||||||
_.flattenDeep(filters.status)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
delete searchParams.statusFilters;
|
|
||||||
}
|
|
||||||
history.push({ search: queryString.stringify(searchParams) });
|
history.push({ search: queryString.stringify(searchParams) });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -244,6 +237,17 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
key: "queued_for_parts",
|
key: "queued_for_parts",
|
||||||
sorter: (a, b) => a.queued_for_parts - b.queued_for_parts,
|
sorter: (a, b) => a.queued_for_parts - b.queued_for_parts,
|
||||||
sortOrder: sortcolumn === "queued_for_parts" && sortorder,
|
sortOrder: sortcolumn === "queued_for_parts" && sortorder,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
text: "Queued",
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Unqueued",
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
//onFilter: (value, record) => record.queued_for_parts === value,
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<JobRemoveFromPartsQueue
|
<JobRemoveFromPartsQueue
|
||||||
checked={record.queued_for_parts}
|
checked={record.queued_for_parts}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
|
import Icon, { BarsOutlined } from "@ant-design/icons";
|
||||||
|
import { Tabs } from "antd";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { FaShieldAlt } from "react-icons/fa";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
|
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
|
||||||
|
import ScoreboardTimeTickets from "../../components/scoreboard-timetickets/scoreboard-timetickets.component";
|
||||||
import {
|
import {
|
||||||
setBreadcrumbs,
|
setBreadcrumbs,
|
||||||
setSelectedHeader,
|
setSelectedHeader,
|
||||||
} from "../../redux/application/application.actions";
|
} from "../../redux/application/application.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import queryString from "query-string";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -22,7 +28,9 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
|
|
||||||
export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
|
export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
|
const { tab } = searchParams;
|
||||||
|
const history = useHistory();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("titles.scoreboard");
|
document.title = t("titles.scoreboard");
|
||||||
setSelectedHeader("scoreboard");
|
setSelectedHeader("scoreboard");
|
||||||
@@ -37,7 +45,41 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
|
|||||||
return (
|
return (
|
||||||
<FeatureWrapper featureName="scoreboard">
|
<FeatureWrapper featureName="scoreboard">
|
||||||
<RbacWrapper action="scoreboard:view">
|
<RbacWrapper action="scoreboard:view">
|
||||||
<ScoreboardDisplay />
|
<Tabs
|
||||||
|
activeKey={tab || "sb"}
|
||||||
|
destroyInactiveTabPane
|
||||||
|
onChange={(key) => {
|
||||||
|
searchParams.tab = key;
|
||||||
|
history.push({
|
||||||
|
search: queryString.stringify(searchParams),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tabs.TabPane
|
||||||
|
tab={
|
||||||
|
<span>
|
||||||
|
<Icon component={FaShieldAlt} />
|
||||||
|
{t("scoreboard.labels.jobs")}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
destroyInactiveTabPane
|
||||||
|
key="sb"
|
||||||
|
>
|
||||||
|
<ScoreboardDisplay />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane
|
||||||
|
tab={
|
||||||
|
<span>
|
||||||
|
<BarsOutlined />
|
||||||
|
{t("scoreboard.labels.timetickets")}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
destroyInactiveTabPane
|
||||||
|
key="tickets"
|
||||||
|
>
|
||||||
|
<ScoreboardTimeTickets />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
</Tabs>
|
||||||
</RbacWrapper>
|
</RbacWrapper>
|
||||||
</FeatureWrapper>
|
</FeatureWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -352,6 +352,9 @@
|
|||||||
"employees": {
|
"employees": {
|
||||||
"page": "Employees -> List"
|
"page": "Employees -> List"
|
||||||
},
|
},
|
||||||
|
"inventory": {
|
||||||
|
"list": "Inventory -> List"
|
||||||
|
},
|
||||||
"jobs": {
|
"jobs": {
|
||||||
"admin": "Jobs -> Admin",
|
"admin": "Jobs -> Admin",
|
||||||
"available-list": "Jobs -> Available List",
|
"available-list": "Jobs -> Available List",
|
||||||
@@ -1069,6 +1072,26 @@
|
|||||||
"printpack": "Intake Print Pack"
|
"printpack": "Intake Print Pack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"inventory": {
|
||||||
|
"actions": {
|
||||||
|
"addtoinventory": "Add to Inventory",
|
||||||
|
"consumefrominventory": "Consume from Inventory?"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"inserting": "Error inserting inventory item. {{error}}"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"consumedbyjob": "Consumed by Job",
|
||||||
|
"frombillinvoicenumber": "Original Bill Invoice Number",
|
||||||
|
"fromvendor": "Original Bill Vendor",
|
||||||
|
"inventory": "Inventory",
|
||||||
|
"showall": "Show All Inventory",
|
||||||
|
"showavailable": "Show Only Available Inventory"
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"inserted": "Added line to inventory."
|
||||||
|
}
|
||||||
|
},
|
||||||
"joblines": {
|
"joblines": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"new": "New Line"
|
"new": "New Line"
|
||||||
@@ -1754,6 +1777,7 @@
|
|||||||
"export-logs": "Export Logs",
|
"export-logs": "Export Logs",
|
||||||
"help": "Help",
|
"help": "Help",
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
|
"inventory": "Inventory",
|
||||||
"jobs": "Jobs",
|
"jobs": "Jobs",
|
||||||
"newjob": "Create New Job",
|
"newjob": "Create New Job",
|
||||||
"owners": "Owners",
|
"owners": "Owners",
|
||||||
@@ -2403,11 +2427,21 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"asoftodaytarget": "As of Today",
|
"asoftodaytarget": "As of Today",
|
||||||
|
"calendarperiod": "Periods based on calendar weeks/months.",
|
||||||
"dailyactual": "Actual (D)",
|
"dailyactual": "Actual (D)",
|
||||||
"dailytarget": "Daily",
|
"dailytarget": "Daily",
|
||||||
|
"jobs": "Jobs",
|
||||||
|
"lastmonth": "Last Month",
|
||||||
|
"lastweek": "Last Week",
|
||||||
"monthlytarget": "Monthly",
|
"monthlytarget": "Monthly",
|
||||||
|
"productivestatistics": "Productive Hours Statistics",
|
||||||
|
"productivetimeticketsoverdate": "Productive Hours over Selected Dates",
|
||||||
"targets": "Targets",
|
"targets": "Targets",
|
||||||
|
"thismonth": "This Month",
|
||||||
|
"thisweek": "This Week",
|
||||||
|
"timetickets": "Timetickets",
|
||||||
"todateactual": "Actual (MTD)",
|
"todateactual": "Actual (MTD)",
|
||||||
|
"totaloverperiod": "Total over Selected Dates",
|
||||||
"weeklyactual": "Actual (W)",
|
"weeklyactual": "Actual (W)",
|
||||||
"weeklytarget": "Weekly",
|
"weeklytarget": "Weekly",
|
||||||
"workingdays": "Working Days / Month"
|
"workingdays": "Working Days / Month"
|
||||||
@@ -2418,11 +2452,6 @@
|
|||||||
"updated": "Scoreboard updated."
|
"updated": "Scoreboard updated."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scoredboard": {
|
|
||||||
"successes": {
|
|
||||||
"updated": "Scoreboard entry updated."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tech": {
|
"tech": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"employeeid": "Employee ID",
|
"employeeid": "Employee ID",
|
||||||
@@ -2523,6 +2552,7 @@
|
|||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
"dms": "DMS Export",
|
"dms": "DMS Export",
|
||||||
"export-logs": "Export Logs",
|
"export-logs": "Export Logs",
|
||||||
|
"inventory": "Inventory",
|
||||||
"jobs": "Jobs",
|
"jobs": "Jobs",
|
||||||
"jobs-active": "Active Jobs",
|
"jobs-active": "Active Jobs",
|
||||||
"jobs-admin": "Admin",
|
"jobs-admin": "Admin",
|
||||||
@@ -2563,6 +2593,7 @@
|
|||||||
"dashboard": "Dashboard | $t(titles.app)",
|
"dashboard": "Dashboard | $t(titles.app)",
|
||||||
"dms": "DMS Export | $t(titles.app)",
|
"dms": "DMS Export | $t(titles.app)",
|
||||||
"export-logs": "Export Logs | $t(titles.app)",
|
"export-logs": "Export Logs | $t(titles.app)",
|
||||||
|
"inventory": "Inventory | $t(titles.app)",
|
||||||
"jobs": "Active Jobs | $t(titles.app)",
|
"jobs": "Active Jobs | $t(titles.app)",
|
||||||
"jobs-admin": "Job {{ro_number}} - Admin | $t(titles.app)",
|
"jobs-admin": "Job {{ro_number}} - Admin | $t(titles.app)",
|
||||||
"jobs-all": "All Jobs | $t(titles.app)",
|
"jobs-all": "All Jobs | $t(titles.app)",
|
||||||
|
|||||||
@@ -352,6 +352,9 @@
|
|||||||
"employees": {
|
"employees": {
|
||||||
"page": ""
|
"page": ""
|
||||||
},
|
},
|
||||||
|
"inventory": {
|
||||||
|
"list": ""
|
||||||
|
},
|
||||||
"jobs": {
|
"jobs": {
|
||||||
"admin": "",
|
"admin": "",
|
||||||
"available-list": "",
|
"available-list": "",
|
||||||
@@ -1069,6 +1072,26 @@
|
|||||||
"printpack": ""
|
"printpack": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"inventory": {
|
||||||
|
"actions": {
|
||||||
|
"addtoinventory": "",
|
||||||
|
"consumefrominventory": ""
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"inserting": ""
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"consumedbyjob": "",
|
||||||
|
"frombillinvoicenumber": "",
|
||||||
|
"fromvendor": "",
|
||||||
|
"inventory": "",
|
||||||
|
"showall": "",
|
||||||
|
"showavailable": ""
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"inserted": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"joblines": {
|
"joblines": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"new": ""
|
"new": ""
|
||||||
@@ -1754,6 +1777,7 @@
|
|||||||
"export-logs": "",
|
"export-logs": "",
|
||||||
"help": "",
|
"help": "",
|
||||||
"home": "Casa",
|
"home": "Casa",
|
||||||
|
"inventory": "",
|
||||||
"jobs": "Trabajos",
|
"jobs": "Trabajos",
|
||||||
"newjob": "",
|
"newjob": "",
|
||||||
"owners": "propietarios",
|
"owners": "propietarios",
|
||||||
@@ -2403,11 +2427,21 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"asoftodaytarget": "",
|
"asoftodaytarget": "",
|
||||||
|
"calendarperiod": "",
|
||||||
"dailyactual": "",
|
"dailyactual": "",
|
||||||
"dailytarget": "",
|
"dailytarget": "",
|
||||||
|
"jobs": "",
|
||||||
|
"lastmonth": "",
|
||||||
|
"lastweek": "",
|
||||||
"monthlytarget": "",
|
"monthlytarget": "",
|
||||||
|
"productivestatistics": "",
|
||||||
|
"productivetimeticketsoverdate": "",
|
||||||
"targets": "",
|
"targets": "",
|
||||||
|
"thismonth": "",
|
||||||
|
"thisweek": "",
|
||||||
|
"timetickets": "",
|
||||||
"todateactual": "",
|
"todateactual": "",
|
||||||
|
"totaloverperiod": "",
|
||||||
"weeklyactual": "",
|
"weeklyactual": "",
|
||||||
"weeklytarget": "",
|
"weeklytarget": "",
|
||||||
"workingdays": ""
|
"workingdays": ""
|
||||||
@@ -2418,11 +2452,6 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scoredboard": {
|
|
||||||
"successes": {
|
|
||||||
"updated": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tech": {
|
"tech": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"employeeid": "",
|
"employeeid": "",
|
||||||
@@ -2523,6 +2552,7 @@
|
|||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"dms": "",
|
"dms": "",
|
||||||
"export-logs": "",
|
"export-logs": "",
|
||||||
|
"inventory": "",
|
||||||
"jobs": "",
|
"jobs": "",
|
||||||
"jobs-active": "",
|
"jobs-active": "",
|
||||||
"jobs-admin": "",
|
"jobs-admin": "",
|
||||||
@@ -2563,6 +2593,7 @@
|
|||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"dms": "",
|
"dms": "",
|
||||||
"export-logs": "",
|
"export-logs": "",
|
||||||
|
"inventory": "",
|
||||||
"jobs": "Todos los trabajos | $t(titles.app)",
|
"jobs": "Todos los trabajos | $t(titles.app)",
|
||||||
"jobs-admin": "",
|
"jobs-admin": "",
|
||||||
"jobs-all": "",
|
"jobs-all": "",
|
||||||
|
|||||||
@@ -352,6 +352,9 @@
|
|||||||
"employees": {
|
"employees": {
|
||||||
"page": ""
|
"page": ""
|
||||||
},
|
},
|
||||||
|
"inventory": {
|
||||||
|
"list": ""
|
||||||
|
},
|
||||||
"jobs": {
|
"jobs": {
|
||||||
"admin": "",
|
"admin": "",
|
||||||
"available-list": "",
|
"available-list": "",
|
||||||
@@ -1069,6 +1072,26 @@
|
|||||||
"printpack": ""
|
"printpack": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"inventory": {
|
||||||
|
"actions": {
|
||||||
|
"addtoinventory": "",
|
||||||
|
"consumefrominventory": ""
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"inserting": ""
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"consumedbyjob": "",
|
||||||
|
"frombillinvoicenumber": "",
|
||||||
|
"fromvendor": "",
|
||||||
|
"inventory": "",
|
||||||
|
"showall": "",
|
||||||
|
"showavailable": ""
|
||||||
|
},
|
||||||
|
"successes": {
|
||||||
|
"inserted": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
"joblines": {
|
"joblines": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"new": ""
|
"new": ""
|
||||||
@@ -1754,6 +1777,7 @@
|
|||||||
"export-logs": "",
|
"export-logs": "",
|
||||||
"help": "",
|
"help": "",
|
||||||
"home": "Accueil",
|
"home": "Accueil",
|
||||||
|
"inventory": "",
|
||||||
"jobs": "Emplois",
|
"jobs": "Emplois",
|
||||||
"newjob": "",
|
"newjob": "",
|
||||||
"owners": "Propriétaires",
|
"owners": "Propriétaires",
|
||||||
@@ -2403,11 +2427,21 @@
|
|||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
"asoftodaytarget": "",
|
"asoftodaytarget": "",
|
||||||
|
"calendarperiod": "",
|
||||||
"dailyactual": "",
|
"dailyactual": "",
|
||||||
"dailytarget": "",
|
"dailytarget": "",
|
||||||
|
"jobs": "",
|
||||||
|
"lastmonth": "",
|
||||||
|
"lastweek": "",
|
||||||
"monthlytarget": "",
|
"monthlytarget": "",
|
||||||
|
"productivestatistics": "",
|
||||||
|
"productivetimeticketsoverdate": "",
|
||||||
"targets": "",
|
"targets": "",
|
||||||
|
"thismonth": "",
|
||||||
|
"thisweek": "",
|
||||||
|
"timetickets": "",
|
||||||
"todateactual": "",
|
"todateactual": "",
|
||||||
|
"totaloverperiod": "",
|
||||||
"weeklyactual": "",
|
"weeklyactual": "",
|
||||||
"weeklytarget": "",
|
"weeklytarget": "",
|
||||||
"workingdays": ""
|
"workingdays": ""
|
||||||
@@ -2418,11 +2452,6 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scoredboard": {
|
|
||||||
"successes": {
|
|
||||||
"updated": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tech": {
|
"tech": {
|
||||||
"fields": {
|
"fields": {
|
||||||
"employeeid": "",
|
"employeeid": "",
|
||||||
@@ -2523,6 +2552,7 @@
|
|||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"dms": "",
|
"dms": "",
|
||||||
"export-logs": "",
|
"export-logs": "",
|
||||||
|
"inventory": "",
|
||||||
"jobs": "",
|
"jobs": "",
|
||||||
"jobs-active": "",
|
"jobs-active": "",
|
||||||
"jobs-admin": "",
|
"jobs-admin": "",
|
||||||
@@ -2563,6 +2593,7 @@
|
|||||||
"dashboard": "",
|
"dashboard": "",
|
||||||
"dms": "",
|
"dms": "",
|
||||||
"export-logs": "",
|
"export-logs": "",
|
||||||
|
"inventory": "",
|
||||||
"jobs": "Tous les emplois | $t(titles.app)",
|
"jobs": "Tous les emplois | $t(titles.app)",
|
||||||
"jobs-admin": "",
|
"jobs-admin": "",
|
||||||
"jobs-all": "",
|
"jobs-all": "",
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
- function:
|
- function:
|
||||||
schema: public
|
schema: public
|
||||||
name: search_exportlog
|
name: search_exportlog
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_inventory
|
||||||
- function:
|
- function:
|
||||||
schema: public
|
schema: public
|
||||||
name: search_jobs
|
name: search_jobs
|
||||||
|
|||||||
@@ -402,6 +402,14 @@
|
|||||||
- name: jobline
|
- name: jobline
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on: joblineid
|
foreign_key_constraint_on: joblineid
|
||||||
|
array_relationships:
|
||||||
|
- name: inventories
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: billlineid
|
||||||
|
table:
|
||||||
|
schema: public
|
||||||
|
name: inventory
|
||||||
insert_permissions:
|
insert_permissions:
|
||||||
- role: user
|
- role: user
|
||||||
permission:
|
permission:
|
||||||
@@ -541,6 +549,13 @@
|
|||||||
table:
|
table:
|
||||||
schema: public
|
schema: public
|
||||||
name: exportlog
|
name: exportlog
|
||||||
|
- name: inventories
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: consumedbybillid
|
||||||
|
table:
|
||||||
|
schema: public
|
||||||
|
name: inventory
|
||||||
- name: parts_orders
|
- name: parts_orders
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -751,6 +766,13 @@
|
|||||||
table:
|
table:
|
||||||
schema: public
|
schema: public
|
||||||
name: exportlog
|
name: exportlog
|
||||||
|
- name: inventories
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: shopid
|
||||||
|
table:
|
||||||
|
schema: public
|
||||||
|
name: inventory
|
||||||
- name: jobs
|
- name: jobs
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
@@ -2112,6 +2134,97 @@
|
|||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
allow_aggregations: true
|
allow_aggregations: true
|
||||||
|
- table:
|
||||||
|
schema: public
|
||||||
|
name: inventory
|
||||||
|
object_relationships:
|
||||||
|
- name: bill
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: consumedbybillid
|
||||||
|
- name: billline
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: billlineid
|
||||||
|
- name: bodyshop
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: shopid
|
||||||
|
- name: jobline
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on: joblineid
|
||||||
|
insert_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
check:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
columns:
|
||||||
|
- actual_cost
|
||||||
|
- actual_price
|
||||||
|
- billlineid
|
||||||
|
- consumedbybillid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- joblineid
|
||||||
|
- line_desc
|
||||||
|
- quantity
|
||||||
|
- shopid
|
||||||
|
- updated_at
|
||||||
|
backend_only: false
|
||||||
|
select_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- actual_cost
|
||||||
|
- actual_price
|
||||||
|
- billlineid
|
||||||
|
- consumedbybillid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- joblineid
|
||||||
|
- line_desc
|
||||||
|
- quantity
|
||||||
|
- shopid
|
||||||
|
- updated_at
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
allow_aggregations: true
|
||||||
|
update_permissions:
|
||||||
|
- role: user
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- actual_cost
|
||||||
|
- actual_price
|
||||||
|
- billlineid
|
||||||
|
- consumedbybillid
|
||||||
|
- created_at
|
||||||
|
- id
|
||||||
|
- joblineid
|
||||||
|
- line_desc
|
||||||
|
- quantity
|
||||||
|
- shopid
|
||||||
|
- updated_at
|
||||||
|
filter:
|
||||||
|
bodyshop:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
check: null
|
||||||
- table:
|
- table:
|
||||||
schema: public
|
schema: public
|
||||||
name: ioevents
|
name: ioevents
|
||||||
@@ -2211,6 +2324,13 @@
|
|||||||
table:
|
table:
|
||||||
schema: public
|
schema: public
|
||||||
name: billlines
|
name: billlines
|
||||||
|
- name: inventories
|
||||||
|
using:
|
||||||
|
foreign_key_constraint_on:
|
||||||
|
column: joblineid
|
||||||
|
table:
|
||||||
|
schema: public
|
||||||
|
name: inventory
|
||||||
- name: parts_order_lines
|
- name: parts_order_lines
|
||||||
using:
|
using:
|
||||||
foreign_key_constraint_on:
|
foreign_key_constraint_on:
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP TABLE "public"."inventory";
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE "public"."inventory" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "shopid" uuid NOT NULL, "billid" uuid, "joblineid" uuid, "line_desc" text NOT NULL, "actual_price" numeric NOT NULL, "actual_cost" numeric NOT NULL, "quantity" numeric NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("joblineid") REFERENCES "public"."joblines"("id") ON UPDATE restrict ON DELETE restrict);
|
||||||
|
CREATE OR REPLACE FUNCTION "public"."set_current_timestamp_updated_at"()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
_new record;
|
||||||
|
BEGIN
|
||||||
|
_new := NEW;
|
||||||
|
_new."updated_at" = NOW();
|
||||||
|
RETURN _new;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
CREATE TRIGGER "set_public_inventory_updated_at"
|
||||||
|
BEFORE UPDATE ON "public"."inventory"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE "public"."set_current_timestamp_updated_at"();
|
||||||
|
COMMENT ON TRIGGER "set_public_inventory_updated_at" ON "public"."inventory"
|
||||||
|
IS 'trigger to set value of column "updated_at" to current timestamp on row update';
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."inventory" drop constraint "inventory_billid_fkey";
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
alter table "public"."inventory"
|
||||||
|
add constraint "inventory_billid_fkey"
|
||||||
|
foreign key ("billid")
|
||||||
|
references "public"."billlines"
|
||||||
|
("id") on update restrict on delete restrict;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."inventory" drop constraint "inventory_shopid_fkey";
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
alter table "public"."inventory"
|
||||||
|
add constraint "inventory_shopid_fkey"
|
||||||
|
foreign key ("shopid")
|
||||||
|
references "public"."bodyshops"
|
||||||
|
("id") on update restrict on delete restrict;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."inventory" rename column "billlineid" to "billid";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."inventory" rename column "billid" to "billlineid";
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."inventory" add column "consumedbybillid" uuid
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."inventory" add column "consumedbybillid" uuid
|
||||||
|
null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."inventory" drop constraint "inventory_consumedbybillid_fkey";
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
alter table "public"."inventory"
|
||||||
|
add constraint "inventory_consumedbybillid_fkey"
|
||||||
|
foreign key ("consumedbybillid")
|
||||||
|
references "public"."bills"
|
||||||
|
("id") on update restrict on delete restrict;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
alter table "public"."inventory" drop constraint "inventory_consumedbybillid_fkey",
|
||||||
|
add constraint "inventory_consumedbybillid_fkey"
|
||||||
|
foreign key ("shopid")
|
||||||
|
references "public"."bodyshops"
|
||||||
|
("id") on update restrict on delete restrict;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
alter table "public"."inventory" drop constraint "inventory_consumedbybillid_fkey",
|
||||||
|
add constraint "inventory_consumedbybillid_fkey"
|
||||||
|
foreign key ("consumedbybillid")
|
||||||
|
references "public"."bills"
|
||||||
|
("id") on update restrict on delete set null;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
alter table "public"."inventory" drop constraint "inventory_consumedbybillid_fkey",
|
||||||
|
add constraint "inventory_consumedbybillid_fkey"
|
||||||
|
foreign key ("shopid")
|
||||||
|
references "public"."bodyshops"
|
||||||
|
("id") on update restrict on delete restrict;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
alter table "public"."inventory" drop constraint "inventory_consumedbybillid_fkey",
|
||||||
|
add constraint "inventory_consumedbybillid_fkey"
|
||||||
|
foreign key ("consumedbybillid")
|
||||||
|
references "public"."bills"
|
||||||
|
("id") on update cascade on delete set null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP INDEX IF EXISTS "public"."inventory_consumedbybillid";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
CREATE INDEX "inventory_consumedbybillid" on
|
||||||
|
"public"."inventory" using btree ("consumedbybillid");
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
DROP INDEX IF EXISTS "public"."inventory_shopididx";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
CREATE INDEX "inventory_shopididx" on
|
||||||
|
"public"."inventory" using btree ("shopid");
|
||||||
40
hasura/migrations/1654022042838_run_sql_migration/down.sql
Normal file
40
hasura/migrations/1654022042838_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- CREATE OR REPLACE FUNCTION public.search_inventory (search text)
|
||||||
|
-- RETURNS SETOF inventory
|
||||||
|
-- LANGUAGE plpgsql
|
||||||
|
-- STABLE
|
||||||
|
-- AS $function$
|
||||||
|
-- BEGIN
|
||||||
|
-- IF search = '' THEN
|
||||||
|
-- RETURN query
|
||||||
|
-- SELECT
|
||||||
|
-- *
|
||||||
|
-- FROM
|
||||||
|
-- inventory;
|
||||||
|
-- ELSE
|
||||||
|
-- RETURN query
|
||||||
|
-- SELECT
|
||||||
|
-- *
|
||||||
|
-- FROM
|
||||||
|
-- inventory i,
|
||||||
|
-- billlines bl,
|
||||||
|
-- bills b,
|
||||||
|
-- vendors v
|
||||||
|
-- WHERE
|
||||||
|
-- i.billlineid = bl.id
|
||||||
|
-- AND bl.billid = b.id
|
||||||
|
-- AND b.vendorid = v.id
|
||||||
|
-- AND i.line_desc ILIKE '%' || search || '%'
|
||||||
|
-- OR b.invoice_number ILIKE '%' || search || '%'
|
||||||
|
-- OR v.name ILIKE '%' || search || '%'
|
||||||
|
-- ORDER BY
|
||||||
|
-- i.line_desc ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL,
|
||||||
|
-- b.invoice_number ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL,
|
||||||
|
-- v.name ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL;
|
||||||
|
-- END IF;
|
||||||
|
-- END
|
||||||
|
-- $function$;
|
||||||
38
hasura/migrations/1654022042838_run_sql_migration/up.sql
Normal file
38
hasura/migrations/1654022042838_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
CREATE OR REPLACE FUNCTION public.search_inventory (search text)
|
||||||
|
RETURNS SETOF inventory
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
STABLE
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
IF search = '' THEN
|
||||||
|
RETURN query
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
inventory;
|
||||||
|
ELSE
|
||||||
|
RETURN query
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
inventory i,
|
||||||
|
billlines bl,
|
||||||
|
bills b,
|
||||||
|
vendors v
|
||||||
|
WHERE
|
||||||
|
i.billlineid = bl.id
|
||||||
|
AND bl.billid = b.id
|
||||||
|
AND b.vendorid = v.id
|
||||||
|
AND i.line_desc ILIKE '%' || search || '%'
|
||||||
|
OR b.invoice_number ILIKE '%' || search || '%'
|
||||||
|
OR v.name ILIKE '%' || search || '%'
|
||||||
|
ORDER BY
|
||||||
|
i.line_desc ILIKE '%' || search || '%'
|
||||||
|
OR NULL,
|
||||||
|
b.invoice_number ILIKE '%' || search || '%'
|
||||||
|
OR NULL,
|
||||||
|
v.name ILIKE '%' || search || '%'
|
||||||
|
OR NULL;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$function$;
|
||||||
37
hasura/migrations/1654023497348_run_sql_migration/down.sql
Normal file
37
hasura/migrations/1654023497348_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- CREATE OR REPLACE FUNCTION public.search_inventory (search text)
|
||||||
|
-- RETURNS SETOF inventory
|
||||||
|
-- LANGUAGE plpgsql
|
||||||
|
-- STABLE
|
||||||
|
-- AS $function$
|
||||||
|
-- BEGIN
|
||||||
|
-- IF search = '' THEN
|
||||||
|
-- RETURN query
|
||||||
|
-- SELECT
|
||||||
|
-- *
|
||||||
|
-- FROM
|
||||||
|
-- inventory;
|
||||||
|
-- ELSE
|
||||||
|
-- RETURN query
|
||||||
|
-- SELECT
|
||||||
|
-- *
|
||||||
|
-- FROM
|
||||||
|
-- inventory inner JOIN billlines ON inventory.billlineid = billlines.id
|
||||||
|
-- inner JOIN bills ON billlines.billid = bills.id
|
||||||
|
-- inner JOIN vendors ON bills.vendorid = vendors.id
|
||||||
|
--
|
||||||
|
-- WHERE
|
||||||
|
-- inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
-- OR bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
-- OR vendors.name ILIKE '%' || search || '%'
|
||||||
|
-- ORDER BY
|
||||||
|
-- inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL,
|
||||||
|
-- bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL,
|
||||||
|
-- vendors.name ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL;
|
||||||
|
-- END IF;
|
||||||
|
-- END
|
||||||
|
-- $function$;
|
||||||
35
hasura/migrations/1654023497348_run_sql_migration/up.sql
Normal file
35
hasura/migrations/1654023497348_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
CREATE OR REPLACE FUNCTION public.search_inventory (search text)
|
||||||
|
RETURNS SETOF inventory
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
STABLE
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
IF search = '' THEN
|
||||||
|
RETURN query
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
inventory;
|
||||||
|
ELSE
|
||||||
|
RETURN query
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
inventory inner JOIN billlines ON inventory.billlineid = billlines.id
|
||||||
|
inner JOIN bills ON billlines.billid = bills.id
|
||||||
|
inner JOIN vendors ON bills.vendorid = vendors.id
|
||||||
|
|
||||||
|
WHERE
|
||||||
|
inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
OR bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
OR vendors.name ILIKE '%' || search || '%'
|
||||||
|
ORDER BY
|
||||||
|
inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
OR NULL,
|
||||||
|
bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
OR NULL,
|
||||||
|
vendors.name ILIKE '%' || search || '%'
|
||||||
|
OR NULL;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$function$;
|
||||||
37
hasura/migrations/1654023506265_run_sql_migration/down.sql
Normal file
37
hasura/migrations/1654023506265_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- CREATE OR REPLACE FUNCTION public.search_inventory (search text)
|
||||||
|
-- RETURNS SETOF inventory
|
||||||
|
-- LANGUAGE plpgsql
|
||||||
|
-- STABLE
|
||||||
|
-- AS $function$
|
||||||
|
-- BEGIN
|
||||||
|
-- IF search = '' THEN
|
||||||
|
-- RETURN query
|
||||||
|
-- SELECT
|
||||||
|
-- *
|
||||||
|
-- FROM
|
||||||
|
-- inventory;
|
||||||
|
-- ELSE
|
||||||
|
-- RETURN query
|
||||||
|
-- SELECT
|
||||||
|
-- *
|
||||||
|
-- FROM
|
||||||
|
-- inventory inner JOIN billlines ON inventory.billlineid = billlines.id
|
||||||
|
-- inner JOIN bills ON billlines.billid = bills.id
|
||||||
|
-- inner JOIN vendors ON bills.vendorid = vendors.id
|
||||||
|
--
|
||||||
|
-- WHERE
|
||||||
|
-- inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
-- OR bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
-- OR vendors.name ILIKE '%' || search || '%'
|
||||||
|
-- ORDER BY
|
||||||
|
-- inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL,
|
||||||
|
-- bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL,
|
||||||
|
-- vendors.name ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL;
|
||||||
|
-- END IF;
|
||||||
|
-- END
|
||||||
|
-- $function$;
|
||||||
35
hasura/migrations/1654023506265_run_sql_migration/up.sql
Normal file
35
hasura/migrations/1654023506265_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
CREATE OR REPLACE FUNCTION public.search_inventory (search text)
|
||||||
|
RETURNS SETOF inventory
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
STABLE
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
IF search = '' THEN
|
||||||
|
RETURN query
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
inventory;
|
||||||
|
ELSE
|
||||||
|
RETURN query
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
inventory inner JOIN billlines ON inventory.billlineid = billlines.id
|
||||||
|
inner JOIN bills ON billlines.billid = bills.id
|
||||||
|
inner JOIN vendors ON bills.vendorid = vendors.id
|
||||||
|
|
||||||
|
WHERE
|
||||||
|
inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
OR bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
OR vendors.name ILIKE '%' || search || '%'
|
||||||
|
ORDER BY
|
||||||
|
inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
OR NULL,
|
||||||
|
bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
OR NULL,
|
||||||
|
vendors.name ILIKE '%' || search || '%'
|
||||||
|
OR NULL;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$function$;
|
||||||
37
hasura/migrations/1654023641223_run_sql_migration/down.sql
Normal file
37
hasura/migrations/1654023641223_run_sql_migration/down.sql
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- CREATE OR REPLACE FUNCTION public.search_inventory (search text)
|
||||||
|
-- RETURNS SETOF inventory
|
||||||
|
-- LANGUAGE plpgsql
|
||||||
|
-- STABLE
|
||||||
|
-- AS $function$
|
||||||
|
-- BEGIN
|
||||||
|
-- IF search = '' THEN
|
||||||
|
-- RETURN query
|
||||||
|
-- SELECT
|
||||||
|
-- *
|
||||||
|
-- FROM
|
||||||
|
-- inventory;
|
||||||
|
-- ELSE
|
||||||
|
-- RETURN query
|
||||||
|
-- SELECT
|
||||||
|
-- inventory.*
|
||||||
|
-- FROM
|
||||||
|
-- inventory inner JOIN billlines ON inventory.billlineid = billlines.id
|
||||||
|
-- inner JOIN bills ON billlines.billid = bills.id
|
||||||
|
-- inner JOIN vendors ON bills.vendorid = vendors.id
|
||||||
|
--
|
||||||
|
-- WHERE
|
||||||
|
-- inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
-- OR bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
-- OR vendors.name ILIKE '%' || search || '%'
|
||||||
|
-- ORDER BY
|
||||||
|
-- inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL,
|
||||||
|
-- bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL,
|
||||||
|
-- vendors.name ILIKE '%' || search || '%'
|
||||||
|
-- OR NULL;
|
||||||
|
-- END IF;
|
||||||
|
-- END
|
||||||
|
-- $function$;
|
||||||
35
hasura/migrations/1654023641223_run_sql_migration/up.sql
Normal file
35
hasura/migrations/1654023641223_run_sql_migration/up.sql
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
CREATE OR REPLACE FUNCTION public.search_inventory (search text)
|
||||||
|
RETURNS SETOF inventory
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
STABLE
|
||||||
|
AS $function$
|
||||||
|
BEGIN
|
||||||
|
IF search = '' THEN
|
||||||
|
RETURN query
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
inventory;
|
||||||
|
ELSE
|
||||||
|
RETURN query
|
||||||
|
SELECT
|
||||||
|
inventory.*
|
||||||
|
FROM
|
||||||
|
inventory inner JOIN billlines ON inventory.billlineid = billlines.id
|
||||||
|
inner JOIN bills ON billlines.billid = bills.id
|
||||||
|
inner JOIN vendors ON bills.vendorid = vendors.id
|
||||||
|
|
||||||
|
WHERE
|
||||||
|
inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
OR bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
OR vendors.name ILIKE '%' || search || '%'
|
||||||
|
ORDER BY
|
||||||
|
inventory.line_desc ILIKE '%' || search || '%'
|
||||||
|
OR NULL,
|
||||||
|
bills.invoice_number ILIKE '%' || search || '%'
|
||||||
|
OR NULL,
|
||||||
|
vendors.name ILIKE '%' || search || '%'
|
||||||
|
OR NULL;
|
||||||
|
END IF;
|
||||||
|
END
|
||||||
|
$function$;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- create index inventory_linedescidx on inventory(line_desc);
|
||||||
1
hasura/migrations/1654023762220_run_sql_migration/up.sql
Normal file
1
hasura/migrations/1654023762220_run_sql_migration/up.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
create index inventory_linedescidx on inventory(line_desc);
|
||||||
@@ -211,7 +211,7 @@ async function QueryInsuranceCo(oauthClient, qbo_realmId, req, job) {
|
|||||||
qbo_realmId,
|
qbo_realmId,
|
||||||
"query",
|
"query",
|
||||||
`select * From Customer where DisplayName = '${StandardizeName(
|
`select * From Customer where DisplayName = '${StandardizeName(
|
||||||
job.ins_co_nm
|
job.ins_co_nm.trim()
|
||||||
)}'`
|
)}'`
|
||||||
),
|
),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -239,7 +239,7 @@ async function InsertInsuranceCo(oauthClient, qbo_realmId, req, job, bodyshop) {
|
|||||||
const insCo = bodyshop.md_ins_cos.find((i) => i.name === job.ins_co_nm);
|
const insCo = bodyshop.md_ins_cos.find((i) => i.name === job.ins_co_nm);
|
||||||
|
|
||||||
const Customer = {
|
const Customer = {
|
||||||
DisplayName: job.ins_co_nm,
|
DisplayName: job.ins_co_nm.trim(),
|
||||||
BillWithParent: true,
|
BillWithParent: true,
|
||||||
BillAddr: {
|
BillAddr: {
|
||||||
City: job.ownr_city,
|
City: job.ownr_city,
|
||||||
@@ -269,6 +269,7 @@ async function InsertInsuranceCo(oauthClient, qbo_realmId, req, job, bodyshop) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.InsertInsuranceCo = InsertInsuranceCo;
|
exports.InsertInsuranceCo = InsertInsuranceCo;
|
||||||
|
|
||||||
async function QueryOwner(oauthClient, qbo_realmId, req, job) {
|
async function QueryOwner(oauthClient, qbo_realmId, req, job) {
|
||||||
const ownerName = generateOwnerTier(job, true, null);
|
const ownerName = generateOwnerTier(job, true, null);
|
||||||
const result = await oauthClient.makeApiCall({
|
const result = await oauthClient.makeApiCall({
|
||||||
|
|||||||
@@ -249,13 +249,13 @@ const generateInvoiceQbxml = (
|
|||||||
)}:${generateJobTier(jobs_by_pk)}`
|
)}:${generateJobTier(jobs_by_pk)}`
|
||||||
).trim(),
|
).trim(),
|
||||||
},
|
},
|
||||||
ARAccountRef: {
|
|
||||||
FullName: bodyshop.md_responsibility_centers.ar.accountname,
|
|
||||||
},
|
|
||||||
...(jobs_by_pk.class
|
...(jobs_by_pk.class
|
||||||
? { ClassRef: { FullName: jobs_by_pk.class } }
|
? { ClassRef: { FullName: jobs_by_pk.class } }
|
||||||
: {}),
|
: {}),
|
||||||
|
|
||||||
|
ARAccountRef: {
|
||||||
|
FullName: bodyshop.md_responsibility_centers.ar.accountname,
|
||||||
|
},
|
||||||
TxnDate: moment(jobs_by_pk.date_invoiced)
|
TxnDate: moment(jobs_by_pk.date_invoiced)
|
||||||
.tz(bodyshop.timezone)
|
.tz(bodyshop.timezone)
|
||||||
.format("YYYY-MM-DD"),
|
.format("YYYY-MM-DD"),
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ exports.addQbxmlHeader = addQbxmlHeader = (xml) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.generateSourceTier = (jobs_by_pk) => {
|
exports.generateSourceTier = (jobs_by_pk) => {
|
||||||
return jobs_by_pk.ins_co_nm && jobs_by_pk.ins_co_nm.trim();
|
return jobs_by_pk.ins_co_nm && jobs_by_pk.ins_co_nm.trim().replace(":", " ");
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.generateJobTier = (jobs_by_pk) => {
|
exports.generateJobTier = (jobs_by_pk) => {
|
||||||
return jobs_by_pk.ro_number && jobs_by_pk.ro_number.trim();
|
return jobs_by_pk.ro_number && jobs_by_pk.ro_number.trim().replace(":", " ");
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.generateOwnerTier = (jobs_by_pk, isThreeTier, twotierpref) => {
|
exports.generateOwnerTier = (jobs_by_pk, isThreeTier, twotierpref) => {
|
||||||
@@ -24,7 +24,9 @@ exports.generateOwnerTier = (jobs_by_pk, isThreeTier, twotierpref) => {
|
|||||||
: `${`${jobs_by_pk.ownr_ln || ""} ${
|
: `${`${jobs_by_pk.ownr_ln || ""} ${
|
||||||
jobs_by_pk.ownr_fn || ""
|
jobs_by_pk.ownr_fn || ""
|
||||||
}`.substring(0, 30)} #${jobs_by_pk.owner.accountingid || ""}`
|
}`.substring(0, 30)} #${jobs_by_pk.owner.accountingid || ""}`
|
||||||
).trim();
|
)
|
||||||
|
.trim()
|
||||||
|
.replace(":", " ");
|
||||||
} else {
|
} else {
|
||||||
//What's the 2 tier pref?
|
//What's the 2 tier pref?
|
||||||
if (twotierpref === "source") {
|
if (twotierpref === "source") {
|
||||||
@@ -40,7 +42,9 @@ exports.generateOwnerTier = (jobs_by_pk, isThreeTier, twotierpref) => {
|
|||||||
: `${`${jobs_by_pk.ownr_ln || ""} ${
|
: `${`${jobs_by_pk.ownr_ln || ""} ${
|
||||||
jobs_by_pk.ownr_fn || ""
|
jobs_by_pk.ownr_fn || ""
|
||||||
}`.substring(0, 30)} #${jobs_by_pk.owner.accountingid || ""}`
|
}`.substring(0, 30)} #${jobs_by_pk.owner.accountingid || ""}`
|
||||||
).trim();
|
)
|
||||||
|
.trim()
|
||||||
|
.replace(":", " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -523,6 +523,7 @@ function CalculateAdditional(job) {
|
|||||||
additionalCostItems: [],
|
additionalCostItems: [],
|
||||||
adjustments: null,
|
adjustments: null,
|
||||||
towing: null,
|
towing: null,
|
||||||
|
shipping: Dinero(),
|
||||||
storage: null,
|
storage: null,
|
||||||
pvrt: null,
|
pvrt: null,
|
||||||
total: null,
|
total: null,
|
||||||
@@ -530,6 +531,7 @@ function CalculateAdditional(job) {
|
|||||||
ret.towing = Dinero({
|
ret.towing = Dinero({
|
||||||
amount: Math.round((job.towing_payable || 0) * 100),
|
amount: Math.round((job.towing_payable || 0) * 100),
|
||||||
});
|
});
|
||||||
|
|
||||||
ret.additionalCosts = job.joblines
|
ret.additionalCosts = job.joblines
|
||||||
.filter((jl) => !jl.removed && IsAdditionalCost(jl))
|
.filter((jl) => !jl.removed && IsAdditionalCost(jl))
|
||||||
.reduce((acc, val) => {
|
.reduce((acc, val) => {
|
||||||
@@ -537,6 +539,11 @@ function CalculateAdditional(job) {
|
|||||||
amount: Math.round((val.act_price || 0) * 100),
|
amount: Math.round((val.act_price || 0) * 100),
|
||||||
}).multiply(val.part_qty || 1);
|
}).multiply(val.part_qty || 1);
|
||||||
|
|
||||||
|
if (val.db_ref === "936004") {
|
||||||
|
//Shipping line IO-1921.
|
||||||
|
ret.shipping = ret.shipping.add(lineValue);
|
||||||
|
}
|
||||||
|
|
||||||
if (val.line_desc.toLowerCase().includes("towing")) {
|
if (val.line_desc.toLowerCase().includes("towing")) {
|
||||||
ret.towing = lineValue;
|
ret.towing = lineValue;
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
Reference in New Issue
Block a user