Compare commits

...

58 Commits

Author SHA1 Message Date
Allan Carr
e8cda88a33 IO-3075 Crisp in Rome Online
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-28 16:17:11 -08:00
Allan Carr
71a74c5437 Merged in hotfix/2025-01-23 (pull request #2082)
Hotfix/2025 01 23
2025-01-23 23:21:28 +00:00
Allan Carr
6fe4d982f5 Merged in feature/IO-3108-Job-Totals-USA-PASL (pull request #2081)
Feature/IO-3108 Job Totals USA PASL
2025-01-23 23:20:44 +00:00
Allan Carr
5ec032d8d6 IO-3108 Remove Console Log
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-23 15:21:10 -08:00
Allan Carr
2718a66fb0 IO-3108 Adjust Initial Values/FieldValues
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-23 15:19:33 -08:00
Allan Carr
4c737371e3 IO-3108 Adjust Initial Values
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-23 14:49:51 -08:00
Dave Richer
ee7a3d0bdf Merged in hotfix/2025-01-23 (pull request #2079)
Hotfix/2025 01 23
2025-01-23 21:01:43 +00:00
Allan Carr
181af581e5 Merged in feature/IO-3108-Job-Totals-USA-PASL (pull request #2078)
IO-3108 Job Totals USA PASL

Approved-by: Dave Richer
2025-01-23 21:00:43 +00:00
Dave Richer
11e2f5d83d feature/IO-3108-Job-Totals-USA-PASL - Fix submit button for Product Fruits reasons, to go into hotfix 2025-01-23 12:57:10 -08:00
Allan Carr
cbc723fa38 IO-3108 Job Totals USA PASL
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-23 12:05:35 -08:00
Allan Carr
95c310119f Merged in hotfix/2025-01-22 (pull request #2073)
IO-3099 check for intellipay initialization before calling. rename files to remove erroneous period.
2025-01-22 16:52:28 +00:00
Allan Carr
ebe1facbd1 Merged in feature/IO-3099-wait-for-intellipay (pull request #2072)
IO-3099 check for intellipay initialization before calling. rename files to remove erroneous period.
2025-01-22 16:50:08 +00:00
Patrick Fic
b021992552 IO-3099 check for intellipay initialization before calling. rename files to remove erroneous period. 2025-01-20 12:16:24 -08:00
Dave Richer
0e218abbf4 Merged in release/2025-01-17 (pull request #2067)
Release/2025 01 17 into master-AIO - IO-999 IO-1927 IO-2951 IO-3022 IO-3060 IO-3063 IO-3065 IO-3076 IO-3078 IO-3080 IO-3082 IO-3083 IO-3094 IO-3096
2025-01-18 04:04:26 +00:00
Dave Richer
0cf7961d7d Merged in hotfix/AdditionalProductFruitIds (pull request #2066)
hotfix/AdditionalProductFruitsIds - Add additional IDs for product fruits
2025-01-17 18:15:56 +00:00
Dave Richer
ca02937461 Merged in hotfix/AdditionalProductFruitIds (pull request #2064)
hotfix/AdditionalProductFruitsIds - Add additional IDs for product fruits
2025-01-17 18:14:26 +00:00
Dave Richer
fa7e0a107b hotfix/AdditionalProductFruitsIds - Add additional IDs for product fruits 2025-01-17 10:13:41 -08:00
Allan Carr
9ccffd73ba Merged in feature/IO-999-Part-Tax-rate-Label (pull request #2062)
IO-999 Part Tax Rate Label

Approved-by: Dave Richer
2025-01-17 17:53:05 +00:00
Dave Richer
f68ad181e4 feature/IO-999-Part-Tax-rate-Label 2025-01-17 09:52:35 -08:00
Dave Richer
23bd6085a8 feature/IO-3096-Global-Notifications-Preferences
-  Package Updates
2025-01-17 09:51:55 -08:00
Allan Carr
e4325e39bf Merged in feature/IO-2951-Ro-Guard-Labor-Warning (pull request #2061)
IO-2951 RO Guard Labor Warning

Approved-by: Dave Richer
2025-01-17 15:32:51 +00:00
Allan Carr
584c2e5de2 IO-999 Part Tax Rate Label
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-16 18:39:41 -08:00
Allan Carr
eccc992cfa IO-2951 RO Guard Labor Warning
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-16 18:28:54 -08:00
Allan Carr
f293e80d0d Merged in feature/IO-3063-LOU-on-Schedule-PopOver (pull request #2059)
IO-3063 LOU on Schedule PopOver

Approved-by: Dave Richer
2025-01-16 15:23:59 +00:00
Allan Carr
eea2a758b0 IO-3063 LOU on Schedule PopOver
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-15 17:14:08 -08:00
Allan Carr
8d7d4ab4ac Merged in feature/IO-3083-i18-Translation-Shop-Info-Warning (pull request #2044)
IO-3083 i18 Translation Warning in Shop Info

Approved-by: Dave Richer
2025-01-15 15:49:37 +00:00
Dave Richer
f9105806ba feature/IO-3083-i18-Translation-Shop-Info-Warning
resolve conflicts
2025-01-15 07:49:19 -08:00
Dave Richer
acd278f5b4 Merged in release/2025-01-17 (pull request #2057)
Release/2025 01 17 IO-3065, IO-3076, IO-3094
2025-01-15 15:40:44 +00:00
Allan Carr
a4a6fac224 Merged in feature/IO-3094-Purchases-by-Date-Excel (pull request #2056)
IO-3094 Purchases by Date - Excel Report

Approved-by: Dave Richer
2025-01-15 15:39:12 +00:00
Patrick Fic
b2b7064007 Merged in feature/IO-3065-0-dollar-parts-status (pull request #2055)
feature/IO-3065-0-dollar-parts-status

Approved-by: Dave Richer
2025-01-15 15:38:42 +00:00
Allan Carr
4a56fbb135 IO-3094 Purchases by Date - Excel Report
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-14 18:00:46 -08:00
Allan Carr
e1fcb0ecba IO-3083 Merge Conflit Resolution
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-14 17:54:12 -08:00
Allan Carr
ee5e091118 Merge branch 'release/2025-01-17' into feature/IO-3083-i18-Translation-Shop-Info-Warning
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>

# Conflicts:
#	client/src/translations/en_us/common.json
2025-01-14 17:48:10 -08:00
Patrick Fic
9f46f8ec31 IO-3065 All manual toggle to include $0 line in part status count. 2025-01-14 13:49:38 -08:00
Patrick Fic
8c737ff0ae Merged in feature/IO-3076-daily-usage-reports (pull request #2054)
feature/IO-3076-daily-usage-reports

Approved-by: Patrick Fic
2025-01-14 21:14:13 +00:00
Patrick Fic
a8ef681cf1 IO-3076 Adjust daily usage report window. 2025-01-14 13:13:09 -08:00
Dave Richer
494c8b6867 Merged in release/2025-01-17 (pull request #2053)
Release/2025 01 17 into test-AIO - IO-1927, IO-3022, IO-3060, IO-3078, IO-3080, IO-3082
2025-01-14 17:02:26 +00:00
Dave Richer
262b5600d8 Merged in feature/IO-3060-Realtime-Notification-System (pull request #2052)
feature/IO-3060-Realtime-Notifications- Checkpoint
2025-01-14 17:01:20 +00:00
Allan Carr
1861bd68d1 Merged in feature/IO-3080-Restrict-Claimable-Hours-Label (pull request #2042)
IO-3080 Restrict Claimable Hours Label

Approved-by: Dave Richer
2025-01-14 14:43:12 +00:00
Allan Carr
348561c812 Merged in feature/IO-3078-Job-Presets-Company-Setup-Markup-Rule (pull request #2043)
IO-3078 Jobs Presets Company Setup Markup Discount

Approved-by: Dave Richer
2025-01-14 14:42:59 +00:00
Allan Carr
bf4a52b3c1 Merged in feature/IO-3022-Export-Payments-Payment-Methods (pull request #2045)
IO-3022 Export Payments - Payment Method

Approved-by: Dave Richer
2025-01-14 14:42:34 +00:00
Allan Carr
1950023f37 Merged in feature/IO-3082-Additional-IO-Basic-Lock-Out-Reports (pull request #2041)
IO-3082 Additional IO Basic Lock Out Reports

Approved-by: Dave Richer
2025-01-14 14:42:17 +00:00
Allan Carr
a2798a02b3 Merged in feature/IO-1927-Mark-Exported-Primary (pull request #2046)
IO-1927 Export Button as Primary

Approved-by: Dave Richer
2025-01-14 14:41:54 +00:00
Dave Richer
5fcb5a3a3e Merged in release/2025-01-17 (pull request #2051)
Release/2025 01 17
2025-01-14 01:44:03 +00:00
Dave Richer
ba41b29538 Merged in feature/IO-3060-Realtime-Notification-System (pull request #2050)
Feature/IO-3060 Realtime Notification System
2025-01-14 01:42:46 +00:00
Patrick Fic
d187ed6f73 Merged in release/2025-01-10 (pull request #2049)
Release/2025 01 10
2025-01-10 22:03:04 +00:00
Patrick Fic
13a57406d9 Merged in release/2025-01-10 (pull request #2048)
Release/2025 01 10
2025-01-10 20:06:28 +00:00
Patrick Fic
68c1ac3e70 Merged in feature/IO-3076-daily-usage-reports (pull request #2047)
Feature/IO-3076 daily usage reports

Approved-by: Dave Richer
2025-01-10 19:47:37 +00:00
Patrick Fic
2f267a9f2c IO-3076 updates to usage report. 2025-01-10 11:39:18 -08:00
Allan Carr
c7f293ceca IO-1927 Export Button as Primary
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-09 19:28:28 -08:00
Allan Carr
66e60e96ad IO-3022 Export Payments - Payment Method
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-09 19:16:45 -08:00
Allan Carr
54a9beb37f IO-3083 i18 Translation Warning in Shop Info
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-09 19:00:23 -08:00
Allan Carr
779cc7d9e8 IO-3078 Jobs Presets Company Setup Markup Discount
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-09 18:57:21 -08:00
Allan Carr
c1b3df9c3b IO-3080 Restrict Claimable Hours Label
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-09 17:43:43 -08:00
Allan Carr
80379cdd79 IO-3082 Additional IO Basic Lock Out Reports
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2025-01-09 17:37:30 -08:00
Patrick Fic
d6fbf02092 IO-3076 Initial usage reports design. 2025-01-09 11:22:08 -08:00
Patrick Fic
91c3ac56fa Merged in release/2025-01-10 (pull request #2039)
Release/2025 01 10
2025-01-08 18:39:00 +00:00
Patrick Fic
7d21cb7d70 Merged in feature/IO-3067-implement-learn-more-link-for-rome-upsells (pull request #2037)
Revert "IO-3067 Add learn more link for Rome."
2025-01-07 18:24:52 +00:00
47 changed files with 12070 additions and 11474 deletions

View File

@@ -6369,6 +6369,27 @@
<folder_node> <folder_node>
<name>md_parts_scan</name> <name>md_parts_scan</name>
<children> <children>
<concept_node>
<name>caseInsensitive</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>expression</name> <name>expression</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -6390,6 +6411,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>field</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>flags</name> <name>flags</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -6411,6 +6453,48 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>operation</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>value</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> </children>
</folder_node> </folder_node>
<concept_node> <concept_node>
@@ -11987,6 +12071,158 @@
</concept_node> </concept_node>
</children> </children>
</folder_node> </folder_node>
<folder_node>
<name>operations</name>
<children>
<concept_node>
<name>contains</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>ends_with</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>equals</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>greater_than</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>less_than</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>not_equals</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>starts_with</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node> <folder_node>
<name>successes</name> <name>successes</name>
<children> <children>
@@ -23131,6 +23367,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>alt_partno</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>amount</name> <name>amount</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -23236,6 +23493,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>include_in_part_cnt</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<folder_node> <folder_node>
<name>lbr_types</name> <name>lbr_types</name>
<children> <children>

View File

@@ -62,7 +62,17 @@
t = d.getElementsByTagName("script")[0]; t = d.getElementsByTagName("script")[0];
t.parentNode.insertBefore(s, t); t.parentNode.insertBefore(s, t);
</script> </script>
<script type="text/javascript">
window.$crisp = [];
window.CRISP_WEBSITE_ID = "36724f62-2eb0-4b29-9cdd-9905fb99913e";
(function () {
d = document;
s = d.createElement("script");
s.src = "https://client.crisp.chat/l.js";
s.async = 1;
d.getElementsByTagName("head")[0].appendChild(s);
})();
</script>
<% } %> <% } %>
<script> <script>
!(function () { !(function () {

View File

@@ -85,6 +85,17 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, payments,
sortOrder: state.sortedInfo.columnKey === "amount" && state.sortedInfo.order, sortOrder: state.sortedInfo.columnKey === "amount" && state.sortedInfo.order,
render: (text, record) => <CurrencyFormatter>{record.amount}</CurrencyFormatter> render: (text, record) => <CurrencyFormatter>{record.amount}</CurrencyFormatter>
}, },
{
title: t("payments.fields.type"),
dataIndex: "type",
key: "type",
sorter: (a, b) => a.type.localeCompare(b.type),
sortOrder: state.sortedInfo.columnKey === "type" && state.sortedInfo.order,
filters: bodyshop.md_payment_types.map((s) => {
return { text: s, value: [s] };
}),
onFilter: (value, record) => value.includes(record.type)
},
{ {
title: t("payments.fields.memo"), title: t("payments.fields.memo"),
dataIndex: "memo", dataIndex: "memo",

View File

@@ -133,15 +133,19 @@ const CardPaymentModalComponent = ({
if (window.intellipay) { if (window.intellipay) {
// eslint-disable-next-line no-eval // eslint-disable-next-line no-eval
eval(response.data); eval(response.data);
pollForIntelliPay(() => {
SetIntellipayCallbackFunctions(); SetIntellipayCallbackFunctions();
window.intellipay.autoOpen(); window.intellipay.autoOpen();
});
} else { } else {
const rg = document.createRange(); const rg = document.createRange();
const node = rg.createContextualFragment(response.data); const node = rg.createContextualFragment(response.data);
document.documentElement.appendChild(node); document.documentElement.appendChild(node);
pollForIntelliPay(() => {
SetIntellipayCallbackFunctions(); SetIntellipayCallbackFunctions();
window.intellipay.isAutoOpen = true; window.intellipay.isAutoOpen = true;
window.intellipay.initialize(); window.intellipay.initialize();
});
} }
} catch (error) { } catch (error) {
notification.open({ notification.open({
@@ -345,3 +349,27 @@ const CardPaymentModalComponent = ({
}; };
export default connect(mapStateToProps, mapDispatchToProps)(CardPaymentModalComponent); export default connect(mapStateToProps, mapDispatchToProps)(CardPaymentModalComponent);
//Poll for window.IntelliPay.fixAmount for 5 seconds. If it doesn't come up, just try anyways to force the possible error.
function pollForIntelliPay(callbackFunction) {
const timeout = 5000;
const interval = 150; // Poll every 100 milliseconds
const startTime = Date.now();
function checkFixAmount() {
if (window.intellipay && window.intellipay.fixAmount !== undefined) {
callbackFunction();
return;
}
if (Date.now() - startTime >= timeout) {
console.log("Stopped polling IntelliPay after 10 seconds. Attemping to set functions anyways.");
callbackFunction();
return;
}
setTimeout(checkFixAmount, interval);
}
checkFixAmount();
}

View File

@@ -6,7 +6,7 @@ import { createStructuredSelector } from "reselect";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectCardPayment } from "../../redux/modals/modals.selectors"; import { selectCardPayment } from "../../redux/modals/modals.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import CardPaymentModalComponent from "./card-payment-modal.component."; import CardPaymentModalComponent from "./card-payment-modal.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
cardPaymentModal: selectCardPayment, cardPaymentModal: selectCardPayment,

View File

@@ -1,10 +1,10 @@
import * as Sentry from "@sentry/react";
import { Button, Col, Collapse, Result, Row, Space } from "antd"; import { Button, Col, Collapse, Result, Row, Space } from "antd";
import React from "react"; import React from "react";
import { withTranslation } from "react-i18next"; import { withTranslation } from "react-i18next";
import { logImEXEvent } from "../../firebase/firebase.utils";
import * as Sentry from "@sentry/react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import InstanceRenderManager from "../../utils/instanceRenderMgr"; import InstanceRenderManager from "../../utils/instanceRenderMgr";
@@ -38,10 +38,6 @@ class ErrorBoundary extends React.Component {
} }
handleErrorSubmit = () => { handleErrorSubmit = () => {
InstanceRenderManager({
executeFunction: true,
args: [],
imex: () => {
window.$crisp.push([ window.$crisp.push([
"do", "do",
"message:send", "message:send",
@@ -58,8 +54,7 @@ class ErrorBoundary extends React.Component {
]); ]);
window.$crisp.push(["do", "chat:open"]); window.$crisp.push(["do", "chat:open"]);
}
});
// const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue** // const errorDescription = `**Please add relevant details about what you were doing before you encountered this issue**
// ---- // ----

View File

@@ -650,14 +650,7 @@ function Header({
icon: <Icon component={QuestionCircleFilled} />, icon: <Icon component={QuestionCircleFilled} />,
label: t("menus.header.help"), label: t("menus.header.help"),
onClick: () => { onClick: () => {
window.open( window.open("https://help.imex.online/", "_blank");
InstanceRenderManager({
imex: "https://help.imex.online/",
rome: "https://rometech.com//"
}),
"_blank"
);
} }
}, },
...(InstanceRenderManager({ ...(InstanceRenderManager({

View File

@@ -1,30 +1,30 @@
import { AlertFilled } from "@ant-design/icons"; import { AlertFilled } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Button, Divider, Dropdown, Form, Input, notification, Popover, Select, Space } from "antd"; import { Button, Divider, Dropdown, Form, Input, notification, Popover, Select, Space } from "antd";
import parsePhoneNumber from "libphonenumber-js"; import parsePhoneNumber from "libphonenumber-js";
import dayjs from "../../utils/day";
import queryString from "query-string"; import queryString from "query-string";
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link, useLocation, useNavigate } from "react-router-dom"; import { Link, useLocation, useNavigate } from "react-router-dom";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions"; import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
import { setModalContext } from "../../redux/modals/modals.actions"; import { setModalContext } from "../../redux/modals/modals.actions";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import dayjs from "../../utils/day";
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
import ChatOpenButton from "../chat-open-button/chat-open-button.component"; import ChatOpenButton from "../chat-open-button/chat-open-button.component";
import DataLabel from "../data-label/data-label.component"; import DataLabel from "../data-label/data-label.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component"; import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component";
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component"; import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
import ScheduleAtChange from "./job-at-change.component"; import ScheduleAtChange from "./job-at-change.component";
import ScheduleEventColor from "./schedule-event.color.component"; import ScheduleEventColor from "./schedule-event.color.component";
import ScheduleEventNote from "./schedule-event.note.component"; import ScheduleEventNote from "./schedule-event.note.component";
import { useMutation } from "@apollo/client";
import { UPDATE_APPOINTMENT } from "../../graphql/appointments.queries";
import ProductionListColumnComment from "../production-list-columns/production-list-columns.comment.component";
import SocketContext from "../../contexts/SocketIO/socketContext.jsx";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop bodyshop: selectBodyshop
@@ -127,6 +127,7 @@ export function ScheduleEventComponent({
<DataLabel label={t("jobs.fields.ownr_ph2")}> <DataLabel label={t("jobs.fields.ownr_ph2")}>
<ChatOpenButton phone={event.job && event.job.ownr_ph2} jobid={event.job.id} /> <ChatOpenButton phone={event.job && event.job.ownr_ph2} jobid={event.job.id} />
</DataLabel> </DataLabel>
<DataLabel hideIfNull label={t("jobs.fields.loss_of_use")}>{(event.job && event.job.loss_of_use) || ""}</DataLabel>
<DataLabel label={t("jobs.fields.alt_transport")}> <DataLabel label={t("jobs.fields.alt_transport")}>
{(event.job && event.job.alt_transport) || ""} {(event.job && event.job.alt_transport) || ""}
<ScheduleAtChange job={event && event.job} /> <ScheduleAtChange job={event && event.job} />

View File

@@ -519,6 +519,7 @@ export function JobLinesComponent({
{selectedLines.length > 0 && ` (${selectedLines.length})`} {selectedLines.length > 0 && ` (${selectedLines.length})`}
</Button> </Button>
<Button <Button
id="job-lines-order-parts-button"
disabled={(job && !job.converted) || (selectedLines.length > 0 ? false : true) || jobRO || technician} disabled={(job && !job.converted) || (selectedLines.length > 0 ? false : true) || jobRO || technician}
onClick={() => { onClick={() => {
setPartsOrderContext({ setPartsOrderContext({
@@ -541,6 +542,7 @@ export function JobLinesComponent({
{selectedLines.length > 0 && ` (${selectedLines.length})`} {selectedLines.length > 0 && ` (${selectedLines.length})`}
</Button> </Button>
<Button <Button
id="job-lines-filter-parts-only-button"
onClick={() => { onClick={() => {
setState((state) => ({ setState((state) => ({
...state, ...state,
@@ -554,7 +556,7 @@ export function JobLinesComponent({
<FilterFilled /> {t("jobs.actions.filterpartsonly")} <FilterFilled /> {t("jobs.actions.filterpartsonly")}
</Button> </Button>
<Dropdown menu={markMenu} trigger={["click"]}> <Dropdown menu={markMenu} trigger={["click"]}>
<Button>{t("jobs.actions.mark")}</Button> <Button id="repair-data-mark-button">{t("jobs.actions.mark")}</Button>
</Dropdown> </Dropdown>
<Button <Button
disabled={jobRO || technician} disabled={jobRO || technician}

View File

@@ -192,6 +192,23 @@ export function JobLinesUpsertModalComponent({ bodyshop, open, jobLine, handleCa
<Form.Item label={t("joblines.fields.tax_part")} name="tax_part" valuePropName="checked" initialValue={true}> <Form.Item label={t("joblines.fields.tax_part")} name="tax_part" valuePropName="checked" initialValue={true}>
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Form.Item dependencies={[["act_price"]]} noStyle>
{() => {
if (form.getFieldValue("act_price") === 0) {
return (
<Form.Item
label={t("joblines.fields.include_in_part_cnt")}
name="include_in_part_cnt"
valuePropName="checked"
>
<Switch />
</Form.Item>
);
} else {
return null;
}
}}
</Form.Item>
</LayoutFormRow> </LayoutFormRow>
</Form> </Form>
</Modal> </Modal>

View File

@@ -173,7 +173,7 @@ export function JobsCloseExportButton({
} }
}); });
if (!!!jobUpdateResponse.errors) { if (!jobUpdateResponse.errors) {
notification.open({ notification.open({
type: "success", type: "success",
key: "jobsuccessexport", key: "jobsuccessexport",
@@ -222,7 +222,7 @@ export function JobsCloseExportButton({
}; };
return ( return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled}> <Button onClick={handleQbxml} loading={loading} disabled={disabled} type="primary">
{t("jobs.actions.export")} {t("jobs.actions.export")}
</Button> </Button>
); );

View File

@@ -1,7 +1,8 @@
import { useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { Button, Form, Input, notification, Popover, Select, Space, Switch } from "antd"; import { Button, Form, Input, notification, Popover, Select, Space, Switch } from "antd";
import axios from "axios"; import axios from "axios";
import React, { useState } from "react"; import { some } from "lodash";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
@@ -18,7 +19,14 @@ const mapStateToProps = createStructuredSelector({
jobRO: selectJobReadOnly jobRO: selectJobReadOnly
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
insertAuditTrail: ({ jobid, operation, type }) => dispatch(insertAuditTrail({ jobid, operation, type })) insertAuditTrail: ({ jobid, operation, type }) =>
dispatch(
insertAuditTrail({
jobid,
operation,
type
})
)
}); });
export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTrail, parentFormIsFieldsTouched }) { export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTrail, parentFormIsFieldsTouched }) {
@@ -27,6 +35,7 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
const [mutationConvertJob] = useMutation(CONVERT_JOB_TO_RO); const [mutationConvertJob] = useMutation(CONVERT_JOB_TO_RO);
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const allFormValues = Form.useWatch([], form);
const handleConvert = async ({ employee_csr, category, ...values }) => { const handleConvert = async ({ employee_csr, category, ...values }) => {
if (parentFormIsFieldsTouched()) { if (parentFormIsFieldsTouched()) {
@@ -69,6 +78,8 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
setLoading(false); setLoading(false);
}; };
const submitDisabled = useCallback(() => some(allFormValues, (v) => v === undefined), [allFormValues]);
const popMenu = ( const popMenu = (
<div> <div>
<Form <Form
@@ -77,9 +88,12 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
onFinish={handleConvert} onFinish={handleConvert}
initialValues={{ initialValues={{
driveable: true, driveable: true,
towin: false, towin: job.towin,
ca_gst_registrant: job.ca_gst_registrant,
employee_csr: job.employee_csr, employee_csr: job.employee_csr,
category: job.category category: job.category,
referral_source: job.referral_source,
referral_source_extra: job.referral_source_extra ?? ""
}} }}
> >
<Form.Item <Form.Item
@@ -209,7 +223,7 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
<Switch /> <Switch />
</Form.Item> </Form.Item>
<Space wrap> <Space wrap>
<Button type="primary" danger onClick={() => form.submit()} loading={loading}> <Button disabled={submitDisabled()} type="primary" danger onClick={() => form.submit()} loading={loading}>
{t("jobs.actions.convert")} {t("jobs.actions.convert")}
</Button> </Button>
<Button onClick={() => setOpen(false)}>{t("general.actions.close")}</Button> <Button onClick={() => setOpen(false)}>{t("general.actions.close")}</Button>
@@ -231,11 +245,6 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
loading={loading} loading={loading}
onClick={() => { onClick={() => {
setOpen(true); setOpen(true);
form.setFieldsValue({
driveable: true,
towin: false,
employee_csr: job.employee_csr
});
}} }}
> >
{t("jobs.actions.convert")} {t("jobs.actions.convert")}

View File

@@ -982,7 +982,7 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true, form })
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow> <LayoutFormRow header={t("jobs.labels.cieca_pfo")}>
<Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt"> <Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt">
<InputNumber min={0} max={100} precision={4} disabled={jobRO} /> <InputNumber min={0} max={100} precision={4} disabled={jobRO} />
</Form.Item> </Form.Item>

View File

@@ -10,8 +10,8 @@ import { auth, logImEXEvent } from "../../firebase/firebase.utils";
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries"; import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
import { UPDATE_JOBS } from "../../graphql/jobs.queries"; import { UPDATE_JOBS } from "../../graphql/jobs.queries";
import { insertAuditTrail } from "../../redux/application/application.actions"; import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors"; import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import client from "../../utils/GraphQLClient"; import client from "../../utils/GraphQLClient";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -165,7 +165,7 @@ export function JobsExportAllButton({
} }
}); });
if (!!!jobUpdateResponse.errors) { if (!jobUpdateResponse.errors) {
notification.open({ notification.open({
type: "success", type: "success",
key: "jobsuccessexport", key: "jobsuccessexport",
@@ -213,13 +213,13 @@ export function JobsExportAllButton({
}) })
); );
if (!!completedCallback) completedCallback([]); if (completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false); if (loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
}; };
return ( return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled || jobIds?.length > 10}> <Button onClick={handleQbxml} loading={loading} disabled={disabled || jobIds?.length > 10} type="primary">
{t("jobs.actions.exportselected")} {t("jobs.actions.exportselected")}
</Button> </Button>
); );

View File

@@ -1,6 +1,5 @@
import { EditFilled } from "@ant-design/icons"; import { EditFilled } from "@ant-design/icons";
import { Alert, Card, Col, Row, Space, Table, Typography } from "antd"; import { Alert, Card, Col, Row, Space, Table, Typography } from "antd";
import _ from "lodash";
import React, { useEffect, useMemo, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -9,11 +8,11 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter"; import CurrencyFormatter from "../../utils/CurrencyFormatter";
import { alphaSort } from "../../utils/sorters"; import { alphaSort } from "../../utils/sorters";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component"; import LaborAllocationsAdjustmentEdit from "../labor-allocations-adjustment-edit/labor-allocations-adjustment-edit.component";
import UpsellComponent, { upsellEnum } from "../upsell/upsell.component";
import "./labor-allocations-table.styles.scss"; import "./labor-allocations-table.styles.scss";
import { CalculateAllocationsTotals } from "./labor-allocations-table.utility"; import { CalculateAllocationsTotals } from "./labor-allocations-table.utility";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import UpsellComponent, { upsellEnum } from "../upsell/upsell.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
technician: selectTechnician technician: selectTechnician
@@ -65,6 +64,7 @@ export function LaborAllocationsTable({
key: "total", key: "total",
sorter: (a, b) => a.total - b.total, sorter: (a, b) => a.total - b.total,
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order, sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
align: "right",
render: (text, record) => record.total.toFixed(1) render: (text, record) => record.total.toFixed(1)
}, },
{ {
@@ -73,6 +73,7 @@ export function LaborAllocationsTable({
key: "hrs_claimed", key: "hrs_claimed",
sorter: (a, b) => a.claimed - b.claimed, sorter: (a, b) => a.claimed - b.claimed,
sortOrder: state.sortedInfo.columnKey === "claimed" && state.sortedInfo.order, sortOrder: state.sortedInfo.columnKey === "claimed" && state.sortedInfo.order,
align: "right",
render: (text, record) => record.claimed && record.claimed.toFixed(1) render: (text, record) => record.claimed && record.claimed.toFixed(1)
}, },
{ {
@@ -81,6 +82,7 @@ export function LaborAllocationsTable({
key: "adjustments", key: "adjustments",
sorter: (a, b) => a.adjustments - b.adjustments, sorter: (a, b) => a.adjustments - b.adjustments,
sortOrder: state.sortedInfo.columnKey === "adjustments" && state.sortedInfo.order, sortOrder: state.sortedInfo.columnKey === "adjustments" && state.sortedInfo.order,
align: "right",
render: (text, record) => ( render: (text, record) => (
<Space wrap> <Space wrap>
{record.adjustments.toFixed(1)} {record.adjustments.toFixed(1)}
@@ -100,17 +102,17 @@ export function LaborAllocationsTable({
{ {
title: t("jobs.labels.difference"), title: t("jobs.labels.difference"),
dataIndex: "difference", dataIndex: "difference",
key: "difference", key: "difference",
sorter: (a, b) => a.difference - b.difference, sorter: (a, b) => a.difference - b.difference,
sortOrder: state.sortedInfo.columnKey === "difference" && state.sortedInfo.order, sortOrder: state.sortedInfo.columnKey === "difference" && state.sortedInfo.order,
align: "right",
render: (text, record) => ( render: (text, record) => (
<strong <strong
style={{ style={{
color: record.difference >= 0 ? "green" : "red" color: record.difference.toFixed(1) >= 0 ? "green" : "red"
}} }}
> >
{_.round(record.difference, 1)} {(Math.abs(record.difference) < 0.05 ? 0 : record.difference).toFixed(1)}
</strong> </strong>
) )
} }
@@ -129,7 +131,6 @@ export function LaborAllocationsTable({
ellipsis: true, ellipsis: true,
render: (text, record) => `${record.op_code_desc || ""}${record.alt_partm ? ` ${record.alt_partm}` : ""}` render: (text, record) => `${record.op_code_desc || ""}${record.alt_partm ? ` ${record.alt_partm}` : ""}`
}, },
{ {
title: t("joblines.fields.act_price"), title: t("joblines.fields.act_price"),
dataIndex: "act_price", dataIndex: "act_price",
@@ -187,7 +188,7 @@ export function LaborAllocationsTable({
{ hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 } { hrs_total: 0, hrs_claimed: 0, adjustments: 0, difference: 0 }
); );
if (summary.difference !== 0 && typeof warningCallback === "function") { if (Math.abs(summary.difference.toFixed(1)) !== 0 && typeof warningCallback === "function") {
warningCallback({ key: "labor", warning: t("jobs.labels.outstandinghours") }); warningCallback({ key: "labor", warning: t("jobs.labels.outstandinghours") });
} }
@@ -217,19 +218,21 @@ export function LaborAllocationsTable({
summary={() => ( summary={() => (
<Table.Summary.Row> <Table.Summary.Row>
<Table.Summary.Cell> <Table.Summary.Cell>
<Typography.Title level={4}>{t("general.labels.totals")}</Typography.Title> <Typography.Title level={4} style={{ margin: 0, lineHeight: 1 }}>
{t("general.labels.totals")}
</Typography.Title>
</Table.Summary.Cell> </Table.Summary.Cell>
<Table.Summary.Cell>{summary.hrs_total.toFixed(1)}</Table.Summary.Cell> <Table.Summary.Cell align="right">{summary.hrs_total.toFixed(1)}</Table.Summary.Cell>
<Table.Summary.Cell>{summary.hrs_claimed.toFixed(1)}</Table.Summary.Cell> <Table.Summary.Cell align="right">{summary.hrs_claimed.toFixed(1)}</Table.Summary.Cell>
<Table.Summary.Cell>{summary.adjustments.toFixed(1)}</Table.Summary.Cell> <Table.Summary.Cell align="right">{summary.adjustments.toFixed(1)}</Table.Summary.Cell>
<Table.Summary.Cell> <Table.Summary.Cell align="right">
<Typography.Text <Typography.Text
style={{ style={{
fontWeight: "bold", fontWeight: "bold",
color: summary.difference >= 0 ? "green" : "red" color: summary.difference.toFixed(1) >= 0 ? "green" : "red"
}} }}
> >
{summary.difference.toFixed(1)} {(Math.abs(summary.difference) < 0.05 ? 0 : summary.difference).toFixed(1)}
</Typography.Text> </Typography.Text>
</Table.Summary.Cell> </Table.Summary.Cell>
</Table.Summary.Row> </Table.Summary.Row>
@@ -261,11 +264,10 @@ export function LaborAllocationsTable({
</Card> </Card>
</Col> </Col>
)} )}
{showWarning && summary.difference !== 0 && ( {showWarning && Math.abs(summary.difference.toFixed(1)) !== 0 && (
<Alert style={{ margin: "8px 0px" }} type="warning" message={t("jobs.labels.outstandinghours")} /> <Alert style={{ margin: "8px 0px" }} type="warning" message={t("jobs.labels.outstandinghours")} />
)} )}
</Row> </Row>
); );
} }
export default connect(mapStateToProps, null)(LaborAllocationsTable); export default connect(mapStateToProps, null)(LaborAllocationsTable);

View File

@@ -38,7 +38,11 @@ export default function OwnerFindModalContainer({
}, [callSearchowners, modalProps.open, owner]); }, [callSearchowners, modalProps.open, owner]);
return ( return (
<Modal title={t("owners.labels.existing_owners")} width={"80%"} {...modalProps}> <Modal
title={<span id="owner-find-modal-title">{t("owners.labels.existing_owners")}</span>}
width={"80%"}
{...modalProps}
>
{loading ? <LoadingSpinner /> : null} {loading ? <LoadingSpinner /> : null}
{error ? <AlertComponent message={error.message} type="error" /> : null} {error ? <AlertComponent message={error.message} type="error" /> : null}
{owner ? ( {owner ? (

View File

@@ -48,7 +48,7 @@ export function PayableExportAll({
let PartnerResponse; let PartnerResponse;
setLoading(true); setLoading(true);
if (!!loadingCallback) loadingCallback(true); if (loadingCallback) loadingCallback(true);
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payables`, { PartnerResponse = await axios.post(`/qbo/payables`, {
bills: billids, bills: billids,
@@ -85,7 +85,7 @@ export function PayableExportAll({
notification["error"]({ notification["error"]({
message: t("bills.errors.exporting-partner") message: t("bills.errors.exporting-partner")
}); });
if (!!loadingCallback) loadingCallback(false); if (loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
return; return;
} }
@@ -152,7 +152,7 @@ export function PayableExportAll({
} }
} }
}); });
if (!!!billUpdateResponse.errors) { if (!billUpdateResponse.errors) {
notification.open({ notification.open({
type: "success", type: "success",
key: "billsuccessexport", key: "billsuccessexport",
@@ -187,8 +187,8 @@ export function PayableExportAll({
}); });
await Promise.all(proms); await Promise.all(proms);
if (!!completedCallback) completedCallback([]); if (completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false); if (loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
}; };
@@ -200,7 +200,7 @@ export function PayableExportAll({
); );
return ( return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled || billids?.length > 10}> <Button onClick={handleQbxml} loading={loading} disabled={disabled || billids?.length > 10} type="primary">
{t("jobs.actions.exportselected")} {t("jobs.actions.exportselected")}
</Button> </Button>
); );

View File

@@ -46,7 +46,7 @@ export function PayableExportButton({
logImEXEvent("accounting_export_payable"); logImEXEvent("accounting_export_payable");
setLoading(true); setLoading(true);
if (!!loadingCallback) loadingCallback(true); if (loadingCallback) loadingCallback(true);
//Check if it's a QBO Setup. //Check if it's a QBO Setup.
let PartnerResponse; let PartnerResponse;
@@ -88,7 +88,7 @@ export function PayableExportButton({
notification["error"]({ notification["error"]({
message: t("bills.errors.exporting-partner") message: t("bills.errors.exporting-partner")
}); });
if (!!loadingCallback) loadingCallback(false); if (loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
return; return;
} }
@@ -149,7 +149,7 @@ export function PayableExportButton({
} }
} }
}); });
if (!!!billUpdateResponse.errors) { if (!billUpdateResponse.errors) {
notification.open({ notification.open({
type: "success", type: "success",
key: "billsuccessexport", key: "billsuccessexport",
@@ -186,7 +186,7 @@ export function PayableExportButton({
} }
} }
if (!!loadingCallback) loadingCallback(false); if (loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
}; };
@@ -198,7 +198,7 @@ export function PayableExportButton({
); );
return ( return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled}> <Button onClick={handleQbxml} loading={loading} disabled={disabled} type="primary">
{t("jobs.actions.export")} {t("jobs.actions.export")}
</Button> </Button>
); );

View File

@@ -55,7 +55,7 @@ export function PaymentExportButton({
} else { } else {
//Default is QBD //Default is QBD
if (!!loadingCallback) loadingCallback(true); if (loadingCallback) loadingCallback(true);
let QbXmlResponse; let QbXmlResponse;
try { try {
@@ -88,7 +88,7 @@ export function PaymentExportButton({
notification["error"]({ notification["error"]({
message: t("payments.errors.exporting-partner") message: t("payments.errors.exporting-partner")
}); });
if (!!loadingCallback) loadingCallback(false); if (loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
return; return;
} }
@@ -148,7 +148,7 @@ export function PaymentExportButton({
} }
} }
}); });
if (!!!paymentUpdateResponse.errors) { if (!paymentUpdateResponse.errors) {
notification.open({ notification.open({
type: "success", type: "success",
key: "paymentsuccessexport", key: "paymentsuccessexport",
@@ -184,12 +184,12 @@ export function PaymentExportButton({
) )
]); ]);
} }
if (!!loadingCallback) loadingCallback(false); if (loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
}; };
return ( return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled}> <Button onClick={handleQbxml} loading={loading} disabled={disabled} type="primary">
{t("jobs.actions.export")} {t("jobs.actions.export")}
</Button> </Button>
); );

View File

@@ -44,7 +44,7 @@ export function PaymentsExportAllButton({
const handleQbxml = async () => { const handleQbxml = async () => {
setLoading(true); setLoading(true);
if (!!loadingCallback) loadingCallback(true); if (loadingCallback) loadingCallback(true);
let PartnerResponse; let PartnerResponse;
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) { if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
PartnerResponse = await axios.post(`/qbo/payments`, { PartnerResponse = await axios.post(`/qbo/payments`, {
@@ -76,7 +76,7 @@ export function PaymentsExportAllButton({
notification["error"]({ notification["error"]({
message: t("payments.errors.exporting-partner") message: t("payments.errors.exporting-partner")
}); });
if (!!loadingCallback) loadingCallback(false); if (loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
return; return;
} }
@@ -140,7 +140,7 @@ export function PaymentsExportAllButton({
} }
} }
}); });
if (!!!paymentUpdateResponse.errors) { if (!paymentUpdateResponse.errors) {
notification.open({ notification.open({
type: "success", type: "success",
key: "paymentsuccessexport", key: "paymentsuccessexport",
@@ -174,13 +174,13 @@ export function PaymentsExportAllButton({
); );
}); });
await Promise.all(proms); await Promise.all(proms);
if (!!completedCallback) completedCallback([]); if (completedCallback) completedCallback([]);
if (!!loadingCallback) loadingCallback(false); if (loadingCallback) loadingCallback(false);
setLoading(false); setLoading(false);
}; };
return ( return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled || paymentIds?.length > 10}> <Button onClick={handleQbxml} loading={loading} disabled={disabled || paymentIds?.length > 10} type="primary">
{t("jobs.actions.exportselected")} {t("jobs.actions.exportselected")}
</Button> </Button>
); );

View File

@@ -555,7 +555,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name={["md_email_cc", "parts_order"]} name={["md_email_cc", "parts_order"]}
label={t("bodyshop.fields.md_email_cc", { template: "parts_order" })} label={t("bodyshop.fields.md_email_cc", { template: "parts_orders" })}
rules={[ rules={[
{ {
//message: t("general.validation.required"), //message: t("general.validation.required"),
@@ -567,9 +567,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name={["md_email_cc", "parts_return_slip"]} name={["md_email_cc", "parts_return_slip"]}
label={t("bodyshop.fields.md_email_cc", { label={t("bodyshop.fields.md_email_cc", { template: "parts_returns" })}
template: "parts_return_slip"
})}
rules={[ rules={[
{ {
//message: t("general.validation.required"), //message: t("general.validation.required"),
@@ -1261,7 +1259,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
key={`${index}prt_dsmk_p`} key={`${index}prt_dsmk_p`}
name={[field.name, "prt_dsmk_p"]} name={[field.name, "prt_dsmk_p"]}
> >
<InputNumber precision={0} min={0} max={100} /> <InputNumber precision={0} min={-100} max={100} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t("joblines.fields.ah_detail_line")} label={t("joblines.fields.ah_detail_line")}

View File

@@ -73,6 +73,7 @@ export const QUERY_PAYMENTS_FOR_EXPORT = gql`
transactionid transactionid
paymentnum paymentnum
date date
type
exportlogs { exportlogs {
id id
successful successful

View File

@@ -49,6 +49,7 @@ export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
est_ct_fn est_ct_fn
est_ct_ln est_ct_ln
comment comment
loss_of_use
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) { labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
aggregate { aggregate {
sum { sum {

View File

@@ -238,6 +238,7 @@ export const UPDATE_JOB_LINE = gql`
convertedtolbr convertedtolbr
convertedtolbr_data convertedtolbr_data
assigned_team assigned_team
include_in_part_cnt
} }
} }
} }

View File

@@ -581,6 +581,7 @@ export const GET_JOB_BY_PK = gql`
status status
tax_part tax_part
unq_seq unq_seq
include_in_part_cnt
} }
kmin kmin
kmout kmout

View File

@@ -31,7 +31,7 @@ import { addAlerts } from "../../redux/application/application.actions.js";
const JobsPage = lazy(() => import("../jobs/jobs.page")); const JobsPage = lazy(() => import("../jobs/jobs.page"));
const CardPaymentModalContainer = lazy( const CardPaymentModalContainer = lazy(
() => import("../../components/card-payment-modal/card-payment-modal.container.") () => import("../../components/card-payment-modal/card-payment-modal.container.jsx")
); );
const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page.container")); const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page.container"));

View File

@@ -26,6 +26,7 @@ import {
updateCurrentUser updateCurrentUser
} from "../../firebase/firebase.utils"; } from "../../firebase/firebase.utils";
import { QUERY_EULA } from "../../graphql/bodyshop.queries"; import { QUERY_EULA } from "../../graphql/bodyshop.queries";
import cleanAxios from "../../utils/CleanAxios";
import client from "../../utils/GraphQLClient"; import client from "../../utils/GraphQLClient";
import dayjs from "../../utils/day"; import dayjs from "../../utils/day";
import InstanceRenderManager from "../../utils/instanceRenderMgr"; import InstanceRenderManager from "../../utils/instanceRenderMgr";
@@ -47,7 +48,6 @@ import {
validatePasswordResetSuccess validatePasswordResetSuccess
} from "./user.actions"; } from "./user.actions";
import UserActionTypes from "./user.types"; import UserActionTypes from "./user.types";
import cleanAxios from "../../utils/CleanAxios";
const fpPromise = FingerprintJS.load(); const fpPromise = FingerprintJS.load();
@@ -234,16 +234,18 @@ export function* signInSuccessSaga({ payload }) {
LogRocket.identify(payload.email); LogRocket.identify(payload.email);
try { try {
window.$crisp.push(["set", "user:nickname", [payload.displayName || payload.email]]);
window.$crisp.push(["set", "session:segments", [["user"]]]);
InstanceRenderManager({ InstanceRenderManager({
executeFunction: true, executeFunction: true,
args: [], args: [],
imex: () => { imex: () => {
window.$crisp.push(["set", "user:nickname", [payload.displayName || payload.email]]); window.$crisp.push(["set", "session:segments", [["imex"]]]);
window.$crisp.push(["set", "session:segments", [["user"]]]);
}, },
rome: () => { rome: () => {
window.$zoho.salesiq.visitor.name(payload.displayName || payload.email); window.$zoho.salesiq.visitor.name(payload.displayName || payload.email);
window.$zoho.salesiq.visitor.email(payload.email); window.$zoho.salesiq.visitor.email(payload.email);
window.$crisp.push(["set", "session:segments", [["rome"]]]);
} }
}); });
} catch (error) { } catch (error) {

View File

@@ -256,15 +256,6 @@
} }
}, },
"bodyshop": { "bodyshop": {
"operations": {
"starts_with": "Starts With",
"contains": "Contains",
"ends_with": "Ends With",
"equals": "Equals",
"not_equals": "Not Equals",
"greater_than": "Greater Than",
"less_than": "Less Than"
},
"actions": { "actions": {
"add_task_preset": "Add Task Preset", "add_task_preset": "Add Task Preset",
"addapptcolor": "Add Appointment Color", "addapptcolor": "Add Appointment Color",
@@ -366,7 +357,7 @@
"md_ccc_rates": "Courtesy Car Contract Rate Presets", "md_ccc_rates": "Courtesy Car Contract Rate Presets",
"md_classes": "Classes", "md_classes": "Classes",
"md_ded_notes": "Deductible Notes", "md_ded_notes": "Deductible Notes",
"md_email_cc": "Auto Email CC: $t(printcenter.subjects.jobs.{{template}})", "md_email_cc": "Auto Email CC: $t(parts_orders.labels.{{template}})",
"md_from_emails": "Additional From Emails", "md_from_emails": "Additional From Emails",
"md_functionality_toggles": { "md_functionality_toggles": {
"parts_queue_toggle": "Auto Add Imported/Supplemented Jobs to Parts Queue" "parts_queue_toggle": "Auto Add Imported/Supplemented Jobs to Parts Queue"
@@ -388,10 +379,12 @@
"md_lost_sale_reasons": "Lost Sale Reasons", "md_lost_sale_reasons": "Lost Sale Reasons",
"md_parts_order_comment": "Parts Orders Comments", "md_parts_order_comment": "Parts Orders Comments",
"md_parts_scan": { "md_parts_scan": {
"caseInsensitive": "Case Insensitive",
"expression": "",
"field": "Field", "field": "Field",
"flags": "",
"operation": "Operation", "operation": "Operation",
"value": "Value", "value": "Value"
"caseInsensitive": "Case Insensitive"
}, },
"md_payment_types": "Payment Types", "md_payment_types": "Payment Types",
"md_referral_sources": "Referral Sources", "md_referral_sources": "Referral Sources",
@@ -636,7 +629,7 @@
"target_touchtime": "Target Touch Time", "target_touchtime": "Target Touch Time",
"timezone": "Timezone", "timezone": "Timezone",
"tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs", "tt_allow_post_to_invoiced": "Allow Time Tickets to be posted to Invoiced & Exported Jobs",
"tt_enforce_hours_for_tech_console": "Restrict Claimable hours from Tech Console", "tt_enforce_hours_for_tech_console": "Restrict Claimable Hours",
"use_fippa": "Conceal Customer Information on Generated Documents?", "use_fippa": "Conceal Customer Information on Generated Documents?",
"use_paint_scale_data": "Use Paint Scale Data for Job Costing?", "use_paint_scale_data": "Use Paint Scale Data for Job Costing?",
"uselocalmediaserver": "Use Local Media Server?", "uselocalmediaserver": "Use Local Media Server?",
@@ -721,6 +714,15 @@
"task-presets": "Task Presets", "task-presets": "Task Presets",
"workingdays": "Working Days" "workingdays": "Working Days"
}, },
"operations": {
"contains": "Contains",
"ends_with": "Ends With",
"equals": "Equals",
"greater_than": "Greater Than",
"less_than": "Less Than",
"not_equals": "Not Equals",
"starts_with": "Starts With"
},
"successes": { "successes": {
"areyousure": "Are you sure you want to continue?", "areyousure": "Are you sure you want to continue?",
"defaultviewcreated": "Default view created successfully.", "defaultviewcreated": "Default view created successfully.",
@@ -1434,11 +1436,13 @@
"act_price_before_ppc": "Original Part Price", "act_price_before_ppc": "Original Part Price",
"adjustment": "Adjustment", "adjustment": "Adjustment",
"ah_detail_line": "Mark as Detail Labor Line (Autohouse Only)", "ah_detail_line": "Mark as Detail Labor Line (Autohouse Only)",
"alt_partno": "Alt Part #",
"amount": "Amount", "amount": "Amount",
"assigned_team": "Team", "assigned_team": "Team",
"assigned_team_name": "Team {{name}}", "assigned_team_name": "Team {{name}}",
"create_ppc": "Create PPC?", "create_ppc": "Create PPC?",
"db_price": "List Price", "db_price": "List Price",
"include_in_part_cnt": "Include in Parts Status Count",
"lbr_types": { "lbr_types": {
"LA1": "LA1", "LA1": "LA1",
"LA2": "LA2", "LA2": "LA2",
@@ -1463,7 +1467,6 @@
"mod_lbr_ty": "Labor Type", "mod_lbr_ty": "Labor Type",
"notes": "Notes", "notes": "Notes",
"oem_partno": "OEM Part #", "oem_partno": "OEM Part #",
"alt_partno": "Alt Part #",
"op_code_desc": "Op Code Description", "op_code_desc": "Op Code Description",
"part_qty": "Qty.", "part_qty": "Qty.",
"part_type": "Part Type", "part_type": "Part Type",
@@ -2053,7 +2056,7 @@
"parts": "Parts", "parts": "Parts",
"parts_lines": "Parts Lines", "parts_lines": "Parts Lines",
"parts_received": "Parts Rec.", "parts_received": "Parts Rec.",
"parts_tax_rates": "Parts Tax rates", "parts_tax_rates": "Profile - Parts",
"partsfilter": "Parts Only", "partsfilter": "Parts Only",
"partssubletstotal": "Parts & Sublets Total", "partssubletstotal": "Parts & Sublets Total",
"partstotal": "Parts Total (ex. Taxes)", "partstotal": "Parts Total (ex. Taxes)",
@@ -2529,6 +2532,7 @@
"orderhistory": "Order History", "orderhistory": "Order History",
"parts_order": "Parts Order", "parts_order": "Parts Order",
"parts_orders": "Parts Orders", "parts_orders": "Parts Orders",
"parts_returns": "Parts Returns",
"print": "Show Printed Form", "print": "Show Printed Form",
"receive": "Receive Parts Order", "receive": "Receive Parts Order",
"removefrompartsqueue": "Unqueue from Parts Queue?", "removefrompartsqueue": "Unqueue from Parts Queue?",
@@ -3077,7 +3081,7 @@
"purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)", "purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)",
"purchase_return_ratio_grouped_by_vendor_summary": "Purchase & Return Ratio by Vendor (Summary)", "purchase_return_ratio_grouped_by_vendor_summary": "Purchase & Return Ratio by Vendor (Summary)",
"purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)", "purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)",
"purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)", "purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)","purchases_by_date_excel": "Purchases by Date - Excel",
"purchases_by_date_range_detail": "Purchases by Date - Detail", "purchases_by_date_range_detail": "Purchases by Date - Detail",
"purchases_by_date_range_summary": "Purchases by Date - Summary", "purchases_by_date_range_summary": "Purchases by Date - Summary",
"purchases_by_ro_detail_date": "Purchases by RO - Detail", "purchases_by_ro_detail_date": "Purchases by RO - Detail",

View File

@@ -256,15 +256,6 @@
} }
}, },
"bodyshop": { "bodyshop": {
"operations": {
"starts_with": "",
"contains": "",
"ends_with": "",
"equals": "",
"not_equals": "",
"greater_than": "",
"less_than": ""
},
"actions": { "actions": {
"add_task_preset": "", "add_task_preset": "",
"addapptcolor": "", "addapptcolor": "",
@@ -388,10 +379,12 @@
"md_lost_sale_reasons": "", "md_lost_sale_reasons": "",
"md_parts_order_comment": "", "md_parts_order_comment": "",
"md_parts_scan": { "md_parts_scan": {
"caseInsensitive": "",
"expression": "",
"field": "", "field": "",
"flags": "",
"operation": "", "operation": "",
"value": "", "value": ""
"caseInsensitive": ""
}, },
"md_payment_types": "", "md_payment_types": "",
"md_referral_sources": "", "md_referral_sources": "",
@@ -721,6 +714,15 @@
"task-presets": "", "task-presets": "",
"workingdays": "" "workingdays": ""
}, },
"operations": {
"contains": "",
"ends_with": "",
"equals": "",
"greater_than": "",
"less_than": "",
"not_equals": "",
"starts_with": ""
},
"successes": { "successes": {
"areyousure": "", "areyousure": "",
"defaultviewcreated": "", "defaultviewcreated": "",
@@ -1434,11 +1436,13 @@
"act_price_before_ppc": "", "act_price_before_ppc": "",
"adjustment": "", "adjustment": "",
"ah_detail_line": "", "ah_detail_line": "",
"alt_partno": "",
"amount": "", "amount": "",
"assigned_team": "", "assigned_team": "",
"assigned_team_name": "", "assigned_team_name": "",
"create_ppc": "", "create_ppc": "",
"db_price": "Precio de base de datos", "db_price": "Precio de base de datos",
"include_in_part_cnt": "",
"lbr_types": { "lbr_types": {
"LA1": "", "LA1": "",
"LA2": "", "LA2": "",
@@ -1463,7 +1467,6 @@
"mod_lbr_ty": "Tipo de trabajo", "mod_lbr_ty": "Tipo de trabajo",
"notes": "", "notes": "",
"oem_partno": "OEM parte #", "oem_partno": "OEM parte #",
"alt_partno": "",
"op_code_desc": "", "op_code_desc": "",
"part_qty": "", "part_qty": "",
"part_type": "Tipo de parte", "part_type": "Tipo de parte",
@@ -2529,6 +2532,7 @@
"orderhistory": "Historial de pedidos", "orderhistory": "Historial de pedidos",
"parts_order": "", "parts_order": "",
"parts_orders": "", "parts_orders": "",
"parts_returns": "",
"print": "Mostrar formulario impreso", "print": "Mostrar formulario impreso",
"receive": "", "receive": "",
"removefrompartsqueue": "", "removefrompartsqueue": "",
@@ -3078,6 +3082,7 @@
"purchase_return_ratio_grouped_by_vendor_summary": "", "purchase_return_ratio_grouped_by_vendor_summary": "",
"purchases_by_cost_center_detail": "", "purchases_by_cost_center_detail": "",
"purchases_by_cost_center_summary": "", "purchases_by_cost_center_summary": "",
"purchases_by_date_excel": "",
"purchases_by_date_range_detail": "", "purchases_by_date_range_detail": "",
"purchases_by_date_range_summary": "", "purchases_by_date_range_summary": "",
"purchases_by_ro_detail_date": "", "purchases_by_ro_detail_date": "",

View File

@@ -256,15 +256,6 @@
} }
}, },
"bodyshop": { "bodyshop": {
"operations": {
"starts_with": "",
"contains": "",
"ends_with": "",
"equals": "",
"not_equals": "",
"greater_than": "",
"less_than": ""
},
"actions": { "actions": {
"add_task_preset": "", "add_task_preset": "",
"addapptcolor": "", "addapptcolor": "",
@@ -388,10 +379,12 @@
"md_lost_sale_reasons": "", "md_lost_sale_reasons": "",
"md_parts_order_comment": "", "md_parts_order_comment": "",
"md_parts_scan": { "md_parts_scan": {
"caseInsensitive": "",
"expression": "",
"field": "", "field": "",
"flags": "",
"operation": "", "operation": "",
"value": "", "value": ""
"caseInsensitive": ""
}, },
"md_payment_types": "", "md_payment_types": "",
"md_referral_sources": "", "md_referral_sources": "",
@@ -721,6 +714,15 @@
"task-presets": "", "task-presets": "",
"workingdays": "" "workingdays": ""
}, },
"operations": {
"contains": "",
"ends_with": "",
"equals": "",
"greater_than": "",
"less_than": "",
"not_equals": "",
"starts_with": ""
},
"successes": { "successes": {
"areyousure": "", "areyousure": "",
"defaultviewcreated": "", "defaultviewcreated": "",
@@ -1434,11 +1436,13 @@
"act_price_before_ppc": "", "act_price_before_ppc": "",
"adjustment": "", "adjustment": "",
"ah_detail_line": "", "ah_detail_line": "",
"alt_partno": "",
"amount": "", "amount": "",
"assigned_team": "", "assigned_team": "",
"assigned_team_name": "", "assigned_team_name": "",
"create_ppc": "", "create_ppc": "",
"db_price": "Prix de la base de données", "db_price": "Prix de la base de données",
"include_in_part_cnt": "",
"lbr_types": { "lbr_types": {
"LA1": "", "LA1": "",
"LA2": "", "LA2": "",
@@ -1463,7 +1467,6 @@
"mod_lbr_ty": "Type de travail", "mod_lbr_ty": "Type de travail",
"notes": "", "notes": "",
"oem_partno": "Pièce OEM #", "oem_partno": "Pièce OEM #",
"alt_partno": "",
"op_code_desc": "", "op_code_desc": "",
"part_qty": "", "part_qty": "",
"part_type": "Type de pièce", "part_type": "Type de pièce",
@@ -2529,6 +2532,7 @@
"orderhistory": "Historique des commandes", "orderhistory": "Historique des commandes",
"parts_order": "", "parts_order": "",
"parts_orders": "", "parts_orders": "",
"parts_returns": "",
"print": "Afficher le formulaire imprimé", "print": "Afficher le formulaire imprimé",
"receive": "", "receive": "",
"removefrompartsqueue": "", "removefrompartsqueue": "",
@@ -3078,6 +3082,7 @@
"purchase_return_ratio_grouped_by_vendor_summary": "", "purchase_return_ratio_grouped_by_vendor_summary": "",
"purchases_by_cost_center_detail": "", "purchases_by_cost_center_detail": "",
"purchases_by_cost_center_summary": "", "purchases_by_cost_center_summary": "",
"purchases_by_date_excel": "",
"purchases_by_date_range_detail": "", "purchases_by_date_range_detail": "",
"purchases_by_date_range_summary": "", "purchases_by_date_range_summary": "",
"purchases_by_ro_detail_date": "", "purchases_by_ro_detail_date": "",

View File

@@ -1444,7 +1444,8 @@ export const TemplateList = (type, context) => {
object: i18n.t("reportcenter.labels.objects.jobs"), object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_exported") field: i18n.t("jobs.fields.date_exported")
}, },
group: "sales" group: "sales",
featureNameRestricted: "export"
}, },
gsr_by_estimator: { gsr_by_estimator: {
title: i18n.t("reportcenter.templates.gsr_by_estimator"), title: i18n.t("reportcenter.templates.gsr_by_estimator"),
@@ -1865,7 +1866,8 @@ export const TemplateList = (type, context) => {
object: i18n.t("reportcenter.labels.objects.jobs"), object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_open") field: i18n.t("jobs.fields.date_open")
}, },
group: "jobs" group: "jobs",
featureNameRestricted: "bills"
}, },
psr_by_make: { psr_by_make: {
title: i18n.t("reportcenter.templates.psr_by_make"), title: i18n.t("reportcenter.templates.psr_by_make"),
@@ -1901,7 +1903,8 @@ export const TemplateList = (type, context) => {
object: i18n.t("reportcenter.labels.objects.parts_orders"), object: i18n.t("reportcenter.labels.objects.parts_orders"),
field: i18n.t("parts_orders.fields.order_date") field: i18n.t("parts_orders.fields.order_date")
}, },
group: "jobs" group: "jobs",
featureNameRestricted: "bills"
}, },
returns_grouped_by_vendor_detailed: { returns_grouped_by_vendor_detailed: {
title: i18n.t("reportcenter.templates.returns_grouped_by_vendor_detailed"), title: i18n.t("reportcenter.templates.returns_grouped_by_vendor_detailed"),
@@ -1913,7 +1916,8 @@ export const TemplateList = (type, context) => {
object: i18n.t("reportcenter.labels.objects.parts_orders"), object: i18n.t("reportcenter.labels.objects.parts_orders"),
field: i18n.t("parts_orders.fields.order_date") field: i18n.t("parts_orders.fields.order_date")
}, },
group: "jobs" group: "jobs",
featureNameRestricted: "bills"
}, },
scheduled_parts_list: { scheduled_parts_list: {
title: i18n.t("reportcenter.templates.scheduled_parts_list"), title: i18n.t("reportcenter.templates.scheduled_parts_list"),
@@ -2225,7 +2229,19 @@ export const TemplateList = (type, context) => {
field: i18n.t("jobs.fields.date_open") field: i18n.t("jobs.fields.date_open")
}, },
group: "jobs" group: "jobs"
} },
purchases_by_date_excel: {
title: i18n.t("reportcenter.templates.purchases_by_date_excel"),
subject: i18n.t("reportcenter.templates.purchases_by_date_excel"),
key: "purchases_by_date_excel",
reporttype: "excel",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.bills"),
field: i18n.t("bills.fields.date")
},
group: "purchases"
},
} }
: {}), : {}),
...(!type || type === "courtesycarcontract" ...(!type || type === "courtesycarcontract"

View File

@@ -2997,6 +2997,7 @@
- est_seq - est_seq
- glass_flag - glass_flag
- id - id
- include_in_part_cnt
- ioucreated - ioucreated
- jobid - jobid
- lbr_amt - lbr_amt
@@ -3066,6 +3067,7 @@
- est_seq - est_seq
- glass_flag - glass_flag
- id - id
- include_in_part_cnt
- ioucreated - ioucreated
- jobid - jobid
- lbr_amt - lbr_amt
@@ -3146,6 +3148,7 @@
- est_seq - est_seq
- glass_flag - glass_flag
- id - id
- include_in_part_cnt
- ioucreated - ioucreated
- jobid - jobid
- lbr_amt - lbr_amt

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE VIEW "public"."joblines_status" AS
-- SELECT j.jobid,
-- j.status,
-- count(1) AS count,
-- j.part_type
-- FROM joblines j
-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND ((j.part_qty)::numeric <> (0)::numeric) AND ((j.act_price <> (0)::numeric) OR (j.include_in_part_cnt is TRUE))AND (j.removed IS FALSE))
-- GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE VIEW "public"."joblines_status" AS
SELECT j.jobid,
j.status,
count(1) AS count,
j.part_type
FROM joblines j
WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND ((j.part_qty)::numeric <> (0)::numeric) AND ((j.act_price <> (0)::numeric) OR (j.include_in_part_cnt is TRUE))AND (j.removed IS FALSE))
GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -0,0 +1,10 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE VIEW "public"."joblines_status" AS
-- SELECT j.jobid,
-- j.status,
-- count(1) AS count,
-- j.part_type
-- FROM joblines j
-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND ((j.part_qty)::numeric <> (0)::numeric) AND ((j.act_price <> (0)::numeric) OR (j.include_in_part_cnt is TRUE))AND (j.removed IS FALSE))
-- GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE VIEW "public"."joblines_status" AS
SELECT j.jobid,
j.status,
count(1) AS count,
j.part_type
FROM joblines j
WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND ((j.part_qty)::numeric <> (0)::numeric) AND ((j.act_price <> (0)::numeric) OR (j.include_in_part_cnt is TRUE))AND (j.removed IS FALSE))
GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -329,7 +329,8 @@ const main = async () => {
main().catch((error) => { main().catch((error) => {
logger.log(`Main-API-Error: Something was not caught in the application.`, "error", "api", null, { logger.log(`Main-API-Error: Something was not caught in the application.`, "error", "api", null, {
error: error.message, error: error.message,
errorjson: JSON.stringify(error) errorjson: JSON.stringify(error),
stack: error.stack
}); });
// Note: If we want the app to crash on all uncaught async operations, we would // Note: If we want the app to crash on all uncaught async operations, we would
// need to put a `process.exit(1);` here // need to put a `process.exit(1);` here

View File

@@ -3,3 +3,4 @@ exports.autohouse = require("./autohouse").default;
exports.chatter = require("./chatter").default; exports.chatter = require("./chatter").default;
exports.claimscorp = require("./claimscorp").default; exports.claimscorp = require("./claimscorp").default;
exports.kaizen = require("./kaizen").default; exports.kaizen = require("./kaizen").default;
exports.usageReport = require("./usageReport").default;

View File

@@ -0,0 +1,90 @@
const path = require("path");
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
const client = require("../graphql-client/graphql-client").client;
const emailer = require("../email/sendemail");
const moment = require("moment-timezone");
const converter = require("json-2-csv");
const logger = require("../utils/logger");
const queries = require("../graphql-client/queries");
const InstanceMgr = require("../utils/instanceMgr").default;
exports.default = async (req, res) => {
try {
logger.log("usage-report-email-start", "debug", req?.user?.email, null, {});
if (InstanceMgr({ rome: false, imex: true })) {
//Disable for ImEX at the moment.
res.sendStatus(403);
logger.log("usage-report-email-forbidden", "warn", req?.user?.email, null, {});
return;
}
if (process.env.NODE_ENV !== "production") {
res.sendStatus(403);
return;
}
// Validate using autohouse token header.
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
res.sendStatus(401);
logger.log("usage-report-email-forbidden", "warn", req?.user?.email, null, {});
return;
}
//Query the usage data.
const queryResults = await client.request(queries.STATUS_UPDATE, {
today: moment().startOf("day").subtract(7, "days"),
period: moment().subtract(90, "days").startOf("day")
});
//Massage the data.
const shopList = queryResults.bodyshops.map((shop) => ({
"Shop Name": shop.shopname,
"Days Since Creation": moment().diff(moment(shop.created_at), "days"),
"Jobs Created": shop.jobs_created.aggregate.count,
"Jobs Updated": shop.jobs_updated.aggregate.count,
"Owners Created": shop.owners_created.aggregate.count,
"Owners Updated": shop.owners_updated.aggregate.count,
"Vehicles Created": shop.vehicles_created.aggregate.count,
"Vehicles Updated": shop.vehicles_updated.aggregate.count,
"Tasks Created": shop.tasks_created.aggregate.count,
"Tasks Updated": shop.tasks_updated.aggregate.count
}));
const csv = converter.json2csv(shopList, { emptyFieldValue: "" });
emailer
.sendTaskEmail({
to: ["patrick.fic@convenient-brands.com", "bradley.rhoades@convenient-brands.com"],
subject: `RO Usage Report - ${moment().format("MM/DD/YYYY")}`,
text: `
Usage Report for ${moment().format("MM/DD/YYYY")} for Rome Online Customers.
Notes:
- Days Since Creation: The number of days since the shop was created. Only shops created in the last 90 days are included.
- Updated values should be higher than created values.
- Counts are inclusive of the last 7 days of data.
`,
attachments: [{ filename: `RO Usage Report ${moment().format("MM/DD/YYYY")}.csv`, content: csv }]
})
.then(() => {
logger.log("usage-report-email-success", "debug", req?.user?.email, null, {
csv
});
})
.catch((error) => {
logger.log("usage-report-email-send-error", "ERROR", req?.user?.email, null, {
error: error.message,
stack: error.stack
});
});
res.sendStatus(200);
return;
} catch (error) {
logger.log("usage-report-email-error", "ERROR", req?.user?.email, null, {
error: error.message,
stack: error.stack
});
res.status(500).json({ error: error.message, stack: error.stack });
}
};

View File

@@ -2617,3 +2617,76 @@ exports.CREATE_CONVERSATION = `mutation CREATE_CONVERSATION($conversation: [conv
} }
} }
`; `;
exports.STATUS_UPDATE = `query STATUS_UPDATE($period: timestamptz!, $today: timestamptz!) {
bodyshops(where: { created_at: { _gte: $period } }) {
shopname
id
created_at
jobs_created: jobs_aggregate(where: { created_at: { _gte: $today } }) {
aggregate {
count
}
}
jobs_updated: jobs_aggregate(where: { updated_at: { _gte: $today } }) {
aggregate {
count
}
}
owners_created: owners_aggregate(where: { created_at: { _gte: $today } }) {
aggregate {
count
}
}
owners_updated: owners_aggregate(where: { updated_at: { _gte: $today } }) {
aggregate {
count
}
}
vehicles_created: vehicles_aggregate(
where: { created_at: { _gte: $today } }
) {
aggregate {
count
}
}
vehicles_updated: vehicles_aggregate(
where: { updated_at: { _gte: $today } }
) {
aggregate {
count
}
}
tasks_created: tasks_aggregate(where: { created_at: { _gte: $today } }) {
aggregate {
count
}
}
tasks_updated: tasks_aggregate(where: { updated_at: { _gte: $today } }) {
aggregate {
count
}
}
jobs {
parts_orders_created: parts_orders_aggregate(
where: { created_at: { _gte: $today } }
) {
aggregate {
count
}
}
parts_orders_updated: parts_orders_aggregate(
where: { updated_at: { _gte: $today } }
) {
aggregate {
count
}
}
}
}
}
`

View File

@@ -818,6 +818,7 @@ function CalculateTaxesTotals(job, otherTotals) {
PAG: Dinero(), PAG: Dinero(),
PAO: Dinero(), PAO: Dinero(),
PAS: Dinero(), PAS: Dinero(),
PASL: Dinero(),
PAP: Dinero(), PAP: Dinero(),
PAM: Dinero(), PAM: Dinero(),

View File

@@ -1,10 +1,11 @@
const express = require("express"); const express = require("express");
const router = express.Router(); const router = express.Router();
const { autohouse, claimscorp, chatter, kaizen } = require("../data/data"); const { autohouse, claimscorp, chatter, kaizen, usageReport } = require("../data/data");
router.post("/ah", autohouse); router.post("/ah", autohouse);
router.post("/cc", claimscorp); router.post("/cc", claimscorp);
router.post("/chatter", chatter); router.post("/chatter", chatter);
router.post("/kaizen", kaizen); router.post("/kaizen", kaizen);
router.post("/usagereport", usageReport);
module.exports = router; module.exports = router;

View File

@@ -41,7 +41,7 @@ exports.taskHandler = async (req, res) => {
return res.status(200).send(csv); return res.status(200).send(csv);
} catch (error) { } catch (error) {
res.status(500).json({ error: error.message, stack: error.stackTrace }); res.status(500).json({ error: error.message, stack: error.stack });
} }
}; };