1<?php 2/* Copyright (C) 2015-2017 Laurent Destailleur <eldy@users.sourceforge.net> 3 * Copyright (C) 2018-2021 Nicolas ZABOURI <info@inovea-conseil.com> 4 * Copyright (C) 2018 Juanjo Menent <jmenent@2byte.es> 5 * Copyright (C) 2019 Ferran Marcet <fmarcet@2byte.es> 6 * Copyright (C) 2019-2021 Frédéric France <frederic.france@netlogic.fr> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program. If not, see <https://www.gnu.org/licenses/>. 20 * or see https://www.gnu.org/ 21 */ 22 23/** 24 * \file htdocs/core/actions_massactions.inc.php 25 * \brief Code for actions done with massaction button (send by email, merge pdf, delete, ...) 26 */ 27 28 29// $massaction must be defined 30// $objectclass and $objectlabel must be defined 31// $parameters, $object, $action must be defined for the hook. 32 33// $permissiontoread, $permissiontoadd, $permissiontodelete, $permissiontoclose may be defined 34// $uploaddir may be defined (example to $conf->projet->dir_output."/";) 35// $toselect may be defined 36// $diroutputmassaction may be defined 37 38 39// Protection 40if (empty($objectclass) || empty($uploaddir)) 41{ 42 dol_print_error(null, 'include of actions_massactions.inc.php is done but var $objectclass or $uploaddir was not defined'); 43 exit; 44} 45 46// For backward compatibility 47if (!empty($permtoread) && empty($permissiontoread)) $permissiontoread = $permtoread; 48if (!empty($permtocreate) && empty($permissiontoadd)) $permissiontoadd = $permtocreate; 49if (!empty($permtodelete) && empty($permissiontodelete)) $permissiontodelete = $permtodelete; 50 51 52// Mass actions. Controls on number of lines checked. 53$maxformassaction = (empty($conf->global->MAIN_LIMIT_FOR_MASS_ACTIONS) ? 1000 : $conf->global->MAIN_LIMIT_FOR_MASS_ACTIONS); 54if (!empty($massaction) && is_array($toselect) && count($toselect) < 1) 55{ 56 $error++; 57 setEventMessages($langs->trans("NoRecordSelected"), null, "warnings"); 58} 59if (!$error && is_array($toselect) && count($toselect) > $maxformassaction) 60{ 61 setEventMessages($langs->trans('TooManyRecordForMassAction', $maxformassaction), null, 'errors'); 62 $error++; 63} 64 65if (!$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 66{ 67 $massaction = 'presend'; 68} 69if (!$error && $massaction == 'confirm_presend') 70{ 71 $resaction = ''; 72 $nbsent = 0; 73 $nbignored = 0; 74 $langs->load("mails"); 75 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 76 77 $listofobjectid = array(); 78 $listofobjectthirdparties = array(); 79 $listofobjectcontacts = array(); 80 $listofobjectref = array(); 81 $contactidtosend = array(); 82 $attachedfilesThirdpartyObj = array(); 83 $oneemailperrecipient = (GETPOST('oneemailperrecipient') == 'on' ? 1 : 0); 84 85 if (!$error) 86 { 87 $thirdparty = new Societe($db); 88 89 $objecttmp = new $objectclass($db); 90 if ($objecttmp->element == 'expensereport') $thirdparty = new User($db); 91 if ($objecttmp->element == 'holiday') $thirdparty = new User($db); 92 93 foreach ($toselect as $toselectid) 94 { 95 $objecttmp = new $objectclass($db); // we must create new instance because instance is saved into $listofobjectref array for future use 96 $result = $objecttmp->fetch($toselectid); 97 if ($result > 0) 98 { 99 $listofobjectid[$toselectid] = $toselectid; 100 101 $thirdpartyid = ($objecttmp->fk_soc ? $objecttmp->fk_soc : $objecttmp->socid); 102 if ($objecttmp->element == 'societe') $thirdpartyid = $objecttmp->id; 103 if ($objecttmp->element == 'expensereport') $thirdpartyid = $objecttmp->fk_user_author; 104 if ($objecttmp->element == 'holiday') $thirdpartyid = $objecttmp->fk_user; 105 if (empty($thirdpartyid)) $thirdpartyid = 0; 106 107 if ($objectclass == 'Facture') { 108 $tmparraycontact = array(); 109 $tmparraycontact = $objecttmp->liste_contact(-1, 'external', 0, 'BILLING'); 110 if (is_array($tmparraycontact) && count($tmparraycontact) > 0) { 111 foreach ($tmparraycontact as $data_email) { 112 $listofobjectcontacts[$toselectid][$data_email['id']] = $data_email['email']; 113 } 114 } 115 } 116 117 $listofobjectthirdparties[$thirdpartyid] = $thirdpartyid; 118 $listofobjectref[$thirdpartyid][$toselectid] = $objecttmp; 119 } 120 } 121 } 122 123 // Check mandatory parameters 124 if (GETPOST('fromtype', 'alpha') === 'user' && empty($user->email)) 125 { 126 $error++; 127 setEventMessages($langs->trans("NoSenderEmailDefined"), null, 'warnings'); 128 $massaction = 'presend'; 129 } 130 131 $receiver = $_POST['receiver']; 132 if (!is_array($receiver)) 133 { 134 if (empty($receiver) || $receiver == '-1') $receiver = array(); 135 else $receiver = array($receiver); 136 } 137 if (!trim($_POST['sendto']) && count($receiver) == 0 && count($listofobjectthirdparties) == 1) // if only one recipient, receiver is mandatory 138 { 139 $error++; 140 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Recipient")), null, 'warnings'); 141 $massaction = 'presend'; 142 } 143 144 if (!GETPOST('subject', 'restricthtml')) 145 { 146 $error++; 147 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("MailTopic")), null, 'warnings'); 148 $massaction = 'presend'; 149 } 150 151 // Loop on each recipient/thirdparty 152 if (!$error) 153 { 154 foreach ($listofobjectthirdparties as $thirdpartyid) 155 { 156 $result = $thirdparty->fetch($thirdpartyid); 157 if ($result < 0) 158 { 159 dol_print_error($db); 160 exit; 161 } 162 163 $sendto = ''; 164 $sendtocc = ''; 165 $sendtobcc = ''; 166 $sendtoid = array(); 167 168 // Define $sendto 169 $tmparray = array(); 170 if (trim($_POST['sendto'])) 171 { 172 // Recipients are provided into free text 173 $tmparray[] = trim($_POST['sendto']); 174 } 175 if (count($receiver) > 0) 176 { 177 foreach ($receiver as $key=>$val) 178 { 179 // Recipient was provided from combo list 180 if ($val == 'thirdparty') // Id of third party or user 181 { 182 $tmparray[] = $thirdparty->name.' <'.$thirdparty->email.'>'; 183 } elseif ($val && method_exists($thirdparty, 'contact_get_property')) // Id of contact 184 { 185 $tmparray[] = $thirdparty->contact_get_property((int) $val, 'email'); 186 $sendtoid[] = $val; 187 } 188 } 189 } 190 $sendto = implode(',', $tmparray); 191 192 // Define $sendtocc 193 $receivercc = $_POST['receivercc']; 194 if (!is_array($receivercc)) 195 { 196 if ($receivercc == '-1') $receivercc = array(); 197 else $receivercc = array($receivercc); 198 } 199 $tmparray = array(); 200 if (trim($_POST['sendtocc'])) 201 { 202 $tmparray[] = trim($_POST['sendtocc']); 203 } 204 if (count($receivercc) > 0) 205 { 206 foreach ($receivercc as $key=>$val) 207 { 208 // Recipient was provided from combo list 209 if ($val == 'thirdparty') // Id of third party 210 { 211 $tmparray[] = $thirdparty->name.' <'.$thirdparty->email.'>'; 212 } elseif ($val) // Id du contact 213 { 214 $tmparray[] = $thirdparty->contact_get_property((int) $val, 'email'); 215 //$sendtoid[] = $val; TODO Add also id of contact in CC ? 216 } 217 } 218 } 219 $sendtocc = implode(',', $tmparray); 220 221 //var_dump($listofobjectref);exit; 222 $listofqualifiedobj = array(); 223 $listofqualifiedref = array(); 224 $thirdpartywithoutemail = array(); 225 226 foreach ($listofobjectref[$thirdpartyid] as $objectid => $objectobj) 227 { 228 //var_dump($thirdpartyid.' - '.$objectid.' - '.$objectobj->statut); 229 if ($objectclass == 'Propal' && $objectobj->statut == Propal::STATUS_DRAFT) 230 { 231 $langs->load("errors"); 232 $nbignored++; 233 $resaction .= '<div class="error">'.$langs->trans('ErrorOnlyProposalNotDraftCanBeSentInMassAction', $objectobj->ref).'</div><br>'; 234 continue; // Payment done or started or canceled 235 } 236 if ($objectclass == 'Commande' && $objectobj->statut == Commande::STATUS_DRAFT) 237 { 238 $langs->load("errors"); 239 $nbignored++; 240 $resaction .= '<div class="error">'.$langs->trans('ErrorOnlyOrderNotDraftCanBeSentInMassAction', $objectobj->ref).'</div><br>'; 241 continue; 242 } 243 if ($objectclass == 'Facture' && $objectobj->statut == Facture::STATUS_DRAFT) 244 { 245 $langs->load("errors"); 246 $nbignored++; 247 $resaction .= '<div class="error">'.$langs->trans('ErrorOnlyInvoiceValidatedCanBeSentInMassAction', $objectobj->ref).'</div><br>'; 248 continue; // Payment done or started or canceled 249 } 250 251 // Test recipient 252 if (empty($sendto)) // For the case, no recipient were set (multi thirdparties send) 253 { 254 if ($objectobj->element == 'societe') 255 { 256 $sendto = $objectobj->email; 257 } elseif ($objectobj->element == 'expensereport') 258 { 259 $fuser = new User($db); 260 $fuser->fetch($objectobj->fk_user_author); 261 $sendto = $fuser->email; 262 } elseif ($objectobj->element == 'holiday') 263 { 264 $fuser = new User($db); 265 $fuser->fetch($objectobj->fk_user); 266 $sendto = $fuser->email; 267 } elseif ($objectobj->element == 'facture' && !empty($listofobjectcontacts[$objectid])) 268 { 269 $emails_to_sends = array(); 270 $objectobj->fetch_thirdparty(); 271 $contactidtosend = array(); 272 foreach ($listofobjectcontacts[$objectid] as $contactemailid => $contactemailemail) { 273 $emails_to_sends[] = $objectobj->thirdparty->contact_get_property($contactemailid, 'email'); 274 if (!in_array($contactemailid, $contactidtosend)) { 275 $contactidtosend[] = $contactemailid; 276 } 277 } 278 if (count($emails_to_sends) > 0) { 279 $sendto = implode(',', $emails_to_sends); 280 } 281 } else { 282 $objectobj->fetch_thirdparty(); 283 $sendto = $objectobj->thirdparty->email; 284 } 285 } 286 287 if (empty($sendto)) 288 { 289 if ($objectobj->element == 'societe') { 290 $objectobj->thirdparty = $objectobj; // Hack so following code is comaptible when objectobj is a thirdparty 291 } 292 293 //print "No recipient for thirdparty ".$objectobj->thirdparty->name; 294 $nbignored++; 295 if (empty($thirdpartywithoutemail[$objectobj->thirdparty->id])) 296 { 297 $resaction .= '<div class="error">'.$langs->trans('NoRecipientEmail', $objectobj->thirdparty->name).'</div><br>'; 298 } 299 dol_syslog('No recipient for thirdparty: '.$objectobj->thirdparty->name, LOG_WARNING); 300 $thirdpartywithoutemail[$objectobj->thirdparty->id] = 1; 301 continue; 302 } 303 304 if ($_POST['addmaindocfile']) 305 { 306 // TODO Use future field $objectobj->fullpathdoc to know where is stored default file 307 // TODO If not defined, use $objectobj->modelpdf (or defaut invoice config) to know what is template to use to regenerate doc. 308 $filename = dol_sanitizeFileName($objectobj->ref).'.pdf'; 309 $subdir = ''; 310 // TODO Set subdir to be compatible with multi levels dir trees 311 // $subdir = get_exdir($objectobj->id, 2, 0, 0, $objectobj, $objectobj->element) 312 $filedir = $uploaddir.'/'.$subdir.dol_sanitizeFileName($objectobj->ref); 313 $file = $filedir.'/'.$filename; 314 315 // For supplier invoices, we use the file provided by supplier, not the one we generate 316 if ($objectobj->element == 'invoice_supplier') 317 { 318 $fileparams = dol_most_recent_file($uploaddir.'/'.get_exdir($objectobj->id, 2, 0, 0, $objectobj, $objectobj->element).$objectobj->ref, preg_quote($objectobj->ref, '/').'([^\-])+'); 319 $file = $fileparams['fullname']; 320 } 321 322 $mime = dol_mimetype($file); 323 324 if (dol_is_file($file)) 325 { 326 // Create form object 327 $attachedfilesThirdpartyObj[$thirdpartyid][$objectid] = array( 328 'paths'=>array($file), 329 'names'=>array($filename), 330 'mimes'=>array($mime) 331 ); 332 } else { 333 $nbignored++; 334 $langs->load("errors"); 335 $resaction .= '<div class="error">'.$langs->trans('ErrorCantReadFile', $file).'</div><br>'; 336 dol_syslog('Failed to read file: '.$file, LOG_WARNING); 337 continue; 338 } 339 } 340 341 // Object of thirdparty qualified, we add it 342 $listofqualifiedobj[$objectid] = $objectobj; 343 $listofqualifiedref[$objectid] = $objectobj->ref; 344 345 346 //var_dump($listofqualifiedref); 347 } 348 349 // Send email if there is at least one qualified object for current thirdparty 350 if (count($listofqualifiedobj) > 0) 351 { 352 $langs->load("commercial"); 353 354 $reg = array(); 355 $fromtype = GETPOST('fromtype'); 356 if ($fromtype === 'user') { 357 $from = $user->getFullName($langs).' <'.$user->email.'>'; 358 } elseif ($fromtype === 'company') { 359 $from = $conf->global->MAIN_INFO_SOCIETE_NOM.' <'.$conf->global->MAIN_INFO_SOCIETE_MAIL.'>'; 360 } elseif (preg_match('/user_aliases_(\d+)/', $fromtype, $reg)) { 361 $tmp = explode(',', $user->email_aliases); 362 $from = trim($tmp[($reg[1] - 1)]); 363 } elseif (preg_match('/global_aliases_(\d+)/', $fromtype, $reg)) { 364 $tmp = explode(',', $conf->global->MAIN_INFO_SOCIETE_MAIL_ALIASES); 365 $from = trim($tmp[($reg[1] - 1)]); 366 } elseif (preg_match('/senderprofile_(\d+)_(\d+)/', $fromtype, $reg)) { 367 $sql = 'SELECT rowid, label, email FROM '.MAIN_DB_PREFIX.'c_email_senderprofile WHERE rowid = '.(int) $reg[1]; 368 $resql = $db->query($sql); 369 $obj = $db->fetch_object($resql); 370 if ($obj) 371 { 372 $from = $obj->label.' <'.$obj->email.'>'; 373 } 374 } else { 375 $from = $_POST['fromname'].' <'.$_POST['frommail'].'>'; 376 } 377 378 $replyto = $from; 379 $subject = GETPOST('subject', 'restricthtml'); 380 $message = GETPOST('message', 'restricthtml'); 381 382 $sendtobcc = GETPOST('sendtoccc'); 383 if ($objectclass == 'Propal') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_PROPOSAL_TO) ? '' : (($sendtobcc ? ", " : "").$conf->global->MAIN_MAIL_AUTOCOPY_PROPOSAL_TO)); 384 if ($objectclass == 'Commande') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_ORDER_TO) ? '' : (($sendtobcc ? ", " : "").$conf->global->MAIN_MAIL_AUTOCOPY_ORDER_TO)); 385 if ($objectclass == 'Facture') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_INVOICE_TO) ? '' : (($sendtobcc ? ", " : "").$conf->global->MAIN_MAIL_AUTOCOPY_INVOICE_TO)); 386 if ($objectclass == 'Supplier_Proposal') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO) ? '' : (($sendtobcc ? ", " : "").$conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO)); 387 if ($objectclass == 'CommandeFournisseur') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO) ? '' : (($sendtobcc ? ", " : "").$conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO)); 388 if ($objectclass == 'FactureFournisseur') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO) ? '' : (($sendtobcc ? ", " : "").$conf->global->MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO)); 389 if ($objectclass == 'Project') $sendtobcc .= (empty($conf->global->MAIN_MAIL_AUTOCOPY_PROJECT_TO) ? '' : (($sendtobcc ? ", " : "").$conf->global->MAIN_MAIL_AUTOCOPY_PROJECT_TO)); 390 391 // $listofqualifiedobj is array with key = object id and value is instance of qualified objects, for the current thirdparty (but thirdparty property is not loaded yet) 392 // $looparray will be an array with number of email to send for the current thirdparty (so 1 or n if n object for same thirdparty) 393 $looparray = array(); 394 if (!$oneemailperrecipient) 395 { 396 $looparray = $listofqualifiedobj; 397 foreach ($looparray as $key => $objecttmp) 398 { 399 $looparray[$key]->thirdparty = $thirdparty; // Force thirdparty on object 400 } 401 } else { 402 $objectforloop = new $objectclass($db); 403 $objectforloop->thirdparty = $thirdparty; // Force thirdparty on object (even if object was not loaded) 404 $looparray[0] = $objectforloop; 405 } 406 //var_dump($looparray);exit; 407 dol_syslog("We have set an array of ".count($looparray)." emails to send. oneemailperrecipient=".$oneemailperrecipient); 408 //var_dump($oneemailperrecipient); var_dump($listofqualifiedobj); var_dump($listofqualifiedref); 409 foreach ($looparray as $objectid => $objecttmp) // $objecttmp is a real object or an empty object if we choose to send one email per thirdparty instead of one per object 410 { 411 // Make substitution in email content 412 if (!empty($conf->projet->enabled) && method_exists($objecttmp, 'fetch_projet') && is_null($objecttmp->project)) 413 { 414 $objecttmp->fetch_projet(); 415 } 416 $substitutionarray = getCommonSubstitutionArray($langs, 0, null, $objecttmp); 417 $substitutionarray['__ID__'] = ($oneemailperrecipient ? join(', ', array_keys($listofqualifiedobj)) : $objecttmp->id); 418 $substitutionarray['__REF__'] = ($oneemailperrecipient ? join(', ', $listofqualifiedref) : $objecttmp->ref); 419 $substitutionarray['__EMAIL__'] = $thirdparty->email; 420 $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"/>'; 421 422 $parameters = array('mode'=>'formemail'); 423 424 if (!empty($listofobjectthirdparties)) { 425 $parameters['listofobjectthirdparties'] = $listofobjectthirdparties; 426 } 427 if (!empty($listofobjectref)) { 428 $parameters['listofobjectref'] = $listofobjectref; 429 } 430 431 complete_substitutions_array($substitutionarray, $langs, $objecttmp, $parameters); 432 433 $subjectreplaced = make_substitutions($subject, $substitutionarray); 434 $messagereplaced = make_substitutions($message, $substitutionarray); 435 436 $attachedfiles = array('paths'=>array(), 'names'=>array(), 'mimes'=>array()); 437 if ($oneemailperrecipient) 438 { 439 // if "one email per recipient" is check we must collate $attachedfiles by thirdparty 440 if (is_array($attachedfilesThirdpartyObj[$thirdparty->id]) && count($attachedfilesThirdpartyObj[$thirdparty->id])) 441 { 442 foreach ($attachedfilesThirdpartyObj[$thirdparty->id] as $keyObjId => $objAttachedFiles) { 443 // Create form object 444 $attachedfiles = array( 445 'paths'=>array_merge($attachedfiles['paths'], $objAttachedFiles['paths']), 446 'names'=>array_merge($attachedfiles['names'], $objAttachedFiles['names']), 447 'mimes'=>array_merge($attachedfiles['mimes'], $objAttachedFiles['mimes']) 448 ); 449 } 450 } 451 } elseif (!empty($attachedfilesThirdpartyObj[$thirdparty->id][$objectid])) { 452 // Create form object 453 // if "one email per recipient" isn't check we must separate $attachedfiles by object 454 $attachedfiles = $attachedfilesThirdpartyObj[$thirdparty->id][$objectid]; 455 } 456 457 $filepath = $attachedfiles['paths']; 458 $filename = $attachedfiles['names']; 459 $mimetype = $attachedfiles['mimes']; 460 461 // Define the trackid when emails sent from the mass action 462 if ($oneemailperrecipient) 463 { 464 $trackid = 'thi'.$thirdparty->id; 465 if ($objecttmp->element == 'expensereport') $trackid = 'use'.$thirdparty->id; 466 if ($objecttmp->element == 'holiday') $trackid = 'use'.$thirdparty->id; 467 } else { 468 $trackid = strtolower(get_class($objecttmp)); 469 if (get_class($objecttmp) == 'Contrat') $trackid = 'con'; 470 if (get_class($objecttmp) == 'Propal') $trackid = 'pro'; 471 if (get_class($objecttmp) == 'Commande') $trackid = 'ord'; 472 if (get_class($objecttmp) == 'Facture') $trackid = 'inv'; 473 if (get_class($objecttmp) == 'Supplier_Proposal') $trackid = 'spr'; 474 if (get_class($objecttmp) == 'CommandeFournisseur') $trackid = 'sor'; 475 if (get_class($objecttmp) == 'FactureFournisseur') $trackid = 'sin'; 476 477 $trackid .= $objecttmp->id; 478 } 479 //var_dump($filepath); 480 //var_dump($trackid);exit; 481 //var_dump($subjectreplaced); 482 483 if (empty($sendcontext)) $sendcontext = 'standard'; 484 485 // Send mail (substitutionarray must be done just before this) 486 require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; 487 $mailfile = new CMailFile($subjectreplaced, $sendto, $from, $messagereplaced, $filepath, $mimetype, $filename, $sendtocc, $sendtobcc, $deliveryreceipt, -1, '', '', $trackid, '', $sendcontext); 488 if ($mailfile->error) 489 { 490 $resaction .= '<div class="error">'.$mailfile->error.'</div>'; 491 } else { 492 $result = $mailfile->sendfile(); 493 if ($result) 494 { 495 $resaction .= $langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($sendto, 2)).'<br>'; // Must not contain " 496 497 $error = 0; 498 499 // Insert logs into agenda 500 foreach ($listofqualifiedobj as $objid2 => $objectobj2) 501 { 502 if ((!$oneemailperrecipient) && $objid2 != $objectid) continue; // We discard this pass to avoid duplicate with other pass in looparray at higher level 503 504 dol_syslog("Try to insert email event into agenda for objid=".$objid2." => objectobj=".get_class($objectobj2)); 505 506 /*if ($objectclass == 'Propale') $actiontypecode='AC_PROP'; 507 if ($objectclass == 'Commande') $actiontypecode='AC_COM'; 508 if ($objectclass == 'Facture') $actiontypecode='AC_FAC'; 509 if ($objectclass == 'Supplier_Proposal') $actiontypecode='AC_SUP_PRO'; 510 if ($objectclass == 'CommandeFournisseur') $actiontypecode='AC_SUP_ORD'; 511 if ($objectclass == 'FactureFournisseur') $actiontypecode='AC_SUP_INV';*/ 512 513 $actionmsg = $langs->transnoentities('MailSentBy').' '.$from.' '.$langs->transnoentities('To').' '.$sendto; 514 if ($message) 515 { 516 if ($sendtocc) $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('Bcc').": ".$sendtocc); 517 $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('MailTopic').": ".$subjectreplaced); 518 $actionmsg = dol_concatdesc($actionmsg, $langs->transnoentities('TextUsedInTheMessageBody').":"); 519 $actionmsg = dol_concatdesc($actionmsg, $messagereplaced); 520 } 521 $actionmsg2 = ''; 522 523 // Initialisation donnees 524 $objectobj2->sendtoid = (empty($contactidtosend) ? 0 : $contactidtosend); 525 $objectobj2->actionmsg = $actionmsg; // Long text 526 $objectobj2->actionmsg2 = $actionmsg2; // Short text 527 $objectobj2->fk_element = $objid2; 528 $objectobj2->elementtype = $objectobj2->element; 529 530 $triggername = strtoupper(get_class($objectobj2)).'_SENTBYMAIL'; 531 if ($triggername == 'SOCIETE_SENTBYMAIL') $triggername = 'COMPANY_SENTBYMAIL'; 532 if ($triggername == 'CONTRAT_SENTBYMAIL') $triggername = 'CONTRACT_SENTBYMAIL'; 533 if ($triggername == 'COMMANDE_SENTBYMAIL') $triggername = 'ORDER_SENTBYMAIL'; 534 if ($triggername == 'FACTURE_SENTBYMAIL') $triggername = 'BILL_SENTBYMAIL'; 535 if ($triggername == 'EXPEDITION_SENTBYMAIL') $triggername = 'SHIPPING_SENTBYMAIL'; 536 if ($triggername == 'COMMANDEFOURNISSEUR_SENTBYMAIL') $triggername = 'ORDER_SUPPLIER_SENTBYMAIL'; 537 if ($triggername == 'FACTUREFOURNISSEUR_SENTBYMAIL') $triggername = 'BILL_SUPPLIER_SENTBYMAIL'; 538 if ($triggername == 'SUPPLIERPROPOSAL_SENTBYMAIL') $triggername = 'PROPOSAL_SUPPLIER_SENTBYMAIL'; 539 540 if (!empty($triggername)) 541 { 542 // Call trigger 543 $result = $objectobj2->call_trigger($triggername, $user); 544 if ($result < 0) $error++; 545 // End call triggers 546 547 if ($error) 548 { 549 setEventMessages($db->lasterror(), $errors, 'errors'); 550 dol_syslog("Error in trigger ".$triggername.' '.$db->lasterror(), LOG_ERR); 551 } 552 } 553 554 $nbsent++; // Nb of object sent 555 } 556 } else { 557 $langs->load("other"); 558 if ($mailfile->error) 559 { 560 $resaction .= $langs->trans('ErrorFailedToSendMail', $from, $sendto); 561 $resaction .= '<br><div class="error">'.$mailfile->error.'</div>'; 562 } elseif (!empty($conf->global->MAIN_DISABLE_ALL_MAILS)) { 563 $resaction .= '<div class="warning">No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS</div>'; 564 } else { 565 $resaction .= $langs->trans('ErrorFailedToSendMail', $from, $sendto) . '<br><div class="error">(unhandled error)</div>'; 566 } 567 } 568 } 569 } 570 } 571 } 572 573 $resaction .= ($resaction ? '<br>' : $resaction); 574 $resaction .= '<strong>'.$langs->trans("ResultOfMailSending").':</strong><br>'."\n"; 575 $resaction .= $langs->trans("NbSelected").': '.count($toselect)."\n<br>"; 576 $resaction .= $langs->trans("NbIgnored").': '.($nbignored ? $nbignored : 0)."\n<br>"; 577 $resaction .= $langs->trans("NbSent").': '.($nbsent ? $nbsent : 0)."\n<br>"; 578 579 if ($nbsent) 580 { 581 $action = ''; // Do not show form post if there was at least one successfull sent 582 //setEventMessages($langs->trans("EMailSentToNRecipients", $nbsent.'/'.count($toselect)), null, 'mesgs'); 583 setEventMessages($langs->trans("EMailSentForNElements", $nbsent.'/'.count($toselect)), null, 'mesgs'); 584 setEventMessages($resaction, null, 'mesgs'); 585 } else { 586 //setEventMessages($langs->trans("EMailSentToNRecipients", 0), null, 'warnings'); // May be object has no generated PDF file 587 setEventMessages($resaction, null, 'warnings'); 588 } 589 590 $action = 'list'; 591 $massaction = ''; 592 } 593} 594 595if ($massaction == 'confirm_createbills') // Create bills from orders 596{ 597 $orders = GETPOST('toselect', 'array'); 598 $createbills_onebythird = GETPOST('createbills_onebythird', 'int'); 599 $validate_invoices = GETPOST('validate_invoices', 'int'); 600 601 $TFact = array(); 602 $TFactThird = array(); 603 604 $nb_bills_created = 0; 605 606 $db->begin(); 607 608 foreach ($orders as $id_order) 609 { 610 $cmd = new Commande($db); 611 if ($cmd->fetch($id_order) <= 0) continue; 612 613 $objecttmp = new Facture($db); 614 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. 615 else { 616 // Load extrafields of order 617 $cmd->fetch_optionals(); 618 619 $objecttmp->socid = $cmd->socid; 620 $objecttmp->type = $objecttmp::TYPE_STANDARD; 621 $objecttmp->cond_reglement_id = $cmd->cond_reglement_id; 622 $objecttmp->mode_reglement_id = $cmd->mode_reglement_id; 623 $objecttmp->fk_project = $cmd->fk_project; 624 $objecttmp->multicurrency_code = $cmd->multicurrency_code; 625 if (empty($createbills_onebythird)) $objecttmp->ref_client = $cmd->ref_client; 626 627 $datefacture = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); 628 if (empty($datefacture)) 629 { 630 $datefacture = dol_now(); 631 } 632 633 $objecttmp->date = $datefacture; 634 $objecttmp->origin = 'commande'; 635 $objecttmp->origin_id = $id_order; 636 637 $objecttmp->array_options = $cmd->array_options; // Copy extrafields 638 639 $res = $objecttmp->create($user); 640 641 if ($res > 0) $nb_bills_created++; 642 } 643 644 if ($objecttmp->id > 0) 645 { 646 $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element ("; 647 $sql .= "fk_source"; 648 $sql .= ", sourcetype"; 649 $sql .= ", fk_target"; 650 $sql .= ", targettype"; 651 $sql .= ") VALUES ("; 652 $sql .= $id_order; 653 $sql .= ", '".$db->escape($objecttmp->origin)."'"; 654 $sql .= ", ".$objecttmp->id; 655 $sql .= ", '".$db->escape($objecttmp->element)."'"; 656 $sql .= ")"; 657 658 if (!$db->query($sql)) 659 { 660 $error++; 661 } 662 663 if (!$error) 664 { 665 $lines = $cmd->lines; 666 if (empty($lines) && method_exists($cmd, 'fetch_lines')) 667 { 668 $cmd->fetch_lines(); 669 $lines = $cmd->lines; 670 } 671 672 $fk_parent_line = 0; 673 $num = count($lines); 674 675 for ($i = 0; $i < $num; $i++) 676 { 677 $desc = ($lines[$i]->desc ? $lines[$i]->desc : ''); 678 // If we build one invoice for several order, we must put the invoice of order on the line 679 if (!empty($createbills_onebythird)) 680 { 681 $desc = dol_concatdesc($desc, $langs->trans("Order").' '.$cmd->ref.' - '.dol_print_date($cmd->date, 'day')); 682 } 683 684 if ($lines[$i]->subprice < 0) 685 { 686 // Negative line, we create a discount line 687 $discount = new DiscountAbsolute($db); 688 $discount->fk_soc = $objecttmp->socid; 689 $discount->amount_ht = abs($lines[$i]->total_ht); 690 $discount->amount_tva = abs($lines[$i]->total_tva); 691 $discount->amount_ttc = abs($lines[$i]->total_ttc); 692 $discount->tva_tx = $lines[$i]->tva_tx; 693 $discount->fk_user = $user->id; 694 $discount->description = $desc; 695 $discountid = $discount->create($user); 696 if ($discountid > 0) 697 { 698 $result = $objecttmp->insert_discount($discountid); 699 //$result=$discount->link_to_invoice($lineid,$id); 700 } else { 701 setEventMessages($discount->error, $discount->errors, 'errors'); 702 $error++; 703 break; 704 } 705 } else { 706 // Positive line 707 $product_type = ($lines[$i]->product_type ? $lines[$i]->product_type : 0); 708 // Date start 709 $date_start = false; 710 if ($lines[$i]->date_debut_prevue) $date_start = $lines[$i]->date_debut_prevue; 711 if ($lines[$i]->date_debut_reel) $date_start = $lines[$i]->date_debut_reel; 712 if ($lines[$i]->date_start) $date_start = $lines[$i]->date_start; 713 //Date end 714 $date_end = false; 715 if ($lines[$i]->date_fin_prevue) $date_end = $lines[$i]->date_fin_prevue; 716 if ($lines[$i]->date_fin_reel) $date_end = $lines[$i]->date_fin_reel; 717 if ($lines[$i]->date_end) $date_end = $lines[$i]->date_end; 718 // Reset fk_parent_line for no child products and special product 719 if (($lines[$i]->product_type != 9 && empty($lines[$i]->fk_parent_line)) || $lines[$i]->product_type == 9) 720 { 721 $fk_parent_line = 0; 722 } 723 724 // Extrafields 725 if (method_exists($lines[$i], 'fetch_optionals')) { 726 $lines[$i]->fetch_optionals(); 727 $array_options = $lines[$i]->array_options; 728 } 729 730 $result = $objecttmp->addline( 731 $desc, 732 $lines[$i]->subprice, 733 $lines[$i]->qty, 734 $lines[$i]->tva_tx, 735 $lines[$i]->localtax1_tx, 736 $lines[$i]->localtax2_tx, 737 $lines[$i]->fk_product, 738 $lines[$i]->remise_percent, 739 $date_start, 740 $date_end, 741 0, 742 $lines[$i]->info_bits, 743 $lines[$i]->fk_remise_except, 744 'HT', 745 0, 746 $product_type, 747 $lines[$i]->rang, 748 $lines[$i]->special_code, 749 $objecttmp->origin, 750 $lines[$i]->rowid, 751 $fk_parent_line, 752 $lines[$i]->fk_fournprice, 753 $lines[$i]->pa_ht, 754 $lines[$i]->label, 755 $array_options, 756 100, 757 0, 758 $lines[$i]->fk_unit 759 ); 760 if ($result > 0) 761 { 762 $lineid = $result; 763 } else { 764 $lineid = 0; 765 $error++; 766 break; 767 } 768 // Defined the new fk_parent_line 769 if ($result > 0 && $lines[$i]->product_type == 9) 770 { 771 $fk_parent_line = $result; 772 } 773 } 774 } 775 } 776 } 777 778 //$cmd->classifyBilled($user); // Disabled. This behavior must be set or not using the workflow module. 779 780 if (!empty($createbills_onebythird) && empty($TFactThird[$cmd->socid])) $TFactThird[$cmd->socid] = $objecttmp; 781 else $TFact[$objecttmp->id] = $objecttmp; 782 } 783 784 // Build doc with all invoices 785 $TAllFact = empty($createbills_onebythird) ? $TFact : $TFactThird; 786 $toselect = array(); 787 788 if (!$error && $validate_invoices) 789 { 790 $massaction = $action = 'builddoc'; 791 792 foreach ($TAllFact as &$objecttmp) 793 { 794 $result = $objecttmp->validate($user); 795 if ($result <= 0) 796 { 797 $error++; 798 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 799 break; 800 } 801 802 $id = $objecttmp->id; // For builddoc action 803 $object = $objecttmp; 804 805 // Builddoc 806 $donotredirect = 1; 807 $upload_dir = $conf->facture->dir_output; 808 $permissiontoadd = $user->rights->facture->creer; 809 810 // Call action to build doc 811 $savobject = $object; 812 $object = $objecttmp; 813 include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php'; 814 $object = $savobject; 815 } 816 817 $massaction = $action = 'confirm_createbills'; 818 } 819 820 if (!$error) 821 { 822 $db->commit(); 823 setEventMessages($langs->trans('BillCreated', $nb_bills_created), null, 'mesgs'); 824 825 // Make a redirect to avoid to bill twice if we make a refresh or back 826 $param = ''; 827 if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param .= '&contextpage='.urlencode($contextpage); 828 if ($limit > 0 && $limit != $conf->liste_limit) $param .= '&limit='.urlencode($limit); 829 if ($sall) $param .= '&sall='.urlencode($sall); 830 if ($socid > 0) $param .= '&socid='.urlencode($socid); 831 if ($search_status != '') $param .= '&search_status='.urlencode($search_status); 832 if ($search_orderday) $param .= '&search_orderday='.urlencode($search_orderday); 833 if ($search_ordermonth) $param .= '&search_ordermonth='.urlencode($search_ordermonth); 834 if ($search_orderyear) $param .= '&search_orderyear='.urlencode($search_orderyear); 835 if ($search_deliveryday) $param .= '&search_deliveryday='.urlencode($search_deliveryday); 836 if ($search_deliverymonth) $param .= '&search_deliverymonth='.urlencode($search_deliverymonth); 837 if ($search_deliveryyear) $param .= '&search_deliveryyear='.urlencode($search_deliveryyear); 838 if ($search_ref) $param .= '&search_ref='.urlencode($search_ref); 839 if ($search_company) $param .= '&search_company='.urlencode($search_company); 840 if ($search_ref_customer) $param .= '&search_ref_customer='.urlencode($search_ref_customer); 841 if ($search_user > 0) $param .= '&search_user='.urlencode($search_user); 842 if ($search_sale > 0) $param .= '&search_sale='.urlencode($search_sale); 843 if ($search_total_ht != '') $param .= '&search_total_ht='.urlencode($search_total_ht); 844 if ($search_total_vat != '') $param .= '&search_total_vat='.urlencode($search_total_vat); 845 if ($search_total_ttc != '') $param .= '&search_total_ttc='.urlencode($search_total_ttc); 846 if ($search_project_ref >= 0) $param .= "&search_project_ref=".urlencode($search_project_ref); 847 if ($show_files) $param .= '&show_files='.urlencode($show_files); 848 if ($optioncss != '') $param .= '&optioncss='.urlencode($optioncss); 849 if ($billed != '') $param .= '&billed='.urlencode($billed); 850 851 header("Location: ".$_SERVER['PHP_SELF'].'?'.$param); 852 exit; 853 } else { 854 $db->rollback(); 855 $action = 'create'; 856 $_GET["origin"] = $_POST["origin"]; 857 $_GET["originid"] = $_POST["originid"]; 858 setEventMessages("Error", null, 'errors'); 859 $error++; 860 } 861} 862 863if (!$error && $massaction == 'cancelorders') 864{ 865 $db->begin(); 866 867 $nbok = 0; 868 869 870 $orders = GETPOST('toselect', 'array'); 871 foreach ($orders as $id_order) 872 { 873 $cmd = new Commande($db); 874 if ($cmd->fetch($id_order) <= 0) 875 continue; 876 877 if ($cmd->statut != Commande::STATUS_VALIDATED) 878 { 879 $langs->load('errors'); 880 setEventMessages($langs->trans("ErrorObjectMustHaveStatusValidToBeCanceled", $cmd->ref), null, 'errors'); 881 $error++; 882 break; 883 } else { 884 // TODO We do not provide warehouse so no stock change here for the moment. 885 $result = $cmd->cancel(); 886 } 887 888 if ($result < 0) 889 { 890 setEventMessages($cmd->error, $cmd->errors, 'errors'); 891 $error++; 892 break; 893 } else $nbok++; 894 } 895 if (!$error) 896 { 897 if ($nbok > 1) 898 setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs'); 899 else setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs'); 900 $db->commit(); 901 } else { 902 $db->rollback(); 903 } 904} 905 906 907if (!$error && $massaction == "builddoc" && $permissiontoread && !GETPOST('button_search')) 908{ 909 if (empty($diroutputmassaction)) 910 { 911 dol_print_error(null, 'include of actions_massactions.inc.php is done but var $diroutputmassaction was not defined'); 912 exit; 913 } 914 915 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 916 require_once DOL_DOCUMENT_ROOT.'/core/lib/pdf.lib.php'; 917 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 918 919 $objecttmp = new $objectclass($db); 920 $listofobjectid = array(); 921 $listofobjectthirdparties = array(); 922 $listofobjectref = array(); 923 foreach ($toselect as $toselectid) 924 { 925 $objecttmp = new $objectclass($db); // must create new instance because instance is saved into $listofobjectref array for future use 926 $result = $objecttmp->fetch($toselectid); 927 if ($result > 0) 928 { 929 $listofobjectid[$toselectid] = $toselectid; 930 $thirdpartyid = $objecttmp->fk_soc ? $objecttmp->fk_soc : $objecttmp->socid; 931 $listofobjectthirdparties[$thirdpartyid] = $thirdpartyid; 932 $listofobjectref[$toselectid] = $objecttmp->ref; 933 } 934 } 935 936 $arrayofinclusion = array(); 937 foreach ($listofobjectref as $tmppdf) $arrayofinclusion[] = '^'.preg_quote(dol_sanitizeFileName($tmppdf), '/').'\.pdf$'; 938 foreach ($listofobjectref as $tmppdf) $arrayofinclusion[] = '^'.preg_quote(dol_sanitizeFileName($tmppdf), '/').'_[a-zA-Z0-9-_]+\.pdf$'; // To include PDF generated from ODX files 939 $listoffiles = dol_dir_list($uploaddir, 'all', 1, implode('|', $arrayofinclusion), '\.meta$|\.png', 'date', SORT_DESC, 0, true); 940 941 // build list of files with full path 942 $files = array(); 943 foreach ($listofobjectref as $basename) 944 { 945 $basename = dol_sanitizeFileName($basename); 946 foreach ($listoffiles as $filefound) 947 { 948 if (strstr($filefound["name"], $basename)) 949 { 950 $files[] = $uploaddir.'/'.$basename.'/'.$filefound["name"]; 951 break; 952 } 953 } 954 } 955 956 // Define output language (Here it is not used because we do only merging existing PDF) 957 $outputlangs = $langs; 958 $newlang = ''; 959 if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) { 960 $newlang = GETPOST('lang_id', 'aZ09'); 961 } 962 //elseif ($conf->global->MAIN_MULTILANGS && empty($newlang) && is_object($objecttmp->thirdparty)) { // On massaction, we can have several values for $objecttmp->thirdparty 963 // $newlang = $objecttmp->thirdparty->default_lang; 964 //} 965 if (!empty($newlang)) { 966 $outputlangs = new Translate("", $conf); 967 $outputlangs->setDefaultLang($newlang); 968 } 969 970 if (!empty($conf->global->USE_PDFTK_FOR_PDF_CONCAT)) 971 { 972 // Create output dir if not exists 973 dol_mkdir($diroutputmassaction); 974 975 // Defined name of merged file 976 $filename = strtolower(dol_sanitizeFileName($langs->transnoentities($objectlabel))); 977 $filename = preg_replace('/\s/', '_', $filename); 978 979 // Save merged file 980 if (in_array($objecttmp->element, array('facture', 'facture_fournisseur')) && $search_status == Facture::STATUS_VALIDATED) 981 { 982 if ($option == 'late') $filename .= '_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Unpaid"))).'_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Late"))); 983 else $filename .= '_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Unpaid"))); 984 } 985 if ($year) $filename .= '_'.$year; 986 if ($month) $filename .= '_'.$month; 987 988 if (count($files) > 0) 989 { 990 $now = dol_now(); 991 $file = $diroutputmassaction.'/'.$filename.'_'.dol_print_date($now, 'dayhourlog').'.pdf'; 992 993 $input_files = ''; 994 foreach ($files as $f) { 995 $input_files .= ' '.escapeshellarg($f); 996 } 997 998 $cmd = 'pdftk '.$input_files.' cat output '.escapeshellarg($file); 999 exec($cmd); 1000 1001 // check if pdftk is installed 1002 if (file_exists($file)) { 1003 if (!empty($conf->global->MAIN_UMASK)) 1004 @chmod($file, octdec($conf->global->MAIN_UMASK)); 1005 1006 $langs->load("exports"); 1007 setEventMessages($langs->trans('FileSuccessfullyBuilt', $filename.'_'.dol_print_date($now, 'dayhourlog')), null, 'mesgs'); 1008 } else { 1009 setEventMessages($langs->trans('ErrorPDFTkOutputFileNotFound'), null, 'errors'); 1010 } 1011 } else { 1012 setEventMessages($langs->trans('NoPDFAvailableForDocGenAmongChecked'), null, 'errors'); 1013 } 1014 } else { 1015 // Create empty PDF 1016 $formatarray = pdf_getFormat(); 1017 $page_largeur = $formatarray['width']; 1018 $page_hauteur = $formatarray['height']; 1019 $format = array($page_largeur, $page_hauteur); 1020 1021 $pdf = pdf_getInstance($format); 1022 1023 if (class_exists('TCPDF')) 1024 { 1025 $pdf->setPrintHeader(false); 1026 $pdf->setPrintFooter(false); 1027 } 1028 $pdf->SetFont(pdf_getPDFFont($outputlangs)); 1029 1030 if (!empty($conf->global->MAIN_DISABLE_PDF_COMPRESSION)) $pdf->SetCompression(false); 1031 1032 // Add all others 1033 foreach ($files as $file) 1034 { 1035 // Charge un document PDF depuis un fichier. 1036 $pagecount = $pdf->setSourceFile($file); 1037 for ($i = 1; $i <= $pagecount; $i++) 1038 { 1039 $tplidx = $pdf->importPage($i); 1040 $s = $pdf->getTemplatesize($tplidx); 1041 $pdf->AddPage($s['h'] > $s['w'] ? 'P' : 'L'); 1042 $pdf->useTemplate($tplidx); 1043 } 1044 } 1045 1046 // Create output dir if not exists 1047 dol_mkdir($diroutputmassaction); 1048 1049 // Defined name of merged file 1050 $filename = strtolower(dol_sanitizeFileName($langs->transnoentities($objectlabel))); 1051 $filename = preg_replace('/\s/', '_', $filename); 1052 1053 // Save merged file 1054 if (in_array($objecttmp->element, array('facture', 'facture_fournisseur')) && $search_status == Facture::STATUS_VALIDATED) 1055 { 1056 if ($option == 'late') $filename .= '_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Unpaid"))).'_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Late"))); 1057 else $filename .= '_'.strtolower(dol_sanitizeFileName($langs->transnoentities("Unpaid"))); 1058 } 1059 if ($year) $filename .= '_'.$year; 1060 if ($month) $filename .= '_'.$month; 1061 if ($pagecount) 1062 { 1063 $now = dol_now(); 1064 $file = $diroutputmassaction.'/'.$filename.'_'.dol_print_date($now, 'dayhourlog').'.pdf'; 1065 $pdf->Output($file, 'F'); 1066 if (!empty($conf->global->MAIN_UMASK)) 1067 @chmod($file, octdec($conf->global->MAIN_UMASK)); 1068 1069 $langs->load("exports"); 1070 setEventMessages($langs->trans('FileSuccessfullyBuilt', $filename.'_'.dol_print_date($now, 'dayhourlog')), null, 'mesgs'); 1071 } else { 1072 setEventMessages($langs->trans('NoPDFAvailableForDocGenAmongChecked'), null, 'errors'); 1073 } 1074 } 1075} 1076 1077// Remove a file from massaction area 1078if ($action == 'remove_file') 1079{ 1080 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 1081 1082 $langs->load("other"); 1083 $upload_dir = $diroutputmassaction; 1084 $file = $upload_dir.'/'.GETPOST('file'); 1085 $ret = dol_delete_file($file); 1086 if ($ret) setEventMessages($langs->trans("FileWasRemoved", GETPOST('file')), null, 'mesgs'); 1087 else setEventMessages($langs->trans("ErrorFailToDeleteFile", GETPOST('file')), null, 'errors'); 1088 $action = ''; 1089} 1090 1091 1092// Validate records 1093if (!$error && $massaction == 'validate' && $permissiontoadd) 1094{ 1095 $objecttmp = new $objectclass($db); 1096 1097 if (($objecttmp->element == 'facture' || $objecttmp->element == 'invoice') && !empty($conf->stock->enabled) && !empty($conf->global->STOCK_CALCULATE_ON_BILL)) 1098 { 1099 $langs->load("errors"); 1100 setEventMessages($langs->trans('ErrorMassValidationNotAllowedWhenStockIncreaseOnAction'), null, 'errors'); 1101 $error++; 1102 } 1103 if ($objecttmp->element == 'invoice_supplier' && !empty($conf->stock->enabled) && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL)) 1104 { 1105 $langs->load("errors"); 1106 setEventMessages($langs->trans('ErrorMassValidationNotAllowedWhenStockIncreaseOnAction'), null, 'errors'); 1107 $error++; 1108 } 1109 if (!$error) 1110 { 1111 $db->begin(); 1112 1113 $nbok = 0; 1114 foreach ($toselect as $toselectid) 1115 { 1116 $result = $objecttmp->fetch($toselectid); 1117 if ($result > 0) 1118 { 1119 $result = $objecttmp->validate($user); 1120 if ($result == 0) { 1121 $langs->load("errors"); 1122 setEventMessages($langs->trans("ErrorObjectMustHaveStatusDraftToBeValidated", $objecttmp->ref), null, 'errors'); 1123 $error++; 1124 break; 1125 } elseif ($result < 0) { 1126 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 1127 $error++; 1128 break; 1129 } else { 1130 // validate() rename pdf but do not regenerate 1131 // Define output language 1132 if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) { 1133 $outputlangs = $langs; 1134 $newlang = ''; 1135 if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) { 1136 $newlang = GETPOST('lang_id', 'aZ09'); 1137 } 1138 if ($conf->global->MAIN_MULTILANGS && empty($newlang)) { 1139 $newlang = $objecttmp->thirdparty->default_lang; 1140 } 1141 if (!empty($newlang)) { 1142 $outputlangs = new Translate("", $conf); 1143 $outputlangs->setDefaultLang($newlang); 1144 $outputlangs->load('products'); 1145 } 1146 $model = $objecttmp->model_pdf; 1147 $ret = $objecttmp->fetch($objecttmp->id); // Reload to get new records 1148 // To be sure vars is defined 1149 $hidedetails = !empty($hidedetails) ? $hidedetails : 0; 1150 $hidedesc = !empty($hidedesc) ? $hidedesc : 0; 1151 $hideref = !empty($hideref) ? $hideref : 0; 1152 $moreparams = !empty($moreparams) ? $moreparams : null; 1153 1154 $result = $objecttmp->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref); 1155 if ($result < 0) { 1156 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 1157 } 1158 } 1159 $nbok++; 1160 } 1161 } else { 1162 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 1163 $error++; 1164 break; 1165 } 1166 } 1167 1168 if (!$error) 1169 { 1170 if ($nbok > 1) setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs'); 1171 else setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs'); 1172 $db->commit(); 1173 } else { 1174 $db->rollback(); 1175 } 1176 //var_dump($listofobjectthirdparties);exit; 1177 } 1178} 1179 1180// Closed records 1181if (!$error && $massaction == 'closed' && $objectclass == "Propal" && $permissiontoclose) { 1182 $db->begin(); 1183 1184 $objecttmp = new $objectclass($db); 1185 $nbok = 0; 1186 foreach ($toselect as $toselectid) { 1187 $result = $objecttmp->fetch($toselectid); 1188 if ($result > 0) { 1189 $result = $objecttmp->cloture($user, 3); 1190 if ($result <= 0) { 1191 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 1192 $error++; 1193 break; 1194 } else $nbok++; 1195 } else { 1196 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 1197 $error++; 1198 break; 1199 } 1200 } 1201 1202 if (!$error) { 1203 if ($nbok > 1) 1204 setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs'); 1205 else setEventMessages($langs->trans("RecordsModified", $nbok), null, 'mesgs'); 1206 $db->commit(); 1207 } else { 1208 $db->rollback(); 1209 } 1210} 1211 1212//var_dump($_POST);var_dump($massaction);exit; 1213 1214// Delete record from mass action (massaction = 'delete' for direct delete, action/confirm='delete'/'yes' with a confirmation step before) 1215if (!$error && ($massaction == 'delete' || ($action == 'delete' && $confirm == 'yes')) && $permissiontodelete) 1216{ 1217 $db->begin(); 1218 1219 $objecttmp = new $objectclass($db); 1220 $nbok = 0; 1221 foreach ($toselect as $toselectid) 1222 { 1223 $result = $objecttmp->fetch($toselectid); 1224 if ($result > 0) 1225 { 1226 // Refuse deletion for some objects/status 1227 if ($objectclass == 'Facture' && empty($conf->global->INVOICE_CAN_ALWAYS_BE_REMOVED) && $objecttmp->status != Facture::STATUS_DRAFT) 1228 { 1229 $langs->load("errors"); 1230 $nbignored++; 1231 $resaction .= '<div class="error">'.$langs->trans('ErrorOnlyDraftStatusCanBeDeletedInMassAction', $objecttmp->ref).'</div><br>'; 1232 continue; 1233 } 1234 1235 if ($objectclass == "Task" && $objecttmp->hasChildren() > 0) 1236 { 1237 $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task SET fk_task_parent = 0 WHERE fk_task_parent = ".$objecttmp->id; 1238 $res = $db->query($sql); 1239 1240 if (!$res) 1241 { 1242 setEventMessage('ErrorRecordParentingNotModified', 'errors'); 1243 $error++; 1244 } 1245 } 1246 1247 if (in_array($objecttmp->element, array('societe', 'member'))) $result = $objecttmp->delete($objecttmp->id, $user, 1); 1248 else { 1249 if (get_class($objecttmp) === "ActionComm") { 1250 $result = $objecttmp->delete(); 1251 } else { 1252 $result = $objecttmp->delete($user); 1253 } 1254 } 1255 if ($result <= 0) 1256 { 1257 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 1258 $error++; 1259 break; 1260 } else $nbok++; 1261 } else { 1262 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 1263 $error++; 1264 break; 1265 } 1266 } 1267 1268 if (!$error) 1269 { 1270 if ($nbok > 1) setEventMessages($langs->trans("RecordsDeleted", $nbok), null, 'mesgs'); 1271 else setEventMessages($langs->trans("RecordDeleted", $nbok), null, 'mesgs'); 1272 $db->commit(); 1273 } else { 1274 $db->rollback(); 1275 } 1276 //var_dump($listofobjectthirdparties);exit; 1277} 1278 1279// Generate document foreach object according to model linked to object 1280// @todo : propose model selection 1281if (!$error && $massaction == 'generate_doc' && $permissiontoread) 1282{ 1283 $db->begin(); 1284 1285 $objecttmp = new $objectclass($db); 1286 $nbok = 0; 1287 foreach ($toselect as $toselectid) 1288 { 1289 $result = $objecttmp->fetch($toselectid); 1290 if ($result > 0) 1291 { 1292 $outputlangs = $langs; 1293 $newlang = ''; 1294 1295 if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang = GETPOST('lang_id', 'aZ09'); 1296 if ($conf->global->MAIN_MULTILANGS && empty($newlang) && isset($objecttmp->thirdparty->default_lang)) $newlang = $objecttmp->thirdparty->default_lang; // for proposal, order, invoice, ... 1297 if ($conf->global->MAIN_MULTILANGS && empty($newlang) && isset($objecttmp->default_lang)) $newlang = $objecttmp->default_lang; // for thirdparty 1298 if (!empty($newlang)) 1299 { 1300 $outputlangs = new Translate("", $conf); 1301 $outputlangs->setDefaultLang($newlang); 1302 } 1303 1304 // To be sure vars is defined 1305 if (empty($hidedetails)) $hidedetails = 0; 1306 if (empty($hidedesc)) $hidedesc = 0; 1307 if (empty($hideref)) $hideref = 0; 1308 if (empty($moreparams)) $moreparams = null; 1309 1310 $result = $objecttmp->generateDocument($objecttmp->modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams); 1311 1312 if ($result <= 0) 1313 { 1314 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 1315 $error++; 1316 break; 1317 } else $nbok++; 1318 } else { 1319 setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); 1320 $error++; 1321 break; 1322 } 1323 } 1324 1325 if (!$error) 1326 { 1327 if ($nbok > 1) setEventMessages($langs->trans("RecordsGenerated", $nbok), null, 'mesgs'); 1328 else setEventMessages($langs->trans("RecordGenerated", $nbok), null, 'mesgs'); 1329 $db->commit(); 1330 } else { 1331 $db->rollback(); 1332 } 1333} 1334 1335$parameters['toselect'] = $toselect; 1336$parameters['uploaddir'] = $uploaddir; 1337$parameters['massaction'] = $massaction; 1338$parameters['diroutputmassaction'] = $diroutputmassaction; 1339 1340$reshook = $hookmanager->executeHooks('doMassActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks 1341if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); 1342