Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
71 lines
4.7 KiB
TypeScript
71 lines
4.7 KiB
TypeScript
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');
|
|
});
|
|
});
|