Remove user-app v1 and ship pending order, auth, and app fixes.

Drop the duplicate customer app from the monorepo and keep user-app-v2 as the only app; include order pricing, admin cart, OTP/SMS, and dashboard cache updates.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
mahmood
2026-10-28 16:22:32 +03:30
co-authored by Cursor
parent bc881a0a8a
commit 520454481b
367 changed files with 335 additions and 28536 deletions
@@ -109,7 +109,7 @@ describe('OrdersService.createOrder — order ban', () => {
/** MON-08 (owner ruling 2026-10-08): an app order is priced by the server, never by the app. */
describe('OrdersService.createOrder — server-side making charge', () => {
function build() {
function build(rialWage?: number | string | null) {
const created: Record<string, any>[] = [];
const tx = {
order: {
@@ -124,7 +124,13 @@ describe('OrdersService.createOrder — server-side making charge', () => {
customer: { findUnique: jest.fn(async () => ({ id: 'c1', canPlaceOrders: true, referredByAgentId: null, goldDebtGrams: new Prisma.Decimal(0), goldCreditLimitGrams: new Prisma.Decimal(1000) })) },
$transaction: jest.fn(async (fn: (t: typeof tx) => unknown) => fn(tx)),
};
const catalog = { getVariantOrThrow: jest.fn(async () => ({ id: 'v1', rashWeightGrams: '10' })) };
const catalog = {
getVariantOrThrow: jest.fn(async () => ({
id: 'v1',
rashWeightGrams: '10',
...(rialWage !== undefined ? { rialWage } : {}),
})),
};
const service = new OrdersService(prisma as never, catalog as never, {} as never, { orderStatusReached: jest.fn() } as never, wageStub as never, smsStub as never);
return { service, created };
}
@@ -145,6 +151,35 @@ describe('OrdersService.createOrder — server-side making charge', () => {
expect(created[0].flatRialCharge.toString()).toBe('999');
});
it('charges the variant rial wage on an app order and ignores a tampered body', async () => {
const warn = jest.spyOn(Logger.prototype, 'warn').mockImplementation(() => undefined);
try {
const { service, created } = build(500000);
await service.createOrder(tampered, 'c1', { placedBy: 'customer' });
expect(created[0].items.create[0].flatRialCharge.toString()).toBe('500000');
expect(created[0].flatRialCharge.toString()).toBe('500000');
const lines = warn.mock.calls.map((c) => String(c[0])).join('\n');
expect(lines).toContain('flatRialCharge sent=999 used=500000');
} finally {
warn.mockRestore();
}
});
it('keeps an operator rial figure when the variant has its own wage', async () => {
const { service, created } = build(500000);
await service.createOrder(tampered, 'c1', { placedBy: 'operator' });
expect(created[0].items.create[0].flatRialCharge.toString()).toBe('999');
expect(created[0].flatRialCharge.toString()).toBe('999');
});
it('copies the variant rial wage when an operator omits the field', async () => {
const omitted = { items: [{ productVariantId: 'v1', quantity: 2, wagePercent: 10 }] } as never;
const { service, created } = build(500000);
await service.createOrder(omitted, 'c1', { placedBy: 'operator' });
expect(created[0].items.create[0].flatRialCharge.toString()).toBe('500000');
expect(created[0].flatRialCharge.toString()).toBe('1000000');
});
/**
* MON-08 step 3 (owner ruling): «تست مقایسه‌ای که نشان دهد نتیجه برای کاربر
* عادی یکسان است». An unmodified app sends what the server derives