Added reminaing balance to payments screen BOD-231
This commit is contained in:
@@ -13749,6 +13749,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>pae</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>pal</name>
|
<name>pal</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -20102,6 +20123,48 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>labels</name>
|
<name>labels</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>balance</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>customer</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>electronicpayment</name>
|
<name>electronicpayment</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -20123,6 +20186,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>insurance</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>new</name>
|
<name>new</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -20186,6 +20270,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>totalpayments</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>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { Divider, Typography } from "antd";
|
import { Button, Divider, Space, Statistic, Typography } from "antd";
|
||||||
import React from "react";
|
import Dinero from "dinero.js";
|
||||||
|
import React, { useMemo } 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 { setModalContext } from "../../redux/modals/modals.actions";
|
||||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
||||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||||
@@ -12,63 +14,110 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
bodyshop: selectBodyshop,
|
bodyshop: selectBodyshop,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
setPaymentContext: (context) =>
|
||||||
|
dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||||
|
});
|
||||||
|
|
||||||
const stripeTestEnv = process.env.REACT_APP_STRIPE_PUBLIC_KEY; //.includes("test");
|
const stripeTestEnv = process.env.REACT_APP_STRIPE_PUBLIC_KEY; //.includes("test");
|
||||||
|
|
||||||
export function JobsDetailTotals({ job, bodyshop }) {
|
export function JobsDetailTotals({
|
||||||
|
job,
|
||||||
|
bodyshop,
|
||||||
|
setPaymentContext,
|
||||||
|
refetch,
|
||||||
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const total = useMemo(() => {
|
||||||
|
return (
|
||||||
|
job.payments &&
|
||||||
|
job.payments.reduce((acc, val) => {
|
||||||
|
acc = acc.add(Dinero({ amount: Math.round(val.amount * 100) }));
|
||||||
|
return acc;
|
||||||
|
}, Dinero())
|
||||||
|
);
|
||||||
|
}, [job.payments]);
|
||||||
|
|
||||||
|
const balance = useMemo(() => {
|
||||||
|
return Dinero(job.job_totals.totals.total_repairs).subtract(total);
|
||||||
|
}, [job.job_totals.totals.total_repairs, total]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Typography.Title level={4}>
|
<Typography.Title level={4}>
|
||||||
{t("payments.labels.title")}
|
{t("payments.labels.title")}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<table style={{ width: "100%" }}>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{t("payments.fields.created_at")}</th>
|
|
||||||
<th>{t("payments.fields.payer")}</th>
|
|
||||||
<th>{t("payments.fields.amount")}</th>
|
|
||||||
<th>{t("payments.fields.memo")}</th>
|
|
||||||
<th>{t("payments.fields.type")}</th>
|
|
||||||
<th>{t("payments.fields.transactionid")}</th>
|
|
||||||
<th>{t("payments.fields.stripeid")}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{job.payments.map((p, idx) => (
|
|
||||||
<tr key={idx}>
|
|
||||||
<td>
|
|
||||||
<DateTimeFormatter>{p.created_at}</DateTimeFormatter>
|
|
||||||
</td>
|
|
||||||
<td>{p.payer}</td>
|
|
||||||
<td>
|
|
||||||
<CurrencyFormatter>{p.amount}</CurrencyFormatter>
|
|
||||||
</td>
|
|
||||||
<td>{p.memo}</td>
|
|
||||||
<td>{p.type}</td>
|
|
||||||
<td>{p.transactionid}</td>
|
|
||||||
<td>
|
|
||||||
{p.stripeid ? (
|
|
||||||
<a
|
|
||||||
href={
|
|
||||||
stripeTestEnv
|
|
||||||
? `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/test/payments/${p.stripeid}`
|
|
||||||
: `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/payments/${p.stripeid}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{p.stripeid}
|
|
||||||
</a>
|
|
||||||
) : null}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
<div className="imex-flex-row">
|
||||||
|
<table style={{ flex: 1 }}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{t("payments.fields.created_at")}</th>
|
||||||
|
<th>{t("payments.fields.payer")}</th>
|
||||||
|
<th>{t("payments.fields.amount")}</th>
|
||||||
|
<th>{t("payments.fields.memo")}</th>
|
||||||
|
<th>{t("payments.fields.type")}</th>
|
||||||
|
<th>{t("payments.fields.transactionid")}</th>
|
||||||
|
<th>{t("payments.fields.stripeid")}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{job.payments.map((p, idx) => (
|
||||||
|
<tr key={idx}>
|
||||||
|
<td>
|
||||||
|
<DateTimeFormatter>{p.created_at}</DateTimeFormatter>
|
||||||
|
</td>
|
||||||
|
<td>{p.payer}</td>
|
||||||
|
<td>
|
||||||
|
<CurrencyFormatter>{p.amount}</CurrencyFormatter>
|
||||||
|
</td>
|
||||||
|
<td>{p.memo}</td>
|
||||||
|
<td>{p.type}</td>
|
||||||
|
<td>{p.transactionid}</td>
|
||||||
|
<td>
|
||||||
|
{p.stripeid ? (
|
||||||
|
<a
|
||||||
|
href={
|
||||||
|
stripeTestEnv
|
||||||
|
? `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/test/payments/${p.stripeid}`
|
||||||
|
: `https://dashboard.stripe.com/${bodyshop.stripe_acct_id}/payments/${p.stripeid}`
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{p.stripeid}
|
||||||
|
</a>
|
||||||
|
) : null}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<Space direction="vertical">
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
setPaymentContext({
|
||||||
|
actions: { refetch: refetch },
|
||||||
|
context: { jobId: job.id },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t("menus.header.enterpayment")}
|
||||||
|
</Button>
|
||||||
|
<Statistic
|
||||||
|
title={t("payments.labels.totalpayments")}
|
||||||
|
value={total.toFormat()}
|
||||||
|
/>
|
||||||
|
<Statistic
|
||||||
|
title={t("payments.labels.balance")}
|
||||||
|
valueStyle={{ color: balance.getAmount() > 0 ? "red" : "green" }}
|
||||||
|
value={balance.toFormat()}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<JobTotalsTable job={job} />
|
<JobTotalsTable job={job} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default connect(mapStateToProps, null)(JobsDetailTotals);
|
export default connect(mapStateToProps, mapDispatchToProps)(JobsDetailTotals);
|
||||||
|
|||||||
@@ -59,6 +59,26 @@ export function PaymentFormComponent({ form, stripeStateArr, bodyshop }) {
|
|||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
label={t("payments.fields.payer")}
|
||||||
|
name="payer"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t("general.validation.required"),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
<Select.Option value={t("payments.labels.customer")}>
|
||||||
|
{t("payments.labels.customer")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value={t("payments.labels.insurance")}>
|
||||||
|
{t("payments.labels.insurance")}
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("payments.fields.type")}
|
label={t("payments.fields.type")}
|
||||||
name="type"
|
name="type"
|
||||||
@@ -75,6 +95,7 @@ export function PaymentFormComponent({ form, stripeStateArr, bodyshop }) {
|
|||||||
<Select.Option value="AMEX">AMEX</Select.Option>
|
<Select.Option value="AMEX">AMEX</Select.Option>
|
||||||
<Select.Option value="Discover">Discover</Select.Option>
|
<Select.Option value="Discover">Discover</Select.Option>
|
||||||
<Select.Option value="Cash">Cash</Select.Option>
|
<Select.Option value="Cash">Cash</Select.Option>
|
||||||
|
<Select.Option value="EFT">EFT</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ export function JobsDetailPage({
|
|||||||
}
|
}
|
||||||
key="totals"
|
key="totals"
|
||||||
>
|
>
|
||||||
<JobsDetailTotals job={job} />
|
<JobsDetailTotals job={job} refetch={refetch} />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
<Tabs.TabPane
|
<Tabs.TabPane
|
||||||
tab={
|
tab={
|
||||||
|
|||||||
@@ -867,6 +867,7 @@
|
|||||||
"ownr_ea": "Email",
|
"ownr_ea": "Email",
|
||||||
"ownr_ph1": "Phone 1",
|
"ownr_ph1": "Phone 1",
|
||||||
"paa": "Aftermarket",
|
"paa": "Aftermarket",
|
||||||
|
"pae": "Existing",
|
||||||
"pal": "LKQ",
|
"pal": "LKQ",
|
||||||
"pam": "Remanufactured",
|
"pam": "Remanufactured",
|
||||||
"pan": "OEM/New",
|
"pan": "OEM/New",
|
||||||
@@ -1240,10 +1241,14 @@
|
|||||||
"type": "Type"
|
"type": "Type"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"balance": "Balance",
|
||||||
|
"customer": "Customer",
|
||||||
"electronicpayment": "Use Electronic Payment Processing?",
|
"electronicpayment": "Use Electronic Payment Processing?",
|
||||||
|
"insurance": "Insurance",
|
||||||
"new": "New Payment",
|
"new": "New Payment",
|
||||||
"signup": "Please contact support to sign up for electronic payments.",
|
"signup": "Please contact support to sign up for electronic payments.",
|
||||||
"title": "Payments"
|
"title": "Payments",
|
||||||
|
"totalpayments": "Total Payments"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"payment": "Payment created successfully. ",
|
"payment": "Payment created successfully. ",
|
||||||
|
|||||||
@@ -867,6 +867,7 @@
|
|||||||
"ownr_ea": "Email",
|
"ownr_ea": "Email",
|
||||||
"ownr_ph1": "Teléfono 1",
|
"ownr_ph1": "Teléfono 1",
|
||||||
"paa": "",
|
"paa": "",
|
||||||
|
"pae": "",
|
||||||
"pal": "",
|
"pal": "",
|
||||||
"pam": "",
|
"pam": "",
|
||||||
"pan": "",
|
"pan": "",
|
||||||
@@ -1240,10 +1241,14 @@
|
|||||||
"type": ""
|
"type": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"balance": "",
|
||||||
|
"customer": "",
|
||||||
"electronicpayment": "",
|
"electronicpayment": "",
|
||||||
|
"insurance": "",
|
||||||
"new": "",
|
"new": "",
|
||||||
"signup": "",
|
"signup": "",
|
||||||
"title": ""
|
"title": "",
|
||||||
|
"totalpayments": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"payment": "",
|
"payment": "",
|
||||||
|
|||||||
@@ -867,6 +867,7 @@
|
|||||||
"ownr_ea": "Email",
|
"ownr_ea": "Email",
|
||||||
"ownr_ph1": "Téléphone 1",
|
"ownr_ph1": "Téléphone 1",
|
||||||
"paa": "",
|
"paa": "",
|
||||||
|
"pae": "",
|
||||||
"pal": "",
|
"pal": "",
|
||||||
"pam": "",
|
"pam": "",
|
||||||
"pan": "",
|
"pan": "",
|
||||||
@@ -1240,10 +1241,14 @@
|
|||||||
"type": ""
|
"type": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
|
"balance": "",
|
||||||
|
"customer": "",
|
||||||
"electronicpayment": "",
|
"electronicpayment": "",
|
||||||
|
"insurance": "",
|
||||||
"new": "",
|
"new": "",
|
||||||
"signup": "",
|
"signup": "",
|
||||||
"title": ""
|
"title": "",
|
||||||
|
"totalpayments": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"payment": "",
|
"payment": "",
|
||||||
|
|||||||
@@ -29,9 +29,10 @@ Handlebars.registerHelper("dinerof", function (context, block) {
|
|||||||
Handlebars.registerHelper("objectKeys", function (obj, block) {
|
Handlebars.registerHelper("objectKeys", function (obj, block) {
|
||||||
var accum = "";
|
var accum = "";
|
||||||
|
|
||||||
Object.keys(obj).map((key) => {
|
obj &&
|
||||||
accum += block.fn({ key, value: obj[key] });
|
Object.keys(obj).map((key) => {
|
||||||
});
|
accum += block.fn({ key, value: obj[key] });
|
||||||
|
});
|
||||||
return accum;
|
return accum;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user