dolibarr  7.0.0-beta
actions_massactions.inc.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2015-2017 Laurent Destailleur <eldy@users.sourceforge.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  * or see http://www.gnu.org/
17  */
18 
25 // $massaction must be defined
26 // $objectclass and $objectlabel must be defined
27 // $parameters, $object, $action must be defined for the hook.
28 
29 // $permtoread, $permtocreate and $permtodelete may be defined
30 // $uploaddir may be defined (example to $conf->projet->dir_output."/";)
31 // $toselect may be defined
32 
33 
34 // Protection
35 if (empty($objectclass) || empty($uploaddir))
36 {
37  dol_print_error(null, 'include of actions_massactions.inc.php is done but var $massaction or $objectclass or $uploaddir was not defined');
38  exit;
39 }
40 
41 
42 // Mass actions. Controls on number of lines checked.
43 $maxformassaction=(empty($conf->global->MAIN_LIMIT_FOR_MASS_ACTIONS)?1000:$conf->global->MAIN_LIMIT_FOR_MASS_ACTIONS);
44 if (! empty($massaction) && count($toselect) < 1)
45 {
46  $error++;
47  setEventMessages($langs->trans("NoRecordSelected"), null, "warnings");
48 }
49 if (! $error && count($toselect) > $maxformassaction)
50 {
51  setEventMessages($langs->trans('TooManyRecordForMassAction',$maxformassaction), null, 'errors');
52  $error++;
53 }
54 
55 if (! $error && $massaction == 'confirm_presend' && ! GETPOST('sendmail')) // If we do not choose button send (for example when we change template or limit), we must not send email, but keep on send email form
56 {
57  $massaction='presend';
58 }
59 if (! $error && $massaction == 'confirm_presend')
60 {
61  $resaction = '';
62  $nbsent = 0;
63  $nbignored = 0;
64  $langs->load("mails");
65  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
66 
67  $listofobjectid=array();
68  $listofobjectthirdparties=array();
69  $listofobjectref=array();
70 
71  if (! $error)
72  {
73  $thirdparty=new Societe($db);
74  if ($objecttmp->element == 'expensereport') $thirdparty=new User($db);
75 
76  $objecttmp=new $objectclass($db);
77  foreach($toselect as $toselectid)
78  {
79  $objecttmp=new $objectclass($db); // we must create new instance because instance is saved into $listofobjectref array for future use
80  $result=$objecttmp->fetch($toselectid);
81  if ($result > 0)
82  {
83  $listofobjectid[$toselectid]=$toselectid;
84  $thirdpartyid=$objecttmp->fk_soc?$objecttmp->fk_soc:$objecttmp->socid;
85  if ($objecttmp->element == 'societe') $thirdpartyid=$objecttmp->id;
86  if ($objecttmp->element == 'expensereport') $thirdpartyid=$objecttmp->fk_user_author;
87  $listofobjectthirdparties[$thirdpartyid]=$thirdpartyid;
88  $listofobjectref[$thirdpartyid][$toselectid]=$objecttmp;
89  }
90  }
91  }
92 
93  // Check mandatory parameters
94  if (empty($user->email))
95  {
96  $error++;
97  setEventMessages($langs->trans("NoSenderEmailDefined"), null, 'warnings');
98  $massaction='presend';
99  }
100 
101  $receiver=$_POST['receiver'];
102  if (! is_array($receiver))
103  {
104  if (empty($receiver) || $receiver == '-1') $receiver=array();
105  else $receiver=array($receiver);
106  }
107  if (! trim($_POST['sendto']) && count($receiver) == 0 && count($listofobjectthirdparties) == 1) // if only one recipient, receiver is mandatory
108  {
109  $error++;
110  setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Recipient")), null, 'warnings');
111  $massaction='presend';
112  }
113 
114  if (! GETPOST('subject','none'))
115  {
116  $error++;
117  setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("MailTopic")), null, 'warnings');
118  $massaction='presend';
119  }
120 
121  // Loop on each recipient/thirdparty
122  if (! $error)
123  {
124  foreach ($listofobjectthirdparties as $thirdpartyid)
125  {
126  $result = $thirdparty->fetch($thirdpartyid);
127  if ($result < 0)
128  {
129  dol_print_error($db);
130  exit;
131  }
132 
133  $sendto='';
134  $sendtocc='';
135  $sendtobcc='';
136  $sendtoid = array();
137 
138  // Define $sendto
139  $tmparray=array();
140  if (trim($_POST['sendto']))
141  {
142  // Recipients are provided into free text
143  $tmparray[] = trim($_POST['sendto']);
144  }
145  if (count($receiver)>0)
146  {
147  foreach($receiver as $key=>$val)
148  {
149  // Recipient was provided from combo list
150  if ($val == 'thirdparty') // Id of third party or user
151  {
152  $tmparray[] = $thirdparty->name.' <'.$thirdparty->email.'>';
153  }
154  elseif ($val && method_exists($thirdparty, 'contact_get_property')) // Id of contact
155  {
156  $tmparray[] = $thirdparty->contact_get_property((int) $val,'email');
157  $sendtoid[] = $val;
158  }
159  }
160  }
161  $sendto=implode(',',$tmparray);
162 
163  // Define $sendtocc
164  $receivercc=$_POST['receivercc'];
165  if (! is_array($receivercc))
166  {
167  if ($receivercc == '-1') $receivercc=array();
168  else $receivercc=array($receivercc);
169  }
170  $tmparray=array();
171  if (trim($_POST['sendtocc']))
172  {
173  $tmparray[] = trim($_POST['sendtocc']);
174  }
175  if (count($receivercc) > 0)
176  {
177  foreach($receivercc as $key=>$val)
178  {
179  // Recipient was provided from combo list
180  if ($val == 'thirdparty') // Id of third party
181  {
182  $tmparray[] = $thirdparty->name.' <'.$thirdparty->email.'>';
183  }
184  elseif ($val) // Id du contact
185  {
186  $tmparray[] = $thirdparty->contact_get_property((int) $val,'email');
187  //$sendtoid[] = $val; TODO Add also id of contact in CC ?
188  }
189  }
190  }
191  $sendtocc=implode(',',$tmparray);
192 
193  //var_dump($listofobjectref);exit;
194  $attachedfiles=array('paths'=>array(), 'names'=>array(), 'mimes'=>array());
195  $listofqualifiedobj=array();
196  $listofqualifiedref=array();
197  $thirdpartywithoutemail=array();
198 
199  foreach($listofobjectref[$thirdpartyid] as $objectid => $objectobj)
200  {
201  //var_dump($thirdpartyid.' - '.$objectid.' - '.$objectobj->statut);
202  if ($objectclass == 'Propal' && $objectobj->statut == Propal::STATUS_DRAFT)
203  {
204  $langs->load("errors");
205  $nbignored++;
206  $resaction.='<div class="error">'.$langs->trans('ErrorOnlyProposalNotDraftCanBeSentInMassAction',$objectobj->ref).'</div><br>';
207  continue; // Payment done or started or canceled
208  }
209  if ($objectclass == 'Commande' && $objectoj->statut == Commande::STATUS_DRAFT)
210  {
211  $langs->load("errors");
212  $nbignored++;
213  $resaction.='<div class="error">'.$langs->trans('ErrorOnlyOrderNotDraftCanBeSentInMassAction',$objectobj->ref).'</div><br>';
214  continue;
215  }
216  if ($objectclass == 'Facture' && $objectobj->statut != Facture::STATUS_VALIDATED)
217  {
218  $langs->load("errors");
219  $nbignored++;
220  $resaction.='<div class="error">'.$langs->trans('ErrorOnlyInvoiceValidatedCanBeSentInMassAction',$objectobj->ref).'</div><br>';
221  continue; // Payment done or started or canceled
222  }
223 
224  // Test recipient
225  if (empty($sendto)) // For the case, no recipient were set (multi thirdparties send)
226  {
227  if ($objectobj->element == 'expensereport')
228  {
229  $fuser = new User($db);
230  $fuser->fetch($objectobj->fk_user_author);
231  $sendto = $fuser->email;
232  }
233  else
234  {
235  $objectobj->fetch_thirdparty();
236  $sendto = $objectobj->thirdparty->email;
237  }
238  }
239 
240  if (empty($sendto))
241  {
242  //print "No recipient for thirdparty ".$objectobj->thirdparty->name;
243  $nbignored++;
244  if (empty($thirdpartywithoutemail[$objectobj->thirdparty->id]))
245  {
246  $resaction.='<div class="error">'.$langs->trans('NoRecipientEmail',$objectobj->thirdparty->name).'</div><br>';
247  }
248  dol_syslog('No recipient for thirdparty: '.$objectobj->thirdparty->name, LOG_WARNING);
249  $thirdpartywithoutemail[$objectobj->thirdparty->id]=1;
250  continue;
251  }
252 
253  if ($_POST['addmaindocfile'])
254  {
255  // TODO Use future field $objectobj->fullpathdoc to know where is stored default file
256  // TODO If not defined, use $objectobj->modelpdf (or defaut invoice config) to know what is template to use to regenerate doc.
257  $filename=dol_sanitizeFileName($objectobj->ref).'.pdf';
258  $filedir=$uploaddir . '/' . dol_sanitizeFileName($objectobj->ref);
259  $file = $filedir . '/' . $filename;
260  $mime = dol_mimetype($file);
261 
262  if (dol_is_file($file))
263  {
264  // Create form object
265  $attachedfiles=array(
266  'paths'=>array_merge($attachedfiles['paths'],array($file)),
267  'names'=>array_merge($attachedfiles['names'],array($filename)),
268  'mimes'=>array_merge($attachedfiles['mimes'],array($mime))
269  );
270  }
271  else
272  {
273  $nbignored++;
274  $langs->load("errors");
275  $resaction.='<div class="error">'.$langs->trans('ErrorCantReadFile',$file).'</div><br>';
276  dol_syslog('Failed to read file: '.$file, LOG_WARNING);
277  continue;
278  }
279  }
280 
281  // Object of thirdparty qualified
282  $listofqualifiedobj[$objectid]=$objectobj;
283  $listofqualifiedref[$objectid]=$objectobj->ref;
284 
285 
286  //var_dump($listofqualifiedref);
287  }
288 
289  // Send email if there is at least one qualified record
290  if (count($listofqualifiedobj) > 0)
291  {
292  $langs->load("commercial");
293 
294  $fromtype = GETPOST('fromtype');
295  if ($fromtype === 'user') {
296  $from = $user->getFullName($langs) .' <'.$user->email.'>';
297  }
298  elseif ($fromtype === 'company') {
299  $from = $conf->global->MAIN_INFO_SOCIETE_NOM .' <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>';
300  }
301  elseif (preg_match('/user_aliases_(\d+)/', $fromtype, $reg)) {
302  $tmp=explode(',', $user->email_aliases);
303  $from = trim($tmp[($reg[1] - 1)]);
304  }
305  elseif (preg_match('/global_aliases_(\d+)/', $fromtype, $reg)) {
306  $tmp=explode(',', $conf->global->MAIN_INFO_SOCIETE_MAIL_ALIASES);
307  $from = trim($tmp[($reg[1] - 1)]);
308  }
309  elseif (preg_match('/senderprofile_(\d+)_(\d+)/', $fromtype, $reg)) {
310  $sql='SELECT rowid, label, email FROM '.MAIN_DB_PREFIX.'c_email_senderprofile WHERE rowid = '.(int) $reg[1];
311  $resql = $db->query($sql);
312  $obj = $db->fetch_object($resql);
313  if ($obj)
314  {
315  $from = $obj->label.' <'.$obj->email.'>';
316  }
317  }
318  else {
319  $from = $_POST['fromname'] . ' <' . $_POST['frommail'] .'>';
320  }
321 
322  $replyto = $from;
323  $subject = GETPOST('subject','none');
324  $message = GETPOST('message','none');
325 
326  $sendtobcc = GETPOST('sendtoccc');
327  if ($objectclass == 'Propale') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_PROPOSAL_TO) ? '' : (($sendtobcc?", ":"").$conf->global->MAIN_MAIL_AUTOCOPY_PROPOSAL_TO));
328  if ($objectclass == 'Commande') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_ORDER_TO) ? '' : (($sendtobcc?", ":"").$conf->global->MAIN_MAIL_AUTOCOPY_ORDER_TO));
329  if ($objectclass == 'Facture') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_INVOICE_TO) ? '' : (($sendtobcc?", ":"").$conf->global->MAIN_MAIL_AUTOCOPY_INVOICE_TO));
330  if ($objectclass == 'Supplier_Proposal') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO) ? '' : (($sendtobcc?", ":"").$conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO));
331  if ($objectclass == 'CommandeFournisseur') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO) ? '' : (($sendtobcc?", ":"").$conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO));
332  if ($objectclass == 'FactureFournisseur') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO) ? '' : (($sendtobcc?", ":"").$conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO));
333 
334  // $listofqualifiedobj is array with key = object id of qualified objects for the current thirdparty
335  $oneemailperrecipient=(GETPOST('oneemailperrecipient')=='on'?1:0);
336  $looparray=array();
337  if (! $oneemailperrecipient)
338  {
339  $looparray = $listofqualifiedobj;
340  }
341  else
342  {
343  $objectforloop=new $objectclass($db);
344  $objectforloop->thirdparty = $thirdparty;
345  $looparray[0]=$objectforloop;
346  }
347  //var_dump($looparray);exit;
348 
349  foreach ($looparray as $objecttmp) // $objecttmp is a real object or an empty if we choose to send one email per thirdparty instead of per record
350  {
351  // Make substitution in email content
352  $substitutionarray=getCommonSubstitutionArray($langs, 0, null, $objecttmp);
353  $substitutionarray['__ID__'] = ($oneemailperrecipient ? join(', ',array_keys($listofqualifiedobj)) : $objecttmp->id);
354  $substitutionarray['__REF__'] = ($oneemailperrecipient ? join(', ',$listofqualifiedref) : $objecttmp->ref);
355  $substitutionarray['__EMAIL__'] = $thirdparty->email;
356  $substitutionarray['__CHECK_READ__'] = '<img src="'.DOL_MAIN_URL_ROOT.'/public/emailing/mailing-read.php?tag='.$thirdparty->tag.'&securitykey='.urlencode($conf->global->MAILING_EMAIL_UNSUBSCRIBE_KEY).'" width="1" height="1" style="width:1px;height:1px" border="0"/>';
357 
358  $parameters=array('mode'=>'formemail');
359  complete_substitutions_array($substitutionarray, $langs, $objecttmp, $parameters);
360 
361  $subject=make_substitutions($subject, $substitutionarray);
362  $message=make_substitutions($message, $substitutionarray);
363 
364  $filepath = $attachedfiles['paths'];
365  $filename = $attachedfiles['names'];
366  $mimetype = $attachedfiles['mimes'];
367 
368  //var_dump($filepath);
369 
370  // Send mail (substitutionarray must be done just before this)
371  require_once(DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php');
372  $mailfile = new CMailFile($subject,$sendto,$from,$message,$filepath,$mimetype,$filename,$sendtocc,$sendtobcc,$deliveryreceipt,-1);
373  if ($mailfile->error)
374  {
375  $resaction.='<div class="error">'.$mailfile->error.'</div>';
376  }
377  else
378  {
379  $result=$mailfile->sendfile();
380  if ($result)
381  {
382  $resaction.=$langs->trans('MailSuccessfulySent',$mailfile->getValidAddress($from,2),$mailfile->getValidAddress($sendto,2)).'<br>'; // Must not contain "
383 
384  $error=0;
385 
386  // Insert logs into agenda
387  foreach($listofqualifiedobj as $objid => $objectobj)
388  {
389  /*if ($objectclass == 'Propale') $actiontypecode='AC_PROP';
390  if ($objectclass == 'Commande') $actiontypecode='AC_COM';
391  if ($objectclass == 'Facture') $actiontypecode='AC_FAC';
392  if ($objectclass == 'Supplier_Proposal') $actiontypecode='AC_SUP_PRO';
393  if ($objectclass == 'CommandeFournisseur') $actiontypecode='AC_SUP_ORD';
394  if ($objectclass == 'FactureFournisseur') $actiontypecode='AC_SUP_INV';*/
395 
396  $actionmsg=$langs->transnoentities('MailSentBy').' '.$from.' '.$langs->transnoentities('To').' '.$sendto;
397  if ($message)
398  {
399  if ($sendtocc) $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('Bcc') . ": " . $sendtocc);
400  $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('MailTopic') . ": " . $subject);
401  $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('TextUsedInTheMessageBody') . ":");
402  $actionmsg = dol_concatdesc($actionmsg, $message);
403  }
404  $actionmsg2='';
405 
406  // Initialisation donnees
407  $objectobj->sendtoid = 0;
408  $objectobj->actionmsg = $actionmsg; // Long text
409  $objectobj->actionmsg2 = $actionmsg2; // Short text
410  $objectobj->fk_element = $objid;
411  $objectobj->elementtype = $objectobj->element;
412 
413  $triggername = strtoupper(get_class($objectobj)) .'_SENTBYMAIL';
414  if ($triggername == 'SOCIETE_SENTBYMAIL') $triggername = 'COMPANY_SENTBYEMAIL';
415  if ($triggername == 'CONTRAT_SENTBYMAIL') $triggername = 'CONTRACT_SENTBYEMAIL';
416  if ($triggername == 'COMMANDE_SENTBYMAIL') $triggername = 'ORDER_SENTBYEMAIL';
417  if ($triggername == 'FACTURE_SENTBYMAIL') $triggername = 'BILL_SENTBYEMAIL';
418  if ($triggername == 'EXPEDITION_SENTBYMAIL') $triggername = 'SHIPPING_SENTBYEMAIL';
419  if ($triggername == 'COMMANDEFOURNISSEUR_SENTBYMAIL') $triggername = 'ORDER_SUPPLIER_SENTBYMAIL';
420  if ($triggername == 'FACTUREFOURNISSEUR_SENTBYMAIL') $triggername = 'BILL_SUPPLIER_SENTBYEMAIL';
421  if ($triggername == 'SUPPLIERPROPOSAL_SENTBYMAIL') $triggername = 'PROPOSAL_SUPPLIER_SENTBYEMAIL';
422 
423  if (! empty($trigger_name))
424  {
425  // Appel des triggers
426  include_once(DOL_DOCUMENT_ROOT . "/core/class/interfaces.class.php");
427  $interface=new Interfaces($db);
428  $result=$interface->run_triggers($trigger_name, $objectobj, $user, $langs, $conf);
429  if ($result < 0) { $error++; $errors=$interface->errors; }
430  // Fin appel triggers
431 
432  if ($error)
433  {
434  setEventMessages($db->lasterror(), $errors, 'errors');
435  dol_syslog("Error in trigger ".$trigger_name.' '.$db->lasterror(), LOG_ERR);
436  }
437  }
438 
439  $nbsent++;
440  }
441  }
442  else
443  {
444  $langs->load("other");
445  if ($mailfile->error)
446  {
447  $resaction.=$langs->trans('ErrorFailedToSendMail',$from,$sendto);
448  $resaction.='<br><div class="error">'.$mailfile->error.'</div>';
449  }
450  else
451  {
452  $resaction.='<div class="warning">No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS</div>';
453  }
454  }
455  }
456  }
457  }
458  }
459 
460  $resaction.=($resaction?'<br>':$resaction);
461  $resaction.='<strong>'.$langs->trans("ResultOfMailSending").':</strong><br>'."\n";
462  $resaction.=$langs->trans("NbSelected").': '.count($toselect)."\n<br>";
463  $resaction.=$langs->trans("NbIgnored").': '.($nbignored?$nbignored:0)."\n<br>";
464  $resaction.=$langs->trans("NbSent").': '.($nbsent?$nbsent:0)."\n<br>";
465 
466  if ($nbsent)
467  {
468  $action=''; // Do not show form post if there was at least one successfull sent
469  //setEventMessages($langs->trans("EMailSentToNRecipients", $nbsent.'/'.count($toselect)), null, 'mesgs');
470  setEventMessages($langs->trans("EMailSentForNElements", $nbsent.'/'.count($toselect)), null, 'mesgs');
471  setEventMessages($resaction, null, 'mesgs');
472  }
473  else
474  {
475  //setEventMessages($langs->trans("EMailSentToNRecipients", 0), null, 'warnings'); // May be object has no generated PDF file
476  setEventMessages($resaction, null, 'warnings');
477  }
478 
479  $action='list';
480  $massaction='';
481  }
482 }
483 
484 if ($massaction == 'confirm_createbills')
485 {
486  $orders = GETPOST('toselect','array');
487  $createbills_onebythird = GETPOST('createbills_onebythird', 'int');
488  $validate_invoices = GETPOST('valdate_invoices', 'int');
489 
490  $TFact = array();
491  $TFactThird = array();
492 
493  $nb_bills_created = 0;
494 
495  $db->begin();
496 
497  foreach($orders as $id_order)
498  {
499  $cmd = new Commande($db);
500  if ($cmd->fetch($id_order) <= 0) continue;
501 
502  $objecttmp = new Facture($db);
503  if (!empty($createbills_onebythird) && !empty($TFactThird[$cmd->socid])) $objecttmp = $TFactThird[$cmd->socid]; // If option "one bill per third" is set, we use already created order.
504  else {
505 
506  $objecttmp->socid = $cmd->socid;
507  $objecttmp->type = Facture::TYPE_STANDARD;
508  $objecttmp->cond_reglement_id = $cmd->cond_reglement_id;
509  $objecttmp->mode_reglement_id = $cmd->mode_reglement_id;
510  $objecttmp->fk_project = $cmd->fk_project;
511 
512  $datefacture = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']);
513  if (empty($datefacture))
514  {
515  $datefacture = dol_mktime(date("h"), date("M"), 0, date("m"), date("d"), date("Y"));
516  }
517 
518  $objecttmp->date = $datefacture;
519  $objecttmp->origin = 'commande';
520  $objecttmp->origin_id = $id_order;
521 
522  $res = $objecttmp->create($user);
523 
524  if($res > 0) $nb_bills_created++;
525  }
526 
527  if ($objecttmp->id > 0)
528  {
529  $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
530  $sql.= "fk_source";
531  $sql.= ", sourcetype";
532  $sql.= ", fk_target";
533  $sql.= ", targettype";
534  $sql.= ") VALUES (";
535  $sql.= $id_order;
536  $sql.= ", '".$objecttmp->origin."'";
537  $sql.= ", ".$objecttmp->id;
538  $sql.= ", '".$objecttmp->element."'";
539  $sql.= ")";
540 
541  if (! $db->query($sql))
542  {
543  $error++;
544  }
545 
546  if (! $error)
547  {
548  $lines = $cmd->lines;
549  if (empty($lines) && method_exists($cmd, 'fetch_lines'))
550  {
551  $cmd->fetch_lines();
552  $lines = $cmd->lines;
553  }
554 
555  $fk_parent_line=0;
556  $num=count($lines);
557 
558  for ($i=0;$i<$num;$i++)
559  {
560  $desc=($lines[$i]->desc?$lines[$i]->desc:$lines[$i]->libelle);
561  if ($lines[$i]->subprice < 0)
562  {
563  // Negative line, we create a discount line
564  $discount = new DiscountAbsolute($db);
565  $discount->fk_soc=$objecttmp->socid;
566  $discount->amount_ht=abs($lines[$i]->total_ht);
567  $discount->amount_tva=abs($lines[$i]->total_tva);
568  $discount->amount_ttc=abs($lines[$i]->total_ttc);
569  $discount->tva_tx=$lines[$i]->tva_tx;
570  $discount->fk_user=$user->id;
571  $discount->description=$desc;
572  $discountid=$discount->create($user);
573  if ($discountid > 0)
574  {
575  $result=$objecttmp->insert_discount($discountid);
576  //$result=$discount->link_to_invoice($lineid,$id);
577  }
578  else
579  {
580  setEventMessages($discount->error, $discount->errors, 'errors');
581  $error++;
582  break;
583  }
584  }
585  else
586  {
587  // Positive line
588  $product_type=($lines[$i]->product_type?$lines[$i]->product_type:0);
589  // Date start
590  $date_start=false;
591  if ($lines[$i]->date_debut_prevue) $date_start=$lines[$i]->date_debut_prevue;
592  if ($lines[$i]->date_debut_reel) $date_start=$lines[$i]->date_debut_reel;
593  if ($lines[$i]->date_start) $date_start=$lines[$i]->date_start;
594  //Date end
595  $date_end=false;
596  if ($lines[$i]->date_fin_prevue) $date_end=$lines[$i]->date_fin_prevue;
597  if ($lines[$i]->date_fin_reel) $date_end=$lines[$i]->date_fin_reel;
598  if ($lines[$i]->date_end) $date_end=$lines[$i]->date_end;
599  // Reset fk_parent_line for no child products and special product
600  if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9)
601  {
602  $fk_parent_line = 0;
603  }
604  $result = $objecttmp->addline(
605  $desc,
606  $lines[$i]->subprice,
607  $lines[$i]->qty,
608  $lines[$i]->tva_tx,
609  $lines[$i]->localtax1_tx,
610  $lines[$i]->localtax2_tx,
611  $lines[$i]->fk_product,
612  $lines[$i]->remise_percent,
613  $date_start,
614  $date_end,
615  0,
616  $lines[$i]->info_bits,
617  $lines[$i]->fk_remise_except,
618  'HT',
619  0,
620  $product_type,
621  $ii,
622  $lines[$i]->special_code,
623  $objecttmp->origin,
624  $lines[$i]->rowid,
625  $fk_parent_line,
626  $lines[$i]->fk_fournprice,
627  $lines[$i]->pa_ht,
628  $lines[$i]->label
629  );
630  if ($result > 0)
631  {
632  $lineid=$result;
633  }
634  else
635  {
636  $lineid=0;
637  $error++;
638  break;
639  }
640  // Defined the new fk_parent_line
641  if ($result > 0 && $lines[$i]->product_type == 9)
642  {
643  $fk_parent_line = $result;
644  }
645  }
646  }
647  }
648  }
649 
650  //$cmd->classifyBilled($user); // Disabled. This behavior must be set or not using the workflow module.
651 
652  if(!empty($createbills_onebythird) && empty($TFactThird[$cmd->socid])) $TFactThird[$cmd->socid] = $objecttmp;
653  else $TFact[$objecttmp->id] = $objecttmp;
654  }
655 
656  // Build doc with all invoices
657  $TAllFact = empty($createbills_onebythird) ? $TFact : $TFactThird;
658  $toselect = array();
659 
660  if (! $error && $validate_invoices)
661  {
662  $massaction = $action = 'builddoc';
663  foreach($TAllFact as &$objecttmp)
664  {
665  $result = $objecttmp->validate($user);
666  if ($result <= 0)
667  {
668  $error++;
669  setEventMessages($objecttmp->error, $objecttmp->errors, 'errors');
670  break;
671  }
672 
673  $id = $objecttmp->id; // For builddoc action
674 
675  // Builddoc
676  $donotredirect = 1;
677  $upload_dir = $conf->facture->dir_output;
678  $permissioncreate=$user->rights->facture->creer;
679  include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
680  }
681 
682  $massaction = $action = 'confirm_createbills';
683  }
684 
685  if (! $error)
686  {
687  $db->commit();
688  setEventMessage($langs->trans('BillCreated', $nb_bills_created));
689  }
690  else
691  {
692  $db->rollback();
693  $action='create';
694  $_GET["origin"]=$_POST["origin"];
695  $_GET["originid"]=$_POST["originid"];
696  setEventMessages("Error", null, 'errors');
697  $error++;
698  }
699 }
700 
701 if (! $error && $massaction == "builddoc" && $permtoread && ! GETPOST('button_search'))
702 {
703  if (empty($diroutputmassaction))
704  {
705  dol_print_error(null, 'include of actions_massactions.inc.php is done but var $diroutputmassaction was not defined');
706  exit;
707  }
708 
709  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
710  require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php';
711  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
712 
713  $objecttmp=new $objectclass($db);
714  $listofobjectid=array();
715  $listofobjectthirdparties=array();
716  $listofobjectref=array();
717  foreach($toselect as $toselectid)
718  {
719  $objecttmp=new $objectclass($db); // must create new instance because instance is saved into $listofobjectref array for future use
720  $result=$objecttmp->fetch($toselectid);
721  if ($result > 0)
722  {
723  $listofobjectid[$toselectid]=$toselectid;
724  $thirdpartyid=$objecttmp->fk_soc?$objecttmp->fk_soc:$objecttmp->socid;
725  $listofobjectthirdparties[$thirdpartyid]=$thirdpartyid;
726  $listofobjectref[$toselectid]=$objecttmp->ref;
727  }
728  }
729 
730  $arrayofinclusion=array();
731  foreach($listofobjectref as $tmppdf) $arrayofinclusion[]='^'.preg_quote(dol_sanitizeFileName($tmppdf).'.pdf','/').'$';
732  $listoffiles = dol_dir_list($uploaddir,'all',1,implode('|',$arrayofinclusion),'\.meta$|\.png','date',SORT_DESC,0,true);
733 
734  // build list of files with full path
735  $files = array();
736  foreach($listofobjectref as $basename)
737  {
738  $basename = dol_sanitizeFileName($basename);
739  foreach($listoffiles as $filefound)
740  {
741  if (strstr($filefound["name"],$basename))
742  {
743  $files[] = $uploaddir.'/'.$basename.'/'.$filefound["name"];
744  break;
745  }
746  }
747  }
748 
749  // Define output language (Here it is not used because we do only merging existing PDF)
750  $outputlangs = $langs;
751  $newlang='';
752  if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id','aZ09')) $newlang=GETPOST('lang_id','aZ09');
753  if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang=$objecttmp->thirdparty->default_lang;
754  if (! empty($newlang))
755  {
756  $outputlangs = new Translate("",$conf);
757  $outputlangs->setDefaultLang($newlang);
758  }
759 
760  if (!empty($conf->global->USE_PDFTK_FOR_PDF_CONCAT))
761  {
762  // Create output dir if not exists
763  dol_mkdir($diroutputmassaction);
764 
765  // Defined name of merged file
766  $filename=strtolower(dol_sanitizeFileName($langs->transnoentities($objectlabel)));
767  $filename=preg_replace('/\s/','_',$filename);
768 
769  // Save merged file
770  if (in_array($objecttmp->element, array('facture', 'facture_fournisseur')) && $search_status == Facture::STATUS_VALIDATED)
771  {
772  if ($option=='late') $filename.='_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Unpaid"))).'_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Late")));
773  else $filename.='_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Unpaid")));
774  }
775  if ($year) $filename.='_'.$year;
776  if ($month) $filename.='_'.$month;
777 
778  if (count($files)>0)
779  {
780  $now=dol_now();
781  $file=$diroutputmassaction.'/'.$filename.'_'.dol_print_date($now,'dayhourlog').'.pdf';
782 
783  $input_files = '';
784  foreach($files as $f) {
785  $input_files.=' '.escapeshellarg($f);
786  }
787 
788  $cmd = 'pdftk '.escapeshellarg($input_files).' cat output '.escapeshellarg($file);
789  exec($cmd);
790 
791  if (! empty($conf->global->MAIN_UMASK))
792  @chmod($file, octdec($conf->global->MAIN_UMASK));
793 
794  $langs->load("exports");
795  setEventMessages($langs->trans('FileSuccessfullyBuilt',$filename.'_'.dol_print_date($now,'dayhourlog')), null, 'mesgs');
796  }
797  else
798  {
799  setEventMessages($langs->trans('NoPDFAvailableForDocGenAmongChecked'), null, 'errors');
800  }
801  }
802  else {
803  // Create empty PDF
804  $formatarray=pdf_getFormat();
805  $page_largeur = $formatarray['width'];
806  $page_hauteur = $formatarray['height'];
807  $format = array($page_largeur,$page_hauteur);
808 
809  $pdf=pdf_getInstance($format);
810 
811  if (class_exists('TCPDF'))
812  {
813  $pdf->setPrintHeader(false);
814  $pdf->setPrintFooter(false);
815  }
816  $pdf->SetFont(pdf_getPDFFont($outputlangs));
817 
818  if (! empty($conf->global->MAIN_DISABLE_PDF_COMPRESSION)) $pdf->SetCompression(false);
819 
820  // Add all others
821  foreach($files as $file)
822  {
823  // Charge un document PDF depuis un fichier.
824  $pagecount = $pdf->setSourceFile($file);
825  for ($i = 1; $i <= $pagecount; $i++)
826  {
827  $tplidx = $pdf->importPage($i);
828  $s = $pdf->getTemplatesize($tplidx);
829  $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L');
830  $pdf->useTemplate($tplidx);
831  }
832  }
833 
834  // Create output dir if not exists
835  dol_mkdir($diroutputmassaction);
836 
837  // Defined name of merged file
838  $filename=strtolower(dol_sanitizeFileName($langs->transnoentities($objectlabel)));
839  $filename=preg_replace('/\s/','_',$filename);
840 
841  // Save merged file
842  if (in_array($objecttmp->element, array('facture', 'facture_fournisseur')) && $search_status == Facture::STATUS_VALIDATED)
843  {
844  if ($option=='late') $filename.='_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Unpaid"))).'_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Late")));
845  else $filename.='_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Unpaid")));
846  }
847  if ($year) $filename.='_'.$year;
848  if ($month) $filename.='_'.$month;
849  if ($pagecount)
850  {
851  $now=dol_now();
852  $file=$diroutputmassaction.'/'.$filename.'_'.dol_print_date($now,'dayhourlog').'.pdf';
853  $pdf->Output($file,'F');
854  if (! empty($conf->global->MAIN_UMASK))
855  @chmod($file, octdec($conf->global->MAIN_UMASK));
856 
857  $langs->load("exports");
858  setEventMessages($langs->trans('FileSuccessfullyBuilt',$filename.'_'.dol_print_date($now,'dayhourlog')), null, 'mesgs');
859  }
860  else
861  {
862  setEventMessages($langs->trans('NoPDFAvailableForDocGenAmongChecked'), null, 'errors');
863  }
864  }
865 }
866 
867 // Remove a file from massaction area
868 if ($action == 'remove_file')
869 {
870  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
871 
872  $langs->load("other");
873  $upload_dir = $diroutputmassaction;
874  $file = $upload_dir . '/' . GETPOST('file');
875  $ret=dol_delete_file($file);
876  if ($ret) setEventMessages($langs->trans("FileWasRemoved", GETPOST('file')), null, 'mesgs');
877  else setEventMessages($langs->trans("ErrorFailToDeleteFile", GETPOST('file')), null, 'errors');
878  $action='';
879 }
880 
881 // Validate records
882 if (! $error && $massaction == 'validate' && $permtocreate)
883 {
884  $objecttmp=new $objectclass($db);
885 
886  if ($objecttmp->element == 'invoice' && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_BILL))
887  {
888  $langs->load("errors");
889  setEventMessages($langs->trans('ErrorMassValidationNotAllowedWhenStockIncreaseOnAction'), null, 'errors');
890  $error++;
891  }
892  if ($objecttmp->element == 'invoice_supplier' && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL))
893  {
894  $langs->load("errors");
895  setEventMessages($langs->trans('ErrorMassValidationNotAllowedWhenStockIncreaseOnAction'), null, 'errors');
896  $error++;
897  }
898  if (! $error)
899  {
900  $db->begin();
901 
902  $nbok = 0;
903  foreach($toselect as $toselectid)
904  {
905  $result=$objecttmp->fetch($toselectid);
906  if ($result > 0)
907  {
908  //if (in_array($objecttmp->element, array('societe','member'))) $result = $objecttmp->delete($objecttmp->id, $user, 1);
909  //else
910  $result = $objecttmp->validate($user);
911  if ($result == 0)
912  {
913  $langs->load("errors");
914  setEventMessages($langs->trans("ErrorObjectMustHaveStatusDraftToBeValidated", $objecttmp->ref), null, 'errors');
915  $error++;
916  break;
917  }
918  elseif ($result < 0)
919  {
920  setEventMessages($objecttmp->error, $objecttmp->errors, 'errors');
921  $error++;
922  break;
923  }
924  else $nbok++;
925  }
926  else
927  {
928  setEventMessages($objecttmp->error, $objecttmp->errors, 'errors');
929  $error++;
930  break;
931  }
932  }
933 
934  if (! $error)
935  {
936  if ($nbok > 1) setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs');
937  else setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs');
938  $db->commit();
939  }
940  else
941  {
942  $db->rollback();
943  }
944  //var_dump($listofobjectthirdparties);exit;
945  }
946 }
947 
948 // Delete record from mass action (massaction = 'delete' for direct delete, action/confirm='delete'/'yes' with a confirmation step before)
949 if (! $error && ($massaction == 'delete' || ($action == 'delete' && $confirm == 'yes')) && $permtodelete)
950 {
951  $db->begin();
952 
953  $objecttmp=new $objectclass($db);
954  $nbok = 0;
955  foreach($toselect as $toselectid)
956  {
957  $result=$objecttmp->fetch($toselectid);
958  if ($result > 0)
959  {
960  // Refuse deletion for some status ?
961  /*
962  if ($objectclass == 'Facture' && $objecttmp->status == Facture::STATUS_DRAFT)
963  {
964  $langs->load("errors");
965  $nbignored++;
966  $resaction.='<div class="error">'.$langs->trans('ErrorOnlyDraftStatusCanBeDeletedInMassAction',$objecttmp->ref).'</div><br>';
967  continue;
968  }*/
969 
970  if (in_array($objecttmp->element, array('societe','member'))) $result = $objecttmp->delete($objecttmp->id, $user, 1);
971  else $result = $objecttmp->delete($user);
972 
973  if ($result <= 0)
974  {
975  setEventMessages($objecttmp->error, $objecttmp->errors, 'errors');
976  $error++;
977  break;
978  }
979  else $nbok++;
980  }
981  else
982  {
983  setEventMessages($objecttmp->error, $objecttmp->errors, 'errors');
984  $error++;
985  break;
986  }
987  }
988 
989  if (! $error)
990  {
991  if ($nbok > 1) setEventMessages($langs->trans("RecordsDeleted", $nbok), null, 'mesgs');
992  else setEventMessages($langs->trans("RecordDeleted", $nbok), null, 'mesgs');
993  $db->commit();
994  }
995  else
996  {
997  $db->rollback();
998  }
999  //var_dump($listofobjectthirdparties);exit;
1000 }
1001 
1002 $parameters['toselect']=$toselect;
1003 $parameters['uploaddir']=$uploaddir;
1004 
1005 $reshook=$hookmanager->executeHooks('doMassActions',$parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
1006 if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1007 
1008 
1009 
pdf_getInstance($format='', $metric='mm', $pagetype='P')
Return a PDF instance object.
Definition: pdf.lib.php:82
const TYPE_STANDARD
Standard invoice.
setEventMessages($mesg, $mesgs, $style='mesgs')
Set event messages in dol_events session object.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm=false, $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
pdf_getFormat(Translate $outputlangs=null)
Return array with format properties of default PDF format.
Definition: pdf.lib.php:42
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return mime type of a file.
Class to manage Dolibarr users.
Definition: user.class.php:39
dol_print_error($db='', $error='', $errors=null)
Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remonte...
pdf_getPDFFont($outputlangs)
Return font name to use for PDF generation.
Definition: pdf.lib.php:233
dol_concatdesc($text1, $text2, $forxml=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="")
Scan a directory and return a list of files/directories.
Definition: files.lib.php:58
GETPOST($paramname, $check='none', $method=0, $filter=NULL, $options=NULL, $noreplace=0)
Return value of a param into GET or POST supervariable.
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
Return array of possible common substitutions.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='')
Write log message into outputs.
Class to manage third parties objects (customers, suppliers, prospects...)
const STATUS_VALIDATED
Validated (need to be paid)
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,$sendto,$replyto,$message,$filepath,$mimetype,$filename,$cc,$ccc,$deliveryreceipt,$msgishtml,$errors_to,$css,$trackid,$moreinheader,$sendcontext); $mailfile->sendfile();.
Class to manage customers orders.
const STATUS_DRAFT
Draft status.
Class to manage translations.
dol_now($mode='gmt')
Return date for now.
dol_is_file($pathoffile)
Return if path is a file.
Definition: files.lib.php:427
const STATUS_DRAFT
Draft status.
dol_print_date($time, $format='', $tzoutput='tzserver', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
setEventMessage($mesgs, $style='mesgs')
Set event message in dol_events session object.
dol_mkdir($dir, $dataroot='', $newmask=null)
Creation of a directory (this can create recursive subdir)
if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if(!empty($conf->fournisseur->enabled)&&$user->rights->fournisseur->facture->lire) if(!empty($conf->don->enabled)&&$user->rights->societe->lire) if(!empty($conf->tax->enabled)&&$user->rights->tax->charges->lire) if(!empty($conf->facture->enabled)&&!empty($conf->commande->enabled)&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if(!empty($conf->fournisseur->enabled)&&$user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1013
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null)
Remove a file or several files with a mask.
Definition: files.lib.php:1103
Class to manage absolute discounts.
make_substitutions($text, $substitutionarray, $outputlangs=null)
Make substition into a text string, replacing keys with vals from $substitutionarray (oldval=>newval)...
Class to manage invoices.
Class to manage triggers.
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...