From 8c00b17debbd0d369e71d5194932a9e579f0da4f Mon Sep 17 00:00:00 2001 From: Jukka Ahonen Date: Fri, 15 Nov 2024 14:37:33 +0200 Subject: [PATCH 1/9] tasotarkistus data to rent model --- ...llings_in_housing_companies_price_index.py | 26 ++++ leasing/models/rent.py | 128 ++++++++++-------- leasing/serializers/rent.py | 29 ++++ 3 files changed, 124 insertions(+), 59 deletions(-) create mode 100644 leasing/migrations/0081_rent_old_dwellings_in_housing_companies_price_index.py diff --git a/leasing/migrations/0081_rent_old_dwellings_in_housing_companies_price_index.py b/leasing/migrations/0081_rent_old_dwellings_in_housing_companies_price_index.py new file mode 100644 index 00000000..c8bf04b7 --- /dev/null +++ b/leasing/migrations/0081_rent_old_dwellings_in_housing_companies_price_index.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.14 on 2024-11-15 08:32 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("leasing", "0080_alter_contact_options"), + ] + + operations = [ + migrations.AddField( + model_name="rent", + name="old_dwellings_in_housing_companies_price_index", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="leasing.olddwellingsinhousingcompaniespriceindex", + verbose_name="Old dwellings in housing companies price index", + ), + ), + ] diff --git a/leasing/models/rent.py b/leasing/models/rent.py index 59344bb3..485d4ea5 100644 --- a/leasing/models/rent.py +++ b/leasing/models/rent.py @@ -99,6 +99,65 @@ class Meta(NameModel.Meta): verbose_name_plural = pgettext_lazy("Model name", "Rent intended uses") +class OldDwellingsInHousingCompaniesPriceIndex(TimeStampedModel): + """ + In Finnish: Vanhojen osakeasuntojen hintaindeksi + + From my understanding, a unique price index is identified by the API + database table URL and code of the index. A single table can hold multiple + indexes, and a single code can be used in multiple tables. + + Members: + code: Code for the index's table column. Example: "ketj_P_QA_T". \ + Same code is shared between tables for different intervals, e.g. \ + yearly or quarterly. + name: Name of the index. Example: "Index (2020=100)". + comment: Comment for the index's table column. + source: Source of the data. + source_table_updated: UTC timestamp when the source table was last updated. + source_table_label: Label for the source table. + url: API endpoint URL. + """ + + # Maximum lengths are arbitrary, but set to avoid extra large input. + CHARFIELD_MAX_LENGTH = 255 + + code = models.CharField( + verbose_name=_("Index code"), + max_length=CHARFIELD_MAX_LENGTH, + unique=True, + ) + name = models.CharField( + verbose_name=_("Index name"), + max_length=CHARFIELD_MAX_LENGTH, + ) + comment = models.TextField(verbose_name=_("Region"), blank=True) + source = models.CharField( + verbose_name=_("Data source"), + blank=True, + max_length=CHARFIELD_MAX_LENGTH, + ) + source_table_updated = models.DateTimeField( + verbose_name=_("Source table updated"), null=True + ) + source_table_label = models.TextField( + verbose_name=_("Source table label"), + blank=True, + ) + url = models.CharField( + verbose_name=_("API endpoint URL"), + max_length=CHARFIELD_MAX_LENGTH, + ) + + class Meta: + verbose_name = pgettext_lazy( + "model name", "price index of old dwellings in housing companies" + ) + verbose_name_plural = pgettext_lazy( + "model name", "price indexes of old dwellings in housing companies" + ) + + class Rent(TimeStampedSafeDeleteModel): """ In Finnish: Vuokran perustiedot @@ -241,6 +300,16 @@ class Rent(TimeStampedSafeDeleteModel): null=True, ) + # In Finnish: Tasotarkistusindeksi + old_dwellings_in_housing_companies_price_index = models.ForeignKey( + OldDwellingsInHousingCompaniesPriceIndex, + verbose_name=_("Old dwellings in housing companies price index"), + related_name="+", + on_delete=models.PROTECT, + blank=True, + null=True, + ) + recursive_get_related_skip_relations = ["lease"] class Meta: @@ -1408,65 +1477,6 @@ class LegacyIndex(models.Model): ) -class OldDwellingsInHousingCompaniesPriceIndex(TimeStampedModel): - """ - In Finnish: Vanhojen osakeasuntojen hintaindeksi - - From my understanding, a unique price index is identified by the API - database table URL and code of the index. A single table can hold multiple - indexes, and a single code can be used in multiple tables. - - Members: - code: Code for the index's table column. Example: "ketj_P_QA_T". \ - Same code is shared between tables for different intervals, e.g. \ - yearly or quarterly. - name: Name of the index. Example: "Index (2020=100)". - comment: Comment for the index's table column. - source: Source of the data. - source_table_updated: UTC timestamp when the source table was last updated. - source_table_label: Label for the source table. - url: API endpoint URL. - """ - - # Maximum lengths are arbitrary, but set to avoid extra large input. - CHARFIELD_MAX_LENGTH = 255 - - code = models.CharField( - verbose_name=_("Index code"), - max_length=CHARFIELD_MAX_LENGTH, - unique=True, - ) - name = models.CharField( - verbose_name=_("Index name"), - max_length=CHARFIELD_MAX_LENGTH, - ) - comment = models.TextField(verbose_name=_("Region"), blank=True) - source = models.CharField( - verbose_name=_("Data source"), - blank=True, - max_length=CHARFIELD_MAX_LENGTH, - ) - source_table_updated = models.DateTimeField( - verbose_name=_("Source table updated"), null=True - ) - source_table_label = models.TextField( - verbose_name=_("Source table label"), - blank=True, - ) - url = models.CharField( - verbose_name=_("API endpoint URL"), - max_length=CHARFIELD_MAX_LENGTH, - ) - - class Meta: - verbose_name = pgettext_lazy( - "model name", "price index of old dwellings in housing companies" - ) - verbose_name_plural = pgettext_lazy( - "model name", "price indexes of old dwellings in housing companies" - ) - - class IndexNumberYearly(TimeStampedModel): """ In Finnish: Indeksipisteluku, vuosittain diff --git a/leasing/serializers/rent.py b/leasing/serializers/rent.py index 2dfe7e09..fe6a6d13 100644 --- a/leasing/serializers/rent.py +++ b/leasing/serializers/rent.py @@ -14,7 +14,9 @@ FixedInitialYearRent, Index, IndexAdjustedRent, + IndexNumberYearly, LeaseBasisOfRent, + OldDwellingsInHousingCompaniesPriceIndex, PayableRent, ReceivableType, Rent, @@ -49,6 +51,26 @@ class Meta: fields = "__all__" +class IndexNumberYearlySerializer(serializers.Serializer): + id = serializers.IntegerField(required=False) + number = serializers.DecimalField(max_digits=10, decimal_places=2) + year = serializers.IntegerField() + region = serializers.CharField(max_length=255, required=False, allow_null=True) + comment = serializers.CharField(max_length=255, required=False, allow_null=True) + + class Meta: + model = IndexNumberYearly + fields = ("id", "number", "year", "region", "comment") + + +class OldDwellingsInHousingCompaniesPriceIndexSerializer(serializers.ModelSerializer): + numbers = IndexNumberYearlySerializer(many=True, required=False, allow_null=True) + + class Meta: + model = OldDwellingsInHousingCompaniesPriceIndex + fields = "__all__" + + class RentDueDateSerializer( FieldPermissionsSerializerMixin, serializers.ModelSerializer ): @@ -387,6 +409,12 @@ class RentSerializer( source="get_due_dates_as_daymonths", read_only=True, ) + override_receivable_type = ReceivableTypeSerializer(required=False, allow_null=True) + old_dwellings_in_housing_companies_price_index = ( + OldDwellingsInHousingCompaniesPriceIndexSerializer( + required=False, allow_null=True + ) + ) class Meta: model = Rent @@ -419,6 +447,7 @@ class Meta: "manual_ratio", "manual_ratio_previous", "override_receivable_type", + "old_dwellings_in_housing_companies_price_index", ) def override_permission_check_field_name(self, field_name): From c8441e6958a4c17f502e971c3ba98a2d8399d9aa Mon Sep 17 00:00:00 2001 From: Jukka Ahonen Date: Fri, 15 Nov 2024 12:48:01 +0000 Subject: [PATCH 2/9] tasotarkistus: IndexNumberYearlySerializer changes to match the model definition --- leasing/serializers/rent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/leasing/serializers/rent.py b/leasing/serializers/rent.py index fe6a6d13..88a1177d 100644 --- a/leasing/serializers/rent.py +++ b/leasing/serializers/rent.py @@ -53,10 +53,10 @@ class Meta: class IndexNumberYearlySerializer(serializers.Serializer): id = serializers.IntegerField(required=False) - number = serializers.DecimalField(max_digits=10, decimal_places=2) + number = serializers.DecimalField(max_digits=8, decimal_places=1) year = serializers.IntegerField() region = serializers.CharField(max_length=255, required=False, allow_null=True) - comment = serializers.CharField(max_length=255, required=False, allow_null=True) + comment = serializers.CharField(required=False, allow_null=True) class Meta: model = IndexNumberYearly From 66aa8589991dfeb11c183f9864c0c7e88bb9ad27 Mon Sep 17 00:00:00 2001 From: Jukka Ahonen Date: Thu, 21 Nov 2024 12:58:58 +0000 Subject: [PATCH 3/9] rent model: add start_price_index --- .../migrations/0082_rent_start_price_index.py | 20 +++++++++++++++++++ leasing/models/rent.py | 16 +++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 leasing/migrations/0082_rent_start_price_index.py diff --git a/leasing/migrations/0082_rent_start_price_index.py b/leasing/migrations/0082_rent_start_price_index.py new file mode 100644 index 00000000..72f3f12b --- /dev/null +++ b/leasing/migrations/0082_rent_start_price_index.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.16 on 2024-11-21 11:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("leasing", "0081_rent_old_dwellings_in_housing_companies_price_index"), + ] + + operations = [ + migrations.AddField( + model_name="rent", + name="start_price_index", + field=models.DecimalField( + decimal_places=1, max_digits=8, null=True, verbose_name="Index number" + ), + ), + ] diff --git a/leasing/models/rent.py b/leasing/models/rent.py index 485d4ea5..7405b318 100644 --- a/leasing/models/rent.py +++ b/leasing/models/rent.py @@ -310,6 +310,14 @@ class Rent(TimeStampedSafeDeleteModel): null=True, ) + # In Finnish: Tasotarkistusindeksi vuokran alkaessa + start_price_index = models.DecimalField( + verbose_name=_("Index number"), + decimal_places=1, + max_digits=8, + null=True, + ) + recursive_get_related_skip_relations = ["lease"] class Meta: @@ -822,6 +830,14 @@ def is_active_on_period(self, date_range_start, date_range_end): return False + def set_start_price_index(self): + if self.old_dwellings_in_housing_companies_price_index: + start_index_number_yearly = IndexNumberYearly.objects.get( + index=self.old_dwellings_in_housing_companies_price_index, + year=self.start_date.year - 1, + ) + self.start_price_index = start_index_number_yearly.number + class RentDueDate(TimeStampedSafeDeleteModel): """ From 3aae2a629066ec4c8b51d9ef4fa7fe002c690f5d Mon Sep 17 00:00:00 2001 From: Jukka Ahonen Date: Thu, 21 Nov 2024 14:06:15 +0000 Subject: [PATCH 4/9] tasotarkistus: renaming of model attributes --- ...emove_indexnumberyearly_number_and_more.py | 37 +++++++++++++++++++ leasing/models/rent.py | 14 +++---- leasing/serializers/rent.py | 4 +- locale/fi/LC_MESSAGES/django.po | 10 ++++- 4 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 leasing/migrations/0083_remove_indexnumberyearly_number_and_more.py diff --git a/leasing/migrations/0083_remove_indexnumberyearly_number_and_more.py b/leasing/migrations/0083_remove_indexnumberyearly_number_and_more.py new file mode 100644 index 00000000..6dd497fe --- /dev/null +++ b/leasing/migrations/0083_remove_indexnumberyearly_number_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.16 on 2024-11-21 13:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("leasing", "0082_rent_start_price_index"), + ] + + operations = [ + migrations.RemoveField( + model_name="indexnumberyearly", + name="number", + ), + migrations.AddField( + model_name="indexnumberyearly", + name="point_figure", + field=models.DecimalField( + decimal_places=1, + max_digits=8, + null=True, + verbose_name="Index point figure", + ), + ), + migrations.AlterField( + model_name="rent", + name="start_price_index", + field=models.DecimalField( + decimal_places=1, + max_digits=8, + null=True, + verbose_name="Start price index point figure", + ), + ), + ] diff --git a/leasing/models/rent.py b/leasing/models/rent.py index 7405b318..ced27a74 100644 --- a/leasing/models/rent.py +++ b/leasing/models/rent.py @@ -310,9 +310,9 @@ class Rent(TimeStampedSafeDeleteModel): null=True, ) - # In Finnish: Tasotarkistusindeksi vuokran alkaessa - start_price_index = models.DecimalField( - verbose_name=_("Index number"), + # In Finnish: Tasotarkistusindeksin pisteluku vuokran alkaessa (edellisen vuoden keskiarvo) + start_price_index_point_figure = models.DecimalField( + verbose_name=_("Start price index point figure"), decimal_places=1, max_digits=8, null=True, @@ -830,13 +830,13 @@ def is_active_on_period(self, date_range_start, date_range_end): return False - def set_start_price_index(self): + def set_start_price_index_point_figure(self): if self.old_dwellings_in_housing_companies_price_index: start_index_number_yearly = IndexNumberYearly.objects.get( index=self.old_dwellings_in_housing_companies_price_index, year=self.start_date.year - 1, ) - self.start_price_index = start_index_number_yearly.number + self.start_price_index_point_figure = start_index_number_yearly.point_figure class RentDueDate(TimeStampedSafeDeleteModel): @@ -1525,8 +1525,8 @@ class IndexNumberYearly(TimeStampedModel): # should be enough if the numbers are at most in the 100s of thousands. # Largest index number in the system at the moment is year 1914's index # with a number around 260 000. - number = models.DecimalField( - verbose_name=_("Index number"), + point_figure = models.DecimalField( + verbose_name=_("Index point figure"), decimal_places=1, max_digits=8, null=True, diff --git a/leasing/serializers/rent.py b/leasing/serializers/rent.py index 88a1177d..24c57478 100644 --- a/leasing/serializers/rent.py +++ b/leasing/serializers/rent.py @@ -53,14 +53,14 @@ class Meta: class IndexNumberYearlySerializer(serializers.Serializer): id = serializers.IntegerField(required=False) - number = serializers.DecimalField(max_digits=8, decimal_places=1) + point_figure = serializers.DecimalField(max_digits=8, decimal_places=1) year = serializers.IntegerField() region = serializers.CharField(max_length=255, required=False, allow_null=True) comment = serializers.CharField(required=False, allow_null=True) class Meta: model = IndexNumberYearly - fields = ("id", "number", "year", "region", "comment") + fields = ("id", "point_figure", "year", "region", "comment") class OldDwellingsInHousingCompaniesPriceIndexSerializer(serializers.ModelSerializer): diff --git a/locale/fi/LC_MESSAGES/django.po b/locale/fi/LC_MESSAGES/django.po index 30ea4a76..1483b97e 100644 --- a/locale/fi/LC_MESSAGES/django.po +++ b/locale/fi/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: MVJ 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-30 14:47+0200\n" +"POT-Creation-Date: 2024-11-21 15:41+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: \n" "Language: fi\n" @@ -2564,6 +2564,12 @@ msgstr "" msgid "Override receivable type" msgstr "Korvaava saamislaji" +msgid "Old dwellings in housing companies price index" +msgstr "Vanhojen osakeasuntojen hintaindeksi" + +msgid "Start price index point figure" +msgstr "Tasotarkistusindeksin pisteluku vuokran alkaessa" + msgctxt "Model name" msgid "Rent" msgstr "Vuokra" @@ -2767,7 +2773,7 @@ msgctxt "model name" msgid "price indexes of old dwellings in housing companies" msgstr "Vanhojen osakeasuntojen hintaindeksit" -msgid "Index number" +msgid "Index point figure" msgstr "Indeksipisteluku" msgid "Comment" From 126a2031d5ec49b996585d89d6067a9eb8f7c070 Mon Sep 17 00:00:00 2001 From: Jukka Ahonen Date: Thu, 21 Nov 2024 16:25:00 +0200 Subject: [PATCH 5/9] tasotarkistus: make changes into a single migration --- ...move_indexnumberyearly_number_and_more.py} | 21 ++++++++++++--- ...llings_in_housing_companies_price_index.py | 26 ------------------- .../migrations/0082_rent_start_price_index.py | 20 -------------- 3 files changed, 17 insertions(+), 50 deletions(-) rename leasing/migrations/{0083_remove_indexnumberyearly_number_and_more.py => 0081_remove_indexnumberyearly_number_and_more.py} (53%) delete mode 100644 leasing/migrations/0081_rent_old_dwellings_in_housing_companies_price_index.py delete mode 100644 leasing/migrations/0082_rent_start_price_index.py diff --git a/leasing/migrations/0083_remove_indexnumberyearly_number_and_more.py b/leasing/migrations/0081_remove_indexnumberyearly_number_and_more.py similarity index 53% rename from leasing/migrations/0083_remove_indexnumberyearly_number_and_more.py rename to leasing/migrations/0081_remove_indexnumberyearly_number_and_more.py index 6dd497fe..b1da3578 100644 --- a/leasing/migrations/0083_remove_indexnumberyearly_number_and_more.py +++ b/leasing/migrations/0081_remove_indexnumberyearly_number_and_more.py @@ -1,12 +1,13 @@ -# Generated by Django 4.2.16 on 2024-11-21 13:54 +# Generated by Django 4.2.16 on 2024-11-21 14:14 from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ("leasing", "0082_rent_start_price_index"), + ("leasing", "0080_alter_contact_options"), ] operations = [ @@ -24,9 +25,21 @@ class Migration(migrations.Migration): verbose_name="Index point figure", ), ), - migrations.AlterField( + migrations.AddField( + model_name="rent", + name="old_dwellings_in_housing_companies_price_index", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="leasing.olddwellingsinhousingcompaniespriceindex", + verbose_name="Old dwellings in housing companies price index", + ), + ), + migrations.AddField( model_name="rent", - name="start_price_index", + name="start_price_index_point_figure", field=models.DecimalField( decimal_places=1, max_digits=8, diff --git a/leasing/migrations/0081_rent_old_dwellings_in_housing_companies_price_index.py b/leasing/migrations/0081_rent_old_dwellings_in_housing_companies_price_index.py deleted file mode 100644 index c8bf04b7..00000000 --- a/leasing/migrations/0081_rent_old_dwellings_in_housing_companies_price_index.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 4.2.14 on 2024-11-15 08:32 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("leasing", "0080_alter_contact_options"), - ] - - operations = [ - migrations.AddField( - model_name="rent", - name="old_dwellings_in_housing_companies_price_index", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="+", - to="leasing.olddwellingsinhousingcompaniespriceindex", - verbose_name="Old dwellings in housing companies price index", - ), - ), - ] diff --git a/leasing/migrations/0082_rent_start_price_index.py b/leasing/migrations/0082_rent_start_price_index.py deleted file mode 100644 index 72f3f12b..00000000 --- a/leasing/migrations/0082_rent_start_price_index.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 4.2.16 on 2024-11-21 11:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("leasing", "0081_rent_old_dwellings_in_housing_companies_price_index"), - ] - - operations = [ - migrations.AddField( - model_name="rent", - name="start_price_index", - field=models.DecimalField( - decimal_places=1, max_digits=8, null=True, verbose_name="Index number" - ), - ), - ] From a2271cf2b55e0de0db6604e13952a291b1d4799d Mon Sep 17 00:00:00 2001 From: Jukka Ahonen Date: Fri, 22 Nov 2024 08:41:19 +0000 Subject: [PATCH 6/9] tasotarkistus: update import function to match latest changes --- .../commands/import_periodic_rent_adjustment_index.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/leasing/management/commands/import_periodic_rent_adjustment_index.py b/leasing/management/commands/import_periodic_rent_adjustment_index.py index 82f6ca20..d7f1ad3c 100644 --- a/leasing/management/commands/import_periodic_rent_adjustment_index.py +++ b/leasing/management/commands/import_periodic_rent_adjustment_index.py @@ -316,7 +316,9 @@ def _update_or_create_index_numbers( for dp in data_points: year = int(dp["key"][year_key_pos]) - number = _cast_index_number_to_float_or_none(dp["values"][number_value_pos]) + point_figure = _cast_index_number_to_float_or_none( + dp["values"][number_value_pos] + ) region = dp["key"][region_key_pos] comment = _find_comment_for_value(dp, comments, columns) # TODO verify in 5.9. meeting: should we exclude "ennakkotieto" commented values? @@ -324,7 +326,7 @@ def _update_or_create_index_numbers( index=index, year=year, defaults={ - "number": number, + "point_figure": point_figure, "region": region, "comment": comment, }, From c06e0c2f32a2473bd0f632cb056c1b3098c17191 Mon Sep 17 00:00:00 2001 From: Jukka Ahonen Date: Fri, 22 Nov 2024 12:51:40 +0000 Subject: [PATCH 7/9] tasotarkistus: update model and attribute names --- .../import_periodic_rent_adjustment_index.py | 10 +- ...emove_indexnumberyearly_number_and_more.py | 50 ---------- .../0083_indexpointfigureyearly_and_more.py | 95 +++++++++++++++++++ leasing/models/__init__.py | 4 +- leasing/models/rent.py | 10 +- leasing/serializers/rent.py | 14 +-- 6 files changed, 114 insertions(+), 69 deletions(-) delete mode 100644 leasing/migrations/0081_remove_indexnumberyearly_number_and_more.py create mode 100644 leasing/migrations/0083_indexpointfigureyearly_and_more.py diff --git a/leasing/management/commands/import_periodic_rent_adjustment_index.py b/leasing/management/commands/import_periodic_rent_adjustment_index.py index d7f1ad3c..58e6c8b6 100644 --- a/leasing/management/commands/import_periodic_rent_adjustment_index.py +++ b/leasing/management/commands/import_periodic_rent_adjustment_index.py @@ -7,7 +7,7 @@ from django.utils.dateparse import parse_datetime from leasing.models.rent import ( - IndexNumberYearly, + IndexPointFigureYearly, OldDwellingsInHousingCompaniesPriceIndex, ) @@ -316,17 +316,15 @@ def _update_or_create_index_numbers( for dp in data_points: year = int(dp["key"][year_key_pos]) - point_figure = _cast_index_number_to_float_or_none( - dp["values"][number_value_pos] - ) + value = _cast_index_number_to_float_or_none(dp["values"][number_value_pos]) region = dp["key"][region_key_pos] comment = _find_comment_for_value(dp, comments, columns) # TODO verify in 5.9. meeting: should we exclude "ennakkotieto" commented values? - _, created = IndexNumberYearly.objects.update_or_create( + _, created = IndexPointFigureYearly.objects.update_or_create( index=index, year=year, defaults={ - "point_figure": point_figure, + "value": value, "region": region, "comment": comment, }, diff --git a/leasing/migrations/0081_remove_indexnumberyearly_number_and_more.py b/leasing/migrations/0081_remove_indexnumberyearly_number_and_more.py deleted file mode 100644 index b1da3578..00000000 --- a/leasing/migrations/0081_remove_indexnumberyearly_number_and_more.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 4.2.16 on 2024-11-21 14:14 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("leasing", "0080_alter_contact_options"), - ] - - operations = [ - migrations.RemoveField( - model_name="indexnumberyearly", - name="number", - ), - migrations.AddField( - model_name="indexnumberyearly", - name="point_figure", - field=models.DecimalField( - decimal_places=1, - max_digits=8, - null=True, - verbose_name="Index point figure", - ), - ), - migrations.AddField( - model_name="rent", - name="old_dwellings_in_housing_companies_price_index", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.PROTECT, - related_name="+", - to="leasing.olddwellingsinhousingcompaniespriceindex", - verbose_name="Old dwellings in housing companies price index", - ), - ), - migrations.AddField( - model_name="rent", - name="start_price_index_point_figure", - field=models.DecimalField( - decimal_places=1, - max_digits=8, - null=True, - verbose_name="Start price index point figure", - ), - ), - ] diff --git a/leasing/migrations/0083_indexpointfigureyearly_and_more.py b/leasing/migrations/0083_indexpointfigureyearly_and_more.py new file mode 100644 index 00000000..6f376510 --- /dev/null +++ b/leasing/migrations/0083_indexpointfigureyearly_and_more.py @@ -0,0 +1,95 @@ +# Generated by Django 4.2.16 on 2024-11-22 11:42 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("leasing", "0082_remove_rent_seasonal_end_day_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="IndexPointFigureYearly", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "created_at", + models.DateTimeField( + auto_now_add=True, verbose_name="Time created" + ), + ), + ( + "modified_at", + models.DateTimeField(auto_now=True, verbose_name="Time modified"), + ), + ( + "value", + models.DecimalField( + decimal_places=1, max_digits=8, null=True, verbose_name="Value" + ), + ), + ("year", models.PositiveSmallIntegerField(verbose_name="Year")), + ( + "region", + models.CharField(blank=True, max_length=255, verbose_name="Region"), + ), + ("comment", models.TextField(blank=True, verbose_name="Comment")), + ( + "index", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="point_figures", + to="leasing.olddwellingsinhousingcompaniespriceindex", + verbose_name="Index", + ), + ), + ], + options={ + "verbose_name": "index number", + "verbose_name_plural": "index numbers", + "ordering": ("-index", "-year"), + }, + ), + migrations.AddField( + model_name="rent", + name="old_dwellings_in_housing_companies_price_index", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="+", + to="leasing.olddwellingsinhousingcompaniespriceindex", + verbose_name="Old dwellings in housing companies price index", + ), + ), + migrations.AddField( + model_name="rent", + name="start_price_index_point_figure", + field=models.DecimalField( + decimal_places=1, + max_digits=8, + null=True, + verbose_name="Start price index point figure", + ), + ), + migrations.DeleteModel( + name="IndexNumberYearly", + ), + migrations.AddConstraint( + model_name="indexpointfigureyearly", + constraint=models.UniqueConstraint( + fields=("index", "year"), name="unique_price_index_number" + ), + ), + ] diff --git a/leasing/models/__init__.py b/leasing/models/__init__.py index a3298f90..f14f1f06 100644 --- a/leasing/models/__init__.py +++ b/leasing/models/__init__.py @@ -108,7 +108,7 @@ FixedInitialYearRent, Index, IndexAdjustedRent, - IndexNumberYearly, + IndexPointFigureYearly, LeaseBasisOfRent, LeaseBasisOfRentManagementSubvention, LeaseBasisOfRentTemporarySubvention, @@ -170,7 +170,7 @@ "Hitas", "Index", "IndexAdjustedRent", - "IndexNumberYearly", + "IndexPointFigureYearly", "InfillDevelopmentCompensation", "InfillDevelopmentCompensationAttachment", "InfillDevelopmentCompensationDecision", diff --git a/leasing/models/rent.py b/leasing/models/rent.py index ced27a74..74b4b434 100644 --- a/leasing/models/rent.py +++ b/leasing/models/rent.py @@ -832,7 +832,7 @@ def is_active_on_period(self, date_range_start, date_range_end): def set_start_price_index_point_figure(self): if self.old_dwellings_in_housing_companies_price_index: - start_index_number_yearly = IndexNumberYearly.objects.get( + start_index_number_yearly = IndexPointFigureYearly.objects.get( index=self.old_dwellings_in_housing_companies_price_index, year=self.start_date.year - 1, ) @@ -1493,7 +1493,7 @@ class LegacyIndex(models.Model): ) -class IndexNumberYearly(TimeStampedModel): +class IndexPointFigureYearly(TimeStampedModel): """ In Finnish: Indeksipisteluku, vuosittain @@ -1519,14 +1519,14 @@ class IndexNumberYearly(TimeStampedModel): OldDwellingsInHousingCompaniesPriceIndex, verbose_name=_("Index"), on_delete=models.PROTECT, - related_name="numbers", + related_name="point_figures", ) # max_digits is arbitrary for the number. No need to limit it, although 7 # should be enough if the numbers are at most in the 100s of thousands. # Largest index number in the system at the moment is year 1914's index # with a number around 260 000. - point_figure = models.DecimalField( - verbose_name=_("Index point figure"), + value = models.DecimalField( + verbose_name=_("Value"), decimal_places=1, max_digits=8, null=True, diff --git a/leasing/serializers/rent.py b/leasing/serializers/rent.py index 24c57478..ff593be9 100644 --- a/leasing/serializers/rent.py +++ b/leasing/serializers/rent.py @@ -14,7 +14,7 @@ FixedInitialYearRent, Index, IndexAdjustedRent, - IndexNumberYearly, + IndexPointFigureYearly, LeaseBasisOfRent, OldDwellingsInHousingCompaniesPriceIndex, PayableRent, @@ -51,20 +51,22 @@ class Meta: fields = "__all__" -class IndexNumberYearlySerializer(serializers.Serializer): +class IndexPointFigureYearlySerializer(serializers.Serializer): id = serializers.IntegerField(required=False) - point_figure = serializers.DecimalField(max_digits=8, decimal_places=1) + value = serializers.DecimalField(max_digits=8, decimal_places=1) year = serializers.IntegerField() region = serializers.CharField(max_length=255, required=False, allow_null=True) comment = serializers.CharField(required=False, allow_null=True) class Meta: - model = IndexNumberYearly - fields = ("id", "point_figure", "year", "region", "comment") + model = IndexPointFigureYearly + fields = ("id", "value", "year", "region", "comment") class OldDwellingsInHousingCompaniesPriceIndexSerializer(serializers.ModelSerializer): - numbers = IndexNumberYearlySerializer(many=True, required=False, allow_null=True) + point_figures = IndexPointFigureYearlySerializer( + many=True, required=False, allow_null=True + ) class Meta: model = OldDwellingsInHousingCompaniesPriceIndex From 7ea4fcddf8b94ba821ea8e3386ecaf0b673ca1b2 Mon Sep 17 00:00:00 2001 From: Jukka Ahonen Date: Fri, 29 Nov 2024 12:19:50 +0200 Subject: [PATCH 8/9] rent model: set_start_price_index_point_figure method --- leasing/models/rent.py | 4 +-- leasing/tests/conftest.py | 19 +++++++++++- leasing/tests/models/test_rent.py | 48 ++++++++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/leasing/models/rent.py b/leasing/models/rent.py index 74b4b434..39a4a398 100644 --- a/leasing/models/rent.py +++ b/leasing/models/rent.py @@ -834,9 +834,9 @@ def set_start_price_index_point_figure(self): if self.old_dwellings_in_housing_companies_price_index: start_index_number_yearly = IndexPointFigureYearly.objects.get( index=self.old_dwellings_in_housing_companies_price_index, - year=self.start_date.year - 1, + year=self.lease.start_date.year - 1, ) - self.start_price_index_point_figure = start_index_number_yearly.point_figure + self.start_price_index_point_figure = start_index_number_yearly.value class RentDueDate(TimeStampedSafeDeleteModel): diff --git a/leasing/tests/conftest.py b/leasing/tests/conftest.py index 4a1a1797..7a1b15ca 100755 --- a/leasing/tests/conftest.py +++ b/leasing/tests/conftest.py @@ -67,7 +67,10 @@ LandUseAgreementType, ) from leasing.models.receivable_type import ReceivableType -from leasing.models.rent import OldDwellingsInHousingCompaniesPriceIndex +from leasing.models.rent import ( + IndexPointFigureYearly, + OldDwellingsInHousingCompaniesPriceIndex, +) from leasing.models.service_unit import ServiceUnitGroupMapping from leasing.models.tenant import TenantRentShare @@ -145,6 +148,20 @@ class Meta: model = Rent +@register +class OldDwellingsInHousingCompaniesPriceIndexFactory( + factory.django.DjangoModelFactory +): + class Meta: + model = OldDwellingsInHousingCompaniesPriceIndex + + +@register +class IndexPointFigureYearlyFactory(factory.django.DjangoModelFactory): + class Meta: + model = IndexPointFigureYearly + + @register class ContractRentFactory(factory.django.DjangoModelFactory): class Meta: diff --git a/leasing/tests/models/test_rent.py b/leasing/tests/models/test_rent.py index 18697502..f3834f03 100644 --- a/leasing/tests/models/test_rent.py +++ b/leasing/tests/models/test_rent.py @@ -12,7 +12,7 @@ RentCycle, RentType, ) -from leasing.models import Index, RentAdjustment, RentDueDate +from leasing.models import Index, Rent, RentAdjustment, RentDueDate from leasing.models.utils import DayMonth @@ -2841,3 +2841,49 @@ def test_is_the_last_billing_period( rent.save() assert rent.is_the_last_billing_period(billing_period) == expected + + +@pytest.mark.django_db +def test_set_start_price_index_point_figure_without_index(rent_factory, lease_factory): + """The point figure should be None if the rent has no old_dwellings_in_housing_companies_price_index.""" + lease = lease_factory() + rent: Rent = rent_factory(lease=lease) + + rent.set_start_price_index_point_figure() + + assert rent.start_price_index_point_figure is None + + +@pytest.mark.django_db +def test_set_start_price_index_point_figure_with_last_year_index( + rent_factory, + lease_factory, + old_dwellings_in_housing_companies_price_index_factory, + index_point_figure_yearly_factory, +): + """The point figure should be set if rent has old_dwellings_in_housing_companies_price_index + and it should be the year previous to the LEASE's start date.""" + lease = lease_factory(start_date=date(year=2024, month=1, day=1)) + old_dwellings_in_housing_companies_price_index = ( + old_dwellings_in_housing_companies_price_index_factory() + ) + index_point_figure_yearly_factory( + value=101, year=2021, index=old_dwellings_in_housing_companies_price_index + ) + index_point_figure_yearly_factory( + value=102, year=2022, index=old_dwellings_in_housing_companies_price_index + ) + expected_point_figure = index_point_figure_yearly_factory( + value=103, year=2023, index=old_dwellings_in_housing_companies_price_index + ) + index_point_figure_yearly_factory( + value=104, year=2024, index=old_dwellings_in_housing_companies_price_index + ) + rent: Rent = rent_factory( + lease=lease, + old_dwellings_in_housing_companies_price_index=old_dwellings_in_housing_companies_price_index, + ) + + rent.set_start_price_index_point_figure() + + assert rent.start_price_index_point_figure == expected_point_figure.value From 057ceffcb32cbff34a9155ad6b0db0a974f7443d Mon Sep 17 00:00:00 2001 From: Jukka Ahonen Date: Mon, 2 Dec 2024 14:19:47 +0000 Subject: [PATCH 9/9] tasotarkistus: field permissions --- .../commands/set_group_field_permissions.py | 9 +++++++++ .../commands/set_group_model_permissions.py | 9 +++++++++ leasing/serializers/rent.py | 14 +++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/leasing/management/commands/set_group_field_permissions.py b/leasing/management/commands/set_group_field_permissions.py index 205b8b5f..0154b222 100644 --- a/leasing/management/commands/set_group_field_permissions.py +++ b/leasing/management/commands/set_group_field_permissions.py @@ -499,6 +499,15 @@ 6: "view", 7: "change", }, + "old_dwellings_in_housing_companies_price_index": { + 1: "view", + 2: "view", + 3: "view", + 4: "view", + 5: "view", + 6: "view", + 7: "change", + }, }, "tenant": {"reference": {6: "change"}, "tenantcontact_set": {6: "change"}}, "leasearea": { diff --git a/leasing/management/commands/set_group_model_permissions.py b/leasing/management/commands/set_group_model_permissions.py index c3d54d2f..4f0fde0f 100644 --- a/leasing/management/commands/set_group_model_permissions.py +++ b/leasing/management/commands/set_group_model_permissions.py @@ -973,6 +973,15 @@ 6: ("view",), 7: ("view", "add", "change", "delete"), }, + "olddwellingsinhousingcompaniespriceindex": { + 1: ("view",), + 2: ("view",), + 3: ("view",), + 4: ("view",), + 5: ("view",), + 6: ("view",), + 7: ("view", "add", "change", "delete"), + }, "uidata": { 1: ("view", "add", "change", "delete"), 2: ("view", "add", "change", "delete"), diff --git a/leasing/serializers/rent.py b/leasing/serializers/rent.py index ff593be9..1f03988a 100644 --- a/leasing/serializers/rent.py +++ b/leasing/serializers/rent.py @@ -63,7 +63,10 @@ class Meta: fields = ("id", "value", "year", "region", "comment") -class OldDwellingsInHousingCompaniesPriceIndexSerializer(serializers.ModelSerializer): +class OldDwellingsInHousingCompaniesPriceIndexSerializer( + FieldPermissionsSerializerMixin, serializers.ModelSerializer +): + id = serializers.IntegerField(required=False) point_figures = IndexPointFigureYearlySerializer( many=True, required=False, allow_null=True ) @@ -489,6 +492,7 @@ class Meta: "manual_ratio", "manual_ratio_previous", "override_receivable_type", + "old_dwellings_in_housing_companies_price_index", ) @@ -523,6 +527,13 @@ class RentCreateUpdateSerializer( required=False, allow_null=True, ) + old_dwellings_in_housing_companies_price_index = InstanceDictPrimaryKeyRelatedField( + instance_class=OldDwellingsInHousingCompaniesPriceIndex, + queryset=OldDwellingsInHousingCompaniesPriceIndex.objects.all(), + related_serializer=OldDwellingsInHousingCompaniesPriceIndexSerializer, + required=False, + allow_null=True, + ) class Meta: model = Rent @@ -554,6 +565,7 @@ class Meta: "manual_ratio", "manual_ratio_previous", "override_receivable_type", + "old_dwellings_in_housing_companies_price_index", ) def validate(self, rent_data: dict):