Welcome to the Accounts Receivable system! This system is responsible for creating, sending, and tracking invoices sent to clients so we can be paid for our products and services.
Status: early domain pass. The toolchain and every library in the stack are set up and proven by the test suite, and clients, products, and services are built end to end — models, seed data, and CRUD pages behind the application shell. Invoices and line items arrive in a later pass.
This project is a Django application written in Python. It uses SQLite for the database and the Django ORM, with Django Ninja and Pydantic for the JSON API. Python versions are managed with asdf and dependencies with uv.
Before you get started, be sure to:
- Have a recent version of git installed (e.g.
brew install giton a Mac) - Have asdf installed, with the python plugin (
asdf plugin add python) - Have uv 0.13 or newer installed (e.g.
brew install uvon a Mac)
$ ./setup.sh
That installs the pinned Python, creates the virtualenv, installs dependencies, and applies migrations. If you would rather run the steps yourself:
-
Install the Python version this project pins:
$ asdf install -
Create the virtualenv on that interpreter:
$ uv venv --python "$(asdf which python)"(The interpreter is passed explicitly on purpose. uv does not read
.tool-versions, and a Homebrew Python on yourPATHmay also satisfy this project'srequires-python— so without it you can silently get the wrong one.) -
Install dependencies:
$ uv sync -
Apply migrations:
$ uv run python manage.py migrate
Optionally load development seed data (two clients, two products, and two services):
$ uv run python manage.py seed
That command is safe to re-run; it will not duplicate rows.
Then start the server:
$ ./run.sh
And you should be able to see the app in a browser: http://localhost:8000
The JSON API is mounted at /api, with interactive docs at
http://localhost:8000/api/docs.
The API is unauthenticated and CSRF-exempt, which is Django Ninja's default. That matches the source application, which has no users at all.
To run the suite of tests:
$ ./test.sh
To run a single test file:
$ ./test.sh tests/test_django_factory.py
To check linting and formatting:
$ ./lint.sh
| Layer | Tool | Rails equivalent |
|---|---|---|
| Language | Python | Ruby |
| Version manager | asdf | asdf / rbenv |
| Project + dependencies | uv | Bundler |
| Web framework | Django | Rails |
| ORM | Django ORM | ActiveRecord |
| Migrations | Django Migrations | Rails migrations |
| Views / templates | Django Template Language | ERB |
| JSON API | Django Ninja + Pydantic | — |
| Test runner | pytest | RSpec |
| Django test integration | pytest-django | rspec-rails |
| Mocking | pytest-mock | rspec-mocks |
| Test data factories | PolyFactory | FactoryBot |
| Lint + format | ruff | standardrb |
.tool-versions is the single source of truth for the Python version — there is
deliberately no .python-version, which uv would honour and asdf would ignore.
This project currently pins Python 3.14.9 rather than 3.15. Python 3.15 shipped on
2026-10-09, but Django's support for it lands in Django 6.1.3, expected 2026-11-03;
pytest-django and django-ninja have not published 3.15 support either. requires-python
in pyproject.toml already allows 3.15, so the bump is a one-line change to
.tool-versions once those land.
PolyFactory has no Django ORM support — it covers Pydantic, attrs, msgspec and
SQLAlchemy, but ships no DjangoModelFactory. tests/factories/base.py is a small
adapter built on PolyFactory's BaseFactory that fills the FactoryBot role for Django
models; its own behaviour is covered by tests/test_django_factory.py, and its
deviations from FactoryBot are documented in the module docstring. Pydantic schemas
need no adapter — PolyFactory supports them natively.
Domain factories live in tests/factories/domain.py. tests/testapp/ holds throwaway
models used only to exercise the adapter, with their factories in
tests/factories/models.py; neither is part of the domain.
Never name a pytest fixture
client— pytest-django reserves it for the Django test client, and this domain's central noun isClient.
CLAUDE.md records the standing decisions behind this project, including the one that
shapes the most code: this application will never have authentication or login sessions.