Compare commits
67 Commits
feature/IO
...
feature/IO
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
168d4246af | ||
|
|
ee613db0cb | ||
|
|
55b892a74e | ||
|
|
16754d9657 | ||
|
|
33db67122c | ||
|
|
b9b88b0e23 | ||
|
|
992ad71910 | ||
|
|
51d1f926c2 | ||
|
|
c70447f337 | ||
|
|
dc367e1a30 | ||
|
|
ee7997ffbc | ||
|
|
2dbb5adbbb | ||
|
|
cc9f342575 | ||
|
|
252262f4a7 | ||
|
|
f77a16648f | ||
|
|
d6e3c54b68 | ||
|
|
7294e96a77 | ||
|
|
a4f4f45251 | ||
|
|
acc91abc0c | ||
|
|
18d5176cb9 | ||
|
|
d6d6ced7a4 | ||
|
|
52809cc849 | ||
|
|
c525b7ea3f | ||
|
|
e799417aaf | ||
|
|
ef077c2d48 | ||
|
|
e78b114544 | ||
|
|
df170ddd27 | ||
|
|
17928741e3 | ||
|
|
a6b825ffdf | ||
|
|
0d3bc0e95f | ||
|
|
0f62a2d73d | ||
|
|
bcdd32f92f | ||
|
|
6865fcbffc | ||
|
|
8e623c71a9 | ||
|
|
1e06502464 | ||
|
|
f2914868e2 | ||
|
|
561f0313f9 | ||
|
|
1e27c4e1ae | ||
|
|
46676ba8eb | ||
|
|
02a49efbea | ||
|
|
5f2a5e1025 | ||
|
|
42552e4f4f | ||
|
|
f2af78f056 | ||
|
|
269d55bde3 | ||
|
|
71f3dbbeb4 | ||
|
|
73dfb74ed7 | ||
|
|
7dd6baef33 | ||
|
|
519b532091 | ||
|
|
4fb9c37c0d | ||
|
|
c9b63be29f | ||
|
|
84bc735dce | ||
|
|
a44b9417a1 | ||
|
|
9050276ea7 | ||
|
|
059b854db9 | ||
|
|
6fd70b165b | ||
|
|
11d94cf286 | ||
|
|
c98a48ea14 | ||
|
|
b8fa80419b | ||
|
|
6513432993 | ||
|
|
132ecffc40 | ||
|
|
cdf02a8eac | ||
|
|
45c943a78f | ||
|
|
25d145d864 | ||
|
|
7822e3f90e | ||
|
|
a4a612fbe4 | ||
|
|
07da472e82 | ||
|
|
6475a8bdce |
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,12 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import { DeleteFilled } from "@ant-design/icons";
|
||||||
import { useLazyQuery, useMutation } from "@apollo/client";
|
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||||
import { Button, Card, Col, Form, Input, notification, Row, Space, Spin, Statistic } from "antd";
|
import { Button, Card, Col, Form, Input, Row, Space, Spin, Statistic, notification } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import dayjs from "../../utils/day";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { INSERT_PAYMENT_RESPONSE, QUERY_RO_AND_OWNER_BY_JOB_PKS } from "../../graphql/payment_response.queries";
|
import { INSERT_PAYMENT_RESPONSE, QUERY_RO_AND_OWNER_BY_JOB_PKS } from "../../graphql/payment_response.queries";
|
||||||
import { INSERT_NEW_PAYMENT } from "../../graphql/payments.queries";
|
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||||
import { selectCardPayment } from "../../redux/modals/modals.selectors";
|
import { selectCardPayment } from "../../redux/modals/modals.selectors";
|
||||||
@@ -28,12 +26,12 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisible, insertAuditTrail }) => {
|
const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisible, insertAuditTrail }) => {
|
||||||
const { context } = cardPaymentModal;
|
const { context, actions } = cardPaymentModal;
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
// const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
||||||
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -42,7 +40,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
skip: true
|
skip: true
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("🚀 ~ file: card-payment-modal.component..jsx:61 ~ data:", data);
|
|
||||||
//Initialize the intellipay window.
|
//Initialize the intellipay window.
|
||||||
const SetIntellipayCallbackFunctions = () => {
|
const SetIntellipayCallbackFunctions = () => {
|
||||||
console.log("*** Set IntelliPay callback functions.");
|
console.log("*** Set IntelliPay callback functions.");
|
||||||
@@ -51,16 +48,20 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.intellipay.runOnApproval(async function (response) {
|
window.intellipay.runOnApproval(async function (response) {
|
||||||
console.warn("*** Running On Approval Script ***");
|
//2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback.
|
||||||
form.setFieldValue("paymentResponse", response);
|
//Add a slight delay to allow the refetch to properly get the data.
|
||||||
form.submit();
|
setTimeout(() => {
|
||||||
|
if (actions && actions.refetch && typeof actions.refetch === "function")
|
||||||
|
actions.refetch();
|
||||||
|
setLoading(false);
|
||||||
|
toggleModalVisible();
|
||||||
|
}, 750);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.intellipay.runOnNonApproval(async function (response) {
|
window.intellipay.runOnNonApproval(async function (response) {
|
||||||
// Mutate unsuccessful payment
|
// Mutate unsuccessful payment
|
||||||
|
|
||||||
const { payments } = form.getFieldsValue();
|
const { payments } = form.getFieldsValue();
|
||||||
|
|
||||||
await insertPaymentResponse({
|
await insertPaymentResponse({
|
||||||
variables: {
|
variables: {
|
||||||
paymentResponse: payments.map((payment) => ({
|
paymentResponse: payments.map((payment) => ({
|
||||||
@@ -85,50 +86,9 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFinish = async (values) => {
|
|
||||||
try {
|
|
||||||
await insertPayment({
|
|
||||||
variables: {
|
|
||||||
paymentInput: values.payments.map((payment) => ({
|
|
||||||
amount: payment.amount,
|
|
||||||
transactionid: (values.paymentResponse.paymentid || "").toString(),
|
|
||||||
payer: t("payments.labels.customer"),
|
|
||||||
type: values.paymentResponse.cardbrand,
|
|
||||||
jobid: payment.jobid,
|
|
||||||
date: dayjs(Date.now()),
|
|
||||||
payment_responses: {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
amount: payment.amount,
|
|
||||||
bodyshopid: bodyshop.id,
|
|
||||||
|
|
||||||
jobid: payment.jobid,
|
|
||||||
declinereason: values.paymentResponse.declinereason,
|
|
||||||
ext_paymentid: values.paymentResponse.paymentid.toString(),
|
|
||||||
successful: true,
|
|
||||||
response: values.paymentResponse
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
refetchQueries: ["GET_JOB_BY_PK"]
|
|
||||||
});
|
|
||||||
toggleModalVisible();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
notification.open({
|
|
||||||
type: "error",
|
|
||||||
message: t("payments.errors.inserting", { error: error.message })
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleIntelliPayCharge = async () => {
|
const handleIntelliPayCharge = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
//Validate
|
//Validate
|
||||||
try {
|
try {
|
||||||
await form.validateFields();
|
await form.validateFields();
|
||||||
@@ -140,7 +100,8 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
try {
|
try {
|
||||||
const response = await axios.post("/intellipay/lightbox_credentials", {
|
const response = await axios.post("/intellipay/lightbox_credentials", {
|
||||||
bodyshop,
|
bodyshop,
|
||||||
refresh: !!window.intellipay
|
refresh: !!window.intellipay,
|
||||||
|
paymentSplitMeta: form.getFieldsValue(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window.intellipay) {
|
if (window.intellipay) {
|
||||||
@@ -169,7 +130,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
<Card title="Card Payment">
|
<Card title="Card Payment">
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<Form
|
<Form
|
||||||
onFinish={handleFinish}
|
|
||||||
form={form}
|
form={form}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
initialValues={{
|
initialValues={{
|
||||||
@@ -246,18 +206,14 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{() => {
|
{() => {
|
||||||
console.log("Updating the owner info section.");
|
|
||||||
//If all of the job ids have been fileld in, then query and update the IP field.
|
//If all of the job ids have been fileld in, then query and update the IP field.
|
||||||
const { payments } = form.getFieldsValue();
|
const { payments } = form.getFieldsValue();
|
||||||
if (payments?.length > 0 && payments?.filter((p) => p?.jobid).length === payments?.length) {
|
if (
|
||||||
console.log("**Calling refetch.");
|
payments?.length > 0 &&
|
||||||
|
payments?.filter((p) => p?.jobid).length === payments?.length
|
||||||
|
) {
|
||||||
refetch({ jobids: payments.map((p) => p.jobid) });
|
refetch({ jobids: payments.map((p) => p.jobid) });
|
||||||
}
|
}
|
||||||
console.log(
|
|
||||||
"Acc info",
|
|
||||||
data,
|
|
||||||
payments && data && data.jobs.length > 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Input
|
<Input
|
||||||
@@ -300,6 +256,13 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
type="hidden"
|
type="hidden"
|
||||||
value={totalAmountToCharge?.toFixed(2)}
|
value={totalAmountToCharge?.toFixed(2)}
|
||||||
/>
|
/>
|
||||||
|
<Input
|
||||||
|
className="ipayfield"
|
||||||
|
data-ipayname="comment"
|
||||||
|
type="hidden"
|
||||||
|
value={btoa(JSON.stringify(payments))}
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
// data-ipayname="submit"
|
// data-ipayname="submit"
|
||||||
@@ -314,11 +277,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{/* Lightbox payment response when it is completed */}
|
|
||||||
<Form.Item name="paymentResponse" hidden>
|
|
||||||
<Input type="hidden" />
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
</Form>
|
||||||
</Spin>
|
</Spin>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const CourtesyCarStatusComponent = ({ value, onChange }, ref) => {
|
|||||||
<Option value="courtesycars.status.out">{t("courtesycars.status.out")}</Option>
|
<Option value="courtesycars.status.out">{t("courtesycars.status.out")}</Option>
|
||||||
<Option value="courtesycars.status.sold">{t("courtesycars.status.sold")}</Option>
|
<Option value="courtesycars.status.sold">{t("courtesycars.status.sold")}</Option>
|
||||||
<Option value="courtesycars.status.leasereturn">{t("courtesycars.status.leasereturn")}</Option>
|
<Option value="courtesycars.status.leasereturn">{t("courtesycars.status.leasereturn")}</Option>
|
||||||
|
<Option value="courtesycars.status.unavailable">{t("courtesycars.status.unavailable")}</Option>
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,7 +61,11 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
|
|||||||
{
|
{
|
||||||
text: t("courtesycars.status.leasereturn"),
|
text: t("courtesycars.status.leasereturn"),
|
||||||
value: "courtesycars.status.leasereturn"
|
value: "courtesycars.status.leasereturn"
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
text: t("courtesycars.status.unavailable"),
|
||||||
|
value: "courtesycars.status.unavailable",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
onFilter: (value, record) => record.status === value,
|
onFilter: (value, record) => record.status === value,
|
||||||
sortOrder: state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
sortOrder: state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { BsKanban } from "react-icons/bs";
|
import { BsKanban } from "react-icons/bs";
|
||||||
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar, FaTasks } from "react-icons/fa";
|
import { FaCalendarAlt, FaCarCrash, FaCreditCard, FaFileInvoiceDollar, FaTasks } from "react-icons/fa";
|
||||||
|
import { FiLogOut } from "react-icons/fi";
|
||||||
import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from "react-icons/gi";
|
import { GiPayMoney, GiPlayerTime, GiSettingsKnobs } from "react-icons/gi";
|
||||||
import { IoBusinessOutline } from "react-icons/io5";
|
import { IoBusinessOutline } from "react-icons/io5";
|
||||||
import { RiSurveyLine } from "react-icons/ri";
|
import { RiSurveyLine } from "react-icons/ri";
|
||||||
@@ -42,7 +43,6 @@ import { selectRecentItems, selectSelectedHeader } from "../../redux/application
|
|||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { signOutStart } from "../../redux/user/user.actions";
|
import { signOutStart } from "../../redux/user/user.actions";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import { FiLogOut } from "react-icons/fi";
|
|
||||||
import { checkBeta, handleBeta, setBeta } from "../../utils/betaHandler";
|
import { checkBeta, handleBeta, setBeta } from "../../utils/betaHandler";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
@@ -311,10 +311,12 @@ function Header({
|
|||||||
{
|
{
|
||||||
key: "home",
|
key: "home",
|
||||||
icon: <HomeFilled />,
|
icon: <HomeFilled />,
|
||||||
|
id: "header-home",
|
||||||
label: <Link to="/manage/">{t("menus.header.home")}</Link>
|
label: <Link to="/manage/">{t("menus.header.home")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "schedule",
|
key: "schedule",
|
||||||
|
id: "header-schedule",
|
||||||
icon: <Icon component={FaCalendarAlt} />,
|
icon: <Icon component={FaCalendarAlt} />,
|
||||||
label: <Link to="/manage/schedule">{t("menus.header.schedule")}</Link>
|
label: <Link to="/manage/schedule">{t("menus.header.schedule")}</Link>
|
||||||
},
|
},
|
||||||
@@ -326,16 +328,19 @@ function Header({
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "activejobs",
|
key: "activejobs",
|
||||||
|
id: "header-active-jobs",
|
||||||
icon: <FileFilled />,
|
icon: <FileFilled />,
|
||||||
label: <Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
|
label: <Link to="/manage/jobs">{t("menus.header.activejobs")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "readyjobs",
|
key: "readyjobs",
|
||||||
|
id: "header-ready-jobs",
|
||||||
icon: <CheckCircleOutlined />,
|
icon: <CheckCircleOutlined />,
|
||||||
label: <Link to="/manage/jobs/ready">{t("menus.header.readyjobs")}</Link>
|
label: <Link to="/manage/jobs/ready">{t("menus.header.readyjobs")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "parts-queue",
|
key: "parts-queue",
|
||||||
|
id: "header-parts-queue",
|
||||||
icon: <ToolFilled />,
|
icon: <ToolFilled />,
|
||||||
label: <Link to="/manage/partsqueue">{t("menus.header.parts-queue")}</Link>
|
label: <Link to="/manage/partsqueue">{t("menus.header.parts-queue")}</Link>
|
||||||
},
|
},
|
||||||
@@ -347,6 +352,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "newjob",
|
key: "newjob",
|
||||||
|
id: "header-new-job",
|
||||||
icon: <FileAddOutlined />,
|
icon: <FileAddOutlined />,
|
||||||
label: <Link to="/manage/jobs/new">{t("menus.header.newjob")}</Link>
|
label: <Link to="/manage/jobs/new">{t("menus.header.newjob")}</Link>
|
||||||
},
|
},
|
||||||
@@ -355,6 +361,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "alljobs",
|
key: "alljobs",
|
||||||
|
id: "header-all-jobs",
|
||||||
icon: <UnorderedListOutlined />,
|
icon: <UnorderedListOutlined />,
|
||||||
label: <Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
label: <Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
||||||
},
|
},
|
||||||
@@ -363,6 +370,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "productionlist",
|
key: "productionlist",
|
||||||
|
id: "header-production-list",
|
||||||
icon: <ScheduleOutlined />,
|
icon: <ScheduleOutlined />,
|
||||||
label: <Link to="/manage/production/list">{t("menus.header.productionlist")}</Link>
|
label: <Link to="/manage/production/list">{t("menus.header.productionlist")}</Link>
|
||||||
},
|
},
|
||||||
@@ -374,6 +382,7 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "productionboard",
|
key: "productionboard",
|
||||||
|
id: "header-production-board",
|
||||||
icon: <Icon component={BsKanban} />,
|
icon: <Icon component={BsKanban} />,
|
||||||
label: <Link to="/manage/production/board">{t("menus.header.productionboard")}</Link>
|
label: <Link to="/manage/production/board">{t("menus.header.productionboard")}</Link>
|
||||||
}
|
}
|
||||||
@@ -391,6 +400,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "scoreboard",
|
key: "scoreboard",
|
||||||
|
id: "header-scoreboard",
|
||||||
icon: <LineChartOutlined />,
|
icon: <LineChartOutlined />,
|
||||||
label: <Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
label: <Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
||||||
}
|
}
|
||||||
@@ -401,15 +411,18 @@ function Header({
|
|||||||
{
|
{
|
||||||
key: "customers",
|
key: "customers",
|
||||||
icon: <UserOutlined />,
|
icon: <UserOutlined />,
|
||||||
|
id: "header-customers",
|
||||||
label: t("menus.header.customers"),
|
label: t("menus.header.customers"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "owners",
|
key: "owners",
|
||||||
|
id: "header-owners",
|
||||||
icon: <TeamOutlined />,
|
icon: <TeamOutlined />,
|
||||||
label: <Link to="/manage/owners">{t("menus.header.owners")}</Link>
|
label: <Link to="/manage/owners">{t("menus.header.owners")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "vehicles",
|
key: "vehicles",
|
||||||
|
id: "header-vehicles",
|
||||||
icon: <CarFilled />,
|
icon: <CarFilled />,
|
||||||
label: <Link to="/manage/vehicles">{t("menus.header.vehicles")}</Link>
|
label: <Link to="/manage/vehicles">{t("menus.header.vehicles")}</Link>
|
||||||
}
|
}
|
||||||
@@ -423,21 +436,25 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "ccs",
|
key: "ccs",
|
||||||
|
id: "header-css",
|
||||||
icon: <CarFilled />,
|
icon: <CarFilled />,
|
||||||
label: t("menus.header.courtesycars"),
|
label: t("menus.header.courtesycars"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "courtesycarsall",
|
key: "courtesycarsall",
|
||||||
|
id: "header-courtesycars-all",
|
||||||
icon: <CarFilled />,
|
icon: <CarFilled />,
|
||||||
label: <Link to="/manage/courtesycars">{t("menus.header.courtesycars-all")}</Link>
|
label: <Link to="/manage/courtesycars">{t("menus.header.courtesycars-all")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "contracts",
|
key: "contracts",
|
||||||
|
id: "header-contracts",
|
||||||
icon: <FileFilled />,
|
icon: <FileFilled />,
|
||||||
label: <Link to="/manage/courtesycars/contracts">{t("menus.header.courtesycars-contracts")}</Link>
|
label: <Link to="/manage/courtesycars/contracts">{t("menus.header.courtesycars-contracts")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "newcontract",
|
key: "newcontract",
|
||||||
|
id: "header-newcontract",
|
||||||
icon: <FileAddFilled />,
|
icon: <FileAddFilled />,
|
||||||
label: <Link to="/manage/courtesycars/contracts/new">{t("menus.header.courtesycars-newcontract")}</Link>
|
label: <Link to="/manage/courtesycars/contracts/new">{t("menus.header.courtesycars-newcontract")}</Link>
|
||||||
}
|
}
|
||||||
@@ -450,6 +467,7 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "accounting",
|
key: "accounting",
|
||||||
|
id: "header-accounting",
|
||||||
icon: <DollarCircleFilled />,
|
icon: <DollarCircleFilled />,
|
||||||
label: t("menus.header.accounting"),
|
label: t("menus.header.accounting"),
|
||||||
children: accountingChildren
|
children: accountingChildren
|
||||||
@@ -458,6 +476,7 @@ function Header({
|
|||||||
: []),
|
: []),
|
||||||
{
|
{
|
||||||
key: "phonebook",
|
key: "phonebook",
|
||||||
|
id: "header-phonebook",
|
||||||
icon: <PhoneOutlined />,
|
icon: <PhoneOutlined />,
|
||||||
label: <Link to="/manage/phonebook">{t("menus.header.phonebook")}</Link>
|
label: <Link to="/manage/phonebook">{t("menus.header.phonebook")}</Link>
|
||||||
},
|
},
|
||||||
@@ -469,6 +488,7 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "temporarydocs",
|
key: "temporarydocs",
|
||||||
|
id: "header-temporarydocs",
|
||||||
icon: <PaperClipOutlined />,
|
icon: <PaperClipOutlined />,
|
||||||
label: <Link to="/manage/temporarydocs">{t("menus.header.temporarydocs")}</Link>
|
label: <Link to="/manage/temporarydocs">{t("menus.header.temporarydocs")}</Link>
|
||||||
}
|
}
|
||||||
@@ -505,21 +525,25 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "shopsubmenu",
|
key: "shopsubmenu",
|
||||||
|
id: "header-shopsubmenu",
|
||||||
icon: <SettingOutlined />,
|
icon: <SettingOutlined />,
|
||||||
label: t("menus.header.shop"),
|
label: t("menus.header.shop"),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "shop",
|
key: "shop",
|
||||||
|
id: "header-shop",
|
||||||
icon: <Icon component={GiSettingsKnobs} />,
|
icon: <Icon component={GiSettingsKnobs} />,
|
||||||
label: <Link to="/manage/shop?tab=info">{t("menus.header.shop_config")}</Link>
|
label: <Link to="/manage/shop?tab=info">{t("menus.header.shop_config")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "dashboard",
|
key: "dashboard",
|
||||||
|
id: "header-dashboard",
|
||||||
icon: <DashboardFilled />,
|
icon: <DashboardFilled />,
|
||||||
label: <Link to="/manage/dashboard">{t("menus.header.dashboard")}</Link>
|
label: <Link to="/manage/dashboard">{t("menus.header.dashboard")}</Link>
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "reportcenter",
|
key: "reportcenter",
|
||||||
|
id: "header-reportcenter",
|
||||||
icon: <BarChartOutlined />,
|
icon: <BarChartOutlined />,
|
||||||
label: t("menus.header.reportcenter"),
|
label: t("menus.header.reportcenter"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -531,6 +555,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "shop-vendors",
|
key: "shop-vendors",
|
||||||
|
id: "header-shop-vendors",
|
||||||
icon: <Icon component={IoBusinessOutline} />,
|
icon: <Icon component={IoBusinessOutline} />,
|
||||||
label: <Link to="/manage/shop/vendors">{t("menus.header.shop_vendors")}</Link>
|
label: <Link to="/manage/shop/vendors">{t("menus.header.shop_vendors")}</Link>
|
||||||
},
|
},
|
||||||
@@ -542,6 +567,7 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "shop-csi",
|
key: "shop-csi",
|
||||||
|
id: "header-shop-csi",
|
||||||
icon: <Icon component={RiSurveyLine} />,
|
icon: <Icon component={RiSurveyLine} />,
|
||||||
label: <Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
label: <Link to="/manage/shop/csi">{t("menus.header.shop_csi")}</Link>
|
||||||
}
|
}
|
||||||
@@ -555,6 +581,7 @@ function Header({
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
key: "signout",
|
key: "signout",
|
||||||
|
id: "header-signout",
|
||||||
icon: <Icon component={FiLogOut} />,
|
icon: <Icon component={FiLogOut} />,
|
||||||
danger: true,
|
danger: true,
|
||||||
label: t("user.actions.signout"),
|
label: t("user.actions.signout"),
|
||||||
@@ -562,6 +589,7 @@ function Header({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "help",
|
key: "help",
|
||||||
|
id: "header-help",
|
||||||
icon: <Icon component={QuestionCircleFilled} />,
|
icon: <Icon component={QuestionCircleFilled} />,
|
||||||
label: t("menus.header.help"),
|
label: t("menus.header.help"),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
@@ -576,14 +604,22 @@ function Header({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// {
|
...(InstanceRenderManager({
|
||||||
// key: 'rescue',
|
imex: true,
|
||||||
// icon: <Icon component={CarFilled}/>,
|
rome: false,
|
||||||
// label: t("menus.header.rescueme"),
|
promanager: false
|
||||||
// onClick: () => {
|
})
|
||||||
// window.open("https://imexrescue.com/", "_blank");
|
? [
|
||||||
// }
|
{
|
||||||
// },
|
key: "rescue",
|
||||||
|
icon: <Icon component={CarFilled} />,
|
||||||
|
label: t("menus.header.rescueme"),
|
||||||
|
onClick: () => {
|
||||||
|
window.open("https://imexrescue.com/", "_blank");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
|
||||||
...(InstanceRenderManager({
|
...(InstanceRenderManager({
|
||||||
imex: true,
|
imex: true,
|
||||||
@@ -593,6 +629,7 @@ function Header({
|
|||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "shiftclock",
|
key: "shiftclock",
|
||||||
|
id: "header-shiftclock",
|
||||||
icon: <Icon component={GiPlayerTime} />,
|
icon: <Icon component={GiPlayerTime} />,
|
||||||
label: <Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
label: <Link to="/manage/shiftclock">{t("menus.header.shiftclock")}</Link>
|
||||||
}
|
}
|
||||||
@@ -600,6 +637,7 @@ function Header({
|
|||||||
: []),
|
: []),
|
||||||
{
|
{
|
||||||
key: "profile",
|
key: "profile",
|
||||||
|
id: "header-profile",
|
||||||
icon: <UserOutlined />,
|
icon: <UserOutlined />,
|
||||||
label: <Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
|
label: <Link to="/manage/profile">{t("menus.currentuser.profile")}</Link>
|
||||||
}
|
}
|
||||||
@@ -635,6 +673,7 @@ function Header({
|
|||||||
{
|
{
|
||||||
key: "recent",
|
key: "recent",
|
||||||
icon: <ClockCircleFilled />,
|
icon: <ClockCircleFilled />,
|
||||||
|
id: "header-recent",
|
||||||
children: recentItems.map((i, idx) => ({
|
children: recentItems.map((i, idx) => ({
|
||||||
key: idx,
|
key: idx,
|
||||||
label: <Link to={i.url}>{i.label}</Link>
|
label: <Link to={i.url}>{i.label}</Link>
|
||||||
@@ -648,6 +687,7 @@ function Header({
|
|||||||
imex: () => {
|
imex: () => {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "beta-switch",
|
key: "beta-switch",
|
||||||
|
id: "header-beta-switch",
|
||||||
style: { marginLeft: "auto" },
|
style: { marginLeft: "auto" },
|
||||||
label: (
|
label: (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { createStructuredSelector } from "reselect";
|
|||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import { QUERY_JOBLINE_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
|
import { QUERY_JOBLINE_TASKS_PAGINATED } from "../../graphql/tasks.queries.js";
|
||||||
import TaskListContainer from "../task-list/task-list.container.jsx";
|
import TaskListContainer from "../task-list/task-list.container.jsx";
|
||||||
|
import FeatureWrapper from "../feature-wrapper/feature-wrapper.component.jsx";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -98,55 +99,59 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
|
|||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
: {
|
|
||||||
key: "dispatch-lines",
|
|
||||||
children: t("parts_orders.labels.notyetordered")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col md={24} lg={8}>
|
|
||||||
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
|
||||||
<Timeline
|
|
||||||
items={
|
|
||||||
data.billlines.length > 0
|
|
||||||
? data.billlines.map((line) => ({
|
|
||||||
key: line.id,
|
|
||||||
children: (
|
|
||||||
<Row wrap>
|
|
||||||
<Col span={4}>
|
|
||||||
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
|
|
||||||
{line.bill.invoice_number}
|
|
||||||
</Link>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
|
||||||
<span>
|
|
||||||
{`${t("billlines.fields.actual_price")}: `}
|
|
||||||
<CurrencyFormatter>{line.actual_price}</CurrencyFormatter>
|
|
||||||
</span>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
|
||||||
<span>
|
|
||||||
{`${t("billlines.fields.actual_cost")}: `}
|
|
||||||
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
|
||||||
</span>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}>
|
|
||||||
<DateFormatter>{line.bill.date}</DateFormatter>
|
|
||||||
</Col>
|
|
||||||
<Col span={4}> {line.bill.vendor.name}</Col>
|
|
||||||
</Row>
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
key: "no-orders",
|
key: "dispatch-lines",
|
||||||
children: t("bills.labels.nobilllines")
|
children: t("parts_dispatch.labels.notyetdispatched")
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
|
<FeatureWrapper featureName="bills" noauth={() => null}>
|
||||||
|
<Col md={24} lg={8}>
|
||||||
|
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
|
||||||
|
<Timeline
|
||||||
|
items={
|
||||||
|
data.billlines.length > 0
|
||||||
|
? data.billlines.map((line) => ({
|
||||||
|
key: line.id,
|
||||||
|
children: (
|
||||||
|
<Row wrap>
|
||||||
|
<Col span={4}>
|
||||||
|
<Link to={`/manage/jobs/${jobid}?tab=partssublet&billid=${line.bill.id}`}>
|
||||||
|
{line.bill.invoice_number}
|
||||||
|
</Link>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<span>
|
||||||
|
{`${t("billlines.fields.actual_price")}: `}
|
||||||
|
<CurrencyFormatter>{line.actual_price}</CurrencyFormatter>
|
||||||
|
</span>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<span>
|
||||||
|
{`${t("billlines.fields.actual_cost")}: `}
|
||||||
|
<CurrencyFormatter>{line.actual_cost}</CurrencyFormatter>
|
||||||
|
</span>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}>
|
||||||
|
<DateFormatter>{line.bill.date}</DateFormatter>
|
||||||
|
</Col>
|
||||||
|
<Col span={4}> {line.bill.vendor.name}</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
: [
|
||||||
|
{
|
||||||
|
key: "no-orders",
|
||||||
|
children: t("bills.labels.nobilllines")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</FeatureWrapper>
|
||||||
<Col md={24} lg={24}>
|
<Col md={24} lg={24}>
|
||||||
<TaskListContainer
|
<TaskListContainer
|
||||||
parentJobId={jobid}
|
parentJobId={jobid}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import PartsOrderModalContainer from "../parts-order-modal/parts-order-modal.con
|
|||||||
import JobLinesExpander from "./job-lines-expander.component";
|
import JobLinesExpander from "./job-lines-expander.component";
|
||||||
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
|
import JobLinesPartPriceChange from "./job-lines-part-price-change.component";
|
||||||
import { FaTasks } from "react-icons/fa";
|
import { FaTasks } from "react-icons/fa";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
@@ -552,7 +553,7 @@ export function JobLinesComponent({
|
|||||||
>
|
>
|
||||||
{t("joblines.actions.new")}
|
{t("joblines.actions.new")}
|
||||||
</Button>
|
</Button>
|
||||||
{bodyshop.region_config.toLowerCase().startsWith("us") && <JobSendPartPriceChangeComponent job={job} />}
|
{InstanceRenderManager({ rome: <JobSendPartPriceChangeComponent job={job} /> })}
|
||||||
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
|
<JobCreateIOU job={job} selectedJobLines={selectedLines} />
|
||||||
<Input.Search
|
<Input.Search
|
||||||
placeholder={t("general.labels.search")}
|
placeholder={t("general.labels.search")}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { DownCircleFilled } from "@ant-design/icons";
|
import { DownCircleFilled } from "@ant-design/icons";
|
||||||
import { useApolloClient, useMutation } from "@apollo/client";
|
import { useApolloClient, useMutation } from "@apollo/client";
|
||||||
import { Button, Card, Dropdown, Form, Input, Modal, notification, Popconfirm, Popover, Select, Space } from "antd";
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
|
import { Button, Card, Dropdown, Form, Input, Modal, Popconfirm, Popover, Select, Space, notification } from "antd";
|
||||||
|
import axios from "axios";
|
||||||
|
import parsePhoneNumber from "libphonenumber-js";
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -8,27 +11,24 @@ import { Link, useNavigate } from "react-router-dom";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
import { CANCEL_APPOINTMENTS_BY_JOB_ID, INSERT_MANUAL_APPT } from "../../graphql/appointments.queries";
|
import { CANCEL_APPOINTMENTS_BY_JOB_ID, INSERT_MANUAL_APPT } from "../../graphql/appointments.queries";
|
||||||
|
import { GET_CURRENT_QUESTIONSET_ID, INSERT_CSI } from "../../graphql/csi.queries";
|
||||||
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
|
import { DELETE_JOB, UPDATE_JOB, VOID_JOB } from "../../graphql/jobs.queries";
|
||||||
import { insertAuditTrail } from "../../redux/application/application.actions";
|
import { insertAuditTrail } from "../../redux/application/application.actions";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
|
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||||
|
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
import dayjs from "../../utils/day";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
|
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
||||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
import AddToProduction from "./jobs-detail-header-actions.addtoproduction.util";
|
||||||
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
import DuplicateJob from "./jobs-detail-header-actions.duplicate.util";
|
||||||
import axios from "axios";
|
|
||||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
|
||||||
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
|
||||||
import { GET_CURRENT_QUESTIONSET_ID, INSERT_CSI } from "../../graphql/csi.queries";
|
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
|
||||||
import parsePhoneNumber from "libphonenumber-js";
|
|
||||||
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
|
||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
|
||||||
import FormDateTimePickerComponent from "../form-date-time-picker/form-date-time-picker.component";
|
|
||||||
import dayjs from "../../utils/day";
|
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
import JobsDetailHeaderActionsToggleProduction from "./jobs-detail-header-actions.toggle-production";
|
import JobsDetailHeaderActionsToggleProduction from "./jobs-detail-header-actions.toggle-production";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -83,6 +83,13 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
modal: "timeTicketTask"
|
modal: "timeTicketTask"
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
|
setTaskUpsertContext: (context) =>
|
||||||
|
dispatch(
|
||||||
|
setModalContext({
|
||||||
|
context: context,
|
||||||
|
modal: "taskUpsert"
|
||||||
|
})
|
||||||
|
),
|
||||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||||
setMessage: (text) => dispatch(setMessage(text))
|
setMessage: (text) => dispatch(setMessage(text))
|
||||||
@@ -104,7 +111,8 @@ export function JobsDetailHeaderActions({
|
|||||||
setEmailOptions,
|
setEmailOptions,
|
||||||
openChatByPhone,
|
openChatByPhone,
|
||||||
setMessage,
|
setMessage,
|
||||||
setTimeTicketTaskContext
|
setTimeTicketTaskContext,
|
||||||
|
setTaskUpsertContext
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const client = useApolloClient();
|
const client = useApolloClient();
|
||||||
@@ -759,14 +767,14 @@ export function JobsDetailHeaderActions({
|
|||||||
logImEXEvent("job_header_enter_card_payment");
|
logImEXEvent("job_header_enter_card_payment");
|
||||||
|
|
||||||
setCardPaymentContext({
|
setCardPaymentContext({
|
||||||
actions: {},
|
actions: { refetch },
|
||||||
context: { jobid: job.id }
|
context: { jobid: job.id }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasFeatureAccess({ featureName: "courtesycars" })) {
|
if (HasFeatureAccess({ featureName: "courtesycars", bodyshop })) {
|
||||||
menuItems.push({
|
menuItems.push({
|
||||||
key: "cccontract",
|
key: "cccontract",
|
||||||
disabled: jobRO || !job.converted,
|
disabled: jobRO || !job.converted,
|
||||||
@@ -778,6 +786,16 @@ export function JobsDetailHeaderActions({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menuItems.push({
|
||||||
|
key: "createtask",
|
||||||
|
label: t("menus.header.create_task"),
|
||||||
|
onClick: () =>
|
||||||
|
setTaskUpsertContext({
|
||||||
|
actions: {},
|
||||||
|
context: { jobid: job.id }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
menuItems.push(
|
menuItems.push(
|
||||||
job.inproduction
|
job.inproduction
|
||||||
? {
|
? {
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import ChatOpenButton from "../chat-open-button/chat-open-button.component";
|
|||||||
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
|
||||||
import { setJoyRideSteps } from "../../redux/application/application.actions";
|
import { setJoyRideSteps } from "../../redux/application/application.actions";
|
||||||
import { OwnerNameDisplayFunction } from "./../owner-name-display/owner-name-display.component";
|
import { OwnerNameDisplayFunction } from "./../owner-name-display/owner-name-display.component";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
});
|
});
|
||||||
@@ -315,34 +313,6 @@ export function JobsList({ bodyshop, setJoyRideSteps }) {
|
|||||||
title={t("titles.bc.jobs-active")}
|
title={t("titles.bc.jobs-active")}
|
||||||
extra={
|
extra={
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
{InstanceRenderManager({
|
|
||||||
promanager: (
|
|
||||||
<Button
|
|
||||||
onClick={() =>
|
|
||||||
setJoyRideSteps([
|
|
||||||
{
|
|
||||||
target: "#active-jobs-list",
|
|
||||||
content: "This is where you will see all work coming in and currently here."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
target: "#header-jobs",
|
|
||||||
spotlightClicks: true,
|
|
||||||
disableOverlayClose: true,
|
|
||||||
content:
|
|
||||||
"The jobs menu lets you access additional pages to see more information. You can import new jobs, search all jobs, or manage your current production."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
target: "#header-jobs-available",
|
|
||||||
content: "You can find jobs to import here."
|
|
||||||
}
|
|
||||||
])
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Start Walk Through
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
|
|
||||||
<Button onClick={() => refetch()}>
|
<Button onClick={() => refetch()}>
|
||||||
<SyncOutlined />
|
<SyncOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { Link } from "react-router-dom";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort, statusSort } from "../../utils/sorters";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||||
import OwnerDetailUpdateJobsComponent from "../owner-detail-update-jobs/owner-detail-update-jobs.component";
|
import OwnerDetailUpdateJobsComponent from "../owner-detail-update-jobs/owner-detail-update-jobs.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -75,7 +76,18 @@ function OwnerDetailJobsComponent({ bodyshop, owner }) {
|
|||||||
})),
|
})),
|
||||||
onFilter: (value, record) => value.includes(record.status)
|
onFilter: (value, record) => value.includes(record.status)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.actual_completion"),
|
||||||
|
dataIndex: "actual_completion",
|
||||||
|
key: "actual_completion",
|
||||||
|
render: (text, record) => (
|
||||||
|
<DateTimeFormatter>{record.actual_completion}</DateTimeFormatter>
|
||||||
|
),
|
||||||
|
sorter: (a, b) => dateSort(a.actual_completion, b.actual_completion),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "actual_completion" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.clm_total"),
|
title: t("jobs.fields.clm_total"),
|
||||||
dataIndex: "clm_total",
|
dataIndex: "clm_total",
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { DeleteFilled, EyeFilled, SyncOutlined } from "@ant-design/icons";
|
import { DeleteFilled, EyeFilled, SyncOutlined } from "@ant-design/icons";
|
||||||
import { useMutation } from "@apollo/client";
|
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||||
import { Button, Card, Checkbox, Drawer, Grid, Input, Popconfirm, Space, Table } from "antd";
|
import { Button, Card, Checkbox, Drawer, Grid, Input, Popconfirm, Space, Table } from "antd";
|
||||||
import { PageHeader } from "@ant-design/pro-layout";
|
import { PageHeader } from "@ant-design/pro-layout";
|
||||||
|
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
import { QUERY_BILL_BY_PK } from "../../graphql/bills.queries";
|
||||||
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
|
import { DELETE_PARTS_ORDER } from "../../graphql/parts-orders.queries";
|
||||||
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
import { selectJobReadOnly } from "../../redux/application/application.selectors";
|
||||||
import { setModalContext } from "../../redux/modals/modals.actions";
|
import { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateFormatter } from "../../utils/DateFormatter";
|
import { DateFormatter } from "../../utils/DateFormatter";
|
||||||
import { alphaSort } from "../../utils/sorters";
|
|
||||||
import { TemplateList } from "../../utils/TemplateConstants";
|
import { TemplateList } from "../../utils/TemplateConstants";
|
||||||
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import DataLabel from "../data-label/data-label.component";
|
import DataLabel from "../data-label/data-label.component";
|
||||||
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
|
import PartsOrderBackorderEta from "../parts-order-backorder-eta/parts-order-backorder-eta.component";
|
||||||
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
|
import PartsOrderCmReceived from "../parts-order-cm-received/parts-order-cm-received.component";
|
||||||
@@ -81,19 +82,46 @@ export function PartsOrderListTableComponent({
|
|||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {}
|
sortedInfo: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [returnfrombill, setReturnFromBill] = useState();
|
||||||
|
const [billData, setBillData] = useState();
|
||||||
const search = queryString.parse(useLocation().search);
|
const search = queryString.parse(useLocation().search);
|
||||||
const selectedpartsorder = search.partsorderid;
|
const selectedpartsorder = search.partsorderid;
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
|
|
||||||
|
const [billQuery] = useLazyQuery(QUERY_BILL_BY_PK);
|
||||||
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
|
const [deletePartsOrder] = useMutation(DELETE_PARTS_ORDER);
|
||||||
|
|
||||||
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
|
const parts_orders = billsQuery.data ? billsQuery.data.parts_orders : [];
|
||||||
const { refetch } = billsQuery;
|
const { refetch } = billsQuery;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (returnfrombill === null) {
|
||||||
|
setBillData(null);
|
||||||
|
} else {
|
||||||
|
const fetchData = async () => {
|
||||||
|
const result = await billQuery({
|
||||||
|
variables: { billid: returnfrombill },
|
||||||
|
});
|
||||||
|
setBillData(result.data);
|
||||||
|
};
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
}, [returnfrombill, billQuery]);
|
||||||
|
|
||||||
const recordActions = (record, showView = false) => (
|
const recordActions = (record, showView = false) => (
|
||||||
<Space direction="horizontal" wrap>
|
<Space direction="horizontal" wrap>
|
||||||
{showView && (
|
{showView && (
|
||||||
<Button onClick={() => handleOnRowClick(record)}>
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (record.returnfrombill) {
|
||||||
|
setReturnFromBill(record.returnfrombill);
|
||||||
|
} else {
|
||||||
|
setReturnFromBill(null);
|
||||||
|
}
|
||||||
|
handleOnRowClick(record);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<EyeFilled />
|
<EyeFilled />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -395,7 +423,14 @@ export function PartsOrderListTableComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<PageHeader title={record && `${record.vendor.name} - ${record.order_number}`} extra={recordActions(record)} />
|
<PageHeader
|
||||||
|
title={
|
||||||
|
billData
|
||||||
|
? `${record.vendor.name} - ${record.order_number} - ${t("bills.labels.returnfrombill")}: ${billData.bills_by_pk.invoice_number}`
|
||||||
|
: `${record.vendor.name} - ${record.order_number}`
|
||||||
|
}
|
||||||
|
extra={recordActions(record)}
|
||||||
|
/>
|
||||||
<Table
|
<Table
|
||||||
scroll={{
|
scroll={{
|
||||||
x: true //y: "50rem"
|
x: true //y: "50rem"
|
||||||
|
|||||||
@@ -119,8 +119,14 @@ const PaymentExpandedRowComponent = ({ record, bodyshop }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Descriptions title={t("job_payments.titles.descriptions")} contentStyle={{ fontWeight: "600" }} column={4}>
|
<Descriptions
|
||||||
<Descriptions.Item label={t("job_payments.titles.payer")}>{record.payer}</Descriptions.Item>
|
title={t("job_payments.titles.descriptions")}
|
||||||
|
contentStyle={{ fontWeight: "600" }}
|
||||||
|
column={4}
|
||||||
|
>
|
||||||
|
<Descriptions.Item label={t("job_payments.titles.hint")}>
|
||||||
|
{payment_response?.response?.methodhint}
|
||||||
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.payername")}>
|
<Descriptions.Item label={t("job_payments.titles.payername")}>
|
||||||
{payment_response?.response?.nameOnCard ?? ""}
|
{payment_response?.response?.nameOnCard ?? ""}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
@@ -132,7 +138,7 @@ const PaymentExpandedRowComponent = ({ record, bodyshop }) => {
|
|||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.transactionid")}>{record.transactionid}</Descriptions.Item>
|
<Descriptions.Item label={t("job_payments.titles.transactionid")}>{record.transactionid}</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.paymentid")}>
|
<Descriptions.Item label={t("job_payments.titles.paymentid")}>
|
||||||
{payment_response?.response?.paymentreferenceid ?? ""}
|
{payment_response?.ext_paymentid ?? ""}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.paymenttype")}>{record.type}</Descriptions.Item>
|
<Descriptions.Item label={t("job_payments.titles.paymenttype")}>{record.type}</Descriptions.Item>
|
||||||
<Descriptions.Item label={t("job_payments.titles.paymentnum")}>{record.paymentnum}</Descriptions.Item>
|
<Descriptions.Item label={t("job_payments.titles.paymentnum")}>{record.paymentnum}</Descriptions.Item>
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { useSplitTreatments } from '@splitsoftware/splitio-react';
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Button, Card, Tabs } from 'antd';
|
import { Button, Card, Tabs } from "antd";
|
||||||
import React from 'react';
|
import React from "react";
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from 'react-redux';
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from 'reselect';
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from '../../redux/user/user.selectors';
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import ShopInfoGeneral from './shop-info.general.component';
|
import ShopInfoGeneral from "./shop-info.general.component";
|
||||||
import ShopInfoIntakeChecklistComponent from './shop-info.intake.component';
|
import ShopInfoIntakeChecklistComponent from "./shop-info.intake.component";
|
||||||
import ShopInfoLaborRates from './shop-info.laborrates.component';
|
import ShopInfoLaborRates from "./shop-info.laborrates.component";
|
||||||
import ShopInfoOrderStatusComponent from './shop-info.orderstatus.component';
|
import ShopInfoOrderStatusComponent from "./shop-info.orderstatus.component";
|
||||||
import ShopInfoPartsScan from './shop-info.parts-scan';
|
import ShopInfoPartsScan from "./shop-info.parts-scan";
|
||||||
import ShopInfoRbacComponent from './shop-info.rbac.component';
|
import ShopInfoRbacComponent from "./shop-info.rbac.component";
|
||||||
import ShopInfoResponsibilityCenterComponent from './shop-info.responsibilitycenters.component';
|
import ShopInfoResponsibilityCenterComponent from "./shop-info.responsibilitycenters.component";
|
||||||
import ShopInfoROStatusComponent from './shop-info.rostatus.component';
|
import ShopInfoROStatusComponent from "./shop-info.rostatus.component";
|
||||||
import ShopInfoSchedulingComponent from './shop-info.scheduling.component';
|
import ShopInfoSchedulingComponent from "./shop-info.scheduling.component";
|
||||||
import ShopInfoSpeedPrint from './shop-info.speedprint.component';
|
import ShopInfoSpeedPrint from "./shop-info.speedprint.component";
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import ShopInfoTaskPresets from './shop-info.task-presets.component';
|
import ShopInfoTaskPresets from "./shop-info.task-presets.component";
|
||||||
import queryString from 'query-string';
|
import queryString from "query-string";
|
||||||
import InstanceRenderManager from '../../utils/instanceRenderMgr';
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import ShopInfoRoGuard from './shop-info.roguard.component';
|
import ShopInfoRoGuard from "./shop-info.roguard.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -47,44 +47,52 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
{
|
{
|
||||||
key: "general",
|
key: "general",
|
||||||
label: t("bodyshop.labels.shopinfo"),
|
label: t("bodyshop.labels.shopinfo"),
|
||||||
children: <ShopInfoGeneral form={form} />
|
children: <ShopInfoGeneral form={form} />,
|
||||||
|
id: "tab-shop-general"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "speedprint",
|
key: "speedprint",
|
||||||
label: t("bodyshop.labels.speedprint"),
|
label: t("bodyshop.labels.speedprint"),
|
||||||
children: <ShopInfoSpeedPrint form={form} />
|
children: <ShopInfoSpeedPrint form={form} />,
|
||||||
|
id: "tab-shop-speedprint"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "rbac",
|
key: "rbac",
|
||||||
label: t("bodyshop.labels.rbac"),
|
label: t("bodyshop.labels.rbac"),
|
||||||
children: <ShopInfoRbacComponent form={form} />
|
children: <ShopInfoRbacComponent form={form} />,
|
||||||
|
id: "tab-shop-rbac"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "roStatus",
|
key: "roStatus",
|
||||||
label: t("bodyshop.labels.jobstatuses"),
|
label: t("bodyshop.labels.jobstatuses"),
|
||||||
children: <ShopInfoROStatusComponent form={form} />
|
children: <ShopInfoROStatusComponent form={form} />,
|
||||||
|
id: "tab-shop-rostatus"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "scheduling",
|
key: "scheduling",
|
||||||
label: t("bodyshop.labels.scheduling"),
|
label: t("bodyshop.labels.scheduling"),
|
||||||
children: <ShopInfoSchedulingComponent form={form} />
|
children: <ShopInfoSchedulingComponent form={form} />,
|
||||||
|
id: "tab-shop-scheduling"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "orderStatus",
|
key: "orderStatus",
|
||||||
label: t("bodyshop.labels.orderstatuses"),
|
label: t("bodyshop.labels.orderstatuses"),
|
||||||
children: <ShopInfoOrderStatusComponent form={form} />
|
children: <ShopInfoOrderStatusComponent form={form} />,
|
||||||
|
id: "tab-shop-orderstatus"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "responsibilityCenters",
|
key: "responsibilityCenters",
|
||||||
label: t("bodyshop.labels.responsibilitycenters.title"),
|
label: t("bodyshop.labels.responsibilitycenters.title"),
|
||||||
children: <ShopInfoResponsibilityCenterComponent form={form} />
|
children: <ShopInfoResponsibilityCenterComponent form={form} />,
|
||||||
|
id: "tab-shop-responsibilitycenters"
|
||||||
},
|
},
|
||||||
...InstanceRenderManager({
|
...InstanceRenderManager({
|
||||||
imex: [
|
imex: [
|
||||||
{
|
{
|
||||||
key: "checklists",
|
key: "checklists",
|
||||||
label: t("bodyshop.labels.checklists"),
|
label: t("bodyshop.labels.checklists"),
|
||||||
children: <ShopInfoIntakeChecklistComponent form={form} />
|
children: <ShopInfoIntakeChecklistComponent form={form} />,
|
||||||
|
id: "tab-shop-checklists"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
rome: "USE_IMEX",
|
rome: "USE_IMEX",
|
||||||
@@ -93,14 +101,16 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
{
|
{
|
||||||
key: "laborrates",
|
key: "laborrates",
|
||||||
label: t("bodyshop.labels.laborrates"),
|
label: t("bodyshop.labels.laborrates"),
|
||||||
children: <ShopInfoLaborRates form={form} />
|
children: <ShopInfoLaborRates form={form} />,
|
||||||
|
id: "tab-shop-laborrates"
|
||||||
},
|
},
|
||||||
...(CriticalPartsScanning.treatment === "on"
|
...(CriticalPartsScanning.treatment === "on"
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: "partsscan",
|
key: "partsscan",
|
||||||
label: t("bodyshop.labels.partsscan"),
|
label: t("bodyshop.labels.partsscan"),
|
||||||
children: <ShopInfoPartsScan form={form} />
|
children: <ShopInfoPartsScan form={form} />,
|
||||||
|
id: "tab-shop-partsscan"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
@@ -109,21 +119,23 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
{
|
{
|
||||||
key: "task-presets",
|
key: "task-presets",
|
||||||
label: t("bodyshop.labels.task-presets"),
|
label: t("bodyshop.labels.task-presets"),
|
||||||
children: <ShopInfoTaskPresets form={form} />
|
children: <ShopInfoTaskPresets form={form} />,
|
||||||
|
id: "tab-shop-task-presets"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
...InstanceRenderManager({
|
...InstanceRenderManager({
|
||||||
imex: [
|
imex: [
|
||||||
{
|
{
|
||||||
key: 'roguard',
|
key: "roguard",
|
||||||
label: t('bodyshop.labels.roguard.title'),
|
label: t("bodyshop.labels.roguard.title"),
|
||||||
children: <ShopInfoRoGuard form={form} />,
|
children: <ShopInfoRoGuard form={form} />,
|
||||||
},
|
id: "tab-shop-roguard"
|
||||||
],
|
}
|
||||||
rome: 'USE_IMEX',
|
],
|
||||||
promanager: [],
|
rome: "USE_IMEX",
|
||||||
}),
|
promanager: []
|
||||||
|
})
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import PhoneFormItem, { PhoneItemFormatterValidation } from "../form-items-forma
|
|||||||
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
import FeatureWrapper, { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
// TODO: Client Update, this might break
|
// TODO: Client Update, this might break
|
||||||
const timeZonesList = Intl.supportedValuesOf("timeZone");
|
const timeZonesList = Intl.supportedValuesOf("timeZone");
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
@@ -144,285 +145,289 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
<InputNumber min={0} />
|
<InputNumber min={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup">
|
<FeatureWrapper featureName="export" noauth={() => null}>
|
||||||
<Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}>
|
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup">
|
||||||
<Switch />
|
<Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}>
|
||||||
</Form.Item>
|
<Switch />
|
||||||
{InstanceRenderManager({
|
</Form.Item>
|
||||||
imex: (
|
{InstanceRenderManager({
|
||||||
<Form.Item shouldUpdate noStyle>
|
imex: (
|
||||||
{() => (
|
<Form.Item shouldUpdate noStyle>
|
||||||
|
{() => (
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.qbo_usa")}
|
||||||
|
shouldUpdate
|
||||||
|
valuePropName="checked"
|
||||||
|
name={["accountingconfig", "qbo_usa"]}
|
||||||
|
>
|
||||||
|
<Switch disabled={!form.getFieldValue(["accountingconfig", "qbo"])} />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<Form.Item label={t("bodyshop.labels.qbo_departmentid")} name={["accountingconfig", "qbo_departmentid"]}>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.labels.accountingtiers")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["accountingconfig", "tiers"]}
|
||||||
|
>
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value={2}>2</Radio>
|
||||||
|
<Radio value={3}>3</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item shouldUpdate>
|
||||||
|
{() => {
|
||||||
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.labels.qbo_usa")}
|
label={t("bodyshop.labels.2tiersetup")}
|
||||||
shouldUpdate
|
shouldUpdate
|
||||||
valuePropName="checked"
|
rules={[
|
||||||
name={["accountingconfig", "qbo_usa"]}
|
{
|
||||||
|
required: form.getFieldValue(["accountingconfig", "tiers"]) === 2
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["accountingconfig", "twotierpref"]}
|
||||||
>
|
>
|
||||||
<Switch disabled={!form.getFieldValue(["accountingconfig", "qbo"])} />
|
<Radio.Group disabled={form.getFieldValue(["accountingconfig", "tiers"]) === 3}>
|
||||||
|
<Radio value="name">{t("bodyshop.labels.2tiername")}</Radio>
|
||||||
|
<Radio value="source">{t("bodyshop.labels.2tiersource")}</Radio>
|
||||||
|
</Radio.Group>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
)}
|
);
|
||||||
</Form.Item>
|
}}
|
||||||
)
|
</Form.Item>
|
||||||
})}
|
<Form.Item
|
||||||
<Form.Item label={t("bodyshop.labels.qbo_departmentid")} name={["accountingconfig", "qbo_departmentid"]}>
|
label={t("bodyshop.labels.printlater")}
|
||||||
<Input />
|
valuePropName="checked"
|
||||||
</Form.Item>
|
name={["accountingconfig", "printlater"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.labels.accountingtiers")}
|
<Switch />
|
||||||
rules={[
|
</Form.Item>
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.labels.emaillater")}
|
||||||
//message: t("general.validation.required"),
|
valuePropName="checked"
|
||||||
}
|
name={["accountingconfig", "emaillater"]}
|
||||||
]}
|
>
|
||||||
name={["accountingconfig", "tiers"]}
|
<Switch />
|
||||||
>
|
</Form.Item>
|
||||||
<Radio.Group>
|
<Form.Item
|
||||||
<Radio value={2}>2</Radio>
|
label={t("bodyshop.fields.inhousevendorid")}
|
||||||
<Radio value={3}>3</Radio>
|
name={"inhousevendorid"}
|
||||||
</Radio.Group>
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
<Form.Item shouldUpdate>
|
required: true
|
||||||
{() => {
|
//message: t("general.validation.required"),
|
||||||
return (
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.default_adjustment_rate")}
|
||||||
|
name={"default_adjustment_rate"}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={2} />
|
||||||
|
</Form.Item>
|
||||||
|
{InstanceRenderManager({
|
||||||
|
imex: (
|
||||||
|
<Form.Item label={t("bodyshop.fields.federal_tax_id")} name="federal_tax_id">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<Form.Item label={t("bodyshop.fields.state_tax_id")} name="state_tax_id">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
{InstanceRenderManager({
|
||||||
|
imex: (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.labels.2tiersetup")}
|
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
||||||
shouldUpdate
|
name={["bill_tax_rates", "federal_tax_rate"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: form.getFieldValue(["accountingconfig", "tiers"]) === 2
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
name={["accountingconfig", "twotierpref"]}
|
|
||||||
>
|
>
|
||||||
<Radio.Group disabled={form.getFieldValue(["accountingconfig", "tiers"]) === 3}>
|
<InputNumber />
|
||||||
<Radio value="name">{t("bodyshop.labels.2tiername")}</Radio>
|
|
||||||
<Radio value="source">{t("bodyshop.labels.2tiersource")}</Radio>
|
|
||||||
</Radio.Group>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
);
|
)
|
||||||
}}
|
})}
|
||||||
</Form.Item>
|
<Form.Item
|
||||||
<Form.Item
|
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
||||||
label={t("bodyshop.labels.printlater")}
|
name={["bill_tax_rates", "state_tax_rate"]}
|
||||||
valuePropName="checked"
|
rules={[
|
||||||
name={["accountingconfig", "printlater"]}
|
{
|
||||||
>
|
required: true
|
||||||
<Switch />
|
//message: t("general.validation.required"),
|
||||||
</Form.Item>
|
}
|
||||||
<Form.Item
|
]}
|
||||||
label={t("bodyshop.labels.emaillater")}
|
>
|
||||||
valuePropName="checked"
|
<InputNumber />
|
||||||
name={["accountingconfig", "emaillater"]}
|
</Form.Item>
|
||||||
>
|
<Form.Item
|
||||||
<Switch />
|
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
||||||
</Form.Item>
|
name={["bill_tax_rates", "local_tax_rate"]}
|
||||||
<Form.Item
|
rules={[
|
||||||
label={t("bodyshop.fields.inhousevendorid")}
|
{
|
||||||
name={"inhousevendorid"}
|
required: true
|
||||||
rules={[
|
//message: t("general.validation.required"),
|
||||||
{
|
}
|
||||||
required: true
|
]}
|
||||||
//message: t("general.validation.required"),
|
>
|
||||||
}
|
<InputNumber />
|
||||||
]}
|
</Form.Item>
|
||||||
>
|
<Form.Item
|
||||||
<Input />
|
name={["md_payment_types"]}
|
||||||
</Form.Item>
|
label={t("bodyshop.fields.md_payment_types")}
|
||||||
<Form.Item
|
rules={[
|
||||||
label={t("bodyshop.fields.default_adjustment_rate")}
|
{
|
||||||
name={"default_adjustment_rate"}
|
required: true,
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber min={0} precision={2} />
|
|
||||||
</Form.Item>
|
|
||||||
{InstanceRenderManager({
|
|
||||||
imex: (
|
|
||||||
<Form.Item label={t("bodyshop.fields.federal_tax_id")} name="federal_tax_id">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<Form.Item label={t("bodyshop.fields.state_tax_id")} name="state_tax_id">
|
|
||||||
<Input />
|
|
||||||
</Form.Item>
|
|
||||||
{InstanceRenderManager({
|
|
||||||
imex: (
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.invoice_federal_tax_rate")}
|
|
||||||
name={["bill_tax_rates", "federal_tax_rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.invoice_state_tax_rate")}
|
|
||||||
name={["bill_tax_rates", "state_tax_rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.invoice_local_tax_rate")}
|
|
||||||
name={["bill_tax_rates", "local_tax_rate"]}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_payment_types"]}
|
|
||||||
label={t("bodyshop.fields.md_payment_types")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array"
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="tags" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_categories"]}
|
|
||||||
label={t("bodyshop.fields.md_categories")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
type: "array"
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Select mode="tags" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name={["enforce_class"]} label={t("bodyshop.fields.enforce_class")} valuePropName="checked">
|
|
||||||
<Switch />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ReceivableCustomField1"]}
|
|
||||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 1 })}
|
|
||||||
>
|
|
||||||
{ReceivableCustomFieldSelect}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ReceivableCustomField2"]}
|
|
||||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 2 })}
|
|
||||||
>
|
|
||||||
{ReceivableCustomFieldSelect}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["accountingconfig", "ReceivableCustomField3"]}
|
|
||||||
label={t("bodyshop.fields.ReceivableCustomField", { number: 3 })}
|
|
||||||
>
|
|
||||||
{ReceivableCustomFieldSelect}
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name={["md_classes"]}
|
|
||||||
label={t("bodyshop.fields.md_classes")}
|
|
||||||
rules={[
|
|
||||||
({ getFieldValue }) => {
|
|
||||||
return {
|
|
||||||
required: getFieldValue("enforce_class"),
|
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
type: "array"
|
type: "array"
|
||||||
};
|
}
|
||||||
}
|
]}
|
||||||
]}
|
>
|
||||||
>
|
<Select mode="tags" />
|
||||||
<Select mode="tags" />
|
</Form.Item>
|
||||||
</Form.Item>
|
<Form.Item
|
||||||
{ClosingPeriod.treatment === "on" && (
|
name={["md_categories"]}
|
||||||
<>
|
label={t("bodyshop.fields.md_categories")}
|
||||||
<Form.Item
|
rules={[
|
||||||
name={["accountingconfig", "ClosingPeriod"]}
|
{
|
||||||
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
//message: t("general.validation.required"),
|
||||||
>
|
type: "array"
|
||||||
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
}
|
||||||
</Form.Item>
|
]}
|
||||||
</>
|
>
|
||||||
)}
|
<Select mode="tags" />
|
||||||
</LayoutFormRow>
|
</Form.Item>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup">
|
<Form.Item name={["enforce_class"]} label={t("bodyshop.fields.enforce_class")} valuePropName="checked">
|
||||||
<Form.Item
|
<Switch />
|
||||||
label={t("bodyshop.fields.dailypainttarget")}
|
</Form.Item>
|
||||||
name={["scoreboard_target", "dailyPaintTarget"]}
|
<Form.Item
|
||||||
rules={[
|
name={["accountingconfig", "ReceivableCustomField1"]}
|
||||||
{
|
label={t("bodyshop.fields.ReceivableCustomField", { number: 1 })}
|
||||||
required: true
|
>
|
||||||
//message: t("general.validation.required"),
|
{ReceivableCustomFieldSelect}
|
||||||
}
|
</Form.Item>
|
||||||
]}
|
<Form.Item
|
||||||
>
|
name={["accountingconfig", "ReceivableCustomField2"]}
|
||||||
<InputNumber min={0} precision={0} />
|
label={t("bodyshop.fields.ReceivableCustomField", { number: 2 })}
|
||||||
</Form.Item>
|
>
|
||||||
|
{ReceivableCustomFieldSelect}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["accountingconfig", "ReceivableCustomField3"]}
|
||||||
|
label={t("bodyshop.fields.ReceivableCustomField", { number: 3 })}
|
||||||
|
>
|
||||||
|
{ReceivableCustomFieldSelect}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name={["md_classes"]}
|
||||||
|
label={t("bodyshop.fields.md_classes")}
|
||||||
|
rules={[
|
||||||
|
({ getFieldValue }) => {
|
||||||
|
return {
|
||||||
|
required: getFieldValue("enforce_class"),
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
type: "array"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select mode="tags" />
|
||||||
|
</Form.Item>
|
||||||
|
{ClosingPeriod.treatment === "on" && (
|
||||||
|
<>
|
||||||
|
<Form.Item
|
||||||
|
name={["accountingconfig", "ClosingPeriod"]}
|
||||||
|
label={t("bodyshop.fields.closingperiod")} //{t("reportcenter.labels.dates")}
|
||||||
|
>
|
||||||
|
<DatePicker.RangePicker format="MM/DD/YYYY" presets={DatePickerRanges} />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</LayoutFormRow>
|
||||||
|
</FeatureWrapper>
|
||||||
|
<FeatureWrapper featureName="scoreboard" noauth={() => null}>
|
||||||
|
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup">
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.dailypainttarget")}
|
||||||
|
name={["scoreboard_target", "dailyPaintTarget"]}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} precision={0} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.dailybodytarget")}
|
label={t("bodyshop.fields.dailybodytarget")}
|
||||||
name={["scoreboard_target", "dailyBodyTarget"]}
|
name={["scoreboard_target", "dailyBodyTarget"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} precision={0} />
|
<InputNumber min={0} precision={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.lastnumberworkingdays")}
|
label={t("bodyshop.fields.lastnumberworkingdays")}
|
||||||
name={["scoreboard_target", "lastNumberWorkingDays"]}
|
name={["scoreboard_target", "lastNumberWorkingDays"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={0} max={12} precision={0} />
|
<InputNumber min={0} max={12} precision={0} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.ignoreblockeddays")}
|
label={t("bodyshop.fields.ignoreblockeddays")}
|
||||||
name={["scoreboard_target", "ignoreblockeddays"]}
|
name={["scoreboard_target", "ignoreblockeddays"]}
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.prodtargethrs")}
|
label={t("bodyshop.fields.prodtargethrs")}
|
||||||
name={["prodtargethrs"]}
|
name={["prodtargethrs"]}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true
|
required: true
|
||||||
//message: t("general.validation.required"),
|
//message: t("general.validation.required"),
|
||||||
}
|
}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<InputNumber min={1} precision={1} />
|
<InputNumber min={1} precision={1} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
</FeatureWrapper>
|
||||||
<LayoutFormRow header={t("bodyshop.labels.systemsettings")} id="systemsettings">
|
<LayoutFormRow header={t("bodyshop.labels.systemsettings")} id="systemsettings">
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={["md_referral_sources"]}
|
name={["md_referral_sources"]}
|
||||||
@@ -567,27 +572,32 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<Select mode="tags" />
|
<Select mode="tags" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
name={["tt_allow_post_to_invoiced"]}
|
{HasFeatureAccess({ featureName: "timetickets", bodyshop }) && (
|
||||||
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
|
<>
|
||||||
valuePropName="checked"
|
<Form.Item
|
||||||
>
|
name={["tt_allow_post_to_invoiced"]}
|
||||||
<Switch />
|
label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
|
||||||
</Form.Item>
|
valuePropName="checked"
|
||||||
<Form.Item
|
>
|
||||||
name={["tt_enforce_hours_for_tech_console"]}
|
<Switch />
|
||||||
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
|
</Form.Item>
|
||||||
valuePropName="checked"
|
<Form.Item
|
||||||
>
|
name={["tt_enforce_hours_for_tech_console"]}
|
||||||
<Switch />
|
label={t("bodyshop.fields.tt_enforce_hours_for_tech_console")}
|
||||||
</Form.Item>
|
valuePropName="checked"
|
||||||
<Form.Item
|
>
|
||||||
name={["bill_allow_post_to_closed"]}
|
<Switch />
|
||||||
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
</Form.Item>
|
||||||
valuePropName="checked"
|
<Form.Item
|
||||||
>
|
name={["bill_allow_post_to_closed"]}
|
||||||
<Switch />
|
label={t("bodyshop.fields.bill_allow_post_to_closed")}
|
||||||
</Form.Item>
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={["md_ded_notes"]}
|
name={["md_ded_notes"]}
|
||||||
label={t("bodyshop.fields.md_ded_notes")}
|
label={t("bodyshop.fields.md_ded_notes")}
|
||||||
@@ -1042,111 +1052,118 @@ export function ShopInfoGeneral({ form, bodyshop }) {
|
|||||||
}}
|
}}
|
||||||
</Form.List>
|
</Form.List>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")} id="md_ccc_rates">
|
<FeatureWrapper featureName="courtesycars" noauth={() => null}>
|
||||||
<Form.List name={["md_ccc_rates"]}>
|
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")} id="md_ccc_rates">
|
||||||
{(fields, { add, remove, move }) => {
|
<Form.List name={["md_ccc_rates"]}>
|
||||||
return (
|
{(fields, { add, remove, move }) => {
|
||||||
<div>
|
return (
|
||||||
{fields.map((field, index) => (
|
<div>
|
||||||
<Form.Item key={field.key}>
|
{fields.map((field, index) => (
|
||||||
<LayoutFormRow noDivider>
|
<Form.Item key={field.key}>
|
||||||
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
|
<LayoutFormRow noDivider>
|
||||||
<Input />
|
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
|
||||||
</Form.Item>
|
<Input />
|
||||||
<Form.Item label={t("contracts.fields.actax")} key={`${index}actax`} name={[field.name, "actax"]}>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.actax")}
|
||||||
<Form.Item
|
key={`${index}actax`}
|
||||||
label={t("contracts.fields.dailyfreekm")}
|
name={[field.name, "actax"]}
|
||||||
key={`${index}dailyfreekm`}
|
>
|
||||||
name={[field.name, "dailyfreekm"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.dailyfreekm")}
|
||||||
<Form.Item
|
key={`${index}dailyfreekm`}
|
||||||
label={t("contracts.fields.refuelcharge")}
|
name={[field.name, "dailyfreekm"]}
|
||||||
key={`${index}refuelcharge`}
|
>
|
||||||
name={[field.name, "refuelcharge"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.refuelcharge")}
|
||||||
<Form.Item
|
key={`${index}refuelcharge`}
|
||||||
label={t("contracts.fields.excesskmrate")}
|
name={[field.name, "refuelcharge"]}
|
||||||
key={`${index}excesskmrate`}
|
>
|
||||||
name={[field.name, "excesskmrate"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.excesskmrate")}
|
||||||
<Form.Item
|
key={`${index}excesskmrate`}
|
||||||
label={t("contracts.fields.cleanupcharge")}
|
name={[field.name, "excesskmrate"]}
|
||||||
key={`${index}cleanupcharge`}
|
>
|
||||||
name={[field.name, "cleanupcharge"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.cleanupcharge")}
|
||||||
<Form.Item
|
key={`${index}cleanupcharge`}
|
||||||
label={t("contracts.fields.damagewaiver")}
|
name={[field.name, "cleanupcharge"]}
|
||||||
key={`${index}damagewaiver`}
|
>
|
||||||
name={[field.name, "damagewaiver"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.damagewaiver")}
|
||||||
<Form.Item
|
key={`${index}damagewaiver`}
|
||||||
label={t("contracts.fields.federaltax")}
|
name={[field.name, "damagewaiver"]}
|
||||||
key={`${index}federaltax`}
|
>
|
||||||
name={[field.name, "federaltax"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.federaltax")}
|
||||||
<Form.Item
|
key={`${index}federaltax`}
|
||||||
label={t("contracts.fields.statetax")}
|
name={[field.name, "federaltax"]}
|
||||||
key={`${index}statetax`}
|
>
|
||||||
name={[field.name, "statetax"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.statetax")}
|
||||||
<Form.Item
|
key={`${index}statetax`}
|
||||||
label={t("contracts.fields.localtax")}
|
name={[field.name, "statetax"]}
|
||||||
key={`${index}localtax`}
|
>
|
||||||
name={[field.name, "localtax"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.localtax")}
|
||||||
<Form.Item
|
key={`${index}localtax`}
|
||||||
label={t("contracts.fields.coverage")}
|
name={[field.name, "localtax"]}
|
||||||
key={`${index}coverage`}
|
>
|
||||||
name={[field.name, "coverage"]}
|
<InputNumber precision={2} />
|
||||||
>
|
</Form.Item>
|
||||||
<InputNumber precision={2} />
|
<Form.Item
|
||||||
</Form.Item>
|
label={t("contracts.fields.coverage")}
|
||||||
|
key={`${index}coverage`}
|
||||||
|
name={[field.name, "coverage"]}
|
||||||
|
>
|
||||||
|
<InputNumber precision={2} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<DeleteFilled
|
<DeleteFilled
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
remove(field.name);
|
remove(field.name);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FormListMoveArrows move={move} index={index} total={fields.length} />
|
<FormListMoveArrows move={move} index={index} total={fields.length} />
|
||||||
</Space>
|
</Space>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
</div>
|
||||||
<Form.Item>
|
);
|
||||||
<Button
|
}}
|
||||||
type="dashed"
|
</Form.List>
|
||||||
onClick={() => {
|
</LayoutFormRow>
|
||||||
add();
|
</FeatureWrapper>
|
||||||
}}
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
>
|
|
||||||
{t("general.actions.add")}
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Form.List>
|
|
||||||
</LayoutFormRow>
|
|
||||||
<LayoutFormRow grow header={t("bodyshop.fields.md_jobline_presets")} id="md_jobline_presets">
|
<LayoutFormRow grow header={t("bodyshop.fields.md_jobline_presets")} id="md_jobline_presets">
|
||||||
<Form.List name={["md_jobline_presets"]}>
|
<Form.List name={["md_jobline_presets"]}>
|
||||||
{(fields, { add, remove, move }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
||||||
import { Form, InputNumber } from "antd";
|
import { Form, InputNumber } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
import RbacWrapper from "../rbac-wrapper/rbac-wrapper.component";
|
||||||
import { useSplitTreatments } from "@splitsoftware/splitio-react";
|
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -29,210 +30,219 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
return (
|
return (
|
||||||
<RbacWrapper action="shop:rbac">
|
<RbacWrapper action="shop:rbac">
|
||||||
<LayoutFormRow>
|
<LayoutFormRow>
|
||||||
<Form.Item
|
{...HasFeatureAccess({ featureName: "export", bodyshop }) ? [
|
||||||
label={t("bodyshop.fields.rbac.accounting.exportlog")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.accounting.exportlog")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "accounting:exportlog"]}
|
]}
|
||||||
>
|
name={["md_rbac", "accounting:exportlog"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.accounting.payables")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.accounting.payables")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "accounting:payables"]}
|
]}
|
||||||
>
|
name={["md_rbac", "accounting:payables"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.accounting.payments")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.accounting.payments")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "accounting:payments"]}
|
]}
|
||||||
>
|
name={["md_rbac", "accounting:payments"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>,
|
||||||
label={t("bodyshop.fields.rbac.accounting.receivables")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.accounting.receivables")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "accounting:receivables"]}
|
]}
|
||||||
>
|
name={["md_rbac", "accounting:receivables"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>
|
||||||
label={t("bodyshop.fields.rbac.bills.delete")}
|
]:[]}
|
||||||
rules={[
|
{...HasFeatureAccess({ featureName: "bills", bodyshop }) ? [
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.delete")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:delete"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:delete"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.bills.enter")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.enter")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:enter"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:enter"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.bills.list")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.list")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:list"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:list"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.bills.reexport")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.reexport")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:reexport"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:reexport"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.bills.view")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.bills.view")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "bills:view"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "bills:view"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.contracts.create")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>
|
||||||
{
|
]:[]}
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
{...HasFeatureAccess({ featureName: "courtesycars", bodyshop }) ? [
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.contracts.create")}
|
||||||
name={["md_rbac", "contracts:create"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.contracts.detail")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "contracts:create"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.contracts.detail")}
|
||||||
name={["md_rbac", "contracts:detail"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.contracts.list")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "contracts:detail"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.contracts.list")}
|
||||||
name={["md_rbac", "contracts:list"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.courtesycar.create")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "contracts:list"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.courtesycar.create")}
|
||||||
name={["md_rbac", "courtesycar:create"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.courtesycar.detail")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "courtesycar:create"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.courtesycar.detail")}
|
||||||
name={["md_rbac", "courtesycar:detail"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.courtesycar.list")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "courtesycar:detail"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>,
|
||||||
}
|
<Form.Item
|
||||||
]}
|
label={t("bodyshop.fields.rbac.courtesycar.list")}
|
||||||
name={["md_rbac", "courtesycar:list"]}
|
rules={[
|
||||||
>
|
{
|
||||||
<InputNumber />
|
required: true
|
||||||
</Form.Item>
|
//message: t("general.validation.required"),
|
||||||
<Form.Item
|
}
|
||||||
label={t("bodyshop.fields.rbac.csi.export")}
|
]}
|
||||||
rules={[
|
name={["md_rbac", "courtesycar:list"]}
|
||||||
{
|
>
|
||||||
required: true
|
<InputNumber />
|
||||||
//message: t("general.validation.required"),
|
</Form.Item>
|
||||||
}
|
]:[]}
|
||||||
]}
|
{...HasFeatureAccess({ featureName: "csi", bodyshop }) ? [
|
||||||
name={["md_rbac", "csi:export"]}
|
<Form.Item
|
||||||
>
|
label={t("bodyshop.fields.rbac.csi.export")}
|
||||||
<InputNumber />
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
<Form.Item
|
required: true
|
||||||
label={t("bodyshop.fields.rbac.csi.page")}
|
//message: t("general.validation.required"),
|
||||||
rules={[
|
}
|
||||||
{
|
]}
|
||||||
required: true
|
name={["md_rbac", "csi:export"]}
|
||||||
//message: t("general.validation.required"),
|
>
|
||||||
}
|
<InputNumber />
|
||||||
]}
|
</Form.Item>,
|
||||||
name={["md_rbac", "csi:page"]}
|
<Form.Item
|
||||||
>
|
label={t("bodyshop.fields.rbac.csi.page")}
|
||||||
<InputNumber />
|
rules={[
|
||||||
</Form.Item>
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "csi:page"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
]:[]}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.employees.page")}
|
label={t("bodyshop.fields.rbac.employees.page")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -425,18 +435,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.bills.list")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "bills:list"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.employees.page")}
|
label={t("bodyshop.fields.rbac.employees.page")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -510,18 +508,21 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{HasFeatureAccess({ featureName: "visualboard", bodyshop }) && (
|
||||||
label={t("bodyshop.fields.rbac.production.board")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.production.board")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "production:board"]}
|
]}
|
||||||
>
|
name={["md_rbac", "production:board"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.production.list")}
|
label={t("bodyshop.fields.rbac.production.list")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -546,102 +547,142 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{HasFeatureAccess({ featureName: "scoreboard", bodyshop }) && (
|
||||||
label={t("bodyshop.fields.rbac.scoreboard.view")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.scoreboard.view")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "scoreboard:view"]}
|
]}
|
||||||
>
|
name={["md_rbac", "scoreboard:view"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>
|
||||||
label={t("bodyshop.fields.rbac.shiftclock.view")}
|
)}
|
||||||
rules={[
|
{...HasFeatureAccess({ featureName: "timetickets", bodyshop }) ? [
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.shiftclock.view")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "shiftclock:view"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "shiftclock:view"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.shop.config")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.shop.config")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "shop:config"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "shop:config"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "timetickets:edit"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "timetickets:edit"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "timetickets:shiftedit"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "timetickets:shiftedit"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "timetickets:editcommitted"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "timetickets:editcommitted"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.ttapprovals.view")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.ttapprovals.view")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "ttapprovals:view"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "ttapprovals:view"]}
|
||||||
<Form.Item
|
>
|
||||||
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
|
<InputNumber />
|
||||||
rules={[
|
</Form.Item>,
|
||||||
{
|
<Form.Item
|
||||||
required: true
|
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
|
||||||
//message: t("general.validation.required"),
|
rules={[
|
||||||
}
|
{
|
||||||
]}
|
required: true
|
||||||
name={["md_rbac", "ttapprovals:approve"]}
|
//message: t("general.validation.required"),
|
||||||
>
|
}
|
||||||
<InputNumber />
|
]}
|
||||||
</Form.Item>
|
name={["md_rbac", "ttapprovals:approve"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.enter")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:enter"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.list")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:list"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>,
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true
|
||||||
|
//message: t("general.validation.required"),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
name={["md_rbac", "timetickets:shiftedit"]}
|
||||||
|
>
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
|
]:[]}
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.shop.vendors")}
|
label={t("bodyshop.fields.rbac.shop.vendors")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -690,7 +731,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
{/* <Form.Item
|
||||||
label={t("bodyshop.fields.rbac.shop.templates")}
|
label={t("bodyshop.fields.rbac.shop.templates")}
|
||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
@@ -701,79 +742,22 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
|
|||||||
name={["md_rbac", "shop:templates"]}
|
name={["md_rbac", "shop:templates"]}
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item> */}
|
||||||
<Form.Item
|
{HasFeatureAccess({ featureName: "media", bodyshop }) && (
|
||||||
label={t("bodyshop.fields.rbac.shop.vendors")}
|
<Form.Item
|
||||||
rules={[
|
label={t("bodyshop.fields.rbac.temporarydocs.view")}
|
||||||
{
|
rules={[
|
||||||
required: true
|
{
|
||||||
//message: t("general.validation.required"),
|
required: true
|
||||||
}
|
//message: t("general.validation.required"),
|
||||||
]}
|
}
|
||||||
name={["md_rbac", "shop:vendors"]}
|
]}
|
||||||
>
|
name={["md_rbac", "temporarydocs:view"]}
|
||||||
<InputNumber />
|
>
|
||||||
</Form.Item>
|
<InputNumber />
|
||||||
<Form.Item
|
</Form.Item>
|
||||||
label={t("bodyshop.fields.rbac.temporarydocs.view")}
|
)}
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "temporarydocs:view"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.timetickets.edit")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "timetickets:edit"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.timetickets.enter")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "timetickets:enter"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.timetickets.list")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "timetickets:list"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
|
|
||||||
rules={[
|
|
||||||
{
|
|
||||||
required: true
|
|
||||||
//message: t("general.validation.required"),
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
name={["md_rbac", "timetickets:shiftedit"]}
|
|
||||||
>
|
|
||||||
<InputNumber />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("bodyshop.fields.rbac.users.editaccess")}
|
label={t("bodyshop.fields.rbac.users.editaccess")}
|
||||||
rules={[
|
rules={[
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
|
|||||||
{(fields, { add, remove, move }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<LayoutFormRow>
|
<Space size='large' wrap>
|
||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<Form.Item key={field.key}>
|
<Form.Item key={field.key}>
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
@@ -386,7 +386,7 @@ export function ShopInfoROStatusComponent({ bodyshop, form }) {
|
|||||||
</Space>
|
</Space>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
))}
|
))}
|
||||||
</LayoutFormRow>
|
</Space>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
<Button
|
<Button
|
||||||
type="dashed"
|
type="dashed"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button, Result } from "antd";
|
import { Result } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
@@ -16,19 +16,5 @@ export default connect(mapStateToProps, mapDispatchToProps)(ShopSubStatus);
|
|||||||
export function ShopSubStatus({ bodyshop }) {
|
export function ShopSubStatus({ bodyshop }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { sub_status } = bodyshop;
|
const { sub_status } = bodyshop;
|
||||||
return (
|
return <Result status="403" title={t(`general.labels.sub_status.${sub_status}`)} />;
|
||||||
<Result
|
|
||||||
status="403"
|
|
||||||
title={t(`general.labels.sub_status.${sub_status}`)}
|
|
||||||
extra={
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
alert("Not implemented yet.");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("general.actions.submitticket")}
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { Link } from "react-router-dom";
|
|||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { alphaSort, statusSort } from "../../utils/sorters";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
|
import { alphaSort, dateSort, statusSort } from "../../utils/sorters";
|
||||||
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
import OwnerNameDisplay, { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
|
||||||
import VehicleDetailUpdateJobsComponent from "../vehicle-detail-update-jobs/vehicle-detail-update-jobs.component";
|
import VehicleDetailUpdateJobsComponent from "../vehicle-detail-update-jobs/vehicle-detail-update-jobs.component";
|
||||||
|
|
||||||
@@ -67,9 +68,20 @@ export function VehicleDetailJobsComponent({ vehicle, bodyshop }) {
|
|||||||
text: status,
|
text: status,
|
||||||
value: status
|
value: status
|
||||||
})),
|
})),
|
||||||
onFilter: (value, record) => value.includes(record.status)
|
onFilter: (value, record) => value.includes(record.status),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("jobs.fields.actual_completion"),
|
||||||
|
dataIndex: "actual_completion",
|
||||||
|
key: "actual_completion",
|
||||||
|
render: (text, record) => (
|
||||||
|
<DateTimeFormatter>{record.actual_completion}</DateTimeFormatter>
|
||||||
|
),
|
||||||
|
sorter: (a, b) => dateSort(a.actual_completion, b.actual_completion),
|
||||||
|
sortOrder:
|
||||||
|
state.sortedInfo.columnKey === "actual_completion" &&
|
||||||
|
state.sortedInfo.order,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: t("jobs.fields.clm_total"),
|
title: t("jobs.fields.clm_total"),
|
||||||
dataIndex: "clm_total",
|
dataIndex: "clm_total",
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ export const QUERY_BILLS_BY_JOBID = gql`
|
|||||||
order_date
|
order_date
|
||||||
deliver_by
|
deliver_by
|
||||||
return
|
return
|
||||||
|
returnfrombill
|
||||||
orderedby
|
orderedby
|
||||||
parts_order_lines {
|
parts_order_lines {
|
||||||
id
|
id
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export const QUERY_OWNER_BY_ID = gql`
|
|||||||
tax_number
|
tax_number
|
||||||
jobs(order_by: { date_open: desc }) {
|
jobs(order_by: { date_open: desc }) {
|
||||||
id
|
id
|
||||||
|
actual_completion
|
||||||
ro_number
|
ro_number
|
||||||
clm_no
|
clm_no
|
||||||
status
|
status
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const QUERY_VEHICLE_BY_ID = gql`
|
|||||||
notes
|
notes
|
||||||
jobs(order_by: { date_open: desc }) {
|
jobs(order_by: { date_open: desc }) {
|
||||||
id
|
id
|
||||||
|
actual_completion
|
||||||
ro_number
|
ro_number
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
ownr_fn
|
ownr_fn
|
||||||
|
|||||||
@@ -51,6 +51,17 @@ export function JobsAvailablePageContainer({ partnerVersion, setBreadcrumbs, set
|
|||||||
{!partnerVersion && (
|
{!partnerVersion && (
|
||||||
<AlertComponent
|
<AlertComponent
|
||||||
type="warning"
|
type="warning"
|
||||||
|
action={
|
||||||
|
<a
|
||||||
|
href={InstanceRenderManager({
|
||||||
|
imex: "https://partner.imex.online/Setup.exe",
|
||||||
|
rome: "https://partner.romeonline.io/Setup.exe",
|
||||||
|
promanager: "https://dzaenazwrgg60.cloudfront.net/Setup.exe"
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Button size="small">{t("general.actions.download")}</Button>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
message={t("general.messages.partnernotrunning", {
|
message={t("general.messages.partnernotrunning", {
|
||||||
app: InstanceRenderManager({
|
app: InstanceRenderManager({
|
||||||
imex: "$t(titles.imexonline)",
|
imex: "$t(titles.imexonline)",
|
||||||
|
|||||||
@@ -20,18 +20,21 @@ export default function JobsCreateComponent({ form }) {
|
|||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
title: t("jobs.labels.create.vehicleinfo"),
|
title: t("jobs.labels.create.vehicleinfo"),
|
||||||
|
id: "step-job-vehicleinfo",
|
||||||
content: <JobsCreateVehicleInfoContainer form={form} />,
|
content: <JobsCreateVehicleInfoContainer form={form} />,
|
||||||
validation: !!state.vehicle.new || !!state.vehicle.selectedid || !!state.vehicle.none,
|
validation: !!state.vehicle.new || !!state.vehicle.selectedid || !!state.vehicle.none,
|
||||||
error: t("vehicles.errors.selectexistingornew")
|
error: t("vehicles.errors.selectexistingornew")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.labels.create.ownerinfo"),
|
title: t("jobs.labels.create.ownerinfo"),
|
||||||
|
id: "step-job-ownerinfo",
|
||||||
content: <JobsCreateOwnerInfoContainer />,
|
content: <JobsCreateOwnerInfoContainer />,
|
||||||
validation: !!state.owner.new || !!state.owner.selectedid,
|
validation: !!state.owner.new || !!state.owner.selectedid,
|
||||||
error: t("owners.errors.selectexistingornew")
|
error: t("owners.errors.selectexistingornew")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("jobs.labels.create.jobinfo"),
|
title: t("jobs.labels.create.jobinfo"),
|
||||||
|
id: "step-job-jobinfo",
|
||||||
content: <JobsCreateJobsInfo form={form} selected={pageIndex === 2} />
|
content: <JobsCreateJobsInfo form={form} selected={pageIndex === 2} />
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Button, Collapse, FloatButton, Layout, Space, Spin, Tag } from "antd";
|
import { FloatButton, Layout, Spin } from "antd";
|
||||||
// import preval from "preval.macro";
|
// import preval from "preval.macro";
|
||||||
import React, { lazy, Suspense, useEffect, useState } from "react";
|
import React, { lazy, Suspense, useEffect, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -117,7 +117,6 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyRideFinished }) {
|
export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoyRideFinished }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [chatVisible] = useState(false);
|
const [chatVisible] = useState(false);
|
||||||
const [tours, setTours] = useState([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const widgetId = InstanceRenderManager({
|
const widgetId = InstanceRenderManager({
|
||||||
@@ -630,28 +629,6 @@ export function Manage({ conflict, bodyshop, enableJoyRide, joyRideSteps, setJoy
|
|||||||
Disclaimer & Notices
|
Disclaimer & Notices
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{InstanceRenderManager({
|
|
||||||
promanager: (
|
|
||||||
<Collapse>
|
|
||||||
<Collapse.Panel header="DEVELOPMENT ONLY - ProductFruits Tours">
|
|
||||||
<Space>
|
|
||||||
<Button
|
|
||||||
onClick={async () => {
|
|
||||||
setTours(await window.productFruits.api.tours.getTours());
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Get Tours
|
|
||||||
</Button>
|
|
||||||
{tours.map((tour) => (
|
|
||||||
<Tag key={tour.id} onClick={() => window.productFruits.api.tours.tryStartTour(tour.id)}>
|
|
||||||
{tour.name}
|
|
||||||
</Tag>
|
|
||||||
))}
|
|
||||||
</Space>
|
|
||||||
</Collapse.Panel>
|
|
||||||
</Collapse>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Footer>
|
</Footer>
|
||||||
</Layout>
|
</Layout>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -72,10 +72,17 @@ export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push({
|
InstanceRenderManager({
|
||||||
key: "licensing",
|
executeFunction: true,
|
||||||
label: t("bodyshop.labels.licensing"),
|
args: [],
|
||||||
children: <ShopInfoUsersComponent />
|
imex: () => {
|
||||||
|
items.push({
|
||||||
|
key: "licensing",
|
||||||
|
label: t("bodyshop.labels.licensing"),
|
||||||
|
children: <ShopInfoUsersComponent />
|
||||||
|
});
|
||||||
|
},
|
||||||
|
rome: "USE_IMEX"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (HasFeatureAccess({ featureName: "csi", bodyshop })) {
|
if (HasFeatureAccess({ featureName: "csi", bodyshop })) {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ const INITIAL_STATE = {
|
|||||||
name: "ShopName",
|
name: "ShopName",
|
||||||
address: InstanceRenderManager({
|
address: InstanceRenderManager({
|
||||||
imex: "noreply@iemx.online",
|
imex: "noreply@iemx.online",
|
||||||
rome: "noreply@romeonline.io"
|
rome: "noreply@romeonline.io",
|
||||||
|
promanager: "noreply@promanager.web-est.com"
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
to: null,
|
to: null,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { getToken } from "firebase/messaging";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import LogRocket from "logrocket";
|
import LogRocket from "logrocket";
|
||||||
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
|
import { all, call, delay, put, select, takeLatest } from "redux-saga/effects";
|
||||||
|
import { Userpilot } from "userpilot";
|
||||||
import { factory } from "../../App/App.container";
|
import { factory } from "../../App/App.container";
|
||||||
import {
|
import {
|
||||||
analytics,
|
analytics,
|
||||||
@@ -25,6 +26,10 @@ import {
|
|||||||
messaging,
|
messaging,
|
||||||
updateCurrentUser
|
updateCurrentUser
|
||||||
} from "../../firebase/firebase.utils";
|
} from "../../firebase/firebase.utils";
|
||||||
|
import { QUERY_EULA } from "../../graphql/bodyshop.queries";
|
||||||
|
import client from "../../utils/GraphQLClient";
|
||||||
|
import day from "../../utils/day";
|
||||||
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import {
|
import {
|
||||||
checkInstanceId,
|
checkInstanceId,
|
||||||
sendPasswordResetFailure,
|
sendPasswordResetFailure,
|
||||||
@@ -43,11 +48,6 @@ import {
|
|||||||
validatePasswordResetSuccess
|
validatePasswordResetSuccess
|
||||||
} from "./user.actions";
|
} from "./user.actions";
|
||||||
import UserActionTypes from "./user.types";
|
import UserActionTypes from "./user.types";
|
||||||
import client from "../../utils/GraphQLClient";
|
|
||||||
import { QUERY_EULA } from "../../graphql/bodyshop.queries";
|
|
||||||
import day from "../../utils/day";
|
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
import { Userpilot } from "userpilot";
|
|
||||||
|
|
||||||
const fpPromise = FingerprintJS.load();
|
const fpPromise = FingerprintJS.load();
|
||||||
|
|
||||||
@@ -310,10 +310,41 @@ export function* SetAuthLevelFromShopDetails({ payload }) {
|
|||||||
updateUserDetailsSuccess(authRecord[0] ? { validemail: authRecord[0].user.validemail } : { validemail: false })
|
updateUserDetailsSuccess(authRecord[0] ? { validemail: authRecord[0].user.validemail } : { validemail: false })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const user = yield select((state) => state.user.currentUser);
|
||||||
if (payload.features.singleDeviceOnly) {
|
if (payload.features.singleDeviceOnly) {
|
||||||
const user = yield select((state) => state.user.currentUser);
|
if (
|
||||||
|
!(
|
||||||
|
user.email.includes("@imex.") ||
|
||||||
|
user.email.includes("@rome.") ||
|
||||||
|
user.email.includes("@rometech.") ||
|
||||||
|
user.email.includes("@promanager.")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
yield put(setInstanceId(user.uid));
|
||||||
|
}
|
||||||
|
|
||||||
if (!(user.email.includes("@imex.") || user.email.includes("@rome."))) yield put(setInstanceId(user.uid));
|
//For Rome, check to make sure it's not a PM shop.
|
||||||
|
try {
|
||||||
|
InstanceRenderManager({
|
||||||
|
executeFunction: true,
|
||||||
|
args: [],
|
||||||
|
rome: () => {
|
||||||
|
if (
|
||||||
|
payload.imexshopid.toLowerCase().startsWith("pm_") &&
|
||||||
|
!(
|
||||||
|
user.email.includes("@imex.") ||
|
||||||
|
user.email.includes("@rome.") ||
|
||||||
|
user.email.includes("@rometech.") ||
|
||||||
|
user.email.includes("@promanager.")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error("You are not authorized to use this application.");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
promanager: () => {}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
yield put(setInstanceConflict());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
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
@@ -1,5 +1,6 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { auth } from "../firebase/firebase.utils";
|
import { auth } from "../firebase/firebase.utils";
|
||||||
|
import InstanceRenderManager from "./instanceRenderMgr";
|
||||||
|
|
||||||
axios.defaults.baseURL =
|
axios.defaults.baseURL =
|
||||||
import.meta.env.VITE_APP_AXIOS_BASE_API_URL ||
|
import.meta.env.VITE_APP_AXIOS_BASE_API_URL ||
|
||||||
@@ -12,6 +13,15 @@ export const axiosAuthInterceptorId = axios.interceptors.request.use(
|
|||||||
if (token) {
|
if (token) {
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
|
InstanceRenderManager({
|
||||||
|
executeFunction: true,
|
||||||
|
args: [],
|
||||||
|
promanager: () => {
|
||||||
|
if (!config.url.startsWith("http://localhost:1337")) {
|
||||||
|
config.headers["Convenient-Company"] = "promanager";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import client from "../utils/GraphQLClient";
|
|||||||
import cleanAxios from "./CleanAxios";
|
import cleanAxios from "./CleanAxios";
|
||||||
import { TemplateList } from "./TemplateConstants";
|
import { TemplateList } from "./TemplateConstants";
|
||||||
import { generateTemplate } from "./graphQLmodifier";
|
import { generateTemplate } from "./graphQLmodifier";
|
||||||
|
import InstanceRenderManager from "./instanceRenderMgr";
|
||||||
const server = import.meta.env.VITE_APP_REPORTS_SERVER_URL;
|
const server = import.meta.env.VITE_APP_REPORTS_SERVER_URL;
|
||||||
|
|
||||||
jsreport.serverUrl = server;
|
jsreport.serverUrl = server;
|
||||||
@@ -71,8 +72,8 @@ export default async function RenderTemplate(
|
|||||||
...contextData,
|
...contextData,
|
||||||
...templateObject.variables,
|
...templateObject.variables,
|
||||||
...templateObject.context,
|
...templateObject.context,
|
||||||
headerpath: `/${bodyshop.imexshopid}/header.html`,
|
headerpath: `/${InstanceRenderManager({ imex: bodyshop.imexshopid, rome: bodyshop.imexshopid, promanager: "GENERIC" })}/header.html`,
|
||||||
footerpath: `/${bodyshop.imexshopid}/footer.html`,
|
footerpath: `/${InstanceRenderManager({ imex: bodyshop.imexshopid, rome: bodyshop.imexshopid, promanager: "GENERIC" })}/footer.html`,
|
||||||
bodyshop: bodyshop,
|
bodyshop: bodyshop,
|
||||||
filters: templateObject?.filters,
|
filters: templateObject?.filters,
|
||||||
sorters: templateObject?.sorters,
|
sorters: templateObject?.sorters,
|
||||||
|
|||||||
@@ -920,6 +920,7 @@
|
|||||||
- cdk_dealerid
|
- cdk_dealerid
|
||||||
- city
|
- city
|
||||||
- claimscorpid
|
- claimscorpid
|
||||||
|
- convenient_company
|
||||||
- country
|
- country
|
||||||
- created_at
|
- created_at
|
||||||
- default_adjustment_rate
|
- default_adjustment_rate
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Could not auto-generate a down migration.
|
||||||
|
-- Please write an appropriate down migration for the SQL below:
|
||||||
|
-- alter table "public"."bodyshops" add column "convenient_company" text
|
||||||
|
-- null;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "convenient_company" text
|
||||||
|
null;
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
const DineroQbFormat = require("./accounting-constants").DineroQbFormat;
|
const DineroQbFormat = require("./accounting-constants").DineroQbFormat;
|
||||||
|
|
||||||
const Dinero = require("dinero.js");
|
const Dinero = require("dinero.js");
|
||||||
const { DiscountNotAlreadyCounted } = require("../job/job-totals");
|
|
||||||
const logger = require("../utils/logger");
|
|
||||||
const InstanceManager = require("../utils/instanceMgr").default;
|
const InstanceManager = require("../utils/instanceMgr").default;
|
||||||
|
const { DiscountNotAlreadyCounted } = InstanceManager({
|
||||||
|
imex: require("../job/job-totals"),
|
||||||
|
rome: require("../job/job-totals-USA"),
|
||||||
|
promanager: "USE_ROME"
|
||||||
|
});
|
||||||
|
const logger = require("../utils/logger");
|
||||||
|
|
||||||
exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes, classes }) {
|
exports.default = function ({ bodyshop, jobs_by_pk, qbo = false, items, taxCodes, classes }) {
|
||||||
const InvoiceLineAdd = [];
|
const InvoiceLineAdd = [];
|
||||||
|
|||||||
@@ -9,8 +9,12 @@ const CdkBase = require("../web-sockets/web-socket");
|
|||||||
|
|
||||||
const Dinero = require("dinero.js");
|
const Dinero = require("dinero.js");
|
||||||
const _ = require("lodash");
|
const _ = require("lodash");
|
||||||
const { DiscountNotAlreadyCounted } = require("../job/job-totals");
|
|
||||||
const InstanceManager = require("../utils/instanceMgr").default;
|
const InstanceManager = require("../utils/instanceMgr").default;
|
||||||
|
const { DiscountNotAlreadyCounted } = InstanceManager({
|
||||||
|
imex: require("../job/job-totals"),
|
||||||
|
rome: require("../job/job-totals-USA"),
|
||||||
|
promanager: "USE_ROME"
|
||||||
|
});
|
||||||
|
|
||||||
exports.default = async function (socket, jobid) {
|
exports.default = async function (socket, jobid) {
|
||||||
try {
|
try {
|
||||||
@@ -232,10 +236,36 @@ exports.default = async function (socket, jobid) {
|
|||||||
const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
|
const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
|
||||||
const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName);
|
const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName);
|
||||||
if (mapaAccount) {
|
if (mapaAccount) {
|
||||||
if (!costCenterHash[mapaAccountName]) costCenterHash[mapaAccountName] = Dinero();
|
if (!costCenterHash[mapaAccountName])
|
||||||
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
|
costCenterHash[mapaAccountName] = Dinero();
|
||||||
Dinero(job.job_totals.rates.mapa.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
|
if (job.bodyshop.use_paint_scale_data === true) {
|
||||||
);
|
if (job.mixdata.length > 0) {
|
||||||
|
costCenterHash[mapaAccountName] = costCenterHash[
|
||||||
|
mapaAccountName
|
||||||
|
].add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round(
|
||||||
|
((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) *
|
||||||
|
100
|
||||||
|
),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
costCenterHash[mapaAccountName] = costCenterHash[
|
||||||
|
mapaAccountName
|
||||||
|
].add(
|
||||||
|
Dinero(job.job_totals.rates.mapa.total).percentage(
|
||||||
|
bodyshop?.cdk_configuration?.sendmaterialscosting
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
|
||||||
|
Dinero(job.job_totals.rates.mapa.total).percentage(
|
||||||
|
bodyshop?.cdk_configuration?.sendmaterialscosting
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//console.log("NO MAPA ACCOUNT FOUND!!");
|
//console.log("NO MAPA ACCOUNT FOUND!!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const ses = new aws.SES({
|
|||||||
|
|
||||||
const transporter = nodemailer.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
SES: { ses, aws },
|
SES: { ses, aws },
|
||||||
sendingRate: 40 // 40 emails per second.
|
sendingRate: InstanceManager({ imex: 40, rome: 10 })
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize the Tasks Email Queue
|
// Initialize the Tasks Email Queue
|
||||||
@@ -41,38 +41,28 @@ const tasksEmailQueueCleanup = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handling SIGINT (e.g., Ctrl+C)
|
if (process.env.NODE_ENV !== "development") {
|
||||||
process.on("SIGINT", async () => {
|
// Handling SIGINT (e.g., Ctrl+C)
|
||||||
await tasksEmailQueueCleanup();
|
process.on("SIGINT", async () => {
|
||||||
process.exit(0);
|
await tasksEmailQueueCleanup();
|
||||||
});
|
process.exit(0);
|
||||||
// Handling SIGTERM (e.g., sent by system shutdown)
|
});
|
||||||
process.on("SIGTERM", async () => {
|
// Handling SIGTERM (e.g., sent by system shutdown)
|
||||||
await tasksEmailQueueCleanup();
|
process.on("SIGTERM", async () => {
|
||||||
process.exit(0);
|
await tasksEmailQueueCleanup();
|
||||||
});
|
process.exit(0);
|
||||||
// Handling uncaught exceptions
|
});
|
||||||
process.on("uncaughtException", async (err) => {
|
// Handling uncaught exceptions
|
||||||
await tasksEmailQueueCleanup();
|
process.on("uncaughtException", async (err) => {
|
||||||
process.exit(1); // Exit with an 'error' code
|
await tasksEmailQueueCleanup();
|
||||||
});
|
process.exit(1); // Exit with an 'error' code
|
||||||
// Handling unhandled promise rejections
|
});
|
||||||
process.on("unhandledRejection", async (reason, promise) => {
|
// Handling unhandled promise rejections
|
||||||
await tasksEmailQueueCleanup();
|
process.on("unhandledRejection", async (reason, promise) => {
|
||||||
process.exit(1); // Exit with an 'error' code
|
await tasksEmailQueueCleanup();
|
||||||
});
|
process.exit(1); // Exit with an 'error' code
|
||||||
|
});
|
||||||
const fromEmails = InstanceManager({
|
}
|
||||||
imex: "ImEX Online <noreply@imex.online>",
|
|
||||||
rome: "Rome Online <noreply@romeonline.io>",
|
|
||||||
promanager: "ProManager <noreply@promanager.web-est.com>"
|
|
||||||
});
|
|
||||||
|
|
||||||
const endPoints = InstanceManager({
|
|
||||||
imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online",
|
|
||||||
rome: process.env?.NODE_ENV === "test" ? "https//test.romeonline.io" : "https://romeonline.io",
|
|
||||||
promanager: process.env?.NODE_ENV === "test" ? "https//test.promanager.web-est.com" : "https://promanager.web-est.com"
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format the date for the email.
|
* Format the date for the email.
|
||||||
@@ -105,6 +95,17 @@ const formatPriority = (priority) => {
|
|||||||
* @returns {{header, body: string, subHeader: string}}
|
* @returns {{header, body: string, subHeader: string}}
|
||||||
*/
|
*/
|
||||||
const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId) => {
|
const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId) => {
|
||||||
|
const endPoints = InstanceManager({
|
||||||
|
imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online",
|
||||||
|
rome:
|
||||||
|
bodyshop.convenient_company === "promanager"
|
||||||
|
? process.env?.NODE_ENV === "test"
|
||||||
|
? "https//test.promanager.web-est.com"
|
||||||
|
: "https://promanager.web-est.com"
|
||||||
|
: process.env?.NODE_ENV === "test"
|
||||||
|
? "https//test.romeonline.io"
|
||||||
|
: "https://romeonline.io"
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
header: title,
|
header: title,
|
||||||
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)}`,
|
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)}`,
|
||||||
@@ -121,12 +122,15 @@ const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, j
|
|||||||
* @param taskIds
|
* @param taskIds
|
||||||
* @param successCallback
|
* @param successCallback
|
||||||
*/
|
*/
|
||||||
const sendMail = (type, to, subject, html, taskIds, successCallback) => {
|
const sendMail = (type, to, subject, html, taskIds, successCallback, requestInstance) => {
|
||||||
// Push next messages to Nodemailer
|
const fromEmails = InstanceManager({
|
||||||
//transporter.once("idle", () => {
|
imex: "ImEX Online <noreply@imex.online>",
|
||||||
// Note: This is commented out because despite being in the documentation, it does not work
|
rome:
|
||||||
// and stackoverflow suggests it is not needed
|
requestInstance === "promanager"
|
||||||
// if (transporter.isIdle()) {
|
? "ProManager <noreply@promanager.web-est.com>"
|
||||||
|
: "Rome Online <noreply@romeonline.io>"
|
||||||
|
});
|
||||||
|
|
||||||
transporter.sendMail(
|
transporter.sendMail(
|
||||||
{
|
{
|
||||||
from: fromEmails,
|
from: fromEmails,
|
||||||
@@ -184,7 +188,10 @@ const taskAssignedEmail = async (req, res) => {
|
|||||||
tasks_by_pk.job,
|
tasks_by_pk.job,
|
||||||
newTask.id
|
newTask.id
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
tasks_by_pk.bodyshop.convenient_company
|
||||||
);
|
);
|
||||||
|
|
||||||
// We return success regardless because we don't want to block the event trigger.
|
// We return success regardless because we don't want to block the event trigger.
|
||||||
@@ -233,6 +240,14 @@ const tasksRemindEmail = async (req, res) => {
|
|||||||
|
|
||||||
// Iterate over all recipients and send the email.
|
// Iterate over all recipients and send the email.
|
||||||
recipientCounts.forEach((recipient) => {
|
recipientCounts.forEach((recipient) => {
|
||||||
|
const fromEmails = InstanceManager({
|
||||||
|
imex: "ImEX Online <noreply@imex.online>",
|
||||||
|
rome:
|
||||||
|
onlyTask.bodyshop.convenient_company === "promanager"
|
||||||
|
? "ProManager <noreply@promanager.web-est.com>"
|
||||||
|
: "Rome Online <noreply@romeonline.io>"
|
||||||
|
});
|
||||||
|
|
||||||
const emailData = {
|
const emailData = {
|
||||||
from: fromEmails,
|
from: fromEmails,
|
||||||
to: recipient.email
|
to: recipient.email
|
||||||
@@ -261,6 +276,18 @@ const tasksRemindEmail = async (req, res) => {
|
|||||||
}
|
}
|
||||||
// There are multiple emails to send to this author.
|
// There are multiple emails to send to this author.
|
||||||
else {
|
else {
|
||||||
|
const endPoints = InstanceManager({
|
||||||
|
imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online",
|
||||||
|
rome:
|
||||||
|
allTasks[0].bodyshop.convenient_company === "promanager"
|
||||||
|
? process.env?.NODE_ENV === "test"
|
||||||
|
? "https//test.promanager.web-est.com"
|
||||||
|
: "https://promanager.web-est.com"
|
||||||
|
: process.env?.NODE_ENV === "test"
|
||||||
|
? "https//test.romeonline.io"
|
||||||
|
: "https://romeonline.io"
|
||||||
|
});
|
||||||
|
|
||||||
const allTasks = groupedTasks[recipient.email];
|
const allTasks = groupedTasks[recipient.email];
|
||||||
emailData.subject = `New Tasks Reminder - ${allTasks.length} Tasks require your attention`;
|
emailData.subject = `New Tasks Reminder - ${allTasks.length} Tasks require your attention`;
|
||||||
emailData.html = generateEmailTemplate({
|
emailData.html = generateEmailTemplate({
|
||||||
@@ -278,11 +305,19 @@ const tasksRemindEmail = async (req, res) => {
|
|||||||
|
|
||||||
if (emailData?.subject && emailData?.html) {
|
if (emailData?.subject && emailData?.html) {
|
||||||
// Send Email
|
// Send Email
|
||||||
sendMail("remind", emailData.to, emailData.subject, emailData.html, taskIds, (taskIds) => {
|
sendMail(
|
||||||
for (const taskId of taskIds) {
|
"remind",
|
||||||
tasksEmailQueue.push(taskId);
|
emailData.to,
|
||||||
}
|
emailData.subject,
|
||||||
});
|
emailData.html,
|
||||||
|
taskIds,
|
||||||
|
(taskIds) => {
|
||||||
|
for (const taskId of taskIds) {
|
||||||
|
tasksEmailQueue.push(taskId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allTasks[0].bodyshop.convenient_company
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1790,6 +1790,7 @@ exports.GET_CDK_ALLOCATIONS = `query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
|
|||||||
md_responsibility_centers
|
md_responsibility_centers
|
||||||
cdk_configuration
|
cdk_configuration
|
||||||
pbs_configuration
|
pbs_configuration
|
||||||
|
use_paint_scale_data
|
||||||
}
|
}
|
||||||
ro_number
|
ro_number
|
||||||
dms_allocation
|
dms_allocation
|
||||||
@@ -1897,6 +1898,10 @@ exports.GET_CDK_ALLOCATIONS = `query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
|
|||||||
line_ref
|
line_ref
|
||||||
unq_seq
|
unq_seq
|
||||||
}
|
}
|
||||||
|
mixdata(limit: 1, order_by: {updated_at: desc}) {
|
||||||
|
jobid
|
||||||
|
totalliquidcost
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
@@ -2431,6 +2436,7 @@ exports.QUERY_REMIND_TASKS = `
|
|||||||
jobid
|
jobid
|
||||||
bodyshop {
|
bodyshop {
|
||||||
shopname
|
shopname
|
||||||
|
convenient_company
|
||||||
}
|
}
|
||||||
bodyshopid
|
bodyshopid
|
||||||
}
|
}
|
||||||
@@ -2453,6 +2459,7 @@ query QUERY_TASK_BY_ID($id: uuid!) {
|
|||||||
}
|
}
|
||||||
bodyshop{
|
bodyshop{
|
||||||
shopname
|
shopname
|
||||||
|
convenient_company
|
||||||
}
|
}
|
||||||
job{
|
job{
|
||||||
ro_number
|
ro_number
|
||||||
@@ -2467,3 +2474,12 @@ query QUERY_TASK_BY_ID($id: uuid!) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
exports.GET_JOBS_BY_PKS = `query GET_JOBS_BY_PKS($ids: [uuid!]!) {
|
||||||
|
jobs(where: {id: {_in: $ids}}) {
|
||||||
|
id
|
||||||
|
shopid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const domain = process.env.NODE_ENV ? "secure" : "test";
|
|||||||
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");
|
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");
|
||||||
|
|
||||||
const client = new SecretsManagerClient({
|
const client = new SecretsManagerClient({
|
||||||
region: "ca-central-1"
|
region: "ca-central-1" //TODO-AIO: instance manager required when merged to master-AIO
|
||||||
});
|
});
|
||||||
|
|
||||||
const gqlClient = require("../graphql-client/graphql-client").client;
|
const gqlClient = require("../graphql-client/graphql-client").client;
|
||||||
@@ -145,45 +145,89 @@ exports.generate_payment_url = async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
exports.postback = async (req, res) => {
|
exports.postback = async (req, res) => {
|
||||||
logger.log("intellipay-postback", "ERROR", req.user?.email, null, req.body);
|
logger.log("intellipay-postback", "DEBUG", req.user?.email, null, req.body);
|
||||||
const { body: values } = req;
|
const { body: values } = req;
|
||||||
|
|
||||||
if (!values.invoice) {
|
const comment = Buffer.from(values?.comment, "base64").toString();
|
||||||
|
|
||||||
|
if ((!values.invoice || values.invoice === "") && !comment) {
|
||||||
|
//invoice is specified through the pay link. Comment by IO.
|
||||||
|
logger.log("intellipay-postback-ignored", "DEBUG", req.user?.email, null, req.body);
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO query job by account name
|
|
||||||
const job = await gqlClient.request(queries.GET_JOB_BY_PK, {
|
|
||||||
id: values.invoice
|
|
||||||
});
|
|
||||||
// TODO add mutation to database
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, {
|
if (values.invoice) {
|
||||||
paymentInput: {
|
//This is a link email that's been sent out.
|
||||||
amount: values.total,
|
const job = await gqlClient.request(queries.GET_JOB_BY_PK, {
|
||||||
transactionid: `C00 ${values.authcode}`,
|
id: values.invoice
|
||||||
payer: "Customer",
|
});
|
||||||
type: values.cardtype,
|
|
||||||
jobid: values.invoice,
|
|
||||||
date: moment(Date.now())
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await gqlClient.request(queries.INSERT_PAYMENT_RESPONSE, {
|
const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, {
|
||||||
paymentResponse: {
|
paymentInput: {
|
||||||
amount: values.total,
|
amount: values.total,
|
||||||
bodyshopid: job.jobs_by_pk.shopid,
|
transactionid: values.authcode,
|
||||||
paymentid: paymentResult.id,
|
payer: "Customer",
|
||||||
jobid: values.invoice,
|
type: values.cardtype,
|
||||||
declinereason: "Approved",
|
jobid: values.invoice,
|
||||||
ext_paymentid: values.paymentid,
|
date: moment(Date.now())
|
||||||
successful: true,
|
}
|
||||||
response: values
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
res.send({ message: "Postback Successful" });
|
const responseResults = await gqlClient.request(queries.INSERT_PAYMENT_RESPONSE, {
|
||||||
|
paymentResponse: {
|
||||||
|
amount: values.total,
|
||||||
|
bodyshopid: job.jobs_by_pk.shopid,
|
||||||
|
paymentid: paymentResult.id,
|
||||||
|
jobid: values.invoice,
|
||||||
|
declinereason: "Approved",
|
||||||
|
ext_paymentid: values.paymentid,
|
||||||
|
successful: true,
|
||||||
|
response: values
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.log("intellipay-postback-link-success", "DEBUG", req.user?.email, null, {
|
||||||
|
iprequest: values,
|
||||||
|
responseResults,
|
||||||
|
paymentResult
|
||||||
|
});
|
||||||
|
res.sendStatus(200);
|
||||||
|
} else if (comment) {
|
||||||
|
//This has been triggered by IO and may have multiple jobs.
|
||||||
|
const partialPayments = JSON.parse(comment);
|
||||||
|
const jobs = await gqlClient.request(queries.GET_JOBS_BY_PKS, {
|
||||||
|
ids: partialPayments.map((p) => p.jobid)
|
||||||
|
});
|
||||||
|
|
||||||
|
const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, {
|
||||||
|
paymentInput: partialPayments.map((p) => ({
|
||||||
|
amount: p.amount,
|
||||||
|
transactionid: values.authcode,
|
||||||
|
payer: "Customer",
|
||||||
|
type: values.cardtype,
|
||||||
|
jobid: p.jobid,
|
||||||
|
date: moment(Date.now()),
|
||||||
|
payment_responses: {
|
||||||
|
data: {
|
||||||
|
amount: values.total,
|
||||||
|
bodyshopid: jobs.jobs[0].shopid,
|
||||||
|
jobid: p.jobid,
|
||||||
|
declinereason: "Approved",
|
||||||
|
ext_paymentid: values.paymentid,
|
||||||
|
successful: true,
|
||||||
|
response: values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
logger.log("intellipay-postback-app-success", "DEBUG", req.user?.email, null, {
|
||||||
|
iprequest: values,
|
||||||
|
paymentResult
|
||||||
|
});
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("intellipay-postback-error", "ERROR", req.user?.email, null, {
|
logger.log("intellipay-postback-error", "ERROR", req.user?.email, null, {
|
||||||
error: JSON.stringify(error),
|
error: JSON.stringify(error),
|
||||||
|
|||||||
@@ -2,7 +2,12 @@ const _ = require("lodash");
|
|||||||
const Dinero = require("dinero.js");
|
const Dinero = require("dinero.js");
|
||||||
const queries = require("../graphql-client/queries");
|
const queries = require("../graphql-client/queries");
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
const { DiscountNotAlreadyCounted } = require("./job-totals");
|
const InstanceManager = require("../utils/instanceMgr").default;
|
||||||
|
const { DiscountNotAlreadyCounted } = InstanceManager({
|
||||||
|
imex: require("../job/job-totals"),
|
||||||
|
rome: require("../job/job-totals-USA"),
|
||||||
|
promanager: "USE_ROME"
|
||||||
|
});
|
||||||
|
|
||||||
// Dinero.defaultCurrency = "USD";
|
// Dinero.defaultCurrency = "USD";
|
||||||
// Dinero.globalLocale = "en-CA";
|
// Dinero.globalLocale = "en-CA";
|
||||||
|
|||||||
@@ -718,6 +718,7 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
|
|
||||||
const taxableAmounts = {
|
const taxableAmounts = {
|
||||||
PAA: Dinero(),
|
PAA: Dinero(),
|
||||||
|
PAE: Dinero(),
|
||||||
PAN: Dinero(),
|
PAN: Dinero(),
|
||||||
PAL: Dinero(),
|
PAL: Dinero(),
|
||||||
PAR: Dinero(),
|
PAR: Dinero(),
|
||||||
|
|||||||
Reference in New Issue
Block a user