sinfactura-types 1.10.433 → 1.10.435

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.
@@ -127,6 +127,11 @@ exports.isOverCycleCountToleranceRule = isOverCycleCountToleranceRule;
127
127
  * - `CYCLE_COUNT_TOLERANCE_EXCEEDED` · 403 — a finalise with at least one
128
128
  * over-tolerance line, from a caller who is neither ADMIN nor MANAGER. The
129
129
  * session stays `open` and NO stock moved.
130
+ * - `CYCLE_COUNT_LINES_UNCOUNTED` · 409 — a finalise of a session with at least
131
+ * one line carrying neither `countedAt` nor `skippedAt`; `data.productIds`
132
+ * names those products. Any role, and checked BEFORE the tolerance gate, so a
133
+ * caller gets this rather than the 403. The session stays `open` and NO stock
134
+ * moved: count the lines, or skip them, and finalise again.
130
135
  * - `CYCLE_COUNT_ID_UNUSABLE` · 500 — the minted id is not a legal `stocktakeId`.
131
136
  * Unreachable while the mint is `STK` + digits; published anyway because a
132
137
  * consumer that leaves it unmapped would show an operator an internal sentence.
@@ -187,6 +192,9 @@ exports.CYCLE_COUNT_ERROR_CODES = [
187
192
  // `main` rather than in either PR's own gate.
188
193
  'CYCLE_COUNT_CONTENT_CONFLICT',
189
194
  'CYCLE_COUNT_TOLERANCE_EXCEEDED',
195
+ // Finalise refuses a line that is neither counted nor skipped rather than
196
+ // applying it as counted 0; `data.productIds` names the products.
197
+ 'CYCLE_COUNT_LINES_UNCOUNTED',
190
198
  'CYCLE_COUNT_ID_UNUSABLE',
191
199
  'ROLE_FORBIDDEN',
192
200
  'VALIDATION_FAILED',
@@ -118,7 +118,13 @@ exports.SERVICE_TERMINAL_STATUSES = [
118
118
  * ⚠️ **`pending_receipt` is entered at intake and never re-entered.** It is
119
119
  * absent from every other row's list on purpose: equipment that has arrived
120
120
  * cannot un-arrive, and a ticket cannot be walked back to "not here yet" to
121
- * hide that it was received. That is the only new constraint this table adds.
121
+ * hide that it was received.
122
+ *
123
+ * ⚠️ **`on_site` is entered at intake, or from `pending_receipt` only.** The
124
+ * second edge is how an operator moves a visit ticket opened before `on_site`
125
+ * existed; nothing moves one automatically. Its only exit is `in_shop` — the
126
+ * technician bringing the equipment in. `returned`/`disposed` are unreachable
127
+ * from it because the shop never held what they would record handing back.
122
128
  *
123
129
  * ⚠️ **`returned` and `disposed` are NOT terminal, deliberately.** Custody is a
124
130
  * statement about physical fact, not a workflow, and the operator UI presents
@@ -130,7 +136,8 @@ exports.SERVICE_TERMINAL_STATUSES = [
130
136
  * about where someone else's property is.
131
137
  */
132
138
  exports.SERVICE_CUSTODY_TRANSITIONS = {
133
- pending_receipt: ['in_shop'],
139
+ pending_receipt: ['in_shop', 'on_site'],
140
+ on_site: ['in_shop'],
134
141
  in_shop: ['returned', 'disposed'],
135
142
  returned: ['in_shop', 'disposed'],
136
143
  disposed: ['in_shop', 'returned'],
@@ -421,6 +421,11 @@ declare global {
421
421
  /**
422
422
  * What the operator physically counted.
423
423
  *
424
+ * ⚠️ Only meaningful once `countedAt` is set. A line with neither
425
+ * `countedAt` nor `skippedAt` is UNCOUNTED whatever this holds — a fresh
426
+ * `mode: 'start'` line carries `0` here — and finalise refuses the whole
427
+ * session `409 CYCLE_COUNT_LINES_UNCOUNTED` rather than read it as counted 0.
428
+ *
424
429
  * ⚠️ Still required on a SKIPPED line (`skippedAt` set), where it carries
425
430
  * no meaning: send `0`. Finalise, the tolerance gate and every variance
426
431
  * ignore it there — a skipped line is never read as "counted zero".
@@ -438,7 +443,13 @@ declare global {
438
443
  lotId?: string;
439
444
  /** Why the line differs. ⚠️ Never personal data — it rides into an un-scrubbed, TTL-less row. */
440
445
  note?: string;
441
- /** Unix ms this line was captured. Its distance from `finalizedAt` is how stale `systemQuantity` is. */
446
+ /**
447
+ * Unix ms this line was captured. Its distance from `finalizedAt` is how stale `systemQuantity` is.
448
+ *
449
+ * ⚠️ It is also what marks the line COUNTED: a line with neither this nor
450
+ * `skippedAt` is uncounted, and finalise refuses the session
451
+ * `409 CYCLE_COUNT_LINES_UNCOUNTED` until every line has one or the other.
452
+ */
442
453
  countedAt?: number;
443
454
  /**
444
455
  * Unix ms the operator marked this line UNREACHABLE (a locked case, a
@@ -548,6 +559,11 @@ export declare const isOverCycleCountToleranceRule: (input: {
548
559
  * - `CYCLE_COUNT_TOLERANCE_EXCEEDED` · 403 — a finalise with at least one
549
560
  * over-tolerance line, from a caller who is neither ADMIN nor MANAGER. The
550
561
  * session stays `open` and NO stock moved.
562
+ * - `CYCLE_COUNT_LINES_UNCOUNTED` · 409 — a finalise of a session with at least
563
+ * one line carrying neither `countedAt` nor `skippedAt`; `data.productIds`
564
+ * names those products. Any role, and checked BEFORE the tolerance gate, so a
565
+ * caller gets this rather than the 403. The session stays `open` and NO stock
566
+ * moved: count the lines, or skip them, and finalise again.
551
567
  * - `CYCLE_COUNT_ID_UNUSABLE` · 500 — the minted id is not a legal `stocktakeId`.
552
568
  * Unreachable while the mint is `STK` + digits; published anyway because a
553
569
  * consumer that leaves it unmapped would show an operator an internal sentence.
@@ -595,5 +611,5 @@ export declare const isOverCycleCountToleranceRule: (input: {
595
611
  * ⚠️ Cross-cutting refusals are NOT repeated here — check `GATING_ERROR_CODES`
596
612
  * as well.
597
613
  */
598
- export declare const CYCLE_COUNT_ERROR_CODES: readonly ["CYCLE_COUNT_NOT_FOUND", "CYCLE_COUNT_NOT_OPEN", "CYCLE_COUNT_ILLEGAL_TRANSITION", "CYCLE_COUNT_VERSION_CONFLICT", "CYCLE_COUNT_CONTENT_CONFLICT", "CYCLE_COUNT_TOLERANCE_EXCEEDED", "CYCLE_COUNT_ID_UNUSABLE", "ROLE_FORBIDDEN", "VALIDATION_FAILED", "INVALID_CURSOR", "PRODUCT_NOT_FOUND", "STOCK_ADJUSTMENT_RACED", "CYCLE_COUNT_SCOPE_EMPTY", "CYCLE_COUNT_SCOPE_OVER_CAP", "CYCLE_COUNT_SCOPE_INCOMPLETE", "CYCLE_COUNT_SCOPE_CHANGED", "CYCLE_COUNT_START_KEY_REQUIRED", "CYCLE_COUNT_BASELINE_LOCKED", "CYCLE_COUNT_DEPOSITO_OVER_CAP", "WAREHOUSE_NOT_FOUND", "NO_PRIMARY_WAREHOUSE"];
614
+ export declare const CYCLE_COUNT_ERROR_CODES: readonly ["CYCLE_COUNT_NOT_FOUND", "CYCLE_COUNT_NOT_OPEN", "CYCLE_COUNT_ILLEGAL_TRANSITION", "CYCLE_COUNT_VERSION_CONFLICT", "CYCLE_COUNT_CONTENT_CONFLICT", "CYCLE_COUNT_TOLERANCE_EXCEEDED", "CYCLE_COUNT_LINES_UNCOUNTED", "CYCLE_COUNT_ID_UNUSABLE", "ROLE_FORBIDDEN", "VALIDATION_FAILED", "INVALID_CURSOR", "PRODUCT_NOT_FOUND", "STOCK_ADJUSTMENT_RACED", "CYCLE_COUNT_SCOPE_EMPTY", "CYCLE_COUNT_SCOPE_OVER_CAP", "CYCLE_COUNT_SCOPE_INCOMPLETE", "CYCLE_COUNT_SCOPE_CHANGED", "CYCLE_COUNT_START_KEY_REQUIRED", "CYCLE_COUNT_BASELINE_LOCKED", "CYCLE_COUNT_DEPOSITO_OVER_CAP", "WAREHOUSE_NOT_FOUND", "NO_PRIMARY_WAREHOUSE"];
599
615
  export type CycleCountErrorCode = (typeof CYCLE_COUNT_ERROR_CODES)[number];
@@ -122,6 +122,11 @@ export const isOverCycleCountToleranceRule = (input, tolerancePercent) => {
122
122
  * - `CYCLE_COUNT_TOLERANCE_EXCEEDED` · 403 — a finalise with at least one
123
123
  * over-tolerance line, from a caller who is neither ADMIN nor MANAGER. The
124
124
  * session stays `open` and NO stock moved.
125
+ * - `CYCLE_COUNT_LINES_UNCOUNTED` · 409 — a finalise of a session with at least
126
+ * one line carrying neither `countedAt` nor `skippedAt`; `data.productIds`
127
+ * names those products. Any role, and checked BEFORE the tolerance gate, so a
128
+ * caller gets this rather than the 403. The session stays `open` and NO stock
129
+ * moved: count the lines, or skip them, and finalise again.
125
130
  * - `CYCLE_COUNT_ID_UNUSABLE` · 500 — the minted id is not a legal `stocktakeId`.
126
131
  * Unreachable while the mint is `STK` + digits; published anyway because a
127
132
  * consumer that leaves it unmapped would show an operator an internal sentence.
@@ -182,6 +187,9 @@ export const CYCLE_COUNT_ERROR_CODES = [
182
187
  // `main` rather than in either PR's own gate.
183
188
  'CYCLE_COUNT_CONTENT_CONFLICT',
184
189
  'CYCLE_COUNT_TOLERANCE_EXCEEDED',
190
+ // Finalise refuses a line that is neither counted nor skipped rather than
191
+ // applying it as counted 0; `data.productIds` names the products.
192
+ 'CYCLE_COUNT_LINES_UNCOUNTED',
185
193
  'CYCLE_COUNT_ID_UNUSABLE',
186
194
  'ROLE_FORBIDDEN',
187
195
  'VALIDATION_FAILED',
@@ -132,8 +132,17 @@ declare global {
132
132
  * has not run yet. It is a starting state only: `equipmentReceived: false`
133
133
  * at intake is the one way to reach it, and NO transition leads back into
134
134
  * it, because equipment that has arrived cannot un-arrive.
135
+ *
136
+ * ⚠️ `on_site` means the work happens at the CUSTOMER's site and the shop
137
+ * never holds the equipment. Intake writes it for every ticket opened from a
138
+ * maintenance visit (`appointment`); a ticket opened before this state
139
+ * existed reaches it by the `pending_receipt → on_site` edit. It stays put
140
+ * through `ready → delivered` — the handover mints the order but records no
141
+ * handback, because nothing changed hands — and `abandoned_disposed` is
142
+ * refused, because the shop cannot dispose of property it never held. Its
143
+ * one exit is `in_shop`, for the technician bringing the equipment in.
135
144
  */
136
- type ServiceCustody = 'pending_receipt' | 'in_shop' | 'returned' | 'disposed';
145
+ type ServiceCustody = 'pending_receipt' | 'on_site' | 'in_shop' | 'returned' | 'disposed';
137
146
  /** Operator-set urgency, used for Kanban ordering and SLA hints. */
138
147
  type ServicePriority = 'low' | 'normal' | 'high' | 'urgent';
139
148
  /**
@@ -745,10 +754,12 @@ declare global {
745
754
  * Custody of the customer's property, independent of `status`. This is
746
755
  * what answers "which cancelled orders still have equipment here?".
747
756
  *
748
- * Two starting states, chosen at intake by `equipmentReceived`: the
749
- * default `in_shop` (the customer handed the equipment over), and
750
- * `pending_receipt` (the ticket was opened first — a phone booking, or a
751
- * pickup that has not run). ⚠️ A `pending_receipt` row is NOT holding
757
+ * Three starting states. Without an `appointment`, `equipmentReceived`
758
+ * chooses between the default `in_shop` (the customer handed the
759
+ * equipment over) and `pending_receipt` (the ticket was opened first — a
760
+ * phone booking, or a pickup that has not run). A ticket opened from a
761
+ * maintenance visit starts `on_site`: the work is done where the
762
+ * equipment is. ⚠️ Neither `pending_receipt` nor `on_site` is holding
752
763
  * anything, so a report answering "what is on our shelves" must filter
753
764
  * to `in_shop` rather than to "not returned and not disposed".
754
765
  */
@@ -766,14 +777,16 @@ declare global {
766
777
  * than printing none.
767
778
  *
768
779
  * One field rather than `returnedAt` + `disposedAt`, because
769
- * `custodyImpliedBy` moves custody on TWO terminals (`delivered` →
780
+ * the status machine moves custody on TWO terminals (`delivered` →
770
781
  * `returned`, `abandoned_disposed` → `disposed`) and disposing of
771
782
  * someone else's property is the event most likely to be disputed.
772
783
  * Paired with `custody` it answers both halves: which state the
773
784
  * property is in, and when it entered it.
774
785
  *
775
786
  * Server-written at all three custody writers — intake, the implied
776
- * terminals, and the operator-set edit. Absent on rows written before
787
+ * terminals, and the operator-set edit. ⚠️ NOT re-stamped when an `on_site`
788
+ * ticket is delivered: custody does not move there, so the stamp keeps
789
+ * dating the last real move. Absent on rows written before
777
790
  * this shipped; forward-only, nothing backfills them, and a reader must
778
791
  * tolerate absence by printing no date. A backfill would be a fabricated
779
792
  * record of when someone's property changed hands.
@@ -918,10 +931,15 @@ declare global {
918
931
  dated: number;
919
932
  /** Promised completion / pickup date shown to the customer. */
920
933
  promisedAt?: number;
921
- /** Unix ms the order reached `ready`. The unclaimed-equipment clock starts here. */
934
+ /**
935
+ * Unix ms the order reached `ready`. The unclaimed-equipment clock starts
936
+ * here — except on an `on_site` ticket, which has nothing on the shelf and
937
+ * is never unclaimed.
938
+ */
922
939
  readyAt?: number;
923
940
  /**
924
- * Unix ms of each "your equipment is ready" notification. A list because
941
+ * Unix ms of each "your equipment is ready" notification (its on-site
942
+ * variant, for an `on_site` ticket, asks for no pickup). A list because
925
943
  * you notify more than once before an unclaimed device is disposed of.
926
944
  */
927
945
  notifiedAt?: number[];
@@ -969,9 +987,11 @@ declare global {
969
987
  * staff clicked «abrir orden» on a `RECURRING_VISIT`, and the booking
970
988
  * carries this ticket back as `Appointment.serviceOrderId`.
971
989
  *
972
- * ⚠️ A ticket opened this way always starts `custody: 'pending_receipt'`:
973
- * the equipment is still at the customer's site, and intake refuses
974
- * `equipmentReceived: true` beside an `appointment`.
990
+ * ⚠️ A ticket opened this way starts `custody: 'on_site'`: the work is
991
+ * done at the customer's site, and intake refuses `equipmentReceived:
992
+ * true` beside an `appointment`. The on-site marker is that CUSTODY value,
993
+ * never this field — this link is removed when the visit is called off,
994
+ * while the ticket's custody stays what it was.
975
995
  *
976
996
  * ⚠️ **Live, not provenance.** A reschedule re-points all three fields at
977
997
  * the replacement booking; cancelling, skipping or a `NO_SHOW` REMOVES all
@@ -1084,14 +1104,17 @@ declare global {
1084
1104
  }
1085
1105
  /**
1086
1106
  * The `409` body of a refused CUSTODY move, with the custody states that ARE
1087
- * reachable — `SERVICE_CUSTODY_TRANSITIONS[current]`.
1107
+ * reachable — `SERVICE_CUSTODY_TRANSITIONS[current]` for an edit; for a status
1108
+ * refusal, only those of them from which that status would then succeed.
1088
1109
  *
1089
1110
  * - `CUSTODY_ILLEGAL_TRANSITION` — `mode: 'edit'` sent a `custody` the
1090
1111
  * current one cannot move to.
1091
- * - `SERVICE_ORDER_CUSTODY_NOT_RECEIVED` — `mode: 'status'` asked for
1092
- * `delivered` or `abandoned_disposed`, which move the customer's property,
1093
- * while the equipment was never received (`pending_receipt`). Receive it
1094
- * first; the status itself was not the problem.
1112
+ * - `SERVICE_ORDER_CUSTODY_NOT_RECEIVED` — `mode: 'status'` asked for a
1113
+ * status the current custody cannot close into: `delivered` or
1114
+ * `abandoned_disposed` while the equipment was never received
1115
+ * (`pending_receipt`), or `abandoned_disposed` on an `on_site` ticket,
1116
+ * whose property the shop never held. Receive it first; the status itself
1117
+ * was not the problem. (`delivered` on `on_site` is NOT refused.)
1095
1118
  */
1096
1119
  interface ServiceOrderCustodyRefusal {
1097
1120
  error: 'CUSTODY_ILLEGAL_TRANSITION' | 'SERVICE_ORDER_CUSTODY_NOT_RECEIVED';
@@ -1166,7 +1189,13 @@ export declare const SERVICE_TERMINAL_STATUSES: readonly ["delivered", "cancelle
1166
1189
  * ⚠️ **`pending_receipt` is entered at intake and never re-entered.** It is
1167
1190
  * absent from every other row's list on purpose: equipment that has arrived
1168
1191
  * cannot un-arrive, and a ticket cannot be walked back to "not here yet" to
1169
- * hide that it was received. That is the only new constraint this table adds.
1192
+ * hide that it was received.
1193
+ *
1194
+ * ⚠️ **`on_site` is entered at intake, or from `pending_receipt` only.** The
1195
+ * second edge is how an operator moves a visit ticket opened before `on_site`
1196
+ * existed; nothing moves one automatically. Its only exit is `in_shop` — the
1197
+ * technician bringing the equipment in. `returned`/`disposed` are unreachable
1198
+ * from it because the shop never held what they would record handing back.
1170
1199
  *
1171
1200
  * ⚠️ **`returned` and `disposed` are NOT terminal, deliberately.** Custody is a
1172
1201
  * statement about physical fact, not a workflow, and the operator UI presents
@@ -115,7 +115,13 @@ export const SERVICE_TERMINAL_STATUSES = [
115
115
  * ⚠️ **`pending_receipt` is entered at intake and never re-entered.** It is
116
116
  * absent from every other row's list on purpose: equipment that has arrived
117
117
  * cannot un-arrive, and a ticket cannot be walked back to "not here yet" to
118
- * hide that it was received. That is the only new constraint this table adds.
118
+ * hide that it was received.
119
+ *
120
+ * ⚠️ **`on_site` is entered at intake, or from `pending_receipt` only.** The
121
+ * second edge is how an operator moves a visit ticket opened before `on_site`
122
+ * existed; nothing moves one automatically. Its only exit is `in_shop` — the
123
+ * technician bringing the equipment in. `returned`/`disposed` are unreachable
124
+ * from it because the shop never held what they would record handing back.
119
125
  *
120
126
  * ⚠️ **`returned` and `disposed` are NOT terminal, deliberately.** Custody is a
121
127
  * statement about physical fact, not a workflow, and the operator UI presents
@@ -127,7 +133,8 @@ export const SERVICE_TERMINAL_STATUSES = [
127
133
  * about where someone else's property is.
128
134
  */
129
135
  export const SERVICE_CUSTODY_TRANSITIONS = {
130
- pending_receipt: ['in_shop'],
136
+ pending_receipt: ['in_shop', 'on_site'],
137
+ on_site: ['in_shop'],
131
138
  in_shop: ['returned', 'disposed'],
132
139
  returned: ['in_shop', 'disposed'],
133
140
  disposed: ['in_shop', 'returned'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sinfactura-types",
3
- "version": "1.10.433",
3
+ "version": "1.10.435",
4
4
  "description": "Shared wire and entity contracts for the SINFACTURA platform.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",