From 9bf0355e4798d116bc25b2fda0fb987a699ac1ac Mon Sep 17 00:00:00 2001 From: Jepp9350 <2jepp9350@gmail.com> Date: Fri, 28 Feb 2025 12:07:46 +0100 Subject: [PATCH] Add routes and objects for managing collected order invoices Introduced GET and POST endpoints for collected order invoices, enabling listing and creation functionalities for authorized users. Added a new database object to handle invoice-related operations and validations. Included utility functions to enforce parameter requirements and data integrity. --- .../objects/collected_order_invoices_o.php | 130 ++++++++++++++++ .../nginx/app/routes/orderInvoicesRoute.php | 145 ++++++++++++++++++ services/nginx/app/traits/db_object_t.php | 1 + services/nginx/app/traits/route_t.php | 13 ++ 4 files changed, 289 insertions(+) create mode 100644 services/nginx/app/objects/collected_order_invoices_o.php create mode 100644 services/nginx/app/routes/orderInvoicesRoute.php diff --git a/services/nginx/app/objects/collected_order_invoices_o.php b/services/nginx/app/objects/collected_order_invoices_o.php new file mode 100644 index 00000000..ef119738 --- /dev/null +++ b/services/nginx/app/objects/collected_order_invoices_o.php @@ -0,0 +1,130 @@ +setTable('collected_order_invoices'); + } + + /** + * Create a new collected order invoice + * @param int $customer_number The E-conomic customer number + * @param string|null $name The name of the invoice (optional) + * @param string|null $notes The notes for the invoice (optional) + * @param int|null $processor The processor id (optional) + * @return self The created object + * @throws Exception If the object was not created successfully + */ + public function add(int $customer_number, ?string $name = null, ?string $notes = null, ?int $processor = null): self + { + global /** @var db $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); + // 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 + ]); + self::select((int)$tmp_id); + self::requireSelected(); + self::objectChanged(); + return $this; + } + + /** + * Require the E-conomic customer number to be a valid customer + * @throws Exception If the customer number is not a valid customer + */ + private static function requireValidCustomer(string $customer_number): void + { + $customers = new users_o(); + $customers->select($customer_number); + $customers->requireSelected(); + } + + public function objectChanged(): void + { + //TODO: Add cache invalidation + } + + /** + * List all collected order invoices for a customer + * @param int $customer_number The E-conomic customer number + * @return array The list of collected order invoices + * @throws Exception If the request was not successful + */ + public function getCustomerInvoiceCollections(int $customer_number): array + { + $collections = self::getFieldsWhere( + [ + 'customer_number' => $customer_number, + 'deleted_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; + } + + public function getObjectProperties(): void + { + $this->customer_number = new object_property($this->table, $this->id, 'customer_number', 'int', false); + $this->name = new object_property($this->table, $this->id, 'name', 'string', false); + $this->notes = new object_property($this->table, $this->id, 'notes', 'string', false); + $this->processor = new object_property($this->table, $this->id, 'processor', 'int', false); + $this->external_id = new object_property($this->table, $this->id, 'external_id', 'string', false); + $this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false); + $this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'string', false); + $this->closed_at = new object_property($this->table, $this->id, 'closed_at', 'string', false); + } + + public function asArray(): array + { + return [ + 'id' => (int)$this->id, + 'customer_number' => (int)$this->customer_number->value(), + 'name' => (string)$this->name->value(), + 'notes' => (string)$this->notes->value(), + 'processor' => (int)$this->processor->value(), + 'external_id' => (string)$this->external_id->value(), + 'created_at' => (string)$this->created_at->value(), + 'updated_at' => (string)$this->updated_at->value(), + 'closed_at' => (string)$this->closed_at->value(), + ]; + } + +} \ No newline at end of file diff --git a/services/nginx/app/routes/orderInvoicesRoute.php b/services/nginx/app/routes/orderInvoicesRoute.php new file mode 100644 index 00000000..67de9112 --- /dev/null +++ b/services/nginx/app/routes/orderInvoicesRoute.php @@ -0,0 +1,145 @@ + GET */ + $this->get('/collected-invoices', function () { + global $response; + self::requirePermission('list_collected_invoices'); + $user = (new authentication())->get_user(); + if ($user) { + (new logs_o())->add('orderInvoices', 'global', 1, $user->id, 'LIST_COLLECTED_INVOICES', 'User accessed the list of collected order invoices'); + $collected_order_invoices = new collected_order_invoices_o(); + // Check if the request contains an ID + if (self::isParametersSet(['id'])) { + self::requireType((int)self::getParameter('id'), self::type_int()); + $collected_order_invoices->select((int)self::getParameter('id')); + $collected_order_invoices->requireSelected(); + $response->success($collected_order_invoices->asArray()); + } + // Return the list of collected order invoices + $response->success($collected_order_invoices->listObjectsWithPaginationIfSet( + function ($collected_order_invoice) { + return [ + 'id' => (int)$collected_order_invoice['id'], + 'name' => (string)$collected_order_invoice['name'], + 'notes' => (string)$collected_order_invoice['notes'], + 'customer_number' => (int)$collected_order_invoice['customer_number'], + 'processor' => $collected_order_invoice['processor'] ? (int)$collected_order_invoice['processor'] : null, + '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'] + ]; + } + )); + } else { + (new logs_o())->add('orderInvoices', 'global', 0, 0, 'LIST_COLLECTED_INVOICES', 'User tried to access the list of collected order invoices without a valid session'); + $response->error('Invalid session', 400); + } + }, + [ + 'list_collected_invoices' => 'List ALL collected order invoices. This is a superuser-only route.' + ] + ); + + /** Collected order invoices > POST */ + $this->post('/collected-invoices', function () { + global $response; + self::requirePermission('add_collected_invoice'); + $user = (new authentication())->get_user(); + if ($user) { + (new logs_o())->add('orderInvoices', 'global', 1, $user->id, 'ADD_COLLECTED_INVOICE', 'User added a collected order invoice'); + // Require the customer number, and validate its type and length + self::requireParameters(['customer_number']); + self::requireType((int)self::getParameter('customer_number'), 'int'); + self::requireMinLength('customer_number', 1); + self::requireMaxLength('customer_number', 10); + // Require the customer number to be above 0 + self::requireMinValue((int)self::getParameter('customer_number'), 1); + // Validate the customer number against the database + $customer = (new users_o())->select((int)self::getParameter('customer_number')); + $customer->requireSelected(); + // Define the variables + $name = null; + $notes = null; + $processor = null; + // Check if the name is set + if (self::isParametersSet(['name'])) { + self::requireType((string)self::getParameter('name'), 'string'); + self::requireMinLength('name', 1); + self::requireMaxLength('name', 255); + $name = (string)self::getParameter('name'); + } + // Check if the notes are set + if (self::isParametersSet(['notes'])) { + self::requireType((string)self::getParameter('notes'), 'string'); + $notes = (string)self::getParameter('notes'); + } + // Check if the processor is set + if (self::isParametersSet(['processor'])) { + self::requireType((int)self::getParameter('processor'), 'int'); + $processor = (int)self::getParameter('processor'); + } + // Add the collected order invoice + $collected_order_invoices = new collected_order_invoices_o(); + $collected_order_invoices->add( + $customer->id, + $name, + $notes, + $processor + ); + $response->success($collected_order_invoices->asArray()); + } else { + (new logs_o())->add('orderInvoices', 'global', 0, 0, 'ADD_COLLECTED_INVOICE', 'User tried to add a collected order invoice without a valid session'); + $response->error('Invalid session', 400); + } + }, + [ + 'add_collected_invoice' => 'Add a collected order invoice. This is a superuser-only route.' + ] + ); + + /** Modules > MotorAPI > Lookup > GET */ + $this->get('/modules/motorapi/lookup', function () { + global $response; + self::requirePermission('modules_motorapi_lookup'); + $user = (new authentication())->get_user(); + if ($user) { + self::requireParameters(['license_plate']); + self::requireType('license_plate', 'string'); + self::requireMinLength('license_plate', 1); + self::requireMaxLength('license_plate', 10); + (new logs_o())->add('modules_motorapi', 'global', 1, $user->id, 'MODULES_MOTORAPI', 'Successfully looked up license plate information'); + $result = (new motorapi())->getLicensePlateInformation($this->fromRequest('license_plate'), false); + $response->success((object)$result); + } else { + (new logs_o())->add('modules_motorapi', 'global', 1, 0, 'MODULES_MOTORAPI', 'No user found, or invalid session'); + $response->error('Invalid session', 400); + } + }, + [ + 'modules_motorapi_lookup' => 'Lookup license plate information' + ] + ); + + } +} \ No newline at end of file diff --git a/services/nginx/app/traits/db_object_t.php b/services/nginx/app/traits/db_object_t.php index fb176ead..51919151 100644 --- a/services/nginx/app/traits/db_object_t.php +++ b/services/nginx/app/traits/db_object_t.php @@ -737,6 +737,7 @@ trait db_object_t * Select an object by id * @param int $id The id of the object in the database * @returns $this + * @throws Exception If the object does not exist in the database, it throws an exception */ public function select(int $id): self { diff --git a/services/nginx/app/traits/route_t.php b/services/nginx/app/traits/route_t.php index afbdccbb..523ded9d 100644 --- a/services/nginx/app/traits/route_t.php +++ b/services/nginx/app/traits/route_t.php @@ -39,11 +39,24 @@ trait route_t return true; } + public function requireMinValue(int $value, int $min): void + { + global $response; + if ($value < $min) { + $response->error('Parameter must be at least ' . $min, 400); + } + } + public function type_int(): string { return 'integer'; } + public function type_string(): string + { + return 'string'; + } + public function requireParameters(array $parameters): void { global $response;