Files
api/services/nginx/app/objects/bookings_o.php
T
Jepp9350 7a48710fb1 Validate recipients and improve SMS notification handling
Add recipient validation to the SMS gateway API to ensure no empty recipient arrays are processed. Enhance department SMS notification logic by dynamically retrieving phone numbers and logging failures for improved error tracking. Simplify SMS delivery flag logic for better readability.
2025-04-22 15:24:04 +02:00

834 lines
35 KiB
PHP

<?php
namespace objects;
use classes\db;
use classes\email;
use classes\gatewayapi;
use classes\object_property;
use classes\pdf_generator;
use classes\response;
use classes\slack;
use classes\wash_certificate_store;
use classes\wordpress_bookings_remote;
use Exception;
use Psr\Http\Client\ClientExceptionInterface;
use traits\db_object_t;
class bookings_o extends db
{
use db_object_t;
public object_property $customer_number;
public object_property $wash_type;
public object_property $contact_email;
public object_property $reference_number;
public object_property $regNrTraekker;
public object_property $regNrTrailer;
public object_property $washCertificateEmail;
public object_property $date;
public object_property $department;
public object_property $pickup_bool;
public object_property $notes;
public object_property $washCertificateStatus;
public object_property $washCertificateUrl;
public object_property $wash_certificate_pdf;
public object_property $status;
public object_property $data;
public function structure(): void
{
$this->setTable('bookings');
}
public function objectChanged(): void
{
// Clear the cache
redis->clear_department_booking_count($this->department->value());
}
public function getCustomerBookingsPaginated(int $customer_number, int $page = 1, int $limit = 10, array $order = ['id' => 'DESC'], string $search = null, array $filters = null): array
{
global /** @var response $response */
$db, $response;
// Add the customer number to the filters
$filters['customer_number'] = $customer_number;
// List the objects with pagination
$array = $this->listObjectsWithPagination($page, $limit, $search, $filters, $order);
// Get the total number of objects
$total = $this->getTotalObjects($search, $filters);
$response->paginate($page, $limit, $total);
return $array;
}
public function getDepartmentBookingsUnfulfilledCount(int $department_id): int
{
global /** @var response $response */
$db, $response;
// Check if the count is cached
if (redis->get_department_booking_count($department_id)) {
$response->add_meta('cached', true);
return redis->get_department_booking_count($department_id);
}
$sql = "SELECT COUNT(*) as count FROM $this->table WHERE department = $department_id AND status = 'pending'";
$result = $db->query($sql);
$row = $db->fetch_assoc($result);
// Cache the count
redis->cache_department_booking_count($department_id, $row['count']);
return $row['count'];
}
public function delete(int $id): void
{
$this->id = $id;
$this->getObjectProperties();
// Set the status to cancelled
$this->status->set('cancelled');
// Remove the cache
redis->clear_department_booking_count($this->department->value());
}
public function getObjectProperties(): void
{
$this->customer_number = new object_property($this->table, $this->id, 'customer_number', 'int', true);
$this->wash_type = new object_property($this->table, $this->id, 'wash_type', 'string', true);
$this->contact_email = new object_property($this->table, $this->id, 'contact_email', 'string', true);
$this->reference_number = new object_property($this->table, $this->id, 'reference_number', 'string', true);
$this->regNrTraekker = new object_property($this->table, $this->id, 'regNrTraekker', 'string', true);
$this->regNrTrailer = new object_property($this->table, $this->id, 'regNrTrailer', 'string', true);
$this->washCertificateEmail = new object_property($this->table, $this->id, 'washCertificateEmail', 'string', true);
$this->date = new object_property($this->table, $this->id, 'date', 'string', true);
$this->department = new object_property($this->table, $this->id, 'department', 'int', true);
$this->pickup_bool = new object_property($this->table, $this->id, 'pickup_bool', 'int', true);
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', true);
$this->washCertificateStatus = new object_property($this->table, $this->id, 'washCertificateStatus', 'string', true);
$this->washCertificateUrl = new object_property($this->table, $this->id, 'washCertificateUrl', 'string', true);
$this->wash_certificate_pdf = new object_property($this->table, $this->id, 'wash_certificate_pdf', 'string', true);
$this->status = new object_property($this->table, $this->id, 'status', 'string', true);
$this->data = new object_property($this->table, $this->id, 'data', 'string', true);
}
public function parseBookings(array $listObjectsWithPaginationIfSet): array
{
// Parse the customer numbers
$bookings = $this->parseCustomerNumbers($listObjectsWithPaginationIfSet);
return $bookings;
}
public function parseCustomerNumbers(array $listObjectsWithPaginationIfSet): array
{
// Parse the customer numbers
foreach ( $listObjectsWithPaginationIfSet as $key => $value ) {
$listObjectsWithPaginationIfSet[$key]['customer_name'] = (new users_o())->getCustomerName($value['customer_number']);
}
return $listObjectsWithPaginationIfSet;
}
public function syncBookings(): array
{
global /** @var response $response */
$db, $response;
$wordpress_bookings_remote = new wordpress_bookings_remote();
// Get all the bookings from the remote API
$bookings = $wordpress_bookings_remote->get_all_bookings()['data']['all_wash_bookings'];
// Parse the bookings
$parsed_bookings = $wordpress_bookings_remote->parse_bookings($bookings);
// Remove the cancelled bookings from the $parsed_bookings array
$cancelled_bookings = $this->getAllCancelledOrCompletedBookings();
foreach ( $cancelled_bookings as $cancelled_booking ) {
// Remove the cancelled booking from the parsed bookings
foreach ( $parsed_bookings as $key => $parsed_booking ) {
if ((int)$parsed_booking['id'] === (int)$cancelled_booking['id']) {
unset($parsed_bookings[$key]);
}
}
}
// Sync the bookings
foreach ( $parsed_bookings as $booking ) {
// Add or update the booking
$this->addOrUpdate(
$booking['id'],
$booking['customer_number'],
$booking['wash_type'],
$booking['contact_email'],
$booking['reference_number'],
$booking['regNrTraekker'],
$booking['regNrTrailer'],
$booking['washCertificateEmail'],
$booking['date'],
$booking['department'],
$booking['pickup_bool'] ? 1 : 0,
$booking['notes'],
$booking['washCertificateStatus'],
$booking['washCertificateUrl'],
$booking['status']
);
}
return [
"bookings" => count($bookings),
"cancelled" => count($cancelled_bookings),
"parsed" => count($parsed_bookings),
];
}
public function getAllCancelledOrCompletedBookings(): array
{
global $db;
// Get all the cancelled bookings
$sql = "SELECT * FROM $this->table WHERE status = 'cancelled' OR status = 'completed'";
$result = $db->query($sql);
return $db->fetch_all($result);
}
public function addOrUpdate(int $id, $customer_number, $wash_type, $contact_email, $reference_number, $regNrTraekker, $regNrTrailer, $washCertificateEmail, $date, $department, $pickup_bool, $notes, $washCertificateStatus, $washCertificateUrl, $status): void
{
global $db;
// Avoid SQL injection
$wash_type = $db->escape_string($wash_type);
$contact_email = $db->escape_string($contact_email);
$reference_number = $db->escape_string($reference_number);
$regNrTraekker = $db->escape_string($regNrTraekker);
$regNrTrailer = $db->escape_string($regNrTrailer);
$washCertificateEmail = $db->escape_string($washCertificateEmail);
$date = $db->escape_string($date);
// Parse the department name to id
$department = $this->getDepartmentIdByLegacyName($department);
$notes = $db->escape_string($notes);
$washCertificateStatus = $db->escape_string($washCertificateStatus);
$washCertificateUrl = $db->escape_string($washCertificateUrl);
$status = $db->escape_string($status);
// Check if the entry already exists
$sql = "SELECT * FROM $this->table WHERE id = $id";
$result = $db->query($sql);
if ($db->num_rows($result) === 0) {
// Send a department webhook if the booking is new
$slack = new slack();
try {
$slack->send_department_booking_notification($department, $slack->format_new_booking(
$id,
$customer_number,
$wash_type,
$contact_email,
$reference_number,
$regNrTraekker,
$regNrTrailer,
$washCertificateEmail,
$date,
$department,
$pickup_bool,
$notes,
$washCertificateStatus,
$washCertificateUrl,
$status
));
} catch (Exception $e) {
// Log the error
$logs = new logs_o();
$logs->add('slack', 0, 3, 0, 'SEND_DEPARTMENT_BOOKING_NOTIFICATION', $e->getMessage());
}
}
// Create a new record in the database ( Replace the code, if an entry already exists )
$sql = "INSERT INTO $this->table (id, customer_number, wash_type, contact_email, reference_number, regNrTraekker, regNrTrailer, washCertificateEmail, date, department, pickup_bool, notes, washCertificateStatus, washCertificateUrl, status) VALUES ($id, $customer_number, '$wash_type', '$contact_email', '$reference_number', '$regNrTraekker', '$regNrTrailer', '$washCertificateEmail', '$date', '$department', $pickup_bool, '$notes', '$washCertificateStatus', '$washCertificateUrl', '$status') ON DUPLICATE KEY UPDATE customer_number = $customer_number, wash_type = '$wash_type', contact_email = '$contact_email', reference_number = '$reference_number', regNrTraekker = '$regNrTraekker', regNrTrailer = '$regNrTrailer', washCertificateEmail = '$washCertificateEmail', date = '$date', department = '$department', pickup_bool = $pickup_bool, notes = '$notes', washCertificateStatus = '$washCertificateStatus', washCertificateUrl = '$washCertificateUrl', status = '$status'";
$db->query($sql);
// Clear the cache
redis->clear_department_booking_count($department);
}
public function getDepartmentIdByLegacyName(string $departmentName): int
{
// Get the department id by the legacy name
$departmentLegacyNames = [
'køge' => 4,
'taastrup' => 2,
'aarhusc' => 5,
'roskilde' => 6,
'hvidovre' => 1,
'glostrup' => 3,
'taulov' => 7,
];
return $departmentLegacyNames[strtolower($departmentName)] ?? 0;
}
/**
* Add a new booking
* @throws Exception If the object could not be created
*/
public function add(int $customer_number, string $wash_type, string $contact_email, string $reference_number, string $regNrTraekker, string $regNrTrailer, string $washCertificateEmail, string $date, int $department, int $pickup_bool, string $notes, string $washCertificateStatus, string $washCertificateUrl, string $status, array $data = []): void
{
global $db;
$tmp_id = self::add_object([
'customer_number' => (int)$db->escape_string($customer_number),
'wash_type' => (string)$db->escape_string($wash_type),
'contact_email' => (string)$db->escape_string($contact_email),
'reference_number' => (string)$db->escape_string($reference_number),
'regNrTraekker' => (string)$db->escape_string($regNrTraekker),
'regNrTrailer' => (string)$db->escape_string($regNrTrailer),
'washCertificateEmail' => (string)$washCertificateEmail ? $db->escape_string($washCertificateEmail) : '',
'date' => (string)$db->escape_string($date),
'department' => (int)$db->escape_string($department),
'pickup_bool' => (int)$db->escape_string($pickup_bool),
'notes' => (string)$db->escape_string($notes),
'washCertificateStatus' => (string)$db->escape_string($washCertificateStatus),
'washCertificateUrl' => (string)$db->escape_string($washCertificateUrl),
'status' => (string)$db->escape_string($status),
'data' => json_encode($data),
]);
// Check if the booking was created successfully
self::select((int)$tmp_id);
self::requireSelected();
self::notifyNewBooking();
}
/**
* 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;
$deliverEmail = (
!empty($this->contact_email->value()) &&
!$deliverSlack // Only send email if slack is not available
);
// Check if the department has a slack webhook
if ($deliverSlack) {
// Send a notification to the department
$slack = new slack();
$slack->send_department_booking_notification($department->id, $slack->format_new_booking(
$this->id,
$customer_array['customer_number'],
self::formatWashTypeFromServices(json_decode($this->data->value(), true)),
$this->contact_email->value(),
$this->reference_number->value() ?: '-',
$this->regNrTraekker->value(),
$this->regNrTrailer->value(),
$this->washCertificateEmail->value(),
$this->date->value(),
$department->id,
(bool)$this->pickup_bool->value(),
$this->notes->value(),
$this->washCertificateStatus->value(),
$this->washCertificateUrl->value(),
$this->status->value()
));
}
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());
}
}
if ($deliverEmail) {
// Send an email notification to the department
$email = new email();
$email->sendBookingNotification($this->id);
}
}
/**
* Convert the object to an array
* @options include_customer_name Include the customer name
* @options include_department_name Include the department name
* @options include_parsed_services Include the parsed services
* @param array $options [@options] The options to include in the array
* @return array The object as an array
* @throws Exception If the object is not selected
*/
public function asArray(array $options = []): array
{
self::requireSelected();
$options = array_merge([
'include_customer_name' => false,
'include_department_name' => false,
'include_parsed_services' => false,
], $options);
$tmp_array = [
'id' => (int)$this->id,
'customer_number' => (int)$this->customer_number->value(),
'wash_type' => $this->wash_type->value(),
'contact_email' => $this->contact_email->value(),
'reference_number' => $this->reference_number->value(),
'regNrTraekker' => $this->regNrTraekker->value(),
'regNrTrailer' => $this->regNrTrailer->value(),
'washCertificateEmail' => $this->washCertificateEmail->value(),
'date' => $this->date->value(),
'department' => (int)$this->department->value(),
'pickup_bool' => $this->pickup_bool->value() ? true : false,
'notes' => $this->notes->value(),
'washCertificateStatus' => $this->washCertificateStatus->value(),
'washCertificateUrl' => $this->washCertificateUrl->value(),
'wash_certificate_pdf' => $this->wash_certificate_pdf->value(),
'status' => $this->status->value(),
'data' => $this->data->value() ? json_decode($this->data->value(), true) : null,
];
// Include the customer name
if ($options['include_customer_name']) {
$customer = (new users_o())->getUserByCustomerNumber((int)$this->customer_number->value());
$tmp_array['customer_name'] = $customer->getCustomerName($this->customer_number->value());
}
// Include the department name
if ($options['include_department_name']) {
$department = (new departments_o())->select((int)$this->department->value());
if ($department->exists()) {
$tmp_array['department_name'] = $department->name->value();
}
}
// Include the parsed services
if ($options['include_parsed_services']) {
// Format the wash type from the services array as followed:
// 1 => 'Udvendig sættevognstræk ( Trækker / trailer )',
$tmp_array['parsed_services']['string'] = $this->formatWashTypeFromServices(
$this->data->value() ? json_decode($this->data->value(), true) : []
);
$tmp_array['parsed_services']['array'] = $this->data->value() ? json_decode($this->data->value(), true) : [];
}
return $tmp_array;
}
/**
* Format the wash type from the services array
* @param array $services The services array
* @return string The formatted wash type
*/
private function formatWashTypeFromServices(array $services): string
{
// Format the wash type from the services array
$wash_services = self::getWashServicesString(self::getWashServices());
$string = '';
foreach ( $services as $service ) {
if (isset($wash_services[$service])) {
$string .= $wash_services[$service] . ', ';
}
}
// Remove the last comma and space
return rtrim($string, ', ');
}
private static function getWashServicesString(array $services): array
{
// Format the wash services as a string (product1, product2, product3)
$string = [];
foreach ( $services as $key => $service ) {
if (isset($service['overrides']['name'])) {
$string[$key] = $service['overrides']['name'];
} else {
$string[$key] = $service['name'];
}
}
return $string;
}
/**
* @throws Exception
*/
private function getWashServices(): array
{
// Define the product function
// Get the wash services from the database
return [
// Exterior wash "Sættevognstræk"
1 => self::defineProduct(1, 3, [
'name' => 'Udvendig sættevognstræk ( Trækker / trailer )',
'description' => 'Udvendig vask af sættevognstræk',
]),
// Exterior trailer wash "Udvendig trailer vask"
2 => self::defineProduct(2, 2, [
'name' => 'Udvendig trailer vask',
'description' => 'Udvendig vask af trailer',
]),
3 => self::defineProduct(3, 10, [
'name' => 'Indvendig trailer vask',
'description' => 'Indvendig vask af trailer',
]),
4 => self::defineProduct(41, 41),
];
}
/**
* Define a product for the booking
* @param int $id
* @param int $product_id
* @param array $overrides
* @param array $options
* @param array $addons
* @return array
* @throws Exception
* @see getWashServices()
* @see getWashServicesString()
*/
private function defineProduct(int $id, int $product_id, array $overrides = [], array $options = [], array $addons = []): array
{
// Check if the product exists
if (!empty($product_id)) {
$product_obj = (new products_o())->select((int)$product_id);
if ($product_obj->exists()) {
$default_options = $product_obj->asArray();
}
}
// Return the product
return [
'id' => (int)$id,
'product_id' => (int)$product_id,
'addons' => [...$addons],
...($default_options ?? []),
'overrides' => [
'name' => null,
'quantity' => null,
'description' => null,
'notes' => null,
'reference' => null,
...$overrides,
],
...$options,
];
}
public function checkUnfulfilledBookings(): void
{
$unfulfilled_bookings = [];
function addUnfulfilledBooking(int $department, array $arr): array
{
// Check if the department has a count in the unfulfilled bookings array
if (!isset($arr[$department])) {
$arr[$department] = 0;
}
// Add the unfulfilled booking to the department count
$arr[$department]++;
return $arr;
}
global $db;
// Get all the unfulfilled bookings
$bookings = $this->listObjectsWithPagination(1, 100000, null, ['status' => 'pending']);
// Check if the booking has been fulfilled
foreach ( $bookings as $booking ) {
if ($this->isCancelled($booking['id'])) {
echo "Booking with ID $booking[id] has been cancelled\n";
continue;
}
// Check if the booking is scheduled for the future
if (strtotime($booking['date']) > time()) {
continue;
}
// Check if the booking has been fulfilled
$fulfilled = $this->checkBookingFulfilled($booking['id']);
if (!$fulfilled) {
$unfulfilled_bookings = addUnfulfilledBooking($booking['department'], $unfulfilled_bookings);
echo "Booking with ID $booking[id] has not been fulfilled\n";
}
}
// Send a department webhook if the booking has not been fulfilled
foreach ( $unfulfilled_bookings as $department => $count ) {
$slack = new slack();
try {
$slack->send_department_booking_notification($department, 'There are ' . $count . ' unfulfilled bookings');
} catch (Exception $e) {
// Log the error
$logs = new logs_o();
$logs->add('slack', 0, 3, 0, 'SEND_DEPARTMENT_UNFULFILLED_BOOKINGS_NOTIFICATION', $e->getMessage());
}
}
}
private static function isCancelled(int $id): bool
{
global $db;
// Check if the booking has been cancelled
$sql = "SELECT status FROM bookings WHERE id = $id";
$result = $db->query($sql);
$row = $db->fetch_assoc($result);
return $row['status'] === 'cancelled';
}
public function checkBookingFulfilled(int $booking_id): bool
{
// Check if the booking has a wash certificate
$wash_certificate_store = new wash_certificate_store();
return $wash_certificate_store->washCertificateExists($booking_id);
}
public function completeWashWithoutWashCertificate(int $id): void
{
global $db;
// Set the status to completed
$sql = "UPDATE $this->table SET status = 'completed', washCertificateStatus = 'cancelled' WHERE id = $id";
$db->query($sql);
}
/**
* 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();
// Check if the booking already has a wash certificate
if ($this->hasWashCertificate()) {
throw new Exception('Booking already has a wash certificate');
}
// 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['regNrTraekker'],
'reg_2' => $booking_array['regNrTrailer'],
'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 ?: '-',
'type' => $booking_array['wash_type'],
])
->getHtml()
);
$pdf_path = $pdf_generator->generate_pdf();
// Set the PDF in the booking
$this->wash_certificate_pdf->set($pdf_path);
// Add the wash certificate to the bookings data (If it does not exist)
if (!self::hasServiceInBookingData(4)) {
self::addServiceToBookingData(4);
}
}
/**
* Check if the booking has a wash certificate
* @throws Exception If the object is not selected
*/
public function hasWashCertificate(): bool
{
self::requireSelected();
// Check if the booking has a wash certificate
if ($this->wash_certificate_pdf->value() !== null) {
return true;
}
return false;
}
/**
* Check if the booking data has a service
* @param int $service_id The service id
* @return bool True if the service exists, false otherwise
* @throws Exception If the object is not selected
*/
public function hasServiceInBookingData(int $service_id): bool
{
self::requireSelected();
// Get the current data
$data = $this->data->value() ? json_decode($this->data->value(), true) : [];
// Check if the service exists
return in_array($service_id, $data);
}
/**
* Add a service to the booking data
* @param int $service_id The service id
* @throws Exception If the object is not selected
*/
public function addServiceToBookingData(int $service_id): void
{
self::requireSelected();
// Get the current data
$data = $this->data->value() ? json_decode($this->data->value(), true) : [];
// Add the service to the data
$data[] = (int)$service_id;
// Set the data
$this->data->set(json_encode($data));
}
/**
* Send the wash certificate to the customer
* @throws Exception If the object is not selected
* @throws ClientExceptionInterface
*/
public function sendWashCertificateToCustomer(): void
{
self::requireSelected();
// Check if the booking has a wash certificate
if (!$this->hasWashCertificate()) {
throw new Exception('Booking does not have a wash certificate');
}
// Send the wash certificate to the customer
$email = new email();
$email->sendWashCertificateEmail(
$this->id,
(new users_o())->getCustomerName((int)$this->customer_number->value()),
$this->contact_email->value(),
$this->reference_number->value() ?: '-',
$this->wash_certificate_pdf->value(),
$this->washCertificateEmail->value(),
);
}
/**
* @throws Exception
*/
public function createTransaction(): orders_o
{
global $response;
self::requireSelected();
// Get the services from the booking
$services = $this->data->value() ? json_decode($this->data->value(), true) : [];
// Get the department from the booking
$department = (new departments_o())->select((int)$this->department->value());
if (!$department->exists()) {
throw new Exception('Department not found');
}
// Create a new order
$order = new orders_o();
$order->add(
(int)$this->customer_number->value(),
(int)$this->id,
(string)$this->reference_number->value(),
(string)$this->notes->value(),
(int)$this->department->value(),
(string)$this->regNrTraekker->value(),
(string)$this->regNrTrailer->value(),
);
// Set the orders booking id
$order->booking_id->set((int)$this->id);
// Define the relation order item ids, when applicable.
$relations = [
// This is the order item id for the primary product, the wash certificate applies to.
// This can be left null, if the product does not exist.
4 => null,
];
// Sort the services, so the primary product is first, and the services with relations are last.
$services_sorted = [];
$services_not_relation = [];
$services_relation = [];
foreach ( $services as $service ) {
// Get the service id
$service_id = (int)$service;
// Check if the service has the posibility to have a relation (null values are allowed)
if (in_array($service_id, array_keys($relations))) {
$services_relation[] = $service_id;
} else {
$services_not_relation[] = $service_id;
}
}
// Sort the services, so the primary product is first, and the services with (potential) relations are last.
$services_sorted = [
...$services_not_relation,
...$services_relation,
];
// Add the services to the order
foreach ( $services_sorted as $service_id ) {
$service = self::getWashServiceProduct((int)$service_id);
// Add the service to the order
$tmp_order_item = new order_items_o();
$tmp_order_item->addItemToOrder(
$order->id,
(int)$service->id,
(int)$response->get_user()->id,
(int)1,
$relations[(int)$service_id] ?? null,
);
// Check if the service allows for a wash certificate
if ((int)$service_id === 3) {
// Set the relation id for the wash certificate
$relations[4] = (int)$tmp_order_item->id;
}
}
// Return the order
return $order;
}
/**
* @throws Exception
*/
private function getWashServiceProduct(int $service_id): products_o
{
// Get the product id from the service id
$services = self::getWashServices();
if (!isset($services[$service_id])) {
throw new Exception('Service not found');
}
$product_id = $services[$service_id]['product_id'];
// Get the wash service product from the database
$product = (new products_o())->select($product_id);
if (!$product->exists()) {
throw new Exception('Product not found');
}
return $product;
}
}