feat(labors): trilingual name — add name_en column + Labor::label() helper
- Migration: add nullable `labors.name_en` alongside existing name_ro/
name_ru.
- Labor model: label(?locale) accessor returns name_{locale} with
name_ro fallback.
- LaborResource form: expose 3rd 'Nume (EN)' field.
- Table + Select displays now go through label() so options show the
locale-appropriate name:
/app/labors table column,
WorkOrderResource works Select ('[category] name (Nh)'),
ServiceTemplate items Select,
ServiceComposer WorkOrderWork snapshot on create.
- Snapshot name saved on wo_works.name at creation reflects the
current locale; existing rows keep their stored snapshot untouched.
All 306 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,7 @@ class LaborResource extends Resource
|
||||
Forms\Components\TextInput::make('code')->label(__('Cod'))->maxLength(32),
|
||||
Forms\Components\TextInput::make('name_ro')->label(__('Nume (RO)'))->required()->maxLength(160),
|
||||
Forms\Components\TextInput::make('name_ru')->label(__('Nume (RU)'))->maxLength(160),
|
||||
Forms\Components\TextInput::make('name_en')->label(__('Nume (EN)'))->maxLength(160),
|
||||
Forms\Components\Select::make('pricing_mode')
|
||||
->label(__('Mod tarifare'))
|
||||
->options(\App\Support\I18n::opts(Labor::PRICING_MODES))
|
||||
@@ -81,7 +82,11 @@ class LaborResource extends Resource
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('category')->label(__('Categorie'))->badge()->sortable(),
|
||||
Tables\Columns\TextColumn::make('name_ro')->label(__('Manoperă'))->searchable()->sortable(),
|
||||
Tables\Columns\TextColumn::make('name_ro')
|
||||
->label(__('Manoperă'))
|
||||
->searchable()
|
||||
->sortable()
|
||||
->getStateUsing(fn ($record) => $record->label()),
|
||||
Tables\Columns\TextColumn::make('pricing_mode')
|
||||
->label(__('Tarifare'))
|
||||
->formatStateUsing(fn ($s) => $s === 'fixed' ? __('Fix') : __('Pe oră'))
|
||||
|
||||
+4
-2
@@ -36,13 +36,15 @@ class ItemsRelationManager extends RelationManager
|
||||
->required(),
|
||||
Forms\Components\Select::make('labor_id')
|
||||
->label(__('Manoperă'))
|
||||
->options(fn () => Labor::where('is_active', true)->pluck('name_ro', 'id'))
|
||||
->options(fn () => Labor::where('is_active', true)->get()
|
||||
->mapWithKeys(fn ($l) => [$l->id => $l->label()])
|
||||
->all())
|
||||
->searchable()
|
||||
->visible(fn (Get $get) => $get('kind') === 'labor')
|
||||
->live()
|
||||
->afterStateUpdated(function ($state, Set $set) {
|
||||
if ($state && $l = Labor::find($state)) {
|
||||
$set('name', $l->name_ro);
|
||||
$set('name', $l->label());
|
||||
$set('hours', $l->hours);
|
||||
}
|
||||
}),
|
||||
|
||||
+2
-2
@@ -31,13 +31,13 @@ class WorksRelationManager extends RelationManager
|
||||
->label(__('Catalog manoperă'))
|
||||
->options(fn () => Labor::where('is_active', true)
|
||||
->get()
|
||||
->mapWithKeys(fn ($l) => [$l->id => '[' . __($l->category) . '] ' . $l->name_ro . ' (' . $l->hours . __('h') . ')'])
|
||||
->mapWithKeys(fn ($l) => [$l->id => '[' . __($l->category) . '] ' . $l->label() . ' (' . $l->hours . __('h') . ')'])
|
||||
->toArray())
|
||||
->searchable()
|
||||
->live()
|
||||
->afterStateUpdated(function ($state, Set $set) {
|
||||
if ($state && $labor = Labor::find($state)) {
|
||||
$set('name', $labor->name_ro);
|
||||
$set('name', $labor->label());
|
||||
$set('hours', $labor->hours);
|
||||
$set('price_per_hour', $labor->hours > 0 ? round($labor->price / max((float) $labor->hours, 1), 2) : 0);
|
||||
}
|
||||
|
||||
@@ -22,10 +22,18 @@ class Labor extends Model
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'company_id', 'category', 'name_ro', 'name_ru', 'code',
|
||||
'company_id', 'category', 'name_ro', 'name_ru', 'name_en', 'code',
|
||||
'hours', 'pricing_mode', 'fixed_price', 'price', 'is_active', 'notes',
|
||||
];
|
||||
|
||||
/** Localised name — prefers name_{locale}, falls back to name_ro. */
|
||||
public function label(?string $locale = null): string
|
||||
{
|
||||
$locale = $locale ?: app()->getLocale();
|
||||
$col = 'name_' . $locale;
|
||||
return $this->{$col} ?: ($this->name_ro ?: '');
|
||||
}
|
||||
|
||||
protected $casts = [
|
||||
'hours' => 'decimal:2',
|
||||
'fixed_price' => 'decimal:2',
|
||||
|
||||
@@ -44,7 +44,7 @@ class ServiceComposer
|
||||
$work = WorkOrderWork::create([
|
||||
'work_order_id' => $wo->id,
|
||||
'labor_id' => $labor->id,
|
||||
'name' => $labor->name_ro,
|
||||
'name' => $labor->label(),
|
||||
'hours' => $hours,
|
||||
'price_per_hour' => $pricePerHour,
|
||||
'status' => 'todo',
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (! Schema::hasColumn('labors', 'name_en')) {
|
||||
Schema::table('labors', function (Blueprint $t) {
|
||||
$t->string('name_en')->nullable()->after('name_ru');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (Schema::hasColumn('labors', 'name_en')) {
|
||||
Schema::table('labors', function (Blueprint $t) {
|
||||
$t->dropColumn('name_en');
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1279,6 +1279,7 @@
|
||||
"Number": "Number",
|
||||
"Number generated": "Number generated",
|
||||
"Nume": "Name",
|
||||
"Nume (EN)": "Name (EN)",
|
||||
"Nume (RO)": "Name (RO)",
|
||||
"Nume (RU)": "Name (RU)",
|
||||
"Nume (descriere)": "Name (description)",
|
||||
|
||||
@@ -1279,6 +1279,7 @@
|
||||
"Number": "Номер",
|
||||
"Number generated": "Номер создан",
|
||||
"Nume": "Имя",
|
||||
"Nume (EN)": "Название (EN)",
|
||||
"Nume (RO)": "Название (RO)",
|
||||
"Nume (RU)": "Название (RU)",
|
||||
"Nume (descriere)": "Название (описание)",
|
||||
|
||||
Reference in New Issue
Block a user