feat: per-row and global discount with margin validation
Parts (wo_parts) already had discount_pct; now also added to wo_works. Labor catalog gets a cost_price field as the hard minimum sell floor. - WorkOrderWork: discount_pct + cost_price fields; total calculation now includes discount: hours × price_per_hour × (1 − disc/100) - Labor model: cost_price field (used to pre-fill work rows and validate) - WorksRelationManager: discount_pct field with live helper text showing effective price + margin %; validation blocks save if below buy cost or below company min_discount_margin_pct; "Reducere globală" header action applies % to all work rows (skips rows that would breach limits) - PartsRelationManager: same validation on existing discount_pct field; helper text (🟢/🟠/🔴) shows effective price + margin in real time; "Reducere globală" header action for all parts - Settings page: new "Reduceri & limite preț" section with min_discount_margin_pct — 0 = no limit, >0 = hard block below that % - LaborResource form: cost_price field Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,7 @@ class Settings extends Page
|
||||
'telegram_bot_token' => data_get($settings, 'telegram.bot_token'),
|
||||
'reminder_after_days' => data_get($settings, 'reminder.after_days', 365),
|
||||
'reminder_cooldown_days' => data_get($settings, 'reminder.cooldown_days', 30),
|
||||
'min_discount_margin_pct' => $settings['min_discount_margin_pct'] ?? 0,
|
||||
'default_internal_margin_pct' => $settings['default_internal_margin_pct'] ?? null,
|
||||
'show_internal_margin_details' => data_get($settings, 'show_internal_margin_details', true),
|
||||
'shop_enabled' => data_get($settings, 'shop.enabled', false),
|
||||
@@ -123,6 +124,21 @@ class Settings extends Page
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('labor_rate')->label(__('Tarif normo-oră'))->numeric()->required(),
|
||||
]),
|
||||
Schemas\Components\Section::make(__('Reduceri & limite preț'))
|
||||
->description(__('Setează marja minimă permisă la vânzare. Sub această limită formularul blochează salvarea.'))
|
||||
->columns(2)
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('min_discount_margin_pct')
|
||||
->label(__('Marjă minimă vânzare (%)'))
|
||||
->numeric()
|
||||
->step(0.1)
|
||||
->minValue(0)
|
||||
->maxValue(99)
|
||||
->suffix('%')
|
||||
->placeholder('Ex: 10')
|
||||
->default(0)
|
||||
->helperText(__('0 = fără limită. Ex: 10 = marja minimă 10%, adică nu poți da reducere care aduce marja sub 10%.')),
|
||||
]),
|
||||
Schemas\Components\Section::make(__('Marjă internă (nu TVA)'))
|
||||
->description(__('Procent implicit aplicat la manopere. Se scade din prețul de manoperă pentru a determina baza salariului mecanicului. Vizibilă doar rolurilor cu drept „finance.view_internal_margin".'))
|
||||
->columns(2)
|
||||
@@ -315,6 +331,8 @@ class Settings extends Page
|
||||
'after_days' => (int) ($data['reminder_after_days'] ?? 365),
|
||||
'cooldown_days' => (int) ($data['reminder_cooldown_days'] ?? 30),
|
||||
],
|
||||
'min_discount_margin_pct' => isset($data['min_discount_margin_pct']) && $data['min_discount_margin_pct'] !== ''
|
||||
? (float) $data['min_discount_margin_pct'] : 0,
|
||||
'default_internal_margin_pct' => $data['default_internal_margin_pct'] !== '' && $data['default_internal_margin_pct'] !== null
|
||||
? (float) $data['default_internal_margin_pct'] : null,
|
||||
'show_internal_margin_details' => (bool) ($data['show_internal_margin_details'] ?? true),
|
||||
|
||||
Reference in New Issue
Block a user