@operato/ops-contract 0.9.20 → 0.9.22
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/contract.js +1 -1
- package/dist/mes-profile.d.ts +0 -6
- package/dist/mes-profile.js +1 -7
- package/dist/operational-ingest.d.ts +6 -0
- package/dist/operational-ingest.js +24 -11
- package/dist-cjs/index.cjs +22 -19
- package/package.json +1 -1
package/dist/contract.js
CHANGED
|
@@ -1112,7 +1112,7 @@ export const OP_EVENT = {
|
|
|
1112
1112
|
* 승인은 자재를 움직이지 않는다. 자재가 트윈 밖으로 나가는 것은 EPCIS `shipping` 사건이다. 둘을 한 사건으로
|
|
1113
1113
|
* 접지 않는다 — 승인됐는데 안 나간 배치가 트윈이 보여야 할 상태다.
|
|
1114
1114
|
*/
|
|
1115
|
-
materialLot: 'material-lot.status',
|
|
1115
|
+
materialLot: 'material-lot.status', // 레코드: { lotId(표준 MaterialLotID = ItemState.epc 의 값), status, decidedBy?, reason?, decidedAt? }
|
|
1116
1116
|
/**
|
|
1117
1117
|
* **이 목록이 전부다** — 연결된 시스템이 현재 목록을 한 바퀴 다 보낸 뒤 그것을 알린다.
|
|
1118
1118
|
*
|
package/dist/mes-profile.d.ts
CHANGED
|
@@ -4,12 +4,6 @@ export declare const MES_BIZSTEP: {
|
|
|
4
4
|
readonly receiving: "urn:epcglobal:cbv:bizstep:receiving";
|
|
5
5
|
readonly producing: "urn:epcglobal:cbv:bizstep:commissioning";
|
|
6
6
|
readonly storing: "urn:epcglobal:cbv:bizstep:storing";
|
|
7
|
-
/**
|
|
8
|
-
* **출하** — CBV `shipping`: 물품이 시설을 떠난다. 자재를 트윈 밖으로 내는 것은 이 사건이다(disposition
|
|
9
|
-
* `in_transit`, readPoint = 출하 dock). 출하 **승인**은 이 사건이 아니라 로트 상태(`OP_EVENT.materialLot`)다 —
|
|
10
|
-
* 승인됐는데 안 나간 배치가 보여야 한다(2026-09-16, ADR-0046 곁).
|
|
11
|
-
*/
|
|
12
|
-
readonly shipping: "urn:epcglobal:cbv:bizstep:shipping";
|
|
13
7
|
};
|
|
14
8
|
/** 작업지시(Work Order) = 생산 오더 거래 유형. */
|
|
15
9
|
export declare const BTT_PRODORDER = "urn:epcglobal:cbv:btt:prodorder";
|
package/dist/mes-profile.js
CHANGED
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
export const MES_BIZSTEP = {
|
|
3
3
|
receiving: 'urn:epcglobal:cbv:bizstep:receiving', // 원자재 수령
|
|
4
4
|
producing: 'urn:epcglobal:cbv:bizstep:commissioning', // 생산(제품 최초 생성)
|
|
5
|
-
storing: 'urn:epcglobal:cbv:bizstep:storing'
|
|
6
|
-
/**
|
|
7
|
-
* **출하** — CBV `shipping`: 물품이 시설을 떠난다. 자재를 트윈 밖으로 내는 것은 이 사건이다(disposition
|
|
8
|
-
* `in_transit`, readPoint = 출하 dock). 출하 **승인**은 이 사건이 아니라 로트 상태(`OP_EVENT.materialLot`)다 —
|
|
9
|
-
* 승인됐는데 안 나간 배치가 보여야 한다(2026-09-16, ADR-0046 곁).
|
|
10
|
-
*/
|
|
11
|
-
shipping: 'urn:epcglobal:cbv:bizstep:shipping'
|
|
5
|
+
storing: 'urn:epcglobal:cbv:bizstep:storing' // 완제품 저장
|
|
12
6
|
};
|
|
13
7
|
/** 작업지시(Work Order) = 생산 오더 거래 유형. */
|
|
14
8
|
export const BTT_PRODORDER = 'urn:epcglobal:cbv:btt:prodorder';
|
|
@@ -65,6 +65,12 @@ export interface ObjectShape {
|
|
|
65
65
|
* 거부했다. 자원(설비·사람·자산)을 먼저 보고, 작업을 오더보다 먼저 본다.
|
|
66
66
|
*/
|
|
67
67
|
export declare function operationalKindOf(record: unknown): OperationalKind | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* 어느 갈래도 아닐 때의 거절 문장 — **표에서 세어 만든다.** 손으로 적은 목록은 다섯에서 멈춰 있었다(kind 는 열둘).
|
|
70
|
+
* 그 문장은 보내는 쪽이 실제로 읽는 것이라, 빠진 갈래는 「애초에 못 보내는 것」으로 읽힌다(2026-09-16, 인티그레이션
|
|
71
|
+
* 레인 측정). 여기서 만들면 kind 가 늘 때 문장도 함께 는다.
|
|
72
|
+
*/
|
|
73
|
+
export declare function unknownKindReason(): string;
|
|
68
74
|
/** 이 레코드가 운영 사실인가 — 호스트의 라우팅이 묻는 자리. */
|
|
69
75
|
/**
|
|
70
76
|
* 그 종류의 사실을 **무엇이 가리키나** — 선언된 identity 칸의 이름.
|
|
@@ -323,17 +323,19 @@ const SPECS = {
|
|
|
323
323
|
/*
|
|
324
324
|
* **로트 상태** — ISA-95 `MaterialLot.Status`. 출하 승인(batch release)이 들어오는 문(§`OP_EVENT.materialLot`).
|
|
325
325
|
*
|
|
326
|
-
* `disposition`(subjectId + decision) 과 필드가 겹치지 않는다 — 여기는 `
|
|
327
|
-
*
|
|
328
|
-
*
|
|
326
|
+
* `disposition`(subjectId + decision) 과 필드가 겹치지 않는다 — 여기는 `lotId` + `status`. 정체 이름이 표준
|
|
327
|
+
* `MaterialLotID` 인 이유가 하나 더 있다: 이 문은 `epc` 를 든 레코드를 EPCIS 어휘로 보고 받지 않는다
|
|
328
|
+
* (§`operationalKindOf`). 값은 로트의 식별자(`ItemState.epc` 와 같은 값)다. 상태 낱말은 열려 있어 enum 이
|
|
329
|
+
* 없다(오더 상태와 같은 규율). 로트 전체의 사실이므로 부분(`subLotId`)은 받지 않는다 — 부분마다 다른 상태가
|
|
330
|
+
* 필요해지면 그것은 다른 사실이다.
|
|
329
331
|
*/
|
|
330
332
|
'material-lot': {
|
|
331
333
|
eventType: OP_EVENT.materialLot,
|
|
332
|
-
match: ['
|
|
334
|
+
match: ['lotId', 'status'],
|
|
333
335
|
matchOrder: 85,
|
|
334
|
-
identity: '
|
|
335
|
-
required: ['
|
|
336
|
-
fields: {
|
|
336
|
+
identity: 'lotId',
|
|
337
|
+
required: ['lotId', 'status'],
|
|
338
|
+
fields: { lotId: 'string', status: 'string', decidedBy: 'string', reason: 'string', decidedAt: 'string', recordTime: 'string' }
|
|
337
339
|
},
|
|
338
340
|
test: {
|
|
339
341
|
eventType: OP_EVENT.test,
|
|
@@ -425,6 +427,20 @@ export function operationalKindOf(record) {
|
|
|
425
427
|
}
|
|
426
428
|
return undefined;
|
|
427
429
|
}
|
|
430
|
+
/**
|
|
431
|
+
* 어느 갈래도 아닐 때의 거절 문장 — **표에서 세어 만든다.** 손으로 적은 목록은 다섯에서 멈춰 있었다(kind 는 열둘).
|
|
432
|
+
* 그 문장은 보내는 쪽이 실제로 읽는 것이라, 빠진 갈래는 「애초에 못 보내는 것」으로 읽힌다(2026-09-16, 인티그레이션
|
|
433
|
+
* 레인 측정). 여기서 만들면 kind 가 늘 때 문장도 함께 는다.
|
|
434
|
+
*/
|
|
435
|
+
export function unknownKindReason() {
|
|
436
|
+
const shapes = MATCH_ORDER.map(kind => {
|
|
437
|
+
const spec = SPECS[kind];
|
|
438
|
+
const fields = spec.match ?? [spec.identity];
|
|
439
|
+
return `${kind}: ${fields.join('+')}`;
|
|
440
|
+
});
|
|
441
|
+
// vocabulary-guard: allow 거부 이유가 계약 필드 이름을 말한다
|
|
442
|
+
return `어느 운영 사실인지 모른다 — 갈래를 정하는 필드가 필요하다(${shapes.join(' · ')}). epc 를 든 레코드는 EPCIS 문으로 간다`;
|
|
443
|
+
}
|
|
428
444
|
/**
|
|
429
445
|
* 판정 순서 — 표의 `matchOrder` 에서 한 번만 만든다.
|
|
430
446
|
*
|
|
@@ -471,10 +487,7 @@ export function ingestOperationalRecords(records, opts) {
|
|
|
471
487
|
for (const record of arr) {
|
|
472
488
|
const kind = operationalKindOf(record);
|
|
473
489
|
if (!kind) {
|
|
474
|
-
rejected.push({
|
|
475
|
-
record,
|
|
476
|
-
errors: ['어느 운영 사실인지 모른다 — 정체 필드가 필요하다(taskId · moverId(+good=품질) · personId · assetId · orderId)'] // vocabulary-guard: allow 거부 이유가 계약 필드 이름을 말한다
|
|
477
|
-
});
|
|
490
|
+
rejected.push({ record, errors: [unknownKindReason()] });
|
|
478
491
|
continue;
|
|
479
492
|
}
|
|
480
493
|
const spec = SPECS[kind];
|
package/dist-cjs/index.cjs
CHANGED
|
@@ -230,6 +230,7 @@ __export(index_exports, {
|
|
|
230
230
|
transactionEvent: () => transactionEvent,
|
|
231
231
|
transformationEvent: () => transformationEvent,
|
|
232
232
|
unavailableVerdict: () => unavailableVerdict,
|
|
233
|
+
unknownKindReason: () => unknownKindReason,
|
|
233
234
|
validateDomainDefinition: () => validateDomainDefinition,
|
|
234
235
|
validateEpcisEvent: () => validateEpcisEvent,
|
|
235
236
|
validatePerformance: () => validatePerformance,
|
|
@@ -925,6 +926,7 @@ var OP_EVENT = {
|
|
|
925
926
|
* 접지 않는다 — 승인됐는데 안 나간 배치가 트윈이 보여야 할 상태다.
|
|
926
927
|
*/
|
|
927
928
|
materialLot: "material-lot.status",
|
|
929
|
+
// 레코드: { lotId(표준 MaterialLotID = ItemState.epc 의 값), status, decidedBy?, reason?, decidedAt? }
|
|
928
930
|
/**
|
|
929
931
|
* **이 목록이 전부다** — 연결된 시스템이 현재 목록을 한 바퀴 다 보낸 뒤 그것을 알린다.
|
|
930
932
|
*
|
|
@@ -1316,14 +1318,8 @@ var MES_BIZSTEP = {
|
|
|
1316
1318
|
// 원자재 수령
|
|
1317
1319
|
producing: "urn:epcglobal:cbv:bizstep:commissioning",
|
|
1318
1320
|
// 생산(제품 최초 생성)
|
|
1319
|
-
storing: "urn:epcglobal:cbv:bizstep:storing"
|
|
1321
|
+
storing: "urn:epcglobal:cbv:bizstep:storing"
|
|
1320
1322
|
// 완제품 저장
|
|
1321
|
-
/**
|
|
1322
|
-
* **출하** — CBV `shipping`: 물품이 시설을 떠난다. 자재를 트윈 밖으로 내는 것은 이 사건이다(disposition
|
|
1323
|
-
* `in_transit`, readPoint = 출하 dock). 출하 **승인**은 이 사건이 아니라 로트 상태(`OP_EVENT.materialLot`)다 —
|
|
1324
|
-
* 승인됐는데 안 나간 배치가 보여야 한다(2026-09-16, ADR-0046 곁).
|
|
1325
|
-
*/
|
|
1326
|
-
shipping: "urn:epcglobal:cbv:bizstep:shipping"
|
|
1327
1323
|
};
|
|
1328
1324
|
var BTT_PRODORDER = "urn:epcglobal:cbv:btt:prodorder";
|
|
1329
1325
|
function sgtinUri(companyPrefix, itemRef, serial) {
|
|
@@ -3713,17 +3709,19 @@ var SPECS = {
|
|
|
3713
3709
|
/*
|
|
3714
3710
|
* **로트 상태** — ISA-95 `MaterialLot.Status`. 출하 승인(batch release)이 들어오는 문(§`OP_EVENT.materialLot`).
|
|
3715
3711
|
*
|
|
3716
|
-
* `disposition`(subjectId + decision) 과 필드가 겹치지 않는다 — 여기는 `
|
|
3717
|
-
*
|
|
3718
|
-
*
|
|
3712
|
+
* `disposition`(subjectId + decision) 과 필드가 겹치지 않는다 — 여기는 `lotId` + `status`. 정체 이름이 표준
|
|
3713
|
+
* `MaterialLotID` 인 이유가 하나 더 있다: 이 문은 `epc` 를 든 레코드를 EPCIS 어휘로 보고 받지 않는다
|
|
3714
|
+
* (§`operationalKindOf`). 값은 로트의 식별자(`ItemState.epc` 와 같은 값)다. 상태 낱말은 열려 있어 enum 이
|
|
3715
|
+
* 없다(오더 상태와 같은 규율). 로트 전체의 사실이므로 부분(`subLotId`)은 받지 않는다 — 부분마다 다른 상태가
|
|
3716
|
+
* 필요해지면 그것은 다른 사실이다.
|
|
3719
3717
|
*/
|
|
3720
3718
|
"material-lot": {
|
|
3721
3719
|
eventType: OP_EVENT.materialLot,
|
|
3722
|
-
match: ["
|
|
3720
|
+
match: ["lotId", "status"],
|
|
3723
3721
|
matchOrder: 85,
|
|
3724
|
-
identity: "
|
|
3725
|
-
required: ["
|
|
3726
|
-
fields: {
|
|
3722
|
+
identity: "lotId",
|
|
3723
|
+
required: ["lotId", "status"],
|
|
3724
|
+
fields: { lotId: "string", status: "string", decidedBy: "string", reason: "string", decidedAt: "string", recordTime: "string" }
|
|
3727
3725
|
},
|
|
3728
3726
|
test: {
|
|
3729
3727
|
eventType: OP_EVENT.test,
|
|
@@ -3805,6 +3803,14 @@ function operationalKindOf(record) {
|
|
|
3805
3803
|
}
|
|
3806
3804
|
return void 0;
|
|
3807
3805
|
}
|
|
3806
|
+
function unknownKindReason() {
|
|
3807
|
+
const shapes = MATCH_ORDER.map((kind) => {
|
|
3808
|
+
const spec = SPECS[kind];
|
|
3809
|
+
const fields = spec.match ?? [spec.identity];
|
|
3810
|
+
return `${kind}: ${fields.join("+")}`;
|
|
3811
|
+
});
|
|
3812
|
+
return `\uC5B4\uB290 \uC6B4\uC601 \uC0AC\uC2E4\uC778\uC9C0 \uBAA8\uB978\uB2E4 \u2014 \uAC08\uB798\uB97C \uC815\uD558\uB294 \uD544\uB4DC\uAC00 \uD544\uC694\uD558\uB2E4(${shapes.join(" \xB7 ")}). epc \uB97C \uB4E0 \uB808\uCF54\uB4DC\uB294 EPCIS \uBB38\uC73C\uB85C \uAC04\uB2E4`;
|
|
3813
|
+
}
|
|
3808
3814
|
var MATCH_ORDER = Object.keys(SPECS).sort(
|
|
3809
3815
|
(a, b) => (SPECS[a].matchOrder ?? 1e3) - (SPECS[b].matchOrder ?? 1e3)
|
|
3810
3816
|
);
|
|
@@ -3822,11 +3828,7 @@ function ingestOperationalRecords(records, opts) {
|
|
|
3822
3828
|
for (const record of arr) {
|
|
3823
3829
|
const kind = operationalKindOf(record);
|
|
3824
3830
|
if (!kind) {
|
|
3825
|
-
rejected.push({
|
|
3826
|
-
record,
|
|
3827
|
-
errors: ["\uC5B4\uB290 \uC6B4\uC601 \uC0AC\uC2E4\uC778\uC9C0 \uBAA8\uB978\uB2E4 \u2014 \uC815\uCCB4 \uD544\uB4DC\uAC00 \uD544\uC694\uD558\uB2E4(taskId \xB7 moverId(+good=\uD488\uC9C8) \xB7 personId \xB7 assetId \xB7 orderId)"]
|
|
3828
|
-
// vocabulary-guard: allow 거부 이유가 계약 필드 이름을 말한다
|
|
3829
|
-
});
|
|
3831
|
+
rejected.push({ record, errors: [unknownKindReason()] });
|
|
3830
3832
|
continue;
|
|
3831
3833
|
}
|
|
3832
3834
|
const spec = SPECS[kind];
|
|
@@ -4790,6 +4792,7 @@ function nonEmpty(value) {
|
|
|
4790
4792
|
transactionEvent,
|
|
4791
4793
|
transformationEvent,
|
|
4792
4794
|
unavailableVerdict,
|
|
4795
|
+
unknownKindReason,
|
|
4793
4796
|
validateDomainDefinition,
|
|
4794
4797
|
validateEpcisEvent,
|
|
4795
4798
|
validatePerformance,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@operato/ops-contract",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.22",
|
|
4
4
|
"description": "Operations domain contract — the standard vocabulary that producers and readers agree on (EPCIS 2.0/GS1, ISA-95, IEC 61850/ISO 50001). Types, guards, validation. No state, no engine.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist-cjs/index.cjs",
|