wire Printful shipping rates into cart calculation
Add blueprint_id and print_provider_id to Printful provider_data so the generic shipping calculator can look up rates. Fix v2 API request format (order_items key) and response field names. Fetch one representative variant per product to get accurate per-item rates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3c788bff78
commit
0cfcb2448e
@ -187,7 +187,7 @@ defmodule SimpleshopTheme.Clients.Printful do
|
|||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
def calculate_shipping(recipient, items) do
|
def calculate_shipping(recipient, items) do
|
||||||
post("/v2/shipping-rates", %{recipient: recipient, items: items})
|
post("/v2/shipping-rates", %{recipient: recipient, order_items: items})
|
||||||
end
|
end
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
@ -168,19 +168,22 @@ defmodule SimpleshopTheme.Providers.Printful do
|
|||||||
|
|
||||||
with api_key when is_binary(api_key) <- ProviderConnection.get_api_key(conn),
|
with api_key when is_binary(api_key) <- ProviderConnection.get_api_key(conn),
|
||||||
:ok <- set_credentials(api_key, store_id) do
|
:ok <- set_credentials(api_key, store_id) do
|
||||||
variant_ids = extract_catalog_variant_ids(products)
|
# Build per-product items: one representative variant per product
|
||||||
|
product_items = extract_per_product_items(products)
|
||||||
|
|
||||||
Logger.info(
|
Logger.info(
|
||||||
"Fetching Printful shipping rates for #{length(variant_ids)} catalogue variants"
|
"Fetching Printful shipping rates for #{length(product_items)} product(s), " <>
|
||||||
|
"#{length(target_countries())} countries"
|
||||||
)
|
)
|
||||||
|
|
||||||
rates =
|
rates =
|
||||||
target_countries()
|
for {catalog_product_id, variant_id} <- product_items,
|
||||||
|> Enum.with_index()
|
{country_code, _index} <- Enum.with_index(target_countries()),
|
||||||
|> Enum.flat_map(fn {country_code, index} ->
|
reduce: [] do
|
||||||
if index > 0, do: Process.sleep(100)
|
acc ->
|
||||||
fetch_rates_for_country(variant_ids, country_code, products)
|
if acc != [], do: Process.sleep(100)
|
||||||
end)
|
acc ++ fetch_rate_for_product(catalog_product_id, variant_id, country_code)
|
||||||
|
end
|
||||||
|
|
||||||
{:ok, rates}
|
{:ok, rates}
|
||||||
else
|
else
|
||||||
@ -193,30 +196,23 @@ defmodule SimpleshopTheme.Providers.Printful do
|
|||||||
["GB", "US", "DE", "FR", "CA", "AU", "IE", "NL", "AT", "BE"]
|
["GB", "US", "DE", "FR", "CA", "AU", "IE", "NL", "AT", "BE"]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_rates_for_country(variant_ids, country_code, products) do
|
defp fetch_rate_for_product(catalog_product_id, variant_id, country_code) do
|
||||||
items =
|
items = [%{source: "catalog", catalog_variant_id: variant_id, quantity: 1}]
|
||||||
Enum.map(variant_ids, fn vid ->
|
|
||||||
%{source: "catalog", catalog_variant_id: vid, quantity: 1}
|
|
||||||
end)
|
|
||||||
|
|
||||||
case Client.calculate_shipping(%{country_code: country_code}, items) do
|
case Client.calculate_shipping(%{country_code: country_code}, items) do
|
||||||
{:ok, rates} when is_list(rates) ->
|
{:ok, rates} when is_list(rates) ->
|
||||||
# Take the STANDARD rate (cheapest)
|
standard = Enum.find(rates, &(&1["shipping"] == "STANDARD")) || List.first(rates)
|
||||||
standard = Enum.find(rates, &(&1["id"] == "STANDARD")) || List.first(rates)
|
|
||||||
|
|
||||||
if standard do
|
if standard do
|
||||||
catalog_product_id = extract_first_catalog_product_id(products)
|
|
||||||
rate_cents = parse_price(standard["rate"])
|
|
||||||
|
|
||||||
[
|
[
|
||||||
%{
|
%{
|
||||||
blueprint_id: catalog_product_id,
|
blueprint_id: catalog_product_id,
|
||||||
print_provider_id: 0,
|
print_provider_id: 0,
|
||||||
country_code: country_code,
|
country_code: country_code,
|
||||||
first_item_cost: rate_cents,
|
first_item_cost: parse_price(standard["rate"]),
|
||||||
additional_item_cost: 0,
|
additional_item_cost: 0,
|
||||||
currency: String.upcase(standard["currency"] || "USD"),
|
currency: String.upcase(standard["currency"] || "USD"),
|
||||||
handling_time_days: standard["maxDeliveryDays"]
|
handling_time_days: standard["max_delivery_days"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
else
|
else
|
||||||
@ -232,33 +228,24 @@ defmodule SimpleshopTheme.Providers.Printful do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp extract_catalog_variant_ids(products) do
|
# Returns {catalog_product_id, first_catalog_variant_id} per product
|
||||||
|
defp extract_per_product_items(products) do
|
||||||
products
|
products
|
||||||
|> Enum.flat_map(fn product ->
|
|> Enum.flat_map(fn product ->
|
||||||
provider_data = product[:provider_data] || %{}
|
provider_data = product[:provider_data] || %{}
|
||||||
|
|
||||||
|
catalog_product_id =
|
||||||
|
provider_data[:catalog_product_id] || provider_data["catalog_product_id"]
|
||||||
|
|
||||||
catalog_variant_ids =
|
catalog_variant_ids =
|
||||||
provider_data[:catalog_variant_ids] || provider_data["catalog_variant_ids"] || []
|
provider_data[:catalog_variant_ids] || provider_data["catalog_variant_ids"] || []
|
||||||
|
|
||||||
if catalog_variant_ids == [] do
|
case {catalog_product_id, catalog_variant_ids} do
|
||||||
# Fall back to extracting from raw data
|
{nil, _} -> []
|
||||||
raw = provider_data[:raw] || provider_data["raw"] || %{}
|
{_, []} -> []
|
||||||
sync_variants = raw["sync_variants"] || raw[:sync_variants] || []
|
{cpid, [vid | _]} -> [{cpid, vid}]
|
||||||
Enum.map(sync_variants, fn sv -> sv["variant_id"] || sv[:variant_id] end)
|
|
||||||
else
|
|
||||||
catalog_variant_ids
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> Enum.reject(&is_nil/1)
|
|
||||||
|> Enum.uniq()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp extract_first_catalog_product_id(products) do
|
|
||||||
products
|
|
||||||
|> Enum.find_value(fn product ->
|
|
||||||
provider_data = product[:provider_data] || %{}
|
|
||||||
provider_data[:catalog_product_id] || provider_data["catalog_product_id"]
|
|
||||||
end) || 0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@ -310,6 +297,9 @@ defmodule SimpleshopTheme.Providers.Printful do
|
|||||||
provider_data: %{
|
provider_data: %{
|
||||||
catalog_product_id: catalog_product_id,
|
catalog_product_id: catalog_product_id,
|
||||||
catalog_variant_ids: catalog_variant_ids,
|
catalog_variant_ids: catalog_variant_ids,
|
||||||
|
# Shipping calc uses these generic keys (shared with Printify)
|
||||||
|
blueprint_id: catalog_product_id,
|
||||||
|
print_provider_id: 0,
|
||||||
thumbnail_url: sync_product["thumbnail_url"],
|
thumbnail_url: sync_product["thumbnail_url"],
|
||||||
options: build_option_types(sync_variants),
|
options: build_option_types(sync_variants),
|
||||||
raw: %{sync_product: sync_product}
|
raw: %{sync_product: sync_product}
|
||||||
|
|||||||
@ -178,6 +178,8 @@ defmodule SimpleshopTheme.Providers.PrintfulTest do
|
|||||||
|
|
||||||
# Provider data
|
# Provider data
|
||||||
assert normalized.provider_data.catalog_product_id == 71
|
assert normalized.provider_data.catalog_product_id == 71
|
||||||
|
assert normalized.provider_data.blueprint_id == 71
|
||||||
|
assert normalized.provider_data.print_provider_id == 0
|
||||||
assert is_list(normalized.provider_data.options)
|
assert is_list(normalized.provider_data.options)
|
||||||
assert length(normalized.provider_data.catalog_variant_ids) == 4
|
assert length(normalized.provider_data.catalog_variant_ids) == 4
|
||||||
end
|
end
|
||||||
@ -357,6 +359,8 @@ defmodule SimpleshopTheme.Providers.PrintfulTest do
|
|||||||
provider_data: %{
|
provider_data: %{
|
||||||
catalog_product_id: catalog_product_id,
|
catalog_product_id: catalog_product_id,
|
||||||
catalog_variant_ids: catalog_variant_ids,
|
catalog_variant_ids: catalog_variant_ids,
|
||||||
|
blueprint_id: catalog_product_id,
|
||||||
|
print_provider_id: 0,
|
||||||
thumbnail_url: sync_product["thumbnail_url"],
|
thumbnail_url: sync_product["thumbnail_url"],
|
||||||
options: build_option_types(sync_variants),
|
options: build_option_types(sync_variants),
|
||||||
raw: %{sync_product: sync_product}
|
raw: %{sync_product: sync_product}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user