IO-256 Exporting of payments
This commit is contained in:
@@ -8,8 +8,26 @@ import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
|
|||||||
import { alphaSort, dateSort } from "../../utils/sorters";
|
import { alphaSort, dateSort } from "../../utils/sorters";
|
||||||
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
import PaymentExportButton from "../payment-export-button/payment-export-button.component";
|
||||||
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
|
import PaymentsExportAllButton from "../payments-export-all-button/payments-export-all-button.component";
|
||||||
|
import QboAuthorizeComponent from "../qbo-authorize/qbo-authorize.component";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
export default function AccountingPayablesTableComponent({
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(AccountingPayablesTableComponent);
|
||||||
|
|
||||||
|
export function AccountingPayablesTableComponent({
|
||||||
|
bodyshop,
|
||||||
loading,
|
loading,
|
||||||
payments,
|
payments,
|
||||||
}) {
|
}) {
|
||||||
@@ -163,6 +181,9 @@ export default function AccountingPayablesTableComponent({
|
|||||||
loadingCallback={setTransInProgress}
|
loadingCallback={setTransInProgress}
|
||||||
completedCallback={setSelectedPayments}
|
completedCallback={setSelectedPayments}
|
||||||
/>
|
/>
|
||||||
|
{bodyshop.accountingconfig && bodyshop.accountingconfig.qbo && (
|
||||||
|
<QboAuthorizeComponent />
|
||||||
|
)}
|
||||||
<Input
|
<Input
|
||||||
value={state.search}
|
value={state.search}
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { useLazyQuery } from "@apollo/client";
|
||||||
import { Input, Modal } from "antd";
|
import { Input, Modal } from "antd";
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useLazyQuery, useQuery } from "@apollo/client";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { QUERY_SEARCH_OWNER_BY_IDX } from "../../graphql/owners.queries";
|
import { QUERY_SEARCH_OWNER_BY_IDX } from "../../graphql/owners.queries";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
|
|||||||
@@ -33,55 +33,64 @@ export function PaymentExportButton({
|
|||||||
|
|
||||||
const handleQbxml = async () => {
|
const handleQbxml = async () => {
|
||||||
logImEXEvent("accounting_payment_export");
|
logImEXEvent("accounting_payment_export");
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
if (!!loadingCallback) loadingCallback(true);
|
//Check if it's a QBO Setup.
|
||||||
|
|
||||||
let QbXmlResponse;
|
|
||||||
try {
|
|
||||||
QbXmlResponse = await axios.post(
|
|
||||||
"/accounting/qbxml/payments",
|
|
||||||
{ payments: [paymentId] },
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
console.log("handle -> XML", QbXmlResponse);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error getting QBXML from Server.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("payments.errors.exporting", {
|
|
||||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
if (loadingCallback) loadingCallback(false);
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let PartnerResponse;
|
let PartnerResponse;
|
||||||
|
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||||
try {
|
PartnerResponse = await axios.post(`/qbo/payments`, {
|
||||||
PartnerResponse = await axios.post(
|
withCredentials: true,
|
||||||
"http://localhost:1337/qb/",
|
payments: [paymentId],
|
||||||
//"http://609feaeae986.ngrok.io/qb/",
|
|
||||||
QbXmlResponse.data
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error connecting to quickbooks or partner.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("payments.errors.exporting-partner"),
|
|
||||||
});
|
});
|
||||||
if (!!loadingCallback) loadingCallback(false);
|
} else {
|
||||||
setLoading(false);
|
//Default is QBD
|
||||||
return;
|
|
||||||
|
if (!!loadingCallback) loadingCallback(true);
|
||||||
|
|
||||||
|
let QbXmlResponse;
|
||||||
|
try {
|
||||||
|
QbXmlResponse = await axios.post(
|
||||||
|
"/accounting/qbxml/payments",
|
||||||
|
{ payments: [paymentId] },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log("handle -> XML", QbXmlResponse);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error getting QBXML from Server.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("payments.errors.exporting", {
|
||||||
|
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (loadingCallback) loadingCallback(false);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PartnerResponse = await axios.post(
|
||||||
|
"http://localhost:1337/qb/",
|
||||||
|
QbXmlResponse.data
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error connecting to quickbooks or partner.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("payments.errors.exporting-partner"),
|
||||||
|
});
|
||||||
|
if (!!loadingCallback) loadingCallback(false);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
|
console.log("handleQbxml -> PartnerResponse", PartnerResponse);
|
||||||
const failedTransactions = PartnerResponse.data.filter((r) => !r.success);
|
const failedTransactions = PartnerResponse.data.filter((r) => !r.success);
|
||||||
|
const successfulTransactions = PartnerResponse.data.filter(
|
||||||
|
(r) => r.success
|
||||||
|
);
|
||||||
if (failedTransactions.length > 0) {
|
if (failedTransactions.length > 0) {
|
||||||
//Uh oh. At least one was no good.
|
//Uh oh. At least one was no good.
|
||||||
failedTransactions.map((ft) =>
|
failedTransactions.map((ft) =>
|
||||||
@@ -123,7 +132,14 @@ export function PaymentExportButton({
|
|||||||
|
|
||||||
const paymentUpdateResponse = await updatePayment({
|
const paymentUpdateResponse = await updatePayment({
|
||||||
variables: {
|
variables: {
|
||||||
paymentIdList: [paymentId],
|
paymentIdList: successfulTransactions.map(
|
||||||
|
(st) =>
|
||||||
|
st[
|
||||||
|
bodyshop.accountingconfig && bodyshop.accountingconfig.qbo
|
||||||
|
? "paymentid"
|
||||||
|
: "id"
|
||||||
|
]
|
||||||
|
),
|
||||||
payment: {
|
payment: {
|
||||||
exportedat: new Date(),
|
exportedat: new Date(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -33,42 +33,50 @@ export function PaymentsExportAllButton({
|
|||||||
const handleQbxml = async () => {
|
const handleQbxml = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
if (!!loadingCallback) loadingCallback(true);
|
if (!!loadingCallback) loadingCallback(true);
|
||||||
|
let PartnerResponse;
|
||||||
let QbXmlResponse;
|
if (bodyshop.accountingconfig && bodyshop.accountingconfig.qbo) {
|
||||||
try {
|
PartnerResponse = await axios.post(`/qbo/payments`, {
|
||||||
QbXmlResponse = await axios.post("/accounting/qbxml/payments", {
|
withCredentials: true,
|
||||||
payments: paymentIds,
|
payments: paymentIds,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} else {
|
||||||
console.log("Error getting QBXML from Server.", error);
|
let QbXmlResponse;
|
||||||
notification["error"]({
|
try {
|
||||||
message: t("payments.errors.exporting", {
|
QbXmlResponse = await axios.post("/accounting/qbxml/payments", {
|
||||||
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
payments: paymentIds,
|
||||||
}),
|
});
|
||||||
});
|
} catch (error) {
|
||||||
if (loadingCallback) loadingCallback(false);
|
console.log("Error getting QBXML from Server.", error);
|
||||||
setLoading(false);
|
notification["error"]({
|
||||||
return;
|
message: t("payments.errors.exporting", {
|
||||||
|
error: "Unable to retrieve QBXML. " + JSON.stringify(error.message),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (loadingCallback) loadingCallback(false);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
PartnerResponse = await axios.post(
|
||||||
|
"http://localhost:1337/qb/",
|
||||||
|
QbXmlResponse.data
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error connecting to quickbooks or partner.", error);
|
||||||
|
notification["error"]({
|
||||||
|
message: t("payments.errors.exporting-partner"),
|
||||||
|
});
|
||||||
|
if (!!loadingCallback) loadingCallback(false);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let PartnerResponse;
|
const groupedData = _.groupBy(
|
||||||
|
PartnerResponse.data,
|
||||||
try {
|
bodyshop.accountingconfig.qbo ? "paymentid" : "id"
|
||||||
PartnerResponse = await axios.post(
|
);
|
||||||
"http://localhost:1337/qb/",
|
|
||||||
QbXmlResponse.data
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error connecting to quickbooks or partner.", error);
|
|
||||||
notification["error"]({
|
|
||||||
message: t("payments.errors.exporting-partner"),
|
|
||||||
});
|
|
||||||
if (!!loadingCallback) loadingCallback(false);
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const groupedData = _.groupBy(PartnerResponse.data, "id");
|
|
||||||
const proms = [];
|
const proms = [];
|
||||||
Object.keys(groupedData).forEach((key) => {
|
Object.keys(groupedData).forEach((key) => {
|
||||||
proms.push(
|
proms.push(
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ app.post("/qbo/authorize", fb.validateFirebaseIdToken, qbo.authorize);
|
|||||||
app.get("/qbo/callback", qbo.callback);
|
app.get("/qbo/callback", qbo.callback);
|
||||||
app.post("/qbo/receivables", fb.validateFirebaseIdToken, qbo.receivables);
|
app.post("/qbo/receivables", fb.validateFirebaseIdToken, qbo.receivables);
|
||||||
app.post("/qbo/payables", fb.validateFirebaseIdToken, qbo.payables);
|
app.post("/qbo/payables", fb.validateFirebaseIdToken, qbo.payables);
|
||||||
|
app.post("/qbo/payments", fb.validateFirebaseIdToken, qbo.payments);
|
||||||
|
|
||||||
var data = require("./server/data/data");
|
var data = require("./server/data/data");
|
||||||
app.post("/data/ah", data.autohouse);
|
app.post("/data/ah", data.autohouse);
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ exports.default = async (req, res) => {
|
|||||||
ret.push({
|
ret.push({
|
||||||
billid: bill.id,
|
billid: bill.id,
|
||||||
success: false,
|
success: false,
|
||||||
errorMessage: error.message,
|
errorMessage:
|
||||||
|
(error && error.authResponse.body) || JSON.stringify(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,7 @@ async function QueryVendorRecord(oauthClient, req, bill) {
|
|||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||||
error: JSON.stringify(error),
|
error: (error && error.authResponse.body) || JSON.stringify(error),
|
||||||
method: "QueryVendorRecord",
|
method: "QueryVendorRecord",
|
||||||
});
|
});
|
||||||
throw error;
|
throw error;
|
||||||
@@ -136,7 +137,7 @@ async function InsertVendorRecord(oauthClient, req, bill) {
|
|||||||
return result && result.Vendor;
|
return result && result.Vendor;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||||
error: JSON.stringify(error),
|
error: (error && error.authResponse.body) || JSON.stringify(error),
|
||||||
method: "InsertVendorRecord",
|
method: "InsertVendorRecord",
|
||||||
});
|
});
|
||||||
throw error;
|
throw error;
|
||||||
@@ -185,7 +186,7 @@ async function InsertBill(oauthClient, req, bill, vendor) {
|
|||||||
return result && result.Bill;
|
return result && result.Bill;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||||
error: JSON.stringify(error),
|
error: (error && error.authResponse.body) || JSON.stringify(error),
|
||||||
method: "InsertBill",
|
method: "InsertBill",
|
||||||
});
|
});
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -0,0 +1,274 @@
|
|||||||
|
const path = require("path");
|
||||||
|
require("dotenv").config({
|
||||||
|
path: path.resolve(
|
||||||
|
process.cwd(),
|
||||||
|
`.env.${process.env.NODE_ENV || "development"}`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
const logger = require("../../utils/logger");
|
||||||
|
const Dinero = require("dinero.js");
|
||||||
|
|
||||||
|
const apiGqlClient = require("../../graphql-client/graphql-client").client;
|
||||||
|
const queries = require("../../graphql-client/queries");
|
||||||
|
const {
|
||||||
|
refresh: refreshOauthToken,
|
||||||
|
setNewRefreshToken,
|
||||||
|
} = require("./qbo-callback");
|
||||||
|
const OAuthClient = require("intuit-oauth");
|
||||||
|
const moment = require("moment");
|
||||||
|
const GraphQLClient = require("graphql-request").GraphQLClient;
|
||||||
|
const {
|
||||||
|
QueryInsuranceCo,
|
||||||
|
InsertInsuranceCo,
|
||||||
|
InsertJob,
|
||||||
|
InsertOwner,
|
||||||
|
QueryJob,
|
||||||
|
QueryOwner,
|
||||||
|
} = require("../qbo/qbo-receivables");
|
||||||
|
const { urlBuilder } = require("./qbo");
|
||||||
|
const { DineroQbFormat } = require("../accounting-constants");
|
||||||
|
|
||||||
|
exports.default = async (req, res) => {
|
||||||
|
const oauthClient = new OAuthClient({
|
||||||
|
clientId: process.env.QBO_CLIENT_ID,
|
||||||
|
clientSecret: process.env.QBO_SECRET,
|
||||||
|
environment:
|
||||||
|
process.env.NODE_ENV === "production" ? "production" : "sandbox",
|
||||||
|
redirectUri: process.env.QBO_REDIRECT_URI,
|
||||||
|
logging: true,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
//Fetch the API Access Tokens & Set them for the session.
|
||||||
|
const response = await apiGqlClient.request(queries.GET_QBO_AUTH, {
|
||||||
|
email: req.user.email,
|
||||||
|
});
|
||||||
|
|
||||||
|
oauthClient.setToken(response.associations[0].qbo_auth);
|
||||||
|
|
||||||
|
await refreshOauthToken(oauthClient, req);
|
||||||
|
|
||||||
|
const BearerToken = req.headers.authorization;
|
||||||
|
const { payments: paymentsToQuery } = req.body;
|
||||||
|
//Query Job Info
|
||||||
|
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {
|
||||||
|
headers: {
|
||||||
|
Authorization: BearerToken,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
logger.log("qbo-payment-create", "DEBUG", req.user.email, paymentsToQuery);
|
||||||
|
const result = await client
|
||||||
|
.setHeaders({ Authorization: BearerToken })
|
||||||
|
.request(queries.QUERY_PAYMENTS_FOR_EXPORT, {
|
||||||
|
payments: paymentsToQuery,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { payments, bodyshops } = result;
|
||||||
|
const bodyshop = bodyshops[0];
|
||||||
|
|
||||||
|
const ret = [];
|
||||||
|
|
||||||
|
for (const payment of payments) {
|
||||||
|
try {
|
||||||
|
const isThreeTier = bodyshop.accountingconfig.tiers === 3;
|
||||||
|
const twoTierPref = bodyshop.accountingconfig.twotierpref;
|
||||||
|
|
||||||
|
//Replace this with a for-each loop to check every single Job that's included in the list.
|
||||||
|
|
||||||
|
let insCoCustomerTier, ownerCustomerTier, jobTier;
|
||||||
|
if (isThreeTier || twoTierPref === "source") {
|
||||||
|
//Insert the insurance company tier.
|
||||||
|
//Query for top level customer, the insurance company name.
|
||||||
|
insCoCustomerTier = await QueryInsuranceCo(
|
||||||
|
oauthClient,
|
||||||
|
req,
|
||||||
|
payment.job
|
||||||
|
);
|
||||||
|
if (!insCoCustomerTier) {
|
||||||
|
//Creating the Insurance Customer.
|
||||||
|
insCoCustomerTier = await InsertInsuranceCo(
|
||||||
|
oauthClient,
|
||||||
|
req,
|
||||||
|
payment.job,
|
||||||
|
bodyshop
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isThreeTier || twoTierPref === "name") {
|
||||||
|
//Insert the name/owner and account for whether the source should be the ins co in 3 tier..
|
||||||
|
ownerCustomerTier = await QueryOwner(oauthClient, req, payment.job);
|
||||||
|
//Query for the owner itself.
|
||||||
|
if (!ownerCustomerTier) {
|
||||||
|
ownerCustomerTier = await InsertOwner(
|
||||||
|
oauthClient,
|
||||||
|
req,
|
||||||
|
payment.job,
|
||||||
|
isThreeTier,
|
||||||
|
insCoCustomerTier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Query for the Job or Create it.
|
||||||
|
jobTier = await QueryJob(oauthClient, req, payment.job);
|
||||||
|
|
||||||
|
// Need to validate that the job tier is associated to the right individual?
|
||||||
|
|
||||||
|
if (!jobTier) {
|
||||||
|
jobTier = await InsertJob(
|
||||||
|
oauthClient,
|
||||||
|
req,
|
||||||
|
payment.job,
|
||||||
|
isThreeTier,
|
||||||
|
ownerCustomerTier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await InsertPayment(oauthClient, req, payment, jobTier);
|
||||||
|
ret.push({ paymentid: payment.id, success: true });
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("qbo-payment-create-error", "ERROR", req.user.email, {
|
||||||
|
error: (error && error.authResponse.body) || JSON.stringify(error),
|
||||||
|
});
|
||||||
|
|
||||||
|
ret.push({
|
||||||
|
paymentid: payment.id,
|
||||||
|
success: false,
|
||||||
|
errorMessage:
|
||||||
|
(error && error.authResponse.body) || JSON.stringify(error),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json(ret);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
logger.log("qbo-payment-create-error", "ERROR", req.user.email, { error });
|
||||||
|
res.status(400).json(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function InsertPayment(oauthClient, req, payment, parentRef) {
|
||||||
|
const { paymentMethods, invoices } = await QueryMetaData(
|
||||||
|
oauthClient,
|
||||||
|
req,
|
||||||
|
payment.job.ro_number
|
||||||
|
);
|
||||||
|
|
||||||
|
if (invoices.length !== 1) {
|
||||||
|
throw new Error(
|
||||||
|
`More than 1 invoice with DocNumber ${payment.ro_number} found.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const paymentQbo = {
|
||||||
|
CustomerRef: {
|
||||||
|
value: parentRef.Id,
|
||||||
|
},
|
||||||
|
TxnDate: moment(payment.date).format("YYYY-MM-DD"),
|
||||||
|
//DueDate: bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
|
||||||
|
DocNumber: payment.paymentnum,
|
||||||
|
TotalAmt: Dinero({
|
||||||
|
amount: Math.round(payment.amount * 100),
|
||||||
|
}).toFormat(DineroQbFormat),
|
||||||
|
PaymentMethodRef: {
|
||||||
|
value: paymentMethods[payment.type],
|
||||||
|
},
|
||||||
|
Line: [
|
||||||
|
{
|
||||||
|
Amount: Dinero({
|
||||||
|
amount: Math.round(payment.amount * 100),
|
||||||
|
}).toFormat(DineroQbFormat),
|
||||||
|
LinkedTxn: [
|
||||||
|
{
|
||||||
|
TxnId: invoices[0].Id,
|
||||||
|
TxnType: "Invoice",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const result = await oauthClient.makeApiCall({
|
||||||
|
url: urlBuilder(req.cookies.qbo_realmId, "payment"),
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(paymentQbo),
|
||||||
|
});
|
||||||
|
setNewRefreshToken(req.user.email, result);
|
||||||
|
return result && result.Bill;
|
||||||
|
} catch (error) {
|
||||||
|
logger.log("qbo-payables-error", "DEBUG", req.user.email, payment.id, {
|
||||||
|
error: JSON.stringify(error),
|
||||||
|
method: "InsertPayment",
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function QueryMetaData(oauthClient, req, ro_number) {
|
||||||
|
const invoice = await oauthClient.makeApiCall({
|
||||||
|
url: urlBuilder(
|
||||||
|
req.cookies.qbo_realmId,
|
||||||
|
"query",
|
||||||
|
`select * From Invoice where DocNumber = '${ro_number}'`
|
||||||
|
),
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const paymentMethods = await oauthClient.makeApiCall({
|
||||||
|
url: urlBuilder(
|
||||||
|
req.cookies.qbo_realmId,
|
||||||
|
"query",
|
||||||
|
`select * From PaymentMethod`
|
||||||
|
),
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setNewRefreshToken(req.user.email, paymentMethods);
|
||||||
|
|
||||||
|
// const classes = await oauthClient.makeApiCall({
|
||||||
|
// url: urlBuilder(req.cookies.qbo_realmId, "query", `select * From Class`),
|
||||||
|
// method: "POST",
|
||||||
|
// headers: {
|
||||||
|
// "Content-Type": "application/json",
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
const paymentMethodMapping = {};
|
||||||
|
|
||||||
|
paymentMethods.json &&
|
||||||
|
paymentMethods.json.QueryResponse &&
|
||||||
|
paymentMethods.json.QueryResponse.PaymentMethod.forEach((t) => {
|
||||||
|
paymentMethodMapping[t.Name] = t.Id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// const accountMapping = {};
|
||||||
|
|
||||||
|
// accounts.json &&
|
||||||
|
// accounts.json.QueryResponse &&
|
||||||
|
// accounts.json.QueryResponse.Account.forEach((t) => {
|
||||||
|
// accountMapping[t.Name] = t.Id;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const classMapping = {};
|
||||||
|
// classes.json &&
|
||||||
|
// classes.json.QueryResponse &&
|
||||||
|
// classes.json.QueryResponse.Class.forEach((t) => {
|
||||||
|
// accountMapping[t.Name] = t.Id;
|
||||||
|
// });
|
||||||
|
|
||||||
|
return {
|
||||||
|
paymentMethods: paymentMethodMapping,
|
||||||
|
invoices:
|
||||||
|
invoice.json &&
|
||||||
|
invoice.json.QueryResponse &&
|
||||||
|
invoice.json.QueryResponse.Invoice,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ exports.default = async (req, res) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(insCoCustomerTier);
|
|
||||||
if (isThreeTier || twoTierPref === "name") {
|
if (isThreeTier || twoTierPref === "name") {
|
||||||
//Insert the name/owner and account for whether the source should be the ins co in 3 tier..
|
//Insert the name/owner and account for whether the source should be the ins co in 3 tier..
|
||||||
ownerCustomerTier = await QueryOwner(oauthClient, req, job);
|
ownerCustomerTier = await QueryOwner(oauthClient, req, job);
|
||||||
@@ -95,7 +95,7 @@ exports.default = async (req, res) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(ownerCustomerTier);
|
|
||||||
//Query for the Job or Create it.
|
//Query for the Job or Create it.
|
||||||
jobTier = await QueryJob(oauthClient, req, job);
|
jobTier = await QueryJob(oauthClient, req, job);
|
||||||
|
|
||||||
@@ -110,14 +110,15 @@ exports.default = async (req, res) => {
|
|||||||
ownerCustomerTier
|
ownerCustomerTier
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
console.log(jobTier);
|
|
||||||
await InsertInvoice(oauthClient, req, job, bodyshop, jobTier);
|
await InsertInvoice(oauthClient, req, job, bodyshop, jobTier);
|
||||||
ret.push({ jobid: job.id, success: true });
|
ret.push({ jobid: job.id, success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ret.push({
|
ret.push({
|
||||||
jobid: job.id,
|
jobid: job.id,
|
||||||
success: false,
|
success: false,
|
||||||
errorMessage: error.message,
|
errorMessage:
|
||||||
|
(error && error.authResponse.body) || JSON.stringify(error),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,6 +161,7 @@ async function QueryInsuranceCo(oauthClient, req, job) {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exports.QueryInsuranceCo = QueryInsuranceCo;
|
||||||
async function InsertInsuranceCo(oauthClient, req, job, bodyshop) {
|
async function InsertInsuranceCo(oauthClient, req, job, bodyshop) {
|
||||||
const insCo = bodyshop.md_ins_cos.find((i) => i.name === job.ins_co_nm);
|
const insCo = bodyshop.md_ins_cos.find((i) => i.name === job.ins_co_nm);
|
||||||
|
|
||||||
@@ -192,7 +194,7 @@ async function InsertInsuranceCo(oauthClient, req, job, bodyshop) {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exports.InsertInsuranceCo = InsertInsuranceCo;
|
||||||
async function QueryOwner(oauthClient, req, job) {
|
async function QueryOwner(oauthClient, req, job) {
|
||||||
const ownerName = generateOwnerTier(job, true, null);
|
const ownerName = generateOwnerTier(job, true, null);
|
||||||
const result = await oauthClient.makeApiCall({
|
const result = await oauthClient.makeApiCall({
|
||||||
@@ -214,7 +216,7 @@ async function QueryOwner(oauthClient, req, job) {
|
|||||||
result.json.QueryResponse.Customer[0]
|
result.json.QueryResponse.Customer[0]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
exports.QueryOwner = QueryOwner;
|
||||||
async function InsertOwner(oauthClient, req, job, isThreeTier, parentTierRef) {
|
async function InsertOwner(oauthClient, req, job, isThreeTier, parentTierRef) {
|
||||||
const ownerName = generateOwnerTier(job, true, null);
|
const ownerName = generateOwnerTier(job, true, null);
|
||||||
const Customer = {
|
const Customer = {
|
||||||
@@ -254,7 +256,7 @@ async function InsertOwner(oauthClient, req, job, isThreeTier, parentTierRef) {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exports.InsertOwner = InsertOwner;
|
||||||
async function QueryJob(oauthClient, req, job) {
|
async function QueryJob(oauthClient, req, job) {
|
||||||
const result = await oauthClient.makeApiCall({
|
const result = await oauthClient.makeApiCall({
|
||||||
url: urlBuilder(
|
url: urlBuilder(
|
||||||
@@ -275,7 +277,7 @@ async function QueryJob(oauthClient, req, job) {
|
|||||||
result.json.QueryResponse.Customer[0]
|
result.json.QueryResponse.Customer[0]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
exports.QueryJob = QueryJob;
|
||||||
async function InsertJob(oauthClient, req, job, isThreeTier, parentTierRef) {
|
async function InsertJob(oauthClient, req, job, isThreeTier, parentTierRef) {
|
||||||
const Customer = {
|
const Customer = {
|
||||||
DisplayName: job.ro_number,
|
DisplayName: job.ro_number,
|
||||||
@@ -314,7 +316,7 @@ async function InsertJob(oauthClient, req, job, isThreeTier, parentTierRef) {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exports.InsertJob = InsertJob;
|
||||||
async function QueryMetaData(oauthClient, req) {
|
async function QueryMetaData(oauthClient, req) {
|
||||||
const items = await oauthClient.makeApiCall({
|
const items = await oauthClient.makeApiCall({
|
||||||
url: urlBuilder(req.cookies.qbo_realmId, "query", `select * From Item`),
|
url: urlBuilder(req.cookies.qbo_realmId, "query", `select * From Item`),
|
||||||
|
|||||||
@@ -22,3 +22,4 @@ exports.authorize = require("./qbo-authorize").default;
|
|||||||
exports.refresh = require("./qbo-callback").refresh;
|
exports.refresh = require("./qbo-callback").refresh;
|
||||||
exports.receivables = require("./qbo-receivables").default;
|
exports.receivables = require("./qbo-receivables").default;
|
||||||
exports.payables = require("./qbo-payables").default;
|
exports.payables = require("./qbo-payables").default;
|
||||||
|
exports.payments = require("./qbo-payments").default;
|
||||||
|
|||||||
@@ -142,9 +142,6 @@ const generatePayment = (payment, isThreeTier, twoTierPref) => {
|
|||||||
payment.stripeid || ""
|
payment.stripeid || ""
|
||||||
} ${payment.payer ? ` PAID BY ${payment.payer}` : ""}`,
|
} ${payment.payer ? ` PAID BY ${payment.payer}` : ""}`,
|
||||||
IsAutoApply: true,
|
IsAutoApply: true,
|
||||||
// AppliedToTxnAdd:{
|
|
||||||
// T
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user