016416502a
- результат «В корзину»: одна причина, дата созвона стирается, платящего не выбросить; - реклама на корзине встаёт СРАЗУ — явной веткой, а не случайно через unknown_stage; - у «Выслано КП» дата созвона стала необязательной: бывает «пришлите на почту, если интересно — перезвоню». Врущий старый тест на 422 без даты поправлен; - фильтр доски date_mode=todo|changed + период (добавлен вид «завтра»). «Надо сделать» — созвон в периоде ИЛИ просрочен, кроме отказа и корзины. «Менялось» — есть движение стадии ИЛИ запись разговора за период. Прогон: 1245/1245 (отдел продаж + все юнит-тесты).
163 lines
6.8 KiB
PHP
163 lines
6.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Services\Sales\SalesPeriodRange;
|
|
use App\Services\Sales\SalesPeriodResolver;
|
|
use Carbon\CarbonImmutable;
|
|
|
|
// Заморозка времени: 15 июня 2026, 12:00 МСК
|
|
beforeEach(function (): void {
|
|
CarbonImmutable::setTestNow(
|
|
CarbonImmutable::parse('2026-06-15 12:00:00', 'Europe/Moscow'),
|
|
);
|
|
});
|
|
|
|
afterEach(function (): void {
|
|
CarbonImmutable::setTestNow();
|
|
});
|
|
|
|
// ─── kind=this ────────────────────────────────────────────────────────────────
|
|
|
|
it('kind=this returns current month range (June 2026)', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
$range = $resolver->resolve(['kind' => 'this']);
|
|
|
|
expect($range)->toBeInstanceOf(SalesPeriodRange::class);
|
|
expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-01 00:00:00');
|
|
expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-30 23:59:59');
|
|
});
|
|
|
|
// ─── kind=prev ────────────────────────────────────────────────────────────────
|
|
|
|
it('kind=prev returns previous month range (May 2026)', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
$range = $resolver->resolve(['kind' => 'prev']);
|
|
|
|
expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-05-01 00:00:00');
|
|
expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-05-31 23:59:59');
|
|
});
|
|
|
|
// ─── kind=prev2 ───────────────────────────────────────────────────────────────
|
|
|
|
it('kind=prev2 returns month-before-previous range (April 2026)', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
$range = $resolver->resolve(['kind' => 'prev2']);
|
|
|
|
expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-04-01 00:00:00');
|
|
expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-04-30 23:59:59');
|
|
});
|
|
|
|
// ─── kind=custom ──────────────────────────────────────────────────────────────
|
|
|
|
it('kind=custom returns exact from/to bounds', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
$range = $resolver->resolve([
|
|
'kind' => 'custom',
|
|
'from' => '2026-03-10',
|
|
'to' => '2026-05-20',
|
|
]);
|
|
|
|
expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-03-10 00:00:00');
|
|
expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-05-20 23:59:59');
|
|
});
|
|
|
|
it('kind=custom: monthsIn returns all three month-starts for Mar-May span', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
$range = $resolver->resolve([
|
|
'kind' => 'custom',
|
|
'from' => '2026-03-10',
|
|
'to' => '2026-05-20',
|
|
]);
|
|
|
|
$months = $resolver->monthsIn($range);
|
|
|
|
expect($months)->toHaveCount(3);
|
|
expect($months[0]->format('Y-m-d'))->toBe('2026-03-01');
|
|
expect($months[1]->format('Y-m-d'))->toBe('2026-04-01');
|
|
expect($months[2]->format('Y-m-d'))->toBe('2026-05-01');
|
|
});
|
|
|
|
it('kind=custom: throws InvalidArgumentException when from > to', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
expect(fn () => $resolver->resolve([
|
|
'kind' => 'custom',
|
|
'from' => '2026-05-20',
|
|
'to' => '2026-03-10',
|
|
]))->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('kind=custom: throws InvalidArgumentException when from is missing', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
expect(fn () => $resolver->resolve([
|
|
'kind' => 'custom',
|
|
'to' => '2026-05-20',
|
|
]))->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
it('kind=custom: throws InvalidArgumentException when to is missing', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
expect(fn () => $resolver->resolve([
|
|
'kind' => 'custom',
|
|
'from' => '2026-03-10',
|
|
]))->toThrow(InvalidArgumentException::class);
|
|
});
|
|
|
|
// ─── monthsIn ─────────────────────────────────────────────────────────────────
|
|
|
|
it('monthsIn for single-month "this" range returns one entry (June 2026)', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
$range = $resolver->resolve(['kind' => 'this']);
|
|
|
|
$months = $resolver->monthsIn($range);
|
|
|
|
expect($months)->toHaveCount(1);
|
|
expect($months[0]->format('Y-m-d'))->toBe('2026-06-01');
|
|
});
|
|
|
|
// ─── timezone ─────────────────────────────────────────────────────────────────
|
|
|
|
it('range start carries Europe/Moscow timezone', function (): void {
|
|
$resolver = new SalesPeriodResolver;
|
|
$range = $resolver->resolve(['kind' => 'this']);
|
|
|
|
expect($range->start->timezoneName)->toBe('Europe/Moscow');
|
|
});
|
|
|
|
// ─── новые периоды: сегодня / вчера / 7 дней / 30 дней ────────────────────────
|
|
|
|
it('kind=today — только сегодняшний день', function (): void {
|
|
$range = (new SalesPeriodResolver)->resolve(['kind' => 'today']);
|
|
|
|
expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-15 00:00:00');
|
|
expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-15 23:59:59');
|
|
});
|
|
|
|
it('kind=yesterday — только вчерашний день', function (): void {
|
|
$range = (new SalesPeriodResolver)->resolve(['kind' => 'yesterday']);
|
|
|
|
expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-14 00:00:00');
|
|
expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-14 23:59:59');
|
|
});
|
|
|
|
it('kind=tomorrow — только завтрашний день', function (): void {
|
|
$range = (new SalesPeriodResolver)->resolve(['kind' => 'tomorrow']);
|
|
|
|
expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-16 00:00:00');
|
|
expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-16 23:59:59');
|
|
});
|
|
|
|
it('kind=d7 — последние 7 дней, включая сегодня', function (): void {
|
|
$range = (new SalesPeriodResolver)->resolve(['kind' => 'd7']);
|
|
|
|
expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-09 00:00:00');
|
|
expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-15 23:59:59');
|
|
});
|
|
|
|
it('kind=d30 — последние 30 дней, включая сегодня', function (): void {
|
|
$range = (new SalesPeriodResolver)->resolve(['kind' => 'd30']);
|
|
|
|
expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-05-17 00:00:00');
|
|
expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-15 23:59:59');
|
|
});
|