feat(injector-protocols): full module — resource + PDF export via Browsershot
Implements the injector diagnostic protocol module per spec:
- Migration: injector_protocols + injector_protocol_rows with company_id
(multi-tenant), vehicle_id (not car_id — this CRM uses Vehicle),
master_id FK to users, snapshot fields (car_model/plate/vin/mileage/year/
injector_brand) captured at protocol time.
- Models with BelongsToTenant; auto-generate protocol_number as
PS-{tenantId}-{year}-{seq6}; auto-seed 8 empty rows on create.
- Permissions: injector_protocols.view + injector_protocols.conclude.
Default roles: owner/admin (both), manager (both), receptionist (view
only), mechanic (both). Test permission count updated 52→54.
- InjectorProtocolResource under Service nav group with 4 sections:
data auto+client (with client_id/vehicle_id lookups auto-filling
snapshot fields), reason checkboxes, 8-row repeater for measurements
(Repeater with position hidden; fixed 8, non-addable/deletable),
conclusion + comment + master signature.
- Table with columns, badge filters, PDF action.
- Blade PDF template: pixel-close to reference (graphite/gold/blue
brand tokens, striped rows, rotated 'ФОРСУНКИ' header).
- Route /app/injector-protocols/{id}/pdf with permission gate,
streams PDF via new InjectorProtocolPdfService (Browsershot).
- Dockerfile: install nodejs 22 + chromium + noto fonts + puppeteer-core
in /opt/browsershot; env vars for Browsershot binary paths.
- +71 RU/EN translations covering resource + PDF template.
All 306 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+71
-1
@@ -28,6 +28,7 @@
|
||||
"2FA dezactivat.": "2FA dezactivat.",
|
||||
"2FA resetat": "2FA reset",
|
||||
"5–8.5h/10": "5–8.5h/10",
|
||||
"8 forsunki × 6 măsurători (Înainte / După). Introducerea se face pe rând.": "8 injectors × 6 measurements (Before / After). One row at a time.",
|
||||
":n poziții importate în Purchase nouă": ":n items imported into a new Purchase",
|
||||
"> 0 calculează automat prețul client.": "> 0 auto-calculates client price.",
|
||||
"A1-03": "A1-03",
|
||||
@@ -260,11 +261,13 @@
|
||||
"Autentificare 2FA": "2FA authentication",
|
||||
"Auto": "Vehicle",
|
||||
"Auto / model": "Vehicle / model",
|
||||
"Auto CRM": "CRM vehicle",
|
||||
"Auto-import Excel/CSV": "Auto-import Excel/CSV",
|
||||
"AutoCRM PSauto · psauto.service.mir.md": "AutoCRM PSauto · psauto.service.mir.md",
|
||||
"AutoCRM SRL": "AutoCRM SRL",
|
||||
"Automată": "Automatic",
|
||||
"Automobile": "Vehicles",
|
||||
"Autoservis / Client": "Auto service / Client",
|
||||
"Avans": "Advance",
|
||||
"Avans achitat": "Advance paid",
|
||||
"Avg zile": "Avg days",
|
||||
@@ -363,6 +366,7 @@
|
||||
"Caută": "Search",
|
||||
"Caută client, mașină, număr...": "Search client, vehicle, number...",
|
||||
"Caută în tabel": "Search in table",
|
||||
"Cauza adresării": "Reason for request",
|
||||
"Caz asigurare": "Insurance case",
|
||||
"Caz de asigurare": "Insurance case",
|
||||
"Ce a spus clientul...": "What the client said...",
|
||||
@@ -403,6 +407,7 @@
|
||||
"Client ID": "Client ID",
|
||||
"Client VIP": "VIP client",
|
||||
"Client existent": "Existing client",
|
||||
"Client informat / a luat cunoștință": "Client informed / acknowledged",
|
||||
"Client legat (CRM)": "Linked client (CRM)",
|
||||
"Client name": "Client name",
|
||||
"Client nou": "New client",
|
||||
@@ -445,6 +450,7 @@
|
||||
"Comandă nouă #": "New order #",
|
||||
"Comandă primită": "Order received",
|
||||
"Combustibil": "Fuel",
|
||||
"Comentariu / recomandări maistru": "Master comment / recommendations",
|
||||
"Comenzi": "Orders",
|
||||
"Comercial": "Commercial",
|
||||
"Comercial (van/camion)": "Commercial (van/truck)",
|
||||
@@ -527,6 +533,7 @@
|
||||
"Creează cerere": "Create lead",
|
||||
"Creează comandă de aprovizionare": "Create purchase order",
|
||||
"Creează planuri (ex: Free, Basic, Pro) și atribuie-le companiilor.": "Creează planuri (ex: Free, Basic, Pro) și atribuie-le companiilor.",
|
||||
"Creează primul protocol de diagnostic forsunki. Numărul se generează automat (PS-{tenant}-{an}-{seq}).": "Create the first injector diagnostic protocol. Number is auto-generated (PS-{tenant}-{year}-{seq}).",
|
||||
"Creează un bot la @BotFather, lipește token-ul aici și apasă „Setează webhook": "Create a bot with @BotFather, paste the token here and click \"Set webhook\".",
|
||||
"Creează un bot la @BotFather, lipește token-ul aici și apasă „Setează webhook\". Clienții îți scriu la bot, partajează telefonul, iar codul se leagă automat de fișa lor.": "Create a bot with @BotFather, paste the token here and click \"Set webhook\". Clients write to the bot, share their phone, and the code auto-links to their order.",
|
||||
"Crescător": "Ascending",
|
||||
@@ -546,6 +553,7 @@
|
||||
"Cumulabil = se înmulțește cu alți coeficienți. Necumulabil = doar cel mai mare necumulabil se aplică.": "Stackable = multiplies with other coefficients. Non-stackable = only the largest non-stackable applies.",
|
||||
"Curier": "Courier",
|
||||
"Currency": "Currency",
|
||||
"Curățare": "Cleaning",
|
||||
"Curăță": "Clear",
|
||||
"Custom": "Custom",
|
||||
"Cutie": "Gearbox",
|
||||
@@ -559,6 +567,7 @@
|
||||
"DENY — interzice dreptul": "DENY — deny the permission",
|
||||
"DOT": "DOT",
|
||||
"DSG": "DSG",
|
||||
"DUPĂ": "AFTER",
|
||||
"Da, confirmă": "Yes, confirm",
|
||||
"Da, șterge": "Yes, delete",
|
||||
"Dacă e gol, generăm 10 caractere random.": "If empty, we generate 10 random characters.",
|
||||
@@ -570,6 +579,7 @@
|
||||
"Data comandă": "Order date",
|
||||
"Data creării": "Created at",
|
||||
"Data deschiderii:": "Data deschiderii:",
|
||||
"Data emiterii": "Issue date",
|
||||
"Data factură": "Invoice date",
|
||||
"Data ieșire": "Delivery date",
|
||||
"Data intrare": "Intake date",
|
||||
@@ -580,6 +590,7 @@
|
||||
"Data început": "Start date",
|
||||
"Data închiderii:": "Close date:",
|
||||
"Date": "Date",
|
||||
"Date auto & client": "Vehicle & client data",
|
||||
"Date generale": "General data",
|
||||
"Date insuficiente": "Insufficient data",
|
||||
"Date legale (apar pe facturi)": "Date legale (apar pe facturi)",
|
||||
@@ -605,6 +616,7 @@
|
||||
"Deal-uri active": "Active deals",
|
||||
"Deals": "Deals",
|
||||
"Debit": "Debit",
|
||||
"Debit ÎNAINTE": "Flow BEFORE",
|
||||
"Deblochează": "Unblock",
|
||||
"Decembrie": "December",
|
||||
"Decode VIN": "Decode VIN",
|
||||
@@ -644,6 +656,7 @@
|
||||
"Deschide panoul pe telefon și acceptă notificările întâi.": "Open the panel on the phone and accept notifications first.",
|
||||
"Deschide tenantul": "Deschide tenantul",
|
||||
"Deschide →": "Open →",
|
||||
"Deschidere ÎNAINTE": "Opening BEFORE",
|
||||
"Deschis": "Opened",
|
||||
"Deschis la": "Opened at",
|
||||
"Descrescător": "Descending",
|
||||
@@ -682,6 +695,7 @@
|
||||
"Doar arhivate": "Archived only",
|
||||
"Doar clienți VIP": "VIP clients only",
|
||||
"Doar clienții mei": "My clients only",
|
||||
"Doar dacă maistrul nu e în listă.": "Only if the master is not in the list.",
|
||||
"Doar la manopere (proprii + subcontract). Baza salariu = preț client × (1 − marjă/100). Lasă gol pentru a folosi valoarea implicită a companiei.": "Only for labor (own + subcontract). Salary base = client price × (1 − margin/100). Leave empty to use company default.",
|
||||
"Doar pentru cazuri speciale. Lasă gol → folosește marja mecanicului.": "For special cases only. Leave empty → uses the mechanic's margin.",
|
||||
"Doar tu ești aici": "Only you are here",
|
||||
@@ -750,6 +764,7 @@
|
||||
"Engine": "Engine",
|
||||
"Eroare": "Error",
|
||||
"Eroare la recepție": "Receiving error",
|
||||
"Etanșeitate ÎNAINTE": "Tightness BEFORE",
|
||||
"Etape": "Stages",
|
||||
"Etapă": "Stage",
|
||||
"Etichete piese": "Etichete piese",
|
||||
@@ -830,6 +845,7 @@
|
||||
"Fișă asociată": "Linked order",
|
||||
"Fișă de lucru": "Work order",
|
||||
"Fișă lucru": "Work order",
|
||||
"Fișă lucru (opțional)": "Work order (opt.)",
|
||||
"Folie PPF": "PPF film",
|
||||
"Folosește AI Assistant": "Use AI Assistant",
|
||||
"Folosește „Check-in depozit": "Use \"Check-in storage\"",
|
||||
@@ -840,6 +856,7 @@
|
||||
"Force delete": "Force delete",
|
||||
"Force logout": "Force logout",
|
||||
"Format:": "Format:",
|
||||
"Forsunka": "Injector",
|
||||
"Foto": "Photo",
|
||||
"Foto factură": "Invoice photo",
|
||||
"Foto piesă": "Part photo",
|
||||
@@ -942,6 +959,8 @@
|
||||
"Informația de bază — click pe titlul secțiunilor de mai jos pentru detalii.": "Basic information — click on the section titles below for details.",
|
||||
"Informație": "Info",
|
||||
"Injectoare": "Injectors",
|
||||
"Injectoare noi": "New injectors",
|
||||
"Injectoarele în normă, apte pentru exploatare": "Injectors OK, fit for use",
|
||||
"Instrucțiuni adiționale": "Additional instructions",
|
||||
"Integrări": "Integrations",
|
||||
"Integrări de plată": "Payment integrations",
|
||||
@@ -1082,7 +1101,8 @@
|
||||
"Manual": "Manual",
|
||||
"Manuală": "Manual",
|
||||
"Mapare coloane": "Column mapping",
|
||||
"Marca": "Make",
|
||||
"Marca": "Brand",
|
||||
"Marca injectoarelor": "Injector brand",
|
||||
"Marca/Model": "Make/Model",
|
||||
"Marca/Model:": "Marca/Model:",
|
||||
"Marchează Gata": "Mark ready",
|
||||
@@ -1160,6 +1180,7 @@
|
||||
"Model Claude": "Claude model",
|
||||
"Model Gemini": "Gemini model",
|
||||
"Model OpenAI": "OpenAI model",
|
||||
"Model auto": "Vehicle model",
|
||||
"Model implicit": "Default model",
|
||||
"Model year": "Model year",
|
||||
"Modele": "Models",
|
||||
@@ -1178,6 +1199,7 @@
|
||||
"Mulțumim": "Thank you",
|
||||
"Mâine": "Tomorrow",
|
||||
"Mărci auto suportate (separate prin virgulă)": "Supported makes (comma-separated)",
|
||||
"Măsurători mecanica injectoarelor": "Injector mechanics measurements",
|
||||
"Name": "Name",
|
||||
"Neachitat": "Unpaid",
|
||||
"Necesită acțiune": "Needs action",
|
||||
@@ -1185,6 +1207,7 @@
|
||||
"Necesită aprobare client": "Needs client approval",
|
||||
"Necesită atenție": "Needs attention",
|
||||
"Necesită cel puțin 2 recepții complete cu data așteptată setată.": "Requires at least 2 full receipts with expected date set.",
|
||||
"Necesită curățare repetată": "Repeat cleaning needed",
|
||||
"Necompletat": "Empty",
|
||||
"Neconfirmat": "Unconfirmed",
|
||||
"Neconfirmate": "Unconfirmed",
|
||||
@@ -1238,6 +1261,7 @@
|
||||
"Niciun mecanic nu a finalizat lucrări în această perioadă.": "No mechanic has completed work in this period.",
|
||||
"Niciun plan definit": "Niciun plan definit",
|
||||
"Niciun preț înregistrat": "No prices recorded",
|
||||
"Niciun protocol": "No protocols",
|
||||
"Niciun rest de încasat. 🎉": "No balance to collect. 🎉",
|
||||
"Niciun rezultat pentru": "No results for",
|
||||
"Niciun set de anvelope": "No tire sets",
|
||||
@@ -1268,10 +1292,13 @@
|
||||
"Nou tehnician": "New technician",
|
||||
"Nouă": "New",
|
||||
"Nr.": "No.",
|
||||
"Nr. auto": "Plate",
|
||||
"Nr. dosar daună": "Damage file no.",
|
||||
"Nr. factură": "Invoice no.",
|
||||
"Nr. fișă": "Order no.",
|
||||
"Nr. injectoare": "Injector count",
|
||||
"Nr. poliță": "Policy no.",
|
||||
"Nr. protocol": "Protocol no.",
|
||||
"Nr. telefon": "Phone number",
|
||||
"Nr. înmatriculare": "License plate",
|
||||
"Nr.:": "Nr.:",
|
||||
@@ -1307,6 +1334,7 @@
|
||||
"Număr": "Number",
|
||||
"Numărul de zile gratis după ce un tenant alege acest plan.": "Free trial days when a tenant chooses this plan.",
|
||||
"OCR eșuat": "OCR failed",
|
||||
"OK": "OK",
|
||||
"Observații": "Notes",
|
||||
"Obține token cu": "Get token with",
|
||||
"Octombrie": "October",
|
||||
@@ -1556,6 +1584,7 @@
|
||||
"Progres": "Progress",
|
||||
"Progres etapă": "Stage progress",
|
||||
"Proprietar": "Owner",
|
||||
"Protocol nou": "New protocol",
|
||||
"Provider implicit": "Default provider",
|
||||
"Provider:": "Provider:",
|
||||
"Public": "Public",
|
||||
@@ -1616,6 +1645,7 @@
|
||||
"Recomandare": "Referral",
|
||||
"Recomandat": "Recommended",
|
||||
"Recomandate": "Recommended",
|
||||
"Recomandată înlocuire": "Replacement recommended",
|
||||
"Recomandă": "Recommend",
|
||||
"Recomandări": "Recommendations",
|
||||
"Recomandări AI": "AI recommendations",
|
||||
@@ -1641,6 +1671,7 @@
|
||||
"Renunță": "Cancel",
|
||||
"Reordonează": "Reorder",
|
||||
"Reparație": "Repair",
|
||||
"Reparație capitală motor": "Engine overhaul",
|
||||
"Reparație caroserie": "Body repair",
|
||||
"Reparații": "Repairs",
|
||||
"Report": "Report",
|
||||
@@ -1671,6 +1702,7 @@
|
||||
"Rezervare": "Reservation",
|
||||
"Rezervat": "Reserved",
|
||||
"Rezervă o programare": "Rezervă o programare",
|
||||
"Rezistență ÎNAINTE": "Resistance BEFORE",
|
||||
"Rezultat": "Result",
|
||||
"Rezultate": "Results",
|
||||
"Rezumat": "Summary",
|
||||
@@ -1762,6 +1794,7 @@
|
||||
"Selectează perioadă": "Select period",
|
||||
"Semnat": "Signed",
|
||||
"Semnat de": "Signed by",
|
||||
"Semnat de (manual)": "Signed by (manual)",
|
||||
"Semnează": "Sign",
|
||||
"Semnătură": "Signature",
|
||||
"Semnătură digitală": "Digital signature",
|
||||
@@ -2094,6 +2127,7 @@
|
||||
"Venit manopere": "Labor revenue",
|
||||
"Venituri": "Revenue",
|
||||
"Verificare": "Verification",
|
||||
"Verificare / diagnostic": "Check / diagnosis",
|
||||
"Verificat": "Verified",
|
||||
"Verifică emailul": "Check your email",
|
||||
"Verifică și amintește": "Verify and remind",
|
||||
@@ -2392,6 +2426,8 @@
|
||||
"programări": "appointments",
|
||||
"programări active": "active appointments",
|
||||
"programări neconfirmate < 24h": "unconfirmed appointments < 24h",
|
||||
"protocoale forsunki": "injector protocols",
|
||||
"protocol forsunki": "injector protocol",
|
||||
"rata confirmare": "confirmation rate",
|
||||
"referral": "Referral",
|
||||
"reguli markup": "markup rules",
|
||||
@@ -2446,6 +2482,8 @@
|
||||
"Începe lucrul": "Start work",
|
||||
"Închide": "Close",
|
||||
"Închide fișa": "Close order",
|
||||
"Închidere KZ ÎNAINTE": "Closing KZ BEFORE",
|
||||
"Închidere ÎNAINTE": "Closing BEFORE",
|
||||
"Închis": "Closed",
|
||||
"Închis (Duminică/sărbătoare)": "Closed (Sunday/holiday)",
|
||||
"Închis la": "Closed at",
|
||||
@@ -2453,6 +2491,7 @@
|
||||
"Încărcare STO": "Workshop load",
|
||||
"Încărcare celulă": "Cell load",
|
||||
"Încărcare service": "Workshop load",
|
||||
"Înlocuire": "Replacement",
|
||||
"Înregistrare": "Register",
|
||||
"Înregistrat": "Registered",
|
||||
"Înregistrează cheltuială": "Record expense",
|
||||
@@ -2487,10 +2526,41 @@
|
||||
"șters": "șters",
|
||||
"Țară": "Country",
|
||||
"Ține-mă minte": "Remember me",
|
||||
"Автомобиль": "Vehicle",
|
||||
"Автосервис / Клиент": "Auto service / Client",
|
||||
"Время закр. в КЗ, мс": "Closing time (KZ), ms",
|
||||
"Время закрытия, мс": "Closing time, ms",
|
||||
"Время открытия, мс": "Opening time, ms",
|
||||
"Герметичность": "Tightness",
|
||||
"Год выпуска": "Year",
|
||||
"Гос. номер": "Plate number",
|
||||
"ДАННЫЕ АВТОМОБИЛЯ И КЛИЕНТА": "VEHICLE AND CLIENT DATA",
|
||||
"Дата выдачи": "Issue date",
|
||||
"ЗАКЛЮЧЕНИЕ": "CONCLUSION",
|
||||
"Заключение": "Conclusion",
|
||||
"Заполняйте строки по количеству форсунок; лишние строки оставьте пустыми.": "Fill in rows per injector count; leave extra rows empty.",
|
||||
"ИЗМЕРЕНИЯ МЕХАНИКИ ФОРСУНОК": "INJECTOR MECHANICS MEASUREMENTS",
|
||||
"Клиент ознакомлен (подпись)": "Client acknowledged (signature)",
|
||||
"Кол-во форсунок": "Injector count",
|
||||
"Комментарий / рекомендации мастера": "Master comment / recommendations",
|
||||
"Марка форсунок": "Injector brand",
|
||||
"Мастер (ФИО, подпись)": "Master (name, signature)",
|
||||
"ПРИЧИНА ОБРАЩЕНИЯ": "REASON FOR REQUEST",
|
||||
"ПРОТОКОЛ ДИАГНОСТИКИ И ОЧИСТКИ": "DIAGNOSTIC AND CLEANING PROTOCOL",
|
||||
"Пробег, км": "Mileage, km",
|
||||
"Расход, мл": "Flow, ml",
|
||||
"Сопротивление, Ом": "Resistance, Ω",
|
||||
"Телефон": "Phone",
|
||||
"ФОРСУНКИ": "INJECTORS",
|
||||
"ЭЛЕКТРОКЛАПАННЫХ ФОРСУНОК": "OF SOLENOID INJECTORS",
|
||||
"до": "before",
|
||||
"после": "after",
|
||||
"— (multe VIN-uri europene nu respectă checksum-ul NA)": "— (multe VIN-uri europene nu respectă checksum-ul NA)",
|
||||
"— alege —": "— select —",
|
||||
"— fără pod —": "— no bay —",
|
||||
"— niciunul —": "— niciunul —",
|
||||
"№": "No.",
|
||||
"№ протокола:": "Protocol no.:",
|
||||
"ℹ️": "ℹ️",
|
||||
"← Săpt. anterioară": "← Prev. week",
|
||||
"← Înapoi": "← Back",
|
||||
|
||||
Reference in New Issue
Block a user