Merge branch 'dev/fifo-v2' into 'development'

Implement delete transfer to laying, chickin, and stock adjustment

See merge request mbugroup/lti-api!371
This commit is contained in:
Adnan Zahir
2026-03-17 11:07:52 +07:00
52 changed files with 4244 additions and 820 deletions
@@ -0,0 +1,118 @@
BEGIN;
-- MARKETING_OUT: if AYAM-only rule exists, convert back to global rule.
UPDATE fifo_stock_v2_overconsume_rules
SET
flag_group_code = NULL,
allow_overconsume = FALSE,
priority = 20,
reason = 'fifo_v2_exception_marketing_block',
is_active = TRUE
WHERE lane = 'USABLE'
AND function_code = 'MARKETING_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_marketing_block_ayam_only';
-- MARKETING_OUT: if global row already exists, keep it active.
UPDATE fifo_stock_v2_overconsume_rules
SET
allow_overconsume = FALSE,
priority = 20,
is_active = TRUE
WHERE lane = 'USABLE'
AND function_code = 'MARKETING_OUT'
AND flag_group_code IS NULL
AND reason = 'fifo_v2_exception_marketing_block';
-- MARKETING_OUT: insert global rule if still missing.
INSERT INTO fifo_stock_v2_overconsume_rules(
flag_group_code,
function_code,
lane,
allow_overconsume,
priority,
reason,
is_active
)
SELECT NULL, 'MARKETING_OUT', 'USABLE', FALSE, 20, 'fifo_v2_exception_marketing_block', TRUE
WHERE NOT EXISTS (
SELECT 1
FROM fifo_stock_v2_overconsume_rules
WHERE lane = 'USABLE'
AND function_code = 'MARKETING_OUT'
AND flag_group_code IS NULL
AND reason = 'fifo_v2_exception_marketing_block'
);
-- MARKETING_OUT: deactivate AYAM-only duplicates if any remain.
UPDATE fifo_stock_v2_overconsume_rules
SET
is_active = FALSE
WHERE lane = 'USABLE'
AND function_code = 'MARKETING_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_marketing_block_ayam_only';
-- STOCK_TRANSFER_OUT: if AYAM-only rule exists, convert back to global rule.
UPDATE fifo_stock_v2_overconsume_rules
SET
flag_group_code = NULL,
allow_overconsume = FALSE,
priority = 30,
reason = 'fifo_v2_exception_transfer_block',
is_active = TRUE
WHERE lane = 'USABLE'
AND function_code = 'STOCK_TRANSFER_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_transfer_block_ayam_only';
-- STOCK_TRANSFER_OUT: if global row already exists, keep it active.
UPDATE fifo_stock_v2_overconsume_rules
SET
allow_overconsume = FALSE,
priority = 30,
is_active = TRUE
WHERE lane = 'USABLE'
AND function_code = 'STOCK_TRANSFER_OUT'
AND flag_group_code IS NULL
AND reason = 'fifo_v2_exception_transfer_block';
-- STOCK_TRANSFER_OUT: insert global rule if still missing.
INSERT INTO fifo_stock_v2_overconsume_rules(
flag_group_code,
function_code,
lane,
allow_overconsume,
priority,
reason,
is_active
)
SELECT NULL, 'STOCK_TRANSFER_OUT', 'USABLE', FALSE, 30, 'fifo_v2_exception_transfer_block', TRUE
WHERE NOT EXISTS (
SELECT 1
FROM fifo_stock_v2_overconsume_rules
WHERE lane = 'USABLE'
AND function_code = 'STOCK_TRANSFER_OUT'
AND flag_group_code IS NULL
AND reason = 'fifo_v2_exception_transfer_block'
);
-- STOCK_TRANSFER_OUT: deactivate AYAM-only duplicates if any remain.
UPDATE fifo_stock_v2_overconsume_rules
SET
is_active = FALSE
WHERE lane = 'USABLE'
AND function_code = 'STOCK_TRANSFER_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_transfer_block_ayam_only';
-- CHICKIN_OUT: rollback AYAM-only hard-block added by up migration.
UPDATE fifo_stock_v2_overconsume_rules
SET
is_active = FALSE
WHERE lane = 'USABLE'
AND function_code = 'CHICKIN_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_chickin_block_ayam_only';
COMMIT;
@@ -0,0 +1,139 @@
BEGIN;
-- MARKETING_OUT: if global rule exists, convert to AYAM-specific.
UPDATE fifo_stock_v2_overconsume_rules
SET
flag_group_code = 'AYAM',
allow_overconsume = FALSE,
priority = 20,
reason = 'fifo_v2_exception_marketing_block_ayam_only',
is_active = TRUE
WHERE lane = 'USABLE'
AND function_code = 'MARKETING_OUT'
AND flag_group_code IS NULL
AND reason = 'fifo_v2_exception_marketing_block';
-- MARKETING_OUT: if AYAM-specific row already exists, enforce desired value.
UPDATE fifo_stock_v2_overconsume_rules
SET
allow_overconsume = FALSE,
priority = 20,
is_active = TRUE
WHERE lane = 'USABLE'
AND function_code = 'MARKETING_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_marketing_block_ayam_only';
-- MARKETING_OUT: insert AYAM-specific if no suitable row exists.
INSERT INTO fifo_stock_v2_overconsume_rules(
flag_group_code,
function_code,
lane,
allow_overconsume,
priority,
reason,
is_active
)
SELECT 'AYAM', 'MARKETING_OUT', 'USABLE', FALSE, 20, 'fifo_v2_exception_marketing_block_ayam_only', TRUE
WHERE NOT EXISTS (
SELECT 1
FROM fifo_stock_v2_overconsume_rules
WHERE lane = 'USABLE'
AND function_code = 'MARKETING_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_marketing_block_ayam_only'
);
-- MARKETING_OUT: deactivate remaining global rule (if any duplicate row exists).
UPDATE fifo_stock_v2_overconsume_rules
SET
is_active = FALSE
WHERE lane = 'USABLE'
AND function_code = 'MARKETING_OUT'
AND flag_group_code IS NULL
AND reason = 'fifo_v2_exception_marketing_block';
-- STOCK_TRANSFER_OUT: if global rule exists, convert to AYAM-specific.
UPDATE fifo_stock_v2_overconsume_rules
SET
flag_group_code = 'AYAM',
allow_overconsume = FALSE,
priority = 30,
reason = 'fifo_v2_exception_transfer_block_ayam_only',
is_active = TRUE
WHERE lane = 'USABLE'
AND function_code = 'STOCK_TRANSFER_OUT'
AND flag_group_code IS NULL
AND reason = 'fifo_v2_exception_transfer_block';
-- STOCK_TRANSFER_OUT: if AYAM-specific row already exists, enforce desired value.
UPDATE fifo_stock_v2_overconsume_rules
SET
allow_overconsume = FALSE,
priority = 30,
is_active = TRUE
WHERE lane = 'USABLE'
AND function_code = 'STOCK_TRANSFER_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_transfer_block_ayam_only';
-- STOCK_TRANSFER_OUT: insert AYAM-specific if no suitable row exists.
INSERT INTO fifo_stock_v2_overconsume_rules(
flag_group_code,
function_code,
lane,
allow_overconsume,
priority,
reason,
is_active
)
SELECT 'AYAM', 'STOCK_TRANSFER_OUT', 'USABLE', FALSE, 30, 'fifo_v2_exception_transfer_block_ayam_only', TRUE
WHERE NOT EXISTS (
SELECT 1
FROM fifo_stock_v2_overconsume_rules
WHERE lane = 'USABLE'
AND function_code = 'STOCK_TRANSFER_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_transfer_block_ayam_only'
);
-- STOCK_TRANSFER_OUT: deactivate remaining global rule (if any duplicate row exists).
UPDATE fifo_stock_v2_overconsume_rules
SET
is_active = FALSE
WHERE lane = 'USABLE'
AND function_code = 'STOCK_TRANSFER_OUT'
AND flag_group_code IS NULL
AND reason = 'fifo_v2_exception_transfer_block';
-- CHICKIN_OUT: enforce AYAM-specific hard-block (cannot pending).
UPDATE fifo_stock_v2_overconsume_rules
SET
allow_overconsume = FALSE,
priority = 25,
is_active = TRUE
WHERE lane = 'USABLE'
AND function_code = 'CHICKIN_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_chickin_block_ayam_only';
INSERT INTO fifo_stock_v2_overconsume_rules(
flag_group_code,
function_code,
lane,
allow_overconsume,
priority,
reason,
is_active
)
SELECT 'AYAM', 'CHICKIN_OUT', 'USABLE', FALSE, 25, 'fifo_v2_exception_chickin_block_ayam_only', TRUE
WHERE NOT EXISTS (
SELECT 1
FROM fifo_stock_v2_overconsume_rules
WHERE lane = 'USABLE'
AND function_code = 'CHICKIN_OUT'
AND flag_group_code = 'AYAM'
AND reason = 'fifo_v2_exception_chickin_block_ayam_only'
);
COMMIT;
@@ -0,0 +1,14 @@
BEGIN;
ALTER TABLE adjustment_stocks
DROP CONSTRAINT IF EXISTS chk_adjustment_stocks_paired_not_self;
ALTER TABLE adjustment_stocks
DROP CONSTRAINT IF EXISTS fk_adjustment_stocks_paired_adjustment_id;
DROP INDEX IF EXISTS idx_adjustment_stocks_paired_adjustment_id;
ALTER TABLE adjustment_stocks
DROP COLUMN IF EXISTS paired_adjustment_id;
COMMIT;
@@ -0,0 +1,86 @@
BEGIN;
ALTER TABLE adjustment_stocks
ADD COLUMN IF NOT EXISTS paired_adjustment_id BIGINT NULL;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'fk_adjustment_stocks_paired_adjustment_id'
) THEN
ALTER TABLE adjustment_stocks
ADD CONSTRAINT fk_adjustment_stocks_paired_adjustment_id
FOREIGN KEY (paired_adjustment_id)
REFERENCES adjustment_stocks(id)
ON DELETE SET NULL
ON UPDATE CASCADE;
END IF;
END $$;
ALTER TABLE adjustment_stocks
DROP CONSTRAINT IF EXISTS chk_adjustment_stocks_paired_not_self;
ALTER TABLE adjustment_stocks
ADD CONSTRAINT chk_adjustment_stocks_paired_not_self
CHECK (paired_adjustment_id IS NULL OR paired_adjustment_id <> id);
CREATE INDEX IF NOT EXISTS idx_adjustment_stocks_paired_adjustment_id
ON adjustment_stocks(paired_adjustment_id);
-- Backfill pairing untuk depletion-out <-> depletion-in existing records.
WITH candidates AS (
SELECT
src.id AS src_id,
dst.id AS dst_id,
ABS(EXTRACT(EPOCH FROM (dst.created_at - src.created_at))) AS ts_diff,
ABS(dst.id - src.id) AS id_diff,
ROW_NUMBER() OVER (
PARTITION BY src.id
ORDER BY ABS(EXTRACT(EPOCH FROM (dst.created_at - src.created_at))) ASC,
ABS(dst.id - src.id) ASC,
dst.id ASC
) AS rn_src,
ROW_NUMBER() OVER (
PARTITION BY dst.id
ORDER BY ABS(EXTRACT(EPOCH FROM (dst.created_at - src.created_at))) ASC,
ABS(dst.id - src.id) ASC,
src.id ASC
) AS rn_dst
FROM adjustment_stocks src
JOIN adjustment_stocks dst
ON dst.id <> src.id
AND dst.transaction_type = src.transaction_type
AND dst.function_code = 'RECORDING_DEPLETION_IN'
AND src.function_code = 'RECORDING_DEPLETION_OUT'
AND dst.paired_adjustment_id IS NULL
AND src.paired_adjustment_id IS NULL
AND ABS((COALESCE(src.usage_qty, 0) + COALESCE(src.pending_qty, 0)) - COALESCE(dst.total_qty, 0)) < 0.0001
AND COALESCE(src.price, 0) = COALESCE(dst.price, 0)
AND COALESCE(src.grand_total, 0) = COALESCE(dst.grand_total, 0)
AND ABS(EXTRACT(EPOCH FROM (dst.created_at - src.created_at))) <= 120
),
chosen AS (
SELECT src_id, dst_id
FROM candidates
WHERE rn_src = 1
AND rn_dst = 1
)
UPDATE adjustment_stocks src
SET paired_adjustment_id = c.dst_id
FROM chosen c
WHERE src.id = c.src_id
AND src.paired_adjustment_id IS NULL;
WITH chosen AS (
SELECT a.id AS src_id, a.paired_adjustment_id AS dst_id
FROM adjustment_stocks a
WHERE a.function_code = 'RECORDING_DEPLETION_OUT'
AND a.paired_adjustment_id IS NOT NULL
)
UPDATE adjustment_stocks dst
SET paired_adjustment_id = c.src_id
FROM chosen c
WHERE dst.id = c.dst_id
AND dst.paired_adjustment_id IS NULL;
COMMIT;