mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-24 07:15:44 +00:00
feat(FE): Validate quantity against available stock
This commit is contained in:
@@ -46,6 +46,7 @@ import {
|
|||||||
TRANSACTION_SUBTYPE_OPTIONS,
|
TRANSACTION_SUBTYPE_OPTIONS,
|
||||||
} from '@/config/constant';
|
} from '@/config/constant';
|
||||||
import NumberInput from '@/components/input/NumberInput';
|
import NumberInput from '@/components/input/NumberInput';
|
||||||
|
import { formatNumber } from '@/lib/helper';
|
||||||
|
|
||||||
interface InventoryAdjustmentFormProps {
|
interface InventoryAdjustmentFormProps {
|
||||||
type?: 'add' | 'detail';
|
type?: 'add' | 'detail';
|
||||||
@@ -62,6 +63,7 @@ const InventoryAdjustmentForm = ({
|
|||||||
setInventoryAdjustmentFormErrorMessage,
|
setInventoryAdjustmentFormErrorMessage,
|
||||||
] = useState('');
|
] = useState('');
|
||||||
const [quantityLabel, setQuantityLabel] = useState('Kuantitas');
|
const [quantityLabel, setQuantityLabel] = useState('Kuantitas');
|
||||||
|
const [quantityError, setQuantityError] = useState<string>('');
|
||||||
|
|
||||||
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(
|
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(
|
||||||
null
|
null
|
||||||
@@ -211,6 +213,25 @@ const InventoryAdjustmentForm = ({
|
|||||||
: [];
|
: [];
|
||||||
}, [productWarehouses]);
|
}, [productWarehouses]);
|
||||||
|
|
||||||
|
// Get available quantity from selected product
|
||||||
|
const selectedProductQuantity = useMemo(() => {
|
||||||
|
if (!selectedProduct) return 0;
|
||||||
|
const product = productOptions.find(
|
||||||
|
(opt) => opt.value === selectedProduct.value
|
||||||
|
);
|
||||||
|
return product?.quantity ?? 0;
|
||||||
|
}, [selectedProduct, productOptions]);
|
||||||
|
|
||||||
|
// Check if current transaction subtype reduces stock
|
||||||
|
const isStockOutSubtype = useMemo(() => {
|
||||||
|
const subtype = selectedTransactionSubtype?.value;
|
||||||
|
return (
|
||||||
|
subtype === 'RECORDING_STOCK_OUT' ||
|
||||||
|
subtype === 'RECORDING_DEPLETION_OUT' ||
|
||||||
|
subtype === 'MARKETING_OUT'
|
||||||
|
);
|
||||||
|
}, [selectedTransactionSubtype]);
|
||||||
|
|
||||||
const selectedProductFlags = useMemo(() => {
|
const selectedProductFlags = useMemo(() => {
|
||||||
if (!selectedProduct) return [];
|
if (!selectedProduct) return [];
|
||||||
const product = productOptions.find(
|
const product = productOptions.find(
|
||||||
@@ -372,6 +393,28 @@ const InventoryAdjustmentForm = ({
|
|||||||
}
|
}
|
||||||
}, [selectedTransactionSubtype]);
|
}, [selectedTransactionSubtype]);
|
||||||
|
|
||||||
|
// Validate quantity against available stock
|
||||||
|
useEffect(() => {
|
||||||
|
const qty = Number(formik.values.qty);
|
||||||
|
if (
|
||||||
|
isStockOutSubtype &&
|
||||||
|
selectedProduct &&
|
||||||
|
qty > 0 &&
|
||||||
|
qty > selectedProductQuantity
|
||||||
|
) {
|
||||||
|
setQuantityError(
|
||||||
|
`Kuantitas ${formatNumber(qty)} melebihi stok tersedia (${formatNumber(selectedProductQuantity)})`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setQuantityError('');
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
formik.values.qty,
|
||||||
|
isStockOutSubtype,
|
||||||
|
selectedProduct,
|
||||||
|
selectedProductQuantity,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedTransactionType?.value === 'RECORDING' && selectedProduct) {
|
if (selectedTransactionType?.value === 'RECORDING' && selectedProduct) {
|
||||||
setSelectedTransactionSubtype(null);
|
setSelectedTransactionSubtype(null);
|
||||||
@@ -804,11 +847,18 @@ const InventoryAdjustmentForm = ({
|
|||||||
value={formik.values.qty}
|
value={formik.values.qty}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
isError={formik.touched.qty && Boolean(formik.errors.qty)}
|
isError={
|
||||||
errorMessage={formik.errors.qty as string}
|
(formik.touched.qty && Boolean(formik.errors.qty)) ||
|
||||||
|
Boolean(quantityError)
|
||||||
|
}
|
||||||
|
errorMessage={(formik.errors.qty as string) || quantityError}
|
||||||
readOnly={type === 'detail'}
|
readOnly={type === 'detail'}
|
||||||
|
bottomLabel={
|
||||||
|
selectedProduct
|
||||||
|
? `Kuantitas: ${formatNumber(selectedProductQuantity)}`
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Number Input Price */}
|
{/* Number Input Price */}
|
||||||
<NumberInput
|
<NumberInput
|
||||||
required
|
required
|
||||||
@@ -856,7 +906,7 @@ const InventoryAdjustmentForm = ({
|
|||||||
type='submit'
|
type='submit'
|
||||||
color='primary'
|
color='primary'
|
||||||
isLoading={formik.isSubmitting}
|
isLoading={formik.isSubmitting}
|
||||||
disabled={formik.isSubmitting}
|
disabled={formik.isSubmitting || Boolean(quantityError)}
|
||||||
className='px-4 w-full sm:w-auto'
|
className='px-4 w-full sm:w-auto'
|
||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
|
|||||||
Reference in New Issue
Block a user