Merged in release/2023-01-06 (pull request #652)

Release/2023 01 06
This commit is contained in:
Patrick Fic
2023-01-06 19:26:18 +00:00
30 changed files with 426 additions and 439 deletions

View File

@@ -4509,6 +4509,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </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> <concept_node>
<name>enforce_referral</name> <name>enforce_referral</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -42381,6 +42402,27 @@
<folder_node> <folder_node>
<name>labels</name> <name>labels</name>
<children> <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> <concept_node>
<name>employeevacation</name> <name>employeevacation</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

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

View File

@@ -1,8 +1,4 @@
import { import React from "react";
PaymentRequestButtonElement,
useStripe,
} from "@stripe/react-stripe-js";
import React, { useEffect, useState } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
@@ -19,49 +15,6 @@ const mapDispatchToProps = (dispatch) => ({
}); });
function Test({ bodyshop, setEmailOptions }) { 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 ( return (
<div> <div>
<button <button

View File

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

View File

@@ -25,8 +25,6 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(setModalContext({ context: context, modal: "payment" })), dispatch(setModalContext({ context: context, modal: "payment" })),
}); });
const stripeTestEnv = process.env.REACT_APP_STRIPE_PUBLIC_KEY; //.includes("test");
export function JobPayments({ export function JobPayments({
job, job,
jobRO, jobRO,
@@ -94,23 +92,6 @@ export function JobPayments({
state.sortedInfo.columnKey === "transactionid" && state.sortedInfo.columnKey === "transactionid" &&
state.sortedInfo.order, 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"), title: t("general.labels.actions"),
dataIndex: "actions", dataIndex: "actions",

View File

@@ -166,6 +166,16 @@ export default function ScoreboardAddButton({
painthrs: 0, 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({ form.setFieldsValue({
date: new moment(), date: new moment(),
bodyhrs: Math.round(v.bodyhrs * 10) / 10, bodyhrs: Math.round(v.bodyhrs * 10) / 10,

View File

@@ -9,6 +9,7 @@ import {
Space, Space,
Switch, Switch,
} from "antd"; } from "antd";
import axios from "axios";
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";
@@ -18,7 +19,6 @@ import { insertAuditTrail } from "../../redux/application/application.actions";
import { selectJobReadOnly } from "../../redux/application/application.selectors"; import { selectJobReadOnly } from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import AuditTrailMapping from "../../utils/AuditTrailMappings"; import AuditTrailMapping from "../../utils/AuditTrailMappings";
import axios from "axios";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -83,7 +83,11 @@ export function JobsConvertButton({
layout="vertical" layout="vertical"
form={form} form={form}
onFinish={handleConvert} onFinish={handleConvert}
initialValues={{ driveable: true, towin: false }} initialValues={{
driveable: true,
towin: false,
employee_csr: job.employee_csr,
}}
> >
<Form.Item <Form.Item
name={["ins_co_nm"]} name={["ins_co_nm"]}
@@ -151,6 +155,41 @@ export function JobsConvertButton({
</Form.Item> </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 <Form.Item
label={t("jobs.fields.ca_gst_registrant")} label={t("jobs.fields.ca_gst_registrant")}
name="ca_gst_registrant" name="ca_gst_registrant"
@@ -194,7 +233,14 @@ export function JobsConvertButton({
// style={{ display: job.converted ? "none" : "" }} // style={{ display: job.converted ? "none" : "" }}
disabled={job.converted || jobRO} disabled={job.converted || jobRO}
loading={loading} loading={loading}
onClick={() => setVisible(true)} onClick={() => {
setVisible(true);
form.setFieldsValue({
driveable: true,
towin: false,
employee_csr: job.employee_csr,
});
}}
> >
{t("jobs.actions.convert")} {t("jobs.actions.convert")}
</Button> </Button>

View File

@@ -1,12 +1,10 @@
import { useTreatments } from "@splitsoftware/splitio-react"; import { useTreatments } from "@splitsoftware/splitio-react";
import { CardElement } from "@stripe/react-stripe-js"; import { Form, Input, Radio, Select } from "antd";
import { Checkbox, Form, Input, Radio, Select } 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 Alert from "../alert/alert.component";
import DatePickerFormItem from "../form-date-picker/form-date-picker.component"; import DatePickerFormItem from "../form-date-picker/form-date-picker.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component"; import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import JobSearchSelect from "../job-search-select/job-search-select.component"; import JobSearchSelect from "../job-search-select/job-search-select.component";
@@ -19,11 +17,9 @@ const mapStateToProps = createStructuredSelector({
export function PaymentFormComponent({ export function PaymentFormComponent({
form, form,
stripeStateArr,
bodyshop, bodyshop,
disabled, disabled,
}) { }) {
const [stripeState, setStripeState] = stripeStateArr;
const { Qb_Multi_Ar } = useTreatments( const { Qb_Multi_Ar } = useTreatments(
["Qb_Multi_Ar"], ["Qb_Multi_Ar"],
{}, {},
@@ -31,9 +27,6 @@ export function PaymentFormComponent({
); );
const { t } = useTranslation(); const { t } = useTranslation();
const handleStripeChange = (e) => {
setStripeState({ error: e.error, cardComplete: e.complete });
};
return ( return (
<div> <div>
@@ -150,57 +143,6 @@ export function PaymentFormComponent({
</Form.Item> </Form.Item>
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow grow> <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 <Form.Item
label={t("general.labels.sendby")} label={t("general.labels.sendby")}
name="sendby" name="sendby"

View File

@@ -1,23 +1,20 @@
import { useApolloClient, useMutation } from "@apollo/client"; import { useMutation } from "@apollo/client";
import { CardElement, useElements, useStripe } from "@stripe/react-stripe-js";
import { Button, Form, Modal, notification } from "antd"; import { Button, Form, Modal, notification } from "antd";
import axios from "axios";
import React, { useEffect, 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 { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils";
import { GET_JOB_INFO_FOR_STRIPE } from "../../graphql/jobs.queries";
import { import {
INSERT_NEW_PAYMENT, INSERT_NEW_PAYMENT,
UPDATE_PAYMENT, UPDATE_PAYMENT
} from "../../graphql/payments.queries"; } from "../../graphql/payments.queries";
import { setEmailOptions } from "../../redux/email/email.actions"; import { setEmailOptions } from "../../redux/email/email.actions";
import { toggleModalVisible } from "../../redux/modals/modals.actions"; import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectPayment } from "../../redux/modals/modals.selectors"; import { selectPayment } from "../../redux/modals/modals.selectors";
import { import {
selectBodyshop, selectBodyshop,
selectCurrentUser, selectCurrentUser
} from "../../redux/user/user.selectors"; } from "../../redux/user/user.selectors";
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
@@ -45,82 +42,23 @@ function PaymentModalContainer({
const [enterAgain, setEnterAgain] = useState(false); const [enterAgain, setEnterAgain] = useState(false);
const [insertPayment] = useMutation(INSERT_NEW_PAYMENT); const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
const [updatePayment] = useMutation(UPDATE_PAYMENT); const [updatePayment] = useMutation(UPDATE_PAYMENT);
const client = useApolloClient();
const stripe = useStripe();
const elements = useElements();
const { t } = useTranslation(); const { t } = useTranslation();
const { context, actions, visible } = paymentModal; const { context, actions, visible } = paymentModal;
const [loading, setLoading] = useState(false); 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 handleFinish = async (values) => {
const { useStripe, sendby, ...paymentObj } = 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); setLoading(true);
try { 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)) { if (!context || (context && !context.id)) {
const newPayment = await insertPayment({ const newPayment = await insertPayment({
variables: { variables: {
paymentInput: { paymentInput: {
...paymentObj, ...paymentObj,
stripeid:
stripePayment &&
stripePayment.paymentIntent &&
stripePayment.paymentIntent.id,
}, },
}, },
}); });
@@ -236,11 +174,7 @@ function PaymentModalContainer({
layout="vertical" layout="vertical"
initialValues={context || {}} initialValues={context || {}}
> >
<PaymentForm <PaymentForm form={form} />
form={form}
stripeStateArr={stripeStateArr}
disabled={context && context.stripeid}
/>
</Form> </Form>
</Modal> </Modal>
); );
@@ -250,15 +184,3 @@ export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(PaymentModalContainer); )(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 PrintWrapperComponent from "../print-wrapper/print-wrapper.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.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({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -125,23 +123,6 @@ export function PaymentsListPaginated({
dataIndex: "transactionid", dataIndex: "transactionid",
key: "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"), title: t("payments.fields.created_at"),
dataIndex: "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 React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
@@ -27,7 +35,6 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
const handleOk = (e) => { const handleOk = (e) => {
e.stopPropagation(); e.stopPropagation();
form.submit(); form.submit();
setIsModalVisible(false);
}; };
const handleCancel = () => { const handleCancel = () => {
@@ -37,18 +44,24 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
const handleFinish = async ({ template, ...values }) => { const handleFinish = async ({ template, ...values }) => {
const { sendtype, ...restVals } = values; const { sendtype, ...restVals } = values;
setLoading(true); setLoading(true);
await GenerateDocument( try {
{ await GenerateDocument(
name: TemplateList("job_special")[template].key, {
variables: { id: jobId }, name: TemplateList("job_special")[template].key,
context: restVals, variables: { id: jobId },
}, context: restVals,
{}, },
"p", {},
jobId "p",
); jobId
setLoading(false); );
setIsModalVisible(false); setIsModalVisible(false);
} catch (error) {
notification.open({ type: "error", message: JSON.stringify(error) });
} finally {
setLoading(false);
}
form.resetFields(); form.resetFields();
}; };
@@ -60,7 +73,15 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
layout="vertical" layout="vertical"
form={form} form={form}
> >
<Form.Item required name="template"> <Form.Item
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
name="template"
>
<Radio.Group> <Radio.Group>
<Radio.Button value="parts_label_multiple"> <Radio.Button value="parts_label_multiple">
{t("printcenter.jobs.parts_label_multiple")} {t("printcenter.jobs.parts_label_multiple")}
@@ -71,14 +92,24 @@ export function PrintCenterJobsLabels({ bodyshop, jobId }) {
</Radio.Group> </Radio.Group>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
required rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
label={t("printcenter.jobs.labels.position")} label={t("printcenter.jobs.labels.position")}
name="position" name="position"
> >
<InputNumber min={1} precision={0} /> <InputNumber min={1} precision={0} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
required rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
label={t("printcenter.jobs.labels.count")} label={t("printcenter.jobs.labels.count")}
name="count" name="count"
> >

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

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

View File

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

View File

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

View File

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

View File

@@ -501,6 +501,10 @@ export const GET_JOB_BY_PK = gql`
first_name first_name
last_name last_name
} }
employee_csr
employee_prep
employee_refinish
employee_body
alt_transport alt_transport
intakechecklist intakechecklist
invoice_final_note invoice_final_note
@@ -851,6 +855,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
owner_owing owner_owing
special_coverage_policy special_coverage_policy
suspended suspended
lbr_adjustments
available_jobs { available_jobs {
id id
} }
@@ -1096,6 +1101,9 @@ export const UPDATE_JOB_ASSIGNMENTS = gql`
first_name first_name
last_name last_name
} }
employee_csr
employee_body
employee_prep
} }
} }
} }
@@ -1153,6 +1161,7 @@ export const CONVERT_JOB_TO_RO = gql`
$towin: Boolean $towin: Boolean
$referral_source: String $referral_source: String
$referral_source_extra: String $referral_source_extra: String
$employee_csr: uuid
) { ) {
update_jobs( update_jobs(
where: { id: { _eq: $jobId } } where: { id: { _eq: $jobId } }
@@ -1165,6 +1174,7 @@ export const CONVERT_JOB_TO_RO = gql`
driveable: $driveable driveable: $driveable
referral_source: $referral_source referral_source: $referral_source
referral_source_extra: $referral_source_extra referral_source_extra: $referral_source_extra
employee_csr: $employee_csr
} }
) { ) {
returning { returning {
@@ -1175,6 +1185,10 @@ export const CONVERT_JOB_TO_RO = gql`
ins_co_nm ins_co_nm
referral_source referral_source
referral_source_extra referral_source_extra
employee_csr
employee_csr_rel {
id
}
} }
} }
} }
@@ -1900,6 +1914,7 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
kmin kmin
kmout kmout
qb_multiple_payers qb_multiple_payers
lbr_adjustments
joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) { joblines(where: { removed: { _eq: false } }, order_by: { line_no: asc }) {
id id
removed removed

View File

@@ -14,7 +14,7 @@ import { persistor, store } from "./redux/store";
import reportWebVitals from "./reportWebVitals"; import reportWebVitals from "./reportWebVitals";
import "./translations/i18n"; import "./translations/i18n";
import "./utils/CleanAxios"; import "./utils/CleanAxios";
require("dotenv").config(); import { BrowserTracing } from "@sentry/tracing";
// Dinero.defaultCurrency = "CAD"; // Dinero.defaultCurrency = "CAD";
// Dinero.globalLocale = "en-CA"; // Dinero.globalLocale = "en-CA";
@@ -29,10 +29,18 @@ if (process.env.NODE_ENV !== "development") {
"Module specifier, 'zlib' does not start with", "Module specifier, 'zlib' does not start with",
], ],
integrations: [ integrations: [
// new Integrations.BrowserTracing(), new BrowserTracing(),
// new Sentry.Integrations.Breadcrumbs({ console: true }), // 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, environment: process.env.NODE_ENV,
tracesSampleRate: 0.2,
// We recommend adjusting this value in production, or using tracesSampler // We recommend adjusting this value in production, or using tracesSampler
// for finer control // for finer control
// tracesSampleRate: 0.5, // tracesSampleRate: 0.5,
@@ -41,7 +49,7 @@ if (process.env.NODE_ENV !== "development") {
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<BrowserRouter> <BrowserRouter >
<PersistGate <PersistGate
loading={<LoadingSpinner message="Restoring your settings..." />} loading={<LoadingSpinner message="Restoring your settings..." />}
persistor={persistor} 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 { BackTop, Layout } from "antd";
import preval from "preval.macro"; import preval from "preval.macro";
import React, { lazy, Suspense, useEffect } from "react"; import React, { lazy, Suspense, useEffect } from "react";
@@ -170,18 +168,6 @@ const DmsPayables = lazy(() =>
const { Content, Footer } = Layout; 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({ const mapStateToProps = createStructuredSelector({
conflict: selectInstanceConflict, conflict: selectInstanceConflict,
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -207,9 +193,9 @@ export function Manage({ match, conflict, bodyshop }) {
<Suspense <Suspense
fallback={<LoadingSpinner message={t("general.labels.loadingapp")} />} fallback={<LoadingSpinner message={t("general.labels.loadingapp")} />}
> >
<Elements stripe={stripePromise}>
<PaymentModalContainer /> <PaymentModalContainer />
</Elements>
<BreadCrumbs /> <BreadCrumbs />
<BillEnterModalContainer /> <BillEnterModalContainer />
<JobCostingModal /> <JobCostingModal />

View File

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

View File

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

View File

@@ -136,10 +136,6 @@ export function* calculateScheduleLoad({ payload: end }) {
} }
}); });
console.log(
"🚀 ~ file: application.sagas.js:160 ~ function*calculateScheduleLoad ~ load.productionTotal",
load.productionTotal
);
//Propagate the expected load to each day. //Propagate the expected load to each day.
const range = Math.round(moment.duration(end.diff(today)).asDays()); const range = Math.round(moment.duration(end.diff(today)).asDays());
for (var day = 0; day < range; day++) { for (var day = 0; day < range; day++) {
@@ -175,6 +171,7 @@ export function* calculateScheduleLoad({ payload: end }) {
(load[current].hoursOut || 0); (load[current].hoursOut || 0);
} }
} }
yield put(setProblemJobs(problemJobs)); yield put(setProblemJobs(problemJobs));
yield put(scheduleLoadSuccess(load)); yield put(scheduleLoadSuccess(load));
} catch (error) { } catch (error) {

View File

@@ -279,6 +279,7 @@
}, },
"email": "General Shop Email", "email": "General Shop Email",
"enforce_class": "Enforce Class on Conversion?", "enforce_class": "Enforce Class on Conversion?",
"enforce_conversion_csr": "Enforce CSR on Conversion?",
"enforce_referral": "Enforce Referrals", "enforce_referral": "Enforce Referrals",
"federal_tax_id": "Federal Tax ID (GST/HST)", "federal_tax_id": "Federal Tax ID (GST/HST)",
"ignoreblockeddays": "Scoreboard - Ignore Blocked Days", "ignoreblockeddays": "Scoreboard - Ignore Blocked Days",
@@ -2500,6 +2501,7 @@
}, },
"schedule": { "schedule": {
"labels": { "labels": {
"atssummary": "ATS Summary",
"employeevacation": "Employee Vacations", "employeevacation": "Employee Vacations",
"intake": "Intake Events", "intake": "Intake Events",
"manual": "Manual Events", "manual": "Manual Events",

View File

@@ -279,6 +279,7 @@
}, },
"email": "", "email": "",
"enforce_class": "", "enforce_class": "",
"enforce_conversion_csr": "",
"enforce_referral": "", "enforce_referral": "",
"federal_tax_id": "", "federal_tax_id": "",
"ignoreblockeddays": "", "ignoreblockeddays": "",
@@ -2500,6 +2501,7 @@
}, },
"schedule": { "schedule": {
"labels": { "labels": {
"atssummary": "",
"employeevacation": "", "employeevacation": "",
"intake": "", "intake": "",
"manual": "", "manual": "",

View File

@@ -279,6 +279,7 @@
}, },
"email": "", "email": "",
"enforce_class": "", "enforce_class": "",
"enforce_conversion_csr": "",
"enforce_referral": "", "enforce_referral": "",
"federal_tax_id": "", "federal_tax_id": "",
"ignoreblockeddays": "", "ignoreblockeddays": "",
@@ -2500,6 +2501,7 @@
}, },
"schedule": { "schedule": {
"labels": { "labels": {
"atssummary": "",
"employeevacation": "", "employeevacation": "",
"intake": "", "intake": "",
"manual": "", "manual": "",

View File

@@ -2178,20 +2178,21 @@
estree-walker "^1.0.1" estree-walker "^1.0.1"
picomatch "^2.2.2" picomatch "^2.2.2"
"@sentry/browser@7.7.0": "@sentry/browser@7.28.1":
version "7.7.0" version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.7.0.tgz#7810ee98d4969bd0367e29ac0af6c5779db7e6c4" resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.28.1.tgz#c8086e26079809aa401a05d9b4a6e4df63a9c965"
integrity sha512-oyzpWcsjVZTaf14zAL89Ng1DUHlbjN+V8pl8dR9Y9anphbzL5BK9p0fSK4kPIrO4GukK+XrKnLJDPuE/o7WR3g== integrity sha512-N8j93IcrWKWorfJ5D+RSKVAvcR4S5tIcZ/HvFPMrQWnfVa/jtJcrKThdjZYteA0wjmPiy8/D3KA8nB91yulBPA==
dependencies: dependencies:
"@sentry/core" "7.7.0" "@sentry/core" "7.28.1"
"@sentry/types" "7.7.0" "@sentry/replay" "7.28.1"
"@sentry/utils" "7.7.0" "@sentry/types" "7.28.1"
"@sentry/utils" "7.28.1"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/cli@^1.74.4": "@sentry/cli@^1.74.6":
version "1.74.5" version "1.74.6"
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.5.tgz#4a5c622913087c9ab6f82994da9a7526423779b8" resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.6.tgz#c4f276e52c6f5e8c8d692845a965988068ebc6f5"
integrity sha512-Ze1ec306ZWHtrxKypOJ8nhtFqkrx2f/6bRH+DcJzEQ3bBePQ0ZnqJTTe4BBHADYBtxFIaUWzCZ6DquLz2Zv/sw== integrity sha512-pJ7JJgozyjKZSTjOGi86chIngZMLUlYt2HOog+OJn+WGvqEkVymu8m462j1DiXAnex9NspB4zLLNuZ/R6rTQHg==
dependencies: dependencies:
https-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0"
mkdirp "^0.5.5" mkdirp "^0.5.5"
@@ -2201,65 +2202,65 @@
proxy-from-env "^1.1.0" proxy-from-env "^1.1.0"
which "^2.0.2" which "^2.0.2"
"@sentry/core@7.7.0": "@sentry/core@7.28.1":
version "7.7.0" version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.7.0.tgz#1a2d477897552d179380f7c54c7d81a4e98ea29a" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.28.1.tgz#c712ce17469b18b01606108817be24a99ed2116e"
integrity sha512-Z15ACiuiFINFcK4gbMrnejLn4AVjKBPJOWKrrmpIe8mh+Y9diOuswt5mMUABs+jhpZvqht3PBLLGBL0WMsYMYA== integrity sha512-7wvnuvn/mrAfcugWoCG/3pqDIrUgH5t+HisMJMGw0h9Tc33KqrmqMDCQVvjlrr2pWrw/vuUCFdm8CbUHJ832oQ==
dependencies: dependencies:
"@sentry/hub" "7.7.0" "@sentry/types" "7.28.1"
"@sentry/types" "7.7.0" "@sentry/utils" "7.28.1"
"@sentry/utils" "7.7.0"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/hub@7.7.0": "@sentry/react@^7.28.1":
version "7.7.0" version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-7.7.0.tgz#9ad3471cf5ecaf1a9d3a3a04ca2515ffec9e2c09" resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.28.1.tgz#79531b98176765a788cdf7f8f09b8ba713a95cfb"
integrity sha512-6gydK234+a0nKhBRDdIJ7Dp42CaiW2juTiHegUVDq+482balVzbZyEAmESCmuzKJhx5BhlCElVxs/cci1NjMpg== integrity sha512-sFKK7uDREh84GyJcXDNuiQQ5VhLx7XJTOAdELxLv4HEI6BxbBRz0zNRQiKamTRkz9NmL7bZtld5TfbpOo9kijg==
dependencies: dependencies:
"@sentry/types" "7.7.0" "@sentry/browser" "7.28.1"
"@sentry/utils" "7.7.0" "@sentry/types" "7.28.1"
tslib "^1.9.3" "@sentry/utils" "7.28.1"
"@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"
hoist-non-react-statics "^3.3.2" hoist-non-react-statics "^3.3.2"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/tracing@^7.7.0": "@sentry/replay@7.28.1":
version "7.7.0" version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.7.0.tgz#67324b755a28e115289755e44a0b8b467a63d0b2" resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.28.1.tgz#fbdd377923e082423b95e3f128cb9af9451aca70"
integrity sha512-HNmvTwemuc21q/K6HXsSp9njkne6N1JQ71TB+QGqYU5VtxsVgYSUhhYqV6WcHz7LK4Hj6TvNFoeu69/rO0ysgw== integrity sha512-Os0PzMjKlwtHwzTU0kfVzGzsi4Vaj3g2arCl4Qnr3b6kYTb9WOFZo/n/v56ss7Z+nZG3K8W5PisoD4MRsRJRig==
dependencies: dependencies:
"@sentry/hub" "7.7.0" "@sentry/core" "7.28.1"
"@sentry/types" "7.7.0" "@sentry/types" "7.28.1"
"@sentry/utils" "7.7.0" "@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" tslib "^1.9.3"
"@sentry/types@7.7.0": "@sentry/types@7.28.1":
version "7.7.0" version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.7.0.tgz#dd6bd3d119d7efea0e85dbaa4b17de1c22b63c7a" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.28.1.tgz#9018b4c152b475de9bedd267237393d3c9b1253d"
integrity sha512-4x8O7uerSGLnYC10krHl9t8h7xXHn5FextqKYbTCXCnx2hC8D+9lz8wcbQAFo0d97wiUYqI8opmEgFVGx7c5hQ== integrity sha512-DvSplMVrVEmOzR2M161V5+B8Up3vR71xMqJOpWTzE9TqtFJRGPtqT/5OBsNJJw1+/j2ssMcnKwbEo9Q2EGeS6g==
"@sentry/utils@7.7.0": "@sentry/utils@7.28.1":
version "7.7.0" version "7.28.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.7.0.tgz#013e3097c4268a76de578494c7df999635fb0ad4" resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.28.1.tgz#0a7b6aa4b09e91e4d1aded2a8c8dbaf818cee96e"
integrity sha512-fD+ROSFpeJlK7bEvUT2LOW7QqgjBpXJwVISKZ0P2fuzclRC3KoB2pbZgBM4PXMMTiSzRGWhvfRRjBiBvQJBBJQ== integrity sha512-75/jzLUO9HH09iC9TslNimGbxOP3jgn89P+q7uR+rp2fJfRExHVeKJZQdK0Ij4/SmE7TJ3Uh2r154N0INZEx1g==
dependencies: dependencies:
"@sentry/types" "7.7.0" "@sentry/types" "7.28.1"
tslib "^1.9.3" tslib "^1.9.3"
"@sentry/webpack-plugin@^1.19.0": "@sentry/webpack-plugin@^1.20.0":
version "1.19.0" version "1.20.0"
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.19.0.tgz#2b134318f1552ba7f3e3f9c83c71a202095f7a44" resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.20.0.tgz#e7add76122708fb6b4ee7951294b521019720e58"
integrity sha512-qSpdgdGMtdzagGveSWgo2b+t8PdPUscuOjbOyWCsJme9jlTFnNk0rX7JEA55OUozikKHM/+vVh08USLBnPboZw== integrity sha512-Ssj1mJVFsfU6vMCOM2d+h+KQR7QHSfeIP16t4l20Uq/neqWXZUQ2yvQfe4S3BjdbJXz/X4Rw8Hfy1Sd0ocunYw==
dependencies: dependencies:
"@sentry/cli" "^1.74.4" "@sentry/cli" "^1.74.6"
webpack-sources "^2.0.0 || ^3.0.0"
"@sinonjs/commons@^1.7.0": "@sinonjs/commons@^1.7.0":
version "1.8.3" version "1.8.3"
@@ -14615,6 +14616,11 @@ webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-
source-list-map "^2.0.0" source-list-map "^2.0.0"
source-map "~0.6.1" 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: webpack@4.44.2:
version "4.44.2" version "4.44.2"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72"

View File

@@ -855,6 +855,7 @@
- deliverchecklist - deliverchecklist
- email - email
- enforce_class - enforce_class
- enforce_conversion_csr
- enforce_referral - enforce_referral
- entegral_configuration - entegral_configuration
- entegral_id - entegral_id
@@ -951,6 +952,7 @@
- deliverchecklist - deliverchecklist
- email - email
- enforce_class - enforce_class
- enforce_conversion_csr
- enforce_referral - enforce_referral
- federal_tax_id - federal_tax_id
- id - id

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