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