2
0
forked from Wavyzz/dolibarr

merge upstream

This commit is contained in:
2025-10-11 14:57:18 +02:00
84 changed files with 2391 additions and 834 deletions

View File

@@ -1182,12 +1182,6 @@ parameters:
count: 1
path: ../../../htdocs/ai/admin/setup.php
-
message: '#^Call to function is_object\(\) with ActionComm\|Adherent\|Commande\|CommandeFournisseur\|Contact\|Contrat\|Expedition\|ExpenseReport\|Facture\|FactureFournisseur\|Fichinter\|Mo\|Product\|Project\|Propal\|Task will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
count: 2
path: ../../../htdocs/api/class/api_documents.class.php
-
message: '#^Variable \$modulepart in empty\(\) always exists and is not falsy\.$#'
identifier: empty.variable
@@ -6846,12 +6840,6 @@ parameters:
count: 2
path: ../../../htdocs/core/lib/admin.lib.php
-
message: '#^Variable \$strictw3c in empty\(\) always exists and is not falsy\.$#'
identifier: empty.variable
count: 13
path: ../../../htdocs/core/lib/admin.lib.php
-
message: '#^Negated boolean expression is always false\.$#'
identifier: booleanNot.alwaysFalse
@@ -14586,12 +14574,6 @@ parameters:
count: 1
path: ../../../htdocs/public/eventorganization/subscriptionok.php
-
message: '#^Variable \$suffix might not be defined\.$#'
identifier: variable.undefined
count: 1
path: ../../../htdocs/public/eventorganization/subscriptionok.php
-
message: '#^Call to function is_numeric\(\) with int will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType

View File

@@ -12,7 +12,7 @@ return [
// PhanUndeclaredProperty : 420+ occurrences
// PhanTypeMismatchProperty : 100+ occurrences
// PhanTypeMismatchArgument : 70+ occurrences
// PhanUndeclaredGlobalVariable : 70+ occurrences
// PhanUndeclaredGlobalVariable : 65+ occurrences
// PhanTypeMismatchArgumentNullable : 40+ occurrences
// PhanTypeInvalidDimOffset : 15+ occurrences
// PhanTypeMismatchDimFetch : 15+ occurrences
@@ -230,7 +230,6 @@ return [
'htdocs/projet/tasks.php' => ['PhanTypeMismatchArgument'],
'htdocs/projet/tasks/time.php' => ['PhanTypeInvalidDimOffset', 'PhanUndeclaredProperty'],
'htdocs/projet/tasks/tpl/linkedobjectblock.tpl.php' => ['PhanUndeclaredProperty'],
'htdocs/public/eventorganization/subscriptionok.php' => ['PhanUndeclaredGlobalVariable'],
'htdocs/public/members/new.php' => ['PhanUndeclaredGlobalVariable'],
'htdocs/public/payment/newpayment.php' => ['PhanUndeclaredProperty'],
'htdocs/public/payment/paymentok.php' => ['PhanTypeMismatchArgumentProbablyReal'],

View File

@@ -37,6 +37,7 @@
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
/**
* @var Conf $conf
@@ -76,9 +77,63 @@ $error = 0;
include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php';
global $conf;
if ($action == 'updateMask') {
$maskconst = GETPOST('maskconst', 'aZ09');
$maskvalue = GETPOST('maskvalue', 'alpha');
if ($action == 'set_default') {
$res = 0;
if ($maskconst && preg_match('/_MASK$/', $maskconst)) {
$res = dolibarr_set_const($db, $maskconst, $maskvalue, 'chaine', 0, '', $conf->entity);
}
if (!($res > 0)) {
$error++;
}
if (!$error) {
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
} else {
setEventMessages($langs->trans("Error"), null, 'errors');
}
} elseif ($action == 'specimen') { // For fiche expensereport
$modele = GETPOST('module', 'alpha');
$adherentspecimen = new Adherent($db);
$adherentspecimen->initAsSpecimen();
$adherentspecimen->status = 0; // Force statut draft to show watermark
// Search template files
$file = '';
$classname = '';
$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
foreach ($dirmodels as $reldir) {
$file = dol_buildpath($reldir."core/modules/member/doc/pdf_".$modele.".modules.php", 0);
if (file_exists($file)) {
$classname = "pdf_".$modele;
break;
}
}
if ($classname !== '') {
require_once $file;
$module = new $classname($db);
'@phan-var-force ModelePDFMember $module';
/** @var ModelePDFMember $module */
if ($module->write_file($adherentspecimen, $langs) > 0) {
header("Location: ".DOL_URL_ROOT."/document.php?modulepart=member&file=SPECIMEN.pdf");
return;
} else {
setEventMessages($module->error, $module->errors, 'errors');
dol_syslog($module->error, LOG_ERR);
}
} else {
setEventMessages($langs->trans("ErrorModuleNotFound"), null, 'errors');
dol_syslog($langs->trans("ErrorModuleNotFound"), LOG_ERR);
}
} elseif ($action == 'set_default') {
$ret = addDocumentModel($value, $type, $label, $scandir);
$res = true;
} elseif ($action == 'del_default') {
@@ -286,6 +341,7 @@ foreach ($dirModMember as $dirroot) {
continue;
}
$modCodeMember = new $file();
/** @var ModeleNumRefMembers $modCodeMember */
// Show modules according to features level
if ($modCodeMember->version == 'development' && getDolGlobalInt('MAIN_FEATURES_LEVEL') < 2) {
continue;

View File

@@ -180,16 +180,11 @@ print "</tr>\n";
foreach ($tableau as $key => $const) { // Loop on each param
$label = '';
// $const is a const key like 'MYMODULE_ABC'
if (is_array($const)) {
$type = $const['type'];
$label = $const['label'];
$help = $const['help'];
$const = $key;
} else {
$type = $const;
$const = $key;
}
$type = $const['type'];
$label = $const['label'];
$help = empty($const['help']) ? '' : $const['help'];
$const = $key;
$sql = "SELECT rowid, ".$db->decrypt('name')." as name, ".$db->decrypt('value')." as value, type, note";
$sql .= " FROM ".MAIN_DB_PREFIX."const";
$sql .= " WHERE ".$db->decrypt('name')." = '".$db->escape($const)."'";

View File

@@ -29,21 +29,21 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*
* @var string $dolibarr_main_url_root
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php';
// Load translation files required by the page
$langs->loadLangs(array("admin", "members"));
@@ -244,7 +244,8 @@ if (getDolGlobalString('MEMBER_ENABLE_PUBLIC')) {
print '<tr class="oddeven drag" id="trforcenature"><td>';
print $langs->trans("ForceMemberNature");
print '</td><td>';
$forcenature = getDolGlobalString('MEMBER_NEWFORM_FORCEMORPHY');
$forcenature = getDolGlobalString('MEMBER_NEWFORM_FORCEMORPHY'); // 'phy' or 'mor'
print $form->selectarray("MEMBER_NEWFORM_FORCEMORPHY", $morphys, $forcenature, $langs->trans("No"), 0, 0, '', 0, 0, 0, '', 'width200');
print "</td></tr>\n";

View File

@@ -836,6 +836,7 @@ class Adherent extends CommonObject
$sql = "UPDATE ".MAIN_DB_PREFIX."adherent SET";
$sql .= " ref = '".$this->db->escape($this->ref)."'";
$sql .= ", ref_ext = ".(empty($this->ref_ext) ? "null" : "'".$this->db->escape($this->ref_ext)."'");
$sql .= ", civility = ".($this->civility_id ? "'".$this->db->escape($this->civility_id)."'" : "null");
$sql .= ", firstname = ".($this->firstname ? "'".$this->db->escape($this->firstname)."'" : "null");
$sql .= ", lastname = ".($this->lastname ? "'".$this->db->escape($this->lastname)."'" : "null");
@@ -1747,8 +1748,6 @@ class Adherent extends CommonObject
public function fetch_subscriptions()
{
// phpcs:enable
global $langs;
require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php';
$sql = "SELECT c.rowid, c.fk_adherent, c.fk_type, c.subscription, c.note as note_public, c.fk_bank,";
@@ -1811,8 +1810,6 @@ class Adherent extends CommonObject
*/
public function fetchPartnerships($mode)
{
global $langs;
require_once DOL_DOCUMENT_ROOT.'/partnership/class/partnership.class.php';
@@ -1829,15 +1826,16 @@ class Adherent extends CommonObject
* @param double $amount Amount of subscription (0 accepted for some members)
* @param int $accountid Id bank account. NOT USED.
* @param string $operation Code of payment mode (if Id bank account provided). Example: 'CB', ... NOT USED.
* @param string $label Label operation (if Id bank account provided).
* @param string $num_chq Numero cheque (if Id bank account provided)
* @param string $label Label operation stored into public note.
* @param string $num_chq Numero cheque (if Id bank account provided). NOT USED.
* @param string $emetteur_nom Name of cheque writer
* @param string $emetteur_banque Name of bank of cheque
* @param int $datesubend Date end subscription
* @param int $fk_type Member type id
* @param string $ref_ext To save an external ref
* @return int rowid of record added, <0 if KO
*/
public function subscription($date, $amount, $accountid = 0, $operation = '', $label = '', $num_chq = '', $emetteur_nom = '', $emetteur_banque = '', $datesubend = 0, $fk_type = null)
public function subscription($date, $amount, $accountid = 0, $operation = '', $label = '', $num_chq = '', $emetteur_nom = '', $emetteur_banque = '', $datesubend = 0, $fk_type = null, $ref_ext = '')
{
global $user;
@@ -1866,9 +1864,10 @@ class Adherent extends CommonObject
$subscription->dateh = $date; // Date of new subscription
$subscription->datef = $datefin; // End data of new subscription
$subscription->amount = $amount;
$subscription->note = $label; // deprecated
$subscription->note_public = $label;
$subscription->note_private = '';
$subscription->fk_type = $fk_type;
$subscription->ref_ext = $ref_ext;
if (empty($subscription->user_creation_id)) {
$subscription->user_creation_id = $user->id;

View File

@@ -165,12 +165,14 @@ class Subscription extends CommonObject
$type = $this->fk_type;
}
$sql = "INSERT INTO ".MAIN_DB_PREFIX."subscription (fk_adherent, fk_type, datec, dateadh, datef, subscription, note, fk_user_creat)";
$sql = "INSERT INTO ".MAIN_DB_PREFIX."subscription (fk_adherent, fk_type, datec, dateadh, datef, subscription, note, note_private, ref_ext, fk_user_creat)";
$sql .= " VALUES (".((int) $this->fk_adherent).", '".$this->db->escape((string) $type)."', '".$this->db->idate($now)."',";
$sql .= " '".$this->db->idate($this->dateh)."',";
$sql .= " '".$this->db->idate($this->datef)."',";
$sql .= " ".((float) $this->amount).",";
$sql .= " '".$this->db->escape($this->note_public ? $this->note_public : $this->note)."',";
$sql .= " '".$this->db->escape($this->note_private)."',";
$sql .= " ".(empty($this->ref_ext) ? "null" : "'".$this->db->escape($this->ref_ext)."'").",";
$sql .= " ".((int) ($this->user_creation_id > 0 ? $this->user_creation_id : $user->id));
$sql .= ")";

View File

@@ -25,7 +25,6 @@
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -52,8 +51,6 @@ $hookmanager->initHooks(array('homesetup'));
* View
*/
$form = new Form($db);
$wikihelp = 'EN:First_setup|FR:Premiers_paramétrages|ES:Primeras_configuraciones';
llxHeader('', $langs->trans("Setup"), $wikihelp, '', 0, 0, '', '', '', 'mod-admin page-index');
@@ -84,8 +81,9 @@ if (getDolGlobalString('MAIN_MOTD_SETUPPAGE')) {
}
print '<span class="opacitymedium hideonsmartphone">';
print $langs->trans("SetupDescription1").' ';
print $langs->trans("AreaForAdminOnly").' ';
print $langs->trans("SetupDescription1").'<br>';
//print $langs->trans("AreaForAdminOnly").' ';
print '<br>';
print $langs->trans("SetupDescription2", $langs->transnoentities("MenuCompanySetup"), $langs->transnoentities("Modules"));
print "<br><br>";
print '</span>';
@@ -138,7 +136,7 @@ print '<section class="setupsection setupmodules cursorpointer">';
// Define $nbmodulesnotautoenabled - TODO This code is at different places
$nbmodulesnotautoenabled = count($conf->modules);
$listofmodulesautoenabled = array('agenda', 'fckeditor', 'export', 'import');
$listofmodulesautoenabled = array('user', 'agenda', 'fckeditor', 'export', 'import'); // All modules with ->enabled_bydefault to true (so enabled during install)
foreach ($listofmodulesautoenabled as $moduleautoenable) {
if (in_array($moduleautoenable, $conf->modules)) {
$nbmodulesnotautoenabled--;
@@ -150,7 +148,7 @@ print img_picto('', 'cog', 'class="paddingright valignmiddle double"');
print ' ';
print '<a class="nounderlineimp" href="'.DOL_URL_ROOT.'/admin/modules.php?mainmenu=home">'.$langs->transnoentities("Setup").' - '.$langs->transnoentities("Modules").'</a>';
print '<br><br>'.$langs->trans("SetupDescription4b");
if ($nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
if ($nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
$langs->load("errors");
$warnpicto = img_warning($langs->trans("WarningEnableYourModulesApplications"), 'style="padding-right: 6px;"');
print '<br><div class="warning"><a href="'.DOL_URL_ROOT.'/admin/modules.php?mainmenu=home">'.$warnpicto.$langs->trans("WarningEnableYourModulesApplications").'</a></div>';

View File

@@ -165,11 +165,14 @@ if (dol_is_file($dolibarrdataroot.'/installmodules.lock')) {
$allowonlineinstall = false;
}
//$remotestore = new Dolistore(false);
$remotestore = new ExternalModules();
$debug = false;
$remotestore = new ExternalModules($debug);
if ($mode == 'marketplace') {
// Make remote calls
$remotestore->loadRemoteSources();
if (GETPOSTINT('dol_resetcache')) {
dol_delete_file($remotestore->cache_file);
}
$remotestore->loadRemoteSources(false);
}
$object = new stdClass();
@@ -693,7 +696,7 @@ $nbofactivatedmodules = count($conf->modules);
// Define $nbmodulesnotautoenabled - TODO This code is at different places
$nbmodulesnotautoenabled = count($conf->modules);
$listofmodulesautoenabled = array('agenda', 'fckeditor', 'export', 'import');
$listofmodulesautoenabled = array('user', 'agenda', 'fckeditor', 'export', 'import');
foreach ($listofmodulesautoenabled as $moduleautoenable) {
if (in_array($moduleautoenable, $conf->modules)) {
$nbmodulesnotautoenabled--;
@@ -709,7 +712,7 @@ if ($mode == 'common' || $mode == 'commonkanban') {
$desc .= ' '.$langs->trans("ModulesDesc2", '{picto2}');
$desc = str_replace('{picto}', img_picto('', 'switch_off', 'class="size15x"'), $desc);
$desc = str_replace('{picto2}', img_picto('', 'setup', 'class="size15x"'), $desc);
if ($nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
if ($nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
$deschelp .= '<div class="info hideonsmartphone">'.$desc."<br></div>\n";
}
if (getDolGlobalString('MAIN_SETUP_MODULES_INFO')) { // Show a custom message. A good usage for SaaS with option MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING.
@@ -1119,7 +1122,13 @@ if ($mode == 'common' || $mode == 'commonkanban') {
$codeenabledisable .= '<!-- This module is a core module and it may have a warning to show when we activate it (note: your country is '.$mysoc->country_code.') -->'."\n";
foreach ($arrayofwarnings[$modName] as $keycountry => $cursorwarningmessage) {
if (preg_match('/^always/', $keycountry) || ($mysoc->country_code && preg_match('/^'.$mysoc->country_code.'/', $keycountry))) {
$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans($cursorwarningmessage, $objMod->getName(), $mysoc->country_code);
if (!is_array($cursorwarningmessage)) {
$cursorwarningmessage = array($cursorwarningmessage);
}
foreach ($cursorwarningmessage as $messagetoshow) {
// TODO Use replacement instead of always adding param module name and country code to the string message
$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans($messagetoshow, $objMod->getName(), $mysoc->country_code);
}
}
}
}
@@ -1127,15 +1136,21 @@ if ($mode == 'common' || $mode == 'commonkanban') {
$codeenabledisable .= '<!-- This module is an external module and it may have a warning to show (note: your country is '.$mysoc->country_code.') -->'."\n";
foreach ($arrayofwarningsext as $keymodule => $arrayofwarningsextbycountry) {
$keymodulelowercase = strtolower(preg_replace('/^mod/', '', $keymodule));
if (in_array($keymodulelowercase, $conf->modules)) { // If module that request warning is on
if (preg_match('/^always/', $keymodulelowercase) || in_array($keymodulelowercase, $conf->modules)) { // If module that trigger the warning is on
foreach ($arrayofwarningsextbycountry as $keycountry => $cursorwarningmessage) {
if (preg_match('/^always/', $keycountry) || ($mysoc->country_code && preg_match('/^'.$mysoc->country_code.'/', $keycountry))) {
$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans($cursorwarningmessage, $objMod->getName(), $mysoc->country_code, $modules[$keymodule]->getName());
if (!is_array($cursorwarningmessage)) {
$cursorwarningmessage = array($cursorwarningmessage);
}
foreach ($cursorwarningmessage as $messagetoshow) {
// TODO Use replacement instead of always adding param module name to enable and country code to the string message and triggering module
$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans($messagetoshow, $objMod->getName(), $mysoc->country_code, $modules[$keymodule]->getName());
}
$warningmessage .= ($warningmessage ? "\n" : "").($warningmessage ? "\n" : "").$langs->trans("Module").' : '.$objMod->getName();
if (!empty($objMod->editor_name)) {
$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans("Publisher").' : '.$objMod->editor_name;
}
if (!empty($objMod->editor_name)) {
if ($keymodulelowercase != 'always') {
$warningmessage .= ($warningmessage ? "\n" : "").$langs->trans("ModuleTriggeringThisWarning").' : '.$modules[$keymodule]->getName();
}
}

View File

@@ -319,6 +319,8 @@ class ExternalModules
{
global $langs;
$langs->load("products");
$html = "";
$last_month = dol_now() - (30 * 24 * 60 * 60);
$dolibarrversiontouse = DOL_VERSION; // full string with version
@@ -510,18 +512,42 @@ class ExternalModules
$html .= '<td class="margeCote"><h2 class="appTitle">';
$html .= dolPrintHTML(dol_string_nohtmltag($product["label"]));
$html .= '<br><small>';
$html .= $version; // No dol_escape_htmltag, it is already escape html
$html .= $version; // Version Dolibarr. No dol_escape_htmltag, it is already escape html
$html .= '</small></h2>';
$html .= '<small> ';
$html .= '<small class="appDateCreation appRef"> ';
if (empty($product['tms'])) {
$html .= '<span class="opacitymedium">'.$langs->trans("DateCreation").': '.$langs->trans("Unknown").'</span>';
$html .= img_picto($langs->trans('DateCreation'), 'calendar', 'class="pictofixedwidth"').'<span class="opacitymedium">'.$langs->trans("DateCreation").': ';
$html .= (!empty($product['datec']) ? dol_print_date(dol_stringtotime($product['datec']), 'day') : $langs->trans("Unknown")).'</span>';
} else {
$html .= '<span class="opacitymedium">'.dol_print_date(dol_stringtotime($product['tms']), 'day').'</span>';
$html .= img_picto($langs->trans('DateModification'), 'calendar', 'class="pictofixedwidth"').'<span class="opacitymedium">'.dol_print_date(dol_stringtotime($product['tms']), 'day').'</span>';
}
$html .= ' - '.$langs->trans('Ref').' '.dolPrintHTML($product["ref"]);
$html .= ' &nbsp; '.$langs->trans('Ref').' '.dolPrintHTML(preg_replace('/@.*$/', '', $product["ref"]));
//$html .= ' - '.dol_escape_htmltag($langs->trans('Id')).': '.((int) $product["id"]);
$html .= '</small><br>';
$html .= '<small>'.$langs->trans('Source').': '.$product["source"].'</small><br>';
//$html .= '<div class="appSource valignmiddle inline-block">'.$langs->trans('Source').' &nbsp; </div>';
$html .= '<div class="appSource valignmiddle inline-block">';
if ($product["source"] == 'dolistore') {
//$html .= img_picto('DoliStore', 'shop', 'class="pictofixedwidth"');
$html .= '<img border="0" title="'.dolPrintHTML($langs->trans('Source').": DoliStore").'" class="imgautosize imgmaxwidth100 valignmiddle" style="height: 14px" src="'.DOL_URL_ROOT.'/theme/dolistore_squarred.svg">';
} elseif ($product["source"] == 'githubcommunity') {
$html .= img_picto($langs->trans('Source').': GitHub community repo', 'group', 'class="pictofixedwidth valignmiddle"');
} else {
$html .= img_picto($langs->trans('Source').': '.$langs->trans('Other'), 'generic', 'class="pictofixedwidth"');
}
//$html .= $product["source"];
$html .= '</div> &nbsp;';
if (!empty($product['phpmin']) && $product['phpmin'] != 'unknown') {
$html .= ' <span class="badge-secondary" style="padding: 2px; border-radius: 5px">PHP min '.$product['phpmin'].'</span>';
}
if (!empty($product['phpmax']) && $product['phpmax'] != 'unknown') {
$html .= ' <span class="badge-secondary" style="padding: 2px; border-radius: 5px">PHP max '.$product['phpmax'].'</span>';
}
if (!empty($product['author'])) {
$html .= ' - '.$langs->trans("Author").' : '.$product['author'];
}
$html .= '<br>';
$html .= '<br>'.dolPrintHTML(dol_string_nohtmltag($product["description"]));
$html .= '</td>';
// do not load if display none

View File

@@ -78,6 +78,7 @@ if (!GETPOST('hidenavmenu')) {
<!-- Summary -->
<?php $documentation->showSummary(); ?>
<br>
<!-- List of usage font awesome icon -->
<div class="documentation-section" id="img-picto-section-list">
@@ -161,6 +162,9 @@ if (!GETPOST('hidenavmenu')) {
<!-- -->
<br><br>
<!-- List of usage font awesome icon -->
<div class="documentation-section" id="icon-section-list">
@@ -255,3 +259,308 @@ if (!GETPOST('hidenavmenu')) {
<?php
// Output close body + html
$documentation->docFooter();
/**
* Get all usage icon key usable for img_picto(..., key)
*
* @return string[]
* @see getImgPictoConv()
*/
function getImgPictoNameList()
{
return array_merge(array_keys(getImgPictoConv()), array(
// Reduce this list to picto that are not already into getImgPictoConv()
'1downarrow',
'1uparrow',
'1leftarrow',
'1rightarrow',
'1uparrow_selected',
'1downarrow_selected',
'1leftarrow_selected',
'1rightarrow_selected',
'accountancy',
'accounting_account',
'account',
'accountline',
'action',
'add',
'address',
'ai',
'angle-double-down',
'angle-double-up',
'asset',
'back',
'bank_account',
'barcode',
'bank',
'bell',
'bill',
'billa',
'billr',
'billd',
'birthday-cake',
'blockedlog',
'bom',
'bookcal',
'bookmark',
'briefcase-medical',
'bug',
'building',
'card',
'calendarlist',
'calendar',
'calendarmonth',
'calendarweek',
'calendarday',
'calendarperuser',
'calendarpertype',
'hourglass',
'cash-register',
'category',
'chart',
'check',
'clock',
'clone',
'close_title',
'code',
'cog',
'collab',
'company',
'contact',
'country',
'contract',
'conversation',
'cron',
'cross',
'cubes',
'check-circle',
'check-square',
'circle',
'stop-circle',
'currency',
'multicurrency',
'chevron-left',
'chevron-right',
'chevron-down',
'chevron-up',
'chevron-double-left',
'chevron-double-right',
'chevron-double-down',
'chevron-double-top',
'commercial',
'companies',
'delete',
'dolly',
'dollyrevert',
'donation',
'download',
'dynamicprice',
'edit',
'ellipsis-h',
'email',
'entity',
'envelope',
'eraser',
'establishment',
'expensereport',
'external-link-alt',
'external-link-square-alt',
'eye',
'filter',
'file',
'file-o',
'file-code',
'file-export',
'file-import',
'file-upload',
'autofill',
'folder',
'folder-open',
'folder-plus',
'font',
'generate',
'generic',
'globe',
'globe-americas',
'graph',
'grip',
'grip_title',
'group',
'hands-helping',
'help',
'holiday',
'id-card',
'images',
'incoterm',
'info',
'info_black',
'intervention',
'inventory',
'intracommreport',
'jobprofile',
'key',
'knowledgemanagement',
'label',
'language',
'layout',
'line',
'link',
'list',
'list-alt',
'listlight',
'loan',
'lock',
'lot',
'long-arrow-alt-right',
'margin',
'map-marker-alt',
'member',
'meeting',
'minus',
'money-bill-alt',
'movement',
'mrp',
'note',
'next',
'off',
'on',
'order',
'paragraph',
'play',
'pdf',
'phone',
'phoning',
'phoning_mobile',
'phoning_fax',
'playdisabled',
'previous',
'poll',
'pos',
'printer',
'product',
'propal',
'proposal',
'puce',
'resize',
'search',
'service',
'stats',
'stock',
'security',
'setup',
'share-alt',
'sign-out',
'split',
'stripe',
'stripe-s',
'switch_off',
'switch_on',
'switch_on_grey',
'switch_on_warning',
'switch_on_red',
'tools',
'unlink',
'uparrow',
'user',
'user-tie',
'vcard',
'wrench',
'discord',
'facebook',
'flickr',
'instagram',
'linkedin',
'github',
'google',
'jabber',
'meetup',
'microsoft',
'skype',
'slack',
'twitter',
'pinterest',
'reddit',
'snapchat',
'tumblr',
'youtube',
'viadeo',
'google-plus-g',
'whatsapp',
'generic',
'home',
'hrm',
'members',
'products',
'invoicing',
'partnership',
'payment',
'payment_vat',
'pencil-ruler',
'pictoconfirm',
'preview',
'project',
'projectpub',
'projecttask',
'question',
'refresh',
'region',
'salary',
'shipment',
'state',
'supplier_invoice',
'supplier_invoicea',
'supplier_invoicer',
'supplier_invoiced',
'technic',
'ticket',
'error',
'warning',
'recent',
'reception',
'recruitmentcandidature',
'recruitmentjobposition',
'replacement',
'resource',
'recurring',
'rss',
'search-plus',
'shapes',
'skill',
'square',
'sort-numeric-down',
'status',
'stop-circle',
'supplier',
'supplier_proposal',
'supplier_order',
'supplier_invoice',
'terminal',
'tick',
'timespent',
'title_setup',
'title_accountancy',
'title_bank',
'title_hrm',
'title_agenda',
'trip',
'uncheck',
'undo',
'url',
'user-cog',
'user-injured',
'user-md',
'upload',
'vat',
'website',
'workstation',
'webhook',
'world',
'private',
'conferenceorbooth',
'eventorganization',
'stamp',
'signature',
'webportal'
));
}

View File

@@ -505,7 +505,7 @@ class Documents extends DolibarrApi
throw new RestException(404, 'Invoice not found');
}
$upload_dir = getMultidirOutput($object) . "/facture/".get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier').dol_sanitizeFileName($object->ref);
$upload_dir = getMultidirOutput($object) . "/".get_exdir($object->id, 2, 0, 0, $object, 'invoice_supplier').dol_sanitizeFileName($object->ref);
} elseif ($modulepart == 'produit' || $modulepart == 'product' || $modulepart == 'service') {
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
@@ -892,6 +892,16 @@ class Documents extends DolibarrApi
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
$object = new Contact($this->db);
$fetchbyid = true;
} elseif ($modulepart == 'societe' || $modulepart == 'company') {
$modulepart = 'societe';
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
$object = new Societe($this->db);
$fetchbyid = true;
} elseif ($modulepart == 'ticket' ) {
$modulepart = 'ticket';
require_once DOL_DOCUMENT_ROOT.'/ticket/class/ticket.class.php';
$object = new Ticket($this->db);
$fetchbyid = true;
} elseif ($modulepart == 'contrat' || $modulepart == 'contract') {
$modulepart = 'contrat';
require_once DOL_DOCUMENT_ROOT . '/contrat/class/contrat.class.php';
@@ -907,19 +917,18 @@ class Documents extends DolibarrApi
throw new RestException(500, 'Modulepart '.$modulepart.' not implemented yet.');
}
if (is_object($object)) {
if ($fetchbyid) {
// @phan-suppress-next-line PhanPluginSuspiciousParamPosition
$result = $object->fetch((int) $ref);
} else {
$result = $object->fetch(0, $ref);
}
// at this step $object is always an object
if ($fetchbyid) {
// @phan-suppress-next-line PhanPluginSuspiciousParamPosition
$result = $object->fetch((int) $ref);
} else {
$result = $object->fetch(0, $ref);
}
if ($result == 0) {
throw new RestException(404, "Object with ref '".$ref."' was not found.");
} elseif ($result < 0) {
throw new RestException(500, 'Error while fetching object: '.$object->error);
}
if ($result == 0) {
throw new RestException(404, "Object with ref '".$ref."' was not found.");
} elseif ($result < 0) {
throw new RestException(500, 'Error while fetching object: '.$object->error);
}
if (!($object->id > 0)) {
@@ -1056,7 +1065,8 @@ class Documents extends DolibarrApi
}
$moreinfo = array('note_private' => 'File uploaded using API /documents from IP '.getUserRemoteIP());
if (!empty($object) && is_object($object) && $object->id > 0) {
// $object may be null
if (is_object($object) && $object->id > 0) {
$moreinfo['src_object_type'] = $object->table_element;
$moreinfo['src_object_id'] = $object->id;
}

View File

@@ -1,7 +1,7 @@
<?php
/* Copyright (C) 2009-2010 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
/* Copyright (C) 2009-2010 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2024-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -94,9 +94,6 @@ function llxFooter($comment = '', $zone = 'private', $disabledoutputofmessages =
}
require_once '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -105,6 +102,9 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
// Security check
if (!isModEnabled('clicktodial')) {
accessforbidden();
@@ -112,34 +112,9 @@ if (!isModEnabled('clicktodial')) {
// Define Asterisk setup
if (!getDolGlobalString('ASTERISK_HOST')) {
$conf->global->ASTERISK_HOST = "127.0.0.1";
}
if (!getDolGlobalString('ASTERISK_TYPE')) {
$conf->global->ASTERISK_TYPE = "SIP/";
}
if (!getDolGlobalString('ASTERISK_INDICATIF')) {
$conf->global->ASTERISK_INDICATIF = "0";
}
if (!getDolGlobalString('ASTERISK_PORT')) {
$conf->global->ASTERISK_PORT = 5038;
}
if (getDolGlobalString('ASTERISK_INDICATIF') == 'NONE') {
$conf->global->ASTERISK_INDICATIF = '';
}
if (!getDolGlobalString('ASTERISK_CONTEXT')) {
$conf->global->ASTERISK_CONTEXT = "from-internal";
}
if (!getDolGlobalString('ASTERISK_WAIT_TIME')) {
$conf->global->ASTERISK_WAIT_TIME = "30";
}
if (!getDolGlobalString('ASTERISK_PRIORITY')) {
$conf->global->ASTERISK_PRIORITY = "1";
}
if (!getDolGlobalString('ASTERISK_MAX_RETRY')) {
$conf->global->ASTERISK_MAX_RETRY = "2";
}
$login = GETPOST('login', 'alphanohtml');
$password = GETPOST('password', 'password');
@@ -153,29 +128,29 @@ $caller = preg_replace('/[\n\r]/', '', $caller);
$called = preg_replace('/[\n\r]/', '', $called);
// IP address of Asterisk server
$strHost = getDolGlobalString('ASTERISK_HOST');
$strHost = getDolGlobalString('ASTERISK_HOST', '127.0.0.1');
// Specify the type of extension through which your extension is connected.
// ex: SIP/, IAX2/, ZAP/, etc
$channel = getDolGlobalString('ASTERISK_TYPE');
$channel = getDolGlobalString('ASTERISK_TYPE', 'SIP/');
// Outgoing call sign
$prefix = getDolGlobalString('ASTERISK_INDICATIF');
$prefix = getDolGlobalString('ASTERISK_INDICATIF', '0');
// Asterisk Port
$port = getDolGlobalString('ASTERISK_PORT');
$port = getDolGlobalInt('ASTERISK_PORT', 5038);
// Context ( generalement from-internal )
$strContext = getDolGlobalString('ASTERISK_CONTEXT');
$strContext = getDolGlobalString('ASTERISK_CONTEXT', 'from-internal');
// Waiting time before hanging up
$strWaitTime = getDolGlobalString('ASTERISK_WAIT_TIME');
$strWaitTime = getDolGlobalString('ASTERISK_WAIT_TIME', '30');
// Priority
$strPriority = getDolGlobalString('ASTERISK_PRIORITY');
$strPriority = getDolGlobalString('ASTERISK_PRIORITY', '1');
// Number of call attempts
$strMaxRetry = getDolGlobalString('ASTERISK_MAX_RETRY');
$strMaxRetry = getDolGlobalString('ASTERISK_MAX_RETRY', "2");
/*

View File

@@ -45,7 +45,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
*/
// Load translation files required by the page
$langs->loadLangs(array('admin', 'bills', 'blockedlog', 'other'));
$langs->loadLangs(array('admin', 'banks', 'bills', 'blockedlog', 'other'));
// Access Control
if ((!$user->admin && !$user->hasRight('blockedlog', 'read')) || empty($conf->blockedlog->enabled)) {
@@ -389,10 +389,11 @@ if (GETPOST('withtab', 'alpha')) {
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php').'">'.$langs->trans("BackToModuleList").'</a>';
}
print load_fiche_titre($title, $linkback);
print load_fiche_titre($title, $linkback, 'blockedlog');
if (GETPOST('withtab', 'alpha')) {
$head = blockedlogadmin_prepare_head();
print dol_get_fiche_head($head, 'fingerprints', '', -1);
}
@@ -523,7 +524,7 @@ print '</td>';
// Actions code
print '<td class="liste_titre">';
print $form->multiselectarray('search_code', $block_static->trackedevents, $search_code, 0, 0, 'maxwidth150', 1);
print $form->multiselectarray('search_code', $block_static->trackedevents, $search_code, 0, 0, 'maxwidth200', 1);
print '</td>';
// Ref
@@ -560,6 +561,7 @@ if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
print '</tr>';
print '<tr class="liste_titre">';
// Action column
if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
@@ -574,7 +576,6 @@ print getTitleFieldOfList($langs->trans('Amount'), 0, $_SERVER["PHP_SELF"], '',
print getTitleFieldOfList($langs->trans('DataOfArchivedEvent'), 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'center ', 0, $langs->trans('DataOfArchivedEventHelp'), 1)."\n";
print getTitleFieldOfList($langs->trans('Fingerprint'), 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, '')."\n";
print getTitleFieldOfList($form->textwithpicto($langs->trans('Status'), $langs->trans('DataOfArchivedEventHelp2')), 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'center ')."\n";
//print getTitleFieldOfList('', 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'center ')."\n";
print getTitleFieldOfList('', 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, '')."\n";
// Action column
if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
@@ -657,7 +658,11 @@ if (is_array($blocks)) {
// Ref
print '<td class="nowraponall">';
print dol_escape_htmltag($block->ref_object);
if (!empty($block->ref_object)) {
print dol_escape_htmltag($block->ref_object);
} else {
// Ref not stored
}
print '</td>';
// Amount

View File

@@ -57,7 +57,7 @@ if ((!$user->admin && !$user->hasRight('blockedlog', 'read')) || empty($conf->bl
accessforbidden();
}
$langs->loadLangs(array("admin", "bills", "cashdesk", "companies"));
$langs->loadLangs(array("admin", "bills", "cashdesk", "companies", "members", "products"));
/*
@@ -115,7 +115,7 @@ function formatObject($objtoshow, $prefix)
$arrayoffields = $tmpobject->fields;
}
// Convert the key stored into blocked log into the key used into ->fields
$convertkey = array(
'name' => 'nom',
'country_code' => 'fk_pays',
@@ -129,7 +129,41 @@ function formatObject($objtoshow, $prefix)
'posmodule' => 'POSModule',
'posnumber' => 'POSTerminal',
'managers' => 'Managers',
'type_code' => 'PaymentMode'
'type_code' => 'PaymentMode',
'datec' => 'DateCreation',
'dateh' => 'DateSubscription',
'datef' => 'DateEndSubscription',
'fk_adherent' => 'MemberId',
'amount' => 'Amount',
'id' => 'ID',
'ref' => 'Ref',
'date' => 'Date',
'total_ht' => 'TotalHT',
'total_ttc' => 'TotalTTC',
'total_tva' => 'TotalVAT',
'total_localtax1' => 'TotalTax2',
'total_localtax2' => 'TotalTax3',
'multicurrency_total_ht' => 'TotalHTShortCurrency',
'multicurrency_total_ttc' => 'TotalTTCShortCurrency',
'multicurrency_total_tva' => 'TotalVATShortCurrency',
'tva_tx' => 'VatRate',
'localtax1_tx' => 'Localtax1Rate',
'localtax2_tx' => 'Localtax2Rate',
'multicurrency_code' => 'Currency',
'qty' => 'Quantity',
'nom' => 'Name',
'name' => 'Name',
'email' => 'Email',
'revenuestamp' => 'RevenueStamp',
'code_client' => 'CustomerCode',
'capital' => 'Capital',
'localtax1_value' => 'UseLocalTax1',
'localtax2_value' => 'UseLocalTax2',
'subprice' => 'UnitPrice',
'product_type' => 'ProductType',
'type' => 'InvoiceType',
'info_bits' => 'TVA NPR or NOT',
'special_code' => 'Special line (WEEE line, option, id of module...)'
);
if (is_object($newobjtoshow) || is_array($newobjtoshow)) {
@@ -148,7 +182,7 @@ function formatObject($objtoshow, $prefix)
// Label
$s .= '<td>';
$label = ''.$convertkey[$key];
$label = '';
if (isset($arrayoffields[$key]['label'])) {
$label = $langs->trans($tmpobject->fields[$key]['label']);
} elseif (!empty($convertkey[$key]) && isset($arrayoffields[$convertkey[$key]]['label'])) {
@@ -163,6 +197,12 @@ function formatObject($objtoshow, $prefix)
if (empty($label) && !empty($otherlabels[$key])) {
$label = $langs->trans($otherlabels[$key]);
}
if (empty($label) && !empty($otherlabels[$convertkey[$key]])) {
$label = $langs->trans($otherlabels[$convertkey[$key]]);
}
if (empty($label)) {
$label = array_key_exists($key, $convertkey) ? $convertkey[$key] : '';
}
if (!empty($label)) {
$s .= '<span class="opacitymedium">'.$label.'</span>';
}

View File

@@ -44,6 +44,12 @@ class BlockedLog
*/
public $entity;
/**
* Picto
* @var string
*/
public $picto = 'blockedlog';
/**
* @var string Error message
*/
@@ -176,13 +182,18 @@ class BlockedLog
$this->trackedevents = array();
$sep = 0;
// Customer Invoice/Facture / Payment
if (isModEnabled('invoice')) {
$this->trackedevents['BILL_VALIDATE'] = array('id' => 'BILL_VALIDATE', 'label' => 'logBILL_VALIDATE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logBILL_VALIDATE'));
//$this->trackedevents['BILL_UPDATE'] = array('id' => 'BILL_VALIDATE', 'label' => 'logBILL_UPDATE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logBILL_UPDATE'));
$this->trackedevents['BILL_SENTBYMAIL'] = array('id' => 'BILL_SENTBYMAIL', 'label' => 'logBILL_SENTBYMAIL', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logBILL_SENTBYMAIL'));
$this->trackedevents['DOC_DOWNLOAD'] = array('id' => 'DOC_DOWNLOAD', 'label' => 'BlockedLogBillDownload', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('BlockedLogBillDownload'));
$this->trackedevents['DOC_PREVIEW'] = array('id' => 'DOC_PREVIEW', 'label' => 'BlockedLogBillPreview', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('BlockedLogBillPreview'));
$sep++;
$this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("Invoices").' | '.$langs->trans("Payments").'</span>', 'disabled' => 1);
$this->trackedevents['BILL_VALIDATE'] = array('id' => 'BILL_VALIDATE', 'label' => 'logBILL_VALIDATE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logBILL_VALIDATE'));
//$this->trackedevents['BILL_UPDATE'] = array('id' => 'BILL_VALIDATE', 'label' => 'logBILL_UPDATE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logBILL_UPDATE'));
$this->trackedevents['BILL_SENTBYMAIL'] = array('id' => 'BILL_SENTBYMAIL', 'label' => 'logBILL_SENTBYMAIL', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logBILL_SENTBYMAIL'));
$this->trackedevents['DOC_DOWNLOAD'] = array('id' => 'DOC_DOWNLOAD', 'label' => 'BlockedLogBillDownload', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('BlockedLogBillDownload'));
$this->trackedevents['DOC_PREVIEW'] = array('id' => 'DOC_PREVIEW', 'label' => 'BlockedLogBillPreview', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('BlockedLogBillPreview'));
$this->trackedevents['PAYMENT_CUSTOMER_CREATE'] = array('id' => 'PAYMENT_CUSTOMER_CREATE', 'label' => 'logPAYMENT_CUSTOMER_CREATE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_CUSTOMER_CREATE'));
$this->trackedevents['PAYMENT_CUSTOMER_DELETE'] = array('id' => 'PAYMENT_CUSTOMER_DELETE', 'label' => 'logPAYMENT_CUSTOMER_DELETE', 'labelhtml' => img_picto('', 'bill', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_CUSTOMER_DELETE'));
}
@@ -202,53 +213,82 @@ class BlockedLog
// Donation
if (isModEnabled('don')) {
$this->trackedevents['DON_VALIDATE'] = 'logDON_VALIDATE';
$this->trackedevents['DON_DELETE'] = 'logDON_DELETE';
//$this->trackedevents['DON_SENTBYMAIL']='logDON_SENTBYMAIL';
$this->trackedevents['DONATION_PAYMENT_CREATE'] = 'logDONATION_PAYMENT_CREATE';
$this->trackedevents['DONATION_PAYMENT_DELETE'] = 'logDONATION_PAYMENT_DELETE';
if (!empty($this->trackedevents)) {
$sep++;
$this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("Donations").' | '.$langs->trans("Payments").'</span>', 'disabled' => 1);
}
$this->trackedevents['DON_VALIDATE'] = array('id' => 'DON_VALIDATE', 'label' => 'logDON_VALIDATE', 'labelhtml' => img_picto('', 'donation', 'class="pictofixedwidth").').$langs->trans('logDON_VALIDATE'));
$this->trackedevents['DON_DELETE'] = array('id' => 'DON_DELETE', 'label' => 'logDON_DELETE', 'labelhtml' => img_picto('', 'donation', 'class="pictofixedwidth").').$langs->trans('logDON_DELETE'));
//$this->trackedevents['DON_SENTBYMAIL'] = array('id' => 'BILL_VALIDATE', img_picto('', 'don', 'class="pictofixedwidth").').$langs->trans('labelhtml' => 'logDON_SENTBYMAIL');
$this->trackedevents['DONATION_PAYMENT_CREATE'] = array('id' => 'DONATION_PAYMENT_CREATE', 'label' => 'logDONATION_PAYMENT_CREATE', 'labelhtml' => img_picto('', 'donation', 'class="pictofixedwidth").').$langs->trans('logDONATION_PAYMENT_CREATE'));
$this->trackedevents['DONATION_PAYMENT_DELETE'] = array('id' => 'DONATION_PAYMENT_DELETE', 'label' => 'logDONATION_PAYMENT_DELETE', 'labelhtml' => img_picto('', 'donation', 'class="pictofixedwidth").').$langs->trans('logDONATION_PAYMENT_DELETE'));
}
/*
// Salary
if (isModEnabled('salary')) {
$this->trackedevents['PAYMENT_SALARY_CREATE']='BlockedLogSalaryPaymentCreate';
$this->trackedevents['PAYMENT_SALARY_MODIFY']='BlockedLogSalaryPaymentCreate';
$this->trackedevents['PAYMENT_SALARY_DELETE']='BlockedLogSalaryPaymentCreate';
$this->trackedevents['PAYMENT_SALARY_CREATE'] = 'BlockedLogSalaryPaymentCreate';
$this->trackedevents['PAYMENT_SALARY_MODIFY'] = 'BlockedLogSalaryPaymentCreate';
$this->trackedevents['PAYMENT_SALARY_DELETE'] = 'BlockedLogSalaryPaymentCreate';
}
*/
// Members
if (isModEnabled('member')) {
$this->trackedevents['MEMBER_SUBSCRIPTION_CREATE'] = 'logMEMBER_SUBSCRIPTION_CREATE';
$this->trackedevents['MEMBER_SUBSCRIPTION_MODIFY'] = 'logMEMBER_SUBSCRIPTION_MODIFY';
$this->trackedevents['MEMBER_SUBSCRIPTION_DELETE'] = 'logMEMBER_SUBSCRIPTION_DELETE';
if (!empty($this->trackedevents)) {
$sep++;
$this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("MenuMembers").'</span>', 'disabled' => 1);
}
$this->trackedevents['MEMBER_SUBSCRIPTION_CREATE'] = array('id' => 'MEMBER_SUBSCRIPTION_CREATE', 'label' => 'logMEMBER_SUBSCRIPTION_CREATE', 'labelhtml' => img_picto('', 'member', 'class="pictofixedwidth").').$langs->trans('logMEMBER_SUBSCRIPTION_CREATE'));
$this->trackedevents['MEMBER_SUBSCRIPTION_MODIFY'] = array('id' => 'MEMBER_SUBSCRIPTION_MODIFY', 'label' => 'logMEMBER_SUBSCRIPTION_MODIFY', 'labelhtml' => img_picto('', 'member', 'class="pictofixedwidth").').$langs->trans('logMEMBER_SUBSCRIPTION_MODIFY'));
$this->trackedevents['MEMBER_SUBSCRIPTION_DELETE'] = array('id' => 'MEMBER_SUBSCRIPTION_DELETE', 'label' => 'logMEMBER_SUBSCRIPTION_DELETE', 'labelhtml' => img_picto('', 'member', 'class="pictofixedwidth").').$langs->trans('logMEMBER_SUBSCRIPTION_DELETE'));
}
// Bank
if (isModEnabled("bank")) {
$this->trackedevents['PAYMENT_VARIOUS_CREATE'] = 'logPAYMENT_VARIOUS_CREATE';
$this->trackedevents['PAYMENT_VARIOUS_MODIFY'] = 'logPAYMENT_VARIOUS_MODIFY';
$this->trackedevents['PAYMENT_VARIOUS_DELETE'] = 'logPAYMENT_VARIOUS_DELETE';
if (!empty($this->trackedevents)) {
$sep++;
$this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("VariousPayment").'</span>', 'disabled' => 1);
}
$this->trackedevents['PAYMENT_VARIOUS_CREATE'] = array('id' => 'PAYMENT_VARIOUS_CREATE', 'label' => 'logPAYMENT_VARIOUS_CREATE', 'labelhtml' => img_picto('', 'bank', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_VARIOUS_CREATE'));
$this->trackedevents['PAYMENT_VARIOUS_MODIFY'] = array('id' => 'PAYMENT_VARIOUS_MODIFY', 'label' => 'logPAYMENT_VARIOUS_MODIFY', 'labelhtml' => img_picto('', 'bank', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_VARIOUS_MODIFY'));
$this->trackedevents['PAYMENT_VARIOUS_DELETE'] = array('id' => 'PAYMENT_VARIOUS_DELETE', 'label' => 'logPAYMENT_VARIOUS_DELETE', 'labelhtml' => img_picto('', 'bank', 'class="pictofixedwidth").').$langs->trans('logPAYMENT_VARIOUS_DELETE'));
}
// Cashdesk
// Cash register closing
// $conf->global->BANK_ENABLE_POS_CASHCONTROL must be set to 1 by all external POS modules
$moduleposenabled = (isModEnabled('cashdesk') || isModEnabled('takepos') || getDolGlobalString('BANK_ENABLE_POS_CASHCONTROL'));
if ($moduleposenabled) {
$this->trackedevents['CASHCONTROL_VALIDATE'] = 'logCASHCONTROL_VALIDATE';
if (!empty($this->trackedevents)) {
$sep++;
$this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("CashControl").'</span>', 'disabled' => 1);
}
$this->trackedevents['CASHCONTROL_VALIDATE'] = array('id' => 'CASHCONTROL_VALIDATE', 'label' => 'logCASHCONTROL_VALIDATE', 'labelhtml' => img_picto('', 'pos', 'class="pictofixedwidth").').$langs->trans('logCASHCONTROL_VALIDATE'));
}
// Add more action to track from a conf variable
// For example: STOCK_MOVEMENT,...
if (getDolGlobalString('BLOCKEDLOG_ADD_ACTIONS_SUPPORTED')) {
if (!empty($this->trackedevents)) {
$sep++;
$this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----------</span>', 'disabled' => 1);
}
$tmparrayofmoresupportedevents = explode(',', getDolGlobalString('BLOCKEDLOG_ADD_ACTIONS_SUPPORTED'));
foreach ($tmparrayofmoresupportedevents as $val) {
$this->trackedevents[$val] = 'log'.$val;
$this->trackedevents[$val] = array('id' => $val, 'label' => 'log'.$val, 'labelhtml' => img_picto('', 'generic', 'class="pictofixedwidth").').$langs->trans('log'.$val));
}
}
$this->trackedevents['BLOCKEDLOG_EXPORT'] = 'logBLOCKEDLOG_EXPORT';
if (!empty($this->trackedevents)) {
$sep++;
$this->trackedevents['separator_'.$sep] = array('id' => 'separator_'.$sep, 'label' => '----------', 'labelhtml' => '<span class="opacitymedium">----- '.$langs->trans("Other").'</span>', 'disabled' => 1);
}
$this->trackedevents['BLOCKEDLOG_EXPORT'] = array('id' => 'BLOCKEDLOG_EXPORT', 'label' => 'logBLOCKEDLOG_EXPORT', 'labelhtml' => img_picto('', $this->picto, 'class="pictofixedwidth").').$langs->trans('logBLOCKEDLOG_EXPORT'));
return 1;
}

View File

@@ -107,14 +107,14 @@ print '</span>';
// Define $nbmodulesnotautoenabled - TODO This code is at different places
$nbmodulesnotautoenabled = count($conf->modules);
$listofmodulesautoenabled = array('agenda', 'fckeditor', 'export', 'import');
$listofmodulesautoenabled = array('user', 'agenda', 'fckeditor', 'export', 'import');
foreach ($listofmodulesautoenabled as $moduleautoenable) {
if (in_array($moduleautoenable, $conf->modules)) {
$nbmodulesnotautoenabled--;
}
}
if ($user->admin && $nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
if ($user->admin && $nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
$langs->load("admin");
print info_admin($langs->trans("WarningOnlyCategoryTypesOfActivatedModules").' '.$langs->trans("YouCanEnableModulesFrom"));
}

View File

@@ -33,14 +33,6 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -48,6 +40,13 @@ require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
$MAXAGENDA = getDolGlobalString('AGENDA_EXT_NB', 5);
$DELAYFORCACHE = 300; // 300 seconds
@@ -184,6 +183,8 @@ if ($user->socid && $socid) {
$result = restrictedArea($user, 'societe', $socid);
}
require_once DOL_DOCUMENT_ROOT.'/core/redirect_if_setup_not_complete.inc.php';
/*
* Actions

View File

@@ -19,6 +19,7 @@
use Luracast\Restler\RestException;
require_once DOL_DOCUMENT_ROOT.'/comm/mailing/class/mailing.class.php';
require_once DOL_DOCUMENT_ROOT.'/comm/mailing/class/mailing_targets.class.php';
/**
* API class for mass mailings
@@ -39,11 +40,24 @@ class Mailings extends DolibarrApi
'body'
);
/**
* @var string[] Mandatory fields, checked when create and update object
*/
public static $TARGETFIELDS = array(
'fk_mailing',
'email'
);
/**
* @var Mailing {@type Mailing}
*/
public $mailing;
/**
* @var MailingTarget {@type MailingTarget}
*/
public $mailing_target;
/**
* Constructor
*/
@@ -52,6 +66,7 @@ class Mailings extends DolibarrApi
global $db;
$this->db = $db;
$this->mailing = new Mailing($this->db);
$this->mailing_target = new MailingTarget($this->db);
}
/**
@@ -125,7 +140,8 @@ class Mailings extends DolibarrApi
* @phan-return Mailing[]|array{data:Mailing[],pagination:array{total:int,page:int,page_count:int,limit:int}}
* @phpstan-return Mailing[]|array{data:Mailing[],pagination:array{total:int,page:int,page_count:int,limit:int}}
*
* @throws RestException
* @throws RestException 400
* @throws RestException 403
*/
public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $fk_projects = '', $sqlfilters = '', $properties = '', $pagination_data = false, $loadlinkedobjects = 0)
{
@@ -213,6 +229,114 @@ class Mailings extends DolibarrApi
return $obj_ret;
}
/**
* List mass mailing targets
*
* Get a list of mass mailing targets
*
* @since 23.0.0 Initial implementation
*
* @param int $id Mass mailing ID
* @param string $sortfield Sort field
* @param string $sortorder Sort order
* @param int $limit Limit for list
* @param int $page Page number
* @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.lastname:like:'John Doe') and (t.statut:=:3)"
* @param string $properties Restrict the data returned to these properties. Ignored if empty. Comma separated list of properties names
* @param bool $pagination_data If this parameter is set to true the response will include pagination data. Default value is false. Page starts from 0*
* @return array Array of order objects
* @phan-return Mailing[]|array{data:Mailing[],pagination:array{total:int,page:int,page_count:int,limit:int}}
* @phpstan-return Mailing[]|array{data:Mailing[],pagination:array{total:int,page:int,page_count:int,limit:int}}
*
* @url GET {id}/targets
*
* @throws RestException 400
* @throws RestException 403
* @throws RestException 404
*/
public function indexTargets($id, $sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $sqlfilters = '', $properties = '', $pagination_data = false)
{
if (!DolibarrApiAccess::$user->hasRight('mailing', 'read')) {
throw new RestException(403);
}
$fetchMailingResult = $this->mailing->fetch($id);
if ($fetchMailingResult < 0) {
throw new RestException(404, 'Mass mailing not found, id='.$id);
}
$fk_project = $this->mailing->fk_project;
if (!DolibarrApi::_checkAccessToResource('project', ((int) $fk_project))) {
throw new RestException(403, 'Access (project) not allowed for login '.DolibarrApiAccess::$user->login);
}
$obj_ret = array();
$sql = "SELECT t.rowid";
$sql .= " FROM ".MAIN_DB_PREFIX."mailing_cibles AS t";
$sql .= " WHERE t.fk_mailing = ".((int) $id);
// Add sql filters
if ($sqlfilters) {
$errormessage = '';
$sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
if ($errormessage) {
throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
}
}
//this query will return total mass mailing targets with the filters given
$sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
$sql .= $this->db->order($sortfield, $sortorder);
if ($limit) {
if ($page < 0) {
$page = 0;
}
$offset = $limit * $page;
$sql .= $this->db->plimit($limit + 1, $offset);
}
dol_syslog("API Rest request mass mailing target");
$result = $this->db->query($sql);
if ($result) {
$num = $this->db->num_rows($result);
$min = min($num, ($limit <= 0 ? $num : $limit));
$i = 0;
while ($i < $min) {
$obj = $this->db->fetch_object($result);
$mailing_target = new MailingTarget($this->db);
if ($mailing_target->fetch($obj->rowid) > 0) {
$obj_ret[] = $this->_filterObjectProperties($this->_cleanTargetDatas($mailing_target), $properties);
}
$i++;
}
} else {
throw new RestException(503, 'Error when retrieve list of mass mailing targetss : '.$this->db->lasterror());
}
//if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
if ($pagination_data) {
$totalsResult = $this->db->query($sqlTotals);
$total = $this->db->fetch_object($totalsResult)->total;
$tmp = $obj_ret;
$obj_ret = [];
$obj_ret['data'] = $tmp;
$obj_ret['pagination'] = [
'total' => (int) $total,
'page' => $page, //count starts from 0
'page_count' => ceil((int) $total / $limit),
'limit' => $limit
];
}
return $obj_ret;
}
/**
* Clone a mass mailing
*
@@ -264,7 +388,8 @@ class Mailings extends DolibarrApi
* @phpstan-param ?array<string,string> $request_data
* @return int ID of mass mailing
*
* @throws RestException
* @throws RestException 403
* @throws RestException 500 System error
*/
public function post($request_data = null)
{
@@ -307,7 +432,9 @@ class Mailings extends DolibarrApi
* @phpstan-param ?array<string,string> $request_data
* @return Object Object with cleaned properties
*
* @throws RestException
* @throws RestException 403
* @throws RestException 404
* @throws RestException 500 System error
*/
public function put($id, $request_data = null)
{
@@ -397,6 +524,254 @@ class Mailings extends DolibarrApi
);
}
/**
* Update a mass mailing general fields (won't change lines of mass mailing)
*
* @since 23.0.0 Initial implementation
*
* @param int $id Id of mass mailing with the targetid to update
* @param int $targetid Id mass mailing target to update
* @param array $request_data Datas
* @phan-param ?array<string,string> $request_data
* @phpstan-param ?array<string,string> $request_data
* @return Object Object with cleaned properties
*
* @url PUT {id}/updateTarget/{targetid}
*
* @throws RestException 403
* @throws RestException 404
* @throws RestException 500 System error
*/
public function updateTarget($id, $targetid, $request_data = null)
{
if (!DolibarrApiAccess::$user->hasRight('mailing', 'write')) {
throw new RestException(403);
}
$fetchMailingResult = $this->mailing->fetch($id);
if ($fetchMailingResult < 0) {
throw new RestException(404, 'Mass mailing not found, id='.$id);
}
$result = $this->mailing_target->fetch($targetid);
if ($result < 0) {
throw new RestException(404, 'Mass mailing target not found, id='.$targetid);
}
if ($id != $this->mailing_target->fk_mailing) {
throw new RestException(404, 'Target id='.$targetid.' is does not belong to mailing id='.$id);
}
if (!DolibarrApi::_checkAccessToResource('project', ((int) $this->mailing->fk_project))) {
throw new RestException(403, 'Access (project) not allowed for login '.DolibarrApiAccess::$user->login);
}
if (!DolibarrApi::_checkAccessToResource('mailing', $this->mailing->id)) {
throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
}
foreach ($request_data as $field => $value) {
if ($field == 'id') {
throw new RestException(400, 'Changing id field is forbidden');
}
if ($field == 'fk_mailing') {
throw new RestException(400, 'Changing fk_mailing field is forbidden to protect inserting a wrong fk_mailing number. Use a POST to create a new mailing target with the correct mailing id, then an PUT to update the new target in the right mailing id, and finally a delete to remove the old target');
}
if ($field === 'caller') {
// Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
$this->mailing_target->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
continue;
}
$this->mailing_target->$field = $this->_checkValForAPI($field, $value, $this->mailing_target);
}
if ($this->mailing_target->update(DolibarrApiAccess::$user) > 0) {
return $this->getTarget($id, $targetid);
} else {
throw new RestException(500, $this->mailing_target->error);
}
}
/**
* Create a mass mailing
*
* @since 23.0.0 Initial implementation
*
* @param int $id Id of mass mailing to create a target for
* @param array $request_data Request data
* @phan-param ?array<string,string> $request_data
* @phpstan-param ?array<string,string> $request_data
* @return int ID of mass mailing
*
* @url POST {id}/createTarget
*
* @throws RestException 400
* @throws RestException 403
* @throws RestException 404
* @throws RestException 500 System error
*/
public function postTarget($id, $request_data = null)
{
if (!DolibarrApiAccess::$user->hasRight('mailing', 'write')) {
throw new RestException(403, "Insufficiant rights");
}
// Check mandatory fields
$result = $this->_validateTarget($request_data);
$fk_mailing_id = 0;
foreach ($request_data as $field => $value) {
if ($field === 'caller') {
// Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
$this->mailing_target->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
continue;
}
if ($field === 'fk_project') {
if (!DolibarrApi::_checkAccessToResource('project', ((int) $value))) {
throw new RestException(403, 'Access (project) not allowed for login '.DolibarrApiAccess::$user->login);
}
}
if ($field == 'id') {
throw new RestException(400, 'Creating with id field is forbidden');
}
if ($field == 'fk_mailing') {
$fetchMailingResult = $this->mailing->fetch((int) $value);
if ($fetchMailingResult < 0) {
throw new RestException(404, 'Mass mailing not found, id='.((int) $value));
}
if (!DolibarrApi::_checkAccessToResource('project', ((int) $this->mailing->fk_project))) {
throw new RestException(403, 'Access (project) not allowed for login '.DolibarrApiAccess::$user->login);
}
$fk_mailing_id = ((int) $value);
}
$this->mailing_target->$field = $this->_checkValForAPI($field, $value, $this->mailing_target);
}
if (0 == $fk_mailing_id) {
throw new RestException(404, 'Mass mailing not found, id='.((int) $fk_mailing_id));
}
if ($this->mailing_target->create(DolibarrApiAccess::$user) < 0) {
throw new RestException(500, "Error creating mass mailing target", array_merge(array($this->mailing->error), $this->mailing->errors));
}
return ((int) $this->mailing_target->id);
}
/**
* Get a target in a mass mailing
*
* Return an array with info about a mass mailing target
*
* @since 23.0.0 Initial implementation
*
* @param int $id Id of mass mailing with the targetid to get
* @param int $targetid Id mass mailing target to get
* @return Object Object with cleaned properties
*
* @url GET {id}/getTarget/{targetid}
*
* @throws RestException
*/
public function getTarget($id, $targetid)
{
return $this->_fetchTarget($id, $targetid);
}
/**
* Get properties of an mailing object
*
* Return an array with mailing information
*
* @param int $id ID of mailing object
* @param int $targetid Id mass mailing target
* @return Object Object with cleaned properties
*
* @throws RestException 403
* @throws RestException 404
*/
private function _fetchTarget($id, $targetid)
{
if (!DolibarrApiAccess::$user->hasRight('mailing', 'read')) {
throw new RestException(403);
}
$fetchMailingResult = $this->mailing->fetch($id);
if ($fetchMailingResult < 0) {
throw new RestException(404, 'Mass mailing not found, id='.$id);
}
$result = $this->mailing_target->fetch($targetid);
if ($result < 0) {
throw new RestException(404, 'Mass mailing target not found, id='.$targetid);
}
if ($id != $this->mailing_target->fk_mailing) {
throw new RestException(404, 'Target id='.$targetid.' is does not belong to mailing id='.$id);
}
if (!DolibarrApi::_checkAccessToResource('project', ((int) $this->mailing->fk_project))) {
throw new RestException(403, 'Access (project) not allowed for login '.DolibarrApiAccess::$user->login);
}
if (!DolibarrApi::_checkAccessToResource('mailing', $this->mailing->id)) {
throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
}
return $this->_cleanTargetDatas($this->mailing_target);
}
/**
* Delete a mass mailing general fields (won't change lines of mass mailing)
*
* @since 23.0.0 Initial implementation
*
* @param int $id Id of mass mailing with the targetid to delete
* @param int $targetid Id mass mailing target to delete
* @return array
* @phan-return array{success:array{code:int,message:string}}
* @phpstan-return array{success:array{code:int,message:string}}
*
* @url DELETE {id}/deleteTarget/{targetid}
*
* @throws RestException 403
* @throws RestException 404
* @throws RestException 500 System error
*/
public function deleteTarget($id, $targetid)
{
if (!DolibarrApiAccess::$user->hasRight('mailing', 'delete')) {
throw new RestException(403);
}
$fetchMailingResult = $this->mailing->fetch($id);
if ($fetchMailingResult < 0) {
throw new RestException(404, 'Mass mailing not found, id='.$id);
}
$result = $this->mailing_target->fetch($targetid);
if ($result < 0) {
throw new RestException(404, 'Mass mailing target not found, id='.$targetid);
}
if ($id != $this->mailing_target->fk_mailing) {
throw new RestException(404, 'Target id='.$targetid.' is does not belong to mailing id='.$id);
}
if (!DolibarrApi::_checkAccessToResource('project', ((int) $this->mailing->fk_project))) {
throw new RestException(403, 'Access (project) not allowed for login '.DolibarrApiAccess::$user->login);
}
if (!DolibarrApi::_checkAccessToResource('mailing', $this->mailing->id)) {
throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
}
if (!$this->mailing_target->delete(DolibarrApiAccess::$user)) {
throw new RestException(500, 'Error when delete Mass mailing target: '.$this->mailing->error);
}
return array(
'success' => array(
'code' => 200,
'message' => 'Deleting target id='.$targetid.' belonging to mailing id='.$id
)
);
}
/**
* Delete targets of a mass mailing
*
@@ -612,6 +987,162 @@ class Mailings extends DolibarrApi
return $mailing;
}
/**
* Validate fields before create or update object
*
* @param ?array<string,string> $data Array with data to verify
* @return array<string,string>
*
* @throws RestException
*/
private function _validateTarget($data)
{
if ($data === null) {
$data = array();
}
$mailing_target = array();
foreach (Mailings::$TARGETFIELDS as $field) {
if (!isset($data[$field])) {
throw new RestException(400, "$field field missing");
}
$mailing_target[$field] = $data[$field];
}
return $mailing_target;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
/**
* Clean sensible object (mailing target) datas
*
* @param Object $object Object to clean
* @return Object Object with cleaned properties
*/
protected function _cleanTargetDatas($object)
{
// phpcs:enable
$object = parent::_cleanObjectDatas($object);
unset($object->TRIGGER_PREFIX);
unset($object->actionmsg);
unset($object->actionmsg2);
unset($object->actiontypecode);
unset($object->alreadypaid);
unset($object->array_options);
unset($object->array_languages);
unset($object->barcode_type_code);
unset($object->barcode_type_coder);
unset($object->barcode_type_label);
unset($object->barcode_type);
unset($object->canvas);
unset($object->civility_code);
unset($object->civility_id);
unset($object->comments);
unset($object->cond_reglement_id);
unset($object->cond_reglement_supplier_id);
unset($object->contact_id);
unset($object->contact);
unset($object->contacts_ids_internal);
unset($object->contacts_ids);
unset($object->context);
unset($object->country_code);
unset($object->country_id);
unset($object->country);
unset($object->date_cloture);
unset($object->date_creation);
unset($object->date_validation);
unset($object->db);
unset($object->demand_reason_id);
unset($object->deposit_percent);
unset($object->element_for_permission);
unset($object->element);
unset($object->entity);
unset($object->error);
unset($object->errorhidden);
unset($object->errors);
unset($object->extraparams);
unset($object->fields);
unset($object->fk_account);
unset($object->fk_bank);
unset($object->fk_delivery_address);
unset($object->fk_element);
unset($object->fk_multicurrency);
unset($object->fk_projet);
unset($object->fk_project);
unset($object->fk_user_creat);
unset($object->fk_user_modif);
unset($object->import_key);
unset($object->isextrafieldmanaged);
unset($object->ismultientitymanaged);
unset($object->last_main_doc);
unset($object->lines);
unset($object->linked_objects);
unset($object->linkedObjects);
unset($object->linkedObjectsIds);
unset($object->mode_reglement_id);
unset($object->model_pdf);
unset($object->module);
unset($object->multicurrency_code);
unset($object->multicurrency_total_ht);
unset($object->multicurrency_total_localtax1);
unset($object->multicurrency_total_localtax2);
unset($object->multicurrency_total_ttc);
unset($object->multicurrency_total_tva);
unset($object->multicurrency_tx);
unset($object->name);
unset($object->nb);
unset($object->nbphoto);
unset($object->newref);
unset($object->next_prev_filter);
unset($object->note);
unset($object->note_public);
unset($object->note_private);
unset($object->oldcopy);
unset($object->oldref);
unset($object->origin_id);
unset($object->origin_object);
unset($object->origin_type);
unset($object->origin);
unset($object->output);
unset($object->product);
unset($object->project);
unset($object->ref_ext);
unset($object->ref_next);
unset($object->ref_previous);
unset($object->ref);
unset($object->region_code);
unset($object->region_id);
unset($object->region);
unset($object->restrictiononfksoc);
unset($object->retained_warranty_fk_cond_reglement);
unset($object->sendtoid);
unset($object->shipping_method_id);
unset($object->shipping_method);
unset($object->showphoto_on_popup);
unset($object->specimen);
unset($object->state_code);
unset($object->state_id);
unset($object->state);
unset($object->table_element_line);
unset($object->table_element);
unset($object->thirdparty);
unset($object->total_ht);
unset($object->total_localtax1);
unset($object->total_localtax2);
unset($object->total_ttc);
unset($object->total_tva);
unset($object->totalpaid_multicurrency);
unset($object->totalpaid);
unset($object->tpl);
unset($object->transport_mode_id);
unset($object->user);
unset($object->user_creation_id);
unset($object->user_validation_id);
unset($object->user_closing_id);
unset($object->user_modification_id);
unset($object->warehouse_id);
return $object;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
/**
@@ -666,8 +1197,6 @@ class Mailings extends DolibarrApi
unset($object->import_key);
unset($object->isextrafieldmanaged);
unset($object->ismultientitymanaged);
unset($object->labelStatus);
unset($object->labelStatusShort);
unset($object->last_main_doc);
unset($object->lastname);
unset($object->lines);

View File

@@ -26,7 +26,7 @@
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
require_once DOL_DOCUMENT_ROOT.'/comm/mailing/class/mailing_targets.class.php';
/**
* Class to manage emailings module
@@ -174,6 +174,11 @@ class Mailing extends CommonObject
*/
public $substitutionarrayfortest;
/**
* @var MailingTarget[]
*/
public $targets = array();
/**
* @var ?int The related project ID
* @see setProject(), project
@@ -621,7 +626,7 @@ class Mailing extends CommonObject
$now = dol_now();
$sql = "UPDATE ".MAIN_DB_PREFIX."mailing ";
$sql .= " SET statut = 0, fk_user_modif=".$user->id;
$sql .= " SET statut = 0, tms = '".$this->db->idate($now)."', fk_user_modif=".$user->id;
$sql .= " WHERE rowid = ".((int) $this->id);
dol_syslog("Mailing::valid", LOG_DEBUG);

View File

@@ -0,0 +1,505 @@
<?php
/* Copyright (C) 2025 Cloned from htdocs/comm/mailing/class/mailing.class.php then modified
* Copyright (C) 2025 Jon Bendtsen <jon.bendtsen.github@jonb.dk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file htdocs/comm/mailing/class/mailing_target.class.php
* \ingroup mailing
* \brief File of class to manage emailing targets module
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
/**
* Class to manage emailings module
*/
class MailingTarget extends CommonObject
{
/**
* @var string ID to identify managed object
*/
public $element = 'mailing_target';
/**
* @var string Name of table without prefix where object is stored
*/
public $table_element = 'mailing_cibles';
/**
* @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
*/
public $picto = 'contact';
/**
* @var int Mailing id that this mailing_target is related to.
*/
public $fk_mailing;
/**
* @var int Contact id that this mailing_target is related to.
*/
public $fk_contact;
/**
* @var string lastname of the mailing_target
*/
public $lastname;
/**
* @var string firstname of the mailing_target
*/
public $firstname;
/**
* @var string email of the mailing_target
*/
public $email;
/**
* @var string other
*/
public $other;
/**
* @var string tag
*/
public $tag;
/**
* @var int status
* @deprecated Use $status
*/
public $statut; // Status 0=Not sent, 1=Sent, 2=Read, 3=Read and unsubscribed, -1=Error
/**
* @var int status
*/
public $status; // Status 0=Not sent, 1=Sent, 2=Read, 3=Read and unsubscribed, -1=Error
/**
* @var array<int,string> statut dest
*/
public $statut_dest = array();
/**
* @var string source_url of the mailing_target
*/
public $source_url;
/**
* @var int source_id of the mailing_target
*/
public $source_id;
/**
* @var string source_type
*/
public $source_type;
/**
* @var integer|''|null date sending
*/
public $date_envoi;
/**
* Update timestamp record (tms)
* @var integer
* @deprecated Use $date_modification
*/
public $tms;
/**
* @var string error_text from trying to send email
*/
public $error_text;
const STATUS_NOTSENT = 0;
const STATUS_SENT = 1;
const STATUS_READ = 2;
const STATUS_READANDUNSUBSCRIBED = 3;
const STATUS_ERROR = -1;
/**
* Constructor
*
* @param DoliDB $db Database handler
*/
public function __construct($db)
{
$this->db = $db;
// List of language codes for status
$this->labelStatus[0] = 'TargetStatusNotSent';
$this->labelStatus[1] = 'TargetStatusSent';
$this->labelStatus[2] = 'TargetStatusRead';
$this->labelStatus[3] = 'TargetStatusReadAndUnsubscribed';
$this->labelStatus[-1] = 'TargetStatusError';
$this->statut_dest[0] = 'TargetStatusNotSent';
$this->statut_dest[1] = 'TargetStatusSent';
$this->statut_dest[2] = 'TargetStatusRead';
$this->statut_dest[3] = 'TargetStatusReadAndUnsubscribed'; // Read but ask to not be contacted anymore
$this->statut_dest[-1] = 'TargetStatusError';
}
/**
* Create an Mailing Target
*
* @param User $user Object of user making creation
* @return int Return integer <0 if KO, Id of created object if OK
*/
public function create($user)
{
global $conf, $langs;
if (empty($this->fk_mailing)) {
$this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Mailing"));
return -2;
// we probably should also check that this number actually exists in ".MAIN_DB_PREFIX."mailing";
}
if (0 == $this->fk_mailing) {
$this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Mailing"));
return -4;
}
if (empty($this->email)) {
$this->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Email"));
return -3;
}
if (empty($this->statut)) {
$statut = 0;
}
if (empty($this->status)) {
$status = 0;
}
if ($this->status !== $this->statut) {
$this->error = 'Status='.$this->status.' and Statut='.$this->statut.' field must be identical';
return -4;
}
if (empty($this->fk_contact)) {
$fk_contact = 0;
}
$error = 0;
$this->db->begin();
// 2025-10-09 06:33:26 DEBUG 192.168.127.1 52 33 sql=INSERT INTO llx_mailing_cibles (fk_mailing, fk_contact, email, statut) VALUES ('4', .((int) 0)., 'jon@jonb.dk', .((int) )).
//2025-10-09 06:35:13 DEBUG 192.168.127.1 54 33 sql=INSERT INTO llx_mailing_cibles (fk_mailing, fk_contact, email, statut) VALUES (4, .((int) 0)., 'jon@jonb.dk', .((int) )).
$sql = "INSERT INTO ".MAIN_DB_PREFIX."mailing_cibles";
$sql .= " (fk_mailing, fk_contact, email, statut)";
$sql .= " VALUES (".((int) $this->fk_mailing).", ";
$sql .= ((int) $this->fk_contact).", ";
$sql .= "'".$this->db->escape($this->email)."', ";
$sql .= ((int) $conf->statut)." )";
dol_syslog(__METHOD__, LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."mailing_cibles");
$result = $this->update($user);
if ($result < 0) {
$error++;
}
if (!$error) {
$this->db->commit();
return $this->id;
} else {
$this->db->rollback();
dol_syslog(__METHOD__ . ' ' . $this->error, LOG_ERR);
return -2;
}
} else {
$this->error = $this->db->lasterror();
$this->db->rollback();
return -1;
}
}
/**
* Delete Mailing target
*
* @param User $user User that delete
* @return int >0 if OK, <0 if KO
*/
public function delete($user)
{
$error = 0;
$this->db->begin();
$sql = "DELETE FROM ".MAIN_DB_PREFIX."mailing_cibles";
$sql .= " WHERE rowid = " . ((int) $this->id);
dol_syslog(__METHOD__, LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
dol_syslog(__METHOD__ . ' success');
$this->db->commit();
return 1;
} else {
$this->db->rollback();
$this->error = $this->db->lasterror();
return -1;
}
}
/**
* Set notsent mailing target
*
* @return int Return integer <0 if KO, >0 if OK
*/
public function setNotSent()
{
$now = dol_now();
$sql = "UPDATE ".MAIN_DB_PREFIX."mailing_cibles ";
$sql .= " SET statut = ".((int) self::STATUS_NOTSENT).", tms = '".$this->db->idate($now)."'";
$sql .= " WHERE rowid = ".((int) $this->id);
dol_syslog("Mailing::valid", LOG_DEBUG);
if ($this->db->query($sql)) {
return 1;
} else {
$this->error = $this->db->lasterror();
return -1;
}
}
/**
* Set sent mailing target
*
* @return int Return integer <0 if KO, >0 if OK
*/
public function setSent()
{
$now = dol_now();
$sql = "UPDATE ".MAIN_DB_PREFIX."mailing_cibles ";
$sql .= " SET statut = ".((int) self::STATUS_SENT).", tms = '".$this->db->idate($now)."'";
$sql .= " WHERE rowid = ".((int) $this->id);
dol_syslog("Mailing::valid", LOG_DEBUG);
if ($this->db->query($sql)) {
return 1;
} else {
$this->error = $this->db->lasterror();
return -1;
}
}
/**
* Set read mailing target
*
* @return int Return integer <0 if KO, >0 if OK
*/
public function setRead()
{
$now = dol_now();
$sql = "UPDATE ".MAIN_DB_PREFIX."mailing_cibles ";
$sql .= " SET statut = ".((int) self::STATUS_READ).", tms = '".$this->db->idate($now)."'";
$sql .= " WHERE rowid = ".((int) $this->id);
dol_syslog("Mailing::valid", LOG_DEBUG);
if ($this->db->query($sql)) {
return 1;
} else {
$this->error = $this->db->lasterror();
return -1;
}
}
/**
* Set read and unsubscribed mailing target
*
* @return int Return integer <0 if KO, >0 if OK
*/
public function setReadAndUnsubscribed()
{
$now = dol_now();
$sql = "UPDATE ".MAIN_DB_PREFIX."mailing_cibles ";
$sql .= " SET statut = ".((int) self::STATUS_READANDUNSUBSCRIBED).", tms = '".$this->db->idate($now)."'";
$sql .= " WHERE rowid = ".((int) $this->id);
dol_syslog("Mailing::valid", LOG_DEBUG);
if ($this->db->query($sql)) {
return 1;
} else {
$this->error = $this->db->lasterror();
return -1;
}
}
/**
* Set error mailing target
*
* @return int Return integer <0 if KO, >0 if OK
*/
public function setError()
{
$now = dol_now();
$sql = "UPDATE ".MAIN_DB_PREFIX."mailing_cibles ";
$sql .= " SET statut = ".((int) self::STATUS_ERROR).", tms = '".$this->db->idate($now)."'";
$sql .= " WHERE rowid = ".((int) $this->id);
dol_syslog("Mailing::valid", LOG_DEBUG);
if ($this->db->query($sql)) {
return 1;
} else {
$this->error = $this->db->lasterror();
return -1;
}
}
/**
* Update an Mailing Target
*
* @param User $user Object of user making change
* @return int Return integer < 0 if KO, > 0 if OK
*/
public function update($user)
{
global $langs;
if (empty($this->fk_mailing)) {
return -2;
// we probably should also check that this number actually exists in ".MAIN_DB_PREFIX."mailing";
}
if (empty($this->email)) {
return -3;
}
if (empty($this->statut)) {
$statut = 0;
}
if (empty($this->status)) {
$status = 0;
}
if ($this->status !== $this->statut) {
return -4;
}
if (empty($this->fk_contact)) {
$fk_contact = 0;
}
$now = dol_now();
$error = 0;
$this->db->begin();
$sql = "UPDATE ".MAIN_DB_PREFIX."mailing_cibles";
$sql .= " SET fk_mailing = '".((int) $this->fk_mailing)."'";
$sql .= ", fk_contact = '".((int) $this->fk_contact)."'";
$sql .= ", lastname = '".$this->db->escape($this->lastname)."'";
$sql .= ", firstname = '".$this->db->escape($this->firstname)."'";
$sql .= ", email = '".$this->db->escape($this->email)."'";
$sql .= ", other = '".$this->db->escape($this->other)."'";
$sql .= ", tag = '".$this->db->escape($this->tag)."'";
$sql .= ", statut = '".((int) $this->statut)."'";
$sql .= ", source_url = '".$this->db->escape($this->source_url)."'";
$sql .= ", source_id = '".((int) $this->source_id)."'";
$sql .= ", source_type = '".$this->db->escape($this->source_type)."'";
if ($this->date_envoi) {
$sql .= ", date_envoi = '".$this->db->idate($this->date_envoi)."'";
}
$sql .= ", error_text = '".($this->error_text ? $this->db->escape($this->error_text) : null)."'";
$sql .= " WHERE rowid = ".(int) $this->id;
dol_syslog(__METHOD__, LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
dol_syslog(__METHOD__ . ' success');
$this->db->commit();
return 1;
} else {
if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
$this->error = $langs->trans("ErrorRecordAlreadyExists", $this->email);
} else {
$this->error = $this->db->lasterror();
}
$this->db->rollback();
return -6;
}
}
/**
* Get object from database
*
* @param int $rowid Id of Mailing Target
* @return int Return integer <0 if KO, >0 if OK
*/
public function fetch($rowid)
{
$sql = "SELECT t.rowid";
$sql .= ", t.fk_mailing";
$sql .= ", t.fk_contact";
$sql .= ", t.lastname";
$sql .= ", t.firstname";
$sql .= ", t.email";
$sql .= ", t.other";
$sql .= ", t.tag";
$sql .= ", t.statut as status";
$sql .= ", t.source_url";
$sql .= ", t.source_id";
$sql .= ", t.source_type";
$sql .= ", t.date_envoi";
$sql .= ", t.tms as date_modification";
$sql .= ", t.error_text";
$sql .= " FROM ".MAIN_DB_PREFIX."mailing_cibles as t";
$sql .= " WHERE t.rowid = ".(int) $rowid;
dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
$result = $this->db->query($sql);
if ($result) {
if ($this->db->num_rows($result)) {
$obj = $this->db->fetch_object($result);
$this->id = $obj->rowid;
$this->fk_mailing = $obj->fk_mailing;
$this->fk_contact = $obj->fk_contact;
$this->lastname = $obj->lastname;
$this->firstname = $obj->firstname;
$this->email = $obj->email;
$this->other = $obj->other;
$this->tag = $obj->tag;
$this->statut = $obj->status; // deprecated
$this->status = $obj->status;
$this->source_url = $obj->source_url;
$this->source_id = $obj->source_id;
$this->source_type = $obj->source_type;
$this->date_envoi = $this->db->jdate($obj->date_envoi);
$this->date_modification = $this->db->jdate($obj->date_modification); // tms
$this->tms = $this->db->jdate($obj->date_modification); // tms
$this->error_text = $obj->error_text;
return 1;
} else {
dol_syslog(get_class($this)."::fetch Error -1");
return -1;
}
} else {
dol_syslog(get_class($this)."::fetch Error -2");
return -2;
}
}
}

View File

@@ -1676,10 +1676,10 @@ class Contrat extends CommonObject
}
/**
* Mets a jour une ligne de contrat
* Update a contract line
*
* @param int $rowid Id de la ligne de facture
* @param string $desc Description de la ligne
* @param int $rowid Id of contract line
* @param string $desc Description of line
* @param float $pu Prix unitaire
* @param float $qty Quantite
* @param float $remise_percent Percentage discount of the line
@@ -1701,7 +1701,7 @@ class Contrat extends CommonObject
*/
public function updateline($rowid, $desc, $pu, $qty, $remise_percent, $date_start, $date_end, $tvatx, $localtax1tx = 0.0, $localtax2tx = 0.0, $date_start_real = '', $date_end_real = '', $price_base_type = 'HT', $info_bits = 0, $fk_fournprice = null, $pa_ht = 0, $array_options = array(), $fk_unit = null, $rang = 0)
{
global $user, $conf, $langs, $mysoc;
global $user, $langs, $mysoc;
$error = 0;
@@ -1822,6 +1822,17 @@ class Contrat extends CommonObject
}
}
// Update column llx_contrat.denormalized_lower_panned_end_date with next expiration date of an open contract
$sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat as c";
$sqltoupdatecontract .= " SET c.denormalized_lower_planned_end_date = (SELECT MIN(date_fin_validite) FROM ".MAIN_DB_PREFIX."contratdet as cd WHERE cd.fk_contrat = ".((int) $this->id)." AND cd.statut = ".ContratLigne::STATUS_OPEN.")";
$sqltoupdatecontract .= " WHERE c.rowid = ".((int) $this->id);
$resqltoupdatecontract = $this->db->query($sqltoupdatecontract);
if (!$resqltoupdatecontract) {
$this->error = $this->db->lasterror();
$this->db->rollback();
return -1;
}
if (empty($error)) {
// Call trigger
$this->context['line_id'] = $rowid;
@@ -2761,7 +2772,6 @@ class Contrat extends CommonObject
// Load contract
$object = new Contrat($this->db);
$object->fetch($obj->rowid); // fetch also lines
//$object->fetch_thirdparty();
if ($object->id <= 0) {
$error++;
@@ -2830,6 +2840,17 @@ class Contrat extends CommonObject
if ($resqlupdate) {
$contractlineprocessed[$obj->lid] = $object->ref;
// Update column llx_contrat.denormalized_lower_panned_end_date with next expiration date of an open contract
$sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat as c";
$sqltoupdatecontract .= " SET c.denormalized_lower_planned_end_date = (SELECT MIN(date_fin_validite) FROM ".MAIN_DB_PREFIX."contratdet as cd WHERE cd.fk_contrat = ".((int) $object->id)." AND cd.statut = ".ContratLigne::STATUS_OPEN.")";
$sqltoupdatecontract .= " WHERE c.rowid = ".((int) $object->id);
$resqltoupdatecontract = $this->db->query($sqltoupdatecontract);
if (!$resqltoupdatecontract) {
$this->error = $this->db->lasterror();
$this->db->rollback();
return -1;
}
$actioncode = 'RENEW_CONTRACT';
$now = dol_now();

View File

@@ -540,11 +540,6 @@ class ContratLigne extends CommonObjectLine
$this->date_start_real = $this->db->jdate($obj->date_start_real);
$this->date_end = $this->db->jdate($obj->date_end);
$this->date_end_real = $this->db->jdate($obj->date_end_real);
// For backward compatibility
//$this->date_ouverture_prevue = $this->db->jdate($obj->date_ouverture_prevue);
//$this->date_ouverture = $this->db->jdate($obj->date_ouverture);
//$this->date_fin_validite = $this->db->jdate($obj->date_fin_validite);
//$this->date_cloture = $this->db->jdate($obj->date_cloture);
$this->tva_tx = $obj->tva_tx;
$this->vat_src_code = $obj->vat_src_code;
@@ -920,13 +915,14 @@ class ContratLigne extends CommonObjectLine
$this->db->begin();
$this->statut = ContratLigne::STATUS_OPEN;
$this->status = ContratLigne::STATUS_OPEN;
$this->date_start_real = $date;
$this->date_end = $date_end;
$this->fk_user_ouverture = $user->id;
$this->date_end_real = null;
$this->commentaire = $comment;
$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".((int) $this->statut).",";
$sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".((int) $this->status).",";
$sql .= " date_ouverture = ".(dol_strlen((string) $this->date_start_real) != 0 ? "'".$this->db->idate($this->date_start_real)."'" : "null").",";
if ($date_end >= 0) {
$sql .= " date_fin_validite = ".(dol_strlen($this->date_end) != 0 ? "'".$this->db->idate($this->date_end)."'" : "null").",";
@@ -939,6 +935,19 @@ class ContratLigne extends CommonObjectLine
dol_syslog(get_class($this)."::active_line", LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql) {
if ($date_end >= 0) {
// Update column llx_contrat.denormalized_lower_panned_end_date with next expiration date of an open contract
$sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat as c";
$sqltoupdatecontract .= " SET c.denormalized_lower_planned_end_date = (SELECT MIN(date_fin_validite) FROM ".MAIN_DB_PREFIX."contratdet as cd WHERE cd.fk_contrat = ".((int) $this->fk_contrat)." AND cd.statut = ".ContratLigne::STATUS_OPEN.")";
$sqltoupdatecontract .= " WHERE c.rowid = ".((int) $this->fk_contrat);
$resqltoupdatecontract = $this->db->query($sqltoupdatecontract);
if (!$resqltoupdatecontract) {
$this->error = $this->db->lasterror();
$this->db->rollback();
return -1;
}
}
// Call trigger
$result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user);
if ($result < 0) {
@@ -992,6 +1001,17 @@ class ContratLigne extends CommonObjectLine
$resql = $this->db->query($sql);
if ($resql) {
// Update column llx_contrat.denormalized_lower_panned_end_date with next expiration date of an open contract
$sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat as c";
$sqltoupdatecontract .= " SET c.denormalized_lower_planned_end_date = (SELECT MIN(date_fin_validite) FROM ".MAIN_DB_PREFIX."contratdet as cd WHERE cd.fk_contrat = ".((int) $this->fk_contrat)." AND cd.statut = ".ContratLigne::STATUS_OPEN.")";
$sqltoupdatecontract .= " WHERE c.rowid = ".((int) $this->fk_contrat);
$resqltoupdatecontract = $this->db->query($sqltoupdatecontract);
if (!$resqltoupdatecontract) {
$this->error = $this->db->lasterror();
$this->db->rollback();
return -1;
}
if (!$notrigger) {
// Call trigger
$result = $this->call_trigger('LINECONTRACT_CLOSE', $user);

View File

@@ -35,6 +35,13 @@
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/contrat/class/contrat.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
@@ -45,14 +52,6 @@ if (isModEnabled("category")) {
require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
}
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
// Load translation files required by the page
$langs->loadLangs(array('contracts', 'products', 'companies', 'compta'));
@@ -324,7 +323,7 @@ $sql .= " MIN(".$db->ifsql("cd.statut=4", "cd.date_fin_validite", "null").") as
$sql .= " SUM(".$db->ifsql("cd.statut=0", '1', '0').') as nb_initial,';
$sql .= " SUM(".$db->ifsql("cd.statut=4 AND (cd.date_fin_validite IS NULL OR cd.date_fin_validite >= '".$db->idate($now)."')", '1', '0').') as nb_running,';
$sql .= " SUM(".$db->ifsql("cd.statut=4 AND (cd.date_fin_validite IS NOT NULL AND cd.date_fin_validite < '".$db->idate($now)."')", '1', '0').') as nb_expired,';
$sql .= " SUM(".$db->ifsql("cd.statut=4 AND (cd.date_fin_validite IS NOT NULL AND cd.date_fin_validite < '".$db->idate($now - $conf->contrat->services->expires->warning_delay)."')", '1', '0').') as nb_late,';
$sql .= " SUM(".$db->ifsql("cd.statut=4 AND (cd.date_fin_validite IS NOT NULL AND cd.date_fin_validite < '".$db->idate($now - $conf->contract->services->expires->warning_delay)."')", '1', '0').') as nb_late,';
$sql .= " SUM(".$db->ifsql("cd.statut=5", '1', '0').') as nb_closed';
// Add fields from extrafields
if (!empty($extrafields->attributes[$object->table_element]['label'])) {

View File

@@ -8,6 +8,7 @@
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2019-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025 Joachim Kueter <git-jk@bloxera.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -626,7 +627,9 @@ class CMailFile
if (!empty($this->atleastonefile)) {
foreach ($filename_list as $i => $val) {
$content = file_get_contents($filename_list[$i]);
$smtps->setAttachment($content, $mimefilename_list[$i], $mimetype_list[$i], (empty($cid_list[$i]) ? '' : $cid_list[$i]));
if (empty($cid_list[$i])) {
$smtps->setAttachment($content, $mimefilename_list[$i], $mimetype_list[$i], (empty($cid_list[$i]) ? '' : $cid_list[$i]));
}
}
}
@@ -1869,15 +1872,18 @@ class CMailFile
$mimetype_list[$i] = "application/octet-stream";
}
// Skip files that have a CID (they will be added as inline images instead)
// @phpstan-ignore-next-line
if (!empty($cidlist) && is_array($cidlist) && isset($cidlist[$i]) && $cidlist[$i] !== null && $cidlist[$i] !== '') {
continue; // Skip this file as it will be processed as inline image
}
$out .= "--".$this->mixed_boundary.$this->eol;
$out .= "Content-Disposition: attachment; filename=\"".$filename_list[$i]."\"".$this->eol;
$out .= "Content-Type: ".$mimetype_list[$i]."; name=\"".$filename_list[$i]."\"".$this->eol;
$out .= "Content-Transfer-Encoding: base64".$this->eol;
$out .= "Content-Description: ".$filename_list[$i].$this->eol;
if (!empty($cidlist) && is_array($cidlist) && $cidlist[$i]) {
$out .= "X-Attachment-Id: ".$cidlist[$i].$this->eol;
$out .= "Content-ID: <".$cidlist[$i].'>'.$this->eol;
}
$out .= $this->eol;
$out .= $encoded;
$out .= $this->eol;
@@ -2209,6 +2215,22 @@ class CMailFile
$i++;
}
// Also add cidfromdata images to images_encoded array so they are sent as inline images
foreach ($this->html_images as $img) {
if ($img['type'] == 'cidfromdata') {
$image_content = file_get_contents($img['fullpath']);
if ($image_content !== false) {
$idx = count($this->images_encoded);
$this->images_encoded[$idx]['name'] = $img['name'];
$this->images_encoded[$idx]['fullpath'] = $img['fullpath'];
$this->images_encoded[$idx]['content_type'] = $img['content_type'];
$this->images_encoded[$idx]['cid'] = $img['cid'];
$this->images_encoded[$idx]['type'] = 'cidfromdata';
$this->images_encoded[$idx]['image_encoded'] = chunk_split(base64_encode($image_content), 68, $this->eol);
}
}
}
return 1;
} else {
return 0;

View File

@@ -1062,7 +1062,7 @@ abstract class CommonInvoice extends CommonObject
$statusdispute = $moreparams['dispute_status'] ? img_picto($langs->trans("DisputeOpen"), 'warning') : '';
}
*/
if ($moreparams['dispute_status']) {
if (isset($moreparams['dispute_status']) && $moreparams['dispute_status']) {
$labelStatus .= ' - '.$langs->trans("DisputeOpen");
$statusType = 'status8';
}

View File

@@ -24,6 +24,8 @@
* \brief File of the superclass of classes of lines of business objects (invoice, contract, proposal, orders, etc. ...)
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
/**
* Parent class for class inheritance lines of business objects

View File

@@ -9800,11 +9800,13 @@ class Form
$tmpcolor = '';
$tmppicto = '';
$tmplabelhtml = '';
$tmpdisabled = '';
if (is_array($value) && array_key_exists('id', $value) && array_key_exists('label', $value)) {
$tmpkey = $value['id'];
$tmpvalue = empty($value['label']) ? '' : $value['label'];
$tmpcolor = empty($value['color']) ? '' : $value['color'];
$tmppicto = empty($value['picto']) ? '' : $value['picto'];
$tmpdisabled = empty($value['disabled']) ? '' : $value['disabled'];
$tmplabelhtml = empty($value['labelhtml']) ? (empty($value['data-html']) ? '' : $value['data-html']) : $value['labelhtml'];
}
$newval = ($translate ? $langs->trans($tmpvalue) : $tmpvalue);
@@ -9814,6 +9816,9 @@ class Form
if (is_array($selected) && !empty($selected) && in_array((string) $tmpkey, $selected) && ((string) $tmpkey != '')) {
$out .= ' selected';
}
if ($tmpdisabled) {
$out .= ' disabled="disabled"';
}
if (!empty($tmplabelhtml)) {
$out .= ' data-html="' . dol_escape_htmltag($tmplabelhtml, 0, 0, '', 0, 1) . '"';
} else {

View File

@@ -776,7 +776,7 @@ function modules_prepare_head($nbofactivatedmodules, $nboftotalmodules, $nbmodul
$head = array();
$mode = getDolGlobalString('MAIN_MODULE_SETUP_ON_LIST_BY_DEFAULT', 'commonkanban');
$head[$h][0] = DOL_URL_ROOT."/admin/modules.php?mode=".$mode;
if ($nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled)
if ($nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled)
//$head[$h][1] = $form->textwithpicto($langs->trans("AvailableModules"), $desc);
$head[$h][1] = $langs->trans("AvailableModules");
$head[$h][1] .= $form->textwithpicto('', $langs->trans("YouMustEnableOneModule").'.<br><br><span class="opacitymedium">'.$desc.'</span>', 1, 'warning');

View File

@@ -5437,7 +5437,13 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srco
$pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
// Fix some values of $pictowithouttext
$pictoconvertkey = array('facture' => 'bill', 'shipping' => 'shipment', 'fichinter' => 'intervention', 'agenda' => 'calendar', 'invoice_supplier' => 'supplier_invoice', 'order_supplier' => 'supplier_order');
$pictoconvertkey = array(
'facture' => 'bill',
'shipping' => 'shipment',
'fichinter' => 'intervention',
'agenda' => 'calendar',
'invoice_supplier' => 'supplier_invoice',
'order_supplier' => 'supplier_order');
if (in_array($pictowithouttext, array_keys($pictoconvertkey))) {
$pictowithouttext = $pictoconvertkey[$pictowithouttext];
}
@@ -5489,7 +5495,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srco
return $enabledisablehtml;
}
if (empty($srconly) && in_array($pictowithouttext, getImgPictoNameList())) {
if (empty($srconly) && !preg_match('/[\.\/@]/', $pictowithouttext)) { // If picto code does not contains a / and no . inside, it is not a path to an image file on disk
$fakey = $pictowithouttext;
$facolor = '';
$fasize = '';
@@ -5796,309 +5802,10 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srco
return '<img src="' . $fullpathpicto . '"' . ($notitle ? '' : ' alt="' . dolPrintHTMLForAttribute($alt, 0, $allowothertags) . '"') . (($notitle || empty($titlealt)) ? '' : ' title="' . dolPrintHTMLForAttribute($titlealt, 0, $allowothertags) . '"') . ($moreatt ? ' ' . $moreatt . ($morecss ? ' class="' . $morecss . '"' : '') : ' class="inline-block' . ($morecss ? ' ' . $morecss : '') . '"') . '>'; // Alt is used for accessibility, title for popup
}
/**
* Get all usage icon key usable for img_picto(..., key)
*
* @return string[]
*/
function getImgPictoNameList()
{
return array(
'1downarrow',
'1uparrow',
'1leftarrow',
'1rightarrow',
'1uparrow_selected',
'1downarrow_selected',
'1leftarrow_selected',
'1rightarrow_selected',
'accountancy',
'accounting_account',
'account',
'accountline',
'action',
'add',
'address',
'ai',
'angle-double-down',
'angle-double-up',
'asset',
'back',
'bank_account',
'barcode',
'bank',
'bell',
'bill',
'billa',
'billr',
'billd',
'birthday-cake',
'bom',
'bookcal',
'bookmark',
'briefcase-medical',
'bug',
'building',
'card',
'calendarlist',
'calendar',
'calendarmonth',
'calendarweek',
'calendarday',
'calendarperuser',
'calendarpertype',
'hourglass',
'cash-register',
'category',
'chart',
'check',
'clock',
'clone',
'close_title',
'code',
'cog',
'collab',
'company',
'contact',
'country',
'contract',
'conversation',
'cron',
'cross',
'cubes',
'check-circle',
'check-square',
'circle',
'stop-circle',
'currency',
'multicurrency',
'chevron-left',
'chevron-right',
'chevron-down',
'chevron-up',
'chevron-double-left',
'chevron-double-right',
'chevron-double-down',
'chevron-double-top',
'commercial',
'companies',
'delete',
'dolly',
'dollyrevert',
'donation',
'download',
'dynamicprice',
'edit',
'ellipsis-h',
'email',
'entity',
'envelope',
'eraser',
'establishment',
'expensereport',
'external-link-alt',
'external-link-square-alt',
'eye',
'filter',
'file',
'file-o',
'file-code',
'file-export',
'file-import',
'file-upload',
'autofill',
'folder',
'folder-open',
'folder-plus',
'font',
'generate',
'generic',
'globe',
'globe-americas',
'graph',
'grip',
'grip_title',
'group',
'hands-helping',
'help',
'holiday',
'id-card',
'images',
'incoterm',
'info',
'info_black',
'intervention',
'inventory',
'intracommreport',
'jobprofile',
'key',
'knowledgemanagement',
'label',
'language',
'layout',
'line',
'link',
'list',
'list-alt',
'listlight',
'loan',
'lock',
'lot',
'long-arrow-alt-right',
'margin',
'map-marker-alt',
'member',
'meeting',
'minus',
'money-bill-alt',
'movement',
'mrp',
'note',
'next',
'off',
'on',
'order',
'paragraph',
'play',
'pdf',
'phone',
'phoning',
'phoning_mobile',
'phoning_fax',
'playdisabled',
'previous',
'poll',
'pos',
'printer',
'product',
'propal',
'proposal',
'puce',
'resize',
'search',
'service',
'stats',
'stock',
'security',
'setup',
'share-alt',
'sign-out',
'split',
'stripe',
'stripe-s',
'switch_off',
'switch_on',
'switch_on_grey',
'switch_on_warning',
'switch_on_red',
'tools',
'unlink',
'uparrow',
'user',
'user-tie',
'vcard',
'wrench',
'discord',
'facebook',
'flickr',
'instagram',
'linkedin',
'github',
'google',
'jabber',
'meetup',
'microsoft',
'skype',
'slack',
'twitter',
'pinterest',
'reddit',
'snapchat',
'tumblr',
'youtube',
'viadeo',
'google-plus-g',
'whatsapp',
'generic',
'home',
'hrm',
'members',
'products',
'invoicing',
'partnership',
'payment',
'payment_vat',
'pencil-ruler',
'pictoconfirm',
'preview',
'project',
'projectpub',
'projecttask',
'question',
'refresh',
'region',
'salary',
'shipment',
'state',
'supplier_invoice',
'supplier_invoicea',
'supplier_invoicer',
'supplier_invoiced',
'technic',
'ticket',
'error',
'warning',
'recent',
'reception',
'recruitmentcandidature',
'recruitmentjobposition',
'replacement',
'resource',
'recurring',
'rss',
'search-plus',
'shapes',
'skill',
'square',
'sort-numeric-down',
'status',
'stop-circle',
'supplier',
'supplier_proposal',
'supplier_order',
'supplier_invoice',
'terminal',
'tick',
'timespent',
'title_setup',
'title_accountancy',
'title_bank',
'title_hrm',
'title_agenda',
'trip',
'uncheck',
'undo',
'url',
'user-cog',
'user-injured',
'user-md',
'upload',
'vat',
'website',
'workstation',
'webhook',
'world',
'private',
'conferenceorbooth',
'eventorganization',
'stamp',
'signature',
'webportal'
);
}
/**
* Get array to convert the Dolibarr picto keys into font awesome keys
* Get array to convert the Dolibarr picto keys into Font awesome keys
*
* @param string $mode 'fa' to get conversion array for Font-Awesome
* @return string[] Array of conversion
@@ -6108,192 +5815,197 @@ function getImgPictoConv($mode = 'fa')
{
global $conf;
// Array when the fa picto key is different than the Dolibarr picto key.
$arrayconvpictotofa = array(
'account' => 'university',
'accounting_account' => 'clipboard-list',
'accountline' => 'receipt',
'accountancy' => 'search-dollar',
'action' => 'calendar-alt',
'add' => 'plus-circle',
'address' => 'address-book',
'ai' => 'magic',
'asset' => 'money-check-alt',
'autofill' => 'fill',
'back' => 'arrow-left',
'bank_account' => 'university',
'bill' => 'file-invoice-dollar',
'billa' => 'file-excel',
'billr' => 'file-invoice-dollar',
'billd' => 'file-medical',
'bookcal' => 'calendar-check',
'supplier_invoice' => 'file-invoice-dollar',
'supplier_invoicea' => 'file-excel',
'supplier_invoicer' => 'file-invoice-dollar',
'supplier_invoiced' => 'file-medical',
'bom' => 'shapes',
'card' => 'address-card',
'chart' => 'chart-line',
'company' => 'building',
'contact' => 'address-book',
'contract' => 'suitcase',
'collab' => 'people-arrows',
'conversation' => 'comments',
'country' => 'globe-americas',
'cron' => 'business-time',
'cross' => 'times',
'chevron-double-left' => 'angle-double-left',
'chevron-double-right' => 'angle-double-right',
'chevron-double-down' => 'angle-double-down',
'chevron-double-top' => 'angle-double-up',
'donation' => 'file-alt',
'dynamicprice' => 'hand-holding-usd',
'setup' => 'cog',
'companies' => 'building',
'products' => 'cube',
'commercial' => 'suitcase',
'invoicing' => 'coins',
'accounting' => 'search-dollar',
'category' => 'tag',
'dollyrevert' => 'dolly',
'file-o' => 'file',
'generate' => 'plus-square',
'hrm' => 'user-tie',
'incoterm' => 'truck-loading',
'margin' => 'calculator',
'members' => 'user-friends',
'ticket' => 'ticket-alt',
'globe' => 'external-link-alt',
'lot' => 'barcode',
'email' => 'at',
'establishment' => 'building',
'edit' => 'pencil-alt',
'entity' => 'globe',
'graph' => 'chart-line',
'grip_title' => 'arrows-alt',
'grip' => 'arrows-alt',
'help' => 'question-circle',
'generic' => 'file',
'holiday' => 'umbrella-beach',
'info' => 'info-circle',
'info_black' => 'info-circle',
'inventory' => 'boxes',
'intracommreport' => 'globe-europe',
'jobprofile' => 'cogs',
'knowledgemanagement' => 'ticket-alt',
'label' => 'layer-group',
'layout' => 'columns',
'line' => 'bars',
'loan' => 'money-bill-alt',
'member' => 'user-alt',
'meeting' => 'chalkboard-teacher',
'mrp' => 'cubes',
'next' => 'arrow-alt-circle-right',
'trip' => 'wallet',
'expensereport' => 'wallet',
'group' => 'users',
'movement' => 'people-carry',
'sign-out' => 'sign-out-alt',
'switch_off' => 'toggle-off',
'switch_on' => 'toggle-on',
'switch_on_grey' => 'toggle-on',
'switch_on_warning' => 'toggle-on',
'switch_on_red' => 'toggle-on',
'check' => 'check',
'bookmark' => 'star',
'bank' => 'university',
'close_title' => 'times',
'delete' => 'trash',
'filter' => 'filter',
'list-alt' => 'list-alt',
'calendarlist' => 'bars',
'calendar' => 'calendar-alt',
'calendarmonth' => 'calendar-alt',
'calendarweek' => 'calendar-week',
'calendarday' => 'calendar-day',
'calendarperuser' => 'table',
'calendarpertype' => 'table',
'intervention' => 'ambulance',
'invoice' => 'file-invoice-dollar',
'order' => 'file-invoice',
'error' => 'exclamation-triangle',
'warning' => 'exclamation-triangle',
'other' => 'square',
'playdisabled' => 'play',
'pdf' => 'file-pdf',
'poll' => 'check-double',
'pos' => 'cash-register',
'preview' => 'binoculars',
'project' => 'project-diagram',
'projectpub' => 'project-diagram',
'projecttask' => 'tasks',
'propal' => 'file-signature',
'proposal' => 'file-signature',
'partnership' => 'handshake',
'payment' => 'money-check-alt',
'payment_vat' => 'money-check-alt',
'pictoconfirm' => 'check-square',
'phoning' => 'phone',
'phoning_mobile' => 'mobile-alt',
'phoning_fax' => 'fax',
'previous' => 'arrow-alt-circle-left',
'printer' => 'print',
'product' => 'cube',
'puce' => 'angle-right',
'recent' => 'check-square',
'reception' => 'dolly',
'recruitmentjobposition' => 'id-card-alt',
'recruitmentcandidature' => 'id-badge',
'resize' => 'crop',
'supplier_order' => 'dol-order_supplier',
'supplier_proposal' => 'file-signature',
'refresh' => 'redo',
'region' => 'map-marked',
'replacement' => 'exchange-alt',
'resource' => 'laptop-house',
'recurring' => 'history',
'service' => 'concierge-bell',
'skill' => 'shapes',
'state' => 'map-marked-alt',
'security' => 'key',
'salary' => 'wallet',
'shipment' => 'dolly',
'stock' => 'box-open',
'stats' => 'chart-bar',
'split' => 'code-branch',
'status' => 'stop-circle',
'stripe' => 'stripe-s',
'supplier' => 'building',
'technic' => 'cogs',
'tick' => 'check',
'timespent' => 'clock',
'title_setup' => 'tools',
'title_accountancy' => 'money-check-alt',
'title_bank' => 'university',
'title_hrm' => 'umbrella-beach',
'title_agenda' => 'calendar-alt',
'uncheck' => 'times',
'uparrow' => 'share',
'url' => 'external-link-alt',
'vat' => 'money-check-alt',
'vcard' => 'arrow-alt-circle-down',
'jabber' => 'comment',
'website' => 'globe-americas',
'workstation' => 'pallet',
'webhook' => 'bullseye',
'world' => 'globe',
'private' => 'user-lock',
'conferenceorbooth' => 'chalkboard-teacher',
'eventorganization' => 'project-diagram',
'webportal' => 'door-open'
);
if (empty($mode) || $mode == 'fa') {
// Array when the fa picto key is different than the Dolibarr picto key.
$arrayconvpictotofa = array(
'account' => 'university',
'accounting_account' => 'clipboard-list',
'accountline' => 'receipt',
'accountancy' => 'search-dollar',
'action' => 'calendar-alt',
'add' => 'plus-circle',
'address' => 'address-book',
'ai' => 'magic',
'asset' => 'money-check-alt',
'autofill' => 'fill',
'back' => 'arrow-left',
'bank_account' => 'university',
'bill' => 'file-invoice-dollar',
'billa' => 'file-excel',
'billr' => 'file-invoice-dollar',
'billd' => 'file-medical',
'blockedlog' => 'file-archive',
'bookcal' => 'calendar-check',
'supplier_invoice' => 'file-invoice-dollar',
'supplier_invoicea' => 'file-excel',
'supplier_invoicer' => 'file-invoice-dollar',
'supplier_invoiced' => 'file-medical',
'bom' => 'shapes',
'card' => 'address-card',
'chart' => 'chart-line',
'company' => 'building',
'contact' => 'address-book',
'contract' => 'suitcase',
'collab' => 'people-arrows',
'conversation' => 'comments',
'country' => 'globe-americas',
'cron' => 'business-time',
'cross' => 'times',
'chevron-double-left' => 'angle-double-left',
'chevron-double-right' => 'angle-double-right',
'chevron-double-down' => 'angle-double-down',
'chevron-double-top' => 'angle-double-up',
'donation' => 'file-alt',
'dynamicprice' => 'hand-holding-usd',
'setup' => 'cog',
'companies' => 'building',
'products' => 'cube',
'commercial' => 'suitcase',
'invoicing' => 'coins',
'accounting' => 'search-dollar',
'category' => 'tag',
'dollyrevert' => 'dolly',
'file-o' => 'file',
'generate' => 'plus-square',
'hrm' => 'user-tie',
'incoterm' => 'truck-loading',
'margin' => 'calculator',
'members' => 'user-friends',
'ticket' => 'ticket-alt',
'globe' => 'external-link-alt',
'lot' => 'barcode',
'email' => 'at',
'establishment' => 'building',
'edit' => 'pencil-alt',
'entity' => 'globe',
'graph' => 'chart-line',
'grip_title' => 'arrows-alt',
'grip' => 'arrows-alt',
'help' => 'question-circle',
'generic' => 'file',
'holiday' => 'umbrella-beach',
'info' => 'info-circle',
'info_black' => 'info-circle',
'inventory' => 'boxes',
'intracommreport' => 'globe-europe',
'jobprofile' => 'cogs',
'knowledgemanagement' => 'ticket-alt',
'label' => 'layer-group',
'layout' => 'columns',
'line' => 'bars',
'loan' => 'money-bill-alt',
'member' => 'user-alt',
'meeting' => 'chalkboard-teacher',
'mrp' => 'cubes',
'next' => 'arrow-alt-circle-right',
'trip' => 'wallet',
'expensereport' => 'wallet',
'group' => 'users',
'movement' => 'people-carry',
'sign-out' => 'sign-out-alt',
'switch_off' => 'toggle-off',
'switch_on' => 'toggle-on',
'switch_on_grey' => 'toggle-on',
'switch_on_warning' => 'toggle-on',
'switch_on_red' => 'toggle-on',
'check' => 'check',
'bookmark' => 'star',
'bank' => 'university',
'close_title' => 'times',
'delete' => 'trash',
'filter' => 'filter',
'list-alt' => 'list-alt',
'calendarlist' => 'bars',
'calendar' => 'calendar-alt',
'calendarmonth' => 'calendar-alt',
'calendarweek' => 'calendar-week',
'calendarday' => 'calendar-day',
'calendarperuser' => 'table',
'calendarpertype' => 'table',
'intervention' => 'ambulance',
'invoice' => 'file-invoice-dollar',
'order' => 'file-invoice',
'error' => 'exclamation-triangle',
'warning' => 'exclamation-triangle',
'other' => 'square',
'playdisabled' => 'play',
'pdf' => 'file-pdf',
'poll' => 'check-double',
'pos' => 'cash-register',
'preview' => 'binoculars',
'project' => 'project-diagram',
'projectpub' => 'project-diagram',
'projecttask' => 'tasks',
'propal' => 'file-signature',
'proposal' => 'file-signature',
'partnership' => 'handshake',
'payment' => 'money-check-alt',
'payment_vat' => 'money-check-alt',
'pictoconfirm' => 'check-square',
'phoning' => 'phone',
'phoning_mobile' => 'mobile-alt',
'phoning_fax' => 'fax',
'previous' => 'arrow-alt-circle-left',
'printer' => 'print',
'product' => 'cube',
'puce' => 'angle-right',
'recent' => 'check-square',
'reception' => 'dolly',
'recruitmentjobposition' => 'id-card-alt',
'recruitmentcandidature' => 'id-badge',
'resize' => 'crop',
'supplier_order' => 'dol-order_supplier',
'supplier_proposal' => 'file-signature',
'refresh' => 'redo',
'region' => 'map-marked',
'replacement' => 'exchange-alt',
'resource' => 'laptop-house',
'recurring' => 'history',
'service' => 'concierge-bell',
'skill' => 'shapes',
'state' => 'map-marked-alt',
'security' => 'key',
'salary' => 'wallet',
'shipment' => 'dolly',
'stock' => 'box-open',
'stats' => 'chart-bar',
'split' => 'code-branch',
'status' => 'stop-circle',
'stripe' => 'stripe-s',
'supplier' => 'building',
'technic' => 'cogs',
'tick' => 'check',
'timespent' => 'clock',
'title_setup' => 'tools',
'title_accountancy' => 'money-check-alt',
'title_bank' => 'university',
'title_hrm' => 'umbrella-beach',
'title_agenda' => 'calendar-alt',
'uncheck' => 'times',
'uparrow' => 'share',
'url' => 'external-link-alt',
'vat' => 'money-check-alt',
'vcard' => 'arrow-alt-circle-down',
'jabber' => 'comment',
'website' => 'globe-americas',
'workstation' => 'pallet',
'webhook' => 'bullseye',
'world' => 'globe',
'private' => 'user-lock',
'conferenceorbooth' => 'chalkboard-teacher',
'eventorganization' => 'project-diagram',
'webportal' => 'door-open'
);
if ($conf->currency == 'EUR') {
$arrayconvpictotofa['currency'] = 'euro-sign';
$arrayconvpictotofa['multicurrency'] = 'dollar-sign';
if ($conf->currency == 'EUR') {
$arrayconvpictotofa['currency'] = 'euro-sign';
$arrayconvpictotofa['multicurrency'] = 'dollar-sign';
} else {
$arrayconvpictotofa['currency'] = 'dollar-sign';
$arrayconvpictotofa['multicurrency'] = 'euro-sign';
}
} else {
$arrayconvpictotofa['currency'] = 'dollar-sign';
$arrayconvpictotofa['multicurrency'] = 'euro-sign';
$arrayconvpictotofa = array();
}
return $arrayconvpictotofa;

View File

@@ -1166,7 +1166,7 @@ function get_left_menu_home($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu =
if ($usemenuhider || empty($leftmenu) || $leftmenu == "setup") {
// Define $nbmodulesnotautoenabled - TODO This code is at different places
$nbmodulesnotautoenabled = count($conf->modules);
$listofmodulesautoenabled = array('agenda', 'fckeditor', 'export', 'import');
$listofmodulesautoenabled = array('user', 'agenda', 'fckeditor', 'export', 'import');
foreach ($listofmodulesautoenabled as $moduleautoenable) {
if (in_array($moduleautoenable, $conf->modules)) {
$nbmodulesnotautoenabled--;
@@ -1182,7 +1182,7 @@ function get_left_menu_home($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu =
}
$newmenu->add(dolBuildUrl("/admin/company.php", ['mainmenu' => 'home']), $langs->trans("MenuCompanySetup").$warnpicto, 1);
$warnpicto = '';
if ($nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only user module enabled
if ($nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled)
$langs->load("errors");
$warnpicto = img_warning($langs->trans("WarningMandatorySetupNotComplete"));
}
@@ -2345,8 +2345,8 @@ function get_left_menu_mrp($mainmenu, &$newmenu, $usemenuhider = 1, $leftmenu =
$langs->load("mrp");
$newmenu->add("", $langs->trans("MenuMRP"), 0, $user->hasRight('mrp', 'read'), '', $mainmenu, 'mrp', 0, '', '', '', img_picto('', 'mrp', 'class="paddingright pictofixedwidth"'));
$newmenu->add("/mrp/mo_card.php?leftmenu=mo&action=create", $langs->trans("NewMO"), 1, $user->hasRight('mrp', 'write'), '', $mainmenu, '');
$newmenu->add("/mrp/mo_list.php?leftmenu=mo", $langs->trans("List"), 1, $user->hasRight('mrp', 'read'), '', $mainmenu, '');
$newmenu->add("/mrp/mo_card.php?leftmenu=mrp&action=create", $langs->trans("NewMO"), 1, $user->hasRight('mrp', 'write'), '', $mainmenu, '');
$newmenu->add("/mrp/mo_list.php?leftmenu=mrp", $langs->trans("List"), 1, $user->hasRight('mrp', 'read'), '', $mainmenu, '');
}
}
}

View File

@@ -450,16 +450,16 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it
public $langfiles;
/**
* @var array<string,string> Array of warnings to show when we activate the module
* @var array<string,mixed> Array of warnings to show when we activate the module
*
* array('always'='text') or array('FR'='text')
* array('always'=>'text') or array('FR'=>array('text1', 'text2))
*/
public $warnings_activation;
/**
* @var array<string,string> Array of warnings to show when we activate an external module
* @var array<string,mixed> Array of warnings to show when we activate a module if another module is on
*
* array('always'='text') or array('FR'='text')
* array('modOtherModule' => array('always'=>'text')) or array('modOtherModule' => array('FR'=>array('text1', 'text2)))
*/
public $warnings_activation_ext;

View File

@@ -142,8 +142,8 @@ class modAi extends DolibarrModules
$this->phpmin = array(7, 0); // Minimum version of PHP required by module
// Messages at activation
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'AiWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -92,8 +92,8 @@ class modAsset extends DolibarrModules
$this->langfiles = array("assets");
$this->phpmin = array(7, 0); // Minimum version of PHP required by module
$this->need_dolibarr_version = array(7, 0); // Minimum version of Dolibarr required by module
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'AssetsWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -56,7 +56,7 @@ class modBlockedLog extends DolibarrModules
// Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase)
$this->const_name = 'MAIN_MODULE_'.strtoupper($this->name);
// Name of image file used for this module.
$this->picto = 'technic';
$this->picto = 'blockedlog';
// Data directories to create when module is enabled
$this->dirs = array();
@@ -73,18 +73,18 @@ class modBlockedLog extends DolibarrModules
$this->conflictwith = array(); // List of modules id this module is in conflict with
$this->langfiles = array('blockedlog');
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
$this->warnings_unactivation = array('FR'=>'BlockedLogAreRequiredByYourCountryLegislation');
// Currently, activation is not automatic because only companies (in France) making invoices to non business customers must
// enable this module.
/*if (getDolGlobalString('BLOCKEDLOG_DISABLE_NOT_ALLOWED_FOR_COUNTRY')) {
$tmp=explode(',', getDolGlobalString('BLOCKEDLOG_DISABLE_NOT_ALLOWED_FOR_COUNTRY'));
$tmp = explode(',', getDolGlobalString('BLOCKEDLOG_DISABLE_NOT_ALLOWED_FOR_COUNTRY'));
$this->automatic_activation = array();
foreach($tmp as $key)
foreach($tmp as $countrycodekey)
{
$this->automatic_activation[$key]='BlockedLogActivatedBecauseRequiredByYourCountryLegislation';
$this->automatic_activation[$countrycodekey] = 'BlockedLogActivatedBecauseRequiredByYourCountryLegislation';
}
}*/
//var_dump($this->automatic_activation);

View File

@@ -115,8 +115,8 @@ class modBom extends DolibarrModules
$this->langfiles = array("mrp");
//$this->phpmin = array(7, 0)); // Minimum version of PHP required by module
$this->need_dolibarr_version = array(9, 0); // Minimum version of Dolibarr required by module
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'BomWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -139,8 +139,8 @@ class modBookCal extends DolibarrModules
$this->langfiles = array("agenda");
// Messages at activation
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'BookCalWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -93,8 +93,8 @@ class modDataPolicy extends DolibarrModules
$this->conflictwith = array(); // List of module class names as string this module is in conflict with
$this->langfiles = array("datapolicy");
$this->phpmin = array(7, 1); // Minimum version of PHP required by module
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'datapolicyWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants
// List of particular constants to add when module is enabled (key, 'chaine', value, desc, visible, 'current' or 'allentities', deleteonunactive)

View File

@@ -93,8 +93,8 @@ class modDav extends DolibarrModules
$this->langfiles = array("admin");
$this->phpmin = array(7, 0); // Minimum version of PHP required by module
$this->need_dolibarr_version = array(7, 0); // Minimum version of Dolibarr required by module
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'davWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -91,8 +91,8 @@ class modEmailCollector extends DolibarrModules
$this->requiredby = array(); // List of module ids to disable if this one is disabled
$this->conflictwith = array(); // List of module class names as string this module is in conflict with
$this->langfiles = array("admin");
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'davWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -130,8 +130,8 @@ class modEventOrganization extends DolibarrModules
$this->need_dolibarr_version = array(13, -3); // Minimum version of Dolibarr required by module
// Messages at activation
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'EventOrganizationWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -69,8 +69,8 @@ class modFacture extends DolibarrModules
$this->requiredby = array("modComptabilite", "modAccounting");
$this->conflictwith = array();
$this->langfiles = array("bills", "companies", "compta", "products");
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='text')
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='text')
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
// Config pages
$this->config_page_url = array("invoice.php");

View File

@@ -145,8 +145,8 @@ class modKnowledgeManagement extends DolibarrModules
$this->need_dolibarr_version = array(11, -3); // Minimum version of Dolibarr required by module
// Messages at activation
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'KnowledgeManagementWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -125,8 +125,8 @@ class modMrp extends DolibarrModules
$this->langfiles = array("mrp");
$this->phpmin = array(7, 0); // Minimum version of PHP required by module
$this->need_dolibarr_version = array(8, 0); // Minimum version of Dolibarr required by module
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'MrpWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -152,8 +152,8 @@ class modPartnership extends DolibarrModules
$this->need_dolibarr_version = array(11, -3); // Minimum version of Dolibarr required by module
// Messages at activation
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'PartnershipWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -125,8 +125,8 @@ class modRecruitment extends DolibarrModules
$this->langfiles = array("recruitment");
$this->phpmin = array(7, 0); // Minimum version of PHP required by module
$this->need_dolibarr_version = array(11, -3); // Minimum version of Dolibarr required by module
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'RecruitmentWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -126,8 +126,8 @@ class modStockTransfer extends DolibarrModules
$this->conflictwith = array(); // List of module class names as string this module is in conflict with. Example: array('modModuleToDisable1', ...)
$this->langfiles = array("stocktransfer@stocktransfer");
$this->phpmin = array(7, 0); // Minimum version of PHP required by module
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'StockTransferWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -107,8 +107,8 @@ class modTakePos extends DolibarrModules
$this->langfiles = array("cashdesk");
$this->phpmin = array(7, 0); // Minimum version of PHP required by module
$this->need_dolibarr_version = array(4, 0); // Minimum version of Dolibarr required by module
$this->warnings_activation = array('FR'=>'WarningNoteModulePOSForFrenchLaw'); // Warning to show when we activate module. array('always'='text') or array('FR'='text')
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array('FR' => array('WarningNoteModulePOSForFrenchLaw', 'WarningNoteModulePOSForFrenchLaw2', 'WarningNoteModulePOSForFrenchLaw3')); // Warning to show when we activate module. array('always' => 'text') or array('FR' => array('text1', 'text2))
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'TakePosWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -71,6 +71,7 @@ class modUser extends DolibarrModules
$this->phpmin = array(7, 0); // Minimum version of PHP required by module
$this->langfiles = array("main", "users", "companies", "members", "salaries", "hrm");
$this->always_enabled = true; // Can't be disabled
//$this->auto_enabled = true; // always_enabled already set
// Constants
$this->const = array();

View File

@@ -148,8 +148,8 @@ class modWebPortal extends DolibarrModules
//$this->need_javascript_ajax = 0;
// Messages at activation
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'WebPortalWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -145,8 +145,8 @@ class modWebhook extends DolibarrModules
$this->need_dolibarr_version = array(11, -3); // Minimum version of Dolibarr required by module
// Messages at activation
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'WebhookWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -128,8 +128,8 @@ class modWorkstation extends DolibarrModules
$this->langfiles = array("mrp");
$this->phpmin = array(7, 0); // Minimum version of PHP required by module
$this->need_dolibarr_version = array(11, -3); // Minimum version of Dolibarr required by module
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
$this->warnings_activation_ext = array();
//$this->automatic_activation = array('FR'=>'WorkstationWasAutomaticallyActivatedBecauseOfYourCountryChoice');
// Constants

View File

@@ -128,9 +128,7 @@ class modZapier extends DolibarrModules
//$this->phpmin = array(7, 0);
// Minimum version of Dolibarr required by module
$this->need_dolibarr_version = array(10, 0);
// Warning to show when we activate module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation = array();
// Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','ES'='textes'...)
$this->warnings_activation_ext = array();
// Constants
// List of particular constants to add when module is enabled (key, 'chaine', value, desc, visible, 'current' or 'allentities', deleteonunactive)

View File

@@ -0,0 +1,48 @@
<?php
/* Copyright (C) 2025 Laurent Destailleur <eldy@destailleur.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* @var Conf $conf
*/
'
@phan-var-force Conf $conf
';
// Protection to avoid direct call of template
if (empty($conf) || !is_object($conf)) {
print "Error, template page can't be called as URL";
exit(1);
}
// Define $nbmodulesnotautoenabled - TODO This code is at different places
$nbmodulesnotautoenabled = count($conf->modules);
$listofmodulesautoenabled = array('user', 'agenda', 'fckeditor', 'export', 'import');
foreach ($listofmodulesautoenabled as $moduleautoenable) {
if (in_array($moduleautoenable, $conf->modules)) {
$nbmodulesnotautoenabled--;
}
}
// Check if company name is defined (first install)
if (!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') || !getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY')) {
header("Location: ".DOL_URL_ROOT."/admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete");
exit;
}
if ($nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only autoenabled modules (property ->enabled_bydefault in modules) are activated
header("Location: ".DOL_URL_ROOT."/admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete");
exit;
}

View File

@@ -25,7 +25,6 @@
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -43,6 +42,7 @@ if ($user->socid > 0) {
$socid = $user->socid;
}
require_once DOL_DOCUMENT_ROOT.'/core/redirect_if_setup_not_complete.inc.php';
/*

View File

@@ -75,6 +75,11 @@ $offset = $limit * $page;
$pageprev = $page - 1;
$pagenext = $page + 1;
$outputdir = $conf->cron->dir_output;
if (empty($outputdir)) {
$outputdir = $conf->cronjob->dir_output;
}
// Initialize technical objects
$object = new Cronjob($db);
$extrafields = new ExtraFields($db);
@@ -94,11 +99,6 @@ if (!$sortorder) {
$sortorder = 'ASC,DESC';
}
$outputdir = $conf->cron->dir_output;
if (empty($outputdir)) {
$outputdir = $conf->cronjob->dir_output;
}
// List of fields to search into when doing a "search in all"
$fieldstosearchall = array();
// foreach ($object->fields as $key => $val) {
@@ -617,12 +617,11 @@ $totalarray = array();
$totalarray['nbfield'] = 0;
$imaxinloop = ($limit ? min($num, $limit) : $num);
if ($num > 0) {
// Loop on each job
while ($i < $imaxinloop) {
$obj = $db->fetch_object($result);
$obj = $db->fetch_object($resql);
if (empty($obj)) {
break;
}

View File

@@ -465,7 +465,7 @@ if ($action == 'create') {
print '</td>';
// Amount
print "<tr>".'<td class="fieldrequired">'.$langs->trans("Amount").'</td><td><input type="text" name="amount" value="'.dol_escape_htmltag(GETPOST("amount")).'" size="10"> '.$langs->trans("Currency".$conf->currency).'</td></tr>';
print "<tr>".'<td class="fieldrequired">'.$langs->trans("Amount").'</td><td><input type="text" name="amount" value="'.dol_escape_htmltag(GETPOST("amount")).'" size="10" spellcheck="false"> '.$langs->trans("Currency".$conf->currency).'</td></tr>';
// Public donation
print '<tr><td class="fieldrequired">'.$langs->trans("PublicDonation")."</td><td>";
@@ -494,7 +494,7 @@ if ($action == 'create') {
}
print '</td></tr>';
print "<tr>".'<td>'.$langs->trans("EMail").'</td><td>'.img_picto('', 'object_email', 'class="paddingrightonly"').'<input type="text" name="email" value="'.dol_escape_htmltag(GETPOST("email")).'" class="maxwidth200"></td></tr>';
print '<tr><td>'.$langs->trans("EMail").'</td><td>'.img_picto('', 'object_email', 'class="paddingrightonly"').'<input type="text" name="email" value="'.dol_escape_htmltag(GETPOST("email")).'" class="maxwidth200" spellcheck="false"></td></tr>';
}
// Payment mode
@@ -588,7 +588,7 @@ if (!empty($id) && $action == 'edit') {
// Amount
if ($object->status == 0) {
print "<tr>".'<td class="fieldrequired">'.$langs->trans("Amount").'</td><td><input type="text" name="amount" size="10" value="'.price($object->amount).'"> '.$langs->trans("Currency".$conf->currency).'</td></tr>';
print "<tr>".'<td class="fieldrequired">'.$langs->trans("Amount").'</td><td><input type="text" name="amount" size="10" value="'.price($object->amount).'" spellcheck="false"> '.$langs->trans("Currency".$conf->currency).'</td></tr>';
} else {
print '<tr><td>'.$langs->trans("Amount").'</td><td>';
print price($object->amount, 0, $langs, 0, 0, -1, $conf->currency);
@@ -632,7 +632,7 @@ if (!empty($id) && $action == 'edit') {
}
print '</td></tr>';
print "<tr>".'<td>'.$langs->trans("EMail").'</td><td><input type="text" name="email" class="maxwidth200" value="'.dol_escape_htmltag($object->email).'"></td></tr>';
print '<tr><td>'.$langs->trans("EMail").'</td><td><input type="text" name="email" class="maxwidth200" value="'.dol_escape_htmltag($object->email).'" spellcheck="false"></td></tr>';
}
// Payment mode
print "<tr><td>".$langs->trans("PaymentMode")."</td><td>\n";

View File

@@ -219,7 +219,7 @@ if ($action == 'create') {
print '<tr><td>'.$langs->trans('Numero');
print ' <em>('.$langs->trans("ChequeOrTransferNumber").')</em>';
print '</td>';
print '<td colspan="2"><input name="num_payment" type="text" value="'.GETPOST('num_payment').'"></td></tr>'."\n";
print '<td colspan="2"><input name="num_payment" type="text" value="'.GETPOST('num_payment').'" spellcheck="false"></td></tr>'."\n";
// Check transmitter
print '<tr><td class="'.(GETPOST('paiementcode') == 'CHQ' ? 'fieldrequired ' : '').'fieldrequireddyn">'.$langs->trans('CheckTransmitter');

View File

@@ -2060,7 +2060,7 @@ class ExpenseReport extends CommonObject
*/
public function checkRules($type = 0, $seller = '')
{
global $conf, $db, $langs, $mysoc;
global $conf, $langs, $mysoc;
$langs->load('trips');
@@ -2070,7 +2070,7 @@ class ExpenseReport extends CommonObject
$seller->tva_assuj = 1; // Most seller uses vat
}
$expensereportrule = new ExpenseReportRule($db);
$expensereportrule = new ExpenseReportRule($this->db);
$rulestocheck = $expensereportrule->getAllRule($this->line->fk_c_type_fees, $this->line->date, $this->fk_user_author);
$violation = 0;
@@ -2740,7 +2740,7 @@ class ExpenseReport extends CommonObject
*/
public function computeTotalKm($fk_cat, $qty, $tva)
{
global $langs, $db, $conf;
global $langs, $conf;
$cumulYearQty = 0;
$ranges = array();
@@ -2757,7 +2757,7 @@ class ExpenseReport extends CommonObject
return -1;
}
$currentUser = new User($db);
$currentUser = new User($this->db);
$currentUser->fetch($this->fk_user);
$currentUser->loadRights('expensereport');
//Clean

View File

@@ -475,14 +475,14 @@ if ($step == 1 || !$datatoexport) {
// Define $nbmodulesnotautoenabled - TODO This code is at different places
$nbmodulesnotautoenabled = count($conf->modules);
$listofmodulesautoenabled = array('agenda', 'fckeditor', 'export', 'import');
$listofmodulesautoenabled = array('user', 'agenda', 'fckeditor', 'export', 'import');
foreach ($listofmodulesautoenabled as $moduleautoenable) {
if (in_array($moduleautoenable, $conf->modules)) {
$nbmodulesnotautoenabled--;
}
}
if ($user->admin && $nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
if ($user->admin && $nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
print info_admin($langs->trans("WarningOnlyProfilesOfActivatedModules").' '.$langs->trans("YouCanEnableModulesFrom"));
}

View File

@@ -141,7 +141,7 @@ $search_multicurrency_montant_ht = GETPOST('search_multicurrency_montant_ht', 'a
$search_multicurrency_montant_tva = GETPOST('search_multicurrency_montant_tva', 'alpha');
$search_multicurrency_montant_ttc = GETPOST('search_multicurrency_montant_ttc', 'alpha');
$optioncss = GETPOST('optioncss', 'alpha');
$billed = GETPOSTINT('billed');
$billed = GETPOST('billed', 'int'); // Value '' must be possible
$search_project_ref = GETPOST('search_project_ref', 'alpha');
$search_btn = GETPOST('button_search', 'alpha');
$search_remove_btn = GETPOST('button_removefilter', 'alpha');

View File

@@ -934,7 +934,7 @@ if (empty($reshook)) {
$object->fk_incoterms = GETPOSTINT('incoterm_id');
$object->location_incoterms = GETPOST('location_incoterms', 'alpha');
$object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
$object->multicurrency_tx = GETPOSTINT('originmulticurrency_tx');
$object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
$object->transport_mode_id = GETPOSTINT('transport_mode_id');
// Proprietes particulieres a facture avoir
@@ -1037,7 +1037,7 @@ if (empty($reshook)) {
$object->fk_incoterms = GETPOSTINT('incoterm_id');
$object->location_incoterms = GETPOST('location_incoterms', 'alpha');
$object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
$object->multicurrency_tx = GETPOSTINT('originmulticurrency_tx');
$object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
// Source facture
$object->fac_rec = $fac_recid;
@@ -1104,7 +1104,7 @@ if (empty($reshook)) {
$object->fk_incoterms = GETPOSTINT('incoterm_id');
$object->location_incoterms = GETPOST('location_incoterms', 'alpha');
$object->multicurrency_code = GETPOST('multicurrency_code', 'alpha');
$object->multicurrency_tx = GETPOSTINT('originmulticurrency_tx');
$object->multicurrency_tx = GETPOSTFLOAT('originmulticurrency_tx');
$object->transport_mode_id = GETPOSTINT('transport_mode_id');
// Auto calculation of date due if not filled by user

View File

@@ -352,14 +352,14 @@ if ($step == 1 || !$datatoimport) {
// Define $nbmodulesnotautoenabled - TODO This code is at different places
$nbmodulesnotautoenabled = count($conf->modules);
$listofmodulesautoenabled = array('agenda', 'fckeditor', 'export', 'import');
$listofmodulesautoenabled = array('user', 'agenda', 'fckeditor', 'export', 'import');
foreach ($listofmodulesautoenabled as $moduleautoenable) {
if (in_array($moduleautoenable, $conf->modules)) {
$nbmodulesnotautoenabled--;
}
}
if ($user->admin && $nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
if ($user->admin && $nbmodulesnotautoenabled < getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only minimal initial modules enabled
print info_admin($langs->trans("WarningOnlyProfilesOfActivatedModules").' '.$langs->trans("YouCanEnableModulesFrom"));
}

View File

@@ -31,8 +31,6 @@
define('CSRFCHECK_WITH_TOKEN', 1); // We force need to use a token to login when making a POST
require 'main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -42,6 +40,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
*
* @var string $conffile defined into filefunc.inc.php
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
// If not defined, we select menu "home"
$_GET['mainmenu'] = GETPOST('mainmenu', 'aZ09') ? GETPOST('mainmenu', 'aZ09') : 'home'; // Keep this ?
@@ -49,29 +48,13 @@ $action = GETPOST('action', 'aZ09');
$hookmanager->initHooks(array('index'));
require_once DOL_DOCUMENT_ROOT.'/core/redirect_if_setup_not_complete.inc.php';
/*
* Actions
*/
// Define $nbmodulesnotautoenabled - TODO This code is at different places
$nbmodulesnotautoenabled = count($conf->modules);
$listofmodulesautoenabled = array('agenda', 'fckeditor', 'export', 'import');
foreach ($listofmodulesautoenabled as $moduleautoenable) {
if (in_array($moduleautoenable, $conf->modules)) {
$nbmodulesnotautoenabled--;
}
}
// Check if company name is defined (first install)
if (!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') || !getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY')) {
header("Location: ".DOL_URL_ROOT."/admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete");
exit;
}
if ($nbmodulesnotautoenabled <= getDolGlobalInt('MAIN_MIN_NB_ENABLED_MODULE_FOR_WARNING', 1)) { // If only autoenabled modules (property ->enabled_bydefault in modules) are activated
header("Location: ".DOL_URL_ROOT."/admin/index.php?mainmenu=home&leftmenu=setup&mesg=setupnotcomplete");
exit;
}
if (GETPOST('addbox')) { // Add box (when submit is done from a form when ajax disabled)
require_once DOL_DOCUMENT_ROOT.'/core/class/infobox.class.php';
$zone = GETPOSTINT('areacode');

View File

@@ -172,6 +172,9 @@ ALTER TABLE llx_subscription ADD INDEX idx_subscription_fk_adherent (fk_adherent
ALTER TABLE llx_subscription ADD INDEX idx_subscription_fk_bank (fk_bank);
ALTER TABLE llx_subscription ADD INDEX idx_subscription_dateadh (dateadh);
ALTER TABLE llx_subscription ADD COLUMN ref_ext varchar(128);
ALTER TABLE llx_subscription ADD COLUMN note_private text;
ALTER TABLE llx_bank_import ADD COLUMN fitid varchar(255) NULL after id_account; -- OFX Financial Institution Transaction ID "FITID"
ALTER TABLE llx_element_contact ADD mandatory_signature TINYINT AFTER element_id;
@@ -231,4 +234,8 @@ ALTER TABLE llx_accounting_bookkeeping_piece ADD INDEX idx_accounting_bookkeepin
ALTER TABLE llx_mailing ADD COLUMN fk_project integer DEFAULT NULL;
UPDATE llx_c_units SET label = 'unitP' WHERE code = 'P';
ALTER TABLE llx_receptiondet_batch ADD COLUMN description text AFTER fk_product;
ALTER TABLE llx_receptiondet_batch ADD COLUMN fk_unit integer AFTER qty;
ALTER TABLE llx_receptiondet_batch ADD COLUMN rang integer DEFAULT 0 AFTER cost_price;
-- end of migration

View File

@@ -19,9 +19,7 @@
-- into stock_movement so this table may be useless.
--
-- Detail of each lines of a reception (qty, batch and into which warehouse must be
-- received or has been receveived a purchase order line).
--
-- This table should be renamed into llx_receptiondet_batch
-- received or has been received a purchase order line).
-- ===================================================================
create table llx_receptiondet_batch
@@ -32,11 +30,13 @@ create table llx_receptiondet_batch
fk_elementdet integer, -- ID of line of main source object.
element_type varchar(50) DEFAULT 'supplier_order' NOT NULL, -- Type of source object ('supplier_order', ...)
fk_product integer,
qty float, -- qty to move
fk_entrepot integer, -- ID of warehouse to use for the stock change
description text, -- Product description/label of non origin
qty float, -- qty to move
fk_unit integer, -- ID of unit code
fk_entrepot integer, -- ID of warehouse to use for the stock change
fk_projet integer DEFAULT NULL,
comment varchar(255), -- comment on movement
batch varchar(128) DEFAULT NULL, -- serial/lot number
comment varchar(255), -- comment on movement
batch varchar(128) DEFAULT NULL, -- serial/lot number
eatby date DEFAULT NULL,
sellby date DEFAULT NULL,
status integer,
@@ -44,5 +44,6 @@ create table llx_receptiondet_batch
datec datetime,
tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
cost_price double(24,8) DEFAULT 0,
extraparams varchar(255) -- to stock other parameters in json format
rang integer DEFAULT 0, -- Position of line
extraparams varchar(255) -- to stock other parameters in json format
)ENGINE=innodb;

View File

@@ -29,5 +29,7 @@ create table llx_subscription
fk_bank integer DEFAULT NULL, -- id of bank transaction in llx_bank
fk_user_creat integer DEFAULT NULL,
fk_user_valid integer DEFAULT NULL,
note text
ref_ext varchar(128), -- reference into an external system (not used by dolibarr)
note text, -- public note
note_private text -- private note
)ENGINE=innodb;

View File

@@ -56,6 +56,7 @@ if (file_exists($conffile)) {
require_once $dolibarr_main_document_root.'/core/lib/admin.lib.php';
include_once $dolibarr_main_document_root.'/core/lib/images.lib.php';
require_once $dolibarr_main_document_root.'/core/class/extrafields.class.php';
require_once $dolibarr_main_document_root.'/core/class/html.form.class.php';
require_once 'lib/repair.lib.php';
$ok = 0;
@@ -94,6 +95,8 @@ if (!is_object($conf)) {
* View
*/
$form = new Form($db);
pHeader($langs->trans("Repair"), "upgrade2", GETPOST('action', 'aZ09'));
// Action to launch the repair script
@@ -219,7 +222,8 @@ $sections = [
],
[
'name' => 'repair_mailing_path',
'info' => 'Repair path of mailing files.<br>Should be applied when using emailing module with > 99 mailings.<br>In that case, please also set MAILING_USE_NEW_PATH_FOR_FILES.'
'info' => 'Repair path of mailing files',
'tooltip' => 'Should be applied when using emailing module with > 99 mailings. In that case, please also set MAILING_USE_NEW_PATH_FOR_FILES.'
]
],
'Clean tables and data' => [
@@ -280,18 +284,27 @@ $sections = [
]
];
$conf->use_javascript_ajax = 1;
foreach ($sections as $section => $options) {
print '<tr style="background:#f4f4f4;font-weight:bold"><td colspan="5">'.$section.'</td></tr>';
foreach ($options as $opt) {
$option = $opt['name'];
$info = $opt['info'];
$tooltip = empty($opt['tooltip']) ? '' : $opt['tooltip'];
$value = GETPOST($option, 'alpha') ? GETPOST($option, 'alpha') : 'undefined';
// Generate links with the right option and value
$url_test = $_SERVER['PHP_SELF'].'?'.$option.'=test';
$url_confirmed = $_SERVER['PHP_SELF'].'?'.$option.'=confirmed';
print '<tr>';
print '<td>' . $option . '</td>';
print '<td>' . $info . '</td>';
print '<td>';
if ($tooltip) {
print $form->textwithpicto($info, $tooltip);
} else {
print $info;
}
print '</td>';
print '<td class="center"><a href="'.$url_test.'" title="Launch test on option '.$option.'">test</a>'.($value == 'test' ? ' (X)' : '').'</td>';
print '<td class="center"><a href="'.$url_confirmed.'" title="Launch confirmed on option '.$option.'">confirmed</a>'.($value == 'confirmed' ? ' (X)' : '').'</td>';
print '</tr>';
@@ -301,8 +314,7 @@ print '</table>';
print '<br id="sectionresult">';
print '<table cellspacing="0" cellpadding="1" class="centpercent">';
print '<br>';
$conf->setValues($db);
@@ -324,11 +336,18 @@ $oneoptionset = (GETPOST('standard', 'alpha') || GETPOST('restore_thirdparties_l
if ($ok && $oneoptionset) {
// Show wait message
print '<tr><td colspan="2">'.$langs->trans("PleaseBePatient").'<br><br></td></tr>';
print $langs->trans("PleaseBePatient").'<br><br>';
// Flush (some browser need a certain amount of data)
print str_repeat(' ', 1024);
ob_flush();
flush();
}
print '<table cellspacing="0" cellpadding="1" class="centpercent">';
// run_sql: Run repair SQL file
if ($ok && GETPOST('standard', 'alpha')) {
$dir = "mysql/migration/";
@@ -371,6 +390,13 @@ if ($ok && GETPOST('standard', 'alpha')) {
// sync_extrafields: Search list of fields declared and list of fields created into databases, then create fields missing
if ($ok && GETPOST('standard', 'alpha')) {
require_once DOL_DOCUMENT_ROOT.'/contrat/class/contratligne.class.php';
print '<tr><td colspan="2"><br>*** Update denormalized_lower_planned_end_date.</td></tr>';
$sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat as c";
$sqltoupdatecontract .= " SET c.denormalized_lower_planned_end_date = (SELECT MIN(date_fin_validite) FROM ".MAIN_DB_PREFIX."contratdet as cd WHERE cd.fk_contrat = c.rowid AND cd.statut = ".ContratLigne::STATUS_OPEN.")";
$resqltoupdatecontract = $db->query($sqltoupdatecontract);
$extrafields = new ExtraFields($db);
// List of tables that has an extrafield table
@@ -385,7 +411,7 @@ if ($ok && GETPOST('standard', 'alpha')) {
'adherent_type' => 'adherent_type', 'user' => 'user', 'partnership' => 'partnership', 'projet' => 'projet', 'projet_task' => 'projet_task', 'ticket' => 'ticket');
//$listofmodulesextra = array('fichinter'=>'fichinter');
print '<tr><td colspan="2"><br>*** Check fields into extra table structure match table of definition. If not add column into table</td></tr>';
print '<tr><td colspan="2"><br>*** Check that fields into the extra table structure match the table of definition. If not, add column into table</td></tr>';
foreach ($listofmodulesextra as $tablename => $elementtype) {
// Get list of fields
$tableextra = MAIN_DB_PREFIX.$tablename.'_extrafields';
@@ -2148,7 +2174,9 @@ if (empty($actiondone)) {
}
if ($oneoptionset) {
print '<br>';
print '<div class="center" style="padding-top: 10px"><a href="../index.php?mainmenu=home&leftmenu=home'.(GETPOSTISSET("login") ? '&username='.urlencode(GETPOST("login")) : '').'">';
print img_picto('', 'url', 'class="pictofixedwidth"');
print $langs->trans("GoToDolibarr");
print '</a></div>';
}

View File

@@ -240,7 +240,7 @@ Updated=Updated
AchatTelechargement=Buy / Download
GoModuleSetupArea=To deploy/install a new module, go to the Module setup area: <a href="%s">%s</a>.
DoliStoreDesc=DoliStore, the official market place for Dolibarr ERP/CRM external modules
CommunityModulesDesc=Dolibarr community modules repository. This repository contains some modules for Dolibarr ERP CRM referenced on the community repository.
CommunityModulesDesc=Dolibarr community modules repository. This repository contains some external modules for Dolibarr ERP CRM referenced on the community repository.
DoliPartnersDesc=List of companies providing custom-developed modules or features.<br>Note: since Dolibarr is an open source application, <i>anyone</i> experienced in PHP programming should be able to develop a module.
WebSiteDesc=External websites for more add-on (non-core) modules...
DevelopYourModuleDesc=Some solutions to develop your own module...
@@ -1293,8 +1293,8 @@ Delays_MAIN_DELAY_HOLIDAYS=Leave requests to approve
Delays_MAIN_DELAY_MRP=Manufacturing order in progress
Delays_MAIN_DELAY_SUPPLIER_PROPALS_TO_CLOSE=Supplier proposal not closed
Delays_MAIN_DELAY_SUPPLIER_PROPALS_TO_BILL=Supplier proposal not billed
SetupDescription1=Before starting to use Dolibarr some initial parameters must be defined and modules enabled/configured.
SetupDescription2=The following two sections (the two first entries in the Setup menu) are mandatory:
SetupDescription1=This configuration area must be used to configure the initial settings and enable the modules/features you need...
SetupDescription2=The following two configuration sections (the two first entries in the Setup menu) are mandatory.
SetupDescription3=<a href="%s">%s -> %s</a><br><br>Basic parameters used to customize the default behavior of your application (e.g for country-related features).
SetupDescription4=<a href="%s">%s -> %s</a><br><br>This software is a suite of many modules/applications. The modules related to your needs must be enabled and configured. Menu entries will appears with the activation of these modules.
SetupDescription5=Other Setup menu entries manage optional parameters.
@@ -2161,7 +2161,9 @@ UserHasNoPermissions=This user has no permissions defined
TypeCdr=Use "None" if the date of payment term is date of invoice plus a delta in days (delta is field "%s")<br>Use "At end of month", if, after delta, the date must be increased to reach the end of month (+ an optional "%s" in days)<br>Use "Current/Next" to have payment term date being the first Nth of the month after delta (delta is field "%s", N is stored into field "%s")
BaseCurrency=Reference currency of the company (go into setup of company to change this)
WarningNoteModuleInvoiceForFrenchLaw=This module %s is compliant with French laws (Loi Finance 2016).
WarningNoteModulePOSForFrenchLaw=This module %s is technically compliant with French laws (Loi Finance 2016) because module Non Reversible Logs is automatically activated. You must however obtain an attestation or a certificate to be allow to us it. Contact your webhosting provider, your integrator or reseller.
WarningNoteModulePOSForFrenchLaw=This module %s is "technically" compliant with French laws (Loi Finance 2016) because module Non Reversible Logs is automatically activated. You must however obtain an attestation or a certificate to be allow to us it.
WarningNoteModulePOSForFrenchLaw2=Enabling this module also allows you to make daily or monthly cash register closing from menu Bank and Cash.
WarningNoteModulePOSForFrenchLaw3=Contact your webhosting provider, your integrator or reseller.
WarningInstallationMayBecomeNotCompliantWithLaw=You are trying to install module %s that is an external module. Activating an external module means you trust the publisher of that module and that you are sure that this module does not adversely impact the behavior of your application, and is compliant with laws of your country (%s). If the module introduces an illegal feature, you become responsible for the use of illegal software.
WarningExperimentalFeatureInvoiceSituationNeedToUpgradeToProgressiveMode=If you are using the experimental mode for situation invoices, you will need to update your data to switch from the experimental mode to the official mode. You can contact a partner to help you with this task. A list of preferred partners is available by following <a href="%s" target="_blank">this link</a>
MAIN_PDF_MARGIN_LEFT=Left margin on PDF

View File

@@ -183,7 +183,7 @@ YourSEPAMandate=Your SEPA mandate
FindYourSEPAMandate=This is your SEPA mandate to authorize our company to make direct debit order to your bank. Return it signed (scan of the signed document) or send it by mail to
AutoReportLastAccountStatement=Automatically fill the field 'number of bank statement' with last statement number when making reconciliation
CashControl=POS cash control
NewCashFence=New cash control (opening or closing)
NewCashFence=New cash register opening or closing
BankColorizeMovement=Colorize movements
BankColorizeMovementDesc=If this function is enable, you can choose specific background color for debit or credit movements
BankColorizeMovementName1=Background color for debit movement

View File

@@ -18,8 +18,8 @@ BlockedlogInfoDialog=Log Details
ListOfTrackedEvents=List of tracked events
Fingerprint=Fingerprint
DownloadLogCSV=Export unalterable logs (CSV)
DataOfArchivedEvent=Complementary data of archived event
DataOfArchivedEventHelp=This field contains the complementary data that was archived on real time. Even if some parent business event could have been purged or modified, the data archived here is the original data, and it can't be modified.
DataOfArchivedEvent=Complete data of archived event
DataOfArchivedEventHelp=This field contains the complementary data that was archived on real time. Even if some parent business event could have been canceled or modified, the data stored here is the original data, and it can't be modified.
DataOfArchivedEventHelp2=The integrity of data on each line is guaranteed if the status of the line is OK
ImpossibleToReloadObject=Original object (type %s, id %s) not linked (see 'Full datas' column to get unalterable saved data)
BlockedLogAreRequiredByYourCountryLegislation=Unalterable Logs module may be required by the legislation of your country. Disabling this module may render any future transactions invalid with respect to the law and the use of legal software as they can not be validated by a tax audit.

View File

@@ -52,8 +52,8 @@ Footer=Footer
AmountAtEndOfPeriod=Amount at end of period (day, month or year)
TheoricalAmount=Theoretical amount
RealAmount=Real amount
CashFence=Cash box closing
CashFenceDone=Cash box closing done for the period
CashFence=Cash register closing/opening
CashFenceDone=Cash register closing/opening done for the period
NbOfInvoices=Nb of invoices
Paymentnumpad=Type of Pad to enter payment
Numberspad=Numbers Pad

View File

@@ -428,9 +428,10 @@ Total=Total
SubTotal=Subtotal
TotalHTShort=Total (excl.)
TotalHT100Short=Total 100%% (excl.)
TotalHTShortCurrency=Total (excl. in currency)
TotalHTShortCurrency=Total (excl., in currency)
TotalHTWithDiscount=Total (excl.) with discount
TotalTTCShort=Total (inc. tax)
TotalTTCShortCurrency=Total (inc. tax, in currency)
TotalHT=Total (excl. tax)
TotalHTforthispage=Total (excl. tax) for this page
Totalforthispage=Total for this page
@@ -439,6 +440,8 @@ TotalforAllPages=Total for all pages
TotalTTC=Total (inc. tax)
TotalTTCToYourCredit=Total (inc. tax) to your credit
TotalVAT=Total tax
TotalVATShort=Total tax
TotalVATShortCurrency=Total tax (in currency)
TotalVATIN=Total IGST
TotalLT1=Total tax 2
TotalLT2=Total tax 3

View File

@@ -458,3 +458,4 @@ GenerateWithAI=Generate with AI
PriceByCustomeAndMultiPricesAbility=Different prices for each customer + Multiple price segments per product/service (each customer is in one price segment)
WhenProductVirtualOnOptionAreForced=When the option 'Enable Kits' (Product module) is on, some modes for automatic stock decrease or increase may be not available. The other options have no restriction.
UpdatedRecently=Updated recently
ProductType=Type of product

View File

@@ -163,8 +163,8 @@ class modMyModule extends DolibarrModules
$this->need_javascript_ajax = 0;
// Messages at activation
$this->warnings_activation = array(); // Warning to show when we activate module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate an external module. array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation = array(); // Warning to show when we activate a module. Example: array('always'='text') or array('FR'='textfr','MX'='textmx'...)
$this->warnings_activation_ext = array(); // Warning to show when we activate a module if another module is on. Example: array('modOtherModule' => array('always'=>'text')) or array('always' => array('FR'=>'textfr','MX'=>'textmx'...))
//$this->automatic_activation = array('FR'=>'MyModuleWasAutomaticallyActivatedBecauseOfYourCountryChoice');
//$this->always_enabled = false; // If true, can't be disabled. Value true is reserved for core modules. Not allowed for external modules.
@@ -331,7 +331,7 @@ class modMyModule extends DolibarrModules
'url' => '/mymodule/mymoduleindex.php',
'langs' => 'mymodule@mymodule', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
'position' => 1000 + $r,
'enabled' => 'isModEnabled("mymodule")', // Define condition to show or hide menu entry. Use 'isModEnabled("mymodule")' if entry must be visible if module is enabled.
'enabled' => "isModEnabled('mymodule')", // Define condition to show or hide menu entry. Use "isModEnabled('mymodule')" if entry must be visible if module is enabled (those quote marks are importants).
'perms' => '1', // Use 'perms'=>'$user->hasRight("mymodule", "myobject", "read")' if you want your menu with a permission rules
'target' => '',
'user' => 2, // 0=Menu for internal users, 1=external users, 2=both
@@ -350,7 +350,7 @@ class modMyModule extends DolibarrModules
'url' => '/mymodule/mymoduleindex.php',
'langs' => 'mymodule@mymodule', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
'position' => 1000 + $r,
'enabled' => 'isModEnabled("mymodule")', // Define condition to show or hide menu entry. Use 'isModEnabled("mymodule")' if entry must be visible if module is enabled.
'enabled' => "isModEnabled('mymodule')", // Define condition to show or hide menu entry. Use isModEnabled("mymodule") if entry must be visible if module is enabled.
'perms' => '$user->hasRight("mymodule", "myobject", "read")',
'target' => '',
'user' => 2, // 0=Menu for internal users, 1=external users, 2=both
@@ -365,7 +365,7 @@ class modMyModule extends DolibarrModules
'url' => '/mymodule/myobject_card.php?action=create',
'langs' => 'mymodule@mymodule', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
'position' => 1000 + $r,
'enabled' => 'isModEnabled("mymodule")', // Define condition to show or hide menu entry. Use 'isModEnabled("mymodule")' if entry must be visible if module is enabled. Use '$leftmenu==\'system\'' to show if leftmenu system is selected.
'enabled' => "isModEnabled('mymodule')", // Define condition to show or hide menu entry. Use isModEnabled("mymodule") if entry must be visible if module is enabled. Use '$leftmenu==\'system\'' to show if leftmenu system is selected.
'perms' => '$user->hasRight("mymodule", "myobject", "write")'
'target' => '',
'user' => 2, // 0=Menu for internal users, 1=external users, 2=both
@@ -380,7 +380,7 @@ class modMyModule extends DolibarrModules
'url' => '/mymodule/myobject_list.php',
'langs' => 'mymodule@mymodule', // Lang file to use (without .lang) by module. File must be in langs/code_CODE/ directory.
'position' => 1000 + $r,
'enabled' => 'isModEnabled("mymodule")', // Define condition to show or hide menu entry. Use 'isModEnabled("mymodule")' if entry must be visible if module is enabled.
'enabled' => "isModEnabled('mymodule')", // Define condition to show or hide menu entry. Use isModEnabled("mymodule") if entry must be visible if module is enabled.
'perms' => '$user->hasRight("mymodule", "myobject", "read")'
'target' => '',
'user' => 2, // 0=Menu for internal users, 1=external users, 2=both

View File

@@ -241,12 +241,12 @@ class Societe extends CommonObject
'outstanding_limit' => array('type' => 'double(24,8)', 'label' => 'OutstandingBill', 'enabled' => 1, 'visible' => -1, 'position' => 310, 'isameasure' => 1),
'order_min_amount' => array('type' => 'double(24,8)', 'label' => 'Order min amount', 'enabled' => 'isModEnabled("order") && !empty($conf->global->ORDER_MANAGE_MIN_AMOUNT)', 'visible' => -1, 'position' => 315, 'isameasure' => 1),
'supplier_order_min_amount' => array('type' => 'double(24,8)', 'label' => 'Supplier order min amount', 'enabled' => 'isModEnabled("order") && !empty($conf->global->ORDER_MANAGE_MIN_AMOUNT)', 'visible' => -1, 'position' => 320, 'isameasure' => 1),
'fk_shipping_method' => array('type' => 'integer', 'label' => 'Fk shipping method', 'enabled' => 1, 'visible' => -1, 'position' => 330),
'tva_assuj' => array('type' => 'tinyint(4)', 'label' => 'Tva assuj', 'enabled' => 1, 'visible' => -1, 'position' => 335),
'localtax1_assuj' => array('type' => 'tinyint(4)', 'label' => 'Localtax1 assuj', 'enabled' => 1, 'visible' => -1, 'position' => 340),
'localtax1_value' => array('type' => 'double(6,3)', 'label' => 'Localtax1 value', 'enabled' => 1, 'visible' => -1, 'position' => 345),
'localtax2_assuj' => array('type' => 'tinyint(4)', 'label' => 'Localtax2 assuj', 'enabled' => 1, 'visible' => -1, 'position' => 350),
'localtax2_value' => array('type' => 'double(6,3)', 'label' => 'Localtax2 value', 'enabled' => 1, 'visible' => -1, 'position' => 355),
'fk_shipping_method' => array('type' => 'integer', 'label' => 'ShippingMode', 'enabled' => 1, 'visible' => -1, 'position' => 330),
'tva_assuj' => array('type' => 'tinyint(4)', 'label' => 'VATIsUsed', 'enabled' => 1, 'visible' => -1, 'position' => 335),
'localtax1_assuj' => array('type' => 'tinyint(4)', 'label' => 'LocalTax1IsUsed', 'enabled' => 1, 'visible' => -1, 'position' => 340),
'localtax1_value' => array('type' => 'double(6,3)', 'label' => 'LocalTax1 value', 'enabled' => 1, 'visible' => -1, 'position' => 345),
'localtax2_assuj' => array('type' => 'tinyint(4)', 'label' => 'LocalTax2IsUsed', 'enabled' => 1, 'visible' => -1, 'position' => 350),
'localtax2_value' => array('type' => 'double(6,3)', 'label' => 'LocalTax2 value', 'enabled' => 1, 'visible' => -1, 'position' => 355),
'vat_reverse_charge' => array('type' => 'tinyint(4)', 'label' => 'Vat reverse charge', 'enabled' => 1, 'visible' => -1, 'position' => 335),
'barcode' => array('type' => 'varchar(255)', 'label' => 'Barcode', 'enabled' => 1, 'visible' => -1, 'position' => 360),
'price_level' => array('type' => 'integer', 'label' => 'Price level', 'enabled' => '$conf->global->PRODUIT_MULTIPRICES || getDolGlobalString("PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES") || getDolGlobalString("PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES")', 'visible' => -1, 'position' => 365),

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Calque_2" data-name="Calque 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 121 106.77">
<defs>
<style>
.cls-1 {
fill: #a05a2c;
}
.cls-1, .cls-2 {
stroke-width: 0px;
}
.cls-2 {
fill: #263c5c;
}
</style>
</defs>
<g id="Calque_1-2" data-name="Calque 1">
<path class="cls-2" d="m60.44,75.48h1.41c4.97,0,8.51-1.03,10.64-3.1,2.12-2.07,3.35-5.7,3.68-10.89.44-8.44,1.23-14.38,2.36-17.8,1.13-3.42,3.44-6.82,6.91-10.18,2.91-2.83,6.37-4.98,10.36-6.45-2.98-5.59-7.11-10.52-12.39-14.78C73.21,4.09,59.1,0,41.09,0H0v106.02h31.53V30.45h10.64c18.07,0,27.11,7.22,27.11,21.67,0,15.38-9.19,23.07-27.57,23.07h-5.05v31.14c4.35.21,7.48.31,9.4.31,5.08,0,9.88-.49,14.38-1.48v-29.68Z"/>
<path class="cls-1" d="m121,27.8v33.4c-4.09,0-6.9.76-8.43,2.29-1.53,1.53-2.5,4.54-2.91,9.05-1.35,14.96-7.15,25.53-17.4,31.69-5.44,3.21-13.15,2.47-23.14,2.47h-4.04v-27.47h1.32c4.66,0,7.99-1.9,9.98-3.84,1.99-1.94,3.15-5.35,3.46-10.21.41-7.92,1.15-16.29,2.21-19.5,1.06-3.21,3.22-6.39,6.49-9.55,5.7-5.54,13.59-8.31,23.69-8.31h8.78Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -7611,11 +7611,11 @@ select.multiselectononeline {
{
/* CSS to have the dropdown boxes larger that the input search area */
.select2-container.select2-container--open:not(.graphtype, .limit, .combolargeelem):not(.yesno) .select2-dropdown.ui-dialog {
min-width: 230px !important;
min-width: 260px !important;
}
.select2-container.select2-container--open:not(.graphtype, .limit, .combolargeelem):not(.yesno) .select2-dropdown--below:not(.onrightofpage),
.select2-container.select2-container--open:not(.graphtype, .limit, .combolargeelem):not(.yesno) .select2-dropdown--above:not(.onrightofpage) {
min-width: 230px !important;
min-width: 260px !important;
}
.onrightofpage span.select2-dropdown.ui-dialog.select2-dropdown--below,
.onrightofpage span.select2-dropdown.ui-dialog.select2-dropdown--above {

View File

@@ -7457,11 +7457,11 @@ select.multiselectononeline {
{
/* CSS to have the dropdown boxes larger that the input search area */
.select2-container.select2-container--open:not(.graphtype, .limit, .combolargeelem):not(.yesno) .select2-dropdown.ui-dialog {
min-width: 240px !important;
min-width: 260px !important;
}
.select2-container.select2-container--open:not(.graphtype, .limit, .combolargeelem):not(.yesno) .select2-dropdown--below:not(.onrightofpage),
.select2-container.select2-container--open:not(.graphtype, .limit, .combolargeelem):not(.yesno) .select2-dropdown--above:not(.onrightofpage) {
min-width: 240px !important;
min-width: 260px !important;
}
.onrightofpage span.select2-dropdown.ui-dialog.select2-dropdown--below,
.onrightofpage span.select2-dropdown.ui-dialog.select2-dropdown--above {

View File

@@ -1308,7 +1308,7 @@ if ($action == 'create' || $action == 'adduserldap') {
print '<input type="hidden" name="user_mobile" value="'.dol_escape_htmltag($ldap_mobile).'">';
print $ldap_mobile;
} else {
print '<input class="maxwidth200 widthcentpercentminusx" type="text" name="user_mobile" value="'.dol_escape_htmltag(GETPOST('user_mobile', 'alphanohtml')).'">';
print '<input class="maxwidth200 widthcentpercentminusx" type="text" name="user_mobile" value="'.dol_escape_htmltag(GETPOST('user_mobile', 'alphanohtml')).'" spellcheck="false">';
}
print '</td></tr>';
@@ -1332,7 +1332,7 @@ if ($action == 'create' || $action == 'adduserldap') {
print '<input type="hidden" name="email" value="'.dol_escape_htmltag($ldap_mail).'">';
print $ldap_mail;
} else {
print '<input type="text" name="email" class="maxwidth500 widthcentpercentminusx" value="'.dol_escape_htmltag(GETPOST('email', 'alphanohtml')).'">';
print '<input type="text" name="email" class="maxwidth500 widthcentpercentminusx" value="'.dol_escape_htmltag(GETPOST('email', 'alphanohtml')).'" spellcheck="false">';
}
print '</td></tr>';
@@ -2377,7 +2377,7 @@ if ($action == 'create' || $action == 'adduserldap') {
print "<tr>".'<td><span class="fieldrequired">'.$langs->trans("Login").'</span></td>';
print '<td>';
if ($user->admin && !$object->ldap_sid) {
print '<input maxlength="50" type="text" class="flat" name="login" value="'.$object->login.'">';
print '<input maxlength="50" type="text" class="flat" name="login" value="'.$object->login.'" spellcheck="false">';
} else {
print '<input type="hidden" name="login" value="'.$object->login.'">';
print $object->login;
@@ -2769,7 +2769,7 @@ if ($action == 'create' || $action == 'adduserldap') {
print '<td>';
print img_picto('', 'phoning_mobile', 'class="pictofixedwidth"');
if ($permissiontoedit && empty($object->ldap_sid)) {
print '<input type="text" name="user_mobile" class="flat maxwidth200 widthcentpercentminusx" value="'.$object->user_mobile.'">';
print '<input type="text" name="user_mobile" class="flat maxwidth200 widthcentpercentminusx" value="'.$object->user_mobile.'" spellcheck="false">';
} else {
print '<input type="hidden" name="user_mobile" value="'.$object->user_mobile.'">';
print $object->user_mobile;

View File

@@ -718,7 +718,7 @@ if (!empty($id) || !empty($ref)) {
} ?>
<tr>
<td><label for="reference"><?php echo $langs->trans('Reference') ?></label></td>
<td><input type="text" id="reference" name="reference" value="<?php echo trim($reference) ?>"></td>
<td><input type="text" id="reference" name="reference" value="<?php echo trim($reference) ?>" spellcheck="false"></td>
</tr>
<?php
if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {

View File

@@ -0,0 +1,187 @@
# GET mass mailings
GET http://{{hostnport}}/api/index.php/mailings
HTTP 200
# GET mass sorted mailings
GET http://{{hostnport}}/api/index.php/mailings?sortfield=t.rowid&sortorder=ASC
HTTP 200
# GET mass sorted mailings
GET http://{{hostnport}}/api/index.php/mailings?sortfield=t.rowid&sortorder=ASC
HTTP 200
# GET with limit and page
GET http://{{hostnport}}/api/index.php/mailings?limit=100&page=1
HTTP 200
# GET with fk_project
GET http://{{hostnport}}/api/index.php/mailings?fk_projects=1
HTTP 200
# GET with properties=id%2Cstatus
GET http://{{hostnport}}/api/index.php/mailings?properties=id%2Cstatus
HTTP 200
# GET with pagination_data=false
GET http://{{hostnport}}/api/index.php/mailings?pagination_data=false
HTTP 200
# GET with pagination_data=true
GET http://{{hostnport}}/api/index.php/mailings?pagination_data=true
HTTP 200
[Asserts]
jsonpath "$.data" exists
jsonpath "$.pagination.total" >= 0
jsonpath "$.pagination.page" >= 0
jsonpath "$.pagination.page_count" >= 0
jsonpath "$.pagination.limit" == 100
# GET mailing with ID 0 - which should not exist
GET http://{{hostnport}}/api/index.php/mailings/0
HTTP 404
{"error":{"code":404,"message":"Not Found: Mass mailing not found, id=0"}}
# POST {}
POST http://{{hostnport}}/api/index.php/mailings
{}
HTTP 400
{"error":{"code":400,"message":"Bad Request: title field missing"}}
# DELETE
DELETE http://{{hostnport}}/api/index.php/mailings/
HTTP 405
# DELETE mailing with ID 0 - which should not exist
DELETE http://{{hostnport}}/api/index.php/mailings/0
HTTP 404
{"error":{"code":404,"message":"Not Found: Mass mailing not found, id=0"}}
# PUT
PUT http://{{hostnport}}/api/index.php/mailings/
{}
HTTP 405
# PUT
PUT http://{{hostnport}}/api/index.php/mailings/0
{}
HTTP 404
{"error":{"code":404,"message":"Not Found: Mass mailing not found, id=0"}}
DELETE http://{{hostnport}}/api/index.php/mailings//deleteTargets
HTTP 400
# DELETE targets of mailing with ID 0 - which should not exist
DELETE http://{{hostnport}}/api/index.php/mailings/0/deleteTargets
HTTP 404
{"error":{"code":404,"message":"Not Found: Mass mailing not found, id=0"}}
PUT http://{{hostnport}}/api/index.php/mailings//resetTargetsStatus
HTTP 400
# reset target status of mailing with ID 0 - which should not exist
PUT http://{{hostnport}}/api/index.php/mailings/0/resetTargetsStatus
HTTP 404
{"error":{"code":404,"message":"Not Found: Mass mailing not found, id=0"}}
PUT http://{{hostnport}}/api/index.php/mailings//settodraft
HTTP 400
# set mailing to draft with ID 0 - which should not exist
PUT http://{{hostnport}}/api/index.php/mailings/0/settodraft
HTTP 404
{"error":{"code":404,"message":"Not Found: Mass mailing not found, id=0"}}
PUT http://{{hostnport}}/api/index.php/mailings//validate
HTTP 400
# validate mailing with ID 0 - which should not exist
PUT http://{{hostnport}}/api/index.php/mailings/0/validate
HTTP 404
{"error":{"code":404,"message":"Not Found: Mass mailing not found, id=0"}}
GET http://{{hostnport}}/api/index.php/mailings/clone/
HTTP 400
GET http://{{hostnport}}/api/index.php/mailings/clone/0
HTTP 404
{"error":{"code":404,"message":"Not Found: Mass mailing to clone not found, id=0"}}
GET http://{{hostnport}}/api/index.php/mailings//targets
HTTP 400
# get targets of mailing with ID 0 - which should not exist
GET http://{{hostnport}}/api/index.php/mailings/0/targets
HTTP 404
{"error":{"code":404,"message":"Not Found: Mass mailing not found, id=0"}}
# get a target in a mailing
GET http://{{hostnport}}/api/index.php/mailings/0/getTarget/0
HTTP 404
# get a target in a mailing
PUT http://{{hostnport}}/api/index.php/mailings/0/updateTarget/0
{}
HTTP 404
# Delete a target in a mailing
DELETE http://{{hostnport}}/api/index.php/mailings/0/deleteTarget/0
{}
HTTP 404
# Create a target in a mailing
POST http://{{hostnport}}/api/index.php/mailings/0/createTarget
{}
HTTP 400
# Create a target in a mailing
POST http://{{hostnport}}/api/index.php/mailings/0/createTarget
{
"statut": "0",
"status": "0",
"lastname": "Example",
"firstname": "Bad",
"date_modification": 1759051258,
"tms": 1759051258,
"fk_mailing": "0",
"fk_contact": "0",
"email": "bad@example.com",
"other": "",
"tag": "acaa266a6b12a85a7db47a377a10333b",
"source_url": "",
"source_id": null,
"source_type": "file",
"date_envoi": "",
"error_text": null
}
HTTP 404
# Create a target in a mailing
POST http://{{hostnport}}/api/index.php/mailings/0/createTarget
{
"id": "0"
}
HTTP 400
{"error":{"code":400,"message":"Bad Request: fk_mailing field missing"}}
# Create a target in a mailing
POST http://{{hostnport}}/api/index.php/mailings/0/createTarget
{
"id": "0",
"fk_mailing" : "0"
}
HTTP 400
{"error":{"code":400,"message":"Bad Request: email field missing"}}
# Create a target in a mailing
POST http://{{hostnport}}/api/index.php/mailings/0/createTarget
{
"id": "0",
"email": "bad@example.com",
"fk_mailing" : "0"
}
HTTP 400
{"error":{"code":400,"message":"Bad Request: Creating with id field is forbidden"}}

View File

@@ -17,14 +17,14 @@ if [[ "" != "${DOLISUBURL}" ]]; then
fi
echo "First we run tests that do not require authentication"
find api/ gui/ public/ -type f -iname '00*.hurl' -exec hurl --variable "hostnport=${hostnport}" --test "{}" \;
find api/ gui/ public/ -type f -iname '00*.hurl' -exec hurl --variable "hostnport=${hostnport}" --test "{}" + || exit 1
# Now we get ready to run tests that do require authentication
if [[ -z ${DOLAPIKEY+x} ]]; then
echo "DOLAPIKEY bash variable is unset, no API tests that require authentication"
else
echo "Now we are ready to run API tests that do require authentication"
find api/ -type f -iname '10*.hurl' -not -iname '00*.hurl' -exec hurl --variable "hostnport=${hostnport}" --header "${DOLAPIKEY}" --test "{}" \;
find api/ -type f -iname '10*.hurl' -not -iname '00*.hurl' -exec hurl --variable "hostnport=${hostnport}" --header "${DOLAPIKEY}" --test "{}" + || exit 2
fi
./save_login_cookie.sh
@@ -32,5 +32,5 @@ if [[ -z ${COOKIEJAR+x} ]]; then
COOKIEJAR=/tmp/cookie.jar
fi
echo "Now we are ready to run GUI tests that do require authentication"
find gui/ -type f -iname '10*.hurl' -not -iname 'save_login_cookie.hurl' -not -iname '00*.hurl' -exec hurl --variable "hostnport=${hostnport}" --cookie "${COOKIEJAR}" --test "{}" \;
find gui/ -type f -iname '10*.hurl' -not -iname 'save_login_cookie.hurl' -not -iname '00*.hurl' -exec hurl --variable "hostnport=${hostnport}" --cookie "${COOKIEJAR}" --test "{}" + || exit 3
rm -rf "${COOKIEJAR}"

View File

@@ -17,10 +17,12 @@ if [[ "" != "${DOLISUBURL}" ]]; then
fi
if [[ -z ${DOLIUSERNAME+x} ]]; then
read -rp "Your Dolibarr Username: " DOLIUSERNAME
echo "To do GUI tests we need:"
read -rp " Your Dolibarr Username: " DOLIUSERNAME
fi
if [[ -z ${DOLIPASSWORD+x} ]]; then
read -rsp "Your Dolibarr Password: " DOLIPASSWORD
echo "To do GUI tests we need:"
read -rsp " Your Dolibarr Password: " DOLIPASSWORD
echo ""
fi