Untitled
unknown
typescript
a year ago
16 kB
17
Indexable
import config from "@mercadoni/elementals/config";
import moment, { Moment } from "moment";
import jwt from "jsonwebtoken";
import { DynamicParameterValue } from "../../../entities/DynamicParameter";
import Job, { JobEventSource } from "../../../entities/Job";
import { getAbsoluteDifferenceInMinutes } from "../../../utils/Dates";
import InvariantViolation from "../../../utils/errors/InvariantViolation";
import { JobWithEvents } from "../../../interactors/InteractorResponseModel";
import Event, { EventType } from "../../../entities/Event";
import Task, { TaskType } from "../../../entities/Task";
import JobQuote from "../../../entities/JobQuote";
import { getEstimatedStepDurationInMinutes } from "../creation/CalculateEstimatedDurations";
import { OperationalModel, StartTimeByTask } from "../../../entities/Slot";
import { StepType } from "../../../entities/Step";
import { goBackToStorage } from "../tasks/GoBackToStorage";
import { setTaskOptimalsByJobAndStartOptimals } from "../creation/CalculateOptimals";
const MAXIMUM_SLOT_LENGTH_IN_MINUTES = 12 * 60;
// add this validations in the interactor
// const jwtScret = config.get("jwt_secret");
// const slotPayload = jwt.verify(slotId!, jwtScret) as SlotPayloadJwt;
// if (!slotPayload) {
// throw new InvariantViolation("INVALID_SLOT_ID_FOR_RESCHEDULE", { jobId: job.getId() });
// }
// here you are going to receive all the information
export const rescheduleJobV2 = async (
job: Job,
newFrom: Date,
newTo: Date,
jobQuote: JobQuote,
dynamicParameterValue: DynamicParameterValue,
backToStorage: boolean,
origin = JobEventSource.OPERATIONS,
reusablePackages = false,
reason?: string,
metadata?: Record<string, any>,
startTimeByTask?: StartTimeByTask,
): Promise<JobWithEvents> => {
validateDates(newFrom, newTo, job.getId());
const slotLength = validateSlotLength(newFrom, newTo, job.getId());
updateSlot(job, newFrom, newTo);
const event = buildRescheduleEvent(job, origin, reason, metadata);
const { rescheduleOffsetPercentage } = dynamicParameterValue;
const targetDate = moment(newFrom).add(Math.ceil((slotLength * rescheduleOffsetPercentage) / 100), "minutes");
if(startTimeByTask) {
setTaskOptimalsByJobAndStartOptimals(job, startTimeByTask);
// create this method in src/use_cases/jobs/creation/CalculateOptimals.ts
// export const setTaskOptimalsByJobAndStartOptimals = (job: Job, startTimeByTask: StartTimeByTask): void => {
// for (const task of job.getTasks()) {
// const taskType = task.getType();
// const departments = task.getDepartments();
// let currentTime: Date;
// const timeDetailsByTask: TimeDetailsByTask =
// startTimeByTask[getCapabilitiesInfoKey(taskType, departments) as TaskType]!;
// currentTime = moment.utc(timeDetailsByTask.startDate).toDate();
// task.getSteps().forEach((step, index) => {
// step.setOptimalStartTime(currentTime);
// const duration = timeDetailsByTask.stepsDuration[index];
// currentTime = moment.utc(currentTime).add(duration, "minutes").toDate();
// step.setOptimalEndTime(currentTime);
// });
// }
// }
}else{
// you have to revert the changes made in this function
await rescheduleJob(job, jobQuote, dynamicParameterValue, targetDate);
}
const resetDeliveryEvents: Event[] = [];
if (backToStorage) {
const { triggeredEvents } = goBackToStorage(job, reusablePackages);
resetDeliveryEvents.push(...triggeredEvents);
}
return {
associatedJob: job,
triggeredEvents: [event, ...resetDeliveryEvents],
};
};
const rescheduleJob = async (
job: Job,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
target: Moment
): Promise<void> => {
switch (job.getSlot().getOpsModel()) {
case OperationalModel.FULL_SERVICE:
await rescheduleFullserviceJob(job, jobQuote, parameterValue, target, startTimeByTask);
break;
case OperationalModel.PICK_AND_DELIVERY:
await reschedulePickAndDeliveryJob(job, jobQuote, parameterValue, target, startTimeByTask);
break;
case OperationalModel.PICK_AND_COLLECT:
await reschedulePickAndCollectJob(job, jobQuote, parameterValue, target, startTimeByTask);
break;
case OperationalModel.PICK_AND_COLLECT_NO_TRANSFER:
await reschedulePickAndCollectNoTransferJob(job, jobQuote, parameterValue, target, startTimeByTask);
break;
case OperationalModel.PICK_AND_DELIVERY_WITH_STORAGE:
await reschedulePickAndDeliveryWithStorageJob(job, jobQuote, parameterValue, target, startTimeByTask);
break;
case OperationalModel.PICK_AND_DELIVERY_WITH_STORAGE_NO_TRANSFER:
await reschedulePickAndDeliveryWithStorageNoTransferJob(job, jobQuote, parameterValue, target, startTimeByTask);
break;
case OperationalModel.ZONE_PICKING_AND_COLLECT:
await rescheduleZonePickingAndCollectJob(job, jobQuote, parameterValue, target, startTimeByTask);
break;
case OperationalModel.ZONE_PICKING_AND_DELIVERY_WITH_STORAGE:
await rescheduleZonePickingAndDeliveryWithStorageJob(job, jobQuote, parameterValue, target, startTimeByTask);
break;
default:
throw new InvariantViolation("OPERATIONAL_MODEL_NOT_SUPPORTED_FOR_RESCHEDULE", { jobId: job.getId() });
}
try {
job.getDeliveryTask().setRoute(null);
} catch (error) {
return;
}
};
const rescheduleFullserviceJob = async (
job: Job,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
target: Moment,
): Promise<void> => {
const fullServiceTask = job.getTaskByType(TaskType.FULL_SERVICE);
await rescheduleTask(fullServiceTask, jobQuote, parameterValue, target);
};
const reschedulePickAndDeliveryJob = async (
job: Job,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
target: Moment,
startTimeByTask?: StartTimeByTask
): Promise<void> => {
const deliveryTask = job.getTaskByType(TaskType.DELIVERY);
const pickingTask = job.getTaskByType(TaskType.PICKING);
if (startTimeByTask) {
job.getTasks().forEach((task) => {
rescheduleTaskWithSlotId(task, startTimeByTask);
})
} else {
await rescheduleTask(deliveryTask, jobQuote, parameterValue, target);
const transferStep = deliveryTask.getStepOfType(deliveryTask.getTransferStepType());
await rescheduleTask(pickingTask, jobQuote, parameterValue, moment.utc(transferStep.getOptimalEndTime()));
}
};
const reschedulePickAndCollectJob = async (
job: Job,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
target: Moment | null,
startTimeByTask?: StartTimeByTask
): Promise<void> => {
const pickUpTask = job.getTaskByType(TaskType.PICK_UP);
const storageTask = job.getTaskByType(TaskType.STORAGE);
const pickingWithStorageTask = job.getTaskByType(TaskType.PICKING_WITH_STORAGE);
if (startTimeByTask) {
rescheduleTaskWithSlotId(pickUpTask, startTimeByTask);
rescheduleTaskWithSlotId(storageTask, startTimeByTask);
rescheduleTaskWithSlotId(pickingWithStorageTask, startTimeByTask);
} else {
await rescheduleTask(pickUpTask, jobQuote, parameterValue, target!);
const deliveringStep = pickUpTask.getStepOfType(StepType.DELIVERING);
await rescheduleTask(storageTask, jobQuote, parameterValue, moment.utc(deliveringStep.getOptimalStartTime()));
const transferStep = storageTask.getStepOfType(storageTask.getTransferStepType());
await rescheduleTask(
pickingWithStorageTask,
jobQuote,
parameterValue,
moment.utc(transferStep.getOptimalEndTime())
);
}
};
const reschedulePickAndCollectNoTransferJob = async (
job: Job,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
target: Moment | null,
startTimeByTask?: StartTimeByTask
): Promise<void> => {
const pickUpTask = job.getTaskByType(TaskType.PICK_UP);
const pickAndStorageTask = job.getTaskByType(TaskType.PICKING_AND_STORAGE);
if (startTimeByTask) {
rescheduleTaskWithSlotId(pickUpTask, startTimeByTask);
rescheduleTaskWithSlotId(pickAndStorageTask, startTimeByTask);
} else {
await rescheduleTask(pickUpTask, jobQuote, parameterValue, target!);
const deliveringStep = pickUpTask.getStepOfType(StepType.DELIVERING);
await rescheduleTask(
pickAndStorageTask,
jobQuote,
parameterValue,
moment.utc(deliveringStep.getOptimalStartTime())
);
}
};
const reschedulePickAndDeliveryWithStorageJob = async (
job: Job,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
target: Moment | null,
startTimeByTask?: StartTimeByTask
): Promise<void> => {
const deliveryWithStorageTask = job.getTaskByType(TaskType.DELIVERY_WITH_STORAGE);
const pickUpForDeliveryTask = job.getTaskByType(TaskType.PICK_UP_FOR_DELIVERY);
const storageTask = job.getTaskByType(TaskType.STORAGE);
const pickingWithStorageTask = job.getTaskByType(TaskType.PICKING_WITH_STORAGE);
if (startTimeByTask) {
rescheduleTaskWithSlotId(deliveryWithStorageTask, startTimeByTask);
rescheduleTaskWithSlotId(pickUpForDeliveryTask, startTimeByTask);
rescheduleTaskWithSlotId(storageTask, startTimeByTask);
rescheduleTaskWithSlotId(pickingWithStorageTask, startTimeByTask);
} else {
await rescheduleTask(deliveryWithStorageTask, jobQuote, parameterValue, target!);
const secondTransferStep = deliveryWithStorageTask.getStepOfType(deliveryWithStorageTask.getTransferStepType());
await rescheduleTask(
pickUpForDeliveryTask,
jobQuote,
parameterValue,
moment.utc(secondTransferStep.getOptimalEndTime())
);
await rescheduleTask(storageTask, jobQuote, parameterValue, moment.utc(secondTransferStep.getOptimalStartTime()));
const firstTransferStep = storageTask.getStepOfType(storageTask.getTransferStepType());
await rescheduleTask(
pickingWithStorageTask,
jobQuote,
parameterValue,
moment.utc(firstTransferStep.getOptimalEndTime())
);
}
};
const reschedulePickAndDeliveryWithStorageNoTransferJob = async (
job: Job,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
target: Moment | null,
startTimeByTask?: StartTimeByTask
): Promise<void> => {
const deliveryWithStorageTask = job.getTaskByType(TaskType.DELIVERY_WITH_STORAGE);
const pickUpForDeliveryTask = job.getTaskByType(TaskType.PICK_UP_FOR_DELIVERY);
const pickingAndStorageTask = job.getTaskByType(TaskType.PICKING_AND_STORAGE);
if (startTimeByTask) {
rescheduleTaskWithSlotId(deliveryWithStorageTask, startTimeByTask);
rescheduleTaskWithSlotId(pickUpForDeliveryTask, startTimeByTask);
rescheduleTaskWithSlotId(pickingAndStorageTask, startTimeByTask);
} else {
await rescheduleTask(deliveryWithStorageTask, jobQuote, parameterValue, target!);
const transferStep = deliveryWithStorageTask.getStepOfType(deliveryWithStorageTask.getTransferStepType());
await rescheduleTask(pickUpForDeliveryTask, jobQuote, parameterValue, moment.utc(transferStep.getOptimalEndTime()));
await rescheduleTask(
pickingAndStorageTask,
jobQuote,
parameterValue,
moment.utc(transferStep.getOptimalStartTime())
);
}
};
const rescheduleZonePickingAndCollectJob = async (
job: Job,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
target: Moment | null,
startTimeByTask?: StartTimeByTask
): Promise<void> => {
const pickUpTask = job.getTaskByType(TaskType.PICK_UP);
const consolidationTask = job.getTaskByType(TaskType.CONSOLIDATION);
const zonePickingTasks = job.getTasksByType(TaskType.ZONE_PICKING);
if (startTimeByTask) {
rescheduleTaskWithSlotId(pickUpTask, startTimeByTask);
rescheduleTaskWithSlotId(consolidationTask, startTimeByTask);
zonePickingTasks.forEach((zonePickingTask) => {
return rescheduleTaskWithSlotId(zonePickingTask, startTimeByTask);
});
} else {
await rescheduleTask(pickUpTask, jobQuote, parameterValue, target!);
await rescheduleTask(consolidationTask, jobQuote, parameterValue, moment.utc(pickUpTask.getOptimalStartTime()));
await Promise.all(
zonePickingTasks.map(async (zonePickingTask: Task) => {
return rescheduleTask(
zonePickingTask,
jobQuote,
parameterValue,
moment.utc(consolidationTask.getOptimalStartTime())
);
})
);
}
};
const rescheduleZonePickingAndDeliveryWithStorageJob = async (
job: Job,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
target: Moment | null,
startTimeByTask?: StartTimeByTask
): Promise<void> => {
const deliveryWithStorageTask = job.getTaskByType(TaskType.DELIVERY_WITH_STORAGE);
const pickUpForDeliveryTask = job.getTaskByType(TaskType.PICK_UP_FOR_DELIVERY);
const consolidationTask = job.getTaskByType(TaskType.CONSOLIDATION);
const zonePickingTasks = job.getTasksByType(TaskType.ZONE_PICKING);
if (startTimeByTask) {
setTaskOptimalsByJobAndStartOptimals(job, startTimeByTask);
} else {
await rescheduleTask(deliveryWithStorageTask, jobQuote, parameterValue, target!);
const transferStep = deliveryWithStorageTask.getStepOfType(deliveryWithStorageTask.getTransferStepType());
await rescheduleTask(pickUpForDeliveryTask, jobQuote, parameterValue, moment.utc(transferStep.getOptimalEndTime()));
await rescheduleTask(
consolidationTask,
jobQuote,
parameterValue,
moment.utc(pickUpForDeliveryTask.getOptimalStartTime())
);
await Promise.all(
zonePickingTasks.map(async (zonePickingTask: Task) => {
return rescheduleTask(
zonePickingTask,
jobQuote,
parameterValue,
moment.utc(consolidationTask.getOptimalStartTime())
);
})
);
}
};
const rescheduleTask = async (
task: Task,
jobQuote: JobQuote,
parameterValue: DynamicParameterValue,
endTime: Moment
): Promise<void> => {
let optimalStartTime: Moment;
let optimalEndTime = moment.utc(endTime);
const steps = task.getSteps();
for (let i = steps.length - 1; i >= 0; i--) {
const step = steps[i];
const stepDurationInMinutes = await getEstimatedStepDurationInMinutes(
step.getType(),
jobQuote,
optimalEndTime,
parameterValue
);
optimalStartTime = moment.utc(optimalEndTime).subtract(stepDurationInMinutes, "minutes");
step.setOptimalStartTime(optimalStartTime.toDate());
step.setOptimalEndTime(optimalEndTime.toDate());
optimalEndTime = optimalStartTime;
}
};
const validateDates = (from: Date, to: Date, jobId: string): void => {
if (moment(to).isBefore(moment(from))) {
throw new InvariantViolation("INVALID_SLOT_FOR_RESCHEDULE", { jobId });
}
};
const validateSlotLength = (from: Date, to: Date, jobId: string): number => {
const slotLength = getAbsoluteDifferenceInMinutes(from, to);
if (slotLength > MAXIMUM_SLOT_LENGTH_IN_MINUTES) {
throw new InvariantViolation("INVALID_SLOT_LENGTH_FOR_RESCHEDULE", { jobId });
}
return slotLength;
};
const updateSlot = (job: Job, from: Date, to: Date): void => {
job.getSlot().setFrom(from);
job.getSlot().setTo(to);
};
const buildRescheduleEvent = (
job: Job,
origin: JobEventSource,
reason?: string,
metadata?: Record<string, any>
): Event => {
return new Event(job.getId(), EventType.RESCHEDULED, job.getClientId(), {
taskIds: job.getTasks().map((task) => {
return task.getId();
}),
origin,
reason,
metadata,
});
};
Editor is loading...
Leave a Comment