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:
@@ -1,7 +1,110 @@
|
||||
"""
|
||||
Views для приложения парсеров.
|
||||
|
||||
TODO: Добавить views по мере необходимости.
|
||||
"""
|
||||
|
||||
# Views будут добавлены по мере разработки конкретных парсеров
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
|
||||
from apps.parsers.models import FinancialReport
|
||||
from apps.parsers.serializers import (
|
||||
FinancialReportDetailSerializer,
|
||||
FinancialReportSerializer,
|
||||
FNSFileUploadSerializer,
|
||||
)
|
||||
from apps.parsers.tasks import process_fns_file
|
||||
from django.conf import settings
|
||||
from rest_framework import status
|
||||
from rest_framework.parsers import MultiPartParser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
|
||||
|
||||
class FinancialReportViewSet(ReadOnlyModelViewSet):
|
||||
"""
|
||||
API для просмотра финансовых отчетов ФНС.
|
||||
|
||||
list:
|
||||
Получить список всех отчетов.
|
||||
Поддерживает фильтрацию по: ogrn, external_id, status.
|
||||
|
||||
retrieve:
|
||||
Получить детальную информацию об отчете, включая все строки.
|
||||
"""
|
||||
|
||||
queryset = FinancialReport.objects.all().order_by("-created_at")
|
||||
filterset_fields = ["ogrn", "external_id", "status", "source"]
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action == "retrieve":
|
||||
return FinancialReportDetailSerializer
|
||||
return FinancialReportSerializer
|
||||
|
||||
|
||||
class FNSReportUploadView(APIView):
|
||||
"""
|
||||
API для загрузки файлов бухгалтерской отчетности ФНС.
|
||||
|
||||
POST:
|
||||
Пакетная загрузка файлов.
|
||||
Файлы сохраняются во временную директорию и ставятся в очередь
|
||||
на обработку через Celery.
|
||||
|
||||
Request:
|
||||
multipart/form-data с полем 'files' (можно несколько файлов)
|
||||
|
||||
Response:
|
||||
{
|
||||
"queued": 3,
|
||||
"skipped": 1,
|
||||
"task_ids": ["uuid1", "uuid2", "uuid3"]
|
||||
}
|
||||
"""
|
||||
|
||||
parser_classes = [MultiPartParser]
|
||||
|
||||
def post(self, request):
|
||||
serializer = FNSFileUploadSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
files = serializer.validated_data["files"]
|
||||
task_ids = []
|
||||
queued = 0
|
||||
skipped = 0
|
||||
|
||||
# Создаём директорию для загрузки
|
||||
upload_dir = Path(settings.FNS_WATCH_DIRECTORY)
|
||||
upload_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
from apps.parsers.services import FNSReportService
|
||||
|
||||
for file in files:
|
||||
# Вычисляем хеш файла
|
||||
file_content = file.read()
|
||||
file_hash = hashlib.sha256(file_content).hexdigest()
|
||||
file.seek(0)
|
||||
|
||||
# Проверяем дубликат
|
||||
if FNSReportService.exists_by_hash(file_hash):
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
# Сохраняем файл
|
||||
file_path = upload_dir / file.name
|
||||
with open(file_path, "wb") as f:
|
||||
for chunk in file.chunks():
|
||||
f.write(chunk)
|
||||
|
||||
# Ставим в очередь
|
||||
task = process_fns_file.delay(str(file_path))
|
||||
task_ids.append(task.id)
|
||||
queued += 1
|
||||
|
||||
return Response(
|
||||
{
|
||||
"queued": queued,
|
||||
"skipped": skipped,
|
||||
"task_ids": task_ids,
|
||||
},
|
||||
status=status.HTTP_202_ACCEPTED,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user