Current section
7 Versions
Jump to
Current section
7 Versions
Compare versions
5
files changed
+286
additions
-226
deletions
| @@ -1,5 +1,11 @@ | |
| 1 1 | # ExCatalog |
| 2 2 | |
| 3 | + [](https://coveralls.io/github/mithereal/ex_catalog?branch=main) |
| 4 | +  |
| 5 | + [](https://hex.pm/packages/ex_catalog) |
| 6 | +  |
| 7 | +  |
| 8 | + |
| 3 9 | ** A General E-commerce Catalog System ** |
| 4 10 | |
| 5 11 | Think of this like an old school paper mail in catalog, we dont need all the fluff (extra db tables etc) as in in most ecommerce implementations, this is just a catalog, decoupled from inventory management with some options such as csv and pdf export, integer or binary primary key. |
| @@ -1,7 +1,9 @@ | |
| 1 | - {<<"app">>,<<"ex_catalog">>}. |
| 2 | - {<<"build_tools">>,[<<"mix">>]}. |
| 1 | + {<<"links">>,[{<<"GitHub">>,<<"https://github.com/mithereal/ex_catalog">>}]}. |
| 2 | + {<<"name">>,<<"ex_catalog">>}. |
| 3 | + {<<"version">>,<<"1.5.6">>}. |
| 3 4 | {<<"description">>,<<"E-commerce Catalog for Elixir.">>}. |
| 4 5 | {<<"elixir">>,<<"~> 1.13">>}. |
| 6 | + {<<"app">>,<<"ex_catalog">>}. |
| 5 7 | {<<"files">>, |
| 6 8 | [<<"lib">>,<<"lib/ex_catalog.ex">>,<<"lib/ex_catalog">>, |
| 7 9 | <<"lib/ex_catalog/tasks">>,<<"lib/ex_catalog/tasks/currency_reload.ex">>, |
| @@ -32,57 +34,55 @@ | |
| 32 34 | <<"lib/ex_catalog/template/category.ex">>, |
| 33 35 | <<"lib/ex_catalog/template/product.ex">>,<<"mix.exs">>,<<"README.md">>]}. |
| 34 36 | {<<"licenses">>,[<<"Apache-2.0">>]}. |
| 35 | - {<<"links">>,[{<<"GitHub">>,<<"https://github.com/mithereal/ex_catalog">>}]}. |
| 36 | - {<<"name">>,<<"ex_catalog">>}. |
| 37 37 | {<<"requirements">>, |
| 38 | - [[{<<"app">>,<<"postgrex">>}, |
| 39 | - {<<"name">>,<<"postgrex">>}, |
| 38 | + [[{<<"name">>,<<"postgrex">>}, |
| 39 | + {<<"app">>,<<"postgrex">>}, |
| 40 40 | {<<"optional">>,false}, |
| 41 | - {<<"repository">>,<<"hexpm">>}, |
| 42 | - {<<"requirement">>,<<">= 0.0.0">>}], |
| 43 | - [{<<"app">>,<<"ecto">>}, |
| 44 | - {<<"name">>,<<"ecto">>}, |
| 41 | + {<<"requirement">>,<<">= 0.0.0">>}, |
| 42 | + {<<"repository">>,<<"hexpm">>}], |
| 43 | + [{<<"name">>,<<"ecto">>}, |
| 44 | + {<<"app">>,<<"ecto">>}, |
| 45 45 | {<<"optional">>,false}, |
| 46 | - {<<"repository">>,<<"hexpm">>}, |
| 47 | - {<<"requirement">>,<<"~> 3.5">>}], |
| 48 | - [{<<"app">>,<<"ecto_sql">>}, |
| 49 | - {<<"name">>,<<"ecto_sql">>}, |
| 46 | + {<<"requirement">>,<<"~> 3.5">>}, |
| 47 | + {<<"repository">>,<<"hexpm">>}], |
| 48 | + [{<<"name">>,<<"ecto_sql">>}, |
| 49 | + {<<"app">>,<<"ecto_sql">>}, |
| 50 50 | {<<"optional">>,false}, |
| 51 | - {<<"repository">>,<<"hexpm">>}, |
| 52 | - {<<"requirement">>,<<"~> 3.5">>}], |
| 53 | - [{<<"app">>,<<"ecto_soft_delete">>}, |
| 54 | - {<<"name">>,<<"ecto_soft_delete">>}, |
| 51 | + {<<"requirement">>,<<"~> 3.5">>}, |
| 52 | + {<<"repository">>,<<"hexpm">>}], |
| 53 | + [{<<"name">>,<<"ecto_soft_delete">>}, |
| 54 | + {<<"app">>,<<"ecto_soft_delete">>}, |
| 55 55 | {<<"optional">>,false}, |
| 56 | - {<<"repository">>,<<"hexpm">>}, |
| 57 | - {<<"requirement">>,<<">= 0.0.0">>}], |
| 58 | - [{<<"app">>,<<"ex_money">>}, |
| 59 | - {<<"name">>,<<"ex_money">>}, |
| 56 | + {<<"requirement">>,<<">= 0.0.0">>}, |
| 57 | + {<<"repository">>,<<"hexpm">>}], |
| 58 | + [{<<"name">>,<<"ex_money">>}, |
| 59 | + {<<"app">>,<<"ex_money">>}, |
| 60 60 | {<<"optional">>,false}, |
| 61 | - {<<"repository">>,<<"hexpm">>}, |
| 62 | - {<<"requirement">>,<<"~> 5.12">>}], |
| 63 | - [{<<"app">>,<<"ex_money_sql">>}, |
| 64 | - {<<"name">>,<<"ex_money_sql">>}, |
| 61 | + {<<"requirement">>,<<">= 0.0.0">>}, |
| 62 | + {<<"repository">>,<<"hexpm">>}], |
| 63 | + [{<<"name">>,<<"ex_money_sql">>}, |
| 64 | + {<<"app">>,<<"ex_money_sql">>}, |
| 65 65 | {<<"optional">>,false}, |
| 66 | - {<<"repository">>,<<"hexpm">>}, |
| 67 | - {<<"requirement">>,<<"~> 1.8">>}], |
| 68 | - [{<<"app">>,<<"paginator">>}, |
| 69 | - {<<"name">>,<<"paginator">>}, |
| 66 | + {<<"requirement">>,<<">= 0.0.0">>}, |
| 67 | + {<<"repository">>,<<"hexpm">>}], |
| 68 | + [{<<"name">>,<<"paginator">>}, |
| 69 | + {<<"app">>,<<"paginator">>}, |
| 70 70 | {<<"optional">>,false}, |
| 71 | - {<<"repository">>,<<"hexpm">>}, |
| 72 | - {<<"requirement">>,<<"~> 1.2.0">>}], |
| 73 | - [{<<"app">>,<<"ecto_autoslug_field">>}, |
| 74 | - {<<"name">>,<<"ecto_autoslug_field">>}, |
| 71 | + {<<"requirement">>,<<"~> 1.2.0">>}, |
| 72 | + {<<"repository">>,<<"hexpm">>}], |
| 73 | + [{<<"name">>,<<"ecto_autoslug_field">>}, |
| 74 | + {<<"app">>,<<"ecto_autoslug_field">>}, |
| 75 75 | {<<"optional">>,false}, |
| 76 | - {<<"repository">>,<<"hexpm">>}, |
| 77 | - {<<"requirement">>,<<"~> 3.0">>}], |
| 78 | - [{<<"app">>,<<"csv">>}, |
| 79 | - {<<"name">>,<<"csv">>}, |
| 76 | + {<<"requirement">>,<<"~> 3.0">>}, |
| 77 | + {<<"repository">>,<<"hexpm">>}], |
| 78 | + [{<<"name">>,<<"csv">>}, |
| 79 | + {<<"app">>,<<"csv">>}, |
| 80 80 | {<<"optional">>,true}, |
| 81 | - {<<"repository">>,<<"hexpm">>}, |
| 82 | - {<<"requirement">>,<<"~> 3.0">>}], |
| 83 | - [{<<"app">>,<<"chromic_pdf">>}, |
| 84 | - {<<"name">>,<<"chromic_pdf">>}, |
| 81 | + {<<"requirement">>,<<"~> 3.0">>}, |
| 82 | + {<<"repository">>,<<"hexpm">>}], |
| 83 | + [{<<"name">>,<<"chromic_pdf">>}, |
| 84 | + {<<"app">>,<<"chromic_pdf">>}, |
| 85 85 | {<<"optional">>,true}, |
| 86 | - {<<"repository">>,<<"hexpm">>}, |
| 87 | - {<<"requirement">>,<<"~> 1.7">>}]]}. |
| 88 | - {<<"version">>,<<"1.5.5">>}. |
| 86 | + {<<"requirement">>,<<"~> 1.7">>}, |
| 87 | + {<<"repository">>,<<"hexpm">>}]]}. |
| 88 | + {<<"build_tools">>,[<<"mix">>]}. |
| @@ -10,12 +10,6 @@ defmodule ExCatalog do | |
| 10 10 | |
| 11 11 | @doc """ |
| 12 12 | List version. |
| 13 | - |
| 14 | - ## Examples |
| 15 | - |
| 16 | - iex> ExCatalog.version() |
| 17 | - |
| 18 | - |
| 19 13 | """ |
| 20 14 | @version Mix.Project.config()[:version] |
| 21 15 | def version, do: @version |
| @@ -107,6 +101,7 @@ defmodule ExCatalog do | |
| 107 101 | preload: [:metas], |
| 108 102 | preload: [:primary_image], |
| 109 103 | preload: [:images], |
| 104 | + preload: [:manufacturer], |
| 110 105 | preload: [:videos] |
| 111 106 | ) |
| 112 107 | |
| @@ -117,6 +112,7 @@ defmodule ExCatalog do | |
| 117 112 | preload: [:metas], |
| 118 113 | preload: [:primary_image], |
| 119 114 | preload: [:images], |
| 115 | + preload: [:manufacturer], |
| 120 116 | preload: [:videos] |
| 121 117 | ) |
| 122 118 | |> with_undeleted |
| @@ -131,6 +127,7 @@ defmodule ExCatalog do | |
| 131 127 | preload: [:categories], |
| 132 128 | preload: [:metas], |
| 133 129 | preload: [:primary_image], |
| 130 | + preload: [:manufacturer], |
| 134 131 | preload: [:images], |
| 135 132 | preload: [:videos] |
| 136 133 | ) |
| @@ -142,6 +139,7 @@ defmodule ExCatalog do | |
| 142 139 | preload: [:categories], |
| 143 140 | preload: [:metas], |
| 144 141 | preload: [:primary_image], |
| 142 | + preload: [:manufacturer], |
| 145 143 | preload: [:images], |
| 146 144 | preload: [:videos] |
| 147 145 | ) |
| @@ -198,7 +196,9 @@ defmodule ExCatalog do | |
| 198 196 | |
| 199 197 | ## Examples |
| 200 198 | |
| 201 | - iex> ExCatalog.product("2242", :USD) |
| 199 | + iex> price = Money.new(:USD, 100) |
| 200 | + iex> product = %{sku: "12345", price: price, title: "test product", sub_title: "test product", description: "test product"} |
| 201 | + iex> ExCatalog.product(product.sku) |
| 202 202 | |
| 203 203 | |
| 204 204 | """ |
| @@ -215,6 +215,7 @@ defmodule ExCatalog do | |
| 215 215 | preload: [:categories], |
| 216 216 | preload: [:metas], |
| 217 217 | preload: [:primary_image], |
| 218 | + preload: [:manufacturer], |
| 218 219 | preload: [:images], |
| 219 220 | preload: [:videos] |
| 220 221 | ) |
| @@ -226,21 +227,28 @@ defmodule ExCatalog do | |
| 226 227 | preload: [:categories], |
| 227 228 | preload: [:metas], |
| 228 229 | preload: [:primary_image], |
| 230 | + preload: [:manufacturer], |
| 229 231 | preload: [:images], |
| 230 232 | preload: [:videos] |
| 231 233 | ) |
| 232 234 | |> with_undeleted |
| 233 235 | end |
| 234 236 | |
| 235 | - [reply] = @repo.all(query) |
| 237 | + reply = @repo.all(query) |
| 236 238 | |
| 237 | - case currency do |
| 239 | + case List.first(reply) do |
| 238 240 | nil -> |
| 239 241 | reply |
| 240 242 | |
| 241 | - _ -> |
| 242 | - {:ok, price} = ExCatalog.Currencies.convert(reply.price, currency) |
| 243 | - %{reply | price: price} |
| 243 | + record -> |
| 244 | + case currency do |
| 245 | + nil -> |
| 246 | + record |
| 247 | + |
| 248 | + currency -> |
| 249 | + {:ok, price} = ExCatalog.Currencies.convert(record.price, currency) |
| 250 | + %{record | price: price} |
| 251 | + end |
| 244 252 | end |
| 245 253 | end |
| 246 254 | |
| @@ -248,8 +256,7 @@ defmodule ExCatalog do | |
| 248 256 | List product with preloads by category and optional currency conversion. |
| 249 257 | |
| 250 258 | ## Examples |
| 251 | - |
| 252 | - iex> ExCatalog.products_by_category("test_category", "2242", :USD, false, order_by: :sku) |
| 259 | + iex> ExCatalog.products_by_category("test_category", 1, :USD, false, order_by: :sku) |
| 253 260 | |
| 254 261 | |
| 255 262 | """ |
| @@ -258,103 +265,111 @@ defmodule ExCatalog do | |
| 258 265 | end |
| 259 266 | |
| 260 267 | def products_by_category(slug, limit, metadata, cursor, currency, deleted, opts) do |
| 261 | - category = @repo.get_by(%Category{}, slug: slug) |
| 268 | + category = @repo.get_by(Category, slug: slug) |
| 262 269 | |
| 263 | - import Ecto.Query |
| 264 | - import Ecto.SoftDelete.Query |
| 265 | - |
| 266 | - order = Keyword.get(opts, :order) || :desc |
| 267 | - by = Keyword.get(opts, :order_by) || :updated_at |
| 268 | - order_by = [{order, by}] |
| 269 | - origins = Keyword.get(opts, :origins) || [] |
| 270 | - |
| 271 | - query = |
| 272 | - case(deleted) do |
| 273 | - false -> |
| 274 | - from(p in ExCatalog.Product, |
| 275 | - where: p.category_id == ^category.id and p.origin not in ^origins, |
| 276 | - order_by: ^order_by, |
| 277 | - preload: [:variations], |
| 278 | - preload: [:categories], |
| 279 | - preload: [:metas], |
| 280 | - preload: [:primary_image], |
| 281 | - preload: [:images], |
| 282 | - preload: [:videos] |
| 283 | - ) |
| 284 | - |
| 285 | - true -> |
| 286 | - from(p in ExCatalog.Product, |
| 287 | - where: p.category_id == ^category.id and p.origin not in ^origins, |
| 288 | - preload: [:variations], |
| 289 | - preload: [:categories], |
| 290 | - preload: [:metas], |
| 291 | - preload: [:primary_image], |
| 292 | - preload: [:images], |
| 293 | - preload: [:videos] |
| 294 | - ) |
| 295 | - |> with_undeleted |
| 296 | - end |
| 297 | - |
| 298 | - reply = |
| 299 | - case cursor do |
| 300 | - :before -> |
| 301 | - @repo.paginate( |
| 302 | - query, |
| 303 | - before: metadata.before, |
| 304 | - include_total_count: true, |
| 305 | - cursor_fields: [:inserted_at, :id], |
| 306 | - limit: limit |
| 307 | - ) |
| 308 | - |
| 309 | - :after -> |
| 310 | - @repo.paginate( |
| 311 | - query, |
| 312 | - after: metadata.after, |
| 313 | - include_total_count: true, |
| 314 | - cursor_fields: [:inserted_at, :id], |
| 315 | - limit: limit |
| 316 | - ) |
| 317 | - |
| 318 | - _ -> |
| 319 | - @repo.paginate( |
| 320 | - query, |
| 321 | - include_total_count: true, |
| 322 | - cursor_fields: [:inserted_at, :id], |
| 323 | - limit: limit |
| 324 | - ) |
| 325 | - end |
| 326 | - |
| 327 | - case currency do |
| 270 | + case category do |
| 328 271 | nil -> |
| 329 | - reply |
| 272 | + {:error, "Invalid Category"} |
| 330 273 | |
| 331 274 | _ -> |
| 332 | - modified = |
| 333 | - Enum.map(reply.entries, fn x -> |
| 334 | - {:ok, price} = ExCatalog.Currencies.convert(x.price, currency) |
| 335 | - %{x | price: price} |
| 336 | - end) |
| 275 | + import Ecto.Query |
| 276 | + import Ecto.SoftDelete.Query |
| 337 277 | |
| 338 | - {modified, reply.metadata} |
| 278 | + order = Keyword.get(opts, :order) || :desc |
| 279 | + by = Keyword.get(opts, :order_by) || :updated_at |
| 280 | + order_by = [{order, by}] |
| 281 | + origins = Keyword.get(opts, :origins) || [] |
| 282 | + |
| 283 | + query = |
| 284 | + case(deleted) do |
| 285 | + false -> |
| 286 | + from(p in ExCatalog.Product, |
| 287 | + where: p.category_id == ^category.id and p.origin not in ^origins, |
| 288 | + order_by: ^order_by, |
| 289 | + preload: [:variations], |
| 290 | + preload: [:categories], |
| 291 | + preload: [:metas], |
| 292 | + preload: [:primary_image], |
| 293 | + preload: [:manufacturer], |
| 294 | + preload: [:images], |
| 295 | + preload: [:videos] |
| 296 | + ) |
| 297 | + |
| 298 | + true -> |
| 299 | + from(p in ExCatalog.Product, |
| 300 | + where: p.category_id == ^category.id and p.origin not in ^origins, |
| 301 | + preload: [:variations], |
| 302 | + preload: [:categories], |
| 303 | + preload: [:metas], |
| 304 | + preload: [:primary_image], |
| 305 | + preload: [:manufacturer], |
| 306 | + preload: [:images], |
| 307 | + preload: [:videos] |
| 308 | + ) |
| 309 | + |> with_undeleted |
| 310 | + end |
| 311 | + |
| 312 | + reply = |
| 313 | + case cursor do |
| 314 | + :before -> |
| 315 | + @repo.paginate( |
| 316 | + query, |
| 317 | + before: metadata.before, |
| 318 | + include_total_count: true, |
| 319 | + cursor_fields: [:inserted_at, :id], |
| 320 | + limit: limit |
| 321 | + ) |
| 322 | + |
| 323 | + :after -> |
| 324 | + @repo.paginate( |
| 325 | + query, |
| 326 | + after: metadata.after, |
| 327 | + include_total_count: true, |
| 328 | + cursor_fields: [:inserted_at, :id], |
| 329 | + limit: limit |
| 330 | + ) |
| 331 | + |
| 332 | + _ -> |
| 333 | + @repo.paginate( |
| 334 | + query, |
| 335 | + include_total_count: true, |
| 336 | + cursor_fields: [:inserted_at, :id], |
| 337 | + limit: limit |
| 338 | + ) |
| 339 | + end |
| 340 | + |
| 341 | + case currency do |
| 342 | + nil -> |
| 343 | + reply |
| 344 | + |
| 345 | + _ -> |
| 346 | + modified = |
| 347 | + Enum.map(reply.entries, fn x -> |
| 348 | + {:ok, price} = ExCatalog.Currencies.convert(x.price, currency) |
| 349 | + %{x | price: price} |
| 350 | + end) |
| 351 | + |
| 352 | + {modified, reply.metadata} |
| 353 | + end |
| 339 354 | end |
| 340 355 | end |
| 341 356 | |
| 342 357 | ### |
| 343 358 | @doc """ |
| 344 | - List product with preloads by category and optional currency conversion. |
| 359 | + List product with preloads by country and optional currency conversion. |
| 345 360 | |
| 346 361 | ## Examples |
| 347 362 | |
| 348 | - iex> ExCatalog.products_by_category("test_category", "2242", :USD, false, order_by: :sku) |
| 363 | + iex> ExCatalog.products_by_country(["US"], 2, :USD, false, order_by: :sku) |
| 349 364 | |
| 350 365 | |
| 351 366 | """ |
| 352 | - def products_by_country(slug, limit \\ 25, currency \\ :USD, deleted \\ false, opts \\ []) |
| 353 | - when is_list(slug) do |
| 354 | - products_by_country(slug, limit, nil, nil, currency, deleted, opts) |
| 367 | + def products_by_country(origin, limit \\ 25, currency \\ :USD, deleted \\ false, opts \\ []) |
| 368 | + when is_list(origin) do |
| 369 | + products_by_country(origin, limit, nil, nil, currency, deleted, opts) |
| 355 370 | end |
| 356 371 | |
| 357 | - def products_by_country(slug, limit, metadata, cursor, currency, deleted, opts) do |
| 372 | + def products_by_country(origin, limit, metadata, cursor, currency, deleted, opts) do |
| 358 373 | import Ecto.Query |
| 359 374 | import Ecto.SoftDelete.Query |
| 360 375 | |
| @@ -366,23 +381,25 @@ defmodule ExCatalog do | |
| 366 381 | case(deleted) do |
| 367 382 | false -> |
| 368 383 | from(p in ExCatalog.Product, |
| 369 | - where: p.slug not in ^slug, |
| 384 | + where: p.origin not in ^origin, |
| 370 385 | order_by: ^order_by, |
| 371 386 | preload: [:variations], |
| 372 387 | preload: [:categories], |
| 373 388 | preload: [:metas], |
| 374 389 | preload: [:primary_image], |
| 390 | + preload: [:manufacturer], |
| 375 391 | preload: [:images], |
| 376 392 | preload: [:videos] |
| 377 393 | ) |
| 378 394 | |
| 379 395 | true -> |
| 380 396 | from(p in ExCatalog.Product, |
| 381 | - where: p.slug not in ^slug, |
| 397 | + where: p.origin not in ^origin, |
| 382 398 | preload: [:variations], |
| 383 399 | preload: [:categories], |
| 384 400 | preload: [:metas], |
| 385 401 | preload: [:primary_image], |
| 402 | + preload: [:manufacturer], |
| 386 403 | preload: [:images], |
| 387 404 | preload: [:videos] |
| 388 405 | ) |
| @@ -434,96 +451,105 @@ defmodule ExCatalog do | |
| 434 451 | end |
| 435 452 | |
| 436 453 | @doc """ |
| 437 | - List product with preloads by category and optional currency conversion. |
| 454 | + List product with preloads by manufacturer and optional currency conversion. |
| 438 455 | |
| 439 456 | ## Examples |
| 440 457 | |
| 441 | - iex> ExCatalog.products_by_category("test_category", "2242", :USD, false, order_by: :sku) |
| 458 | + iex> ExCatalog.products_by_manufacturer("test_manufacturer", 2, :USD, false, order_by: :sku) |
| 442 459 | |
| 443 460 | |
| 444 461 | """ |
| 462 | + |
| 445 463 | def products_by_manufacturer(slug, limit \\ 25, currency \\ :USD, deleted \\ false, opts \\ []) do |
| 446 464 | products_by_manufacturer(slug, limit, nil, nil, currency, deleted, opts) |
| 447 465 | end |
| 448 466 | |
| 449 467 | def products_by_manufacturer(slug, limit, metadata, cursor, currency, deleted, opts) do |
| 450 | - manufacturer = @repo.get_by(%Manufacturer{}, slug: slug) |
| 468 | + manufacturer = @repo.get_by(Manufacturer, slug: slug) |
| 451 469 | |
| 452 | - import Ecto.Query |
| 453 | - import Ecto.SoftDelete.Query |
| 454 | - |
| 455 | - order = Keyword.get(opts, :order) || :desc |
| 456 | - by = Keyword.get(opts, :order_by) || :updated_at |
| 457 | - order_by = [{order, by}] |
| 458 | - |
| 459 | - query = |
| 460 | - case(deleted) do |
| 461 | - false -> |
| 462 | - from(ExCatalog.Product, |
| 463 | - where: [manufacturer: ^manufacturer], |
| 464 | - order_by: ^order_by, |
| 465 | - preload: [:variations], |
| 466 | - preload: [:categories], |
| 467 | - preload: [:metas], |
| 468 | - preload: [:primary_image], |
| 469 | - preload: [:images], |
| 470 | - preload: [:videos] |
| 471 | - ) |
| 472 | - |
| 473 | - true -> |
| 474 | - from(ExCatalog.Product, |
| 475 | - where: [origin: ^slug], |
| 476 | - preload: [:variations], |
| 477 | - preload: [:categories], |
| 478 | - preload: [:metas], |
| 479 | - preload: [:primary_image], |
| 480 | - preload: [:images], |
| 481 | - preload: [:videos] |
| 482 | - ) |
| 483 | - |> with_undeleted |
| 484 | - end |
| 485 | - |
| 486 | - reply = |
| 487 | - case cursor do |
| 488 | - :before -> |
| 489 | - @repo.paginate( |
| 490 | - query, |
| 491 | - before: metadata.before, |
| 492 | - include_total_count: true, |
| 493 | - cursor_fields: [:inserted_at, :id], |
| 494 | - limit: limit |
| 495 | - ) |
| 496 | - |
| 497 | - :after -> |
| 498 | - @repo.paginate( |
| 499 | - query, |
| 500 | - after: metadata.after, |
| 501 | - include_total_count: true, |
| 502 | - cursor_fields: [:inserted_at, :id], |
| 503 | - limit: limit |
| 504 | - ) |
| 505 | - |
| 506 | - _ -> |
| 507 | - @repo.paginate( |
| 508 | - query, |
| 509 | - include_total_count: true, |
| 510 | - cursor_fields: [:inserted_at, :id], |
| 511 | - limit: limit |
| 512 | - ) |
| 513 | - end |
| 514 | - |
| 515 | - case currency do |
| 470 | + case manufacturer do |
| 516 471 | nil -> |
| 517 | - reply |
| 472 | + {:error, "Invalid Manufacturer"} |
| 518 473 | |
| 519 474 | _ -> |
| 520 | - modified = |
| 521 | - Enum.map(reply.entries, fn x -> |
| 522 | - {:ok, price} = ExCatalog.Currencies.convert(x.price, currency) |
| 523 | - %{x | price: price} |
| 524 | - end) |
| 475 | + import Ecto.Query |
| 476 | + import Ecto.SoftDelete.Query |
| 525 477 | |
| 526 | - {modified, reply.metadata} |
| 478 | + order = Keyword.get(opts, :order) || :desc |
| 479 | + by = Keyword.get(opts, :order_by) || :updated_at |
| 480 | + order_by = [{order, by}] |
| 481 | + |
| 482 | + query = |
| 483 | + case(deleted) do |
| 484 | + false -> |
| 485 | + from(ExCatalog.Product, |
| 486 | + where: [manufacturer: ^manufacturer], |
| 487 | + order_by: ^order_by, |
| 488 | + preload: [:variations], |
| 489 | + preload: [:categories], |
| 490 | + preload: [:metas], |
| 491 | + preload: [:primary_image], |
| 492 | + preload: [:manufacturer], |
| 493 | + preload: [:images], |
| 494 | + preload: [:videos] |
| 495 | + ) |
| 496 | + |
| 497 | + true -> |
| 498 | + from(ExCatalog.Product, |
| 499 | + where: [manufacturer: ^manufacturer], |
| 500 | + preload: [:variations], |
| 501 | + preload: [:categories], |
| 502 | + preload: [:metas], |
| 503 | + preload: [:primary_image], |
| 504 | + preload: [:manufacturer], |
| 505 | + preload: [:images], |
| 506 | + preload: [:videos] |
| 507 | + ) |
| 508 | + |> with_undeleted |
| 509 | + end |
| 510 | + |
| 511 | + reply = |
| 512 | + case cursor do |
| 513 | + :before -> |
| 514 | + @repo.paginate( |
| 515 | + query, |
| 516 | + before: metadata.before, |
| 517 | + include_total_count: true, |
| 518 | + cursor_fields: [:inserted_at, :id], |
| 519 | + limit: limit |
| 520 | + ) |
| 521 | + |
| 522 | + :after -> |
| 523 | + @repo.paginate( |
| 524 | + query, |
| 525 | + after: metadata.after, |
| 526 | + include_total_count: true, |
| 527 | + cursor_fields: [:inserted_at, :id], |
| 528 | + limit: limit |
| 529 | + ) |
| 530 | + |
| 531 | + _ -> |
| 532 | + @repo.paginate( |
| 533 | + query, |
| 534 | + include_total_count: true, |
| 535 | + cursor_fields: [:inserted_at, :id], |
| 536 | + limit: limit |
| 537 | + ) |
| 538 | + end |
| 539 | + |
| 540 | + case currency do |
| 541 | + nil -> |
| 542 | + reply |
| 543 | + |
| 544 | + _ -> |
| 545 | + modified = |
| 546 | + Enum.map(reply.entries, fn x -> |
| 547 | + {:ok, price} = ExCatalog.Currencies.convert(x.price, currency) |
| 548 | + %{x | price: price} |
| 549 | + end) |
| 550 | + |
| 551 | + {modified, reply.metadata} |
| 552 | + end |
| 527 553 | end |
| 528 554 | end |
| 529 555 | |
| @@ -533,7 +559,7 @@ defmodule ExCatalog do | |
| 533 559 | |
| 534 560 | ## Examples |
| 535 561 | |
| 536 | - iex> id = "1111-2222-3333-4444" |
| 562 | + iex> id = Ecto.UUID.generate |
| 537 563 | iex> ExCatalog.active(:category, id) |
| 538 564 | iex> ExCatalog.active(:product, id) |
| 539 565 | |
| @@ -556,8 +582,12 @@ defmodule ExCatalog do | |
| 556 582 | |
| 557 583 | case Keyword.has_key?(@repo.__info__(:functions), :soft_restore!) do |
| 558 584 | true -> |
| 559 | - @repo.one!(query) |
| 560 | - |> @repo.soft_restore!() |
| 585 | + try do |
| 586 | + @repo.one!(query) |
| 587 | + |> @repo.soft_restore!() |
| 588 | + rescue |
| 589 | + _ -> {:error, "Not Available"} |
| 590 | + end |
| 561 591 | |
| 562 592 | false -> |
| 563 593 | {:error, "Not Available"} |
| @@ -566,7 +596,6 @@ defmodule ExCatalog do | |
| 566 596 | |
| 567 597 | def active(type, id, false) do |
| 568 598 | import Ecto.Query |
| 569 | - import Ecto.SoftDelete.Query |
| 570 599 | |
| 571 600 | case type do |
| 572 601 | :category -> |
| @@ -36,6 +36,7 @@ defmodule ExCatalog.Product do | |
| 36 36 | ) |
| 37 37 | |
| 38 38 | has_many(:images, ExCatalog.Image, on_replace: :delete) |
| 39 | + has_many(:videos, ExCatalog.Video, on_replace: :delete) |
| 39 40 | has_many(:metas, ExCatalog.Meta, on_replace: :delete) |
| 40 41 | |
| 41 42 | timestamps() |
| @@ -54,10 +55,11 @@ defmodule ExCatalog.Product do | |
| 54 55 | :description, |
| 55 56 | :primary_image_id, |
| 56 57 | :owner_id, |
| 57 | - :origin, |
| 58 | - :manufacturer |
| 58 | + :origin |
| 59 59 | ]) |
| 60 60 | |> validate_required([:sku, :price, :title, :sub_title, :description]) |
| 61 | + |> TitleSlug.maybe_generate_slug() |
| 62 | + |> TitleSlug.unique_constraint() |
| 61 63 | end |
| 62 64 | |
| 63 65 | @doc false |
| @@ -1,7 +1,7 @@ | |
| 1 1 | defmodule ExCatalog.MixProject do |
| 2 2 | use Mix.Project |
| 3 3 | |
| 4 | - @version "1.5.5" |
| 4 | + @version "1.5.6" |
| 5 5 | @source_url "https://github.com/mithereal/ExCatalog" |
| 6 6 | |
| 7 7 | def project do |
| @@ -9,13 +9,20 @@ defmodule ExCatalog.MixProject do | |
| 9 9 | app: :ex_catalog, |
| 10 10 | version: @version, |
| 11 11 | elixir: "~> 1.13", |
| 12 | - build_embedded: Mix.env() == :prod, |
| 13 12 | start_permanent: Mix.env() == :prod, |
| 13 | + test_coverage: [tool: ExCoveralls], |
| 14 | + preferred_cli_env: [ |
| 15 | + coveralls: :test, |
| 16 | + "coveralls.detail": :test, |
| 17 | + "coveralls.post": :test, |
| 18 | + "coveralls.html": :test |
| 19 | + ], |
| 14 20 | package: package(), |
| 15 21 | description: description(), |
| 16 22 | deps: deps(), |
| 17 23 | docs: docs(), |
| 18 | - aliases: aliases() |
| 24 | + aliases: aliases(), |
| 25 | + dialyzer: dialyzer() |
| 19 26 | ] |
| 20 27 | end |
| 21 28 | |
| @@ -45,6 +52,8 @@ defmodule ExCatalog.MixProject do | |
| 45 52 | defp docs do |
| 46 53 | [ |
| 47 54 | main: "ExCatalog", |
| 55 | + homepage_url: @source_url, |
| 56 | + source_ref: "v#{@version}", |
| 48 57 | source_url: @source_url, |
| 49 58 | extras: ["README.md"] |
| 50 59 | ] |
| @@ -59,12 +68,13 @@ defmodule ExCatalog.MixProject do | |
| 59 68 | {:ecto, "~> 3.5"}, |
| 60 69 | {:ecto_sql, "~> 3.5"}, |
| 61 70 | {:ecto_soft_delete, ">= 0.0.0"}, |
| 62 | - {:ex_money, "~> 5.12"}, |
| 63 | - {:ex_money_sql, "~> 1.8"}, |
| 71 | + {:ex_money, ">= 0.0.0"}, |
| 72 | + {:ex_money_sql, ">= 0.0.0"}, |
| 64 73 | {:paginator, "~> 1.2.0"}, |
| 65 74 | {:ecto_autoslug_field, "~> 3.0"}, |
| 66 75 | {:csv, "~> 3.0", optional: true}, |
| 67 | - {:chromic_pdf, "~> 1.7", optional: true} |
| 76 | + {:chromic_pdf, "~> 1.7", optional: true}, |
| 77 | + {:excoveralls, "~> 0.14", only: [:test, :dev]} |
| 68 78 | ] |
| 69 79 | end |
| 70 80 | |
| @@ -82,4 +92,17 @@ defmodule ExCatalog.MixProject do | |
| 82 92 | install: ["ExCatalog.install", "ecto.setup"] |
| 83 93 | ] |
| 84 94 | end |
| 95 | + |
| 96 | + defp dialyzer() do |
| 97 | + [ |
| 98 | + plt_add_deps: :transitive, |
| 99 | + plt_add_apps: [:ex_unit, :mix], |
| 100 | + flags: [ |
| 101 | + :error_handling, |
| 102 | + :race_conditions, |
| 103 | + :underspecs, |
| 104 | + :unmatched_returns |
| 105 | + ] |
| 106 | + ] |
| 107 | + end |
| 85 108 | end |