531 lines
26 KiB
PHP
531 lines
26 KiB
PHP
<?php
|
|
|
|
namespace routes;
|
|
|
|
use classes\authentication;
|
|
use objects\categories_o;
|
|
use objects\department_categories_o;
|
|
use objects\departments_o;
|
|
use objects\logs_o;
|
|
use objects\product_options_o;
|
|
use objects\products_o;
|
|
use objects\users_o;
|
|
use traits\route_t;
|
|
|
|
class productsRoute
|
|
{
|
|
use route_t;
|
|
|
|
/**
|
|
* Get the customer object if the customer_id parameter is provided (In the request 'customer_id')
|
|
* @return users_o|null
|
|
*/
|
|
private function getCustomerIfProvided(bool $restrictToOwnCustomer = false): ?users_o
|
|
{
|
|
$customerId = $this->getOptionalPositiveIntParameter('customer_id');
|
|
if ($customerId === null) {
|
|
return null;
|
|
}
|
|
|
|
if ($restrictToOwnCustomer && !$this->isOwnCustomerContext($customerId)) {
|
|
$this->emitForbidden(['list_products']);
|
|
}
|
|
|
|
try {
|
|
$customerObject = (new users_o())->getUserByCustomerNumber($customerId);
|
|
if ($customerObject->exists()) {
|
|
return $customerObject;
|
|
}
|
|
} catch (\Exception $e) {
|
|
// Log the incident
|
|
(new logs_o())->add('products', 'global', 3, 0, 'GET_CUSTOMER_FAILED', 'Failed to get customer with id ' . $customerId . '. Error: ' . $e->getMessage());
|
|
// Return null
|
|
return null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the department id if the department_id parameter is provided (In the request 'department_id')
|
|
* @return int|null
|
|
*/
|
|
private function getDepartmentIdIfProvided(): ?int
|
|
{
|
|
return $this->getOptionalPositiveIntParameter('department_id');
|
|
}
|
|
|
|
private function getOptionalPositiveIntParameter(string $parameter): ?int
|
|
{
|
|
global $response;
|
|
if (!self::isParametersSet([$parameter])) {
|
|
return null;
|
|
}
|
|
|
|
$value = self::getParameter($parameter);
|
|
if ($this->isNullLikeOptionalParameter($value)) {
|
|
return null;
|
|
}
|
|
|
|
$parsed = null;
|
|
if (is_int($value)) {
|
|
$parsed = $value;
|
|
} elseif (is_string($value) && preg_match('/^\d+$/', trim($value)) === 1) {
|
|
$parsed = (int)trim($value);
|
|
} else {
|
|
$response->error('Invalid ' . $parameter, 400);
|
|
}
|
|
|
|
if ($parsed === null || $parsed <= 0) {
|
|
$response->error('Invalid ' . $parameter, 400);
|
|
}
|
|
|
|
return $parsed;
|
|
}
|
|
|
|
private function isNullLikeOptionalParameter(mixed $value): bool
|
|
{
|
|
if ($value === null) {
|
|
return true;
|
|
}
|
|
|
|
if (!is_string($value)) {
|
|
return false;
|
|
}
|
|
|
|
return in_array(strtolower(trim($value)), ['', 'null', 'undefined'], true);
|
|
}
|
|
|
|
private function isCustomerBookingSession(bool $hasAuthenticatedUser, bool $hasCustomerPermission, bool $isSubuserSession): bool
|
|
{
|
|
return ($hasAuthenticatedUser && $hasCustomerPermission) || $isSubuserSession;
|
|
}
|
|
|
|
private function canReadProductList(bool $isCustomerBookingSession, bool $hasListProductsPermission): bool
|
|
{
|
|
return $isCustomerBookingSession || $hasListProductsPermission;
|
|
}
|
|
|
|
private function shouldRestrictCustomerBookingProducts(bool $isCustomerBookingSession, bool $hasListProductsPermission): bool
|
|
{
|
|
return $isCustomerBookingSession && !$hasListProductsPermission;
|
|
}
|
|
|
|
private function isBookingVisibleProduct(array $product): bool
|
|
{
|
|
return (bool)($product['display_in_booking_form'] ?? false);
|
|
}
|
|
|
|
private function filterProductsVisibleOnBookingForm(array $products): array
|
|
{
|
|
return array_values(array_filter($products, function ($product): bool {
|
|
return is_array($product) && $this->isBookingVisibleProduct($product);
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Get the category (ID) if the category parameter is provided (In the request 'category')
|
|
* @return int|null
|
|
*/
|
|
private function getCategoryIfProvided(): ?int
|
|
{
|
|
return $this->getOptionalPositiveIntParameter('category');
|
|
}
|
|
|
|
/**
|
|
* Get the product id if the id parameter is provided (In the request 'id')
|
|
* @return int|null
|
|
*/
|
|
private function getProductIdIfProvided(): ?int
|
|
{
|
|
return $this->getOptionalPositiveIntParameter('id');
|
|
}
|
|
|
|
/**
|
|
* Parse the products price in the (optional) department pricing, with the (optional) customer discounts applied
|
|
* @param array $products
|
|
* @param users_o|null $customer
|
|
* @param int|null $departmentId
|
|
* @return array
|
|
*/
|
|
private function parseProductsPrice(array $products, ?users_o $customer, ?int $departmentId): array
|
|
{
|
|
// Check if the departmentId is set
|
|
if ($departmentId) {
|
|
// Apply the departments unique pricing
|
|
$products = (new products_o())->applyDepartmentPricing($products, $departmentId, true);
|
|
}
|
|
// Check if the customer is set
|
|
if ($customer !== null) {
|
|
// Apply the customers unique discounts
|
|
$products = (new products_o())->applyCustomerDiscounts($products, $customer, $departmentId);
|
|
} else {
|
|
$products = products_o::stripDepartmentPriceSources($products);
|
|
}
|
|
return $products;
|
|
}
|
|
|
|
/**
|
|
* Parse the addons/options price in the (optional) department pricing, with the (optional) customer discounts applied
|
|
* @param array $options
|
|
* @param users_o|null $customer
|
|
* @param int|null $departmentId
|
|
* @return array
|
|
*/
|
|
private function parseOptionsPrice(array $options, ?users_o $customer, ?int $departmentId): array
|
|
{
|
|
foreach ($options as $key => $option) {
|
|
// Get the options product
|
|
$product = $option['product'];
|
|
// Apply the department pricing and customer discounts to the product
|
|
$product = self::parseProductsPrice([$product], $customer, $departmentId)[0];
|
|
// Update the option with the new product
|
|
$options[$key]['product'] = $product;
|
|
// Set the price of the option to the price of the product
|
|
$options[$key]['price'] = (int)$product['price'];
|
|
}
|
|
return $options;
|
|
}
|
|
|
|
|
|
|
|
public function run(): void
|
|
{
|
|
$this->get('/products', function () {
|
|
// Check if the user is logged in
|
|
global $response;
|
|
$permission_node = 'list_products';
|
|
$isProductDetailsRestricted = true;
|
|
$auth = new authentication();
|
|
$user = $auth->get_user();
|
|
$subuser = $auth->get_subuser();
|
|
$hasAuthenticatedUser = $user !== false && $user !== null;
|
|
$isSubuserSession = $subuser !== false;
|
|
$hasCustomerPermission = $hasAuthenticatedUser ? self::hasPermission('user') : false;
|
|
$isCustomerBookingSession = $this->isCustomerBookingSession($hasAuthenticatedUser, $hasCustomerPermission, $isSubuserSession);
|
|
$hasListProductsPermission = $hasAuthenticatedUser ? self::hasPermission($permission_node) : false;
|
|
if ($hasAuthenticatedUser || $isSubuserSession) {
|
|
$isProductDetailsRestricted = false;
|
|
if (!$this->canReadProductList($isCustomerBookingSession, $hasListProductsPermission)) {
|
|
$this->emitForbidden([$permission_node]);
|
|
}
|
|
}
|
|
// Set the user id to 0 if guest
|
|
$responsibleUserId = $hasAuthenticatedUser ? (int)$user->id : 0;
|
|
$parseProduct = function ($product, $isGuest, $onlyBookingVisible = false): array {
|
|
$addons = (new product_options_o())->getProductOptions($product['id']);
|
|
if ($onlyBookingVisible) {
|
|
$addons = array_values(array_filter($addons, function ($option): bool {
|
|
return (bool)($option['product']['display_in_booking_form'] ?? false);
|
|
}));
|
|
}
|
|
|
|
$tmpProduct = [
|
|
'id' => (int)$product['id'],
|
|
'name' => (string)$product['name'],
|
|
'description' => (string)$product['description'],
|
|
'price' => (int)$product['price'],
|
|
'subscription_allowed' => (boolean)$product['subscription_allowed'],
|
|
'category' => (int)$product['category'],
|
|
'piktogram' => (string)$product['piktogram'],
|
|
'economic_product_id' => (int)$product['economic_product_id'],
|
|
'apply_category_discount' => (boolean)$product['apply_category_discount'],
|
|
'requires_note' => \objects\products_o::productDataRequiresOrderItemNote($product),
|
|
'created_at' => (string)$product['created_at'],
|
|
'updated_at' => (string)$product['updated_at'],
|
|
'addons' => $addons,
|
|
'is_wash' => (bool)$product['is_wash'],
|
|
'display_in_booking_form' => (bool)$product['display_in_booking_form'],
|
|
'order_priority' => (int)$product['order_priority'],
|
|
];
|
|
/** Apply guest filters and redactions, if any */
|
|
if ($isGuest) {
|
|
|
|
$tmpProductGuest = [
|
|
...$tmpProduct,
|
|
'name' => $tmpProduct['display_in_booking_form'] ? $tmpProduct['name'] : 'Login to view',
|
|
'description' => '',
|
|
'price' => 0,
|
|
'economic_product_id' => 0,
|
|
'apply_category_discount' => false,
|
|
'requires_note' => false,
|
|
'addons' => $tmpProduct['display_in_booking_form'] ?
|
|
array_map(function ($option) {
|
|
if ($option['product']['display_in_booking_form'] === false) {
|
|
$option['product']['name'] = 'Login to view';
|
|
$option['product']['description'] = '';
|
|
$option['product']['economic_product_id'] = 0;
|
|
$option['product']['apply_category_discount'] = false;
|
|
$option['product']['requires_note'] = false;
|
|
$option['product']['restricted'] = true;
|
|
}
|
|
$option['price'] = 0;
|
|
$option['product']['price'] = 0;
|
|
$option['restricted'] = $option['product']['restricted'] ?? false;
|
|
return $option;
|
|
}, $tmpProduct['addons']) : [],
|
|
'restricted' => !$tmpProduct['display_in_booking_form']
|
|
];
|
|
}
|
|
return $isGuest ? $tmpProductGuest : $tmpProduct;
|
|
};
|
|
|
|
// Check if the request was successful
|
|
if ($hasAuthenticatedUser || $isSubuserSession || $isProductDetailsRestricted) {
|
|
// Define the variables
|
|
$restrictCustomerBookingProducts = $this->shouldRestrictCustomerBookingProducts($isCustomerBookingSession, $hasListProductsPermission);
|
|
$customer = $this->getCustomerIfProvided($restrictCustomerBookingProducts); // This is only used if the customer_id parameter is provided
|
|
$departmentId = $this->getDepartmentIdIfProvided(); // This is only used if the department_id parameter is provided
|
|
$category = $this->getCategoryIfProvided(); // This is only used if the category parameter is provided (ID of the category)
|
|
$productId = $this->getProductIdIfProvided(); // This is only used if the id parameter is provided (ID of the product)
|
|
$useFinalPrice = self::isParametersSet(['final_price']) && self::getParameter('final_price') === 'true';
|
|
// Check if the "final_price" parameter is set, and true.
|
|
if ($useFinalPrice) {
|
|
// Determine the products to return
|
|
if ($category) {
|
|
// Get products in the category
|
|
$products = (new products_o())->listObjectsByCategory($category);
|
|
// If the category is "6" exterior washes, include category "1" and "3"
|
|
if ($category === 6) {
|
|
$additionalProducts = (new products_o())->listObjectsByCategory(1);
|
|
$products = array_merge($products, $additionalProducts);
|
|
$additionalProducts = (new products_o())->listObjectsByCategory(3);
|
|
$products = array_merge($products, $additionalProducts);
|
|
}
|
|
} elseif ($productId) {
|
|
// Get the specific product
|
|
$product = (new products_o())->select($productId);
|
|
if ($product->exists()) {
|
|
$products = [$product->asArray()];
|
|
} else {
|
|
$products = [];
|
|
}
|
|
} else {
|
|
// Get all products
|
|
$products = (array)(new products_o())->listObjectsWithPaginationIfSet(
|
|
function ($product) use ($isProductDetailsRestricted, $parseProduct) {
|
|
return $parseProduct($product, $isProductDetailsRestricted);
|
|
}
|
|
);
|
|
}
|
|
if ($restrictCustomerBookingProducts) {
|
|
$products = $this->filterProductsVisibleOnBookingForm($products);
|
|
}
|
|
// Return all products, with the department pricing and customer discounts applied
|
|
//$response->success(
|
|
// array_map(function ($product) {
|
|
// return parseProduct($product);
|
|
// }, self::parseProductsPrice($products, $customer, $departmentId))
|
|
//);
|
|
$result = array_map(function ($product) use ($customer, $departmentId, $isProductDetailsRestricted, $restrictCustomerBookingProducts, $parseProduct) {
|
|
$productArray = $parseProduct($product, $isProductDetailsRestricted, $restrictCustomerBookingProducts);
|
|
// Get the price of the product with the department pricing and customer discounts applied
|
|
$productArray = $parseProduct(self::parseProductsPrice([$productArray], $customer, $departmentId)[0], $isProductDetailsRestricted, $restrictCustomerBookingProducts);
|
|
// Get the options for the product
|
|
$productArray['addons'] = self::parseOptionsPrice($productArray['addons'], $customer, $departmentId);
|
|
// Return the product with the updated price
|
|
return $productArray;
|
|
}, $products);
|
|
// If the productId is set, return a single object instead of an array
|
|
$response->success($productId && count($result) > 0 ? $result[0] : $result);
|
|
}
|
|
// Check if the id is set in the request (to get a specific product)
|
|
if (self::isParametersSet(['id'])) {
|
|
// Log the incident
|
|
(new logs_o())->add('products', 'global', 1, $responsibleUserId, 'LIST_PRODUCTS', 'Successfully listed product with id ' . $response->getRequestParameter('id'));
|
|
// Return the product
|
|
$product = (new products_o())->select((int)self::getParameter('id'))->asArray();
|
|
if ($restrictCustomerBookingProducts && !$this->isBookingVisibleProduct($product)) {
|
|
$response->success([]);
|
|
}
|
|
$response->success(
|
|
$parseProduct(
|
|
$product, $isProductDetailsRestricted, $restrictCustomerBookingProducts
|
|
)
|
|
);
|
|
}
|
|
// Check if the category is set in the request
|
|
// Check if the category is set
|
|
if ($category !== null) {
|
|
// Log the incident
|
|
(new logs_o())->add('products', 'global', 1, $responsibleUserId, 'LIST_PRODUCTS', 'Successfully listed products in category ' . $category);
|
|
// Return the list of products
|
|
$products = (new products_o())->listObjectsByCategory($category);
|
|
if ($restrictCustomerBookingProducts) {
|
|
$products = $this->filterProductsVisibleOnBookingForm($products);
|
|
}
|
|
// Check if the department_id is set
|
|
if ($departmentId !== null) {
|
|
// Apply the departments unique pricing
|
|
$products = (new products_o())->applyDepartmentPricing((array)$products, $departmentId);
|
|
}
|
|
$response->success(
|
|
array_map(function ($product) use ($isProductDetailsRestricted, $restrictCustomerBookingProducts, $parseProduct) {
|
|
return $parseProduct($product, $isProductDetailsRestricted, $restrictCustomerBookingProducts);
|
|
}, $products)
|
|
);
|
|
}
|
|
// Log the incident
|
|
(new logs_o())->add('products', 'global', 1, $responsibleUserId, 'LIST_PRODUCTS', 'Successfully listed products');
|
|
// Check if the department_id is set
|
|
if ($departmentId !== null) {
|
|
// Get all product ids contained in a category attached to the department
|
|
$departmentSpecificProducts = (new departments_o())->select($departmentId)->getAllProductInDepartmentCategories();
|
|
// Get the product ids as an array
|
|
$departmentSpecificProductIds = array_map(function ($product) {
|
|
return $product->id;
|
|
}, $departmentSpecificProducts);
|
|
$products = (array)(new products_o())->listObjectsWithPaginationIfSet(
|
|
function ($product) use ($isProductDetailsRestricted, $restrictCustomerBookingProducts, $parseProduct) {
|
|
// Only include products that are in the department specific product ids
|
|
return $parseProduct($product, $isProductDetailsRestricted, $restrictCustomerBookingProducts);
|
|
},
|
|
(new products_o())->forceRestrictFilters([
|
|
'id' => $departmentSpecificProductIds,
|
|
])
|
|
);
|
|
if ($restrictCustomerBookingProducts) {
|
|
$products = $this->filterProductsVisibleOnBookingForm($products);
|
|
}
|
|
// Return the list of products
|
|
$response->success(
|
|
(new products_o())->applyDepartmentPricing($products, $departmentId)
|
|
);
|
|
}
|
|
// Return the list of products
|
|
$products = (new products_o())->listObjectsWithPaginationIfSet(function ($product) use ($isProductDetailsRestricted, $restrictCustomerBookingProducts, $parseProduct) {
|
|
return $parseProduct($product, $isProductDetailsRestricted, $restrictCustomerBookingProducts);
|
|
});
|
|
if ($restrictCustomerBookingProducts) {
|
|
$products = $this->filterProductsVisibleOnBookingForm((array)$products);
|
|
}
|
|
$response->success($products);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('products', 'global', 1, 0, 'LIST_PRODUCTS', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'list_products' => 'List all products. Authenticated customer booking sessions may read booking-visible products without the permission.'
|
|
]
|
|
);
|
|
|
|
$this->post('/products', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('add_product');
|
|
// Get the user object
|
|
$user = (new authentication())->get_user();
|
|
// Check if the request was successful
|
|
if ($user) {
|
|
// Get the request data
|
|
$name = $response->getRequestParameter('name');
|
|
$description = $response->getRequestParameter('description') ?? null;
|
|
$price = $response->getRequestParameter('price');
|
|
$category = $response->getRequestParameter('category') ?? null;
|
|
$piktogram = $response->getRequestParameter('piktogram') ?? null;
|
|
$economicProductId = $response->getRequestParameter('economicProductId') ?? null;
|
|
// Check if the required fields are set
|
|
self::requireParameters(
|
|
[
|
|
'name',
|
|
'price'
|
|
]
|
|
);
|
|
// Add the product
|
|
(new products_o())->add(
|
|
(string)$name,
|
|
(string)$description ?? '',
|
|
(int)$price,
|
|
(int)$category ?? '',
|
|
(int)$piktogram ?? null,
|
|
(int)$economicProductId ?? null
|
|
);
|
|
// Log the incident
|
|
(new logs_o())->add('products', 'global', 1, $user->id, 'ADD_PRODUCT', 'Product name: ' . $name);
|
|
// Return a success message
|
|
$response->success(['message' => 'Product added successfully']);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('products', 'global', 1, 0, 'ADD_PRODUCT', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'add_product' => 'Add a product'
|
|
]
|
|
);
|
|
|
|
$this->put('/products', function () {
|
|
// Require the user to be logged in
|
|
global $response;
|
|
$this->requirePermission('edit_product');
|
|
// Get the user object
|
|
$user = (new authentication())->get_user();
|
|
// Check if the request was successful
|
|
if ($user) {
|
|
// Check if the required fields are set
|
|
self::requireParameters(['id']);
|
|
$id = $response->getRequestParameter('id');
|
|
$product = (new products_o())->select((int)$id);
|
|
// Check if the product exists
|
|
if (!$product->exists()) {
|
|
$response->error('Product not found', 404);
|
|
}
|
|
// Update the fields that are set
|
|
if (self::isParametersSet(['name'])) {
|
|
$product->name->set($response->getRequestParameter('name'));
|
|
}
|
|
if (self::isParametersSet(['description'])) {
|
|
$product->description->set($response->getRequestParameter('description') ?? '');
|
|
}
|
|
if (self::isParametersSet(['price'])) {
|
|
$product->price->set($response->getRequestParameter('price'));
|
|
}
|
|
if (self::isParametersSet(['subscription_allowed'])) {
|
|
$product->subscription_allowed->set($response->getRequestParameter('subscription_allowed') ? 1 : 0);
|
|
}
|
|
if (self::isParametersSet(['category'])) {
|
|
$product->category->set($response->getRequestParameter('category') ?? '');
|
|
}
|
|
if (self::isParametersSet(['piktogram'])) {
|
|
$product->piktogram->set($response->getRequestParameter('piktogram') ?? '');
|
|
}
|
|
if (self::isParametersSet(['economic_product_id'])) {
|
|
$product->economic_product_id->set($response->getRequestParameter('economic_product_id') ?? '');
|
|
}
|
|
if (self::isParametersSet(['apply_category_discount'])) {
|
|
$product->apply_category_discount->set($response->getRequestParameter('apply_category_discount') ? 1 : 0);
|
|
}
|
|
if (self::isParametersSet(['requires_note'])) {
|
|
$product->requires_note->set($response->getRequestParameter('requires_note') ? 1 : 0);
|
|
}
|
|
if (self::isParametersSet(['is_wash'])) {
|
|
$product->is_wash->set($response->getRequestParameter('is_wash') ? 1 : 0);
|
|
}
|
|
if (self::isParametersSet(['display_in_booking_form'])) {
|
|
$product->display_in_booking_form->set($response->getRequestParameter('display_in_booking_form') ? 1 : 0);
|
|
}
|
|
if (self::isParametersSet(['order_priority'])) {
|
|
$product->order_priority->set((int)$response->getRequestParameter('order_priority') ?? '');
|
|
}
|
|
(new logs_o())->add('products', 'global', 1, $user->id, 'EDIT_PRODUCT', 'Product id: ' . $id);
|
|
// Return a success message
|
|
$response->success(['message' => 'Product edited successfully']);
|
|
} else {
|
|
// Log the incident
|
|
(new logs_o())->add('products', 'global', 1, 0, 'EDIT_PRODUCT', 'No user found, or invalid session');
|
|
// Return an error
|
|
$response->error('Invalid session', 400);
|
|
}
|
|
},
|
|
[
|
|
'edit_product' => 'Edit a product'
|
|
]
|
|
);
|
|
}
|
|
}
|