Merged in release/2022-02-04 (pull request #366)

Release/2022 02 04
This commit is contained in:
Patrick Fic
2022-02-05 01:35:28 +00:00
56 changed files with 255 additions and 20 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.7.1"> <babeledit_project be_version="2.7.1" version="1.2">
<!-- <!--
BabelEdit project file BabelEdit project file
@@ -40261,6 +40261,53 @@
</concept_node> </concept_node>
</children> </children>
</folder_node> </folder_node>
<folder_node>
<name>validation</name>
<children>
<concept_node>
<name>clockoffmustbeafterclockon</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>clockoffwithoutclockon</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
</children> </children>
</folder_node> </folder_node>
<folder_node> <folder_node>

View File

@@ -8,7 +8,7 @@ import {
Space, Space,
Switch, Switch,
Table, Table,
Tooltip, Tooltip
} from "antd"; } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -334,6 +334,19 @@ export function BillEnterModalLinesComponent({
additional: (record, index) => ( additional: (record, index) => (
<Form.Item shouldUpdate style={{ display: "inline-block" }}> <Form.Item shouldUpdate style={{ display: "inline-block" }}>
{() => { {() => {
const price = getFieldValue([
"billlines",
record.name,
"actual_price",
]);
const adjustmentRate = getFieldValue([
"billlines",
record.name,
"lbr_adjustment",
"rate",
]);
if (getFieldValue(["billlines", record.name, "deductedfromlbr"])) if (getFieldValue(["billlines", record.name, "deductedfromlbr"]))
return ( return (
<div> <div>
@@ -406,6 +419,9 @@ export function BillEnterModalLinesComponent({
> >
<InputNumber precision={2} min={0.01} /> <InputNumber precision={2} min={0.01} />
</Form.Item> </Form.Item>
{price &&
adjustmentRate &&
`${(price / adjustmentRate).toFixed(1)} hrs`}
</div> </div>
); );
return <></>; return <></>;

View File

@@ -38,6 +38,7 @@ export default function ScheduleEventContainer({ bodyshop, event, refetch }) {
job: { job: {
date_scheduled: null, date_scheduled: null,
scheduled_in: null, scheduled_in: null,
scheduled_completion:null,
status: bodyshop.md_ro_statuses.default_imported, status: bodyshop.md_ro_statuses.default_imported,
}, },
}, },

View File

@@ -6,9 +6,11 @@ import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { setPartnerVersion } from "../../redux/application/application.actions"; import { setPartnerVersion } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser //currentUser: selectCurrentUser
bodyshop: selectBodyshop,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language)) //setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -19,7 +21,7 @@ export default connect(
mapDispatchToProps mapDispatchToProps
)(PartnerPingComponent); )(PartnerPingComponent);
export function PartnerPingComponent({ setPartnerVersion }) { export function PartnerPingComponent({ bodyshop, setPartnerVersion }) {
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {
@@ -29,16 +31,25 @@ export function PartnerPingComponent({ setPartnerVersion }) {
//if (process.env.NODE_ENV === "development") return; //if (process.env.NODE_ENV === "development") return;
const PartnerResponse = await axios.post("http://localhost:1337/ping/"); const PartnerResponse = await axios.post("http://localhost:1337/ping/");
const { appver, qbpath } = PartnerResponse.data; const { appver, qbpath } = PartnerResponse.data;
if (!bodyshop) return;
setPartnerVersion(appver); setPartnerVersion(appver);
console.log({ appver, qbpath }); console.log({ appver, qbpath });
if (!qbpath) { if (
!qbpath &&
!(
bodyshop &&
(bodyshop.cdk_dealerid ||
bodyshop.pbs_serialnumber ||
bodyshop.accountingconfig.qbo)
)
) {
notification["error"]({ notification["error"]({
title: "", title: "",
message: t("general.messages.noacctfilepath"), message: t("general.messages.noacctfilepath"),
}); });
} }
} catch (error) { } catch (error) {
console.log(error);
notification["error"]({ notification["error"]({
title: "", title: "",
message: t("general.messages.partnernotrunning"), message: t("general.messages.partnernotrunning"),
@@ -48,7 +59,7 @@ export function PartnerPingComponent({ setPartnerVersion }) {
// Execute the created function directly // Execute the created function directly
checkPartnerStatus(); checkPartnerStatus();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, [bodyshop]);
return <></>; return <></>;
} }

View File

@@ -30,6 +30,8 @@ import AlertComponent from "../alert/alert.component";
import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import PartsOrderModalComponent from "./parts-order-modal.component"; import PartsOrderModalComponent from "./parts-order-modal.component";
import axios from "axios"; import axios from "axios";
import { useTreatments } from "@splitsoftware/splitio-react";
import _ from "lodash";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser, currentUser: selectCurrentUser,
@@ -57,6 +59,11 @@ export function PartsOrderModalContainer({
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const client = useApolloClient(); const client = useApolloClient();
const { OEConnection_PriceChange } = useTreatments(
["OEConnection_PriceChange"],
{},
bodyshop.imexshopid
);
const { visible, context, actions } = partsOrderModal; const { visible, context, actions } = partsOrderModal;
const { const {
jobId, jobId,
@@ -230,11 +237,22 @@ export function PartsOrderModalContainer({
id: insertResult.data.insert_parts_orders.returning[0].id, id: insertResult.data.insert_parts_orders.returning[0].id,
}, },
}); });
let po;
//Massage the data based on the split. Should they be able to overwrite OEC pricing?
if (OEConnection_PriceChange.treatment === "on") {
//Set the flag to include the override.
po = _.cloneDeep(partsOrder.data.parts_orders_by_pk);
po.parts_order_lines.forEach((pol) => {
pol.priceChange = true;
});
}
console.log(partsOrder.data.parts_orders_by_pk);
const oecResponse = await axios.post( const oecResponse = await axios.post(
"http://localhost:1337/oec/", "http://localhost:1337/oec/",
partsOrder.data.parts_orders_by_pk, po || partsOrder.data.parts_orders_by_pk,
{ {
headers: { headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken()}`, Authorization: `Bearer ${await auth.currentUser.getIdToken()}`,

View File

@@ -208,7 +208,7 @@ export function TimeTicketModalComponent({
> >
<InputNumber min={0} precision={1} /> <InputNumber min={0} precision={1} />
</Form.Item> </Form.Item>
{isEdit && ( {
<> <>
<Form.Item label={t("timetickets.fields.clockon")} name="clockon"> <Form.Item label={t("timetickets.fields.clockon")} name="clockon">
<FormDateTimePicker <FormDateTimePicker
@@ -221,7 +221,29 @@ export function TimeTicketModalComponent({
} }
/> />
</Form.Item> </Form.Item>
<Form.Item label={t("timetickets.fields.clockoff")} name="clockoff"> <Form.Item
label={t("timetickets.fields.clockoff")}
name="clockoff"
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
const clockon = getFieldValue("clockon");
if (!value) return Promise.resolve();
if (!clockon && value)
return Promise.reject(
t("timetickets.validation.clockoffwithoutclockon")
);
if (!value.isSameOrAfter(clockon))
return Promise.reject(
t("timetickets.validation.clockoffmustbeafterclockon")
);
return Promise.resolve();
},
}),
]}
>
<FormDateTimePicker <FormDateTimePicker
disabled={ disabled={
!HasRbacAccess({ !HasRbacAccess({
@@ -233,7 +255,7 @@ export function TimeTicketModalComponent({
/> />
</Form.Item> </Form.Item>
</> </>
)} }
<Form.Item label={t("timetickets.fields.memo")} name="memo"> <Form.Item label={t("timetickets.fields.memo")} name="memo">
<MemoInput /> <MemoInput />

View File

@@ -2406,6 +2406,10 @@
"clockedout": "Clocked out successfully.", "clockedout": "Clocked out successfully.",
"created": "Time ticket entered successfully.", "created": "Time ticket entered successfully.",
"deleted": "Time ticket deleted successfully." "deleted": "Time ticket deleted successfully."
},
"validation": {
"clockoffmustbeafterclockon": "Clock off time must be the same or after clock in time.",
"clockoffwithoutclockon": "Clock off time cannot be set without a clock in time."
} }
}, },
"titles": { "titles": {

View File

@@ -2406,6 +2406,10 @@
"clockedout": "", "clockedout": "",
"created": "", "created": "",
"deleted": "" "deleted": ""
},
"validation": {
"clockoffmustbeafterclockon": "",
"clockoffwithoutclockon": ""
} }
}, },
"titles": { "titles": {

View File

@@ -2406,6 +2406,10 @@
"clockedout": "", "clockedout": "",
"created": "", "created": "",
"deleted": "" "deleted": ""
},
"validation": {
"clockoffmustbeafterclockon": "",
"clockoffwithoutclockon": ""
} }
}, },
"titles": { "titles": {

View File

@@ -815,6 +815,7 @@
- email - email
- enforce_class - enforce_class
- enforce_referral - enforce_referral
- entegral_id
- features - features
- federal_tax_id - federal_tax_id
- id - id

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "appointments_bodyshopid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "appointments_bodyshopid" on
"public"."appointments" using btree ("bodyshopid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "appointments_jobid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "appointments_jobid" on
"public"."appointments" using btree ("jobid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "appointments_start";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "appointments_start" on
"public"."appointments" using btree ("start");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "appointments_end";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "appointments_end" on
"public"."appointments" using btree ("end");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "associations_bodyshopid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "associations_bodyshopid" on
"public"."associations" using btree ("shopid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "associations_useremail";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "associations_useremail" on
"public"."associations" using btree ("useremail");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "invoicelines_billid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "invoicelines_billid" on
"public"."billlines" using btree ("billid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "invoicelines_jobid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "invoicelines_jobid" on
"public"."billlines" using btree ("joblineid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "bills_jobid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "bills_jobid" on
"public"."bills" using btree ("jobid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "conversations_shopid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "conversations_shopid" on
"public"."conversations" using btree ("bodyshopid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "documents_shopid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "documents_shopid" on
"public"."documents" using btree ("bodyshopid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "documents_jobid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "documents_jobid" on
"public"."documents" using btree ("jobid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "documents_billid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "documents_billid" on
"public"."documents" using btree ("billid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "jobline_jobid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "jobline_jobid" on
"public"."joblines" using btree ("jobid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "jobs_updatedat";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "jobs_updatedat" on
"public"."jobs" using btree ("updated_at");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "jobs_bodyshopid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "jobs_bodyshopid" on
"public"."jobs" using btree ("shopid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "messages_conversationid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "messages_conversationid" on
"public"."messages" using btree ("conversationid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "timetickets_jobid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "timetickets_jobid" on
"public"."timetickets" using btree ("jobid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "parts_orders_jobid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "parts_orders_jobid" on
"public"."parts_orders" using btree ("jobid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "parts_order_lines_poid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "parts_order_lines_poid" on
"public"."parts_order_lines" using btree ("orderid");

View File

@@ -0,0 +1 @@
DROP INDEX IF EXISTS "parts_order_lines_joblineid";

View File

@@ -0,0 +1,2 @@
CREATE INDEX "parts_order_lines_joblineid" on
"public"."parts_order_lines" using btree ("job_line_id");

View File

@@ -9,6 +9,7 @@ exports.default = function ({
qbo = false, qbo = false,
items, items,
taxCodes, taxCodes,
classes,
}) { }) {
const InvoiceLineAdd = []; const InvoiceLineAdd = [];
const responsibilityCenters = bodyshop.md_responsibility_centers; const responsibilityCenters = bodyshop.md_responsibility_centers;
@@ -95,6 +96,9 @@ exports.default = function ({
DetailType: "SalesItemLineDetail", DetailType: "SalesItemLineDetail",
Amount: DineroAmount, Amount: DineroAmount,
SalesItemLineDetail: { SalesItemLineDetail: {
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
: {}),
ItemRef: { ItemRef: {
value: items[account.accountitem], value: items[account.accountitem],
}, },
@@ -161,6 +165,9 @@ exports.default = function ({
DetailType: "SalesItemLineDetail", DetailType: "SalesItemLineDetail",
Amount: DineroAmount, Amount: DineroAmount,
SalesItemLineDetail: { SalesItemLineDetail: {
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
: {}),
ItemRef: { ItemRef: {
value: items[account.accountitem], value: items[account.accountitem],
}, },
@@ -227,6 +234,9 @@ exports.default = function ({
ItemRef: { ItemRef: {
value: items[mapaAccount.accountitem], value: items[mapaAccount.accountitem],
}, },
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
: {}),
TaxCodeRef: { TaxCodeRef: {
value: QboTaxId, value: QboTaxId,
}, },
@@ -290,6 +300,9 @@ exports.default = function ({
ItemRef: { ItemRef: {
value: items[mashAccount.accountitem], value: items[mashAccount.accountitem],
}, },
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
: {}),
TaxCodeRef: { TaxCodeRef: {
value: QboTaxId, value: QboTaxId,
}, },
@@ -367,6 +380,9 @@ exports.default = function ({
amount: Math.round((jobs_by_pk.towing_payable || 0) * 100), amount: Math.round((jobs_by_pk.towing_payable || 0) * 100),
}).toFormat(DineroQbFormat), }).toFormat(DineroQbFormat),
SalesItemLineDetail: { SalesItemLineDetail: {
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
: {}),
ItemRef: { ItemRef: {
value: items[account.accountitem], value: items[account.accountitem],
}, },
@@ -415,6 +431,9 @@ exports.default = function ({
amount: Math.round((jobs_by_pk.storage_payable || 0) * 100), amount: Math.round((jobs_by_pk.storage_payable || 0) * 100),
}).toFormat(DineroQbFormat), }).toFormat(DineroQbFormat),
SalesItemLineDetail: { SalesItemLineDetail: {
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
: {}),
ItemRef: { ItemRef: {
value: items[account.accountitem], value: items[account.accountitem],
}, },
@@ -466,6 +485,9 @@ exports.default = function ({
amount: Math.round((jobs_by_pk.adjustment_bottom_line || 0) * 100), amount: Math.round((jobs_by_pk.adjustment_bottom_line || 0) * 100),
}).toFormat(DineroQbFormat), }).toFormat(DineroQbFormat),
SalesItemLineDetail: { SalesItemLineDetail: {
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
: {}),
ItemRef: { ItemRef: {
value: items[account.accountitem], value: items[account.accountitem],
}, },
@@ -554,6 +576,9 @@ exports.default = function ({
DineroQbFormat DineroQbFormat
), ),
SalesItemLineDetail: { SalesItemLineDetail: {
...(jobs_by_pk.class
? { ClassRef: { value: classes[jobs_by_pk.class] } }
: {}),
ItemRef: { ItemRef: {
value: items["PVRT"], value: items["PVRT"],
}, },

View File

@@ -181,11 +181,9 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
TxnDate: moment(bill.date).format("YYYY-MM-DD"), TxnDate: moment(bill.date).format("YYYY-MM-DD"),
//DueDate: bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"), //DueDate: bill.due_date && moment(bill.due_date).format("YYYY-MM-DD"),
DocNumber: bill.invoice_number, DocNumber: bill.invoice_number,
...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}), //...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),
PrivateNote: `RO ${bill.job.ro_number || ""} OWNER ${ PrivateNote: `RO ${bill.job.ro_number || ""}`,
bill.job.ownr_fn || ""
} ${bill.job.ownr_ln || ""} ${bill.job.ownr_co_nm || ""}`,
Line: bill.billlines.map((il) => Line: bill.billlines.map((il) =>
generateBillLine( generateBillLine(
il, il,
@@ -249,12 +247,11 @@ const generateBillLine = (
) => { ) => {
const account = costCenters.find((c) => c.name === billLine.cost_center); const account = costCenters.find((c) => c.name === billLine.cost_center);
console.log(account.accountname, accounts[account.accountname]);
return { return {
DetailType: "AccountBasedExpenseLineDetail", DetailType: "AccountBasedExpenseLineDetail",
AccountBasedExpenseLineDetail: { AccountBasedExpenseLineDetail: {
...(jobClass ? { ClassRef: { Id: classes[jobClass] } } : {}), ...(jobClass ? { ClassRef: { value: classes[jobClass] } } : {}),
TaxCodeRef: { TaxCodeRef: {
value: value:
taxCodes[findTaxCode(billLine.applicable_taxes, ioSalesTaxCodes)], taxCodes[findTaxCode(billLine.applicable_taxes, ioSalesTaxCodes)],
@@ -324,7 +321,7 @@ async function QueryMetaData(oauthClient, qbo_realmId, req) {
classes.json.QueryResponse && classes.json.QueryResponse &&
classes.json.QueryResponse.Class && classes.json.QueryResponse.Class &&
classes.json.QueryResponse.Class.forEach((t) => { classes.json.QueryResponse.Class.forEach((t) => {
accountMapping[t.Name] = t.Id; classMapping[t.Name] = t.Id;
}); });
return { return {

View File

@@ -356,7 +356,7 @@ async function InsertJob(oauthClient, qbo_realmId, req, job, parentTierRef) {
exports.InsertJob = InsertJob; exports.InsertJob = InsertJob;
async function QueryMetaData(oauthClient, qbo_realmId, req) { async function QueryMetaData(oauthClient, qbo_realmId, req) {
const items = await oauthClient.makeApiCall({ const items = await oauthClient.makeApiCall({
url: urlBuilder(qbo_realmId, "query", `select * From Item`), url: urlBuilder(qbo_realmId, "query", `select * From Item where active=true maxresults 1000`),
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@@ -402,7 +402,7 @@ async function QueryMetaData(oauthClient, qbo_realmId, req) {
classes.json.QueryResponse && classes.json.QueryResponse &&
classes.json.QueryResponse.Class && classes.json.QueryResponse.Class &&
classes.json.QueryResponse.Class.forEach((t) => { classes.json.QueryResponse.Class.forEach((t) => {
itemMapping[t.Name] = t.Id; classMapping[t.Name] = t.Id;
}); });
return { return {
@@ -431,13 +431,19 @@ async function InsertInvoice(
qbo: true, qbo: true,
items, items,
taxCodes, taxCodes,
classes
}); });
const invoiceObj = { const invoiceObj = {
Line: InvoiceLineAdd, Line: InvoiceLineAdd,
TxnDate: moment(job.date_invoiced).format("YYYY-MM-DD"), TxnDate: moment(job.date_invoiced).format("YYYY-MM-DD"),
DocNumber: job.ro_number, DocNumber: job.ro_number,
...(job.class ? { ClassRef: { Id: classes[job.class] } } : {}), ...(job.class ? { ClassRef: { value: classes[job.class] } } : {}),
CustomerMemo: {
value: `${job.clm_no ? `Claim No: ${job.clm_no}` : ``}${
job.po_number ? `PO No: ${job.po_number}` : ``
}`.trim(),
},
CustomerRef: { CustomerRef: {
value: parentTierRef.Id, value: parentTierRef.Id,
}, },
@@ -447,6 +453,15 @@ async function InsertInvoice(
...(bodyshop.accountingconfig.emaillater && job.ownr_ea ...(bodyshop.accountingconfig.emaillater && job.ownr_ea
? { EmailStatus: "NeedToSend" } ? { EmailStatus: "NeedToSend" }
: {}), : {}),
BillAddr: {
Line3: `${job.ownr_city || ""}, ${job.ownr_st || ""} ${
job.ownr_zip || ""
}`.trim(),
Line2: job.ownr_addr1 || "",
Line1: `${job.ownr_fn || ""} ${job.ownr_ln || ""} ${
job.ownr_co_nm || ""
}`,
},
}; };
logger.log("qbo-receivable-objectlog", "DEBUG", req.user.email, job.id, { logger.log("qbo-receivable-objectlog", "DEBUG", req.user.email, job.id, {

View File

@@ -566,6 +566,13 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
status status
est_ct_fn est_ct_fn
est_ct_ln est_ct_ln
ownr_fn
ownr_ln
ownr_co_nm
ownr_addr1
ownr_city
ownr_st
ownr_ph1
ownr_zip ownr_zip
referral_source referral_source
loss_type loss_type