Merge remote-tracking branch 'origin/cards'

This commit is contained in:
2025-03-11 22:38:50 +03:00
72 changed files with 4534 additions and 3286 deletions

View File

@@ -1,10 +1,14 @@
from sqlalchemy.orm import configure_mappers
from .auth import *
from .project import *
from .module import *
from .board import *
from .status import *
from .deal import *
from .attribute import *
from .card import *
from .card_tag import *
from .auth import *
from .card import *
from .client import *
from .service import *
from .product import *
@@ -15,7 +19,7 @@ from .marketplace import *
from .payroll import *
from .billing import *
from .marketplace_products import *
from .deal_group import *
from .card_group import *
from .transaction import *
from .residues import *
from .shipping import *

102
models/attribute.py Normal file
View File

@@ -0,0 +1,102 @@
import pickle
from typing import TYPE_CHECKING
from sqlalchemy import ForeignKey, Table, Column, UniqueConstraint, Index
from sqlalchemy.orm import Mapped, mapped_column, relationship
from models.base import BaseModel
if TYPE_CHECKING:
from models import Project, BaseModel, Card
project_attribute = Table(
'project_attribute',
BaseModel.metadata,
Column('project_id', ForeignKey('projects.id')),
Column('attribute_id', ForeignKey('attributes.id')),
)
class AttributeType(BaseModel):
__tablename__ = 'attribute_types'
id: Mapped[int] = mapped_column(primary_key=True)
type: Mapped[str] = mapped_column(nullable=False, unique=True)
name: Mapped[str] = mapped_column(nullable=False, unique=True)
is_deleted: Mapped[bool] = mapped_column(default=False)
attributes: Mapped['Attribute'] = relationship(
'Attribute',
back_populates='type',
lazy='noload',
)
class Attribute(BaseModel):
__tablename__ = 'attributes'
id: Mapped[int] = mapped_column(primary_key=True)
label: Mapped[str] = mapped_column(nullable=False)
name: Mapped[str] = mapped_column(nullable=False, index=True)
is_applicable_to_group: Mapped[bool] = mapped_column(
default=False,
comment='Применять ли изменения атрибута карточки ко всем карточкам в группе',
)
is_nullable: Mapped[bool] = mapped_column(default=False, nullable=False)
default_value: Mapped[bytes] = mapped_column(nullable=True)
is_deleted: Mapped[bool] = mapped_column(default=False)
description: Mapped[str] = mapped_column(default="", nullable=False)
projects: Mapped[list['Project']] = relationship(
'Project',
uselist=True,
secondary='project_attribute',
back_populates='attributes',
lazy='noload',
)
type_id: Mapped[int] = mapped_column(ForeignKey('attribute_types.id'), nullable=False)
type: Mapped[AttributeType] = relationship(
'AttributeType',
back_populates='attributes',
lazy='joined',
)
card_attributes: Mapped[list['CardAttribute']] = relationship(
'CardAttribute',
uselist=True,
lazy='noload',
back_populates='attribute',
)
class CardAttribute(BaseModel):
__tablename__ = 'card_attributes'
id: Mapped[int] = mapped_column(primary_key=True)
value: Mapped[bytes] = mapped_column(nullable=True)
card_id: Mapped[int] = mapped_column(ForeignKey('cards.id'), nullable=False)
card: Mapped['Card'] = relationship(
'Card',
back_populates='attributes',
lazy='noload',
)
attribute_id: Mapped[int] = mapped_column(ForeignKey('attributes.id'), nullable=False)
attribute: Mapped[Attribute] = relationship(
'Attribute',
back_populates='card_attributes',
lazy='joined',
)
__table_args__ = (
UniqueConstraint('card_id', 'attribute_id', name='uq_card_id_attribute_id'),
Index('idx_card_id_attribute_id', 'card_id', 'attribute_id', unique=True)
)
def set_value(self, value):
self.value = pickle.dumps(value)
def get_value(self):
return pickle.loads(self.value)

View File

@@ -10,7 +10,7 @@ from models.work_shifts import WorkShift
if TYPE_CHECKING:
from models.payroll import PayRate, PaymentRecord
from models import Deal, DealEmployees
from models import Card, CardEmployees
role_permissions = Table(
'role_permissions',
@@ -115,7 +115,7 @@ class User(BaseModel):
foreign_keys="WorkShift.user_id"
)
managed_deals: Mapped[list["Deal"]] = relationship(
managed_cards: Mapped[list["Card"]] = relationship(
back_populates="manager",
uselist=True,
)
@@ -127,7 +127,7 @@ class User(BaseModel):
cascade="all, delete-orphan"
)
deals: Mapped[list['DealEmployees']] = relationship(
cards: Mapped[list['CardEmployees']] = relationship(
back_populates='user',
lazy='selectin'
)

View File

@@ -35,14 +35,17 @@ class BarcodeTemplate(BaseModel):
__tablename__ = 'barcode_templates'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
name = Column(String, nullable=False, index=True, comment='Название шаблона')
attributes = relationship('BarcodeTemplateAttribute',
secondary=barcode_template_attribute_link,
lazy='selectin'
)
additional_attributes = relationship('BarcodeTemplateAdditionalField',
lazy='selectin',
back_populates='barcode_template',
cascade="all, delete")
attributes = relationship(
'BarcodeTemplateAttribute',
secondary=barcode_template_attribute_link,
lazy='selectin',
)
additional_attributes = relationship(
'BarcodeTemplateAdditionalField',
lazy='selectin',
back_populates='barcode_template',
cascade="all, delete",
)
additional_field = Column(String, nullable=True, comment='Дополнительное поле')
is_default = Column(Boolean, nullable=False, default=False, comment='По умолчанию')

View File

@@ -7,17 +7,19 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship
from models import BaseModel
if TYPE_CHECKING:
from models import Deal, DealGroup
from models import Card, CardGroup
class DealBillRequest(BaseModel):
__tablename__ = 'deal_bill_requests'
class CardBillRequest(BaseModel):
__tablename__ = 'card_bill_requests'
deal_id: Mapped[int] = mapped_column(ForeignKey('deals.id'),
nullable=False,
primary_key=True,
unique=True)
deal: Mapped['Deal'] = relationship(back_populates='bill_request')
card_id: Mapped[int] = mapped_column(
ForeignKey('cards.id'),
nullable=False,
primary_key=True,
unique=True,
)
card: Mapped['Card'] = relationship(back_populates='bill_request')
created_at: Mapped[datetime.datetime] = mapped_column(nullable=False)
paid: Mapped[bool] = mapped_column(nullable=False, default=False)
@@ -29,11 +31,13 @@ class DealBillRequest(BaseModel):
class GroupBillRequest(BaseModel):
__tablename__ = 'group_bill_requests'
group_id: Mapped[int] = mapped_column(ForeignKey('deal_groups.id'),
nullable=False,
primary_key=True,
unique=True)
group: Mapped['DealGroup'] = relationship(back_populates='bill_request')
group_id: Mapped[int] = mapped_column(
ForeignKey('card_groups.id'),
nullable=False,
primary_key=True,
unique=True,
)
group: Mapped['CardGroup'] = relationship(back_populates='bill_request')
created_at: Mapped[datetime.datetime] = mapped_column(nullable=False)
paid: Mapped[bool] = mapped_column(nullable=False, default=False)

View File

@@ -4,10 +4,10 @@ from typing import TYPE_CHECKING
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from models import BaseModel
from models.base import BaseModel
if TYPE_CHECKING:
from models import Project, DealStatus, Deal
from models import Project, CardStatus, Card
class Board(BaseModel):
@@ -26,6 +26,6 @@ class Board(BaseModel):
lazy="selectin",
)
deal_statuses: Mapped[list["DealStatus"]] = relationship("DealStatus", back_populates="board", lazy="selectin", cascade="all,delete")
statuses: Mapped[list["CardStatus"]] = relationship("CardStatus", back_populates="board", lazy="selectin", cascade="all,delete")
deals: Mapped[list["Deal"]] = relationship("Deal", uselist=True, back_populates="board", lazy="selectin")
cards: Mapped[list["Card"]] = relationship("Card", uselist=True, back_populates="board", lazy="selectin")

128
models/card.py Normal file
View File

@@ -0,0 +1,128 @@
from datetime import datetime
from typing import Optional, TYPE_CHECKING
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref, Mapped, mapped_column
from models.base import BaseModel
from .marketplace import BaseMarketplace
from .shipping import Pallet, Box
from .shipping_warehouse import ShippingWarehouse
if TYPE_CHECKING:
from . import (
CardBillRequest, User, BaseModel, Board, CardStatus, CardGroup, CardAttribute, Client, CardTag,
CardService as CardServiceModel, CardProduct,
)
class Card(BaseModel):
__tablename__ = 'cards'
# region Base card attributes
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(nullable=False, comment='Название карточки')
comment: Mapped[str] = mapped_column(nullable=False, server_default='', comment='Комментарий')
created_at: Mapped[datetime] = mapped_column(nullable=False, comment='Дата создания')
is_deleted: Mapped[bool] = mapped_column(nullable=False, server_default='0', default=False, comment='Удалена')
is_completed: Mapped[bool] = mapped_column(nullable=False, server_default='0', default=False, comment='Завершена')
lexorank: Mapped[str] = mapped_column(nullable=False, comment='Lexorank', index=True)
board_id: Mapped[int] = mapped_column(ForeignKey('boards.id'), nullable=True, server_default='1')
board: Mapped['Board'] = relationship(
'Board',
back_populates='cards',
)
current_status_id: Mapped[int] = mapped_column(
ForeignKey('card_statuses.id'),
nullable=False,
comment='Текущий статус',
)
status: Mapped['CardStatus'] = relationship(lazy='selectin')
status_history = relationship('CardStatusHistory', back_populates='card', cascade="all, delete-orphan")
attributes: Mapped[list['CardAttribute']] = relationship(
'CardAttribute',
uselist=True,
back_populates='card',
lazy='selectin',
)
group: Mapped[Optional["CardGroup"]] = relationship(
'CardGroup',
secondary='card_relations',
lazy='joined',
back_populates='cards'
)
tags: Mapped[list['CardTag']] = relationship(
'CardTag',
secondary='cards_card_tags',
back_populates='cards',
lazy='selectin',
)
# endregion
# region Attributes handled by modules
# module servicesAndProducts
is_locked: Mapped[bool] = mapped_column(default=False, server_default='0')
is_services_profit_accounted: Mapped[bool] = mapped_column(default=True, server_default='1')
shipping_warehouse_id: Mapped[int] = mapped_column(ForeignKey('shipping_warehouses.id'), nullable=True)
shipping_warehouse: Mapped["ShippingWarehouse"] = relationship()
base_marketplace_key: Mapped[str] = mapped_column(ForeignKey("base_marketplaces.key"), nullable=True)
base_marketplace: Mapped["BaseMarketplace"] = relationship(lazy="joined")
services: Mapped[list['CardServiceModel']] = relationship(
'CardService',
back_populates='card',
cascade="all, delete-orphan",
order_by="desc(CardService.service_id)"
)
products: Mapped[list['CardProduct']] = relationship(
'CardProduct',
back_populates='card',
cascade="all, delete-orphan",
order_by="desc(CardProduct.product_id)"
)
bill_request: Mapped[Optional['CardBillRequest']] = relationship(back_populates='card', lazy='joined')
# module client
client_id: Mapped[Optional[int]] = mapped_column(
ForeignKey('clients.id', ondelete='CASCADE'),
nullable=True,
comment='ID клиента',
)
client: Mapped['Client'] = relationship('Client', backref=backref('cards', cascade="all, delete-orphan"))
# module managers
manager_id: Mapped[int] = mapped_column(ForeignKey('users.id'), nullable=True)
manager: Mapped[Optional["User"]] = relationship(back_populates='managed_cards', lazy='joined')
# module shipment
pallets: Mapped[list[Pallet]] = relationship(back_populates='card', lazy='selectin')
boxes: Mapped[list[Box]] = relationship(back_populates='card', lazy='selectin')
# module employees
employees: Mapped[list['CardEmployees']] = relationship(back_populates='card', lazy='selectin')
# endregion
class CardEmployees(BaseModel):
__tablename__ = 'card_employees'
user_id: Mapped[int] = mapped_column(ForeignKey('users.id'), primary_key=True)
user: Mapped['User'] = relationship('User', back_populates='cards', lazy='selectin')
card_id: Mapped[int] = mapped_column(ForeignKey('cards.id'), primary_key=True)
card: Mapped[Card] = relationship('Card', back_populates='employees', lazy='selectin')
created_at: Mapped[datetime] = mapped_column()

View File

@@ -7,11 +7,11 @@ from models import BaseModel
from models import GroupBillRequest
if TYPE_CHECKING:
from models import Deal
from models import Card
class DealGroup(BaseModel):
__tablename__ = 'deal_groups'
class CardGroup(BaseModel):
__tablename__ = 'card_groups'
id: Mapped[int] = mapped_column(
primary_key=True
)
@@ -21,9 +21,9 @@ class DealGroup(BaseModel):
lexorank: Mapped[str] = mapped_column(
nullable=False
)
deals: Mapped[list["Deal"]] = relationship(
cards: Mapped[list["Card"]] = relationship(
back_populates='group',
secondary='deal_relations'
secondary='card_relations'
)
bill_request: Mapped[Optional['GroupBillRequest']] = relationship(
back_populates='group',
@@ -31,9 +31,9 @@ class DealGroup(BaseModel):
)
deal_relations = Table(
'deal_relations',
card_relations = Table(
'card_relations',
BaseModel.metadata,
Column('deal_id', ForeignKey('deals.id'), primary_key=True, unique=True),
Column('group_id', ForeignKey('deal_groups.id'), primary_key=True)
Column('card_id', ForeignKey('cards.id'), primary_key=True, unique=True),
Column('group_id', ForeignKey('card_groups.id'), primary_key=True)
)

45
models/card_tag.py Normal file
View File

@@ -0,0 +1,45 @@
from typing import TYPE_CHECKING
from sqlalchemy import ForeignKey, Column, Table, UniqueConstraint, Index
from sqlalchemy.orm import mapped_column, Mapped, relationship
from models import BaseModel
if TYPE_CHECKING:
from models import Project, Card
cards_card_tags = Table(
'cards_card_tags',
BaseModel.metadata,
Column('card_id', ForeignKey('cards.id'), primary_key=True),
Column('card_tag_id', ForeignKey('card_tags.id'), primary_key=True),
)
class CardTag(BaseModel):
__tablename__ = 'card_tags'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(nullable=False)
is_deleted: Mapped[bool] = mapped_column(default=False, server_default='0')
project_id: Mapped[int] = mapped_column(
ForeignKey('projects.id'),
nullable=False,
)
project: Mapped['Project'] = relationship(
'Project',
back_populates='tags',
lazy='noload',
)
cards: Mapped[list['Card']] = relationship(
secondary='cards_card_tags',
lazy='noload',
back_populates='tags',
)
__table_args__ = (
Index('idx_card_name_project_id', 'name', 'project_id', 'is_deleted'),
)

View File

@@ -1,70 +1,59 @@
from datetime import datetime
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, Mapped, mapped_column
from models import BaseModel
if TYPE_CHECKING:
from models import ResidualPallet, ResidualBox
from models import ResidualPallet, ResidualBox, Product, BarcodeTemplate, User
class Client(BaseModel):
__tablename__ = 'clients'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
name = Column(String, nullable=False, unique=True, comment='Название клиента')
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(nullable=False, unique=True, comment='Название клиента')
# TODO replace with additional model
company_name = Column(String,
nullable=False,
server_default='',
comment='Название компании')
company_name: Mapped[str] = mapped_column(
nullable=False,
server_default='',
comment='Название компании',
)
created_at = Column(DateTime, nullable=False, comment='Дата создания')
created_at: Mapped[datetime] = mapped_column(nullable=False, comment='Дата создания')
products = relationship('Product', back_populates='client')
details = relationship('ClientDetails', uselist=False, back_populates='client', cascade='all, delete',
lazy='joined')
products: Mapped[list['Product']] = relationship('Product', back_populates='client')
details: Mapped['ClientDetails'] = relationship(
uselist=False,
back_populates='client',
cascade='all, delete',
lazy='joined',
)
barcode_template_id = Column(Integer, ForeignKey('barcode_templates.id'), nullable=True)
barcode_template = relationship('BarcodeTemplate', lazy='selectin')
barcode_template_id: Mapped[int] = mapped_column(ForeignKey('barcode_templates.id'), nullable=True)
barcode_template: Mapped['BarcodeTemplate'] = relationship('BarcodeTemplate', lazy='selectin')
comment: Mapped[Optional[str]] = mapped_column(nullable=True, server_default=None, comment='Комментарий')
pallets: Mapped[list["ResidualPallet"]] = relationship(back_populates='client', lazy='selectin')
boxes: Mapped[list["ResidualBox"]] = relationship(back_populates='client', lazy='selectin')
pallets: Mapped[list['ResidualPallet']] = relationship(back_populates='client', lazy='selectin')
boxes: Mapped[list['ResidualBox']] = relationship(back_populates='client', lazy='selectin')
class ClientDetails(BaseModel):
__tablename__ = 'client_details'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
id: Mapped[int] = mapped_column(primary_key=True)
client_id = Column(Integer, ForeignKey('clients.id'), unique=True, nullable=False, comment='ID клиента')
client = relationship('Client', back_populates='details', cascade='all, delete', uselist=False)
client_id: Mapped[int] = mapped_column(ForeignKey('clients.id'), unique=True, nullable=False, comment='ID клиента')
client: Mapped[Client] = relationship('Client', back_populates='details', cascade='all, delete', uselist=False)
telegram = Column(String)
phone_number = Column(String)
inn = Column(String)
email = Column(String)
telegram: Mapped[Optional[str]] = mapped_column()
phone_number: Mapped[Optional[str]] = mapped_column()
inn: Mapped[Optional[str]] = mapped_column()
email: Mapped[Optional[str]] = mapped_column()
last_modified_at = Column(DateTime, nullable=False)
last_modified_at: Mapped[datetime] = mapped_column(nullable=False)
modified_by_user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
modified_by_user = relationship('User')
# class ClientContact(BaseModel):
# __tablename__ = 'client_contact'
# id: Mapped[int] = mapped_column(primary_key=True)
#
# client_id: Mapped[int] = mapped_column(ForeignKey('clients.id'))
# client: Mapped["Client"] = relationship('Client', back_populates='users')
#
# first_name: Mapped[str] = mapped_column()
# last_name: Mapped[str] = mapped_column()
#
# telegram: Mapped[str] = mapped_column()
# phone_number: Mapped[str] = mapped_column()
# email: Mapped[str] = mapped_column()
# inn: Mapped[str] = mapped_column()
#
modified_by_user_id: Mapped[int] = mapped_column(ForeignKey('users.id'), nullable=False)
modified_by_user: Mapped['User'] = relationship('User')

View File

@@ -1,106 +0,0 @@
from datetime import datetime
from enum import IntEnum, unique
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean
from sqlalchemy.orm import relationship, backref, Mapped, mapped_column
from models.base import BaseModel
from .marketplace import BaseMarketplace
from .board import Board
from .status import DealStatus
from .shipping import Pallet, Box
from .shipping_warehouse import ShippingWarehouse
if TYPE_CHECKING:
from . import (
DealBillRequest,
DealGroup,
User,
)
class Deal(BaseModel):
__tablename__ = 'deals'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
name = Column(String, nullable=False, comment='Название сделки')
created_at = Column(DateTime, nullable=False, comment='Дата создания')
client_id = Column(Integer, ForeignKey('clients.id', ondelete='CASCADE'), nullable=False, comment='ID клиента')
client = relationship('Client', backref=backref('deals', cascade="all, delete-orphan"))
status_history = relationship('DealStatusHistory', back_populates='deal', cascade="all, delete-orphan")
is_deleted = Column(Boolean, nullable=False, server_default='0', default=False, comment='Удалена')
is_completed = Column(Boolean, nullable=False, server_default='0', default=False, comment='Завершена')
is_locked: Mapped[bool] = mapped_column(default=False, server_default='0')
is_accounted: Mapped[bool] = mapped_column(default=True, server_default='1')
current_status_id: Mapped[int] = mapped_column(
ForeignKey("deal_statuses.id"),
nullable=False,
comment='Текущий статус',
)
status: Mapped["DealStatus"] = relationship(lazy="selectin")
shipping_warehouse_id: Mapped[int] = mapped_column(ForeignKey('shipping_warehouses.id'), nullable=True)
shipping_warehouse: Mapped["ShippingWarehouse"] = relationship()
base_marketplace_key: Mapped[str] = mapped_column(ForeignKey("base_marketplaces.key"), nullable=True)
base_marketplace: Mapped["BaseMarketplace"] = relationship(lazy="joined")
delivery_date: Mapped[Optional[datetime]] = mapped_column(nullable=True)
receiving_slot_date: Mapped[Optional[datetime]] = mapped_column(nullable=True)
services = relationship(
'DealService',
back_populates='deal',
cascade="all, delete-orphan",
order_by="desc(DealService.service_id)"
)
products = relationship(
'DealProduct',
back_populates='deal',
cascade="all, delete-orphan",
order_by="desc(DealProduct.product_id)"
)
board_id: Mapped[int] = mapped_column(ForeignKey('boards.id'), nullable=True, server_default='1')
board: Mapped[Board] = relationship(
"Board",
back_populates="deals",
)
# TODO remake with sequence
lexorank = Column(String, nullable=False, comment='Lexorank', index=True)
comment = Column(String, nullable=False, server_default='', comment='Коментарий к заданию')
bill_request: Mapped[Optional['DealBillRequest']] = relationship(back_populates='deal', lazy='joined')
group: Mapped[Optional["DealGroup"]] = relationship(
'DealGroup',
secondary='deal_relations',
lazy='joined',
back_populates='deals'
)
manager_id: Mapped[int] = mapped_column(ForeignKey('users.id'), nullable=True)
manager: Mapped[Optional["User"]] = relationship(back_populates='managed_deals', lazy='joined')
pallets: Mapped[list[Pallet]] = relationship(back_populates='deal', lazy='selectin')
boxes: Mapped[list[Box]] = relationship(back_populates='deal', lazy='selectin')
employees: Mapped[list['DealEmployees']] = relationship(back_populates='deal', lazy='selectin')
class DealEmployees(BaseModel):
__tablename__ = 'deal_employees'
user_id: Mapped[int] = mapped_column(ForeignKey('users.id'), primary_key=True)
user: Mapped['User'] = relationship('User', back_populates='deals', lazy='selectin')
deal_id: Mapped[int] = mapped_column(ForeignKey('deals.id'), primary_key=True)
deal: Mapped[Deal] = relationship('Deal', back_populates='employees', lazy='selectin')
created_at: Mapped[datetime] = mapped_column()

35
models/module.py Normal file
View File

@@ -0,0 +1,35 @@
from typing import TYPE_CHECKING, Optional
from sqlalchemy import Table, Column, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from models.base import BaseModel
if TYPE_CHECKING:
from models import Project
project_module = Table(
'project_module',
BaseModel.metadata,
Column('project_id', ForeignKey('projects.id')),
Column('module_id', ForeignKey('modules.id')),
)
class Module(BaseModel):
__tablename__ = 'modules'
id: Mapped[int] = mapped_column(primary_key=True)
key: Mapped[str] = mapped_column(unique=True, nullable=False)
label: Mapped[str] = mapped_column(nullable=False)
icon_name: Mapped[Optional[str]] = mapped_column(unique=True, nullable=False)
is_deleted: Mapped[bool] = mapped_column(default=False)
projects: Mapped[list['Project']] = relationship(
'Project',
uselist=True,
secondary='project_module',
back_populates='modules',
lazy='noload',
)

View File

@@ -1,74 +1,93 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional
from sqlalchemy import Column, Integer, String, ForeignKey, Sequence
from sqlalchemy.orm import relationship, Mapped
from sqlalchemy.testing.schema import mapped_column
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, Mapped, mapped_column
from models import BaseModel
from models.base import BaseModel
if TYPE_CHECKING:
from models import Marketplace
from models import Client, BarcodeTemplate, WildberriesProduct, OzonProduct
class Product(BaseModel):
__tablename__ = 'products'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
name = Column(String, nullable=False, index=True)
article = Column(String, nullable=False, default='', server_default='', index=True)
factory_article = Column(String, nullable=False, default='', server_default='', index=True)
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(nullable=False, index=True)
article: Mapped[str] = mapped_column(nullable=False, default='', server_default='', index=True)
factory_article: Mapped[str] = mapped_column(nullable=False, default='', server_default='', index=True)
client_id = Column(Integer, ForeignKey('clients.id'), nullable=False, comment='ID сделки')
client = relationship('Client', back_populates='products')
barcodes = relationship('ProductBarcode', back_populates='product', cascade="all, delete-orphan")
client_id: Mapped[int] = mapped_column(ForeignKey('clients.id'), nullable=False)
client: Mapped['Client'] = relationship('Client', back_populates='products')
barcodes: Mapped[list['ProductBarcode']] = relationship(
'ProductBarcode',
back_populates='product',
cascade='all, delete-orphan',
)
barcode_template_id = Column(Integer, ForeignKey('barcode_templates.id'), nullable=True)
barcode_template = relationship('BarcodeTemplate', lazy='joined')
barcode_template_id: Mapped[Optional[int]] = mapped_column(ForeignKey('barcode_templates.id'), nullable=True)
barcode_template: Mapped['BarcodeTemplate'] = relationship('BarcodeTemplate', lazy='joined')
barcode_image = relationship('ProductBarcodeImage', back_populates='product', lazy='joined', uselist=False)
barcode_image: Mapped['ProductBarcodeImage'] = relationship(
'ProductBarcodeImage',
back_populates='product',
lazy='joined',
uselist=False,
)
# Attributes
# TODO move to another table
brand = Column(String, nullable=True, comment='Бренд')
color = Column(String, nullable=True, comment='Цвет')
composition = Column(String, nullable=True, comment='Состав')
size = Column(String, nullable=True, comment='Размер')
additional_info = Column(String, nullable=True, comment='Дополнительное поле')
images = relationship('ProductImage',
back_populates='product',
lazy='selectin',
cascade="all, delete-orphan")
brand: Mapped[Optional[str]] = mapped_column(nullable=True, comment='Бренд')
color: Mapped[Optional[str]] = mapped_column(nullable=True, comment='Цвет')
composition: Mapped[Optional[str]] = mapped_column(nullable=True, comment='Состав')
size: Mapped[Optional[str]] = mapped_column(nullable=True, comment='Размер')
additional_info: Mapped[Optional[str]] = mapped_column(nullable=True, comment='Дополнительное поле')
images: Mapped[list['ProductImage']] = relationship(
'ProductImage',
back_populates='product',
lazy='selectin',
cascade='all, delete-orphan',
)
wildberries_products = relationship('WildberriesProduct',
back_populates='product',
cascade="all, delete-orphan",
uselist=True)
wildberries_products: Mapped[list['WildberriesProduct']] = relationship(
'WildberriesProduct',
back_populates='product',
cascade='all, delete-orphan',
uselist=True,
)
ozon_products = relationship('OzonProduct',
back_populates='product',
cascade="all, delete-orphan",
uselist=True)
ozon_products: Mapped[list['OzonProduct']] = relationship(
'OzonProduct',
back_populates='product',
cascade='all, delete-orphan',
uselist=True,
)
class ProductImage(BaseModel):
__tablename__ = 'product_images'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
product_id = Column(Integer, ForeignKey('products.id'), nullable=False)
product: Mapped["Product"] = relationship(back_populates='images')
id: Mapped[int] = mapped_column(primary_key=True)
product_id: Mapped[int] = mapped_column(ForeignKey('products.id'), nullable=False)
product: Mapped['Product'] = relationship(back_populates='images')
image_url = Column(String, nullable=False)
image_url: Mapped[str] = mapped_column(nullable=False)
class ProductBarcode(BaseModel):
__tablename__ = 'product_barcodes'
product_id = Column(Integer, ForeignKey('products.id'), nullable=False, comment='ID товара', primary_key=True)
product: Mapped["Product"] = relationship(back_populates='barcodes')
product_id: Mapped[int] = mapped_column(
ForeignKey('products.id'),
nullable=False,
comment='ID товара',
primary_key=True,
)
product: Mapped['Product'] = relationship(back_populates='barcodes')
barcode = Column(String, nullable=False, index=True, comment='ШК товара', primary_key=True)
barcode: Mapped[str] = mapped_column(nullable=False, index=True, comment='ШК товара', primary_key=True)
class ProductBarcodeImage(BaseModel):
__tablename__ = 'product_barcode_images'
product_id = Column(Integer, ForeignKey('products.id'), primary_key=True, comment='ID товара')
product: Mapped["Product"] = relationship(back_populates='barcode_image')
product_id: Mapped[int] = mapped_column(ForeignKey('products.id'), primary_key=True, comment='ID товара')
product: Mapped['Product'] = relationship(back_populates='barcode_image')
filename = Column(String, nullable=False)
filename: Mapped[str] = mapped_column(nullable=False)

View File

@@ -3,22 +3,48 @@ from typing import TYPE_CHECKING
from sqlalchemy.orm import mapped_column, Mapped, relationship
from models import BaseModel
from models.base import BaseModel
if TYPE_CHECKING:
from board import Board
from attribute import Attribute
from module import Module
from card_tag import CardTag
class Project(BaseModel):
__tablename__ = "projects"
__tablename__ = 'projects'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(nullable=False)
created_at: Mapped[datetime] = mapped_column(nullable=False)
is_deleted: Mapped[bool] = mapped_column(default=False)
boards: Mapped[list["Board"]] = relationship(
"Board",
back_populates="project",
lazy="noload",
boards: Mapped[list['Board']] = relationship(
'Board',
back_populates='project',
lazy='noload',
)
attributes: Mapped[list['Attribute']] = relationship(
'Attribute',
secondary='project_attribute',
back_populates='projects',
lazy='selectin',
)
modules: Mapped[list['Module']] = relationship(
'Module',
secondary='project_module',
back_populates='projects',
lazy='selectin',
order_by='asc(Module.id)',
)
tags: Mapped[list['CardTag']] = relationship(
'CardTag',
back_populates='project',
primaryjoin="and_(Project.id == CardTag.project_id, CardTag.is_deleted == False)",
order_by='asc(CardTag.id)',
lazy='selectin',
)

View File

@@ -1,115 +1,138 @@
from sqlalchemy import Table, Column, Integer, ForeignKey, ForeignKeyConstraint, UniqueConstraint, String
from typing import TYPE_CHECKING
from sqlalchemy import Table, Column, ForeignKey, ForeignKeyConstraint, UniqueConstraint
from sqlalchemy.orm import relationship, mapped_column, Mapped
from models import Product
from models.base import BaseModel
deal_product_service_employees = Table(
'deal_product_service_employees',
if TYPE_CHECKING:
from models import Card, Service, User
card_product_service_employees = Table(
'card_product_service_employees',
BaseModel.metadata,
Column('deal_id', primary_key=True),
Column('card_id', primary_key=True),
Column('service_id', primary_key=True),
Column('product_id', primary_key=True),
Column('user_id', ForeignKey('users.id'), primary_key=True),
ForeignKeyConstraint(
['deal_id', 'product_id', 'service_id'],
['deal_product_services.deal_id', 'deal_product_services.product_id', 'deal_product_services.service_id']
['card_id', 'product_id', 'service_id'],
['card_product_services.card_id', 'card_product_services.product_id', 'card_product_services.service_id']
)
)
deal_service_employees = Table(
'deal_service_employees',
card_service_employees = Table(
'card_service_employees',
BaseModel.metadata,
Column('deal_id', primary_key=True),
Column('card_id', primary_key=True),
Column('service_id', primary_key=True),
Column('user_id', ForeignKey('users.id'), primary_key=True),
ForeignKeyConstraint(
['deal_id', 'service_id'],
['deal_services.deal_id', 'deal_services.service_id']
['card_id', 'service_id'],
['card_services.card_id', 'card_services.service_id']
)
)
class DealService(BaseModel):
__tablename__ = 'deal_services'
deal_id = Column(Integer, ForeignKey('deals.id'),
nullable=False,
comment='ID Сделки',
primary_key=True)
deal = relationship('Deal', back_populates='services')
class CardService(BaseModel):
__tablename__ = 'card_services'
card_id: Mapped[int] = mapped_column(
ForeignKey('cards.id'),
comment='ID Сделки',
primary_key=True,
)
card: Mapped['Card'] = relationship('Card', back_populates='services')
service_id = Column(Integer, ForeignKey('services.id'), nullable=False, comment='ID Услуги', primary_key=True)
service = relationship('Service')
service_id: Mapped[int] = mapped_column(ForeignKey('services.id'), nullable=False, comment='ID Услуги', primary_key=True)
service: Mapped['Service'] = relationship('Service')
quantity = Column(Integer, nullable=False, comment='Кол-во услуги')
price = Column(Integer, nullable=False, server_default='0', comment='Цена услуги')
quantity: Mapped[int] = mapped_column(nullable=False, comment='Кол-во услуги')
price: Mapped[int] = mapped_column(nullable=False, server_default='0', comment='Цена услуги')
is_fixed_price: Mapped[bool] = mapped_column(default=False, server_default='0', comment='Фиксированная цена')
employees = relationship('User', secondary=deal_service_employees)
employees: Mapped[list['User']] = relationship('User', secondary=card_service_employees)
__table_args__ = (
UniqueConstraint('deal_id', 'service_id', name='uix_deal_service'),
UniqueConstraint('card_id', 'service_id', name='uix_card_service'),
)
class DealProductService(BaseModel):
__tablename__ = 'deal_product_services'
deal_id = Column(Integer, primary_key=True, nullable=False, comment='ID Сделки')
class CardProductService(BaseModel):
__tablename__ = 'card_product_services'
card_id: Mapped[int] = mapped_column(primary_key=True, nullable=False, comment='ID Сделки')
product_id = Column(Integer, primary_key=True, nullable=False, comment='ID Продукта')
product_id: Mapped[int] = mapped_column(primary_key=True, nullable=False, comment='ID Продукта')
service_id = Column(Integer, ForeignKey('services.id'), primary_key=True, nullable=False, comment='ID Услуги')
service_id: Mapped[int] = mapped_column(
ForeignKey('services.id'),
primary_key=True,
nullable=False,
comment='ID Услуги',
)
price = Column(Integer, nullable=False, comment='Цена услуги')
price: Mapped[int] = mapped_column(nullable=False, comment='Цена услуги')
is_fixed_price: Mapped[bool] = mapped_column(default=False, server_default='0', comment='Фиксированная цена')
deal_product = relationship('DealProduct',
back_populates='services',
primaryjoin="and_(DealProductService.deal_id == DealProduct.deal_id, "
"DealProductService.product_id == DealProduct.product_id)",
foreign_keys=[deal_id, product_id])
card_product: Mapped['CardProduct'] = relationship(
'CardProduct',
back_populates='services',
primaryjoin="and_(CardProductService.card_id == CardProduct.card_id, "
"CardProductService.product_id == CardProduct.product_id)",
foreign_keys=[card_id, product_id],
)
service = relationship('Service',
foreign_keys=[service_id],
lazy='joined'
)
employees = relationship('User',
secondary=deal_product_service_employees,
)
service: Mapped['Service'] = relationship(
'Service',
foreign_keys=[service_id],
lazy='joined',
)
employees: Mapped[list['User']] = relationship(
'User',
secondary=card_product_service_employees,
)
__table_args__ = (
ForeignKeyConstraint(
['deal_id', 'product_id'],
['deal_products.deal_id', 'deal_products.product_id']
['card_id', 'product_id'],
['card_products.card_id', 'card_products.product_id']
),
)
class DealProduct(BaseModel):
__tablename__ = 'deal_products'
deal_id = Column(Integer, ForeignKey('deals.id'), primary_key=True, nullable=False, comment='ID Сделки')
product_id = Column(Integer, ForeignKey('products.id'), primary_key=True, nullable=False, comment='ID Продукта')
quantity = Column(Integer, nullable=False, comment='Кол-во продукта')
comment = Column(String, nullable=False, server_default='', comment='Комментарий к товару')
class CardProduct(BaseModel):
__tablename__ = 'card_products'
card_id: Mapped[int] = mapped_column(ForeignKey('cards.id'), primary_key=True, nullable=False, comment='ID карточки')
product_id: Mapped[int] = mapped_column(
ForeignKey('products.id'),
primary_key=True,
nullable=False,
comment='ID Продукта',
)
quantity: Mapped[int] = mapped_column(nullable=False, comment='Кол-во продукта')
comment: Mapped[str] = mapped_column(nullable=False, server_default='', comment='Комментарий к товару')
deal = relationship('Deal',
back_populates='products',
foreign_keys=[deal_id])
product = relationship(
card: Mapped['Card'] = relationship(
'Card',
back_populates='products',
foreign_keys=[card_id],
)
product: Mapped['Product'] = relationship(
'Product',
lazy='joined',
foreign_keys=[product_id],
)
services = relationship('DealProductService',
back_populates='deal_product',
cascade="all, delete-orphan",
primaryjoin="and_(DealProductService.deal_id == DealProduct.deal_id, "
"DealProductService.product_id == DealProduct.product_id)",
foreign_keys=[DealProductService.deal_id, DealProductService.product_id],
lazy='selectin',
order_by="desc(DealProductService.service_id)"
)
services: Mapped[list['CardProductService']] = relationship(
'CardProductService',
back_populates='card_product',
cascade="all, delete-orphan",
primaryjoin="and_(CardProductService.card_id == CardProduct.card_id, "
"CardProductService.product_id == CardProduct.product_id)",
foreign_keys=[CardProductService.card_id, CardProductService.product_id],
lazy='selectin',
order_by="desc(CardProductService.service_id)"
)
barcode_template_attribute_link = Table(

View File

@@ -1,4 +0,0 @@
from sqlalchemy import Sequence
from models import BaseModel

View File

@@ -1,4 +1,4 @@
from sqlalchemy import Column, Integer, String, ForeignKey, Double, asc, Table
from sqlalchemy import Column, Integer, String, ForeignKey, Double, Table
from sqlalchemy.orm import relationship, mapped_column, Mapped
import enums.service
@@ -14,11 +14,15 @@ services_kit_services = Table(
class Service(BaseModel):
__tablename__ = 'services'
id: Mapped[int] = mapped_column(Integer, autoincrement=True, primary_key=True, index=True)
name = Column(String, nullable=False, comment='Название услуги')
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(nullable=False, comment='Название услуги')
category_id = Column(Integer, ForeignKey('service_categories.id'), nullable=False, comment='ID категории услуги')
category = relationship('ServiceCategory', lazy='joined')
category_id: Mapped[int] = mapped_column(
ForeignKey('service_categories.id'),
nullable=False,
comment='ID категории услуги',
)
category: Mapped['ServiceCategory'] = relationship('ServiceCategory', lazy='joined')
is_deleted: Mapped[bool] = mapped_column(
nullable=False,
server_default='0',
@@ -37,15 +41,18 @@ class Service(BaseModel):
comment='Себестоимость услуги'
)
service_type = Column(Integer,
server_default=f'{enums.service.ServiceType.DEAL_SERVICE}',
nullable=False,
comment='Тип услуги')
price_ranges = relationship('ServicePriceRange',
back_populates='service',
lazy='selectin',
order_by="asc(ServicePriceRange.from_quantity)",
cascade="all, delete-orphan")
service_type: Mapped[int] = mapped_column(
server_default=f'{enums.service.ServiceType.DEAL_SERVICE}',
nullable=False,
comment='Тип услуги',
)
price_ranges: Mapped[list['ServicePriceRange']] = relationship(
'ServicePriceRange',
back_populates='service',
lazy='selectin',
order_by="asc(ServicePriceRange.from_quantity)",
cascade="all, delete-orphan",
)
rank: Mapped[str] = mapped_column(
nullable=False,
server_default='',
@@ -55,25 +62,25 @@ class Service(BaseModel):
class ServicePriceRange(BaseModel):
__tablename__ = 'service_price_ranges'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
service_id = Column(Integer, ForeignKey('services.id'), nullable=False, comment='ID услуги')
service = relationship('Service', back_populates='price_ranges')
from_quantity = Column(Integer, nullable=False, comment='От количества')
to_quantity = Column(Integer, nullable=False, comment='До количества')
price = Column(Double, nullable=False, comment='Цена')
id: Mapped[int] = mapped_column(primary_key=True)
service_id: Mapped[int] = mapped_column(ForeignKey('services.id'), nullable=False, comment='ID услуги')
service: Mapped['Service'] = relationship('Service', back_populates='price_ranges')
from_quantity: Mapped[int] = mapped_column(nullable=False, comment='От количества')
to_quantity: Mapped[int] = mapped_column(nullable=False, comment='До количества')
price: Mapped[float] = mapped_column(Double, nullable=False, comment='Цена')
class ServiceCategory(BaseModel):
__tablename__ = 'service_categories'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
name = Column(String, nullable=False)
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(nullable=False)
is_deleted: Mapped[bool] = mapped_column(
nullable=False,
server_default='0',
comment='Удалена ли категория'
)
deal_service_rank: Mapped[str] = mapped_column(
card_service_rank: Mapped[str] = mapped_column(
nullable=False,
server_default='',
comment='Ранг услуги для сделки'

View File

@@ -5,15 +5,15 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship
from models import BaseModel
if TYPE_CHECKING:
from models import Deal, Product, Client
from models import Card, Product
class Pallet(BaseModel):
__tablename__ = 'pallets'
id: Mapped[int] = mapped_column(primary_key=True)
deal_id: Mapped[int] = mapped_column(ForeignKey('deals.id'))
deal: Mapped['Deal'] = relationship(back_populates='pallets')
card_id: Mapped[int] = mapped_column(ForeignKey('cards.id'))
card: Mapped['Card'] = relationship(back_populates='pallets')
boxes: Mapped[list['Box']] = relationship(
back_populates='pallet',
@@ -30,6 +30,24 @@ class Pallet(BaseModel):
)
class Box(BaseModel):
__tablename__ = 'boxes'
id: Mapped[int] = mapped_column(primary_key=True)
shipping_products: Mapped[list['ShippingProduct']] = relationship(
back_populates='box',
uselist=True,
lazy='joined',
cascade='all, delete-orphan',
)
pallet_id: Mapped[Optional[int]] = mapped_column(ForeignKey('pallets.id'))
pallet: Mapped[Pallet] = relationship(back_populates='boxes')
card_id: Mapped[Optional[int]] = mapped_column(ForeignKey('cards.id'))
card: Mapped['Card'] = relationship(back_populates='boxes')
class ShippingProduct(BaseModel):
__tablename__ = 'shipping_products'
id: Mapped[int] = mapped_column(primary_key=True)
@@ -38,21 +56,8 @@ class ShippingProduct(BaseModel):
product_id: Mapped[int] = mapped_column(ForeignKey('products.id'))
product: Mapped['Product'] = relationship(lazy='joined')
pallet_id: Mapped[int] = mapped_column(ForeignKey('pallets.id'))
pallet: Mapped['Pallet'] = relationship(lazy='joined')
class Box(BaseModel):
__tablename__ = 'boxes'
id: Mapped[int] = mapped_column(primary_key=True)
quantity: Mapped[int] = mapped_column(default=0)
product_id: Mapped[Optional[int]] = mapped_column(ForeignKey('products.id'), nullable=True)
product: Mapped['Product'] = relationship(lazy='joined')
pallet_id: Mapped[Optional[int]] = mapped_column(ForeignKey('pallets.id'))
pallet: Mapped[Pallet] = relationship(back_populates='boxes')
pallet: Mapped[Optional['Pallet']] = relationship(lazy='joined')
deal_id: Mapped[Optional[int]] = mapped_column(ForeignKey('deals.id'))
deal: Mapped['Deal'] = relationship(back_populates='boxes')
box_id: Mapped[Optional[int]] = mapped_column(ForeignKey('boxes.id'))
box: Mapped[Optional['Box']] = relationship(lazy='joined')

View File

@@ -1,16 +1,17 @@
from datetime import datetime
from typing import TYPE_CHECKING
from sqlalchemy import ForeignKey, Column, Integer, DateTime, String
from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from models import BaseModel
if TYPE_CHECKING:
from models import Board
from models import Board, Card, User
class DealStatus(BaseModel):
__tablename__ = "deal_statuses"
class CardStatus(BaseModel):
__tablename__ = 'card_statuses'
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(nullable=False)
@@ -19,43 +20,45 @@ class DealStatus(BaseModel):
is_deleted: Mapped[bool] = mapped_column(default=False, nullable=False)
board_id: Mapped[int] = mapped_column(ForeignKey('boards.id'), nullable=False)
board: Mapped["Board"] = relationship("Board", back_populates="deal_statuses")
board: Mapped['Board'] = relationship('Board', back_populates='statuses')
class DealStatusHistory(BaseModel):
__tablename__ = 'deals_status_history'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
class CardStatusHistory(BaseModel):
__tablename__ = 'cards_status_history'
id: Mapped[int] = mapped_column(primary_key=True)
deal_id = Column(Integer, ForeignKey('deals.id'), nullable=False, comment='ID сделки')
deal = relationship('Deal', back_populates='status_history')
card_id: Mapped[int] = mapped_column(ForeignKey('cards.id'), nullable=False, comment='ID карточки')
card: Mapped['Card'] = relationship('Card', back_populates='status_history')
user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
user = relationship('User')
user_id: Mapped[int] = mapped_column(ForeignKey('users.id'), nullable=False)
user: Mapped['User'] = relationship('User')
changed_at = Column(DateTime, nullable=False, comment='Дата и время когда произошла смена статуса')
changed_at: Mapped[datetime] = mapped_column(nullable=False, comment='Дата и время когда произошла смена статуса')
from_status_id: Mapped[int] = mapped_column(
ForeignKey('deal_statuses.id'),
ForeignKey('card_statuses.id'),
nullable=False,
comment='Предыдущий статус',
)
from_status: Mapped[DealStatus] = relationship(
'DealStatus',
from_status: Mapped[CardStatus] = relationship(
'CardStatus',
foreign_keys=[from_status_id],
lazy='joined',
)
to_status_id: Mapped[int] = mapped_column(
ForeignKey('deal_statuses.id'),
ForeignKey('card_statuses.id'),
nullable=False,
comment='Новый статус',
)
to_status: Mapped[DealStatus] = relationship(
'DealStatus',
to_status: Mapped[CardStatus] = relationship(
'CardStatus',
foreign_keys=[to_status_id],
lazy='joined',
)
next_status_deadline = Column(DateTime,
comment='Дедлайн до которого сделку нужно перевести на следующий этап')
comment = Column(String, nullable=False, comment='Коментарий', server_default='')
next_status_deadline: Mapped[datetime] = mapped_column(
comment='Дедлайн до которого сделку нужно перевести на следующий этап',
nullable=True,
)
comment: Mapped[str] = mapped_column(nullable=False, comment='Комментарий', server_default='')