Issues with Decimal

what am I doing wrong here? Decimal doesn’t’ seem to be working!

schema "purchase_order" do
    embeds_many(:purchase_order_item, PurchaseOrderItem, on_replace: :delete)
    field(:po_no, :integer, read_after_writes: true)
    field(:tax, :float, read_after_writes: true)
    field(:total, :decimal, read_after_writes: true)
    field(:delivery_charges, :decimal, read_after_writes: true)
    field(:approved, :boolean, read_after_writes: true)
    field(:date_approved, :utc_datetime, read_after_writes: true)
    belongs_to(:distribution_centre, DistributionCentre)
    belongs_to(:supplier, Supplier)

    timestamps(type: :utc_datetime, read_after_writes: true)
  end

  @doc false
  def changeset(purchase_order, attrs) do
    purchase_order
    |> cast(attrs, [
      :distribution_centre_id,
      :supplier_id,
      :approved,
      :delivery_charges,
      :po_no,
      :tax,
      :total,
      :date_approved
    ])
    |> cast_embed(:purchase_order_item, required: true, with: &PurchaseOrderItem.changeset/2)
    |> set_total
    |> validate_inclusion(:po_no, generate_po_code())
    |> validate_required([:distribution_centre_id, :approved])
    |> unique_constraint(:po_no, name: :purchase_order_po_no_index)
    |> foreign_key_constraint(:distribution_centre_id)
    |> foreign_key_constraint(:supplier_id, name: :purchase_order_supplier_id_fkey)
  end

  defp set_total(changeset) do
    purchase_order_items = get_field(changeset, :purchase_order_item)

    total =
      Enum.reduce(purchase_order_items, Decimal.new(0), fn items, acc ->
        taxes = get_field(changeset, :tax) |> Decimal.from_float()
        delivery_charges = get_field(changeset, :delivery_charges)
        tax_and_delivery = Decimal.add(taxes, delivery_charges)
        Decimal.add(tax_and_delivery, Decimal.add(acc, items.total))
      end)

    changeset
    |> put_change(:total, total)
  end

  defp generate_po_code(length \\ 10) do
    code =
      10
      |> :math.pow(length)
      |> round()
      |> :rand.uniform()
      |> Integer.to_string()
      |> String.pad_leading(length, "0")

    {:ok, code}
  end

And this is the embedded schema.

embedded_schema do
    field(:item_name, :string, read_after_writes: true)
    field(:description, :string, read_after_writes: true)
    field(:quantity, :integer, read_after_writes: true)
    field(:unit_price, :decimal, read_after_writes: true)
    field(:total, :decimal, read_after_writes: true)
    field(:delete, :boolean, virtual: true)

    timestamps(type: :utc_datetime, read_after_writes: true)
  end

  @doc false
  def changeset(%PurchaseOrderItem{} = purchase_order_item, attrs) do
    purchase_order_item
    |> cast(attrs, [
      :item_name,
      :description,
      :total,
      :quantity,
      :unit_price,
      :delete
    ])
    |> set_delete
    |> set_total
    |> validate_required([:item_name, :description, :quantity, :unit_price])
  end

  defp set_total(changeset) do
    quantity = get_field(changeset, :quantity) |> Decimal.new()
    unit_price = get_field(changeset, :unit_price)
    quantity_and_price = Decimal.mult(quantity, unit_price)

    changeset
    |> put_change(:total, quantity_and_price)
  end

  defp set_delete(changeset) do
    if get_change(changeset, :delete) do
      %{changeset | action: :delete}
    else
      changeset
    end
  end

What is the incorrect behavior that you are seeing?

I am still trying to wrap my hands around Ecto as newbie Elixir Programmer. Thanks Axelson for your response I got it to work

1 Like

I’m glad that you got it to work!