|
|
@@ -15,7 +15,7 @@ import CloseIcon from "@mui/icons-material/Close"; |
|
|
import { Autocomplete } from "@mui/material"; |
|
|
import { Autocomplete } from "@mui/material"; |
|
|
import { WarehouseResult } from "@/app/api/warehouse"; |
|
|
import { WarehouseResult } from "@/app/api/warehouse"; |
|
|
import { fetchWarehouseListClient } from "@/app/api/warehouse/client"; |
|
|
import { fetchWarehouseListClient } from "@/app/api/warehouse/client"; |
|
|
import { updateInventoryLotLineQuantities } from "@/app/api/inventory/actions"; |
|
|
|
|
|
|
|
|
import { createStockTransfer } from "@/app/api/inventory/actions"; |
|
|
|
|
|
|
|
|
interface Props { |
|
|
interface Props { |
|
|
inventoryLotLines: InventoryLotLineResult[] | null; |
|
|
inventoryLotLines: InventoryLotLineResult[] | null; |
|
|
@@ -31,7 +31,7 @@ const InventoryLotLineTable: React.FC<Props> = ({ inventoryLotLines, pagingContr |
|
|
const [stockTransferModalOpen, setStockTransferModalOpen] = useState(false); |
|
|
const [stockTransferModalOpen, setStockTransferModalOpen] = useState(false); |
|
|
const [selectedLotLine, setSelectedLotLine] = useState<InventoryLotLineResult | null>(null); |
|
|
const [selectedLotLine, setSelectedLotLine] = useState<InventoryLotLineResult | null>(null); |
|
|
const [startLocation, setStartLocation] = useState<string>(""); |
|
|
const [startLocation, setStartLocation] = useState<string>(""); |
|
|
const [targetLocation, setTargetLocation] = useState<string>(""); |
|
|
|
|
|
|
|
|
const [targetLocation, setTargetLocation] = useState<number | null>(null); // Store warehouse ID instead of code |
|
|
const [targetLocationInput, setTargetLocationInput] = useState<string>(""); |
|
|
const [targetLocationInput, setTargetLocationInput] = useState<string>(""); |
|
|
const [qtyToBeTransferred, setQtyToBeTransferred] = useState<number>(0); |
|
|
const [qtyToBeTransferred, setQtyToBeTransferred] = useState<number>(0); |
|
|
const [warehouses, setWarehouses] = useState<WarehouseResult[]>([]); |
|
|
const [warehouses, setWarehouses] = useState<WarehouseResult[]>([]); |
|
|
@@ -65,7 +65,7 @@ const InventoryLotLineTable: React.FC<Props> = ({ inventoryLotLines, pagingContr |
|
|
setSelectedLotLine(lotLine); |
|
|
setSelectedLotLine(lotLine); |
|
|
setStockTransferModalOpen(true); |
|
|
setStockTransferModalOpen(true); |
|
|
setStartLocation(lotLine.warehouse.code || ""); |
|
|
setStartLocation(lotLine.warehouse.code || ""); |
|
|
setTargetLocation(""); |
|
|
|
|
|
|
|
|
setTargetLocation(null); |
|
|
setTargetLocationInput(""); |
|
|
setTargetLocationInput(""); |
|
|
setQtyToBeTransferred(0); |
|
|
setQtyToBeTransferred(0); |
|
|
}, |
|
|
}, |
|
|
@@ -188,34 +188,46 @@ const InventoryLotLineTable: React.FC<Props> = ({ inventoryLotLines, pagingContr |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
const handleCloseStockTransferModal = useCallback(() => { |
|
|
const handleCloseStockTransferModal = useCallback(() => { |
|
|
setStockTransferModalOpen(false); |
|
|
|
|
|
setSelectedLotLine(null); |
|
|
|
|
|
setStartLocation(""); |
|
|
|
|
|
setTargetLocation(""); |
|
|
|
|
|
setTargetLocationInput(""); |
|
|
|
|
|
setQtyToBeTransferred(0); |
|
|
|
|
|
|
|
|
setStockTransferModalOpen(false); |
|
|
|
|
|
setSelectedLotLine(null); |
|
|
|
|
|
setStartLocation(""); |
|
|
|
|
|
setTargetLocation(null); |
|
|
|
|
|
setTargetLocationInput(""); |
|
|
|
|
|
setQtyToBeTransferred(0); |
|
|
}, []); |
|
|
}, []); |
|
|
|
|
|
|
|
|
const handleSubmitStockTransfer = useCallback(async () => { |
|
|
const handleSubmitStockTransfer = useCallback(async () => { |
|
|
try { |
|
|
|
|
|
setIsUploading(true); |
|
|
|
|
|
|
|
|
|
|
|
// Decrease the inQty (availableQty) in the source inventory lot line |
|
|
|
|
|
|
|
|
if (!selectedLotLine || !targetLocation || qtyToBeTransferred <= 0) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
setIsUploading(true); |
|
|
|
|
|
|
|
|
|
|
|
const request = { |
|
|
|
|
|
inventoryLotLineId: selectedLotLine.id, |
|
|
|
|
|
transferredQty: qtyToBeTransferred, |
|
|
|
|
|
warehouseId: targetLocation, // targetLocation now contains warehouse ID |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// TODO: Add logic to increase qty in target location warehouse |
|
|
|
|
|
|
|
|
|
|
|
alert(t("Stock transfer successful")); |
|
|
|
|
|
handleCloseStockTransferModal(); |
|
|
|
|
|
|
|
|
|
|
|
// TODO: Refresh the inventory lot lines list |
|
|
|
|
|
} catch (error: any) { |
|
|
|
|
|
console.error("Error transferring stock:", error); |
|
|
|
|
|
alert(error?.message || t("Failed to transfer stock. Please try again.")); |
|
|
|
|
|
} finally { |
|
|
|
|
|
setIsUploading(false); |
|
|
|
|
|
|
|
|
const response = await createStockTransfer(request); |
|
|
|
|
|
|
|
|
|
|
|
if (response && response.type === "success") { |
|
|
|
|
|
alert(t("Stock transfer successful")); |
|
|
|
|
|
handleCloseStockTransferModal(); |
|
|
|
|
|
|
|
|
|
|
|
// Refresh the inventory lot lines list |
|
|
|
|
|
window.location.reload(); // Or use your preferred refresh method |
|
|
|
|
|
} else { |
|
|
|
|
|
throw new Error(response?.message || t("Failed to transfer stock")); |
|
|
} |
|
|
} |
|
|
}, [selectedLotLine, targetLocation, qtyToBeTransferred, originalQty, handleCloseStockTransferModal, setIsUploading, t]); |
|
|
|
|
|
|
|
|
} catch (error: any) { |
|
|
|
|
|
console.error("Error transferring stock:", error); |
|
|
|
|
|
alert(error?.message || t("Failed to transfer stock. Please try again.")); |
|
|
|
|
|
} finally { |
|
|
|
|
|
setIsUploading(false); |
|
|
|
|
|
} |
|
|
|
|
|
}, [selectedLotLine, targetLocation, qtyToBeTransferred, handleCloseStockTransferModal, setIsUploading, t]); |
|
|
|
|
|
|
|
|
return <> |
|
|
return <> |
|
|
<Typography variant="h6">{inventory ? `${t("Item selected")}: ${inventory.itemCode} | ${inventory.itemName} (${t(inventory.itemType)})` : t("No items are selected yet.")}</Typography> |
|
|
<Typography variant="h6">{inventory ? `${t("Item selected")}: ${inventory.itemCode} | ${inventory.itemName} (${t(inventory.itemType)})` : t("No items are selected yet.")}</Typography> |
|
|
@@ -276,55 +288,55 @@ const InventoryLotLineTable: React.FC<Props> = ({ inventoryLotLines, pagingContr |
|
|
</Grid> |
|
|
</Grid> |
|
|
<Grid item xs={5.5}> |
|
|
<Grid item xs={5.5}> |
|
|
<Autocomplete |
|
|
<Autocomplete |
|
|
options={warehouses.filter(w => w.code !== startLocation)} |
|
|
|
|
|
getOptionLabel={(option) => option.code || ""} |
|
|
|
|
|
value={targetLocation ? warehouses.find(w => w.code === targetLocation) || null : null} |
|
|
|
|
|
inputValue={targetLocationInput} |
|
|
|
|
|
onInputChange={(event, newInputValue) => { |
|
|
|
|
|
setTargetLocationInput(newInputValue); |
|
|
|
|
|
if (targetLocation && newInputValue !== targetLocation) { |
|
|
|
|
|
setTargetLocation(""); |
|
|
|
|
|
} |
|
|
|
|
|
}} |
|
|
|
|
|
onChange={(event, newValue) => { |
|
|
|
|
|
if (newValue) { |
|
|
|
|
|
setTargetLocation(newValue.code); |
|
|
|
|
|
setTargetLocationInput(newValue.code); |
|
|
|
|
|
} else { |
|
|
|
|
|
setTargetLocation(""); |
|
|
|
|
|
setTargetLocationInput(""); |
|
|
|
|
|
} |
|
|
|
|
|
}} |
|
|
|
|
|
filterOptions={(options, { inputValue }) => { |
|
|
|
|
|
if (!inputValue || inputValue.trim() === "") return options; |
|
|
|
|
|
const searchTerm = inputValue.toLowerCase().trim(); |
|
|
|
|
|
return options.filter((option) => |
|
|
|
|
|
(option.code || "").toLowerCase().includes(searchTerm) || |
|
|
|
|
|
(option.name || "").toLowerCase().includes(searchTerm) || |
|
|
|
|
|
(option.description || "").toLowerCase().includes(searchTerm) |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
options={warehouses.filter(w => w.code !== startLocation)} |
|
|
|
|
|
getOptionLabel={(option) => option.code || ""} |
|
|
|
|
|
value={targetLocation ? warehouses.find(w => w.id === targetLocation) || null : null} |
|
|
|
|
|
inputValue={targetLocationInput} |
|
|
|
|
|
onInputChange={(event, newInputValue) => { |
|
|
|
|
|
setTargetLocationInput(newInputValue); |
|
|
|
|
|
if (targetLocation && newInputValue !== warehouses.find(w => w.id === targetLocation)?.code) { |
|
|
|
|
|
setTargetLocation(null); |
|
|
|
|
|
} |
|
|
|
|
|
}} |
|
|
|
|
|
onChange={(event, newValue) => { |
|
|
|
|
|
if (newValue) { |
|
|
|
|
|
setTargetLocation(newValue.id); |
|
|
|
|
|
setTargetLocationInput(newValue.code); |
|
|
|
|
|
} else { |
|
|
|
|
|
setTargetLocation(null); |
|
|
|
|
|
setTargetLocationInput(""); |
|
|
|
|
|
} |
|
|
|
|
|
}} |
|
|
|
|
|
filterOptions={(options, { inputValue }) => { |
|
|
|
|
|
if (!inputValue || inputValue.trim() === "") return options; |
|
|
|
|
|
const searchTerm = inputValue.toLowerCase().trim(); |
|
|
|
|
|
return options.filter((option) => |
|
|
|
|
|
(option.code || "").toLowerCase().includes(searchTerm) || |
|
|
|
|
|
(option.name || "").toLowerCase().includes(searchTerm) || |
|
|
|
|
|
(option.description || "").toLowerCase().includes(searchTerm) |
|
|
|
|
|
); |
|
|
|
|
|
}} |
|
|
|
|
|
isOptionEqualToValue={(option, value) => option.id === value.id} |
|
|
|
|
|
autoHighlight={false} |
|
|
|
|
|
autoSelect={false} |
|
|
|
|
|
clearOnBlur={false} |
|
|
|
|
|
renderOption={(props, option) => ( |
|
|
|
|
|
<li {...props}> |
|
|
|
|
|
{option.code} |
|
|
|
|
|
</li> |
|
|
|
|
|
)} |
|
|
|
|
|
renderInput={(params) => ( |
|
|
|
|
|
<TextField |
|
|
|
|
|
{...params} |
|
|
|
|
|
label={t("Target Location")} |
|
|
|
|
|
variant="outlined" |
|
|
|
|
|
fullWidth |
|
|
|
|
|
InputLabelProps={{ |
|
|
|
|
|
shrink: !!targetLocation || !!targetLocationInput, |
|
|
|
|
|
sx: { fontSize: "0.9375rem" }, |
|
|
}} |
|
|
}} |
|
|
isOptionEqualToValue={(option, value) => option.code === value.code} |
|
|
|
|
|
autoHighlight={false} |
|
|
|
|
|
autoSelect={false} |
|
|
|
|
|
clearOnBlur={false} |
|
|
|
|
|
renderOption={(props, option) => ( |
|
|
|
|
|
<li {...props}> |
|
|
|
|
|
{option.code} |
|
|
|
|
|
</li> |
|
|
|
|
|
)} |
|
|
|
|
|
renderInput={(params) => ( |
|
|
|
|
|
<TextField |
|
|
|
|
|
{...params} |
|
|
|
|
|
label={t("Target Location")} |
|
|
|
|
|
variant="outlined" |
|
|
|
|
|
fullWidth |
|
|
|
|
|
InputLabelProps={{ |
|
|
|
|
|
shrink: !!targetLocation || !!targetLocationInput, |
|
|
|
|
|
sx: { fontSize: "0.9375rem" }, |
|
|
|
|
|
}} |
|
|
|
|
|
/> |
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
/> |
|
|
|
|
|
)} |
|
|
/> |
|
|
/> |
|
|
</Grid> |
|
|
</Grid> |
|
|
</Grid> |
|
|
</Grid> |
|
|
|