import { describe, expect, it } from 'vitest'; import { partnerSplit, partnerTerms, withinDailyLimits } from './partners.ts'; import { isProviderFreeWithGap } from './slots.ts'; import { fromYerevan } from './time.ts'; describe('партнёры услуг: деньги', () => { it('как Uber: наш % с оплаченного, остальное партнёру', () => { const r = partnerSplit(20_000, { deal: 'COMMISSION', commissionPct: 30, profitSharePct: 0, fotPct: 0, taxPct: 0 }); expect(r).toMatchObject({ partner: 14_000, margin: 6_000, fot: 0, tax: 0, pct: 30 }); }); it('расходы наши: % партнёра от (оплачено − ФОТ − налог)', () => { const r = partnerSplit(20_000, { deal: 'PROFIT_SHARE', commissionPct: 0, profitSharePct: 30, fotPct: 60, taxPct: 5 }); // ФОТ 12 000, налог 1 000, прибыль 7 000 → партнёру 2 100 expect(r).toMatchObject({ fot: 12_000, tax: 1_000, partner: 2_100, cost: 14_100, margin: 5_900 }); }); it('ФОТ и налог по умолчанию из настроек, свои — у партнёра', () => { const base = { deal: 'PROFIT_SHARE' as const, commissionPct: 0, profitSharePct: 50 }; expect(partnerTerms({ ...base, fotPct: null, taxPct: null }, { fotPct: 50, taxPct: 10 })).toMatchObject({ fotPct: 50, taxPct: 10 }); expect(partnerTerms({ ...base, fotPct: 40, taxPct: 0 }, { fotPct: 50, taxPct: 10 })).toMatchObject({ fotPct: 40, taxPct: 0 }); }); }); describe('партнёры услуг: нагрузка сотрудника', () => { it('лимит заказов и часов в день', () => { const day = [{ durationMinutes: 180 }, { durationMinutes: 120 }]; expect(withinDailyLimits({ maxOrdersPerDay: 3, maxHoursPerDay: null }, day, 60)).toBe(true); expect(withinDailyLimits({ maxOrdersPerDay: 2, maxHoursPerDay: null }, day, 60)).toBe(false); expect(withinDailyLimits({ maxOrdersPerDay: null, maxHoursPerDay: 6 }, day, 60)).toBe(true); expect(withinDailyLimits({ maxOrdersPerDay: null, maxHoursPerDay: 6 }, day, 61)).toBe(false); }); it('между заказами остаётся время на дорогу', () => { // суббота 24.10.2026, график 9:00–21:00 (0 — понедельник), занят 10:00–12:00 const p = { id: 'p', schedule: [{ weekday: 5, startMinute: 9 * 60, endMinute: 21 * 60 }], busy: [{ start: fromYerevan(2026, 10, 24, 600), end: fromYerevan(2026, 10, 24, 720) }] }; const at = (m: number, len = 120) => ({ start: fromYerevan(2026, 10, 24, m), end: fromYerevan(2026, 10, 24, m + len) }); expect(isProviderFreeWithGap(p, at(12 * 60), 0)).toBe(true); expect(isProviderFreeWithGap(p, at(12 * 60 + 30), 60)).toBe(false); expect(isProviderFreeWithGap(p, at(13 * 60), 60)).toBe(true); expect(isProviderFreeWithGap(p, at(7 * 60 + 30, 90), 60)).toBe(false); }); });