Compare commits

...

49 Commits

Author SHA1 Message Date
Allan Carr
168d4246af IO-2785 AIO IO Header Rescue Link
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-05-22 11:43:19 -07:00
Patrick Fic
ee613db0cb Merged in test-AIO (pull request #1443)
Resolve instance conflict for promanager.
2024-05-13 15:26:07 +00:00
Patrick Fic
55b892a74e Resolve instance conflict for promanager. 2024-05-13 08:25:45 -07:00
Patrick Fic
16754d9657 Merged in test-AIO (pull request #1442)
Test AIO
2024-05-10 22:36:17 +00:00
Patrick Fic
33db67122c Fix RBAC spread. 2024-05-10 15:35:26 -07:00
Patrick Fic
b9b88b0e23 Merged in release/AIO/2024-05-10 (pull request #1441)
Release/AIO/2024 05 10
2024-05-10 16:11:42 +00:00
Patrick Fic
992ad71910 Merge branch 'release/AIO/2024-05-10' into test-AIO 2024-05-10 08:33:31 -07:00
Patrick Fic
51d1f926c2 Improve spread for feature wrapped RBAC items. 2024-05-10 08:29:52 -07:00
Patrick Fic
c70447f337 Merge branch 'release/AIO/2024-05-10' into test-AIO 2024-05-10 08:08:32 -07:00
Patrick Fic
dc367e1a30 Add PAE Part Tax type for USA. 2024-05-10 08:08:15 -07:00
Patrick Fic
ee7997ffbc Merge branch 'release/AIO/2024-05-10' into test-AIO 2024-05-08 15:16:55 -07:00
Patrick Fic
2dbb5adbbb Merge branch 'feature/AIO/promanager' into release/AIO/2024-05-10 2024-05-08 15:16:47 -07:00
Patrick Fic
cc9f342575 Update expired page, remove RBAC, and prevent sign in. 2024-05-08 15:16:03 -07:00
Patrick Fic
252262f4a7 Merge branch 'master-AIO' into feature/AIO/promanager 2024-05-08 14:21:55 -07:00
Patrick Fic
f77a16648f Merged in release/AIO/2024-04-26 (pull request #1440)
Release/AIO/2024 04 26
2024-05-03 21:42:14 +00:00
Patrick Fic
d6e3c54b68 Merge branch 'release/AIO/2024-04-26' into test-AIO 2024-05-03 14:38:38 -07:00
Patrick Fic
7294e96a77 Merge branch 'release/2024-04-26' into release/AIO/2024-04-26 2024-05-03 14:38:04 -07:00
Allan Carr
a4f4f45251 Merged in feature/IO-2717-Paint-Material-from-Scale-for-CDK (pull request #1438)
IO-2717 CDK Use Scale Data for Paint Materials Cost
2024-05-02 05:53:35 +00:00
Allan Carr
acc91abc0c IO-2717 CDK Use Scale Data for Paint Materials Cost
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-05-01 13:26:24 -07:00
Patrick Fic
18d5176cb9 Merge branch 'release/2024-04-26' into release/AIO/2024-04-26 2024-05-01 11:49:09 -07:00
Patrick Fic
d6d6ced7a4 CC modifications. 2024-05-01 11:47:42 -07:00
Patrick Fic
52809cc849 Merge branch 'release/AIO/2024-04-26' into test-AIO 2024-04-29 12:10:03 -07:00
Patrick Fic
c525b7ea3f Place featurewrapped on bills in job line expander. 2024-04-29 12:06:42 -07:00
Patrick Fic
e799417aaf Remove header for partner interaction to comply with CORS. 2024-04-29 10:50:18 -07:00
Patrick Fic
ef077c2d48 Add render manager for part price changes. 2024-04-29 10:33:34 -07:00
Patrick Fic
e78b114544 Merge branch 'release/AIO/2024-04-26' into test-AIO 2024-04-26 08:18:25 -07:00
Patrick Fic
df170ddd27 Merge branch 'release/2024-04-26' into release/AIO/2024-04-26 2024-04-25 17:24:59 -07:00
Patrick Fic
17928741e3 Merge branch 'feature/IO-2766-intellipay-postback-refactor' into release/2024-04-26 2024-04-25 16:44:55 -07:00
Patrick Fic
a6b825ffdf Move intellipay to server side processing. 2024-04-25 16:43:49 -07:00
Patrick Fic
0d3bc0e95f Merge branch 'hotfix/AIO/2024-04-24' into release/AIO/2024-04-26 2024-04-25 08:19:00 -07:00
Patrick Fic
0f62a2d73d Merge branch 'hotfix/AIO/2024-04-24' into master-AIO 2024-04-24 11:13:22 -07:00
Patrick Fic
bcdd32f92f Merge branch 'hotfix/AIO/2024-04-24' into test-AIO 2024-04-24 10:52:18 -07:00
Patrick Fic
6865fcbffc Missing import instance 2024-04-24 10:51:09 -07:00
Patrick Fic
8e623c71a9 Merge branch 'hotfix/AIO/2024-04-24' into test-AIO 2024-04-24 10:17:40 -07:00
Patrick Fic
1e06502464 Correct instance manager for job totals in other files. 2024-04-24 10:17:28 -07:00
Allan Carr
f2914868e2 Merged in feature/IO-2761-Actual-Complete-in-Vehicle-Owner-Details (pull request #1434)
IO-2761 Actual Completion in Vehicle and Owners Job Details lists

Approved-by: Dave Richer
2024-04-23 17:40:10 +00:00
Allan Carr
561f0313f9 Merged in feature/IO-2762-Return-From-Bill-Reference (pull request #1435)
IO-2762 Return from Bill Reference in Parts Return Drawer

Approved-by: Dave Richer
2024-04-23 17:39:27 +00:00
Allan Carr
1e27c4e1ae Merged in feature/IO-2763-Job-Action-Button (pull request #1436)
IO-2763 Job Action Button

Approved-by: Dave Richer
2024-04-23 17:38:28 +00:00
Allan Carr
02a49efbea IO-2762 Return from Bill Reference in Parts Return Drawer
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-04-22 14:14:54 -07:00
Patrick Fic
5f2a5e1025 Merge branch 'release/AIO/2024-04-26' into test-AIO 2024-04-22 14:03:50 -07:00
Patrick Fic
42552e4f4f Promanager resolution & remove product fruit dev items. 2024-04-22 14:03:29 -07:00
Patrick Fic
f2af78f056 Merge branch 'release/AIO/2024-04-26' into test-AIO 2024-04-22 13:15:11 -07:00
Patrick Fic
269d55bde3 Add missing translations. 2024-04-22 13:14:58 -07:00
Patrick Fic
71f3dbbeb4 Merge branch 'release/AIO/2024-04-26' into test-AIO 2024-04-22 12:39:53 -07:00
Patrick Fic
73dfb74ed7 ProManager instance server updates. 2024-04-22 12:39:30 -07:00
Patrick Fic
7dd6baef33 Hardcore generic template for ProManager. 2024-04-22 11:28:49 -07:00
Patrick Fic
519b532091 Add download messages for partner. 2024-04-22 11:28:36 -07:00
Allan Carr
4fb9c37c0d IO-2761 Actual Completion in Vehicle and Owners Job Details lists
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-04-22 09:15:59 -07:00
Patrick Fic
c9b63be29f Hardcore generic template for ProManager. 2024-04-22 08:43:41 -07:00
40 changed files with 13668 additions and 11454 deletions

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -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";
@@ -604,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,

View File

@@ -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,13 +99,16 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
</Row> </Row>
) )
})) }))
: { : [
{
key: "dispatch-lines", key: "dispatch-lines",
children: t("parts_orders.labels.notyetordered") children: t("parts_dispatch.labels.notyetdispatched")
} }
]
} }
/> />
</Col> </Col>
<FeatureWrapper featureName="bills" noauth={() => null}>
<Col md={24} lg={8}> <Col md={24} lg={8}>
<Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title> <Typography.Title level={4}>{t("bills.labels.bills")}</Typography.Title>
<Timeline <Timeline
@@ -147,6 +151,7 @@ export function JobLinesExpander({ jobline, jobid, bodyshop }) {
} }
/> />
</Col> </Col>
</FeatureWrapper>
<Col md={24} lg={24}> <Col md={24} lg={24}>
<TaskListContainer <TaskListContainer
parentJobId={jobid} parentJobId={jobid}

View File

@@ -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")}

View File

@@ -767,7 +767,7 @@ 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 }
}); });
} }

View File

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

View File

@@ -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",

View File

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

View File

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

View File

@@ -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,6 +145,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
<InputNumber min={0} /> <InputNumber min={0} />
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<FeatureWrapper featureName="export" noauth={() => null}>
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup"> <LayoutFormRow header={t("bodyshop.labels.accountingsetup")} id="accountingsetup">
<Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}> <Form.Item label={t("bodyshop.labels.qbo")} valuePropName="checked" name={["accountingconfig", "qbo"]}>
<Switch /> <Switch />
@@ -364,6 +366,8 @@ export function ShopInfoGeneral({ form, bodyshop }) {
</> </>
)} )}
</LayoutFormRow> </LayoutFormRow>
</FeatureWrapper>
<FeatureWrapper featureName="scoreboard" noauth={() => null}>
<LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup"> <LayoutFormRow header={t("bodyshop.labels.scoreboardsetup")} id="scoreboardsetup">
<Form.Item <Form.Item
label={t("bodyshop.fields.dailypainttarget")} label={t("bodyshop.fields.dailypainttarget")}
@@ -423,6 +427,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
<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,6 +572,9 @@ export function ShopInfoGeneral({ form, bodyshop }) {
> >
<Select mode="tags" /> <Select mode="tags" />
</Form.Item> </Form.Item>
{HasFeatureAccess({ featureName: "timetickets", bodyshop }) && (
<>
<Form.Item <Form.Item
name={["tt_allow_post_to_invoiced"]} name={["tt_allow_post_to_invoiced"]}
label={t("bodyshop.fields.tt_allow_post_to_invoiced")} label={t("bodyshop.fields.tt_allow_post_to_invoiced")}
@@ -588,6 +596,8 @@ export function ShopInfoGeneral({ form, bodyshop }) {
> >
<Switch /> <Switch />
</Form.Item> </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,6 +1052,7 @@ export function ShopInfoGeneral({ form, bodyshop }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
<FeatureWrapper featureName="courtesycars" noauth={() => null}>
<LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")} id="md_ccc_rates"> <LayoutFormRow grow header={t("bodyshop.fields.md_ccc_rates")} id="md_ccc_rates">
<Form.List name={["md_ccc_rates"]}> <Form.List name={["md_ccc_rates"]}>
{(fields, { add, remove, move }) => { {(fields, { add, remove, move }) => {
@@ -1053,7 +1064,11 @@ export function ShopInfoGeneral({ form, bodyshop }) {
<Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}> <Form.Item label={t("general.labels.label")} key={`${index}label`} name={[field.name, "label"]}>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label={t("contracts.fields.actax")} key={`${index}actax`} name={[field.name, "actax"]}> <Form.Item
label={t("contracts.fields.actax")}
key={`${index}actax`}
name={[field.name, "actax"]}
>
<InputNumber precision={2} /> <InputNumber precision={2} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
@@ -1147,6 +1162,8 @@ export function ShopInfoGeneral({ form, bodyshop }) {
}} }}
</Form.List> </Form.List>
</LayoutFormRow> </LayoutFormRow>
</FeatureWrapper>
<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 }) => {

View File

@@ -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,6 +30,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
return ( return (
<RbacWrapper action="shop:rbac"> <RbacWrapper action="shop:rbac">
<LayoutFormRow> <LayoutFormRow>
{...HasFeatureAccess({ featureName: "export", bodyshop }) ? [
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.accounting.exportlog")} label={t("bodyshop.fields.rbac.accounting.exportlog")}
rules={[ rules={[
@@ -40,7 +42,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "accounting:exportlog"]} name={["md_rbac", "accounting:exportlog"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.accounting.payables")} label={t("bodyshop.fields.rbac.accounting.payables")}
rules={[ rules={[
@@ -52,7 +54,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "accounting:payables"]} name={["md_rbac", "accounting:payables"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.accounting.payments")} label={t("bodyshop.fields.rbac.accounting.payments")}
rules={[ rules={[
@@ -64,7 +66,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "accounting:payments"]} name={["md_rbac", "accounting:payments"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.accounting.receivables")} label={t("bodyshop.fields.rbac.accounting.receivables")}
rules={[ rules={[
@@ -77,6 +79,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
]:[]}
{...HasFeatureAccess({ featureName: "bills", bodyshop }) ? [
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.bills.delete")} label={t("bodyshop.fields.rbac.bills.delete")}
rules={[ rules={[
@@ -88,7 +92,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "bills:delete"]} name={["md_rbac", "bills:delete"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.bills.enter")} label={t("bodyshop.fields.rbac.bills.enter")}
rules={[ rules={[
@@ -100,7 +104,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "bills:enter"]} name={["md_rbac", "bills:enter"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.bills.list")} label={t("bodyshop.fields.rbac.bills.list")}
rules={[ rules={[
@@ -112,7 +116,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "bills:list"]} name={["md_rbac", "bills:list"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.bills.reexport")} label={t("bodyshop.fields.rbac.bills.reexport")}
rules={[ rules={[
@@ -124,7 +128,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "bills:reexport"]} name={["md_rbac", "bills:reexport"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.bills.view")} label={t("bodyshop.fields.rbac.bills.view")}
rules={[ rules={[
@@ -137,6 +141,9 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
]:[]}
{...HasFeatureAccess({ featureName: "courtesycars", bodyshop }) ? [
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.contracts.create")} label={t("bodyshop.fields.rbac.contracts.create")}
rules={[ rules={[
@@ -148,7 +155,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "contracts:create"]} name={["md_rbac", "contracts:create"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.contracts.detail")} label={t("bodyshop.fields.rbac.contracts.detail")}
rules={[ rules={[
@@ -160,7 +167,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "contracts:detail"]} name={["md_rbac", "contracts:detail"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.contracts.list")} label={t("bodyshop.fields.rbac.contracts.list")}
rules={[ rules={[
@@ -172,7 +179,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "contracts:list"]} name={["md_rbac", "contracts:list"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.courtesycar.create")} label={t("bodyshop.fields.rbac.courtesycar.create")}
rules={[ rules={[
@@ -184,7 +191,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "courtesycar:create"]} name={["md_rbac", "courtesycar:create"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.courtesycar.detail")} label={t("bodyshop.fields.rbac.courtesycar.detail")}
rules={[ rules={[
@@ -196,7 +203,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "courtesycar:detail"]} name={["md_rbac", "courtesycar:detail"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.courtesycar.list")} label={t("bodyshop.fields.rbac.courtesycar.list")}
rules={[ rules={[
@@ -209,6 +216,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
]:[]}
{...HasFeatureAccess({ featureName: "csi", bodyshop }) ? [
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.csi.export")} label={t("bodyshop.fields.rbac.csi.export")}
rules={[ rules={[
@@ -220,7 +229,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "csi:export"]} name={["md_rbac", "csi:export"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.csi.page")} label={t("bodyshop.fields.rbac.csi.page")}
rules={[ rules={[
@@ -233,6 +242,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </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,6 +508,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
{HasFeatureAccess({ featureName: "visualboard", bodyshop }) && (
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.production.board")} label={t("bodyshop.fields.rbac.production.board")}
rules={[ rules={[
@@ -522,6 +521,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
)}
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.production.list")} label={t("bodyshop.fields.rbac.production.list")}
rules={[ rules={[
@@ -546,6 +547,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
{HasFeatureAccess({ featureName: "scoreboard", bodyshop }) && (
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.scoreboard.view")} label={t("bodyshop.fields.rbac.scoreboard.view")}
rules={[ rules={[
@@ -558,6 +560,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
)}
{...HasFeatureAccess({ featureName: "timetickets", bodyshop }) ? [
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.shiftclock.view")} label={t("bodyshop.fields.rbac.shiftclock.view")}
rules={[ rules={[
@@ -569,7 +573,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "shiftclock:view"]} name={["md_rbac", "shiftclock:view"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.shop.config")} label={t("bodyshop.fields.rbac.shop.config")}
rules={[ rules={[
@@ -581,7 +585,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "shop:config"]} name={["md_rbac", "shop:config"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>,
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.timetickets.edit")} label={t("bodyshop.fields.rbac.timetickets.edit")}
rules={[ rules={[
@@ -593,7 +597,79 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
name={["md_rbac", "timetickets:edit"]} name={["md_rbac", "timetickets:edit"]}
> >
<InputNumber /> <InputNumber />
</Form.Item> </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
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:editcommitted"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.view")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "ttapprovals:view"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
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 <Form.Item
label={t("bodyshop.fields.rbac.timetickets.shiftedit")} label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[ rules={[
@@ -606,42 +682,7 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </Form.Item>
<Form.Item ]:[]}
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:editcommitted"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.view")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "ttapprovals:view"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "ttapprovals:approve"]}
>
<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,19 +742,8 @@ 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")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "shop:vendors"]}
>
<InputNumber />
</Form.Item>
<Form.Item <Form.Item
label={t("bodyshop.fields.rbac.temporarydocs.view")} label={t("bodyshop.fields.rbac.temporarydocs.view")}
rules={[ rules={[
@@ -726,54 +756,8 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
> >
<InputNumber /> <InputNumber />
</Form.Item> </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={[

View File

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

View File

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

View File

@@ -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",

View File

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

View File

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

View File

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

View File

@@ -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)",

View File

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

View File

@@ -72,11 +72,18 @@ export function ShopPage({ bodyshop, setSelectedHeader, setBreadcrumbs }) {
}); });
} }
InstanceRenderManager({
executeFunction: true,
args: [],
imex: () => {
items.push({ items.push({
key: "licensing", key: "licensing",
label: t("bodyshop.labels.licensing"), label: t("bodyshop.labels.licensing"),
children: <ShopInfoUsersComponent /> children: <ShopInfoUsersComponent />
}); });
},
rome: "USE_IMEX"
});
if (HasFeatureAccess({ featureName: "csi", bodyshop })) { if (HasFeatureAccess({ featureName: "csi", bodyshop })) {
items.push({ items.push({

View File

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

View File

@@ -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 })
); );
if (payload.features.singleDeviceOnly) {
const user = yield select((state) => state.user.currentUser); const user = yield select((state) => state.user.currentUser);
if (payload.features.singleDeviceOnly) {
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 {

View File

@@ -136,12 +136,12 @@
"jobsupplement": "Job supplement imported.", "jobsupplement": "Job supplement imported.",
"jobsuspend": "Suspend Toggle set to {{status}}", "jobsuspend": "Suspend Toggle set to {{status}}",
"jobvoid": "Job has been voided.", "jobvoid": "Job has been voided.",
"tasks_created": "Task '{{title}}' created by {{createdBy}}",
"tasks_updated": "Task '{{title}}' updated by {{updatedBy}}",
"tasks_deleted": "Task '{{title}}' deleted by {{deletedBy}}",
"tasks_undeleted": "Task '{{title}}' undeleted by {{undeletedBy}}",
"tasks_completed": "Task '{{title}}' completed by {{completedBy}}", "tasks_completed": "Task '{{title}}' completed by {{completedBy}}",
"tasks_uncompleted": "Task '{{title}}' uncompleted by {{uncompletedBy}}" "tasks_created": "Task '{{title}}' created by {{createdBy}}",
"tasks_deleted": "Task '{{title}}' deleted by {{deletedBy}}",
"tasks_uncompleted": "Task '{{title}}' uncompleted by {{uncompletedBy}}",
"tasks_undeleted": "Task '{{title}}' undeleted by {{undeletedBy}}",
"tasks_updated": "Task '{{title}}' updated by {{updatedBy}}"
} }
}, },
"billlines": { "billlines": {
@@ -230,11 +230,12 @@
"markexported": "Mark Exported", "markexported": "Mark Exported",
"markforreexport": "Mark for Re-export", "markforreexport": "Mark for Re-export",
"new": "New Bill", "new": "New Bill",
"nobilllines": "This part has not yet been recieved.", "nobilllines": "",
"noneselected": "No bill selected.", "noneselected": "No bill selected.",
"onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.", "onlycmforinvoiced": "Only credit memos can be entered for any Job that has been invoiced, exported, or voided.",
"printlabels": "Print Labels", "printlabels": "Print Labels",
"retailtotal": "Bills Retail Total", "retailtotal": "Bills Retail Total",
"returnfrombill": "Return From Bill",
"savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.", "savewithdiscrepancy": "You are about to save this bill with a discrepancy. The system will continue to use the calculated amount using the bill lines. Press cancel to return to the bill.",
"state_tax": "State Tax", "state_tax": "State Tax",
"subtotal": "Subtotal", "subtotal": "Subtotal",
@@ -865,10 +866,11 @@
}, },
"status": { "status": {
"in": "Available", "in": "Available",
"inservice": "In Service", "inservice": "Service/Maintenance",
"leasereturn": "Lease Returned", "leasereturn": "Lease Returned",
"out": "Rented", "out": "Rented",
"sold": "Sold" "sold": "Sold",
"unavailable": "Unavailable"
}, },
"successes": { "successes": {
"saved": "Courtesy Car saved successfully." "saved": "Courtesy Car saved successfully."
@@ -924,7 +926,6 @@
}, },
"titles": { "titles": {
"joblifecycle": "Job Life Cycles", "joblifecycle": "Job Life Cycles",
"tasks": "Tasks",
"labhours": "Total Body Hours", "labhours": "Total Body Hours",
"larhours": "Total Refinish Hours", "larhours": "Total Refinish Hours",
"monthlyemployeeefficiency": "Monthly Employee Efficiency", "monthlyemployeeefficiency": "Monthly Employee Efficiency",
@@ -939,7 +940,8 @@
"scheduledindate": "Sheduled In Today: {{date}}", "scheduledindate": "Sheduled In Today: {{date}}",
"scheduledintoday": "Sheduled In Today", "scheduledintoday": "Sheduled In Today",
"scheduledoutdate": "Sheduled Out Today: {{date}}", "scheduledoutdate": "Sheduled Out Today: {{date}}",
"scheduledouttoday": "Sheduled Out Today" "scheduledouttoday": "Sheduled Out Today",
"tasks": "Tasks"
} }
}, },
"dms": { "dms": {
@@ -1134,6 +1136,7 @@
"delete": "Delete", "delete": "Delete",
"deleteall": "Delete All", "deleteall": "Delete All",
"deselectall": "Deselect All", "deselectall": "Deselect All",
"download": "Download",
"edit": "Edit", "edit": "Edit",
"login": "Login", "login": "Login",
"print": "Print", "print": "Print",
@@ -1359,6 +1362,7 @@
"amount": "Amount", "amount": "Amount",
"dateOfPayment": "Date of Payment", "dateOfPayment": "Date of Payment",
"descriptions": "Payment Details", "descriptions": "Payment Details",
"hint": "Hint",
"payer": "Payer", "payer": "Payer",
"payername": "Payer Name", "payername": "Payer Name",
"paymentid": "Payment Reference ID", "paymentid": "Payment Reference ID",
@@ -1868,7 +1872,6 @@
"appointmentconfirmation": "Send confirmation to customer?", "appointmentconfirmation": "Send confirmation to customer?",
"associationwarning": "Any changes to associations will require updating the data from the new parent record to the Job.", "associationwarning": "Any changes to associations will require updating the data from the new parent record to the Job.",
"audit": "Audit Trail", "audit": "Audit Trail",
"tasks": "Tasks",
"available": "Available", "available": "Available",
"availablejobs": "Available Jobs", "availablejobs": "Available Jobs",
"ca_bc_pvrt": { "ca_bc_pvrt": {
@@ -2013,7 +2016,8 @@
}, },
"ppc": "This line contains a part price change.", "ppc": "This line contains a part price change.",
"ppdnotexported": "PPDs not Exported", "ppdnotexported": "PPDs not Exported",
"profileadjustments": "Profile Disc./Mkup","profitbypassrequired": "Minimum gross profit requirements have not been met.", "profileadjustments": "Profile Disc./Mkup",
"profitbypassrequired": "Minimum gross profit requirements have not been met.",
"profits": "Job Profits", "profits": "Job Profits",
"prt_dsmk_total": "Line Item Adjustment", "prt_dsmk_total": "Line Item Adjustment",
"rates": "Rates", "rates": "Rates",
@@ -2035,7 +2039,6 @@
"remove_from_ar": "Remove from AR", "remove_from_ar": "Remove from AR",
"returntotals": "Return Totals", "returntotals": "Return Totals",
"ro_guard": { "ro_guard": {
"enforce_ar": "AR collection enforced.", "enforce_ar": "AR collection enforced.",
"enforce_bills": "Bill discrepancy enforced.", "enforce_bills": "Bill discrepancy enforced.",
"enforce_cm": "Credit memo entry enforced.", "enforce_cm": "Credit memo entry enforced.",
@@ -2064,6 +2067,7 @@
"supplementnote": "The Job had a supplement imported.", "supplementnote": "The Job had a supplement imported.",
"suspended": "SUSPENDED", "suspended": "SUSPENDED",
"suspense": "Suspense", "suspense": "Suspense",
"tasks": "Tasks",
"threshhold": "Max Threshold: ${{amount}}", "threshhold": "Max Threshold: ${{amount}}",
"total_cost": "Total Cost", "total_cost": "Total Cost",
"total_cust_payable": "Total Customer Amount Payable", "total_cust_payable": "Total Customer Amount Payable",
@@ -2150,107 +2154,18 @@
} }
} }
}, },
"tasks": {
"failures": {
"created": "Failed to create Task.",
"deleted": "Failed to toggle Task deletion.",
"updated": "Failed to update Task.",
"completed": "Failed to toggle Task completion."
},
"successes": {
"created": "Task created successfully.",
"deleted": "Toggled Task deletion successfully.",
"updated": "Task updated successfully.",
"completed": "Toggled Task completion successfully."
},
"date_presets": {
"next_week": "Next Week",
"two_weeks": "Two Weeks",
"three_weeks": "Three Weeks",
"one_month": "One Month",
"today": "Today",
"tomorrow": "Tomorrow",
"three_months": "Three Months",
"day": "Day",
"days": "Days",
"delivery": "Delivery",
"completion": "Completion"
},
"actions": {
"new": "New Task",
"edit": "Edit Task"
},
"titles": {
"all_tasks": "All Tasks",
"job_tasks": "Job Tasks",
"mine": "My Tasks",
"my_tasks": "My Tasks",
"completed": "Completed Tasks",
"deleted": "Deleted Tasks"
},
"buttons": {
"create": "Create Task",
"complete": "Toggle Complete",
"delete": "Toggle Delete",
"edit": "Edit",
"refresh": "Refresh",
"myTasks": "Mine",
"allTasks": "All"
},
"placeholders": {
"description": "Enter a description",
"jobid": "Select a Job",
"joblineid": "Select a Job Line",
"partsorderid": "Select a Parts Order",
"billid": "Select a Bill",
"assigned_to": "Select an Employee"
},
"fields": {
"priorities": {
"low": "Low",
"medium": "Medium",
"high": "High"
},
"created_at": "Created At",
"jobline": "Job Line",
"parts_order": "Parts Order",
"bill": "Bill",
"title": "Title",
"joblineid": "Job Line",
"jobid": "Job",
"partsorderid": "Parts Order",
"billid": "Bill",
"description": "Description",
"due_date": "Due Date",
"actions": "Actions",
"priority": "Priority",
"remind_at": "Remind At",
"assigned_to": "Assigned To",
"completed": "Completed",
"job": {
"ro_number": "RO #"
}
},
"validation": {
"due_at_error_message": "The due date must be in the future!",
"remind_at_error_message": "The reminder date and time must be at least 30 minutes in the future!"
}
},
"menus": { "menus": {
"currentuser": { "currentuser": {
"languageselector": "Language", "languageselector": "Language",
"profile": "Profile" "profile": "Profile"
}, },
"header": { "header": {
"create_task": "Create Task",
"tasks": "Tasks",
"my_tasks": "My Tasks",
"all_tasks": "All Tasks",
"accounting": "Accounting", "accounting": "Accounting",
"accounting-payables": "Payables", "accounting-payables": "Payables",
"accounting-payments": "Payments", "accounting-payments": "Payments",
"accounting-receivables": "Receivables", "accounting-receivables": "Receivables",
"activejobs": "Active Jobs", "activejobs": "Active Jobs",
"all_tasks": "All Tasks",
"alljobs": "All Jobs", "alljobs": "All Jobs",
"allpayments": "All Payments", "allpayments": "All Payments",
"availablejobs": "Available Jobs", "availablejobs": "Available Jobs",
@@ -2259,6 +2174,7 @@
"courtesycars-all": "All Courtesy Cars", "courtesycars-all": "All Courtesy Cars",
"courtesycars-contracts": "Contracts", "courtesycars-contracts": "Contracts",
"courtesycars-newcontract": "New Contract", "courtesycars-newcontract": "New Contract",
"create_task": "Create Task",
"customers": "Customers", "customers": "Customers",
"dashboard": "Dashboard", "dashboard": "Dashboard",
"enterbills": "Enter Bills", "enterbills": "Enter Bills",
@@ -2271,6 +2187,7 @@
"home": "Home", "home": "Home",
"inventory": "Inventory", "inventory": "Inventory",
"jobs": "Jobs", "jobs": "Jobs",
"my_tasks": "My Tasks",
"newjob": "Create New Job", "newjob": "Create New Job",
"owners": "Owners", "owners": "Owners",
"parts-queue": "Parts Queue", "parts-queue": "Parts Queue",
@@ -2297,6 +2214,7 @@
"shop_csi": "CSI", "shop_csi": "CSI",
"shop_templates": "Templates", "shop_templates": "Templates",
"shop_vendors": "Vendors", "shop_vendors": "Vendors",
"tasks": "Tasks",
"temporarydocs": "Temporary Documents", "temporarydocs": "Temporary Documents",
"timetickets": "Time Tickets", "timetickets": "Time Tickets",
"ttapprovals": "Time Ticket Approvals", "ttapprovals": "Time Ticket Approvals",
@@ -2490,6 +2408,7 @@
"percent_accepted": "% Accepted" "percent_accepted": "% Accepted"
}, },
"labels": { "labels": {
"notyetdispatched": "This part has not been dispatched.",
"parts_dispatch": "Parts Dispatch" "parts_dispatch": "Parts Dispatch"
} }
}, },
@@ -3106,6 +3025,92 @@
"updated": "Scoreboard updated." "updated": "Scoreboard updated."
} }
}, },
"tasks": {
"actions": {
"edit": "Edit Task",
"new": "New Task"
},
"buttons": {
"allTasks": "All",
"complete": "Toggle Complete",
"create": "Create Task",
"delete": "Toggle Delete",
"edit": "Edit",
"myTasks": "Mine",
"refresh": "Refresh"
},
"date_presets": {
"completion": "Completion",
"day": "Day",
"days": "Days",
"delivery": "Delivery",
"next_week": "Next Week",
"one_month": "One Month",
"three_months": "Three Months",
"three_weeks": "Three Weeks",
"today": "Today",
"tomorrow": "Tomorrow",
"two_weeks": "Two Weeks"
},
"failures": {
"completed": "Failed to toggle Task completion.",
"created": "Failed to create Task.",
"deleted": "Failed to toggle Task deletion.",
"updated": "Failed to update Task."
},
"fields": {
"actions": "Actions",
"assigned_to": "Assigned To",
"bill": "Bill",
"billid": "Bill",
"completed": "Completed",
"created_at": "Created At",
"description": "Description",
"due_date": "Due Date",
"job": {
"ro_number": "RO #"
},
"jobid": "Job",
"jobline": "Job Line",
"joblineid": "Job Line",
"parts_order": "Parts Order",
"partsorderid": "Parts Order",
"priorities": {
"high": "High",
"low": "Low",
"medium": "Medium"
},
"priority": "Priority",
"remind_at": "Remind At",
"title": "Title"
},
"placeholders": {
"assigned_to": "Select an Employee",
"billid": "Select a Bill",
"description": "Enter a description",
"jobid": "Select a Job",
"joblineid": "Select a Job Line",
"partsorderid": "Select a Parts Order"
},
"successes": {
"completed": "Toggled Task completion successfully.",
"created": "Task created successfully.",
"deleted": "Toggled Task deletion successfully.",
"updated": "Task updated successfully."
},
"titles": {
"all_tasks": "All Tasks",
"completed": "Completed Tasks",
"deleted": "Deleted Tasks",
"job_tasks": "Job Tasks",
"mine": "My Tasks",
"my_tasks": "My Tasks"
},
"validation": {
"due_at_error_message": "The due date must be in the future!",
"remind_at_error_message": "The reminder date and time must be at least 30 minutes in the future!"
}
},
"tech": { "tech": {
"fields": { "fields": {
"employeeid": "Employee ID", "employeeid": "Employee ID",
@@ -3207,20 +3212,16 @@
} }
}, },
"titles": { "titles": {
"tasks": "Tasks",
"all_tasks": "All Tasks",
"my_tasks": "My Tasks",
"accounting-payables": "Payables | {{app}}", "accounting-payables": "Payables | {{app}}",
"accounting-payments": "Payments | {{app}}", "accounting-payments": "Payments | {{app}}",
"accounting-receivables": "Receivables | {{app}}", "accounting-receivables": "Receivables | {{app}}",
"all_tasks": "All Tasks",
"app": "", "app": "",
"bc": { "bc": {
"tasks": "Tasks",
"all_tasks": "All Tasks",
"my_tasks": "My Tasks",
"accounting-payables": "Payables", "accounting-payables": "Payables",
"accounting-payments": "Payments", "accounting-payments": "Payments",
"accounting-receivables": "Receivables", "accounting-receivables": "Receivables",
"all_tasks": "All Tasks",
"availablejobs": "Available Jobs", "availablejobs": "Available Jobs",
"bills-list": "Bills", "bills-list": "Bills",
"contracts": "Contracts", "contracts": "Contracts",
@@ -3244,6 +3245,7 @@
"jobs-intake": "Intake", "jobs-intake": "Intake",
"jobs-new": "Create a New Job", "jobs-new": "Create a New Job",
"jobs-ready": "Ready Jobs", "jobs-ready": "Ready Jobs",
"my_tasks": "My Tasks",
"owner-detail": "{{name}}", "owner-detail": "{{name}}",
"owners": "Owners", "owners": "Owners",
"parts-queue": "Parts Queue", "parts-queue": "Parts Queue",
@@ -3258,6 +3260,7 @@
"shop-csi": "CSI Responses", "shop-csi": "CSI Responses",
"shop-templates": "Shop Templates", "shop-templates": "Shop Templates",
"shop-vendors": "Vendors", "shop-vendors": "Vendors",
"tasks": "Tasks",
"temporarydocs": "Temporary Documents", "temporarydocs": "Temporary Documents",
"timetickets": "Time Tickets", "timetickets": "Time Tickets",
"ttapprovals": "Time Ticket Approvals", "ttapprovals": "Time Ticket Approvals",
@@ -3288,6 +3291,7 @@
"jobsdetail": "Job {{ro_number}} | {{app}}", "jobsdetail": "Job {{ro_number}} | {{app}}",
"jobsdocuments": "Job Documents {{ro_number}} | {{app}}", "jobsdocuments": "Job Documents {{ro_number}} | {{app}}",
"manageroot": "Home | {{app}}", "manageroot": "Home | {{app}}",
"my_tasks": "My Tasks",
"owners": "All Owners | {{app}}", "owners": "All Owners | {{app}}",
"owners-detail": "{{name}} | {{app}}", "owners-detail": "{{name}} | {{app}}",
"parts-queue": "Parts Queue | {{app}}", "parts-queue": "Parts Queue | {{app}}",
@@ -3307,6 +3311,7 @@
"shop-csi": "CSI Responses | {{app}}", "shop-csi": "CSI Responses | {{app}}",
"shop-templates": "Shop Templates | {{app}}", "shop-templates": "Shop Templates | {{app}}",
"shop_vendors": "Vendors | {{app}}", "shop_vendors": "Vendors | {{app}}",
"tasks": "Tasks",
"techconsole": "Technician Console | {{app}}", "techconsole": "Technician Console | {{app}}",
"techjobclock": "Technician Job Clock | {{app}}", "techjobclock": "Technician Job Clock | {{app}}",
"techjoblookup": "Technician Job Lookup | {{app}}", "techjoblookup": "Technician Job Lookup | {{app}}",

View File

@@ -136,12 +136,12 @@
"jobsupplement": "", "jobsupplement": "",
"jobsuspend": "", "jobsuspend": "",
"jobvoid": "", "jobvoid": "",
"tasks_created": "",
"tasks_updated": "",
"tasks_deleted": "",
"tasks_undeleted": "",
"tasks_completed": "", "tasks_completed": "",
"tasks_uncompleted": "" "tasks_created": "",
"tasks_deleted": "",
"tasks_uncompleted": "",
"tasks_undeleted": "",
"tasks_updated": ""
} }
}, },
"billlines": { "billlines": {
@@ -230,10 +230,12 @@
"markexported": "", "markexported": "",
"markforreexport": "", "markforreexport": "",
"new": "", "new": "",
"nobilllines": "",
"noneselected": "", "noneselected": "",
"onlycmforinvoiced": "", "onlycmforinvoiced": "",
"printlabels": "", "printlabels": "",
"retailtotal": "", "retailtotal": "",
"returnfrombill": "",
"savewithdiscrepancy": "", "savewithdiscrepancy": "",
"state_tax": "", "state_tax": "",
"subtotal": "", "subtotal": "",
@@ -867,7 +869,8 @@
"inservice": "", "inservice": "",
"leasereturn": "", "leasereturn": "",
"out": "", "out": "",
"sold": "" "sold": "",
"unavailable": ""
}, },
"successes": { "successes": {
"saved": "" "saved": ""
@@ -923,7 +926,6 @@
}, },
"titles": { "titles": {
"joblifecycle": "", "joblifecycle": "",
"tasks": "",
"labhours": "", "labhours": "",
"larhours": "", "larhours": "",
"monthlyemployeeefficiency": "", "monthlyemployeeefficiency": "",
@@ -938,7 +940,8 @@
"scheduledindate": "", "scheduledindate": "",
"scheduledintoday": "", "scheduledintoday": "",
"scheduledoutdate": "", "scheduledoutdate": "",
"scheduledouttoday": "" "scheduledouttoday": "",
"tasks": ""
} }
}, },
"dms": { "dms": {
@@ -1133,6 +1136,7 @@
"delete": "Borrar", "delete": "Borrar",
"deleteall": "", "deleteall": "",
"deselectall": "", "deselectall": "",
"download": "",
"edit": "Editar", "edit": "Editar",
"login": "", "login": "",
"print": "", "print": "",
@@ -1358,6 +1362,7 @@
"amount": "", "amount": "",
"dateOfPayment": "", "dateOfPayment": "",
"descriptions": "", "descriptions": "",
"hint": "",
"payer": "", "payer": "",
"payername": "", "payername": "",
"paymentid": "", "paymentid": "",
@@ -1867,7 +1872,6 @@
"appointmentconfirmation": "¿Enviar confirmación al cliente?", "appointmentconfirmation": "¿Enviar confirmación al cliente?",
"associationwarning": "", "associationwarning": "",
"audit": "", "audit": "",
"tasks": "",
"available": "", "available": "",
"availablejobs": "", "availablejobs": "",
"ca_bc_pvrt": { "ca_bc_pvrt": {
@@ -2039,7 +2043,8 @@
"enforce_bills": "", "enforce_bills": "",
"enforce_cm": "", "enforce_cm": "",
"enforce_labor": "", "enforce_labor": "",
"enforce_ppd": "","enforce_profit": "", "enforce_ppd": "",
"enforce_profit": "",
"enforce_sublet": "", "enforce_sublet": "",
"enforce_validation": "", "enforce_validation": "",
"enforced": "" "enforced": ""
@@ -2062,6 +2067,7 @@
"supplementnote": "", "supplementnote": "",
"suspended": "", "suspended": "",
"suspense": "", "suspense": "",
"tasks": "",
"threshhold": "", "threshhold": "",
"total_cost": "", "total_cost": "",
"total_cust_payable": "", "total_cust_payable": "",
@@ -2148,107 +2154,18 @@
} }
} }
}, },
"tasks": {
"failures": {
"created": "",
"deleted": "",
"updated": "",
"completed": ""
},
"successes": {
"created": "",
"deleted": "",
"updated": "",
"completed": ""
},
"date_presets": {
"next_week": "",
"two_weeks": "",
"three_weeks": "",
"one_month": "",
"today": "",
"tomorrow": "",
"three_months": "",
"day": "",
"days": "",
"delivery": "",
"completion": ""
},
"actions": {
"new": "",
"edit": ""
},
"titles": {
"all_tasks": "",
"job_tasks": "",
"mine": "",
"my_tasks": "",
"completed": "",
"deleted": ""
},
"buttons": {
"create": "",
"complete": "",
"delete": "",
"edit": "",
"refresh": "",
"myTasks": "",
"allTasks": ""
},
"placeholders": {
"description": "",
"jobid": "",
"joblineid": "",
"partsorderid": "",
"billid": "",
"assigned_to": ""
},
"fields": {
"created_at": "" ,
"jobline": "",
"parts_order": "",
"bill": "",
"priorities": {
"low": "",
"medium": "",
"high": ""
},
"title": "",
"joblineid": "",
"jobid": "",
"partsorderid": "",
"billid": "",
"description": "",
"due_date": "",
"actions": "",
"priority": "",
"remind_at": "",
"assigned_to": "",
"completed": "",
"job": {
"ro_number": ""
}
},
"validation": {
"due_at_error_message": "",
"remind_at_error_message": ""
}
},
"menus": { "menus": {
"currentuser": { "currentuser": {
"languageselector": "idioma", "languageselector": "idioma",
"profile": "Perfil" "profile": "Perfil"
}, },
"header": { "header": {
"create_task": "",
"tasks": "",
"all_tasks": "",
"my_tasks": "",
"accounting": "", "accounting": "",
"accounting-payables": "", "accounting-payables": "",
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",
"activejobs": "Empleos activos", "activejobs": "Empleos activos",
"all_tasks": "",
"alljobs": "", "alljobs": "",
"allpayments": "", "allpayments": "",
"availablejobs": "Trabajos disponibles", "availablejobs": "Trabajos disponibles",
@@ -2257,6 +2174,7 @@
"courtesycars-all": "", "courtesycars-all": "",
"courtesycars-contracts": "", "courtesycars-contracts": "",
"courtesycars-newcontract": "", "courtesycars-newcontract": "",
"create_task": "",
"customers": "Clientes", "customers": "Clientes",
"dashboard": "", "dashboard": "",
"enterbills": "", "enterbills": "",
@@ -2269,6 +2187,7 @@
"home": "Casa", "home": "Casa",
"inventory": "", "inventory": "",
"jobs": "Trabajos", "jobs": "Trabajos",
"my_tasks": "",
"newjob": "", "newjob": "",
"owners": "propietarios", "owners": "propietarios",
"parts-queue": "", "parts-queue": "",
@@ -2295,6 +2214,7 @@
"shop_csi": "", "shop_csi": "",
"shop_templates": "", "shop_templates": "",
"shop_vendors": "Vendedores", "shop_vendors": "Vendedores",
"tasks": "",
"temporarydocs": "", "temporarydocs": "",
"timetickets": "", "timetickets": "",
"ttapprovals": "", "ttapprovals": "",
@@ -2488,6 +2408,7 @@
"percent_accepted": "" "percent_accepted": ""
}, },
"labels": { "labels": {
"notyetdispatched": "",
"parts_dispatch": "" "parts_dispatch": ""
} }
}, },
@@ -3104,6 +3025,92 @@
"updated": "" "updated": ""
} }
}, },
"tasks": {
"actions": {
"edit": "",
"new": ""
},
"buttons": {
"allTasks": "",
"complete": "",
"create": "",
"delete": "",
"edit": "",
"myTasks": "",
"refresh": ""
},
"date_presets": {
"completion": "",
"day": "",
"days": "",
"delivery": "",
"next_week": "",
"one_month": "",
"three_months": "",
"three_weeks": "",
"today": "",
"tomorrow": "",
"two_weeks": ""
},
"failures": {
"completed": "",
"created": "",
"deleted": "",
"updated": ""
},
"fields": {
"actions": "",
"assigned_to": "",
"bill": "",
"billid": "",
"completed": "",
"created_at": "",
"description": "",
"due_date": "",
"job": {
"ro_number": ""
},
"jobid": "",
"jobline": "",
"joblineid": "",
"parts_order": "",
"partsorderid": "",
"priorities": {
"high": "",
"low": "",
"medium": ""
},
"priority": "",
"remind_at": "",
"title": ""
},
"placeholders": {
"assigned_to": "",
"billid": "",
"description": "",
"jobid": "",
"joblineid": "",
"partsorderid": ""
},
"successes": {
"completed": "",
"created": "",
"deleted": "",
"updated": ""
},
"titles": {
"all_tasks": "",
"completed": "",
"deleted": "",
"job_tasks": "",
"mine": "",
"my_tasks": ""
},
"validation": {
"due_at_error_message": "",
"remind_at_error_message": ""
}
},
"tech": { "tech": {
"fields": { "fields": {
"employeeid": "", "employeeid": "",
@@ -3205,20 +3212,16 @@
} }
}, },
"titles": { "titles": {
"tasks": "",
"all_tasks": "",
"my_tasks": "",
"accounting-payables": "", "accounting-payables": "",
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",
"all_tasks": "",
"app": "", "app": "",
"bc": { "bc": {
"tasks": "",
"all_tasks": "",
"my_tasks": "",
"accounting-payables": "", "accounting-payables": "",
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",
"all_tasks": "",
"availablejobs": "", "availablejobs": "",
"bills-list": "", "bills-list": "",
"contracts": "", "contracts": "",
@@ -3242,6 +3245,7 @@
"jobs-intake": "", "jobs-intake": "",
"jobs-new": "", "jobs-new": "",
"jobs-ready": "", "jobs-ready": "",
"my_tasks": "",
"owner-detail": "", "owner-detail": "",
"owners": "", "owners": "",
"parts-queue": "", "parts-queue": "",
@@ -3256,6 +3260,7 @@
"shop-csi": "", "shop-csi": "",
"shop-templates": "", "shop-templates": "",
"shop-vendors": "", "shop-vendors": "",
"tasks": "",
"temporarydocs": "", "temporarydocs": "",
"timetickets": "", "timetickets": "",
"ttapprovals": "", "ttapprovals": "",
@@ -3286,6 +3291,7 @@
"jobsdetail": "Trabajo {{ro_number}} | {{app}}", "jobsdetail": "Trabajo {{ro_number}} | {{app}}",
"jobsdocuments": "Documentos de trabajo {{ro_number}} | {{app}}", "jobsdocuments": "Documentos de trabajo {{ro_number}} | {{app}}",
"manageroot": "Casa | {{app}}", "manageroot": "Casa | {{app}}",
"my_tasks": "",
"owners": "Todos los propietarios | {{app}}", "owners": "Todos los propietarios | {{app}}",
"owners-detail": "", "owners-detail": "",
"parts-queue": "", "parts-queue": "",
@@ -3305,6 +3311,7 @@
"shop-csi": "", "shop-csi": "",
"shop-templates": "", "shop-templates": "",
"shop_vendors": "Vendedores | {{app}}", "shop_vendors": "Vendedores | {{app}}",
"tasks": "",
"techconsole": "{{app}}", "techconsole": "{{app}}",
"techjobclock": "{{app}}", "techjobclock": "{{app}}",
"techjoblookup": "{{app}}", "techjoblookup": "{{app}}",

View File

@@ -136,12 +136,12 @@
"jobsupplement": "", "jobsupplement": "",
"jobsuspend": "", "jobsuspend": "",
"jobvoid": "", "jobvoid": "",
"tasks_created": "",
"tasks_updated": "",
"tasks_deleted": "",
"tasks_undeleted": "",
"tasks_completed": "", "tasks_completed": "",
"tasks_uncompleted": "" "tasks_created": "",
"tasks_deleted": "",
"tasks_uncompleted": "",
"tasks_undeleted": "",
"tasks_updated": ""
} }
}, },
"billlines": { "billlines": {
@@ -230,10 +230,12 @@
"markexported": "", "markexported": "",
"markforreexport": "", "markforreexport": "",
"new": "", "new": "",
"nobilllines": "",
"noneselected": "", "noneselected": "",
"onlycmforinvoiced": "", "onlycmforinvoiced": "",
"printlabels": "", "printlabels": "",
"retailtotal": "", "retailtotal": "",
"returnfrombill": "",
"savewithdiscrepancy": "", "savewithdiscrepancy": "",
"state_tax": "", "state_tax": "",
"subtotal": "", "subtotal": "",
@@ -867,7 +869,8 @@
"inservice": "", "inservice": "",
"leasereturn": "", "leasereturn": "",
"out": "", "out": "",
"sold": "" "sold": "",
"unavailable": ""
}, },
"successes": { "successes": {
"saved": "" "saved": ""
@@ -923,7 +926,6 @@
}, },
"titles": { "titles": {
"joblifecycle": "", "joblifecycle": "",
"tasks": "",
"labhours": "", "labhours": "",
"larhours": "", "larhours": "",
"monthlyemployeeefficiency": "", "monthlyemployeeefficiency": "",
@@ -938,7 +940,8 @@
"scheduledindate": "", "scheduledindate": "",
"scheduledintoday": "", "scheduledintoday": "",
"scheduledoutdate": "", "scheduledoutdate": "",
"scheduledouttoday": "" "scheduledouttoday": "",
"tasks": ""
} }
}, },
"dms": { "dms": {
@@ -1133,6 +1136,7 @@
"delete": "Effacer", "delete": "Effacer",
"deleteall": "", "deleteall": "",
"deselectall": "", "deselectall": "",
"download": "",
"edit": "modifier", "edit": "modifier",
"login": "", "login": "",
"print": "", "print": "",
@@ -1358,6 +1362,7 @@
"amount": "", "amount": "",
"dateOfPayment": "", "dateOfPayment": "",
"descriptions": "", "descriptions": "",
"hint": "",
"payer": "", "payer": "",
"payername": "", "payername": "",
"paymentid": "", "paymentid": "",
@@ -1867,7 +1872,6 @@
"appointmentconfirmation": "Envoyer une confirmation au client?", "appointmentconfirmation": "Envoyer une confirmation au client?",
"associationwarning": "", "associationwarning": "",
"audit": "", "audit": "",
"tasks": "",
"available": "", "available": "",
"availablejobs": "", "availablejobs": "",
"ca_bc_pvrt": { "ca_bc_pvrt": {
@@ -2039,7 +2043,8 @@
"enforce_bills": "", "enforce_bills": "",
"enforce_cm": "", "enforce_cm": "",
"enforce_labor": "", "enforce_labor": "",
"enforce_ppd": "","enforce_profit": "", "enforce_ppd": "",
"enforce_profit": "",
"enforce_sublet": "", "enforce_sublet": "",
"enforce_validation": "", "enforce_validation": "",
"enforced": "" "enforced": ""
@@ -2062,6 +2067,7 @@
"supplementnote": "", "supplementnote": "",
"suspended": "", "suspended": "",
"suspense": "", "suspense": "",
"tasks": "",
"threshhold": "", "threshhold": "",
"total_cost": "", "total_cost": "",
"total_cust_payable": "", "total_cust_payable": "",
@@ -2148,107 +2154,18 @@
} }
} }
}, },
"tasks": {
"failures": {
"created": "",
"deleted": "",
"updated": "",
"completed": ""
},
"successes": {
"created": "",
"deleted": "",
"updated": "",
"completed": ""
},
"date_presets": {
"next_week": "",
"two_weeks": "",
"three_weeks": "",
"one_month": "",
"today": "",
"tomorrow": "",
"three_months": "",
"day": "",
"days": "",
"delivery": "",
"completion": ""
},
"actions": {
"new": "",
"edit": ""
},
"titles": {
"all_tasks": "",
"job_tasks": "",
"mine": "",
"my_tasks": "",
"completed": "",
"deleted": ""
},
"buttons": {
"create": "",
"complete": "",
"delete": "",
"edit": "",
"refresh": "",
"myTasks": "",
"allTasks": ""
},
"placeholders": {
"description": "",
"jobid": "",
"joblineid": "",
"partsorderid": "",
"billid": "",
"assigned_to": ""
},
"fields": {
"created_at": "",
"jobline": "",
"parts_order": "",
"bill": "",
"priorities": {
"low": "",
"medium": "",
"high": ""
},
"title": "",
"joblineid": "",
"jobid": "",
"partsorderid": "",
"billid": "",
"description": "",
"due_date": "",
"actions": "",
"priority": "",
"remind_at": "",
"assigned_to": "",
"completed": "",
"job": {
"ro_number": ""
}
},
"validation": {
"due_at_error_message": "",
"remind_at_error_message": ""
}
},
"menus": { "menus": {
"currentuser": { "currentuser": {
"languageselector": "La langue", "languageselector": "La langue",
"profile": "Profil" "profile": "Profil"
}, },
"header": { "header": {
"create_task": "",
"tasks": "",
"my_tasks": "",
"all_tasks": "",
"accounting": "", "accounting": "",
"accounting-payables": "", "accounting-payables": "",
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",
"activejobs": "Emplois actifs", "activejobs": "Emplois actifs",
"all_tasks": "",
"alljobs": "", "alljobs": "",
"allpayments": "", "allpayments": "",
"availablejobs": "Emplois disponibles", "availablejobs": "Emplois disponibles",
@@ -2257,6 +2174,7 @@
"courtesycars-all": "", "courtesycars-all": "",
"courtesycars-contracts": "", "courtesycars-contracts": "",
"courtesycars-newcontract": "", "courtesycars-newcontract": "",
"create_task": "",
"customers": "Les clients", "customers": "Les clients",
"dashboard": "", "dashboard": "",
"enterbills": "", "enterbills": "",
@@ -2269,6 +2187,7 @@
"home": "Accueil", "home": "Accueil",
"inventory": "", "inventory": "",
"jobs": "Emplois", "jobs": "Emplois",
"my_tasks": "",
"newjob": "", "newjob": "",
"owners": "Propriétaires", "owners": "Propriétaires",
"parts-queue": "", "parts-queue": "",
@@ -2295,6 +2214,7 @@
"shop_csi": "", "shop_csi": "",
"shop_templates": "", "shop_templates": "",
"shop_vendors": "Vendeurs", "shop_vendors": "Vendeurs",
"tasks": "",
"temporarydocs": "", "temporarydocs": "",
"timetickets": "", "timetickets": "",
"ttapprovals": "", "ttapprovals": "",
@@ -2488,6 +2408,7 @@
"percent_accepted": "" "percent_accepted": ""
}, },
"labels": { "labels": {
"notyetdispatched": "",
"parts_dispatch": "" "parts_dispatch": ""
} }
}, },
@@ -3104,6 +3025,92 @@
"updated": "" "updated": ""
} }
}, },
"tasks": {
"actions": {
"edit": "",
"new": ""
},
"buttons": {
"allTasks": "",
"complete": "",
"create": "",
"delete": "",
"edit": "",
"myTasks": "",
"refresh": ""
},
"date_presets": {
"completion": "",
"day": "",
"days": "",
"delivery": "",
"next_week": "",
"one_month": "",
"three_months": "",
"three_weeks": "",
"today": "",
"tomorrow": "",
"two_weeks": ""
},
"failures": {
"completed": "",
"created": "",
"deleted": "",
"updated": ""
},
"fields": {
"actions": "",
"assigned_to": "",
"bill": "",
"billid": "",
"completed": "",
"created_at": "",
"description": "",
"due_date": "",
"job": {
"ro_number": ""
},
"jobid": "",
"jobline": "",
"joblineid": "",
"parts_order": "",
"partsorderid": "",
"priorities": {
"high": "",
"low": "",
"medium": ""
},
"priority": "",
"remind_at": "",
"title": ""
},
"placeholders": {
"assigned_to": "",
"billid": "",
"description": "",
"jobid": "",
"joblineid": "",
"partsorderid": ""
},
"successes": {
"completed": "",
"created": "",
"deleted": "",
"updated": ""
},
"titles": {
"all_tasks": "",
"completed": "",
"deleted": "",
"job_tasks": "",
"mine": "",
"my_tasks": ""
},
"validation": {
"due_at_error_message": "",
"remind_at_error_message": ""
}
},
"tech": { "tech": {
"fields": { "fields": {
"employeeid": "", "employeeid": "",
@@ -3208,17 +3215,13 @@
"accounting-payables": "", "accounting-payables": "",
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",
"all_tasks": "",
"app": "", "app": "",
"tasks": "",
"all_tasks": "",
"my_tasks": "",
"bc": { "bc": {
"tasks": "",
"all_tasks": "",
"my_tasks": "",
"accounting-payables": "", "accounting-payables": "",
"accounting-payments": "", "accounting-payments": "",
"accounting-receivables": "", "accounting-receivables": "",
"all_tasks": "",
"availablejobs": "", "availablejobs": "",
"bills-list": "", "bills-list": "",
"contracts": "", "contracts": "",
@@ -3242,6 +3245,7 @@
"jobs-intake": "", "jobs-intake": "",
"jobs-new": "", "jobs-new": "",
"jobs-ready": "", "jobs-ready": "",
"my_tasks": "",
"owner-detail": "", "owner-detail": "",
"owners": "", "owners": "",
"parts-queue": "", "parts-queue": "",
@@ -3256,6 +3260,7 @@
"shop-csi": "", "shop-csi": "",
"shop-templates": "", "shop-templates": "",
"shop-vendors": "", "shop-vendors": "",
"tasks": "",
"temporarydocs": "", "temporarydocs": "",
"timetickets": "", "timetickets": "",
"ttapprovals": "", "ttapprovals": "",
@@ -3286,6 +3291,7 @@
"jobsdetail": "Travail {{ro_number}} | {{app}}", "jobsdetail": "Travail {{ro_number}} | {{app}}",
"jobsdocuments": "Documents de travail {{ro_number}} | {{app}}", "jobsdocuments": "Documents de travail {{ro_number}} | {{app}}",
"manageroot": "Accueil | {{app}}", "manageroot": "Accueil | {{app}}",
"my_tasks": "",
"owners": "Tous les propriétaires | {{app}}", "owners": "Tous les propriétaires | {{app}}",
"owners-detail": "", "owners-detail": "",
"parts-queue": "", "parts-queue": "",
@@ -3305,6 +3311,7 @@
"shop-csi": "", "shop-csi": "",
"shop-templates": "", "shop-templates": "",
"shop_vendors": "Vendeurs | {{app}}", "shop_vendors": "Vendeurs | {{app}}",
"tasks": "",
"techconsole": "{{app}}", "techconsole": "{{app}}",
"techjobclock": "{{app}}", "techjobclock": "{{app}}",
"techjoblookup": "{{app}}", "techjoblookup": "{{app}}",

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "convenient_company" text
null;

View File

@@ -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 = [];

View File

@@ -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!!");
} }

View File

@@ -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,6 +41,7 @@ const tasksEmailQueueCleanup = async () => {
} }
}; };
if (process.env.NODE_ENV !== "development") {
// Handling SIGINT (e.g., Ctrl+C) // Handling SIGINT (e.g., Ctrl+C)
process.on("SIGINT", async () => { process.on("SIGINT", async () => {
await tasksEmailQueueCleanup(); await tasksEmailQueueCleanup();
@@ -61,18 +62,7 @@ process.on("unhandledRejection", async (reason, promise) => {
await tasksEmailQueueCleanup(); await tasksEmailQueueCleanup();
process.exit(1); // Exit with an 'error' code 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(
"remind",
emailData.to,
emailData.subject,
emailData.html,
taskIds,
(taskIds) => {
for (const taskId of taskIds) { for (const taskId of taskIds) {
tasksEmailQueue.push(taskId); tasksEmailQueue.push(taskId);
} }
}); },
allTasks[0].bodyshop.convenient_company
);
} }
}); });

View File

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

View File

@@ -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,24 +145,29 @@ 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
try {
if (values.invoice) {
//This is a link email that's been sent out.
const job = await gqlClient.request(queries.GET_JOB_BY_PK, { const job = await gqlClient.request(queries.GET_JOB_BY_PK, {
id: values.invoice id: values.invoice
}); });
// TODO add mutation to database
try {
const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, { const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, {
paymentInput: { paymentInput: {
amount: values.total, amount: values.total,
transactionid: `C00 ${values.authcode}`, transactionid: values.authcode,
payer: "Customer", payer: "Customer",
type: values.cardtype, type: values.cardtype,
jobid: values.invoice, jobid: values.invoice,
@@ -170,7 +175,7 @@ exports.postback = async (req, res) => {
} }
}); });
await gqlClient.request(queries.INSERT_PAYMENT_RESPONSE, { const responseResults = await gqlClient.request(queries.INSERT_PAYMENT_RESPONSE, {
paymentResponse: { paymentResponse: {
amount: values.total, amount: values.total,
bodyshopid: job.jobs_by_pk.shopid, bodyshopid: job.jobs_by_pk.shopid,
@@ -183,7 +188,46 @@ exports.postback = async (req, res) => {
} }
}); });
res.send({ message: "Postback Successful" }); 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),

View File

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

View File

@@ -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(),