02021a21cc575f6d79b66b50da2a86b352dd4fe9
[garradin.git] / include / class.compta_comptes.php
1 <?php
2
3 namespace Garradin;
4
5 class Compta_Comptes
6 {
7 const CAISSE = 530;
8
9 const PASSIF = 0x01;
10 const ACTIF = 0x02;
11 const PRODUIT = 0x04;
12 const CHARGE = 0x08;
13
14 public function importPlan()
15 {
16 $plan = json_decode(file_get_contents(ROOT . '/include/data/plan_comptable.json'), true);
17
18 $db = DB::getInstance();
19 $db->exec('BEGIN;');
20 $ids = [];
21
22 foreach ($plan as $id=>$compte)
23 {
24 $ids[] = $id;
25
26 if ($db->simpleQuerySingle('SELECT 1 FROM compta_comptes WHERE id = ?;', false, $id))
27 {
28 $db->simpleUpdate('compta_comptes', [
29 'parent' => $compte['parent'],
30 'libelle' => $compte['nom'],
31 'position' => $compte['position'],
32 'plan_comptable' => 1,
33 ], 'id = \''.$db->escapeString($id).'\'');
34 }
35 else
36 {
37 $db->simpleInsert('compta_comptes', [
38 'id' => $id,
39 'parent' => $compte['parent'],
40 'libelle' => $compte['nom'],
41 'position' => $compte['position'],
42 'plan_comptable' => 1,
43 ]);
44 }
45 }
46
47 $db->exec('DELETE FROM compta_comptes WHERE id NOT IN(\''.implode('\', \'', $ids).'\') AND plan_comptable = 1;');
48
49 $db->exec('END;');
50
51 return true;
52 }
53
54 public function add($data)
55 {
56 $this->_checkFields($data, true);
57
58 $db = DB::getInstance();
59
60 if (empty($data['id']))
61 {
62 $new_id = $data['parent'];
63 $nb_sous_comptes = $db->simpleQuerySingle('SELECT COUNT(*) FROM compta_comptes WHERE parent = ?;', false, $new_id);
64
65 // Pas plus de 26 sous-comptes par compte, parce que l'alphabet s'arrête à 26 lettres
66 if ($nb_sous_comptes >= 26)
67 {
68 throw new UserException('Nombre de sous-comptes maximal atteint pour ce compte parent-ci.');
69 }
70
71 $new_id .= chr(65+(int)$nb_sous_comptes);
72 }
73 else
74 {
75 $new_id = $data['id'];
76 }
77
78 if (isset($data['position']))
79 {
80 $position = (int) $data['position'];
81 }
82 else
83 {
84 $position = $db->simpleQuerySingle('SELECT position FROM compta_comptes WHERE id = ?;', false, $data['parent']);
85 }
86
87 $db->simpleInsert('compta_comptes', [
88 'id' => $new_id,
89 'libelle' => trim($data['libelle']),
90 'parent' => $data['parent'],
91 'plan_comptable' => 0,
92 'position' => (int)$position,
93 ]);
94
95 return $new_id;
96 }
97
98 public function edit($id, $data)
99 {
100 $db = DB::getInstance();
101
102 // Vérification que l'on peut éditer ce compte
103 if ($db->simpleQuerySingle('SELECT plan_comptable FROM compta_comptes WHERE id = ?;', false, $id))
104 {
105 throw new UserException('Ce compte fait partie du plan comptable et n\'est pas modifiable.');
106 }
107
108 if (isset($data['position']) && empty($data['position']))
109 {
110 throw new UserException('Aucune position du compte n\'a été indiquée.');
111 }
112
113 $this->_checkFields($data);
114
115 $update = [
116 'libelle' => trim($data['libelle']),
117 ];
118
119 if (isset($data['position']))
120 {
121 $update['position'] = (int) trim($data['position']);
122 }
123
124 $db->simpleUpdate('compta_comptes', $update, 'id = \''.$db->escapeString(trim($id)).'\'');
125
126 return true;
127 }
128
129 public function delete($id)
130 {
131 $db = DB::getInstance();
132
133 // Ne pas supprimer un compte qui est utilisé !
134 if ($db->simpleQuerySingle('SELECT 1 FROM compta_journal WHERE compte_debit = ? OR compte_debit = ? LIMIT 1;', false, $id, $id))
135 {
136 throw new UserException('Ce compte ne peut être supprimé car des opérations comptables y sont liées.');
137 }
138
139 if ($db->simpleQuerySingle('SELECT 1 FROM compta_comptes_bancaires WHERE id = ? LIMIT 1;', false, $id))
140 {
141 throw new UserException('Ce compte ne peut être supprimé car il est lié à un compte bancaire.');
142 }
143
144 if ($db->simpleQuerySingle('SELECT 1 FROM compta_categories WHERE compte = ? LIMIT 1;', false, $id))
145 {
146 throw new UserException('Ce compte ne peut être supprimé car des catégories y sont liées.');
147 }
148
149 $db->simpleExec('DELETE FROM compta_comptes WHERE id = ?;', trim($id));
150
151 return true;
152 }
153
154 /**
155 * Peut-on supprimer ce compte ? (OUI s'il n'a pas d'écriture liée)
156 * @param string $id Numéro du compte
157 * @return boolean TRUE si le compte n'a pas d'écriture liée
158 */
159 public function canDelete($id)
160 {
161 $db = DB::getInstance();
162
163 if ($db->simpleQuerySingle('SELECT 1 FROM compta_journal
164 WHERE compte_debit = ? OR compte_debit = ? LIMIT 1;', false, $id, $id))
165 {
166 return false;
167 }
168
169 if ($db->simpleQuerySingle('SELECT 1 FROM compta_categories WHERE compte = ? LIMIT 1;', false, $id))
170 {
171 return false;
172 }
173
174 return true;
175 }
176
177 /**
178 * Peut-on désactiver ce compte ? (OUI s'il n'a pas d'écriture liée dans l'exercice courant)
179 * @param string $id Numéro du compte
180 * @return boolean TRUE si le compte n'a pas d'écriture liée dans l'exercice courant
181 */
182 public function canDisable($id)
183 {
184 $db = DB::getInstance();
185
186 if ($db->simpleQuerySingle('SELECT 1 FROM compta_journal
187 WHERE id_exercice = (SELECT id FROM compta_exercices WHERE cloture = 0 LIMIT 1)
188 AND (compte_debit = ? OR compte_debit = ?) LIMIT 1;', false, $id, $id))
189 {
190 return false;
191 }
192
193 if ($db->simpleQuerySingle('SELECT 1 FROM compta_categories WHERE compte = ? LIMIT 1;', false, $id))
194 {
195 return false;
196 }
197
198 return true;
199 }
200
201 /**
202 * Désactiver un compte
203 * Le compte ne sera plus utilisable pour les écritures ou les catégories mais restera en base de données
204 * @param string $id Numéro du compte
205 * @return boolean TRUE si la désactivation a fonctionné, une exception utilisateur si
206 * la désactivation n'est pas possible.
207 */
208 public function disable($id)
209 {
210 $db = DB::getInstance();
211
212 // Ne pas désactiver un compte utilisé dans l'exercice courant
213 if ($db->simpleQuerySingle('SELECT 1 FROM compta_journal
214 WHERE id_exercice = (SELECT id FROM compta_exercices WHERE cloture = 0 LIMIT 1)
215 AND (compte_debit = ? OR compte_debit = ?) LIMIT 1;', false, $id, $id))
216 {
217 throw new UserException('Ce compte ne peut être désactivé car des écritures y sont liées sur l\'exercice courant. '
218 . 'Il faut supprimer ou ré-attribuer ces écritures avant de pouvoir supprimer le compte.');
219 }
220
221 // Ne pas désactiver un compte utilisé pour une catégorie
222 if ($db->simpleQuerySingle('SELECT 1 FROM compta_categories WHERE compte = ? LIMIT 1;', false, $id))
223 {
224 throw new UserException('Ce compte ne peut être désactivé car des catégories y sont liées.');
225 }
226
227 return $db->simpleUpdate('compta_comptes', ['desactive' => 1], 'id = \''.$db->escapeString(trim($id)).'\'');
228 }
229
230 public function get($id)
231 {
232 $db = DB::getInstance();
233 return $db->simpleQuerySingle('SELECT * FROM compta_comptes WHERE id = ?;', true, trim($id));
234 }
235
236 public function getList($parent = 0)
237 {
238 $db = DB::getInstance();
239 return $db->simpleStatementFetchAssocKey('SELECT id, * FROM compta_comptes WHERE parent = ? ORDER BY id;', SQLITE3_ASSOC, $parent);
240 }
241
242 public function getListAll($parent = 0)
243 {
244 $db = DB::getInstance();
245 return $db->queryFetchAssoc('SELECT id, libelle FROM compta_comptes ORDER BY id;');
246 }
247
248 public function listTree($parent = 0, $include_children = true)
249 {
250 $db = DB::getInstance();
251
252 if ($include_children)
253 {
254 $parent = $parent ? 'WHERE parent LIKE \''.$db->escapeString($parent).'%\' ' : '';
255 }
256 else
257 {
258 $parent = $parent ? 'WHERE parent = \''.$db->escapeString($parent).'\' ' : 'WHERE parent = 0';
259 }
260
261 return $db->simpleStatementFetch('SELECT * FROM compta_comptes '.$parent.' ORDER BY id;');
262 }
263
264 protected function _checkFields(&$data, $force_parent_check = false)
265 {
266 $db = DB::getInstance();
267
268 if (empty($data['libelle']) || !trim($data['libelle']))
269 {
270 throw new UserException('Le libellé ne peut rester vide.');
271 }
272
273 $data['libelle'] = trim($data['libelle']);
274
275 if (isset($data['id']))
276 {
277 $force_parent_check = true;
278 $data['id'] = trim($data['id']);
279
280 if ($db->simpleQuerySingle('SELECT 1 FROM compta_comptes WHERE id = ?;', false, $data['id']))
281 {
282 throw new UserException('Le compte numéro '.$data['id'].' existe déjà.');
283 }
284 }
285
286 if (isset($data['parent']) || $force_parent_check)
287 {
288 if (empty($data['parent']) && !trim($data['parent']))
289 {
290 throw new UserException('Le compte ne peut pas ne pas avoir de compte parent.');
291 }
292
293 if (!($id = $db->simpleQuerySingle('SELECT id FROM compta_comptes WHERE id = ?;', false, $data['parent'])))
294 {
295 throw new UserException('Le compte parent indiqué n\'existe pas.');
296 }
297
298 $data['parent'] = trim($id);
299 }
300
301 if (isset($data['id']))
302 {
303 if (strncmp($data['id'], $data['parent'], strlen($data['parent'])) !== 0)
304 {
305 throw new UserException('Le compte '.$data['id'].' n\'est pas un sous-compte de '.$data['parent'].'.');
306 }
307 }
308
309 return true;
310 }
311
312 public function getPositions()
313 {
314 return [
315 self::ACTIF => 'Actif',
316 self::PASSIF => 'Passif',
317 self::ACTIF | self::PASSIF => 'Actif ou passif (déterminé automatiquement au bilan selon le solde du compte)',
318 self::CHARGE => 'Charge',
319 self::PRODUIT => 'Produit',
320 self::CHARGE | self::PRODUIT => 'Charge et produit',
321 ];
322 }
323 }
324
325 ?>