From 1c5b013b9fdcf12eee2fbebc242e663185d5963a Mon Sep 17 00:00:00 2001 From: M1 AIR Date: Sun, 8 Mar 2026 19:53:24 +0700 Subject: [PATCH] fix: make fifo migrations order-safe for fresh setup --- ...8090010_seed_fifo_stock_v2_config.down.sql | 34 +-- ...218090010_seed_fifo_stock_v2_config.up.sql | 248 +---------------- ..._disable_chickin_fifo_consumption.down.sql | 10 +- ...07_disable_chickin_fifo_consumption.up.sql | 149 +---------- ...d_fifo_stock_v2_config_after_core.down.sql | 37 +++ ...eed_fifo_stock_v2_config_after_core.up.sql | 250 ++++++++++++++++++ ...ickin_fifo_consumption_after_core.down.sql | 13 + ...chickin_fifo_consumption_after_core.up.sql | 151 +++++++++++ 8 files changed, 457 insertions(+), 435 deletions(-) create mode 100644 internal/database/migrations/20260306090010_seed_fifo_stock_v2_config_after_core.down.sql create mode 100644 internal/database/migrations/20260306090010_seed_fifo_stock_v2_config_after_core.up.sql create mode 100644 internal/database/migrations/20260306090011_disable_chickin_fifo_consumption_after_core.down.sql create mode 100644 internal/database/migrations/20260306090011_disable_chickin_fifo_consumption_after_core.up.sql diff --git a/internal/database/migrations/20260218090010_seed_fifo_stock_v2_config.down.sql b/internal/database/migrations/20260218090010_seed_fifo_stock_v2_config.down.sql index 05786a61..771c060a 100644 --- a/internal/database/migrations/20260218090010_seed_fifo_stock_v2_config.down.sql +++ b/internal/database/migrations/20260218090010_seed_fifo_stock_v2_config.down.sql @@ -1,37 +1,5 @@ BEGIN; -DELETE FROM fifo_stock_v2_overconsume_rules -WHERE reason IN ( - 'fifo_v2_default_allow', - 'fifo_v2_exception_ayam_depletion_block', - 'fifo_v2_exception_marketing_block', - 'fifo_v2_exception_transfer_block', - 'fifo_v2_exception_adjustment_block', - 'fifo_v2_exception_transfer_laying_block' -); - -DELETE FROM fifo_stock_v2_route_rules -WHERE flag_group_code IN ('AYAM', 'AFKIR_CULLING_MATI', 'PAKAN', 'OVK', 'TELUR', 'TELUR_GRADE'); - -DELETE FROM fifo_stock_v2_traits -WHERE source_table IN ( - 'purchase_items', - 'stock_transfer_details', - 'laying_transfer_targets', - 'laying_transfer_sources', - 'adjustment_stocks', - 'recording_stocks', - 'recording_depletions', - 'recording_eggs', - 'marketing_delivery_products', - 'project_chickins', - 'project_flock_populations' -); - -DELETE FROM fifo_stock_v2_flag_members -WHERE flag_group_code IN ('AYAM', 'AFKIR_CULLING_MATI', 'PAKAN', 'OVK', 'TELUR', 'TELUR_GRADE'); - -DELETE FROM fifo_stock_v2_flag_groups -WHERE code IN ('AYAM', 'AFKIR_CULLING_MATI', 'PAKAN', 'OVK', 'TELUR', 'TELUR_GRADE'); +-- no-op: moved to 20260306090010_seed_fifo_stock_v2_config_after_core.down.sql COMMIT; diff --git a/internal/database/migrations/20260218090010_seed_fifo_stock_v2_config.up.sql b/internal/database/migrations/20260218090010_seed_fifo_stock_v2_config.up.sql index f791aebf..11fe42ac 100644 --- a/internal/database/migrations/20260218090010_seed_fifo_stock_v2_config.up.sql +++ b/internal/database/migrations/20260218090010_seed_fifo_stock_v2_config.up.sql @@ -1,250 +1,6 @@ BEGIN; -INSERT INTO fifo_stock_v2_flag_groups(code, name, priority) -VALUES - ('AYAM', 'AYAM', 10), - ('AFKIR_CULLING_MATI', 'AFKIR/CULLING/MATI', 20), - ('PAKAN', 'PAKAN', 30), - ('OVK', 'OVK', 40), - ('TELUR', 'TELUR', 50), - ('TELUR_GRADE', 'UTUH/PUTIH/RETAK/PECAH/PAPACAL/JUMBO', 60) -ON CONFLICT (code) DO UPDATE -SET - name = EXCLUDED.name, - priority = EXCLUDED.priority, - updated_at = NOW(); - -INSERT INTO fifo_stock_v2_flag_members(flag_name, flag_group_code, priority) -VALUES - ('DOC', 'AYAM', 10), - ('PULLET', 'AYAM', 20), - ('LAYER', 'AYAM', 30), - - ('AYAM-AFKIR', 'AFKIR_CULLING_MATI', 10), - ('AYAM-CULLING', 'AFKIR_CULLING_MATI', 20), - ('AYAM-MATI', 'AFKIR_CULLING_MATI', 30), - - ('PAKAN', 'PAKAN', 10), - ('PRE-STARTER', 'PAKAN', 20), - ('STARTER', 'PAKAN', 30), - ('FINISHER', 'PAKAN', 40), - - ('OVK', 'OVK', 10), - ('OBAT', 'OVK', 20), - ('VITAMIN', 'OVK', 30), - ('KIMIA', 'OVK', 40), - - ('TELUR', 'TELUR', 10), - - ('TELUR-UTUH', 'TELUR_GRADE', 10), - ('TELUR-PUTIH', 'TELUR_GRADE', 20), - ('TELUR-RETAK', 'TELUR_GRADE', 30), - ('TELUR-PECAH', 'TELUR_GRADE', 40), - ('TELUR-PAPACAL', 'TELUR_GRADE', 50), - ('TELUR-JUMBO', 'TELUR_GRADE', 60) -ON CONFLICT (flag_name) DO UPDATE -SET - flag_group_code = EXCLUDED.flag_group_code, - priority = EXCLUDED.priority, - updated_at = NOW(); - -INSERT INTO fifo_stock_v2_traits( - source_table, - lane, - date_table, - date_join_left_col, - date_join_right_col, - date_column, - fallback_date_column, - sort_priority, - id_column -) -VALUES - ('purchase_items', 'STOCKABLE', NULL, NULL, NULL, 'received_date', NULL, 10, 'id'), - - ('stock_transfer_details', 'STOCKABLE', 'stock_transfers', 'stock_transfer_id', 'id', 'transfer_date', NULL, 20, 'id'), - ('stock_transfer_details', 'USABLE', 'stock_transfers', 'stock_transfer_id', 'id', 'transfer_date', NULL, 20, 'id'), - - ('laying_transfer_targets', 'STOCKABLE', 'laying_transfers', 'laying_transfer_id', 'id', 'transfer_date', NULL, 25, 'id'), - ('laying_transfer_sources', 'USABLE', 'laying_transfers', 'laying_transfer_id', 'id', 'transfer_date', NULL, 25, 'id'), - - ('adjustment_stocks', 'STOCKABLE', NULL, NULL, NULL, 'created_at', NULL, 30, 'id'), - ('adjustment_stocks', 'USABLE', NULL, NULL, NULL, 'created_at', NULL, 30, 'id'), - - ('recording_stocks', 'USABLE', 'recordings', 'recording_id', 'id', 'record_datetime', NULL, 35, 'id'), - ('recording_depletions', 'USABLE', 'recordings', 'recording_id', 'id', 'record_datetime', NULL, 35, 'id'), - ('recording_depletions', 'STOCKABLE', 'recordings', 'recording_id', 'id', 'record_datetime', NULL, 35, 'id'), - - ('recording_eggs', 'STOCKABLE', 'recordings', 'recording_id', 'id', 'record_datetime', 'created_at', 40, 'id'), - - ('marketing_delivery_products', 'USABLE', NULL, NULL, NULL, 'delivery_date', 'created_at', 45, 'id'), - - ('project_chickins', 'USABLE', NULL, NULL, NULL, 'chick_in_date', 'created_at', 50, 'id'), - ('project_flock_populations', 'STOCKABLE', 'project_chickins', 'project_chickin_id', 'id', 'chick_in_date', 'created_at', 55, 'id') -ON CONFLICT (source_table, lane) DO UPDATE -SET - date_table = EXCLUDED.date_table, - date_join_left_col = EXCLUDED.date_join_left_col, - date_join_right_col = EXCLUDED.date_join_right_col, - date_column = EXCLUDED.date_column, - fallback_date_column = EXCLUDED.fallback_date_column, - sort_priority = EXCLUDED.sort_priority, - id_column = EXCLUDED.id_column, - is_active = TRUE; - -INSERT INTO fifo_stock_v2_route_rules( - flag_group_code, - lane, - function_code, - source_table, - source_id_column, - product_warehouse_col, - quantity_col, - used_quantity_col, - pending_quantity_col, - scope_sql, - legacy_type_key, - allow_pending_default, - is_active -) -VALUES - -- AYAM STOCKABLE - ('AYAM', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), - ('AYAM', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), - ('AYAM', 'STOCKABLE', 'PURCHASE_IN', 'purchase_items', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'PURCHASE_ITEMS', TRUE, TRUE), - ('AYAM', 'STOCKABLE', 'TRANSFER_TO_LAYING_IN', 'laying_transfer_targets', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'TRANSFERTOLAYING_IN', TRUE, TRUE), - ('AYAM', 'STOCKABLE', 'POPULATION_IN', 'project_flock_populations', 'id', 'product_warehouse_id', 'total_qty', 'total_used_qty', NULL, NULL, 'PROJECT_FLOCK_POPULATION', TRUE, TRUE), - - -- AYAM USABLE - ('AYAM', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), - ('AYAM', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), - ('AYAM', 'USABLE', 'CHICKIN_OUT', 'project_chickins', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_usage_qty', 'deleted_at IS NULL', 'PROJECT_CHICKIN', TRUE, TRUE), - ('AYAM', 'USABLE', 'RECORDING_DEPLETION_OUT', 'recording_depletions', 'id', 'source_product_warehouse_id', 'qty', NULL, 'pending_qty', NULL, 'RECORDING_DEPLETION', TRUE, TRUE), - ('AYAM', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), - ('AYAM', 'USABLE', 'TRANSFER_TO_LAYING_OUT', 'laying_transfer_sources', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_usage_qty', 'deleted_at IS NULL', 'TRANSFERTOLAYING_OUT', TRUE, TRUE), - - -- AFKIR/CULLING/MATI STOCKABLE - ('AFKIR_CULLING_MATI', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), - ('AFKIR_CULLING_MATI', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), - ('AFKIR_CULLING_MATI', 'STOCKABLE', 'RECORDING_DEPLETION_IN', 'recording_depletions', 'id', 'product_warehouse_id', 'qty', NULL, NULL, NULL, 'RECORDING_DEPLETION', TRUE, TRUE), - - -- AFKIR/CULLING/MATI USABLE - ('AFKIR_CULLING_MATI', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), - ('AFKIR_CULLING_MATI', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), - ('AFKIR_CULLING_MATI', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), - - -- PAKAN STOCKABLE - ('PAKAN', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), - ('PAKAN', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), - ('PAKAN', 'STOCKABLE', 'PURCHASE_IN', 'purchase_items', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'PURCHASE_ITEMS', TRUE, TRUE), - - -- PAKAN USABLE - ('PAKAN', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), - ('PAKAN', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), - ('PAKAN', 'USABLE', 'RECORDING_STOCK_OUT', 'recording_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'RECORDING_STOCK', TRUE, TRUE), - ('PAKAN', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), - - -- OVK STOCKABLE - ('OVK', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), - ('OVK', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), - ('OVK', 'STOCKABLE', 'PURCHASE_IN', 'purchase_items', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'PURCHASE_ITEMS', TRUE, TRUE), - - -- OVK USABLE - ('OVK', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), - ('OVK', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), - ('OVK', 'USABLE', 'RECORDING_STOCK_OUT', 'recording_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'RECORDING_STOCK', TRUE, TRUE), - ('OVK', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), - - -- TELUR STOCKABLE - ('TELUR', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), - ('TELUR', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), - ('TELUR', 'STOCKABLE', 'RECORDING_EGG_IN', 'recording_eggs', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'RECORDING_EGG', TRUE, TRUE), - - -- TELUR USABLE - ('TELUR', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), - ('TELUR', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), - ('TELUR', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), - - -- TELUR_GRADE STOCKABLE - ('TELUR_GRADE', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), - ('TELUR_GRADE', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), - ('TELUR_GRADE', 'STOCKABLE', 'RECORDING_EGG_IN', 'recording_eggs', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'RECORDING_EGG', TRUE, TRUE), - - -- TELUR_GRADE USABLE - ('TELUR_GRADE', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), - ('TELUR_GRADE', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), - ('TELUR_GRADE', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE) -ON CONFLICT (flag_group_code, lane, function_code, source_table) DO UPDATE -SET - source_id_column = EXCLUDED.source_id_column, - product_warehouse_col = EXCLUDED.product_warehouse_col, - quantity_col = EXCLUDED.quantity_col, - used_quantity_col = EXCLUDED.used_quantity_col, - pending_quantity_col = EXCLUDED.pending_quantity_col, - scope_sql = EXCLUDED.scope_sql, - legacy_type_key = EXCLUDED.legacy_type_key, - allow_pending_default = EXCLUDED.allow_pending_default, - is_active = EXCLUDED.is_active, - updated_at = NOW(); - -INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active) -SELECT NULL, NULL, 'USABLE', TRUE, 999, 'fifo_v2_default_allow', TRUE -WHERE NOT EXISTS ( - SELECT 1 FROM fifo_stock_v2_overconsume_rules - WHERE flag_group_code IS NULL - AND function_code IS NULL - AND lane = 'USABLE' - AND reason = 'fifo_v2_default_allow' -); - -INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active) -SELECT 'AYAM', 'RECORDING_DEPLETION_OUT', 'USABLE', FALSE, 10, 'fifo_v2_exception_ayam_depletion_block', TRUE -WHERE NOT EXISTS ( - SELECT 1 FROM fifo_stock_v2_overconsume_rules - WHERE flag_group_code = 'AYAM' - AND function_code = 'RECORDING_DEPLETION_OUT' - AND lane = 'USABLE' - AND reason = 'fifo_v2_exception_ayam_depletion_block' -); - -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 flag_group_code IS NULL - AND function_code = 'MARKETING_OUT' - AND lane = 'USABLE' - AND reason = 'fifo_v2_exception_marketing_block' -); - -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 flag_group_code IS NULL - AND function_code = 'STOCK_TRANSFER_OUT' - AND lane = 'USABLE' - AND reason = 'fifo_v2_exception_transfer_block' -); - -INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active) -SELECT NULL, 'ADJUSTMENT_OUT', 'USABLE', FALSE, 40, 'fifo_v2_exception_adjustment_block', TRUE -WHERE NOT EXISTS ( - SELECT 1 FROM fifo_stock_v2_overconsume_rules - WHERE flag_group_code IS NULL - AND function_code = 'ADJUSTMENT_OUT' - AND lane = 'USABLE' - AND reason = 'fifo_v2_exception_adjustment_block' -); - -INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active) -SELECT NULL, 'TRANSFER_TO_LAYING_OUT', 'USABLE', FALSE, 50, 'fifo_v2_exception_transfer_laying_block', TRUE -WHERE NOT EXISTS ( - SELECT 1 FROM fifo_stock_v2_overconsume_rules - WHERE flag_group_code IS NULL - AND function_code = 'TRANSFER_TO_LAYING_OUT' - AND lane = 'USABLE' - AND reason = 'fifo_v2_exception_transfer_laying_block' -); +-- no-op: moved to 20260306090010_seed_fifo_stock_v2_config_after_core.up.sql +-- to ensure FIFO core tables exist before seeding on fresh migrations. COMMIT; diff --git a/internal/database/migrations/20260228143207_disable_chickin_fifo_consumption.down.sql b/internal/database/migrations/20260228143207_disable_chickin_fifo_consumption.down.sql index ee662a07..d39d2de9 100644 --- a/internal/database/migrations/20260228143207_disable_chickin_fifo_consumption.down.sql +++ b/internal/database/migrations/20260228143207_disable_chickin_fifo_consumption.down.sql @@ -1,13 +1,5 @@ BEGIN; --- Restore CHICKIN route if rollback is required. --- NOTE: released PROJECT_CHICKIN allocations are not restored by this down migration. -UPDATE fifo_stock_v2_route_rules -SET is_active = TRUE, - updated_at = NOW() -WHERE flag_group_code = 'AYAM' - AND lane = 'USABLE' - AND function_code = 'CHICKIN_OUT' - AND source_table = 'project_chickins'; +-- no-op: moved to 20260306090011_disable_chickin_fifo_consumption_after_core.down.sql COMMIT; diff --git a/internal/database/migrations/20260228143207_disable_chickin_fifo_consumption.up.sql b/internal/database/migrations/20260228143207_disable_chickin_fifo_consumption.up.sql index 43936c01..447f72bf 100644 --- a/internal/database/migrations/20260228143207_disable_chickin_fifo_consumption.up.sql +++ b/internal/database/migrations/20260228143207_disable_chickin_fifo_consumption.up.sql @@ -1,151 +1,6 @@ BEGIN; --- Disable CHICKIN as FIFO USABLE so chick-in acts as business tagging/conversion, --- not physical stock consumption. -UPDATE fifo_stock_v2_route_rules -SET is_active = FALSE, - updated_at = NOW() -WHERE flag_group_code = 'AYAM' - AND lane = 'USABLE' - AND function_code = 'CHICKIN_OUT' - AND source_table = 'project_chickins' - AND is_active = TRUE; - --- Release existing active allocations created by PROJECT_CHICKIN --- and return warehouse qty back. -WITH released AS ( - UPDATE stock_allocations - SET status = 'RELEASED', - released_at = COALESCE(released_at, NOW()), - updated_at = NOW(), - note = CASE - WHEN COALESCE(note, '') = '' THEN 'fifo_v2_chickin_conversion_release' - ELSE note || '; fifo_v2_chickin_conversion_release' - END - WHERE usable_type = 'PROJECT_CHICKIN' - AND status = 'ACTIVE' - RETURNING product_warehouse_id, qty -), -pw_delta AS ( - SELECT product_warehouse_id, COALESCE(SUM(qty), 0) AS qty_delta - FROM released - GROUP BY product_warehouse_id -) -UPDATE product_warehouses pw -SET qty = COALESCE(pw.qty, 0) + d.qty_delta -FROM pw_delta d -WHERE pw.id = d.product_warehouse_id; - --- Resync stockable total_used columns from remaining ACTIVE allocations. - --- purchase_items (PURCHASE_ITEMS) -UPDATE purchase_items pi -SET total_used = COALESCE(a.used, 0) -FROM ( - SELECT stockable_id, SUM(qty) AS used - FROM stock_allocations - WHERE status = 'ACTIVE' - AND stockable_type = 'PURCHASE_ITEMS' - GROUP BY stockable_id -) a -WHERE pi.id = a.stockable_id; - -UPDATE purchase_items pi -SET total_used = 0 -WHERE NOT EXISTS ( - SELECT 1 - FROM stock_allocations sa - WHERE sa.status = 'ACTIVE' - AND sa.stockable_type = 'PURCHASE_ITEMS' - AND sa.stockable_id = pi.id -); - --- stock_transfer_details (STOCK_TRANSFER_IN) -UPDATE stock_transfer_details std -SET total_used = COALESCE(a.used, 0) -FROM ( - SELECT stockable_id, SUM(qty) AS used - FROM stock_allocations - WHERE status = 'ACTIVE' - AND stockable_type = 'STOCK_TRANSFER_IN' - GROUP BY stockable_id -) a -WHERE std.id = a.stockable_id; - -UPDATE stock_transfer_details std -SET total_used = 0 -WHERE NOT EXISTS ( - SELECT 1 - FROM stock_allocations sa - WHERE sa.status = 'ACTIVE' - AND sa.stockable_type = 'STOCK_TRANSFER_IN' - AND sa.stockable_id = std.id -); - --- adjustment_stocks (ADJUSTMENT_IN) -UPDATE adjustment_stocks ast -SET total_used = COALESCE(a.used, 0) -FROM ( - SELECT stockable_id, SUM(qty) AS used - FROM stock_allocations - WHERE status = 'ACTIVE' - AND stockable_type = 'ADJUSTMENT_IN' - GROUP BY stockable_id -) a -WHERE ast.id = a.stockable_id; - -UPDATE adjustment_stocks ast -SET total_used = 0 -WHERE NOT EXISTS ( - SELECT 1 - FROM stock_allocations sa - WHERE sa.status = 'ACTIVE' - AND sa.stockable_type = 'ADJUSTMENT_IN' - AND sa.stockable_id = ast.id -); - --- laying_transfer_targets (TRANSFERTOLAYING_IN) -UPDATE laying_transfer_targets ltt -SET total_used = COALESCE(a.used, 0) -FROM ( - SELECT stockable_id, SUM(qty) AS used - FROM stock_allocations - WHERE status = 'ACTIVE' - AND stockable_type = 'TRANSFERTOLAYING_IN' - GROUP BY stockable_id -) a -WHERE ltt.id = a.stockable_id; - -UPDATE laying_transfer_targets ltt -SET total_used = 0 -WHERE NOT EXISTS ( - SELECT 1 - FROM stock_allocations sa - WHERE sa.status = 'ACTIVE' - AND sa.stockable_type = 'TRANSFERTOLAYING_IN' - AND sa.stockable_id = ltt.id -); - --- recording_eggs (RECORDING_EGG) -UPDATE recording_eggs re -SET total_used = COALESCE(a.used, 0) -FROM ( - SELECT stockable_id, SUM(qty) AS used - FROM stock_allocations - WHERE status = 'ACTIVE' - AND stockable_type = 'RECORDING_EGG' - GROUP BY stockable_id -) a -WHERE re.id = a.stockable_id; - -UPDATE recording_eggs re -SET total_used = 0 -WHERE NOT EXISTS ( - SELECT 1 - FROM stock_allocations sa - WHERE sa.status = 'ACTIVE' - AND sa.stockable_type = 'RECORDING_EGG' - AND sa.stockable_id = re.id -); +-- no-op: moved to 20260306090011_disable_chickin_fifo_consumption_after_core.up.sql +-- to ensure FIFO core + seed are applied before this data update migration. COMMIT; diff --git a/internal/database/migrations/20260306090010_seed_fifo_stock_v2_config_after_core.down.sql b/internal/database/migrations/20260306090010_seed_fifo_stock_v2_config_after_core.down.sql new file mode 100644 index 00000000..05786a61 --- /dev/null +++ b/internal/database/migrations/20260306090010_seed_fifo_stock_v2_config_after_core.down.sql @@ -0,0 +1,37 @@ +BEGIN; + +DELETE FROM fifo_stock_v2_overconsume_rules +WHERE reason IN ( + 'fifo_v2_default_allow', + 'fifo_v2_exception_ayam_depletion_block', + 'fifo_v2_exception_marketing_block', + 'fifo_v2_exception_transfer_block', + 'fifo_v2_exception_adjustment_block', + 'fifo_v2_exception_transfer_laying_block' +); + +DELETE FROM fifo_stock_v2_route_rules +WHERE flag_group_code IN ('AYAM', 'AFKIR_CULLING_MATI', 'PAKAN', 'OVK', 'TELUR', 'TELUR_GRADE'); + +DELETE FROM fifo_stock_v2_traits +WHERE source_table IN ( + 'purchase_items', + 'stock_transfer_details', + 'laying_transfer_targets', + 'laying_transfer_sources', + 'adjustment_stocks', + 'recording_stocks', + 'recording_depletions', + 'recording_eggs', + 'marketing_delivery_products', + 'project_chickins', + 'project_flock_populations' +); + +DELETE FROM fifo_stock_v2_flag_members +WHERE flag_group_code IN ('AYAM', 'AFKIR_CULLING_MATI', 'PAKAN', 'OVK', 'TELUR', 'TELUR_GRADE'); + +DELETE FROM fifo_stock_v2_flag_groups +WHERE code IN ('AYAM', 'AFKIR_CULLING_MATI', 'PAKAN', 'OVK', 'TELUR', 'TELUR_GRADE'); + +COMMIT; diff --git a/internal/database/migrations/20260306090010_seed_fifo_stock_v2_config_after_core.up.sql b/internal/database/migrations/20260306090010_seed_fifo_stock_v2_config_after_core.up.sql new file mode 100644 index 00000000..f791aebf --- /dev/null +++ b/internal/database/migrations/20260306090010_seed_fifo_stock_v2_config_after_core.up.sql @@ -0,0 +1,250 @@ +BEGIN; + +INSERT INTO fifo_stock_v2_flag_groups(code, name, priority) +VALUES + ('AYAM', 'AYAM', 10), + ('AFKIR_CULLING_MATI', 'AFKIR/CULLING/MATI', 20), + ('PAKAN', 'PAKAN', 30), + ('OVK', 'OVK', 40), + ('TELUR', 'TELUR', 50), + ('TELUR_GRADE', 'UTUH/PUTIH/RETAK/PECAH/PAPACAL/JUMBO', 60) +ON CONFLICT (code) DO UPDATE +SET + name = EXCLUDED.name, + priority = EXCLUDED.priority, + updated_at = NOW(); + +INSERT INTO fifo_stock_v2_flag_members(flag_name, flag_group_code, priority) +VALUES + ('DOC', 'AYAM', 10), + ('PULLET', 'AYAM', 20), + ('LAYER', 'AYAM', 30), + + ('AYAM-AFKIR', 'AFKIR_CULLING_MATI', 10), + ('AYAM-CULLING', 'AFKIR_CULLING_MATI', 20), + ('AYAM-MATI', 'AFKIR_CULLING_MATI', 30), + + ('PAKAN', 'PAKAN', 10), + ('PRE-STARTER', 'PAKAN', 20), + ('STARTER', 'PAKAN', 30), + ('FINISHER', 'PAKAN', 40), + + ('OVK', 'OVK', 10), + ('OBAT', 'OVK', 20), + ('VITAMIN', 'OVK', 30), + ('KIMIA', 'OVK', 40), + + ('TELUR', 'TELUR', 10), + + ('TELUR-UTUH', 'TELUR_GRADE', 10), + ('TELUR-PUTIH', 'TELUR_GRADE', 20), + ('TELUR-RETAK', 'TELUR_GRADE', 30), + ('TELUR-PECAH', 'TELUR_GRADE', 40), + ('TELUR-PAPACAL', 'TELUR_GRADE', 50), + ('TELUR-JUMBO', 'TELUR_GRADE', 60) +ON CONFLICT (flag_name) DO UPDATE +SET + flag_group_code = EXCLUDED.flag_group_code, + priority = EXCLUDED.priority, + updated_at = NOW(); + +INSERT INTO fifo_stock_v2_traits( + source_table, + lane, + date_table, + date_join_left_col, + date_join_right_col, + date_column, + fallback_date_column, + sort_priority, + id_column +) +VALUES + ('purchase_items', 'STOCKABLE', NULL, NULL, NULL, 'received_date', NULL, 10, 'id'), + + ('stock_transfer_details', 'STOCKABLE', 'stock_transfers', 'stock_transfer_id', 'id', 'transfer_date', NULL, 20, 'id'), + ('stock_transfer_details', 'USABLE', 'stock_transfers', 'stock_transfer_id', 'id', 'transfer_date', NULL, 20, 'id'), + + ('laying_transfer_targets', 'STOCKABLE', 'laying_transfers', 'laying_transfer_id', 'id', 'transfer_date', NULL, 25, 'id'), + ('laying_transfer_sources', 'USABLE', 'laying_transfers', 'laying_transfer_id', 'id', 'transfer_date', NULL, 25, 'id'), + + ('adjustment_stocks', 'STOCKABLE', NULL, NULL, NULL, 'created_at', NULL, 30, 'id'), + ('adjustment_stocks', 'USABLE', NULL, NULL, NULL, 'created_at', NULL, 30, 'id'), + + ('recording_stocks', 'USABLE', 'recordings', 'recording_id', 'id', 'record_datetime', NULL, 35, 'id'), + ('recording_depletions', 'USABLE', 'recordings', 'recording_id', 'id', 'record_datetime', NULL, 35, 'id'), + ('recording_depletions', 'STOCKABLE', 'recordings', 'recording_id', 'id', 'record_datetime', NULL, 35, 'id'), + + ('recording_eggs', 'STOCKABLE', 'recordings', 'recording_id', 'id', 'record_datetime', 'created_at', 40, 'id'), + + ('marketing_delivery_products', 'USABLE', NULL, NULL, NULL, 'delivery_date', 'created_at', 45, 'id'), + + ('project_chickins', 'USABLE', NULL, NULL, NULL, 'chick_in_date', 'created_at', 50, 'id'), + ('project_flock_populations', 'STOCKABLE', 'project_chickins', 'project_chickin_id', 'id', 'chick_in_date', 'created_at', 55, 'id') +ON CONFLICT (source_table, lane) DO UPDATE +SET + date_table = EXCLUDED.date_table, + date_join_left_col = EXCLUDED.date_join_left_col, + date_join_right_col = EXCLUDED.date_join_right_col, + date_column = EXCLUDED.date_column, + fallback_date_column = EXCLUDED.fallback_date_column, + sort_priority = EXCLUDED.sort_priority, + id_column = EXCLUDED.id_column, + is_active = TRUE; + +INSERT INTO fifo_stock_v2_route_rules( + flag_group_code, + lane, + function_code, + source_table, + source_id_column, + product_warehouse_col, + quantity_col, + used_quantity_col, + pending_quantity_col, + scope_sql, + legacy_type_key, + allow_pending_default, + is_active +) +VALUES + -- AYAM STOCKABLE + ('AYAM', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), + ('AYAM', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), + ('AYAM', 'STOCKABLE', 'PURCHASE_IN', 'purchase_items', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'PURCHASE_ITEMS', TRUE, TRUE), + ('AYAM', 'STOCKABLE', 'TRANSFER_TO_LAYING_IN', 'laying_transfer_targets', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'TRANSFERTOLAYING_IN', TRUE, TRUE), + ('AYAM', 'STOCKABLE', 'POPULATION_IN', 'project_flock_populations', 'id', 'product_warehouse_id', 'total_qty', 'total_used_qty', NULL, NULL, 'PROJECT_FLOCK_POPULATION', TRUE, TRUE), + + -- AYAM USABLE + ('AYAM', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), + ('AYAM', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), + ('AYAM', 'USABLE', 'CHICKIN_OUT', 'project_chickins', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_usage_qty', 'deleted_at IS NULL', 'PROJECT_CHICKIN', TRUE, TRUE), + ('AYAM', 'USABLE', 'RECORDING_DEPLETION_OUT', 'recording_depletions', 'id', 'source_product_warehouse_id', 'qty', NULL, 'pending_qty', NULL, 'RECORDING_DEPLETION', TRUE, TRUE), + ('AYAM', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), + ('AYAM', 'USABLE', 'TRANSFER_TO_LAYING_OUT', 'laying_transfer_sources', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_usage_qty', 'deleted_at IS NULL', 'TRANSFERTOLAYING_OUT', TRUE, TRUE), + + -- AFKIR/CULLING/MATI STOCKABLE + ('AFKIR_CULLING_MATI', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), + ('AFKIR_CULLING_MATI', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), + ('AFKIR_CULLING_MATI', 'STOCKABLE', 'RECORDING_DEPLETION_IN', 'recording_depletions', 'id', 'product_warehouse_id', 'qty', NULL, NULL, NULL, 'RECORDING_DEPLETION', TRUE, TRUE), + + -- AFKIR/CULLING/MATI USABLE + ('AFKIR_CULLING_MATI', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), + ('AFKIR_CULLING_MATI', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), + ('AFKIR_CULLING_MATI', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), + + -- PAKAN STOCKABLE + ('PAKAN', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), + ('PAKAN', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), + ('PAKAN', 'STOCKABLE', 'PURCHASE_IN', 'purchase_items', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'PURCHASE_ITEMS', TRUE, TRUE), + + -- PAKAN USABLE + ('PAKAN', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), + ('PAKAN', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), + ('PAKAN', 'USABLE', 'RECORDING_STOCK_OUT', 'recording_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'RECORDING_STOCK', TRUE, TRUE), + ('PAKAN', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), + + -- OVK STOCKABLE + ('OVK', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), + ('OVK', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), + ('OVK', 'STOCKABLE', 'PURCHASE_IN', 'purchase_items', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'PURCHASE_ITEMS', TRUE, TRUE), + + -- OVK USABLE + ('OVK', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), + ('OVK', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), + ('OVK', 'USABLE', 'RECORDING_STOCK_OUT', 'recording_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'RECORDING_STOCK', TRUE, TRUE), + ('OVK', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), + + -- TELUR STOCKABLE + ('TELUR', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), + ('TELUR', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), + ('TELUR', 'STOCKABLE', 'RECORDING_EGG_IN', 'recording_eggs', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'RECORDING_EGG', TRUE, TRUE), + + -- TELUR USABLE + ('TELUR', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), + ('TELUR', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), + ('TELUR', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE), + + -- TELUR_GRADE STOCKABLE + ('TELUR_GRADE', 'STOCKABLE', 'ADJUSTMENT_IN', 'adjustment_stocks', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'ADJUSTMENT_IN', TRUE, TRUE), + ('TELUR_GRADE', 'STOCKABLE', 'STOCK_TRANSFER_IN', 'stock_transfer_details', 'id', 'dest_product_warehouse_id', 'total_qty', 'total_used', NULL, 'deleted_at IS NULL', 'STOCK_TRANSFER_IN', TRUE, TRUE), + ('TELUR_GRADE', 'STOCKABLE', 'RECORDING_EGG_IN', 'recording_eggs', 'id', 'product_warehouse_id', 'total_qty', 'total_used', NULL, NULL, 'RECORDING_EGG', TRUE, TRUE), + + -- TELUR_GRADE USABLE + ('TELUR_GRADE', 'USABLE', 'ADJUSTMENT_OUT', 'adjustment_stocks', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'ADJUSTMENT_OUT', TRUE, TRUE), + ('TELUR_GRADE', 'USABLE', 'STOCK_TRANSFER_OUT', 'stock_transfer_details', 'id', 'source_product_warehouse_id', 'usage_qty', NULL, 'pending_qty', 'deleted_at IS NULL', 'STOCK_TRANSFER_OUT', TRUE, TRUE), + ('TELUR_GRADE', 'USABLE', 'MARKETING_OUT', 'marketing_delivery_products', 'id', 'product_warehouse_id', 'usage_qty', NULL, 'pending_qty', NULL, 'MARKETING_DELIVERY', TRUE, TRUE) +ON CONFLICT (flag_group_code, lane, function_code, source_table) DO UPDATE +SET + source_id_column = EXCLUDED.source_id_column, + product_warehouse_col = EXCLUDED.product_warehouse_col, + quantity_col = EXCLUDED.quantity_col, + used_quantity_col = EXCLUDED.used_quantity_col, + pending_quantity_col = EXCLUDED.pending_quantity_col, + scope_sql = EXCLUDED.scope_sql, + legacy_type_key = EXCLUDED.legacy_type_key, + allow_pending_default = EXCLUDED.allow_pending_default, + is_active = EXCLUDED.is_active, + updated_at = NOW(); + +INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active) +SELECT NULL, NULL, 'USABLE', TRUE, 999, 'fifo_v2_default_allow', TRUE +WHERE NOT EXISTS ( + SELECT 1 FROM fifo_stock_v2_overconsume_rules + WHERE flag_group_code IS NULL + AND function_code IS NULL + AND lane = 'USABLE' + AND reason = 'fifo_v2_default_allow' +); + +INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active) +SELECT 'AYAM', 'RECORDING_DEPLETION_OUT', 'USABLE', FALSE, 10, 'fifo_v2_exception_ayam_depletion_block', TRUE +WHERE NOT EXISTS ( + SELECT 1 FROM fifo_stock_v2_overconsume_rules + WHERE flag_group_code = 'AYAM' + AND function_code = 'RECORDING_DEPLETION_OUT' + AND lane = 'USABLE' + AND reason = 'fifo_v2_exception_ayam_depletion_block' +); + +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 flag_group_code IS NULL + AND function_code = 'MARKETING_OUT' + AND lane = 'USABLE' + AND reason = 'fifo_v2_exception_marketing_block' +); + +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 flag_group_code IS NULL + AND function_code = 'STOCK_TRANSFER_OUT' + AND lane = 'USABLE' + AND reason = 'fifo_v2_exception_transfer_block' +); + +INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active) +SELECT NULL, 'ADJUSTMENT_OUT', 'USABLE', FALSE, 40, 'fifo_v2_exception_adjustment_block', TRUE +WHERE NOT EXISTS ( + SELECT 1 FROM fifo_stock_v2_overconsume_rules + WHERE flag_group_code IS NULL + AND function_code = 'ADJUSTMENT_OUT' + AND lane = 'USABLE' + AND reason = 'fifo_v2_exception_adjustment_block' +); + +INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active) +SELECT NULL, 'TRANSFER_TO_LAYING_OUT', 'USABLE', FALSE, 50, 'fifo_v2_exception_transfer_laying_block', TRUE +WHERE NOT EXISTS ( + SELECT 1 FROM fifo_stock_v2_overconsume_rules + WHERE flag_group_code IS NULL + AND function_code = 'TRANSFER_TO_LAYING_OUT' + AND lane = 'USABLE' + AND reason = 'fifo_v2_exception_transfer_laying_block' +); + +COMMIT; diff --git a/internal/database/migrations/20260306090011_disable_chickin_fifo_consumption_after_core.down.sql b/internal/database/migrations/20260306090011_disable_chickin_fifo_consumption_after_core.down.sql new file mode 100644 index 00000000..ee662a07 --- /dev/null +++ b/internal/database/migrations/20260306090011_disable_chickin_fifo_consumption_after_core.down.sql @@ -0,0 +1,13 @@ +BEGIN; + +-- Restore CHICKIN route if rollback is required. +-- NOTE: released PROJECT_CHICKIN allocations are not restored by this down migration. +UPDATE fifo_stock_v2_route_rules +SET is_active = TRUE, + updated_at = NOW() +WHERE flag_group_code = 'AYAM' + AND lane = 'USABLE' + AND function_code = 'CHICKIN_OUT' + AND source_table = 'project_chickins'; + +COMMIT; diff --git a/internal/database/migrations/20260306090011_disable_chickin_fifo_consumption_after_core.up.sql b/internal/database/migrations/20260306090011_disable_chickin_fifo_consumption_after_core.up.sql new file mode 100644 index 00000000..43936c01 --- /dev/null +++ b/internal/database/migrations/20260306090011_disable_chickin_fifo_consumption_after_core.up.sql @@ -0,0 +1,151 @@ +BEGIN; + +-- Disable CHICKIN as FIFO USABLE so chick-in acts as business tagging/conversion, +-- not physical stock consumption. +UPDATE fifo_stock_v2_route_rules +SET is_active = FALSE, + updated_at = NOW() +WHERE flag_group_code = 'AYAM' + AND lane = 'USABLE' + AND function_code = 'CHICKIN_OUT' + AND source_table = 'project_chickins' + AND is_active = TRUE; + +-- Release existing active allocations created by PROJECT_CHICKIN +-- and return warehouse qty back. +WITH released AS ( + UPDATE stock_allocations + SET status = 'RELEASED', + released_at = COALESCE(released_at, NOW()), + updated_at = NOW(), + note = CASE + WHEN COALESCE(note, '') = '' THEN 'fifo_v2_chickin_conversion_release' + ELSE note || '; fifo_v2_chickin_conversion_release' + END + WHERE usable_type = 'PROJECT_CHICKIN' + AND status = 'ACTIVE' + RETURNING product_warehouse_id, qty +), +pw_delta AS ( + SELECT product_warehouse_id, COALESCE(SUM(qty), 0) AS qty_delta + FROM released + GROUP BY product_warehouse_id +) +UPDATE product_warehouses pw +SET qty = COALESCE(pw.qty, 0) + d.qty_delta +FROM pw_delta d +WHERE pw.id = d.product_warehouse_id; + +-- Resync stockable total_used columns from remaining ACTIVE allocations. + +-- purchase_items (PURCHASE_ITEMS) +UPDATE purchase_items pi +SET total_used = COALESCE(a.used, 0) +FROM ( + SELECT stockable_id, SUM(qty) AS used + FROM stock_allocations + WHERE status = 'ACTIVE' + AND stockable_type = 'PURCHASE_ITEMS' + GROUP BY stockable_id +) a +WHERE pi.id = a.stockable_id; + +UPDATE purchase_items pi +SET total_used = 0 +WHERE NOT EXISTS ( + SELECT 1 + FROM stock_allocations sa + WHERE sa.status = 'ACTIVE' + AND sa.stockable_type = 'PURCHASE_ITEMS' + AND sa.stockable_id = pi.id +); + +-- stock_transfer_details (STOCK_TRANSFER_IN) +UPDATE stock_transfer_details std +SET total_used = COALESCE(a.used, 0) +FROM ( + SELECT stockable_id, SUM(qty) AS used + FROM stock_allocations + WHERE status = 'ACTIVE' + AND stockable_type = 'STOCK_TRANSFER_IN' + GROUP BY stockable_id +) a +WHERE std.id = a.stockable_id; + +UPDATE stock_transfer_details std +SET total_used = 0 +WHERE NOT EXISTS ( + SELECT 1 + FROM stock_allocations sa + WHERE sa.status = 'ACTIVE' + AND sa.stockable_type = 'STOCK_TRANSFER_IN' + AND sa.stockable_id = std.id +); + +-- adjustment_stocks (ADJUSTMENT_IN) +UPDATE adjustment_stocks ast +SET total_used = COALESCE(a.used, 0) +FROM ( + SELECT stockable_id, SUM(qty) AS used + FROM stock_allocations + WHERE status = 'ACTIVE' + AND stockable_type = 'ADJUSTMENT_IN' + GROUP BY stockable_id +) a +WHERE ast.id = a.stockable_id; + +UPDATE adjustment_stocks ast +SET total_used = 0 +WHERE NOT EXISTS ( + SELECT 1 + FROM stock_allocations sa + WHERE sa.status = 'ACTIVE' + AND sa.stockable_type = 'ADJUSTMENT_IN' + AND sa.stockable_id = ast.id +); + +-- laying_transfer_targets (TRANSFERTOLAYING_IN) +UPDATE laying_transfer_targets ltt +SET total_used = COALESCE(a.used, 0) +FROM ( + SELECT stockable_id, SUM(qty) AS used + FROM stock_allocations + WHERE status = 'ACTIVE' + AND stockable_type = 'TRANSFERTOLAYING_IN' + GROUP BY stockable_id +) a +WHERE ltt.id = a.stockable_id; + +UPDATE laying_transfer_targets ltt +SET total_used = 0 +WHERE NOT EXISTS ( + SELECT 1 + FROM stock_allocations sa + WHERE sa.status = 'ACTIVE' + AND sa.stockable_type = 'TRANSFERTOLAYING_IN' + AND sa.stockable_id = ltt.id +); + +-- recording_eggs (RECORDING_EGG) +UPDATE recording_eggs re +SET total_used = COALESCE(a.used, 0) +FROM ( + SELECT stockable_id, SUM(qty) AS used + FROM stock_allocations + WHERE status = 'ACTIVE' + AND stockable_type = 'RECORDING_EGG' + GROUP BY stockable_id +) a +WHERE re.id = a.stockable_id; + +UPDATE recording_eggs re +SET total_used = 0 +WHERE NOT EXISTS ( + SELECT 1 + FROM stock_allocations sa + WHERE sa.status = 'ACTIVE' + AND sa.stockable_type = 'RECORDING_EGG' + AND sa.stockable_id = re.id +); + +COMMIT;