Added working days to scheduling IO-153

This commit is contained in:
Patrick Fic
2021-01-29 17:26:48 -08:00
parent e71a7bd17b
commit 10e70230ba
20 changed files with 638 additions and 20 deletions

View File

@@ -6184,6 +6184,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>workingdays</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>
@@ -10413,6 +10434,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>friday</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>hours</name>
<definition_loaded>false</definition_loaded>
@@ -10581,6 +10623,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>monday</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>na</name>
<definition_loaded>false</definition_loaded>
@@ -10770,6 +10833,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>saturday</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>search</name>
<definition_loaded>false</definition_loaded>
@@ -10854,6 +10938,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>sunday</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>text</name>
<definition_loaded>false</definition_loaded>
@@ -10875,6 +10980,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>thursday</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>totals</name>
<definition_loaded>false</definition_loaded>
@@ -10896,6 +11022,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>tuesday</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>unknown</name>
<definition_loaded>false</definition_loaded>
@@ -10959,6 +11106,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>wednesday</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>yes</name>
<definition_loaded>false</definition_loaded>

View File

@@ -184,6 +184,9 @@ export function JobsDetailHeaderActions({
{ defaultOpenStatus: bodyshop.md_ro_statuses.default_imported },
(newJobId) => {
history.push(`/manage/jobs/${newJobId}`);
notification["success"]({
message: t("jobs.successes.duplicated"),
});
}
)
}

View File

@@ -8,13 +8,14 @@ import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import {
selectScheduleLoad,
selectScheduleLoadCalculating
selectScheduleLoadCalculating,
} from "../../redux/application/application.selectors";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import ScheduleBlockDay from "../schedule-block-day/schedule-block-day.component";
import ScheduleCalendarHeaderGraph from "./schedule-calendar-header-graph.component";
import moment from "moment";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -124,10 +125,42 @@ export function ScheduleCalendarHeaderComponent({
</div>
) : null;
const isShopOpen = (date) => {
let day;
switch (moment(date).day()) {
case 0:
day = "sunday";
break;
case 1:
day = "monday";
break;
case 2:
day = "tuesday";
break;
case 3:
day = "wednesday";
break;
case 4:
day = "thursday";
break;
case 5:
day = "friday";
break;
case 6:
day = "saturday";
break;
default:
day = "sunday";
break;
}
return bodyshop.workingdays[day];
};
return (
<div className="imex-calendar-load">
<ScheduleBlockDay date={date} refetch={refetch}>
<div>
<div style={{ color: isShopOpen(date) ? "" : "tomato" }}>
{label}
{calculating ? <LoadingSkeleton /> : LoadComponent}
</div>

View File

@@ -31,7 +31,7 @@ export function ScheduleJobModalComponent({
const [loading, setLoading] = useState(false);
const [smartOptions, setSmartOptions] = useState([]);
const handleAuto = async () => {
const handleSmartScheduling = async () => {
setLoading(true);
try {
const response = await axios.post("/scheduling/job", {
@@ -92,7 +92,7 @@ export function ScheduleJobModalComponent({
</Form.Item>
</LayoutFormRow>
<Card title={t("appointments.labels.smartscheduling")}>
<Button onClick={handleAuto} loading={loading}>
<Button onClick={handleSmartScheduling} loading={loading}>
{t("appointments.actions.calculate")}
</Button>
{smartOptions.length > 0 && (

View File

@@ -193,6 +193,57 @@ export default function ShopInfoComponent({ form, saveLoading }) {
<InputNumber min={15} precision={0} />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow header={t("bodyshop.labels.workingdays")}>
<Form.Item
label={t("general.labels.sunday")}
name={["workingdays", "sunday"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("general.labels.monday")}
name={["workingdays", "monday"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("general.labels.tuesday")}
name={["workingdays", "tuesday"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("general.labels.wednesday")}
name={["workingdays", "wednesday"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("general.labels.thursday")}
name={["workingdays", "thursday"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("general.labels.friday")}
name={["workingdays", "friday"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("general.labels.saturday")}
name={["workingdays", "saturday"]}
valuePropName="checked"
>
<Switch />
</Form.Item>
</LayoutFormRow>
<LayoutFormRow>
<Form.Item
label={t("bodyshop.fields.dailypainttarget")}

View File

@@ -76,6 +76,7 @@ export const QUERY_BODYSHOP = gql`
schedule_end_time
imexshopid
default_adjustment_rate
workingdays
employees {
id
first_name
@@ -150,6 +151,7 @@ export const UPDATE_SHOP = gql`
schedule_end_time
imexshopid
default_adjustment_rate
workingdays
employees {
id
first_name

View File

@@ -385,7 +385,7 @@ export function Manage({ match, conflict }) {
<BackTop />
<div style={{ textAlign: "center", margin: "1rem 0rem" }}>
<div>
{`ImEX Online V.${process.env.NODE_ENV}-${process.env.REACT_APP_GIT_SHA}`}{" "}
{`ImEX Online V.${process.env.NODE_ENV}-${process.env.REACT_APP_GIT_SHA}`}
</div>
<Link to="/about" target="_blank" style={{ color: "#ccc" }}>
Disclaimer

View File

@@ -393,7 +393,8 @@
},
"scheduling": "SMART Scheduling",
"shopinfo": "Shop Information",
"speedprint": "Speed Print Configuration"
"speedprint": "Speed Print Configuration",
"workingdays": "Working Days"
},
"successes": {
"save": "Shop configuration saved successfully. "
@@ -687,6 +688,7 @@
"email": "Email",
"errors": "Errors",
"exceptiontitle": "An error has occurred.",
"friday": "Friday",
"hours": "hrs",
"in": "In",
"instanceconflictext": "Your $t(titles.app) account can only be used on one device at any given time. Refresh your session to take control.",
@@ -695,6 +697,7 @@
"loadingapp": "Loading $t(titles.app)",
"loadingshop": "Loading shop data...",
"loggingin": "Authorizing...",
"monday": "Monday",
"na": "N/A",
"no": "No",
"out": "Out",
@@ -704,15 +707,20 @@
"passwordsdonotmatch": "The passwords you have entered do not match.",
"print": "Print",
"required": "Required",
"saturday": "Saturday",
"search": "Search...",
"selectdate": "Select date...",
"sendagain": "Send Again",
"sendby": "Send By",
"sunday": "Sunday",
"text": "Text",
"thursday": "Thursday",
"totals": "Totals",
"tuesday": "Tuesday",
"unknown": "Unknown",
"username": "Username",
"view": "View",
"wednesday": "Wednesday",
"yes": "Yes"
},
"languages": {

View File

@@ -393,7 +393,8 @@
},
"scheduling": "",
"shopinfo": "",
"speedprint": ""
"speedprint": "",
"workingdays": ""
},
"successes": {
"save": ""
@@ -687,6 +688,7 @@
"email": "",
"errors": "",
"exceptiontitle": "",
"friday": "",
"hours": "",
"in": "en",
"instanceconflictext": "",
@@ -695,6 +697,7 @@
"loadingapp": "Cargando $t(titles.app)",
"loadingshop": "Cargando datos de la tienda ...",
"loggingin": "Iniciando sesión ...",
"monday": "",
"na": "N / A",
"no": "",
"out": "Afuera",
@@ -704,15 +707,20 @@
"passwordsdonotmatch": "",
"print": "",
"required": "",
"saturday": "",
"search": "Buscar...",
"selectdate": "",
"sendagain": "",
"sendby": "",
"sunday": "",
"text": "",
"thursday": "",
"totals": "",
"tuesday": "",
"unknown": "Desconocido",
"username": "",
"view": "",
"wednesday": "",
"yes": ""
},
"languages": {

View File

@@ -393,7 +393,8 @@
},
"scheduling": "",
"shopinfo": "",
"speedprint": ""
"speedprint": "",
"workingdays": ""
},
"successes": {
"save": ""
@@ -687,6 +688,7 @@
"email": "",
"errors": "",
"exceptiontitle": "",
"friday": "",
"hours": "",
"in": "dans",
"instanceconflictext": "",
@@ -695,6 +697,7 @@
"loadingapp": "Chargement de $t(titles.app)",
"loadingshop": "Chargement des données de la boutique ...",
"loggingin": "Vous connecter ...",
"monday": "",
"na": "N / A",
"no": "",
"out": "En dehors",
@@ -704,15 +707,20 @@
"passwordsdonotmatch": "",
"print": "",
"required": "",
"saturday": "",
"search": "Chercher...",
"selectdate": "",
"sendagain": "",
"sendby": "",
"sunday": "",
"text": "",
"thursday": "",
"totals": "",
"tuesday": "",
"unknown": "Inconnu",
"username": "",
"view": "",
"wednesday": "",
"yes": ""
},
"languages": {

View File

@@ -16,7 +16,7 @@ export const TemplateList = (type, context) => {
title: i18n.t("printcenter.jobs.estimate_detail"),
description: "Est Detail",
subject: `${i18n.t("printcenter.jobs.estimate_detail")} - ${
context.job.ro_number
context && context.job && context.job.ro_number
}`,
key: "estimate_detail",
disabled: false,
@@ -25,7 +25,7 @@ export const TemplateList = (type, context) => {
title: i18n.t("printcenter.jobs.casl_authorization"),
description: "CASL Authorization",
subject: `${i18n.t("printcenter.jobs.casl_authorization")} - ${
context.job.ro_number
context && context.job && context.job.ro_number
}`,
key: "casl_authorization",
disabled: false,
@@ -35,7 +35,7 @@ export const TemplateList = (type, context) => {
description: "Diagnostic Authorization",
subject: `${i18n.t(
"printcenter.jobs.diagnostic_authorization"
)} - ${context.job.ro_number}`,
)} - ${context && context.job && context.job.ro_number}`,
key: "diagnostic_authorization",
disabled: false,
},
@@ -43,7 +43,7 @@ export const TemplateList = (type, context) => {
title: i18n.t("printcenter.jobs.job_notes"),
description: "All Jobs Notes",
subject: `${i18n.t("printcenter.jobs.job_notes")} - ${
context.job.ro_number
context && context.job && context.job.ro_number
}`,
key: "job_notes",
disabled: false,
@@ -57,7 +57,7 @@ export const TemplateList = (type, context) => {
description: "Appointment Confirmation",
subject: `${i18n.t(
"printcenter.appointments.appointment_confirmation"
)} - ${context.job.ro_number}`,
)} - ${context && context.job && context.job.ro_number}`,
key: "appointment_confirmation",
disabled: false,
},
@@ -70,7 +70,14 @@ export const TemplateList = (type, context) => {
description: "Parts Order",
key: "parts_order_confirmation",
subject: `${bodyshop.shopname} Parts Order ${
(context && context.job && context.job.ro_number) || ""
(context &&
context &&
context.job &&
context.job &&
context &&
context.job &&
context.job.ro_number) ||
""
}`,
disabled: false,
},

View File

@@ -0,0 +1,5 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" DROP COLUMN "workingdays";
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
cascade: false
read_only: false
sql: ALTER TABLE "public"."bodyshops" ADD COLUMN "workingdays" jsonb NOT NULL
DEFAULT jsonb_build_object();
type: run_sql

View File

@@ -0,0 +1,76 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- federal_tax_id
- id
- imexshopid
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- logo_img_path
- md_categories
- md_classes
- md_ins_cos
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- messagingservicesid
- phone
- prodtargethrs
- production_config
- region_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- stripe_acct_id
- target_touchtime
- template_header
- textid
- updated_at
- zip_post
computed_fields: []
filter:
associations:
bodyshop:
associations:
user:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: bodyshops
schema: public
type: create_select_permission

View File

@@ -0,0 +1,77 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- federal_tax_id
- id
- imexshopid
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- logo_img_path
- md_categories
- md_classes
- md_ins_cos
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- messagingservicesid
- phone
- prodtargethrs
- production_config
- region_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- stripe_acct_id
- target_touchtime
- template_header
- textid
- updated_at
- workingdays
- zip_post
computed_fields: []
filter:
associations:
bodyshop:
associations:
user:
authid:
_eq: X-Hasura-User-Id
role: user
table:
name: bodyshops
schema: public
type: create_select_permission

View File

@@ -0,0 +1,69 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_update_permission
- args:
permission:
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- federal_tax_id
- id
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- logo_img_path
- md_categories
- md_classes
- md_ins_cos
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- phone
- prodtargethrs
- production_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- target_touchtime
- updated_at
- zip_post
filter:
associations:
bodyshop:
associations:
user:
authid:
_eq: X-Hasura-User-Id
set: {}
role: user
table:
name: bodyshops
schema: public
type: create_update_permission

View File

@@ -0,0 +1,70 @@
- args:
role: user
table:
name: bodyshops
schema: public
type: drop_update_permission
- args:
permission:
columns:
- accountingconfig
- address1
- address2
- appt_alt_transport
- appt_colors
- appt_length
- bill_tax_rates
- city
- country
- created_at
- default_adjustment_rate
- deliverchecklist
- email
- enforce_class
- federal_tax_id
- id
- inhousevendorid
- insurance_vendor_id
- intakechecklist
- logo_img_path
- md_categories
- md_classes
- md_ins_cos
- md_labor_rates
- md_messaging_presets
- md_notes_presets
- md_order_statuses
- md_parts_locations
- md_rbac
- md_referral_sources
- md_responsibility_centers
- md_ro_statuses
- phone
- prodtargethrs
- production_config
- schedule_end_time
- schedule_start_time
- scoreboard_target
- shopname
- shoprates
- speedprint
- ssbuckets
- state
- state_tax_id
- target_touchtime
- updated_at
- workingdays
- zip_post
filter:
associations:
bodyshop:
associations:
user:
authid:
_eq: X-Hasura-User-Id
set: {}
role: user
table:
name: bodyshops
schema: public
type: create_update_permission

View File

@@ -768,6 +768,7 @@ tables:
- template_header
- textid
- updated_at
- workingdays
- zip_post
filter:
associations:
@@ -826,6 +827,7 @@ tables:
- state_tax_id
- target_touchtime
- updated_at
- workingdays
- zip_post
filter:
associations:

View File

@@ -186,6 +186,7 @@ query QUERY_UPCOMING_APPOINTMENTS($now: timestamptz!, $jobId: uuid!) {
bodyshop {
ssbuckets
target_touchtime
workingdays
}
jobhrs: joblines_aggregate {
aggregate {

View File

@@ -31,7 +31,7 @@ exports.job = async (req, res) => {
});
const { appointments, jobs } = result;
const { ssbuckets } = result.jobs_by_pk.bodyshop;
const { ssbuckets, workingdays } = result.jobs_by_pk.bodyshop;
const jobHrs = result.jobs_by_pk.jobhrs.aggregate.sum.mod_lb_hrs;
const JobBucket = ssbuckets.filter(
@@ -40,14 +40,14 @@ exports.job = async (req, res) => {
)[0];
const bucketMatrix = {};
const yesterday = moment().subtract(1, "day");
//Get latest date + add 5 days to allow for back end adding..
const totalMatrixDays = moment
.max([
...appointments.map((a) => moment(a.start)),
...jobs
.map((p) => moment(p.scheduled_completion))
.filter((p) => p.isValid()),
.filter((p) => p.isValid() && p.isAfter(yesterday)),
])
.add("5", "days")
.diff(moment(), "days");
@@ -102,6 +102,7 @@ exports.job = async (req, res) => {
(bucket) =>
bucket.gte <= jobHrs && (!!bucket.lt ? bucket.lt > jobHrs : true)
)[0];
console.log(pjobBucket.id, JobBucket.id, pjobBucket.id === JobBucket.id);
if (pjobBucket.id === JobBucket.id) {
//Theyre the same classification. Add it to the matrix.
const compDate = moment(pjob.scheduled_completion);
@@ -112,7 +113,6 @@ exports.job = async (req, res) => {
? compDate.format("yyyy-MM-DD")
: todayIsoString
: todayIsoString;
console.log("bucketMatrix", bucketMatrix);
bucketMatrix[dateToUse] = {
...bucketMatrix[dateToUse],
out: (bucketMatrix[dateToUse].out || 0) + 1,
@@ -125,13 +125,37 @@ exports.job = async (req, res) => {
const possibleDates = [];
const bucketMatrixKeys = Object.keys(bucketMatrix);
bucketMatrixKeys.forEach((bmkey) => {
if (JobBucket.target > bucketMatrix[bmkey].in - bucketMatrix[bmkey].out)
const isShopOpen = workingdays[dayOfWeekMapper(moment(bmkey).day())];
if (
JobBucket.target > bucketMatrix[bmkey].in - bucketMatrix[bmkey].out &&
isShopOpen
)
possibleDates.push(new Date(bmkey).toISOString().substr(0, 10));
});
console.log("possibleDates", possibleDates, "bucketMatrix", bucketMatrix);
res.json(possibleDates);
} catch (error) {
console.log("error", error);
res.status(400).send(error);
}
};
const dayOfWeekMapper = (numberOfDay) => {
switch (numberOfDay) {
case 0:
return "sunday";
case 1:
return "monday";
case 2:
return "tuesday";
case 3:
return "wednesday";
case 4:
return "thursday";
case 5:
return "friday";
case 6:
return "saturday";
}
};