feat(fns): парсер ФНС бухгалтерской отчетности

- Модели FinancialReport и FinancialReportLine
- FNSExcelParser для файлов fin_{id}_{ogrn}.xlsx
- FNSReportService с дедупликацией по хешу файла
- Celery задачи для мониторинга папки (каждые 5 мин)
- API: POST /fns/upload/, GET /fns/reports/
- Django admin интеграция
- 25 unit-тестов
This commit is contained in:
2026-02-01 14:44:19 +01:00
parent eb0d6f2600
commit cd0e21350b
17 changed files with 1537 additions and 10 deletions

View File

@@ -1,7 +1,83 @@
"""
Сериализаторы для приложения парсеров.
TODO: Добавить сериализаторы по мере необходимости.
"""
# Сериализаторы будут добавлены по мере разработки конкретных парсеров
from apps.parsers.models import FinancialReport, FinancialReportLine
from rest_framework import serializers
class FinancialReportLineSerializer(serializers.ModelSerializer):
"""Сериализатор строки финансового отчета."""
class Meta:
model = FinancialReportLine
fields = [
"id",
"form_code",
"line_code",
"line_name",
"year",
"period_start",
"period_end",
]
class FinancialReportSerializer(serializers.ModelSerializer):
"""Сериализатор финансового отчета."""
lines_count = serializers.SerializerMethodField()
class Meta:
model = FinancialReport
fields = [
"id",
"external_id",
"ogrn",
"file_name",
"file_hash",
"load_batch",
"status",
"source",
"error_message",
"created_at",
"updated_at",
"lines_count",
]
read_only_fields = fields
def get_lines_count(self, obj) -> int:
return obj.lines.count()
class FinancialReportDetailSerializer(FinancialReportSerializer):
"""Сериализатор финансового отчета с детализацией строк."""
lines = FinancialReportLineSerializer(many=True, read_only=True)
class Meta(FinancialReportSerializer.Meta):
fields = FinancialReportSerializer.Meta.fields + ["lines"]
class FNSFileUploadSerializer(serializers.Serializer):
"""Сериализатор для загрузки файлов FNS."""
files = serializers.ListField(
child=serializers.FileField(),
allow_empty=False,
help_text="Список файлов для загрузки (fin_*.xlsx)",
)
def validate_files(self, value):
"""Валидация файлов."""
import re
pattern = re.compile(r"^fin_\d+_\d{13,15}\.xlsx$")
for file in value:
if not pattern.match(file.name):
raise serializers.ValidationError(
f"Неверный формат имени файла: {file.name}. "
"Ожидается: fin_{{id}}_{{ogrn}}.xlsx"
)
return value