diff --git a/services/nginx/app/classes/economic.php b/services/nginx/app/classes/economic.php index c4759692..c3fe2683 100644 --- a/services/nginx/app/classes/economic.php +++ b/services/nginx/app/classes/economic.php @@ -94,11 +94,12 @@ class economic implements economic_i * Get a invoiceDraft (Helper) from the Economic system * @param int $draft_invoice_number The Economic draft invoice number * @param string $currency The conversion rate to use (default: DKK) + * @param bool $skip_fetch Skip fetching the invoice draft from the Economic system (default: false) * @return economic_invoice_draft */ - public function getInvoiceDraft(int $draft_invoice_number, string $currency = 'DKK'): economic_invoice_draft + public function getInvoiceDraft(int $draft_invoice_number, string $currency = 'DKK', bool $skip_fetch = false): economic_invoice_draft { - return new $this->helpers->economic_invoice_draft($draft_invoice_number, $currency); + return new $this->helpers->economic_invoice_draft($draft_invoice_number, $currency, $skip_fetch); } /** diff --git a/services/nginx/app/modules/economic/endpoints/invoices/draft/economic_invoices_draft_endpoint.php b/services/nginx/app/modules/economic/endpoints/invoices/draft/economic_invoices_draft_endpoint.php index 935b3f51..45e92b29 100644 --- a/services/nginx/app/modules/economic/endpoints/invoices/draft/economic_invoices_draft_endpoint.php +++ b/services/nginx/app/modules/economic/endpoints/invoices/draft/economic_invoices_draft_endpoint.php @@ -61,7 +61,7 @@ class economic_invoices_draft_endpoint */ public function add_order(int $invoiceDraftId, orders_o $order, string $currency = 'DKK'): void { - $draftInvoice = (new economic())->getInvoiceDraft($invoiceDraftId, strtoupper($currency)); + $draftInvoice = (new economic())->getInvoiceDraft($invoiceDraftId, strtoupper($currency), true); // Add the transaction header (Timestamp, department, etc.) $draftInvoice->addNewTransactionHeader($order); // Add the order lines diff --git a/services/nginx/app/modules/economic/helpers/economic_invoice_draft.php b/services/nginx/app/modules/economic/helpers/economic_invoice_draft.php index 4c23df5b..74013534 100644 --- a/services/nginx/app/modules/economic/helpers/economic_invoice_draft.php +++ b/services/nginx/app/modules/economic/helpers/economic_invoice_draft.php @@ -45,11 +45,17 @@ class economic_invoice_draft * Construct a new Economic draft invoice object * @throws Exception if the invoice data is invalid or empty */ - public function __construct(int $draft_invoice_number, string $currency = 'DKK') + public function __construct(int $draft_invoice_number, string $currency = 'DKK', bool $skip_fetch = false) { $this->setDraftInvoiceNumber($draft_invoice_number); $this->setCurrency(strtoupper($currency)); - $this->fetchDraftInvoiceData(); + // If the skip fetch is set to false, fetch the draft invoice data + if (!$skip_fetch) { + $this->fetchDraftInvoiceData(); + } else { + $this->draft_invoice_data = new \stdClass(); + $this->draft_invoice_data->draftInvoiceNumber = $this->draft_invoice_number; + } $this->requireSelected(); } diff --git a/services/nginx/app/objects/collected_order_invoices_o.php b/services/nginx/app/objects/collected_order_invoices_o.php index 0fe53e7c..3735c6c3 100644 --- a/services/nginx/app/objects/collected_order_invoices_o.php +++ b/services/nginx/app/objects/collected_order_invoices_o.php @@ -360,9 +360,14 @@ class collected_order_invoices_o extends db // Create an economic draft self::createInvoiceDraft(); + + // Check if the invoice draft exists + if (!self::isDraftExisting()) { + throw new Exception('Failed to create invoice draft'); + } // Add the invoices to the invoice draft - self::addInvoicesToDraft(); + self::addInvoicesToDraft(true); // Close the invoice collection // If the invoice collection is closed, we don't want to close it again @@ -534,17 +539,20 @@ class collected_order_invoices_o extends db /** * Add the invoices to the invoice draft - * @throws Exception If the request was not successful + * @param bool $skip_check If the check for the invoice collection being booked should be skipped * @throws Exception If the invoice collection is not set * @throws Exception If the invoice collection is already closed * @throws Exception If the invoice draft was not found + * @throws Exception If the request was not successful */ - public function addInvoicesToDraft(): self + public function addInvoicesToDraft(bool $skip_check = false): self { // Require the invoice collection to be selected self::requireSelected(); // Require the invoice collection to be open self::requireInvoiceIsNotBooked(); + // Set the timeout to 0, to prevent the script from timing out + set_time_limit(0); // Get the orders in the invoice collection $orders = self::getOrders(); // Check if there are any orders in the invoice collection @@ -552,11 +560,16 @@ class collected_order_invoices_o extends db throw new Exception('No orders in invoice collection'); } // Require the invoice draft to be set (and exists) - self::requireInvoiceDraft(); - + if (!$skip_check) { + self::requireInvoiceDraft(); + } + // Get the invoice draft id from the external id + $draft_id = self::getInvoiceDraftId(); + // Get the customer currency + $currency = self::getCustomerCurrency($this->customer_number->value()); // Add the invoices to the invoice draft foreach ( $orders as $order ) { - self::addInvoiceToDraft($order['id']); + self::addInvoiceToDraft($order['id'], true, $draft_id, $currency); } return $this; @@ -612,38 +625,6 @@ class collected_order_invoices_o extends db } } - /** - * Add an invoice to the invoice draft - * @param int $order_id The order id to add - * @throws Exception If the request was not successful - * @throws Exception If the invoice collection is not set - * @throws Exception If the invoice collection is already closed - * @throws Exception If the invoice draft was not found - */ - public function addInvoiceToDraft(int $order_id): self - { - // Require the invoice collection to be selected - self::requireSelected(); - // Require the invoice collection to be open - self::requireInvoiceIsNotBooked(); - // Get the order object - $order = new orders_o(); - $order->select($order_id); - $order->requireSelected(); - // Require the invoice draft to be set (and exists) - self::requireInvoiceDraft(); - - // Add the invoice to the invoice draft - $economic = new economic(); - $economic->invoices->draft->add_order( - self::getInvoiceDraftId(), - $order, - (string)self::getCustomerCurrency($order->customer_id->value()), - ); - - return $this; - } - /** * Get the customer currency * @return string The customer currency @@ -660,6 +641,52 @@ class collected_order_invoices_o extends db return $customer->getCurrency(); } + /** + * Add an invoice to the invoice draft + * @param int $order_id The order id to add + * @param bool $skip_check If the check for the invoice collection being booked should be skipped + * @param string $currency The currency to use (default: DKK) + * @throws Exception If the request was not successful + * @throws Exception If the invoice collection is not set + * @throws Exception If the invoice collection is already closed + * @throws Exception If the invoice draft was not found + */ + public function addInvoiceToDraft(int $order_id, bool $skip_check = false, int $draft_id = null, string $currency = null): self + { + // Require the invoice collection to be selected + self::requireSelected(); + // Get the order object + $order = new orders_o(); + $order->select($order_id); + $order->requireSelected(); + // Require the invoice draft to be set (and exists) + if (!$skip_check) { + // Require the invoice collection to be open + self::requireInvoiceIsNotBooked(); + // Require the invoice draft to be set + self::requireInvoiceDraft(); + } + // Get the invoice draft id from the external id, if not set + if (empty($draft_id)) { + $draft_id = self::getInvoiceDraftId(); + } + + // If the currency is not set, get the customer currency + if (empty($currency)) { + $currency = (string)self::getCustomerCurrency($order->customer_id->value()); + } + + // Add the invoice to the invoice draft + $economic = new economic(); + $economic->invoices->draft->add_order( + $draft_id, + $order, + (string)$currency + ); + + return $this; + } + /** * Close the invoice collection * @throws Exception If the request was not successful diff --git a/services/nginx/app/objects/currency_conversion_rates_o.php b/services/nginx/app/objects/currency_conversion_rates_o.php index 9381153e..7f745bc0 100644 --- a/services/nginx/app/objects/currency_conversion_rates_o.php +++ b/services/nginx/app/objects/currency_conversion_rates_o.php @@ -185,6 +185,10 @@ class currency_conversion_rates_o extends db if (!self::validateCurrencyFormat($currency)) { throw new Exception('The currency is not valid.'); } + // Check if the currency is 'DKK', then return 1.0 (base currency) + if ($currency === 'DKK') { + return 1.0; + } // Get the conversion rate $result = self::getFieldsWhere(['currency' => $currency], ['rate']); if (empty($result)) { diff --git a/services/nginx/app/objects/orders_o.php b/services/nginx/app/objects/orders_o.php index 4dd65224..56884d35 100644 --- a/services/nginx/app/objects/orders_o.php +++ b/services/nginx/app/objects/orders_o.php @@ -259,7 +259,7 @@ class orders_o extends db { $order = new orders_o(); $order->getOrderById($orderId); - return (new users_o())->getCustomerByIdOrCustomerNumber($order->customer_id->value()); + return (new users_o())->getUserByCustomerNumber($order->customer_id->value()); } public function getOrderById(int $id): orders_o diff --git a/services/nginx/app/objects/user_key_value_pairs_o.php b/services/nginx/app/objects/user_key_value_pairs_o.php index 62023385..1c9c61e7 100644 --- a/services/nginx/app/objects/user_key_value_pairs_o.php +++ b/services/nginx/app/objects/user_key_value_pairs_o.php @@ -108,4 +108,28 @@ class user_key_value_pairs_o extends db return $keys; } + public function getCustomerNumbersWithKey(array $keys): array + { + global $db; + // Get all the users, and their customer numbers, with the given keys + $sql = "SELECT user_id FROM $this->table WHERE var IN ('" . implode("','", $keys) . "')"; + $result = $db->query($sql); + $result = $db->fetch_all($result); + $user_ids = []; + // Add the user ids to the array + foreach ( $result as $row ) { + $user_ids[] = (int)$row['user_id']; + } + // Get the customer numbers from the users table + $sql = "SELECT id, customer_number FROM users WHERE id IN (" . implode(',', $user_ids) . ")"; + $result = $db->query($sql); + $result = $db->fetch_all($result); + $customer_numbers = []; + // Return the customer numbers + foreach ( $result as $row ) { + $customer_numbers[] = (int)$row['customer_number']; + } + return $customer_numbers; + } + } \ No newline at end of file diff --git a/services/nginx/app/routes/orderInvoicesRoute.php b/services/nginx/app/routes/orderInvoicesRoute.php index 492e2b49..4b02d52b 100644 --- a/services/nginx/app/routes/orderInvoicesRoute.php +++ b/services/nginx/app/routes/orderInvoicesRoute.php @@ -306,7 +306,7 @@ class orderInvoicesRoute }, $users_o->forceRestrictFilters( [ - 'active_invoices' => 'NOT ZERO', + //'active_invoices' => 'NOT ZERO', ] ) ); @@ -349,7 +349,7 @@ class orderInvoicesRoute }, $users_o->forceRestrictFilters( [ - 'active_invoices' => 'NOT ZERO', + //'active_invoices' => 'NOT ZERO', ] ) ); diff --git a/services/nginx/app/routes/ordersRoute.php b/services/nginx/app/routes/ordersRoute.php index 64f37091..3551d352 100644 --- a/services/nginx/app/routes/ordersRoute.php +++ b/services/nginx/app/routes/ordersRoute.php @@ -33,6 +33,7 @@ class ordersRoute $economic_module_orders = new economic_module_orders(); $orders = new orders_o(); // Return the list of departments + $orders->setView('orders_with_invoice_collections'); $response->success( $orders->listObjectsWithPaginationIfSet( function ($order) { diff --git a/services/nginx/app/traits/db_object_t.php b/services/nginx/app/traits/db_object_t.php index 0b8f027b..6f74bd32 100644 --- a/services/nginx/app/traits/db_object_t.php +++ b/services/nginx/app/traits/db_object_t.php @@ -220,7 +220,6 @@ trait db_object_t foreach ( $temp as $key => $value ) { if (!is_array($value) && strtolower($value) === 'null') { $temp[$key] = null; - continue; } } $filters = $temp; @@ -319,7 +318,7 @@ trait db_object_t continue; } // If the value is null, add a where clause to check if the field is null - if ($value === null || (is_string($value) && strtolower($value) === 'null')) { + if ($value === null || (is_string($value) && strtolower($value) === 'null') || (is_string($value) && strtolower($value === 'is_null'))) { $whereClauses[] = "$field IS NULL"; continue; } @@ -335,6 +334,78 @@ trait db_object_t } $whereClauses[] = "`$field` = ?"; $params[] = $value; + } else { + // Add support for 'date_from' and 'date_to' filters + // Example of date_from: 'date_from:2023-01-01' + // Example of date_to: 'date_to:2023-12-31' + // In total the filter string would look like: 'created_at-date_from:2023-01-01,created_at-date_to:2023-12-31' + if (preg_match('/^(.+?)-(date_from|date_to)/', $field, $matches)) { + $fieldName = $matches[1]; + $filterType = $matches[2]; + $dateValue = $value; + // If the (date_to|date_from) is E.g. '2023-12-31', then we need to include the entire from/to date + // If the date is to be inclusive, we need to add 1 day to the date + if ($filterType === 'date_to') { + $dateValue = date('Y-m-d', strtotime($dateValue . ' +1 day')); + } + + if (in_array($fieldName, $fields)) { + if ($filterType === 'date_from') { + $whereClauses[] = "`$fieldName` >= ?"; + } elseif ($filterType === 'date_to') { + $whereClauses[] = "`$fieldName` <= ?"; + } + $params[] = $dateValue; + } + } elseif (preg_match('/^(.+?)-has_attribute/', $field, $matches)) { + // If the filter is e.g. customer_number-has_attribute:invoiceOrdersIndividually + // Get the field name and the attribute + $fieldName = $matches[1]; + // Set the attribute to the value, without the (optional) "!" prefix + $attribute = (string)str_replace('!', '', $value); + $state = !str_starts_with($value, '!'); + // Check if the field name is valid + if (!in_array($fieldName, $fields)) { + throw new Exception('Invalid filter: ' . $field); + } + $attributes = array(); + // Push the attribute to the array + $attributes[] = (string)$attribute; + // Get the customer numbers with the attribute + $tmp_customer_numbers_with_attribute = (new users_o())->getCustomerNumbersWithAttributes($attributes); + // If the state is true, add the customer numbers to the where clause + if ($state) { + $whereClauses[] = "$fieldName IN (" . implode(',', array_map('intval', $tmp_customer_numbers_with_attribute)) . ")"; + } else { + // If the state is false, add the customer numbers to the where clause + $whereClauses[] = "$fieldName NOT IN (" . implode(',', array_map('intval', $tmp_customer_numbers_with_attribute)) . ")"; + } + } elseif (preg_match('/^(.+?)-has_key/', $field, $matches)) { + // If the filter is e.g. customer_number-has_key:invoiceOrdersIndividually + // Get the field name and the key + $fieldName = $matches[1]; + // Set the key to the value, without the (optional) "!" prefix + $key = (string)str_replace('!', '', $value); + $state = !str_starts_with($value, '!'); + // Check if the field name is valid + if (!in_array($fieldName, $fields)) { + throw new Exception('Invalid filter: ' . $field); + } + $attributes = array(); + // Push the attribute to the array + $attributes[] = (string)$key; + // Get the customer numbers with the attribute + $tmp_customer_numbers_with_attribute = (new user_key_value_pairs_o())->getCustomerNumbersWithKey($attributes); + // If the state is true, add the customer numbers to the where clause + if ($state) { + $whereClauses[] = "$fieldName IN (" . implode(',', array_map('intval', $tmp_customer_numbers_with_attribute)) . ")"; + } else { + // If the state is false, add the customer numbers to the where clause + $whereClauses[] = "$fieldName NOT IN (" . implode(',', array_map('intval', $tmp_customer_numbers_with_attribute)) . ")"; + } + } else { + throw new Exception('Invalid filter: ' . $field); + } } } } diff --git a/services/nginx/nginx.conf b/services/nginx/nginx.conf index eba20470..24da1de0 100644 --- a/services/nginx/nginx.conf +++ b/services/nginx/nginx.conf @@ -72,6 +72,7 @@ http { include fastcgi_params; fastcgi_pass php:9000; fastcgi_index index.php; + fastcgi_read_timeout 600; fastcgi_param SCRIPT_FILENAME index.php; }