@@ -3753,6 +3753,27 @@
|
|||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>md_jobline_presets</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>md_payment_types</name>
|
<name>md_payment_types</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -10532,6 +10553,27 @@
|
|||||||
<folder_node>
|
<folder_node>
|
||||||
<name>errors</name>
|
<name>errors</name>
|
||||||
<children>
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>refreshrequired</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>updatinglayout</name>
|
<name>updatinglayout</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
@@ -15803,6 +15845,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>presets</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>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
@@ -24413,6 +24476,27 @@
|
|||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>newjob</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>owners</name>
|
<name>owners</name>
|
||||||
<definition_loaded>false</definition_loaded>
|
<definition_loaded>false</definition_loaded>
|
||||||
|
|||||||
@@ -32,13 +32,9 @@ export default function DashboardMonthlyLaborSales({ data, ...cardProps }) {
|
|||||||
return {
|
return {
|
||||||
name: t(`joblines.fields.lbr_types.${key.toUpperCase()}`),
|
name: t(`joblines.fields.lbr_types.${key.toUpperCase()}`),
|
||||||
value: laborData[key].getAmount() / 100,
|
value: laborData[key].getAmount() / 100,
|
||||||
// color: pieColor(i.status),
|
color: pieColor(key.toUpperCase()),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log(
|
|
||||||
"🚀 ~ file: monthly-parts-sales.component.jsx ~ line 34 ~ chartData",
|
|
||||||
chartData
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={t("dashboard.titles.monthlylaborsales")} {...cardProps}>
|
<Card title={t("dashboard.titles.monthlylaborsales")} {...cardProps}>
|
||||||
@@ -72,36 +68,56 @@ export const DashboardMonthlyRevenueGraphGql = `
|
|||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const pieColor = (type) => {
|
||||||
|
if (type === "LAA") return "lightgreen";
|
||||||
|
else if (type === "LAB") return "dodgerblue";
|
||||||
|
else if (type === "LAD") return "aliceblue";
|
||||||
|
else if (type === "LAE") return "seafoam";
|
||||||
|
else if (type === "LAG") return "chartreuse";
|
||||||
|
else if (type === "LAF") return "magenta";
|
||||||
|
else if (type === "LAM") return "gold";
|
||||||
|
else if (type === "LAR") return "crimson";
|
||||||
|
else if (type === "LAU") return "slategray";
|
||||||
|
else if (type === "LA1") return "slategray";
|
||||||
|
else if (type === "LA2") return "slategray";
|
||||||
|
else if (type === "LA3") return "slategray";
|
||||||
|
else if (type === "LA4") return "slategray";
|
||||||
|
return "slategray";
|
||||||
|
};
|
||||||
|
|
||||||
const renderActiveShape = (props) => {
|
const renderActiveShape = (props) => {
|
||||||
const RADIAN = Math.PI / 180;
|
//const RADIAN = Math.PI / 180;
|
||||||
const {
|
const {
|
||||||
cx,
|
cx,
|
||||||
cy,
|
cy,
|
||||||
midAngle,
|
//midAngle,
|
||||||
innerRadius,
|
innerRadius,
|
||||||
outerRadius,
|
outerRadius,
|
||||||
startAngle,
|
startAngle,
|
||||||
endAngle,
|
endAngle,
|
||||||
fill,
|
fill,
|
||||||
payload,
|
payload,
|
||||||
percent,
|
// percent,
|
||||||
value,
|
value,
|
||||||
} = props;
|
} = props;
|
||||||
const sin = Math.sin(-RADIAN * midAngle);
|
// const sin = Math.sin(-RADIAN * midAngle);
|
||||||
const cos = Math.cos(-RADIAN * midAngle);
|
// const cos = Math.cos(-RADIAN * midAngle);
|
||||||
const sx = cx + (outerRadius + 10) * cos;
|
// // const sx = cx + (outerRadius + 10) * cos;
|
||||||
const sy = cy + (outerRadius + 10) * sin;
|
// const sy = cy + (outerRadius + 10) * sin;
|
||||||
const mx = cx + (outerRadius + 30) * cos;
|
// const mx = cx + (outerRadius + 30) * cos;
|
||||||
const my = cy + (outerRadius + 30) * sin;
|
// const my = cy + (outerRadius + 30) * sin;
|
||||||
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
// //const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
||||||
const ey = my;
|
// const ey = my;
|
||||||
const textAnchor = cos >= 0 ? "start" : "end";
|
//const textAnchor = cos >= 0 ? "start" : "end";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>
|
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
|
||||||
{payload.name}
|
{payload.name}
|
||||||
</text>
|
</text>
|
||||||
|
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
|
||||||
|
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
|
||||||
|
</text>
|
||||||
<Sector
|
<Sector
|
||||||
cx={cx}
|
cx={cx}
|
||||||
cy={cy}
|
cy={cy}
|
||||||
@@ -120,29 +136,28 @@ const renderActiveShape = (props) => {
|
|||||||
outerRadius={outerRadius + 10}
|
outerRadius={outerRadius + 10}
|
||||||
fill={fill}
|
fill={fill}
|
||||||
/>
|
/>
|
||||||
<path
|
|
||||||
d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
|
|
||||||
stroke={fill}
|
|
||||||
fill="none"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<text
|
|
||||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
|
||||||
y={ey}
|
|
||||||
textAnchor={textAnchor}
|
|
||||||
fill="#333"
|
|
||||||
>
|
|
||||||
{payload.name}
|
|
||||||
</text>
|
|
||||||
<text
|
|
||||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
|
||||||
y={ey}
|
|
||||||
dy={18}
|
|
||||||
textAnchor={textAnchor}
|
|
||||||
fill="#999"
|
|
||||||
>
|
|
||||||
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
// <path
|
||||||
|
// d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
|
||||||
|
// stroke={fill}
|
||||||
|
// fill="none"
|
||||||
|
// />;
|
||||||
|
// <text
|
||||||
|
// x={ex + (cos >= 0 ? 1 : -1) * 12}
|
||||||
|
// y={ey}
|
||||||
|
// textAnchor={textAnchor}
|
||||||
|
// fill="#333"
|
||||||
|
// >
|
||||||
|
// {payload.name}
|
||||||
|
// </text>
|
||||||
|
// <text
|
||||||
|
// x={ex + (cos >= 0 ? 1 : -1) * 12}
|
||||||
|
// y={ey}
|
||||||
|
// dy={18}
|
||||||
|
// textAnchor={textAnchor}
|
||||||
|
// fill="#999"
|
||||||
|
// >
|
||||||
|
// {Dinero({ amount: Math.round(value * 100) }).toFormat()}
|
||||||
|
// </text>
|
||||||
|
|||||||
@@ -29,13 +29,9 @@ export default function DashboardMonthlyPartsSales({ data, ...cardProps }) {
|
|||||||
return {
|
return {
|
||||||
name: t(`joblines.fields.part_types.${key.toUpperCase()}`),
|
name: t(`joblines.fields.part_types.${key.toUpperCase()}`),
|
||||||
value: partData[key].getAmount() / 100,
|
value: partData[key].getAmount() / 100,
|
||||||
// color: pieColor(i.status),
|
color: pieColor(key.toUpperCase()),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
console.log(
|
|
||||||
"🚀 ~ file: monthly-parts-sales.component.jsx ~ line 34 ~ chartData",
|
|
||||||
chartData
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card title={t("dashboard.titles.monthlypartssales")} {...cardProps}>
|
<Card title={t("dashboard.titles.monthlypartssales")} {...cardProps}>
|
||||||
@@ -68,37 +64,55 @@ export default function DashboardMonthlyPartsSales({ data, ...cardProps }) {
|
|||||||
export const DashboardMonthlyRevenueGraphGql = `
|
export const DashboardMonthlyRevenueGraphGql = `
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
const pieColor = (type) => {
|
||||||
|
if (type === "PAA") return "darkgreen";
|
||||||
|
else if (type === "PAC") return "green";
|
||||||
|
else if (type === "PAE") return "gold";
|
||||||
|
else if (type === "PAG") return "seafoam";
|
||||||
|
else if (type === "PAL") return "chartreuse";
|
||||||
|
else if (type === "PAM") return "magenta";
|
||||||
|
else if (type === "PAN") return "crimson";
|
||||||
|
else if (type === "PAO") return "gold";
|
||||||
|
else if (type === "PAP") return "crimson";
|
||||||
|
else if (type === "PAR") return "indigo";
|
||||||
|
else if (type === "PAS") return "dodgerblue";
|
||||||
|
else if (type === "PASL") return "dodgerblue";
|
||||||
|
return "slategray";
|
||||||
|
};
|
||||||
|
|
||||||
const renderActiveShape = (props) => {
|
const renderActiveShape = (props) => {
|
||||||
const RADIAN = Math.PI / 180;
|
// const RADIAN = Math.PI / 180;
|
||||||
const {
|
const {
|
||||||
cx,
|
cx,
|
||||||
cy,
|
cy,
|
||||||
midAngle,
|
// midAngle,
|
||||||
innerRadius,
|
innerRadius,
|
||||||
outerRadius,
|
outerRadius,
|
||||||
startAngle,
|
startAngle,
|
||||||
endAngle,
|
endAngle,
|
||||||
fill,
|
fill,
|
||||||
payload,
|
payload,
|
||||||
percent,
|
// percent,
|
||||||
value,
|
value,
|
||||||
} = props;
|
} = props;
|
||||||
const sin = Math.sin(-RADIAN * midAngle);
|
// const sin = Math.sin(-RADIAN * midAngle);
|
||||||
const cos = Math.cos(-RADIAN * midAngle);
|
// const cos = Math.cos(-RADIAN * midAngle);
|
||||||
const sx = cx + (outerRadius + 10) * cos;
|
// const sx = cx + (outerRadius + 10) * cos;
|
||||||
const sy = cy + (outerRadius + 10) * sin;
|
//const sy = cy + (outerRadius + 10) * sin;
|
||||||
const mx = cx + (outerRadius + 30) * cos;
|
// const mx = cx + (outerRadius + 30) * cos;
|
||||||
const my = cy + (outerRadius + 30) * sin;
|
//const my = cy + (outerRadius + 30) * sin;
|
||||||
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
// const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
||||||
const ey = my;
|
// const ey = my;
|
||||||
const textAnchor = cos >= 0 ? "start" : "end";
|
// const textAnchor = cos >= 0 ? "start" : "end";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>
|
<text x={cx} y={cy} dy={0} textAnchor="middle" fill={fill}>
|
||||||
{payload.name}
|
{payload.name}
|
||||||
</text>
|
</text>
|
||||||
|
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
|
||||||
|
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
|
||||||
|
</text>
|
||||||
<Sector
|
<Sector
|
||||||
cx={cx}
|
cx={cx}
|
||||||
cy={cy}
|
cy={cy}
|
||||||
@@ -117,29 +131,6 @@ const renderActiveShape = (props) => {
|
|||||||
outerRadius={outerRadius + 10}
|
outerRadius={outerRadius + 10}
|
||||||
fill={fill}
|
fill={fill}
|
||||||
/>
|
/>
|
||||||
<path
|
|
||||||
d={`M${sx},${sy}L${mx},${my}L${ex},${ey}`}
|
|
||||||
stroke={fill}
|
|
||||||
fill="none"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<text
|
|
||||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
|
||||||
y={ey}
|
|
||||||
textAnchor={textAnchor}
|
|
||||||
fill="#333"
|
|
||||||
>
|
|
||||||
{payload.name}
|
|
||||||
</text>
|
|
||||||
<text
|
|
||||||
x={ex + (cos >= 0 ? 1 : -1) * 12}
|
|
||||||
y={ey}
|
|
||||||
dy={18}
|
|
||||||
textAnchor={textAnchor}
|
|
||||||
fill="#999"
|
|
||||||
>
|
|
||||||
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
|
|
||||||
</text>
|
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
|
|||||||
type="monotone"
|
type="monotone"
|
||||||
name="Accumulated Sales"
|
name="Accumulated Sales"
|
||||||
dataKey="accSales"
|
dataKey="accSales"
|
||||||
fill="#8884d8"
|
fill="#3CB371"
|
||||||
stroke="#8884d8"
|
stroke="#3CB371"
|
||||||
/>
|
/>
|
||||||
<Bar
|
<Bar
|
||||||
name="Daily Sales"
|
name="Daily Sales"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Card, Typography } from "antd";
|
|
||||||
import { SyncOutlined } from "@ant-design/icons";
|
import { SyncOutlined } from "@ant-design/icons";
|
||||||
|
import { Card } from "antd";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
export default function DashboardRefreshRequired(props) {
|
export default function DashboardRefreshRequired(props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@@ -11,14 +11,14 @@ export default function DashboardRefreshRequired(props) {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SyncOutlined />
|
<SyncOutlined style={{ fontSize: "300%", margin: "1rem" }} />
|
||||||
<Typography.Title level={4}>
|
<div>{t("dashboard.errors.refreshrequired")}</div>
|
||||||
{t("dashboard.errors.refreshrequired")}
|
|
||||||
</Typography.Title>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -198,15 +198,19 @@ const componentList = {
|
|||||||
label: "Production Dollars",
|
label: "Production Dollars",
|
||||||
component: DashboardTotalProductionDollars,
|
component: DashboardTotalProductionDollars,
|
||||||
gqlFragment: null,
|
gqlFragment: null,
|
||||||
w: 2,
|
w: 1,
|
||||||
h: 1,
|
h: 1,
|
||||||
|
minW: 2,
|
||||||
|
minH: 1,
|
||||||
},
|
},
|
||||||
ProductionHours: {
|
ProductionHours: {
|
||||||
label: "Production Hours",
|
label: "Production Hours",
|
||||||
component: DashboardTotalProductionHours,
|
component: DashboardTotalProductionHours,
|
||||||
gqlFragment: DashboardTotalProductionHoursGql,
|
gqlFragment: DashboardTotalProductionHoursGql,
|
||||||
w: 2,
|
w: 3,
|
||||||
h: 1,
|
h: 1,
|
||||||
|
minW: 3,
|
||||||
|
minH: 1,
|
||||||
},
|
},
|
||||||
ProjectedMonthlySales: {
|
ProjectedMonthlySales: {
|
||||||
label: "Projected Monthly Sales",
|
label: "Projected Monthly Sales",
|
||||||
@@ -214,6 +218,8 @@ const componentList = {
|
|||||||
gqlFragment: DashboardProjectedMonthlySalesGql,
|
gqlFragment: DashboardProjectedMonthlySalesGql,
|
||||||
w: 2,
|
w: 2,
|
||||||
h: 1,
|
h: 1,
|
||||||
|
minW: 2,
|
||||||
|
minH: 1,
|
||||||
},
|
},
|
||||||
MonthlyRevenueGraph: {
|
MonthlyRevenueGraph: {
|
||||||
label: "Monthly Sales Graph",
|
label: "Monthly Sales Graph",
|
||||||
@@ -254,12 +260,15 @@ const componentList = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createDashboardQuery = (state) => {
|
const createDashboardQuery = (state) => {
|
||||||
const componentBasedAdditions = state.layout
|
const componentBasedAdditions =
|
||||||
.map((item, index) => componentList[item.i].gqlFragment || "")
|
state &&
|
||||||
.join("");
|
Array.isArray(state.layout) &&
|
||||||
|
state.layout
|
||||||
|
.map((item, index) => componentList[item.i].gqlFragment || "")
|
||||||
|
.join("");
|
||||||
return gql`
|
return gql`
|
||||||
query QUERY_DASHBOARD_DETAILS {
|
query QUERY_DASHBOARD_DETAILS {
|
||||||
${componentBasedAdditions}
|
${componentBasedAdditions || ""}
|
||||||
monthly_sales: jobs(where: {_and: [{date_invoiced: {_gte: "${moment()
|
monthly_sales: jobs(where: {_and: [{date_invoiced: {_gte: "${moment()
|
||||||
.startOf("month")
|
.startOf("month")
|
||||||
.format("YYYY-MM-DD")}"}}, {date_invoiced: {_lte: "${moment()
|
.format("YYYY-MM-DD")}"}}, {date_invoiced: {_lte: "${moment()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import Icon, {
|
import Icon, {
|
||||||
|
FileAddOutlined,
|
||||||
BankFilled,
|
BankFilled,
|
||||||
BarChartOutlined,
|
BarChartOutlined,
|
||||||
CarFilled,
|
CarFilled,
|
||||||
@@ -111,12 +112,14 @@ function Header({
|
|||||||
{t("menus.header.availablejobs")}
|
{t("menus.header.availablejobs")}
|
||||||
</Link>
|
</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
<Menu.Item key="newjob" icon={<FileAddOutlined />}>
|
||||||
|
<Link to="/manage/jobs/new">{t("menus.header.newjob")}</Link>
|
||||||
|
</Menu.Item>
|
||||||
<Menu.Divider key="div1" />
|
<Menu.Divider key="div1" />
|
||||||
<Menu.Item key="alljobs" icon={<UnorderedListOutlined />}>
|
<Menu.Item key="alljobs" icon={<UnorderedListOutlined />}>
|
||||||
<Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
<Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Divider key="div2" />
|
<Menu.Divider key="div2" />
|
||||||
|
|
||||||
<Menu.Item key="productionlist" icon={<ScheduleOutlined />}>
|
<Menu.Item key="productionlist" icon={<ScheduleOutlined />}>
|
||||||
<Link to="/manage/production/list">
|
<Link to="/manage/production/list">
|
||||||
{t("menus.header.productionlist")}
|
{t("menus.header.productionlist")}
|
||||||
@@ -128,7 +131,6 @@ function Header({
|
|||||||
</Link>
|
</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Divider key="div3" />
|
<Menu.Divider key="div3" />
|
||||||
|
|
||||||
<Menu.Item key="scoreboard" icon={<LineChartOutlined />}>
|
<Menu.Item key="scoreboard" icon={<LineChartOutlined />}>
|
||||||
<Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
<Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { DownOutlined } from "@ant-design/icons";
|
||||||
|
import { Dropdown, Menu } from "antd";
|
||||||
|
import React from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
//currentUser: selectCurrentUser
|
||||||
|
bodyshop: selectBodyshop,
|
||||||
|
});
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||||
|
});
|
||||||
|
|
||||||
|
export function JoblinePresetButton({ bodyshop, form }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleSelect = (item) => {
|
||||||
|
form.setFieldsValue(item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const menu = (
|
||||||
|
<Menu>
|
||||||
|
{bodyshop.md_jobline_presets.map((i, idx) => (
|
||||||
|
<Menu.Item onClick={() => handleSelect(i)} onItemHover key={idx}>
|
||||||
|
{i.label}
|
||||||
|
</Menu.Item>
|
||||||
|
))}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Dropdown trigger={["click"]} overlay={menu}>
|
||||||
|
<a
|
||||||
|
className="ant-dropdown-link"
|
||||||
|
href="# "
|
||||||
|
onClick={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
{t("joblines.labels.presets")} <DownOutlined />
|
||||||
|
</a>
|
||||||
|
</Dropdown>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(JoblinePresetButton);
|
||||||
@@ -3,7 +3,7 @@ import React, { useEffect } from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import InputCurrency from "../form-items-formatted/currency-form-item.component";
|
import InputCurrency from "../form-items-formatted/currency-form-item.component";
|
||||||
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
|
||||||
|
import JoblinesPreset from "../job-lines-preset-button/job-lines-preset-button.component";
|
||||||
export default function JobLinesUpsertModalComponent({
|
export default function JobLinesUpsertModalComponent({
|
||||||
visible,
|
visible,
|
||||||
jobLine,
|
jobLine,
|
||||||
@@ -32,6 +32,7 @@ export default function JobLinesUpsertModalComponent({
|
|||||||
onOk={() => form.submit()}
|
onOk={() => form.submit()}
|
||||||
okButtonProps={{ loading: loading }}
|
okButtonProps={{ loading: loading }}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
|
e
|
||||||
>
|
>
|
||||||
<Form
|
<Form
|
||||||
onFinish={handleFinish}
|
onFinish={handleFinish}
|
||||||
@@ -41,6 +42,9 @@ export default function JobLinesUpsertModalComponent({
|
|||||||
form={form}
|
form={form}
|
||||||
>
|
>
|
||||||
<LayoutFormRow grow>
|
<LayoutFormRow grow>
|
||||||
|
<Form.Item label={t("joblines.fields.line_no")} name="line_no">
|
||||||
|
<InputNumber />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t("joblines.fields.line_desc")}
|
label={t("joblines.fields.line_desc")}
|
||||||
rules={[
|
rules={[
|
||||||
@@ -53,6 +57,7 @@ export default function JobLinesUpsertModalComponent({
|
|||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<JoblinesPreset form={form} />
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
<LayoutFormRow grow>
|
<LayoutFormRow grow>
|
||||||
<Form.Item label={t("joblines.fields.mod_lbr_ty")} name="mod_lbr_ty">
|
<Form.Item label={t("joblines.fields.mod_lbr_ty")} name="mod_lbr_ty">
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
|
import { useQuery } from "@apollo/client";
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import JobsCreateVehicleInfoComponent from "./jobs-create-vehicle-info.component";
|
import { SEARCH_VEHICLES } from "../../graphql/vehicles.queries";
|
||||||
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
||||||
import AlertComponent from "../alert/alert.component";
|
import AlertComponent from "../alert/alert.component";
|
||||||
import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
|
import JobsCreateVehicleInfoComponent from "./jobs-create-vehicle-info.component";
|
||||||
import { useQuery } from "@apollo/client";
|
|
||||||
|
|
||||||
export default function JobsCreateVehicleInfoContainer({ form }) {
|
export default function JobsCreateVehicleInfoContainer({ form }) {
|
||||||
const [state] = useContext(JobCreateContext);
|
const [state] = useContext(JobCreateContext);
|
||||||
const { loading, error, data } = useQuery(SEARCH_VEHICLE_BY_VIN, {
|
const { loading, error, data } = useQuery(SEARCH_VEHICLES, {
|
||||||
variables: { vin: `%${state.vehicle.search}%` },
|
variables: { search: `%${state.vehicle.search}%` },
|
||||||
skip: !state.vehicle.search,
|
skip: !state.vehicle.search,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ export default function JobsCreateVehicleInfoContainer({ form }) {
|
|||||||
return (
|
return (
|
||||||
<JobsCreateVehicleInfoComponent
|
<JobsCreateVehicleInfoComponent
|
||||||
loading={loading}
|
loading={loading}
|
||||||
vehicles={data ? data.vehicles : null}
|
vehicles={data ? data.search_vehicles : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -825,6 +825,180 @@ export default function ShopInfoGeneral({ form }) {
|
|||||||
}}
|
}}
|
||||||
</Form.List>
|
</Form.List>
|
||||||
</LayoutFormRow>
|
</LayoutFormRow>
|
||||||
|
<LayoutFormRow grow header={t("bodyshop.fields.md_jobline_presets")}>
|
||||||
|
<Form.List name={["md_jobline_presets"]}>
|
||||||
|
{(fields, { add, remove, move }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{fields.map((field, index) => (
|
||||||
|
<Form.Item key={field.key}>
|
||||||
|
<LayoutFormRow noDivider>
|
||||||
|
<Form.Item
|
||||||
|
label={t("general.labels.label")}
|
||||||
|
key={`${index}label`}
|
||||||
|
name={[field.name, "label"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.line_desc")}
|
||||||
|
key={`${index}line_desc`}
|
||||||
|
name={[field.name, "line_desc"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.mod_lbr_ty")}
|
||||||
|
key={`${index}mod_lbr_ty`}
|
||||||
|
name={[field.name, "mod_lbr_ty"]}
|
||||||
|
>
|
||||||
|
<Select allowClear>
|
||||||
|
<Select.Option value="LAA">
|
||||||
|
{t("joblines.fields.lbr_types.LAA")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LAB">
|
||||||
|
{t("joblines.fields.lbr_types.LAB")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LAD">
|
||||||
|
{t("joblines.fields.lbr_types.LAD")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LAE">
|
||||||
|
{t("joblines.fields.lbr_types.LAE")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LAF">
|
||||||
|
{t("joblines.fields.lbr_types.LAF")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LAG">
|
||||||
|
{t("joblines.fields.lbr_types.LAG")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LAM">
|
||||||
|
{t("joblines.fields.lbr_types.LAM")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LAR">
|
||||||
|
{t("joblines.fields.lbr_types.LAR")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LAS">
|
||||||
|
{t("joblines.fields.lbr_types.LAS")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LAU">
|
||||||
|
{t("joblines.fields.lbr_types.LAU")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LA1">
|
||||||
|
{t("joblines.fields.lbr_types.LA1")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LA2">
|
||||||
|
{t("joblines.fields.lbr_types.LA2")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LA3">
|
||||||
|
{t("joblines.fields.lbr_types.LA3")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="LA4">
|
||||||
|
{t("joblines.fields.lbr_types.LA4")}
|
||||||
|
</Select.Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.mod_lb_hrs")}
|
||||||
|
key={`${index}mod_lb_hrs`}
|
||||||
|
name={[field.name, "mod_lb_hrs"]}
|
||||||
|
>
|
||||||
|
<InputNumber precision={1} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.part_type")}
|
||||||
|
key={`${index}part_type`}
|
||||||
|
name={[field.name, "part_type"]}
|
||||||
|
>
|
||||||
|
<Select allowClear>
|
||||||
|
<Select.Option value="PAA">
|
||||||
|
{t("joblines.fields.part_types.PAA")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAC">
|
||||||
|
{t("joblines.fields.part_types.PAC")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAE">
|
||||||
|
{t("joblines.fields.part_types.PAE")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAL">
|
||||||
|
{t("joblines.fields.part_types.PAL")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAM">
|
||||||
|
{t("joblines.fields.part_types.PAM")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAN">
|
||||||
|
{t("joblines.fields.part_types.PAN")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAO">
|
||||||
|
{t("joblines.fields.part_types.PAO")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAR">
|
||||||
|
{t("joblines.fields.part_types.PAR")}
|
||||||
|
</Select.Option>
|
||||||
|
<Select.Option value="PAS">
|
||||||
|
{t("joblines.fields.part_types.PAS")}
|
||||||
|
</Select.Option>
|
||||||
|
</Select>{" "}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.oem_partno")}
|
||||||
|
key={`${index}oem_partno`}
|
||||||
|
name={[field.name, "oem_partno"]}
|
||||||
|
>
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.part_qty")}
|
||||||
|
key={`${index}part_qty`}
|
||||||
|
name={[field.name, "part_qty"]}
|
||||||
|
>
|
||||||
|
<CurrencyInput precision={2} min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.act_price")}
|
||||||
|
key={`${index}act_price`}
|
||||||
|
name={[field.name, "act_price"]}
|
||||||
|
>
|
||||||
|
<CurrencyInput precision={2} min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label={t("joblines.fields.prt_dsmk_p")}
|
||||||
|
key={`${index}prt_dsmk_p`}
|
||||||
|
name={[field.name, "prt_dsmk_p"]}
|
||||||
|
>
|
||||||
|
<InputNumber precision={0} min={0} max={100} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Space wrap>
|
||||||
|
<DeleteFilled
|
||||||
|
onClick={() => {
|
||||||
|
remove(field.name);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<FormListMoveArrows
|
||||||
|
move={move}
|
||||||
|
index={index}
|
||||||
|
total={fields.length}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</LayoutFormRow>
|
||||||
|
</Form.Item>
|
||||||
|
))}
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="dashed"
|
||||||
|
onClick={() => {
|
||||||
|
add();
|
||||||
|
}}
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
>
|
||||||
|
{t("general.actions.add")}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
</LayoutFormRow>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,7 +228,6 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
|||||||
where: { scheduled_completion: { _gte: $start, _lte: $end } }
|
where: { scheduled_completion: { _gte: $start, _lte: $end } }
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
|
|
||||||
ro_number
|
ro_number
|
||||||
scheduled_completion
|
scheduled_completion
|
||||||
labhrs: joblines_aggregate(
|
labhrs: joblines_aggregate(
|
||||||
@@ -250,15 +249,9 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arrJobs: jobs(
|
arrJobs: jobs(where: { scheduled_in: { _gte: $start, _lte: $end } }) {
|
||||||
where: {
|
|
||||||
scheduled_in: { _gte: $start, _lte: $end }
|
|
||||||
removed: { _eq: false }
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
id
|
id
|
||||||
scheduled_in
|
scheduled_in
|
||||||
|
|
||||||
ro_number
|
ro_number
|
||||||
labhrs: joblines_aggregate(
|
labhrs: joblines_aggregate(
|
||||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ export const QUERY_BODYSHOP = gql`
|
|||||||
enforce_referral
|
enforce_referral
|
||||||
website
|
website
|
||||||
jc_hourly_rates
|
jc_hourly_rates
|
||||||
|
md_jobline_presets
|
||||||
employees {
|
employees {
|
||||||
id
|
id
|
||||||
active
|
active
|
||||||
@@ -173,6 +174,7 @@ export const UPDATE_SHOP = gql`
|
|||||||
enforce_referral
|
enforce_referral
|
||||||
website
|
website
|
||||||
jc_hourly_rates
|
jc_hourly_rates
|
||||||
|
md_jobline_presets
|
||||||
employees {
|
employees {
|
||||||
id
|
id
|
||||||
first_name
|
first_name
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ export const UPDATE_JOB_LINE = gql`
|
|||||||
db_price
|
db_price
|
||||||
act_price
|
act_price
|
||||||
line_desc
|
line_desc
|
||||||
|
line_no
|
||||||
oem_partno
|
oem_partno
|
||||||
notes
|
notes
|
||||||
location
|
location
|
||||||
|
|||||||
@@ -133,6 +133,36 @@ export const SEARCH_VEHICLE_BY_VIN = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const SEARCH_VEHICLES = gql`
|
||||||
|
query SEARCH_VEHICLES($search: String!) {
|
||||||
|
search_vehicles(args: { search: $search }) {
|
||||||
|
id
|
||||||
|
plate_no
|
||||||
|
plate_st
|
||||||
|
v_vin
|
||||||
|
v_model_yr
|
||||||
|
v_model_desc
|
||||||
|
v_make_desc
|
||||||
|
v_color
|
||||||
|
v_bstyle
|
||||||
|
updated_at
|
||||||
|
v_type
|
||||||
|
v_trimcode
|
||||||
|
v_tone
|
||||||
|
v_stage
|
||||||
|
v_prod_dt
|
||||||
|
v_paint_codes
|
||||||
|
v_options
|
||||||
|
v_mldgcode
|
||||||
|
v_makecode
|
||||||
|
v_engine
|
||||||
|
v_cond
|
||||||
|
trim_color
|
||||||
|
db_v_code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const SEARCH_VEHICLES_BY_ID_FOR_AUTOCOMPLETE = gql`
|
export const SEARCH_VEHICLES_BY_ID_FOR_AUTOCOMPLETE = gql`
|
||||||
query SEARCH_VEHICLES_BY_ID_FOR_AUTOCOMPLETE($id: uuid!) {
|
query SEARCH_VEHICLES_BY_ID_FOR_AUTOCOMPLETE($id: uuid!) {
|
||||||
vehicles_by_pk(id: $id) {
|
vehicles_by_pk(id: $id) {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = t("titles.jobs-create");
|
document.title = t("titles.jobs-create");
|
||||||
setSelectedHeader("availablejobs");
|
setSelectedHeader("newjob");
|
||||||
setBreadcrumbs([
|
setBreadcrumbs([
|
||||||
{ link: "/manage/available", label: t("titles.bc.availablejobs") },
|
{ link: "/manage/available", label: t("titles.bc.availablejobs") },
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -243,6 +243,7 @@
|
|||||||
"street2": "Street 2",
|
"street2": "Street 2",
|
||||||
"zip": "Zip/Postal Code"
|
"zip": "Zip/Postal Code"
|
||||||
},
|
},
|
||||||
|
"md_jobline_presets": "Jobline Presets",
|
||||||
"md_payment_types": "Payment Types",
|
"md_payment_types": "Payment Types",
|
||||||
"md_referral_sources": "Referral Sources",
|
"md_referral_sources": "Referral Sources",
|
||||||
"messaginglabel": "Messaging Preset Label",
|
"messaginglabel": "Messaging Preset Label",
|
||||||
@@ -668,6 +669,7 @@
|
|||||||
"addcomponent": "Add Component"
|
"addcomponent": "Add Component"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"refreshrequired": "You must refresh the dashboard data to see this component.",
|
||||||
"updatinglayout": "Error saving updated layout {{message}}"
|
"updatinglayout": "Error saving updated layout {{message}}"
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -992,7 +994,8 @@
|
|||||||
"billref": "Latest Bill",
|
"billref": "Latest Bill",
|
||||||
"edit": "Edit Line",
|
"edit": "Edit Line",
|
||||||
"new": "New Line",
|
"new": "New Line",
|
||||||
"nostatus": "No Status"
|
"nostatus": "No Status",
|
||||||
|
"presets": "Jobline Presets"
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "Job line created successfully.",
|
"created": "Job line created successfully.",
|
||||||
@@ -1440,6 +1443,7 @@
|
|||||||
"help": "Help",
|
"help": "Help",
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"jobs": "Jobs",
|
"jobs": "Jobs",
|
||||||
|
"newjob": "Create New Job",
|
||||||
"owners": "Owners",
|
"owners": "Owners",
|
||||||
"parts-queue": "Parts Queue",
|
"parts-queue": "Parts Queue",
|
||||||
"phonebook": "Phonebook",
|
"phonebook": "Phonebook",
|
||||||
|
|||||||
@@ -243,6 +243,7 @@
|
|||||||
"street2": "",
|
"street2": "",
|
||||||
"zip": ""
|
"zip": ""
|
||||||
},
|
},
|
||||||
|
"md_jobline_presets": "",
|
||||||
"md_payment_types": "",
|
"md_payment_types": "",
|
||||||
"md_referral_sources": "",
|
"md_referral_sources": "",
|
||||||
"messaginglabel": "",
|
"messaginglabel": "",
|
||||||
@@ -668,6 +669,7 @@
|
|||||||
"addcomponent": ""
|
"addcomponent": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"refreshrequired": "",
|
||||||
"updatinglayout": ""
|
"updatinglayout": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -992,7 +994,8 @@
|
|||||||
"billref": "",
|
"billref": "",
|
||||||
"edit": "Línea de edición",
|
"edit": "Línea de edición",
|
||||||
"new": "Nueva línea",
|
"new": "Nueva línea",
|
||||||
"nostatus": ""
|
"nostatus": "",
|
||||||
|
"presets": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "",
|
"created": "",
|
||||||
@@ -1440,6 +1443,7 @@
|
|||||||
"help": "",
|
"help": "",
|
||||||
"home": "Casa",
|
"home": "Casa",
|
||||||
"jobs": "Trabajos",
|
"jobs": "Trabajos",
|
||||||
|
"newjob": "",
|
||||||
"owners": "propietarios",
|
"owners": "propietarios",
|
||||||
"parts-queue": "",
|
"parts-queue": "",
|
||||||
"phonebook": "",
|
"phonebook": "",
|
||||||
|
|||||||
@@ -243,6 +243,7 @@
|
|||||||
"street2": "",
|
"street2": "",
|
||||||
"zip": ""
|
"zip": ""
|
||||||
},
|
},
|
||||||
|
"md_jobline_presets": "",
|
||||||
"md_payment_types": "",
|
"md_payment_types": "",
|
||||||
"md_referral_sources": "",
|
"md_referral_sources": "",
|
||||||
"messaginglabel": "",
|
"messaginglabel": "",
|
||||||
@@ -668,6 +669,7 @@
|
|||||||
"addcomponent": ""
|
"addcomponent": ""
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"refreshrequired": "",
|
||||||
"updatinglayout": ""
|
"updatinglayout": ""
|
||||||
},
|
},
|
||||||
"labels": {
|
"labels": {
|
||||||
@@ -992,7 +994,8 @@
|
|||||||
"billref": "",
|
"billref": "",
|
||||||
"edit": "Ligne d'édition",
|
"edit": "Ligne d'édition",
|
||||||
"new": "Nouvelle ligne",
|
"new": "Nouvelle ligne",
|
||||||
"nostatus": ""
|
"nostatus": "",
|
||||||
|
"presets": ""
|
||||||
},
|
},
|
||||||
"successes": {
|
"successes": {
|
||||||
"created": "",
|
"created": "",
|
||||||
@@ -1440,6 +1443,7 @@
|
|||||||
"help": "",
|
"help": "",
|
||||||
"home": "Accueil",
|
"home": "Accueil",
|
||||||
"jobs": "Emplois",
|
"jobs": "Emplois",
|
||||||
|
"newjob": "",
|
||||||
"owners": "Propriétaires",
|
"owners": "Propriétaires",
|
||||||
"parts-queue": "",
|
"parts-queue": "",
|
||||||
"phonebook": "",
|
"phonebook": "",
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "md_jobline_presets";
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- args:
|
||||||
|
cascade: false
|
||||||
|
read_only: false
|
||||||
|
sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "md_jobline_presets" jsonb NULL
|
||||||
|
DEFAULT jsonb_build_array();
|
||||||
|
type: run_sql
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- bill_tax_rates
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- imexshopid
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- jobsizelimit
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- messagingservicesid
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- region_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- stripe_acct_id
|
||||||
|
- sub_status
|
||||||
|
- target_touchtime
|
||||||
|
- template_header
|
||||||
|
- textid
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_select_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
allow_aggregations: false
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- bill_tax_rates
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- imexshopid
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- jobsizelimit
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_jobline_presets
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- messagingservicesid
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- region_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- stripe_acct_id
|
||||||
|
- sub_status
|
||||||
|
- target_touchtime
|
||||||
|
- template_header
|
||||||
|
- textid
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
computed_fields: []
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_select_permission
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- bill_tax_rates
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- target_touchtime
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
- args:
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: drop_update_permission
|
||||||
|
- args:
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- accountingconfig
|
||||||
|
- address1
|
||||||
|
- address2
|
||||||
|
- appt_alt_transport
|
||||||
|
- appt_colors
|
||||||
|
- appt_length
|
||||||
|
- bill_tax_rates
|
||||||
|
- city
|
||||||
|
- country
|
||||||
|
- created_at
|
||||||
|
- default_adjustment_rate
|
||||||
|
- deliverchecklist
|
||||||
|
- email
|
||||||
|
- enforce_class
|
||||||
|
- enforce_referral
|
||||||
|
- federal_tax_id
|
||||||
|
- id
|
||||||
|
- inhousevendorid
|
||||||
|
- insurance_vendor_id
|
||||||
|
- intakechecklist
|
||||||
|
- jc_hourly_rates
|
||||||
|
- logo_img_path
|
||||||
|
- md_categories
|
||||||
|
- md_ccc_rates
|
||||||
|
- md_classes
|
||||||
|
- md_hour_split
|
||||||
|
- md_ins_cos
|
||||||
|
- md_jobline_presets
|
||||||
|
- md_labor_rates
|
||||||
|
- md_messaging_presets
|
||||||
|
- md_notes_presets
|
||||||
|
- md_order_statuses
|
||||||
|
- md_parts_locations
|
||||||
|
- md_payment_types
|
||||||
|
- md_rbac
|
||||||
|
- md_referral_sources
|
||||||
|
- md_responsibility_centers
|
||||||
|
- md_ro_statuses
|
||||||
|
- phone
|
||||||
|
- prodtargethrs
|
||||||
|
- production_config
|
||||||
|
- schedule_end_time
|
||||||
|
- schedule_start_time
|
||||||
|
- scoreboard_target
|
||||||
|
- shopname
|
||||||
|
- shoprates
|
||||||
|
- speedprint
|
||||||
|
- ssbuckets
|
||||||
|
- state
|
||||||
|
- state_tax_id
|
||||||
|
- target_touchtime
|
||||||
|
- updated_at
|
||||||
|
- use_fippa
|
||||||
|
- website
|
||||||
|
- workingdays
|
||||||
|
- zip_post
|
||||||
|
filter:
|
||||||
|
associations:
|
||||||
|
_and:
|
||||||
|
- user:
|
||||||
|
authid:
|
||||||
|
_eq: X-Hasura-User-Id
|
||||||
|
- active:
|
||||||
|
_eq: true
|
||||||
|
set: {}
|
||||||
|
role: user
|
||||||
|
table:
|
||||||
|
name: bodyshops
|
||||||
|
schema: public
|
||||||
|
type: create_update_permission
|
||||||
@@ -779,6 +779,7 @@ tables:
|
|||||||
- md_classes
|
- md_classes
|
||||||
- md_hour_split
|
- md_hour_split
|
||||||
- md_ins_cos
|
- md_ins_cos
|
||||||
|
- md_jobline_presets
|
||||||
- md_labor_rates
|
- md_labor_rates
|
||||||
- md_messaging_presets
|
- md_messaging_presets
|
||||||
- md_notes_presets
|
- md_notes_presets
|
||||||
@@ -849,6 +850,7 @@ tables:
|
|||||||
- md_classes
|
- md_classes
|
||||||
- md_hour_split
|
- md_hour_split
|
||||||
- md_ins_cos
|
- md_ins_cos
|
||||||
|
- md_jobline_presets
|
||||||
- md_labor_rates
|
- md_labor_rates
|
||||||
- md_messaging_presets
|
- md_messaging_presets
|
||||||
- md_notes_presets
|
- md_notes_presets
|
||||||
|
|||||||
@@ -31,13 +31,17 @@ exports.default = async (req, res) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log("***Number of Failed jobs***: ", erroredJobs.length);
|
console.log(
|
||||||
|
"***Number of Failed jobs***: ",
|
||||||
|
erroredJobs.length,
|
||||||
|
JSON.stringify(erroredJobs.map((x) => x.error))
|
||||||
|
);
|
||||||
var ret = builder
|
var ret = builder
|
||||||
.create(autoHouseObject, {
|
.create(autoHouseObject, {
|
||||||
version: "1.0",
|
version: "1.0",
|
||||||
encoding: "UTF-8",
|
encoding: "UTF-8",
|
||||||
})
|
})
|
||||||
.end({ pretty: true });
|
.end({ pretty: true, allowEmptyTags: true });
|
||||||
|
|
||||||
//***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
|
//***TODO Change filing naming when creating the cron job. IM_ShopInternalName_DDMMYYYY_HHMMSS.xml
|
||||||
res.type("application/xml");
|
res.type("application/xml");
|
||||||
@@ -48,6 +52,8 @@ exports.default = async (req, res) => {
|
|||||||
const CreateRepairOrderTag = (job, errorCallback) => {
|
const CreateRepairOrderTag = (job, errorCallback) => {
|
||||||
//Level 2
|
//Level 2
|
||||||
|
|
||||||
|
const repairCosts = CreateCosts(job);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const ret = {
|
const ret = {
|
||||||
RepairOrderInformation: {
|
RepairOrderInformation: {
|
||||||
@@ -63,8 +69,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
ShopState: job.bodyshop.state,
|
ShopState: job.bodyshop.state,
|
||||||
ShopZip: job.bodyshop.zip_post,
|
ShopZip: job.bodyshop.zip_post,
|
||||||
ShopPhone: job.bodyshop.phone,
|
ShopPhone: job.bodyshop.phone,
|
||||||
EstimatorID: `${job.est_ct_fn} ${job.est_ct_ln}`,
|
EstimatorID: `${job.est_ct_fn || ""} ${job.est_ct_ln || ""}`,
|
||||||
EstimatorName: `${job.est_ct_fn} ${job.est_ct_ln}`,
|
EstimatorName: `${job.est_ct_fn || ""} ${job.est_ct_ln || ""}`,
|
||||||
},
|
},
|
||||||
CustomerInformation: {
|
CustomerInformation: {
|
||||||
FirstName: job.ownr_fn,
|
FirstName: job.ownr_fn,
|
||||||
@@ -97,7 +103,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
VehiclePaintCode: null,
|
VehiclePaintCode: null,
|
||||||
VehicleTrimCode: null,
|
VehicleTrimCode: null,
|
||||||
VehicleBodyStyle: null,
|
VehicleBodyStyle: null,
|
||||||
DriveableFlag: job.tlos_ind ? "Y" : "N",
|
DriveableFlag: job.driveable ? "Y" : "N",
|
||||||
},
|
},
|
||||||
|
|
||||||
InsuranceInformation: {
|
InsuranceInformation: {
|
||||||
@@ -251,25 +257,39 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
},
|
},
|
||||||
RevisedTotals: {
|
RevisedTotals: {
|
||||||
BodyHours: job.job_totals.rates.lab.hours,
|
BodyHours: job.job_totals.rates.lab.hours,
|
||||||
|
BodyRepairHours: job.joblines
|
||||||
|
.filter((line) => repairOpCodes.includes(line.lbr_op))
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0),
|
||||||
|
BodyReplaceHours: job.joblines
|
||||||
|
.filter((line) => replaceOpCodes.includes(line.lbr_op))
|
||||||
|
.reduce((acc, val) => acc + val.mod_lb_hrs, 0),
|
||||||
RefinishHours: job.job_totals.rates.lar.hours,
|
RefinishHours: job.job_totals.rates.lar.hours,
|
||||||
MechanicalHours: job.job_totals.rates.lam.hours,
|
MechanicalHours: job.job_totals.rates.lam.hours,
|
||||||
StructuralHours: job.job_totals.rates.las.hours,
|
StructuralHours: job.job_totals.rates.las.hours,
|
||||||
PartsTotal: Dinero(job.job_totals.parts.parts.total).toFormat(
|
PartsTotal: Dinero(job.job_totals.parts.parts.total).toFormat(
|
||||||
AHDineroFormat
|
AHDineroFormat
|
||||||
),
|
),
|
||||||
PartsTotalCost: 0,
|
PartsTotalCost: repairCosts.PartsTotalCost.toFormat(AHDineroFormat),
|
||||||
PartsOEM: Dinero(
|
PartsOEM: Dinero(
|
||||||
job.job_totals.parts.parts.list.PAN &&
|
job.job_totals.parts.parts.list.PAN &&
|
||||||
job.job_totals.parts.parts.list.PAN.total
|
job.job_totals.parts.parts.list.PAN.total
|
||||||
).toFormat(AHDineroFormat),
|
)
|
||||||
PartsOEMCost: 0,
|
.add(
|
||||||
|
Dinero(
|
||||||
|
job.job_totals.parts.parts.list.PAP &&
|
||||||
|
job.job_totals.parts.parts.list.PAP.total
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.toFormat(AHDineroFormat),
|
||||||
|
PartsOEMCost: repairCosts.PartsOemCost.toFormat(AHDineroFormat),
|
||||||
PartsAM: Dinero(
|
PartsAM: Dinero(
|
||||||
job.job_totals.parts.parts.list.PAA &&
|
job.job_totals.parts.parts.list.PAA &&
|
||||||
job.job_totals.parts.parts.list.PAA.total
|
job.job_totals.parts.parts.list.PAA.total
|
||||||
).toFormat(AHDineroFormat),
|
).toFormat(AHDineroFormat),
|
||||||
PartsAMCost: 0,
|
PartsAMCost: repairCosts.PartsAMCost.toFormat(AHDineroFormat),
|
||||||
PartsReconditioned: null,
|
PartsReconditioned: null,
|
||||||
PartsReconditionedCost: null,
|
PartsReconditionedCost:
|
||||||
|
repairCosts.PartsReconditionedCost.toFormat(AHDineroFormat),
|
||||||
PartsRecycled: Dinero(
|
PartsRecycled: Dinero(
|
||||||
job.job_totals.parts.parts.list.PAR &&
|
job.job_totals.parts.parts.list.PAR &&
|
||||||
job.job_totals.parts.parts.list.PAR.total
|
job.job_totals.parts.parts.list.PAR.total
|
||||||
@@ -389,10 +409,108 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
|||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log("Error calculating job", error);
|
||||||
errorCallback(job, error);
|
errorCallback(job, error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CreateCosts = (job) => {
|
||||||
|
//Create a mapping based on AH Requirements
|
||||||
|
|
||||||
|
const billTotalsByCostCenters = job.bills.reduce((bill_acc, bill_val) => {
|
||||||
|
//At the bill level.
|
||||||
|
bill_val.billlines.map((line_val) => {
|
||||||
|
//At the bill line level.
|
||||||
|
//console.log("JobCostingPartsTable -> line_val", line_val);
|
||||||
|
if (!bill_acc[line_val.cost_center])
|
||||||
|
bill_acc[line_val.cost_center] = Dinero();
|
||||||
|
|
||||||
|
bill_acc[line_val.cost_center] = bill_acc[line_val.cost_center].add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round((line_val.actual_cost || 0) * 100),
|
||||||
|
})
|
||||||
|
.multiply(line_val.quantity)
|
||||||
|
.multiply(bill_val.is_credit_memo ? -1 : 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
return bill_acc;
|
||||||
|
}, {});
|
||||||
|
const materialsHours = { mapaHrs: 0, mashHrs: 0 };
|
||||||
|
//If the hourly rates for job costing are set, add them in.
|
||||||
|
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mapa) {
|
||||||
|
if (
|
||||||
|
!billTotalsByCostCenters[
|
||||||
|
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||||
|
]
|
||||||
|
)
|
||||||
|
billTotalsByCostCenters[
|
||||||
|
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||||
|
] = Dinero();
|
||||||
|
billTotalsByCostCenters[
|
||||||
|
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||||
|
] = billTotalsByCostCenters[
|
||||||
|
job.bodyshop.md_responsibility_centers.defaults.costs.MAPA
|
||||||
|
].add(
|
||||||
|
Dinero({
|
||||||
|
amount:
|
||||||
|
(job.bodyshop.jc_hourly_rates &&
|
||||||
|
job.bodyshop.jc_hourly_rates.mapa * 100) ||
|
||||||
|
0,
|
||||||
|
}).multiply(materialsHours.mapaHrs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const ticketTotalsByCostCenter = job.timetickets.reduce(
|
||||||
|
(ticket_acc, ticket_val) => {
|
||||||
|
//At the invoice level.
|
||||||
|
if (!ticket_acc[ticket_val.cost_center])
|
||||||
|
ticket_acc[ticket_val.cost_center] = Dinero();
|
||||||
|
|
||||||
|
ticket_acc[ticket_val.cost_center] = ticket_acc[
|
||||||
|
ticket_val.cost_center
|
||||||
|
].add(
|
||||||
|
Dinero({
|
||||||
|
amount: Math.round((ticket_val.rate || 0) * 100),
|
||||||
|
}).multiply(ticket_val.actualhrs || ticket_val.productivehrs || 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
return ticket_acc;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const defaultCosts = job.bodyshop.md_responsibility_centers.defaults.costs;
|
||||||
|
|
||||||
|
return {
|
||||||
|
PartsTotalCost: Object.keys(billTotalsByCostCenters).reduce((acc, key) => {
|
||||||
|
return acc.add(billTotalsByCostCenters[key]);
|
||||||
|
}, Dinero()),
|
||||||
|
PartsOemCost: (billTotalsByCostCenters[defaultCosts.PAN] || Dinero()).add(
|
||||||
|
billTotalsByCostCenters[defaultCosts.PAP] || Dinero()
|
||||||
|
),
|
||||||
|
PartsAMCost: billTotalsByCostCenters[defaultCosts.PAA] || Dinero(),
|
||||||
|
PartsReconditionedCost: Dinero(),
|
||||||
|
PartsRecycledCost: billTotalsByCostCenters[defaultCosts.PAR] || Dinero(),
|
||||||
|
PartsOtherCost: billTotalsByCostCenters[defaultCosts.PAO] || Dinero(),
|
||||||
|
SubletTotalCost: billTotalsByCostCenters[defaultCosts.PAS] || Dinero(),
|
||||||
|
BodyLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAB] || Dinero(),
|
||||||
|
RefinishLaborTotalCost:
|
||||||
|
ticketTotalsByCostCenter[defaultCosts.LAR] || Dinero(),
|
||||||
|
MechanicalLaborTotalCost:
|
||||||
|
ticketTotalsByCostCenter[defaultCosts.LAM] || Dinero(),
|
||||||
|
StructuralLaborTotalCost:
|
||||||
|
ticketTotalsByCostCenter[defaultCosts.LAS] || Dinero(),
|
||||||
|
PMTotalCost: billTotalsByCostCenters[defaultCosts.MAPA] || Dinero(),
|
||||||
|
BMTotalCost: billTotalsByCostCenters[defaultCosts.MASH] || Dinero(),
|
||||||
|
MiscTotalCost: billTotalsByCostCenters[defaultCosts.PAO] || Dinero(),
|
||||||
|
TowingTotalCost: billTotalsByCostCenters[defaultCosts.TOW] || Dinero(),
|
||||||
|
StorageTotalCost: Dinero(),
|
||||||
|
DetailTotal: Dinero(),
|
||||||
|
DetailTotalCost: Dinero(),
|
||||||
|
SalesTaxTotalCost: Dinero(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const StatusMapping = (status, md_ro_statuses) => {
|
const StatusMapping = (status, md_ro_statuses) => {
|
||||||
//EST, SCH, ARR, IPR, RDY, DEL, CLO, CAN, UNDEFINED.
|
//EST, SCH, ARR, IPR, RDY, DEL, CLO, CAN, UNDEFINED.
|
||||||
const {
|
const {
|
||||||
@@ -493,3 +611,6 @@ const generateNullDetailLine = () => {
|
|||||||
EstimateAmount: null,
|
EstimateAmount: null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const repairOpCodes = ["OP4", "OP9", "OP10"];
|
||||||
|
const replaceOpCodes = ["OP2", "OP5", "OP11", "OP12"];
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
|
|||||||
rate_mapa
|
rate_mapa
|
||||||
rate_mash
|
rate_mash
|
||||||
job_totals
|
job_totals
|
||||||
|
driveable
|
||||||
bodyshop {
|
bodyshop {
|
||||||
id
|
id
|
||||||
shopname
|
shopname
|
||||||
@@ -350,6 +351,8 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
|
|||||||
md_ro_statuses
|
md_ro_statuses
|
||||||
md_order_statuses
|
md_order_statuses
|
||||||
autohouseid
|
autohouseid
|
||||||
|
md_responsibility_centers
|
||||||
|
jc_hourly_rates
|
||||||
}
|
}
|
||||||
joblines (where:{removed: {_eq:false}}){
|
joblines (where:{removed: {_eq:false}}){
|
||||||
id
|
id
|
||||||
@@ -366,7 +369,10 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
|
|||||||
part_qty
|
part_qty
|
||||||
part_type
|
part_type
|
||||||
oem_partno
|
oem_partno
|
||||||
billlines (order_by:{bill:{date:desc_nulls_last}}) {
|
lbr_op
|
||||||
|
profitcenter_part
|
||||||
|
profitcenter_labor
|
||||||
|
billlines (order_by:{bill:{date:desc_nulls_last}}) {
|
||||||
actual_cost
|
actual_cost
|
||||||
actual_price
|
actual_price
|
||||||
quantity
|
quantity
|
||||||
@@ -377,7 +383,27 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
|
|||||||
invoice_number
|
invoice_number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
} bills {
|
||||||
|
id
|
||||||
|
federal_tax_rate
|
||||||
|
local_tax_rate
|
||||||
|
state_tax_rate
|
||||||
|
is_credit_memo
|
||||||
|
billlines {
|
||||||
|
actual_cost
|
||||||
|
cost_center
|
||||||
|
id
|
||||||
|
quantity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timetickets {
|
||||||
|
id
|
||||||
|
rate
|
||||||
|
cost_center
|
||||||
|
actualhrs
|
||||||
|
productivehrs
|
||||||
|
}
|
||||||
area_of_damage
|
area_of_damage
|
||||||
employee_prep_rel {
|
employee_prep_rel {
|
||||||
first_name
|
first_name
|
||||||
|
|||||||
@@ -353,11 +353,12 @@ function CalculateTaxesTotals(job, otherTotals) {
|
|||||||
|
|
||||||
//Audatex sends additional glass part types. IO-774
|
//Audatex sends additional glass part types. IO-774
|
||||||
const BackupGlassTax =
|
const BackupGlassTax =
|
||||||
job.parts_tax_rates.PAGD ||
|
job.parts_tax_rates &&
|
||||||
job.parts_tax_rates.PAGF ||
|
(job.parts_tax_rates.PAGD ||
|
||||||
job.parts_tax_rates.PAGP ||
|
job.parts_tax_rates.PAGF ||
|
||||||
job.parts_tax_rates.PAGQ ||
|
job.parts_tax_rates.PAGP ||
|
||||||
job.parts_tax_rates.PAGR;
|
job.parts_tax_rates.PAGQ ||
|
||||||
|
job.parts_tax_rates.PAGR);
|
||||||
|
|
||||||
job.joblines
|
job.joblines
|
||||||
.filter((jl) => !jl.removed)
|
.filter((jl) => !jl.removed)
|
||||||
|
|||||||
Reference in New Issue
Block a user