Overview
Comment:Implement list of services per user
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: 18fdb5646861c1b3a77747dfbb9d88c1065be8f4
User & Date: bohwaz on 2020-11-08 00:50:47
Other Links: branch diff | manifest | tags
Context
2020-11-08
01:41
Implement partial service fee payments check-in: 80bb461020 user: bohwaz tags: dev
00:50
Implement list of services per user check-in: 18fdb56468 user: bohwaz tags: dev
2020-11-07
20:19
Fix user details page, display number of services check-in: e9afce499d user: bohwaz tags: dev
Changes

Modified src/include/data/1.0.0_migration.sql from [3f12a3dd9d] to [dd9fed4e04].

120
121
122
123
124
125
126

127
128
129
130
131
132
133
	SELECT id, intitule, CAST(montant*100 AS integer), id,
		(SELECT id FROM acc_accounts WHERE code = (SELECT compte FROM compta_categories WHERE id = id_categorie_compta))
	FROM cotisations WHERE montant > 0 OR id_categorie_compta IS NOT NULL;

INSERT INTO services_users SELECT cm.id, cm.id_membre, cm.id_cotisation,
	cm.id_cotisation,
	1,

	cm.date,
	CASE
		WHEN c.duree IS NOT NULL THEN date(cm.date, '+'||c.duree||' days')
		WHEN c.fin IS NOT NULL THEN c.fin
		ELSE NULL
	END
	FROM cotisations_membres cm







>







120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
	SELECT id, intitule, CAST(montant*100 AS integer), id,
		(SELECT id FROM acc_accounts WHERE code = (SELECT compte FROM compta_categories WHERE id = id_categorie_compta))
	FROM cotisations WHERE montant > 0 OR id_categorie_compta IS NOT NULL;

INSERT INTO services_users SELECT cm.id, cm.id_membre, cm.id_cotisation,
	cm.id_cotisation,
	1,
	NULL,
	cm.date,
	CASE
		WHEN c.duree IS NOT NULL THEN date(cm.date, '+'||c.duree||' days')
		WHEN c.fin IS NOT NULL THEN c.fin
		ELSE NULL
	END
	FROM cotisations_membres cm

Modified src/include/data/1.0.0_schema.sql from [d082661890] to [8324ddb304].

67
68
69
70
71
72
73

74
75
76
77
78
79
80
(
    id INTEGER NOT NULL PRIMARY KEY,
    id_user INTEGER NOT NULL REFERENCES membres (id) ON DELETE CASCADE,
    id_service INTEGER NOT NULL REFERENCES services (id) ON DELETE CASCADE,
    id_fee INTEGER NULL REFERENCES services_fees (id) ON DELETE CASCADE,

    paid INTEGER NOT NULL DEFAULT 0,


    date TEXT NOT NULL DEFAULT CURRENT_DATE CHECK (date(date) IS NOT NULL AND date(date) = date),
    expiry_date TEXT NULL CHECK (date(expiry_date) IS NULL OR date(expiry_date) = expiry_date)
);

CREATE UNIQUE INDEX IF NOT EXISTS su_unique ON services_users (id_user, id_service, date);








>







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
(
    id INTEGER NOT NULL PRIMARY KEY,
    id_user INTEGER NOT NULL REFERENCES membres (id) ON DELETE CASCADE,
    id_service INTEGER NOT NULL REFERENCES services (id) ON DELETE CASCADE,
    id_fee INTEGER NULL REFERENCES services_fees (id) ON DELETE CASCADE,

    paid INTEGER NOT NULL DEFAULT 0,
    expected_amount INTEGER NULL,

    date TEXT NOT NULL DEFAULT CURRENT_DATE CHECK (date(date) IS NOT NULL AND date(date) = date),
    expiry_date TEXT NULL CHECK (date(expiry_date) IS NULL OR date(expiry_date) = expiry_date)
);

CREATE UNIQUE INDEX IF NOT EXISTS su_unique ON services_users (id_user, id_service, date);

Modified src/include/lib/Garradin/Entities/Accounting/Transaction.php from [33a9541a79] to [db20f366ec].

536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
...
566
567
568
569
570
571
572
















573
574
575
576
577
578
579
...
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
	}

	static public function getTypesDetails()
	{
		$details = [
			self::TYPE_REVENUE => [
				'accounts' => [
						[
						'label' => 'De',
						'targets' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING],
						'position' => 'debit',
					],
					[
						'label' => 'Vers',
						'targets' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING],
						'position' => 'credit',
					],
				],
				'label' => self::TYPES_NAMES[self::TYPE_REVENUE],
				'help' => null,
			],
			self::TYPE_EXPENSE => [
				'accounts' => [
					[
						'label' => 'Type de dépense',
						'targets' => [Account::TYPE_EXPENSE],
						'position' => 'debit',
................................................................................
						'targets' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING],
						'position' => 'credit',
					],
				],
				'label' => self::TYPES_NAMES[self::TYPE_EXPENSE],
				'help' => null,
			],
















			self::TYPE_DEBT => [
				'accounts' => [
					[
						'label' => 'Compte de tiers',
						'targets' => [Account::TYPE_THIRD_PARTY],
						'position' => 'credit',
					],
................................................................................
						'position' => 'credit',
					],
				],
				'label' => self::TYPES_NAMES[self::TYPE_CREDIT],
				'help' => 'Quand un membre ou un fournisseur doit de l\'argent à l\'association',
			],
			self::TYPE_ADVANCED => [
				'accounts' => [
					[
						'label' => 'Type de recette',
						'targets' => [Account::TYPE_REVENUE],
						'position' => 'credit',
					],
					[
						'label' => 'Compte d\'encaissement',
						'targets' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING],
						'position' => 'debit',
					],
				],
				'label' => self::TYPES_NAMES[self::TYPE_ADVANCED],
				'help' => 'Choisir les comptes du plan comptable, ventiler une écriture sur plusieurs comptes, etc.',
			],
		];

		foreach ($details as $key => &$type) {
			$type = (object) $type;







|
|
|
|


|

|



<







 







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







 







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







536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554

555
556
557
558
559
560
561
...
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
...
614
615
616
617
618
619
620
621











622
623
624
625
626
627
628
	}

	static public function getTypesDetails()
	{
		$details = [
			self::TYPE_REVENUE => [
				'accounts' => [
					[
						'label' => 'Type de recette',
						'targets' => [Account::TYPE_REVENUE],
						'position' => 'credit',
					],
					[
						'label' => 'Compte d\'encaissement',
						'targets' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING],
						'position' => 'debit',
					],
				],
				'label' => self::TYPES_NAMES[self::TYPE_REVENUE],

			],
			self::TYPE_EXPENSE => [
				'accounts' => [
					[
						'label' => 'Type de dépense',
						'targets' => [Account::TYPE_EXPENSE],
						'position' => 'debit',
................................................................................
						'targets' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING],
						'position' => 'credit',
					],
				],
				'label' => self::TYPES_NAMES[self::TYPE_EXPENSE],
				'help' => null,
			],
			self::TYPE_TRANSFER => [
				'accounts' => [
						[
						'label' => 'De',
						'targets' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING],
						'position' => 'debit',
					],
					[
						'label' => 'Vers',
						'targets' => [Account::TYPE_BANK, Account::TYPE_CASH, Account::TYPE_OUTSTANDING],
						'position' => 'credit',
					],
				],
				'label' => self::TYPES_NAMES[self::TYPE_TRANSFER],
				'help' => 'Dépôt en banque, virement interne, etc.',
			],
			self::TYPE_DEBT => [
				'accounts' => [
					[
						'label' => 'Compte de tiers',
						'targets' => [Account::TYPE_THIRD_PARTY],
						'position' => 'credit',
					],
................................................................................
						'position' => 'credit',
					],
				],
				'label' => self::TYPES_NAMES[self::TYPE_CREDIT],
				'help' => 'Quand un membre ou un fournisseur doit de l\'argent à l\'association',
			],
			self::TYPE_ADVANCED => [
				'accounts' => [],











				'label' => self::TYPES_NAMES[self::TYPE_ADVANCED],
				'help' => 'Choisir les comptes du plan comptable, ventiler une écriture sur plusieurs comptes, etc.',
			],
		];

		foreach ($details as $key => &$type) {
			$type = (object) $type;

Modified src/include/lib/Garradin/Entities/Services/Service_User.php from [01f53898cb] to [9f8d55fe38].

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
..
93
94
95
96
97
98
99





100
101
102
103
104
105
106
	const TABLE = 'services_users';

	protected $id;
	protected $id_user;
	protected $id_service;
	protected $id_fee;
	protected $paid;

	protected $date;
	protected $expiry_date;

	protected $_types = [
		'id'          => 'int',
		'id_user'     => 'int',
		'id_fee'      => 'int',
		'id_service'  => 'int',
		'paid'        => 'bool',

		'date'        => 'date',
		'expiry_date' => '?date',
	];

	protected $_service, $_fee;

	public function selfCheck(): void
	{

		$this->assert(!DB::getInstance()->test(self::TABLE, 'id_user = ? AND id_service = ? AND date = ?', $this->id_user, $this->id_service, $this->date->format('Y-m-d')), 'Cette activité a déjà été enregistrée pour ce membre et ce jour');
	}

	public function importForm(?array $source = null)
	{
		if (null === $source) {
			$source = $_POST;
		}
................................................................................

		$db = DB::getInstance();
		$db->begin();

		$su = new self;
		$su->date = new \DateTime;
		$su->importForm($source);





		$su->save();

		if ($su->fee()->id_account && !empty($source['amount'])) {
			$transaction = new Transaction;
			$transaction->id_creator = $user_id;
			$transaction->id_year = $su->fee()->id_year;








>









>








>
|







 







>
>
>
>
>







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
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
	const TABLE = 'services_users';

	protected $id;
	protected $id_user;
	protected $id_service;
	protected $id_fee;
	protected $paid;
	protected $expected_amount;
	protected $date;
	protected $expiry_date;

	protected $_types = [
		'id'          => 'int',
		'id_user'     => 'int',
		'id_fee'      => 'int',
		'id_service'  => 'int',
		'paid'        => 'bool',
		'expected_amount' => '?int',
		'date'        => 'date',
		'expiry_date' => '?date',
	];

	protected $_service, $_fee;

	public function selfCheck(): void
	{
		$this->paid = (bool) $this->paid;
		$this->assert(!DB::getInstance()->test(self::TABLE, 'id_user = ? AND id_service = ? AND date = ? AND id != ?', $this->id_user, $this->id_service, $this->date->format('Y-m-d'), $this->id), 'Cette activité a déjà été enregistrée pour ce membre et ce jour');
	}

	public function importForm(?array $source = null)
	{
		if (null === $source) {
			$source = $_POST;
		}
................................................................................

		$db = DB::getInstance();
		$db->begin();

		$su = new self;
		$su->date = new \DateTime;
		$su->importForm($source);

		if ($su->fee() && $su->id_user) {
			$su->expected_amount = $su->fee()->getAmountForUser($su->id_user);
		}

		$su->save();

		if ($su->fee()->id_account && !empty($source['amount'])) {
			$transaction = new Transaction;
			$transaction->id_creator = $user_id;
			$transaction->id_year = $su->fee()->id_year;

Modified src/include/lib/Garradin/Form.php from [cf95095bb5] to [024879329f].

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
			$db = DB::getInstance();
			return $db->test($params[0], $db->where($params[1], $value));
		});
	}

	public function run(callable $fn, ?string $csrf_key = null, ?string $redirect = null): bool
	{
		if (!$this->check($csrf_key)) {
			return false;
		}

		try {
			call_user_func($fn);

			if (null !== $redirect) {







|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
			$db = DB::getInstance();
			return $db->test($params[0], $db->where($params[1], $value));
		});
	}

	public function run(callable $fn, ?string $csrf_key = null, ?string $redirect = null): bool
	{
		if (null !== $csrf_key && !$this->check($csrf_key)) {
			return false;
		}

		try {
			call_user_func($fn);

			if (null !== $redirect) {

Modified src/include/lib/Garradin/Services/Services.php from [f203184d7c] to [89f45ea0ac].

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
<?php

namespace Garradin\Services;

use Garradin\Config;
use Garradin\DB;
use Garradin\Entities\Services\Service;
use Garradin\Entities\Services\Service_User;
use KD2\DB\EntityManager;

class Services
{
	static public function get(int $id)
	{
		return EntityManager::findOneById(Service::class, $id);
................................................................................
	}

	static public function listAssoc()
	{
		return DB::getInstance()->getAssoc('SELECT id, label FROM services ORDER BY label COLLATE NOCASE;');
	}

	static public function countForUser(int $user_id)
	{
		return DB::getInstance()->count(Service_User::TABLE, 'id_user = ?', $user_id);
	}

	static public function listGroupedWithFees(?int $user_id = null)
	{
		$services = DB::getInstance()->getGrouped('SELECT id, label, duration, start_date, end_date, description FROM services;');
		$fees = Fees::listAllByService($user_id);
		$out = [];

		foreach ($fees as $fee) {







<







 







<
<
<
<
<







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
<?php

namespace Garradin\Services;

use Garradin\Config;
use Garradin\DB;
use Garradin\Entities\Services\Service;

use KD2\DB\EntityManager;

class Services
{
	static public function get(int $id)
	{
		return EntityManager::findOneById(Service::class, $id);
................................................................................
	}

	static public function listAssoc()
	{
		return DB::getInstance()->getAssoc('SELECT id, label FROM services ORDER BY label COLLATE NOCASE;');
	}






	static public function listGroupedWithFees(?int $user_id = null)
	{
		$services = DB::getInstance()->getGrouped('SELECT id, label, duration, start_date, end_date, description FROM services;');
		$fees = Fees::listAllByService($user_id);
		$out = [];

		foreach ($fees as $fee) {

Added src/include/lib/Garradin/Services/Services_User.php version [f581a9e609].















































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
<?php

namespace Garradin\Services;

use Garradin\DB;
use Garradin\DynamicList;
use Garradin\Entities\Services\Service_User;
use KD2\DB\EntityManager;

class Services_User
{
	static public function get(int $id)
	{
		return EntityManager::findOneById(Service_User::class, $id);
	}

	static public function countForUser(int $user_id)
	{
		return DB::getInstance()->count(Service_User::TABLE, 'id_user = ?', $user_id);
	}

	static public function perUserList(int $user_id): DynamicList
	{
		$columns = [
			'id' => [
				'select' => 'su.id',
			],
			'label' => [
				'select' => 's.label',
				'label' => 'Activité',
			],
			'date' => [
				'label' => 'Date d\'inscription',
				'select' => 'su.date',
			],
			'status' => [
				'label' => 'Statut',
				'select' => 'CASE WHEN su.expiry_date < date() THEN -1 WHEN su.expiry_date >= date() THEN 1 ELSE 0 END',
			],
			'expiry' => [
				'label' => 'Date d\'expiration',
				'select' => 'MAX(su.expiry_date)',
			],
			'fee' => [
				'label' => 'Tarif',
				'select' => 'sf.label',
			],
			'paid' => [
				'label' => 'Payé',
				'select' => 'su.paid',
			],
			'amount' => [
				'label' => 'Reste à régler',
				'select' => 'expected_amount - SUM(tl.debit)',
			],
		];

		$tables = 'services_users su
			INNER JOIN services s ON s.id = su.id_service
			INNER JOIN services_fees sf ON sf.id = su.id_fee
			LEFT JOIN acc_transactions_users tu ON tu.id_service_user = su.id
			LEFT JOIN acc_transactions_lines tl ON tl.id_transaction = tu.id_transaction';
		$conditions = sprintf('su.id_user = %d', $user_id);

		$list = new DynamicList($columns, $tables, $conditions);
		$list->orderBy('date', true);
		$list->groupBy('su.id');
		$list->setCount('COUNT(*)');
		return $list;
	}
}

Modified src/templates/admin/membres/fiche.tpl from [b44e351993] to [7ab0b8783e].

16
17
18
19
20
21
22
23
24


25
26
27

28
29
30
31
32
33
34
        {if $nb_services == 1}
            Inscrit à <strong>{$nb_services}</strong> activité
        {elseif $nb_activites}
            Inscrit à <strong>{$nb_services}</strong> activités
        {else}
            N'est inscrit à aucune activité
        {/if}
        — <a href="{$admin_url}services/user.php?id={$membre.id}">Voir les inscriptions et règlements des activités</a>
    </dd>


    {if $session->canAccess('membres', Membres::DROIT_ECRITURE)}
        <dd>{linkbutton href="services/save.php?user=%d"|args:$membre.id label="Enregistrer une activité" shape="plus"}</dd>
    {/if}

    {if $session->canAccess('membres', Membres::DROIT_ACCES)}
        {if !empty($transactions_linked)}
            <dt>Écritures comptables liées</dt>
            <dd><a href="{$admin_url}acc/transactions/user.php?id={$membre.id}">{$transactions_linked} écritures comptables liées à ce membre</a></dd>
        {/if}
        {if !empty($transactions_created)}
            <dt>Écritures comptables créées</dt>







<

>
>
|
|
|
>







16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
        {if $nb_services == 1}
            Inscrit à <strong>{$nb_services}</strong> activité
        {elseif $nb_activites}
            Inscrit à <strong>{$nb_services}</strong> activités
        {else}
            N'est inscrit à aucune activité
        {/if}

    </dd>
    <dd>
        {linkbutton href="services/user.php?id=%d"|args:$membre.id label="Liste des inscriptions aux activités" shape="menu"}
        {if $session->canAccess('membres', Membres::DROIT_ECRITURE)}
            {linkbutton href="services/save.php?user=%d"|args:$membre.id label="Enregistrer une activité" shape="plus"}
        {/if}
    </dd>
    {if $session->canAccess('membres', Membres::DROIT_ACCES)}
        {if !empty($transactions_linked)}
            <dt>Écritures comptables liées</dt>
            <dd><a href="{$admin_url}acc/transactions/user.php?id={$membre.id}">{$transactions_linked} écritures comptables liées à ce membre</a></dd>
        {/if}
        {if !empty($transactions_created)}
            <dt>Écritures comptables créées</dt>

Modified src/templates/services/save.tpl from [972b3293ea] to [4ea249f9e2].

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
				</label>
			</dd>
			{/foreach}
		</dl>
		{/foreach}

		<dl>
			{input type="checkbox" name="paid" value="1" label="Marquer cette activité comme payée"}
			<dd class="help">En cas de règlement en plusieurs fois, il sera possible de cocher cette case lorsque le solde aura été réglé.</dd>
		</dl>
	</fieldset>

	<fieldset class="accounting">
		<legend>Enregistrement en comptabilité</legend>

		<dl>
			{input type="money" name="amount" label="Montant réglé par le membre" required=1}
			{input type="list" target="acc/charts/accounts/selector.php?targets=%s"|args:$account_targets name="account" label="Compte de règlement" required=1}
			{input type="text" name="payment_reference" label="Référence de paiement" help="Numéro de chèque, numéro de transaction CB, etc."}
		</dl>
{/if}
	</fieldset>

	<p class="submit">
................................................................................
	var amount = parseInt(elm.getAttribute('data-user-amount'), 10);

	// Toggle accounting part of the form
	var accounting = elm.getAttribute('data-account') ? true : false;
	g.toggle('.accounting', accounting);
	$('#f_amount').required = accounting;

	$('#f_paid_1').checked = true;

	// Fill the amount paid by the user
	if (amount) {
		$('#f_amount').value = g.formatMoney(amount);
	}
}

$('input[name=id_service]').forEach((e) => {







|
|







|







 







<
<







72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
...
119
120
121
122
123
124
125


126
127
128
129
130
131
132
				</label>
			</dd>
			{/foreach}
		</dl>
		{/foreach}

		<dl>
			{input type="checkbox" name="paid" value="1" default="1" label="Marquer cette activité comme payée"}
			<dd class="help">Décocher cette case pour pouvoir suivre les règlements de personnes qui payent en plusieurs fois. Il sera possible de cocher cette case lorsque le solde aura été réglé.</dd>
		</dl>
	</fieldset>

	<fieldset class="accounting">
		<legend>Enregistrement en comptabilité</legend>

		<dl>
			{input type="money" name="amount" label="Montant réglé par le membre" required=1 help="En cas de règlement en plusieurs fois il sera possible d'ajouter des règlements via la page de suivi des activités de ce membre."}
			{input type="list" target="acc/charts/accounts/selector.php?targets=%s"|args:$account_targets name="account" label="Compte de règlement" required=1}
			{input type="text" name="payment_reference" label="Référence de paiement" help="Numéro de chèque, numéro de transaction CB, etc."}
		</dl>
{/if}
	</fieldset>

	<p class="submit">
................................................................................
	var amount = parseInt(elm.getAttribute('data-user-amount'), 10);

	// Toggle accounting part of the form
	var accounting = elm.getAttribute('data-account') ? true : false;
	g.toggle('.accounting', accounting);
	$('#f_amount').required = accounting;



	// Fill the amount paid by the user
	if (amount) {
		$('#f_amount').value = g.formatMoney(amount);
	}
}

$('input[name=id_service]').forEach((e) => {

Added src/templates/services/user.tpl version [70da61d01d].

























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
{include file="admin/_head.tpl" title="%s — Liste des activités"|args:$user.identite current="membres/services"}

<p>
	{linkbutton href="membres/fiche.php?id=%d"|args:$user.id label="Retour à la fiche membre" shape="user"}
	{linkbutton href="services/save.php?user=%d"|args:$user.id label="Enregistrer une activité" shape="plus"}
</p>

{form_errors}

<dl class="cotisation">
	<dt>Nombre d'activités inscrites pour ce membre</dt>
	<dd>
		{$list->count()}
	</dd>
</dl>

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

	{foreach from=$list->iterate() item="row"}
		<tr>
			<th>{$row.label}</th>
			<td>{$row.date|date_short}</td>
			<td>
				{if $row.status == 1}
					<b class="confirm">À jour</b>
				{elseif $row.status == -1}
					<b class="error">En retard</b>
				{else}
					Pas d'expiration
				{/if}
			</td>
			<td>{$row.expiry|date_short}</td>
			<td>{$row.fee}</td>
			<td>{if $row.paid}<b class="confirm">Oui</b>{else}<b class="error">Non</b>{/if}</td>
			<td>{$row.amount|raw|money_currency}</td>
			<td class="actions">
				{if $session->canAccess('membres', Membres::DROIT_ECRITURE)}
					{if $row.paid}
						{linkbutton shape="reset" label="Marquer comme non payé" href="services/user.php?id=%d&su_id=%d&paid=0"|args:$user.id,$row.id}
					{else}
						{linkbutton shape="check" label="Marquer comme payé" href="services/user.php?id=%d&su_id=%d&paid=1"|args:$user.id,$row.id}
					{/if}
				{/if}
				{if $session->canAccess('compta', Membres::DROIT_ACCES)}
					{linkbutton shape="menu" label="Liste des écritures" href="acc/transactions/service_user.php?id=%d"|args:$row.id}
				{/if}
				{if $session->canAccess('compta', Membres::DROIT_ECRITURE)}
					{linkbutton shape="plus" label="Nouveau règlement" href="services/payment.php?id=%d"|args:$row.id}
				{/if}
			</td>
		</tr>
	{/foreach}

	</tbody>
</table>

{pagination url=$list->paginationURL() page=$list.page bypage=$list.per_page total=$list->count()}


{include file="admin/_foot.tpl"}

Modified src/www/admin/membres/fiche.php from [d7be312f13] to [4e74c286f1].

1
2
3
4
5
6
7
8
9
10
11
12
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
namespace Garradin;

use Garradin\Accounting\Transactions;
use Garradin\Services\Services;

require_once __DIR__ . '/_inc.php';

qv(['id' => 'required|numeric']);

$id = (int) qg('id');

................................................................................
$tpl->assign('champs', $champs->getList());

$cats = new Membres\Categories;

$categorie = $cats->get($membre->id_categorie);
$tpl->assign('categorie', $categorie);

$tpl->assign('nb_services', Services::countForUser($membre->id));

if ($session->canAccess('compta', Membres::DROIT_ACCES)) {
	$tpl->assign('transactions_linked', Transactions::countForUser($membre->id));
	$tpl->assign('transactions_created', Transactions::countForCreator($membre->id));
}

$tpl->assign('membre', $membre);

$tpl->display('admin/membres/fiche.tpl');




|







 







|









1
2
3
4
5
6
7
8
9
10
11
12
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
namespace Garradin;

use Garradin\Accounting\Transactions;
use Garradin\Services\Services_User;

require_once __DIR__ . '/_inc.php';

qv(['id' => 'required|numeric']);

$id = (int) qg('id');

................................................................................
$tpl->assign('champs', $champs->getList());

$cats = new Membres\Categories;

$categorie = $cats->get($membre->id_categorie);
$tpl->assign('categorie', $categorie);

$tpl->assign('nb_services', Services_User::countForUser($membre->id));

if ($session->canAccess('compta', Membres::DROIT_ACCES)) {
	$tpl->assign('transactions_linked', Transactions::countForUser($membre->id));
	$tpl->assign('transactions_created', Transactions::countForCreator($membre->id));
}

$tpl->assign('membre', $membre);

$tpl->display('admin/membres/fiche.tpl');

Modified src/www/admin/services/details.php from [44c311f406] to [c951b03a28].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace Garradin;
use Garradin\Services\Services;

require_once __DIR__ . '/_inc.php';

$session->requireAccess('membres', Membres::DROIT_ADMIN);

$service = Services::get((int) qg('id'));

if (!$service) {
	throw new UserException("Cette activité n'existe pas");
}







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace Garradin;
use Garradin\Services\Services;

require_once __DIR__ . '/_inc.php';

$session->requireAccess('membres', Membres::DROIT_ACCES);

$service = Services::get((int) qg('id'));

if (!$service) {
	throw new UserException("Cette activité n'existe pas");
}

Modified src/www/admin/services/fees/details.php from [eea7b6d83a] to [fb9b7b8df1].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace Garradin;
use Garradin\Services\Fees;

require_once __DIR__ . '/../_inc.php';

$session->requireAccess('membres', Membres::DROIT_ADMIN);

$fee = Fees::get((int) qg('id'));

if (!$fee) {
	throw new UserException("Ce tarif n'existe pas");
}







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
namespace Garradin;
use Garradin\Services\Fees;

require_once __DIR__ . '/../_inc.php';

$session->requireAccess('membres', Membres::DROIT_ACCES);

$fee = Fees::get((int) qg('id'));

if (!$fee) {
	throw new UserException("Ce tarif n'existe pas");
}

Modified src/www/admin/services/save.php from [31cc1e68ef] to [3028376e0f].

18
19
20
21
22
23
24




25
26
27
28
29
30
31

if (!$user_id) {
	$user_id = f('id_user');
}

$user_id = (int) $user_id ?: null;
$user_name = $user_id ? (new Membres)->getNom($user_id) : null;





$grouped_services = Services::listGroupedWithFees($user_id);

if (!count($grouped_services)) {
	Utils::redirect(ADMIN_URL . 'services/?CREATE');
}








>
>
>
>







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

if (!$user_id) {
	$user_id = f('id_user');
}

$user_id = (int) $user_id ?: null;
$user_name = $user_id ? (new Membres)->getNom($user_id) : null;

if (!$user_name) {
	$user_id = null;
}

$grouped_services = Services::listGroupedWithFees($user_id);

if (!count($grouped_services)) {
	Utils::redirect(ADMIN_URL . 'services/?CREATE');
}

Added src/www/admin/services/user.php version [5f66debe7d].































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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;
use Garradin\Services\Services_User;

require_once __DIR__ . '/_inc.php';

$session->requireAccess('membres', Membres::DROIT_ACCES);

$user = (new Membres)->get((int) qg('id'));

if (!$user) {
	throw new UserException("Cet utilisateur est introuvable");
}

$form->runIf($session->canAccess('membres', Membres::DROIT_ECRITURE) && null !== qg('paid') && qg('su_id'), function () use ($user) {
	$su = Services_User::get((int) qg('su_id'));

	if (!$su) {
		throw new UserException("Cette inscription est introuvable");
	}

	$su->paid = (bool)qg('paid');
	$su->save();
}, null, ADMIN_URL . 'services/user.php?id=' . $user->id);

$list = Services_User::perUserList($user->id);
$list->loadFromQueryString();

$tpl->assign(compact('list', 'user'));

$tpl->display('services/user.tpl');

Modified src/www/admin/static/admin.css from [c978e1b722] to [46007bb6d0].

1034
1035
1036
1037
1038
1039
1040

1041
1042
1043
1044
1045
1046
1047

h2.ruler:after {
    left: 0.5em;
    margin-right: -50%;
}

.icn-btn {

    display: inline-block;
    color: rgb(var(--gMainColor));
    border: 1px solid rgba(var(--gSecondColor), 0.5);
    background-color: rgba(var(--gSecondColor), 0.1);
    font-size: inherit;
    border-radius: .2em;
    padding: .2em .4em;







>







1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048

h2.ruler:after {
    left: 0.5em;
    margin-right: -50%;
}

.icn-btn {
    user-select: none;
    display: inline-block;
    color: rgb(var(--gMainColor));
    border: 1px solid rgba(var(--gSecondColor), 0.5);
    background-color: rgba(var(--gSecondColor), 0.1);
    font-size: inherit;
    border-radius: .2em;
    padding: .2em .4em;