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 { TableService } from '../admin-table/table.service';
import type { TableQueryDto } from '../admin-table/table-query.dto';
import { AccountingHooksService } from '../accounting-sync/accounting-hooks.service';
import { OrdersService } from '../orders/orders.service';
import type { UpdateOrderStatusDto } from './update-status.dto';
@@ -40,6 +41,7 @@ export class AdminOrdersService {
private readonly prisma: PrismaService,
private readonly table: TableService,
private readonly orders: OrdersService,
private readonly accounting: AccountingHooksService,
) {}
/** Fields the XTable filter bar and sorter may reference. */
@@ -220,11 +222,14 @@ export class AdminOrdersService {
);
}
await this.prisma.$transaction([
this.prisma.orderStatusEvent.deleteMany({ where: { orderId: id } }),
this.prisma.orderItem.deleteMany({ where: { orderId: id } }),
this.prisma.order.delete({ where: { id } }),
]);
await this.prisma.$transaction(async (tx) => {
// D27 — a temporary document may already be in the books (the rule can
// post it at placement); withdraw it like a cancellation.
await this.accounting.orderCancelled(tx, id);
await tx.orderStatusEvent.deleteMany({ where: { orderId: id } });
await tx.orderItem.deleteMany({ where: { orderId: id } });
await tx.order.delete({ where: { id } });
});
return { success: true };
}
@@ -405,9 +410,12 @@ export class AdminOrdersService {
where: { orderId },
orderBy: { createdAt: 'desc' },
});
return this.prisma.order.update({
where: { id: orderId },
data: { status: latest?.toStatus ?? OrderStatus.PLACED },
const rolledBackTo = latest?.toStatus ?? OrderStatus.PLACED;
return this.prisma.$transaction(async (tx) => {
const updated = await tx.order.update({ where: { id: orderId }, data: { status: rolledBackTo } });
// D27 — rolled back below the document rule: the پیش‌فاکتور is withdrawn.
await this.accounting.orderStatusReached(tx, orderId, rolledBackTo);
return updated;
});
}
}