@things-factory/warehouse-base 4.3.678 → 4.3.681

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.
@@ -177,7 +177,8 @@ let InventoryHistoryQuery = class InventoryHistoryQuery {
177
177
  OR Lower(prd.description) LIKE ANY(ARRAY[${productValue}])
178
178
  OR Lower(prd.brand) LIKE ANY(ARRAY[${productValue}])
179
179
  OR Lower(prd.brand_sku) LIKE ANY(ARRAY[${productValue}])
180
- )`;
180
+ )
181
+ `;
181
182
  }
182
183
  let productDescQuery = '';
183
184
  if (productDesc) {
@@ -190,7 +191,7 @@ let InventoryHistoryQuery = class InventoryHistoryQuery {
190
191
  await tx.query(`
191
192
  create temp table temp_products AS
192
193
  (
193
- select prd.*, prd.id::varchar as product_id from products prd
194
+ select prd.id, prd.sku, prd.name, prd.description, prd.type, prd.aux_value_1 from products prd
194
195
  inner join bizplaces b on b.id = prd.bizplace_id
195
196
  inner join companies c on c.domain_id = b.domain_id
196
197
  inner join bizplaces b2 on b2.company_id = c.id
@@ -201,124 +202,114 @@ let InventoryHistoryQuery = class InventoryHistoryQuery {
201
202
  create temp table temp_data_src AS
202
203
  (
203
204
  SELECT prd.sku as sku, prd.name AS product_name, prd.description AS product_description, prd.type as product_type, prd.aux_value_1 AS product_aux_value_1, trim(invh.batch_id) as batch_id, invh.product_id,
204
- invh.packing_type, invh.bizplace_id, invh.domain_id,
205
- invh.ref_order_id, invh.order_no, invh.order_ref_no, invh.transaction_type, invh.status, invh.created_at,
205
+ invh.packing_type, invh.bizplace_id,
206
+ invh.order_no, invh.order_ref_no, invh.transaction_type, invh.status, invh.created_at,
206
207
  TRUNC(CAST(invh.qty AS NUMERIC), 3) as qty, TRUNC(CAST(invh.opening_qty AS NUMERIC), 3) as opening_qty,
207
208
  invh.uom, TRUNC(CAST(COALESCE(invh.uom_value, 0) AS NUMERIC), 3) as uom_value, TRUNC(CAST(COALESCE(invh.opening_uom_value, 0) AS NUMERIC), 3) as opening_uom_value
208
- FROM reduced_inventory_histories invh
209
+ FROM inventory_histories invh
209
210
  INNER JOIN temp_products prd ON prd.id = invh.product_id::uuid
210
211
  WHERE
211
- invh.domain_id = $1
212
+ (invh.qty <> 0 OR invh.uom_value <> 0)
213
+ AND invh.domain_id = $1
212
214
  AND invh.bizplace_id = $2
213
215
  AND invh.created_at <= $3::timestamp
214
216
  ${batchNoQuery}
215
217
  )
216
218
  `, [domain.id, bizplace.id, toDate.value]);
217
219
  await tx.query(`
218
- create temp table temp_inv_history as (
219
- SELECT src.sku, src.product_name, src.product_description, src.product_type, src.product_aux_value_1, src.batch_id, src.product_id, src.packing_type, src.uom,
220
- src.bizplace_id, src.domain_id,
221
- TRUNC(CAST(SUM(COALESCE(src.qty,0)) AS NUMERIC), 3) AS qty,
222
- TRUNC(CAST(SUM(COALESCE(src.opening_qty,0)) AS NUMERIC), 3) AS opening_qty,
223
- TRUNC(CAST(SUM(COALESCE(src.uom_value,0)) AS NUMERIC), 3) AS uom_value,
224
- TRUNC(CAST(SUM(COALESCE(src.opening_uom_value,0)) AS NUMERIC), 3) AS opening_uom_value,
220
+ CREATE TEMP TABLE temp_inv_history AS (
221
+ SELECT
222
+ src.sku,
223
+ src.product_name,
224
+ src.product_description,
225
+ src.product_type,
226
+ src.product_aux_value_1,
227
+ src.batch_id,
228
+ src.product_id,
229
+ src.packing_type,
230
+ src.uom,
231
+ src.bizplace_id,
232
+ TRUNC(CAST(SUM(COALESCE(src.qty, 0)) AS NUMERIC), 3) AS qty,
233
+ TRUNC(CAST(SUM(COALESCE(src.opening_qty, 0)) AS NUMERIC), 3) AS opening_qty,
234
+ TRUNC(CAST(SUM(COALESCE(src.uom_value, 0)) AS NUMERIC), 3) AS uom_value,
235
+ TRUNC(CAST(SUM(COALESCE(src.opening_uom_value, 0)) AS NUMERIC), 3) AS opening_uom_value,
225
236
  'Opening Balance' AS order_name,
226
237
  '-' AS ref_no,
227
- '-' as transaction_type,
238
+ '-' AS transaction_type,
228
239
  0 AS rn,
229
- $1::timestamp AS created_at,
230
- $1::date AS created_date
231
- FROM temp_data_src src
232
- WHERE src.created_at < $1::timestamp
233
- GROUP BY src.sku, src.product_name, src.product_description, src.product_type, src.product_aux_value_1, src.batch_id, src.product_id, src.packing_type, src.uom,
234
- src.bizplace_id, src.domain_id
235
- UNION ALL
236
- SELECT sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
237
- domain_id, TRUNC(CAST(sum(qty) AS NUMERIC), 3) as qty, TRUNC(CAST(sum(opening_qty) AS NUMERIC), 3) as opening_qty, TRUNC(CAST(sum(uom_value) AS NUMERIC), 3) as uom_value, TRUNC(CAST(sum(opening_uom_value) AS NUMERIC), 3) as opening_uom_value,
238
- order_name, ref_no, '-' as transaction_type ,1 AS rn, MIN(created_at) - $2::interval as created_at, (MIN(created_at) - $2::interval)::DATE as created_date
239
- FROM (
240
- SELECT invh.sku, invh.product_name, invh.product_description, invh.product_type, invh.product_aux_value_1, invh.batch_id, invh.product_id, invh.packing_type, invh.bizplace_id,
241
- invh.domain_id,
242
- invh.qty, invh.opening_qty, invh.uom_value, invh.uom, invh.opening_uom_value,
243
- COALESCE(order_no, '-') AS order_name,
244
- COALESCE(order_ref_no, '-') AS ref_no,
245
- COALESCE(invh.transaction_type, '-') AS transaction_type,
246
- invh.created_at,
247
- invh.created_at::date as created_date
248
- FROM temp_data_src invh
249
- WHERE (invh.qty <> 0 OR invh.uom_value <> 0) AND
250
- invh.transaction_type <> 'ADJUSTMENT' AND
251
- invh.transaction_type <> 'PICKING' AND
252
- invh.transaction_type <> 'CANCEL_ORDER' AND
253
- invh.transaction_type <>'NEW'
254
- AND invh.created_at >= $1::timestamp
255
- ) AS inv_movement
256
- GROUP BY sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
257
- domain_id, order_name, ref_no, rn
258
- UNION ALL
259
- SELECT sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
260
- domain_id, TRUNC(CAST(sum(qty) AS NUMERIC), 3) as qty, TRUNC(CAST(sum(opening_qty) AS NUMERIC), 3) as opening_qty, TRUNC(CAST(sum(uom_value) AS NUMERIC), 3) as uom_value, TRUNC(CAST(sum(opening_uom_value) AS NUMERIC), 3) as opening_uom_value,
261
- CASE
262
- WHEN transaction_type IN ('PICKING', 'CANCEL_ORDER') THEN order_no
263
- ELSE order_name
264
- END AS order_name,
240
+ $1::timestamp AS created_at
241
+ FROM temp_data_src src
242
+ WHERE src.created_at < $1::timestamp
243
+ GROUP BY
244
+ src.sku,
245
+ src.product_name,
246
+ src.product_description,
247
+ src.product_type,
248
+ src.product_aux_value_1,
249
+ src.batch_id,
250
+ src.product_id,
251
+ src.packing_type,
252
+ src.uom,
253
+ src.bizplace_id
254
+
255
+ UNION ALL
256
+
257
+ -- Part 2: All Inventory Movements (combined into a single scan)
258
+ -- This SELECT statement replaces the three separate UNION ALLs for
259
+ -- transaction movements, using CASE statements to handle different
260
+ -- transaction types and statuses in a single pass.
261
+ SELECT
262
+ sku,
263
+ product_name,
264
+ product_description,
265
+ product_type,
266
+ product_aux_value_1,
267
+ batch_id,
268
+ product_id,
269
+ packing_type,
270
+ uom,
271
+ bizplace_id,
272
+ TRUNC(CAST(SUM(
273
+ -- qty and uom_value logic for all cases combined
274
+ CASE
275
+ WHEN transaction_type = 'ADJUSTMENT' AND status = 'MISSING' THEN -opening_qty
276
+ ELSE qty
277
+ END
278
+ ) AS NUMERIC), 3) AS qty,
279
+ TRUNC(CAST(SUM(opening_qty) AS NUMERIC), 3) AS opening_qty,
280
+ TRUNC(CAST(SUM(
281
+ CASE
282
+ WHEN transaction_type = 'ADJUSTMENT' AND status = 'MISSING' THEN -opening_uom_value
283
+ ELSE uom_value
284
+ END
285
+ ) AS NUMERIC), 3) AS uom_value,
286
+ TRUNC(CAST(SUM(opening_uom_value) AS NUMERIC), 3) AS opening_uom_value,
287
+ CASE
288
+ WHEN transaction_type IN ('PICKING', 'CANCEL_ORDER') THEN order_no
289
+ WHEN transaction_type IN ('ADJUSTMENT', 'NEW') THEN transaction_type
290
+ ELSE COALESCE(order_no, '-')
291
+ END AS order_name,
265
292
  CASE
266
- WHEN transaction_type IN ('PICKING', 'CANCEL_ORDER') THEN ref_no
267
- ELSE order_name
268
- END AS ref_no,
269
- transaction_type,
270
- 1 AS rn,
271
- created_at - $2::interval as created_at,
272
- created_at::date as created_date
273
- FROM (
274
- SELECT
275
- invh.sku, invh.product_name, invh.product_description, invh.product_type, invh.product_aux_value_1, invh.batch_id, invh.product_id,
276
- invh.packing_type, invh.uom, invh.bizplace_id, invh.domain_id,
277
- invh.qty, invh.opening_qty, invh.uom_value, invh.opening_uom_value,
278
- invh.transaction_type,
279
- invh.transaction_type AS order_name,
280
- order_no,
281
- order_ref_no AS ref_no,
282
- invh.created_at,
283
- invh.created_at::date as created_date
284
- FROM temp_data_src invh
285
- WHERE (invh.qty <> 0 OR invh.uom_value <> 0) AND
286
- (invh.transaction_type = 'ADJUSTMENT'
287
- OR invh.transaction_type = 'PICKING'
288
- OR invh.transaction_type = 'CANCEL_ORDER'
289
- OR invh.transaction_type = 'NEW')
290
- AND invh.created_at >= $1::timestamp
291
- ) AS inv_movement
292
- GROUP BY
293
- sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
294
- domain_id, inv_movement.transaction_type,inv_movement.order_no,order_name, ref_no, rn, created_at
295
- UNION ALL
296
- SELECT sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
297
- domain_id, TRUNC(CAST(sum(-opening_qty) AS NUMERIC), 3) as qty, TRUNC(CAST(sum(opening_qty) AS NUMERIC), 3) as opening_qty, TRUNC(CAST(sum(-opening_uom_value) AS NUMERIC), 3) as uom_value, TRUNC(CAST(sum(opening_uom_value) AS NUMERIC), 3) as opening_uom_value,
298
- order_name, ref_no, transaction_type,1 AS rn, created_at - $2::interval as created_at, created_at::date as created_date
299
- FROM (
300
- SELECT invh.sku, invh.product_name, invh.product_description, invh.product_type, invh.product_aux_value_1, invh.batch_id, invh.product_id, invh.packing_type, invh.uom, invh.bizplace_id,
301
- invh.domain_id,
302
- invh.qty, invh.opening_qty, invh.uom_value, invh.opening_uom_value,
303
- invh.transaction_type as transaction_type,
304
- invh.transaction_type AS order_name,
305
- invh.transaction_type AS ref_no,
306
- invh.created_at,
307
- invh.created_at:: date as created_date
308
- FROM temp_data_src invh
309
- WHERE
310
- invh.transaction_type = 'ADJUSTMENT'
311
- AND invh.status = 'MISSING'
312
- AND invh.created_at >= $1::timestamp
313
- ) AS inv_movement
314
- GROUP BY sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
315
- domain_id, order_name, ref_no, inv_movement.transaction_type,rn, created_at
316
- )`, [fromDate.value, tzoffset]);
293
+ WHEN transaction_type IN ('PICKING', 'CANCEL_ORDER') THEN order_ref_no
294
+ WHEN transaction_type IN ('ADJUSTMENT', 'NEW') THEN transaction_type
295
+ ELSE COALESCE(order_ref_no, '-')
296
+ END AS ref_no,
297
+ transaction_type,
298
+ 1 AS rn,
299
+ MIN(created_at) - $2::interval AS created_at
300
+ FROM temp_data_src
301
+ WHERE
302
+ created_at >= $1::timestamp
303
+ GROUP BY
304
+ sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id,
305
+ packing_type, uom, bizplace_id,
306
+ order_name, ref_no, transaction_type
307
+ )`, [fromDate.value, tzoffset]);
317
308
  let result;
318
309
  if ((byPallet === null || byPallet === void 0 ? void 0 : byPallet.value) === true) {
319
310
  result = await tx.query(`
320
311
  select sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
321
- domain_id, qty, opening_qty, round(uom_value::decimal,2) as uom_value, opening_uom_value, order_name, ref_no, transaction_type,created_at::date
312
+ qty, opening_qty, round(uom_value::decimal,2) as uom_value, opening_uom_value, order_name, ref_no, transaction_type,created_at::date
322
313
  from temp_inv_history invh where
323
314
  exists (
324
315
  select 1 from (
@@ -334,10 +325,10 @@ let InventoryHistoryQuery = class InventoryHistoryQuery {
334
325
  else {
335
326
  result = await tx.query(`
336
327
  select sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
337
- domain_id, sum(qty) as qty, sum(opening_qty) as opening_qty, sum(uom_value) as uom_value, sum(opening_uom_value) as opening_uom_value, order_name, ref_no, transaction_type, created_at::date
328
+ sum(qty) as qty, sum(opening_qty) as opening_qty, sum(uom_value) as uom_value, sum(opening_uom_value) as opening_uom_value, order_name, ref_no, transaction_type, created_at::date
338
329
  from(
339
330
  select sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
340
- domain_id, qty, opening_qty, TRUNC(CAST(uom_value AS NUMERIC), 3) as uom_value, opening_uom_value, order_name, ref_no, transaction_type,created_at::date
331
+ qty, opening_qty, TRUNC(CAST(uom_value AS NUMERIC), 3) as uom_value, opening_uom_value, order_name, ref_no, transaction_type,created_at
341
332
  from temp_inv_history invh where
342
333
  exists (
343
334
  select 1 from (
@@ -349,7 +340,7 @@ let InventoryHistoryQuery = class InventoryHistoryQuery {
349
340
  )
350
341
  ) foo
351
342
  GROUP BY sku, product_name, product_description, product_type, product_aux_value_1, batch_id, product_id, packing_type, uom, bizplace_id,
352
- domain_id, order_name, ref_no, transaction_type, created_at
343
+ order_name, ref_no, transaction_type, created_at
353
344
  ORDER BY sku asc, product_name asc, product_description asc, packing_type asc, batch_id asc, uom asc, created_at asc,
354
345
  CASE
355
346
  WHEN transaction_type = 'CANCEL_ORDER' THEN 2
@@ -1524,91 +1515,250 @@ async function massageInventoryPalletSummary(tx, params, bizplace, context) {
1524
1515
  let hasTransactionOrBalanceFilter = params.filters.find(data => data.name === 'hasTransactionOrBalance');
1525
1516
  let hasTransactionOrBalanceQuery = '';
1526
1517
  if (hasTransactionOrBalanceFilter && hasTransactionOrBalanceFilter.value) {
1527
- hasTransactionOrBalanceQuery = 'and (opening_qty > 0 or total_in_qty > 0 or adjustment_qty > 0)';
1518
+ hasTransactionOrBalanceQuery = 'and (c.opening_qty > 0 or c.total_in_qty > 0 or c.adjustment_qty > 0)';
1528
1519
  }
1529
1520
  await tx.query(`
1530
- create temp table temp_inventory_pallet_summary ON COMMIT DROP as (
1531
- select invh.*,
1532
- invh.opening_qty + invh.total_in_qty + invh.total_out_qty + invh.adjustment_qty as closing_qty from (
1533
- select product_sku, product_id, product_name, product_description, product_type,
1534
- SUM(case when invHistory.created_at < $1 then
1535
- case when invHistory.status = 'STORED' then 1
1536
- when invhistory.status = 'TERMINATED' then -1
1537
- else 0 end
1538
- else 0 end) as opening_qty,
1539
- SUM(case when invHistory.created_at >= $1 and invHistory.created_at <= $2 then
1540
- case when (invHistory.transaction_type = 'UNLOADING' or invHistory.transaction_type = 'NEW' or invHistory.transaction_type = 'CANCEL_ORDER') then 1 else 0 end
1541
- else 0 end) as total_in_qty,
1542
- SUM(case when invHistory.created_at >= $1 and invHistory.created_at <= $2 then
1543
- case when invHistory.status = 'TERMINATED' AND invHistory.transaction_type <> 'ADJUSTMENT' then -1 else 0 end
1544
- else 0 end) as total_out_qty,
1545
- SUM(case when invHistory.created_at >= $1 and invHistory.created_at <= $2 then
1546
- case when invHistory.status = 'TERMINATED' AND invHistory.transaction_type = 'ADJUSTMENT' then -1
1547
- when invHistory.status = 'STORED' AND invHistory.transaction_type = 'ADJUSTMENT' then 1 else 0 end
1548
- else 0 end) as adjustment_qty
1549
- from(
1550
- select pallet_id, seq, status, transaction_type, product_sku, product_id, product_name, product_description, product_type,
1551
- inventory_history_id, packing_type, packing_size, qty, opening_qty, created_at from (
1552
- select row_number() over(partition by invh.pallet_id order by invh.created_at asc) as rn, invh.* ,
1553
- prd.sku as product_sku, prd.name as product_name, prd.description as product_description, prd.type AS product_type
1554
- from temp_inv_history invh
1555
- inner join temp_products prd on prd.id = invh.product_id
1556
- where (transaction_type = 'UNLOADING' or invh.transaction_type = 'NEW' or invh.transaction_type = 'CANCEL_ORDER')
1557
- and invh.created_at >= $1
1558
- ) as invIn where rn = 1
1559
- union all
1560
- select pallet_id, seq, status, transaction_type, product_sku, product_id, product_name, product_description, product_type,
1561
- inventory_history_id, packing_type, packing_size, qty, opening_qty, created_at from (
1562
- SELECT row_number() over(partition by pallet_id, invh.product_id order by seq asc) as rn,
1563
- invh.pallet_id, invh.product_id, invh.packing_type, invh.packing_size,
1564
- invh.batch_id, invh.inventory_history_id,
1565
- invh.seq, 'STORED' AS status, invh.transaction_type, invh.qty, invh.opening_qty,
1566
- invh.created_at,
1567
- prd.sku as product_sku, prd.name as product_name, prd.description as product_description, prd.type AS product_type
1568
- from temp_inv_history invh
1569
- inner join temp_products prd on prd.id = invh.product_id
1570
- where invh.created_at < $1
1571
- and (invh.status = 'STORED' or invh.status = 'UNLOADED')
1572
- ) as invStored where rn = 1
1573
- union all
1574
- select pallet_id, seq, status, transaction_type, product_sku, product_id, product_name, product_description, product_type,
1575
- inventory_history_id, packing_type, packing_size, qty, opening_qty, started_at as created_at from (
1576
- select row_number() over(partition by invh.pallet_id, invh.product_id order by invh.seq desc) as rn, invh.*,
1577
- prd.sku as product_sku, prd.name as product_name, prd.description as product_description, prd.type AS product_type, repeatedGroup.started_at
1578
- from temp_inv_history invh
1579
- inner join (
1580
- select pallet_id, min(created_at) as started_at, max(created_at) as ended_at, min(seq) as min_seq, max(seq) as max_seq, status from (
1581
- select startData.*, sum(startflag) over (partition by pallet_id order by seq) as grp from (
1582
- select s.*,
1583
- (case when lag(status) over (partition by pallet_id order by seq) = status then 0 else 1 end) as startflag
1584
- from temp_inv_history s
1585
- ) startData
1586
- ) endData
1587
- group by pallet_id, grp, status
1588
- ) repeatedGroup on repeatedGroup.pallet_id = invh.pallet_id and repeatedGroup.min_seq <= invh.seq and repeatedGroup.max_seq >= invh.seq
1589
- inner join temp_products prd on prd.id = invh.product_id
1590
- where invh.status = 'TERMINATED' and invh.transaction_type IN ('TERMINATED', 'ADJUSTMENT')
1591
- ) as invOut where rn=1
1592
- union all
1593
- SELECT invh.pallet_id, invh.seq, invh.status, invh.transaction_type,
1594
- prd.sku AS product_sku, invh.product_id, prd.name AS product_name, prd.description AS product_description, prd.type AS product_type,
1595
- invh.inventory_history_id, invh.packing_type, invh.packing_size, invh.qty, invh.opening_qty, invh.created_at
1596
- FROM temp_inv_history invh
1597
- INNER JOIN (
1598
- SELECT invh.pallet_id, invh.seq AS stored_seq, invh2.seq AS terminated_seq FROM temp_inv_history invh
1599
- INNER JOIN temp_inv_history invh2 ON invh2.pallet_id = invh.pallet_id AND invh2.status='STORED' AND invh2.seq = invh.seq + 1 AND invh2.product_id <> invh.product_id
1600
- WHERE invh.transaction_type = 'ADJUSTMENT'
1601
- ) dt ON dt.pallet_id = invh.pallet_id AND (dt.stored_seq = invh.seq OR dt.terminated_seq = invh.seq)
1602
- inner join temp_products prd on prd.id = invh.product_id
1603
- where invh.created_at >= $1
1604
- ORDER BY product_name, pallet_id, seq
1605
- ) as invHistory
1606
- group by product_sku, product_id, product_name, product_description, product_type
1607
- ) invh
1608
- where 1=1
1609
- ${hasTransactionOrBalanceQuery}
1521
+ create temp table temp_inventory_pallet_summary ON COMMIT DROP as
1522
+ with params as (
1523
+ select $1::timestamp as t1, $2::timestamp as t2
1524
+ ),
1525
+ base as (
1526
+ select
1527
+ invh.*,
1528
+ prd.sku as product_sku,
1529
+ prd.name as product_name,
1530
+ prd.description as product_description,
1531
+ prd.type as product_type
1532
+ from temp_inv_history invh
1533
+ join temp_products prd on prd.id = invh.product_id
1534
+ ),
1535
+
1536
+ /* ---- Opening snapshot as-of t1 (FIX) ----
1537
+ For each pallet, take the last row <= t1; if status='STORED',
1538
+ count it toward that row's product_id opening.
1539
+ */
1540
+ opening_last_asof as (
1541
+ select *
1542
+ from (
1543
+ select h.*,
1544
+ row_number() over (partition by h.pallet_id order by h.created_at desc, h.seq desc) as rn
1545
+ from temp_inv_history h
1546
+ join params p on true
1547
+ where h.created_at <= p.t1
1548
+ ) x
1549
+ where rn = 1 and status = 'STORED'
1550
+ ),
1551
+ opening_agg as (
1552
+ select
1553
+ p.sku as product_sku,
1554
+ p.id as product_id,
1555
+ p.name as product_name,
1556
+ p.description as product_description,
1557
+ p.type as product_type,
1558
+ count(*)::int as opening_qty
1559
+ from opening_last_asof o
1560
+ join temp_products p on p.id = o.product_id
1561
+ group by p.sku, p.id, p.name, p.description, p.type
1562
+ ),
1563
+
1564
+ /* ---- Arrivals inside window: first arrival-like row per pallet (your rule) ---- */
1565
+ invIn as (
1566
+ select b.*,
1567
+ row_number() over (partition by b.pallet_id order by b.created_at, b.seq) as rn
1568
+ from base b
1569
+ join params p on true
1570
+ where b.transaction_type in ('UNLOADING','NEW','CANCEL_ORDER')
1571
+ and b.created_at >= p.t1
1572
+ ),
1573
+ invIn_first as (
1574
+ select
1575
+ pallet_id, seq, status, transaction_type,
1576
+ product_sku, product_id, product_name, product_description, product_type,
1577
+ inventory_history_id, packing_type, packing_size, qty, opening_qty, created_at
1578
+ from invIn
1579
+ where rn = 1
1580
+ ),
1581
+
1582
+ /* ---- TERMINATED reps (incl. ADJUSTMENT terminations), using your runs logic ---- */
1583
+ status_runs as (
1584
+ select
1585
+ endData.pallet_id,
1586
+ min(endData.created_at) as started_at,
1587
+ max(endData.created_at) as ended_at,
1588
+ min(endData.seq) as min_seq,
1589
+ max(endData.seq) as max_seq,
1590
+ endData.status
1591
+ from (
1592
+ select startData.*,
1593
+ sum(startflag) over (partition by startData.pallet_id order by startData.seq) as grp
1594
+ from (
1595
+ select s.*,
1596
+ case when lag(s.status) over (partition by s.pallet_id order by s.seq) = s.status then 0 else 1 end as startflag
1597
+ from temp_inv_history s
1598
+ ) startData
1599
+ ) endData
1600
+ group by endData.pallet_id, endData.status, endData.grp
1601
+ ),
1602
+ invOut_base as (
1603
+ select b.*, r.started_at
1604
+ from base b
1605
+ join status_runs r
1606
+ on r.pallet_id = b.pallet_id
1607
+ and r.min_seq <= b.seq
1608
+ and r.max_seq >= b.seq
1609
+ where b.status = 'TERMINATED'
1610
+ and b.transaction_type in ('TERMINATED','ADJUSTMENT')
1611
+ ),
1612
+
1613
+ /* ---- Transfer pairing (seq-gap tolerant & tolerant txn tags) ---- */
1614
+ /* STORED side in-window; pair to nearest prior TERMINATED (from raw history), different product */
1615
+ adj_stored AS (
1616
+ SELECT b.*
1617
+ FROM base b
1618
+ JOIN params p ON TRUE
1619
+ WHERE b.status = 'STORED'
1620
+ AND b.created_at BETWEEN p.t1 AND p.t2
1621
+ ),
1622
+ adj_pairs AS (
1623
+ SELECT
1624
+ s.pallet_id,
1625
+ s.seq AS stored_seq,
1626
+ t.seq AS terminated_seq,
1627
+ s.product_id AS new_product_id,
1628
+ t.product_id AS old_product_id,
1629
+ s.inventory_history_id AS stored_inventory_history_id,
1630
+ t.inventory_history_id AS terminated_inventory_history_id,
1631
+ s.created_at AS stored_at,
1632
+ t.created_at AS terminated_at,
1633
+ s.transaction_type AS stored_txn,
1634
+ t.transaction_type AS terminated_txn
1635
+ FROM adj_stored s
1636
+ JOIN LATERAL (
1637
+ SELECT t.*
1638
+ FROM temp_inv_history t -- use raw history (old product may be outside temp_products)
1639
+ WHERE t.pallet_id = s.pallet_id
1640
+ AND t.seq < s.seq
1641
+ AND t.status = 'TERMINATED'
1642
+ ORDER BY t.seq DESC
1643
+ LIMIT 1
1644
+ ) t ON TRUE
1645
+ WHERE t.product_id IS DISTINCT FROM s.product_id
1646
+ AND (s.transaction_type='ADJUSTMENT' OR t.transaction_type='ADJUSTMENT')
1647
+ ),
1648
+ adj_pairs_terminated_keys AS (
1649
+ SELECT pallet_id, terminated_seq AS seq
1650
+ FROM adj_pairs
1651
+ ),
1652
+
1653
+ /* Representative TERMINATED per (pallet, product), EXCLUDING those used by a pair (avoid double-count) */
1654
+ invOut_rep AS (
1655
+ SELECT *
1656
+ FROM (
1657
+ SELECT iob.*,
1658
+ row_number() over (partition by iob.pallet_id, iob.product_id order by iob.seq desc) as rn
1659
+ FROM invOut_base iob
1660
+ LEFT JOIN adj_pairs_terminated_keys k
1661
+ ON k.pallet_id = iob.pallet_id
1662
+ AND k.seq = iob.seq
1663
+ WHERE k.pallet_id IS NULL
1664
+ ) z
1665
+ WHERE rn = 1
1666
+ ),
1667
+
1668
+ /* Materialize both sides of the paired transfer */
1669
+ adj_pairs_stored_rows AS (
1670
+ SELECT
1671
+ b.pallet_id, b.seq, b.status, b.transaction_type,
1672
+ b.product_sku, b.product_id, b.product_name, b.product_description, b.product_type,
1673
+ b.inventory_history_id, b.packing_type, b.packing_size, b.qty, b.opening_qty, b.created_at
1674
+ FROM base b
1675
+ JOIN adj_pairs ap
1676
+ ON ap.pallet_id = b.pallet_id
1677
+ AND ap.stored_seq = b.seq
1678
+ ),
1679
+ adj_pairs_terminated_rows AS (
1680
+ SELECT
1681
+ b.pallet_id, b.seq, b.status, b.transaction_type,
1682
+ b.product_sku, b.product_id, b.product_name, b.product_description, b.product_type,
1683
+ b.inventory_history_id, b.packing_type, b.packing_size, b.qty, b.opening_qty, b.created_at
1684
+ FROM base b
1685
+ JOIN adj_pairs ap
1686
+ ON ap.pallet_id = b.pallet_id
1687
+ AND ap.terminated_seq = b.seq
1688
+ ),
1689
+
1690
+ /* ---- Unified stream for window movement ---- */
1691
+ invHistory as (
1692
+ select * from invIn_first
1693
+ union all
1694
+ select
1695
+ pallet_id, seq, status, transaction_type,
1696
+ product_sku, product_id, product_name, product_description, product_type,
1697
+ inventory_history_id, packing_type, packing_size, qty, opening_qty, created_at
1698
+ from invOut_rep
1699
+ union all
1700
+ select * from adj_pairs_stored_rows
1701
+ union all
1702
+ select * from adj_pairs_terminated_rows
1703
+ ),
1704
+
1705
+ /* ---- Movement aggregation only (no opening here) ---- */
1706
+ movement_agg as (
1707
+ select
1708
+ ih.product_sku,
1709
+ ih.product_id,
1710
+ ih.product_name,
1711
+ ih.product_description,
1712
+ ih.product_type,
1713
+
1714
+ sum(case when ih.created_at >= (select t1 from params)
1715
+ and ih.created_at <= (select t2 from params) then
1716
+ case when ih.transaction_type in ('UNLOADING','NEW','CANCEL_ORDER') then 1 else 0 end
1717
+ else 0 end)::int as total_in_qty,
1718
+
1719
+ sum(case when ih.created_at >= (select t1 from params)
1720
+ and ih.created_at <= (select t2 from params) then
1721
+ case when ih.status = 'TERMINATED'
1722
+ and ih.transaction_type <> 'ADJUSTMENT' then -1 else 0 end
1723
+ else 0 end)::int as total_out_qty,
1724
+
1725
+ sum(case when ih.created_at >= (select t1 from params)
1726
+ and ih.created_at <= (select t2 from params) then
1727
+ case when ih.status = 'TERMINATED' and ih.transaction_type = 'ADJUSTMENT' then -1
1728
+ when ih.status = 'STORED' and ih.transaction_type = 'ADJUSTMENT' then 1
1729
+ else 0 end
1730
+ else 0 end)::int as adjustment_qty
1731
+
1732
+ from invHistory ih
1733
+ group by ih.product_sku, ih.product_id, ih.product_name, ih.product_description, ih.product_type
1734
+ ),
1735
+
1736
+ /* ---- Combine opening snapshot + window movement ---- */
1737
+ combined as (
1738
+ select
1739
+ coalesce(o.product_sku, m.product_sku) as product_sku,
1740
+ coalesce(o.product_id, m.product_id) as product_id,
1741
+ coalesce(o.product_name,m.product_name) as product_name,
1742
+ coalesce(o.product_description,m.product_description) as product_description,
1743
+ coalesce(o.product_type,m.product_type) as product_type,
1744
+
1745
+ coalesce(o.opening_qty, 0) as opening_qty,
1746
+ coalesce(m.total_in_qty, 0) as total_in_qty,
1747
+ coalesce(m.total_out_qty, 0) as total_out_qty,
1748
+ coalesce(m.adjustment_qty, 0) as adjustment_qty
1749
+ from opening_agg o
1750
+ full outer join movement_agg m
1751
+ on m.product_id = o.product_id
1610
1752
  )
1611
- `, [fromDate.value, toDate.value]);
1753
+
1754
+ select
1755
+ c.*,
1756
+ (c.opening_qty + c.total_in_qty + c.total_out_qty + c.adjustment_qty) as closing_qty
1757
+ from combined c
1758
+ where 1=1
1759
+ ${hasTransactionOrBalanceQuery}
1760
+ ;
1761
+ `, [fromDate.value, toDate.value]);
1612
1762
  const total = await tx.query(`select count(*) as count, sum(total_in_qty) as totalInQty, sum(opening_qty) as totalOpeningBal from temp_inventory_pallet_summary`);
1613
1763
  const result = await tx.query(`
1614
1764
  select *, 'PALLET' as packing_type from temp_inventory_pallet_summary ORDER BY product_name, product_description OFFSET $1 LIMIT $2