@@ -8229,6 +8229,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>qbo</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>rbac</name>
|
<name>rbac</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -32575,6 +32596,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>csi_invitation_action</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>diagnostic_authorization</name>
|
<name>diagnostic_authorization</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -33205,6 +33247,48 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>sgi_certificate_of_repairs</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>sgi_windshield_auth</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>stolen_recovery_checklist</name>
|
<name>stolen_recovery_checklist</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -4,18 +4,18 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"proxy": "http://localhost:5000",
|
"proxy": "http://localhost:5000",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.4.13",
|
"@apollo/client": "^3.4.14",
|
||||||
"@craco/craco": "^6.3.0",
|
"@craco/craco": "^6.3.0",
|
||||||
"@fingerprintjs/fingerprintjs": "^3.3.0",
|
"@fingerprintjs/fingerprintjs": "^3.3.0",
|
||||||
"@lourenci/react-kanban": "^2.1.0",
|
"@lourenci/react-kanban": "^2.1.0",
|
||||||
"@openreplay/tracker": "^3.3.1",
|
"@openreplay/tracker": "^3.4.0",
|
||||||
"@openreplay/tracker-assist": "^3.1.1",
|
"@openreplay/tracker-assist": "^3.4.0",
|
||||||
"@openreplay/tracker-graphql": "^3.0.0",
|
"@openreplay/tracker-graphql": "^3.0.0",
|
||||||
"@openreplay/tracker-redux": "^3.0.0",
|
"@openreplay/tracker-redux": "^3.0.0",
|
||||||
"@sentry/react": "^6.13.0",
|
"@sentry/react": "^6.13.2",
|
||||||
"@sentry/tracing": "^6.13.0",
|
"@sentry/tracing": "^6.13.2",
|
||||||
"@stripe/react-stripe-js": "^1.4.0",
|
"@stripe/react-stripe-js": "^1.5.0",
|
||||||
"@stripe/stripe-js": "^1.17.1",
|
"@stripe/stripe-js": "^1.18.0",
|
||||||
"@tanem/react-nprogress": "^3.0.79",
|
"@tanem/react-nprogress": "^3.0.79",
|
||||||
"antd": "^4.16.13",
|
"antd": "^4.16.13",
|
||||||
"apollo-link-logger": "^2.0.0",
|
"apollo-link-logger": "^2.0.0",
|
||||||
@@ -26,15 +26,15 @@
|
|||||||
"enquire-js": "^0.2.1",
|
"enquire-js": "^0.2.1",
|
||||||
"env-cmd": "^10.1.0",
|
"env-cmd": "^10.1.0",
|
||||||
"exifr": "^7.1.3",
|
"exifr": "^7.1.3",
|
||||||
"firebase": "^9.0.2",
|
"firebase": "^9.1.0",
|
||||||
"graphql": "^15.5.3",
|
"graphql": "^15.6.0",
|
||||||
"i18next": "^21.0.0",
|
"i18next": "^21.1.1",
|
||||||
"i18next-browser-languagedetector": "^6.1.2",
|
"i18next-browser-languagedetector": "^6.1.2",
|
||||||
"jsoneditor": "^9.5.4",
|
"jsoneditor": "^9.5.6",
|
||||||
"jsreport-browser-client-dist": "^1.3.0",
|
"jsreport-browser-client-dist": "^1.3.0",
|
||||||
"libphonenumber-js": "^1.9.34",
|
"libphonenumber-js": "^1.9.34",
|
||||||
"logrocket": "^2.0.0",
|
"logrocket": "^2.0.0",
|
||||||
"markerjs2": "^2.11.2",
|
"markerjs2": "^2.12.0",
|
||||||
"moment-business-days": "^1.2.0",
|
"moment-business-days": "^1.2.0",
|
||||||
"phone": "^3.1.8",
|
"phone": "^3.1.8",
|
||||||
"preval.macro": "^5.0.0",
|
"preval.macro": "^5.0.0",
|
||||||
@@ -59,13 +59,13 @@
|
|||||||
"react-scripts": "^4.0.3",
|
"react-scripts": "^4.0.3",
|
||||||
"react-sublime-video": "^0.2.5",
|
"react-sublime-video": "^0.2.5",
|
||||||
"react-virtualized": "^9.22.3",
|
"react-virtualized": "^9.22.3",
|
||||||
"recharts": "^2.1.3",
|
"recharts": "^2.1.4",
|
||||||
"redux": "^4.1.1",
|
"redux": "^4.1.1",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"redux-saga": "^1.1.3",
|
"redux-saga": "^1.1.3",
|
||||||
"redux-state-sync": "^3.1.2",
|
"redux-state-sync": "^3.1.2",
|
||||||
"reselect": "^4.0.0",
|
"reselect": "^4.0.0",
|
||||||
"sass": "^1.41.1",
|
"sass": "^1.42.1",
|
||||||
"socket.io-client": "^4.2.0",
|
"socket.io-client": "^4.2.0",
|
||||||
"styled-components": "^5.3.1",
|
"styled-components": "^5.3.1",
|
||||||
"subscriptions-transport-ws": "^0.9.18",
|
"subscriptions-transport-ws": "^0.9.18",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import App from "./App";
|
|||||||
import trackerGraphQL from "@openreplay/tracker-graphql";
|
import trackerGraphQL from "@openreplay/tracker-graphql";
|
||||||
//import trackerRedux from "@openreplay/tracker-redux";
|
//import trackerRedux from "@openreplay/tracker-redux";
|
||||||
import Tracker from "@openreplay/tracker";
|
import Tracker from "@openreplay/tracker";
|
||||||
import trackerAssist from "@openreplay/tracker-assist";
|
//import trackerAssist from "@openreplay/tracker-assist";
|
||||||
import { getCurrentUser } from "../firebase/firebase.utils";
|
import { getCurrentUser } from "../firebase/firebase.utils";
|
||||||
moment.locale("en-US");
|
moment.locale("en-US");
|
||||||
|
|
||||||
@@ -29,9 +29,9 @@ export const tracker = new Tracker({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
tracker.use(
|
// tracker.use(
|
||||||
trackerAssist({ confirmText: "Technical support is about to assist you." })
|
// trackerAssist({ confirmText: "Technical support is about to assist you." })
|
||||||
); // check the list of available options below
|
// ); // check the list of available options below
|
||||||
export const recordGraphQL = tracker.use(trackerGraphQL());
|
export const recordGraphQL = tracker.use(trackerGraphQL());
|
||||||
tracker.start();
|
tracker.start();
|
||||||
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
if (process.env.NODE_ENV === "production") LogRocket.init("gvfvfw/bodyshopapp");
|
||||||
|
|||||||
24
client/src/assets/C2QB_composite_English.svg
Normal file
|
After Width: | Height: | Size: 51 KiB |
24
client/src/assets/C2QB_transparent_English.svg
Normal file
|
After Width: | Height: | Size: 51 KiB |
4
client/src/assets/qbo/C2QB_green_btn_med_default.svg
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
5
client/src/assets/qbo/C2QB_green_btn_med_hover.svg
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
4
client/src/assets/qbo/C2QB_green_btn_short_default.svg
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
5
client/src/assets/qbo/C2QB_green_btn_short_hover.svg
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
4
client/src/assets/qbo/C2QB_green_btn_tall_default.svg
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
5
client/src/assets/qbo/C2QB_green_btn_tall_hover.svg
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
5
client/src/assets/qbo/C2QB_transparent_btn_med_hover.svg
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
@@ -9,8 +9,25 @@ import PayableExportAll from "../payable-export-all-button/payable-export-all-bu
|
|||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
export default function AccountingPayablesTableComponent({ loading, bills }) {
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(AccountingPayablesTableComponent);
|
||||||
|
|
||||||
|
export function AccountingPayablesTableComponent({ bodyshop, loading, bills }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [selectedBills, setSelectedBills] = useState([]);
|
const [selectedBills, setSelectedBills] = useState([]);
|
||||||
const [transInProgress, setTransInProgress] = useState(false);
|
const [transInProgress, setTransInProgress] = useState(false);
|
||||||
@@ -166,6 +183,9 @@ export default function AccountingPayablesTableComponent({ loading, bills }) {
|
|||||||
loadingCallback={setTransInProgress}
|
loadingCallback={setTransInProgress}
|
||||||
completedCallback={setSelectedBills}
|
completedCallback={setSelectedBills}
|
||||||
/>
|
/>
|
||||||
|
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
|
||||||
|
<QboAuthorizeComponent />
|
||||||
|
)}
|
||||||
<Input
|
<Input
|
||||||
value={state.search}
|
value={state.search}
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import JobsExportAllButton from "../jobs-export-all-button/jobs-export-all-butto
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
@@ -206,6 +207,9 @@ export function AccountingReceivablesTableComponent({
|
|||||||
completedCallback={setSelectedJobs}
|
completedCallback={setSelectedJobs}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
|
||||||
|
<QboAuthorizeComponent />
|
||||||
|
)}
|
||||||
<Input.Search
|
<Input.Search
|
||||||
value={state.search}
|
value={state.search}
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
|
|||||||
@@ -1,11 +1,24 @@
|
|||||||
import { useMutation } from "@apollo/client";
|
import { useMutation, useLazyQuery } from "@apollo/client";
|
||||||
import { Button, Card, Form, InputNumber, notification, Popover } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Form,
|
||||||
|
InputNumber,
|
||||||
|
notification,
|
||||||
|
Popover,
|
||||||
|
Space,
|
||||||
|
} from "antd";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { INSERT_SCOREBOARD_ENTRY } from "../../graphql/scoreboard.queries";
|
import {
|
||||||
|
INSERT_SCOREBOARD_ENTRY,
|
||||||
|
QUERY_SCOREBOARD_ENTRY,
|
||||||
|
UPDATE_SCOREBOARD_ENTRY,
|
||||||
|
} from "../../graphql/scoreboard.queries";
|
||||||
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
import FormDatePicker from "../form-date-picker/form-date-picker.component";
|
||||||
|
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||||
|
|
||||||
export default function ScoreboardAddButton({
|
export default function ScoreboardAddButton({
|
||||||
job,
|
job,
|
||||||
@@ -14,17 +27,46 @@ export default function ScoreboardAddButton({
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [insertScoreboardEntry] = useMutation(INSERT_SCOREBOARD_ENTRY);
|
const [insertScoreboardEntry] = useMutation(INSERT_SCOREBOARD_ENTRY);
|
||||||
|
const [updateScoreboardEntry] = useMutation(UPDATE_SCOREBOARD_ENTRY);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
const [visibility, setVisibility] = useState(false);
|
const [visibility, setVisibility] = useState(false);
|
||||||
|
const [callQuery, { loading: entryLoading, data: entryData }] = useLazyQuery(
|
||||||
|
QUERY_SCOREBOARD_ENTRY
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visibility) {
|
||||||
|
callQuery({ variables: { jobid: job.id } });
|
||||||
|
}
|
||||||
|
}, [visibility, job.id, callQuery]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("UE", entryData);
|
||||||
|
if (entryData && entryData.scoreboard && entryData.scoreboard[0]) {
|
||||||
|
console.log("Setting FOrm");
|
||||||
|
form.setFieldsValue(entryData.scoreboard[0]);
|
||||||
|
}
|
||||||
|
}, [entryData, form]);
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
const handleFinish = async (values) => {
|
||||||
logImEXEvent("job_close_add_to_scoreboard");
|
logImEXEvent("job_close_add_to_scoreboard");
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const result = await insertScoreboardEntry({
|
let result;
|
||||||
variables: { sbInput: [{ jobid: job.id, ...values }] },
|
|
||||||
});
|
if (entryData && entryData.scoreboard && entryData.scoreboard[0]) {
|
||||||
|
result = await updateScoreboardEntry({
|
||||||
|
variables: {
|
||||||
|
sbId: entryData.scoreboard[0].id,
|
||||||
|
sbInput: values,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result = await insertScoreboardEntry({
|
||||||
|
variables: { sbInput: [{ jobid: job.id, ...values }] },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!!result.errors) {
|
if (!!result.errors) {
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
@@ -44,53 +86,62 @@ export default function ScoreboardAddButton({
|
|||||||
const overlay = (
|
const overlay = (
|
||||||
<Card>
|
<Card>
|
||||||
<div>
|
<div>
|
||||||
<Form
|
{entryLoading ? (
|
||||||
form={form}
|
<LoadingSpinner />
|
||||||
layout="vertical"
|
) : (
|
||||||
onFinish={handleFinish}
|
<Form
|
||||||
initialValues={{}}
|
form={form}
|
||||||
>
|
layout="vertical"
|
||||||
<Form.Item
|
onFinish={handleFinish}
|
||||||
label={t("scoreboard.fields.date")}
|
initialValues={{}}
|
||||||
name="date"
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
<FormDatePicker />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("scoreboard.fields.date")}
|
||||||
<Form.Item
|
name="date"
|
||||||
label={t("scoreboard.fields.bodyhrs")}
|
rules={[
|
||||||
name="bodyhrs"
|
{
|
||||||
rules={[
|
required: true,
|
||||||
{
|
//message: t("general.validation.required"),
|
||||||
required: true,
|
},
|
||||||
//message: t("general.validation.required"),
|
]}
|
||||||
},
|
>
|
||||||
]}
|
<FormDatePicker />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={1} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("scoreboard.fields.bodyhrs")}
|
||||||
<Form.Item
|
name="bodyhrs"
|
||||||
label={t("scoreboard.fields.painthrs")}
|
rules={[
|
||||||
name="painthrs"
|
{
|
||||||
rules={[
|
required: true,
|
||||||
{
|
//message: t("general.validation.required"),
|
||||||
required: true,
|
},
|
||||||
//message: t("general.validation.required"),
|
]}
|
||||||
},
|
>
|
||||||
]}
|
<InputNumber precision={1} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={1} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("scoreboard.fields.painthrs")}
|
||||||
|
name="painthrs"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber precision={1} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Button type="primary" htmlType="submit">
|
<Space wrap>
|
||||||
{t("general.actions.save")}
|
<Button type="primary" htmlType="submit">
|
||||||
</Button>
|
{t("general.actions.save")}
|
||||||
</Form>
|
</Button>
|
||||||
|
<Button onClick={() => setVisibility(false)}>
|
||||||
|
{t("general.actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
@@ -99,7 +150,7 @@ export default function ScoreboardAddButton({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
const v = job.joblines.reduce(
|
const v = job.joblines.reduce(
|
||||||
(acc, val) => {
|
(acc, val) => {
|
||||||
if (val.mod_lbr_ty === "LAB")
|
if (val.mod_lbr_ty !== "LAR")
|
||||||
acc = { ...acc, bodyhrs: acc.bodyhrs + val.mod_lb_hrs };
|
acc = { ...acc, bodyhrs: acc.bodyhrs + val.mod_lb_hrs };
|
||||||
if (val.mod_lbr_ty === "LAR")
|
if (val.mod_lbr_ty === "LAR")
|
||||||
acc = { ...acc, painthrs: acc.painthrs + val.mod_lb_hrs };
|
acc = { ...acc, painthrs: acc.painthrs + val.mod_lb_hrs };
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export function JobsAvailableContainer({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||||
await CheckTaxRates(estData, bodyshop);
|
await CheckTaxRates(estData.est_data, bodyshop);
|
||||||
|
|
||||||
const newTotals = (
|
const newTotals = (
|
||||||
await Axios.post("/job/totals", {
|
await Axios.post("/job/totals", {
|
||||||
@@ -140,16 +140,17 @@ export function JobsAvailableContainer({
|
|||||||
: {}),
|
: {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (selectedOwner) {
|
||||||
|
newJob.ownerid = selectedOwner;
|
||||||
|
delete newJob.owner;
|
||||||
|
}
|
||||||
|
if (newJob.vehicleid) {
|
||||||
|
delete newJob.vehicle;
|
||||||
|
}
|
||||||
|
|
||||||
insertNewJob({
|
insertNewJob({
|
||||||
variables: {
|
variables: {
|
||||||
job: selectedOwner
|
job: newJob,
|
||||||
? Object.assign(
|
|
||||||
{},
|
|
||||||
newJob,
|
|
||||||
{ owner: null },
|
|
||||||
{ ownerid: selectedOwner }
|
|
||||||
)
|
|
||||||
: newJob,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((r) => {
|
.then((r) => {
|
||||||
@@ -199,11 +200,10 @@ export function JobsAvailableContainer({
|
|||||||
message: t("jobs.errors.creating", { error: "No job data present." }),
|
message: t("jobs.errors.creating", { error: "No job data present." }),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
|
||||||
await CheckTaxRates(estData, bodyshop);
|
|
||||||
|
|
||||||
//create upsert job
|
//create upsert job
|
||||||
let supp = replaceEmpty({ ...estData.est_data });
|
let supp = replaceEmpty({ ...estData.est_data });
|
||||||
|
//IO-539 Check for Parts Rate on PAL for SGI use case.
|
||||||
|
await CheckTaxRates(supp, bodyshop);
|
||||||
|
|
||||||
delete supp.owner;
|
delete supp.owner;
|
||||||
delete supp.vehicle;
|
delete supp.vehicle;
|
||||||
@@ -391,101 +391,104 @@ function replaceEmpty(someObj, replaceValue = null) {
|
|||||||
value === "" ? replaceValue || null : value;
|
value === "" ? replaceValue || null : value;
|
||||||
//^ because you seem to want to replace (strings) "null" or "undefined" too
|
//^ because you seem to want to replace (strings) "null" or "undefined" too
|
||||||
const temp = JSON.stringify(someObj, replacer);
|
const temp = JSON.stringify(someObj, replacer);
|
||||||
console.log("Parsed", JSON.parse(temp));
|
|
||||||
return JSON.parse(temp);
|
return JSON.parse(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function CheckTaxRates(estData, bodyshop) {
|
async function CheckTaxRates(estData, bodyshop) {
|
||||||
|
console.log(
|
||||||
|
"🚀 ~ file: jobs-available-table.container.jsx ~ line 398 ~ estData",
|
||||||
|
estData
|
||||||
|
);
|
||||||
//LKQ Check
|
//LKQ Check
|
||||||
if (
|
if (
|
||||||
!estData.est_data.parts_tax_rates?.PAL ||
|
!estData.parts_tax_rates?.PAL ||
|
||||||
estData.est_data.parts_tax_rates?.PAL?.prt_tax_rt === null ||
|
estData.parts_tax_rates?.PAL?.prt_tax_rt === null ||
|
||||||
estData.est_data.parts_tax_rates?.PAL?.prt_tax_rt === 0
|
estData.parts_tax_rates?.PAL?.prt_tax_rt === 0
|
||||||
) {
|
) {
|
||||||
const res = await confirmDialog(
|
const res = await confirmDialog(
|
||||||
`ImEX Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
`ImEX Online has detected that there is a missing tax rate for LKQ parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||||
);
|
);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (!estData.est_data.parts_tax_rates.PAL) {
|
if (!estData.parts_tax_rates.PAL) {
|
||||||
estData.est_data.parts_tax_rates.PAL = {
|
estData.parts_tax_rates.PAL = {
|
||||||
prt_discp: 0,
|
prt_discp: 0,
|
||||||
prt_mktyp: true,
|
prt_mktyp: true,
|
||||||
prt_mkupp: 0,
|
prt_mkupp: 0,
|
||||||
prt_type: "PAL",
|
prt_type: "PAL",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
estData.est_data.parts_tax_rates.PAL.prt_tax_rt =
|
estData.parts_tax_rates.PAL.prt_tax_rt =
|
||||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||||
estData.est_data.parts_tax_rates.PAL.prt_tax_in = true;
|
estData.parts_tax_rates.PAL.prt_tax_in = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//PAC Check
|
//PAC Check
|
||||||
if (
|
if (
|
||||||
!estData.est_data.parts_tax_rates?.PAC ||
|
!estData.parts_tax_rates?.PAC ||
|
||||||
estData.est_data.parts_tax_rates?.PAC?.prt_tax_rt === null ||
|
estData.parts_tax_rates?.PAC?.prt_tax_rt === null ||
|
||||||
estData.est_data.parts_tax_rates?.PAC?.prt_tax_rt === 0
|
estData.parts_tax_rates?.PAC?.prt_tax_rt === 0
|
||||||
) {
|
) {
|
||||||
const res = await confirmDialog(
|
const res = await confirmDialog(
|
||||||
`ImEX Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
`ImEX Online has detected that there is a missing tax rate for rechromed parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||||
);
|
);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (!estData.est_data.parts_tax_rates.PAC) {
|
if (!estData.parts_tax_rates.PAC) {
|
||||||
estData.est_data.parts_tax_rates.PAC = {
|
estData.parts_tax_rates.PAC = {
|
||||||
prt_discp: 0,
|
prt_discp: 0,
|
||||||
prt_mktyp: true,
|
prt_mktyp: true,
|
||||||
prt_mkupp: 0,
|
prt_mkupp: 0,
|
||||||
prt_type: "PAC",
|
prt_type: "PAC",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
estData.est_data.parts_tax_rates.PAC.prt_tax_rt =
|
estData.parts_tax_rates.PAC.prt_tax_rt =
|
||||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||||
estData.est_data.parts_tax_rates.PAC.prt_tax_in = true;
|
estData.parts_tax_rates.PAC.prt_tax_in = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//PAM Check
|
//PAM Check
|
||||||
if (
|
if (
|
||||||
!estData.est_data.parts_tax_rates?.PAM ||
|
!estData.parts_tax_rates?.PAM ||
|
||||||
estData.est_data.parts_tax_rates?.PAM?.prt_tax_rt === null ||
|
estData.parts_tax_rates?.PAM?.prt_tax_rt === null ||
|
||||||
estData.est_data.parts_tax_rates?.PAM?.prt_tax_rt === 0
|
estData.parts_tax_rates?.PAM?.prt_tax_rt === 0
|
||||||
) {
|
) {
|
||||||
const res = await confirmDialog(
|
const res = await confirmDialog(
|
||||||
`ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
`ImEX Online has detected that there is a missing tax rate for remanufactured parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||||
);
|
);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (!estData.est_data.parts_tax_rates.PAM) {
|
if (!estData.parts_tax_rates.PAM) {
|
||||||
estData.est_data.parts_tax_rates.PAM = {
|
estData.parts_tax_rates.PAM = {
|
||||||
prt_discp: 0,
|
prt_discp: 0,
|
||||||
prt_mktyp: true,
|
prt_mktyp: true,
|
||||||
prt_mkupp: 0,
|
prt_mkupp: 0,
|
||||||
prt_type: "PAM",
|
prt_type: "PAM",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
estData.est_data.parts_tax_rates.PAM.prt_tax_rt =
|
estData.parts_tax_rates.PAM.prt_tax_rt =
|
||||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||||
estData.est_data.parts_tax_rates.PAM.prt_tax_in = true;
|
estData.parts_tax_rates.PAM.prt_tax_in = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!estData.est_data.parts_tax_rates?.PAR ||
|
!estData.parts_tax_rates?.PAR ||
|
||||||
estData.est_data.parts_tax_rates?.PAR?.prt_tax_rt === null ||
|
estData.parts_tax_rates?.PAR?.prt_tax_rt === null ||
|
||||||
estData.est_data.parts_tax_rates?.PAR?.prt_tax_rt === 0
|
estData.parts_tax_rates?.PAR?.prt_tax_rt === 0
|
||||||
) {
|
) {
|
||||||
const res = await confirmDialog(
|
const res = await confirmDialog(
|
||||||
`ImEX Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
`ImEX Online has detected that there is a missing tax rate for recored parts. Pressing OK will set the tax rate to ${bodyshop.bill_tax_rates.state_tax_rate}% and enable the rate. Pressing cancel will keep the tax rate as is.`
|
||||||
);
|
);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (!estData.est_data.parts_tax_rates.PAR) {
|
if (!estData.parts_tax_rates.PAR) {
|
||||||
estData.est_data.parts_tax_rates.PAR = {
|
estData.parts_tax_rates.PAR = {
|
||||||
prt_discp: 0,
|
prt_discp: 0,
|
||||||
prt_mktyp: true,
|
prt_mktyp: true,
|
||||||
prt_mkupp: 0,
|
prt_mkupp: 0,
|
||||||
prt_type: "PAR",
|
prt_type: "PAR",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
estData.est_data.parts_tax_rates.PAR.prt_tax_rt =
|
estData.parts_tax_rates.PAR.prt_tax_rt =
|
||||||
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
bodyshop.bill_tax_rates.state_tax_rate / 100;
|
||||||
estData.est_data.parts_tax_rates.PAR.prt_tax_in = true;
|
estData.parts_tax_rates.PAR.prt_tax_in = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export function JobsCloseExportButton({
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const handleQbxml = async () => {
|
const handleQbxml = async () => {
|
||||||
|
//Check if it's a CDK setup.
|
||||||
if (bodyshop.cdk_dealerid) {
|
if (bodyshop.cdk_dealerid) {
|
||||||
history.push(`/manage/dms?jobId=${jobId}`);
|
history.push(`/manage/dms?jobId=${jobId}`);
|
||||||
return;
|
return;
|
||||||
@@ -41,48 +42,58 @@ export function JobsCloseExportButton({
|
|||||||
logImEXEvent("jobs_close_export");
|
logImEXEvent("jobs_close_export");
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
let QbXmlResponse;
|
//Check if it's a QBO Setup.
|
||||||
try {
|
|
||||||
QbXmlResponse = await axios.post(
|
|
||||||
"/accounting/qbxml/receivables",
|
|
||||||
{ jobIds: [jobId] },
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
console.log("handle -> XML", QbXmlResponse);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error getting QBXML from Server.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("jobs.errors.exporting", {
|
|
||||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let PartnerResponse;
|
let PartnerResponse;
|
||||||
try {
|
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||||
PartnerResponse = await axios.post(
|
PartnerResponse = await axios.post(`/qbo/receivables`, {
|
||||||
"http://localhost:1337/qb/",
|
withCredentials: true,
|
||||||
// "http://609feaeae986.ngrok.io/qb/",
|
jobIds: [jobId],
|
||||||
QbXmlResponse.data,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error connecting to quickbooks or partner.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("jobs.errors.exporting-partner"),
|
|
||||||
});
|
});
|
||||||
setLoading(false);
|
} else {
|
||||||
return;
|
//Default is QBD
|
||||||
|
|
||||||
|
let QbXmlResponse;
|
||||||
|
try {
|
||||||
|
QbXmlResponse = await axios.post(
|
||||||
|
"/accounting/qbxml/receivables",
|
||||||
|
{ jobIds: [jobId] },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log("handle -> XML", QbXmlResponse);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error getting QBXML from Server.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.exporting", {
|
||||||
|
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PartnerResponse = await axios.post(
|
||||||
|
"http://localhost:1337/qb/",
|
||||||
|
// "http://609feaeae986.ngrok.io/qb/",
|
||||||
|
QbXmlResponse.data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error connecting to quickbooks or partner.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.exporting-partner"),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("PartnerResponse", PartnerResponse);
|
console.log("PartnerResponse", PartnerResponse);
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ export function JobsDetailHeaderCsi({
|
|||||||
replyTo: bodyshop.email,
|
replyTo: bodyshop.email,
|
||||||
},
|
},
|
||||||
template: {
|
template: {
|
||||||
name: TemplateList("job").csi_invitation.key,
|
name: TemplateList("job_special").csi_invitation_action.key,
|
||||||
variables: {
|
variables: {
|
||||||
id: result.data.insert_csi.returning[0].id,
|
id: result.data.insert_csi.returning[0].id,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,53 +34,61 @@ export function JobsExportAllButton({
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const handleQbxml = async () => {
|
const handleQbxml = async () => {
|
||||||
logImEXEvent("jobs_export_all");
|
logImEXEvent("jobs_export_all");
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
let QbXmlResponse;
|
|
||||||
try {
|
|
||||||
QbXmlResponse = await axios.post(
|
|
||||||
"/accounting/qbxml/receivables",
|
|
||||||
{ jobIds: jobIds },
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error getting QBXML from Server.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("jobs.errors.exporting", {
|
|
||||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let PartnerResponse;
|
let PartnerResponse;
|
||||||
try {
|
setLoading(true);
|
||||||
PartnerResponse = await axios.post(
|
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||||
"http://localhost:1337/qb/",
|
PartnerResponse = await axios.post(`/qbo/receivables`, {
|
||||||
// "http://609feaeae986.ngrok.io/qb/",
|
withCredentials: true,
|
||||||
QbXmlResponse.data,
|
jobIds: jobIds,
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error connecting to quickbooks or partner.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("jobs.errors.exporting-partner"),
|
|
||||||
});
|
});
|
||||||
setLoading(false);
|
} else {
|
||||||
return;
|
let QbXmlResponse;
|
||||||
}
|
try {
|
||||||
|
QbXmlResponse = await axios.post(
|
||||||
|
"/accounting/qbxml/receivables",
|
||||||
|
{ jobIds: jobIds },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error getting QBXML from Server.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.exporting", {
|
||||||
|
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PartnerResponse = await axios.post(
|
||||||
|
"http://localhost:1337/qb/",
|
||||||
|
// "http://609feaeae986.ngrok.io/qb/",
|
||||||
|
QbXmlResponse.data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error connecting to quickbooks or partner.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.exporting-partner"),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
console.log("PartnerResponse", PartnerResponse);
|
console.log("PartnerResponse", PartnerResponse);
|
||||||
const groupedData = _.groupBy(PartnerResponse.data, "id");
|
const groupedData = _.groupBy(
|
||||||
|
PartnerResponse.data,
|
||||||
|
bodyshop.accountingconfig.qbo ? "jobid" : "id"
|
||||||
|
);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
Object.keys(groupedData).map(async (key) => {
|
Object.keys(groupedData).map(async (key) => {
|
||||||
@@ -157,6 +165,7 @@ export function JobsExportAllButton({
|
|||||||
|
|
||||||
if (!!completedCallback) completedCallback([]);
|
if (!!completedCallback) completedCallback([]);
|
||||||
if (!!loadingCallback) loadingCallback(false);
|
if (!!loadingCallback) loadingCallback(false);
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -35,52 +35,61 @@ export function PayableExportAll({
|
|||||||
|
|
||||||
const handleQbxml = async () => {
|
const handleQbxml = async () => {
|
||||||
logImEXEvent("accounting_payables_export_all");
|
logImEXEvent("accounting_payables_export_all");
|
||||||
|
let PartnerResponse;
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
if (!!loadingCallback) loadingCallback(true);
|
if (!!loadingCallback) loadingCallback(true);
|
||||||
|
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||||
let QbXmlResponse;
|
PartnerResponse = await axios.post(`/qbo/receivables`, {
|
||||||
try {
|
withCredentials: true,
|
||||||
QbXmlResponse = await axios.post(
|
bills: billids,
|
||||||
"/accounting/qbxml/payables",
|
|
||||||
{ bills: billids },
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error getting QBXML from Server.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("bills.errors.exporting", {
|
|
||||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
if (loadingCallback) loadingCallback(false);
|
} else {
|
||||||
setLoading(false);
|
let QbXmlResponse;
|
||||||
return;
|
try {
|
||||||
}
|
QbXmlResponse = await axios.post(
|
||||||
|
"/accounting/qbxml/payables",
|
||||||
|
{ bills: billids },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error getting QBXML from Server.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("bills.errors.exporting", {
|
||||||
|
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (loadingCallback) loadingCallback(false);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let PartnerResponse;
|
try {
|
||||||
|
PartnerResponse = await axios.post(
|
||||||
try {
|
"http://localhost:1337/qb/",
|
||||||
PartnerResponse = await axios.post(
|
QbXmlResponse.data
|
||||||
"http://localhost:1337/qb/",
|
);
|
||||||
QbXmlResponse.data
|
} catch (error) {
|
||||||
);
|
console.log("Error connecting to quickbooks or partner.", error);
|
||||||
} catch (error) {
|
notification["error"]({
|
||||||
console.log("Error connecting to quickbooks or partner.", error);
|
message: t("bills.errors.exporting-partner"),
|
||||||
notification["error"]({
|
});
|
||||||
message: t("bills.errors.exporting-partner"),
|
if (!!loadingCallback) loadingCallback(false);
|
||||||
});
|
setLoading(false);
|
||||||
if (!!loadingCallback) loadingCallback(false);
|
return;
|
||||||
setLoading(false);
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
|
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
|
||||||
const groupedData = _.groupBy(PartnerResponse.data, "id");
|
const groupedData = _.groupBy(
|
||||||
|
PartnerResponse.data,
|
||||||
|
bodyshop.accountingconfig.qbo ? "billid" : "id"
|
||||||
|
);
|
||||||
|
|
||||||
const proms = [];
|
const proms = [];
|
||||||
Object.keys(groupedData).forEach((key) => {
|
Object.keys(groupedData).forEach((key) => {
|
||||||
proms.push(
|
proms.push(
|
||||||
|
|||||||
@@ -38,44 +38,53 @@ export function PayableExportButton({
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
if (!!loadingCallback) loadingCallback(true);
|
if (!!loadingCallback) loadingCallback(true);
|
||||||
|
|
||||||
let QbXmlResponse;
|
//Check if it's a QBO Setup.
|
||||||
try {
|
|
||||||
QbXmlResponse = await axios.post(
|
|
||||||
"/accounting/qbxml/payables",
|
|
||||||
{ bills: [billId] },
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error getting QBXML from Server.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("bills.errors.exporting", {
|
|
||||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
if (loadingCallback) loadingCallback(false);
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let PartnerResponse;
|
let PartnerResponse;
|
||||||
|
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||||
try {
|
PartnerResponse = await axios.post(`/qbo/payables`, {
|
||||||
PartnerResponse = await axios.post(
|
withCredentials: true,
|
||||||
"http://localhost:1337/qb/",
|
bills: [billId],
|
||||||
QbXmlResponse.data
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error connecting to quickbooks or partner.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("bills.errors.exporting-partner"),
|
|
||||||
});
|
});
|
||||||
if (!!loadingCallback) loadingCallback(false);
|
} else {
|
||||||
setLoading(false);
|
//Default is QBD
|
||||||
return;
|
|
||||||
|
let QbXmlResponse;
|
||||||
|
try {
|
||||||
|
QbXmlResponse = await axios.post(
|
||||||
|
"/accounting/qbxml/payables",
|
||||||
|
{ bills: [billId] },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error getting QBXML from Server.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("bills.errors.exporting", {
|
||||||
|
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (loadingCallback) loadingCallback(false);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PartnerResponse = await axios.post(
|
||||||
|
"http://localhost:1337/qb/",
|
||||||
|
QbXmlResponse.data
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error connecting to quickbooks or partner.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("bills.errors.exporting-partner"),
|
||||||
|
});
|
||||||
|
if (!!loadingCallback) loadingCallback(false);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
|
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
|
||||||
@@ -123,7 +132,14 @@ export function PayableExportButton({
|
|||||||
});
|
});
|
||||||
const billUpdateResponse = await updateBill({
|
const billUpdateResponse = await updateBill({
|
||||||
variables: {
|
variables: {
|
||||||
billIdList: successfulTransactions.map((st) => st.id),
|
billIdList: successfulTransactions.map(
|
||||||
|
(st) =>
|
||||||
|
st[
|
||||||
|
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
|
||||||
|
? "billid"
|
||||||
|
: "id"
|
||||||
|
]
|
||||||
|
),
|
||||||
bill: {
|
bill: {
|
||||||
exported: true,
|
exported: true,
|
||||||
exported_at: new Date(),
|
exported_at: new Date(),
|
||||||
|
|||||||
@@ -1,28 +1,35 @@
|
|||||||
import { Card, Col, Input, Row, Space, Typography } from "antd";
|
import { Card, Col, Input, Row, Space, Typography } from "antd";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectPrintCenter } from "../../redux/modals/modals.selectors";
|
import { selectPrintCenter } from "../../redux/modals/modals.selectors";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
import Jobd3RdPartyModal from "../job-3rd-party-modal/job-3rd-party-modal.component";
|
import Jobd3RdPartyModal from "../job-3rd-party-modal/job-3rd-party-modal.component";
|
||||||
import PrintCenterItem from "../print-center-item/print-center-item.component";
|
import PrintCenterItem from "../print-center-item/print-center-item.component";
|
||||||
import PrintCenterSpeedPrint from "../print-center-speed-print/print-center-speed-print.component";
|
import PrintCenterSpeedPrint from "../print-center-speed-print/print-center-speed-print.component";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
printCenterModal: selectPrintCenter,
|
printCenterModal: selectPrintCenter,
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({});
|
const mapDispatchToProps = (dispatch) => ({});
|
||||||
|
|
||||||
export function PrintCenterJobsComponent({ printCenterModal }) {
|
export function PrintCenterJobsComponent({ printCenterModal, bodyshop }) {
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
const { id: jobId } = printCenterModal.context;
|
const { id: jobId } = printCenterModal.context;
|
||||||
const tempList = TemplateList("job", {});
|
const tempList = TemplateList("job", {});
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const JobsReportsList = Object.keys(tempList).map((key) => {
|
const JobsReportsList = Object.keys(tempList)
|
||||||
return tempList[key];
|
.map((key) => {
|
||||||
});
|
return tempList[key];
|
||||||
|
})
|
||||||
|
.filter(
|
||||||
|
(temp) =>
|
||||||
|
!temp.regions || (temp.regions && temp.regions[bodyshop.region_config])
|
||||||
|
);
|
||||||
|
|
||||||
const filteredJobsReportsList =
|
const filteredJobsReportsList =
|
||||||
search !== ""
|
search !== ""
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import { Button, Space } from "antd";
|
import { Space, Tag } from "antd";
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
import React, { useEffect } from "react";
|
|
||||||
//import QboImg from "./qbo_signin.png";
|
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { useLocation } from "react-router-dom";
|
import React, { useEffect } from "react";
|
||||||
import { useCookies } from "react-cookie";
|
import { useCookies } from "react-cookie";
|
||||||
|
import { useHistory, useLocation } from "react-router-dom";
|
||||||
|
import QboSignIn from "../../assets/qbo/C2QB_green_btn_med_default.svg";
|
||||||
|
|
||||||
export default function QboAuthorizeComponent() {
|
export default function QboAuthorizeComponent() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const history = useHistory();
|
||||||
const [, setCookie] = useCookies(["access_token", "refresh_token"]);
|
const [cookies, setCookie] = useCookies(["access_token", "refresh_token"]);
|
||||||
|
|
||||||
const handleQbSignIn = async () => {
|
const handleQbSignIn = async () => {
|
||||||
const result = await Axios.post("/qbo/authorize");
|
const result = await Axios.post("/qbo/authorize");
|
||||||
console.log("pushing to history", result.data);
|
|
||||||
window.location.href = result.data;
|
window.location.href = result.data;
|
||||||
};
|
};
|
||||||
const qs = queryString.parse(location.search);
|
const qs = queryString.parse(location.search);
|
||||||
@@ -35,42 +34,24 @@ export default function QboAuthorizeComponent() {
|
|||||||
path: "/",
|
path: "/",
|
||||||
expires,
|
expires,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
history.push({ pathname: `/manage/accounting/receivables` });
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [qs, location, setCookie]);
|
}, [qs, location, setCookie]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Space>
|
||||||
<Space wrap>
|
<img
|
||||||
<Button onClick={handleQbSignIn}>
|
onClick={handleQbSignIn}
|
||||||
{/* <img
|
alt="Sign In to QuickBooks Online"
|
||||||
src={QboImg}
|
src={QboSignIn}
|
||||||
alt="Sign in with Intuit"
|
style={{ cursor: "pointer" }}
|
||||||
onClick={handleQbSignIn}
|
/>
|
||||||
/> */}
|
{!cookies.qbo_realmId && (
|
||||||
auth
|
<Tag color="red">No QuickBooks company has been connected.</Tag>
|
||||||
</Button>
|
)}
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
const response = await Axios.get(`/qbo/refresh`, {
|
|
||||||
withCredentials: true,
|
|
||||||
});
|
|
||||||
console.log(response);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Refresh Token
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
const response = await Axios.post(`/qbo/receivables`, {
|
|
||||||
withCredentials: true,
|
|
||||||
});
|
|
||||||
console.log(response);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
REC
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
{error && JSON.parse(decodeURIComponent(error)).error_description}
|
{error && JSON.parse(decodeURIComponent(error)).error_description}
|
||||||
</div>
|
</Space>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
|
import * as Utils from "../scoreboard-targets-table/scoreboard-targets-table.util";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
|
const graphProps = {
|
||||||
|
strokeWidth: 3,
|
||||||
|
};
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
@@ -51,7 +56,7 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const theValue = {
|
const theValue = {
|
||||||
date: moment(val).format("D dd"),
|
date: moment(val).format("D ddd"),
|
||||||
paintHrs: _.round(dayhrs.painthrs, 1),
|
paintHrs: _.round(dayhrs.painthrs, 1),
|
||||||
bodyHrs: _.round(dayhrs.bodyhrs, 1),
|
bodyHrs: _.round(dayhrs.bodyhrs, 1),
|
||||||
accTargetHrs: _.round(
|
accTargetHrs: _.round(
|
||||||
@@ -81,36 +86,37 @@ export function ScoreboardChart({ sbEntriesByDate, bodyshop }) {
|
|||||||
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
|
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
|
||||||
>
|
>
|
||||||
<CartesianGrid stroke="#f5f5f5" />
|
<CartesianGrid stroke="#f5f5f5" />
|
||||||
<XAxis dataKey="date" />
|
<XAxis dataKey="date" strokeWidth={graphProps.strokeWidth} />
|
||||||
<YAxis />
|
<YAxis strokeWidth={graphProps.strokeWidth} />
|
||||||
<Tooltip />
|
<Tooltip />
|
||||||
<Legend />
|
<Legend />
|
||||||
<Area
|
<Area
|
||||||
type="monotone"
|
type="monotone"
|
||||||
name="Accumulated Hours"
|
name="Accumulated Hours"
|
||||||
dataKey="accHrs"
|
dataKey="accHrs"
|
||||||
fill="#8884d8"
|
fill="lightgreen"
|
||||||
stroke="#8884d8"
|
stroke="green"
|
||||||
/>
|
/>
|
||||||
<Bar
|
<Bar
|
||||||
name="Body Hours"
|
name="Body Hours"
|
||||||
dataKey="bodyHrs"
|
dataKey="bodyHrs"
|
||||||
stackId="day"
|
stackId="day"
|
||||||
barSize={20}
|
barSize={20}
|
||||||
fill="#cecece"
|
fill="darkblue"
|
||||||
/>
|
/>
|
||||||
<Bar
|
<Bar
|
||||||
name="Paint Hours"
|
name="Paint Hours"
|
||||||
dataKey="paintHrs"
|
dataKey="paintHrs"
|
||||||
stackId="day"
|
stackId="day"
|
||||||
barSize={20}
|
barSize={20}
|
||||||
fill="#413ea0"
|
fill="darkred"
|
||||||
/>
|
/>
|
||||||
<Line
|
<Line
|
||||||
name="Target Hours"
|
name="Target Hours"
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey="accTargetHrs"
|
dataKey="accTargetHrs"
|
||||||
stroke="#ff7300"
|
stroke="#ff7300"
|
||||||
|
strokeWidth={graphProps.strokeWidth}
|
||||||
/>
|
/>
|
||||||
</ComposedChart>
|
</ComposedChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
|
|||||||
@@ -1,12 +1,33 @@
|
|||||||
import { Col, Row } from "antd";
|
import { Col, Row } from "antd";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import ScoreboardChart from "../scoreboard-chart/scoreboard-chart.component";
|
import ScoreboardChart from "../scoreboard-chart/scoreboard-chart.component";
|
||||||
import ScoreboardLastDays from "../scoreboard-last-days/scoreboard-last-days.component";
|
import ScoreboardLastDays from "../scoreboard-last-days/scoreboard-last-days.component";
|
||||||
import ScoreboardTargetsTable from "../scoreboard-targets-table/scoreboard-targets-table.component";
|
import ScoreboardTargetsTable from "../scoreboard-targets-table/scoreboard-targets-table.component";
|
||||||
|
|
||||||
export default function ScoreboardDisplayComponent({ scoreboardSubscription }) {
|
import { connect } from "react-redux";
|
||||||
const { data } = scoreboardSubscription;
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import moment from "moment";
|
||||||
|
import { useApolloClient } from "@apollo/client";
|
||||||
|
import { GET_BLOCKED_DAYS } from "../../graphql/scoreboard.queries";
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//currentUser: selectCurrentUser
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(ScoreboardDisplayComponent);
|
||||||
|
|
||||||
|
export function ScoreboardDisplayComponent({
|
||||||
|
bodyshop,
|
||||||
|
scoreboardSubscription,
|
||||||
|
}) {
|
||||||
|
const { data } = scoreboardSubscription;
|
||||||
|
const client = useApolloClient();
|
||||||
const scoreBoardlist = (data && data.scoreboard) || [];
|
const scoreBoardlist = (data && data.scoreboard) || [];
|
||||||
|
|
||||||
const sbEntriesByDate = {};
|
const sbEntriesByDate = {};
|
||||||
@@ -19,6 +40,29 @@ export default function ScoreboardDisplayComponent({ scoreboardSubscription }) {
|
|||||||
sbEntriesByDate[entryDate].push(i);
|
sbEntriesByDate[entryDate].push(i);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
//Update the locals.
|
||||||
|
async function setMomentSettings() {
|
||||||
|
const {
|
||||||
|
data: { appointments },
|
||||||
|
} = await client.query({
|
||||||
|
query: GET_BLOCKED_DAYS,
|
||||||
|
variables: {
|
||||||
|
start: moment().startOf("month"),
|
||||||
|
end: moment().endOf("month"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
moment.updateLocale("ca", {
|
||||||
|
workingWeekdays: translateSettingsToWorkingDays(bodyshop.workingdays),
|
||||||
|
holidays: appointments.map((h) => moment(h.start).format("MM-DD-YYYY")),
|
||||||
|
holidayFormat: "MM-DD-YYYY",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setMomentSettings();
|
||||||
|
}, [client, bodyshop]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row gutter={[16, 16]}>
|
<Row gutter={[16, 16]}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
@@ -35,3 +79,30 @@ export default function ScoreboardDisplayComponent({ scoreboardSubscription }) {
|
|||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function translateSettingsToWorkingDays(workingdays) {
|
||||||
|
const days = [];
|
||||||
|
|
||||||
|
if (workingdays.monday) {
|
||||||
|
days.push(1);
|
||||||
|
}
|
||||||
|
if (workingdays.tuesday) {
|
||||||
|
days.push(2);
|
||||||
|
}
|
||||||
|
if (workingdays.wednesday) {
|
||||||
|
days.push(3);
|
||||||
|
}
|
||||||
|
if (workingdays.thursday) {
|
||||||
|
days.push(4);
|
||||||
|
}
|
||||||
|
if (workingdays.friday) {
|
||||||
|
days.push(5);
|
||||||
|
}
|
||||||
|
if (workingdays.saturday) {
|
||||||
|
days.push(6);
|
||||||
|
}
|
||||||
|
if (workingdays.sunday) {
|
||||||
|
days.push(0);
|
||||||
|
}
|
||||||
|
return days;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import moment from "moment-business-days";
|
import moment from "moment-business-days";
|
||||||
|
|
||||||
moment.updateLocale("ca", {
|
// moment.updateLocale("ca", {
|
||||||
workingWeekdays: [1, 2, 3, 4, 5],
|
// workingWeekdays: [1, 2, 3, 4, 5, 6],
|
||||||
});
|
// });
|
||||||
|
|
||||||
export const CalculateWorkingDaysThisMonth = () => {
|
export const CalculateWorkingDaysThisMonth = () => {
|
||||||
return moment().endOf("month").businessDaysIntoMonth();
|
return moment().endOf("month").businessDaysIntoMonth();
|
||||||
|
|||||||
@@ -121,6 +121,13 @@ export default function ShopInfoGeneral({ form }) {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")}>
|
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")}>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.qbo")}
|
||||||
|
valuePropName="checked"
|
||||||
|
name={["accountingconfig", "qbo"]}
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.labels.accountingtiers")}
|
label={t("bodyshop.labels.accountingtiers")}
|
||||||
rules={[
|
rules={[
|
||||||
|
|||||||
@@ -63,12 +63,12 @@ export const logImEXEvent = (eventName, additionalParams, stateProp = null) => {
|
|||||||
null,
|
null,
|
||||||
...additionalParams,
|
...additionalParams,
|
||||||
};
|
};
|
||||||
console.log(
|
// console.log(
|
||||||
"%c[Analytics]",
|
// "%c[Analytics]",
|
||||||
"background-color: green ;font-weight:bold;",
|
// "background-color: green ;font-weight:bold;",
|
||||||
eventName,
|
// eventName,
|
||||||
eventParams
|
// eventParams
|
||||||
);
|
// );
|
||||||
logEvent(analytics, eventName, eventParams);
|
logEvent(analytics, eventName, eventParams);
|
||||||
|
|
||||||
//Log event to OpenReplay server.
|
//Log event to OpenReplay server.
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ export const QUERY_PARTS_QUEUE = gql`
|
|||||||
) {
|
) {
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
|
ownr_co_nm
|
||||||
ownr_ph1
|
ownr_ph1
|
||||||
ownr_ea
|
ownr_ea
|
||||||
plate_no
|
plate_no
|
||||||
@@ -98,6 +99,7 @@ export const QUERY_PARTS_QUEUE = gql`
|
|||||||
status
|
status
|
||||||
updated_at
|
updated_at
|
||||||
vehicleid
|
vehicleid
|
||||||
|
ownerid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
@@ -110,6 +112,7 @@ export const SUBSCRIPTION_JOBS_IN_PRODUCTION = gql`
|
|||||||
ro_number
|
ro_number
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
|
ownr_co_nm
|
||||||
v_model_yr
|
v_model_yr
|
||||||
v_model_desc
|
v_model_desc
|
||||||
clm_no
|
clm_no
|
||||||
@@ -540,6 +543,7 @@ export const GET_JOB_BY_PK = gql`
|
|||||||
db_ref
|
db_ref
|
||||||
manual_line
|
manual_line
|
||||||
prt_dsmk_p
|
prt_dsmk_p
|
||||||
|
prt_dsmk_m
|
||||||
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
billlines(limit: 1, order_by: { bill: { date: desc } }) {
|
||||||
id
|
id
|
||||||
quantity
|
quantity
|
||||||
@@ -1636,6 +1640,7 @@ export const QUERY_ALL_JOBS_PAGINATED_STATUS_FILTERED = gql`
|
|||||||
) {
|
) {
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
|
ownr_co_nm
|
||||||
ownerid
|
ownerid
|
||||||
ownr_ph1
|
ownr_ph1
|
||||||
ownr_ea
|
ownr_ea
|
||||||
|
|||||||
@@ -51,3 +51,34 @@ export const UPDATE_SCOREBOARD_ENTRY = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_SCOREBOARD_ENTRY = gql`
|
||||||
|
query QUERY_SCOREBOARD_ENTRY($jobid: uuid!) {
|
||||||
|
scoreboard(where: { jobid: { _eq: $jobid } }) {
|
||||||
|
bodyhrs
|
||||||
|
date
|
||||||
|
id
|
||||||
|
painthrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_BLOCKED_DAYS = gql`
|
||||||
|
query GET_BLOCKED_DAYS($start: timestamptz, $end: timestamptz) {
|
||||||
|
appointments(
|
||||||
|
where: {
|
||||||
|
_and: [
|
||||||
|
{ block: { _eq: true } }
|
||||||
|
{ canceled: { _eq: false } }
|
||||||
|
{ start: { _gte: $start } }
|
||||||
|
{ end: { _lte: $end } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
block
|
||||||
|
start
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("Manual State", state);
|
|
||||||
const handleFinish = (values) => {
|
const handleFinish = (values) => {
|
||||||
let job = Object.assign(
|
let job = Object.assign(
|
||||||
{},
|
{},
|
||||||
@@ -142,6 +141,10 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
job = { ...job, ...ownerData };
|
job = { ...job, ...ownerData };
|
||||||
|
|
||||||
|
if (job.owner === null) delete job.owner;
|
||||||
|
if (job.vehicle === null) delete job.vehicle;
|
||||||
|
|
||||||
runInsertJob(job);
|
runInsertJob(job);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ export function PartsQueuePageComponent({ bodyshop }) {
|
|||||||
// sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
// sorter: (a, b) => alphaSort(a.ownr_ln, b.ownr_ln),
|
||||||
// sortOrder: sortcolumn === "owner" && sortorder,
|
// sortOrder: sortcolumn === "owner" && sortorder,
|
||||||
render: (text, record) => {
|
render: (text, record) => {
|
||||||
return record.owner ? (
|
return record.ownerid ? (
|
||||||
<Link to={"/manage/owners/" + record.owner.id}>
|
<Link to={"/manage/owners/" + record.ownerid}>
|
||||||
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
{`${record.ownr_fn || ""} ${record.ownr_ln || ""} ${
|
||||||
record.ownr_co_nm || ""
|
record.ownr_co_nm || ""
|
||||||
}`}
|
}`}
|
||||||
|
|||||||
@@ -509,6 +509,7 @@
|
|||||||
"orderstatuses": "Order Statuses",
|
"orderstatuses": "Order Statuses",
|
||||||
"partslocations": "Parts Locations",
|
"partslocations": "Parts Locations",
|
||||||
"printlater": "Print Later",
|
"printlater": "Print Later",
|
||||||
|
"qbo": "Use QuickBooks Online?",
|
||||||
"rbac": "Role Based Access Control",
|
"rbac": "Role Based Access Control",
|
||||||
"responsibilitycenters": {
|
"responsibilitycenters": {
|
||||||
"costs": "Cost Centers",
|
"costs": "Cost Centers",
|
||||||
@@ -1952,6 +1953,7 @@
|
|||||||
"coversheet_landscape": "Coversheet (Landscape)",
|
"coversheet_landscape": "Coversheet (Landscape)",
|
||||||
"coversheet_portrait": "Coversheet Portrait",
|
"coversheet_portrait": "Coversheet Portrait",
|
||||||
"csi_invitation": "CSI Invitation",
|
"csi_invitation": "CSI Invitation",
|
||||||
|
"csi_invitation_action": "CSI Invite",
|
||||||
"diagnostic_authorization": "Diagnostic Authorization",
|
"diagnostic_authorization": "Diagnostic Authorization",
|
||||||
"estimate": "Estimate Only",
|
"estimate": "Estimate Only",
|
||||||
"estimate_detail": "Estimate Details",
|
"estimate_detail": "Estimate Details",
|
||||||
@@ -1982,6 +1984,8 @@
|
|||||||
"qc_sheet": "Quality Control Sheet",
|
"qc_sheet": "Quality Control Sheet",
|
||||||
"ro_totals": "RO Totals",
|
"ro_totals": "RO Totals",
|
||||||
"ro_with_description": "RO Summary with Descriptions",
|
"ro_with_description": "RO Summary with Descriptions",
|
||||||
|
"sgi_certificate_of_repairs": "SGI - Certificate of Repairs",
|
||||||
|
"sgi_windshield_auth": "SGI - Windshield Authorization",
|
||||||
"stolen_recovery_checklist": "Stolen Recovery Checklist",
|
"stolen_recovery_checklist": "Stolen Recovery Checklist",
|
||||||
"supplement_request": "Supplement Request",
|
"supplement_request": "Supplement Request",
|
||||||
"thank_you_ro": "Thank You Letter",
|
"thank_you_ro": "Thank You Letter",
|
||||||
|
|||||||
@@ -509,6 +509,7 @@
|
|||||||
"orderstatuses": "",
|
"orderstatuses": "",
|
||||||
"partslocations": "",
|
"partslocations": "",
|
||||||
"printlater": "",
|
"printlater": "",
|
||||||
|
"qbo": "",
|
||||||
"rbac": "",
|
"rbac": "",
|
||||||
"responsibilitycenters": {
|
"responsibilitycenters": {
|
||||||
"costs": "",
|
"costs": "",
|
||||||
@@ -1952,6 +1953,7 @@
|
|||||||
"coversheet_landscape": "",
|
"coversheet_landscape": "",
|
||||||
"coversheet_portrait": "",
|
"coversheet_portrait": "",
|
||||||
"csi_invitation": "",
|
"csi_invitation": "",
|
||||||
|
"csi_invitation_action": "",
|
||||||
"diagnostic_authorization": "",
|
"diagnostic_authorization": "",
|
||||||
"estimate": "",
|
"estimate": "",
|
||||||
"estimate_detail": "",
|
"estimate_detail": "",
|
||||||
@@ -1982,6 +1984,8 @@
|
|||||||
"qc_sheet": "",
|
"qc_sheet": "",
|
||||||
"ro_totals": "",
|
"ro_totals": "",
|
||||||
"ro_with_description": "",
|
"ro_with_description": "",
|
||||||
|
"sgi_certificate_of_repairs": "",
|
||||||
|
"sgi_windshield_auth": "",
|
||||||
"stolen_recovery_checklist": "",
|
"stolen_recovery_checklist": "",
|
||||||
"supplement_request": "",
|
"supplement_request": "",
|
||||||
"thank_you_ro": "",
|
"thank_you_ro": "",
|
||||||
|
|||||||
@@ -509,6 +509,7 @@
|
|||||||
"orderstatuses": "",
|
"orderstatuses": "",
|
||||||
"partslocations": "",
|
"partslocations": "",
|
||||||
"printlater": "",
|
"printlater": "",
|
||||||
|
"qbo": "",
|
||||||
"rbac": "",
|
"rbac": "",
|
||||||
"responsibilitycenters": {
|
"responsibilitycenters": {
|
||||||
"costs": "",
|
"costs": "",
|
||||||
@@ -1952,6 +1953,7 @@
|
|||||||
"coversheet_landscape": "",
|
"coversheet_landscape": "",
|
||||||
"coversheet_portrait": "",
|
"coversheet_portrait": "",
|
||||||
"csi_invitation": "",
|
"csi_invitation": "",
|
||||||
|
"csi_invitation_action": "",
|
||||||
"diagnostic_authorization": "",
|
"diagnostic_authorization": "",
|
||||||
"estimate": "",
|
"estimate": "",
|
||||||
"estimate_detail": "",
|
"estimate_detail": "",
|
||||||
@@ -1982,6 +1984,8 @@
|
|||||||
"qc_sheet": "",
|
"qc_sheet": "",
|
||||||
"ro_totals": "",
|
"ro_totals": "",
|
||||||
"ro_with_description": "",
|
"ro_with_description": "",
|
||||||
|
"sgi_certificate_of_repairs": "",
|
||||||
|
"sgi_windshield_auth": "",
|
||||||
"stolen_recovery_checklist": "",
|
"stolen_recovery_checklist": "",
|
||||||
"supplement_request": "",
|
"supplement_request": "",
|
||||||
"thank_you_ro": "",
|
"thank_you_ro": "",
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ const roundTripLink = new ApolloLink((operation, forward) => {
|
|||||||
return forward(operation).map((data) => {
|
return forward(operation).map((data) => {
|
||||||
// Called after server responds
|
// Called after server responds
|
||||||
const time = new Date() - operation.getContext().start;
|
const time = new Date() - operation.getContext().start;
|
||||||
console.log(
|
// console.log(
|
||||||
`Operation ${operation.operationName} took ${time} to complete`
|
// `Operation ${operation.operationName} took ${time} to complete`
|
||||||
);
|
// );
|
||||||
TrackExecutionTime(operation.operationName, time);
|
TrackExecutionTime(operation.operationName, time);
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -352,6 +352,28 @@ export const TemplateList = (type, context) => {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
group: "ro",
|
group: "ro",
|
||||||
},
|
},
|
||||||
|
sgi_certificate_of_repairs: {
|
||||||
|
title: i18n.t("printcenter.jobs.sgi_certificate_of_repairs"),
|
||||||
|
description: "Thank You Letter by RO",
|
||||||
|
key: "sgi_certificate_of_repairs",
|
||||||
|
subject: i18n.t("printcenter.jobs.sgi_certificate_of_repairs"),
|
||||||
|
disabled: false,
|
||||||
|
group: "ro",
|
||||||
|
regions: {
|
||||||
|
CA_SK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sgi_windshield_auth: {
|
||||||
|
title: i18n.t("printcenter.jobs.sgi_windshield_auth"),
|
||||||
|
description: "Thank You Letter by RO",
|
||||||
|
key: "sgi_windshield_auth",
|
||||||
|
subject: i18n.t("printcenter.jobs.sgi_windshield_auth"),
|
||||||
|
disabled: false,
|
||||||
|
group: "pre",
|
||||||
|
regions: {
|
||||||
|
CA_SK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
// parts_label_multi: {
|
// parts_label_multi: {
|
||||||
// title: i18n.t("printcenter.jobs.parts_label_multi"),
|
// title: i18n.t("printcenter.jobs.parts_label_multi"),
|
||||||
// description: "Thank You Letter by RO",
|
// description: "Thank You Letter by RO",
|
||||||
@@ -370,6 +392,13 @@ export const TemplateList = (type, context) => {
|
|||||||
key: "special_thirdpartypayer",
|
key: "special_thirdpartypayer",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
},
|
},
|
||||||
|
csi_invitation_action: {
|
||||||
|
title: i18n.t("printcenter.jobs.csi_invitation_action"),
|
||||||
|
description: "CSI invite",
|
||||||
|
key: "csi_invitation_action",
|
||||||
|
subject: i18n.t("printcenter.jobs.csi_invitation_action"),
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
...(!type || type === "appointment"
|
...(!type || type === "appointment"
|
||||||
|
|||||||
848
client/yarn.lock
@@ -1,2 +1,7 @@
|
|||||||
|
version: 2
|
||||||
endpoint: https://bodyshop-dev-db.herokuapp.com
|
endpoint: https://bodyshop-dev-db.herokuapp.com
|
||||||
admin_secret: Dev-BodyShopApp!
|
admin_secret: Dev-BodyShopApp!
|
||||||
|
metadata_directory: metadata
|
||||||
|
actions:
|
||||||
|
kind: synchronous
|
||||||
|
handler_webhook_baseurl: http://localhost:3000
|
||||||
|
|||||||
0
hasura/metadata/actions.graphql
Normal file
6
hasura/metadata/actions.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
actions: []
|
||||||
|
custom_types:
|
||||||
|
enums: []
|
||||||
|
input_objects: []
|
||||||
|
objects: []
|
||||||
|
scalars: []
|
||||||
1
hasura/metadata/allow_list.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
1
hasura/metadata/cron_triggers.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
27
hasura/metadata/functions.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_bills
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_cccontracts
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_dms_vehicles
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_exportlog
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_jobs
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_owners
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_payments
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_phonebook
|
||||||
|
- function:
|
||||||
|
schema: public
|
||||||
|
name: search_vehicles
|
||||||
1
hasura/metadata/query_collections.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
1
hasura/metadata/remote_schemas.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
4775
hasura/metadata/tables.yaml
Normal file
1
hasura/metadata/version.yaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
version: 2
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" DROP COLUMN "website";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" ADD COLUMN "website" text NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."documents" DROP COLUMN "takenat";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."documents" ADD COLUMN "takenat" timestamptz NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" ADD CONSTRAINT "bodyshops_autohouseid_key" UNIQUE ("autohouseid");
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" DROP CONSTRAINT "bodyshops_autohouseid_key";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" DROP COLUMN "jc_hourly_rates";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" ADD COLUMN "jc_hourly_rates" jsonb NULL DEFAULT jsonb_build_object();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."vehicles" ALTER COLUMN "v_vin" SET NOT NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."vehicles" ALTER COLUMN "v_vin" DROP NOT NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE ONLY "public"."joblines" ALTER COLUMN "prt_dsmk_p" DROP DEFAULT;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE ONLY "public"."joblines" ALTER COLUMN "prt_dsmk_p" SET DEFAULT 0;
|
||||||
3
hasura/migrations/1622571551101_run_sql_migration/up.sql
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
update joblines
|
||||||
|
set prt_dsmk_p = 0 where joblines.prt_dsmk_p is null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."conversations" DROP COLUMN "archived";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."conversations" ADD COLUMN "archived" boolean NOT NULL DEFAULT false;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."parts_orders" DROP COLUMN "orderedby";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."parts_orders" ADD COLUMN "orderedby" text NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
alter table "public"."parts_orders" drop constraint "parts_orders_orderedby_fkey";
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
alter table "public"."parts_orders"
|
||||||
|
add constraint "parts_orders_orderedby_fkey"
|
||||||
|
foreign key ("orderedby")
|
||||||
|
references "public"."users"
|
||||||
|
("email") on update set null on delete set null;
|
||||||
8
hasura/migrations/1622650359491_set_fk_public_exportlog_billid/down.sql
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
alter table "public"."exportlog" drop constraint "exportlog_billid_fkey",
|
||||||
|
add constraint "exportlog_billid_fkey"
|
||||||
|
foreign key ("billid")
|
||||||
|
references "public"."bills"
|
||||||
|
("id")
|
||||||
|
on update restrict
|
||||||
|
on delete restrict;
|
||||||
6
hasura/migrations/1622650359491_set_fk_public_exportlog_billid/up.sql
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
alter table "public"."exportlog" drop constraint "exportlog_billid_fkey",
|
||||||
|
add constraint "exportlog_billid_fkey"
|
||||||
|
foreign key ("billid")
|
||||||
|
references "public"."bills"
|
||||||
|
("id") on update cascade on delete cascade;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
ALTER TABLE ONLY "public"."users" ALTER COLUMN "dashboardlayout" SET DEFAULT jsonb_build_array();
|
||||||
|
ALTER TABLE "public"."users" ALTER COLUMN "dashboardlayout" SET NOT NULL;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."users" ALTER COLUMN "dashboardlayout" DROP DEFAULT;
|
||||||
|
ALTER TABLE "public"."users" ALTER COLUMN "dashboardlayout" DROP NOT NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" DROP COLUMN "md_jobline_presets";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" ADD COLUMN "md_jobline_presets" jsonb NULL DEFAULT jsonb_build_array();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" DROP COLUMN "cdk_dealerid";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" ADD COLUMN "cdk_dealerid" text NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" DROP COLUMN "features";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" ADD COLUMN "features" jsonb NULL DEFAULT jsonb_build_object();
|
||||||
30
hasura/migrations/1625768789569_run_sql_migration/up.sql
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION public.search_payments(search text)
|
||||||
|
RETURNS SETOF payments
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
STABLE
|
||||||
|
AS $function$
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
if search = '' then
|
||||||
|
return query select * from payments ;
|
||||||
|
else
|
||||||
|
return query SELECT
|
||||||
|
p.*
|
||||||
|
FROM
|
||||||
|
payments p, jobs j
|
||||||
|
WHERE
|
||||||
|
p.jobid = j.id AND
|
||||||
|
(
|
||||||
|
search <% p.paymentnum OR
|
||||||
|
search <% j.ownr_fn OR
|
||||||
|
search <% j.ownr_ln OR
|
||||||
|
search <% j.ownr_co_nm OR
|
||||||
|
search <% j.ro_number OR
|
||||||
|
search <% (p.payer) OR
|
||||||
|
search <% (p.transactionid) OR
|
||||||
|
search <% (p.memo));
|
||||||
|
end if;
|
||||||
|
|
||||||
|
END
|
||||||
|
$function$;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
alter table "public"."vehicles" add constraint "vehicles_v_vin_shopid_key" unique ("v_vin", "shopid");
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
alter table "public"."vehicles" drop constraint "vehicles_v_vin_shopid_key";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" DROP COLUMN "attach_pdf_to_email";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."bodyshops" ADD COLUMN "attach_pdf_to_email" boolean NOT NULL DEFAULT False;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" ADD COLUMN "schemaname" text;
|
||||||
|
ALTER TABLE "public"."audit_trail" ALTER COLUMN "schemaname" DROP NOT NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" DROP COLUMN "schemaname" CASCADE;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" ADD COLUMN "tabname" text;
|
||||||
|
ALTER TABLE "public"."audit_trail" ALTER COLUMN "tabname" DROP NOT NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" DROP COLUMN "tabname" CASCADE;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" ADD COLUMN "recordid" uuid;
|
||||||
|
ALTER TABLE "public"."audit_trail" ALTER COLUMN "recordid" DROP NOT NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" DROP COLUMN "recordid" CASCADE;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" DROP COLUMN "jobid";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" ADD COLUMN "jobid" uuid NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" DROP COLUMN "billid";
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
ALTER TABLE "public"."audit_trail" ADD COLUMN "billid" uuid NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
alter table "public"."audit_trail" drop constraint "audit_trail_billid_fkey";
|
||||||
6
hasura/migrations/1626970720625_set_fk_public_audit_trail_billid/up.sql
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
alter table "public"."audit_trail"
|
||||||
|
add constraint "audit_trail_billid_fkey"
|
||||||
|
foreign key ("billid")
|
||||||
|
references "public"."bills"
|
||||||
|
("id") on update cascade on delete cascade;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
alter table "public"."audit_trail" drop constraint "audit_trail_billid_fkey",
|
||||||
|
add constraint "audit_trail_billid_fkey"
|
||||||
|
foreign key ("billid")
|
||||||
|
references "public"."bills"
|
||||||
|
("id")
|
||||||
|
on update cascade
|
||||||
|
on delete cascade;
|
||||||