diff --git a/app/Filament/Tenant/Resources/LaborResource/RelationManagers/DefaultPartsRelationManager.php b/app/Filament/Tenant/Resources/LaborResource/RelationManagers/DefaultPartsRelationManager.php index c284419..74d7d10 100644 --- a/app/Filament/Tenant/Resources/LaborResource/RelationManagers/DefaultPartsRelationManager.php +++ b/app/Filament/Tenant/Resources/LaborResource/RelationManagers/DefaultPartsRelationManager.php @@ -36,11 +36,11 @@ class DefaultPartsRelationManager extends RelationManager ->live() ->afterStateUpdated(function ($state, Set $set) { if ($state && $p = Part::find($state)) { - $set('unit', $p->unit); + if ($p->unit_id) { $set('unit_id', $p->unit_id); } else { $set('unit', $p->unit); } } }), Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->default(1)->required(), - Forms\Components\TextInput::make('unit')->label(__('UM'))->default('buc')->maxLength(16), + Forms\Components\Select::make('unit_id')->label(__('UM'))->options(\App\Models\Tenant\Unit::forSelect())->searchable()->default(fn () => \App\Models\Tenant\Unit::where('code','buc')->value('id')), ]); } @@ -52,7 +52,7 @@ class DefaultPartsRelationManager extends RelationManager Tables\Columns\TextColumn::make('part.name')->label(__('Piesă'))->wrap(), Tables\Columns\TextColumn::make('part.article')->label(__('Cod'))->placeholder('—'), Tables\Columns\TextColumn::make('qty')->label(__('Cant.'))->alignRight(), - Tables\Columns\TextColumn::make('unit')->label(__('UM')), + Tables\Columns\TextColumn::make('unit_id')->label(__('UM'))->getStateUsing(fn ($record) => $record->unitLabel()), ]) ->headerActions([ Actions\CreateAction::make() diff --git a/app/Filament/Tenant/Resources/PartResource.php b/app/Filament/Tenant/Resources/PartResource.php index 345032b..6a3a07d 100644 --- a/app/Filament/Tenant/Resources/PartResource.php +++ b/app/Filament/Tenant/Resources/PartResource.php @@ -93,7 +93,7 @@ class PartResource extends Resource ->columns(4) ->schema([ Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->default(0)->required(), - Forms\Components\TextInput::make('unit')->label(__('UM'))->default('buc')->maxLength(16), + Forms\Components\Select::make('unit_id')->label(__('UM'))->options(\App\Models\Tenant\Unit::forSelect())->searchable()->default(fn () => \App\Models\Tenant\Unit::where('code','buc')->value('id')), Forms\Components\TextInput::make('min_qty')->label(__('Minim'))->numeric()->default(0), Forms\Components\Toggle::make('is_active')->label(__('Activ'))->default(true), Forms\Components\Toggle::make('is_published') @@ -159,7 +159,7 @@ class PartResource extends Resource ->alignRight() ->color(fn ($state) => (float) $state > 0 ? 'info' : null) ->toggleable(), - Tables\Columns\TextColumn::make('unit')->label(__('UM')), + Tables\Columns\TextColumn::make('unit_id')->label(__('UM'))->getStateUsing(fn ($record) => $record->unitLabel()), Tables\Columns\TextColumn::make('location')->label(__('Loc.'))->placeholder('—'), Tables\Columns\TextColumn::make('sell_price')->label(__('Preț vz.'))->money('MDL')->alignRight(), Tables\Columns\IconColumn::make('is_published')->label(__('Magazin'))->boolean()->toggleable(), diff --git a/app/Filament/Tenant/Resources/PurchaseResource/RelationManagers/ItemsRelationManager.php b/app/Filament/Tenant/Resources/PurchaseResource/RelationManagers/ItemsRelationManager.php index 82c3fc3..32a5e2d 100644 --- a/app/Filament/Tenant/Resources/PurchaseResource/RelationManagers/ItemsRelationManager.php +++ b/app/Filament/Tenant/Resources/PurchaseResource/RelationManagers/ItemsRelationManager.php @@ -40,7 +40,7 @@ class ItemsRelationManager extends RelationManager if ($state && $part = Part::find($state)) { $set('name', $part->name); $set('article', $part->article); - $set('unit', $part->unit); + if ($part->unit_id) { $set('unit_id', $part->unit_id); } else { $set('unit', $part->unit); } $set('buy_price', $part->buy_price); } }) @@ -48,7 +48,7 @@ class ItemsRelationManager extends RelationManager Forms\Components\TextInput::make('name')->label(__('Denumire'))->required()->columnSpanFull(), Forms\Components\TextInput::make('article')->label(__('Cod articol')), Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->default(1)->required(), - Forms\Components\TextInput::make('unit')->label(__('UM'))->default('buc'), + Forms\Components\Select::make('unit_id')->label(__('UM'))->options(\App\Models\Tenant\Unit::forSelect())->searchable()->default(fn () => \App\Models\Tenant\Unit::where('code','buc')->value('id')), Forms\Components\TextInput::make('buy_price')->label(__('Preț achiziție'))->numeric()->required(), ]); } @@ -66,7 +66,7 @@ class ItemsRelationManager extends RelationManager ->alignRight() ->color(fn ($state, $record) => $record->isFullyReceived() ? 'success' : ((float) $state > 0 ? 'warning' : 'gray')) ->formatStateUsing(fn ($state, $record) => sprintf('%.2f / %.2f', (float) $state, (float) $record->qty)), - Tables\Columns\TextColumn::make('unit')->label(__('UM')), + Tables\Columns\TextColumn::make('unit_id')->label(__('UM'))->getStateUsing(fn ($record) => $record->unitLabel()), Tables\Columns\TextColumn::make('buy_price')->money('MDL')->alignRight(), Tables\Columns\TextColumn::make('total')->money('MDL')->alignRight(), ]) @@ -84,7 +84,7 @@ class ItemsRelationManager extends RelationManager ->schema([ Forms\Components\Placeholder::make('outstanding') ->label(__('Restanță')) - ->content(fn (PurchaseItem $r) => sprintf('%.2f %s', $r->outstanding(), $r->unit ?? 'buc')), + ->content(fn (PurchaseItem $r) => sprintf('%.2f %s', $r->outstanding(), $r->unitLabel() ?: 'buc')), Forms\Components\TextInput::make('qty') ->label(__('Cantitate recepționată')) ->numeric() diff --git a/app/Filament/Tenant/Resources/UnitResource.php b/app/Filament/Tenant/Resources/UnitResource.php new file mode 100644 index 0000000..fb2e85b --- /dev/null +++ b/app/Filament/Tenant/Resources/UnitResource.php @@ -0,0 +1,104 @@ +components([ + Schemas\Components\Section::make(__('Cod & sortare')) + ->columns(3) + ->schema([ + Forms\Components\TextInput::make('code') + ->label(__('Cod')) + ->required() + ->maxLength(16) + ->helperText(__('Codul intern (buc, l, kg, ...) — stocat în DB.')), + Forms\Components\TextInput::make('sort_order') + ->label(__('Ordine')) + ->numeric() + ->default(100), + Forms\Components\Toggle::make('is_active') + ->label(__('Activ')) + ->default(true), + ]), + Schemas\Components\Section::make(__('Nume în cele 3 limbi')) + ->columns(3) + ->schema([ + Forms\Components\TextInput::make('name_ro')->label('Română (RO)')->required()->maxLength(40), + Forms\Components\TextInput::make('name_ru')->label('Русский (RU)')->required()->maxLength(40), + Forms\Components\TextInput::make('name_en')->label('English (EN)')->required()->maxLength(40), + ]), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('sort_order')->label(__('Ordine'))->sortable(), + Tables\Columns\TextColumn::make('code')->searchable()->sortable(), + Tables\Columns\TextColumn::make('name_ro')->label('RO')->searchable(), + Tables\Columns\TextColumn::make('name_ru')->label('RU')->searchable(), + Tables\Columns\TextColumn::make('name_en')->label('EN')->searchable(), + Tables\Columns\IconColumn::make('is_active')->label(__('Activ'))->boolean(), + ]) + ->defaultSort('sort_order') + ->filters([ + Tables\Filters\TernaryFilter::make('is_active')->label(__('Active')), + ]) + ->actions([ + Actions\EditAction::make(), + Actions\DeleteAction::make(), + ]) + ->emptyStateHeading(__('Niciun UM înregistrat')) + ->emptyStateDescription(__('Migrarea inițială a creat setul standard. Adaugă altele dacă ai nevoie (ex: rulou, doză, cutie).')) + ->emptyStateIcon('heroicon-o-scale'); + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListUnits::route('/'), + 'create' => Pages\CreateUnit::route('/create'), + 'edit' => Pages\EditUnit::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Tenant/Resources/UnitResource/Pages/CreateUnit.php b/app/Filament/Tenant/Resources/UnitResource/Pages/CreateUnit.php new file mode 100644 index 0000000..d65fc3b --- /dev/null +++ b/app/Filament/Tenant/Resources/UnitResource/Pages/CreateUnit.php @@ -0,0 +1,11 @@ +label(__('Adaugă UM'))]; + } +} diff --git a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PartsRelationManager.php b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PartsRelationManager.php index 4094386..37c115d 100644 --- a/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PartsRelationManager.php +++ b/app/Filament/Tenant/Resources/WorkOrderResource/RelationManagers/PartsRelationManager.php @@ -42,7 +42,7 @@ class PartsRelationManager extends RelationManager $set('name', $part->name); $set('article', $part->article); $set('brand', $part->brand); - $set('unit', $part->unit); + if ($part->unit_id) { $set('unit_id', $part->unit_id); } else { $set('unit', $part->unit); } $set('buy_price', $part->buy_price); $set('sell_price', $part->sell_price); } @@ -52,7 +52,7 @@ class PartsRelationManager extends RelationManager Forms\Components\TextInput::make('article')->label(__('Cod articol'))->maxLength(64), Forms\Components\TextInput::make('brand')->maxLength(64), Forms\Components\TextInput::make('qty')->label(__('Cantitate'))->numeric()->default(1)->required(), - Forms\Components\TextInput::make('unit')->label(__('UM'))->maxLength(16)->default('buc'), + Forms\Components\Select::make('unit_id')->label(__('UM'))->options(\App\Models\Tenant\Unit::forSelect())->searchable()->default(fn () => \App\Models\Tenant\Unit::where('code','buc')->value('id'))->default('buc'), Forms\Components\TextInput::make('buy_price')->label(__('Preț achiziție'))->numeric()->default(0), Forms\Components\TextInput::make('sell_price')->label(__('Preț vânzare'))->numeric()->required(), Forms\Components\TextInput::make('discount_pct')->label(__('Discount %'))->numeric()->default(0), diff --git a/app/Models/Tenant/LaborPart.php b/app/Models/Tenant/LaborPart.php index 8d40e76..92a46ac 100644 --- a/app/Models/Tenant/LaborPart.php +++ b/app/Models/Tenant/LaborPart.php @@ -10,7 +10,17 @@ class LaborPart extends Model { use BelongsToTenant; - protected $fillable = ['company_id', 'labor_id', 'part_id', 'qty', 'unit']; + protected $fillable = ['company_id', 'labor_id', 'part_id', 'qty', 'unit', 'unit_id']; + + public function unitModel(): BelongsTo + { + return $this->belongsTo(Unit::class, 'unit_id'); + } + + public function unitLabel(?string $locale = null): string + { + return Unit::labelFor($this->unit_id, $this->unit, $locale); + } protected $casts = ['qty' => 'decimal:2']; diff --git a/app/Models/Tenant/Part.php b/app/Models/Tenant/Part.php index 303c6d0..7a5c3c3 100644 --- a/app/Models/Tenant/Part.php +++ b/app/Models/Tenant/Part.php @@ -44,12 +44,23 @@ class Part extends Model implements HasMedia protected $fillable = [ 'company_id', 'name', 'article', 'brand', 'category', - 'qty', 'qty_reserved', 'unit', 'min_qty', + 'qty', 'qty_reserved', 'unit', 'unit_id', 'min_qty', 'buy_price', 'sell_price', 'hidden_markup_pct', 'location', 'barcode', 'preferred_supplier_id', 'is_active', 'is_published', 'notes', ]; + public function unitModel() + { + return $this->belongsTo(Unit::class, 'unit_id'); + } + + /** Localised UM label (Unit::label() or the legacy `unit` string). */ + public function unitLabel(?string $locale = null): string + { + return Unit::labelFor($this->unit_id, $this->unit, $locale); + } + protected $casts = [ 'qty' => 'decimal:2', 'qty_reserved' => 'decimal:3', diff --git a/app/Models/Tenant/PurchaseItem.php b/app/Models/Tenant/PurchaseItem.php index 9065128..6dd9ef0 100644 --- a/app/Models/Tenant/PurchaseItem.php +++ b/app/Models/Tenant/PurchaseItem.php @@ -12,9 +12,19 @@ class PurchaseItem extends Model protected $fillable = [ 'company_id', 'purchase_id', 'part_id', - 'name', 'article', 'qty', 'qty_received', 'unit', 'buy_price', 'total', 'received', + 'name', 'article', 'qty', 'qty_received', 'unit', 'unit_id', 'buy_price', 'total', 'received', ]; + public function unitModel() + { + return $this->belongsTo(Unit::class, 'unit_id'); + } + + public function unitLabel(?string $locale = null): string + { + return Unit::labelFor($this->unit_id, $this->unit, $locale); + } + protected $casts = [ 'qty' => 'decimal:2', 'qty_received' => 'decimal:2', diff --git a/app/Models/Tenant/Unit.php b/app/Models/Tenant/Unit.php new file mode 100644 index 0000000..8de7beb --- /dev/null +++ b/app/Models/Tenant/Unit.php @@ -0,0 +1,56 @@ + 'bool', + 'sort_order' => 'int', + ]; + + /** Returns the localised name (name_ro / name_ru / name_en). Fallback: name_ro, then code. */ + public function label(?string $locale = null): string + { + $locale = $locale ?: app()->getLocale(); + $col = 'name_' . $locale; + return $this->{$col} ?: ($this->name_ro ?: $this->code); + } + + /** Options array keyed by id for Filament Select. */ + public static function forSelect(?string $locale = null): array + { + return static::query() + ->where('is_active', true) + ->orderBy('sort_order') + ->orderBy('code') + ->get() + ->mapWithKeys(fn (Unit $u) => [$u->id => $u->label($locale)]) + ->all(); + } + + /** Look up label from either a Unit id or a legacy code string. */ + public static function labelFor(?int $unitId, ?string $codeFallback = null, ?string $locale = null): string + { + if ($unitId) { + $u = static::find($unitId); + if ($u) return $u->label($locale); + } + if ($codeFallback) { + $u = static::where('code', $codeFallback)->first(); + if ($u) return $u->label($locale); + return $codeFallback; + } + return ''; + } +} diff --git a/app/Models/Tenant/WorkOrderPart.php b/app/Models/Tenant/WorkOrderPart.php index 45201c8..79a645a 100644 --- a/app/Models/Tenant/WorkOrderPart.php +++ b/app/Models/Tenant/WorkOrderPart.php @@ -22,11 +22,21 @@ class WorkOrderPart extends Model protected $fillable = [ 'company_id', 'work_order_id', 'part_id', 'name', 'article', 'brand', - 'qty', 'unit', 'buy_price', 'sell_price', + 'qty', 'unit', 'unit_id', 'buy_price', 'sell_price', 'discount_pct', 'total', 'status', 'notes', 'requires_approval', 'approved_at', 'approval_token', 'declined_at', ]; + public function unitModel() + { + return $this->belongsTo(Unit::class, 'unit_id'); + } + + public function unitLabel(?string $locale = null): string + { + return Unit::labelFor($this->unit_id, $this->unit, $locale); + } + protected $casts = [ 'qty' => 'decimal:2', 'buy_price' => 'decimal:2', diff --git a/database/migrations/2026_07_16_000001_create_units_and_link_unit_id.php b/database/migrations/2026_07_16_000001_create_units_and_link_unit_id.php new file mode 100644 index 0000000..7cf2388 --- /dev/null +++ b/database/migrations/2026_07_16_000001_create_units_and_link_unit_id.php @@ -0,0 +1,128 @@ +id(); + $t->foreignId('company_id')->constrained('companies')->cascadeOnDelete(); + $t->string('code', 16); + $t->string('name_ro', 40); + $t->string('name_ru', 40); + $t->string('name_en', 40); + $t->boolean('is_active')->default(true); + $t->integer('sort_order')->default(0); + $t->timestamps(); + $t->unique(['company_id', 'code']); + }); + } + + foreach (['parts', 'wo_parts', 'purchase_items', 'labor_parts'] as $table) { + if (! Schema::hasColumn($table, 'unit_id')) { + Schema::table($table, function (Blueprint $t) { + $t->foreignId('unit_id')->nullable()->after('unit')->constrained('units')->nullOnDelete(); + }); + } + } + + $defaults = [ + ['code' => 'buc', 'ro' => 'buc', 'ru' => 'шт', 'en' => 'pcs', 'sort' => 10], + ['code' => 'set', 'ro' => 'set', 'ru' => 'компл', 'en' => 'set', 'sort' => 20], + ['code' => 'l', 'ro' => 'l', 'ru' => 'л', 'en' => 'L', 'sort' => 30], + ['code' => 'ml', 'ro' => 'ml', 'ru' => 'мл', 'en' => 'mL', 'sort' => 40], + ['code' => 'kg', 'ro' => 'kg', 'ru' => 'кг', 'en' => 'kg', 'sort' => 50], + ['code' => 'g', 'ro' => 'g', 'ru' => 'г', 'en' => 'g', 'sort' => 60], + ['code' => 'm', 'ro' => 'm', 'ru' => 'м', 'en' => 'm', 'sort' => 70], + ['code' => 'cm', 'ro' => 'cm', 'ru' => 'см', 'en' => 'cm', 'sort' => 80], + ['code' => 'm2', 'ro' => 'm²', 'ru' => 'м²', 'en' => 'sq.m', 'sort' => 90], + ['code' => 'ora', 'ro' => 'oră', 'ru' => 'час', 'en' => 'hour', 'sort' => 100], + ['code' => 'pack', 'ro' => 'pach', 'ru' => 'уп', 'en' => 'pack', 'sort' => 110], + ]; + + $companies = DB::table('companies')->pluck('id'); + $now = now(); + foreach ($companies as $companyId) { + foreach ($defaults as $u) { + DB::table('units')->insertOrIgnore([ + 'company_id' => $companyId, + 'code' => $u['code'], + 'name_ro' => $u['ro'], + 'name_ru' => $u['ru'], + 'name_en' => $u['en'], + 'is_active' => true, + 'sort_order' => $u['sort'], + 'created_at' => $now, + 'updated_at' => $now, + ]); + } + + $extraCodes = collect(); + foreach (['parts', 'wo_parts', 'purchase_items', 'labor_parts'] as $table) { + $codes = DB::table($table) + ->where('company_id', $companyId) + ->whereNotNull('unit') + ->distinct()->pluck('unit')->filter()->map(fn ($c) => trim((string) $c))->filter(); + $extraCodes = $extraCodes->merge($codes); + } + $extraCodes = $extraCodes->unique(); + + $existingCodes = DB::table('units')->where('company_id', $companyId)->pluck('code')->all(); + foreach ($extraCodes as $code) { + if (in_array($code, $existingCodes, true)) continue; + if (mb_strlen($code) > 16) continue; + DB::table('units')->insert([ + 'company_id' => $companyId, + 'code' => $code, + 'name_ro' => $code, + 'name_ru' => $code, + 'name_en' => $code, + 'is_active' => true, + 'sort_order' => 200, + 'created_at' => $now, + 'updated_at' => $now, + ]); + } + + foreach (['parts', 'wo_parts', 'purchase_items', 'labor_parts'] as $table) { + DB::statement(" + UPDATE {$table} + SET unit_id = ( + SELECT id FROM units + WHERE units.company_id = {$table}.company_id + AND units.code = {$table}.unit + LIMIT 1 + ) + WHERE {$table}.company_id = ? + AND {$table}.unit_id IS NULL + AND {$table}.unit IS NOT NULL + ", [$companyId]); + } + } + } + + public function down(): void + { + foreach (['parts', 'wo_parts', 'purchase_items', 'labor_parts'] as $table) { + Schema::table($table, function (Blueprint $t) { + $t->dropForeign(['unit_id']); + $t->dropColumn('unit_id'); + }); + } + Schema::dropIfExists('units'); + } +}; diff --git a/lang/en.json b/lang/en.json index c11570e..4bf813b 100644 --- a/lang/en.json +++ b/lang/en.json @@ -83,6 +83,7 @@ "Acțiuni în bulk": "Bulk actions", "Acțiuni în masă": "Bulk actions", "Adaugă": "Add", + "Adaugă UM": "Add UoM", "Adaugă articol": "Add item", "Adaugă articol în comandă": "Add item to order", "Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.": "Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.", @@ -366,6 +367,7 @@ "Climatizare": "AC", "Closed at": "Closed at", "Cod": "Code", + "Cod & sortare": "Code & sort", "Cod QR": "QR code", "Cod articol": "Article code", "Cod bare": "Barcode", @@ -374,6 +376,7 @@ "Cod tracking": "Tracking code", "Cod uzină": "Factory code", "Code": "Code", + "Codul intern (buc, l, kg, ...) — stocat în DB.": "Internal code (buc, l, kg, ...) — stored in DB.", "Codul produsului": "Product code", "Coduri cross (OEM/echivalente)": "Cross codes (OEM/equivalents)", "Coeficient": "Coeficient", @@ -1033,6 +1036,7 @@ "Metodă plată": "Payment method", "Miercuri": "Wednesday", "Migrare la alt sistem CRM": "Migrare la alt sistem CRM", + "Migrarea inițială a creat setul standard. Adaugă altele dacă ai nevoie (ex: rulou, doză, cutie).": "The initial migration created the standard set. Add others if you need (e.g. roll, dose, box).", "Mileage": "Mileage", "Min qty": "Min qty", "Min.": "Min.", @@ -1106,6 +1110,7 @@ "Nicio programare în această perioadă.": "No appointments in this period.", "Nicio programare în perioada selectată.": "No appointments in selected period.", "Niciodată": "Never", + "Niciun UM înregistrat": "No units of measure", "Niciun client magazin": "Niciun client magazin", "Niciun client pierdut. 🎉": "Niciun client pierdut. 🎉", "Niciun client încă": "No clients yet", @@ -1177,6 +1182,7 @@ "Nume canal": "Channel name", "Nume familie": "Last name", "Nume template": "Template name", + "Nume în cele 3 limbi": "Name in 3 languages", "Nume, cod articol, brand...": "Name, article, brand...", "Nume:": "Nume:", "Numele este obligatoriu": "Numele este obligatoriu", @@ -2212,6 +2218,8 @@ "template": "template", "template-uri": "templates", "ultima fișă": "ultima fișă", + "unitate de măsură": "unit of measure", + "unități de măsură": "units of measure", "utilizator": "user", "utilizatori": "users", "ușor": "ușor", diff --git a/lang/en/nav.php b/lang/en/nav.php index 2ca29b2..1d982d4 100644 --- a/lang/en/nav.php +++ b/lang/en/nav.php @@ -38,6 +38,7 @@ return [ 'Companii' => 'Companies', 'Depozit' => 'Warehouse', 'Depozite' => 'Warehouses', + 'Unități de măsură' => 'Units of measure', 'Facturi & abonamente' => 'Invoices & subscriptions', 'Finanțe (consolidat)' => 'Finance (consolidated)', 'Fișe lucru' => 'Work orders', diff --git a/lang/ro/nav.php b/lang/ro/nav.php index c62d38f..e538551 100644 --- a/lang/ro/nav.php +++ b/lang/ro/nav.php @@ -40,6 +40,7 @@ return [ 'Companii' => 'Companii', 'Depozit' => 'Depozit', 'Depozite' => 'Depozite', + 'Unități de măsură' => 'Unități de măsură', 'Facturi & abonamente' => 'Facturi & abonamente', 'Finanțe (consolidat)' => 'Finanțe (consolidat)', 'Fișe lucru' => 'Fișe lucru', diff --git a/lang/ru.json b/lang/ru.json index 0d412ee..425997e 100644 --- a/lang/ru.json +++ b/lang/ru.json @@ -83,6 +83,7 @@ "Acțiuni în bulk": "Массовые действия", "Acțiuni în masă": "Массовые действия", "Adaugă": "Добавить", + "Adaugă UM": "Добавить ЕИ", "Adaugă articol": "Добавить позицию", "Adaugă articol în comandă": "Добавить позицию в заказ", "Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.": "Adaugă atelierele terțe la care trimiți lucrări (turbo, cutii, vopsitorie, PDR) și urmărește costul + marja.", @@ -366,6 +367,7 @@ "Climatizare": "Кондиционер", "Closed at": "Закрыт", "Cod": "Код", + "Cod & sortare": "Код и сортировка", "Cod QR": "QR-код", "Cod articol": "Артикул", "Cod bare": "Штрих-код", @@ -374,6 +376,7 @@ "Cod tracking": "Код отслеживания", "Cod uzină": "Заводской код", "Code": "Код", + "Codul intern (buc, l, kg, ...) — stocat în DB.": "Внутренний код (buc, l, kg, ...) — хранится в БД.", "Codul produsului": "Код товара", "Coduri cross (OEM/echivalente)": "Кросс-коды (OEM/эквиваленты)", "Coeficient": "Coeficient", @@ -1033,6 +1036,7 @@ "Metodă plată": "Способ оплаты", "Miercuri": "Среда", "Migrare la alt sistem CRM": "Migrare la alt sistem CRM", + "Migrarea inițială a creat setul standard. Adaugă altele dacă ai nevoie (ex: rulou, doză, cutie).": "Начальная миграция создала стандартный набор. Добавьте другие при необходимости (например: рулон, доза, коробка).", "Mileage": "Пробег", "Min qty": "Мин. кол-во", "Min.": "Мин.", @@ -1106,6 +1110,7 @@ "Nicio programare în această perioadă.": "Нет записей в этом периоде.", "Nicio programare în perioada selectată.": "Нет записей за выбранный период.", "Niciodată": "Никогда", + "Niciun UM înregistrat": "Нет единиц измерения", "Niciun client magazin": "Niciun client magazin", "Niciun client pierdut. 🎉": "Niciun client pierdut. 🎉", "Niciun client încă": "Пока нет клиентов", @@ -1177,6 +1182,7 @@ "Nume canal": "Название канала", "Nume familie": "Фамилия", "Nume template": "Название шаблона", + "Nume în cele 3 limbi": "Название на 3 языках", "Nume, cod articol, brand...": "Название, артикул, бренд...", "Nume:": "Nume:", "Numele este obligatoriu": "Numele este obligatoriu", @@ -2212,6 +2218,8 @@ "template": "шаблон", "template-uri": "шаблоны", "ultima fișă": "ultima fișă", + "unitate de măsură": "единица измерения", + "unități de măsură": "единицы измерения", "utilizator": "пользователь", "utilizatori": "пользователи", "ușor": "ușor", diff --git a/lang/ru/nav.php b/lang/ru/nav.php index 43cd744..330c005 100644 --- a/lang/ru/nav.php +++ b/lang/ru/nav.php @@ -40,6 +40,7 @@ return [ 'Companii' => 'Компании', 'Depozit' => 'Склад', 'Depozite' => 'Склады', + 'Unități de măsură' => 'Единицы измерения', 'Facturi & abonamente' => 'Счета и подписки', 'Finanțe (consolidat)' => 'Финансы (сводно)', 'Fișe lucru' => 'Заказ-наряды',