diff --git a/services/nginx/app/classes/economic.php b/services/nginx/app/classes/economic.php index 1643a709..98553350 100644 --- a/services/nginx/app/classes/economic.php +++ b/services/nginx/app/classes/economic.php @@ -6,8 +6,11 @@ require_once WD . '/modules/economic/endpoints/economic_orders_endpoint.php'; require_once WD . '/modules/economic/endpoints/economic_invoices_endpoint.php'; require_once WD . '/modules/economic/endpoints/economic_departments_endpoint.php'; require_once WD . '/modules/economic/endpoints/economic_layouts_endpoint.php'; +require_once WD . '/modules/economic/endpoints/economic_customers_endpoint.php'; + use economic_c; +use endpoints\economic_customers_endpoint; use endpoints\economic_departments_endpoint; use endpoints\economic_invoices_endpoint; use endpoints\economic_layouts_endpoint; @@ -41,6 +44,11 @@ class economic implements economic_i * @var economic_layouts_endpoint */ public economic_layouts_endpoint $layouts; + /** + * Any endpoints reached by the /customers endpoint + * @var economic_customers_endpoint + */ + public economic_customers_endpoint $customers; public function __construct() @@ -50,5 +58,6 @@ class economic implements economic_i $this->invoices = new economic_invoices_endpoint(); $this->departments = new economic_departments_endpoint(); $this->layouts = new economic_layouts_endpoint(); + $this->customers = new economic_customers_endpoint(); } } \ No newline at end of file diff --git a/services/nginx/app/classes/response.php b/services/nginx/app/classes/response.php index 4945bf0b..81ef9d97 100644 --- a/services/nginx/app/classes/response.php +++ b/services/nginx/app/classes/response.php @@ -128,7 +128,7 @@ class response implements response_i $this->data[$key] = $value; } - public function getRequestParameter(string $key): string|null + public function getRequestParameter(string $key): mixed { // Get the request data if the method is POST, PUT or PATCH if ($_SERVER['REQUEST_METHOD'] === 'POST' || $_SERVER['REQUEST_METHOD'] === 'PUT' || $_SERVER['REQUEST_METHOD'] === 'PATCH') { diff --git a/services/nginx/app/modules/economic/customers/economicCustomers.php b/services/nginx/app/modules/economic/customers/economicCustomers.php index bcc69ecf..d5cc76e8 100644 --- a/services/nginx/app/modules/economic/customers/economicCustomers.php +++ b/services/nginx/app/modules/economic/customers/economicCustomers.php @@ -3,6 +3,7 @@ namespace customers; use economic_m; +use Exception; use objects\users_o; class economicCustomers extends economic_m @@ -91,4 +92,62 @@ class economicCustomers extends economic_m $response = json_decode($response); return $response; } + + /** + * Get the list of customers + * @param int $page + * @param int $limit + * @param string|null $search + * @return object The list of customers + * @throws Exception + */ + public function listCustomers(int $page, int $limit, string|null $search = null): object + { + // Normalize pagination parameters + $page = max(1, $page); // Ensure it's at least 1 + $skipPages = $page - 1; + + // Construct the base URL with pagination + $url = '/customers?pagesize=' . $limit . '&skippages=' . $skipPages; + + // Define filterable property groups + $likeSupported = [ + 'zip', 'customerNumber', 'customerGroup.customerGroupNumber', 'name', 'address', + 'city', 'country', 'email', 'telephoneAndFaxNumber', 'website', 'mobilePhone' + ]; + + // If a search term is present, build the filter expressions + if (!empty($search)) { + // Escape special characters in the search string + $escapedSearch = str_replace( + ['$', '(', ')', '*', ',', '[', ']'], + ['$$', '$(', '$)', '$*', '$,', '$[', '$]'], + $search + ); + + // Build $like filters + $filters = []; + foreach ( $likeSupported as $property ) { + $filters[] = $property . '$like:' . $escapedSearch; + } + + // Join the filters with `$or:` + $filterString = implode('$or:', $filters); + + // URL encode and append the filter string + $url .= '&filter=' . urlencode($filterString); + } + + // Send the GET request to the API endpoint + $response = $this->send_request($url, 'GET', ''); + + // Validate and decode the response safely + $responseObject = json_decode($response); + if ($responseObject === null) { + throw new Exception('Failed to decode the response from the API'); + } + return $responseObject; + } + + } \ No newline at end of file diff --git a/services/nginx/app/modules/economic/endpoints/customers/economic_customers_endpoint.php b/services/nginx/app/modules/economic/endpoints/customers/economic_customers_endpoint.php new file mode 100644 index 00000000..4c551901 --- /dev/null +++ b/services/nginx/app/modules/economic/endpoints/customers/economic_customers_endpoint.php @@ -0,0 +1,40 @@ +send_request( + '/customers/' . $customer_number, + 'GET'); + // Return the response as an object + return json_decode($response); + } + + /** + * Does a customer exist? + * @param int $customer_number + * @return bool + */ + public function exists(int $customer_number): bool + { + $response = $this->send_request( + '/customers/' . $customer_number, + 'GET'); + $tmp = json_decode($response); + // Return true if the customer exists + return isset($tmp->customerNumber); + } + +} \ No newline at end of file diff --git a/services/nginx/app/modules/economic/endpoints/economic_customers_endpoint.php b/services/nginx/app/modules/economic/endpoints/economic_customers_endpoint.php new file mode 100644 index 00000000..c02ebb27 --- /dev/null +++ b/services/nginx/app/modules/economic/endpoints/economic_customers_endpoint.php @@ -0,0 +1,23 @@ +customers = new customers\economic_customers_endpoint(); + } + +} \ 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 dc52fc09..9a033c8f 100644 --- a/services/nginx/app/objects/users_o.php +++ b/services/nginx/app/objects/users_o.php @@ -842,4 +842,20 @@ class users_o extends db $this->getCustomerEcocomicData($user['customer_number']); } } + + public function isImportedFromEconomic($customerNumber): bool + { + // Check if the user is imported from the external source + if (self::countRowsWhere(['customer_number' => $customerNumber]) > 0) { + return true; + } + return false; + } + + public function getUserIdFromEconomic($customerNumber): int + { + // Get the user id from the external source + $user = self::getFieldsWhere(['customer_number' => $customerNumber], ['id']); + return $user[0]['id']; + } } \ No newline at end of file diff --git a/services/nginx/app/routes/customerSearchRoute.php b/services/nginx/app/routes/customerSearchRoute.php index a6e8bf04..768d87c3 100644 --- a/services/nginx/app/routes/customerSearchRoute.php +++ b/services/nginx/app/routes/customerSearchRoute.php @@ -5,6 +5,7 @@ namespace routes; use classes\authentication; use customers\economicCustomers; use objects\logs_o; +use objects\users_o; use traits\route_t; class customerSearchRoute @@ -47,5 +48,67 @@ class customerSearchRoute $response->error('Invalid session', 400); } }); + + self::get('/customers', function () { + // Require the user to be logged in + global $response; + self::requirePermission('list_customers'); + // Get the user object + $user = (new authentication())->get_user(); + // Check if the request was successful + if ($user) { + // Check if the pagination parameters are set + $page = self::fromRequest('page') ?? 1; + $limit = self::fromRequest('limit') ?? 10; + $search = self::fromRequest('search') ?? null; + $filter = self::fromRequest('filter') ?? null; + // Log the incident + (new logs_o())->add('customers', 'global', 1, $user->id, 'LIST_CUSTOMERS', 'Successfully listed customers'); + // Create the economic customers object + $economicCustomers = new economicCustomers(); + $result = (object)$economicCustomers->listCustomers( + (int)$page, + (int)$limit, + $search, + $filter + ); + // Parse the pagination meta from E-conomic to the standard format used in this application + $response->paginate( + $page, + $limit, + $result->pagination->results, + $search, + $filter + ); + // Create the users object + $users_o = new users_o(); + // Return the list of users + $response->success( + self::parseFunction($result->collection, function ($customer) use ($users_o) { + // Add the user id to the customer object + $customer->id = null; + // Update the user id if the customer is imported from E-conomic + if ($users_o->isImportedFromEconomic($customer->customerNumber)) { + $customer->id = $users_o->getUserIdFromEconomic($customer->customerNumber); + } + return $customer; + }) + ); + } else { + // Log the incident + (new logs_o())->add('customers', 'global', 1, 0, 'LIST_CUSTOMERS', 'No user found, or invalid session'); + // Return an error + $response->error('Invalid session', 400); + } + }); + } + + private static function parseFunction($collection, \Closure $param): array + { + $result = []; + foreach ( $collection as $item ) { + $result[] = $param($item); + } + return $result; } } \ No newline at end of file diff --git a/services/nginx/app/routes/moduleEconomicRoute.php b/services/nginx/app/routes/moduleEconomicRoute.php new file mode 100644 index 00000000..574d7772 --- /dev/null +++ b/services/nginx/app/routes/moduleEconomicRoute.php @@ -0,0 +1,61 @@ + Customers > Import customer */ + $this->post('/economic/customers/import', function () { + global $response; + $this->requirePermission('economic_import_customer'); + $user = (new authentication())->get_user(); + if ($user) { + // Check if the customer number is set + self::requireParameters(['customer_number']); + self::requireType(self::getParameter('customer_number'), self::TYPE_INT()); + $users_o = new users_o(); + // Check if the customer is already imported + $isCustomerImported = $users_o->isImportedFromEconomic(self::getParameter('customer_number')); + if ($isCustomerImported) { + $response->error('Customer already imported', 400); + } + // Check if the customer exists in the economic system + $economic = new economic(); + $doesCustomerExists = $economic->customers->customers->exists(self::getParameter('customer_number')); + if (!$doesCustomerExists) { + $response->error('Customer does not exist', 400); + } + // Import the customer + $customer = $users_o->getUserByCustomerNumber(self::getParameter('customer_number')); + // Make sure the user returned is valid + if (!$customer->exists()) { + $response->error('Unable to import customer', 400); + } + (new logs_o())->add('economic', 'global', 1, $user->id, 'ECONOMIC_IMPORT_CUSTOMER', 'Successfully imported customer'); + $response->success( + $customer->asArray() + ); + } else { + (new logs_o())->add('economic', 'global', 1, 0, 'ECONOMIC_IMPORT_CUSTOMER', 'No user found, or invalid session'); + $response->error('Invalid session', 400); + } + }); + } +} \ 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 8986dbe7..d7b4968a 100644 --- a/services/nginx/app/traits/db_object_t.php +++ b/services/nginx/app/traits/db_object_t.php @@ -43,6 +43,35 @@ trait db_object_t // Define the table and fields of the objects in the database } + public function countRowsWhere(array $fieldsAndValues): int + { + global $db; + $table = $this->table; + $where = []; + foreach ( $fieldsAndValues as $field => $value ) { + $where[] = "$field = '$value'"; + } + $where = implode(' AND ', $where); + $sql = "SELECT COUNT(*) AS count FROM $table WHERE $where"; + $result = $db->query($sql); + $row = $db->fetch_assoc($result); + return $row['count']; + } + + public function getFieldsWhere(array $fieldsAndValues, array $fields): array + { + global $db; + $table = $this->table; + $where = []; + foreach ( $fieldsAndValues as $field => $value ) { + $where[] = "$field = '$value'"; + } + $where = implode(' AND ', $where); + $sql = "SELECT " . implode(', ', $fields) . " FROM $table WHERE $where"; + $result = $db->query($sql); + return $db->fetch_all($result); + } + /** * Set the searchable fields * @param array $fields The fields to search in the database (e.g. ['name', 'email']) diff --git a/services/nginx/app/traits/route_t.php b/services/nginx/app/traits/route_t.php index 20769e15..76d61199 100644 --- a/services/nginx/app/traits/route_t.php +++ b/services/nginx/app/traits/route_t.php @@ -20,6 +20,41 @@ trait route_t // Add the routes here } + /** + * Require type + * @param mixed $value + * @param string $type + * @return bool + */ + public function requireType(mixed $value, string $type): bool + { + // Get the type of the value + $value_type = gettype($value); + // Check if the value is of the required type + if ($value_type !== $type) { + global $response; + $response->error('Invalid type. Expected: ' . $type . ' Got: ' . $value_type, 400); + } + return true; + } + + public function type_int(): string + { + return 'integer'; + } + + /** + * Get the parameter from the request by name + * This is a shorthand for the response class method + * @param string $parameter + * @return mixed|null + */ + public function getParameter(string $parameter): mixed + { + global $response; + return $response->getRequestParameter($parameter); + } + public function requireParameters(array $parameters): void { global $response;