@rytass/wms-module-graphql 0.9.0 → 0.10.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.
package/README.md CHANGED
@@ -9,7 +9,7 @@ Pair with [`@rytass/wms-module-core`](https://www.npmjs.com/package/@rytass/wms-
9
9
  ```bash
10
10
  yarn add @rytass/wms-module-graphql @rytass/wms-module-core
11
11
  # peer deps
12
- yarn add @nestjs/common @nestjs/graphql @nestjs/apollo @apollo/server graphql dataloader reflect-metadata rxjs
12
+ yarn add @nestjs/common @nestjs/graphql @nestjs/apollo @apollo/server @as-integrations/express5 graphql reflect-metadata rxjs @rytass/wms-base-nestjs-module
13
13
  ```
14
14
 
15
15
  ## Quick start
@@ -151,7 +151,8 @@ Because the switches mutate process-global registration state, a process may reg
151
151
  ## Inventory counting
152
152
 
153
153
  Compare the ledger against what is physically on the shelf, then adjust the ledger to
154
- match. Eleven operations (`inventory-count-order:{read,create,update,start,count,add-item,remove-item,reset,cancel,settle}`)
154
+ match. Twelve operations
155
+ (`inventory-count-order:{read,read-items,create,update,start,count,add-item,remove-item,reset,cancel,settle}`)
155
156
  back the flow:
156
157
 
157
158
  ```
@@ -173,6 +174,13 @@ back the flow:
173
174
  - **`countDate` is the business date.** The variance is reported under the date the count
174
175
  was created for, not the day settlement happened to run. `createdAt` keeps the real
175
176
  wall-clock time.
177
+ - **The sheet has its own query.** `inventoryCountOrderItems` paginates and filters the
178
+ lines (料號 / 物料說明 / 批號 / 儲位 / 載具 as ILIKE; 供應商 / 客戶 / 銷售訂單號碼 / 銷售訂單項次
179
+ exactly; plus `onlyVariance`). A field resolver cannot take a page or a filter, and a
180
+ count covering an aisle runs to thousands of lines.
181
+ - **`startInventoryCount` snapshots eight attributes per line** — `held`, `inShipment`,
182
+ `inspectionResult`, `transferId`, `vendorId`, `customerId`, `saleId`, `saleSerialId` —
183
+ so the sheet keeps showing the world the variance is measured against, not today's.
176
184
 
177
185
  ### ⚠️ Starting a count locks its locations
178
186
 
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ var eventEmitter = require('@nestjs/event-emitter');
4
+ var wmsModuleCore = require('@rytass/wms-module-core');
5
+
6
+ /**
7
+ * Bridges WMS domain events onto `@nestjs/event-emitter`'s `EventEmitter2`, so
8
+ * consumers can subscribe with `@OnEvent('wms.<event-name>')` instead of (or in
9
+ * addition to) the typed `afterListeners`.
10
+ *
11
+ * Pass this into `WmsModuleGraphqlModule.forRootAsync({ eventEmitterBridge })`.
12
+ * Importing this module is the only place `@nestjs/event-emitter` is referenced,
13
+ * keeping it an entirely optional peer dependency — the core graphql module
14
+ * never imports it.
15
+ *
16
+ * Prerequisite: register `EventEmitterModule.forRoot()` in your app so an
17
+ * `EventEmitter2` instance is available for injection.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { wmsEventEmitterBridgeProvider } from '@rytass/wms-module-graphql/adapters/event-emitter';
22
+ *
23
+ * WmsModuleGraphqlModule.forRootAsync({
24
+ * ...coreOptions,
25
+ * eventEmitterBridge: wmsEventEmitterBridgeProvider,
26
+ * });
27
+ *
28
+ * // elsewhere
29
+ * @OnEvent('wms.stock.allocated')
30
+ * handle(payload: WmsStockEventPayload) { ... }
31
+ * ```
32
+ */
33
+ const wmsEventEmitterBridgeProvider = {
34
+ provide: wmsModuleCore.WMS_EVENT_EMITTER_BRIDGE,
35
+ useFactory: (emitter) => ({
36
+ forward: (event) => {
37
+ emitter.emit(`wms.${event.name}`, event.payload);
38
+ },
39
+ }),
40
+ inject: [eventEmitter.EventEmitter2],
41
+ };
42
+
43
+ exports.wmsEventEmitterBridgeProvider = wmsEventEmitterBridgeProvider;
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+
3
+ var graphql = require('@nestjs/graphql');
4
+
5
+ const extractRequest = (context) => {
6
+ const gqlContext = graphql.GqlExecutionContext.create(context).getContext();
7
+ return gqlContext?.req;
8
+ };
9
+ /**
10
+ * Builds a {@link WmsAuthChecker} that authorizes WMS operations against
11
+ * `@rytass/member-base-nestjs-module`'s Casbin setup.
12
+ *
13
+ * It reuses everything the host's global `CasbinGuard` already prepared on the
14
+ * request — the verified `payload`, the `enforcer`, and even the consumer's
15
+ * (possibly customized) `casbinPermissionChecker`. WMS therefore enforces with
16
+ * the *exact same* policy engine and rules as the host's own resolvers, with no
17
+ * member-base import on the WMS side.
18
+ *
19
+ * Prerequisite: register `MemberBaseModule` with its global guard enabled
20
+ * (the default), and expose the request on the GraphQL context as `req`
21
+ * (Apollo's default).
22
+ */
23
+ const createMemberBaseWmsAuthChecker = (options) => {
24
+ return async (context, requirement) => {
25
+ const request = extractRequest(context);
26
+ // No verified member → unauthenticated. CasbinGuard sets `payload` to
27
+ // undefined when the token is missing or invalid.
28
+ if (!request?.payload) {
29
+ return false;
30
+ }
31
+ // The enforcer / checker are injected by CasbinGuard. Their absence means
32
+ // the host did not enable the global guard — fail closed rather than
33
+ // silently allowing everything.
34
+ if (!request.enforcer || !request.casbinPermissionChecker) {
35
+ return false;
36
+ }
37
+ const actions = options.map(requirement);
38
+ if (actions.length === 0) {
39
+ return false;
40
+ }
41
+ return request.casbinPermissionChecker({
42
+ enforcer: request.enforcer,
43
+ payload: request.payload,
44
+ actions,
45
+ });
46
+ };
47
+ };
48
+ /**
49
+ * A {@link WmsActorExtractor} for `@rytass/member-base-nestjs-module`. Reads the
50
+ * verified JWT payload that `CasbinGuard` attaches to the request and surfaces it
51
+ * as the event/hook `actor`. Returns `null` for unauthenticated requests.
52
+ *
53
+ * Pass to `WmsModuleGraphqlModule.forRootAsync({ actorExtractor })` so every
54
+ * operation event and before-hook carries `{ id, domain }`. Imports nothing from
55
+ * member-base — it only reads the runtime request fields.
56
+ */
57
+ const memberBaseActorExtractor = (context) => {
58
+ const payload = extractRequest(context)?.payload;
59
+ if (!payload?.id) {
60
+ return null;
61
+ }
62
+ return { id: payload.id, domain: payload.domain, raw: payload };
63
+ };
64
+
65
+ exports.createMemberBaseWmsAuthChecker = createMemberBaseWmsAuthChecker;
66
+ exports.memberBaseActorExtractor = memberBaseActorExtractor;
@@ -74,7 +74,8 @@ static forRootAsync(
74
74
  **盤點單 (Inventory Count Order)**
75
75
  - `InventoryCountOrderEntity` / `InventoryCountOrderEntityClass` — 盤點單主檔(`countDate` 為盤差記帳基準日)
76
76
  - `InventoryCountOrderLocationEntity` / `InventoryCountOrderLocationEntityClass` — 盤點範圍,已展開至所有下層儲位(`isRoot` 標示使用者實際選的節點)
77
- - `InventoryCountOrderItemEntity` / `InventoryCountOrderItemEntityClass` — 盤點單身(`isAddition` 標示盤盈新增列)
77
+ - `InventoryCountOrderItemEntity` / `InventoryCountOrderItemEntityClass` — 盤點單身(`isAddition` 標示盤盈新增列)。
78
+ 開始盤點時另外快照 8 個當下屬性:`held`、`inShipment`、`inspectionResult`、`transferId`、`vendorId`、`customerId`、`saleId`、`saleSerialId`
78
79
  - `InventoryCountOrderChangeEntity` / `InventoryCountOrderChangeEntityClass` — 盤差異動(正=盤盈、負=盤虧)
79
80
  - 來源: `lib/models/extensions/inventory-count-order/`
80
81
 
@@ -178,7 +179,7 @@ static forRootAsync(
178
179
  | `MergeBatchOrderService<T>` | `merge` | 批號合併 | `lib/services/merge-batch-order.service.ts` |
179
180
  | `ReclassifyInventoryOrderService<T>` | `reclassify` | 庫存轉料號 | `lib/services/reclassify-inventory-order.service.ts` |
180
181
  | `ScrapeService<T>` | `scrape` | 庫存報廢 | `lib/services/scrape.service.ts` |
181
- | `InventoryCountOrderService<T>` | `findMany`, `findOne`, `create`, `update`, `start`, `count`, `addItem`, `removeItem`, `reset`, `cancel`, `settle` | 盤點單管理。`start` 會鎖定盤點範圍內的儲位,`settle` 是唯一會改動庫存的方法 | `lib/services/inventory-count-order.service.ts` |
182
+ | `InventoryCountOrderService<T>` | `findMany`, `findOne`, `findItems`, `create`, `update`, `start`, `count`, `addItem`, `removeItem`, `reset`, `cancel`, `settle` | 盤點單管理。`start` 會鎖定盤點範圍內的儲位,`settle` 是唯一會改動庫存的方法。`findItems` 是單身的分頁查詢 | `lib/services/inventory-count-order.service.ts` |
182
183
  | `QualityInspectionOrderService<T>` | `inspect` | 品質檢驗 | `lib/services/quality-inspection-order.service.ts` |
183
184
  | `SaleOrderService` | (DataSource 直接操作) | 銷售訂單查詢 | `lib/services/sale-order.service.ts` |
184
185
  | `HelperService` | `getSeq` | 序號產生工具 | `lib/services/helper.service.ts` |
@@ -286,7 +287,7 @@ enum InventoryCountStatus { PENDING, COUNTING, COMPLETED, CANCELLED }
286
287
 
287
288
  ### `WmsOperationName` (type) / `WMS_OPERATION_NAMES` (const)
288
289
  ```ts
289
- type WmsOperationName = 'material:read' | 'material:create' | /* …58 項… */;
290
+ type WmsOperationName = 'material:read' | 'material:create' | /* …59 項… */;
290
291
  const WMS_OPERATION_NAMES: readonly WmsOperationName[];
291
292
  ```
292
293
  本套件所有 root operation 的 `` `${resource}:${action}` `` 名稱。同一組字串有三個用途:
@@ -627,7 +628,7 @@ WmsModuleGraphqlModule.forRootAsync({
627
628
  | `MaterialQuery` | `material`, `materials` | `MaterialDto`, `Materials` | `lib/queries/material.query.ts` |
628
629
  | `StockQuery` | `stock`, `stocks`, `stocksGroupedByMaterial`, `aggregatedStocks`, `changeLogs` | `StockDto`, `Stocks`, `Materials`, `AggregatedStocks`, `ChangeLogs` | `lib/queries/stock.query.ts` |
629
630
  | `BatchQuery` | `batches` | `Batches` | `lib/queries/batch.query.ts` |
630
- | `InventoryCountOrderQuery` | `inventoryCountOrders`, `inventoryCountOrder` | `InventoryCountOrders`, `InventoryCountOrderDto` | `lib/queries/inventory-count-order.query.ts` |
631
+ | `InventoryCountOrderQuery` | `inventoryCountOrders`, `inventoryCountOrder`, `inventoryCountOrderItems` | `InventoryCountOrders`, `InventoryCountOrderDto`, `InventoryCountOrderItems` | `lib/queries/inventory-count-order.query.ts` |
631
632
  | `LocationQuery` | `location`, `locations` | `LocationDto`, `Locations` | `lib/queries/location.query.ts` |
632
633
  | `ReceiveInventoryOrderQuery` | `receiveInventoryOrder`, `receiveInventoryOrders` | `ReceiveInventoryOrderDto`, `ReceiveInventoryOrders` | `lib/queries/recieve-inventory-order.query.ts` |
633
634
  | `ShipInventoryOrderQuery` | `shipInventoryOrder`, `shipInventoryOrders` | `ShipInventoryOrderDto`, `ShipInventoryOrders` | `lib/queries/ship-inventory-order.query.ts` |
@@ -735,6 +736,9 @@ WmsModuleGraphqlModule.forRootAsync({
735
736
 
736
737
  **庫存**
737
738
  - `FindStockArgs` (extends `PaginationArgs`) — 來源: `lib/inputs/find-stock.args.ts`
739
+ - `materialKey` / `batchKey` / `locationKey` / `loaderKey` 為 ILIKE 模糊比對。
740
+ - `vendorId` / `customerId` / `saleId` / `saleSerialId` 取自批號,為**完全比對**:它們是上游系統的識別碼,
741
+ 模糊比對會讓 `SO-1` 撈到 `SO-10`。四支 stock query(`stocks`、`stocksGroupedByMaterial`、`aggregatedStocks`、`changeLogs`)皆適用。
738
742
  - `DateRangeArgs` (`@InputType`) — 來源: `lib/inputs/date-range.args.ts`
739
743
 
740
744
  **批號**
@@ -348,23 +348,37 @@ The `DEFAULT` location/loader/loaderType returned by the second query was seeded
348
348
 
349
349
  ### 4.6 Version compatibility matrix
350
350
 
351
- Tested combinations as of 0.1.2:
352
-
353
- | Package | Tested version | Notes |
354
- |----------------------------------|-------------------------|--------------------------------------------------------------------|
355
- | NestJS (`@nestjs/*`) | 11.x | 10.x untested; some Express-5-related APIs differ |
356
- | `@nestjs/graphql` | 13.x | Required for `@nestjs/apollo@13` |
357
- | `@nestjs/apollo` | 13.0.x – 13.1.x | 13.2+ requires @apollo/server@^5 which breaks lib's v4 plugin usage |
358
- | `@apollo/server` | 4.x | v5 is breaking; not supported |
359
- | `@as-integrations/express5` | 1.x | Required runtime peer |
360
- | TypeORM | 0.3.x | 0.2.x not supported (different DataSource API) |
361
- | PostgreSQL | 14+ | Native dialect |
362
- | `graphql` | 16.x | 15.x untested |
363
- | Node.js | 20 LTS or 22 | 18 deprecated, 24 untested |
364
- | Next.js | 15.x Pages Router | App Router unsupported (see §13.11) |
365
- | React | 19.x | 18.x likely works but untested |
366
- | `@mezzanine-ui/react` | 1.3.x | Earlier versions untested |
367
- | `nuqs` | 2.x (Pages adapter) | App Router adapter untested (see §13.11) |
351
+ Two different questions, so two columns. **Accepted** is what the published
352
+ `peerDependencies` allow — a version outside it fails to install. **Tested** is the
353
+ combination CI and the integration suite actually run against; anything else is
354
+ accepted because nothing in the lib's use of that package is known to break, not
355
+ because it was exercised.
356
+
357
+ | Package | Accepted (peer range) | Tested | Notes |
358
+ |-----------------------------|---------------------------|-------------------|-----------------------------------------------------------------------------|
359
+ | `@nestjs/common` / `core` | 10.x – 11.x | 11.1.x | **12.x is not accepted**: the whole NestJS 12 line is ESM-only, which this package (dual CJS+ESM) and a webpack-bundled host cannot consume yet |
360
+ | `@nestjs/graphql` | 13.x | 13.4.5 | 14.x requires NestJS 12 (ESM-only) |
361
+ | `@nestjs/apollo` | 13.x | 13.4.5 | The lib itself imports neither `@nestjs/apollo` nor `@apollo/server`; the peer records that a driver is required |
362
+ | `@apollo/server` | not a peer | 5.5.1 | Chosen by your driver, not by this package |
363
+ | `@as-integrations/express5` | 1.x | 1.1.2 | Required runtime peer of the Express driver |
364
+ | `graphql` | 16.x | 16.12.0 | 17.x is blocked upstream — `@apollo/server@5` still peers `^16.11.0` |
365
+ | `rxjs` | 7.x | 7.8.x | One import (the mutation interceptor) |
366
+ | `@nestjs/event-emitter` | 2.x – 3.x (optional) | 3.1.0 | The next release is 12.x, which is ESM-only |
367
+ | `@rytass/wms-base-nestjs-module` | 0.4.x | 0.4.0 | Imported directly by this package (the warehouse-map mutation and the location resolver) and shared with `@rytass/wms-module-core`. Declared as a peer since 0.10.0 so both see the same copy |
368
+ | TypeORM | 0.3.x | 0.3.28 | 0.2.x not supported (different DataSource API) |
369
+ | PostgreSQL | — | 14+ | Native dialect |
370
+ | Node.js | — | 20 LTS / 22 | 18 deprecated, 24 untested |
371
+ | Next.js | 15.x – 16.x | 15.5.x Pages | Only `next/router` and `next/navigation` are used; App Router unsupported (see §13.11) |
372
+ | React / React DOM | 18.x – 19.x | 19.2.x | No React-19-only API is used, and `@mezzanine-ui/react` peers `>=18` |
373
+ | `@mezzanine-ui/react` | 1.x | 1.4.1 | `@mezzanine-ui/system` is required — 45 of the lib's SCSS files `@use` it |
374
+ | `nuqs` | 2.x | 2.8.x | Pages adapter; App Router adapter untested (see §13.11) |
375
+
376
+ > **Why `@apollo/server` v5 is fine.** An earlier release pinned `@nestjs/apollo` to
377
+ > `>=13.0.0 <13.2.0`, on the grounds that 13.2+ moved to `@apollo/server@^5` and "the lib
378
+ > uses v4 plugin APIs". That was wrong: the only `ApolloServerPluginLandingPageLocalDefault`
379
+ > usage lives in this repo's own `apps/api`, never in the published package, which imports
380
+ > nothing from `@apollo/server` at all. The pin excluded the very combination the test suite
381
+ > runs on, so it has been widened back to `^13.0.0`.
368
382
 
369
383
  ---
370
384
 
package/index.cjs.js CHANGED
@@ -1760,6 +1760,20 @@ let InventoryCountOrderItemDto = class InventoryCountOrderItemDto {
1760
1760
  countedQuantity;
1761
1761
  /** `true` for a line added during counting for stock the books never had. */
1762
1762
  isAddition;
1763
+ // ── Batch attributes as they were when counting started ────────────────── //
1764
+ //
1765
+ // Snapshotted rather than resolved live: the batch keeps moving after a count
1766
+ // settles, and a sheet that silently rewrites itself months later stops being
1767
+ // a record of what was found. `null` before counting has started.
1768
+ held;
1769
+ inShipment;
1770
+ /** `null` = awaiting inspection, `false` = failed, `true` = passed. */
1771
+ inspectionResult;
1772
+ transferId;
1773
+ vendorId;
1774
+ customerId;
1775
+ saleId;
1776
+ saleSerialId;
1763
1777
  };
1764
1778
  tslib.__decorate([
1765
1779
  graphql.Field(() => graphql.ID),
@@ -1801,6 +1815,38 @@ tslib.__decorate([
1801
1815
  graphql.Field(() => Boolean),
1802
1816
  tslib.__metadata("design:type", Boolean)
1803
1817
  ], InventoryCountOrderItemDto.prototype, "isAddition", void 0);
1818
+ tslib.__decorate([
1819
+ graphql.Field(() => Boolean, { nullable: true }),
1820
+ tslib.__metadata("design:type", Object)
1821
+ ], InventoryCountOrderItemDto.prototype, "held", void 0);
1822
+ tslib.__decorate([
1823
+ graphql.Field(() => Boolean, { nullable: true }),
1824
+ tslib.__metadata("design:type", Object)
1825
+ ], InventoryCountOrderItemDto.prototype, "inShipment", void 0);
1826
+ tslib.__decorate([
1827
+ graphql.Field(() => Boolean, { nullable: true }),
1828
+ tslib.__metadata("design:type", Object)
1829
+ ], InventoryCountOrderItemDto.prototype, "inspectionResult", void 0);
1830
+ tslib.__decorate([
1831
+ graphql.Field(() => graphql.ID, { nullable: true }),
1832
+ tslib.__metadata("design:type", Object)
1833
+ ], InventoryCountOrderItemDto.prototype, "transferId", void 0);
1834
+ tslib.__decorate([
1835
+ graphql.Field(() => String, { nullable: true }),
1836
+ tslib.__metadata("design:type", Object)
1837
+ ], InventoryCountOrderItemDto.prototype, "vendorId", void 0);
1838
+ tslib.__decorate([
1839
+ graphql.Field(() => String, { nullable: true }),
1840
+ tslib.__metadata("design:type", Object)
1841
+ ], InventoryCountOrderItemDto.prototype, "customerId", void 0);
1842
+ tslib.__decorate([
1843
+ graphql.Field(() => String, { nullable: true }),
1844
+ tslib.__metadata("design:type", Object)
1845
+ ], InventoryCountOrderItemDto.prototype, "saleId", void 0);
1846
+ tslib.__decorate([
1847
+ graphql.Field(() => String, { nullable: true }),
1848
+ tslib.__metadata("design:type", Object)
1849
+ ], InventoryCountOrderItemDto.prototype, "saleSerialId", void 0);
1804
1850
  InventoryCountOrderItemDto = tslib.__decorate([
1805
1851
  graphql.ObjectType()
1806
1852
  ], InventoryCountOrderItemDto);
@@ -1868,6 +1914,11 @@ tslib.__decorate([
1868
1914
  InventoryCountOrderDto = tslib.__decorate([
1869
1915
  graphql.ObjectType()
1870
1916
  ], InventoryCountOrderDto);
1917
+ let InventoryCountOrderItems = class InventoryCountOrderItems extends PaginatedResponse(InventoryCountOrderItemDto) {
1918
+ };
1919
+ InventoryCountOrderItems = tslib.__decorate([
1920
+ graphql.ObjectType()
1921
+ ], InventoryCountOrderItems);
1871
1922
  let InventoryCountOrders = class InventoryCountOrders extends PaginatedResponse(InventoryCountOrderDto) {
1872
1923
  };
1873
1924
  InventoryCountOrders = tslib.__decorate([
@@ -2556,6 +2607,15 @@ let FindStockArgs = class FindStockArgs extends PaginationArgs {
2556
2607
  batchKey;
2557
2608
  locationKey;
2558
2609
  loaderKey;
2610
+ /**
2611
+ * Commercial attributes carried by the stock's batch. Matched exactly — they
2612
+ * are identifiers from the host system, and a partial match on 「SO-1」 would
2613
+ * quietly sweep in 「SO-10」.
2614
+ */
2615
+ vendorId;
2616
+ customerId;
2617
+ saleId;
2618
+ saleSerialId;
2559
2619
  };
2560
2620
  tslib.__decorate([
2561
2621
  graphql.Field(() => graphql.ID, { nullable: true }),
@@ -2601,6 +2661,22 @@ tslib.__decorate([
2601
2661
  graphql.Field(() => String, { nullable: true }),
2602
2662
  tslib.__metadata("design:type", String)
2603
2663
  ], FindStockArgs.prototype, "loaderKey", void 0);
2664
+ tslib.__decorate([
2665
+ graphql.Field(() => String, { nullable: true }),
2666
+ tslib.__metadata("design:type", String)
2667
+ ], FindStockArgs.prototype, "vendorId", void 0);
2668
+ tslib.__decorate([
2669
+ graphql.Field(() => String, { nullable: true }),
2670
+ tslib.__metadata("design:type", String)
2671
+ ], FindStockArgs.prototype, "customerId", void 0);
2672
+ tslib.__decorate([
2673
+ graphql.Field(() => String, { nullable: true }),
2674
+ tslib.__metadata("design:type", String)
2675
+ ], FindStockArgs.prototype, "saleId", void 0);
2676
+ tslib.__decorate([
2677
+ graphql.Field(() => String, { nullable: true }),
2678
+ tslib.__metadata("design:type", String)
2679
+ ], FindStockArgs.prototype, "saleSerialId", void 0);
2604
2680
  FindStockArgs = tslib.__decorate([
2605
2681
  graphql.ArgsType()
2606
2682
  ], FindStockArgs);
@@ -6991,6 +7067,67 @@ tslib.__decorate([
6991
7067
  FindInventoryCountOrderArgs = tslib.__decorate([
6992
7068
  graphql.ArgsType()
6993
7069
  ], FindInventoryCountOrderArgs);
7070
+ let FindInventoryCountOrderItemArgs = class FindInventoryCountOrderItemArgs extends PaginationArgs {
7071
+ id;
7072
+ materialKey;
7073
+ materialDescription;
7074
+ batchKey;
7075
+ locationKey;
7076
+ loaderSerialId;
7077
+ vendorId;
7078
+ customerId;
7079
+ saleId;
7080
+ saleSerialId;
7081
+ /** Only lines whose counted quantity differs from the books (顯示盤差項目). */
7082
+ onlyVariance;
7083
+ };
7084
+ tslib.__decorate([
7085
+ graphql.Field(() => graphql.ID),
7086
+ tslib.__metadata("design:type", String)
7087
+ ], FindInventoryCountOrderItemArgs.prototype, "id", void 0);
7088
+ tslib.__decorate([
7089
+ graphql.Field(() => String, { nullable: true }),
7090
+ tslib.__metadata("design:type", String)
7091
+ ], FindInventoryCountOrderItemArgs.prototype, "materialKey", void 0);
7092
+ tslib.__decorate([
7093
+ graphql.Field(() => String, { nullable: true }),
7094
+ tslib.__metadata("design:type", String)
7095
+ ], FindInventoryCountOrderItemArgs.prototype, "materialDescription", void 0);
7096
+ tslib.__decorate([
7097
+ graphql.Field(() => String, { nullable: true }),
7098
+ tslib.__metadata("design:type", String)
7099
+ ], FindInventoryCountOrderItemArgs.prototype, "batchKey", void 0);
7100
+ tslib.__decorate([
7101
+ graphql.Field(() => String, { nullable: true }),
7102
+ tslib.__metadata("design:type", String)
7103
+ ], FindInventoryCountOrderItemArgs.prototype, "locationKey", void 0);
7104
+ tslib.__decorate([
7105
+ graphql.Field(() => String, { nullable: true }),
7106
+ tslib.__metadata("design:type", String)
7107
+ ], FindInventoryCountOrderItemArgs.prototype, "loaderSerialId", void 0);
7108
+ tslib.__decorate([
7109
+ graphql.Field(() => String, { nullable: true }),
7110
+ tslib.__metadata("design:type", String)
7111
+ ], FindInventoryCountOrderItemArgs.prototype, "vendorId", void 0);
7112
+ tslib.__decorate([
7113
+ graphql.Field(() => String, { nullable: true }),
7114
+ tslib.__metadata("design:type", String)
7115
+ ], FindInventoryCountOrderItemArgs.prototype, "customerId", void 0);
7116
+ tslib.__decorate([
7117
+ graphql.Field(() => String, { nullable: true }),
7118
+ tslib.__metadata("design:type", String)
7119
+ ], FindInventoryCountOrderItemArgs.prototype, "saleId", void 0);
7120
+ tslib.__decorate([
7121
+ graphql.Field(() => String, { nullable: true }),
7122
+ tslib.__metadata("design:type", String)
7123
+ ], FindInventoryCountOrderItemArgs.prototype, "saleSerialId", void 0);
7124
+ tslib.__decorate([
7125
+ graphql.Field(() => Boolean, { nullable: true }),
7126
+ tslib.__metadata("design:type", Boolean)
7127
+ ], FindInventoryCountOrderItemArgs.prototype, "onlyVariance", void 0);
7128
+ FindInventoryCountOrderItemArgs = tslib.__decorate([
7129
+ graphql.ArgsType()
7130
+ ], FindInventoryCountOrderItemArgs);
6994
7131
  let CreateInventoryCountOrderArgs = class CreateInventoryCountOrderArgs {
6995
7132
  name;
6996
7133
  countDate;
@@ -7149,6 +7286,17 @@ let InventoryCountOrderQuery = class InventoryCountOrderQuery {
7149
7286
  const [items, total] = await this.inventoryCountOrderService.findMany(args);
7150
7287
  return { items, total };
7151
7288
  }
7289
+ /**
7290
+ * The count sheet, filtered and paginated.
7291
+ *
7292
+ * Separate from `inventoryCountOrder.items` on purpose: the detail page needs
7293
+ * to narrow thousands of lines down to the shelf in front of the operator, and
7294
+ * a field resolver cannot take filters or a page.
7295
+ */
7296
+ async inventoryCountOrderItems({ id, ...options }) {
7297
+ const [items, total] = await this.inventoryCountOrderService.findItems(id, options);
7298
+ return { items, total };
7299
+ }
7152
7300
  async inventoryCountOrder({ id }) {
7153
7301
  return this.inventoryCountOrderService.findOne(id);
7154
7302
  }
@@ -7161,6 +7309,14 @@ tslib.__decorate([
7161
7309
  tslib.__metadata("design:paramtypes", [FindInventoryCountOrderArgs]),
7162
7310
  tslib.__metadata("design:returntype", Promise)
7163
7311
  ], InventoryCountOrderQuery.prototype, "inventoryCountOrders", null);
7312
+ tslib.__decorate([
7313
+ graphql.Query(() => InventoryCountOrderItems),
7314
+ WmsOperation({ resource: 'inventory-count-order', action: 'read-items' }),
7315
+ tslib.__param(0, graphql.Args()),
7316
+ tslib.__metadata("design:type", Function),
7317
+ tslib.__metadata("design:paramtypes", [FindInventoryCountOrderItemArgs]),
7318
+ tslib.__metadata("design:returntype", Promise)
7319
+ ], InventoryCountOrderQuery.prototype, "inventoryCountOrderItems", null);
7164
7320
  tslib.__decorate([
7165
7321
  graphql.Query(() => InventoryCountOrderDto),
7166
7322
  WmsOperation({ resource: 'inventory-count-order', action: 'read' }),
@@ -21,6 +21,15 @@ export declare class InventoryCountOrderItemDto {
21
21
  countedQuantity: number | null;
22
22
  /** `true` for a line added during counting for stock the books never had. */
23
23
  isAddition: boolean;
24
+ held: boolean | null;
25
+ inShipment: boolean | null;
26
+ /** `null` = awaiting inspection, `false` = failed, `true` = passed. */
27
+ inspectionResult: boolean | null;
28
+ transferId: string | null;
29
+ vendorId: string | null;
30
+ customerId: string | null;
31
+ saleId: string | null;
32
+ saleSerialId: string | null;
24
33
  }
25
34
  /**
26
35
  * A count order. `items` and `locations` come from
@@ -43,6 +52,9 @@ export declare class InventoryCountOrderDto {
43
52
  cancelledAt: Date | null;
44
53
  createdAt: Date;
45
54
  }
55
+ declare const InventoryCountOrderItems_base: new () => import("./pagination.dto").PaginatedType<InventoryCountOrderItemDto>;
56
+ export declare class InventoryCountOrderItems extends InventoryCountOrderItems_base {
57
+ }
46
58
  declare const InventoryCountOrders_base: new () => import("./pagination.dto").PaginatedType<InventoryCountOrderDto>;
47
59
  export declare class InventoryCountOrders extends InventoryCountOrders_base {
48
60
  }
@@ -29,6 +29,20 @@ let InventoryCountOrderItemDto = class InventoryCountOrderItemDto {
29
29
  countedQuantity;
30
30
  /** `true` for a line added during counting for stock the books never had. */
31
31
  isAddition;
32
+ // ── Batch attributes as they were when counting started ────────────────── //
33
+ //
34
+ // Snapshotted rather than resolved live: the batch keeps moving after a count
35
+ // settles, and a sheet that silently rewrites itself months later stops being
36
+ // a record of what was found. `null` before counting has started.
37
+ held;
38
+ inShipment;
39
+ /** `null` = awaiting inspection, `false` = failed, `true` = passed. */
40
+ inspectionResult;
41
+ transferId;
42
+ vendorId;
43
+ customerId;
44
+ saleId;
45
+ saleSerialId;
32
46
  };
33
47
  __decorate([
34
48
  Field(() => ID),
@@ -70,6 +84,38 @@ __decorate([
70
84
  Field(() => Boolean),
71
85
  __metadata("design:type", Boolean)
72
86
  ], InventoryCountOrderItemDto.prototype, "isAddition", void 0);
87
+ __decorate([
88
+ Field(() => Boolean, { nullable: true }),
89
+ __metadata("design:type", Object)
90
+ ], InventoryCountOrderItemDto.prototype, "held", void 0);
91
+ __decorate([
92
+ Field(() => Boolean, { nullable: true }),
93
+ __metadata("design:type", Object)
94
+ ], InventoryCountOrderItemDto.prototype, "inShipment", void 0);
95
+ __decorate([
96
+ Field(() => Boolean, { nullable: true }),
97
+ __metadata("design:type", Object)
98
+ ], InventoryCountOrderItemDto.prototype, "inspectionResult", void 0);
99
+ __decorate([
100
+ Field(() => ID, { nullable: true }),
101
+ __metadata("design:type", Object)
102
+ ], InventoryCountOrderItemDto.prototype, "transferId", void 0);
103
+ __decorate([
104
+ Field(() => String, { nullable: true }),
105
+ __metadata("design:type", Object)
106
+ ], InventoryCountOrderItemDto.prototype, "vendorId", void 0);
107
+ __decorate([
108
+ Field(() => String, { nullable: true }),
109
+ __metadata("design:type", Object)
110
+ ], InventoryCountOrderItemDto.prototype, "customerId", void 0);
111
+ __decorate([
112
+ Field(() => String, { nullable: true }),
113
+ __metadata("design:type", Object)
114
+ ], InventoryCountOrderItemDto.prototype, "saleId", void 0);
115
+ __decorate([
116
+ Field(() => String, { nullable: true }),
117
+ __metadata("design:type", Object)
118
+ ], InventoryCountOrderItemDto.prototype, "saleSerialId", void 0);
73
119
  InventoryCountOrderItemDto = __decorate([
74
120
  ObjectType()
75
121
  ], InventoryCountOrderItemDto);
@@ -137,10 +183,15 @@ __decorate([
137
183
  InventoryCountOrderDto = __decorate([
138
184
  ObjectType()
139
185
  ], InventoryCountOrderDto);
186
+ let InventoryCountOrderItems = class InventoryCountOrderItems extends PaginatedResponse(InventoryCountOrderItemDto) {
187
+ };
188
+ InventoryCountOrderItems = __decorate([
189
+ ObjectType()
190
+ ], InventoryCountOrderItems);
140
191
  let InventoryCountOrders = class InventoryCountOrders extends PaginatedResponse(InventoryCountOrderDto) {
141
192
  };
142
193
  InventoryCountOrders = __decorate([
143
194
  ObjectType()
144
195
  ], InventoryCountOrders);
145
196
 
146
- export { InventoryCountOrderDto, InventoryCountOrderItemDto, InventoryCountOrders };
197
+ export { InventoryCountOrderDto, InventoryCountOrderItemDto, InventoryCountOrderItems, InventoryCountOrders };
@@ -12,4 +12,13 @@ export declare class FindStockArgs extends PaginationArgs {
12
12
  batchKey?: string;
13
13
  locationKey?: string;
14
14
  loaderKey?: string;
15
+ /**
16
+ * Commercial attributes carried by the stock's batch. Matched exactly — they
17
+ * are identifiers from the host system, and a partial match on 「SO-1」 would
18
+ * quietly sweep in 「SO-10」.
19
+ */
20
+ vendorId?: string;
21
+ customerId?: string;
22
+ saleId?: string;
23
+ saleSerialId?: string;
15
24
  }
@@ -15,6 +15,15 @@ let FindStockArgs = class FindStockArgs extends PaginationArgs {
15
15
  batchKey;
16
16
  locationKey;
17
17
  loaderKey;
18
+ /**
19
+ * Commercial attributes carried by the stock's batch. Matched exactly — they
20
+ * are identifiers from the host system, and a partial match on 「SO-1」 would
21
+ * quietly sweep in 「SO-10」.
22
+ */
23
+ vendorId;
24
+ customerId;
25
+ saleId;
26
+ saleSerialId;
18
27
  };
19
28
  __decorate([
20
29
  Field(() => ID, { nullable: true }),
@@ -60,6 +69,22 @@ __decorate([
60
69
  Field(() => String, { nullable: true }),
61
70
  __metadata("design:type", String)
62
71
  ], FindStockArgs.prototype, "loaderKey", void 0);
72
+ __decorate([
73
+ Field(() => String, { nullable: true }),
74
+ __metadata("design:type", String)
75
+ ], FindStockArgs.prototype, "vendorId", void 0);
76
+ __decorate([
77
+ Field(() => String, { nullable: true }),
78
+ __metadata("design:type", String)
79
+ ], FindStockArgs.prototype, "customerId", void 0);
80
+ __decorate([
81
+ Field(() => String, { nullable: true }),
82
+ __metadata("design:type", String)
83
+ ], FindStockArgs.prototype, "saleId", void 0);
84
+ __decorate([
85
+ Field(() => String, { nullable: true }),
86
+ __metadata("design:type", String)
87
+ ], FindStockArgs.prototype, "saleSerialId", void 0);
63
88
  FindStockArgs = __decorate([
64
89
  ArgsType()
65
90
  ], FindStockArgs);
@@ -14,6 +14,20 @@ export declare class FindInventoryCountOrderArgs extends PaginationArgs {
14
14
  */
15
15
  locationIds?: string[];
16
16
  }
17
+ export declare class FindInventoryCountOrderItemArgs extends PaginationArgs {
18
+ id: string;
19
+ materialKey?: string;
20
+ materialDescription?: string;
21
+ batchKey?: string;
22
+ locationKey?: string;
23
+ loaderSerialId?: string;
24
+ vendorId?: string;
25
+ customerId?: string;
26
+ saleId?: string;
27
+ saleSerialId?: string;
28
+ /** Only lines whose counted quantity differs from the books (顯示盤差項目). */
29
+ onlyVariance?: boolean;
30
+ }
17
31
  export declare class CreateInventoryCountOrderArgs {
18
32
  name: string;
19
33
  countDate: string;
@@ -44,6 +44,67 @@ __decorate([
44
44
  FindInventoryCountOrderArgs = __decorate([
45
45
  ArgsType()
46
46
  ], FindInventoryCountOrderArgs);
47
+ let FindInventoryCountOrderItemArgs = class FindInventoryCountOrderItemArgs extends PaginationArgs {
48
+ id;
49
+ materialKey;
50
+ materialDescription;
51
+ batchKey;
52
+ locationKey;
53
+ loaderSerialId;
54
+ vendorId;
55
+ customerId;
56
+ saleId;
57
+ saleSerialId;
58
+ /** Only lines whose counted quantity differs from the books (顯示盤差項目). */
59
+ onlyVariance;
60
+ };
61
+ __decorate([
62
+ Field(() => ID),
63
+ __metadata("design:type", String)
64
+ ], FindInventoryCountOrderItemArgs.prototype, "id", void 0);
65
+ __decorate([
66
+ Field(() => String, { nullable: true }),
67
+ __metadata("design:type", String)
68
+ ], FindInventoryCountOrderItemArgs.prototype, "materialKey", void 0);
69
+ __decorate([
70
+ Field(() => String, { nullable: true }),
71
+ __metadata("design:type", String)
72
+ ], FindInventoryCountOrderItemArgs.prototype, "materialDescription", void 0);
73
+ __decorate([
74
+ Field(() => String, { nullable: true }),
75
+ __metadata("design:type", String)
76
+ ], FindInventoryCountOrderItemArgs.prototype, "batchKey", void 0);
77
+ __decorate([
78
+ Field(() => String, { nullable: true }),
79
+ __metadata("design:type", String)
80
+ ], FindInventoryCountOrderItemArgs.prototype, "locationKey", void 0);
81
+ __decorate([
82
+ Field(() => String, { nullable: true }),
83
+ __metadata("design:type", String)
84
+ ], FindInventoryCountOrderItemArgs.prototype, "loaderSerialId", void 0);
85
+ __decorate([
86
+ Field(() => String, { nullable: true }),
87
+ __metadata("design:type", String)
88
+ ], FindInventoryCountOrderItemArgs.prototype, "vendorId", void 0);
89
+ __decorate([
90
+ Field(() => String, { nullable: true }),
91
+ __metadata("design:type", String)
92
+ ], FindInventoryCountOrderItemArgs.prototype, "customerId", void 0);
93
+ __decorate([
94
+ Field(() => String, { nullable: true }),
95
+ __metadata("design:type", String)
96
+ ], FindInventoryCountOrderItemArgs.prototype, "saleId", void 0);
97
+ __decorate([
98
+ Field(() => String, { nullable: true }),
99
+ __metadata("design:type", String)
100
+ ], FindInventoryCountOrderItemArgs.prototype, "saleSerialId", void 0);
101
+ __decorate([
102
+ Field(() => Boolean, { nullable: true }),
103
+ __metadata("design:type", Boolean)
104
+ ], FindInventoryCountOrderItemArgs.prototype, "onlyVariance", void 0);
105
+ FindInventoryCountOrderItemArgs = __decorate([
106
+ ArgsType()
107
+ ], FindInventoryCountOrderItemArgs);
47
108
  let CreateInventoryCountOrderArgs = class CreateInventoryCountOrderArgs {
48
109
  name;
49
110
  countDate;
@@ -193,4 +254,4 @@ InventoryCountOrderIdArgs = __decorate([
193
254
  ArgsType()
194
255
  ], InventoryCountOrderIdArgs);
195
256
 
196
- export { AddInventoryCountItemArgs, CountInventoryArgs, CountInventoryItemInput, CreateInventoryCountOrderArgs, FindInventoryCountOrderArgs, InventoryCountOrderIdArgs, RemoveInventoryCountItemArgs, UpdateInventoryCountOrderArgs };
257
+ export { AddInventoryCountItemArgs, CountInventoryArgs, CountInventoryItemInput, CreateInventoryCountOrderArgs, FindInventoryCountOrderArgs, FindInventoryCountOrderItemArgs, InventoryCountOrderIdArgs, RemoveInventoryCountItemArgs, UpdateInventoryCountOrderArgs };
@@ -1,9 +1,17 @@
1
1
  import { InventoryCountOrderService } from '@rytass/wms-module-core';
2
- import { InventoryCountOrderDto, InventoryCountOrders } from '../dto/inventory-count-order.dto';
3
- import { FindInventoryCountOrderArgs, InventoryCountOrderIdArgs } from '../inputs/inventory-count-order.args';
2
+ import { InventoryCountOrderDto, InventoryCountOrderItems, InventoryCountOrders } from '../dto/inventory-count-order.dto';
3
+ import { FindInventoryCountOrderArgs, FindInventoryCountOrderItemArgs, InventoryCountOrderIdArgs } from '../inputs/inventory-count-order.args';
4
4
  export declare class InventoryCountOrderQuery {
5
5
  private readonly inventoryCountOrderService;
6
6
  constructor(inventoryCountOrderService: InventoryCountOrderService);
7
7
  inventoryCountOrders(args: FindInventoryCountOrderArgs): Promise<InventoryCountOrders>;
8
+ /**
9
+ * The count sheet, filtered and paginated.
10
+ *
11
+ * Separate from `inventoryCountOrder.items` on purpose: the detail page needs
12
+ * to narrow thousands of lines down to the shelf in front of the operator, and
13
+ * a field resolver cannot take filters or a page.
14
+ */
15
+ inventoryCountOrderItems({ id, ...options }: FindInventoryCountOrderItemArgs): Promise<InventoryCountOrderItems>;
8
16
  inventoryCountOrder({ id }: InventoryCountOrderIdArgs): Promise<InventoryCountOrderDto>;
9
17
  }
@@ -2,8 +2,8 @@ import { __decorate, __param, __metadata } from 'tslib';
2
2
  import { Injectable } from '@nestjs/common';
3
3
  import { Query, Args } from '@nestjs/graphql';
4
4
  import { InventoryCountOrderService } from '@rytass/wms-module-core';
5
- import { InventoryCountOrders, InventoryCountOrderDto } from '../dto/inventory-count-order.dto.js';
6
- import { FindInventoryCountOrderArgs, InventoryCountOrderIdArgs } from '../inputs/inventory-count-order.args.js';
5
+ import { InventoryCountOrders, InventoryCountOrderItems, InventoryCountOrderDto } from '../dto/inventory-count-order.dto.js';
6
+ import { FindInventoryCountOrderArgs, FindInventoryCountOrderItemArgs, InventoryCountOrderIdArgs } from '../inputs/inventory-count-order.args.js';
7
7
  import '../auth/wms-auth.guard.js';
8
8
  import { WmsOperation } from '../auth/wms-operation.decorator.js';
9
9
 
@@ -16,6 +16,17 @@ let InventoryCountOrderQuery = class InventoryCountOrderQuery {
16
16
  const [items, total] = await this.inventoryCountOrderService.findMany(args);
17
17
  return { items, total };
18
18
  }
19
+ /**
20
+ * The count sheet, filtered and paginated.
21
+ *
22
+ * Separate from `inventoryCountOrder.items` on purpose: the detail page needs
23
+ * to narrow thousands of lines down to the shelf in front of the operator, and
24
+ * a field resolver cannot take filters or a page.
25
+ */
26
+ async inventoryCountOrderItems({ id, ...options }) {
27
+ const [items, total] = await this.inventoryCountOrderService.findItems(id, options);
28
+ return { items, total };
29
+ }
19
30
  async inventoryCountOrder({ id }) {
20
31
  return this.inventoryCountOrderService.findOne(id);
21
32
  }
@@ -28,6 +39,14 @@ __decorate([
28
39
  __metadata("design:paramtypes", [FindInventoryCountOrderArgs]),
29
40
  __metadata("design:returntype", Promise)
30
41
  ], InventoryCountOrderQuery.prototype, "inventoryCountOrders", null);
42
+ __decorate([
43
+ Query(() => InventoryCountOrderItems),
44
+ WmsOperation({ resource: 'inventory-count-order', action: 'read-items' }),
45
+ __param(0, Args()),
46
+ __metadata("design:type", Function),
47
+ __metadata("design:paramtypes", [FindInventoryCountOrderItemArgs]),
48
+ __metadata("design:returntype", Promise)
49
+ ], InventoryCountOrderQuery.prototype, "inventoryCountOrderItems", null);
31
50
  __decorate([
32
51
  Query(() => InventoryCountOrderDto),
33
52
  WmsOperation({ resource: 'inventory-count-order', action: 'read' }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rytass/wms-module-graphql",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Rytass WMS Module — GraphQL schema, resolvers and DTOs for warehouse management",
5
5
  "keywords": [
6
6
  "rytass",
@@ -30,10 +30,12 @@
30
30
  },
31
31
  "./adapters/member-base": {
32
32
  "types": "./adapters/member-base.d.ts",
33
+ "require": "./adapters/member-base.cjs.js",
33
34
  "import": "./adapters/member-base.js"
34
35
  },
35
36
  "./adapters/event-emitter": {
36
37
  "types": "./adapters/event-emitter.d.ts",
38
+ "require": "./adapters/event-emitter.cjs.js",
37
39
  "import": "./adapters/event-emitter.js"
38
40
  },
39
41
  "./package.json": "./package.json"
@@ -50,17 +52,21 @@
50
52
  },
51
53
  "dependencies": {
52
54
  "dataloader": "^2.2.3",
53
- "lru-cache": "^10.0.0"
55
+ "lodash": "^4.17.0",
56
+ "lru-cache": "^10.0.0 || ^11.0.0"
54
57
  },
55
58
  "peerDependencies": {
56
59
  "@as-integrations/express5": "^1.0.0",
57
- "@nestjs/apollo": ">=13.0.0 <13.2.0",
58
- "@nestjs/common": "^11.0.0",
60
+ "@nestjs/apollo": "^13.0.0",
61
+ "@nestjs/common": "^10.0.0 || ^11.0.0",
62
+ "@nestjs/core": "^10.0.0 || ^11.0.0",
59
63
  "@nestjs/graphql": "^13.0.0",
60
- "@rytass/wms-module-core": "^0.6.0",
64
+ "@rytass/wms-base-nestjs-module": "^0.4.0",
65
+ "@rytass/wms-module-core": "^0.7.0",
61
66
  "graphql": "^16.0.0",
62
67
  "reflect-metadata": "^0.1.13 || ^0.2.0",
63
- "rxjs": "^7.8.0",
68
+ "rxjs": "^7.0.0",
69
+ "typeorm": "^0.3.0",
64
70
  "@nestjs/event-emitter": "^2.0.0 || ^3.0.0"
65
71
  },
66
72
  "peerDependenciesMeta": {