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