IO-2782-Send-Promanager-Welcome-Email - Update for merge conflict
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
@@ -4730,6 +4730,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>batchid</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>bill_allow_post_to_closed</name>
|
<name>bill_allow_post_to_closed</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -4856,6 +4877,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>companycode</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>country</name>
|
<name>country</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -5564,6 +5606,53 @@
|
|||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>intellipay_config</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>cash_discount_percentage</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>enable_cash_discount</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>
|
<concept_node>
|
||||||
<name>invoice_federal_tax_rate</name>
|
<name>invoice_federal_tax_rate</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -11109,6 +11198,48 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>intellipay</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>intellipay_cash_discount</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>jobstatuses</name>
|
<name>jobstatuses</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -22230,6 +22361,27 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>buttons</name>
|
<name>buttons</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>create_short_link</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>goback</name>
|
<name>goback</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -49635,6 +49787,48 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>templates</name>
|
<name>templates</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>adp_payroll_flat</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>adp_payroll_straight</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>anticipated_revenue</name>
|
<name>anticipated_revenue</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { DeleteFilled } from "@ant-design/icons";
|
import { DeleteFilled, CopyFilled } from "@ant-design/icons";
|
||||||
import { useLazyQuery, useMutation } from "@apollo/client";
|
import { useLazyQuery, useMutation } from "@apollo/client";
|
||||||
import { Button, Card, Col, Form, Input, Row, Space, Spin, Statistic, notification } from "antd";
|
import { Button, Card, Col, Form, Input, Row, Space, Spin, Statistic, message, notification } from "antd";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -14,10 +14,12 @@ import { selectBodyshop } from "../../redux/user/user.selectors";
|
|||||||
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
import AuditTrailMapping from "../../utils/AuditTrailMappings";
|
||||||
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
||||||
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
import JobSearchSelectComponent from "../job-search-select/job-search-select.component";
|
||||||
|
import { getCurrentUser } from "../../firebase/firebase.utils";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
cardPaymentModal: selectCardPayment,
|
cardPaymentModal: selectCardPayment,
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: getCurrentUser
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
@@ -25,11 +27,17 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment"))
|
toggleModalVisible: () => dispatch(toggleModalVisible("cardPayment"))
|
||||||
});
|
});
|
||||||
|
|
||||||
const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisible, insertAuditTrail }) => {
|
const CardPaymentModalComponent = ({
|
||||||
|
bodyshop,
|
||||||
|
currentUser,
|
||||||
|
cardPaymentModal,
|
||||||
|
toggleModalVisible,
|
||||||
|
insertAuditTrail
|
||||||
|
}) => {
|
||||||
const { context, actions } = cardPaymentModal;
|
const { context, actions } = cardPaymentModal;
|
||||||
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const [paymentLink, setPaymentLink] = useState();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
// const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
// const [insertPayment] = useMutation(INSERT_NEW_PAYMENT);
|
||||||
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
const [insertPaymentResponse] = useMutation(INSERT_PAYMENT_RESPONSE);
|
||||||
@@ -51,8 +59,7 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
//2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback.
|
//2024-04-25: Nothing is going to happen here anymore. We'll completely rely on the callback.
|
||||||
//Add a slight delay to allow the refetch to properly get the data.
|
//Add a slight delay to allow the refetch to properly get the data.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (actions && actions.refetch && typeof actions.refetch === "function")
|
if (actions && actions.refetch && typeof actions.refetch === "function") actions.refetch();
|
||||||
actions.refetch();
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toggleModalVisible();
|
toggleModalVisible();
|
||||||
}, 750);
|
}, 750);
|
||||||
@@ -86,7 +93,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleIntelliPayCharge = async () => {
|
const handleIntelliPayCharge = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
//Validate
|
//Validate
|
||||||
@@ -101,7 +107,7 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
const response = await axios.post("/intellipay/lightbox_credentials", {
|
const response = await axios.post("/intellipay/lightbox_credentials", {
|
||||||
bodyshop,
|
bodyshop,
|
||||||
refresh: !!window.intellipay,
|
refresh: !!window.intellipay,
|
||||||
paymentSplitMeta: form.getFieldsValue(),
|
paymentSplitMeta: form.getFieldsValue()
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window.intellipay) {
|
if (window.intellipay) {
|
||||||
@@ -126,6 +132,42 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleIntelliPayChargeShortLink = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
//Validate
|
||||||
|
try {
|
||||||
|
await form.validateFields();
|
||||||
|
} catch (error) {
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { payments } = form.getFieldsValue();
|
||||||
|
const response = await axios.post("/intellipay/generate_payment_url", {
|
||||||
|
bodyshop,
|
||||||
|
amount: payments?.reduce((acc, val) => {
|
||||||
|
return acc + (val?.amount || 0);
|
||||||
|
}, 0),
|
||||||
|
account: payments && data && data.jobs.length > 0 ? data.jobs.map((j) => j.ro_number).join(", ") : null,
|
||||||
|
comment: btoa(JSON.stringify({ payments, userEmail: currentUser.email })),
|
||||||
|
paymentSplitMeta: form.getFieldsValue()
|
||||||
|
});
|
||||||
|
if (response.data) {
|
||||||
|
setPaymentLink(response.data?.shorUrl);
|
||||||
|
navigator.clipboard.writeText(response.data?.shorUrl);
|
||||||
|
message.success(t("general.actions.copied"));
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
} catch (error) {
|
||||||
|
notification.open({
|
||||||
|
type: "error",
|
||||||
|
message: t("job_payments.notifications.error.openingip")
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title="Card Payment">
|
<Card title="Card Payment">
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
@@ -208,10 +250,7 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
{() => {
|
{() => {
|
||||||
//If all of the job ids have been fileld in, then query and update the IP field.
|
//If all of the job ids have been fileld in, then query and update the IP field.
|
||||||
const { payments } = form.getFieldsValue();
|
const { payments } = form.getFieldsValue();
|
||||||
if (
|
if (payments?.length > 0 && payments?.filter((p) => p?.jobid).length === payments?.length) {
|
||||||
payments?.length > 0 &&
|
|
||||||
payments?.filter((p) => p?.jobid).length === payments?.length
|
|
||||||
) {
|
|
||||||
refetch({ jobids: payments.map((p) => p.jobid) });
|
refetch({ jobids: payments.map((p) => p.jobid) });
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -246,7 +285,6 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
const totalAmountToCharge = payments?.reduce((acc, val) => {
|
const totalAmountToCharge = payments?.reduce((acc, val) => {
|
||||||
return acc + (val?.amount || 0);
|
return acc + (val?.amount || 0);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space style={{ float: "right" }}>
|
<Space style={{ float: "right" }}>
|
||||||
<Statistic title="Amount To Charge" value={totalAmountToCharge} precision={2} />
|
<Statistic title="Amount To Charge" value={totalAmountToCharge} precision={2} />
|
||||||
@@ -273,11 +311,36 @@ const CardPaymentModalComponent = ({ bodyshop, cardPaymentModal, toggleModalVisi
|
|||||||
>
|
>
|
||||||
{t("job_payments.buttons.proceedtopayment")}
|
{t("job_payments.buttons.proceedtopayment")}
|
||||||
</Button>
|
</Button>
|
||||||
|
<Space direction="vertical" align="center">
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
// data-ipayname="submit"
|
||||||
|
className="ipayfield"
|
||||||
|
loading={queryLoading || loading}
|
||||||
|
disabled={!(totalAmountToCharge > 0)}
|
||||||
|
onClick={handleIntelliPayChargeShortLink}
|
||||||
|
>
|
||||||
|
{t("job_payments.buttons.create_short_link")}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
{paymentLink && (
|
||||||
|
<Space
|
||||||
|
style={{ cursor: "pointer", float: "right" }}
|
||||||
|
align="end"
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(paymentLink);
|
||||||
|
message.success(t("general.actions.copied"));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>{paymentLink}</div>
|
||||||
|
<CopyFilled />
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
</Spin>
|
</Spin>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createStructuredSelector } from "reselect";
|
import { createStructuredSelector } from "reselect";
|
||||||
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
import { openChatByPhone, setMessage } from "../../redux/messaging/messaging.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
import CurrencyFormItemComponent from "../form-items-formatted/currency-form-item.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
openChatByPhone: (phone) => dispatch(openChatByPhone(phone)),
|
||||||
@@ -20,7 +21,7 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
});
|
});
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(PaymentsGenerateLink);
|
export default connect(mapStateToProps, mapDispatchToProps)(PaymentsGenerateLink);
|
||||||
|
|
||||||
export function PaymentsGenerateLink({ bodyshop, callback, job, openChatByPhone, setMessage }) {
|
export function PaymentsGenerateLink({ bodyshop, currentUser, callback, job, openChatByPhone, setMessage }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
@@ -30,29 +31,35 @@ export function PaymentsGenerateLink({ bodyshop, callback, job, openChatByPhone,
|
|||||||
|
|
||||||
const handleFinish = async ({ amount }) => {
|
const handleFinish = async ({ amount }) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
let p;
|
||||||
const p = parsePhoneNumber(job.ownr_ph1, "CA");
|
try {
|
||||||
|
p = parsePhoneNumber(job.ownr_ph1 || "", "CA");
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Unable to parse phone number");
|
||||||
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const response = await axios.post("/intellipay/generate_payment_url", {
|
const response = await axios.post("/intellipay/generate_payment_url", {
|
||||||
bodyshop,
|
bodyshop,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
account: job.ro_number,
|
account: job.ro_number,
|
||||||
invoice: job.id
|
comment: btoa(JSON.stringify({ payments: [{ jobid: job.id, amount }], userEmail: currentUser.email }))
|
||||||
});
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setPaymentLink(response.data.shorUrl);
|
setPaymentLink(response.data.shorUrl);
|
||||||
|
|
||||||
openChatByPhone({
|
if (p) {
|
||||||
phone_num: p.formatInternational(),
|
openChatByPhone({
|
||||||
jobid: job.id
|
phone_num: p.formatInternational(),
|
||||||
});
|
jobid: job.id
|
||||||
setMessage(
|
});
|
||||||
t("payments.labels.smspaymentreminder", {
|
setMessage(
|
||||||
shopname: bodyshop.shopname,
|
t("payments.labels.smspaymentreminder", {
|
||||||
amount: amount,
|
shopname: bodyshop.shopname,
|
||||||
payment_link: response.data.shorUrl
|
amount: amount,
|
||||||
})
|
payment_link: response.data.shorUrl
|
||||||
);
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
//Add in confirmation & errors.
|
//Add in confirmation & errors.
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import ShopInfoTaskPresets from "./shop-info.task-presets.component";
|
|||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
import ShopInfoRoGuard from "./shop-info.roguard.component";
|
import ShopInfoRoGuard from "./shop-info.roguard.component";
|
||||||
|
import ShopInfoIntellipay from "./shop-intellipay-config.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
@@ -135,6 +136,17 @@ export function ShopInfoComponent({ bodyshop, form, saveLoading }) {
|
|||||||
],
|
],
|
||||||
rome: "USE_IMEX",
|
rome: "USE_IMEX",
|
||||||
promanager: []
|
promanager: []
|
||||||
|
}),
|
||||||
|
...InstanceRenderManager({
|
||||||
|
imex: [],
|
||||||
|
rome: [
|
||||||
|
{
|
||||||
|
key: "intellipay",
|
||||||
|
label: t("bodyshop.labels.intellipay"),
|
||||||
|
children: <ShopInfoIntellipay form={form} />
|
||||||
|
}
|
||||||
|
],
|
||||||
|
promanager: []
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { Alert, Form, InputNumber, Switch } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ShopInfoIntellipay);
|
||||||
|
|
||||||
|
export function ShopInfoIntellipay({ bodyshop, form }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item dependencies={[["intellipay_config", "enable_cash_discount"]]}>
|
||||||
|
{() => {
|
||||||
|
const { intellipay_config } = form.getFieldsValue();
|
||||||
|
|
||||||
|
if (intellipay_config?.enable_cash_discount)
|
||||||
|
return <Alert message={t("bodyshop.labels.intellipay_cash_discount")} />;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<LayoutFormRow noDivider>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.intellipay_config.enable_cash_discount")}
|
||||||
|
valuePropName="checked"
|
||||||
|
name={["intellipay_config", "enable_cash_discount"]}
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("bodyshop.fields.intellipay_config.cash_discount_percentage")}
|
||||||
|
valuePropName="checked"
|
||||||
|
dependencies={[["intellipay_config", "enable_cash_discount"]]}
|
||||||
|
name={["intellipay_config", "cash_discount_percentage"]}
|
||||||
|
rules={[
|
||||||
|
({ getFieldsValue }) => ({ required: form.getFieldValue(["intellipay_config", "enable_cash_discount"]) })
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} max={100} precision={1} suffix='%'/>
|
||||||
|
</Form.Item>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -138,7 +138,8 @@ export const QUERY_BODYSHOP = gql`
|
|||||||
tt_enforce_hours_for_tech_console
|
tt_enforce_hours_for_tech_console
|
||||||
md_tasks_presets
|
md_tasks_presets
|
||||||
use_paint_scale_data
|
use_paint_scale_data
|
||||||
md_ro_guard
|
intellipay_config
|
||||||
|
md_ro_guard
|
||||||
employee_teams(order_by: { name: asc }, where: { active: { _eq: true } }) {
|
employee_teams(order_by: { name: asc }, where: { active: { _eq: true } }) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@@ -266,7 +267,8 @@ export const UPDATE_SHOP = gql`
|
|||||||
enforce_conversion_category
|
enforce_conversion_category
|
||||||
tt_enforce_hours_for_tech_console
|
tt_enforce_hours_for_tech_console
|
||||||
md_tasks_presets
|
md_tasks_presets
|
||||||
md_ro_guard
|
intellipay_config
|
||||||
|
md_ro_guard
|
||||||
employee_teams(order_by: { name: asc }, where: { active: { _eq: true } }) {
|
employee_teams(order_by: { name: asc }, where: { active: { _eq: true } }) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import { Tabs } from "antd";
|
import { Tabs } from "antd";
|
||||||
import React, { useEffect } from "react";
|
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
import React, { useEffect } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
||||||
|
import ShopCsiConfig from "../../components/shop-csi-config/shop-csi-config.component";
|
||||||
import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container";
|
import ShopEmployeesContainer from "../../components/shop-employees/shop-employees.container";
|
||||||
import ShopInfoContainer from "../../components/shop-info/shop-info.container";
|
import ShopInfoContainer from "../../components/shop-info/shop-info.container";
|
||||||
import ShopCsiConfig from "../../components/shop-csi-config/shop-csi-config.component";
|
import ShopInfoUsersComponent from "../../components/shop-users/shop-users.component";
|
||||||
import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
|
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { createStructuredSelector } from "reselect";
|
|
||||||
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import ShopInfoUsersComponent from "../../components/shop-users/shop-users.component";
|
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
||||||
|
|
||||||
import ShopTeamsContainer from "../../components/shop-teams/shop-teams.container";
|
|
||||||
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
|
import { HasFeatureAccess } from "../../components/feature-wrapper/feature-wrapper.component";
|
||||||
|
import ShopTeamsContainer from "../../components/shop-teams/shop-teams.container";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop
|
||||||
|
|||||||
@@ -332,6 +332,10 @@
|
|||||||
"next_contact_hours": "Automatic Next Contact Date - Hours from Intake",
|
"next_contact_hours": "Automatic Next Contact Date - Hours from Intake",
|
||||||
"templates": "Intake Templates"
|
"templates": "Intake Templates"
|
||||||
},
|
},
|
||||||
|
"intellipay_config": {
|
||||||
|
"cash_discount_percentage": "Cash Discount %",
|
||||||
|
"enable_cash_discount": "Enable Cash Discounting"
|
||||||
|
},
|
||||||
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
|
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
|
||||||
"invoice_local_tax_rate": "Invoices - Local Tax Rate",
|
"invoice_local_tax_rate": "Invoices - Local Tax Rate",
|
||||||
"invoice_state_tax_rate": "Invoices - State Tax Rate",
|
"invoice_state_tax_rate": "Invoices - State Tax Rate",
|
||||||
@@ -663,6 +667,8 @@
|
|||||||
"filehandlers": "Adjusters",
|
"filehandlers": "Adjusters",
|
||||||
"insurancecos": "Insurance Companies",
|
"insurancecos": "Insurance Companies",
|
||||||
"intakechecklist": "Intake Checklist",
|
"intakechecklist": "Intake Checklist",
|
||||||
|
"intellipay": "IntelliPay",
|
||||||
|
"intellipay_cash_discount": "Please ensure that cash discounting has been enabled on your merchant account. Reach out to IntelliPay Support if you need assistance. ",
|
||||||
"jobstatuses": "Job Statuses",
|
"jobstatuses": "Job Statuses",
|
||||||
"laborrates": "Labor Rates",
|
"laborrates": "Labor Rates",
|
||||||
"licensing": "Licensing",
|
"licensing": "Licensing",
|
||||||
@@ -1367,6 +1373,7 @@
|
|||||||
},
|
},
|
||||||
"job_payments": {
|
"job_payments": {
|
||||||
"buttons": {
|
"buttons": {
|
||||||
|
"create_short_link": "Generate Short Link",
|
||||||
"goback": "Go Back",
|
"goback": "Go Back",
|
||||||
"proceedtopayment": "Proceed to Payment",
|
"proceedtopayment": "Proceed to Payment",
|
||||||
"refundpayment": "Refund Payment"
|
"refundpayment": "Refund Payment"
|
||||||
|
|||||||
@@ -332,6 +332,10 @@
|
|||||||
"next_contact_hours": "",
|
"next_contact_hours": "",
|
||||||
"templates": ""
|
"templates": ""
|
||||||
},
|
},
|
||||||
|
"intellipay_config": {
|
||||||
|
"cash_discount_percentage": "",
|
||||||
|
"enable_cash_discount": ""
|
||||||
|
},
|
||||||
"invoice_federal_tax_rate": "",
|
"invoice_federal_tax_rate": "",
|
||||||
"invoice_local_tax_rate": "",
|
"invoice_local_tax_rate": "",
|
||||||
"invoice_state_tax_rate": "",
|
"invoice_state_tax_rate": "",
|
||||||
@@ -663,6 +667,8 @@
|
|||||||
"filehandlers": "",
|
"filehandlers": "",
|
||||||
"insurancecos": "",
|
"insurancecos": "",
|
||||||
"intakechecklist": "",
|
"intakechecklist": "",
|
||||||
|
"intellipay": "",
|
||||||
|
"intellipay_cash_discount": "",
|
||||||
"jobstatuses": "",
|
"jobstatuses": "",
|
||||||
"laborrates": "",
|
"laborrates": "",
|
||||||
"licensing": "",
|
"licensing": "",
|
||||||
@@ -1367,6 +1373,7 @@
|
|||||||
},
|
},
|
||||||
"job_payments": {
|
"job_payments": {
|
||||||
"buttons": {
|
"buttons": {
|
||||||
|
"create_short_link": "",
|
||||||
"goback": "",
|
"goback": "",
|
||||||
"proceedtopayment": "",
|
"proceedtopayment": "",
|
||||||
"refundpayment": ""
|
"refundpayment": ""
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -939,6 +939,7 @@
|
|||||||
- inhousevendorid
|
- inhousevendorid
|
||||||
- insurance_vendor_id
|
- insurance_vendor_id
|
||||||
- intakechecklist
|
- intakechecklist
|
||||||
|
- intellipay_config
|
||||||
- jc_hourly_rates
|
- jc_hourly_rates
|
||||||
- jobsizelimit
|
- jobsizelimit
|
||||||
- last_name_first
|
- last_name_first
|
||||||
@@ -1040,6 +1041,7 @@
|
|||||||
- inhousevendorid
|
- inhousevendorid
|
||||||
- insurance_vendor_id
|
- insurance_vendor_id
|
||||||
- intakechecklist
|
- intakechecklist
|
||||||
|
- intellipay_config
|
||||||
- jc_hourly_rates
|
- jc_hourly_rates
|
||||||
- last_name_first
|
- last_name_first
|
||||||
- localmediaserverhttp
|
- localmediaserverhttp
|
||||||
@@ -4240,6 +4242,63 @@
|
|||||||
- active:
|
- active:
|
||||||
_eq: true
|
_eq: true
|
||||||
event_triggers:
|
event_triggers:
|
||||||
|
- name: job_modified
|
||||||
|
definition:
|
||||||
|
enable_manual: false
|
||||||
|
update:
|
||||||
|
columns:
|
||||||
|
- clm_no
|
||||||
|
- v_make_desc
|
||||||
|
- date_next_contact
|
||||||
|
- status
|
||||||
|
- employee_csr
|
||||||
|
- employee_prep
|
||||||
|
- clm_total
|
||||||
|
- suspended
|
||||||
|
- employee_body
|
||||||
|
- ro_number
|
||||||
|
- actual_in
|
||||||
|
- ownr_co_nm
|
||||||
|
- v_model_yr
|
||||||
|
- comment
|
||||||
|
- job_totals
|
||||||
|
- v_vin
|
||||||
|
- ownr_fn
|
||||||
|
- scheduled_completion
|
||||||
|
- special_coverage_policy
|
||||||
|
- v_color
|
||||||
|
- ca_gst_registrant
|
||||||
|
- scheduled_delivery
|
||||||
|
- actual_delivery
|
||||||
|
- actual_completion
|
||||||
|
- kanbanparent
|
||||||
|
- est_ct_fn
|
||||||
|
- employee_refinish
|
||||||
|
- ownr_ph1
|
||||||
|
- date_last_contacted
|
||||||
|
- alt_transport
|
||||||
|
- inproduction
|
||||||
|
- est_ct_ln
|
||||||
|
- production_vars
|
||||||
|
- category
|
||||||
|
- v_model_desc
|
||||||
|
- date_invoiced
|
||||||
|
- est_co_nm
|
||||||
|
- ownr_ln
|
||||||
|
retry_conf:
|
||||||
|
interval_sec: 10
|
||||||
|
num_retries: 0
|
||||||
|
timeout_sec: 60
|
||||||
|
webhook_from_env: HASURA_API_URL
|
||||||
|
headers:
|
||||||
|
- name: event-secret
|
||||||
|
value_from_env: EVENT_SECRET
|
||||||
|
request_transform:
|
||||||
|
method: POST
|
||||||
|
query_params: {}
|
||||||
|
template_engine: Kriti
|
||||||
|
url: '{{$base_url}}/job/job-updated'
|
||||||
|
version: 2
|
||||||
- name: job_status_transition
|
- name: job_status_transition
|
||||||
definition:
|
definition:
|
||||||
enable_manual: true
|
enable_manual: true
|
||||||
|
|||||||
@@ -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 "intellipay_config" jsonb
|
||||||
|
-- not null default jsonb_build_object();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
alter table "public"."bodyshops" add column "intellipay_config" jsonb
|
||||||
|
not null default jsonb_build_object();
|
||||||
@@ -94,8 +94,9 @@ const formatPriority = (priority) => {
|
|||||||
* @param taskId
|
* @param taskId
|
||||||
* @returns {{header, body: string, subHeader: string}}
|
* @returns {{header, body: string, subHeader: string}}
|
||||||
*/
|
*/
|
||||||
const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId) => {
|
|
||||||
const endPoints = InstanceManager({
|
const getEndpoints = () =>
|
||||||
|
InstanceManager({
|
||||||
imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online",
|
imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online",
|
||||||
rome:
|
rome:
|
||||||
bodyshop.convenient_company === "promanager"
|
bodyshop.convenient_company === "promanager"
|
||||||
@@ -106,6 +107,9 @@ const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, j
|
|||||||
? "https//test.romeonline.io"
|
? "https//test.romeonline.io"
|
||||||
: "https://romeonline.io"
|
: "https://romeonline.io"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId) => {
|
||||||
|
const endPoints = getEndpoints();
|
||||||
return {
|
return {
|
||||||
header: title,
|
header: title,
|
||||||
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)}`,
|
subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)}`,
|
||||||
@@ -333,5 +337,6 @@ const tasksRemindEmail = async (req, res) => {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
taskAssignedEmail,
|
taskAssignedEmail,
|
||||||
tasksRemindEmail
|
tasksRemindEmail,
|
||||||
|
getEndpoints
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2502,6 +2502,13 @@ exports.GET_JOBS_BY_PKS = `query GET_JOBS_BY_PKS($ids: [uuid!]!) {
|
|||||||
jobs(where: {id: {_in: $ids}}) {
|
jobs(where: {id: {_in: $ids}}) {
|
||||||
id
|
id
|
||||||
shopid
|
shopid
|
||||||
|
ro_number
|
||||||
|
ownr_co_nm
|
||||||
|
ownr_fn
|
||||||
|
ownr_ln
|
||||||
|
v_make_desc
|
||||||
|
v_model_yr
|
||||||
|
v_model_desc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ const axios = require("axios");
|
|||||||
const moment = require("moment");
|
const moment = require("moment");
|
||||||
const logger = require("../utils/logger");
|
const logger = require("../utils/logger");
|
||||||
const InstanceManager = require("../utils/instanceMgr").default;
|
const InstanceManager = require("../utils/instanceMgr").default;
|
||||||
|
const { sendTaskEmail } = require("../email/sendemail");
|
||||||
|
const generateEmailTemplate = require("../email/generateTemplate");
|
||||||
|
const { getEndpoints } = require("../email/tasksEmails");
|
||||||
require("dotenv").config({
|
require("dotenv").config({
|
||||||
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
|
||||||
});
|
});
|
||||||
@@ -129,6 +131,7 @@ exports.generate_payment_url = async (req, res) => {
|
|||||||
//...req.body,
|
//...req.body,
|
||||||
amount: Dinero({ amount: Math.round(req.body.amount * 100) }).toFormat("0.00"),
|
amount: Dinero({ amount: Math.round(req.body.amount * 100) }).toFormat("0.00"),
|
||||||
account: req.body.account,
|
account: req.body.account,
|
||||||
|
comment: req.body.comment,
|
||||||
invoice: req.body.invoice,
|
invoice: req.body.invoice,
|
||||||
createshorturl: true
|
createshorturl: true
|
||||||
//The postback URL is set at the CP teller global terminal settings page.
|
//The postback URL is set at the CP teller global terminal settings page.
|
||||||
@@ -162,7 +165,67 @@ exports.postback = async (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.invoice) {
|
if (comment) {
|
||||||
|
//Shifted the order to have this first to retain backwards compatibility for the old style of short link.
|
||||||
|
//This has been triggered by IO and may have multiple jobs.
|
||||||
|
const parsedComment = JSON.parse(comment);
|
||||||
|
|
||||||
|
//Adding in the user email to the short pay email.
|
||||||
|
//Need to check this to ensure backwards compatibility for clients that don't update.
|
||||||
|
|
||||||
|
const partialPayments = Array.isArray(parsedComment) ? parsedComment : parsedComment.payments;
|
||||||
|
|
||||||
|
const jobs = await gqlClient.request(queries.GET_JOBS_BY_PKS, {
|
||||||
|
ids: partialPayments.map((p) => p.jobid)
|
||||||
|
});
|
||||||
|
|
||||||
|
const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, {
|
||||||
|
paymentInput: partialPayments.map((p) => ({
|
||||||
|
amount: p.amount,
|
||||||
|
transactionid: values.authcode,
|
||||||
|
payer: "Customer",
|
||||||
|
type: values.cardtype,
|
||||||
|
jobid: p.jobid,
|
||||||
|
date: moment(Date.now()),
|
||||||
|
payment_responses: {
|
||||||
|
data: {
|
||||||
|
amount: values.total,
|
||||||
|
bodyshopid: jobs.jobs[0].shopid,
|
||||||
|
jobid: p.jobid,
|
||||||
|
declinereason: "Approved",
|
||||||
|
ext_paymentid: values.paymentid,
|
||||||
|
successful: true,
|
||||||
|
response: values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
logger.log("intellipay-postback-app-success", "DEBUG", req.user?.email, null, {
|
||||||
|
iprequest: values,
|
||||||
|
paymentResult
|
||||||
|
});
|
||||||
|
|
||||||
|
if (values.origin === "OneLink" && parsedComment.userEmail) {
|
||||||
|
//Send an email, it was a text to pay link.
|
||||||
|
const endPoints = getEndpoints();
|
||||||
|
sendTaskEmail({
|
||||||
|
to: parsedComment.userEmail,
|
||||||
|
subject: `New Payment(s) Received - RO ${jobs.jobs.map((j) => j.ro_number).join(", ")}`,
|
||||||
|
type: "html",
|
||||||
|
html: generateEmailTemplate({
|
||||||
|
header: "New Payment(s) Received",
|
||||||
|
subHeader: "",
|
||||||
|
body: jobs.jobs
|
||||||
|
.map(
|
||||||
|
(job) =>
|
||||||
|
`Reference: <a href="${endPoints}/manage/jobs/${job.id}">${job.ro_number || "N/A"}</a> | ${job.ownr_co_nm ? job.ownr_co_nm : `${job.ownr_fn || ""} ${job.ownr_ln || ""}`.trim()} | ${`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim()} | $${partialPayments.find((p) => p.jobid === job.id).amount}`
|
||||||
|
)
|
||||||
|
.join("<br/>")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
res.sendStatus(200);
|
||||||
|
}
|
||||||
|
} else if (values.invoice) {
|
||||||
//This is a link email that's been sent out.
|
//This is a link email that's been sent out.
|
||||||
const job = await gqlClient.request(queries.GET_JOB_BY_PK, {
|
const job = await gqlClient.request(queries.GET_JOB_BY_PK, {
|
||||||
id: values.invoice
|
id: values.invoice
|
||||||
@@ -198,39 +261,6 @@ exports.postback = async (req, res) => {
|
|||||||
paymentResult
|
paymentResult
|
||||||
});
|
});
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
} else if (comment) {
|
|
||||||
//This has been triggered by IO and may have multiple jobs.
|
|
||||||
const partialPayments = JSON.parse(comment);
|
|
||||||
const jobs = await gqlClient.request(queries.GET_JOBS_BY_PKS, {
|
|
||||||
ids: partialPayments.map((p) => p.jobid)
|
|
||||||
});
|
|
||||||
|
|
||||||
const paymentResult = await gqlClient.request(queries.INSERT_NEW_PAYMENT, {
|
|
||||||
paymentInput: partialPayments.map((p) => ({
|
|
||||||
amount: p.amount,
|
|
||||||
transactionid: values.authcode,
|
|
||||||
payer: "Customer",
|
|
||||||
type: values.cardtype,
|
|
||||||
jobid: p.jobid,
|
|
||||||
date: moment(Date.now()),
|
|
||||||
payment_responses: {
|
|
||||||
data: {
|
|
||||||
amount: values.total,
|
|
||||||
bodyshopid: jobs.jobs[0].shopid,
|
|
||||||
jobid: p.jobid,
|
|
||||||
declinereason: "Approved",
|
|
||||||
ext_paymentid: values.paymentid,
|
|
||||||
successful: true,
|
|
||||||
response: values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
logger.log("intellipay-postback-app-success", "DEBUG", req.user?.email, null, {
|
|
||||||
iprequest: values,
|
|
||||||
paymentResult
|
|
||||||
});
|
|
||||||
res.sendStatus(200);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("intellipay-postback-error", "ERROR", req.user?.email, null, {
|
logger.log("intellipay-postback-error", "ERROR", req.user?.email, null, {
|
||||||
|
|||||||
Reference in New Issue
Block a user