+
-
- {JSON.stringify(record.date_exported)}
+
+
+
),
},
diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx
index 85ae5c631..17c538f9f 100644
--- a/client/src/components/header/header.component.jsx
+++ b/client/src/components/header/header.component.jsx
@@ -229,6 +229,11 @@ function Header({
{t("menus.header.accounting-receivables")}
+
+
+ {t("menus.header.accounting-payables")}
+
+
diff --git a/client/src/components/invoice-export-button/invoice-export-button.component.jsx b/client/src/components/invoice-export-button/invoice-export-button.component.jsx
index e9ee6f068..724bd5a0b 100644
--- a/client/src/components/invoice-export-button/invoice-export-button.component.jsx
+++ b/client/src/components/invoice-export-button/invoice-export-button.component.jsx
@@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { auth } from "../../firebase/firebase.utils";
-import { UPDATE_INVOICE } from "../../graphql/invoices.queries";
+import { UPDATE_INVOICES } from "../../graphql/invoices.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -19,7 +19,7 @@ export function InvoiceExportButton({
loadingCallback,
}) {
const { t } = useTranslation();
- const [updateInvoice] = useMutation(UPDATE_INVOICE);
+ const [updateInvoice] = useMutation(UPDATE_INVOICES);
const [loading, setLoading] = useState(false);
const handleQbxml = async () => {
@@ -51,6 +51,7 @@ export function InvoiceExportButton({
}
let PartnerResponse;
+
try {
PartnerResponse = await axios.post(
"http://e9c5a8ed9079.ngrok.io/qb/",
@@ -66,26 +67,42 @@ export function InvoiceExportButton({
return;
}
- const invoiceUpdateResponse = await updateInvoice({
- variables: {
- invoiceId: invoiceId,
- invoice: {
- exported: true,
- exported_at: new Date(),
+ 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),
- }),
});
+ if (!!!invoiceUpdateResponse.errors) {
+ notification["success"]({
+ message: t("jobs.successes.exported"),
+ });
+ } else {
+ notification["error"]({
+ message: t("jobs.errors.exporting", {
+ error: JSON.stringify(invoiceUpdateResponse.error),
+ }),
+ });
+ }
}
if (!!loadingCallback) loadingCallback(false);
diff --git a/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx b/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx
index 405e9c877..cbaaff6bf 100644
--- a/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx
+++ b/client/src/components/jobs-close-export-button/jobs-close-export-button.component.jsx
@@ -44,7 +44,7 @@ export function JobsCloseExportButton({ bodyshop, jobId, disabled }) {
let PartnerResponse;
try {
PartnerResponse = await axios.post(
- "http://localhost:1337/qb/",
+ "http://e9c5a8ed9079.ngrok.io/qb/",
QbXmlResponse.data,
{
headers: {
@@ -62,26 +62,40 @@ export function JobsCloseExportButton({ bodyshop, jobId, disabled }) {
}
console.log("PartnerResponse", PartnerResponse);
- const jobUpdateResponse = await updateJob({
- variables: {
- jobId: jobId,
- job: {
- status: bodyshop.md_ro_statuses.default_exported || "Exported*",
- date_exported: new Date(),
- },
- },
- });
- if (!!!jobUpdateResponse.errors) {
- notification["success"]({
- message: t("jobs.successes.exported"),
- });
+ //Check to see if any of them failed. If they didn't don't execute the update.
+ const failedTransactions = 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("jobs.errors.exporting", {
+ error: ft.errorMessage || "",
+ }),
+ })
+ );
} else {
- notification["error"]({
- message: t("jobs.errors.exporting", {
- error: JSON.stringify(jobUpdateResponse.error),
- }),
+ const jobUpdateResponse = await updateJob({
+ variables: {
+ jobId: jobId,
+ job: {
+ status: bodyshop.md_ro_statuses.default_exported || "Exported*",
+ date_exported: new Date(),
+ },
+ },
});
+
+ if (!!!jobUpdateResponse.errors) {
+ notification["success"]({
+ message: t("jobs.successes.exported"),
+ });
+ } else {
+ notification["error"]({
+ message: t("jobs.errors.exporting", {
+ error: JSON.stringify(jobUpdateResponse.error),
+ }),
+ });
+ }
}
setLoading(false);
diff --git a/client/src/graphql/invoices.queries.js b/client/src/graphql/invoices.queries.js
index 539a95339..456ef02be 100644
--- a/client/src/graphql/invoices.queries.js
+++ b/client/src/graphql/invoices.queries.js
@@ -135,3 +135,17 @@ export const UPDATE_INVOICE = gql`
}
}
`;
+export const UPDATE_INVOICES = gql`
+ mutation UPDATE_INVOICES(
+ $invoiceIdList: [uuid!]!
+ $invoice: invoices_set_input!
+ ) {
+ update_invoices(where: { id: { _in: $invoiceIdList } }, _set: $invoice) {
+ returning {
+ id
+ exported
+ exported_at
+ }
+ }
+ }
+`;
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index 1ecccb897..0226308ce 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -706,7 +706,8 @@
"suspense": "Suspense",
"total_repairs": "Total Repairs",
"totals": "Totals",
- "vehicle_info": "Vehicle"
+ "vehicle_info": "Vehicle",
+ "viewallocations": "View Allocations"
},
"successes": {
"addedtoproduction": "Job added to production board.",
@@ -731,6 +732,7 @@
},
"header": {
"accounting": "Accounting",
+ "accounting-payables": "Payables",
"accounting-receivables": "Receivables",
"activejobs": "Active Jobs",
"alljobs": "All Jobs",
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index 123be1be9..8ad37cc7d 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -706,7 +706,8 @@
"suspense": "",
"total_repairs": "",
"totals": "",
- "vehicle_info": "Vehículo"
+ "vehicle_info": "Vehículo",
+ "viewallocations": ""
},
"successes": {
"addedtoproduction": "",
@@ -731,6 +732,7 @@
},
"header": {
"accounting": "",
+ "accounting-payables": "",
"accounting-receivables": "",
"activejobs": "Empleos activos",
"alljobs": "",
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index cbb57cfb5..b5f824dab 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -706,7 +706,8 @@
"suspense": "",
"total_repairs": "",
"totals": "",
- "vehicle_info": "Véhicule"
+ "vehicle_info": "Véhicule",
+ "viewallocations": ""
},
"successes": {
"addedtoproduction": "",
@@ -731,6 +732,7 @@
},
"header": {
"accounting": "",
+ "accounting-payables": "",
"accounting-receivables": "",
"activejobs": "Emplois actifs",
"alljobs": "",
diff --git a/server/accounting/qbxml/qbxml-payables.js b/server/accounting/qbxml/qbxml-payables.js
index 478e61987..edcf34180 100644
--- a/server/accounting/qbxml/qbxml-payables.js
+++ b/server/accounting/qbxml/qbxml-payables.js
@@ -32,7 +32,11 @@ exports.default = async (req, res) => {
const QbXmlToExecute = [];
invoices.map((i) => {
- QbXmlToExecute.push(generateBill(i));
+ QbXmlToExecute.push({
+ id: i.id,
+ okStatusCodes: ["0"],
+ qbxml: generateBill(i),
+ });
});
//For each invoice.
diff --git a/server/accounting/qbxml/qbxml-receivables.js b/server/accounting/qbxml/qbxml-receivables.js
index e65fd1d47..1c16f5da7 100644
--- a/server/accounting/qbxml/qbxml-receivables.js
+++ b/server/accounting/qbxml/qbxml-receivables.js
@@ -33,14 +33,30 @@ exports.default = async (req, res) => {
//Is this a two tier, or 3 tier setup?
const isThreeTier = bodyshop.accountingconfig.tiers === 3;
if (isThreeTier) {
- QbXmlToExecute.push(
- generateSourceCustomerQbxml(jobs_by_pk, bodyshop) // Create the source customer.
- );
+ QbXmlToExecute.push({
+ id: jobId,
+ okStatusCodes: ["0", "3100"],
+ qbxml: generateSourceCustomerQbxml(jobs_by_pk, bodyshop), // Create the source customer.
+ });
}
- QbXmlToExecute.push(generateJobQbxml(jobs_by_pk, bodyshop, isThreeTier, 2));
- QbXmlToExecute.push(generateJobQbxml(jobs_by_pk, bodyshop, isThreeTier, 3));
+
+ QbXmlToExecute.push({
+ id: jobId,
+ okStatusCodes: ["0", "3100"],
+ qbxml: generateJobQbxml(jobs_by_pk, bodyshop, isThreeTier, 2),
+ });
+
+ QbXmlToExecute.push({
+ id: jobId,
+ okStatusCodes: ["0", "3100"],
+ qbxml: generateJobQbxml(jobs_by_pk, bodyshop, isThreeTier, 3),
+ });
//Generate the actual invoice.
- QbXmlToExecute.push(generateInvoiceQbxml(jobs_by_pk, bodyshop));
+ QbXmlToExecute.push({
+ id: jobId,
+ okStatusCodes: ["0"],
+ qbxml: generateInvoiceQbxml(jobs_by_pk, bodyshop),
+ });
res.status(200).json(QbXmlToExecute);
} catch (error) {
@@ -196,7 +212,6 @@ const generateInvoiceQbxml = (jobs_by_pk, bodyshop) => {
FullName: bodyshop.md_responsibility_centers.taxes.federal.accountitem,
},
Desc: bodyshop.md_responsibility_centers.taxes.federal.accountdesc,
- //Quantity: 1,
Amount: federal_tax.toFormat(DineroQbFormat),
});
}
@@ -207,7 +222,6 @@ const generateInvoiceQbxml = (jobs_by_pk, bodyshop) => {
FullName: bodyshop.md_responsibility_centers.taxes.state.accountitem,
},
Desc: bodyshop.md_responsibility_centers.taxes.state.accountdesc,
- //Quantity: 1,
Amount: state_tax.toFormat(DineroQbFormat),
});
}
@@ -218,7 +232,6 @@ const generateInvoiceQbxml = (jobs_by_pk, bodyshop) => {
FullName: bodyshop.md_responsibility_centers.taxes.local.accountitem,
},
Desc: bodyshop.md_responsibility_centers.taxes.local.accountdesc,
- //Quantity: 1,
Amount: local_tax.toFormat(DineroQbFormat),
});
}
diff --git a/server/accounting/qbxml/qbxmlObject.json b/server/accounting/qbxml/qbxmlObject.json
new file mode 100644
index 000000000..85d61be8c
--- /dev/null
+++ b/server/accounting/qbxml/qbxmlObject.json
@@ -0,0 +1,5 @@
+{
+ "id": "12345",
+ "okStatusCodes": ["0", "31400"],
+ "qbxml": "the qbxml string"
+}