diff --git a/ChangeLog b/ChangeLog index 98c22d90bf7..48ba5daa562 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,27 @@ English Dolibarr ChangeLog -------------------------------------------------------------- +***** ChangeLog for 13.0.4 compared to 13.0.2 ***** + +FIX: Allow disabling of a module (not a dangerous action) even if there is problem with token (due to bugged modules). +FIX: 13.0 - fatal - missing inclusion of ajax.lib.php for calling `ajax_autocompleter()` +FIX: #17919 pictures in docs. +FIX: #18006 +FIX: Accountancy - if we define a date start, automatic binding try to continue to solve old binding +FIX: Accoutancy Limit date payment not registered on purchases operations +FIX: Can't edit replacement invoice +FIX: deposit can create credit note in payment conf +FIX: division by zero on create +FIX: holiday: balances not updated correctly with pgsql because of case sensitivity field +FIX: holiday: status filter parameter has been renamed but not in links it was used +FIX: List and Create Companies Left Menus +FIX: method exists +FIX: need to add payment sum to getlibstatus function in object linked block +FIX: permission to close a proposal when using advanced permissions +FIX: Problem of z-index with popup and top menu +FIX: same thing on supplier orders +FIX: Status of invoice when making a replacement invoice +FIX: update contact birthday alert ***** ChangeLog for 14.0.0 compared to 13.0.0 ***** diff --git a/htdocs/admin/emailcollector_card.php b/htdocs/admin/emailcollector_card.php index 4e2c9e34bf9..fdc0b893087 100644 --- a/htdocs/admin/emailcollector_card.php +++ b/htdocs/admin/emailcollector_card.php @@ -103,6 +103,7 @@ $debuginfo = ''; $parameters = array(); $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); } @@ -239,7 +240,6 @@ if ($action == 'confirm_collect') { - /* * View */ @@ -434,6 +434,14 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea $connectstringtarget = $connectstringserver.$object->getEncodedUtf7($targetdir); } + $timeoutconnect = empty($conf->global->MAIN_USE_CONNECT_TIMEOUT) ? 10 : $conf->global->MAIN_USE_CONNECT_TIMEOUT; + $timeoutread = empty($conf->global->MAIN_USE_RESPONSE_TIMEOUT) ? 30 : $conf->global->MAIN_USE_RESPONSE_TIMEOUT; + + dol_syslog("imap_open connectstring=".$connectstringsource." login=".$object->login." password=".$object->password." timeoutconnect=".$timeoutconnect." timeoutread=".$timeoutread); + + imap_timeout(IMAP_OPENTIMEOUT, $timeoutconnect); + imap_timeout(IMAP_READTIMEOUT, $timeoutread); + $connection = imap_open($connectstringsource, $object->login, $object->password); } catch (Exception $e) { print $e->getMessage(); @@ -449,12 +457,15 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea if (function_exists('imap_last_error')) { $morehtml .= '
'.imap_last_error(); } + dol_syslog("Error ".$morehtml, LOG_WARNING); //var_dump(imap_errors()) } else { + dol_syslog("Imap connected. Now we call imap_num_msg()"); $morehtml .= imap_num_msg($connection); } if ($connection) { + dol_syslog("Imap close"); imap_close($connection); } diff --git a/htdocs/blockedlog/class/blockedlog.class.php b/htdocs/blockedlog/class/blockedlog.class.php index 112456480e1..a7be2fb3630 100644 --- a/htdocs/blockedlog/class/blockedlog.class.php +++ b/htdocs/blockedlog/class/blockedlog.class.php @@ -549,110 +549,111 @@ class BlockedLog $totalamount = 0; - if (!is_array($object->amounts) && $object->amount) { - $object->amounts = array($object->id => $object->amount); - } + // Loop on each invoice payment amount + if (is_array($object->amounts) && !empty($object->amounts)) { + $paymentpartnumber = 0; + foreach ($object->amounts as $objid => $amount) { + if (empty($amount)) { + continue; + } - $paymentpartnumber = 0; - foreach ($object->amounts as $objid => $amount) { - if (empty($amount)) { - continue; - } + $totalamount += $amount; - $totalamount += $amount; + $tmpobject = null; + if ($this->element == 'payment_supplier') { + include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; + $tmpobject = new FactureFournisseur($this->db); + } elseif ($this->element == 'payment') { + include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; + $tmpobject = new Facture($this->db); + } elseif ($this->element == 'payment_donation') { + include_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php'; + $tmpobject = new Don($this->db); + } elseif ($this->element == 'payment_various') { + include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php'; + $tmpobject = new PaymentVarious($this->db); + } - $tmpobject = null; - if ($this->element == 'payment_supplier') { - include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php'; - $tmpobject = new FactureFournisseur($this->db); - } elseif ($this->element == 'payment') { - include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; - $tmpobject = new Facture($this->db); - } elseif ($this->element == 'payment_donation') { - include_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php'; - $tmpobject = new Don($this->db); - } elseif ($this->element == 'payment_various') { - include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php'; - $tmpobject = new PaymentVarious($this->db); - } + if (!is_object($tmpobject)) { + continue; + } - if (!is_object($tmpobject)) { - continue; - } + $result = $tmpobject->fetch($objid); - $result = $tmpobject->fetch($objid); - - if ($result <= 0) { - $this->error = $tmpobject->error; - $this->errors = $tmpobject->errors; - dol_syslog("Failed to fetch object with id ".$objid, LOG_ERR); - return -1; - } - - $paymentpart = new stdClass(); - $paymentpart->amount = $amount; - - if (!in_array($this->element, array('payment_donation', 'payment_various'))) { - $result = $tmpobject->fetch_thirdparty(); - if ($result == 0) { - $this->error = 'Failed to fetch thirdparty for object with id '.$tmpobject->id; - $this->errors[] = $this->error; - dol_syslog("Failed to fetch thirdparty for object with id ".$tmpobject->id, LOG_ERR); - return -1; - } elseif ($result < 0) { + if ($result <= 0) { $this->error = $tmpobject->error; $this->errors = $tmpobject->errors; + dol_syslog("Failed to fetch object with id ".$objid, LOG_ERR); return -1; } - $paymentpart->thirdparty = new stdClass(); - foreach ($tmpobject->thirdparty as $key => $value) { - if (in_array($key, $arrayoffieldstoexclude)) { - continue; // Discard some properties - } - if (!in_array($key, array( - 'name', 'name_alias', 'ref_ext', 'address', 'zip', 'town', 'state_code', 'country_code', 'idprof1', 'idprof2', 'idprof3', 'idprof4', 'idprof5', 'idprof6', 'phone', 'fax', 'email', 'barcode', - 'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur' - ))) { - continue; // Discard if not into a dedicated list - } - if (!is_object($value) && !is_null($value) && $value !== '') { - $paymentpart->thirdparty->{$key} = $value; - } - } - } + $paymentpart = new stdClass(); + $paymentpart->amount = $amount; - // Init object to avoid warnings - if ($this->element == 'payment_donation') { - $paymentpart->donation = new stdClass(); - } else { - $paymentpart->invoice = new stdClass(); - } + if (!in_array($this->element, array('payment_donation', 'payment_various'))) { + $result = $tmpobject->fetch_thirdparty(); + if ($result == 0) { + $this->error = 'Failed to fetch thirdparty for object with id '.$tmpobject->id; + $this->errors[] = $this->error; + dol_syslog("Failed to fetch thirdparty for object with id ".$tmpobject->id, LOG_ERR); + return -1; + } elseif ($result < 0) { + $this->error = $tmpobject->error; + $this->errors = $tmpobject->errors; + return -1; + } - if ($this->element != 'payment_various') { - foreach ($tmpobject as $key => $value) { - if (in_array($key, $arrayoffieldstoexclude)) { - continue; // Discard some properties - } - if (!in_array($key, array( - 'ref', 'ref_client', 'ref_supplier', 'date', 'datef', 'type', 'total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2', 'revenuestamp', 'datepointoftax', 'note_public' - ))) { - continue; // Discard if not into a dedicated list - } - if (!is_object($value) && !is_null($value) && $value !== '') { - if ($this->element == 'payment_donation') { - $paymentpart->donation->{$key} = $value; - } elseif ($this->element == 'payment_various') { - $paymentpart->various->{$key} = $value; - } else { - $paymentpart->invoice->{$key} = $value; + $paymentpart->thirdparty = new stdClass(); + foreach ($tmpobject->thirdparty as $key => $value) { + if (in_array($key, $arrayoffieldstoexclude)) { + continue; // Discard some properties + } + if (!in_array($key, array( + 'name', 'name_alias', 'ref_ext', 'address', 'zip', 'town', 'state_code', 'country_code', 'idprof1', 'idprof2', 'idprof3', 'idprof4', 'idprof5', 'idprof6', 'phone', 'fax', 'email', 'barcode', + 'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur' + ))) { + continue; // Discard if not into a dedicated list + } + if (!is_object($value) && !is_null($value) && $value !== '') { + $paymentpart->thirdparty->{$key} = $value; } } } - $paymentpartnumber++; // first payment will be 1 - $this->object_data->payment_part[$paymentpartnumber] = $paymentpart; + // Init object to avoid warnings + if ($this->element == 'payment_donation') { + $paymentpart->donation = new stdClass(); + } else { + $paymentpart->invoice = new stdClass(); + } + + if ($this->element != 'payment_various') { + foreach ($tmpobject as $key => $value) { + if (in_array($key, $arrayoffieldstoexclude)) { + continue; // Discard some properties + } + if (!in_array($key, array( + 'ref', 'ref_client', 'ref_supplier', 'date', 'datef', 'type', 'total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2', 'revenuestamp', 'datepointoftax', 'note_public' + ))) { + continue; // Discard if not into a dedicated list + } + if (!is_object($value) && !is_null($value) && $value !== '') { + if ($this->element == 'payment_donation') { + $paymentpart->donation->{$key} = $value; + } elseif ($this->element == 'payment_various') { + $paymentpart->various->{$key} = $value; + } else { + $paymentpart->invoice->{$key} = $value; + } + } + } + + $paymentpartnumber++; // first payment will be 1 + $this->object_data->payment_part[$paymentpartnumber] = $paymentpart; + } } + } elseif (!empty($object->amount)) { + $totalamount = $object->amount; } $this->object_data->amount = $totalamount; diff --git a/htdocs/categories/class/categorie.class.php b/htdocs/categories/class/categorie.class.php index fccea96e5ad..bb70b520fa1 100644 --- a/htdocs/categories/class/categorie.class.php +++ b/htdocs/categories/class/categorie.class.php @@ -34,6 +34,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.class.php'; require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; @@ -56,6 +57,7 @@ class Categorie extends CommonObject const TYPE_WAREHOUSE = 'warehouse'; const TYPE_ACTIONCOMM = 'actioncomm'; const TYPE_WEBSITE_PAGE = 'website_page'; + const TYPE_TICKET = 'ticket'; /** * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png @@ -78,7 +80,8 @@ class Categorie extends CommonObject 'bank_line' => 8, 'warehouse' => 9, 'actioncomm' => 10, - 'website_page' => 11 + 'website_page' => 11, + 'ticket' => 12 ); /** @@ -98,7 +101,8 @@ class Categorie extends CommonObject 8 => 'bank_line', 9 => 'warehouse', 10 => 'actioncomm', - 11 => 'website_page' + 11 => 'website_page', + 12 => 'ticket' ); /** @@ -141,7 +145,8 @@ class Categorie extends CommonObject 'project' => 'Project', 'warehouse'=> 'Entrepot', 'actioncomm' => 'ActionComm', - 'website_page' => 'WebsitePage' + 'website_page' => 'WebsitePage', + 'ticket' => 'Ticket' ); /** @@ -234,6 +239,8 @@ class Categorie extends CommonObject * @see Categorie::TYPE_WAREHOUSE * @see Categorie::TYPE_ACTIONCOMM * @see Categorie::TYPE_WEBSITE_PAGE + * @see Categorie::TYPE_TICKET + */ public $type; diff --git a/htdocs/categories/viewcat.php b/htdocs/categories/viewcat.php index eeca990ef28..8ebb9f43b1e 100644 --- a/htdocs/categories/viewcat.php +++ b/htdocs/categories/viewcat.php @@ -142,6 +142,11 @@ if ($id > 0 && $removeelem > 0) { $tmpobject = new User($db); $result = $tmpobject->fetch($removeelem); $elementtype = 'user'; + } elseif ($type == Categorie::TYPE_TICKET && $user->rights->ticket->write) { + require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; + $tmpobject = new Ticket($db); + $result = $tmpobject->fetch($removeelem); + $elementtype = 'ticket'; } $result = $object->del_type($tmpobject, $elementtype); @@ -167,7 +172,8 @@ if ($user->rights->categorie->supprimer && $action == 'confirm_delete' && $confi if ($elemid && $action == 'addintocategory' && (($type == Categorie::TYPE_PRODUCT && ($user->rights->produit->creer || $user->rights->service->creer)) || ($type == Categorie::TYPE_CUSTOMER && $user->rights->societe->creer) || - ($type == Categorie::TYPE_SUPPLIER && $user->rights->societe->creer) + ($type == Categorie::TYPE_SUPPLIER && $user->rights->societe->creer) || + ($type == Categorie::TYPE_TICKET && $user->rights->ticket->write) )) { if ($type == Categorie::TYPE_PRODUCT) { require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; @@ -181,6 +187,10 @@ if ($elemid && $action == 'addintocategory' && require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; $newobject = new Societe($db); $elementtype = 'supplier'; + } elseif ($type == Categorie::TYPE_TICKET) { + require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; + $newobject = new Ticket($db); + $elementtype = 'ticket'; } $result = $newobject->fetch($elemid); @@ -1024,6 +1034,78 @@ if ($type == Categorie::TYPE_WAREHOUSE) { } } +if ($type == Categorie::TYPE_TICKET) { + $permission = ($user->rights->categorie->creer || $user->rights->categorie->creer); + + $tickets = $object->getObjectsInCateg($type, 0, $limit, $offset); + if ($tickets < 0) { + dol_print_error($db, $object->error, $object->errors); + } else { + // Form to add record into a category + $showclassifyform = 1; + if ($showclassifyform) { + print '
'; + print '
'; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print '
'; + print $langs->trans("AddTicketIntoCategory").'  '; + $form->selectTickets('', 'elemid'); + print '
'; + print '
'; + } + + print '
'; + print ''; + print ''; + print ''; + print ''; + print ''; + + print '
'; + $param = '&limit='.$limit.'&id='.$id.'&type='.$type; $num = count($tickets); $nbtotalofrecords = ''; $newcardbutton = ''; + print_barre_liste($langs->trans("Ticket"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'ticket', 0, $newcardbutton, '', $limit); + + + print ''."\n"; + print ''."\n"; + + if (count($tickets) > 0) { + $i = 0; + foreach ($tickets as $ticket) { + $i++; + if ($i > $limit) break; + + print "\t".''."\n"; + print '\n"; + print '\n"; + // Link to delete from category + print ''; + print "\n"; + } + } else { + print ''; + } + print "
'.$langs->trans("Ref").'
'; + print $ticket->getNomUrl(1); + print "'.$ticket->label."'; + if ($permission) { + print ""; + print $langs->trans("DeleteFromCat"); + print img_picto($langs->trans("DeleteFromCat"), 'unlink', '', false, 0, 0, '', 'paddingleft'); + print ""; + } + print '
'.$langs->trans("ThisCategoryHasNoItems").'
\n"; + + print '
'."\n"; + } +} // End of page llxFooter(); diff --git a/htdocs/comm/index.php b/htdocs/comm/index.php index 9d0d9f1befc..11accdb3f0a 100644 --- a/htdocs/comm/index.php +++ b/htdocs/comm/index.php @@ -497,6 +497,50 @@ if ((!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SU } } + +/* + * Draft interventionals + */ +if (!empty($conf->ficheinter->enabled)) { + $sql = "SELECT f.rowid, f.ref, s.nom as name, s.rowid as socid"; + $sql .= " FROM ".MAIN_DB_PREFIX."fichinter as f"; + $sql .= ", ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$socid) { + $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + } + $sql .= " WHERE f.entity IN (".getEntity('intervention').")"; + $sql .= " AND f.fk_soc = s.rowid"; + $sql .= " AND f.fk_statut = 0"; + if ($socid) { + $sql .= " AND f.fk_soc = ".((int) $socid); + } + if (!$user->rights->societe->client->voir && !$socid) { + $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id); + } + + $resql = $db->query($sql); + if ($resql) { + print '
'; + print ''; + print ''; + print ''; + $langs->load("fichinter"); + $num = $db->num_rows($resql); + if ($num) { + $i = 0; + while ($i < $num) { + $obj = $db->fetch_object($resql); + print ''; + print '"; + print ''; + $i++; + } + } + print "
'.$langs->trans("DraftFichinter").'
'; + print "rowid."\">".img_object($langs->trans("ShowFichinter"), "intervention").' '.$obj->ref."'.img_object($langs->trans("ShowCompany"), "company").' '.dol_trunc($obj->name, 24).'
"; + } +} + print '
'; print '
'; diff --git a/htdocs/compta/facture/list.php b/htdocs/compta/facture/list.php index cc52984d6d4..f25b2b05975 100644 --- a/htdocs/compta/facture/list.php +++ b/htdocs/compta/facture/list.php @@ -1043,7 +1043,7 @@ if ($resql) { $moreforfilter .= img_picto($tmptitle, 'user', 'class="pictofixedwidth"').$form->select_dolusers($search_user, 'search_user', $tmptitle, '', 0, '', '', 0, 0, 0, '', 0, '', 'maxwidth250'); $moreforfilter .= '
'; } - // If the user can view prospects other than his' + // Filter on product tags if (!empty($conf->categorie->enabled) && $user->rights->categorie->lire && ($user->rights->produit->lire || $user->rights->service->lire)) { include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; $moreforfilter .= '
'; diff --git a/htdocs/compta/index.php b/htdocs/compta/index.php index 8db217747ef..b1ad18e6461 100644 --- a/htdocs/compta/index.php +++ b/htdocs/compta/index.php @@ -226,7 +226,7 @@ if (!empty($conf->facture->enabled) && !empty($user->rights->facture->lire)) { print $thirdpartystatic->getNomUrl(1, 'customer', 44); print ''; if (!empty($conf->global->MAIN_SHOW_HT_ON_SUMMARY)) { - print ''.price($obj->total_ht).''; + print ''.price($obj->total_ht).''; } print ''.price($obj->total_ttc).''; print ''.dol_print_date($db->jdate($obj->tms), 'day').''; diff --git a/htdocs/core/actions_massactions.inc.php b/htdocs/core/actions_massactions.inc.php index cd8a3817602..e5d4de26278 100644 --- a/htdocs/core/actions_massactions.inc.php +++ b/htdocs/core/actions_massactions.inc.php @@ -1470,6 +1470,80 @@ if (!$error && ($action == 'affecttag' && $confirm == 'yes') && $permissiontoadd } } +if (!$error && ($massaction == 'enable' || ($action == 'enable' && $confirm == 'yes')) && $permissiontoadd) { + $db->begin(); + + $objecttmp = new $objectclass($db); + $nbok = 0; + foreach ($toselect as $toselectid) { + $result = $objecttmp->fetch($toselectid); + if ($result>0) { + if (in_array($objecttmp->element, array('societe'))) { + $result =$objecttmp->setStatut(1); + } + if ($result <= 0) { + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } else { + $nbok++; + } + } else { + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } + } + + if (!$error) { + if ($nbok > 1) { + setEventMessages($langs->trans("RecordsEnabled", $nbok), null, 'mesgs'); + } else { + setEventMessages($langs->trans("RecordEnabled"), null, 'mesgs'); + } + $db->commit(); + } else { + $db->rollback(); + } +} + +if (!$error && ($massaction == 'disable' || ($action == 'disable' && $confirm == 'yes')) && $permissiontoadd) { + $db->begin(); + + $objecttmp = new $objectclass($db); + $nbok = 0; + foreach ($toselect as $toselectid) { + $result = $objecttmp->fetch($toselectid); + if ($result>0) { + if (in_array($objecttmp->element, array('societe'))) { + $result =$objecttmp->setStatut(0); + } + if ($result <= 0) { + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } else { + $nbok++; + } + } else { + setEventMessages($objecttmp->error, $objecttmp->errors, 'errors'); + $error++; + break; + } + } + + if (!$error) { + if ($nbok > 1) { + setEventMessages($langs->trans("RecordsDisabled", $nbok), null, 'mesgs'); + } else { + setEventMessages($langs->trans("RecordDisabled"), null, 'mesgs'); + } + $db->commit(); + } else { + $db->rollback(); + } +} + $parameters['toselect'] = $toselect; $parameters['uploaddir'] = $uploaddir; $parameters['massaction'] = $massaction; diff --git a/htdocs/core/boxes/box_factures.php b/htdocs/core/boxes/box_factures.php index 2a97d738cd8..12382ccb583 100644 --- a/htdocs/core/boxes/box_factures.php +++ b/htdocs/core/boxes/box_factures.php @@ -180,7 +180,7 @@ class box_factures extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="right nowraponall"', + 'td' => 'class="right nowraponall amount"', 'text' => price($objp->total_ht, 0, $langs, 0, -1, -1, $conf->currency), ); diff --git a/htdocs/core/boxes/box_factures_imp.php b/htdocs/core/boxes/box_factures_imp.php index 391fc2e7cd0..400ae910749 100644 --- a/htdocs/core/boxes/box_factures_imp.php +++ b/htdocs/core/boxes/box_factures_imp.php @@ -182,7 +182,7 @@ class box_factures_imp extends ModeleBoxes ); $this->info_box_contents[$line][] = array( - 'td' => 'class="nowraponall right"', + 'td' => 'class="nowraponall right amount"', 'text' => price($objp->total_ht, 0, $langs, 0, -1, -1, $conf->currency), ); diff --git a/htdocs/core/class/commonobject.class.php b/htdocs/core/class/commonobject.class.php index b3ae722a5d3..ceac7015065 100644 --- a/htdocs/core/class/commonobject.class.php +++ b/htdocs/core/class/commonobject.class.php @@ -6521,11 +6521,11 @@ abstract class CommonObject } elseif (in_array($type, array('int', 'integer'))) { $tmp = explode(',', $size); $newsize = $tmp[0]; - $out = ''; + $out = ' 0 ? ' maxlength="'.$newsize.'"' : '').' value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>'; } elseif (in_array($type, array('real'))) { $out = ''; } elseif (preg_match('/varchar/', $type)) { - $out = ''; + $out = ' 0 ? ' maxlength="'.$size.'"' : '').' value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>'; } elseif (in_array($type, array('mail', 'phone', 'url'))) { $out = ''; } elseif (preg_match('/^text/', $type)) { @@ -7492,7 +7492,7 @@ abstract class CommonObject if ($display_type == 'card') { $out .= ''; - if ( ! empty($conf->global->MAIN_VIEW_LINE_NUMBER) ) { + if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER) && $action == 'view') { $out .= ''; } $out .= ' attrval, [...]) + * @param string $nooutput No print, return the output into a string + * @return void|string + */ + public function selectTickets($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0) + { + global $langs, $conf; + + $out = ''; + + // check parameters + if (is_null($ajaxoptions)) $ajaxoptions = array(); + + if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) { + $placeholder = ''; + + if ($selected && empty($selected_input_value)) { + require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; + $tickettmpselect = new Ticket($this->db); + $tickettmpselect->fetch($selected); + $selected_input_value = $tickettmpselect->ref; + unset($tickettmpselect); + } + + $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions); + + if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel").' : '; + elseif ($hidelabel > 1) { + $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"'; + if ($hidelabel == 2) { + $out .= img_picto($langs->trans("Search"), 'search'); + } + } + $out .= 'global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />'; + if ($hidelabel == 3) { + $out .= img_picto($langs->trans("Search"), 'search'); + } + } else { + $out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss); + } + + if (empty($nooutput)) print $out; + else return $out; + } + + + /** + * Return list of tickets. + * Called by selectTickets. + * + * @param int $selected Preselected ticket + * @param string $htmlname Name of select html + * @param string $filtertype Filter on ticket type + * @param int $limit Limit on number of returned lines + * @param string $filterkey Filter on product + * @param int $status Ticket status + * @param int $outputmode 0=HTML select string, 1=Array + * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. + * @param int $forcecombo Force to use combo box + * @param string $morecss Add more css on select + * @return array Array of keys for json + */ + public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '') + { + global $langs, $conf, $user, $db; + + $out = ''; + $outarray = array(); + + $selectFields = " p.rowid, p.ref, p.message"; + + $sql = "SELECT "; + $sql .= $selectFields; + $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p"; + $sql .= ' WHERE p.entity IN ('.getEntity('ticket').')'; + + // Add criteria on ref/label + if ($filterkey != '') { + $sql .= ' AND ('; + $prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on + // For natural search + $scrit = explode(' ', $filterkey); + $i = 0; + if (count($scrit) > 1) $sql .= "("; + foreach ($scrit as $crit) { + if ($i > 0) $sql .= " AND "; + $sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'"; + $sql .= ")"; + $i++; + } + if (count($scrit) > 1) $sql .= ")"; + $sql .= ')'; + } + + $sql .= $this->db->plimit($limit, 0); + + // Build output string + dol_syslog(get_class($this)."::selectTicketsList search tickets", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) { + require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php'; + + $num = $this->db->num_rows($result); + + $events = null; + + if (!$forcecombo) { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname, $events, $conf->global->TICKET_USE_SEARCH_TO_SELECT); + } + + $out .= ''; + + $this->db->free($result); + + if (empty($outputmode)) return $out; + return $outarray; + } else { + dol_print_error($db); + } + } + + /** + * constructTicketListOption. + * This define value for &$opt and &$optJson. + * + * @param resource $objp Result set of fetch + * @param string $opt Option (var used for returned value in string option format) + * @param string $optJson Option (var used for returned value in json format) + * @param string $selected Preselected value + * @param string $filterkey Filter key to highlight + * @return void + */ + protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '') + { + global $langs, $conf, $user, $db; + + $outkey = ''; + $outval = ''; + $outref = ''; + $outlabel = ''; + $outtype = ''; + + $label = $objp->label; + + $outkey = $objp->rowid; + $outref = $objp->ref; + $outlabel = $objp->label; + $outtype = $objp->fk_product_type; + + $opt = '\n"; + $optJson = array('key'=>$outkey, 'value'=>$outref, 'type'=>$outtypem); + } + /** * Generic method to select a component from a combo list. diff --git a/htdocs/core/class/html.formticket.class.php b/htdocs/core/class/html.formticket.class.php index 0a4a622f4a8..53ba83015b3 100644 --- a/htdocs/core/class/html.formticket.class.php +++ b/htdocs/core/class/html.formticket.class.php @@ -272,6 +272,15 @@ class FormTicket print ''; } + //Categories + if ($conf->categorie->enabled) { + // Categories + print ''.$langs->trans("Categories").''; + $cate_arbo = $form->select_all_categories(Categorie::TYPE_TICKET, '', 'parent', 64, 0, 1); + print img_picto('', 'category').$form->multiselectarray('categories', $cate_arbo, GETPOST('categories', 'array'), '', 0, 'quatrevingtpercent widthcentpercentminusx', 0, 0); + print ""; + } + // Attached files if (!empty($this->withfile)) { // Define list of attached files @@ -656,11 +665,11 @@ class FormTicket print ajax_combobox('select'.$htmlname); } elseif ($htmlname!='') { $groupticket=GETPOST($htmlname, 'aZ09'); - $groupticketchild=GETPOST($htmlname.'_child', 'aZ09'); + $child_id=GETPOST($htmlname.'_child_id', 'aZ09')?GETPOST($htmlname.'_child_id', 'aZ09'):0; $arraycodenotparent[] = ""; $arrayidused = array(); $stringtoprint = ''.$langs->trans("GroupOfTicket").' '; - $stringtoprint .= ''; $stringtoprint .= ''; $sql = "SELECT ctc.rowid, ctc.code, ctc.label, ctc.fk_parent, "; @@ -697,13 +706,16 @@ class FormTicket } if ($num_rows_level0 == 1) { return ''; + } else { + $stringtoprint .= ''; + $stringtoprint .= ''; } $stringtoprint .= ' '; $levelid = 1; while ($levelid <= $use_multilevel) { $tabscript = array(); - $stringtoprint .= ''; $stringtoprint .= ''; $sql = "SELECT ctc.rowid, ctc.code, ctc.label, ctc.fk_parent, ctcjoin.code as codefather, "; @@ -742,7 +754,7 @@ class FormTicket if ($isparent == 'NOTPARENT') { $arraycodenotparent[] = $groupvalue; } - $iselected = $groupticketchild == $obj->code ?'selected':''; + $iselected = $groupticket == $obj->code ?'selected':''; $stringtoprint .= ''; if (empty($tabscript[$groupcodefather])) { $tabscript[$groupcodefather] = 'if ($("#'.$htmlname.($levelid > 1 ?'_child_'.$levelid-1:'').'")[0].value == "'.dol_escape_js($groupcodefather).'"){ @@ -760,15 +772,43 @@ class FormTicket dol_print_error($this->db); } $stringtoprint .=''; - //$stringtoprint .= ajax_combobox($htmlname.'_child_'.$levelid); $stringtoprint .=''; } - + $stringtoprint .=''; $stringtoprint .= ajax_combobox($htmlname); return $stringtoprint; diff --git a/htdocs/core/js/lib_foot.js.php b/htdocs/core/js/lib_foot.js.php index beb2788ca24..00f161a31ef 100644 --- a/htdocs/core/js/lib_foot.js.php +++ b/htdocs/core/js/lib_foot.js.php @@ -263,6 +263,8 @@ print '$( document ).ready(function() { var confirmContent = $(this).attr(\'data-confirm-content\'); var confirmActionBtnLabel = $(this).attr(\'data-confirm-action-btn-label\'); var confirmCancelBtnLabel = $(this).attr(\'data-confirm-cancel-btn-label\'); + var confirmModal = $(this).attr(\'data-confirm-modal\'); + if(confirmModal == undefined){ confirmModal = false; } var confirmId = \'confirm-dialog-box\'; if($(this).attr(\'id\') != undefined){ var confirmId = confirmId + "-" + $(this).attr(\'id\'); } @@ -277,8 +279,9 @@ print '$( document ).ready(function() { $confirmBox.dialog({ autoOpen: true, - modal: false, + modal: confirmModal, //width: Math.min($( window ).width() - 50, 1700), + width: \'auto\', dialogClass: \'confirm-dialog-box\', buttons: [ { @@ -297,11 +300,11 @@ print '$( document ).ready(function() { } ], close: function( event, ui ) { - $(\'#\'+confirmBox).remove(); -}, + $(\'#\'+confirmBox).remove(); + }, open: function( event, ui ) { - $confirmBox.html(confirmContent); -} + $confirmBox.html(confirmContent); + } }); }); }); diff --git a/htdocs/core/lib/company.lib.php b/htdocs/core/lib/company.lib.php index 21b6f9a68e4..cb710c19875 100644 --- a/htdocs/core/lib/company.lib.php +++ b/htdocs/core/lib/company.lib.php @@ -1899,7 +1899,7 @@ function show_actions_done($conf, $langs, $db, $filterobj, $objcon = '', $noprin } // Status - $out .= ''.$actionstatic->LibStatut($histo[$key]['percent'], 3, 0, $histo[$key]['datestart']).''; + $out .= ''.$actionstatic->LibStatut($histo[$key]['percent'], 2, 0, $histo[$key]['datestart']).''; // Actions $out .= ''; diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 6b57ea2b63d..a8879ad64f5 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -6828,6 +6828,8 @@ function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $substitutionarray = array_merge($substitutionarray, array( '__MYCOMPANY_NAME__' => $mysoc->name, '__MYCOMPANY_EMAIL__' => $mysoc->email, + '__MYCOMPANY_PHONE__' => $mysoc->phone, + '__MYCOMPANY_FAX__' => $mysoc->fax, '__MYCOMPANY_PROFID1__' => $mysoc->idprof1, '__MYCOMPANY_PROFID2__' => $mysoc->idprof2, '__MYCOMPANY_PROFID3__' => $mysoc->idprof3, @@ -8487,8 +8489,10 @@ function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, if (!empty($hookmanager)) { $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head); $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters); - if ($reshook > 0) { + if ($reshook > 0) { // Hook ask to replace completely the array $head = $hookmanager->resArray; + } else { // Hook + $head = array_merge($head, $hookmanager->resArray); } $h = count($head); } @@ -9768,7 +9772,23 @@ function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $st * @param string $url the url for link * @param string $id attribute id of button * @param int $userRight user action right - * @param array $params various params for future : recommended rather than adding more function arguments + * // phpcs:disable + * @param array $params = [ // Various params for future : recommended rather than adding more function arguments + * 'attr' => [ // to add or override button attributes + * 'xxxxx' => '', // your xxxxx attribute you want + * 'class' => '', // to add more css class to the button class attribute + * 'classOverride' => '' // to replace class attribute of the button + * ], + * 'confirm' => [ + * 'url' => 'http://', // Overide Url to go when user click on action btn, if empty default url is $url.?confirm=yes, for no js compatibility use $url for fallback confirm. + * 'title' => '', // Overide title of modal, if empty default title use "ConfirmBtnCommonTitle" lang key + * 'action-btn-label' => '', // Overide label of action button, if empty default label use "Confirm" lang key + * 'cancel-btn-label' => '', // Overide label of cancel button, if empty default label use "CloseDialog" lang key + * 'content' => '', // Overide text of content, if empty default content use "ConfirmBtnCommonContent" lang key + * 'modal' => true, // true|false to display dialog as a modal (with dark background) + * ], + * ] + * // phpcs:enable * @return string html button */ function dolGetButtonAction($label, $html = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array()) @@ -9816,7 +9836,7 @@ function dolGetButtonAction($label, $html = '', $actionType = 'default', $url = } // Js Confirm button - if (!empty($params['confirm'])) { + if ($userRight && !empty($params['confirm'])) { if (!is_array($params['confirm'])) { $params['confirm'] = array( 'url' => $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes' @@ -9827,8 +9847,11 @@ function dolGetButtonAction($label, $html = '', $actionType = 'default', $url = $attr['data-confirm-url'] = $params['confirm']['url']; $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label); $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label); + $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']); $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm'); $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog'); + $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true; + $attr['class'].= ' butActionConfirm'; } diff --git a/htdocs/core/modules/modBarcode.class.php b/htdocs/core/modules/modBarcode.class.php index fab12a0ae59..877dca880f7 100644 --- a/htdocs/core/modules/modBarcode.class.php +++ b/htdocs/core/modules/modBarcode.class.php @@ -93,6 +93,7 @@ class modBarcode extends DolibarrModules // Main menu entries $r = 0; + $this->menu[$r] = array( 'fk_menu'=>'fk_mainmenu=tools', // Use 'fk_mainmenu=xxx' or 'fk_mainmenu=xxx,fk_leftmenu=yyy' where xxx is mainmenucode and yyy is a leftmenucode 'mainmenu'=>'tools', @@ -106,7 +107,7 @@ class modBarcode extends DolibarrModules 'enabled'=>'$conf->barcode->enabled', // Define condition to show or hide menu entry. Use '$conf->mymodule->enabled' if entry must be visible if module is enabled. Use '$leftmenu==\'system\'' to show if leftmenu system is selected. 'perms'=>'($conf->global->MAIN_USE_ADVANCED_PERMS && $user->rights->barcode->lire_advance) || (! $conf->global->MAIN_USE_ADVANCED_PERMS)', // Use 'perms'=>'$user->rights->mymodule->level1->level2' if you want your menu with a permission rules 'target'=>'', - 'user'=>2, // 0=Menu for internal users, 1=external users, 2=both + 'user'=>0, // 0=Menu for internal users, 1=external users, 2=both ); $r++; diff --git a/htdocs/core/modules/modHoliday.class.php b/htdocs/core/modules/modHoliday.class.php index 2d34cee5626..be0b39ecc7e 100644 --- a/htdocs/core/modules/modHoliday.class.php +++ b/htdocs/core/modules/modHoliday.class.php @@ -135,6 +135,28 @@ class modHoliday extends DolibarrModules //$r++; + // Cronjobs + $arraydate = dol_getdate(dol_now()); + $datestart = dol_mktime(4, 0, 0, $arraydate['mon'], $arraydate['mday'], $arraydate['year']); + $this->cronjobs = array( + 0 => array( + 'label' => 'HolidayBalanceMonthlyUpdate', + 'jobtype' => 'method', + 'class' => 'holiday/class/holiday.class.php', + 'objectname' => 'Holiday', + 'method' => 'updateBalance', + 'parameters' => '', + 'comment' => 'Update holiday balance every month', + 'frequency' => 1, + 'unitfrequency' => 3600 * 24, + 'priority' => 50, + 'status' => 1, + 'test' => '$conf->holiday->enabled', + 'datestart' => $datestart + ) + ); + + // Permissions $this->rights = array(); // Permission array used by this module $r = 0; diff --git a/htdocs/core/modules/modTicket.class.php b/htdocs/core/modules/modTicket.class.php index 15f2b5fecca..705c89be80f 100644 --- a/htdocs/core/modules/modTicket.class.php +++ b/htdocs/core/modules/modTicket.class.php @@ -283,6 +283,19 @@ class modTicket extends DolibarrModules 'target' => '', 'user' => 0); $r++; + + $this->menu[$r] = array('fk_menu' => 'fk_mainmenu=ticket,fk_leftmenu=ticket', + 'type' => 'left', + 'titre' => 'Categories', + 'mainmenu' => 'ticket', + 'url' => '/categories/index.php?type=12', + 'langs' => 'ticket', + 'position' => 107, + 'enabled' => '$conf->categorie->enabled', + 'perms' => '$user->rights->ticket->read', + 'target' => '', + 'user' => 0); + $r++; } /** diff --git a/htdocs/core/tpl/massactions_pre.tpl.php b/htdocs/core/tpl/massactions_pre.tpl.php index 4b8680e11bb..c40f896e702 100644 --- a/htdocs/core/tpl/massactions_pre.tpl.php +++ b/htdocs/core/tpl/massactions_pre.tpl.php @@ -194,6 +194,14 @@ if ($massaction == 'presend') { print dol_get_fiche_end(); } + +if ($massaction == 'preenable') { + print $form->formconfirm($_SERVER["PHP_SELF"], $langs->trans("ConfirmMassEnabling"), $langs->trans("ConfirmMassEnablingQuestion", count($toselect)), "enable", null, '', 0, 200, 500, 1); +} +if ($massaction == 'predisable') { + print $form->formconfirm($_SERVER["PHP_SELF"], $langs->trans("ConfirmMassDisabling"), $langs->trans("ConfirmMassDisablingQuestion", count($toselect)), "disable", null, '', 0, 200, 500, 1); +} + // Allow Pre-Mass-Action hook (eg for confirmation dialog) $parameters = array( 'toselect' => $toselect, diff --git a/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php index cab0702c5c1..741cc4d09bc 100644 --- a/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php +++ b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php @@ -118,6 +118,8 @@ class InterfaceActionsBlockedLog extends DolibarrTriggers foreach ($object->amounts as $amount) { $amounts += price2num($amount); } + } elseif (!empty($object->amount)) { + $amounts = $object->amount; } } elseif (strpos($action, 'PAYMENT') !== false && !in_array($action, array('PAYMENT_ADD_TO_BANK'))) { $qualified++; diff --git a/htdocs/emailcollector/class/emailcollector.class.php b/htdocs/emailcollector/class/emailcollector.class.php index 0b45db6a808..16df7774470 100644 --- a/htdocs/emailcollector/class/emailcollector.class.php +++ b/htdocs/emailcollector/class/emailcollector.class.php @@ -324,6 +324,9 @@ class EmailCollector extends CommonObject // Clear fields $object->ref = "copy_of_".$object->ref; $object->title = $langs->trans("CopyOf")." ".$object->title; + if (empty($object->host)) { + $object->host = 'imap.example.com'; + } // ... // Clear extrafields that are unique if (is_array($object->array_options) && count($object->array_options) > 0) { @@ -849,11 +852,11 @@ class EmailCollector extends CommonObject $regexstring = ''; //$transformationstring=''; $regforregex = array(); - if (preg_match('/^EXTRACT:([a-zA-Z0-9]+):(.*):([^:])$/', $valueforproperty, $regforregex)) { + if (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*):([^:])$/', $valueforproperty, $regforregex)) { $sourcefield = $regforregex[1]; $regexstring = $regforregex[2]; //$transofrmationstring=$regforregex[3]; - } elseif (preg_match('/^EXTRACT:([a-zA-Z0-9]+):(.*)$/', $valueforproperty, $regforregex)) { + } elseif (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*)$/', $valueforproperty, $regforregex)) { $sourcefield = $regforregex[1]; $regexstring = $regforregex[2]; } @@ -1199,15 +1202,20 @@ class EmailCollector extends CommonObject $iforemailloop = 0; foreach ($arrayofemail as $imapemail) { if ($nbemailprocessed > 1000) { - break; // Do not process more than 1000 email per launch (this is a different protection than maxnbcollectedpercollect + break; // Do not process more than 1000 email per launch (this is a different protection than maxnbcollectedpercollect) } $iforemailloop++; + // GET header and overview datas + $header = imap_fetchheader($connection, $imapemail, 0); + $overview = imap_fetch_overview($connection, $imapemail, 0); + + /* print $header; var_dump($overview); */ + + // Process $header of email $header = preg_replace('/\r\n\s+/m', ' ', $header); // When a header line is on several lines, merge lines - /*print $header; - print $header;*/ $matches = array(); preg_match_all('/([^: ]+): (.+?(?:\r\n\s(?:.+?))*)\r\n/m', $header, $matches); @@ -1297,9 +1305,6 @@ class EmailCollector extends CommonObject $this->db->begin(); - // GET Email meta datas - $overview = imap_fetch_overview($connection, $imapemail, 0); - dol_syslog("msgid=".$overview[0]->message_id." date=".dol_print_date($overview[0]->udate, 'dayrfc', 'gmt')." from=".$overview[0]->from." to=".$overview[0]->to." subject=".$overview[0]->subject); $overview[0]->subject = $this->decodeSMTPSubject($overview[0]->subject); @@ -1309,10 +1314,14 @@ class EmailCollector extends CommonObject // Removed emojis $overview[0]->subject = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $overview[0]->subject); - // Parse IMAP email structure + // GET IMAP email structure/content + global $htmlmsg, $plainmsg, $charset, $attachments; + $this->getmsg($connection, $imapemail); + //print $plainmsg; var_dump($plainmsg); exit; + //$htmlmsg,$plainmsg,$charset,$attachments $messagetext = $plainmsg ? $plainmsg : dol_string_nohtmltag($htmlmsg, 0); // Removed emojis @@ -1640,13 +1649,15 @@ class EmailCollector extends CommonObject if ($operation['type'] == 'loadthirdparty' || $operation['type'] == 'loadandcreatethirdparty') { if (empty($operation['actionparam'])) { $errorforactions++; - $this->error = "Action loadthirdparty or loadandcreatethirdparty has empty parameter. Must be a rule like 'SET:xxx' or 'EXTRACT:(body|subject):regex' to define how to set or extract data"; + $this->error = "Action loadthirdparty or loadandcreatethirdparty has empty parameter. Must be a rule like 'name=HEADER:^From:(.*);' or 'name=SET:xxx' or 'name=EXTRACT:(body|subject):regex where 'name' can be replaced with 'id' or 'email' to define how to set or extract data. More properties can also be set, for example client=SET:2;"; $this->errors[] = $this->error; } else { $actionparam = $operation['actionparam']; + $idtouseforthirdparty = ''; $nametouseforthirdparty = ''; + $emailtouseforthirdparty = ''; - // $this->actionparam = 'SET:aaa' or 'EXTRACT:BODY:....' + // $actionparam = 'param=SET:aaa' or 'param=EXTRACT:BODY:....' $arrayvaluetouse = dolExplodeIntoArray($actionparam, ';', '='); foreach ($arrayvaluetouse as $propertytooverwrite => $valueforproperty) { $sourcestring = ''; @@ -1654,7 +1665,7 @@ class EmailCollector extends CommonObject $regexstring = ''; $regforregex = array(); - if (preg_match('/^EXTRACT:([a-zA-Z0-9]+):(.*)$/', $valueforproperty, $regforregex)) { + if (preg_match('/^EXTRACT:([a-zA-Z0-9_]+):(.*)$/', $valueforproperty, $regforregex)) { $sourcefield = $regforregex[1]; $regexstring = $regforregex[2]; } @@ -1674,10 +1685,18 @@ class EmailCollector extends CommonObject if (preg_match('/'.$regexstring.'/ms', $sourcestring, $regforval)) { //var_dump($regforval[count($regforval)-1]);exit; // Overwrite param $tmpproperty - $nametouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null; + if ($propertytooverwrite == 'id') { + $idtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null; + } elseif ($propertytooverwrite == 'email') { + $emailtouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null; + } else { + $nametouseforthirdparty = isset($regforval[count($regforval) - 1]) ? trim($regforval[count($regforval) - 1]) : null; + } } else { // Regex not found + $idtouseforthirdparty = null; $nametouseforthirdparty = null; + $emailtouseforthirdparty = null; } //var_dump($object->$tmpproperty);exit; } else { @@ -1689,7 +1708,13 @@ class EmailCollector extends CommonObject } elseif (preg_match('/^(SET|SETIFEMPTY):(.*)$/', $valueforproperty, $reg)) { //if (preg_match('/^options_/', $tmpproperty)) $object->array_options[preg_replace('/^options_/', '', $tmpproperty)] = $reg[1]; //else $object->$tmpproperty = $reg[1]; - $nametouseforthirdparty = $reg[2]; + if ($propertytooverwrite == 'id') { + $idtouseforthirdparty = $reg[2]; + } elseif ($propertytooverwrite == 'email') { + $emailtouseforthirdparty = $reg[2]; + } else { + $nametouseforthirdparty = $reg[2]; + } } else { $errorforactions++; $this->error = 'Bad syntax for description of action parameters: '.$actionparam; @@ -1698,8 +1723,8 @@ class EmailCollector extends CommonObject } } - if (!$errorforactions && $nametouseforthirdparty) { - $result = $thirdpartystatic->fetch(0, $nametouseforthirdparty); + if (!$errorforactions && ($idtouseforthirdparty || $emailtouseforthirdparty || $nametouseforthirdparty)) { + $result = $thirdpartystatic->fetch($idtouseforthirdparty, $nametouseforthirdparty, '', '', '', '', '', '', '', '', $emailtouseforthirdparty); if ($result < 0) { $errorforactions++; $this->error = 'Error when getting thirdparty with name '.$nametouseforthirdparty.' (may be 2 record exists with same name ?)'; @@ -1707,20 +1732,20 @@ class EmailCollector extends CommonObject break; } elseif ($result == 0) { if ($operation['type'] == 'loadthirdparty') { - dol_syslog("Third party with name ".$nametouseforthirdparty." was not found"); + dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." was not found"); $errorforactions++; $this->error = 'ErrorFailedToLoadThirdParty'; $this->errors[] = 'ErrorFailedToLoadThirdParty'; } elseif ($operation['type'] == 'loadandcreatethirdparty') { - dol_syslog("Third party with name ".$nametouseforthirdparty." was not found. We try to create it."); + dol_syslog("Third party with id=".$idtouseforthirdparty." email=".$emailtouseforthirdparty." name=".$nametouseforthirdparty." was not found. We try to create it."); // Create thirdparty $thirdpartystatic->name = $nametouseforthirdparty; if ($fromtext != $nametouseforthirdparty) { $thirdpartystatic->name_alias = $fromtext; } - $thirdpartystatic->email = $from; + $thirdpartystatic->email = ($emailtouseforthirdparty ? $emailtouseforthirdparty : $from); // Overwrite values with values extracted from source email $errorforthisaction = $this->overwritePropertiesOfObject($thirdpartystatic, $operation['actionparam'], $messagetext, $subject, $header); diff --git a/htdocs/fourn/class/fournisseur.commande.dispatch.class.php b/htdocs/fourn/class/fournisseur.commande.dispatch.class.php index 5457a9ee376..cd1ff267fb7 100644 --- a/htdocs/fourn/class/fournisseur.commande.dispatch.class.php +++ b/htdocs/fourn/class/fournisseur.commande.dispatch.class.php @@ -220,13 +220,12 @@ class CommandeFournisseurDispatch extends CommonObject $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element); if (!$notrigger) { - // Uncomment this and change MYOBJECT to your own tag if you - // want this action calls a trigger. - - //// Call triggers - //$result=$this->call_trigger('MYOBJECT_CREATE',$user); - //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail} - //// End call triggers + // Call triggers + $result=$this->call_trigger('LINERECEPTION_CREATE', $user); + if ($result < 0) { + $error++; + } + // End call triggers } } diff --git a/htdocs/holiday/class/holiday.class.php b/htdocs/holiday/class/holiday.class.php index 2b0641ff9ae..527c0353486 100644 --- a/htdocs/holiday/class/holiday.class.php +++ b/htdocs/holiday/class/holiday.class.php @@ -227,7 +227,7 @@ class Holiday extends CommonObject if ($result >= 0) { $this->db->commit(); - return 1; + return 0; // for cronjob use (0 is OK, any other value is an error code) } else { $this->db->rollback(); return -1; @@ -1251,6 +1251,8 @@ class Holiday extends CommonObject public function LibStatut($status, $mode = 0, $startdate = '') { // phpcs:enable + global $langs; + if (empty($this->labelStatus) || empty($this->labelStatusShort)) { global $langs; //$langs->load("mymodule"); @@ -1266,9 +1268,11 @@ class Holiday extends CommonObject $this->labelStatusShort[self::STATUS_REFUSED] = $langs->trans('RefuseCP'); } + $params = array(); $statusType = 'status6'; - if (!empty($startdate) && $startdate > dol_now()) { + if (!empty($startdate) && $startdate >= dol_now()) { // If not yet passed, we use a green "in live" color $statusType = 'status4'; + $params = array('tooltip'=>$this->labelStatus[$status].' - '.$langs->trans("Forthcoming")); } if ($status == self::STATUS_DRAFT) { $statusType = 'status0'; @@ -1283,7 +1287,7 @@ class Holiday extends CommonObject $statusType = 'status5'; } - return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode); + return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode, '', $params); } diff --git a/htdocs/holiday/list.php b/htdocs/holiday/list.php index ece1f9c9ec7..ab57a497e8e 100644 --- a/htdocs/holiday/list.php +++ b/htdocs/holiday/list.php @@ -730,6 +730,7 @@ if ($resql) { $holidaystatic->id = $obj->rowid; $holidaystatic->ref = ($obj->ref ? $obj->ref : $obj->rowid); $holidaystatic->statut = $obj->status; + $holidaystatic->date_debut = $db->jdate($obj->date_debut); // User $userstatic->id = $obj->fk_user; @@ -790,7 +791,7 @@ if ($resql) { } if (!empty($arrayfields['duration']['checked'])) { print ''; - $nbopenedday = num_open_day($db->jdate($obj->date_debut, 1), $db->jdate($obj->date_fin, 1), 0, 1, $obj->halfday); + $nbopenedday = num_open_day($db->jdate($obj->date_debut, 1), $db->jdate($obj->date_fin, 1), 0, 1, $obj->halfday); // user jdate(..., 1) because num_open_day need UTC dates print $nbopenedday.' '.$langs->trans('DurationDays'); print ''; if (!$i) { diff --git a/htdocs/install/mysql/migration/12.0.0-13.0.0.sql b/htdocs/install/mysql/migration/12.0.0-13.0.0.sql index 1ebb8e380a7..29c2aad9bd4 100644 --- a/htdocs/install/mysql/migration/12.0.0-13.0.0.sql +++ b/htdocs/install/mysql/migration/12.0.0-13.0.0.sql @@ -367,7 +367,7 @@ ALTER TABLE llx_actioncomm_reminder ADD UNIQUE uk_actioncomm_reminder_unique (fk ALTER TABLE llx_actioncomm_reminder ADD INDEX idx_actioncomm_reminder_status (status); - +ALTER TABLE llx_inventorydet ADD COLUMN fk_warehouse integer DEFAULT 0; ALTER TABLE llx_inventorydet ADD UNIQUE uk_inventorydet(fk_inventory, fk_warehouse, fk_product, batch); ALTER TABLE llx_commandedet ADD COLUMN ref_ext varchar(255) AFTER label; diff --git a/htdocs/install/mysql/tables/llx_facture.sql b/htdocs/install/mysql/tables/llx_facture.sql index acb43444ad2..ecdc44915bb 100644 --- a/htdocs/install/mysql/tables/llx_facture.sql +++ b/htdocs/install/mysql/tables/llx_facture.sql @@ -86,7 +86,7 @@ create table llx_facture fk_incoterms integer, -- for incoterms location_incoterms varchar(255), -- for incoterms - fk_mode_transport integer, -- for intracomm report + fk_transport_mode integer, -- for intracomm report situation_cycle_ref smallint, -- situation cycle reference situation_counter smallint, -- situation counter diff --git a/htdocs/install/mysql/tables/llx_facture_fourn.sql b/htdocs/install/mysql/tables/llx_facture_fourn.sql index e5e78f87028..1b7401898ab 100644 --- a/htdocs/install/mysql/tables/llx_facture_fourn.sql +++ b/htdocs/install/mysql/tables/llx_facture_fourn.sql @@ -74,7 +74,7 @@ create table llx_facture_fourn fk_incoterms integer, -- for incoterms location_incoterms varchar(255), -- for incoterms - fk_mode_transport integer, -- for intracomm report + fk_transport_mode integer, -- for intracomm report model_pdf varchar(255), last_main_doc varchar(255), -- relative filepath+filename of last main generated document diff --git a/htdocs/install/mysql/tables/llx_object_lang.sql b/htdocs/install/mysql/tables/llx_object_lang.sql index cc4352f3acc..01b70b208ff 100644 --- a/htdocs/install/mysql/tables/llx_object_lang.sql +++ b/htdocs/install/mysql/tables/llx_object_lang.sql @@ -25,7 +25,7 @@ create table llx_object_lang fk_object integer DEFAULT 0 NOT NULL, type_object varchar(32) NOT NULL, -- value found into $object->element: 'thirdparty', 'contact', '...' property varchar(32) NOT NULL, -- name of property - lang varchar(5) DEFAULT 0 NOT NULL, + lang varchar(5) DEFAULT '' NOT NULL, value text, import_key varchar(14) DEFAULT NULL )ENGINE=innodb; diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index 3f8077ba9f3..4ec6a69b053 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -2092,7 +2092,7 @@ LargerThan=Larger than IfTrackingIDFoundEventWillBeLinked=Note that If a tracking ID of an object is found into email, or if the email is an answer of an email aready collected and linked to an object, the created event will be automatically linked to the known related object. WithGMailYouCanCreateADedicatedPassword=With a GMail account, if you enabled the 2 steps validation, it is recommanded to create a dedicated second password for the application instead of using your own account passsword from https://myaccount.google.com/. EmailCollectorTargetDir=It may be a desired behaviour to move the email into another tag/directory when it was processed successfully. Just set name of directory here to use this feature (Do NOT use special characters in name). Note that you must also use a read/write login account. -EmailCollectorLoadThirdPartyHelp=You can use this action to use the email content to find and load an existing thirdparty in your database. The found (or created) thirdparty will be used for following actions that need it. In the parameter field you can use for example 'EXTRACT:BODY:Name:\s([^\s]*)' if you want to extract the name of the thirdparty from a string 'Name: name to find' found into the body. +EmailCollectorLoadThirdPartyHelp=You can use this action to use the email content to find and load an existing thirdparty in your database. The found (or created) thirdparty will be used for following actions that need it.
For example, if you want to create a thirdparty with a name extracted a string 'Name: name to find' found into the body, use sender email as email, you can set the parameter field like this:
'email=HEADER:^From:(.*);name=EXTRACT:BODY:Name:\s([^\s]*);client=SET:2;'
EndPointFor=End point for %s : %s DeleteEmailCollector=Delete email collector ConfirmDeleteEmailCollector=Are you sure you want to delete this email collector? diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang index 3ed7441afa5..cb97af5c183 100644 --- a/htdocs/langs/en_US/errors.lang +++ b/htdocs/langs/en_US/errors.lang @@ -264,6 +264,7 @@ ErrorAnAmountWithoutTaxIsRequired=Error, amount is mandatory ErrorAPercentIsRequired=Error, please fill in the percentage correctly ErrorYouMustFirstSetupYourChartOfAccount=You must first setup your chart of account ErrorFailedToFindEmailTemplate=Failed to find template with code name %s +ErrorDurationForServiceNotDefinedCantCalculateHourlyPrice=Duration not defined on service. No way to calculate the hourly price. # Warnings WarningParamUploadMaxFileSizeHigherThanPostMaxSize=Your PHP parameter upload_max_filesize (%s) is higher than PHP parameter post_max_size (%s). This is not a consistent setup. diff --git a/htdocs/langs/en_US/holiday.lang b/htdocs/langs/en_US/holiday.lang index 2393a02ee50..0e6b1d69b36 100644 --- a/htdocs/langs/en_US/holiday.lang +++ b/htdocs/langs/en_US/holiday.lang @@ -132,3 +132,4 @@ FreeLegalTextOnHolidays=Free text on PDF WatermarkOnDraftHolidayCards=Watermarks on draft leave requests HolidaysToApprove=Holidays to approve NobodyHasPermissionToValidateHolidays=Nobody has permission to validate holidays +HolidayBalanceMonthlyUpdate=Monthly update of holiday balance diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index 863b94af564..7dfafe73edb 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -1137,3 +1137,14 @@ CopiedToClipboard=Copied to clipboard InformationOnLinkToContract=This amount is only the total of all the lines of the contract. No notion of time is taken into consideration. ConfirmCancel=Are you sure you want to cancel EmailMsgID=Email MsgID +SetToEnabled=Set to enabled +SetToDisabled=Set to disabled +ConfirmMassEnabling=mass enabling confirmation +ConfirmMassEnablingQuestion=Are you sure you want to enable the %s selected record(s)? +ConfirmMassDisabling=mass disabling confirmation +ConfirmMassDisablingQuestion=Are you sure you want to disable the %s selected record(s)? +RecordsEnabled=%s record(s) enabled +RecordsDisabled=%s record(s) disabled +RecordEnabled=Record enabled +RecordDisabled=Record disabled +Forthcoming=Forthcoming diff --git a/htdocs/langs/fr_FR/categories.lang b/htdocs/langs/fr_FR/categories.lang index 4b806e1edbd..20bfb122c75 100644 --- a/htdocs/langs/fr_FR/categories.lang +++ b/htdocs/langs/fr_FR/categories.lang @@ -17,6 +17,7 @@ ContactsCategoriesArea=Espace tags/catégories de contacts AccountsCategoriesArea=Espace des tags/categories de comptes bancaires ProjectsCategoriesArea=Espace des tags/catégories des projets UsersCategoriesArea=Espace des tags/catégories des utilisateurs +TicketsCategoriesArea=Espace tags/catégories des tickets SubCats=Sous-catégories CatList=Liste des tags/catégories CatListAll=Liste de toutes les catégories (de tous types) @@ -90,6 +91,7 @@ CategorieRecursivHelp=Si l'option est activé, quand un produit est ajouté dans AddProductServiceIntoCategory=Ajouter le produit/service suivant AddCustomerIntoCategory=Assigner cette catégorie au client AddSupplierIntoCategory=Assigner cette catégorie au fournisseur +AddTicketIntoCategory=Assigner cette catégorie au ticket ShowCategory=Afficher tag/catégorie ByDefaultInList=Par défaut dans la liste ChooseCategory=Choisissez une catégorie diff --git a/htdocs/langs/fr_FR/main.lang b/htdocs/langs/fr_FR/main.lang index bf49ccccf54..2453fab1e16 100644 --- a/htdocs/langs/fr_FR/main.lang +++ b/htdocs/langs/fr_FR/main.lang @@ -1137,3 +1137,13 @@ CopiedToClipboard=Copié dans le presse-papier InformationOnLinkToContract=Ce montant n’est que le total de toutes les lignes du contrat. Aucune notion de temps n’est prise en considération. ConfirmCancel=Êtes-vous sûr de vous annuler EmailMsgID=E-mail MsgID +SetToEnabled=Activer +SetToDisabled=Désactiver +ConfirmMassEnabling=Confirmation d'activation en masse +ConfirmMassEnablingQuestion=Êtes-vous sur de vouloir activer les %s enregistrement(s) sélectionné(s) ? +ConfirmMassDisabling=Confirmation de desactivation en masse +ConfirmMassDisablingQuestion=Êtes-vous sur de vouloir desactiver les %s enregistrement(s) sélectionné(s) ? +RecordsEnabled=%s enregistrement(s) activé(s) +RecordsDisabled=%s enregistrement(s) désactivé(s) +RecordEnabled=Enregistrement activé +RecordDisabled=Enregistrement désactivé diff --git a/htdocs/product/card.php b/htdocs/product/card.php index 01a2d9acfdd..2472c764d7e 100644 --- a/htdocs/product/card.php +++ b/htdocs/product/card.php @@ -1234,7 +1234,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) { if ($type == 1) { print ''.$langs->trans("Duration").''; print ''; - print $formproduct->selectMeasuringUnits("duration_unit", "time", GETPOST('duration_value', 'alpha'), 0, 1); + print $formproduct->selectMeasuringUnits("duration_unit", "time", (GETPOSTISSET('duration_value') ? GETPOSTISSET('duration_value', 'alpha') : 'h'), 0, 1); print ''; } diff --git a/htdocs/product/class/html.formproduct.class.php b/htdocs/product/class/html.formproduct.class.php index 5be93a9b9d1..555bc08d03e 100644 --- a/htdocs/product/class/html.formproduct.class.php +++ b/htdocs/product/class/html.formproduct.class.php @@ -414,7 +414,7 @@ class FormProduct dol_print_error($db); return -1; } else { - $return .= ''; if ($adddefault || $adddefault === '') { $return .= ''; } @@ -447,6 +447,8 @@ class FormProduct $return .= ''; } + $return .= ajax_combobox($name); + return $return; } diff --git a/htdocs/projet/class/task.class.php b/htdocs/projet/class/task.class.php index b7e1994e86b..5b92b9cccdf 100644 --- a/htdocs/projet/class/task.class.php +++ b/htdocs/projet/class/task.class.php @@ -116,6 +116,7 @@ class Task extends CommonObject public $timespent_datehour; // More accurate start date (same than timespent_date but includes hours, minutes and seconds) public $timespent_withhour; // 1 = we entered also start hours for timesheet line public $timespent_fk_user; + public $timespent_thm; public $timespent_note; public $comments = array(); @@ -1224,6 +1225,7 @@ class Task extends CommonObject $ret = -2; } + // Update hourly rate of this time spent entry $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time"; $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user $sql .= " WHERE rowid = ".((int) $tasktime_id); @@ -1437,6 +1439,7 @@ class Task extends CommonObject $sql .= " ptt.task_duration,"; $sql .= " ptt.fk_user,"; $sql .= " ptt.note,"; + $sql .= " ptt.thm,"; $sql .= " pt.rowid as task_id,"; $sql .= " pt.ref as task_ref,"; $sql .= " pt.label as task_label,"; @@ -1447,7 +1450,7 @@ class Task extends CommonObject $sql .= " FROM ".MAIN_DB_PREFIX."projet_task_time as ptt, ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet as p"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid"; $sql .= " WHERE ptt.fk_task = pt.rowid AND pt.fk_projet = p.rowid"; - $sql .= " AND ptt.fk_user = ".$userobj->id; + $sql .= " AND ptt.fk_user = ".((int) $userobj->id); $sql .= " AND pt.entity IN (".getEntity('project').")"; if ($morewherefilter) { $sql .= $morewherefilter; @@ -1483,6 +1486,7 @@ class Task extends CommonObject $newobj->timespent_withhour = $obj->task_date_withhour; $newobj->timespent_duration = $obj->task_duration; $newobj->timespent_fk_user = $obj->fk_user; + $newobj->timespent_thm = $obj->thm; // hourly rate $newobj->timespent_note = $obj->note; $arrayres[] = $newobj; @@ -1575,10 +1579,12 @@ class Task extends CommonObject } if ($ret == 1 && ($this->timespent_old_duration != $this->timespent_duration)) { - $newDuration = $this->timespent_duration - $this->timespent_old_duration; - + // Recalculate amount of time spent for task and update denormalized field $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task"; $sql .= " SET duration_effective = (SELECT SUM(task_duration) FROM ".MAIN_DB_PREFIX."projet_task_time as ptt where ptt.fk_task = ".((int) $this->id).")"; + if (isset($this->progress)) { + $sql .= ", progress = ".((float) $this->progress); // Do not overwrite value if not provided + } $sql .= " WHERE rowid = ".((int) $this->id); dol_syslog(get_class($this)."::updateTimeSpent", LOG_DEBUG); @@ -1587,6 +1593,17 @@ class Task extends CommonObject $this->db->rollback(); $ret = -2; } + + // Update hourly rate of this time spent entry, but only if it was not set initialy + $sql = "UPDATE ".MAIN_DB_PREFIX."projet_task_time"; + $sql .= " SET thm = (SELECT thm FROM ".MAIN_DB_PREFIX."user WHERE rowid = ".((int) $this->timespent_fk_user).")"; // set average hour rate of user + $sql .= " WHERE (thm IS NULL OR thm = 0) AND rowid = ".((int) $this->timespent_id); + + dol_syslog(get_class($this)."::addTimeSpent", LOG_DEBUG); + if (!$this->db->query($sql)) { + $this->error = $this->db->lasterror(); + $ret = -2; + } } if ($ret >= 0) { diff --git a/htdocs/projet/tasks/time.php b/htdocs/projet/tasks/time.php index 2cfb39bf437..0e7377f8751 100644 --- a/htdocs/projet/tasks/time.php +++ b/htdocs/projet/tasks/time.php @@ -262,10 +262,10 @@ if (($action == 'updateline' || $action == 'updatesplitline') && !$cancel && $us $object->timespent_duration = GETPOSTINT("new_durationhour") * 60 * 60; // We store duration in seconds $object->timespent_duration += (GETPOSTINT("new_durationmin") ? GETPOSTINT('new_durationmin') : 0) * 60; // We store duration in seconds if (GETPOST("timelinehour") != '' && GETPOST("timelinehour") >= 0) { // If hour was entered - $object->timespent_date = dol_mktime(GETPOST("timelinehour"), GETPOST("timelinemin"), 0, GETPOST("timelinemonth"), GETPOST("timelineday"), GETPOST("timelineyear")); + $object->timespent_date = dol_mktime(GETPOST("timelinehour", 'int'), GETPOST("timelinemin", 'int'), 0, GETPOST("timelinemonth", 'int'), GETPOST("timelineday", 'int'), GETPOST("timelineyear", 'int')); $object->timespent_withhour = 1; } else { - $object->timespent_date = dol_mktime(12, 0, 0, GETPOST("timelinemonth"), GETPOST("timelineday"), GETPOST("timelineyear")); + $object->timespent_date = dol_mktime(12, 0, 0, GETPOST("timelinemonth", 'int'), GETPOST("timelineday", 'int'), GETPOST("timelineyear", 'int')); } $object->timespent_fk_user = GETPOST("userid_line", 'int'); @@ -354,6 +354,13 @@ if ($action == 'confirm_generateinvoice') { $prodDurationHours = 1.0; if ($idprod > 0) { $tmpproduct->fetch($idprod); + + if (empty($tmpproduct->duration_value)) { + $error++; + $langs->load("errors"); + setEventMessages($langs->trans("ErrorDurationForServiceNotDefinedCantCalculateHourlyPrice"), null, 'errors'); + } + if ($tmpproduct->duration_unit == 'i') { $prodDurationHours = 1. / 60; } @@ -381,6 +388,8 @@ if ($action == 'confirm_generateinvoice') { $localtax1 = $dataforprice['localtax1']; $localtax2 = $dataforprice['localtax2']; } else { + $prodDurationHours = 1; + $pu_ht = 0; $txtva = get_default_tva($mysoc, $projectstatic->thirdparty); $localtax1 = get_default_localtax($mysoc, $projectstatic->thirdparty, 1); @@ -483,14 +492,14 @@ if ($action == 'confirm_generateinvoice') { break; } } - } elseif ($generateinvoicemode == 'onelinepertask') { + } elseif ($generateinvoicemode == 'onelinepertask') { // One line for each different task $arrayoftasks = array(); foreach ($toselect as $key => $value) { // Get userid, timepent - $object->fetchTimeSpent($value); - // $object->id is the task id + $object->fetchTimeSpent($value); // Call method to get list of timespent for a timespent line id (We use the utiliy method found into Task object) + // $object->id is now the task id $arrayoftasks[$object->id]['timespent'] += $object->timespent_duration; - $arrayoftasks[$object->id]['totalvaluetodivideby3600'] += $object->timespent_duration * $object->timespent_thm; + $arrayoftasks[$object->id]['totalvaluetodivideby3600'] += ($object->timespent_duration * $object->timespent_thm); } foreach ($arrayoftasks as $task_id => $value) { @@ -500,24 +509,46 @@ if ($action == 'confirm_generateinvoice') { $qtyhour = $value['timespent'] / 3600; $qtyhourtext = convertSecondToTime($value['timespent'], 'all', $conf->global->MAIN_DURATION_OF_WORKDAY); - // If no unit price known - if (empty($pu_ht)) { - $pu_ht = price2num($value['totalvaluetodivideby3600'] / 3600, 'MU'); + if ($idprod > 0) { + // If a product is defined, we msut use the $prodDurationHours and $pu_ht of product (already set previously). + $pu_ht_for_task = $pu_ht; + // If we want to reuse the value of timespent (so use same price than cost price) + if (!empty($conf->global->PROJECT_TIME_SPENT_INTO_INVOICE_USE_VALUE)) { + $pu_ht_for_task = price2num($value['totalvaluetodivideby3600'] / $value['timespent'], 'MU') * $prodDurationHours; + } + $pa_ht = price2num($value['totalvaluetodivideby3600'] / $value['timespent'], 'MU') * $prodDurationHours; + } else { + // If not product used, we use the hour unit for duration and unit price. + $pu_ht_for_task = 0; + // If we want to reuse the value of timespent (so use same price than cost price) + if (!empty($conf->global->PROJECT_TIME_SPENT_INTO_INVOICE_USE_VALUE)) { + $pu_ht_for_task = price2num($value['totalvaluetodivideby3600'] / $value['timespent'], 'MU'); + } + $pa_ht = price2num($value['totalvaluetodivideby3600'] / $value['timespent'], 'MU'); } // Add lines + $date_start = ''; + $date_end = ''; $lineName = $ftask->ref.' - '.$ftask->label; - $lineid = $tmpinvoice->addline($lineName, $pu_ht, round($qtyhour / $prodDurationHours, 2), $txtva, $localtax1, $localtax2, ($idprod > 0 ? $idprod : 0)); - - // Update lineid into line of timespent - $sql = 'UPDATE '.MAIN_DB_PREFIX.'projet_task_time SET invoice_line_id = '.((int) $lineid).', invoice_id = '.((int) $tmpinvoice->id); - $sql .= ' WHERE rowid IN ('.$db->sanitize(join(',', $toselect)).')'; - $result = $db->query($sql); - if (!$result) { + $lineid = $tmpinvoice->addline($lineName, $pu_ht_for_task, price2num($qtyhour / $prodDurationHours, 'MS'), $txtva, $localtax1, $localtax2, ($idprod > 0 ? $idprod : 0), 0, $date_start, $date_end, 0, 0, '', 'HT', 0, 1, -1, 0, '', 0, 0, null, $pa_ht); + if ($lineid < 0) { $error++; - setEventMessages($db->lasterror(), null, 'errors'); + setEventMessages($tmpinvoice->error, $tmpinvoice->errors, 'errors'); break; } + + if (!$error) { + // Update lineid into line of timespent + $sql = 'UPDATE '.MAIN_DB_PREFIX.'projet_task_time SET invoice_line_id = '.((int) $lineid).', invoice_id = '.((int) $tmpinvoice->id); + $sql .= ' WHERE rowid IN ('.$db->sanitize(join(',', $toselect)).')'; + $result = $db->query($sql); + if (!$result) { + $error++; + setEventMessages($db->lasterror(), null, 'errors'); + break; + } + } } } } @@ -1042,7 +1073,7 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { print ''; if ($projectstatic->thirdparty->id > 0) { - print ''; + print '
'; print ''; print ''; - $date1 = $db->jdate($task_time->task_date); $date2 = $db->jdate($task_time->task_datehour); + print ''; + // Date if (!empty($arrayfields['t.task_date']['checked'])) { print ''; if (!$i) { $totalarray['nbfield']++; diff --git a/htdocs/public/payment/paymentok.php b/htdocs/public/payment/paymentok.php index 2f566d4bb31..ab8e925d4f5 100644 --- a/htdocs/public/payment/paymentok.php +++ b/htdocs/public/payment/paymentok.php @@ -813,6 +813,111 @@ if ($ispaymentok) { $postactionmessages[] = 'Invoice paid '.$tmptag['INV'].' was not found'; $ispostactionok = -1; } + } elseif (array_key_exists('ORD', $tmptag) && $tmptag['ORD'] > 0) { + include_once DOL_DOCUMENT_ROOT . '/commande/class/commande.class.php'; + $object = new Commande($db); + $result = $object->fetch($tmptag['ORD']); + if ($result) { + $FinalPaymentAmt = $_SESSION["FinalPaymentAmt"]; + + $paymentTypeId = 0; + if ($paymentmethod == 'paybox') $paymentTypeId = $conf->global->PAYBOX_PAYMENT_MODE_FOR_PAYMENTS; + if ($paymentmethod == 'paypal') $paymentTypeId = $conf->global->PAYPAL_PAYMENT_MODE_FOR_PAYMENTS; + if ($paymentmethod == 'stripe') $paymentTypeId = $conf->global->STRIPE_PAYMENT_MODE_FOR_PAYMENTS; + if (empty($paymentTypeId)) { + $paymentType = $_SESSION["paymentType"]; + if (empty($paymentType)) $paymentType = 'CB'; + $paymentTypeId = dol_getIdFromCode($db, $paymentType, 'c_paiement', 'code', 'id', 1); + } + + $currencyCodeType = $_SESSION['currencyCodeType']; + + // Do action only if $FinalPaymentAmt is set (session variable is cleaned after this page to avoid duplicate actions when page is POST a second time) + if (!empty($conf->facture->enabled)) { + if (!empty($FinalPaymentAmt) && $paymentTypeId > 0 ) { + include_once DOL_DOCUMENT_ROOT . '/compta/facture/class/facture.class.php'; + $invoice = new Facture($db); + $result = $invoice->createFromOrder($object, $user); + if ($result > 0) { + $object->classifyBilled($user); + $invoice->validate($user); + // Creation of payment line + include_once DOL_DOCUMENT_ROOT . '/compta/paiement/class/paiement.class.php'; + $paiement = new Paiement($db); + $paiement->datepaye = $now; + if ($currencyCodeType == $conf->currency) { + $paiement->amounts = array($invoice->id => $FinalPaymentAmt); // Array with all payments dispatching with invoice id + } else { + $paiement->multicurrency_amounts = array($invoice->id => $FinalPaymentAmt); // Array with all payments dispatching + + $postactionmessages[] = 'Payment was done in a different currency that currency expected of company'; + $ispostactionok = -1; + $error++; + } + $paiement->paiementid = $paymentTypeId; + $paiement->num_payment = ''; + $paiement->note_public = 'Online payment ' . dol_print_date($now, 'standard') . ' from ' . $ipaddress; + $paiement->ext_payment_id = $TRANSACTIONID; + $paiement->ext_payment_site = ''; + + if (!$error) { + $paiement_id = $paiement->create($user, 1); // This include closing invoices and regenerating documents + if ($paiement_id < 0) { + $postactionmessages[] = $paiement->error . ' ' . join("
\n", $paiement->errors); + $ispostactionok = -1; + $error++; + } else { + $postactionmessages[] = 'Payment created'; + $ispostactionok = 1; + } + } + + if (!$error && !empty($conf->banque->enabled)) { + $bankaccountid = 0; + if ($paymentmethod == 'paybox') $bankaccountid = $conf->global->PAYBOX_BANK_ACCOUNT_FOR_PAYMENTS; + elseif ($paymentmethod == 'paypal') $bankaccountid = $conf->global->PAYPAL_BANK_ACCOUNT_FOR_PAYMENTS; + elseif ($paymentmethod == 'stripe') $bankaccountid = $conf->global->STRIPE_BANK_ACCOUNT_FOR_PAYMENTS; + + if ($bankaccountid > 0) { + $label = '(CustomerInvoicePayment)'; + if ($object->type == Facture::TYPE_CREDIT_NOTE) $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note + $result = $paiement->addPaymentToBank($user, 'payment', $label, $bankaccountid, '', ''); + if ($result < 0) { + $postactionmessages[] = $paiement->error . ' ' . join("
\n", $paiement->errors); + $ispostactionok = -1; + $error++; + } else { + $postactionmessages[] = 'Bank transaction of payment created'; + $ispostactionok = 1; + } + } else { + $postactionmessages[] = 'Setup of bank account to use in module ' . $paymentmethod . ' was not set. No way to record the payment.'; + $ispostactionok = -1; + $error++; + } + } + + if (!$error) { + $db->commit(); + } else { + $db->rollback(); + } + } else { + $postactionmessages[] = 'Failed to create invoice form order ' . $tmptag['ORD'] . '.'; + $ispostactionok = -1; + } + } else { + $postactionmessages[] = 'Failed to get a valid value for "amount paid" (' . $FinalPaymentAmt . ') or "payment type" (' . $paymentType . ') to record the payment of order ' . $tmptag['ORD'] . '. May be payment was already recorded.'; + $ispostactionok = -1; + } + } else { + $postactionmessages[] = 'Invoice module is not enable'; + $ispostactionok = -1; + } + } else { + $postactionmessages[] = 'Order paid ' . $tmptag['ORD'] . ' was not found'; + $ispostactionok = -1; + } } elseif (array_key_exists('DON', $tmptag) && $tmptag['DON'] > 0) { include_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php'; $don = new Don($db); diff --git a/htdocs/public/test/badges.php b/htdocs/public/test/badges.php index fd0add00af1..2190b96db7e 100644 --- a/htdocs/public/test/badges.php +++ b/htdocs/public/test/badges.php @@ -20,7 +20,9 @@ if (!defined('NOREQUIREAJAX')) { if (!defined('NOSESSION')) { define('NOSESSION', '1'); } - +if (!defined('NOREQUIREMENU')) { + define('NOREQUIREMENU', '1'); +} session_cache_limiter('public'); require_once '../../main.inc.php'; @@ -30,19 +32,9 @@ if ($dolibarr_main_prod) { accessforbidden(); } + +llxHeader('', 'Documentation and examples for theme'); ?> - - - - - - - - - - - -

Badges

Documentation and examples for badges, our small count and labeling component.

@@ -342,5 +334,4 @@ if ($dolibarr_main_prod) {
- - \ No newline at end of file + +
+

Button for action

+

Documentation and examples for buttons.

+ +

Example of simple usage

+ +

Buttons for user allowed to click.

+ +
+ My default action'; + $actionType = 'default'; + $n++; + $id = 'mybuttonid'.$n; + $url = '#'.$id; + $userRight = 1; + $params = array(); + + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight); + + + $html = ' My delete action'; + $actionType = 'delete'; + $n++; + $id = 'mybuttonid'.$n; + $url = $_SERVER['PHP_SELF'] . '?token='.newToken().'#'.$id; + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight); + + + $html = ' My danger action'; + $actionType = 'danger'; + $n++; + $id = 'mybuttonid'.$n; + $url = $_SERVER['PHP_SELF'] . '?token='.newToken().'#'.$id; + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight); + + ?> +
+ +

Buttons for user NOT allowed to click.

+ +
+ My default action'; + $actionType = 'default'; + $n++; + $id = 'mybuttonid'.$n; + $url = '#'.$id; + $userRight = 0; + + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight); + + + $html = ' My delete action'; + $actionType = 'delete'; + $n++; + $id = 'mybuttonid'.$n; + $url = $_SERVER['PHP_SELF'] . '?token='.newToken().'#'.$id; + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight); + + + $html = ' My danger action'; + $actionType = 'danger'; + $n++; + $id = 'mybuttonid'.$n; + $url = $_SERVER['PHP_SELF'] . '?token='.newToken().'#'.$id; + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight); + + ?> +
+ + +

Example of confirm dialog

+ +

Buttons for user allowed to click.

+ +
+ My default action'; + $actionType = 'default'; + $n++; + $id = 'mybuttonid'.$n; + $url = '#'.$id; + $userRight = 1; + $params = array( + 'confirm' => true + ); + + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight, $params); + + + $html = ' My delete action'; + $actionType = 'delete'; + $n++; + $id = 'mybuttonid'.$n; + $url = $_SERVER['PHP_SELF'] . '?token='.newToken().'#'.$id; + + $params = array( + 'confirm' => array( + 'url' => 'your confirm action url', + 'title' => 'Your title to display', + 'action-btn-label' => 'Your confirm label', + 'cancel-btn-label' => 'Your cancel label', + 'content' => 'Content to display with HTML compatible
  • test 01
  • test 02
  • test 03
' + ) + ); + + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight, $params); + + ?> +
+ +

Buttons for user NOT allowed to click.

+ +
+ My default action'; + $actionType = 'default'; + $n++; + $id = 'mybuttonid'.$n; + $url = '#'.$id; + $userRight = 0; + $params = array( + 'confirm' => true + ); + + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight, $params); + + + $html = ' My delete action'; + $actionType = 'delete'; + $n++; + $id = 'mybuttonid'.$n; + $url = $_SERVER['PHP_SELF'] . '?token='.newToken().'#'.$id; + + $params = array( + 'confirm' => array( + 'url' => 'your confirm action url', + 'title' => 'Your title to display', + 'action-btn-label' => 'Your confirm label', + 'cancel-btn-label' => 'Your cancel label', + 'content' => 'Content to display with HTML compatible
  • test 01
  • test 02
  • test 03
' + ) + ); + + print dolGetButtonAction($label, $html, $actionType, $url, $id, $userRight, $params); + + ?> +
+ + +
+ + - * Copyright (C) 2004-2019 Laurent Destailleur + * Copyright (C) 2004-2021 Laurent Destailleur * Copyright (C) 2004 Eric Seigne * Copyright (C) 2003 Brian Fraval * Copyright (C) 2006 Andre Cianfarani @@ -1280,7 +1280,8 @@ class Societe extends CommonObject $this->get_codefournisseur($this, 1); } - $this->code_compta = trim($this->code_compta); + $this->code_compta_client = trim(empty($this->code_compta) ? $this->code_compta_client : $this->code_compta); + $this->code_compta = $this->code_compta_client; // for backward compatbility $this->code_compta_fournisseur = trim($this->code_compta_fournisseur); // Check parameters. More tests are done later in the ->verify() @@ -1292,8 +1293,8 @@ class Societe extends CommonObject $customer = false; if (!empty($allowmodcodeclient) && !empty($this->client)) { - // Attention get_codecompta peut modifier le code suivant le module utilise - if (empty($this->code_compta)) { + // If $allowmodcodeclient is set and value is not set, we generate it + if (empty($this->code_compta_client)) { $ret = $this->get_codecompta('customer'); if ($ret < 0) { return -1; @@ -1305,8 +1306,8 @@ class Societe extends CommonObject $supplier = false; if (!empty($allowmodcodefournisseur) && !empty($this->fournisseur)) { - // Attention get_codecompta peut modifier le code suivant le module utilise - if ($this->code_compta_fournisseur == "") { + // If $allowmodcodefournisseur is set and value is not set, we generate it + if (empty($this->code_compta_fournisseur)) { $ret = $this->get_codecompta('supplier'); if ($ret < 0) { return -1; @@ -1460,7 +1461,7 @@ class Societe extends CommonObject if ($customer) { $sql .= ", code_client = ".(!empty($this->code_client) ? "'".$this->db->escape($this->code_client)."'" : "null"); - $sql .= ", code_compta = ".(!empty($this->code_compta) ? "'".$this->db->escape($this->code_compta)."'" : "null"); + $sql .= ", code_compta = ".(!empty($this->code_compta_client) ? "'".$this->db->escape($this->code_compta_client)."'" : "null"); } if ($supplier) { @@ -3311,7 +3312,8 @@ class Societe extends CommonObject $result = $mod->get_code($this->db, $this, $type); if ($type == 'customer') { - $this->code_compta = $mod->code; + $this->code_compta_client = $mod->code; + $this->code_compta = $this->code_compta_client; // For backward compatibility } elseif ($type == 'supplier') { $this->code_compta_fournisseur = $mod->code; } @@ -3323,6 +3325,7 @@ class Societe extends CommonObject } } else { if ($type == 'customer') { + $this->code_compta_client = ''; $this->code_compta = ''; } elseif ($type == 'supplier') { $this->code_compta_fournisseur = ''; @@ -4262,21 +4265,6 @@ class Societe extends CommonObject return $lib; } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Set prospect level - * - * @param User $user User who sets the discount - * @return int <0 if KO, >0 if OK - * @deprecated Use update function instead - */ - public function set_prospect_level(User $user) - { - // phpcs:enable - return $this->update($this->id, $user); - } - /** * Return status of prospect * @@ -4356,20 +4344,6 @@ class Societe extends CommonObject return "Error, mode/status not found"; } - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Set outstanding value - * - * @param User $user User making change - * @return int <0 if KO, >0 if OK - * @deprecated Use update function instead - */ - public function set_OutstandingBill(User $user) - { - // phpcs:enable - return $this->update($this->id, $user); - } - /** * Return amount of order not paid and total * diff --git a/htdocs/societe/list.php b/htdocs/societe/list.php index 5935ac43b70..6835794e916 100644 --- a/htdocs/societe/list.php +++ b/htdocs/societe/list.php @@ -865,7 +865,13 @@ if ($user->rights->societe->supprimer) { if ($user->rights->societe->creer) { $arrayofmassactions['preaffecttag'] = img_picto('', 'category', 'class="pictofixedwidth"').$langs->trans("AffectTag"); } -if (GETPOST('nomassaction', 'int') || in_array($massaction, array('presend', 'predelete', 'preaffecttag'))) { +if ($user->rights->societe->creer) { + $arrayofmassactions['preenable'] = img_picto('', '', 'class="pictofixedwidth"').$langs->trans("SetToEnabled"); +} +if ($user->rights->societe->creer) { + $arrayofmassactions['predisable'] = img_picto('', '', 'class="pictofixedwidth"').$langs->trans("SetToDisabled"); +} +if (GETPOST('nomassaction', 'int') || in_array($massaction, array('presend', 'predelete', 'preaffecttag', 'preenable', 'preclose'))) { $arrayofmassactions = array(); } $massactionbutton = $form->selectMassAction('', $arrayofmassactions); diff --git a/htdocs/societe/project.php b/htdocs/societe/project.php index 038ed7350d5..c6256324257 100644 --- a/htdocs/societe/project.php +++ b/htdocs/societe/project.php @@ -136,7 +136,7 @@ if ($socid) { $params = ''; $backtopage = $_SERVER['PHP_SELF'].'?socid='.$object->id; - $newcardbutton = dolGetButtonTitle($langs->trans("NewProject"), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/projet/card.php?action=create&socid='.$object->id.'&backtopage='.urlencode($backtopage), '', 1, $params); + $newcardbutton = dolGetButtonTitle($langs->trans("NewProject"), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/projet/card.php?action=create&socid='.$object->id.'&backtopageforcancel='.urlencode($backtopage), '', 1, $params); print '
'; diff --git a/htdocs/takepos/invoice.php b/htdocs/takepos/invoice.php index 8cef291c2d7..721a7b92ae8 100644 --- a/htdocs/takepos/invoice.php +++ b/htdocs/takepos/invoice.php @@ -1095,7 +1095,7 @@ $( document ).ready(function() { $max_sale = 0; while ($obj = $db->fetch_object($resql)) { echo '$("#customerandsales").append(\''; - echo 'jdate($obj->datec), '%H:%M', 'tzuser'))).'" onclick="place=\\\''; + echo 'jdate($obj->datec), '%H:%M', 'tzuser')).' - '.$obj->ref).'" onclick="place=\\\''; $num_sale = str_replace(")", "", str_replace("(PROV-POS".$_SESSION["takeposterminal"]."-", "", $obj->ref)); echo $num_sale; if (str_replace("-", "", $num_sale) > $max_sale) { diff --git a/htdocs/theme/eldy/badges.inc.php b/htdocs/theme/eldy/badges.inc.php index dcb61499b63..58317deba15 100644 --- a/htdocs/theme/eldy/badges.inc.php +++ b/htdocs/theme/eldy/badges.inc.php @@ -258,9 +258,9 @@ function _createStatusBadgeCss($statusName, $statusVarNamePrefix = '', $commentL print $cssPrefix.".badge-status".$statusName." {\n"; print " color: ".$thisBadgeTextColor." !important;\n"; if (in_array((string) $statusName, $TBadgeBorderOnly)) { - print " border-color: ".$thisBadgeBorderColor.";\n"; + print " border-color: ".$thisBadgeBorderColor." !important;\n"; } - print " background-color: ".$thisBadgeBackgroundColor.";\n"; + print " background-color: ".$thisBadgeBackgroundColor." !important;\n"; print "}\n"; print $cssPrefix.".font-status".$statusName." {\n"; @@ -269,14 +269,14 @@ function _createStatusBadgeCss($statusName, $statusVarNamePrefix = '', $commentL print $cssPrefix.".badge-status".$statusName.".focus, ".$cssPrefix.".badge-status".$statusName.":focus {\n"; print " outline: 0;\n"; - print " box-shadow: 0 0 0 0.2rem ".colorHexToRgb($thisBadgeBackgroundColor, 0.5).";\n"; + print " box-shadow: 0 0 0 0.2rem ".colorHexToRgb($thisBadgeBackgroundColor, 0.5)." !important;\n"; print "}\n"; print $cssPrefix.".badge-status".$statusName.":focus, ".$cssPrefix.".badge-status".$statusName.":hover {\n"; print " color: ".$thisBadgeTextColor." !important;\n"; //print " background-color: " . colorDarker($thisBadgeBackgroundColor, 10) . ";\n"; if (in_array((string) $statusName, $TBadgeBorderOnly)) { - print " border-color: ".colorDarker($thisBadgeBorderColor, 10).";\n"; + print " border-color: ".colorDarker($thisBadgeBorderColor, 10)." !important;\n"; } print "}\n"; } diff --git a/htdocs/theme/md/badges.inc.php b/htdocs/theme/md/badges.inc.php index fd1b1122a17..4a36177e852 100644 --- a/htdocs/theme/md/badges.inc.php +++ b/htdocs/theme/md/badges.inc.php @@ -261,9 +261,9 @@ function _createStatusBadgeCss($statusName, $statusVarNamePrefix = '', $commentL print $cssPrefix.".badge-status".$statusName." {\n"; print " color: ".$thisBadgeTextColor." !important;\n"; if (in_array((string) $statusName, $TBadgeBorderOnly)) { - print " border-color: ".$thisBadgeBorderColor.";\n"; + print " border-color: ".$thisBadgeBorderColor." !important;\n"; } - print " background-color: ".$thisBadgeBackgroundColor.";\n"; + print " background-color: ".$thisBadgeBackgroundColor." !important;\n"; print "}\n"; print $cssPrefix.".font-status".$statusName." {\n"; @@ -272,14 +272,14 @@ function _createStatusBadgeCss($statusName, $statusVarNamePrefix = '', $commentL print $cssPrefix.".badge-status".$statusName.".focus, ".$cssPrefix.".badge-status".$statusName.":focus {\n"; print " outline: 0;\n"; - print " box-shadow: 0 0 0 0.2rem ".colorHexToRgb($thisBadgeBackgroundColor, 0.5).";\n"; + print " box-shadow: 0 0 0 0.2rem ".colorHexToRgb($thisBadgeBackgroundColor, 0.5)." !important;\n"; print "}\n"; print $cssPrefix.".badge-status".$statusName.":focus, ".$cssPrefix.".badge-status".$statusName.":hover {\n"; print " color: ".$thisBadgeTextColor." !important;\n"; //print " background-color: ".colorDarker($thisBadgeBackgroundColor, 10).";\n"; if (in_array((string) $statusName, $TBadgeBorderOnly)) { - print " border-color: ".colorDarker($thisBadgeBorderColor, 10).";\n"; + print " border-color: ".colorDarker($thisBadgeBorderColor, 10)." !important;\n"; } print "}\n"; } diff --git a/htdocs/ticket/card.php b/htdocs/ticket/card.php index 5faa7b201b7..be53c46b051 100644 --- a/htdocs/ticket/card.php +++ b/htdocs/ticket/card.php @@ -31,6 +31,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; +require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; if (!empty($conf->projet->enabled)) { include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; include_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php'; @@ -202,6 +203,10 @@ if (empty($reshook)) { $contactid = GETPOST('contactid', 'int'); $type_contact = GETPOST("type", 'alpha'); + // Category association + $categories = GETPOST('categories', 'array'); + $object->setCategories($categories); + if ($contactid > 0 && $type_contact) { $typeid = (GETPOST('typecontact') ? GETPOST('typecontact') : GETPOST('type')); $result = $object->add_contact($contactid, $typeid, 'external'); @@ -312,7 +317,11 @@ if (empty($reshook)) { $object->severity_code = GETPOST('severity_code', 'alpha'); $ret = $object->update($user); - if ($ret <= 0) { + if ($ret > 0) { + // Category association + $categories = GETPOST('categories', 'array'); + $object->setCategories($categories); + } else { $error++; } @@ -1054,6 +1063,13 @@ if ($action == 'create' || $action == 'presend') { print '
'; } + // Categories + if ($conf->categorie->enabled) { + print '"; + } + // Other attributes include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php'; diff --git a/htdocs/ticket/class/ticket.class.php b/htdocs/ticket/class/ticket.class.php index b3514f5fe0d..5bd8ec05864 100644 --- a/htdocs/ticket/class/ticket.class.php +++ b/htdocs/ticket/class/ticket.class.php @@ -2499,6 +2499,51 @@ class Ticket extends CommonObject return array('listofpaths'=>$listofpaths, 'listofnames'=>$listofnames, 'listofmimes'=>$mimetype); } + /** + * Sets object to supplied categories. + * + * Deletes object from existing categories not supplied. + * Adds it to non existing supplied categories. + * Existing categories are left untouch. + * + * @param int[]|int $categories Category or categories IDs + * @return void + */ + public function setCategories($categories) + { + // Handle single category + if (!is_array($categories)) { + $categories = array($categories); + } + + // Get current categories + include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + $c = new Categorie($this->db); + $existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id'); + + // Diff + if (is_array($existing)) { + $to_del = array_diff($existing, $categories); + $to_add = array_diff($categories, $existing); + } else { + $to_del = array(); // Nothing to delete + $to_add = $categories; + } + + // Process + foreach ($to_del as $del) { + if ($c->fetch($del) > 0) { + $c->del_type($this, Categorie::TYPE_TICKET); + } + } + foreach ($to_add as $add) { + if ($c->fetch($add) > 0) { + $c->add_type($this, Categorie::TYPE_TICKET); + } + } + + return; + } /** * Add new message on a ticket (private/public area). Can also send it be email if GETPOST('send_email', 'int') is set. diff --git a/test/phpunit/HolidayTest.php b/test/phpunit/HolidayTest.php index 418b81f8b41..4fbd57c212b 100644 --- a/test/phpunit/HolidayTest.php +++ b/test/phpunit/HolidayTest.php @@ -366,6 +366,6 @@ class HolidayTest extends PHPUnit\Framework\TestCase $localobjecta->updateConfCP('lastUpdate', '20100101120000'); $result = $localobjecta->updateBalance(); - $this->assertEquals($result, 1); + $this->assertEquals($result, 0); } } diff --git a/test/phpunit/imap_header.txt b/test/phpunit/imap_header.txt new file mode 100644 index 00000000000..5891c66a3da --- /dev/null +++ b/test/phpunit/imap_header.txt @@ -0,0 +1,75 @@ +Delivered-To: testldrdev@gmail.com +Received: by 2002:ab3:5782:0:0:0:0:0 with SMTP id e2csp698208ltc; Thu, 15 Jul + 2021 13:49:41 -0700 (PDT) +X-Received: by 2002:a63:eb51:: with SMTP id b17mr490127pgk.288.1626382180943; + Thu, 15 Jul 2021 13:49:40 -0700 (PDT) +ARC-Seal: i=1; a=rsa-sha256; t=1626382180; cv=none; d=google.com; + s=arc-20160816; + b=rEpZkUN9MvFJ+8ckMD+urx6epAp5zUAICUiJjTY9yAAwmp54gZ9XYu/qoVgqVe5rAU + oayiqojPUzHArxaIlmftWyK4kF6ErAQxMZocn944kHeCGOigIB2y+u+ij3Ip4Vg6PW62 + 4cATlnRoDTpnckwrXHnOonzSEHunE2smZgtujdNJ/1p/vbmhByQl3Kg71NzCLzdE3nr6 + PYQNl/XbTL4BUpKBsbPCbJDNeNOqu/FHBI3R7Nfoy+HDY9LR+aYBR4N6jBQMKyKM32JJ + 2yrN9ky6N1n2xfvD4lnbqDyqvgWjmTGP+Dk+UAUeAj3YsS/hM2z41UA9xicPoC/B+Wft XWPQ== +ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; + s=arc-20160816; h=to:from:subject:message-id:feedback-id:date:mime-version + :dkim-signature; bh=jzUFV8bCmEmAeTpb8amH8O1QwkNos0xYlYqLtpP6SBc=; + b=cLnW/O5/vTbct4uiVIanDpvITsoy5pp8c0Cy3ZKppY1t2U0bCaedhyI9LkbwXU0ytU + i+qgnSrwydhnvY8Yy13KDUkVtjU9dxmQ/YDYBsLjS7WeFEy9OQkIqCR1czmUoUSEPbgr + d/tsVEPg/LwHW74WuG6vUVRidF9XBBQqQHR7ADCvo4DQKRURzpeDF4ggDceiWfhpYjbZ + Un//GKujM5WGfV7kkqfprw5UsVG8+pjbKdnTwsCKJ3htju9eYXANf4Odn/kjA5OjP3l3 + fuqCeus0fSyUw9T4DLe4knRxReOL+MfjQc65ATjFlOfl/9/CPauSFkmPrGKeUHi7m2bA rVCQ== +ARC-Authentication-Results: i=1; mx.google.com; dkim=pass + header.i=@accounts.google.com header.s=20161025 header.b=XOpeYXK3; spf=pass + (google.com: domain of + 3zj_wyagtcnmef-ivgcprttflekj.xffxcv.tfdkvjkcuiuvmxdrzc.tfd@gaia.bounces.google.com + designates 209.85.220.73 as permitted sender) + smtp.mailfrom=3ZJ_wYAgTCnMef-iVgcpRTTflekj.XffXcV.TfdkVjkcUiUVmXdRZc.Tfd@gaia.bounces.google.com; + dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=accounts.google.com +Return-Path: <3ZJ_wYAgTCnMef-iVgcpRTTflekj.XffXcV.TfdkVjkcUiUVmXdRZc.Tfd@gaia.bounces.google.com> +Received: from mail-sor-f73.google.com (mail-sor-f73.google.com. + [209.85.220.73]) by mx.google.com with SMTPS id + m24sor3147147pls.67.2021.07.15.13.49.40 for (Google + Transport Security); Thu, 15 Jul 2021 13:49:40 -0700 (PDT) +Received-SPF: pass (google.com: domain of + 3zj_wyagtcnmef-ivgcprttflekj.xffxcv.tfdkvjkcuiuvmxdrzc.tfd@gaia.bounces.google.com + designates 209.85.220.73 as permitted sender) client-ip=209.85.220.73; +Authentication-Results: mx.google.com; dkim=pass header.i=@accounts.google.com + header.s=20161025 header.b=XOpeYXK3; spf=pass (google.com: domain of + 3zj_wyagtcnmef-ivgcprttflekj.xffxcv.tfdkvjkcuiuvmxdrzc.tfd@gaia.bounces.google.com + designates 209.85.220.73 as permitted sender) + smtp.mailfrom=3ZJ_wYAgTCnMef-iVgcpRTTflekj.XffXcV.TfdkVjkcUiUVmXdRZc.Tfd@gaia.bounces.google.com; + dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=accounts.google.com +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=accounts.google.com; + s=20161025; h=mime-version:date:feedback-id:message-id:subject:from:to; + bh=jzUFV8bCmEmAeTpb8amH8O1QwkNos0xYlYqLtpP6SBc=; + b=XOpeYXK3QbrfDh9/pqAG7UruMxyCVah4Lj5tfBPHRh8C6AvzStTVsgRTFzPGuxB9cq + XRneLOf3kYKud2kkKMNShaG+/t65suM2MTo3q81Qvt9N6n8motuXmnLnNM7kVCaXM//s + BxZjiL4nXdjrQBF0N7wcns7xFS+J9pKZbfbm0BaUV32Kb7ef63PQHwhE+au4ytUynBqj + hxfA977RfwEXbFmUInVRJ6IJhMoygnx/Y10AccV0TuuLtnNKJIsPFv04SJcOclwaNJ3H + 69GSdpRSNJyqn7Rzm4va6l4Lxhjfjzm7+IVzMx717A5e8dLiLjxftCRS1F6EXTeYUvQ+ QcUQ== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; + s=20161025; + h=x-gm-message-state:mime-version:date:feedback-id:message-id:subject + :from:to; bh=jzUFV8bCmEmAeTpb8amH8O1QwkNos0xYlYqLtpP6SBc=; + b=uWWatGm2u/TYFa5pXhZCPJ59bmYVNEj9qv3Z5wiotMZ7pWc9mR0LYGgD99IjNs7E0G + hhv+GwtrMGft69VPG4GyUbmd7pm27G+ykFu2kLfB5lTRhZNjaqeLLkIS4KgbTlAsJJ7q + UNrIvPgpAksxnY79qtneThZOTI4o0s+V94A5T5p3/PN99YXq22RxE/aHCIpMIj/NyJeJ + RUGdEaiAjQXuG70ekiFAy2cePOI+WCFZEfYmq6vAJO4y285ID/ovjv4cpFIqTu3QFb/u + gdZFQriVXnKyZryLbjN1PlpK0772vsatNh5jC8GgOsJjSqJmZLNR5urtUlnRmTaJ8hoV F3lA== +X-Gm-Message-State: AOAM5338zSEHryX6TtcvwcYlbrnaMvleKLH9RhoGJaqMLvXSPlD7sWEt + pW5IuEivfKUIAHzbl8HeQWtM86OUvsPb26Z/ddNWtw== +X-Google-Smtp-Source: ABdhPJxfD6pBcAwUGMIoqNyPu3KT+9RON4ujFwzRhm3eCKUYxpRR+2hPoCLgdMzCE1TnOL7ShV3hLBEBAQzFtzZ6ue8glg== +MIME-Version: 1.0 +X-Received: by 2002:a17:902:ed82:b029:ef:48c8:128e with SMTP id + e2-20020a170902ed82b02900ef48c8128emr4950093plj.72.1626382180494; Thu, 15 Jul + 2021 13:49:40 -0700 (PDT) +Date: Thu, 15 Jul 2021 20:49:40 GMT +X-Account-Notification-Type: 46-anexp#nret-fa +Feedback-ID: 46-anexp#nret-fa:account-notifier +X-Notifications: 56c00ab9d6600000 +X-Notifications-Bounce-Info: ARgXy4C6Xm7P0JA65M5w_0EB-KbSHnYA8DuTVsZzaMJT7AktG4DWNKZC7qYb4FkrhakLBYzS11Hsoc4qaJocq__FbWMZwTVOmo9d1gWCxx2JMJOgcGQxMKdQ_UVnkuDZugVRr6nih0prpV8OI7Y0ijFI5KVo1M_9f-HXojR-v_jydOpt3lpHGJ-AIQ +Message-ID: +Subject: =?UTF-8?B?QWxlcnRlIGRlIHPDqWN1cml0w6k=?= +From: Google +To: testldrdev@gmail.com +Content-Type: multipart/alternative; boundary="000000000000232f0005c72f9c4b" diff --git a/test/phpunit/imap_htmlmsg.txt b/test/phpunit/imap_htmlmsg.txt new file mode 100644 index 00000000000..fe6eb0de8d3 --- /dev/null +++ b/test/phpunit/imap_htmlmsg.txt @@ -0,0 +1 @@ +
'; print $langs->trans('DateInvoice'); @@ -1464,11 +1495,11 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { break; } - print '
'; @@ -1592,9 +1623,13 @@ if (($id > 0 || !empty($ref)) || $projectidforalltimes > 0) { // Value spent if (!empty($arrayfields['value']['checked'])) { + $langs->load("salaries"); + print ''; $value = price2num($task_time->thm * $task_time->task_duration / 3600, 'MT', 1); + print 'thm).'">'; print price($value, 1, $langs, 1, -1, -1, $conf->currency); + print ''; print '
'.$langs->trans("Categories").''; + print $form->showCategories($object->id, Categorie::TYPE_TICKET, 1); + print "
Clé de sécurité ajoutée pour la validation en deux étapes
testldrdev@gmail.com
Si vous n'avez pas ajouté de clé de sécurité, quelqu'un utilise peut-être votre compte. Vérifiez et sécurisez votre compte dès maintenant.
Vous pouvez aussi voir l'activité liée à la sécurité de votre compte ici :
https://myaccount.google.com/notifications
Cet e-mail vous a été envoyé pour vous informer de modifications importantes apportées à votre compte et aux services Google que vous utilisez.


\ No newline at end of file diff --git a/test/phpunit/imap_plaintext.txt b/test/phpunit/imap_plaintext.txt new file mode 100644 index 00000000000..33d44eb847c --- /dev/null +++ b/test/phpunit/imap_plaintext.txt @@ -0,0 +1 @@ +[image: Google] Clé de sécurité ajoutée pour la validation en deux étapes testldrdev@gmail.com Si vous n'avez pas ajouté de clé de sécurité, quelqu'un utilise peut-être votre compte. Vérifiez et sécurisez votre compte dès maintenant. Consulter l'activité Vous pouvez aussi voir l'activité liée à la sécurité de votre compte ici : https://myaccount.google.com/notifications Cet e-mail vous a été envoyé pour vous informer de modifications importantes apportées à votre compte et aux services Google que vous utilisez. © 2021 Google Ireland Ltd., Gordon House, Barrow Street, Dublin 4, Ireland \ No newline at end of file