upload spreadsheet of opening inventory

This commit is contained in:
Sibunnayak 2024-10-25 10:37:58 +05:30
parent c478471124
commit 5f436716e9
4 changed files with 302 additions and 13 deletions

View File

@ -167,6 +167,7 @@ import SingleSales from "./views/Sales/SingleSale";
import MobileApp from "./views/configuration/MobileApp";
import PDOpeningInventory from "./views/OpeningInventory/PDOpeningInventory";
import DistributorOpeningInventory from "./views/PrincipalDistributors/OpeningInventory";
import UploadOpeningInventory from "./views/PrincipalDistributors/UploadOpeningInventory";
const routes = [
//dashboard
@ -425,6 +426,12 @@ const routes = [
element: DistributorOpeningInventory,
navName: "Distributor",
},
{
path: "/:distributortype/opening-inventory/upload/:id",
name: " Distributor Opening Inventory Upload",
element: UploadOpeningInventory,
navName: "Distributor",
},
//----------------------- End Product Management Routes------------------------------------------------
//Departure

View File

@ -15,6 +15,7 @@ const DistributorOpeningInventory = () => {
const [loading, setLoading] = useState(false);
const [productsData, setProductsData] = useState([]);
const [allProductsData, setAllProductsData] = useState([]);
const [categories, setCategories] = useState([]);
const [brands, setBrands] = useState([]);
const [user, setUser] = useState(null);
@ -77,8 +78,22 @@ const DistributorOpeningInventory = () => {
},
}
);
// console.log(response.data.products);
setProductsData(response.data?.products || []);
setTotalData(response.data?.totalProducts || 0);
// Merge new products with existing ones in allProductsData
setAllProductsData((prev) => {
const updatedList = [...prev];
response.data?.products?.forEach((product) => {
const index = updatedList.findIndex((p) => p._id === product._id);
if (index > -1) {
updatedList[index] = product; // Update existing product
} else {
updatedList.push(product); // Add new product
}
});
return updatedList;
});
} catch (err) {
swal({
title: "Error",
@ -157,19 +172,30 @@ const DistributorOpeningInventory = () => {
...prevStocks,
[sku]: newStock, // Update stock for specific product (identified by SKU)
}));
// Update the stock directly in allProductsData
setAllProductsData((prev) =>
prev.map((product) =>
product.SKU === sku ? { ...product, stock: newStock } : product
)
);
};
const handleSubmitStocks = async () => {
try {
const updatedProducts = productsData.map((product) => ({
...product,
stock: updatedStocks[product.SKU] || product.stock,
const updatedProducts = allProductsData.map((product) => ({
_id: product._id,
SKU: product.SKU,
name: product.name,
openingInventory: updatedStocks[product.SKU] || product.stock,
}));
await axios.post(
// console.log(updatedProducts);
// console.log(id);
await axios.put(
distributortype === "principaldistributor"
? `/api/pd/updateStocks/${id}`
: `/api/rd/updateStocks/${id}`,
{ updatedProducts },
? `/api/pd/stock-update`
: `/api/rd/stock-update`,
{ products: updatedProducts, userId: id },
{ headers: { Authorization: `Bearer ${token}` } }
);
swal("Success", "Stocks updated successfully!", "success");
@ -215,7 +241,23 @@ const DistributorOpeningInventory = () => {
<strong>Email:</strong> {user?.email}
</Typography>
</div>
<div className="page-title-right mr-2">
<Button
variant="contained"
color="primary"
className="font-bold capitalize"
onClick={() =>
navigate(
`/principaldistributor/opening-inventory/upload/${id}`,
{
replace: true,
}
)
}
>
Upload Spreadsheet
</Button>
</div>
{/* Back Button on the right */}
<div className="page-title-right">
<Button
@ -236,7 +278,7 @@ const DistributorOpeningInventory = () => {
<div className="row mt-2 mb-1">
<div className="col-12">
<div style={{ fontSize: "22px" }} className="fw-bold">
Product Stocks
Product Opening Inventory
</div>
</div>
</div>
@ -312,9 +354,23 @@ const DistributorOpeningInventory = () => {
))}
</select>
</div>
<div className="col-lg-2">
<Button
className="mt-4"
variant="contained"
color="primary"
onClick={handleSubmitStocks}
disabled={loading}
>
Save
</Button>
</div>
</div>
<div className="table-responsive table-shoot mt-3">
<div
className="table-responsive table-shoot mt-3"
style={{ overflowX: "auto" }}
>
<table
className="table table-centered table-nowrap"
style={{ border: "1px solid" }}

View File

@ -0,0 +1,222 @@
import React, { useState } from "react";
import axios from "axios";
import swal from "sweetalert";
import { isAutheticated } from "src/auth";
import { useNavigate, useParams } from "react-router-dom";
import { toast } from "react-hot-toast";
const UploadOpeningInventory = () => {
const [file, setFile] = useState(null);
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState([]);
const [newlyCreated, setNewlyCreated] = useState([]);
const [updatedInventories, setupdatedInventories] = useState([]);
const navigate = useNavigate();
const token = isAutheticated();
const { id, distributortype } = useParams();
const handleFileChange = (e) => {
const selectedFile = e.target.files[0];
if (
selectedFile &&
selectedFile.type ===
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
) {
setFile(selectedFile);
} else {
swal("Error", "Please upload a valid Excel file", "error");
setFile(null);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!file) {
toast.error("Please select a file to upload");
return;
}
// console.log(file);
// console.log(token);
setLoading(true);
setErrors([]);
setNewlyCreated([]);
setupdatedInventories([]);
try {
const formData = new FormData();
formData.append("file", file);
const { data } = await axios.post(
`/api/openinginventories/upload/${id}`,
formData,
{
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${token}`,
},
}
);
// console.log(data);
if (data?.errors && data?.errors?.length > 0) {
setErrors(data.errors);
}
if (data?.newlyCreated && data?.newlyCreated?.length > 0) {
setNewlyCreated(data?.newlyCreated);
}
if (data?.updatedOpeningInventories && data?.updatedOpeningInventories?.length > 0) {
setupdatedInventories(data?.updatedOpeningInventories);
// console.log(data.updatedOpeningInventories);
}
// Redirect or display success message
if (data?.errors && data?.errors.length > 0) {
setErrors(data?.errors);
swal({
title: "SpreadSheet Upload Successful",
text: "A few Products have errors. Please fix them and upload again.",
icon: "warning",
button: "OK",
});
} else if (
data?.newlyCreated.length > 0 ||
data?.updatedInventories.length > 0
) {
swal({
title: "SpreadSheet Upload Successful",
text: "Product Opening Inventory uploaded successfully.",
icon: "success",
buttons: "OK",
});
} else {
toast.success("File processed successfully with no new entries.");
handleCancel();
}
setFile(null); // Clear the file input
document.querySelector('input[type="file"]').value = "";
} catch (error) {
console.error("Upload error:", error);
swal(
"Error",
`Failed to upload Opening Inventory Spreedsheet: ${
error.response?.data?.message || "An unexpected error occurred"
}`,
"error"
);
} finally {
setLoading(false);
}
};
const handleCancel = () => {
navigate(
distributortype === "principaldistributor"
? `/principaldistributor/opening-inventory/${id}`
: `/retaildistributor/opening-inventory/${id}`
);
};
return (
<div className="container mt-4">
<div className="mb-6">
<button onClick={handleCancel} className="btn btn-secondary">
Back
</button>
</div>
<h5 className="mb-6 mt-4">Add Multiple Products Opening inventory</h5>
<div className="my-3">
<div className="row">
<div className="col-lg-9">
<input
type="file"
name="file"
className="form-control"
accept=".xlsx"
onChange={handleFileChange}
/>
</div>
<div className="col-lg-3">
<button
className="btn btn-primary"
onClick={handleSubmit}
disabled={loading}
>
{loading ? "Uploading..." : "Upload"}
</button>
</div>
</div>
<p className="pt-1 pl-2 text-secondary">Upload only .xlsx files*</p>
</div>
{errors.length > 0 && (
<div className="my-4">
<h6>Finding errors while adding the Opening Inventory.</h6>
<table className="table table-bordered">
<thead>
<tr>
<th>SKU</th>
<th>Product Name</th>
<th>Opening Inventory</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{errors.map((error, index) => (
<tr key={index}>
<td>{error.SKU || "N/A"}</td>
<td>{error.productName || "N/A"}</td>
<td>{error.openingInventory || "N/A"}</td>
<td>{error.message}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{updatedInventories.length > 0 && (
<div className="my-4">
<h6>Updated Products in the Opening Inventory List</h6>
<table className="table table-bordered">
<thead>
<tr>
<th>SKU</th>
<th>Product Name</th>
<th>Opening Inventory</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{updatedInventories.map((distributor, index) => (
<tr key={index}>
<td>{distributor.SKU || "N/A"}</td>
<td>{distributor.productName || "N/A"}</td>
<td>{distributor.openingInventory || "N/A"}</td>
<td>{distributor.updatedFields}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{newlyCreated.length > 0 && (
<div className="my-4">
<h6>Newly Added Products in Opening Inventory:</h6>
<table className="table table-bordered">
<thead>
<tr>
<th>SKU</th>
<th>Product Name</th>
<th>Opening Inventory</th>
</tr>
</thead>
<tbody>
{newlyCreated.map((distributor, index) => (
<tr key={index}>
<td>{distributor.SKU || "N/A"}</td>
<td>{distributor.productName || "N/A"}</td>
<td>{distributor.openingInventory }</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
);
};
export default UploadOpeningInventory;

View File

@ -205,13 +205,17 @@ const principalDistributor = () => {
</div>
</div>
<div className="table-responsive table-shoot mt-3">
<div
className="table-responsive table-shoot mt-3"
style={{ overflowX: "auto" }}
>
<table
className="table table-centered table-nowrap"
style={{
border: "1px solid",
tableLayout: "fixed",
width: "100%",
tableLayout: "fixed", // Keeps columns fixed width
width: "100%", // Ensures table takes full width
minWidth: "1000px", // Prevents the table from shrinking too much
}}
>
<thead