diff --git a/client/src/components/dms-post-form/dms-post-form.component.jsx b/client/src/components/dms-post-form/dms-post-form.component.jsx index 89738cc95..46bad37c3 100644 --- a/client/src/components/dms-post-form/dms-post-form.component.jsx +++ b/client/src/components/dms-post-form/dms-post-form.component.jsx @@ -14,7 +14,6 @@ import { Typography } from "antd"; import Dinero from "dinero.js"; -import dayjs from "../../utils/day"; import React from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; @@ -22,6 +21,7 @@ import { createStructuredSelector } from "reselect"; import { determineDmsType } from "../../pages/dms/dms.container"; import { selectBodyshop } from "../../redux/user/user.selectors"; import i18n from "../../translations/i18n"; +import dayjs from "../../utils/day"; import DmsCdkMakes from "../dms-cdk-makes/dms-cdk-makes.component"; import DmsCdkMakesRefetch from "../dms-cdk-makes/dms-cdk-makes.refetch.component"; import FormDatePicker from "../form-date-picker/form-date-picker.component"; @@ -89,7 +89,7 @@ export function DmsPostForm({ bodyshop, socket, job, logsRef }) { job.area_of_damage && job.area_of_damage.impact1 ? " " + t("jobs.labels.dms.damageto", { - area_of_damage: (job.area_of_damage && job.area_of_damage.impact1) || "UNKNOWN" + area_of_damage: (job.area_of_damage && job.area_of_damage.impact1.padStart(2, "0")) || "UNKNOWN" }) : "" }`.slice(0, 239), diff --git a/client/src/components/job-close-ro-guard/job-close-ro-guard.container.jsx b/client/src/components/job-close-ro-guard/job-close-ro-guard.container.jsx index c75c04d6c..6beffacb3 100644 --- a/client/src/components/job-close-ro-guard/job-close-ro-guard.container.jsx +++ b/client/src/components/job-close-ro-guard/job-close-ro-guard.container.jsx @@ -1,6 +1,6 @@ -import React, { useCallback, useState } from "react"; import { LockOutlined } from "@ant-design/icons"; import { Badge, Card, Col, Collapse, Form, Input, Row, Space, Tooltip } from "antd"; +import React, { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; @@ -12,8 +12,8 @@ import JobCloseRoGuardBills from "./job-close-ro-guard.bills"; import JobCloseRoGuardPpd from "./job-close-ro-guard.ppd"; import JobCloseRoGuardProfit from "./job-close-ro-guard.profit"; import "./job-close-ro-guard.styles.scss"; -import JobCloseRoGuardTtLifecycle from "./job-close-ro-guard.tt-lifecycle"; import InstanceRenderManager from "../../utils/instanceRenderMgr"; +import JobCloseRoGuardTtLifecycle from "./job-close-ro-guard.tt-lifecycle"; const mapStateToProps = createStructuredSelector({ //currentUser: selectCurrentUser diff --git a/client/src/components/job-detail-cards/job-detail-cards.damage.component.jsx b/client/src/components/job-detail-cards/job-detail-cards.damage.component.jsx index e899344d4..00e0a01b4 100644 --- a/client/src/components/job-detail-cards/job-detail-cards.damage.component.jsx +++ b/client/src/components/job-detail-cards/job-detail-cards.damage.component.jsx @@ -1,14 +1,21 @@ import React from "react"; import { useTranslation } from "react-i18next"; -import CardTemplate from "./job-detail-cards.template.component"; import Car from "../job-damage-visual/job-damage-visual.component"; +import CardTemplate from "./job-detail-cards.template.component"; export default function JobDetailCardsDamageComponent({ loading, data }) { const { t } = useTranslation(); const { area_of_damage } = data; return ( - {area_of_damage ? : t("jobs.errors.nodamage")} + {area_of_damage ? ( + + ) : ( + t("jobs.errors.nodamage") + )} ); } diff --git a/client/src/components/job-detail-cards/job-detail-cards.parts.component.jsx b/client/src/components/job-detail-cards/job-detail-cards.parts.component.jsx index 92e875f99..a567918f5 100644 --- a/client/src/components/job-detail-cards/job-detail-cards.parts.component.jsx +++ b/client/src/components/job-detail-cards/job-detail-cards.parts.component.jsx @@ -26,6 +26,16 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) { const { t } = useTranslation(); const { joblines_status } = data; + const filteredJobLines = data.joblines.filter( + (j) => + j.part_type !== null && + j.part_type !== "PAE" && + j.part_type !== "PAS" && + j.part_type !== "PASL" && + j.part_qty !== 0 && + j.act_price !== 0 + ); +//TODO: Correct jobline_statuses view by including the part_qty !== 0 and act_price !== 0 const columns = [ { title: t("joblines.fields.line_desc"), @@ -95,7 +105,7 @@ export function JobDetailCardsPartsComponent({ loading, data, jobRO }) {
- +
); diff --git a/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx b/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx index ac1976e34..df2b43cd0 100644 --- a/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx +++ b/client/src/components/jobs-detail-general/jobs-detail-general.component.jsx @@ -189,7 +189,10 @@ export function JobsDetailGeneral({ bodyshop, jobRO, job, form }) { {job.area_of_damage ? ( - + ) : ( t("jobs.errors.nodamage") )} diff --git a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx index e0437fef0..ca3c4722d 100644 --- a/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx +++ b/client/src/components/shop-info/shop-info.responsibilitycenters.component.jsx @@ -1,17 +1,17 @@ import { DeleteFilled } from "@ant-design/icons"; -import { Button, Form, Input, InputNumber, Select, Space, Switch, Typography } from "antd"; +import { useSplitTreatments } from "@splitsoftware/splitio-react"; +import { Button, Form, Input, InputNumber, Select, Space, Switch } from "antd"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; -import styled from "styled-components"; -import LayoutFormRow from "../layout-form-row/layout-form-row.component"; -import DataLabel from "../data-label/data-label.component"; -import { selectBodyshop } from "../../redux/user/user.selectors"; import { connect } from "react-redux"; import { createStructuredSelector } from "reselect"; -import { useSplitTreatments } from "@splitsoftware/splitio-react"; -import ShopInfoResponsibilitycentersTaxesComponent from "./shop-info.responsibilitycenters.taxes.component"; +import styled from "styled-components"; +import { selectBodyshop } from "../../redux/user/user.selectors"; import InstanceRenderManager from "../../utils/instanceRenderMgr"; +import DataLabel from "../data-label/data-label.component"; import FormListMoveArrows from "../form-list-move-arrows/form-list-move-arrows.component"; +import LayoutFormRow from "../layout-form-row/layout-form-row.component"; +import ShopInfoResponsibilitycentersTaxesComponent from "./shop-info.responsibilitycenters.taxes.component"; const SelectorDiv = styled.div` .ant-form-item .ant-select { @@ -214,7 +214,6 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { - { @@ -274,7 +273,6 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { > - { remove(field.name); @@ -386,7 +384,6 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { > */} - {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && ( - {(fields, { add, remove, move }) => { @@ -579,1631 +575,1632 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) { }} - {(bodyshop.cdk_dealerid || bodyshop.pbs_serialnumber) && ( <> - - {(fields, { add, remove, move }) => { - return ( -
- {fields.map((field, index) => ( - -
- - - - - - - - { - remove(field.name); - }} - /> - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); - } - return Promise.reject(t("bodyshop.validation.centermustexist")); + + + {(fields, { add, remove, move }) => { + return ( +
+ {fields.map((field, index) => ( + +
+ 0 ? false : true}> + - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + ]} + > + + + + + + { + remove(field.name); + }} + /> + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAA`} - name={[field.name, "costs", "LAA"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-ats`} + name={[field.name, "costs", "ATS"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAB`} - name={[field.name, "costs", "LAB"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAA`} + name={[field.name, "costs", "LAA"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAD`} - name={[field.name, "costs", "LAD"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAB`} + name={[field.name, "costs", "LAB"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAE`} - name={[field.name, "costs", "LAE"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAD`} + name={[field.name, "costs", "LAD"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAF`} - name={[field.name, "costs", "LAF"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAE`} + name={[field.name, "costs", "LAE"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAG`} - name={[field.name, "costs", "LAG"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAF`} + name={[field.name, "costs", "LAF"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAM`} - name={[field.name, "costs", "LAM"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAG`} + name={[field.name, "costs", "LAG"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAR`} - name={[field.name, "costs", "LAR"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAM`} + name={[field.name, "costs", "LAM"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAS`} - name={[field.name, "costs", "LAS"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAR`} + name={[field.name, "costs", "LAR"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LAU`} - name={[field.name, "costs", "LAU"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAS`} + name={[field.name, "costs", "LAS"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LA1`} - name={[field.name, "costs", "LA1"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LAU`} + name={[field.name, "costs", "LAU"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LA2`} - name={[field.name, "costs", "LA2"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LA1`} + name={[field.name, "costs", "LA1"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LA3`} - name={[field.name, "costs", "LA3"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LA2`} + name={[field.name, "costs", "LA2"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-LA4`} - name={[field.name, "costs", "LA4"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LA3`} + name={[field.name, "costs", "LA3"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAA`} - name={[field.name, "costs", "PAA"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-LA4`} + name={[field.name, "costs", "LA4"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAC`} - name={[field.name, "costs", "PAC"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAA`} + name={[field.name, "costs", "PAA"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAG`} - name={[field.name, "costs", "PAG"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAC`} + name={[field.name, "costs", "PAC"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAL`} - name={[field.name, "costs", "PAL"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAG`} + name={[field.name, "costs", "PAG"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAM`} - name={[field.name, "costs", "PAM"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAL`} + name={[field.name, "costs", "PAL"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAN`} - name={[field.name, "costs", "PAN"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAM`} + name={[field.name, "costs", "PAM"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAO`} - name={[field.name, "costs", "PAO"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAN`} + name={[field.name, "costs", "PAN"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAP`} - name={[field.name, "costs", "PAP"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAO`} + name={[field.name, "costs", "PAO"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAR`} - name={[field.name, "costs", "PAR"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAP`} + name={[field.name, "costs", "PAP"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PAS`} - name={[field.name, "costs", "PAS"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAR`} + name={[field.name, "costs", "PAR"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-PASL`} - name={[field.name, "costs", "PASL"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PAS`} + name={[field.name, "costs", "PAS"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-TOW`} - name={[field.name, "costs", "TOW"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-PASL`} + name={[field.name, "costs", "PASL"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-MAPA`} - name={[field.name, "costs", "MAPA"]} - > - - - ({ - validator(rule, value) { - if (costOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-TOW`} + name={[field.name, "costs", "TOW"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}costs-MASH`} - name={[field.name, "costs", "MASH"]} - > - - - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-MAPA`} + name={[field.name, "costs", "MAPA"]} + > + + + ({ + validator(rule, value) { + if (costOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-ATS`} - name={[field.name, "profits", "ATS"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}costs-MASH`} + name={[field.name, "costs", "MASH"]} + > + + + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAA`} - name={[field.name, "profits", "LAA"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-ATS`} + name={[field.name, "profits", "ATS"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAB`} - name={[field.name, "profits", "LAB"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAA`} + name={[field.name, "profits", "LAA"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAD`} - name={[field.name, "profits", "LAD"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAB`} + name={[field.name, "profits", "LAB"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAE`} - name={[field.name, "profits", "LAE"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAD`} + name={[field.name, "profits", "LAD"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAF`} - name={[field.name, "profits", "LAF"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAE`} + name={[field.name, "profits", "LAE"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAG`} - name={[field.name, "profits", "LAG"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAF`} + name={[field.name, "profits", "LAF"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAM`} - name={[field.name, "profits", "LAM"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAG`} + name={[field.name, "profits", "LAG"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAR`} - name={[field.name, "profits", "LAR"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAM`} + name={[field.name, "profits", "LAM"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAS`} - name={[field.name, "profits", "LAS"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAR`} + name={[field.name, "profits", "LAR"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LAU`} - name={[field.name, "profits", "LAU"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAS`} + name={[field.name, "profits", "LAS"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LA1`} - name={[field.name, "profits", "LA1"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LAU`} + name={[field.name, "profits", "LAU"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LA2`} - name={[field.name, "profits", "LA2"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LA1`} + name={[field.name, "profits", "LA1"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LA3`} - name={[field.name, "profits", "LA3"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LA2`} + name={[field.name, "profits", "LA2"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-LA4`} - name={[field.name, "profits", "LA4"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LA3`} + name={[field.name, "profits", "LA3"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAA`} - name={[field.name, "profits", "PAA"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-LA4`} + name={[field.name, "profits", "LA4"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAC`} - name={[field.name, "profits", "PAC"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAA`} + name={[field.name, "profits", "PAA"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAG`} - name={[field.name, "profits", "PAG"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAC`} + name={[field.name, "profits", "PAC"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAL`} - name={[field.name, "profits", "PAL"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAG`} + name={[field.name, "profits", "PAG"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAM`} - name={[field.name, "profits", "PAM"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAL`} + name={[field.name, "profits", "PAL"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAN`} - name={[field.name, "profits", "PAN"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAM`} + name={[field.name, "profits", "PAM"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAO`} - name={[field.name, "profits", "PAO"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAN`} + name={[field.name, "profits", "PAN"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAP`} - name={[field.name, "profits", "PAP"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAO`} + name={[field.name, "profits", "PAO"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAR`} - name={[field.name, "profits", "PAR"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAP`} + name={[field.name, "profits", "PAP"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PAS`} - name={[field.name, "profits", "PAS"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAR`} + name={[field.name, "profits", "PAR"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-PASL`} - name={[field.name, "profits", "PASL"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PAS`} + name={[field.name, "profits", "PAS"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-TOW`} - name={[field.name, "profits", "TOW"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-PASL`} + name={[field.name, "profits", "PASL"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-MAPA`} - name={[field.name, "profits", "MAPA"]} - > - - - ({ - validator(rule, value) { - if (profitOptions.includes(value)) { - return Promise.resolve(); + }) + ]} + key={`${index}profits-TOW`} + name={[field.name, "profits", "TOW"]} + > + + + ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); } - return Promise.reject(t("bodyshop.validation.centermustexist")); - } - }) - ]} - key={`${index}profits-MASH`} - name={[field.name, "profits", "MASH"]} - > - - - -
+ }) + ]} + key={`${index}profits-MAPA`} + name={[field.name, "profits", "MAPA"]} + > + +
+ ({ + validator(rule, value) { + if (profitOptions.includes(value)) { + return Promise.resolve(); + } + return Promise.reject(t("bodyshop.validation.centermustexist")); + } + }) + ]} + key={`${index}profits-MASH`} + name={[field.name, "profits", "MASH"]} + > + + + +
+
+ ))} + + - ))} - - - -
- ); - }} - +
+ ); + }} +
+ )} @@ -3720,7 +3717,6 @@ export function ShopInfoResponsibilityCenterComponent({ bodyshop, form }) {
- )} - {t("bodyshop.labels.responsibilitycenters.sales_tax_codes")} - - {(fields, { add, remove }) => { - return ( -
- {fields.map((field, index) => ( - - - - - - - - - - - - - - - - - - { - remove(field.name); - }} - /> - + + + {(fields, { add, remove }) => { + return ( +
+ {fields.map((field, index) => ( + + 0 ? false : true}> + + + + + + + + + + + + + + + + { + remove(field.name); + }} + /> + + + ))} + + - ))} - - - -
- ); - }} -
+
+ ); + }} +
+ ); } diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index c6eff8adf..377552b72 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -905,7 +905,7 @@ export const QUERY_JOB_CARD_DETAILS = gql` } joblines( - where: { removed: { _eq: false }, part_type: { _is_null: false, _nin: ["PAE", "PAS", "PASL"] } } + where: { removed: { _eq: false } } order_by: { line_no: asc } ) { id diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index 72f172697..4baa28fef 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -647,7 +647,10 @@ "payers": "Payers" }, "cdk_dealerid": "CDK Dealer ID", + "costsmapping": "Costs Mapping", + "dms_allocations": "DMS Allocations", "pbs_serialnumber": "PBS Serial Number", + "profitsmapping": "Profits Mapping", "title": "DMS" }, "emaillater": "Email Later", @@ -2947,6 +2950,8 @@ "purchases_by_cost_center_summary": "Purchases by Cost Center (Summary)", "purchases_by_date_range_detail": "Purchases by Date - Detail", "purchases_by_date_range_summary": "Purchases by Date - Summary", + "purchases_by_ro_detail_date": "Purchases by RO - Detail", + "purchases_by_ro_summary_date": "Purchases by RO - Summary", "purchases_by_vendor_detailed_date_range": "Purchases By Vendor - Detailed", "purchases_by_vendor_summary_date_range": "Purchases by Vendor - Summary", "purchases_grouped_by_vendor_detailed": "Purchases Grouped by Vendor - Detailed", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index c1876522f..d65e54245 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -647,7 +647,10 @@ "payers": "" }, "cdk_dealerid": "", + "costsmapping": "", + "dms_allocations": "", "pbs_serialnumber": "", + "profitsmapping": "", "title": "" }, "emaillater": "", @@ -2937,6 +2940,8 @@ "purchases_by_cost_center_summary": "", "purchases_by_date_range_detail": "", "purchases_by_date_range_summary": "", + "purchases_by_ro_detail_date": "", + "purchases_by_ro_summary_date": "", "purchases_by_vendor_detailed_date_range": "", "purchases_by_vendor_summary_date_range": "", "purchases_grouped_by_vendor_detailed": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 38697b677..4abb5d683 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -647,7 +647,10 @@ "payers": "" }, "cdk_dealerid": "", + "costsmapping": "", + "dms_allocations": "", "pbs_serialnumber": "", + "profitsmapping": "", "title": "" }, "emaillater": "", @@ -2937,6 +2940,8 @@ "purchases_by_cost_center_summary": "", "purchases_by_date_range_detail": "", "purchases_by_date_range_summary": "", + "purchases_by_ro_detail_date": "", + "purchases_by_ro_summary_date": "", "purchases_by_vendor_detailed_date_range": "", "purchases_by_vendor_summary_date_range": "", "purchases_grouped_by_vendor_detailed": "", diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index 0296fc317..8fda63504 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -1081,6 +1081,32 @@ export const TemplateList = (type, context) => { }, group: "purchases" }, + purchases_by_ro_detail_date: { + title: i18n.t("reportcenter.templates.purchases_by_ro_detail_date"), + description: "", + subject: i18n.t("reportcenter.templates.purchases_by_ro_detail_date"), + key: "purchases_by_ro_detail_date", + //idtype: "vendor", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.jobs"), + field: i18n.t("jobs.fields.date_invoiced") + }, + group: "purchases" + }, + purchases_by_ro_summary_date: { + title: i18n.t("reportcenter.templates.purchases_by_ro_summary_date"), + description: "", + subject: i18n.t("reportcenter.templates.purchases_by_ro_summary_date"), + key: "purchases_by_ro_summary_date", + //idtype: "vendor", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.jobs"), + field: i18n.t("jobs.fields.date_invoiced") + }, + group: "purchases" + }, job_costing_ro_date_summary: { title: i18n.t("reportcenter.templates.job_costing_ro_date_summary"), description: "", diff --git a/hasura/migrations/1720712957502_run_sql_migration/down.sql b/hasura/migrations/1720712957502_run_sql_migration/down.sql new file mode 100644 index 000000000..25f6a61a9 --- /dev/null +++ b/hasura/migrations/1720712957502_run_sql_migration/down.sql @@ -0,0 +1,10 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- CREATE OR REPLACE VIEW "public"."joblines_status" AS +-- SELECT j.jobid, +-- j.status, +-- count(1) AS count, +-- j.part_type +-- FROM joblines j +-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> 0) AND (j.act_price <> 0) AND (j.removed IS FALSE)) +-- GROUP BY j.jobid, j.status, j.part_type; diff --git a/hasura/migrations/1720712957502_run_sql_migration/up.sql b/hasura/migrations/1720712957502_run_sql_migration/up.sql new file mode 100644 index 000000000..8ffd417d5 --- /dev/null +++ b/hasura/migrations/1720712957502_run_sql_migration/up.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE VIEW "public"."joblines_status" AS + SELECT j.jobid, + j.status, + count(1) AS count, + j.part_type + FROM joblines j + WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> 0) AND (j.act_price <> 0) AND (j.removed IS FALSE)) + GROUP BY j.jobid, j.status, j.part_type; diff --git a/hasura/migrations/1720713022389_run_sql_migration/down.sql b/hasura/migrations/1720713022389_run_sql_migration/down.sql new file mode 100644 index 000000000..6e9bfe3ec --- /dev/null +++ b/hasura/migrations/1720713022389_run_sql_migration/down.sql @@ -0,0 +1,10 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- CREATE OR REPLACE VIEW "public"."joblines_status" AS +-- SELECT j.jobid, +-- j.status, +-- count(1) AS count, +-- j.part_type +-- FROM joblines j +-- WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> (0)::numeric) AND (j.act_price <> (0)::numeric) AND (j.removed IS FALSE)) +-- GROUP BY j.jobid, j.status, j.part_type; diff --git a/hasura/migrations/1720713022389_run_sql_migration/up.sql b/hasura/migrations/1720713022389_run_sql_migration/up.sql new file mode 100644 index 000000000..109db1e8e --- /dev/null +++ b/hasura/migrations/1720713022389_run_sql_migration/up.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE VIEW "public"."joblines_status" AS + SELECT j.jobid, + j.status, + count(1) AS count, + j.part_type + FROM joblines j + WHERE ((j.part_type IS NOT NULL) AND (j.part_type <> 'PAE'::text) AND (j.part_type <> 'PAS'::text) AND (j.part_type <> 'PASL'::text) AND (j.part_qty <> (0)::numeric) AND (j.act_price <> (0)::numeric) AND (j.removed IS FALSE)) + GROUP BY j.jobid, j.status, j.part_type; diff --git a/package.json b/package.json index c1923fd0c..b4df10dfe 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "better-queue": "^3.8.12", "bluebird": "^3.7.2", "body-parser": "^1.20.2", + "canvas": "^2.11.2", + "chart.js": "^4.4.3", "cloudinary": "^2.2.0", "compression": "^1.7.4", "cookie-parser": "^1.4.6", diff --git a/server/cdk/cdk-calculate-allocations.js b/server/cdk/cdk-calculate-allocations.js index b557f322d..c47addbfe 100644 --- a/server/cdk/cdk-calculate-allocations.js +++ b/server/cdk/cdk-calculate-allocations.js @@ -16,403 +16,402 @@ const { DiscountNotAlreadyCounted } = InstanceManager({ promanager: "USE_ROME" }); +exports.defaultRoute = async function (req, res) { + try { + CdkBase.createLogEvent(req, "DEBUG", `Received request to calculate allocations for ${req.body.jobid}`); + const jobData = await QueryJobData(req, req.BearerToken, req.body.jobid); + return res.status(200).json({ data: calculateAllocations(req, jobData) }); + } catch (error) { + console.log(error); + CdkBase.createLogEvent(req, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`); + res.status(500).json({ error: `Error encountered in CdkCalculateAllocations. ${error}` }); + } +}; + exports.default = async function (socket, jobid) { try { - CdkBase.createLogEvent(socket, "DEBUG", `Received request to calculate allocations for ${jobid}`); - const job = await QueryJobData(socket, jobid); - const { bodyshop } = job; - - const taxAllocations = InstanceManager({ - executeFunction: true, - deubg: true, - args: [], - imex: () => ({ - local: { - center: bodyshop.md_responsibility_centers.taxes.local.name, - sale: Dinero(job.job_totals.totals.local_tax), - cost: Dinero(), - profitCenter: bodyshop.md_responsibility_centers.taxes.local, - costCenter: bodyshop.md_responsibility_centers.taxes.local - }, - state: { - center: bodyshop.md_responsibility_centers.taxes.state.name, - sale: Dinero(job.job_totals.totals.state_tax), - cost: Dinero(), - profitCenter: bodyshop.md_responsibility_centers.taxes.state, - costCenter: bodyshop.md_responsibility_centers.taxes.state - }, - federal: { - center: bodyshop.md_responsibility_centers.taxes.federal.name, - sale: Dinero(job.job_totals.totals.federal_tax), - cost: Dinero(), - profitCenter: bodyshop.md_responsibility_centers.taxes.federal, - costCenter: bodyshop.md_responsibility_centers.taxes.federal - } - }), - rome: () => ({ - tax_ty1: { - center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name, - sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]), - cost: Dinero(), - profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`], - costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`] - }, - tax_ty2: { - center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name, - sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]), - cost: Dinero(), - profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`], - costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`] - }, - tax_ty3: { - center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name, - sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]), - cost: Dinero(), - profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`], - costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`] - }, - tax_ty4: { - center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name, - sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]), - cost: Dinero(), - profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`], - costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`] - }, - tax_ty5: { - center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name, - sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]), - cost: Dinero(), - profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`], - costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`] - } - }) - }); - - //Determine if there are MAPA and MASH lines already on the estimate. - //If there are, don't do anything extra (mitchell estimate) - //Otherwise, calculate them and add them to the default MAPA and MASH centers. - let hasMapaLine = false; - let hasMashLine = false; - - const profitCenterHash = job.joblines.reduce((acc, val) => { - //Check the Parts Assignment - if (val.db_ref === "936008") { - //If either of these DB REFs change, they also need to change in job-totals/job-costing calculations. - hasMapaLine = true; - } - if (val.db_ref === "936007") { - hasMashLine = true; - } - - if (val.profitcenter_part) { - if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero(); - - let DineroAmount = Dinero({ - amount: Math.round(val.act_price * 100) - }).multiply(val.part_qty || 1); - - DineroAmount = DineroAmount.add( - ((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) && - DiscountNotAlreadyCounted(val, job.joblines) - ? val.prt_dsmk_m - ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) - : Dinero({ - amount: Math.round(val.act_price * 100) - }) - .multiply(val.part_qty || 0) - .percentage(Math.abs(val.prt_dsmk_p || 0)) - .multiply(val.prt_dsmk_p > 0 ? 1 : -1) - : Dinero() - ); - - acc[val.profitcenter_part] = acc[val.profitcenter_part].add(DineroAmount); - } - if (val.profitcenter_labor && val.mod_lbr_ty) { - //Check the Labor Assignment. - - if (!acc[val.profitcenter_labor]) acc[val.profitcenter_labor] = Dinero(); - - acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add( - Dinero({ - amount: Math.round(job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100) - }).multiply(val.mod_lb_hrs) - ); - } - return acc; - }, {}); - - const selectedDmsAllocationConfig = bodyshop.md_responsibility_centers.dms_defaults.find( - (d) => d.name === job.dms_allocation - ); - CdkBase.createLogEvent( - socket, - "DEBUG", - `Using DMS Allocation ${selectedDmsAllocationConfig && selectedDmsAllocationConfig.name} for cost export.` - ); - - let costCenterHash = {}; - //Check whether to skip this if PBS and using AP module. - const disablebillwip = !!bodyshop?.pbs_configuration?.disablebillwip; - - if (!disablebillwip) { - costCenterHash = job.bills.reduce((bill_acc, bill_val) => { - bill_val.billlines.map((line_val) => { - if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]]) - bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = Dinero(); - - let lineDinero = Dinero({ - amount: Math.round((line_val.actual_cost || 0) * 100) - }) - .multiply(line_val.quantity) - .multiply(bill_val.is_credit_memo ? -1 : 1); - - bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = - bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(lineDinero); - return null; - }); - return bill_acc; - }, {}); - } - - job.timetickets.forEach((ticket) => { - //Get the total amount of the ticket. - let TicketTotal = Dinero({ - amount: Math.round( - ticket.rate * - (ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) * - 100 - ) - }); - //Add it to the right cost center. - if (!costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]]) - costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = Dinero(); - - costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = - costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]].add(TicketTotal); - }); - - if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) { - // console.log("Adding MAPA Line Manually."); - const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA; - - const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName); - - if (mapaAccount) { - if (!profitCenterHash[mapaAccountName]) profitCenterHash[mapaAccountName] = Dinero(); - - profitCenterHash[mapaAccountName] = profitCenterHash[mapaAccountName].add( - Dinero(job.job_totals.rates.mapa.total) - ); - } else { - //console.log("NO MAPA ACCOUNT FOUND!!"); - } - } - - if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) { - // console.log("Adding MASH Line Manually."); - - const mashAccountName = selectedDmsAllocationConfig.profits.MASH; - - const mashAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mashAccountName); - - if (mashAccount) { - if (!profitCenterHash[mashAccountName]) profitCenterHash[mashAccountName] = Dinero(); - - profitCenterHash[mashAccountName] = profitCenterHash[mashAccountName].add( - Dinero(job.job_totals.rates.mash.total) - ); - } else { - // console.log("NO MASH ACCOUNT FOUND!!"); - } - } - console.log( - Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting), - typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting) - ); - if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) { - //Manually send the percentage of the costing. - - //Paint Mat - const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA; - const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName); - if (mapaAccount) { - if (!costCenterHash[mapaAccountName]) - costCenterHash[mapaAccountName] = Dinero(); - if (job.bodyshop.use_paint_scale_data === true) { - if (job.mixdata.length > 0) { - costCenterHash[mapaAccountName] = costCenterHash[ - mapaAccountName - ].add( - Dinero({ - amount: Math.round( - ((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) * - 100 - ), - }) - ); - } else { - costCenterHash[mapaAccountName] = costCenterHash[ - mapaAccountName - ].add( - Dinero(job.job_totals.rates.mapa.total).percentage( - bodyshop?.cdk_configuration?.sendmaterialscosting - ) - ); - } - } else { - costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add( - Dinero(job.job_totals.rates.mapa.total).percentage( - bodyshop?.cdk_configuration?.sendmaterialscosting - ) - ); - } - } else { - //console.log("NO MAPA ACCOUNT FOUND!!"); - } - - //Shop Mat - const mashAccountName = selectedDmsAllocationConfig.costs.MASH; - const mashAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mashAccountName); - if (mashAccount) { - if (!costCenterHash[mashAccountName]) costCenterHash[mashAccountName] = Dinero(); - costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add( - Dinero(job.job_totals.rates.mash.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting) - ); - } else { - // console.log("NO MASH ACCOUNT FOUND!!"); - } - } - - const { ca_bc_pvrt } = job; - if (ca_bc_pvrt) { - // const pvrtAccount = bodyshop.md_responsibility_centers.profits.find( - // (c) => c.name === mashAccountName - // ); - - taxAllocations.state.sale = taxAllocations.state.sale.add( - Dinero({ amount: Math.round((ca_bc_pvrt || 0) * 100) }) - ); - } - - if (job.towing_payable && job.towing_payable !== 0) { - const towAccountName = selectedDmsAllocationConfig.profits.TOW; - - const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === towAccountName); - - if (towAccount) { - if (!profitCenterHash[towAccountName]) profitCenterHash[towAccountName] = Dinero(); - - profitCenterHash[towAccountName] = profitCenterHash[towAccountName].add( - Dinero({ - amount: Math.round((job.towing_payable || 0) * 100) - }) - ); - } else { - // console.log("NO MASH ACCOUNT FOUND!!"); - } - } - if (job.storage_payable && job.storage_payable !== 0) { - const storageAccountName = selectedDmsAllocationConfig.profits.TOW; - - const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === storageAccountName); - - if (towAccount) { - if (!profitCenterHash[storageAccountName]) profitCenterHash[storageAccountName] = Dinero(); - - profitCenterHash[storageAccountName] = profitCenterHash[storageAccountName].add( - Dinero({ - amount: Math.round((job.storage_payable || 0) * 100) - }) - ); - } else { - // console.log("NO MASH ACCOUNT FOUND!!"); - } - } - - if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) { - const otherAccountName = selectedDmsAllocationConfig.profits.PAO; - - const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === otherAccountName); - - if (otherAccount) { - if (!profitCenterHash[otherAccountName]) profitCenterHash[otherAccountName] = Dinero(); - - profitCenterHash[otherAccountName] = profitCenterHash[otherAccountName].add( - Dinero({ - amount: Math.round((job.adjustment_bottom_line || 0) * 100) - }) - ); - } else { - // console.log("NO MASH ACCOUNT FOUND!!"); - } - } - if (InstanceManager({ rome: true })) { - //profile level adjustments - Object.keys(job.job_totals.parts.adjustments).forEach((key) => { - const accountName = selectedDmsAllocationConfig.profits[key]; - - const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName); - - if (otherAccount) { - if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero(); - - profitCenterHash[accountName] = profitCenterHash[accountName].add( - Dinero(job.job_totals.parts.adjustments[key]) - ); - } else { - CdkBase.createLogEvent( - socket, - "ERROR", - `Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}` - ); - } - }); - } - - const jobAllocations = _.union(Object.keys(profitCenterHash), Object.keys(costCenterHash)).map((key) => { - const profitCenter = bodyshop.md_responsibility_centers.profits.find((c) => c.name === key); - const costCenter = bodyshop.md_responsibility_centers.costs.find((c) => c.name === key); - - return { - center: key, - sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(), - cost: costCenterHash[key] ? costCenterHash[key] : Dinero(), - profitCenter, - costCenter - }; - }); - - return [ - ...jobAllocations, - ...Object.keys(taxAllocations) - .filter((key) => taxAllocations[key].sale.getAmount() > 0 || taxAllocations[key].cost.getAmount() > 0) - .map((key) => { - if ( - key === "federal" && - selectedDmsAllocationConfig.gst_override && - selectedDmsAllocationConfig.gst_override !== "" - ) { - const ret = { ...taxAllocations[key], tax: key }; - ret.costCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override; - ret.profitCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override; - return ret; - } else { - return { ...taxAllocations[key], tax: key }; - } - }) - ]; + const jobData = await QueryJobData(socket, "Bearer " + socket.handshake.auth.token, jobid); + return calculateAllocations(socket, jobData); } catch (error) { console.log(error); CdkBase.createLogEvent(socket, "ERROR", `Error encountered in CdkCalculateAllocations. ${error}`); } }; -async function QueryJobData(socket, jobid) { - CdkBase.createLogEvent(socket, "DEBUG", `Querying job data for id ${jobid}`); +async function QueryJobData(connectionData, token, jobid) { + CdkBase.createLogEvent(connectionData, "DEBUG", `Querying job data for id ${jobid}`); const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, {}); - const result = await client - .setHeaders({ Authorization: `Bearer ${socket.handshake.auth.token}` }) - .request(queries.GET_CDK_ALLOCATIONS, { id: jobid }); - CdkBase.createLogEvent(socket, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`); + const result = await client.setHeaders({ Authorization: token }).request(queries.GET_CDK_ALLOCATIONS, { id: jobid }); + CdkBase.createLogEvent(connectionData, "TRACE", `Job data query result ${JSON.stringify(result, null, 2)}`); return result.jobs_by_pk; } + +function calculateAllocations(connectionData, job) { + const { bodyshop } = job; + + const taxAllocations = InstanceManager({ + executeFunction: true, + deubg: true, + args: [], + imex: () => ({ + local: { + center: bodyshop.md_responsibility_centers.taxes.local.name, + sale: Dinero(job.job_totals.totals.local_tax), + cost: Dinero(), + profitCenter: bodyshop.md_responsibility_centers.taxes.local, + costCenter: bodyshop.md_responsibility_centers.taxes.local + }, + state: { + center: bodyshop.md_responsibility_centers.taxes.state.name, + sale: Dinero(job.job_totals.totals.state_tax), + cost: Dinero(), + profitCenter: bodyshop.md_responsibility_centers.taxes.state, + costCenter: bodyshop.md_responsibility_centers.taxes.state + }, + federal: { + center: bodyshop.md_responsibility_centers.taxes.federal.name, + sale: Dinero(job.job_totals.totals.federal_tax), + cost: Dinero(), + profitCenter: bodyshop.md_responsibility_centers.taxes.federal, + costCenter: bodyshop.md_responsibility_centers.taxes.federal + } + }), + rome: () => ({ + tax_ty1: { + center: bodyshop.md_responsibility_centers.taxes[`tax_ty1`].name, + sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty1Tax`]), + cost: Dinero(), + profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`], + costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty1`] + }, + tax_ty2: { + center: bodyshop.md_responsibility_centers.taxes[`tax_ty2`].name, + sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty2Tax`]), + cost: Dinero(), + profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`], + costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty2`] + }, + tax_ty3: { + center: bodyshop.md_responsibility_centers.taxes[`tax_ty3`].name, + sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty3Tax`]), + cost: Dinero(), + profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`], + costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty3`] + }, + tax_ty4: { + center: bodyshop.md_responsibility_centers.taxes[`tax_ty4`].name, + sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty4Tax`]), + cost: Dinero(), + profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`], + costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty4`] + }, + tax_ty5: { + center: bodyshop.md_responsibility_centers.taxes[`tax_ty5`].name, + sale: Dinero(job.job_totals.totals.us_sales_tax_breakdown[`ty5Tax`]), + cost: Dinero(), + profitCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`], + costCenter: bodyshop.md_responsibility_centers.taxes[`tax_ty5`] + } + }) + }); + + //Determine if there are MAPA and MASH lines already on the estimate. + //If there are, don't do anything extra (mitchell estimate) + //Otherwise, calculate them and add them to the default MAPA and MASH centers. + let hasMapaLine = false; + let hasMashLine = false; + + const profitCenterHash = job.joblines.reduce((acc, val) => { + //Check the Parts Assignment + if (val.db_ref === "936008") { + //If either of these DB REFs change, they also need to change in job-totals/job-costing calculations. + hasMapaLine = true; + } + if (val.db_ref === "936007") { + hasMashLine = true; + } + + if (val.profitcenter_part) { + if (!acc[val.profitcenter_part]) acc[val.profitcenter_part] = Dinero(); + + let DineroAmount = Dinero({ + amount: Math.round(val.act_price * 100) + }).multiply(val.part_qty || 1); + + DineroAmount = DineroAmount.add( + ((val.prt_dsmk_m && val.prt_dsmk_m !== 0) || (val.prt_dsmk_p && val.prt_dsmk_p !== 0)) && + DiscountNotAlreadyCounted(val, job.joblines) + ? val.prt_dsmk_m + ? Dinero({ amount: Math.round(val.prt_dsmk_m * 100) }) + : Dinero({ + amount: Math.round(val.act_price * 100) + }) + .multiply(val.part_qty || 0) + .percentage(Math.abs(val.prt_dsmk_p || 0)) + .multiply(val.prt_dsmk_p > 0 ? 1 : -1) + : Dinero() + ); + + acc[val.profitcenter_part] = acc[val.profitcenter_part].add(DineroAmount); + } + if (val.profitcenter_labor && val.mod_lbr_ty) { + //Check the Labor Assignment. + + if (!acc[val.profitcenter_labor]) acc[val.profitcenter_labor] = Dinero(); + + acc[val.profitcenter_labor] = acc[val.profitcenter_labor].add( + Dinero({ + amount: Math.round(job[`rate_${val.mod_lbr_ty.toLowerCase()}`] * 100) + }).multiply(val.mod_lb_hrs) + ); + } + return acc; + }, {}); + + const selectedDmsAllocationConfig = bodyshop.md_responsibility_centers.dms_defaults.find( + (d) => d.name === job.dms_allocation + ); + CdkBase.createLogEvent( + connectionData, + "DEBUG", + `Using DMS Allocation ${selectedDmsAllocationConfig && selectedDmsAllocationConfig.name} for cost export.` + ); + + let costCenterHash = {}; + //Check whether to skip this if PBS and using AP module. + const disablebillwip = !!bodyshop?.pbs_configuration?.disablebillwip; + + if (!disablebillwip) { + costCenterHash = job.bills.reduce((bill_acc, bill_val) => { + bill_val.billlines.map((line_val) => { + if (!bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]]) + bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = Dinero(); + + let lineDinero = Dinero({ + amount: Math.round((line_val.actual_cost || 0) * 100) + }) + .multiply(line_val.quantity) + .multiply(bill_val.is_credit_memo ? -1 : 1); + + bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]] = + bill_acc[selectedDmsAllocationConfig.costs[line_val.cost_center]].add(lineDinero); + return null; + }); + return bill_acc; + }, {}); + } + + job.timetickets.forEach((ticket) => { + //Get the total amount of the ticket. + let TicketTotal = Dinero({ + amount: Math.round( + ticket.rate * + (ticket.employee && ticket.employee.flat_rate ? ticket.productivehrs || 0 : ticket.actualhrs || 0) * + 100 + ) + }); + //Add it to the right cost center. + if (!costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]]) + costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = Dinero(); + + costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]] = + costCenterHash[selectedDmsAllocationConfig.costs[ticket.ciecacode]].add(TicketTotal); + }); + + if (!hasMapaLine && job.job_totals.rates.mapa.total.amount > 0) { + // console.log("Adding MAPA Line Manually."); + const mapaAccountName = selectedDmsAllocationConfig.profits.MAPA; + + const mapaAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mapaAccountName); + + if (mapaAccount) { + if (!profitCenterHash[mapaAccountName]) profitCenterHash[mapaAccountName] = Dinero(); + + profitCenterHash[mapaAccountName] = profitCenterHash[mapaAccountName].add( + Dinero(job.job_totals.rates.mapa.total) + ); + } else { + //console.log("NO MAPA ACCOUNT FOUND!!"); + } + } + + if (!hasMashLine && job.job_totals.rates.mash.total.amount > 0) { + // console.log("Adding MASH Line Manually."); + + const mashAccountName = selectedDmsAllocationConfig.profits.MASH; + + const mashAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === mashAccountName); + + if (mashAccount) { + if (!profitCenterHash[mashAccountName]) profitCenterHash[mashAccountName] = Dinero(); + + profitCenterHash[mashAccountName] = profitCenterHash[mashAccountName].add( + Dinero(job.job_totals.rates.mash.total) + ); + } else { + // console.log("NO MASH ACCOUNT FOUND!!"); + } + } + // console.log( + // Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting), + // typeof Number.isInteger(bodyshop?.cdk_configuration?.sendmaterialscosting) + // ); + if (!!bodyshop?.cdk_configuration?.sendmaterialscosting) { + //Manually send the percentage of the costing. + + //Paint Mat + const mapaAccountName = selectedDmsAllocationConfig.costs.MAPA; + const mapaAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mapaAccountName); + if (mapaAccount) { + if (!costCenterHash[mapaAccountName]) costCenterHash[mapaAccountName] = Dinero(); + if (job.bodyshop.use_paint_scale_data === true) { + if (job.mixdata.length > 0) { + costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add( + Dinero({ + amount: Math.round(((job.mixdata[0] && job.mixdata[0].totalliquidcost) || 0) * 100) + }) + ); + } else { + costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add( + Dinero(job.job_totals.rates.mapa.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting) + ); + } + } else { + costCenterHash[mapaAccountName] = costCenterHash[mapaAccountName].add( + Dinero(job.job_totals.rates.mapa.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting) + ); + } + } else { + //console.log("NO MAPA ACCOUNT FOUND!!"); + } + + //Shop Mat + const mashAccountName = selectedDmsAllocationConfig.costs.MASH; + const mashAccount = bodyshop.md_responsibility_centers.costs.find((c) => c.name === mashAccountName); + if (mashAccount) { + if (!costCenterHash[mashAccountName]) costCenterHash[mashAccountName] = Dinero(); + costCenterHash[mashAccountName] = costCenterHash[mashAccountName].add( + Dinero(job.job_totals.rates.mash.total).percentage(bodyshop?.cdk_configuration?.sendmaterialscosting) + ); + } else { + // console.log("NO MASH ACCOUNT FOUND!!"); + } + } + + const { ca_bc_pvrt } = job; + if (ca_bc_pvrt) { + // const pvrtAccount = bodyshop.md_responsibility_centers.profits.find( + // (c) => c.name === mashAccountName + // ); + + taxAllocations.state.sale = taxAllocations.state.sale.add(Dinero({ amount: Math.round((ca_bc_pvrt || 0) * 100) })); + } + + if (job.towing_payable && job.towing_payable !== 0) { + const towAccountName = selectedDmsAllocationConfig.profits.TOW; + + const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === towAccountName); + + if (towAccount) { + if (!profitCenterHash[towAccountName]) profitCenterHash[towAccountName] = Dinero(); + + profitCenterHash[towAccountName] = profitCenterHash[towAccountName].add( + Dinero({ + amount: Math.round((job.towing_payable || 0) * 100) + }) + ); + } else { + // console.log("NO MASH ACCOUNT FOUND!!"); + } + } + if (job.storage_payable && job.storage_payable !== 0) { + const storageAccountName = selectedDmsAllocationConfig.profits.TOW; + + const towAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === storageAccountName); + + if (towAccount) { + if (!profitCenterHash[storageAccountName]) profitCenterHash[storageAccountName] = Dinero(); + + profitCenterHash[storageAccountName] = profitCenterHash[storageAccountName].add( + Dinero({ + amount: Math.round((job.storage_payable || 0) * 100) + }) + ); + } else { + // console.log("NO MASH ACCOUNT FOUND!!"); + } + } + + if (job.adjustment_bottom_line && job.adjustment_bottom_line !== 0) { + const otherAccountName = selectedDmsAllocationConfig.profits.PAO; + + const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === otherAccountName); + + if (otherAccount) { + if (!profitCenterHash[otherAccountName]) profitCenterHash[otherAccountName] = Dinero(); + + profitCenterHash[otherAccountName] = profitCenterHash[otherAccountName].add( + Dinero({ + amount: Math.round((job.adjustment_bottom_line || 0) * 100) + }) + ); + } else { + // console.log("NO MASH ACCOUNT FOUND!!"); + } + } + if (InstanceManager({ rome: true })) { + //profile level adjustments + Object.keys(job.job_totals.parts.adjustments).forEach((key) => { + const accountName = selectedDmsAllocationConfig.profits[key]; + + const otherAccount = bodyshop.md_responsibility_centers.profits.find((c) => c.name === accountName); + + if (otherAccount) { + if (!profitCenterHash[accountName]) profitCenterHash[accountName] = Dinero(); + + profitCenterHash[accountName] = profitCenterHash[accountName].add( + Dinero(job.job_totals.parts.adjustments[key]) + ); + } else { + CdkBase.createLogEvent( + connectionData, + "ERROR", + `Error encountered in CdkCalculateAllocations. Unable to find adjustment account. ${error}` + ); + } + }); + } + + const jobAllocations = _.union(Object.keys(profitCenterHash), Object.keys(costCenterHash)).map((key) => { + const profitCenter = bodyshop.md_responsibility_centers.profits.find((c) => c.name === key); + const costCenter = bodyshop.md_responsibility_centers.costs.find((c) => c.name === key); + + return { + center: key, + sale: profitCenterHash[key] ? profitCenterHash[key] : Dinero(), + cost: costCenterHash[key] ? costCenterHash[key] : Dinero(), + profitCenter, + costCenter + }; + }); + + return [ + ...jobAllocations, + ...Object.keys(taxAllocations) + .filter((key) => taxAllocations[key].sale.getAmount() > 0 || taxAllocations[key].cost.getAmount() > 0) + .map((key) => { + if ( + key === "federal" && + selectedDmsAllocationConfig.gst_override && + selectedDmsAllocationConfig.gst_override !== "" + ) { + const ret = { ...taxAllocations[key], tax: key }; + ret.costCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override; + ret.profitCenter.dms_acctnumber = selectedDmsAllocationConfig.gst_override; + return ret; + } else { + return { ...taxAllocations[key], tax: key }; + } + }) + ]; +} diff --git a/server/data/kaizen.js b/server/data/kaizen.js index e0f82b58a..60e307956 100644 --- a/server/data/kaizen.js +++ b/server/data/kaizen.js @@ -244,6 +244,7 @@ const CreateRepairOrderTag = (job, errorCallback) => { }, InsuranceCompany: job.ins_co_nm || "", Claim: job.clm_no || "", + DMSAllocation: job.dms_allocation || "", Contacts: { CSR: job.employee_csr_rel ? `${ @@ -643,7 +644,8 @@ const CreateJobLines = (joblines) => { part_qty: jobline.part_qty, part_price: jobline.act_price, labor_type: jobline.mod_lbr_ty, - labor_hours: jobline.mod_lb_hrs + labor_hours: jobline.mod_lb_hrs, + labor_sale: jobline.lbr_amt }); }); return repairLines; @@ -660,7 +662,10 @@ const CreateTimeTickets = (timetickets) => { .trim(), productive_hrs: ticket.productivehrs, actual_hrs: ticket.actualhrs, - cost_center: ticket.cost_center + cost_center: ticket.cost_center, + flat_rate: ticket.flat_rate, + rate: ticket.rate, + ticket_cost: ticket.flat_rate ? ticket.rate * ticket.productive_hrs : ticket.rate * ticket.actual_hrs }); }); return timeTickets; diff --git a/server/graphql-client/queries.js b/server/graphql-client/queries.js index 4e63fff7e..9a2e139b4 100644 --- a/server/graphql-client/queries.js +++ b/server/graphql-client/queries.js @@ -1139,6 +1139,7 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu date_open date_repairstarted date_void + dms_allocation employee_body_rel { first_name last_name @@ -1184,6 +1185,7 @@ exports.KAIZEN_QUERY = `query KAIZEN_EXPORT($start: timestamptz, $bodyshopid: uu } db_price id + lbr_amt lbr_op line_desc line_ind diff --git a/server/render/canvas-colors.js b/server/render/canvas-colors.js new file mode 100644 index 000000000..a23208b2a --- /dev/null +++ b/server/render/canvas-colors.js @@ -0,0 +1,76 @@ +const borderColors = [ + "rgba(193, 0, 50, 1)", + "rgba(0, 101, 68, 1)", + "rgba(0, 45, 98, 1)", + "rgba(253, 184, 0, 1)", + "rgba(200, 112, 126, 1)", + "rgba(228, 142, 88, 1)", + "rgba(90, 160, 141, 1)", + "rgba(103, 143, 174, 1)", + "rgba(192, 136, 99, 1)", + "rgba(234, 3, 55, 1)", + "rgba(0, 149, 67, 1)", + "rgba(0, 81, 155, 1)", + "rgba(226, 143, 173, 1)", + "rgba(237, 170, 125, 1)", + "rgba(76, 146, 177, 1)", + "rgba(172, 153, 193, 1)", + "rgba(173, 167, 89, 1)", + "rgba(254, 197, 222, 1)", + "rgba(177, 231, 223, 1)", + "rgba(120, 199, 235, 1)", + "rgba(239, 180, 193, 1)", + "rgba(240, 199, 171, 1)", + "rgba(168, 200, 121, 1)", + "rgba(150, 177, 208, 1)", + "rgba(200, 194, 189, 1)", + "rgba(244, 244, 244, 1)", + "rgba(255, 99, 132, 1)", + "rgba(54, 162, 235, 1)", + "rgba(255, 206, 86, 1)", + "rgba(75, 192, 192, 1)", + "rgba(153, 102, 255, 1)", + "rgba(255, 159, 64, 1)", + "rgba(170, 183, 184, 1)", +]; + +const backgroundColors = [ + 'rgba(193, 0, 50, 0.2)', + 'rgba(0, 101, 68, 0.2)', + 'rgba(0, 45, 98, 0.2)', + 'rgba(253, 184, 0, 0.2)', + 'rgba(200, 112, 126, 0.2)', + 'rgba(228, 142, 88, 0.2)', + 'rgba(90, 160, 141, 0.2)', + 'rgba(103, 143, 174, 0.2)', + 'rgba(192, 136, 99, 0.2)', + 'rgba(234, 3, 55, 0.2)', + 'rgba(0, 149, 67, 0.2)', + 'rgba(0, 81, 155, 0.2)', + 'rgba(226, 143, 173, 0.2)', + 'rgba(237, 170, 125, 0.2)', + 'rgba(76, 146, 177, 0.2)', + 'rgba(172, 153, 193, 0.2)', + 'rgba(173, 167, 89, 0.2)', + 'rgba(254, 197, 222, 0.2)', + 'rgba(177, 231, 223, 0.2)', + 'rgba(120, 199, 235, 0.2)', + 'rgba(239, 180, 193, 0.2)', + 'rgba(240, 199, 171, 0.2)', + 'rgba(168, 200, 121, 0.2)', + 'rgba(150, 177, 208, 0.2)', + 'rgba(200, 194, 189, 0.2)', + 'rgba(244, 244, 244, 0.2)', + 'rgba(255, 99, 132, 0.2)', + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(75, 192, 192, 0.2)', + 'rgba(153, 102, 255, 0.2)', + 'rgba(255, 159, 64, 0.2)', + 'rgba(170, 183, 184, 0.2)', +]; + +module.exports = { + borderColors, + backgroundColors, +}; diff --git a/server/render/canvas-handler.js b/server/render/canvas-handler.js new file mode 100644 index 000000000..010a3bec3 --- /dev/null +++ b/server/render/canvas-handler.js @@ -0,0 +1,92 @@ +const { createCanvas } = require("canvas"); +const Chart = require("chart.js/auto"); + +const { backgroundColors, borderColors } = require("./canvas-colors"); +const { isObject, defaultsDeep, isNumber } = require("lodash"); + +exports.canvastest = function (req, res) { + console.log("Incoming test request.", req); + res.status(200).send("OK"); +}; + +exports.canvas = function (req, res) { + const { w, h, values, keys, override } = req.body; + console.log("Incoming Canvas Request:", w, h, values, keys, override); + + // Gate required values + if (!values || !keys) { + res.status(400).send("Missing required data"); + return; + } + + // Override must be an object if it exists + if (override && !isObject(override)) { + res.status(400).send("Override must be an object"); + return; + } + + // Set the default Width and Height + let [width, height] = [500, 275]; + + // Allow for custom width and height + if (isNumber(w)) { + width = w; + } + if (isNumber(h)) { + height = h; + } + + const configuration = { + type: "doughnut", + data: { + labels: keys, + datasets: [ + { + data: values, + backgroundColor: backgroundColors, + borderColor: borderColors, + borderWidth: 1 + } + ] + }, + options: { + devicePixelRatio: 4, + responsive: false, + maintainAspectRatio: true, + circumference: 180, + rotation: -90, + plugins: { + legend: { + labels: { + boxWidth: 20, + font: { + family: "'Montserrat'", + size: 10, + style: "normal", + weight: "normal" + } + }, + position: "left" + } + } + } + }; + + // If we have a valid override object, merge it with the default configuration object. + // This allows for you to override the default configuration with a custom one. + const defaults = () => { + if (!override || !isObject(override)) { + return configuration; + } + return defaultsDeep(override, configuration); + }; + + res.status(200).send( + (() => { + const canvas = createCanvas(width, height); + const ctx = canvas.getContext("2d"); + new Chart(ctx, defaults()); + return canvas.toDataURL(); + })() + ); +}; diff --git a/server/routes/cdkRoutes.js b/server/routes/cdkRoutes.js index 5fd0536cb..614e59d2b 100644 --- a/server/routes/cdkRoutes.js +++ b/server/routes/cdkRoutes.js @@ -1,11 +1,13 @@ const express = require("express"); const router = express.Router(); const cdkGetMake = require("../cdk/cdk-get-makes"); +const cdkCalculateAllocations = require("../cdk/cdk-calculate-allocations"); const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware"); const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware"); router.use(validateFirebaseIdTokenMiddleware); router.post("/getvehicles", withUserGraphQLClientMiddleware, cdkGetMake.default); +router.post("/calculate-allocations", withUserGraphQLClientMiddleware, cdkCalculateAllocations.defaultRoute); module.exports = router; diff --git a/server/routes/miscellaneousRoutes.js b/server/routes/miscellaneousRoutes.js index b2dd80b26..fded5f44b 100644 --- a/server/routes/miscellaneousRoutes.js +++ b/server/routes/miscellaneousRoutes.js @@ -11,6 +11,7 @@ const eventAuthorizationMiddleware = require("../middleware/eventAuthorizationMI const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware"); const withUserGraphQLClientMiddleware = require("../middleware/withUserGraphQLClientMiddleware"); const { taskAssignedEmail, tasksRemindEmail } = require("../email/tasksEmails"); +const { canvastest } = require("../render/canvas-handler"); //Test route to ensure Express is responding. router.get("/test", async function (req, res) { @@ -49,4 +50,7 @@ router.post("/tasks-remind-handler", eventAuthorizationMiddleware, tasksRemindEm router.post("/record-handler/arms", data.arms); router.post("/taskHandler", validateFirebaseIdTokenMiddleware, taskHandler.taskHandler); +// Canvas Test +router.post("/canvastest", validateFirebaseIdTokenMiddleware, canvastest); + module.exports = router; diff --git a/server/routes/renderRoutes.js b/server/routes/renderRoutes.js index 5bb91616a..13288d989 100644 --- a/server/routes/renderRoutes.js +++ b/server/routes/renderRoutes.js @@ -2,8 +2,10 @@ const express = require("express"); const router = express.Router(); const { inlinecss } = require("../render/inlinecss"); const validateFirebaseIdTokenMiddleware = require("../middleware/validateFirebaseIdTokenMiddleware"); +const { canvas } = require("../render/canvas-handler"); // Define the route for inline CSS rendering router.post("/inlinecss", validateFirebaseIdTokenMiddleware, inlinecss); +router.post("/canvas", validateFirebaseIdTokenMiddleware, canvas); module.exports = router;