Ты — архитектор-разработчик ERP-системы с опытом проектирования складского и операционного учёта.
Задача: Перепроектировать логику учёта запчастей, резервирования и списания из рекламаций так, чтобы система была:
консистентной,
масштабируемой,
безопасной с точки зрения остатков,
без отрицательных физических остатков.
Упрощённые, временные и компромиссные решения не допускаются. Проектировать необходимо «как правильно».
Каталог запчастей — справочник, а не источник остатков.
В каталоге запрещено хранить:
фактические остатки,
зарезервированные остатки,
отрицательные значения.
Физические остатки не могут быть отрицательными ни при каких сценариях.
Любое изменение количества должно быть выражено через операции (движения).
Нехватка запчастей учитывается отдельной сущностью, а не минусами.
Назначение: справочник.
Поля:
part_id / sku
цены и тарифы
нормативные коды
min_stock
метаданные
❌ Не хранит количества.
2.2. PartOrder (Заказ / партия)
Назначение: физическое поступление.
Поля:
order_id
part_id
ordered_qty
available_qty
with_documents (bool)
status (ordered / in_stock / issued)
Инвариант:
available_qty >= 0
2.3. InventoryMovement (Движение)
Назначение: единственный механизм изменения остатков.
Поля:
movement_id
part_id
qty
movement_type (receipt, reserve, issue, reserve_cancel)
source_type (order / reclamation)
source_id
with_documents
timestamp
Остатки рассчитываются агрегированием движений.
2.4. Reservation (логическая модель)
Реализуется через движения типа reserve.
Назначение:
уменьшение свободного остатка,
предотвращение двойного использования,
отделение резерва от физического списания.
2.5. Shortage (Дефицит)
Назначение: фиксация неудовлетворённой потребности.
Поля:
shortage_id
part_id
with_documents
required_qty
reserved_qty
missing_qty
reclamation_id
status (open / closed)
❗ Не влияет напрямую на физический остаток.
Пользователь выбирает part_id, количество и with_documents.
Система рассчитывает:
свободный остаток = физический остаток − активные резервы.
Выполняется:
резервирование доступного количества (reserve movement),
создание Shortage, если required_qty > free_qty.
Физический остаток не уменьшается.
Списание возможно только при наличии резерва.
При списании:
создаётся движение issue,
резерв закрывается или уменьшается.
Остаток партии уменьшается, но не уходит в минус.
При создании PartOrder:
система ищет открытые Shortage по part_id + with_documents.
Поступившее количество:
резервируется под дефициты (FIFO / по дате),
уменьшает missing_qty.
При missing_qty = 0:
дефицит закрывается (status = closed).
Формируется автоматически, без ручного редактирования.
6.1. Критический уровень
Показывать все Shortage со статусом open.
6.2. Минимальный остаток
Показывать Part, для которых:
free_stock < min_stock
Запрещено:
хранить остатки в каталоге,
использовать отрицательные значения,
списывать детали напрямую из рекламации,
корректировать остатки без InventoryMovement.
Нужно предоставить:
Схему сущностей и связей (ER / логическую модель).
Описание ключевых инвариантов и бизнес-ограничений.
Псевдокод или пошаговые алгоритмы:
резервирования,
списания,
закрытия дефицита.
Перечень существующих механизмов, которые должны быть удалены или переписаны.
Фокус на корректности модели и безопасности данных, а не на UI.