Overview
Comment:Transaction editing is working now
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev
Files: files | file ages | folders
SHA1: 27f81b84552092f339c5756480a87a9423cb0877
User & Date: bohwaz on 2020-10-02 23:37:50
Other Links: branch diff | manifest | tags
Context
2020-10-03
00:53
Years list check-in: 4495da48a1 user: bohwaz tags: dev
2020-10-02
23:37
Transaction editing is working now check-in: 27f81b8455 user: bohwaz tags: dev
2020-10-01
14:04
Refactor transaction form, add transaction editing check-in: f2c1ee8e78 user: bohwaz tags: dev
Changes

Modified src/include/lib/Garradin/Entities/Accounting/Transaction.php from [14ee05e6ca] to [3b1790ae44].

46
47
48
49
50
51
52

53
54
55
56
57
58
59
		'label'     => 'required|string|max:200',
		'notes'     => 'string|max:20000',
		'reference' => 'string|max:200',
		'date'      => 'required|date_format:d/m/Y',
	];

	protected $_lines;


	public function getLinesWithAccounts()
	{
		$em = EntityManager::getInstance(Line::class);
		return $em->DB()->get('SELECT a.*, b.label AS account_name, b.code AS account_code FROM ' . Line::TABLE  .' a INNER JOIN ' . Account::TABLE . ' b ON b.id = a.id_account WHERE a.id_transaction = ? ORDER BY a.id;', $this->id);
	}








>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
		'label'     => 'required|string|max:200',
		'notes'     => 'string|max:20000',
		'reference' => 'string|max:200',
		'date'      => 'required|date_format:d/m/Y',
	];

	protected $_lines;
	protected $_old_lines = [];

	public function getLinesWithAccounts()
	{
		$em = EntityManager::getInstance(Line::class);
		return $em->DB()->get('SELECT a.*, b.label AS account_name, b.code AS account_code FROM ' . Line::TABLE  .' a INNER JOIN ' . Account::TABLE . ' b ON b.id = a.id_account WHERE a.id_transaction = ? ORDER BY a.id;', $this->id);
	}

128
129
130
131
132
133
134





135
136
137
138
139
140
141
		}

		foreach ($this->_lines as &$line)
		{
			$line->id_transaction = $this->id();
			$line->save();
		}






		return true;
	}

	public function delete(): bool
	{
		if ($this->validated) {







>
>
>
>
>







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
		}

		foreach ($this->_lines as &$line)
		{
			$line->id_transaction = $this->id();
			$line->save();
		}

		foreach ($this->_old_lines as $line)
		{
			$line->delete();
		}

		return true;
	}

	public function delete(): bool
	{
		if ($this->validated) {
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
		}

		if (0 !== $total) {
			throw new ValidationException('Écriture non équilibrée : déséquilibre entre débits et crédits');
		}
	}

	public function importFromSimpleForm(int $chart_id, ?array $source = null): void
	{
		if (null === $source) {
			$source = $_POST;
		}

		if (empty($source['type'])) {
			throw new ValidationException('Type d\'écriture inconnu');







|







186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
		}

		if (0 !== $total) {
			throw new ValidationException('Écriture non équilibrée : déséquilibre entre débits et crédits');
		}
	}

	public function importFromNewForm(int $chart_id, ?array $source = null): void
	{
		if (null === $source) {
			$source = $_POST;
		}

		if (empty($source['type'])) {
			throw new ValidationException('Type d\'écriture inconnu');
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
























239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254

255
256

257
258
259

260
261
262
263
264
265
266
267
268
269
				'id_account'    => $to,
				'id_analytical' => !empty($source['id_analytical']) ? $source['id_analytical'] : null,
			]);
			$this->add($line);
		}
		else {
			foreach ($source['lines'] as $i => $line) {
				$line['id_account'] = @count($line['id_account']) ? key($line['id_account']) : null;

				if (!$line['id_account']) {
					throw new ValidationException('Numéro de compte invalide sur la ligne ' . ($i+1));
				}

				$line = (new Line)->import($line);
				$this->add($line);
			}
		}
	}

























	public function year()
	{
		return EntityManager::findOneById(Year::class, $this->id_year);
	}

	public function listFiles()
	{
		return Fichiers::listLinkedFiles(Fichiers::LIEN_COMPTA, $this->id());
	}

	public function linkToUser(int $user_id, ?int $service_id = null)
	{
		if (!$this->id()) {
			throw new \LogicException('Cannot link a non-saved transaction');
		}


		$db = EntityManager::getInstance(self::class)->DB();


		return $db->insert('acc_transactions_users', [
			'id_transaction' => $this->id(),

			'id_user'        => $user_id,
			'id_service'     => $service_id,
		]);
	}

	public function listLinkedUsers()
	{
		$db = EntityManager::getInstance(self::class)->DB();
		$identity_column = Config::getInstance()->get('champ_identite');
		$sql = sprintf('SELECT m.id, m.%s AS identity FROM membres m INNER JOIN acc_transactions_users l ON l.id_user = m.id WHERE l.id_transaction = ?;', $identity_column);







|










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











|

|
<
|
>

|
>

|
|
>
|
<
|







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

283
284
285
286
287
288
289
290
291
292

293
294
295
296
297
298
299
300
				'id_account'    => $to,
				'id_analytical' => !empty($source['id_analytical']) ? $source['id_analytical'] : null,
			]);
			$this->add($line);
		}
		else {
			foreach ($source['lines'] as $i => $line) {
				$line['id_account'] = @count($line['account']) ? key($line['account']) : null;

				if (!$line['id_account']) {
					throw new ValidationException('Numéro de compte invalide sur la ligne ' . ($i+1));
				}

				$line = (new Line)->import($line);
				$this->add($line);
			}
		}
	}

	public function importFromEditForm(?array $source = null): void
	{
		if (null === $source) {
			$source = $_POST;
		}

		$this->importForm();

		$this->_old_lines = $this->getLines();
		$this->_lines = [];

		foreach ($source['lines'] as $i => $line) {
			$line['id_account'] = @count($line['account']) ? key($line['account']) : null;

			if (!$line['id_account']) {
				var_dump($source); exit;
				throw new ValidationException('Numéro de compte invalide sur la ligne ' . ($i+1));
			}

			$line = (new Line)->import($line);
			$this->add($line);
		}
	}

	public function year()
	{
		return EntityManager::findOneById(Year::class, $this->id_year);
	}

	public function listFiles()
	{
		return Fichiers::listLinkedFiles(Fichiers::LIEN_COMPTA, $this->id());
	}

	public function updateLinkedUsers(array $users)
	{
		$db = EntityManager::getInstance(self::class)->DB();


		$db->begin();

		$sql = sprintf('DELETE FROM acc_transactions_users WHERE id_transaction = ? AND %s;', $db->where('id_user', 'NOT IN', $users));
		$db->preparedQuery($sql, $this->id());

		foreach ($users as $id) {
			$db->preparedQuery('INSERT OR IGNORE INTO acc_transactions_users (id_transaction, id_user) VALUES (?, ?);', $this->id(), $id);
		}


		$db->commit();
	}

	public function listLinkedUsers()
	{
		$db = EntityManager::getInstance(self::class)->DB();
		$identity_column = Config::getInstance()->get('champ_identite');
		$sql = sprintf('SELECT m.id, m.%s AS identity FROM membres m INNER JOIN acc_transactions_users l ON l.id_user = m.id WHERE l.id_transaction = ?;', $identity_column);

Modified src/templates/acc/accounts/selector.tpl from [72132eb911] to [bd29d0107e].

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
	<table class="accounts">
		<tbody>
		{foreach from=$accounts item="account"}
			<tr class="account-level-{$account.code|strlen}">
				<td>{$account.code}</td>
				<th>{$account.label}</th>
				<td class="actions">
					<button class="icn-btn" value="{$account.id}" data-label="{$account.code}" data-icon="&rarr;">Sélectionner</button>
					}
				</td>
			</tr>
		{/foreach}
		</tbody>
	</table>

{/if}







|
<







28
29
30
31
32
33
34
35

36
37
38
39
40
41
42
	<table class="accounts">
		<tbody>
		{foreach from=$accounts item="account"}
			<tr class="account-level-{$account.code|strlen}">
				<td>{$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>

{/if}

Modified src/templates/acc/transactions/_lines_form.tpl from [95d838e536] to [c5834d1e2e].

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
		</tr>
	</thead>
	<tbody>
	{foreach from=$lines key="k" item="line"}
		<tr>
			<th>
				{if isset($lines_accounts)}
					{input type="list" target="%sacc/accounts/selector.php?target=all"|args:$admin_url name="lines[account][]" value=$lines_accounts[$k]}
				{else}
					{input type="list" target="%sacc/accounts/selector.php?target=all"|args:$admin_url name="lines[account][]"}
				{/if}
			</th>
			<td>{input type="money" name="lines[debit][]" value=$line.debit size=5}</td>
			<td>{input type="money" name="lines[credit][]" value=$line.credit size=5}</td>
			<td>{input type="text" name="lines[reference][]" value=$line.reference size=10}</td>







|







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
		</tr>
	</thead>
	<tbody>
	{foreach from=$lines key="k" item="line"}
		<tr>
			<th>
				{if isset($lines_accounts)}
					{input type="list" target="%sacc/accounts/selector.php?target=all&fullname=1"|args:$admin_url name="lines[account][]" value=$lines_accounts[$k]}
				{else}
					{input type="list" target="%sacc/accounts/selector.php?target=all"|args:$admin_url name="lines[account][]"}
				{/if}
			</th>
			<td>{input type="money" name="lines[debit][]" value=$line.debit size=5}</td>
			<td>{input type="money" name="lines[credit][]" value=$line.credit size=5}</td>
			<td>{input type="text" name="lines[reference][]" value=$line.reference size=10}</td>

Modified src/templates/acc/transactions/details.tpl from [6ca0ac5041] to [7ee5310677].

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
		</aside>
	</dd>
	{foreachelse}
	<dd>-</dd>
	{/foreach}
</dl>

<table class="list multi">
	<thead>
		<tr>
			<th colspan="2">Comptes</th>
			<td>Débit</td>
			<td>Crédit</td>
			<td>Libellé</td>
			<td>Référence</td>







|







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
		</aside>
	</dd>
	{foreachelse}
	<dd>-</dd>
	{/foreach}
</dl>

<table class="list">
	<thead>
		<tr>
			<th colspan="2">Comptes</th>
			<td>Débit</td>
			<td>Crédit</td>
			<td>Libellé</td>
			<td>Référence</td>

Modified src/templates/admin/membres/selector.tpl from [908da99bf1] to [1b73c5c74d].

47
48
49
50
51
52
53


54
55
56
57
		if (evt.target.tagName && evt.target.tagName == 'BUTTON') {
			return;
		}

		e.querySelector('button').click();
	};
});


</script>
{/literal}

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







>
>




47
48
49
50
51
52
53
54
55
56
57
58
59
		if (evt.target.tagName && evt.target.tagName == 'BUTTON') {
			return;
		}

		e.querySelector('button').click();
	};
});

document.querySelector('input').focus();
</script>
{/literal}

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

Modified src/www/admin/acc/transactions/edit.php from [0cc65f8dd2] to [a41f081562].

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

namespace Garradin;

use Garradin\Accounting\Transactions;

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

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

$transaction = Transactions::get((int) qg('id'));

if (!$transaction) {
    throw new UserException('Cette écriture n\'existe pas');
}

$chart = $year->chart();
$accounts = $chart->accounts();





















































if (f('delete'))
{
    if ($form->check('acc_edit_' . $transaction->id))
    {
        try
        {
            $transaction->delete();
            Utils::redirect(ADMIN_URL . 'acc/');
        }
        catch (UserException $e)
        {
            $form->addError($e->getMessage());
        }
    }
}

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

$lines = $transaction->getLinesWithAccounts();
$lines_accounts = [];

foreach ($lines as $k => $line) {
    $lines_accounts[$k] = [$line->id_account => sprintf('%s - %s', $line->account_code, $line->account_name)];
}

$tpl->assign('lines', $lines);
$tpl->assign('lines_accounts', $lines_accounts);
$tpl->assign('analytical_accounts', ['' => '-- Aucun'] + $accounts->listAnalytical());
$tpl->assign('linked_users', $transaction->listLinkedUsersAssoc());

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













|





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

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








|








1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71







72
73
74

75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<?php

namespace Garradin;

use Garradin\Accounting\Transactions;

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

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

$transaction = Transactions::get((int) qg('id'));

if (!$transaction) {
	throw new UserException('Cette écriture n\'existe pas');
}

$chart = $year->chart();
$accounts = $chart->accounts();

$rules = [
	'lines' => 'array|required',
];

if (f('save') && $form->check('acc_edit_' . $transaction->id(), $rules)) {
	try {
		$lines = f('lines');
		$max = count($lines['label']);

		if ($max != count($lines['debit'])
			|| $max != count($lines['credit'])
			|| $max != count($lines['reference'])
			|| $max != count($lines['account']))
		{
			throw new UserException('Erreur dans les lignes de l\'écriture');
		}

		$out = [];

		// Reorder the POST data as a proper array
		for ($i = 0; $i < $max; $i++) {
			$out[] = [
				'debit'      => $lines['debit'][$i],
				'credit'     => $lines['credit'][$i],
				'reference'  => $lines['reference'][$i],
				'label'      => $lines['label'][$i],
				'account'    => $lines['account'][$i],
				'id_analytical' => !empty($lines['id_analytical'][$i]) ? $lines['id_analytical'][$i] : null,
			];
		}

		$_POST['type'] = 'advanced';
		$_POST['lines'] = $lines = $out;

		$transaction->importFromEditForm();
		$transaction->save();

		// Append file
		if (!empty($_FILES['file']['name'])) {
			$file = Fichiers::upload($_FILES['file']);
			$file->linkTo(Fichiers::LIEN_COMPTA, $transaction->id());
		}

		// Link members
		if (null !== f('users') && is_array(f('users'))) {
			$transaction->updateLinkedUsers(array_keys(f('users')));
		}
		else {
			// Remove all
			$transaction->updateLinkedUsers([]);
		}








		Utils::redirect(ADMIN_URL . 'acc/transactions/details.php?id=' . $transaction->id());
	}
	catch (UserException $e) {

		$form->addError($e->getMessage());
	}

}

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

$lines = $transaction->getLinesWithAccounts();
$lines_accounts = [];

foreach ($lines as $k => $line) {
	$lines_accounts[$k] = [$line->id_account => sprintf('%s - %s', $line->account_code, $line->account_name)];
}

$tpl->assign('lines', $lines);
$tpl->assign('lines_accounts', $lines_accounts);
$tpl->assign('analytical_accounts', ['' => '-- Aucun'] + $accounts->listAnalytical());
$tpl->assign('linked_users', $transaction->listLinkedUsersAssoc());

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

Modified src/www/admin/acc/transactions/new.php from [14490d4037] to [70fe30ac8b].

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29




























30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
$chart = $year->chart();
$accounts = $chart->accounts();

$transaction = new Transaction;
$lines = [[], []];

if (f('save') && $form->check('acc_transaction_new')) {
    try {
        // Advanced transaction: handle lines
        if (f('type') == 'advanced' && $lines = f('lines'))
        {
            $max = count($lines['label']);

            if ($max != count($lines['debit'])
                || $max != count($lines['credit'])
                || $max != count($lines['reference'])
                || $max != count($lines['account']))
            {
                throw new UserException('Erreur dans les lignes de l\'écriture');
            }





























            $out = [];

            // Reorder the POST data as a proper array
            for ($i = 0; $i < $max; $i++) {
                $out[] = [
                    'debit'      => $lines['debit'][$i],
                    'credit'     => $lines['credit'][$i],
                    'reference'  => $lines['reference'][$i],
                    'label'      => $lines['label'][$i],
                    'account'    => $lines['account'][$i],
                    'id_analytical' => isset($lines['id_analytical'][$i]) ? $lines['id_analytical'][$i] : null,
                ];
            }

            $_POST['lines'] = $lines = $out;
        }

        $transaction->id_year = $year->id();
        $transaction->importFromSimpleForm($chart->id());
        $transaction->id_creator = $session->getUser()->id;
        $transaction->save();

        // Append file
        if (!empty($_FILES['file']['name'])) {
            $file = Fichiers::upload($_FILES['file']);
            $file->linkTo(Fichiers::LIEN_COMPTA, $transaction->id());
        }

        // Link members
        if (!empty($_POST['users']) && is_array($_POST['users'])) {
            foreach ($_POST['users'] as $id => $label) {
                $transaction->linkToUser((int) $id);
            }
        }

        Utils::redirect(Utils::getSelfURL(false) . '?ok=' . $transaction->id());
    }
    catch (UserException $e) {
        $form->addError($e->getMessage());
    }
}

$tpl->assign('date', $session->get('context_compta_date') ?: false);
$tpl->assign('ok', (int) qg('ok'));

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

$tpl->assign('analytical_accounts', ['' => '-- Aucun'] + $accounts->listAnalytical());
$tpl->display('acc/transactions/new.tpl');







|
|
|
|
|

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

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









10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58




























59
60

61
62
63

64
65
66
67
68
69
70
71
72
73
74
75
76
77
$chart = $year->chart();
$accounts = $chart->accounts();

$transaction = new Transaction;
$lines = [[], []];

if (f('save') && $form->check('acc_transaction_new')) {
	try {
		// Advanced transaction: handle lines
		if (f('type') == 'advanced' && $lines = f('lines'))
		{
			$max = count($lines['label']);

			if ($max != count($lines['debit'])
				|| $max != count($lines['credit'])
				|| $max != count($lines['reference'])
				|| $max != count($lines['account']))
			{
				throw new UserException('Erreur dans les lignes de l\'écriture');
			}

			$out = [];

			// Reorder the POST data as a proper array
			for ($i = 0; $i < $max; $i++) {
				$out[] = [
					'debit'      => $lines['debit'][$i],
					'credit'     => $lines['credit'][$i],
					'reference'  => $lines['reference'][$i],
					'label'      => $lines['label'][$i],
					'account'    => $lines['account'][$i],
					'id_analytical' => !empty($lines['id_analytical'][$i]) ? $lines['id_analytical'][$i] : null,
				];
			}

			$_POST['lines'] = $lines = $out;
		}

		$transaction->id_year = $year->id();
		$transaction->importFromNewForm($chart->id());
		$transaction->id_creator = $session->getUser()->id;
		$transaction->save();

		// Append file
		if (!empty($_FILES['file']['name'])) {
			$file = Fichiers::upload($_FILES['file']);
			$file->linkTo(Fichiers::LIEN_COMPTA, $transaction->id());
		}





























		 // Link members
		if (null !== f('users') && is_array(f('users'))) {

			$transaction->updateLinkedUsers(array_keys(f('users')));
		}


		Utils::redirect(Utils::getSelfURL(false) . '?ok=' . $transaction->id());
	}
	catch (UserException $e) {
		$form->addError($e->getMessage());
	}
}

$tpl->assign('date', $session->get('context_compta_date') ?: false);
$tpl->assign('ok', (int) qg('ok'));

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

$tpl->assign('analytical_accounts', ['' => '-- Aucun'] + $accounts->listAnalytical());
$tpl->display('acc/transactions/new.tpl');

Added src/www/admin/static/scripts/accounting.js version [d2536887ec].





























































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
function initTransactionForm() {
	// Advanced transaction: line management
	var lines = $('.transaction-lines tbody tr');

	function initLine(row) {
		row.querySelector('button[name="remove_line"]').onclick = () => {
			var count = $('.transaction-lines tbody tr').length;

			if (count <= 2) {
				alert("Il n'est pas possible d'avoir moins de deux lignes dans une écriture.");
				return false;
			}

			row.parentNode.removeChild(row);
			updateTotals();
		};

		// Update totals and disable other amount input
		var inputs = row.querySelectorAll('input.money');

		inputs.forEach((i, k) => {
			i.onkeyup = (e) => {
				if (!e.key.match(/^([0-9,.]|Separator|Backspace|Delete)$/i)) {
					return true;
				}

				if (i.readOnly) {
					i.value = e.key.match(/[0-9.,]/) ? e.key : '0';
					i.readOnly = false;
					inputs[+!k].readOnly = true;
					inputs[+!k].value = '0';
				}

				updateTotals();
			};

			if (+i.value == 0) {
				i.readOnly = true;
				i.value = '0';
			}
		});
	}

	lines.forEach(initLine);

	function updateTotals() {
		var amounts = $('.transaction-lines tbody input.money');
		var debit = credit = 0;

		amounts.forEach((i) => {
			var v = i.value.replace(/[^0-9.,]/, '');
			if (v.length == 0) return;

			v = v.split(/[,.]/);
			var d = v.length == 2 ? v[1] : '0';
			v = v[0] + (d + '00').substr(0, 2);
			v = parseInt(v, 10);

			if (i.name.match(/debit/)) {
				debit += v;
			}
			else {
				credit += v;
			}
		});

		$('#lines_message').innerHTML = (debit === credit) ? '' : '<span class="alert">Écriture non équilibrée</span>';

		debit = debit ? debit + '' : '000';
		credit = credit ? credit + '' : '000';
		$('#f_debit_total').value = (debit.substr(0, debit.length-2) || '0') + ',' + debit.substr(-2);
		$('#f_credit_total').value = (credit.substr(0, credit.length-2) || '0') + ',' + credit.substr(-2);
	}

	// Add row button
	$('.transaction-lines tfoot button')[0].onclick = () => {
		var line = $('.transaction-lines tbody tr')[0];
		var n = line.cloneNode(true);
		n.querySelectorAll('input').forEach((e) => {
			e.value = '';
		});
		n.querySelector('.input-list .label').innerHTML = '';
		var b = n.querySelector('.input-list button');
		b.onclick = () => {
			g.current_list_input = b.parentNode;
			g.openFrameDialog(b.value);
			return false;
		};
		initLine(n);
		line.parentNode.appendChild(n);
	};

	updateTotals();
}

Modified src/www/admin/static/scripts/global.js from [c2c45e5176] to [ef372ef71b].

232
233
234
235
236
237
238



239
240
241
242
243
244
245
			var btn = document.createElement('button');
			btn.className = 'icn-btn';
			btn.type = 'button';
			btn.setAttribute('data-icon', '✘');
			btn.onclick = () => span.parentNode.removeChild(span);
			span.appendChild(btn);
		}




		i.appendChild(span);
		g.closeDialog();
		i.firstChild.focus();
	};

	// Sélecteurs de listes







>
>
>







232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
			var btn = document.createElement('button');
			btn.className = 'icn-btn';
			btn.type = 'button';
			btn.setAttribute('data-icon', '✘');
			btn.onclick = () => span.parentNode.removeChild(span);
			span.appendChild(btn);
		}
		else {
			i.removeChild(i.querySelector('span'));
		}

		i.appendChild(span);
		g.closeDialog();
		i.firstChild.focus();
	};

	// Sélecteurs de listes