add Printful mockup generator and post-sync angle enrichment

New PrintfulGenerator module creates demo products in Printful with
multi-variant support (multiple colours/sizes per product type).
Mix task gets --provider flag to choose between Printify and Printful.

After syncing Printful products, MockupEnricher Oban worker calls the
legacy mockup generator API to produce extra angle images (back, left,
right) and appends them as product images. Jobs are staggered 45s apart
with snooze-based 429 handling. Flat products (canvas, poster) get no
extras — apparel and 3D products get 1-5 extra angles each.

Also fixes:
- cross-provider slug uniqueness (appends -2, -3 suffix)
- category mapping order (Accessories before Canvas Prints)
- image dedup by URL instead of colour (fixes canvas variants)
- artwork URL stored in provider_data for enricher access

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-15 16:52:53 +00:00
parent 61cb2b7a87
commit 1aceaf9444
7 changed files with 916 additions and 76 deletions

View File

@@ -172,6 +172,31 @@ defmodule SimpleshopTheme.Clients.Printful do
get("/store/products/#{product_id}")
end
@doc """
Create a sync product with variants and design files.
## Example
create_sync_product(%{
sync_product: %{name: "My T-Shirt"},
sync_variants: [%{
variant_id: 4011,
retail_price: "29.99",
files: [%{url: "https://example.com/design.png", type: "default"}]
}]
})
"""
def create_sync_product(product_data) do
post("/store/products", product_data)
end
@doc """
Delete a sync product and all its variants.
"""
def delete_sync_product(product_id) do
delete("/store/products/#{product_id}")
end
# =============================================================================
# Shipping (v2)
# =============================================================================
@@ -242,6 +267,28 @@ defmodule SimpleshopTheme.Clients.Printful do
get(path)
end
# =============================================================================
# Mockup generator (legacy, multi-angle)
# =============================================================================
@doc """
Create a mockup generator task for a catalog product.
Returns `{:ok, %{"task_key" => "gt-...", "status" => "pending"}}`.
"""
def create_mockup_generator_task(catalog_product_id, body) do
post("/mockup-generator/create-task/#{catalog_product_id}", body)
end
@doc """
Poll a mockup generator task by task key.
Returns `{:ok, %{"status" => "completed", "mockups" => [...]}}` when done.
"""
def get_mockup_generator_task(task_key) do
get("/mockup-generator/task?task_key=#{task_key}")
end
# =============================================================================
# Files (v2)
# =============================================================================