@fabriktor/fx 0.0.140 → 0.0.141
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/core/address.d.ts +2 -0
- package/dist/src/core/address.js +6 -0
- package/dist/src/core/measurement.d.ts +15 -0
- package/dist/src/core/measurement.js +45 -0
- package/dist/src/fx.js +3 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +3 -0
- package/dist/src/routes/availability.js +5 -4
- package/dist/src/routes/departure.d.ts +45 -0
- package/dist/src/routes/departure.js +98 -0
- package/dist/src/routes/planning.d.ts +1 -1
- package/dist/src/routes/planning.js +4 -11
- package/dist/src/routes/routes.d.ts +5 -1
- package/dist/src/routes/routes.js +22 -0
- package/dist/src/routes/workspace.js +2 -1
- package/package.json +3 -2
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { dateTimezone } from "./date.js";
|
|
2
|
+
// Always use the saved backend computation, never the device's timezone.
|
|
3
|
+
export function addressTimezone(address) {
|
|
4
|
+
const timezone = address.address_timezone.trim();
|
|
5
|
+
return timezone && dateTimezone(timezone) === timezone ? timezone : null;
|
|
6
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type DistanceUnit = "meter" | "kilometer" | "foot" | "yard" | "mile";
|
|
2
|
+
export type DurationUnit = "day" | "hour" | "minute" | "second";
|
|
3
|
+
export type Measurement<U extends string> = {
|
|
4
|
+
value: number;
|
|
5
|
+
unit: U;
|
|
6
|
+
};
|
|
7
|
+
type RoadUnits = {
|
|
8
|
+
large: DistanceUnit;
|
|
9
|
+
small: DistanceUnit;
|
|
10
|
+
threshold: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const roadDistanceUnits: Readonly<Record<string, RoadUnits>>;
|
|
13
|
+
export declare function roadDistance(meters: number, country: string): Measurement<DistanceUnit> | undefined;
|
|
14
|
+
export declare function durationParts(value: number, unit: DurationUnit): Measurement<DurationUnit>[];
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import convert from "convert";
|
|
2
|
+
// Road-distance preferences, not a country's system for every measurement.
|
|
3
|
+
// Thresholds/units follow Unicode CLDR's road usage; metric is the fallback.
|
|
4
|
+
// https://github.com/unicode-org/cldr/blob/main/common/supplemental/units.xml
|
|
5
|
+
const metricRoadUnits = {
|
|
6
|
+
large: "kilometer",
|
|
7
|
+
small: "meter",
|
|
8
|
+
threshold: 0.9,
|
|
9
|
+
};
|
|
10
|
+
export const roadDistanceUnits = {
|
|
11
|
+
US: { large: "mile", small: "foot", threshold: 0.5 },
|
|
12
|
+
GB: { large: "mile", small: "yard", threshold: 0.5 },
|
|
13
|
+
};
|
|
14
|
+
export function roadDistance(meters, country) {
|
|
15
|
+
if (!Number.isFinite(meters) || meters < 0)
|
|
16
|
+
return;
|
|
17
|
+
const units = roadDistanceUnits[country.trim().toUpperCase()] ?? metricRoadUnits;
|
|
18
|
+
const large = convert(meters, "meter").to(units.large);
|
|
19
|
+
return large >= units.threshold
|
|
20
|
+
? { value: large, unit: units.large }
|
|
21
|
+
: {
|
|
22
|
+
value: convert(meters, "meter").to(units.small),
|
|
23
|
+
unit: units.small,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const durationUnits = [
|
|
27
|
+
"day",
|
|
28
|
+
"hour",
|
|
29
|
+
"minute",
|
|
30
|
+
"second",
|
|
31
|
+
];
|
|
32
|
+
export function durationParts(value, unit) {
|
|
33
|
+
if (!Number.isFinite(value) || value < 0)
|
|
34
|
+
return [];
|
|
35
|
+
let seconds = Math.round(convert(value, unit).to("second"));
|
|
36
|
+
const parts = [];
|
|
37
|
+
for (const target of durationUnits) {
|
|
38
|
+
const amount = Math.floor(convert(seconds, "second").to(target));
|
|
39
|
+
if (!amount)
|
|
40
|
+
continue;
|
|
41
|
+
parts.push({ value: amount, unit: target });
|
|
42
|
+
seconds -= convert(amount, target).to("second");
|
|
43
|
+
}
|
|
44
|
+
return parts.length ? parts : [{ value: 0, unit: "second" }];
|
|
45
|
+
}
|
package/dist/src/fx.js
CHANGED
package/dist/src/index.d.ts
CHANGED
|
@@ -7,10 +7,12 @@ export * from "./chat/optimistic.js";
|
|
|
7
7
|
export * from "./chat/send-queue.js";
|
|
8
8
|
export * from "./contacts/contacts.js";
|
|
9
9
|
export * from "./contacts/scope.js";
|
|
10
|
+
export * from "./core/address.js";
|
|
10
11
|
export * from "./core/date.js";
|
|
11
12
|
export * from "./core/err.js";
|
|
12
13
|
export * from "./core/events.js";
|
|
13
14
|
export * from "./core/id.js";
|
|
15
|
+
export * from "./core/measurement.js";
|
|
14
16
|
export * from "./core/multi-create.js";
|
|
15
17
|
export * from "./core/pre-action.js";
|
|
16
18
|
export * from "./core/pull.js";
|
|
@@ -36,6 +38,7 @@ export * from "./privacy/privacy.js";
|
|
|
36
38
|
export * from "./processes/processes.js";
|
|
37
39
|
export * from "./routes/arrival.js";
|
|
38
40
|
export * from "./routes/availability.js";
|
|
41
|
+
export * from "./routes/departure.js";
|
|
39
42
|
export * from "./routes/planning.js";
|
|
40
43
|
export * from "./routes/punch.js";
|
|
41
44
|
export * from "./routes/routes.js";
|
package/dist/src/index.js
CHANGED
|
@@ -7,10 +7,12 @@ export * from "./chat/optimistic.js";
|
|
|
7
7
|
export * from "./chat/send-queue.js";
|
|
8
8
|
export * from "./contacts/contacts.js";
|
|
9
9
|
export * from "./contacts/scope.js";
|
|
10
|
+
export * from "./core/address.js";
|
|
10
11
|
export * from "./core/date.js";
|
|
11
12
|
export * from "./core/err.js";
|
|
12
13
|
export * from "./core/events.js";
|
|
13
14
|
export * from "./core/id.js";
|
|
15
|
+
export * from "./core/measurement.js";
|
|
14
16
|
export * from "./core/multi-create.js";
|
|
15
17
|
export * from "./core/pre-action.js";
|
|
16
18
|
export * from "./core/pull.js";
|
|
@@ -36,6 +38,7 @@ export * from "./privacy/privacy.js";
|
|
|
36
38
|
export * from "./processes/processes.js";
|
|
37
39
|
export * from "./routes/arrival.js";
|
|
38
40
|
export * from "./routes/availability.js";
|
|
41
|
+
export * from "./routes/departure.js";
|
|
39
42
|
export * from "./routes/planning.js";
|
|
40
43
|
export * from "./routes/punch.js";
|
|
41
44
|
export * from "./routes/routes.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { UserTypeClient, UserTypeEmployer, } from "@fabriktor/schema";
|
|
2
|
-
import {
|
|
2
|
+
import { addressTimezone } from "../core/address.js";
|
|
3
|
+
import { hoursPerDay, minutesOfDay, minutesPerHour, sameCalendarDay, } from "../core/date.js";
|
|
3
4
|
import { errValidation } from "../core/err.js";
|
|
4
5
|
import { normalizeIDs, resolvedID } from "../core/id.js";
|
|
5
6
|
import { newChatData } from "../chat/data.js";
|
|
@@ -11,7 +12,7 @@ export const RoutesChatDataType = {
|
|
|
11
12
|
AvailabilitySubmitted: "routes.availability.submitted",
|
|
12
13
|
};
|
|
13
14
|
const msgAvailabilityAccess = "This availability does not match the client and employer of the location.";
|
|
14
|
-
const msgAvailabilityTimezone = "The
|
|
15
|
+
const msgAvailabilityTimezone = "The location needs a valid saved address timezone.";
|
|
15
16
|
const msgAvailabilityHours = "The employer's working hours must form a valid same-day window.";
|
|
16
17
|
const msgAvailabilityWindows = "Provide enough distinct, non-overlapping availability windows within the employer's working hours.";
|
|
17
18
|
const msgAvailabilityData = "The availability request is invalid.";
|
|
@@ -82,8 +83,8 @@ export async function loadAvailabilityContext(readers, actor, reference, signal)
|
|
|
82
83
|
employer.id !== reference.employer_id ||
|
|
83
84
|
employer.account_identity !== UserTypeEmployer)
|
|
84
85
|
throw errValidation({ msg: msgAvailabilityAccess });
|
|
85
|
-
const timezone =
|
|
86
|
-
if (!timezone
|
|
86
|
+
const timezone = addressTimezone(location);
|
|
87
|
+
if (!timezone)
|
|
87
88
|
throw errValidation({ msg: msgAvailabilityTimezone });
|
|
88
89
|
const { work_day_start_hour: start, work_day_end_hour: end } = company;
|
|
89
90
|
if (![start, end].every((hour) => Number.isInteger(hour) && hour >= 0 && hour < hoursPerDay) ||
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { BillableOperator, JobsOperator, UsersOperator } from "@fabriktor/client";
|
|
2
|
+
import type { Address, InRoute, Location, Route, User, Warehouse } from "@fabriktor/schema";
|
|
3
|
+
export declare const RouteDepartureSource: {
|
|
4
|
+
readonly Company: "company";
|
|
5
|
+
readonly Warehouse: "warehouse";
|
|
6
|
+
readonly SavedRoute: "saved_route";
|
|
7
|
+
};
|
|
8
|
+
export type RouteDepartureReference = {
|
|
9
|
+
source: typeof RouteDepartureSource.Company;
|
|
10
|
+
} | {
|
|
11
|
+
source: typeof RouteDepartureSource.Warehouse;
|
|
12
|
+
warehouse_id: string;
|
|
13
|
+
} | {
|
|
14
|
+
source: typeof RouteDepartureSource.SavedRoute;
|
|
15
|
+
route_id: string;
|
|
16
|
+
};
|
|
17
|
+
export type RouteDeparture = {
|
|
18
|
+
reference: RouteDepartureReference;
|
|
19
|
+
address: Address;
|
|
20
|
+
name: string;
|
|
21
|
+
};
|
|
22
|
+
export type RoutePlanningReaders = {
|
|
23
|
+
users: Pick<UsersOperator, "findOneUser">;
|
|
24
|
+
billable: Pick<BillableOperator, "findOneWarehouse">;
|
|
25
|
+
jobs: Pick<JobsOperator, "findOneLocation">;
|
|
26
|
+
};
|
|
27
|
+
export declare const RoutePlanningBlock: {
|
|
28
|
+
readonly Departure: "route_departure_invalid";
|
|
29
|
+
readonly DepartureChanged: "route_departure_changed";
|
|
30
|
+
readonly DestinationTimezone: "route_destination_timezone";
|
|
31
|
+
readonly DestinationChanged: "route_destination_changed";
|
|
32
|
+
};
|
|
33
|
+
export declare function companyRouteDeparture(user: User): RouteDeparture;
|
|
34
|
+
export declare function warehouseRouteDeparture(warehouse: Warehouse, owner_id: string): RouteDeparture;
|
|
35
|
+
export declare function loadRouteDeparture(readers: RoutePlanningReaders, owner_id: string, reference: Exclude<RouteDepartureReference, {
|
|
36
|
+
source: typeof RouteDepartureSource.SavedRoute;
|
|
37
|
+
}>, signal?: AbortSignal): Promise<RouteDeparture>;
|
|
38
|
+
export declare function validateRoutePlanLocation(location: Location, owner_id: string, timezone: string): void;
|
|
39
|
+
export declare function validateRoutePlanAddresses(readers: RoutePlanningReaders, opts: {
|
|
40
|
+
owner_id: string;
|
|
41
|
+
input: InRoute;
|
|
42
|
+
departure: RouteDepartureReference;
|
|
43
|
+
current?: Route;
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { UserTypeEmployer } from "@fabriktor/schema";
|
|
2
|
+
import { addressTimezone } from "../core/address.js";
|
|
3
|
+
import { resolvedID } from "../core/id.js";
|
|
4
|
+
import { PreActionBlocked } from "../core/pre-action.js";
|
|
5
|
+
export const RouteDepartureSource = {
|
|
6
|
+
Company: "company",
|
|
7
|
+
Warehouse: "warehouse",
|
|
8
|
+
SavedRoute: "saved_route",
|
|
9
|
+
};
|
|
10
|
+
export const RoutePlanningBlock = {
|
|
11
|
+
Departure: "route_departure_invalid",
|
|
12
|
+
DepartureChanged: "route_departure_changed",
|
|
13
|
+
DestinationTimezone: "route_destination_timezone",
|
|
14
|
+
DestinationChanged: "route_destination_changed",
|
|
15
|
+
};
|
|
16
|
+
export function companyRouteDeparture(user) {
|
|
17
|
+
if (user.account_identity !== UserTypeEmployer ||
|
|
18
|
+
!user.place_id.trim() ||
|
|
19
|
+
!addressTimezone(user))
|
|
20
|
+
throw new PreActionBlocked(RoutePlanningBlock.Departure);
|
|
21
|
+
return {
|
|
22
|
+
reference: { source: RouteDepartureSource.Company },
|
|
23
|
+
address: user,
|
|
24
|
+
name: "",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function warehouseRouteDeparture(warehouse, owner_id) {
|
|
28
|
+
if (warehouse.owner_id !== owner_id ||
|
|
29
|
+
warehouse.is_archived ||
|
|
30
|
+
!warehouse.place_id.trim() ||
|
|
31
|
+
!addressTimezone(warehouse))
|
|
32
|
+
throw new PreActionBlocked(RoutePlanningBlock.Departure);
|
|
33
|
+
return {
|
|
34
|
+
reference: {
|
|
35
|
+
source: RouteDepartureSource.Warehouse,
|
|
36
|
+
warehouse_id: warehouse.id,
|
|
37
|
+
},
|
|
38
|
+
address: warehouse,
|
|
39
|
+
name: warehouse.name,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export async function loadRouteDeparture(readers, owner_id, reference, signal) {
|
|
43
|
+
if (reference.source === RouteDepartureSource.Warehouse) {
|
|
44
|
+
const warehouse = (await readers.billable.findOneWarehouse({
|
|
45
|
+
id: reference.warehouse_id,
|
|
46
|
+
signal,
|
|
47
|
+
})).value;
|
|
48
|
+
if (warehouse?.id === reference.warehouse_id)
|
|
49
|
+
return warehouseRouteDeparture(warehouse, owner_id);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const user = (await readers.users.findOneUser({ id: owner_id, signal }))
|
|
53
|
+
.value;
|
|
54
|
+
if (user?.id === owner_id)
|
|
55
|
+
return companyRouteDeparture(user);
|
|
56
|
+
}
|
|
57
|
+
throw new PreActionBlocked(RoutePlanningBlock.Departure);
|
|
58
|
+
}
|
|
59
|
+
export function validateRoutePlanLocation(location, owner_id, timezone) {
|
|
60
|
+
if (location.owner_id !== owner_id ||
|
|
61
|
+
location.is_archived ||
|
|
62
|
+
!location.place_id.trim() ||
|
|
63
|
+
!resolvedID(location.client_id))
|
|
64
|
+
throw new PreActionBlocked(RoutePlanningBlock.DestinationChanged);
|
|
65
|
+
if (addressTimezone(location) !== timezone)
|
|
66
|
+
throw new PreActionBlocked(RoutePlanningBlock.DestinationTimezone);
|
|
67
|
+
}
|
|
68
|
+
// Re-read saved operational addresses before optimization. A queued draft must
|
|
69
|
+
// not submit stale location ownership, Place IDs or address timezones.
|
|
70
|
+
export async function validateRoutePlanAddresses(readers, opts) {
|
|
71
|
+
const { input, departure, current, signal } = opts;
|
|
72
|
+
if (departure.source === RouteDepartureSource.SavedRoute) {
|
|
73
|
+
if (!current ||
|
|
74
|
+
current.id !== departure.route_id ||
|
|
75
|
+
current.owner_id !== opts.owner_id ||
|
|
76
|
+
current.start_place_id !== input.start_place_id ||
|
|
77
|
+
current.timezone !== input.timezone)
|
|
78
|
+
throw new PreActionBlocked(RoutePlanningBlock.DepartureChanged);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const saved = await loadRouteDeparture(readers, opts.owner_id, departure, signal);
|
|
82
|
+
if (saved.address.place_id !== input.start_place_id ||
|
|
83
|
+
addressTimezone(saved.address) !== input.timezone)
|
|
84
|
+
throw new PreActionBlocked(RoutePlanningBlock.DepartureChanged);
|
|
85
|
+
}
|
|
86
|
+
for (const destination of input.destinations) {
|
|
87
|
+
const location = (await readers.jobs.findOneLocation({
|
|
88
|
+
id: destination.location_id,
|
|
89
|
+
signal,
|
|
90
|
+
})).value;
|
|
91
|
+
if (!location ||
|
|
92
|
+
location.id !== destination.location_id ||
|
|
93
|
+
location.client_id !== destination.client_id ||
|
|
94
|
+
location.place_id !== destination.place_id)
|
|
95
|
+
throw new PreActionBlocked(RoutePlanningBlock.DestinationChanged);
|
|
96
|
+
validateRoutePlanLocation(location, opts.owner_id, input.timezone);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -23,7 +23,7 @@ export declare function newRoutePlan(opts: {
|
|
|
23
23
|
company: Pick<PrefCompany, "work_day_start_hour" | "work_day_end_hour">;
|
|
24
24
|
start_place_id: string;
|
|
25
25
|
}): InRoute;
|
|
26
|
-
export declare function routeDestination(location: Location, owner_id: string): InRoute["destinations"][number];
|
|
26
|
+
export declare function routeDestination(location: Location, owner_id: string, timezone: string): InRoute["destinations"][number];
|
|
27
27
|
export declare function validateRouteEmployees(ids: readonly string[], allowed: readonly string[], occupied?: readonly string[]): void;
|
|
28
28
|
export type RouteAvailabilityWindow = {
|
|
29
29
|
availability: ClientAvailability;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { isValidInputDate, minutesOfDay, sameCalendarDay, } from "../core/date.js";
|
|
2
2
|
import { errValidation } from "../core/err.js";
|
|
3
|
-
import { normalizeIDs
|
|
3
|
+
import { normalizeIDs } from "../core/id.js";
|
|
4
4
|
import { routeInput, validateRouteTimeWindow } from "./validation.js";
|
|
5
|
+
import { validateRoutePlanLocation } from "./departure.js";
|
|
5
6
|
import { PreActionBlocked } from "../core/pre-action.js";
|
|
6
7
|
const routePreparationBlocked = "route_preparation";
|
|
7
|
-
const msgPlanningLocation = "Select an active location with a client and a saved address.";
|
|
8
8
|
const msgPlanningEmployees = "Choose available employees who are not already in another vehicle.";
|
|
9
9
|
export const RouteMutationBlock = {
|
|
10
10
|
Busy: "route_mutation_busy",
|
|
@@ -46,15 +46,8 @@ export function newRoutePlan(opts) {
|
|
|
46
46
|
override_itinerary: routePlanningDefaults.override_itinerary,
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
-
export function routeDestination(location, owner_id) {
|
|
50
|
-
|
|
51
|
-
location.is_archived ||
|
|
52
|
-
!location.place_id.trim() ||
|
|
53
|
-
!resolvedID(location.client_id))
|
|
54
|
-
throw errValidation({
|
|
55
|
-
field: "destinations",
|
|
56
|
-
msg: msgPlanningLocation,
|
|
57
|
-
});
|
|
49
|
+
export function routeDestination(location, owner_id, timezone) {
|
|
50
|
+
validateRoutePlanLocation(location, owner_id, timezone);
|
|
58
51
|
return {
|
|
59
52
|
location_id: location.id,
|
|
60
53
|
client_id: location.client_id,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RouteDepartureReference, type RoutePlanningReaders } from "./departure.js";
|
|
1
2
|
import type { RoutesOperator } from "@fabriktor/client";
|
|
2
3
|
import type { ClientAvailability, InRoute, Route, RouteTeam, RPRoutesAvailability, Tag, Tracking } from "@fabriktor/schema";
|
|
3
4
|
import { RoutesWorkspace, type PrepareRouteOptions, type RoutesActor } from "./workspace.js";
|
|
@@ -21,6 +22,7 @@ export type CreateValidatedRouteResult = Awaited<ReturnType<RoutesOperator["inse
|
|
|
21
22
|
export type CreateRouteResult = CreateValidatedRouteResult;
|
|
22
23
|
export type CreatePlannedRouteOptions = Omit<PrepareRouteOptions, "request"> & {
|
|
23
24
|
attempt: MultiCreateAttempt;
|
|
25
|
+
departure: RouteDepartureReference;
|
|
24
26
|
};
|
|
25
27
|
export type ReplaceRouteOptions = RouteReplaceOptions & {
|
|
26
28
|
availability?: RPRoutesAvailability;
|
|
@@ -36,6 +38,7 @@ export type RouteMutationOptions = {
|
|
|
36
38
|
export type ReplacePlannedRouteOptions = RouteMutationOptions & {
|
|
37
39
|
in: InRoute;
|
|
38
40
|
days_ago: PrepareRouteOptions["days_ago"];
|
|
41
|
+
departure: RouteDepartureReference;
|
|
39
42
|
};
|
|
40
43
|
export type CreateTagOptions = TagInsertOptions & Pick<MultiCreateCheckpoint, "before_request" | "on_created">;
|
|
41
44
|
export type ReplaceTagOptions = TagReplaceOptions;
|
|
@@ -49,7 +52,7 @@ export type CreateRouteTeamOptions = RouteTeamInsertOptions & Pick<MultiCreateCh
|
|
|
49
52
|
export type ReplaceRouteTeamOptions = RouteTeamReplaceOptions;
|
|
50
53
|
export type SearchRouteTeamsOptions = OwnerSearchOptions;
|
|
51
54
|
export type SearchRouteTeamsPage = SearchPage<RouteTeam>;
|
|
52
|
-
export type RoutesFXOptions = {
|
|
55
|
+
export type RoutesFXOptions = RoutePlanningReaders & {
|
|
53
56
|
routes: RoutesOperator;
|
|
54
57
|
};
|
|
55
58
|
export interface RoutesFXOperator {
|
|
@@ -72,6 +75,7 @@ export interface RoutesFXOperator {
|
|
|
72
75
|
searchRouteTeams(opts: SearchRouteTeamsOptions): Promise<SearchRouteTeamsPage>;
|
|
73
76
|
}
|
|
74
77
|
export declare class RoutesFX implements RoutesFXOperator {
|
|
78
|
+
private readonly opts;
|
|
75
79
|
private routes;
|
|
76
80
|
private routeMutationPending;
|
|
77
81
|
readonly workspace: RoutesWorkspace;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
4
4
|
// not use this file except in compliance with the License. You may obtain
|
|
5
5
|
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
import { validateRoutePlanAddresses, } from "./departure.js";
|
|
6
7
|
import { errResolveFailed, errValidation } from "../core/err.js";
|
|
7
8
|
import { RoutesWorkspace, routeCanManage, routeCanPlan, } from "./workspace.js";
|
|
8
9
|
import { RoutePreparationBlocked, RouteMutationBlock, routePlanChanged, } from "./planning.js";
|
|
@@ -27,10 +28,12 @@ const msgTrackingMissing = "Tracking could not be resolved after the mutation.";
|
|
|
27
28
|
const msgCreatedRouteTeamIDMissing = "Created route team response did not contain an ID.";
|
|
28
29
|
const msgRouteTeamMissing = "Route team could not be resolved after the mutation.";
|
|
29
30
|
export class RoutesFX {
|
|
31
|
+
opts;
|
|
30
32
|
routes;
|
|
31
33
|
routeMutationPending = false;
|
|
32
34
|
workspace;
|
|
33
35
|
constructor(opts) {
|
|
36
|
+
this.opts = opts;
|
|
34
37
|
this.routes = opts.routes;
|
|
35
38
|
this.workspace = new RoutesWorkspace(opts.routes);
|
|
36
39
|
}
|
|
@@ -51,6 +54,18 @@ export class RoutesFX {
|
|
|
51
54
|
throw errValidation({ msg: msgRouteOwner });
|
|
52
55
|
return route;
|
|
53
56
|
}
|
|
57
|
+
try {
|
|
58
|
+
validateRouteInput(opts.in);
|
|
59
|
+
await validateRoutePlanAddresses(this.opts, {
|
|
60
|
+
owner_id: actor.owner_id,
|
|
61
|
+
input: opts.in,
|
|
62
|
+
departure: opts.departure,
|
|
63
|
+
signal: attempt.signal,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw new RoutePreparationBlocked(error);
|
|
68
|
+
}
|
|
54
69
|
const prepared = await this.workspace
|
|
55
70
|
.prepareRoute({
|
|
56
71
|
actor,
|
|
@@ -121,6 +136,13 @@ export class RoutesFX {
|
|
|
121
136
|
validateRouteInput(input);
|
|
122
137
|
if (!routePlanChanged(current, input))
|
|
123
138
|
return current;
|
|
139
|
+
await validateRoutePlanAddresses(this.opts, {
|
|
140
|
+
owner_id: opts.actor.owner_id,
|
|
141
|
+
input,
|
|
142
|
+
departure: opts.departure,
|
|
143
|
+
current,
|
|
144
|
+
signal: opts.signal,
|
|
145
|
+
});
|
|
124
146
|
const availability = await this.workspace.availability({
|
|
125
147
|
actor: opts.actor,
|
|
126
148
|
employee_ids: input.vehicles.flatMap((vehicle) => vehicle.employee_ids),
|
|
@@ -91,7 +91,8 @@ export class RoutesWorkspace {
|
|
|
91
91
|
const query = {
|
|
92
92
|
...queryParams({
|
|
93
93
|
owner_id,
|
|
94
|
-
|
|
94
|
+
// A day filter refers to the calendar day stored on each route.
|
|
95
|
+
// Employers may operate depots in different timezones.
|
|
95
96
|
utilized_employee_ids: actor.active_mode === UserTypeEmployee
|
|
96
97
|
? actor.owner_id
|
|
97
98
|
: optionalIDs(opts.employee_ids),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabriktor/fx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.141",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@fabriktor/client": "0.0.98",
|
|
21
21
|
"@fabriktor/fx": "0.0.94",
|
|
22
|
-
"@fabriktor/schema": "0.0.68"
|
|
22
|
+
"@fabriktor/schema": "0.0.68",
|
|
23
|
+
"convert": "8.0.2"
|
|
23
24
|
}
|
|
24
25
|
}
|