From 19a7afac2772dd2ce10090ae04a78d38169ec9c7 Mon Sep 17 00:00:00 2001 From: Vasyka Date: Thu, 7 May 2026 13:01:42 +0000 Subject: [PATCH] =?UTF-8?q?Faza=208:=20PDF=20generation=20pentru=20fi?= =?UTF-8?q?=C8=99a=20lucru=20(DomPDF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - barryvdh/laravel-dompdf instalat - WorkOrderPdfService: încarcă WO cu toate relațiile (works/parts/payments), embed-ează logo ca data URI, foloseste theme_color din settings - Blade template /resources/views/pdf/work-order.blade.php: - Header cu logo + date companie + nr fișă + data - Box-uri client + auto (kilometraj/VIN/plate) - Plângere + diagnostic - Tabel manopere (h, preț/h, total) cu maistru pe fiecare rând - Tabel piese (cod, brand, qty, preț, total) - Box total cu discount + plăți efectuate + rest de achitat - Block recomandări cu fundal galben (warning) - Linii semnătură client + maistru - Footer cu timestamp generare - Action 'PDF' (icon descărcare) pe rând în lista de WO - Action 'Descarcă PDF' în header-ul paginii Edit WO --- .../Tenant/Resources/WorkOrderResource.php | 13 + .../WorkOrderResource/Pages/EditWorkOrder.php | 20 +- app/Services/WorkOrderPdfService.php | 53 ++ composer.json | 1 + composer.lock | 524 +++++++++++++++++- resources/views/pdf/work-order.blade.php | 295 ++++++++++ 6 files changed, 904 insertions(+), 2 deletions(-) create mode 100644 app/Services/WorkOrderPdfService.php create mode 100644 resources/views/pdf/work-order.blade.php diff --git a/app/Filament/Tenant/Resources/WorkOrderResource.php b/app/Filament/Tenant/Resources/WorkOrderResource.php index b8b7fab..479aab0 100644 --- a/app/Filament/Tenant/Resources/WorkOrderResource.php +++ b/app/Filament/Tenant/Resources/WorkOrderResource.php @@ -134,6 +134,19 @@ class WorkOrderResource extends Resource ->options(fn () => User::pluck('name', 'id')), ]) ->actions([ + Actions\Action::make('pdf') + ->label('PDF') + ->icon('heroicon-m-document-arrow-down') + ->color('gray') + ->action(function (WorkOrder $r) { + $svc = app(\App\Services\WorkOrderPdfService::class); + $pdf = $svc->generate($r); + $filename = $svc->filename($r); + return response()->streamDownload( + fn () => print($pdf->output()), + $filename + ); + }), Actions\EditAction::make(), Actions\DeleteAction::make(), ]) diff --git a/app/Filament/Tenant/Resources/WorkOrderResource/Pages/EditWorkOrder.php b/app/Filament/Tenant/Resources/WorkOrderResource/Pages/EditWorkOrder.php index 6dabac0..63cc074 100644 --- a/app/Filament/Tenant/Resources/WorkOrderResource/Pages/EditWorkOrder.php +++ b/app/Filament/Tenant/Resources/WorkOrderResource/Pages/EditWorkOrder.php @@ -3,6 +3,8 @@ namespace App\Filament\Tenant\Resources\WorkOrderResource\Pages; use App\Filament\Tenant\Resources\WorkOrderResource; +use App\Models\Tenant\WorkOrder; +use App\Services\WorkOrderPdfService; use Filament\Actions; use Filament\Resources\Pages\EditRecord; @@ -12,6 +14,22 @@ class EditWorkOrder extends EditRecord protected function getHeaderActions(): array { - return [Actions\DeleteAction::make()]; + return [ + Actions\Action::make('pdf') + ->label('Descarcă PDF') + ->icon('heroicon-m-document-arrow-down') + ->color('gray') + ->action(function () { + /** @var WorkOrder $wo */ + $wo = $this->record; + $svc = app(WorkOrderPdfService::class); + $pdf = $svc->generate($wo); + return response()->streamDownload( + fn () => print($pdf->output()), + $svc->filename($wo) + ); + }), + Actions\DeleteAction::make(), + ]; } } diff --git a/app/Services/WorkOrderPdfService.php b/app/Services/WorkOrderPdfService.php new file mode 100644 index 0000000..b3987b7 --- /dev/null +++ b/app/Services/WorkOrderPdfService.php @@ -0,0 +1,53 @@ +load(['client', 'vehicle', 'master', 'works.master', 'parts', 'payments']); + $company = $wo->company ?: \App\Models\Central\Company::find($wo->company_id); + + $themeColor = $company?->settings['theme_color'] ?? '#3B82F6'; + $currency = $company?->settings['currency'] ?? 'MDL'; + $worksTotal = (float) $wo->works->sum('total'); + $partsTotal = (float) $wo->parts->sum('total'); + $paid = (float) $wo->payments->sum('amount'); + $balance = max(0, (float) $wo->total - $paid); + + // Embed logo as data URI (DomPDF can't fetch external URLs reliably). + $logoData = null; + if ($company && ($logoMedia = $company->getFirstMedia('logo'))) { + try { + $path = $logoMedia->getPath(); + if (file_exists($path)) { + $mime = mime_content_type($path) ?: 'image/png'; + $logoData = 'data:' . $mime . ';base64,' . base64_encode(file_get_contents($path)); + } + } catch (\Throwable $e) { + $logoData = null; + } + } + + return Pdf::loadView('pdf.work-order', [ + 'wo' => $wo, + 'company' => $company, + 'themeColor' => $themeColor, + 'currency' => $currency, + 'worksTotal' => $worksTotal, + 'partsTotal' => $partsTotal, + 'paid' => $paid, + 'balance' => $balance, + 'logoData' => $logoData, + ])->setPaper('A4'); + } + + public function filename(WorkOrder $wo): string + { + return 'WO-' . $wo->number . '-' . optional($wo->opened_at)->format('Ymd') . '.pdf'; + } +} diff --git a/composer.json b/composer.json index 7f64b04..20b2bdf 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "license": "MIT", "require": { "php": "^8.2", + "barryvdh/laravel-dompdf": "^3.1", "filament/filament": "^5.6", "filament/spatie-laravel-media-library-plugin": "^5.6", "laravel/framework": "^12.0", diff --git a/composer.lock b/composer.lock index e9324bf..d98dd6a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,85 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5125ba119dd13de03ede18bda2cbaae5", + "content-hash": "9abf8007c46bb50da99790f38e3cc040", "packages": [ + { + "name": "barryvdh/laravel-dompdf", + "version": "v3.1.2", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-dompdf.git", + "reference": "ee3b72b19ccdf57d0243116ecb2b90261344dedc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/ee3b72b19ccdf57d0243116ecb2b90261344dedc", + "reference": "ee3b72b19ccdf57d0243116ecb2b90261344dedc", + "shasum": "" + }, + "require": { + "dompdf/dompdf": "^3.0", + "illuminate/support": "^9|^10|^11|^12|^13.0", + "php": "^8.1" + }, + "require-dev": { + "larastan/larastan": "^2.7|^3.0", + "orchestra/testbench": "^7|^8|^9.16|^10|^11.0", + "phpro/grumphp": "^2.5", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "PDF": "Barryvdh\\DomPDF\\Facade\\Pdf", + "Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf" + }, + "providers": [ + "Barryvdh\\DomPDF\\ServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\DomPDF\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "A DOMPDF Wrapper for Laravel", + "keywords": [ + "dompdf", + "laravel", + "pdf" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-dompdf/issues", + "source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.2" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2026-02-21T08:51:10+00:00" + }, { "name": "blade-ui-kit/blade-heroicons", "version": "2.7.0", @@ -871,6 +948,161 @@ ], "time": "2024-02-05T11:56:58+00:00" }, + { + "name": "dompdf/dompdf", + "version": "v3.1.5", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496", + "reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496", + "shasum": "" + }, + "require": { + "dompdf/php-font-lib": "^1.0.0", + "dompdf/php-svg-lib": "^1.0.0", + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ext-gd": "*", + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "type": "library", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v3.1.5" + }, + "time": "2026-03-03T13:54:37+00:00" + }, + { + "name": "dompdf/php-font-lib", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/a6e9a688a2a80016ac080b97be73d3e10c444c9a", + "reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11 || ^12" + }, + "type": "library", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "The FontLib Community", + "homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/dompdf/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.2" + }, + "time": "2026-01-20T14:10:26+00:00" + }, + { + "name": "dompdf/php-svg-lib", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/8259ffb930817e72b1ff1caef5d226501f3dfeb1", + "reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4 || ^9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The SvgLib Community", + "homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/dompdf/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.2" + }, + "time": "2026-01-02T16:01:13+00:00" + }, { "name": "dragonmantank/cron-expression", "version": "v3.6.0", @@ -3722,6 +3954,73 @@ ], "time": "2026-04-11T18:38:28+00:00" }, + { + "name": "masterminds/html5", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fcf91eb64359852f00d921887b219479b4f21251" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + }, + "time": "2025-07-25T09:04:22+00:00" + }, { "name": "monolog/monolog", "version": "3.10.0", @@ -5430,6 +5729,86 @@ ], "time": "2026-03-19T10:36:26+00:00" }, + { + "name": "sabberworm/php-css-parser", + "version": "v9.3.0", + "source": { + "type": "git", + "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", + "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949", + "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "thecodingmachine/safe": "^1.3 || ^2.5 || ^3.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "1.4.0", + "phpstan/extension-installer": "1.4.3", + "phpstan/phpstan": "1.12.32 || 2.1.32", + "phpstan/phpstan-phpunit": "1.4.2 || 2.0.8", + "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.7", + "phpunit/phpunit": "8.5.52", + "rawr/phpunit-data-provider": "3.3.1", + "rector/rector": "1.2.10 || 2.2.8", + "rector/type-perfect": "1.0.0 || 2.1.0", + "squizlabs/php_codesniffer": "4.0.1", + "thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.1" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.4.x-dev" + } + }, + "autoload": { + "files": [ + "src/Rule/Rule.php", + "src/RuleSet/RuleContainer.php" + ], + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + }, + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Jake Hotson", + "email": "jake.github@qzdesign.co.uk" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.3.0" + }, + "time": "2026-03-03T17:31:43+00:00" + }, { "name": "scrivo/highlight.php", "version": "v9.18.1.10", @@ -8996,6 +9375,149 @@ ], "time": "2026-03-30T13:44:50+00:00" }, + { + "name": "thecodingmachine/safe", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^10", + "squizlabs/php_codesniffer": "^3.2" + }, + "type": "library", + "autoload": { + "files": [ + "lib/special_cases.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/mysqli.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rnp.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/OskarStark", + "type": "github" + }, + { + "url": "https://github.com/shish", + "type": "github" + }, + { + "url": "https://github.com/silasjoisten", + "type": "github" + }, + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2026-02-04T18:08:13+00:00" + }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "v2.4.0", diff --git a/resources/views/pdf/work-order.blade.php b/resources/views/pdf/work-order.blade.php new file mode 100644 index 0000000..1a5e0da --- /dev/null +++ b/resources/views/pdf/work-order.blade.php @@ -0,0 +1,295 @@ + + + + + {{ $wo->number }} — Fișă lucru + + + +
+
+
+ @if (!empty($logoData)) + logo +
{{ $company->display_name ?? $company->name }}
+ @else +
{{ $company->display_name ?? $company->name }}
+ @endif + @if ($company->city) +
{{ $company->city }}
+ @endif + @if ($company->phone) +
📞 {{ $company->phone }}
+ @endif + @if ($company->email) +
✉ {{ $company->email }}
+ @endif +
+
+
FIȘĂ DE LUCRU
+
Nr.: {{ $wo->number }}
+
Data deschiderii: {{ $wo->opened_at?->format('d.m.Y') }}
+ @if ($wo->closed_at) +
Data închiderii: {{ $wo->closed_at->format('d.m.Y') }}
+ @endif +
+ Status: {{ \App\Models\Tenant\WorkOrder::STATUSES[$wo->status] ?? $wo->status }} +
+
+
+
+ +
+
+
+

Client

+
+
Nume: {{ $wo->client?->name ?? '—' }}
+
Telefon: {{ $wo->client?->phone ?? '—' }}
+ @if ($wo->client?->email) +
Email: {{ $wo->client->email }}
+ @endif +
+
+
+
+
+

Auto

+
+
Marca/Model: {{ $wo->vehicle?->make }} {{ $wo->vehicle?->model }} {{ $wo->vehicle?->year }}
+
Nr.: {{ $wo->vehicle?->plate ?? '—' }}
+ @if ($wo->vehicle?->vin) +
VIN: {{ $wo->vehicle->vin }}
+ @endif +
Km la intrare: {{ $wo->mileage_in ? number_format($wo->mileage_in, 0, '.', ' ') : '—' }}
+ @if ($wo->mileage_out) +
Km la ieșire: {{ number_format($wo->mileage_out, 0, '.', ' ') }}
+ @endif +
+
+
+
+ + @if ($wo->complaint || $wo->diagnosis) +
+ @if ($wo->complaint) +

Plângere client

+
{{ $wo->complaint }}
+ @endif + @if ($wo->diagnosis) +

Diagnostic

+
{{ $wo->diagnosis }}
+ @endif +
+ @endif + + @if ($wo->works->isNotEmpty()) +

Manopere

+ + + + + + + + + + + @foreach ($wo->works as $w) + + + + + + + @endforeach + + + + + + + +
DenumireOrePreț/hTotal
{{ $w->name }}@if ($w->master?->name) — {{ $w->master->name }}@endif{{ number_format((float) $w->hours, 2) }}{{ number_format((float) $w->price_per_hour, 2, '.', ' ') }}{{ number_format((float) $w->total, 2, '.', ' ') }}
Subtotal manopere:{{ number_format((float) $worksTotal, 2, '.', ' ') }} {{ $currency }}
+ @endif + + @if ($wo->parts->isNotEmpty()) +

Piese

+ + + + + + + + + + + + @foreach ($wo->parts as $p) + + + + + + + + @endforeach + + + + + + + +
DenumireCodCant.PrețTotal
{{ $p->name }}@if ($p->brand) ({{ $p->brand }})@endif{{ $p->article ?? '—' }}{{ number_format((float) $p->qty, 2) }} {{ $p->unit }}{{ number_format((float) $p->sell_price, 2, '.', ' ') }}{{ number_format((float) $p->total, 2, '.', ' ') }}
Subtotal piese:{{ number_format((float) $partsTotal, 2, '.', ' ') }} {{ $currency }}
+ @endif + +
+ @if ($wo->discount_pct > 0) +
Subtotal: {{ number_format((float) ($worksTotal + $partsTotal), 2, '.', ' ') }} {{ $currency }}
+
Discount: -{{ $wo->discount_pct }}%
+ @endif +
+ TOTAL DE PLATĂ: {{ number_format((float) $wo->total, 2, '.', ' ') }} {{ $currency }} +
+ @if ($paid > 0) +
Achitat: {{ number_format($paid, 2, '.', ' ') }} {{ $currency }}
+
Rest de achitat: {{ number_format($balance, 2, '.', ' ') }} {{ $currency }}
+ @endif +
+ + @if ($wo->recommendations) +
+

⚠ Recomandări

+
{{ $wo->recommendations }}
+
+ @endif + +
+ + + + + +
+
Client (nume, semnătură):
+
+
+
Maistru / Recepție:
+
+
+
+ + + +