from .models import users, accruals
from .schemas import UserCreate, AccrualCreate
from .db import database
from sqlalchemy import select, func, desc
from typing import Optional
from decimal import Decimal
def _record_to_dict(record):
"""Convert a databases Record to a plain dict and normalize Decimal->float."""
d = dict(record)
for k, v in list(d.items()):
if isinstance(v, Decimal):
try:
d[k] = float(v)
except Exception:
d[k] = v
return d
async def create_user(user: UserCreate):
query = users.insert().values(email=user.email, name=user.name)
user_id = await database.execute(query)
return {"id": user_id, **user.dict()}
async def get_users():
query = users.select()
rows = await database.fetch_all(query)
return [_record_to_dict(r) for r in rows]
async def create_accrual(accrual: AccrualCreate):
query = accruals.insert().values(**accrual.dict())
accrual_id = await database.execute(query)
# ensure numeric types are normalized
result = {"id": accrual_id, **accrual.dict()}
if "total_amount_rub" in result and isinstance(result["total_amount_rub"], Decimal):
result["total_amount_rub"] = float(result["total_amount_rub"])
return result
async def get_accruals(
skip: int = 0,
limit: int = 100,
service_group: Optional[str] = None,
sales_platform: Optional[str] = None,
accrual_type: Optional[str] = None,
):
"""Get accruals with optional filtering"""
query = accruals.select()
if service_group:
query = query.where(accruals.c.service_group == service_group)
if sales_platform:
query = query.where(accruals.c.sales_platform == sales_platform)
if accrual_type:
query = query.where(accruals.c.accrual_type == accrual_type)
query = query.offset(skip).limit(limit)
rows = await database.fetch_all(query)
return [_record_to_dict(r) for r in rows]
async def get_accrual_stats(
service_group: Optional[str] = None,
sales_platform: Optional[str] = None,
accrual_type: Optional[str] = None,
):
"""Get statistics for accruals"""
query = accruals.select()
if service_group:
query = query.where(accruals.c.service_group == service_group)
if sales_platform:
query = query.where(accruals.c.sales_platform == sales_platform)
if accrual_type:
query = query.where(accruals.c.accrual_type == accrual_type)
# Get all matching records for calculations
rows = await database.fetch_all(query)
records = [_record_to_dict(r) for r in rows]
if not records:
return {
"total_count": 0,
"total_amount": 0,
"avg_amount": 0,
"min_amount": 0,
"max_amount": 0,
}
# Calculate statistics
total_count = len(records)
amounts = [float(r["total_amount_rub"]) for r in records]
total_amount = sum(amounts)
avg_amount = total_amount / total_count if total_count > 0 else 0
min_amount = min(amounts)
max_amount = max(amounts)
# Calculate by platform
by_platform = {}
for record in records:
platform = record["sales_platform"]
if platform not in by_platform:
by_platform[platform] = {"count": 0, "total": 0}
by_platform[platform]["count"] += 1
by_platform[platform]["total"] += float(record["total_amount_rub"])
# Calculate by service group
by_service_group = {}
for record in records:
group = record["service_group"]
if group not in by_service_group:
by_service_group[group] = {"count": 0, "total": 0}
by_service_group[group]["count"] += 1
by_service_group[group]["total"] += float(record["total_amount_rub"])
return {
"total_count": total_count,
"total_amount": total_amount,
"avg_amount": avg_amount,
"min_amount": min_amount,
"max_amount": max_amount,
"by_platform": by_platform,
"by_service_group": by_service_group,
}
async def update_accrual(id_accrual: str, updates: dict):
"""Update accrual identified by id_accrual with provided fields."""
if not updates:
return None
query = accruals.update().where(accruals.c.id_accrual == id_accrual).values(**updates)
await database.execute(query)
updated = await database.fetch_one(accruals.select().where(accruals.c.id_accrual == id_accrual))
return _record_to_dict(updated) if updated else None
async def delete_accrual(id_accrual: str) -> bool:
"""Delete accrual by id_accrual."""
query = accruals.delete().where(accruals.c.id_accrual == id_accrual)
await database.execute(query)
return True