From d3d3657dfdad9fb1fa159f33b4942cb555226cc5 Mon Sep 17 00:00:00 2001 From: Jeppe Bundgaard Date: Wed, 3 Dec 2025 08:45:13 +0100 Subject: [PATCH] Generate and attach wash certificate to orders - Added `generateWashCertificate` method to `orders_o` for wash certificate PDF generation and attachment. - Introduced `OTHER_TYPE_WASH_CERTIFICATE` constant in `attachment_content` for new attachment type. - Added `/order/wash-certificate` route to provide wash certificate generation functionality via API. --- .../helpers/attachment_content.php | 1 + services/nginx/app/objects/orders_o.php | 83 +++++++++++++++++++ services/nginx/app/routes/orderRoute.php | 57 +++++++++++++ 3 files changed, 141 insertions(+) diff --git a/services/nginx/app/modules/attachments/helpers/attachment_content.php b/services/nginx/app/modules/attachments/helpers/attachment_content.php index 761d641b..b395fd8d 100644 --- a/services/nginx/app/modules/attachments/helpers/attachment_content.php +++ b/services/nginx/app/modules/attachments/helpers/attachment_content.php @@ -4,6 +4,7 @@ namespace attachments\helpers; class attachment_content { + const OTHER_TYPE_WASH_CERTIFICATE = 'WASH_CERTIFICATE'; public ?string $image; // Used to store the attachment object name, in the attachment store. public ?string $document; // Used to store the attachment object name, in the attachment store. public ?attachment_relation $relation; // Used to store the attachment relation object. diff --git a/services/nginx/app/objects/orders_o.php b/services/nginx/app/objects/orders_o.php index eaa44ee4..f837a61a 100644 --- a/services/nginx/app/objects/orders_o.php +++ b/services/nginx/app/objects/orders_o.php @@ -2,10 +2,13 @@ namespace objects; +use attachments\helpers\attachment_content; use classes\db; +use classes\pdf_generator; use classes\motorapi; use classes\object_property; use classes\response; +use DateTime; use Exception; use helpers\xlvask_usage_log; use helpers\xlvask_wash_item; @@ -1376,6 +1379,86 @@ class orders_o extends db return false; // No wash certificate product found in the order items } + /** + * Generate and attach a wash certificate directly on an order (without a booking) + * @param int|null $safety_seal Optional safety seal number + * @param string|null $operator Optional operator/employee name who carried out the wash + * @param string|DateTime|null $date Optional date of the wash (defaults to current date) + * @throws Exception If the order is not selected or required related objects are missing + */ + public function generateWashCertificate(int|null $safety_seal = null, string|null $operator = null, $date = null): void + { + self::requireSelected(); + // Avoid generating duplicate certificates + if ($this->hasWashCertificateAttached()) { + return; + } + // Get the department for the order + $department = (new departments_o())->select((int)$this->department_id->value()); + if (!$department->exists()) { + throw new Exception('Department not found'); + } + // Get branding for the department + $branding = (new branding_o())->select((int)$department->branding->value()); + if (!$branding->exists()) { + throw new Exception('Branding not found'); + } + // Get the customer for the order + $customer = (new users_o())->getUserByCustomerNumber((int)$this->customer_id->value()); + $department_array = $department->asArray(); + $department_array['branding'] = $branding->asArray(); + $customer_array = $customer->asArray(); + $order_array = $this->asArray(); + // Format the date as 17:35 02-12-2025 + $date = ($date instanceof DateTime ? $date : ($date !== null ? new DateTime($date) : new DateTime())); + $date_formatted = ($date instanceof DateTime ? $date->format('d-m-Y') : date('d-m-Y')); + $time_formatted = ($date instanceof DateTime ? $date->format('H:i') : date('H:i')); + + // Generate PDF + $pdf_generator = new pdf_generator(); + $pdf_generator->add_html( + $pdf_generator->templates->getTemplate('wash_certificate') + ->setCompany([ + 'name' => 'Truck Wash', + 'address' => 'Letland Allé 2', + 'zip' => 2630, + 'city' => 'Taastrup', + 'phone_prefix' => 45, + 'phone' => 43717886, + 'email' => 'cph@truckwash.dk', + 'website' => 'www.truckwash.dk', + 'images' => [ + 'logo' => '/truckwash-banner-png.png', + 'banner' => '/truckwash-banner-png.png', + 'signature' => '/truckwash-underskrift.png', + ], + ]) + ->addData([ + 'booking_number' => $this->id, // Used as document number on the template + 'seal_number' => ($safety_seal ?? null), + 'reg_1' => $order_array['reg_1'], + 'reg_2' => $order_array['reg_2'], + 'date' => $date_formatted, + 'time' => $time_formatted, + 'carried_out_by' => ($operator ?? null), + 'department_id' => $department->id, + 'department_name' => $department_array['branding']['name'] ?: $department_array['name'], + 'department_address' => $department_array['branding']['address'] ?: $department_array['description'], + 'customer_name' => $customer->getCustomerName($customer_array['customer_number']), + 'customer_address' => $customer->getCustomerEcocomicData($customer_array['customer_number'])->economic_customer->address ?: '-', + 'wash_type' => 'ORDER_WASH' + ]) + ->getHtml() + ); + $pdf_path = $pdf_generator->generate_pdf(); + + // Attach PDF to the order + $this->addAttachment(new attachment_content((object)[ + 'document' => $pdf_path, + 'other' => attachment_content::OTHER_TYPE_WASH_CERTIFICATE, + ])); + } + /** * @throws Exception */ diff --git a/services/nginx/app/routes/orderRoute.php b/services/nginx/app/routes/orderRoute.php index ca284172..e5e90e79 100644 --- a/services/nginx/app/routes/orderRoute.php +++ b/services/nginx/app/routes/orderRoute.php @@ -54,6 +54,63 @@ class orderRoute ] ); + $this->post('/order/wash-certificate', function () { + // Require the user to be logged in and have permission + global /** @var response $response */ + $response; + $this->requirePermission('complete_bookings'); + // Get the user + $user = (new authentication())->get_user(); + if (!$user) { + (new logs_o())->add('orders', 'global', 1, 0, 'GENERATE_ORDER_WASH_CERTIFICATE', 'No user found, or invalid session'); + $response->error('Invalid session', 400); + } + + // Parse request body + $data = json_decode(file_get_contents('php://input'), true) ?? []; + if (!isset($data['id']) || !(int)$data['id']) { + $response->error('Order id is required', 400); + } + + $orders_o = new orders_o(); + $orders_o->select((int)$data['id']); + if (!$orders_o->exists()) { + $response->error('Order not found', 400); + } + + // Check department access + self::requireDepartmentAccess($orders_o->department_id->value()); + + // Collect optional params + $safety_seal = null; + if (isset($data['safety_seal']) && $data['safety_seal'] !== '') { + $safety_seal = (int)$data['safety_seal']; + } + $operator = isset($data['operator']) && $data['operator'] !== '' ? (string)$data['operator'] : (string)$user->display_name->value(); + // Set the date to the creation date of the order if not provided + if (isset($data['date']) && $data['date'] !== '') { + $date = date('Y-m-d H:i:s', strtotime($data['date'])); + } else { + $date = date('Y-m-d H:i:s', strtotime($orders_o->created_at->value())); + } + // Determine if already exists + $already_exists = $orders_o->hasWashCertificateAttached(); + // Generate (no-op if already exists) + $orders_o->generateWashCertificate($safety_seal, $operator, $date); + $now_exists = $orders_o->hasWashCertificateAttached(); + + (new logs_o())->add('orders', $orders_o->department_id->value(), 1, $user->id, 'GENERATE_ORDER_WASH_CERTIFICATE', 'Wash certificate ' . ($already_exists ? 'already existed' : 'generated')); + + $response->success([ + 'order_id' => (int)$orders_o->id, + 'created' => !$already_exists && $now_exists, + 'already_existed' => $already_exists, + ]); + }, [ + 'complete_bookings' => 'Generate and attach a wash certificate PDF to an order', + 'department_access_:id' => 'Access to the department the order is in' + ]); + /** * $this->put('/order', function () { *