feat: accounting integration (D27), SMS relay driver (D26), rebuild user apps
- Add Tahesab/Zarrin accounting sync module with admin panel UI - Add SMS relay driver as default provider, refactor SMS into driver pattern - Update customer services, orders, products, and catalog queries - Rebuild user-app and user-app-v2 dist assets - Add Prisma migration for accounting tables - Update docker-compose and env examples - Update parity docs (DECISIONS, ISSUES, PROGRESS) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
a72fa607a5
commit
19d490e790
@@ -1361,3 +1361,60 @@ against the real `jsonwebtoken` and asserts the reader can no longer produce it.
|
||||
**Standing rule from here on:** read an environment variable with `envOr` /
|
||||
`envOrNull`, never with `??`, unless an empty string is a meaningful value for
|
||||
that specific variable.
|
||||
|
||||
## D26 — SMS through the owner's relay gateway, drivers per provider, 2026-10-05
|
||||
|
||||
The owner supplied the PHP client of their own SMS gateway — a relay at
|
||||
`http://185.231.112.101/api/createsms` that handles SMS for several panels and
|
||||
apps. Owner rulings:
|
||||
|
||||
| Topic | Ruling |
|
||||
|---|---|
|
||||
| Default | The relay **replaces Trez as the default**. Trez is **kept**, selectable with `SMS_PROVIDER=trez`; more providers may follow. |
|
||||
| Secret | The `secret_key` **lives in source** as the default. The gateway has a second verification factor held elsewhere, so the owner does not treat it as sensitive. `SMS_RELAY_SECRET` overrides it. (Contrast SEC-12, where the Trez credentials stay env-only.) |
|
||||
| Platform | **Every** message goes through Kavenegar (`platform: "kave"`). |
|
||||
| Host | The server's own IP is correct, as in the PHP client; `SMS_RELAY_HOST` overrides it. |
|
||||
| Address | The IP over HTTP is correct. HTTPS on that IP presents an expired `*.montpar.ir` certificate (checked 2026-10-05), so HTTPS is not usable on the IP; `SMS_RELAY_URL` can switch later. |
|
||||
| Replies | Undocumented; left to the implementation. A 2xx counts as sent unless the JSON body says `status: false`, `success: false`, `ok: false`, `status: "error"`/`"failed"` or carries `error`. |
|
||||
|
||||
Implementation: `apps/api/src/modules/sms/drivers/` — one `SmsDriver` per
|
||||
provider (`relay.driver.ts`, `trez.driver.ts`) registered in `drivers/index.ts`.
|
||||
A new provider is one file plus one registry line. `SmsService` keeps the
|
||||
cross-provider rules: trim, persist every message (SEC-11 for OTP unchanged),
|
||||
log failures and never throw (D20). Plan: `docs/SMS-RELAY-PLAN.md`.
|
||||
|
||||
Consequence: with no SMS variables set, **every environment now sends real
|
||||
SMS**, local development included. Set `SMS_PROVIDER=none` on a machine that
|
||||
must not send.
|
||||
|
||||
## D27 — accounting integration (Tahesab / Zarrin), switchable from the admin panel, 2026-10-05
|
||||
|
||||
The owner wants the app connected to the shop's accounting software, and the
|
||||
software differs per installation: **Tahesab** or **Zarrin**, picked with one
|
||||
switch in the admin panel (`none | tahesab | zarrin`). Plan and the full API
|
||||
survey: `docs/ACCOUNTING-INTEGRATION-PLAN.md`. Owner rulings:
|
||||
|
||||
| Topic | Ruling |
|
||||
|---|---|
|
||||
| Scope | Customer sync, balance sync, order documents, and ready-made items (کارساخته) ↔ products. **Everything else stays in the app** — repayment documents (melted gold, cash, cheque) are not sent. |
|
||||
| Balance | The accounting balance is **shown to the customer**; the **credit-limit check stays on the internal ledger**. |
|
||||
| Report | «گزارش» reads from the accounting software when the integration is on, so it agrees with the balance shown. Supersedes D24 for that case only; D24 remains the fallback. |
|
||||
| Customers | Created in the accounting software when an admin marks them **VERIFIED**; existing ones are linked by **mobile**. Accounts that exist only in the accounting software are **created in the app**, keyed by mobile (national code falls back to the mobile, as elsewhere). |
|
||||
| Documents | Sent at the debt-recognition point (`DELIVERED_TO_AGENT`) as **temporary** (موقت) documents for the accountant to finalise. Ready-made items only for now; gold/coin trading later. |
|
||||
| Products | One accounting کارساخته = one app **Product**. Title, code and stock belong to the accounting software; images, description, variants, category and **making charge belong to the app**. |
|
||||
| Visibility | **A product with no image is never shown in the app** — all products, automatically. |
|
||||
| Stock | Accounting stock is shown in the admin panel; at zero the product is unavailable in the app (pre-order variants excepted). |
|
||||
|
||||
Security carried over from the reference's mistakes (SEC-06/16/17): TLS
|
||||
verification is never disabled (a custom CA can be supplied instead), the
|
||||
token is write-only and encrypted at rest with `ACCOUNTING_SECRET_KEY`, the
|
||||
generic `const` settings routes refuse `accounting.*` keys, and statement
|
||||
details are scoped to the token's own customer.
|
||||
|
||||
Later rulings, same day (plan §11–§12, ACC-08/09): sync is real-time (change
|
||||
feed polled every 5 s, sends right after commit, order-time live stock that
|
||||
falls back to the last known figure); sync buttons on the wholesale-customer
|
||||
and product lists and a live balance on the app profile, all working whenever
|
||||
a provider is connected regardless of the feature switches; and the order
|
||||
status that posts the temporary document (پیشفاکتور) is an admin-chosen rule
|
||||
(`documents.triggerStatus`, default «تحویل مسئول فروش»).
|
||||
|
||||
Reference in New Issue
Block a user