From 7b4bf94329e057a2a6c0e16f76f5f45585640c51 Mon Sep 17 00:00:00 2001 From: Adnan Zahir Date: Wed, 1 Apr 2026 16:14:07 +0700 Subject: [PATCH] codex/fix: qty 0 after PO to farm-level warehouse --- docs/qa_farm_stock_test_cases.csv | 45 +++++++++++++ docs/qa_farm_stock_test_cases.xlsx | Bin 0 -> 15601 bytes .../services/fifo_stock_v2_helper.go | 11 +++ .../services/fifo_stock_v2_helper_test.go | 63 ++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 docs/qa_farm_stock_test_cases.csv create mode 100644 docs/qa_farm_stock_test_cases.xlsx diff --git a/docs/qa_farm_stock_test_cases.csv b/docs/qa_farm_stock_test_cases.csv new file mode 100644 index 00000000..3d05f943 --- /dev/null +++ b/docs/qa_farm_stock_test_cases.csv @@ -0,0 +1,45 @@ +ID;Kategori;Area;Judul;Tipe;Prioritas;Setup/Precondition;Langkah Uji;Hasil yang Diharapkan +TC-A01;Migrasi dan Keamanan Data;Database;Migrasi aman pada DB tidak kosong;Integration;High;Gunakan snapshot DB staging yang sudah berisi recording, depletion, telur, penjualan, dan closing.;1. Jalankan migrasi 20260330110000_add_recording_attribution_fields_for_farm_stock.up.sql. 2. Inspect schema hasil migrasi.;Kolom recording_depletions.source_project_flock_kandang_id dan recording_eggs.project_flock_kandang_id tersedia dan nullable, index dan FK tersedia, tidak ada data historis yang terhapus atau berubah destruktif. +TC-A02;Migrasi dan Keamanan Data;Database;Backfill deterministik berjalan;Integration;High;Ada data historis recording dengan recordings.project_flock_kandangs_id yang valid.;1. Query recording_depletions dan recording_eggs yang lama. 2. Bandingkan dengan kandang pada parent recording.;source_project_flock_kandang_id dan project_flock_kandang_id terisi sama dengan kandang parent recording untuk row yang sebelumnya null. +TC-A03;Migrasi dan Keamanan Data;Reporting;Report historis kandang-only tidak berubah;Regression;High;Gunakan snapshot yang hanya memiliki data stok historis milik kandang, tanpa pooled stock farm-level.;1. Jalankan closing/report/HPP sebelum deploy. 2. Jalankan lagi sesudah deploy pada snapshot yang sama. 3. Bandingkan hasil.;Total dan hasil report tetap sama untuk skenario historis kandang-only. +TC-B01;Purchase dan Warehouse;Purchase;Purchase pakan langsung ke gudang farm;UAT;High;Tersedia PO atau purchase request untuk produk Pakan Starter.;1. Buat purchase ke Gudang Farm A. 2. Approve dan receive purchase.;Stok masuk ke product_warehouse level farm, tidak perlu transfer paksa ke kandang, FIFO/HPP purchase tetap benar. +TC-B02;Purchase dan Warehouse;Purchase;Purchase pakan langsung ke gudang kandang;Regression;High;Tersedia PO atau purchase request untuk produk Pakan Starter.;1. Buat purchase ke Gudang Kandang A1. 2. Approve dan receive purchase.;Stok masuk ke gudang kandang dan perilaku tetap sama seperti flow lama. +TC-B03;Purchase dan Warehouse;Purchase;Purchase OVK langsung ke gudang farm;UAT;High;Tersedia PO atau purchase request untuk produk OVK A.;1. Buat purchase ke Gudang Farm A. 2. Approve dan receive purchase.;Stok OVK masuk ke gudang farm dan bisa dipakai kemudian pada recording. +TC-B04;Purchase dan Warehouse;Product Warehouse;Gudang farm shared tidak diubah diam-diam menjadi milik kandang;Regression;High;Sudah ada row product_warehouse level farm untuk Pakan Starter di Gudang Farm A.;1. Trigger flow yang memanggil ensure/find product warehouse untuk produk yang sama. 2. Inspect row existing.;Row farm-level tetap farm-level, project_flock_kandang_id tidak dibackfill diam-diam, row khusus kandang dibuat terpisah bila memang diperlukan. +TC-C01;Recording Stock Consumption;Recording;Recording kandang memakai pakan dari gudang kandang;Regression;High;Stok pakan tersedia di Gudang Kandang A1.;1. Buka recording untuk Kandang A1. 2. Pilih pakan dari gudang kandang. 3. Submit dan approve.;Recording berhasil, stok keluar dari product_warehouse kandang, atribusi kandang tetap A1, HPP pemakaian muncul di closing/HPP A1. +TC-C02;Recording Stock Consumption;Recording;Recording kandang memakai pakan dari gudang farm;UAT;High;Stok pakan hanya tersedia di Gudang Farm A.;1. Buka recording untuk Kandang A1. 2. Pilih stok pakan farm-level. 3. Submit dan approve.;Recording berhasil tanpa transfer ke kandang, stok fisik berkurang dari gudang farm, usage/HPP tetap teratribusi ke Kandang A1, closing farm dan kandang tetap bisa dihitung. +TC-C03;Recording Stock Consumption;Recording;Recording kandang memakai OVK dari gudang farm;UAT;High;Stok OVK hanya tersedia di Gudang Farm A.;1. Buka recording untuk Kandang A1. 2. Pilih stok OVK farm-level. 3. Submit dan approve.;Stok OVK keluar dari gudang farm dan biaya pemakaian teratribusi ke kandang yang dipilih. +TC-C04;Recording Stock Consumption;Frontend Recording;Selector recording menampilkan opsi stok farm dan kandang dengan jelas;UI Regression;Medium;Produk yang sama tersedia di Gudang Farm A dan Gudang Kandang A1.;1. Buka form recording untuk A1. 2. Buka selector pakan.;Kedua opsi terlihat, label membedakan gudang atau scope dengan jelas, farm stock tidak tersembunyi secara salah. +TC-C05;Recording Stock Consumption;Recording;Recording A1 tidak boleh memakai stok kandang A2;Negative;High;Pakan Starter tersedia di Gudang Kandang A2.;1. Buka recording untuk A1. 2. Periksa opsi stok yang bisa dipilih.;Opsi Gudang Kandang A2 tidak bisa dipilih, stok farm tetap bisa dipilih. +TC-C06;Recording Stock Consumption;Recording;Perilaku pending stock dan usage lama tetap berjalan;Regression;Medium;Tidak ada setup khusus selain data recording yang valid.;1. Buat usage stock. 2. Buka kembali halaman edit dan detail.;Tampilan dan perhitungan pending atau usage tetap benar, tidak ada regresi pada route FIFO-v2. +TC-D01;Recording Telur dan Atribusi;Recording;Recording telur ke gudang kandang tetap berjalan;Regression;High;Kandang A1 aktif dan gudang telur kandang tersedia.;1. Record telur untuk A1 ke Gudang Kandang A1. 2. Approve.;Stok telur di gudang kandang bertambah dan asal kandang tetap A1. +TC-D02;Recording Telur dan Atribusi;Recording;Recording telur di kandang menyimpan stok ke gudang farm;UAT;High;Egg product warehouse tersedia di Gudang Farm A.;1. Record telur untuk A1. 2. Pilih Gudang Farm A sebagai gudang telur. 3. Submit dan approve.;Stok telur fisik masuk ke gudang farm, recording_eggs.project_flock_kandang_id bernilai A1, tidak ada transfer paksa ke kandang. +TC-D03;Recording Telur dan Atribusi;Reporting;Stok telur pooled di farm tetap punya jejak asal kandang;Integration;High;A1 record 100 telur ke gudang farm dan A2 record 150 telur ke gudang farm yang sama.;1. Inspect row telur yang tersimpan. 2. Inspect hasil costing atau report setelahnya.;Stok fisik pooled di gudang farm, tetapi asal kandang tetap bisa dibedakan per row atau allocation, HPP per kandang tetap dapat dihitung. +TC-D04;Recording Telur dan Atribusi;Recording Detail;Known gap pada detail recording dipahami;Known Limitation;Low;Sudah menjalankan TC-D02.;1. Buka detail recording setelah transaksi telur ke gudang farm.;Logika bisnis tetap berjalan, tetapi detail API atau UI mungkin belum menampilkan egg-origin secara eksplisit karena detail DTO belum diperluas. +TC-E01;Depletion dan Atribusi Populasi;Recording;Depletion dari gudang ayam milik kandang normal;Regression;High;A1 memiliki populasi ayam di gudang kandang.;1. Buat depletion. 2. Approve.;Depletion berhasil, alokasi populasi ter-resolve ke A1, HPP atau usage tetap benar. +TC-E02;Depletion dan Atribusi Populasi;Recording;Depletion dari sumber ayam fisik farm-level dengan source kandang A1;UAT;High;Stok ayam secara fisik ada di gudang farm dan punya jejak sumber ke A1.;1. Buat depletion untuk A1. 2. Gunakan path source atau farm-level yang didukung backend. 3. Approve.;source_product_warehouse_id menunjuk ke sumber fisik yang benar, source_project_flock_kandang_id bernilai A1, alokasi populasi berhasil tanpa mengasumsikan gudang fisik milik A1. +TC-E03;Depletion dan Atribusi Populasi;Recording;Depletion gagal bila sumber populasi tidak dapat diatribusikan;Negative;High;Buat kasus stok ayam farm-level tanpa source kandang yang valid.;1. Coba approve depletion.;Backend menolak dengan error yang jelas dan tidak ada silent misattribution. +TC-F01;Marketing dan Penjualan;Sales Order;Sales order dari gudang kandang tetap berjalan;Regression;High;Stok produk tersedia di Gudang Kandang A1.;1. Buat SO dari Gudang Kandang A1. 2. Lakukan delivery.;Perilaku lama tetap berjalan normal. +TC-F02;Marketing dan Penjualan;Sales Order;Sales order dari gudang farm untuk telur;UAT;High;Stok telur farm-level tersedia dan berasal dari A1.;1. Buat SO menggunakan Gudang Farm A. 2. Lakukan delivery.;SO dan DO berhasil, stok fisik berkurang dari gudang farm, HPP dan COGS telur tetap teratribusi ke kandang penghasil melalui allocation. +TC-F03;Marketing dan Penjualan;Sales Order;Sales order dari gudang farm untuk telur pooled A1 dan A2;Integration;High;Stok telur pooled tersedia di gudang farm dari A1 dan A2.;1. Buat penjualan. 2. Lakukan delivery. 3. Inspect closing atau report.;Stok fisik berkurang sekali dari gudang farm, revenue dan HPP terbagi benar ke A1 dan A2, tidak bergantung pada pw.project_flock_kandang_id. +TC-F04;Marketing dan Penjualan;Sales Order;Sales order dari gudang farm untuk ayam atau culling;UAT;High;Stok ayam atau culling farm-level tersedia dengan jejak sumber dari A1 dan A2.;1. Buat SO dari gudang farm. 2. Buat DO dan approve.;allocatePopulationForMarketingDelivery menurunkan atribusi kandang dari source groups atau allocation, tidak gagal karena gudang jual tidak punya project_flock_kandang_id, HPP dan COGS teratribusi ke kandang sumber. +TC-F05;Marketing dan Penjualan;Frontend Marketing;UI sales menampilkan semantik Gudang Fisik;UI Regression;Medium;Tidak ada setup khusus selain akses ke form SO.;1. Buka form SO. 2. Periksa label selector gudang dan label tabel produk.;UI menggunakan label Gudang Fisik, bukan Kandang yang menyesatkan, dan label produk memuat detail produk serta gudang atau scope. +TC-F06;Marketing dan Penjualan;Delivery Order;Layar delivery order tetap kompatibel;Regression;Medium;Sudah ada SO dari gudang farm.;1. Lakukan delivery untuk SO farm-level. 2. Periksa tabel dan detail DO.;Tidak ada masalah payload, gudang fisik tampil dengan benar, dan tidak ada kebingungan akibat wording lama berbasis kandang. +TC-G01;Report, Closing, dan HPP;Daily Marketing Report;Daily marketing report untuk penjualan telur farm-level;UAT;Medium;Sudah menjalankan TC-F02.;1. Jalankan daily marketing report. 2. Uji export.;Row muncul pada gudang fisik yang benar, report tidak menyiratkan gudang sama dengan kandang, export berjalan. +TC-G02;Report, Closing, dan HPP;Closing Sales;Closing sales untuk penjualan pooled farm-level;UAT;High;Ada penjualan pooled telur atau ayam dari gudang farm.;1. Buka closing sales.;Penjualan bisa tampil teratribusi per kandang, label menunjukkan Kandang Atribusi, HPP dan revenue tetap benar secara matematis. +TC-G03;Report, Closing, dan HPP;HPP per Kandang;HPP per kandang mencakup konsumsi pakan atau OVK dari gudang farm;UAT;High;A1 sudah memakai pakan atau OVK dari gudang farm.;1. Jalankan report HPP per kandang.;Biaya usage muncul di A1 dan tidak hilang walaupun gudang fisiknya level farm. +TC-G04;Report, Closing, dan HPP;Closing Sapronak;Outgoing sapronak menampilkan gudang fisik dengan benar;UI Regression;Medium;Ada data outgoing sapronak yang valid.;1. Buka tabel closing outgoing sapronak.;Header jelas menunjukkan Gudang Asal (Fisik) dan Gudang Tujuan (Fisik). +TC-G05;Report, Closing, dan HPP;Compatibility;Data historis kandang-owned dan pooled data baru dapat coexist;Regression;High;Dalam satu date range ada transaksi lama kandang-owned dan transaksi baru pooled farm-level.;1. Jalankan closing. 2. Jalankan report. 3. Jalankan HPP.;Kedua jenis data diproses dengan benar, tidak ada double count dan tidak ada atribusi yang hilang. +TC-H01;FIFO-v2 dan Integritas Allocation;FIFO-v2;Kontrak FIFO-v2 tidak berubah;Integration;High;Gunakan data uji yang mencakup recording stock, depletion, egg, dan marketing.;1. Verifikasi route FIFO untuk RECORDING_STOCK_OUT, RECORDING_DEPLETION_OUT, RECORDING_DEPLETION_IN, RECORDING_EGG_IN, dan MARKETING_OUT. 2. Bandingkan dengan RFC.md dan seed config FIFO-v2.;Tidak ada perubahan semantik route yang tidak disengaja. +TC-H02;FIFO-v2 dan Integritas Allocation;Stock Allocation;Stock allocation tetap konsisten untuk pakan dari gudang farm;Integration;High;Sudah menjalankan TC-C02.;1. Inspect stock_allocations setelah transaksi.;Allocation consume terbentuk dengan benar dan tidak ada row allocation yatim atau rusak. +TC-H03;FIFO-v2 dan Integritas Allocation;Stock Allocation;Stock allocation tetap konsisten untuk penjualan telur pooled;Integration;High;Sudah menjalankan TC-F03.;1. Inspect stock_allocations. 2. Inspect row atribusi turunannya.;Allocation mendukung atribusi HPP kembali ke kandang sumber. +TC-H04;FIFO-v2 dan Integritas Allocation;Population Allocation;Population allocation tetap konsisten untuk penjualan ayam pooled;Integration;High;Sudah menjalankan TC-F04.;1. Inspect population allocations.;Penggunaan kandang sumber teralokasi dengan benar dan tidak fallback ke atribusi null saat source tersedia. +TC-I01;Negative dan Guard Cases;Recording;Recording dari stok farm-level dengan qty tidak cukup;Negative;High;Stok farm-level tersedia tetapi qty lebih kecil dari pemakaian yang diinput.;1. Buat recording dengan qty melebihi stok. 2. Submit atau approve.;Muncul validation atau business error dan tidak ada korupsi parsial. +TC-I02;Negative dan Guard Cases;Marketing;Marketing dari stok farm-level dengan qty tidak cukup;Negative;High;Stok farm-level tersedia tetapi qty lebih kecil dari qty penjualan.;1. Buat SO atau DO dengan qty melebihi stok. 2. Submit atau approve.;Delivery atau approval diblok dan stok tetap konsisten. +TC-I03;Negative dan Guard Cases;Frontend Selector;Opsi produk sama di gudang berbeda tidak salah terpilih;UI Regression;Medium;Produk yang sama tersedia di gudang farm dan gudang kandang.;1. Pilih masing-masing opsi secara eksplisit di UI. 2. Save. 3. Buka kembali edit atau detail.;Opsi yang terpilih jelas dan tetap stabil setelah save atau edit. +TC-I04;Negative dan Guard Cases;Product Warehouse;Row gudang shared tidak diatribusikan ulang oleh flow maintenance;Regression;High;Ada row shared farm warehouse yang sudah aktif.;1. Jalankan flow yang menyentuh logic ensure/find product warehouse. 2. Cek ulang row farm shared.;Tidak ada mutasi diam-diam pada project_flock_kandang_id. +TC-J01;Regression Frontend dan UX;Recording Form;Form recording menampilkan opsi stok farm dan kandang hanya dalam scope farm yang sama;UI Regression;Medium;Ada stok di gudang farm, gudang kandang saat ini, dan gudang kandang lain.;1. Buka form recording untuk kandang tertentu. 2. Periksa opsi stock selector.;Gudang farm dan gudang kandang saat ini terlihat, gudang kandang lain tersembunyi. +TC-J02;Regression Frontend dan UX;Recording Form;Selector recording telur mengizinkan gudang farm;UI Regression;Medium;Egg warehouse tersedia di gudang farm.;1. Buka form recording telur. 2. Buka selector tujuan telur.;Gudang farm terlihat sebagai opsi tujuan telur. +TC-J03;Regression Frontend dan UX;Sales Form;Form sales memakai semantik gudang secara konsisten;UI Regression;Medium;Akses ke halaman marketing tersedia.;1. Buka form sales. 2. Periksa label selector dan summary table.;Label menggunakan Gudang Fisik secara konsisten dan tidak ada wording Kandang yang menyesatkan untuk stok fisik. +TC-J04;Regression Frontend dan UX;Marketing Modal;Modal list marketing menampilkan label gudang fisik;UI Regression;Low;Akses ke modal product list tersedia.;1. Buka modal product list di marketing.;Kolom menampilkan label Gudang Fisik. +TC-K01;Known Limitation;Recording Detail;Detail recording belum menampilkan source atau origin attribution baru;Known Limitation;Low;Sudah ada recording telur farm-level dan depletion dengan source attribution.;1. Buat transaksi. 2. Buka detail recording.;Transaksi berjalan dan atribusi tersimpan di DB, tetapi detail API atau UI mungkin belum menampilkan field source atau origin tersebut \ No newline at end of file diff --git a/docs/qa_farm_stock_test_cases.xlsx b/docs/qa_farm_stock_test_cases.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..437e962c32b5f4e1d56956d21d7fc1f78a06127f GIT binary patch literal 15601 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