Compare commits
13 Commits
feature/pb
...
release/20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74f791541f | ||
|
|
630fa23f6c | ||
|
|
a0922f2944 | ||
|
|
b8f001625b | ||
|
|
53394efebf | ||
|
|
2c00e5ee79 | ||
|
|
34e220dcad | ||
|
|
67eb430ff9 | ||
|
|
331b2c517b | ||
|
|
05d9f20a66 | ||
|
|
1511b87959 | ||
|
|
9c3e4b7b83 | ||
|
|
b718f49071 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -112,3 +112,4 @@ firebase/.env
|
||||
.elasticbeanstalk/*
|
||||
!.elasticbeanstalk/*.cfg.yml
|
||||
!.elasticbeanstalk/*.global.yml
|
||||
logs/oAuthClient-log.log
|
||||
|
||||
@@ -135,7 +135,7 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
|
||||
label={t("jobs.fields.referral_source_extra")}
|
||||
name="referral_source_extra"
|
||||
>
|
||||
<Input />
|
||||
<Input disabled={jobRO}/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t("jobs.fields.alt_transport")} name="alt_transport">
|
||||
<Select disabled={jobRO} allowClear>
|
||||
|
||||
@@ -5,9 +5,8 @@ import {
|
||||
Input,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Typography,
|
||||
Space, Switch,
|
||||
Typography
|
||||
} from "antd";
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
@@ -75,6 +74,12 @@ export function ScheduleJobModalComponent({
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<Space>
|
||||
<Typography.Title level={3}>{lbrHrsData?.jobs_by_pk?.ro_number}</Typography.Title>
|
||||
<Typography.Title
|
||||
level={4}
|
||||
>{`B/R Hrs:${lbrHrsData?.jobs_by_pk.labhrs?.aggregate.sum.mod_lb_hrs}/${lbrHrsData?.jobs_by_pk.larhrs?.aggregate.sum.mod_lb_hrs}`}</Typography.Title>
|
||||
</Space>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
name="start"
|
||||
|
||||
@@ -208,6 +208,7 @@ export const QUERY_LBR_HRS_BY_PK = gql`
|
||||
query QUERY_LBR_HRS_BY_PK($id: uuid!) {
|
||||
jobs_by_pk(id: $id) {
|
||||
id
|
||||
ro_number
|
||||
labhrs: joblines_aggregate(
|
||||
where: {
|
||||
_and: [{ mod_lbr_ty: { _neq: "LAR" } }, { removed: { _eq: false } }]
|
||||
@@ -535,6 +536,7 @@ export const GET_JOB_BY_PK = gql`
|
||||
unq_seq
|
||||
line_ind
|
||||
line_desc
|
||||
line_ref
|
||||
part_type
|
||||
oem_partno
|
||||
db_price
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -76,7 +76,7 @@ exports.default = function ({
|
||||
{
|
||||
local: false,
|
||||
federal: true,
|
||||
state: jobline.tax_part,
|
||||
state: (jobline.db_ref === "900511" || jobline.db_ref === "900510") ? true: jobline.tax_part,
|
||||
},
|
||||
bodyshop.md_responsibility_centers.sales_tax_codes
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const urlBuilder = require("./qbo").urlBuilder;
|
||||
const StandardizeName = require("./qbo").StandardizeName;
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
@@ -114,7 +115,9 @@ async function QueryVendorRecord(oauthClient, qbo_realmId, req, bill) {
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
"query",
|
||||
`select * From vendor where DisplayName = '${bill.vendor.name}'`
|
||||
`select * From vendor where DisplayName = '${StandardizeName(
|
||||
bill.vendor.name
|
||||
)}'`
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -152,7 +155,7 @@ async function InsertVendorRecord(oauthClient, qbo_realmId, req, bill) {
|
||||
body: JSON.stringify(Vendor),
|
||||
});
|
||||
setNewRefreshToken(req.user.email, result);
|
||||
return result && result.Vendor;
|
||||
return result && result.json && result.json.Vendor;
|
||||
} catch (error) {
|
||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||
error:
|
||||
@@ -180,9 +183,9 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
|
||||
DocNumber: bill.invoice_number,
|
||||
...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),
|
||||
|
||||
Memo: `RO ${bill.job.ro_number || ""} OWNER ${bill.job.ownr_fn || ""} ${
|
||||
bill.job.ownr_ln || ""
|
||||
} ${bill.job.ownr_co_nm || ""}`,
|
||||
PrivateNote: `RO ${bill.job.ro_number || ""} OWNER ${
|
||||
bill.job.ownr_fn || ""
|
||||
} ${bill.job.ownr_ln || ""} ${bill.job.ownr_co_nm || ""}`,
|
||||
Line: bill.billlines.map((il) =>
|
||||
generateBillLine(
|
||||
il,
|
||||
@@ -211,7 +214,7 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
|
||||
body: JSON.stringify(billQbo),
|
||||
});
|
||||
setNewRefreshToken(req.user.email, result);
|
||||
return result && result.Bill;
|
||||
return result && result.json && result.json.Bill;
|
||||
} catch (error) {
|
||||
logger.log("qbo-payables-error", "DEBUG", req.user.email, bill.id, {
|
||||
error:
|
||||
@@ -245,6 +248,8 @@ const generateBillLine = (
|
||||
costCenters
|
||||
) => {
|
||||
const account = costCenters.find((c) => c.name === billLine.cost_center);
|
||||
|
||||
console.log(account.accountname, accounts[account.accountname]);
|
||||
return {
|
||||
DetailType: "AccountBasedExpenseLineDetail",
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ const {
|
||||
} = require("../qbo/qbo-receivables");
|
||||
const { urlBuilder } = require("./qbo");
|
||||
const { DineroQbFormat } = require("../accounting-constants");
|
||||
const { findTaxCode } = require("../qb-receivables-lines");
|
||||
|
||||
exports.default = async (req, res) => {
|
||||
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 });
|
||||
} catch (error) {
|
||||
logger.log("qbo-payment-create-error", "ERROR", req.user.email, {
|
||||
@@ -173,7 +184,8 @@ async function InsertPayment(
|
||||
oauthClient,
|
||||
qbo_realmId,
|
||||
req,
|
||||
payment.job.ro_number
|
||||
payment.job.ro_number,
|
||||
false
|
||||
);
|
||||
|
||||
if (invoices && invoices.length !== 1) {
|
||||
@@ -235,7 +247,13 @@ async function InsertPayment(
|
||||
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({
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
@@ -288,8 +306,50 @@ async function QueryMetaData(oauthClient, qbo_realmId, req, ro_number) {
|
||||
// classes.json.QueryResponse.Class.forEach((t) => {
|
||||
// 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 {
|
||||
...ret,
|
||||
paymentMethods: paymentMethodMapping,
|
||||
invoices:
|
||||
invoice.json &&
|
||||
@@ -297,3 +357,87 @@ async function QueryMetaData(oauthClient, qbo_realmId, req, ro_number) {
|
||||
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 && 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const urlBuilder = require("./qbo").urlBuilder;
|
||||
const StandardizeName = require("./qbo").StandardizeName;
|
||||
|
||||
const path = require("path");
|
||||
require("dotenv").config({
|
||||
path: path.resolve(
|
||||
@@ -166,7 +168,9 @@ async function QueryInsuranceCo(oauthClient, qbo_realmId, req, job) {
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
"query",
|
||||
`select * From Customer where DisplayName = '${job.ins_co_nm}'`
|
||||
`select * From Customer where DisplayName = '${StandardizeName(
|
||||
job.ins_co_nm
|
||||
)}'`
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -228,7 +232,9 @@ async function QueryOwner(oauthClient, qbo_realmId, req, job) {
|
||||
url: urlBuilder(
|
||||
qbo_realmId,
|
||||
"query",
|
||||
`select * From Customer where DisplayName = '${ownerName}'`
|
||||
`select * From Customer where DisplayName = '${StandardizeName(
|
||||
ownerName
|
||||
)}'`
|
||||
),
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -458,7 +464,7 @@ async function InsertInvoice(
|
||||
body: JSON.stringify(invoiceObj),
|
||||
});
|
||||
setNewRefreshToken(req.user.email, result);
|
||||
return result && result.Invoice;
|
||||
return result && result.json && result.json.Invoice;
|
||||
} catch (error) {
|
||||
logger.log("qbo-receivables-error", "DEBUG", req.user.email, job.id, {
|
||||
error,
|
||||
|
||||
@@ -14,7 +14,12 @@ function urlBuilder(realmId, object, query = null) {
|
||||
}`;
|
||||
}
|
||||
|
||||
function StandardizeName(str) {
|
||||
return str.replace(new RegExp(/'/g), "\\'");
|
||||
}
|
||||
|
||||
exports.urlBuilder = urlBuilder;
|
||||
exports.StandardizeName = StandardizeName;
|
||||
exports.callback = require("./qbo-callback").default;
|
||||
exports.authorize = require("./qbo-authorize").default;
|
||||
exports.refresh = require("./qbo-callback").refresh;
|
||||
|
||||
@@ -152,7 +152,7 @@ const generatePayment = (payment, isThreeTier, twoTierPref) => {
|
||||
QBXML: {
|
||||
QBXMLMsgsRq: {
|
||||
"@onError": "continueOnError",
|
||||
CreditMemoAddRq: {
|
||||
wMemoAddRq: {
|
||||
CreditMemoAdd: {
|
||||
CustomerRef: {
|
||||
FullName: (payment.job.bodyshop.accountingconfig.tiers === 3
|
||||
|
||||
@@ -15,7 +15,7 @@ const CalcualteAllocations = require("./cdk-calculate-allocations").default;
|
||||
|
||||
const moment = require("moment");
|
||||
|
||||
const replaceSpecialRegex = `[^a-zA-Z0-9 .,\n #]+/g`;
|
||||
const replaceSpecialRegex = /[^a-zA-Z0-9 .,\n #]+/g;
|
||||
|
||||
exports.default = async function (socket, { txEnvelope, jobid }) {
|
||||
socket.logEvents = [];
|
||||
@@ -584,15 +584,21 @@ async function InsertDmsCustomer(socket, newCustomerNumber) {
|
||||
addressLine:
|
||||
socket.JobData.ownr_addr1 &&
|
||||
socket.JobData.ownr_addr1.replace(replaceSpecialRegex, ""),
|
||||
city: socket.JobData.ownr_city&& socket.JobData.ownr_city.replace(replaceSpecialRegex, ""),
|
||||
country: null,
|
||||
city:
|
||||
socket.JobData.ownr_city &&
|
||||
socket.JobData.ownr_city.replace(replaceSpecialRegex, ""),
|
||||
country:
|
||||
socket.JobData.ownr_ctry &&
|
||||
socket.JobData.ownr_ctry.replace(replaceSpecialRegex, ""),
|
||||
postalCode:
|
||||
socket.JobData.ownr_zip &&
|
||||
socket.JobData.ownr_zip //TODO Need to remove for US Based customers.
|
||||
.toUpperCase()
|
||||
.replace(/\W/g, "")
|
||||
.replace(/(...)/, "$1 "),
|
||||
stateOrProvince: socket.JobData.ownr_st&&socket.JobData.ownr_st.replace(replaceSpecialRegex, ""),
|
||||
stateOrProvince:
|
||||
socket.JobData.ownr_st &&
|
||||
socket.JobData.ownr_st.replace(replaceSpecialRegex, ""),
|
||||
},
|
||||
contactInfo: {
|
||||
mainTelephoneNumber: {
|
||||
|
||||
@@ -174,6 +174,7 @@ query QUERY_JOBS_FOR_CDK_EXPORT($id: uuid!) {
|
||||
ownr_ph2
|
||||
ownr_zip
|
||||
ownr_city
|
||||
ownr_ctry
|
||||
ownr_st
|
||||
ownr_ea
|
||||
ins_co_nm
|
||||
@@ -797,6 +798,7 @@ exports.GET_JOB_BY_PK = ` query GET_JOB_BY_PK($id: uuid!) {
|
||||
line_ind
|
||||
line_desc
|
||||
part_type
|
||||
line_ref
|
||||
oem_partno
|
||||
db_price
|
||||
act_price
|
||||
|
||||
@@ -486,7 +486,9 @@ function CalculateTaxesTotals(job, otherTotals) {
|
||||
Dinero({ amount: Math.round((val.act_price || 0) * 100) })
|
||||
.multiply(val.part_qty || 0)
|
||||
.add(
|
||||
val.prt_dsmk_m && val.prt_dsmk_m !== 0
|
||||
val.prt_dsmk_m &&
|
||||
val.prt_dsmk_m !== 0 &&
|
||||
DiscountNotAlreadyCounted(val, job.joblines)
|
||||
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
|
||||
: Dinero({
|
||||
amount: Math.round(val.act_price * 100),
|
||||
@@ -566,3 +568,9 @@ function CalculateTaxesTotals(job, otherTotals) {
|
||||
}
|
||||
|
||||
exports.default = Totals;
|
||||
|
||||
function DiscountNotAlreadyCounted(jobline, joblines) {
|
||||
if (jobline.db_ref !== "900510") return true;
|
||||
const ParentLine = joblines.find((j) => j.unq_seq === jobline.line_ref);
|
||||
return ParentLine && !(ParentLine.prt_dsmk_m && ParentLine.prt_dsmk_m !== 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user