Updates:
- Restructure table models - Remove React/Next.js frontend (in favor of HTMX)
This commit is contained in:
@@ -1,26 +1,62 @@
|
||||
from sqlmodel import SQLModel
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class UserRole(str, Enum):
|
||||
"""User roles for system access.
|
||||
|
||||
Attributes:
|
||||
ADMIN (str): Administrator with full access.
|
||||
WRITE (str): User with write permissions.
|
||||
READ_ONLY (str): User with read-only permissions.
|
||||
"""
|
||||
ADMIN = "admin"
|
||||
WRITE = "write"
|
||||
READ_ONLY = "read_only"
|
||||
|
||||
|
||||
class TransactionType(str, Enum):
|
||||
"""Types of financial transactions.
|
||||
|
||||
Attributes:
|
||||
SALE (str): Sale transaction.
|
||||
PURCHASE (str): Purchase transaction.
|
||||
CREDIT (str): Credit transaction.
|
||||
"""
|
||||
SALE = "sell"
|
||||
PURCHASE = "buy"
|
||||
CREDIT = "credit"
|
||||
|
||||
|
||||
class TransactionStatus(str, Enum):
|
||||
"""Possible statuses of a transaction.
|
||||
|
||||
Attributes:
|
||||
UNPAID (str): Transaction not paid.
|
||||
PARTIALLY_PAID (str): Transaction partially paid.
|
||||
PAID (str): Transaction fully paid.
|
||||
CANCELLED (str): Transaction cancelled.
|
||||
"""
|
||||
UNPAID = "unpaid"
|
||||
PARTIALLY_PAID = "partially_paid"
|
||||
PAID = "paid"
|
||||
CANCELLED = 'cancelled'
|
||||
|
||||
|
||||
class PartnerType(str, Enum):
|
||||
"""Types of business partners.
|
||||
|
||||
Attributes:
|
||||
CLIENT (str): Client partner.
|
||||
SUPPLIER (str): Supplier partner.
|
||||
"""
|
||||
CLIENT = "client"
|
||||
SUPPLIER = "supplier"
|
||||
|
||||
class PaymentMethod(str, Enum):
|
||||
"""Payment methods available.
|
||||
|
||||
Attributes:
|
||||
MOMO (str): Mobile money.
|
||||
BANK (str): Bank transfer.
|
||||
CASH (str): Cash payment.
|
||||
"""
|
||||
MOMO = "momo"
|
||||
BANK = "bank"
|
||||
CASH = "cash"
|
||||
|
||||
+200
-48
@@ -1,56 +1,81 @@
|
||||
"""
|
||||
This module contains Pydantic/Database Models that map database tables validate
|
||||
and serialize api responses.
|
||||
Models module.
|
||||
|
||||
If the logic is identical -> SQLModel is used to do both.
|
||||
Otherwise pydantic - for api responses
|
||||
And SQLAlchemy is used for db data validation.
|
||||
This module contains Pydantic and SQLModel classes for database table mapping,
|
||||
API request/response validation, and serialization.
|
||||
|
||||
TODO:
|
||||
Mapping & validation for:
|
||||
- Clients, Suppliers, Products, payments
|
||||
|
||||
Done:
|
||||
* Table mappings
|
||||
The models include:
|
||||
- User
|
||||
- Partner
|
||||
- Product
|
||||
- Transaction and its details
|
||||
- Payment
|
||||
- Credit account
|
||||
- Inventory
|
||||
"""
|
||||
|
||||
from sqlmodel import SQLModel, Field, UniqueConstraint
|
||||
from datetime import datetime
|
||||
from datetime import datetime, date
|
||||
from sqlalchemy import Column, DateTime, func, Enum as SQLEnum
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
from base import UserRole, PartnerType, TransactionType, TransactionStatus
|
||||
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.
|
||||
"""
|
||||
User table mapping, api response validation and serialisation
|
||||
"""
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
username: str = Field(nullable=False,unique=True, max_length=100)
|
||||
role: UserRole = Field(nullable=True, default=PartnerType.CLIENT)
|
||||
role: UserRole = Field(nullable=False, max_length= 10, default=UserRole.READ_ONLY)
|
||||
password_hash: str = Field(nullable=False)
|
||||
|
||||
|
||||
class Partner(SQLModel, table=True):
|
||||
"""Clients table mapping, api response validation and serialisation"""
|
||||
"""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, default=PartnerType.CLIENT)
|
||||
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 response validation and serialisation
|
||||
"""Products table mapping, API request/response validation, and serialization.
|
||||
|
||||
NOTE: Every time a product's purchase price changes, it should be updated
|
||||
here as well
|
||||
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.
|
||||
"""
|
||||
__table_args__ = (UniqueConstraint("product_code"))
|
||||
|
||||
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),
|
||||
@@ -60,18 +85,44 @@ class Product(SQLModel, table=True):
|
||||
|
||||
|
||||
class Transaction(SQLModel, table=True):
|
||||
"""
|
||||
Transaction table mapping, api response validation and serialisation
|
||||
"""Transaction table mapping, API request/response validation, and serialization.
|
||||
|
||||
Include both business events to/from suppliers and to/from clients
|
||||
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)
|
||||
sa_column=Column(
|
||||
SQLEnum(TransactionType),
|
||||
nullable=False,
|
||||
default=TransactionType.SALE
|
||||
)
|
||||
)
|
||||
transaction_status: TransactionStatus
|
||||
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())
|
||||
@@ -86,32 +137,133 @@ class Transaction(SQLModel, table=True):
|
||||
)
|
||||
|
||||
|
||||
class Transaction_items(SQLModel, table=True):
|
||||
"""
|
||||
Transaction table mapping, api response validation and serialisation
|
||||
Includes transactions details from transactions
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
class Payment(SQLModel, table=True):
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
class Credit_accounts(SQLModel, table=True):
|
||||
"""Credit table mapping, api response validation and serialisation
|
||||
|
||||
Include both credit from suppliers and to clients
|
||||
"""
|
||||
__tablename__: str = "credit_accounts"
|
||||
__tablename__: str = "transaction_details"
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
product_code: str = Field(nullable=False, foreign_key="product.product_code")
|
||||
client_id: Optional[int] = Field(nullable=True, foreign_key="client.id")
|
||||
supplier_id: Optional[int] = Field(nullable=True, foreign_key="supplier.id")
|
||||
partner_id: int = Field(nullable=False, foreign_key="partner.id")
|
||||
product_id: str = Field(nullable=False, foreign_key="product.id")
|
||||
qty: int = Field(nullable=False)
|
||||
amount: int = Field(nullable=False)
|
||||
date: datetime = Field(
|
||||
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: PaymentMethod = Field(
|
||||
sa_column=Column(
|
||||
SQLEnum(PaymentMethod),
|
||||
nullable=False,
|
||||
default=PaymentMethod.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)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
"""
|
||||
Custom validation schema
|
||||
"""
|
||||
from sqlmodel import SQLModel
|
||||
from typing import Optional
|
||||
|
||||
|
||||
Reference in New Issue
Block a user