Add Columbia Coatings API client, DTOs, and sync settings

Phase 1b of the Columbia Coatings integration: the typed read client and
its configuration, ahead of the sync/mapper service.

- ColumbiaProductDtos: wire-shape models for GET /products. tiered_pricing
  is captured as JsonElement because the API returns it as an object on
  simple products but an empty array on variable ones — binding it raw
  avoids a deserialization throw; the mapper interprets it.
- IColumbiaCoatingsApiClient / ColumbiaCoatingsApiClient: pages the catalog
  via GET /products (NOT the export download_url, which is Cloudflare-blocked
  for server clients). Sends X-API-Key from config, honors 429/Retry-After,
  and THROWS on any page failure so a partial pull can never be mistaken for
  the full catalog (protects the later discontinuation sweep).
- ColumbiaIntegrationConstants: single home for config keys, setting keys,
  and the derived Source/manufacturer/category values.
- Config: Columbia:ApiKey (blank — secret supplied per environment) and
  Columbia:BaseUrl in appsettings.
- SeedColumbiaSyncSettings migration: seeds SuperAdmin-managed platform
  settings ColumbiaSyncEnabled (off by default), ColumbiaSyncIntervalDays
  (7), and last-sync tracking, under a new "Integrations" group.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 10:47:00 -04:00
parent c98f9faf63
commit 39f61b9718
9 changed files with 11831 additions and 3 deletions
+1
View File
@@ -222,6 +222,7 @@ builder.Services.AddScoped<IInventoryAiLookupService, InventoryAiLookupService>(
builder.Services.AddScoped<ICustomFormulaAiService, CustomFormulaAiService>();
builder.Services.AddScoped<IFormulaLibraryService, FormulaLibraryService>();
builder.Services.AddHttpClient();
builder.Services.AddScoped<IColumbiaCoatingsApiClient, ColumbiaCoatingsApiClient>();
builder.Services.AddScoped<ICompanyLogoService, CompanyLogoService>();
builder.Services.AddScoped<IEquipmentManualService, EquipmentManualService>();
builder.Services.AddScoped<IPricingCalculationService, PricingCalculationService>();