part_ratio.py 4.1 KB
"""
数据模型 - 库销比
"""

from dataclasses import dataclass
from decimal import Decimal
from datetime import datetime
from typing import Optional


@dataclass
class PartRatio:
    """配件库销比数据"""
    
    id: int
    group_id: int
    brand_id: Optional[int] = None
    brand_grouping_id: Optional[int] = None
    supplier_id: Optional[int] = None
    supplier_name: Optional[str] = None
    area_id: Optional[int] = None
    area_name: Optional[str] = None
    shop_id: int = 0
    shop_name: Optional[str] = None
    part_id: Optional[int] = None
    part_code: str = ""
    part_name: Optional[str] = None
    part_biz_type: Optional[str] = None
    unit_price: Decimal = Decimal("0")
    cost_price: Decimal = Decimal("0")
    storage_total_cnt: Decimal = Decimal("0")
    in_stock_unlocked_cnt: Decimal = Decimal("0")
    has_plan_cnt: Decimal = Decimal("0")
    on_the_way_cnt: Decimal = Decimal("0")
    out_stock_cnt: Decimal = Decimal("0")
    storage_locked_cnt: Decimal = Decimal("0")
    out_stock_ongoing_cnt: Decimal = Decimal("0")
    buy_cnt: int = 0
    transfer_cnt: int = 0
    gen_transfer_cnt: int = 0
    part_tag: Optional[int] = None
    stock_age: Optional[int] = None
    unit: Optional[str] = None
    out_times: Optional[int] = None
    statistics_date: str = ""

    @property
    def valid_storage_cnt(self) -> Decimal:
        """有效库存数量 = 在库未锁 + 在途 + 计划数"""
        return self.in_stock_unlocked_cnt + self.on_the_way_cnt + self.has_plan_cnt

    @property
    def valid_storage_amount(self) -> Decimal:
        """有效库存金额"""
        return self.valid_storage_cnt * self.cost_price

    @property
    def avg_sales_cnt(self) -> Decimal:
        """平均销量 (90天出库数 + 未关单已锁 + 未关单出库 + 订件) / 3"""
        total = (self.out_stock_cnt or Decimal("0")) + self.storage_locked_cnt + self.out_stock_ongoing_cnt + Decimal(str(self.buy_cnt))
        return total / Decimal("3")

    @property
    def avg_sales_amount(self) -> Decimal:
        """平均销量金额"""
        return self.avg_sales_cnt * self.cost_price

    @property
    def current_ratio(self) -> Decimal:
        """当前库销比"""
        if self.avg_sales_cnt == 0:
            return Decimal("999")
        return self.valid_storage_cnt / self.avg_sales_cnt

    @classmethod
    def from_dict(cls, data: dict) -> "PartRatio":
        """从字典创建"""
        return cls(
            id=data.get("id", 0),
            group_id=data.get("group_id", 0),
            brand_id=data.get("brand_id"),
            brand_grouping_id=data.get("brand_grouping_id"),
            supplier_id=data.get("supplier_id"),
            supplier_name=data.get("supplier_name"),
            area_id=data.get("area_id"),
            area_name=data.get("area_name"),
            shop_id=data.get("shop_id", 0),
            shop_name=data.get("shop_name"),
            part_id=data.get("part_id"),
            part_code=data.get("part_code", ""),
            part_name=data.get("part_name"),
            part_biz_type=data.get("part_biz_type"),
            unit_price=Decimal(str(data.get("unit_price", 0))),
            cost_price=Decimal(str(data.get("cost_price", 0))),
            storage_total_cnt=Decimal(str(data.get("storage_total_cnt", 0))),
            in_stock_unlocked_cnt=Decimal(str(data.get("in_stock_unlocked_cnt", 0))),
            has_plan_cnt=Decimal(str(data.get("has_plan_cnt", 0))),
            on_the_way_cnt=Decimal(str(data.get("on_the_way_cnt", 0))),
            out_stock_cnt=Decimal(str(data.get("out_stock_cnt", 0))),
            storage_locked_cnt=Decimal(str(data.get("storage_locked_cnt", 0))),
            out_stock_ongoing_cnt=Decimal(str(data.get("out_stock_ongoing_cnt", 0))),
            buy_cnt=int(data.get("buy_cnt", 0)),
            transfer_cnt=int(data.get("transfer_cnt", 0)),
            gen_transfer_cnt=int(data.get("gen_transfer_cnt", 0)),
            part_tag=data.get("part_tag"),
            stock_age=data.get("stock_age"),
            unit=data.get("unit"),
            out_times=data.get("out_times"),
            statistics_date=data.get("statistics_date", ""),
        )