From 55c74e64b2292c83bf27314f81d009cbac97234d Mon Sep 17 00:00:00 2001
From: indigane <indigane@users.noreply.github.com>
Date: Tue, 28 May 2024 09:45:22 +0300
Subject: [PATCH] Add regulated half hitas housing companies report

---
 backend/hitas/services/housing_company.py    | 30 +++++++++
 backend/hitas/tests/apis/test_api_reports.py | 68 ++++++++++++++++++++
 backend/hitas/urls.py                        |  9 ++-
 backend/hitas/views/__init__.py              |  3 +-
 backend/hitas/views/reports.py               | 13 +++-
 backend/openapi.yaml                         | 21 ++++++
 6 files changed, 141 insertions(+), 3 deletions(-)

diff --git a/backend/hitas/services/housing_company.py b/backend/hitas/services/housing_company.py
index e2862906a..a2deaa0ea 100644
--- a/backend/hitas/services/housing_company.py
+++ b/backend/hitas/services/housing_company.py
@@ -340,6 +340,36 @@ def find_regulated_housing_companies_for_reporting() -> list[HousingCompanyWithR
     )
 
 
+def find_regulated_half_hitas_housing_companies_for_reporting() -> list[HousingCompanyWithRegulatedReportAnnotations]:
+    return list(
+        HousingCompany.objects.select_related("postal_code")
+        .prefetch_related(
+            "real_estates__buildings__apartments",
+        )
+        .filter(
+            regulation_status=RegulationStatus.REGULATED,
+            hitas_type=HitasType.HALF_HITAS,
+        )
+        .alias(
+            _acquisition_price=get_first_sale_acquisition_price("real_estates__buildings__apartments__id"),
+        )
+        .annotate(
+            _completion_date=max_date_if_all_not_null("real_estates__buildings__apartments__completion_date"),
+            surface_area=Round(Sum("real_estates__buildings__apartments__surface_area")),
+            realized_acquisition_price=Sum("_acquisition_price"),
+            avg_price_per_square_meter=Round(
+                F("realized_acquisition_price") / F("surface_area"),
+                precision=2,
+            ),
+            apartment_count=Count("real_estates__buildings__apartments"),
+        )
+        .order_by(
+            "postal_code__value",
+            "_completion_date",
+        )
+    )
+
+
 def find_unregulated_housing_companies_for_reporting() -> list[HousingCompanyWithUnregulatedReportAnnotations]:
     return list(
         HousingCompany.objects.select_related("postal_code")
diff --git a/backend/hitas/tests/apis/test_api_reports.py b/backend/hitas/tests/apis/test_api_reports.py
index 225b1d30b..b46be09d4 100644
--- a/backend/hitas/tests/apis/test_api_reports.py
+++ b/backend/hitas/tests/apis/test_api_reports.py
@@ -539,6 +539,74 @@ def test__api__regulated_housing_companies_report__multiple_housing_companies(ap
     ]
 
 
+@pytest.mark.django_db
+def test__api__regulated_half_hitas_housing_companies_report__multiple_housing_companies(api_client: HitasAPIClient):
+    housing_company_1: HousingCompany = HousingCompanyFactory.create(
+        postal_code__value="00001",
+        postal_code__cost_area=1,
+        hitas_type=HitasType.HALF_HITAS,
+    )
+    sale_1: ApartmentSale = ApartmentSaleFactory.create(
+        purchase_date=datetime.date(2020, 1, 1),
+        purchase_price=50_000,
+        apartment_share_of_housing_company_loans=10_000,
+        apartment__surface_area=100,
+        apartment__completion_date=datetime.date(2020, 1, 1),
+        apartment__building__real_estate__housing_company=housing_company_1,
+    )
+
+    housing_company_2: HousingCompany = HousingCompanyFactory.create(
+        postal_code__value="00002",
+        postal_code__cost_area=1,
+        hitas_type=HitasType.HALF_HITAS,
+    )
+    sale_2: ApartmentSale = ApartmentSaleFactory.create(
+        purchase_date=datetime.date(2021, 1, 1),
+        purchase_price=100_000,
+        apartment_share_of_housing_company_loans=44_000,
+        apartment__surface_area=50,
+        apartment__completion_date=datetime.date(2021, 1, 1),
+        apartment__building__real_estate__housing_company=housing_company_2,
+    )
+
+    url = reverse("hitas:regulated-half-hitas-housing-companies-report-list")
+    response: HttpResponse = api_client.get(url)
+
+    workbook: Workbook = load_workbook(BytesIO(response.content), data_only=False)
+    worksheet: Worksheet = workbook.worksheets[0]
+
+    assert list(worksheet.values) == [
+        (
+            "Kalleusalue",
+            "Postinumero",
+            "Yhtiö",
+            "Osoite",
+            "Valmistumispäivä",
+            "Asuntojen lukumäärä",
+            "Keskineliöhinta",
+        ),
+        (
+            housing_company_1.postal_code.cost_area,
+            housing_company_1.postal_code.value,
+            housing_company_1.display_name,
+            housing_company_1.street_address,
+            datetime.datetime.fromisoformat(sale_1.apartment.completion_date.isoformat()),
+            1,
+            int(sale_1.total_price / sale_1.apartment.surface_area),
+        ),
+        (
+            housing_company_2.postal_code.cost_area,
+            housing_company_2.postal_code.value,
+            housing_company_2.display_name,
+            housing_company_2.street_address,
+            datetime.datetime.fromisoformat(sale_2.apartment.completion_date.isoformat()),
+            1,
+            int(sale_2.total_price / sale_2.apartment.surface_area),
+        ),
+        (None, None, None, None, None, None, None),  # Empty row at the bottom for filtering and sorting
+    ]
+
+
 @pytest.mark.django_db
 def test__api__regulated_housing_companies_report__unregulated_not_included(api_client: HitasAPIClient):
     housing_company_1: HousingCompany = HousingCompanyFactory.create(
diff --git a/backend/hitas/urls.py b/backend/hitas/urls.py
index 59e98666d..6108ba2f7 100644
--- a/backend/hitas/urls.py
+++ b/backend/hitas/urls.py
@@ -81,10 +81,17 @@
 # /api/v1/reports/download-regulated-housing-companies-report
 router.register(
     r"reports/download-regulated-housing-companies-report",
-    views.RegulateHousingCompaniesReportView,
+    views.RegulatedHousingCompaniesReportView,
     basename="regulated-housing-companies-report",
 )
 
+# /api/v1/reports/download-regulated-half-hitas-housing-companies-report
+router.register(
+    r"reports/download-regulated-half-hitas-housing-companies-report",
+    views.RegulatedHalfHitasHousingCompaniesReportView,
+    basename="regulated-half-hitas-housing-companies-report",
+)
+
 # /api/v1/reports/download-unregulated-housing-companies-report
 router.register(
     r"reports/download-unregulated-housing-companies-report",
diff --git a/backend/hitas/views/__init__.py b/backend/hitas/views/__init__.py
index ce5295af9..d899b436d 100644
--- a/backend/hitas/views/__init__.py
+++ b/backend/hitas/views/__init__.py
@@ -34,7 +34,8 @@
     MultipleOwnershipsReportView,
     OwnershipsByCompanyJSONReportView,
     OwnershipsByHousingCompanyReport,
-    RegulateHousingCompaniesReportView,
+    RegulatedHalfHitasHousingCompaniesReportView,
+    RegulatedHousingCompaniesReportView,
     SalesByPostalCodeAndAreaReportView,
     SalesReportView,
     UnregulatedHousingCompaniesReportView,
diff --git a/backend/hitas/views/reports.py b/backend/hitas/views/reports.py
index 7092a35b9..b45fc2799 100644
--- a/backend/hitas/views/reports.py
+++ b/backend/hitas/views/reports.py
@@ -11,6 +11,7 @@
 from hitas.services.apartment_sale import find_sales_on_interval_for_reporting
 from hitas.services.housing_company import (
     find_housing_companies_for_state_reporting,
+    find_regulated_half_hitas_housing_companies_for_reporting,
     find_regulated_housing_companies_for_reporting,
     find_unregulated_housing_companies_for_reporting,
 )
@@ -57,7 +58,7 @@ def list(self, request: Request, *args, **kwargs) -> HttpResponse:
         return get_excel_response(filename=filename, excel=workbook)
 
 
-class RegulateHousingCompaniesReportView(ViewSet):
+class RegulatedHousingCompaniesReportView(ViewSet):
     renderer_classes = [HitasJSONRenderer, ExcelRenderer]
 
     def list(self, request: Request, *args, **kwargs) -> HttpResponse:
@@ -67,6 +68,16 @@ def list(self, request: Request, *args, **kwargs) -> HttpResponse:
         return get_excel_response(filename=filename, excel=workbook)
 
 
+class RegulatedHalfHitasHousingCompaniesReportView(ViewSet):
+    renderer_classes = [HitasJSONRenderer, ExcelRenderer]
+
+    def list(self, request: Request, *args, **kwargs) -> HttpResponse:
+        housing_companies = find_regulated_half_hitas_housing_companies_for_reporting()
+        workbook = build_regulated_housing_companies_report_excel(housing_companies)
+        filename = "Valvonnan piirissä olevat puolihitas-yhtiöt.xlsx"
+        return get_excel_response(filename=filename, excel=workbook)
+
+
 class UnregulatedHousingCompaniesReportView(ViewSet):
     renderer_classes = [HitasJSONRenderer, ExcelRenderer]
 
diff --git a/backend/openapi.yaml b/backend/openapi.yaml
index 62d9ce12b..bfa122aa2 100644
--- a/backend/openapi.yaml
+++ b/backend/openapi.yaml
@@ -4008,6 +4008,27 @@ paths:
         "500":
           $ref: "#/components/responses/InternalServerError"
 
+  /api/v1/reports/download-regulated-half-hitas-housing-companies-report:
+    get:
+      description: Download an Excel report of half hitas regulated housing companies
+      operationId: fetch-regulated-half-hitas-housing-companies-report-excel
+      tags:
+        - Reports
+      responses:
+        "200":
+          description: Successfully downloaded a regulated half hitas housing company report
+          content:
+            application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
+              schema:
+                type: string
+                format: binary
+        "400":
+          $ref: "#/components/responses/BadRequest"
+        "404":
+          $ref: "#/components/responses/NotFound"
+        "500":
+          $ref: "#/components/responses/InternalServerError"
+
   /api/v1/reports/download-unregulated-housing-companies-report:
     get:
       description: Download an Excel report of housing companies released from regulation