Import supplier cost updates from a CSV file

Java · Spring Boot · beginner · modification

Adds an upload for the supplier's monthly cost file. `SupplierCostController` takes the multipart file and hands its stream to `SupplierCostImportService.importCosts`, which checks the header, splits each data row into its three fields, validates the cost against a strict decimal pattern, looks up all the file's SKUs in one `findBySkuIn` query instead of one query per row, and applies the costs inside a single transaction. Rows it cannot apply are reported by line number so purchasing can fix them in the supplier portal and upload again. The description column is informational and is not stored.

Shop backend on Spring Boot 3.3 (Java 21), Spring Data JPA (Hibernate 6) on PostgreSQL. Existing and unchanged: the `Product` entity (`id`, `sku` unique and non-null, `name`, `unitCost` a non-null `BigDecimal` in a `numeric(10,2)` column, with `setUnitCost`), the `SecurityConfig` that requires `ROLE_PURCHASING` on `/api/purchasing/**` and handles CSRF, and `spring.servlet.multipart.max-file-size=1MB`. A supplier file has at most 2,000 rows. There is one product catalog (no tenants). Spring Boot's default error handling renders a `ResponseStatusException` with its status and no stack trace.

Requirements

Files touched

--- src/main/java/com/acme/shop/catalog/ProductRepository.java
 
 import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.Collection;
+import java.util.List;
 import java.util.Optional;
 
 
     Optional<Product> findBySku(String sku);
+
+    List<Product> findBySkuIn(Collection<String> skus);
 }
 
--- src/main/java/com/acme/shop/purchasing/SupplierCostController.java
+package com.acme.shop.purchasing;
+
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+import java.io.IOException;
+import java.io.InputStream;
+
+@RestController
+@RequestMapping("/api/purchasing/supplier-costs")
+public class SupplierCostController {
+
+    private final SupplierCostImportService importService;

Review this PR

Java practice