2026-07-07 21:48:46 -03:00
|
|
|
# RecoReco -- calendar-scheduled recurring cards
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
Kanboard's built-in recurrence is event-triggered (a card recurs when you move or close it).
|
|
|
|
|
RecoReco adds **calendar-triggered** recurrence: a card fires on a date, on its own, via cron --
|
|
|
|
|
made for bills, rent, and subscriptions.
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
A card marked recurring **stays** as a template; on schedule, RecoReco spawns a plain
|
|
|
|
|
(non-recurring) copy into a column you choose. The template advances to the next date; the copy
|
|
|
|
|
keeps the fired date.
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-10 09:12:30 -03:00
|
|
|
> **A note on card order.** Because the template keeps its identity (and its id) for the life of the
|
|
|
|
|
> plan while its due date advances, the template ends up as the *oldest* id carrying the *latest*
|
|
|
|
|
> date. The spawned copies, by contrast, are ordered naturally (older copy = older id = older date).
|
|
|
|
|
> This is intentional: a stable, editable template that stays in place is worth more than making the
|
|
|
|
|
> one generator card sort by date. Native Kanboard recurrence avoids the mismatch only by
|
|
|
|
|
> reincarnating the card every cycle, which RecoReco deliberately does not do.
|
|
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
RecoReco is opt-in per card and inert until you mark a card, so it needs no per-board setting. It
|
|
|
|
|
leaves native recurrence untouched (the two are mutually exclusive per card).
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
## The idea
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
- **The due date is the anchor.** All timing (day, month, weekday, time) is read from the card's
|
|
|
|
|
due date -- there are no separate date inputs. A card without a due date cannot be made recurring.
|
|
|
|
|
- **Frequencies:** yearly, monthly by day, monthly by weekday, weekly, daily.
|
|
|
|
|
- **Last day of the month:** an explicit checkbox (honored only when the due date is the last day,
|
|
|
|
|
or the last such weekday, of its month).
|
|
|
|
|
- **Lead time:** "create the copy N days before the due date", so the card shows up early enough
|
|
|
|
|
to act on; the copy is still due on the real day.
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-07 23:18:20 -03:00
|
|
|
## Scheduling (cron)
|
|
|
|
|
|
|
|
|
|
The engine runs from the CLI command `recoreco:run`. Add it to cron **four times a day** -- at
|
|
|
|
|
05:58, 11:58, 17:58 and 23:58:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
58 5,11,17,23 * * * cd /path/to/kanboard && php cli recoreco:run >> /var/log/recoreco.log 2>&1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
(Point the path at your install and run it as the user that owns Kanboard's data.) Each run looks
|
2026-07-08 19:24:42 -03:00
|
|
|
ahead 12 hours; the runs overlap by 6 hours, so a single missed run is covered by the next one.
|
|
|
|
|
|
|
|
|
|
Each run walks a template forward from its current due date and spawns **every** occurrence whose
|
|
|
|
|
fire time has arrived -- so occurrences already in the past are backfilled, not skipped. To avoid
|
|
|
|
|
flooding the board when a template was set far in the past, a single run spawns at most the **12
|
|
|
|
|
most recent** due occurrences; any older overflow is skipped (contiguous, no gap) and the template
|
|
|
|
|
advances straight to the next future occurrence. `days before` shifts a copy earlier within the
|
|
|
|
|
lead. Runs are idempotent: an occurrence is never spawned twice, so running an extra time is safe.
|
|
|
|
|
|
|
|
|
|
You can trigger the exact same pass by hand -- either on the CLI (`php cli recoreco:run`) or with
|
|
|
|
|
the **Run now** button under **Settings -> RecoReco** (admin only). The button and cron do the
|
|
|
|
|
identical thing.
|
2026-07-07 23:18:20 -03:00
|
|
|
|
2026-07-10 09:12:30 -03:00
|
|
|
## Stopping a finite plan
|
|
|
|
|
|
|
|
|
|
By default a recurring card recurs forever. To make it stop, give it a **limit** in the Recurring
|
|
|
|
|
schedule dialog:
|
|
|
|
|
|
|
|
|
|
- **Stop after N recurrences** -- a plain count; blank or 0 means recur forever. RecoReco keeps a
|
|
|
|
|
1-based progress counter and turns the recurrence off once it passes the limit.
|
|
|
|
|
- **Follow FinanceBuddy installments** -- shown only on boards where FinanceBuddy is enabled (and on
|
|
|
|
|
by default there). RecoReco reads the card's installment total and current *live* from FinanceBuddy
|
|
|
|
|
(never copied) and stops after the last installment. Because the numbers are read fresh, editing
|
|
|
|
|
them in FinanceBuddy just works: start mid-plan (enter `222/420`), re-purchase (reset the current
|
|
|
|
|
installment), or extend (raise the total) -- no RecoReco bookkeeping to keep in sync.
|
|
|
|
|
|
|
|
|
|
When a followed plan ends, the parked template is left one past the last installment (`p4/3` for a
|
|
|
|
|
three-installment plan) as a visible "spent" marker, its recurrence off, with an activity-stream
|
|
|
|
|
entry recording the completion.
|
|
|
|
|
|
2026-07-10 23:04:51 -03:00
|
|
|
## Trigger column and move-mode
|
|
|
|
|
|
|
|
|
|
Two options in the Recurring schedule dialog change *where* and *how* a card recurs:
|
|
|
|
|
|
|
|
|
|
- **Trigger column.** By default (`Any`) a card fires wherever it sits. Set a **Trigger column** and
|
|
|
|
|
it only fires while parked there -- so a template spawns only from its "active" column, and moving
|
|
|
|
|
it elsewhere quietly pauses it. The **`any but selected`** checkbox inverts the choice (fire in
|
|
|
|
|
every column *except* the selected one, e.g. "any but Archive"). The **target column never
|
|
|
|
|
triggers**, in any mode -- this is what lets a moved card come to rest in the target without
|
|
|
|
|
re-firing. (One edge to know: a *duplicate* template dragged into its own target column no longer
|
|
|
|
|
spawns; previously it fired from any column. Templates normally sit elsewhere, so this rarely
|
|
|
|
|
bites.)
|
|
|
|
|
- **Move instead of copy.** Tick **"Move the card instead of creating a copy"** and RecoReco
|
|
|
|
|
relocates the *one* card to the target column each period instead of spawning a clone -- made for a
|
|
|
|
|
recurring chore you don't want piling up copies. Pair it with a trigger column: e.g. trigger =
|
|
|
|
|
`Done`, target = `Todo` -- cron moves the card `Done -> Todo` when it's due; you do the task and
|
|
|
|
|
drag it back to `Done`; repeat. Move-mode honors the recurrence limit (stop after N moves) but not
|
|
|
|
|
the FinanceBuddy follow (there is no copy to advance an installment on). Moves are silent (no
|
|
|
|
|
"moved to column" notification each period).
|
|
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
## Status
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-08 19:24:42 -03:00
|
|
|
All five frequencies work (yearly, monthly by day, monthly by weekday, weekly, daily) with the
|
2026-07-10 09:12:30 -03:00
|
|
|
last-day rule, board recurrence icons, backfill with the 12-occurrence cap, the Run-now button, a
|
2026-07-10 23:04:51 -03:00
|
|
|
recurrence limit (standalone or following FinanceBuddy installments), the FinanceBuddy installment
|
|
|
|
|
hand-off, a per-card trigger column, and move-mode (relocate instead of copy).
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
## Requirements
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
- Kanboard >= 1.2.0
|
2026-07-07 23:18:20 -03:00
|
|
|
- cron (to run `recoreco:run`)
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
## Installation
|
2026-07-07 18:36:04 -03:00
|
|
|
|
2026-07-07 21:48:46 -03:00
|
|
|
Copy this folder into your Kanboard installation as `plugins/RecoReco/`. The directory name must be
|
|
|
|
|
exactly `RecoReco` (Kanboard derives the plugin namespace from the folder name). No build step and
|
2026-07-07 23:18:20 -03:00
|
|
|
no database migration. Then add the cron entry above.
|
2026-07-07 18:36:04 -03:00
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
AGPL-3.0. See LICENSE.
|
2026-07-10 09:12:30 -03:00
|
|
|
|
2026-07-13 08:56:14 -03:00
|
|
|
## More Kanboard plugins by Dr. Beco
|
2026-07-10 09:12:30 -03:00
|
|
|
|
|
|
|
|
All free and AGPL-3.0, at [code.beco.cc](https://code.beco.cc/beco):
|
|
|
|
|
|
|
|
|
|
- **[FinanceBuddy](https://code.beco.cc/beco/FinanceBuddy)** -- attach a money value (debit/credit
|
2026-07-13 08:46:28 -03:00
|
|
|
and installments) to cards, shown on the card and totalled per column.
|
|
|
|
|
- **[QualKard](https://code.beco.cc/beco/QualKard)** -- flashcard spaced repetition: grade a card by
|
|
|
|
|
dragging it to a column, and it comes back when due (needs RecoReco).
|
|
|
|
|
- **[WorkspaceOrg](https://code.beco.cc/beco/WorkspaceOrg)** -- group your projects into personal
|
|
|
|
|
per-user workspaces, shown on a "My workspaces" dashboard page with a badge on each project.
|
2026-07-10 09:12:30 -03:00
|
|
|
- **[OrganonTweaks](https://code.beco.cc/beco/OrganonTweaks)** -- an umbrella of small
|
|
|
|
|
quality-of-life board tweaks: remove an empty column, always show the comment icon, emphasize due
|
|
|
|
|
dates, extra search keywords and shared board filters, and more.
|
|
|
|
|
- **[BulkMoveTasks](https://code.beco.cc/beco/BulkMoveTasks)** -- move every task from one board
|
|
|
|
|
column to another in a single action.
|
|
|
|
|
- **[ShrinkVertically](https://code.beco.cc/beco/ShrinkVertically)** -- shrink vertically-collapsed
|
|
|
|
|
board columns so the horizontal scrollbar stays within reach.
|
|
|
|
|
- **[TweakDrag](https://code.beco.cc/beco/TweakDrag)** -- board drag and touch niceties:
|
|
|
|
|
drag-to-scroll, a wider column gap, and smoother card dragging.
|