Skip to content

Commit

Permalink
Allow to accept/reject multiple drawdowns at once
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominic Tubach committed Feb 4, 2025
1 parent 4899ebb commit d3277a7
Show file tree
Hide file tree
Showing 12 changed files with 612 additions and 27 deletions.
10 changes: 10 additions & 0 deletions Civi/Api4/FundingDrawdown.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
namespace Civi\Api4;

use Civi\Funding\Api4\Action\FundingDrawdown\AcceptAction;
use Civi\Funding\Api4\Action\FundingDrawdown\AcceptMultipleAction;
use Civi\Funding\Api4\Action\FundingDrawdown\CreateAction;
use Civi\Funding\Api4\Action\FundingDrawdown\GetAction;
use Civi\Funding\Api4\Action\FundingDrawdown\GetFieldsAction;
use Civi\Funding\Api4\Action\FundingDrawdown\RejectAction;
use Civi\Funding\Api4\Action\FundingDrawdown\RejectMultipleAction;
use Civi\Funding\Api4\Action\FundingDrawdown\SaveAction;
use Civi\Funding\Api4\Action\FundingDrawdown\UpdateAction;
use Civi\Funding\Api4\Permissions;
Expand All @@ -30,6 +32,10 @@ public static function accept(bool $checkPermissions = TRUE): AcceptAction {
return \Civi::service(AcceptAction::class)->setCheckPermissions($checkPermissions);
}

public static function acceptMultiple(bool $checkPermissions = TRUE): AcceptMultipleAction {
return (new AcceptMultipleAction())->setCheckPermissions($checkPermissions);
}

public static function create($checkPermissions = TRUE) {
return \Civi::service(CreateAction::class)->setCheckPermissions($checkPermissions);
}
Expand All @@ -46,6 +52,10 @@ public static function reject(bool $checkPermissions = TRUE): RejectAction {
return \Civi::service(RejectAction::class)->setCheckPermissions($checkPermissions);
}

public static function rejectMultiple(bool $checkPermissions = TRUE): RejectMultipleAction {
return (new RejectMultipleAction())->setCheckPermissions($checkPermissions);
}

public static function save($checkPermissions = TRUE) {
return \Civi::service(SaveAction::class)->setCheckPermissions($checkPermissions);
}
Expand Down
48 changes: 48 additions & 0 deletions Civi/Funding/Api4/Action/FundingDrawdown/AcceptMultipleAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/*
* Copyright (C) 2025 SYSTOPIA GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation in version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

declare(strict_types = 1);

namespace Civi\Funding\Api4\Action\FundingDrawdown;

use Civi\Api4\FundingDrawdown;
use Civi\Api4\Generic\AbstractAction;
use Civi\Api4\Generic\Result;
use Civi\Funding\Api4\Action\Traits\IdsParameterTrait;

class AcceptMultipleAction extends AbstractAction {

use IdsParameterTrait;

public function __construct() {
parent::__construct(FundingDrawdown::getEntityName(), 'acceptMultiple');
}

/**
* @inheritDoc
* @throws \CRM_Core_Exception
*/
public function _run(Result $result): void {
foreach ($this->getIds() as $id) {
$result[$id] = FundingDrawdown::accept()
->setId($id)
->execute()
->single();
}
}

}
48 changes: 48 additions & 0 deletions Civi/Funding/Api4/Action/FundingDrawdown/RejectMultipleAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/*
* Copyright (C) 2025 SYSTOPIA GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation in version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

declare(strict_types = 1);

namespace Civi\Funding\Api4\Action\FundingDrawdown;

use Civi\Api4\FundingDrawdown;
use Civi\Api4\Generic\AbstractAction;
use Civi\Api4\Generic\Result;
use Civi\Funding\Api4\Action\Traits\IdsParameterTrait;

class RejectMultipleAction extends AbstractAction {

use IdsParameterTrait;

public function __construct() {
parent::__construct(FundingDrawdown::getEntityName(), 'rejectMultiple');
}

/**
* @inheritDoc
* @throws \CRM_Core_Exception
*/
public function _run(Result $result): void {
foreach ($this->getIds() as $id) {
$result[$id] = FundingDrawdown::reject()
->setId($id)
->execute()
->single();
}
}

}
62 changes: 62 additions & 0 deletions Civi/Funding/Controller/DrawdownDocumentDownloadController.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@
use Civi\Funding\FundingAttachmentManagerInterface;
use Civi\Funding\PayoutProcess\DrawdownManager;
use CRM_Funding_ExtensionUtil as E;
use setasign\Fpdi\Fpdi;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Tomsgu\PdfMerger\PdfCollection;
use Tomsgu\PdfMerger\PdfFile;
use Tomsgu\PdfMerger\PdfMerger;

final class DrawdownDocumentDownloadController implements PageControllerInterface {

Expand All @@ -47,6 +52,19 @@ public function __construct(FundingAttachmentManagerInterface $attachmentManager
* @throws \CRM_Core_Exception
*/
public function handle(Request $request): Response {
if ($request->query->has('drawdownIds')) {
$drawdownIds = (string) $request->query->get('drawdownIds');

Check failure on line 56 in Civi/Funding/Controller/DrawdownDocumentDownloadController.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Cannot cast mixed to string.

Check failure on line 56 in Civi/Funding/Controller/DrawdownDocumentDownloadController.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 8.0 prefer-lowest

Cannot cast mixed to string.

Check failure on line 56 in Civi/Funding/Controller/DrawdownDocumentDownloadController.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 8.3 prefer-lowest

Cannot cast mixed to string.

if (preg_match('/^[1-9][0-9]*(,[1-9][0-9]*)*$/', $drawdownIds) !== 1) {
throw new BadRequestHttpException('Invalid drawdown IDs');
}

$drawdownIds = array_map(fn (string $id) => (int) $id, explode(',', $drawdownIds));
/** @phpstan-var list<int> $drawdownIds */

return $this->downloadMultiple($drawdownIds);
}

$drawdownId = $request->query->get('drawdownId');

if (!is_numeric($drawdownId)) {
Expand Down Expand Up @@ -92,4 +110,48 @@ private function download(int $drawdownId): Response {
->setMaxAge(300);
}

/**
* @phpstan-param list<int> $drawdownIds
*
* @throws \CRM_Core_Exception
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
private function downloadMultiple(array $drawdownIds): Response {
$pdfCollection = new PdfCollection();
foreach ($drawdownIds as $drawdownId) {
$drawdown = $this->drawdownManager->get($drawdownId);
if (NULL === $drawdown) {
throw new AccessDeniedHttpException();
}

$attachment = $this->attachmentManager->getLastByFileType(
'civicrm_funding_drawdown',
$drawdownId,
$drawdown->getAmount() < 0 ? FileTypeNames::PAYBACK_CLAIM : FileTypeNames::PAYMENT_INSTRUCTION,
);

if (NULL === $attachment) {
throw new NotFoundHttpException("Drawdown document (ID: $drawdownId) does not exist");
}

$pdfCollection->addPdf($attachment->getPath());
}

$merger = new PdfMerger(new Fpdi());
$merger->merge($pdfCollection);

$filename = E::ts('payment-instructions') . '.pdf';
$headers = [
'Content-Type' => 'application/pdf',
'Content-Disposition' => HeaderUtils::makeDisposition(HeaderUtils::DISPOSITION_INLINE, $filename),
];

return (new Response(
$merger->merge($pdfCollection, $filename, PdfMerger::MODE_STRING, PdfFile::ORIENTATION_AUTO_DETECT),
200,
$headers
))->setMaxAge(300);
}

}
2 changes: 1 addition & 1 deletion Civi/Funding/Page/RemoteTransferContractDownloadPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/**
* @codeCoverageIgnore
*/
class RemoteTransferContractDownloadPage extends AbstractRemoteControllerPage {
final class RemoteTransferContractDownloadPage extends AbstractRemoteControllerPage {

protected function getController(): PageControllerInterface {
return \Civi::service(TransferContractDownloadController::class);
Expand Down
9 changes: 9 additions & 0 deletions ang/afsearchDrawdowns.aff.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div af-fieldset="">
<div class="af-container af-layout-inline">
<af-field name="FundingDrawdown_FundingPayoutProcess_payout_process_id_01_FundingPayoutProcess_FundingCase_funding_case_id_01.identifier" defn="{required: false, input_attrs: {}}" />
<af-field name="status" defn="{input_attrs: {multiple: true}, required: false}" />
<af-field name="FundingDrawdown_FundingPayoutProcess_payout_process_id_01_FundingPayoutProcess_FundingCase_funding_case_id_01.recipient_contact_id" defn="{required: false, input_attrs: {}}" />
<af-field name="FundingDrawdown_FundingPayoutProcess_payout_process_id_01_FundingPayoutProcess_FundingCase_funding_case_id_01.funding_program_id" defn="{input_type: 'Select', input_attrs: {multiple: true}, required: false}" />
</div>
<crm-search-display-table search-name="FundingDrawdownsAll" display-name="Table"></crm-search-display-table>
</div>
18 changes: 18 additions & 0 deletions ang/afsearchDrawdowns.aff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
use CRM_Funding_ExtensionUtil as E;

return [
'type' => 'search',
'requires' => ['crmFunding'],
'title' => E::ts('Drawdowns'),
'icon' => 'fa-list-alt',
'server_route' => 'civicrm/funding/drawdown/list',
'permission' => [
'access Funding',
'administer Funding',
],
'permission_operator' => 'OR',
'search_displays' => [
'FundingDrawdownsAll.Table',
],
];
Loading

0 comments on commit d3277a7

Please sign in to comment.