@@ -3753,6 +3753,27 @@
|
||||
</concept_node>
|
||||
</children>
|
||||
</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>
|
||||
<name>md_payment_types</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -10532,6 +10553,27 @@
|
||||
<folder_node>
|
||||
<name>errors</name>
|
||||
<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>
|
||||
<name>updatinglayout</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
@@ -15803,6 +15845,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
@@ -24413,6 +24476,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</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>
|
||||
<name>owners</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -32,13 +32,9 @@ export default function DashboardMonthlyLaborSales({ data, ...cardProps }) {
|
||||
return {
|
||||
name: t(`joblines.fields.lbr_types.${key.toUpperCase()}`),
|
||||
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 (
|
||||
<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 RADIAN = Math.PI / 180;
|
||||
//const RADIAN = Math.PI / 180;
|
||||
const {
|
||||
cx,
|
||||
cy,
|
||||
midAngle,
|
||||
//midAngle,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
fill,
|
||||
payload,
|
||||
percent,
|
||||
// percent,
|
||||
value,
|
||||
} = props;
|
||||
const sin = Math.sin(-RADIAN * midAngle);
|
||||
const cos = Math.cos(-RADIAN * midAngle);
|
||||
const sx = cx + (outerRadius + 10) * cos;
|
||||
const sy = cy + (outerRadius + 10) * sin;
|
||||
const mx = cx + (outerRadius + 30) * cos;
|
||||
const my = cy + (outerRadius + 30) * sin;
|
||||
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
||||
const ey = my;
|
||||
const textAnchor = cos >= 0 ? "start" : "end";
|
||||
// const sin = Math.sin(-RADIAN * midAngle);
|
||||
// const cos = Math.cos(-RADIAN * midAngle);
|
||||
// // const sx = cx + (outerRadius + 10) * cos;
|
||||
// const sy = cy + (outerRadius + 10) * sin;
|
||||
// const mx = cx + (outerRadius + 30) * cos;
|
||||
// const my = cy + (outerRadius + 30) * sin;
|
||||
// //const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
||||
// const ey = my;
|
||||
//const textAnchor = cos >= 0 ? "start" : "end";
|
||||
|
||||
return (
|
||||
<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}
|
||||
</text>
|
||||
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
|
||||
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
|
||||
</text>
|
||||
<Sector
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
@@ -120,29 +136,28 @@ const renderActiveShape = (props) => {
|
||||
outerRadius={outerRadius + 10}
|
||||
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>
|
||||
);
|
||||
};
|
||||
// <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 {
|
||||
name: t(`joblines.fields.part_types.${key.toUpperCase()}`),
|
||||
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 (
|
||||
<Card title={t("dashboard.titles.monthlypartssales")} {...cardProps}>
|
||||
@@ -68,37 +64,55 @@ export default function DashboardMonthlyPartsSales({ data, ...cardProps }) {
|
||||
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 RADIAN = Math.PI / 180;
|
||||
// const RADIAN = Math.PI / 180;
|
||||
const {
|
||||
cx,
|
||||
cy,
|
||||
midAngle,
|
||||
// midAngle,
|
||||
innerRadius,
|
||||
outerRadius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
fill,
|
||||
payload,
|
||||
percent,
|
||||
// percent,
|
||||
value,
|
||||
} = props;
|
||||
const sin = Math.sin(-RADIAN * midAngle);
|
||||
const cos = Math.cos(-RADIAN * midAngle);
|
||||
const sx = cx + (outerRadius + 10) * cos;
|
||||
const sy = cy + (outerRadius + 10) * sin;
|
||||
const mx = cx + (outerRadius + 30) * cos;
|
||||
const my = cy + (outerRadius + 30) * sin;
|
||||
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
||||
const ey = my;
|
||||
const textAnchor = cos >= 0 ? "start" : "end";
|
||||
// const sin = Math.sin(-RADIAN * midAngle);
|
||||
// const cos = Math.cos(-RADIAN * midAngle);
|
||||
// const sx = cx + (outerRadius + 10) * cos;
|
||||
//const sy = cy + (outerRadius + 10) * sin;
|
||||
// const mx = cx + (outerRadius + 30) * cos;
|
||||
//const my = cy + (outerRadius + 30) * sin;
|
||||
// const ex = mx + (cos >= 0 ? 1 : -1) * 22;
|
||||
// const ey = my;
|
||||
// const textAnchor = cos >= 0 ? "start" : "end";
|
||||
|
||||
return (
|
||||
<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}
|
||||
</text>
|
||||
<text x={cx} y={cy} dy={16} textAnchor="middle" fill={fill}>
|
||||
{Dinero({ amount: Math.round(value * 100) }).toFormat()}
|
||||
</text>
|
||||
<Sector
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
@@ -117,29 +131,6 @@ const renderActiveShape = (props) => {
|
||||
outerRadius={outerRadius + 10}
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -69,8 +69,8 @@ export default function DashboardMonthlyRevenueGraph({ data, ...cardProps }) {
|
||||
type="monotone"
|
||||
name="Accumulated Sales"
|
||||
dataKey="accSales"
|
||||
fill="#8884d8"
|
||||
stroke="#8884d8"
|
||||
fill="#3CB371"
|
||||
stroke="#3CB371"
|
||||
/>
|
||||
<Bar
|
||||
name="Daily Sales"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Card, Typography } from "antd";
|
||||
import { SyncOutlined } from "@ant-design/icons";
|
||||
|
||||
import { Card } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function DashboardRefreshRequired(props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -11,14 +11,14 @@ export default function DashboardRefreshRequired(props) {
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
<SyncOutlined />
|
||||
<Typography.Title level={4}>
|
||||
{t("dashboard.errors.refreshrequired")}
|
||||
</Typography.Title>
|
||||
<SyncOutlined style={{ fontSize: "300%", margin: "1rem" }} />
|
||||
<div>{t("dashboard.errors.refreshrequired")}</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -198,15 +198,19 @@ const componentList = {
|
||||
label: "Production Dollars",
|
||||
component: DashboardTotalProductionDollars,
|
||||
gqlFragment: null,
|
||||
w: 2,
|
||||
w: 1,
|
||||
h: 1,
|
||||
minW: 2,
|
||||
minH: 1,
|
||||
},
|
||||
ProductionHours: {
|
||||
label: "Production Hours",
|
||||
component: DashboardTotalProductionHours,
|
||||
gqlFragment: DashboardTotalProductionHoursGql,
|
||||
w: 2,
|
||||
w: 3,
|
||||
h: 1,
|
||||
minW: 3,
|
||||
minH: 1,
|
||||
},
|
||||
ProjectedMonthlySales: {
|
||||
label: "Projected Monthly Sales",
|
||||
@@ -214,6 +218,8 @@ const componentList = {
|
||||
gqlFragment: DashboardProjectedMonthlySalesGql,
|
||||
w: 2,
|
||||
h: 1,
|
||||
minW: 2,
|
||||
minH: 1,
|
||||
},
|
||||
MonthlyRevenueGraph: {
|
||||
label: "Monthly Sales Graph",
|
||||
@@ -254,12 +260,15 @@ const componentList = {
|
||||
};
|
||||
|
||||
const createDashboardQuery = (state) => {
|
||||
const componentBasedAdditions = state.layout
|
||||
.map((item, index) => componentList[item.i].gqlFragment || "")
|
||||
.join("");
|
||||
const componentBasedAdditions =
|
||||
state &&
|
||||
Array.isArray(state.layout) &&
|
||||
state.layout
|
||||
.map((item, index) => componentList[item.i].gqlFragment || "")
|
||||
.join("");
|
||||
return gql`
|
||||
query QUERY_DASHBOARD_DETAILS {
|
||||
${componentBasedAdditions}
|
||||
${componentBasedAdditions || ""}
|
||||
monthly_sales: jobs(where: {_and: [{date_invoiced: {_gte: "${moment()
|
||||
.startOf("month")
|
||||
.format("YYYY-MM-DD")}"}}, {date_invoiced: {_lte: "${moment()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Icon, {
|
||||
FileAddOutlined,
|
||||
BankFilled,
|
||||
BarChartOutlined,
|
||||
CarFilled,
|
||||
@@ -111,12 +112,14 @@ function Header({
|
||||
{t("menus.header.availablejobs")}
|
||||
</Link>
|
||||
</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.Item key="alljobs" icon={<UnorderedListOutlined />}>
|
||||
<Link to="/manage/jobs/all">{t("menus.header.alljobs")}</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Divider key="div2" />
|
||||
|
||||
<Menu.Item key="productionlist" icon={<ScheduleOutlined />}>
|
||||
<Link to="/manage/production/list">
|
||||
{t("menus.header.productionlist")}
|
||||
@@ -128,7 +131,6 @@ function Header({
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Divider key="div3" />
|
||||
|
||||
<Menu.Item key="scoreboard" icon={<LineChartOutlined />}>
|
||||
<Link to="/manage/scoreboard">{t("menus.header.scoreboard")}</Link>
|
||||
</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 InputCurrency from "../form-items-formatted/currency-form-item.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({
|
||||
visible,
|
||||
jobLine,
|
||||
@@ -32,6 +32,7 @@ export default function JobLinesUpsertModalComponent({
|
||||
onOk={() => form.submit()}
|
||||
okButtonProps={{ loading: loading }}
|
||||
onCancel={handleCancel}
|
||||
e
|
||||
>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
@@ -41,6 +42,9 @@ export default function JobLinesUpsertModalComponent({
|
||||
form={form}
|
||||
>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item label={t("joblines.fields.line_no")} name="line_no">
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t("joblines.fields.line_desc")}
|
||||
rules={[
|
||||
@@ -53,6 +57,7 @@ export default function JobLinesUpsertModalComponent({
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<JoblinesPreset form={form} />
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow>
|
||||
<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 JobsCreateVehicleInfoComponent from "./jobs-create-vehicle-info.component";
|
||||
import { SEARCH_VEHICLES } from "../../graphql/vehicles.queries";
|
||||
import JobCreateContext from "../../pages/jobs-create/jobs-create.context";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
import { SEARCH_VEHICLE_BY_VIN } from "../../graphql/vehicles.queries";
|
||||
import { useQuery } from "@apollo/client";
|
||||
import JobsCreateVehicleInfoComponent from "./jobs-create-vehicle-info.component";
|
||||
|
||||
export default function JobsCreateVehicleInfoContainer({ form }) {
|
||||
const [state] = useContext(JobCreateContext);
|
||||
const { loading, error, data } = useQuery(SEARCH_VEHICLE_BY_VIN, {
|
||||
variables: { vin: `%${state.vehicle.search}%` },
|
||||
const { loading, error, data } = useQuery(SEARCH_VEHICLES, {
|
||||
variables: { search: `%${state.vehicle.search}%` },
|
||||
skip: !state.vehicle.search,
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function JobsCreateVehicleInfoContainer({ form }) {
|
||||
return (
|
||||
<JobsCreateVehicleInfoComponent
|
||||
loading={loading}
|
||||
vehicles={data ? data.vehicles : null}
|
||||
vehicles={data ? data.search_vehicles : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -825,6 +825,180 @@ export default function ShopInfoGeneral({ form }) {
|
||||
}}
|
||||
</Form.List>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -228,7 +228,6 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
||||
where: { scheduled_completion: { _gte: $start, _lte: $end } }
|
||||
) {
|
||||
id
|
||||
|
||||
ro_number
|
||||
scheduled_completion
|
||||
labhrs: joblines_aggregate(
|
||||
@@ -250,15 +249,9 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
arrJobs: jobs(
|
||||
where: {
|
||||
scheduled_in: { _gte: $start, _lte: $end }
|
||||
removed: { _eq: false }
|
||||
}
|
||||
) {
|
||||
arrJobs: jobs(where: { scheduled_in: { _gte: $start, _lte: $end } }) {
|
||||
id
|
||||
scheduled_in
|
||||
|
||||
ro_number
|
||||
labhrs: joblines_aggregate(
|
||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||
|
||||
@@ -88,6 +88,7 @@ export const QUERY_BODYSHOP = gql`
|
||||
enforce_referral
|
||||
website
|
||||
jc_hourly_rates
|
||||
md_jobline_presets
|
||||
employees {
|
||||
id
|
||||
active
|
||||
@@ -173,6 +174,7 @@ export const UPDATE_SHOP = gql`
|
||||
enforce_referral
|
||||
website
|
||||
jc_hourly_rates
|
||||
md_jobline_presets
|
||||
employees {
|
||||
id
|
||||
first_name
|
||||
|
||||
@@ -159,6 +159,7 @@ export const UPDATE_JOB_LINE = gql`
|
||||
db_price
|
||||
act_price
|
||||
line_desc
|
||||
line_no
|
||||
oem_partno
|
||||
notes
|
||||
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`
|
||||
query SEARCH_VEHICLES_BY_ID_FOR_AUTOCOMPLETE($id: uuid!) {
|
||||
vehicles_by_pk(id: $id) {
|
||||
|
||||
@@ -57,7 +57,7 @@ function JobsCreateContainer({ bodyshop, setBreadcrumbs, setSelectedHeader }) {
|
||||
|
||||
useEffect(() => {
|
||||
document.title = t("titles.jobs-create");
|
||||
setSelectedHeader("availablejobs");
|
||||
setSelectedHeader("newjob");
|
||||
setBreadcrumbs([
|
||||
{ link: "/manage/available", label: t("titles.bc.availablejobs") },
|
||||
{
|
||||
|
||||
@@ -243,6 +243,7 @@
|
||||
"street2": "Street 2",
|
||||
"zip": "Zip/Postal Code"
|
||||
},
|
||||
"md_jobline_presets": "Jobline Presets",
|
||||
"md_payment_types": "Payment Types",
|
||||
"md_referral_sources": "Referral Sources",
|
||||
"messaginglabel": "Messaging Preset Label",
|
||||
@@ -668,6 +669,7 @@
|
||||
"addcomponent": "Add Component"
|
||||
},
|
||||
"errors": {
|
||||
"refreshrequired": "You must refresh the dashboard data to see this component.",
|
||||
"updatinglayout": "Error saving updated layout {{message}}"
|
||||
},
|
||||
"labels": {
|
||||
@@ -992,7 +994,8 @@
|
||||
"billref": "Latest Bill",
|
||||
"edit": "Edit Line",
|
||||
"new": "New Line",
|
||||
"nostatus": "No Status"
|
||||
"nostatus": "No Status",
|
||||
"presets": "Jobline Presets"
|
||||
},
|
||||
"successes": {
|
||||
"created": "Job line created successfully.",
|
||||
@@ -1440,6 +1443,7 @@
|
||||
"help": "Help",
|
||||
"home": "Home",
|
||||
"jobs": "Jobs",
|
||||
"newjob": "Create New Job",
|
||||
"owners": "Owners",
|
||||
"parts-queue": "Parts Queue",
|
||||
"phonebook": "Phonebook",
|
||||
|
||||
@@ -243,6 +243,7 @@
|
||||
"street2": "",
|
||||
"zip": ""
|
||||
},
|
||||
"md_jobline_presets": "",
|
||||
"md_payment_types": "",
|
||||
"md_referral_sources": "",
|
||||
"messaginglabel": "",
|
||||
@@ -668,6 +669,7 @@
|
||||
"addcomponent": ""
|
||||
},
|
||||
"errors": {
|
||||
"refreshrequired": "",
|
||||
"updatinglayout": ""
|
||||
},
|
||||
"labels": {
|
||||
@@ -992,7 +994,8 @@
|
||||
"billref": "",
|
||||
"edit": "Línea de edición",
|
||||
"new": "Nueva línea",
|
||||
"nostatus": ""
|
||||
"nostatus": "",
|
||||
"presets": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": "",
|
||||
@@ -1440,6 +1443,7 @@
|
||||
"help": "",
|
||||
"home": "Casa",
|
||||
"jobs": "Trabajos",
|
||||
"newjob": "",
|
||||
"owners": "propietarios",
|
||||
"parts-queue": "",
|
||||
"phonebook": "",
|
||||
|
||||
@@ -243,6 +243,7 @@
|
||||
"street2": "",
|
||||
"zip": ""
|
||||
},
|
||||
"md_jobline_presets": "",
|
||||
"md_payment_types": "",
|
||||
"md_referral_sources": "",
|
||||
"messaginglabel": "",
|
||||
@@ -668,6 +669,7 @@
|
||||
"addcomponent": ""
|
||||
},
|
||||
"errors": {
|
||||
"refreshrequired": "",
|
||||
"updatinglayout": ""
|
||||
},
|
||||
"labels": {
|
||||
@@ -992,7 +994,8 @@
|
||||
"billref": "",
|
||||
"edit": "Ligne d'édition",
|
||||
"new": "Nouvelle ligne",
|
||||
"nostatus": ""
|
||||
"nostatus": "",
|
||||
"presets": ""
|
||||
},
|
||||
"successes": {
|
||||
"created": "",
|
||||
@@ -1440,6 +1443,7 @@
|
||||
"help": "",
|
||||
"home": "Accueil",
|
||||
"jobs": "Emplois",
|
||||
"newjob": "",
|
||||
"owners": "Propriétaires",
|
||||
"parts-queue": "",
|
||||
"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_hour_split
|
||||
- md_ins_cos
|
||||
- md_jobline_presets
|
||||
- md_labor_rates
|
||||
- md_messaging_presets
|
||||
- md_notes_presets
|
||||
@@ -849,6 +850,7 @@ tables:
|
||||
- md_classes
|
||||
- md_hour_split
|
||||
- md_ins_cos
|
||||
- md_jobline_presets
|
||||
- md_labor_rates
|
||||
- md_messaging_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
|
||||
.create(autoHouseObject, {
|
||||
version: "1.0",
|
||||
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
|
||||
res.type("application/xml");
|
||||
@@ -48,6 +52,8 @@ exports.default = async (req, res) => {
|
||||
const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
//Level 2
|
||||
|
||||
const repairCosts = CreateCosts(job);
|
||||
|
||||
try {
|
||||
const ret = {
|
||||
RepairOrderInformation: {
|
||||
@@ -63,8 +69,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
ShopState: job.bodyshop.state,
|
||||
ShopZip: job.bodyshop.zip_post,
|
||||
ShopPhone: job.bodyshop.phone,
|
||||
EstimatorID: `${job.est_ct_fn} ${job.est_ct_ln}`,
|
||||
EstimatorName: `${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 || ""}`,
|
||||
},
|
||||
CustomerInformation: {
|
||||
FirstName: job.ownr_fn,
|
||||
@@ -97,7 +103,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
VehiclePaintCode: null,
|
||||
VehicleTrimCode: null,
|
||||
VehicleBodyStyle: null,
|
||||
DriveableFlag: job.tlos_ind ? "Y" : "N",
|
||||
DriveableFlag: job.driveable ? "Y" : "N",
|
||||
},
|
||||
|
||||
InsuranceInformation: {
|
||||
@@ -251,25 +257,39 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
},
|
||||
RevisedTotals: {
|
||||
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,
|
||||
MechanicalHours: job.job_totals.rates.lam.hours,
|
||||
StructuralHours: job.job_totals.rates.las.hours,
|
||||
PartsTotal: Dinero(job.job_totals.parts.parts.total).toFormat(
|
||||
AHDineroFormat
|
||||
),
|
||||
PartsTotalCost: 0,
|
||||
PartsTotalCost: repairCosts.PartsTotalCost.toFormat(AHDineroFormat),
|
||||
PartsOEM: Dinero(
|
||||
job.job_totals.parts.parts.list.PAN &&
|
||||
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(
|
||||
job.job_totals.parts.parts.list.PAA &&
|
||||
job.job_totals.parts.parts.list.PAA.total
|
||||
).toFormat(AHDineroFormat),
|
||||
PartsAMCost: 0,
|
||||
PartsAMCost: repairCosts.PartsAMCost.toFormat(AHDineroFormat),
|
||||
PartsReconditioned: null,
|
||||
PartsReconditionedCost: null,
|
||||
PartsReconditionedCost:
|
||||
repairCosts.PartsReconditionedCost.toFormat(AHDineroFormat),
|
||||
PartsRecycled: Dinero(
|
||||
job.job_totals.parts.parts.list.PAR &&
|
||||
job.job_totals.parts.parts.list.PAR.total
|
||||
@@ -389,10 +409,108 @@ const CreateRepairOrderTag = (job, errorCallback) => {
|
||||
};
|
||||
return ret;
|
||||
} catch (error) {
|
||||
console.log("Error calculating 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) => {
|
||||
//EST, SCH, ARR, IPR, RDY, DEL, CLO, CAN, UNDEFINED.
|
||||
const {
|
||||
@@ -493,3 +611,6 @@ const generateNullDetailLine = () => {
|
||||
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_mash
|
||||
job_totals
|
||||
driveable
|
||||
bodyshop {
|
||||
id
|
||||
shopname
|
||||
@@ -350,6 +351,8 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
|
||||
md_ro_statuses
|
||||
md_order_statuses
|
||||
autohouseid
|
||||
md_responsibility_centers
|
||||
jc_hourly_rates
|
||||
}
|
||||
joblines (where:{removed: {_eq:false}}){
|
||||
id
|
||||
@@ -366,7 +369,10 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
|
||||
part_qty
|
||||
part_type
|
||||
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_price
|
||||
quantity
|
||||
@@ -377,7 +383,27 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz) {
|
||||
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
|
||||
employee_prep_rel {
|
||||
first_name
|
||||
|
||||
@@ -353,11 +353,12 @@ function CalculateTaxesTotals(job, otherTotals) {
|
||||
|
||||
//Audatex sends additional glass part types. IO-774
|
||||
const BackupGlassTax =
|
||||
job.parts_tax_rates.PAGD ||
|
||||
job.parts_tax_rates.PAGF ||
|
||||
job.parts_tax_rates.PAGP ||
|
||||
job.parts_tax_rates.PAGQ ||
|
||||
job.parts_tax_rates.PAGR;
|
||||
job.parts_tax_rates &&
|
||||
(job.parts_tax_rates.PAGD ||
|
||||
job.parts_tax_rates.PAGF ||
|
||||
job.parts_tax_rates.PAGP ||
|
||||
job.parts_tax_rates.PAGQ ||
|
||||
job.parts_tax_rates.PAGR);
|
||||
|
||||
job.joblines
|
||||
.filter((jl) => !jl.removed)
|
||||
|
||||
Reference in New Issue
Block a user