Extensions Paheko  Check-in [c43bba7fd9]

Overview
Comment:HelloAsso: member auto-registration support implemented
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: c43bba7fd958238da338f974e3b3da862dc8406c
User & Date: alinaar on 2023-05-31 16:43:33
Other Links: branch diff | manifest | tags
Context
2023-05-31
16:44
Merge with #28521d20d0 check-in: f83112263d user: alinaar tags: dev
16:43
HelloAsso: member auto-registration support implemented check-in: c43bba7fd9 user: alinaar tags: dev
2023-05-28
18:53
HelloAsso: Chargeable item label clarified (options and donations) check-in: e821445e61 user: alinaar tags: dev
Changes

Modified helloasso/admin/chargeable.php from [1b0737d81b] to [98c27e97a5].

29
30
31
32
33
34
35

36
37
38
39
40
41
42

$csrf_key = 'accounts_setting';

$form->runIf('save', function () use ($chargeable) {
	// ToDo: add a nice check
	$chargeable->set('id_credit_account', (int)array_keys($_POST['credit'])[0]);
	$chargeable->set('id_debit_account', (int)array_keys($_POST['debit'])[0]);

	$chargeable->save();
}, $csrf_key, 'chargeable.php?id=' . $id . '&ok');

$item = $chargeable->id_item ? EntityManager::findOneById(Item::class, $chargeable->id_item) : null;
$form = EntityManager::findOneById(Form::class, $chargeable->id_form);
$credit_account = $chargeable->id_credit_account ? EntityManager::findOneById(Account::class, (int)$chargeable->id_credit_account) : null;
$debit_account = $chargeable->id_debit_account ? EntityManager::findOneById(Account::class, (int)$chargeable->id_debit_account) : null;







>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

$csrf_key = 'accounts_setting';

$form->runIf('save', function () use ($chargeable) {
	// ToDo: add a nice check
	$chargeable->set('id_credit_account', (int)array_keys($_POST['credit'])[0]);
	$chargeable->set('id_debit_account', (int)array_keys($_POST['debit'])[0]);
	$chargeable->set('register_user', (int)isset($_POST['register_user']));
	$chargeable->save();
}, $csrf_key, 'chargeable.php?id=' . $id . '&ok');

$item = $chargeable->id_item ? EntityManager::findOneById(Item::class, $chargeable->id_item) : null;
$form = EntityManager::findOneById(Form::class, $chargeable->id_form);
$credit_account = $chargeable->id_credit_account ? EntityManager::findOneById(Account::class, (int)$chargeable->id_credit_account) : null;
$debit_account = $chargeable->id_debit_account ? EntityManager::findOneById(Account::class, (int)$chargeable->id_debit_account) : null;

Modified helloasso/admin/config.php from [7bd10ad627] to [424b31d652].

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php

namespace Garradin;

use Garradin\Plugin\HelloAsso\HelloAsso;


use Garradin\Users\DynamicFields;

$session->requireAccess($session::SECTION_CONFIG, $session::ACCESS_ADMIN);

$ha = HelloAsso::getInstance();

if ((array_key_exists('tab', $_GET) && $_GET['tab'] === 'client') || !$ha->isConfigured())
	Utils::redirect(PLUGIN_ADMIN_URL . 'config_client.php');

$csrf_key = sprintf('config_plugin_%s', $plugin->id);

$form->runIf('save', function () use ($ha) {
	$ha->saveConfig(f('map'), f('merge_names'), f('match_email_field'));
}, $csrf_key, '?ok');

$match_options = [
	0 => 'Nom et prénom',
	1 => 'Adresse e-mail',
];

$merge_names_options = $ha::MERGE_NAMES_OPTIONS;

//$fields_names = $ha::PAYER_FIELDS;
$fields_names = [];

$champs = DynamicFields::getInstance()->all();

$target_fields = [
	null => '-- Ne pas importer',
];

foreach ($champs as $key => $config) {
	if (!isset($config->label)) {
		continue;
	}

	$target_fields[$key] = $config->label;
}

$plugin_config = $ha->getConfig();

$tpl->assign(compact('merge_names_options', 'match_options', 'csrf_key', 'fields_names', 'target_fields', 'plugin_config'));

$tpl->display(PLUGIN_ROOT . '/templates/config.tpl');





>













|









|
<

<
<
|


|
|



<
|


<
<
|


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31


32
33
34
35
36
37
38
39

40
41
42


43
44
45
<?php

namespace Garradin;

use Garradin\Plugin\HelloAsso\HelloAsso;
use Garradin\Plugin\HelloAsso\API;

use Garradin\Users\DynamicFields;

$session->requireAccess($session::SECTION_CONFIG, $session::ACCESS_ADMIN);

$ha = HelloAsso::getInstance();

if ((array_key_exists('tab', $_GET) && $_GET['tab'] === 'client') || !$ha->isConfigured())
	Utils::redirect(PLUGIN_ADMIN_URL . 'config_client.php');

$csrf_key = sprintf('config_plugin_%s', $plugin->id);

$form->runIf('save', function () use ($ha) {
	$ha->saveConfig(f('payer_map'), f('merge_names'), f('match_email_field'));
}, $csrf_key, '?ok');

$match_options = [
	0 => 'Nom et prénom',
	1 => 'Adresse e-mail',
];

$merge_names_options = $ha::MERGE_NAMES_OPTIONS;

$payer_fields = API::PAYER_FIELDS;




$dynamic_fields = [
	null => '-- Ne pas importer',
];
$fields = DynamicFields::getInstance()->all();
foreach ($fields as $key => $config) {
	if (!isset($config->label)) {
		continue;
	}

	$dynamic_fields[$key] = $config->label;
}



$tpl->assign(compact('merge_names_options', 'match_options', 'csrf_key', 'payer_fields', 'dynamic_fields'));

$tpl->display(PLUGIN_ROOT . '/templates/config.tpl');

Modified helloasso/admin/config_client.php from [9b42024a38] to [025f039dca].

1
2
3
4
5

6
7
8


9
10
11
12
13
14
15
16
17
18
19
20









21
22
23
24
25
26



































27
28
29
30
31
32
33
34
35






36
37
38
<?php

namespace Garradin;

use Garradin\Plugin\HelloAsso\HelloAsso;


use KD2\DB\EntityManager as EM;
use Garradin\Entities\Accounting\Account;



$session->requireAccess($session::SECTION_CONFIG, $session::ACCESS_ADMIN);

$csrf_key = sprintf('config_plugin_%s', $plugin->id);

$ha = HelloAsso::getInstance();

$form->runIf('save', function () use ($ha) {
	if ($client_secret = f('client_secret')) {
		$ha->saveClient(f('client_id'), $client_secret);
	}
	// ToDo: add a nice form check









	$ha->saveConfig($_POST);
	Utils::redirect('?ok=' . ($client_secret ? 'connection' : 'config'));
}, $csrf_key, null);

$credit_account = EM::findOneById(Account::class, (int)$plugin->getConfig()->id_credit_account);
$debit_account = EM::findOneById(Account::class, (int)$plugin->getConfig()->id_debit_account);




































$tpl->assign([
	'client_id'  => $ha->getClientId(),
	'secret'     => '',
	'csrf_key'   => $csrf_key,
	'restricted' => $ha->isTrial(),
	'chart_id' => Plugin\HelloAsso\HelloAsso::CHART_ID, // ToDo: make it dynamic
	'default_credit_account' => (null !== $credit_account) ? [ $credit_account->id => $credit_account->code . ' — ' . $credit_account->label ] : null,
	'default_debit_account' => (null !== $debit_account) ? [ $debit_account->id => $debit_account->code . ' — ' . $debit_account->label ] : null






]);

$tpl->display(PLUGIN_ROOT . '/templates/config_client.tpl');





>



>
>












>
>
>
>
>
>
>
>
>
|





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








|
>
>
>
>
>
>



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<?php

namespace Garradin;

use Garradin\Plugin\HelloAsso\HelloAsso;
use Garradin\Plugin\HelloAsso\API;

use KD2\DB\EntityManager as EM;
use Garradin\Entities\Accounting\Account;
use Garradin\Entities\Users\Category;
use Garradin\Users\DynamicFields;

$session->requireAccess($session::SECTION_CONFIG, $session::ACCESS_ADMIN);

$csrf_key = sprintf('config_plugin_%s', $plugin->id);

$ha = HelloAsso::getInstance();

$form->runIf('save', function () use ($ha) {
	if ($client_secret = f('client_secret')) {
		$ha->saveClient(f('client_id'), $client_secret);
	}
	// ToDo: add a nice form check
	$data = $_POST;
	$data['payer_map']['name'] = (int)$data['payer_map']['name'];
	foreach ($data['payer_map'] as $field => $value) {
		if ($value === 'null') {
			$data['payer_map'][$field] = null;
		}
	}
	$data['user_match_type'] = (int)$data['user_match_type'];
	$data['user_match_field'] = $data['user_match_field'];
	$ha->saveConfig($data);
	Utils::redirect('?ok=' . ($client_secret ? 'connection' : 'config'));
}, $csrf_key, null);

$credit_account = EM::findOneById(Account::class, (int)$plugin->getConfig()->id_credit_account);
$debit_account = EM::findOneById(Account::class, (int)$plugin->getConfig()->id_debit_account);

$categories = EM::getInstance(Category::class)->all('SELECT * FROM @TABLE');
$category_options = [];
foreach ($categories as $category) {
	$category_options[(int)$category->id] = $category->name;
}

$dynamic_fields = $email_fields = [
	'null' => '-- Ne pas importer',
];
$user_match_fields = [];

$fields = DynamicFields::getInstance()->all();
foreach ($fields as $key => $config) {
	if (!isset($config->label)) {
		continue;
	}
	$dynamic_fields[$key] = $config->label;
}
$payer_fields = API::PAYER_FIELDS;
// The following fields have a specific process
unset($payer_fields['firstName']);
unset($payer_fields['lastName']);
unset($payer_fields['email']);

$fields = DynamicFields::getEmailFields();
foreach ($fields as $field) {
	$email_fields[$field] = $field;
	$user_match_fields[$field] = $field;
}

$fields = DynamicFields::getNameFields();
foreach ($fields as $field) {
	$user_match_fields[$field] = $field;
}

$tpl->assign([
	'client_id'  => $ha->getClientId(),
	'secret'     => '',
	'csrf_key'   => $csrf_key,
	'restricted' => $ha->isTrial(),
	'chart_id' => Plugin\HelloAsso\HelloAsso::CHART_ID, // ToDo: make it dynamic
	'default_credit_account' => (null !== $credit_account) ? [ $credit_account->id => $credit_account->code . ' — ' . $credit_account->label ] : null,
	'default_debit_account' => (null !== $debit_account) ? [ $debit_account->id => $debit_account->code . ' — ' . $debit_account->label ] : null,
	'category_options' => $category_options,
	'payer_fields' => $payer_fields,
	'dynamic_fields' => $dynamic_fields,
	'email_fields' => $email_fields,
	'user_match_fields' => $ha::USER_MATCH_TYPES,
	'merge_names_options' => $ha::MERGE_NAMES_OPTIONS
]);

$tpl->display(PLUGIN_ROOT . '/templates/config_client.tpl');

Modified helloasso/admin/order.php from [7ed6bfb7c3] to [edd8895eee].

1
2
3
4
5
6
7
8





9
10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25
26
27


28
29
30
<?php

namespace Garradin;

use Garradin\Plugin\HelloAsso\Orders;
use Garradin\Plugin\HelloAsso\Payments;
use Garradin\Plugin\HelloAsso\Items;
use Garradin\Plugin\HelloAsso\Options;






require __DIR__ . '/_inc.php';

$order = Orders::get((int)qg('id'));

if (!$order) {
	throw new UserException('Commande inconnue');
}


$payments = Payments::list($order);
$items = Items::list($order);
$options = Options::list($order);

$payer_infos = $order->getPayerInfos();

//$found_user = $ha->findUserForPayment($order->payer);
//$mapped_user = $ha->getMappedUser($order->payer);
$found_user = $mapped_user = [];



$tpl->assign(compact('order', 'payments', 'items', 'options', 'payer_infos', 'found_user', 'mapped_user'));

$tpl->display(PLUGIN_ROOT . '/templates/order.tpl');








>
>
>
>
>









>










>
>
|


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php

namespace Garradin;

use Garradin\Plugin\HelloAsso\Orders;
use Garradin\Plugin\HelloAsso\Payments;
use Garradin\Plugin\HelloAsso\Items;
use Garradin\Plugin\HelloAsso\Options;
use Garradin\Plugin\HelloAsso\HelloAsso as HA;

use KD2\DB\EntityManager as EM;

use Garradin\Entities\Users\User;

require __DIR__ . '/_inc.php';

$order = Orders::get((int)qg('id'));

if (!$order) {
	throw new UserException('Commande inconnue');
}

$user = $order->id_user ? EM::findOneById(User::class, (int)$order->id_user) : null;
$payments = Payments::list($order);
$items = Items::list($order);
$options = Options::list($order);

$payer_infos = $order->getPayerInfos();

//$found_user = $ha->findUserForPayment($order->payer);
//$mapped_user = $ha->getMappedUser($order->payer);
$found_user = $mapped_user = [];

$user_match_field_label = (int)$plugin->getConfig()->user_match_type;

$tpl->assign(compact('order', 'user', 'payments', 'items', 'options', 'payer_infos', 'found_user', 'mapped_user', 'user_match_field_label'));

$tpl->display(PLUGIN_ROOT . '/templates/order.tpl');

Modified helloasso/admin/sync.php from [24cb973245] to [bddd888e91].

1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17

18



19
20
21
22
23
24
25
<?php

namespace Garradin;

require __DIR__ . '/_inc.php';

use KD2\DB\EntityManager as EM;
use Garradin\Entities\Accounting\Account;

use Garradin\Plugin\HelloAsso\Forms;

use Garradin\Plugin\HelloAsso\Entities\Chargeable;
use Garradin\Plugin\HelloAsso\Chargeables;

$csrf_key = 'sync';

$form->runIf('sync', function() use ($ha) {
	$ha->sync();

}, $csrf_key, PLUGIN_ADMIN_URL . 'sync.php?ok=1');




$default_ca = EM::findOneById(Account::class, (int)$plugin->getConfig()->id_credit_account);
$default_da = EM::findOneById(Account::class, (int)$plugin->getConfig()->id_debit_account);

$tpl->assign([
	'last_sync' => $ha->getLastSync(),
	'csrf_key' => $csrf_key,










>





|

>
|
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

namespace Garradin;

require __DIR__ . '/_inc.php';

use KD2\DB\EntityManager as EM;
use Garradin\Entities\Accounting\Account;

use Garradin\Plugin\HelloAsso\Forms;
use Garradin\Plugin\HelloAsso\Items;
use Garradin\Plugin\HelloAsso\Entities\Chargeable;
use Garradin\Plugin\HelloAsso\Chargeables;

$csrf_key = 'sync';

$form->runIf('sync', function() use ($ha, $tpl) {
	$ha->sync();
	if (!$exceptions = Items::getExceptions()) {
		Utils::redirect(PLUGIN_ADMIN_URL . 'sync.php?ok=1');
	}
	$tpl->assign('exceptions', $exceptions);
}, $csrf_key);

$default_ca = EM::findOneById(Account::class, (int)$plugin->getConfig()->id_credit_account);
$default_da = EM::findOneById(Account::class, (int)$plugin->getConfig()->id_debit_account);

$tpl->assign([
	'last_sync' => $ha->getLastSync(),
	'csrf_key' => $csrf_key,
42
43
44
45
46
47
48



49
50
51
52
			{
				$source[$id_item]['credit'] = (int)$id_credit_account;
				$source[$id_item]['debit'] = (int)$id_debit_account;
			}
		}
		Chargeables::setAccounts($source);
	}



	$ha->sync();
}, null, PLUGIN_ADMIN_URL . 'sync.php?ok=1');

$tpl->display(PLUGIN_ROOT . '/templates/sync.tpl');







>
>
>




47
48
49
50
51
52
53
54
55
56
57
58
59
60
			{
				$source[$id_item]['credit'] = (int)$id_credit_account;
				$source[$id_item]['debit'] = (int)$id_debit_account;
			}
		}
		Chargeables::setAccounts($source);
	}
	if (array_key_exists('register_user', $_POST)) {
		Chargeables::setUserRegistrators(array_keys($_POST['register_user']));
	}
	$ha->sync();
}, null, PLUGIN_ADMIN_URL . 'sync.php?ok=1');

$tpl->display(PLUGIN_ROOT . '/templates/sync.tpl');

Modified helloasso/lib/API.php from [9076f8fdd9] to [49de7a7f7a].

10
11
12
13
14
15
16











17
18
19
20
21
22
23
use KD2\HTTP;
use KD2\DB\EntityManager;

class API
{
	//const BASE_URL = 'https://api.helloasso.com/';
	const BASE_URL = 'https://api.helloasso-sandbox.com/';












	protected $ha;
	protected $oauth;
	protected $client_id;

	static protected $_instance = null;








>
>
>
>
>
>
>
>
>
>
>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
use KD2\HTTP;
use KD2\DB\EntityManager;

class API
{
	//const BASE_URL = 'https://api.helloasso.com/';
	const BASE_URL = 'https://api.helloasso-sandbox.com/';
	const PAYER_FIELDS = [
		'firstName' => 'Prénom',
		'lastName' => 'Nom',
		'email' => 'Courriel',
		'address' => 'Adresse',
		'city' => 'Ville',
		'zipCode' => 'Code postale',
		'country' => 'Pays',
		'dateOfBirth' => 'Date de naissance',
		'company' => 'Association/entreprise'
	];

	protected $ha;
	protected $oauth;
	protected $client_id;

	static protected $_instance = null;

Modified helloasso/lib/ChargeableInterface.php from [c5d759431e] to [eb05565996].

1
2
3
4
5
6
7
8
9

10
<?php

namespace Garradin\Plugin\HelloAsso;

interface ChargeableInterface
{
	public function getItemId(): ?int;
	public function getLabel(): string;
	public function getAmount(): ?int;

}









>

1
2
3
4
5
6
7
8
9
10
11
<?php

namespace Garradin\Plugin\HelloAsso;

interface ChargeableInterface
{
	public function getItemId(): ?int;
	public function getLabel(): string;
	public function getAmount(): ?int;
	public function setUserId(?int $id): void;
}

Modified helloasso/lib/Chargeables.php from [4bb6b22054] to [e6e3a29971].

84
85
86
87
88
89
90




91
92
93
94
95
96
97
				'label' => 'Libellé',
				'select' => 'c.label'
			],
			'amount' => [
				'label' => 'Montant',
				'select' => 'c.amount'
			],




			'credit_account' => [
				'label' => 'Recette',
				'select' => 'ca.code'
			],
			'id_credit_account' => [
				'select' => 'c.id_credit_account'
			],







>
>
>
>







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
				'label' => 'Libellé',
				'select' => 'c.label'
			],
			'amount' => [
				'label' => 'Montant',
				'select' => 'c.amount'
			],
			'register_user' => [
				'label' => 'Inscrip. Auto',
				'select' => 'c.register_user'
			],
			'credit_account' => [
				'label' => 'Recette',
				'select' => 'ca.code'
			],
			'id_credit_account' => [
				'select' => 'c.id_credit_account'
			],
131
132
133
134
135
136
137


138
139
140
141
142
143
144
		$list->setTitle(sprintf('%s - Items', $for->name));

		$list->setModifier(function ($row) {
			$row->type_label = ($row->id_item !== null) ? (Item::TYPES[$row->item_type] ?? 'Inconnu') : (Form::TYPES[$row->form_type] ?? 'Inconnu');
			if ($row->type === Chargeable::OPTION_TYPE) {
				$row->type_label .= ' - ' . Chargeable::TYPES[$row->type];
			}



			if (isset($row->custom_fields)) {
				$row->custom_fields = json_decode($row->custom_fields, true);
			}
		});

		$list->setExportCallback(function (&$row) {







>
>







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
		$list->setTitle(sprintf('%s - Items', $for->name));

		$list->setModifier(function ($row) {
			$row->type_label = ($row->id_item !== null) ? (Item::TYPES[$row->item_type] ?? 'Inconnu') : (Form::TYPES[$row->form_type] ?? 'Inconnu');
			if ($row->type === Chargeable::OPTION_TYPE) {
				$row->type_label .= ' - ' . Chargeable::TYPES[$row->type];
			}
			
			$row->register_user = $row->register_user ? 'oui' : '';

			if (isset($row->custom_fields)) {
				$row->custom_fields = json_decode($row->custom_fields, true);
			}
		});

		$list->setExportCallback(function (&$row) {
187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202
203
204
205
206












	{
		$chargeable = new Chargeable();
		$chargeable->set('type', $type);
		$chargeable->set('id_form', $id_form);
		$chargeable->set('id_item', $entity->getItemId());
		$chargeable->set('label', $entity->getLabel());
		$chargeable->set('amount', ($type === Chargeable::ONLY_ONE_ITEM_FORM_TYPE ? null : $entity->getAmount()));

		$chargeable->save();
		return $chargeable;
	}

	static public function setAccounts(array $source): void
	{
		foreach ($source as $id => $accounts) {
			if (DB::getInstance()->exec(sprintf('UPDATE %s SET id_credit_account = %d, id_debit_account = %d WHERE id = %d;', Chargeable::TABLE, $accounts['credit'], $accounts['debit'], (int)$id)) === false) {
				throw new \RuntimeException(sprintf('Cannot update %s plugin Items\' accounting accounts.', HelloAsso::PROVIDER_LABEL));
			}
		}
	}
}



















>












|
>
>
>
>
>
>
>
>
>
>
>
>
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
	{
		$chargeable = new Chargeable();
		$chargeable->set('type', $type);
		$chargeable->set('id_form', $id_form);
		$chargeable->set('id_item', $entity->getItemId());
		$chargeable->set('label', $entity->getLabel());
		$chargeable->set('amount', ($type === Chargeable::ONLY_ONE_ITEM_FORM_TYPE ? null : $entity->getAmount()));
		$chargeable->set('register_user', 0);
		$chargeable->save();
		return $chargeable;
	}

	static public function setAccounts(array $source): void
	{
		foreach ($source as $id => $accounts) {
			if (DB::getInstance()->exec(sprintf('UPDATE %s SET id_credit_account = %d, id_debit_account = %d WHERE id = %d;', Chargeable::TABLE, $accounts['credit'], $accounts['debit'], (int)$id)) === false) {
				throw new \RuntimeException(sprintf('Cannot update %s plugin Items\' accounting accounts.', HelloAsso::PROVIDER_LABEL));
			}
		}
	}

	static public function setUserRegistrators(array $ids): void
	{
		foreach ($ids as $id) {
			if (!is_int($id)) {
				throw new \InvalidArgumentException(sprintf('User (Chargeable) registrator ID must be an integer. "%s" provided.', $id));
			}
		}
		if (DB::getInstance()->exec(sprintf('UPDATE %s SET register_user = 1 WHERE id IN (%s);', Chargeable::TABLE, implode(', ', $ids))) === false) {
			throw new \RuntimeException(sprintf('Cannot set %s plugin Chargeables\' user registrators.', HelloAsso::PROVIDER_LABEL));
		}
	}
}

Modified helloasso/lib/Entities/Chargeable.php from [cd61ecfb35] to [a9128f3a79].

25
26
27
28
29
30
31

32
33
34
35
36
37
38
	protected int		$id_form;
	protected ?int		$id_item; // ONLY_ONE_ITEM_FORM_TYPE forms/payments have always only one item so we do not care about its value
	protected ?int		$id_credit_account;
	protected ?int		$id_debit_account;
	protected int		$type;
	protected string	$label;
	protected ?int		$amount;


	protected ?string	$_form_name = null;
	protected ?string	$_item_name = null;

	public function setForm_name(string $name): void
	{
		$this->_form_name = $name;







>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
	protected int		$id_form;
	protected ?int		$id_item; // ONLY_ONE_ITEM_FORM_TYPE forms/payments have always only one item so we do not care about its value
	protected ?int		$id_credit_account;
	protected ?int		$id_debit_account;
	protected int		$type;
	protected string	$label;
	protected ?int		$amount;
	protected int		$register_user;

	protected ?string	$_form_name = null;
	protected ?string	$_item_name = null;

	public function setForm_name(string $name): void
	{
		$this->_form_name = $name;

Modified helloasso/lib/Entities/Form.php from [5af3819487] to [0210547673].

45
46
47
48
49
50
51
52


		return $this->name;
	}

	public function getAmount(): ?int
	{
		return null;
	}
}









|
>
>
45
46
47
48
49
50
51
52
53
54
		return $this->name;
	}

	public function getAmount(): ?int
	{
		return null;
	}

	public function setUserId(?int $id): void {}
}

Modified helloasso/lib/Entities/Item.php from [c17f6f487f] to [7f7435b0dd].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
<?php

namespace Garradin\Plugin\HelloAsso\Entities;

use Garradin\Entity;
use Garradin\Plugin\HelloAsso\ChargeableInterface;

class Item extends Entity implements ChargeableInterface
{
	const TABLE = 'plugin_helloasso_items';

	protected int $id;
	protected int $id_order;
	protected int $id_form;
	protected ?int $id_user;
	protected ?int $id_transaction;
	protected string $type;
	protected string $state;
	protected string $label;
	protected string $person;
	protected int $amount;
	protected int $has_options;
	protected ?string $custom_fields;

	protected string $raw_data;

	const TYPES = [
		'Donation'        => 'Don',
		'Payment'         => 'Paiement',
		'Registration'    => 'Inscription',
		'Membership'      => 'Adhésion',
		'MonthlyDonation' => 'Don mensuel',











|
|
|
|
|
|
|
|
|
|
|
|
>
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

namespace Garradin\Plugin\HelloAsso\Entities;

use Garradin\Entity;
use Garradin\Plugin\HelloAsso\ChargeableInterface;

class Item extends Entity implements ChargeableInterface
{
	const TABLE = 'plugin_helloasso_items';

	protected int			$id;
	protected int			$id_order;
	protected int			$id_form;
	protected ?int			$id_user;
	protected ?int			$id_transaction;
	protected string		$type;
	protected string		$state;
	protected string		$label;
	protected string		$person;
	protected int			$amount;
	protected int			$has_options;
	/*protected ?string $custom_fields;*/
	protected ?\stdClass	$custom_fields; // Is a mix between real HelloAsso custom fields and plugin generated infos during sync
	protected string		$raw_data;

	const TYPES = [
		'Donation'        => 'Don',
		'Payment'         => 'Paiement',
		'Registration'    => 'Inscription',
		'Membership'      => 'Adhésion',
		'MonthlyDonation' => 'Don mensuel',
56
57
58
59
60
61
62
63





		return $this->label;
	}

	public function getAmount(): ?int
	{
		return $this->amount;
	}
}












|
>
>
>
>
>
57
58
59
60
61
62
63
64
65
66
67
68
69
		return $this->label;
	}

	public function getAmount(): ?int
	{
		return $this->amount;
	}

	public function setUserId(?int $id): void
	{
		$this->set('id_user', $id);
	}
}

Modified helloasso/lib/Entities/Option.php from [36ff74212e] to [64d137b929].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35





<?php

namespace Garradin\Plugin\HelloAsso\Entities;

use Garradin\Entity;
use Garradin\Plugin\HelloAsso\ChargeableInterface;

class Option extends Entity implements ChargeableInterface
{
	const TABLE = 'plugin_helloasso_item_options';

	protected int		$id;
	protected int		$id_item;
	protected int		$id_order; // Redundant but needed by DynamicList since it does not handle JOIN statement
	protected ?int		$id_transaction;

	protected string	$label;
	protected int		$amount;
	protected ?string	$custom_fields;
	protected string	$raw_data;

	public function getItemId(): ?int
	{
		return $this->id_item;
	}

	public function getLabel(): string
	{
		return $this->label;
	}

	public function getAmount(): ?int
	{
		return $this->amount;
	}
}
















|
|
|
|
>
|
|
|
|















|
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php

namespace Garradin\Plugin\HelloAsso\Entities;

use Garradin\Entity;
use Garradin\Plugin\HelloAsso\ChargeableInterface;

class Option extends Entity implements ChargeableInterface
{
	const TABLE = 'plugin_helloasso_item_options';

	protected int			$id;
	protected int			$id_item;
	protected int			$id_order; // Redundant but needed by DynamicList since it does not handle JOIN statement
	protected ?int			$id_user;
	protected ?int			$id_transaction;
	protected string		$label;
	protected int			$amount;
	protected ?\stdClass	$custom_fields;
	protected string		$raw_data;

	public function getItemId(): ?int
	{
		return $this->id_item;
	}

	public function getLabel(): string
	{
		return $this->label;
	}

	public function getAmount(): ?int
	{
		return $this->amount;
	}

	public function setUserId(?int $id): void
	{
		$this->set('id_user', $id);
	}
}

Modified helloasso/lib/Entities/Order.php from [8a884c7098] to [a5cda5cb7a].

46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

		return $paid >= $total ? self::STATUS_PAID : self::STATUS_WAITING;
	}

	public function getPayerInfos(): array
	{
		$data = json_decode($this->raw_data);
		return $data ? Payment::getPayerInfos($data->payer) : [];
	}

	public function listItems(): array
	{
		return EM::getInstance(Item::class)->all('SELECT * FROM @TABLE WHERE id_order = ? ORDER BY id DESC;', $this->id());
	}








|







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

		return $paid >= $total ? self::STATUS_PAID : self::STATUS_WAITING;
	}

	public function getPayerInfos(): array
	{
		$data = json_decode($this->raw_data);
		return $data ? Payment::formatPersonInfos($data->payer) : [];
	}

	public function listItems(): array
	{
		return EM::getInstance(Item::class)->all('SELECT * FROM @TABLE WHERE id_order = ? ORDER BY id DESC;', $this->id());
	}

Modified helloasso/lib/Entities/Payment.php from [4b8457a9fe] to [8632806434].

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
		'address'     => 'Adresse postale',
		'zipCode'     => 'Code postal',
		'city'        => 'Ville',
		'country'     => 'Pays',
		'dateOfBirth' => 'Date de naissance',
	];

	static public function getPayerName(\stdClass $payer)
	{
		$names = [!empty($payer->company) ? $payer->company . ' — ' : null, $payer->firstName ?? null, $payer->lastName ?? null];
		$names = array_filter($names);

		$names = implode(' ', $names);

		if (!empty($payer->city)) {
			$names .= sprintf(' (%s)', $payer->city);
		}

		return $names;
	}

	static public function getPayerInfos(\stdClass $payer)
	{
		$data = [];

		foreach (self::PAYER_FIELDS as $key => $name) {
			if (!isset($payer->$key)) {
				continue;
			}

			$value = $payer->$key;

			if ($key == 'dateOfBirth') {
				$value = new \DateTime($value);
			}

			$data[$name] = $value;
		}

		return $data;
	}
}







|

|




|
|





|




|



|











53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
		'address'     => 'Adresse postale',
		'zipCode'     => 'Code postal',
		'city'        => 'Ville',
		'country'     => 'Pays',
		'dateOfBirth' => 'Date de naissance',
	];

	static public function getPersonName(\stdClass $person)
	{
		$names = [!empty($person->company) ? $person->company . ' — ' : null, $person->firstName ?? null, $person->lastName ?? null];
		$names = array_filter($names);

		$names = implode(' ', $names);

		if (!empty($person->city)) {
			$names .= sprintf(' (%s)', $person->city);
		}

		return $names;
	}

	static public function formatPersonInfos(\stdClass $person)
	{
		$data = [];

		foreach (self::PAYER_FIELDS as $key => $name) {
			if (!isset($person->$key)) {
				continue;
			}

			$value = $person->$key;

			if ($key == 'dateOfBirth') {
				$value = new \DateTime($value);
			}

			$data[$name] = $value;
		}

		return $data;
	}
}

Modified helloasso/lib/HelloAsso.php from [da9f5f27f3] to [824267f35d].

1
2
3
4
5
6

7
8
9
10
11
12
13
<?php

namespace Garradin\Plugin\HelloAsso;

use Garradin\Config;
use Garradin\DB;

use Garradin\Entities\Plugin;
use Garradin\Entities\Users\User;
use Garradin\Entities\Payments\Payment;
use KD2\DB\EntityManager;

use Garradin\Plugin\HelloAsso\Entities\Form;







>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

namespace Garradin\Plugin\HelloAsso;

use Garradin\Config;
use Garradin\DB;
use Garradin\Users\DynamicFields;
use Garradin\Entities\Plugin;
use Garradin\Entities\Users\User;
use Garradin\Entities\Payments\Payment;
use KD2\DB\EntityManager;

use Garradin\Plugin\HelloAsso\Entities\Form;

28
29
30
31
32
33
34
35
36

37
38




39
40
41



42
43
44
45
46
47
48
49
50
	const REDIRECTION_FILE = 'payer.php';
	const PER_PAGE = 100;

	const MERGE_NAMES_FIRST_LAST = 0;
	const MERGE_NAMES_LAST_FIRST = 1;

	const MERGE_NAMES_OPTIONS = [
		self::MERGE_NAMES_FIRST_LAST => 'Prénom Nom',
		self::MERGE_NAMES_LAST_FIRST => 'Nom Prénom',

	];





	protected $plugin;
	protected ?\stdClass $config;





	static protected $_instance;

	static public function getInstance()
	{
		if (null === self::$_instance) {
			self::$_instance = new self;
		}








<

>


>
>
>
>
|
|

>
>
>

|







29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
	const REDIRECTION_FILE = 'payer.php';
	const PER_PAGE = 100;

	const MERGE_NAMES_FIRST_LAST = 0;
	const MERGE_NAMES_LAST_FIRST = 1;

	const MERGE_NAMES_OPTIONS = [

		self::MERGE_NAMES_LAST_FIRST => 'Nom Prénom',
		self::MERGE_NAMES_FIRST_LAST => 'Prénom Nom'
	];

	const USER_MATCH_NAME = 0;
	const USER_MATCH_EMAIL = 1;
	const USER_MATCH_TYPES = [ self::USER_MATCH_NAME => 'Nom et prénom', self::USER_MATCH_EMAIL => 'Courriel' ];

	protected				$plugin;
	protected ?\stdClass	$config;

	static protected ?array	$_userMatchField = null;
	static protected int	$_mergeNamesOption;
	static protected array	$_existingUsersCache = [];

	static protected		$_instance;

	static public function getInstance()
	{
		if (null === self::$_instance) {
			self::$_instance = new self;
		}

122
123
124
125
126
127
128

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143




144
145
146
147
148
149
150
	}

	public function initConfig(): bool {
		$this->plugin->setConfigProperty('accounting', self::ACCOUNTING_ENABLED);
		$this->plugin->setConfigProperty('client_id', '');
		$this->plugin->setConfigProperty('id_credit_account', false);
		$this->plugin->setConfigProperty('id_debit_account', false);

		return $this->plugin->save();
	}

	public function saveConfig(array $data): bool
	{
		/* Old code to rebuild
		 * saveConfig(array $map, $merge_names, $match_email_field)
		$this->plugin->setConfigProperty('merge_names', (int) $merge_names);
		$this->plugin->setConfigProperty('match_email_field', (bool) $match_email_field);
		$this->plugin->setConfigProperty('map_user_fields', $map);*/
		$this->plugin->setConfigProperty('accounting', $data['accounting']);
		if (array_key_exists('default_credit', $data))
			$this->plugin->setConfigProperty('id_credit_account', array_keys($data['default_credit'])[0]);
		if (array_key_exists('default_debit', $data))
			$this->plugin->setConfigProperty('id_debit_account', array_keys($data['default_debit'])[0]);




		return $this->plugin->save();
	}

	public function isConfigured(): bool
	{
		return empty($this->config->oauth) ? false : true;
	}







>





<
<
<
<
<





>
>
>
>







130
131
132
133
134
135
136
137
138
139
140
141
142





143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
	}

	public function initConfig(): bool {
		$this->plugin->setConfigProperty('accounting', self::ACCOUNTING_ENABLED);
		$this->plugin->setConfigProperty('client_id', '');
		$this->plugin->setConfigProperty('id_credit_account', false);
		$this->plugin->setConfigProperty('id_debit_account', false);
		$this->plugin->setConfigProperty('id_category', false);
		return $this->plugin->save();
	}

	public function saveConfig(array $data): bool
	{





		$this->plugin->setConfigProperty('accounting', $data['accounting']);
		if (array_key_exists('default_credit', $data))
			$this->plugin->setConfigProperty('id_credit_account', array_keys($data['default_credit'])[0]);
		if (array_key_exists('default_debit', $data))
			$this->plugin->setConfigProperty('id_debit_account', array_keys($data['default_debit'])[0]);
		$this->plugin->setConfigProperty('id_category', (int)$data['id_category']);
		$this->plugin->setConfigProperty('payer_map', $data['payer_map']);
		$this->plugin->setConfigProperty('user_match_type', $data['user_match_type']);
		$this->plugin->setConfigProperty('user_match_field', $data['user_match_field']);
		return $this->plugin->save();
	}

	public function isConfigured(): bool
	{
		return empty($this->config->oauth) ? false : true;
	}
396
397
398
399
400
401
402




403
















































			return true;
		}
		else {
			return false;
		}
	}





}























































>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
			return true;
		}
		else {
			return false;
		}
	}

	static public function guessUserIdentifier(\stdClass $source): ?string
	{
		if (self::getUserMatchField()[1] === 'name') {
			return self::guessUserName($source);
		}
		if (isset($source->email))
			return $source->email;

		return $source->{self::getUserMatchField()[2]} ?? null;
	}

	static public function guessUserName(\stdClass $source): string
	{
		if (self::getInstance()->plugin()->getConfig()->payer_map->name === HelloAsso::MERGE_NAMES_FIRST_LAST) {
			return $source->firstName . ' ' . $source->lastName;
		}
		return $source->lastName . ' ' . $source->firstName;
	}

	static public function userAlreadyExists(string $identifier): bool
	{
		return (bool)self::getUserId($identifier);
	}

	static public function getUserId(string $identifier): ?int
	{
		if (array_key_exists($identifier, self::$_existingUsersCache)) {
			return self::$_existingUsersCache[$identifier];
		}
		$id_user = EntityManager::getInstance(User::class)->col(sprintf('SELECT id FROM @TABLE WHERE %s = ?;', self::getUserMatchField()[0]), $identifier);
		self::$_existingUsersCache[$identifier] = (false === $id_user) ? null : $id_user;

		return self::$_existingUsersCache[$identifier];
	}

	static public function addUserToCache(string $identifier, int $id_user): void
	{
		self::$_existingUsersCache[$identifier] = $id_user;
	}

	static public function getUserMatchField(): array
	{
		if (null === self::$_userMatchField) {
			if (self::getInstance()->plugin()->getConfig()->user_match_type === self::USER_MATCH_NAME) {
				self::$_userMatchField = [ DynamicFields::getFirstNameField(), 'name', null ];
			}
			else {
				self::$_userMatchField = [ DynamicFields::getFirstEmailField(), 'email', self::getInstance()->plugin()->getConfig()->user_match_field ];
			}
		}
		return self::$_userMatchField;
	}
}

Modified helloasso/lib/Items.php from [8e07481fa3] to [98194df7c2].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19



20
21
22


23
24
25
26
27
28
29






30
31
32
33
34
35
36
37
38
39



40

41
42
43
44
45
46

47
48
49

50
51
52

53
54
55
56








57
58
59
60
61
62
63
64
65
66
67
68
69
70


71
72
73
74
75
76
77
<?php

namespace Garradin\Plugin\HelloAsso;

use Garradin\Plugin\HelloAsso\Entities\Form;
use Garradin\Plugin\HelloAsso\Entities\Item;
use Garradin\Plugin\HelloAsso\Entities\Chargeable;
use Garradin\Plugin\HelloAsso\Entities\Option;
use Garradin\Plugin\HelloAsso\Entities\Order;
use Garradin\Plugin\HelloAsso\Entities\Payment;
use Garradin\Plugin\HelloAsso\API;
use Garradin\Plugin\HelloAsso\HelloAsso;
use Garradin\Plugin\HelloAsso\ChargeableInterface;

use Garradin\DB;
use Garradin\DynamicList;
use Garradin\Utils;
use Garradin\Entities\Accounting\Transaction;
use Garradin\Accounting\Years;



use Garradin\Plugin\HelloAsso\Payments;

use KD2\DB\EntityManager as EM;



class Items
{
	const TRANSACTION_PREFIX = 'Item';
	const TRANSACTION_NOTE = 'Générée automatiquement par l\'extension ' . HelloAsso::PROVIDER_LABEL . '.';
	const DONATION_LABEL = 'Don';
	const CHECKOUT_LABEL = 'Commande #%d (%s)';







	static public function get(int $id): ?Item
	{
		return EM::findOneById(Item::class, $id);
	}

	static public function list($for): DynamicList
	{
		$columns = [
			'id' => [



				'label' => 'Référence'

			],
			'id_transaction' => [
				'label' => 'Écriture'
			],
			'amount' => [
				'label' => 'Montant'

			],
			'type' => [
				'label' => 'Type'

			],
			'label' => [
				'label' => 'Objet'

			],
			'person' => [
				'label' => 'Personne'
			],








			'options' => [
				'label' => 'Options',
				'select' => "(CASE WHEN has_options THEN 'oui' ELSE '-' END)"
			], // sprintf("(SELECT (CASE WHEN COUNT(id) > 0 THEN 'oui' ELSE '-' END) FROM %s o WHERE o.id_item = %s.id)", Option::TABLE, Item::TABLE)
			'custom_fields' => [
				'label' => 'Champs'
			],
			'state' => [
				'label' => 'Statut'
			],
			'id_order' => [],
		];

		$tables = Item::TABLE;



		if ($for instanceof Form) {
			unset($columns['custom_fields']);
		}

		$list = new DynamicList($columns, $tables);












|







>
>
>



>
>




|


>
>
>
>
>
>










>
>
>
|
>





|
>


|
>


|
>




>
>
>
>
>
>
>
>













|
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<?php

namespace Garradin\Plugin\HelloAsso;

use Garradin\Plugin\HelloAsso\Entities\Form;
use Garradin\Plugin\HelloAsso\Entities\Item;
use Garradin\Plugin\HelloAsso\Entities\Chargeable;
use Garradin\Plugin\HelloAsso\Entities\Option;
use Garradin\Plugin\HelloAsso\Entities\Order;
use Garradin\Plugin\HelloAsso\Entities\Payment;
use Garradin\Plugin\HelloAsso\API;
use Garradin\Plugin\HelloAsso\HelloAsso as HA;
use Garradin\Plugin\HelloAsso\ChargeableInterface;

use Garradin\DB;
use Garradin\DynamicList;
use Garradin\Utils;
use Garradin\Entities\Accounting\Transaction;
use Garradin\Accounting\Years;
use Garradin\Users\DynamicFields;
use Garradin\Users\Users;
use Garradin\Entities\Users\User;
use Garradin\Plugin\HelloAsso\Payments;

use KD2\DB\EntityManager as EM;

use Garradin\Plugin\HelloAsso\Mock\MockItems;

class Items
{
	const TRANSACTION_PREFIX = 'Item';
	const TRANSACTION_NOTE = 'Générée automatiquement par l\'extension ' . HA::PROVIDER_LABEL . '.';
	const DONATION_LABEL = 'Don';
	const CHECKOUT_LABEL = 'Commande #%d (%s)';
	const DUPLICATE_MEMBER_PREFIX = 'Doublon-%s-';

	static protected string	$_nameField;
	static protected array	$_userFieldsMap;
	static protected array	$_userIdsByLoginCache = []; // Used when userMatchField is different from the Paheko login field
	static protected array	$_exceptions = [];

	static public function get(int $id): ?Item
	{
		return EM::findOneById(Item::class, $id);
	}

	static public function list($for): DynamicList
	{
		$columns = [
			'id' => [
				'select' => 'i.id'
			],
			'id_chargeable' => [
				'label' => 'Référence',
				'select' => 'c.id'
			],
			'id_transaction' => [
				'label' => 'Écriture'
			],
			'amount' => [
				'label' => 'Montant',
				'select' => 'i.amount'
			],
			'type' => [
				'label' => 'Type',
				'select' => 'i.type'
			],
			'label' => [
				'label' => 'Objet',
				'select' => 'i.label'
			],
			'person' => [
				'label' => 'Personne'
			],
			'id_user' => [],
			'user_name' => [
				'label' => 'Membre correspondant*',
				'select' => 'u.nom'
			],
			'numero' => [
				'select' => 'u.numero'
			],
			'options' => [
				'label' => 'Options',
				'select' => "(CASE WHEN has_options THEN 'oui' ELSE '-' END)"
			], // sprintf("(SELECT (CASE WHEN COUNT(id) > 0 THEN 'oui' ELSE '-' END) FROM %s o WHERE o.id_item = %s.id)", Option::TABLE, Item::TABLE)
			'custom_fields' => [
				'label' => 'Champs'
			],
			'state' => [
				'label' => 'Statut'
			],
			'id_order' => [],
		];

		$tables = Item::TABLE . ' i
			INNER JOIN ' . Chargeable::TABLE . ' c ON (c.id_form = i.id_form AND c.label = i.label AND c.amount = i.amount)
			LEFT JOIN ' . User::TABLE . ' u ON (u.id = i.id_user)';

		if ($for instanceof Form) {
			unset($columns['custom_fields']);
		}

		$list = new DynamicList($columns, $tables);

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110


















111
112
113

114
115
116
117
118
119
120
121
122
123
124



125

126
127

128

129
130
131
132
133
134
135
136
137
138
139
140
141
142
































143
144
145
146
147
148
149
150
151
152
153
154
155
156





157





158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209




210
211


212
213
214
















































































































215
216
217
218








219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251






252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
			}
		});

		$list->setExportCallback(function (&$row) {
			$row->amount = $row->amount ? Utils::money_format($row->amount, '.', '', false) : null;

			// Serialize custom fields as a text field
			if (isset($row->custom_fields)) {
				$row->custom_fields = implode("\n", array_map(function ($v, $k) { return "$k: $v"; },
					$row->custom_fields, array_keys($row->custom_fields)));
			}
		});

		$list->orderBy('id', true);
		return $list;
	}



















	static public function sync(string $org_slug, bool $accounting = true): void
	{

		$params = [
			'pageSize'  => HelloAsso::getPageSize(),
		];

		$page_count = 1;

		for ($i = 1; $i <= $page_count; $i++) {
			$params['pageIndex'] = $i;
			$result = API::getInstance()->listOrganizationItems($org_slug, $params);
			$page_count = $result->pagination->totalPages;




			foreach ($result->data as $order) {

				self::syncItem($order, $accounting);
			}



			if (HelloAsso::isTrial()) {
				break;
			}
		}
	}

	static protected function syncItem(\stdClass $data, bool $accounting): Item
	{
		$entity = self::get($data->id) ?? new Item;

		$entity->set('raw_data', json_encode($data));

		$data = self::transform($data);

































		if (!$entity->exists()) {
			$entity->set('id', $data->id);
			$entity->set('id_order', $data->order_id);
			$entity->set('id_form', Forms::getId($data->org_slug, $data->form_slug));
		}

		$entity->set('amount', $data->amount);
		$entity->set('state', $data->state);
		$entity->set('type', $data->type);
		$entity->set('person', $data->user_name ?? $data->payer_name);
		$entity->set('label', self::generateLabel($data, (int)$entity->id_form));
		$entity->set('custom_fields', count($data->fields) ? json_encode($data->fields) : null);
		$entity->set('has_options', (int)isset($data->options));






		$entity->save();






		$optionEntities = [];
		if (isset($data->options)) {
			foreach ($data->options as $option) {
				$optionEntities[] = self::syncOption($option, $data, $entity->id, $accounting);
			}
		}
		// Creating a transaction only if payment is unique and already done (not pending) and accounts sets
		if ($accounting && !$entity->id_transaction && (count($data->payments) === 1 && $data->payments[0]->state === Payments::AUTHORIZED_STATUS))
		{
			if ($entity->amount) {
				if ($data->order->formType !== 'Checkout') { // All cases except Checkout
					self::accountChargeable((int)$entity->id_form, $entity, Chargeable::TYPE_FROM_FORM[$data->order->formType], (int)$data->payments[0]->id, new \DateTime($data->payments[0]->date));
				}
				else
				{
					if (!$payment = Payments::get((int)$data->payments[0]->id)) {
						throw new \RuntimeException(sprintf('Payment #%d matching checkout item #%d not found.', $data->payments[0]->id, $entity->id));
					}
					if (isset($payment->id_credit_account) && $payment->id_credit_account && $payment->id_debit_account) {// This feature will be available once the ChekoutIntent callback is fixed
						$transaction = self::createTransaction($entity, [$payment->id_credit_account, $payment->id_debit_account], (int)$data->payments[0]->id, $payment->date);
						$payment->set('id_transaction', $transaction->id);
					}
					elseif (self::accountChargeable((int)$entity->id_form, $entity, Chargeable::TYPE_FROM_FORM[$data->order->formType], (int)$data->payments[0]->id, new \DateTime($data->payments[0]->date))) {
						$payment->set('id_transaction', $entity->id_transaction);
					}
					$payment->save();
				}
			}
			if (isset($data->options)) {
				foreach ($optionEntities as $option) {
					self::accountChargeable((int)$entity->id_form, $option, Chargeable::OPTION_TYPE, (int)$data->payments[0]->id, new \DateTime($data->payments[0]->date));
				}
			}
		}

		return $entity;
	}

	static protected function syncOption(\stdClass $data, \stdClass $full_data, int $id_item, bool $accounting): Option
	{
		$option = EM::findOne(Option::class, 'SELECT * FROM @TABLE WHERE id_item = :id_item AND label = :name AND amount = :amount', $id_item, $data->name, $data->amount) ?? new Option;
		$option->set('raw_data', json_encode($data));
		$data = self::transformOption($data);
		
		if (!$option->exists()) {
			$option->set('id_item', (int)$full_data->id);
			$option->set('id_order', (int)$full_data->order_id);
		}
		$option->set('amount', $data->amount);
		$option->set('label', $data->name ?? Forms::getName($option->id_form));
		$option->set('custom_fields', count($data->fields) ? json_encode($data->fields) : null);




		$option->save();



		return $option;
	}

















































































































	static protected function accountChargeable(int $id_form, ChargeableInterface $entity, int $type, int $payment_ref, \DateTime $date): bool
	{
		$amount = ($type === Chargeable::ONLY_ONE_ITEM_FORM_TYPE ? null : $entity->getAmount());
		$chargeable = Chargeables::get($id_form, $type, $entity->getLabel(), $amount);








		if (null === $chargeable) {
			$chargeable = Chargeables::createChargeable($id_form, $entity, $type);
		}
		elseif ($chargeable->id_credit_account && $chargeable->id_debit_account) {
			$transaction = self::createTransaction($entity, [(int)$chargeable->id_credit_account, (int)$chargeable->id_debit_account], $payment_ref, $date);
			$entity->id_transaction = (int)$transaction->id;
			$entity->save();
			return true;
		}
		return false;
	}

	static protected function transform(\stdClass $data): \stdClass
	{
		$data->id = (int) $data->id;
		$data->order_id = (int) $data->order->id;
		$data->payer_name = isset($data->payer) ? Payment::getPayerName($data->payer) : null;
		$data->payer_infos = isset($data->payer) ? Payment::getPayerInfos($data->payer) : null;
		$data->user_name = isset($data->user) ? Payment::getPayerName($data->user) : null;
		$data->amount = (int) $data->amount;
		$data->form_slug = $data->order->formSlug;
		$data->org_slug = $data->order->organizationSlug;
		$data->fields = [];

		if (!empty($data->user)) {
			$data->fields = Payment::getPayerInfos($data->user);
		}

		if (!empty($data->customFields)) {
			foreach ($data->customFields as $field) {
				$data->fields[$field->name] = $field->answer;
			}
		}







		return $data;
	}

	static protected function transformOption(\stdClass $data): \stdClass
	{
		$data->fields = [];

		if (!empty($data->user)) {
			$data->fields = Payment::getPayerInfos($data->user);
		}

		if (!empty($data->customFields)) {
			foreach ($data->customFields as $field) {
				$data->fields[$field->name] = $field->answer;
			}
		}







|


|





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>

|









>
>
>

>
|
|
>
|
>
|







|
<
|



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|


|
|
|
|
|
|
|

>
>
>
>
>
|
>
>
>
>
>


<
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|


|










|
|
>
>
>
>


>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|


|
>
>
>
>
>
>
>
>



|












|
|
<



<

|
|
<






>
>
>
>
>
>









|







123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

254
255
256






























257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425

426
427
428

429
430
431

432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
			}
		});

		$list->setExportCallback(function (&$row) {
			$row->amount = $row->amount ? Utils::money_format($row->amount, '.', '', false) : null;

			// Serialize custom fields as a text field
			/*if (isset($row->custom_fields)) {
				$row->custom_fields = implode("\n", array_map(function ($v, $k) { return "$k: $v"; },
					$row->custom_fields, array_keys($row->custom_fields)));
			}*/
		});

		$list->orderBy('id', true);
		return $list;
	}

	static protected function init(): void
	{
		self::$_nameField = DynamicFields::getFirstNameField();
		self::setUserFieldsMap();
	}

	static protected function setUserFieldsMap(): void
	{
		$map = clone HA::getInstance()->plugin()->getConfig()->payer_map;
		unset($map->name); // name has a specific process
		foreach ($map as $api_field => $user_field) {
			if (null === $user_field) {
				unset($map->$api_field);
			}
		}
		self::$_userFieldsMap = array_flip((array)$map);
	}

	static public function sync(string $org_slug, bool $accounting = true): void
	{
		self::init();
		$params = [
			'pageSize'  => HA::getPageSize(),
		];

		$page_count = 1;

		for ($i = 1; $i <= $page_count; $i++) {
			$params['pageIndex'] = $i;
			$result = API::getInstance()->listOrganizationItems($org_slug, $params);
			$page_count = $result->pagination->totalPages;

			//$result->data = MockItems::donationAndOptions();
			//$result->data = MockItems::multipleSubscriptions();

			foreach ($result->data as $order) {
				try {
					self::syncItem($order, $accounting);
				}
				catch (SyncException $e) { self::catchSyncException($e); }
			}

			if (HA::isTrial()) {
				break;
			}
		}
	}

	static protected function syncItem(\stdClass $data, bool $accounting): Item
	{
		$item = self::get($data->id) ?? new Item;

		$item->set('raw_data', json_encode($data));

		$data = self::transform($data);

		self::setItem($item, $data);
		$item->save();

		try {
			self::handleUserRegistration($data, (int)$item->id_form, $item, Chargeable::TYPE_FROM_FORM[$data->order->formType]);
		}
		catch (SyncException $e) { self::catchSyncException($e); }
		
		try {
			$optionEntities = self::syncOptions($data, $item, $accounting);
		}
		catch (SyncException $e) { self::catchSyncException($e); }
		
		try {
			self::handleAccounting($item, $data, $optionEntities, $accounting);
		}
		catch (SyncException $e) { self::catchSyncException($e); }

		return $item;
	}

	static protected function setItem(Item $item, \stdClass $data): void
	{
		// ToDo: add some cache for those checks
		if (!EM::getInstance(Order::class)->col(sprintf('SELECT id FROM @TABLE WHERE id = :id_order;'), $data->order_id)) {
			throw new SyncException(sprintf('Tried to synchronized the item (ID: %d) of an inexisting (never synchronized?) order #%d.', $data->id, $data->order_id));
		}
		$id_form = Forms::getId($data->org_slug, $data->form_slug);
		if (!EM::getInstance(Form::class)->col(sprintf('SELECT id FROM @TABLE WHERE id = :id_order;'), $id_form)) {
			throw new SyncException(sprintf('Tried to synchronized the item (ID: %d) of an inexisting (never synchronized?) order #%d.', $data->id, $id_form));
		}

		if (!$item->exists()) {
			$item->set('id', $data->id);
			$item->set('id_order', $data->order_id);
			$item->set('id_form', $id_form);
		}

		$item->set('amount', $data->amount);
		$item->set('state', $data->state);
		$item->set('type', $data->type);
		$item->set('person', $data->beneficiary_label ?? $data->payer_name);
		$item->set('label', self::generateLabel($data, (int)$item->id_form));
		$item->set('custom_fields', count($data->fields) ? (object)$data->fields : null);
		$item->set('has_options', (int)isset($data->options));

		$identifier = HA::guessUserIdentifier($data->beneficiary);
		if ($identifier && ($id_user = HA::getUserId($identifier))) {
			$item->set('id_user', $id_user);
		}
	}

	static protected function syncOptions(\stdClass $data, Item $item, int $accounting): array
	{
		if (!isset($data->options)) {
			return [];
		}

		$optionEntities = [];

		foreach ($data->options as $option) {
			$optionEntities[] = self::syncOption($option, $data, $item->id_form, $item->id, $accounting);
		}






























		return $optionEntities;
	}

	static protected function syncOption(\stdClass $data, \stdClass $full_data, int $id_form, int $id_item, bool $accounting): Option
	{
		$option = EM::findOne(Option::class, 'SELECT * FROM @TABLE WHERE id_item = :id_item AND label = :name AND amount = :amount', $id_item, $data->name, $data->amount) ?? new Option;
		$option->set('raw_data', json_encode($data));
		$data = self::transformOption($data);
		
		if (!$option->exists()) {
			$option->set('id_item', (int)$full_data->id);
			$option->set('id_order', (int)$full_data->order_id);
		}
		$option->set('amount', $data->amount);
		$option->set('label', $data->name ?? Forms::getName($id_form));
		$option->set('custom_fields', count($data->fields) ? (object)$data->fields : null);
		$identifier = HA::guessUserIdentifier($full_data->beneficiary);
		if ($identifier && ($id_user = HA::getUserId($identifier))) {
			$option->set('id_user', $id_user);
		}
		$option->save();

		self::handleUserRegistration($full_data, $id_form, $option, Chargeable::OPTION_TYPE);

		return $option;
	}

	static protected function handleAccounting(Item $item, \stdClass $data, array $optionEntities, int $accounting): void
	{
		// Creating a transaction only if payment is unique and already done (not pending) and accounts sets
		if ($accounting && !$item->id_transaction && (count($data->payments) === 1 && $data->payments[0]->state === Payments::AUTHORIZED_STATUS))
		{
			if ($item->amount) {
				if ($data->order->formType !== 'Checkout') { // All cases except Checkout
					self::accountChargeable((int)$item->id_form, $item, Chargeable::TYPE_FROM_FORM[$data->order->formType], (int)$data->payments[0]->id, new \DateTime($data->payments[0]->date));
				}
				else
				{
					if (!$payment = Payments::get((int)$data->payments[0]->id)) {
						throw new \RuntimeException(sprintf('Payment #%d matching checkout item #%d not found.', $data->payments[0]->id, $item->id));
					}
					if (isset($payment->id_credit_account) && $payment->id_credit_account && $payment->id_debit_account) {// This feature will be available once the ChekoutIntent callback is fixed
						$transaction = self::createTransaction($item, [$payment->id_credit_account, $payment->id_debit_account], (int)$data->payments[0]->id, $payment->date);
						$payment->set('id_transaction', $transaction->id);
					}
					elseif (self::accountChargeable((int)$item->id_form, $item, Chargeable::TYPE_FROM_FORM[$data->order->formType], (int)$data->payments[0]->id, new \DateTime($data->payments[0]->date))) {
						$payment->set('id_transaction', $item->id_transaction);
					}
					$payment->save();
				}
			}
			if (isset($data->options)) {
				foreach ($optionEntities as $option) {
					self::accountChargeable((int)$item->id_form, $option, Chargeable::OPTION_TYPE, (int)$data->payments[0]->id, new \DateTime($data->payments[0]->date));
				}
			}
		}
	}

	static protected function handleUserRegistration(\stdClass $data, int $id_form, ChargeableInterface $entity, int $chargeable_type)
	{
		$chargeable = self::getChargeable($id_form, $entity, $chargeable_type);
		if (!$chargeable->register_user) {
			return null;
		}
		if (!$identifier = HA::guessUserIdentifier($data->beneficiary)) {
			throw new SyncException(sprintf(
				'Commande n°%s : Impossible d\'inscrire le membre "%s". Aucun %s à lui associer comme identifiant.' . "\n" . 'Informations reçues de HelloAsso :' . "\n" . '%s' . "\n\n" .
				'Soit il n\'y a pas de champ pour saisir cette information dans le formulaire HelloAsso, soit l\'utilisateur/trice n\'a pas renseigné cette information, soit l\'option "Champ utilisé pour savoir si un membre existe déjà" est mal réglée.',
				$data->order_id,
				$data->beneficiary_label,
				HA::USER_MATCH_TYPES[HA::getInstance()->plugin()->getConfig()->user_match_type],
				json_encode($data->beneficiary, JSON_UNESCAPED_UNICODE)
			));
		}
		if (HA::userAlreadyExists($identifier)) {
			return true;
		}

		$source = [
			'id_parent' => null,
			self::$_nameField => HA::guessUserName($data->beneficiary),
			'date_inscription' => new \DateTime($data->order->date)
		];

		foreach (self::$_userFieldsMap as $user_field => $api_field) {
			if (isset($data->beneficiary->$api_field))
				$source[$user_field] = $data->beneficiary->$api_field;
		}
		$user_match_field = HA::getUserMatchField();

		// The user match field may not exist (aka. not filled during the HelloAsso checkout) when the payer is also the beneficiary
		if (isset($data->beneficiary->{$user_match_field[2]})) {
			$source[$user_match_field[0]] = $data->beneficiary->{$user_match_field[2]};
		}

		$conflict = false;
		if ($user_match_field[0] !== DynamicFields::getLoginField())
		{
			$db = DB::getInstance();
			$login_field = DynamicFields::getLoginField();
			if (array_key_exists($login_field, $source))
			{
				$id_user = EM::getInstance(User::class)->col(sprintf('SELECT id FROM @TABLE WHERE %s = ? COLLATE NOCASE;', $db->quoteIdentifier($login_field)), $source[$login_field]);
				if ($id_user) {
					//$conflict = true;
					$source[$login_field] = sprintf(self::DUPLICATE_MEMBER_PREFIX . $source[$login_field], uniqid());
					//unset($source[$login_field]);
				}
			}
		}

		if (!$conflict)
		{
			$user = Users::create();
			$user->importForm($source);
			$user->set('id_category', (int)HA::getInstance()->plugin()->getConfig()->id_category);
			$user->setNumberIfEmpty();
			$user->save();
			$id_user = (int)$user->id;
		}

		if (!$conflict)
		{
			HA::addUserToCache(HA::guessUserIdentifier($data->beneficiary), $id_user);
			
			$entity->setUserId($id_user);
			$entity->save();

			$order = EM::findOneById(Order::class, (int)$entity->id_order);
			$order->set('id_user', (int)$id_user);
			$order->save();

			return $id_user;
		}
		return null;
	}


	static protected function getChargeable(int $id_form, ChargeableInterface $entity, int $type): Chargeable
	{
		$amount = ($type === Chargeable::ONLY_ONE_ITEM_FORM_TYPE ? null : $entity->getAmount());
		if ($chargeable = Chargeables::get($id_form, $type, $entity->getLabel(), $amount)) {
			return $chargeable;
		}
		return Chargeables::createChargeable($id_form, $entity, $type);
	}

	static protected function accountChargeable(int $id_form, ChargeableInterface $entity, int $type, int $payment_ref, \DateTime $date): bool
	{
		$chargeable = self::getChargeable($id_form, $entity, $type);
		if (null === $chargeable) {
			$chargeable = Chargeables::createChargeable($id_form, $entity, $type);
		}
		elseif ($entity->getAmount() && $chargeable->id_credit_account && $chargeable->id_debit_account) {
			$transaction = self::createTransaction($entity, [(int)$chargeable->id_credit_account, (int)$chargeable->id_debit_account], $payment_ref, $date);
			$entity->id_transaction = (int)$transaction->id;
			$entity->save();
			return true;
		}
		return false;
	}

	static protected function transform(\stdClass $data): \stdClass
	{
		$data->id = (int) $data->id;
		$data->order_id = (int) $data->order->id;
		$data->payer_name = isset($data->payer) ? Payment::getPersonName($data->payer) : null;
		$data->payer_infos = isset($data->payer) ? Payment::formatPersonInfos($data->payer) : null;

		$data->amount = (int) $data->amount;
		$data->form_slug = $data->order->formSlug;
		$data->org_slug = $data->order->organizationSlug;


		$user_data = (!empty($data->user)) ? Payment::formatPersonInfos($data->user) : null;
		$data->fields = $user_data ?? [];


		if (!empty($data->customFields)) {
			foreach ($data->customFields as $field) {
				$data->fields[$field->name] = $field->answer;
			}
		}

		$data->beneficiary = $data->fields ? (object)array_merge($data->fields, (array)$data->user) : $data->payer;
		if (!isset($data->beneficiary->email) && ($data->payer->firstName === $data->beneficiary->firstName) && ($data->payer->lastName === $data->beneficiary->lastName) && !empty($data->payer->email)) {
			$data->beneficiary->email = $data->payer->email;
		}
		$data->beneficiary_label = isset($data->user) ? Payment::getPersonName($data->user) : null;

		return $data;
	}

	static protected function transformOption(\stdClass $data): \stdClass
	{
		$data->fields = [];

		if (!empty($data->user)) {
			$data->fields = Payment::formatPersonInfos($data->user);
		}

		if (!empty($data->customFields)) {
			foreach ($data->customFields as $field) {
				$data->fields[$field->name] = $field->answer;
			}
		}
323
324
325
326
327
328
329










330
331
332
333
334
335
336
				return Forms::getName($id_form);
			}
		}
		else {
			return $data->name;
		}
	}











	static public function reset(): void
	{
		$sql = sprintf('DELETE FROM %s;', Item::TABLE);
		DB::getInstance()->exec($sql);
	}
}







>
>
>
>
>
>
>
>
>
>







515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
				return Forms::getName($id_form);
			}
		}
		else {
			return $data->name;
		}
	}

	static protected function catchSyncException(SyncException $e): void
	{
		self::$_exceptions[] = $e;
	}

	static public function getExceptions(): array
	{
		return self::$_exceptions;
	}

	static public function reset(): void
	{
		$sql = sprintf('DELETE FROM %s;', Item::TABLE);
		DB::getInstance()->exec($sql);
	}
}

Modified helloasso/lib/Options.php from [ffcd8420f5] to [f4c97332a5].

1
2
3
4
5
6

7
8
9
10
11
12
13


14
15
16
17
18
19
20
21
22








23
24
25
26
27
28

29
30
31
32
33
34
35
<?php

namespace Garradin\Plugin\HelloAsso;

use Garradin\Plugin\HelloAsso\Entities\Option;
use Garradin\DynamicList;


class Options
{
	static public function list($for): DynamicList
	{
		$columns = [
			'id' => [],


			'id_transaction' => [
				'label' => 'Écriture'
			],
			'amount' => [
				'label' => 'Montant',
			],
			'label' => [
				'label' => 'Objet',
			],








			'custom_fields' => [
				'label' => 'Champs',
			]
		];

		$tables = Option::TABLE;


		$list = new DynamicList($columns, $tables);

		$conditions = sprintf('id_order = %d', $for->id);
		$list->setConditions($conditions);
		$list->setTitle(sprintf('Commande - %d - Items', $for->id));







>






|
>
>









>
>
>
>
>
>
>
>





|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php

namespace Garradin\Plugin\HelloAsso;

use Garradin\Plugin\HelloAsso\Entities\Option;
use Garradin\DynamicList;
use Garradin\Entities\Users\User;

class Options
{
	static public function list($for): DynamicList
	{
		$columns = [
			'id' => [
				'select' => 'o.id'
			],
			'id_transaction' => [
				'label' => 'Écriture'
			],
			'amount' => [
				'label' => 'Montant',
			],
			'label' => [
				'label' => 'Objet',
			],
			'id_user' => [],
			'user_numero' => [
				'select' => 'u.numero'
			],
			'user_name' => [
				'label' => 'Personne',
				'select' => 'u.nom'
			],
			'custom_fields' => [
				'label' => 'Champs',
			]
		];

		$tables = Option::TABLE . ' o
			LEFT JOIN  ' . User::TABLE . ' u ON (u.id = o.id_user)';

		$list = new DynamicList($columns, $tables);

		$conditions = sprintf('id_order = %d', $for->id);
		$list->setConditions($conditions);
		$list->setTitle(sprintf('Commande - %d - Items', $for->id));

Modified helloasso/lib/Orders.php from [f0e66e128b] to [4f13af925d].

1
2
3
4
5
6
7
8

9
10
11
12

13
14
15
16
17
18
19
<?php

namespace Garradin\Plugin\HelloAsso;

use Garradin\Plugin\HelloAsso\Entities\Form;
use Garradin\Plugin\HelloAsso\Entities\Order;
use Garradin\Plugin\HelloAsso\Entities\Payment;
use Garradin\Plugin\HelloAsso\API;


use Garradin\DB;
use Garradin\DynamicList;
use Garradin\Utils;


use KD2\DB\EntityManager as EM;

class Orders
{
	static public function get(int $id): ?Order
	{








>




>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace Garradin\Plugin\HelloAsso;

use Garradin\Plugin\HelloAsso\Entities\Form;
use Garradin\Plugin\HelloAsso\Entities\Order;
use Garradin\Plugin\HelloAsso\Entities\Payment;
use Garradin\Plugin\HelloAsso\API;
use Garradin\Plugin\HelloAsso\HelloAsso as HA;

use Garradin\DB;
use Garradin\DynamicList;
use Garradin\Utils;
use Garradin\Entities\Users\User;

use KD2\DB\EntityManager as EM;

class Orders
{
	static public function get(int $id): ?Order
	{
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

109
110
111
112

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
		$list->orderBy('date', true);
		return $list;
	}

	static public function sync(string $org_slug): void
	{
		$params = [
			'pageSize'  => HelloAsso::getPageSize(),
		];

		$page_count = 1;

		for ($i = 1; $i <= $page_count; $i++) {
			$params['pageIndex'] = $i;
			$result = API::getInstance()->listOrganizationOrders($org_slug, $params);
			$page_count = $result->pagination->totalPages;

			foreach ($result->data as $order) {
				self::syncOrder($order);
			}

			if (HelloAsso::isTrial()) {
				break;
			}
		}
	}

	static protected function syncOrder(\stdClass $data): void
	{
		$entity = self::get($data->id) ?? new Order;

		$entity->set('raw_data', json_encode($data));

		$data = self::transform($data);

		if (!$entity->exists()) {
			$entity->set('id', $data->id);
			$entity->set('id_form', Forms::getId($data->org_slug, $data->form_slug));
		}


		$entity->set('amount', $data->amount);
		$entity->set('status', $data->status);
		$entity->set('date', $data->date);
		$entity->set('person', $data->payer_name);

		$entity->save();
	}

	static protected function transform(\stdClass $data): \stdClass
	{
		$data->id = (int) $data->id;
		$data->date = new \DateTime($data->date);
		$data->status = Order::getStatus($data);
		$data->payer_name = isset($data->payer) ? Payment::getPayerName($data->payer) : null;
		$data->payer_infos = isset($data->payer) ? Payment::getPayerInfos($data->payer) : null;
		$data->amount = (int) ($data->amount->total ?? 0);
		$data->form_slug = $data->formSlug;
		$data->org_slug = $data->organizationSlug;

		return $data;
	}
	
	static public function reset(): void
	{
		$sql = sprintf('DELETE FROM %s;', Order::TABLE);
		DB::getInstance()->exec($sql);
	}
}







|













|


















>




>








|
|













71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
		$list->orderBy('date', true);
		return $list;
	}

	static public function sync(string $org_slug): void
	{
		$params = [
			'pageSize'  => HA::getPageSize(),
		];

		$page_count = 1;

		for ($i = 1; $i <= $page_count; $i++) {
			$params['pageIndex'] = $i;
			$result = API::getInstance()->listOrganizationOrders($org_slug, $params);
			$page_count = $result->pagination->totalPages;

			foreach ($result->data as $order) {
				self::syncOrder($order);
			}

			if (HA::isTrial()) {
				break;
			}
		}
	}

	static protected function syncOrder(\stdClass $data): void
	{
		$entity = self::get($data->id) ?? new Order;

		$entity->set('raw_data', json_encode($data));

		$data = self::transform($data);

		if (!$entity->exists()) {
			$entity->set('id', $data->id);
			$entity->set('id_form', Forms::getId($data->org_slug, $data->form_slug));
		}

		$entity->set('id_user', HA::getUserId(HA::guessUserIdentifier($data->payer))); // The user may subscribe by himself/herself a long time after his/her order
		$entity->set('amount', $data->amount);
		$entity->set('status', $data->status);
		$entity->set('date', $data->date);
		$entity->set('person', $data->payer_name);

		$entity->save();
	}

	static protected function transform(\stdClass $data): \stdClass
	{
		$data->id = (int) $data->id;
		$data->date = new \DateTime($data->date);
		$data->status = Order::getStatus($data);
		$data->payer_name = isset($data->payer) ? Payment::getPersonName($data->payer) : null;
		$data->payer_infos = isset($data->payer) ? Payment::formatPersonInfos($data->payer) : null;
		$data->amount = (int) ($data->amount->total ?? 0);
		$data->form_slug = $data->formSlug;
		$data->org_slug = $data->organizationSlug;

		return $data;
	}
	
	static public function reset(): void
	{
		$sql = sprintf('DELETE FROM %s;', Order::TABLE);
		DB::getInstance()->exec($sql);
	}
}

Modified helloasso/lib/Payments.php from [320920d0d4] to [b9dbf88c8c].

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
		$formated = clone $data;
		$formated->id = (int) $data->id;
		$formated->id_order = (int) $data->order->id ?: null;
		$formated->date = new \DateTime($data->date);
		$formated->status = HA\Payment::STATES[$data->state] ?? '--';
		$formated->transferred = isset($data->cashOutState) && $data->cashOutState == HA\Payment::CASH_OUT_OK ? true : false;
		$formated->transfer_date = isset($data->cashOutDate) ? new \DateTime($data->cashOutDate) : null;
		$formated->payer_name = isset($data->payer) ? HA\Payment::getPayerName($data->payer) : null;
		$formated->payer_infos = isset($data->payer) ? HA\Payment::getPayerInfos($data->payer) : null;
		$formated->form_slug = $data->order->formSlug;
		$formated->org_slug = $data->order->organizationSlug;

		return $formated;
	}
	
	static public function reset(): void







|
|







207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
		$formated = clone $data;
		$formated->id = (int) $data->id;
		$formated->id_order = (int) $data->order->id ?: null;
		$formated->date = new \DateTime($data->date);
		$formated->status = HA\Payment::STATES[$data->state] ?? '--';
		$formated->transferred = isset($data->cashOutState) && $data->cashOutState == HA\Payment::CASH_OUT_OK ? true : false;
		$formated->transfer_date = isset($data->cashOutDate) ? new \DateTime($data->cashOutDate) : null;
		$formated->payer_name = isset($data->payer) ? HA\Payment::getPersonName($data->payer) : null;
		$formated->payer_infos = isset($data->payer) ? HA\Payment::formatPersonInfos($data->payer) : null;
		$formated->form_slug = $data->order->formSlug;
		$formated->org_slug = $data->order->organizationSlug;

		return $formated;
	}
	
	static public function reset(): void

Added helloasso/lib/SyncException.php version [aa6a0e0453].

















>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
<?php

namespace Garradin\Plugin\HelloAsso;

class SyncException extends \RuntimeException
{

}

Modified helloasso/schema.sql from [f16757826b] to [36fd2eb4f8].

42
43
44
45
46
47
48

49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67

68
69
70
71
72
73
74
);

CREATE TABLE IF NOT EXISTS plugin_helloasso_item_options (
	id INTEGER PRIMARY KEY NOT NULL,
	id_item INTEGER NOT NULL REFERENCES plugin_helloasso_items(id) ON DELETE CASCADE,
	-- Redundant but needed by DynamicList since it does not handle JOIN statement
	id_order INTEGER NOT NULL REFERENCES plugin_helloasso_items(id) ON DELETE CASCADE,

	id_transaction INTEGER NULL REFERENCES acc_transactions(id) ON DELETE SET NULL,
	label TEXT NOT NULL,
	amount INTEGER NOT NULL,
	raw_data TEXT NOT NULL,
	custom_fields TEXT NULL
);

CREATE TABLE IF NOT EXISTS plugin_helloasso_chargeables (
	id INTEGER PRIMARY KEY NOT NULL,
	id_form INTEGER NOT NULL REFERENCES plugin_helloasso_forms(id) ON DELETE CASCADE,
	id_item INTEGER NULL REFERENCES plugin_helloasso_items(id) ON DELETE CASCADE,
	id_credit_account INTEGER NULL REFERENCES acc_accounts (id) ON DELETE SET NULL,
	id_debit_account INTEGER NULL REFERENCES acc_accounts (id) ON DELETE SET NULL,
	type INTEGER NOT NULL,
	label TEXT NOT NULL,
	amount INTEGER NULL

);

CREATE UNIQUE INDEX IF NOT EXISTS plugin_helloasso_chargeables_key ON plugin_helloasso_chargeables(id_form, id_item, type, label, amount);


/*
CREATE TABLE IF NOT EXISTS plugin_helloasso_options (
	id INTEGER PRIMARY KEY NOT NULL,
	id_order INTEGER NOT NULL REFERENCES plugin_helloasso_orders(id) ON DELETE CASCADE,
	hash TEXT NOT NULL,
	label TEXT NOT NULL,







>










|




|
>



>







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
);

CREATE TABLE IF NOT EXISTS plugin_helloasso_item_options (
	id INTEGER PRIMARY KEY NOT NULL,
	id_item INTEGER NOT NULL REFERENCES plugin_helloasso_items(id) ON DELETE CASCADE,
	-- Redundant but needed by DynamicList since it does not handle JOIN statement
	id_order INTEGER NOT NULL REFERENCES plugin_helloasso_items(id) ON DELETE CASCADE,
	id_user INTEGER NULL REFERENCES users(id) ON DELETE SET NULL,
	id_transaction INTEGER NULL REFERENCES acc_transactions(id) ON DELETE SET NULL,
	label TEXT NOT NULL,
	amount INTEGER NOT NULL,
	raw_data TEXT NOT NULL,
	custom_fields TEXT NULL
);

CREATE TABLE IF NOT EXISTS plugin_helloasso_chargeables (
	id INTEGER PRIMARY KEY NOT NULL,
	id_form INTEGER NOT NULL REFERENCES plugin_helloasso_forms(id) ON DELETE CASCADE,
	id_item INTEGER NULL REFERENCES plugin_helloasso_items(id) ON DELETE SET NULL,
	id_credit_account INTEGER NULL REFERENCES acc_accounts (id) ON DELETE SET NULL,
	id_debit_account INTEGER NULL REFERENCES acc_accounts (id) ON DELETE SET NULL,
	type INTEGER NOT NULL,
	label TEXT NOT NULL,
	amount INTEGER NULL,
	register_user UNSIGNED INTEGER NOT NULL
);

CREATE UNIQUE INDEX IF NOT EXISTS plugin_helloasso_chargeables_key ON plugin_helloasso_chargeables(id_form, id_item, type, label, amount);
CREATE INDEX IF NOT EXISTS plugin_helloasso_chargeables_get ON plugin_helloasso_chargeables(id_form, label, amount);

/*
CREATE TABLE IF NOT EXISTS plugin_helloasso_options (
	id INTEGER PRIMARY KEY NOT NULL,
	id_order INTEGER NOT NULL REFERENCES plugin_helloasso_orders(id) ON DELETE CASCADE,
	hash TEXT NOT NULL,
	label TEXT NOT NULL,

Modified helloasso/templates/_chargeables_list.tpl from [5360b872d4] to [b13377d9e6].

1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19

{include file="common/dynamic_list_head.tpl"}

{assign var='chargeable_need_config' value=false}

	{foreach from=$list->iterate() item="row"}

		<tr {if $plugin.config.accounting && (!$row.id_credit_account || !$row.id_debit_account)}class="awaits_account_configuration"{/if}>
			<td class="num"><a href="{"chargeable.php?id=%s"|args:$row.id}">{$row.id}</a></td>
			<td>{$row.type_label}</td>
			<td>{$row.label}</td>
			<td class="money">{$row.amount|money_currency|raw}</td>

			<td class="num"><a href="{$admin_url}acc/accounts/journal.php?id={$row.id_credit_account|intval}">{$row.credit_account}</a></td>
			<td class="num"><a href="{$admin_url}acc/accounts/journal.php?id={$row.id_debit_account|intval}">{$row.debit_account}</a></td>
			
			{* Not yet supported
			{if property_exists($row, 'custom_fields')}
			<td>
				{if $row.custom_fields}












>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

{include file="common/dynamic_list_head.tpl"}

{assign var='chargeable_need_config' value=false}

	{foreach from=$list->iterate() item="row"}

		<tr {if $plugin.config.accounting && (!$row.id_credit_account || !$row.id_debit_account)}class="awaits_account_configuration"{/if}>
			<td class="num"><a href="{"chargeable.php?id=%s"|args:$row.id}">{$row.id}</a></td>
			<td>{$row.type_label}</td>
			<td>{$row.label}</td>
			<td class="money">{$row.amount|money_currency|raw}</td>
			<td>{$row.register_user}</td>
			<td class="num"><a href="{$admin_url}acc/accounts/journal.php?id={$row.id_credit_account|intval}">{$row.credit_account}</a></td>
			<td class="num"><a href="{$admin_url}acc/accounts/journal.php?id={$row.id_debit_account|intval}">{$row.debit_account}</a></td>
			
			{* Not yet supported
			{if property_exists($row, 'custom_fields')}
			<td>
				{if $row.custom_fields}

Modified helloasso/templates/_items_list.tpl from [c8cdff8228] to [47e5f8f5bd].

1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19

{include file="common/dynamic_list_head.tpl"}

	{foreach from=$list->iterate() item="row"}

		<tr>
			<td class="num"><a href="order.php?id={$row.id_order}">{$row.id}</a></td>
			<td class="num"><a href="{$admin_url}acc/transactions/details.php?id={$row.id_transaction}">{$row.id_transaction}</a></td>
			<td class="money">{$row.amount|money_currency|raw}</td>
			<td>{$row.type}</td>
			<td>{$row.label}</td>
			<td>{$row.person}</td>

			<td>{$row.options|escape|nl2br}</td>
			{if property_exists($row, 'custom_fields')}
			<td>
				{if $row.custom_fields}
				<table>
					{foreach from=$row.custom_fields item="value" key="name"}
					<tr>






|




|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

{include file="common/dynamic_list_head.tpl"}

	{foreach from=$list->iterate() item="row"}

		<tr>
			<td class="num"><a href="chargeable.php?id={$row.id_chargeable}">{$row.id_chargeable}</a></td>
			<td class="num"><a href="{$admin_url}acc/transactions/details.php?id={$row.id_transaction}">{$row.id_transaction}</a></td>
			<td class="money">{$row.amount|money_currency|raw}</td>
			<td>{$row.type}</td>
			<td>{$row.label}</td>
			<td class="num">{$row.person}</td>
			<td class="num">{if $row.id_user}{$row.user_name} <a href="{$admin_url}users/details.php?id={$row.id_user}">{$row.numero}</a>{/if}</td>
			<td>{$row.options|escape|nl2br}</td>
			{if property_exists($row, 'custom_fields')}
			<td>
				{if $row.custom_fields}
				<table>
					{foreach from=$row.custom_fields item="value" key="name"}
					<tr>
34
35
36
37
38
39
40







	{/foreach}

	</tbody>
</table>











>
>
>
>
35
36
37
38
39
40
41
42
43
44
45



	{/foreach}

	</tbody>
</table>

<p class="help block">
	* Est différent de la "personne" si il y a un doute que le/la payeur/euse soit la même personne (ex: courriel différent).
</p>

Modified helloasso/templates/_options_list.tpl from [a3c0446f66] to [757513acdf].

1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16

{include file="common/dynamic_list_head.tpl"}

	{foreach from=$list->iterate() item="row"}

		<tr>
			<td class="num"><a href="{$admin_url}acc/transactions/details.php?id={$row.id_transaction}">{$row.id_transaction}</a></td>
			<td class="money">{$row.amount|money_currency|raw}</td>
			<td>{$row.label}</td>

			<td>{$row.options|escape|nl2br}</td>
			{if property_exists($row, 'custom_fields')}
			<td>
				{if $row.custom_fields}
				<table>
					{foreach from=$row.custom_fields item="value" key="name"}
					<tr>









>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

{include file="common/dynamic_list_head.tpl"}

	{foreach from=$list->iterate() item="row"}

		<tr>
			<td class="num"><a href="{$admin_url}acc/transactions/details.php?id={$row.id_transaction}">{$row.id_transaction}</a></td>
			<td class="money">{$row.amount|money_currency|raw}</td>
			<td>{$row.label}</td>
			<td class="num">{if $row.id_user}{$row.user_name} <a href="{$admin_url}users/details.php?id={$row.id_user}">{$row.user_numero}</a>{/if}</td>
			<td>{$row.options|escape|nl2br}</td>
			{if property_exists($row, 'custom_fields')}
			<td>
				{if $row.custom_fields}
				<table>
					{foreach from=$row.custom_fields item="value" key="name"}
					<tr>

Modified helloasso/templates/_payments_list.tpl from [d99bdac14c] to [20ff8a2294].

1
2
3
4
5
6

7
8
9

10
11
12
13
14
15
16
{include file="common/dynamic_list_head.tpl"}

	{foreach from=$list->iterate() item="row"}
		<tr>
			<td class="num"><a href="payment.php?id={$row.id}">{$row.reference}</a></td>
			<td class="num">

				{foreach from=$row.transactions item="id_transaction"}
					{link href="!acc/transactions/details.php?id=%d"|args:$id_transaction label="#%d"|args:$id_transaction}
				{/foreach}

			</td>
			<td>{$row.label}</td>
			<td>{$row.date|date}</td>
			<td class="money">{$row.amount|money_currency|raw}</td>
			<td>
				{if $row.id_author && $row.author}
					<a href="{$admin_url}users/details.php?id={$row.author.id|intval}">{$row.author.nom}</a>






>
|
|
|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{include file="common/dynamic_list_head.tpl"}

	{foreach from=$list->iterate() item="row"}
		<tr>
			<td class="num"><a href="payment.php?id={$row.id}">{$row.reference}</a></td>
			<td class="num">
				{if $row.transactions}
					{foreach from=$row.transactions item="id_transaction"}
						{link href="!acc/transactions/details.php?id=%d"|args:$id_transaction label="#%d"|args:$id_transaction}
					{/foreach}
				{/if}
			</td>
			<td>{$row.label}</td>
			<td>{$row.date|date}</td>
			<td class="money">{$row.amount|money_currency|raw}</td>
			<td>
				{if $row.id_author && $row.author}
					<a href="{$admin_url}users/details.php?id={$row.author.id|intval}">{$row.author.nom}</a>

Modified helloasso/templates/chargeable.tpl from [17797400d2] to [2ce08d7dec].

35
36
37
38
39
40
41
42
43







44
45
46
47
48
49
50
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
			{else}
				En fonctionnement.
			{/if}
		</dd>
	{/if}
</dl>

{if $plugin->config->accounting}
	<form method="post" action="{$self_url}">







		<fieldset>
			<legend>Comptabilité</legend>
			<dl>
				{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'6':$chart_id name="credit" label="Type de recette" required=true default=$credit_account}
				{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'1:2:3':$chart_id name="debit" label="Compte d'encaissement" required=true default=$debit_account}
			</dl>
			<p class="help block">Cette modification impacte uniquement les <em>futures</em> synchronisations. Elle n'est pas rétro-active.</p>
		</fieldset>

		<p class="submit">
			{csrf_field key=$csrf_key}
			{button type="submit" class="main" name="save" label="Enregistrer" shape="right"}
		</p>
	</form>
{/if}

{if $TECH_DETAILS}
	<dl style="background-color: black; color: limegreen; padding-top: 0.8em;" class="describe">
		<dt style="color: limegreen;">item</dt>
		<dd><pre>{if $chargeable->id_item}{$parent_item|var_dump}{else}NULL{/if}</pre></dd>
		<dt style="color: limegreen;">item->raw_data</dt>
		<dd><pre>{if $chargeable->id_item}{$parent_item->raw_data|json_revamp}{else}NULL{/if}</pre></dd>
	</dl>
{/if}

{include file="_foot.tpl"}







<
|
>
>
>
>
>
>
>








>
|
|
|
|
|
<











35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72
73
74
			{else}
				En fonctionnement.
			{/if}
		</dd>
	{/if}
</dl>


<form method="post" action="{$self_url}">
	<fieldset>
		<legend>Options</legend>
		<dl>
			{input type="checkbox" name="register_user" value="1" label="Inscrire comme membre" source=$chargeable help="Inscrira automatiquement la personne comme membre Paheko si cet article est commandé."}
		</dl>
	</fieldset>
	{if $plugin->config->accounting}
		<fieldset>
			<legend>Comptabilité</legend>
			<dl>
				{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'6':$chart_id name="credit" label="Type de recette" required=true default=$credit_account}
				{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'1:2:3':$chart_id name="debit" label="Compte d'encaissement" required=true default=$debit_account}
			</dl>
			<p class="help block">Cette modification impacte uniquement les <em>futures</em> synchronisations. Elle n'est pas rétro-active.</p>
		</fieldset>
	{/if}
	<p class="submit">
		{csrf_field key=$csrf_key}
		{button type="submit" class="main" name="save" label="Enregistrer" shape="right"}
	</p>
</form>


{if $TECH_DETAILS}
	<dl style="background-color: black; color: limegreen; padding-top: 0.8em;" class="describe">
		<dt style="color: limegreen;">item</dt>
		<dd><pre>{if $chargeable->id_item}{$parent_item|var_dump}{else}NULL{/if}</pre></dd>
		<dt style="color: limegreen;">item->raw_data</dt>
		<dd><pre>{if $chargeable->id_item}{$parent_item->raw_data|json_revamp}{else}NULL{/if}</pre></dd>
	</dl>
{/if}

{include file="_foot.tpl"}

Modified helloasso/templates/config.tpl from [f6abb1cd1b] to [2bd85c7123].

11
12
13
14
15
16
17
18





19
20
21
22
23
24
25
26
27
28

{form_errors}

<form method="post" action="{$self_url}">
	<fieldset>
		<legend>Correspondance des membres</legend>
		<dl>
			{input type="select" options=$match_options name="match_email_field" source=$plugin_config required=true label="Champ utilisé pour savoir si un membre existe déjà"}





		</dl>
	</fieldset>

	<p class="submit">
		{csrf_field key=$csrf_key}
		{button type="submit" class="main" name="save" label="Enregistrer" shape="right"}
	</p>
</form>

{include file="_foot.tpl"}







|
>
>
>
>
>










11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

{form_errors}

<form method="post" action="{$self_url}">
	<fieldset>
		<legend>Correspondance des membres</legend>
		<dl>
			{input type="select" options=$match_options name="match_email_field" source=$plugin.config required=true label="Champ utilisé pour savoir si un membre existe déjà"}
		</dl>
		<dl>
			{foreach from=$payer_fields key='field' item='label'}
				{input type="select" name="payer_map[%s]"|args:$field label=$label options=$dynamic_fields required=true}
			{/foreach}
		</dl>
	</fieldset>

	<p class="submit">
		{csrf_field key=$csrf_key}
		{button type="submit" class="main" name="save" label="Enregistrer" shape="right"}
	</p>
</form>

{include file="_foot.tpl"}

Modified helloasso/templates/config_client.tpl from [9ae9aa18e9] to [5f03186899].

56
57
58
59
60
61
62

























63
64
65
66
67
68












69
		<p class="help block">Cette option n'est pas définitive et pourra être changée plus tard.</p>
		<dl>
			{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'6':$chart_id name="default_credit" label="Type de recette par défaut" required=false default=$default_credit_account help="Sera proposé par défaut pour vous faire gagner du temps lors de vos saisies."}
			{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'1:2:3':$chart_id name="default_debit" label="Compte d'encaissement par défaut" required=false default=$default_debit_account}
		</dl>
	</fieldset>


























	<p class="submit">
		{csrf_field key=$csrf_key}
		{button type="submit" class="main" name="save" label="Enregistrer" shape="right"}
	</p>
</form>













{include file="_foot.tpl"}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






>
>
>
>
>
>
>
>
>
>
>
>

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
		<p class="help block">Cette option n'est pas définitive et pourra être changée plus tard.</p>
		<dl>
			{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'6':$chart_id name="default_credit" label="Type de recette par défaut" required=false default=$default_credit_account help="Sera proposé par défaut pour vous faire gagner du temps lors de vos saisies."}
			{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'1:2:3':$chart_id name="default_debit" label="Compte d'encaissement par défaut" required=false default=$default_debit_account}
		</dl>
	</fieldset>

	<fieldset>
		<legend>Correspondance des membres</legend>
		<dl>
			{input type="select" options=$user_match_fields name="user_match_type" source=$plugin.config required=true label="Champ utilisé pour savoir si un membre existe déjà"}
			<span class="user_match_field">
				{input type="text" name="user_match_field" label='Intitulé du champ HelloAsso correspondant à "%s"'|args:$user_match_fields[1] source=$plugin.config help='Dans la section "Informations relatives à vos tarifs" de la configuration de votre formulaire HelloAsso.'}
			</span>
			{input type="select" name="id_category" label="Catégorie" options=$category_options required=true default=$plugin.config.id_category help="Catégorie dans laquelle les membres automatiquement inscrit·e·s seront ajouté·e·s."}
			{input type="select" name="payer_map[name]" label="Ordre de fusion des champs nom et prénom" options=$merge_names_options required=true default=$plugin.config.payer_map.name}
		</dl>
	</fieldset>
	<fieldset>
		<legend>Correspondance des payeur/euse.s</legend>
		<p class="help block">
			HelloAsso fournit les informations suivantes sur le/la payeur/euse. Choisissez comment vous souhaitez les lier à Paheko.
		</p>
		<dl>
			{input type="select" name="payer_map[email]" label="Courriel" options=$email_fields required=true default=$plugin.config.payer_map.email}
			{foreach from=$payer_fields key='field' item='label'}
				{assign var='source' value=$plugin.config.payer_map}
				{input type="select" name="payer_map[%s]"|args:$field label=$label options=$dynamic_fields required=true default=$source->$field}
			{/foreach}
		</dl>
	</fieldset>

	<p class="submit">
		{csrf_field key=$csrf_key}
		{button type="submit" class="main" name="save" label="Enregistrer" shape="right"}
	</p>
</form>

<script type="text/javascript">
{literal}
(function () {
	g.toggle('.user_match_field', $('#f_user_match_type').value === '1');

	$('#f_user_match_type').onchange = () => {
		g.toggle('.user_match_field', $('#f_user_match_type').value === '1');
	};
})();
{/literal}
</script>

{include file="_foot.tpl"}

Modified helloasso/templates/order.tpl from [8e14549a34] to [406a75ad3e].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{include file="_head.tpl" title="Commande n°%s — %s"|args:$order.id,$order.person}

{include file="./_menu.tpl" current="home"}

<h2 class="ruler">Informations de la commande</h2>

<dl class="describe">
	<dt>Personne</dt>
	<dd>{$order.person}</dd>
	<dt>Référence</dt>
	<dd>{$order.id}</dd>
	<dt>Montant total</dt>
	<dd>{$order.amount|money_currency|raw}</dd>
	<dt>Date</dt>
	<dd>{$order.date|date}</dd>
	<dt>Statut</dt>








|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{include file="_head.tpl" title="Commande n°%s — %s"|args:$order.id,$order.person}

{include file="./_menu.tpl" current="home"}

<h2 class="ruler">Informations de la commande</h2>

<dl class="describe">
	<dt>Personne</dt>
	<dd class="num">{if $order.id_user}{$user->nom} <a href="{$admin_url}users/details.php?id={$user->id|intval}">{$user->numero}</a>{else}{$order.person}{/if}</dd>
	<dt>Référence</dt>
	<dd>{$order.id}</dd>
	<dt>Montant total</dt>
	<dd>{$order.amount|money_currency|raw}</dd>
	<dt>Date</dt>
	<dd>{$order.date|date}</dd>
	<dt>Statut</dt>

Modified helloasso/templates/sync.tpl from [6e9a2cf774] to [f25a627dd7].

1
2
3
4
5
6
7
8
9









10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
{include file="_head.tpl" title="HelloAsso"}

{include file="%s/templates/_menu.tpl"|args:$plugin_root current="sync"}

{if $_GET.ok}
	{if $_GET.ok == 1 && (!$plugin->config->accounting || ($plugin->config->accounting && !$chargeables))}
		<p class="confirm block">Synchronisation effectuée avec succès.</p>
	{/if}
{/if}










{if $plugin->config->accounting}
	{if $chargeables}
	<form method="POST" action="{$self_url}">
		<p class="alert block">
			{if !$default_debit_account && !$default_credit_account}
				Pour pouvoir synchroniser la comptabilité, merci de renseigner les types de recette et comptes d'encaissement pour articles suivants :
			{else}
				Pour pouvoir synchroniser la comptabilité, merci de confirmer les types de recette et comptes d'encaissement pré-remplis pour articles suivants :
			{/if}
		</p>
		{foreach from=$chargeables key='form_name' item='form'}
			<fieldset>
				<legend>{if $form_name === 'Checkout'}Paiements isolés{else}{$form_name}{/if}</legend>
				{foreach from=$form item='chargeable'}
					{if $chargeable.type !== Plugin\HelloAsso\Entities\Chargeable::ONLY_ONE_ITEM_FORM_TYPE}
						<fieldset>
							<legend>
					{/if}
							{if $chargeable.type === Plugin\HelloAsso\Entities\Chargeable::CHECKOUT_TYPE}
								{$chargeable->label}
							{elseif $chargeable.type !== Plugin\HelloAsso\Entities\Chargeable::ONLY_ONE_ITEM_FORM_TYPE}
								{if $chargeable.type === Plugin\HelloAsso\Entities\Chargeable::OPTION_TYPE}"{$chargeable->getItem_name()}" option {/if}"{$chargeable.label}" {$chargeable.amount|escape|money_currency}
							{/if}
						</legend>
						<dl>
							{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'6':$chart_id name="chargeable_credit[%d]"|args:$chargeable.id label="Type de recette" required=1 default=$default_credit_account}
							{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'1:2:3':$chart_id name="chargeable_debit[%d]"|args:$chargeable.id label="Compte d'encaissement" required=1 default=$default_debit_account}

						</dl>
					{if $chargeable.type !== Plugin\HelloAsso\Entities\Chargeable::ONLY_ONE_ITEM_FORM_TYPE}
						</fieldset>
					{/if}
				{/foreach}
			</fieldset>
		{/foreach}









>
>
>
>
>
>
>
>
>



|


|

|



















>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
{include file="_head.tpl" title="HelloAsso"}

{include file="%s/templates/_menu.tpl"|args:$plugin_root current="sync"}

{if $_GET.ok}
	{if $_GET.ok == 1 && (!$plugin->config->accounting || ($plugin->config->accounting && !$chargeables))}
		<p class="confirm block">Synchronisation effectuée avec succès.</p>
	{/if}
{/if}

{if isset($exceptions)}
	<p class="alert block">
		Les erreurs suivantes sont survenues durant la synchronisation.<br />Les autres entrées ont été synchronisées normalement.
	</p>
	{foreach from=$exceptions item='e'}
		<p class="error block">{$e->getMessage()|escape|nl2br}</p>
	{/foreach}
{/if}

{if $plugin->config->accounting}
	{if $chargeables}
	<form method="POST" action="{$self_url_no_qs}">
		<p class="alert block">
			{if !$default_debit_account && !$default_credit_account}
				Pour pouvoir synchroniser la comptabilité, merci de renseigner les types de recette et comptes d'encaissement pour les articles suivants :
			{else}
				Pour pouvoir synchroniser la comptabilité, merci de confirmer les types de recette et comptes d'encaissement pré-remplis pour les articles suivants :
			{/if}
		</p>
		{foreach from=$chargeables key='form_name' item='form'}
			<fieldset>
				<legend>{if $form_name === 'Checkout'}Paiements isolés{else}{$form_name}{/if}</legend>
				{foreach from=$form item='chargeable'}
					{if $chargeable.type !== Plugin\HelloAsso\Entities\Chargeable::ONLY_ONE_ITEM_FORM_TYPE}
						<fieldset>
							<legend>
					{/if}
							{if $chargeable.type === Plugin\HelloAsso\Entities\Chargeable::CHECKOUT_TYPE}
								{$chargeable->label}
							{elseif $chargeable.type !== Plugin\HelloAsso\Entities\Chargeable::ONLY_ONE_ITEM_FORM_TYPE}
								{if $chargeable.type === Plugin\HelloAsso\Entities\Chargeable::OPTION_TYPE}"{$chargeable->getItem_name()}" option {/if}"{$chargeable.label}" {$chargeable.amount|escape|money_currency}
							{/if}
						</legend>
						<dl>
							{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'6':$chart_id name="chargeable_credit[%d]"|args:$chargeable.id label="Type de recette" required=1 default=$default_credit_account}
							{input type="list" target="!acc/charts/accounts/selector.php?targets=%s&chart=%d"|args:'1:2:3':$chart_id name="chargeable_debit[%d]"|args:$chargeable.id label="Compte d'encaissement" required=1 default=$default_debit_account}
							{input type="checkbox" name="register_user[%d]"|args:$chargeable.id value="1" label="Inscrire comme membre" source=$chargeable help="Inscrira automatiquement la personne comme membre Paheko si cet article est commandé."}
						</dl>
					{if $chargeable.type !== Plugin\HelloAsso\Entities\Chargeable::ONLY_ONE_ITEM_FORM_TYPE}
						</fieldset>
					{/if}
				{/foreach}
			</fieldset>
		{/foreach}