Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/GaletteHelloasso/Controllers/HelloassoController.php
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,12 @@ public function webhook(Request $request, Response $response): Response
return $response->withStatus(500, 'Internal error');
}
return $response->withStatus(200);
} else {
Analog::log(
'A Helloasso payment has been successfully stored as a public donation',
Analog::DEBUG
);
$hh->setState(HelloassoHistory::STATE_PUBLIC);
}
}
return $response->withStatus(200);
Expand Down
37 changes: 29 additions & 8 deletions lib/GaletteHelloasso/HelloassoHistory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Galette\Core\Login;
use Galette\Core\History;
use Galette\Core\Preferences;
use Galette\Entity\Adherent;
use Galette\Filters\HistoryList;

/**
Expand All @@ -32,7 +33,8 @@ class HelloassoHistory extends History
public const int STATE_NONE = 0;
public const int STATE_PROCESSED = 1;
public const int STATE_ERROR = 2;
public const int STATE_ALREADYDONE = 3;
public const int STATE_PUBLIC = 3;
public const int STATE_ALREADYDONE = 4;

private int $id;

Expand Down Expand Up @@ -123,7 +125,6 @@ public function getHelloassoHistory(): array
{
$orig = $this->getHistory();
$new = [];
$dedup = [];
if (count($orig) > 0) {
foreach ($orig as $o) {
try {
Expand All @@ -133,13 +134,11 @@ public function getHelloassoHistory(): array
$oa = Galette::jsonDecode($o['request']);
}

$member_id = $oa['metadata']['member_id'] ?? 0;

$o['member_fullname'] = $this->getMemberFullName($member_id);
$o['raw_request'] = print_r($oa, true);
$o['request'] = $oa;
if (in_array($o['checkout_id'], $dedup)) {
$o['duplicate'] = true;
} else {
$dedup[] = $o['checkout_id'];
}

$new[] = $o;
} catch (\Exception $e) {
Expand All @@ -154,6 +153,28 @@ public function getHelloassoHistory(): array
return $new;
}

/**
* Gets Member full name
*
* @param int $id ID of the member to retrieve
*/
protected function getMemberFullName(int $id): string
{
$fullname = _T('None', 'helloasso');

$select = $this->zdb->select(Adherent::TABLE);
$select->columns(['prenom_adh', 'nom_adh']);
$select->where(['id_adh' => $id]);
$result = $this->zdb->execute($select);
$row = $result->current();

if ($row) {
$fullname = mb_strtoupper($row['nom_adh']) . ' ' . $row['prenom_adh'];
}

return $fullname;
}

/**
* Builds the order clause
*
Expand Down Expand Up @@ -181,7 +202,7 @@ public function isProcessed(array $request): bool
$select->where(
[
'checkout_id' => $request['data']['id'],
'state' => self::STATE_PROCESSED
'state' => [self::STATE_PROCESSED, self::STATE_PUBLIC]
]
);
$results = $this->zdb->execute($select);
Expand Down
7 changes: 7 additions & 0 deletions scripts/upgrade-to-1.1.0-mysql.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
--
-- This file is part of Galette Helloasso plugin (https://galette-community.github.io/plugin-helloasso).
-- SPDX-FileCopyrightText: Copyright © 2025-2026 The Galette Team
-- SPDX-License-Identifier: GPL-3.0-or-later
--

UPDATE galette_helloasso_history SET state = 3 WHERE state = 0;
7 changes: 7 additions & 0 deletions scripts/upgrade-to-1.1.0-pgsql.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
--
-- This file is part of Galette Helloasso plugin (https://galette-community.github.io/plugin-helloasso).
-- SPDX-FileCopyrightText: Copyright © 2025-2026 The Galette Team
-- SPDX-License-Identifier: GPL-3.0-or-later
--

UPDATE galette_helloasso_history SET state = 3 WHERE state = 0;
147 changes: 120 additions & 27 deletions templates/default/helloasso_history.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,37 @@
'collapse': true
},
{
'label': _T("Name"),
'label': _T("Payment nb.", "helloasso"),
'collapse': true
},
{
'label': _T("Subject"),
'label': _T("Payer name", "helloasso"),
'collapse': true
},
{
'label': _T("Member"),
'collapse': true
},
{
'label': _T("Contribution type"),
'collapse': true
},
{
'label': _T("Amount"),
'collapse': true
},
{'label': _T("Request", "helloasso")}
{
'label': _T("Method", "helloasso"),
'collapse': true
},
{
'label': _T("Status", "helloasso"),
'collapse': true
},
{
'label': _T("Actions"),
'collapse': true
}
] %}
{{ parent() }}
{% endblock %}
Expand All @@ -63,53 +82,117 @@
</td>
<td class="left collapsing" data-col-label="{{ _T("Date") }}">
{{ log.history_date|date(_T("Y-m-d H:i:s")) }}
{% if log.duplicate is defined %}
<i class="exclamation circle red icon tooltip" data-html="{{ _T("Duplicate", "helloasso") }}"></i>
<span class="visually-hidden">
{{ _T("Duplicate", "helloasso") }}
</span>
{% endif %}
</td>
<td class="left collapsing" data-col-label="{{ _T("Name") }}">
<td class="left collapsing" data-col-label="{{ _T("Payment nb.", "helloasso") }}">
{{ log.checkout_id }}
</td>
<td class="left collapsing" data-col-label="{{ _T("Payer name", "helloasso") }}">
{% if log.request is iterable %}
{{ log.request.data.payer.lastName|upper }} {{ log.request.data.payer.firstName }}
{% else %}
{{ _T("No request or unable to read request.", "helloasso") }}
{% endif %}
</td>
<td class="left collapsing" data-col-label="{{ _T("Member") }}">
{% if log.request is iterable %}
{% if log.request.metadata.member_id is defined %}
<a href="{{ url_for("member", {"id": log.request.metadata.member_id}) }}">
{% endif %}
{{ log.request.data.payer.lastName|upper }} {{ log.request.data.payer.firstName }}
{{ log.member_fullname }}
{% if log.request.metadata.member_id is defined %}
</a>
{% endif %}
{% else %}
{{ _T("No request or unable to read request.", "helloasso") }}
{% endif %}
</td>
<td class="left collapsing" data-col-label="{{ _T("Subject") }}">
<td class="left collapsing" data-col-label="{{ _T("Contribution type") }}">
{% if log.request is iterable %}
{% if log.request.metadata.item_name is defined %}
<span class="ui green small label">
{{ _T("Payment nb.", "helloasso") }}
{{ log.checkout_id }}
</span>
| {{ log.request.metadata.item_name }}
{{ log.request.metadata.item_name }}
{% endif %}
{% else %}
{{ _T("No request or unable to read request.", "helloasso") }}
{% endif %}
</td>
<td class="right collapsing" data-col-label="{{ _T("Amount") }}">
{{ log.amount }}
</td>
<td class="left" data-col-label="{{ _T("Request", "helloasso") }}">
<details class="ui basic styled accordion">
<summary class="title">
<i class="caret down icon" aria-hidden="true"></i> {{ _T("Show/hide full request", "helloasso") }}
</summary>
<div class="content">
<pre>{{ log.raw_request }}</pre>
</div>
</details>
<td class="left collapsing" data-col-label="{{ _T("Method", "helloasso") }}">
{% if log.request is iterable %}
{% if log.request.data.paymentMeans is defined %}
{{ log.request.data.paymentMeans }}
{% endif %}
{% else %}
{{ _T("No request or unable to read request.", "helloasso") }}
{% endif %}
</td>
<td class="left collapsing" data-col-label="{{ _T("Status", "helloasso") }}">
{% if log.state == constant('GaletteHelloasso\\HelloassoHistory::STATE_PUBLIC') or log.state == constant('GaletteHelloasso\\HelloassoHistory::STATE_PROCESSED') %}
<span class="icon-only">
<i class="check green icon" aria-hidden="true"></i>
<span class="visually-hidden">
{{ _T("Payment properly processed", "helloasso") }}
</span>
</span>
{% elseif log.state == constant('GaletteHelloasso\\HelloassoHistory::STATE_ERROR') %}
<span class="icon-only">
<i class="times red icon" aria-hidden="true"></i>
<span class="visually-hidden">
{{ _T("An error occurred and the corresponding contribution has not been created", "helloasso") }}
</span>
</span>
{% elseif log.state == constant('GaletteHelloasso\\HelloassoHistory::STATE_ALREADYDONE') %}
<span class="icon-only">
<i class="exclamation triangle orange icon" aria-hidden="true"></i>
<span class="visually-hidden">
{{ _T("Duplicate", "helloasso") }}
</span>
</span>
{% endif %}
</td>
<td class="left" data-col-label="{{ _T("Actions") }}">
{% if log.request is iterable %}
{% if log.request.data.paymentReceiptUrl is defined %}
<a href="{{ log.request.data.paymentReceiptUrl }}" class="icon-only" target="_blank">
<i class="file pdf green icon" aria-hidden="true"></i>
<span class="visually-hidden">
{{ _T("Download HelloAsso payment confirmation of payment nb. %1$s", "helloasso")|replace({"%1$s": log.checkout_id}) }}
</span>
</a>
{% endif %}
{% endif %}
<a href="#" class="icon-only view-request" data-modal="modal-{{ loop.index }}">
<i class="info circle blue icon" aria-hidden="true"></i>
<span class="visually-hidden">
{{ _T("View full request of payment nb. %1$s", "helloasso")|replace({"%1$s": log.checkout_id}) }}
</span>
</a>
</td>
</tr>
{% else %}
<tr><td colspan="6" class="emptylist">{{ _T("logs are empty") }}</td></tr>
<tr><td colspan="10" class="emptylist">{{ _T("logs are empty") }}</td></tr>
{% endfor %}
{% endblock %}

{% block content %}
{{ parent() }}

{% for log in logs %}
<div id="modal-{{ loop.index }}" class="ui large modal">
<div class="header">
{{ _T('Full request of payment nb. %1$s', 'helloasso')|replace({'%1$s': log.checkout_id}) }}
</div>
<div class="scrolling content">
<pre>{{ log.raw_request }}</pre>
</div>
<div class="actions">
<div class="ui labeled icon deny button">
<i class="times icon" aria-hidden="true"></i>
{{ _T('Close') }}
</div>
</div>
</div>
{% endfor %}
{% endblock %}

Expand All @@ -118,5 +201,15 @@
$('#nbshow').change(function() {
this.form.submit();
});

document.addEventListener('click', (e) => {
const trigger = e.target.closest('.view-request');
if (!trigger) return;

e.preventDefault();

const modalSelector = '#' + trigger.dataset.modal;
$(modalSelector).modal('show');
});
</script>
{% endblock %}