|
|
|
@@ -0,0 +1,123 @@
|
|
|
|
|
<?php
|
|
|
|
|
namespace Moko\Component\MokoSuiteNpo\Api\Controller;
|
|
|
|
|
|
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
|
|
|
|
|
|
use Joomla\CMS\Factory;
|
|
|
|
|
use Joomla\CMS\MVC\Controller\BaseController;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* NPO Reports + Recurring Donations API.
|
|
|
|
|
*
|
|
|
|
|
* GET /reports/annual/{year} — Annual impact summary
|
|
|
|
|
* GET /reports/donor/{id} — Donor impact statement
|
|
|
|
|
* GET /recurring — Active recurring donations
|
|
|
|
|
* POST /recurring — Create recurring pledge
|
|
|
|
|
* POST /recurring/{id}/cancel — Cancel recurring donation
|
|
|
|
|
*/
|
|
|
|
|
class NpoReportsController extends BaseController
|
|
|
|
|
{
|
|
|
|
|
private function requireAuth(string $action = 'core.manage'): void
|
|
|
|
|
{
|
|
|
|
|
$user = Factory::getApplication()->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);
|
|
|
|
|
|
|
|
|
|
// 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($db->quoteName('status') . ' = ' . $db->quote('active')));
|
|
|
|
|
$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();
|
|
|
|
|
}
|
|
|
|
|
}
|