Files
portal/app/database/seeders/PricingTierSeeder.php
T
Дмитрий a907fea031 feat(db): Plan 4 Task 1 — schema delta v8.18 → v8.19 + models/seeder
Schema delta (4 правки в db/schema.sql):
- tenants + delivered_in_month INT NOT NULL DEFAULT 0 CHECK (>=0) — month
  counter для PricingTierResolver O(1) lookup на горячем пути.
- lead_charges + charge_source VARCHAR(8) DEFAULT 'rub' CHECK IN ('prepaid','rub')
  + ALTER ADD CONSTRAINT chk_lead_charges_prepaid_zero_price (prepaid → price=0).
- supplier_leads + recovered_from_csv_at TIMESTAMPTZ + partial index
  WHERE recovered_from_csv_at IS NOT NULL.
- Новая таблица supplier_csv_reconcile_log (SaaS-level, без RLS) + 2 индекса
  (started_at DESC, partial status WHERE status IN ('drift_alert','failed')).

Метрики: 61 → 62 базовых таблиц / 114 → 117 индексов / 39 RLS (без изменений).

Сопутствующие правки:
- db/CHANGELOG_schema.md — v8.19 entry (18 записей).
- db/02_grants.sql — GRANT SELECT,INSERT,UPDATE on supplier_csv_reconcile_log
  + GRANT USAGE,SELECT on sequence для crm_supplier_worker.
- app/Models/Tenant — fillable+casts: delivered_in_month integer.
- app/Models/LeadCharge — fillable+casts: charge_source string.
- app/Models/SupplierLead — fillable+casts: recovered_from_csv_at datetime.
- LeadChargeFactory — defaults charge_source='rub' + prepaid() state.
- PricingTierSeeder (новый) — 7 ступеней дефолтного тарифа (placeholder,
  Plan 4 Открытый вопрос #1: 100/200/400/800/1500/3000/∞ leads at
  50000/45000/40000/35000/30000/27000/25000 копеек).
- DatabaseSeeder — call PricingTierSeeder; убран broken Laravel scaffold
  User::factory(['name' => ...]) (наша схема first_name/last_name).

Tests (Plan 4 surface):
- SchemaDeltaTest.php — 5 it() блоков: delivered_in_month CHECK, charge_source
  CHECK на prepaid+zero-price, recovered_from_csv_at колонка, reconcile_log
  таблица+status CHECK, migrate:fresh idempotency (skip in parallel).
- pest --filter=SchemaDeltaTest: 5/5 PASS (9 assertions, 2076 ms).
- pest --filter='Tenant|LeadCharge|SupplierLead|Plan4': 111/111 PASS.

CI gates:
- composer pint: passed.
- composer stan: passed (0 errors above baseline — @phpstan-ignore-next-line
  на $this->markTestSkipped в Pest closure rebound context).

Verify:
- migrate:fresh --seed на DB_DATABASE=liderra_testing: 0 errors, 754 ms.
- PricingTier::count() = 7.

Концерны (НЕ блокируют Task 1):
- pest --parallel: 617/622 PASS + 4 skipped + 1 flaky FAIL — flaky test
  колеблется между ProjectExtensionsTest::supplierB1_B2_B3_relations
  (SupplierProjectFactory race: closure пикает signal_type=sms до override
  platform=B1 → CHECK chk_supplier_projects_b1_not_for_sms violation)
  и NewLeadNotificationTest::webhook_дубль_Биз_19 (известный microsecond
  precision quirk в anti-spam exclusion, memory feedback_environment.md).
  Оба теста существуют на main (HEAD 0802f7c = plan4-billing branch HEAD
  до этого commit'а), Plan 4 их не трогает. Фиксы — вне Task 1 scope.

Spec: docs/superpowers/specs/2026-05-11-plan4-billing-csv-admin-design.md §2.
Plan: docs/superpowers/plans/2026-05-11-plan4-billing-csv-admin-plan.md Task 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:38:38 +03:00

36 lines
1.3 KiB
PHP

<?php
declare(strict_types=1);
namespace Database\Seeders;
use App\Models\PricingTier;
use Illuminate\Database\Seeder;
class PricingTierSeeder extends Seeder
{
/**
* 7 ступеней дефолтного тарифа (Plan 4 spec §2.3 — placeholder, ожидает
* подтверждения заказчика. Открытый вопрос #1).
*/
public function run(): void
{
$tiers = [
['tier_no' => 1, 'leads_in_tier' => 100, 'price_per_lead_kopecks' => 50000],
['tier_no' => 2, 'leads_in_tier' => 200, 'price_per_lead_kopecks' => 45000],
['tier_no' => 3, 'leads_in_tier' => 400, 'price_per_lead_kopecks' => 40000],
['tier_no' => 4, 'leads_in_tier' => 800, 'price_per_lead_kopecks' => 35000],
['tier_no' => 5, 'leads_in_tier' => 1500, 'price_per_lead_kopecks' => 30000],
['tier_no' => 6, 'leads_in_tier' => 3000, 'price_per_lead_kopecks' => 27000],
['tier_no' => 7, 'leads_in_tier' => null, 'price_per_lead_kopecks' => 25000],
];
foreach ($tiers as $tier) {
PricingTier::updateOrCreate(
['tier_no' => $tier['tier_no'], 'effective_from' => '1970-01-01'],
array_merge($tier, ['is_active' => true, 'effective_from' => '1970-01-01']),
);
}
}
}