From c5a6b2006f6097548475813194710741a5cfe08f Mon Sep 17 00:00:00 2001 From: Jepp9350 <2jepp9350@gmail.com> Date: Thu, 26 Jun 2025 16:06:02 +0200 Subject: [PATCH] Add methods for retrieving customers and transactions within date ranges, enhance `orders_o` functionality, and improve customer object construction in invoicing routes. --- services/nginx/app/objects/order_items_o.php | 1 - services/nginx/app/objects/orders_o.php | 99 ++++++++++++++++--- services/nginx/app/objects/users_o.php | 21 ++-- .../nginx/app/routes/InvoicingPeriodRoute.php | 78 ++++++++++++++- 4 files changed, 173 insertions(+), 26 deletions(-) diff --git a/services/nginx/app/objects/order_items_o.php b/services/nginx/app/objects/order_items_o.php index 3cf3c746..ea497389 100644 --- a/services/nginx/app/objects/order_items_o.php +++ b/services/nginx/app/objects/order_items_o.php @@ -197,7 +197,6 @@ class order_items_o extends db if ($notes) { $this->notes->set($notes); } - $this->objectChanged(); } catch (Exception $e) { $response->error($e->getMessage()); diff --git a/services/nginx/app/objects/orders_o.php b/services/nginx/app/objects/orders_o.php index 9f5c7d82..bea53599 100644 --- a/services/nginx/app/objects/orders_o.php +++ b/services/nginx/app/objects/orders_o.php @@ -143,23 +143,11 @@ class orders_o extends db { self::requireSelected(); $order_collection = new collected_order_invoices_o(); - $order_collection->select($this->invoice_collection_id->value()); - if (!$order_collection->exists()) { - throw new Exception('Order collection not found'); - } + $order_collection->select((int)$this->invoice_collection_id->value()); + $order_collection->requireSelected(); return $order_collection; } - public function exists(): bool - { - // Check if the id is greater than 0, and that the deleted_at property is null - if ($this->id > 0) { - $this->getObjectProperties(); - return $this->deleted_at->value() === null; - } - return false; - } - public function restore(): void { // Set the deleted_at property to null @@ -233,6 +221,16 @@ class orders_o extends db $this->{$data['field']}->set($data['value']); } + public function exists(): bool + { + // Check if the id is greater than 0, and that the deleted_at property is null + if ($this->id > 0) { + $this->getObjectProperties(); + return $this->deleted_at->value() === null; + } + return false; + } + /** * Mark the order as completed * @throws Exception If the order is not selected @@ -802,4 +800,77 @@ class orders_o extends db // Return the order object return $this; } + + /** + * Get a list of customers who have placed orders within a specific date range + * @param string $dateFrom (E.g. "2023-01-01 00:00:00") + * @param string $dateTo (E.g. "2023-01-31 23:59:59") + * @return users_o[] + * @throws Exception + */ + public function getCustomersWithOrdersInDateRange(string $dateFrom, string $dateTo): array + { + global $db; + // Validate the date range + if (strtotime($dateFrom) === false || strtotime($dateTo) === false) { + throw new Exception('Invalid date range provided'); + } + if (strtotime($dateFrom) > strtotime($dateTo)) { + throw new Exception('The start date cannot be after the end date'); + } + // Prepare the SQL query to find customers with orders in the date range (unique customer IDs) + $dateFrom = $db->escape_string($dateFrom); + $dateTo = $db->escape_string($dateTo); + $sql = "SELECT DISTINCT customer_id FROM $this->table WHERE created_at BETWEEN '$dateFrom' AND '$dateTo' AND deleted_at IS NULL"; + $result = $db->query($sql); + $customer_numbers = []; + while ($row = $result->fetch_assoc()) { + // Trim and cast the customer_id to an integer + $customer_number = (int)trim($row['customer_id']); + if ($customer_number > 0) { + $customer_numbers[] = $customer_number; // Only add valid customer numbers + } + } + if (empty($customer_numbers)) { + return []; // No customers found in the date range + } + + // Get the customers by their customer numbers + return (new users_o())->getUsersByCustomerNumbers($customer_numbers); + } + + /** + * Get transactions for a specific customer within a date range + * @param int $customerNumber The customer number to filter by + * @param string $dateFrom The start date of the date range (inclusive) "Y-m-d H:i:s" format + * @param string $dateTo The end date of the date range (inclusive) "Y-m-d H:i:s" format + * @return orders_o[] The transactions for the customer within the specified date range + * @throws Exception If the date range is invalid or if no transactions are found + */ + public function getTransactionsForCustomer(int $customerNumber, string $dateFrom, string $dateTo): array + { + global $db; + // Validate the date range + if (strtotime($dateFrom) === false || strtotime($dateTo) === false) { + throw new Exception('Invalid date range provided'); + } + if (strtotime($dateFrom) > strtotime($dateTo)) { + throw new Exception('The start date cannot be after the end date'); + } + // Prepare the SQL query to find transactions for the customer in the date range + $dateFrom = $db->escape_string($dateFrom); + $dateTo = $db->escape_string($dateTo); + $sql = "SELECT id FROM $this->table WHERE customer_id = $customerNumber AND created_at BETWEEN '$dateFrom' AND '$dateTo' AND deleted_at IS NULL"; + $result = $db->query($sql); + if ($result->num_rows === 0) { + return []; // No transactions found for the customer in the date range + } + $transactions = []; + while ($row = $result->fetch_assoc()) { + $order = new orders_o(); + $order->select((int)$row['id']); + $transactions[] = $order; + } + return $transactions; + } } \ No newline at end of file diff --git a/services/nginx/app/objects/users_o.php b/services/nginx/app/objects/users_o.php index edb8c7c0..7d859e4b 100644 --- a/services/nginx/app/objects/users_o.php +++ b/services/nginx/app/objects/users_o.php @@ -1191,17 +1191,24 @@ class users_o extends db $sql = "SELECT id, customer_number FROM $this->table WHERE customer_number IN ($customer_numbers)"; $result = $db->query($sql); // Create an array of user objects - $users = []; + $ids = []; while ($row = $result->fetch_assoc()) { + $ids[] = (int)$row['id']; + } + // Only get unique IDs + $ids = array_unique($ids); + $users = []; + foreach ( $ids as $id ) { $user = new users_o(); - $user->select((int)$row['id']); + $user->id = $id; + $user->getObjectProperties(); if ($customerNumberAndUserArray) { - // If the customerNumberAndUserArray is true, return an array with the customer number and user object - $users[(int)$row['customer_number']] = $user; - continue; + // If the user should be returned with the customer number + $users[$user->customer_number->value()] = $user; + } else { + // If the user should be returned as an object + $users[] = $user; } - // Otherwise, return the user object - $users[] = $user; } return $users; } diff --git a/services/nginx/app/routes/InvoicingPeriodRoute.php b/services/nginx/app/routes/InvoicingPeriodRoute.php index 36c27ee1..f4cb8170 100644 --- a/services/nginx/app/routes/InvoicingPeriodRoute.php +++ b/services/nginx/app/routes/InvoicingPeriodRoute.php @@ -3,6 +3,7 @@ namespace routes; use classes\authentication; +use Exception; use objects\customer_vehicles_o; use objects\logs_o; use objects\orders_o; @@ -63,7 +64,7 @@ class InvoicingPeriodRoute } /** - * @throws \Exception + * @throws Exception */ private static function getInvoicingPeriod(string $dateFrom, string $dateTo): array { @@ -74,12 +75,13 @@ class InvoicingPeriodRoute 'vehicle_subscriptions' => self::getVehicleSubscriptions($dateFrom, $dateTo), 'fixed_pricing' => self::getFixedPricing($dateFrom, $dateTo), 'tank_cleaning' => self::getTankCleaning($dateFrom, $dateTo), + 'all' => self::getCustomersWithTransactions($dateFrom, $dateTo), ], ]; } /** - * @throws \Exception + * @throws Exception */ private static function getVehicleSubscriptions(string $dateFrom, string $dateTo): array { @@ -124,7 +126,7 @@ class InvoicingPeriodRoute } /** - * @throws \Exception + * @throws Exception */ private static function getFixedPricing(string $dateFrom, string $dateTo): array { @@ -169,7 +171,7 @@ class InvoicingPeriodRoute } /** - * @throws \Exception + * @throws Exception */ private static function getTankCleaning(string $dateFrom, string $dateTo): array { @@ -211,4 +213,72 @@ class InvoicingPeriodRoute } return $tank_cleaning; } + + /** + * @throws Exception + */ + private static function getCustomersWithTransactions(string $dateFrom, string $dateTo): array + { + // Define the customers with orders in the specified date range + $customers = (new orders_o)->getCustomersWithOrdersInDateRange($dateFrom, $dateTo); + $tmp = []; + foreach ( $customers as $customer ) { + $tmp[] = self::constructCustomerObject( + (int)$customer->customer_number->value(), + (new \objects\users_o())->getCustomerName((int)$customer->customer_number->value()), + (new \objects\orders_o())->getTransactionsForCustomer( + (int)$customer->customer_number->value(), + $dateFrom, + $dateTo + ), + ); + } + return $tmp; + } + + /** + * @param int $customer_number + * @param string $customer_name + * @param orders_o[] $transactions + * @param bool $requires_action + * @return array + * @throws Exception + */ + protected static function constructCustomerObject( + int $customer_number, + string $customer_name, + array $transactions = [], + bool $requires_action = false + ): array + { + return [ + 'customer_number' => $customer_number, + 'customer_name' => $customer_name, + 'transactions' => $parsed_transactions = array_map(function ($transaction) { + return [ + 'id' => $transaction->id, + 'date' => $transaction->created_at->value(), + 'amount' => $transaction->getNetAmount(), + 'booked' => $transaction->isBooked(), + ]; + }, $transactions), + 'requires_action' => self::checkRequiresAction($parsed_transactions, $requires_action), + ]; + } + + private static function checkRequiresAction(array $parsed_transactions, bool $requires_action): bool + { + // If requires_action is already set to true, return true + if ($requires_action) { + return true; + } + // Check if any transaction is not booked + foreach ( $parsed_transactions as $transaction ) { + if (!$transaction['booked']) { + return true; + } + } + // If all transactions are booked, return false + return false; + } } \ No newline at end of file