Skip to main content

Django Apps

The server is organized into 9 Django apps under apps/server/. Project configuration lives in project_config/ (settings, urls, wsgi).

App Overview

AppPurposeHas ModelsHas Migrations
commonTimestampMixin, SyncMixin, IsOwner permission, StandardPaginationNo (abstract only)No
usersCustom User model, register/login/me endpointsYesYes
categoriesSystem defaults (18 seeded) + user custom categoriesYesYes
transactionsUUID PK, soft delete, nested line items, summary endpointYesYes
budgetsBudget CRUD with computed spent/remaining from transactionsYesYes
recurringRecurringRule model + generate_recurring management commandYesYes
receiptsUpload, OCR (EasyOCR), AI parsing (GPT-4o), confirm-to-transactionYesYes
agentAI chat via OpenAI function callingNoNo
syncE2E encrypted push/pull/status/resolve endpointsNoNo

Common App

Provides shared base classes and utilities used across all apps:

  • TimestampMixin -- Adds created_at and updated_at auto-fields
  • SyncMixin -- Adds client_id, synced_at, deleted_at for 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 where next_date <= today