150 lines
5.3 KiB
Python
150 lines
5.3 KiB
Python
from pydantic import BaseModel, ConfigDict, validator, Field
|
|
from typing import List, Optional
|
|
from decimal import Decimal
|
|
from datetime import datetime
|
|
from app.models import SplitTypeEnum, ExpenseSplitStatusEnum, ExpenseOverallStatusEnum
|
|
from app.schemas.user import UserPublic
|
|
from app.schemas.settlement_activity import SettlementActivityPublic
|
|
|
|
class ExpenseSplitBase(BaseModel):
|
|
user_id: int
|
|
owed_amount: Decimal
|
|
share_percentage: Optional[Decimal] = None
|
|
share_units: Optional[int] = None
|
|
|
|
class ExpenseSplitCreate(ExpenseSplitBase):
|
|
pass
|
|
|
|
class ExpenseSplitPublic(ExpenseSplitBase):
|
|
id: int
|
|
expense_id: int
|
|
user: Optional[UserPublic] = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
status: ExpenseSplitStatusEnum
|
|
paid_at: Optional[datetime] = None
|
|
settlement_activities: List[SettlementActivityPublic] = []
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
class RecurrencePatternBase(BaseModel):
|
|
type: str = Field(..., description="Type of recurrence: daily, weekly, monthly, yearly")
|
|
interval: int = Field(..., description="Interval of recurrence (e.g., every X days/weeks/months/years)")
|
|
days_of_week: Optional[List[int]] = Field(None, description="Days of week for weekly recurrence (0-6, Sunday-Saturday)")
|
|
end_date: Optional[datetime] = Field(None, description="Optional end date for the recurrence")
|
|
max_occurrences: Optional[int] = Field(None, description="Optional maximum number of occurrences")
|
|
|
|
class RecurrencePatternCreate(RecurrencePatternBase):
|
|
pass
|
|
|
|
class RecurrencePatternUpdate(RecurrencePatternBase):
|
|
pass
|
|
|
|
class RecurrencePatternInDB(RecurrencePatternBase):
|
|
id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
class ExpenseBase(BaseModel):
|
|
description: str
|
|
total_amount: Decimal
|
|
currency: Optional[str] = "USD"
|
|
expense_date: Optional[datetime] = None
|
|
split_type: SplitTypeEnum
|
|
list_id: Optional[int] = None
|
|
group_id: Optional[int] = None
|
|
item_id: Optional[int] = None
|
|
paid_by_user_id: int
|
|
is_recurring: bool = Field(False, description="Whether this is a recurring expense")
|
|
recurrence_pattern: Optional[RecurrencePatternCreate] = Field(None, description="Recurrence pattern for recurring expenses")
|
|
|
|
class ExpenseCreate(ExpenseBase):
|
|
splits_in: Optional[List[ExpenseSplitCreate]] = None
|
|
|
|
@validator('total_amount')
|
|
def total_amount_must_be_positive(cls, v):
|
|
if v <= Decimal('0'):
|
|
raise ValueError('Total amount must be positive')
|
|
return v
|
|
|
|
@validator('group_id', always=True)
|
|
def check_list_or_group_id(cls, v, values):
|
|
if values.get('list_id') is None and v is None:
|
|
raise ValueError('Either list_id or group_id must be provided for an expense')
|
|
return v
|
|
|
|
@validator('recurrence_pattern')
|
|
def validate_recurrence_pattern(cls, v, values):
|
|
if values.get('is_recurring') and not v:
|
|
raise ValueError('Recurrence pattern is required for recurring expenses')
|
|
if not values.get('is_recurring') and v:
|
|
raise ValueError('Recurrence pattern should not be provided for non-recurring expenses')
|
|
return v
|
|
|
|
class ExpenseUpdate(BaseModel):
|
|
description: Optional[str] = None
|
|
total_amount: Optional[Decimal] = None
|
|
currency: Optional[str] = None
|
|
expense_date: Optional[datetime] = None
|
|
split_type: Optional[SplitTypeEnum] = None
|
|
list_id: Optional[int] = None
|
|
group_id: Optional[int] = None
|
|
item_id: Optional[int] = None
|
|
version: int
|
|
is_recurring: Optional[bool] = None
|
|
recurrence_pattern: Optional[RecurrencePatternUpdate] = None
|
|
next_occurrence: Optional[datetime] = None
|
|
|
|
class ExpensePublic(ExpenseBase):
|
|
id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
version: int
|
|
created_by_user_id: int
|
|
splits: List[ExpenseSplitPublic] = []
|
|
paid_by_user: Optional[UserPublic] = None
|
|
overall_settlement_status: ExpenseOverallStatusEnum
|
|
is_recurring: bool
|
|
next_occurrence: Optional[datetime]
|
|
last_occurrence: Optional[datetime]
|
|
recurrence_pattern: Optional[RecurrencePatternInDB]
|
|
parent_expense_id: Optional[int]
|
|
generated_expenses: List['ExpensePublic'] = []
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
class SettlementBase(BaseModel):
|
|
group_id: int
|
|
paid_by_user_id: int
|
|
paid_to_user_id: int
|
|
amount: Decimal
|
|
settlement_date: Optional[datetime] = None
|
|
description: Optional[str] = None
|
|
|
|
class SettlementCreate(SettlementBase):
|
|
@validator('amount')
|
|
def amount_must_be_positive(cls, v):
|
|
if v <= Decimal('0'):
|
|
raise ValueError('Settlement amount must be positive')
|
|
return v
|
|
|
|
@validator('paid_to_user_id')
|
|
def payer_and_payee_must_be_different(cls, v, values):
|
|
if 'paid_by_user_id' in values and v == values['paid_by_user_id']:
|
|
raise ValueError('Payer and payee cannot be the same user')
|
|
return v
|
|
|
|
class SettlementUpdate(BaseModel):
|
|
amount: Optional[Decimal] = None
|
|
settlement_date: Optional[datetime] = None
|
|
description: Optional[str] = None
|
|
version: int
|
|
|
|
class SettlementPublic(SettlementBase):
|
|
id: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
version: int
|
|
created_by_user_id: int
|
|
model_config = ConfigDict(from_attributes=True) |