Files
lux_fiscal/backend/app/db/models/order.py
lauadminandClaude Opus 5.5 56ac0fc370 Allow editing orders in the order card
Cashiers can edit recipient, TTN, notes, goods and total in the order
modal and save via PATCH /orders/{id}. Edited orders get edited_at and
are no longer overwritten by CRM sync. Editing is blocked once a receipt
exists; changing the TTN resets Nova Poshta tracking fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 17:23:41 +03:00

57 lines
3.2 KiB
Python

"""Локальная копия заказов, синхронизированная из CRM.
Живёт отдельно от live-проксирования (`app/services/crm/`), чтобы кассир мог
скрыть ненужный заказ (soft-delete) и он не появился снова при следующей
синхронизации, и чтобы отличать заказы без чека от уже пробитых.
"""
from __future__ import annotations
from datetime import datetime
from typing import Any
from sqlalchemy import BigInteger, Boolean, DateTime, String, Text
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column
from app.db.base import Base, TimestampMixin
class Order(TimestampMixin, Base):
__tablename__ = "orders"
# PK — собственный ID заказа в CRM, а не сгенерированный UUID: синхронизация
# делает upsert по нему.
id: Mapped[str] = mapped_column(String(32), primary_key=True)
create_date_time: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
recipient_name: Mapped[str | None] = mapped_column(String(255))
recipient_phone: Mapped[str | None] = mapped_column(String(32))
recipient_email: Mapped[str | None] = mapped_column(String(320))
waybill_number: Mapped[str | None] = mapped_column(String(64))
notes: Mapped[str | None] = mapped_column(Text)
# Деньги — integer копейки (конвенция проекта), а не строка/float.
total_amount_kopecks: Mapped[int] = mapped_column(BigInteger, nullable=False)
# Товарные позиции — снимок из CRM для отображения в модалке. Построчно в
# копейки не переводится: не участвует в денежных расчётах, только в показе.
goods: Mapped[list[dict[str, Any]]] = mapped_column(JSONB, nullable=False, default=list)
is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)
deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
# Заказ правили вручную в карточке — синхронизация из CRM его больше не
# перезаписывает, иначе правки кассира затирались бы при следующем открытии.
edited_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
# Заполняется будущей интеграцией с Checkbox — сейчас всегда NULL.
receipt_created_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
# Статус ТТН Nova Poshta — опрашивается ARQ worker'ом раз в минуту для
# заказов без чека, у которых есть номер ТТН (см. app/worker.py).
np_status: Mapped[str | None] = mapped_column(String(255))
np_status_code: Mapped[str | None] = mapped_column(String(16))
np_cod_amount_kopecks: Mapped[int | None] = mapped_column(BigInteger)
np_payment_status: Mapped[str | None] = mapped_column(String(32))