Overview
Comment:Improve account selector
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | accounts_refactor
Files: files | file ages | folders
SHA3-256: d0e4ec3dcd91e973c2938f0d87efc2b99168b15b0d9b1affede37c3e0c644adc
User & Date: bohwaz on 2022-11-01 12:59:48
Other Links: branch diff | manifest | tags
Context
2022-11-02
18:20
Set default value check-in: eff8d70378 user: bohwaz tags: accounts_refactor
2022-11-01
12:59
Improve account selector check-in: d0e4ec3dcd user: bohwaz tags: accounts_refactor
02:10
Display account position when editing check-in: 3be1776397 user: bohwaz tags: accounts_refactor
Changes

Modified src/include/lib/Garradin/Accounting/Accounts.php from [a87f755f70] to [a8c7f0f408].

90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105













106
107
108
109
110
111
112
			],
		];

		$tables = 'acc_accounts';
		$conditions = 'id_chart = ' . $this->chart_id;

		if (!empty($types)) {

			$conditions .= ' AND ' . DB::getInstance()->where('type', $types);
		}

		$list = new DynamicList($columns, $tables, $conditions);
		$list->orderBy('code', false);
		$list->setPageSize(null);

		return $list;
	}














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

	/**







>









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







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

		$tables = 'acc_accounts';
		$conditions = 'id_chart = ' . $this->chart_id;

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

		$list = new DynamicList($columns, $tables, $conditions);
		$list->orderBy('code', false);
		$list->setPageSize(null);

		return $list;
	}

	public function listAll(array $types = null): array
	{
		$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);
	}

	/**
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
		}
	}

	/**
	 * List common accounts, grouped by type
	 * @return array
	 */
	public function listCommonGrouped(): array
	{

		$types = Account::COMMON_TYPES;


		$out = [];

		foreach ($types as $type) {
			$out[$type] = (object) [
				'label'    => Account::TYPES_NAMES[$type],
				'type'     => $type,
				'accounts' => [],
			];
		}

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

		$query = $this->em->iterate($sql);







|

>
|
>
>












|







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

	/**
	 * List common accounts, grouped by type
	 * @return array
	 */
	public function listCommonGrouped(array $types = null): array
	{
		if (null === $types) {
			$types = Account::COMMON_TYPES;
		}

		$out = [];

		foreach ($types as $type) {
			$out[$type] = (object) [
				'label'    => Account::TYPES_NAMES[$type],
				'type'     => $type,
				'accounts' => [],
			];
		}

		$sql = sprintf('SELECT a.* FROM @TABLE a
			LEFT JOIN acc_transactions_lines b ON b.id_account = a.id
			WHERE a.id_chart = %d AND a.%s AND (a.bookmark = 1 OR b.id IS NOT NULL)
			GROUP BY a.id
			ORDER BY type, code COLLATE NOCASE;',
			$this->chart_id,
			$this->em->DB()->where('type', $types)
		);

		$query = $this->em->iterate($sql);

Modified src/include/lib/Garradin/Entities/Accounting/Account.php from [e6c5ae3db3] to [58fd50cc23].

655
656
657
658
659
660
661
662












		if (isset($source['code_value'], $source['code_base'])) {
			$source['code'] = trim($source['code_base']) . trim($source['code_value']);
		}

		parent::importForm($source);
	}
}


















|
>
>
>
>
>
>
>
>
>
>
>
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673

		if (isset($source['code_value'], $source['code_base'])) {
			$source['code'] = trim($source['code_base']) . trim($source['code_value']);
		}

		parent::importForm($source);
	}

	public function level(): int
	{
		$level = strlen($this->code);

		if ($level > 6) {
			$level = 6;
		}

		return $level;
	}
}

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

168
169
170
171
172
173
174
175













































	public function label_years()
	{
		$start = Utils::date_fr($this->start_date, 'Y');
		$end = Utils::date_fr($this->end_date, 'Y');
		return $start == $end ? $start : sprintf('%s-%s', $start, substr($end, -2));
	}
}



















































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

	public function label_years()
	{
		$start = Utils::date_fr($this->start_date, 'Y');
		$end = Utils::date_fr($this->end_date, 'Y');
		return $start == $end ? $start : sprintf('%s-%s', $start, substr($end, -2));
	}


	/**
	 * List common accounts used in this year, grouped by type
	 * @return array
	 */
	public function listCommonAccountsGrouped(array $types = null): array
	{
		if (null === $types) {
			$types = Account::COMMON_TYPES;
		}

		$out = [];

		foreach ($types as $type) {
			$out[$type] = (object) [
				'label'    => Account::TYPES_NAMES[$type],
				'type'     => $type,
				'accounts' => [],
			];
		}

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

}

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

1
2
3
4
5
6
7
8
{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}
|







1
2
3
4
5
6
7
8
{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}

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

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
		</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><label>{input type="checkbox" name="typed_only" value=0 default=0 default=$all} N'afficher que les comptes favoris</label></p>
	</header>

	{if isset($grouped_accounts)}
		<?php $index = 1; ?>
		{foreach from=$grouped_accounts item="group"}
			<h2 class="ruler">{$group.label}</h2>

			<table class="list">
				<tbody>
				{foreach from=$group.accounts item="account"}
					<tr data-idx="{$index}">

						<td>{$account.code}</td>
						<th>{$account.label}</th>
						<td class="desc">{$account.description}</td>
						<td class="actions">
							<button class="icn-btn" value="{$account.id}" data-label="{$account.code} — {$account.label}" data-icon="&rarr;">Sélectionner</button>
						</td>
					</tr>
					<?php $index++; ?>
				{/foreach}
				</tbody>
			</table>
		{/foreach}

	{else}

		<table class="accounts">
			<tbody>
			{foreach from=$accounts item="account"}
				<tr data-idx="{$iteration}" class="account-level-{$account.code|strlen}">

					<td>{$account.code}</td>
					<th>{$account.label}</th>
					<td>
					{if $account.type}
						{icon shape="star"} <?=Entities\Accounting\Account::TYPES_NAMES[$account->type]?>
					{/if}
					</td>
					<td class="actions">
						<button class="icn-btn" value="{$account.id}" data-label="{$account.code} — {$account.label}" data-icon="&rarr;">Sélectionner</button>
					</td>
				</tr>
			{/foreach}
			</tbody>
		</table>







|










|
>
|














|


|
>
|

<
<
<
<
<







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
		</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; ?>
		{foreach from=$grouped_accounts item="group"}
			<h2 class="ruler">{$group.label}</h2>

			<table class="list">
				<tbody>
				{foreach from=$group.accounts item="account"}
					<tr data-idx="{$index}" class="account">
						<td class="bookmark">{if $account.bookmark}{icon shape="star" title="Compte favori"}{/if}</td>
						<td class="num">{$account.code}</td>
						<th>{$account.label}</th>
						<td class="desc">{$account.description}</td>
						<td class="actions">
							<button class="icn-btn" value="{$account.id}" data-label="{$account.code} — {$account.label}" data-icon="&rarr;">Sélectionner</button>
						</td>
					</tr>
					<?php $index++; ?>
				{/foreach}
				</tbody>
			</table>
		{/foreach}

	{else}

		<table class="list">
			<tbody>
			{foreach from=$accounts item="account"}
				<tr data-idx="{$iteration}" class="account account-level-{$account->level()}">
					<td class="bookmark">{if $account.bookmark}{icon shape="star" title="Compte favori"}{/if}</td>
					<td class="num">{$account.code}</td>
					<th>{$account.label}</th>





					<td class="actions">
						<button class="icn-btn" value="{$account.id}" data-label="{$account.code} — {$account.label}" data-icon="&rarr;">Sélectionner</button>
					</td>
				</tr>
			{/foreach}
			</tbody>
		</table>

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

21
22
23
24
25
26
27

28
29
30
31
32
33
34
if ($chart->archived) {
	throw new UserException("Il n'est pas possible de modifier un plan comptable archivé.");
}

$accounts = $chart->accounts();

$account = new Account;

$account->id_chart = $chart->id();

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

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







>







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
if ($chart->archived) {
	throw new UserException("Il n'est pas possible de modifier un plan comptable archivé.");
}

$accounts = $chart->accounts();

$account = new Account;
$account->bookmark = true;
$account->id_chart = $chart->id();

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

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

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

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
$targets = qg('targets');
$targets = $targets ? explode(':', $targets) : [];
$chart = (int) qg('chart') ?: null;

$targets = array_map('intval', $targets);
$targets_str = implode(':', $targets);


$all = qg('all');







if (null !== $all || !$targets) {



	$all = true;
	$session->set('account_selector_all', (bool) $all);
}

$all = (bool) $session->get('account_selector_all');


// Cache the page until the charts have changed
$last_change = Config::getInstance()->get('last_chart_change') ?: time();
$hash = sha1($targets_str . $chart . $last_change . '=' . $all);

// Exit if there's no need to reload
Utils::HTTPCache($hash, null, 10);

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

	if ($year) {
		$chart = $year->chart();
	}
}
elseif ($current_year) {
	$chart = $current_year->chart();

}

if (!$chart) {
	throw new UserException('Aucun exercice ouvert disponible');
}

$accounts = $chart->accounts();

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

if (!count($targets)) {
	$tpl->assign('accounts', !$all ? $accounts->listCommonTypes() : $accounts->listAll());
}
elseif ($all) {
	$tpl->assign('accounts', $accounts->listAll($targets));
}






else {
	$tpl->assign('grouped_accounts', $accounts->listCommonGrouped($targets));
}

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

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







>
|
>
>
>
>
>
>

|
>
>
>
|
|


|
>



|
















>








|


|

|


>
>
>
>
>
>




<
<

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
$targets = qg('targets');
$targets = $targets ? explode(':', $targets) : [];
$chart = (int) qg('chart') ?: null;

$targets = array_map('intval', $targets);
$targets_str = implode(':', $targets);

$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);
}

$filter = $session->get('account_selector_filter') ?? 'usual';


// Cache the page until the charts have changed
$last_change = Config::getInstance()->get('last_chart_change') ?: time();
$hash = sha1($targets_str . $chart . $last_change . '=' . $filter);

// Exit if there's no need to reload
Utils::HTTPCache($hash, null, 10);

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

	if ($year) {
		$chart = $year->chart();
	}
}
elseif ($current_year) {
	$chart = $current_year->chart();
	$year = $current_year;
}

if (!$chart) {
	throw new UserException('Aucun exercice ouvert disponible');
}

$accounts = $chart->accounts();

$tpl->assign(compact('chart', 'targets', 'targets_str', 'filter_options', 'filter'));

if (!count($targets)) {
	$tpl->assign('accounts', $filter != 'all' ? $accounts->listCommonTypes() : $accounts->listAll());
}
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');

Modified src/www/admin/static/scripts/global.js from [6a73cef81a] to [8edf83b061].

459
460
461
462
463
464
465





466
467
468
469
470
471
472

				if (!type) {
					let url = e.href + (e.href.indexOf('?') > 0 ? '&' : '?') + '_dialog';

					if (m = e.getAttribute('target').match(/_dialog=(.*)/)) {
						url += '=' + m[1];
					}






					g.openFrameDialog(url, e.getAttribute('data-dialog-height') ? '90%' : 'auto');
					return false;
				}

				if (type.match(/^image\//)) {
					var i = document.createElement('img');







>
>
>
>
>







459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477

				if (!type) {
					let url = e.href + (e.href.indexOf('?') > 0 ? '&' : '?') + '_dialog';

					if (m = e.getAttribute('target').match(/_dialog=(.*)/)) {
						url += '=' + m[1];
					}

					if (location.href.match(/_dialog/)) {
						location.href = url;
						return false;
					}

					g.openFrameDialog(url, e.getAttribute('data-dialog-height') ? '90%' : 'auto');
					return false;
				}

				if (type.match(/^image\//)) {
					var i = document.createElement('img');

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

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

		return false;
	});

	q.focus();
}

var o = document.getElementById('f_typed_only_0');

if (o) {
	o.onchange = () => {
		let s = new URLSearchParams(window.location.search);
		s.set("all", o.checked ? 0 : 1);
		window.location.search = s.toString();
	};
}







|




|



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

		return false;
	});

	q.focus();
}

var o = document.getElementById('f_filter');

if (o) {
	o.onchange = () => {
		let s = new URLSearchParams(window.location.search);
		s.set("filter", o.value);
		window.location.search = s.toString();
	};
}

Modified src/www/admin/static/styles/03-forms.css from [da6a895d27] to [ea8a24e1d1].

710
711
712
713
714
715
716









717
718
719
720
721
722
723
    float: right;
    margin: 0;
}

.selector header h2 input {
    width: calc(100% - 1em);
}










@media screen and (max-width: 1279px) {
    #queryBuilder table tr {
        display: flex;
        flex-wrap: wrap;
        padding: .5em 0;
        margin-left: 6rem;







>
>
>
>
>
>
>
>
>







710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
    float: right;
    margin: 0;
}

.selector header h2 input {
    width: calc(100% - 1em);
}

.selector select {
    font-size: .9em;
    margin: .2em 0;
    padding: .2em;
    border-color: var(--gLightBorderColor);
    cursor: pointer;
    width: auto;
}

@media screen and (max-width: 1279px) {
    #queryBuilder table tr {
        display: flex;
        flex-wrap: wrap;
        padding: .5em 0;
        margin-left: 6rem;

Modified src/www/admin/static/styles/10-accounting.css from [96d5f10998] to [9b52cf3446].

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
    margin: 1rem;
}

.year-infos .graphs.small figure img {
    max-width: 500px;
}

tr.account td.num { font-family: monospace; text-align: left; }
table tr.account th { font-weight: normal; }
tr.account-level-1 th { font-size: 1.6em; }
tr.account-level-2 th { padding-left: 1em; font-size: 1.3em; }
tr.account-level-3 th { padding-left: 2em; }
tr.account-level-4 th { padding-left: 3em; }
tr.account-level-5 th { padding-left: 4em; }
tr.account-level-6 th { padding-left: 5em; }
tr.account button { opacity: 0.6; transition: opacity .3s; width: 8em;}
tr.account button[value="1"] { opacity: 1; }



table.projects tr.title p.help { font-weight: normal; text-align: center; }

.attachments {
    text-align: center;
}

.attachments .files-list {
    justify-content: center;
}







|







|

>
>










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
    margin: 1rem;
}

.year-infos .graphs.small figure img {
    max-width: 500px;
}

tr.account td.num { font-family: monospace; text-align: left; width: 8%;}
table tr.account th { font-weight: normal; }
tr.account-level-1 th { font-size: 1.6em; }
tr.account-level-2 th { padding-left: 1em; font-size: 1.3em; }
tr.account-level-3 th { padding-left: 2em; }
tr.account-level-4 th { padding-left: 3em; }
tr.account-level-5 th { padding-left: 4em; }
tr.account-level-6 th { padding-left: 5em; }
tr.account button[name*="bookmark"] { opacity: 0.6; transition: opacity .3s; width: 8em;}
tr.account button[value="1"] { opacity: 1; }
tr.account td.bookmark { width: 2%; }
tr.account td.bookmark b { color: var(--gBorderColor); }

table.projects tr.title p.help { font-weight: normal; text-align: center; }

.attachments {
    text-align: center;
}

.attachments .files-list {
    justify-content: center;
}