platform-types 1.10.710 → 1.10.712
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/cjs/purchaseReceipt.js +5 -2
- package/dist/purchaseReceipt.d.ts +81 -3
- package/dist/purchaseReceipt.js +5 -2
- package/dist/stock.d.ts +44 -0
- package/package.json +1 -1
|
@@ -40,10 +40,13 @@ exports.PURCHASE_RECEIPT_MAX_LINES_DEPOSITOS = 32;
|
|
|
40
40
|
* - `STOCK_INCOME_RACED` · 409 — the ledger-key retry budget ran out. Nothing moved.
|
|
41
41
|
* - `PRODUCT_NOT_FOUND` · 404 — a line's product no longer exists. Nothing moved.
|
|
42
42
|
* - `PURCHASE_RECEIPT_NOT_FOUND` · 404 — `mode=receipt` addressed nothing.
|
|
43
|
+
* - `PURCHASE_RECEIPT_KEY_CONFLICT` · 409 — the `receiptKey` already booked a
|
|
44
|
+
* receipt from a different body. Nothing moved; a new delivery needs a new key.
|
|
43
45
|
* - `INVALID_CURSOR` · 400
|
|
44
46
|
*
|
|
45
|
-
* A replay of a `receiptKey` that already committed is NOT a
|
|
46
|
-
* 200 with the stored receipt and `replayed: true`.
|
|
47
|
+
* A replay of a `receiptKey` that already committed with the SAME body is NOT a
|
|
48
|
+
* refusal: it answers 200 with the stored receipt and `replayed: true`. A receipt
|
|
49
|
+
* stored with no `requestFingerprint` replays whatever the body.
|
|
47
50
|
*
|
|
48
51
|
* ⚠️ Cross-cutting refusals are NOT repeated here — check `GATING_ERROR_CODES`
|
|
49
52
|
* as well.
|
|
@@ -75,7 +75,10 @@ declare global {
|
|
|
75
75
|
/** Original order FX stamp retained for receipt-linked valuation reversals. */
|
|
76
76
|
currencyValue?: number;
|
|
77
77
|
currencyValueAt?: number;
|
|
78
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Internal request identity for safe receipt-key retries, written on every
|
|
80
|
+
* receipt booked by `mode: 'receive'`. Opaque: a client must ignore it.
|
|
81
|
+
*/
|
|
79
82
|
requestFingerprint?: string;
|
|
80
83
|
updatedAt?: number;
|
|
81
84
|
/** Optimistic revision for cumulative receipt-linked reversals. */
|
|
@@ -99,6 +102,78 @@ declare global {
|
|
|
99
102
|
notes?: string;
|
|
100
103
|
items: PurchaseReceiptItem[];
|
|
101
104
|
}
|
|
105
|
+
/** The fields every receipt line carries, whichever quantity and cost it names. */
|
|
106
|
+
interface PurchaseReceiptLineBaseInput {
|
|
107
|
+
/** Index into the ordering `PurchaseOrder.items`; at most once per receipt. */
|
|
108
|
+
purchaseOrderItemIndex: number;
|
|
109
|
+
/** Batch the units arrived as — recorded as sent, see `PurchaseReceiptItem.lotId`. */
|
|
110
|
+
lotId?: string;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* How much arrived: EXACTLY one of the line's base-unit `quantity` or its
|
|
114
|
+
* `purchaseQuantity` in the order line's frozen purchase unit. Naming both, or
|
|
115
|
+
* neither, is `400 VALIDATION_FAILED`.
|
|
116
|
+
*/
|
|
117
|
+
type PurchaseReceiptLineQuantityInput = {
|
|
118
|
+
quantity: number;
|
|
119
|
+
purchaseQuantity?: never;
|
|
120
|
+
} | {
|
|
121
|
+
purchaseQuantity: number;
|
|
122
|
+
quantity?: never;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* What it cost: AT MOST one of `cost` (per base unit) or `purchaseUnitCost` (per
|
|
126
|
+
* purchase unit), both in the purchase order's currency. Naming neither values the
|
|
127
|
+
* line at the order line's own frozen `purchaseUnitCost` when it has one, else
|
|
128
|
+
* at its `unitCost`; naming both is `400 VALIDATION_FAILED`.
|
|
129
|
+
*/
|
|
130
|
+
type PurchaseReceiptLineCostInput = {
|
|
131
|
+
cost?: number;
|
|
132
|
+
purchaseUnitCost?: never;
|
|
133
|
+
} | {
|
|
134
|
+
purchaseUnitCost?: number;
|
|
135
|
+
cost?: never;
|
|
136
|
+
};
|
|
137
|
+
/** One line of a `PurchaseReceiptPostRequest`. */
|
|
138
|
+
type PurchaseReceiptLineInput = PurchaseReceiptLineBaseInput & PurchaseReceiptLineQuantityInput & PurchaseReceiptLineCostInput;
|
|
139
|
+
/**
|
|
140
|
+
* `POST /purchase-orders { mode: 'receive' }` — books one physical delivery
|
|
141
|
+
* against a purchase order, ADMIN or MANAGER only (`403 ADMIN_REQUIRED`).
|
|
142
|
+
* Refusals are `PURCHASE_RECEIPT_ERROR_CODES`.
|
|
143
|
+
*
|
|
144
|
+
* Idempotent on `receiptKey`: the same key with the same body answers the stored
|
|
145
|
+
* receipt with `replayed: true`; the same key with a different body is
|
|
146
|
+
* `409 PURCHASE_RECEIPT_KEY_CONFLICT`. EVERY body field binds the key, including
|
|
147
|
+
* one the server ignores (`warehouseId` on a store whose `stockAuthority` is not
|
|
148
|
+
* `'depositos'`), so a retry must resend the identical body; only `dated` sent as
|
|
149
|
+
* a string or a number, and an empty `notes` or none, count as the same. A
|
|
150
|
+
* receipt stored with no `requestFingerprint` replays whatever the body.
|
|
151
|
+
*
|
|
152
|
+
* Tied to its validator by `AssertPurchaseReceiptPostRequestMatchesSchema` in
|
|
153
|
+
* the api's `purchaseOrders/_receive.ts`.
|
|
154
|
+
*/
|
|
155
|
+
interface PurchaseReceiptPostRequest {
|
|
156
|
+
mode: 'receive';
|
|
157
|
+
/** `PO` + 6–10 digits. */
|
|
158
|
+
purchaseOrderId: string;
|
|
159
|
+
/** Client-minted UUID v4 — mint it once per delivery and reuse it on every retry. */
|
|
160
|
+
receiptKey: string;
|
|
161
|
+
/**
|
|
162
|
+
* The depósito the goods arrived at. Read only on a store whose
|
|
163
|
+
* `stockAuthority` is `'depositos'`; absent there means the primary.
|
|
164
|
+
* `WH` + 6–10 digits.
|
|
165
|
+
*/
|
|
166
|
+
warehouseId?: string;
|
|
167
|
+
/** `YYYYMMDD` day the goods arrived; absent means today. */
|
|
168
|
+
dated?: string | number;
|
|
169
|
+
/** At most 1000 characters. ⚠️ Never personal data. */
|
|
170
|
+
notes?: string;
|
|
171
|
+
/**
|
|
172
|
+
* 1..`PURCHASE_RECEIPT_MAX_LINES` lines (`PURCHASE_RECEIPT_MAX_LINES_DEPOSITOS`
|
|
173
|
+
* on a depósitos store), each naming a distinct `purchaseOrderItemIndex`.
|
|
174
|
+
*/
|
|
175
|
+
lines: PurchaseReceiptLineInput[];
|
|
176
|
+
}
|
|
102
177
|
}
|
|
103
178
|
/**
|
|
104
179
|
* Refusals from `POST /purchase-orders { mode: 'receive' }` and the receipt reads
|
|
@@ -124,10 +199,13 @@ declare global {
|
|
|
124
199
|
* - `STOCK_INCOME_RACED` · 409 — the ledger-key retry budget ran out. Nothing moved.
|
|
125
200
|
* - `PRODUCT_NOT_FOUND` · 404 — a line's product no longer exists. Nothing moved.
|
|
126
201
|
* - `PURCHASE_RECEIPT_NOT_FOUND` · 404 — `mode=receipt` addressed nothing.
|
|
202
|
+
* - `PURCHASE_RECEIPT_KEY_CONFLICT` · 409 — the `receiptKey` already booked a
|
|
203
|
+
* receipt from a different body. Nothing moved; a new delivery needs a new key.
|
|
127
204
|
* - `INVALID_CURSOR` · 400
|
|
128
205
|
*
|
|
129
|
-
* A replay of a `receiptKey` that already committed is NOT a
|
|
130
|
-
* 200 with the stored receipt and `replayed: true`.
|
|
206
|
+
* A replay of a `receiptKey` that already committed with the SAME body is NOT a
|
|
207
|
+
* refusal: it answers 200 with the stored receipt and `replayed: true`. A receipt
|
|
208
|
+
* stored with no `requestFingerprint` replays whatever the body.
|
|
131
209
|
*
|
|
132
210
|
* ⚠️ Cross-cutting refusals are NOT repeated here — check `GATING_ERROR_CODES`
|
|
133
211
|
* as well.
|
package/dist/purchaseReceipt.js
CHANGED
|
@@ -37,10 +37,13 @@ export const PURCHASE_RECEIPT_MAX_LINES_DEPOSITOS = 32;
|
|
|
37
37
|
* - `STOCK_INCOME_RACED` · 409 — the ledger-key retry budget ran out. Nothing moved.
|
|
38
38
|
* - `PRODUCT_NOT_FOUND` · 404 — a line's product no longer exists. Nothing moved.
|
|
39
39
|
* - `PURCHASE_RECEIPT_NOT_FOUND` · 404 — `mode=receipt` addressed nothing.
|
|
40
|
+
* - `PURCHASE_RECEIPT_KEY_CONFLICT` · 409 — the `receiptKey` already booked a
|
|
41
|
+
* receipt from a different body. Nothing moved; a new delivery needs a new key.
|
|
40
42
|
* - `INVALID_CURSOR` · 400
|
|
41
43
|
*
|
|
42
|
-
* A replay of a `receiptKey` that already committed is NOT a
|
|
43
|
-
* 200 with the stored receipt and `replayed: true`.
|
|
44
|
+
* A replay of a `receiptKey` that already committed with the SAME body is NOT a
|
|
45
|
+
* refusal: it answers 200 with the stored receipt and `replayed: true`. A receipt
|
|
46
|
+
* stored with no `requestFingerprint` replays whatever the body.
|
|
44
47
|
*
|
|
45
48
|
* ⚠️ Cross-cutting refusals are NOT repeated here — check `GATING_ERROR_CODES`
|
|
46
49
|
* as well.
|
package/dist/stock.d.ts
CHANGED
|
@@ -506,6 +506,50 @@ declare global {
|
|
|
506
506
|
/** Hydrated movement returned by readers after deriving ids from DynamoDB keys. */
|
|
507
507
|
interface StockSale extends StockBase, StockSaleWrite {
|
|
508
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* `data` of `GET /products?mode=stock&productId=…` — one product's ledger
|
|
511
|
+
* TOTALS, never its movement rows.
|
|
512
|
+
*
|
|
513
|
+
* - `incomes` — `Σ quantity` over the product's `INCOME#` rows.
|
|
514
|
+
* - `sales` — `Σ quantity` over the product's `SALE#` rows.
|
|
515
|
+
* - `stock` — on-hand, `incomes − sales`.
|
|
516
|
+
*
|
|
517
|
+
* Rows carrying `skip` are excluded from both sums.
|
|
518
|
+
*
|
|
519
|
+
* ⚠️ **`stock` is ABSENT exactly when the read truncated** — when either
|
|
520
|
+
* leg's paged read stopped short, which the envelope reports as top-level
|
|
521
|
+
* `truncated: true` ({@link ProductStockTotalsResponse}). It is never `0`
|
|
522
|
+
* or `null` in that case: a short read of either leg makes the difference
|
|
523
|
+
* WRONG, in either direction and by any amount, so absence is the signal.
|
|
524
|
+
* Read `stock === undefined` as "not computable from this read", never as
|
|
525
|
+
* zero. `incomes` and `sales` are still present then, but each is only the
|
|
526
|
+
* sum over the rows that were read — NOT a bound on the true leg total,
|
|
527
|
+
* because ledger quantities are signed (a service-part restore writes a
|
|
528
|
+
* negative-quantity `SALE#` row).
|
|
529
|
+
*
|
|
530
|
+
* A leg with no rows sums to `0`, never absent: a product with an empty
|
|
531
|
+
* ledger answers `incomes: 0, sales: 0, stock: 0` with `truncated: false`.
|
|
532
|
+
* The handler does not check that the product exists, so an unknown
|
|
533
|
+
* `productId` answers the same zeros rather than a 404.
|
|
534
|
+
*/
|
|
535
|
+
interface ProductStockTotals {
|
|
536
|
+
incomes: number;
|
|
537
|
+
sales: number;
|
|
538
|
+
stock?: number;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* The success envelope of `GET /products?mode=stock&productId=…`.
|
|
542
|
+
*
|
|
543
|
+
* `truncated` is always present: `true` exactly when `data.stock` is absent
|
|
544
|
+
* (see {@link ProductStockTotals}). There is no resume cursor — a truncated
|
|
545
|
+
* read means the on-hand figure is not computable from this endpoint, not
|
|
546
|
+
* that a next page exists.
|
|
547
|
+
*/
|
|
548
|
+
interface ProductStockTotalsResponse {
|
|
549
|
+
message: string;
|
|
550
|
+
data: ProductStockTotals;
|
|
551
|
+
truncated: boolean;
|
|
552
|
+
}
|
|
509
553
|
/**
|
|
510
554
|
* The body of one nightly stock-drift reconciliation run
|
|
511
555
|
* (`stacks/lambdas/stockReconciliationCron/index.ts`).
|