Bariki cashback, order edit, and current work in progress

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
zabudko-ruandClaude Opus 5.5 committed 2026-10-25 22:57:03 +04:00
1 parent dda917fc71
commit 45477fb018
94 files changed
+10955 -650

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
import { afterAll, beforeEach, describe, expect, it } from 'vitest';
import { createOrder, editOrderComposition, giveConsent, markPaid, previewOrderEdit, staffChecklistItem, startClient, transitionOrder } from '../src/index.ts';
import { db, fixtures, makeCtx, NOW, slotAt, tgUser } from './helpers.ts';
let fx: Awaited<ReturnType<typeof fixtures>>;
beforeEach(async () => {
fx = await fixtures();
});
afterAll(() => db.$disconnect());
async function order(ctx: ReturnType<typeof makeCtx>['ctx'], tg: number) {
const s = await startClient(ctx, tgUser(tg));
await giveConsent(ctx, s.clientId);
return createOrder(ctx, { clientId: s.clientId, serviceCode: 'cleaning', params: { cleaningType: 'regular', rooms: 2, bathrooms: 1 }, scheduledAt: slotAt(2, 11), addressText: 'Pushkin 10', districtId: fx.kentron.id });
}
const admin = () => db.adminUser.create({ data: { name: 'Op', roles: ['SUPPORT'] } });
describe('команда меняет заказ', () => {
it('состав: пересчёт цены и длительности, история, уведомление по галочке', async () => {
const { ctx, events } = makeCtx(NOW);
const o = await order(ctx, 1501);
const op = await admin();
const p = await previewOrderEdit(ctx, { orderId: o.id, params: { cleaningType: 'regular', rooms: 3, bathrooms: 1, extras: { windows: 2 } } });
expect(p.totalPrice).toBeGreaterThan(o.totalPrice);
expect(p.durationMinutes).toBeGreaterThan(o.durationMinutes);
await editOrderComposition(ctx, { orderId: o.id, adminId: op.id, params: p.params, notify: true });
const after = await db.order.findUniqueOrThrow({ where: { id: o.id } });
expect(after).toMatchObject({ totalPrice: p.totalPrice, durationMinutes: p.durationMinutes });
expect((after.params as Record<string, unknown>).rooms).toBe(3);
expect(events).toContainEqual({ type: 'order.edited', orderId: o.id });
expect(await db.orderStatusHistory.count({ where: { orderId: o.id, event: 'editedByStaff' } })).toBe(1);
});
it('своя цена — только с причиной', async () => {
const { ctx } = makeCtx(NOW);
const o = await order(ctx, 1502);
const op = await admin();
await expect(editOrderComposition(ctx, { orderId: o.id, adminId: op.id, params: { cleaningType: 'regular', rooms: 2, bathrooms: 1 }, totalPrice: 12_345 })).rejects.toMatchObject({ code: 'REASON_REQUIRED' });
await editOrderComposition(ctx, { orderId: o.id, adminId: op.id, params: { cleaningType: 'regular', rooms: 2, bathrooms: 1 }, totalPrice: 12_345, reason: 'договорились' });
expect((await db.order.findUniqueOrThrow({ where: { id: o.id } })).totalPrice).toBe(12_345);
});
it('после оплаты: кэшбек пересчитывается по новой цене', async () => {
const { ctx } = makeCtx(NOW);
const o = await order(ctx, 1503);
const op = await admin();
await db.order.update({ where: { id: o.id }, data: { status: 'CLEANING', providerId: fx.maria.id } });
await db.orderChecklistItem.updateMany({ where: { orderId: o.id }, data: { checkedAt: NOW, requiresPhoto: false } });
await transitionOrder(ctx, { orderId: o.id, to: 'DONE', actor: 'ADMIN' });
await markPaid(ctx, { orderId: o.id, by: { type: 'ADMIN', id: op.id } });
const before = await db.pointsAccrual.findFirstOrThrow({ where: { orderId: o.id, status: { not: 'REVERSED' } } });
await editOrderComposition(ctx, { orderId: o.id, adminId: op.id, params: { cleaningType: 'regular', rooms: 4, bathrooms: 2 } });
const now = await db.pointsAccrual.findFirstOrThrow({ where: { orderId: o.id, status: { not: 'REVERSED' } } });
expect(now.id).not.toBe(before.id);
expect(now.amount).toBeGreaterThan(before.amount);
});
it('чек-лист за клинера: пункт с фото — без фото только с причиной; потом заказ можно закрыть', async () => {
const { ctx } = makeCtx(NOW);
const o = await order(ctx, 1504);
const op = await admin();
await db.order.update({ where: { id: o.id }, data: { status: 'CLEANING', providerId: fx.maria.id } });
const items = await db.orderChecklistItem.findMany({ where: { orderId: o.id } });
const photo = items.find((i) => i.requiresPhoto)!;
await expect(staffChecklistItem(ctx, { orderId: o.id, adminId: op.id, code: photo.code, checked: true })).rejects.toMatchObject({ code: 'PHOTO_REQUIRED' });
for (const i of items) await staffChecklistItem(ctx, { orderId: o.id, adminId: op.id, code: i.code, checked: true, reason: i.requiresPhoto ? 'фото в чате' : null });
await transitionOrder(ctx, { orderId: o.id, to: 'DONE', actor: 'ADMIN' });
expect((await db.order.findUniqueOrThrow({ where: { id: o.id } })).status).toBe('DONE');
});
});