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:
s.esmaeili
2026-10-06 16:48:36 +03:30
co-authored by Claude Opus 4.6
parent a72fa607a5
commit 19d490e790
179 changed files with 12538 additions and 370 deletions
@@ -3,6 +3,7 @@ import { OrderChannel, OrderStatus, Prisma } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import { CatalogService } from '../catalog/catalog.service';
import { LedgerService } from '../accounting/ledger.service';
import { AccountingHooksService } from '../accounting-sync/accounting-hooks.service';
import { CreateOrderDto } from './dto/create-order.dto';
interface ComputedItem {
@@ -23,6 +24,7 @@ export class OrdersService {
private readonly prisma: PrismaService,
private readonly catalog: CatalogService,
private readonly ledger: LedgerService,
private readonly accounting: AccountingHooksService,
) {}
/**
@@ -121,6 +123,9 @@ export class OrdersService {
include: { items: true, statusEvents: true },
});
// D27 — the document rule may say «at order placement».
await this.accounting.orderStatusReached(tx, order.id, initialStatus);
return order;
});
}
@@ -151,6 +156,14 @@ export class OrdersService {
await this.ledger.postOrderDebt(orderId, tx);
}
// D27 — the accounting document (پیش‌فاکتور) follows the owner's rule on
// which status posts it, queued in this same transaction.
if (newStatus === OrderStatus.CANCELLED && order.status !== OrderStatus.CANCELLED) {
await this.accounting.orderCancelled(tx, orderId);
} else if (newStatus !== order.status) {
await this.accounting.orderStatusReached(tx, orderId, newStatus);
}
return updated;
});
}