Added reminaing balance to payments screen BOD-231

This commit is contained in:
Patrick Fic
2020-09-08 10:23:25 -07:00
parent af8e39da75
commit 92ee64a413
8 changed files with 244 additions and 53 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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