Git Product home page Git Product logo

money_sql's Issues

Add keyword list to error return from money_with_currency cast/1

I have a problem with the parsing error messages being of variable and potentially sizeable length (the error messages are citing the input value). Also, I have a problem when the user starts typing the amount by first stating the currency so for as long as there is no number next to it, the error shows: Amount cannot be converted to a number: "".

I need to replace the former with a generic, fixed length message such as "Invalid amount", and the latter with no error message at all. As I now see it, the only way to do this is to intercept these in the LiveView generated MyApp.ErrorHelpers.error_tag/2 function, which is fine for me. But, the amount of information I get there is not enough. The details provided are the Money type tuple and the validation: :cast. as show below:

{"Amount cannot be converted to a number: """,
[type: {:parameterized, Money.Ecto.Composite.Type, []}, validation: :cast]}
Can you please also provide the error module atom itself so I can tell the errors one from another?

Could not read file error after upgrading to 1.10.0

Hi @kipcole9 ๐Ÿ‘‹๐Ÿผ
After upgrading to 1.10.0, I get the following error while trying to run the migration for min and max:

11:24:35.472 [info] == Running 20231102101154 Marko.Repo.Migrations.AddPostgresMoneyMinmaxFunctions.up/0 forward
** (File.Error) could not read file "priv/SQL/postgres/get_currency_code_type.sql": no such file or directory
    (elixir 1.15.5) lib/file.ex:358: File.read!/1
    (ex_money_sql 1.10.0) lib/money/migration.ex:19: Money.Migration.postgres_money_with_currency_type/1
    (ex_money_sql 1.10.0) lib/money/migration.ex:5: Money.Migration.adjust_for_type/2
    priv/repo/migrations/20231102101154_add_postgres_money_minmax_functions.exs:42: Marko.Repo.Migrations.AddPostgresMoneyMinmaxFunctions.up/0
    (ecto_sql 3.10.2) lib/ecto/migration/runner.ex:289: Ecto.Migration.Runner.perform_operation/3
    (stdlib 5.0.2) timer.erl:270: :timer.tc/2
    (ecto_sql 3.10.2) lib/ecto/migration/runner.ex:25: Ecto.Migration.Runner.run/8
    (ecto_sql 3.10.2) lib/ecto/migrator.ex:366: Ecto.Migrator.attempt/8

I saw a similar closed issue from some years ago. I think the issue is here:

query = File.read!("priv/SQL/postgres/get_currency_code_type.sql")

Maybe it should be using something like

defp read_sql_file(db_type, file_name) when db_type in @supported_db_types do
instead.

Getting `type/0` is undefined errors when upgrading

I just went to upgrade a few dependencies:

ecto 3.6.1 => 3.6.2
ex_money 5.5.1 => 5.5.3
ex_money_sql 1.3.1 => 1.4.4

And all my tests are failing w/ this:

     ** (UndefinedFunctionError) function Money.Ecto.Composite.Type.type/0 is undefined or private. Did you mean one of:

           * type/1

     stacktrace:
       (ex_money_sql 1.4.4) Money.Ecto.Composite.Type.type()
       (ecto 3.6.2) lib/ecto/type.ex:906: Ecto.Type.adapter_dump/3
       (elixir 1.12.0) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
       (elixir 1.12.0) lib/enum.ex:1675: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
       (elixir 1.12.0) lib/enum.ex:1675: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
       (elixir 1.12.0) lib/enum.ex:2356: Enum."-reduce/3-lists^foldl/2-0-"/3
       (ecto 3.6.2) lib/ecto/repo/queryable.ex:208: Ecto.Repo.Queryable.execute/4
       (ecto 3.6.2) lib/ecto/repo/queryable.ex:19: Ecto.Repo.Queryable.all/3

I remember seeing you get into a conversation w/ Jose about this, upstream, in ecto, but I can't seem to find that. I don't see any issues filed here so I wanted to check if I'm missing something..

Thanks, as always, Kip!

Broken/Unsupported CompositeType defaults

Having field defaults in a schema seems to be broken or unsupported.

  schema "my table" do
    field :value, Money.Ecto.Composite.Type, default: Money.new(:BRL, 0)
  end

Example:

     Assertion with == failed
     code:  assert bc_november.value == Money.new(:BRL, 0)
     left:  %Money{amount: #Decimal<0>, currency: :BRL, format_options: [{:default, #Money<:BRL, 0>}]}
     right: %Money{amount: #Decimal<0>, currency: :BRL, format_options: []}

Shouldn't we support https://hexdocs.pm/ecto/Ecto.Schema.html#field/3 options as well? default at least.
Mostly because even if the value isn't given, it defaults to something.

I'd more than happy to fix this. ๐Ÿ‘

Couldn't generate migration for custom type due to incorrect path

Hello,

First of all thanks for the package!

I have encountered an error when generating the migration using mix money.gen.postgres.migration. This is returned on my machine (Windows 10, Erlang/OTP 22, Elixir 1.8.2 compiled with Erlang/OTP 20):

> mix money.gen.postgres.migration
* creating priv/repo/migrations
** (File.Error) could not read file "c:/my_app/_build/dev/lib/ex_money/priv/SQL/postgres/create_money_with_currency.sql": no such file or directory
    (elixir) lib/file.ex:353: File.read!/1
    lib/mix/tasks/money_postgres_migration.ex:62: Mix.Tasks.Money.Gen.Postgres.Migration.migration_template/1
    lib/mix/tasks/money_postgres_migration.ex:39: anonymous fn/2 in Mix.Tasks.Money.Gen.Postgres.Migration.run/1
    (elixir) lib/enum.ex:769: Enum."-each/2-lists^foreach/1-0-"/2
    (elixir) lib/enum.ex:769: Enum.each/2
    (mix) lib/mix/task.ex:331: Mix.Task.run_task/3
    (mix) lib/mix/cli.ex:79: Mix.CLI.run_task/2
    (elixir) lib/code.ex:767: Code.require_file/2

I believe that the package could not find the file since it looked in ex_money instead of ex_money_sql, possibly following the package split, so I worked around it by copying priv/ from ex_money_sql to ex_money. I am very new to Elixir so I don't know how to fix it in code, but I hope this helps :)

Sum returns ERROR 42804 (datatype_mismatch)

๐Ÿ‘‹๐Ÿผ Hi there!
I'm following the steps in the docs for aggregating money values, but after following the steps, I get the following error:

** (Postgrex.Error) ERROR 42804 (datatype_mismatch) returned record type does not match expected record type

Returned type character varying does not match expected type character(3) in column 1.
    (ecto_sql 3.10.2) lib/ecto/adapters/sql.ex:1047: Ecto.Adapters.SQL.raise_sql_call_error/1
    (ecto_sql 3.10.2) lib/ecto/adapters/sql.ex:945: Ecto.Adapters.SQL.execute/6
    (ecto 3.10.3) lib/ecto/repo/queryable.ex:229: Ecto.Repo.Queryable.execute/4
    (ecto 3.10.3) lib/ecto/repo/queryable.ex:19: Ecto.Repo.Queryable.all/3
    (ecto 3.10.3) lib/ecto/repo/queryable.ex:151: Ecto.Repo.Queryable.one/3
    iex:5: (file)

Here's the migration for the schema part:

# ...

    create table(:products) do
      # ...
      add(:price, :money_with_currency, null: false)
    end

# ...

Here there query I'm using:

     query = from(p in Product, select: %{amount: type(sum(p.price), p.price)})

    Repo.one(query)

Which creates the following query:

SELECT sum(p0."price")::money_with_currency FROM "products" AS p0 []

Other than running the migration to create the specific sum functions for money_with_currency, is there anything else I might be missing? By the way, I'm using PostgreSQL 15.

Thanks in advance ๐Ÿ™Œ

Not compatible with Decimal 2.0

Hi there,

This library relies on Decimal.parse/1, which had a breaking change with the 2.0 release. Decimal is brought in as a transitive dependency from Money, which allows 2.0.0. This causes casting to crash if a project is updated to Decimal 2.0.

Possible bug in ecto? Asking here first for clarification

Hi there,

I just tried upgrading to ex_money_sql ~> 1.4 and I have compilation errors in my application now. I think what's going on is that the Ecto type function from the Query API doesn't support parameterized types, but I'm not quite sure... If this is an issue with ecto itself, I can open an issue with them as well.

I've created a minimal repo to demonstrate the issue: https://github.com/doughsay/ecto_money
The details are in the readme, but I'll duplicate them here for convenience.

# insert some moneys:

%EctoMoney.Money{amount: Money.new("100.00", :USD)} |> EctoMoney.Repo.insert()
%EctoMoney.Money{amount: Money.new("50.00", :USD)} |> EctoMoney.Repo.insert()
%EctoMoney.Money{amount: Money.new("75.00", :USD)} |> EctoMoney.Repo.insert()

# run a custom sum query and cast the response to a Money struct:

import Ecto.Query

query =
  from(
    money in "moneys",
    select: %{
      total: type(sum(money.amount), EctoMoneyType),
    }
  )

EctoMoney.Repo.all(query)

Expected:

This is the response you get when using ex_money_sql == 1.3.1

[%{total: #Money<:USD, 225.00>}]

Actual:

Using ex_money_sql ~> 1.4, with the new parameterized type, we get a
compilation error:

** (UndefinedFunctionError) function Money.Ecto.Composite.Type.type/0 is undefined or private. Did you mean one of:

      * type/1

    (ex_money_sql 1.4.2) Money.Ecto.Composite.Type.type()
    (ecto 3.5.7) lib/ecto/type.ex:421: Ecto.Type.match?/2
    (ecto 3.5.7) lib/ecto/query/builder.ex:646: Ecto.Query.Builder.assert_type!/3
    (ecto 3.5.7) lib/ecto/query/builder.ex:415: Ecto.Query.Builder.escape/5
    (ecto 3.5.7) lib/ecto/query/builder.ex:462: Ecto.Query.Builder.escape_with_type/5
    (ecto 3.5.7) lib/ecto/query/builder/select.ex:111: anonymous fn/4 in Ecto.Query.Builder.Select.escape_pairs/4
    (elixir 1.11.3) lib/enum.ex:1533: Enum."-map_reduce/3-lists^mapfoldl/2-0-"/3
    (ecto 3.5.7) lib/ecto/query/builder/select.ex:82: Ecto.Query.Builder.Select.escape/4

Workarounds

You can remove the casting, but then you get back a tuple instead of a Money
struct.

query =
  from(
    money in "moneys",
    select: %{
      total: sum(money.amount),
    }
  )

EctoMoney.Repo.all(query)

Results in:

[%{total: {"USD", #Decimal<225.00>}}]

Aggregate function treating null as zero amount

@sergiotapia has expressed interest in aggregating NULL values as zero-amount money.

The current aggregate function is STRICT meaning that NULL values are not processed. In some cases it is desirable to return a zero-amount money rather than NULL.

Changing the current function to remove STRICT may have unexpected results and therefore a new function that explicitly has this behaviour may be required.

Dependencie issue with ex_money 5.2

Hello,

I'm using both ex_money and ex_money_sql.
I set the current latest version for both as following:

#mix.exs
defp deps do
  [
    {:ex_money, "~> 5.2"},
    {:ex_money_sql, "~> 1.3"},
    ..
   ]
end

But since this package (ex_money_sql) pull ex_money 5.0 I'm getting an unchecked dependency error:

Unchecked dependencies for environment dev:
* ex_money (Hex package)
  the dependency does not match the requirement "~> 5.2", got "5.0.2"
** (Mix) Can't continue due to errors on dependencies

Is it just a version bumping issue or is ex_money_sql not compatible with the latest ex_money?

I'm interested to install the latest version of ex_money (or at least 5.1) since I'm getting parse errors for string without currencies.
I see you talked about this issue and its update here: https://elixirforum.com/t/ex-money-money-with-currency-type/20143/8

NB: I'll try to see if I can fix it on my side and will come back if I managed to do it..

Ecto types return malformed error when casting invalid currency

When casting an invalid currency, the both ecto types return an error tuple with another tuple as the second element.

{:error, {Cldr.UnknownCurrencyError, "The currency \"AAA\" is invalid"}}

This violates the typespec for Ecto.Type.cast: cast(term()) :: {:ok, term()} | {:error, keyword()} | :error, and causes an exception when used in Ecto.Changeset.cast

Please see this demo repo for a demonstration of the issue.

Incidentally, it appears that the load functions also violate the error typespec from Ecto.Type.

Cast function used to return error when currency is nil

Upgraded to version 1.6 and a condition is not handled in the money_sql code:

iex(1) > Money.Ecto.Composite.Type.cast(%{amount: "10", currency: nil})  
** (CondClauseError) no cond clause evaluated to a truthy value
    (digital_token 0.4.0) lib/digital_token.ex:142: DigitalToken.validate_token/1
    (ex_money 5.12.0) lib/money.ex:2280: Money.do_validate_currency/2
    (ex_money 5.12.0) lib/money.ex:211: Money.new/3
    (ex_money_sql 1.6.0) lib/money/ecto/money_ecto_composite_type.ex:96: Money.Ecto.Composite.Type.cast/2

Prior to 1.6, it was returning:

iex(1) > Money.Ecto.Composite.Type.cast(%{amount: "10", currency: nil})
{:error, [message: "The currency nil is invalid"]}

Add Ecto Changeset validations

@DaTrader, @LostKobrakai, In commit cd2547d I have added Money.Validation.validate_money/3 that mirrors the way Ecto.Changeset.validate_number/3 operates.

Comments and suggestions welcome. Here is the draft changelog entry:

Enhancements

  • Adds the module Money.Validation to provide Ecto Changeset validations. In particular it adds Money.Validation.validate_money/3 which behaves exactly like Ecto.Changeset.validate_number/3 only for t:Money types.

Money.Ecto.Map.Type.embed_as/2 should return :dump instead of :self

When using a Money.Ecto.Map.Type in an an embedded schema, the parameterized type is not loading the price correctly:

defmodule MyEmbed do
  embedded_schema do
    field(:price, Money.Ecto.Map.Type)
  end
end

Instead of loading the price through Money.Ecto.Map.Type, parsing the :amount with Decimal.parse/1, the field is loaded directly into a Money struct because the embed_as/2 callback implementation return :self.

While this appears to be ok when the locale is set to English, it is not the case and somehow dangerous with locales that use a comma instead of a dot for pricing format:

data = """
{
  "id": "431ab887-69a9-4fc6-85d4-3c0b83f8e2ed",
  "price": {
    "amount": "1.85",
    "currency": "CHF"
  }
}
"""

MyCldr.put_locale(:en)

Ecto.embedded_load(MyEmbed, Jason.decode!(data), :json)

returns CHF 1.85

%MyEmbed{
  id: "431ab887-69a9-4fc6-85d4-3c0b83f8e2ed",
  price: Money.new(:CHF, "1.85"),
}

but when changing the locale to German for example, things don't go as excepted:

MyCldr.put_locale(:de)

Ecto.embedded_load(MyEmbed, Jason.decode!(data), :json)

returns CHF 185

%MyEmbed{
  id: "431ab887-69a9-4fc6-85d4-3c0b83f8e2ed",
  price: Money.new(:CHF, "185"),
}

Changing the Money.Ecto.Map.Type.embed_as/2 to return :dump instead of :self fixes the issue and forces the data to be loaded through Money.Ecto.Map.Type.load/2.

Not sure if this is also the case with Money.Ecto.Composite.Type. But I assume this has the same issue and also need to return :dump instead of :self.

Unmatched clause for `Cldr.Decimal.parse/1`

Hi ๐Ÿ‘‹

Thanks for the great work on Money & CLDR!

I think there is a small bug in Money.Ecto.Composite.Type#cast/1: on L62 (https://github.com/kipcole9/money_sql/blob/master/lib/money/ecto/money_ecto_composite_type.ex#L62), the code calls Cldr.Decimal.parse/1 and the with clause matches on {amount, ""}, with else matching on {:error, {_, message}} or :error. However, when the amount cannot be parsed, Cldr.Decimal.parse/1 returns {:error, amount} (https://github.com/elixir-cldr/cldr_utils/blob/master/lib/cldr/decimal/decimal.ex#L34), which cannot match any of those.

iex(3)> Money.Ecto.Composite.Type.cast(%{"currency" => "USD", "amount" => "yes"})
** (WithClauseError) no with clause matching: {:error, "yes"}
    (ex_money_sql 1.3.1) lib/money/ecto/money_ecto_composite_type.ex:62: Money.Ecto.Composite.Type.cast/1

One option might be to change Money.Ecto.Composite.Type#cast/1 to match the correct return value, while the alternative could be to have Cldr.Decimal return the same :error atom that comes back from Decimal.parse/1 (https://github.com/ericmj/decimal/blob/v2.0.0/lib/decimal.ex#L1177) and which Money.Ecto.Composite.Type#cast/1 already expects to handle. I'll be happy to submit a PR in either case.

Composite type cast does not respect locales

iex(5)> Money.Ecto.Composite.Type.cast(%{currency: :NOK, amount: "218,75"})
** (WithClauseError) no with clause matching: {:error, "218,75"}
    (ex_money_sql 1.3.1) lib/money/ecto/money_ecto_composite_type.ex:62: Money.Ecto.Composite.Type.cast/1
iex(5)> Money.Ecto.Composite.Type.cast(%{currency: :NOK, amount: "218.75"})
{:ok, #Money<:NOK, 218.75>}
iex(6)> Money.new(:NOK, "218,72")
#Money<:NOK, 218.72>

The perpetrator is here:
https://github.com/kipcole9/money_sql/blob/master/lib/money/ecto/money_ecto_composite_type.ex#L52

The step trying to parse Decimal is unneccessary as Money does its own validation (which is also Locale aware).
So it will error out on invalid decimals (unknown strings or float source) and as such should not require this extra step with the with clause.

It's a pretty trivial change, do you just wanna plop the code change in and release new version or do you want me to send a PR?

`undefined function migrations_path/1` when attempting to compile

I ran in to #1, but before seeing the issue I tried to clean my deps, refetch and recompile them.

Now, running mix deps.compile yields

== Compilation error in file lib/mix/tasks/money_gen_aggregate_functions.ex ==
** (CompileError) lib/mix/tasks/money_gen_aggregate_functions.ex:30: undefined function migrations_path/1
    (elixir) src/elixir_locals.erl:107: :elixir_locals."-ensure_no_undefined_local/3-lc$^0/1-0-"/2
    (elixir) src/elixir_locals.erl:107: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
could not compile dependency :ex_money_sql, "mix compile" failed. You can recompile this dependency with "mix deps.compile ex_money_sql", update it with "mix deps.update ex_money_sql" or clean it with "mix deps.clean ex_money_sql"

I'm able to compile when only ex_money is listed as a dependency, but not once I add ex_money_sql.

I'm using {:ecto_sql, "~> 3.0"}.

If I don't include ex_money_sql (so I can compile), and poke around in iex it appears that among the conditional clauses in https://github.com/kipcole9/money_sql/blob/master/lib/money/migration.ex have the following values: line 4 true, line 10 false, line 16 true.

Let me know if I can provide any other relevant info.

===

If I use the master branch as the dependency, I get the same error. But after commenting lines 4 and 8 in https://github.com/kipcole9/money_sql/blob/master/lib/money/migration.ex, I get the following when recompiling:

==> ecto
Compiling 54 files (.ex)
Generated ecto app
==> ex_cldr_currencies
Compiling 2 files (.ex)
Generated ex_cldr_currencies app
==> ex_cldr_numbers
Compiling 4 files (.erl)
Compiling 24 files (.ex)
Generated ex_cldr_numbers app
==> ex_money
Compiling 20 files (.ex)
warning: function Ecto.Migrator.migrations_path/1 is undefined (module Ecto.Migrator is not available)
  lib/money/migration.ex:5

Generated ex_money app
==> postgrex
Compiling 61 files (.ex)
Generated postgrex app
==> ecto_sql
Compiling 25 files (.ex)
Generated ecto_sql app
==> ex_money_sql
Compiling 7 files (.ex)
warning: redefining module Money.Migration (current version loaded from /home/david/Documents/dev/coinage_guru/ingestion_engine/_build/dev/lib/ex_money/ebin/Elixir.Money.Migration.beam)
  lib/money/migration.ex:1

Sooo, using the master branch of BOTH ex_money and ex_money_sql (and using override: true in ex_money to override the version specified as the ex_money_sql dependency), I then run in to issue #1 (so I guess it's not fixed on master?).

Then, I can apply the work around indicate there:

cp -r my_project_dir/_build/dev/lib/ex_money_sql/priv/SQL/postgres/* my_project_dir/_build/dev/lib/ex_money/priv/SQL/postgres/

And THEN the migration works. So it seems that once the migration looks for the files in the right place (or they are moved to where they're expected), bumping version numbers on both packages should solve the issues.

function Money.Ecto.Composite.Type.type/0 is undefined or private when using Ecto.Query.Api.type/2

Versions

Elixir: 1.11.3 (compiled with Erlang/OTP 23)
Ecto: 3.5.8
Erlang: 23.2.5
ex_money_sql: 1.4.3
ex_money: 5.5.1

The issue

I'm running into an issue where I am selecting a column and casting it to a composite money type with the type/2 function from Ecto.Query.API. Example:

MyModel
|> select([m], %{money: type(m.money, Money.Ecto.Composite.Type)})

And I get this error:

** (UndefinedFunctionError) function Money.Ecto.Composite.Type.type/0 is undefined or private. Did you mean one of:

      * type/1

The Ecto docs still specify type/0 as a valid callback so it's unclear to me why this was removed from Money.Ecto.Composite.Type. Any help would be greatly appreciated.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.