asArray(); }, $getAddons); } /** * Convert the object to an array * @return array * @throws Exception If the object is not selected */ public function asArray(): array { self::requireSelected(); // Check if the vehicle is cached $cached = $this->getCached('asArray', $this->id); if ($cached) { return (array)$cached; } $wash_subscription = (bool)$this->wash_subscription->value(); $addons = self::transformObjectsToArray( self::getAddons() ); $available_addons = self::transformObjectsToArray( self::getAvailableAddons() ); $last_order_id = self::getLastOrderId(); $customer_number = (int)$this->customer_id->value(); $xlvask = $this->hasXLVask() ? $this->getXLVask() : null; $tmp = [ 'id' => (int)$this->id, 'user_id' => (int)(new users_o())->getUserIdFromEconomic((int)$customer_number), 'customer_id' => (int)$this->customer_id->value(), 'customer_name' => (string)(new users_o())->getCustomerName((int)$customer_number), 'type' => (int)$this->type->value(), 'reg' => (string)$this->reg->value(), 'reference' => ($this->reference->value() ? (string)$this->reference->value() : null), 'wash_subscription' => $wash_subscription, 'barred' => (bool)(new users_o())->isCustomerBarred((int)$customer_number), 'addons' => [ 'enabled' => count($addons), 'available' => count($available_addons), 'list' => $addons, ], 'last_order_id' => ($last_order_id ? (int)$last_order_id : null), 'xlvask' => ($xlvask?->toArray()), 'vehicle_types' => array_map(function ($vehicle_type) { return $vehicle_type->toArray(); }, $this->getVehicleTypes()), ]; // Cache the result $this->cache('asArray', $tmp, $this->id); $this->setCachedExpiration('asArray', self::$asArrayCacheExpiration, $this->id); return $tmp; } /** * Get the addons for the vehicle * @return array{customer_vehicles_addons_o} * @throws Exception If the object is not selected */ public function getAddons(): array { self::requireSelected(); $addons = (new customer_vehicles_addons_o())->getFieldsWhere([ 'vehicle_id' => (int)$this->id, ], [ 'id', ]); return array_map(function ($addon) { return (new customer_vehicles_addons_o())->select((int)$addon['id']); }, $addons); } /** * Get the available addons for the vehicle * @return array{product_options_o} * @throws Exception If the object is not selected */ public function getAvailableAddons(): array { self::requireSelected(); $available_addons = (new product_options_o())->getProductOptions((int)$this->type->value()); // Filter the addons, to only show the ones that has "subscription_allowed" set to true $available_addons = array_filter($available_addons, function ($addon) { return (bool)$addon['product']['subscription_allowed']; }); return array_map(function ($addon) { return (new product_options_o())->select((int)$addon['id']); }, $available_addons); } /** * Get the last order id for the vehicle * @return int|null * @throws Exception If the object is not selected * @throws Exception If the there is an error selecting the order */ private function getLastOrderId(): ?int { self::requireSelected(); $last_order = $this->getLastOrderByPlate((string)$this->reg->value()); return $last_order?->id ? (int)$last_order->id : null; } /** * Get last order with order items for plate (supports unregistered vehicles) * @param string $reg The registration number of the vehicle * @return orders_o|null The last order object for the vehicle, or null if no orders found * @throws Exception If there is an error selecting the order */ public function getLastOrderByPlate(string $reg): ?orders_o { $orders_o = (new orders_o()); $orders = $orders_o->getFieldsWhere([ 'reg_1' => $reg, 'deleted_at' => null, ], [ 'id', ]); // Find the last order with order items while (count($orders) > 0) { $last_order = array_pop($orders); $order = (new orders_o())->select((int)$last_order['id']); if ($order->exists() && count((new order_items_o())->getFieldsWhere([ 'order_id' => (int)$order->id, 'deleted_at' => null, ], ['id'])) > 0) { return $order; } } return null; } /** * Check if the vehicle has an XLVask object * @return bool * @throws Exception If the object is not selected * @note This method checks if the vehicle is registered in the XLVask system. */ public function hasXLVask(): bool { self::requireSelected(); $xlvask = new xlvask(); $vehicles = new $xlvask->helpers->xlvask_vehicles(); /** @var xlvask_vehicles $vehicles */ return $vehicles->getVehicleByRegistrationNumber((string)$this->reg->value(), true) !== null; } /** * Get the XLVask object for the vehicle * @return xlvask_vehicle * @throws Exception If the object is not selected */ public function getXLVask(): xlvask_vehicle { self::requireSelected(); $xlvask = new xlvask(); $vehicles = new $xlvask->helpers->xlvask_vehicles(); /** @var xlvask_vehicles $vehicles */ return $vehicles->getVehicleByRegistrationNumber((string)$this->reg->value()); } /** * Get the vehicle types applicable to the vehicle (Wash types) * @return array{xlvask_vehicle_type} * @note This method retrieves the vehicle types from the XLVask system that are applicable to the vehicle. * @return array of xlvask_vehicle_type objects * @throws Exception If the object is not selected or if there is an error retrieving the vehicle types * @see xlvask_vehicle_type */ public function getVehicleTypes(): array { self::requireSelected(); return (new xlvask())->helpers->xlvask_vehicle_types::getVehicleTypesByProductId( (int)$this->type->value() ); } /** * Add the default addons to the vehicle * @return void * @throws Exception If the object is not selected * @throws Exception If the there is an error adding the addon * @throws Exception If the there is an error selecting the addon * @throws Exception If the there is an error selecting the vehicle */ public function addDefaultAddons(): void { self::requireSelected(); // If any of the available addons have the product id in the $automatic_addon_product_ids array, add it to the vehicle $automatic_addon_product_ids = [ 23, // Spot Free- Varevogn 24, // Spot Free- Lastbil ]; $available_addons = self::getAvailableAddons(); /** @var product_options_o $addon */ foreach ( $available_addons as $addon ) { //echo 'Vehicle id: ' . $this->id . PHP_EOL; //echo 'Addon id: ' . $addon->id . PHP_EOL; //echo 'Primary product id: ' . $addon->product_id->value() . PHP_EOL; //echo 'Addon product id: ' . $addon->option_id->value() . PHP_EOL; //echo 'Is in array: ' . (in_array((int)$addon->option_id->value(), $automatic_addon_product_ids) ? 'true' : 'false') . PHP_EOL; //print_r($available_addons); if (in_array((int)$addon->option_id->value(), $automatic_addon_product_ids)) { // Add the addon to the vehicle (new customer_vehicles_addons_o())->add( (int)$this->id, (int)$addon->id, (int)1, ); } } } /** * @throws Exception If the creation of the object fails */ public function add(int $customer_number, int $type, string $reg, bool $subscription, string|null $reference = null): void { global $db, $response; $tmp_id = self::add_object([ 'customer_id' => (int)$customer_number, 'type' => (int)$type, 'reg' => (string)$reg, 'wash_subscription' => (bool)$subscription ? 1 : 0, 'reference' => ($reference ? (string)$reference : null), ]); self::select((int)$tmp_id); self::objectChanged(); self::addDefaultAddons(); } public function objectChanged(): void { // Clear the cache for the object $this->deleteCached('asArray', $this->id); } public function structure(): void { $this->setTable('customer_vehicles'); } public function getObjectProperties(): void { $this->customer_id = new object_property($this->table, $this->id, 'customer_id', 'int', true); $this->type = new object_property($this->table, $this->id, 'type', 'int', true); $this->reg = new object_property($this->table, $this->id, 'reg', 'string', true); $this->reference = new object_property($this->table, $this->id, 'reference', 'string', false); $this->wash_subscription = new object_property($this->table, $this->id, 'wash_subscription', 'bool', false); $this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false); } /** * Get the customer object for the vehicle * @param string $reg The registration number of the vehicle * @return int|null The customer number for the vehicle, or null if the vehicle is not registered to a customer * @throws Exception If the customer_id does not exist */ public function getPlateCustomerNumber(string $reg): int|null { // Get the customer id from the vehicle $result = self::getFieldsWhere([ 'reg' => $reg, ], [ 'customer_id', ]); // If the customer id is not found, return null if (count($result) === 0) { return null; } // If the customer id is found, return it return (int)$result[0]['customer_id']; } /** * Get the plate reference if it exists, otherwise return a fallback value * @param string $RegistrationNumber The registration number of the vehicle * @param mixed $fallback The fallback value to return if the vehicle does not exist or has no reference * @throws Exception */ public function getPlateReferenceIfExists(string $RegistrationNumber, mixed $fallback = ''): mixed { $vehicle = self::selectByPlate($RegistrationNumber); if (!$vehicle->exists()) { return $fallback; // If the vehicle does not exist, return the fallback value } // If the vehicle has a reference, return it if ($vehicle->reference->value() !== null) { return (string)$vehicle->reference->value(); } // If the vehicle does not have a reference, return the fallback value return $fallback; } /** * @throws Exception */ public function selectByPlate(string $reg): customer_vehicles_o { $result = self::getFieldsWhere([ 'reg' => $reg, ], [ 'id', ]); if (count($result) === 0) { return new customer_vehicles_o(); } return (new customer_vehicles_o())->select((int)$result[0]['id']); } /** * Set the vehicle type id for the vehicle in the XLVask system * @param string $vehicleTypeId The vehicle type id to set in the XLVask system * @note This method updates the vehicle type in the XLVask system for the vehicle. * @throws Exception If the associated customer is not registered in the XLVask system * @throws Exception If the vehicle is not registered in the XLVask system * @throws Exception If the vehicle type id is not valid or if there is an error updating the vehicle type in the XLVask system * @throws Exception If the object is not selected * @see xlvask_vehicle * @example * $vehicle = new customer_vehicles_o(); * $vehicle->select(1); // Select the vehicle with id 1 * $vehicleTypeId = 'some-vehicle-type-id'; // The vehicle type id to set * $vehicle->setVehicleTypeId($vehicleTypeId); * @see xlvask_vehicle_type */ public function setVehicleTypeId(string $vehicleTypeId): void { self::requireSelected(); $xlvask = new xlvask(); // Check if the vehicle is registered in the XLVask system if (!$this->hasXLVask()) { // Create a new XLVask vehicle throw new Exception('Vehicle is not registered in the XLVask system. Please register the vehicle first.'); } // Set the vehicle type id $xlvask_vehicle = $this->getXLVask(); $xlvask_vehicle->vehicleTypeId = $vehicleTypeId; // Update the XLVask vehicle $xlvask->updateVehicle($xlvask_vehicle); // Cache the updated vehicle $xlvask->getCache()->setVehicleCache($xlvask_vehicle->registrationNumber, $xlvask_vehicle); $this->objectChanged(); } /** * Create a new vehicle in the XLVask system * * @param string $registrationNumber The registration number of the vehicle * @param int $customerId The customer id of the vehicle * @param string|null $vehicleTypeId The vehicle type id of the vehicle, if not provided, the default vehicle type will be used * @return xlvask_vehicle The created XLVask vehicle object * @throws Exception If the customer is not registered in the XLVask system * @throws Exception If the vehicle is already registered in the XLVask system * @throws Exception If the vehicle type id is not valid or if there is an error creating the vehicle in the XLVask system * @throws Exception If the object is not selected * @note This method creates a new vehicle in the XLVask system for the vehicle. * @see xlvask_vehicle * @see xlvask_vehicle_type * @example * $vehicle = new customer_vehicles_o(); * $vehicle->select(1); // Select the vehicle with id 1 * $vehicleTypeId = 'some-vehicle-type-id'; // The vehicle type id to set, if not provided, the default vehicle type will be used * $vehicle->createXLVaskVehicle($vehicleTypeId); */ public function createXLVaskVehicle(string $vehicleTypeId): xlvask_vehicle { self::requireSelected(); $xlvask = new xlvask(); // Check if the customer is registered in the XLVask system $xlvask_customer = $this->getXLVaskCustomer(); // Make sure the vehicle type id is valid for this vehicle type $valid_vehicle_types = $xlvask->helpers->xlvask_vehicle_types::getVehicleTypesByProductId( (int)$this->type->value() ); if (!in_array($vehicleTypeId, array_map(fn($type) => $type->vehicleTypeId, $valid_vehicle_types))) { throw new Exception('Invalid vehicle type id provided. Please provide a valid vehicle type id for this vehicle type.'); } // Create a new XLVask vehicle $xlvask_vehicle = new xlvask_vehicle(); $xlvask_vehicle->registrationNumber = (string)$this->reg->value(); $xlvask_vehicle->customerId = $xlvask_customer->customerId; $xlvask_vehicle->vehicleTypeId = $vehicleTypeId; $xlvask_vehicle->active = true; // Set the vehicle as active $xlvask_vehicle->autoStartOnLpr = true; // Enable auto start on LPR // Add the vehicle to the XLVask system $xlvask->createVehicle($xlvask_vehicle); // Cache the vehicle $vehicles = $xlvask->helpers->new($xlvask->helpers->xlvask_vehicles); /** @var xlvask_vehicles $vehicles */ $xlvask_vehicle = $vehicles::getVehicleByRegistrationNumber((string)$this->reg->value()); $xlvask->getCache()->setVehicleCache($xlvask_vehicle->registrationNumber, $xlvask_vehicle); $this->objectChanged(); return $xlvask_vehicle; } /** * Get the XLVask customer object for the vehicle * @return xlvask_customer The XLVask customer object for the vehicle * @throws Exception If the object is not selected * @note This method retrieves the XLVask customer object for the vehicle. */ public function getXLVaskCustomer(): xlvask_customer { self::requireSelected(); $customer_number = (int)$this->customer_id->value(); $xlvask = new xlvask(); if ($xlvask->getCache()->isCustomerCached($customer_number)) { return $xlvask->getCache()->getCustomerCache($customer_number); } // If the customer is not cached, throw an exception throw new Exception('Customer is not registered in the XLVask system. Please register the customer first.'); } /** * Set the auto start on LPR for the vehicle in the XLVask system * @param bool $autoStartOnLpr Whether to enable auto start on LPR for the vehicle * @throws Exception If the object is not selected * @note This method updates the auto start on LPR setting for the vehicle in the XLVask system. */ public function setAutoStartOnLpr(bool $autoStartOnLpr): void { self::requireSelected(); $xlvask_vehicle = $this->getXLVask(); $xlvask_vehicle->autoStartOnLpr = $autoStartOnLpr; $xlvask = new xlvask(); $xlvask->updateVehicle($xlvask_vehicle); // Cache the updated vehicle $xlvask->getCache()->setVehicleCache($xlvask_vehicle->registrationNumber, $xlvask_vehicle); $this->objectChanged(); } /** * Get the first two transactions, that are not deleted, and contains the product of the vehicle type for the vehicle in the list of transaction ids * @param array $transaction_ids [ 1, 2, 3 ] * @return array An array of transaction ids * @throws Exception If the object is not selected * @note This method retrieves the first two transactions that are not deleted and contain the product of the vehicle type for the vehicle in the provided list of transaction ids. * The transactions are ordered by creation date in descending order. * If no transactions are found, an empty array is returned. * If only one transaction is found, an array with one transaction id is returned. * If two transactions are found, an array with two transaction ids is returned. * @example * $vehicle = new customer_vehicles_o(); * $vehicle->select(1); // Select the vehicle with id 1 * $transaction_ids = [1, 2, 3, 4, 5]; // The list of transaction ids to check * $result = $vehicle->getSubscriptionAppliedTransactionsFromList($transaction_ids); * print_r($result); // Array of transaction ids */ public function getSubscriptionAppliedTransactionsFromList(array $transaction_ids): array { global $db; self::requireSelected(); if (count($transaction_ids) === 0) { return []; } //print_r($transaction_ids); // Convert the array of transaction ids to a comma separated string $orders = ''; foreach ($transaction_ids as $transaction_id) { // Check if the transaction is included in the invoicing. $tmp = (new orders_o())->select((int)$transaction_id); if (!$tmp->isIncludedInInvoicing()) { continue; // The transaction is not included in the invoicing, skip it } $orders .= (int)$transaction_id . ','; } // Remove the last comma $orders = rtrim($orders, ','); // Get the first two transactions that are not deleted and contains at least one order item with the 'product_id' of the vehicle type for the vehicle $query = " SELECT o.id FROM orders o JOIN order_items oi ON oi.order_id = o.id WHERE o.deleted_at IS NULL AND oi.deleted_at IS NULL AND o.id IN ($orders) AND oi.product_id = " . (int)$this->type->value() . " GROUP BY o.id ORDER BY o.created_at ASC LIMIT 2 "; //echo $query . PHP_EOL; /** @var mysqli_result $result */ $result = $db->query($query); $transaction_ids = $db->fetch_all($result); return array_map(fn($transaction) => (int)$transaction['id'], $transaction_ids); } /** * @throws Exception */ public function getLastTransactions(int $numberOfTransactions = 5): array { self::requireSelected(); $orders_o = new orders_o(); $orders = $orders_o->getFieldsWhere([ 'reg_1' => (string)$this->reg->value(), 'deleted_at' => null, ], [ 'id', ], [ 'created_at' => 'DESC', ], $numberOfTransactions); return array_map(fn($order) => (int)$order['id'], $orders); } }