Sagar Patel
Add inventory tracking
802b31f
Raw
History Blame Contribute Delete
2 kB
"""Pydantic schemas for VoiceLedger transactions."""
from __future__ import annotations
from typing import Literal
from pydantic import BaseModel, Field, field_validator, model_validator
TransactionType = Literal[
"sale",
"expense",
"inventory_purchase",
"customer_credit",
"customer_payment",
"unknown",
]
PaymentStatus = Literal["paid", "unpaid", "credit", "unknown"]
class Transaction(BaseModel):
"""Structured bookkeeping record parsed from a user's note."""
transaction_type: TransactionType = Field(
default="unknown",
description="Business classification for the transaction.",
)
item: str | None = Field(default=None, description="Product or expense item.")
quantity: float | None = Field(default=None, ge=0)
unit_price: float | None = Field(default=None, ge=0)
amount: float | None = Field(default=None, ge=0)
customer: str | None = Field(default=None)
payment_status: PaymentStatus = Field(default="unknown")
notes: str = Field(default="")
confidence: float = Field(default=0.0, ge=0.0, le=1.0)
@field_validator("item", "customer", mode="before")
@classmethod
def normalize_optional_text(cls, value: object) -> str | None:
"""Trim optional text fields and convert blanks to None."""
if value is None:
return None
text = str(value).strip()
return text or None
@field_validator("notes", mode="before")
@classmethod
def normalize_notes(cls, value: object) -> str:
"""Store notes as a stripped string."""
if value is None:
return ""
return str(value).strip()
@model_validator(mode="after")
def derive_amount(self) -> "Transaction":
"""Derive amount from quantity and unit price when possible."""
if self.amount is None and self.quantity is not None and self.unit_price is not None:
self.amount = round(self.quantity * self.unit_price, 2)
return self