87 lines
3.8 KiB
Python
87 lines
3.8 KiB
Python
from datetime import date
|
|
from decimal import Decimal
|
|
from typing import Annotated
|
|
from fastapi import APIRouter, Form, HTTPException, Query, Request
|
|
from fastapi.responses import RedirectResponse
|
|
from services.asset_service import create_asset as create_asset_record
|
|
from models import CATEGORIES
|
|
from services.income_service import assets, available_years, get_entry, list_entries, save_entry, delete_entry
|
|
from views import render
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def form_page(request, data, error=None, status=200, entry_id=None):
|
|
return render(request, 'income_form.html', {'data': data, 'assets': assets(), 'error': error, 'entry_id': entry_id}, status)
|
|
|
|
|
|
@router.get('/income')
|
|
def history(request: Request, year: str | None = None,
|
|
month: str | None = None,
|
|
asset_id: str | None = None, category: str | None = None,
|
|
page: Annotated[int, Query(ge=1, le=1000000)] = 1):
|
|
def number(value, maximum):
|
|
if not value:
|
|
return None
|
|
try:
|
|
parsed = int(value)
|
|
if not 1 <= parsed <= maximum:
|
|
raise ValueError
|
|
return parsed
|
|
except ValueError:
|
|
raise HTTPException(422, 'Ungültiger Filter.') from None
|
|
year, month, asset_id = number(year, 9999), number(month, 12), number(asset_id, 9223372036854775807)
|
|
category = category or None
|
|
if category is not None and category not in CATEGORIES:
|
|
raise HTTPException(422, 'Ungültige Kategorie.')
|
|
rows = list_entries(year, month, asset_id, category, limit=101, offset=(page-1)*100)
|
|
return render(request, 'income.html', dict(entries=rows[:100], more=len(rows)>100, page=page,
|
|
assets=assets(), years=available_years(), filters=dict(year=year, month=month, asset_id=asset_id, category=category)))
|
|
|
|
|
|
@router.get('/income/new')
|
|
def new(request: Request):
|
|
return form_page(request, {'date': date.today().isoformat(), 'received': True, 'expected': False,
|
|
'asset_id': request.query_params.get('asset_id', ''), 'category': 'dividend'})
|
|
|
|
|
|
@router.get('/income/{entry_id}/edit')
|
|
def edit(request: Request, entry_id: int):
|
|
data = get_entry(entry_id)
|
|
data['amount'] = str(Decimal(data['amount']) / 100).replace('.', ',')
|
|
return form_page(request, data, entry_id=entry_id)
|
|
|
|
|
|
@router.post('/income/new')
|
|
@router.post('/income/{entry_id}/edit')
|
|
def save(request: Request, date: Annotated[str, Form()] = '', asset_id: Annotated[str, Form()] = '',
|
|
category: Annotated[str, Form()] = '', amount: Annotated[str, Form()] = '',
|
|
note: Annotated[str, Form()] = '', expected: Annotated[str, Form()] = '',
|
|
received: Annotated[str, Form()] = '', entry_id: int | None = None):
|
|
data = dict(date=date, asset_id=asset_id, category=category, amount=amount, note=note, expected=expected, received=received)
|
|
try:
|
|
save_entry(data, entry_id)
|
|
except ValueError as error:
|
|
return form_page(request, data, str(error), 422, entry_id)
|
|
return RedirectResponse('/?message=saved', status_code=303)
|
|
|
|
|
|
@router.post('/income/{entry_id}/delete')
|
|
def delete(entry_id: int):
|
|
delete_entry(entry_id)
|
|
return RedirectResponse('/?message=deleted', status_code=303)
|
|
|
|
|
|
@router.get('/assets/new')
|
|
def new_asset(request: Request):
|
|
return render(request, 'asset_form.html', {'data': {}})
|
|
|
|
|
|
@router.post('/assets/new')
|
|
def create_asset(request: Request, name: Annotated[str, Form()], asset_type: Annotated[str, Form()], ticker: Annotated[str, Form()] = ''):
|
|
try:
|
|
asset_id = create_asset_record(name, asset_type, ticker, reuse=True)['id']
|
|
except ValueError as error:
|
|
return render(request, 'asset_form.html', {'data': dict(name=name, asset_type=asset_type, ticker=ticker), 'error': str(error)}, 422)
|
|
return RedirectResponse(f'/income/new?asset_id={asset_id}', status_code=303)
|