Compare commits

...

29 Commits

Author SHA1 Message Date
Patrick Fic
e63f8c7c45 IO-2143 Add truncation for long vehicle notes. 2023-01-20 09:20:07 -08:00
Patrick Fic
968816b4a6 IO-2134 Add option to choose CSR on job conversion. 2023-01-06 10:12:07 -08:00
Patrick Fic
9de076f060 IO-2132 Updated approach to ATS summary. 2023-01-06 09:56:23 -08:00
Patrick Fic
08d334e93a Resolve rounding for QBO Receivables Multi Payer. 2023-01-05 17:06:47 -08:00
Patrick Fic
af009a0bb3 Updates to Sentry & removal of Stripe. 2023-01-03 12:23:25 -08:00
Patrick Fic
f8e74d9bad IO-2089 Incorporate Lbr Adjustments into scoreboard. 2023-01-02 14:48:10 -08:00
Patrick Fic
f6bf1ce793 IO-2109 Resolve label printing validation issue. 2023-01-02 14:15:59 -08:00
Patrick Fic
9413bc60cf IO-2134 Add CSR to conversion. 2023-01-02 13:56:35 -08:00
Patrick Fic
34f5fad365 IO-2132 Add Weekly ATS Summary 2023-01-02 11:46:23 -08:00
Patrick Fic
d69ce2d2a9 Merged in release/2022-12-30 (pull request #649)
Release/2022 12 30
2022-12-30 19:30:25 +00:00
Patrick Fic
b0755a0cde IO-2133 Manual Line Highlighting 2022-12-27 13:32:24 -08:00
Patrick Fic
a551258895 IO-2129 Add reports to report center. 2022-12-26 10:33:47 -08:00
Patrick Fic
8a0281fb43 Merged in release/2022-12-16 (pull request #643)
Release/2022 12 16
2022-12-16 18:59:41 +00:00
Patrick Fic
1df023fd15 Remove unneeded import to fix CI. 2022-12-15 17:09:05 -08:00
Patrick Fic
54277e4548 IO-2131 Resolve job search select filtering issue. 2022-12-15 16:39:47 -08:00
Patrick Fic
cffa1e2172 IO-2121 Add custom label to conversation. 2022-12-15 10:13:30 -08:00
Patrick Fic
6a25dff32d Merged in release/2022-12-16 (pull request #639)
Resolve scheduling loading issue.
2022-12-14 16:59:04 +00:00
Patrick Fic
ecfc365926 Resolve scheduling loading issue. 2022-12-14 08:30:16 -08:00
Patrick Fic
2f0bb4539e Merged in release/2022-12-09 (pull request #638)
Release/2022 12 09
2022-12-10 01:32:06 +00:00
Patrick Fic
2ac7f9b678 Add ownr_co_nm to search own query. 2022-12-09 14:07:27 -08:00
Patrick Fic
e192a63575 IO-2123 Add cc inventory print. 2022-12-09 11:35:13 -08:00
Patrick Fic
4c42522f3a IO-2122 Add time ticket report. 2022-12-09 08:09:53 -08:00
Patrick Fic
dad3dc9e42 Autohouse based updates. 2022-12-08 14:47:51 -08:00
Patrick Fic
b2b754bee0 IO-2118 Add expected production hours to scheduling. 2022-12-05 15:20:37 -08:00
Patrick Fic
8f9f80e8ee IO-2084 Add actualr epair start date. 2022-12-05 14:38:36 -08:00
Patrick Fic
0cf677abbf Merged in release/2022-12-02 (pull request #635)
IO-2116 Add account number to control types for DMS.
2022-12-03 03:16:53 +00:00
Patrick Fic
bc3238aba2 IO-2116 Add account number to control types for DMS. 2022-11-30 08:47:20 -08:00
Patrick Fic
df8c94cda2 Merged in release/2022-11-25 (pull request #632)
IO-2114 Resolve issue with unfound payer.
2022-11-26 00:01:25 +00:00
Patrick Fic
e1dc257279 Merged in release/2022-11-25 (pull request #631)
Release/2022 11 25
2022-11-25 20:57:01 +00:00
59 changed files with 1085 additions and 497 deletions

View File

@@ -17,5 +17,4 @@
"no-console": "off"
},
"settings": {}
//"plugins": ["cypress"]
}

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.7.1">
<babeledit_project be_version="2.7.1" version="1.2">
<!--
BabelEdit project file
@@ -736,6 +736,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>expectedprodhrs</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>history</name>
<definition_loaded>false</definition_loaded>
@@ -4402,6 +4423,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>sendmaterialscosting</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>srcco</name>
<definition_loaded>false</definition_loaded>
@@ -4467,6 +4509,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>enforce_conversion_csr</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>enforce_referral</name>
<definition_loaded>false</definition_loaded>
@@ -22264,6 +22327,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>date_repairstarted</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>date_scheduled</name>
<definition_loaded>false</definition_loaded>
@@ -22456,6 +22540,32 @@
</translation>
</translations>
</concept_node>
<folder_node>
<name>control_type</name>
<children>
<concept_node>
<name>account_number</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<concept_node>
<name>cost</name>
<definition_loaded>false</definition_loaded>
@@ -32581,11 +32691,53 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>updatinglabel</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>addlabel</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>archive</name>
<definition_loaded>false</definition_loaded>
@@ -36430,6 +36582,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>courtesy_car_inventory</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>courtesy_car_terms</name>
<definition_loaded>false</definition_loaded>
@@ -38088,6 +38261,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>timetickets_ro</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>vehicle_check_in</name>
<definition_loaded>false</definition_loaded>
@@ -41904,6 +42098,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>returns_grouped_by_vendor_detailed</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>returns_grouped_by_vendor_summary</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>schedule</name>
<definition_loaded>false</definition_loaded>
@@ -42166,6 +42402,27 @@
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>atssummary</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>employeevacation</name>
<definition_loaded>false</definition_loaded>

View File

@@ -9,11 +9,9 @@
"@craco/craco": "^6.4.5",
"@fingerprintjs/fingerprintjs": "^3.3.3",
"@jsreport/browser-client": "^3.1.0",
"@sentry/react": "^7.7.0",
"@sentry/tracing": "^7.7.0",
"@sentry/react": "^7.28.1",
"@sentry/tracing": "^7.28.1",
"@splitsoftware/splitio-react": "^1.6.0",
"@stripe/react-stripe-js": "^1.9.0",
"@stripe/stripe-js": "^1.32.0",
"@tanem/react-nprogress": "^5.0.8",
"antd": "^4.22.3",
"apollo-link-logger": "^2.0.0",
@@ -119,7 +117,7 @@
"react-error-overlay": "6.0.9"
},
"devDependencies": {
"@sentry/webpack-plugin": "^1.19.0",
"@sentry/webpack-plugin": "^1.20.0",
"@testing-library/cypress": "^8.0.3",
"cypress": "^10.3.1",
"eslint-plugin-cypress": "^2.12.1",

View File

@@ -147,4 +147,9 @@
//Update row highlighting on production board.
.ant-table-tbody > tr.ant-table-row:hover > td {
background: #eaeaea !important;
}
}
.job-line-manual{
color: tomato;
font-style: italic;
}

View File

@@ -1,8 +1,4 @@
import {
PaymentRequestButtonElement,
useStripe,
} from "@stripe/react-stripe-js";
import React, { useEffect, useState } from "react";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
@@ -19,49 +15,6 @@ const mapDispatchToProps = (dispatch) => ({
});
function Test({ bodyshop, setEmailOptions }) {
const stripe = useStripe();
const [paymentRequest, setPaymentRequest] = useState(null);
useEffect(() => {
if (stripe) {
const pr = stripe.paymentRequest({
country: "CA",
displayItems: [{ label: "Deductible", amount: 1099 }],
currency: "cad",
total: {
label: "Demo total",
amount: 1099,
},
requestPayerName: true,
requestPayerEmail: true,
});
// Check the availability of the Payment Request API.
pr.canMakePayment().then((result) => {
if (result) {
setPaymentRequest(pr);
} else {
// var details = {
// total: { label: "", amount: { currency: "CAD", value: "0.00" } },
// };
new PaymentRequest(
[{ supportedMethods: ["basic-card"] }],
{}
// details
).show();
}
});
}
}, [stripe]);
if (paymentRequest) {
return (
<div style={{ height: "300px" }}>
<PaymentRequestButtonElement options={{ paymentRequest }} />
</div>
);
}
return (
<div>
<button

View File

@@ -107,11 +107,6 @@ export function AccountingPayablesTableComponent({
dataIndex: "transactionid",
key: "transactionid",
},
{
title: t("payments.fields.stripeid"),
dataIndex: "stripeid",
key: "stripeid",
},
{
title: t("payments.fields.created_at"),
dataIndex: "created_at",

View File

@@ -6,8 +6,8 @@ import { setSelectedConversation } from "../../redux/messaging/messaging.actions
import { selectSelectedConversation } from "../../redux/messaging/messaging.selectors";
import { TimeAgoFormatter } from "../../utils/DateFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import "./chat-conversation-list.styles.scss";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import "./chat-conversation-list.styles.scss";
const mapStateToProps = createStructuredSelector({
selectedConversation: selectSelectedConversation,
@@ -38,17 +38,20 @@ export function ChatConversationListComponent({
: null
}`}
>
{item.job_conversations.length > 0 ? (
<div className="chat-name">
{item.job_conversations.map((j, idx) => (
<div key={idx}>
<OwnerNameDisplay ownerObject={j.job} />
</div>
))}
</div>
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
<div sryle={{ display: "inline-block" }}>
{item.label && <div className="chat-name">{item.label}</div>}
{item.job_conversations.length > 0 ? (
<div className="chat-name">
{item.job_conversations.map((j, idx) => (
<div key={idx}>
<OwnerNameDisplay ownerObject={j.job} />
</div>
))}
</div>
) : (
<PhoneFormatter>{item.phone_num}</PhoneFormatter>
)}
</div>
<div sryle={{ display: "inline-block" }}>
<div>
{item.job_conversations.length > 0

View File

@@ -3,6 +3,7 @@ import React from "react";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
import ChatArchiveButton from "../chat-archive-button/chat-archive-button.component";
import ChatConversationTitleTags from "../chat-conversation-title-tags/chat-conversation-title-tags.component";
import ChatLabelComponent from "../chat-label/chat-label.component";
import ChatTagRoContainer from "../chat-tag-ro/chat-tag-ro.container";
export default function ChatConversationTitle({ conversation }) {
@@ -11,6 +12,7 @@ export default function ChatConversationTitle({ conversation }) {
<PhoneNumberFormatter>
{conversation && conversation.phone_num}
</PhoneNumberFormatter>
<ChatLabelComponent conversation={conversation} />
<ChatConversationTitleTags
jobConversations={
(conversation && conversation.job_conversations) || []

View File

@@ -0,0 +1,67 @@
import { PlusOutlined } from "@ant-design/icons";
import { useMutation } from "@apollo/client";
import { Input, notification, Spin, Tag, Tooltip } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { UPDATE_CONVERSATION_LABEL } from "../../graphql/conversations.queries";
export default function ChatLabel({ conversation }) {
const [loading, setLoading] = useState(false);
const [editing, setEditing] = useState(false);
const [value, setValue] = useState(conversation.label);
const { t } = useTranslation();
const [updateLabel] = useMutation(UPDATE_CONVERSATION_LABEL);
const handleSave = async () => {
setLoading(true);
try {
const response = await updateLabel({
variables: { id: conversation.id, label: value },
});
if (response.errors) {
notification["error"]({
message: t("messages.errors.updatinglabel", {
error: JSON.stringify(response.errors),
}),
});
} else {
setEditing(false);
}
} catch (error) {
notification["error"]({
message: t("messages.errors.updatinglabel", {
error: JSON.stringify(error),
}),
});
} finally {
setLoading(false);
}
};
if (editing) {
return (
<div>
<Input
autoFocus
value={value}
onChange={(e) => setValue(e.target.value)}
onBlur={handleSave}
allowClear
/>
{loading && <Spin size="small" />}
</div>
);
} else {
return conversation.label && conversation.label.trim() !== "" ? (
<Tag style={{ cursor: "pointer" }} onClick={() => setEditing(true)}>
{conversation.label}
</Tag>
) : (
<Tooltip title={t("messaging.labels.addlabel")}>
<PlusOutlined
style={{ cursor: "pointer" }}
onClick={() => setEditing(true)}
/>
</Tooltip>
);
}
}

View File

@@ -1,5 +1,14 @@
import { SyncOutlined, WarningFilled } from "@ant-design/icons";
import { Button, Card, Input, Space, Table, Tooltip } from "antd";
import {
Button,
Card,
Dropdown,
Input,
Menu,
Space,
Table,
Tooltip,
} from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
@@ -7,6 +16,8 @@ import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
import moment from "moment";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
const [state, setState] = useState({
sortedInfo: {},
@@ -166,6 +177,32 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Dropdown
trigger="click"
overlay={
<Menu>
<Menu.Item
onClick={() =>
GenerateDocument(
{
name: TemplateList("courtesycar").courtesy_car_inventory
.key,
variables: {
//id: contract.id
},
},
{},
"p"
)
}
>
{t("printcenter.courtesycarcontract.courtesy_car_inventory")}
</Menu.Item>
</Menu>
}
>
<Button>{t("general.labels.print")}</Button>
</Dropdown>
<Link to={`/manage/courtesycars/new`}>
<Button>{t("courtesycars.actions.new")}</Button>
</Link>

View File

@@ -8,6 +8,8 @@ export default function DataLabel({
vertical,
visible = true,
valueStyle = {},
valueClassName,
onValueClick,
...props
}) {
if (!visible || (hideIfNull && !!!children)) return null;
@@ -28,7 +30,10 @@ export default function DataLabel({
marginLeft: ".3rem",
fontWeight: "bolder",
wordWrap: "break-word",
cursor: onValueClick !== undefined ? "pointer" : "",
}}
className={valueClassName}
onClick={onValueClick}
>
{typeof children === "string" ? (
<Typography.Text style={valueStyle}>{children}</Typography.Text>

View File

@@ -345,7 +345,20 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
t(`jobs.fields.${cdkPayer?.control_type}`)}
</div>
);
else {
else if (
i18n.exists(
`jobs.fields.dms.control_type.${cdkPayer?.control_type}`
)
) {
return (
<div>
{cdkPayer &&
t(
`jobs.fields.dms.control_type.${cdkPayer?.control_type}`
)}
</div>
);
} else {
return null;
}
}}

View File

@@ -59,14 +59,18 @@ export default function JobDetailCardsDatesComponent({ loading, data }) {
<DateTimeFormatter>{data.scheduled_in}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.actual_in ? (
<Timeline.Item>
<label>{t("jobs.fields.actual_in")}: </label>
<DateTimeFormatter>{data.actual_in}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.date_repairstarted ? (
<Timeline.Item>
<label>{t("jobs.fields.date_repairstarted")}: </label>
<DateTimeFormatter>{data.date_repairstarted}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.scheduled_completion ? (
<Timeline.Item>
<label>{t("jobs.fields.scheduled_completion")}: </label>

View File

@@ -103,6 +103,7 @@ export function JobLinesComponent({
fixed: "left",
key: "line_desc",
sorter: (a, b) => alphaSort(a.line_desc, b.line_desc),
onCell: (record) => ({ className: record.manual_line && "job-line-manual" }),
sortOrder:
state.sortedInfo.columnKey === "line_desc" && state.sortedInfo.order,
ellipsis: true,

View File

@@ -25,8 +25,6 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setModalContext({ context: context, modal: "payment" })),
});
const stripeTestEnv = process.env.REACT_APP_STRIPE_PUBLIC_KEY; //.includes("test");
export function JobPayments({
job,
jobRO,
@@ -94,23 +92,6 @@ export function JobPayments({
state.sortedInfo.columnKey === "transactionid" &&
state.sortedInfo.order,
},
{
title: t("payments.fields.stripeid"),
dataIndex: "stripeid",
key: "stripeid",
render: (text, record) =>
record.stripeid ? (
<a
href={
stripeTestEnv
? `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/test/payments/${record.stripeid}`
: `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/payments/${record.stripeid}`
}
>
{record.stripeid}
</a>
) : null,
},
{
title: t("general.labels.actions"),
dataIndex: "actions",

View File

@@ -166,6 +166,16 @@ export default function ScoreboardAddButton({
painthrs: 0,
}
);
//Add Labor Adjustments
v.painthrs = v.painthrs + (job.lbr_adjustments.LAR || 0);
v.bodyhrs =
v.bodyhrs +
Object.keys(job.lbr_adjustments)
.filter((key) => key !== "LAR")
.reduce((acc, val) => {
return acc + job.lbr_adjustments[val];
}, 0);
form.setFieldsValue({
date: new moment(),
bodyhrs: Math.round(v.bodyhrs * 10) / 10,

View File

@@ -43,11 +43,9 @@ const JobSearchSelect = (
search: value,
...(convertedOnly || notExported
? {
variables: {
...(convertedOnly ? { isConverted: true } : {}),
...(notExported ? { notExported: true } : {}),
...(notInvoiced ? { notInvoiced: true } : {}),
},
...(convertedOnly ? { isConverted: true } : {}),
...(notExported ? { notExported: true } : {}),
...(notInvoiced ? { notInvoiced: true } : {}),
}
: {}),
},
@@ -79,6 +77,7 @@ const JobSearchSelect = (
disabled={disabled}
showSearch
autoFocus
allowClear
style={{
width: "100%",
}}

View File

@@ -126,6 +126,9 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
<Form.Item label={t("jobs.fields.actual_in")} name="actual_in">
<DateTimePicker />
</Form.Item>
<Form.Item label={t("jobs.fields.date_repairstarted")} name="date_repairstarted">
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.date_last_contacted")}
name="date_last_contacted"

View File

@@ -9,6 +9,7 @@ import {
Space,
Switch,
} from "antd";
import axios from "axios";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -18,7 +19,6 @@ import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import axios from "axios";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
@@ -83,7 +83,11 @@ export function JobsConvertButton({
layout="vertical"
form={form}
onFinish={handleConvert}
initialValues={{ driveable: true, towin: false }}
initialValues={{
driveable: true,
towin: false,
employee_csr: job.employee_csr,
}}
>
<Form.Item
name={["ins_co_nm"]}
@@ -151,6 +155,41 @@ export function JobsConvertButton({
</Form.Item>
</>
)}
{bodyshop.enforce_conversion_csr && (
<Form.Item
name={"employee_csr"}
label={t("jobs.fields.employee_csr")}
rules={[
{
required: bodyshop.enforce_conversion_csr,
//message: t("general.validation.required"),
},
]}
>
<Select
showSearch
style={{ width: 200 }}
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children
.toLowerCase()
.indexOf(input.toLowerCase()) >= 0
}
>
{bodyshop.employees
.filter((emp) => emp.active)
.map((emp) => (
<Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
</Select>
</Form.Item>
)}
<Form.Item
label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant"
@@ -194,7 +233,14 @@ export function JobsConvertButton({
// style={{ display: job.converted ? "none" : "" }}
disabled={job.converted || jobRO}
loading={loading}
onClick={() => setVisible(true)}
onClick={() => {
setVisible(true);
form.setFieldsValue({
driveable: true,
towin: false,
employee_csr: job.employee_csr,
});
}}
>
{t("jobs.actions.convert")}
</Button>

View File

@@ -42,7 +42,10 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
<Form.Item label={t("jobs.fields.date_towin")} name="date_towin">
<DateTimePicker disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.date_rentalresp")} name="date_rentalresp">
<Form.Item
label={t("jobs.fields.date_rentalresp")}
name="date_rentalresp"
>
<DateTimePicker disabled={jobRO} />
</Form.Item>
</FormRow>
@@ -73,6 +76,12 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
title={t("jobs.labels.calc_repair_days")}
/>
</Tooltip>
<Form.Item
label={t("jobs.fields.date_repairstarted")}
name="date_repairstarted"
>
<DateTimePicker disabled={jobRO} />
</Form.Item>
</FormRow>
<FormRow header={t("jobs.forms.repairdates")}>
<Form.Item

View File

@@ -5,7 +5,7 @@ import {
BranchesOutlined,
} from "@ant-design/icons";
import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
import React from "react";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
@@ -56,7 +56,7 @@ const colSpan = {
export function JobsDetailHeader({ job, bodyshop, disabled }) {
const { t } = useTranslation();
const [notesClamped, setNotesClamped] = useState(true);
const vehicleTitle = `${job.v_model_yr || ""} ${job.v_color || ""}
${job.v_make_desc || ""}
${job.v_model_desc || ""}`.trim();
@@ -229,6 +229,8 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<DataLabel
label={t("vehicles.fields.notes")}
valueStyle={{ whiteSpace: "pre-wrap" }}
valueClassName={notesClamped ? "clamp" : ""}
onValueClick={() => setNotesClamped(!notesClamped)}
>
{job.vehicle.notes}
</DataLabel>

View File

@@ -6,3 +6,12 @@
display: flex;
flex-wrap: wrap;
}
.clamp {
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
overflow-wrap: break-word;
}

View File

@@ -159,7 +159,7 @@ export function PayableExportAll({
} else {
notification["error"]({
message: t("bills.errors.exporting", {
error: JSON.stringify(billUpdateResponse.error),
error: JSON.stringify(billUpdateResponse.errors),
}),
});
}

View File

@@ -1,12 +1,10 @@
import { useTreatments } from "@splitsoftware/splitio-react";
import { CardElement } from "@stripe/react-stripe-js";
import { Checkbox, Form, Input, Radio, Select } from "antd";
import { Form, Input, Radio, Select } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import Alert from "../alert/alert.component";
import DatePickerFormItem from "../form-date-picker/form-date-picker.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobSearchSelect from "../job-search-select/job-search-select.component";
@@ -19,11 +17,9 @@ const mapStateToProps = createStructuredSelector({
export function PaymentFormComponent({
form,
stripeStateArr,
bodyshop,
disabled,
}) {
const [stripeState, setStripeState] = stripeStateArr;
const { Qb_Multi_Ar } = useTreatments(
["Qb_Multi_Ar"],
{},
@@ -31,9 +27,6 @@ export function PaymentFormComponent({
);
const { t } = useTranslation();
const handleStripeChange = (e) => {
setStripeState({ error: e.error, cardComplete: e.complete });
};
return (
<div>
@@ -150,57 +143,6 @@ export function PaymentFormComponent({
</Form.Item>
</LayoutFormRow>
<LayoutFormRow grow>
<div>
<Form.Item
label={t("payments.labels.electronicpayment")}
name="useStripe"
valuePropName="checked"
>
<Checkbox
defaultChecked={!!bodyshop.stripe_acct_id}
disabled={!!!bodyshop.stripe_acct_id || disabled}
/>
</Form.Item>
{!bodyshop.stripe_acct_id ? (
<div style={{ fontStyle: "italic" }}>
{t("payments.labels.signup")}
</div>
) : null}
<Form.Item shouldUpdate>
{() => {
if (form.getFieldValue("useStripe"))
return (
<CardElement
options={{
style: {
base: {
color: "#32325d",
//fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: "antialiased",
//fontSize: "16px",
"::placeholder": {
color: "#aab7c4",
},
},
invalid: {
color: "#fa755a",
iconColor: "#fa755a",
},
},
}}
onChange={handleStripeChange}
/>
);
return null;
}}
</Form.Item>
{stripeState.error ? (
<Alert type="error" message={stripeState.error.message} />
) : null}
</div>
<Form.Item
label={t("general.labels.sendby")}
name="sendby"

View File

@@ -1,23 +1,20 @@
import { useApolloClient, useMutation } from "@apollo/client";
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
import { useMutation } from "@apollo/client";
import { Button, Form, Modal, notification } from "antd";
import axios from "axios";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { GET_JOB_INFO_FOR_STRIPE } from "../../graphql/jobs.queries";
import {
INSERT_NEW_PAYMENT,
UPDATE_PAYMENT,
UPDATE_PAYMENT
} from "../../graphql/payments.queries";
import { setEmailOptions } from "../../redux/email/email.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectPayment } from "../../redux/modals/modals.selectors";
import {
selectBodyshop,
selectCurrentUser,
selectCurrentUser
} from "../../redux/user/user.selectors";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
@@ -45,82 +42,23 @@ function PaymentModalContainer({
const [enterAgain, setEnterAgain] = useState(false);
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
const [updatePayment] = useMutation(UPDATE_PAYMENT);
const client = useApolloClient();
const stripe = useStripe();
const elements = useElements();
const { t } = useTranslation();
const { context, actions, visible } = paymentModal;
const [loading, setLoading] = useState(false);
const stripeStateArr = useState({
error: null,
cardComplete: false,
});
const stripeState = stripeStateArr[0];
const cardValid = !!!stripeState.error && stripeState.cardComplete;
const handleFinish = async (values) => {
const { useStripe, sendby, ...paymentObj } = values;
if (useStripe && !cardValid) return;
if ((useStripe && !stripe) || !elements) {
// Stripe.js has not yet loaded.
// Make sure to disable form submission until Stripe.js has loaded.
return;
}
setLoading(true);
try {
let stripePayment;
if (useStripe && bodyshop.stripe_acct_id) {
logImEXEvent("payment_stripe_attempt");
const secretKey = await axios.post("/stripe/payment", {
amount: Math.round(values.amount * 100),
stripe_acct_id: bodyshop.stripe_acct_id,
});
const { data } = await client.query({
query: GET_JOB_INFO_FOR_STRIPE,
variables: { jobid: values.jobid },
});
stripePayment = await stripe.confirmCardPayment(
secretKey.data.clientSecret,
{
payment_method: {
card: elements.getElement(CardElement),
billing_details: {
name: `${data.jobs_by_pk.ownr_fn || ""} ${
data.jobs_by_pk.ownr_ln || ""
} ${data.jobs_by_pk.ownr_co_nm || ""}`,
email: data.jobs_by_pk.ownr_ea,
phone: data.jobs_by_pk.ownr_ph1,
},
},
}
);
if (stripePayment.paymentIntent.status === "succeeded") {
notification["success"]({ message: t("payments.successes.stripe") });
} else {
notification["error"]({ message: t("payments.errors.stripe") });
throw new Error();
}
}
logImEXEvent("payment_insert");
if (!context || (context && !context.id)) {
const newPayment = await insertPayment({
variables: {
paymentInput: {
...paymentObj,
stripeid:
stripePayment &&
stripePayment.paymentIntent &&
stripePayment.paymentIntent.id,
},
},
});
@@ -236,11 +174,7 @@ function PaymentModalContainer({
layout="vertical"
initialValues={context || {}}
>
<PaymentForm
form={form}
stripeStateArr={stripeStateArr}
disabled={context && context.stripeid}
/>
<PaymentForm form={form} />
</Form>
</Modal>
);
@@ -250,15 +184,3 @@ export default connect(
mapStateToProps,
mapDispatchToProps
)(PaymentModalContainer);
// const pr = stripe.paymentRequest({
// country: "CA",
// currency: "CAD",
// total: {
// label: "Demo total",
// amount: 1099,
// },
// requestPayerName: true,
// requestPayerEmail: true,
// });
// console.log("handleFinish -> pr", pr);

View File

@@ -16,8 +16,6 @@ import CaBcEtfTableModalContainer from "../ca-bc-etf-table-modal/ca-bc-etf-table
import PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
const stripeTestEnv = process.env.REACT_APP_STRIPE_PUBLIC_KEY; //.includes("test");
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
bodyshop: selectBodyshop,
@@ -125,23 +123,6 @@ export function PaymentsListPaginated({
dataIndex: "transactionid",
key: "transactionid",
},
{
title: t("payments.fields.stripeid"),
dataIndex: "stripeid",
key: "stripeid",
render: (text, record) =>
record.stripeid ? (
<a
href={
stripeTestEnv
? `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/test/payments/${record.stripeid}`
: `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/payments/${record.stripeid}`
}
>
{record.stripeid}
</a>
) : null,
},
{
title: t("payments.fields.created_at"),
dataIndex: "created_at",

View File

@@ -1,4 +1,12 @@
import { Button, Card, Form, InputNumber, Popover, Radio } from "antd";
import {
Button,
Card,
Form,
InputNumber,
notification,
Popover,
Radio,
} from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -27,7 +35,6 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
const handleOk = (e) => {
e.stopPropagation();
form.submit();
setIsModalVisible(false);
};
const handleCancel = () => {
@@ -37,18 +44,24 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
const handleFinish = async ({ template, ...values }) => {
const { sendtype, ...restVals } = values;
setLoading(true);
await GenerateDocument(
{
name: TemplateList("job_special")[template].key,
variables: { id: jobId },
context: restVals,
},
{},
"p",
jobId
);
setLoading(false);
setIsModalVisible(false);
try {
await GenerateDocument(
{
name: TemplateList("job_special")[template].key,
variables: { id: jobId },
context: restVals,
},
{},
"p",
jobId
);
setIsModalVisible(false);
} catch (error) {
notification.open({ type: "error", message: JSON.stringify(error) });
} finally {
setLoading(false);
}
form.resetFields();
};
@@ -60,7 +73,15 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
layout="vertical"
form={form}
>
<Form.Item required name="template">
<Form.Item
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name="template"
>
<Radio.Group>
<Radio.Button value="parts_label_multiple">
{t("printcenter.jobs.parts_label_multiple")}
@@ -71,14 +92,24 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
</Radio.Group>
</Form.Item>
<Form.Item
required
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
label={t("printcenter.jobs.labels.position")}
name="position"
>
<InputNumber min={1} precision={0} />
</Form.Item>
<Form.Item
required
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
label={t("printcenter.jobs.labels.count")}
name="count"
>

View File

@@ -534,6 +534,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
<JobPartsQueueCount parts={record.joblines_status} record={record} />
),
},
//Added as a place holder for St Claude. Not implemented as it requires another join for a field used by only 1 client.
// {
// title: i18n.t("vehicles.fields.v_paint_codes", { number: "" }),
@@ -555,6 +556,29 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
// </span>
// ) : null,
// },
{
title: i18n.t("jobs.fields.date_repairstarted"),
dataIndex: "date_repairstarted",
key: "date_repairstarted",
ellipsis: true,
sorter: (a, b) => dateSort(a.date_repairstarted, b.date_repairstarted),
sortOrder:
state.sortedInfo.columnKey === "date_repairstarted" &&
state.sortedInfo.order,
render: (text, record) => (
<ProductionListDate record={record} field="date_repairstarted" time />
),
},
{
title: i18n.t("jobs.fields.date_repairstarted") + " (HH:MM)",
dataIndex: "date_repairstarted_time",
key: "date_repairstarted_time",
ellipsis: true,
render: (text, record) => (
<TimeFormatter>{record.date_repairstarted}</TimeFormatter>
),
},
];
};
export default r;

View File

@@ -0,0 +1,48 @@
import { Space } from "antd";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectScheduleLoad } from "../../redux/application/application.selectors";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
scheduleLoad: selectScheduleLoad,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
});
export function ScheduleAtsSummary({ scheduleLoad, appointments }) {
const { t } = useTranslation();
const atsSummary = useMemo(() => {
let atsSummary = {};
if (!appointments || appointments.length === 0) {
return {};
}
appointments
.filter((a) => a.isintake)
.forEach((a) => {
if (!a.job.alt_transport) return;
if (!atsSummary[a.job.alt_transport]) {
atsSummary[a.job.alt_transport] = 1;
} else {
atsSummary[a.job.alt_transport] = atsSummary[a.job.alt_transport] + 1;
}
});
return atsSummary;
}, [appointments]);
if (Object.keys(atsSummary).length > 0)
return (
<Space wrap>
{t("schedule.labels.atssummary")}
{Object.keys(atsSummary).map((key) => (
<span key={key}>{`${key}: ${atsSummary[key]}`}</span>
))}
</Space>
);
return null;
}
export default connect(mapStateToProps, mapDispatchToProps)(ScheduleAtsSummary);

View File

@@ -1,19 +1,17 @@
import { Popover } from "antd";
import { RadarChartOutlined } from "@ant-design/icons";
import { Popover, Space } from "antd";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import {
PolarAngleAxis,
Legend, PolarAngleAxis,
PolarGrid,
PolarRadiusAxis,
Radar,
Legend,
RadarChart,
Tooltip,
Radar, RadarChart,
Tooltip
} from "recharts";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { RadarChartOutlined } from "@ant-design/icons";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
@@ -23,7 +21,7 @@ const mapDispatchToProps = (dispatch) => ({
export function ScheduleCalendarHeaderGraph({ bodyshop, loadData }) {
const { ssbuckets } = bodyshop;
const { t } = useTranslation();
const data = useMemo(() => {
return (
(loadData &&
@@ -43,6 +41,10 @@ export function ScheduleCalendarHeaderGraph({ bodyshop, loadData }) {
const popContent = (
<div>
<Space>
{t("appointments.labels.expectedprodhrs")}
<strong>{loadData?.expectedHours?.toFixed(1)}</strong>
</Space>
<RadarChart
// cx={300}
// cy={250}

View File

@@ -3,6 +3,7 @@ import { Button, Card, Checkbox, Col, PageHeader, Row, Space } from "antd";
import { t } from "i18next";
import React, { useMemo } from "react";
import useLocalStorage from "../../utils/useLocalStorage";
import ScheduleAtsSummary from "../schedule-ats-summary/schedule-ats-summary.component";
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
import ScheduleModal from "../schedule-job-modal/schedule-job-modal.container";
import ScheduleManualEvent from "../schedule-manual-event/schedule-manual-event.component";
@@ -35,6 +36,7 @@ export default function ScheduleCalendarComponent({ data, refetch }) {
<PageHeader
extra={
<Space wrap>
<ScheduleAtsSummary appointments={filteredData} />
<Checkbox
checked={filter?.intake}
onChange={(e) => {

View File

@@ -466,6 +466,13 @@ export default function ShopInfoGeneral({ form }) {
>
<Switch />
</Form.Item>
<Form.Item
name={["enforce_conversion_csr"]}
label={t("bodyshop.fields.enforce_conversion_csr")}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
name={["target_touchtime"]}
label={t("bodyshop.fields.target_touchtime")}

View File

@@ -139,6 +139,12 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.dms.sendmaterialscosting")}
name={["cdk_configuration", "sendmaterialscosting"]}
>
<InputNumber min={0} max={100} />
</Form.Item>
{bodyshop.pbs_serialnumber && (
<Form.Item
label={t("bodyshop.fields.dms.disablecontactvehiclecreation")}
@@ -200,7 +206,7 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
// },
// ]}
>
<Select showSearch>
<Select allowClear showSearch>
<Select.Option value="ro_number">
{t("jobs.fields.ro_number")}
</Select.Option>
@@ -210,6 +216,9 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
<Select.Option value="po_number">
{t("jobs.fields.ponumber")}
</Select.Option>
<Select.Option value="account_number">
{t("jobs.fields.dms.control_type.account_number")}
</Select.Option>
</Select>
</Form.Item>

View File

@@ -364,6 +364,7 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
ownr_fn
ownr_ln
ownr_co_nm
alt_transport
labhrs: joblines_aggregate(
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
) {

View File

@@ -111,6 +111,7 @@ export const QUERY_BODYSHOP = gql`
localmediaserverhttp
localmediaservernetwork
localmediatoken
enforce_conversion_csr
employees {
user_email
id
@@ -220,6 +221,7 @@ export const UPDATE_SHOP = gql`
localmediaserverhttp
localmediaservernetwork
localmediatoken
enforce_conversion_csr
employees {
id
first_name

View File

@@ -43,6 +43,7 @@ export const CONVERSATION_LIST_QUERY = gql`
updated_at
unreadcnt
archived
label
messages_aggregate(
where: { read: { _eq: false }, isoutbound: { _eq: false } }
) {
@@ -88,6 +89,7 @@ export const GET_CONVERSATION_DETAILS = gql`
id
phone_num
archived
label
job_conversations {
jobid
conversationid
@@ -136,3 +138,15 @@ export const TOGGLE_CONVERSATION_ARCHIVE = gql`
}
}
`;
export const UPDATE_CONVERSATION_LABEL = gql`
mutation UPDATE_CONVERSATION_LABEL($id: uuid!, $label: String) {
update_conversations_by_pk(
pk_columns: { id: $id }
_set: { label: $label }
) {
label
id
}
}
`;

View File

@@ -142,6 +142,7 @@ export const QUERY_EXACT_JOB_IN_PRODUCTION = gql`
employee_refinish
employee_prep
employee_csr
date_repairstarted
joblines_status {
part_type
status
@@ -221,6 +222,7 @@ export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql`
employee_refinish
employee_prep
employee_csr
date_repairstarted
joblines_status {
part_type
status
@@ -302,6 +304,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
employee_prep
employee_csr
suspended
date_repairstarted
joblines_status {
part_type
status
@@ -498,6 +501,10 @@ export const GET_JOB_BY_PK = gql`
first_name
last_name
}
employee_csr
employee_prep
employee_refinish
employee_body
alt_transport
intakechecklist
invoice_final_note
@@ -672,6 +679,7 @@ export const GET_JOB_BY_PK = gql`
date_towin
date_rentalresp
date_exported
date_repairstarted
status
owner_owing
tax_registration_number
@@ -847,6 +855,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
owner_owing
special_coverage_policy
suspended
lbr_adjustments
available_jobs {
id
}
@@ -917,7 +926,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
date_next_contact
date_open
date_exported
date_repairstarted
date_scheduled
date_estimated
employee_body_rel {
@@ -1092,6 +1101,9 @@ export const UPDATE_JOB_ASSIGNMENTS = gql`
first_name
last_name
}
employee_csr
employee_body
employee_prep
}
}
}
@@ -1149,6 +1161,7 @@ export const CONVERT_JOB_TO_RO = gql`
$towin: Boolean
$referral_source: String
$referral_source_extra: String
$employee_csr: uuid
) {
update_jobs(
where: { id: { _eq: $jobId } }
@@ -1161,6 +1174,7 @@ export const CONVERT_JOB_TO_RO = gql`
driveable: $driveable
referral_source: $referral_source
referral_source_extra: $referral_source_extra
employee_csr: $employee_csr
}
) {
returning {
@@ -1171,6 +1185,10 @@ export const CONVERT_JOB_TO_RO = gql`
ins_co_nm
referral_source
referral_source_extra
employee_csr
employee_csr_rel {
id
}
}
}
}
@@ -1896,6 +1914,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
kmin
kmout
qb_multiple_payers
lbr_adjustments
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
id
removed

View File

@@ -5,6 +5,7 @@ export const QUERY_SEARCH_OWNER_BY_IDX = gql`
search_owners(args: { search: $search }, limit: 100) {
ownr_fn
ownr_ln
ownr_co_nm
ownr_ph1
ownr_ph2
ownr_addr1

View File

@@ -14,7 +14,7 @@ import { persistor, store } from "./redux/store";
import reportWebVitals from "./reportWebVitals";
import "./translations/i18n";
import "./utils/CleanAxios";
require("dotenv").config();
import { BrowserTracing } from "@sentry/tracing";
// Dinero.defaultCurrency = "CAD";
// Dinero.globalLocale = "en-CA";
@@ -29,10 +29,18 @@ if (process.env.NODE_ENV !== "development") {
"Module specifier, 'zlib' does not start with",
],
integrations: [
// new Integrations.BrowserTracing(),
new BrowserTracing(),
// new Sentry.Integrations.Breadcrumbs({ console: true }),
new Sentry.Replay(),
],
// This sets the sample rate to be 10%. You may want this to be 100% while
// in development and sample at a lower rate in production
replaysSessionSampleRate: 0.1,
// If the entire session is not sampled, use the below sample rate to sample
// sessions when an error occurs.
replaysOnErrorSampleRate: 1.0,
environment: process.env.NODE_ENV,
tracesSampleRate: 0.2,
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
// tracesSampleRate: 0.5,
@@ -41,7 +49,7 @@ if (process.env.NODE_ENV !== "development") {
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<BrowserRouter >
<PersistGate
loading={<LoadingSpinner message="Restoring your settings..." />}
persistor={persistor}

View File

@@ -1,5 +1,3 @@
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
import { BackTop, Layout } from "antd";
import preval from "preval.macro";
import React, { lazy, Suspense, useEffect } from "react";
@@ -170,18 +168,6 @@ const DmsPayables = lazy(() =>
const { Content, Footer } = Layout;
const stripePromise = new Promise((resolve, reject) => {
// client.query({ query: QUERY_STRIPE_ID }).then((resp) => {
// if (resp.data.bodyshops[0])
resolve(
loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
stripeAccount: "No Stripe Id Resolve",
})
);
// reject();
// });
});
const mapStateToProps = createStructuredSelector({
conflict: selectInstanceConflict,
bodyshop: selectBodyshop,
@@ -207,9 +193,9 @@ export function Manage({ match, conflict, bodyshop }) {
<Suspense
fallback={<LoadingSpinner message={t("general.labels.loadingapp")} />}
>
<Elements stripe={stripePromise}>
<PaymentModalContainer />
</Elements>
<BreadCrumbs />
<BillEnterModalContainer />
<JobCostingModal />

View File

@@ -1,81 +1,81 @@
import {
PaymentRequestButtonElement,
useStripe,
} from "@stripe/react-stripe-js";
import React, { useEffect, useState } from "react";
// import {
// PaymentRequestButtonElement,
// useStripe,
// } from "@stripe/react-stripe-js";
// import React, { useEffect, useState } from "react";
export default function MobilePaymentComponent() {
const stripe = useStripe();
// export default function MobilePaymentComponent() {
// const stripe = useStripe();
const [paymentRequest, setPaymentRequest] = useState(null);
useEffect(() => {
if (stripe) {
const pr = stripe.paymentRequest({
country: "CA",
displayItems: [{ label: "Deductible", amount: 1 }],
currency: "cad",
total: {
label: "Demo total",
amount: 1,
},
requestPayerName: true,
requestPayerEmail: true,
});
// const [paymentRequest, setPaymentRequest] = useState(null);
// useEffect(() => {
// if (stripe) {
// const pr = stripe.paymentRequest({
// country: "CA",
// displayItems: [{ label: "Deductible", amount: 1 }],
// currency: "cad",
// total: {
// label: "Demo total",
// amount: 1,
// },
// requestPayerName: true,
// requestPayerEmail: true,
// });
// Check the availability of the Payment Request API.
pr.canMakePayment().then((result) => {
if (result) {
setPaymentRequest(pr);
} else {
// var details = {
// total: { label: "", amount: { currency: "CAD", value: "0.00" } },
// };
// new PaymentRequest(
// [{ supportedMethods: ["basic-card"] }],
// {}
// // details
// ).show();
}
});
}
}, [stripe]);
// // Check the availability of the Payment Request API.
// pr.canMakePayment().then((result) => {
// if (result) {
// setPaymentRequest(pr);
// } else {
// // var details = {
// // total: { label: "", amount: { currency: "CAD", value: "0.00" } },
// // };
// // new PaymentRequest(
// // [{ supportedMethods: ["basic-card"] }],
// // {}
// // // details
// // ).show();
// }
// });
// }
// }, [stripe]);
if (paymentRequest) {
paymentRequest.on("paymentmethod", async (ev) => {
//Call server side to get the client secret
// Confirm the PaymentIntent without handling potential next actions (yet).
const { error: confirmError } = await stripe.confirmCardPayment(
"clientSecret",
{ payment_method: ev.paymentMethod.id },
{ handleActions: false }
);
// if (paymentRequest) {
// paymentRequest.on("paymentmethod", async (ev) => {
// //Call server side to get the client secret
// // Confirm the PaymentIntent without handling potential next actions (yet).
// const { error: confirmError } = await stripe.confirmCardPayment(
// "clientSecret",
// { payment_method: ev.paymentMethod.id },
// { handleActions: false }
// );
if (confirmError) {
// Report to the browser that the payment failed, prompting it to
// re-show the payment interface, or show an error message and close
// the payment interface.
ev.complete("fail");
} else {
// Report to the browser that the confirmation was successful, prompting
// it to close the browser payment method collection interface.
ev.complete("success");
// Let Stripe.js handle the rest of the payment flow.
const {
error, //paymentIntent
} = await stripe.confirmCardPayment("clientSecret");
if (error) {
// The payment failed -- ask your customer for a new payment method.
} else {
// The payment has succeeded.
}
}
});
return (
<div style={{ height: "300px" }}>
<PaymentRequestButtonElement options={{ paymentRequest }} />
</div>
);
}
// if (confirmError) {
// // Report to the browser that the payment failed, prompting it to
// // re-show the payment interface, or show an error message and close
// // the payment interface.
// ev.complete("fail");
// } else {
// // Report to the browser that the confirmation was successful, prompting
// // it to close the browser payment method collection interface.
// ev.complete("success");
// // Let Stripe.js handle the rest of the payment flow.
// const {
// error, //paymentIntent
// } = await stripe.confirmCardPayment("clientSecret");
// if (error) {
// // The payment failed -- ask your customer for a new payment method.
// } else {
// // The payment has succeeded.
// }
// }
// });
// return (
// <div style={{ height: "300px" }}>
// <PaymentRequestButtonElement options={{ paymentRequest }} />
// </div>
// );
// }
return <div></div>;
}
// return <div></div>;
// }

View File

@@ -1,23 +1,23 @@
import { Elements } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
import React from "react";
import MobilePaymentComponent from "./mobile-payment.component";
// import { Elements } from "@stripe/react-stripe-js";
// import { loadStripe } from "@stripe/stripe-js";
// import React from "react";
// import MobilePaymentComponent from "./mobile-payment.component";
const stripePromise = new Promise((resolve, reject) => {
resolve(
loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
stripeAccount: "acct_1Fa7lFIEahEZW8b4",
})
);
});
// const stripePromise = new Promise((resolve, reject) => {
// resolve(
// loadStripe(process.env.REACT_APP_STRIPE_PUBLIC_KEY, {
// stripeAccount: "acct_1Fa7lFIEahEZW8b4",
// })
// );
// });
export default function MobilePaymentContainer() {
return (
<div>
The mobile payment container.
<Elements stripe={stripePromise}>
<MobilePaymentComponent />
</Elements>
</div>
);
}
// export default function MobilePaymentContainer() {
// return (
// <div>
// The mobile payment container.
// <Elements stripe={stripePromise}>
// <MobilePaymentComponent />
// </Elements>
// </div>
// );
// }

View File

@@ -35,6 +35,7 @@ export function* calculateScheduleLoad({ payload: end }) {
const load = {
productionTotal: {},
productionHours: 0,
};
//Set the current load.
@@ -45,6 +46,10 @@ export function* calculateScheduleLoad({ payload: end }) {
prodJobs.forEach((item) => {
//Add all of the jobs currently in production to the buckets so that we have a starting point.
const bucketId = CheckJobBucket(buckets, item);
load.productionHours =
load.productionHours +
item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs;
if (bucketId) {
load.productionTotal[bucketId].count =
load.productionTotal[bucketId].count + 1;
@@ -149,6 +154,10 @@ export function* calculateScheduleLoad({ payload: end }) {
load[current].jobsIn || [],
load[current].jobsOut || []
);
load[current].expectedHours =
load.productionHours +
(load[current].hoursIn || 0) -
(load[current].hoursOut || 0);
} else {
load[current].expectedLoad = CalculateLoad(
load[prev].expectedLoad,
@@ -156,8 +165,13 @@ export function* calculateScheduleLoad({ payload: end }) {
load[current].jobsIn || [],
load[current].jobsOut || []
);
load[current].expectedHours =
load[prev].expectedHours +
(load[current].hoursIn || 0) -
(load[current].hoursOut || 0);
}
}
yield put(setProblemJobs(problemJobs));
yield put(scheduleLoadSuccess(load));
} catch (error) {

View File

@@ -50,6 +50,7 @@
"cancelledappointment": "Canceled appointment for: ",
"completingjobs": "Completing Jobs",
"dataconsistency": "{{ro_number}} has a data consistency issue. It has been excluded for scheduling purposes. CODE: {{code}}.",
"expectedprodhrs": "Expected Production Hours:",
"history": "History",
"inproduction": "Jobs In Production",
"manualevent": "Add Manual Appointment",
@@ -273,10 +274,12 @@
"itc_local": "Local Tax is ITC?",
"itc_state": "State Tax is ITC?",
"mappingname": "DMS Mapping Name",
"sendmaterialscosting": "Materials Cost as % of Sale",
"srcco": "Source Company #/Dealer #"
},
"email": "General Shop Email",
"enforce_class": "Enforce Class on Conversion?",
"enforce_conversion_csr": "Enforce CSR on Conversion?",
"enforce_referral": "Enforce Referrals",
"federal_tax_id": "Federal Tax ID (GST/HST)",
"ignoreblockeddays": "Scoreboard - Ignore Blocked Days",
@@ -1350,6 +1353,7 @@
"date_next_contact": "Next Contact Date",
"date_open": "Open",
"date_rentalresp": "Shop Rental Responsibility Start",
"date_repairstarted": "Repairs Started",
"date_scheduled": "Scheduled",
"date_towin": "Towed In",
"ded_amt": "Deductible",
@@ -1360,6 +1364,9 @@
"address": "Customer Address",
"amount": "Amount",
"center": "Center",
"control_type": {
"account_number": "Account Number"
},
"cost": "Cost",
"cost_dms_acctnumber": "Cost DMS Acct #",
"dms_make": "DMS Make",
@@ -1909,9 +1916,11 @@
},
"errors": {
"invalidphone": "The phone number is invalid. Unable to open conversation. ",
"noattachedjobs": "No jobs have been associated to this conversation. "
"noattachedjobs": "No jobs have been associated to this conversation. ",
"updatinglabel": "Error updating label. {{error}}"
},
"labels": {
"addlabel": "Add a label to this conversation.",
"archive": "Archive",
"maxtenimages": "You can only select up to a maximum of 10 images at a time.",
"messaging": "Messaging",
@@ -2162,6 +2171,7 @@
"courtesycarcontract": {
"courtesy_car_contract": "Courtesy Car Contract",
"courtesy_car_impound": "Impound Charges",
"courtesy_car_inventory": "Courtesy Car Inventory",
"courtesy_car_terms": "Courtesy Car Terms"
},
"errors": {
@@ -2248,6 +2258,7 @@
"supplement_request": "Supplement Request",
"thank_you_ro": "Thank You Letter",
"thirdpartypayer": "Third Party Payer",
"timetickets_ro": "Time Tickets",
"vehicle_check_in": "Vehicle Intake",
"vehicle_delivery_check": "Vehicle Delivery Checklist",
"window_tag": "Window Tag",
@@ -2472,6 +2483,8 @@
"purchases_by_vendor_summary_date_range": "Purchases by Vendor - Summary",
"purchases_grouped_by_vendor_detailed": "Purchases Grouped by Vendor - Detailed",
"purchases_grouped_by_vendor_summary": "Purchases Grouped by Vendor - Summary",
"returns_grouped_by_vendor_detailed": "Returns Grouped by Vendor - Detailed",
"returns_grouped_by_vendor_summary": "Returns Grouped by Vendor - Summary",
"schedule": "Appointment Schedule",
"scoreboard_detail": "Scoreboard Detail",
"scoreboard_summary": "Scoreboard Summary",
@@ -2488,6 +2501,7 @@
},
"schedule": {
"labels": {
"atssummary": "ATS Summary",
"employeevacation": "Employee Vacations",
"intake": "Intake Events",
"manual": "Manual Events",

View File

@@ -50,6 +50,7 @@
"cancelledappointment": "Cita cancelada para:",
"completingjobs": "",
"dataconsistency": "",
"expectedprodhrs": "",
"history": "",
"inproduction": "",
"manualevent": "",
@@ -273,10 +274,12 @@
"itc_local": "",
"itc_state": "",
"mappingname": "",
"sendmaterialscosting": "",
"srcco": ""
},
"email": "",
"enforce_class": "",
"enforce_conversion_csr": "",
"enforce_referral": "",
"federal_tax_id": "",
"ignoreblockeddays": "",
@@ -1350,6 +1353,7 @@
"date_next_contact": "",
"date_open": "Abierto",
"date_rentalresp": "",
"date_repairstarted": "",
"date_scheduled": "Programado",
"date_towin": "",
"ded_amt": "Deducible",
@@ -1360,6 +1364,9 @@
"address": "",
"amount": "",
"center": "",
"control_type": {
"account_number": ""
},
"cost": "",
"cost_dms_acctnumber": "",
"dms_make": "",
@@ -1909,9 +1916,11 @@
},
"errors": {
"invalidphone": "",
"noattachedjobs": ""
"noattachedjobs": "",
"updatinglabel": ""
},
"labels": {
"addlabel": "",
"archive": "",
"maxtenimages": "",
"messaging": "Mensajería",
@@ -2162,6 +2171,7 @@
"courtesycarcontract": {
"courtesy_car_contract": "",
"courtesy_car_impound": "",
"courtesy_car_inventory": "",
"courtesy_car_terms": ""
},
"errors": {
@@ -2248,6 +2258,7 @@
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
"timetickets_ro": "",
"vehicle_check_in": "",
"vehicle_delivery_check": "",
"window_tag": "",
@@ -2472,6 +2483,8 @@
"purchases_by_vendor_summary_date_range": "",
"purchases_grouped_by_vendor_detailed": "",
"purchases_grouped_by_vendor_summary": "",
"returns_grouped_by_vendor_detailed": "",
"returns_grouped_by_vendor_summary": "",
"schedule": "",
"scoreboard_detail": "",
"scoreboard_summary": "",
@@ -2488,6 +2501,7 @@
},
"schedule": {
"labels": {
"atssummary": "",
"employeevacation": "",
"intake": "",
"manual": "",

View File

@@ -50,6 +50,7 @@
"cancelledappointment": "Rendez-vous annulé pour:",
"completingjobs": "",
"dataconsistency": "",
"expectedprodhrs": "",
"history": "",
"inproduction": "",
"manualevent": "",
@@ -273,10 +274,12 @@
"itc_local": "",
"itc_state": "",
"mappingname": "",
"sendmaterialscosting": "",
"srcco": ""
},
"email": "",
"enforce_class": "",
"enforce_conversion_csr": "",
"enforce_referral": "",
"federal_tax_id": "",
"ignoreblockeddays": "",
@@ -1350,6 +1353,7 @@
"date_next_contact": "",
"date_open": "Ouvrir",
"date_rentalresp": "",
"date_repairstarted": "",
"date_scheduled": "Prévu",
"date_towin": "",
"ded_amt": "Déductible",
@@ -1360,6 +1364,9 @@
"address": "",
"amount": "",
"center": "",
"control_type": {
"account_number": ""
},
"cost": "",
"cost_dms_acctnumber": "",
"dms_make": "",
@@ -1909,9 +1916,11 @@
},
"errors": {
"invalidphone": "",
"noattachedjobs": ""
"noattachedjobs": "",
"updatinglabel": ""
},
"labels": {
"addlabel": "",
"archive": "",
"maxtenimages": "",
"messaging": "Messagerie",
@@ -2162,6 +2171,7 @@
"courtesycarcontract": {
"courtesy_car_contract": "",
"courtesy_car_impound": "",
"courtesy_car_inventory": "",
"courtesy_car_terms": ""
},
"errors": {
@@ -2248,6 +2258,7 @@
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
"timetickets_ro": "",
"vehicle_check_in": "",
"vehicle_delivery_check": "",
"window_tag": "",
@@ -2472,6 +2483,8 @@
"purchases_by_vendor_summary_date_range": "",
"purchases_grouped_by_vendor_detailed": "",
"purchases_grouped_by_vendor_summary": "",
"returns_grouped_by_vendor_detailed": "",
"returns_grouped_by_vendor_summary": "",
"schedule": "",
"scoreboard_detail": "",
"scoreboard_summary": "",
@@ -2488,6 +2501,7 @@
},
"schedule": {
"labels": {
"atssummary": "",
"employeevacation": "",
"intake": "",
"manual": "",

View File

@@ -488,6 +488,14 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "pre",
},
timetickets_ro: {
title: i18n.t("printcenter.jobs.timetickets_ro"),
description: "CASL Authorization",
subject: i18n.t("printcenter.jobs.timetickets_ro"),
key: "timetickets_ro",
disabled: false,
group: "financial",
},
}
: {}),
...(!type || type === "job_special"
@@ -1694,6 +1702,38 @@ export const TemplateList = (type, context) => {
},
group: "jobs",
},
returns_grouped_by_vendor_summary: {
title: i18n.t(
"reportcenter.templates.returns_grouped_by_vendor_summary"
),
subject: i18n.t(
"reportcenter.templates.returns_grouped_by_vendor_summary"
),
key: "returns_grouped_by_vendor_summary",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.parts_orders"),
field: i18n.t("parts_orders.fields.order_date"),
},
group: "jobs",
},
returns_grouped_by_vendor_detailed: {
title: i18n.t(
"reportcenter.templates.returns_grouped_by_vendor_detailed"
),
subject: i18n.t(
"reportcenter.templates.returns_grouped_by_vendor_detailed"
),
key: "returns_grouped_by_vendor_detailed",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.parts_orders"),
field: i18n.t("parts_orders.fields.order_date"),
},
group: "jobs",
},
}
: {}),
...(!type || type === "courtesycarcontract"
@@ -1731,6 +1771,21 @@ export const TemplateList = (type, context) => {
},
}
: {}),
...(!type || type === "courtesycar"
? {
courtesy_car_inventory: {
title: i18n.t(
"printcenter.courtesycarcontract.courtesy_car_inventory"
),
description: "Est Detail",
subject: i18n.t(
"printcenter.courtesycarcontract.courtesy_car_inventory"
),
key: "courtesy_car_inventory",
disabled: false,
},
}
: {}),
...(!type || type === "bill"
? {
inhouse_invoice: {

View File

@@ -2178,20 +2178,21 @@
estree-walker "^1.0.1"
picomatch "^2.2.2"
"@sentry/browser@7.7.0":
version "7.7.0"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.7.0.tgz#7810ee98d4969bd0367e29ac0af6c5779db7e6c4"
integrity sha512-oyzpWcsjVZTaf14zAL89Ng1DUHlbjN+V8pl8dR9Y9anphbzL5BK9p0fSK4kPIrO4GukK+XrKnLJDPuE/o7WR3g==
"@sentry/browser@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.28.1.tgz#c8086e26079809aa401a05d9b4a6e4df63a9c965"
integrity sha512-N8j93IcrWKWorfJ5D+RSKVAvcR4S5tIcZ/HvFPMrQWnfVa/jtJcrKThdjZYteA0wjmPiy8/D3KA8nB91yulBPA==
dependencies:
"@sentry/core" "7.7.0"
"@sentry/types" "7.7.0"
"@sentry/utils" "7.7.0"
"@sentry/core" "7.28.1"
"@sentry/replay" "7.28.1"
"@sentry/types" "7.28.1"
"@sentry/utils" "7.28.1"
tslib "^1.9.3"
"@sentry/cli@^1.74.4":
version "1.74.5"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.5.tgz#4a5c622913087c9ab6f82994da9a7526423779b8"
integrity sha512-Ze1ec306ZWHtrxKypOJ8nhtFqkrx2f/6bRH+DcJzEQ3bBePQ0ZnqJTTe4BBHADYBtxFIaUWzCZ6DquLz2Zv/sw==
"@sentry/cli@^1.74.6":
version "1.74.6"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.6.tgz#c4f276e52c6f5e8c8d692845a965988068ebc6f5"
integrity sha512-pJ7JJgozyjKZSTjOGi86chIngZMLUlYt2HOog+OJn+WGvqEkVymu8m462j1DiXAnex9NspB4zLLNuZ/R6rTQHg==
dependencies:
https-proxy-agent "^5.0.0"
mkdirp "^0.5.5"
@@ -2201,65 +2202,65 @@
proxy-from-env "^1.1.0"
which "^2.0.2"
"@sentry/core@7.7.0":
version "7.7.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.7.0.tgz#1a2d477897552d179380f7c54c7d81a4e98ea29a"
integrity sha512-Z15ACiuiFINFcK4gbMrnejLn4AVjKBPJOWKrrmpIe8mh+Y9diOuswt5mMUABs+jhpZvqht3PBLLGBL0WMsYMYA==
"@sentry/core@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.28.1.tgz#c712ce17469b18b01606108817be24a99ed2116e"
integrity sha512-7wvnuvn/mrAfcugWoCG/3pqDIrUgH5t+HisMJMGw0h9Tc33KqrmqMDCQVvjlrr2pWrw/vuUCFdm8CbUHJ832oQ==
dependencies:
"@sentry/hub" "7.7.0"
"@sentry/types" "7.7.0"
"@sentry/utils" "7.7.0"
"@sentry/types" "7.28.1"
"@sentry/utils" "7.28.1"
tslib "^1.9.3"
"@sentry/hub@7.7.0":
version "7.7.0"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.7.0.tgz#9ad3471cf5ecaf1a9d3a3a04ca2515ffec9e2c09"
integrity sha512-6gydK234+a0nKhBRDdIJ7Dp42CaiW2juTiHegUVDq+482balVzbZyEAmESCmuzKJhx5BhlCElVxs/cci1NjMpg==
"@sentry/react@^7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.28.1.tgz#79531b98176765a788cdf7f8f09b8ba713a95cfb"
integrity sha512-sFKK7uDREh84GyJcXDNuiQQ5VhLx7XJTOAdELxLv4HEI6BxbBRz0zNRQiKamTRkz9NmL7bZtld5TfbpOo9kijg==
dependencies:
"@sentry/types" "7.7.0"
"@sentry/utils" "7.7.0"
tslib "^1.9.3"
"@sentry/react@^7.7.0":
version "7.7.0"
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.7.0.tgz#a519dd727f863c1abf1a77beac01a3336c856828"
integrity sha512-93Khad3YAln6mKU9E15QH09XC1ARIOpNTRpnBl6AGl3bVhSdLExsbKDLa7Rx0mW2j4z/prOC6VEHf5mBvg4mPg==
dependencies:
"@sentry/browser" "7.7.0"
"@sentry/types" "7.7.0"
"@sentry/utils" "7.7.0"
"@sentry/browser" "7.28.1"
"@sentry/types" "7.28.1"
"@sentry/utils" "7.28.1"
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
"@sentry/tracing@^7.7.0":
version "7.7.0"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.7.0.tgz#67324b755a28e115289755e44a0b8b467a63d0b2"
integrity sha512-HNmvTwemuc21q/K6HXsSp9njkne6N1JQ71TB+QGqYU5VtxsVgYSUhhYqV6WcHz7LK4Hj6TvNFoeu69/rO0ysgw==
"@sentry/replay@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.28.1.tgz#fbdd377923e082423b95e3f128cb9af9451aca70"
integrity sha512-Os0PzMjKlwtHwzTU0kfVzGzsi4Vaj3g2arCl4Qnr3b6kYTb9WOFZo/n/v56ss7Z+nZG3K8W5PisoD4MRsRJRig==
dependencies:
"@sentry/hub" "7.7.0"
"@sentry/types" "7.7.0"
"@sentry/utils" "7.7.0"
"@sentry/core" "7.28.1"
"@sentry/types" "7.28.1"
"@sentry/utils" "7.28.1"
"@sentry/tracing@^7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.28.1.tgz#d276e4d17a79190a88112696c73de12c209607a1"
integrity sha512-uWspnuz+7FyW8ES5lRaVA7O/YJSzMlSkvBFtgzaoKmdaueokU/sRLwlCsrdgwavG1wpm79df7R1iiSeqhaXDlw==
dependencies:
"@sentry/core" "7.28.1"
"@sentry/types" "7.28.1"
"@sentry/utils" "7.28.1"
tslib "^1.9.3"
"@sentry/types@7.7.0":
version "7.7.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.7.0.tgz#dd6bd3d119d7efea0e85dbaa4b17de1c22b63c7a"
integrity sha512-4x8O7uerSGLnYC10krHl9t8h7xXHn5FextqKYbTCXCnx2hC8D+9lz8wcbQAFo0d97wiUYqI8opmEgFVGx7c5hQ==
"@sentry/types@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.28.1.tgz#9018b4c152b475de9bedd267237393d3c9b1253d"
integrity sha512-DvSplMVrVEmOzR2M161V5+B8Up3vR71xMqJOpWTzE9TqtFJRGPtqT/5OBsNJJw1+/j2ssMcnKwbEo9Q2EGeS6g==
"@sentry/utils@7.7.0":
version "7.7.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.7.0.tgz#013e3097c4268a76de578494c7df999635fb0ad4"
integrity sha512-fD+ROSFpeJlK7bEvUT2LOW7QqgjBpXJwVISKZ0P2fuzclRC3KoB2pbZgBM4PXMMTiSzRGWhvfRRjBiBvQJBBJQ==
"@sentry/utils@7.28.1":
version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.28.1.tgz#0a7b6aa4b09e91e4d1aded2a8c8dbaf818cee96e"
integrity sha512-75/jzLUO9HH09iC9TslNimGbxOP3jgn89P+q7uR+rp2fJfRExHVeKJZQdK0Ij4/SmE7TJ3Uh2r154N0INZEx1g==
dependencies:
"@sentry/types" "7.7.0"
"@sentry/types" "7.28.1"
tslib "^1.9.3"
"@sentry/webpack-plugin@^1.19.0":
version "1.19.0"
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.19.0.tgz#2b134318f1552ba7f3e3f9c83c71a202095f7a44"
integrity sha512-qSpdgdGMtdzagGveSWgo2b+t8PdPUscuOjbOyWCsJme9jlTFnNk0rX7JEA55OUozikKHM/+vVh08USLBnPboZw==
"@sentry/webpack-plugin@^1.20.0":
version "1.20.0"
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.20.0.tgz#e7add76122708fb6b4ee7951294b521019720e58"
integrity sha512-Ssj1mJVFsfU6vMCOM2d+h+KQR7QHSfeIP16t4l20Uq/neqWXZUQ2yvQfe4S3BjdbJXz/X4Rw8Hfy1Sd0ocunYw==
dependencies:
"@sentry/cli" "^1.74.4"
"@sentry/cli" "^1.74.6"
webpack-sources "^2.0.0 || ^3.0.0"
"@sinonjs/commons@^1.7.0":
version "1.8.3"
@@ -2304,18 +2305,6 @@
shallowequal "^1.1.0"
unfetch "^4.2.0"
"@stripe/react-stripe-js@^1.9.0":
version "1.9.0"
resolved "https://registry.yarnpkg.com/@stripe/react-stripe-js/-/react-stripe-js-1.9.0.tgz#74809a274d7db110c3daf6f68ca4d62c6e6559c7"
integrity sha512-Fn49X+Gu5fOTxhPOita1cPMi0jw+0v4xfJ3FCXbbvmfuuDl3M7ZvpRkoijBjql11NXsaXO3TMm3rkN3mEolJzw==
dependencies:
prop-types "^15.7.2"
"@stripe/stripe-js@^1.32.0":
version "1.32.0"
resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.32.0.tgz#4ecdd298db61ad9b240622eafed58da974bd210e"
integrity sha512-7EvBnbBfS1aynfLRmBFcuumHNGjKxnNkO47rorFBktqDYHwo7Yw6pfDW2iqq0R8r7i7XiJEdWPvvEgQAiDrx3A==
"@surma/rollup-plugin-off-main-thread@^1.1.1":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-1.4.2.tgz#e6786b6af5799f82f7ab3a82e53f6182d2b91a58"
@@ -14615,6 +14604,11 @@ webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-
source-list-map "^2.0.0"
source-map "~0.6.1"
"webpack-sources@^2.0.0 || ^3.0.0":
version "3.2.3"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
webpack@4.44.2:
version "4.44.2"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72"

View File

@@ -855,6 +855,7 @@
- deliverchecklist
- email
- enforce_class
- enforce_conversion_csr
- enforce_referral
- entegral_configuration
- entegral_id
@@ -951,6 +952,7 @@
- deliverchecklist
- email
- enforce_class
- enforce_conversion_csr
- enforce_referral
- federal_tax_id
- id
@@ -1241,6 +1243,7 @@
- bodyshopid
- created_at
- id
- label
- phone_num
- unreadcnt
- updated_at
@@ -1252,6 +1255,7 @@
- bodyshopid
- created_at
- id
- label
- phone_num
- unreadcnt
- updated_at
@@ -1273,6 +1277,7 @@
- bodyshopid
- created_at
- id
- label
- phone_num
- unreadcnt
- updated_at
@@ -3025,6 +3030,7 @@
- date_next_contact
- date_open
- date_rentalresp
- date_repairstarted
- date_scheduled
- date_towin
- ded_amt
@@ -3288,6 +3294,7 @@
- date_next_contact
- date_open
- date_rentalresp
- date_repairstarted
- date_scheduled
- date_towin
- ded_amt
@@ -3561,6 +3568,7 @@
- date_next_contact
- date_open
- date_rentalresp
- date_repairstarted
- date_scheduled
- date_towin
- ded_amt

View File

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

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "date_repairstarted" timestamptz
null;

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"."conversations" add column "label" text
-- null;

View File

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

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 "enforce_conversion_csr" boolean
-- null default 'false';

View File

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

View File

@@ -654,9 +654,9 @@ exports.default = function ({
if (qbo) {
InvoiceLineAdd.push({
DetailType: "SalesItemLineDetail",
Amount: Dinero({ amount: (payer.amount || 0) * 100 * -1 }).toFormat(
DineroQbFormat
),
Amount: Dinero({
amount: Math.round((payer.amount || 0) * 100) * -1,
}).toFormat(DineroQbFormat),
SalesItemLineDetail: {
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
@@ -687,9 +687,9 @@ exports.default = function ({
FullName: responsibilityCenters.qb_multiple_payers?.accountitem,
},
Desc: `${payer.name} Liability`,
Amount: Dinero({ amount: (payer.amount || 0) * 100 * -1 }).toFormat(
DineroQbFormat
),
Amount: Dinero({
amount: Math.round((payer.amount || 0) * 100) * -1,
}).toFormat(DineroQbFormat),
});
}
});

View File

@@ -209,6 +209,48 @@ exports.default = async function (socket, jobid) {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
console.log(
Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting),
typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) {
//Manually send the percentage of the costing.
//Paint Mat
const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.costs.find(
(c) => c.name === mapaAccountName
);
if (mapaAccount) {
if (!costCenterHash[mapaAccountName])
costCenterHash[mapaAccountName] = Dinero();
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(
bodyshop?.cdk_configuration?.sendmaterialscosting
)
);
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
//Shop Mat
const mashAccountName = selectedDmsAllocationConfig.costs.MASH;
const mashAccount = bodyshop.md_responsibility_centers.costs.find(
(c) => c.name === mashAccountName
);
if (mashAccount) {
if (!costCenterHash[mashAccountName])
costCenterHash[mashAccountName] = Dinero();
costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total).percentage(
bodyshop?.cdk_configuration?.sendmaterialscosting
)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
const { ca_bc_pvrt } = job;
if (ca_bc_pvrt) {
// const pvrtAccount = bodyshop.md_responsibility_centers.profits.find(

View File

@@ -334,12 +334,17 @@ const CreateRepairOrderTag = (job, errorCallback) => {
.format(AhDateFormat)) ||
"",
InsInspDate: null,
StartDate:
(job.actual_in &&
moment(job.actual_in)
.tz(job.bodyshop.timezone)
.format(AhDateFormat)) ||
"",
StartDate: job.date_repairstarted
? (job.date_repairstarted &&
moment(job.date_repairstarted)
.tz(job.bodyshop.timezone)
.format(AhDateFormat)) ||
""
: (job.date_repairstarted &&
moment(job.actual_in)
.tz(job.bodyshop.timezone)
.format(AhDateFormat)) ||
"",
PartsOrder: null,
TeardownHold: null,
SupplementSubmittedDate: null,
@@ -496,7 +501,13 @@ const CreateRepairOrderTag = (job, errorCallback) => {
FrameHours: job.job_totals.rates.laf.hours.toFixed(2),
GlassHours: job.job_totals.rates.lag.hours.toFixed(2),
DetailHours: job.job_totals.rates.lad.hours.toFixed(2),
LaborMiscHours: 0,
LaborMiscHours: (
job.job_totals.rates.la1.hours +
job.job_totals.rates.la2.hours +
job.job_totals.rates.la3.hours +
job.job_totals.rates.la4.hours +
job.job_totals.rates.lau.hours
).toFixed(2),
PartsTotal: Dinero(job.job_totals.parts.parts.total).toFormat(
AHDineroFormat
@@ -579,7 +590,12 @@ const CreateRepairOrderTag = (job, errorCallback) => {
),
DetailLaborTotalCost:
repairCosts.DetailLaborTotalCost.toFormat(AHDineroFormat),
LaborMiscTotal: 0,
LaborMiscTotal: Dinero(job.job_totals.rates.la1.total)
.add(Dinero(job.job_totals.rates.la2.total))
.add(Dinero(job.job_totals.rates.la3.total))
.add(Dinero(job.job_totals.rates.la4.total))
.add(Dinero(job.job_totals.rates.lau.total))
.toFormat(AHDineroFormat),
LaborMiscTotalCost: 0,
MiscellaneousChargeTotal: 0,
MiscellaneousChargeTotalCost: 0,

View File

@@ -695,6 +695,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
driveable
parts_tax_rates
ded_amt
date_repairstarted
joblines(where: {removed: {_eq: false}}) {
id
line_no
@@ -1010,6 +1011,7 @@ vehicle{
date_scheduled
date_invoiced
date_exported
date_repairstarted
status
owner_owing
tax_registration_number