fix: align reporting forms and demo analytics
All checks were successful
All checks were successful
This commit is contained in:
@@ -41,6 +41,8 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
organization=self.organization,
|
||||
report_year=2026,
|
||||
report_quarter=1,
|
||||
report_month=6,
|
||||
avg_employees=1050,
|
||||
avg_payroll_employees=995,
|
||||
payroll_fund=Decimal("1000000.00"),
|
||||
military_output_actual=Decimal("11000000.00"),
|
||||
@@ -56,6 +58,8 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
organization=self.organization,
|
||||
report_year=2025,
|
||||
report_quarter=1,
|
||||
report_month=12,
|
||||
avg_employees=1020,
|
||||
avg_payroll_employees=970,
|
||||
payroll_fund=Decimal("900000.00"),
|
||||
military_output_actual=Decimal("9000000.00"),
|
||||
@@ -88,11 +92,19 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
FormF3RecordFactory.create(
|
||||
organization=self.organization,
|
||||
report_year=2026,
|
||||
avg_employees=1050,
|
||||
production_workers=620,
|
||||
engineering_workers=210,
|
||||
administrative_workers=220,
|
||||
workers_needed=35,
|
||||
avg_employees=None,
|
||||
production_workers=None,
|
||||
engineering_workers=None,
|
||||
administrative_workers=None,
|
||||
workers_needed=None,
|
||||
average_employee_age=Decimal("42.00"),
|
||||
employees_under_20=4,
|
||||
employees_20_29=18,
|
||||
employees_30_39=28,
|
||||
employees_40_49=26,
|
||||
employees_50_59=17,
|
||||
employees_over_60=7,
|
||||
machine_tools_and_equipment=46,
|
||||
total_equipment=187,
|
||||
domestic_equipment=91,
|
||||
imported_equipment=96,
|
||||
@@ -109,14 +121,16 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
FormF3RecordFactory.create(
|
||||
organization=self.organization,
|
||||
report_year=2025,
|
||||
avg_employees=1020,
|
||||
avg_employees=None,
|
||||
average_employee_age=Decimal("41.00"),
|
||||
)
|
||||
FormF4RecordFactory.create(
|
||||
organization=self.organization,
|
||||
report_year=2026,
|
||||
revenue_rsbu=Decimal("1100000000.00"),
|
||||
net_profit_rsbu=Decimal("320000000.00"),
|
||||
ebitda_rsbu=Decimal("480000000.00"),
|
||||
report_half_year=2,
|
||||
revenue_rsbu=Decimal("477807.00"),
|
||||
net_profit_rsbu=Decimal("442414.00"),
|
||||
ebitda_rsbu=Decimal("138564.00"),
|
||||
gross_profit_rsbu=Decimal("520000000.00"),
|
||||
operating_profit_rsbu=Decimal("300000000.00"),
|
||||
net_debt_rsbu=Decimal("200000000.00"),
|
||||
@@ -131,9 +145,10 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
FormF4RecordFactory.create(
|
||||
organization=self.organization,
|
||||
report_year=2025,
|
||||
revenue_rsbu=Decimal("980000000.00"),
|
||||
net_profit_rsbu=Decimal("250000000.00"),
|
||||
ebitda_rsbu=Decimal("410000000.00"),
|
||||
report_half_year=2,
|
||||
revenue_rsbu=Decimal("450000.00"),
|
||||
net_profit_rsbu=Decimal("400000.00"),
|
||||
ebitda_rsbu=Decimal("120000.00"),
|
||||
ros=Decimal("21.00"),
|
||||
roa=Decimal("10.50"),
|
||||
roe=Decimal("14.10"),
|
||||
@@ -152,14 +167,31 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
organization=self.organization,
|
||||
report_year=2026,
|
||||
category="Станочное оборудование",
|
||||
total_equipment=54,
|
||||
domestic_equipment=31,
|
||||
imported_equipment=23,
|
||||
total_equipment=187,
|
||||
domestic_equipment=None,
|
||||
imported_equipment=96,
|
||||
commissioned_equipment=12,
|
||||
decommissioned_equipment=7,
|
||||
age_under_5=70,
|
||||
age_under_5_imported=35,
|
||||
age_under_5_utilization_rate=Decimal("92.00"),
|
||||
age_under_5_lease_share_itn_percent=Decimal("20.00"),
|
||||
age_5_10=41,
|
||||
age_5_10_imported=21,
|
||||
age_5_10_utilization_rate=Decimal("92.00"),
|
||||
age_5_10_lease_share_itn_percent=Decimal("20.00"),
|
||||
age_10_15=33,
|
||||
age_10_15_imported=17,
|
||||
age_10_15_utilization_rate=Decimal("92.00"),
|
||||
age_10_15_lease_share_itn_percent=Decimal("20.00"),
|
||||
age_15_20=22,
|
||||
age_15_20_imported=12,
|
||||
age_15_20_utilization_rate=Decimal("92.00"),
|
||||
age_15_20_lease_share_itn_percent=Decimal("20.00"),
|
||||
age_over_20=21,
|
||||
age_over_20_imported=11,
|
||||
age_over_20_utilization_rate=Decimal("92.00"),
|
||||
age_over_20_lease_share_itn_percent=Decimal("20.00"),
|
||||
physical_wear_percent=Decimal("28.40"),
|
||||
utilization_rate=Decimal("92.00"),
|
||||
avg_shift_work=Decimal("1.80"),
|
||||
@@ -231,14 +263,24 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
self.assertEqual(response.data["organization_id"], str(self.organization.id))
|
||||
self.assertEqual(response.data["group"], "efficiency")
|
||||
self.assertEqual(response.data["periods"], [2025, 2026])
|
||||
self.assertEqual(
|
||||
response.data["report_periods"],
|
||||
[
|
||||
{"year": 2025, "report_half_year": 2},
|
||||
{"year": 2026, "report_half_year": 2},
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
response.data["kpis"].keys(),
|
||||
{"revenue", "ebitda", "net_profit", "revenue_per_employee"},
|
||||
)
|
||||
self.assertGreater(response.data["kpis"]["revenue_per_employee"]["value"], 0)
|
||||
self.assertEqual(response.data["kpis"]["revenue"]["value"], 477807)
|
||||
self.assertEqual(response.data["kpis"]["net_profit"]["value"], 442414)
|
||||
self.assertEqual(response.data["kpis"]["ebitda"]["value"], 138564)
|
||||
self.assertEqual(response.data["kpis"]["revenue_per_employee"]["value"], 455)
|
||||
self.assertEqual(
|
||||
response.data["kpis"]["revenue_per_employee"]["unit"],
|
||||
"rub_per_employee",
|
||||
"rub_thousands_per_employee",
|
||||
)
|
||||
for kpi in response.data["kpis"].values():
|
||||
self.assertIn("previous_value", kpi)
|
||||
@@ -251,7 +293,7 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
self.assertIn("unit", series)
|
||||
self.assertIn("points", series)
|
||||
expected_unit = (
|
||||
"rub_per_employee"
|
||||
"rub_thousands_per_employee"
|
||||
if series["metric"] == "revenue_per_employee"
|
||||
else "rub_thousands"
|
||||
)
|
||||
@@ -283,10 +325,43 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["periods"], [])
|
||||
self.assertEqual(response.data["report_periods"], [])
|
||||
self.assertEqual(response.data["kpis"], {})
|
||||
self.assertFalse(response.data["data_available"])
|
||||
self.assertEqual(response.data["message"], "Данные отсутствуют")
|
||||
|
||||
def test_economics_normalizes_f2_rubles_to_thousands_without_f4(self):
|
||||
organization = OrganizationFactory.create()
|
||||
FormF1RecordFactory.create(
|
||||
organization=organization,
|
||||
report_year=2026,
|
||||
report_month=6,
|
||||
avg_employees=100,
|
||||
)
|
||||
FormF2RecordFactory.create(
|
||||
organization=organization,
|
||||
report_year=2026,
|
||||
revenue=Decimal("1200000.00"),
|
||||
net_profit=Decimal("300000.00"),
|
||||
ebitda=Decimal("480000.00"),
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
f"/api/v1/organizations/{organization.id}/analytics/economics/"
|
||||
"?group=efficiency&from_year=2026&to_year=2026"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["kpis"]["revenue"]["value"], 1200)
|
||||
self.assertEqual(
|
||||
response.data["kpis"]["revenue_per_employee"]["value"],
|
||||
12,
|
||||
)
|
||||
self.assertEqual(
|
||||
response.data["kpis"]["revenue"]["unit"],
|
||||
"rub_thousands",
|
||||
)
|
||||
|
||||
def test_personnel_contract(self):
|
||||
personnel_response = self.client.get(
|
||||
f"/api/v1/organizations/{self.organization.id}/analytics/personnel/"
|
||||
@@ -297,6 +372,7 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
personnel_response.data["organization_id"], str(self.organization.id)
|
||||
)
|
||||
self.assertEqual(personnel_response.data["report_year"], 2026)
|
||||
self.assertEqual(personnel_response.data["report_month"], 6)
|
||||
self.assertEqual(
|
||||
personnel_response.data["headcount"]["average_employees"],
|
||||
1050,
|
||||
@@ -306,6 +382,7 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
set(personnel_response.data["history"][0]),
|
||||
{
|
||||
"year",
|
||||
"report_month",
|
||||
"average_employees",
|
||||
"avg_payroll_employees",
|
||||
"average_age",
|
||||
@@ -313,15 +390,22 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
},
|
||||
)
|
||||
self.assertEqual(len(personnel_response.data["age_distribution"]), 3)
|
||||
self.assertIn("average_age", personnel_response.data)
|
||||
self.assertEqual(personnel_response.data["average_age"], 42.0)
|
||||
self.assertEqual(
|
||||
personnel_response.data["headcount"]["avg_payroll_employees"], 995
|
||||
)
|
||||
self.assertEqual(personnel_response.data["headcount"]["payroll_fund"], 1000000)
|
||||
self.assertEqual(
|
||||
personnel_response.data["headcount"]["payroll_fund"], 1000000000
|
||||
)
|
||||
self.assertIsNone(personnel_response.data["headcount"]["production_workers"])
|
||||
self.assertEqual(
|
||||
personnel_response.data["age_distribution"][0]["age_group"],
|
||||
"under_30",
|
||||
)
|
||||
self.assertEqual(
|
||||
personnel_response.data["age_distribution"][0]["employees_count"],
|
||||
22,
|
||||
)
|
||||
self.assertIn("employees_count", personnel_response.data["age_distribution"][0])
|
||||
|
||||
def test_yearly_analytics_use_latest_available_year_when_requested_year_is_empty(
|
||||
@@ -374,15 +458,19 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.data["summary"]["total_equipment"], 187)
|
||||
self.assertEqual(response.data["summary"]["machine_tools_and_equipment"], 46)
|
||||
self.assertIsNone(response.data["summary"]["domestic_equipment"])
|
||||
self.assertEqual(response.data["summary"]["imported_equipment"], 96)
|
||||
self.assertEqual(response.data["summary"]["physical_wear_percent"], 32.0)
|
||||
self.assertEqual(response.data["summary"]["weighted_wear_percent"], 32.0)
|
||||
self.assertIsNone(response.data["summary"]["weighted_wear_percent"])
|
||||
self.assertEqual(response.data["summary"]["utilization_rate"], 0.92)
|
||||
self.assertEqual(response.data["summary"]["commissioned_equipment"], 1)
|
||||
self.assertEqual(response.data["summary"]["decommissioned_equipment"], 1)
|
||||
self.assertEqual(response.data["summary"]["commissioned_equipment"], 12)
|
||||
self.assertEqual(response.data["summary"]["decommissioned_equipment"], 7)
|
||||
self.assertEqual(
|
||||
response.data["age_distribution"][0]["bucket"], "under_5_years"
|
||||
)
|
||||
self.assertEqual(len(response.data["age_distribution"]), 5)
|
||||
self.assertEqual(response.data["age_distribution"][0]["imported_equipment"], 35)
|
||||
self.assertEqual(
|
||||
set(response.data["age_distribution"][0]),
|
||||
{
|
||||
@@ -400,13 +488,13 @@ class OrganizationAnalyticsApiTest(APITestCase):
|
||||
response.data["categories"][0],
|
||||
{
|
||||
"category": "Станочное оборудование",
|
||||
"total_equipment": 54,
|
||||
"domestic_equipment": 31,
|
||||
"imported_equipment": 23,
|
||||
"physical_wear_percent": 28.4,
|
||||
"weighted_wear_percent": 28.4,
|
||||
"total_equipment": 187,
|
||||
"domestic_equipment": None,
|
||||
"imported_equipment": 96,
|
||||
"physical_wear_percent": None,
|
||||
"weighted_wear_percent": None,
|
||||
"utilization_rate": 0.92,
|
||||
"lease_share_itn_percent": None,
|
||||
"lease_share_itn_percent": 20.0,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user