Introduce modular goal criteria system for users, departments, and products

- Add `goals_criteria` base class implementing customizable goal criteria.
- Introduce interfaces and traits for modular handling of users, departments, and products in goal criteria.
- Include enumeration for goal types (`goals_criteria_type`).
- Implement timeframe and target management through traits.
- Provide initial configuration for enabling the goals module.
This commit is contained in:
Jeppe Bundgaard
2026-01-26 16:01:47 +01:00
parent e1e6d9c911
commit 4bfda5aea4
18 changed files with 658 additions and 0 deletions
@@ -0,0 +1,46 @@
<?php
namespace goals\classes;
use goals\helpers\goals_criteria_type;
use goals\interfaces\goals_criteria_i;
use goals\traits\goals_result_parser_t;
use goals\traits\goals_target_t;
use goals\traits\goals_timeframe_t;
class goals_criteria implements goals_criteria_i
{
use goals_timeframe_t,
goals_target_t,
goals_result_parser_t;
/**
* The users criteria
* @var goals_criteria_users $users
*/
public goals_criteria_users $users;
/**
* The departments criteria
* @var goals_criteria_departments $departments
*/
public goals_criteria_departments $departments;
/**
* Criteria type (products sold, etc.)
* @var goals_criteria_type $type
*/
public goals_criteria_type $type = goals_criteria_type::NONE;
/**
* Products criteria
* @var goals_criteria_products|null $products
*/
public ?goals_criteria_products $products;
/**
* Constructor
*/
public function __construct()
{
$this->users = new goals_criteria_users();
$this->departments = new goals_criteria_departments();
$this->products = new goals_criteria_products();
}
}
@@ -0,0 +1,12 @@
<?php
namespace goals\classes;
use goals\interfaces\goals_criteria_departments_i;
use goals\traits\goals_criteria_departments_t;
use objects\departments_o;
class goals_criteria_departments implements goals_criteria_departments_i
{
use goals_criteria_departments_t;
}
@@ -0,0 +1,40 @@
<?php
namespace goals\classes;
use goals\interfaces\goals_criteria_products_i;
use goals\traits\goals_criteria_products_t;
use objects\order_items_o;
class goals_criteria_products implements goals_criteria_products_i
{
use goals_criteria_products_t;
/**
* Get the sold items count based on the criteria.
* @param goals_criteria $goal
* @return int
*/
public function count(goals_criteria $goal): int
{
$count = 0;
$product_ids = $this->listIDs();
$department_ids = $goal->departments->listIDs();
$customer_numbers = $goal->users->listCustomerNumbers();
$datetime_start = $goal->start;
$datetime_end = $goal->end;
$items = order_items_o::getListByCriteria(
department_ids: $department_ids,
product_ids: $product_ids,
customer_numbers: $customer_numbers,
datetime_start: $datetime_start,
datetime_end: $datetime_end,
);
foreach ($items as $item) {
$count += $item->quantity;
}
return $count;
}
}
@@ -0,0 +1,13 @@
<?php
namespace goals\classes;
use goals\interfaces\goals_criteria_users_i;
use goals\traits\goals_criteria_users_t;
use objects\users_o;
class goals_criteria_users implements goals_criteria_users_i
{
use goals_criteria_users_t;
}
@@ -0,0 +1,29 @@
<?php
namespace goals\config;
use Exception;
use traits\module_config_variable;
class goals_enabled_c
{
use module_config_variable;
/**
* @throws Exception
*/
public function __construct()
{
self::setupConfigVariable(
'goals',
'enabled',
'bool',
true,
null,
'Whether the goals module is enabled or not',
'1',
false,
'false'
);
}
}
@@ -0,0 +1,28 @@
<?php
namespace goals;
require_once WD . '/modules/goals/config/goals_enabled_c.php';
// Require helpers
use goals\config\goals_enabled_c;
use traits\module_config_t;
class goals_c
{
use module_config_t;
/**
* The status of the goals module
* @var goals_enabled_c
*/
public goals_enabled_c $enabled;
public function __construct()
{
$this->setupConfig('goals');
$this->allowUpdate([
goals_enabled_c::class,
]);
$this->enabled = new goals_enabled_c();
}
}
@@ -0,0 +1,23 @@
<?php
namespace goals\helpers;
enum goals_criteria_type
{
case PRODUCT; // When the goal is related to a product
case NONE; // When there is no specific criteria
public static function tryFrom(string $param): ?goals_criteria_type
{
return match (strtoupper($param)) {
'PRODUCT' => goals_criteria_type::PRODUCT,
'NONE' => goals_criteria_type::NONE,
default => null,
};
}
public function equals(goals_criteria_type $param): bool
{
return $this === $param;
}
}
@@ -0,0 +1,42 @@
<?php
namespace goals\interfaces;
use objects\department_lanes_o;
use objects\departments_o;
interface goals_criteria_departments_i
{
/**
* Set the departments to be included in the criteria
* @param departments_o[] $departments The departments to be included in the criteria
*/
public function set(array $departments): self;
/**
* Get the departments included in the criteria
* @return departments_o[] The departments included in the criteria
*/
public function list(): array;
/**
* Get the department IDs included in the criteria
* @return int[] The department IDs included in the criteria
*/
public function listIDs(): array;
/**
* Add department(s) to the criteria
* @param departments_o|departments_o[] $departments The department(s) to add to the criteria
* @return self
*/
public function add(departments_o|array $departments): self;
/**
* Clear all departments from the criteria
* @return self
*/
public function clear(): self;
/**
* Remove department(s) from the criteria
* @param departments_o|departments_o[] $departments The department(s) to remove from the criteria
* @return self
*/
public function remove(departments_o|array $departments): self;
}
@@ -0,0 +1,11 @@
<?php
namespace goals\interfaces;
use objects\department_lanes_o;
use objects\departments_o;
use objects\users_o;
interface goals_criteria_i
{
}
@@ -0,0 +1,43 @@
<?php
namespace goals\interfaces;
use objects\department_lanes_o;
use objects\departments_o;
use objects\products_o;
interface goals_criteria_products_i
{
/**
* Set the products to be included in the criteria
* @param products_o[] $products The products / customers to be included in the criteria
*/
public function set(array $products): self;
/**
* Get the products included in the criteria
* @return products_o[] The products / customers included in the criteria
*/
public function list(): array;
/**
* Get the product IDs included in the criteria
* @return int[] The product IDs included in the criteria
*/
public function listIDs(): array;
/**
* Add user(s) to the criteria
* @param products_o|products_o[] $products The user(s) to add to the criteria
* @return self
*/
public function add(products_o|array $products): self;
/**
* Clear all products from the criteria
* @return self
*/
public function clear(): self;
/**
* Remove user(s) from the criteria
* @param products_o|products_o[] $products The user(s) to remove from the criteria
* @return self
*/
public function remove(products_o|array $products): self;
}
@@ -0,0 +1,43 @@
<?php
namespace goals\interfaces;
use objects\department_lanes_o;
use objects\departments_o;
use objects\users_o;
interface goals_criteria_users_i
{
/**
* Set the users to be included in the criteria
* @param users_o[] $users The Users / customers to be included in the criteria
*/
public function set(array $users): self;
/**
* Get the users included in the criteria
* @return users_o[] The Users / customers included in the criteria
*/
public function list(): array;
/**
* Get the customer numbers of the users included in the criteria
* @return int[] The customer numbers of the users included in the criteria
*/
public function listCustomerNumbers(): array;
/**
* Add user(s) to the criteria
* @param users_o|users_o[] $users The user(s) to add to the criteria
* @return self
*/
public function add(users_o|array $users): self;
/**
* Clear all users from the criteria
* @return self
*/
public function clear(): self;
/**
* Remove user(s) from the criteria
* @param users_o|users_o[] $users The user(s) to remove from the criteria
* @return self
*/
public function remove(users_o|array $users): self;
}
@@ -0,0 +1,14 @@
<?php
namespace goals\interfaces;
use objects\departments_o;
interface goals_result_parser_i
{
/**
* Get the product count based on the criteria.
*
* @return int
*/
}
@@ -0,0 +1,79 @@
<?php
namespace goals\traits;
use objects\departments_o;
trait goals_criteria_departments_t
{
/**
* @var departments_o[] The Departments to be included in the criteria
*/
protected array $list = [];
/**
* @inheritDoc
*/
public function set(array $departments): self
{
$this->list = $departments;
return $this;
}
/**
* @inheritDoc
*/
public function list(): array
{
return $this->list;
}
/**
* @inheritDoc
*/
public function listIDs(): array
{
return array_map(function ($department) {
return $department->id;
}, $this->list);
}
/**
* @inheritDoc
*/
public function add(array|departments_o $departments): self
{
if (is_array($departments)) {
$this->list = array_merge($this->list, $departments);
} else {
$this->list[] = $departments;
}
return $this;
}
/**
* @inheritDoc
*/
public function clear(): self
{
$this->list = [];
return $this;
}
/**
* @inheritDoc
*/
public function remove(array|departments_o $departments): self
{
if (is_array($departments)) {
$this->list = array_filter($this->list, function ($dept) use ($departments) {
return !in_array($dept, $departments, true);
});
} else {
$this->list = array_filter($this->list, function ($dept) use ($departments) {
return $dept !== $departments;
});
}
return $this;
}
}
@@ -0,0 +1,79 @@
<?php
namespace goals\traits;
use objects\products_o;
trait goals_criteria_products_t
{
/**
* @var products_o[] The Products to be included in the criteria
*/
protected array $list = [];
/**
* @inheritDoc
*/
public function set(array $Products): self
{
$this->list = $Products;
return $this;
}
/**
* @inheritDoc
*/
public function list(): array
{
return $this->list;
}
/**
* @inheritdoc
*/
public function listIDs(): array
{
return array_map(function ($product) {
return $product->id;
}, $this->list);
}
/**
* @inheritDoc
*/
public function add(array|products_o $Products): self
{
if (is_array($Products)) {
$this->list = array_merge($this->list, $Products);
} else {
$this->list[] = $Products;
}
return $this;
}
/**
* @inheritDoc
*/
public function clear(): self
{
$this->list = [];
return $this;
}
/**
* @inheritDoc
*/
public function remove(array|products_o $Products): self
{
if (is_array($Products)) {
$this->list = array_filter($this->list, function ($dept) use ($Products) {
return !in_array($dept, $Products, true);
});
} else {
$this->list = array_filter($this->list, function ($dept) use ($Products) {
return $dept !== $Products;
});
}
return $this;
}
}
@@ -0,0 +1,81 @@
<?php
namespace goals\traits;
use objects\users_o;
trait goals_criteria_users_t
{
/**
* @var users_o[] The Users / customers to be included in the criteria
*/
protected array $list = [];
/**
* @inheritDoc
*/
public function set(array $users): self
{
$this->list = $users;
return $this;
}
/**
* @inheritDoc
*/
public function list(): array
{
return $this->list;
}
/**
* @inheritDoc
*/
public function listCustomerNumbers(): array
{
return array_map(
function (users_o $user) {
return (int)$user->customer_number->value();
},
$this->list()
);
}
/**
* @inheritDoc
*/
public function add(users_o|array $users): self
{
if (is_array($users)) {
$this->list = array_merge($this->list, $users);
} else {
$this->list[] = $users;
}
return $this;
}
/**
* @inheritDoc
*/
public function clear(): self
{
$this->list = [];
return $this;
}
/**
* @inheritDoc
*/
public function remove(users_o|array $users): self
{
if (is_array($users)) {
$this->list = array_filter($this->list, function ($user) use ($users) {
return !in_array($user, $users, true);
});
} else {
$this->list = array_filter($this->list, function ($user) use ($users) {
return $user !== $users;
});
}
return $this;
}
}
@@ -0,0 +1,10 @@
<?php
namespace goals\traits;
use objects\departments_o;
use objects\products_o;
trait goals_result_parser_t
{
}
@@ -0,0 +1,31 @@
<?php
namespace goals\traits;
trait goals_target_t
{
/**
* Target (Goal target value)
* @var float $target
*/
public float $target = 0.0;
/**
* Set the target value
* @param float|int $value
* @return void
*/
public function setTarget(float|int $value): void
{
$this->target = (float)$value;
}
/**
* Get the target value
* @return float
*/
public function getTarget(): float
{
return $this->target;
}
}
@@ -0,0 +1,34 @@
<?php
namespace goals\traits;
use DateTime;
trait goals_timeframe_t
{
/**
* The start time of the timeframe.
*
* @var datetime|null
*/
public ?datetime $start = null;
/**
* The end time of the timeframe.
*
* @var datetime|null
*/
public ?datetime $end = null;
/**
* Sets the timeframe for the goal.
*
* @param datetime|null $start The start time of the timeframe.
* @param datetime|null $end The end time of the timeframe.
* @return void
*/
public function setTimeframe(?datetime $start = null, ?datetime $end = null): void
{
$this->start = $start;
$this->end = $end;
}
}