Add customer listing and import functionality for Economic
Implemented endpoints to list and import customers from the Economic API, including query filtering, pagination, and validation. Added supporting trait methods, database functions, and error handling. Expanded the user object for Economic integration and introduced utilities for type checking and parameter extraction.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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') {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace endpoints\customers;
|
||||
|
||||
use traits\economic_endpoint_t;
|
||||
|
||||
class economic_customers_endpoint
|
||||
{
|
||||
use economic_endpoint_t;
|
||||
|
||||
/**
|
||||
* Get a customer by customer number
|
||||
* @param int $customer_number
|
||||
* @return object
|
||||
*/
|
||||
public function get(int $customer_number): object
|
||||
{
|
||||
$response = $this->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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace endpoints;
|
||||
require_once WD . '/modules/economic/endpoints/customers/economic_customers_endpoint.php';
|
||||
|
||||
use traits\economic_endpoint_t;
|
||||
|
||||
class economic_customers_endpoint
|
||||
{
|
||||
use economic_endpoint_t;
|
||||
|
||||
/**
|
||||
* Any endpoints reached by the /customers endpoint
|
||||
* @var customers\economic_customers_endpoint
|
||||
*/
|
||||
public customers\economic_customers_endpoint $customers;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->customers = new customers\economic_customers_endpoint();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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'];
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace routes;
|
||||
|
||||
use classes\authentication;
|
||||
use classes\economic;
|
||||
use classes\response;
|
||||
use classes\router;
|
||||
use objects\logs_o;
|
||||
use objects\users_o;
|
||||
use traits\route_t;
|
||||
|
||||
class moduleEconomicRoute
|
||||
{
|
||||
use route_t;
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
global /** @var response $response */
|
||||
/** @var router $router */
|
||||
$router, $response;
|
||||
|
||||
|
||||
/** Economic > 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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'])
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user