From e8a636dd9fb894ec35874b167416e7b1228e639d Mon Sep 17 00:00:00 2001 From: Floris272 Date: Fri, 24 Jan 2025 17:31:49 +0100 Subject: [PATCH] add product prijs & frequentie --- .../0004_product_frequentie_product_prijs.py | 44 +++++++++++ .../producten/models/product.py | 32 +++++++- .../producten/tests/api/test_product.py | 23 ++++++ .../producten/tests/factories.py | 5 ++ .../producten/tests/test_product_admin.py | 4 + src/open_producten/producten/views.py | 4 +- src/producten-openapi.yaml | 79 +++++++++++++++++++ 7 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 src/open_producten/producten/migrations/0004_product_frequentie_product_prijs.py diff --git a/src/open_producten/producten/migrations/0004_product_frequentie_product_prijs.py b/src/open_producten/producten/migrations/0004_product_frequentie_product_prijs.py new file mode 100644 index 0000000..68bb65b --- /dev/null +++ b/src/open_producten/producten/migrations/0004_product_frequentie_product_prijs.py @@ -0,0 +1,44 @@ +# Generated by Django 4.2.17 on 2025-01-29 10:26 + +from decimal import Decimal +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("producten", "0003_product_status_alter_product_aanmaak_datum_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="product", + name="frequentie", + field=models.CharField( + choices=[ + ("eenmalig", "Eenmalig"), + ("maandelijks", "Maandelijks"), + ("jaarlijks", "Jaarlijks"), + ], + default="eenmalig", + help_text="De frequentie van betalingen.", + max_length=30, + verbose_name="Prijs frequentie", + ), + preserve_default=False, + ), + migrations.AddField( + model_name="product", + name="prijs", + field=models.DecimalField( + decimal_places=2, + default="0.01", + help_text="De prijs van het product.", + max_digits=8, + validators=[django.core.validators.MinValueValidator(Decimal("0.01"))], + verbose_name="bedrag", + ), + preserve_default=False, + ), + ] diff --git a/src/open_producten/producten/models/product.py b/src/open_producten/producten/models/product.py index c68c100..2121a06 100644 --- a/src/open_producten/producten/models/product.py +++ b/src/open_producten/producten/models/product.py @@ -1,6 +1,12 @@ from datetime import date - -from django.core.validators import MinLengthValidator, RegexValidator, ValidationError +from decimal import Decimal + +from django.core.validators import ( + MinLengthValidator, + MinValueValidator, + RegexValidator, + ValidationError, +) from django.db import models from django.utils.translation import gettext_lazy as _ @@ -12,6 +18,13 @@ from .validators import validate_bsn +class PrijsFrequentieChoices(models.TextChoices): + + EENMALIG = "eenmalig", _("Eenmalig") + MAANDELIJKS = "maandelijks", _("Maandelijks") + JAARLIJKS = "jaarlijks", _("Jaarlijks") + + class Product(BasePublishableModel): product_type = models.ForeignKey( ProductType, @@ -66,6 +79,21 @@ class Product(BasePublishableModel): blank=True, ) + prijs = models.DecimalField( + verbose_name=_("bedrag"), + decimal_places=2, + max_digits=8, + validators=[MinValueValidator(Decimal("0.01"))], + help_text=_("De prijs van het product."), + ) + + frequentie = models.CharField( + _("Prijs frequentie"), + max_length=30, + choices=PrijsFrequentieChoices.choices, + help_text=_("De frequentie van betalingen."), + ) + class Meta: verbose_name = _("Product") verbose_name_plural = _("Producten") diff --git a/src/open_producten/producten/tests/api/test_product.py b/src/open_producten/producten/tests/api/test_product.py index 6e2e8e5..554ee95 100644 --- a/src/open_producten/producten/tests/api/test_product.py +++ b/src/open_producten/producten/tests/api/test_product.py @@ -24,6 +24,9 @@ def setUp(self): "product_type_id": self.product_type.id, "bsn": "111222333", "status": "initieel", + "prijs": "20.20", + "frequentie": "eenmalig", + "data": [], } self.path = reverse("product-list") @@ -44,6 +47,12 @@ def test_required_fields(self): "product_type_id": [ ErrorDetail(string=_("This field is required."), code="required") ], + "prijs": [ + ErrorDetail(string=_("This field is required."), code="required") + ], + "frequentie": [ + ErrorDetail(string=_("This field is required."), code="required") + ], }, ) @@ -62,6 +71,8 @@ def test_create_product(self): "gepubliceerd": False, "start_datum": None, "eind_datum": None, + "prijs": str(product.prijs), + "frequentie": product.frequentie, "aanmaak_datum": product.aanmaak_datum.astimezone().isoformat(), "update_datum": product.update_datum.astimezone().isoformat(), "product_type": { @@ -212,6 +223,8 @@ def test_read_producten(self): "gepubliceerd": False, "start_datum": None, "eind_datum": None, + "prijs": str(product1.prijs), + "frequentie": product1.frequentie, "aanmaak_datum": product1.aanmaak_datum.astimezone().isoformat(), "update_datum": product1.update_datum.astimezone().isoformat(), "product_type": { @@ -236,6 +249,8 @@ def test_read_producten(self): "gepubliceerd": False, "start_datum": None, "eind_datum": None, + "prijs": str(product2.prijs), + "frequentie": product2.frequentie, "aanmaak_datum": product2.aanmaak_datum.astimezone().isoformat(), "update_datum": product2.update_datum.astimezone().isoformat(), "product_type": { @@ -271,6 +286,8 @@ def test_read_product(self): "gepubliceerd": False, "start_datum": None, "eind_datum": None, + "prijs": str(product.prijs), + "frequentie": product.frequentie, "aanmaak_datum": "2025-12-31T01:00:00+01:00", "update_datum": "2025-12-31T01:00:00+01:00", "product_type": { @@ -304,6 +321,8 @@ def test_update_state_and_dates_are_not_checked_when_not_changed(self): "bsn": "111222333", "start_datum": datetime.date(2025, 12, 31), "eind_datum": datetime.date(2026, 12, 31), + "prijs": "10", + "frequentie": "eenmalig", } product = ProductFactory.create(**data) @@ -382,6 +401,8 @@ def test_update_state_and_dates_are_checked_when_product_type_is_changed(self): toegestane_statussen=[] ).id, "bsn": "111222333", + "prijs": "10", + "frequentie": "eenmalig", } | test["field"] product = ProductFactory.create(**data) @@ -448,6 +469,8 @@ def test_update_state_and_dates_are_checked_when_changed(self): "product_type_id": product_type.id, "bsn": "111222333", "status": "initieel", + "prijs": "10", + "frequentie": "eenmalig", } product = ProductFactory.create(**data) diff --git a/src/open_producten/producten/tests/factories.py b/src/open_producten/producten/tests/factories.py index e87e295..58cb8c0 100644 --- a/src/open_producten/producten/tests/factories.py +++ b/src/open_producten/producten/tests/factories.py @@ -2,10 +2,15 @@ from ...producttypen.tests.factories import ProductTypeFactory from ..models import Product +from ..models.product import PrijsFrequentieChoices class ProductFactory(factory.django.DjangoModelFactory): product_type = factory.SubFactory(ProductTypeFactory) + prijs = factory.fuzzy.FuzzyDecimal(1, 10) + frequentie = factory.fuzzy.FuzzyChoice( + [x[0] for x in PrijsFrequentieChoices.choices] + ) class Meta: model = Product diff --git a/src/open_producten/producten/tests/test_product_admin.py b/src/open_producten/producten/tests/test_product_admin.py index 4ac47eb..ef436f9 100644 --- a/src/open_producten/producten/tests/test_product_admin.py +++ b/src/open_producten/producten/tests/test_product_admin.py @@ -41,6 +41,8 @@ def test_status_and_dates_are_validated_when_changed( "bsn": "111222333", "start_datum": datetime.date(2025, 12, 31), "eind_datum": datetime.date(2026, 12, 31), + "prijs": "10", + "frequentie": "eenmalig", } product = ProductFactory.create(**data) @@ -67,6 +69,8 @@ def test_status__and_dates_are_validated_when_product_type_is_changed( "bsn": "111222333", "start_datum": datetime.date(2025, 12, 31), "eind_datum": datetime.date(2026, 12, 31), + "prijs": "10", + "frequentie": "eenmalig", } product = ProductFactory.create(**data) diff --git a/src/open_producten/producten/views.py b/src/open_producten/producten/views.py index 7ee4564..0c7007a 100644 --- a/src/open_producten/producten/views.py +++ b/src/open_producten/producten/views.py @@ -27,6 +27,8 @@ "gepubliceerd": False, "bsn": "111222333", "status": "gereed", + "prijs": "20.20", + "frequentie": "eenmalig", }, request_only=True, ) @@ -67,4 +69,4 @@ class ProductViewSet(AuditTrailViewSetMixin, OrderedModelViewSet): lookup_url_field = "id" serializer_class = ProductSerializer filter_backends = [DjangoFilterBackend] - filterset_fields = ["gepubliceerd", "status"] + filterset_fields = ["gepubliceerd", "status", "frequentie"] diff --git a/src/producten-openapi.yaml b/src/producten-openapi.yaml index 54e33df..d4e9612 100644 --- a/src/producten-openapi.yaml +++ b/src/producten-openapi.yaml @@ -18,6 +18,21 @@ paths: description: Deze lijst kan gefilterd wordt met query-string parameters. summary: Alle PRODUCTEN opvragen. parameters: + - in: query + name: frequentie + schema: + type: string + title: Prijs frequentie + enum: + - eenmalig + - jaarlijks + - maandelijks + description: |- + De frequentie van betalingen. + + * `eenmalig` - Eenmalig + * `maandelijks` - Maandelijks + * `jaarlijks` - Jaarlijks - in: query name: gepubliceerd schema: @@ -110,6 +125,8 @@ paths: gepubliceerd: false bsn: '111222333' status: gereed + prijs: '20.20' + frequentie: eenmalig summary: Create product required: true security: @@ -379,6 +396,16 @@ components: required: - veld_a - veld_b + FrequentieEnum: + enum: + - eenmalig + - maandelijks + - jaarlijks + type: string + description: |- + * `eenmalig` - Eenmalig + * `maandelijks` - Maandelijks + * `jaarlijks` - Jaarlijks NestedProductType: type: object properties: @@ -559,6 +586,22 @@ components: pattern: ^[0-9]*$ maxLength: 8 minLength: 8 + prijs: + type: string + format: decimal + pattern: ^-?\d{0,6}(?:\.\d{0,2})?$ + title: Bedrag + description: De prijs van het product. + frequentie: + allOf: + - $ref: '#/components/schemas/FrequentieEnum' + title: Prijs frequentie + description: |- + De frequentie van betalingen. + + * `eenmalig` - Eenmalig + * `maandelijks` - Maandelijks + * `jaarlijks` - Jaarlijks Product: type: object properties: @@ -624,9 +667,27 @@ components: pattern: ^[0-9]*$ maxLength: 8 minLength: 8 + prijs: + type: string + format: decimal + pattern: ^-?\d{0,6}(?:\.\d{0,2})?$ + title: Bedrag + description: De prijs van het product. + frequentie: + allOf: + - $ref: '#/components/schemas/FrequentieEnum' + title: Prijs frequentie + description: |- + De frequentie van betalingen. + + * `eenmalig` - Eenmalig + * `maandelijks` - Maandelijks + * `jaarlijks` - Jaarlijks required: - aanmaak_datum + - frequentie - id + - prijs - product_type - update_datum ProductRequest: @@ -680,7 +741,25 @@ components: pattern: ^[0-9]*$ maxLength: 8 minLength: 8 + prijs: + type: string + format: decimal + pattern: ^-?\d{0,6}(?:\.\d{0,2})?$ + title: Bedrag + description: De prijs van het product. + frequentie: + allOf: + - $ref: '#/components/schemas/FrequentieEnum' + title: Prijs frequentie + description: |- + De frequentie van betalingen. + + * `eenmalig` - Eenmalig + * `maandelijks` - Maandelijks + * `jaarlijks` - Jaarlijks required: + - frequentie + - prijs - product_type_id StatusEnum: enum: