codex/fix: inconsistent stock options and availability

This commit is contained in:
Adnan Zahir
2026-04-04 09:53:10 +07:00
parent f302bcdb4b
commit c6d8533190
2 changed files with 89 additions and 26 deletions
@@ -160,8 +160,10 @@ const SalesOrderProductForm = ({
ProductWarehouseApi.basePath, ProductWarehouseApi.basePath,
'id', 'id',
'product.name', 'product.name',
'', 'search',
{ {
limit: '100',
available_only: 'true',
warehouse_id: formik.values.warehouse_id?.toString() ?? '', warehouse_id: formik.values.warehouse_id?.toString() ?? '',
type: formik.values.marketing_type?.value.toLocaleUpperCase() ?? '', type: formik.values.marketing_type?.value.toLocaleUpperCase() ?? '',
} }
@@ -210,6 +210,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
useState<string>(''); useState<string>('');
const [eggProductsLocationId, setEggProductsLocationId] = const [eggProductsLocationId, setEggProductsLocationId] =
useState<string>(''); useState<string>('');
const [knownProductWarehouses, setKnownProductWarehouses] = useState<
Record<number, ProductWarehouse>
>({});
const [isApproveLoading, setIsApproveLoading] = useState(false); const [isApproveLoading, setIsApproveLoading] = useState(false);
const [isRejectLoading, setIsRejectLoading] = useState(false); const [isRejectLoading, setIsRejectLoading] = useState(false);
@@ -606,6 +609,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
} = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', { } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', {
flags: 'PAKAN,OVK', flags: 'PAKAN,OVK',
limit: '100', limit: '100',
available_only: 'true',
location_id: stockProductsLocationId, location_id: stockProductsLocationId,
...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}), ...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}),
}); });
@@ -614,8 +618,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
rawData: depletionProductsData, rawData: depletionProductsData,
isLoadingOptions: isLoadingDepletionProducts, isLoadingOptions: isLoadingDepletionProducts,
loadMore: loadMoreDepletionProducts, loadMore: loadMoreDepletionProducts,
} = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', '', { } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', {
limit: '100', limit: '100',
available_only: 'true',
location_id: depletionProductsLocationId, location_id: depletionProductsLocationId,
...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}), ...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}),
type: 'AYAM', type: 'AYAM',
@@ -684,6 +689,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
} = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', { } = useSelect(ProductWarehouseApi.basePath, 'id', 'product.name', 'search', {
limit: '100', limit: '100',
type: 'TELUR', type: 'TELUR',
available_only: 'true',
location_id: eggProductsLocationId, location_id: eggProductsLocationId,
...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}), ...(selectedKandangId ? { kandang_id: selectedKandangId.toString() } : {}),
}); });
@@ -953,6 +959,77 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
[] []
); );
const mergeKnownProductWarehouses = useCallback(
(items: Array<ProductWarehouse | null | undefined>) => {
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<ProductWarehouse | null | undefined> = [];
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( const buildProductWarehouseOptions = useCallback(
(productWarehouses: ProductWarehouse[]) => (productWarehouses: ProductWarehouse[]) =>
productWarehouses productWarehouses
@@ -965,7 +1042,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
.map((productWarehouse) => ({ .map((productWarehouse) => ({
value: productWarehouse.id, value: productWarehouse.id,
label: getProductWarehouseOptionLabel(productWarehouse), label: getProductWarehouseOptionLabel(productWarehouse),
})), }))
.sort((a, b) => a.label.localeCompare(b.label)),
[selectedKandangId] [selectedKandangId]
); );
@@ -1245,12 +1323,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const getAvailableStock = useCallback( const getAvailableStock = useCallback(
(productWarehouseId: number) => { (productWarehouseId: number) => {
if ((type as 'add' | 'edit' | 'detail') === 'detail') return 0; if ((type as 'add' | 'edit' | 'detail') === 'detail') return 0;
if (!isResponseSuccess(stockProducts)) return 0; const productWarehouse = getKnownProductWarehouse(productWarehouseId);
const data = stockProducts.data as unknown as ProductWarehouse[];
const productWarehouse = data.find((pw) => pw.id === productWarehouseId);
return productWarehouse?.quantity ?? 0; return productWarehouse?.quantity ?? 0;
}, },
[stockProducts, type] [getKnownProductWarehouse, type]
); );
const getStockUsageError = useCallback( const getStockUsageError = useCallback(
@@ -1344,10 +1420,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const getProductFlagBadgeAdornment = useCallback( const getProductFlagBadgeAdornment = useCallback(
(productWarehouseId: number) => { (productWarehouseId: number) => {
if (!isResponseSuccess(stockProducts)) return null; const productWarehouse = getKnownProductWarehouse(productWarehouseId);
const data = stockProducts.data as unknown as ProductWarehouse[];
const productWarehouse = data.find((pw) => pw.id === productWarehouseId);
if (!productWarehouse) return null; if (!productWarehouse) return null;
const hasPakanFlag = productWarehouse.product.flags?.includes('PAKAN'); const hasPakanFlag = productWarehouse.product.flags?.includes('PAKAN');
@@ -1363,7 +1436,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
return null; return null;
}, },
[stockProducts] [getKnownProductWarehouse]
); );
const getProductUomSuffix = useCallback( const getProductUomSuffix = useCallback(
@@ -1388,23 +1461,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
} }
} }
let rawData; const productWarehouse = getKnownProductWarehouse(productWarehouseId);
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);
return productWarehouse?.product.uom.name || null; return productWarehouse?.product.uom.name || null;
}, },
[stockProducts, depletionProductsData, eggProductsData, initialValues, type] [getKnownProductWarehouse, initialValues, type]
); );
const getAvailableStockProductOptions = useCallback( const getAvailableStockProductOptions = useCallback(