Overview
Comment:Restrict accounts modification to the type we are asking for, when coming from the selector
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | accounts_refactor
Files: files | file ages | folders
SHA3-256: 06d7e3435e9601181efae6e1dd0e821f4a3081deb6c97a96d73c55aaa09b6eeb
User & Date: bohwaz on 2022-11-04 19:17:29
Other Links: branch diff | manifest | tags
Context
2022-11-04
19:31
Move export to chart check-in: de918cfd32 user: bohwaz tags: accounts_refactor
19:17
Restrict accounts modification to the type we are asking for, when coming from the selector check-in: 06d7e3435e user: bohwaz tags: accounts_refactor
17:35
Fix some issues with projects refactor check-in: 27098db828 user: bohwaz tags: accounts_refactor
Changes

Modified src/include/lib/Garradin/Accounting/Accounts.php from [34fca63884] to [912601d1ee].

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
		$condition = '';

		if (!empty($types)) {
			$types = array_map('intval', $types);
			$condition = ' AND ' . DB::getInstance()->where('type', $types);
		}

		$sql = sprintf('SELECT * FROM @TABLE WHERE id_chart = %d %s;', $this->chart_id, $condition);
		return $this->em->all($sql);
	}

	public function listForCodes(array $codes): array
	{
		return DB::getInstance()->getGrouped('SELECT code, id, label FROM acc_accounts WHERE id_chart = ?;', $this->chart_id);
	}

	/**
	 * Return all accounts from current chart
	 */
	public function export(): \Generator
	{
		$res = $this->em->DB()->iterate($this->em->formatQuery('SELECT
			code, label, description, position, type, user AS added
			FROM @TABLE WHERE id_chart = ? ORDER BY code COLLATE U_NOCASE;'),
			$this->chart_id);

		foreach ($res as $row) {
			$row->type = Account::TYPES_NAMES[$row->type];
			$row->position = Account::POSITIONS_NAMES[$row->position];
			$row->added = $row->added ? 'Ajouté' : '';
			yield $row;







|















|







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
		$condition = '';

		if (!empty($types)) {
			$types = array_map('intval', $types);
			$condition = ' AND ' . DB::getInstance()->where('type', $types);
		}

		$sql = sprintf('SELECT * FROM @TABLE WHERE id_chart = %d %s ORDER BY code COLLATE NOCASE;', $this->chart_id, $condition);
		return $this->em->all($sql);
	}

	public function listForCodes(array $codes): array
	{
		return DB::getInstance()->getGrouped('SELECT code, id, label FROM acc_accounts WHERE id_chart = ?;', $this->chart_id);
	}

	/**
	 * Return all accounts from current chart
	 */
	public function export(): \Generator
	{
		$res = $this->em->DB()->iterate($this->em->formatQuery('SELECT
			code, label, description, position, type, user AS added
			FROM @TABLE WHERE id_chart = ? ORDER BY code COLLATE NOCASE;'),
			$this->chart_id);

		foreach ($res as $row) {
			$row->type = Account::TYPES_NAMES[$row->type];
			$row->position = Account::POSITIONS_NAMES[$row->position];
			$row->added = $row->added ? 'Ajouté' : '';
			yield $row;

Modified src/include/lib/Garradin/Entities/Accounting/Account.php from [0a08478367] to [7a843d8100].

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226

	protected ?int $id;
	protected int $id_chart;
	protected string $code;
	protected string $label;
	protected ?string $description;
	protected int $position = 0;
	protected int $type = 0;
	protected bool $user = false;
	protected bool $bookmark = false;

	protected $_position = [];

	public function selfCheck(): void
	{







|







212
213
214
215
216
217
218
219
220
221
222
223
224
225
226

	protected ?int $id;
	protected int $id_chart;
	protected string $code;
	protected string $label;
	protected ?string $description;
	protected int $position = 0;
	protected int $type;
	protected bool $user = false;
	protected bool $bookmark = false;

	protected $_position = [];

	public function selfCheck(): void
	{
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
		if (!$base) {
			return $base;
		}

		$pattern = $base . '_%';

		$db = DB::getInstance();
		$used_codes = $db->getAssoc(sprintf('SELECT code, code FROM %s WHERE code LIKE ? AND id_chart = ?;', Account::TABLE), $this->chart_id, $pattern);
		$used_codes = array_values($used_codes);
		$used_codes = array_map(fn($a) => substr($a, strlen($base)), $used_codes);

		$count = $db->count(Account::TABLE, 'id_chart = ? AND code LIKE ?', $this->chart_id, $pattern);
		$letter = null;

		// Make sure we don't reuse an existing code
		while (!$letter || in_array($letter, $used_codes)) {
			// Get new account code, eg. 512A, 99AA, 99BZ etc.
			$letter = Utils::num2alpha($count++);
		}







|



|







327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
		if (!$base) {
			return $base;
		}

		$pattern = $base . '_%';

		$db = DB::getInstance();
		$used_codes = $db->getAssoc(sprintf('SELECT code, code FROM %s WHERE code LIKE ? AND id_chart = ?;', Account::TABLE), $pattern, $this->id_chart);
		$used_codes = array_values($used_codes);
		$used_codes = array_map(fn($a) => substr($a, strlen($base)), $used_codes);

		$count = $db->count(Account::TABLE, 'id_chart = ? AND code LIKE ?', $this->id_chart, $pattern);
		$letter = null;

		// Make sure we don't reuse an existing code
		while (!$letter || in_array($letter, $used_codes)) {
			// Get new account code, eg. 512A, 99AA, 99BZ etc.
			$letter = Utils::num2alpha($count++);
		}

Modified src/include/lib/Garradin/Entities/Accounting/Project.php from [50b0367f7a] to [8fed002baa].

1
2
3
4

5
6
7
8
9
10
11
<?php

namespace Garradin\Entities\Accounting;


use Garradin\Entity;

/**
 * Analytical projects
 */
class Project extends Entity
{




>







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

namespace Garradin\Entities\Accounting;

use Garradin\DB;
use Garradin\Entity;

/**
 * Analytical projects
 */
class Project extends Entity
{
27
28
29
30
31
32
33









34
35
36
37
38
39
40
		$this->assert(trim($this->label) !== '', 'L\'intitulé de projet ne peut rester vide.');
		$this->assert(strlen($this->label) <= 200, 'L\'intitulé de compte ne peut faire plus de 200 caractères.');

		if (null !== $this->description) {
			$this->assert(trim($this->description) !== '', 'L\'intitulé de projet est invalide.');
			$this->assert(strlen($this->description) <= 2000, 'L\'intitulé de compte ne peut faire plus de 2000 caractères.');
		}










		parent::selfCheck();
	}

	public function importForm(?array $source = null)
	{
		if (null === $source) {







>
>
>
>
>
>
>
>
>







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
		$this->assert(trim($this->label) !== '', 'L\'intitulé de projet ne peut rester vide.');
		$this->assert(strlen($this->label) <= 200, 'L\'intitulé de compte ne peut faire plus de 200 caractères.');

		if (null !== $this->description) {
			$this->assert(trim($this->description) !== '', 'L\'intitulé de projet est invalide.');
			$this->assert(strlen($this->description) <= 2000, 'L\'intitulé de compte ne peut faire plus de 2000 caractères.');
		}

		$db = DB::getInstance();

		if ($this->exists()) {
			$this->assert(!$db->test(self::TABLE, 'code = ? AND id != ?', $this->code, $this->id()), 'Ce code est déjà utilisé par un autre projet.');
		}
		else {
			$this->assert(!$db->test(self::TABLE, 'code = ?', $this->code), 'Ce code est déjà utilisé par un autre projet.');
		}

		parent::selfCheck();
	}

	public function importForm(?array $source = null)
	{
		if (null === $source) {

Modified src/include/lib/Garradin/Entities/Accounting/Transaction.php from [843dcfd173] to [8b5c47b933].

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
		'Dépense',
		'Virement',
		'Dette',
		'Créance',
	];

	protected ?int $id;
	protected int $type = 0;
	protected int $status = 0;
	protected string $label;
	protected ?string $notes = null;
	protected ?string $reference = null;

	protected \KD2\DB\Date $date;








|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
		'Dépense',
		'Virement',
		'Dette',
		'Créance',
	];

	protected ?int $id;
	protected ?int $type = null;
	protected int $status = 0;
	protected string $label;
	protected ?string $notes = null;
	protected ?string $reference = null;

	protected \KD2\DB\Date $date;

Modified src/include/lib/Garradin/Entities/Accounting/Year.php from [f570fb0ebe] to [81cc63f281].

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
			];
		}

		$db = DB::getInstance();

		$sql = sprintf('SELECT a.* FROM acc_accounts a
			LEFT JOIN acc_transactions_lines b ON b.id_account = a.id
			LEFT JOIN acc_transactions c ON c.id = b.id_transaction
			WHERE a.id_chart = %d AND a.%s AND (a.bookmark = 1 OR a.user = 1 OR b.id IS NOT NULL) AND c.id_year = ?
			GROUP BY a.id
			ORDER BY type, code COLLATE NOCASE;',
			$this->chart_id,

			$db->where('type', $types),
			$this->id()
		);

		$query = $db->iterate($sql);

		foreach ($query as $row) {
			$out[$row->type]->accounts[] = $row;
		}

		return $out;
	}

}







|
|


|
>
|
<












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
			];
		}

		$db = DB::getInstance();

		$sql = sprintf('SELECT a.* FROM acc_accounts a
			LEFT JOIN acc_transactions_lines b ON b.id_account = a.id
			LEFT JOIN acc_transactions c ON c.id = b.id_transaction AND c.id_year = %d
			WHERE a.id_chart = %d AND a.%s AND (a.bookmark = 1 OR a.user = 1 OR c.id IS NOT NULL)
			GROUP BY a.id
			ORDER BY type, code COLLATE NOCASE;',
			$this->id(),
			$this->id_chart,
			$db->where('type', $types)

		);

		$query = $db->iterate($sql);

		foreach ($query as $row) {
			$out[$row->type]->accounts[] = $row;
		}

		return $out;
	}

}

Modified src/include/lib/Garradin/Template.php from [543942724f] to [eb62a3d200].

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
		$this->assign('www_url', WWW_URL);
		$this->assign('admin_url', ADMIN_URL);
		$this->assign('help_url', sprintf(HELP_URL, str_replace('/admin/', '', Utils::getSelfURI(false))));
		$this->assign('self_url', Utils::getSelfURI());
		$this->assign('self_url_no_qs', Utils::getSelfURI(false));

		$this->assign('is_logged', false);
		$this->assign('dialog', isset($_GET['_dialog']));

		$this->assign('password_pattern', sprintf('.{%d,}', Session::MINIMUM_PASSWORD_LENGTH));
		$this->assign('password_length', Session::MINIMUM_PASSWORD_LENGTH);

		$this->register_compile_function('continue', function (Smartyer $s, $pos, $block, $name, $raw_args) {
			if ($block == 'continue')
			{







|







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
		$this->assign('www_url', WWW_URL);
		$this->assign('admin_url', ADMIN_URL);
		$this->assign('help_url', sprintf(HELP_URL, str_replace('/admin/', '', Utils::getSelfURI(false))));
		$this->assign('self_url', Utils::getSelfURI());
		$this->assign('self_url_no_qs', Utils::getSelfURI(false));

		$this->assign('is_logged', false);
		$this->assign('dialog', isset($_GET['_dialog']) ? ($_GET['_dialog'] ?: true) : false);

		$this->assign('password_pattern', sprintf('.{%d,}', Session::MINIMUM_PASSWORD_LENGTH));
		$this->assign('password_length', Session::MINIMUM_PASSWORD_LENGTH);

		$this->register_compile_function('continue', function (Smartyer $s, $pos, $block, $name, $raw_args) {
			if ($block == 'continue')
			{

Modified src/templates/acc/charts/accounts/_nav.tpl from [9a874fc548] to [874d4ab505].

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
{if $dialog !== 'manage'}
<nav class="tabs">
{if $dialog}
	{* JS trick to get back to the original iframe URL! *}
	<aside>
		{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
			{linkbutton href="!acc/charts/accounts/new.php?id=%d"|args:$chart.id label="Ajouter un compte" shape="plus"}
		{/if}
		{linkbutton shape="left" label="Retour à la sélection de compte" href="#" onclick="g.reloadParentDialog(); return false;"}
	</aside>
	<ul>
{else}
	<ul>
		<li><a href="{$admin_url}acc/years/">Exercices</a></li>
		<li><a href="{$admin_url}acc/projects/">Projets <em>(compta analytique)</em></a></li>
		<li class="current"><a href="{$admin_url}acc/charts/">Plans comptables</a></li>
	</ul>
	{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
		<aside>{linkbutton href="!acc/charts/accounts/new.php?id=%d"|args:$chart.id label="Ajouter un compte" shape="plus" target="_dialog=manage"}</aside>
	{/if}
	<ul class="sub">
		<li class="title">{$chart.label}</li>
{/if}

		<li{if $current == 'favorites'} class="current"{/if}>{link href="!acc/charts/accounts/?id=%d"|args:$chart.id label="Comptes usuels"}</li>
		<li{if $current == 'all'} class="current"{/if}>{link href="!acc/charts/accounts/all.php?id=%d"|args:$chart.id label="Tous les comptes"}</li>
	</ul>
</nav>
{/if}
|





|











|





|
|



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
{if !$dialog || $dialog !== 'manage'}
<nav class="tabs">
{if $dialog}
	{* JS trick to get back to the original iframe URL! *}
	<aside>
		{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
			{linkbutton href="!acc/charts/accounts/new.php?id=%d&%s"|args:$chart.id,$types_arg label="Ajouter un compte" shape="plus"}
		{/if}
		{linkbutton shape="left" label="Retour à la sélection de compte" href="#" onclick="g.reloadParentDialog(); return false;"}
	</aside>
	<ul>
{else}
	<ul>
		<li><a href="{$admin_url}acc/years/">Exercices</a></li>
		<li><a href="{$admin_url}acc/projects/">Projets <em>(compta analytique)</em></a></li>
		<li class="current"><a href="{$admin_url}acc/charts/">Plans comptables</a></li>
	</ul>
	{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
		<aside>{linkbutton href="!acc/charts/accounts/new.php?id=%d&%s"|args:$chart.id,$types_arg label="Ajouter un compte" shape="plus" target=$dialog_target}</aside>
	{/if}
	<ul class="sub">
		<li class="title">{$chart.label}</li>
{/if}

		<li{if $current == 'favorites'} class="current"{/if}>{link href="!acc/charts/accounts/?id=%d&%s"|args:$chart.id,$types_arg label="Comptes usuels"}</li>
		<li{if $current == 'all'} class="current"{/if}>{link href="!acc/charts/accounts/all.php?id=%d&%s"|args:$chart.id,$types_arg label="Tous les comptes"}</li>
	</ul>
</nav>
{/if}

Modified src/templates/acc/charts/accounts/all.tpl from [c7bac3ef69] to [e87e058cee].

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
			</td>
			<td>
				{if $account.user}<em>Ajouté</em>{/if}
			</td>
			<td class="actions">
				{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN) && !$chart.archived}
					{if $account.user || !$chart.code}
						{linkbutton shape="delete" label="Supprimer" href="!acc/charts/accounts/delete.php?id=%d"|args:$account.id target="_dialog=manage"}
					{/if}
					{linkbutton shape="edit" label="Modifier" href="!acc/charts/accounts/edit.php?id=%d"|args:$account.id target="_dialog=manage"}
				{/if}
			</td>
		</tr>
	{/foreach}
	</tbody>
</table>

<script type="text/javascript" src="{$admin_url}static/scripts/accounts_bookmark.js"></script>

</form>

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







|

|












23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
			</td>
			<td>
				{if $account.user}<em>Ajouté</em>{/if}
			</td>
			<td class="actions">
				{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN) && !$chart.archived}
					{if $account.user || !$chart.code}
						{linkbutton shape="delete" label="Supprimer" href="!acc/charts/accounts/delete.php?id=%d&%s"|args:$account.id,$types_arg target=$dialog_target}
					{/if}
					{linkbutton shape="edit" label="Modifier" href="!acc/charts/accounts/edit.php?id=%d%s"|args:$account.id,$types_arg target=$dialog_target}
				{/if}
			</td>
		</tr>
	{/foreach}
	</tbody>
</table>

<script type="text/javascript" src="{$admin_url}static/scripts/accounts_bookmark.js"></script>

</form>

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

Modified src/templates/acc/charts/accounts/index.tpl from [9df69c6b13] to [af532ac7cc].

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
<table class="list">
{foreach from=$accounts_grouped item="group"}
	<tbody>
		<tr>
			<td colspan="4"><h2 class="ruler">{$group.label}</h2></td>
			<td class="actions">
				{if !$chart.archived}
					{linkbutton label="Ajouter un compte" shape="plus" href="!acc/charts/accounts/new.php?id=%d&type=%d"|args:$chart.id,$group.type  target="_dialog=manage"}
				{/if}
			</td>
		</tr>

	{foreach from=$group.accounts item="account"}
		<tr class="account">
			<td class="num">{$account.code}</td>
			<th>{$account.label}</th>
			<td class="desc">{$account.description}</td>
			<td>
				<?php
				$shape = $account->bookmark ? 'check' : 'uncheck';
				$title = $account->bookmark ? 'Ôter des favoris' : 'Marquer comme favori';
				?>
				{button shape=$shape name="bookmark[%d]"|args:$account.id value=$account.bookmark label="Favori" title=$title type="submit"}
			</td>
			<td class="actions">
				{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN) && !$chart.archived}
					{if (!$chart->code || $account->user) && $account->canDelete()}
						{linkbutton shape="delete" label="Supprimer" href="!acc/charts/accounts/delete.php?id=%d"|args:$account.id  target="_dialog=manage"}
					{/if}
					{linkbutton shape="edit" label="Modifier" href="!acc/charts/accounts/edit.php?id=%d"|args:$account.id  target="_dialog=manage"}
				{/if}
			</td>
		</tr>
	{/foreach}
	</tbody>
{/foreach}
</table>

<script type="text/javascript" src="{$admin_url}static/scripts/accounts_bookmark.js"></script>
</form>

{include file="admin/_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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<table class="list">
{foreach from=$accounts_grouped item="group"}
	<tbody>
		<tr>
			<td colspan="4"><h2 class="ruler">{$group.label}</h2></td>
			<td class="actions">
				{if !$chart.archived}
					{linkbutton label="Ajouter un compte" shape="plus" href="!acc/charts/accounts/new.php?id=%d&type=%d&%s"|args:$chart.id,$group.type,$types_arg  target=$dialog_target}
				{/if}
			</td>
		</tr>

	{foreach from=$group.accounts item="account"}
		<tr class="account">
			<td class="num">{$account.code}</td>
			<th>{$account.label}</th>
			<td class="desc">{$account.description}</td>
			<td>
				<?php
				$shape = $account->bookmark ? 'check' : 'uncheck';
				$title = $account->bookmark ? 'Ôter des favoris' : 'Marquer comme favori';
				?>
				{button shape=$shape name="bookmark[%d]"|args:$account.id value=$account.bookmark label="Favori" title=$title type="submit"}
			</td>
			<td class="actions">
				{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN) && !$chart.archived}
					{if (!$chart->code || $account->user) && $account->canDelete()}
						{linkbutton shape="delete" label="Supprimer" href="!acc/charts/accounts/delete.php?id=%d&%s"|args:$account.id,$types_arg  target=$dialog_target}
					{/if}
					{linkbutton shape="edit" label="Modifier" href="!acc/charts/accounts/edit.php?id=%d&%s"|args:$account.id,$types_arg  target=$dialog_target}
				{/if}
			</td>
		</tr>
	{/foreach}
	</tbody>
{/foreach}
</table>

<script type="text/javascript" src="{$admin_url}static/scripts/accounts_bookmark.js"></script>
</form>

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

Modified src/templates/acc/charts/accounts/new.tpl from [e0cd0bf70e] to [7f293e5d2c].

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
		<p class="help">
			Si ce compte vous convient tel quel, vous pouvez l'ajouter à vos comptes favoris, il sera ainsi toujours dans les listes de comptes.<br />
			Sinon vous pouvez créer un sous-compte pour plus de détails.
		</p>

		<p class="submit">
			{csrf_field key=$csrf_key}
			{button type="submit" shape="star" name="bookmark" value=$ask.id label="Ajouter ce compte à mes favoris" class="main"}
			— ou —
			{button type="submit" shape="right" name="from" value=$ask.id label="Créer un sous-compte" class="main"}
		</p>
	</fieldset>

</form>








|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
		<p class="help">
			Si ce compte vous convient tel quel, vous pouvez l'ajouter à vos comptes favoris, il sera ainsi toujours dans les listes de comptes.<br />
			Sinon vous pouvez créer un sous-compte pour plus de détails.
		</p>

		<p class="submit">
			{csrf_field key=$csrf_key}
			{button type="submit" shape="star" name="toggle_bookmark" value=$ask.id label="Ajouter ce compte à mes favoris" class="main"}
			— ou —
			{button type="submit" shape="right" name="from" value=$ask.id label="Créer un sous-compte" class="main"}
		</p>
	</fieldset>

</form>

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
		</p>

		<table class="list">
			<tbody>
			{foreach from=$missing item="item"}
				<tr class="account account-level-{$item.level}">
					<td class="num">{$item.code}</td>
					<th>{linkbutton href="?id=%d&type=%d&ask=%d"|args:$account.id_chart,$account.type,$item.id label=$item.label}</th>
					<td>{if $item.description}<span class="help">{$item.description|escape|nl2br}</span>{/if}</td>
					<td class="actions">
						{linkbutton href="?id=%d&type=%d&ask=%d"|args:$account.id_chart,$account.type,$item.id label="Sélectionner" shape="right"}
					</td>
				</tr>
			{/foreach}
			</tbody>
		</table>
	</fieldset>








|


|







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
		</p>

		<table class="list">
			<tbody>
			{foreach from=$missing item="item"}
				<tr class="account account-level-{$item.level}">
					<td class="num">{$item.code}</td>
					<th>{linkbutton href="?id=%d&type=%d&ask=%d&%s"|args:$account.id_chart,$account.type,$item.id,$types_arg label=$item.label}</th>
					<td>{if $item.description}<span class="help">{$item.description|escape|nl2br}</span>{/if}</td>
					<td class="actions">
						{linkbutton href="?id=%d&type=%d&ask=%d&%s"|args:$account.id_chart,$account.type,$item.id,$types_arg label="Sélectionner" shape="right"}
					</td>
				</tr>
			{/foreach}
			</tbody>
		</table>
	</fieldset>

Modified src/templates/acc/charts/accounts/selector.tpl from [36117210ca] to [aa02383c7a].

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
	<header>
		<h2>
			<input type="text" placeholder="Recherche rapide" id="lookup" />
		</h2>

		{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
			<?php $page = isset($grouped_accounts) ? '' : 'all.php'; ?>
			<p class="edit">{linkbutton label="Modifier les comptes" href="!acc/charts/accounts/%s?id=%d"|args:$page,$chart.id shape="edit"}</aside></p>
		{/if}

		<p>{input type="select" name="filter" options=$filter_options default=$filter}</p>
	</header>

	{if isset($grouped_accounts)}
		<?php $index = 1; ?>







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
	<header>
		<h2>
			<input type="text" placeholder="Recherche rapide" id="lookup" />
		</h2>

		{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
			<?php $page = isset($grouped_accounts) ? '' : 'all.php'; ?>
			<p class="edit">{linkbutton label="Modifier les comptes" href="!acc/charts/accounts/%s?id=%d&types=%s"|args:$page,$chart.id,$targets_str shape="edit"}</aside></p>
		{/if}

		<p>{input type="select" name="filter" options=$filter_options default=$filter}</p>
	</header>

	{if isset($grouped_accounts)}
		<?php $index = 1; ?>

Modified src/templates/acc/projects/_nav.tpl from [e940847414] to [d3af9724e3].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{if !$dialog}
<nav class="tabs">
	<ul>
		<li><a href="{$admin_url}acc/years/">Exercices</a></li>
		<li class="current"><a href="{$admin_url}acc/projects/">Projets <em>(compta analytique)</em></a></li>
		<li><a href="{$admin_url}acc/charts/">Plans comptables</a></li>
	</ul>

	{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
	<aside>
		{linkbutton label="Créer un nouveau projet" href="edit.php" shape="plus"}
	</aside>

	<ul class="sub">
		<li{if $current != 'config'} class="current"{/if}>{link href="!acc/projects/" label="Liste des projets"}</li>
		<li{if $current == 'config'} class="current"{/if}>{link href="!acc/projects/config.php" label="Configuration"}</li>
	</ul>
	{/if}
</nav>
{/if}










|









1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{if !$dialog}
<nav class="tabs">
	<ul>
		<li><a href="{$admin_url}acc/years/">Exercices</a></li>
		<li class="current"><a href="{$admin_url}acc/projects/">Projets <em>(compta analytique)</em></a></li>
		<li><a href="{$admin_url}acc/charts/">Plans comptables</a></li>
	</ul>

	{if $session->canAccess($session::SECTION_ACCOUNTING, $session::ACCESS_ADMIN)}
	<aside>
		{linkbutton label="Créer un nouveau projet" href="edit.php" shape="plus" target="_dialog"}
	</aside>

	<ul class="sub">
		<li{if $current != 'config'} class="current"{/if}>{link href="!acc/projects/" label="Liste des projets"}</li>
		<li{if $current == 'config'} class="current"{/if}>{link href="!acc/projects/config.php" label="Configuration"}</li>
	</ul>
	{/if}
</nav>
{/if}

Modified src/templates/acc/projects/edit.tpl from [352c97aa6e] to [2f87196e30].

1
2
3
4


5
6
7
8
9
10
11
12
{include file="admin/_head.tpl" title="Projet" current="acc/years"}

{include file="./_nav.tpl" current=null}



<form method="post" action="">
	<fieldset>
		<legend>{if $project->exists()}Modifier un projet{else}Créer un projet{/if}</legend>
		<dl>
			{input type="text" required=true name="label" label="Libellé du projet" source=$project}
			{input type="text" required=true name="code" label="Code du projet" source=$project help="Utile pour retrouver le projet rapidement. Ne peut contenir que des chiffres et des lettres majuscules." pattern="[0-9A-Z_]+"}
			{input type="textarea" required=false name="description" label="Description du projet" source=$project}
			<dt>Archivage</dt>




>
>
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
{include file="admin/_head.tpl" title="Projet" current="acc/years"}

{include file="./_nav.tpl" current=null}

{form_errors}

<form method="post" action="" data-focus="1">
	<fieldset>
		<legend>{if $project->exists()}Modifier un projet{else}Créer un projet{/if}</legend>
		<dl>
			{input type="text" required=true name="label" label="Libellé du projet" source=$project}
			{input type="text" required=true name="code" label="Code du projet" source=$project help="Utile pour retrouver le projet rapidement. Ne peut contenir que des chiffres et des lettres majuscules." pattern="[0-9A-Z_]+"}
			{input type="textarea" required=false name="description" label="Description du projet" source=$project}
			<dt>Archivage</dt>

Added src/www/admin/acc/charts/accounts/_inc.php version [675fc186a8].









































































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

namespace Garradin;

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

// Only open edit/delete/new actions in a dialog if we are not already in a dialog
$dialog_target = !isset($_GET['_dialog']) ? '_dialog=manage' : null;

$types = null;
$types_arg = null;

// Filter only some types (if coming from a selector)
if (qg('types')) {
	$types = explode(':', qg('types'));
	$types = array_map('intval', $types);
	$types_arg = 'types=' . implode(':', $types);
}

$tpl->assign(compact('types_arg', 'dialog_target'));

function chart_reload_or_redirect(string $url)
{
	global $types_arg;

	if (($_GET['_dialog'] ?? null) === 'manage') {
		Utils::reloadParentFrame();
		return;
	}

	if ($types_arg) {
		$url .= '&' . $types_arg;
	}

	Utils::redirect($url);
}

Modified src/www/admin/acc/charts/accounts/all.php from [eb3a7efcd1] to [45c8971e82].

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

use Garradin\Accounting\Charts;

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

$chart = null;

if ($id = (int)qg('id')) {
	$chart = Charts::get($id);
}
elseif (CURRENT_YEAR_ID) {





|







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

use Garradin\Accounting\Charts;

require_once __DIR__ . '/_inc.php';

$chart = null;

if ($id = (int)qg('id')) {
	$chart = Charts::get($id);
}
elseif (CURRENT_YEAR_ID) {
32
33
34
35
36
37
38
39
40
41


42
43
44
	$value = current($b);
	$a = $accounts->get($id);
	$a->bookmark = (bool) $value;
	$a->save();
}, null, Utils::getSelfURI());


$list = $accounts->list();
$list->loadFromQueryString();



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

$tpl->display('acc/charts/accounts/all.tpl');







|


>
>
|


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
	$value = current($b);
	$a = $accounts->get($id);
	$a->bookmark = (bool) $value;
	$a->save();
}, null, Utils::getSelfURI());


$list = $accounts->list($types);
$list->loadFromQueryString();

$target = !isset($_GET['_dialog']) ? '_dialog=manage' : null;

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

$tpl->display('acc/charts/accounts/all.tpl');

Modified src/www/admin/acc/charts/accounts/delete.php from [c7b1adebad] to [f88d2bbd2d].

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

use Garradin\Accounting\Accounts;

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

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

$account = Accounts::get((int) qg('id'));

if (!$account) {
	throw new UserException("Le compte demandé n'existe pas.");





|







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

use Garradin\Accounting\Accounts;

require_once __DIR__ . '/_inc.php';

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

$account = Accounts::get((int) qg('id'));

if (!$account) {
	throw new UserException("Le compte demandé n'existe pas.");
23
24
25
26
27
28
29

30

31
32
33
34
	throw new UserException("Ce compte ne peut être supprimé car des écritures y sont liées (sur l'exercice courant ou sur un exercice déjà clôturé).\nSi vous souhaitez faire du ménage dans la liste des comptes il est recommandé de créer un nouveau plan comptable. Attention, il n'est pas possible de modifier le plan comptable d'un exercice ouvert.");
}

$csrf_key = 'acc_accounts_delete_' . $account->id();

$form->runIf('delete', function () use ($account) {
	$account->delete();

}, $csrf_key, sprintf('!acc/charts/accounts/?id=%d', $account->id_chart));


$tpl->assign(compact('account', 'csrf_key'));

$tpl->display('acc/charts/accounts/delete.tpl');







>
|
>




23
24
25
26
27
28
29
30
31
32
33
34
35
36
	throw new UserException("Ce compte ne peut être supprimé car des écritures y sont liées (sur l'exercice courant ou sur un exercice déjà clôturé).\nSi vous souhaitez faire du ménage dans la liste des comptes il est recommandé de créer un nouveau plan comptable. Attention, il n'est pas possible de modifier le plan comptable d'un exercice ouvert.");
}

$csrf_key = 'acc_accounts_delete_' . $account->id();

$form->runIf('delete', function () use ($account) {
	$account->delete();

	chart_reload_or_redirect(sprintf('!acc/charts/accounts/?id=%d', $account->id_chart));
}, $csrf_key);

$tpl->assign(compact('account', 'csrf_key'));

$tpl->display('acc/charts/accounts/delete.tpl');

Modified src/www/admin/acc/charts/accounts/edit.php from [ece7871f6a] to [44a35c591a].

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

use Garradin\Accounting\Accounts;

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

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

$account = Accounts::get((int) qg('id'));

if (!$account) {
	throw new UserException("Le compte demandé n'existe pas.");





|







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

use Garradin\Accounting\Accounts;

require_once __DIR__ . '/_inc.php';

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

$account = Accounts::get((int) qg('id'));

if (!$account) {
	throw new UserException("Le compte demandé n'existe pas.");
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
	}
	else {
		$account->importForm();
	}

	$account->save();

	if (isset($_GET['_dialog'])) {
		Utils::reloadParentFrame();
		return;
	}

	$page = '';

	if (!$account->type) {
		$page = 'all.php';
	}

	Utils::redirect(sprintf('!acc/charts/accounts/%s?id=%d', $page, $account->id_chart));
}, $csrf_key);

if ($account->type) {
	$tpl->assign('code_base', $account->getNumberBase());
	$tpl->assign('code_value', $account->getNumberUserPart());
}

$tpl->assign(compact('account', 'can_edit', 'chart'));

$tpl->display('acc/charts/accounts/edit.tpl');







<
<
<
<
<






|










28
29
30
31
32
33
34





35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
	}
	else {
		$account->importForm();
	}

	$account->save();






	$page = '';

	if (!$account->type) {
		$page = 'all.php';
	}

	chart_reload_or_redirect(sprintf('!acc/charts/accounts/%s?id=%d', $page, $account->id_chart));
}, $csrf_key);

if ($account->type) {
	$tpl->assign('code_base', $account->getNumberBase());
	$tpl->assign('code_value', $account->getNumberUserPart());
}

$tpl->assign(compact('account', 'can_edit', 'chart'));

$tpl->display('acc/charts/accounts/edit.tpl');

Modified src/www/admin/acc/charts/accounts/index.php from [01427cdcb5] to [9349ac07de].

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
<?php
namespace Garradin;

use Garradin\Accounting\Charts;

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

$chart = null;

if ($id = (int)qg('id')) {
	$chart = Charts::get($id);
}
elseif (CURRENT_YEAR_ID) {
	$year = $current_year;
	$chart = $year->chart();
}

if (!$chart) {
	throw new UserException('Aucun plan comptable spécifié');
}

$accounts = $chart->accounts();

$tpl->assign(compact('chart'));
$tpl->assign('accounts_grouped', $accounts->listCommonGrouped(null, true));
$tpl->display('acc/charts/accounts/index.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
<?php
namespace Garradin;

use Garradin\Accounting\Charts;

require_once __DIR__ . '/_inc.php';

$chart = null;

if ($id = (int)qg('id')) {
	$chart = Charts::get($id);
}
elseif (CURRENT_YEAR_ID) {
	$year = $current_year;
	$chart = $year->chart();
}

if (!$chart) {
	throw new UserException('Aucun plan comptable spécifié');
}

$accounts = $chart->accounts();

$tpl->assign(compact('chart'));
$tpl->assign('accounts_grouped', $accounts->listCommonGrouped($types, true));
$tpl->display('acc/charts/accounts/index.tpl');

Modified src/www/admin/acc/charts/accounts/new.php from [1f47f5b4ff] to [189981f44a].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
namespace Garradin;

use Garradin\Entities\Accounting\Account;
use Garradin\Entities\Accounting\Transaction;
use Garradin\Entities\Accounting\Line;
use Garradin\Accounting\Accounts;
use Garradin\Accounting\Charts;
use Garradin\Membres\Session;

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

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

$chart = Charts::get((int)qg('id'));

if (!$chart) {
	throw new UserException('Ce plan comptable n\'existe pas');










|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
namespace Garradin;

use Garradin\Entities\Accounting\Account;
use Garradin\Entities\Accounting\Transaction;
use Garradin\Entities\Accounting\Line;
use Garradin\Accounting\Accounts;
use Garradin\Accounting\Charts;
use Garradin\Membres\Session;

require_once __DIR__ . '/_inc.php';

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

$chart = Charts::get((int)qg('id'));

if (!$chart) {
	throw new UserException('Ce plan comptable n\'existe pas');
30
31
32
33
34
35
36



37
38
39
40
41
42
43

44

45
46
47
48
49
50
51

$type = f('type') ?? qg('type');

// Simple creation with pre-determined account type
if ($type !== null) {
	$account->type = (int)$type;
}




$csrf_key = 'account_new';

$form->runIf('bookmark', function () use ($accounts) {
	$a = $accounts->get(f('bookmark'));
	$a->bookmark = true;
	$a->save();

}, $csrf_key, '!acc/charts/accounts/?id=' . $chart->id());


$form->runIf('save', function () use ($account, $accounts, $chart, $current_year) {
	$db = DB::getInstance();

	$db->begin();
	$account->importForm();








>
>
>



|
|


>
|
>







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

$type = f('type') ?? qg('type');

// Simple creation with pre-determined account type
if ($type !== null) {
	$account->type = (int)$type;
}
elseif (isset($types) && is_array($types) && count($types) == 1) {
	$account->type = (int)current($types);
}

$csrf_key = 'account_new';

$form->runIf('toggle_bookmark', function () use ($accounts, $chart) {
	$a = $accounts->get(f('toggle_bookmark'));
	$a->bookmark = true;
	$a->save();

	chart_reload_or_redirect('!acc/charts/accounts/?id=' . $chart->id());
}, $csrf_key);

$form->runIf('save', function () use ($account, $accounts, $chart, $current_year) {
	$db = DB::getInstance();

	$db->begin();
	$account->importForm();

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

	$page = '';

	if (!$account->type) {
		$page = 'all.php';
	}

	$url = sprintf('!acc/charts/accounts/%s?id=%d', $page, $account->id_chart);
	Utils::redirect($url);
}, $csrf_key);

$types_create = [
	Account::TYPE_EXPENSE => [
		'label' => Account::TYPES_NAMES[Account::TYPE_EXPENSE],
		'help' => 'Compte destiné à recevoir les dépenses (charges)',
	],







|
<







81
82
83
84
85
86
87
88

89
90
91
92
93
94
95

	$page = '';

	if (!$account->type) {
		$page = 'all.php';
	}

	chart_reload_or_redirect(sprintf('!acc/charts/accounts/%s?id=%d', $page, $account->id_chart));

}, $csrf_key);

$types_create = [
	Account::TYPE_EXPENSE => [
		'label' => Account::TYPES_NAMES[Account::TYPE_EXPENSE],
		'help' => 'Compte destiné à recevoir les dépenses (charges)',
	],
133
134
135
136
137
138
139

140

141
142
143
144
145
	$ask = $accounts->get($id);
}

if ($account->type && !$from) {
	$code_base = $account->getNumberBase();
	$code_value = $account->getNewNumberAvailable($code_base);


	$missing = $accounts->listMissing($account->type);

}

$tpl->assign(compact('types_create', 'account', 'chart', 'ask', 'csrf_key', 'missing', 'code_base', 'code_value', 'from'));

$tpl->display('acc/charts/accounts/new.tpl');







>
|
>





137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
	$ask = $accounts->get($id);
}

if ($account->type && !$from) {
	$code_base = $account->getNumberBase();
	$code_value = $account->getNewNumberAvailable($code_base);

	if (null === f('from')) {
		$missing = $accounts->listMissing($account->type);
	}
}

$tpl->assign(compact('types_create', 'account', 'chart', 'ask', 'csrf_key', 'missing', 'code_base', 'code_value', 'from'));

$tpl->display('acc/charts/accounts/new.tpl');

Modified src/www/admin/acc/charts/accounts/selector.php from [216eaab641] to [c9b4113a17].

19
20
21
22
23
24
25
26
27




28
29
30
31
32
33
34

$year = null;
$filter = qg('filter');
$filter_options = [
//	'bookmark' => 'Voir seulement les comptes favoris',
	'usual' => 'Voir seulement les comptes favoris et usuels',
	'all' => 'Voir tous les comptes',
	'any' => 'Voir tout le plan comptable',
];





if (null !== $filter) {
	if (!array_key_exists($filter, $filter_options)) {
		$filter = 'usual';
	}

	$session->set('account_selector_filter', $filter);







<

>
>
>
>







19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37

$year = null;
$filter = qg('filter');
$filter_options = [
//	'bookmark' => 'Voir seulement les comptes favoris',
	'usual' => 'Voir seulement les comptes favoris et usuels',
	'all' => 'Voir tous les comptes',

];

if (!count($targets)) {
	$filter_options['any'] = 'Voir tout le plan comptable';
}

if (null !== $filter) {
	if (!array_key_exists($filter, $filter_options)) {
		$filter = 'usual';
	}

	$session->set('account_selector_filter', $filter);
73
74
75
76
77
78
79
80
81
82
83
84
85
86
elseif ($filter == 'all') {
	$tpl->assign('accounts', $accounts->listAll($targets));
}
elseif ($filter == 'any') {
	$tpl->assign('accounts', $accounts->listAll());
}
elseif ($year) {
	$tpl->assign('grouped_accounts', $year->listCommonGrouped($targets));
}
else {
	$tpl->assign('grouped_accounts', $accounts->listCommonGrouped($targets));
}

$tpl->display('acc/charts/accounts/selector.tpl');







|






76
77
78
79
80
81
82
83
84
85
86
87
88
89
elseif ($filter == 'all') {
	$tpl->assign('accounts', $accounts->listAll($targets));
}
elseif ($filter == 'any') {
	$tpl->assign('accounts', $accounts->listAll());
}
elseif ($year) {
	$tpl->assign('grouped_accounts', $year->listCommonAccountsGrouped($targets));
}
else {
	$tpl->assign('grouped_accounts', $accounts->listCommonGrouped($targets));
}

$tpl->display('acc/charts/accounts/selector.tpl');

Modified src/www/admin/static/scripts/selector.js from [e20909621c] to [eeca799734].

97
98
99
100
101
102
103

104

105
106
107
108
109
110
111
	current = available[idx];
	current.querySelector('button').focus();

	evt.preventDefault();
	return false;
});


buttons[0].focus();


var q = document.getElementById('lookup');

if (q) {
	q.addEventListener('keyup', (e) => {
		var query = new RegExp(RegExp.escape(normalizeString(q.value)), 'i');








>
|
>







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
	current = available[idx];
	current.querySelector('button').focus();

	evt.preventDefault();
	return false;
});

if (buttons[0]) {
	buttons[0].focus();
}

var q = document.getElementById('lookup');

if (q) {
	q.addEventListener('keyup', (e) => {
		var query = new RegExp(RegExp.escape(normalizeString(q.value)), 'i');