Metadata-Version: 2.4
Name: django-toconline
Version: 2.0.1
Summary: Django integration for the TOConline API
License-Expression: MIT
License-File: LICENSE
Keywords: Django,TOConline,API
Author: Daniel Pinto
Author-email: dmp593@gmail.com
Requires-Python: >=3.10,<4.0
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Dist: Django (>=5.2,<7.0)
Requires-Dist: pydantic (>=2.12,<3.0)
Requires-Dist: requests (>=2.32.5,<3.0)
Project-URL: Documentation, https://github.com/dmp593/django-toconline#readme
Project-URL: Repository, https://github.com/dmp593/django-toconline
Description-Content-Type: text/markdown

# Django TOConline

A Django integration and complete OpenAPI client for Portugal's TOConline
commercial API.

The package persists OAuth2 tokens and exposes a complete spec-driven OpenAPI
client. It returns Pydantic models for documented successful response schemas
and raw JSON when the OpenAPI document does not provide a response schema. An
opt-in resource view also flattens recognised JSON:API responses for concise
application code.

Official sources:

- [TOConline API documentation](https://api-docs.toconline.pt/)
- [TOConline OpenAPI specification](https://app.swaggerhub.com/apis-docs/toconline.pt/toc-online_open_api/1.0.0)
- [Compatibility audit for this package](https://github.com/dmp593/django-toconline/blob/main/docs/api-compatibility.md)

## Compatibility

| Django | Python |
| --- | --- |
| 5.2 | 3.10–3.14 |
| 6.0 | 3.12–3.14 |

Django 6.0 itself requires Python 3.12 or newer. The package retains Django
5.2 support so applications still on Python 3.10 or 3.11 have a supported
upgrade path.

## Installation

```bash
python -m pip install django-toconline
```

Add the application to `INSTALLED_APPS` and run migrations:

```python
INSTALLED_APPS = [
    # ...
    "toconline",
]
```

```bash
python manage.py migrate
```

## Configuration

Keep all credentials in environment-backed settings. TOConline supplies the
API URL and OAuth URL with the integration credentials for each company.

```python
import os

TOCONLINE_BASE_URL = os.environ["TOCONLINE_BASE_URL"]
TOCONLINE_OAUTH_BASE_URL = os.environ.get(
    "TOCONLINE_OAUTH_BASE_URL",
    f"{TOCONLINE_BASE_URL}/oauth",
)
TOCONLINE_OAUTH_CLIENT_ID = os.environ["TOCONLINE_OAUTH_CLIENT_ID"]
TOCONLINE_OAUTH_CLIENT_SECRET = os.environ[
    "TOCONLINE_OAUTH_CLIENT_SECRET"
]
TOCONLINE_OAUTH_REDIRECT_URI = os.environ.get(
    "TOCONLINE_OAUTH_REDIRECT_URI",
    "https://oauth.pstmn.io/v1/callback",
)
TOCONLINE_TIMEOUT = 10
```

`TOCONLINE_BASE_URL` is the API host URL without `/api`.
`TOCONLINE_OAUTH_BASE_URL` is the OAuth service URL; it defaults to
`<TOCONLINE_BASE_URL>/oauth` for backward compatibility.

TOConline test tenants can expose those services on different hosts. In that
case configure both explicitly, for example an `api10.toconline.pt` API host
and an `app10.toconline.pt` OAuth host. Do not point `TOCONLINE_BASE_URL` at
the OAuth host: the client appends `/api` to that setting.

## Complete v2 API client

Version 2.0.1 implements all 120 operations published in TOConline's OpenAPI
1.0.0 document. The v2 client is exposed lazily from the configured Django
OAuth transport, so it uses the same secure token persistence, timeout, and
host validation.

```python
from toconline.services import toconline
from toconline.api import models

document = toconline.api.sales.create_sales_document(
    body=models.ApiV1CommercialSalesDocumentsPostRequest(
        date="2026-07-22",
        document_type="FT",
        customer_tax_registration_number="999999990",
        lines=[],
    )
)
```

Body dictionaries are accepted and validated too:

```python
purchase = toconline.api.purchases.finalize_purchase_document(
    id="purchase-document-id",
    body={},
)
```

Available namespaces are `company`, `catalog`, `sales`, `purchases`,
`documents`, and `auxiliaries`. Every endpoint has one English, typed method
name, for example `toconline.api.company.list_customers()`. Each method accepts
`params=` for documented query filters.

See [the v2 OpenAPI client guide](docs/v2-openapi-client.md) for the complete
developer guide: Django and standalone setup, token storage, extending
transports, basic API operations, financial/document workflows, errors,
testing, generated models, endpoint registry, and regeneration.

Package maintainers should read the [OpenAPI generation and maintenance
guide](docs/openapi-generation.md) before changing the vendored specification
or generated client files.

### Without Django

For a CLI, worker, or another Python framework, use the included in-memory
OAuth transport instead of writing one:

```python
from toconline.api import DefaultTransport, TocOnlineCredentials

transport = DefaultTransport(
    base_url="https://api10.toconline.pt",
    oauth_base_url="https://app10.toconline.pt/oauth",
    credentials=TocOnlineCredentials(
        client_id="...",
        client_secret="...",
        redirect_uri="https://oauth.pstmn.io/v1/callback",
    ),
)

customers = transport.api.company.list_customers()
```

`DefaultTransport` keeps the OAuth token in memory and refreshes it when
needed. The Django `TocOnline` service extends this class and only replaces
the token storage with the `TocOnlineToken` model.

### Ergonomic JSON:API resources

`transport.api` preserves the OpenAPI response exactly. For recognised
JSON:API responses, use `transport.resources` to access fields directly:

```python
services = transport.resources.catalog.list_services()
services[0].accounting_number

customers = transport.resources.company.list_customers()
customers[0].business_name
```

It can also create known JSON:API envelopes and infer their resource type:

```python
customer = transport.resources.company.create_customer(
    attributes={
        "business_name": "Example, Lda.",
        "tax_registration_number": "999999990",
    }
)
```

Non-JSON:API results, such as regular objects, PDF bytes, URLs, and empty
responses, remain unchanged. See the [v2 OpenAPI client guide](docs/v2-openapi-client.md#jsonapi-resource-view)
for request rules, collection endpoints, and the distinction from typed
generated models.

Authentication is automatic. The first request obtains and stores a token in
`TocOnlineToken`; subsequent requests refresh it before expiry. Token values
are deliberately hidden from Django admin, but the application database and
its backups must still be treated as sensitive.

## Development and tests

The test suite is deterministic and does not contact TOConline or use real
credentials.

The OpenAPI transport contract test covers all 120 published operations. An
additional opt-in live test exercises every GET operation through the real v2
client without saving OAuth tokens or sending request bodies. It is deliberately
off by default and must use a disposable tenant:

```bash
cp .env.example .env
# Edit .env with disposable-tenant credentials.
make test-live
```

`make test-live` safely loads the key/value pairs from `.env` only for that
test process; it never executes `.env` as shell code. `.env` is ignored by
Git; `.env.example` is the versioned template. Do not place production
credentials in either local live-test configuration.

Set `TOCONLINE_LIVE_CASH_ACCOUNT_ID` as well when the tenant has no existing
cash account but you need to exercise `GET /api/cash_accounts/{id}`. Mutating
operations remain covered by deterministic contract tests; their live safety
policy is enforced by `test_live_openapi.py`. Do not enable live mutations on a
production tenant, or send e-mail / communicate documents to the Tax Authority
without a separately approved sandbox workflow.

```bash
make requirements-test
make test
```

To regenerate the checked-in OpenAPI models and endpoint registry after an
intentional spec update:

```bash
python -m pip install -r requirements-dev.txt
python scripts/generate_openapi_artifacts.py
python scripts/generate_openapi_artifacts.py --check
```

To expose deprecation warnings during framework upgrades:

```bash
.venv/bin/python -Wa manage.py test tests
```

CI covers the lower supported Python/Django pair, Django 6 on its minimum
Python version, and the newest supported Python/Django pair.

## Upgrade notes for 2.0.0

- The package version is now 2.0.0 because the new v2 API has typed OpenAPI
  response semantics instead of the legacy JSON:API-unwrapping behavior.
- All 120 operations in the vendored TOConline OpenAPI 1.0.0 document are
  available under `toconline.api` and protected by a spec-coverage test.
- `pydantic>=2.12,<3.0` is now a runtime dependency.

## Breaking changes in 2.0.0

- The legacy `toconline.resources` module and generic `TocOnline` CRUD,
  document, and raw-request helpers were removed.
- Use the complete generated client under `toconline.api` for every published
  TOConline operation. Applications that require the legacy surface must keep
  using a 1.x release.

## Upgrade notes from 1.0.x

- The supported Django range is now `>=5.2,<7.0`, which permits Django 6.
- OAuth and API hosts may be configured separately.
- Test settings no longer contain credential-like values, and tests no longer
  mutate a live TOConline account.

## License

MIT. See [LICENSE](LICENSE).

