feat: add tests for import polling, login page, money utilities, and query keys
- Implemented tests for import polling functionality, ensuring correct behavior for active and terminal import statuses. - Created tests for the login page, validating user interactions and error handling. - Added unit tests for money utility functions, covering parsing, formatting, and summation of monetary values. - Developed tests for query keys, ensuring proper scoping and invalidation roots. chore: setup testing environment and configuration - Added setup file for testing with Vitest and Testing Library. - Configured TypeScript and Vite for the frontend project. - Created Docker and Nginx configurations for local development and production environments. - Established a PostgreSQL initialization script for setting up application roles. docs: add README and environment configuration for infra - Included README for the infra directory, detailing local development setup and service descriptions. - Created an example environment configuration file for local development with placeholders for sensitive information.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
from datetime import date
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
|
||||
from app.analytics import service
|
||||
from app.dependencies import Context, Db
|
||||
|
||||
router = APIRouter(prefix="/analytics", tags=["analytics"])
|
||||
|
||||
|
||||
@router.get("/category-spend")
|
||||
def category_spend(
|
||||
date_from: date = Query(alias="from"),
|
||||
date_to: date = Query(alias="to"),
|
||||
*,
|
||||
db: Db,
|
||||
context: Context,
|
||||
) -> list[dict]:
|
||||
rows = service.category_spend(
|
||||
db, space_id=context.space.id, date_from=date_from, date_to=date_to
|
||||
)
|
||||
return [
|
||||
{
|
||||
"categoryId": row["category_id"],
|
||||
"name": row["category"] if not row["category"].startswith("category.") else None,
|
||||
"nameKey": row["category"] if row["category"].startswith("category.") else None,
|
||||
"amountKopecks": row["amount_kopecks"],
|
||||
}
|
||||
for row in rows
|
||||
]
|
||||
|
||||
|
||||
@router.get("/cash-flow")
|
||||
def cash_flow(
|
||||
date_from: date = Query(alias="from"),
|
||||
date_to: date = Query(alias="to"),
|
||||
*,
|
||||
db: Db,
|
||||
context: Context,
|
||||
) -> list[dict]:
|
||||
rows = service.cash_flow(db, space_id=context.space.id, date_from=date_from, date_to=date_to)
|
||||
months: dict[str, dict[str, int | str]] = {}
|
||||
for row in rows:
|
||||
month = row["date"].strftime("%Y-%m")
|
||||
bucket = months.setdefault(
|
||||
month,
|
||||
{
|
||||
"month": month,
|
||||
"incomeKopecks": 0,
|
||||
"expenseKopecks": 0,
|
||||
"refundsKopecks": 0,
|
||||
"netKopecks": 0,
|
||||
},
|
||||
)
|
||||
amount = int(row["amount_kopecks"])
|
||||
key = {
|
||||
"income": "incomeKopecks",
|
||||
"expense": "expenseKopecks",
|
||||
"refund": "refundsKopecks",
|
||||
}[row["type"]]
|
||||
bucket[key] = int(bucket[key]) + abs(amount)
|
||||
bucket["netKopecks"] = int(bucket["netKopecks"]) + amount
|
||||
return list(months.values())
|
||||
|
||||
|
||||
@router.get("/plan-fact")
|
||||
def plan_fact(month: str, db: Db, context: Context) -> dict:
|
||||
from app.budgeting.router import parse_month
|
||||
|
||||
year, month_number = parse_month(month)
|
||||
state = service.plan_fact(db, space_id=context.space.id, year=year, month=month_number)
|
||||
categories = state.get("monthly_categories", [])
|
||||
return {
|
||||
"month": month,
|
||||
"items": [
|
||||
{
|
||||
"categoryId": row["category_id"],
|
||||
"name": row["name"] if not str(row["name"]).startswith("category.") else None,
|
||||
"nameKey": row["name"] if str(row["name"]).startswith("category.") else None,
|
||||
"plannedKopecks": row["planned"],
|
||||
"actualKopecks": row["spent"],
|
||||
}
|
||||
for row in categories
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@router.get("/balances")
|
||||
def balances(db: Db, context: Context) -> dict:
|
||||
from datetime import UTC, datetime
|
||||
|
||||
rows = service.balances(db, space_id=context.space.id)
|
||||
assets = sum(max(row["balance_kopecks"], 0) for row in rows if row["kind"] != "credit")
|
||||
liabilities = sum(row["debt_kopecks"] for row in rows)
|
||||
return {
|
||||
"asOf": datetime.now(UTC).date(),
|
||||
"assetsKopecks": assets,
|
||||
"liabilitiesKopecks": liabilities,
|
||||
"netKopecks": assets - liabilities,
|
||||
"items": [
|
||||
{
|
||||
"accountId": row["account_id"],
|
||||
"name": row["account"],
|
||||
"type": row["kind"],
|
||||
"balanceKopecks": row["balance_kopecks"],
|
||||
}
|
||||
for row in rows
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user