- Restructure table models
- Remove React/Next.js frontend (in favor of HTMX)
This commit is contained in:
2025-08-24 20:25:24 +02:00
parent 648448ebdc
commit 49c813778b
98 changed files with 248 additions and 10042 deletions
+40 -4
View File
@@ -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
View File
@@ -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)
+3
View File
@@ -1,3 +1,6 @@
"""
Custom validation schema
"""
from sqlmodel import SQLModel
from typing import Optional