Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/hesabixAPI/adapters/db/models/product.py

178 lines
8.1 KiB
Python
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from datetime import datetime
from decimal import Decimal
from enum import Enum
from typing import Any, Dict, List
from sqlalchemy import (
String,
Integer,
DateTime,
Text,
ForeignKey,
UniqueConstraint,
Boolean,
Numeric,
JSON,
Enum as SQLEnum,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from adapters.db.session import Base
class ProductItemType(str, Enum):
PRODUCT = "کالا"
SERVICE = "خدمت"
class Product(Base):
"""
موجودیت کالا/خدمت در سطح هر کسب‌وکار
- کد دستی/اتوماتیک یکتا در هر کسب‌وکار
- پشتیبانی از مالیات فروش/خرید، کنترل موجودی و واحدها
- اتصال به دسته‌بندی‌ها و ویژگی‌ها (ویژگی‌ها از طریق جدول لینک)
"""
__tablename__ = "products"
__table_args__ = (
UniqueConstraint("business_id", "code", name="uq_products_business_code"),
)
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
business_id: Mapped[int] = mapped_column(Integer, ForeignKey("businesses.id", ondelete="CASCADE"), nullable=False, index=True)
item_type: Mapped[ProductItemType] = mapped_column(
SQLEnum(ProductItemType, values_callable=lambda obj: [e.value for e in obj], name="product_item_type_enum"),
nullable=False,
default=ProductItemType.PRODUCT,
comment="نوع آیتم (کالا/خدمت)",
)
code: Mapped[str] = mapped_column(String(64), nullable=False, comment="کد یکتا در هر کسب‌وکار")
name: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
# بارکدهای عمومی (چند مقدار با ویرگول؛ برای اسکن و چاپ برچسب)
general_barcodes: Mapped[str | None] = mapped_column(
Text,
nullable=True,
comment="بارکدهای عمومی جدا شده با ویرگول",
)
# دسته‌بندی (اختیاری)
category_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("categories.id", ondelete="SET NULL"), nullable=True, index=True)
# واحدها
main_unit: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True, comment="واحد اصلی شمارش")
secondary_unit: Mapped[str | None] = mapped_column(String(32), nullable=True, index=True, comment="واحد فرعی شمارش")
unit_conversion_factor: Mapped[Decimal | None] = mapped_column(Numeric(18, 6), nullable=True)
# قیمت‌های پایه (نمایشی)
base_sales_price: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True)
base_sales_note: Mapped[str | None] = mapped_column(Text, nullable=True)
base_purchase_price: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True)
base_purchase_note: Mapped[str | None] = mapped_column(Text, nullable=True)
# قیمت ارزی (P6 — فقط کسب‌وکار چندارزی استفاده می‌کند)
sales_price_fx: Mapped[Decimal | None] = mapped_column(
Numeric(18, 6), nullable=True, comment="قیمت فروش ارزی"
)
purchase_price_fx: Mapped[Decimal | None] = mapped_column(
Numeric(18, 6), nullable=True, comment="قیمت خرید ارزی"
)
price_fx_currency_id: Mapped[int | None] = mapped_column(
Integer,
ForeignKey("currencies.id", ondelete="SET NULL"),
nullable=True,
index=True,
comment="ارز قیمت‌های ارزی",
)
auto_update_base_from_fx: Mapped[bool] = mapped_column(
Boolean,
default=False,
nullable=False,
comment="به‌روزرسانی خودکار قیمت پایه از نرخ × قیمت ارزی",
)
# کنترل موجودی
track_inventory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
reorder_point: Mapped[int | None] = mapped_column(Integer, nullable=True)
min_order_qty: Mapped[int | None] = mapped_column(Integer, nullable=True)
lead_time_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
# حالت موجودی (فله‌ای/یونیک)
inventory_mode: Mapped[str | None] = mapped_column(String(16), nullable=True, default="bulk", comment="حالت موجودی: bulk (فله‌ای) یا unique (یونیک)")
track_serial: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, comment="ردیابی سریال نامبر برای کالاهای یونیک")
track_barcode: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, comment="ردیابی بارکد برای کالاهای یونیک")
# مالیات
is_sales_taxable: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_purchase_taxable: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
sales_tax_rate: Mapped[Decimal | None] = mapped_column(Numeric(5, 2), nullable=True)
purchase_tax_rate: Mapped[Decimal | None] = mapped_column(Numeric(5, 2), nullable=True)
tax_type_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
tax_code: Mapped[str | None] = mapped_column(String(100), nullable=True)
tax_unit_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True)
# عکس کالا
image_file_id: Mapped[str | None] = mapped_column(String(36), ForeignKey("file_storage.id", ondelete="SET NULL"), nullable=True, index=True)
# انبار پیش‌فرض
default_warehouse_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("warehouses.id", ondelete="SET NULL"), nullable=True, index=True, comment="انبار پیش‌فرض برای کالا")
# وضعیت فعال/غیرفعال
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, comment="آیا محصول فعال است؟")
# شبکهٔ انتشار کالا (کاتالوگ عمومی)
is_public_catalog: Mapped[bool] = mapped_column(
Boolean,
default=False,
nullable=False,
comment="انتشار در API عمومی کاتالوگ",
)
catalog_public_uuid: Mapped[str | None] = mapped_column(
String(36),
nullable=True,
unique=True,
index=True,
comment="شناسهٔ عمومی برای لینک کاتالوگ",
)
# پروفایل شبکهٔ تأمین (کاتالوگ عمومی)
catalog_short_description: Mapped[str | None] = mapped_column(
Text,
nullable=True,
comment="خلاصه کوتاه برای کاتالوگ عمومی",
)
catalog_expert_review: Mapped[str | None] = mapped_column(
Text,
nullable=True,
comment="بررسی تخصصی برای کاتالوگ عمومی",
)
catalog_specifications: Mapped[List[Dict[str, Any]] | None] = mapped_column(
JSON,
nullable=True,
comment="مشخصات فنی کاتالوگ",
)
catalog_brand: Mapped[str | None] = mapped_column(String(255), nullable=True)
catalog_model: Mapped[str | None] = mapped_column(String(255), nullable=True)
catalog_country_of_origin: Mapped[str | None] = mapped_column(String(128), nullable=True)
catalog_video_url: Mapped[str | None] = mapped_column(String(512), nullable=True)
catalog_gallery_file_ids: Mapped[List[str] | None] = mapped_column(
JSON,
nullable=True,
comment="شناسه‌های فایل گالری کاتالوگ",
)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, nullable=False)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
# Relationships
image_file = relationship("FileStorage", foreign_keys=[image_file_id], lazy="select")
default_warehouse = relationship("Warehouse", foreign_keys=[default_warehouse_id], lazy="select")
category = relationship("BusinessCategory", foreign_keys=[category_id], lazy="select")