IO-1503 QBO Credit Memos
This commit is contained in:
@@ -27,6 +27,7 @@ const {
|
|||||||
} = require("../qbo/qbo-receivables");
|
} = require("../qbo/qbo-receivables");
|
||||||
const { urlBuilder } = require("./qbo");
|
const { urlBuilder } = require("./qbo");
|
||||||
const { DineroQbFormat } = require("../accounting-constants");
|
const { DineroQbFormat } = require("../accounting-constants");
|
||||||
|
const { findTaxCode } = require("../qb-receivables-lines");
|
||||||
|
|
||||||
exports.default = async (req, res) => {
|
exports.default = async (req, res) => {
|
||||||
const oauthClient = new OAuthClient({
|
const oauthClient = new OAuthClient({
|
||||||
@@ -135,7 +136,17 @@ exports.default = async (req, res) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await InsertPayment(oauthClient, qbo_realmId, req, payment, jobTier);
|
if (payment.amount > 0) {
|
||||||
|
await InsertPayment(oauthClient, qbo_realmId, req, payment, jobTier);
|
||||||
|
} else {
|
||||||
|
await InsertCreditMemo(
|
||||||
|
oauthClient,
|
||||||
|
qbo_realmId,
|
||||||
|
req,
|
||||||
|
payment,
|
||||||
|
jobTier
|
||||||
|
);
|
||||||
|
}
|
||||||
ret.push({ paymentid: payment.id, success: true });
|
ret.push({ paymentid: payment.id, success: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log("qbo-payment-create-error", "ERROR", req.user.email, {
|
logger.log("qbo-payment-create-error", "ERROR", req.user.email, {
|
||||||
@@ -173,7 +184,8 @@ async function InsertPayment(
|
|||||||
oauthClient,
|
oauthClient,
|
||||||
qbo_realmId,
|
qbo_realmId,
|
||||||
req,
|
req,
|
||||||
payment.job.ro_number
|
payment.job.ro_number,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
if (invoices && invoices.length !== 1) {
|
if (invoices && invoices.length !== 1) {
|
||||||
@@ -235,7 +247,13 @@ async function InsertPayment(
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function QueryMetaData(oauthClient, qbo_realmId, req, ro_number) {
|
async function QueryMetaData(
|
||||||
|
oauthClient,
|
||||||
|
qbo_realmId,
|
||||||
|
req,
|
||||||
|
ro_number,
|
||||||
|
isCreditMemo
|
||||||
|
) {
|
||||||
const invoice = await oauthClient.makeApiCall({
|
const invoice = await oauthClient.makeApiCall({
|
||||||
url: urlBuilder(
|
url: urlBuilder(
|
||||||
qbo_realmId,
|
qbo_realmId,
|
||||||
@@ -288,8 +306,50 @@ async function QueryMetaData(oauthClient, qbo_realmId, req, ro_number) {
|
|||||||
// classes.json.QueryResponse.Class.forEach((t) => {
|
// classes.json.QueryResponse.Class.forEach((t) => {
|
||||||
// accountMapping[t.Name] = t.Id;
|
// accountMapping[t.Name] = t.Id;
|
||||||
// });
|
// });
|
||||||
|
let ret = {};
|
||||||
|
|
||||||
|
if (isCreditMemo) {
|
||||||
|
const taxCodes = await oauthClient.makeApiCall({
|
||||||
|
url: urlBuilder(qbo_realmId, "query", `select * From TaxCode`),
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const items = await oauthClient.makeApiCall({
|
||||||
|
url: urlBuilder(qbo_realmId, "query", `select * From Item`),
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
setNewRefreshToken(req.user.email, items);
|
||||||
|
|
||||||
|
const itemMapping = {};
|
||||||
|
|
||||||
|
items.json &&
|
||||||
|
items.json.QueryResponse &&
|
||||||
|
items.json.QueryResponse.Item &&
|
||||||
|
items.json.QueryResponse.Item.forEach((t) => {
|
||||||
|
itemMapping[t.Name] = t.Id;
|
||||||
|
});
|
||||||
|
const taxCodeMapping = {};
|
||||||
|
|
||||||
|
taxCodes.json &&
|
||||||
|
taxCodes.json.QueryResponse &&
|
||||||
|
taxCodes.json.QueryResponse.TaxCode &&
|
||||||
|
taxCodes.json.QueryResponse.TaxCode.forEach((t) => {
|
||||||
|
taxCodeMapping[t.Name] = t.Id;
|
||||||
|
});
|
||||||
|
ret = {
|
||||||
|
...ret,
|
||||||
|
items: itemMapping,
|
||||||
|
taxCodes: taxCodeMapping,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
...ret,
|
||||||
paymentMethods: paymentMethodMapping,
|
paymentMethods: paymentMethodMapping,
|
||||||
invoices:
|
invoices:
|
||||||
invoice.json &&
|
invoice.json &&
|
||||||
@@ -297,3 +357,85 @@ async function QueryMetaData(oauthClient, qbo_realmId, req, ro_number) {
|
|||||||
invoice.json.QueryResponse.Invoice,
|
invoice.json.QueryResponse.Invoice,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
async function InsertCreditMemo(
|
||||||
|
oauthClient,
|
||||||
|
qbo_realmId,
|
||||||
|
req,
|
||||||
|
payment,
|
||||||
|
parentRef
|
||||||
|
) {
|
||||||
|
const { paymentMethods, invoices, items, taxCodes } = await QueryMetaData(
|
||||||
|
oauthClient,
|
||||||
|
qbo_realmId,
|
||||||
|
req,
|
||||||
|
payment.job.ro_number,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
if (invoices && 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"),
|
||||||
|
DocNumber: payment.paymentnum,
|
||||||
|
...(invoices[0] ? { InvoiceRef: { value: invoices[0].Id } } : {}),
|
||||||
|
Line: [
|
||||||
|
{
|
||||||
|
DetailType: "SalesItemLineDetail",
|
||||||
|
Amount: Dinero({ amount: Math.round(payment.amount * -100) }).toFormat(
|
||||||
|
DineroQbFormat
|
||||||
|
),
|
||||||
|
SalesItemLineDetail: {
|
||||||
|
ItemRef: {
|
||||||
|
value:
|
||||||
|
items[
|
||||||
|
payment.job.bodyshop.md_responsibility_centers.refund
|
||||||
|
.accountitem
|
||||||
|
],
|
||||||
|
},
|
||||||
|
Qty: 1,
|
||||||
|
TaxCodeRef: {
|
||||||
|
value:
|
||||||
|
taxCodes[
|
||||||
|
findTaxCode(
|
||||||
|
{
|
||||||
|
local: false,
|
||||||
|
federal: false,
|
||||||
|
state: false,
|
||||||
|
},
|
||||||
|
payment.job.bodyshop.md_responsibility_centers.sales_tax_codes
|
||||||
|
)
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
logger.log("qbo-payments-objectlog", "DEBUG", req.user.email, payment.id, {
|
||||||
|
paymentQbo,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const result = await oauthClient.makeApiCall({
|
||||||
|
url: urlBuilder(qbo_realmId, "creditmemo"),
|
||||||
|
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: error && error.message,
|
||||||
|
method: "InsertCreditMemo",
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ const generatePayment = (payment, isThreeTier, twoTierPref) => {
|
|||||||
QBXML: {
|
QBXML: {
|
||||||
QBXMLMsgsRq: {
|
QBXMLMsgsRq: {
|
||||||
"@onError": "continueOnError",
|
"@onError": "continueOnError",
|
||||||
CreditMemoAddRq: {
|
wMemoAddRq: {
|
||||||
CreditMemoAdd: {
|
CreditMemoAdd: {
|
||||||
CustomerRef: {
|
CustomerRef: {
|
||||||
FullName: (payment.job.bodyshop.accountingconfig.tiers === 3
|
FullName: (payment.job.bodyshop.accountingconfig.tiers === 3
|
||||||
|
|||||||
Reference in New Issue
Block a user