first commit
This commit is contained in:
134
README.md
Normal file
134
README.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# 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:
|
||||
|
||||
- https://tiny.com.br/api-docs/api2-webhooks
|
||||
- https://tiny.com.br/api-docs/api2-webhooks-tiny
|
||||
- https://tiny.com.br/api-docs/api2-webhooks-atualizacao-situacao-pedido
|
||||
- https://tiny.com.br/api-docs/api2-pedidos-gerar-ordem-producao
|
||||
|
||||
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:
|
||||
|
||||
```sh
|
||||
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:
|
||||
|
||||
```sh
|
||||
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
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npm run sync
|
||||
```
|
||||
|
||||
For a no-write validation run:
|
||||
|
||||
```sh
|
||||
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:
|
||||
|
||||
```sh
|
||||
docker compose run --rm production-orders-sync
|
||||
```
|
||||
|
||||
The included `postgres` service is only a local example. In normal deployment, set `DATABASE_URL` to the existing Graphs Postgres database.
|
||||
|
||||
## 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`
|
||||
Reference in New Issue
Block a user