Added untested changes for exporting all receivables BOD-240
This commit is contained in:
@@ -39,7 +39,8 @@ export default function AccountingPayablesTableComponent({
|
|||||||
to={{
|
to={{
|
||||||
pathname: `/manage/shop/vendors`,
|
pathname: `/manage/shop/vendors`,
|
||||||
search: queryString.stringify({ selectedvendor: record.vendor.id }),
|
search: queryString.stringify({ selectedvendor: record.vendor.id }),
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{record.vendor.name}
|
{record.vendor.name}
|
||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
@@ -60,7 +61,8 @@ export default function AccountingPayablesTableComponent({
|
|||||||
invoiceid: record.id,
|
invoiceid: record.id,
|
||||||
vendorid: record.vendor.id,
|
vendorid: record.vendor.id,
|
||||||
}),
|
}),
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
{record.invoice_number}
|
{record.invoice_number}
|
||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
@@ -160,7 +162,7 @@ export default function AccountingPayablesTableComponent({
|
|||||||
/>
|
/>
|
||||||
<InvoiceExportAllButton
|
<InvoiceExportAllButton
|
||||||
invoiceIds={selectedInvoices}
|
invoiceIds={selectedInvoices}
|
||||||
disabled={transInProgress}
|
disabled={transInProgress || selectedInvoices.length === 0}
|
||||||
loadingCallback={setTransInProgress}
|
loadingCallback={setTransInProgress}
|
||||||
completedCallback={setSelectedInvoices}
|
completedCallback={setSelectedInvoices}
|
||||||
/>
|
/>
|
||||||
@@ -168,10 +170,10 @@ export default function AccountingPayablesTableComponent({
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
size='small'
|
size="small"
|
||||||
pagination={{ position: "top", pageSize: 50 }}
|
pagination={{ position: "top", pageSize: 50 }}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey='id'
|
rowKey="id"
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
rowSelection={{
|
rowSelection={{
|
||||||
onSelectAll: (selected, selectedRows) =>
|
onSelectAll: (selected, selectedRows) =>
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import CurrencyFormatter from "../../utils/CurrencyFormatter";
|
|||||||
import { alphaSort } from "../../utils/sorters";
|
import { alphaSort } from "../../utils/sorters";
|
||||||
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
|
import JobExportButton from "../jobs-close-export-button/jobs-close-export-button.component";
|
||||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
import { JobsExportAllButton } from "../jobs-export-all-button/jobs-export-all-button.component";
|
||||||
|
|
||||||
export default function AccountingReceivablesTableComponent({ loading, jobs }) {
|
export default function AccountingReceivablesTableComponent({ loading, jobs }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [selectedJobs, setSelectedJobs] = useState([]);
|
||||||
|
const [transInProgress, setTransInProgress] = useState(false);
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
sortedInfo: {},
|
sortedInfo: {},
|
||||||
@@ -178,8 +181,15 @@ export default function AccountingReceivablesTableComponent({ loading, jobs }) {
|
|||||||
loading={loading}
|
loading={loading}
|
||||||
title={() => {
|
title={() => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="imex-table-header">
|
||||||
<Input
|
<JobsExportAllButton
|
||||||
|
jobIds={selectedJobs}
|
||||||
|
disabled={transInProgress || selectedJobs.length === 0}
|
||||||
|
loadingCallback={setTransInProgress}
|
||||||
|
completedCallback={setSelectedJobs}
|
||||||
|
/>
|
||||||
|
<Input.Search
|
||||||
|
className="imex-table-header__search"
|
||||||
value={state.search}
|
value={state.search}
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
placeholder={t("general.labels.search")}
|
placeholder={t("general.labels.search")}
|
||||||
@@ -189,11 +199,23 @@ export default function AccountingReceivablesTableComponent({ loading, jobs }) {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
dataSource={dataSource}
|
dataSource={dataSource}
|
||||||
size='small'
|
size="small"
|
||||||
pagination={{ position: "top" }}
|
pagination={{ position: "top" }}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey='id'
|
rowKey="id"
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
|
rowSelection={{
|
||||||
|
onSelectAll: (selected, selectedRows) =>
|
||||||
|
setSelectedJobs(selectedRows.map((i) => i.id)),
|
||||||
|
onSelect: (record, selected, selectedRows, nativeEvent) => {
|
||||||
|
setSelectedJobs(selectedRows.map((i) => i.id));
|
||||||
|
},
|
||||||
|
getCheckboxProps: (record) => ({
|
||||||
|
disabled: record.exported,
|
||||||
|
}),
|
||||||
|
selectedRowKeys: selectedJobs,
|
||||||
|
type: "checkbox",
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function JobsCloseExportButton({ bodyshop, jobId, disabled }) {
|
|||||||
try {
|
try {
|
||||||
QbXmlResponse = await axios.post(
|
QbXmlResponse = await axios.post(
|
||||||
"/accounting/qbxml/receivables",
|
"/accounting/qbxml/receivables",
|
||||||
{ jobId: jobId },
|
{ jobIds: [jobId] },
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
|
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
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_JOB, UPDATE_JOBS } from "../../graphql/jobs.queries";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
export function JobsExportAllButton({
|
||||||
|
bodyshop,
|
||||||
|
jobIds,
|
||||||
|
disabled,
|
||||||
|
loadingCallback,
|
||||||
|
completedCallback,
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [updateJob] = useMutation(UPDATE_JOBS);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const handleQbxml = async () => {
|
||||||
|
logImEXEvent("jobs_export_all");
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
let QbXmlResponse;
|
||||||
|
try {
|
||||||
|
QbXmlResponse = await axios.post(
|
||||||
|
"/accounting/qbxml/receivables",
|
||||||
|
{ jobIds: jobIds },
|
||||||
|
{
|
||||||
|
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("jobs.errors.exporting", {
|
||||||
|
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let PartnerResponse;
|
||||||
|
try {
|
||||||
|
PartnerResponse = await axios.post(
|
||||||
|
"http://localhost:1337/qb/",
|
||||||
|
// "http://609feaeae986.ngrok.io/qb/",
|
||||||
|
QbXmlResponse.data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error connecting to quickbooks or partner.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("jobs.errors.exporting-partner"),
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("PartnerResponse", PartnerResponse);
|
||||||
|
|
||||||
|
//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);
|
||||||
|
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("jobs.errors.exporting", {
|
||||||
|
error: ft.errorMessage || "",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (successfulTransactions.length > 0) {
|
||||||
|
const jobUpdateResponse = await updateJob({
|
||||||
|
variables: {
|
||||||
|
jobIds: successfulTransactions.map((st) => st.id),
|
||||||
|
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),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Set the list of selected invoices to be nothing.
|
||||||
|
if (!!completedCallback) completedCallback([]);
|
||||||
|
if (!!loadingCallback) loadingCallback(false);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={handleQbxml}
|
||||||
|
loading={loading}
|
||||||
|
disabled={disabled}
|
||||||
|
type="dashed"
|
||||||
|
>
|
||||||
|
{t("jobs.actions.export")}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(JobsExportAllButton);
|
||||||
@@ -16,7 +16,7 @@ const { generateJobTier, generateOwnerTier, generateSourceTier } = QbXmlUtils;
|
|||||||
|
|
||||||
exports.default = async (req, res) => {
|
exports.default = async (req, res) => {
|
||||||
const BearerToken = req.headers.authorization;
|
const BearerToken = req.headers.authorization;
|
||||||
const { jobId } = req.body;
|
const { jobIds } = req.body;
|
||||||
|
|
||||||
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -27,51 +27,56 @@ exports.default = async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const result = await client
|
const result = await client
|
||||||
.setHeaders({ Authorization: BearerToken })
|
.setHeaders({ Authorization: BearerToken })
|
||||||
.request(queries.QUERY_JOBS_FOR_RECEIVABLES_EXPORT, { id: jobId });
|
.request(queries.QUERY_JOBS_FOR_RECEIVABLES_EXPORT, { ids: jobIds });
|
||||||
const { jobs_by_pk } = result;
|
console.log("result", result);
|
||||||
const { bodyshop } = jobs_by_pk;
|
const { jobs } = result;
|
||||||
|
const { bodyshops } = result;
|
||||||
const QbXmlToExecute = [];
|
const QbXmlToExecute = [];
|
||||||
|
|
||||||
//Is this a two tier, or 3 tier setup?
|
const bodyshop = bodyshops[0];
|
||||||
const isThreeTier = bodyshop.accountingconfig.tiers === 3;
|
|
||||||
const twoTierPref = bodyshop.accountingconfig.twotierpref;
|
jobs.map((jobs_by_pk) => {
|
||||||
|
//Is this a two tier, or 3 tier setup?
|
||||||
|
const isThreeTier = bodyshop.accountingconfig.tiers === 3;
|
||||||
|
const twoTierPref = bodyshop.accountingconfig.twotierpref;
|
||||||
|
|
||||||
|
if (isThreeTier) {
|
||||||
|
QbXmlToExecute.push({
|
||||||
|
id: jobs_by_pk.id,
|
||||||
|
okStatusCodes: ["0", "3100"],
|
||||||
|
qbxml: generateSourceCustomerQbxml(jobs_by_pk, bodyshop), // Create the source customer.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (isThreeTier) {
|
|
||||||
QbXmlToExecute.push({
|
QbXmlToExecute.push({
|
||||||
id: jobId,
|
id: jobs_by_pk.id,
|
||||||
okStatusCodes: ["0", "3100"],
|
okStatusCodes: ["0", "3100"],
|
||||||
qbxml: generateSourceCustomerQbxml(jobs_by_pk, bodyshop), // Create the source customer.
|
qbxml: generateJobQbxml(
|
||||||
|
jobs_by_pk,
|
||||||
|
bodyshop,
|
||||||
|
isThreeTier,
|
||||||
|
2,
|
||||||
|
twoTierPref
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
QbXmlToExecute.push({
|
QbXmlToExecute.push({
|
||||||
id: jobId,
|
id: jobs_by_pk.id,
|
||||||
okStatusCodes: ["0", "3100"],
|
okStatusCodes: ["0", "3100"],
|
||||||
qbxml: generateJobQbxml(
|
qbxml: generateJobQbxml(
|
||||||
jobs_by_pk,
|
jobs_by_pk,
|
||||||
bodyshop,
|
bodyshop,
|
||||||
isThreeTier,
|
isThreeTier,
|
||||||
2,
|
3,
|
||||||
twoTierPref
|
twoTierPref
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
//Generate the actual invoice.
|
||||||
QbXmlToExecute.push({
|
QbXmlToExecute.push({
|
||||||
id: jobId,
|
id: jobs_by_pk.id,
|
||||||
okStatusCodes: ["0", "3100"],
|
okStatusCodes: ["0"],
|
||||||
qbxml: generateJobQbxml(
|
qbxml: generateInvoiceQbxml(jobs_by_pk, bodyshop),
|
||||||
jobs_by_pk,
|
});
|
||||||
bodyshop,
|
|
||||||
isThreeTier,
|
|
||||||
3,
|
|
||||||
twoTierPref
|
|
||||||
),
|
|
||||||
});
|
|
||||||
//Generate the actual invoice.
|
|
||||||
QbXmlToExecute.push({
|
|
||||||
id: jobId,
|
|
||||||
okStatusCodes: ["0"],
|
|
||||||
qbxml: generateInvoiceQbxml(jobs_by_pk, bodyshop),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).json(QbXmlToExecute);
|
res.status(200).json(QbXmlToExecute);
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ mutation UPDATE_MESSAGE($msid: String!, $fields: messages_set_input!) {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
exports.QUERY_JOBS_FOR_RECEIVABLES_EXPORT = `
|
exports.QUERY_JOBS_FOR_RECEIVABLES_EXPORT = `
|
||||||
query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($id: uuid!) {
|
query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($ids: [uuid!]!) {
|
||||||
jobs_by_pk(id: $id) {
|
jobs(where: {id: {_in: $ids}}) {
|
||||||
id
|
id
|
||||||
job_totals
|
job_totals
|
||||||
date_invoiced
|
date_invoiced
|
||||||
@@ -59,14 +59,14 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($id: uuid!) {
|
|||||||
ownr_city
|
ownr_city
|
||||||
ownr_st
|
ownr_st
|
||||||
ins_co_nm
|
ins_co_nm
|
||||||
owner{
|
owner {
|
||||||
accountingid
|
accountingid
|
||||||
}
|
}
|
||||||
bodyshop {
|
}
|
||||||
id
|
bodyshops(where: {associations: {active: {_eq: true}}}) {
|
||||||
md_responsibility_centers
|
id
|
||||||
accountingconfig
|
md_responsibility_centers
|
||||||
}
|
accountingconfig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
Reference in New Issue
Block a user