Пример #1
0
 def setup_default(self, default, default_currency, nullable):
     if default is None and not nullable:
         # Backwards compatible fix for non-nullable fields
         default = 0.0
     if isinstance(default, string_types):
         try:
             # handle scenario where default is formatted like:
             # 'amount currency-code'
             amount, currency = default.split(' ')
         except ValueError:
             # value error would be risen if the default is
             # without the currency part, i.e
             # 'amount'
             amount = default
             currency = default_currency
         default = Money(Decimal(amount), Currency(code=currency))
     elif isinstance(default, (float, Decimal, int)):
         default = Money(default, default_currency)
     elif isinstance(default, OldMoney):
         default.__class__ = Money
     if not (nullable and default is None) and not isinstance(default, Money):
         raise ValueError('default value must be an instance of Money, is: %s' % default)
     return default
Пример #2
0
 def test_patching(self):
     ProxyModel.objects.create(money=Money("100.0", "USD"))
     # This will fail if ProxyModel.objects doesn't have the patched manager
     assert ProxyModel.objects.filter(
         money__gt=Money("50.00", "GBP")).count() == 0
Пример #3
0
class TestExpressions:
    @pytest.mark.skipif(VERSION[:2] < (2, 2),
                        reason="skipping tests for Django < 2.2")
    def test_bulk_update(self):
        assert ModelWithVanillaMoneyField.objects.filter(
            integer=0).count() == 0
        assert ModelWithVanillaMoneyField.objects.filter(
            integer=1).count() == 0
        ModelWithVanillaMoneyField.objects.create(money=Money(1, "USD"),
                                                  integer=0)
        ModelWithVanillaMoneyField.objects.create(money=Money(2, "USD"),
                                                  integer=1)
        first_inst = ModelWithVanillaMoneyField.objects.get(integer=0)
        second_inst = ModelWithVanillaMoneyField.objects.get(integer=1)
        assert first_inst.money == Money(1, "USD")
        assert second_inst.money == Money(2, "USD")
        first_inst.money = Money(3, "RUB")
        second_inst.money = Money(4, "UAH")
        second_inst.second_money = Money(5, "BYN")
        ModelWithVanillaMoneyField.objects.bulk_update(
            [first_inst, second_inst],
            ("money", "money_currency", "second_money",
             "second_money_currency"))
        assert ModelWithVanillaMoneyField.objects.get(
            integer=0).money == Money(3, "RUB")
        assert ModelWithVanillaMoneyField.objects.get(
            integer=1).money == Money(4, "UAH")
        assert ModelWithVanillaMoneyField.objects.get(
            integer=1).second_money == Money(5, "BYN")

    def test_conditional_update(self):
        ModelWithVanillaMoneyField.objects.bulk_create((
            ModelWithVanillaMoneyField(money=Money(1, "USD"), integer=0),
            ModelWithVanillaMoneyField(money=Money(2, "USD"), integer=1),
        ))
        ModelWithVanillaMoneyField.objects.update(
            money=Case(When(integer=0, then=Value(10)), default=Value(0)))
        assert ModelWithVanillaMoneyField.objects.get(
            integer=0).money == Money(10, "USD")
        assert ModelWithVanillaMoneyField.objects.get(
            integer=1).money == Money(0, "USD")

    def test_create_func(self):
        instance = ModelWithVanillaMoneyField.objects.create(
            money=Func(Value(-10), function="ABS"))
        instance.refresh_from_db()
        assert instance.money.amount == 10

    @pytest.mark.parametrize("value, expected",
                             ((None, None), (10, Money(10, "USD")),
                              (Money(10, "EUR"), Money(10, "EUR"))))
    def test_value_create(self, value, expected):
        instance = NullMoneyFieldModel.objects.create(field=Value(value))
        instance.refresh_from_db()
        assert instance.field == expected

    def test_value_create_invalid(self):
        with pytest.raises(ValidationError):
            ModelWithVanillaMoneyField.objects.create(money=Value("string"))

    def test_expressions_for_non_money_fields(self):
        instance = ModelWithVanillaMoneyField.objects.create(money=Money(
            1, "USD"),
                                                             integer=0)
        assert ModelWithVanillaMoneyField.objects.get(money=F("integer") +
                                                      1) == instance
        assert ModelWithVanillaMoneyField.objects.get(Q(money=F("integer") +
                                                        1)) == instance
Пример #4
0
 def test_objects_creation(self):
     SimpleModel.objects.create(money=Money("100.0", "USD"))
     assert SimpleModel.objects.count() == 1
Пример #5
0
 def test_query_not_null(self):
     money = Money(100, "EUR")
     ModelWithNullableCurrency.objects.create(money=money)
     instance = ModelWithNullableCurrency.objects.get()
     assert instance.money == money
Пример #6
0
class TestVanillaMoneyField:
    @pytest.mark.parametrize(
        "model_class, kwargs, expected",
        (
            (ModelWithVanillaMoneyField, {
                "money": Money("100.0")
            }, Money("100.0")),
            (ModelWithVanillaMoneyField, {
                "money": OldMoney("100.0")
            }, Money("100.0")),
            (BaseModel, {}, Money(0, "USD")),
            (BaseModel, {
                "money": "111.2"
            }, Money("111.2", "USD")),
            (BaseModel, {
                "money": Money("123", "PLN")
            }, Money("123", "PLN")),
            (BaseModel, {
                "money": OldMoney("123", "PLN")
            }, Money("123", "PLN")),
            (BaseModel, {
                "money": ("123", "PLN")
            }, Money("123", "PLN")),
            (BaseModel, {
                "money": (123.0, "PLN")
            }, Money("123", "PLN")),
            (ModelWithDefaultAsMoney, {}, Money("0.01", "RUB")),
            (ModelWithDefaultAsFloat, {}, Money("12.05", "PLN")),
            (ModelWithDefaultAsStringWithCurrency, {}, Money("123", "USD")),
            (ModelWithDefaultAsString, {}, Money("123", "PLN")),
            (ModelWithDefaultAsInt, {}, Money("123", "GHS")),
            (ModelWithDefaultAsDecimal, {}, Money("0.01", "CHF")),
            (CryptoModel, {
                "money": Money(10, "USDT")
            }, Money(10, "USDT")),
        ),
    )
    def test_create_defaults(self, model_class, kwargs, expected):
        instance = model_class.objects.create(**kwargs)
        assert instance.money == expected

        retrieved = model_class.objects.get(pk=instance.pk)
        assert retrieved.money == expected

    def test_old_money_defaults(self):
        instance = ModelWithDefaultAsOldMoney.objects.create()
        assert instance.money == Money(".01", "RUB")

    @pytest.mark.parametrize(
        "model_class, other_value",
        (
            (ModelWithVanillaMoneyField, Money("100.0")),
            (BaseModel, Money(0, "USD")),
            (ModelWithDefaultAsMoney, Money("0.01", "RUB")),
            (ModelWithDefaultAsFloat, OldMoney("12.05", "PLN")),
            (ModelWithDefaultAsFloat, Money("12.05", "PLN")),
        ),
    )
    def test_revert_to_default(self, model_class, other_value):
        if hasattr(model_class._meta, "get_field"):
            default_instance = model_class._meta.get_field(
                "money").get_default()
        else:
            default_instance = model_class._meta.get_field_by_name(
                "money").default
        instance1 = model_class.objects.create()
        pk = instance1.pk
        # Grab a fresh instance, change the currency to something non-default
        # and unexpected
        instance2 = model_class.objects.get(id=pk)
        instance2.money = Money(other_value.amount, "DKK")
        instance2.save()
        instance3 = model_class.objects.get(id=pk)
        assert instance3.money == Money(other_value.amount, "DKK")
        # Now change the field back to the default currency
        instance3.money = copy(default_instance)
        instance3.save()
        instance4 = model_class.objects.get(id=pk)
        assert instance4.money == default_instance

    @pytest.mark.parametrize("value",
                             ((1, "USD", "extra_string"), (1, None), (1, )))
    def test_invalid_values(self, value):
        with pytest.raises(ValidationError):
            BaseModel.objects.create(money=value)

    @pytest.mark.parametrize("money_class", (Money, OldMoney))
    @pytest.mark.parametrize("field_name", ("money", "second_money"))
    def test_save_new_value(self, field_name, money_class):
        ModelWithVanillaMoneyField.objects.create(
            **{field_name: money_class("100.0")})

        # Try setting the value directly
        retrieved = ModelWithVanillaMoneyField.objects.get()
        setattr(retrieved, field_name, Money(1, "DKK"))
        retrieved.save()
        retrieved = ModelWithVanillaMoneyField.objects.get()

        assert getattr(retrieved, field_name) == Money(1, "DKK")

    def test_rounding(self):
        money = Money("100.0623456781123219")

        instance = ModelWithVanillaMoneyField.objects.create(money=money)
        # TODO. Should instance.money be rounded too?

        retrieved = ModelWithVanillaMoneyField.objects.get(pk=instance.pk)

        assert retrieved.money == Money("100.06")

    @pytest.fixture(params=[Money, OldMoney])
    def objects_setup(self, request):
        Money = request.param
        ModelWithTwoMoneyFields.objects.bulk_create((
            ModelWithTwoMoneyFields(amount1=Money(1, "USD"),
                                    amount2=Money(2, "USD")),
            ModelWithTwoMoneyFields(amount1=Money(2, "USD"),
                                    amount2=Money(0, "USD")),
            ModelWithTwoMoneyFields(amount1=Money(3, "USD"),
                                    amount2=Money(0, "USD")),
            ModelWithTwoMoneyFields(amount1=Money(4, "USD"),
                                    amount2=Money(0, "GHS")),
            ModelWithTwoMoneyFields(amount1=Money(5, "USD"),
                                    amount2=Money(5, "USD")),
            ModelWithTwoMoneyFields(amount1=Money(5, "EUR"),
                                    amount2=Money(5, "USD")),
        ))

    @pytest.mark.parametrize(
        "filters, expected_count",
        (
            (Q(amount1=F("amount2")), 1),
            (Q(amount1__gt=F("amount2")), 2),
            (Q(amount1__in=(Money(1, "USD"), Money(5, "EUR"))), 2),
            (Q(id__in=(-1, -2)), 0),
            (Q(amount1=Money(1, "USD")) | Q(amount2=Money(0, "USD")), 3),
            (Q(amount1=Money(1, "USD")) | Q(amount1=Money(4, "USD"))
             | Q(amount2=Money(0, "GHS")), 2),
            (Q(amount1=OldMoney(1, "USD")) | Q(amount1=OldMoney(4, "USD"))
             | Q(amount2=OldMoney(0, "GHS")), 2),
            (Q(amount1=Money(1, "USD")) | Q(amount1=Money(5, "USD"))
             | Q(amount2=Money(0, "GHS")), 3),
            (Q(amount1=Money(1, "USD"))
             | Q(amount1=Money(4, "USD"), amount2=Money(0, "GHS")), 2),
            (Q(amount1=Money(1, "USD"))
             | Q(amount1__gt=Money(4, "USD"), amount2=Money(0, "GHS")), 1),
            (Q(amount1=Money(1, "USD"))
             | Q(amount1__gte=Money(4, "USD"), amount2=Money(0, "GHS")), 2),
        ),
    )
    @pytest.mark.usefixtures("objects_setup")
    def test_comparison_lookup(self, filters, expected_count):
        assert ModelWithTwoMoneyFields.objects.filter(
            filters).count() == expected_count

    def test_date_lookup(self):
        DateTimeModel.objects.create(field=Money(1, "USD"),
                                     created="2016-12-05")
        assert DateTimeModel.objects.filter(
            created__date="2016-12-01").count() == 0
        assert DateTimeModel.objects.filter(
            created__date="2016-12-05").count() == 1

    @pytest.mark.parametrize(
        "lookup, rhs, expected",
        (
            ("startswith", 2, 1),
            ("regex", "^[134]", 3),
            ("iregex", "^[134]", 3),
            ("istartswith", 2, 1),
            ("contains", 5, 2),
            ("lt", 5, 4),
            ("endswith", 5, 2),
            ("iendswith", 5, 2),
            ("gte", 4, 3),
            ("iexact", 3, 1),
            ("exact", 3, 1),
            ("isnull", True, 0),
            ("range", (3, 5), 4),
            ("lte", 2, 2),
            ("gt", 3, 3),
            ("icontains", 5, 2),
            ("in", (1, 0), 1),
        ),
    )
    @pytest.mark.usefixtures("objects_setup")
    def test_all_lookups(self, lookup, rhs, expected):
        kwargs = {"amount1__" + lookup: rhs}
        assert ModelWithTwoMoneyFields.objects.filter(
            **kwargs).count() == expected

    def test_exact_match(self):
        money = Money("100.0")

        instance = ModelWithVanillaMoneyField.objects.create(money=money)
        retrieved = ModelWithVanillaMoneyField.objects.get(money=money)

        assert instance.pk == retrieved.pk

    def test_issue_300_regression(self):
        date = datetime.datetime(year=2017, month=2, day=1)
        ModelIssue300.objects.filter(money__created=date)
        ModelIssue300.objects.filter(money__created__gt=date)

    def test_range_search(self):
        money = Money("3")

        instance = ModelWithVanillaMoneyField.objects.create(
            money=Money("100.0"))
        retrieved = ModelWithVanillaMoneyField.objects.get(money__gt=money)

        assert instance.pk == retrieved.pk

        assert ModelWithVanillaMoneyField.objects.filter(
            money__lt=money).count() == 0

    def test_filter_chaining(self):
        usd_instance = ModelWithVanillaMoneyField.objects.create(
            money=Money(100, "USD"))
        ModelWithVanillaMoneyField.objects.create(money=Money(100, "EUR"))
        query = ModelWithVanillaMoneyField.objects.filter().filter(
            money=Money(100, "USD"))
        assert usd_instance in query
        assert query.count() == 1

    @pytest.mark.parametrize(
        "model_class",
        (ModelWithVanillaMoneyField, ModelWithChoicesMoneyField))
    def test_currency_querying(self, model_class):
        model_class.objects.create(money=Money("100.0", "ZWN"))

        assert model_class.objects.filter(
            money__lt=Money("1000", "USD")).count() == 0
        assert model_class.objects.filter(
            money__lt=Money("1000", "ZWN")).count() == 1

    @pytest.mark.usefixtures("objects_setup")
    def test_in_lookup(self):
        assert ModelWithTwoMoneyFields.objects.filter(
            amount1__in=(Money(1, "USD"), Money(5, "EUR"))).count() == 2
        assert (ModelWithTwoMoneyFields.objects.filter(
            Q(amount1__lte=Money(2, "USD")),
            amount1__in=(Money(1, "USD"), Money(3, "USD"))).count() == 1)
        assert ModelWithTwoMoneyFields.objects.exclude(
            amount1__in=(Money(1, "USD"), Money(5, "EUR"))).count() == 4
        assert ModelWithTwoMoneyFields.objects.filter(
            amount1__in=(1, Money(5, "EUR"))).count() == 2
        assert ModelWithTwoMoneyFields.objects.filter(
            amount1__in=(1, 5)).count() == 3

    @pytest.mark.usefixtures("objects_setup")
    def test_in_lookup_f_expression(self):
        assert ModelWithTwoMoneyFields.objects.filter(
            amount1__in=(Money(4, "USD"), F("amount2"))).count() == 2

    def test_isnull_lookup(self):
        NullMoneyFieldModel.objects.create(field=None)
        NullMoneyFieldModel.objects.create(field=Money(100, "USD"))

        queryset = NullMoneyFieldModel.objects.filter(field=None)
        assert queryset.count() == 1

    def test_null_default(self):
        instance = NullMoneyFieldModel.objects.create()
        assert instance.field is None
Пример #7
0
class ModelWithValidation(models.Model):
    balance = MoneyField(max_digits=10,
                         decimal_places=2,
                         validators=[MinValueValidator(Money(100, "GBP"))])
Пример #8
0
 def test_shared_currency(self):
     instance, created = ModelWithSharedCurrency.objects.get_or_create(
         first=10, second=15, currency="USD")
     assert instance.first == Money(10, "USD")
     assert instance.second == Money(15, "USD")
Пример #9
0
 def test_create_with_money(self):
     value = Money(10, "USD")
     instance = ModelWithSharedCurrency.objects.create(first=value, second=value)
     assert instance.first == value
     assert instance.second == value
Пример #10
0
 def test_old_money_defaults(self):
     instance = ModelWithDefaultAsOldMoney.objects.create()
     assert instance.money == Money(".01", "RUB")
Пример #11
0
 def test_attributes(self, instance):
     assert instance.first == Money(10, "USD")
     assert instance.second == Money(15, "USD")
     assert instance.currency == "USD"
Пример #12
0
def test_override_decorator():
    """
    When current locale is changed, Money instances should be represented correctly.
    """
    with override("cs"):
        assert str(Money(10, "CZK")) == "10.00 Kč"
Пример #13
0
 def test_default_currency(self):
     klass = self.create_class(default_currency=None, default=Money(10, "EUR"), max_digits=10, decimal_places=2)
     assert str(klass._meta.fields[2].default_currency) == "EUR"
     instance = klass()
     assert instance.field == Money(10, "EUR")
Пример #14
0
def test_migration_serialization():
    serialized = "djmoney.money.Money(100, 'GBP')"
    assert MigrationWriter.serialize(Money(100, "GBP")) == (serialized, {"import djmoney.money"})
Пример #15
0
class ModelWithDefaultAsMoney(models.Model):
    money = MoneyField(default=Money("0.01", "RUB"),
                       max_digits=10,
                       decimal_places=2)
Пример #16
0
 def test_defaults(self):
     money = Money(10, "EUR")
     instance, _ = ModelWithVanillaMoneyField.objects.get_or_create(
         integer=1, defaults={"money": money})
     assert instance.money == money
Пример #17
0
class TestValidation:
    @pytest.mark.parametrize(
        "value, error",
        (
            (Money(50, "EUR"),
             u"Ensure this value is greater than or equal to 100.00 €."),
            (Money(1500, "EUR"),
             u"Ensure this value is less than or equal to 1,000.00 €."),
            (Money(40, "USD"),
             "Ensure this value is greater than or equal to $50.00."),
            (Money(600, "USD"),
             "Ensure this value is less than or equal to $500.00."),
            (Money(400, "NOK"),
             "Ensure this value is greater than or equal to 500.00 Nkr."),
            (Money(950, "NOK"),
             "Ensure this value is less than or equal to 900.00 Nkr."),
            (Money(5, "SEK"),
             "Ensure this value is greater than or equal to 10."),
            (Money(1600,
                   "SEK"), "Ensure this value is less than or equal to 1500."),
        ),
    )
    def test_invalid(self, value, error):
        form = ValidatedMoneyModelForm(data={
            "money_0": value.amount,
            "money_1": value.currency
        })
        assert not form.is_valid()
        assert form.errors == {"money": [error]}

    @pytest.mark.parametrize("value", (Money(150, "EUR"), Money(
        200, "USD"), Money(50, "SEK"), Money(600, "NOK")))
    def test_valid(self, value):
        assert ValidatedMoneyModelForm(data={
            "money_0": value.amount,
            "money_1": value.currency
        }).is_valid()

    @pytest.mark.parametrize("value", (Money(-0.01, "EUR"), Money(
        -1, "USD"), Money(-10, "NOK"), Money(-100, "SEK")))
    def test_non_negative_validator(self, value):
        """Fails if Validator(0) silently allows negative values."""
        form = PositiveValidatedMoneyModelForm(data={
            "money_0": value.amount,
            "money_1": value.currency
        })
        assert not form.is_valid()
        assert form.errors == {
            "money": ["Ensure this value is greater than or equal to 0."]
        }

    @pytest.mark.parametrize("value", (Money(0, "PHP"), Money(
        0.01, "EUR"), Money(1, "USD"), Money(10, "NOK"), Money(100, "SEK")))
    def test_positive_validator(self, value):
        """Fails if MinMoneyValidator(0) blocks positive values.

        MinMoneyValidator(0) should also allow exactly 0.
        """
        form = PositiveValidatedMoneyModelForm(data={
            "money_0": value.amount,
            "money_1": value.currency
        })
        assert form.is_valid()

    def test_default_django_validator(self):
        form = MoneyModelFormWithValidation(data={
            "balance_0": 0,
            "balance_1": "GBP"
        })
        assert not form.is_valid()
        assert form.errors == {
            "balance":
            [u"Ensure this value is greater than or equal to GB£100.00."]
        }
Пример #18
0
 def test_create_default(self):
     money = Money(100, "SEK")
     instance = ModelWithNullableCurrency.objects.create(money=money)
     assert instance.money == money
Пример #19
0
 def test_validation(self):
     instance = ModelWithVanillaMoneyField.objects.create(
         money=Money("42.00", "USD"))
     form = DisabledFieldForm(data={}, instance=instance)
     assert not form.errors
     assert form.is_valid()
Пример #20
0
class TestFExpressions:

    parametrize_f_objects = pytest.mark.parametrize(
        "f_obj, expected",
        (
            (F("money") + Money(100, "USD"), Money(200, "USD")),
            (F("money") + OldMoney(100, "USD"), Money(200, "USD")),
            (Money(100, "USD") + F("money"), Money(200, "USD")),
            (F("money") - Money(100, "USD"), Money(0, "USD")),
            (Money(100, "USD") - F("money"), Money(0, "USD")),
            (F("money") * 2, Money(200, "USD")),
            (F("money") * F("integer"), Money(200, "USD")),
            (Money(50, "USD") * F("integer"), Money(100, "USD")),
            (F("integer") * Money(50, "USD"), Money(100, "USD")),
            (Money(50, "USD") / F("integer"), Money(25, "USD")),
            (Money(51, "USD") % F("integer"), Money(1, "USD")),
            (F("money") / 2, Money(50, "USD")),
            (F("money") % 98, Money(2, "USD")),
            (F("money") / F("integer"), Money(50, "USD")),
            (F("money") + F("money"), Money(200, "USD")),
            (F("money") - F("money"), Money(0, "USD")),
        ),
    )

    @parametrize_f_objects
    def test_save(self, f_obj, expected):
        instance = ModelWithVanillaMoneyField.objects.create(money=Money(
            100, "USD"),
                                                             integer=2)
        instance.money = f_obj
        instance.save()
        instance.refresh_from_db()
        assert instance.money == expected

    @parametrize_f_objects
    def test_f_update(self, f_obj, expected):
        instance = ModelWithVanillaMoneyField.objects.create(money=Money(
            100, "USD"),
                                                             integer=2)
        ModelWithVanillaMoneyField.objects.update(money=f_obj)
        instance.refresh_from_db()
        assert instance.money == expected

    def test_default_update(self):
        instance = ModelWithVanillaMoneyField.objects.create(money=Money(
            100, "USD"),
                                                             integer=2)
        second_money = Money(100, "USD")
        ModelWithVanillaMoneyField.objects.update(second_money=second_money)
        instance.refresh_from_db()
        assert instance.second_money == second_money

    @pytest.mark.parametrize(
        "create_kwargs, filter_value, in_result",
        (
            ({
                "money": Money(100, "USD"),
                "second_money": Money(100, "USD")
            }, {
                "money": F("money")
            }, True),
            ({
                "money": Money(100, "USD"),
                "second_money": Money(100, "USD")
            }, {
                "money": F("second_money")
            }, True),
            ({
                "money": Money(100, "USD"),
                "second_money": Money(100, "EUR")
            }, {
                "money": F("second_money")
            }, False),
            ({
                "money": Money(50, "USD"),
                "second_money": Money(100, "USD")
            }, {
                "second_money": F("money") * 2
            }, True),
            (
                {
                    "money": Money(50, "USD"),
                    "second_money": Money(100, "USD")
                },
                {
                    "second_money": F("money") + Money(50, "USD")
                },
                True,
            ),
            ({
                "money": Money(50, "USD"),
                "second_money": Money(100, "EUR")
            }, {
                "second_money": F("money") * 2
            }, False),
            (
                {
                    "money": Money(50, "USD"),
                    "second_money": Money(100, "EUR")
                },
                {
                    "second_money": F("money") + Money(50, "USD")
                },
                False,
            ),
        ),
    )
    def test_filtration(self, create_kwargs, filter_value, in_result):
        instance = ModelWithVanillaMoneyField.objects.create(**create_kwargs)
        assert (instance in ModelWithVanillaMoneyField.objects.filter(
            **filter_value)) is in_result

    def test_update_fields_save(self):
        instance = ModelWithVanillaMoneyField.objects.create(money=Money(
            100, "USD"),
                                                             integer=2)
        instance.money = F("money") + Money(100, "USD")
        instance.save(update_fields=["money"])
        instance.refresh_from_db()
        assert instance.money == Money(200, "USD")

    INVALID_EXPRESSIONS = [
        F("money") + Money(100, "EUR"),
        F("money") * F("money"),
        F("money") / F("money"),
        F("money") % F("money"),
        F("money") + F("integer"),
        F("money") + F("second_money"),
        F("money")**F("money"),
        F("money")**F("integer"),
        F("money")**2,
    ]

    @pytest.mark.parametrize("f_obj", INVALID_EXPRESSIONS)
    def test_invalid_expressions_access(self, f_obj):
        instance = ModelWithVanillaMoneyField.objects.create(
            money=Money(100, "USD"))
        with pytest.raises(ValidationError):
            instance.money = f_obj
class TestMoneyField:
    def get_serializer(
        self, model_class, field_name=None, instance=None, data=fields.empty, fields_="__all__", field_kwargs=None
    ):
        class MetaSerializer(serializers.SerializerMetaclass):
            def __new__(cls, name, bases, attrs):
                from djmoney.contrib.django_rest_framework import MoneyField

                if field_name is not None and field_kwargs is not None:
                    attrs[field_name] = MoneyField(max_digits=10, decimal_places=2, **field_kwargs)
                return super().__new__(cls, name, bases, attrs)

        class Serializer(serializers.ModelSerializer, metaclass=MetaSerializer):
            class Meta:
                model = model_class
                fields = fields_

        return Serializer(instance=instance, data=data)

    @pytest.mark.parametrize(
        "model_class, create_kwargs, expected",
        (
            (NullMoneyFieldModel, {"field": None}, {"field": None, "field_currency": "USD"}),
            (NullMoneyFieldModel, {"field": Money(10, "USD")}, {"field": "10.00", "field_currency": "USD"}),
            (
                ModelWithVanillaMoneyField,
                {"money": Money(10, "USD")},
                {
                    "integer": 0,
                    "money": "10.00",
                    "money_currency": "USD",
                    "second_money": "0.00",
                    "second_money_currency": "EUR",
                },
            ),
        ),
    )
    def test_to_representation(self, model_class, create_kwargs, expected):
        instance = model_class.objects.create(**create_kwargs)
        expected["id"] = instance.id
        serializer = self.get_serializer(model_class, instance=instance)
        assert serializer.data == expected

    @pytest.mark.parametrize(
        "model_class, field, field_kwargs, value, expected",
        (
            (NullMoneyFieldModel, "field", None, None, None),
            (NullMoneyFieldModel, "field", {"default_currency": "EUR", "allow_null": True}, None, None),
            (NullMoneyFieldModel, "field", None, Money(10, "USD"), Money(10, "USD")),
            (NullMoneyFieldModel, "field", {"default_currency": "EUR"}, Money(10, "USD"), Money(10, "USD")),
            (NullMoneyFieldModel, "field", {"default_currency": "EUR"}, 10, Money(10, "EUR")),
            (ModelWithVanillaMoneyField, "money", None, Money(10, "USD"), Money(10, "USD")),
            (ModelWithVanillaMoneyField, "money", {"default_currency": "EUR"}, Money(10, "USD"), Money(10, "USD")),
            (ModelWithVanillaMoneyField, "money", None, 10, Money(10, "XYZ")),
            (ModelWithVanillaMoneyField, "money", {"default_currency": "EUR"}, 10, Money(10, "EUR")),
        ),
    )
    def test_to_internal_value(self, model_class, field, field_kwargs, value, expected):
        serializer = self.get_serializer(model_class, field_name=field, data={field: value}, field_kwargs=field_kwargs)
        assert serializer.is_valid()
        instance = serializer.save()
        assert getattr(instance, field) == expected

    def test_invalid_value(self):
        serializer = self.get_serializer(ModelWithVanillaMoneyField, data={"money": None})
        assert not serializer.is_valid()
        error_text = "This field may not be null."
        assert serializer.errors == {"money": [error_text]}

    @pytest.mark.parametrize(
        "body, field_kwargs, expected",
        (
            ({"field": "10", "field_currency": "EUR"}, None, Money(10, "EUR")),
            ({"field": "10"}, {"default_currency": "EUR"}, Money(10, "EUR")),
            ({"field": "12.20", "field_currency": "GBP"}, None, Money(12.20, "GBP")),
            ({"field": "15.15", "field_currency": "USD"}, None, Money(15.15, "USD")),
            ({"field": None, "field_currency": None}, None, None),
            ({"field": None, "field_currency": None}, {"default_currency": "EUR"}, None),
            ({"field": "16", "field_currency": None}, None, Decimal("16.00")),
            ({"field": "16", "field_currency": None}, {"default_currency": "EUR"}, Decimal("16.00")),
            ({"field": None, "field_currency": "USD"}, None, None),
            ({"field": None, "field_currency": "USD"}, {"default_currency": "EUR"}, None),
        ),
    )
    def test_post_put_values(self, body, field_kwargs, expected):
        if field_kwargs is not None:
            field_kwargs["allow_null"] = True
        serializer = self.get_serializer(NullMoneyFieldModel, data=body, field_name="field", field_kwargs=field_kwargs)
        serializer.is_valid()
        assert serializer.validated_data["field"] == expected

    def test_serializer_with_fields(self):
        serializer = self.get_serializer(ModelWithVanillaMoneyField, data={"money": "10.00"}, fields_=("money",))
        serializer.is_valid(True)
        assert serializer.data == {"money": "10.00"}

    @pytest.mark.parametrize(
        "value, error",
        (
            (Money(50, "EUR"), "Ensure this value is greater than or equal to €100.00."),
            (Money(1500, "EUR"), "Ensure this value is less than or equal to €1,000.00."),
            (Money(40, "USD"), "Ensure this value is greater than or equal to $50.00."),
            (Money(600, "USD"), "Ensure this value is less than or equal to $500.00."),
            (Money(400, "NOK"), "Ensure this value is greater than or equal to NOK500.00."),
            (Money(950, "NOK"), "Ensure this value is less than or equal to NOK900.00."),
            (Money(5, "SEK"), "Ensure this value is greater than or equal to 10."),
            (Money(1600, "SEK"), "Ensure this value is less than or equal to 1500."),
        ),
    )
    def test_model_validators(self, value, error):
        serializer = self.get_serializer(
            ValidatedMoneyModel, data={"money": value.amount, "money_currency": value.currency.code}
        )
        assert not serializer.is_valid()
        assert serializer.errors["money"][0] == error

    @pytest.mark.parametrize(
        "value, error",
        (
            (Money(50, "EUR"), "Ensure this value is greater than or equal to 100."),
            (Money(1500, "EUR"), "Ensure this value is less than or equal to 1000."),
        ),
    )
    def test_boundary_values(self, value, error):
        serializer = self.get_serializer(
            NullMoneyFieldModel,
            data={"field": value.amount, "field_currency": value.currency.code},
            field_name="field",
            field_kwargs={"min_value": 100, "max_value": 1000},
        )
        assert not serializer.is_valid()
        assert serializer.errors["field"][0] == error
Пример #22
0
 def test_invalid_expressions_access(self, f_obj):
     instance = ModelWithVanillaMoneyField.objects.create(
         money=Money(100, "USD"))
     with pytest.raises(ValidationError):
         instance.money = f_obj
Пример #23
0
 def objects_setup(self, request):
     Money = request.param
     ModelWithTwoMoneyFields.objects.bulk_create((
         ModelWithTwoMoneyFields(amount1=Money(1, "USD"),
                                 amount2=Money(2, "USD")),
         ModelWithTwoMoneyFields(amount1=Money(2, "USD"),
                                 amount2=Money(0, "USD")),
         ModelWithTwoMoneyFields(amount1=Money(3, "USD"),
                                 amount2=Money(0, "USD")),
         ModelWithTwoMoneyFields(amount1=Money(4, "USD"),
                                 amount2=Money(0, "GHS")),
         ModelWithTwoMoneyFields(amount1=Money(5, "USD"),
                                 amount2=Money(5, "USD")),
         ModelWithTwoMoneyFields(amount1=Money(5, "EUR"),
                                 amount2=Money(5, "USD")),
     ))
Пример #24
0
def test_allow_expression_nodes_without_money():
    """Allow querying on expression nodes that are not Money"""
    desc = "hundred"
    ModelWithNonMoneyField.objects.create(money=Money(100.0), desc=desc)
    instance = ModelWithNonMoneyField.objects.filter(desc=F("desc")).get()
    assert instance.desc == desc
Пример #25
0
 def test_in_lookup_f_expression(self):
     assert ModelWithTwoMoneyFields.objects.filter(
         amount1__in=(Money(4, "USD"), F("amount2"))).count() == 2
Пример #26
0
 def test_instances(self):
     ProxyModel.objects.create(money=Money("100.0", "USD"))
     assert isinstance(ProxyModel.objects.get(pk=1), ProxyModel)
Пример #27
0
    def test_isnull_lookup(self):
        NullMoneyFieldModel.objects.create(field=None)
        NullMoneyFieldModel.objects.create(field=Money(100, "USD"))

        queryset = NullMoneyFieldModel.objects.filter(field=None)
        assert queryset.count() == 1
Пример #28
0
 def test_sub_default(self):
     with pytest.raises(TypeError):
         Money(10, "EUR") - Money(1, "USD")
Пример #29
0
class TestGetOrCreate:
    @pytest.mark.parametrize(
        "model, field_name, kwargs, currency",
        (
            (ModelWithVanillaMoneyField, "money", {
                "money_currency": "PLN"
            }, "PLN"),
            (ModelWithVanillaMoneyField, "money", {
                "money": Money(0, "EUR")
            }, "EUR"),
            (ModelWithVanillaMoneyField, "money", {
                "money": OldMoney(0, "EUR")
            }, "EUR"),
            (ModelWithSharedCurrency, "first", {
                "first": 10,
                "second": 15,
                "currency": "CZK"
            }, "CZK"),
        ),
    )
    def test_get_or_create_respects_currency(self, model, field_name, kwargs,
                                             currency):
        instance, created = model.objects.get_or_create(**kwargs)
        field = getattr(instance, field_name)
        assert str(
            field.currency
        ) == currency, "currency should be taken into account in get_or_create"

    def test_get_or_create_respects_defaults(self):
        value = Money(10, "SEK")
        instance = ModelWithUniqueIdAndCurrency.objects.create(money=value)
        instance, created = ModelWithUniqueIdAndCurrency.objects.get_or_create(
            id=instance.id, money_currency=instance.money_currency)
        assert not created
        assert instance.money == value

    def test_defaults(self):
        money = Money(10, "EUR")
        instance, _ = ModelWithVanillaMoneyField.objects.get_or_create(
            integer=1, defaults={"money": money})
        assert instance.money == money

    def test_currency_field_lookup(self):
        value = Money(10, "EUR")
        ModelWithVanillaMoneyField.objects.create(money=value)
        instance, created = ModelWithVanillaMoneyField.objects.get_or_create(
            money_currency__iexact="eur")
        assert not created
        assert instance.money == value

    @pytest.mark.parametrize(
        "model, create_kwargs, get_kwargs",
        (
            (NullMoneyFieldModel, {
                "field": Money(100, "USD")
            }, {
                "field": 100,
                "field_currency": "USD"
            }),
            (ModelWithSharedCurrency, {
                "first": 10,
                "second": 15,
                "currency": "USD"
            }, {
                "first": 10,
                "currency": "USD"
            }),
        ),
    )
    def test_no_default_model(self, model, create_kwargs, get_kwargs):
        model.objects.create(**create_kwargs)
        instance, created = model.objects.get_or_create(**get_kwargs)
        assert not created

    def test_shared_currency(self):
        instance, created = ModelWithSharedCurrency.objects.get_or_create(
            first=10, second=15, currency="USD")
        assert instance.first == Money(10, "USD")
        assert instance.second == Money(15, "USD")
Пример #30
0
def test_forced_l10n():
    mp = Money(2.3, 'PLN')
    mp.use_l10n = True
    assert_template('{{ money }}', '2,30 zł', {'money': mp})
Пример #31
0
 def test_ne_currency(self):
     assert Money(10, "EUR") != Money(10, "USD")