Files
autocrm/app/Filament/Tenant/Resources/PricingCoefficientResource.php
T
Vasyka c90c35d930 Stage 8 — Smart Pricing Engine: contextual coefficients
Contextual multipliers layered on top of base MarkupRule pricing, applied
per work-order line based on vehicle, client and urgency.

Schema:
- pricing_coefficients (multiplier, conditions JSON, priority, stackable)
- vehicles.vehicle_class (sedan/suv/commercial/hybrid/ev/premium)
- clients.is_vip
- work_orders.urgency (normal/urgent/express)

PricingEngine::quote(Part, Vehicle?, Client?, urgency):
- base = MarkupRule on buy_price (fallback sell_price or buy×1.30)
- context: class (explicit or inferred hybrid/ev from fuel), age, vip, urgency
- stackable coefficients all multiply; non-stackable take only the highest
- returns {base, final, applied[]} breakdown

PricingCoefficient::matches(ctx) — classes/age range/vip/urgency conditions
(empty = always applies).

Filament:
- PricingCoefficientResource with condition builder (classes, age, vip, urgency)
- vehicle_class select, client is_vip toggle, WO urgency select
- "Preț inteligent" action on WO parts shows breakdown + applies sell_price

Tests (6 new):
- base-only without coefficients; age coefficient gating; VIP; express urgency;
  stackable multiply vs non-stackable highest-wins; hybrid inferred from fuel

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 05:40:27 +00:00

109 lines
5.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Filament\Tenant\Resources;
use App\Filament\Tenant\Resources\PricingCoefficientResource\Pages;
use App\Models\Tenant\PricingCoefficient;
use Filament\Actions;
use Filament\Forms;
use Filament\Resources\Resource;
use Filament\Schemas;
use Filament\Schemas\Schema;
use Filament\Tables;
use Filament\Tables\Table;
class PricingCoefficientResource extends Resource
{
protected static ?string $model = PricingCoefficient::class;
protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-adjustments-horizontal';
protected static ?string $navigationLabel = 'Coeficienți preț';
protected static string|\UnitEnum|null $navigationGroup = 'Depozit';
protected static ?string $modelLabel = 'coeficient';
protected static ?string $pluralModelLabel = 'coeficienți preț';
protected static ?int $navigationSort = 46;
public static function form(Schema $schema): Schema
{
return $schema->components([
Schemas\Components\Section::make('Coeficient')
->columns(2)
->schema([
Forms\Components\TextInput::make('name')->label('Denumire')->required()
->placeholder('ex: Mașină veche, Client VIP, Express')->columnSpanFull(),
Forms\Components\TextInput::make('multiplier')
->label('Multiplicator')
->numeric()
->required()
->default(1.10)
->helperText('1.15 = +15% peste prețul de bază. 0.95 = -5%.'),
Forms\Components\TextInput::make('priority')->label('Prioritate')->numeric()->default(100),
Forms\Components\Toggle::make('stackable')
->label('Cumulabil')
->default(true)
->helperText('Cumulabil = se înmulțește cu alți coeficienți. Necumulabil = doar cel mai mare necumulabil se aplică.'),
Forms\Components\Toggle::make('is_active')->label('Activ')->default(true),
]),
Schemas\Components\Section::make('Condiții (toate trebuie îndeplinite)')
->description('Lasă gol = se aplică mereu. Combină condițiile pentru a ținti situații specifice.')
->columns(2)
->schema([
Forms\Components\CheckboxList::make('conditions.classes')
->label('Clase auto')
->options(PricingCoefficient::VEHICLE_CLASSES)
->columns(2)
->columnSpanFull(),
Forms\Components\TextInput::make('conditions.age_min')->label('Vârstă min (ani)')->numeric(),
Forms\Components\TextInput::make('conditions.age_max')->label('Vârstă max (ani)')->numeric(),
Forms\Components\Toggle::make('conditions.client_vip')->label('Doar clienți VIP'),
Forms\Components\CheckboxList::make('conditions.urgency')
->label('Urgență')
->options(PricingCoefficient::URGENCY)
->columns(3)
->columnSpanFull(),
]),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('priority')->label('Prio')->sortable()->alignRight(),
Tables\Columns\TextColumn::make('name')->searchable()->sortable(),
Tables\Columns\TextColumn::make('multiplier')
->label('Multiplicator')
->formatStateUsing(fn ($s) => '×' . rtrim(rtrim(number_format((float) $s, 3), '0'), '.'))
->alignRight()
->color(fn ($s) => (float) $s >= 1 ? 'success' : 'warning'),
Tables\Columns\IconColumn::make('stackable')->label('Cumul.')->boolean(),
Tables\Columns\IconColumn::make('is_active')->label('Activ')->boolean(),
])
->filters([
Tables\Filters\TernaryFilter::make('is_active')->label('Active'),
])
->actions([
Actions\EditAction::make(),
Actions\DeleteAction::make(),
])
->emptyStateHeading('Niciun coeficient')
->emptyStateDescription('Adaugă reguli care ajustează prețul în funcție de vârsta mașinii, clasă (SUV, comercial, hibrid), client VIP sau urgență. Se aplică peste markup-ul de bază pe fișele de lucru.')
->emptyStateIcon('heroicon-o-adjustments-horizontal')
->defaultSort('priority');
}
public static function getPages(): array
{
return [
'index' => Pages\ListPricingCoefficients::route('/'),
'create' => Pages\CreatePricingCoefficient::route('/create'),
'edit' => Pages\EditPricingCoefficient::route('/{record}/edit'),
];
}
}