Product schema for e-commerce shop.
Supports both physical and digital products with JSONB flexibility.
Fields
title- Product title (required)slug- URL-friendly identifier (unique)description- Short descriptionbody_html- Full rich text descriptionstatus- draft | active | archivedproduct_type- physical | digitalvendor- Brand/manufacturertags- JSONB array of tagsprice- Base price (required)compare_at_price- Original price for discountscost_per_item- Cost for profit calculationcurrency- ISO currency code (default: USD)taxable- Subject to taxweight_grams- Weight for shippingrequires_shipping- Needs physical deliverymade_to_order- Always available regardless of inventoryimages- JSONB array of image objectsfeatured_image- Main image URLseo_title- SEO titleseo_description- SEO descriptionfile_uuid- Storage file reference (digital products)download_limit- Max downloads (digital)download_expiry_days- Days until download expiresmetadata- JSONB for custom fields
Summary
Functions
Returns true if product is active.
Changeset for product creation and updates.
Returns true if product is digital.
Calculates discount percentage.
Returns the display price (compare_at_price if set, otherwise price).
Returns the list of localized field names.
Returns true if product has a discount (compare_at_price > price).
Returns true if product is physical.
Returns true if product requires shipping.
Slug generation for per-language slugs. Public because the AI translation adapter
(AITranslatable.slug_base/3) derives a slug from a translated title.
Types
@type t() :: %PhoenixKitEcommerce.Product{ __meta__: term(), body_html: term(), category: term(), category_uuid: term(), compare_at_price: term(), cost_per_item: term(), created_by_user: term(), created_by_uuid: term(), currency: term(), description: term(), download_expiry_days: term(), download_limit: term(), featured_image: term(), featured_image_uuid: term(), file_uuid: term(), image_uuids: term(), images: term(), inserted_at: term(), made_to_order: term(), metadata: term(), price: term(), product_type: term(), requires_shipping: term(), seo_description: term(), seo_title: term(), slug: term(), status: term(), tags: term(), taxable: term(), title: term(), updated_at: term(), uuid: term(), vendor: term(), weight_grams: term() }
Functions
Returns true if product is active.
Changeset for product creation and updates.
Returns true if product is digital.
Calculates discount percentage.
Returns the display price (compare_at_price if set, otherwise price).
Returns the list of localized field names.
Returns true if product has a discount (compare_at_price > price).
Returns true if product is physical.
Returns true if product requires shipping.
Slug generation for per-language slugs. Public because the AI translation adapter
(AITranslatable.slug_base/3) derives a slug from a translated title.
lang is the language the text is IN, and it changes the answer: German ö
expands to oe while Estonian ö folds to o, and Ukrainian Cyrillic follows
Ukraine's own romanization rather than Russian's. The reduce above had lang
bound and was discarding it, so every language got the neutral rule — a German
title slugged grosse where German orthography wants groesse.
Passing nil still produces a correct slug, just not locale-tuned.