i18n(finance): full page — tabs/periods/pnl/balance + Expense.CATEGORIES

- All 4 tabs (Overview/Cashflow/P&L/Balanță) built with __() around
  the text portion, emoji stays in code
- 4 period presets wrapped with __()
- P&L table Marjă/Marjă piese footer wrapped
- Expense::CATEGORIES values look up through __() in the expensesByCat
  loop so categories show translated (Salariu → Зарплата etc.)
- +25 translations covering all UI copy on /app/finance

306 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-17 06:28:53 +00:00
parent 5a2b97833a
commit 2b56bbc35b
4 changed files with 60 additions and 40 deletions
+6 -6
View File
@@ -45,10 +45,10 @@ class Finance extends Page
public function tabs(): array
{
return [
'overview' => '📊 Overview',
'cashflow' => '💸 Cashflow',
'pnl' => '📈 P&L',
'balance' => __('⚖️ Balanță'),
'overview' => '📊 ' . __('Overview'),
'cashflow' => '💸 ' . __('Cashflow'),
'pnl' => '📈 ' . __('P&L'),
'balance' => '⚖️ ' . __('Balanță'),
];
}
@@ -57,8 +57,8 @@ class Finance extends Page
return [
'this_month' => __('Luna curentă'),
'last_month' => __('Luna trecută'),
'this_year' => 'Anul curent',
'last_30' => 'Ultimele 30 zile',
'this_year' => __('Anul curent'),
'last_30' => __('Ultimele 30 zile'),
];
}
+26 -16
View File
@@ -56,6 +56,7 @@
"Acest email a fost generat automat. Vă rugăm nu răspundeți.": "This email was auto-generated. Please do not reply.",
"Achitat": "Paid",
"Achitat azi": "Paid today",
"Achiziție piese": "Parts purchase",
"Achiziții": "Purchases",
"Acord": "Agreed",
"Acord client": "Client agreement",
@@ -63,7 +64,7 @@
"Actions": "Actions",
"Activ": "Active",
"Activare": "Activation",
"Active": "Active",
"Active": "Assets",
"Active acum": "Active now",
"Activează": "Activate",
"Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.": "Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.",
@@ -307,7 +308,8 @@
"Card MAIB / MICB / Victoriabank, MD Cash, e-money": "Card MAIB / MICB / Victoriabank, MD Cash, e-money",
"Card bancar": "Bank card",
"Caroserie": "Body type",
"Cashflow zilnic — verde = încasări, roșu = cheltuieli": "Daily cashflow — green = income, red = expense",
"Cashflow": "Cashflow",
"Cashflow zilnic — verde = încasări, roșu = cheltuieli": "Daily cashflow — green = income, red = expenses",
"Catalog manoperă": "Labor catalog",
"Categorie": "Category",
"Categorie implicită": "Default category",
@@ -534,7 +536,7 @@
"Datorie": "Debt",
"Datorii": "Debts",
"Datorii clienți": "Client debt",
"Datorii clienți (de încasat)": "Client debts (to collect)",
"Datorii clienți (de încasat)": "Client debt (to collect)",
"Daune": "Daune",
"De către": "By",
"De făcut": "To do",
@@ -985,7 +987,7 @@
"Maistru / Recepție:": "Master / Reception:",
"Make": "Make",
"Manager": "Manager",
"Manopere": "Labor lines",
"Manopere": "Labor",
"Manoperă": "Labor",
"Manuală": "Manual",
"Marca": "Make",
@@ -1008,7 +1010,9 @@
"Marjă internă (%)": "Internal margin (%)",
"Marjă internă (%) — override": "Internal margin (%) — override",
"Marjă internă (nu TVA)": "Internal margin (not VAT)",
"Marjă piese:": "Parts margin:",
"Marjă profit:": "Profit margin:",
"Marjă:": "Margin:",
"Marketing": "Marketing",
"Marketing (UTM)": "Marketing (UTM)",
"Markup": "Markup",
@@ -1241,8 +1245,10 @@
"Ore programate": "Scheduled hours",
"Ore total": "Ore total",
"Ore/zi": "Hours/day",
"Overview": "Overview",
"Owner": "Owner",
"Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.": "Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.",
"P&L": "P&L",
"PDF": "PDF",
"PDR": "PDR",
"PNG/ICO, max 512 KB.": "PNG/ICO, max 512 KB.",
@@ -1296,7 +1302,7 @@
"Perioadă (YYYY-MM)": "Period (YYYY-MM)",
"Perioadă raport": "Report period",
"Perioadă trial (zile)": "Trial period (days)",
"Perioadă:": "Perioadă:",
"Perioadă:": "Period:",
"Permisiuni": "Permisiuni",
"Persoană contact": "Contact person",
"Persoană fizică": "Individual",
@@ -1310,7 +1316,7 @@
"Pierdut": "Lost",
"Pierdute": "Pierdute",
"Piese": "Parts",
"Piese (vânzare)": "Piese (vânzare)",
"Piese (vânzare)": "Parts (sales)",
"Piese impl.": "Default parts",
"Piese implicite": "Default parts",
"Piese în stoc": "Piese în stoc",
@@ -1569,6 +1575,7 @@
"SUV": "SUV",
"SUV / Crossover": "SUV / Crossover",
"Salarii": "Salaries",
"Salariu": "Salary",
"Salariu & comisioane": "Salariu & comisioane",
"Salariu & marjă": "Salary & margin",
"Salariu bază": "Base salary",
@@ -1597,6 +1604,7 @@
"Schimb ulei + filtru": "Oil + filter change",
"Scoate din magazin": "Scoate din magazin",
"Scrie mesajul tău...": "Scrie mesajul tău...",
"Scule / consumabile": "Tools / consumables",
"Scăzut": "Low",
"Se completează automat când clientul scrie la bot": "Auto-filled when the client writes to the bot",
"Se procesează...": "Processing...",
@@ -1695,7 +1703,7 @@
"Stoc adăugat": "Stoc adăugat",
"Stoc minim": "Stoc minim",
"Stoc minim atins": "Minimum stock reached",
"Stoc piese (la preț achiziție)": "Stoc piese (la preț achiziție)",
"Stoc piese (la preț achiziție)": "Parts stock (at purchase price)",
"Stock": "Stock",
"Storage": "Storage",
"Storage (MB)": "Storage (MB)",
@@ -1752,6 +1760,7 @@
"Tarifare": "Rating",
"Task": "Task",
"Tasks": "Tasks",
"Taxe": "Taxes",
"Taxă depozitare": "Storage fee",
"Taxă livrare": "Delivery fee",
"Tehnice": "Technical",
@@ -1817,7 +1826,7 @@
"Toate produsele": "All products",
"Toate rolurile": "All roles",
"Toate statusurile": "Toate statusurile",
"Toate timpurile": "Toate timpurile",
"Toate timpurile": "All time",
"Token generat!": "Token generat!",
"Token public": "Token public",
"Token revocat": "Token revocat",
@@ -1826,20 +1835,20 @@
"Top piese vândute": "Top piese vândute",
"Tot apelul trebuie făcut pe subdomain-ul tău:": "Tot apelul trebuie făcut pe subdomain-ul tău:",
"Total": "Total",
"Total active": "Total active",
"Total cheltuit": "Total cheltuit",
"Total costuri": "Total costuri",
"Total active": "Total assets",
"Total cheltuit": "Total spent",
"Total costuri": "Total costs",
"Total deals active": "Total active deals",
"Total general": "Grand total",
"Total ieșiri": "Total ieșiri",
"Total intrări": "Total intrări",
"Total ieșiri": "Total outflow",
"Total intrări": "Total inflow",
"Total net": "Total net",
"Total paid": "Total paid",
"Total plătit": "Total paid",
"Total rezultate": "Total results",
"Total venituri": "Total venituri",
"Total venituri": "Total revenue",
"Total în baza de date": "Total in database",
"Total încasat": "Total încasat",
"Total încasat": "Total received",
"Total înregistrate": "Total registered",
"Toți": "All",
"Toți VIP-ii sunt în contact recent.": "Toți VIP-ii sunt în contact recent.",
@@ -1885,6 +1894,7 @@
"Ultima logare": "Ultima logare",
"Ultima modificare": "Last modified",
"Ultima sincronizare": "Last synced",
"Ultimele 30 zile": "Last 30 days",
"Ultimul cod citit:": "Last code read:",
"Un depozit implicit a fost creat la migrare. Adaugă altele dacă ai locații fizice separate (sucursală, hală, mobil).": "A default warehouse was created at migration. Add others if you have separate physical locations (branch, hall, mobile).",
"Updated at": "Updated at",
@@ -1937,7 +1947,7 @@
"Venit luna curentă": "Current month revenue",
"Venit luna trecută": "Venit luna trecută",
"Venit manopere": "Labor revenue",
"Venituri": "Revenues",
"Venituri": "Revenue",
"Verificare": "Verification",
"Verificat": "Verified",
"Verifică emailul": "Check your email",
+25 -15
View File
@@ -56,6 +56,7 @@
"Acest email a fost generat automat. Vă rugăm nu răspundeți.": "Это письмо сгенерировано автоматически. Пожалуйста, не отвечайте.",
"Achitat": "Оплачено",
"Achitat azi": "Оплачено сегодня",
"Achiziție piese": "Закупка запчастей",
"Achiziții": "Закупки",
"Acord": "Согласовано",
"Acord client": "Согласование клиента",
@@ -63,7 +64,7 @@
"Actions": "Действия",
"Activ": "Активный",
"Activare": "Активация",
"Active": "Активный",
"Active": "Активы",
"Active acum": "В работе сейчас",
"Activează": "Активировать",
"Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.": "Activează / dezactivează notificările auto către clienți. Telegram are prioritate dacă clientul are cont legat.",
@@ -307,7 +308,8 @@
"Card MAIB / MICB / Victoriabank, MD Cash, e-money": "Card MAIB / MICB / Victoriabank, MD Cash, e-money",
"Card bancar": "Банковская карта",
"Caroserie": "Кузов",
"Cashflow zilnic — verde = încasări, roșu = cheltuieli": "Дневной cashflow — зелёный = доход, красный = расход",
"Cashflow": "Cashflow",
"Cashflow zilnic — verde = încasări, roșu = cheltuieli": "Дневной cashflow — зелёный = поступления, красный = расходы",
"Catalog manoperă": "Каталог работ",
"Categorie": "Категория",
"Categorie implicită": "Категория по умолчанию",
@@ -461,7 +463,7 @@
"Cost (terț)": "Стоимость (субподр.)",
"Cost piese (achiziție)": "Стоимость запчастей (закупка)",
"Cost/lead": "Cost/lead",
"Costuri": "Стоимости",
"Costuri": "Затраты",
"Country": "Страна",
"Coupé": "Купе",
"Creanțe": "Дебиторка",
@@ -1008,7 +1010,9 @@
"Marjă internă (%)": "Внутренняя маржа (%)",
"Marjă internă (%) — override": "Внутренняя маржа (%) — override",
"Marjă internă (nu TVA)": "Внутренняя маржа (не НДС)",
"Marjă piese:": "Маржа запчастей:",
"Marjă profit:": "Маржа прибыли:",
"Marjă:": "Маржа:",
"Marketing": "Маркетинг",
"Marketing (UTM)": "Marketing (UTM)",
"Markup": "Наценка",
@@ -1096,7 +1100,7 @@
"Nesemnat": "Не подписано",
"Nesincronizat": "Не синхронизировано",
"Net 30 / Avans": "Net 30 / Аванс",
"Net:": "Net:",
"Net:": "Итого:",
"Neverificat": "Не проверено",
"New client": "Новый клиент",
"New mașină": "Новое авто",
@@ -1241,8 +1245,10 @@
"Ore programate": "Запланированные часы",
"Ore total": "Ore total",
"Ore/zi": "Часов/день",
"Overview": "Обзор",
"Owner": "Владелец",
"Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.": "Owner = drepturi totale. Admin = aproape la fel. Sales = doar tenanți + planuri. Finance = facturi. Support = read-only.",
"P&L": "P&L",
"PDF": "PDF",
"PDR": "PDR",
"PNG/ICO, max 512 KB.": "PNG/ICO, max 512 KB.",
@@ -1296,7 +1302,7 @@
"Perioadă (YYYY-MM)": "Период (ГГГГ-ММ)",
"Perioadă raport": "Период отчёта",
"Perioadă trial (zile)": "Пробный период (дни)",
"Perioadă:": "Perioadă:",
"Perioadă:": "Период:",
"Permisiuni": "Permisiuni",
"Persoană contact": "Контактное лицо",
"Persoană fizică": "Физ. лицо",
@@ -1310,7 +1316,7 @@
"Pierdut": "Потерян",
"Pierdute": "Pierdute",
"Piese": "Запчасти",
"Piese (vânzare)": "Piese (vânzare)",
"Piese (vânzare)": "Запчасти (продажа)",
"Piese impl.": "Запчасти",
"Piese implicite": "Запчасти по умолчанию",
"Piese în stoc": "Piese în stoc",
@@ -1569,6 +1575,7 @@
"SUV": "SUV",
"SUV / Crossover": "SUV / Кроссовер",
"Salarii": "Зарплаты",
"Salariu": "Зарплата",
"Salariu & comisioane": "Salariu & comisioane",
"Salariu & marjă": "Зарплата и маржа",
"Salariu bază": "Базовая зарплата",
@@ -1597,6 +1604,7 @@
"Schimb ulei + filtru": "Замена масла + фильтр",
"Scoate din magazin": "Scoate din magazin",
"Scrie mesajul tău...": "Scrie mesajul tău...",
"Scule / consumabile": "Инструменты / расходники",
"Scăzut": "Низкий",
"Se completează automat când clientul scrie la bot": "Заполняется автоматически, когда клиент пишет боту",
"Se procesează...": "Обработка...",
@@ -1695,7 +1703,7 @@
"Stoc adăugat": "Stoc adăugat",
"Stoc minim": "Stoc minim",
"Stoc minim atins": "Достигнут мин. остаток",
"Stoc piese (la preț achiziție)": "Stoc piese (la preț achiziție)",
"Stoc piese (la preț achiziție)": "Остаток запчастей (по закупке)",
"Stock": "Остаток",
"Storage": "Хранение",
"Storage (MB)": "Storage (MB)",
@@ -1752,6 +1760,7 @@
"Tarifare": "Тарификация",
"Task": "Задача",
"Tasks": "Задачи",
"Taxe": "Налоги",
"Taxă depozitare": "Плата за хранение",
"Taxă livrare": "Плата за доставку",
"Tehnice": "Технические",
@@ -1817,7 +1826,7 @@
"Toate produsele": "Все товары",
"Toate rolurile": "Все роли",
"Toate statusurile": "Toate statusurile",
"Toate timpurile": "Toate timpurile",
"Toate timpurile": "За всё время",
"Token generat!": "Token generat!",
"Token public": "Token public",
"Token revocat": "Token revocat",
@@ -1826,20 +1835,20 @@
"Top piese vândute": "Top piese vândute",
"Tot apelul trebuie făcut pe subdomain-ul tău:": "Tot apelul trebuie făcut pe subdomain-ul tău:",
"Total": "Итого",
"Total active": "Total active",
"Total cheltuit": "Total cheltuit",
"Total costuri": "Total costuri",
"Total active": "Всего активов",
"Total cheltuit": "Всего потрачено",
"Total costuri": "Всего затрат",
"Total deals active": "Всего активных сделок",
"Total general": "Общий итог",
"Total ieșiri": "Total ieșiri",
"Total intrări": "Total intrări",
"Total ieșiri": "Всего расходов",
"Total intrări": "Всего поступлений",
"Total net": "Total net",
"Total paid": "Всего оплачено",
"Total plătit": "Всего оплачено",
"Total rezultate": "Всего результатов",
"Total venituri": "Total venituri",
"Total venituri": "Всего доходов",
"Total în baza de date": "Всего в базе",
"Total încasat": "Total încasat",
"Total încasat": "Всего получено",
"Total înregistrate": "Всего зарегистрировано",
"Toți": "Все",
"Toți VIP-ii sunt în contact recent.": "Toți VIP-ii sunt în contact recent.",
@@ -1885,6 +1894,7 @@
"Ultima logare": "Ultima logare",
"Ultima modificare": "Последнее изменение",
"Ultima sincronizare": "Последняя синхронизация",
"Ultimele 30 zile": "Последние 30 дней",
"Ultimul cod citit:": "Последний код:",
"Un depozit implicit a fost creat la migrare. Adaugă altele dacă ai locații fizice separate (sucursală, hală, mobil).": "Склад по умолчанию создан при миграции. Добавьте другие, если есть отдельные физические локации (филиал, ангар, мобильный).",
"Updated at": "Обновлён",
@@ -99,7 +99,7 @@
<table class="fn-tbl">
<tr><td>{{ __('Cost piese (achiziție)') }}</td><td class="fn-r fn-danger">{{ number_format($data['partsCost'], 2, '.', ' ') }}</td></tr>
@foreach ($data['expensesByCat'] as $row)
<tr><td>{{ \App\Models\Tenant\Expense::CATEGORIES[$row->category] ?? $row->category }}</td><td class="fn-r fn-danger">{{ number_format((float) $row->total, 2, '.', ' ') }}</td></tr>
<tr><td>{{ __(\App\Models\Tenant\Expense::CATEGORIES[$row->category] ?? $row->category) }}</td><td class="fn-r fn-danger">{{ number_format((float) $row->total, 2, '.', ' ') }}</td></tr>
@endforeach
<tr style="border-top:2px solid #e5e7eb;"><td class="fn-bold">{{ __('Total costuri') }}</td><td class="fn-r fn-bold fn-danger">{{ number_format($data['partsCost'] + $data['totalExpenses'], 2, '.', ' ') }}</td></tr>
</table>
@@ -108,8 +108,8 @@
<div class="fn-card" style="text-align:right;">
<div class="fn-stat-l">{{ __('Profit net') }}</div>
<div class="fn-stat-v {{ $data['netProfit'] >= 0 ? 'fn-success' : 'fn-danger' }}">{{ number_format($data['netProfit'], 2, '.', ' ') }} MDL</div>
<div style="font-size:12px;color:#6b7280;margin-top:4px;">Marjă: {{ $data['marginPct'] }}%</div>
<div style="font-size:11px;color:#9ca3af;margin-top:4px;">Marjă piese: {{ number_format($data['partsMargin'], 2, '.', ' ') }} MDL</div>
<div style="font-size:12px;color:#6b7280;margin-top:4px;">{{ __('Marjă:') }} {{ $data['marginPct'] }}%</div>
<div style="font-size:11px;color:#9ca3af;margin-top:4px;">{{ __('Marjă piese:') }} {{ number_format($data['partsMargin'], 2, '.', ' ') }} MDL</div>
</div>
@elseif ($tab === 'balance')