Successful 2tier and 3 tier export of invoices. BOD-83 BOD-131

This commit is contained in:
Patrick Fic
2020-06-02 09:19:46 -07:00
parent 5564b5dc4a
commit 73040064d4
16 changed files with 265 additions and 112 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.6.1"> <babeledit_project be_version="2.6.1" version="1.2">
<!-- <!--
BabelEdit project file BabelEdit project file
@@ -7374,6 +7374,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>export</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> <concept_node>
<name>gotojob</name> <name>gotojob</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>

View File

@@ -68,7 +68,7 @@ export default connect(
try { try {
const response2 = await axios.post( const response2 = await axios.post(
"http://localhost:1337/qb/receivables", "http://e9c5a8ed9079.ngrok.io/qb/receivables",
response.data, response.data,
{ {
headers: { headers: {

View File

@@ -19,6 +19,7 @@ export function JobsCloseLabmatAllocationButton({
allocation, allocation,
setAllocations, setAllocations,
bodyshop, bodyshop,
invoiced
}) { }) {
const [visible, setVisible] = useState(false); const [visible, setVisible] = useState(false);
const [state, setState] = useState({ center: "", amount: 0 }); const [state, setState] = useState({ center: "", amount: 0 });
@@ -87,7 +88,7 @@ export function JobsCloseLabmatAllocationButton({
<Button <Button
onClick={handleAllocate} onClick={handleAllocate}
disabled={ disabled={
state.amount === 0 || state.center === "" || remainingAmount === 0 state.amount === 0 || state.center === "" || remainingAmount === 0 || invoiced
} }
> >
{t("jobs.actions.allocate")} {t("jobs.actions.allocate")}
@@ -95,9 +96,9 @@ export function JobsCloseLabmatAllocationButton({
</div> </div>
<div> <div>
{visible ? ( {visible ? (
<CloseCircleFilled onClick={() => setVisible(false)} /> <CloseCircleFilled onClick={() => setVisible(false)} disabled={invoiced} />
) : ( ) : (
<PlusCircleFilled onClick={() => setVisible(true)} /> <PlusCircleFilled onClick={() => setVisible(true)} disabled={invoiced}/>
)} )}
</div> </div>
</div> </div>

View File

@@ -4,14 +4,15 @@ export default function JobsCloseLabMatAllocationTags({
allocationKey, allocationKey,
allocation, allocation,
setAllocations, setAllocations,
invoiced,
}) { }) {
return ( return (
<div> <div>
{allocation.allocations.map((a, idx) => ( {allocation.allocations.map((a, idx) => (
<Tag <Tag
closable closable={!invoiced} //Value is whether it is invoiced.
visible visible
color='green' color="green"
onClose={() => { onClose={() => {
setAllocations((state) => { setAllocations((state) => {
return { return {

View File

@@ -14,6 +14,7 @@ export function JobsCloseAutoAllocate({
setLabmatAllocations, setLabmatAllocations,
partsAllocations, partsAllocations,
setPartsAllocations, setPartsAllocations,
invoiced
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const handleAllocate = () => { const handleAllocate = () => {
@@ -62,6 +63,6 @@ export function JobsCloseAutoAllocate({
}); });
}; };
return <Button onClick={handleAllocate}>{t("jobs.actions.autoallocate")}</Button>; return <Button onClick={handleAllocate} disabled={invoiced}>{t("jobs.actions.autoallocate")}</Button>;
} }
export default connect(mapStateToProps, null)(JobsCloseAutoAllocate); export default connect(mapStateToProps, null)(JobsCloseAutoAllocate);

View File

@@ -0,0 +1,43 @@
import { Button } from "antd";
import axios from "axios";
import React from "react";
import { useTranslation } from "react-i18next";
import { auth } from "../../firebase/firebase.utils";
export default function JobsCloseExportButton({ jobId, disabled }) {
const { t } = useTranslation();
const handleQbxml = async () => {
const response = await axios.post(
"/accounting/qbxml/receivables",
{ jobId: jobId },
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
},
}
);
console.log("handle -> XML", response);
try {
const response2 = await axios.post(
"http://e9c5a8ed9079.ngrok.io/qb/receivables",
response.data,
{
headers: {
Authorization: `Bearer ${await auth.currentUser.getIdToken(true)}`,
},
}
);
console.log("handle -> result", response2);
} catch (error) {
console.log("error", error, JSON.stringify(error));
}
};
return (
<Button onClick={handleQbxml} disabled={disabled} type="dashed">
{t("jobs.actions.export")}
</Button>
);
}

View File

@@ -8,6 +8,7 @@ export default function JobCloseLabMatAllocation({
labmatAllocations, labmatAllocations,
setLabmatAllocations, setLabmatAllocations,
labMatTotalAllocation, labMatTotalAllocation,
invoiced
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -50,6 +51,7 @@ export default function JobCloseLabMatAllocation({
<td> <td>
<AllocationButton <AllocationButton
allocationKey={alloc} allocationKey={alloc}
invoiced={invoiced}
remainingAmount={labmatAllocations[alloc].total remainingAmount={labmatAllocations[alloc].total
.subtract( .subtract(
Dinero({ Dinero({
@@ -69,6 +71,7 @@ export default function JobCloseLabMatAllocation({
<td> <td>
<AllocationTags <AllocationTags
allocationKey={alloc} allocationKey={alloc}
invoiced={invoiced}
allocation={labmatAllocations[alloc]} allocation={labmatAllocations[alloc]}
setAllocations={setLabmatAllocations} setAllocations={setLabmatAllocations}
/> />

View File

@@ -8,6 +8,7 @@ export default function JobsClosePartsAllocation({
partsAllocations, partsAllocations,
setPartsAllocations, setPartsAllocations,
partsAllocatedTotal, partsAllocatedTotal,
invoiced,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -50,6 +51,7 @@ export default function JobsClosePartsAllocation({
<td> <td>
<AllocationButton <AllocationButton
allocationKey={alloc} allocationKey={alloc}
invoiced={invoiced}
remainingAmount={partsAllocations[alloc].total remainingAmount={partsAllocations[alloc].total
.subtract( .subtract(
Dinero({ Dinero({
@@ -68,6 +70,7 @@ export default function JobsClosePartsAllocation({
</td> </td>
<td> <td>
<AllocationTags <AllocationTags
invoiced={invoiced}
allocationKey={alloc} allocationKey={alloc}
allocation={partsAllocations[alloc]} allocation={partsAllocations[alloc]}
setAllocations={setPartsAllocations} setAllocations={setPartsAllocations}

View File

@@ -18,6 +18,8 @@ export function JobsCloseSaveButton({
jobTotals, jobTotals,
labMatAllocations, labMatAllocations,
partsAllocations, partsAllocations,
setInvoicedState,
disabled,
}) { }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
@@ -38,9 +40,23 @@ export function JobsCloseSaveButton({
}, },
}, },
}, },
optimisticResponse: {
update_jobs: {
returning: {
id: jobId,
date_invoiced: new Date(),
status: bodyshop.md_ro_statuses.default_invoiced || "Invoiced*",
invoice_allocation: {
labMatAllocations,
partsAllocations,
},
},
},
},
}); });
if (!!!result.errors) { if (!!!result.errors) {
notification["success"]({ message: t("jobs.successes.invoiced") }); notification["success"]({ message: t("jobs.successes.invoiced") });
setInvoicedState(true);
} else { } else {
notification["error"]({ notification["error"]({
message: t("jobs.errors.invoicing", { message: t("jobs.errors.invoicing", {
@@ -50,14 +66,14 @@ export function JobsCloseSaveButton({
} }
setLoading(false); setLoading(false);
}; };
return ( return (
<Button <Button
onClick={handleSave} onClick={handleSave}
disabled={suspenseAmount > 0} disabled={suspenseAmount > 0 || disabled}
loading={loading} loading={loading}
> >
{t("general.actions.close")} {t("general.actions.save")}
</Button> </Button>
); );
} }

View File

@@ -755,11 +755,6 @@ export const QUERY_ALL_JOBS_PAGINATED = gql`
export const QUERY_JOB_CLOSE_DETAILS = gql` export const QUERY_JOB_CLOSE_DETAILS = gql`
query QUERY_JOB_CLOSE_DETAILS($id: uuid!) { query QUERY_JOB_CLOSE_DETAILS($id: uuid!) {
jobs_by_pk(id: $id) { jobs_by_pk(id: $id) {
po_number
special_coverage_policy
scheduled_delivery
converted
est_number
ro_number ro_number
clm_total clm_total
inproduction inproduction
@@ -769,26 +764,12 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
v_model_desc v_model_desc
v_make_desc v_make_desc
v_color v_color
invoice_allocation
ins_co_id ins_co_id
policy_no policy_no
loss_date
clm_no clm_no
area_of_damage
ins_co_nm ins_co_nm
ins_addr1
ins_city
ins_ct_ln
ins_ct_fn
ins_ea
ins_ph1
est_co_nm
est_ct_fn
est_ct_ln
pay_date
est_ph1
est_ea
regie_number regie_number
scheduled_completion
id id
ded_amt ded_amt
ded_status ded_status
@@ -819,20 +800,6 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
ownr_zip ownr_zip
ownr_ctry ownr_ctry
ownr_ph1 ownr_ph1
owner {
id
ownr_fn
ownr_ln
ownr_ea
ownr_addr1
ownr_addr2
ownr_city
ownr_st
ownr_zip
ownr_ctry
ownr_ph1
}
labor_rate_desc
rate_atp rate_atp
rate_la1 rate_la1
rate_la2 rate_la2
@@ -857,18 +824,10 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
rate_mapa rate_mapa
rate_mash rate_mash
rate_matd rate_matd
actual_completion status
scheduled_delivery
actual_delivery
date_invoiced
date_closed
date_exported
status
owner_owing owner_owing
joblines { joblines {
id id
unq_seq
line_ind
tax_part tax_part
line_desc line_desc
prt_dsmk_p prt_dsmk_p
@@ -885,7 +844,6 @@ export const QUERY_JOB_CLOSE_DETAILS = gql`
lbr_amt lbr_amt
op_code_desc op_code_desc
} }
cieca_ttl
} }
} }
`; `;

View File

@@ -8,33 +8,76 @@ import Dinero from "dinero.js";
import JobsCloseTotals from "../../components/jobs-close-totals/jobs-close-totals.component"; import JobsCloseTotals from "../../components/jobs-close-totals/jobs-close-totals.component";
import JobsCloseAutoAllocate from "../../components/jobs-close-auto-allocate/jobs-close-auto-allocate.component"; import JobsCloseAutoAllocate from "../../components/jobs-close-auto-allocate/jobs-close-auto-allocate.component";
import JobsCloseSaveButton from "../../components/jobs-close-save-button/jobs-close-save-button.component"; import JobsCloseSaveButton from "../../components/jobs-close-save-button/jobs-close-save-button.component";
import JobsCloseExportButton from "../../components/jobs-close-export-button/jobs-close-export-button.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
}); });
export function JobsCloseComponent({ job, bodyshop, jobTotals }) { export function JobsCloseComponent({ job, bodyshop, jobTotals }) {
const [invoiced, setInvoiced] = useState(!!job.invoice_allocation);
const [labmatAllocations, setLabmatAllocations] = useState( const [labmatAllocations, setLabmatAllocations] = useState(
Object.keys(jobTotals.rates).reduce((acc, val) => { !!job.invoice_allocation && !!job.invoice_allocation.labMatAllocations
acc[val] = jobTotals.rates[val]; ? Object.keys(job.invoice_allocation.labMatAllocations).reduce(
if (val.includes("subtotal")) return acc; (acc, val) => {
//Not a subtotal - therefore can be allocated. if (val.includes("subtotal")) {
acc[val].allocations = []; acc[val] = Dinero(job.invoice_allocation.labMatAllocations[val]);
return acc; } else {
}, {}) acc[val] = {
...job.invoice_allocation.labMatAllocations[val],
total: Dinero(
job.invoice_allocation.labMatAllocations[val].total
),
allocations: job.invoice_allocation.labMatAllocations[
val
].allocations.map((item) => {
return { ...item, amount: Dinero(item.amount) };
}),
};
}
return acc;
},
{}
)
: Object.keys(jobTotals.rates).reduce((acc, val) => {
acc[val] = jobTotals.rates[val];
if (val.includes("subtotal")) return acc;
//Not a subtotal - therefore can be allocated.
acc[val].allocations = [];
return acc;
}, {})
); );
const [partsAllocations, setPartsAllocations] = useState({ const [partsAllocations, setPartsAllocations] = useState(
...Object.keys(jobTotals.parts.parts.list).reduce((acc, val) => { !!job.invoice_allocation && !!job.invoice_allocation.partsAllocations
acc[val] = { ...jobTotals.parts.parts.list[val], allocations: [] }; ? Object.keys(job.invoice_allocation.partsAllocations).reduce(
(acc, val) => {
acc[val] = {
...job.invoice_allocation.partsAllocations[val],
total: Dinero(job.invoice_allocation.partsAllocations[val].total),
allocations: job.invoice_allocation.partsAllocations[
val
].allocations.map((item) => {
return { ...item, amount: Dinero(item.amount) };
}),
};
return acc;
},
{}
)
: {
...Object.keys(jobTotals.parts.parts.list).reduce((acc, val) => {
acc[val] = { ...jobTotals.parts.parts.list[val], allocations: [] };
return acc; return acc;
}, {}), }, {}),
pas: { pas: {
...jobTotals.parts.sublets, ...jobTotals.parts.sublets,
allocations: [], allocations: [],
}, },
}); }
);
const labmatAllocatedTotalsArray = Object.keys(labmatAllocations) const labmatAllocatedTotalsArray = Object.keys(labmatAllocations)
.filter((i) => !i.includes("subtotal")) .filter((i) => !i.includes("subtotal"))
@@ -61,6 +104,8 @@ export function JobsCloseComponent({ job, bodyshop, jobTotals }) {
<div> <div>
<JobsCloseSaveButton <JobsCloseSaveButton
jobId={job.id} jobId={job.id}
invoiced={invoiced}
setInvoicedState={setInvoiced}
partsAllocations={partsAllocations} partsAllocations={partsAllocations}
labMatAllocations={labmatAllocations} labMatAllocations={labmatAllocations}
jobTotals={jobTotals} jobTotals={jobTotals}
@@ -69,6 +114,7 @@ export function JobsCloseComponent({ job, bodyshop, jobTotals }) {
.subtract(partsAllocatedTotal) .subtract(partsAllocatedTotal)
.getAmount()} .getAmount()}
/> />
<JobsCloseExportButton jobId={job.id} disabled={!invoiced} />
<JobsCloseTotals <JobsCloseTotals
jobTotals={jobTotals} jobTotals={jobTotals}
labMatTotal={labmatAllocatedTotal} labMatTotal={labmatAllocatedTotal}
@@ -79,16 +125,19 @@ export function JobsCloseComponent({ job, bodyshop, jobTotals }) {
setLabmatAllocations={setLabmatAllocations} setLabmatAllocations={setLabmatAllocations}
partsAllocations={partsAllocations} partsAllocations={partsAllocations}
setPartsAllocations={setPartsAllocations} setPartsAllocations={setPartsAllocations}
invoiced={invoiced}
/> />
<JobsCloseLaborMaterialAllocation <JobsCloseLaborMaterialAllocation
labmatAllocations={labmatAllocations} labmatAllocations={labmatAllocations}
setLabmatAllocations={setLabmatAllocations} setLabmatAllocations={setLabmatAllocations}
labMatTotalAllocation={labmatAllocatedTotal} labMatTotalAllocation={labmatAllocatedTotal}
invoiced={invoiced}
/> />
<JobsClosePartsAllocation <JobsClosePartsAllocation
partsAllocations={partsAllocations} partsAllocations={partsAllocations}
setPartsAllocations={setPartsAllocations} setPartsAllocations={setPartsAllocations}
partsAllocatedTotal={partsAllocatedTotal} partsAllocatedTotal={partsAllocatedTotal}
invoiced={invoiced}
/> />
</div> </div>
); );

View File

@@ -500,6 +500,7 @@
"autoallocate": "Auto Allocate", "autoallocate": "Auto Allocate",
"changestatus": "Change Status", "changestatus": "Change Status",
"convert": "Convert", "convert": "Convert",
"export": "Export",
"gotojob": "Go to Job", "gotojob": "Go to Job",
"manualnew": "Create New Job Manually", "manualnew": "Create New Job Manually",
"postInvoices": "Post Invoices", "postInvoices": "Post Invoices",

View File

@@ -500,6 +500,7 @@
"autoallocate": "", "autoallocate": "",
"changestatus": "Cambiar Estado", "changestatus": "Cambiar Estado",
"convert": "Convertir", "convert": "Convertir",
"export": "",
"gotojob": "", "gotojob": "",
"manualnew": "", "manualnew": "",
"postInvoices": "Contabilizar facturas", "postInvoices": "Contabilizar facturas",

View File

@@ -500,6 +500,7 @@
"autoallocate": "", "autoallocate": "",
"changestatus": "Changer le statut", "changestatus": "Changer le statut",
"convert": "Convertir", "convert": "Convertir",
"export": "",
"gotojob": "", "gotojob": "",
"manualnew": "", "manualnew": "",
"postInvoices": "Poster des factures", "postInvoices": "Poster des factures",

View File

@@ -31,17 +31,16 @@ exports.default = async (req, res) => {
//Is this a two tier, or 3 tier setup? //Is this a two tier, or 3 tier setup?
const isThreeTier = bodyshop.accountingconfig.tiers === 3; const isThreeTier = bodyshop.accountingconfig.tiers === 3;
QbXmlToExecute.push(
generateCustomerQbxml(jobs_by_pk, bodyshop, isThreeTier)
);
if (isThreeTier) { if (isThreeTier) {
QbXmlToExecute.push(generateJobQbxml(jobs_by_pk, bodyshop, 2)); QbXmlToExecute.push(
QbXmlToExecute.push(generateJobQbxml(jobs_by_pk, bodyshop, 3)); generateSourceCustomerQbxml(jobs_by_pk, bodyshop) // Create the source customer.
);
} }
QbXmlToExecute.push(generateJobQbxml(jobs_by_pk, bodyshop, isThreeTier, 2));
QbXmlToExecute.push(generateJobQbxml(jobs_by_pk, bodyshop, isThreeTier, 3));
//Generate the actual invoice.
QbXmlToExecute.push(generateInvoiceQbxml(jobs_by_pk, bodyshop));
console.log(QbXmlToExecute);
res.status(200).json(QbXmlToExecute); res.status(200).json(QbXmlToExecute);
} catch (error) { } catch (error) {
console.log("error", error); console.log("error", error);
@@ -49,31 +48,21 @@ exports.default = async (req, res) => {
} }
}; };
const generateCustomerQbxml = (jobs_by_pk, bodyshop, isThreeTier) => { const generateSourceCustomerQbxml = (jobs_by_pk, bodyshop) => {
const customerQbxmlObj = { const customerQbxmlObj = {
QBXML: { QBXML: {
QBXMLMsgsRq: { QBXMLMsgsRq: {
"@onError": "continueOnError", "@onError": "continueOnError",
CustomerAddRq: { CustomerAddRq: {
CustomerAdd: { CustomerAdd: {
Name: isThreeTier Name: jobs_by_pk.ins_co_nm,
? jobs_by_pk.ins_co_nm BillAddress: {
: jobs_by_pk.ownr_co_nm Addr1: jobs_by_pk.ownr_addr1,
? `${jobs_by_pk.ownr_co_nm} - ${jobs_by_pk.ownr_ln || ""} ${ Addr2: jobs_by_pk.ownr_addr2,
jobs_by_pk.ownr_fn || "" City: jobs_by_pk.ownr_city,
} #${jobs_by_pk.owner.accountingid || ""}` State: jobs_by_pk.ownr_st,
: `${jobs_by_pk.ownr_ln || ""} ${jobs_by_pk.ownr_fn || ""} #${ PostalCode: jobs_by_pk.ownrzip,
jobs_by_pk.owner.accountingid || "" },
}`,
BillAddress: isThreeTier
? null
: {
Addr1: jobs_by_pk.ownr_addr1,
Addr2: jobs_by_pk.ownr_addr2,
City: jobs_by_pk.ownr_city,
State: jobs_by_pk.ownr_st,
PostalCode: jobs_by_pk.ownrzip,
},
}, },
}, },
}, },
@@ -93,15 +82,35 @@ const generateCustomerQbxml = (jobs_by_pk, bodyshop, isThreeTier) => {
return customerQbxml_Full; return customerQbxml_Full;
}; };
const generateJobQbxml = (jobs_by_pk, bodyshop, isThreeTier, tierLevel) => { const generateSourceTier = (jobs_by_pk) => {
const tier1Name = jobs_by_pk.ownr_co_nm; return jobs_by_pk.ins_co_nm;
const tier2Name = jobs_by_pk.ownr_co_nm };
const generateJobTier = (jobs_by_pk) => {
return jobs_by_pk.ro_number;
};
const generateOwnerTier = (jobs_by_pk) => {
return jobs_by_pk.ownr_co_nm
? `${jobs_by_pk.ownr_co_nm} - ${jobs_by_pk.ownr_ln || ""} ${ ? `${jobs_by_pk.ownr_co_nm} - ${jobs_by_pk.ownr_ln || ""} ${
jobs_by_pk.ownr_fn || "" jobs_by_pk.ownr_fn || ""
} #${jobs_by_pk.owner.accountingid || ""}` } #${jobs_by_pk.owner.accountingid || ""}`
: `${jobs_by_pk.ownr_ln || ""} ${jobs_by_pk.ownr_fn || ""} #${ : `${jobs_by_pk.ownr_ln || ""} ${jobs_by_pk.ownr_fn || ""} #${
jobs_by_pk.owner.accountingid || "" jobs_by_pk.owner.accountingid || ""
}`; }`;
};
const generateJobQbxml = (jobs_by_pk, bodyshop, isThreeTier, tierLevel) => {
let Name;
let ParentRefName;
if (tierLevel === 2) {
Name = generateOwnerTier(jobs_by_pk);
ParentRefName = isThreeTier ? generateSourceTier(jobs_by_pk) : null;
} else if (tierLevel === 3) {
Name = generateJobTier(jobs_by_pk);
ParentRefName = isThreeTier
? `${jobs_by_pk.ins_co_nm}:${generateOwnerTier(jobs_by_pk)}`
: generateOwnerTier(jobs_by_pk);
}
const jobQbxmlObj = { const jobQbxmlObj = {
QBXML: { QBXML: {
@@ -109,10 +118,12 @@ const generateJobQbxml = (jobs_by_pk, bodyshop, isThreeTier, tierLevel) => {
"@onError": "continueOnError", "@onError": "continueOnError",
CustomerAddRq: { CustomerAddRq: {
CustomerAdd: { CustomerAdd: {
Name: tierLevel === 2 ? null : null, Name: Name,
ParentRef: { ParentRef: ParentRefName
FullName: tierLevel === 2 ? null : null, ? {
}, FullName: ParentRefName,
}
: null,
}, },
}, },
}, },
@@ -128,6 +139,7 @@ const generateJobQbxml = (jobs_by_pk, bodyshop, isThreeTier, tierLevel) => {
.end({ pretty: true }); .end({ pretty: true });
const jobQbxml_Full = addQbxmlHeader(jobQbxml_partial); const jobQbxml_Full = addQbxmlHeader(jobQbxml_partial);
console.log("jobQbxml_Full", jobQbxml_Full);
return jobQbxml_Full; return jobQbxml_Full;
}; };
@@ -170,6 +182,46 @@ const generateInvoiceQbxml = (jobs_by_pk, bodyshop) => {
); );
}); });
//Add tax lines
const job_totals = JSON.parse(jobs_by_pk.job_totals);
const federal_tax = Dinero(job_totals.totals.federal_tax);
const state_tax = Dinero(job_totals.totals.state_tax);
const local_tax = Dinero(job_totals.totals.local_tax);
if (federal_tax.getAmount() > 0) {
InvoiceLineAdd.push({
ItemRef: {
FullName: bodyshop.md_responsibility_centers.taxes.federal.accountitem,
},
Desc: bodyshop.md_responsibility_centers.taxes.federal.accountdesc,
//Quantity: 1,
Amount: federal_tax.toFormat(DineroQbFormat),
});
}
if (state_tax.getAmount() > 0) {
InvoiceLineAdd.push({
ItemRef: {
FullName: bodyshop.md_responsibility_centers.taxes.state.accountitem,
},
Desc: bodyshop.md_responsibility_centers.taxes.state.accountdesc,
//Quantity: 1,
Amount: state_tax.toFormat(DineroQbFormat),
});
}
if (local_tax.getAmount() > 0) {
InvoiceLineAdd.push({
ItemRef: {
FullName: bodyshop.md_responsibility_centers.taxes.local.accountitem,
},
Desc: bodyshop.md_responsibility_centers.taxes.local.accountdesc,
//Quantity: 1,
Amount: local_tax.toFormat(DineroQbFormat),
});
}
const invoiceQbxmlObj = { const invoiceQbxmlObj = {
QBXML: { QBXML: {
QBXMLMsgsRq: { QBXMLMsgsRq: {
@@ -177,13 +229,14 @@ const generateInvoiceQbxml = (jobs_by_pk, bodyshop) => {
InvoiceAddRq: { InvoiceAddRq: {
InvoiceAdd: { InvoiceAdd: {
CustomerRef: { CustomerRef: {
//This can equal the Customer or the Customer Job.
FullName: FullName:
bodyshop.accountingconfig.tiers === 3 bodyshop.accountingconfig.tiers === 3
? "3tier" ? `${generateSourceTier(jobs_by_pk)}:${generateOwnerTier(
: bodyshop.accountingconfig.twotierpref === "name" jobs_by_pk
? "2tiername" )}:${generateJobTier(jobs_by_pk)}`
: "2tiersource", : `${generateOwnerTier(jobs_by_pk)}:${generateJobTier(
jobs_by_pk
)}`,
}, },
TxnDate: new Date(), TxnDate: new Date(),
RefNumber: jobs_by_pk.ro_number, RefNumber: jobs_by_pk.ro_number,
@@ -234,7 +287,7 @@ const generateInvoiceLine = (job, allocation, responsibilityCenters) => {
//Rate: 100, //Rate: 100,
Amount: DineroAmount.toFormat(DineroQbFormat), Amount: DineroAmount.toFormat(DineroQbFormat),
SalesTaxCodeRef: { SalesTaxCodeRef: {
FullName: "Z", FullName: "E",
}, },
}; };
}; };

View File

@@ -48,6 +48,7 @@ query QUERY_JOBS_FOR_RECEIVABLES_EXPORT($id: uuid!) {
date_invoiced date_invoiced
ro_number ro_number
clm_total clm_total
clm_no
invoice_allocation invoice_allocation
ownerid ownerid
ownr_ln ownr_ln