-- Tabel payment_allocations menyimpan hasil FIFO matching antara payment dengan -- sub-row anak (purchase_item / marketing_delivery_product / expense_realization). -- Setiap allocation row HARUS terhubung ke tepat 1 child via 3 nullable FK -- (polymorphic-via-multiple-nullable-FK; lebih aman dari single polymorphic kolom). CREATE TABLE IF NOT EXISTS payment_allocations ( id BIGSERIAL PRIMARY KEY, payment_id BIGINT NOT NULL REFERENCES payments(id) ON DELETE CASCADE, purchase_item_id BIGINT NULL REFERENCES purchase_items(id) ON DELETE CASCADE, marketing_delivery_product_id BIGINT NULL REFERENCES marketing_delivery_products(id) ON DELETE CASCADE, expense_realization_id BIGINT NULL REFERENCES expense_realizations(id) ON DELETE CASCADE, amount NUMERIC(15, 3) NOT NULL CHECK (amount > 0), allocated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT chk_payment_alloc_exactly_one CHECK ( num_nonnulls(purchase_item_id, marketing_delivery_product_id, expense_realization_id) = 1 ) ); CREATE INDEX IF NOT EXISTS idx_payment_alloc_payment ON payment_allocations (payment_id); CREATE INDEX IF NOT EXISTS idx_payment_alloc_purchase_item ON payment_allocations (purchase_item_id) WHERE purchase_item_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_payment_alloc_mdp ON payment_allocations (marketing_delivery_product_id) WHERE marketing_delivery_product_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_payment_alloc_realization ON payment_allocations (expense_realization_id) WHERE expense_realization_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_payment_alloc_allocated_at ON payment_allocations (allocated_at); -- Helper partial indexes untuk FIFO loop performance CREATE INDEX IF NOT EXISTS idx_purchase_items_received_date_partial ON purchase_items (received_date) WHERE received_date IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_mdp_delivery_date_partial ON marketing_delivery_products (delivery_date) WHERE delivery_date IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_payments_party_active ON payments (party_type, party_id, payment_date) WHERE deleted_at IS NULL;