مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت. تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
40 lines
1.5 KiB
Markdown
40 lines
1.5 KiB
Markdown
# `@agy/billing-worker`
|
|
|
|
Rolls the **usage ledger** into draft invoices — one invoice per organization
|
|
per billing period.
|
|
|
|
```bash
|
|
pnpm --filter @agy/billing-worker run # one pass, then exit (cron shape)
|
|
pnpm --filter @agy/billing-worker dev # long-running loop
|
|
```
|
|
|
|
## What it does
|
|
|
|
For every organization, for the requested period:
|
|
|
|
1. Skip the organization if the period already has an invoice (**idempotent** —
|
|
a cron job that fires twice must not bill twice).
|
|
2. Otherwise sum the usage ledger for the period.
|
|
3. Skip if the total is zero — no `$0` drafts littering the table.
|
|
4. Otherwise create a `draft` invoice carrying the ledger's total.
|
|
|
|
## What it deliberately does **not** do
|
|
|
|
- **It never invents an amount.** The ledger is the source of truth; the worker
|
|
only snapshots it. See `docs/architecture/overview.md` §8.
|
|
- **It never moves money.** Charging a card is `BillingService.settle_payment`,
|
|
behind an authenticated API call — not a background job.
|
|
|
|
## Implementation
|
|
|
|
`apps/api/app/workers/billing.py`. The service wiring comes from
|
|
`apps/api/app/application/container.py`, so this worker and the API compose
|
|
services identically. Tests: `apps/api/tests/test_workers.py`.
|
|
|
|
## Extracting this into its own process
|
|
|
|
The worker already opens its own database sessions and holds no request state,
|
|
so extraction is a deployment change, not a code change: run
|
|
`python -m app.workers billing --loop` as its own process with the same
|
|
`DATABASE_URL`, and point the cron/loop interval at your billing cycle.
|