IO-2162 Updated smart schedule graph logic.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
<babeledit_project be_version="2.7.1" version="1.2">
|
<babeledit_project version="1.2" be_version="2.7.1">
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
BabelEdit project file
|
BabelEdit project file
|
||||||
|
|||||||
@@ -22,10 +22,6 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function ScheduleCalendarHeaderGraph({ bodyshop, loadData }) {
|
export function ScheduleCalendarHeaderGraph({ bodyshop, loadData }) {
|
||||||
console.log(
|
|
||||||
"🚀 ~ file: schedule-calendar-header-graph.component.js:23 ~ ScheduleCalendarHeaderGraph ~ loadData",
|
|
||||||
loadData
|
|
||||||
);
|
|
||||||
const { ssbuckets } = bodyshop;
|
const { ssbuckets } = bodyshop;
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const data = useMemo(() => {
|
const data = useMemo(() => {
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ export function ScheduleCalendarHeaderComponent({
|
|||||||
<div onClick={(e) => e.stopPropagation()}>
|
<div onClick={(e) => e.stopPropagation()}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{loadData && loadData.jobsOut ? (
|
{loadData && loadData.allJobsOut ? (
|
||||||
loadData.jobsOut.map((j) => (
|
loadData.allJobsOut.map((j) => (
|
||||||
<tr key={j.id}>
|
<tr key={j.id}>
|
||||||
<td>
|
<td>
|
||||||
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
||||||
@@ -102,11 +102,12 @@ export function ScheduleCalendarHeaderComponent({
|
|||||||
<div onClick={(e) => e.stopPropagation()}>
|
<div onClick={(e) => e.stopPropagation()}>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{loadData && loadData.jobsIn ? (
|
{loadData && loadData.allJobsIn ? (
|
||||||
loadData.jobsIn.map((j) => (
|
loadData.allJobsIn.map((j) => (
|
||||||
<tr key={j.id}>
|
<tr key={j.id}>
|
||||||
<td>
|
<td>
|
||||||
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
<Link to={`/manage/jobs/${j.id}`}>{j.ro_number}</Link>
|
||||||
|
{j.status}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<OwnerNameDisplay ownerObject={j} />
|
<OwnerNameDisplay ownerObject={j} />
|
||||||
@@ -142,7 +143,7 @@ export function ScheduleCalendarHeaderComponent({
|
|||||||
title={t("appointments.labels.arrivingjobs")}
|
title={t("appointments.labels.arrivingjobs")}
|
||||||
>
|
>
|
||||||
<Icon component={MdFileDownload} style={{ color: "green" }} />
|
<Icon component={MdFileDownload} style={{ color: "green" }} />
|
||||||
{(loadData.hoursIn || 0) && loadData.hoursIn.toFixed(2)}
|
{(loadData.allHoursIn || 0) && loadData.allHoursIn.toFixed(2)}
|
||||||
</Popover>
|
</Popover>
|
||||||
<Popover
|
<Popover
|
||||||
placement={"bottom"}
|
placement={"bottom"}
|
||||||
@@ -151,7 +152,7 @@ export function ScheduleCalendarHeaderComponent({
|
|||||||
title={t("appointments.labels.completingjobs")}
|
title={t("appointments.labels.completingjobs")}
|
||||||
>
|
>
|
||||||
<Icon component={MdFileUpload} style={{ color: "red" }} />
|
<Icon component={MdFileUpload} style={{ color: "red" }} />
|
||||||
{(loadData.hoursOut || 0) && loadData.hoursOut.toFixed(2)}
|
{(loadData.allHoursOut || 0) && loadData.allHoursOut.toFixed(2)}
|
||||||
</Popover>
|
</Popover>
|
||||||
<ScheduleCalendarHeaderGraph loadData={loadData} />
|
<ScheduleCalendarHeaderGraph loadData={loadData} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -294,6 +294,12 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
|||||||
where: { inproduction: { _eq: true }, suspended: { _eq: false } }
|
where: { inproduction: { _eq: true }, suspended: { _eq: false } }
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
|
actual_in
|
||||||
|
scheduled_in
|
||||||
|
actual_completion
|
||||||
|
scheduled_completion
|
||||||
|
inproduction
|
||||||
|
ro_number
|
||||||
labhrs: joblines_aggregate(
|
labhrs: joblines_aggregate(
|
||||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||||
) {
|
) {
|
||||||
@@ -327,12 +333,15 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
|
status
|
||||||
ro_number
|
ro_number
|
||||||
scheduled_completion
|
scheduled_completion
|
||||||
actual_completion
|
actual_completion
|
||||||
|
scheduled_in
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
|
inproduction
|
||||||
labhrs: joblines_aggregate(
|
labhrs: joblines_aggregate(
|
||||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||||
) {
|
) {
|
||||||
@@ -360,11 +369,16 @@ export const QUERY_SCHEDULE_LOAD_DATA = gql`
|
|||||||
) {
|
) {
|
||||||
id
|
id
|
||||||
scheduled_in
|
scheduled_in
|
||||||
|
actual_in
|
||||||
|
scheduled_completion
|
||||||
ro_number
|
ro_number
|
||||||
ownr_fn
|
ownr_fn
|
||||||
ownr_ln
|
ownr_ln
|
||||||
ownr_co_nm
|
ownr_co_nm
|
||||||
alt_transport
|
alt_transport
|
||||||
|
actual_completion
|
||||||
|
inproduction
|
||||||
|
status
|
||||||
labhrs: joblines_aggregate(
|
labhrs: joblines_aggregate(
|
||||||
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
where: { mod_lbr_ty: { _neq: "LAR" }, removed: { _eq: false } }
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
productionTotal: {},
|
productionTotal: {},
|
||||||
productionHours: 0,
|
productionHours: 0,
|
||||||
};
|
};
|
||||||
|
let problemJobs = [];
|
||||||
//Set the current load.
|
//Set the current load.
|
||||||
buckets.forEach((bucket) => {
|
buckets.forEach((bucket) => {
|
||||||
load.productionTotal[bucket.id] = { count: 0, label: bucket.label };
|
load.productionTotal[bucket.id] = { count: 0, label: bucket.label };
|
||||||
@@ -45,6 +45,32 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
|
|
||||||
prodJobs.forEach((item) => {
|
prodJobs.forEach((item) => {
|
||||||
//Add all of the jobs currently in production to the buckets so that we have a starting point.
|
//Add all of the jobs currently in production to the buckets so that we have a starting point.
|
||||||
|
if (
|
||||||
|
!item.actual_completion &&
|
||||||
|
moment(item.scheduled_completion).isBefore(moment().startOf("day"))
|
||||||
|
) {
|
||||||
|
problemJobs.push({
|
||||||
|
...item,
|
||||||
|
code: "Job was scheduled to go, but it has not been completed. Update the scheduled completion date to correct projections",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
item.actual_completion &&
|
||||||
|
moment(item.actual_completion).isBefore(moment().startOf("day"))
|
||||||
|
) {
|
||||||
|
problemJobs.push({
|
||||||
|
...item,
|
||||||
|
code: "Job is already marked as completed, but it is still in production. This job should be removed from production",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!(item.actual_completion || item.scheduled_completion)) {
|
||||||
|
problemJobs.push({
|
||||||
|
...item,
|
||||||
|
code: "Job does not have a scheduled or actual completion date. Update the scheduled or actual completion dates to correct projections",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const bucketId = CheckJobBucket(buckets, item);
|
const bucketId = CheckJobBucket(buckets, item);
|
||||||
load.productionHours =
|
load.productionHours =
|
||||||
load.productionHours +
|
load.productionHours +
|
||||||
@@ -59,77 +85,119 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
arrJobs.forEach((item) => {
|
arrJobs.forEach((item) => {
|
||||||
if (!item.scheduled_in)
|
if (!item.scheduled_in) {
|
||||||
console.log("JOB HAS NO SCHEDULED IN DATE.", item);
|
console.log("JOB HAS NO SCHEDULED IN DATE.", item);
|
||||||
const itemDate = moment(item.scheduled_in).format("yyyy-MM-DD");
|
problemJobs.push({
|
||||||
|
...item,
|
||||||
|
code: "Job has no scheduled in date",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
item.actual_completion &&
|
||||||
|
moment(item.actual_completion).isAfter(moment())
|
||||||
|
) {
|
||||||
|
problemJobs.push({
|
||||||
|
...item,
|
||||||
|
code: "Job has an actual completion date set in the future",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (item.actual_completion && item.inproduction) {
|
||||||
|
problemJobs.push({
|
||||||
|
...item,
|
||||||
|
code: "Job has an actual completion date but it is still marked in production",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemDate = moment(item.actual_in || item.scheduled_in).format(
|
||||||
|
"yyyy-MM-DD"
|
||||||
|
);
|
||||||
|
|
||||||
|
const AddJobForSchedulingCalc =
|
||||||
|
!item.inproduction &&
|
||||||
|
!moment(item.actual_completion || item.scheduled_completion).isSame(
|
||||||
|
moment(item.actual_in || item.scheduled_in),
|
||||||
|
"day"
|
||||||
|
);
|
||||||
|
|
||||||
if (!!load[itemDate]) {
|
if (!!load[itemDate]) {
|
||||||
load[itemDate].hoursIn =
|
load[itemDate].allHoursIn =
|
||||||
(load[itemDate].hoursIn || 0) +
|
(load[itemDate].allHoursIn || 0) +
|
||||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
load[itemDate].jobsIn.push(item);
|
|
||||||
|
//If the job hasn't already arrived, add it to the jobs in list.
|
||||||
|
// Make sure it also hasn't already been completed, or isn't an in and out job.
|
||||||
|
//This prevents the duplicate counting.
|
||||||
|
load[itemDate].allJobsIn.push(item);
|
||||||
|
if (AddJobForSchedulingCalc) {
|
||||||
|
load[itemDate].jobsIn.push(item);
|
||||||
|
load[itemDate].hoursIn =
|
||||||
|
(load[itemDate].hoursIn || 0) +
|
||||||
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
load[itemDate] = {
|
load[itemDate] = {
|
||||||
jobsIn: [item],
|
allJobsIn: [item],
|
||||||
|
jobsIn: AddJobForSchedulingCalc ? [item] : [], //Same as above, only add it if it isn't already in production.
|
||||||
jobsOut: [],
|
jobsOut: [],
|
||||||
hoursIn:
|
allJobsOut: [],
|
||||||
|
allHoursIn:
|
||||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
item.larhrs.aggregate.sum.mod_lb_hrs,
|
item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||||
|
hoursIn: AddJobForSchedulingCalc
|
||||||
|
? item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs
|
||||||
|
: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let problemJobs = [];
|
|
||||||
compJobs.forEach((item) => {
|
compJobs.forEach((item) => {
|
||||||
if (!(item.actual_completion || item.scheduled_completion))
|
if (!(item.actual_completion || item.scheduled_completion))
|
||||||
console.log("JOB HAS NO COMPLETION DATE.", item);
|
console.warn("JOB HAS NO COMPLETION DATE.", item);
|
||||||
|
|
||||||
const inProdJobs = prodJobs.find((p) => p.id === item.id);
|
const inProdJobs = prodJobs.find((p) => p.id === item.id);
|
||||||
const inArrJobs = arrJobs.find((p) => p.id === item.id);
|
const inArrJobs = arrJobs.find((p) => p.id === item.id);
|
||||||
|
|
||||||
if (!(inProdJobs || inArrJobs)) {
|
const AddJobForSchedulingCalc =
|
||||||
//Job isn't found in production or coming in.
|
item.inproduction &&
|
||||||
//is it going today or scheduled to go today?
|
!moment(item.actual_completion || item.scheduled_completion).isSame(
|
||||||
if (
|
moment(item.scheduled_in),
|
||||||
moment(item.actual_completion || item.scheduled_completion).isSame(
|
"day"
|
||||||
moment(),
|
) &&
|
||||||
"day"
|
(inProdJobs || inArrJobs);
|
||||||
)
|
|
||||||
) {
|
|
||||||
console.log("Job is going today anyways, ignore it.", item);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
moment(item.actual_completion || item.scheduled_completion).isBefore(
|
|
||||||
moment(),
|
|
||||||
"day"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
console.log("Job should have already gone. Ignoring it.", item);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
problemJobs.push({
|
|
||||||
...item,
|
|
||||||
code: "Job is scheduled for completion, but it is not marked in production nor is it an arriving job in this period. Check the scheduled in and completion dates",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const itemDate = moment(
|
const itemDate = moment(
|
||||||
item.actual_completion || item.scheduled_completion
|
item.actual_completion || item.scheduled_completion
|
||||||
).format("yyyy-MM-DD");
|
).format("yyyy-MM-DD");
|
||||||
|
//Skip it, it's already completed.
|
||||||
|
|
||||||
if (!!load[itemDate]) {
|
if (!!load[itemDate]) {
|
||||||
load[itemDate].hoursOut =
|
load[itemDate].allHoursOut =
|
||||||
(load[itemDate].hoursOut || 0) +
|
(load[itemDate].allHoursOut || 0) +
|
||||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
item.larhrs.aggregate.sum.mod_lb_hrs;
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
load[itemDate].jobsOut.push(item);
|
//Add only the jobs that are still in production to get rid of.
|
||||||
|
//If it's not in production, we'd subtract unnecessarily.
|
||||||
|
load[itemDate].allJobsOut.push(item);
|
||||||
|
|
||||||
|
if (AddJobForSchedulingCalc) {
|
||||||
|
load[itemDate].jobsOut.push(item);
|
||||||
|
load[itemDate].hoursOut =
|
||||||
|
(load[itemDate].hoursOut || 0) +
|
||||||
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
load[itemDate] = {
|
load[itemDate] = {
|
||||||
jobsOut: [item],
|
allJobsOut: [item],
|
||||||
hoursOut:
|
jobsOut: AddJobForSchedulingCalc ? [item] : [], //Same as above.
|
||||||
|
hoursOut: AddJobForSchedulingCalc
|
||||||
|
? item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
|
item.larhrs.aggregate.sum.mod_lb_hrs
|
||||||
|
: 0,
|
||||||
|
allHoursOut:
|
||||||
item.labhrs.aggregate.sum.mod_lb_hrs +
|
item.labhrs.aggregate.sum.mod_lb_hrs +
|
||||||
item.larhrs.aggregate.sum.mod_lb_hrs,
|
item.larhrs.aggregate.sum.mod_lb_hrs,
|
||||||
};
|
};
|
||||||
@@ -137,6 +205,7 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Propagate the expected load to each day.
|
//Propagate the expected load to each day.
|
||||||
|
|
||||||
const range = Math.round(moment.duration(end.diff(today)).asDays());
|
const range = Math.round(moment.duration(end.diff(today)).asDays());
|
||||||
for (var day = 0; day < range; day++) {
|
for (var day = 0; day < range; day++) {
|
||||||
const current = moment(today).add(day, "days").format("yyyy-MM-DD");
|
const current = moment(today).add(day, "days").format("yyyy-MM-DD");
|
||||||
@@ -146,6 +215,7 @@ export function* calculateScheduleLoad({ payload: end }) {
|
|||||||
if (!!!load[current]) {
|
if (!!!load[current]) {
|
||||||
load[current] = {};
|
load[current] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (day === 0) {
|
if (day === 0) {
|
||||||
//Starting on day 1. The load is current.
|
//Starting on day 1. The load is current.
|
||||||
load[current].expectedLoad = CalculateLoad(
|
load[current].expectedLoad = CalculateLoad(
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
"blocked": "Blocked",
|
"blocked": "Blocked",
|
||||||
"cancelledappointment": "Canceled appointment for: ",
|
"cancelledappointment": "Canceled appointment for: ",
|
||||||
"completingjobs": "Completing Jobs",
|
"completingjobs": "Completing Jobs",
|
||||||
"dataconsistency": "{{ro_number}} has a data consistency issue. It has been excluded for scheduling purposes. CODE: {{code}}.",
|
"dataconsistency": "{{ro_number}} has a data consistency issue. It may have been excluded for scheduling purposes. CODE: {{code}}.",
|
||||||
"expectedjobs": "Expected Jobs in Production: ",
|
"expectedjobs": "Expected Jobs in Production: ",
|
||||||
"expectedprodhrs": "Expected Production Hours:",
|
"expectedprodhrs": "Expected Production Hours:",
|
||||||
"history": "History",
|
"history": "History",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ let transporter = nodemailer.createTransport({
|
|||||||
});
|
});
|
||||||
|
|
||||||
exports.sendServerEmail = async function ({ subject, text }) {
|
exports.sendServerEmail = async function ({ subject, text }) {
|
||||||
|
if (process.env.NODE_ENV === undefined) return;
|
||||||
try {
|
try {
|
||||||
transporter.sendMail(
|
transporter.sendMail(
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user