Add initial implementations for forms and organization apps with serializers, factories, and admin configurations
Some checks failed
CI/CD Pipeline / Run Tests (push) Failing after 45s
CI/CD Pipeline / Code Quality Checks (push) Failing after 48s
CI/CD Pipeline / Build Docker Images (push) Has been skipped
CI/CD Pipeline / Push to Gitea Registry (push) Has been skipped
CI/CD Pipeline / Deploy to Server (push) Has been skipped

This commit is contained in:
2026-03-28 18:23:06 +01:00
parent 8ed3e1175c
commit 345b1d0cc8
201 changed files with 15097 additions and 6691 deletions

View File

@@ -16,19 +16,51 @@ class FormF1RecordFactory(factory.django.DjangoModelFactory):
model = FormF1Record
organization = factory.SubFactory(OrganizationFactory)
load_batch = factory.LazyAttribute(lambda _: fake.uuid4())
load_batch = factory.Sequence(lambda n: n + 1)
report_year = 2026
report_quarter = 1
# Выпуск военной продукции (факт. цены)
military_output_actual = factory.LazyAttribute(lambda _: fake.pydecimal(min_value=0, max_value=1000000, left_digits=10, right_digits=2))
military_domestic_actual = factory.LazyAttribute(lambda _: fake.pydecimal(min_value=0, max_value=1000000, left_digits=10, right_digits=2))
military_export_actual = factory.LazyAttribute(lambda _: fake.pydecimal(min_value=0, max_value=1000000, left_digits=10, right_digits=2))
military_output_actual = factory.LazyAttribute(
lambda _: fake.pydecimal(
min_value=0, max_value=1000000, left_digits=10, right_digits=2
)
)
military_domestic_actual = factory.LazyAttribute(
lambda _: fake.pydecimal(
min_value=0, max_value=1000000, left_digits=10, right_digits=2
)
)
military_export_actual = factory.LazyAttribute(
lambda _: fake.pydecimal(
min_value=0, max_value=1000000, left_digits=10, right_digits=2
)
)
# Выпуск гражданской продукции (факт. цены)
civilian_output_actual = factory.LazyAttribute(lambda _: fake.pydecimal(min_value=0, max_value=1000000, left_digits=10, right_digits=2))
civilian_domestic_actual = factory.LazyAttribute(lambda _: fake.pydecimal(min_value=0, max_value=1000000, left_digits=10, right_digits=2))
civilian_export_actual = factory.LazyAttribute(lambda _: fake.pydecimal(min_value=0, max_value=1000000, left_digits=10, right_digits=2))
civilian_output_actual = factory.LazyAttribute(
lambda _: fake.pydecimal(
min_value=0, max_value=1000000, left_digits=10, right_digits=2
)
)
civilian_domestic_actual = factory.LazyAttribute(
lambda _: fake.pydecimal(
min_value=0, max_value=1000000, left_digits=10, right_digits=2
)
)
civilian_export_actual = factory.LazyAttribute(
lambda _: fake.pydecimal(
min_value=0, max_value=1000000, left_digits=10, right_digits=2
)
)
# Кадры
avg_employees = factory.LazyAttribute(lambda _: fake.random_int(min=10, max=10000))
avg_payroll_employees = factory.LazyAttribute(lambda _: fake.random_int(min=10, max=10000))
payroll_fund = factory.LazyAttribute(lambda _: fake.pydecimal(min_value=0, max_value=10000000, left_digits=12, right_digits=2))
avg_payroll_employees = factory.LazyAttribute(
lambda _: fake.random_int(min=10, max=10000)
)
payroll_fund = factory.LazyAttribute(
lambda _: fake.pydecimal(
min_value=0, max_value=10000000, left_digits=12, right_digits=2
)
)

View File

@@ -1,5 +1,7 @@
"""Tests for FormF1 model."""
from decimal import Decimal
from django.test import TestCase
from .factories import FormF1RecordFactory
@@ -19,7 +21,9 @@ class FormF1RecordModelTest(TestCase):
def test_record_str_representation(self):
"""Test string representation."""
expected = f"Ф-1: {self.record.organization} ({self.record.load_batch})"
expected = (
f"Ф-1: {self.record.organization.name} (batch: {self.record.load_batch})"
)
self.assertEqual(str(self.record), expected)
def test_organization_relationship(self):
@@ -30,13 +34,13 @@ class FormF1RecordModelTest(TestCase):
def test_decimal_fields_precision(self):
"""Test decimal fields precision."""
field = self.record._meta.get_field("military_output_actual")
self.assertEqual(field.max_digits, 18)
self.assertEqual(field.max_digits, 20)
self.assertEqual(field.decimal_places, 2)
def test_integer_fields(self):
"""Test integer fields."""
self.assertIsInstance(self.record.avg_employees, int)
self.assertIsInstance(self.record.avg_payroll_employees, int)
"""Test headcount fields use decimal values."""
self.assertIsInstance(self.record.avg_employees, (int, Decimal))
self.assertIsInstance(self.record.avg_payroll_employees, (int, Decimal))
def test_load_batch_index(self):
"""Test load_batch has db_index."""

View File

@@ -1,7 +1,5 @@
"""Tests for FormF1 services."""
from io import BytesIO
from unittest.mock import MagicMock, patch
from apps.form_1.services import FormF1Parser, FormF1Service
from django.test import TestCase
@@ -26,20 +24,20 @@ class FormF1ServiceTest(TestCase):
def test_get_by_load_batch(self):
"""Test getting records by load batch."""
batch_id = "test-batch-123"
batch_id = 101
FormF1RecordFactory.create(load_batch=batch_id)
FormF1RecordFactory.create(load_batch=batch_id)
FormF1RecordFactory.create(load_batch="other-batch")
FormF1RecordFactory.create(load_batch=202)
results = FormF1Service.get_by_load_batch(batch_id)
self.assertEqual(results.count(), 2)
def test_delete_by_load_batch(self):
"""Test deleting records by load batch."""
batch_id = "delete-batch"
batch_id = 303
FormF1RecordFactory.create(load_batch=batch_id)
FormF1RecordFactory.create(load_batch=batch_id)
other = FormF1RecordFactory.create(load_batch="keep-batch")
other = FormF1RecordFactory.create(load_batch=404)
count = FormF1Service.delete_by_load_batch(batch_id)
self.assertEqual(count, 2)
@@ -49,13 +47,37 @@ class FormF1ServiceTest(TestCase):
self.assertTrue(FormF1Record.objects.filter(pk=other.pk).exists())
def test_create_versioned_record_archives_previous_period_version(self):
"""Same period reload keeps history but leaves one active version."""
org = OrganizationFactory.create()
previous = FormF1RecordFactory.create(
organization=org,
load_batch=501,
report_year=2026,
report_quarter=1,
)
current = FormF1Service.create_versioned_record(
organization=org,
load_batch=502,
report_year=2026,
report_quarter=1,
military_output_actual=150,
)
previous.refresh_from_db()
self.assertFalse(previous.is_active_version)
self.assertEqual(previous.superseded_by_batch, 502)
self.assertIsNotNone(previous.superseded_at)
self.assertTrue(current.is_active_version)
class FormF1ParserTest(TestCase):
"""Tests for FormF1Parser."""
def test_get_column_mappings_returns_mappings(self):
"""Test get_column_mappings returns correct mappings."""
parser = FormF1Parser()
parser = FormF1Parser(report_year=2026, report_quarter=1)
mappings = parser.get_column_mappings()
self.assertIsInstance(mappings, list)
@@ -63,14 +85,13 @@ class FormF1ParserTest(TestCase):
# Проверяем наличие обязательных полей
field_names = [m.field_name for m in mappings]
self.assertIn("inn", field_names)
self.assertIn("military_output_actual", field_names)
self.assertIn("civilian_output_actual", field_names)
def test_create_record_creates_organization(self):
"""Test create_record creates organization if not exists."""
parser = FormF1Parser()
parser.load_batch = "test-batch"
parser = FormF1Parser(report_year=2026, report_quarter=1)
parser.load_batch = 505
row_data = {
"inn": "1234567890",
@@ -85,3 +106,5 @@ class FormF1ParserTest(TestCase):
self.assertIsNotNone(record)
self.assertEqual(record.organization.inn, "1234567890")
self.assertEqual(record.military_output_actual, 100.0)
self.assertEqual(record.report_year, 2026)
self.assertEqual(record.report_quarter, 1)