Overview
Comment:Fix: issues with decimals, thanks to @jullien
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | dev | 1.0.0-alpha5
Files: files | file ages | folders
SHA1: 86e78f9a068d12d92ca8b36d1d6745cede46a7e4
User & Date: bohwaz on 2020-11-08 23:26:17
Other Links: branch diff | manifest | tags
Context
2020-11-09
00:05
Fix small issues with reconcile check-in: 1d53639dcc user: bohwaz tags: dev
2020-11-08
23:26
Fix: issues with decimals, thanks to @jullien check-in: 86e78f9a06 user: bohwaz tags: dev, 1.0.0-alpha5
19:16
Implement "my services" page check-in: 832ba3f247 user: bohwaz tags: dev, 1.0.0-alpha5
Changes

Modified src/include/lib/Garradin/Accounting/Years.php from [50248f541d] to [d694cb65c4].

17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
	static public function getCurrentOpenYear()
	{
		return EntityManager::findOne(Year::class, 'SELECT * FROM @TABLE WHERE closed = 0 ORDER BY start_date LIMIT 1;');
	}

	static public function listOpen()
	{
		$em = EntityManager::getInstance(Year::class);

		return $em->all('SELECT * FROM @TABLE WHERE closed = 0 ORDER BY end_date;');
	}

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








|
>
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
	static public function getCurrentOpenYear()
	{
		return EntityManager::findOne(Year::class, 'SELECT * FROM @TABLE WHERE closed = 0 ORDER BY start_date LIMIT 1;');
	}

	static public function listOpen()
	{
		$db = EntityManager::getInstance(Year::class)->DB();
		return $db->get('SELECT *, (SELECT 1 FROM acc_transactions WHERE id_year = acc_years.id LIMIT 1) AS has_transactions
			FROM acc_years WHERE closed = 0 ORDER BY end_date;');
	}

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

Modified src/include/lib/Garradin/Entity.php from [32e7c887ca] to [7b7a325745].

36
37
38
39
40
41
42



43
44
45
46
47
48
49
		if ($type == 'date') {
			if (preg_match('!^\d{2}/\d{2}/\d{2}$!', $value)) {
				return \DateTime::createFromFormat('d/m/y', $value);
			}
			elseif (preg_match('!^\d{2}/\d{2}/\d{4}$!', $value)) {
				return \DateTime::createFromFormat('d/m/Y', $value);
			}



		}
		else {
			return parent::filterUserValue($type, $value, $key);
		}
	}

	protected function assert(bool $test, string $message = null): void







>
>
>







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
		if ($type == 'date') {
			if (preg_match('!^\d{2}/\d{2}/\d{2}$!', $value)) {
				return \DateTime::createFromFormat('d/m/y', $value);
			}
			elseif (preg_match('!^\d{2}/\d{2}/\d{4}$!', $value)) {
				return \DateTime::createFromFormat('d/m/Y', $value);
			}
			else {
				throw new ValidationException('Format de date invalide (merci d\'utiliser le format) JJ/MM/AAAA) : ' . $value);
			}
		}
		else {
			return parent::filterUserValue($type, $value, $key);
		}
	}

	protected function assert(bool $test, string $message = null): void

Modified src/include/lib/Garradin/Utils.php from [e1571e929f] to [a268cc4010].

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
130
131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
146
147
148
149

        return true;
    }

    static public function moneyToInteger($value)
    {
        if (trim($value) === '') {
            return null;
        }

        if (!preg_match('/^(\d+)(?:[,.](\d{1,2}))?$/', $value, $match)) {
            throw new UserException('Le format du montant est invalide. Format accepté, exemple : 142,02');
        }

        $value = $match[1] . str_pad((int)@$match[2], 2, '0', STR_PAD_RIGHT);
................................................................................
    }

    static public function money_format($number, string $dec_point = ',', string $thousands_sep = ' ', $zero_if_empty = true): string {
        if ($number == 0) {
            return $zero_if_empty ? '0' : '0,00';
        }


        $number = (int) $number;

        $decimals = substr($number, -2);
        $number = (int) substr($number, 0, -2);

        return sprintf('%s%s%s', number_format($number, 0, $dec_point, $thousands_sep), $dec_point, $decimals);
    }

    static public function getRequestURI()
    {
        if (!empty($_SERVER['REQUEST_URI']))
            return $_SERVER['REQUEST_URI'];
        else







|







 







>
|

|


|







113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

        return true;
    }

    static public function moneyToInteger($value)
    {
        if (trim($value) === '') {
            return 0;
        }

        if (!preg_match('/^(\d+)(?:[,.](\d{1,2}))?$/', $value, $match)) {
            throw new UserException('Le format du montant est invalide. Format accepté, exemple : 142,02');
        }

        $value = $match[1] . str_pad((int)@$match[2], 2, '0', STR_PAD_RIGHT);
................................................................................
    }

    static public function money_format($number, string $dec_point = ',', string $thousands_sep = ' ', $zero_if_empty = true): string {
        if ($number == 0) {
            return $zero_if_empty ? '0' : '0,00';
        }

        $sign = $number < 0 ? '-' : '';
        $number = abs((int) $number);

        $decimals = substr('0' . $number, -2);
        $number = (int) substr($number, 0, -2);

        return sprintf('%s%s%s%s', $sign, number_format($number, 0, $dec_point, $thousands_sep), $dec_point, $decimals);
    }

    static public function getRequestURI()
    {
        if (!empty($_SERVER['REQUEST_URI']))
            return $_SERVER['REQUEST_URI'];
        else

Modified src/templates/acc/index.tpl from [804c83a943] to [e40c179f0d].

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
			<li><a href="{$admin_url}acc/search.php?year={$year.id}"><strong>Recherche</strong></a></li>
			{if $session->canAccess('compta', Membres::DROIT_ADMIN)}
				<li><a href="{$admin_url}acc/years/import.php?id={$year.id}">Import / export</a></li>
			{/if}
		</ul>
	</nav>


	<section class="graphs">
		{foreach from=$graphs key="url" item="label"}
		<figure>
			<img src="{$url|args:$year.id}" alt="" />
			<figcaption>{$label}</figcaption>
		</figure>
		{/foreach}
	</section>

</section>


{foreachelse}
	<p class="block alert">
		Il n'y a aucun exercice ouvert en cours.<br />
		{linkbutton label="Ouvrir un nouvel exercice" shape="plus" href="acc/years/new.php"}
	</p>
{/foreach}

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







>








>











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
			<li><a href="{$admin_url}acc/search.php?year={$year.id}"><strong>Recherche</strong></a></li>
			{if $session->canAccess('compta', Membres::DROIT_ADMIN)}
				<li><a href="{$admin_url}acc/years/import.php?id={$year.id}">Import / export</a></li>
			{/if}
		</ul>
	</nav>

	{if $year.has_transactions}
	<section class="graphs">
		{foreach from=$graphs key="url" item="label"}
		<figure>
			<img src="{$url|args:$year.id}" alt="" />
			<figcaption>{$label}</figcaption>
		</figure>
		{/foreach}
	</section>
	{/if}
</section>


{foreachelse}
	<p class="block alert">
		Il n'y a aucun exercice ouvert en cours.<br />
		{linkbutton label="Ouvrir un nouvel exercice" shape="plus" href="acc/years/new.php"}
	</p>
{/foreach}

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

Modified src/templates/acc/transactions/_lines_form.tpl from [ed229d73ee] to [09691f99cf].

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
assert(is_array($analytical_accounts));
assert(!isset($lines_accounts) || is_array($lines_accounts));
?>

<table class="list transaction-lines">
	<thead>
		<tr>
			<th>Compte</th>
			<td>Débit</td>
			<td>Crédit</td>
			<td>Réf. ligne</td>
			<td>Libellé ligne</td>
			{if count($analytical_accounts) > 0}
				<td>Analytique</td>
			{/if}
			<td></td>
		</tr>
	</thead>
	<tbody>
	{foreach from=$lines key="k" item="line"}
		<tr>
			<th>
				{if isset($lines_accounts)}
					{input type="list" target="acc/charts/accounts/selector.php?chart=%d"|args:$chart_id name="lines[account][]" default=$lines_accounts[$k]}
				{else}
					{input type="list" target="acc/charts/accounts/selector.php?chart=%d"|args:$chart_id name="lines[account][]"}
				{/if}
			</th>
			<td class="money">{input type="money" name="lines[debit][]" default=$line.debit size=5}</td>
			<td class="money">{input type="money" name="lines[credit][]" default=$line.credit size=5}</td>
			<td>{input type="text" name="lines[reference][]" default=$line.reference size=10}</td>
			<td>{input type="text" name="lines[label][]" default=$line.label}</td>
			{if count($analytical_accounts) > 0}
				<td>{input default=$line.id_analytical type="select" name="lines[id_analytical][]" options=$analytical_accounts}</td>
			{/if}







|













|
<
|
<
<
<
|







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
assert(is_array($analytical_accounts));
assert(!isset($lines_accounts) || is_array($lines_accounts));
?>

<table class="list transaction-lines">
	<thead>
		<tr>
			<td>Compte</td>
			<td>Débit</td>
			<td>Crédit</td>
			<td>Réf. ligne</td>
			<td>Libellé ligne</td>
			{if count($analytical_accounts) > 0}
				<td>Analytique</td>
			{/if}
			<td></td>
		</tr>
	</thead>
	<tbody>
	{foreach from=$lines key="k" item="line"}
		<tr>
			<td>

				{input type="list" target="acc/charts/accounts/selector.php?chart=%d"|args:$chart_id name="lines[account][]" default=$line.account}



			</td>
			<td class="money">{input type="money" name="lines[debit][]" default=$line.debit size=5}</td>
			<td class="money">{input type="money" name="lines[credit][]" default=$line.credit size=5}</td>
			<td>{input type="text" name="lines[reference][]" default=$line.reference size=10}</td>
			<td>{input type="text" name="lines[label][]" default=$line.label}</td>
			{if count($analytical_accounts) > 0}
				<td>{input default=$line.id_analytical type="select" name="lines[id_analytical][]" options=$analytical_accounts}</td>
			{/if}

Modified src/www/admin/acc/index.php from [26b2d4ae1a] to [f1d40ee231].

4
5
6
7
8
9
10
11
12
13
14
15
16
17
use Garradin\Accounting\Years;
use Garradin\Accounting\Graph;

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

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

$years = new Years;

$tpl->assign('graphs', Graph::URL_LIST);

$tpl->assign('years', $years->listOpen());

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







<
<


|


4
5
6
7
8
9
10


11
12
13
14
15
use Garradin\Accounting\Years;
use Garradin\Accounting\Graph;

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

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



$tpl->assign('graphs', Graph::URL_LIST);

$tpl->assign('years', Years::listOpen());

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

Modified src/www/admin/acc/transactions/edit.php from [ebef420d95] to [6970179df1].

61
62
63
64
65
66
67









68
69
70
71
72

73
74
75
76
77
78
79
80
	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');







>
>
>
>
>
>
>
>
>
|
<

|
|
>



<




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
	catch (UserException $e) {
		$form->addError($e->getMessage());
	}
}

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

if (!empty($_POST['lines']) && is_array($_POST['lines'])) {
	$lines = Utils::array_transpose($_POST['lines']);

	foreach ($lines as &$line) {
		$line['credit'] = Utils::moneyToInteger($line['credit']);
		$line['debit'] = Utils::moneyToInteger($line['debit']);
	}
}
else {
	$lines = $transaction->getLinesWithAccounts();


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

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

$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 [1707c5a857] to [e1558cb4be].

38
39
40
41
42
43
44








45
46
47
48
49
50
51

	$transaction->type = Transaction::getTypeFromAccountType($account->type);
	$key = sprintf('account_%d_%d', $transaction->type, 0);

	if (!isset($_POST[$key])) {
		$_POST[$key] = [$account->id => sprintf('%s — %s', $account->code, $account->label)];
	}








}

if (f('save') && $form->check('acc_transaction_new')) {
	try {
		$transaction->id_year = $current_year->id();
		$transaction->importFromNewForm();
		$transaction->id_creator = $session->getUser()->id;







>
>
>
>
>
>
>
>







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

	$transaction->type = Transaction::getTypeFromAccountType($account->type);
	$key = sprintf('account_%d_%d', $transaction->type, 0);

	if (!isset($_POST[$key])) {
		$_POST[$key] = [$account->id => sprintf('%s — %s', $account->code, $account->label)];
	}
}
elseif (!empty($_POST['lines']) && is_array($_POST['lines'])) {
	$lines = Utils::array_transpose($_POST['lines']);

	foreach ($lines as &$line) {
		$line['credit'] = Utils::moneyToInteger($line['credit']);
		$line['debit'] = Utils::moneyToInteger($line['debit']);
	}
}

if (f('save') && $form->check('acc_transaction_new')) {
	try {
		$transaction->id_year = $current_year->id();
		$transaction->importFromNewForm();
		$transaction->id_creator = $session->getUser()->id;

Modified src/www/admin/static/scripts/accounting.js from [3b65a15965] to [966a50b0f4].

61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

95
96
97
98
			}
			else {
				credit += v;
			}
		});

		if (m = $('#lines_message')) {

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

		debit = debit ? debit + '' : '000';
		credit = credit ? credit + '' : '000';
		$('#f_debit_total').value = g.formatMoney(debit);
		$('#f_credit_total').value = g.formatMoney(credit);
	}

	// 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 = '';
		});
		if (l = n.querySelector('.input-list .label')) {
			l.parentNode.removeChild(l);
		}
		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();
}







>
|













|










<

>




61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

94
95
96
97
98
99
			}
			else {
				credit += v;
			}
		});

		if (m = $('#lines_message')) {
			var diff = credit - debit;
			m.innerHTML = (debit === credit) ? '' : '<span class="alert">Écriture non équilibrée (' + g.formatMoney(diff) + ')</span>';
		}

		debit = debit ? debit + '' : '000';
		credit = credit ? credit + '' : '000';
		$('#f_debit_total').value = g.formatMoney(debit);
		$('#f_credit_total').value = g.formatMoney(credit);
	}

	// 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 = e.className.match(/money/) ? '0' : '';
		});
		if (l = n.querySelector('.input-list .label')) {
			l.parentNode.removeChild(l);
		}
		var b = n.querySelector('.input-list button');
		b.onclick = () => {
			g.current_list_input = b.parentNode;
			g.openFrameDialog(b.value);
			return false;
		};

		line.parentNode.appendChild(n);
		initLine(n);
	};

	updateTotals();
}

Modified src/www/admin/static/scripts/global.js from [7e15ba263a] to [772e0fb33f].

252
253
254
255
256
257
258

259
260
261
262
263
264
265
266
267

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

	g.formatMoney = (v) => {

		v = '' + v;
		return (v.substr(0, v.length-2) || '0') + ',' + v.substr(-2);
	};

	g.getMoneyAsInt = (v) => {
		v = v.replace(/[^0-9.,]/, '');
		if (v.length == 0) return;

		v = v.split(/[,.]/);







>
|
|







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

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

	g.formatMoney = (v) => {
		var s = v < 0 ? '-' : '';
		v = '' + Math.abs(v);
		return s + (v.substr(0, v.length-2) || '0') + ',' + ('00' + v).substr(-2);
	};

	g.getMoneyAsInt = (v) => {
		v = v.replace(/[^0-9.,]/, '');
		if (v.length == 0) return;

		v = v.split(/[,.]/);