Files
CMT/backend/app/schemas/models.py

275 lines
9.6 KiB
Python

"""
Models module.
This module contains Pydantic and SQLModel classes for database table mapping,
API request/response validation, and serialization.
The models include:
- User
- Partner
- Product
- Transaction and its details
- Payment
- Credit account
- Inventory
"""
from sqlmodel import SQLModel, Field
from datetime import datetime, date
from sqlalchemy import Column, String, CheckConstraint, DateTime, func, Enum as SQLEnum
from typing import Optional
from .base import UserRole, PartnerType, TransactionType, TransactionStatus, PaymentMethod
class User(SQLModel, table=True):
"""User table mapping, API request/response validation, and serialization.
Attributes:
id (int, optional): Primary key.
username (str): Unique user name (max 100 chars).
role (UserRole): User role (default READ_ONLY).
password_hash (str): Hashed password.
is_approved (bool): Whether user is approved by admin (default False).
"""
id: Optional[int] = Field(default=None, primary_key=True)
username: str = Field(nullable=False,unique=True, max_length=100)
role: UserRole = Field(nullable=False, max_length= 10, default=UserRole.READ_ONLY)
password_hash: str = Field(nullable=False)
is_approved: bool = Field(nullable=False, default=False)
class Partner(SQLModel, table=True):
"""Partner (client or supplier) mapping, API request/response validation, and serialization.
Attributes:
id (int, optional): Primary key.
tin_number (int): Tax identification number.
names (str): Full name.
type (PartnerType): Partner type (CLIENT or SUPPLIER).
phone_number (str, optional): Phone number.
"""
id: Optional[int] = Field(default=None, primary_key=True)
tin_number: int = Field(nullable=False, unique=True)
names: str = Field(max_length=100, nullable=False)
type: PartnerType = Field(nullable=False, max_length=10, default=PartnerType.CLIENT)
phone_number: str = Field(max_length=10, nullable=True)
class Product(SQLModel, table=True):
"""Products table mapping, API request/response validation, and serialization.
Every time a product's purchase price changes, update here.
selling_price is referential: defaults but can be overridden.
Attributes:
id (int, optional): Primary key.
product_code (str): Unique product code (max 10 chars).
product_name (str): Unique product name (max 20 chars).
purchase_price (int): Last purchase price.
selling_price (int): Reference selling price.
date_modified (datetime): Last modified timestamp.
"""
id: Optional[int] = Field(default=None, primary_key=True)
product_code: str = Field(max_length=10, unique=True, nullable=False)
product_name: str = Field(max_length=20, nullable=False, unique=True)
purchase_price: int = Field(nullable=False)
selling_price: int = Field(nullable=False)
date_modified: datetime = Field(
default=None,
sa_column=Column(DateTime(timezone=True),
server_default=func.now(),
server_onupdate=func.now())
)
class Transaction(SQLModel, table=True):
"""Transaction table mapping, API request/response validation, and serialization.
Includes both business events to/from suppliers and clients.
Attributes:
id (int, optional): Primary key.
partner_id (int): Related partner ID.
transcation_type (TransactionType): Type of transaction.
transaction_status (TransactionStatus): Current status.
total_amount (int): Total transaction amount.
created_by (int): User ID who created.
updated_by (int): User ID who last updated.
created_on (datetime): Creation timestamp.
updated_on (datetime): Last update timestamp.
"""
__tablename__: str = "transactions"
id: Optional[int] = Field(default=None, primary_key=True)
partner_id: Optional[int] = Field(nullable=False, foreign_key="partner.id")
transcation_type: TransactionType = Field(
sa_column=Column(
SQLEnum(TransactionType),
nullable=False,
default=TransactionType.SALE
)
)
transaction_status: TransactionStatus = Field(
sa_column=Column(
SQLEnum(TransactionStatus),
nullable=False,
default=TransactionStatus.UNPAID
)
)
total_amount: int = Field(nullable=False, default=0)
created_by: int = Field(nullable=False, foreign_key="user.id")
updated_by: int = Field(nullable=False, foreign_key="user.id")
created_on: datetime = Field(
default=None,
sa_column=Column(DateTime(timezone=True), server_default=func.now())
)
updated_on: datetime = Field(
default=None,
sa_column=Column(
DateTime(timezone=True),
onupdate=func.now(),
server_default=func.now()
)
)
class Transaction_details(SQLModel, table=True):
"""Transaction details mapping, API request/response validation, and serialization.
Attributes:
id (int, optional): Primary key.
partner_id (int): Related partner ID.
product_id (str): Product ID.
qty (int): Quantity.
selling_price (int): Unit price.
total_value (int): qty * selling_price.
created_by (int): User ID who created.
updated_by (int): User ID who last updated.
created_at (datetime): Creation timestamp.
updated_at (datetime): Last update timestamp.
"""
__tablename__: str = "transaction_details"
id: Optional[int] = Field(default=None, primary_key=True)
partner_id: int = Field(nullable=False, foreign_key="partner.id")
product_id: int = Field(nullable=False, foreign_key="product.id")
qty: int = Field(nullable=False)
selling_price: int = Field(nullable=False)
# qty * selling_price
total_value: int = Field(nullable=False, default=0) # per items
created_by: int = Field(nullable=False, foreign_key="user.id")
updated_by: int = Field(nullable=False, foreign_key="user.id")
created_at: datetime = Field(
default=None,
sa_column=Column(DateTime(timezone=True), server_default=func.now())
)
updated_at: datetime = Field(
default=None,
sa_column=Column(DateTime(timezone=True), server_default=func.now())
)
class Payment(SQLModel, table=True):
"""Payment table mapping, API request/response validation, and serialization.
Attributes:
id (int, optional): Primary key.
transaction_id (int): Related transaction ID.
payment_method (PaymentMethod): Method of payment.
paid_amount (int): Amount paid.
payment_date (date): Date of payment.
created_by (int): User ID who created.
updated_by (int): User ID who last updated.
created_at (datetime): Creation timestamp.
updated_at (datetime): Last update timestamp.
"""
id: Optional[int] = Field(default=None, primary_key=True)
transaction_id: int = Field(nullable=False, foreign_key="transactions.id")
payment_method: str = Field(
sa_column=Column(
String(10),
CheckConstraint("payment_method IN ('momo', 'bank', 'cash')"),
nullable=False,
default="cash"
)
)
paid_amount: int = Field(nullable=False)
payment_date: date = Field(nullable=False)
created_by: int = Field(nullable=False, foreign_key="user.id")
updated_by: int = Field(nullable=False, foreign_key="user.id")
created_at: datetime = Field(
default=None,
sa_column=Column(DateTime(timezone=True), server_default=func.now())
)
updated_at: datetime = Field(
default=None,
sa_column=Column(DateTime(timezone=True), server_default=func.now())
)
class Credit(SQLModel, table=True):
"""Credit account mapping, API request/response validation, and serialization.
Includes both supplier and client credit events.
Attributes:
id (int, optional): Primary key.
partner_id (int): Related partner ID.
transaction_id (int): Related transaction ID.
credit_amount (int): Credit amount.
credit_limit (int): Credit limit.
balance (int): Current balance.
created_by (int): User ID who created.
updated_by (int): User ID who last updated.
created_at (datetime): Creation timestamp.
updated_at (datetime): Last update timestamp.
"""
__tablename__: str = "credit_accounts"
id: Optional[int] = Field(default=None, primary_key=True)
partner_id: int = Field(nullable=False, unique=True, foreign_key="partner.id")
transaction_id: int = Field(nullable=False, foreign_key="transactions.id")
credit_amount: int = Field(nullable=False)
credit_limit: int = Field(nullable=False)
balance: int = Field(nullable=False)
created_by: int = Field(nullable=False, foreign_key="user.id")
updated_by: int = Field(nullable=False, foreign_key="user.id")
created_at: datetime = Field(
default=None,
sa_column=Column(DateTime(timezone=True), server_default=func.now())
)
updated_at: datetime = Field(
default=None,
sa_column=Column(DateTime(timezone=True), server_default=func.now())
)
class Inventory(SQLModel, table=True):
"""Inventory mapping, API request/response validation, and serialization.
Attributes:
id (int, optional): Primary key.
product_id (int): Related product ID.
total_qty (int): Total quantity in inventory.
"""
id: Optional[int] = Field(default=None, primary_key=True)
product_id: int = Field(nullable=False, unique=True, foreign_key="product.id")
total_qty: int = Field(nullable=False, default=0)