From e9aeedc327612cf5f94323ba9d3ab5420d20d67a Mon Sep 17 00:00:00 2001 From: Jepp9350 <2jepp9350@gmail.com> Date: Fri, 28 Feb 2025 13:50:27 +0100 Subject: [PATCH] Add invoice collection assignment logic to orders and users Introduced methods to manage, assign, and retrieve invoice collections for orders and users. Enhanced the system to support flexible invoice grouping, including per-order logic and automated assignment to open or newly created invoice collections. --- .../objects/collected_order_invoices_o.php | 116 +++++++++++++++++- services/nginx/app/objects/orders_o.php | 28 ++++- services/nginx/app/objects/users_o.php | 70 +++++++++-- .../nginx/app/routes/orderInvoicesRoute.php | 3 +- 4 files changed, 201 insertions(+), 16 deletions(-) diff --git a/services/nginx/app/objects/collected_order_invoices_o.php b/services/nginx/app/objects/collected_order_invoices_o.php index ef119738..e1564c07 100644 --- a/services/nginx/app/objects/collected_order_invoices_o.php +++ b/services/nginx/app/objects/collected_order_invoices_o.php @@ -40,20 +40,29 @@ class collected_order_invoices_o extends db $db; // Sanitize the input $customer_number = $db->escape_string($customer_number); - $name = $db->escape_string($name); - $notes = $db->escape_string($notes); - $processor = $db->escape_string($processor); + if (!empty($name)) { + $name = $db->escape_string($name); + } + if (!empty($notes)) { + $notes = $db->escape_string($notes); + } + if (!empty($processor)) { + $processor = $db->escape_string($processor); + } // Require the customer number to be of a valid customer self::requireValidCustomer($customer_number); // Add the object $tmp_id = self::add_object([ 'customer_number' => (int)$customer_number, 'name' => $name, - 'notes' => $notes, - 'processor' => $processor + 'notes' => $notes ]); self::select((int)$tmp_id); self::requireSelected(); + // Set the processor + if (!empty($processor)) { + $this->processor->set($processor); + } self::objectChanged(); return $this; } @@ -124,7 +133,104 @@ class collected_order_invoices_o extends db 'created_at' => (string)$this->created_at->value(), 'updated_at' => (string)$this->updated_at->value(), 'closed_at' => (string)$this->closed_at->value(), + 'orders' => $this->getOrders() ]; } + /** + * Get the orders in the invoice collection + * @param bool $count If the count of orders should be returned instead of the orders + * @return array|int The list of orders in the invoice collection, or the count of orders if $count is true + * @throws Exception If the request was not successful + */ + public function getOrders(bool $count = false): array|int + { + $orders = new orders_o(); + $order_ids = $orders->getFieldsWhere( + [ + 'invoice_collection_id' => $this->id, + 'deleted_at' => null + ], + ['id'] + ); + if ($count) { + return count($order_ids); + } + $result = []; + foreach ( $order_ids as $order_id ) { + $orders->select($order_id['id']); + $orders->requireSelected(); + $result[] = $orders->asArray(); + } + return $result; + } + + /** + * Check if a customer has an open invoice collection + * @param int $customer_number The E-conomic customer number + * @return bool If the customer has an open invoice collection + */ + public function hasOpenInvoiceCollection(int $customer_number): bool + { + $collections = self::getFieldsWhere( + [ + 'customer_number' => $customer_number, + 'closed_at' => null, + ], + ['id'] + ); + return !empty($collections); + } + + /** + * Get the open invoice collections for a customer + * @param int $customer_number The E-conomic customer number + * @return array The open invoice collections + * @throws Exception If the request was not successful + */ + public function getOpenInvoiceCollections(int $customer_number): array + { + $collections = self::getFieldsWhere( + [ + 'customer_number' => $customer_number, + 'closed_at' => null, + ], + ['id', 'name', 'notes', 'processor', 'external_id', 'created_at', 'updated_at', 'closed_at'] + ); + // Parse the results + $result = []; + foreach ( $collections as $collection ) { + $this->id = $collection['id']; + self::getObjectProperties(); + self::requireSelected(); + $result[] = self::asArray(); + } + return $result; + } + + /** + * @param int $customer_number The E-conomic customer number + * @return self The latest open invoice collection + * @throws Exception If no open invoice collections were found + * @throws Exception If the request was not successful + */ + public function getLatestOpenInvoiceCollection(int $customer_number): self + { + $collections = self::getFieldsWhere( + [ + 'customer_number' => $customer_number, + 'closed_at' => null, + ], + ['id', 'name', 'notes', 'processor', 'external_id', 'created_at', 'updated_at', 'closed_at'], + ); + if (empty($collections)) { + throw new Exception('No open invoice collections found'); + } + // Parse the results + $this->id = $collections[0]['id']; + self::getObjectProperties(); + self::requireSelected(); + return $this; + } + } \ No newline at end of file diff --git a/services/nginx/app/objects/orders_o.php b/services/nginx/app/objects/orders_o.php index 37a67735..e8d1e5e7 100644 --- a/services/nginx/app/objects/orders_o.php +++ b/services/nginx/app/objects/orders_o.php @@ -25,6 +25,7 @@ class orders_o extends db public object_property $created_at; public object_property $deleted_at; public stripe_module_orders_o $stripe_module_orders; + public object_property $invoice_collection_id; public function structure(): void { @@ -64,8 +65,9 @@ class orders_o extends db $this->economic_module_orders = (new economic_module_orders())->getByOrderId($this->id); $this->stripe_module_orders = (new stripe_module_orders_o())->select($this->id); $this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp', false); + $this->invoice_collection_id = new object_property($this->table, $this->id, 'invoice_collection_id', 'int', false); } - + public function includeIncludes(): orders_o { @@ -150,6 +152,7 @@ class orders_o extends db 'reg_3' => $this->reg_3->value(), 'created_at' => $this->created_at->value(), 'deleted_at' => $this->deleted_at->value(), + 'invoice_collection_id' => (int)$this->invoice_collection_id->value(), ]; } @@ -318,12 +321,35 @@ class orders_o extends db // Set the values of the object properties $this->getObjectProperties(); + self::requireSelected(); + self::assignToInvoiceCollection(); return $this; } catch (Exception $e) { $response->error($e->getMessage()); } } + /** + * Assign the order to an invoice collection + * @param int|null $invoiceCollectionId The invoice collection id, if not set, the default invoice collection id will be used. + * @throws Exception If the order is not selected + */ + public function assignToInvoiceCollection(int $invoiceCollectionId = null): void + { + // If the invoice collection id is not set, get the default invoice collection id + self::requireSelected(); + // Get the customer + $customer = new users_o(); + $customer->getUserByCustomerNumber($this->customer_id->value()); + $customer->requireSelected(); + // Get the invoice collection id + $invoiceCollectionId = $invoiceCollectionId ?? $customer->getNewOrderInvoiceCollectionId(); + // Assign the order to the invoice collection + $this->invoice_collection_id->set($invoiceCollectionId); + self::objectChanged(); + } + + public function objectChanged(): void { // Since the orders object is not cached, there is no need to invalidate the cache diff --git a/services/nginx/app/objects/users_o.php b/services/nginx/app/objects/users_o.php index 21b5daf3..fc466916 100644 --- a/services/nginx/app/objects/users_o.php +++ b/services/nginx/app/objects/users_o.php @@ -399,15 +399,6 @@ class users_o extends db return $this->doesUserHaveAttribute('requiresReferenceNumber'); } - /** - * If the customer should have an invoice per order (Otherwise, they will have a monthly invoice for all orders) - * @return bool - */ - public function invoicePerOrder(): bool - { - return $this->doesUserHaveAttribute('invoiceAllOrdersIndividually'); - } - /** * If the customer is NOT allowed to purchase spot free washes * @return bool @@ -872,4 +863,65 @@ class users_o extends db $user = self::getFieldsWhere(['customer_number' => $customerNumber], ['id']); return $user[0]['id']; } + + /** + * Get the invoice collection ID, where the new order should be added to. + * This is based on the user's settings. (If the user has the attribute 'invoiceAllOrdersIndividually', the new order should be added to a new invoice collection) + * @return int The ID of the order invoice collection the new order should be added to. + * @throws Exception + */ + public function getNewOrderInvoiceCollectionId(): int + { + // Check if the user has the attribute 'invoiceAllOrdersIndividually' + $invoice_collection = new collected_order_invoices_o(); + if ($this->invoicePerOrder() || !self::hasOpenInvoiceCollection()) { + // Require the user to have a customer number above 0 + if (is_null($this->customer_number->value()) || $this->customer_number->value() === 0) { + throw new Exception('The user does not have a customer number'); + } + // The user has the attribute 'invoiceAllOrdersIndividually', create a new invoice collection + $invoice_collection->add((int)$this->customer_number->value()); + return $invoice_collection->id; + } + // The user does not have the attribute 'invoiceAllOrdersIndividually', get the ID of the last invoice collection + return self::getOpenInvoiceCollection()->id; + } + + /** + * If the customer should have an invoice per order (Otherwise, they will have a monthly invoice for all orders) + * @return bool + */ + public function invoicePerOrder(): bool + { + return $this->doesUserHaveAttribute('invoiceAllOrdersIndividually'); + } + + /** + * Check if the user has an open invoice collection + * @return bool + */ + public function hasOpenInvoiceCollection(): bool + { + // Check if the user has an open invoice collection + $invoice_collection = new collected_order_invoices_o(); + return $invoice_collection->hasOpenInvoiceCollection($this->customer_number->value()); + } + + /** + * Get the open invoice collection of the user + * @return collected_order_invoices_o + * @throws Exception If the user does not have an open invoice collection + * @throws Exception If the user does not have a customer number + */ + public function getOpenInvoiceCollection(): collected_order_invoices_o + { + // Require the user to have a customer number above 0 + if (is_null($this->customer_number->value()) || $this->customer_number->value() === 0) { + throw new Exception('The user does not have a customer number'); + } + self::requireSelected(); + // Get the open invoice collection of the user + $invoice_collection = new collected_order_invoices_o(); + return $invoice_collection->getLatestOpenInvoiceCollection($this->customer_number->value()); + } } \ No newline at end of file diff --git a/services/nginx/app/routes/orderInvoicesRoute.php b/services/nginx/app/routes/orderInvoicesRoute.php index 67de9112..04ebe788 100644 --- a/services/nginx/app/routes/orderInvoicesRoute.php +++ b/services/nginx/app/routes/orderInvoicesRoute.php @@ -47,7 +47,8 @@ class orderInvoicesRoute 'external_id' => (string)$collected_order_invoice['external_id'], 'closed_at' => $collected_order_invoice['closed_at'] ? (string)$collected_order_invoice['closed_at'] : null, 'updated_at' => (string)$collected_order_invoice['updated_at'], - 'created_at' => (string)$collected_order_invoice['created_at'] + 'created_at' => (string)$collected_order_invoice['created_at'], + 'orders' => ((new collected_order_invoices_o())->select((int)$collected_order_invoice['id']))->getOrders(true) ]; } ));