Files
contracts-flask/site/db/dossiers.py
T

126 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""CRUD для досье договоров и доказанных связей с документами.
Досье идентифицируется внутренним UUID, а номер договора и его алиасы
сохраняются как изменяемые утверждения. Поэтому опечатки в документах
не становятся ключом базы и могут быть пересмотрены без переноса данных.
"""
import json
import uuid
from db.connection import execute, query
def _json_value(value, default):
"""Кодировать поля-списки, принимая уже сериализованный JSON без повтора."""
if value is None:
value = default
return value if isinstance(value, str) else json.dumps(value, ensure_ascii=False)
def _decode_dossier(row):
"""Добавить к строке досье Python-представление списка алиасов."""
if not row:
return None
result = dict(row)
try:
result["alias_numbers"] = json.loads(result.get("alias_numbers_json") or "[]")
except (TypeError, json.JSONDecodeError):
result["alias_numbers"] = []
return result
def create_dossier(batch_id, canonical_number, alias_numbers=None,
counterparty_name="", counterparty_inn="",
has_frame_contract=False, status="ACTIVE"):
"""Создать досье и вернуть его запись со стабильным внутренним ID."""
dossier_id = str(uuid.uuid4())
execute(
"""INSERT INTO contract_dossiers
(id, batch_id, canonical_number, alias_numbers_json,
counterparty_name, counterparty_inn, has_frame_contract, status)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)""",
(dossier_id, batch_id, canonical_number,
_json_value(alias_numbers, []), counterparty_name or "",
counterparty_inn or "", int(bool(has_frame_contract)), status),
)
return get_dossier(dossier_id)
def link_document(dossier_id, document_id, role="supplement",
link_status="CONFIRMED", link_reason="", seq_order=0):
"""Создать или обновить связь dossier-document идемпотентно.
Повторная сборка групп не должна создавать дубликаты. При уже имеющейся
связи сохраняется её ID, но актуальные роль, статус, основание и порядок
заменяются значениями нового решения группировщика.
"""
existing = query(
"SELECT id FROM dossier_documents WHERE dossier_id = %s AND document_id = %s",
(dossier_id, document_id),
)
if existing:
link_id = existing[0]["id"]
execute(
"""UPDATE dossier_documents
SET role = %s, link_status = %s, link_reason = %s, seq_order = %s
WHERE id = %s""",
(role, link_status, link_reason or "", seq_order, link_id),
)
else:
link_id = str(uuid.uuid4())
execute(
"""INSERT INTO dossier_documents
(id, dossier_id, document_id, role, link_status, link_reason, seq_order)
VALUES (%s, %s, %s, %s, %s, %s, %s)""",
(link_id, dossier_id, document_id, role, link_status,
link_reason or "", seq_order),
)
rows = query("SELECT * FROM dossier_documents WHERE id = %s", (link_id,))
return rows[0] if rows else None
def list_dossiers_by_batch(batch_id):
"""Получить досье batch вместе с упорядоченным списком документов."""
dossiers = query(
"SELECT * FROM contract_dossiers WHERE batch_id = %s ORDER BY created_at, id",
(batch_id,),
)
result = []
for raw_dossier in dossiers:
dossier = _decode_dossier(raw_dossier)
dossier["documents"] = query(
"""SELECT dd.*, d.filename, d.doc_type, d.own_number, d.parent_number,
d.doc_date, d.counterparty, d.zip_source
FROM dossier_documents dd
JOIN documents d ON d.id = dd.document_id
WHERE dd.dossier_id = %s
ORDER BY dd.seq_order, d.created_at, d.id""",
(dossier["id"],),
)
result.append(dossier)
return result
def get_dossier(dossier_id):
"""Получить досье по внутреннему ID, не загружая список документов."""
rows = query("SELECT * FROM contract_dossiers WHERE id = %s", (dossier_id,))
return _decode_dossier(rows[0]) if rows else None
def list_dossier_documents(dossier_id):
"""Вернуть документы досье в установленном порядке вместе с ролью связи."""
return query(
"""SELECT dd.*, d.filename, d.status AS document_status, d.doc_type,
d.own_number, d.parent_number, d.doc_date, d.counterparty,
d.zip_source
FROM dossier_documents dd
JOIN documents d ON d.id = dd.document_id
WHERE dd.dossier_id = %s
ORDER BY dd.seq_order, d.created_at, d.id""",
(dossier_id,),
)
def clear_batch_dossiers(batch_id):
"""Удалить все досье batch; таблицы с ON DELETE CASCADE очистятся вместе с ними."""
return execute("DELETE FROM contract_dossiers WHERE batch_id = %s", (batch_id,))