Current section
Files
Jump to
Current section
Files
lib/order/order.ex
defmodule Kandis.Order do
alias Kandis.Checkout
alias Kandis.Pdfgenerator
import Kandis.KdHelpers
require Ecto.Query
import HappyWith
@invoice_nr_prefix Application.get_env(:kandis, :invoice_nr_prefix)
@invoice_nr_testprefix Application.get_env(:kandis, :invoice_nr_testprefix)
@local_order Application.get_env(:kandis, :local_order)
@order_record Application.get_env(:kandis, :order_record)
@server_view Application.get_env(:kandis, :server_view)
@repo Application.get_env(:kandis, :repo)
@translation_function Application.get_env(:kandis, :translation_function)
@callback create_lineitem_from_cart_item(map(), map()) :: map()
@callback apply_delivery_cost(map(), map()) :: map()
def t(lang_or_context, translation_key, variables \\ []),
do: @translation_function.(lang_or_context, translation_key, variables)
def create_orderhtml(orderitems, ordervars, order_record \\ nil, mode \\ "order")
when is_map(orderitems) and is_map(ordervars) do
if function_exported?(@local_order, :create_orderhtml, 4) do
@local_order.create_orderhtml(orderitems, ordervars, order_record, mode)
else
Phoenix.View.render(@server_view, "orderhtml.html", %{
orderitems: orderitems,
ordervars: ordervars,
order: order_record,
lang: orderitems.lang,
mode: mode,
invoicemode: mode == "invoice"
})
end
end
def create_orderitems(ordercart, ordervars) when is_map(ordercart) and is_map(ordervars) do
%{
lineitems: [],
stats: %{},
lang: ordercart.lang,
cart_id: ordercart.cart_id,
ordercart: ordercart
}
|> add_lineitems_from_cart(ordercart, ordervars)
|> add_all_totals(ordervars)
end
def add_all_totals(orderitems, ordervars) when is_map(orderitems) and is_map(ordervars) do
orderitems
|> update_stats(ordervars)
|> add_product_subtotal(t(orderitems.lang, "order.subtotal"))
|> @local_order.apply_delivery_cost(ordervars)
|> update_stats(ordervars)
|> add_total(t(orderitems.lang, "order.total"))
|> add_total_taxes(ordervars)
end
def remove_all_deleted(orderitems) do
orderitems
|> update_in([:lineitems], fn lineitems ->
lineitems
|> Enum.filter(&(&1[:deleted] == nil))
end)
end
def remove_all_totals(orderitems) do
orderitems
|> update_in([:lineitems], fn lineitems ->
lineitems
|> Enum.filter(fn item -> !String.contains?("#{item[:type]}", ["total", "addon"]) end)
end)
end
def add_total_taxes(%{stats: stats} = orderitems, _ordervars) do
orderitems
|> update_in([:lineitems], fn lineitems ->
new_lineitems =
stats.taxrates
|> Map.to_list()
|> Enum.map(fn {taxrate, tax_stats} ->
%{
title: t(orderitems.lang, "order.incl_tax", taxrate: taxrate),
type: "total_tax",
total_price: tax_stats.tax
}
end)
lineitems ++ new_lineitems
end)
end
def add_product_subtotal(%{stats: stats} = orderitems, title) when is_binary(title) do
orderitems
|> update_in([:lineitems], fn lineitems ->
new_lineitem = %{
title: title,
type: "subtotal",
total_price: stats.total_price
}
lineitems ++ [new_lineitem]
end)
end
def add_total(%{stats: stats} = orderitems, title) when is_binary(title) do
orderitems
|> update_in([:lineitems], fn lineitems ->
new_lineitem = %{
title: title,
type: "total",
total_price: stats.total_price
}
lineitems = lineitems |> remove_subtotal_if_lastitem()
lineitems ++ [new_lineitem]
end)
end
def remove_subtotal_if_lastitem(lineitems) do
last_item = List.last(lineitems)
case last_item.type do
"subtotal" -> List.delete_at(lineitems, length(lineitems) - 1)
_ -> lineitems
end
end
def add_lineitems_from_cart(orderitems, %{items: cartitems} = _ordercart, ordervars)
when is_map(ordervars) do
orderitems
|> update_in([:lineitems], fn lineitems ->
new_lineitems =
cartitems
|> Enum.map(&@local_order.create_lineitem_from_cart_item(&1, ordervars))
|> Enum.filter(&Kandis.KdHelpers.present?/1)
lineitems ++ new_lineitems
end)
end
def update_stats(orderitems, _ordervars) do
orderitems
|> update_in([:stats], fn stats ->
stats
|> Map.merge(get_stats_for_lineitems(orderitems.lineitems))
end)
end
def get_stats_for_lineitems(lineitems) do
lineitems
# skip totals:
|> Enum.filter(fn a -> not String.contains?(a.type, "total") end)
|> Enum.reduce(
%{total_amount: 0, total_price: "0", total_product_price: "0", taxrates: %{}},
fn item, acc ->
acc
|> update_in(
[:total_amount],
&(&1 + (item[:amount] || 0))
)
|> update_in([:total_price], &Decimal.add(&1, item.total_price))
|> pipe_when(
item.type == "product",
update_in([:total_product_price], &Decimal.add(&1, item.total_price))
)
|> pipe_when(
item[:taxrate],
update_in([:taxrates], &update_taxrate_stats(&1, item))
)
end
)
end
def update_taxrate_stats(taxrates = %{}, %{taxrate: taxrate} = item) do
taxkey = "#{taxrate}"
taxrate_item =
taxrates[taxkey]
|> if_empty(%{tax: "0", net: "0", gross: "0"})
|> taxrate_item_append(create_taxrate_stats_entry_for_item(item))
taxrates
|> Map.put(taxkey, taxrate_item)
end
def update_taxrate_stats(taxes, _, _), do: taxes
def taxrate_item_append(map, new_item) when is_map(map) and is_map(new_item) do
new_item
|> Map.to_list()
|> Enum.reduce(map, fn {key, val}, acc ->
acc
|> update_in([key], &Decimal.add(&1, val))
end)
|> Map.new()
end
def update_ordervars(order_nr, nil) do
case get_by_order_nr(order_nr) do
order when is_map(order) -> {:ok, order}
_ -> {:error, "no order found"}
end
end
def update_ordervars(order_nr, new_ordervars)
when is_map(new_ordervars) and is_binary(order_nr) do
order = get_by_order_nr(order_nr)
if MavuUtils.present?(new_ordervars) do
updated_ordervars =
order.ordervars
|> Map.merge(new_ordervars)
store_archive_version(order.order_nr)
changes =
%{ordervars: updated_ordervars}
|> Map.merge(get_toplevel_order_fields(order.orderitems, updated_ordervars))
{:ok, _updated_order} =
order
|> Ecto.Changeset.change(changes)
|> Ecto.Changeset.change(%{version: order.version + 1})
|> @repo.update()
msg = "updated some order-variables"
payload = %{new_ordervars: new_ordervars}
# diff: generate_diff(order, updated_order)
store_history_entry(order.order_nr, payload, msg)
else
{:ok, order}
end
end
def update_orderitems(order_nr, nil) do
case get_by_order_nr(order_nr) do
order when is_map(order) -> {:ok, order}
_ -> {:error, "no order found"}
end
end
def update_orderitems(order_nr, new_orderitems)
when is_map(new_orderitems) and is_binary(order_nr) do
order = get_by_order_nr(order_nr)
if MavuUtils.present?(new_orderitems) do
updated_orderitems =
merge_orderitem_changes(order.orderitems, new_orderitems)
|> remove_all_deleted()
|> remove_all_totals()
|> add_all_totals(order.ordervars)
store_archive_version(order.order_nr)
changes =
%{orderitems: updated_orderitems}
|> Map.merge(get_toplevel_order_fields(updated_orderitems, order.ordervars))
{:ok, updated_order} =
order
|> Ecto.Changeset.change(changes)
|> Ecto.Changeset.change(%{version: order.version + 1})
|> @repo.update()
decrement_stock_for_order(updated_order)
msg = "updated some order-items"
payload = %{new_orderitems: new_orderitems}
# diff: generate_diff(order, updated_order)
store_history_entry(updated_order.order_nr, payload, msg)
else
{:ok, order}
end
end
def merge_orderitem_changes(orderitems, orderitem_changes)
when is_map(orderitems) and is_map(orderitem_changes) do
{orderitems, orderitem_changes}
|> IO.inspect(label: "mwuits-debug 2021-09-24_22:04 merge_orderitem_changes")
orderitem_changes
|> Map.to_list()
|> Enum.reduce(orderitems, fn {name, values}, acc ->
acc |> merge_orderitem_change(name, values)
end)
|> IO.inspect(label: "mwuits-debug 2021-09-24_22:04 CHANGED VALS")
end
def merge_orderitem_change(orderitems, name, new_values)
when is_map(orderitems) and is_atom(name) and is_map(new_values) do
case "#{name}" do
"orderitem_" <> idx_str ->
# incoming-index is 1-based:
idx = MavuUtils.to_int(idx_str) - 1
orderitems
|> update_in([:lineitems, Access.at(idx)], fn item -> Map.merge(item, new_values) end)
_ ->
# return unchanged version
orderitems
end
end
def generate_diff(order, updated_order) do
MapDiff.diff(order |> clean_for_diff(), updated_order |> clean_for_diff())
end
def clean_for_diff(order) when is_map(order) do
Map.from_struct(order)
|> Map.drop(~w(inserted_at updated_at archive history)a)
end
def store_history_entry(order_nr, payload, msg)
when is_map(payload) and is_binary(msg) and is_binary(order_nr) do
order = get_by_order_nr(order_nr)
history_record = create_history_record(order, payload, msg)
order
|> Ecto.Changeset.change(%{history: (order.history || []) ++ [history_record]})
|> @repo.update()
end
def store_archive_version(order_nr) when is_binary(order_nr) do
order = get_by_order_nr(order_nr)
version_record = create_version_record(order)
order
|> Ecto.Changeset.change(%{archive: (order.archive || []) ++ [version_record]})
|> @repo.update()
end
def erase_archive(order_nr) when is_binary(order_nr) do
order = get_by_order_nr(order_nr)
version_record = create_version_record(order)
order
|> Ecto.Changeset.change(%{archive: []})
|> @repo.update()
{:ok, version_record}
end
def restore_archive_version(order_nr, version)
when is_integer(version) and is_binary(order_nr) do
order = get_by_order_nr(order_nr)
version_record = get_version_record(order, version)
{:ok, updated_order} =
order
|> Ecto.Changeset.change(%{
version: order.version + 1,
ordervars: version_record.ordervars,
orderitems: version_record.orderitems
})
|> @repo.update()
decrement_stock_for_order(updated_order)
msg = "restored from version ##{version}"
payload = %{source_version: version, target_version: updated_order.version}
# diff: generate_diff(order, updated_order)
store_history_entry(updated_order.order_nr, payload, msg)
end
def erase_history(order_nr) when is_binary(order_nr) do
order = get_by_order_nr(order_nr)
version_record = create_version_record(order)
order
|> Ecto.Changeset.change(%{history: []})
|> @repo.update()
{:ok, version_record}
end
def create_version_record(order) when is_map(order) do
order
|> Map.take(~w(ordervars orderitems state version)a)
|> Map.put(:_ts, create_version_timestamp())
end
def get_version_record(order, version) when is_integer(version) and is_map(order) do
order.archive
|> Enum.find(&(&1["version"] == version))
|> case do
nil ->
nil
rec ->
%{
ordervars: rec["ordervars"] |> AtomicMap.convert(safe: true, ignore: true),
orderitems: rec["orderitems"] |> AtomicMap.convert(safe: true, ignore: true)
}
end
end
def create_history_record(order, payload, msg)
when is_map(payload) and is_binary(msg) and is_map(order) do
payload
|> Map.put(:_version, order.version)
|> Map.put(:_ts, create_version_timestamp())
|> Map.put(:_msg, msg)
end
def create_version_timestamp(),
do:
DateTime.utc_now()
|> DateTime.truncate(:second)
|> to_string()
|> String.replace_suffix("Z", "")
def create_taxrate_stats_entry_for_item(item) do
taxfactor = Decimal.div(item.taxrate, 100)
gross = item.total_price
net = Decimal.div(item.total_price, Decimal.add(taxfactor, 1))
tax = Decimal.sub(gross, net)
%{tax: tax, net: net, gross: gross}
end
def extract_shipping_address_fields(ordervars) when is_map(ordervars) do
ordervars
|> Map.to_list()
|> Enum.filter(&String.starts_with?(to_string(elem(&1, 0)), "shipping_"))
|> Enum.map(fn {key, val} ->
{String.trim_leading(to_string(key), "shipping_") |> String.to_existing_atom(), val}
end)
|> Map.new()
end
def atomize_maps(rec) when is_map(rec) do
rec
|> update_in([:orderitems], &AtomicMap.convert(&1, safe: true, ignore: true))
|> update_in([:ordervars], &AtomicMap.convert(&1, safe: true, ignore: true))
end
def atomize_maps(val), do: val
def get_by_id(id) when is_integer(id) do
@repo.get(@order_record, id)
|> atomize_maps()
end
def get_by_cart_id(cart_id) when is_binary(cart_id) do
@order_record
|> Ecto.Query.where([o], o.cart_id == ^cart_id)
|> Ecto.Query.where([o], o.state != "cancelled")
|> @repo.one()
|> atomize_maps()
end
def get_by_order_nr(order_nr) when is_binary(order_nr) do
@repo.get_by(@order_record, order_nr: order_nr)
|> atomize_maps()
end
def get_by_order_nr(nil), do: nil
def get_by_invoice_nr(invoice_nr) when is_binary(invoice_nr) do
@repo.get_by(@order_record, invoice_nr: invoice_nr)
|> atomize_maps()
end
def get_by_any_id(any_id) when is_binary(any_id) do
cond do
String.starts_with?(any_id, @invoice_nr_prefix) -> get_by_invoice_nr(any_id)
String.starts_with?(any_id, @invoice_nr_testprefix) -> get_by_invoice_nr(any_id)
true -> get_by_order_nr(any_id)
end
end
def get_by_any_id(any_id) when is_integer(any_id) do
get_by_id(any_id)
end
def get_by_any_id(%_{} = order) when is_struct(order) do
order
end
def create_new_order(orderitems, ordervars) do
@repo.transaction(fn ->
data = create_order_record_from_checkout(orderitems, ordervars)
struct(@order_record)
|> @order_record.changeset(data)
|> @repo.insert()
end)
|> case do
{:ok, {:ok, %_{} = order}} ->
order
_ ->
nil
end
end
def set_state(any_id, new_status) do
{any_id, new_status} |> Kandis.KdHelpers.log("attempt to set status of order ", :info)
get_by_any_id(any_id)
|> @order_record.changeset(%{state: new_status})
|> @repo.update()
|> case do
{:ok, order} ->
case new_status do
"cancelled" -> Task.start(fn -> update_stock(order) end)
_ -> nil
end
order
{:error, _err} ->
raise "cannot set state on #{any_id}"
end
end
def remove_invoice_nr(order) when is_map(order) do
{order.order_nr} |> Kandis.KdHelpers.log("attempt to remove_invoice_nr of order ", :info)
order
|> @order_record.changeset(%{invoice_nr: nil})
|> @repo.update()
end
def decrement_stock_for_order(%_{} = order) do
sku_amounts_from_items =
order.orderitems.lineitems
|> Enum.filter(&(&1.type == "product"))
|> Enum.map(&%{sku: &1.sku, amount: &1.amount})
Enum.map(sku_amounts_from_items, &decrement_stock_for_sku(&1.sku, &1.amount, order))
# set remaining stock items to 0 (e.g. after editing an order)
skus_from_stock = get_sku_amounts_in_order(order) |> Enum.map(& &1.sku)
skus_from_items = sku_amounts_from_items |> Enum.map(& &1.sku)
hidden_skus = skus_from_stock -- skus_from_items
Enum.map(hidden_skus, &decrement_stock_for_sku(&1, 0, order))
update_stock(order)
order
end
def get_sku_amounts_in_order(order) do
@local_order.get_sku_amounts_in_order(order)
end
def decrement_stock_for_sku(sku, amount, order) do
@local_order.decrement_stock_for_sku(sku, amount, order)
end
def update_stock(order) do
@local_order.update_stock(order)
end
def create_order_record_from_checkout(orderitems, ordervars)
when is_map(orderitems) and is_map(ordervars) do
%{
ordervars: ordervars,
orderitems: orderitems,
cart_id: orderitems.cart_id,
order_nr: create_new_order_nr(is_testorder?(orderitems, ordervars)),
state: "created",
user_id: ordervars[:user_id],
email: ordervars[:email],
payment_type: ordervars[:payment_type],
delivery_type: ordervars[:delivery_type],
shipping_country: Checkout.get_shipping_country(ordervars),
total_price: array_get(orderitems, [:stats, :total_price])
}
end
def get_toplevel_order_fields(orderitems, ordervars)
when is_map(orderitems) and is_map(ordervars) do
%{
email: ordervars[:email],
payment_type: ordervars[:payment_type],
delivery_type: ordervars[:delivery_type],
shipping_country: Checkout.get_shipping_country(ordervars),
total_price: to_dec(array_get(orderitems, [:stats, :total_price])),
tags: ordervars[:tags]
}
end
def is_testorder?(orderitems, _ordervars) do
# orders <=1 EUR are considered test-orders
Decimal.cmp(array_get(orderitems, [:stats, :total_price], "100"), "1.01") == :lt
end
def is_invoiced?(order) do
MavuUtils.present?(order[:invoice_nr])
end
def is_cancelled?(order) do
order[:state] == "cancelled"
end
def create_new_order_nr(is_testmode \\ false) do
nr =
if is_testmode do
get_order_nr_prefix() <> "-TEST-" <> get_random_code(4)
else
get_order_nr_prefix() <> "-" <> get_random_code(4)
end
if order_nr_taken?(nr) do
create_new_order_nr(is_testmode)
else
nr
end
end
def order_nr_taken?(order_nr) when is_binary(order_nr) do
case get_by_order_nr(order_nr) do
nil -> false
_ -> true
end
end
def get_random_code(length) do
Enum.shuffle(~w( A B C D E G H J K L M N P R S T U V X))
|> Enum.join("")
|> String.slice(1..length)
end
def get_order_nr_prefix() do
Date.utc_today()
|> Date.to_string()
|> String.slice(0..-4)
|> String.replace("-", "")
end
def update(data, _params \\ nil) do
case data do
%{"id" => id} -> get_by_id(id)
%{id: id} -> get_by_id(id)
%{"order_nr" => order_nr} -> get_by_order_nr(order_nr)
%{order_nr: order_nr} -> get_by_order_nr(order_nr)
end
|> @order_record.changeset(data)
|> @repo.insert_or_update()
end
def get_order_query(params) do
id = params["id"]
state = params["state"]
@order_record
|> pipe_when(id, Ecto.Query.where([o], o.id == ^id))
|> pipe_when(state, Ecto.Query.where([o], o.state == ^state))
|> Ecto.Query.order_by([o], desc: o.id)
end
def get_orders(params) do
get_order_query(params)
|> @repo.all()
end
def get_orderhtml(%_{} = order, mode \\ "order") do
create_orderhtml(order.orderitems, order.ordervars, order, mode)
end
def get_current_order_for_vid(vid, params \\ %{}) when is_binary(vid) and is_map(params) do
with _cart = %{cart_id: cart_id} <- Kandis.Cart.get_cart_record(vid),
%_{} = order <- get_by_cart_id(cart_id) do
order
else
_err -> get_by_order_nr(params["order_nr"])
end
|> Kandis.KdHelpers.log("get_current_order_for_vid(#{vid})", :info)
end
def get_latest_order_for_vid(vid, params \\ %{}) when is_binary(vid) and is_map(params) do
with _cart = %{cart_id: cart_id} <- Kandis.Cart.get_cart_record(vid),
%_{} = firstorder <- get_all_by_cart_id(cart_id) |> hd(),
%_{} = order <- firstorder |> atomize_maps do
order
else
_err -> get_by_order_nr(params["order_nr"])
end
end
def get_all_by_cart_id(cart_id) do
@order_record
|> Ecto.Query.where([o], o.cart_id == ^cart_id)
|> Ecto.Query.where([o], o.state != "cancelled")
|> Ecto.Query.order_by([o], desc: o.id)
|> @repo.all()
end
# invoice functions
def delete_latest_invoice(invoice_nr) when is_binary(invoice_nr) do
happy_with do
@get_latest_invoice_nr latest_invoice_nr <- get_latest_invoice_nr(get_invoice_nr_prefix())
@ensure_invoice_nr_is_latest a when a == true <- latest_invoice_nr == invoice_nr
@get_order order when is_map(order) <- get_by_invoice_nr(latest_invoice_nr)
@get_file filename when is_binary(filename) <- get_invoice_file(order.order_nr)
@remove_invoice_file :ok <- File.rm(filename)
@remove_invoice_nr_from_order {:ok, order} <- remove_invoice_nr(order)
{:ok, latest_invoice_nr, order}
else
{error_tag, error_context} ->
error_context |> MavuUtils.log(msg = "cannot '#{error_tag}'", :error)
{:error, msg, error_context}
end
end
def get_invoice_file(any_id, params \\ %{}) when is_binary(any_id) or is_integer(any_id),
do: get_order_file(any_id, "invoice", params)
def get_order_file(any_id, mode, params \\ %{})
def get_order_file(any_id, "invoice" = mode, params)
when is_binary(any_id) or is_integer(any_id) do
order = get_by_any_id(any_id)
case order do
%{invoice_nr: invoice_nr} when is_binary(invoice_nr) -> {:ok, invoice_nr}
%{order_nr: order_nr} -> create_and_assign_new_invoice_nr_for_order(order_nr)
end
Pdfgenerator.get_pdf_file_for_order_nr(order.order_nr, order.version, mode, params)
end
def get_order_file(any_id, mode, params) when is_binary(any_id) or is_integer(any_id) do
order = get_by_any_id(any_id)
Pdfgenerator.get_pdf_file_for_order_nr(order.order_nr, order.version, mode, params)
end
def get_invoice_url(any_id, params \\ %{}) when is_binary(any_id) or is_integer(any_id),
do: get_order_file_url(any_id, "invoice", params)
def get_order_file_url(any_id, mode, params \\ %{})
when is_binary(mode) and (is_binary(any_id) or is_integer(any_id)) do
get_order_file(any_id, mode, params)
|> Pdfgenerator.get_url_for_file()
end
def create_new_invoice_nr(prefix) do
get_latest_invoice_nr(prefix)
|> increment_invoice_nr(prefix)
end
def get_invoice_nr_prefix(:test), do: @invoice_nr_testprefix
def get_invoice_nr_prefix(:live), do: @invoice_nr_prefix
def get_invoice_nr_prefix(), do: get_invoice_nr_prefix(:live)
def increment_invoice_nr(invoice_nr, prefix) do
nr = invoice_nr |> String.trim_leading(prefix) |> to_int() |> if_nil(0)
nr = nr + 1
"#{prefix}#{nr}"
end
def order_nr_is_testmode?(order_nr) when is_binary(order_nr) do
String.contains?(order_nr, "-TEST-")
end
def create_and_assign_new_invoice_nr_for_order(order_nr, tries \\ 0)
def create_and_assign_new_invoice_nr_for_order(order_nr, tries) when tries > 100 do
{:error, "could not create new invoice nr for order #{order_nr} after #{tries} tries "}
end
def create_and_assign_new_invoice_nr_for_order(order_nr, tries)
when is_binary(order_nr) do
inv_prefix =
if order_nr_is_testmode?(order_nr) do
@invoice_nr_testprefix
else
@invoice_nr_prefix
end
new_invoice_nr = create_new_invoice_nr(inv_prefix)
%{order_nr: order_nr, invoice_nr: new_invoice_nr}
|> Kandis.Order.update()
|> case do
{:ok, record} ->
{:ok, record[:invoice_nr]}
{:error,
%Ecto.Changeset{errors: [invoice_nr: {_, [constraint: :unique, constraint_name: _]}]}} ->
# try_again
create_and_assign_new_invoice_nr_for_order(order_nr, tries + 1)
{:error, error} ->
{:error, error}
end
end
def finish_order(order_nr) when is_binary(order_nr) do
order = get_by_order_nr(order_nr)
if order.state == "w4payment" do
set_state(order_nr, "paid")
Checkout.reset_checkout(order)
end
order = get_by_order_nr(order_nr)
@local_order.finish_order(order)
end
def get_latest_invoice_nr(prefix) do
invlike = "#{prefix}%"
@order_record
|> Ecto.Query.where([o], like(o.invoice_nr, ^invlike))
|> @repo.aggregate(:max, :invoice_nr)
|> if_nil("#{prefix}#{10000}")
end
def cancel_orders_for_cart_id(cart_id) when is_binary(cart_id) do
@order_record
|> Ecto.Query.where([o], o.cart_id == ^cart_id)
|> Ecto.Query.where([o], o.state == "w4payment")
|> Ecto.Query.update(set: [state: "cancelled"])
|> @repo.update_all([])
end
def cancel_expired_orders(n \\ 100) do
get_expired_orders()
|> Enum.take(n)
|> Enum.map(fn order_nr -> set_state(order_nr, "cancelled") end)
end
def get_expired_orders() do
expire_minutes = 30
expire_time = DateTime.utc_now() |> DateTime.add(expire_minutes * -60, :second)
@order_record
|> Ecto.Query.where([r], r.state == "w4payment")
|> Ecto.Query.where([r], r.updated_at <= ^expire_time)
|> Ecto.Query.select([r], r.order_nr)
|> @repo.all()
end
def get_history_list(order) when is_map(order) do
([get_version_one_record(order)] ++
case order[:history] do
nil -> []
a -> a
end)
|> Enum.map(&AtomicMap.convert(&1, safe: true, ignore: true))
end
def get_version_one_record(order) do
%{
"_msg" => "order created",
"_ts" =>
order.inserted_at
|> NaiveDateTime.truncate(:second)
|> to_string()
|> String.replace_suffix("Z", ""),
"_version" => 1
}
end
end