Compare commits

...

46 Commits

Author SHA1 Message Date
Patrick Fic
75743f44e7 IO-1777 add check for paint materials threshold. 2022-03-18 15:18:48 -07:00
Patrick Fic
71f161ec27 IO-1774 Remove scheduled completion date on void. 2022-03-17 15:47:40 -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
12a5f17351 Add create user. 2022-03-04 10:11:18 -08:00
Patrick Fic
a2032553d9 IO-1746 Smart Scheduling Dates to 10. 2022-03-03 12:39:18 -08:00
Patrick Fic
d22979dadc IO-1748 Remove show phone on vendor search select in report cente.r 2022-03-03 08:52:25 -08:00
Patrick Fic
c1068ec92b IO-1728 Set cc create form to be vertical. 2022-03-03 08:37:21 -08: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
f8151e387e Resolve missing fields from time tickets query. 2022-03-02 16:04:49 -08:00
Patrick Fic
b98bfe566a IO-1760 2022-03-02 15:26:18 -08:00
Patrick Fic
c0220f0ca2 IO-1756 Include documents when sending job notes. 2022-03-02 14:36:19 -08:00
Patrick Fic
3e121a1a25 IO-1759 Replace offset with timezone. 2022-03-02 12:56:29 -08:00
Patrick Fic
a2a8868223 IO-1748 Add vendor # to parts order modal. 2022-03-02 11:18:10 -08:00
Patrick Fic
80d16b4651 IO-1747 Add CSR to Visual Board. 2022-03-02 10:54: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
Patrick Fic
4c1a333514 Autohouse Extract Updates & Remove duedate on payables. 2022-03-01 18:23:52 -08:00
Patrick Fic
0660b79c01 Autohouse Extract Updates & Remove duedate on payables. 2022-03-01 17:27:04 -08:00
Patrick Fic
63ec578b6a IO-1749 Inactive employees removed from drop downs. 2022-03-01 15:17:33 -08:00
Patrick Fic
dcf388ff7c IO-1745 Resolve scheduling showing incorrect days. 2022-03-01 14:53:42 -08:00
Patrick Fic
0cd1b41ed9 IO-1728 Remove unsaved changes popup when creating CC 2022-03-01 13:24:07 -08:00
Patrick Fic
79124daa9a IO-1719 Add invoice number to job line 2022-03-01 13:14:03 -08:00
Patrick Fic
76ec55d709 Merged in release/2022-02-18 (pull request #405)
Updated task scheduler.

Approved-by: Patrick Fic
2022-02-18 20:59:57 +00:00
Patrick Fic
51c5d163a5 Merged in hotfix/2022-02-14 (pull request #395)
Job costing additional fix.
2022-02-15 20:17:19 +00:00
Patrick Fic
a6f2cfba0f Merged in hotfix/2022-02-14 (pull request #393)
Revert "Potential resolution to date time issues post 4pm."
2022-02-15 17:13:36 +00:00
Patrick Fic
da51aeb135 Merged in hotfix/2022-02-14 (pull request #392)
Potential resolution to date time issues post 4pm.
2022-02-15 16:27:59 +00:00
Patrick Fic
1ba904d082 Merged in hotfix/2022-02-14 (pull request #389)
Resolve bill update error.
2022-02-14 21:13:48 +00:00
50 changed files with 603 additions and 116 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>
@@ -20581,6 +20623,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 +20665,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 +27455,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 +34857,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 +38553,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 +43500,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

@@ -9,7 +9,7 @@ import { DateFormatter } from "../../utils/DateFormatter";
import CourtesyCarFuelSlider from "../courtesy-car-fuel-select/courtesy-car-fuel-select.component";
import CourtesyCarStatus from "../courtesy-car-status-select/courtesy-car-status-select.component";
import FormDatePicker from "../form-date-picker/form-date-picker.component";
import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
//import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";
@@ -32,7 +32,7 @@ export default function CourtesyCarCreateFormComponent({ form, saveLoading }) {
}
/>
<FormFieldsChanged form={form} />
{/* <FormFieldsChanged form={form} /> */}
<LayoutFormRow header={t("courtesycars.labels.vehicle")}>
<Form.Item
label={t("courtesycars.fields.make")}

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

@@ -55,15 +55,17 @@ export function JobEmployeeAssignments({
0
}
>
{bodyshop.employees.map((emp) => (
<Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
{bodyshop.employees
.filter((emp) => emp.active)
.map((emp) => (
<Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
</Select>
</Col>
<Col span={24}>

View File

@@ -10,7 +10,7 @@ export default function JobLinesBillRefernece({ jobline }) {
{subletRequired && <WarningFilled />}
{`${(billLine.actual_price * billLine.quantity).toFixed(2)} (${
billLine.bill.vendor.name
})`}
} #${billLine.bill.invoice_number})`}
</div>
);
}

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,
@@ -124,7 +123,7 @@ export function JobNotesComponent({
messageObject={{
subject: Templates.individual_job_note.subject,
}}
id={record.id}
id={jobId}
/>
</Space>
),

View File

@@ -73,6 +73,7 @@ export function PartsOrderModalComponent({
options={vendorList}
disabled={isReturn}
preferredMake={preferredMake}
showPhone
/>
</Form.Item>
<Form.Item

View File

@@ -196,6 +196,11 @@ export function PartsOrderModalContainer({
(item) => item.id === values.vendorid
)[0];
let vendorEmails =
matchingVendor &&
matchingVendor.email &&
matchingVendor.email.split(RegExp("[;,]"));
GenerateDocument(
{
name: isReturn
@@ -206,7 +211,7 @@ export function PartsOrderModalContainer({
},
},
{
to: matchingVendor ? [matchingVendor.email] : null,
to: matchingVendor ? vendorEmails : null,
replyTo: bodyshop.email,
subject: isReturn
? Templates.parts_return_slip.subject

View File

@@ -22,7 +22,7 @@ export default function ProductionBoardCard(
) {
const { t } = useTranslation();
let employee_body, employee_prep, employee_refinish; //employee_csr;
let employee_body, employee_prep, employee_refinish, employee_csr;
if (card.employee_body) {
employee_body = bodyshop.employees.find((e) => e.id === card.employee_body);
}
@@ -34,6 +34,9 @@ export default function ProductionBoardCard(
(e) => e.id === card.employee_refinish
);
}
if (card.employee_csr) {
employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
}
// if (card.employee_csr) {
// employee_csr = bodyshop.employees.find((e) => e.id === card.employee_csr);
// }
@@ -131,11 +134,11 @@ export default function ProductionBoardCard(
)} ${employee_refinish.last_name.charAt(0)}`
: ""
} ${card.larhrs.aggregate.sum.mod_lb_hrs || "?"}h`}</Col>
{/* <Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>{`C: ${
employee_csr
? `${employee_csr.first_name} ${employee_csr.last_name}`
: ""
}`}</Col> */}
}`}</Col>
</Row>
</Col>
)}

View File

@@ -66,6 +66,7 @@ export const createBoardData = (AllStatuses, Jobs, filter) => {
include ||
j.employee_body === employeeId ||
j.employee_prep === employeeId ||
j.employee_csr === employeeId ||
j.employee_refinish === employeeId;
}
@@ -76,9 +77,8 @@ export const createBoardData = (AllStatuses, Jobs, filter) => {
Object.keys(DataGroupedByStatus).map((statusGroupKey) => {
try {
boardLanes.columns.find(
(l) => l.id === statusGroupKey
).cards = sortByParentId(DataGroupedByStatus[statusGroupKey]);
boardLanes.columns.find((l) => l.id === statusGroupKey).cards =
sortByParentId(DataGroupedByStatus[statusGroupKey]);
} catch (error) {
console.log("Error while creating board card", error);
}

View File

@@ -116,15 +116,17 @@ export function ProductionListEmpAssignment({
0
}
>
{bodyshop.employees.map((emp) => (
<Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
{bodyshop.employees
.filter((emp) => emp.active)
.map((emp) => (
<Select.Option
value={emp.id}
key={emp.id}
name={`${emp.first_name} ${emp.last_name}`}
>
{`${emp.first_name} ${emp.last_name}`}
</Select.Option>
))}
</Select>
</Col>
<Col span={24}>

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

View File

@@ -1,12 +1,13 @@
import { HeartOutlined } from "@ant-design/icons";
import { Select, Tag } from "antd";
import { Select, Space, Tag } from "antd";
import React, { forwardRef, useEffect, useState } from "react";
import PhoneNumberFormatter from "../../utils/PhoneFormatter";
const { Option } = Select;
//To be used as a form element only.
const VendorSearchSelect = (
{ value, onChange, options, onSelect, disabled, preferredMake },
{ value, onChange, options, onSelect, disabled, preferredMake, showPhone },
ref
) => {
const [option, setOption] = useState(value);
@@ -35,6 +36,7 @@ const VendorSearchSelect = (
style={{
width: "100%",
}}
dropdownMatchSelectWidth={false}
onChange={setOption}
optionFilterProp="name"
onSelect={onSelect}
@@ -50,10 +52,15 @@ const VendorSearchSelect = (
>
<div className="imex-flex-row">
<div style={{ flex: 1 }}>{o.name}</div>
<HeartOutlined />
{o.discount && o.discount !== 0 ? (
<Tag color="green">{`${o.discount * 100}%`}</Tag>
) : null}
<Space style={{ marginLeft: "1rem" }}>
<HeartOutlined style={{ color: "red" }} />
{o.phone && showPhone && (
<PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>
)}
{o.discount && o.discount !== 0 ? (
<Tag color="green">{`${o.discount * 100}%`}</Tag>
) : null}
</Space>
</div>
</Option>
))
@@ -64,9 +71,14 @@ const VendorSearchSelect = (
<div className="imex-flex-row" style={{ width: "100%" }}>
<div style={{ flex: 1 }}>{o.name}</div>
{o.discount && o.discount !== 0 ? (
<Tag color="green">{`${o.discount * 100}%`}</Tag>
) : null}
<Space style={{ marginLeft: "1rem" }}>
{o.phone && showPhone && (
<PhoneNumberFormatter>{o.phone}</PhoneNumberFormatter>
)}
{o.discount && o.discount !== 0 ? (
<Tag color="green">{`${o.discount * 100}%`}</Tag>
) : null}
</Space>
</div>
</Option>
))

View File

@@ -109,12 +109,14 @@ export default function VendorsFormComponent({
<Form.Item
label={t("vendors.fields.email")}
rules={[
{
type: "email",
message: t("general.validation.invalidemail"),
},
]}
rules={
[
// {
// type: "email",
// message: t("general.validation.invalidemail"),
// },
]
}
name="email"
>
<FormItemEmail email={getFieldValue("email")} />

View File

@@ -59,6 +59,8 @@ export const GET_LINE_TICKET_BY_PK = gql`
employeeid
memo
flat_rate
clockon
clockoff
employee {
id
first_name

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
@@ -696,6 +699,7 @@ export const GET_JOB_BY_PK = gql`
joblineid
bill {
id
invoice_number
vendor {
id
name
@@ -1096,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

@@ -87,6 +87,7 @@ export const QUERY_ALL_VENDORS_FOR_ORDER = gql`
discount
email
active
phone
}
jobs(where: { id: { _eq: $jobId } }) {
v_make_desc

View File

@@ -70,7 +70,12 @@ export function CourtesyCarCreateContainer({
return (
<RbacWrapper action="courtesycar:create">
<Form form={form} autoComplete="new-password" onFinish={handleFinish}>
<Form
form={form}
autoComplete="new-password"
onFinish={handleFinish}
layout="vertical"
>
<CourtesyCarFormComponent form={form} saveLoading={loading} />
</Form>
</RbacWrapper>

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",
@@ -1254,7 +1256,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 +1606,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 +2073,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 +2286,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 +2585,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": "",
@@ -1254,7 +1256,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 +1606,7 @@
"supplementnote": "",
"suspended": "",
"suspense": "",
"threshhold": "",
"total_cost": "",
"total_cust_payable": "",
"total_repairs": "",
@@ -2068,6 +2073,7 @@
"labels": "",
"position": ""
},
"mechanical_authorization": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
@@ -2280,6 +2286,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 +2585,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": "",
@@ -1254,7 +1256,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 +1606,7 @@
"supplementnote": "",
"suspended": "",
"suspense": "",
"threshhold": "",
"total_cost": "",
"total_cust_payable": "",
"total_repairs": "",
@@ -2068,6 +2073,7 @@
"labels": "",
"position": ""
},
"mechanical_authorization": "",
"mpi_animal_checklist": "",
"mpi_eglass_auth": "",
"mpi_final_acct_sheet": "",
@@ -2280,6 +2286,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 +2585,7 @@
},
"labels": {
"fromvehicle": "",
"novehinfo": "",
"relatedjobs": "",
"updatevehicle": ""
},

View File

@@ -39,7 +39,7 @@ export default async function RenderTemplate(
...templateObject.context,
headerpath: `/${bodyshop.imexshopid}/header.html`,
bodyshop: bodyshop,
offset: moment().utcOffset(),
offset: bodyshop.timezone, //moment().utcOffset(),
},
};

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",
@@ -1664,6 +1672,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

@@ -2721,7 +2721,9 @@
- date_last_contacted
- date_next_contact
- date_open
- date_rentalresp
- date_scheduled
- date_towin
- ded_amt
- ded_note
- ded_status
@@ -2804,6 +2806,7 @@
- loss_desc
- loss_of_use
- loss_type
- materials
- other_amount_payable
- owner_owing
- ownerid
@@ -2978,7 +2981,9 @@
- date_last_contacted
- date_next_contact
- date_open
- date_rentalresp
- date_scheduled
- date_towin
- ded_amt
- ded_note
- ded_status
@@ -3061,6 +3066,7 @@
- loss_desc
- loss_of_use
- loss_type
- materials
- other_amount_payable
- owner_owing
- ownerid
@@ -3245,7 +3251,9 @@
- date_last_contacted
- date_next_contact
- date_open
- date_rentalresp
- date_scheduled
- date_towin
- ded_amt
- ded_note
- ded_status
@@ -3328,6 +3336,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

@@ -149,6 +149,7 @@ app.post(
fb.unsubscribe
);
app.post("/adm/updateuser", fb.validateFirebaseIdToken, fb.updateUser);
app.post("/adm/createuser", fb.validateFirebaseIdToken, fb.createUser);
//Stripe Processing
var stripe = require("./server/stripe/payment");
@@ -180,7 +181,6 @@ app.post("/data/arms", data.arms);
var taskHandler = require("./server/tasks/tasks");
app.post("/taskHandler", taskHandler.taskHandler);
var ioevent = require("./server/ioevent/ioevent");
app.post("/ioevent", ioevent.default);
app.post("/newlog", (req, res) => {
@@ -188,7 +188,6 @@ app.post("/newlog", (req, res) => {
logger.log(message, type, user, record, object);
});
var cdkGetMake = require("./server/cdk/cdk-get-makes");
app.post("/cdk/getvehicles", fb.validateFirebaseIdToken, cdkGetMake.default);

View File

@@ -181,12 +181,13 @@ async function InsertBill(oauthClient, qbo_realmId, req, bill, vendor) {
TxnDate: moment(bill.date)
//.tz(bill.job.bodyshop.timezone)
.format("YYYY-MM-DD"),
...(bill.vendor.due_date && {
DueDate: moment(bill.date)
//.tz(bill.job.bodyshop.timezone)
.add(bill.vendor.due_date, "days")
.format("YYYY-MM-DD"),
}),
...(!bill.is_credit_memo &&
bill.vendor.due_date && {
DueDate: moment(bill.date)
//.tz(bill.job.bodyshop.timezone)
.add(bill.vendor.due_date, "days")
.format("YYYY-MM-DD"),
}),
DocNumber: bill.invoice_number,
//...(bill.job.class ? { ClassRef: { Id: classes[bill.job.class] } } : {}),

View File

@@ -75,12 +75,13 @@ const generateBill = (bill) => {
TxnDate: moment(bill.date)
//.tz(bill.job.bodyshop.timezone)
.format("YYYY-MM-DD"),
...(bill.vendor.due_date && {
DueDate: moment(bill.date)
// .tz(bill.job.bodyshop.timezone)
.add(bill.vendor.due_date, "days")
.format("YYYY-MM-DD"),
}),
...(!bill.is_credit_memo &&
bill.vendor.due_date && {
DueDate: moment(bill.date)
// .tz(bill.job.bodyshop.timezone)
.add(bill.vendor.due_date, "days")
.format("YYYY-MM-DD"),
}),
RefNumber: bill.invoice_number,
Memo: `RO ${bill.job.ro_number || ""}`,
ExpenseLineAdd: bill.billlines.map((il) =>

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`,
});
@@ -214,7 +214,7 @@ const CreateRepairOrderTag = (job, errorCallback) => {
Street: job.ownr_addr1 || "",
City: job.ownr_city || "",
State: job.ownr_st || "",
Zip: job.ownr_zip || "",
Zip: (job.ownr_zip && job.ownr_zip.substring(0, 3)) || "",
Phone1: job.ownr_ph1 || "",
Phone2: null,
Phone2Extension: null,
@@ -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)) ||
"",
@@ -488,8 +492,8 @@ const CreateRepairOrderTag = (job, errorCallback) => {
PartsReconditionedCost:
repairCosts.PartsReconditionedCost.toFormat(AHDineroFormat),
PartsRecycled: Dinero(
job.job_totals.parts.parts.list.PAR &&
job.job_totals.parts.parts.list.PAR.total
job.job_totals.parts.parts.list.PAL &&
job.job_totals.parts.parts.list.PAL.total
).toFormat(AHDineroFormat),
PartsRecycledCost:
repairCosts.PartsRecycledCost.toFormat(AHDineroFormat),
@@ -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:
@@ -555,7 +559,9 @@ const CreateRepairOrderTag = (job, errorCallback) => {
AHDineroFormat
),
BMTotalCost: repairCosts.BMTotalCost.toFormat(AHDineroFormat),
MiscTotal: 0,
MiscTotal: Dinero(job.job_totals.additional.additionalCosts).toFormat(
AHDineroFormat
),
MiscTotalCost: 0,
TowingTotal: Dinero(job.job_totals.additional.towing).toFormat(
AHDineroFormat
@@ -588,7 +594,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,
@@ -673,7 +679,7 @@ const CreateCosts = (job) => {
});
return bill_acc;
}, {});
const materialsHours = { mapaHrs: 0, mashHrs: 0 };
//If the hourly rates for job costing are set, add them in.
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mapa) {
if (
@@ -694,9 +700,32 @@ const CreateCosts = (job) => {
(job.bodyshop.jc_hourly_rates &&
job.bodyshop.jc_hourly_rates.mapa * 100) ||
0,
}).multiply(materialsHours.mapaHrs)
}).multiply(job.job_totals.rates.mapa.hours)
);
}
if (job.bodyshop.jc_hourly_rates && job.bodyshop.jc_hourly_rates.mash) {
if (
!billTotalsByCostCenters[
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
]
)
billTotalsByCostCenters[
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
] = Dinero();
billTotalsByCostCenters[
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
] = billTotalsByCostCenters[
job.bodyshop.md_responsibility_centers.defaults.costs.MASH
].add(
Dinero({
amount:
(job.bodyshop.jc_hourly_rates &&
job.bodyshop.jc_hourly_rates.mash * 100) ||
0,
}).multiply(job.job_totals.rates.mash.hours)
);
}
const ticketTotalsByCostCenter = job.timetickets.reduce(
(ticket_acc, ticket_val) => {
//At the invoice level.
@@ -797,7 +826,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

@@ -25,6 +25,43 @@ const adminEmail = [
"patrick@thinkimex.com",
];
exports.createUser = (req, res) => {
logger.log("admin-create-user", "WARN", req.user.email, null, {
request: req.body,
});
if (!adminEmail.includes(req.user.email)) {
logger.log(
"admin-create-user-unauthorized",
"ERROR",
req.user.email,
null,
{
request: req.body,
user: req.user,
}
);
res.sendStatus(404);
}
const { email, displayName, password } = req.body;
admin
.auth()
.createUser({ email, displayName, password })
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
logger.log("admin-update-user-success", "DEBUG", req.user.email, null, {
userRecord,
});
res.json(userRecord);
})
.catch((error) => {
logger.log("admin-update-user-error", "ERROR", req.user.email, null, {
error,
});
res.status(500).json(error);
});
};
exports.updateUser = (req, res) => {
logger.log("admin-update-user", "WARN", req.user.email, null, {
request: req.body,

View File

@@ -960,6 +960,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

@@ -111,10 +111,9 @@ exports.job = async (req, res) => {
}
if (
moment(item.actual_completion || item.scheduled_completion).tz(timezone).isBefore(
moment().tz(timezone),
"day"
)
moment(item.actual_completion || item.scheduled_completion)
.tz(timezone)
.isBefore(moment().tz(timezone), "day")
) {
console.log("Job should have already gone. Ignoring it.", item);
return;
@@ -128,7 +127,9 @@ exports.job = async (req, res) => {
} else {
const itemDate = moment(
item.actual_completion || item.scheduled_completion
).tz(timezone).format("yyyy-MM-DD");
)
.tz(timezone)
.format("yyyy-MM-DD");
if (!!load[itemDate]) {
load[itemDate].hoursOut =
(load[itemDate].hoursOut || 0) +
@@ -153,14 +154,20 @@ exports.job = async (req, res) => {
const end = moment.max([
...filteredArrJobs.map((a) => moment(a.scheduled_in).tz(timezone)),
...filteredCompJobs
.map((p) => moment(p.actual_completion || p.scheduled_completion).tz(timezone))
.map((p) =>
moment(p.actual_completion || p.scheduled_completion).tz(timezone)
)
.filter((p) => p.isValid() && p.isAfter(yesterday)),
moment().tz(timezone).add(15, "days"),
]);
const range = Math.round(moment.duration(end.diff(today)).asDays());
for (var day = 0; day < range; day++) {
const current = moment(today).tz(timezone).add(day, "days").format("yyyy-MM-DD");
const prev = moment(today).tz(timezone)
const current = moment(today)
.tz(timezone)
.add(day, "days")
.format("yyyy-MM-DD");
const prev = moment(today)
.tz(timezone)
.add(day - 1, "days")
.format("yyyy-MM-DD");
if (!!!load[current]) {
@@ -194,7 +201,7 @@ exports.job = async (req, res) => {
load[startIsoFormat] = { blocked: true };
}
});
// //Propose the first 5 dates where we are below target.
// //Propose the first 10 dates where we are below target.
const possibleDates = [];
delete load.productionTotal;
@@ -204,7 +211,7 @@ exports.job = async (req, res) => {
loadKeys.forEach((loadKey) => {
const isShopOpen =
(workingdays[dayOfWeekMapper(moment(loadKey).tz(timezone).day())] || false) &&
(workingdays[dayOfWeekMapper(moment(loadKey).day())] || false) &&
!load[loadKey].blocked;
if (
@@ -216,10 +223,10 @@ exports.job = async (req, res) => {
possibleDates.push(new Date(loadKey).toISOString().substr(0, 10));
});
if (possibleDates.length < 6) {
if (possibleDates.length < 11) {
res.json(possibleDates);
} else {
res.json(possibleDates.slice(0, 5));
res.json(possibleDates.slice(0, 10));
}
} catch (error) {
logger.log("smart-scheduling-error", "ERROR", req.user.email, jobId, {