From 839343de14154a1813466bc4676b6902d634c0a9 Mon Sep 17 00:00:00 2001 From: Sidharth Prabhu Date: Mon, 20 Apr 2026 14:13:17 +0530 Subject: [PATCH] Vendor Order Stacking made functional. --- .../sales/controller/ProductController.java | 92 +++++++++++++------ .../sales/repository/ProductRepository.java | 6 ++ .../sales/service/PurchaseService.java | 20 +++- frontend/src/pages/Purchases.tsx | 37 +++++--- 4 files changed, 116 insertions(+), 39 deletions(-) diff --git a/backend/src/main/java/com/rit/canteen/sales/controller/ProductController.java b/backend/src/main/java/com/rit/canteen/sales/controller/ProductController.java index 7ad3fec4..b9f510cb 100644 --- a/backend/src/main/java/com/rit/canteen/sales/controller/ProductController.java +++ b/backend/src/main/java/com/rit/canteen/sales/controller/ProductController.java @@ -183,36 +183,74 @@ public class ProductController { public ResponseEntity publishProduct(@PathVariable Long id, @RequestBody Product productDetails) { return productRepository.findById(id) .map(product -> { - // Generate new PRD ID - String newProductId = "PRD-" + String.format("%04d", new java.util.Random().nextInt(10000)); + String finalProductId = productDetails.getProductId(); - // Update all details first - product.setName(productDetails.getName()); - product.setProductId(newProductId); - product.setCategory(productDetails.getCategory()); - product.setDescription(productDetails.getDescription()); - product.setBasePrice(productDetails.getBasePrice()); - product.setPrice(productDetails.getPrice()); - product.setOfferPrice(productDetails.getOfferPrice()); - product.setCounter(productDetails.getCounter()); - product.setTag(productDetails.getTag()); - product.setBarcode(productDetails.getBarcode()); - product.setImageData(productDetails.getImageData()); - product.setStock(productDetails.getStock()); - - product.getSessions().clear(); - if (productDetails.getSessions() != null) { - product.getSessions().addAll(productDetails.getSessions()); + // If it's a draft ID or blank, try to find an existing live product by name first + if (finalProductId == null || finalProductId.startsWith("DRAFT-") || finalProductId.isEmpty()) { + List nameMatches = productRepository.findByNameRobust(productDetails.getName()); + if (!nameMatches.isEmpty()) { + finalProductId = nameMatches.get(0).getProductId(); + } } - // Finalize - product.setDraft(false); - product.setActive(true); - - Product updated = productRepository.save(product); - updateStallAssociations(updated, productDetails.getStalls()); - - return ResponseEntity.ok(productRepository.findById(updated.getId()).orElse(updated)); + // If still a draft ID or blank, generate a new PRD ID + if (finalProductId == null || finalProductId.startsWith("DRAFT-") || finalProductId.isEmpty()) { + finalProductId = "PRD-" + String.format("%04d", new java.util.Random().nextInt(10000)); + } + + // Check if a live product with this ID already exists (to merge) + final String targetId = finalProductId; + java.util.Optional existingLive = productRepository.findByProductIdRobust(targetId); + + if (existingLive.isPresent()) { + Product live = existingLive.get(); + // MERGE: Update stock and other details + live.setStock((live.getStock() != null ? live.getStock() : 0) + (productDetails.getStock() != null ? productDetails.getStock() : 0)); + live.setName(productDetails.getName()); + live.setCategory(productDetails.getCategory()); + live.setDescription(productDetails.getDescription()); + live.setBasePrice(productDetails.getBasePrice()); + live.setPrice(productDetails.getPrice()); + live.setOfferPrice(productDetails.getOfferPrice()); + live.setCounter(productDetails.getCounter()); + live.setTag(productDetails.getTag()); + live.setImageData(productDetails.getImageData()); + live.setDraft(false); + live.setActive(true); + + Product savedLive = productRepository.save(live); + productRepository.delete(product); // Delete the draft + updateStallAssociations(savedLive, productDetails.getStalls()); + return ResponseEntity.ok(productRepository.findById(savedLive.getId()).orElse(savedLive)); + } else { + // CREATE/UPDATE as new live product + product.setName(productDetails.getName()); + product.setProductId(finalProductId); + product.setCategory(productDetails.getCategory()); + product.setDescription(productDetails.getDescription()); + product.setBasePrice(productDetails.getBasePrice()); + product.setPrice(productDetails.getPrice()); + product.setOfferPrice(productDetails.getOfferPrice()); + product.setCounter(productDetails.getCounter()); + product.setTag(productDetails.getTag()); + product.setBarcode(productDetails.getBarcode()); + product.setImageData(productDetails.getImageData()); + product.setStock(productDetails.getStock()); + + product.getSessions().clear(); + if (productDetails.getSessions() != null) { + product.getSessions().addAll(productDetails.getSessions()); + } + + // Finalize + product.setDraft(false); + product.setActive(true); + + Product updated = productRepository.save(product); + updateStallAssociations(updated, productDetails.getStalls()); + + return ResponseEntity.ok(productRepository.findById(updated.getId()).orElse(updated)); + } }) .orElse(ResponseEntity.notFound().build()); } diff --git a/backend/src/main/java/com/rit/canteen/sales/repository/ProductRepository.java b/backend/src/main/java/com/rit/canteen/sales/repository/ProductRepository.java index 351dc5d2..97b71b7e 100644 --- a/backend/src/main/java/com/rit/canteen/sales/repository/ProductRepository.java +++ b/backend/src/main/java/com/rit/canteen/sales/repository/ProductRepository.java @@ -24,6 +24,12 @@ public interface ProductRepository extends JpaRepository { List findDistinctCategories(); List findByIsDraftTrue(); + + @Query("SELECT p FROM Product p WHERE LOWER(TRIM(p.name)) = LOWER(TRIM(:name)) AND (p.isDraft = false OR p.isDraft IS NULL)") + List findByNameRobust(@org.springframework.data.repository.query.Param("name") String name); + + @Query("SELECT p FROM Product p WHERE p.productId = :productId AND (p.isDraft = false OR p.isDraft IS NULL)") + java.util.Optional findByProductIdRobust(@org.springframework.data.repository.query.Param("productId") String productId); boolean existsByNameAndCategory(String name, String category); @org.springframework.data.jpa.repository.Modifying diff --git a/backend/src/main/java/com/rit/canteen/sales/service/PurchaseService.java b/backend/src/main/java/com/rit/canteen/sales/service/PurchaseService.java index daa2f3d9..3f09fed1 100644 --- a/backend/src/main/java/com/rit/canteen/sales/service/PurchaseService.java +++ b/backend/src/main/java/com/rit/canteen/sales/service/PurchaseService.java @@ -154,13 +154,31 @@ public class PurchaseService { for (PurchaseOrderItem item : order.getItems()) { Product draftProduct = new Product(); draftProduct.setName(item.getProductName()); + + // Try to find an existing live product with the same name to copy metadata + String itemName = item.getProductName(); + List existingProducts = productRepository.findByNameRobust(itemName); + if (!existingProducts.isEmpty()) { + Product existing = existingProducts.get(0); + draftProduct.setCategory(existing.getCategory()); + draftProduct.setImageData(existing.getImageData()); + draftProduct.setDescription(existing.getDescription()); + draftProduct.setTag(existing.getTag()); + draftProduct.setCounter(existing.getCounter()); + draftProduct.setPrice(existing.getPrice()); + } + draftProduct.setStock(item.getQuantity() != null ? item.getQuantity().intValue() : 0); draftProduct.setBasePrice(item.getRate()); draftProduct.setDraft(true); draftProduct.setActive(false); // Not live yet // Set a unique product ID if possible - draftProduct.setProductId("DRAFT-" + String.format("%04d", new java.util.Random().nextInt(10000)) + "-" + item.getId()); + if (!existingProducts.isEmpty()) { + draftProduct.setProductId(existingProducts.get(0).getProductId()); + } else { + draftProduct.setProductId("DRAFT-" + String.format("%04d", new java.util.Random().nextInt(10000)) + "-" + item.getId()); + } productRepository.save(draftProduct); } diff --git a/frontend/src/pages/Purchases.tsx b/frontend/src/pages/Purchases.tsx index 012c913c..8df54380 100644 --- a/frontend/src/pages/Purchases.tsx +++ b/frontend/src/pages/Purchases.tsx @@ -109,13 +109,29 @@ const Purchases: React.FC = () => { }; const handleItemChange = (index: number, field: keyof PurchaseOrderItem, value: string | number) => { - const updatedItems = [...newOrder.items]; - const item = { ...updatedItems[index], [field]: value }; - if (field === 'quantity' || field === 'rate') { - item.total = Number(item.quantity) * Number(item.rate); - } - updatedItems[index] = item; - setNewOrder({ ...newOrder, items: updatedItems }); + setNewOrder(prev => { + const updatedItems = [...prev.items]; + const item = { ...updatedItems[index], [field]: value }; + if (field === 'quantity' || field === 'rate' || field === 'productName') { + item.total = Number(item.quantity) * Number(item.rate); + } + updatedItems[index] = item; + return { ...prev, items: updatedItems }; + }); + }; + + const handleProductSelect = (index: number, product: any) => { + setNewOrder(prev => { + const updatedItems = [...prev.items]; + updatedItems[index] = { + ...updatedItems[index], + productName: product.name, + rate: product.basePrice || 0, + total: Number(updatedItems[index].quantity) * Number(product.basePrice || 0) + }; + return { ...prev, items: updatedItems }; + }); + setActiveSearchIdx(null); }; const calculateTotal = () => { @@ -442,10 +458,9 @@ const Purchases: React.FC = () => {