defmodule ExCatalog do @moduledoc """ Documentation for `ExCatalog`. """ @repo ExCatalog.Config.repo() alias ExCatalog.Category alias ExCatalog.Product alias ExCatalog.Manufacturer @doc """ List version. """ @version Mix.Project.config()[:version] def version, do: @version @doc """ List all the categories (the index). ## Examples iex> ExCatalog.index(25) """ def index(limit \\ 25, metadata \\ nil, cursor \\ nil, deleted \\ false) do import Ecto.Query import Ecto.SoftDelete.Query query = case(deleted) do false -> from(ExCatalog.Category, preload: [:parent_category], preload: [:image] ) true -> from(ExCatalog.Category, preload: [:parent_category], preload: [:image] ) |> with_undeleted end case cursor do :before -> @repo.paginate( query, before: metadata.before, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) :after -> @repo.paginate( query, after: metadata.after, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) _ -> @repo.paginate( query, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) end end @doc """ List all products with preloads and optional currency conversion.. ## Examples iex> ExCatalog.products(25) iex> ExCatalog.products(25,:USD) """ def products(limit \\ 25, currency \\ :USD, deleted \\ false, owner_id \\ nil) do products(limit, nil, nil, currency, deleted, owner_id) end def products(limit \\ 25, metadata, cursor, currency, deleted, owner_id) do import Ecto.Query import Ecto.SoftDelete.Query query = case(owner_id) do nil -> case(deleted) do false -> from(ExCatalog.Product, preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:images], preload: [:manufacturer], preload: [:videos] ) true -> from(ExCatalog.Product, preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:images], preload: [:manufacturer], preload: [:videos] ) |> with_undeleted end _ -> case(deleted) do false -> from(ExCatalog.Product, where: [owner_id: ^owner_id], preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) true -> from(ExCatalog.Product, where: [owner_id: ^owner_id], preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) |> with_undeleted end end reply = case cursor do :before -> @repo.paginate( query, before: metadata.before, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) :after -> @repo.paginate( query, after: metadata.after, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) _ -> @repo.paginate( query, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) end case currency do nil -> reply _ -> modified = Enum.map(reply.entries, fn x -> {:ok, price} = ExCatalog.Currencies.convert(x.price, currency) %{x | price: price} end) {modified, reply.metadata} end end @doc """ List product with preloads by sku and optional currency conversion. ## Examples iex> price = Money.new(:USD, 100) iex> product = %{sku: "12345", price: price, title: "test product", sub_title: "test product", description: "test product"} iex> ExCatalog.product(product.sku) """ def product(sku, currency \\ nil, deleted \\ false) do import Ecto.Query import Ecto.SoftDelete.Query query = case(deleted) do false -> from(ExCatalog.Product, where: [sku: ^sku], preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) true -> from(ExCatalog.Product, where: [sku: ^sku], preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) |> with_undeleted end reply = @repo.all(query) case List.first(reply) do nil -> reply record -> case currency do nil -> record currency -> {:ok, price} = ExCatalog.Currencies.convert(record.price, currency) %{record | price: price} end end end @doc """ List product with preloads by category and optional currency conversion. ## Examples iex> ExCatalog.products_by_category("test_category", 1, :USD, false, order_by: :sku) """ def products_by_category(slug, limit \\ 25, currency \\ :USD, deleted \\ false, opts \\ []) do products_by_category(slug, limit, nil, nil, currency, deleted, opts) end def products_by_category(slug, limit, metadata, cursor, currency, deleted, opts) do category = @repo.get_by(Category, slug: slug) case category do nil -> {:error, "Invalid Category"} _ -> import Ecto.Query import Ecto.SoftDelete.Query order = Keyword.get(opts, :order) || :desc by = Keyword.get(opts, :order_by) || :updated_at order_by = [{order, by}] origins = Keyword.get(opts, :origins) || [] query = case(deleted) do false -> from(p in ExCatalog.Product, where: p.category_id == ^category.id and p.origin not in ^origins, order_by: ^order_by, preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) true -> from(p in ExCatalog.Product, where: p.category_id == ^category.id and p.origin not in ^origins, preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) |> with_undeleted end reply = case cursor do :before -> @repo.paginate( query, before: metadata.before, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) :after -> @repo.paginate( query, after: metadata.after, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) _ -> @repo.paginate( query, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) end case currency do nil -> reply _ -> modified = Enum.map(reply.entries, fn x -> {:ok, price} = ExCatalog.Currencies.convert(x.price, currency) %{x | price: price} end) {modified, reply.metadata} end end end ### @doc """ List product with preloads by country and optional currency conversion. ## Examples iex> ExCatalog.products_by_country(["US"], 2, :USD, false, order_by: :sku) """ def products_by_country(origin, limit \\ 25, currency \\ :USD, deleted \\ false, opts \\ []) when is_list(origin) do products_by_country(origin, limit, nil, nil, currency, deleted, opts) end def products_by_country(origin, limit, metadata, cursor, currency, deleted, opts) do import Ecto.Query import Ecto.SoftDelete.Query order = Keyword.get(opts, :order) || :desc by = Keyword.get(opts, :order_by) || :updated_at order_by = [{order, by}] query = case(deleted) do false -> from(p in ExCatalog.Product, where: p.origin not in ^origin, order_by: ^order_by, preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) true -> from(p in ExCatalog.Product, where: p.origin not in ^origin, preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) |> with_undeleted end reply = case cursor do :before -> @repo.paginate( query, before: metadata.before, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) :after -> @repo.paginate( query, after: metadata.after, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) _ -> @repo.paginate( query, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) end case currency do nil -> reply _ -> modified = Enum.map(reply.entries, fn x -> {:ok, price} = ExCatalog.Currencies.convert(x.price, currency) %{x | price: price} end) {modified, reply.metadata} end end @doc """ List product with preloads by manufacturer and optional currency conversion. ## Examples iex> ExCatalog.products_by_manufacturer("test_manufacturer", 2, :USD, false, order_by: :sku) """ def products_by_manufacturer(slug, limit \\ 25, currency \\ :USD, deleted \\ false, opts \\ []) do products_by_manufacturer(slug, limit, nil, nil, currency, deleted, opts) end def products_by_manufacturer(slug, limit, metadata, cursor, currency, deleted, opts) do manufacturer = @repo.get_by(Manufacturer, slug: slug) case manufacturer do nil -> {:error, "Invalid Manufacturer"} _ -> import Ecto.Query import Ecto.SoftDelete.Query order = Keyword.get(opts, :order) || :desc by = Keyword.get(opts, :order_by) || :updated_at order_by = [{order, by}] query = case(deleted) do false -> from(ExCatalog.Product, where: [manufacturer: ^manufacturer], order_by: ^order_by, preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) true -> from(ExCatalog.Product, where: [manufacturer: ^manufacturer], preload: [:variations], preload: [:categories], preload: [:metas], preload: [:primary_image], preload: [:manufacturer], preload: [:images], preload: [:videos] ) |> with_undeleted end reply = case cursor do :before -> @repo.paginate( query, before: metadata.before, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) :after -> @repo.paginate( query, after: metadata.after, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) _ -> @repo.paginate( query, include_total_count: true, cursor_fields: [:inserted_at, :id], limit: limit ) end case currency do nil -> reply _ -> modified = Enum.map(reply.entries, fn x -> {:ok, price} = ExCatalog.Currencies.convert(x.price, currency) %{x | price: price} end) {modified, reply.metadata} end end end ### @doc """ Change the status active or disabled, this controls which products the user can see, (by default only the active products are displayed) ## Examples iex> id = Ecto.UUID.generate iex> ExCatalog.active(:category, id) iex> ExCatalog.active(:product, id) """ def active(type, id) do import Ecto.Query import Ecto.SoftDelete.Query query = case type do :category -> from(c in Category, where: c.id == ^id, select: c) |> with_undeleted _ -> from(p in Product, where: p.id == ^id, select: p) |> with_undeleted end case Keyword.has_key?(@repo.__info__(:functions), :soft_restore!) do true -> try do @repo.one!(query) |> @repo.soft_restore!() rescue _ -> {:error, "Not Available"} end false -> {:error, "Not Available"} end end def active(type, id, false) do import Ecto.Query case type do :category -> @repo.get_by!(Category, id: id) from(c in Category, where: c.id == ^id, select: c) |> @repo.soft_delete!() _ -> from(p in Product, where: p.id == ^id, select: p) |> @repo.soft_delete!() end end end