From f539c64588adb2d8c14cf9798418b24b55dc2579 Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 30 Nov 2025 05:40:59 -0500 Subject: [PATCH] small changes. still working --- .../MerchantFeedImportServiceImpl.java | 150 +++++++++++------- .../ballistic/repos/ProductRepository.java | 8 +- 2 files changed, 98 insertions(+), 60 deletions(-) diff --git a/src/main/java/group/goforward/ballistic/imports/MerchantFeedImportServiceImpl.java b/src/main/java/group/goforward/ballistic/imports/MerchantFeedImportServiceImpl.java index a4643d0..622a87a 100644 --- a/src/main/java/group/goforward/ballistic/imports/MerchantFeedImportServiceImpl.java +++ b/src/main/java/group/goforward/ballistic/imports/MerchantFeedImportServiceImpl.java @@ -1,6 +1,6 @@ package group.goforward.ballistic.imports; + import java.math.BigDecimal; -import java.util.Optional; import group.goforward.ballistic.model.Brand; import group.goforward.ballistic.model.Merchant; @@ -41,37 +41,37 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService // TODO: replace this with real feed parsing: // List rows = feedClient.fetch(merchant); - // rows.forEach(row -> upsertProduct(merchant, row)); + // rows.forEach(row -> upsertProduct(merchant, brand, row)); MerchantFeedRow row = new MerchantFeedRow( - "TEST-SKU-001", - "APPG100002", - brand.getName(), - "Test Product From Import", - "This is a long description from AvantLink.", - "Short description from AvantLink.", - "Rifles", - "AR-15 Parts", - "Handguards & Rails", - "https://example.com/thumb.jpg", - "https://example.com/image.jpg", - "https://example.com/buy-link", - "ar-15, handguard, aero", - null, - new BigDecimal("199.99"), // retailPrice - new BigDecimal("149.99"), // salePrice - null, - null, - null, - null, - "https://example.com/medium.jpg", - null, - null, - null - ); + "TEST-SKU-001", + "APPG100002", + brand.getName(), + "Test Product From Import", + "This is a long description from AvantLink.", + "Short description from AvantLink.", + "Rifles", + "AR-15 Parts", + "Handguards & Rails", + "https://example.com/thumb.jpg", + "https://example.com/image.jpg", + "https://example.com/buy-link", + "ar-15, handguard, aero", + null, + new BigDecimal("199.99"), // retailPrice + new BigDecimal("149.99"), // salePrice + null, + null, + null, + null, + "https://example.com/medium.jpg", + null, + null, + null + ); - Product p = createProduct(brand, row); + Product p = upsertProduct(merchant, brand, row); - System.out.println("IMPORT >>> created product id=" + p.getId() + System.out.println("IMPORT >>> upserted product id=" + p.getId() + ", name=" + p.getName() + ", slug=" + p.getSlug() + ", platform=" + p.getPlatform() @@ -79,13 +79,54 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService + ", merchant=" + merchant.getName()); } - private Product createProduct(Brand brand, MerchantFeedRow row) { - System.out.println("IMPORT >>> createProduct brand=" + brand.getName() + /** + * Upsert logic: + * - Try Brand+MPN, then Brand+UPC (for now using sku as a stand-in) + * - If found, update fields but keep existing slug + * - If not found, create a new Product and generate a unique slug + */ + private Product upsertProduct(Merchant merchant, Brand brand, MerchantFeedRow row) { + System.out.println("IMPORT >>> upsertProduct brand=" + brand.getName() + ", sku=" + row.sku() + ", productName=" + row.productName()); - Product p = new Product(); - p.setBrand(brand); + String mpn = trimOrNull(row.manufacturerId()); + String upc = trimOrNull(row.sku()); // later: real UPC column + + java.util.List candidates = java.util.Collections.emptyList(); + + if (mpn != null) { + candidates = productRepository.findAllByBrandAndMpn(brand, mpn); + } + if ((candidates == null || candidates.isEmpty()) && upc != null) { + candidates = productRepository.findAllByBrandAndUpc(brand, upc); + } + + Product p; + boolean isNew = (candidates == null || candidates.isEmpty()); + + if (isNew) { + p = new Product(); + p.setBrand(brand); + } else { + if (candidates.size() > 1) { + System.out.println("IMPORT !!! WARNING: multiple existing products found for brand=" + + brand.getName() + ", mpn=" + mpn + ", upc=" + upc + + ". Using the first match (id=" + candidates.get(0).getId() + ")"); + } + p = candidates.get(0); + } + + updateProductFromRow(p, row, isNew); + + return productRepository.save(p); + } + + /** + * Shared mapping logic from feed row -> Product entity. + * If isNew = true, we generate a slug. Otherwise we leave the slug alone. + */ + private void updateProductFromRow(Product p, MerchantFeedRow row, boolean isNew) { // ---------- NAME ---------- String name = coalesce( @@ -100,25 +141,27 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService p.setName(name); // ---------- SLUG ---------- - String baseForSlug = coalesce( - trimOrNull(name), - trimOrNull(row.sku()) - ); - if (baseForSlug == null) { - baseForSlug = "product-" + System.currentTimeMillis(); - } + if (isNew || p.getSlug() == null || p.getSlug().isBlank()) { + String baseForSlug = coalesce( + trimOrNull(name), + trimOrNull(row.sku()) + ); + if (baseForSlug == null) { + baseForSlug = "product-" + System.currentTimeMillis(); + } - String slug = baseForSlug - .toLowerCase() - .replaceAll("[^a-z0-9]+", "-") - .replaceAll("(^-|-$)", ""); - if (slug.isBlank()) { - slug = "product-" + System.currentTimeMillis(); - } + String slug = baseForSlug + .toLowerCase() + .replaceAll("[^a-z0-9]+", "-") + .replaceAll("(^-|-$)", ""); + if (slug.isBlank()) { + slug = "product-" + System.currentTimeMillis(); + } - // Ensure slug is unique by appending a numeric suffix if needed - String uniqueSlug = generateUniqueSlug(slug); - p.setSlug(uniqueSlug); + // Ensure slug is unique by appending a numeric suffix if needed + String uniqueSlug = generateUniqueSlug(slug); + p.setSlug(uniqueSlug); + } // ---------- DESCRIPTIONS ---------- p.setShortDescription(trimOrNull(row.shortDescription())); @@ -141,24 +184,19 @@ public class MerchantFeedImportServiceImpl implements MerchantFeedImportService p.setMpn(mpn); // Feed doesn’t give us UPC in the header you showed. - // We’ll leave UPC null for now. + // We’ll leave UPC null for now (or map later). p.setUpc(null); // ---------- PLATFORM ---------- - // For now, hard-code to AR-15 to satisfy not-null constraint. - // Later we can infer from row.category()/row.department(). String platform = inferPlatform(row); p.setPlatform(platform != null ? platform : "AR-15"); // ---------- PART ROLE ---------- - // We can do a tiny heuristic off category/subcategory. String partRole = inferPartRole(row); if (partRole == null || partRole.isBlank()) { partRole = "unknown"; } p.setPartRole(partRole); - - return productRepository.save(p); } // --- Helpers ---------------------------------------------------------- diff --git a/src/main/java/group/goforward/ballistic/repos/ProductRepository.java b/src/main/java/group/goforward/ballistic/repos/ProductRepository.java index f438159..82955a9 100644 --- a/src/main/java/group/goforward/ballistic/repos/ProductRepository.java +++ b/src/main/java/group/goforward/ballistic/repos/ProductRepository.java @@ -6,15 +6,15 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; import java.util.UUID; +import java.util.List; public interface ProductRepository extends JpaRepository { Optional findByUuid(UUID uuid); - Optional findByBrandAndMpn(Brand brand, String mpn); - - Optional findByBrandAndUpc(Brand brand, String upc); - boolean existsBySlug(String slug); + List findAllByBrandAndMpn(Brand brand, String mpn); + + List findAllByBrandAndUpc(Brand brand, String upc); } \ No newline at end of file