From 910183d8e711b59458cf9254ca3bacb3df5f60c4 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Thu, 4 Jun 2020 09:53:08 -0700 Subject: [PATCH] Added bulk payables exporting with better error handling. BOD-152 --- bodyshop_translations.babel | 21 +++ .../accounting-payables-table.component.jsx | 12 +- .../invoice-export-all-button.component.jsx | 127 ++++++++++++++++++ client/src/translations/en_us/common.json | 1 + client/src/translations/es/common.json | 1 + client/src/translations/fr/common.json | 1 + 6 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 client/src/components/invoice-export-all-button/invoice-export-all-button.component.jsx diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel index 8190e76ab..c9ef73550 100644 --- a/bodyshop_translations.babel +++ b/bodyshop_translations.babel @@ -7521,6 +7521,27 @@ + + exportselected + false + + + + + + en-US + false + + + es-MX + false + + + fr-CA + false + + + gotojob false diff --git a/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx b/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx index 613e9a151..87598f74b 100644 --- a/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx +++ b/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx @@ -5,6 +5,7 @@ import { Link } from "react-router-dom"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { alphaSort } from "../../utils/sorters"; import InvoiceExportButton from "../invoice-export-button/invoice-export-button.component"; +import InvoiceExportAllButton from "../invoice-export-all-button/invoice-export-all-button.component"; import { DateFormatter } from "../../utils/DateFormatter"; import queryString from "query-string"; @@ -119,8 +120,7 @@ export default function AccountingPayablesTableComponent({ render: (text, record) => (
@@ -128,7 +128,7 @@ export default function AccountingPayablesTableComponent({ ), }, ]; -console.log('transInProgress', transInProgress) + console.log("transInProgress", transInProgress); const handleSearch = (e) => { setState({ ...state, search: e.target.value }); }; @@ -158,6 +158,12 @@ console.log('transInProgress', transInProgress) placeholder={t("general.labels.search")} allowClear /> +
); }} diff --git a/client/src/components/invoice-export-all-button/invoice-export-all-button.component.jsx b/client/src/components/invoice-export-all-button/invoice-export-all-button.component.jsx new file mode 100644 index 000000000..1f74bef10 --- /dev/null +++ b/client/src/components/invoice-export-all-button/invoice-export-all-button.component.jsx @@ -0,0 +1,127 @@ +import { useMutation } from "@apollo/react-hooks"; +import { Button, notification } from "antd"; +import axios from "axios"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { connect } from "react-redux"; +import { createStructuredSelector } from "reselect"; +import { auth } from "../../firebase/firebase.utils"; +import { UPDATE_INVOICES } from "../../graphql/invoices.queries"; +import { selectBodyshop } from "../../redux/user/user.selectors"; +const mapStateToProps = createStructuredSelector({ + bodyshop: selectBodyshop, +}); + +export function InvoiceExportAllButton({ + bodyshop, + invoiceIds, + disabled, + loadingCallback, + completedCallback, +}) { + const { t } = useTranslation(); + const [updateInvoice] = useMutation(UPDATE_INVOICES); + const [loading, setLoading] = useState(false); + + const handleQbxml = async () => { + setLoading(true); + if (!!loadingCallback) loadingCallback(true); + + let QbXmlResponse; + try { + QbXmlResponse = await axios.post( + "/accounting/qbxml/payables", + { invoices: invoiceIds }, + { + headers: { + Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`, + }, + } + ); + console.log("handle -> XML", QbXmlResponse); + } catch (error) { + console.log("Error getting QBXML from Server.", error); + notification["error"]({ + message: t("invoices.errors.exporting", { + error: "Unable to retrieve QBXML. " + JSON.stringify(error.message), + }), + }); + if (loadingCallback) loadingCallback(false); + setLoading(false); + return; + } + + let PartnerResponse; + + try { + PartnerResponse = await axios.post( + "http://48f9a09ccdec.ngrok.io/qb/", + QbXmlResponse.data + ); + } catch (error) { + console.log("Error connecting to quickbooks or partner.", error); + notification["error"]({ + message: t("invoices.errors.exporting-partner"), + }); + if (!!loadingCallback) loadingCallback(false); + setLoading(false); + return; + } + + console.log("handleQbxml -> PartnerResponse", PartnerResponse); + const failedTransactions = PartnerResponse.data.filter((r) => !r.success); + const successfulTransactions = PartnerResponse.data.filter( + (r) => r.success + ); + if (failedTransactions.length > 0) { + //Uh oh. At least one was no good. + failedTransactions.map((ft) => + notification["error"]({ + message: t("invoices.errors.exporting", { + error: ft.errorMessage || "", + }), + }) + ); + } + if (successfulTransactions.length > 0) { + const invoiceUpdateResponse = await updateInvoice({ + variables: { + invoiceIdList: successfulTransactions.map((st) => st.id), + invoice: { + exported: true, + exported_at: new Date(), + }, + }, + }); + if (!!!invoiceUpdateResponse.errors) { + notification["success"]({ + message: t("jobs.successes.exported"), + }); + } else { + notification["error"]({ + message: t("jobs.errors.exporting", { + error: JSON.stringify(invoiceUpdateResponse.error), + }), + }); + } + } + + //Set the list of selected invoices to be nothing. + if (!!completedCallback) completedCallback([]); + if (!!loadingCallback) loadingCallback(false); + setLoading(false); + }; + + return ( + + ); +} + +export default connect(mapStateToProps, null)(InvoiceExportAllButton); diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index ac4aace5a..f11d64f57 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -507,6 +507,7 @@ "changestatus": "Change Status", "convert": "Convert", "export": "Export", + "exportselected": "Export Selected", "gotojob": "Go to Job", "manualnew": "Create New Job Manually", "mark": "Mark", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index ca92e2a96..43523c3d0 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -507,6 +507,7 @@ "changestatus": "Cambiar Estado", "convert": "Convertir", "export": "", + "exportselected": "", "gotojob": "", "manualnew": "", "mark": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index de7a545dc..c4793e5d4 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -507,6 +507,7 @@ "changestatus": "Changer le statut", "convert": "Convertir", "export": "", + "exportselected": "", "gotojob": "", "manualnew": "", "mark": "",