Metadata-Version: 2.5
Name: peresmichael-tax-sdk
Version: 0.1.0
Summary: Python client for the Novabook tax service: record sales, amendments and tax payments, query your tax position at any point in time.
Project-URL: Homepage, https://github.com/makiisthenes/ScalableTaxService
Project-URL: Documentation, https://tax.peresmichael.com/doc
Project-URL: Source, https://github.com/makiisthenes/ScalableTaxService/tree/main/packages/sdk-python
Author-email: Michael Peres <michaelperes1@gmail.com>
License-Expression: ISC
Keywords: api-client,novabook,pydantic,sdk,tax,vat
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
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: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.7
Description-Content-Type: text/markdown

# peresmichael-tax-sdk

Python client for the Novabook tax service. Record sales, amend them, log
tax payments, and query your tax position at any point in time — with
pydantic validation built in. The Python twin of
[`@peresmichael/tax-sdk`](https://www.npmjs.com/package/@peresmichael/tax-sdk)
on npm.

```bash
pip install peresmichael-tax-sdk
```

## Usage

```python
from datetime import datetime, timezone
from tax_sdk import TaxClient

tax = TaxClient(base_url="https://tax.peresmichael.com")

# record a sale
tax.sale(
    date=datetime.now(timezone.utc),
    invoice_id="3419027d-960f-4e8f-b8b7-f7b2b4791824",
    items=[{"item_id": "02db47b6-fe68-4005-a827-24c6e962f3df", "cost": 1099, "tax_rate": 0.2}],
)

# amend an item later — even before its sale has been recorded
tax.amend(date=date, invoice_id=invoice_id, item_id=item_id, cost=798, tax_rate=0.15)

# record a payment towards the tax owed
tax.tax_payment(date=date, amount=15000)

# your tax position at any date, past or future
position = tax.position(datetime.now(timezone.utc))
print(position.tax_position)
```

All amounts are integers in pennies. Cost `1099` is £10.99. Dates can be
`datetime` objects (naive ones are taken as UTC) or ISO 8601 strings.

There is an `AsyncTaxClient` with exactly the same methods for asyncio
code — `await tax.sale(...)` and so on.

## Methods

| Method | What it does |
|--------|--------------|
| `sale(date=, invoice_id=, items=)` | Records a sales event (one or more items on an invoice). |
| `amend(date=, invoice_id=, item_id=, cost=, tax_rate=)` | Changes an item's cost/tax rate from a given date. Works even if the sale arrives later. |
| `tax_payment(date=, amount=)` | Records a payment made towards the tax owed. |
| `position(date)` | Returns the tax owed as of that date, in pennies, with the date and `request_id`. |
| `health()` | `True` when the service is reachable. |

## Options

```python
TaxClient(
    base_url="https://...",   # required
    retries=3,                # attempts per call, 1 disables retrying
    headers={...},            # extra headers on every request
    timeout=10.0,             # seconds per attempt
    transport=...,            # custom httpx transport (proxies, testing)
)
```

## Good to know

- **Writes are queued, not instant.** `sale`, `amend` and `tax_payment`
  return when the service has durably accepted the event (HTTP 202). A
  `position()` read in the same instant may not include it yet — wait a
  moment or poll if you need read-after-write.
- **Retries are automatic and safe.** The service ignores duplicate
  submissions of the same event for five minutes, so a retried timeout can
  never double-count a sale.
- **Keep the `request_id`.** Every result includes one; it is the reference
  the service can trace your exact request by if a number ever needs
  explaining. Pass your own via `sale(..., request_id=...)` to correlate
  with your systems.
- **Errors are typed.** Invalid input raises a pydantic `ValidationError`
  locally, before any network call. Service errors raise `TaxApiError`
  with `status`, `code`, `message` and the `request_id` to quote.

```python
from tax_sdk import TaxApiError

try:
    tax.sale(date=date, invoice_id=invoice_id, items=items)
except TaxApiError as err:
    print(err.status, err.code, err.request_id)
```

## Requirements

Python 3.10+. Depends on `httpx` and `pydantic` v2. Fully type-annotated
(`py.typed`).

---

Models and validation in this package mirror the service's own zod
schemas, so they always match what the API accepts.
