253 lines
9.1 KiB
PHP
253 lines
9.1 KiB
PHP
<?php
|
|
|
|
namespace customers;
|
|
|
|
use economic_m;
|
|
use Exception;
|
|
use objects\users_o;
|
|
|
|
class economicCustomers extends economic_m
|
|
{
|
|
public users_o $users_o;
|
|
|
|
public function searchCustomers(string|int $search, string $filter, int $limit = 10, int $page = 1): object
|
|
{
|
|
// Make sure the search string is ready for the API
|
|
$search = urlencode($search);
|
|
// Search for customers
|
|
$url = '/customers?filter=' . $filter . '$like:' . $search . '&pagesize=' . $limit . '&skippages=' . ($page - 1);
|
|
$response = $this->send_request($url, 'GET', '');
|
|
return json_decode($response);
|
|
}
|
|
|
|
public function getCustomerName(int $customer_number): string|null
|
|
{
|
|
// Check if the customer name is cached
|
|
$tmp_user = self::getUsersObject()->getUserByCustomerNumber($customer_number);
|
|
$cached = $tmp_user->getCached('economic_customer');
|
|
if ($cached) {
|
|
return $cached->name;
|
|
}
|
|
// Get the customer name from the economic system
|
|
$tmp_user->getCustomerEcocomicData($customer_number);
|
|
$cached = $tmp_user->getCached('economic_customer');
|
|
if ($cached) {
|
|
return $cached->name;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function getUsersObject(): users_o
|
|
{
|
|
if (!isset($this->users_o)) {
|
|
$this->users_o = new users_o();
|
|
}
|
|
return $this->users_o;
|
|
}
|
|
|
|
public function getCustomerId(int $customerNumber): object|bool
|
|
{
|
|
if ($customerNumber <= 0) {
|
|
return false;
|
|
}
|
|
|
|
// Check if the customer exists
|
|
$url = '/customers/' . $customerNumber;
|
|
$response = $this->send_request($url, 'GET', '');
|
|
$response = json_decode($response);
|
|
|
|
return isset($response->customerNumber) ? $response : false;
|
|
}
|
|
|
|
public function getCustomerProduct(int $customer_number, int $product_id): object
|
|
{
|
|
// Get the customer product
|
|
$url = '/customers/' . $customer_number . '/templates/invoiceline/' . $product_id;
|
|
$response = $this->send_request($url, 'GET', '');
|
|
return json_decode($response);
|
|
}
|
|
|
|
public function getCustomerDiscountPercentage(int $customer_number): int
|
|
{
|
|
// If the customer number is 0, return 0
|
|
if ($customer_number === 0) {
|
|
return 0;
|
|
}
|
|
// The discount is global, but e-conomic resolves it through a product-specific
|
|
// invoice-line template. For foreign-currency customers some templates can fail
|
|
// if that product has no price in the customer currency, so try a few products
|
|
// before falling back to zero.
|
|
$products = $this->getCustomerProducts($customer_number, 10);
|
|
foreach ($this->extractCustomerProductNumbers($products) as $product_number) {
|
|
try {
|
|
$discount = $this->getCustomerProductDiscount($customer_number, $product_number);
|
|
return (int)($discount->discountPercentage ?? 0);
|
|
} catch (\RuntimeException $exception) {
|
|
if (!$this->isMissingCurrencyPriceLookupError($exception)) {
|
|
throw $exception;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @return int[]
|
|
*/
|
|
private function extractCustomerProductNumbers(object $products): array
|
|
{
|
|
if (!isset($products->collection) || !is_array($products->collection)) {
|
|
return [];
|
|
}
|
|
|
|
$product_numbers = [];
|
|
foreach ( $products->collection as $product ) {
|
|
$product_number = $product->product->productNumber ?? null;
|
|
if ($product_number === null || $product_number === '') {
|
|
continue;
|
|
}
|
|
$product_numbers[] = (int)$product_number;
|
|
}
|
|
|
|
return array_values(array_unique($product_numbers));
|
|
}
|
|
|
|
private function isMissingCurrencyPriceLookupError(\RuntimeException $exception): bool
|
|
{
|
|
$message = $exception->getMessage();
|
|
|
|
return str_contains($message, 'No price in currency')
|
|
&& str_contains($message, 'can be found for the product');
|
|
}
|
|
|
|
public function getCustomerProducts(int $customer_number, int $limit = 10, int $page = 1): object
|
|
{
|
|
// Get the customer products
|
|
$url = '/customers/' . $customer_number . '/templates/invoiceline/?pagesize=' . $limit . '&skippages=' . ($page - 1);
|
|
$response = $this->send_request($url, 'GET', '');
|
|
return json_decode($response);
|
|
}
|
|
|
|
public function getCustomerProductDiscount(int $customer_number, int $product_id)
|
|
{
|
|
// Get the customer global discount
|
|
$url = '/customers/' . $customer_number . '/templates/invoiceline/' . $product_id;
|
|
$response = $this->send_request($url, 'GET', '');
|
|
$response = json_decode($response);
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Get the list of customers
|
|
* @param int $page
|
|
* @param int $limit
|
|
* @param string|null $search
|
|
* @param mixed $barred_filter Supports true/false values (bool, 1/0, true/false, barred/active)
|
|
* @return object The list of customers
|
|
* @throws Exception
|
|
*/
|
|
public function listCustomers(int $page, int $limit, string|null $search = null, mixed $barred_filter = 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', 'corporateIdentificationNumber'
|
|
];
|
|
|
|
$filter_parts = [];
|
|
|
|
// 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:` and keep grouping explicit for later $and composition
|
|
$filter_parts[] = '(' . implode('$or:', $filters) . ')';
|
|
}
|
|
|
|
// Optional barred filter support (all | true/barred | false/active)
|
|
$normalized_barred_filter = $this->normalizeBarredFilter($barred_filter);
|
|
if ($normalized_barred_filter !== null) {
|
|
$filter_parts[] = 'barred$eq:' . ($normalized_barred_filter ? 'true' : 'false');
|
|
}
|
|
|
|
if (!empty($filter_parts)) {
|
|
$filter_string = count($filter_parts) === 1
|
|
? $filter_parts[0]
|
|
: '(' . implode('$and:', $filter_parts) . ')';
|
|
$url .= '&filter=' . urlencode($filter_string);
|
|
}
|
|
|
|
// 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');
|
|
}
|
|
|
|
if (!is_object($responseObject)) {
|
|
throw new \RuntimeException('Malformed e-conomic customers response: expected JSON object.');
|
|
}
|
|
|
|
if (!isset($responseObject->collection) || !is_array($responseObject->collection)) {
|
|
$upstreamMessage = isset($responseObject->message) && is_string($responseObject->message)
|
|
? trim($responseObject->message)
|
|
: 'Missing collection.';
|
|
throw new \RuntimeException('Malformed e-conomic customers response: ' . $upstreamMessage);
|
|
}
|
|
|
|
if (!isset($responseObject->pagination) || !is_object($responseObject->pagination)) {
|
|
throw new \RuntimeException('Malformed e-conomic customers response: missing pagination.');
|
|
}
|
|
|
|
if (!isset($responseObject->pagination->results) || !is_numeric($responseObject->pagination->results)) {
|
|
throw new \RuntimeException('Malformed e-conomic customers response: missing pagination results.');
|
|
}
|
|
|
|
$responseObject->pagination->results = (int)$responseObject->pagination->results;
|
|
|
|
return $responseObject;
|
|
}
|
|
|
|
private function normalizeBarredFilter(mixed $value): ?bool
|
|
{
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
if (is_bool($value)) {
|
|
return $value;
|
|
}
|
|
if (is_int($value)) {
|
|
return $value === 1 ? true : ($value === 0 ? false : null);
|
|
}
|
|
$parsed = strtolower(trim((string)$value));
|
|
return match ($parsed) {
|
|
'1', 'true', 'yes', 'barred', 'only_barred' => true,
|
|
'0', 'false', 'no', 'active', 'not_barred' => false,
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
|
|
}
|