From a4e3069c5c49b1141a9e8cee12ea0a8873b95fe8 Mon Sep 17 00:00:00 2001 From: Vincent Vanwaelscappel Date: Mon, 13 May 2024 21:21:42 +0200 Subject: [PATCH] wait #6906 @4 --- .docker/images/php-fpm/Dockerfile | 2 +- app/Http/Controllers/FluidbookController.php | 41 ++++++++++- app/Models/Order.php | 6 +- app/Notifications/ResahNotification.php | 5 +- package.json | 5 ++ resources/js/printpageaspdf.cjs | 44 +++++++++++ resources/views/order.blade.php | 77 ++++++++++++++++++++ routes/web.php | 10 ++- 8 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 resources/js/printpageaspdf.cjs create mode 100644 resources/views/order.blade.php diff --git a/.docker/images/php-fpm/Dockerfile b/.docker/images/php-fpm/Dockerfile index d53ce81..6f9339a 100644 --- a/.docker/images/php-fpm/Dockerfile +++ b/.docker/images/php-fpm/Dockerfile @@ -67,7 +67,7 @@ ENV LC_ALL fr_FR.UTF-8 COPY --from=composer:2 /usr/bin/composer /usr/bin/composer # IF you need some npm globally installed packages -RUN npm install --unsafe-perm --global yarn puppeteer +RUN npm install --unsafe-perm --global yarn puppeteer fs command-line-args CMD ["php", "-a"] diff --git a/app/Http/Controllers/FluidbookController.php b/app/Http/Controllers/FluidbookController.php index dfdceae..1ced1c8 100644 --- a/app/Http/Controllers/FluidbookController.php +++ b/app/Http/Controllers/FluidbookController.php @@ -6,6 +6,7 @@ use App\Models\Client; use App\Models\Order; use App\Models\User; use App\Notifications\ResahNotification; +use Cubist\Util\CommandLine; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; @@ -43,7 +44,7 @@ class FluidbookController extends Controller $ignore = ['email_confirmation', 'password_confirmation', 'accept', '_token', 'valid_from']; $client = new Client(); foreach ($validator->valid() as $k => $v) { - if (in_array($k, $ignore) || strstr($k,'honeypot_for_bots_')) { + if (in_array($k, $ignore) || strstr($k, 'honeypot_for_bots_')) { continue; } if ($k === 'password') { @@ -104,7 +105,11 @@ class FluidbookController extends Controller $total = 0; $cumul_tva = 0; $cumul_ecotaxe = 0; - foreach (request('details') as $i) { + $raw = request('details'); + $details_json = ['raw' => $raw, 'lines' => [], 'totals' => []]; + foreach ($raw as $i) { + + $p = $data[$i['reference']]; $quantity += $i['quantity']; @@ -118,32 +123,64 @@ class FluidbookController extends Controller $cumul_ecotaxe += $ecotaxe; $d = []; + $jd = []; + $jt = []; foreach ($p as $k => $v) { if (!$k || !$v) { continue; } + $d[] = $k . " : " . $v; + $jd[] = $v; + $jt[] = $k; } $d[] = '--'; $d[] = 'QUANTITE : ' . $i['quantity']; $d[] = 'TOTAL HT : ' . self::formatNumber($tht); + $jt[] = 'QUANTITE'; + $jt[] = 'TOTAL HT'; + $jd[] = $i['quantity']; + $jd[] = self::formatNumber($tht); $details[] = implode("\n", $d); + + $details_json['lines'][] = $jd; } + $details[] = 'TOTAL HT : ' . self::formatNumber($total) . "\n" . 'TVA : ' . self::formatNumber($cumul_tva) . "\n" . 'ECOTAXE : ' . self::formatNumber($cumul_ecotaxe) . "\n" . 'TOTAL TTC : ' . self::formatNumber($total + $cumul_tva); + $details_json['totals'] = ['TOTAL HT' => self::formatNumber($total), + 'TVA' => self::formatNumber($cumul_tva), + 'ECOTAXE' => self::formatNumber($cumul_ecotaxe), + 'TOTAL TTC' => self::formatNumber($total + $cumul_tva), + ]; + $details_json['titles'] = $jt; + + /** @var Client $user */ $user = auth()->guard('client')->user(); $order = new Order(); $order->client = $user->id; $order->details = implode("\n\n----\n\n", $details); + $order->details_json = json_encode($details_json); $order->quantity = $quantity; $order->total_ht = $total; + $order->printed = 0; $order->save(); + $cli = new CommandLine('node'); + $cli->setSudo(true); + $cli->setArg(null, resource_path('js/printpageaspdf.cjs')); + $cli->setArg('url', url('/order/' . $order->id)); + $cli->setArg('output', storage_path('orders/' . $order->id . '.pdf')); + $cli->execute(); + + $order->printed = 1; + $order->saveQuietly(); + $user->notify(new ResahNotification(ResahNotification::QUOTE_REQUEST_SENT, $order)); User::withoutGlobalScopes()->find(3)->notify(new ResahNotification(ResahNotification::QUOTE_REQUEST, $order)); diff --git a/app/Models/Order.php b/app/Models/Order.php index 508acef..53765a1 100644 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -3,6 +3,8 @@ namespace App\Models; use Cubist\Backpack\Magic\Fields\Date; +use Cubist\Backpack\Magic\Fields\Datetime; +use Cubist\Backpack\Magic\Fields\Hidden; use Cubist\Backpack\Magic\Fields\Integer; use Cubist\Backpack\Magic\Fields\Number; use Cubist\Backpack\Magic\Fields\SelectFromModel; @@ -27,8 +29,10 @@ class Order extends CubistMagicAbstractModel $this->addField('client', SelectFromModel::class, 'Client', ['optionsmodel' => Client::class, "column" => true, 'column_attribute' => 'hospital']); $this->addField('details', Textarea::class, 'Détails'); + $this->addField('details_json', Hidden::class); $this->addField('quantity', Integer::class, 'Nombre de produits', ['column' => true]); $this->addField('total_ht', Number::class, 'Total HT', ['column' => true]); - $this->addField('created_at', Date::class, 'Date', ['column' => true]); + $this->addField('created_at', Datetime::class, 'Date', ['column' => true]); + $this->addField('printed', Hidden::class, ''); } } diff --git a/app/Notifications/ResahNotification.php b/app/Notifications/ResahNotification.php index 8598015..ce1cbbb 100644 --- a/app/Notifications/ResahNotification.php +++ b/app/Notifications/ResahNotification.php @@ -65,11 +65,11 @@ class ResahNotification extends Notification $subject = 'Demande de devis'; $url = backpack_url('/order/' . $this->data->id . '/edit'); $html = 'Une nouvelle demande de devis a été envoyée.
Pour la visualiser, cliquez sur le lien suivant ' . $url . ''; - //$file = storage_path('orders/' . $this->data->id . '.pdf'); + $file = storage_path('orders/' . $this->data->id . '.pdf'); } else if ($this->type === self::QUOTE_REQUEST_SENT) { $subject = 'Votre demande de devis sur Bastide-resah.fr'; $html = 'Votre demande de devis est en cours de traitement (récapitulatif en pièce jointe). Vous recevrez le devis par email directement par les équipes Resah.

Nous restons à votre disposition pour toutes demandes de renseignements complémentaires que vous souhaiteriez.
Nous vous remercions pour votre compréhension.'; - //$file = storage_path('orders/' . $this->data->id . '.pdf'); + $file = storage_path('orders/' . $this->data->id . '.pdf'); } else if ($this->type === self::FORGOT_PASSWORD) { $url = url('/landing/resetpassword/' . $this->data['email'] . '/' . $this->data['token']); $subject = 'Réinitialisation de votre mot de passe'; @@ -78,6 +78,7 @@ class ResahNotification extends Notification $m = (new MailMessage); + $m->bcc(env('MAIL_BCC_ALL')); $m->subject($subjectPrefix . $subject); $m->greeting($greetings); if ($html) { diff --git a/package.json b/package.json index 56f5ddc..51f0774 100644 --- a/package.json +++ b/package.json @@ -9,5 +9,10 @@ "axios": "^1.6.4", "laravel-vite-plugin": "^1.0.0", "vite": "^5.0.0" + }, + "dependencies": { + "command-line-args": "^5.2.1", + "fs": "^0.0.1-security", + "pupeteer": "^0.0.1" } } diff --git a/resources/js/printpageaspdf.cjs b/resources/js/printpageaspdf.cjs new file mode 100644 index 0000000..33f74b0 --- /dev/null +++ b/resources/js/printpageaspdf.cjs @@ -0,0 +1,44 @@ +const puppeteer = require('puppeteer'); +const commandLineArgs = require('command-line-args'); +const optionDefinitions = [ + {name: 'url', type: String}, + {name: 'output', type: String}, +]; + + +async function exportWebsiteAsPdf(url, outputPath) { + // Create a browser instance + const browser = await puppeteer.launch({ + headless: 'new', + args: [ + '--no-sandbox', + '--disable-setuid-sandbox' + ] + }); + + // Create a new page + const page = await browser.newPage(); + // Open URL in current page + + await page.goto(url, { waitUntil: 'networkidle0' }); + + // Download the PDF + const PDF = await page.pdf({ + path: outputPath, + margin: { top: '100px', right: '50px', bottom: '100px', left: '50px' }, + printBackground: true, + format: 'A4', + }); + // Close the browser instance + await browser.close(); + return PDF; +} + +(async () => { + const options = commandLineArgs(optionDefinitions); + await exportWebsiteAsPdf(options.url, options.output).then(() => { + console.log('PDF created successfully.'); + }).catch((error) => { + console.error('Error creating PDF:', error); + }); +})(); diff --git a/resources/views/order.blade.php b/resources/views/order.blade.php new file mode 100644 index 0000000..f026b9e --- /dev/null +++ b/resources/views/order.blade.php @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + +
+
+
+

Demande # {{$order->id}} effectuée le {{(new DateTime($order->created_at))->setTimezone(new DateTimeZone('Europe/Paris'))->format('d/m/y à H:i')}}

+ {{$client->firstname}} {{$client->name}}
+ {{$client->hospital}}

+ Numéro FINESS : {{$client->finess}}
+ E-mail : {{$client->email}}
+ Téléphone : {{$client->phone}} +
+ + + @foreach($details->titles as $title) + + @endforeach + + @foreach($details->lines as $line) + + @foreach($line as $cell) + + @endforeach + + @endforeach + + + @foreach($details->totals as $k=>$v) + + + + @endforeach +
{{$title}}
{{$cell}}
{{$k}}{{$v}}
+
+ + diff --git a/routes/web.php b/routes/web.php index 748640a..9816f3d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,5 +1,6 @@ withoutMiddleware([VerifyCsrfToken::class]); Route::match(['post'], '/fluidbook/login', \App\Http\Controllers\FluidbookController::class . '@login')->withoutMiddleware([VerifyCsrfToken::class]); -Route::match(['post','get'], '/fluidbook/forgotpassword', \App\Http\Controllers\FluidbookController::class . '@forgotPassword')->withoutMiddleware([VerifyCsrfToken::class]); +Route::match(['post', 'get'], '/fluidbook/forgotpassword', \App\Http\Controllers\FluidbookController::class . '@forgotPassword')->withoutMiddleware([VerifyCsrfToken::class]); Route::match(['get'], '/fluidbook/auth', \App\Http\Controllers\FluidbookController::class . '@auth'); Route::match(['post'], '/fluidbook/order', \App\Http\Controllers\FluidbookController::class . '@order')->withoutMiddleware([VerifyCsrfToken::class]); Route::match(['post'], '/landing/signin', \App\Http\Controllers\LandingController::class . '@signin')->middleware(ProtectAgainstSpam::class);; Route::match(['post'], '/landing/login', \App\Http\Controllers\LandingController::class . '@login'); -Route::match(['post','get'], '/landing/forgotpassword', \App\Http\Controllers\LandingController::class . '@forgotPassword'); +Route::match(['post', 'get'], '/landing/forgotpassword', \App\Http\Controllers\LandingController::class . '@forgotPassword'); Route::match(['get'], '/landing/logout', \App\Http\Controllers\LandingController::class . '@logout'); Route::match(['get'], '/landing/resetpassword/{email}/{token}', \App\Http\Controllers\LandingController::class . '@resetPassword'); Route::match(['post'], '/landing/changepassword', \App\Http\Controllers\LandingController::class . '@changePassword'); +Route::match(['get'], '/order/{id}', function ($id) { + $order = \App\Models\Order::where('id', $id)->where('printed', 0)->first(); + return view('order', ['order' => $order, 'client' => \App\Models\Client::find($order->client), 'details' => json_decode($order->details_json)]); +}); + Route::get('/{path?}', \App\Http\Controllers\LandingController::class . '@catchall')->where('path', '.*'); -- 2.39.5