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:
2026-07-26 11:27:53 +00:00
parent 0e85035eff
commit 47eb4f62c1
20 changed files with 1218 additions and 8 deletions
+11
View File
@@ -81,6 +81,10 @@ class Permissions
public const AI_ASSISTANT_CONFIGURE_KEYS = 'ai_assistant.configure_keys';
public const ANALYTICS_VIEW = 'analytics.view';
// Injector diagnostic protocols
public const INJECTOR_PROTOCOLS_VIEW = 'injector_protocols.view';
public const INJECTOR_PROTOCOLS_CONCLUDE = 'injector_protocols.conclude';
/** Full list — used by seeder. */
public static function all(): array
{
@@ -102,6 +106,7 @@ class Permissions
self::ADMIN_SETTINGS_EDIT, self::ADMIN_INTEGRATIONS, self::ADMIN_API_TOKENS_MANAGE,
self::ADMIN_AUDIT_LOG_VIEW, self::ADMIN_BACKUP_DOWNLOAD,
self::AI_ASSISTANT_USE, self::AI_ASSISTANT_CONFIGURE_KEYS, self::ANALYTICS_VIEW,
self::INJECTOR_PROTOCOLS_VIEW, self::INJECTOR_PROTOCOLS_CONCLUDE,
];
}
@@ -129,6 +134,7 @@ class Permissions
'admin' => 'Administrare',
'ai_assistant' => 'AI Assistant',
'analytics' => 'Analitică',
'injector_protocols' => 'Protocol forsunki',
];
}
@@ -188,6 +194,8 @@ class Permissions
'ai_assistant.use' => 'Folosește AI Assistant',
'ai_assistant.configure_keys' => 'Configurează chei API pentru AI',
'analytics.view' => 'Vezi rapoarte & analitică',
'injector_protocols.view' => 'Vezi protocoalele de diagnostic forsunki',
'injector_protocols.conclude' => 'Completează Заключение (concluzii + semnătură)',
];
}
@@ -221,6 +229,7 @@ class Permissions
self::SUPPLIERS_VIEW, self::SUPPLIERS_EDIT,
self::AI_ASSISTANT_USE,
self::ANALYTICS_VIEW,
self::INJECTOR_PROTOCOLS_VIEW, self::INJECTOR_PROTOCOLS_CONCLUDE,
];
// accountant: finance + reporting only
@@ -247,6 +256,7 @@ class Permissions
self::SALARIES_VIEW_OWN,
self::INVENTORY_VIEW,
self::AI_ASSISTANT_USE,
self::INJECTOR_PROTOCOLS_VIEW,
];
// mechanic: only own WOs + inventory view
@@ -254,6 +264,7 @@ class Permissions
self::WORK_ORDERS_VIEW_OWN_ASSIGNED, self::WORK_ORDERS_CHANGE_STATUS,
self::INVENTORY_VIEW,
self::SALARIES_VIEW_OWN,
self::INJECTOR_PROTOCOLS_VIEW, self::INJECTOR_PROTOCOLS_CONCLUDE,
];
// viewer: read-only