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

+43
View File
@@ -0,0 +1,43 @@
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);
});
});