@lilaquadrat/studio 12.0.0 → 12.2.0

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.
Files changed (140) hide show
  1. package/lib/models.d.ts +8 -1
  2. package/lib/models.js +8 -1
  3. package/lib/models.js.map +1 -1
  4. package/lib/services.d.ts +8 -1
  5. package/lib/services.js +8 -1
  6. package/lib/services.js.map +1 -1
  7. package/lib/src/Immutable.class.js +10 -4
  8. package/lib/src/Immutable.class.js.map +1 -1
  9. package/lib/src/aggegations/structures.aggregation.d.ts +6 -8
  10. package/lib/src/aggegations/structures.aggregation.js +3 -2
  11. package/lib/src/aggegations/structures.aggregation.js.map +1 -1
  12. package/lib/src/classes/paymentProviders/PaymentProviderFactory.class.d.ts +4 -0
  13. package/lib/src/classes/paymentProviders/PaymentProviderFactory.class.js +8 -0
  14. package/lib/src/classes/paymentProviders/PaymentProviderFactory.class.js.map +1 -0
  15. package/lib/src/classes/paymentProviders/StripePaymentProvider.class.d.ts +9 -0
  16. package/lib/src/classes/paymentProviders/StripePaymentProvider.class.js +49 -0
  17. package/lib/src/classes/paymentProviders/StripePaymentProvider.class.js.map +1 -0
  18. package/lib/src/classes/shiftPlanningModel.class.d.ts +6 -0
  19. package/lib/src/classes/shiftPlanningModel.class.js +26 -0
  20. package/lib/src/classes/shiftPlanningModel.class.js.map +1 -0
  21. package/lib/src/helpers/buildProductName.d.ts +7 -0
  22. package/lib/src/helpers/buildProductName.js +20 -0
  23. package/lib/src/helpers/buildProductName.js.map +1 -0
  24. package/lib/src/helpers/shiftPlanningReports.d.ts +7 -0
  25. package/lib/src/helpers/shiftPlanningReports.js +135 -0
  26. package/lib/src/helpers/shiftPlanningReports.js.map +1 -0
  27. package/lib/src/helpers/shiftPlanningSchemas.d.ts +143 -0
  28. package/lib/src/helpers/shiftPlanningSchemas.js +87 -0
  29. package/lib/src/helpers/shiftPlanningSchemas.js.map +1 -0
  30. package/lib/src/helpers/shiftPlanningTime.d.ts +24 -0
  31. package/lib/src/helpers/shiftPlanningTime.js +191 -0
  32. package/lib/src/helpers/shiftPlanningTime.js.map +1 -0
  33. package/lib/src/helpers/shiftPlanningValidation.d.ts +26 -0
  34. package/lib/src/helpers/shiftPlanningValidation.js +53 -0
  35. package/lib/src/helpers/shiftPlanningValidation.js.map +1 -0
  36. package/lib/src/helpers/structureMatch.d.ts +15 -0
  37. package/lib/src/helpers/structureMatch.js +70 -0
  38. package/lib/src/helpers/structureMatch.js.map +1 -0
  39. package/lib/src/interfaces/PaymentProviderBase.d.ts +14 -0
  40. package/lib/src/interfaces/PaymentProviderBase.js +2 -0
  41. package/lib/src/interfaces/PaymentProviderBase.js.map +1 -0
  42. package/lib/src/models/customers.model.js +10 -0
  43. package/lib/src/models/customers.model.js.map +1 -1
  44. package/lib/src/models/employeeAbsence.model.d.ts +4 -0
  45. package/lib/src/models/employeeAbsence.model.js +11 -0
  46. package/lib/src/models/employeeAbsence.model.js.map +1 -0
  47. package/lib/src/models/historySeen.model.d.ts +4 -0
  48. package/lib/src/models/historySeen.model.js +30 -0
  49. package/lib/src/models/historySeen.model.js.map +1 -0
  50. package/lib/src/models/listsParticipants.model.js +11 -0
  51. package/lib/src/models/listsParticipants.model.js.map +1 -1
  52. package/lib/src/models/permissions.model.js +28 -0
  53. package/lib/src/models/permissions.model.js.map +1 -1
  54. package/lib/src/models/shift.model.d.ts +4 -0
  55. package/lib/src/models/shift.model.js +17 -0
  56. package/lib/src/models/shift.model.js.map +1 -0
  57. package/lib/src/models/shiftPlan.model.d.ts +4 -0
  58. package/lib/src/models/shiftPlan.model.js +14 -0
  59. package/lib/src/models/shiftPlan.model.js.map +1 -0
  60. package/lib/src/models/shiftPlanDays.model.d.ts +4 -0
  61. package/lib/src/models/shiftPlanDays.model.js +15 -0
  62. package/lib/src/models/shiftPlanDays.model.js.map +1 -0
  63. package/lib/src/models/shiftPlanningSettings.model.d.ts +4 -0
  64. package/lib/src/models/shiftPlanningSettings.model.js +10 -0
  65. package/lib/src/models/shiftPlanningSettings.model.js.map +1 -0
  66. package/lib/src/models/workAreas.model.d.ts +4 -0
  67. package/lib/src/models/workAreas.model.js +12 -0
  68. package/lib/src/models/workAreas.model.js.map +1 -0
  69. package/lib/src/services/customers.service.d.ts +11 -0
  70. package/lib/src/services/customers.service.js +27 -0
  71. package/lib/src/services/customers.service.js.map +1 -1
  72. package/lib/src/services/employeeAbsences.service.d.ts +18 -0
  73. package/lib/src/services/employeeAbsences.service.js +57 -0
  74. package/lib/src/services/employeeAbsences.service.js.map +1 -0
  75. package/lib/src/services/history.service.d.ts +22 -0
  76. package/lib/src/services/history.service.js +145 -115
  77. package/lib/src/services/history.service.js.map +1 -1
  78. package/lib/src/services/historySeen.service.d.ts +32 -0
  79. package/lib/src/services/historySeen.service.js +66 -0
  80. package/lib/src/services/historySeen.service.js.map +1 -0
  81. package/lib/src/services/listParticipants.service.d.ts +143 -12
  82. package/lib/src/services/listParticipants.service.js +145 -6
  83. package/lib/src/services/listParticipants.service.js.map +1 -1
  84. package/lib/src/services/lists.service.d.ts +9 -0
  85. package/lib/src/services/lists.service.js +28 -0
  86. package/lib/src/services/lists.service.js.map +1 -1
  87. package/lib/src/services/paymentProviders.service.d.ts +5 -0
  88. package/lib/src/services/paymentProviders.service.js +10 -0
  89. package/lib/src/services/paymentProviders.service.js.map +1 -1
  90. package/lib/src/services/shiftPlanDays.service.d.ts +22 -0
  91. package/lib/src/services/shiftPlanDays.service.js +80 -0
  92. package/lib/src/services/shiftPlanDays.service.js.map +1 -0
  93. package/lib/src/services/shiftPlanningSettings.service.d.ts +11 -0
  94. package/lib/src/services/shiftPlanningSettings.service.js +27 -0
  95. package/lib/src/services/shiftPlanningSettings.service.js.map +1 -0
  96. package/lib/src/services/shiftPlans.service.d.ts +24 -0
  97. package/lib/src/services/shiftPlans.service.js +139 -0
  98. package/lib/src/services/shiftPlans.service.js.map +1 -0
  99. package/lib/src/services/shifts.service.d.ts +29 -0
  100. package/lib/src/services/shifts.service.js +260 -0
  101. package/lib/src/services/shifts.service.js.map +1 -0
  102. package/lib/src/services/structures.service.d.ts +2 -0
  103. package/lib/src/services/structures.service.js +7 -0
  104. package/lib/src/services/structures.service.js.map +1 -1
  105. package/lib/src/services/workAreas.service.d.ts +18 -0
  106. package/lib/src/services/workAreas.service.js +67 -0
  107. package/lib/src/services/workAreas.service.js.map +1 -0
  108. package/lib/tests/buildProductName.spec.d.ts +1 -0
  109. package/lib/tests/buildProductName.spec.js +27 -0
  110. package/lib/tests/buildProductName.spec.js.map +1 -0
  111. package/lib/tests/customersServiceStructures.spec.d.ts +1 -0
  112. package/lib/tests/customersServiceStructures.spec.js +146 -0
  113. package/lib/tests/customersServiceStructures.spec.js.map +1 -0
  114. package/lib/tests/historySeenService.spec.d.ts +1 -0
  115. package/lib/tests/historySeenService.spec.js +159 -0
  116. package/lib/tests/historySeenService.spec.js.map +1 -0
  117. package/lib/tests/listParticipantsHistory.spec.js +49 -3
  118. package/lib/tests/listParticipantsHistory.spec.js.map +1 -1
  119. package/lib/tests/listParticipantsPlanning.spec.d.ts +1 -0
  120. package/lib/tests/listParticipantsPlanning.spec.js +83 -0
  121. package/lib/tests/listParticipantsPlanning.spec.js.map +1 -0
  122. package/lib/tests/listParticipantsServiceStructures.spec.d.ts +1 -0
  123. package/lib/tests/listParticipantsServiceStructures.spec.js +176 -0
  124. package/lib/tests/listParticipantsServiceStructures.spec.js.map +1 -0
  125. package/lib/tests/listsServiceCreateExternalProducts.spec.d.ts +1 -0
  126. package/lib/tests/listsServiceCreateExternalProducts.spec.js +73 -0
  127. package/lib/tests/listsServiceCreateExternalProducts.spec.js.map +1 -0
  128. package/lib/tests/paymentProviders.spec.d.ts +1 -0
  129. package/lib/tests/paymentProviders.spec.js +119 -0
  130. package/lib/tests/paymentProviders.spec.js.map +1 -0
  131. package/lib/tests/paymentProvidersServiceGetProduct.spec.d.ts +1 -0
  132. package/lib/tests/paymentProvidersServiceGetProduct.spec.js +33 -0
  133. package/lib/tests/paymentProvidersServiceGetProduct.spec.js.map +1 -0
  134. package/lib/tests/shiftPlanning.spec.d.ts +1 -0
  135. package/lib/tests/shiftPlanning.spec.js +835 -0
  136. package/lib/tests/shiftPlanning.spec.js.map +1 -0
  137. package/lib/tests/structureMatch.spec.d.ts +1 -0
  138. package/lib/tests/structureMatch.spec.js +64 -0
  139. package/lib/tests/structureMatch.spec.js.map +1 -0
  140. package/package.json +2 -2
@@ -0,0 +1,143 @@
1
+ import type { SchemaObject } from 'ajv';
2
+ export declare const objectSchema: (properties: Record<string, SchemaObject>, required?: string[]) => SchemaObject;
3
+ export declare const objectIdSchema: SchemaObject;
4
+ export declare const instantSchema: SchemaObject;
5
+ /** `enabled` is omitted by clients when false, so it must never be required; omission means false. */
6
+ export declare const policyProperties: {
7
+ daytime: SchemaObject;
8
+ nighttime: SchemaObject;
9
+ maximumConsecutiveWorkingDays: SchemaObject;
10
+ maximumDailyWorkMinutes: SchemaObject;
11
+ minimumRest: SchemaObject;
12
+ breaks: SchemaObject;
13
+ absenceEnforcement: {
14
+ type: string;
15
+ };
16
+ };
17
+ export declare const policySchema: SchemaObject;
18
+ export declare const structureConditionProperties: {
19
+ structure: SchemaObject;
20
+ constraint: {
21
+ enum: string[];
22
+ };
23
+ grade: {
24
+ enum: string[];
25
+ };
26
+ operator: {
27
+ enum: string[];
28
+ };
29
+ value: {
30
+ anyOf: ({
31
+ type: string;
32
+ maxLength: number;
33
+ } | {
34
+ type: string;
35
+ maxLength?: undefined;
36
+ })[];
37
+ };
38
+ };
39
+ export declare const structureConditionSchema: SchemaObject;
40
+ export declare const areaProperties: {
41
+ id: SchemaObject;
42
+ name: SchemaObject;
43
+ description: SchemaObject;
44
+ location: SchemaObject;
45
+ address: SchemaObject;
46
+ structures: SchemaObject;
47
+ };
48
+ export declare const planningDateSchema: SchemaObject;
49
+ export declare const dayAreaProperties: {
50
+ area: SchemaObject;
51
+ };
52
+ export declare const dayProperties: {
53
+ date: SchemaObject;
54
+ name: SchemaObject;
55
+ areas: {
56
+ type: string;
57
+ maxItems: number;
58
+ items: SchemaObject;
59
+ };
60
+ };
61
+ export declare const planWorkAreaProperties: {
62
+ area: SchemaObject;
63
+ categoryIds: {
64
+ type: string;
65
+ maxItems: number;
66
+ items: SchemaObject;
67
+ };
68
+ };
69
+ export declare const workAreaCategoriesSchema: SchemaObject;
70
+ /** Same states the list participant views offer; `unchecked` is stored but not offered by the UI. */
71
+ export declare const participantStates: string[];
72
+ export declare const participantStateSchema: SchemaObject;
73
+ export declare const workHoursSchema: SchemaObject;
74
+ export declare const planProperties: {
75
+ name: SchemaObject;
76
+ timeZone: SchemaObject;
77
+ settingsOverrides: SchemaObject;
78
+ workAreaCategories: SchemaObject;
79
+ defaultParticipantState: {
80
+ anyOf: SchemaObject[];
81
+ };
82
+ workHours: SchemaObject;
83
+ };
84
+ export declare const planInputProperties: {
85
+ list: SchemaObject;
86
+ name: SchemaObject;
87
+ timeZone: SchemaObject;
88
+ settingsOverrides: SchemaObject;
89
+ workAreaCategories: SchemaObject;
90
+ defaultParticipantState: {
91
+ anyOf: SchemaObject[];
92
+ };
93
+ workHours: SchemaObject;
94
+ };
95
+ export declare const scopeProperties: {
96
+ company: SchemaObject;
97
+ project: SchemaObject;
98
+ list: SchemaObject;
99
+ };
100
+ export declare const shiftProperties: {
101
+ area: SchemaObject;
102
+ day: SchemaObject;
103
+ title: SchemaObject;
104
+ start: SchemaObject;
105
+ end: SchemaObject;
106
+ };
107
+ export declare const shiftStaffingProperties: {
108
+ structures: SchemaObject;
109
+ requiredPeople: SchemaObject;
110
+ matchingPeople: SchemaObject;
111
+ };
112
+ export declare const breakSchema: SchemaObject;
113
+ export declare const assignmentProperties: {
114
+ participant: SchemaObject;
115
+ breaks: {
116
+ type: string;
117
+ maxItems: number;
118
+ items: SchemaObject;
119
+ };
120
+ };
121
+ export declare const assignmentInputSchema: {
122
+ type: string;
123
+ maxItems: number;
124
+ items: SchemaObject;
125
+ };
126
+ export declare const assignmentSchema: {
127
+ type: string;
128
+ maxItems: number;
129
+ items: SchemaObject;
130
+ };
131
+ export declare const absenceProperties: {
132
+ customer: SchemaObject;
133
+ type: {
134
+ enum: string[];
135
+ };
136
+ start: SchemaObject;
137
+ end: SchemaObject;
138
+ };
139
+ export declare const filterProperties: {
140
+ from: SchemaObject;
141
+ to: SchemaObject;
142
+ site: SchemaObject;
143
+ };
@@ -0,0 +1,87 @@
1
+ export const objectSchema = (properties, required = Object.keys(properties)) => ({
2
+ type: 'object', properties, required, additionalProperties: false,
3
+ });
4
+ const text = (maxLength, minLength = 0) => ({ type: 'string', minLength, maxLength });
5
+ const integer = (minimum, maximum) => ({ type: 'integer', minimum, maximum });
6
+ export const objectIdSchema = { shiftPlanningObjectId: true };
7
+ export const instantSchema = {
8
+ type: 'string', format: 'date-time', maxLength: 40,
9
+ pattern: '^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,3})?(?:Z|[+-]\\d{2}:\\d{2})$',
10
+ };
11
+ const clockSchema = { type: 'string', pattern: '^(?:[01]\\d|2[0-3]):[0-5]\\d$' };
12
+ const clockPeriod = objectSchema({ start: clockSchema, end: clockSchema });
13
+ /** `enabled` is omitted by clients when false, so it must never be required; omission means false. */
14
+ export const policyProperties = {
15
+ daytime: clockPeriod,
16
+ nighttime: clockPeriod,
17
+ maximumConsecutiveWorkingDays: objectSchema({ enabled: { type: 'boolean' }, days: integer(1, 366) }, ['days']),
18
+ maximumDailyWorkMinutes: objectSchema({ enabled: { type: 'boolean' }, minutes: integer(1, 1440) }, ['minutes']),
19
+ minimumRest: objectSchema({ enabled: { type: 'boolean' }, minutes: integer(0, 10080) }, []),
20
+ breaks: objectSchema({
21
+ enabled: { type: 'boolean' }, maximumContinuousWorkMinutes: integer(1, 10080),
22
+ minimumQualifyingBreakMinutes: integer(1, 1440),
23
+ requirements: { type: 'array', maxItems: 10, items: objectSchema({
24
+ afterWorkMinutes: integer(1, 10080), minimumTotalBreakMinutes: integer(1, 1440),
25
+ }) },
26
+ }, ['maximumContinuousWorkMinutes', 'minimumQualifyingBreakMinutes', 'requirements']),
27
+ absenceEnforcement: { type: 'boolean' },
28
+ };
29
+ export const policySchema = objectSchema(policyProperties, Object.keys(policyProperties).filter((key) => key !== 'absenceEnforcement'));
30
+ const addressProperties = Object.fromEntries(['street', 'streetNumber', 'zipcode', 'city', 'country_name', 'addressAddition'].map((key) => [key, text(250)]));
31
+ addressProperties.country = { type: 'string', pattern: '^[A-Za-z]{2}$' };
32
+ addressProperties.osm_id = { type: 'number' };
33
+ export const structureConditionProperties = {
34
+ structure: text(24, 1),
35
+ constraint: { enum: ['required', 'forbidden'] },
36
+ grade: { enum: ['very-positive', 'positive', 'negative', 'very-negative'] },
37
+ operator: { enum: ['lt', 'lte', 'eq', 'gt', 'gte'] },
38
+ value: { anyOf: [{ type: 'string', maxLength: 250 }, { type: 'number' }, { type: 'boolean' }] },
39
+ };
40
+ export const structureConditionSchema = objectSchema(structureConditionProperties, ['structure']);
41
+ const structuresSchema = { type: 'array', maxItems: 100, items: structureConditionSchema };
42
+ export const areaProperties = {
43
+ id: text(100, 1), name: text(100, 1), description: text(2500), location: text(250), address: objectSchema(addressProperties, []),
44
+ structures: structuresSchema,
45
+ };
46
+ export const planningDateSchema = { type: 'string', format: 'date', pattern: '^\\d{4}-\\d{2}-\\d{2}$' };
47
+ export const dayAreaProperties = { area: objectIdSchema };
48
+ export const dayProperties = {
49
+ date: planningDateSchema,
50
+ name: text(100, 1),
51
+ areas: { type: 'array', maxItems: 100, items: objectSchema(dayAreaProperties, ['area']) },
52
+ };
53
+ export const planWorkAreaProperties = { area: objectIdSchema, categoryIds: { type: 'array', maxItems: 10, items: objectIdSchema } };
54
+ export const workAreaCategoriesSchema = { type: 'array', maxItems: 200, items: objectSchema(planWorkAreaProperties, ['area']) };
55
+ /** Same states the list participant views offer; `unchecked` is stored but not offered by the UI. */
56
+ export const participantStates = ['unchecked', 'paid', 'notuseful', 'confirmed', 'enlisted', 'reserved', 'waiting', 'blocked', 'finalized', 'canceled'];
57
+ export const participantStateSchema = { enum: participantStates };
58
+ export const workHoursSchema = objectSchema({ start: integer(0, 23), end: integer(1, 24) });
59
+ export const planProperties = {
60
+ name: text(100, 1), timeZone: text(100, 1),
61
+ settingsOverrides: objectSchema(policyProperties, []),
62
+ workAreaCategories: workAreaCategoriesSchema,
63
+ defaultParticipantState: { anyOf: [participantStateSchema, { type: 'null' }] },
64
+ workHours: workHoursSchema,
65
+ };
66
+ export const planInputProperties = { ...planProperties, list: objectIdSchema };
67
+ export const scopeProperties = { company: text(200, 1), project: text(200, 1), list: objectIdSchema };
68
+ export const shiftProperties = {
69
+ area: objectIdSchema,
70
+ day: objectIdSchema,
71
+ title: text(100, 1), start: instantSchema, end: instantSchema,
72
+ };
73
+ export const shiftStaffingProperties = {
74
+ structures: structuresSchema,
75
+ requiredPeople: integer(0, 1000),
76
+ matchingPeople: integer(0, 1000),
77
+ };
78
+ export const breakSchema = objectSchema({ start: instantSchema, end: instantSchema });
79
+ export const assignmentProperties = {
80
+ participant: objectIdSchema,
81
+ breaks: { type: 'array', maxItems: 20, items: breakSchema },
82
+ };
83
+ export const assignmentInputSchema = { type: 'array', maxItems: 100, items: objectSchema(assignmentProperties, ['participant']) };
84
+ export const assignmentSchema = { type: 'array', maxItems: 100, items: objectSchema({ ...assignmentProperties, customer: objectIdSchema }) };
85
+ export const absenceProperties = { customer: objectIdSchema, type: { enum: ['vacation', 'sickness'] }, start: instantSchema, end: instantSchema };
86
+ export const filterProperties = { from: instantSchema, to: instantSchema, site: integer(1, Number.MAX_SAFE_INTEGER) };
87
+ //# sourceMappingURL=shiftPlanningSchemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shiftPlanningSchemas.js","sourceRoot":"","sources":["../../../src/helpers/shiftPlanningSchemas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { Shift, ShiftAssignment, ShiftPlanningPolicy, ShiftPlanningPolicyOverrides, ShiftWorkingTime } from '@lilaquadrat/interfaces';
2
+ export declare const minuteMilliseconds = 60000;
3
+ export declare const maximumShiftMilliseconds: number;
4
+ export declare function planningValidation(path: string, message: string): never;
5
+ export declare function normalizeInstant(value: string): string;
6
+ export declare function normalizeDate(value: string): string;
7
+ export declare function interval(start: string, end: string): {
8
+ start: string;
9
+ end: string;
10
+ };
11
+ export declare function localFormatter(timeZone: string): Intl.DateTimeFormat;
12
+ export declare function localDate(instant: string, timeZone: string): string;
13
+ export declare function previousDate(date: string): string;
14
+ export declare function validatePolicy(policy?: ShiftPlanningPolicyOverrides): void;
15
+ export declare function effectivePolicy(company: ShiftPlanningPolicy | undefined, overrides: ShiftPlanningPolicyOverrides): ShiftPlanningPolicy | null;
16
+ export declare function normalizeBreaks(shift: Pick<Shift, 'start' | 'end'>, assignment: ShiftAssignment): ShiftAssignment;
17
+ export declare function workSegments(shift: Pick<Shift, 'start' | 'end'>, assignment: ShiftAssignment): Array<[number, number]>;
18
+ /** Local clock boundaries classify work, while UTC elapsed minutes prevent DST from inventing or losing time. */
19
+ export declare function calculateWorkingTime(shift: Shift, assignment: ShiftAssignment, timeZone: string, policy: ShiftPlanningPolicy): ShiftWorkingTime;
20
+ export declare function workedDates(shift: Shift, assignment: ShiftAssignment, timeZone: string): Set<string>;
21
+ /** Break-excluded work minutes keyed by local calendar date; UTC stepping keeps DST from inventing or losing time. */
22
+ export declare function workedMinutesByDate(shift: Shift, assignment: ShiftAssignment, timeZone: string): Map<string, number>;
23
+ /** Short interruptions reduce net work, but cannot satisfy break rights or reset uninterrupted working time. */
24
+ export declare function breakViolation(shift: Shift, assignment: ShiftAssignment, policy: ShiftPlanningPolicy): boolean;
@@ -0,0 +1,191 @@
1
+ import ImmutableError from '../classes/errors/Immutable.error.js';
2
+ export const minuteMilliseconds = 60000;
3
+ export const maximumShiftMilliseconds = 7 * 24 * 60 * minuteMilliseconds;
4
+ export function planningValidation(path, message) {
5
+ throw new ImmutableError('VALIDATION_FAILED', [{
6
+ instancePath: path, schemaPath: '#/shiftPlanning', keyword: 'shiftPlanning', params: {}, message,
7
+ }]);
8
+ }
9
+ export function normalizeInstant(value) {
10
+ const instant = Date.parse(value);
11
+ if (!Number.isFinite(instant))
12
+ planningValidation('', 'must be a valid instant');
13
+ const normalized = new Date(instant).toISOString();
14
+ if (!/^\d{4}-/.test(normalized))
15
+ planningValidation('', 'must normalize to a four-digit ISO year');
16
+ return normalized;
17
+ }
18
+ export function normalizeDate(value) {
19
+ const instant = new Date(`${value}T00:00:00Z`);
20
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || Number.isNaN(instant.getTime())
21
+ || instant.toISOString().slice(0, 10) !== value)
22
+ planningValidation('/date', 'must be a valid date');
23
+ return value;
24
+ }
25
+ export function interval(start, end) {
26
+ const result = { start: normalizeInstant(start), end: normalizeInstant(end) };
27
+ if (result.end <= result.start)
28
+ planningValidation('/end', 'must be after start');
29
+ return result;
30
+ }
31
+ export function localFormatter(timeZone) {
32
+ try {
33
+ return new Intl.DateTimeFormat('en-GB', {
34
+ timeZone, year: 'numeric', month: '2-digit', day: '2-digit',
35
+ hour: '2-digit', minute: '2-digit', hourCycle: 'h23',
36
+ });
37
+ }
38
+ catch {
39
+ return planningValidation('/timeZone', 'must be a valid IANA time zone');
40
+ }
41
+ }
42
+ function localParts(formatter, instant) {
43
+ const parts = Object.fromEntries(formatter.formatToParts(instant).map((part) => [part.type, part.value]));
44
+ return { date: `${parts.year.padStart(4, '0')}-${parts.month}-${parts.day}`, clock: `${parts.hour}:${parts.minute}` };
45
+ }
46
+ export function localDate(instant, timeZone) {
47
+ return localParts(localFormatter(timeZone), Date.parse(instant)).date;
48
+ }
49
+ export function previousDate(date) {
50
+ const instant = new Date(`${date}T12:00:00Z`);
51
+ instant.setUTCDate(instant.getUTCDate() - 1);
52
+ return instant.toISOString().slice(0, 10);
53
+ }
54
+ export function validatePolicy(policy = {}) {
55
+ if (policy.daytime && policy.nighttime) {
56
+ if (policy.daytime.start === policy.daytime.end
57
+ || policy.daytime.start !== policy.nighttime.end || policy.daytime.end !== policy.nighttime.start) {
58
+ planningValidation('/settings', 'daytime and nighttime must partition the day');
59
+ }
60
+ }
61
+ if (policy.breaks) {
62
+ if (policy.breaks.enabled && !policy.breaks.requirements.length) {
63
+ planningValidation('/settings/breaks/requirements', 'enabled break rules require thresholds');
64
+ }
65
+ let previousWork = 0;
66
+ let previousBreak = 0;
67
+ for (const requirement of policy.breaks.requirements) {
68
+ if (requirement.afterWorkMinutes <= previousWork || requirement.minimumTotalBreakMinutes < previousBreak) {
69
+ planningValidation('/settings/breaks/requirements', 'thresholds must increase and required breaks must not decrease');
70
+ }
71
+ previousWork = requirement.afterWorkMinutes;
72
+ previousBreak = requirement.minimumTotalBreakMinutes;
73
+ }
74
+ }
75
+ }
76
+ export function effectivePolicy(company, overrides) {
77
+ // Whole groups replace defaults, including false; null signals setup/attention rather than silently disabling rules.
78
+ const result = { ...company, ...overrides };
79
+ if (!result.daytime || !result.nighttime || !result.maximumConsecutiveWorkingDays
80
+ || !result.maximumDailyWorkMinutes || !result.minimumRest || !result.breaks)
81
+ return null;
82
+ // Two individually valid groups may be incompatible after inheritance.
83
+ try {
84
+ validatePolicy(result);
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ // Booleans are omitted when false, so a missing flag is false.
90
+ return { ...result, absenceEnforcement: result.absenceEnforcement === true };
91
+ }
92
+ export function normalizeBreaks(shift, assignment) {
93
+ let previousEnd = shift.start;
94
+ const breaks = assignment.breaks.map((entry) => {
95
+ const normalized = interval(entry.start, entry.end);
96
+ if (normalized.start < previousEnd || normalized.end > shift.end) {
97
+ planningValidation('/assignments/breaks', 'breaks must be ordered, non-overlapping and inside the shift');
98
+ }
99
+ previousEnd = normalized.end;
100
+ return normalized;
101
+ });
102
+ return { ...assignment, breaks };
103
+ }
104
+ export function workSegments(shift, assignment) {
105
+ const segments = [];
106
+ let cursor = Date.parse(shift.start);
107
+ for (const entry of assignment.breaks) {
108
+ const start = Date.parse(entry.start);
109
+ if (start > cursor)
110
+ segments.push([cursor, start]);
111
+ cursor = Date.parse(entry.end);
112
+ }
113
+ if (cursor < Date.parse(shift.end))
114
+ segments.push([cursor, Date.parse(shift.end)]);
115
+ return segments;
116
+ }
117
+ /** Local clock boundaries classify work, while UTC elapsed minutes prevent DST from inventing or losing time. */
118
+ export function calculateWorkingTime(shift, assignment, timeZone, policy) {
119
+ const formatter = localFormatter(timeZone);
120
+ const workingTime = {
121
+ customer: assignment.customer.toHexString(), daytimeMinutes: 0, nighttimeMinutes: 0,
122
+ breakMinutes: assignment.breaks.reduce((total, entry) => total + (Date.parse(entry.end) - Date.parse(entry.start)) / minuteMilliseconds, 0),
123
+ };
124
+ const daytime = policy.daytime;
125
+ for (const [start, end] of workSegments(shift, assignment)) {
126
+ let cursor = start;
127
+ // Real UTC minutes keep classification correct through both skipped and repeated DST hours.
128
+ while (cursor < end) {
129
+ const next = Math.min(end, (Math.floor(cursor / minuteMilliseconds) + 1) * minuteMilliseconds);
130
+ const local = localParts(formatter, cursor);
131
+ const isDaytime = daytime.start < daytime.end
132
+ ? local.clock >= daytime.start && local.clock < daytime.end
133
+ : local.clock >= daytime.start || local.clock < daytime.end;
134
+ workingTime[isDaytime ? 'daytimeMinutes' : 'nighttimeMinutes'] += (next - cursor) / minuteMilliseconds;
135
+ cursor = next;
136
+ }
137
+ }
138
+ return workingTime;
139
+ }
140
+ export function workedDates(shift, assignment, timeZone) {
141
+ const formatter = localFormatter(timeZone);
142
+ const result = new Set();
143
+ for (const [start, end] of workSegments(shift, assignment)) {
144
+ // A six-hour step cannot skip a calendar date, including across timezone transitions.
145
+ for (let cursor = start; cursor < end; cursor += 6 * 60 * minuteMilliseconds) {
146
+ result.add(localParts(formatter, cursor).date);
147
+ }
148
+ result.add(localParts(formatter, end - 1).date);
149
+ }
150
+ return result;
151
+ }
152
+ /** Break-excluded work minutes keyed by local calendar date; UTC stepping keeps DST from inventing or losing time. */
153
+ export function workedMinutesByDate(shift, assignment, timeZone) {
154
+ const formatter = localFormatter(timeZone);
155
+ const result = new Map();
156
+ for (const [start, end] of workSegments(shift, assignment)) {
157
+ for (let cursor = start; cursor < end; cursor += minuteMilliseconds) {
158
+ const next = Math.min(end, cursor + minuteMilliseconds);
159
+ const date = localParts(formatter, cursor).date;
160
+ result.set(date, (result.get(date) ?? 0) + (next - cursor) / minuteMilliseconds);
161
+ }
162
+ }
163
+ return result;
164
+ }
165
+ /** Short interruptions reduce net work, but cannot satisfy break rights or reset uninterrupted working time. */
166
+ export function breakViolation(shift, assignment, policy) {
167
+ if (!policy.breaks.enabled)
168
+ return false;
169
+ const requirements = policy.breaks;
170
+ let cursor = Date.parse(shift.start);
171
+ let continuous = 0;
172
+ let maximumContinuous = 0;
173
+ let qualifyingBreaks = 0;
174
+ let allBreaks = 0;
175
+ for (const entry of assignment.breaks) {
176
+ continuous += (Date.parse(entry.start) - cursor) / minuteMilliseconds;
177
+ maximumContinuous = Math.max(maximumContinuous, continuous);
178
+ const duration = (Date.parse(entry.end) - Date.parse(entry.start)) / minuteMilliseconds;
179
+ allBreaks += duration;
180
+ if (duration >= requirements.minimumQualifyingBreakMinutes) {
181
+ qualifyingBreaks += duration;
182
+ continuous = 0;
183
+ }
184
+ cursor = Date.parse(entry.end);
185
+ }
186
+ maximumContinuous = Math.max(maximumContinuous, continuous + (Date.parse(shift.end) - cursor) / minuteMilliseconds);
187
+ const worked = (Date.parse(shift.end) - Date.parse(shift.start)) / minuteMilliseconds - allBreaks;
188
+ return maximumContinuous > requirements.maximumContinuousWorkMinutes
189
+ || requirements.requirements.some((entry) => worked > entry.afterWorkMinutes && qualifyingBreaks < entry.minimumTotalBreakMinutes);
190
+ }
191
+ //# sourceMappingURL=shiftPlanningTime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shiftPlanningTime.js","sourceRoot":"","sources":["../../../src/helpers/shiftPlanningTime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { Filter } from 'mongodb';
2
+ import type { ShiftAssignment, ShiftAssignmentBreak, ShiftPlanningPage, ShiftPlanningViolation } from '@lilaquadrat/interfaces';
3
+ export declare const planningLimit = 30;
4
+ export declare function assertUniquePlanningIds(values: string[], path: string): void;
5
+ export declare function equalBreaks(left: ShiftAssignmentBreak[], right: ShiftAssignmentBreak[]): boolean;
6
+ export declare function equalAssignment(left: ShiftAssignment, right: ShiftAssignment): boolean;
7
+ /** Input ordering is presentation only; an identical employee allocation must not create audit noise. */
8
+ export declare function equalAssignmentSets(left: ShiftAssignment[], right: ShiftAssignment[]): boolean;
9
+ /** These identity fields are fixed enums/hex IDs; severity and object property order are deliberately excluded. */
10
+ export declare function violationIdentity(violation: ShiftPlanningViolation): string;
11
+ export declare function planningIntervalFilter<T extends {
12
+ start: string;
13
+ end: string;
14
+ }>(filters: {
15
+ from?: string;
16
+ to?: string;
17
+ }): Filter<T>;
18
+ export declare function planningPagination(filters: {
19
+ site?: number;
20
+ }): {
21
+ site: number;
22
+ limit: number;
23
+ skip: number;
24
+ };
25
+ export declare function planningSearch(search: string | undefined): string | undefined;
26
+ export declare function planningPage<T>(data: T[], all: number, site: number, limit?: number): ShiftPlanningPage<T>;
@@ -0,0 +1,53 @@
1
+ import ControllerHelper from './ControllerHelper.js';
2
+ import { interval, normalizeInstant, planningValidation } from './shiftPlanningTime.js';
3
+ export const planningLimit = 30;
4
+ export function assertUniquePlanningIds(values, path) {
5
+ if (new Set(values).size !== values.length)
6
+ planningValidation(path, 'identities must be unique');
7
+ }
8
+ export function equalBreaks(left, right) {
9
+ return left.length === right.length && left.every((entry, index) => entry.start === right[index].start && entry.end === right[index].end);
10
+ }
11
+ export function equalAssignment(left, right) {
12
+ return left.participant.equals(right.participant) && left.customer.equals(right.customer)
13
+ && equalBreaks(left.breaks, right.breaks);
14
+ }
15
+ /** Input ordering is presentation only; an identical employee allocation must not create audit noise. */
16
+ export function equalAssignmentSets(left, right) {
17
+ const byParticipant = new Map(right.map((entry) => [entry.participant.toHexString(), entry]));
18
+ return left.length === right.length && byParticipant.size === right.length
19
+ && left.every((entry) => {
20
+ const existing = byParticipant.get(entry.participant.toHexString());
21
+ return existing !== undefined && equalAssignment(entry, existing);
22
+ });
23
+ }
24
+ /** These identity fields are fixed enums/hex IDs; severity and object property order are deliberately excluded. */
25
+ export function violationIdentity(violation) {
26
+ return [violation.customer, violation.shift, violation.rule, violation.relatedShift ?? '', violation.absence ?? ''].join(':');
27
+ }
28
+ export function planningIntervalFilter(filters) {
29
+ const result = {};
30
+ if (filters.from)
31
+ result.end = { $gt: normalizeInstant(filters.from) };
32
+ if (filters.to)
33
+ result.start = { $lt: normalizeInstant(filters.to) };
34
+ if (filters.from && filters.to)
35
+ interval(filters.from, filters.to);
36
+ return result;
37
+ }
38
+ export function planningPagination(filters) {
39
+ const site = filters.site ?? 1;
40
+ if (!Number.isInteger(site) || site < 1)
41
+ planningValidation('/site', 'must be a positive integer');
42
+ const { skip, limit } = ControllerHelper.getLimit(site - 1, planningLimit);
43
+ return { site, limit, skip };
44
+ }
45
+ export function planningSearch(search) {
46
+ if (search !== undefined && search.length > 100)
47
+ planningValidation('/search', 'must be at most 100 characters');
48
+ return search;
49
+ }
50
+ export function planningPage(data, all, site, limit = planningLimit) {
51
+ return { data, all, count: data.length, sites: ControllerHelper.getSites(site, limit, all, data.length) };
52
+ }
53
+ //# sourceMappingURL=shiftPlanningValidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shiftPlanningValidation.js","sourceRoot":"","sources":["../../../src/helpers/shiftPlanningValidation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { ListParticipantMatch, StructureCondition, WithStructures } from '@lilaquadrat/interfaces';
2
+ /** Shift and list participants both carry s-* answers, their own structures and the customer's. */
3
+ export interface ScorableParticipant extends WithStructures {
4
+ user?: WithStructures;
5
+ }
6
+ /**
7
+ * A participant's structures carry the resolved definitions they answered with, for both the
8
+ * list and the customer model. `condition.structure` is a structure `_id`; the answer lives
9
+ * under the definition's `id` on the participant or, for customer structures, on the user.
10
+ *
11
+ * Constraints are checked before grades and only determine the state: a missing required or a
12
+ * present forbidden marks the participant unfit. The score is computed separately from the
13
+ * grades alone, starting at zero and clamped to [-10, 10].
14
+ */
15
+ export declare function structureScore(conditions: StructureCondition[] | undefined, participant: ScorableParticipant): ListParticipantMatch;
@@ -0,0 +1,70 @@
1
+ function definitionsOf(participant) {
2
+ const definitions = new Map();
3
+ const sources = [participant.structures, participant.user?.structures];
4
+ for (const source of sources) {
5
+ for (const structure of source ?? []) {
6
+ if (structure._id !== undefined)
7
+ definitions.set(structure._id.toString(), structure);
8
+ }
9
+ }
10
+ return definitions;
11
+ }
12
+ /** Bounds of the preference score; grades are their own thing and never affect the constraint state. */
13
+ const SCORE_MIN = -10;
14
+ const SCORE_MAX = 10;
15
+ /** Graded conditions add their weight when met. */
16
+ const gradeWeights = {
17
+ 'very-positive': 2, 'positive': 1, 'negative': -1, 'very-negative': -2,
18
+ };
19
+ /** Number conditions compare the answer against `value`; a non-numeric answer never matches. */
20
+ function numberMet(operator, answer, value) {
21
+ const left = Number(answer);
22
+ if (Number.isNaN(left))
23
+ return false;
24
+ if (operator === 'lt')
25
+ return left < value;
26
+ if (operator === 'lte')
27
+ return left <= value;
28
+ if (operator === 'gt')
29
+ return left > value;
30
+ if (operator === 'gte')
31
+ return left >= value;
32
+ return left === value;
33
+ }
34
+ /**
35
+ * A participant's structures carry the resolved definitions they answered with, for both the
36
+ * list and the customer model. `condition.structure` is a structure `_id`; the answer lives
37
+ * under the definition's `id` on the participant or, for customer structures, on the user.
38
+ *
39
+ * Constraints are checked before grades and only determine the state: a missing required or a
40
+ * present forbidden marks the participant unfit. The score is computed separately from the
41
+ * grades alone, starting at zero and clamped to [-10, 10].
42
+ */
43
+ export function structureScore(conditions, participant) {
44
+ const definitions = definitionsOf(participant);
45
+ const answers = participant;
46
+ const userAnswers = participant.user;
47
+ const total = conditions?.length ?? 0;
48
+ let state = 'fulfilled';
49
+ let score = 0;
50
+ let matched = 0;
51
+ for (const condition of conditions ?? []) {
52
+ const structure = definitions.get(condition.structure);
53
+ const answer = structure === undefined
54
+ ? undefined
55
+ : answers[structure.id] !== undefined ? answers[structure.id] : userAnswers?.[structure.id];
56
+ const met = structure?.type === 'number' && condition.operator !== undefined && typeof condition.value === 'number'
57
+ ? numberMet(condition.operator, answer, condition.value)
58
+ : condition.value !== undefined ? answer === condition.value : Boolean(answer);
59
+ if (met)
60
+ matched += 1;
61
+ if (condition.constraint === 'required' && !met)
62
+ state = 'unfit';
63
+ else if (condition.constraint === 'forbidden' && met)
64
+ state = 'unfit';
65
+ if (condition.grade !== undefined && met)
66
+ score += gradeWeights[condition.grade];
67
+ }
68
+ return { state, score: Math.max(SCORE_MIN, Math.min(SCORE_MAX, score)), matched, total };
69
+ }
70
+ //# sourceMappingURL=structureMatch.js.map