Merge branch 'codex/sales-at-farm-level' into 'development'

Codex/sales at farm level

See merge request mbugroup/lti-web-client!367
This commit is contained in:
Adnan Zahir
2026-04-04 10:08:36 +07:00
2 changed files with 97 additions and 26 deletions
@@ -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() ?? '',
}
@@ -210,6 +210,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
useState<string>('');
const [eggProductsLocationId, setEggProductsLocationId] =
useState<string>('');
const [knownProductWarehouses, setKnownProductWarehouses] = useState<
Record<number, ProductWarehouse>
>({});
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,85 @@ 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(
(productWarehouses: ProductWarehouse[]) =>
productWarehouses
@@ -965,7 +1050,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 +1331,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 +1428,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 +1444,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
return null;
},
[stockProducts]
[getKnownProductWarehouse]
);
const getProductUomSuffix = useCallback(
@@ -1388,23 +1469,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(