Add custom-only department pricing enforcement

This commit is contained in:
Jeppe Bundgaard
2026-07-06 10:15:11 +02:00
parent 3f41eebdf6
commit d47ea1d659
16 changed files with 393 additions and 34 deletions
@@ -3,6 +3,7 @@
namespace classes;
use mysqli;
use objects\products_o;
use objects\users_o;
class limited_backoffice_service
@@ -119,6 +120,7 @@ class limited_backoffice_service
public function __construct()
{
departments_schema_bootstrap::ensureTables();
limited_backoffice_schema_bootstrap::ensureTables();
}
@@ -194,7 +196,7 @@ class limited_backoffice_service
$in = implode(',', array_map('intval', $departmentIds));
$sql = "
SELECT `id`, `name`, `description`, `visible`, `archived`
SELECT `id`, `name`, `description`, `visible`, `archived`, `custom_pricing_only`
FROM `departments`
WHERE `id` IN ($in)
ORDER BY `order_priority` ASC, `name` ASC, `id` ASC
@@ -209,6 +211,7 @@ class limited_backoffice_service
'description' => (string)($row['description'] ?? ''),
'visible' => (bool)($row['visible'] ?? false),
'archived' => (bool)($row['archived'] ?? false),
'custom_pricing_only' => (bool)(int)($row['custom_pricing_only'] ?? 0),
], $rows);
}
@@ -224,8 +227,9 @@ class limited_backoffice_service
throw new limited_backoffice_exception('Department not found', 404);
}
$catalog = $this->departmentProductCatalog($departmentId);
if ($catalog['missing_products'] !== []) {
$customPricingOnly = (bool)($department['custom_pricing_only'] ?? false);
$catalog = $this->departmentProductCatalog($departmentId, $customPricingOnly);
if (!$customPricingOnly && $catalog['missing_products'] !== []) {
throw new limited_backoffice_exception('Department price setup is incomplete.', 409, [
'message' => 'Department price setup is incomplete.',
'code' => 'department_price_setup_required',
@@ -257,7 +261,8 @@ class limited_backoffice_service
throw new limited_backoffice_exception('Department not found', 404);
}
$catalog = $this->departmentProductCatalog($departmentId);
$customPricingOnly = (bool)($department['custom_pricing_only'] ?? false);
$catalog = $this->departmentProductCatalog($departmentId, $customPricingOnly);
if ($catalog['required_product_ids'] === []) {
throw new limited_backoffice_exception('Department has no products configured.', 409);
}
@@ -274,7 +279,7 @@ class limited_backoffice_service
sort($providedProductIds);
$missingProductIds = array_values(array_diff($requiredProductIds, $providedProductIds));
if ($missingProductIds !== []) {
if (!$customPricingOnly && $missingProductIds !== []) {
throw new limited_backoffice_exception('Price is required for every department product.', 400, [
'message' => 'Price is required for every department product.',
'missing_product_ids' => $missingProductIds,
@@ -606,13 +611,13 @@ class limited_backoffice_service
}
/**
* @return array{id:int,name:string,description:string}
* @return array{id:int,name:string,description:string,custom_pricing_only:bool}
*/
private function fetchDepartment(int $departmentId): ?array
{
global $db;
$statement = $this->mysqli()->prepare(
'SELECT `id`, `name`, `description` FROM `departments` WHERE `id` = ? LIMIT 1'
'SELECT `id`, `name`, `description`, `custom_pricing_only` FROM `departments` WHERE `id` = ? LIMIT 1'
);
if ($statement === false) {
throw new limited_backoffice_exception('Unable to load department.', 500);
@@ -631,13 +636,14 @@ class limited_backoffice_service
'id' => (int)$row['id'],
'name' => (string)$row['name'],
'description' => (string)($row['description'] ?? ''),
'custom_pricing_only' => (bool)(int)($row['custom_pricing_only'] ?? 0),
];
}
/**
* @return array{categories:array<int,array<string,mixed>>,missing_products:array<int,array<string,mixed>>,required_product_ids:array<int,int>}
*/
private function departmentProductCatalog(int $departmentId): array
private function departmentProductCatalog(int $departmentId, bool $customPricingOnly = false): array
{
global $db;
@@ -698,10 +704,12 @@ class limited_backoffice_service
'id' => $productId,
'name' => (string)$row['product_name'],
'description' => (string)($row['product_description'] ?? ''),
'price' => $row['department_price'] === null ? null : (int)$row['department_price'],
'price' => $row['department_price'] === null
? ($customPricingOnly ? products_o::CUSTOM_PRICING_MISSING_PRICE : null)
: (int)$row['department_price'],
];
if ($row['department_price_id'] === null) {
if ($row['department_price_id'] === null && !$customPricingOnly) {
$missing[] = [
'id' => $productId,
'name' => (string)$row['product_name'],