Compare commits

...

38 Commits

Author SHA1 Message Date
Patrick Fic
09f909142b Resolve sped print error. 2022-03-25 07:21:54 -06:00
Patrick Fic
fb380a5b31 IO-1799 Mark md_from_email 2022-03-24 15:40:25 -06:00
Patrick Fic
3896a0b03d IO-1799 Change from address for emails. 2022-03-24 14:54:36 -06:00
Patrick Fic
832674662d Autohouse add PVRT to salestax per request. 2022-03-24 11:55:48 -06:00
Patrick Fic
f2b2011900 Ignore custom margins on certain templates. 2022-03-22 13:41:06 -06:00
Patrick Fic
15c305317a Update header and footer margins. 2022-03-22 11:11:47 -06:00
Patrick Fic
d6924c2292 Add JSR margins to speed print. 2022-03-21 11:52:44 -06:00
Patrick Fic
7ccd356f0a Add margin to templates generation. 2022-03-21 10:51:44 -07:00
Patrick Fic
592b47b5d4 IO-1789 Incoming hours cap 2022-03-21 11:47:25 -06:00
Patrick Fic
d9beee7f2f Merged in release/2022-03-18 (pull request #427)
IO-1777 add check for paint materials threshold.
2022-03-18 22:19:43 +00:00
Patrick Fic
75743f44e7 IO-1777 add check for paint materials threshold. 2022-03-18 15:18:48 -07:00
Patrick Fic
3d68a7099b Merged in release/2022-03-18 (pull request #426)
Release/2022 03 18
2022-03-18 21:46:33 +00:00
Patrick Fic
71f161ec27 IO-1774 Remove scheduled completion date on void. 2022-03-17 15:47:40 -07:00
Patrick Fic
9b1f24926f Merged in hotfix/2022-03-16 (pull request #424)
Autohouse updates.
2022-03-16 23:42:16 +00:00
Patrick Fic
bcc153caa5 Autohouse updates. 2022-03-16 16:39:42 -07:00
Patrick Fic
f59911d5ab Autohouse Adjustment 2022-03-16 16:38:41 -07:00
Patrick Fic
417958e1e8 Additional Autohouse Updates. 2022-03-16 15:50:03 -07:00
Patrick Fic
ccf2f0ad47 IO-1780 Print off 1 job note. 2022-03-16 14:01:05 -07:00
Patrick Fic
bb993ab1fb IO-1783 Add production filtered by status. 2022-03-16 14:00:04 -07:00
Patrick Fic
9c39c8d59b Add header and footer margins. 2022-03-16 13:53:30 -07:00
Patrick Fic
3439f09d9a IO-1693 Allow 0 line supplements. 2022-03-16 11:47:32 -07:00
Patrick Fic
279e93f0c3 IO-1774 Cancel appointments when voiding. 2022-03-14 13:46:18 -07:00
Patrick Fic
92f14d6fa5 IO-1750 Add towin and rent resp date fields. 2022-03-14 13:25:13 -07:00
Patrick Fic
148c645f18 IO-177 Paint/Mat Thresholds 2022-03-14 10:41:52 -07:00
Patrick Fic
9a65b6a1ce Merged in release/2022-03-11 (pull request #420)
Release/2022 03 11
2022-03-11 18:47:44 +00:00
Patrick Fic
839c82abb9 IO-1776 Mechanical authorization added to print center. 2022-03-11 08:11:44 -08:00
Patrick Fic
471756d7ad IO-1769 Resolve error on part selection. 2022-03-10 11:00:13 -08:00
Patrick Fic
2e2a4920ca IO-1772 Add label for no vehicle info. 2022-03-10 10:55:44 -08:00
Patrick Fic
f775e09391 Merged in hotfix/2022-03-09 (pull request #417)
Remove autohouse log file creation.
2022-03-09 17:23:57 +00:00
Patrick Fic
c0b0bcd55e Merged in hotfix/2022-03-09 (pull request #416)
Remove autohouse log file creation.
2022-03-09 16:42:11 +00:00
Patrick Fic
1bc5493f3f Remove autohouse log file creation. 2022-03-09 08:41:36 -08:00
Patrick Fic
2579558090 IO-1606 IO-1607 IO-1752 Move towing rates. 2022-03-08 14:47:01 -08:00
Patrick Fic
bc9a3a21a8 Merged in release/2022-03-11 (pull request #415)
Remove special characters from Autohouse export.

Approved-by: Patrick Fic
2022-03-07 22:48:22 +00:00
Patrick Fic
f11eb6406d Remove special characters from Autohouse export. 2022-03-07 14:44:48 -08:00
Patrick Fic
3d6bad9e7d Merged in release/2022-03-04 (pull request #413)
release/2022-03-04

Approved-by: Patrick Fic
2022-03-04 18:15:38 +00:00
Patrick Fic
a318f3e74b Merged in hotfix/2022-03-01 (pull request #410)
IO-1759 Replace offset with timezone.
2022-03-03 01:25:12 +00:00
Patrick Fic
e5a5cb4e85 IO-1759 Replace offset with timezone.
(cherry picked from commit 548d9255638839841e7ad7cbb24888c59dd398d3)
2022-03-02 17:23:10 -08:00
Patrick Fic
ce3fbab1dc Merged in hotfix/2022-03-01 (pull request #407)
Autohouse Extract Updates & Remove duedate on payables.

Approved-by: Patrick Fic
2022-03-02 02:24:43 +00:00
41 changed files with 697 additions and 65 deletions

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.7.1">
<babeledit_project be_version="2.7.1" version="1.2">
<!--
BabelEdit project file
@@ -4388,6 +4388,48 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>logo_img_footer_margin</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>logo_img_header_margin</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>logo_img_path</name>
<definition_loaded>false</definition_loaded>
@@ -4556,6 +4598,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>md_from_emails</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>
<folder_node>
<name>md_hour_split</name>
<children>
@@ -7238,6 +7301,32 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>ss_configuration</name>
<children>
<concept_node>
<name>dailyhrslimit</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
<name>ssbuckets</name>
<children>
@@ -13163,6 +13252,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>from</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>subject</name>
<definition_loaded>false</definition_loaded>
@@ -20581,6 +20691,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>date_rentalresp</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>date_scheduled</name>
<definition_loaded>false</definition_loaded>
@@ -20602,6 +20733,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>date_towin</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>ded_amt</name>
<definition_loaded>false</definition_loaded>
@@ -27371,6 +27523,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>threshhold</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>total_cost</name>
<definition_loaded>false</definition_loaded>
@@ -34752,6 +34925,27 @@
</concept_node>
</children>
</folder_node>
<concept_node>
<name>mechanical_authorization</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>mpi_animal_checklist</name>
<definition_loaded>false</definition_loaded>
@@ -38427,6 +38621,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>production_by_repair_status_one</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>production_by_ro</name>
<definition_loaded>false</definition_loaded>
@@ -43353,6 +43568,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>novehinfo</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>relatedjobs</name>
<definition_loaded>false</definition_loaded>

View File

@@ -16,9 +16,14 @@ import EmailDocumentsComponent from "../email-documents/email-documents.componen
import _ from "lodash";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
const mapDispatchToProps = (dispatch) => ({
//setUserLanguage: language => dispatch(setUserLanguage(language))
@@ -28,7 +33,12 @@ export default connect(
mapDispatchToProps
)(EmailOverlayComponent);
export function EmailOverlayComponent({ form, selectedMediaState, bodyshop }) {
export function EmailOverlayComponent({
form,
selectedMediaState,
bodyshop,
currentUser,
}) {
const { t } = useTranslation();
const handleClick = ({ item, key, keyPath }) => {
const email = item.props.value;
@@ -51,6 +61,27 @@ export function EmailOverlayComponent({ form, selectedMediaState, bodyshop }) {
return (
<div>
<Form.Item
label={t("emails.fields.from")}
name="from"
rules={[
{
required: true,
//message: t("general.validation.required"),
},
]}
>
<Select>
<Select.Option key={currentUser.email}>
{currentUser.email}
</Select.Option>
<Select.Option key={bodyshop.email}>{bodyshop.email}</Select.Option>
{bodyshop.md_from_emails &&
bodyshop.md_from_emails.map((e) => (
<Select.Option key={e}>{e}</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label={
<Space>

View File

@@ -56,13 +56,9 @@ export function EmailOverlayContainer({
: bodyshop.shopname,
address: EmailSettings.fromAddress,
},
ReplyTo: {
Email: currentUser.validemail ? currentUser.email : bodyshop.email,
Name: currentUser.displayName,
},
};
const handleFinish = async (values) => {
const handleFinish = async (allValues) => {
logImEXEvent("email_send_from_modal");
//const attachments = [];
@@ -77,10 +73,15 @@ export function EmailOverlayContainer({
// attachments.push(t);
// });
const { from, ...values } = allValues;
setSending(true);
try {
await axios.post("/sendemail", {
...defaultEmailFrom,
ReplyTo: {
Email: from,
Name: currentUser.displayName,
},
...values,
html: rawHtml,
attachments: [
@@ -138,6 +139,7 @@ export function EmailOverlayContainer({
}
form.setFieldsValue({
from: currentUser.validemail ? currentUser.email : bodyshop.email,
...emailConfig.messageOptions,
cc:
emailConfig.messageOptions.cc &&

View File

@@ -64,9 +64,9 @@ export default function JobBillsTotalComponent({
})
);
const totalPartsSublet = Dinero(totals.parts.parts.total).add(
Dinero(totals.parts.sublets.total)
);
const totalPartsSublet = Dinero(totals.parts.parts.total)
.add(Dinero(totals.parts.sublets.total))
.add(Dinero(totals.additional.towing));
const discrepancy = totalPartsSublet.subtract(billTotals);

View File

@@ -462,7 +462,7 @@ export function JobLinesComponent({
};
}}
rowSelection={{
selectedRowKeys: selectedLines.map((item) => item.id),
selectedRowKeys: selectedLines.map((item) => item && item.id),
onSelectAll: (selected, selectedRows, changeRows) => {
setSelectedLines(selectedRows);
},

View File

@@ -18,7 +18,11 @@ export default function JobReconciliationModalComponent({ job, bills }) {
.flat() || [];
const jobLineData = job.joblines.filter(
(j) => j.part_type !== null && j.part_type !== "PAE"
(j) =>
(j.part_type !== null && j.part_type !== "PAE") ||
(j.line_desc &&
j.line_desc.toLowerCase().includes("towing") &&
j.lbr_op === "OP13")
);
return (

View File

@@ -1,4 +1,4 @@
import { Table } from "antd";
import { Space, Table } from "antd";
import Dinero from "dinero.js";
import React, { useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -119,7 +119,18 @@ export default function JobTotalsTableLabor({ job }) {
</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell>{t("jobs.labels.mapa")}</Table.Summary.Cell>
<Table.Summary.Cell>
<Space>
{t("jobs.labels.mapa")}
{job.materials &&
job.materials.mapa &&
job.materials.mapa.cal_maxdlr &&
job.materials.mapa.cal_maxdlr > 0 &&
t("jobs.labels.threshhold", {
amount: job.materials.mapa.cal_maxdlr,
})}
</Space>
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
<CurrencyFormatter>
{job.job_totals.rates.mapa.rate}
@@ -133,7 +144,18 @@ export default function JobTotalsTableLabor({ job }) {
</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell>{t("jobs.labels.mash")}</Table.Summary.Cell>
<Table.Summary.Cell>
<Space wrap>
{t("jobs.labels.mash")}
{job.materials &&
job.materials.mash &&
job.materials.mash.cal_maxdlr &&
job.materials.mash.cal_maxdlr > 0 &&
t("jobs.labels.threshhold", {
amount: job.materials.mash.cal_maxdlr,
})}
</Space>
</Table.Summary.Cell>
<Table.Summary.Cell align="right">
<CurrencyFormatter>
{job.job_totals.rates.mash.rate}

View File

@@ -58,6 +58,15 @@ export default function JobsAdminDatesChange({ job }) {
>
<FormDatePicker format="MM/DD/YYYY" />
</Form.Item>
<Form.Item label={t("jobs.fields.date_towin")} name="date_towin">
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.date_rentalresp")}
name="date_rentalresp"
>
<DateTimePicker />
</Form.Item>
<Form.Item label={t("jobs.fields.date_open")} name="date_open">
<DateTimePicker />
</Form.Item>

View File

@@ -37,7 +37,6 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
});
//Wahtever is left in the existing lines, are lines that should be removed.
const insertQueries = linesToInsert.reduce((acc, value, idx) => {
return acc + generateInsertQuery(value, idx, jobId);
}, "");
@@ -49,6 +48,13 @@ export const GetSupplementDelta = async (client, jobId, newLines) => {
const removeQueries = existingLines.reduce((acc, value, idx) => {
return acc + generateRemoveQuery(value, idx);
}, "");
console.log(insertQueries, updateQueries, removeQueries);
if ((insertQueries + updateQueries + removeQueries).trim() === "") {
return new Promise((resolve, reject) => {
resolve(null);
});
}
return new Promise((resolve, reject) => {
resolve(gql`

View File

@@ -220,12 +220,13 @@ export function JobsAvailableContainer({
);
delete supp.joblines;
await client.mutate({
mutation: gql`
${suppDelta}
`,
});
if (suppDelta !== null) {
await client.mutate({
mutation: gql`
${suppDelta}
`,
});
}
const updateResult = await updateJob({
variables: {
jobId: selectedJob,

View File

@@ -39,6 +39,12 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
<Form.Item label={t("jobs.fields.date_open")} name="date_open">
<DateTimePicker disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.date_towin")} name="date_towin">
<DateTimePicker disabled={jobRO} />
</Form.Item>
<Form.Item label={t("jobs.fields.date_rentalresp")} name="date_rentalresp">
<DateTimePicker disabled={jobRO} />
</Form.Item>
</FormRow>
<FormRow header={t("jobs.forms.scheddates")}>

View File

@@ -401,6 +401,9 @@ export function JobsDetailHeaderActions({
job: {
status: bodyshop.md_ro_statuses.default_void,
voided: true,
scheduled_in: null,
scheduled_completion: null,
inproduction: false,
},
note: [
{

View File

@@ -60,6 +60,13 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
);
}, [job.status, bodyshop.md_ro_statuses.post_production_statuses]);
const vehicleTitle = `${job.v_model_yr || ""} ${job.v_color || ""}
${job.v_make_desc || ""}
${job.v_model_desc || ""}`.trim();
console.log(
"🚀 ~ file: jobs-detail-header.component.jsx ~ line 64 ~ vehicleTitle",
vehicleTitle.length
);
return (
<Row gutter={[16, 16]} style={{ alignItems: "stretch" }}>
<Col {...colSpan}>
@@ -188,9 +195,9 @@ export function JobsDetailHeader({ job, bodyshop, disabled }) {
: job.vehicle && `/manage/vehicles/${job.vehicle.id}`
}
>
{`${job.v_model_yr || ""} ${job.v_color || ""}
${job.v_make_desc || ""}
${job.v_model_desc || ""}`}
{vehicleTitle.length > 0
? vehicleTitle
: t("vehicles.labels.novehinfo")}
</Link>
) : (
<span></span>

View File

@@ -115,7 +115,6 @@ export function JobNotesComponent({
<EditFilled />
</Button>
<PrintWrapperComponent
emailOnly
templateObject={{
name: Templates.individual_job_note.key,

View File

@@ -8,8 +8,11 @@ import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const ProdTemplates = TemplateList("production");
const { production_by_technician_one, production_by_category_one } =
TemplateList("special");
const {
production_by_technician_one,
production_by_category_one,
production_by_repair_status_one,
} = TemplateList("special");
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -95,6 +98,29 @@ export function ProductionListPrint({ bodyshop }) {
</Menu.Item>
))}
</Menu.SubMenu>
<Menu.SubMenu
title={t("reportcenter.templates.production_by_repair_status_one")}
>
{bodyshop.md_ro_statuses.production_statuses.map((e) => (
<Menu.Item
key={e}
onClick={async () => {
setLoading(true);
await GenerateDocument(
{
name: production_by_repair_status_one.key,
variables: { status: e },
},
{},
"p"
);
setLoading(false);
}}
>
{e}
</Menu.Item>
))}
</Menu.SubMenu>
</Menu>
}
>

View File

@@ -142,6 +142,18 @@ export default function ShopInfoGeneral({ form }) {
>
<Input />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.logo_img_header_margin")}
name={["logo_img_path", "headerMargin"]}
>
<InputNumber min={0} />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.logo_img_footer_margin")}
name={["logo_img_path", "footerMargin"]}
>
<InputNumber min={0} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.accountingsetup")}>
<Form.Item
@@ -495,6 +507,18 @@ export default function ShopInfoGeneral({ form }) {
>
<Switch />
</Form.Item>
<Form.Item
name={["md_from_emails"]}
label={t("bodyshop.fields.md_from_emails")}
// rules={[
// {
// //message: t("general.validation.required"),
// type: "array",
// },
// ]}
>
<Select mode="tags" />
</Form.Item>
<Form.Item
name={["md_email_cc", "parts_order"]}
label={t("bodyshop.fields.md_email_cc", { template: "parts_order" })}

View File

@@ -70,6 +70,12 @@ export default function ShopInfoSchedulingComponent({ form }) {
>
<Select mode="tags" />
</Form.Item>
<Form.Item
name={["ss_configuration", "dailyhrslimit"]}
label={t("bodyshop.fields.ss_configuration.dailyhrslimit")}
>
<InputNumber min={0} />
</Form.Item>
</LayoutFormRow>
<Divider orientation="left">{t("bodyshop.labels.workingdays")}</Divider>
<Space wrap size="large">

View File

@@ -101,6 +101,8 @@ export const QUERY_BODYSHOP = gql`
md_filehandlers
md_email_cc
timezone
ss_configuration
md_from_emails
employees {
user_email
id
@@ -199,6 +201,8 @@ export const UPDATE_SHOP = gql`
md_filehandlers
md_email_cc
timezone
ss_configuration
md_from_emails
employees {
id
first_name
@@ -266,7 +270,6 @@ export const QUERY_DELIVER_CHECKLIST = gql`
ro_number
actual_completion
actual_delivery
}
}
`;

View File

@@ -595,6 +595,7 @@ export const GET_JOB_BY_PK = gql`
ca_gst_registrant
ownerid
ded_note
materials
owner {
id
ownr_fn
@@ -649,6 +650,8 @@ export const GET_JOB_BY_PK = gql`
date_invoiced
date_last_contacted
date_next_contact
date_towin
date_rentalresp
date_exported
status
owner_owing
@@ -1097,6 +1100,15 @@ export const VOID_JOB = gql`
insert_notes(objects: $note) {
affected_rows
}
update_appointments(
where: { jobid: { _eq: $jobId } }
_set: { canceled: true }
) {
returning {
id
canceled
}
}
}
`;

View File

@@ -275,6 +275,8 @@
"mash": "Job Costing - Shop Materials Hourly Cost Rate"
},
"lastnumberworkingdays": "Scoreboard - Last Number of Working Days",
"logo_img_footer_margin": "Footer Margin (px)",
"logo_img_header_margin": "Header Margin (px)",
"logo_img_path": "Shop Logo",
"logo_img_path_height": "Logo Image Height",
"logo_img_path_width": "Logo Image Width",
@@ -283,6 +285,7 @@
"md_classes": "Classes",
"md_ded_notes": "Deductible Notes",
"md_email_cc": "Auto Email CC: $t(printcenter.subjects.jobs.{{template}})",
"md_from_emails": "Additional From Emails",
"md_hour_split": {
"paint": "Paint Hour Split",
"prep": "Prep Hour Split"
@@ -453,6 +456,9 @@
"label": "Label",
"templates": "Templates"
},
"ss_configuration": {
"dailyhrslimit": "Daily Incoming Hours Limit"
},
"ssbuckets": {
"gte": "Greater Than/Equal to (hrs)",
"id": "ID",
@@ -825,6 +831,7 @@
},
"fields": {
"cc": "CC",
"from": "From",
"subject": "Subject",
"to": "To"
},
@@ -1254,7 +1261,9 @@
"date_last_contacted": "Last Contacted Date",
"date_next_contact": "Next Contact Date",
"date_open": "Open",
"date_rentalresp": "Shop Rental Responsibility Start",
"date_scheduled": "Scheduled",
"date_towin": "Towed In",
"ded_amt": "Deductible",
"ded_note": "Deductible Note",
"ded_status": "Deductible Status",
@@ -1602,6 +1611,7 @@
"supplementnote": "The job had a supplement imported.",
"suspended": "SUSPENDED",
"suspense": "Suspense",
"threshhold": "Max Threshold: ${{amount}}",
"total_cost": "Total Cost",
"total_cust_payable": "Total Customer Amount Payable",
"total_repairs": "Total Repairs",
@@ -2068,6 +2078,7 @@
"labels": "Labels",
"position": "Starting Position"
},
"mechanical_authorization": "Mechanical Authorization",
"mpi_animal_checklist": "MPI - Animal Checklist",
"mpi_eglass_auth": "MPI - eGlass Auth",
"mpi_final_acct_sheet": "MPI - Final Accounting Sheet",
@@ -2280,6 +2291,7 @@
"production_by_csr": "Production by CSR",
"production_by_last_name": "Production by Last Name",
"production_by_repair_status": "Production by Status",
"production_by_repair_status_one": "Production filtered by Status",
"production_by_ro": "Production by RO",
"production_by_target_date": "Production by Target Date",
"production_by_technician": "Production by Technician",
@@ -2578,6 +2590,7 @@
},
"labels": {
"fromvehicle": "Historical Vehicle Record",
"novehinfo": "No Vehicle Information",
"relatedjobs": "Related Jobs",
"updatevehicle": "Update Vehicle Information"
},

View File

@@ -275,6 +275,8 @@
"mash": ""
},
"lastnumberworkingdays": "",
"logo_img_footer_margin": "",
"logo_img_header_margin": "",
"logo_img_path": "",
"logo_img_path_height": "",
"logo_img_path_width": "",
@@ -283,6 +285,7 @@
"md_classes": "",
"md_ded_notes": "",
"md_email_cc": "",
"md_from_emails": "",
"md_hour_split": {
"paint": "",
"prep": ""
@@ -453,6 +456,9 @@
"label": "",
"templates": ""
},
"ss_configuration": {
"dailyhrslimit": ""
},
"ssbuckets": {
"gte": "",
"id": "",
@@ -825,6 +831,7 @@
},
"fields": {
"cc": "",
"from": "",
"subject": "",
"to": ""
},
@@ -1254,7 +1261,9 @@
"date_last_contacted": "",
"date_next_contact": "",
"date_open": "Abierto",
"date_rentalresp": "",
"date_scheduled": "Programado",
"date_towin": "",
"ded_amt": "Deducible",
"ded_note": "",
"ded_status": "Estado deducible",
@@ -1602,6 +1611,7 @@
"supplementnote": "",
"suspended": "",
"suspense": "",
"threshhold": "",
"total_cost": "",
"total_cust_payable": "",
"total_repairs": "",
@@ -2068,6 +2078,7 @@
"labels": "",
"position": ""
},
"mechanical_authorization": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
@@ -2280,6 +2291,7 @@
"production_by_csr": "",
"production_by_last_name": "",
"production_by_repair_status": "",
"production_by_repair_status_one": "",
"production_by_ro": "",
"production_by_target_date": "",
"production_by_technician": "",
@@ -2578,6 +2590,7 @@
},
"labels": {
"fromvehicle": "",
"novehinfo": "",
"relatedjobs": "",
"updatevehicle": ""
},

View File

@@ -275,6 +275,8 @@
"mash": ""
},
"lastnumberworkingdays": "",
"logo_img_footer_margin": "",
"logo_img_header_margin": "",
"logo_img_path": "",
"logo_img_path_height": "",
"logo_img_path_width": "",
@@ -283,6 +285,7 @@
"md_classes": "",
"md_ded_notes": "",
"md_email_cc": "",
"md_from_emails": "",
"md_hour_split": {
"paint": "",
"prep": ""
@@ -453,6 +456,9 @@
"label": "",
"templates": ""
},
"ss_configuration": {
"dailyhrslimit": ""
},
"ssbuckets": {
"gte": "",
"id": "",
@@ -825,6 +831,7 @@
},
"fields": {
"cc": "",
"from": "",
"subject": "",
"to": ""
},
@@ -1254,7 +1261,9 @@
"date_last_contacted": "",
"date_next_contact": "",
"date_open": "Ouvrir",
"date_rentalresp": "",
"date_scheduled": "Prévu",
"date_towin": "",
"ded_amt": "Déductible",
"ded_note": "",
"ded_status": "Statut de franchise",
@@ -1602,6 +1611,7 @@
"supplementnote": "",
"suspended": "",
"suspense": "",
"threshhold": "",
"total_cost": "",
"total_cust_payable": "",
"total_repairs": "",
@@ -2068,6 +2078,7 @@
"labels": "",
"position": ""
},
"mechanical_authorization": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
@@ -2280,6 +2291,7 @@
"production_by_csr": "",
"production_by_last_name": "",
"production_by_repair_status": "",
"production_by_repair_status_one": "",
"production_by_ro": "",
"production_by_target_date": "",
"production_by_technician": "",
@@ -2578,6 +2590,7 @@
},
"labels": {
"fromvehicle": "",
"novehinfo": "",
"relatedjobs": "",
"updatevehicle": ""
},

View File

@@ -24,13 +24,34 @@ export default async function RenderTemplate(
let { contextData, useShopSpecificTemplate } = await fetchContextData(
templateObject
);
const { ignoreCustomMargins } = Templates[templateObject.name];
let reportRequest = {
template: {
name: useShopSpecificTemplate
? `/${bodyshop.imexshopid}/${templateObject.name}`
: `/${templateObject.name}`,
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
...(renderAsHtml
? {}
: {
recipe: "chrome-pdf",
...(!ignoreCustomMargins && {
chrome: {
marginTop:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.headerMargin &&
bodyshop.logo_img_path.headerMargin > 36
? bodyshop.logo_img_path.headerMargin
: "36px",
marginBottom:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.footerMargin &&
bodyshop.logo_img_path.footerMargin > 36
? bodyshop.logo_img_path.footerMargin
: "36px",
},
}),
}),
...(renderAsExcel ? { recipe: "html-to-xlsx" } : {}),
},
data: {
@@ -121,7 +142,25 @@ export async function RenderTemplates(
name: rootTemplate.useShopSpecificTemplate
? `/${bodyshop.imexshopid}/${rootTemplate.templateObject.name}`
: `/${rootTemplate.templateObject.name}`,
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
...(renderAsHtml
? {}
: {
recipe: "chrome-pdf",
chrome: {
marginTop:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.headerMargin &&
bodyshop.logo_img_path.headerMargin > 36
? bodyshop.logo_img_path.headerMargin
: "36px",
marginBottom:
bodyshop.logo_img_path &&
bodyshop.logo_img_path.footerMargin &&
bodyshop.logo_img_path.footerMargin > 36
? bodyshop.logo_img_path.footerMargin
: "36px",
},
}),
pdfOperations: templateAndData.map((template) => {
return {
template: {

View File

@@ -37,6 +37,14 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "authorization",
},
mechanical_authorization: {
title: i18n.t("printcenter.jobs.mechanical_authorization"),
description: "Diagnostic Authorization",
subject: i18n.t("printcenter.jobs.mechanical_authorization"),
key: "mechanical_authorization",
disabled: false,
group: "authorization",
},
appointment_reminder: {
title: i18n.t("printcenter.jobs.appointment_reminder"),
description: "All Jobs Notes",
@@ -363,6 +371,7 @@ export const TemplateList = (type, context) => {
subject: i18n.t("printcenter.jobs.parts_label_single"),
disabled: false,
group: "ro",
ignoreCustomMargins: true,
},
envelope_return_address: {
title: i18n.t("printcenter.jobs.envelope_return_address"),
@@ -371,6 +380,7 @@ export const TemplateList = (type, context) => {
key: "envelope_return_address",
disabled: false,
group: "ro",
ignoreCustomMargins: true,
},
sgi_certificate_of_repairs: {
title: i18n.t("printcenter.jobs.sgi_certificate_of_repairs"),
@@ -1664,6 +1674,18 @@ export const TemplateList = (type, context) => {
//idtype: "vendor",
disabled: false,
},
production_by_repair_status_one: {
title: i18n.t(
"reportcenter.templates.production_by_repair_status_one"
),
description: "",
subject: i18n.t(
"reportcenter.templates.production_by_repair_status_one"
),
key: "production_by_repair_status_one",
//idtype: "vendor",
disabled: false,
},
}
: {}),
};

View File

@@ -834,6 +834,7 @@
- md_email_cc
- md_estimators
- md_filehandlers
- md_from_emails
- md_hour_split
- md_ins_cos
- md_jobline_presets
@@ -860,6 +861,7 @@
- shopname
- shoprates
- speedprint
- ss_configuration
- ssbuckets
- state
- state_tax_id
@@ -915,6 +917,7 @@
- md_email_cc
- md_estimators
- md_filehandlers
- md_from_emails
- md_hour_split
- md_ins_cos
- md_jobline_presets
@@ -938,6 +941,7 @@
- shopname
- shoprates
- speedprint
- ss_configuration
- ssbuckets
- state
- state_tax_id
@@ -2721,7 +2725,9 @@
- date_last_contacted
- date_next_contact
- date_open
- date_rentalresp
- date_scheduled
- date_towin
- ded_amt
- ded_note
- ded_status
@@ -2804,6 +2810,7 @@
- loss_desc
- loss_of_use
- loss_type
- materials
- other_amount_payable
- owner_owing
- ownerid
@@ -2978,7 +2985,9 @@
- date_last_contacted
- date_next_contact
- date_open
- date_rentalresp
- date_scheduled
- date_towin
- ded_amt
- ded_note
- ded_status
@@ -3061,6 +3070,7 @@
- loss_desc
- loss_of_use
- loss_type
- materials
- other_amount_payable
- owner_owing
- ownerid
@@ -3245,7 +3255,9 @@
- date_last_contacted
- date_next_contact
- date_open
- date_rentalresp
- date_scheduled
- date_towin
- ded_amt
- ded_note
- ded_status
@@ -3328,6 +3340,7 @@
- loss_desc
- loss_of_use
- loss_type
- materials
- other_amount_payable
- owner_owing
- ownerid

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"."jobs" add column "materials" jsonb
-- not null default jsonb_build_object();

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "materials" jsonb
not null default jsonb_build_object();

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"."jobs" add column "towin_date" timestamptz
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "towin_date" timestamptz
null;

View File

@@ -0,0 +1 @@
alter table "public"."jobs" rename column "date_towin" to "towin_date";

View File

@@ -0,0 +1 @@
alter table "public"."jobs" rename column "towin_date" to "date_towin";

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"."jobs" add column "date_rentalresp" timestamptz
-- null;

View File

@@ -0,0 +1,2 @@
alter table "public"."jobs" add column "date_rentalresp" timestamptz
null;

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"."bodyshops" add column "ss_configuration" jsonb
-- null default jsonb_build_object();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "ss_configuration" jsonb
null default jsonb_build_object();

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"."bodyshops" add column "md_from_emails" jsonb
-- null default jsonb_build_array();

View File

@@ -0,0 +1,2 @@
alter table "public"."bodyshops" add column "md_from_emails" jsonb
null default jsonb_build_array();

View File

@@ -96,7 +96,7 @@ exports.default = async (req, res) => {
count: autoHouseObject.AutoHouseExport.RepairOrder.length,
xml: ret,
filename: `IM_${bodyshop.autohouseid}_${moment().format(
"DDMMYYYY_HHMMSS"
"DDMMYYYY_HHMMss"
)}.xml`,
});
@@ -328,8 +328,12 @@ const CreateRepairOrderTag = (job, errorCallback) => {
.format(AhDateFormat)) ||
"",
DeliveryAppointmentDate:
(job.scheduled_delivery &&
moment(job.scheduled_delivery)
// (job.scheduled_delivery &&
// moment(job.scheduled_delivery)
// .tz(job.bodyshop.timezone)
// .format(AhDateFormat)) ||
(job.scheduled_completion &&
moment(job.scheduled_completion)
.tz(job.bodyshop.timezone)
.format(AhDateFormat)) ||
"",
@@ -538,7 +542,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
),
GlassLaborTotalCost:
repairCosts.GlassLaborTotalCost.toFormat(AHDineroFormat),
DetailLaborTotal: Dinero(job.job_totals.rates.lag.total).toFormat(
DetailLaborTotal: Dinero(job.job_totals.rates.lad.total).toFormat(
AHDineroFormat
),
DetailLaborTotalCost:
@@ -572,6 +576,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
SalesTaxTotal: Dinero(job.job_totals.totals.local_tax)
.add(Dinero(job.job_totals.totals.state_tax))
.add(Dinero(job.job_totals.totals.federal_tax))
.add(Dinero(job.job_totals.additional.pvrt))
.toFormat(AHDineroFormat),
SalesTaxTotalCost: 0,
GrossTotal: Dinero(job.job_totals.totals.total_repairs).toFormat(
@@ -590,7 +595,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
AHDineroFormat
),
InsurancePay: Dinero(job.job_totals.totals.total_repairs)
.subtract(Dinero(job.job_totals.totals.custPayable))
.subtract(Dinero(job.job_totals.totals.custPayable.total))
.toFormat(AHDineroFormat),
Deposit: 0,
AmountDue: 0,
@@ -822,7 +827,9 @@ const GenerateDetailLines = (job, line, statuses) => {
)) ||
0,
//Critical: null,
Description: line.line_desc || "",
Description: line.line_desc
? line.line_desc.replace(/[^\x00-\x7F]/g, "")
: "",
DiscountMarkup: line.prt_dsmk_m || 0,
InvoiceNumber: line.billlines[0] && line.billlines[0].bill.invoice_number,
IOUPart: 0,

View File

@@ -464,6 +464,7 @@ exports.QUERY_UPCOMING_APPOINTMENTS = `query QUERY_UPCOMING_APPOINTMENTS($now: t
jobs_by_pk(id: $jobId) {
bodyshop {
ssbuckets
ss_configuration
target_touchtime
workingdays
timezone
@@ -960,6 +961,7 @@ exports.GET_JOB_BY_PK = ` query GET_JOB_BY_PK($id: uuid!) {
voided
ca_bc_pvrt
ca_customer_gst
materials
joblines(where: { removed: { _eq: false } }){
id
line_no

View File

@@ -234,11 +234,30 @@ function CalculateRatesTotals(ratesList) {
if (!ret[property].total) {
ret[property].total = Dinero();
}
ret[property].total = ret[property].total.add(
Dinero({
amount: Math.round((ret[property].rate || 0) * 100),
}).multiply(ret[property].hours)
);
let threshold;
//Check if there is a max for this type.
if (ratesList.materials && ratesList.materials[property]) {
//
if (
ratesList.materials[property].cal_maxdlr &&
ratesList.materials[property].cal_maxdlr > 0
) {
//It has an upper threshhold.
threshold = Dinero({
amount: Math.round(ratesList.materials[property].cal_maxdlr * 100),
});
}
}
const total = Dinero({
amount: Math.round((ret[property].rate || 0) * 100),
}).multiply(ret[property].hours);
if (threshold && total.greaterThanOrEqual(threshold)) {
ret[property].total = ret[property].total.add(threshold);
} else {
ret[property].total = ret[property].total.add(total);
}
}
subtotal = subtotal.add(ret[property].total);
@@ -252,6 +271,7 @@ function CalculateRatesTotals(ratesList) {
return ret;
}
function CalculatePartsTotals(jobLines) {
const ret = jobLines
.filter((jl) => !jl.removed)
@@ -418,21 +438,27 @@ function CalculateAdditional(job) {
pvrt: null,
total: null,
};
ret.towing = Dinero({
amount: Math.round((job.towing_payable || 0) * 100),
});
ret.additionalCosts = job.joblines
.filter((jl) => !jl.removed && IsAdditionalCost(jl))
.reduce((acc, val) => {
const lineValue = Dinero({
amount: Math.round((val.act_price || 0) * 100),
}).multiply(val.part_qty || 1);
ret.additionalCostItems.push({ key: val.line_desc, total: lineValue });
return acc.add(lineValue);
if (val.line_desc.toLowerCase().includes("towing")) {
ret.towing = lineValue;
return acc;
} else {
ret.additionalCostItems.push({ key: val.line_desc, total: lineValue });
return acc.add(lineValue);
}
}, Dinero());
ret.adjustments = Dinero({
amount: Math.round((job.adjustment_bottom_line || 0) * 100),
});
ret.towing = Dinero({
amount: Math.round((job.towing_payable || 0) * 100),
});
ret.storage = Dinero({
amount: Math.round((job.storage_payable || 0) * 100),
});

View File

@@ -5,6 +5,7 @@ const Dinero = require("dinero.js");
const moment = require("moment-timezone");
const logger = require("../utils/logger");
const _ = require("lodash");
const { filter } = require("lodash");
require("dotenv").config({
path: path.resolve(
process.cwd(),
@@ -32,7 +33,8 @@ exports.job = async (req, res) => {
});
const { jobs_by_pk, blockedDays, prodJobs, arrJobs, compJobs } = result;
const { ssbuckets, workingdays, timezone } = result.jobs_by_pk.bodyshop;
const { ssbuckets, workingdays, timezone, ss_configuration } =
result.jobs_by_pk.bodyshop;
const jobHrs = result.jobs_by_pk.jobhrs.aggregate.sum.mod_lb_hrs;
const JobBucket = ssbuckets.filter(
@@ -63,29 +65,68 @@ exports.job = async (req, res) => {
}
});
const filteredArrJobs = arrJobs.filter(
(j) => JobBucket.id === CheckJobBucket(ssbuckets, j)
);
// const filteredArrJobs = arrJobs.filter(
// (j) => JobBucket.id === CheckJobBucket(ssbuckets, j)
// );
const filteredArrJobs = [];
// filteredArrJobs.forEach((item) => {
// const itemDate = moment(item.scheduled_in)
// .tz(timezone)
// .format("yyyy-MM-DD");
// if (!!load[itemDate]) {
// load[itemDate].hoursIn =
// (load[itemDate].hoursIn || 0) +
// item.labhrs.aggregate.sum.mod_lb_hrs +
// item.larhrs.aggregate.sum.mod_lb_hrs;
// load[itemDate].jobsIn.push(item);
// } else {
// load[itemDate] = {
// jobsIn: [item],
// jobsOut: [],
// hoursIn:
// item.labhrs.aggregate.sum.mod_lb_hrs +
// item.larhrs.aggregate.sum.mod_lb_hrs,
// };
// }
// });
arrJobs.forEach((item) => {
let isSameBucket = false;
if (JobBucket.id === CheckJobBucket(ssbuckets, item)) {
filteredArrJobs.push(item);
isSameBucket = true;
}
let jobHours =
item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs;
filteredArrJobs.forEach((item) => {
const itemDate = moment(item.scheduled_in)
.tz(timezone)
.format("yyyy-MM-DD");
if (!!load[itemDate]) {
load[itemDate].hoursIn =
(load[itemDate].hoursIn || 0) +
item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs;
load[itemDate].jobsIn.push(item);
} else {
if (isSameBucket) {
if (!!load[itemDate]) {
load[itemDate].hoursIn = (load[itemDate].hoursIn || 0) + jobHours;
load[itemDate].jobsIn.push(item);
} else {
load[itemDate] = {
jobsIn: [item],
jobsOut: [],
hoursIn: jobHours,
};
}
}
if (!load[itemDate]) {
load[itemDate] = {
jobsIn: [item],
jobsIn: [],
jobsOut: [],
hoursIn:
item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs,
hoursIn: 0,
hoursInTotal: 0,
};
}
load[itemDate].hoursInTotal =
(load[itemDate].hoursInTotal || 0) + jobHours;
});
//Get the completing jobs.
@@ -214,11 +255,25 @@ exports.job = async (req, res) => {
(workingdays[dayOfWeekMapper(moment(loadKey).day())] || false) &&
!load[loadKey].blocked;
let isUnderDailyTotalLimit = true;
if (
ss_configuration &&
ss_configuration.dailyhrslimit &&
ss_configuration.dailyhrslimit > 0 &&
load[loadKey] &&
load[loadKey].hoursInTotal &&
load[loadKey].hoursInTotal > ss_configuration.dailyhrslimit
) {
isUnderDailyTotalLimit = false;
}
if (
load[loadKey].expectedLoad &&
load[loadKey].expectedLoad[JobBucket.id] &&
JobBucket.target > load[loadKey].expectedLoad[JobBucket.id].count &&
isShopOpen
isShopOpen &&
isUnderDailyTotalLimit
)
possibleDates.push(new Date(loadKey).toISOString().substr(0, 10));
});