lavkach3

Форк
0
190 строк · 17.2 Кб
1
import datetime
2
import uuid
3
from typing import Optional
4

5
from sqlalchemy import Sequence, Uuid, ForeignKey, DateTime, UniqueConstraint, ARRAY, \
6
    String
7
from sqlalchemy.orm import relationship, mapped_column, Mapped
8

9
# from app.inventory.location.models import Location, LocationClass
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
18

19

20
class OrderType(Base, AllMixin, CreatedEdited):
21
    """
22
    Order Type - Тип складского задания, определяет поведение складсхих заданий при создании или выполнении
23
    такие как :
24
     -+ Какие зоны источника складское задание будет использовать для резервирования по умолчанию( может быть пустым, значит задается либо вручную, либо согласно стратегии для выбранного товара)
25
     -+ Список зон исключения для поиска зон обратная аналоги с предыдущим утверждением
26
     -+ Какие зоны назначение складское задание будет использовать по умолчанию( может быть пустым, значит задается либо вручную, либо согласно стратегии для выбранного товара)
27
     -+ Список зон исключения для поиска зон обратная аналоги с предыдущим утверждением
28
     -+ Какое складское задание создавать на остаток, если задание выполнено не в полном обьеме
29
     -+ Метод резервирования (при утверждении, вручную, на опередленную дату и время, или за определенное время до начала запланированной)
30
     -+ Список зон (по порядку) в которых задание будет искать товар (не выбрано, значит все внутренние зоны)
31
     - Типы упаковки по порядку с которыми работает Ордер, например  если ничего не выбрано - Ордер ищет обсалютно все товары в выбранных зонах/локация или только в определенных типах упаковки
32
     если выбрано, то будет искать в рамках данных упаковок и в той последовательности (ВАЖНО!) типы упаковках могут быть строго заданы в локациях, тогда приоритетом будет правила локации
33
     -+ Исключающие типы упаковок
34
     -+ Гомогенность - это означает, что в упаковки сборщика не может быть 1 товар из 2х разных партий, те что бы физически партии не могли быть в 1 местоположении
35
     - Разрешать на ходу создавать упаковки для товаров, те кладовщик может на ресурсе создавтаь упаковки для товаров
36
     -+ Список допустимых типов упаковок
37
     -+ Список исключающих типов упаковок
38
     -+ Можно ли создавать данный тип ордера вручную
39
     -+ Можно ли Overdelivery (когда принес больше чем мог)
40
     -+ Может ли быть Оверколичество (когда например в складском задании физически оказалось товар больше, чем предпологалось)
41
     -+ Создатель/Исполнители
42

43
     Так же важно, что массив движений(Move) по сути своей наследует все спецификацию Order
44
    """
45
    __tablename__ = "order_type"
46
    __table_args__ = (
47
        UniqueConstraint('title', 'company_id', name='_order_type_companyid_title_uc'),
48
    )
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]                                                                                  # Возможно ли Overdelivery
78
    barcode: Mapped[str]                                                                                        # Штрих-код ордера для быстрого доступа
79
    strategy: Mapped['PutawayStrategy'] = mapped_column(default=PutawayStrategy.FEFO)                           # Стратегия комплектования
80

81

82

83
class Order(Base, AllMixin, CreatedEdited):
84
    """
85
    это складское задание(ордер), которое обьединяет в себе как общий документ основание
86
    так и складские движения (Move)
87
    Также в Ордере могут быть такие уточнения как
88
    """
89
    __tablename__ = "order"
90
    __table_args__ = (
91
        UniqueConstraint('external_number', 'company_id', name='_order_companyid_external_number_uc'),
92
    )
93
    lsn_seq = Sequence(f'order_lsn_seq')
94
    number: Mapped[str] = mapped_column(index=True)    # Человекочитаемый номер присвается по формуле - {ГОД(2)}-{МЕСЯЦ}-{ДЕНЬ}-{LSN}
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")
111

112
    def __init__(self, **kwargs):
113
        """
114
            Разрешает экстра поля, но удаляет, если их нет в табличке
115
        """
116
        allowed_args = self.__mapper__.class_manager  # returns a dict
117
        kwargs = {k: v for k, v in kwargs.items() if k in allowed_args}
118
        super().__init__(**kwargs)
119

120

121

122

123
class Move(Base, AllMixin, CreatedEdited):
124
    """
125
    Move - это часть Order, но определяющее уже конкретную позицию товара
126
    """
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"))
139
    # ONE OF Возможно либо location_id либо product_id
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]     # Если перемещение кпаковки, то всегда 0
143
    uom_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid, index=True, nullable=False) # Если перемещение упаковкой то None
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")
148

149

150

151
class Suggest(Base, AllMixin):
152
    """
153
    Suggest Саджест, это набор минимальных д ействий для  [[Move]] выполнив который Move будет выполнен, например
154
    """
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)
164

165
class MoveLog(Base, AllMixin):
166
    """
167
    MoveLog - это любое изменение остатка товаров как с точки зрения резервирования остатка, так и с точки зрения прибытия/выбития
168
    MoveLog создается при действиях Move, когда обьект изменяет расчет
169
    Например:
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: правда ли не надо?
176

177
    TODO: Нужно ли делать MoveLog при перемещении package???? кажется что только при выбытии/прибытии упаковки на склад
178

179
    """
180
    __tablename__ = "move_log"
181
    lsn_seq = Sequence(f'move_log_lsn_seq')
182
    product_id: Mapped[uuid.UUID] = mapped_column(Uuid, index=True)  # ForeignKey("basic.product.id")
183
    store_id: Mapped[uuid.UUID] = mapped_column(Uuid, index=True)  # ForeignKey("basic.project.id")
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]
191

192

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.