Merge branch 'master' of https://github.com/inventree/InvenTree into matmair/issue11460

This commit is contained in:
Matthias Mair 2026-06-22 21:59:34 +02:00
commit d0615e706a
No known key found for this signature in database
GPG Key ID: A593429DDA23B66A
28 changed files with 514 additions and 108 deletions

View File

@ -84,6 +84,16 @@ jobs:
docker run --rm inventree-test test -f /home/inventree/gunicorn.conf.py
docker run --rm inventree-test test -f /home/inventree/src/backend/requirements.txt
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/manage.py
# Check that backend translations were compiled into the image (a few representative languages)
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/locale/de/LC_MESSAGES/django.mo
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/locale/fr/LC_MESSAGES/django.mo
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/locale/ru/LC_MESSAGES/django.mo
docker run --rm inventree-test test -f /home/inventree/src/backend/InvenTree/locale/zh_Hans/LC_MESSAGES/django.mo
# Check that frontend translations (lingui) were compiled into the static build output (a few representative languages)
docker run --rm inventree-test grep -q '"src/locales/de/messages.ts"' /home/inventree/src/backend/InvenTree/web/static/web/.vite/manifest.json
docker run --rm inventree-test grep -q '"src/locales/fr/messages.ts"' /home/inventree/src/backend/InvenTree/web/static/web/.vite/manifest.json
docker run --rm inventree-test grep -q '"src/locales/ru/messages.ts"' /home/inventree/src/backend/InvenTree/web/static/web/.vite/manifest.json
docker run --rm inventree-test grep -q '"src/locales/zh_Hans/messages.ts"' /home/inventree/src/backend/InvenTree/web/static/web/.vite/manifest.json
- name: Build Docker Image
# Build the development docker image (using docker-compose.yml)
run: docker compose --project-directory . -f contrib/container/dev-docker-compose.yml build --no-cache

View File

@ -195,6 +195,9 @@ jobs:
--health-interval 10s
--health-timeout 5s
--health-retries 5
permissions:
contents: read # Required for actions/checkout
id-token: write # Required for GitHub OIDC
env:
COVERAGE: true

View File

@ -37,7 +37,7 @@ repos:
files: src/backend/requirements-dev\.(in|txt)$
- id: pip-compile
name: pip-compile requirements-dev.in 3.14
args: [src/backend/requirements-dev.in, -o, src/backend/requirements-dev-3.14.txt, -c, src/backend/requirements-3.14.txt, --python-version=3.14, -c, src/backend/requirements.txt]
args: [src/backend/requirements-dev.in, -o, src/backend/requirements-dev-3.14.txt, -c, src/backend/requirements-3.14.txt, --python-version=3.14, -c, src/backend/requirements.txt, -c, src/backend/requirements-dev.txt]
files: src/backend/requirements-dev\.(in|txt)$
- id: pip-compile
name: pip-compile requirements.txt

5
AGENTS.md Normal file
View File

@ -0,0 +1,5 @@
See [CONTRIBUTING.md](CONTRIBUTING.md) for codebase guidance.
Security reports must follow the security policy in [SECURITY.md](docs/docs/SECURITY.md) and provide how they interact with the [threat model](docs/docs/concepts/threat_model.md).
Do not open a pull request without manual review by a human. Do not file AI generated issues under any circumstances.
Using AI generated content in issue/PR discussions might lead to code of conduct violations and/or bans.

5
CLAUDE.md Normal file
View File

@ -0,0 +1,5 @@
See [CONTRIBUTING.md](CONTRIBUTING.md) for codebase guidance.
Security reports must follow the security policy in [SECURITY.md](docs/docs/SECURITY.md) and provide how they interact with the [threat model](docs/docs/concepts/threat_model.md).
Do not open a pull request without manual review by a human. Do not file AI generated issues under any circumstances.
Using AI generated content in issue/PR discussions might lead to code of conduct violations and/or bans.

View File

@ -1,13 +1,27 @@
### Contributing to InvenTree
# Contributing to InvenTree
Hi there, thank you for your interest in contributing!
Please read our contribution guidelines, before submitting your first pull request to the InvenTree codebase.
### Project File Structure
## About InvenTree
InvenTree is an open-source inventory management system designed for makers, engineers, and small manufacturers.
The codebase is split into two independently deployable layers:
**Backend — Django (Python)**
A REST API built with Django and Django REST Framework. It owns all business logic, the database schema, background task processing (Django-Q2), plugin execution, and report generation. The backend can be used standalone via the API without the frontend.
**Frontend — React (TypeScript)**
A single-page application built with React 19, Mantine 9, and Vite. It communicates exclusively through the backend REST API. It has its own build pipeline, test suite (Playwright), and i18n system (Lingui). The frontend lives entirely in `src/frontend/` and has no Django awareness.
When making changes, be clear about which layer you are working in — they have separate toolchains, test runners, and linting rules.
## Project File Structure
The InvenTree project is split into two main components: frontend and backend. This source is located in the `src` directory. All other files are used for project management, documentation, and testing.
```bash
```
InvenTree/
├─ .devops/ # Files for Azure DevOps
├─ .github/ # Files for GitHub
@ -39,11 +53,209 @@ InvenTree/
│ │ ├─ tsconfig.json # Settings for frontend compilation
├─ .pkgr.yml # Build definition for Debian/Ubuntu packages
├─ .pre-commit-config.yaml # Code formatter/linter configuration
├─ biome.json # JS/TS linting and formatting config
├─ CONTRIBUTING.md # Contribution guidelines and overview
├─ Procfile # Process definition for Debian/Ubuntu packages
├─ pyproject.toml # Python tooling config (Ruff, pytest, coverage, djLint, ty)
├─ README.md # General project information and overview
├─ SECURITY.md # Project security policy
├─ tasks.py # Action definitions for development, testing and deployment
```
### Backend app domains
The following Django apps are defined in `src/backend/InvenTree/`:
| Directory | Domain |
|-----------|--------|
| `build/` | Build (manufacturing/assembly) orders |
| `common/` | Shared models, settings, utilities |
| `company/` | Suppliers, customers, manufacturers |
| `data_exporter/` | Data export functionality |
| `generic/` | Shared enums, events, and state machine utilities |
| `importer/` | Data import functionality |
| `InvenTree/` | Core configuration (settings, URLs, middleware) |
| `machine/` | Support for external machines and devices |
| `order/` | Purchase orders and sales orders |
| `part/` | Parts catalogue and categories |
| `stock/` | Stock items and locations |
| `report/` | Report templates and generation |
| `plugin/` | Plugin system |
| `users/` | User accounts and permissions |
| `web/` | Serves the React frontend static build to Django |
### Frontend layout
```
src/frontend/src/
components/ # Reusable React components
pages/ # Page-level route components
tables/ # Data table components
forms/ # Form components
hooks/ # Custom React hooks
states/ # Zustand global state
locales/ # i18n translation catalogues (Lingui)
```
## Development Setup
The project uses [Invoke](https://www.pyinvoketasks.com/) (`tasks.py`) as the task runner.
```bash
# One-time setup: creates venv at dev/venv/, installs deps, sets up pre-commit hooks
invoke dev.setup-dev
# Apply database migrations
invoke migrate
# Create an admin account (required to log in)
invoke superuser
# Terminal 1 — Django dev server (port 8000)
invoke dev.server
# Terminal 2 — Vite dev server with HMR (port 5173)
invoke dev.frontend-server
# Terminal 3 (optional) — Background task worker
invoke worker
```
A VS Code Dev Container configuration is available at `.devcontainer/` and includes PostgreSQL 15 and Redis 7 sidecar services.
To see all available tasks, run `invoke --list`.
## Running Backend Tests
Always pass `--keepdb` to backend test commands. It reuses the existing test database instead of rebuilding it from scratch on every run, which is significantly faster.
```bash
# All backend tests
invoke dev.test --keepdb
# Specific test module
invoke dev.test --keepdb --runtest=company.test_api
# With coverage
invoke dev.test --keepdb --coverage
# Migration tests only (skip --keepdb here — migrations must run against a fresh DB)
invoke dev.test --migrations
```
## Running Frontend Tests
Frontend tests use [Playwright](https://playwright.dev/) and target Chromium and Firefox. The test runner automatically starts the Vite dev server (port 5173), the Django backend (port 8000), and the background worker — no manual server startup is needed.
```bash
# Open Playwright's interactive UI (recommended for local development)
invoke dev.frontend-test
# Run tests in headless mode from the frontend directory
cd src/frontend && npx playwright test
# Run a specific test file
cd src/frontend && npx playwright test tests/pui_login.spec.ts
# Run tests in a specific browser only
cd src/frontend && npx playwright test --project=chromium
```
Test files live in `src/frontend/tests/` and follow the `pui_*.spec.ts` naming convention.
Locally, tests run with a single worker (required for Vite HMR compatibility). In CI they run with multiple workers against a production build for speed.
## Code Style and Linting
All formatting and linting runs automatically on commit via pre-commit hooks. Never skip them.
### Python (Backend)
Tool: **Ruff** (replaces Black, isort, flake8, and others).
```bash
ruff check src/backend/ # Lint
ruff format src/backend/ # Format
```
- Google-style docstrings enforced
- Enabled rule sets: A, B, C, D, F, I, N, SIM, PIE, PLE, PLW, RUF, UP, W
- Type checking: `ty` (configured in `pyproject.toml`)
### Django Templates
Tool: **djLint**
```bash
djlint src/backend/ --check
```
### JavaScript / TypeScript (Frontend)
Tool: **Biome** (replaces ESLint + Prettier).
```bash
cd src/frontend
yarn biome check . # Lint + format check
yarn biome format . # Format only
```
- Single quotes, space indentation
- `noUnusedImports` is an error
## Making Changes
### Backend
- Each Django app has its own `models.py`, `serializers.py`, `views.py`, `urls.py`, and `filters.py`.
- API endpoints use Django REST Framework; document them with drf-spectacular decorators.
- After changing models, create a migration: `invoke dev.makemigrations`.
- Tests live in `test_*.py` files within each app directory.
- Background tasks go through Django-Q2; define them in the app's `tasks.py`.
### Frontend
- Pages register themselves in `src/frontend/src/router.tsx`.
- Server state fetching uses TanStack Query (React Query); avoid raw `useEffect` for data fetching.
- Global UI state uses Zustand stores in `states/`.
- UI components come from Mantine 9.x; use the Mantine component library before writing custom CSS.
- Add i18n strings with Lingui (`t` macro / `Trans` component); run `invoke int.frontend-trans` to extract from source and compile. (`invoke dev.translate` is for the full translation pipeline and is not intended for local use.)
- Playwright tests live in `src/frontend/tests/`.
### Dependencies
**Python:** Add packages to `src/backend/requirements.in` (or `requirements-dev.in` for dev-only tools). The pre-commit hook runs `pip-compile` automatically on commit to regenerate `requirements.txt`. Never edit `requirements.txt` directly.
**Frontend:** Add packages with `yarn add <package>` from `src/frontend/`.
### Migrations
- Never edit existing migration files; always generate new ones.
- Keep migrations reversible where possible.
- Migration tests run in CI under the tag `migration_test`.
## CI / CD
GitHub Actions workflows live in `.github/workflows/`. Key workflows:
| Workflow | Purpose |
|----------|---------|
| `qc_checks.yaml` | Lint, type-check, backend tests, API schema |
| `frontend.yaml` | Playwright E2E tests, frontend build |
| `docker.yaml` | Docker image builds |
| `translations.yaml` | Crowdin i18n sync |
CI uses path-based filtering — only affected jobs run per PR. Coverage tracked via Codecov; quality analysis via SonarCloud.
## Key Conventions
- **REST API versioning:** Endpoint changes must not break the published API schema. Run `invoke dev.schema` and check the diff before opening a PR.
- **Plugin safety:** The plugin system (`plugin/`) is a public extension point; avoid breaking its interfaces.
- **No hardcoded secrets:** gitleaks runs in pre-commit and CI — any credential-shaped string will block merges.
- **Database portability:** Code must work on PostgreSQL, MySQL/MariaDB, and SQLite. Avoid database-specific SQL or ORM features.
- **Frontend translations:** Every user-visible string must be wrapped in a Lingui `t` macro or `<Trans>` component.
- **Test tagging:** Tag slow or special tests (`@tag('migration_test')`, `@tag('performance_test')`) so CI can filter them selectively.
---
Refer to our [contribution guidelines](https://docs.inventree.org/en/latest/develop/contributing/) for more information!

View File

@ -145,6 +145,9 @@ ENV PATH=/root/.local/bin:$PATH
COPY --from=builder_stage ${INVENTREE_BACKEND_DIR}/InvenTree/web/static/web ${INVENTREE_BACKEND_DIR}/InvenTree/web/static/web
COPY --from=builder_stage /root/.local /root/.local
# Compile Django backend translations during image build
RUN bash -c "cd '${INVENTREE_HOME}' && invoke int.backend-compilemessages"
# Launch the production server
CMD ["sh", "-c", "exec gunicorn -c ./gunicorn.conf.py InvenTree.wsgi -b ${INVENTREE_WEB_ADDR}:${INVENTREE_WEB_PORT} --chdir ${INVENTREE_BACKEND_DIR}/InvenTree"]

View File

@ -102,20 +102,20 @@ no-strip-extras=true
generate-hashes=true
[tool.ty.environment]
root = ["src/backend/InvenTree"]
extra-paths = ["src/backend/InvenTree"]
[tool.ty.rules]
unresolved-reference="ignore" # 21 # see https://github.com/astral-sh/ty/issues/220
unresolved-attribute="ignore" # 505 # need Plugin Mixin typing
call-non-callable="ignore" # 8 ##
invalid-assignment="ignore" # 17 # need to wait for better django field stubs
# invalid-method-override="ignore" # 99
invalid-method-override="ignore" # 104
invalid-return-type="ignore" # 22 ##
# possibly-missing-attribute="ignore" # 25 # https://github.com/astral-sh/ty/issues/164
unknown-argument="ignore" # 3 # need to wait for better django field stubs
invalid-argument-type="ignore" # 49
no-matching-overload="ignore" # 3 # need to wait for betterdjango field stubs
possibly-unbound-attribute="ignore" # 21
# possibly-unbound-attribute="ignore" # 21
[tool.coverage.run]
source = ["src/backend/InvenTree", "InvenTree"]

View File

@ -494,7 +494,7 @@ class BulkCreateMixin:
if unique_create_fields := getattr(self, 'unique_create_fields', None):
existing = collections.defaultdict(list)
for idx, item in enumerate(data):
key = tuple(item[v] for v in list(unique_create_fields)) # type: ignore[not-subscriptable]
key = tuple(item[v] for v in list(unique_create_fields)) # ty: ignore[not-subscriptable]
existing[key].append(idx)
unique_errors = [[] for _ in range(len(data))]

View File

@ -19,10 +19,10 @@ logger = structlog.get_logger('inventree')
def log_error(
path,
error_name=None,
error_info=None,
error_data=None,
path: Optional[str] = None,
error_name: Optional[str] = None,
error_info: Optional[str] = None,
error_data: Optional[str] = None,
scope: Optional[str] = None,
plugin: Optional[str] = None,
):

View File

@ -106,6 +106,32 @@ apps_mfa_bypass = [
"""App namespaces that bypass MFA enforcement - normal security model is still enforced."""
def csrf_failure(request, reason=''):
"""Custom CSRF failure handler.
Returns a JSON response for API/headless requests so the frontend can
provide a meaningful error message to the user
"""
from django.views.csrf import csrf_failure as django_default
if (
request.path.startswith('/_allauth/')
or request.path.startswith('/api/')
or 'application/json' in request.headers.get('Accept', '')
or 'application/json' in request.headers.get('Content-Type', '')
):
return JsonResponse(
{
'detail': _(
'CSRF verification failed. Ensure INVENTREE_SITE_URL and INVENTREE_TRUSTED_ORIGINS are configured correctly.'
)
},
status=403,
)
return django_default(request, reason=reason)
class AuthRequiredMiddleware:
"""Check for user to be authenticated."""

View File

@ -282,7 +282,7 @@ def postprocess_print_stats(result, generator, request, public):
scopes = {}
for path, details in rlt_dict.items():
if details['oauth']:
for scope in details['oauth']:
for scope in list(details['oauth']):
if scope not in scopes:
scopes[scope] = []
scopes[scope].append(path)
@ -343,7 +343,7 @@ def schema_for_view_output_options(view_class):
return extended_view
def exclude_from_schema(klass: type, alternative_path: str) -> type:
def exclude_from_schema(klass: type[Any], alternative_path: str) -> type[Any]:
"""Decorator to exclude a view from the OpenAPI schema.
This is used to hide legacy endpoints from the schema, while still retaining them for backwards compatibility.

View File

@ -9,8 +9,8 @@ def get_ldap_config(debug: bool = False) -> dict:
The returned settings will be updated into the globals() object,
and will be used to configure the LDAP authentication backend.
"""
import django_auth_ldap.config # type: ignore[unresolved-import]
import ldap # type: ignore[unresolved-import]
import django_auth_ldap.config # ty: ignore[unresolved-import]
import ldap # ty: ignore[unresolved-import]
# get global options from dict and use ldap.OPT_* as keys and values
global_options_dict = get_setting(

View File

@ -851,6 +851,7 @@ valid_cookie_modes = ['lax', 'strict', 'none']
COOKIE_MODE = COOKIE_MODE.capitalize() if COOKIE_MODE in valid_cookie_modes else False
# Additional CSRF settings
CSRF_FAILURE_VIEW = 'InvenTree.middleware.csrf_failure'
CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
CSRF_COOKIE_NAME = 'csrftoken'

View File

@ -2,8 +2,7 @@
from unittest.mock import patch
from django.conf import settings
from django.http import Http404
from django.http import Http404, HttpRequest
from django.urls import reverse
from error_report.models import Error
@ -289,18 +288,63 @@ class MiddlewareTests(InvenTreeTestCase):
response, 'window.INVENTREE_SETTINGS', status_code=500
)
# Log stuff # TODO remove
print(
'###DBG-TST###',
'site',
settings.SITE_URL,
'trusted',
settings.CSRF_TRUSTED_ORIGINS,
)
# Check that the correct step triggers the error message
self.assertContains(
response,
'INVE-E7: The visited path `http://testserver` does not match',
status_code=500,
)
def test_csrf_failure(self):
"""Test the custom CSRF failure handler."""
from InvenTree.middleware import csrf_failure
EXPECTED_DETAIL = 'CSRF verification failed. Ensure INVENTREE_SITE_URL and INVENTREE_TRUSTED_ORIGINS are configured correctly.'
def make_request(path, headers=None):
request = HttpRequest()
request.path = path
request.META['SERVER_NAME'] = 'testserver'
request.META['SERVER_PORT'] = '80'
for key, value in (headers or {}).items():
request.META[f'HTTP_{key.upper().replace("-", "_")}'] = value
return request
# API path -> JSON 403 with meaningful message
response = csrf_failure(
make_request('/api/part/'), reason='origin check failed'
)
self.assertEqual(response.status_code, 403)
import json
data = json.loads(response.content)
self.assertEqual(data['detail'], EXPECTED_DETAIL)
# allauth headless path -> JSON 403
response = csrf_failure(make_request('/_allauth/browser/v1/auth/login'))
self.assertEqual(response.status_code, 403)
data = json.loads(response.content)
self.assertEqual(data['detail'], EXPECTED_DETAIL)
# Accept: application/json header -> JSON 403
response = csrf_failure(
make_request('/some/other/path/', {'Accept': 'application/json'}),
reason='origin check failed',
)
self.assertEqual(response.status_code, 403)
data = json.loads(response.content)
self.assertEqual(data['detail'], EXPECTED_DETAIL)
# Content-Type: application/json header -> JSON 403
response = csrf_failure(
make_request('/some/other/path/', {'Content-Type': 'application/json'}),
reason='origin check failed',
)
self.assertEqual(response.status_code, 403)
data = json.loads(response.content)
self.assertEqual(data['detail'], EXPECTED_DETAIL)
# Plain browser request -> falls back to Django default CSRF page (403 HTML, not JSON)
response = csrf_failure(make_request('/web/'), reason='origin check failed')
self.assertEqual(response.status_code, 403)
self.assertNotIn(b'application/json', response.get('Content-Type', '').encode())

View File

@ -577,7 +577,7 @@ class CustomUnitViewset(DataExportViewMixin, viewsets.ModelViewSet):
return {
'name': k,
'is_alias': reg.get_name(k) == k,
'compatible_units': [str(a) for a in unit.compatible_units()],
'compatible_units': [str(a) for a in unit.compatible_units()], # ty:ignore[missing-argument]
'isdimensionless': unit.dimensionless,
}

View File

@ -15,7 +15,7 @@ TESTING = 'test' in sys.argv
def clear():
if not TESTING: # pragma: no cover
os.system('cls' if os.name == 'nt' else 'clear')
os.system('cls' if os.name == 'nt' else 'clear') # ty:ignore[deprecated]
def reverse_association(apps, schema_editor): # pragma: no cover

View File

@ -272,7 +272,7 @@ class ReturnOrderReportContext(report.mixins.BaseReportContext, TypedDict):
customer: Optional[Company]
class TransferOrderReportContext(report.mixins.BaseReportContext, TypedDict):
class TransferOrderReportContext(BaseOrderReportContext, TypedDict):
"""Context for the transfer order model.
Attributes:
@ -639,7 +639,8 @@ class PurchaseOrder(TotalPriceMixin, Order):
def report_context(self) -> PurchaseOrderReportContext:
"""Return report context data for this PurchaseOrder."""
return_ctx = super().report_context()
return_ctx.update({'supplier': self.supplier})
return_ctx.update({'supplier': self.supplier}) # ty:ignore[invalid-key]
return return_ctx
def get_absolute_url(self) -> str:
@ -1372,7 +1373,8 @@ class SalesOrder(TotalPriceMixin, Order):
def report_context(self) -> SalesOrderReportContext:
"""Generate report context data for this SalesOrder."""
return_ctx = super().report_context()
return_ctx.update({'customer': self.customer})
return_ctx.update({'customer': self.customer}) # ty:ignore[invalid-key]
return return_ctx
def get_absolute_url(self) -> str:
@ -2931,7 +2933,8 @@ class ReturnOrder(TotalPriceMixin, Order):
def report_context(self) -> ReturnOrderReportContext:
"""Generate report context data for this ReturnOrder."""
return_ctx = super().report_context()
return_ctx.update({'customer': self.customer})
return_ctx.update({'customer': self.customer}) # ty:ignore[invalid-key]
return return_ctx
def get_absolute_url(self):

View File

@ -66,7 +66,7 @@ class SampleSupplierPlugin(SupplierMixin, InvenTreePlugin):
name=p['name'],
description=p['description'],
exact=p['sku'] == term,
price=f'{p["price"][1][0]:.2f}',
price=f'{p["price"][1][0]:.2f}', # ty:ignore[not-subscriptable]
link=p['link'],
image_url=p['image_url'],
existing_part=getattr(

View File

@ -73,7 +73,9 @@ class ReportConfig(AppConfig):
def cleanup(self):
"""Cleanup old label and report outputs."""
try:
from report.tasks import cleanup_old_report_outputs # type: ignore[import]
from report.tasks import (
cleanup_old_report_outputs, # type: ignore[import] # ty: ignore[unresolved-import]
)
cleanup_old_report_outputs()
except Exception:

View File

@ -1021,7 +1021,7 @@ def format_number(
number = float(number)
if multiplier is not None:
number *= multiplier
number *= float(multiplier)
if integer:
number = int(number)

View File

@ -19,7 +19,7 @@ if __name__ == '__main__':
os.mkdir(STATIC_FOLDER)
print('Downloading tabler icons...')
os.system(f'npm install --prefix {TMP_FOLDER} @tabler/icons @tabler/icons-webfont')
os.system(f'npm install --prefix {TMP_FOLDER} @tabler/icons @tabler/icons-webfont') # ty:ignore[deprecated]
print(f'Copying tabler icons to {STATIC_FOLDER}...')

View File

@ -18,6 +18,7 @@ from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework.serializers import ValidationError
import common.filters
import common.models
import common.settings
import InvenTree.helpers

View File

@ -47,9 +47,7 @@ User.add_to_class('__str__', user_model_str) # Overriding User.__str__
if settings.LDAP_AUTH:
from django_auth_ldap.backend import ( # type: ignore[unresolved-import]
populate_user,
)
from django_auth_ldap.backend import populate_user # ty: ignore[unresolved-import]
@receiver(populate_user)
def create_email_address(user, **kwargs):

View File

@ -1,21 +1,25 @@
# This file was autogenerated by uv via the following command:
# uv pip compile src/backend/requirements-dev.in -o src/backend/requirements-dev-3.14.txt -c src/backend/requirements-3.14.txt --python-version=3.14 -c src/backend/requirements.txt
# uv pip compile src/backend/requirements-dev.in -o src/backend/requirements-dev-3.14.txt -c src/backend/requirements-3.14.txt --python-version=3.14 -c src/backend/requirements.txt -c src/backend/requirements-dev.txt
asgiref==3.11.1 \
--hash=sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce \
--hash=sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# django
build==1.5.0 \
--hash=sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f \
--hash=sha256:302c22c3ba2a0fd5f3911918651341ebb3896176cbdec15bd421f80b1afc7647
# via pip-tools
# via
# -c src/backend/requirements-dev.txt
# pip-tools
certifi==2026.6.17 \
--hash=sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432 \
--hash=sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# requests
cffi==2.0.0 \
@ -105,6 +109,7 @@ cffi==2.0.0 \
--hash=sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# cryptography
charset-normalizer==3.4.7 \
@ -239,13 +244,16 @@ charset-normalizer==3.4.7 \
--hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# pdfminer-six
# requests
click==8.4.1 \
--hash=sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2 \
--hash=sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96
# via pip-tools
# via
# -c src/backend/requirements-dev.txt
# pip-tools
coverage[toml]==7.14.1 \
--hash=sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86 \
--hash=sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd \
@ -353,7 +361,9 @@ coverage[toml]==7.14.1 \
--hash=sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c \
--hash=sha256:fc459e5d73be2d6332fcfe8dbf3d8994671fe33c700f4565988ecfa511547253 \
--hash=sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
cryptography==48.0.1 \
--hash=sha256:08a597acce1ff37f347400087776599e2348a3a8bc53b44120e463cd274efe4a \
--hash=sha256:09f73a725d582cef64b91281a322cd798d14a33b2b6f2b7ad9531dc336d84c02 \
@ -406,6 +416,7 @@ cryptography==48.0.1 \
--hash=sha256:fe0180af5bf9236518a087e35bf2d9a347d5f5f51e63c579d683ddff424e3d46
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# pdfminer-six
django==5.2.15 \
@ -413,6 +424,7 @@ django==5.2.15 \
--hash=sha256:5154a9bf84ac01dde011e367f355c07dbb329532e06810dcf3ef2af269e236e7
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# django-silk
# django-slowtests
@ -420,62 +432,88 @@ django==5.2.15 \
# django-stubs-ext
django-querycount==0.8.3 \
--hash=sha256:0782484e8a1bd29498fa0195a67106e47cdcc98fafe80cebb1991964077cb694
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
django-silk==5.5.0 \
--hash=sha256:41fcabe65d59d31ccdb69daeb3c3e6d30879ab2c00b0875b111fda0f69aec065 \
--hash=sha256:82b5a690d623935be916dd145e2f605a4ac9454d54e03df6a7ffcf38333c8444
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
django-slowtests==1.1.1 \
--hash=sha256:3c6936d420c9df444ac03625b41d97de043c662bbde61fbcd33e4cd407d0c247
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
django-stubs==6.0.5 \
--hash=sha256:7742b8e60afc68a8545158e2bdb103376d5a092f7902c17f370920a9c08eb957 \
--hash=sha256:9fb9eede9b2fc47b36c3dc4a93652eb959dff45466ec4a580e4a22782192d746
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
django-stubs-ext==6.0.5 \
--hash=sha256:1cc325e991303849bce70e19748981b225ef08b37256f263e113caf97cd3bcc0 \
--hash=sha256:a9932c8233d6dd4e34ae0b192026379c1a9be8f0b7c27aa1fa09ae215169773e
# via django-stubs
# via
# -c src/backend/requirements-dev.txt
# django-stubs
django-test-migrations==1.5.0 \
--hash=sha256:1cbff04b1e82c5564a6f635284907b381cc11a2ff883adff46776d9126824f07 \
--hash=sha256:96a08f085fc8bfaa53d44618341d82a2d22fd194c821cd81b147b66f0bec0da8
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
django-types==0.24.0 \
--hash=sha256:af903de8b9ee963b7594459a7a20cb8eaaab176ae2b3244ecaa089e0c570b0d1 \
--hash=sha256:ddb478ca733e0dde5475118dd59ab340156980f9659fd92de2083326ae96100a
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
gprof2dot==2025.4.14 \
--hash=sha256:0742e4c0b4409a5e8777e739388a11e1ed3750be86895655312ea7c20bd0090e \
--hash=sha256:35743e2d2ca027bf48fa7cba37021aaf4a27beeae1ae8e05a50b55f1f921a6ce
# via django-silk
# via
# -c src/backend/requirements-dev.txt
# django-silk
idna==3.18 \
--hash=sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2 \
--hash=sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# requests
iniconfig==2.3.0 \
--hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \
--hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12
# via pytest
# via
# -c src/backend/requirements-dev.txt
# pytest
isort==8.0.1 \
--hash=sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d \
--hash=sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
markdown-it-py==4.2.0 \
--hash=sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49 \
--hash=sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a
# via rich
# via
# -c src/backend/requirements-dev.txt
# rich
mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
# via
# -c src/backend/requirements-dev.txt
# markdown-it-py
packaging==26.2 \
--hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \
--hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# build
# pytest
@ -483,19 +521,27 @@ packaging==26.2 \
pdfminer-six==20260107 \
--hash=sha256:366585ba97e80dffa8f00cebe303d2f381884d8637af4ce422f1df3ef38111a9 \
--hash=sha256:96bfd431e3577a55a0efd25676968ca4ce8fd5b53f14565f85716ff363889602
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
pip==26.1.2 \
--hash=sha256:382ff9f685ee3bc25864f820aa50505825f10f5458ffff07e30a6d96e5715cab \
--hash=sha256:f49cd134c61cf2fd75e0ce2676db03e4054504a5a4986d00f8299ae632dc4605
# via pip-tools
# via
# -c src/backend/requirements-dev.txt
# pip-tools
pip-tools==7.5.3 \
--hash=sha256:3aac0c473240ae90db7213c033401f345b05197293ccbdd2704e52e7a783785e \
--hash=sha256:8fa364779ebc010cbfe17cb9de404457ac733e100840423f28f6955de7742d41
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
pluggy==1.6.0 \
--hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \
--hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746
# via pytest
# via
# -c src/backend/requirements-dev.txt
# pytest
prek==0.4.5 \
--hash=sha256:14109d37b33e5529db41a3539d4f8f72d295f6eeddede3964994d898b8cec05c \
--hash=sha256:1fd98b986767dafdb6b4305b563ee5a3a8f13bd3c78b98d708626815ea9f147f \
@ -514,30 +560,36 @@ prek==0.4.5 \
--hash=sha256:e491a1a4641d91d8b03dcce5588397e76d2a5b432c9b0a6c70475972b4512ab4 \
--hash=sha256:f7517774c72b001573520dc7111156779fd3e5b4452c11f09ff53c71a067e835 \
--hash=sha256:fccd11613ae92619d1ecda0ab3359ceebeb38898909ec84a8d383733d12158cc
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
pycparser==3.0 \
--hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \
--hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# cffi
pygments==2.20.0 \
--hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \
--hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
# via
# -c src/backend/requirements-dev.txt
# pytest
# rich
pyproject-hooks==1.2.0 \
--hash=sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8 \
--hash=sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913
# via
# -c src/backend/requirements-dev.txt
# build
# pip-tools
pytest==9.1.1 \
--hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \
--hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c
# via
# -c src/backend/requirements-dev.txt
# pytest-codspeed
# pytest-django
pytest-codspeed==5.0.3 \
@ -570,31 +622,41 @@ pytest-codspeed==5.0.3 \
--hash=sha256:f56d0339cd98d26f6e561987be25bdd2761a5d53d8f73493b1ebe02d0d451093 \
--hash=sha256:f852bee785a7a124cb1720b1915670c6742af87747dc4d838f3ffdbd365ce9d9 \
--hash=sha256:fe2ea83c924c2250675b75686c3ee456b8cf0208d83d552e182a195fdf467378
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
pytest-django==4.12.0 \
--hash=sha256:3ff300c49f8350ba2953b90297d23bf5f589db69545f56f1ec5f8cff5da83e85 \
--hash=sha256:df94ec819a83c8979c8f6de13d9cdfbe76e8c21d39473cfe2b40c9fc9be3c758
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
requests==2.34.2 \
--hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \
--hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# requests-mock
requests-mock==1.12.1 \
--hash=sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563 \
--hash=sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401
# via -r src/backend/requirements-dev.in
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
rich==15.0.0 \
--hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \
--hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36
# via pytest-codspeed
# via
# -c src/backend/requirements-dev.txt
# pytest-codspeed
setuptools==82.0.1 \
--hash=sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9 \
--hash=sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# -r src/backend/requirements-dev.in
# pip-tools
@ -603,42 +665,50 @@ sqlparse==0.5.5 \
--hash=sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# django
# django-silk
ty==0.0.1a21 \
--hash=sha256:0efba2e52b58f536f4198ba5c4a36cac2ba67d83ec6f429ebc7704233bcda4c3 \
--hash=sha256:1474d883129bb63da3b2380fc7ead824cd3baf6a9551e6aa476ffefc58057af3 \
--hash=sha256:1f276ceab23a1410aec09508248c76ae0989c67fb7a0c287e0d4564994295531 \
--hash=sha256:218d53e7919e885bd98e9196d9cb952d82178b299aa36da6f7f39333eb7400ed \
--hash=sha256:21f708d02b6588323ffdbfdba38830dd0ecfd626db50aa6006b296b5470e52f9 \
--hash=sha256:334d2a212ebf42a0e55d57561926af7679fe1e878175e11dcb81ad8df892844e \
--hash=sha256:3c3bc66fcae41eff133cfe326dd65d82567a2fb5d4efe2128773b10ec2766819 \
--hash=sha256:5dfc73299d441cc6454e36ed0a976877415024143dfca6592dc36f7701424383 \
--hash=sha256:7505aeb8bf2a62f00f12cfa496f6c965074d75c8126268776565284c8a12d5dd \
--hash=sha256:84243455f295ed850bd53f7089819321807d4e6ee3b1cbff6086137ae0259466 \
--hash=sha256:87a200c21e02962e8a27374d9d152582331d57d709672431be58f4f898bf6cad \
--hash=sha256:9463cac96b8f1bb5ba740fe1d42cd6bd152b43c5b159b2f07f8fd629bcdded34 \
--hash=sha256:a8c769987d00fbc33054ff7e342633f475ea10dc43bc60fb9fb056159d48cb90 \
--hash=sha256:ba13d03b9e095216ceb4e4d554a308517f28ab0a6e4dcd07cfe94563e4c2c489 \
--hash=sha256:be8f457d7841b7ead2a3f6b65ba668abc172a1150a0f1f6c0958af3725dbb61a \
--hash=sha256:cc0880ec344fbdf736b05d8d0da01f0caaaa02409bd9a24b68d18d0127a79b0e \
--hash=sha256:e941e9a9d1e54b03eeaf9c3197c26a19cf76009fd5e41e16e5657c1c827bd6d3 \
--hash=sha256:ecf41706b803827b0de8717f32a434dad1e67be9f4b8caf403e12013179ea06a
# via -r src/backend/requirements-dev.in
ty==0.0.51 \
--hash=sha256:08adbe53fb8bc9e7f00e89bf1d3c875a02cda76d83f109d2e6ab1ff35a7bfa8c \
--hash=sha256:25a5b31e6f23fd5dc63ad29087ded09932409e4154e2fe07bbaed015035990bb \
--hash=sha256:2faed19a8f1505370de071c008df52a994fc03a204f3267c3a33a32ca26f854f \
--hash=sha256:429a997394dac73870d71b87cc90efc54da3efaf319e72ca18aeef35a78aef90 \
--hash=sha256:49a21237f6fd1de56beaff0a3e85fe022a09a3401e67e3abec41ce838a5d4d2e \
--hash=sha256:608d417cd1eaf79bcbd713d9830d5e3db9d57ec225c3af3e4ac9a9ff66b45d70 \
--hash=sha256:61b4b6a003c3ebe53a63a1125c9b6542aa01bc1b6c9a235d01ee328d000d61a9 \
--hash=sha256:62ced5e380284f12b2dc4802a3e4ed3dac39913fc6719afde7978814a4c7f169 \
--hash=sha256:62d94f06e8c317e89b6884f2bde443040e596b88c7c79bd944c84c105b06257a \
--hash=sha256:79d1877e93460f936bc10ed1a31525702b7ce51075763ccba993be17f0b9e905 \
--hash=sha256:947986bd82d324b3a5c58ce03f1dad160cdf36443d3e8f64b3484b861ba9bc64 \
--hash=sha256:abd92913bc90d1705ef9391ff8c6822b61e2e827fa295eb30bf0dfabcf815645 \
--hash=sha256:b90172d46365bb9d51a7011cbb5c60cc4f514f42c86635df6c092b717f85e1ac \
--hash=sha256:bc7459348a253247bbfb2669a021e614281b86bbea24c36112b8a6e1a2499a16 \
--hash=sha256:c1bd1355aee86af01e4e21b0bc16fc460fb05905761f0d8b8d70841de0feade8 \
--hash=sha256:cc233a6235fb23e2a44b14731a10043e37ba2f30f2c361cf49ad3633c5b9da9c \
--hash=sha256:dc5e93695ab5dcbf1eef663aee60ec23a413547cc9cb06adcb0d842e9166bd0f \
--hash=sha256:f8f52952cff665bc52a36147e610c10f5699d30007d7a14ab7f345cff93476ff
# via
# -c src/backend/requirements-dev.txt
# -r src/backend/requirements-dev.in
types-psycopg2==2.9.21.20260518 \
--hash=sha256:2fd728a4fa3860db0a4a9813e5f49c30ed329b3d2e60e73530d9db01b2b98420 \
--hash=sha256:8b1f80d90d6799a4fcdac12198b382a8feee9ed4340d5f69b56fc5ffa0644143
# via django-types
# via
# -c src/backend/requirements-dev.txt
# django-types
types-pyyaml==6.0.12.20260518 \
--hash=sha256:d2150f75a231c9fe9c7463bd29487d93e60bac90400287351384bc2284eba7cd \
--hash=sha256:d917f83fb38462550338c1297faedd860b3ec83912b96b1e3d73255f7473e466
# via django-stubs
# via
# -c src/backend/requirements-dev.txt
# django-stubs
typing-extensions==4.15.0 \
--hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
--hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# django-stubs
# django-stubs-ext
@ -648,9 +718,12 @@ urllib3==2.7.0 \
--hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897
# via
# -c src/backend/requirements-3.14.txt
# -c src/backend/requirements-dev.txt
# -c src/backend/requirements.txt
# requests
wheel==0.47.0 \
--hash=sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced \
--hash=sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3
# via pip-tools
# via
# -c src/backend/requirements-dev.txt
# pip-tools

View File

@ -643,25 +643,25 @@ tomli==2.4.1 \
--hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \
--hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049
# via coverage
ty==0.0.1a21 \
--hash=sha256:0efba2e52b58f536f4198ba5c4a36cac2ba67d83ec6f429ebc7704233bcda4c3 \
--hash=sha256:1474d883129bb63da3b2380fc7ead824cd3baf6a9551e6aa476ffefc58057af3 \
--hash=sha256:1f276ceab23a1410aec09508248c76ae0989c67fb7a0c287e0d4564994295531 \
--hash=sha256:218d53e7919e885bd98e9196d9cb952d82178b299aa36da6f7f39333eb7400ed \
--hash=sha256:21f708d02b6588323ffdbfdba38830dd0ecfd626db50aa6006b296b5470e52f9 \
--hash=sha256:334d2a212ebf42a0e55d57561926af7679fe1e878175e11dcb81ad8df892844e \
--hash=sha256:3c3bc66fcae41eff133cfe326dd65d82567a2fb5d4efe2128773b10ec2766819 \
--hash=sha256:5dfc73299d441cc6454e36ed0a976877415024143dfca6592dc36f7701424383 \
--hash=sha256:7505aeb8bf2a62f00f12cfa496f6c965074d75c8126268776565284c8a12d5dd \
--hash=sha256:84243455f295ed850bd53f7089819321807d4e6ee3b1cbff6086137ae0259466 \
--hash=sha256:87a200c21e02962e8a27374d9d152582331d57d709672431be58f4f898bf6cad \
--hash=sha256:9463cac96b8f1bb5ba740fe1d42cd6bd152b43c5b159b2f07f8fd629bcdded34 \
--hash=sha256:a8c769987d00fbc33054ff7e342633f475ea10dc43bc60fb9fb056159d48cb90 \
--hash=sha256:ba13d03b9e095216ceb4e4d554a308517f28ab0a6e4dcd07cfe94563e4c2c489 \
--hash=sha256:be8f457d7841b7ead2a3f6b65ba668abc172a1150a0f1f6c0958af3725dbb61a \
--hash=sha256:cc0880ec344fbdf736b05d8d0da01f0caaaa02409bd9a24b68d18d0127a79b0e \
--hash=sha256:e941e9a9d1e54b03eeaf9c3197c26a19cf76009fd5e41e16e5657c1c827bd6d3 \
--hash=sha256:ecf41706b803827b0de8717f32a434dad1e67be9f4b8caf403e12013179ea06a
ty==0.0.51 \
--hash=sha256:08adbe53fb8bc9e7f00e89bf1d3c875a02cda76d83f109d2e6ab1ff35a7bfa8c \
--hash=sha256:25a5b31e6f23fd5dc63ad29087ded09932409e4154e2fe07bbaed015035990bb \
--hash=sha256:2faed19a8f1505370de071c008df52a994fc03a204f3267c3a33a32ca26f854f \
--hash=sha256:429a997394dac73870d71b87cc90efc54da3efaf319e72ca18aeef35a78aef90 \
--hash=sha256:49a21237f6fd1de56beaff0a3e85fe022a09a3401e67e3abec41ce838a5d4d2e \
--hash=sha256:608d417cd1eaf79bcbd713d9830d5e3db9d57ec225c3af3e4ac9a9ff66b45d70 \
--hash=sha256:61b4b6a003c3ebe53a63a1125c9b6542aa01bc1b6c9a235d01ee328d000d61a9 \
--hash=sha256:62ced5e380284f12b2dc4802a3e4ed3dac39913fc6719afde7978814a4c7f169 \
--hash=sha256:62d94f06e8c317e89b6884f2bde443040e596b88c7c79bd944c84c105b06257a \
--hash=sha256:79d1877e93460f936bc10ed1a31525702b7ce51075763ccba993be17f0b9e905 \
--hash=sha256:947986bd82d324b3a5c58ce03f1dad160cdf36443d3e8f64b3484b861ba9bc64 \
--hash=sha256:abd92913bc90d1705ef9391ff8c6822b61e2e827fa295eb30bf0dfabcf815645 \
--hash=sha256:b90172d46365bb9d51a7011cbb5c60cc4f514f42c86635df6c092b717f85e1ac \
--hash=sha256:bc7459348a253247bbfb2669a021e614281b86bbea24c36112b8a6e1a2499a16 \
--hash=sha256:c1bd1355aee86af01e4e21b0bc16fc460fb05905761f0d8b8d70841de0feade8 \
--hash=sha256:cc233a6235fb23e2a44b14731a10043e37ba2f30f2c361cf49ad3633c5b9da9c \
--hash=sha256:dc5e93695ab5dcbf1eef663aee60ec23a413547cc9cb06adcb0d842e9166bd0f \
--hash=sha256:f8f52952cff665bc52a36147e610c10f5699d30007d7a14ab7f345cff93476ff
# via -r src/backend/requirements-dev.in
types-psycopg2==2.9.21.20260518 \
--hash=sha256:2fd728a4fa3860db0a4a9813e5f49c30ed329b3d2e60e73530d9db01b2b98420 \

View File

@ -131,7 +131,9 @@ export async function doBasicLogin(
default:
notifications.show({
title: `${t`Login failed`} (${err.response.status})`,
message: t`Check your input and try again.`,
message:
err.response?.data?.detail ??
t`Check your input and try again.`,
id: 'auth-login-error',
color: 'red'
});

View File

@ -906,6 +906,23 @@ def backend_trans(c, verbose: bool = False):
success('Backend translations compiled successfully')
@task(help={'verbose': 'Print verbose output'})
@state_logger('backend_compilemessages')
def backend_compilemessages(c, verbose: bool = False):
"""Compile backend Django translation files without loading InvenTree settings."""
info('Compiling backend translations...')
cmd = 'python3 -m django compilemessages'
if verbose:
cmd += ' -v 1'
else:
cmd += ' -v 0'
run(c, cmd, manage_py_dir())
success('Backend translations compiled successfully')
@task(
help={
'clean': 'Clean up old backup files',
@ -2490,6 +2507,7 @@ internal = Collection(
clear_generated,
export_settings_definitions,
export_definitions,
backend_compilemessages,
frontend_build,
frontend_check,
frontend_compile,