@things-factory/warehouse-base 4.3.686 → 4.3.689

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.
@@ -1700,253 +1700,211 @@ async function massageInventoryPalletSummary(tx: EntityManager, params: ListPara
1700
1700
  let toDate = params.filters.find(data => data.name === 'toDate')
1701
1701
  let hasTransactionOrBalanceFilter = params.filters.find(data => data.name === 'hasTransactionOrBalance')
1702
1702
 
1703
- let hasTransactionOrBalanceQuery = ''
1703
+ let hasTransactionOrBalanceQuery = 'TRUE'
1704
1704
  if (hasTransactionOrBalanceFilter && hasTransactionOrBalanceFilter.value) {
1705
- hasTransactionOrBalanceQuery = 'and (c.opening_qty > 0 or c.total_in_qty > 0 or c.adjustment_qty > 0)'
1705
+ hasTransactionOrBalanceQuery = '(merged.opening_qty > 0 or merged.total_in_qty > 0 or merged.adjustment_qty > 0)'
1706
1706
  }
1707
1707
 
1708
+ // Build a slimmer history table for pallet summary to minimize scans
1708
1709
  await tx.query(
1709
1710
  `
1710
- create temp table temp_inventory_pallet_summary ON COMMIT DROP as
1711
- with params as (
1712
- select $1::timestamp as t1, $2::timestamp as t2
1713
- ),
1714
- base as (
1715
- select
1716
- invh.*,
1717
- prd.sku as product_sku,
1718
- prd.name as product_name,
1719
- prd.description as product_description,
1720
- prd.type as product_type
1721
- from temp_inv_history invh
1722
- join temp_products prd on prd.id = invh.product_id
1723
- ),
1724
-
1725
- /* ---- Opening snapshot as-of t1 (FIX) ----
1726
- For each pallet, take the last row <= t1; if status='STORED',
1727
- count it toward that row's product_id opening.
1728
- */
1729
- opening_last_asof as (
1730
- select *
1731
- from (
1732
- select h.*,
1733
- row_number() over (partition by h.pallet_id order by h.created_at desc, h.seq desc) as rn
1734
- from temp_inv_history h
1735
- join params p on true
1736
- where h.created_at <= p.t1
1737
- ) x
1738
- where rn = 1 and status = 'STORED'
1739
- ),
1740
- opening_agg as (
1741
- select
1742
- p.sku as product_sku,
1743
- p.id as product_id,
1744
- p.name as product_name,
1745
- p.description as product_description,
1746
- p.type as product_type,
1747
- count(*)::int as opening_qty
1748
- from opening_last_asof o
1749
- join temp_products p on p.id = o.product_id
1750
- group by p.sku, p.id, p.name, p.description, p.type
1751
- ),
1752
-
1753
- /* ---- Arrivals inside window: first arrival-like row per pallet (your rule) ---- */
1754
- invIn as (
1755
- select b.*,
1756
- row_number() over (partition by b.pallet_id order by b.created_at, b.seq) as rn
1757
- from base b
1758
- join params p on true
1759
- where b.transaction_type in ('UNLOADING','NEW','CANCEL_ORDER')
1760
- and b.created_at >= p.t1
1761
- ),
1762
- invIn_first as (
1763
- select
1764
- pallet_id, seq, status, transaction_type,
1765
- product_sku, product_id, product_name, product_description, product_type,
1766
- inventory_history_id, packing_type, packing_size, qty, opening_qty, created_at
1767
- from invIn
1768
- where rn = 1
1769
- ),
1770
-
1771
- /* ---- TERMINATED reps (incl. ADJUSTMENT terminations), using your runs logic ---- */
1772
- status_runs as (
1773
- select
1774
- endData.pallet_id,
1775
- min(endData.created_at) as started_at,
1776
- max(endData.created_at) as ended_at,
1777
- min(endData.seq) as min_seq,
1778
- max(endData.seq) as max_seq,
1779
- endData.status
1780
- from (
1781
- select startData.*,
1782
- sum(startflag) over (partition by startData.pallet_id order by startData.seq) as grp
1783
- from (
1784
- select s.*,
1785
- case when lag(s.status) over (partition by s.pallet_id order by s.seq) = s.status then 0 else 1 end as startflag
1786
- from temp_inv_history s
1787
- ) startData
1788
- ) endData
1789
- group by endData.pallet_id, endData.status, endData.grp
1790
- ),
1791
- invOut_base as (
1792
- select b.*, r.started_at
1793
- from base b
1794
- join status_runs r
1795
- on r.pallet_id = b.pallet_id
1796
- and r.min_seq <= b.seq
1797
- and r.max_seq >= b.seq
1798
- where b.status = 'TERMINATED'
1799
- and b.transaction_type in ('TERMINATED','ADJUSTMENT')
1800
- ),
1801
-
1802
- /* ---- Transfer pairing (seq-gap tolerant & tolerant txn tags) ---- */
1803
- /* STORED side in-window; pair to nearest prior TERMINATED (from raw history), different product */
1804
- adj_stored AS (
1805
- SELECT b.*
1806
- FROM base b
1807
- JOIN params p ON TRUE
1808
- WHERE b.status = 'STORED'
1809
- AND b.created_at BETWEEN p.t1 AND p.t2
1810
- ),
1811
- adj_pairs AS (
1812
- SELECT
1813
- s.pallet_id,
1814
- s.seq AS stored_seq,
1815
- t.seq AS terminated_seq,
1816
- s.product_id AS new_product_id,
1817
- t.product_id AS old_product_id,
1818
- s.inventory_history_id AS stored_inventory_history_id,
1819
- t.inventory_history_id AS terminated_inventory_history_id,
1820
- s.created_at AS stored_at,
1821
- t.created_at AS terminated_at,
1822
- s.transaction_type AS stored_txn,
1823
- t.transaction_type AS terminated_txn
1824
- FROM adj_stored s
1825
- JOIN LATERAL (
1826
- SELECT t.*
1827
- FROM temp_inv_history t -- use raw history (old product may be outside temp_products)
1828
- WHERE t.pallet_id = s.pallet_id
1829
- AND t.seq < s.seq
1830
- AND t.status = 'TERMINATED'
1831
- ORDER BY t.seq DESC
1832
- LIMIT 1
1833
- ) t ON TRUE
1834
- WHERE t.product_id IS DISTINCT FROM s.product_id
1835
- AND (s.transaction_type='ADJUSTMENT' OR t.transaction_type='ADJUSTMENT')
1836
- ),
1837
- adj_pairs_terminated_keys AS (
1838
- SELECT pallet_id, terminated_seq AS seq
1839
- FROM adj_pairs
1840
- ),
1841
-
1842
- /* Representative TERMINATED per (pallet, product), EXCLUDING those used by a pair (avoid double-count) */
1843
- invOut_rep AS (
1844
- SELECT *
1845
- FROM (
1846
- SELECT iob.*,
1847
- row_number() over (partition by iob.pallet_id, iob.product_id order by iob.seq desc) as rn
1848
- FROM invOut_base iob
1849
- LEFT JOIN adj_pairs_terminated_keys k
1850
- ON k.pallet_id = iob.pallet_id
1851
- AND k.seq = iob.seq
1852
- WHERE k.pallet_id IS NULL
1853
- ) z
1854
- WHERE rn = 1
1855
- ),
1856
-
1857
- /* Materialize both sides of the paired transfer */
1858
- adj_pairs_stored_rows AS (
1859
- SELECT
1860
- b.pallet_id, b.seq, b.status, b.transaction_type,
1861
- b.product_sku, b.product_id, b.product_name, b.product_description, b.product_type,
1862
- b.inventory_history_id, b.packing_type, b.packing_size, b.qty, b.opening_qty, b.created_at
1863
- FROM base b
1864
- JOIN adj_pairs ap
1865
- ON ap.pallet_id = b.pallet_id
1866
- AND ap.stored_seq = b.seq
1867
- ),
1868
- adj_pairs_terminated_rows AS (
1869
- SELECT
1870
- b.pallet_id, b.seq, b.status, b.transaction_type,
1871
- b.product_sku, b.product_id, b.product_name, b.product_description, b.product_type,
1872
- b.inventory_history_id, b.packing_type, b.packing_size, b.qty, b.opening_qty, b.created_at
1873
- FROM base b
1874
- JOIN adj_pairs ap
1875
- ON ap.pallet_id = b.pallet_id
1876
- AND ap.terminated_seq = b.seq
1877
- ),
1878
-
1879
- /* ---- Unified stream for window movement ---- */
1880
- invHistory as (
1881
- select * from invIn_first
1882
- union all
1883
- select
1884
- pallet_id, seq, status, transaction_type,
1885
- product_sku, product_id, product_name, product_description, product_type,
1886
- inventory_history_id, packing_type, packing_size, qty, opening_qty, created_at
1887
- from invOut_rep
1888
- union all
1889
- select * from adj_pairs_stored_rows
1890
- union all
1891
- select * from adj_pairs_terminated_rows
1892
- ),
1893
-
1894
- /* ---- Movement aggregation only (no opening here) ---- */
1895
- movement_agg as (
1896
- select
1897
- ih.product_sku,
1898
- ih.product_id,
1899
- ih.product_name,
1900
- ih.product_description,
1901
- ih.product_type,
1902
-
1903
- sum(case when ih.created_at >= (select t1 from params)
1904
- and ih.created_at <= (select t2 from params) then
1905
- case when ih.transaction_type in ('UNLOADING','NEW','CANCEL_ORDER') then 1 else 0 end
1906
- else 0 end)::int as total_in_qty,
1907
-
1908
- sum(case when ih.created_at >= (select t1 from params)
1909
- and ih.created_at <= (select t2 from params) then
1910
- case when ih.status = 'TERMINATED'
1911
- and ih.transaction_type <> 'ADJUSTMENT' then -1 else 0 end
1912
- else 0 end)::int as total_out_qty,
1913
-
1914
- sum(case when ih.created_at >= (select t1 from params)
1915
- and ih.created_at <= (select t2 from params) then
1916
- case when ih.status = 'TERMINATED' and ih.transaction_type = 'ADJUSTMENT' then -1
1917
- when ih.status = 'STORED' and ih.transaction_type = 'ADJUSTMENT' then 1
1918
- else 0 end
1919
- else 0 end)::int as adjustment_qty
1920
-
1921
- from invHistory ih
1922
- group by ih.product_sku, ih.product_id, ih.product_name, ih.product_description, ih.product_type
1923
- ),
1924
-
1925
- /* ---- Combine opening snapshot + window movement ---- */
1926
- combined as (
1927
- select
1928
- coalesce(o.product_sku, m.product_sku) as product_sku,
1929
- coalesce(o.product_id, m.product_id) as product_id,
1930
- coalesce(o.product_name,m.product_name) as product_name,
1931
- coalesce(o.product_description,m.product_description) as product_description,
1932
- coalesce(o.product_type,m.product_type) as product_type,
1933
-
1934
- coalesce(o.opening_qty, 0) as opening_qty,
1935
- coalesce(m.total_in_qty, 0) as total_in_qty,
1936
- coalesce(m.total_out_qty, 0) as total_out_qty,
1937
- coalesce(m.adjustment_qty, 0) as adjustment_qty
1938
- from opening_agg o
1939
- full outer join movement_agg m
1940
- on m.product_id = o.product_id
1941
- )
1711
+ create temp table temp_inv_history_ps on commit drop as (
1712
+ select pallet_id, product_id, seq, status, transaction_type, created_at
1713
+ from temp_inv_history
1714
+ where (transaction_type in ('UNLOADING','NEW','CANCEL_ORDER','ADJUSTMENT')
1715
+ or status in ('STORED','TERMINATED'))
1716
+ and created_at <= $1::timestamp
1717
+ );
1718
+ `,
1719
+ [toDate.value]
1720
+ )
1721
+
1722
+ await tx.query(
1723
+ `
1724
+ CREATE INDEX IF NOT EXISTS idx_tihps_created_at ON temp_inv_history_ps (created_at);
1725
+ CREATE INDEX IF NOT EXISTS idx_tihps_pallet_seq ON temp_inv_history_ps (pallet_id, seq);
1726
+ CREATE INDEX IF NOT EXISTS idx_tihps_pallet_product_seq ON temp_inv_history_ps (pallet_id, product_id, seq);
1727
+ CREATE INDEX IF NOT EXISTS idx_tihps_product ON temp_inv_history_ps (product_id);
1728
+ CREATE INDEX IF NOT EXISTS idx_tihps_product_created ON temp_inv_history_ps (product_id, created_at);
1729
+ CREATE INDEX IF NOT EXISTS idx_tihps_status ON temp_inv_history_ps (status);
1730
+ CREATE INDEX IF NOT EXISTS idx_tihps_txn ON temp_inv_history_ps (transaction_type);
1731
+ ANALYZE temp_inv_history_ps;
1732
+ `
1733
+ )
1942
1734
 
1943
- select
1944
- c.*,
1945
- (c.opening_qty + c.total_in_qty + c.total_out_qty + c.adjustment_qty) as closing_qty
1946
- from combined c
1947
- where 1=1
1948
- ${hasTransactionOrBalanceQuery}
1949
- ;
1735
+ await tx.query(
1736
+ `
1737
+ -- Params:
1738
+ -- $1 = t1 ::timestamp
1739
+ -- $2 = t2 ::timestamp
1740
+
1741
+ CREATE TEMP TABLE temp_inventory_pallet_summary ON COMMIT DROP AS
1742
+ WITH
1743
+ params AS (SELECT $1::timestamp AS t1, $2::timestamp AS t2),
1744
+
1745
+ /* Pre-filtered slices (minimal cols) */
1746
+ rows_le_t1 AS (
1747
+ SELECT pallet_id, product_id, seq, status
1748
+ FROM temp_inv_history_ps
1749
+ WHERE created_at <= (SELECT t1 FROM params)
1750
+ ),
1751
+ rows_win AS (
1752
+ SELECT pallet_id, product_id, seq, status, transaction_type
1753
+ FROM temp_inv_history_ps
1754
+ WHERE created_at BETWEEN (SELECT t1 FROM params) AND (SELECT t2 FROM params)
1755
+ ),
1756
+ rows_le_t2 AS (
1757
+ SELECT pallet_id, product_id, seq, status, transaction_type
1758
+ FROM temp_inv_history_ps
1759
+ WHERE created_at <= (SELECT t2 FROM params)
1760
+ ),
1761
+
1762
+ /* Opening (<= t1): last-by-seq per pallet, must be STORED */
1763
+ opening_last_asof AS (
1764
+ SELECT DISTINCT ON (h.pallet_id) h.pallet_id, h.product_id, h.seq, h.status
1765
+ FROM rows_le_t1 h
1766
+ ORDER BY h.pallet_id, h.seq DESC
1767
+ ),
1768
+ opening_anchor AS (
1769
+ SELECT pallet_id, product_id
1770
+ FROM opening_last_asof
1771
+ WHERE status = 'STORED'
1772
+ ),
1773
+
1774
+ /* Closing (<= t2): last STORED per (pallet, product) by seq + validity */
1775
+ closing_last_stored AS (
1776
+ SELECT DISTINCT ON (h.pallet_id, h.product_id) h.pallet_id, h.product_id, h.seq
1777
+ FROM rows_le_t2 h
1778
+ WHERE h.status = 'STORED'
1779
+ ORDER BY h.pallet_id, h.product_id, h.seq DESC
1780
+ ),
1781
+ last_term_by_pair AS (
1782
+ SELECT DISTINCT ON (h.pallet_id, h.product_id) h.pallet_id, h.product_id, h.seq AS last_term_seq
1783
+ FROM rows_le_t2 h
1784
+ WHERE h.status = 'TERMINATED'
1785
+ ORDER BY h.pallet_id, h.product_id, h.seq DESC
1786
+ ),
1787
+ inbound_after_term AS (
1788
+ SELECT cls.pallet_id, cls.product_id
1789
+ FROM closing_last_stored cls
1790
+ LEFT JOIN last_term_by_pair lt
1791
+ ON lt.pallet_id = cls.pallet_id AND lt.product_id = cls.product_id
1792
+ WHERE EXISTS (
1793
+ SELECT 1
1794
+ FROM rows_le_t2 h
1795
+ WHERE h.pallet_id = cls.pallet_id
1796
+ AND h.product_id = cls.product_id
1797
+ AND h.transaction_type IN ('UNLOADING','NEW','CANCEL_ORDER')
1798
+ AND h.seq <= cls.seq
1799
+ AND (lt.last_term_seq IS NULL OR h.seq > lt.last_term_seq)
1800
+ )
1801
+ ),
1802
+ valid_closing AS (
1803
+ SELECT cls.pallet_id, cls.product_id
1804
+ FROM closing_last_stored cls
1805
+ LEFT JOIN inbound_after_term ia
1806
+ ON ia.pallet_id = cls.pallet_id AND ia.product_id = cls.product_id
1807
+ LEFT JOIN last_term_by_pair lt
1808
+ ON lt.pallet_id = cls.pallet_id AND lt.product_id = cls.product_id
1809
+ WHERE lt.last_term_seq IS NULL OR ia.pallet_id IS NOT NULL
1810
+ ),
1811
+ closing_anchor AS (
1812
+ SELECT pallet_id, product_id FROM valid_closing
1813
+ ),
1814
+
1815
+ /* Movement presence in window */
1816
+ inbound_pairs AS (
1817
+ SELECT pallet_id, product_id
1818
+ FROM rows_win
1819
+ WHERE transaction_type IN ('UNLOADING','NEW','CANCEL_ORDER')
1820
+ GROUP BY pallet_id, product_id
1821
+ ),
1822
+ terminated_pairs AS (
1823
+ SELECT pallet_id, product_id
1824
+ FROM rows_win
1825
+ WHERE status = 'TERMINATED' AND transaction_type <> 'ADJUSTMENT'
1826
+ GROUP BY pallet_id, product_id
1827
+ ),
1828
+ adjustment_last AS (
1829
+ SELECT x.pallet_id, x.product_id,
1830
+ CASE WHEN x.status = 'TERMINATED' THEN -1
1831
+ WHEN x.status = 'STORED' THEN 1
1832
+ ELSE 0 END AS adj_sign
1833
+ FROM (
1834
+ SELECT a.pallet_id, a.product_id, MAX(a.seq) AS max_seq
1835
+ FROM rows_win a
1836
+ WHERE a.transaction_type = 'ADJUSTMENT'
1837
+ GROUP BY a.pallet_id, a.product_id
1838
+ ) s
1839
+ JOIN rows_win x
1840
+ ON x.pallet_id = s.pallet_id AND x.product_id = s.product_id AND x.seq = s.max_seq
1841
+ ),
1842
+
1843
+ /* Union anchor (opening OR any movement OR valid closing) */
1844
+ anchor_union AS (
1845
+ SELECT * FROM opening_anchor
1846
+ UNION ALL
1847
+ SELECT * FROM inbound_pairs
1848
+ UNION ALL
1849
+ SELECT * FROM terminated_pairs
1850
+ UNION ALL
1851
+ SELECT * FROM closing_anchor
1852
+ ),
1853
+ anchor_distinct AS (
1854
+ SELECT DISTINCT pallet_id, product_id FROM anchor_union
1855
+ ),
1856
+
1857
+ /* Opening aggregation (anchored) */
1858
+ opening_agg AS (
1859
+ SELECT ol.product_id,
1860
+ COUNT(*) FILTER (WHERE ol.status = 'STORED')::int AS opening_qty
1861
+ FROM opening_last_asof ol
1862
+ JOIN anchor_distinct au ON au.pallet_id = ol.pallet_id AND au.product_id = ol.product_id
1863
+ GROUP BY ol.product_id
1864
+ ),
1865
+
1866
+ /* Movement aggregation (anchored) */
1867
+ movement_agg AS (
1868
+ SELECT
1869
+ COALESCE(inb.product_id, term.product_id, adj.product_id) AS product_id,
1870
+ SUM((inb.pallet_id IS NOT NULL)::int) AS total_in_qty,
1871
+ SUM((term.pallet_id IS NOT NULL)::int) AS total_out_qty,
1872
+ COALESCE(SUM(adj.adj_sign), 0)::int AS adjustment_qty
1873
+ FROM anchor_distinct au
1874
+ LEFT JOIN inbound_pairs inb ON inb.pallet_id = au.pallet_id AND inb.product_id = au.product_id
1875
+ LEFT JOIN terminated_pairs term ON term.pallet_id = au.pallet_id AND term.product_id = au.product_id
1876
+ LEFT JOIN adjustment_last adj ON adj.pallet_id = au.pallet_id AND adj.product_id = au.product_id
1877
+ GROUP BY COALESCE(inb.product_id, term.product_id, adj.product_id)
1878
+ ),
1879
+
1880
+ merged AS (
1881
+ SELECT
1882
+ COALESCE(o.product_id, m.product_id) AS product_id,
1883
+ COALESCE(o.opening_qty, 0) AS opening_qty,
1884
+ COALESCE(m.total_in_qty, 0) AS total_in_qty,
1885
+ COALESCE(m.total_out_qty, 0) AS total_out_qty,
1886
+ COALESCE(m.adjustment_qty, 0) AS adjustment_qty
1887
+ FROM opening_agg o
1888
+ FULL JOIN movement_agg m ON m.product_id = o.product_id
1889
+ )
1890
+
1891
+ SELECT
1892
+ p.sku AS product_sku,
1893
+ p.id AS product_id,
1894
+ p.name AS product_name,
1895
+ p.description AS product_description,
1896
+ p.type AS product_type,
1897
+ COALESCE(merged.opening_qty, 0) AS opening_qty,
1898
+ COALESCE(merged.total_in_qty, 0) AS total_in_qty,
1899
+ COALESCE(merged.total_out_qty, 0) AS total_out_qty,
1900
+ COALESCE(merged.adjustment_qty, 0) AS adjustment_qty,
1901
+ (COALESCE(merged.opening_qty, 0)
1902
+ + COALESCE(merged.total_in_qty, 0)
1903
+ + COALESCE(merged.total_out_qty, 0)
1904
+ + COALESCE(merged.adjustment_qty, 0))::int AS closing_qty
1905
+ FROM merged
1906
+ JOIN temp_products p ON p.id = merged.product_id
1907
+ WHERE ${hasTransactionOrBalanceQuery};
1950
1908
  `,
1951
1909
  [fromDate.value, toDate.value]
1952
1910
  )
@@ -2012,6 +1970,13 @@ async function productsQuery(tx: EntityManager, params: ListParam, bizplace: Biz
2012
1970
  )`,
2013
1971
  [bizplace.id]
2014
1972
  )
1973
+
1974
+ await tx.query(
1975
+ `
1976
+ CREATE INDEX IF NOT EXISTS idx_tprod_id ON temp_products (id);
1977
+ ANALYZE temp_products;
1978
+ `
1979
+ )
2015
1980
  }
2016
1981
 
2017
1982
  async function filterInventoryQuery(tx: EntityManager, params: ListParam, bizplace: Bizplace, context: any) {
@@ -2054,12 +2019,24 @@ async function filterInventoryQuery(tx: EntityManager, params: ListParam, bizpla
2054
2019
  WHERE ih.domain_id = $1
2055
2020
  AND ih.bizplace_id = $2
2056
2021
  AND ih.created_at <= $3
2057
- AND (ih.qty <> 0 OR ih.opening_qty <> 0 OR ih.status = 'MISSING')
2022
+ AND EXISTS (SELECT 1 FROM temp_products prd WHERE prd.id = ih.product_id::uuid)
2023
+ ${batchNoQuery}
2058
2024
  )
2059
2025
  `,
2060
2026
  [context.state.domain.id, bizplace.id, toDate.value]
2061
2027
  )
2062
2028
 
2029
+ // Speed up joins and windowed lookups that follow
2030
+ await tx.query(
2031
+ `
2032
+ CREATE INDEX IF NOT EXISTS idx_tfh_pallet_domain ON temp_filtered_histories (pallet_id, domain_id);
2033
+ CREATE INDEX IF NOT EXISTS idx_tfh_product ON temp_filtered_histories (product_id);
2034
+ CREATE INDEX IF NOT EXISTS idx_tfh_created_at ON temp_filtered_histories (created_at);
2035
+ CREATE INDEX IF NOT EXISTS idx_tfh_pallet_seq ON temp_filtered_histories (pallet_id, seq);
2036
+ ANALYZE temp_filtered_histories;
2037
+ `
2038
+ )
2039
+
2063
2040
  /* Step 2: build temp_inv_history by joining inventories with indexed temp_filtered_histories and temp_products */
2064
2041
  await tx.query(
2065
2042
  `
@@ -2072,8 +2049,21 @@ async function filterInventoryQuery(tx: EntityManager, params: ListParam, bizpla
2072
2049
  AND ih.domain_id = i2.domain_id
2073
2050
  INNER JOIN temp_products prd
2074
2051
  ON prd.id = ih.product_id::uuid
2075
- ${batchNoQuery}
2076
2052
  )
2077
2053
  `
2078
2054
  )
2055
+
2056
+ // Critical indexes for the heavy pallet summary query
2057
+ await tx.query(
2058
+ `
2059
+ CREATE INDEX IF NOT EXISTS idx_tih_created_at ON temp_inv_history (created_at);
2060
+ CREATE INDEX IF NOT EXISTS idx_tih_pallet_seq ON temp_inv_history (pallet_id, seq);
2061
+ CREATE INDEX IF NOT EXISTS idx_tih_pallet_product_seq ON temp_inv_history (pallet_id, product_id, seq);
2062
+ CREATE INDEX IF NOT EXISTS idx_tih_product ON temp_inv_history (product_id);
2063
+ CREATE INDEX IF NOT EXISTS idx_tih_pallet_product ON temp_inv_history (pallet_id, product_id);
2064
+ CREATE INDEX IF NOT EXISTS idx_tih_status ON temp_inv_history (status);
2065
+ CREATE INDEX IF NOT EXISTS idx_tih_txn ON temp_inv_history (transaction_type);
2066
+ ANALYZE temp_inv_history;
2067
+ `
2068
+ )
2079
2069
  }