Django Apps
The server is organized into 9 Django apps under apps/server/. Project configuration lives in project_config/ (settings, urls, wsgi).
App Overview
| App | Purpose | Has Models | Has Migrations |
|---|---|---|---|
common | TimestampMixin, SyncMixin, IsOwner permission, StandardPagination | No (abstract only) | No |
users | Custom User model, register/login/me endpoints | Yes | Yes |
categories | System defaults (18 seeded) + user custom categories | Yes | Yes |
transactions | UUID PK, soft delete, nested line items, summary endpoint | Yes | Yes |
budgets | Budget CRUD with computed spent/remaining from transactions | Yes | Yes |
recurring | RecurringRule model + generate_recurring management command | Yes | Yes |
receipts | Upload, OCR (EasyOCR), AI parsing (GPT-4o), confirm-to-transaction | Yes | Yes |
agent | AI chat via OpenAI function calling | No | No |
sync | E2E encrypted push/pull/status/resolve endpoints | No | No |
Common App
Provides shared base classes and utilities used across all apps:
- TimestampMixin -- Adds
created_atandupdated_atauto-fields - SyncMixin -- Adds
client_id,synced_at,deleted_atfor sync support - IsOwner -- DRF permission class that checks
obj.user == request.user - StandardPagination -- Page-based pagination (20 items per page)
URL Configuration
All API endpoints are mounted under /api/v1/:
# project_config/urls.py
urlpatterns = [
path("admin/", admin.site.urls),
path("api/v1/auth/", include("users.urls")),
path("api/v1/transactions/", include("transactions.urls")),
path("api/v1/categories/", include("categories.urls")),
path("api/v1/budgets/", include("budgets.urls")),
path("api/v1/recurring/", include("recurring.urls")),
path("api/v1/receipts/", include("receipts.urls")),
path("api/v1/agent/", include("agent.urls")),
path("api/v1/sync/", include("sync.urls")),
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
path("api/docs/", SpectacularSwaggerView.as_view(), name="swagger-ui"),
]
Viewset Pattern
All viewsets with a user foreign key filter by the authenticated user:
class TransactionViewSet(ModelViewSet):
permission_classes = [IsAuthenticated, IsOwner]
def get_queryset(self):
return Transaction.objects.filter(user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
Categories are a special case: system defaults have user=None, is_default=True and are visible to all users.
Management Commands
python manage.py seed_categories-- Seeds 18 default categories (expense and income types)python manage.py generate_recurring-- Generates transactions from active recurring rules wherenext_date <= today