@@ -2,10 +2,10 @@ const urlBuilder = require("./qbo").urlBuilder;
|
||||
const StandardizeName = require("./qbo").StandardizeName;
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
path: path.resolve(
|
||||
process.cwd(),
|
||||
`.env.${process.env.NODE_ENV || "development"}`
|
||||
),
|
||||
});
|
||||
const logger = require("../../utils/logger");
|
||||
const Dinero = require("dinero.js");
|
||||
@@ -13,8 +13,8 @@ const DineroQbFormat = require("../accounting-constants").DineroQbFormat;
|
||||
const apiGqlClient = require("../../graphql-client/graphql-client").client;
|
||||
const queries = require("../../graphql-client/queries");
|
||||
const {
|
||||
refresh: refreshOauthToken,
|
||||
setNewRefreshToken,
|
||||
refresh: refreshOauthToken,
|
||||
setNewRefreshToken,
|
||||
} = require("./qbo-callback");
|
||||
const OAuthClient = require("intuit-oauth");
|
||||
const moment = require("moment-timezone");
|
||||
@@ -22,316 +22,318 @@ const findTaxCode = require("../qb-receivables-lines").findTaxCode;
|
||||
|
||||
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,
|
||||
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,
|
||||
});
|
||||
|
||||
const { qbo_realmId } = response.associations[0];
|
||||
|
||||
oauthClient.setToken(response.associations[0].qbo_auth);
|
||||
|
||||
if (!qbo_realmId) {
|
||||
res.status(401).json({ error: "No company associated." });
|
||||
return;
|
||||
}
|
||||
|
||||
await refreshOauthToken(oauthClient, req);
|
||||
|
||||
const { bills: billsToQuery, elgen } = req.body;
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
logger.log("qbo-payable-create", "DEBUG", req.user.email, billsToQuery);
|
||||
|
||||
const result = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
.request(queries.QUERY_BILLS_FOR_PAYABLES_EXPORT, {
|
||||
bills: billsToQuery,
|
||||
});
|
||||
|
||||
const { bills, bodyshops } = result;
|
||||
const ret = [];
|
||||
const bodyshop = bodyshops[0];
|
||||
|
||||
for (const bill of bills) {
|
||||
try {
|
||||
let vendorRecord;
|
||||
vendorRecord = await QueryVendorRecord(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
req,
|
||||
bill
|
||||
);
|
||||
|
||||
if (!vendorRecord) {
|
||||
vendorRecord = await InsertVendorRecord(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
req,
|
||||
bill
|
||||
);
|
||||
}
|
||||
|
||||
const insertResults = await InsertBill(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
req,
|
||||
bill,
|
||||
vendorRecord,
|
||||
bodyshop
|
||||
);
|
||||
|
||||
// //No error. Mark the job exported & insert export log.
|
||||
if (elgen) {
|
||||
const result = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
.request(queries.QBO_MARK_BILL_EXPORTED, {
|
||||
billId: bill.id,
|
||||
bill: {
|
||||
exported: true,
|
||||
exported_at: moment().tz(bodyshop.timezone),
|
||||
},
|
||||
logs: [
|
||||
{
|
||||
bodyshopid: bodyshop.id,
|
||||
billid: bill.id,
|
||||
successful: true,
|
||||
useremail: req.user.email,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
ret.push({ billid: bill.id, success: true });
|
||||
} catch (error) {
|
||||
ret.push({
|
||||
billid: bill.id,
|
||||
success: false,
|
||||
errorMessage:
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
try {
|
||||
//Fetch the API Access Tokens & Set them for the session.
|
||||
const response = await apiGqlClient.request(queries.GET_QBO_AUTH, {
|
||||
email: req.user.email,
|
||||
});
|
||||
|
||||
//Add the export log error.
|
||||
if (elgen) {
|
||||
const result = await client
|
||||
.setHeaders({ Authorization: BearerToken })
|
||||
.request(queries.INSERT_EXPORT_LOG, {
|
||||
logs: [
|
||||
{
|
||||
bodyshopid: bodyshop.id,
|
||||
billid: bill.id,
|
||||
successful: false,
|
||||
message: JSON.stringify([
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
]),
|
||||
useremail: req.user.email,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
const {qbo_realmId} = response.associations[0];
|
||||
|
||||
res.status(200).json(ret);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("qbo-payable-create-error", "ERROR", req.user.email, { error: error.message, stack: error.stack });
|
||||
res.status(400).json(error);
|
||||
}
|
||||
oauthClient.setToken(response.associations[0].qbo_auth);
|
||||
|
||||
if (!qbo_realmId) {
|
||||
res.status(401).json({error: "No company associated."});
|
||||
return;
|
||||
}
|
||||
|
||||
await refreshOauthToken(oauthClient, req);
|
||||
|
||||
const {bills: billsToQuery, elgen} = req.body;
|
||||
|
||||
const BearerToken = req.BearerToken;
|
||||
const client = req.userGraphQLClient;
|
||||
|
||||
logger.log("qbo-payable-create", "DEBUG", req.user.email, billsToQuery);
|
||||
|
||||
const result = await client
|
||||
.setHeaders({Authorization: BearerToken})
|
||||
.request(queries.QUERY_BILLS_FOR_PAYABLES_EXPORT, {
|
||||
bills: billsToQuery,
|
||||
});
|
||||
|
||||
const {bills, bodyshops} = result;
|
||||
const ret = [];
|
||||
const bodyshop = bodyshops[0];
|
||||
|
||||
for (const bill of bills) {
|
||||
try {
|
||||
let vendorRecord;
|
||||
vendorRecord = await QueryVendorRecord(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
req,
|
||||
bill
|
||||
);
|
||||
|
||||
if (!vendorRecord) {
|
||||
vendorRecord = await InsertVendorRecord(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
req,
|
||||
bill
|
||||
);
|
||||
}
|
||||
|
||||
const insertResults = await InsertBill(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
req,
|
||||
bill,
|
||||
vendorRecord,
|
||||
bodyshop
|
||||
);
|
||||
|
||||
// //No error. Mark the job exported & insert export log.
|
||||
if (elgen) {
|
||||
const result = await client
|
||||
.setHeaders({Authorization: BearerToken})
|
||||
.request(queries.QBO_MARK_BILL_EXPORTED, {
|
||||
billId: bill.id,
|
||||
bill: {
|
||||
exported: true,
|
||||
exported_at: moment().tz(bodyshop.timezone),
|
||||
},
|
||||
logs: [
|
||||
{
|
||||
bodyshopid: bodyshop.id,
|
||||
billid: bill.id,
|
||||
successful: true,
|
||||
useremail: req.user.email,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
ret.push({billid: bill.id, success: true});
|
||||
} catch (error) {
|
||||
ret.push({
|
||||
billid: bill.id,
|
||||
success: false,
|
||||
errorMessage:
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
});
|
||||
|
||||
//Add the export log error.
|
||||
if (elgen) {
|
||||
const result = await client
|
||||
.setHeaders({Authorization: BearerToken})
|
||||
.request(queries.INSERT_EXPORT_LOG, {
|
||||
logs: [
|
||||
{
|
||||
bodyshopid: bodyshop.id,
|
||||
billid: bill.id,
|
||||
successful: false,
|
||||
message: JSON.stringify([
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
]),
|
||||
useremail: req.user.email,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json(ret);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
logger.log("qbo-payable-create-error", "ERROR", req.user.email, {error: error.message, stack: error.stack});
|
||||
res.status(400).json(error);
|
||||
}
|
||||
};
|
||||
|
||||
async function QueryVendorRecord(oauthClient, qbo_realmId, req, bill) {
|
||||
try {
|
||||
const result = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
"query",
|
||||
`select * From vendor where DisplayName = '${StandardizeName(
|
||||
bill.vendor.name
|
||||
)}'`
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
setNewRefreshToken(req.user.email, result);
|
||||
return (
|
||||
result.json &&
|
||||
result.json.QueryResponse &&
|
||||
result.json.QueryResponse.Vendor &&
|
||||
result.json.QueryResponse.Vendor[0]
|
||||
);
|
||||
} catch (error) {
|
||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||
error:
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
method: "QueryVendorRecord",
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const result = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
"query",
|
||||
`select * From vendor where DisplayName = '${StandardizeName(
|
||||
bill.vendor.name
|
||||
)}'`
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
setNewRefreshToken(req.user.email, result);
|
||||
return (
|
||||
result.json &&
|
||||
result.json.QueryResponse &&
|
||||
result.json.QueryResponse.Vendor &&
|
||||
result.json.QueryResponse.Vendor[0]
|
||||
);
|
||||
} catch (error) {
|
||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||
error:
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
method: "QueryVendorRecord",
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function InsertVendorRecord(oauthClient, qbo_realmId, req, bill) {
|
||||
const Vendor = {
|
||||
DisplayName: bill.vendor.name,
|
||||
};
|
||||
try {
|
||||
const result = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(qbo_realmId, "vendor"),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(Vendor),
|
||||
});
|
||||
setNewRefreshToken(req.user.email, result);
|
||||
return result && result.json && result.json.Vendor;
|
||||
} catch (error) {
|
||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||
error:
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
method: "InsertVendorRecord",
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
const Vendor = {
|
||||
DisplayName: bill.vendor.name,
|
||||
};
|
||||
try {
|
||||
const result = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(qbo_realmId, "vendor"),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(Vendor),
|
||||
});
|
||||
setNewRefreshToken(req.user.email, result);
|
||||
return result && result.json && result.json.Vendor;
|
||||
} catch (error) {
|
||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||
error:
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
method: "InsertVendorRecord",
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function InsertBill(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
req,
|
||||
bill,
|
||||
vendor,
|
||||
bodyshop
|
||||
) {
|
||||
const { accounts, taxCodes, classes } = await QueryMetaData(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
req
|
||||
);
|
||||
|
||||
const lines = bill.billlines.map((il) =>
|
||||
generateBillLine(
|
||||
il,
|
||||
accounts,
|
||||
bill.job.class,
|
||||
bodyshop.md_responsibility_centers.sales_tax_codes,
|
||||
classes,
|
||||
taxCodes,
|
||||
bodyshop.md_responsibility_centers.costs
|
||||
)
|
||||
);
|
||||
|
||||
//QB USA with GST
|
||||
//This was required for the No. 1 Collision Group.
|
||||
if (
|
||||
bodyshop.accountingconfig &&
|
||||
bodyshop.accountingconfig.qbo &&
|
||||
bodyshop.accountingconfig.qbo_usa &&
|
||||
bodyshop.region_config.includes("CA_")
|
||||
) {
|
||||
lines.push({
|
||||
DetailType: "AccountBasedExpenseLineDetail",
|
||||
|
||||
AccountBasedExpenseLineDetail: {
|
||||
...(bill.job.class
|
||||
? { ClassRef: { value: classes[bill.job.class] } }
|
||||
: {}),
|
||||
AccountRef: {
|
||||
value:
|
||||
accounts[
|
||||
bodyshop.md_responsibility_centers.taxes.federal.accountdesc
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
Amount: Dinero({
|
||||
amount: Math.round(
|
||||
bill.billlines.reduce((acc, val) => {
|
||||
return acc + val.actual_cost * val.quantity;
|
||||
}, 0) * 100
|
||||
),
|
||||
})
|
||||
.percentage(bill.federal_tax_rate)
|
||||
|
||||
.toFormat(DineroQbFormat),
|
||||
});
|
||||
}
|
||||
|
||||
const billQbo = {
|
||||
VendorRef: {
|
||||
value: vendor.Id,
|
||||
},
|
||||
TxnDate: moment(bill.date)
|
||||
//.tz(bill.job.bodyshop.timezone)
|
||||
.format("YYYY-MM-DD"),
|
||||
...(!bill.is_credit_memo &&
|
||||
bill.vendor.due_date && {
|
||||
DueDate: moment(bill.date)
|
||||
//.tz(bill.job.bodyshop.timezone)
|
||||
.add(bill.vendor.due_date, "days")
|
||||
.format("YYYY-MM-DD"),
|
||||
}),
|
||||
DocNumber: bill.invoice_number,
|
||||
//...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),
|
||||
...(!(
|
||||
bodyshop.accountingconfig &&
|
||||
bodyshop.accountingconfig.qbo &&
|
||||
bodyshop.accountingconfig.qbo_usa &&
|
||||
bodyshop.region_config.includes("CA_")
|
||||
)
|
||||
? { GlobalTaxCalculation: "TaxExcluded" }
|
||||
: {}),
|
||||
...(bodyshop.accountingconfig.qbo_departmentid &&
|
||||
bodyshop.accountingconfig.qbo_departmentid.trim() !== "" && {
|
||||
DepartmentRef: { value: bodyshop.accountingconfig.qbo_departmentid },
|
||||
}),
|
||||
PrivateNote: `RO ${bill.job.ro_number || ""}`,
|
||||
Line: lines,
|
||||
};
|
||||
logger.log("qbo-payable-objectlog", "DEBUG", req.user.email, bill.id, {
|
||||
billQbo,
|
||||
});
|
||||
try {
|
||||
const result = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(
|
||||
req,
|
||||
bill,
|
||||
vendor,
|
||||
bodyshop
|
||||
) {
|
||||
const {accounts, taxCodes, classes} = await QueryMetaData(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
bill.is_credit_memo ? "vendorcredit" : "bill"
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(billQbo),
|
||||
req
|
||||
);
|
||||
|
||||
const lines = bill.billlines.map((il) =>
|
||||
generateBillLine(
|
||||
il,
|
||||
accounts,
|
||||
bill.job.class,
|
||||
bodyshop.md_responsibility_centers.sales_tax_codes,
|
||||
classes,
|
||||
taxCodes,
|
||||
bodyshop.md_responsibility_centers.costs
|
||||
)
|
||||
);
|
||||
|
||||
//QB USA with GST
|
||||
//This was required for the No. 1 Collision Group.
|
||||
if (
|
||||
bodyshop.accountingconfig &&
|
||||
bodyshop.accountingconfig.qbo &&
|
||||
bodyshop.accountingconfig.qbo_usa &&
|
||||
bodyshop.region_config.includes("CA_")
|
||||
) {
|
||||
lines.push({
|
||||
DetailType: "AccountBasedExpenseLineDetail",
|
||||
|
||||
AccountBasedExpenseLineDetail: {
|
||||
...(bill.job.class
|
||||
? {ClassRef: {value: classes[bill.job.class]}}
|
||||
: {}),
|
||||
AccountRef: {
|
||||
value:
|
||||
accounts[
|
||||
bodyshop.md_responsibility_centers.taxes.federal.accountdesc
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
Amount: Dinero({
|
||||
amount: Math.round(
|
||||
bill.billlines.reduce((acc, val) => {
|
||||
return acc + val.actual_cost * val.quantity;
|
||||
}, 0) * 100
|
||||
),
|
||||
})
|
||||
.percentage(bill.federal_tax_rate)
|
||||
|
||||
.toFormat(DineroQbFormat),
|
||||
});
|
||||
}
|
||||
|
||||
const billQbo = {
|
||||
VendorRef: {
|
||||
value: vendor.Id,
|
||||
},
|
||||
TxnDate: moment(bill.date)
|
||||
//.tz(bill.job.bodyshop.timezone)
|
||||
.format("YYYY-MM-DD"),
|
||||
...(!bill.is_credit_memo &&
|
||||
bill.vendor.due_date && {
|
||||
DueDate: moment(bill.date)
|
||||
//.tz(bill.job.bodyshop.timezone)
|
||||
.add(bill.vendor.due_date, "days")
|
||||
.format("YYYY-MM-DD"),
|
||||
}),
|
||||
DocNumber: bill.invoice_number,
|
||||
//...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),
|
||||
...(!(
|
||||
bodyshop.accountingconfig &&
|
||||
bodyshop.accountingconfig.qbo &&
|
||||
bodyshop.accountingconfig.qbo_usa &&
|
||||
bodyshop.region_config.includes("CA_")
|
||||
)
|
||||
? {GlobalTaxCalculation: "TaxExcluded"}
|
||||
: {}),
|
||||
...(bodyshop.accountingconfig.qbo_departmentid &&
|
||||
bodyshop.accountingconfig.qbo_departmentid.trim() !== "" && {
|
||||
DepartmentRef: {value: bodyshop.accountingconfig.qbo_departmentid},
|
||||
}),
|
||||
PrivateNote: `RO ${bill.job.ro_number || ""}`,
|
||||
Line: lines,
|
||||
};
|
||||
logger.log("qbo-payable-objectlog", "DEBUG", req.user.email, bill.id, {
|
||||
billQbo,
|
||||
});
|
||||
setNewRefreshToken(req.user.email, result);
|
||||
return result && result.json && result.json.Bill;
|
||||
} catch (error) {
|
||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||
error:
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
method: "InsertBill",
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const result = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
bill.is_credit_memo ? "vendorcredit" : "bill"
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(billQbo),
|
||||
});
|
||||
setNewRefreshToken(req.user.email, result);
|
||||
return result && result.json && result.json.Bill;
|
||||
} catch (error) {
|
||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||
error:
|
||||
(error && error.authResponse && error.authResponse.body) ||
|
||||
(error && error.message),
|
||||
method: "InsertBill",
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// [
|
||||
// {
|
||||
// DetailType: "AccountBasedExpenseLineDetail",
|
||||
@@ -346,96 +348,96 @@ async function InsertBill(
|
||||
// ],
|
||||
|
||||
const generateBillLine = (
|
||||
billLine,
|
||||
accounts,
|
||||
jobClass,
|
||||
ioSalesTaxCodes,
|
||||
classes,
|
||||
taxCodes,
|
||||
costCenters
|
||||
billLine,
|
||||
accounts,
|
||||
jobClass,
|
||||
ioSalesTaxCodes,
|
||||
classes,
|
||||
taxCodes,
|
||||
costCenters
|
||||
) => {
|
||||
const account = costCenters.find((c) => c.name === billLine.cost_center);
|
||||
const account = costCenters.find((c) => c.name === billLine.cost_center);
|
||||
|
||||
return {
|
||||
DetailType: "AccountBasedExpenseLineDetail",
|
||||
return {
|
||||
DetailType: "AccountBasedExpenseLineDetail",
|
||||
|
||||
AccountBasedExpenseLineDetail: {
|
||||
...(jobClass ? { ClassRef: { value: classes[jobClass] } } : {}),
|
||||
TaxCodeRef: {
|
||||
value:
|
||||
taxCodes[findTaxCode(billLine.applicable_taxes, ioSalesTaxCodes)],
|
||||
},
|
||||
AccountRef: {
|
||||
value: accounts[account.accountname],
|
||||
},
|
||||
},
|
||||
AccountBasedExpenseLineDetail: {
|
||||
...(jobClass ? {ClassRef: {value: classes[jobClass]}} : {}),
|
||||
TaxCodeRef: {
|
||||
value:
|
||||
taxCodes[findTaxCode(billLine.applicable_taxes, ioSalesTaxCodes)],
|
||||
},
|
||||
AccountRef: {
|
||||
value: accounts[account.accountname],
|
||||
},
|
||||
},
|
||||
|
||||
Amount: Dinero({
|
||||
amount: Math.round(billLine.actual_cost * 100),
|
||||
})
|
||||
.multiply(billLine.quantity || 1)
|
||||
.toFormat(DineroQbFormat),
|
||||
};
|
||||
Amount: Dinero({
|
||||
amount: Math.round(billLine.actual_cost * 100),
|
||||
})
|
||||
.multiply(billLine.quantity || 1)
|
||||
.toFormat(DineroQbFormat),
|
||||
};
|
||||
};
|
||||
|
||||
async function QueryMetaData(oauthClient, qbo_realmId, req) {
|
||||
const accounts = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
"query",
|
||||
`select * From Account where AccountType in ('Cost of Goods Sold', 'Other Current Liability')`
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
setNewRefreshToken(req.user.email, accounts);
|
||||
const taxCodes = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(qbo_realmId, "query", `select * From TaxCode`),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const accounts = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
"query",
|
||||
`select * From Account where AccountType in ('Cost of Goods Sold', 'Other Current Liability')`
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
setNewRefreshToken(req.user.email, accounts);
|
||||
const taxCodes = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(qbo_realmId, "query", `select * From TaxCode`),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const classes = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(qbo_realmId, "query", `select * From Class`),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const classes = await oauthClient.makeApiCall({
|
||||
url: urlBuilder(qbo_realmId, "query", `select * From Class`),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const taxCodeMapping = {};
|
||||
const taxCodeMapping = {};
|
||||
|
||||
taxCodes.json &&
|
||||
taxCodes.json &&
|
||||
taxCodes.json.QueryResponse &&
|
||||
taxCodes.json.QueryResponse.TaxCode &&
|
||||
taxCodes.json.QueryResponse.TaxCode.forEach((t) => {
|
||||
taxCodeMapping[t.Name] = t.Id;
|
||||
taxCodeMapping[t.Name] = t.Id;
|
||||
});
|
||||
|
||||
const accountMapping = {};
|
||||
const accountMapping = {};
|
||||
|
||||
accounts.json &&
|
||||
accounts.json &&
|
||||
accounts.json.QueryResponse &&
|
||||
accounts.json.QueryResponse.Account &&
|
||||
accounts.json.QueryResponse.Account.forEach((t) => {
|
||||
accountMapping[t.FullyQualifiedName] = t.Id;
|
||||
accountMapping[t.FullyQualifiedName] = t.Id;
|
||||
});
|
||||
|
||||
const classMapping = {};
|
||||
classes.json &&
|
||||
const classMapping = {};
|
||||
classes.json &&
|
||||
classes.json.QueryResponse &&
|
||||
classes.json.QueryResponse.Class &&
|
||||
classes.json.QueryResponse.Class.forEach((t) => {
|
||||
classMapping[t.Name] = t.Id;
|
||||
classMapping[t.Name] = t.Id;
|
||||
});
|
||||
|
||||
return {
|
||||
accounts: accountMapping,
|
||||
taxCodes: taxCodeMapping,
|
||||
classes: classMapping,
|
||||
};
|
||||
return {
|
||||
accounts: accountMapping,
|
||||
taxCodes: taxCodeMapping,
|
||||
classes: classMapping,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user