Merged in release/2022-06-10 (pull request #513)

release/2022-06-10

Approved-by: Patrick Fic
This commit is contained in:
Patrick Fic
2022-06-10 22:38:15 +00:00
113 changed files with 4174 additions and 512 deletions

View File

@@ -2181,6 +2181,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>existinginventoryline</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>exporting</name>
<definition_loaded>false</definition_loaded>
@@ -3293,6 +3314,27 @@
<folder_node>
<name>validation</name>
<children>
<concept_node>
<name>inventoryquantity</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>manualinhouse</name>
<definition_loaded>false</definition_loaded>
@@ -5691,6 +5733,53 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>inventory</name>
<children>
<concept_node>
<name>delete</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>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>
<name>jobs</name>
<children>
@@ -15883,6 +15972,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>markedexported</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>message</name>
<definition_loaded>false</definition_loaded>
@@ -17213,6 +17323,435 @@
</folder_node>
</children>
</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>addtoro</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>
<concept_node>
<name>edit</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>new</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>fields</name>
<children>
<concept_node>
<name>comment</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>manualinvoicenumber</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>manualvendor</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>deleteconfirm</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>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>deleted</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>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>
<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>
<name>joblines</name>
<children>
@@ -29934,6 +30473,27 @@
</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>jobs</name>
<definition_loaded>false</definition_loaded>
@@ -40529,6 +41089,27 @@
</translation>
</translations>
</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>
<name>dailyactual</name>
<definition_loaded>false</definition_loaded>
@@ -40571,6 +41152,69 @@
</translation>
</translations>
</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>
<name>monthlytarget</name>
<definition_loaded>false</definition_loaded>
@@ -40592,6 +41236,48 @@
</translation>
</translations>
</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>
<name>targets</name>
<definition_loaded>false</definition_loaded>
@@ -40613,6 +41299,69 @@
</translation>
</translations>
</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>
<name>todateactual</name>
<definition_loaded>false</definition_loaded>
@@ -40634,6 +41383,27 @@
</translation>
</translations>
</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>
<name>weeklyactual</name>
<definition_loaded>false</definition_loaded>
@@ -40769,37 +41539,6 @@
</folder_node>
</children>
</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>
<name>tech</name>
<children>
@@ -42383,6 +43122,27 @@
</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>jobs</name>
<definition_loaded>false</definition_loaded>
@@ -43204,6 +43964,27 @@
</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>jobs</name>
<definition_loaded>false</definition_loaded>

View File

@@ -4,17 +4,17 @@
"private": true,
"proxy": "http://localhost:4000",
"dependencies": {
"@apollo/client": "^3.6.2",
"@apollo/client": "^3.6.6",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^6.4.3",
"@fingerprintjs/fingerprintjs": "^3.3.3",
"@sentry/react": "^6.19.7",
"@sentry/tracing": "^6.19.7",
"@sentry/react": "^7.1.1",
"@sentry/tracing": "^7.1.1",
"@splitsoftware/splitio-react": "^1.4.1",
"@stripe/react-stripe-js": "^1.8.0",
"@stripe/stripe-js": "^1.29.0",
"@tanem/react-nprogress": "^5.0.0",
"antd": "^4.20.5",
"@stripe/react-stripe-js": "^1.8.1",
"@stripe/stripe-js": "^1.31.0",
"@tanem/react-nprogress": "^5.0.1",
"antd": "^4.21.0",
"apollo-link-logger": "^2.0.0",
"axios": "^0.27.2",
"craco-less": "^1.20.0",
@@ -23,19 +23,19 @@
"enquire-js": "^0.2.1",
"env-cmd": "^10.1.0",
"exifr": "^7.1.3",
"firebase": "^9.8.1",
"firebase": "^9.8.2",
"graphql": "^16.5.0",
"i18next": "^21.8.2",
"i18next": "^21.8.9",
"i18next-browser-languagedetector": "^6.1.4",
"jsoneditor": "^9.7.4",
"jsoneditor": "^9.8.0",
"jsreport-browser-client-dist": "^1.3.0",
"libphonenumber-js": "^1.9.53",
"libphonenumber-js": "^1.10.6",
"logrocket": "^3.0.0",
"markerjs2": "^2.21.4",
"moment-business-days": "^1.2.0",
"moment-timezone": "^0.5.34",
"normalize-url": "^7.0.3",
"phone": "^3.1.17",
"phone": "^3.1.20",
"preval.macro": "^5.0.0",
"prop-types": "^15.8.1",
"query-string": "^7.1.1",
@@ -46,11 +46,11 @@
"react-color": "^2.19.3",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.2",
"react-drag-listview": "^0.2.0",
"react-drag-listview": "^0.2.1",
"react-grid-gallery": "^0.5.5",
"react-grid-layout": "^1.3.4",
"react-i18next": "^11.16.9",
"react-icons": "^4.3.1",
"react-i18next": "^11.17.0",
"react-icons": "^4.4.0",
"react-number-format": "^4.9.3",
"react-redux": "^7.2.8",
"react-resizable": "^3.0.4",
@@ -59,14 +59,14 @@
"react-sticky": "^6.0.3",
"react-sublime-video": "^0.2.5",
"react-virtualized": "^9.22.3",
"recharts": "^2.1.9",
"recharts": "^2.1.10",
"redux": "^4.2.0",
"redux-persist": "^6.0.0",
"redux-saga": "^1.1.3",
"redux-state-sync": "^3.1.2",
"reselect": "^4.1.5",
"reselect": "^4.1.6",
"sass": "^1.51.0",
"socket.io-client": "^4.5.0",
"socket.io-client": "^4.5.1",
"styled-components": "^5.3.5",
"subscriptions-transport-ws": "^0.11.0",
"web-vitals": "^2.1.4",

View File

@@ -15,7 +15,8 @@ export default function BillDeleteButton({ bill }) {
setLoading(true);
const result = await deleteBill({
variables: { billId: bill.id },
update(cache) {
update(cache, { errors }) {
if (errors) return;
cache.modify({
fields: {
bills(existingBills, { readField }) {
@@ -36,11 +37,22 @@ export default function BillDeleteButton({ bill }) {
if (!!!result.errors) {
notification["success"]({ message: t("bills.successes.deleted") });
} else {
notification["error"]({
message: t("bills.errors.deleting", {
error: JSON.stringify(result.errors),
}),
});
//Check if it's an fkey violation.
const error = JSON.stringify(result.errors);
if (error.toLowerCase().includes("inventory_billid_fkey")) {
notification["error"]({
message: t("bills.errors.deleting", {
error: t("bills.errors.existinginventoryline"),
}),
});
} else {
notification["error"]({
message: t("bills.errors.deleting", {
error: JSON.stringify(result.errors),
}),
});
}
}
setLoading(false);

View File

@@ -73,8 +73,8 @@ export function BillDetailEditcontainer({
sm: "100%",
md: "100%",
lg: "100%",
xl: "80%",
xxl: "80%",
xl: "90%",
xxl: "90%",
};
const drawerPercentage = selectedBreakpoint
? bpoints[selectedBreakpoint[0]]
@@ -127,7 +127,7 @@ export function BillDetailEditcontainer({
});
billlines.forEach((billline) => {
const { deductedfromlbr, jobline, ...il } = billline;
const { deductedfromlbr, inventories, jobline, ...il } = billline;
delete il.__typename;
if (il.id) {

View File

@@ -12,6 +12,7 @@ import {
UPDATE_JOB,
} from "../../graphql/jobs.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 { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
@@ -50,6 +51,7 @@ function BillEnterModalContainer({
const [insertBill] = useMutation(INSERT_NEW_BILL);
const [updateJobLines] = useMutation(UPDATE_JOB_LINE);
const [updatePartsOrderLines] = useMutation(MUTATION_MARK_RETURN_RECEIVED);
const [updateInventoryLines] = useMutation(UPDATE_INVENTORY_LINES);
const [loading, setLoading] = useState(false);
const client = useApolloClient();
@@ -79,8 +81,13 @@ function BillEnterModalContainer({
}
setLoading(true);
const { upload, location, outstanding_returns, ...remainingValues } =
values;
const {
upload,
location,
outstanding_returns,
inventory,
...remainingValues
} = values;
let adjustmentsToInsert = {};
@@ -190,6 +197,26 @@ function BillEnterModalContainer({
}
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(
remainingValues.billlines

View File

@@ -48,6 +48,7 @@ export function BillFormComponent({
disableInvNumber,
job,
loadOutstandingReturns,
loadInventory,
}) {
const { t } = useTranslation();
const client = useApolloClient();
@@ -61,6 +62,7 @@ export function BillFormComponent({
setDiscount(opt.discount);
opt &&
!billEdit &&
loadOutstandingReturns({
variables: {
jobId: form.getFieldValue("jobid"),
@@ -86,7 +88,7 @@ export function BillFormComponent({
const jobId = form.getFieldValue("jobid");
if (jobId) {
loadLines({ variables: { id: jobId } });
if (form.getFieldValue("is_credit_memo") && vendorId) {
if (form.getFieldValue("is_credit_memo") && vendorId && !billEdit) {
loadOutstandingReturns({
variables: {
jobId: jobId,
@@ -95,12 +97,19 @@ export function BillFormComponent({
});
}
}
if (vendorId === bodyshop.inhousevendorid && !billEdit) {
loadInventory();
}
}, [
form,
billEdit,
loadOutstandingReturns,
loadInventory,
setDiscount,
vendorAutoCompleteOptions,
loadLines,
bodyshop.inhousevendorid,
]);
return (
@@ -425,6 +434,7 @@ export function BillFormComponent({
form={form}
responsibilityCenters={responsibilityCenters}
disabled={disabled}
billEdit={billEdit}
/>
)}

View File

@@ -8,6 +8,9 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import BillFormComponent from "./bill-form.component";
import BillCmdReturnsTableComponent from "../bill-cm-returns-table/bill-cm-returns-table.component";
import { QUERY_UNRECEIVED_LINES } from "../../graphql/parts-orders.queries";
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({
bodyshop: selectBodyshop,
@@ -20,6 +23,12 @@ export function BillFormContainer({
disabled,
disableInvNumber,
}) {
const { Simple_Inventory } = useTreatments(
["Simple_Inventory"],
{},
bodyshop && bodyshop.imexshopid
);
const { data: VendorAutoCompleteData } = useQuery(
SEARCH_VENDOR_AUTOCOMPLETE,
{ fetchPolicy: "network-only", nextFetchPolicy: "network-only" }
@@ -31,6 +40,8 @@ export function BillFormContainer({
const [loadOutstandingReturns, { loading: returnLoading, data: returnData }] =
useLazyQuery(QUERY_UNRECEIVED_LINES);
const [loadInventory, { loading: inventoryLoading, data: inventoryData }] =
useLazyQuery(QUERY_OUTSTANDING_INVENTORY);
return (
<>
@@ -47,6 +58,7 @@ export function BillFormContainer({
responsibilityCenters={bodyshop.md_responsibility_centers || null}
disableInvNumber={disableInvNumber}
loadOutstandingReturns={loadOutstandingReturns}
loadInventory={loadInventory}
/>
{!billEdit && (
<BillCmdReturnsTableComponent
@@ -56,6 +68,14 @@ export function BillFormContainer({
returnData={returnData}
/>
)}
{Simple_Inventory.treatment === "on" && (
<BillInventoryTable
form={form}
inventoryLoading={inventoryLoading}
inventoryData={billEdit ? [] : inventoryData}
billEdit={billEdit}
/>
)}
</>
);
}

View File

@@ -8,7 +8,7 @@ import {
Space,
Switch,
Table,
Tooltip
Tooltip,
} from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
@@ -18,6 +18,8 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import CiecaSelect from "../../utils/Ciecaselect";
import BillLineSearchSelect from "../bill-line-search-select/bill-line-search-select.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({
//currentUser: selectCurrentUser
@@ -34,10 +36,16 @@ export function BillEnterModalLinesComponent({
discount,
form,
responsibilityCenters,
billEdit,
billid,
}) {
const { t } = useTranslation();
const { setFieldsValue, getFieldsValue, getFieldValue } = form;
const { Simple_Inventory } = useTreatments(
["Simple_Inventory"],
{},
bodyshop && bodyshop.imexshopid
);
const columns = (remove) => {
return [
{
@@ -142,6 +150,24 @@ export function BillEnterModalLinesComponent({
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
if (
value &&
getFieldValue("billlines")[field.fieldKey]?.inventories
?.length > value
) {
return Promise.reject(
t("bills.validation.inventoryquantity", {
number:
getFieldValue("billlines")[field.fieldKey]
?.inventories?.length,
})
);
}
return Promise.resolve();
},
}),
],
};
},
@@ -477,9 +503,33 @@ export function BillEnterModalLinesComponent({
dataIndex: "actions",
render: (text, record) => (
<Button disabled={disabled} onClick={() => remove(record.name)}>
<DeleteFilled />
</Button>
<Form.Item shouldUpdate noStyle>
{() => (
<Space wrap>
<Button
disabled={
disabled ||
getFieldValue("billlines")[record.fieldKey]?.inventories
?.length > 0
}
onClick={() => remove(record.name)}
>
<DeleteFilled />
</Button>
{Simple_Inventory.treatment === "on" && (
<BilllineAddInventory
disabled={
!billEdit ||
form.isFieldsTouched() ||
form.getFieldValue("is_credit_memo")
}
billline={getFieldValue("billlines")[record.fieldKey]}
jobid={getFieldValue("jobid")}
/>
)}
</Space>
)}
</Form.Item>
),
},
];

View File

@@ -0,0 +1,173 @@
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";
import { selectBillEnterModal } from "../../redux/modals/modals.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
billEnterModal: selectBillEnterModal,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(mapStateToProps, mapDispatchToProps)(BillInventoryTable);
export function BillInventoryTable({
billEnterModal,
bodyshop,
form,
billEdit,
inventoryLoading,
inventoryData,
}) {
const { t } = useTranslation();
useEffect(() => {
if (inventoryData && inventoryData.inventory) {
form.setFieldsValue({
inventory: billEnterModal.context.consumeinventoryid
? inventoryData.inventory.map((i) => {
if (i.id === billEnterModal.context.consumeinventoryid)
i.consumefrominventory = true;
return i;
})
: inventoryData.inventory,
});
}
}, [inventoryData, form, billEnterModal.context.consumeinventoryid]);
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.fields.comment")}</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}comment`}
name={[field.name, "comment"]}
>
<ReadOnlyFormItemComponent />
</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>
);
}

View File

@@ -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;
}
}

View File

@@ -9,11 +9,14 @@ import { createStructuredSelector } from "reselect";
import {
selectAuthLevel,
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
authLevel: selectAuthLevel,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -24,9 +27,15 @@ export default connect(
mapDispatchToProps
)(BillMarkExportedButton);
export function BillMarkExportedButton({ bodyshop, authLevel, bill }) {
export function BillMarkExportedButton({
currentUser,
bodyshop,
authLevel,
bill,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [updateBill] = useMutation(gql`
mutation UPDATE_BILL($billId: uuid!) {
@@ -46,6 +55,20 @@ export function BillMarkExportedButton({ bodyshop, authLevel, bill }) {
variables: { billId: bill.id },
});
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
billid: bill.id,
successful: true,
message: JSON.stringify([t("general.labels.markedexported")]),
useremail: currentUser.email,
},
],
},
});
if (!result.errors) {
notification["success"]({
message: t("bills.successes.markexported"),
@@ -69,11 +92,7 @@ export function BillMarkExportedButton({ bodyshop, authLevel, bill }) {
if (hasAccess)
return (
<Button
loading={loading}
disabled={bill.exported}
onClick={handleUpdate}
>
<Button loading={loading} disabled={bill.exported} onClick={handleUpdate}>
{t("bills.labels.markexported")}
</Button>
);

View File

@@ -0,0 +1,155 @@
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: billline.applicable_taxes.local,
state: billline.applicable_taxes.state,
federal: billline.applicable_taxes.federal,
},
},
],
};
cm.total = CalculateBillTotal(cm).enteredTotal.getAmount() / 100;
const insertResult = await insertInventoryLine({
variables: {
joblineId:
billline.joblineid === "noline" ? billline.id : billline.joblineid, //This will return null as there will be no jobline that has the id of the bill line.
//Unfortunately, we can't send null as the GQL syntax validation fails.
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 === "noline" ? null : billline.joblineid,
part_type: billline.jobline && 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 >= billline.quantity
}
onClick={addToInventory}
>
<FileAddFilled />
{billline?.inventories?.length > 0 && (
<div>({billline?.inventories?.length} in inv)</div>
)}
</Button>
</Tooltip>
);
}

View File

@@ -1,12 +1,12 @@
import { SyncOutlined } from "@ant-design/icons";
import { Button, Card, Input, Space, Table } from "antd";
import { SyncOutlined, WarningFilled } from "@ant-design/icons";
import { Button, Card, Input, Space, Table, Tooltip } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
import moment from "moment";
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
const [state, setState] = useState({
sortedInfo: {},
@@ -56,7 +56,25 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
onFilter: (value, record) => value.includes(record.status),
sortOrder:
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"),

View File

@@ -139,7 +139,9 @@ export function EmailOverlayComponent({
</Form.Item>
<Divider>{t("emails.labels.preview")}</Divider>
<strong>{t("emails.labels.pdfcopywillbeattached")}</strong>
{bodyshop.attach_pdf_to_email && (
<strong>{t("emails.labels.pdfcopywillbeattached")}</strong>
)}
<Form.Item shouldUpdate>
{() => {

View File

@@ -1,3 +1,4 @@
import { useTreatments } from "@splitsoftware/splitio-react";
import Icon, {
BankFilled,
BarChartOutlined,
@@ -83,6 +84,12 @@ function Header({
setReportCenterContext,
recentItems,
}) {
const { Simple_Inventory } = useTreatments(
["Simple_Inventory"],
{},
bodyshop && bodyshop.imexshopid
);
const { t } = useTranslation();
return (
@@ -199,7 +206,20 @@ function Header({
>
{t("menus.header.enterbills")}
</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 />}>
<Link to="/manage/payments">{t("menus.header.allpayments")}</Link>
</Menu.Item>
@@ -216,7 +236,6 @@ function Header({
{t("menus.header.enterpayment")}
</Menu.Item>
<Menu.Divider key="div5" />
<Menu.Item key="timetickets" icon={<FieldTimeOutlined />}>
<Link to="/manage/timetickets">
{t("menus.header.timetickets")}
@@ -235,7 +254,6 @@ function Header({
{t("menus.header.entertimeticket")}
</Menu.Item>
<Menu.Divider key="div6" />
<Menu.SubMenu
key="accountingexport"
title={t("menus.header.export")}

View File

@@ -0,0 +1,65 @@
import { Button } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import moment from "moment";
import { useTranslation } from "react-i18next";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setBillEnterContext: (context) =>
dispatch(setModalContext({ context: context, modal: "billEnter" })),
});
export default connect(mapStateToProps, mapDispatchToProps)(InventoryBillRo);
export function InventoryBillRo({
bodyshop,
setBillEnterContext,
inventoryline,
}) {
const { t } = useTranslation();
return (
<Button
onClick={() => {
setBillEnterContext({
actions: {
//refetch: refetch
},
context: {
disableInvNumber: true,
//job: { id: job.id },
consumeinventoryid: inventoryline.id,
bill: {
vendorid: bodyshop.inhousevendorid,
invoice_number: "ih",
isinhouse: true,
date: moment(),
total: 0,
billlines: [{}],
// billlines: selectedLines.map((p) => {
// return {
// joblineid: p.id,
// actual_price: p.act_price,
// actual_cost: 0, //p.act_price,
// line_desc: p.line_desc,
// line_remarks: p.line_remarks,
// part_type: p.part_type,
// quantity: p.quantity || 1,
// applicable_taxes: {
// local: false,
// state: false,
// federal: false,
// },
// };
// }),
},
},
});
}}
>
{t("inventory.actions.addtoro")}
</Button>
);
}

View File

@@ -0,0 +1,67 @@
import { DeleteFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, notification, Popconfirm } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { DELETE_INVENTORY_LINE } from "../../graphql/inventory.queries";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
export default function InventoryLineDelete({
inventoryline,
disabled,
refetch,
}) {
const [loading, setLoading] = useState(false);
const { t } = useTranslation();
const [deleteInventoryLine] = useMutation(DELETE_INVENTORY_LINE);
const handleDelete = async () => {
setLoading(true);
const result = await deleteInventoryLine({
variables: { lineId: inventoryline.id },
// update(cache, { errors }) {
// cache.modify({
// fields: {
// inventory(existingInventory, { readField }) {
// console.log(existingInventory);
// return existingInventory.filter(
// (invRef) => inventoryline.id !== readField("id", invRef)
// );
// },
// },
// });
// },
});
if (!!!result.errors) {
notification["success"]({ message: t("inventory.successes.deleted") });
} else {
//Check if it's an fkey violation.
notification["error"]({
message: t("bills.errors.deleting", {
error: JSON.stringify(result.errors),
}),
});
}
if (refetch) refetch();
setLoading(false);
};
return (
<RbacWrapper action="inventory:delete" noauth={<></>}>
<Popconfirm
disabled={disabled || inventoryline.consumedbybillid}
onConfirm={handleDelete}
title={t("inventory.labels.deleteconfirm")}
>
<Button
disabled={disabled || inventoryline.consumedbybillid}
loading={loading}
>
<DeleteFilled />
</Button>
</Popconfirm>
</RbacWrapper>
);
}

View File

@@ -0,0 +1,228 @@
import { EditFilled, SyncOutlined, FileAddFilled } 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 { Link, useHistory, useLocation } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import InventoryBillRo from "../inventory-bill-ro/inventory-bill-ro.component";
import InventoryLineDelete from "../inventory-line-delete/inventory-line-delete.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
setInventoryUpsertContext: (context) =>
dispatch(setModalContext({ context: context, modal: "inventoryUpsert" })),
});
export function JobsList({
bodyshop,
refetch,
loading,
jobs,
total,
setInventoryUpsertContext,
}) {
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,
render: (text, record) =>
record.billline?.bill?.job ? (
<div>
<div>{text}</div>
<strong>{`(${record.billline?.bill?.job?.v_model_yr} ${record.billline?.bill?.job?.v_make_desc} ${record.billline?.bill?.job?.v_model_desc})`}</strong>
</div>
) : (
text
),
},
{
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 || "") +
" " +
(record.manualinvoicenumber || "")
).trim(),
},
{
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 || "") +
" " +
(record.manualvendor || "")
).trim(),
},
{
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.fields.comment"),
dataIndex: "comment",
key: "comment",
},
{
title: t("inventory.labels.consumedbyjob"),
dataIndex: "consumedbyjob",
key: "consumedbyjob",
ellipsis: true,
render: (text, record) =>
record.bill?.job?.ro_number ? (
<Link to={`/manage/jobs/${record.bill?.job?.id}`}>
{record.bill?.job?.ro_number}
</Link>
) : (
<InventoryBillRo inventoryline={record} />
),
},
{
title: t("general.labels.actions"),
dataIndex: "actions",
key: "actions",
ellipsis: true,
render: (text, record) => (
<Space wrap>
<Button
onClick={() => {
setInventoryUpsertContext({
actions: { refetch: refetch },
context: {
existingInventory: record,
},
});
}}
>
<EditFilled />
</Button>
<InventoryLineDelete inventoryline={record} refetch={refetch} />
</Space>
),
},
];
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={() => {
setInventoryUpsertContext({
actions: { refetch: refetch },
context: {},
});
}}
>
<FileAddFilled />
</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);

View File

@@ -0,0 +1,64 @@
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";
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 (
<InventoryListPaginated
refetch={refetch}
loading={loading}
searchParams={searchParams}
total={data ? data.search_inventory_aggregate.aggregate.count : 0}
jobs={data ? data.search_inventory : []}
/>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(InventoryList);

View File

@@ -0,0 +1,68 @@
import { Form, Input, Space } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectInventoryUpsert } from "../../redux/modals/modals.selectors";
import FormItemCurrency from "../form-items-formatted/currency-form-item.component";
const mapStateToProps = createStructuredSelector({
inventoryUpsertModal: selectInventoryUpsert,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(NoteUpsertModalComponent);
export function NoteUpsertModalComponent({ form, inventoryUpsertModal }) {
const { t } = useTranslation();
const { existingInventory } = inventoryUpsertModal.context;
return (
<Space wrap>
<Form.Item
label={t("billlines.fields.line_desc")}
rules={[{ required: true }]}
name="line_desc"
>
<Input />
</Form.Item>
<Form.Item label={t("inventory.fields.comment")} name="comment">
<Input />
</Form.Item>
{!existingInventory && (
<>
<Form.Item
label={t("inventory.fields.manualinvoicenumber")}
name="manualinvoicenumber"
>
<Input />
</Form.Item>
<Form.Item
label={t("inventory.fields.manualvendor")}
name="manualvendor"
>
<Input />
</Form.Item>
<Form.Item
rules={[{ required: true }]}
label={t("billlines.fields.actual_cost")}
name="actual_cost"
>
<FormItemCurrency />
</Form.Item>
<Form.Item
rules={[{ required: true }]}
label={t("billlines.fields.actual_price")}
name="actual_price"
>
<FormItemCurrency />
</Form.Item>
</>
)}
</Space>
);
}

View File

@@ -0,0 +1,126 @@
import { useMutation } from "@apollo/client";
import { Form, Modal, notification } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import {
INSERT_INVENTORY_LINE,
UPDATE_INVENTORY_LINE,
} from "../../graphql/inventory.queries";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectInventoryUpsert } from "../../redux/modals/modals.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import InventoryUpsertModal from "./inventory-upsert-modal.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
inventoryUpsertModal: selectInventoryUpsert,
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("inventoryUpsert")),
});
export function InventoryUpsertModalContainer({
currentUser,
bodyshop,
inventoryUpsertModal,
toggleModalVisible,
}) {
const { t } = useTranslation();
const [insertInventory] = useMutation(INSERT_INVENTORY_LINE);
const [updateInventoryLine] = useMutation(UPDATE_INVENTORY_LINE);
const { visible, context, actions } = inventoryUpsertModal;
const { existingInventory } = context;
const { refetch } = actions;
const [form] = Form.useForm();
useEffect(() => {
//Required to prevent infinite looping.
if (existingInventory && visible) {
form.setFieldsValue(existingInventory);
} else if (!existingInventory && visible) {
form.resetFields();
}
}, [existingInventory, form, visible]);
const handleFinish = async (formValues) => {
const values = formValues;
if (existingInventory) {
logImEXEvent("inventory_update");
updateInventoryLine({
variables: {
inventoryId: existingInventory.id,
inventoryItem: values,
},
}).then((r) => {
notification["success"]({
message: t("inventory.successes.updated"),
});
});
// if (refetch) refetch();
toggleModalVisible();
} else {
logImEXEvent("inventory_insert");
await insertInventory({
variables: {
inventoryItem: { shopid: bodyshop.id, ...values },
},
update(cache, { data }) {
cache.modify({
fields: {
inventory(existingInv) {
return [...existingInv, data.insert_inventory_one];
},
},
});
},
});
if (refetch) refetch();
form.resetFields();
toggleModalVisible();
notification["success"]({
message: t("inventory.successes.inserted"),
});
}
};
return (
<Modal
title={
existingInventory
? t("inventory.actions.edit")
: t("inventory.actions.new")
}
visible={visible}
okText={t("general.actions.save")}
onOk={() => {
form.submit();
}}
onCancel={() => {
toggleModalVisible();
}}
destroyOnClose
>
<Form form={form} onFinish={handleFinish} layout="vertical">
<InventoryUpsertModal form={form} />
</Form>
</Modal>
);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(InventoryUpsertModalContainer);

View File

@@ -86,6 +86,7 @@ export default function JobBillsTotalComponent({
const totalPartsSublet = Dinero(totals.parts.parts.total)
.add(Dinero(totals.parts.sublets.total))
.add(Dinero(totals.additional.shipping))
.add(Dinero(totals.additional.towing));
const discrepancy = totalPartsSublet.subtract(billTotals);

View File

@@ -11,7 +11,7 @@ const mapDispatchToProps = (dispatch) => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(JobPartsQueueCount);
export function JobPartsQueueCount({ bodyshop, parts }) {
export function JobPartsQueueCount({ bodyshop, parts, style }) {
const partsStatus = useMemo(() => {
if (!parts) return null;
return parts.reduce(
@@ -36,7 +36,7 @@ export function JobPartsQueueCount({ bodyshop, parts }) {
if (!parts) return null;
return (
<Row>
<Row style={style}>
<Col span={4}>
<Tooltip title="Total">
<Tag>{partsStatus.total}</Tag>

View File

@@ -22,7 +22,8 @@ export default function JobReconciliationModalComponent({ job, bills }) {
(j.part_type !== null && j.part_type !== "PAE") ||
(j.line_desc &&
j.line_desc.toLowerCase().includes("towing") &&
j.lbr_op === "OP13")
j.lbr_op === "OP13") ||
j.db_ref === "936004" //ADD SHIPPING LINE.
);
return (

View File

@@ -1,5 +1,6 @@
import i18next from "i18next";
import _ from "lodash";
export const reconcileByAssocLine = (
jobLines,
jobLineState,
@@ -73,7 +74,12 @@ export const reconcileByPrice = (
jobLines.forEach((jl) => {
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);
if (matchingBillLineIds.length > 1) {

View File

@@ -169,7 +169,7 @@ export default function ScoreboardAddButton({
};
return (
<Popover content={overlay} visible={visibility}>
<Popover content={overlay} visible={visibility} placement="bottom">
<Button
loading={loading}
disabled={disabled}

View File

@@ -1,8 +1,7 @@
import { LoadingOutlined } from "@ant-design/icons";
import { useLazyQuery } from "@apollo/client";
import { Empty, Select, Space, Tag } from "antd";
import { Select, Space, Tag } from "antd";
import _ from "lodash";
import React, { forwardRef, useEffect } from "react";
import React, { forwardRef, useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import {
SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE,
@@ -24,31 +23,35 @@ const JobSearchSelect = (
ref
) => {
const { t } = useTranslation();
const [theOptions, setTheOptions] = useState([]);
const [callSearch, { loading, error, data }] = useLazyQuery(
SEARCH_JOBS_FOR_AUTOCOMPLETE,
{
...(convertedOnly || notExported
? {
variables: {
...(convertedOnly ? { isConverted: true } : {}),
...(notExported ? { notExported: true } : {}),
...(notInvoiced ? { notInvoiced: true } : {}),
},
}
: {}),
}
{}
);
const [callIdSearch, { loading: idLoading, error: idError, data: idData }] =
useLazyQuery(SEARCH_JOBS_BY_ID_FOR_AUTOCOMPLETE);
const executeSearch = (v) => {
callSearch(v);
if (v && v !== "") callSearch(v);
};
const debouncedExecuteSearch = _.debounce(executeSearch, 500);
const handleSearch = (value) => {
debouncedExecuteSearch({ variables: { search: value } });
debouncedExecuteSearch({
variables: {
search: value,
...(convertedOnly || notExported
? {
variables: {
...(convertedOnly ? { isConverted: true } : {}),
...(notExported ? { notExported: true } : {}),
...(notInvoiced ? { notInvoiced: true } : {}),
},
}
: {}),
},
});
};
useEffect(() => {
@@ -57,13 +60,17 @@ const JobSearchSelect = (
}
}, [restProps.value, callIdSearch]);
const theOptions = _.uniqBy(
[
...(idData && idData.jobs_by_pk ? [idData.jobs_by_pk] : []),
...(data && data.search_jobs ? data.search_jobs : []),
],
"id"
);
useEffect(() => {
setTheOptions(
_.uniqBy(
[
...(idData && idData.jobs_by_pk ? [idData.jobs_by_pk] : []),
...(data && data.search_jobs ? data.search_jobs : []),
],
"id"
)
);
}, [data, idData]);
return (
<div>
@@ -77,7 +84,8 @@ const JobSearchSelect = (
}}
filterOption={false}
onSearch={handleSearch}
notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
loading={loading || idLoading}
//notFoundContent={loading ? <LoadingOutlined /> : <Empty />}
{...restProps}
>
{theOptions
@@ -99,7 +107,6 @@ const JobSearchSelect = (
))
: null}
</Select>
{idLoading || loading ? <LoadingOutlined /> : null}
{error ? <AlertComponent message={error.message} type="error" /> : null}
{idError ? (
<AlertComponent message={idError.message} type="error" />

View File

@@ -6,12 +6,17 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import moment from "moment";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { insertAuditTrail } from "../../redux/application/application.actions";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation }) =>
@@ -22,9 +27,15 @@ export default connect(
mapDispatchToProps
)(JobAdminMarkReexport);
export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) {
export function JobAdminMarkReexport({
insertAuditTrail,
bodyshop,
currentUser,
job,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [markJobForReexport] = useMutation(gql`
mutation MARK_JOB_FOR_REEXPORT($jobId: uuid!) {
update_jobs_by_pk(
@@ -101,6 +112,20 @@ export function JobAdminMarkReexport({ insertAuditTrail, bodyshop, job }) {
variables: { jobId: job.id, date_exported: moment() },
});
await insertExportLog({
variables: {
logs: [
{
bodyshopid: bodyshop.id,
jobid: job.id,
successful: true,
message: JSON.stringify([t("general.labels.markedexported")]),
useremail: currentUser.email,
},
],
},
});
if (!result.errors) {
notification["success"]({ message: t("jobs.successes.save") });
insertAuditTrail({

View File

@@ -4,14 +4,15 @@ import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import {
selectAuthLevel,
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
authLevel: selectAuthLevel,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -23,6 +24,8 @@ export default connect(
)(BillMarkSelectedExported);
export function BillMarkSelectedExported({
bodyshop,
currentUser,
billids,
disabled,
loadingCallback,
@@ -31,7 +34,7 @@ export function BillMarkSelectedExported({
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [insertExportLog] = useMutation(INSERT_EXPORT_LOG);
const [updateBill] = useMutation(gql`
mutation UPDATE_BILL($billIds: [uuid!]!) {
update_bills(where: { id: { _in: $billIds } }, _set: { exported: true }) {
@@ -49,9 +52,21 @@ export function BillMarkSelectedExported({
loadingCallback(true);
const result = await updateBill({
variables: { billIds: billids },
update(cache){
}
update(cache) {},
});
await insertExportLog({
variables: {
logs: billids.map((id) => {
return {
bodyshopid: bodyshop.id,
billid: id,
successful: true,
message: JSON.stringify([t("general.labels.markedexported")]),
useremail: currentUser.email,
};
}),
},
});
if (!result.errors) {

View File

@@ -1,6 +1,6 @@
import Icon from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, Input, Popover } from "antd";
import { Button, Input, Popover, Tooltip } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { FaRegStickyNote } from "react-icons/fa";
@@ -69,10 +69,11 @@ export default function ProductionListColumnComment({ record }) {
cursor: "pointer",
overflow: "hidden",
textOverflow: "ellipsis",
display: "inline-block",
}}
>
<Icon component={FaRegStickyNote} style={{ marginRight: ".2rem" }} />
{record.comment || " "}
<Tooltip title={record.comment}>{record.comment || " "}</Tooltip>
</div>
</Popover>
);

View File

@@ -5,25 +5,26 @@ import moment from "moment";
import React from "react";
import { Link } from "react-router-dom";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { TimeFormatter } from "../../utils/DateFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
import JobAltTransportChange from "../job-at-change/job-at-change.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
import ProductionListColumnAlert from "./production-list-columns.alert.component";
import ProductionListColumnBodyPriority from "./production-list-columns.bodypriority.component";
import ProductionListColumnComment from "./production-list-columns.comment.component";
import ProductionListDate from "./production-list-columns.date.component";
import ProductionListColumnDetailPriority from "./production-list-columns.detailpriority.component";
import ProductionListEmployeeAssignment from "./production-list-columns.empassignment.component";
import ProductionListLastContacted from "./production-list-columns.lastcontacted.component";
import ProductionListColumnPaintPriority from "./production-list-columns.paintpriority.component";
import ProductionListColumnNote from "./production-list-columns.productionnote.component";
import ProductionListColumnStatus from "./production-list-columns.status.component";
import ProductionListColumnCategory from "./production-list-columns.status.category";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
import ProductionListColumnComment from "./production-list-columns.comment.component";
import ProductionListColumnPartsReceived from "./production-list-columns.partsreceived.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
import ProductionListColumnNote from "./production-list-columns.productionnote.component";
import ProductionListColumnCategory from "./production-list-columns.status.category";
import ProductionListColumnStatus from "./production-list-columns.status.component";
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
const r = ({ technician, state, activeStatuses, bodyshop }) => {
return [
@@ -105,6 +106,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
<ProductionListDate record={record} field="actual_in" time />
),
},
{
title: i18n.t("jobs.fields.actual_in") + " (HH:MM)",
dataIndex: "actual_in_time",
key: "actual_in_time",
ellipsis: true,
render: (text, record) => (
<TimeFormatter>{record.actual_in}</TimeFormatter>
),
},
{
title: i18n.t("jobs.fields.scheduled_completion"),
dataIndex: "scheduled_completion",
@@ -124,6 +135,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
/>
),
},
{
title: i18n.t("jobs.fields.scheduled_completion") + " (HH:MM)",
dataIndex: "scheduled_completion_time",
key: "scheduled_completion_time",
ellipsis: true,
render: (text, record) => (
<TimeFormatter>{record.scheduled_completion}</TimeFormatter>
),
},
{
title: i18n.t("jobs.fields.date_last_contacted"),
dataIndex: "date_last_contacted",
@@ -176,6 +197,16 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
/>
),
},
{
title: i18n.t("jobs.fields.scheduled_delivery") + " (HH:MM)",
dataIndex: "scheduled_delivery_time",
key: "scheduled_delivery_time",
ellipsis: true,
render: (text, record) => (
<TimeFormatter>{record.scheduled_delivery}</TimeFormatter>
),
},
{
title: i18n.t("jobs.fields.ins_co_nm"),
dataIndex: "ins_co_nm",

View File

@@ -50,50 +50,45 @@ export default function ProductionListDate({
"production-completion-soon"));
}
return (
<div>
<Dropdown
//trigger={["click"]}
visible={visible}
style={{
height: "19px",
}}
overlay={
<Card
style={{ padding: "1rem" }}
<Dropdown
//trigger={["click"]}
visible={visible}
style={{
height: "19px",
}}
overlay={
<Card style={{ padding: "1rem" }} onClick={(e) => e.stopPropagation()}>
<FormDatePicker
onClick={(e) => e.stopPropagation()}
>
<FormDatePicker
value={(record[field] && moment(record[field])) || null}
onChange={handleChange}
format="MM/DD/YYYY"
isDateOnly={!time}
/>
{time && (
<TimePicker
onClick={(e) => e.stopPropagation()}
value={(record[field] && moment(record[field])) || null}
onChange={handleChange}
format="MM/DD/YYYY"
isDateOnly={!time}
minuteStep={15}
format="hh:mm a"
/>
{time && (
<TimePicker
onClick={(e) => e.stopPropagation()}
value={(record[field] && moment(record[field])) || null}
onChange={handleChange}
minuteStep={15}
format="hh:mm a"
/>
)}
<Button onClick={() => setVisible(false)}>
{t("general.actions.close")}
</Button>
</Card>
}
)}
<Button onClick={() => setVisible(false)}>
{t("general.actions.close")}
</Button>
</Card>
}
>
<div
onClick={() => setVisible(true)}
style={{
height: "19px",
}}
className={className}
>
<div
onClick={() => setVisible(true)}
style={{
height: "19px",
}}
className={className}
>
<DateFormatter bordered={false}>{record[field]}</DateFormatter>
</div>
</Dropdown>
</div>
<DateFormatter bordered={false}>{record[field]}</DateFormatter>
</div>
</Dropdown>
);
}

View File

@@ -25,7 +25,7 @@ const ret = {
"jobs:detail": 1,
"jobs:partsqueue": 4,
"jobs:checklist-view": 2,
"jobs:list-ready": 1,
"bills:enter": 2,
"bills:view": 2,
"bills:list": 2,
@@ -66,5 +66,8 @@ const ret = {
"timetickets:shiftedit": 5,
"users:editaccess": 4,
"inventory:list": 1,
"inventory:delete": 2,
};
export default ret;

View File

@@ -26,7 +26,7 @@ export default function ScoreboardEntryEdit({ entry }) {
return;
} else {
notification["success"]({
message: t("scoredboard.successes.updated"),
message: t("scoreboard.successes.updated"),
});
setVisible(false);
}

View File

@@ -47,3 +47,15 @@ export const ListOfDaysInCurrentMonth = () => {
days.push(dateEnd.format("YYYY-MM-DD"));
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;
};

View File

@@ -0,0 +1,79 @@
import { Card } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import {
Bar,
CartesianGrid,
ComposedChart,
LabelList,
Legend,
ReferenceLine,
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 />
<ReferenceLine
y={
bodyshop.scoreboard_target.dailyBodyTarget +
bodyshop.scoreboard_target.dailyPaintTarget
}
label="Daily Target"
stroke="red"
strokeDasharray="3 3"
/>
{data &&
data.employees.map((e, idx) => (
<Bar
key={`${e}productive`}
name={e}
dataKey={`employees.${e}.productive`}
stackId="productive"
// barSize={20}
fill={data.colors[idx]}
>
<LabelList position="top" />
</Bar>
))}
</ComposedChart>
</ResponsiveContainer>
</Card>
);
}

View File

@@ -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_SB } 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_SB, {
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;
}

View File

@@ -0,0 +1,123 @@
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();
console.log(data);
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>
<Row gutter={[16, 16]}>
<Col span={12}>
<Statistic
title={t("scoreboard.labels.totaloverperiod")}
value={data.totalOverPeriod.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>
);
}

View File

@@ -3,9 +3,28 @@ import React from "react";
import { useTranslation } from "react-i18next";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
export default function ShopInfoRbacComponent({ form }) {
const { t } = useTranslation();
import { useTreatments } from "@splitsoftware/splitio-react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(ShopInfoRbacComponent);
export function ShopInfoRbacComponent({ form, bodyshop }) {
const { t } = useTranslation();
const { Simple_Inventory } = useTreatments(
["Simple_Inventory"],
{},
bodyshop && bodyshop.imexshopid
);
return (
<RbacWrapper action="shop:rbac">
<LayoutFormRow>
@@ -633,6 +652,34 @@ export default function ShopInfoRbacComponent({ form }) {
>
<InputNumber />
</Form.Item>
{Simple_Inventory.treatment === "on" && (
<>
<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>
<Form.Item
label={t("bodyshop.fields.rbac.inventory.delete")}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name={["md_rbac", "inventory:delete"]}
>
<InputNumber />
</Form.Item>
</>
)}
</LayoutFormRow>
</RbacWrapper>
);

View File

@@ -60,7 +60,10 @@ export function SignInComponent({
<Form.Item
name="email"
rules={[
{ required: true, message: t("general.validation.required") },
{
required: true,
message: t("general.validation.required", { label: "Email" }),
},
]}
>
<Input
@@ -71,7 +74,10 @@ export function SignInComponent({
<Form.Item
name="password"
rules={[
{ required: true, message: t("general.validation.required") },
{
required: true,
message: t("general.validation.required", { label: "Password" }),
},
]}
>
<Input

View File

@@ -11,13 +11,21 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
import TechClockInComponent from "./tech-job-clock-in-form.component";
import TechJobPrintTickets from "../tech-job-print-tickets/tech-job-print-tickets.component";
import moment from "moment";
import { setModalContext } from "../../redux/modals/modals.actions";
const mapStateToProps = createStructuredSelector({
technician: selectTechnician,
bodyshop: selectBodyshop,
});
export function TechClockInContainer({ technician, bodyshop }) {
const mapDispatchToProps = (dispatch) => ({
setTimeTicketContext: (context) =>
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
});
export function TechClockInContainer({
setTimeTicketContext,
technician,
bodyshop,
}) {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
const [insertTimeTicket] = useMutation(INSERT_NEW_TIME_TICKET, {
@@ -75,6 +83,16 @@ export function TechClockInContainer({ technician, bodyshop }) {
title={t("timetickets.labels.clockintojob")}
extra={
<Space wrap>
<Button
onClick={() => {
setTimeTicketContext({
actions: {},
context: { timeticket: { employeeid: technician.id } },
});
}}
>
{t("timetickets.actions.enter")}
</Button>
<TechJobPrintTickets />
<Button
type="primary"
@@ -92,4 +110,7 @@ export function TechClockInContainer({ technician, bodyshop }) {
</Card>
);
}
export default connect(mapStateToProps, null)(TechClockInContainer);
export default connect(
mapStateToProps,
mapDispatchToProps
)(TechClockInContainer);

View File

@@ -43,9 +43,9 @@ export function TechLookupJobsDrawer({ bodyshop, setPrintCenterContext }) {
xs: "100%",
sm: "100%",
md: "100%",
lg: "70%",
xl: "70%",
xxl: "70%",
lg: "100%",
xl: "90%",
xxl: "85%",
};
const drawerPercentage = selectedBreakpoint
? bpoints[selectedBreakpoint[0]]

View File

@@ -17,6 +17,7 @@ export default function TimeTicketsDatesSelector() {
if (!!start && !!end) {
history.push({
search: queryString.stringify({
...searchParams,
start: start.format("YYYY-MM-DD"),
end: end.format("YYYY-MM-DD"),
}),
@@ -25,6 +26,7 @@ export default function TimeTicketsDatesSelector() {
} else {
history.push({
search: queryString.stringify({
...searchParams,
start: null,
end: null,
}),

View File

@@ -35,6 +35,7 @@ export function TimeTicketModalComponent({
authLevel,
employeeAutoCompleteOptions,
isEdit,
employeeSelectDisabled,
}) {
const { t } = useTranslation();
@@ -118,6 +119,7 @@ export function TimeTicketModalComponent({
]}
>
<EmployeeSearchSelect
disabled={employeeSelectDisabled}
options={employeeAutoCompleteOptions}
onSelect={(value) => {
const emps =

View File

@@ -244,6 +244,12 @@ export function TimeTicketModalContainer({
employeeAutoCompleteOptions={
EmployeeAutoCompleteData && EmployeeAutoCompleteData.employees
}
employeeSelectDisabled={
timeTicketModal.context?.timeticket?.employeeid &&
!timeTicketModal.context.id
? true
: false
}
/>
</Form>
</Modal>

View File

@@ -152,6 +152,10 @@ export const QUERY_BILL_BY_PK = gql`
state_tax_rate
federal_tax_rate
isinhouse
inventories {
id
line_desc
}
vendor {
id
name
@@ -165,6 +169,9 @@ export const QUERY_BILL_BY_PK = gql`
cost_center
quantity
joblineid
inventories {
id
}
jobline {
oem_partno
part_type

View File

@@ -80,6 +80,7 @@ export const QUERY_ALL_CC = gql`
status
vin
year
mileage
cccontracts(
where: { status: { _eq: "contracts.status.out" } }
order_by: { contract_date: desc }

View File

@@ -0,0 +1,181 @@
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 } }
order_by: { line_desc: asc }
) {
id
actual_cost
actual_price
quantity
billlineid
line_desc
comment
manualinvoicenumber
manualvendor
consumedbybillid
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
comment
manualinvoicenumber
manualvendor
consumedbybillid
bill {
id
invoice_number
job {
ro_number
id
}
}
billline {
id
bill {
id
invoice_number
job {
id
v_make_desc
v_model_desc
v_model_yr
}
vendor {
id
name
}
}
}
}
search_inventory_aggregate(
args: { search: $search }
where: { consumedbybillid: { _is_null: $consumedIsNull } }
) {
aggregate {
count(distinct: true)
}
}
}
`;
export const DELETE_INVENTORY_LINE = gql`
mutation DELETE_INVENTORY_LINE($lineId: uuid!) {
delete_inventory_by_pk(id: $lineId) {
id
}
}
`;
export const INSERT_INVENTORY_LINE = gql`
mutation INSERT_INVENTORY_LINE($inventoryItem: inventory_insert_input!) {
insert_inventory_one(object: $inventoryItem) {
id
line_desc
consumedbybillid
billlineid
actual_price
actual_cost
comment
manualinvoicenumber
manualvendor
bill {
invoice_number
}
}
}
`;
export const UPDATE_INVENTORY_LINE = gql`
mutation UPDATE_INVENTORY_LINE(
$inventoryId: uuid!
$inventoryItem: inventory_set_input!
) {
update_inventory_by_pk(
pk_columns: { id: $inventoryId }
_set: $inventoryItem
) {
id
line_desc
consumedbybillid
billlineid
actual_price
actual_cost
comment
manualinvoicenumber
manualvendor
bill {
invoice_number
}
}
}
`;

View File

@@ -61,6 +61,7 @@ export const GET_LINE_TICKET_BY_PK = gql`
flat_rate
clockon
clockoff
rate
employee {
id
first_name

View File

@@ -85,6 +85,7 @@ export const QUERY_PARTS_QUEUE = gql`
vehicleid
ownerid
queued_for_parts
comment
joblines_status {
count
part_type
@@ -1828,6 +1829,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
ins_co_id
dms_allocation
id
inproduction
ded_amt
ded_status
depreciation_taxes

View File

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

View File

@@ -48,6 +48,7 @@ export const socket = SocketIO(
: window.location.origin,
{
path: "/ws",
withCredentials: true,
auth: async (callback) => {
const token = auth.currentUser && (await auth.currentUser.getIdToken());
callback({ token });

View File

@@ -0,0 +1,34 @@
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";
import InventoryUpsertModalContainer from "../../components/inventory-upsert-modal/inventory-upsert-modal.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">
<InventoryUpsertModalContainer />
<InventoryList />
</RbacWrapper>
);
}
export default connect(null, mapDispatchToProps)(InventoryPage);

View File

@@ -10,6 +10,7 @@ import {
PageHeader,
InputNumber,
Input,
Switch,
} from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -42,7 +43,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
const [closeJob] = useMutation(UPDATE_JOB);
const [loading, setLoading] = useState(false);
const handleFinish = async (values) => {
const handleFinish = async ({ removefromproduction, ...values }) => {
setLoading(true);
const result = await client.mutate({
mutation: generateJobLinesUpdatesForInvoicing(values.joblines),
@@ -63,6 +64,7 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
kmin: values.kmin,
kmout: values.kmout,
dms_allocation: values.dms_allocation,
...(removefromproduction ? { inproduction: false } : {}),
},
},
refetchQueries: ["QUERY_JOB_CLOSE_DETAILS"],
@@ -248,6 +250,15 @@ export function JobsCloseComponent({ job, bodyshop, jobRO }) {
onlyFuture={!!bodyshop.cdk_dealerid}
/>
</Form.Item>
{!jobRO && job.inproduction && (
<Form.Item
label={t("jobs.actions.removefromproduction")}
name="removefromproduction"
valuePropName="checked"
>
<Switch />
</Form.Item>
)}
{(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && (
<Form.Item
label={t("jobs.fields.kmin")}

View File

@@ -34,6 +34,7 @@ const JobsPage = lazy(() => import("../jobs/jobs.page"));
const JobsDetailPage = lazy(() =>
import("../jobs-detail/jobs-detail.page.container")
);
const InventoryListPage = lazy(() => import("../inventory/inventory.page"));
const ProfilePage = lazy(() => import("../profile/profile.container.page"));
const JobsAvailablePage = lazy(() =>
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} />
</Switch>
<Route exact path={`${match.path}/temporarydocs/`} component={TempDocs} />
<Route
exact
path={`${match.path}/inventory/`}
component={InventoryListPage}
/>
<Route
exact
path={`${match.path}/courtesycars/`}

View File

@@ -1,7 +1,6 @@
import { SyncOutlined } from "@ant-design/icons";
import { useQuery } from "@apollo/client";
import { Button, Card, Input, Space, Table } from "antd";
import _ from "lodash";
import queryString from "query-string";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
@@ -12,6 +11,7 @@ import AlertComponent from "../../components/alert/alert.component";
import JobPartsQueueCount from "../../components/job-parts-queue-count/job-parts-queue-count.component";
import JobRemoveFromPartsQueue from "../../components/job-remove-from-parst-queue/job-remove-from-parts-queue.component";
import OwnerNameDisplay from "../../components/owner-name-display/owner-name-display.component";
import ProductionListColumnComment from "../../components/production-list-columns/production-list-columns.comment.component";
import { QUERY_PARTS_QUEUE } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper";
@@ -92,13 +92,7 @@ export function PartsQueuePageComponent({ bodyshop }) {
// searchParams.page = pagination.current;
searchParams.sortcolumn = sorter.columnKey;
searchParams.sortorder = sorter.order;
if (filters.status) {
searchParams.statusFilters = JSON.stringify(
_.flattenDeep(filters.status)
);
} else {
delete searchParams.statusFilters;
}
history.push({ search: queryString.stringify(searchParams) });
};
@@ -235,15 +229,35 @@ export function PartsQueuePageComponent({ bodyshop }) {
dataIndex: "partsstatus",
key: "partsstatus",
render: (text, record) => (
<JobPartsQueueCount parts={record.joblines_status} />
<JobPartsQueueCount
style={{ minWidth: "10rem" }}
parts={record.joblines_status}
/>
),
},
{
title: t("jobs.fields.comment"),
dataIndex: "comment",
key: "comment",
render: (text, record) => <ProductionListColumnComment record={record} />,
},
{
title: t("jobs.fields.queued_for_parts"),
dataIndex: "queued_for_parts",
key: "queued_for_parts",
sorter: (a, b) => a.queued_for_parts - b.queued_for_parts,
sortOrder: sortcolumn === "queued_for_parts" && sortorder,
filters: [
{
text: "Queued",
value: true,
},
{
text: "Unqueued",
value: false,
},
],
onFilter: (value, record) => record.queued_for_parts === value,
render: (text, record) => (
<JobRemoveFromPartsQueue
checked={record.queued_for_parts}

View File

@@ -1,15 +1,21 @@
import Icon, { FieldTimeOutlined } from "@ant-design/icons";
import { Tabs } from "antd";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { FaShieldAlt } from "react-icons/fa";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
import ScoreboardDisplay from "../../components/scoreboard-display/scoreboard-display.component";
import ScoreboardTimeTickets from "../../components/scoreboard-timetickets/scoreboard-timetickets.component";
import {
setBreadcrumbs,
setSelectedHeader,
} from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import queryString from "query-string";
import { useHistory, useLocation } from "react-router-dom";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -22,7 +28,9 @@ const mapDispatchToProps = (dispatch) => ({
export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
const { t } = useTranslation();
const searchParams = queryString.parse(useLocation().search);
const { tab } = searchParams;
const history = useHistory();
useEffect(() => {
document.title = t("titles.scoreboard");
setSelectedHeader("scoreboard");
@@ -37,7 +45,41 @@ export function ScoreboardContainer({ setBreadcrumbs, setSelectedHeader }) {
return (
<FeatureWrapper featureName="scoreboard">
<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>
<FieldTimeOutlined />
{t("scoreboard.labels.timetickets")}
</span>
}
destroyInactiveTabPane
key="tickets"
>
<ScoreboardTimeTickets />
</Tabs.TabPane>
</Tabs>
</RbacWrapper>
</FeatureWrapper>
);

View File

@@ -6,7 +6,6 @@ export const getJobMedia = (jobid) => ({
});
export const getBillMedia = ({ jobid, invoice_number }) => {
console.log("in the action");
return {
type: MediaActionTypes.GET_MEDIA_FOR_BILL,
payload: { jobid, invoice_number },

View File

@@ -23,6 +23,7 @@ const INITIAL_STATE = {
reportCenter: { ...baseModal },
partsReceive: { ...baseModal },
contractFinder: { ...baseModal },
inventoryUpsert: { ...baseModal },
ca_bc_eftTableConvert: { ...baseModal },
};

View File

@@ -70,6 +70,10 @@ export const selectContractFinder = createSelector(
[selectModals],
(modals) => modals.contractFinder
);
export const selectInventoryUpsert = createSelector(
[selectModals],
(modals) => modals.inventoryUpsert
);
export const selectCaBcEtfTableConvert = createSelector(
[selectModals],

View File

@@ -147,6 +147,7 @@
"errors": {
"creating": "Error adding bill. {{error}}",
"deleting": "Error deleting bill. {{error}}",
"existinginventoryline": "This bill cannot be deleted as it is tied to items in inventory.",
"exporting": "Error exporting payable(s). {{error}}",
"exporting-partner": "Unable to connect to ImEX Partner. Please ensure it is running and logged in.",
"invalidro": "Not a valid RO.",
@@ -207,6 +208,7 @@
"reexport": "Bill marked for re-export."
},
"validation": {
"inventoryquantity": "Quantity must be greater than or equal to what has been added to inventory ({{number}}).",
"manualinhouse": "Manual posting to the in house vendor is restricted. ",
"unique_invoice_number": "This invoice number has already been entered for this vendor."
}
@@ -352,6 +354,10 @@
"employees": {
"page": "Employees -> List"
},
"inventory": {
"delete": "Inventory -> Delete",
"list": "Inventory -> List"
},
"jobs": {
"admin": "Jobs -> Admin",
"available-list": "Jobs -> Available List",
@@ -988,6 +994,7 @@
"loadingapp": "Loading $t(titles.app)",
"loadingshop": "Loading shop data...",
"loggingin": "Authorizing...",
"markedexported": "Manually marked as exported.",
"message": "Message",
"monday": "Monday",
"na": "N/A",
@@ -1069,6 +1076,37 @@
"printpack": "Intake Print Pack"
}
},
"inventory": {
"actions": {
"addtoinventory": "Add to Inventory",
"addtoro": "Add to RO",
"consumefrominventory": "Consume from Inventory?",
"edit": "Edit Inventory LIne",
"new": "New Inventory Line"
},
"errors": {
"inserting": "Error inserting inventory item. {{error}}"
},
"fields": {
"comment": "Comment",
"manualinvoicenumber": "Invoice Number",
"manualvendor": "Vendor"
},
"labels": {
"consumedbyjob": "Consumed by Job",
"deleteconfirm": "Are you sure you want to delete this from inventory? The associated bill will not be modified or deleted. ",
"frombillinvoicenumber": "Original Bill Invoice Number",
"fromvendor": "Original Bill Vendor",
"inventory": "Inventory",
"showall": "Show All Inventory",
"showavailable": "Show Only Available Inventory"
},
"successes": {
"deleted": "Inventory lined deleted.",
"inserted": "Added line to inventory.",
"updated": "Inventory line updated."
}
},
"joblines": {
"actions": {
"new": "New Line"
@@ -1754,6 +1792,7 @@
"export-logs": "Export Logs",
"help": "Help",
"home": "Home",
"inventory": "Inventory",
"jobs": "Jobs",
"newjob": "Create New Job",
"owners": "Owners",
@@ -2403,11 +2442,21 @@
},
"labels": {
"asoftodaytarget": "As of Today",
"calendarperiod": "Periods based on calendar weeks/months.",
"dailyactual": "Actual (D)",
"dailytarget": "Daily",
"jobs": "Jobs",
"lastmonth": "Last Month",
"lastweek": "Last Week",
"monthlytarget": "Monthly",
"productivestatistics": "Productive Hours Statistics",
"productivetimeticketsoverdate": "Productive Hours over Selected Dates",
"targets": "Targets",
"thismonth": "This Month",
"thisweek": "This Week",
"timetickets": "Timetickets",
"todateactual": "Actual (MTD)",
"totaloverperiod": "Total over Selected Dates",
"weeklyactual": "Actual (W)",
"weeklytarget": "Weekly",
"workingdays": "Working Days / Month"
@@ -2418,11 +2467,6 @@
"updated": "Scoreboard updated."
}
},
"scoredboard": {
"successes": {
"updated": "Scoreboard entry updated."
}
},
"tech": {
"fields": {
"employeeid": "Employee ID",
@@ -2523,6 +2567,7 @@
"dashboard": "Dashboard",
"dms": "DMS Export",
"export-logs": "Export Logs",
"inventory": "Inventory",
"jobs": "Jobs",
"jobs-active": "Active Jobs",
"jobs-admin": "Admin",
@@ -2563,6 +2608,7 @@
"dashboard": "Dashboard | $t(titles.app)",
"dms": "DMS Export | $t(titles.app)",
"export-logs": "Export Logs | $t(titles.app)",
"inventory": "Inventory | $t(titles.app)",
"jobs": "Active Jobs | $t(titles.app)",
"jobs-admin": "Job {{ro_number}} - Admin | $t(titles.app)",
"jobs-all": "All Jobs | $t(titles.app)",

View File

@@ -147,6 +147,7 @@
"errors": {
"creating": "",
"deleting": "",
"existinginventoryline": "",
"exporting": "",
"exporting-partner": "",
"invalidro": "",
@@ -207,6 +208,7 @@
"reexport": ""
},
"validation": {
"inventoryquantity": "",
"manualinhouse": "",
"unique_invoice_number": ""
}
@@ -352,6 +354,10 @@
"employees": {
"page": ""
},
"inventory": {
"delete": "",
"list": ""
},
"jobs": {
"admin": "",
"available-list": "",
@@ -988,6 +994,7 @@
"loadingapp": "Cargando $t(titles.app)",
"loadingshop": "Cargando datos de la tienda ...",
"loggingin": "Iniciando sesión ...",
"markedexported": "",
"message": "",
"monday": "",
"na": "N / A",
@@ -1069,6 +1076,37 @@
"printpack": ""
}
},
"inventory": {
"actions": {
"addtoinventory": "",
"addtoro": "",
"consumefrominventory": "",
"edit": "",
"new": ""
},
"errors": {
"inserting": ""
},
"fields": {
"comment": "",
"manualinvoicenumber": "",
"manualvendor": ""
},
"labels": {
"consumedbyjob": "",
"deleteconfirm": "",
"frombillinvoicenumber": "",
"fromvendor": "",
"inventory": "",
"showall": "",
"showavailable": ""
},
"successes": {
"deleted": "",
"inserted": "",
"updated": ""
}
},
"joblines": {
"actions": {
"new": ""
@@ -1754,6 +1792,7 @@
"export-logs": "",
"help": "",
"home": "Casa",
"inventory": "",
"jobs": "Trabajos",
"newjob": "",
"owners": "propietarios",
@@ -2403,11 +2442,21 @@
},
"labels": {
"asoftodaytarget": "",
"calendarperiod": "",
"dailyactual": "",
"dailytarget": "",
"jobs": "",
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
"productivestatistics": "",
"productivetimeticketsoverdate": "",
"targets": "",
"thismonth": "",
"thisweek": "",
"timetickets": "",
"todateactual": "",
"totaloverperiod": "",
"weeklyactual": "",
"weeklytarget": "",
"workingdays": ""
@@ -2418,11 +2467,6 @@
"updated": ""
}
},
"scoredboard": {
"successes": {
"updated": ""
}
},
"tech": {
"fields": {
"employeeid": "",
@@ -2523,6 +2567,7 @@
"dashboard": "",
"dms": "",
"export-logs": "",
"inventory": "",
"jobs": "",
"jobs-active": "",
"jobs-admin": "",
@@ -2563,6 +2608,7 @@
"dashboard": "",
"dms": "",
"export-logs": "",
"inventory": "",
"jobs": "Todos los trabajos | $t(titles.app)",
"jobs-admin": "",
"jobs-all": "",

View File

@@ -147,6 +147,7 @@
"errors": {
"creating": "",
"deleting": "",
"existinginventoryline": "",
"exporting": "",
"exporting-partner": "",
"invalidro": "",
@@ -207,6 +208,7 @@
"reexport": ""
},
"validation": {
"inventoryquantity": "",
"manualinhouse": "",
"unique_invoice_number": ""
}
@@ -352,6 +354,10 @@
"employees": {
"page": ""
},
"inventory": {
"delete": "",
"list": ""
},
"jobs": {
"admin": "",
"available-list": "",
@@ -988,6 +994,7 @@
"loadingapp": "Chargement de $t(titles.app)",
"loadingshop": "Chargement des données de la boutique ...",
"loggingin": "Vous connecter ...",
"markedexported": "",
"message": "",
"monday": "",
"na": "N / A",
@@ -1069,6 +1076,37 @@
"printpack": ""
}
},
"inventory": {
"actions": {
"addtoinventory": "",
"addtoro": "",
"consumefrominventory": "",
"edit": "",
"new": ""
},
"errors": {
"inserting": ""
},
"fields": {
"comment": "",
"manualinvoicenumber": "",
"manualvendor": ""
},
"labels": {
"consumedbyjob": "",
"deleteconfirm": "",
"frombillinvoicenumber": "",
"fromvendor": "",
"inventory": "",
"showall": "",
"showavailable": ""
},
"successes": {
"deleted": "",
"inserted": "",
"updated": ""
}
},
"joblines": {
"actions": {
"new": ""
@@ -1754,6 +1792,7 @@
"export-logs": "",
"help": "",
"home": "Accueil",
"inventory": "",
"jobs": "Emplois",
"newjob": "",
"owners": "Propriétaires",
@@ -2403,11 +2442,21 @@
},
"labels": {
"asoftodaytarget": "",
"calendarperiod": "",
"dailyactual": "",
"dailytarget": "",
"jobs": "",
"lastmonth": "",
"lastweek": "",
"monthlytarget": "",
"productivestatistics": "",
"productivetimeticketsoverdate": "",
"targets": "",
"thismonth": "",
"thisweek": "",
"timetickets": "",
"todateactual": "",
"totaloverperiod": "",
"weeklyactual": "",
"weeklytarget": "",
"workingdays": ""
@@ -2418,11 +2467,6 @@
"updated": ""
}
},
"scoredboard": {
"successes": {
"updated": ""
}
},
"tech": {
"fields": {
"employeeid": "",
@@ -2523,6 +2567,7 @@
"dashboard": "",
"dms": "",
"export-logs": "",
"inventory": "",
"jobs": "",
"jobs-active": "",
"jobs-admin": "",
@@ -2563,6 +2608,7 @@
"dashboard": "",
"dms": "",
"export-logs": "",
"inventory": "",
"jobs": "Tous les emplois | $t(titles.app)",
"jobs-admin": "",
"jobs-all": "",

View File

@@ -17,6 +17,11 @@ export function DateTimeFormatter(props) {
)
: null;
}
export function TimeFormatter(props) {
return props.children
? moment(props.children).format(props.format ? props.format : "hh:mm a")
: null;
}
export function TimeAgoFormatter(props) {
const m = moment(props.children);

View File

@@ -80,8 +80,30 @@ export default async function RenderTemplate(
} else {
let pdf;
if (bodyshop.attach_pdf_to_email) {
const pdfRequest = _.cloneDeep(reportRequest);
pdfRequest.template.recipe = "chrome-pdf";
const pdfRequest = _.cloneDeep(reportRequest); //Updates to spread in the header details.
pdfRequest.template = {
...pdfRequest.template,
...{
recipe: "chrome-pdf",
...(!ignoreCustomMargins && {
chrome: {
marginTop:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.headerMargin &&
bodyshop.logo_img_path.headerMargin > 36
? bodyshop.logo_img_path.headerMargin
: "36px",
marginBottom:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.footerMargin &&
bodyshop.logo_img_path.footerMargin > 50
? bodyshop.logo_img_path.footerMargin
: "50px",
},
}),
},
};
console.log("PDFREQ", pdfRequest);
const pdfRender = await jsreport.renderAsync(pdfRequest);
pdf = pdfRender.toDataURI();
}

View File

@@ -1,5 +1,4 @@
export default async function FcmHandler({ client, payload }) {
console.log("Handling payload type", payload);
switch (payload.type) {
case "messaging-inbound":
client.cache.modify({

View File

@@ -36,10 +36,10 @@
lodash "^4.17.21"
resize-observer-polyfill "^1.5.0"
"@apollo/client@^3.6.2":
version "3.6.2"
resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.6.2.tgz#0418bfa6358dd117894c8af396706cfa2b186032"
integrity sha512-DNWyl+NNU2VsfHtXwOr4rV9hnQFPkl2/dNXeouhk9q7bXCWdEh3K8YTt/frULGVKbQjtnlPmz8C+LFI/JZ2N3w==
"@apollo/client@^3.6.6":
version "3.6.6"
resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.6.6.tgz#3fb1f5b11da9a64b51b5a86b62450bee034e7f41"
integrity sha512-AzNLN043wy0bDTTR9wzKYSu+I1IT2Ox3+vWckxgIt88Jfw5jHBvumf3lXE1JlgvbFCTiKS/Sa66AadQXWMVBRQ==
dependencies:
"@graphql-typed-document-node/core" "^3.1.1"
"@wry/context" "^0.6.0"
@@ -50,10 +50,9 @@
optimism "^0.16.1"
prop-types "^15.7.2"
symbol-observable "^4.0.0"
ts-invariant "^0.10.0"
ts-invariant "^0.10.3"
tslib "^2.3.0"
use-sync-external-store "^1.0.0"
zen-observable-ts "^1.2.0"
zen-observable-ts "^1.2.5"
"@asseinfo/react-kanban@^2.2.0":
version "2.2.0"
@@ -1106,10 +1105,10 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.17.9":
version "7.17.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72"
integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==
"@babel/runtime@^7.18.3":
version "7.18.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
dependencies:
regenerator-runtime "^0.13.4"
@@ -1333,12 +1332,12 @@
"@firebase/util" "1.6.0"
tslib "^2.1.0"
"@firebase/app-compat@0.1.25":
version "0.1.25"
resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.1.25.tgz#4ba8b9209cd956e31a3418343db4a21016d7673d"
integrity sha512-FdCnYwIM3R+OuRE7nrAdVT5oNlvSAHQHN1ictB/gjCFs58lXMCe0DCIRDrA7zxaOFIKeWPtx35ZNXv3EdPFNpQ==
"@firebase/app-compat@0.1.26":
version "0.1.26"
resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.1.26.tgz#46ca3ea0929a28b6d894f6d4fd8d49a0fe2b66ca"
integrity sha512-i5UTq1HZAHuhe7RNjgFSezbow4jVxc2oe3Gndsv+Hdut92f8L0AyssOtdU2iOylLlxbTijewAXXui4FAUzXubw==
dependencies:
"@firebase/app" "0.7.24"
"@firebase/app" "0.7.25"
"@firebase/component" "0.5.14"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.6.0"
@@ -1349,10 +1348,10 @@
resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.7.0.tgz#c9e16d1b8bed1a991840b8d2a725fb58d0b5899f"
integrity sha512-6fbHQwDv2jp/v6bXhBw2eSRbNBpxHcd1NBF864UksSMVIqIyri9qpJB1Mn6sGZE+bnDsSQBC5j2TbMxYsJQkQg==
"@firebase/app@0.7.24":
version "0.7.24"
resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.7.24.tgz#bfdfcbf6145d5d1e8e3fcda7a2044a0a510bf476"
integrity sha512-HIbAhayEykbCez1Rl6pmzAWbIx63Mc9+t3ngWKqZdkMnBNAAJvYaUdx7Krus7O9XHUKNw/gzBUETAEYt93jusA==
"@firebase/app@0.7.25":
version "0.7.25"
resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.7.25.tgz#a5ee629d7f957579d5b25105c70316886a75cfc3"
integrity sha512-OemDA3NZS1oEbAPFlWHeVI8Od26ZHAXUivUWFYIsYrw+YjS7FloltwyHB06Q8LQyPJIBPubGkEuzNTHz32EDCQ==
dependencies:
"@firebase/component" "0.5.14"
"@firebase/logger" "0.3.2"
@@ -1360,17 +1359,17 @@
idb "7.0.1"
tslib "^2.1.0"
"@firebase/auth-compat@0.2.14":
version "0.2.14"
resolved "https://registry.yarnpkg.com/@firebase/auth-compat/-/auth-compat-0.2.14.tgz#ea7dcc38121ce2f2cc9a687830025c8c67d356d8"
integrity sha512-1KSNItrTQzky2d0GVCum6d7Hdj9pfNh9aGTN0uJPNk+th9XHBCy0El8Wx5yk0miiyB3h1evWAXdgnIyNs4kTEQ==
"@firebase/auth-compat@0.2.15":
version "0.2.15"
resolved "https://registry.yarnpkg.com/@firebase/auth-compat/-/auth-compat-0.2.15.tgz#dbd04173657fdf525d9da89433b64cd3c658a1c4"
integrity sha512-Kl8pujKWVBJ+76h4tRsS5xI9Dvk8MVSP6eN82rnEgmCxiUsnVj5Adb/WzvS3p4/l++4mRSAEnlIVxZ2Pyaeirg==
dependencies:
"@firebase/auth" "0.20.1"
"@firebase/auth" "0.20.2"
"@firebase/auth-types" "0.11.0"
"@firebase/component" "0.5.14"
"@firebase/util" "1.6.0"
node-fetch "2.6.7"
selenium-webdriver " 4.1.1"
selenium-webdriver "4.1.2"
tslib "^2.1.0"
"@firebase/auth-interop-types@0.1.6":
@@ -1383,16 +1382,16 @@
resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.11.0.tgz#b9c73c60ca07945b3bbd7a097633e5f78fa9e886"
integrity sha512-q7Bt6cx+ySj9elQHTsKulwk3+qDezhzRBFC9zlQ1BjgMueUOnGMcvqmU0zuKlQ4RhLSH7MNAdBV2znVaoN3Vxw==
"@firebase/auth@0.20.1":
version "0.20.1"
resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.20.1.tgz#a0a4cac5c73f3e496a6444d17ec4e1e63f33233c"
integrity sha512-rffEVZOkcQbQG3zcyhgbJFrE3xIDYtaEIIio5/bMCukitIx0n8okKhb0XKXJ/LGO3zZFRwWh4tyU53t6tHB9uQ==
"@firebase/auth@0.20.2":
version "0.20.2"
resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.20.2.tgz#fbd080a1ae279f81615cf03960fd6b9769f615c6"
integrity sha512-anv2dhHXnlHSuXDuXIoCm/w/JJ+SiQ1TAKgNVYlhfq+yvx9Op8CxfTqcfBwfbIZ1gizw4PNLuk82m8KelsKl6Q==
dependencies:
"@firebase/component" "0.5.14"
"@firebase/logger" "0.3.2"
"@firebase/util" "1.6.0"
node-fetch "2.6.7"
selenium-webdriver " 4.1.1"
selenium-webdriver "4.1.2"
tslib "^2.1.0"
"@firebase/component@0.5.14":
@@ -2105,14 +2104,14 @@
estree-walker "^1.0.1"
picomatch "^2.2.2"
"@sentry/browser@6.19.7":
version "6.19.7"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-6.19.7.tgz#a40b6b72d911b5f1ed70ed3b4e7d4d4e625c0b5f"
integrity sha512-oDbklp4O3MtAM4mtuwyZLrgO1qDVYIujzNJQzXmi9YzymJCuzMLSRDvhY83NNDCRxf0pds4DShgYeZdbSyKraA==
"@sentry/browser@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.1.1.tgz#2d1fe4a81e0d9679aa73c5ae69a0958e4e42b422"
integrity sha512-5AQvStZ+nOP/yxsBmeMZpeGLVtuOgnCNvswKd/c1CJwNw7bDmCE4TQeNKp1C3Gb7lSdBk8ViwUKn0ZpoVQ5MTw==
dependencies:
"@sentry/core" "6.19.7"
"@sentry/types" "6.19.7"
"@sentry/utils" "6.19.7"
"@sentry/core" "7.1.1"
"@sentry/types" "7.1.1"
"@sentry/utils" "7.1.1"
tslib "^1.9.3"
"@sentry/cli@^1.74.4":
@@ -2128,69 +2127,57 @@
proxy-from-env "^1.1.0"
which "^2.0.2"
"@sentry/core@6.19.7":
version "6.19.7"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.19.7.tgz#156aaa56dd7fad8c89c145be6ad7a4f7209f9785"
integrity sha512-tOfZ/umqB2AcHPGbIrsFLcvApdTm9ggpi/kQZFkej7kMphjT+SGBiQfYtjyg9jcRW+ilAR4JXC9BGKsdEQ+8Vw==
"@sentry/core@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.1.1.tgz#85f73f8037eec61e37549998de03a6d3d3c635b5"
integrity sha512-SADdAoG5u1LTJhPN5KPtn5HHmH6r0mr6h2LokuZnhj6/okrAuCIIKOb6Fh8jV7j2VuABvew8+FjJHORxi7D/3Q==
dependencies:
"@sentry/hub" "6.19.7"
"@sentry/minimal" "6.19.7"
"@sentry/types" "6.19.7"
"@sentry/utils" "6.19.7"
"@sentry/hub" "7.1.1"
"@sentry/types" "7.1.1"
"@sentry/utils" "7.1.1"
tslib "^1.9.3"
"@sentry/hub@6.19.7":
version "6.19.7"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.19.7.tgz#58ad7776bbd31e9596a8ec46365b45cd8b9cfd11"
integrity sha512-y3OtbYFAqKHCWezF0EGGr5lcyI2KbaXW2Ik7Xp8Mu9TxbSTuwTe4rTntwg8ngPjUQU3SUHzgjqVB8qjiGqFXCA==
"@sentry/hub@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.1.1.tgz#d6aa54e7bd20a71cebd602701dd29d5d581d391a"
integrity sha512-ASsRVjYDIii6ZTf36JnIYKHWBQBk0P42Tgq324MpyPgaeVDg3saBcyXO5iAtWvY6Vmdi2H4JCVDoir2Zz3Me1w==
dependencies:
"@sentry/types" "6.19.7"
"@sentry/utils" "6.19.7"
"@sentry/types" "7.1.1"
"@sentry/utils" "7.1.1"
tslib "^1.9.3"
"@sentry/minimal@6.19.7":
version "6.19.7"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.19.7.tgz#b3ee46d6abef9ef3dd4837ebcb6bdfd01b9aa7b4"
integrity sha512-wcYmSJOdvk6VAPx8IcmZgN08XTXRwRtB1aOLZm+MVHjIZIhHoBGZJYTVQS/BWjldsamj2cX3YGbGXNunaCfYJQ==
"@sentry/react@^7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.1.1.tgz#13d63384d1a8c916b96ea79b74fc419ba967fb6a"
integrity sha512-Z7cZvXHIWxg7OhOSy4InhrRgQPRNtHsyOkIAHkgwW32JYOGTg1HdqQ5mFUxQLejhU/YqsxVjTK4CI58FATykLw==
dependencies:
"@sentry/hub" "6.19.7"
"@sentry/types" "6.19.7"
tslib "^1.9.3"
"@sentry/react@^6.19.7":
version "6.19.7"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.19.7.tgz#58cc2d6da20f7d3b0df40638dfbbbc86c9c85caf"
integrity sha512-VzJeBg/v41jfxUYPkH2WYrKjWc4YiMLzDX0f4Zf6WkJ4v3IlDDSkX6DfmWekjTKBho6wiMkSNy2hJ1dHfGZ9jA==
dependencies:
"@sentry/browser" "6.19.7"
"@sentry/minimal" "6.19.7"
"@sentry/types" "6.19.7"
"@sentry/utils" "6.19.7"
"@sentry/browser" "7.1.1"
"@sentry/types" "7.1.1"
"@sentry/utils" "7.1.1"
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
"@sentry/tracing@^6.19.7":
version "6.19.7"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.19.7.tgz#54bb99ed5705931cd33caf71da347af769f02a4c"
integrity sha512-ol4TupNnv9Zd+bZei7B6Ygnr9N3Gp1PUrNI761QSlHtPC25xXC5ssSD3GMhBgyQrcvpuRcCFHVNNM97tN5cZiA==
"@sentry/tracing@^7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.1.1.tgz#8996c0913803d3c4a103388c9c0dd2410cca8478"
integrity sha512-MJ+EPGfvPlgbJOcZRoIl6+Oi0oRE2nIi/HP2BPJSKGxXFi2Y09bcZUwfxOH8fkUa465jOGBFdCm+sXcbyExvuw==
dependencies:
"@sentry/hub" "6.19.7"
"@sentry/minimal" "6.19.7"
"@sentry/types" "6.19.7"
"@sentry/utils" "6.19.7"
"@sentry/hub" "7.1.1"
"@sentry/types" "7.1.1"
"@sentry/utils" "7.1.1"
tslib "^1.9.3"
"@sentry/types@6.19.7":
version "6.19.7"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7"
integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==
"@sentry/types@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.1.1.tgz#63aa68e7be36d63cc305d01af9119a4cdb186ae3"
integrity sha512-5N1UMd2SqvUXprcIUMyDEju3H9lJY2oWfWQBGo0lG6Amn/lGAPAYlchg+4vQCLutDQMyd8K9zPwcbKn4u6gHdw==
"@sentry/utils@6.19.7":
version "6.19.7"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.7.tgz#6edd739f8185fd71afe49cbe351c1bbf5e7b7c79"
integrity sha512-z95ECmE3i9pbWoXQrD/7PgkBAzJYR+iXtPuTkpBjDKs86O3mT+PXOT3BAn79w2wkn7/i3vOGD2xVr1uiMl26dA==
"@sentry/utils@7.1.1":
version "7.1.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.1.1.tgz#7174a1439f8970510207fa7587385119397e1718"
integrity sha512-DPRHDf3InfyVgmxToE4Z+AATAR4OVm+wsXDLFGGyncR91CE1x4wLQKOcAJJwX3F0Hz1VHENfmx1DvyYTHOrC/A==
dependencies:
"@sentry/types" "6.19.7"
"@sentry/types" "7.1.1"
tslib "^1.9.3"
"@sentry/webpack-plugin@^1.18.9":
@@ -2243,17 +2230,17 @@
shallowequal "^1.1.0"
unfetch "^4.2.0"
"@stripe/react-stripe-js@^1.8.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@stripe/react-stripe-js/-/react-stripe-js-1.8.0.tgz#b07f1d95e75fe93ac5c33346539ec0972298957d"
integrity sha512-WtF2mIWJKnyAaZledC48NfynFckEseCPG8tybwFgisFxHvLPKrArlLISJOi2cjIY20DkMAkSkdlEljXifJi0ZA==
"@stripe/react-stripe-js@^1.8.1":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@stripe/react-stripe-js/-/react-stripe-js-1.8.1.tgz#b646e3a8775dbb0432e234c6e1d67b19c4c0462d"
integrity sha512-8TpHu87zWSX/PhMMIC2C0jYXKl53kdiHz7bDSfLDs2oIynbV5L9cyoCZ1NuTllrNvFtd28fOqPc4IikZYRRDew==
dependencies:
prop-types "^15.7.2"
"@stripe/stripe-js@^1.29.0":
version "1.29.0"
resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.29.0.tgz#f41e46aee711d1eabcb3bbc77376016a250ec962"
integrity sha512-OsUxk0VLlum8E2d6onlEdKuQcvLMs7qTrOXCnl/BGV3fAm65qr6h3e1IZ5AX4lgUlPRrzRcddSOA5DvkKKYLvg==
"@stripe/stripe-js@^1.31.0":
version "1.31.0"
resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.31.0.tgz#2920bdddc3eabb5734f9ca794824e25b378533ac"
integrity sha512-drlXsNvd/s9S1+Ghja0aDB81N3Wr/6iRAZ7MOzKv7AtKxVdvGpOwESw3z1lHl/Wx/rvPpA7whKdB3W2CnVSArw==
"@surma/rollup-plugin-off-main-thread@^1.1.1":
version "1.4.2"
@@ -2366,12 +2353,12 @@
"@svgr/plugin-svgo" "^5.5.0"
loader-utils "^2.0.0"
"@tanem/react-nprogress@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@tanem/react-nprogress/-/react-nprogress-5.0.0.tgz#5da9b9f5719f40192dea7d7157fe5fa0da64061e"
integrity sha512-ReHCykV4pRIiXsPvbIwZ7Qrxx+940W5AdPsrXeFJlxBWUa9HEMtjXxwwvCoFbnlDF1ZziNvriAvgD/rneM9v3g==
"@tanem/react-nprogress@^5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@tanem/react-nprogress/-/react-nprogress-5.0.1.tgz#338249e907c805e51ab9e6c712ee59130f190f93"
integrity sha512-Q6nVFs4EP88L8b/oVS5MJgEfF7NaWfQ5sGuNXVvxtzPn6EmfCsunG5Oj5pZdpDH0MFSRMvd9cfuL/WpW+S+bvg==
dependencies:
"@babel/runtime" "^7.17.9"
"@babel/runtime" "^7.18.3"
hoist-non-react-statics "^3.3.2"
"@testing-library/cypress@^8.0.2":
@@ -2464,42 +2451,6 @@
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803"
integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==
"@types/d3-color@^2":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-2.0.3.tgz#8bc4589073c80e33d126345542f588056511fe82"
integrity sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w==
"@types/d3-interpolate@^2.0.0":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-2.0.2.tgz#78eddf7278b19e48e8652603045528d46897aba0"
integrity sha512-lElyqlUfIPyWG/cD475vl6msPL4aMU7eJvx1//Q177L8mdXoVPFl1djIESF2FKnc0NyaHvQlJpWwKJYwAhUoCw==
dependencies:
"@types/d3-color" "^2"
"@types/d3-path@^2":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-2.0.1.tgz#ca03dfa8b94d8add97ad0cd97e96e2006b4763cb"
integrity sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw==
"@types/d3-scale@^3.0.0":
version "3.3.2"
resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-3.3.2.tgz#18c94e90f4f1c6b1ee14a70f14bfca2bd1c61d06"
integrity sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==
dependencies:
"@types/d3-time" "^2"
"@types/d3-shape@^2.0.0":
version "2.1.3"
resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-2.1.3.tgz#35d397b9e687abaa0de82343b250b9897b8cacf3"
integrity sha512-HAhCel3wP93kh4/rq+7atLdybcESZ5bRHDEZUojClyZWsRuEMo3A52NGYJSh48SxfxEU6RZIVbZL2YFZ2OAlzQ==
dependencies:
"@types/d3-path" "^2"
"@types/d3-time@^2":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-2.1.1.tgz#743fdc821c81f86537cbfece07093ac39b4bc342"
integrity sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg==
"@types/eslint@^7.28.2":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78"
@@ -3048,10 +2999,10 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
mime-types "~2.1.24"
negotiator "0.6.2"
ace-builds@^1.4.14:
version "1.4.14"
resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.14.tgz#2c41ccbccdd09e665d3489f161a20baeb3a3c852"
integrity sha512-NBOQlm9+7RBqRqZwimpgquaLeTJFayqb9UEPtTkpC3TkkwDnlsT/TwsCC0svjt9kEZ6G9mH5AEOHSz6Q/HrzQQ==
ace-builds@^1.5.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.5.3.tgz#05f81d3464a9ea19696e5e6fd0f924d37dab442f"
integrity sha512-WN5BKR2aTSuBmisO8jo3Fytk6sOmJGki82v/Boeic81IgYN8pFHNkXq2anDF0XkmfDWMqLbRoW9sjc/GtKzQbQ==
acorn-globals@^6.0.0:
version "6.0.0"
@@ -3210,10 +3161,10 @@ ansi-styles@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
antd@^4.20.5:
version "4.20.5"
resolved "https://registry.yarnpkg.com/antd/-/antd-4.20.5.tgz#ac6a35f8ed2703dc94ac4661d5d10a88912be457"
integrity sha512-43bV1y9pXsccMBmThEPnk0FdPPl5oqEZs8wneKEtdUm6RusmAHxxWnWGgoqdVsEP2ASRDcwyhECTLu2oPw88ug==
antd@^4.21.0:
version "4.21.0"
resolved "https://registry.yarnpkg.com/antd/-/antd-4.21.0.tgz#44e65d08528b28664d3ef00a539964c2133f3338"
integrity sha512-p8R5scejlWjAIF/NoJ5JF5OMjLbAlCA7u85cNwbtRQOP+14KQDXZyHMT4C5oc9nhz7xxD/Bry6HhPkUmsRuP7Q==
dependencies:
"@ant-design/colors" "^6.0.0"
"@ant-design/icons" "^4.7.0"
@@ -3225,36 +3176,36 @@ antd@^4.20.5:
lodash "^4.17.21"
memoize-one "^6.0.0"
moment "^2.29.2"
rc-cascader "~3.5.0"
rc-cascader "~3.6.0"
rc-checkbox "~2.3.0"
rc-collapse "~3.1.0"
rc-dialog "~8.8.1"
rc-collapse "~3.3.0"
rc-dialog "~8.8.2"
rc-drawer "~4.4.2"
rc-dropdown "~3.5.0"
rc-dropdown "~4.0.0"
rc-field-form "~1.26.1"
rc-image "~5.6.0"
rc-input "~0.0.1-alpha.5"
rc-input-number "~7.3.0"
rc-mentions "~1.7.0"
rc-menu "~9.5.5"
rc-mentions "~1.8.0"
rc-menu "~9.6.0"
rc-motion "^2.5.1"
rc-notification "~4.6.0"
rc-pagination "~3.1.9"
rc-picker "~2.6.4"
rc-progress "~3.2.1"
rc-pagination "~3.1.16"
rc-picker "~2.6.8"
rc-progress "~3.3.2"
rc-rate "~2.9.0"
rc-resize-observer "^1.2.0"
rc-segmented "~2.1.0 "
rc-segmented "~2.1.0"
rc-select "~14.1.1"
rc-slider "~10.0.0"
rc-steps "~4.1.0"
rc-switch "~3.2.0"
rc-table "~7.24.0"
rc-tabs "~11.13.0"
rc-tabs "~11.16.0"
rc-textarea "~0.3.0"
rc-tooltip "~5.1.1"
rc-tree "~5.5.0"
rc-tree-select "~5.3.0"
rc-tree "~5.6.4"
rc-tree-select "~5.4.0"
rc-trigger "^5.2.10"
rc-upload "~4.3.0"
rc-util "^5.20.0"
@@ -6630,20 +6581,20 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
firebase@^9.8.1:
version "9.8.1"
resolved "https://registry.yarnpkg.com/firebase/-/firebase-9.8.1.tgz#502e186078d69d72ab8d4f1b5befa287e0858276"
integrity sha512-VyM+3ijzB1Q24b9v6HzVOB0bXNy0a/maOZlmv2P8M29VXfrS/npo6zntNiOEtcjrCoItZIuWFH4oDGiYkPHxbg==
firebase@^9.8.2:
version "9.8.2"
resolved "https://registry.yarnpkg.com/firebase/-/firebase-9.8.2.tgz#ce2b8a6d41036b323d963280c345b03f4bd4df52"
integrity sha512-cVPpiR18vsLuGWAAVkVhNO6mYsEgYBqawvMI2zxKo2FCtneyBgMwOyWKI8VyCmL5ze5p5QJTPjkoatM6rZkd0Q==
dependencies:
"@firebase/analytics" "0.7.9"
"@firebase/analytics-compat" "0.1.10"
"@firebase/app" "0.7.24"
"@firebase/app" "0.7.25"
"@firebase/app-check" "0.5.8"
"@firebase/app-check-compat" "0.2.8"
"@firebase/app-compat" "0.1.25"
"@firebase/app-compat" "0.1.26"
"@firebase/app-types" "0.7.0"
"@firebase/auth" "0.20.1"
"@firebase/auth-compat" "0.2.14"
"@firebase/auth" "0.20.2"
"@firebase/auth-compat" "0.2.15"
"@firebase/database" "0.13.0"
"@firebase/database-compat" "0.2.0"
"@firebase/firestore" "3.4.9"
@@ -7412,10 +7363,10 @@ i18next-browser-languagedetector@^6.1.4:
dependencies:
"@babel/runtime" "^7.14.6"
i18next@^21.8.2:
version "21.8.2"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.8.2.tgz#b2d9fd7feafc1a9929651c24a5c17ba703552bbc"
integrity sha512-H/oQvA/OXcqurXHemlyDwdIzr9GHYg5/xBuDeFXTXJGMOJFH0ke1wgbsDjFFk2noD4cJfzicVPpUtp39Z+OzgQ==
i18next@^21.8.9:
version "21.8.9"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.8.9.tgz#c79edd5bba61e0a0d5b43a93d52e2d13a526de82"
integrity sha512-PY9a/8ADVmnju1tETeglbbVQi+nM5pcJQWm9kvKMTE3GPgHHtpDsHy5HQ/hccz2/xtW7j3vuso23JdQSH0EttA==
dependencies:
"@babel/runtime" "^7.17.2"
@@ -8721,12 +8672,12 @@ json5@^2.1.2:
dependencies:
minimist "^1.2.5"
jsoneditor@^9.7.4:
version "9.7.4"
resolved "https://registry.yarnpkg.com/jsoneditor/-/jsoneditor-9.7.4.tgz#00ddbd99950f8551e0d5e5d0bb9c69584beabda4"
integrity sha512-Ln+WdNKLwaqaA64RgI8SI5PRA5RpcVougDNF9ILVzgyj3gDY9ev4WIQcgjoEoQC3Ymml3V7scQ08sDKiNSjo9Q==
jsoneditor@^9.8.0:
version "9.8.0"
resolved "https://registry.yarnpkg.com/jsoneditor/-/jsoneditor-9.8.0.tgz#08db81ccf6f6e9fdff8691e42c4fb62d3efdd6ad"
integrity sha512-q1ekwYizbSAny0/UAEOzLviVCyBS5XFGwM/EUNf9KnfB1MRSDmJDWjt4lAqMVz1TUV5O/l3J4/WzUSLQh2tZjw==
dependencies:
ace-builds "^1.4.14"
ace-builds "^1.5.3"
ajv "^6.12.6"
javascript-natural-sort "^0.7.1"
jmespath "^0.16.0"
@@ -8901,10 +8852,10 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
libphonenumber-js@^1.9.53:
version "1.9.53"
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.9.53.tgz#f4f3321f8fb0ee62952c2a8df4711236d2626088"
integrity sha512-3cuMrA2CY3TbKVC0wKye5dXYgxmVVi4g13gzotprQSguFHMqf0pIrMM2Z6ZtMsSWqvtIqi5TuQhGjMhxz0O9Mw==
libphonenumber-js@^1.10.6:
version "1.10.6"
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.6.tgz#a453efe9d692cc9457abb20a712dec34472a7164"
integrity sha512-CIjT100/SmntsUjsLVs2t3ufeN4KdNXUxhD07tH153pdbaCWuAjv0jK/gPuywR3IImB/U/MQM+x9RfhMs5XZiA==
lie@~3.3.0:
version "3.3.0"
@@ -10233,10 +10184,10 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
phone@^3.1.17:
version "3.1.17"
resolved "https://registry.yarnpkg.com/phone/-/phone-3.1.17.tgz#4cf6e8f3738ba35d5261979c98823005af459792"
integrity sha512-62vV6eQGP15iGzCPS+ugaEzuvxz6qWBUlKnrAqzKtGUMT3HlLD7vpoi9NA+Ty7M3KvnKI+idddOgufqu1XAeEA==
phone@^3.1.20:
version "3.1.20"
resolved "https://registry.yarnpkg.com/phone/-/phone-3.1.20.tgz#8b7b326c182ce70eaf7884eae3eee0cd6e7ee7b3"
integrity sha512-RR23OELEPPMWX3fz//ajrElna+9Y10iqBTL6W1SXHeQPnvpVd8ey2tYYcIH8lB70QL3QiTMDiSIwaINcde6SOw==
picocolors@^0.2.1:
version "0.2.1"
@@ -11332,16 +11283,16 @@ rc-align@^4.0.0:
rc-util "^5.3.0"
resize-observer-polyfill "^1.5.1"
rc-cascader@~3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.5.0.tgz#a49b632bc2d0c8ef31b212c8ddd0bea346e64877"
integrity sha512-rpXnWCfvk7Frh2dBzMoA0c7i0nn6aJU7L2NZo8R8pNkrT0sKgytQSpdtPWP+Pq8IkvwbEd8BU8Z8OnOljcqgZg==
rc-cascader@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.6.0.tgz#7db0d373edf4c276bba4b68b7de57fad1486c908"
integrity sha512-p9qwt8E8ZICzPIzyfXF5y7/lbJhRowFj8YhWpdytMomHUZ568duFNwA4H5QVqdC6hg/HIV1YEawOE5jlxSpeww==
dependencies:
"@babel/runtime" "^7.12.5"
array-tree-filter "^2.1.0"
classnames "^2.3.1"
rc-select "~14.1.0"
rc-tree "~5.5.0"
rc-tree "~5.6.3"
rc-util "^5.6.1"
rc-checkbox@~2.3.0:
@@ -11352,10 +11303,10 @@ rc-checkbox@~2.3.0:
"@babel/runtime" "^7.10.1"
classnames "^2.2.1"
rc-collapse@~3.1.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.1.2.tgz#76028a811b845d03d9460ccc409c7ea8ad09db14"
integrity sha512-HujcKq7mghk/gVKeI6EjzTbb8e19XUZpakrYazu1MblEZ3Hu3WBMSN4A3QmvbF6n1g7x6lUlZvsHZ5shABWYOQ==
rc-collapse@~3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.3.0.tgz#ecde33a06ca53c6c672c6a46c701052b88723950"
integrity sha512-nkxjhpYAAwEVbBvZ/qoatLecD0PpRtQ5ja9G+FP1QmsWhs/4VCruhjvRdSpMn9vfluKUnePe3PEy8eeqTeuE0g==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "2.x"
@@ -11363,7 +11314,7 @@ rc-collapse@~3.1.0:
rc-util "^5.2.1"
shallowequal "^1.1.0"
rc-dialog@~8.8.0, rc-dialog@~8.8.1:
rc-dialog@~8.8.0:
version "8.8.1"
resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-8.8.1.tgz#cd8897fbee1de0eab6d237a6abe1e4db8d09dd72"
integrity sha512-7M1WKZCjfIABKEaJVskdYvb80z+RX7I11PeSjPVfLOOaJAmIepvDEd0alBtOZvOL3fZFWlMs4JVZtp9LZgONxA==
@@ -11373,6 +11324,16 @@ rc-dialog@~8.8.0, rc-dialog@~8.8.1:
rc-motion "^2.3.0"
rc-util "^5.21.0"
rc-dialog@~8.8.2:
version "8.8.2"
resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-8.8.2.tgz#2212c63b940cf0ca46f961803f83007966cc6582"
integrity sha512-n1waqBDDKqCCcPCDGycahfawF00WqgtXTXUwxrLStUpfQAo7nzkAvTq9voT78X2qN83UYvrMg1TWCuTueBp+sg==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "^2.2.6"
rc-motion "^2.3.0"
rc-util "^5.21.0"
rc-drawer@~4.4.2:
version "4.4.3"
resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-4.4.3.tgz#2094937a844e55dc9644236a2d9fba79c344e321"
@@ -11382,14 +11343,14 @@ rc-drawer@~4.4.2:
classnames "^2.2.6"
rc-util "^5.7.0"
rc-dropdown@~3.5.0:
version "3.5.2"
resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.5.2.tgz#2f1f4eeb36c07fb67cd599c0cb8e861da3de5527"
integrity sha512-Ty4LsXjkspZuFJSRx3blCLLCDicXM5qds6F1odgEa+jcjC+OJKHQGnvE4FqtoljPaqWm4wG78pbgXH6Ddh2DkA==
rc-dropdown@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.0.1.tgz#f65d9d3d89750241057db59d5a75e43cd4576b68"
integrity sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g==
dependencies:
"@babel/runtime" "^7.10.1"
"@babel/runtime" "^7.18.3"
classnames "^2.2.6"
rc-trigger "^5.0.4"
rc-trigger "^5.3.1"
rc-util "^5.17.0"
rc-field-form@~1.26.1:
@@ -11429,22 +11390,22 @@ rc-input@~0.0.1-alpha.5:
classnames "^2.2.1"
rc-util "^5.18.1"
rc-mentions@~1.7.0:
version "1.7.1"
resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.7.1.tgz#480ad04af4460ee01b6ccd9137fcea23067aa9be"
integrity sha512-JbCS9bTqt6BYN2vfTPythlScLuc42rIlX85n7975RnkfawXlJjskHOlR3o8EpD4asl4KuA2jKTy0dj39DtSVqg==
rc-mentions@~1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.8.0.tgz#4c0c41605064303f7aedec47d4d07e0bbfcc2dc3"
integrity sha512-ch7yfMMvx2UXy+EvE4axm0Vp6VlVZ30WLrZtLtV/Eb1ty7rQQRzNzCwAHAMyw6tNKTMs9t9sF68AVjAzQ0rvJw==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "^2.2.6"
rc-menu "~9.5.1"
rc-menu "~9.6.0"
rc-textarea "^0.3.0"
rc-trigger "^5.0.4"
rc-util "^5.0.1"
rc-menu@~9.5.1, rc-menu@~9.5.5:
version "9.5.5"
resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.5.5.tgz#aa2f151d4191ed089dc1a8141fe365c9b77d61a9"
integrity sha512-wj2y2BAKwSMyWXO3RBf9sNN5V+DFWxFl45Ma6qQEHA5nwwh7p07bNgc6AAJc+L1+LAz+rWz3AU8PYyT17hMHCw==
rc-menu@~9.6.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.6.0.tgz#3263a729a81ae49cfdadee112e97d3c702922829"
integrity sha512-d26waws42U/rVwW/+rOE2FN9pX6wUc9bDy38vVQYoie6gE85auWIpl5oChGlnW6nE2epnTwUsgWl8ipOPgmnUA==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "2.x"
@@ -11492,18 +11453,18 @@ rc-overflow@^1.0.0, rc-overflow@^1.2.0:
rc-resize-observer "^1.0.0"
rc-util "^5.5.1"
rc-pagination@~3.1.9:
version "3.1.14"
resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.1.14.tgz#1f7d0342edb80dca0989e4ddbe937b1d4657d88d"
integrity sha512-tcugvxrtPiVU00Uh0IwC8NIUlxa4KtA9pXcaMNJdSHeO2uQqVkHEwllsULTAWRF3zRV2ozo2weP/DRKIUrX+Zg==
rc-pagination@~3.1.16:
version "3.1.16"
resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.1.16.tgz#b0082108cf027eded18ed61d818d31897c343e81"
integrity sha512-GFcHXJ7XxeJDf9B+ndP4PRDt46maSSgYhiwofBMiIGKIlBhJ0wfu8DMCEvaWJJLpI2u4Gb6zF1dHpiqPFrosPg==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "^2.2.1"
rc-picker@~2.6.4:
version "2.6.5"
resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-2.6.5.tgz#a7cf8eb0723ec81e379c784c4b798b7fe076dd8c"
integrity sha512-4pcg0PgEz4YXBfdwMuHIKaRWaADm3k3g0NtoPIgeGM+VVeOBdUowTx0YSXnT8mQEXcE9lWXX+ZX3biAzQwDM1w==
rc-picker@~2.6.8:
version "2.6.9"
resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-2.6.9.tgz#2f2f82c5340adbe3b30875a25e015c120eb88c9c"
integrity sha512-yH3UYXCADf7REtOAB5cwe1cyFKtB0p204RCN8JdZGG4uuSOZ1IPTkk/GJS6HOpxspZeJCLGzzajuQMDwck9dsw==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "^2.2.1"
@@ -11514,10 +11475,10 @@ rc-picker@~2.6.4:
rc-util "^5.4.0"
shallowequal "^1.1.0"
rc-progress@~3.2.1:
version "3.2.4"
resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.2.4.tgz#4036acdae2566438545bc4df2203248babaf7549"
integrity sha512-M9WWutRaoVkPUPIrTpRIDpX0SPSrVHzxHdCRCbeoBFrd9UFWTYNWRlHsruJM5FH1AZI+BwB4wOJUNNylg/uFSw==
rc-progress@~3.3.2:
version "3.3.3"
resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.3.3.tgz#eb9bffbacab1534f2542f9f6861ce772254362b1"
integrity sha512-MDVNVHzGanYtRy2KKraEaWeZLri2ZHWIRyaE1a9MQ2MuJ09m+Wxj5cfcaoaR6z5iRpHpA59YeUxAlpML8N4PJw==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "^2.2.6"
@@ -11572,7 +11533,7 @@ rc-scroll-anim@^2.7.6:
react-lifecycles-compat "^3.0.4"
tween-functions "1.x"
"rc-segmented@~2.1.0 ":
rc-segmented@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.1.0.tgz#0e0afe646c1a0e44a0e18785f518c42633ec8efc"
integrity sha512-hUlonro+pYoZcwrH6Vm56B2ftLfQh046hrwif/VwLIw1j3zGt52p5mREBwmeVzXnSwgnagpOpfafspzs1asjGw==
@@ -11635,15 +11596,15 @@ rc-table@~7.24.0:
rc-util "^5.14.0"
shallowequal "^1.1.0"
rc-tabs@~11.13.0:
version "11.13.0"
resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.13.0.tgz#083eed578f8ad02dc0d462d73da487fe32e3a573"
integrity sha512-aUw1Pq0B1a2zGX4o/m3yrQycZcCLgDp6gKwn8IAU07q148RRONsVGxi0oLVVe5SE51kOB+j0bk1RX43ZBdZNgA==
rc-tabs@~11.16.0:
version "11.16.0"
resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.16.0.tgz#12447069ea1dc480c729e1e40661cfbd46ac4efe"
integrity sha512-CIDPv3lHaXSHTJevmFP2eHoD3Hq9psfKbOZYf6D4FYPACloNGHpz44y3RGeJgataQ7omFLrGBm3dOBMUki87tA==
dependencies:
"@babel/runtime" "^7.11.2"
classnames "2.x"
rc-dropdown "~3.5.0"
rc-menu "~9.5.1"
rc-dropdown "~4.0.0"
rc-menu "~9.6.0"
rc-resize-observer "^1.0.0"
rc-util "^5.5.0"
@@ -11665,27 +11626,27 @@ rc-tooltip@^5.0.1, rc-tooltip@~5.1.1:
"@babel/runtime" "^7.11.2"
rc-trigger "^5.0.0"
rc-tree-select@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.3.0.tgz#6edd19d1066ad2bfa212f043c3ff701b93828026"
integrity sha512-UN6CUBulmch+CsihnJ73+DtWijEB1hVTC8sdVxq6E0teVAkHQZUvDj+cwZShtShAKvWwXy73PZ1hIHEUrmVcKw==
rc-tree-select@~5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.4.0.tgz#c94b961aca68689f5ee3a43e33881cf693d195ef"
integrity sha512-reRbOqC7Ic/nQocJAJeCl4n6nJUY3NoqiwRXKvhjgZJU7NGr9vIccXEsY+Lghkw5UMpPoxGsIJB0jiAvM18XYA==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "2.x"
rc-select "~14.1.0"
rc-tree "~5.5.0"
rc-tree "~5.6.1"
rc-util "^5.16.1"
rc-tree@~5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.5.0.tgz#ba7c8aea2ad29f40a9c7168e490300f7a50c0f22"
integrity sha512-vpKeFsDyj7weik8UPseCTaSNAPt939qn1dQd8goSbRDajbjJEja0v/WFXyRhOiF1HLemNTfqMz4MYc9qlqyNXg==
rc-tree@~5.6.1, rc-tree@~5.6.3, rc-tree@~5.6.4:
version "5.6.5"
resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.6.5.tgz#1947337fc48f3fe20fabaafb1aed3e4ff1ce71b4"
integrity sha512-Bnyen46B251APyRZ9D/jYeTnSqbSEvK2AkU5B4vWkNYgUJNPrxO+VMgcDRedP/8N7YcsgdDT9hxqVvNOq7oCAQ==
dependencies:
"@babel/runtime" "^7.10.1"
classnames "2.x"
rc-motion "^2.0.1"
rc-util "^5.16.1"
rc-virtual-list "^3.4.2"
rc-virtual-list "^3.4.8"
rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10:
version "5.2.10"
@@ -11698,6 +11659,17 @@ rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10:
rc-motion "^2.0.0"
rc-util "^5.5.0"
rc-trigger@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.3.1.tgz#acafadf3eaf384e7f466c303bfa0f34c8137d7b8"
integrity sha512-5gaFbDkYSefZ14j2AdzucXzlWgU2ri5uEjkHvsf1ynRhdJbKxNOnw4PBZ9+FVULNGFiDzzlVF8RJnR9P/xrnKQ==
dependencies:
"@babel/runtime" "^7.18.3"
classnames "^2.2.6"
rc-align "^4.0.0"
rc-motion "^2.0.0"
rc-util "^5.19.2"
rc-tween-one@^1.2.5:
version "1.8.1"
resolved "https://registry.yarnpkg.com/rc-tween-one/-/rc-tween-one-1.8.1.tgz#5b3b464b9bf2c369efc16c816cccf57bc16ae253"
@@ -11749,6 +11721,15 @@ rc-util@^5.17.0, rc-util@^5.18.1:
react-is "^16.12.0"
shallowequal "^1.1.0"
rc-util@^5.19.2:
version "5.21.5"
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.21.5.tgz#6e2a5699f820ba915f43f11a4b7dfb0b0672d0fa"
integrity sha512-ip7HqX37Cy/RDl9MlrFp+FbcKnsWZ22sF5MS5eSpYLtg5MpC0TMqGb5ukBatoOhgjnLL+eJGR6e7YAJ/dhK09A==
dependencies:
"@babel/runtime" "^7.18.3"
react-is "^16.12.0"
shallowequal "^1.1.0"
rc-util@^5.20.0, rc-util@^5.20.1, rc-util@^5.21.0:
version "5.21.4"
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.21.4.tgz#61e24ad297f679ca0796b618a3ef30eca959d904"
@@ -11767,14 +11748,14 @@ rc-virtual-list@^3.2.0:
rc-resize-observer "^1.0.0"
rc-util "^5.0.7"
rc-virtual-list@^3.4.2:
version "3.4.4"
resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.4.tgz#be42832edecdcffc56260df131e437a2e0473bca"
integrity sha512-Zb2h0B+ZhA/aysZNEUkQYaqx0hd8hq7WMIUegII+1KfFjh99GtHZ4ZMWB+SlpdVadskKJZzUWWzIq5N1mE6AVg==
rc-virtual-list@^3.4.8:
version "3.4.8"
resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.8.tgz#c24c10c6940546b7e2a5e9809402c6716adfd26c"
integrity sha512-qSN+Rv4i/E7RCTvTMr1uZo7f3crJJg/5DekoCagydo9zsXrxj07zsFSxqizqW+ldGA16lwa8So/bIbV9Ofjddg==
dependencies:
classnames "^2.2.6"
rc-resize-observer "^1.0.0"
rc-util "^5.0.7"
rc-util "^5.15.0"
react-app-polyfill@^2.0.0:
version "2.0.0"
@@ -11879,10 +11860,10 @@ react-dom@^17.0.2:
object-assign "^4.1.1"
scheduler "^0.20.2"
react-drag-listview@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/react-drag-listview/-/react-drag-listview-0.2.0.tgz#b672d1d98ec98a642472c678af2cbfa7e513e277"
integrity sha512-YHVxC71QIfakFFGA3ejpPe3fcQYomXBA5M3Z2nEBs7JScfc+dx3kq4xB8dzPHy240lI5213nemoogiL7ZCNsEg==
react-drag-listview@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/react-drag-listview/-/react-drag-listview-0.2.1.tgz#922fa3c37ed4d84f2a349a93b01a623ac565f7dc"
integrity sha512-LFR/14CpmiieITCywfe2rxAg1szAqsynpqgquSgirT9cansDwJdpqAVEjzIRSEql/DRIYTvBYLvMI/HJcVeU4w==
dependencies:
babel-runtime "^6.26.0"
prop-types "^15.5.8"
@@ -11924,19 +11905,19 @@ react-grid-layout@^1.3.4:
react-draggable "^4.0.0"
react-resizable "^3.0.4"
react-i18next@^11.16.9:
version "11.16.9"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.16.9.tgz#890cdac0c49120e075d6c520b43dbad3f91bd2df"
integrity sha512-euXxWvcEAvsY7ZVkwx9ztCq4butqtsGHEkpkuo0RMj8Ru09IF9o2KxCyN+zyv51Nr0aBh/elaTIiR6fMb8YfVg==
react-i18next@^11.17.0:
version "11.17.0"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.17.0.tgz#44a0689dac7903352733e40303b743fa465eb797"
integrity sha512-ewq2S4bVUTRqOMAdM/XvzCn9xUPIryzeBQRghmJ8lC6VI/8Kp7z1GwoLyt8j7GB2ywhN2SjPk7LU4sHzVeu7aw==
dependencies:
"@babel/runtime" "^7.14.5"
html-escaper "^2.0.2"
html-parse-stringify "^3.0.1"
react-icons@^4.3.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.3.1.tgz#2fa92aebbbc71f43d2db2ed1aed07361124e91ca"
integrity sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==
react-icons@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703"
integrity sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg==
react-images@^0.5.16:
version "0.5.19"
@@ -12265,14 +12246,11 @@ recharts-scale@^0.4.4:
dependencies:
decimal.js-light "^2.4.1"
recharts@^2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.1.9.tgz#a52d411a7d822d118f7754cfc9c50db8fab46fb9"
integrity sha512-VozH5uznUvGqD7n224FGj7cmMAenlS0HPCs+7r2HeeHiQK6un6z0CTZfWVAB860xbcr4m+BN/EGMPZmYWd34Rg==
recharts@^2.1.10:
version "2.1.10"
resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.1.10.tgz#4253f4354fcb9328a162f66d7c5c8d33ef7741db"
integrity sha512-me6c8m2Gs88X/nuM2gDSTDIhpSLNMbiTrlE4Cu53hjZNegT3g3xLlTrbYSAQuBCFWuWJAZXCmEuMr6AwizLyaA==
dependencies:
"@types/d3-interpolate" "^2.0.0"
"@types/d3-scale" "^3.0.0"
"@types/d3-shape" "^2.0.0"
classnames "^2.2.5"
d3-interpolate "^2.0.0"
d3-scale "^3.0.0"
@@ -12477,10 +12455,10 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
reselect@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6"
integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==
reselect@^4.1.6:
version "4.1.6"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.6.tgz#19ca2d3d0b35373a74dc1c98692cdaffb6602656"
integrity sha512-ZovIuXqto7elwnxyXbBtCPo9YFEr3uJqj2rRbcOOog1bmu2Ag85M4hixSwFWyaBMKXNgvPaJ9OSu9SkBPIeJHQ==
resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1:
version "1.5.1"
@@ -12828,10 +12806,10 @@ select-hose@^2.0.0:
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
"selenium-webdriver@ 4.1.1":
version "4.1.1"
resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.1.1.tgz#da083177d811f36614950e809e2982570f67d02e"
integrity sha512-Fr9e9LC6zvD6/j7NO8M1M/NVxFX67abHcxDJoP5w2KN/Xb1SyYLjMVPGgD14U2TOiKe4XKHf42OmFw9g2JgCBQ==
selenium-webdriver@4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.1.2.tgz#d463b4335632d2ea41a9e988e435a55dc41f5314"
integrity sha512-e4Ap8vQvhipgBB8Ry9zBiKGkU6kHKyNnWiavGGLKkrdW81Zv7NVMtFOL/j3yX0G8QScM7XIXijKssNd4EUxSOw==
dependencies:
jszip "^3.6.0"
tmp "^0.2.1"
@@ -13088,10 +13066,10 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
socket.io-client@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.0.tgz#3858b6183bab31c5c4eaf3efd0fa50840ebb4504"
integrity sha512-HW61c1G7OrYGxaI79WRn17+b03iBCdvhBj4iqyXHBoL5M8w2MSO/vChsjA93knG4GYEai1/vbXWJna9dzxXtSg==
socket.io-client@^4.5.1:
version "4.5.1"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.1.tgz#cab8da71976a300d3090414e28c2203a47884d84"
integrity sha512-e6nLVgiRYatS+AHXnOnGi4ocOpubvOUCGhyWw8v+/FxW8saHkinG6Dfhi9TU0Kt/8mwJIAASxvw6eujQmjdZVA==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.2"
@@ -13961,7 +13939,7 @@ tryer@^1.0.1:
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
ts-invariant@^0.10.0:
ts-invariant@^0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c"
integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==
@@ -14335,11 +14313,6 @@ use-memo-one@^1.1.1:
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20"
integrity sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==
use-sync-external-store@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz#3343c3fe7f7e404db70f8c687adf5c1652d34e82"
integrity sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ==
use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
@@ -15196,10 +15169,10 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
zen-observable-ts@^1.2.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.3.tgz#c2f5ccebe812faf0cfcde547e6004f65b1a6d769"
integrity sha512-hc/TGiPkAWpByykMwDcem3SdUgA4We+0Qb36bItSuJC9xD0XVBZoFHYoadAomDSNf64CG8Ydj0Qb8Od8BUWz5g==
zen-observable-ts@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58"
integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==
dependencies:
zen-observable "0.8.15"

View File

@@ -10,6 +10,9 @@
- function:
schema: public
name: search_exportlog
- function:
schema: public
name: search_inventory
- function:
schema: public
name: search_jobs

View File

@@ -402,6 +402,14 @@
- name: jobline
using:
foreign_key_constraint_on: joblineid
array_relationships:
- name: inventories
using:
foreign_key_constraint_on:
column: billlineid
table:
schema: public
name: inventory
insert_permissions:
- role: user
permission:
@@ -541,6 +549,13 @@
table:
schema: public
name: exportlog
- name: inventories
using:
foreign_key_constraint_on:
column: consumedbybillid
table:
schema: public
name: inventory
- name: parts_orders
using:
foreign_key_constraint_on:
@@ -751,6 +766,13 @@
table:
schema: public
name: exportlog
- name: inventories
using:
foreign_key_constraint_on:
column: shopid
table:
schema: public
name: inventory
- name: jobs
using:
foreign_key_constraint_on:
@@ -2112,6 +2134,118 @@
- active:
_eq: 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
- comment
- consumedbybillid
- created_at
- id
- joblineid
- line_desc
- manualinvoicenumber
- manualvendor
- quantity
- shopid
- updated_at
backend_only: false
select_permissions:
- role: user
permission:
columns:
- actual_cost
- actual_price
- billlineid
- comment
- consumedbybillid
- created_at
- id
- joblineid
- line_desc
- manualinvoicenumber
- manualvendor
- 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
- comment
- consumedbybillid
- created_at
- id
- joblineid
- line_desc
- manualinvoicenumber
- manualvendor
- quantity
- shopid
- updated_at
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
check: null
delete_permissions:
- role: user
permission:
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
- table:
schema: public
name: ioevents
@@ -2211,6 +2345,13 @@
table:
schema: public
name: billlines
- name: inventories
using:
foreign_key_constraint_on:
column: joblineid
table:
schema: public
name: inventory
- name: parts_order_lines
using:
foreign_key_constraint_on:

View File

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

View File

@@ -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;

View File

@@ -0,0 +1 @@
alter table "public"."inventory" drop constraint "inventory_billid_fkey";

View File

@@ -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;

View File

@@ -0,0 +1 @@
alter table "public"."inventory" drop constraint "inventory_shopid_fkey";

View File

@@ -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;

View File

@@ -0,0 +1 @@
alter table "public"."inventory" rename column "billlineid" to "billid";

View File

@@ -0,0 +1 @@
alter table "public"."inventory" rename column "billid" to "billlineid";

View File

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

View File

@@ -0,0 +1,2 @@
alter table "public"."inventory" add column "consumedbybillid" uuid
null;

View File

@@ -0,0 +1 @@
alter table "public"."inventory" drop constraint "inventory_consumedbybillid_fkey";

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "public"."inventory_consumedbybillid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "inventory_consumedbybillid" on
"public"."inventory" using btree ("consumedbybillid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "public"."inventory_shopididx";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "inventory_shopididx" on
"public"."inventory" using btree ("shopid");

View 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$;

View 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$;

View 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$;

View 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$;

View 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$;

View 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$;

View 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$;

View 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$;

View File

@@ -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);

View File

@@ -0,0 +1 @@
create index inventory_linedescidx on inventory(line_desc);

View File

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

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