@burdenoff/microfe-movethewheels 2026.1017.11 → 2026.1017.13
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/MoveTheWheelsRoot.js +7 -0
- package/dist/MoveTheWheelsRoutes.js +39 -35
- package/dist/components/pallet-preview/PalletPreviewResults.js +189 -0
- package/dist/generated/pallet-preview-operations.d.ts +273 -0
- package/dist/hooks/usePalletPreview.js +86 -0
- package/dist/movethewheels/components/pallet-preview/PalletPreviewResults.d.ts +6 -0
- package/dist/movethewheels/hooks/usePalletPreview.d.ts +98 -0
- package/dist/movethewheels/pages/PalletPreviewPage.d.ts +1 -0
- package/dist/movethewheels/utils/graphqlClient.d.ts +4 -0
- package/dist/movethewheels/utils/palletPreviewInput.d.ts +54 -0
- package/dist/movethewheels/utils/palletPreviewResponse.d.ts +7 -0
- package/dist/pages/PalletPreviewPage.js +501 -0
- package/dist/pages/WarehousesPage.js +83 -78
- package/dist/src/generated/pallet-preview-operations.js +594 -0
- package/dist/utils/graphqlClient.js +27 -19
- package/dist/utils/palletPreviewInput.js +138 -0
- package/dist/utils/palletPreviewResponse.js +191 -0
- package/package.json +5 -4
- package/src/generated/pallet-preview-operations.ts +204 -0
- package/src/movethewheels/MoveTheWheelsRoot.tsx +7 -0
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +2 -0
- package/src/movethewheels/components/pallet-preview/PalletPreviewResults.tsx +220 -0
- package/src/movethewheels/hooks/usePalletPreview.ts +114 -0
- package/src/movethewheels/pages/PalletPreviewPage.tsx +588 -0
- package/src/movethewheels/pages/WarehousesPage.tsx +9 -1
- package/src/movethewheels/utils/graphqlClient.ts +19 -2
- package/src/movethewheels/utils/palletPreviewInput.ts +199 -0
- package/src/movethewheels/utils/palletPreviewResponse.ts +256 -0
- package/src/operations/pallet-preview/preview.graphql +26 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MtwPalletGeometryPreviewInput,
|
|
3
|
+
MtwPalletMeasurementSourceInput,
|
|
4
|
+
} from '../../generated/pallet-preview-operations';
|
|
5
|
+
|
|
6
|
+
export interface SourceDraft {
|
|
7
|
+
reference: string;
|
|
8
|
+
version: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PackageDraft {
|
|
11
|
+
key: number;
|
|
12
|
+
id: string;
|
|
13
|
+
quantity: string;
|
|
14
|
+
source: SourceDraft;
|
|
15
|
+
widthMm: string;
|
|
16
|
+
lengthMm: string;
|
|
17
|
+
heightMm: string;
|
|
18
|
+
grossMassG: string;
|
|
19
|
+
yaw: 'UNKNOWN' | 'NONE' | '0' | '90' | 'BOTH';
|
|
20
|
+
upright: 'UNKNOWN' | 'YES' | 'NO';
|
|
21
|
+
}
|
|
22
|
+
export interface PalletPreviewDraft {
|
|
23
|
+
packages: PackageDraft[];
|
|
24
|
+
pallet: {
|
|
25
|
+
source: SourceDraft;
|
|
26
|
+
widthMm: string;
|
|
27
|
+
lengthMm: string;
|
|
28
|
+
heightMm: string;
|
|
29
|
+
tareMassG: string;
|
|
30
|
+
maxPayloadMassG: string;
|
|
31
|
+
};
|
|
32
|
+
includeEnvelope: boolean;
|
|
33
|
+
loadEnvelope: {
|
|
34
|
+
source: SourceDraft;
|
|
35
|
+
widthMm: string;
|
|
36
|
+
lengthMm: string;
|
|
37
|
+
maxHeightMm: string;
|
|
38
|
+
maxGrossMassG: string;
|
|
39
|
+
};
|
|
40
|
+
additionalPacking: { source: SourceDraft; massG: string };
|
|
41
|
+
clearance: { source: SourceDraft; perimeterMm: string; betweenPackagesMm: string; topMm: string };
|
|
42
|
+
}
|
|
43
|
+
const blankSource = (): SourceDraft => ({ reference: '', version: '' });
|
|
44
|
+
export const newPalletPackage = (key: number): PackageDraft => ({
|
|
45
|
+
key,
|
|
46
|
+
id: '',
|
|
47
|
+
quantity: '',
|
|
48
|
+
source: blankSource(),
|
|
49
|
+
widthMm: '',
|
|
50
|
+
lengthMm: '',
|
|
51
|
+
heightMm: '',
|
|
52
|
+
grossMassG: '',
|
|
53
|
+
yaw: 'UNKNOWN',
|
|
54
|
+
upright: 'UNKNOWN',
|
|
55
|
+
});
|
|
56
|
+
export function newPalletPreviewDraft(): PalletPreviewDraft {
|
|
57
|
+
return {
|
|
58
|
+
packages: [newPalletPackage(1)],
|
|
59
|
+
pallet: {
|
|
60
|
+
source: blankSource(),
|
|
61
|
+
widthMm: '',
|
|
62
|
+
lengthMm: '',
|
|
63
|
+
heightMm: '',
|
|
64
|
+
tareMassG: '',
|
|
65
|
+
maxPayloadMassG: '',
|
|
66
|
+
},
|
|
67
|
+
includeEnvelope: false,
|
|
68
|
+
loadEnvelope: {
|
|
69
|
+
source: blankSource(),
|
|
70
|
+
widthMm: '',
|
|
71
|
+
lengthMm: '',
|
|
72
|
+
maxHeightMm: '',
|
|
73
|
+
maxGrossMassG: '',
|
|
74
|
+
},
|
|
75
|
+
additionalPacking: { source: blankSource(), massG: '' },
|
|
76
|
+
clearance: { source: blankSource(), perimeterMm: '', betweenPackagesMm: '', topMm: '' },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export class PalletInputError extends Error {
|
|
80
|
+
constructor(
|
|
81
|
+
readonly field: string,
|
|
82
|
+
readonly kind: 'WHOLE' | 'MASS' | 'SOURCE' | 'IDENTITY' | 'COUNT'
|
|
83
|
+
) {
|
|
84
|
+
super('Review the supplied pallet preview facts.');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function whole(
|
|
88
|
+
value: string,
|
|
89
|
+
field: string,
|
|
90
|
+
maximum: number,
|
|
91
|
+
zero: boolean,
|
|
92
|
+
required = false
|
|
93
|
+
): number | null {
|
|
94
|
+
if (value === '' && !required) return null;
|
|
95
|
+
if (value.trim() !== value || !/^(0|[1-9][0-9]*)$/.test(value) || value.length > 7)
|
|
96
|
+
throw new PalletInputError(field, 'WHOLE');
|
|
97
|
+
const n = Number(value);
|
|
98
|
+
if (!Number.isSafeInteger(n) || n > maximum || n < (zero ? 0 : 1))
|
|
99
|
+
throw new PalletInputError(field, 'WHOLE');
|
|
100
|
+
return n;
|
|
101
|
+
}
|
|
102
|
+
function mass(value: string, field: string): string | null {
|
|
103
|
+
if (value === '') return null;
|
|
104
|
+
if (
|
|
105
|
+
value.trim() !== value ||
|
|
106
|
+
!/^(0|[1-9][0-9]*)$/.test(value) ||
|
|
107
|
+
value.length > 13 ||
|
|
108
|
+
(value.length === 13 && value > '1000000000000')
|
|
109
|
+
)
|
|
110
|
+
throw new PalletInputError(field, 'MASS');
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
function source(value: SourceDraft, field: string): MtwPalletMeasurementSourceInput | null {
|
|
114
|
+
if (value.reference === '' && value.version === '') return null;
|
|
115
|
+
if (
|
|
116
|
+
!value.reference.trim() ||
|
|
117
|
+
!value.version.trim() ||
|
|
118
|
+
value.reference.length > 255 ||
|
|
119
|
+
value.version.length > 120
|
|
120
|
+
)
|
|
121
|
+
throw new PalletInputError(field, 'SOURCE');
|
|
122
|
+
return { reference: value.reference, version: value.version };
|
|
123
|
+
}
|
|
124
|
+
export function palletPreviewInput(draft: PalletPreviewDraft): MtwPalletGeometryPreviewInput {
|
|
125
|
+
if (!draft.packages.length || draft.packages.length > 100)
|
|
126
|
+
throw new PalletInputError('packages', 'COUNT');
|
|
127
|
+
const ids = new Set<string>();
|
|
128
|
+
let total = 0;
|
|
129
|
+
const packages = draft.packages.map((p) => {
|
|
130
|
+
const key = `package-${p.key}`;
|
|
131
|
+
if (
|
|
132
|
+
!['UNKNOWN', 'NONE', '0', '90', 'BOTH'].includes(p.yaw) ||
|
|
133
|
+
!['UNKNOWN', 'YES', 'NO'].includes(p.upright)
|
|
134
|
+
)
|
|
135
|
+
throw new PalletInputError(key, 'WHOLE');
|
|
136
|
+
if (!p.id.trim() || p.id.trim() !== p.id || p.id.length > 255 || ids.has(p.id))
|
|
137
|
+
throw new PalletInputError(`${key}-id`, 'IDENTITY');
|
|
138
|
+
ids.add(p.id);
|
|
139
|
+
const quantity = whole(p.quantity, `${key}-quantity`, 1000, false, true);
|
|
140
|
+
if (quantity === null) throw new PalletInputError(`${key}-quantity`, 'COUNT');
|
|
141
|
+
total += quantity;
|
|
142
|
+
if (total > 1000) throw new PalletInputError(`${key}-quantity`, 'COUNT');
|
|
143
|
+
return {
|
|
144
|
+
id: p.id,
|
|
145
|
+
quantity,
|
|
146
|
+
source: source(p.source, `${key}-source`),
|
|
147
|
+
widthMm: whole(p.widthMm, `${key}-widthMm`, 1000000, false),
|
|
148
|
+
lengthMm: whole(p.lengthMm, `${key}-lengthMm`, 1000000, false),
|
|
149
|
+
heightMm: whole(p.heightMm, `${key}-heightMm`, 1000000, false),
|
|
150
|
+
grossMassG: mass(p.grossMassG, `${key}-grossMassG`),
|
|
151
|
+
allowedYawDegrees:
|
|
152
|
+
p.yaw === 'UNKNOWN'
|
|
153
|
+
? null
|
|
154
|
+
: p.yaw === 'NONE'
|
|
155
|
+
? []
|
|
156
|
+
: p.yaw === 'BOTH'
|
|
157
|
+
? [0, 90]
|
|
158
|
+
: p.yaw === '0'
|
|
159
|
+
? [0]
|
|
160
|
+
: [90],
|
|
161
|
+
upright: p.upright === 'UNKNOWN' ? null : p.upright === 'YES',
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
const p = draft.pallet,
|
|
165
|
+
e = draft.loadEnvelope,
|
|
166
|
+
c = draft.clearance;
|
|
167
|
+
return {
|
|
168
|
+
units: 'MM_G',
|
|
169
|
+
mode: 'UPRIGHT_SINGLE_LAYER',
|
|
170
|
+
packages,
|
|
171
|
+
pallet: {
|
|
172
|
+
source: source(p.source, 'pallet-source'),
|
|
173
|
+
widthMm: whole(p.widthMm, 'pallet-widthMm', 1000000, false),
|
|
174
|
+
lengthMm: whole(p.lengthMm, 'pallet-lengthMm', 1000000, false),
|
|
175
|
+
heightMm: whole(p.heightMm, 'pallet-heightMm', 1000000, false),
|
|
176
|
+
tareMassG: mass(p.tareMassG, 'pallet-tareMassG'),
|
|
177
|
+
maxPayloadMassG: mass(p.maxPayloadMassG, 'pallet-maxPayloadMassG'),
|
|
178
|
+
},
|
|
179
|
+
loadEnvelope: draft.includeEnvelope
|
|
180
|
+
? {
|
|
181
|
+
source: source(e.source, 'envelope-source'),
|
|
182
|
+
widthMm: whole(e.widthMm, 'envelope-widthMm', 1000000, false),
|
|
183
|
+
lengthMm: whole(e.lengthMm, 'envelope-lengthMm', 1000000, false),
|
|
184
|
+
maxHeightMm: whole(e.maxHeightMm, 'envelope-maxHeightMm', 1000000, false),
|
|
185
|
+
maxGrossMassG: mass(e.maxGrossMassG, 'envelope-maxGrossMassG'),
|
|
186
|
+
}
|
|
187
|
+
: null,
|
|
188
|
+
additionalPacking: {
|
|
189
|
+
source: source(draft.additionalPacking.source, 'packing-source'),
|
|
190
|
+
massG: mass(draft.additionalPacking.massG, 'packing-massG'),
|
|
191
|
+
},
|
|
192
|
+
clearance: {
|
|
193
|
+
source: source(c.source, 'clearance-source'),
|
|
194
|
+
perimeterMm: whole(c.perimeterMm, 'clearance-perimeterMm', 1000000, true),
|
|
195
|
+
betweenPackagesMm: whole(c.betweenPackagesMm, 'clearance-betweenPackagesMm', 1000000, true),
|
|
196
|
+
topMm: whole(c.topMm, 'clearance-topMm', 1000000, true),
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MtwPalletGeometryPreviewInput,
|
|
3
|
+
MtwPalletGeometryPreviewQuery,
|
|
4
|
+
MtwPalletMeasurementSourceInput,
|
|
5
|
+
} from '../../generated/pallet-preview-operations';
|
|
6
|
+
export type PalletPreview = MtwPalletGeometryPreviewQuery['mtwPalletGeometryPreview'];
|
|
7
|
+
export const PALLET_BOUND_KEYS = [
|
|
8
|
+
'placedContainment',
|
|
9
|
+
'placedNonOverlap',
|
|
10
|
+
'permittedUprightYaw',
|
|
11
|
+
'reportedSingleLayerHeight',
|
|
12
|
+
'palletPayloadMass',
|
|
13
|
+
'palletFootprintWithinLoadEnvelope',
|
|
14
|
+
'loadedPalletGrossMass',
|
|
15
|
+
] as const;
|
|
16
|
+
export const PALLET_UNKNOWN_KEYS = [
|
|
17
|
+
'additionalPackingGeometry',
|
|
18
|
+
'handling',
|
|
19
|
+
'securing',
|
|
20
|
+
'centreOfGravityAndStability',
|
|
21
|
+
'supportAndFloorContactLoads',
|
|
22
|
+
'loadingAndUnloadingPath',
|
|
23
|
+
'segregationAndCargoCompatibility',
|
|
24
|
+
] as const;
|
|
25
|
+
function invalid(): never {
|
|
26
|
+
throw new Error('The pallet preview response could not be verified.');
|
|
27
|
+
}
|
|
28
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
29
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
function object(value: unknown): Record<string, unknown> {
|
|
32
|
+
if (!isObject(value)) return invalid();
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function keys(value: Record<string, unknown>, names: readonly string[]): void {
|
|
36
|
+
if (
|
|
37
|
+
names.some((name) => !Object.hasOwn(value, name)) ||
|
|
38
|
+
Object.keys(value).some((name) => name !== '__typename' && !names.includes(name))
|
|
39
|
+
)
|
|
40
|
+
invalid();
|
|
41
|
+
}
|
|
42
|
+
function text(value: unknown, max: number): string {
|
|
43
|
+
if (typeof value !== 'string' || !value.trim() || value.length > max) return invalid();
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
function integer(value: unknown, min = 0, max = 1000000): number {
|
|
47
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < min || value > max)
|
|
48
|
+
return invalid();
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
function nullableInteger(value: unknown): number | null {
|
|
52
|
+
return value === null ? null : integer(value);
|
|
53
|
+
}
|
|
54
|
+
function grams(value: unknown, maximum: string): string | null {
|
|
55
|
+
if (value === null) return null;
|
|
56
|
+
if (
|
|
57
|
+
typeof value !== 'string' ||
|
|
58
|
+
value.trim() !== value ||
|
|
59
|
+
!/^(0|[1-9][0-9]*)$/.test(value) ||
|
|
60
|
+
value.length > maximum.length ||
|
|
61
|
+
(value.length === maximum.length && value > maximum)
|
|
62
|
+
)
|
|
63
|
+
return invalid();
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
function source(value: unknown, expected: MtwPalletMeasurementSourceInput | null | undefined) {
|
|
67
|
+
if (expected == null) {
|
|
68
|
+
if (value !== null) invalid();
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const row = object(value);
|
|
72
|
+
keys(row, ['reference', 'version']);
|
|
73
|
+
const reference = text(row.reference, 255),
|
|
74
|
+
version = text(row.version, 120);
|
|
75
|
+
if (reference !== expected.reference.trim() || version !== expected.version.trim()) invalid();
|
|
76
|
+
return { reference, version };
|
|
77
|
+
}
|
|
78
|
+
function bound(value: unknown): PalletPreview['bounds']['placedContainment'] {
|
|
79
|
+
const row = object(value);
|
|
80
|
+
keys(row, ['status', 'reason']);
|
|
81
|
+
if (row.status !== 'PASS' && row.status !== 'FAIL' && row.status !== 'UNKNOWN') return invalid();
|
|
82
|
+
return { status: row.status, reason: text(row.reason, 4096) };
|
|
83
|
+
}
|
|
84
|
+
function unknown(value: unknown): 'UNKNOWN' {
|
|
85
|
+
if (value !== 'UNKNOWN') invalid();
|
|
86
|
+
return 'UNKNOWN';
|
|
87
|
+
}
|
|
88
|
+
/** New preview only: malformed or mixed envelopes never become a current result. */
|
|
89
|
+
export function validatePalletPreviewEnvelope(value: unknown): void {
|
|
90
|
+
const row = object(value);
|
|
91
|
+
if ('errors' in row && (!Array.isArray(row.errors) || row.errors.length !== 0)) invalid();
|
|
92
|
+
const data = object(row.data);
|
|
93
|
+
keys(data, ['mtwPalletGeometryPreview']);
|
|
94
|
+
if (data.mtwPalletGeometryPreview == null) invalid();
|
|
95
|
+
}
|
|
96
|
+
export function parsePalletPreview(
|
|
97
|
+
data: unknown,
|
|
98
|
+
input: MtwPalletGeometryPreviewInput
|
|
99
|
+
): PalletPreview {
|
|
100
|
+
const envelope = object(data);
|
|
101
|
+
keys(envelope, ['mtwPalletGeometryPreview']);
|
|
102
|
+
const row = object(envelope.mtwPalletGeometryPreview);
|
|
103
|
+
keys(row, [
|
|
104
|
+
'classification',
|
|
105
|
+
'method',
|
|
106
|
+
'units',
|
|
107
|
+
'layoutStatus',
|
|
108
|
+
'requestedQuantity',
|
|
109
|
+
'placedQuantity',
|
|
110
|
+
'requestedPayloadMassG',
|
|
111
|
+
'requestedGrossMassG',
|
|
112
|
+
'placements',
|
|
113
|
+
'unplaced',
|
|
114
|
+
'bounds',
|
|
115
|
+
'unsupported',
|
|
116
|
+
'sources',
|
|
117
|
+
]);
|
|
118
|
+
if (
|
|
119
|
+
row.classification !== 'GEOMETRIC_PROPOSAL_ONLY' ||
|
|
120
|
+
row.method !== 'DETERMINISTIC_SINGLE_LAYER_SHELF_V1' ||
|
|
121
|
+
row.units !== 'MM_G'
|
|
122
|
+
)
|
|
123
|
+
invalid();
|
|
124
|
+
const layoutStatus = row.layoutStatus;
|
|
125
|
+
if (
|
|
126
|
+
layoutStatus !== 'PLACED_BY_METHOD' &&
|
|
127
|
+
layoutStatus !== 'UNKNOWN' &&
|
|
128
|
+
layoutStatus !== 'NO_LAYOUT_FOUND_BY_METHOD'
|
|
129
|
+
)
|
|
130
|
+
return invalid();
|
|
131
|
+
const requestedQuantity = integer(row.requestedQuantity, 1, 1000),
|
|
132
|
+
placedQuantity = integer(row.placedQuantity, 0, 1000);
|
|
133
|
+
if (
|
|
134
|
+
requestedQuantity !== input.packages.reduce((n, p) => n + p.quantity, 0) ||
|
|
135
|
+
placedQuantity > requestedQuantity ||
|
|
136
|
+
!Array.isArray(row.placements) ||
|
|
137
|
+
row.placements.length !== placedQuantity ||
|
|
138
|
+
!Array.isArray(row.unplaced) ||
|
|
139
|
+
row.unplaced.length > input.packages.length
|
|
140
|
+
)
|
|
141
|
+
invalid();
|
|
142
|
+
const seen = new Set<string>();
|
|
143
|
+
const placed = new Map<string, number>();
|
|
144
|
+
const placements = row.placements.map((value: unknown) => {
|
|
145
|
+
const p = object(value);
|
|
146
|
+
keys(p, [
|
|
147
|
+
'packageId',
|
|
148
|
+
'unit',
|
|
149
|
+
'yawDegrees',
|
|
150
|
+
'xMm',
|
|
151
|
+
'yMm',
|
|
152
|
+
'zMm',
|
|
153
|
+
'widthMm',
|
|
154
|
+
'lengthMm',
|
|
155
|
+
'heightMm',
|
|
156
|
+
]);
|
|
157
|
+
const packageId = text(p.packageId, 255),
|
|
158
|
+
requested = input.packages.find((item) => item.id === packageId);
|
|
159
|
+
if (!requested) return invalid();
|
|
160
|
+
const unit = integer(p.unit, 1, requested.quantity),
|
|
161
|
+
yawDegrees = integer(p.yawDegrees, 0, 90);
|
|
162
|
+
if (
|
|
163
|
+
(yawDegrees !== 0 && yawDegrees !== 90) ||
|
|
164
|
+
!requested.allowedYawDegrees?.includes(yawDegrees) ||
|
|
165
|
+
requested.upright !== true ||
|
|
166
|
+
seen.has(`${packageId}:${unit}`)
|
|
167
|
+
)
|
|
168
|
+
invalid();
|
|
169
|
+
seen.add(`${packageId}:${unit}`);
|
|
170
|
+
placed.set(packageId, (placed.get(packageId) ?? 0) + 1);
|
|
171
|
+
const xMm = integer(p.xMm),
|
|
172
|
+
yMm = integer(p.yMm),
|
|
173
|
+
widthMm = integer(p.widthMm, 1),
|
|
174
|
+
lengthMm = integer(p.lengthMm, 1),
|
|
175
|
+
zMm = nullableInteger(p.zMm),
|
|
176
|
+
heightMm = nullableInteger(p.heightMm);
|
|
177
|
+
if (
|
|
178
|
+
widthMm !== (yawDegrees === 0 ? requested.widthMm : requested.lengthMm) ||
|
|
179
|
+
lengthMm !== (yawDegrees === 0 ? requested.lengthMm : requested.widthMm) ||
|
|
180
|
+
heightMm !== (requested.heightMm ?? null) ||
|
|
181
|
+
zMm !== (input.pallet.heightMm ?? null) ||
|
|
182
|
+
input.pallet.widthMm == null ||
|
|
183
|
+
input.pallet.lengthMm == null ||
|
|
184
|
+
xMm + widthMm > input.pallet.widthMm ||
|
|
185
|
+
yMm + lengthMm > input.pallet.lengthMm
|
|
186
|
+
)
|
|
187
|
+
invalid();
|
|
188
|
+
return { packageId, unit, yawDegrees, xMm, yMm, zMm, widthMm, lengthMm, heightMm };
|
|
189
|
+
});
|
|
190
|
+
const missing = new Map<string, number>();
|
|
191
|
+
const unplaced = row.unplaced.map((value: unknown) => {
|
|
192
|
+
const p = object(value);
|
|
193
|
+
keys(p, ['packageId', 'quantity']);
|
|
194
|
+
const packageId = text(p.packageId, 255),
|
|
195
|
+
quantity = integer(p.quantity, 1, 1000);
|
|
196
|
+
if (!input.packages.some((item) => item.id === packageId) || missing.has(packageId)) invalid();
|
|
197
|
+
missing.set(packageId, quantity);
|
|
198
|
+
return { packageId, quantity };
|
|
199
|
+
});
|
|
200
|
+
if (
|
|
201
|
+
input.packages.some((p) => (placed.get(p.id) ?? 0) + (missing.get(p.id) ?? 0) !== p.quantity) ||
|
|
202
|
+
(layoutStatus === 'PLACED_BY_METHOD' && placedQuantity !== requestedQuantity)
|
|
203
|
+
)
|
|
204
|
+
invalid();
|
|
205
|
+
const b = object(row.bounds);
|
|
206
|
+
keys(b, PALLET_BOUND_KEYS);
|
|
207
|
+
const u = object(row.unsupported);
|
|
208
|
+
keys(u, PALLET_UNKNOWN_KEYS);
|
|
209
|
+
const s = object(row.sources);
|
|
210
|
+
keys(s, ['packages', 'pallet', 'loadEnvelope', 'additionalPacking', 'clearance']);
|
|
211
|
+
if (!Array.isArray(s.packages) || s.packages.length !== input.packages.length) invalid();
|
|
212
|
+
const packageSources = s.packages.map((value: unknown, index: number) => {
|
|
213
|
+
const item = object(value);
|
|
214
|
+
keys(item, ['packageId', 'source']);
|
|
215
|
+
const expected = input.packages[index];
|
|
216
|
+
if (item.packageId !== expected.id) invalid();
|
|
217
|
+
return { packageId: expected.id, source: source(item.source, expected.source) };
|
|
218
|
+
});
|
|
219
|
+
return {
|
|
220
|
+
classification: 'GEOMETRIC_PROPOSAL_ONLY',
|
|
221
|
+
method: 'DETERMINISTIC_SINGLE_LAYER_SHELF_V1',
|
|
222
|
+
units: 'MM_G',
|
|
223
|
+
layoutStatus,
|
|
224
|
+
requestedQuantity,
|
|
225
|
+
placedQuantity,
|
|
226
|
+
requestedPayloadMassG: grams(row.requestedPayloadMassG, '1001000000000000'),
|
|
227
|
+
requestedGrossMassG: grams(row.requestedGrossMassG, '1002000000000000'),
|
|
228
|
+
placements,
|
|
229
|
+
unplaced,
|
|
230
|
+
bounds: {
|
|
231
|
+
placedContainment: bound(b.placedContainment),
|
|
232
|
+
placedNonOverlap: bound(b.placedNonOverlap),
|
|
233
|
+
permittedUprightYaw: bound(b.permittedUprightYaw),
|
|
234
|
+
reportedSingleLayerHeight: bound(b.reportedSingleLayerHeight),
|
|
235
|
+
palletPayloadMass: bound(b.palletPayloadMass),
|
|
236
|
+
palletFootprintWithinLoadEnvelope: bound(b.palletFootprintWithinLoadEnvelope),
|
|
237
|
+
loadedPalletGrossMass: bound(b.loadedPalletGrossMass),
|
|
238
|
+
},
|
|
239
|
+
unsupported: {
|
|
240
|
+
additionalPackingGeometry: unknown(u.additionalPackingGeometry),
|
|
241
|
+
handling: unknown(u.handling),
|
|
242
|
+
securing: unknown(u.securing),
|
|
243
|
+
centreOfGravityAndStability: unknown(u.centreOfGravityAndStability),
|
|
244
|
+
supportAndFloorContactLoads: unknown(u.supportAndFloorContactLoads),
|
|
245
|
+
loadingAndUnloadingPath: unknown(u.loadingAndUnloadingPath),
|
|
246
|
+
segregationAndCargoCompatibility: unknown(u.segregationAndCargoCompatibility),
|
|
247
|
+
},
|
|
248
|
+
sources: {
|
|
249
|
+
packages: packageSources,
|
|
250
|
+
pallet: source(s.pallet, input.pallet.source),
|
|
251
|
+
loadEnvelope: source(s.loadEnvelope, input.loadEnvelope?.source),
|
|
252
|
+
additionalPacking: source(s.additionalPacking, input.additionalPacking.source),
|
|
253
|
+
clearance: source(s.clearance, input.clearance.source),
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
query MtwPalletGeometryPreview($input: MtwPalletGeometryPreviewInput!) {
|
|
2
|
+
mtwPalletGeometryPreview(input: $input) {
|
|
3
|
+
classification method units layoutStatus requestedQuantity placedQuantity
|
|
4
|
+
requestedPayloadMassG requestedGrossMassG
|
|
5
|
+
placements { packageId unit yawDegrees xMm yMm zMm widthMm lengthMm heightMm }
|
|
6
|
+
unplaced { packageId quantity }
|
|
7
|
+
bounds {
|
|
8
|
+
placedContainment { status reason }
|
|
9
|
+
placedNonOverlap { status reason }
|
|
10
|
+
permittedUprightYaw { status reason }
|
|
11
|
+
reportedSingleLayerHeight { status reason }
|
|
12
|
+
palletPayloadMass { status reason }
|
|
13
|
+
palletFootprintWithinLoadEnvelope { status reason }
|
|
14
|
+
loadedPalletGrossMass { status reason }
|
|
15
|
+
}
|
|
16
|
+
unsupported {
|
|
17
|
+
additionalPackingGeometry handling securing centreOfGravityAndStability
|
|
18
|
+
supportAndFloorContactLoads loadingAndUnloadingPath segregationAndCargoCompatibility
|
|
19
|
+
}
|
|
20
|
+
sources {
|
|
21
|
+
packages { packageId source { reference version } }
|
|
22
|
+
pallet { reference version } loadEnvelope { reference version }
|
|
23
|
+
additionalPacking { reference version } clearance { reference version }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|