Compare commits
59 Commits
release/20
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8cda88a33 | ||
|
|
71a74c5437 | ||
|
|
6fe4d982f5 | ||
|
|
5ec032d8d6 | ||
|
|
2718a66fb0 | ||
|
|
4c737371e3 | ||
|
|
ee7a3d0bdf | ||
|
|
181af581e5 | ||
|
|
11e2f5d83d | ||
|
|
cbc723fa38 | ||
|
|
95c310119f | ||
|
|
ebe1facbd1 | ||
|
|
b021992552 | ||
|
|
0e218abbf4 | ||
|
|
0cf7961d7d | ||
|
|
ca02937461 | ||
|
|
fa7e0a107b | ||
|
|
9ccffd73ba | ||
|
|
f68ad181e4 | ||
|
|
23bd6085a8 | ||
|
|
e4325e39bf | ||
|
|
584c2e5de2 | ||
|
|
eccc992cfa | ||
|
|
f293e80d0d | ||
|
|
eea2a758b0 | ||
|
|
8d7d4ab4ac | ||
|
|
f9105806ba | ||
|
|
acd278f5b4 | ||
|
|
a4a6fac224 | ||
|
|
b2b7064007 | ||
|
|
4a56fbb135 | ||
|
|
e1fcb0ecba | ||
|
|
ee5e091118 | ||
|
|
9f46f8ec31 | ||
|
|
8c737ff0ae | ||
|
|
a8ef681cf1 | ||
|
|
494c8b6867 | ||
|
|
262b5600d8 | ||
|
|
a06d3c9365 | ||
|
|
1861bd68d1 | ||
|
|
348561c812 | ||
|
|
bf4a52b3c1 | ||
|
|
1950023f37 | ||
|
|
a2798a02b3 | ||
|
|
5fcb5a3a3e | ||
|
|
ba41b29538 | ||
|
|
8a0916a47f | ||
|
|
d187ed6f73 | ||
|
|
13a57406d9 | ||
|
|
3bc6504ae6 | ||
|
|
c7f293ceca | ||
|
|
66e60e96ad | ||
|
|
54a9beb37f | ||
|
|
779cc7d9e8 | ||
|
|
c1b3df9c3b | ||
|
|
80379cdd79 | ||
|
|
e2e5f3f885 | ||
|
|
91c3ac56fa | ||
|
|
7d21cb7d70 |
@@ -6369,6 +6369,27 @@
|
||||
<folder_node>
|
||||
<name>md_parts_scan</name>
|
||||
<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>
|
||||
<name>expression</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -6390,6 +6411,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>flags</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -6411,6 +6453,48 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
@@ -11987,6 +12071,158 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</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>
|
||||
<name>successes</name>
|
||||
<children>
|
||||
@@ -23131,6 +23367,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>amount</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -23236,6 +23493,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>lbr_types</name>
|
||||
<children>
|
||||
|
||||
@@ -62,7 +62,17 @@
|
||||
t = d.getElementsByTagName("script")[0];
|
||||
t.parentNode.insertBefore(s, t);
|
||||
</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>
|
||||
!(function () {
|
||||
|
||||
@@ -85,6 +85,17 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, payments,
|
||||
sortOrder: state.sortedInfo.columnKey === "amount" && state.sortedInfo.order,
|
||||
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"),
|
||||
dataIndex: "memo",
|
||||
|
||||
@@ -133,15 +133,19 @@ const CardPaymentModalComponent = ({
|
||||
if (window.intellipay) {
|
||||
// eslint-disable-next-line no-eval
|
||||
eval(response.data);
|
||||
SetIntellipayCallbackFunctions();
|
||||
window.intellipay.autoOpen();
|
||||
pollForIntelliPay(() => {
|
||||
SetIntellipayCallbackFunctions();
|
||||
window.intellipay.autoOpen();
|
||||
});
|
||||
} else {
|
||||
const rg = document.createRange();
|
||||
const node = rg.createContextualFragment(response.data);
|
||||
document.documentElement.appendChild(node);
|
||||
SetIntellipayCallbackFunctions();
|
||||
window.intellipay.isAutoOpen = true;
|
||||
window.intellipay.initialize();
|
||||
pollForIntelliPay(() => {
|
||||
SetIntellipayCallbackFunctions();
|
||||
window.intellipay.isAutoOpen = true;
|
||||
window.intellipay.initialize();
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
notification.open({
|
||||
@@ -345,3 +349,27 @@ const 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();
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectCardPayment } from "../../redux/modals/modals.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({
|
||||
cardPaymentModal: selectCardPayment,
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { Button, Col, Collapse, Result, Row, Space } from "antd";
|
||||
import React from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
|
||||
@@ -38,28 +38,23 @@ class ErrorBoundary extends React.Component {
|
||||
}
|
||||
|
||||
handleErrorSubmit = () => {
|
||||
InstanceRenderManager({
|
||||
executeFunction: true,
|
||||
args: [],
|
||||
imex: () => {
|
||||
window.$crisp.push([
|
||||
"do",
|
||||
"message:send",
|
||||
[
|
||||
"text",
|
||||
`I hit the following error: \n\n
|
||||
window.$crisp.push([
|
||||
"do",
|
||||
"message:send",
|
||||
[
|
||||
"text",
|
||||
`I hit the following error: \n\n
|
||||
${this.state.error.message}\n\n
|
||||
${this.state.error.stack}\n\n
|
||||
URL:${window.location} as ${this.props.currentUser.email} for ${
|
||||
this.props.bodyshop && this.props.bodyshop.name
|
||||
}
|
||||
`
|
||||
]
|
||||
]);
|
||||
]
|
||||
]);
|
||||
|
||||
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**
|
||||
|
||||
// ----
|
||||
|
||||
@@ -650,14 +650,7 @@ function Header({
|
||||
icon: <Icon component={QuestionCircleFilled} />,
|
||||
label: t("menus.header.help"),
|
||||
onClick: () => {
|
||||
window.open(
|
||||
InstanceRenderManager({
|
||||
imex: "https://help.imex.online/",
|
||||
rome: "https://rometech.com//"
|
||||
}),
|
||||
|
||||
"_blank"
|
||||
);
|
||||
window.open("https://help.imex.online/", "_blank");
|
||||
}
|
||||
},
|
||||
...(InstanceRenderManager({
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import { AlertFilled } from "@ant-design/icons";
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Divider, Dropdown, Form, Input, notification, Popover, Select, Space } from "antd";
|
||||
import parsePhoneNumber from "libphonenumber-js";
|
||||
import dayjs from "../../utils/day";
|
||||
import queryString from "query-string";
|
||||
import React, { useContext, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
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 { setModalContext } from "../../redux/modals/modals.actions";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
import dayjs from "../../utils/day";
|
||||
import { GenerateDocument } from "../../utils/RenderTemplate";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
||||
import DataLabel from "../data-label/data-label.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 ScheduleAtChange from "./job-at-change.component";
|
||||
import ScheduleEventColor from "./schedule-event.color.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({
|
||||
bodyshop: selectBodyshop
|
||||
@@ -127,6 +127,7 @@ export function ScheduleEventComponent({
|
||||
<DataLabel label={t("jobs.fields.ownr_ph2")}>
|
||||
<ChatOpenButton phone={event.job && event.job.ownr_ph2} jobid={event.job.id} />
|
||||
</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")}>
|
||||
{(event.job && event.job.alt_transport) || ""}
|
||||
<ScheduleAtChange job={event && event.job} />
|
||||
|
||||
@@ -519,6 +519,7 @@ export function JobLinesComponent({
|
||||
{selectedLines.length > 0 && ` (${selectedLines.length})`}
|
||||
</Button>
|
||||
<Button
|
||||
id="job-lines-order-parts-button"
|
||||
disabled={(job && !job.converted) || (selectedLines.length > 0 ? false : true) || jobRO || technician}
|
||||
onClick={() => {
|
||||
setPartsOrderContext({
|
||||
@@ -541,6 +542,7 @@ export function JobLinesComponent({
|
||||
{selectedLines.length > 0 && ` (${selectedLines.length})`}
|
||||
</Button>
|
||||
<Button
|
||||
id="job-lines-filter-parts-only-button"
|
||||
onClick={() => {
|
||||
setState((state) => ({
|
||||
...state,
|
||||
@@ -554,7 +556,7 @@ export function JobLinesComponent({
|
||||
<FilterFilled /> {t("jobs.actions.filterpartsonly")}
|
||||
</Button>
|
||||
<Dropdown menu={markMenu} trigger={["click"]}>
|
||||
<Button>{t("jobs.actions.mark")}</Button>
|
||||
<Button id="repair-data-mark-button">{t("jobs.actions.mark")}</Button>
|
||||
</Dropdown>
|
||||
<Button
|
||||
disabled={jobRO || technician}
|
||||
|
||||
@@ -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}>
|
||||
<Switch />
|
||||
</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>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
@@ -173,7 +173,7 @@ export function JobsCloseExportButton({
|
||||
}
|
||||
});
|
||||
|
||||
if (!!!jobUpdateResponse.errors) {
|
||||
if (!jobUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
@@ -222,7 +222,7 @@ export function JobsCloseExportButton({
|
||||
};
|
||||
|
||||
return (
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled} type="primary">
|
||||
{t("jobs.actions.export")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useMutation } from "@apollo/client";
|
||||
import { Button, Form, Input, notification, Popover, Select, Space, Switch } from "antd";
|
||||
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 { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
@@ -18,7 +19,14 @@ const mapStateToProps = createStructuredSelector({
|
||||
jobRO: selectJobReadOnly
|
||||
});
|
||||
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 }) {
|
||||
@@ -27,6 +35,7 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
|
||||
const [mutationConvertJob] = useMutation(CONVERT_JOB_TO_RO);
|
||||
const { t } = useTranslation();
|
||||
const [form] = Form.useForm();
|
||||
const allFormValues = Form.useWatch([], form);
|
||||
|
||||
const handleConvert = async ({ employee_csr, category, ...values }) => {
|
||||
if (parentFormIsFieldsTouched()) {
|
||||
@@ -69,6 +78,8 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const submitDisabled = useCallback(() => some(allFormValues, (v) => v === undefined), [allFormValues]);
|
||||
|
||||
const popMenu = (
|
||||
<div>
|
||||
<Form
|
||||
@@ -77,9 +88,12 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
|
||||
onFinish={handleConvert}
|
||||
initialValues={{
|
||||
driveable: true,
|
||||
towin: false,
|
||||
towin: job.towin,
|
||||
ca_gst_registrant: job.ca_gst_registrant,
|
||||
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
|
||||
@@ -209,7 +223,7 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<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")}
|
||||
</Button>
|
||||
<Button onClick={() => setOpen(false)}>{t("general.actions.close")}</Button>
|
||||
@@ -231,11 +245,6 @@ export function JobsConvertButton({ bodyshop, job, refetch, jobRO, insertAuditTr
|
||||
loading={loading}
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
form.setFieldsValue({
|
||||
driveable: true,
|
||||
towin: false,
|
||||
employee_csr: job.employee_csr
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("jobs.actions.convert")}
|
||||
|
||||
@@ -982,7 +982,7 @@ export function JobsDetailRatesParts({ jobRO, expanded, required = true, form })
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow>
|
||||
<LayoutFormRow header={t("jobs.labels.cieca_pfo")}>
|
||||
<Form.Item label={t("jobs.fields.tax_tow_rt")} name="tax_tow_rt">
|
||||
<InputNumber min={0} max={100} precision={4} disabled={jobRO} />
|
||||
</Form.Item>
|
||||
|
||||
@@ -10,8 +10,8 @@ import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { INSERT_EXPORT_LOG } from "../../graphql/accounting.queries";
|
||||
import { UPDATE_JOBS } from "../../graphql/jobs.queries";
|
||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
@@ -165,7 +165,7 @@ export function JobsExportAllButton({
|
||||
}
|
||||
});
|
||||
|
||||
if (!!!jobUpdateResponse.errors) {
|
||||
if (!jobUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "jobsuccessexport",
|
||||
@@ -213,13 +213,13 @@ export function JobsExportAllButton({
|
||||
})
|
||||
);
|
||||
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (completedCallback) completedCallback([]);
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
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")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { EditFilled } from "@ant-design/icons";
|
||||
import { Alert, Card, Col, Row, Space, Table, Typography } from "antd";
|
||||
import _ from "lodash";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
@@ -9,11 +8,11 @@ import { selectTechnician } from "../../redux/tech/tech.selectors";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||
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 UpsellComponent, { upsellEnum } from "../upsell/upsell.component";
|
||||
import "./labor-allocations-table.styles.scss";
|
||||
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({
|
||||
bodyshop: selectBodyshop,
|
||||
technician: selectTechnician
|
||||
@@ -65,6 +64,7 @@ export function LaborAllocationsTable({
|
||||
key: "total",
|
||||
sorter: (a, b) => a.total - b.total,
|
||||
sortOrder: state.sortedInfo.columnKey === "total" && state.sortedInfo.order,
|
||||
align: "right",
|
||||
render: (text, record) => record.total.toFixed(1)
|
||||
},
|
||||
{
|
||||
@@ -73,6 +73,7 @@ export function LaborAllocationsTable({
|
||||
key: "hrs_claimed",
|
||||
sorter: (a, b) => a.claimed - b.claimed,
|
||||
sortOrder: state.sortedInfo.columnKey === "claimed" && state.sortedInfo.order,
|
||||
align: "right",
|
||||
render: (text, record) => record.claimed && record.claimed.toFixed(1)
|
||||
},
|
||||
{
|
||||
@@ -81,6 +82,7 @@ export function LaborAllocationsTable({
|
||||
key: "adjustments",
|
||||
sorter: (a, b) => a.adjustments - b.adjustments,
|
||||
sortOrder: state.sortedInfo.columnKey === "adjustments" && state.sortedInfo.order,
|
||||
align: "right",
|
||||
render: (text, record) => (
|
||||
<Space wrap>
|
||||
{record.adjustments.toFixed(1)}
|
||||
@@ -100,17 +102,17 @@ export function LaborAllocationsTable({
|
||||
{
|
||||
title: t("jobs.labels.difference"),
|
||||
dataIndex: "difference",
|
||||
|
||||
key: "difference",
|
||||
sorter: (a, b) => a.difference - b.difference,
|
||||
sortOrder: state.sortedInfo.columnKey === "difference" && state.sortedInfo.order,
|
||||
align: "right",
|
||||
render: (text, record) => (
|
||||
<strong
|
||||
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>
|
||||
)
|
||||
}
|
||||
@@ -129,7 +131,6 @@ export function LaborAllocationsTable({
|
||||
ellipsis: true,
|
||||
render: (text, record) => `${record.op_code_desc || ""}${record.alt_partm ? ` ${record.alt_partm}` : ""}`
|
||||
},
|
||||
|
||||
{
|
||||
title: t("joblines.fields.act_price"),
|
||||
dataIndex: "act_price",
|
||||
@@ -187,7 +188,7 @@ export function LaborAllocationsTable({
|
||||
{ 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") });
|
||||
}
|
||||
|
||||
@@ -217,19 +218,21 @@ export function LaborAllocationsTable({
|
||||
summary={() => (
|
||||
<Table.Summary.Row>
|
||||
<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>{summary.hrs_total.toFixed(1)}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell>{summary.hrs_claimed.toFixed(1)}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell>{summary.adjustments.toFixed(1)}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell>
|
||||
<Table.Summary.Cell align="right">{summary.hrs_total.toFixed(1)}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell align="right">{summary.hrs_claimed.toFixed(1)}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell align="right">{summary.adjustments.toFixed(1)}</Table.Summary.Cell>
|
||||
<Table.Summary.Cell align="right">
|
||||
<Typography.Text
|
||||
style={{
|
||||
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>
|
||||
</Table.Summary.Cell>
|
||||
</Table.Summary.Row>
|
||||
@@ -261,11 +264,10 @@ export function LaborAllocationsTable({
|
||||
</Card>
|
||||
</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")} />
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(LaborAllocationsTable);
|
||||
|
||||
@@ -38,7 +38,11 @@ export default function OwnerFindModalContainer({
|
||||
}, [callSearchowners, modalProps.open, owner]);
|
||||
|
||||
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}
|
||||
{error ? <AlertComponent message={error.message} type="error" /> : null}
|
||||
{owner ? (
|
||||
|
||||
@@ -48,7 +48,7 @@ export function PayableExportAll({
|
||||
let PartnerResponse;
|
||||
|
||||
setLoading(true);
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
if (loadingCallback) loadingCallback(true);
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||
PartnerResponse = await axios.post(`/qbo/payables`, {
|
||||
bills: billids,
|
||||
@@ -85,7 +85,7 @@ export function PayableExportAll({
|
||||
notification["error"]({
|
||||
message: t("bills.errors.exporting-partner")
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -152,7 +152,7 @@ export function PayableExportAll({
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!!!billUpdateResponse.errors) {
|
||||
if (!billUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "billsuccessexport",
|
||||
@@ -187,8 +187,8 @@ export function PayableExportAll({
|
||||
});
|
||||
|
||||
await Promise.all(proms);
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (completedCallback) completedCallback([]);
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
@@ -200,7 +200,7 @@ export function PayableExportAll({
|
||||
);
|
||||
|
||||
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")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -46,7 +46,7 @@ export function PayableExportButton({
|
||||
logImEXEvent("accounting_export_payable");
|
||||
|
||||
setLoading(true);
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
if (loadingCallback) loadingCallback(true);
|
||||
|
||||
//Check if it's a QBO Setup.
|
||||
let PartnerResponse;
|
||||
@@ -88,7 +88,7 @@ export function PayableExportButton({
|
||||
notification["error"]({
|
||||
message: t("bills.errors.exporting-partner")
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ export function PayableExportButton({
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!!!billUpdateResponse.errors) {
|
||||
if (!billUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "billsuccessexport",
|
||||
@@ -186,7 +186,7 @@ export function PayableExportButton({
|
||||
}
|
||||
}
|
||||
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
@@ -198,7 +198,7 @@ export function PayableExportButton({
|
||||
);
|
||||
|
||||
return (
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled} type="primary">
|
||||
{t("jobs.actions.export")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -55,7 +55,7 @@ export function PaymentExportButton({
|
||||
} else {
|
||||
//Default is QBD
|
||||
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
if (loadingCallback) loadingCallback(true);
|
||||
|
||||
let QbXmlResponse;
|
||||
try {
|
||||
@@ -88,7 +88,7 @@ export function PaymentExportButton({
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting-partner")
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -148,7 +148,7 @@ export function PaymentExportButton({
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!!!paymentUpdateResponse.errors) {
|
||||
if (!paymentUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "paymentsuccessexport",
|
||||
@@ -184,12 +184,12 @@ export function PaymentExportButton({
|
||||
)
|
||||
]);
|
||||
}
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled}>
|
||||
<Button onClick={handleQbxml} loading={loading} disabled={disabled} type="primary">
|
||||
{t("jobs.actions.export")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -44,7 +44,7 @@ export function PaymentsExportAllButton({
|
||||
|
||||
const handleQbxml = async () => {
|
||||
setLoading(true);
|
||||
if (!!loadingCallback) loadingCallback(true);
|
||||
if (loadingCallback) loadingCallback(true);
|
||||
let PartnerResponse;
|
||||
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||
PartnerResponse = await axios.post(`/qbo/payments`, {
|
||||
@@ -76,7 +76,7 @@ export function PaymentsExportAllButton({
|
||||
notification["error"]({
|
||||
message: t("payments.errors.exporting-partner")
|
||||
});
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
@@ -140,7 +140,7 @@ export function PaymentsExportAllButton({
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!!!paymentUpdateResponse.errors) {
|
||||
if (!paymentUpdateResponse.errors) {
|
||||
notification.open({
|
||||
type: "success",
|
||||
key: "paymentsuccessexport",
|
||||
@@ -174,13 +174,13 @@ export function PaymentsExportAllButton({
|
||||
);
|
||||
});
|
||||
await Promise.all(proms);
|
||||
if (!!completedCallback) completedCallback([]);
|
||||
if (!!loadingCallback) loadingCallback(false);
|
||||
if (completedCallback) completedCallback([]);
|
||||
if (loadingCallback) loadingCallback(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
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")}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -555,7 +555,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
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={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
@@ -567,9 +567,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name={["md_email_cc", "parts_return_slip"]}
|
||||
label={t("bodyshop.fields.md_email_cc", {
|
||||
template: "parts_return_slip"
|
||||
})}
|
||||
label={t("bodyshop.fields.md_email_cc", { template: "parts_returns" })}
|
||||
rules={[
|
||||
{
|
||||
//message: t("general.validation.required"),
|
||||
@@ -1261,7 +1259,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
||||
key={`${index}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
|
||||
label={t("joblines.fields.ah_detail_line")}
|
||||
|
||||
@@ -73,6 +73,7 @@ export const QUERY_PAYMENTS_FOR_EXPORT = gql`
|
||||
transactionid
|
||||
paymentnum
|
||||
date
|
||||
type
|
||||
exportlogs {
|
||||
id
|
||||
successful
|
||||
|
||||
@@ -49,6 +49,7 @@ export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
|
||||
est_ct_fn
|
||||
est_ct_ln
|
||||
comment
|
||||
loss_of_use
|
||||
labhrs: joblines_aggregate(where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }) {
|
||||
aggregate {
|
||||
sum {
|
||||
|
||||
@@ -238,6 +238,7 @@ export const UPDATE_JOB_LINE = gql`
|
||||
convertedtolbr
|
||||
convertedtolbr_data
|
||||
assigned_team
|
||||
include_in_part_cnt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,6 +581,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
status
|
||||
tax_part
|
||||
unq_seq
|
||||
include_in_part_cnt
|
||||
}
|
||||
kmin
|
||||
kmout
|
||||
|
||||
@@ -31,7 +31,7 @@ import { addAlerts } from "../../redux/application/application.actions.js";
|
||||
const JobsPage = lazy(() => import("../jobs/jobs.page"));
|
||||
|
||||
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"));
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
updateCurrentUser
|
||||
} from "../../firebase/firebase.utils";
|
||||
import { QUERY_EULA } from "../../graphql/bodyshop.queries";
|
||||
import cleanAxios from "../../utils/CleanAxios";
|
||||
import client from "../../utils/GraphQLClient";
|
||||
import dayjs from "../../utils/day";
|
||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||
@@ -47,7 +48,6 @@ import {
|
||||
validatePasswordResetSuccess
|
||||
} from "./user.actions";
|
||||
import UserActionTypes from "./user.types";
|
||||
import cleanAxios from "../../utils/CleanAxios";
|
||||
|
||||
const fpPromise = FingerprintJS.load();
|
||||
|
||||
@@ -234,16 +234,18 @@ export function* signInSuccessSaga({ payload }) {
|
||||
LogRocket.identify(payload.email);
|
||||
|
||||
try {
|
||||
window.$crisp.push(["set", "user:nickname", [payload.displayName || payload.email]]);
|
||||
window.$crisp.push(["set", "session:segments", [["user"]]]);
|
||||
InstanceRenderManager({
|
||||
executeFunction: true,
|
||||
args: [],
|
||||
imex: () => {
|
||||
window.$crisp.push(["set", "user:nickname", [payload.displayName || payload.email]]);
|
||||
window.$crisp.push(["set", "session:segments", [["user"]]]);
|
||||
window.$crisp.push(["set", "session:segments", [["imex"]]]);
|
||||
},
|
||||
rome: () => {
|
||||
window.$zoho.salesiq.visitor.name(payload.displayName || payload.email);
|
||||
window.$zoho.salesiq.visitor.email(payload.email);
|
||||
window.$crisp.push(["set", "session:segments", [["rome"]]]);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1444,7 +1444,8 @@ export const TemplateList = (type, context) => {
|
||||
object: i18n.t("reportcenter.labels.objects.jobs"),
|
||||
field: i18n.t("jobs.fields.date_exported")
|
||||
},
|
||||
group: "sales"
|
||||
group: "sales",
|
||||
featureNameRestricted: "export"
|
||||
},
|
||||
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"),
|
||||
field: i18n.t("jobs.fields.date_open")
|
||||
},
|
||||
group: "jobs"
|
||||
group: "jobs",
|
||||
featureNameRestricted: "bills"
|
||||
},
|
||||
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"),
|
||||
field: i18n.t("parts_orders.fields.order_date")
|
||||
},
|
||||
group: "jobs"
|
||||
group: "jobs",
|
||||
featureNameRestricted: "bills"
|
||||
},
|
||||
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"),
|
||||
field: i18n.t("parts_orders.fields.order_date")
|
||||
},
|
||||
group: "jobs"
|
||||
group: "jobs",
|
||||
featureNameRestricted: "bills"
|
||||
},
|
||||
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")
|
||||
},
|
||||
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"
|
||||
|
||||
@@ -207,6 +207,7 @@
|
||||
- default_prod_list_view
|
||||
- id
|
||||
- kanban_settings
|
||||
- notification_settings
|
||||
- qbo_realmId
|
||||
- shopid
|
||||
- useremail
|
||||
@@ -222,6 +223,7 @@
|
||||
- authlevel
|
||||
- default_prod_list_view
|
||||
- kanban_settings
|
||||
- notification_settings
|
||||
- qbo_realmId
|
||||
filter:
|
||||
user:
|
||||
@@ -681,6 +683,31 @@
|
||||
- exported:
|
||||
_eq: false
|
||||
event_triggers:
|
||||
- name: notifications_bills
|
||||
definition:
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 0
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
body:
|
||||
action: transform
|
||||
template: |-
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/notifications/events/handleBillsChange'
|
||||
version: 2
|
||||
- name: os_bills
|
||||
definition:
|
||||
delete:
|
||||
@@ -2805,6 +2832,80 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
- table:
|
||||
name: job_watchers
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_email
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
check:
|
||||
user:
|
||||
_and:
|
||||
- associations:
|
||||
active:
|
||||
_eq: true
|
||||
- authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
columns:
|
||||
- user_email
|
||||
- created_at
|
||||
- id
|
||||
- jobid
|
||||
comment: ""
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- user_email
|
||||
- created_at
|
||||
- id
|
||||
- jobid
|
||||
filter:
|
||||
user:
|
||||
_and:
|
||||
- associations:
|
||||
active:
|
||||
_eq: true
|
||||
- authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- user_email
|
||||
- created_at
|
||||
- id
|
||||
- jobid
|
||||
filter:
|
||||
user:
|
||||
_and:
|
||||
- associations:
|
||||
active:
|
||||
_eq: true
|
||||
- authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
check: null
|
||||
comment: ""
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
filter:
|
||||
user:
|
||||
_and:
|
||||
- associations:
|
||||
active:
|
||||
_eq: true
|
||||
- authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
comment: ""
|
||||
- table:
|
||||
name: joblines
|
||||
schema: public
|
||||
@@ -2896,6 +2997,7 @@
|
||||
- est_seq
|
||||
- glass_flag
|
||||
- id
|
||||
- include_in_part_cnt
|
||||
- ioucreated
|
||||
- jobid
|
||||
- lbr_amt
|
||||
@@ -2965,6 +3067,7 @@
|
||||
- est_seq
|
||||
- glass_flag
|
||||
- id
|
||||
- include_in_part_cnt
|
||||
- ioucreated
|
||||
- jobid
|
||||
- lbr_amt
|
||||
@@ -3045,6 +3148,7 @@
|
||||
- est_seq
|
||||
- glass_flag
|
||||
- id
|
||||
- include_in_part_cnt
|
||||
- ioucreated
|
||||
- jobid
|
||||
- lbr_amt
|
||||
@@ -4326,6 +4430,58 @@
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/record-handler/arms'
|
||||
version: 2
|
||||
- name: notifications_jobs
|
||||
definition:
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
update:
|
||||
columns:
|
||||
- queued_for_parts
|
||||
- employee_prep
|
||||
- clm_total
|
||||
- towin
|
||||
- employee_body
|
||||
- converted
|
||||
- scheduled_in
|
||||
- scheduled_completion
|
||||
- scheduled_delivery
|
||||
- actual_delivery
|
||||
- actual_completion
|
||||
- alt_transport
|
||||
- date_exported
|
||||
- status
|
||||
- employee_csr
|
||||
- actual_in
|
||||
- deliverchecklist
|
||||
- comment
|
||||
- job_totals
|
||||
- employee_refinish
|
||||
- inproduction
|
||||
- production_vars
|
||||
- intakechecklist
|
||||
- cieca_ttl
|
||||
- date_invoiced
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 0
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
body:
|
||||
action: transform
|
||||
template: |-
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/notifications/events/handleJobsChange'
|
||||
version: 2
|
||||
- name: os_jobs
|
||||
definition:
|
||||
delete:
|
||||
@@ -4669,6 +4825,57 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
- table:
|
||||
name: notifications
|
||||
schema: public
|
||||
object_relationships:
|
||||
- name: association
|
||||
using:
|
||||
foreign_key_constraint_on: associationid
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- associationid
|
||||
- created_at
|
||||
- fcm_data
|
||||
- fcm_message
|
||||
- fcm_title
|
||||
- id
|
||||
- jobid
|
||||
- meta
|
||||
- read
|
||||
- ui_translation_meta
|
||||
- ui_translation_string
|
||||
- updated_at
|
||||
filter:
|
||||
association:
|
||||
_and:
|
||||
- active:
|
||||
_eq: true
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
comment: ""
|
||||
update_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- meta
|
||||
- read
|
||||
filter:
|
||||
association:
|
||||
_and:
|
||||
- active:
|
||||
_eq: true
|
||||
- user:
|
||||
authid:
|
||||
_eq: X-Hasura-User-Id
|
||||
check: null
|
||||
comment: ""
|
||||
- table:
|
||||
name: owners
|
||||
schema: public
|
||||
@@ -4909,6 +5116,32 @@
|
||||
- active:
|
||||
_eq: true
|
||||
check: null
|
||||
event_triggers:
|
||||
- name: notifications_parts_dispatch
|
||||
definition:
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 0
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
body:
|
||||
action: transform
|
||||
template: |-
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/notifications/events/handlePartsDispatchChange'
|
||||
version: 2
|
||||
- table:
|
||||
name: parts_dispatch_lines
|
||||
schema: public
|
||||
@@ -5879,6 +6112,36 @@
|
||||
_eq: true
|
||||
check: null
|
||||
event_triggers:
|
||||
- name: notifications_tasks
|
||||
definition:
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
update:
|
||||
columns:
|
||||
- assigned_to
|
||||
- completed
|
||||
- description
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 0
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
body:
|
||||
action: transform
|
||||
template: |-
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/notifications/events/handleTasksChange'
|
||||
version: 2
|
||||
- name: tasks_assigned_changed
|
||||
definition:
|
||||
enable_manual: false
|
||||
@@ -6035,6 +6298,32 @@
|
||||
_eq: X-Hasura-User-Id
|
||||
- active:
|
||||
_eq: true
|
||||
event_triggers:
|
||||
- name: notifications_time_tickets
|
||||
definition:
|
||||
enable_manual: false
|
||||
insert:
|
||||
columns: '*'
|
||||
retry_conf:
|
||||
interval_sec: 10
|
||||
num_retries: 0
|
||||
timeout_sec: 60
|
||||
webhook_from_env: HASURA_API_URL
|
||||
headers:
|
||||
- name: event-secret
|
||||
value_from_env: EVENT_SECRET
|
||||
request_transform:
|
||||
body:
|
||||
action: transform
|
||||
template: |-
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
method: POST
|
||||
query_params: {}
|
||||
template_engine: Kriti
|
||||
url: '{{$base_url}}/notifications/events/handleTimeTicketsChange'
|
||||
version: 2
|
||||
- table:
|
||||
name: transitions
|
||||
schema: public
|
||||
|
||||
@@ -39,50 +39,50 @@ END;
|
||||
$$;
|
||||
CREATE FUNCTION public.assign_ro_number() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
begin
|
||||
IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN
|
||||
UPDATE counters
|
||||
SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum'
|
||||
RETURNING concat(prefix,count) into new.ro_number;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
AS $$
|
||||
begin
|
||||
IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN
|
||||
UPDATE counters
|
||||
SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum'
|
||||
RETURNING concat(prefix,count) into new.ro_number;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
CREATE FUNCTION public.audit_trigger() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
shopid uuid ;
|
||||
email text;
|
||||
BEGIN
|
||||
select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true;
|
||||
IF TG_OP = 'INSERT'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email);
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'UPDATE'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP,
|
||||
json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email);
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'DELETE'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email);
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
END;
|
||||
AS $$
|
||||
DECLARE
|
||||
shopid uuid ;
|
||||
email text;
|
||||
BEGIN
|
||||
select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true;
|
||||
IF TG_OP = 'INSERT'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email);
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'UPDATE'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP,
|
||||
json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email);
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'DELETE'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email);
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
CREATE FUNCTION public.json_diff(l jsonb, r jsonb) RETURNS jsonb
|
||||
LANGUAGE sql
|
||||
AS $$
|
||||
SELECT jsonb_object_agg(a.key, a.value) FROM
|
||||
( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN
|
||||
( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key
|
||||
WHERE a.value != b.value OR b.key IS NULL;
|
||||
AS $$
|
||||
SELECT jsonb_object_agg(a.key, a.value) FROM
|
||||
( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN
|
||||
( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key
|
||||
WHERE a.value != b.value OR b.key IS NULL;
|
||||
$$;
|
||||
CREATE TABLE public.bills (
|
||||
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||
@@ -211,33 +211,33 @@ CREATE TABLE public.exportlog (
|
||||
);
|
||||
CREATE FUNCTION public.search_exportlog(search text) RETURNS SETOF public.exportlog
|
||||
LANGUAGE plpgsql STABLE
|
||||
AS $$ BEGIN IF search = '' THEN RETURN query
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
exportlog e;
|
||||
ELSE RETURN query
|
||||
SELECT
|
||||
e.*
|
||||
FROM
|
||||
exportlog e
|
||||
LEFT JOIN jobs j on j.id = e.jobid
|
||||
LEFT JOIN payments p
|
||||
ON p.id = e.paymentid
|
||||
LEFT JOIN bills b
|
||||
ON e.billid = b.id
|
||||
WHERE
|
||||
(
|
||||
j.ro_number ILIKE '%' || search || '%'
|
||||
OR b.invoice_number ILIKE '%' || search || '%'
|
||||
OR p.paymentnum ILIKE '%' || search || '%'
|
||||
OR e.useremail ILIKE '%' || search || '%'
|
||||
)
|
||||
AND (e.jobid = j.id
|
||||
or e.paymentid = p.id
|
||||
or e.billid = b.id)
|
||||
;
|
||||
END IF;
|
||||
AS $$ BEGIN IF search = '' THEN RETURN query
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
exportlog e;
|
||||
ELSE RETURN query
|
||||
SELECT
|
||||
e.*
|
||||
FROM
|
||||
exportlog e
|
||||
LEFT JOIN jobs j on j.id = e.jobid
|
||||
LEFT JOIN payments p
|
||||
ON p.id = e.paymentid
|
||||
LEFT JOIN bills b
|
||||
ON e.billid = b.id
|
||||
WHERE
|
||||
(
|
||||
j.ro_number ILIKE '%' || search || '%'
|
||||
OR b.invoice_number ILIKE '%' || search || '%'
|
||||
OR p.paymentnum ILIKE '%' || search || '%'
|
||||
OR e.useremail ILIKE '%' || search || '%'
|
||||
)
|
||||
AND (e.jobid = j.id
|
||||
or e.paymentid = p.id
|
||||
or e.billid = b.id)
|
||||
;
|
||||
END IF;
|
||||
END $$;
|
||||
CREATE TABLE public.jobs (
|
||||
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."associations" add column "notification_settings" jsonb
|
||||
-- null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."associations" add column "notification_settings" jsonb
|
||||
null;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."notifications";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE TABLE "public"."notifications" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "created_at" timestamptz NOT NULL DEFAULT now(), "updated_at" timestamptz NOT NULL DEFAULT now(), "job_id" uuid NOT NULL, "association_id" uuid NOT NULL, "ui_translation_string" text NOT NULL, "ui_translation_meta" jsonb, "fcm_title" text NOT NULL, "fcm_message" text NOT NULL, "fcm_data" jsonb, "read" timestamptz, "meta" jsonb, "scenario" Integer NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("job_id") REFERENCES "public"."jobs"("id") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("association_id") REFERENCES "public"."associations"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));COMMENT ON TABLE "public"."notifications" IS E'Real Time Notifications System';
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -0,0 +1,3 @@
|
||||
comment on column "public"."notifications"."scenario" is E'Real Time Notifications System';
|
||||
alter table "public"."notifications" alter column "scenario" drop not null;
|
||||
alter table "public"."notifications" add column "scenario" int4;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."notifications" drop column "scenario" cascade;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- alter table "public"."notifications" add column "scenario" text
|
||||
-- not null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."notifications" add column "scenario" text
|
||||
not null;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE "public"."job_watchers";
|
||||
@@ -0,0 +1,2 @@
|
||||
CREATE TABLE "public"."job_watchers" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "job_id" uuid NOT NULL, "user_email" text NOT NULL, "created_at" timestamptz NOT NULL DEFAULT now(), PRIMARY KEY ("id") , FOREIGN KEY ("user_email") REFERENCES "public"."users"("email") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("job_id") REFERENCES "public"."jobs"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));COMMENT ON TABLE "public"."job_watchers" IS E'Job Watchers';
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
@@ -0,0 +1,3 @@
|
||||
comment on column "public"."notifications"."scenario" is E'Real Time Notifications System';
|
||||
alter table "public"."notifications" alter column "scenario" drop not null;
|
||||
alter table "public"."notifications" add column "scenario" text;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."notifications" drop column "scenario" cascade;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- update associations set notification_settings = '{}';
|
||||
@@ -0,0 +1 @@
|
||||
update associations set notification_settings = '{}';
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."associations" alter column "notification_settings" drop not null;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."associations" alter column "notification_settings" set not null;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Could not auto-generate a down migration.
|
||||
-- Please write an appropriate down migration for the SQL below:
|
||||
-- update notifications set meta = '{}';
|
||||
-- update notifications set fcm_data = '{}';
|
||||
-- update notifications set ui_translation_meta = '{}';
|
||||
@@ -0,0 +1,3 @@
|
||||
update notifications set meta = '{}';
|
||||
update notifications set fcm_data = '{}';
|
||||
update notifications set ui_translation_meta = '{}';
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."notifications" alter column "meta" drop not null;
|
||||
ALTER TABLE "public"."notifications" ALTER COLUMN "meta" drop default;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."notifications" alter column "meta" set default jsonb_build_object();
|
||||
alter table "public"."notifications" alter column "meta" set not null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."notifications" alter column "fcm_data" drop not null;
|
||||
ALTER TABLE "public"."notifications" ALTER COLUMN "fcm_data" drop default;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."notifications" alter column "fcm_data" set default jsonb_build_object();
|
||||
alter table "public"."notifications" alter column "fcm_data" set not null;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."notifications" alter column "ui_translation_meta" drop not null;
|
||||
ALTER TABLE "public"."notifications" ALTER COLUMN "ui_translation_meta" drop default;
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."notifications" alter column "ui_translation_meta" set default jsonb_build_object();
|
||||
alter table "public"."notifications" alter column "ui_translation_meta" set not null;
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."notifications" rename column "jobid" to "job_id";
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."notifications" rename column "job_id" to "jobid";
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."notifications" rename column "associationid" to "association_id";
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."notifications" rename column "association_id" to "associationid";
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."job_watchers" rename column "jobid" to "job_id";
|
||||
@@ -0,0 +1 @@
|
||||
alter table "public"."job_watchers" rename column "job_id" to "jobid";
|
||||
@@ -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';
|
||||
@@ -0,0 +1,2 @@
|
||||
alter table "public"."joblines" add column "include_in_part_cnt" boolean
|
||||
not null default 'false';
|
||||
10
hasura/migrations/1736890690896_run_sql_migration/down.sql
Normal file
10
hasura/migrations/1736890690896_run_sql_migration/down.sql
Normal 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;
|
||||
8
hasura/migrations/1736890690896_run_sql_migration/up.sql
Normal file
8
hasura/migrations/1736890690896_run_sql_migration/up.sql
Normal 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;
|
||||
10
hasura/migrations/1736890701752_run_sql_migration/down.sql
Normal file
10
hasura/migrations/1736890701752_run_sql_migration/down.sql
Normal 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;
|
||||
8
hasura/migrations/1736890701752_run_sql_migration/up.sql
Normal file
8
hasura/migrations/1736890701752_run_sql_migration/up.sql
Normal 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;
|
||||
@@ -39,50 +39,50 @@ END;
|
||||
$$;
|
||||
CREATE FUNCTION public.assign_ro_number() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
begin
|
||||
IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN
|
||||
UPDATE counters
|
||||
SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum'
|
||||
RETURNING concat(prefix,count) into new.ro_number;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
AS $$
|
||||
begin
|
||||
IF NEW.converted = true and (new.ro_number is null or new.ro_number = '') THEN
|
||||
UPDATE counters
|
||||
SET count = count + 1 where shopid=new.shopid AND countertype = 'ronum'
|
||||
RETURNING concat(prefix,count) into new.ro_number;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
CREATE FUNCTION public.audit_trigger() RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
shopid uuid ;
|
||||
email text;
|
||||
BEGIN
|
||||
select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true;
|
||||
IF TG_OP = 'INSERT'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email);
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'UPDATE'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP,
|
||||
json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email);
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'DELETE'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email);
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
END;
|
||||
AS $$
|
||||
DECLARE
|
||||
shopid uuid ;
|
||||
email text;
|
||||
BEGIN
|
||||
select b.id, u.email INTO shopid, email from users u join associations a on u.email = a.useremail join bodyshops b on b.id = a.shopid where u.authid = current_setting('hasura.user', 't')::jsonb->>'x-hasura-user-id' and a.active = true;
|
||||
IF TG_OP = 'INSERT'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, new_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(NEW), NEW.id, shopid, email);
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'UPDATE'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, new_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP,
|
||||
json_diff(to_jsonb(OLD), to_jsonb(NEW)) , json_diff(to_jsonb(NEW), to_jsonb(OLD)), OLD.id, shopid, email);
|
||||
RETURN NEW;
|
||||
ELSIF TG_OP = 'DELETE'
|
||||
THEN
|
||||
INSERT INTO public.audit_trail (tabname, schemaname, operation, old_val, recordid, bodyshopid, useremail)
|
||||
VALUES (TG_RELNAME, TG_TABLE_SCHEMA, TG_OP, row_to_json(OLD), OLD.ID, shopid, email);
|
||||
RETURN OLD;
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
CREATE FUNCTION public.json_diff(l jsonb, r jsonb) RETURNS jsonb
|
||||
LANGUAGE sql
|
||||
AS $$
|
||||
SELECT jsonb_object_agg(a.key, a.value) FROM
|
||||
( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN
|
||||
( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key
|
||||
WHERE a.value != b.value OR b.key IS NULL;
|
||||
AS $$
|
||||
SELECT jsonb_object_agg(a.key, a.value) FROM
|
||||
( SELECT key, value FROM jsonb_each(l) ) a LEFT OUTER JOIN
|
||||
( SELECT key, value FROM jsonb_each(r) ) b ON a.key = b.key
|
||||
WHERE a.value != b.value OR b.key IS NULL;
|
||||
$$;
|
||||
CREATE TABLE public.bills (
|
||||
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||
@@ -211,33 +211,33 @@ CREATE TABLE public.exportlog (
|
||||
);
|
||||
CREATE FUNCTION public.search_exportlog(search text) RETURNS SETOF public.exportlog
|
||||
LANGUAGE plpgsql STABLE
|
||||
AS $$ BEGIN IF search = '' THEN RETURN query
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
exportlog e;
|
||||
ELSE RETURN query
|
||||
SELECT
|
||||
e.*
|
||||
FROM
|
||||
exportlog e
|
||||
LEFT JOIN jobs j on j.id = e.jobid
|
||||
LEFT JOIN payments p
|
||||
ON p.id = e.paymentid
|
||||
LEFT JOIN bills b
|
||||
ON e.billid = b.id
|
||||
WHERE
|
||||
(
|
||||
j.ro_number ILIKE '%' || search || '%'
|
||||
OR b.invoice_number ILIKE '%' || search || '%'
|
||||
OR p.paymentnum ILIKE '%' || search || '%'
|
||||
OR e.useremail ILIKE '%' || search || '%'
|
||||
)
|
||||
AND (e.jobid = j.id
|
||||
or e.paymentid = p.id
|
||||
or e.billid = b.id)
|
||||
;
|
||||
END IF;
|
||||
AS $$ BEGIN IF search = '' THEN RETURN query
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
exportlog e;
|
||||
ELSE RETURN query
|
||||
SELECT
|
||||
e.*
|
||||
FROM
|
||||
exportlog e
|
||||
LEFT JOIN jobs j on j.id = e.jobid
|
||||
LEFT JOIN payments p
|
||||
ON p.id = e.paymentid
|
||||
LEFT JOIN bills b
|
||||
ON e.billid = b.id
|
||||
WHERE
|
||||
(
|
||||
j.ro_number ILIKE '%' || search || '%'
|
||||
OR b.invoice_number ILIKE '%' || search || '%'
|
||||
OR p.paymentnum ILIKE '%' || search || '%'
|
||||
OR e.useremail ILIKE '%' || search || '%'
|
||||
)
|
||||
AND (e.jobid = j.id
|
||||
or e.paymentid = p.id
|
||||
or e.billid = b.id)
|
||||
;
|
||||
END IF;
|
||||
END $$;
|
||||
CREATE TABLE public.jobs (
|
||||
id uuid DEFAULT public.gen_random_uuid() NOT NULL,
|
||||
|
||||
@@ -34,7 +34,7 @@ exports.default = async (req, res) => {
|
||||
|
||||
//Query the usage data.
|
||||
const queryResults = await client.request(queries.STATUS_UPDATE, {
|
||||
today: moment().startOf("day").subtract(3, "days"),
|
||||
today: moment().startOf("day").subtract(7, "days"),
|
||||
period: moment().subtract(90, "days").startOf("day")
|
||||
});
|
||||
|
||||
@@ -63,7 +63,7 @@ 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 3 days of data.
|
||||
- Counts are inclusive of the last 7 days of data.
|
||||
`,
|
||||
attachments: [{ filename: `RO Usage Report ${moment().format("MM/DD/YYYY")}.csv`, content: csv }]
|
||||
})
|
||||
|
||||
@@ -818,6 +818,7 @@ function CalculateTaxesTotals(job, otherTotals) {
|
||||
PAG: Dinero(),
|
||||
PAO: Dinero(),
|
||||
PAS: Dinero(),
|
||||
PASL: Dinero(),
|
||||
PAP: Dinero(),
|
||||
PAM: Dinero(),
|
||||
|
||||
|
||||
5
server/notifications/eventHandlers/handeJobsChange.js
Normal file
5
server/notifications/eventHandlers/handeJobsChange.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const handleJobsChange = (req, res) => {
|
||||
return res.status(200).json({ message: "Jobs change handled." });
|
||||
};
|
||||
|
||||
module.exports = handleJobsChange;
|
||||
5
server/notifications/eventHandlers/handleBillsChange.js
Normal file
5
server/notifications/eventHandlers/handleBillsChange.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const handleBillsChange = (req, res) => {
|
||||
return res.status(200).json({ message: "Bills change handled." });
|
||||
};
|
||||
|
||||
module.exports = handleBillsChange;
|
||||
@@ -0,0 +1,5 @@
|
||||
const handlePartsDispatchChange = (req, res) => {
|
||||
return res.status(200).json({ message: "Parts Dispatch change handled." });
|
||||
};
|
||||
|
||||
module.exports = handlePartsDispatchChange;
|
||||
@@ -0,0 +1,5 @@
|
||||
const handlePartsOrderChange = (req, res) => {
|
||||
return res.status(200).json({ message: "Parts Order change handled." });
|
||||
};
|
||||
|
||||
module.exports = handlePartsOrderChange;
|
||||
5
server/notifications/eventHandlers/handleTasksChange.js
Normal file
5
server/notifications/eventHandlers/handleTasksChange.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const handleTasksChange = (req, res) => {
|
||||
return res.status(200).json({ message: "Tasks change handled." });
|
||||
};
|
||||
|
||||
module.exports = handleTasksChange;
|
||||
@@ -0,0 +1,5 @@
|
||||
const handleTimeTicketsChange = (req, res) => {
|
||||
return res.status(200).json({ message: "Time Tickets change handled." });
|
||||
};
|
||||
|
||||
module.exports = handleTimeTicketsChange;
|
||||
@@ -1,12 +1,28 @@
|
||||
const express = require("express");
|
||||
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
|
||||
const { subscribe, unsubscribe, sendNotification } = require("../firebase/firebase-handler");
|
||||
const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMIddleware");
|
||||
|
||||
const handlePartsOrderChange = require("../notifications/eventHandlers/handlePartsOrderChange");
|
||||
const handlePartsDispatchChange = require("../notifications/eventHandlers/handlePartsDispatchChange");
|
||||
const handleTasksChange = require("../notifications/eventHandlers/handleTasksChange");
|
||||
const handleTimeTicketsChange = require("../notifications/eventHandlers/handleTimeTicketsChange");
|
||||
const handleJobsChange = require("../notifications/eventHandlers/handeJobsChange");
|
||||
const handleBillsChange = require("../notifications/eventHandlers/handleBillsChange");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.use(validateFirebaseIdTokenMiddleware);
|
||||
// These are FCM handlers
|
||||
router.post("/subscribe", validateFirebaseIdTokenMiddleware, subscribe);
|
||||
router.post("/unsubscribe", validateFirebaseIdTokenMiddleware, unsubscribe);
|
||||
router.post("/sendtestnotification", validateFirebaseIdTokenMiddleware, sendNotification);
|
||||
|
||||
router.post("/subscribe", subscribe);
|
||||
router.post("/unsubscribe", unsubscribe);
|
||||
router.post("/sendtestnotification", sendNotification);
|
||||
// Hasura Entry points for creating notifications
|
||||
router.post("/events/handleJobsChange", eventAuthorizationMiddleware, handleJobsChange);
|
||||
router.post("/events/handleBillsChange", eventAuthorizationMiddleware, handleBillsChange);
|
||||
router.post("/events/handlePartsOrderChange", eventAuthorizationMiddleware, handlePartsOrderChange);
|
||||
router.post("/events/handlePartsDispatchChange", eventAuthorizationMiddleware, handlePartsDispatchChange);
|
||||
router.post("/events/handleTasksChange", eventAuthorizationMiddleware, handleTasksChange);
|
||||
router.post("/events/handleTimeTicketsChange", eventAuthorizationMiddleware, handleTimeTicketsChange);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user