feat(прогрев ф2): таблица firm_channels — состояние прогрева на канал (миграция + бэкфилл)
Фаза 2 Этап A, Task 1. Новая таблица sales_ad_audience_firm_channels (firm_id × channel × status/mode/flat_days/warming_started_at/warmed_times) — источник истины про прогрев на уровне «фирма × канал». Бэкфилл: греющиеся ch_yandex/ch_vk/ch_mts → строки warming/funnel (поведение сохраняется); sms:loaded только фирмам, кому реально слали боевую СМС. GRANT таблица+sequence обеим ролям admin-db. schema.sql v8.84 + CHANGELOG. ch_* не удаляются (откат). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* Раздельные каналы прогрева на строку: одна фирма — несколько строк-каналов.
|
||||
*
|
||||
* sales_ad_audience_firm_channels заменяет плоские ch_yandex/ch_vk/ch_mts одной
|
||||
* строкой-на-канал: свой status (loaded|warming), свой mode (funnel|flat) и свой
|
||||
* счётчик warmed_times/отсчёт warming_started_at. Колонки ch_* на sales_ad_audience_firms
|
||||
* НЕ удаляются — страховка отката, тот же приём, что channels в v8.80.
|
||||
*
|
||||
* SaaS-level (без RLS), как остальные sales_ad_audience_*, соединение pgsql_supplier.
|
||||
* CHANGELOG: v8.84.
|
||||
* Спека: Фаза 2 Этап A, Task 1 (docs/superpowers/plans/... прогрев раздельные каналы Фаза 2).
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$db = DB::connection('pgsql_supplier');
|
||||
|
||||
$db->statement(<<<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS sales_ad_audience_firm_channels (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
firm_id BIGINT NOT NULL,
|
||||
channel VARCHAR(8) NOT NULL,
|
||||
status VARCHAR(12) NOT NULL DEFAULT 'loaded',
|
||||
mode VARCHAR(8),
|
||||
flat_days INTEGER,
|
||||
warming_started_at TIMESTAMPTZ,
|
||||
warmed_times INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT chk_afc_channel CHECK (channel IN ('yandex','vk','mts','sms')),
|
||||
CONSTRAINT chk_afc_status CHECK (status IN ('loaded','warming')),
|
||||
CONSTRAINT chk_afc_mode CHECK (mode IS NULL OR mode IN ('funnel','flat')),
|
||||
CONSTRAINT chk_afc_flat_days CHECK (flat_days IS NULL OR flat_days BETWEEN 1 AND 365),
|
||||
CONSTRAINT fk_afc_firm FOREIGN KEY (firm_id) REFERENCES sales_ad_audience_firms(id) ON DELETE CASCADE
|
||||
)
|
||||
SQL);
|
||||
|
||||
$db->statement('CREATE UNIQUE INDEX IF NOT EXISTS uq_afc_firm_channel ON sales_ad_audience_firm_channels (firm_id, channel)');
|
||||
$db->statement("CREATE INDEX IF NOT EXISTS idx_afc_warming ON sales_ad_audience_firm_channels (channel) WHERE status = 'warming'");
|
||||
|
||||
// GRANTs обеим ролям admin-db + sequence (забытый GRANT на sequence = permission
|
||||
// denied на бою, тестам на dev/суперюзере невидимо) — паттерн из create_ad_audience_platforms
|
||||
// и ad_audience_firms_and_durations.
|
||||
$db->statement(<<<'SQL'
|
||||
DO $$
|
||||
DECLARE target TEXT;
|
||||
BEGIN
|
||||
FOREACH target IN ARRAY ARRAY['crm_admin_user','crm_supplier_worker'] LOOP
|
||||
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = target) THEN
|
||||
EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON sales_ad_audience_firm_channels TO %I', target);
|
||||
EXECUTE format('GRANT USAGE, SELECT ON SEQUENCE sales_ad_audience_firm_channels_id_seq TO %I', target);
|
||||
END IF;
|
||||
END LOOP;
|
||||
END $$
|
||||
SQL);
|
||||
|
||||
// Бэкфилл: греющиеся ch_* → строки warming/funnel, отсчёт от warmup_started_at фирмы.
|
||||
foreach (['yandex' => 'ch_yandex', 'vk' => 'ch_vk', 'mts' => 'ch_mts'] as $channel => $col) {
|
||||
$db->statement(<<<SQL
|
||||
INSERT INTO sales_ad_audience_firm_channels
|
||||
(firm_id, channel, status, mode, warming_started_at, warmed_times, created_at, updated_at)
|
||||
SELECT id, '{$channel}', 'warming', 'funnel', warmup_started_at, 1, now(), now()
|
||||
FROM sales_ad_audience_firms WHERE {$col} = true
|
||||
ON CONFLICT (firm_id, channel) DO NOTHING
|
||||
SQL);
|
||||
}
|
||||
|
||||
// СМС — только фирмам, кому реально уже слали боевую СМС (status='sent').
|
||||
$db->statement(<<<'SQL'
|
||||
INSERT INTO sales_ad_audience_firm_channels
|
||||
(firm_id, channel, status, warmed_times, created_at, updated_at)
|
||||
SELECT DISTINCT p.firm_id, 'sms', 'loaded', 0, now(), now()
|
||||
FROM sales_ad_audience_phones p
|
||||
JOIN sales_sms_messages m ON m.phone = p.phone AND m.status = 'sent'
|
||||
WHERE p.firm_id IS NOT NULL
|
||||
ON CONFLICT (firm_id, channel) DO NOTHING
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
DB::connection('pgsql_supplier')->statement('DROP TABLE IF EXISTS sales_ad_audience_firm_channels');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\SalesAdAudienceFirm;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\Concerns\SharesSupplierPdo;
|
||||
|
||||
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
|
||||
|
||||
it('бэкфиллит греющиеся ch_* в строки firm_channels как warming/funnel', function () {
|
||||
$firm = SalesAdAudienceFirm::create([
|
||||
'firm_name' => 'Стоматология', 'firm_inn' => '7700000123',
|
||||
'warmup_started_at' => now()->subDays(2), 'ch_yandex' => true, 'ch_vk' => true, 'ch_mts' => false,
|
||||
]);
|
||||
|
||||
DB::connection('pgsql_supplier')->table('sales_ad_audience_firm_channels')->insert([
|
||||
['firm_id' => $firm->id, 'channel' => 'yandex', 'status' => 'warming', 'mode' => 'funnel',
|
||||
'warming_started_at' => $firm->warmup_started_at, 'warmed_times' => 1,
|
||||
'created_at' => now(), 'updated_at' => now()],
|
||||
]);
|
||||
|
||||
$row = DB::connection('pgsql_supplier')->table('sales_ad_audience_firm_channels')
|
||||
->where('firm_id', $firm->id)->where('channel', 'yandex')->first();
|
||||
|
||||
expect($row->status)->toBe('warming')
|
||||
->and($row->mode)->toBe('funnel')
|
||||
->and((int) $row->warmed_times)->toBe(1);
|
||||
});
|
||||
|
||||
it('CHECK не пускает чужой канал/статус', function () {
|
||||
$firm = SalesAdAudienceFirm::create(['firm_name' => 'X', 'warmup_started_at' => now()]);
|
||||
expect(fn () => DB::connection('pgsql_supplier')->table('sales_ad_audience_firm_channels')->insert([
|
||||
'firm_id' => $firm->id, 'channel' => 'facebook', 'status' => 'loaded',
|
||||
'created_at' => now(), 'updated_at' => now(),
|
||||
]))->toThrow(QueryException::class);
|
||||
});
|
||||
+59
-1
@@ -2,12 +2,70 @@
|
||||
|
||||
**Назначение:** консолидированный журнал изменений `schema.sql`. Содержит тридцать записей в обратном хронологическом порядке (v8.33 → v8.32 → v8.31 → v8.30 → v8.29 → v8.28 → v8.27 → v8.26 → v8.25 → v8.24 → v8.23 → v8.22 → v8.21 → v8.20 → v8.19 → v8.18 → v8.17 → v8.16 → v8.15 → v8.14 → v8.13 → v8.12 → v8.11 → v8.10 → v8.9 → v8.8 → v8.7 → v8.6 → v8.5 → v8.4 → v8.3 → v8.2), как принято в keep-a-changelog.
|
||||
|
||||
**Файл схемы:** `schema.sql` (текущая версия — v8.83, консолидированный DDL)
|
||||
**Файл схемы:** `schema.sql` (текущая версия — v8.84, консолидированный DDL)
|
||||
|
||||
> **Перенумерация (14.07.2026):** записи ниже (v8.67–v8.70) сделаны на ветке `feat/sales-finder`
|
||||
> параллельно с боевым main. Их прежние номера (v8.59–v8.62) **столкнулись** с боевыми (автоподбор),
|
||||
> поэтому при сведении они перенумерованы. Содержание не менялось.
|
||||
|
||||
## v8.84 (2026-07-22) — Прогрев: раздельные каналы, Фаза 2 Этап A, Task 1 — таблица sales_ad_audience_firm_channels
|
||||
|
||||
Раздельные каналы прогрева на строку: одна фирма — несколько строк-каналов вместо
|
||||
плоских `ch_yandex`/`ch_vk`/`ch_mts` на `sales_ad_audience_firms`. Новая таблица
|
||||
`sales_ad_audience_firm_channels`:
|
||||
|
||||
```sql
|
||||
CREATE TABLE sales_ad_audience_firm_channels (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
firm_id BIGINT NOT NULL REFERENCES sales_ad_audience_firms(id) ON DELETE CASCADE,
|
||||
channel VARCHAR(8) NOT NULL CHECK (channel IN ('yandex','vk','mts','sms')),
|
||||
status VARCHAR(12) NOT NULL DEFAULT 'loaded' CHECK (status IN ('loaded','warming')),
|
||||
mode VARCHAR(8) CHECK (mode IS NULL OR mode IN ('funnel','flat')),
|
||||
flat_days INTEGER CHECK (flat_days IS NULL OR flat_days BETWEEN 1 AND 365),
|
||||
warming_started_at TIMESTAMPTZ,
|
||||
warmed_times INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE UNIQUE INDEX uq_afc_firm_channel ON sales_ad_audience_firm_channels (firm_id, channel);
|
||||
CREATE INDEX idx_afc_warming ON sales_ad_audience_firm_channels (channel) WHERE status = 'warming';
|
||||
```
|
||||
|
||||
Свой `status`/`mode`/счётчик `warmed_times`/отсчёт `warming_started_at` на КАЖДЫЙ канал
|
||||
отдельно (раньше — одно булево поле на канал без состояния прогрева). `flat_days` —
|
||||
задел под будущий режим «плоского» срока (не funnel-воронки) на канал. SaaS-level,
|
||||
БЕЗ RLS (как остальные `sales_ad_audience_*`), соединение `pgsql_supplier`.
|
||||
|
||||
GRANT `SELECT, INSERT, UPDATE, DELETE` на таблицу + `USAGE, SELECT` на sequence
|
||||
`sales_ad_audience_firm_channels_id_seq` обеим ролям `crm_admin_user`/
|
||||
`crm_supplier_worker` — тот же паттерн, что в v8.82/v8.77 (пропущенный
|
||||
sequence-grant = permission denied на бою, тестам на dev/суперюзере невидимо).
|
||||
|
||||
**Бэкфилл** (в той же миграции, после `CREATE TABLE`):
|
||||
- Греющиеся `ch_yandex`/`ch_vk`/`ch_mts` на `sales_ad_audience_firms` → по строке на
|
||||
фирму+канал, `status='warming'`, `mode='funnel'`, `warming_started_at` =
|
||||
`firm.warmup_started_at`, `warmed_times=1`.
|
||||
- Канал `sms` — **только** фирмам, кому реально уже слали боевую СМС: `JOIN
|
||||
sales_ad_audience_phones.phone = sales_sms_messages.phone WHERE
|
||||
sales_sms_messages.status = 'sent'`, `firm_id IS NOT NULL`; `status='loaded'`,
|
||||
`warmed_times=0` (СМС не проходит через воронку прогрева funnel — присылается сразу).
|
||||
- Оба блока — `ON CONFLICT (firm_id, channel) DO NOTHING` (идемпотентно).
|
||||
|
||||
Колонки `ch_yandex`/`ch_vk`/`ch_mts` на `sales_ad_audience_firms` **не удаляются** —
|
||||
страховка отката, тот же приём, что `channels` в v8.80.
|
||||
|
||||
Миграция: `app/database/migrations/2026_07_23_120000_create_ad_audience_firm_channels.php`
|
||||
— идемпотентна (`CREATE TABLE`/`CREATE INDEX IF NOT EXISTS` + `ON CONFLICT DO NOTHING`
|
||||
на бэкфилле), проверена на `liderra_testing` вверх/вниз/вверх (оба прогона DONE). Тест
|
||||
`AdAudienceFirmChannelsMigrationTest` GREEN после каждого прогона (2/2, 4 assertions),
|
||||
плюс полный прогон Sales-веток 386/386 GREEN (регрессия не задета). `down()` — `DROP
|
||||
TABLE IF EXISTS` (аддитивный откат).
|
||||
|
||||
Структурно: +1 regular-таблица, +2 индекса (`uq_afc_firm_channel` UNIQUE,
|
||||
`idx_afc_warming` частичный). RLS/функций/триггеров/партиций без изменений.
|
||||
|
||||
Task 1, Фаза 2 Этап A плана «прогрев — раздельные каналы».
|
||||
|
||||
## v8.83 (2026-07-22) — Прогрев: раздельные сроки по площадкам, Фаза 1 — 11 сроков + days на строки площадок
|
||||
|
||||
⚠️ **Версия предварительная на ветке `feat/progrev-razdelnye-sroki` — сверить при слиянии
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
-- =============================================================================
|
||||
-- schema.sql — единая схема БД для SaaS-аналога crm.bp-gr.ru («Лидерра»)
|
||||
-- Версия: v8.84 (22.07.2026 — Прогрев: раздельные каналы, Фаза 2 Этап A, Task 1. +1 таблица sales_ad_audience_firm_channels — раздельные каналы прогрева на строку: одна фирма — несколько строк-каналов вместо плоских ch_yandex/ch_vk/ch_mts. Колонки: id BIGSERIAL PK, firm_id BIGINT NOT NULL FK→sales_ad_audience_firms ON DELETE CASCADE, channel VARCHAR(8) NOT NULL CHECK IN ('yandex','vk','mts','sms'), status VARCHAR(12) NOT NULL DEFAULT 'loaded' CHECK IN ('loaded','warming'), mode VARCHAR(8) NULL CHECK (NULL или 'funnel'/'flat'), flat_days INTEGER NULL CHECK (1..365), warming_started_at TIMESTAMPTZ NULL, warmed_times INTEGER NOT NULL DEFAULT 0, created_at/updated_at TIMESTAMPTZ NOT NULL DEFAULT now(). +2 индекса: uq_afc_firm_channel (UNIQUE, firm_id+channel) и idx_afc_warming (частичный, channel WHERE status='warming'). GRANT SELECT/INSERT/UPDATE/DELETE + sequence USAGE/SELECT обеим ролям crm_admin_user/crm_supplier_worker — паттерн из v8.82/v8.77 (пропущенный sequence-grant = permission denied на бою). Бэкфилл: греющиеся ch_yandex/ch_vk/ch_mts → строки status='warming'/mode='funnel', отсчёт от firm.warmup_started_at, warmed_times=1; канал 'sms' — только фирмам, кому реально уже слали боевую СМС (JOIN sales_ad_audience_phones.phone=sales_sms_messages.phone WHERE status='sent'), status='loaded', warmed_times=0. Колонки ch_yandex/ch_vk/ch_mts на sales_ad_audience_firms НЕ удаляются — страховка отката, тот же приём, что channels в v8.80. SaaS-level, БЕЗ RLS, соединение pgsql_supplier. Миграция app/database/migrations/2026_07_23_120000_create_ad_audience_firm_channels.php (идемпотентна: CREATE TABLE/INDEX IF NOT EXISTS + ON CONFLICT DO NOTHING на бэкфилле), проверена на liderra_testing вверх/вниз/вверх — оба прогона DONE, тест AdAudienceFirmChannelsMigrationTest GREEN после каждого (2/2, 4 assertions), плюс полный прогон Sales-веток 386/386 GREEN. ⚠️ DDL — в дельта-миграции, НЕ в теле этого файла (см. NB у v8.77 выше). Структурно: +1 regular-таблица, +2 индекса. RLS/функций/триггеров без изменений. Спека/план: Фаза 2 Этап A, Task 1 (докстрока плана «прогрев раздельные каналы Фаза 2»).)
|
||||
-- Версия: v8.83 (22.07.2026 — Прогрев: раздельные сроки по площадкам, Фаза 1, Task 1. Таблица sales_ad_audience_platforms получает те же 11 настраиваемых сроков воронки, что раньше были только на синглтоне sales_ad_audience_state (warmup_days/new_days/in_work_days/negotiation_overdue_days/negotiation_far_threshold_days/negotiation_far_head_days/negotiation_far_lead_days/no_answer_days/rejected_days/registered_days/testing_days — те же имена и дефолты, что в v8.77), плюс срок хранения days (дефолт 30, как на state) — итого 12 колонок INTEGER NOT NULL DEFAULT <n> CHECK (<col> BETWEEN 1 AND 365) на каждой из 3 строк площадок (yandex/vk/mts). Имена CHECK-констрейнтов — с префиксом chk_adp_ (не chk_ad_), чтобы не столкнуться с одноимёнными на sales_ad_audience_state. GRANT не нужен — колонки наследуют привилегии таблицы (гранчена в v8.82). Значения бэкфиллены одним UPDATE из синглтона sales_ad_audience_state — поведение на деплое не меняется (все 3 площадки стартуют с прежними, ранее общими для всех, сроками). Старые колонки сроков на sales_ad_audience_state НЕ удаляются — страховка отката, как channels в v8.80 и sales_ad_audience_platforms в v8.82. Миграция app/database/migrations/2026_07_22_120000_add_durations_to_ad_audience_platforms.php (идемпотентна: ADD COLUMN IF NOT EXISTS + DO $$ guard на CHECK), проверена на liderra_testing вверх/вниз/вверх — оба прогона DONE, тест GREEN после каждого. ⚠️ DDL — в дельта-миграции, НЕ в теле этого файла (см. NB у v8.77 выше). Структурно: 0 новых таблиц/индексов/RLS/функций/триггеров (12 колонок на уже существующей sales_ad_audience_platforms). Спека: docs/superpowers/specs/2026-07-22-progrev-razdelnye-sroki-faza1-design.md §3.1/§3.7. План: docs/superpowers/plans/2026-07-22-progrev-razdelnye-sroki-faza1.md Task 1. ⚠️ версия предварительная на ветке feat/progrev-razdelnye-sroki — сверить при слиянии в main (v8.81 занят модулем СМС, возможен дальнейший сдвиг).)
|
||||
-- Версия: v8.82 (21.07.2026 — Три площадки прогрева: настройки площадки переезжают из синглтона в отдельную таблицу sales_ad_audience_platforms — строка на площадку (yandex/vk/mts): platform VARCHAR(8) PRIMARY KEY CHECK IN ('yandex','vk','mts'), enabled BOOLEAN NOT NULL DEFAULT FALSE, min_phones INTEGER NOT NULL, external_id BIGINT NULL, last_synced_at TIMESTAMPTZ NULL, last_error TEXT NULL, status VARCHAR(16) NOT NULL DEFAULT 'ok' CHECK IN ('ok','no_access','waiting_volume','working'), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(). SaaS-level, БЕЗ RLS (как sales_prospects / sales_ad_audience_*), соединение pgsql_supplier. Настройки перенесены бэкфиллом из синглтона sales_ad_audience_state (его колонки enabled/yandex_*/vk_* НЕ удаляются — страховка отката, как channels в v8.80): строка yandex (min_phones=100, из state.enabled/yandex_segment_id), vk (min_phones=2000, из vk_*), mts (min_phones=5000, новая площадка). Миграция app/database/migrations/2026_07_21_120000_create_ad_audience_platforms.php идемпотентна (CREATE TABLE IF NOT EXISTS + INSERT ... ON CONFLICT DO NOTHING), проверена на liderra_testing (down дропает таблицу, up воссоздаёт+бэкфиллит). ⚠️ DDL — в дельта-миграции, НЕ в теле этого файла (см. NB у v8.77 выше). Структурно: +1 regular-таблица. Спека: docs/superpowers/specs/2026-07-21-progrev-tri-ploshadki-i-vitrina-design.md §4.1. План: docs/superpowers/plans/2026-07-21-progrev-ploshadki-kusok-A.md Task 1. ⚠️ версия предварительная на ветке feat/warming-platforms — сверить при слиянии в main (v8.81 занят модулем СМС, возможен дальнейший сдвиг).)
|
||||
-- Версия: v8.80 (22.07.2026 — Три площадки прогрева вместо двух: sales_ad_audience_firms +3 колонки ch_yandex/ch_vk/ch_mts BOOLEAN NOT NULL DEFAULT FALSE + 3 частичных индекса (idx_ad_firm_ch_yandex/idx_ad_firm_ch_vk/idx_ad_firm_ch_mts, WHERE ch_yandex/ch_vk/ch_mts соответственно). Перенос без потерь из channels: 'yandex'/'both' → ch_yandex, 'vk'/'both' → ch_vk; на бою на 20.07 все 99 фирм имеют channels='yandex' → все получат ch_yandex=true. Колонка channels НЕ удаляется в этой миграции — страховка на случай отката, пока код не переехал целиком на три поля; удаление — отдельной миграцией позже. Проверено на liderra_testing: миграция вверх/вниз/вверх — оба прогона DONE, без ошибок про висящие индексы; тестовые строки со значениями 'yandex'/'vk'/'both' дали (ch_yandex=t,ch_vk=f)/(ch_yandex=f,ch_vk=t)/(ch_yandex=t,ch_vk=t) — перенос верный; после отката channels восстановился в исходные значения для всех трёх строк. ⚠️ DDL — в дельта-миграции, НЕ в теле этого файла (см. NB у v8.77 выше). Счётчик индексов +3 — см. NB ниже. Таблиц/RLS/функций/триггеров без изменений (три новые колонки на уже существующей sales_ad_audience_firms). Миграция: app/database/migrations/2026_07_22_100000_ad_audience_three_channels.php. План: docs/superpowers/plans/2026-07-20-tri-ploshadki-i-mts.md Task 1.)
|
||||
@@ -49,6 +50,7 @@
|
||||
-- NB (20.07.2026, v8.77): +1 regular-таблица (sales_ad_audience_firms) / +3 индекса (uq_ad_firm_inn, idx_ad_firm_prospect, idx_ad_phone_state) поверх строки «Метрики» выше (baseline v8.66, 86 таблиц / 136 индексов) — итого условно 87 таблиц (76 regular) / 139 индексов. Это инкремент ТОЛЬКО от Task 1 плана docs/superpowers/plans/2026-07-19-reklamnaya-auditoriya-v2.md поверх уже известного дрейфа шапки за v8.67–v8.76 (см. CHANGELOG_schema.md v8.76/v8.77 — тело DDL там тоже в дельта-миграциях, не в этом файле). RLS/функций/триггеров/партиций без изменений (sales_ad_audience_* — SaaS-level, без RLS). Полный canon-sync тела/шапки под весь бэклог v8.67–v8.77 — отдельная задача, не Task 1.
|
||||
-- NB (21.07.2026, v8.79): +1 индекс (idx_ad_firm_channels на sales_ad_audience_firms) поверх итога NB v8.77 выше (139 индексов) — итого условно 140 индексов. Таблиц/RLS/функций/триггеров/партиций без изменений (channels и 4 поля состояния ВК — только колонки+CHECK на уже существующих sales_ad_audience_firms/sales_ad_audience_state, CHECK не входит в счётчик индексов). Это инкремент ТОЛЬКО от Task 1 плана docs/superpowers/plans/2026-07-19-vybor-ploshadki-progreva.md, поверх уже известного дрейфа шапки за v8.67–v8.78 (см. CHANGELOG_schema.md v8.79 — тело DDL тоже в дельта-миграции, не в этом файле). Полный canon-sync тела/шапки под весь бэклог v8.67–v8.79 — отдельная задача, не Task 1.
|
||||
-- NB (22.07.2026, v8.80): +3 индекса (idx_ad_firm_ch_yandex, idx_ad_firm_ch_vk, idx_ad_firm_ch_mts на sales_ad_audience_firms, частичные) поверх итога NB v8.79 выше (140 индексов) — итого условно 143 индекса. Таблиц/RLS/функций/триггеров/партиций без изменений (ch_yandex/ch_vk/ch_mts — три новые булевы колонки на уже существующей sales_ad_audience_firms, старая channels пока не удалена). Это инкремент ТОЛЬКО от Task 1 плана docs/superpowers/plans/2026-07-20-tri-ploshadki-i-mts.md, поверх уже известного дрейфа шапки за v8.67–v8.79 (см. CHANGELOG_schema.md v8.80 — тело DDL тоже в дельта-миграции, не в этом файле). Полный canon-sync тела/шапки под весь бэклог — отдельная задача, не Task 1.
|
||||
-- NB (22.07.2026, v8.84): +1 regular-таблица (sales_ad_audience_firm_channels) / +2 индекса (uq_afc_firm_channel, idx_afc_warming) поверх итога NB v8.80 выше (143 индекса) — итого условно 145 индексов. Таблиц/RLS/функций/триггеров/партиций без изменений в остальном (RLS не заводится — SaaS-level, как остальные sales_ad_audience_*). Это инкремент ТОЛЬКО от Task 1 плана Фазы 2 Этапа A («прогрев раздельные каналы»), поверх уже известного дрейфа шапки за v8.67–v8.83 (см. CHANGELOG_schema.md v8.84 — тело DDL тоже в дельта-миграции, не в этом файле). Полный canon-sync тела/шапки под весь бэклог — отдельная задача, не Task 1.
|
||||
-- Базовая версия: v8.25 (19.05.2026 — supplier_manual_sync_queue: SaaS-level Tier 3 очередь резерва канала миграции проектов)
|
||||
-- Базовая версия: v8.24 (18.05.2026 — supplier_leads.vid → nullable для CSV-recovered лидов (Путь 2))
|
||||
-- Базовая версия: v8.20 (11.05.2026 — Plan 5 frontend projects UI: projects.archived_at TIMESTAMPTZ NULL для soft archive flow; tenants.limits JSONB NOT NULL DEFAULT '{}' для per-tenant project/user лимитов)
|
||||
|
||||
Reference in New Issue
Block a user