/,/Add to my list/p' /tmp/product.html)"
],
"additionalDirectories": [
"G:\\development\\C Sharp AI\\CartWise"
diff --git a/README.md b/README.md
index e20f5ba..48b19cb 100644
--- a/README.md
+++ b/README.md
@@ -168,7 +168,15 @@ Progress so far (see `docs/scrum-backlog.md` for the authoritative task-level st
- [x] `CW-STORY-03.4` Toggle purchased/skip/delete with optimistic concurrency handling and jQuery progressive enhancement
- [x] `CW-STORY-03.5` Mobile-first/touch-friendly polish pass (not visually verified in a browser — no browser-automation tool available in this environment; recommend a quick manual check)
-Next up: `CW-EPIC-05` Stores, Purchases, and Price Intelligence (`CW-EPIC-04` is post-MVP).
+`CW-EPIC-04` Product Catalog and Barcode Resolution is complete — built ahead of the MVP-tagged epics per `DEC-010` (explicit founder direction to work epics in strict numeric order rather than MVP-first):
+
+- [x] `CW-STORY-04.1` Brand/Product/ProductIdentifier/HouseholdProductPreference entities, EF configuration, migration
+- [x] `CW-STORY-04.2` Local product search and details pages
+- [x] `CW-STORY-04.3` Local-first barcode lookup flow with provider fallback (`IProductDataProvider`)
+- [x] `CW-STORY-04.4` Real `OpenFoodFactsProductDataProvider` (typed `HttpClient`, no API key required)
+- [x] `CW-STORY-04.5` Camera-based scan page (native `BarcodeDetector` API, manual-entry fallback) with add-to-list — verified live against the real Open Food Facts API
+
+Next up: `CW-EPIC-05` Stores, Purchases, and Price Intelligence.
Next up: `CW-EPIC-03` Smart Shopping List.
diff --git a/docs/scrum-backlog.md b/docs/scrum-backlog.md
index 415b192..0d42035 100644
--- a/docs/scrum-backlog.md
+++ b/docs/scrum-backlog.md
@@ -520,11 +520,19 @@ Two more real bugs found by tests (bringing the running total for `CW-EPIC-03` t
- User can add scanned product to the list or record a price
**Tasks**
-- [ ] Create `ScanController`
-- [ ] Build `Views/Scan/Index.cshtml`
-- [ ] Add camera permission flow
-- [ ] Add barcode JS integration
-- [ ] Add add-to-list and record-price actions
+- [x] Create `ScanController`
+- [x] Build `Views/Scan/Index.cshtml`
+- [x] Add camera permission flow
+- [x] Add barcode JS integration
+- [x] Add add-to-list and record-price actions
+
+**Status:** Done, with one deliberate partial: "record-price" isn't wired up because `CW-EPIC-05` (Purchases/Price Intelligence) hasn't been built yet — a direct consequence of `DEC-010`'s strict-numeric-order choice reaching `04.5` before `05` exists. "Add to list" is fully wired instead, which satisfies the acceptance criterion's "add ... to the list **or** record a purchase/price." Will revisit once `CW-EPIC-05`'s `PriceController` exists.
+
+Also added `GET /products/barcode/{barcode}` and `GET /api/products/barcode/{barcode}` to `ProductController` (per AGENTS.md §11 — these were specified but not yet built in `CW-STORY-04.2`/`04.3`, since neither of those stories' task lists mentioned a controller route; the scan page is what actually needed them). The JSON route is the "actual client-side need" AGENTS.md §11 requires before introducing an `/api` route — the scan page's JS calls it via `fetch`/`$.getJSON` to resolve a detected barcode without a full page reload.
+
+No barcode-scanning JS library is vendored — this project has no bundler/npm step (`wwwroot` is plain static files, matching the existing jQuery/Bootstrap vendoring pattern), and pulling in a third-party scanning library's browser build wasn't practical to do well without one. Instead, `wwwroot/js/pages/scan.js` uses the native browser `BarcodeDetector` API (Chrome/Edge/Android WebView) with graceful degradation: browsers lacking it get a clear "not supported" message and a manual barcode-entry text field instead, which also doubles as the non-JS fallback path per AGENTS.md §14.
+
+Verified live end-to-end with a **real** barcode against the **real** Open Food Facts API (not mocked): `GET /api/products/barcode/3017620422003` (Nutella) → provider fallback fires → product persists locally with the correct name/brand/identifier → `/products/{id}` renders it → "Add to my list" persists it onto the household's shopping list. Also confirmed a genuinely-unknown barcode correctly 404s (HTML route) / returns `{"found":false}` (JSON route) after the provider fallback misses too.
---
diff --git a/src/CartWise.Web/Controllers/ProductController.cs b/src/CartWise.Web/Controllers/ProductController.cs
index 6068101..8c4b121 100644
--- a/src/CartWise.Web/Controllers/ProductController.cs
+++ b/src/CartWise.Web/Controllers/ProductController.cs
@@ -60,4 +60,33 @@ public class ProductController : Controller
return View(viewModel);
}
+
+ [HttpGet("products/barcode/{barcode}")]
+ public async Task Barcode(string barcode)
+ {
+ var result = await _productService.ResolveBarcodeAsync(barcode);
+ if (!result.IsSuccess)
+ {
+ return NotFound(result.Error);
+ }
+
+ return RedirectToAction(nameof(Details), new { id = result.Value!.ProductId });
+ }
+
+ [HttpGet("api/products/barcode/{barcode}")]
+ public async Task BarcodeJson(string barcode)
+ {
+ var result = await _productService.ResolveBarcodeAsync(barcode);
+ if (!result.IsSuccess)
+ {
+ return Ok(new { found = false, error = result.Error });
+ }
+
+ return Ok(new
+ {
+ found = true,
+ productId = result.Value!.ProductId,
+ redirectUrl = Url.Action(nameof(Details), new { id = result.Value.ProductId })
+ });
+ }
}
diff --git a/src/CartWise.Web/Controllers/ScanController.cs b/src/CartWise.Web/Controllers/ScanController.cs
new file mode 100644
index 0000000..3b2ad1a
--- /dev/null
+++ b/src/CartWise.Web/Controllers/ScanController.cs
@@ -0,0 +1,12 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+
+namespace CartWise.Web.Controllers;
+
+[Authorize]
+[Route("scan")]
+public class ScanController : Controller
+{
+ [HttpGet("")]
+ public IActionResult Index() => View();
+}
diff --git a/src/CartWise.Web/Views/Product/Details.cshtml b/src/CartWise.Web/Views/Product/Details.cshtml
index 81bc92c..c4eb10e 100644
--- a/src/CartWise.Web/Views/Product/Details.cshtml
+++ b/src/CartWise.Web/Views/Product/Details.cshtml
@@ -29,4 +29,9 @@
}
-
Price history and household insights will appear here once purchase tracking is available.
+
+
+
Price history and recording a purchase will appear here once purchase tracking is available.
diff --git a/src/CartWise.Web/Views/Scan/Index.cshtml b/src/CartWise.Web/Views/Scan/Index.cshtml
new file mode 100644
index 0000000..356fd94
--- /dev/null
+++ b/src/CartWise.Web/Views/Scan/Index.cshtml
@@ -0,0 +1,31 @@
+@{
+ ViewData["Title"] = "Scan a barcode";
+}
+
+
Scan a barcode
+
+
+ Live camera scanning isn't supported in this browser. Enter the barcode manually below.
+