noalyss  Version-9
impress.class.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of NOALYSS.
5  *
6  * NOALYSS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * NOALYSS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with NOALYSS; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */
20 
21 // Copyright Author Dany De Bontridder danydb@aevalys.eu
22 /* !
23  * \file
24  * \brief contains function for the parsing and computing formulae. Test are in scenario/test_parse_formula.php
25  */
26 
27 /**
28  * @class Impress
29  * @brief contains function for the parsing and computing formulae . Test are in scenario/test_parse_formula.php
30  */
31 class Impress
32 {
33  //!< string pattern to use
34  const STR_PATTERN="([\[\{]{1,2}[[:alnum:]]*%*(-[c,d,s,S]){0,1}[\]\}]{1,2})";
35 
36  /* !
37  *
38  * \brief Purpose Parse a formula
39  *
40  * \param $p_cn connexion
41  * \param $p_label
42  * \param $p_formula
43  * \param $p_eval true if we eval here otherwise the function returns
44  * a string which must be evaluated
45  \param $p_type_date : type of the date 0 for accountant period or 1
46  * for calendar
47  * \return array key [ desc , montant ]
48  *
49  *
50  */
51 
52  static function parse_formula($p_cn, $p_label, $p_formula, $p_start, $p_end, $p_eval=true, $p_type_date=0, $p_sql="")
53  {
54  global $g_user;
55  if (Impress::check_formula($p_formula)==false)
56  {
57  if ($p_eval==true)
58  return array('desc'=>$p_label.' Erreur Formule!',
59  'montant'=>0);
60  else
61  return $p_formula;
62  }
63  if ($p_type_date==0)
64  {
65  $cond=sql_filter_per($p_cn, $p_start, $p_end, 'p_id', 'j_tech_per');
66  $cond_anc= "and ".transform_sql_filter_per($cond);
67  }
68  else {
69  $cond="( j_date >= to_date('$p_start','DD.MM.YYYY') and j_date <= to_date('$p_end','DD.MM.YYYY'))";
70  $cond_anc="and ( oa_date >= to_date('$p_start','DD.MM.YYYY') and oa_date <= to_date('$p_end','DD.MM.YYYY'))";
71  }
72 
73  // ------------------- for accounting , analytic or card-------------------------------------
74  if ( DEBUGNOALYSS > 1)
75  {
76  tracedebug("impress.debug.log", "$p_formula ", 'parse_formula-71 $formula to parse' );
77  tracedebug("impress.debug.log", "$p_label ", 'parse_formula-72 $p_label' );
78  tracedebug("impress.debug.log", $p_start,'parse_formula-73 $p_start' );
79  tracedebug("impress.debug.log", $p_end,'parse_formula-74 $p_end ' );
80  tracedebug("impress.debug.log", $p_type_date,'parse_formula-75 $p_type_date' );
81  tracedebug("impress.debug.log", $p_sql,'parse_formula-76 $p_sql' );
82  tracedebug("impress.debug.log", $cond ,'parse_formula-77 $cond SQL accountancy' );
83  tracedebug("impress.debug.log", $cond_anc,'parse_formula-78 $cond_anc SQL Analytic Acc' );
84  }
85  while (preg_match_all(Impress::STR_PATTERN, $p_formula, $e)==true)
86  {
87  $x=$e[0];
88  foreach ($x as $line)
89  {
90 
91  // If there is a FROM clause we must recompute
92  // the time cond
93 
94  if ($p_type_date==0&&preg_match("/FROM=[0-9]+\.[0-9]+/", $p_formula, $afrom)==1)
95  {
96  $from=str_replace('FROM=','',$afrom[0]);
97  $cond = \Impress::compute_periode($p_cn,$from,$p_end);
98  $cond_anc=" and ".transform_sql_filter_per($cond);
99  // We remove FROM out of the p_formula
100  $p_formula=substr_replace($p_formula, "", strpos($p_formula, "FROM"));
101  }
102  if ($p_type_date==1&&preg_match("/FROM=[0-9]+\.[0-9]+/", $p_formula, $afrom)==1)
103  {
104  // We remove FROM out of the p_formula
105  $p_formula=substr_replace($p_formula, "", strpos($p_formula, "FROM"));
106  }
107  $amount=\Impress::compute_amount($p_cn,$line,$cond." ".$p_sql,$cond_anc." ".$p_sql);
108 
109 
110  $p_formula=str_replace($x[0],"(". $amount.")", $p_formula);
111  }
112  }
113 
114  // $p_eval is true then we eval and returns result
115  if ($p_eval==true)
116  {
117  /* -------------------------------------
118  * Protect againt division by zero
119  */
120  $p_formula=remove_divide_zero($p_formula);
121  $p_formula="\$result=".$p_formula.";";
122  try {
123  eval("$p_formula");
124  } catch(Exception $e) {
125  return array("desc"=>"erreur","montant"=>'0');
126  }
127  while (preg_match("/\[([0-9]+)(-[Tt]*)\]/", trim($p_label), $e)==1)
128  {
129  $nom="!!".$e[1]."!!";
130  if (Impress::check_formula($e[0]))
131  {
132  $nom=$p_cn->get_value("SELECT pcm_lib AS acct_name FROM tmp_pcmn WHERE pcm_val::text LIKE $1||'%' ORDER BY pcm_val ASC LIMIT 1",
133  array($e[1]));
134  if ($nom)
135  {
136  if ($e[2]=='-T')
137  $nom=strtoupper($nom);
138  if ($e[2]=='-t')
139  $nom=strtolower($nom);
140  }
141  }
142  $p_label=str_replace($e[0], $nom, $p_label);
143  }
144 
145  $aret=array('desc'=>$p_label,
146  'montant'=>$result);
147  return $aret;
148  }
149  else
150  {
151  // $p_eval is false we returns only the string
152  return $p_formula;
153  }
154  }
155 
156  /* !
157  * \brief Check if formula doesn't contain
158  * php injection
159  * \param string
160  *
161  * \return true if the formula is good otherwise false
162  */
163 
164  static function check_formula($p_string)
165  {
166  // the preg_match gets too complex if we want to add a test
167  // for parenthesis, math function...
168  // So I prefer remove them before testing
169 
170 
171 
172  $p_string=str_replace("round", "", $p_string);
173  $p_string=str_replace("abs", "", $p_string);
174  $p_string=str_replace("(", "", $p_string);
175  $p_string=str_replace(")", "", $p_string);
176  // for the inline test like $a=(cond)?value:other;
177  $p_string=str_replace("?", "+", $p_string);
178  $p_string=str_replace(":", "+", $p_string);
179  $p_string=str_replace(">=", "+", $p_string);
180  $p_string=str_replace("<=", "+", $p_string);
181  $p_string=str_replace(">", "+", $p_string);
182  $p_string=str_replace("<", "+", $p_string);
183  // eat Space + comma
184  $p_string=str_replace(" ", "", $p_string);
185  $p_string=str_replace(",", "", $p_string);
186  // Remove D/C/S
187  $p_string=str_replace("-c", "", $p_string);
188  $p_string=str_replace("-d", "", $p_string);
189  $p_string=str_replace("-s", "", $p_string);
190  $p_string=str_replace("-S", "", $p_string);
191  // Remove T,t
192  $p_string=str_replace("-t", "", $p_string);
193 
194  // analytic accountancy (between {} )
195  $p_string=preg_replace("/\{\{[[:alnum:]]*\}\}/", "", $p_string);
196 
197  // card (between {} )
198  $p_string=preg_replace("/\{[[:alnum:]]*\}/", "", $p_string);
199 
200  // remove date
201  $p_string=preg_replace("/FROM*=*[0-9]+/", "", $p_string);
202  // remove comment
203  $p_string=preg_replace("/#.*/", "", $p_string);
204  // remove php variable $C=
205  $p_string=preg_replace('/\$[a-z]*[A-Z]*[0-9]*[A-Z]*[a-z]*/', "", $p_string);
206  $p_string=preg_replace('/=/', "", $p_string);
207 
208  // remove account
209  $p_string=preg_replace("/\[[0-9]*[A-Z]*%*\]/", "", $p_string);
210 
211  $p_string=preg_replace("/\+|-|\/|\*/", "", $p_string);
212  $p_string=preg_replace("/[0-9]*\.*[0-9]/", "", $p_string);
213 
214  //************************************************************************************************************
215  // If the string is empty then formula should be good
216  //
217  //************************************************************************************************************
218  if ($p_string=='')
219  {
220  return true;
221  }
222  else
223  {
224  return false;
225  }
226  }
227 
228  /**
229  * with the handle of a successull query, echo each row into CSV and
230  * send it directly
231  * @param type $array of data
232  * @param type $aheader double array, each item of the array contains
233  * a key type (num) and a key title
234  */
235  static function array_to_csv($array, $aheader, $p_filename)
236  {
237  $file_csv=new Noalyss_Csv($p_filename);
238  for ($i=0; $i<count($aheader); $i++)
239  {
240  $file_csv->add($aheader[$i]['title']);
241  }
242  $file_csv->write();
243 
244  // fetch all the rows
245  for ($i=0; $i<count($array); $i++)
246  {
247  $row=$array[$i];
248  $e=0;
249  // for each rows, for each value
250  foreach ($array[$i] as $key=> $value)
251  {
252  if ($e>count($aheader))
253  continue;
254 
255  if (isset($aheader[$e]['type']))
256  {
257  switch ($aheader[$e]['type'])
258  {
259  case 'num':
260  $file_csv->add($value, "number");
261  break;
262  default:
263  $file_csv->add($value);
264  }
265  }
266  else
267  {
268  $file_csv->add($value);
269  }
270  $e++;
271  }
272  $file_csv->write();
273  }
274  }
275 
276  /**
277  * return what to consider
278  * - "deb" for the total of the debit ,
279  * - "cred" for total of credit,
280  * - "signed" for tot. debit - tot. credit
281  * - "cdsigned" for tot.credit - tot.debit
282  * - "all" is the balance of accounting in absolute value
283  *
284  * @param string $p_formula
285  * @return "all", "deb","cred","signed" or "cdsigned"
286  */
287  static function find_computing_mode($p_formula)
288  {
289  if (strpos($p_formula, '-d')!=0)
290  {
291  return 'deb';
292  }
293  elseif (strpos($p_formula, '-c')!=0)
294  {
295  return 'cred';
296  }
297  elseif (strpos($p_formula, '-s')!=0)
298  {
299  return 'signed';
300  }
301  elseif (strpos($p_formula, '-S')!=0)
302  {
303  return 'cdsigned';
304  }
305  return 'all';
306  }
307  /**
308  * @brief make the condition SQL for filtering on the period
309  * @param \DatabaseCore $p_cn
310  * @param int $p_from periode id
311  * @param int $p_end until periode id
312  * @throws Exception
313  */
314  static public function compute_periode($p_cn, $p_from,$p_end)
315  {
316  // There is a FROM clause
317  // then we must modify the cond for the periode
318 
319 
320  // Get the periode
321  /* ! \note special value for the clause FROM=00.0000, we take the first day of the exercice of $p_end
322  */
323  if ($p_from=='00.0000')
324  {
325  $current_exercice=$p_cn->get_value('select p_exercice from parm_periode where p_id=$1',
326  [$p_end]);
327  if ( $current_exercice=="") {
328  throw new Execution(_('CP329'));
329  }
330  $first_day=$p_cn->get_value("select to_char(min(p_start),'DD.MM.YYYY') as p_start from parm_periode where p_exercice=$1",
332  $last_day=$p_cn->get_value("select to_char(p_end,'DD.MM.YYYY') from parm_periode where p_id=$1",[$p_end]);
333  // retrieve the first month of this periode
334  if (empty($first_day))
335  throw new Exception('Pas de limite à cette période', 1);
336  $cond=sql_filter_per($p_cn, $first_day, $last_day, 'date', 'j_tech_per');
337  }
338  else
339  {
340  $oPeriode=new Periode($p_cn);
341  try
342  {
343  $pfrom=$oPeriode->find_periode('01.'.$p_from);
344  $cond=sql_filter_per($p_cn, $pfrom, $p_end, 'p_id', 'j_tech_per');
345  }
346  catch (Exception $exp)
347  {
348  /* if none periode is found
349  then we take the first periode of the year
350  */
351 
352  $first_day=$p_cn->get_value("select to_char(min(p_start),'DD.MM.YYYY') as p_start from parm_periode");
353  $last_day=$p_cn->get_value("select to_char(p_end,'DD.MM.YYYY') from parm_periode where p_id=$1",[$p_end]);
354  // retrieve the first month of this periode
355  if (empty($first_day))
356  throw new Exception('Pas de limite à cette période', 1);
357  $cond=sql_filter_per($p_cn, $first_day, $last_day, 'date', 'j_tech_per');
358  }
359  }
360  return $cond;
361  }
362  /**
363  * @brief compute the amount of the accounting ,analytic accounting or a card, the SQL condition
364  * from sql_filter_per must be transformed for ANALYTIC ACCOUNT
365  * @see sql_filter_per
366  * @param DatabaseCore $p_cn
367  * @param string $p_expression part of a formula
368  * @param string $p_cond_sql SQL cond for accountancy
369  * @param string $p_cond_sql SQL cond for analytic accountancy
370  */
371  static function compute_amount($p_cn, $p_expression, $p_cond_sql,$p_cond_anc_sql)
372  {
373  if ( DEBUGNOALYSS > 1)
374  {
375  tracedebug("impress.debug.log", "$p_expression", '$p_expression' );
376  tracedebug("impress.debug.log", "$p_cond_sql", '$p_cond_sql' );
377  }
378  $compute=\Impress::find_computing_mode($p_expression);
379  // remove char for the mode
380  $p_expression=str_replace("-d", "", $p_expression);
381  $p_expression=str_replace("-c", "", $p_expression);
382  $p_expression=str_replace("-s", "", $p_expression);
383  $p_expression=str_replace("-S", "", $p_expression);
384  // we have an account
385  if (preg_match("/\[.*\]/", $p_expression)) {
386  $p_expression=str_replace("[", "", $p_expression);
387  $p_expression=str_replace("]", "", $p_expression);
388  $P=new Acc_Account_Ledger($p_cn, $p_expression);
389  $detail=$P->get_solde_detail($p_cond_sql);
390  } elseif (preg_match("/\{\{.*\}\}/", $p_expression))
391  {
392  $p_expression=str_replace("{", "", $p_expression);
393  $p_expression=str_replace("}", "", $p_expression);
394  $anc_account= new Anc_Account($p_cn);
395  $anc_account->load_by_code($p_expression);
396 
397  if ( DEBUGNOALYSS > 1)
398  {
399  tracedebug("impress.debug.log", $p_expression, 'code analytic account');
400  tracedebug("impress.debug.log", $p_cond_anc_sql, 'condition SQL ');
401  }
402  /// Transform the $p_cond_sql , it comes from sql_filter_per
403  // and looks like j_tech_per in (select p_id from parm_periode where
404 
405  $detail=$anc_account->get_balance($p_cond_anc_sql);
406 
407  } elseif (preg_match("/\{.*\}/", $p_expression))
408  { // we have a card
409  // remove useless char
410  $p_expression=str_replace("{", "", $p_expression);
411  $p_expression=str_replace("}", "", $p_expression);
412  $fiche=new Fiche($p_cn);
413  if ( DEBUGNOALYSS > 1)
414  {
415  tracedebug("impress.debug.log", "$p_expression", 'search_card qcode =');
416  }
417  $fiche->get_by_qcode(strtoupper(trim($p_expression)));
418  $detail=$fiche->get_solde_detail($p_cond_sql);
419  } else {
420  throw new \Exception ("Impress::compute_amount383.".
421  " Unknown expression \$p_expression [$p_expression]".
422  " \$p_cond_sql $p_cond_sql");
423  }
424 
425 
426 
427 
428  // Get sum of account
429 
430  switch ($compute)
431  {
432  case "all":
433  $res=$detail['solde'];
434  break;
435  case 'deb':
436  $res=$detail['debit'];
437  break;
438  case 'cred':
439  $res=$detail['credit'];
440  break;
441  case 'signed':
442  $res=bcsub($detail['debit'], $detail['credit'], 4);
443  break;
444  case 'cdsigned':
445  $res=bcsub($detail['credit'], $detail['debit'], 4);
446  break;
447  }
448  return $res;
449  }
450 
451 }
$detail
$detail
Definition: ajax_display_letter.php:65
$p_end
$p_end
Definition: export_rec_csv.php:18
Impress
contains function for the parsing and computing formulae . Test are in scenario/test_parse_formula....
Definition: impress.class.php:31
$e
$e
Definition: result_cat_card_summary.php:26
Periode
For the periode tables parm_periode and jrn_periode.
Definition: periode.class.php:32
remove_divide_zero
remove_divide_zero($p_formula)
When it is needed to eval a formula , this function prevent the divide by zero.
Definition: ac_common.php:1456
Impress\STR_PATTERN
const STR_PATTERN
< string pattern to use
Definition: impress.class.php:34
$fiche
$fiche
Definition: ajax_add_concerned_card.php:98
Impress\check_formula
static check_formula($p_string)
Definition: impress.class.php:164
Impress\compute_amount
static compute_amount($p_cn, $p_expression, $p_cond_sql, $p_cond_anc_sql)
compute the amount of the accounting ,analytic accounting or a card, the SQL condition from sql_filte...
Definition: impress.class.php:371
Impress\find_computing_mode
static find_computing_mode($p_formula)
return what to consider
Definition: impress.class.php:287
$result
$result
Definition: anc_great_ledger.inc.php:26
$array
$array
Definition: ajax_add_concerned_card.php:115
Noalyss_Csv
Manage the CSV : manage files and write CSV record.
Definition: noalyss_csv.class.php:33
$g_user
global $g_user
Find the default module or the first one.
Definition: action.inc.php:24
Impress\parse_formula
static parse_formula($p_cn, $p_label, $p_formula, $p_start, $p_end, $p_eval=true, $p_type_date=0, $p_sql="")
Definition: impress.class.php:52
$value
$value
Definition: export_document.php:41
$i
$i
Definition: action_document_type_mtable_input.php:83
$first_day
$first_day
Definition: ajax_history_anc_account.php:56
Acc_Account_Ledger
Manage the account from the table jrn, jrnx or tmp_pcmn.
Definition: acc_account_ledger.class.php:27
$from
$from
Definition: balance.inc.php:61
Fiche
define Class fiche and fiche def, those class are using class attribut. When adding or modifing new c...
Definition: fiche.class.php:37
$line
$line
Definition: ajax_display_letter.php:88
$amount
$amount
Definition: ajax_anc_key_compute.php:40
$last_day
$last_day
Definition: ajax_history_anc_account.php:59
sql_filter_per
sql_filter_per($p_cn, $p_from, $p_to, $p_form='p_id', $p_field='jr_tech_per')
Create the condition to filter on the j_tech_per thanks a from and to date.
Definition: ac_common.php:675
$p_start
$p_start
Definition: export_rec_csv.php:17
$row
$row
Definition: ajax_anc_detail_operation.php:33
$current_exercice
$current_exercice
Definition: export_fiche_detail_pdf.php:90
$oPeriode
$oPeriode
Definition: do.php:209
$res
$res
Definition: ajax_preference.php:49
Impress\array_to_csv
static array_to_csv($array, $aheader, $p_filename)
with the handle of a successull query, echo each row into CSV and send it directly
Definition: impress.class.php:235
Anc_Account
Analytic account ; get the balance.
Definition: anc_account.class.php:30
Impress\compute_periode
static compute_periode($p_cn, $p_from, $p_end)
make the condition SQL for filtering on the period
Definition: impress.class.php:314