add recurrence limit + follow FinanceBuddy installments to stop finite plans (v1.6.1)
This commit is contained in:
@@ -121,6 +121,26 @@ class RecoRecoModel extends Base
|
||||
// Default off: only keep the duplicate link when explicitly turned on.
|
||||
$linkCopies = isset($meta['recoreco_link_copies']) && $meta['recoreco_link_copies'] == 1;
|
||||
|
||||
// Finite-plan stop. RecoReco caps its own recurrence: either by its own limit (standalone),
|
||||
// or by following FinanceBuddy's installment total. In follow mode both numbers are read LIVE
|
||||
// from FinanceBuddy (never copied into RecoReco), so a mid-plan start, a re-purchase, or an
|
||||
// extend all just work by editing FinanceBuddy. A blank/0 limit means recur forever. The
|
||||
// spawn loop below stops when counter > limit (1-based; the counter ends on limit+1).
|
||||
$fbTotal = isset($meta['financebuddy_installment_total']) ? (string) $meta['financebuddy_installment_total'] : '';
|
||||
$follow = isset($meta['recoreco_follow_finance']) && $meta['recoreco_follow_finance'] == 1
|
||||
&& $fbTotal !== '' && ctype_digit($fbTotal);
|
||||
|
||||
if ($follow) {
|
||||
$limit = (int) $fbTotal;
|
||||
} else {
|
||||
$limitRaw = isset($meta['recoreco_limit']) ? (string) $meta['recoreco_limit'] : '';
|
||||
$limit = ($limitRaw !== '' && ctype_digit($limitRaw)) ? (int) $limitRaw : 0;
|
||||
}
|
||||
|
||||
// Standalone progress counter (1-based "next occurrence"). Unused in follow mode, where the
|
||||
// counter is FinanceBuddy's live installment_current instead.
|
||||
$count = isset($meta['recoreco_count']) && ctype_digit((string) $meta['recoreco_count']) ? (int) $meta['recoreco_count'] : 1;
|
||||
|
||||
// Phase 1 -- walk forward from the cursor, collecting every occurrence whose fire time has
|
||||
// arrived. The loop exits on the first occurrence still ahead of the horizon, which is then
|
||||
// the "next future" one to park on.
|
||||
@@ -149,23 +169,53 @@ class RecoRecoModel extends Base
|
||||
|
||||
// Phase 3 -- spawn each kept occurrence, dated to that occurrence. Idempotent: skip any that
|
||||
// this template has already spawned (survives a run overlapping cron on the same occurrence).
|
||||
// Two guards enforce the finite-plan limit: the leading one catches an already-complete plan
|
||||
// (a re-enabled done template, or a back-fill overshoot); the trailing one disables the
|
||||
// instant this run's spawn completes the plan.
|
||||
$spawned = 0;
|
||||
$completed = false;
|
||||
|
||||
foreach ($toSpawn as $occurrence) {
|
||||
$counter = $follow ? $this->financeCurrent($task_id) : $count;
|
||||
|
||||
if ($limit > 0 && $counter > $limit) {
|
||||
$completed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($this->cloneExists($task_id, $occurrence)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// duplicate() copies the template's date_due onto the clone, so set it to this occurrence
|
||||
// first, then spawn.
|
||||
// first, then spawn. spawn() fires the hook that lets FinanceBuddy advance its installment.
|
||||
$this->setDue($task_id, $occurrence);
|
||||
$this->spawn($task, $targetColumn);
|
||||
$spawned++;
|
||||
|
||||
// Standalone advances its own counter (only meaningful with a limit set); follow mode
|
||||
// relies on FinanceBuddy having advanced its installment during spawn().
|
||||
if (! $follow && $limit > 0) {
|
||||
$count++;
|
||||
$this->setCount($task_id, $count);
|
||||
}
|
||||
|
||||
$counter = $follow ? $this->financeCurrent($task_id) : $count;
|
||||
|
||||
if ($limit > 0 && $counter > $limit) {
|
||||
$completed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 4 -- park the cursor on the next future occurrence and stop.
|
||||
// Phase 4 -- park the cursor on the next future occurrence. When the plan just completed,
|
||||
// disable the template (its p(total+1) tombstone is already in place) and log it.
|
||||
$this->setDue($task_id, $nextFuture);
|
||||
|
||||
if ($completed) {
|
||||
$this->completePlan($task, $task_id);
|
||||
}
|
||||
|
||||
// Reconcile the duplicate links with the setting (removes both new and old ones when off).
|
||||
$this->syncCloneLinks($task_id, $linkCopies);
|
||||
|
||||
@@ -227,6 +277,46 @@ class RecoRecoModel extends Base
|
||||
$this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update(array('date_due' => $due));
|
||||
}
|
||||
|
||||
/**
|
||||
* FinanceBuddy's live "next installment to spawn" for a task (1-based), read fresh because
|
||||
* FinanceBuddy advances it on every spawn. Defaults to 1 when absent or non-numeric. RecoReco
|
||||
* only reads this key -- it never writes FinanceBuddy metadata.
|
||||
*
|
||||
* @param int $task_id
|
||||
* @return int
|
||||
*/
|
||||
private function financeCurrent($task_id)
|
||||
{
|
||||
$current = $this->taskMetadataModel->get($task_id, 'financebuddy_installment_current', 1);
|
||||
|
||||
return ctype_digit((string) $current) ? (int) $current : 1;
|
||||
}
|
||||
|
||||
private function setCount($task_id, $count)
|
||||
{
|
||||
$this->taskMetadataModel->save($task_id, array('recoreco_count' => (int) $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* A finite plan reached its limit: disable the template and record it in the activity stream,
|
||||
* attributed to the card owner (creator_id has a FK to users, so a system id would fail silently).
|
||||
*
|
||||
* @param array $task
|
||||
* @param int $task_id
|
||||
*/
|
||||
private function completePlan(array $task, $task_id)
|
||||
{
|
||||
$this->taskMetadataModel->save($task_id, array('recoreco_enabled' => 0));
|
||||
$this->logger->info('RecoReco: plan complete on task '.$task_id.' (recurrence disabled)');
|
||||
$this->projectActivityModel->createEvent(
|
||||
(int) $task['project_id'],
|
||||
$task_id,
|
||||
(int) $task['creator_id'],
|
||||
'recoreco.task.complete',
|
||||
array('task' => array('id' => $task_id, 'title' => $task['title']))
|
||||
);
|
||||
}
|
||||
|
||||
private function spawn(array $template, $targetColumn)
|
||||
{
|
||||
// The template's date_due is already the occurrence (setDue), so duplicate() copies it onto
|
||||
|
||||
Reference in New Issue
Block a user