Add advanced target duration configurations and parsing logic to Goals module. Update Workfeed with new CompanyID config. Extend OpenAPI spec with detailed schema mappings and examples.
This commit is contained in:
+90
-7
@@ -8640,6 +8640,30 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/WorkfeedConfigListResponse'
|
||||
examples:
|
||||
default:
|
||||
summary: Workfeed module configuration
|
||||
value:
|
||||
success: true
|
||||
data:
|
||||
- module: workfeed
|
||||
variable: enabled
|
||||
type: bool
|
||||
value: true
|
||||
- module: workfeed
|
||||
variable: api_url
|
||||
type: string
|
||||
value: https://api.workfeed.io
|
||||
- module: workfeed
|
||||
variable: api_key
|
||||
type: string
|
||||
value: wf_live_xxxxxxxxxxxxxxxxx
|
||||
- module: workfeed
|
||||
variable: CompanyID
|
||||
type: string
|
||||
value: "123456"
|
||||
meta: []
|
||||
includes: []
|
||||
post:
|
||||
tags: [Config]
|
||||
summary: Update Workfeed config
|
||||
@@ -10106,18 +10130,56 @@ components:
|
||||
- type: string
|
||||
required: [module, variable, type, value]
|
||||
|
||||
WorkfeedConfigEntry:
|
||||
WorkfeedConfigEnabledEntry:
|
||||
type: object
|
||||
properties:
|
||||
module: { type: string, enum: [workfeed] }
|
||||
variable: { type: string, enum: [enabled, api_url, api_key] }
|
||||
type: { type: string, enum: [bool, string] }
|
||||
value:
|
||||
oneOf:
|
||||
- type: boolean
|
||||
- type: string
|
||||
variable: { type: string, enum: [enabled] }
|
||||
type: { type: string, enum: [bool] }
|
||||
value: { type: boolean }
|
||||
required: [module, variable, type, value]
|
||||
|
||||
WorkfeedConfigApiUrlEntry:
|
||||
type: object
|
||||
properties:
|
||||
module: { type: string, enum: [workfeed] }
|
||||
variable: { type: string, enum: [api_url] }
|
||||
type: { type: string, enum: [string] }
|
||||
value: { type: string, example: "https://api.workfeed.io" }
|
||||
required: [module, variable, type, value]
|
||||
|
||||
WorkfeedConfigApiKeyEntry:
|
||||
type: object
|
||||
properties:
|
||||
module: { type: string, enum: [workfeed] }
|
||||
variable: { type: string, enum: [api_key] }
|
||||
type: { type: string, enum: [string] }
|
||||
value: { type: string, example: "wf_live_xxxxxxxxxxxxxxxxx" }
|
||||
required: [module, variable, type, value]
|
||||
|
||||
WorkfeedConfigCompanyIdEntry:
|
||||
type: object
|
||||
properties:
|
||||
module: { type: string, enum: [workfeed] }
|
||||
variable: { type: string, enum: [CompanyID] }
|
||||
type: { type: string, enum: [string] }
|
||||
value: { type: string, example: "123456" }
|
||||
required: [module, variable, type, value]
|
||||
|
||||
WorkfeedConfigEntry:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/WorkfeedConfigEnabledEntry'
|
||||
- $ref: '#/components/schemas/WorkfeedConfigApiUrlEntry'
|
||||
- $ref: '#/components/schemas/WorkfeedConfigApiKeyEntry'
|
||||
- $ref: '#/components/schemas/WorkfeedConfigCompanyIdEntry'
|
||||
discriminator:
|
||||
propertyName: variable
|
||||
mapping:
|
||||
enabled: '#/components/schemas/WorkfeedConfigEnabledEntry'
|
||||
api_url: '#/components/schemas/WorkfeedConfigApiUrlEntry'
|
||||
api_key: '#/components/schemas/WorkfeedConfigApiKeyEntry'
|
||||
CompanyID: '#/components/schemas/WorkfeedConfigCompanyIdEntry'
|
||||
|
||||
GatewayApiConfigEntry:
|
||||
type: object
|
||||
properties:
|
||||
@@ -10319,6 +10381,27 @@ components:
|
||||
properties:
|
||||
data: { type: array, items: { $ref: '#/components/schemas/WorkfeedConfigEntry' } }
|
||||
required: [data]
|
||||
example:
|
||||
success: true
|
||||
data:
|
||||
- module: workfeed
|
||||
variable: enabled
|
||||
type: bool
|
||||
value: true
|
||||
- module: workfeed
|
||||
variable: api_url
|
||||
type: string
|
||||
value: https://api.workfeed.io
|
||||
- module: workfeed
|
||||
variable: api_key
|
||||
type: string
|
||||
value: wf_live_xxxxxxxxxxxxxxxxx
|
||||
- module: workfeed
|
||||
variable: CompanyID
|
||||
type: string
|
||||
value: "123456"
|
||||
meta: []
|
||||
includes: []
|
||||
|
||||
GatewayApiConfigListResponse:
|
||||
allOf:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace goals\classes;
|
||||
|
||||
use classes\db;
|
||||
use goals\helpers\goals_criteria_target_duration;
|
||||
use goals\helpers\goals_criteria_type;
|
||||
use goals\helpers\goals_criteria_progress_alert_frequency;
|
||||
use goals\helpers\goals_criteria_progress_alert_destination;
|
||||
@@ -87,6 +88,18 @@ class goals_criteria implements goals_criteria_i
|
||||
* @var array<int,float>
|
||||
*/
|
||||
public array $department_weekly_targets;
|
||||
/**
|
||||
* Optional advanced target duration mode.
|
||||
* When null, legacy target behavior is preserved.
|
||||
* @var goals_criteria_target_duration|null
|
||||
*/
|
||||
public ?goals_criteria_target_duration $target_duration = null;
|
||||
/**
|
||||
* Optional cadence amount for advanced target duration modes.
|
||||
* Used for WEEKS, MONTHS, YEARS. Ignored for ENTIRE_DURATION.
|
||||
* @var int|null
|
||||
*/
|
||||
public ?int $target_duration_every = null;
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
@@ -103,6 +116,8 @@ class goals_criteria implements goals_criteria_i
|
||||
$this->progress_alert_style = goals_criteria_progress_alert_style::NONE;
|
||||
$this->department_daily_targets = [];
|
||||
$this->department_weekly_targets = [];
|
||||
$this->target_duration = null;
|
||||
$this->target_duration_every = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,6 +149,31 @@ class goals_criteria implements goals_criteria_i
|
||||
}
|
||||
}
|
||||
|
||||
// Parse advanced target duration mode (ignore unknowns)
|
||||
$rawTargetDuration = null;
|
||||
if (isset($data['target_duration']) && is_string($data['target_duration'])) {
|
||||
$rawTargetDuration = $data['target_duration'];
|
||||
} elseif (isset($data['targetDuration']) && is_string($data['targetDuration'])) {
|
||||
$rawTargetDuration = $data['targetDuration'];
|
||||
}
|
||||
if (is_string($rawTargetDuration)) {
|
||||
$duration = goals_criteria_target_duration::tryFrom($rawTargetDuration);
|
||||
if ($duration !== null) {
|
||||
$criteria->target_duration = $duration;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse advanced target duration cadence value (>=1)
|
||||
$rawTargetDurationEvery = null;
|
||||
if (isset($data['target_duration_every'])) {
|
||||
$rawTargetDurationEvery = $data['target_duration_every'];
|
||||
} elseif (isset($data['targetDurationEvery'])) {
|
||||
$rawTargetDurationEvery = $data['targetDurationEvery'];
|
||||
}
|
||||
if (is_numeric($rawTargetDurationEvery)) {
|
||||
$criteria->target_duration_every = max(1, (int)$rawTargetDurationEvery);
|
||||
}
|
||||
|
||||
// Sanitize label: trim, strip tags, collapse whitespace, max length 255
|
||||
if (isset($data['label']) && is_string($data['label'])) {
|
||||
$label = trim($data['label']);
|
||||
@@ -277,6 +317,16 @@ class goals_criteria implements goals_criteria_i
|
||||
}
|
||||
}
|
||||
$criteria->department_daily_targets = $targets;
|
||||
} elseif (isset($data['departmentDailyTargets']) && is_array($data['departmentDailyTargets'])) {
|
||||
$targets = [];
|
||||
foreach ($data['departmentDailyTargets'] as $deptId => $target) {
|
||||
if (is_numeric($deptId) && is_numeric($target)) {
|
||||
$id = (int)$deptId;
|
||||
$t = max(0.0, (float)$target);
|
||||
$targets[$id] = $t;
|
||||
}
|
||||
}
|
||||
$criteria->department_daily_targets = $targets;
|
||||
}
|
||||
// Parse department weekly targets (expects object with department_id => target)
|
||||
if (isset($data['department_weekly_targets']) && is_array($data['department_weekly_targets'])) {
|
||||
@@ -447,6 +497,8 @@ class goals_criteria implements goals_criteria_i
|
||||
return [
|
||||
'type' => $this->type?->name ?? goals_criteria_type::NONE->name,
|
||||
'target' => $this->target ?? 0,
|
||||
'target_duration' => $this->target_duration?->name,
|
||||
'target_duration_every' => $this->target_duration_every,
|
||||
'label' => (string)$this->label,
|
||||
'start' => ($this->start instanceof \DateTimeInterface) ? $this->start->format(DATE_ATOM) : null,
|
||||
'end' => ($this->end instanceof \DateTimeInterface) ? $this->end->format(DATE_ATOM) : null,
|
||||
@@ -608,6 +660,11 @@ class goals_criteria implements goals_criteria_i
|
||||
return $this->getProgress();
|
||||
}
|
||||
|
||||
public function usesAdvancedTargetDuration(): bool
|
||||
{
|
||||
return $this->target_duration instanceof goals_criteria_target_duration;
|
||||
}
|
||||
|
||||
public function validateAndSanitize(): void
|
||||
{
|
||||
// Normalize custom daily targets map: ints and non-negative; filter to selected departments
|
||||
@@ -636,6 +693,20 @@ class goals_criteria implements goals_criteria_i
|
||||
$this->target = 0;
|
||||
}
|
||||
|
||||
// Normalize advanced target duration values
|
||||
if ($this->target_duration !== null && !in_array($this->target_duration, goals_criteria_target_duration::cases(), true)) {
|
||||
$this->target_duration = null;
|
||||
}
|
||||
if ($this->target_duration === goals_criteria_target_duration::ENTIRE_DURATION) {
|
||||
$this->target_duration_every = null;
|
||||
} elseif ($this->target_duration !== null) {
|
||||
$every = (int)($this->target_duration_every ?? 1);
|
||||
$this->target_duration_every = max(1, $every);
|
||||
} else {
|
||||
// Strict legacy compatibility branch marker: null duration means old behavior.
|
||||
$this->target_duration_every = null;
|
||||
}
|
||||
|
||||
// Ensure timeframe is valid
|
||||
if (($this->start instanceof \DateTimeInterface) && ($this->end instanceof \DateTimeInterface)) {
|
||||
if ($this->end < $this->start) {
|
||||
@@ -751,6 +822,21 @@ class goals_criteria implements goals_criteria_i
|
||||
{
|
||||
$results = [];
|
||||
$department_ids = $this->departments->listIDs();
|
||||
if ($this->usesAdvancedTargetDuration()) {
|
||||
foreach ($department_ids as $dept_id) {
|
||||
$deptId = (int)$dept_id;
|
||||
$results[$deptId] = [
|
||||
'all' => $this->getAdvancedProgressDetailsForDepartment($deptId, null, $department_ids),
|
||||
'today' => $this->getAdvancedProgressDetailsForDepartment($deptId, 'today', $department_ids),
|
||||
'week' => $this->getAdvancedProgressDetailsForDepartment($deptId, 'week', $department_ids),
|
||||
'month' => $this->getAdvancedProgressDetailsForDepartment($deptId, 'month', $department_ids),
|
||||
'year' => $this->getAdvancedProgressDetailsForDepartment($deptId, 'year', $department_ids),
|
||||
'to_date' => $this->getAdvancedProgressDetailsForDepartment($deptId, 'to_date', $department_ids),
|
||||
];
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
foreach ($department_ids as $dept_id) {
|
||||
$dept_criteria = clone $this;
|
||||
$dept_criteria->departments->set([(new \objects\departments_o())->select($dept_id)]);
|
||||
@@ -787,6 +873,25 @@ class goals_criteria implements goals_criteria_i
|
||||
$criteria->setTimeframeByName($timeframe);
|
||||
}
|
||||
|
||||
if ($this->usesAdvancedTargetDuration()) {
|
||||
$target = 0.0;
|
||||
if (($criteria->start instanceof \DateTimeInterface) && ($criteria->end instanceof \DateTimeInterface)) {
|
||||
$target = $this->calculateTargetForRange(
|
||||
$criteria->start,
|
||||
$criteria->end,
|
||||
null,
|
||||
$this->departments->listIDs()
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'count' => $criteria->getProgress(),
|
||||
'target' => $target,
|
||||
'date_from' => ($criteria->start instanceof \DateTimeInterface) ? $criteria->start->format(DATE_ATOM) : null,
|
||||
'date_end' => ($criteria->end instanceof \DateTimeInterface) ? $criteria->end->format(DATE_ATOM) : null,
|
||||
];
|
||||
}
|
||||
|
||||
$target = 0.0;
|
||||
$active_dept_ids = $criteria->departments->listIDs();
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace goals\helpers;
|
||||
|
||||
enum goals_criteria_target_duration
|
||||
{
|
||||
case ENTIRE_DURATION;
|
||||
case WEEKS;
|
||||
case MONTHS;
|
||||
case YEARS;
|
||||
|
||||
public static function tryFrom(string $param): ?goals_criteria_target_duration
|
||||
{
|
||||
return match (strtoupper($param)) {
|
||||
'ENTIRE_DURATION' => goals_criteria_target_duration::ENTIRE_DURATION,
|
||||
'WEEKS' => goals_criteria_target_duration::WEEKS,
|
||||
'MONTHS' => goals_criteria_target_duration::MONTHS,
|
||||
'YEARS' => goals_criteria_target_duration::YEARS,
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
public function equals(goals_criteria_target_duration $param): bool
|
||||
{
|
||||
return $this === $param;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace workfeed\config;
|
||||
|
||||
use Exception;
|
||||
use traits\module_config_variable;
|
||||
|
||||
class workfeed_company_id_c
|
||||
{
|
||||
use module_config_variable;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
self::setupConfigVariable(
|
||||
'workfeed',
|
||||
'CompanyID',
|
||||
'string',
|
||||
false,
|
||||
null,
|
||||
'The Workfeed company identifier',
|
||||
'123456',
|
||||
false,
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,12 @@ namespace workfeed;
|
||||
require_once WD . '/modules/workfeed/config/workfeed_enabled_c.php';
|
||||
require_once WD . '/modules/workfeed/config/workfeed_api_url_c.php';
|
||||
require_once WD . '/modules/workfeed/config/workfeed_api_key_c.php';
|
||||
require_once WD . '/modules/workfeed/config/workfeed_company_id_c.php';
|
||||
|
||||
use traits\module_config_t;
|
||||
use workfeed\config\workfeed_api_key_c;
|
||||
use workfeed\config\workfeed_api_url_c;
|
||||
use workfeed\config\workfeed_company_id_c;
|
||||
use workfeed\config\workfeed_enabled_c;
|
||||
|
||||
class workfeed_c
|
||||
@@ -18,6 +20,7 @@ class workfeed_c
|
||||
public workfeed_enabled_c $enabled;
|
||||
public workfeed_api_url_c $api_url;
|
||||
public workfeed_api_key_c $api_key;
|
||||
public workfeed_company_id_c $company_id;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@@ -26,10 +29,12 @@ class workfeed_c
|
||||
workfeed_enabled_c::class,
|
||||
workfeed_api_url_c::class,
|
||||
workfeed_api_key_c::class,
|
||||
workfeed_company_id_c::class,
|
||||
]);
|
||||
|
||||
$this->enabled = new workfeed_enabled_c();
|
||||
$this->api_url = new workfeed_api_url_c();
|
||||
$this->api_key = new workfeed_api_key_c();
|
||||
$this->company_id = new workfeed_company_id_c();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user