3
from typing import Optional
5
from sqlalchemy import Sequence, Uuid, ForeignKey, DateTime, UniqueConstraint, ARRAY, \
7
from sqlalchemy.orm import relationship, mapped_column, Mapped
10
from app.inventory.location.enums import LocationClass, PutawayStrategy
11
from app.inventory.location.models import Location
12
from app.inventory.order.enums.order_enum import MoveStatus, OrderClass, BackOrderAction, ReservationMethod, \
13
OrderStatus, MoveType, SuggestType, SuggestStatus
14
from app.inventory.quant.models import Lot, Quant
15
from core.db import Base
16
from core.db.mixins import AllMixin, CreatedEdited
17
from core.db.types import ids
20
class OrderType(Base, AllMixin, CreatedEdited):
22
Order Type - Тип складского задания, определяет поведение складсхих заданий при создании или выполнении
24
-+ Какие зоны источника складское задание будет использовать для резервирования по умолчанию( может быть пустым, значит задается либо вручную, либо согласно стратегии для выбранного товара)
25
-+ Список зон исключения для поиска зон обратная аналоги с предыдущим утверждением
26
-+ Какие зоны назначение складское задание будет использовать по умолчанию( может быть пустым, значит задается либо вручную, либо согласно стратегии для выбранного товара)
27
-+ Список зон исключения для поиска зон обратная аналоги с предыдущим утверждением
28
-+ Какое складское задание создавать на остаток, если задание выполнено не в полном обьеме
29
-+ Метод резервирования (при утверждении, вручную, на опередленную дату и время, или за определенное время до начала запланированной)
30
-+ Список зон (по порядку) в которых задание будет искать товар (не выбрано, значит все внутренние зоны)
31
- Типы упаковки по порядку с которыми работает Ордер, например если ничего не выбрано - Ордер ищет обсалютно все товары в выбранных зонах/локация или только в определенных типах упаковки
32
если выбрано, то будет искать в рамках данных упаковок и в той последовательности (ВАЖНО!) типы упаковках могут быть строго заданы в локациях, тогда приоритетом будет правила локации
33
-+ Исключающие типы упаковок
34
-+ Гомогенность - это означает, что в упаковки сборщика не может быть 1 товар из 2х разных партий, те что бы физически партии не могли быть в 1 местоположении
35
- Разрешать на ходу создавать упаковки для товаров, те кладовщик может на ресурсе создавтаь упаковки для товаров
36
-+ Список допустимых типов упаковок
37
-+ Список исключающих типов упаковок
38
-+ Можно ли создавать данный тип ордера вручную
39
-+ Можно ли Overdelivery (когда принес больше чем мог)
40
-+ Может ли быть Оверколичество (когда например в складском задании физически оказалось товар больше, чем предпологалось)
41
-+ Создатель/Исполнители
43
Так же важно, что массив движений(Move) по сути своей наследует все спецификацию Order
45
__tablename__ = "order_type"
47
UniqueConstraint('title', 'company_id', name='_order_type_companyid_title_uc'),
49
lsn_seq = Sequence(f'order_type_lsn_seq')
50
prefix: Mapped[str] = mapped_column(index=True)
51
title: Mapped[str] = mapped_column(index=True)
52
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, index=True, default=uuid.uuid4)
53
order_class: Mapped[OrderClass]
54
allowed_location_src_ids: Mapped[ids] = mapped_column(index=True)
55
exclude_location_src_ids: Mapped[ids] = mapped_column(index=True)
56
allowed_location_dest_ids: Mapped[ids] = mapped_column(index=True)
57
exclude_location_dest_ids: Mapped[ids] = mapped_column(index=True)
58
allowed_location_type_src_ids: Mapped[ids] = mapped_column(index=True)
59
exclude_location_type_src_ids: Mapped[ids] = mapped_column(index=True)
60
allowed_location_type_dest_ids: Mapped[ids] = mapped_column(index=True)
61
exclude_location_type_dest_ids: Mapped[ids] = mapped_column(index=True)
62
allowed_location_class_src_ids: Mapped[Optional[ids]] = mapped_column(ARRAY(String), server_default='{}',index=True)
63
exclude_location_class_src_ids: Mapped[Optional[ids]] = mapped_column(ARRAY(String), server_default='{}', index=True)
64
allowed_location_class_dest_ids: Mapped[Optional[ids]] = mapped_column(ARRAY(String), server_default='{}', index=True)
65
exclude_location_class_dest_ids: Mapped[Optional[ids]] = mapped_column(ARRAY(String), server_default='{}', index=True)
66
order_type_id: Mapped[Optional[uuid.UUID]] = mapped_column(ForeignKey("order_type.id", ondelete='CASCADE'))
67
backorder_action_type: Mapped[BackOrderAction] = mapped_column(default=BackOrderAction.ASK)
68
store_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid, index=True)
69
partner_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid, index=True)
70
reservation_method: Mapped[ReservationMethod] = mapped_column(default=ReservationMethod.AT_CONFIRM)
71
reservation_time_before: Mapped[Optional[int]] = mapped_column(default=0)
72
allowed_package_ids: Mapped[Optional[ids]] = mapped_column(index=True)
73
exclude_package_ids: Mapped[Optional[ids]] = mapped_column(index=True)
74
is_homogeneity: Mapped[bool]
75
is_allow_create_package: Mapped[bool]
76
is_can_create_order_manualy: Mapped[bool]
77
is_overdelivery: Mapped[bool]
79
strategy: Mapped['PutawayStrategy'] = mapped_column(default=PutawayStrategy.FEFO)
83
class Order(Base, AllMixin, CreatedEdited):
85
это складское задание(ордер), которое обьединяет в себе как общий документ основание
86
так и складские движения (Move)
87
Также в Ордере могут быть такие уточнения как
89
__tablename__ = "order"
91
UniqueConstraint('external_number', 'company_id', name='_order_companyid_external_number_uc'),
93
lsn_seq = Sequence(f'order_lsn_seq')
94
number: Mapped[str] = mapped_column(index=True)
95
order_type_id: Mapped[uuid.UUID] = mapped_column(ForeignKey('order_type.id', ondelete='CASCADE'))
96
order_type_rel: Mapped[OrderType] = relationship(lazy='selectin')
97
order_id: Mapped[Optional[uuid.UUID]] = mapped_column(ForeignKey("order.id", ondelete="CASCADE"))
98
external_number: Mapped[Optional[str]]
99
store_id: Mapped[uuid.UUID] = mapped_column(Uuid, index=True)
100
partner_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid, index=True)
101
lot_id: Mapped[Optional['Lot']] = mapped_column(ForeignKey("lot.id", ondelete="SET NULL"))
102
origin_type: Mapped[Optional[str]] = mapped_column(index=True)
103
origin_number: Mapped[Optional[str]] = mapped_column(index=True)
104
planned_datetime: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(timezone=True))
105
actual_datetime: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(timezone=True))
106
expiration_datetime: Mapped[Optional[datetime.datetime]] = mapped_column(DateTime(timezone=True))
107
user_ids: Mapped[Optional[ids]] = mapped_column(index=True)
108
description: Mapped[Optional[str]]
109
status: Mapped['OrderStatus'] = mapped_column(default=OrderStatus.DRAFT)
110
move_list_rel: Mapped[Optional[list["Move"]]] = relationship(back_populates="order_rel", lazy="selectin")
112
def __init__(self, **kwargs):
114
Разрешает экстра поля, но удаляет, если их нет в табличке
116
allowed_args = self.__mapper__.class_manager
117
kwargs = {k: v for k, v in kwargs.items() if k in allowed_args}
118
super().__init__(**kwargs)
123
class Move(Base, AllMixin, CreatedEdited):
125
Move - это часть Order, но определяющее уже конкретную позицию товара
127
__tablename__ = "move"
128
lsn_seq = Sequence(f'move_lsn_seq')
129
type: Mapped[MoveType]
130
move_id: Mapped[Optional[uuid.UUID]] = mapped_column(ForeignKey("move.id", ondelete='RESTRICT'))
131
store_id: Mapped[uuid.UUID] = mapped_column(Uuid, index=True)
132
order_type_id: Mapped[uuid.UUID] = mapped_column(ForeignKey('order_type.id', ondelete='RESTRICT'), nullable=True)
133
order_id: Mapped[uuid.UUID] = mapped_column(ForeignKey('order.id', ondelete='RESTRICT'), nullable=True)
134
order_rel: Mapped[Order] = relationship(back_populates='move_list_rel')
135
location_src_id: Mapped[Optional[Location]] = mapped_column(ForeignKey("location.id", ondelete="SET NULL"))
136
location_dest_id: Mapped[Optional[Location]] = mapped_column(ForeignKey("location.id", ondelete="SET NULL"))
137
lot_id: Mapped[Optional['Lot']] = mapped_column(ForeignKey("lot.id", ondelete="SET NULL"))
138
location_id: Mapped[Optional[Location]] = mapped_column(ForeignKey("location.id", ondelete="SET NULL"))
140
product_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid, index=True, nullable=True)
141
partner_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid, index=True, nullable=True)
142
quantity: Mapped[float]
143
uom_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid, index=True, nullable=False)
144
quant_src_id: Mapped[Optional['Quant']] = mapped_column(ForeignKey("quant.id", ondelete="SET NULL"), index=True)
145
quant_dest_id: Mapped[Optional['Quant']] = mapped_column(ForeignKey("quant.id", ondelete="SET NULL"), index=True)
146
status: Mapped[MoveStatus] = mapped_column(default=MoveStatus.CREATED)
147
suggest_list_rel: Mapped[Optional[list["Suggest"]]] = relationship(lazy="selectin")
151
class Suggest(Base, AllMixin):
153
Suggest Саджест, это набор минимальных д ействий для [[Move]] выполнив который Move будет выполнен, например
155
__tablename__ = "suggest"
156
lsn_seq = Sequence(f'suggest_lsn_seq')
157
move_id: Mapped[uuid.UUID] = mapped_column(ForeignKey('move.id', ondelete='CASCADE'), index=True)
158
priority: Mapped[int]
159
type: Mapped[SuggestType]
160
value: Mapped[Optional[str]]
161
result_value: Mapped[Optional[str]]
162
user_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid, index=True)
163
status: Mapped[SuggestStatus] = mapped_column(default=SuggestStatus.WAITING, index=True)
165
class MoveLog(Base, AllMixin):
167
MoveLog - это любое изменение остатка товаров как с точки зрения резервирования остатка, так и с точки зрения прибытия/выбития
168
MoveLog создается при действиях Move, когда обьект изменяет расчет
170
Если создается Move на 10 товаров A, и в момент смены статуса на confirmed создается движеление MoveLog с типом reserve на 10 пирожков,
171
при условии, что все эти пирожки были доступны для резерва, и установися статус confirmed в обьекте Move
172
Если было доступно только 5, то создасться MoveLog движение на 5 пирожков, при этом статус документа Move будет partially
173
Когда же Move попытается сменить статус На Done - обсалютно ВСЕ документы по нему сторнируются (создаются движения с обратным знаком) и
174
Создаются движения с типом get - на ячейке источнике и put на ячейке назначения
175
ВАЖНО: на ячейка с типом external движения не создаются TODO: правда ли не надо?
177
TODO: Нужно ли делать MoveLog при перемещении package???? кажется что только при выбытии/прибытии упаковки на склад
180
__tablename__ = "move_log"
181
lsn_seq = Sequence(f'move_log_lsn_seq')
182
product_id: Mapped[uuid.UUID] = mapped_column(Uuid, index=True)
183
store_id: Mapped[uuid.UUID] = mapped_column(Uuid, index=True)
184
location_class: Mapped[LocationClass] = mapped_column(index=True)
185
location_type_id: Mapped[uuid.UUID] = mapped_column(ForeignKey('location_type.id', ondelete='SET NULL'), index=True)
186
location_id: Mapped[Optional[uuid.UUID]] = mapped_column(ForeignKey("location.id", ondelete="SET NULL"), index=True)
187
lot_id: Mapped[Optional[uuid.UUID]] = mapped_column(ForeignKey("lot.id", ondelete="SET NULL"), index=True)
188
partner_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid, index=True, nullable=True)
189
quantity: Mapped[float]
190
reserved_quantity: Mapped[float]