- clear stage.

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-07-14 19:04:50 -04:00
24 changed files with 2333 additions and 2055 deletions

View File

@@ -14,7 +14,6 @@ import {
Typography
} from "antd";
import Dinero from "dinero.js";
import dayjs from "../../utils/day";
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
@@ -22,6 +21,7 @@ import { createStructuredSelector } from "reselect";
import { determineDmsType } from "../../pages/dms/dms.container";
import { selectBodyshop } from "../../redux/user/user.selectors";
import i18n from "../../translations/i18n";
import dayjs from "../../utils/day";
import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component";
import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
@@ -89,7 +89,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) {
job.area_of_damage && job.area_of_damage.impact1
? " " +
t("jobs.labels.dms.damageto", {
area_of_damage: (job.area_of_damage && job.area_of_damage.impact1) || "UNKNOWN"
area_of_damage: (job.area_of_damage && job.area_of_damage.impact1.padStart(2, "0")) || "UNKNOWN"
})
: ""
}`.slice(0, 239),

View File

@@ -1,6 +1,6 @@
import React, { useCallback, useState } from "react";
import { LockOutlined } from "@ant-design/icons";
import { Badge, Card, Col, Collapse, Form, Input, Row, Space, Tooltip } from "antd";
import React, { useCallback, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
@@ -12,8 +12,8 @@ import JobCloseRoGuardBills from "./job-close-ro-guard.bills";
import JobCloseRoGuardPpd from "./job-close-ro-guard.ppd";
import JobCloseRoGuardProfit from "./job-close-ro-guard.profit";
import "./job-close-ro-guard.styles.scss";
import JobCloseRoGuardTtLifecycle from "./job-close-ro-guard.tt-lifecycle";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import JobCloseRoGuardTtLifecycle from "./job-close-ro-guard.tt-lifecycle";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser

View File

@@ -1,14 +1,21 @@
import React from "react";
import { useTranslation } from "react-i18next";
import CardTemplate from "./job-detail-cards.template.component";
import Car from "../job-damage-visual/job-damage-visual.component";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsDamageComponent({ loading, data }) {
const { t } = useTranslation();
const { area_of_damage } = data;
return (
<CardTemplate loading={loading} title={t("jobs.labels.cards.damage")}>
{area_of_damage ? <Car dmg1={area_of_damage.impact1} dmg2={area_of_damage.impact2} /> : t("jobs.errors.nodamage")}
{area_of_damage ? (
<Car
dmg1={area_of_damage.impact1 && area_of_damage.impact1.padStart(2, "0")}
dmg2={area_of_damage.impact2 && area_of_damage.impact2.padStart(2, "0")}
/>
) : (
t("jobs.errors.nodamage")
)}
</CardTemplate>
);
}

View File

@@ -26,6 +26,16 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
const { t } = useTranslation();
const { joblines_status } = data;
const filteredJobLines = data.joblines.filter(
(j) =>
j.part_type !== null &&
j.part_type !== "PAE" &&
j.part_type !== "PAS" &&
j.part_type !== "PASL" &&
j.part_qty !== 0 &&
j.act_price !== 0
);
//TODO: Correct jobline_statuses view by including the part_qty !== 0 and act_price !== 0
const columns = [
{
title: t("joblines.fields.line_desc"),
@@ -95,7 +105,7 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
<div>
<CardTemplate loading={loading} title={t("jobs.labels.cards.parts")}>
<PartsStatusPie joblines_status={joblines_status} />
<Table key="id" columns={columns} dataSource={data ? data.joblines : []} />
<Table key="id" columns={columns} dataSource={filteredJobLines ? filteredJobLines : []} />
</CardTemplate>
</div>
);

View File

@@ -189,7 +189,10 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) {
</Col>
<Col {...lossColDamage}>
{job.area_of_damage ? (
<Car dmg1={job.area_of_damage.impact1} dmg2={job.area_of_damage.impact2} />
<Car
dmg1={job.area_of_damage.impact1 && job.area_of_damage.impact1.padStart(2, "0")}
dmg2={job.area_of_damage.impact2 && job.area_of_damage.impact2.padStart(2, "0")}
/>
) : (
t("jobs.errors.nodamage")
)}

View File

@@ -905,7 +905,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
}
joblines(
where: { removed: { _eq: false }, part_type: { _is_null: false, _nin: ["PAE", "PAS", "PASL"] } }
where: { removed: { _eq: false } }
order_by: { line_no: asc }
) {
id

View File

@@ -647,7 +647,10 @@
"payers": "Payers"
},
"cdk_dealerid": "CDK Dealer ID",
"costsmapping": "Costs Mapping",
"dms_allocations": "DMS Allocations",
"pbs_serialnumber": "PBS Serial Number",
"profitsmapping": "Profits Mapping",
"title": "DMS"
},
"emaillater": "Email Later",
@@ -2947,6 +2950,8 @@
"purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)",
"purchases_by_date_range_detail": "Purchases by Date - Detail",
"purchases_by_date_range_summary": "Purchases by Date - Summary",
"purchases_by_ro_detail_date": "Purchases by RO - Detail",
"purchases_by_ro_summary_date": "Purchases by RO - Summary",
"purchases_by_vendor_detailed_date_range": "Purchases By Vendor - Detailed",
"purchases_by_vendor_summary_date_range": "Purchases by Vendor - Summary",
"purchases_grouped_by_vendor_detailed": "Purchases Grouped by Vendor - Detailed",

View File

@@ -647,7 +647,10 @@
"payers": ""
},
"cdk_dealerid": "",
"costsmapping": "",
"dms_allocations": "",
"pbs_serialnumber": "",
"profitsmapping": "",
"title": ""
},
"emaillater": "",
@@ -2937,6 +2940,8 @@
"purchases_by_cost_center_summary": "",
"purchases_by_date_range_detail": "",
"purchases_by_date_range_summary": "",
"purchases_by_ro_detail_date": "",
"purchases_by_ro_summary_date": "",
"purchases_by_vendor_detailed_date_range": "",
"purchases_by_vendor_summary_date_range": "",
"purchases_grouped_by_vendor_detailed": "",

View File

@@ -647,7 +647,10 @@
"payers": ""
},
"cdk_dealerid": "",
"costsmapping": "",
"dms_allocations": "",
"pbs_serialnumber": "",
"profitsmapping": "",
"title": ""
},
"emaillater": "",
@@ -2937,6 +2940,8 @@
"purchases_by_cost_center_summary": "",
"purchases_by_date_range_detail": "",
"purchases_by_date_range_summary": "",
"purchases_by_ro_detail_date": "",
"purchases_by_ro_summary_date": "",
"purchases_by_vendor_detailed_date_range": "",
"purchases_by_vendor_summary_date_range": "",
"purchases_grouped_by_vendor_detailed": "",

View File

@@ -1081,6 +1081,32 @@ export const TemplateList = (type, context) => {
},
group: "purchases"
},
purchases_by_ro_detail_date: {
title: i18n.t("reportcenter.templates.purchases_by_ro_detail_date"),
description: "",
subject: i18n.t("reportcenter.templates.purchases_by_ro_detail_date"),
key: "purchases_by_ro_detail_date",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced")
},
group: "purchases"
},
purchases_by_ro_summary_date: {
title: i18n.t("reportcenter.templates.purchases_by_ro_summary_date"),
description: "",
subject: i18n.t("reportcenter.templates.purchases_by_ro_summary_date"),
key: "purchases_by_ro_summary_date",
//idtype: "vendor",
disabled: false,
rangeFilter: {
object: i18n.t("reportcenter.labels.objects.jobs"),
field: i18n.t("jobs.fields.date_invoiced")
},
group: "purchases"
},
job_costing_ro_date_summary: {
title: i18n.t("reportcenter.templates.job_costing_ro_date_summary"),
description: "",

View File

@@ -0,0 +1,10 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE VIEW "public"."joblines_status" AS
-- SELECT j.jobid,
-- j.status,
-- count(1) AS count,
-- j.part_type
-- FROM joblines j
-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> 0) AND (j.act_price <> 0) AND (j.removed IS FALSE))
-- GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE VIEW "public"."joblines_status" AS
SELECT j.jobid,
j.status,
count(1) AS count,
j.part_type
FROM joblines j
WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> 0) AND (j.act_price <> 0) AND (j.removed IS FALSE))
GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -0,0 +1,10 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- CREATE OR REPLACE VIEW "public"."joblines_status" AS
-- SELECT j.jobid,
-- j.status,
-- count(1) AS count,
-- j.part_type
-- FROM joblines j
-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> (0)::numeric) AND (j.act_price <> (0)::numeric) AND (j.removed IS FALSE))
-- GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -0,0 +1,8 @@
CREATE OR REPLACE VIEW "public"."joblines_status" AS
SELECT j.jobid,
j.status,
count(1) AS count,
j.part_type
FROM joblines j
WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> (0)::numeric) AND (j.act_price <> (0)::numeric) AND (j.removed IS FALSE))
GROUP BY j.jobid, j.status, j.part_type;

View File

@@ -28,6 +28,8 @@
"better-queue": "^3.8.12",
"bluebird": "^3.7.2",
"body-parser": "^1.20.2",
"canvas": "^2.11.2",
"chart.js": "^4.4.3",
"cloudinary": "^2.2.0",
"compression": "^1.7.4",
"cookie-parser": "^1.4.6",

View File

@@ -16,403 +16,402 @@ const { DiscountNotAlreadyCounted } = InstanceManager({
promanager: "USE_ROME"
});
exports.defaultRoute = async function (req, res) {
try {
CdkBase.createLogEvent(req, "DEBUG", `Received request to calculate allocations for ${req.body.jobid}`);
const jobData = await QueryJobData(req, req.BearerToken, req.body.jobid);
return res.status(200).json({ data: calculateAllocations(req, jobData) });
} catch (error) {
console.log(error);
CdkBase.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
res.status(500).json({ error: `Error encountered in CdkCalculateAllocations. ${error}` });
}
};
exports.default = async function (socket, jobid) {
try {
CdkBase.createLogEvent(socket, "DEBUG", `Received request to calculate allocations for ${jobid}`);
const job = await QueryJobData(socket, jobid);
const { bodyshop } = job;
const taxAllocations = InstanceManager({
executeFunction: true,
deubg: true,
args: [],
imex: () => ({
local: {
center: bodyshop.md_responsibility_centers.taxes.local.name,
sale: Dinero(job.job_totals.totals.local_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.local,
costCenter: bodyshop.md_responsibility_centers.taxes.local
},
state: {
center: bodyshop.md_responsibility_centers.taxes.state.name,
sale: Dinero(job.job_totals.totals.state_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.state,
costCenter: bodyshop.md_responsibility_centers.taxes.state
},
federal: {
center: bodyshop.md_responsibility_centers.taxes.federal.name,
sale: Dinero(job.job_totals.totals.federal_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.federal,
costCenter: bodyshop.md_responsibility_centers.taxes.federal
}
}),
rome: () => ({
tax_ty1: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`]
},
tax_ty2: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`]
},
tax_ty3: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`]
},
tax_ty4: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`]
},
tax_ty5: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`]
}
})
});
//Determine if there are MAPA and MASH lines already on the estimate.
//If there are, don't do anything extra (mitchell estimate)
//Otherwise, calculate them and add them to the default MAPA and MASH centers.
let hasMapaLine = false;
let hasMashLine = false;
const profitCenterHash = job.joblines.reduce((acc, val) => {
//Check the Parts Assignment
if (val.db_ref === "936008") {
//If either of these DB REFs change, they also need to change in job-totals/job-costing calculations.
hasMapaLine = true;
}
if (val.db_ref === "936007") {
hasMashLine = true;
}
if (val.profitcenter_part) {
if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero();
let DineroAmount = Dinero({
amount: Math.round(val.act_price * 100)
}).multiply(val.part_qty || 1);
DineroAmount = DineroAmount.add(
((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) &&
DiscountNotAlreadyCounted(val, job.joblines)
? val.prt_dsmk_m
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
: Dinero({
amount: Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0))
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
: Dinero()
);
acc[val.profitcenter_part] = acc[val.profitcenter_part].add(DineroAmount);
}
if (val.profitcenter_labor && val.mod_lbr_ty) {
//Check the Labor Assignment.
if (!acc[val.profitcenter_labor]) acc[val.profitcenter_labor] = Dinero();
acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add(
Dinero({
amount: Math.round(job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100)
}).multiply(val.mod_lb_hrs)
);
}
return acc;
}, {});
const selectedDmsAllocationConfig = bodyshop.md_responsibility_centers.dms_defaults.find(
(d) => d.name === job.dms_allocation
);
CdkBase.createLogEvent(
socket,
"DEBUG",
`Using DMS Allocation ${selectedDmsAllocationConfig && selectedDmsAllocationConfig.name} for cost export.`
);
let costCenterHash = {};
//Check whether to skip this if PBS and using AP module.
const disablebillwip = !!bodyshop?.pbs_configuration?.disablebillwip;
if (!disablebillwip) {
costCenterHash = job.bills.reduce((bill_acc, bill_val) => {
bill_val.billlines.map((line_val) => {
if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]])
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = Dinero();
let lineDinero = Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100)
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1);
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(lineDinero);
return null;
});
return bill_acc;
}, {});
}
job.timetickets.forEach((ticket) => {
//Get the total amount of the ticket.
let TicketTotal = Dinero({
amount: Math.round(
ticket.rate *
(ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) *
100
)
});
//Add it to the right cost center.
if (!costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]])
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = Dinero();
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] =
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]].add(TicketTotal);
});
if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
// console.log("Adding MAPA Line Manually.");
const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!profitCenterHash[mapaAccountName]) profitCenterHash[mapaAccountName] = Dinero();
profitCenterHash[mapaAccountName] = profitCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total)
);
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
}
if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) {
// console.log("Adding MASH Line Manually.");
const mashAccountName = selectedDmsAllocationConfig.profits.MASH;
const mashAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!profitCenterHash[mashAccountName]) profitCenterHash[mashAccountName] = Dinero();
profitCenterHash[mashAccountName] = profitCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
console.log(
Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting),
typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) {
//Manually send the percentage of the costing.
//Paint Mat
const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!costCenterHash[mapaAccountName])
costCenterHash[mapaAccountName] = Dinero();
if (job.bodyshop.use_paint_scale_data === true) {
if (job.mixdata.length > 0) {
costCenterHash[mapaAccountName] = costCenterHash[
mapaAccountName
].add(
Dinero({
amount: Math.round(
((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) *
100
),
})
);
} else {
costCenterHash[mapaAccountName] = costCenterHash[
mapaAccountName
].add(
Dinero(job.job_totals.rates.mapa.total).percentage(
bodyshop?.cdk_configuration?.sendmaterialscosting
)
);
}
} else {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(
bodyshop?.cdk_configuration?.sendmaterialscosting
)
);
}
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
//Shop Mat
const mashAccountName = selectedDmsAllocationConfig.costs.MASH;
const mashAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!costCenterHash[mashAccountName]) costCenterHash[mashAccountName] = Dinero();
costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
const { ca_bc_pvrt } = job;
if (ca_bc_pvrt) {
// const pvrtAccount = bodyshop.md_responsibility_centers.profits.find(
// (c) => c.name === mashAccountName
// );
taxAllocations.state.sale = taxAllocations.state.sale.add(
Dinero({ amount: Math.round((ca_bc_pvrt || 0) * 100) })
);
}
if (job.towing_payable && job.towing_payable !== 0) {
const towAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === towAccountName);
if (towAccount) {
if (!profitCenterHash[towAccountName]) profitCenterHash[towAccountName] = Dinero();
profitCenterHash[towAccountName] = profitCenterHash[towAccountName].add(
Dinero({
amount: Math.round((job.towing_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.storage_payable && job.storage_payable !== 0) {
const storageAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === storageAccountName);
if (towAccount) {
if (!profitCenterHash[storageAccountName]) profitCenterHash[storageAccountName] = Dinero();
profitCenterHash[storageAccountName] = profitCenterHash[storageAccountName].add(
Dinero({
amount: Math.round((job.storage_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) {
const otherAccountName = selectedDmsAllocationConfig.profits.PAO;
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === otherAccountName);
if (otherAccount) {
if (!profitCenterHash[otherAccountName]) profitCenterHash[otherAccountName] = Dinero();
profitCenterHash[otherAccountName] = profitCenterHash[otherAccountName].add(
Dinero({
amount: Math.round((job.adjustment_bottom_line || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (InstanceManager({ rome: true })) {
//profile level adjustments
Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
const accountName = selectedDmsAllocationConfig.profits[key];
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName);
if (otherAccount) {
if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero();
profitCenterHash[accountName] = profitCenterHash[accountName].add(
Dinero(job.job_totals.parts.adjustments[key])
);
} else {
CdkBase.createLogEvent(
socket,
"ERROR",
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
);
}
});
}
const jobAllocations = _.union(Object.keys(profitCenterHash), Object.keys(costCenterHash)).map((key) => {
const profitCenter = bodyshop.md_responsibility_centers.profits.find((c) => c.name === key);
const costCenter = bodyshop.md_responsibility_centers.costs.find((c) => c.name === key);
return {
center: key,
sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(),
cost: costCenterHash[key] ? costCenterHash[key] : Dinero(),
profitCenter,
costCenter
};
});
return [
...jobAllocations,
...Object.keys(taxAllocations)
.filter((key) => taxAllocations[key].sale.getAmount() > 0 || taxAllocations[key].cost.getAmount() > 0)
.map((key) => {
if (
key === "federal" &&
selectedDmsAllocationConfig.gst_override &&
selectedDmsAllocationConfig.gst_override !== ""
) {
const ret = { ...taxAllocations[key], tax: key };
ret.costCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
ret.profitCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
return ret;
} else {
return { ...taxAllocations[key], tax: key };
}
})
];
const jobData = await QueryJobData(socket, "Bearer " + socket.handshake.auth.token, jobid);
return calculateAllocations(socket, jobData);
} catch (error) {
console.log(error);
CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`);
}
};
async function QueryJobData(socket, jobid) {
CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`);
async function QueryJobData(connectionData, token, jobid) {
CdkBase.createLogEvent(connectionData, "DEBUG", `Querying job data for id ${jobid}`);
const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {});
const result = await client
.setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` })
.request(queries.GET_CDK_ALLOCATIONS, { id: jobid });
CdkBase.createLogEvent(socket, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`);
const result = await client.setHeaders({ Authorization: token }).request(queries.GET_CDK_ALLOCATIONS, { id: jobid });
CdkBase.createLogEvent(connectionData, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`);
return result.jobs_by_pk;
}
function calculateAllocations(connectionData, job) {
const { bodyshop } = job;
const taxAllocations = InstanceManager({
executeFunction: true,
deubg: true,
args: [],
imex: () => ({
local: {
center: bodyshop.md_responsibility_centers.taxes.local.name,
sale: Dinero(job.job_totals.totals.local_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.local,
costCenter: bodyshop.md_responsibility_centers.taxes.local
},
state: {
center: bodyshop.md_responsibility_centers.taxes.state.name,
sale: Dinero(job.job_totals.totals.state_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.state,
costCenter: bodyshop.md_responsibility_centers.taxes.state
},
federal: {
center: bodyshop.md_responsibility_centers.taxes.federal.name,
sale: Dinero(job.job_totals.totals.federal_tax),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes.federal,
costCenter: bodyshop.md_responsibility_centers.taxes.federal
}
}),
rome: () => ({
tax_ty1: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`]
},
tax_ty2: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`]
},
tax_ty3: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`]
},
tax_ty4: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`]
},
tax_ty5: {
center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name,
sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]),
cost: Dinero(),
profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`],
costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`]
}
})
});
//Determine if there are MAPA and MASH lines already on the estimate.
//If there are, don't do anything extra (mitchell estimate)
//Otherwise, calculate them and add them to the default MAPA and MASH centers.
let hasMapaLine = false;
let hasMashLine = false;
const profitCenterHash = job.joblines.reduce((acc, val) => {
//Check the Parts Assignment
if (val.db_ref === "936008") {
//If either of these DB REFs change, they also need to change in job-totals/job-costing calculations.
hasMapaLine = true;
}
if (val.db_ref === "936007") {
hasMashLine = true;
}
if (val.profitcenter_part) {
if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero();
let DineroAmount = Dinero({
amount: Math.round(val.act_price * 100)
}).multiply(val.part_qty || 1);
DineroAmount = DineroAmount.add(
((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) &&
DiscountNotAlreadyCounted(val, job.joblines)
? val.prt_dsmk_m
? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) })
: Dinero({
amount: Math.round(val.act_price * 100)
})
.multiply(val.part_qty || 0)
.percentage(Math.abs(val.prt_dsmk_p || 0))
.multiply(val.prt_dsmk_p > 0 ? 1 : -1)
: Dinero()
);
acc[val.profitcenter_part] = acc[val.profitcenter_part].add(DineroAmount);
}
if (val.profitcenter_labor && val.mod_lbr_ty) {
//Check the Labor Assignment.
if (!acc[val.profitcenter_labor]) acc[val.profitcenter_labor] = Dinero();
acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add(
Dinero({
amount: Math.round(job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100)
}).multiply(val.mod_lb_hrs)
);
}
return acc;
}, {});
const selectedDmsAllocationConfig = bodyshop.md_responsibility_centers.dms_defaults.find(
(d) => d.name === job.dms_allocation
);
CdkBase.createLogEvent(
connectionData,
"DEBUG",
`Using DMS Allocation ${selectedDmsAllocationConfig && selectedDmsAllocationConfig.name} for cost export.`
);
let costCenterHash = {};
//Check whether to skip this if PBS and using AP module.
const disablebillwip = !!bodyshop?.pbs_configuration?.disablebillwip;
if (!disablebillwip) {
costCenterHash = job.bills.reduce((bill_acc, bill_val) => {
bill_val.billlines.map((line_val) => {
if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]])
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = Dinero();
let lineDinero = Dinero({
amount: Math.round((line_val.actual_cost || 0) * 100)
})
.multiply(line_val.quantity)
.multiply(bill_val.is_credit_memo ? -1 : 1);
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] =
bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(lineDinero);
return null;
});
return bill_acc;
}, {});
}
job.timetickets.forEach((ticket) => {
//Get the total amount of the ticket.
let TicketTotal = Dinero({
amount: Math.round(
ticket.rate *
(ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) *
100
)
});
//Add it to the right cost center.
if (!costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]])
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = Dinero();
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] =
costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]].add(TicketTotal);
});
if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) {
// console.log("Adding MAPA Line Manually.");
const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!profitCenterHash[mapaAccountName]) profitCenterHash[mapaAccountName] = Dinero();
profitCenterHash[mapaAccountName] = profitCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total)
);
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
}
if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) {
// console.log("Adding MASH Line Manually.");
const mashAccountName = selectedDmsAllocationConfig.profits.MASH;
const mashAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!profitCenterHash[mashAccountName]) profitCenterHash[mashAccountName] = Dinero();
profitCenterHash[mashAccountName] = profitCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
// console.log(
// Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting),
// typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting)
// );
if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) {
//Manually send the percentage of the costing.
//Paint Mat
const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA;
const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName);
if (mapaAccount) {
if (!costCenterHash[mapaAccountName]) costCenterHash[mapaAccountName] = Dinero();
if (job.bodyshop.use_paint_scale_data === true) {
if (job.mixdata.length > 0) {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero({
amount: Math.round(((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) * 100)
})
);
} else {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
}
} else {
costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add(
Dinero(job.job_totals.rates.mapa.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
}
} else {
//console.log("NO MAPA ACCOUNT FOUND!!");
}
//Shop Mat
const mashAccountName = selectedDmsAllocationConfig.costs.MASH;
const mashAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mashAccountName);
if (mashAccount) {
if (!costCenterHash[mashAccountName]) costCenterHash[mashAccountName] = Dinero();
costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add(
Dinero(job.job_totals.rates.mash.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting)
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
const { ca_bc_pvrt } = job;
if (ca_bc_pvrt) {
// const pvrtAccount = bodyshop.md_responsibility_centers.profits.find(
// (c) => c.name === mashAccountName
// );
taxAllocations.state.sale = taxAllocations.state.sale.add(Dinero({ amount: Math.round((ca_bc_pvrt || 0) * 100) }));
}
if (job.towing_payable && job.towing_payable !== 0) {
const towAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === towAccountName);
if (towAccount) {
if (!profitCenterHash[towAccountName]) profitCenterHash[towAccountName] = Dinero();
profitCenterHash[towAccountName] = profitCenterHash[towAccountName].add(
Dinero({
amount: Math.round((job.towing_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.storage_payable && job.storage_payable !== 0) {
const storageAccountName = selectedDmsAllocationConfig.profits.TOW;
const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === storageAccountName);
if (towAccount) {
if (!profitCenterHash[storageAccountName]) profitCenterHash[storageAccountName] = Dinero();
profitCenterHash[storageAccountName] = profitCenterHash[storageAccountName].add(
Dinero({
amount: Math.round((job.storage_payable || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) {
const otherAccountName = selectedDmsAllocationConfig.profits.PAO;
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === otherAccountName);
if (otherAccount) {
if (!profitCenterHash[otherAccountName]) profitCenterHash[otherAccountName] = Dinero();
profitCenterHash[otherAccountName] = profitCenterHash[otherAccountName].add(
Dinero({
amount: Math.round((job.adjustment_bottom_line || 0) * 100)
})
);
} else {
// console.log("NO MASH ACCOUNT FOUND!!");
}
}
if (InstanceManager({ rome: true })) {
//profile level adjustments
Object.keys(job.job_totals.parts.adjustments).forEach((key) => {
const accountName = selectedDmsAllocationConfig.profits[key];
const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName);
if (otherAccount) {
if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero();
profitCenterHash[accountName] = profitCenterHash[accountName].add(
Dinero(job.job_totals.parts.adjustments[key])
);
} else {
CdkBase.createLogEvent(
connectionData,
"ERROR",
`Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}`
);
}
});
}
const jobAllocations = _.union(Object.keys(profitCenterHash), Object.keys(costCenterHash)).map((key) => {
const profitCenter = bodyshop.md_responsibility_centers.profits.find((c) => c.name === key);
const costCenter = bodyshop.md_responsibility_centers.costs.find((c) => c.name === key);
return {
center: key,
sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(),
cost: costCenterHash[key] ? costCenterHash[key] : Dinero(),
profitCenter,
costCenter
};
});
return [
...jobAllocations,
...Object.keys(taxAllocations)
.filter((key) => taxAllocations[key].sale.getAmount() > 0 || taxAllocations[key].cost.getAmount() > 0)
.map((key) => {
if (
key === "federal" &&
selectedDmsAllocationConfig.gst_override &&
selectedDmsAllocationConfig.gst_override !== ""
) {
const ret = { ...taxAllocations[key], tax: key };
ret.costCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
ret.profitCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override;
return ret;
} else {
return { ...taxAllocations[key], tax: key };
}
})
];
}

View File

@@ -244,6 +244,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
},
InsuranceCompany: job.ins_co_nm || "",
Claim: job.clm_no || "",
DMSAllocation: job.dms_allocation || "",
Contacts: {
CSR: job.employee_csr_rel
? `${
@@ -643,7 +644,8 @@ const CreateJobLines = (joblines) => {
part_qty: jobline.part_qty,
part_price: jobline.act_price,
labor_type: jobline.mod_lbr_ty,
labor_hours: jobline.mod_lb_hrs
labor_hours: jobline.mod_lb_hrs,
labor_sale: jobline.lbr_amt
});
});
return repairLines;
@@ -660,7 +662,10 @@ const CreateTimeTickets = (timetickets) => {
.trim(),
productive_hrs: ticket.productivehrs,
actual_hrs: ticket.actualhrs,
cost_center: ticket.cost_center
cost_center: ticket.cost_center,
flat_rate: ticket.flat_rate,
rate: ticket.rate,
ticket_cost: ticket.flat_rate ? ticket.rate * ticket.productive_hrs : ticket.rate * ticket.actual_hrs
});
});
return timeTickets;

View File

@@ -1139,6 +1139,7 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
date_open
date_repairstarted
date_void
dms_allocation
employee_body_rel {
first_name
last_name
@@ -1184,6 +1185,7 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu
}
db_price
id
lbr_amt
lbr_op
line_desc
line_ind

View File

@@ -0,0 +1,76 @@
const borderColors = [
"rgba(193, 0, 50, 1)",
"rgba(0, 101, 68, 1)",
"rgba(0, 45, 98, 1)",
"rgba(253, 184, 0, 1)",
"rgba(200, 112, 126, 1)",
"rgba(228, 142, 88, 1)",
"rgba(90, 160, 141, 1)",
"rgba(103, 143, 174, 1)",
"rgba(192, 136, 99, 1)",
"rgba(234, 3, 55, 1)",
"rgba(0, 149, 67, 1)",
"rgba(0, 81, 155, 1)",
"rgba(226, 143, 173, 1)",
"rgba(237, 170, 125, 1)",
"rgba(76, 146, 177, 1)",
"rgba(172, 153, 193, 1)",
"rgba(173, 167, 89, 1)",
"rgba(254, 197, 222, 1)",
"rgba(177, 231, 223, 1)",
"rgba(120, 199, 235, 1)",
"rgba(239, 180, 193, 1)",
"rgba(240, 199, 171, 1)",
"rgba(168, 200, 121, 1)",
"rgba(150, 177, 208, 1)",
"rgba(200, 194, 189, 1)",
"rgba(244, 244, 244, 1)",
"rgba(255, 99, 132, 1)",
"rgba(54, 162, 235, 1)",
"rgba(255, 206, 86, 1)",
"rgba(75, 192, 192, 1)",
"rgba(153, 102, 255, 1)",
"rgba(255, 159, 64, 1)",
"rgba(170, 183, 184, 1)",
];
const backgroundColors = [
'rgba(193, 0, 50, 0.2)',
'rgba(0, 101, 68, 0.2)',
'rgba(0, 45, 98, 0.2)',
'rgba(253, 184, 0, 0.2)',
'rgba(200, 112, 126, 0.2)',
'rgba(228, 142, 88, 0.2)',
'rgba(90, 160, 141, 0.2)',
'rgba(103, 143, 174, 0.2)',
'rgba(192, 136, 99, 0.2)',
'rgba(234, 3, 55, 0.2)',
'rgba(0, 149, 67, 0.2)',
'rgba(0, 81, 155, 0.2)',
'rgba(226, 143, 173, 0.2)',
'rgba(237, 170, 125, 0.2)',
'rgba(76, 146, 177, 0.2)',
'rgba(172, 153, 193, 0.2)',
'rgba(173, 167, 89, 0.2)',
'rgba(254, 197, 222, 0.2)',
'rgba(177, 231, 223, 0.2)',
'rgba(120, 199, 235, 0.2)',
'rgba(239, 180, 193, 0.2)',
'rgba(240, 199, 171, 0.2)',
'rgba(168, 200, 121, 0.2)',
'rgba(150, 177, 208, 0.2)',
'rgba(200, 194, 189, 0.2)',
'rgba(244, 244, 244, 0.2)',
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)',
'rgba(170, 183, 184, 0.2)',
];
module.exports = {
borderColors,
backgroundColors,
};

View File

@@ -0,0 +1,92 @@
const { createCanvas } = require("canvas");
const Chart = require("chart.js/auto");
const { backgroundColors, borderColors } = require("./canvas-colors");
const { isObject, defaultsDeep, isNumber } = require("lodash");
exports.canvastest = function (req, res) {
console.log("Incoming test request.", req);
res.status(200).send("OK");
};
exports.canvas = function (req, res) {
const { w, h, values, keys, override } = req.body;
console.log("Incoming Canvas Request:", w, h, values, keys, override);
// Gate required values
if (!values || !keys) {
res.status(400).send("Missing required data");
return;
}
// Override must be an object if it exists
if (override && !isObject(override)) {
res.status(400).send("Override must be an object");
return;
}
// Set the default Width and Height
let [width, height] = [500, 275];
// Allow for custom width and height
if (isNumber(w)) {
width = w;
}
if (isNumber(h)) {
height = h;
}
const configuration = {
type: "doughnut",
data: {
labels: keys,
datasets: [
{
data: values,
backgroundColor: backgroundColors,
borderColor: borderColors,
borderWidth: 1
}
]
},
options: {
devicePixelRatio: 4,
responsive: false,
maintainAspectRatio: true,
circumference: 180,
rotation: -90,
plugins: {
legend: {
labels: {
boxWidth: 20,
font: {
family: "'Montserrat'",
size: 10,
style: "normal",
weight: "normal"
}
},
position: "left"
}
}
}
};
// If we have a valid override object, merge it with the default configuration object.
// This allows for you to override the default configuration with a custom one.
const defaults = () => {
if (!override || !isObject(override)) {
return configuration;
}
return defaultsDeep(override, configuration);
};
res.status(200).send(
(() => {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext("2d");
new Chart(ctx, defaults());
return canvas.toDataURL();
})()
);
};

View File

@@ -1,11 +1,13 @@
const express = require("express");
const router = express.Router();
const cdkGetMake = require("../cdk/cdk-get-makes");
const cdkCalculateAllocations = require("../cdk/cdk-calculate-allocations");
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
router.use(validateFirebaseIdTokenMiddleware);
router.post("/getvehicles", withUserGraphQLClientMiddleware, cdkGetMake.default);
router.post("/calculate-allocations", withUserGraphQLClientMiddleware, cdkCalculateAllocations.defaultRoute);
module.exports = router;

View File

@@ -11,6 +11,7 @@ const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMI
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware");
const { taskAssignedEmail, tasksRemindEmail } = require("../email/tasksEmails");
const { canvastest } = require("../render/canvas-handler");
//Test route to ensure Express is responding.
router.get("/test", async function (req, res) {
@@ -49,4 +50,7 @@ router.post("/tasks-remind-handler", eventAuthorizationMiddleware, tasksRemindEm
router.post("/record-handler/arms", data.arms);
router.post("/taskHandler", validateFirebaseIdTokenMiddleware, taskHandler.taskHandler);
// Canvas Test
router.post("/canvastest", validateFirebaseIdTokenMiddleware, canvastest);
module.exports = router;

View File

@@ -2,8 +2,10 @@ const express = require("express");
const router = express.Router();
const { inlinecss } = require("../render/inlinecss");
const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware");
const { canvas } = require("../render/canvas-handler");
// Define the route for inline CSS rendering
router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss);
router.post("/canvas", validateFirebaseIdTokenMiddleware, canvas);
module.exports = router;