Merged in release/2022-01-20 (pull request #657)

Release/2022 01 20
This commit is contained in:
Patrick Fic
2023-01-20 21:49:59 +00:00
22 changed files with 373 additions and 43190 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project be_version="2.7.1" version="1.2"> <babeledit_project version="1.2" be_version="2.7.1">
<!-- <!--
BabelEdit project file BabelEdit project file
@@ -18340,6 +18340,27 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>ah_detail_line</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>db_price</name> <name>db_price</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -19667,6 +19688,27 @@
<folder_node> <folder_node>
<name>validations</name> <name>validations</name>
<children> <children>
<concept_node>
<name>ahdetailonlyonuserdefinedtypes</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>hrsrequirediflbrtyp</name> <name>hrsrequirediflbrtyp</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -28933,6 +28975,27 @@
</concept_node> </concept_node>
</children> </children>
</folder_node> </folder_node>
<concept_node>
<name>profileadjustments</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>prt_dsmk_total</name> <name>prt_dsmk_total</name>
<definition_loaded>false</definition_loaded> <definition_loaded>false</definition_loaded>
@@ -46564,6 +46627,48 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>changepassword</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>profileinfo</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>

View File

@@ -8,6 +8,8 @@ export default function DataLabel({
vertical, vertical,
visible = true, visible = true,
valueStyle = {}, valueStyle = {},
valueClassName,
onValueClick,
...props ...props
}) { }) {
if (!visible || (hideIfNull && !!!children)) return null; if (!visible || (hideIfNull && !!!children)) return null;
@@ -28,7 +30,10 @@ export default function DataLabel({
marginLeft: ".3rem", marginLeft: ".3rem",
fontWeight: "bolder", fontWeight: "bolder",
wordWrap: "break-word", wordWrap: "break-word",
cursor: onValueClick !== undefined ? "pointer" : "",
}} }}
className={valueClassName}
onClick={onValueClick}
> >
{typeof children === "string" ? ( {typeof children === "string" ? (
<Typography.Text style={valueStyle}>{children}</Typography.Text> <Typography.Text style={valueStyle}>{children}</Typography.Text>

View File

@@ -40,6 +40,11 @@ export function JobLinesUpsertModalComponent({
{}, {},
bodyshop.imexshopid bodyshop.imexshopid
); );
const { Autohouse_Detail_line } = useTreatments(
["Autohouse_Detail_line"],
{},
bodyshop.imexshopid
);
return ( return (
<Modal <Modal
@@ -155,6 +160,40 @@ export function JobLinesUpsertModalComponent({
> >
<InputNumber precision={1} /> <InputNumber precision={1} />
</Form.Item> </Form.Item>
{Autohouse_Detail_line.treatment === "on" && (
<Form.Item
label={t("joblines.fields.ah_detail_line")}
name="ah_detail_line"
valuePropName="checked"
dependencies={["mod_lbr_ty"]}
rules={[
({ getFieldValue }) => ({
validator(rule, value) {
console.log(
value === true,
["LA1", "LA2", "LA3", "LA4", "LAU"].includes(
getFieldValue("mod_lbr_ty")
)
);
if (value === false) return Promise.resolve();
if (
value === true &&
["LA1", "LA2", "LA3", "LA4", "LAU"].includes(
getFieldValue("mod_lbr_ty")
)
) {
return Promise.resolve();
}
return Promise.reject(
t("joblines.validations.ahdetailonlyonuserdefinedtypes")
);
},
}),
]}
>
<Switch />
</Form.Item>
)}
</LayoutFormRow> </LayoutFormRow>
<LayoutFormRow> <LayoutFormRow>
<Form.Item label={t("joblines.fields.part_type")} name="part_type"> <Form.Item label={t("joblines.fields.part_type")} name="part_type">
@@ -218,7 +257,6 @@ export function JobLinesUpsertModalComponent({
rules={[ rules={[
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator(rule, value) { validator(rule, value) {
console.log(value);
if (!value || getFieldValue("part_type") !== "PAE") { if (!value || getFieldValue("part_type") !== "PAE") {
return Promise.resolve(); return Promise.resolve();
} }
@@ -229,7 +267,6 @@ export function JobLinesUpsertModalComponent({
}), }),
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator(rule, value) { validator(rule, value) {
console.log(value, !!value);
if ( if (
!!getFieldValue("part_type") === (!!value || value === 0) !!getFieldValue("part_type") === (!!value || value === 0)
) { ) {

View File

@@ -5,7 +5,7 @@ import {
BranchesOutlined, BranchesOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { Card, Col, Row, Space, Tag, Tooltip } from "antd"; import { Card, Col, Row, Space, Tag, Tooltip } from "antd";
import React from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@@ -56,7 +56,7 @@ const colSpan = {
export function JobsDetailHeader({ job, bodyshop, disabled }) { export function JobsDetailHeader({ job, bodyshop, disabled }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [notesClamped, setNotesClamped] = useState(true);
const vehicleTitle = `${job.v_model_yr || ""} ${job.v_color || ""} const vehicleTitle = `${job.v_model_yr || ""} ${job.v_color || ""}
${job.v_make_desc || ""} ${job.v_make_desc || ""}
${job.v_model_desc || ""}`.trim(); ${job.v_model_desc || ""}`.trim();
@@ -229,6 +229,8 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
<DataLabel <DataLabel
label={t("vehicles.fields.notes")} label={t("vehicles.fields.notes")}
valueStyle={{ whiteSpace: "pre-wrap" }} valueStyle={{ whiteSpace: "pre-wrap" }}
valueClassName={notesClamped ? "clamp" : ""}
onValueClick={() => setNotesClamped(!notesClamped)}
> >
{job.vehicle.notes} {job.vehicle.notes}
</DataLabel> </DataLabel>

View File

@@ -6,3 +6,12 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
.clamp {
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
overflow-wrap: break-word;
}

View File

@@ -182,7 +182,7 @@ export function JobsExportAllButton({
return ( return (
<Button onClick={handleQbxml} loading={loading} disabled={disabled}> <Button onClick={handleQbxml} loading={loading} disabled={disabled}>
{t("jobs.actions.export")} {t("jobs.actions.exportselected")}
</Button> </Button>
); );
} }

View File

@@ -1,4 +1,4 @@
import { Button, Form, Input, notification } from "antd"; import { Button, Card, Col, Form, Input, notification } from "antd";
import { LockOutlined } from "@ant-design/icons"; import { LockOutlined } from "@ant-design/icons";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -48,81 +48,99 @@ export default connect(
}; };
return ( return (
<div> <>
<Form <Col span={24}>
onFinish={handleFinish} <Form
autoComplete={"no"} onFinish={handleFinish}
initialValues={currentUser} autoComplete={"no"}
layout="vertical" initialValues={currentUser}
> layout="vertical"
<LayoutFormRow> >
<Form.Item <Card
label={t("user.fields.displayname")} title={t("user.labels.profileinfo")}
rules={[ extra={
{ <Button type="primary" key="submit" htmlType="submit">
required: true, {t("user.actions.updateprofile")}
//message: t("general.validation.required"), </Button>
}, }
]}
name="displayName"
> >
<Input /> <LayoutFormRow noDivider>
</Form.Item> <Form.Item
<Form.Item label={t("user.fields.photourl")} name="photoURL"> label={t("user.fields.displayname")}
<Input /> rules={[
</Form.Item> {
</LayoutFormRow> required: true,
//message: t("general.validation.required"),
<Button type="primary" key="submit" htmlType="submit"> },
{t("user.actions.updateprofile")} ]}
</Button> name="displayName"
</Form> >
<Form <Input />
onFinish={handleChangePassword} </Form.Item>
autoComplete={"no"} <Form.Item label={t("user.fields.photourl")} name="photoURL">
initialValues={currentUser} <Input />
layout="vertical" </Form.Item>
> </LayoutFormRow>
<LayoutFormRow> </Card>
<Form.Item label={t("general.labels.newpassword")} name="password"> </Form>
<Input </Col>
prefix={<LockOutlined />} <Col span={24}>
type="password" <Form
placeholder={t("general.labels.password")} onFinish={handleChangePassword}
/> autoComplete={"no"}
</Form.Item> initialValues={currentUser}
<Form.Item layout="vertical"
label={t("general.labels.confirmpassword")} >
name="password-confirm" <Card
dependencies={["password"]} title={t("user.labels.changepassword")}
rules={[ extra={
{ <Button type="primary" key="submit" htmlType="submit">
required: true, {t("user.actions.changepassword")}
//message: t("general.validation.required"), </Button>
}, }
({ getFieldValue }) => ({
validator(rule, value) {
if (!value || getFieldValue("password") === value) {
return Promise.resolve();
}
return Promise.reject(
t("general.labels.passwordsdonotmatch")
);
},
}),
]}
> >
<Input <LayoutFormRow>
prefix={<LockOutlined />} <Form.Item
type="password" label={t("general.labels.newpassword")}
placeholder={t("general.labels.password")} name="password"
/> >
</Form.Item> <Input
</LayoutFormRow> prefix={<LockOutlined />}
<Button type="primary" key="submit" htmlType="submit"> type="password"
{t("user.actions.changepassword")} placeholder={t("general.labels.password")}
</Button> />
</Form> </Form.Item>
</div> <Form.Item
label={t("general.labels.confirmpassword")}
name="password-confirm"
dependencies={["password"]}
rules={[
{
required: true,
//message: t("general.validation.required"),
},
({ getFieldValue }) => ({
validator(rule, value) {
if (!value || getFieldValue("password") === value) {
return Promise.resolve();
}
return Promise.reject(
t("general.labels.passwordsdonotmatch")
);
},
}),
]}
>
<Input
prefix={<LockOutlined />}
type="password"
placeholder={t("general.labels.password")}
/>
</Form.Item>
</LayoutFormRow>
</Card>
</Form>
</Col>
</>
); );
}); });

View File

@@ -1,13 +1,13 @@
import React from "react"; import { Button, Card, Col, Input, Table, Typography } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Table, Button, Typography } from "antd";
export default function ProfileShopsComponent({ export default function ProfileShopsComponent({
loading, loading,
data, data,
updateActiveShop, updateActiveShop,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [search, setSearch] = useState("");
const columns = [ const columns = [
{ {
title: t("associations.fields.shopname"), title: t("associations.fields.shopname"),
@@ -39,18 +39,39 @@ export default function ProfileShopsComponent({
), ),
}, },
]; ];
console.log("🚀 ~ file: profile-shops.component.jsx:45 ~ data", data);
const filteredData =
search === ""
? data
: data.filter((d) =>
d.bodyshop.shopname.toLowerCase().includes(search.toLowerCase())
);
return ( return (
<Table <Col span={24}>
title={() => ( <Card
<Typography.Title level={4}> title={
{t("profile.labels.activeshop")} <Typography.Title level={4}>
</Typography.Title> {t("profile.labels.activeshop")}
)} </Typography.Title>
loading={loading} }
columns={columns} extra={
rowKey="id" <Input.Search
dataSource={data} value={search}
/> onChange={(e) => setSearch(e.target.value)}
allowClear
placeholder={t("general.labels.search")}
/>
}
>
<Table
pagination={false}
loading={loading}
columns={columns}
rowKey="id"
dataSource={filteredData}
/>
</Card>
</Col>
); );
} }

View File

@@ -1344,7 +1344,14 @@ export default function ShopInfoGeneral({ form }) {
> >
<InputNumber precision={0} min={0} max={100} /> <InputNumber precision={0} min={0} max={100} />
</Form.Item> </Form.Item>
<Form.Item
label={t("joblines.fields.ah_detail_line")}
key={`${index}ah_detail_line`}
name={[field.name, "ah_detail_line"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Space wrap> <Space wrap>
<DeleteFilled <DeleteFilled
onClick={() => { onClick={() => {

View File

@@ -720,6 +720,7 @@ export const GET_JOB_BY_PK = gql`
prt_dsmk_m prt_dsmk_m
ioucreated ioucreated
convertedtolbr convertedtolbr
ah_detail_line
billlines(limit: 1, order_by: { bill: { date: desc } }) { billlines(limit: 1, order_by: { bill: { date: desc } }) {
id id
quantity quantity

View File

@@ -1,3 +1,4 @@
import { Row } from "antd";
import React from "react"; import React from "react";
import ProfileMyComponent from "../../components/profile-my/profile-my.component"; import ProfileMyComponent from "../../components/profile-my/profile-my.component";
import ProfileShopsContainer from "../../components/profile-shops/profile-shops.container"; import ProfileShopsContainer from "../../components/profile-shops/profile-shops.container";
@@ -5,8 +6,10 @@ import ProfileShopsContainer from "../../components/profile-shops/profile-shops.
export default function ProfilePage() { export default function ProfilePage() {
return ( return (
<div> <div>
<ProfileMyComponent /> <Row gutter={[16, 16]}>
<ProfileShopsContainer /> <ProfileMyComponent />
<ProfileShopsContainer />
</Row>
</div> </div>
); );
} }

View File

@@ -1142,6 +1142,7 @@
}, },
"fields": { "fields": {
"act_price": "Retail Price", "act_price": "Retail Price",
"ah_detail_line": "Mark as Detail Labor Line (Autohouse Only)",
"db_price": "List Price", "db_price": "List Price",
"lbr_types": { "lbr_types": {
"LA1": "LA1", "LA1": "LA1",
@@ -1214,6 +1215,7 @@
"updated": "Job line updated successfully." "updated": "Job line updated successfully."
}, },
"validations": { "validations": {
"ahdetailonlyonuserdefinedtypes": "Detail line indicator can only be set for LA1, LA2, LA3, LA4, and LAU labor types.",
"hrsrequirediflbrtyp": "Labor hours are required if a labor type is selected. Clear the labor type if there are no labor hours.", "hrsrequirediflbrtyp": "Labor hours are required if a labor type is selected. Clear the labor type if there are no labor hours.",
"requiredifparttype": "Required if a part type has been specified.", "requiredifparttype": "Required if a part type has been specified.",
"zeropriceexistingpart": "This line cannot have any price since it uses an existing part." "zeropriceexistingpart": "This line cannot have any price since it uses an existing part."
@@ -1694,6 +1696,7 @@
"partstotal": "This is the total of all parts and sublet amounts on the vehicle (some of these may require an in-house invoice).<br/>\nItems such as shop and paint materials, labor online lines, etc. are not included in this total.", "partstotal": "This is the total of all parts and sublet amounts on the vehicle (some of these may require an in-house invoice).<br/>\nItems such as shop and paint materials, labor online lines, etc. are not included in this total.",
"totalreturns": "The total <b>retail</b> amount of returns created for this job." "totalreturns": "The total <b>retail</b> amount of returns created for this job."
}, },
"profileadjustments": "",
"prt_dsmk_total": "Line Item Adjustment", "prt_dsmk_total": "Line Item Adjustment",
"rates": "Rates", "rates": "Rates",
"rates_subtotal": "All Rates Subtotal", "rates_subtotal": "All Rates Subtotal",
@@ -2744,7 +2747,9 @@
"photourl": "Avatar URL" "photourl": "Avatar URL"
}, },
"labels": { "labels": {
"actions": "Actions" "actions": "Actions",
"changepassword": "Change Password",
"profileinfo": "Profile Info"
}, },
"successess": { "successess": {
"passwordchanged": "Password changed successfully. " "passwordchanged": "Password changed successfully. "

View File

@@ -1142,6 +1142,7 @@
}, },
"fields": { "fields": {
"act_price": "Precio actual", "act_price": "Precio actual",
"ah_detail_line": "",
"db_price": "Precio de base de datos", "db_price": "Precio de base de datos",
"lbr_types": { "lbr_types": {
"LA1": "", "LA1": "",
@@ -1214,6 +1215,7 @@
"updated": "" "updated": ""
}, },
"validations": { "validations": {
"ahdetailonlyonuserdefinedtypes": "",
"hrsrequirediflbrtyp": "", "hrsrequirediflbrtyp": "",
"requiredifparttype": "", "requiredifparttype": "",
"zeropriceexistingpart": "" "zeropriceexistingpart": ""
@@ -1694,6 +1696,7 @@
"partstotal": "", "partstotal": "",
"totalreturns": "" "totalreturns": ""
}, },
"profileadjustments": "",
"prt_dsmk_total": "", "prt_dsmk_total": "",
"rates": "Tarifas", "rates": "Tarifas",
"rates_subtotal": "", "rates_subtotal": "",
@@ -2744,7 +2747,9 @@
"photourl": "URL de avatar" "photourl": "URL de avatar"
}, },
"labels": { "labels": {
"actions": "" "actions": "",
"changepassword": "",
"profileinfo": ""
}, },
"successess": { "successess": {
"passwordchanged": "" "passwordchanged": ""

View File

@@ -1142,6 +1142,7 @@
}, },
"fields": { "fields": {
"act_price": "Prix actuel", "act_price": "Prix actuel",
"ah_detail_line": "",
"db_price": "Prix de la base de données", "db_price": "Prix de la base de données",
"lbr_types": { "lbr_types": {
"LA1": "", "LA1": "",
@@ -1214,6 +1215,7 @@
"updated": "" "updated": ""
}, },
"validations": { "validations": {
"ahdetailonlyonuserdefinedtypes": "",
"hrsrequirediflbrtyp": "", "hrsrequirediflbrtyp": "",
"requiredifparttype": "", "requiredifparttype": "",
"zeropriceexistingpart": "" "zeropriceexistingpart": ""
@@ -1694,6 +1696,7 @@
"partstotal": "", "partstotal": "",
"totalreturns": "" "totalreturns": ""
}, },
"profileadjustments": "",
"prt_dsmk_total": "", "prt_dsmk_total": "",
"rates": "Les taux", "rates": "Les taux",
"rates_subtotal": "", "rates_subtotal": "",
@@ -2744,7 +2747,9 @@
"photourl": "URL de l'avatar" "photourl": "URL de l'avatar"
}, },
"labels": { "labels": {
"actions": "" "actions": "",
"changepassword": "",
"profileinfo": ""
}, },
"successess": { "successess": {
"passwordchanged": "" "passwordchanged": ""

View File

@@ -2305,18 +2305,6 @@
shallowequal "^1.1.0" shallowequal "^1.1.0"
unfetch "^4.2.0" unfetch "^4.2.0"
"@stripe/react-stripe-js@^1.9.0":
version "1.9.0"
resolved "https://registry.yarnpkg.com/@stripe/react-stripe-js/-/react-stripe-js-1.9.0.tgz#74809a274d7db110c3daf6f68ca4d62c6e6559c7"
integrity sha512-Fn49X+Gu5fOTxhPOita1cPMi0jw+0v4xfJ3FCXbbvmfuuDl3M7ZvpRkoijBjql11NXsaXO3TMm3rkN3mEolJzw==
dependencies:
prop-types "^15.7.2"
"@stripe/stripe-js@^1.32.0":
version "1.32.0"
resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.32.0.tgz#4ecdd298db61ad9b240622eafed58da974bd210e"
integrity sha512-7EvBnbBfS1aynfLRmBFcuumHNGjKxnNkO47rorFBktqDYHwo7Yw6pfDW2iqq0R8r7i7XiJEdWPvvEgQAiDrx3A==
"@surma/rollup-plugin-off-main-thread@^1.1.1": "@surma/rollup-plugin-off-main-thread@^1.1.1":
version "1.4.2" version "1.4.2"
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-1.4.2.tgz#e6786b6af5799f82f7ab3a82e53f6182d2b91a58" resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-1.4.2.tgz#e6786b6af5799f82f7ab3a82e53f6182d2b91a58"

View File

@@ -2482,6 +2482,7 @@
_eq: true _eq: true
columns: columns:
- act_price - act_price
- ah_detail_line
- alt_co_id - alt_co_id
- alt_overrd - alt_overrd
- alt_part_i - alt_part_i
@@ -2547,6 +2548,7 @@
permission: permission:
columns: columns:
- act_price - act_price
- ah_detail_line
- alt_co_id - alt_co_id
- alt_overrd - alt_overrd
- alt_part_i - alt_part_i
@@ -2623,6 +2625,7 @@
permission: permission:
columns: columns:
- act_price - act_price
- ah_detail_line
- alt_co_id - alt_co_id
- alt_overrd - alt_overrd
- alt_part_i - alt_part_i

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."joblines" add column "ah_detail_line" boolean
-- not null default 'false';

View File

@@ -0,0 +1,2 @@
alter table "public"."joblines" add column "ah_detail_line" boolean
not null default 'false';

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"scripts": { "scripts": {
"setup": "yarn && cd client && yarn", "setup": "rm -rf node_modules && yarn && cd client && rm -rf node_modules && yarn",
"admin": "cd admin && yarn start", "admin": "cd admin && yarn start",
"client": "cd client && yarn start", "client": "cd client && yarn start",
"server": "nodemon server.js", "server": "nodemon server.js",

View File

@@ -214,6 +214,30 @@ const CreateRepairOrderTag = (job, errorCallback) => {
const repairCosts = CreateCosts(job); const repairCosts = CreateCosts(job);
if (job.ro_number === "QBD209") {
console.log("Stop here");
}
//Calculate detail only lines.
const detailAdjustments = job.joblines
.filter((jl) => jl.ah_detail_line && jl.mod_lbr_ty)
.reduce(
(acc, val) => {
return {
hours: acc.hours + val.mod_lb_hrs,
amount: acc.amount.add(
Dinero({
amount: Math.round(
(job.job_totals.rates[val.mod_lbr_ty.toLowerCase()].rate || 0) *
val.mod_lb_hrs *
100
),
})
),
};
},
{ hours: 0, amount: Dinero() }
);
try { try {
const ret = { const ret = {
RepairOrderInformation: { RepairOrderInformation: {
@@ -405,7 +429,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
ElectricalRate: job.rate_lae || 0, ElectricalRate: job.rate_lae || 0,
FrameRate: job.rate_laf || 0, FrameRate: job.rate_laf || 0,
GlassRate: job.rate_lag || 0, GlassRate: job.rate_lag || 0,
DetailRate: job.rate_lad || 0, DetailRate: 0, // job.rate_lad || 0,
LaborMiscRate: 0, LaborMiscRate: 0,
PMRate: job.rate_mapa || 0, PMRate: job.rate_mapa || 0,
BMRate: job.rate_mash || 0, BMRate: job.rate_mash || 0,
@@ -500,13 +524,14 @@ const CreateRepairOrderTag = (job, errorCallback) => {
ElectricalHours: job.job_totals.rates.lae.hours.toFixed(2), ElectricalHours: job.job_totals.rates.lae.hours.toFixed(2),
FrameHours: job.job_totals.rates.laf.hours.toFixed(2), FrameHours: job.job_totals.rates.laf.hours.toFixed(2),
GlassHours: job.job_totals.rates.lag.hours.toFixed(2), GlassHours: job.job_totals.rates.lag.hours.toFixed(2),
DetailHours: job.job_totals.rates.lad.hours.toFixed(2), DetailHours: detailAdjustments.hours, //job.job_totals.rates.lad.hours.toFixed(2),
LaborMiscHours: ( LaborMiscHours: (
job.job_totals.rates.la1.hours + job.job_totals.rates.la1.hours +
job.job_totals.rates.la2.hours + job.job_totals.rates.la2.hours +
job.job_totals.rates.la3.hours + job.job_totals.rates.la3.hours +
job.job_totals.rates.la4.hours + job.job_totals.rates.la4.hours +
job.job_totals.rates.lau.hours job.job_totals.rates.lau.hours -
detailAdjustments.hours
).toFixed(2), ).toFixed(2),
PartsTotal: Dinero(job.job_totals.parts.parts.total).toFormat( PartsTotal: Dinero(job.job_totals.parts.parts.total).toFormat(
@@ -585,16 +610,18 @@ const CreateRepairOrderTag = (job, errorCallback) => {
), ),
GlassLaborTotalCost: GlassLaborTotalCost:
repairCosts.GlassLaborTotalCost.toFormat(AHDineroFormat), repairCosts.GlassLaborTotalCost.toFormat(AHDineroFormat),
DetailLaborTotal: Dinero(job.job_totals.rates.lad.total).toFormat( DetailLaborTotal: detailAdjustments.amount.toFormat(AHDineroFormat),
AHDineroFormat // Dinero(job.job_totals.rates.lad.total).toFormat(
), // AHDineroFormat
DetailLaborTotalCost: // ),
repairCosts.DetailLaborTotalCost.toFormat(AHDineroFormat), DetailLaborTotalCost: Dinero().toFormat(AHDineroFormat),
// repairCosts.DetailLaborTotalCost.toFormat(AHDineroFormat),
LaborMiscTotal: Dinero(job.job_totals.rates.la1.total) LaborMiscTotal: Dinero(job.job_totals.rates.la1.total)
.add(Dinero(job.job_totals.rates.la2.total)) .add(Dinero(job.job_totals.rates.la2.total))
.add(Dinero(job.job_totals.rates.la3.total)) .add(Dinero(job.job_totals.rates.la3.total))
.add(Dinero(job.job_totals.rates.la4.total)) .add(Dinero(job.job_totals.rates.la4.total))
.add(Dinero(job.job_totals.rates.lau.total)) .add(Dinero(job.job_totals.rates.lau.total))
.subtract(detailAdjustments.amount)
.toFormat(AHDineroFormat), .toFormat(AHDineroFormat),
LaborMiscTotalCost: 0, LaborMiscTotalCost: 0,
MiscellaneousChargeTotal: 0, MiscellaneousChargeTotal: 0,
@@ -619,7 +646,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
AHDineroFormat AHDineroFormat
), ),
StorageTotalCost: repairCosts.StorageTotalCost.toFormat(AHDineroFormat), StorageTotalCost: repairCosts.StorageTotalCost.toFormat(AHDineroFormat),
DetailTotal: 0, DetailTotal: detailAdjustments.amount.toFormat(AHDineroFormat),
DetailTotalCost: 0, DetailTotalCost: 0,
SalesTaxTotal: Dinero(job.job_totals.totals.local_tax) SalesTaxTotal: Dinero(job.job_totals.totals.local_tax)
.add(Dinero(job.job_totals.totals.state_tax)) .add(Dinero(job.job_totals.totals.state_tax))
@@ -866,8 +893,8 @@ const CreateCosts = (job) => {
ticketTotalsByCostCenter[defaultCosts.LAE] || Dinero(), ticketTotalsByCostCenter[defaultCosts.LAE] || Dinero(),
FrameLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAF] || Dinero(), FrameLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAF] || Dinero(),
GlassLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAG] || Dinero(), GlassLaborTotalCost: ticketTotalsByCostCenter[defaultCosts.LAG] || Dinero(),
DetailLaborTotalCost: DetailLaborTotalCost: Dinero(),
ticketTotalsByCostCenter[defaultCosts.LAD] || Dinero(), // ticketTotalsByCostCenter[defaultCosts.LAD] || Dinero(),
PMTotalCost: billTotalsByCostCenters[defaultCosts.MAPA] || Dinero(), PMTotalCost: billTotalsByCostCenters[defaultCosts.MAPA] || Dinero(),
BMTotalCost: billTotalsByCostCenters[defaultCosts.MASH] || Dinero(), BMTotalCost: billTotalsByCostCenters[defaultCosts.MASH] || Dinero(),
MiscTotalCost: billTotalsByCostCenters[defaultCosts.PAO] || Dinero(), MiscTotalCost: billTotalsByCostCenters[defaultCosts.PAO] || Dinero(),

View File

@@ -715,6 +715,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
lbr_op lbr_op
profitcenter_part profitcenter_part
profitcenter_labor profitcenter_labor
ah_detail_line
parts_order_lines(order_by: {parts_order: {order_date: desc_nulls_last}} limit: 1){ parts_order_lines(order_by: {parts_order: {order_date: desc_nulls_last}} limit: 1){
parts_order{ parts_order{
id id