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

@@ -15,6 +15,7 @@ from apps.core.excel import (
ParseResult,
RowData,
)
from apps.core.reporting import ReportingPeriodParserMixin, VersionedReportServiceMixin
from apps.core.services import BaseService, BulkOperationsMixin
from apps.form_2.models import FormF2Record
from apps.organization.services import OrganizationService
@@ -24,7 +25,11 @@ from django.db.models import Count, Max
logger = logging.getLogger(__name__)
class FormF2Service(BulkOperationsMixin, BaseService[FormF2Record]):
class FormF2Service(
BulkOperationsMixin,
VersionedReportServiceMixin[FormF2Record],
BaseService[FormF2Record],
):
"""
Сервис для работы с записями формы Ф-2.
@@ -36,11 +41,27 @@ class FormF2Service(BulkOperationsMixin, BaseService[FormF2Record]):
model = FormF2Record
@classmethod
def get_by_organization(cls, organization_id):
"""Получить записи организации."""
return cls.get_queryset().filter(organization_id=organization_id)
@classmethod
def get_by_batch(cls, batch_id: int):
"""Получить записи по номеру загрузки."""
return cls.get_queryset().filter(load_batch=batch_id)
@classmethod
def get_by_load_batch(cls, batch_id: int):
"""Совместимость со старым API сервиса."""
return cls.get_by_batch(batch_id)
@classmethod
def delete_by_load_batch(cls, batch_id: int) -> int:
"""Удалить записи по номеру загрузки."""
deleted_count, _ = cls.get_queryset().filter(load_batch=batch_id).delete()
return deleted_count
@classmethod
def get_next_batch_id(cls) -> int:
"""Получить следующий номер загрузки."""
@@ -60,7 +81,7 @@ class FormF2Service(BulkOperationsMixin, BaseService[FormF2Record]):
)
class FormF2Parser(BaseExcelParser[FormF2Record]):
class FormF2Parser(ReportingPeriodParserMixin, BaseExcelParser[FormF2Record]):
"""
Парсер Excel файла формы Ф-2 (Бухгалтерский баланс).
@@ -81,71 +102,255 @@ class FormF2Parser(BaseExcelParser[FormF2Record]):
"""Маппинг колонок Excel на поля модели."""
return [
# I. Внеоборотные активы
ColumnMapping(4, "Нематериальные активы", "intangible_assets", field_type="decimal"),
ColumnMapping(5, "Результаты исследований и разработок", "rd_results", field_type="decimal"),
ColumnMapping(6, "Нематериальные поисковые активы", "intangible_search_assets", field_type="decimal"),
ColumnMapping(7, "Материальные поисковые активы", "tangible_search_assets", field_type="decimal"),
ColumnMapping(
4, "Нематериальные активы", "intangible_assets", field_type="decimal"
),
ColumnMapping(
5,
"Результаты исследований и разработок",
"rd_results",
field_type="decimal",
),
ColumnMapping(
6,
"Нематериальные поисковые активы",
"intangible_search_assets",
field_type="decimal",
),
ColumnMapping(
7,
"Материальные поисковые активы",
"tangible_search_assets",
field_type="decimal",
),
ColumnMapping(8, "Основные средства", "fixed_assets", field_type="decimal"),
ColumnMapping(9, "Доходные вложения в материальные ценности", "profitable_investments", field_type="decimal"),
ColumnMapping(10, "Финансовые вложения (внеоборотные)", "financial_investments_non_current", field_type="decimal"),
ColumnMapping(11, "Отложенные налоговые активы", "deferred_tax_assets", field_type="decimal"),
ColumnMapping(12, "Прочие внеоборотные активы", "other_non_current_assets", field_type="decimal"),
ColumnMapping(13, "Итого внеоборотные активы", "total_non_current_assets", field_type="decimal"),
ColumnMapping(
9,
"Доходные вложения в материальные ценности",
"profitable_investments",
field_type="decimal",
),
ColumnMapping(
10,
"Финансовые вложения (внеоборотные)",
"financial_investments_non_current",
field_type="decimal",
),
ColumnMapping(
11,
"Отложенные налоговые активы",
"deferred_tax_assets",
field_type="decimal",
),
ColumnMapping(
12,
"Прочие внеоборотные активы",
"other_non_current_assets",
field_type="decimal",
),
ColumnMapping(
13,
"Итого внеоборотные активы",
"total_non_current_assets",
field_type="decimal",
),
# II. Оборотные активы
ColumnMapping(14, "Запасы", "inventories", field_type="decimal"),
ColumnMapping(15, "НДС по приобретённым ценностям", "vat_on_acquired_assets", field_type="decimal"),
ColumnMapping(16, "Дебиторская задолженность", "receivables", field_type="decimal"),
ColumnMapping(17, "Финансовые вложения (оборотные)", "financial_investments_current", field_type="decimal"),
ColumnMapping(18, "Денежные средства и эквиваленты", "cash_and_equivalents", field_type="decimal"),
ColumnMapping(19, "Прочие оборотные активы", "other_current_assets", field_type="decimal"),
ColumnMapping(20, "Итого оборотные активы", "total_current_assets", field_type="decimal"),
ColumnMapping(
15,
"НДС по приобретённым ценностям",
"vat_on_acquired_assets",
field_type="decimal",
),
ColumnMapping(
16, "Дебиторская задолженность", "receivables", field_type="decimal"
),
ColumnMapping(
17,
"Финансовые вложения (оборотные)",
"financial_investments_current",
field_type="decimal",
),
ColumnMapping(
18,
"Денежные средства и эквиваленты",
"cash_and_equivalents",
field_type="decimal",
),
ColumnMapping(
19,
"Прочие оборотные активы",
"other_current_assets",
field_type="decimal",
),
ColumnMapping(
20,
"Итого оборотные активы",
"total_current_assets",
field_type="decimal",
),
ColumnMapping(21, "Баланс (актив)", "total_assets", field_type="decimal"),
# III. Капитал и резервы
ColumnMapping(22, "Уставный капитал", "authorized_capital", field_type="decimal"),
ColumnMapping(23, "Собственные акции, выкупленные у акционеров", "own_shares_bought_back", field_type="decimal"),
ColumnMapping(24, "Переоценка внеоборотных активов", "revaluation_of_non_current_assets", field_type="decimal"),
ColumnMapping(25, "Добавочный капитал", "additional_capital", field_type="decimal"),
ColumnMapping(26, "Резервный капитал", "reserve_capital", field_type="decimal"),
ColumnMapping(27, "Нераспределённая прибыль", "retained_earnings", field_type="decimal"),
ColumnMapping(28, "Итого капитал и резервы", "total_equity", field_type="decimal"),
ColumnMapping(
22, "Уставный капитал", "authorized_capital", field_type="decimal"
),
ColumnMapping(
23,
"Собственные акции, выкупленные у акционеров",
"own_shares_bought_back",
field_type="decimal",
),
ColumnMapping(
24,
"Переоценка внеоборотных активов",
"revaluation_of_non_current_assets",
field_type="decimal",
),
ColumnMapping(
25, "Добавочный капитал", "additional_capital", field_type="decimal"
),
ColumnMapping(
26, "Резервный капитал", "reserve_capital", field_type="decimal"
),
ColumnMapping(
27,
"Нераспределённая прибыль",
"retained_earnings",
field_type="decimal",
),
ColumnMapping(
28, "Итого капитал и резервы", "total_equity", field_type="decimal"
),
# IV. Долгосрочные обязательства
ColumnMapping(29, "Заёмные средства (долгосрочные)", "borrowings_non_current", field_type="decimal"),
ColumnMapping(30, "Отложенные налоговые обязательства", "deferred_tax_liabilities", field_type="decimal"),
ColumnMapping(31, "Оценочные обязательства (долгосрочные)", "estimated_liabilities_non_current", field_type="decimal"),
ColumnMapping(32, "Прочие обязательства (долгосрочные)", "other_liabilities_non_current", field_type="decimal"),
ColumnMapping(33, "Итого долгосрочные обязательства", "total_non_current_liabilities", field_type="decimal"),
ColumnMapping(
29,
"Заёмные средства (долгосрочные)",
"borrowings_non_current",
field_type="decimal",
),
ColumnMapping(
30,
"Отложенные налоговые обязательства",
"deferred_tax_liabilities",
field_type="decimal",
),
ColumnMapping(
31,
"Оценочные обязательства (долгосрочные)",
"estimated_liabilities_non_current",
field_type="decimal",
),
ColumnMapping(
32,
"Прочие обязательства (долгосрочные)",
"other_liabilities_non_current",
field_type="decimal",
),
ColumnMapping(
33,
"Итого долгосрочные обязательства",
"total_non_current_liabilities",
field_type="decimal",
),
# V. Краткосрочные обязательства
ColumnMapping(34, "Заёмные средства (краткосрочные)", "borrowings_current", field_type="decimal"),
ColumnMapping(35, "Кредиторская задолженность", "payables", field_type="decimal"),
ColumnMapping(36, "Доходы будущих периодов", "deferred_income", field_type="decimal"),
ColumnMapping(37, "Оценочные обязательства (краткосрочные)", "estimated_liabilities_current", field_type="decimal"),
ColumnMapping(38, "Прочие обязательства (краткосрочные)", "other_liabilities_current", field_type="decimal"),
ColumnMapping(39, "Итого краткосрочные обязательства", "total_current_liabilities", field_type="decimal"),
ColumnMapping(40, "Баланс (пассив)", "total_liabilities", field_type="decimal"),
ColumnMapping(
34,
"Заёмные средства (краткосрочные)",
"borrowings_current",
field_type="decimal",
),
ColumnMapping(
35, "Кредиторская задолженность", "payables", field_type="decimal"
),
ColumnMapping(
36, "Доходы будущих периодов", "deferred_income", field_type="decimal"
),
ColumnMapping(
37,
"Оценочные обязательства (краткосрочные)",
"estimated_liabilities_current",
field_type="decimal",
),
ColumnMapping(
38,
"Прочие обязательства (краткосрочные)",
"other_liabilities_current",
field_type="decimal",
),
ColumnMapping(
39,
"Итого краткосрочные обязательства",
"total_current_liabilities",
field_type="decimal",
),
ColumnMapping(
40, "Баланс (пассив)", "total_liabilities", field_type="decimal"
),
# Отчёт о финансовых результатах
ColumnMapping(41, "Выручка", "revenue", field_type="decimal"),
ColumnMapping(42, "Себестоимость продаж", "cost_of_sales", field_type="decimal"),
ColumnMapping(
42, "Себестоимость продаж", "cost_of_sales", field_type="decimal"
),
ColumnMapping(43, "Валовая прибыль", "gross_profit", field_type="decimal"),
ColumnMapping(44, "Коммерческие расходы", "selling_expenses", field_type="decimal"),
ColumnMapping(45, "Управленческие расходы", "administrative_expenses", field_type="decimal"),
ColumnMapping(46, "Прибыль от продаж", "profit_from_sales", field_type="decimal"),
ColumnMapping(47, "Проценты к получению", "interest_receivable", field_type="decimal"),
ColumnMapping(48, "Проценты к уплате", "interest_payable", field_type="decimal"),
ColumnMapping(
44, "Коммерческие расходы", "selling_expenses", field_type="decimal"
),
ColumnMapping(
45,
"Управленческие расходы",
"administrative_expenses",
field_type="decimal",
),
ColumnMapping(
46, "Прибыль от продаж", "profit_from_sales", field_type="decimal"
),
ColumnMapping(
47, "Проценты к получению", "interest_receivable", field_type="decimal"
),
ColumnMapping(
48, "Проценты к уплате", "interest_payable", field_type="decimal"
),
ColumnMapping(49, "Прочие доходы", "other_income", field_type="decimal"),
ColumnMapping(50, "Прочие расходы", "other_expenses", field_type="decimal"),
ColumnMapping(51, "Прибыль до налогообложения", "profit_before_tax", field_type="decimal"),
ColumnMapping(52, "Текущий налог на прибыль", "income_tax", field_type="decimal"),
ColumnMapping(
51,
"Прибыль до налогообложения",
"profit_before_tax",
field_type="decimal",
),
ColumnMapping(
52, "Текущий налог на прибыль", "income_tax", field_type="decimal"
),
ColumnMapping(53, "Чистая прибыль", "net_profit", field_type="decimal"),
# Дополнительные показатели
ColumnMapping(54, "EBITDA", "ebitda", field_type="decimal"),
ColumnMapping(55, "Амортизация", "depreciation", field_type="decimal"),
ColumnMapping(56, "Оборотный капитал", "working_capital", field_type="decimal"),
ColumnMapping(
56, "Оборотный капитал", "working_capital", field_type="decimal"
),
ColumnMapping(57, "Чистый долг", "net_debt", field_type="decimal"),
# Прошлый период
ColumnMapping(58, "Баланс (актив) - прошлый период", "total_assets_prev", field_type="decimal"),
ColumnMapping(59, "Баланс (пассив) - прошлый период", "total_liabilities_prev", field_type="decimal"),
ColumnMapping(60, "Выручка - прошлый период", "revenue_prev", field_type="decimal"),
ColumnMapping(61, "Чистая прибыль - прошлый период", "net_profit_prev", field_type="decimal"),
ColumnMapping(
58,
"Баланс (актив) - прошлый период",
"total_assets_prev",
field_type="decimal",
),
ColumnMapping(
59,
"Баланс (пассив) - прошлый период",
"total_liabilities_prev",
field_type="decimal",
),
ColumnMapping(
60, "Выручка - прошлый период", "revenue_prev", field_type="decimal"
),
ColumnMapping(
61,
"Чистая прибыль - прошлый период",
"net_profit_prev",
field_type="decimal",
),
]
def get_next_batch_id(self) -> int:
@@ -153,8 +358,14 @@ class FormF2Parser(BaseExcelParser[FormF2Record]):
return FormF2Service.get_next_batch_id()
@transaction.atomic
def create_record(self, row_data: RowData, batch_id: int) -> FormF2Record:
def create_record(
self,
row_data: RowData | dict[str, Any],
batch_id: int | None = None,
) -> FormF2Record:
"""Создать запись формы Ф-2."""
row_data = self._normalize_row_data(row_data)
batch_id = batch_id or getattr(self, "load_batch", self.get_next_batch_id())
org, _ = OrganizationService.get_or_create_by_inn(
inn=row_data.inn,
defaults={
@@ -165,16 +376,23 @@ class FormF2Parser(BaseExcelParser[FormF2Record]):
},
)
record = FormF2Record.objects.create(
record = FormF2Service.create_versioned_record(
organization=org,
load_batch=batch_id,
report_year=self.report_year,
report_quarter=self.report_quarter,
**row_data.fields,
)
return record
def parse_form_f2_file(file) -> ParseResult:
def parse_form_f2_file(
file,
*,
report_year: int,
report_quarter: int | None = None,
) -> ParseResult:
"""
Парсит Excel файл формы Ф-2.
@@ -184,5 +402,5 @@ def parse_form_f2_file(file) -> ParseResult:
Returns:
ParseResult с результатами парсинга
"""
parser = FormF2Parser()
parser = FormF2Parser(report_year=report_year, report_quarter=report_quarter)
return parser.parse(file)