feat: generate print files, collect delivery addresses, add provider adapters
All checks were successful
Build and deploy / Validate source (push) Successful in 1m45s
Build and deploy / Integration suite on a real stack (push) Successful in 4m48s
Build and deploy / Secret scan and release gate (push) Successful in 11s
Build and deploy / Publish images and notify Portainer (push) Has been skipped

Week 2 work that did not need client inputs.

Print files (1.4): each paid item gets a PDF the width of the film and the
length of the approved layout, with every copy at its reviewed position,
rotation and mirror. Sources are embedded once at original resolution; JPEG
bytes pass through and PNG alpha becomes a soft mask. Artwork the generator
cannot reproduce goes to hand preparation with the reason. The worker renders
outside any transaction, and the operator approves the generated file as the
final one through the existing review.

Delivery address (3.8): required for any non-pickup quote, bound to the
quoted CEP, carried into the order snapshot, the Kanban card and Tiny.

Kanban (1.5): print-file status per item, and a panel of payment events that
need a person (money without an order, refunds after an order) until an
operator records the resolution.

Mercado Pago and Tiny (1.1, 1.3): adapters written from the public API
documentation and tested against fake transports only. Selectable for
sandbox testing with their credentials; the production preflight still
blocks release. Adds payment intents and a PIX step on the Site.

MinIO: Docker Hub and quay.io now refuse anonymous pulls, so local and CI
storage use Chainguard's MinIO build, pinned by digest.

Verified with the full CI integration sequence on a fresh local build,
including the new print_file_test and both browser suites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-24 11:56:46 -03:00
parent cfcbe545f1
commit c18b9e5b87
35 changed files with 2032 additions and 61 deletions

View File

@@ -72,7 +72,9 @@ operator interfaces.
the buttons or drag and drop to **Arte tratada**. Open **Arquivos de produção**,
select the manually prepared final files for every item, enter a review note,
tick the confirmation and click **Aprovar arquivos finais**. Use the harmless
fixture again only for this local test; no printable file is generated.
fixture again only for this local test. The text fixture is not an image, so
its print file shows **preparar à mão**; with a PNG or JPEG artwork the
generated PDF is preselected instead (see "Print files").
Continue through **Fila de impressão →
Imprimindo → Finalizado**. A move to **Correção** requires a reason; it can
return to **Arte recebida** or **Arte tratada**. Finalizado is terminal locally.
@@ -193,8 +195,10 @@ must resolve from the browser; keep `http://localhost:9000` for this stack.
Parts use 15-minute presigned URLs and unfinished reservations expire after
one hour. Clients can cancel a reservation through the upload DELETE endpoint.
Only fake integration adapters and `s3-local` storage are accepted. Startup fails
if a production adapter/environment or nonlocal S3 endpoint is selected.
Fake integration adapters and `s3-local` storage are the default. Mercado Pago
and Tiny can be selected for sandbox testing only (see "Provider sandboxes");
startup fails if their credentials are missing, if any other adapter is
selected, or if a production environment or nonlocal S3 endpoint is used.
The API, worker, and database run on an internal Docker network; web gateways
and MinIO also join a network that permits loopback port publishing.
@@ -216,6 +220,64 @@ storage permits; otherwise reselect them. Saved quote IDs also survive reloads.
Clearing cookies loses a guest session, while registered customers can sign in
again. The operator can still inspect order records.
## Print files
Every paid order queues one print-file job per item. The worker renders a PDF
exactly as wide as the film and as long as the approved layout, placing each
copy at the position, rotation and mirror the customer reviewed. Each source
image is embedded once at its original resolution (JPEG bytes unchanged, PNG
transparency kept as a soft mask), so nothing is resampled. The Kanban card
shows the status per item, the page size and the lowest DPI, and offers
**Baixar PDF**. In **Arquivos de produção** the generated file is preselected
as the final file; untick it to upload one by hand instead.
Only JPEG, PNG, WebP and TIFF are generated. PDF, PSD, AI and CDR artwork, a
file whose proportions do not match the quoted size, a layout longer than was
billed, or an image above `PRINT_MAX_PIXELS` (250 Mpx by default) goes to
**preparar à mão** with the reason, and the operator prepares it as before.
**Gerar arquivos de impressão** retries those items and generates files for
orders paid before the generator existed. After a customer correction the
generated file is no longer offered: it reproduces the replaced artwork.
Layouts longer than about 5 m use the PDF `UserUnit` page scale instead of
being split into pages. Confirm on the factory's FlexiPRINT that such a file
imports at full length before relying on it for long orders.
```bash
docker compose -f compose.local.yaml exec -T api python -m unittest tests.test_printfile -v
docker compose -f compose.local.yaml exec -T api python -m tests.print_file_test
```
With PyMuPDF installed locally (`pip install pymupdf`), the unit suite also
draws each page and checks where every quadrant of the artwork lands.
## Provider sandboxes
`app/mercadopago.py` and `app/tiny.py` follow the providers' public API
documentation and pass their unit suites against a fake transport. They are not
verified integrations until they pass with the client's sandbox accounts.
Put only **test** credentials in `.env`:
```bash
PAYMENT_ADAPTER=mercadopago
MP_ACCESS_TOKEN=TEST-...
MP_WEBHOOK_SECRET=... # "Assinatura secreta" in the webhook settings
MP_NOTIFICATION_URL=https://<public tunnel>/api/payments/webhook
TINY_ADAPTER=tiny
TINY_TOKEN=...
TINY_TAG=Site DTF # optional marker on created orders
```
With Mercado Pago selected, an approved quote shows **Pagar com PIX** on the
Site instead of the local test button. The order is created only by the signed
notification, after the payment is fetched from the Mercado Pago API and its
BRL amount matches the approved total. Mercado Pago must be able to reach the
webhook, so a local run needs a public HTTPS tunnel to the Site port. A paid
notification that cannot become an order, or a refund on an existing order,
appears under **Pagamentos que precisam de atenção** on the Kanban until an
operator records the resolution. Card payment needs the Mercado Pago public key
and its card form on the Site; that part is not built yet.
## Local backup and restore check
```bash