Files
api/services/nginx/app/objects/order_bookings_o.php
T
Jeppe Bundgaard a519478788 Implement wash certificate handling for bookings and order processing
- Added functionality to create and attach wash certificates during booking completion.
- Enhanced `completeBooking` to generate and associate wash certificates with orders based on booking items.
- Updated safety seal parameter handling in relevant methods.
- Integrated wash certificate PDF generation with customer branding and order attachment logic.
- Improved file retrieval logic in the `file_server` to handle missing certificates via alternative store lookup.
2025-11-10 15:45:24 +01:00

356 lines
13 KiB
PHP

<?php
namespace objects;
use attachments\helpers\attachment_content;
use classes\db;
use classes\email;
use classes\gatewayapi;
use classes\object_property;
use classes\pdf_generator;
use classes\slack;
use Exception;
use traits\db_object_t;
class order_bookings_o extends db
{
use db_object_t;
public object_property $customer_number;
public object_property $department;
public object_property $reg_1;
public object_property $reg_2;
public object_property $reg_3;
public object_property $datetime;
public object_property $note;
public object_property $reference;
public object_property $po;
public object_property $pickup;
public object_property $items;
public object_property $order_id;
public object_property $created_at;
public object_property $updated_at;
public object_property $deleted_at;
public function structure(): void
{
$this->setTable('order_bookings');
}
/**
* Add an object
* @param array $data The additional data of the object (e.g. ["key" => "value"])
* @return void
* @throws Exception If the object was not created successfully
*/
public function add(
array $data,
): void
{
global /** @var db $db */
$db;
// Add the object to the database
$new_id = self::add_object($data);
self::select($new_id);
$this->onAfterNewBooking();
}
/**
* @throws Exception
*/
public function onAfterNewBooking(): void
{
self::requireSelected();
// TODO: Send department notification (Slack, SMS)
}
/**
* Notify the department about a new booking
* @throws Exception If the object is not selected
* @throws ClientExceptionInterface If the request to the API fails
*/
public function notifyNewBooking(): void
{
self::requireSelected();
// Get the department from the booking
$department = (new departments_o())->select((int)$this->department->value());
if (!$department->exists()) {
throw new Exception('Department not found');
}
// Get the branding from the department
$branding = (new branding_o())->select((int)$department->branding->value());
if (!$branding->exists()) {
throw new Exception('Branding not found');
}
// Get the customer from the booking
$customer = (new users_o())->getUserByCustomerNumber((int)$this->customer_number->value());
$department_array = $department->asArray();
$customer_array = $customer->asArray();
// Define delivery methods
$deliverSlack = (boolean)!empty($department->slack_webhook->value());
$deliverSMS = true;
// Check if the department has a slack webhook
if ($deliverSlack) {
// Construct email
$message = "*" . $branding->name->value() . "*\n";
$message .= "_New Booking Created_\n\n";
$message .= "*Customer:* " . $customer->getCustomerName($customer_array['customer_number']) . " (`" . $customer_array['customer_number'] . "`)\n";
// Send a notification to the department
$slack = new slack();
$slack->send_department_booking_notification($department->id, $message);
}
if ($deliverSMS) {
// Send an SMS notification to the department
$gatewayapi = new gatewayapi();
try {
$gatewayapi->send(
[
// Add all the phone numbers from the department
...$department->notificationSmsPhoneNumbers()
],
'New booking from ' . $customer->getCustomerName($customer_array['customer_number']) . ' (' . $this->id . ')',
);
} catch (Exception $e) {
// Log the error
$logs = new logs_o();
$logs->add('gatewayapi', 0, 3, 0, 'SEND_DEPARTMENT_BOOKING_NOTIFICATION', $e->getMessage());
}
}
}
public function getObjectProperties(): void
{
$this->customer_number = new object_property($this->table, $this->id, 'customer_number', 'int', false);
$this->department = new object_property($this->table, $this->id, 'department', 'int', false);
$this->reg_1 = new object_property($this->table, $this->id, 'reg_1', 'string', false);
$this->reg_2 = new object_property($this->table, $this->id, 'reg_2', 'string', false);
$this->reg_3 = new object_property($this->table, $this->id, 'reg_3', 'string', false);
$this->datetime = new object_property($this->table, $this->id, 'datetime', 'timestamp', false);
$this->note = new object_property($this->table, $this->id, 'note', 'string', false);
$this->reference = new object_property($this->table, $this->id, 'reference', 'string', false);
$this->po = new object_property($this->table, $this->id, 'po', 'string', false);
$this->pickup = new object_property($this->table, $this->id, 'pickup', 'bool', false);
$this->items = new object_property($this->table, $this->id, 'items', 'json', false);
$this->order_id = new object_property($this->table, $this->id, 'order_id', 'int', false);
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp', false);
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'timestamp', false);
$this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false);
}
public function objectChanged(): void
{
//TODO: Add cache invalidation
}
public function asArray(): array
{
$order_id = $this->order_id->value();
return [
'id' => (int)$this->id,
'customer_number' => (int)$this->customer_number->value(),
'department' => (int)$this->department->value(),
'reg_1' => $this->reg_1->value(),
'reg_2' => $this->reg_2->value(),
'reg_3' => $this->reg_3->value(),
'datetime' => $this->datetime->value(),
'note' => $this->note->value(),
'reference' => $this->reference->value(),
'po' => $this->po->value(),
'pickup' => (bool)$this->pickup->value(),
'items' => $this->items->value(),
'order_id' => $order_id === null ? null : (int)$order_id,
'created_at' => $this->created_at->value(),
'updated_at' => $this->updated_at->value(),
];
}
/**
* @throws Exception
*/
public function completeBooking(int $user_id, string $safety_seal = null): void
{
self::requireSelected();
if (!$this->order_id->value()) {
// Create order, if not already created
self::createOrderBy($user_id);
// Add order items, re-calculate the prices to be customer-specific
self::createOrderItemsBy($user_id);
}
// TODO: Create wash certificate
if (self::containsWashCertificateItem()) self::attachWashCertificate($user_id, $safety_seal);
// TODO: Send wash certificate
}
/**
* @throws Exception
*/
public function createOrderBy(int $user_id): void
{
self::requireSelected();
// Create order
$orders = new orders_o();
$orders->createOrderFromBooking($this->id, $user_id);
// Set the order id in the booking
$this->order_id->set((int)$orders->id);
self::objectChanged();
}
/**
* @throws Exception
*/
public function createOrderItemsBy(int $user_id): void
{
self::requireSelected();
// Get the order
$order = self::getOrder();
$order->requireSelected();
// Add order items
foreach ($this->items->value() as $item) {
if (!isset($item['id']) || !isset($item['quantity'])) {
continue;
}
$orderItems = new order_items_o();
$orderItems->addItemToOrder(
(int)$order->id,
(int)$item['id'],
(int)$user_id,
(int)$item['quantity'],
);
}
}
/**
* @throws Exception
*/
private function containsWashCertificateItem(): bool
{
self::requireSelected();
return self::containsProductId(41);
}
/**
* @throws Exception
*/
private function containsProductId(int $productId): bool
{
self::requireSelected();
$items = $this->items->value();
foreach ($items as $item) {
if (isset($item['id']) && (int)$item['id'] == $productId) {
return true;
}
}
return false;
}
/**
* @throws Exception
*/
public function getOrder(): orders_o
{
self::requireSelected();
if (!$this->order_id->value()) {
throw new \Exception('Order not found for this booking');
}
$order = new orders_o();
$order->select((int)$this->order_id->value());
if (!$order->exists()) {
throw new \Exception('Order not found for this booking');
}
return $order;
}
/**
* @throws Exception
*/
private function attachWashCertificate(int $user_id, string $safety_seal = null): void
{
self::requireSelected();
// Check if the order already has a wash certificate attached
if (self::getOrder()->hasWashCertificateAttached()) {
return;
}
// Get the operator name
$operator = (new users_o())->select($user_id);
if (!$operator->exists()) {
throw new Exception('Operator not found');
}
// Generate wash certificate
self::generateWashCertificate($safety_seal, $operator->display_name->value());
}
/**
* Generate a wash certificate
* @param int|null $safety_seal The safety seal number
* @param string|null $operator The operator name
* @throws Exception If the object is not selected
* @throws Exception If the booking already has a wash certificate
*/
public function generateWashCertificate(int|null $safety_seal = null, string|null $operator = null): void
{
self::requireSelected();
// Generate the wash certificate
// Get the department from the booking
$department = (new departments_o())->select((int)$this->department->value());
if (!$department->exists()) {
throw new Exception('Department not found');
}
// Get the branding from the department
$branding = (new branding_o())->select((int)$department->branding->value());
if (!$branding->exists()) {
throw new Exception('Branding not found');
}
// Get the customer from the booking
$customer = (new users_o())->getUserByCustomerNumber((int)$this->customer_number->value());
$department_array = $department->asArray();
$customer_array = $customer->asArray();
$booking_array = self::asArray();
$department_array['branding'] = $branding->asArray();
//print_r($department_array);
//print_r($customer_array);
//print_r($booking_array);
$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,
'seal_number' => ($safety_seal ?? null),
'reg_1' => $booking_array['reg_1'],
'reg_2' => $booking_array['reg_2'],
'date' => date('d-m-Y'),
'time' => date('H:i'),
'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' => 'BOOK_WASH'
])
->getHtml()
);
$pdf_path = $pdf_generator->generate_pdf();
// Set the PDF in the order
$this->getOrder()->addAttachment(new attachment_content((object)['document' => $pdf_path, 'other' => 'wash_certificate']));
}
}