Merged in release/2022-12-09 (pull request #638)

Release/2022 12 09
This commit is contained in:
Patrick Fic
2022-12-10 01:32:06 +00:00
19 changed files with 270 additions and 23 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
@@ -736,6 +736,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>expectedprodhrs</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>history</name>
<definition_loaded>false</definition_loaded>
@@ -22285,6 +22306,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>date_repairstarted</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>
@@ -36477,6 +36519,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>courtesy_car_inventory</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>courtesy_car_terms</name>
<definition_loaded>false</definition_loaded>
@@ -38135,6 +38198,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>timetickets_ro</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>vehicle_check_in</name>
<definition_loaded>false</definition_loaded>

View File

@@ -1,5 +1,14 @@
import { SyncOutlined, WarningFilled } from "@ant-design/icons";
import { Button, Card, Input, Space, Table, Tooltip } from "antd";
import {
Button,
Card,
Dropdown,
Input,
Menu,
Space,
Table,
Tooltip,
} from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
@@ -7,6 +16,8 @@ import { DateTimeFormatter } from "../../utils/DateFormatter";
import { alphaSort } from "../../utils/sorters";
import { OwnerNameDisplayFunction } from "../owner-name-display/owner-name-display.component";
import moment from "moment";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
const [state, setState] = useState({
sortedInfo: {},
@@ -166,6 +177,32 @@ export default function CourtesyCarsList({ loading, courtesycars, refetch }) {
<Button onClick={() => refetch()}>
<SyncOutlined />
</Button>
<Dropdown
trigger="click"
overlay={
<Menu>
<Menu.Item
onClick={() =>
GenerateDocument(
{
name: TemplateList("courtesycar").courtesy_car_inventory
.key,
variables: {
//id: contract.id
},
},
{},
"p"
)
}
>
{t("printcenter.courtesycarcontract.courtesy_car_inventory")}
</Menu.Item>
</Menu>
}
>
<Button>{t("general.labels.print")}</Button>
</Dropdown>
<Link to={`/manage/courtesycars/new`}>
<Button>{t("courtesycars.actions.new")}</Button>
</Link>

View File

@@ -59,14 +59,18 @@ export default function JobDetailCardsDatesComponent({ loading, data }) {
<DateTimeFormatter>{data.scheduled_in}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.actual_in ? (
<Timeline.Item>
<label>{t("jobs.fields.actual_in")}: </label>
<DateTimeFormatter>{data.actual_in}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.date_repairstarted ? (
<Timeline.Item>
<label>{t("jobs.fields.date_repairstarted")}: </label>
<DateTimeFormatter>{data.date_repairstarted}</DateTimeFormatter>
</Timeline.Item>
) : null}
{data.scheduled_completion ? (
<Timeline.Item>
<label>{t("jobs.fields.scheduled_completion")}: </label>

View File

@@ -126,6 +126,9 @@ export function JobsAdminDatesChange({ insertAuditTrail, job }) {
<Form.Item label={t("jobs.fields.actual_in")} name="actual_in">
<DateTimePicker />
</Form.Item>
<Form.Item label={t("jobs.fields.date_repairstarted")} name="date_repairstarted">
<DateTimePicker />
</Form.Item>
<Form.Item
label={t("jobs.fields.date_last_contacted")}
name="date_last_contacted"

View File

@@ -42,7 +42,10 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
<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">
<Form.Item
label={t("jobs.fields.date_rentalresp")}
name="date_rentalresp"
>
<DateTimePicker disabled={jobRO} />
</Form.Item>
</FormRow>
@@ -73,6 +76,12 @@ export function JobsDetailDatesComponent({ jobRO, job, bodyshop }) {
title={t("jobs.labels.calc_repair_days")}
/>
</Tooltip>
<Form.Item
label={t("jobs.fields.date_repairstarted")}
name="date_repairstarted"
>
<DateTimePicker disabled={jobRO} />
</Form.Item>
</FormRow>
<FormRow header={t("jobs.forms.repairdates")}>
<Form.Item

View File

@@ -534,6 +534,7 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
<JobPartsQueueCount parts={record.joblines_status} record={record} />
),
},
//Added as a place holder for St Claude. Not implemented as it requires another join for a field used by only 1 client.
// {
// title: i18n.t("vehicles.fields.v_paint_codes", { number: "" }),
@@ -555,6 +556,29 @@ const r = ({ technician, state, activeStatuses, bodyshop }) => {
// </span>
// ) : null,
// },
{
title: i18n.t("jobs.fields.date_repairstarted"),
dataIndex: "date_repairstarted",
key: "date_repairstarted",
ellipsis: true,
sorter: (a, b) => dateSort(a.date_repairstarted, b.date_repairstarted),
sortOrder:
state.sortedInfo.columnKey === "date_repairstarted" &&
state.sortedInfo.order,
render: (text, record) => (
<ProductionListDate record={record} field="date_repairstarted" time />
),
},
{
title: i18n.t("jobs.fields.date_repairstarted") + " (HH:MM)",
dataIndex: "date_repairstarted_time",
key: "date_repairstarted_time",
ellipsis: true,
render: (text, record) => (
<TimeFormatter>{record.date_repairstarted}</TimeFormatter>
),
},
];
};
export default r;

View File

@@ -1,19 +1,17 @@
import { Popover } from "antd";
import { RadarChartOutlined } from "@ant-design/icons";
import { Popover, Space } from "antd";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import {
PolarAngleAxis,
Legend, PolarAngleAxis,
PolarGrid,
PolarRadiusAxis,
Radar,
Legend,
RadarChart,
Tooltip,
Radar, RadarChart,
Tooltip
} from "recharts";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { RadarChartOutlined } from "@ant-design/icons";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
});
@@ -23,7 +21,7 @@ const mapDispatchToProps = (dispatch) => ({
export function ScheduleCalendarHeaderGraph({ bodyshop, loadData }) {
const { ssbuckets } = bodyshop;
const { t } = useTranslation();
const data = useMemo(() => {
return (
(loadData &&
@@ -43,6 +41,10 @@ export function ScheduleCalendarHeaderGraph({ bodyshop, loadData }) {
const popContent = (
<div>
<Space>
{t("appointments.labels.expectedprodhrs")}
<strong>{loadData.expectedHours.toFixed(1)}</strong>
</Space>
<RadarChart
// cx={300}
// cy={250}

View File

@@ -142,6 +142,7 @@ export const QUERY_EXACT_JOB_IN_PRODUCTION = gql`
employee_refinish
employee_prep
employee_csr
date_repairstarted
joblines_status {
part_type
status
@@ -221,6 +222,7 @@ export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql`
employee_refinish
employee_prep
employee_csr
date_repairstarted
joblines_status {
part_type
status
@@ -302,6 +304,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql`
employee_prep
employee_csr
suspended
date_repairstarted
joblines_status {
part_type
status
@@ -672,6 +675,7 @@ export const GET_JOB_BY_PK = gql`
date_towin
date_rentalresp
date_exported
date_repairstarted
status
owner_owing
tax_registration_number
@@ -917,7 +921,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
date_next_contact
date_open
date_exported
date_repairstarted
date_scheduled
date_estimated
employee_body_rel {

View File

@@ -5,6 +5,7 @@ export const QUERY_SEARCH_OWNER_BY_IDX = gql`
search_owners(args: { search: $search }, limit: 100) {
ownr_fn
ownr_ln
ownr_co_nm
ownr_ph1
ownr_ph2
ownr_addr1

View File

@@ -35,6 +35,7 @@ export function* calculateScheduleLoad({ payload: end }) {
const load = {
productionTotal: {},
productionHours: 0,
};
//Set the current load.
@@ -45,6 +46,10 @@ export function* calculateScheduleLoad({ payload: end }) {
prodJobs.forEach((item) => {
//Add all of the jobs currently in production to the buckets so that we have a starting point.
const bucketId = CheckJobBucket(buckets, item);
load.productionHours =
load.productionHours +
item.labhrs.aggregate.sum.mod_lb_hrs +
item.larhrs.aggregate.sum.mod_lb_hrs;
if (bucketId) {
load.productionTotal[bucketId].count =
load.productionTotal[bucketId].count + 1;
@@ -131,6 +136,10 @@ export function* calculateScheduleLoad({ payload: end }) {
}
});
console.log(
"🚀 ~ file: application.sagas.js:160 ~ function*calculateScheduleLoad ~ load.productionTotal",
load.productionTotal
);
//Propagate the expected load to each day.
const range = Math.round(moment.duration(end.diff(today)).asDays());
for (var day = 0; day < range; day++) {
@@ -149,6 +158,10 @@ export function* calculateScheduleLoad({ payload: end }) {
load[current].jobsIn || [],
load[current].jobsOut || []
);
load[current].expectedHours =
load.productionHours +
(load[current].hoursIn || 0) -
(load[current].hoursOut || 0);
} else {
load[current].expectedLoad = CalculateLoad(
load[prev].expectedLoad,
@@ -156,6 +169,10 @@ export function* calculateScheduleLoad({ payload: end }) {
load[current].jobsIn || [],
load[current].jobsOut || []
);
load[current].expectedHours =
load[prev].expectedHours +
(load[current].hoursIn || 0) -
(load[current].hoursOut || 0);
}
}
yield put(setProblemJobs(problemJobs));

View File

@@ -50,6 +50,7 @@
"cancelledappointment": "Canceled appointment for: ",
"completingjobs": "Completing Jobs",
"dataconsistency": "{{ro_number}} has a data consistency issue. It has been excluded for scheduling purposes. CODE: {{code}}.",
"expectedprodhrs": "Expected Production Hours:",
"history": "History",
"inproduction": "Jobs In Production",
"manualevent": "Add Manual Appointment",
@@ -1351,6 +1352,7 @@
"date_next_contact": "Next Contact Date",
"date_open": "Open",
"date_rentalresp": "Shop Rental Responsibility Start",
"date_repairstarted": "Repairs Started",
"date_scheduled": "Scheduled",
"date_towin": "Towed In",
"ded_amt": "Deductible",
@@ -2166,6 +2168,7 @@
"courtesycarcontract": {
"courtesy_car_contract": "Courtesy Car Contract",
"courtesy_car_impound": "Impound Charges",
"courtesy_car_inventory": "Courtesy Car Inventory",
"courtesy_car_terms": "Courtesy Car Terms"
},
"errors": {
@@ -2252,6 +2255,7 @@
"supplement_request": "Supplement Request",
"thank_you_ro": "Thank You Letter",
"thirdpartypayer": "Third Party Payer",
"timetickets_ro": "Time Tickets",
"vehicle_check_in": "Vehicle Intake",
"vehicle_delivery_check": "Vehicle Delivery Checklist",
"window_tag": "Window Tag",

View File

@@ -50,6 +50,7 @@
"cancelledappointment": "Cita cancelada para:",
"completingjobs": "",
"dataconsistency": "",
"expectedprodhrs": "",
"history": "",
"inproduction": "",
"manualevent": "",
@@ -1351,6 +1352,7 @@
"date_next_contact": "",
"date_open": "Abierto",
"date_rentalresp": "",
"date_repairstarted": "",
"date_scheduled": "Programado",
"date_towin": "",
"ded_amt": "Deducible",
@@ -2166,6 +2168,7 @@
"courtesycarcontract": {
"courtesy_car_contract": "",
"courtesy_car_impound": "",
"courtesy_car_inventory": "",
"courtesy_car_terms": ""
},
"errors": {
@@ -2252,6 +2255,7 @@
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
"timetickets_ro": "",
"vehicle_check_in": "",
"vehicle_delivery_check": "",
"window_tag": "",

View File

@@ -50,6 +50,7 @@
"cancelledappointment": "Rendez-vous annulé pour:",
"completingjobs": "",
"dataconsistency": "",
"expectedprodhrs": "",
"history": "",
"inproduction": "",
"manualevent": "",
@@ -1351,6 +1352,7 @@
"date_next_contact": "",
"date_open": "Ouvrir",
"date_rentalresp": "",
"date_repairstarted": "",
"date_scheduled": "Prévu",
"date_towin": "",
"ded_amt": "Déductible",
@@ -2166,6 +2168,7 @@
"courtesycarcontract": {
"courtesy_car_contract": "",
"courtesy_car_impound": "",
"courtesy_car_inventory": "",
"courtesy_car_terms": ""
},
"errors": {
@@ -2252,6 +2255,7 @@
"supplement_request": "",
"thank_you_ro": "",
"thirdpartypayer": "",
"timetickets_ro": "",
"vehicle_check_in": "",
"vehicle_delivery_check": "",
"window_tag": "",

View File

@@ -488,6 +488,14 @@ export const TemplateList = (type, context) => {
disabled: false,
group: "pre",
},
timetickets_ro: {
title: i18n.t("printcenter.jobs.timetickets_ro"),
description: "CASL Authorization",
subject: i18n.t("printcenter.jobs.timetickets_ro"),
key: "timetickets_ro",
disabled: false,
group: "financial",
},
}
: {}),
...(!type || type === "job_special"
@@ -1731,6 +1739,21 @@ export const TemplateList = (type, context) => {
},
}
: {}),
...(!type || type === "courtesycar"
? {
courtesy_car_inventory: {
title: i18n.t(
"printcenter.courtesycarcontract.courtesy_car_inventory"
),
description: "Est Detail",
subject: i18n.t(
"printcenter.courtesycarcontract.courtesy_car_inventory"
),
key: "courtesy_car_inventory",
disabled: false,
},
}
: {}),
...(!type || type === "bill"
? {
inhouse_invoice: {

View File

@@ -3025,6 +3025,7 @@
- date_next_contact
- date_open
- date_rentalresp
- date_repairstarted
- date_scheduled
- date_towin
- ded_amt
@@ -3288,6 +3289,7 @@
- date_next_contact
- date_open
- date_rentalresp
- date_repairstarted
- date_scheduled
- date_towin
- ded_amt
@@ -3561,6 +3563,7 @@
- date_next_contact
- date_open
- date_rentalresp
- date_repairstarted
- date_scheduled
- date_towin
- ded_amt

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_repairstarted" timestamptz
-- null;

View File

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

View File

@@ -334,12 +334,17 @@ const CreateRepairOrderTag = (job, errorCallback) => {
.format(AhDateFormat)) ||
"",
InsInspDate: null,
StartDate:
(job.actual_in &&
moment(job.actual_in)
.tz(job.bodyshop.timezone)
.format(AhDateFormat)) ||
"",
StartDate: job.date_repairstarted
? (job.date_repairstarted &&
moment(job.date_repairstarted)
.tz(job.bodyshop.timezone)
.format(AhDateFormat)) ||
""
: (job.date_repairstarted &&
moment(job.actual_in)
.tz(job.bodyshop.timezone)
.format(AhDateFormat)) ||
"",
PartsOrder: null,
TeardownHold: null,
SupplementSubmittedDate: null,
@@ -496,7 +501,13 @@ const CreateRepairOrderTag = (job, errorCallback) => {
FrameHours: job.job_totals.rates.laf.hours.toFixed(2),
GlassHours: job.job_totals.rates.lag.hours.toFixed(2),
DetailHours: job.job_totals.rates.lad.hours.toFixed(2),
LaborMiscHours: 0,
LaborMiscHours: (
job.job_totals.rates.la1.hours +
job.job_totals.rates.la2.hours +
job.job_totals.rates.la3.hours +
job.job_totals.rates.la4.hours +
job.job_totals.rates.lau.hours
).toFixed(2),
PartsTotal: Dinero(job.job_totals.parts.parts.total).toFormat(
AHDineroFormat
@@ -579,7 +590,12 @@ const CreateRepairOrderTag = (job, errorCallback) => {
),
DetailLaborTotalCost:
repairCosts.DetailLaborTotalCost.toFormat(AHDineroFormat),
LaborMiscTotal: 0,
LaborMiscTotal: Dinero(job.job_totals.rates.la1.total)
.add(Dinero(job.job_totals.rates.la2.total))
.add(Dinero(job.job_totals.rates.la3.total))
.add(Dinero(job.job_totals.rates.la4.total))
.add(Dinero(job.job_totals.rates.lau.total))
.toFormat(AHDineroFormat),
LaborMiscTotalCost: 0,
MiscellaneousChargeTotal: 0,
MiscellaneousChargeTotalCost: 0,

View File

@@ -695,6 +695,7 @@ exports.AUTOHOUSE_QUERY = `query AUTOHOUSE_EXPORT($start: timestamptz, $bodyshop
driveable
parts_tax_rates
ded_amt
date_repairstarted
joblines(where: {removed: {_eq: false}}) {
id
line_no
@@ -1010,6 +1011,7 @@ vehicle{
date_scheduled
date_invoiced
date_exported
date_repairstarted
status
owner_owing
tax_registration_number