Ajout : ./garradin
[garradin.git] / include / class.compta_import.php
1 <?php
2
3 namespace Garradin;
4
5 class Compta_Import
6 {
7 protected $csv_header = [
8 'Numéro mouvement',
9 'Date',
10 'Type de mouvement',
11 'Catégorie',
12 'Libellé',
13 'Montant',
14 'Compte de débit - numéro',
15 'Compte de débit - libellé',
16 'Compte de crédit - numéro',
17 'Compte de crédit - libellé',
18 'Moyen de paiement',
19 'Numéro de chèque',
20 'Numéro de pièce',
21 'Remarques'
22 ];
23
24 public function toCSV($exercice)
25 {
26 $db = DB::getInstance();
27
28 $retro_comptatibility = 'SELECT count(montant) AS montants FROM compta_flux WHERE compta_flux.id_journal = journal.id';
29 $res = $db->prepare('
30 SELECT
31 journal.id,
32 strftime(\'%d/%m/%Y\', date) AS date,
33 (CASE cat.type WHEN 1 THEN \'Recette\' WHEN -1 THEN \'Dépense\' ELSE \'Autre\' END) AS type,
34 (CASE cat.intitule WHEN NULL THEN \'\' ELSE cat.intitule END),
35 journal.libelle,
36 abs(flux.montant) AS montant,
37 (CASE WHEN flux.montant >= 0 THEN flux.compte ELSE \'\' END),
38 (CASE WHEN flux.montant >= 0 THEN flux_compte.libelle ELSE \'\' END) AS compte_debit_libelle,
39 (CASE WHEN flux.montant < 0 THEN flux.compte ELSE \'\' END),
40 (CASE WHEN flux.montant < 0 THEN flux_compte.libelle ELSE \'\' END) AS compte_credit_libelle,
41 (CASE moyen_paiement WHEN NULL THEN \'\' ELSE moyen.nom END) AS moyen,
42 numero_cheque,
43 numero_piece,
44 remarques
45 FROM compta_journal AS journal
46 LEFT JOIN compta_categories AS cat ON cat.id = journal.id_categorie
47 LEFT JOIN compta_moyens_paiement AS moyen ON moyen.code = journal.moyen_paiement
48 LEFT JOIN compta_flux AS flux ON flux.id_journal = journal.id
49 LEFT JOIN compta_comptes AS flux_compte ON flux_compte.id = flux.compte
50 WHERE id_exercice = '.(int)$exercice.'
51 ORDER BY journal.id;
52 ')->execute();
53
54 $fp = fopen('php://output', 'w');
55
56 fputcsv($fp, $this->csv_header);
57
58 while ($row = $res->fetchArray(SQLITE3_ASSOC))
59 {
60 fputcsv($fp, $row);
61 }
62
63 fclose($fp);
64
65 return true;
66 }
67
68 public function fromCSV($path)
69 {
70 if (!file_exists($path) || !is_readable($path))
71 {
72 throw new \RuntimeException('Fichier inconnu : '.$path);
73 }
74
75 $fp = fopen($path, 'r');
76
77 if (!$fp)
78 {
79 return false;
80 }
81
82 $db = DB::getInstance();
83 $db->exec('BEGIN;');
84 $comptes = new Compta_Comptes;
85 $banques = new Compta_Comptes_Bancaires;
86 $cats = new Compta_Categories;
87 $journal = new Compta_Journal;
88
89 $columns = array_flip($this->csv_header);
90 $liste_comptes = $db->simpleStatementFetchAssoc('SELECT id, id FROM compta_comptes;');
91 $liste_cats = $db->simpleStatementFetchAssoc('SELECT intitule, id FROM compta_categories;');
92 $liste_moyens = $cats->listMoyensPaiement();
93
94 $col = function($column) use (&$row, &$columns)
95 {
96 if (!isset($columns[$column]))
97 return null;
98
99 if (!isset($row[$columns[$column]]))
100 return null;
101
102 return $row[$columns[$column]];
103 };
104
105 $line = 0;
106 $last_id = 0;
107 $id = 0;
108 $delim = utils::find_csv_delim($fp);
109 unset($data);
110
111 while (!feof($fp))
112 {
113 $row = fgetcsv($fp, 4096, $delim);
114 print_r(["row" => $row]);
115 $line++;
116
117 if (empty($row))
118 {
119 continue;
120 }
121
122 if ($line === 1)
123 {
124 if (trim($row[0]) != 'Numéro mouvement')
125 {
126 throw new UserException('Erreur sur la ligne ' . $line . ' : l\'entête des colonnes est absent ou incorrect.');
127 }
128
129 continue;
130 }
131
132 if (count($row) != count($columns))
133 {
134 $db->exec('ROLLBACK;');
135 throw new UserException('Erreur sur la ligne ' . $line . ' : le nombre de colonnes est incorrect.');
136 }
137
138 if (trim($row[0]) !== '' && !is_numeric($row[0]))
139 {
140 $db->exec('ROLLBACK;');
141 throw new UserException('Erreur sur la ligne ' . $line . ' : la première colonne doit être vide ou contenir le numéro unique d\'opération.');
142 }
143
144 $last_id = $id;
145 $id = $col('Numéro mouvement');
146 if ($id == $last_id)
147 {
148 $montant = $col('Montant');
149 $debit = $col('Compte de débit - numéro');
150 $credit = $col('Compte de crédit - numéro');
151
152 if (trim($debit) == '' && trim($credit) != '')
153 {
154 $debit = null;
155 $fluxs[] =
156 [ 'compte' => $credit
157 , 'montant' => - $montant
158 ];
159 }
160 if (trim($debit) != '' && trim($credit) == '')
161 {
162 $credit = null;
163 $fluxs[] =
164 [ 'compte' => $debit
165 , 'montant' => $montant
166 ];
167 }
168 }
169 else
170 {
171 if (isset($data)) {
172 // NOTE: import previously collected $last_id
173 $data['fluxs'] = $fluxs;
174 $id_journal = $db->simpleQuerySingle('SELECT id FROM compta_journal WHERE id = ?;', false, $last_id);
175 if (empty($last_id) || empty($id_journal))
176 {
177 print_r(["data" => $data]);
178 $journal->add($data);
179 }
180 else
181 {
182 print_r(['id' => $last_id, "data" => $data]);
183 $journal->edit($last_id, $data);
184 }
185 unset($data);
186 }
187
188 // NOTE: start collecting $id
189 $fluxs = [];
190 $date = $col('Date');
191
192 if (!preg_match('!^\d{2}/\d{2}/\d{4}$!', $date))
193 {
194 $db->exec('ROLLBACK;');
195 throw new UserException('Erreur sur la ligne ' . $line . ' : la date n\'est pas au format jj/mm/aaaa.');
196 }
197
198 $date = explode('/', $date);
199 $date = $date[2] . '-' . $date[1] . '-' . $date[0];
200
201 // En dehors de l'exercice courant
202 if ($db->simpleQuerySingle('SELECT 1 FROM compta_exercices
203 WHERE (? < debut OR ? > fin) AND cloture = 0;', false, $date, $date))
204 {
205 continue;
206 }
207
208 $cat = $col('Catégorie');
209 $moyen = strtoupper(substr($col('Moyen de paiement'), 0, 2));
210
211 if (!$moyen || !array_key_exists($moyen, $liste_moyens))
212 {
213 $moyen = false;
214 $cat = false;
215 }
216
217 if ($cat && !array_key_exists($cat, $liste_cats))
218 {
219 $cat = $moyen = false;
220 }
221
222 $montant = $col('Montant');
223 $debit = $col('Compte de débit - numéro');
224 $credit = $col('Compte de crédit - numéro');
225
226 if (trim($debit) == '' && trim($credit) != '')
227 {
228 $debit = null;
229 $fluxs[] =
230 [ 'compte' => $credit
231 , 'montant' => - $montant
232 ];
233 }
234 if (trim($debit) != '' && trim($credit) == '')
235 {
236 $credit = null;
237 $fluxs[] =
238 [ 'compte' => $debit
239 , 'montant' => $montant
240 ];
241 }
242
243 $data = [
244 'libelle' => $col('Libellé'),
245 'date' => $date,
246 'numero_piece' => $col('Numéro de pièce'),
247 'remarques' => $col('Remarques'),
248 ];
249
250 if ($cat)
251 {
252 $data['moyen_paiement'] = $moyen;
253 $data['numero_cheque'] = $col('Numéro de chèque');
254 $data['id_categorie'] = $liste_cats[$cat];
255 }
256 }
257 }
258 if (isset($data)) {
259 // NOTE: import previously collected $id
260 $data['fluxs'] = $fluxs;
261 $id_journal = $db->simpleQuerySingle('SELECT id FROM compta_journal WHERE id = ?;', false, $id);
262 if (empty($id) || empty($id_journal))
263 {
264 print_r(["data" => $data]);
265 $journal->add($data);
266 }
267 else
268 {
269 print_r(['id (end)' => $id, "data" => $data]);
270 $journal->edit($id, $data);
271 }
272 unset($data);
273 }
274
275 $db->exec('END;');
276
277 fclose($fp);
278 return true;
279 }
280
281 public function fromCitizen($path)
282 {
283 if (!file_exists($path) || !is_readable($path))
284 {
285 throw new \RuntimeException('Fichier inconnu : '.$path);
286 }
287
288 $fp = fopen($path, 'r');
289
290 if (!$fp)
291 {
292 return false;
293 }
294
295 $db = DB::getInstance();
296 $db->exec('BEGIN;');
297 $comptes = new Compta_Comptes;
298 $banques = new Compta_Comptes_Bancaires;
299 $cats = new Compta_Categories;
300 $journal = new Compta_Journal;
301
302 $columns = [];
303 $liste_comptes = $db->simpleStatementFetchAssoc('SELECT id, id FROM compta_comptes;');
304 $liste_cats = $db->simpleStatementFetchAssoc('SELECT intitule, id FROM compta_categories;');
305 $liste_moyens = $cats->listMoyensPaiement();
306
307 $get_compte = function ($compte, $intitule) use (&$liste_comptes, &$comptes, &$banques)
308 {
309 if (substr($compte, 0, 2) == '51')
310 {
311 $compte = '512' . substr($compte, -1);
312 }
313
314 // Création comptes
315 if (!array_key_exists($compte, $liste_comptes))
316 {
317 if (substr($compte, 0, 3) == '512')
318 {
319 $liste_comptes[$compte] = $banques->add([
320 'libelle' => $intitule,
321 'banque' => 'Inconnue',
322 ]);
323 }
324 else
325 {
326 $liste_comptes[$compte] = $comptes->add([
327 'id' => $compte,
328 'libelle' => $intitule,
329 'parent' => substr($compte, 0, -1)
330 ]);
331 }
332 }
333
334 return $compte;
335 };
336
337 $col = function($column) use (&$row, &$columns)
338 {
339 if (!isset($columns[$column]))
340 return null;
341
342 if (!isset($row[$columns[$column]]))
343 return null;
344
345 return $row[$columns[$column]];
346 };
347
348 $line = 0;
349 $delim = utils::find_csv_delim($fp);
350
351 while (!feof($fp))
352 {
353 $row = fgetcsv($fp, 4096, $delim);
354 $line++;
355
356 if (empty($row))
357 {
358 continue;
359 }
360
361 if (empty($columns))
362 {
363 $columns = $row;
364 $columns = array_flip($columns);
365 continue;
366 }
367
368 $date = $col('Date');
369
370 if (!preg_match('!^\d{2}/\d{2}/\d{4}$!', $date))
371 {
372 $db->exec('ROLLBACK;');
373 throw new UserException('Erreur sur la ligne ' . $line . ' : la date n\'est pas au format jj/mm/aaaa.');
374 }
375
376 $date = explode('/', $date);
377 $date = $date[2] . '-' . $date[1] . '-' . $date[0];
378
379 if ($db->simpleQuerySingle('SELECT 1 FROM compta_exercices
380 WHERE (? < debut OR ? > fin) AND cloture = 0;', false, $date, $date))
381 {
382 continue;
383 }
384
385 $debit = $get_compte($col('Compte débité - Numéro'), $col('Compte débité - Intitulé'));
386 $credit = $get_compte($col('Compte crédité - Numéro'), $col('Compte crédité - Intitulé'));
387
388 $cat = $col('Rubrique');
389 $moyen = strtoupper(substr($col('Moyen de paiement'), 0, 2));
390
391 if (!$moyen || !array_key_exists($moyen, $liste_moyens))
392 {
393 $moyen = false;
394 $cat = false;
395 }
396
397 if ($cat && !array_key_exists($cat, $liste_cats))
398 {
399 if ($col('Nature') == 'Recette')
400 {
401 $type = $cats::RECETTES;
402 $compte = $credit;
403 }
404 elseif ($col('Nature') == 'Dépense')
405 {
406 $type = $cats::DEPENSES;
407 $compte = $debit;
408 }
409 else
410 {
411 $type = $cats::AUTRES;
412 $cat = false;
413 }
414
415 if ($type != $cats::AUTRES)
416 {
417 $liste_cats[$cat] = $cats->add([
418 'intitule' => $cat,
419 'type' => $type,
420 'compte' => $compte
421 ]);
422 }
423 }
424
425 $data = [
426 'libelle' => $col('Libellé'),
427 'montant' => $col('Montant'),
428 'date' => $date,
429 'compte_credit' => $credit,
430 'compte_debit' => $debit,
431 'numero_piece' => $col('Numéro de pièce'),
432 'remarques' => $col('Remarques'),
433 ];
434
435 if ($cat)
436 {
437 $data['moyen_paiement'] = $moyen;
438 $data['numero_cheque'] = $col('Numéro de chèque');
439 $data['id_categorie'] = $liste_cats[$cat];
440 }
441
442 $journal->add($data);
443 }
444
445 $db->exec('END;');
446
447 fclose($fp);
448 return true;
449 }
450 }
451
452 ?>