From c6d8533190fb463007f843d895f8b7deaa9ebeea Mon Sep 17 00:00:00 2001 From: Adnan Zahir Date: Sat, 4 Apr 2026 09:53:10 +0700 Subject: [PATCH 1/2] codex/fix: inconsistent stock options and availability --- .../sales-order/SalesOrderProductForm.tsx | 4 +- .../recording/form/RecordingForm.tsx | 111 ++++++++++++++---- 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx b/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx index e3b96ac7..8bd48d7c 100644 --- a/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx +++ b/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx @@ -160,8 +160,10 @@ const SalesOrderProductForm = ({ ProductWarehouseApi.basePath, 'id', 'product.name', - '', + 'search', { + limit: '100', + available_only: 'true', warehouse_id: formik.values.warehouse_id?.toString() ?? '', type: formik.values.marketing_type?.value.toLocaleUpperCase() ?? '', } diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 26a9aa1c..379fe864 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -210,6 +210,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { useState(''); const [eggProductsLocationId, setEggProductsLocationId] = useState(''); + const [knownProductWarehouses, setKnownProductWarehouses] = useState< + Record + >({}); const [isApproveLoading, setIsApproveLoading] = useState(false); const [isRejectLoading, setIsRejectLoading] = useState(false); @@ -606,6 +609,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', { flags: 'PAKAN,OVK', limit: '100', + available_only: 'true', location_id: stockProductsLocationId, ...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}), }); @@ -614,8 +618,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { rawData: depletionProductsData, isLoadingOptions: isLoadingDepletionProducts, loadMore: loadMoreDepletionProducts, - } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', '', { + } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', { limit: '100', + available_only: 'true', location_id: depletionProductsLocationId, ...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}), type: 'AYAM', @@ -684,6 +689,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', { limit: '100', type: 'TELUR', + available_only: 'true', location_id: eggProductsLocationId, ...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}), }); @@ -953,6 +959,77 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { [] ); + const mergeKnownProductWarehouses = useCallback( + (items: Array) => { + if (items.length === 0) { + return; + } + + setKnownProductWarehouses((prev) => { + const next = { ...prev }; + let changed = false; + + items.forEach((item) => { + if (!item?.id) { + return; + } + + if (next[item.id] !== item) { + next[item.id] = item; + changed = true; + } + }); + + return changed ? next : prev; + }); + }, + [] + ); + + useEffect(() => { + const items: Array = []; + + if (isResponseSuccess(stockProducts)) { + items.push(...((stockProducts.data as unknown as ProductWarehouse[]) ?? [])); + } + + if (isResponseSuccess(depletionProductsData)) { + items.push( + ...((depletionProductsData.data as unknown as ProductWarehouse[]) ?? []) + ); + } + + if (isResponseSuccess(eggProductsData)) { + items.push(...((eggProductsData.data as unknown as ProductWarehouse[]) ?? [])); + } + + initialValues?.stocks?.forEach((stock) => { + items.push((stock.product_warehouse as ProductWarehouse | undefined) ?? null); + }); + initialValues?.depletions?.forEach((depletion) => { + items.push( + (depletion.product_warehouse as ProductWarehouse | undefined) ?? null + ); + }); + initialValues?.eggs?.forEach((egg) => { + items.push((egg.product_warehouse as ProductWarehouse | undefined) ?? null); + }); + + mergeKnownProductWarehouses(items); + }, [ + stockProducts, + depletionProductsData, + eggProductsData, + initialValues, + mergeKnownProductWarehouses, + ]); + + const getKnownProductWarehouse = useCallback( + (productWarehouseId: number) => + knownProductWarehouses[productWarehouseId] ?? null, + [knownProductWarehouses] + ); + const buildProductWarehouseOptions = useCallback( (productWarehouses: ProductWarehouse[]) => productWarehouses @@ -965,7 +1042,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { .map((productWarehouse) => ({ value: productWarehouse.id, label: getProductWarehouseOptionLabel(productWarehouse), - })), + })) + .sort((a, b) => a.label.localeCompare(b.label)), [selectedKandangId] ); @@ -1245,12 +1323,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const getAvailableStock = useCallback( (productWarehouseId: number) => { if ((type as 'add' | 'edit' | 'detail') === 'detail') return 0; - if (!isResponseSuccess(stockProducts)) return 0; - const data = stockProducts.data as unknown as ProductWarehouse[]; - const productWarehouse = data.find((pw) => pw.id === productWarehouseId); + const productWarehouse = getKnownProductWarehouse(productWarehouseId); return productWarehouse?.quantity ?? 0; }, - [stockProducts, type] + [getKnownProductWarehouse, type] ); const getStockUsageError = useCallback( @@ -1344,10 +1420,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const getProductFlagBadgeAdornment = useCallback( (productWarehouseId: number) => { - if (!isResponseSuccess(stockProducts)) return null; - - const data = stockProducts.data as unknown as ProductWarehouse[]; - const productWarehouse = data.find((pw) => pw.id === productWarehouseId); + const productWarehouse = getKnownProductWarehouse(productWarehouseId); if (!productWarehouse) return null; const hasPakanFlag = productWarehouse.product.flags?.includes('PAKAN'); @@ -1363,7 +1436,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { return null; }, - [stockProducts] + [getKnownProductWarehouse] ); const getProductUomSuffix = useCallback( @@ -1388,23 +1461,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } } - let rawData; - if (dataSource === 'stock') { - rawData = stockProducts; - } else if (dataSource === 'depletion') { - rawData = depletionProductsData; - } else if (dataSource === 'egg') { - rawData = eggProductsData; - } - - if (!isResponseSuccess(rawData)) return null; - - const data = rawData.data as unknown as ProductWarehouse[]; - const productWarehouse = data.find((pw) => pw.id === productWarehouseId); + const productWarehouse = getKnownProductWarehouse(productWarehouseId); return productWarehouse?.product.uom.name || null; }, - [stockProducts, depletionProductsData, eggProductsData, initialValues, type] + [getKnownProductWarehouse, initialValues, type] ); const getAvailableStockProductOptions = useCallback( From 107d412c102008cead0b445899faa718eee7ad70 Mon Sep 17 00:00:00 2001 From: Adnan Zahir Date: Sat, 4 Apr 2026 09:57:01 +0700 Subject: [PATCH 2/2] formatting --- .../production/recording/form/RecordingForm.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 379fe864..9d0b44f0 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -990,7 +990,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const items: Array = []; if (isResponseSuccess(stockProducts)) { - items.push(...((stockProducts.data as unknown as ProductWarehouse[]) ?? [])); + items.push( + ...((stockProducts.data as unknown as ProductWarehouse[]) ?? []) + ); } if (isResponseSuccess(depletionProductsData)) { @@ -1000,11 +1002,15 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } if (isResponseSuccess(eggProductsData)) { - items.push(...((eggProductsData.data as unknown as ProductWarehouse[]) ?? [])); + items.push( + ...((eggProductsData.data as unknown as ProductWarehouse[]) ?? []) + ); } initialValues?.stocks?.forEach((stock) => { - items.push((stock.product_warehouse as ProductWarehouse | undefined) ?? null); + items.push( + (stock.product_warehouse as ProductWarehouse | undefined) ?? null + ); }); initialValues?.depletions?.forEach((depletion) => { items.push( @@ -1012,7 +1018,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { ); }); initialValues?.eggs?.forEach((egg) => { - items.push((egg.product_warehouse as ProductWarehouse | undefined) ?? null); + items.push( + (egg.product_warehouse as ProductWarehouse | undefined) ?? null + ); }); mergeKnownProductWarehouses(items);