From 0190947bb7cacaca63694b3fdcf58ec9992e0c21 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 20 Jun 2026 14:01:57 -0500 Subject: [PATCH 1/3] =?UTF-8?q?Add=20NpoReportsController=20API=20?= =?UTF-8?q?=E2=80=94=20annual=20impact,=20donor=20impact,=20recurring=20do?= =?UTF-8?q?nations=20CRUD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Controller/NpoReportsController.php | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 source/packages/com_mokosuitenpo/api/src/Controller/NpoReportsController.php diff --git a/source/packages/com_mokosuitenpo/api/src/Controller/NpoReportsController.php b/source/packages/com_mokosuitenpo/api/src/Controller/NpoReportsController.php new file mode 100644 index 0000000..0085bd9 --- /dev/null +++ b/source/packages/com_mokosuitenpo/api/src/Controller/NpoReportsController.php @@ -0,0 +1,104 @@ +getIdentity(); + if (!$user || $user->guest || (!$user->authorise('core.admin') && !$user->authorise($action, 'com_mokosuitenpo'))) { + http_response_code(403); + echo json_encode(['error' => 'Access denied']); + Factory::getApplication()->close(); + } + } + + public function annualReport(): void + { + $this->requireAuth(); + $year = Factory::getApplication()->getInput()->getInt('year', (int) date('Y')); + $summary = \Moko\Plugin\System\MokoSuiteNpo\Helper\ImpactReportHelper::getAnnualSummary($year); + $this->sendJson($summary); + } + + public function donorImpact(): void + { + $this->requireAuth(); + $input = Factory::getApplication()->getInput(); + $impact = \Moko\Plugin\System\MokoSuiteNpo\Helper\ImpactReportHelper::getDonorImpact( + $input->getInt('donor_id', 0), + $input->getInt('year', (int) date('Y')) + ); + $this->sendJson($impact); + } + + public function listRecurring(): void + { + $this->requireAuth(); + $db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class); + + $db->setQuery($db->getQuery(true) + ->select('p.*, cd.name AS donor_name') + ->from($db->quoteName('#__mokosuitenpo_pledges', 'p')) + ->join('INNER', $db->quoteName('#__mokosuitenpo_donors', 'd') . ' ON d.id = p.donor_id') + ->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = d.contact_id') + ->where($db->quoteName('p.status') . ' = ' . $db->quote('active')) + ->where('p.saved_payment_id IS NOT NULL') + ->order('p.next_charge_date ASC')); + + $this->sendJson($db->loadObjectList() ?: []); + } + + public function createRecurring(): void + { + $this->requireAuth('npo.donations'); + $input = Factory::getApplication()->getInput(); + + $id = \Moko\Plugin\System\MokoSuiteNpo\Helper\RecurringDonationHelper::createPledge( + $input->getInt('donor_id', 0), + $input->getFloat('amount', 0), + $input->getString('frequency', 'monthly'), + $input->getInt('saved_payment_id', 0), + $input->getInt('fund_id', 0) ?: null + ); + + $this->sendJson(['success' => true, 'pledge_id' => $id]); + } + + public function cancelRecurring(): void + { + $this->requireAuth('npo.donations'); + $id = Factory::getApplication()->getInput()->getInt('id', 0); + + $db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class); + $db->setQuery($db->getQuery(true) + ->update('#__mokosuitenpo_pledges') + ->set($db->quoteName('status') . ' = ' . $db->quote('cancelled')) + ->set($db->quoteName('cancelled_at') . ' = ' . $db->quote(Factory::getDate()->toSql())) + ->where('id = ' . (int) $id)); + $db->execute(); + + $this->sendJson(['success' => true]); + } + + private function sendJson(mixed $data): void + { + header('Content-Type: application/json; charset=utf-8'); + echo json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); + Factory::getApplication()->close(); + } +} -- 2.52.0 From 69b554f4a6414c65017db2b2c965f9b5e28d0d72 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 20 Jun 2026 14:57:03 -0500 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20cancelRecurring=20=E2=80=94=20verify?= =?UTF-8?q?=20pledge=20exists=20and=20is=20active=20before=20cancelling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/Controller/NpoReportsController.php | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/source/packages/com_mokosuitenpo/api/src/Controller/NpoReportsController.php b/source/packages/com_mokosuitenpo/api/src/Controller/NpoReportsController.php index 0085bd9..60d80b8 100644 --- a/source/packages/com_mokosuitenpo/api/src/Controller/NpoReportsController.php +++ b/source/packages/com_mokosuitenpo/api/src/Controller/NpoReportsController.php @@ -85,11 +85,30 @@ class NpoReportsController extends BaseController $id = Factory::getApplication()->getInput()->getInt('id', 0); $db = Factory::getContainer()->get(\Joomla\Database\DatabaseInterface::class); + + // Verify pledge exists and is active before cancelling + $db->setQuery($db->getQuery(true) + ->select('id, status') + ->from('#__mokosuitenpo_pledges') + ->where('id = ' . (int) $id)); + $pledge = $db->loadObject(); + + if (!$pledge) { + http_response_code(404); + $this->sendJson(['success' => false, 'error' => 'Pledge not found']); + return; + } + if ($pledge->status !== 'active') { + $this->sendJson(['success' => false, 'error' => 'Pledge is not active']); + return; + } + $db->setQuery($db->getQuery(true) ->update('#__mokosuitenpo_pledges') ->set($db->quoteName('status') . ' = ' . $db->quote('cancelled')) ->set($db->quoteName('cancelled_at') . ' = ' . $db->quote(Factory::getDate()->toSql())) - ->where('id = ' . (int) $id)); + ->where('id = ' . (int) $id) + ->where($db->quoteName('status') . ' = ' . $db->quote('active'))); $db->execute(); $this->sendJson(['success' => true]); -- 2.52.0 From 774fee24fd9f4f31d73dfb9b55b8a35b0998221b Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 20 Jun 2026 19:57:16 +0000 Subject: [PATCH 3/3] chore(version): auto-bump patch 01.01.01-dev [skip ci] --- source/packages/com_mokosuitenpo/mokosuitenpo.xml | 2 +- source/pkg_mokosuitenpo.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/packages/com_mokosuitenpo/mokosuitenpo.xml b/source/packages/com_mokosuitenpo/mokosuitenpo.xml index 4135fa0..f291a57 100644 --- a/source/packages/com_mokosuitenpo/mokosuitenpo.xml +++ b/source/packages/com_mokosuitenpo/mokosuitenpo.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 01.01.00 + 01.01.01 8.3 MokoSuite NPO component Moko\Component\MokoSuiteNpo diff --git a/source/pkg_mokosuitenpo.xml b/source/pkg_mokosuitenpo.xml index dbef4db..0758b55 100644 --- a/source/pkg_mokosuitenpo.xml +++ b/source/pkg_mokosuitenpo.xml @@ -2,7 +2,7 @@ Package - MokoSuite NPO mokosuitenpo - 01.01.00 + 01.01.01 2026-06-11 Moko Consulting hello@mokoconsulting.tech -- 2.52.0