Files
production-order-tiny/README.md
Cauê Faleiros baade0a980
All checks were successful
production-orders-sync CI/CD / Test (push) Successful in 9m32s
production-orders-sync CI/CD / Build and Push Image (push) Successful in 11s
production-orders-sync CI/CD / Trigger Portainer (push) Successful in 2s
Make compose stack Portainer compatible
2026-07-02 15:18:44 -03:00

5.8 KiB

production-orders-sync

Standalone worker that syncs Tiny ERP production orders directly into the existing Graphs Postgres tables used by GET /api/production-orders.

This is intentionally separate from api-tiny-n8n; it does not call n8n and does not depend on the Tiny -> n8n bridge.

Sync Design

Tiny/Olist API 2.0 public webhook docs do not list an official production-order webhook/event. The documented webhook/event surfaces found are for stock updates, product sending, tracking code sending, invoice sending, product price sending, sales order status updates, and freight quote lookup.

The production-order API page found is an action endpoint, gerar.ordem.producao.pedido.php, which generates production orders from a sales order. That is not a live production-order webhook.

Docs checked:

For that reason, this worker is designed around:

  • backfill mode for historical date ranges
  • recent polling mode for periodic syncs, usually every 15 or 30 minutes from an external scheduler
  • idempotent upserts by tiny_id, so repeated polling updates existing rows without duplicates

An optional future optimization is for a sales-order webhook in the existing middleware to trigger a recent production-order sync. Polling should still remain the source of truth because production-order status can change later without a sales-order webhook.

What It Writes

The worker upserts production_orders by tiny_id, stores the full Tiny production-order object in tiny_payload, updates updated_at on every conflict update, and replaces production_order_markers for each synced order.

It preserves the existing frontend/backend contract:

  • GET /api/production-orders?start=YYYY-MM-DD&end=YYYY-MM-DD&search=...
  • Response shape: { orders, counts }
  • Status values: open, in_progress, finished, canceled

Configure

Copy .env.example to .env and fill in:

TINY_API_TOKEN=...
DATABASE_URL=postgres://...
SYNC_MODE=backfill
SYNC_START_DATE=2026-07-02
SYNC_END_DATE=2026-07-02
DRY_RUN=true
TINY_REQUEST_DELAY_MS=5000

Tiny production-order endpoint names vary by API/account documentation. I could not confirm public documentation for these exact production-order endpoint names, so treat the defaults as inferred and account-specific until dry-run logs prove the payload shape:

TINY_LIST_ENDPOINT=ordens.producao.pesquisa.php
TINY_DETAIL_ENDPOINT=ordem.producao.obter.php
TINY_FETCH_DETAILS=false

If Tiny returns only summary rows from the list endpoint, set TINY_FETCH_DETAILS=true after confirming the detail endpoint for the account.

The worker keeps SYNC_START_DATE and SYNC_END_DATE in YYYY-MM-DD format. Tiny request dates default to DD/MM/YYYY; override with TINY_DATE_FORMAT=YYYY-MM-DD if the endpoint expects ISO dates.

Backfill behavior:

  • SYNC_MODE=backfill
  • SYNC_START_DATE is required.
  • SYNC_END_DATE is optional; when empty, the worker syncs through today in SYNC_TIME_ZONE, default America/Sao_Paulo.

Recent polling behavior:

  • SYNC_MODE=recent
  • RECENT_SYNC_DAYS=2 syncs today plus yesterday.
  • Run the same manual command from cron, Swarm, Kubernetes, or another scheduler every 15 or 30 minutes.
  • Upserts by tiny_id keep repeated runs idempotent.

This worker does not include its own scheduler yet; deployment should run it manually first, then schedule the same command externally.

Manual Run

npm install
npm run sync

For a no-write validation run:

DRY_RUN=true \
SYNC_MODE=backfill \
SYNC_START_DATE=2026-07-02 \
SYNC_END_DATE=2026-07-02 \
TINY_REQUEST_DELAY_MS=5000 \
npm run sync

Inspect the dry-run logs before enabling writes. Dry-run logs include the Tiny response shape, discovered array paths, the first extracted raw order preview, and the exact production_orders / production_order_markers values that would be written.

Docker

Build and run the worker once:

docker build -t production-orders-sync:local .
PRODUCTION_ORDERS_SYNC_IMAGE=production-orders-sync:local docker compose run --rm production-orders-sync

The optional local Postgres example is in docker-compose.local-db.yml. In normal deployment, set DATABASE_URL to the existing Graphs Postgres database.

docker compose -f docker-compose.local-db.yml up -d postgres

Gitea Actions + Portainer

CI/CD is defined in .gitea/workflows/ci-cd.yml:

  • run npm test
  • run npm run check
  • build and push the Docker image
  • trigger the Portainer stack webhook when PORTAINER_WEBHOOK_URL is configured

Portainer stack templates live in deploy/:

  • deploy/portainer-one-shot.yml for first dry-run checks and backfills
  • deploy/portainer-stack.yml for the long-running recent polling stack

See deploy/README.md for required Gitea secrets and Portainer environment variables.

Logs

The worker logs:

  • start/end config without secrets
  • [Production Orders Sync] prefix for easy separation from api-tiny-n8n
  • current page, endpoint, date range, and Tiny-formatted request dates
  • orders found per page
  • mapped rows and dry-run rows that would be upserted
  • rows upserted
  • skipped/invalid rows with raw payload preview
  • Tiny block/rate-limit messages and retries

Tiny Field Mapping

The mapper accepts several common Tiny/PT-BR field variants. Required fields are:

  • Tiny production order ID, mapped to tiny_id
  • product description, mapped to product_description

Status normalization accepts:

  • em_aberto, em aberto, aberta -> open
  • andamento, em andamento -> in_progress
  • finalizada, finalizado -> finished
  • cancelada, cancelado, cancelled -> canceled