Compare commits

..

4 Commits

Author SHA1 Message Date
Laurent Destailleur
e2595b5117 Fix LNE 2025-12-02 22:37:49 +01:00
Laurent Destailleur
793d846d94 Debug v23 2025-12-02 21:40:15 +01:00
Laurent Destailleur
10b087e209 Fix yearly events and duplicate recurring events 2025-12-02 21:33:09 +01:00
Laurent Destailleur
e60de6525b WIP Loi finance 2025-12-02 20:29:20 +01:00
17 changed files with 213 additions and 93 deletions

View File

@@ -30,12 +30,6 @@
// Load Dolibarr environment
require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/defaultvalues.class.php';
require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -43,6 +37,11 @@ require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/agenda.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/defaultvalues.class.php';
require_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
if (!$user->admin) {
accessforbidden();

View File

@@ -34,10 +34,6 @@ if (!defined('NOTOKENRENEWAL')) {
// Load Dolibarr environment
require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -45,6 +41,9 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
// Load translation files required by the page
$langs->loadLangs(array('errors', 'admin', 'modulebuilder', 'exports'));
@@ -268,7 +267,7 @@ $head = modulehelp_prepare_head($objMod);
// Check filters
$modulename = $objMod->getName();
$moduledesc = $objMod->getDesc();
$moduledesc = $objMod->getDesc(1);
$moduleauthor = $objMod->getPublisher();
$moduledir = strtolower(preg_replace('/^mod/i', '', get_class($objMod)));

View File

@@ -4,7 +4,7 @@ BLOCKED LOG
## Feature
This module tracks, in real time, some events into a non reversible log (that you can't modify once recorded) into a block chain.
This module provides compatibility with requirements of laws of some countries (like France with the law Finance 2016 - Norme NF525).
This module provides compatibility with requirements of laws of some countries (like France with the Law Finance or Spain with VeriFactu).
**The tracked events are:**
@@ -17,6 +17,3 @@ You can also read and search into this dedicated log.
All record in the log are linked with the previous one in a blockchain, and content of the record is part of the
signature included into the link, so, once the module is activated, it is no more possible to erase or modify a record without corrupting all the chain.

View File

@@ -30,6 +30,7 @@ require '../../main.inc.php';
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
@@ -90,26 +91,39 @@ $form = new Form($db);
$block_static = new BlockedLog($db);
$block_static->loadTrackedEvents();
$title = $langs->trans("BlockedLogSetup");
$title = $langs->trans("ModuleSetup").' '.$langs->trans('BlockedLog');
$help_url="EN:Module_Unalterable_Archives_-_Logs|FR:Module_Archives_-_Logs_Inaltérable";
llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-blockedlog page-admin_blockedlog');
$linkback = '';
if ($withtab) {
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php').'">'.$langs->trans("BackToModuleList").'</a>';
$linkback = '<a href="'.dolBuildUrl($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php', ['restore_lastsearch_values' => 1]).'">'.img_picto($langs->trans("BackToModuleList"), 'back', 'class="pictofixedwidth"').'<span class="hideonsmartphone">'.$langs->trans("BackToModuleList").'</span></a>';
}
print load_fiche_titre($langs->trans("ModuleSetup").' '.$langs->trans('BlockedLog'), $linkback, 'blockedlog');
$morehtmlcenter = '';
$registrationnumber = getHashUniqueIdOfRegistration();
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
print load_fiche_titre($title, $linkback, 'blockedlog', 0, '', '', $morehtmlcenter);
if ($withtab) {
$head = blockedlogadmin_prepare_head(GETPOST('withtab', 'alpha'));
print dol_get_fiche_head($head, 'blockedlog', '', -1);
}
print $texttop;
print '<br><br>';
print '<span class="opacitymedium">'.$langs->trans("BlockedLogDesc")."</span><br>\n";
if ($mysoc->country_code == 'FR') {
$htmltext = $langs->trans("UnalterableLogTool1FR").'<br>';
}
print info_admin($htmltext, 0, 0, 'warning');
print '<br>';
print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table

View File

@@ -426,19 +426,26 @@ if (!is_array($blocks)) {
$linkback = '';
if (GETPOST('withtab', 'alpha')) {
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php').'">'.$langs->trans("BackToModuleList").'</a>';
$linkback = '<a href="'.dolBuildUrl($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php', ['restore_lastsearch_values' => 1]).'">'.img_picto($langs->trans("BackToModuleList"), 'back', 'class="pictofixedwidth"').'<span class="hideonsmartphone">'.$langs->trans("BackToModuleList").'</span></a>';
}
print load_fiche_titre($title, $linkback, 'blockedlog');
$morehtmlcenter = '';
$registrationnumber = getHashUniqueIdOfRegistration();
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
print load_fiche_titre($title, $linkback, 'blockedlog', 0, '', '', $morehtmlcenter);
$head = blockedlogadmin_prepare_head(GETPOST('withtab', 'alpha'));
print dol_get_fiche_head($head, 'archives', '', -1);
print $texttop;
print '<br><br>';
print '<div class="opacitymedium hideonsmartphone justify">';
print $langs->trans("ArchivesDesc")."<br>";
print "<br>\n";
print "</div>\n";
@@ -451,7 +458,7 @@ if ($mysoc->country_code == 'FR') {
//$htmltext .= $langs->trans("UnalterableLogTool1");
//$htmltext .= $langs->trans("UnalterableLogTool3")."<br>";
print info_admin($htmltext);
print info_admin($htmltext, 0, 0, 'warning');
print '<br>';

View File

@@ -396,15 +396,23 @@ if (!is_array($blocks)) {
$linkback = '';
if (GETPOST('withtab', 'alpha')) {
$linkback = '<a href="'.($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php').'">'.$langs->trans("BackToModuleList").'</a>';
$linkback = '<a href="'.dolBuildUrl($backtopage ? $backtopage : DOL_URL_ROOT.'/admin/modules.php', ['restore_lastsearch_values' => 1]).'">'.img_picto($langs->trans("BackToModuleList"), 'back', 'class="pictofixedwidth"').'<span class="hideonsmartphone">'.$langs->trans("BackToModuleList").'</span></a>';
}
print load_fiche_titre($title, $linkback, 'blockedlog');
$morehtmlcenter = '';
$registrationnumber = getHashUniqueIdOfRegistration();
$texttop = '<small class="opacitymedium">'.$langs->trans("RegistrationNumber").':</small> <small>'.dol_trunc($registrationnumber, 10).'</small>';
print load_fiche_titre($title, $linkback, 'blockedlog', 0, '', '', $morehtmlcenter);
$head = blockedlogadmin_prepare_head(GETPOST('withtab', 'alpha'));
print dol_get_fiche_head($head, 'fingerprints', '', -1);
print $texttop;
print '<br><br>';
print '<div class="opacitymedium hideonsmartphone justify">';
print $langs->trans("FingerprintsDesc")."<br>";

View File

@@ -82,7 +82,7 @@ function blockedlogadmin_prepare_head($withtabsetup)
*
* @return boolean True or false
*/
function isRegistrationRecorded()
function isRegistrationDataSaved()
{
global $mysoc;
@@ -105,24 +105,41 @@ function isRegistrationRecorded()
return true;
}
/**
* Return if the version is a candidate version to get the LNE certification and if the prerequisites are OK.
* The difference between isALNEQualifiedVersion() and isALNERunningVersion() is that this one just check if it has a sense or not to
* activate the restrictions (it is not a strict check) and the second one is a strict check to say restrictions must be enabled and can't be disabled.
* Return a hash unique identifier of the registration
*
* @return boolean True or false
* @return string Hash unique ID (used to idenfiy the registration without disclosing personal data)
*/
function isALNEQualifiedVersion()
function getHashUniqueIdOfRegistration()
{
global $conf;
return dol_hash('dolibarr'.$conf->file->instance_unique_id, 'sha256', 1);
}
/**
* Return if the version is a candidate version to get the LNE certification and if the prerequisites are OK in production to be switched to LNE certified mode.
* The difference with isALNERunningVersion() is that isALNEQualifiedVersion() just checks if it has a sense or not to activate
* the restrictions (it is not a check to say if we are or not in a mode with restrictions activated, but if we are in a context that has a sense to activate them).
* It can be used to show warnings or alerts to end users.
*
* @param int<0,1> $ignoredev Set this to 1 to ignore the fact the version is an alpha or beta version
* @param int<0,1> $ignoremodule Set this to 1 to not take into account if module BlockedLog is on, so function can be used during module activation.
* @return boolean True or false
*/
function isALNEQualifiedVersion($ignoredev = 0, $ignoremodule = 0)
{
global $mysoc;
// For Debug help: Constant set by developer to force all LNE restrictions even if country is not France so we can test them on any dev instance.
// Note that you can force, with this option, to enabling of the LNE restrictions but you can't force the disabling of the LNE restriction.
// Note that you can force, with this option, the enabling of the LNE restrictions, but there is no way to force the disabling of the LNE restriction.
if (defined('CERTIF_LNE') && (int) constant('CERTIF_LNE') === 2) {
return true;
}
if (preg_match('/\-/', DOL_VERSION)) { // This is not a stable version
if (!$ignoredev && preg_match('/\-/', DOL_VERSION)) { // This is not a stable version
return false;
}
if ($mysoc->country_code != 'FR') {
@@ -131,7 +148,7 @@ function isALNEQualifiedVersion()
if (!defined('CERTIF_LNE') || (int) constant('CERTIF_LNE') === 0) {
return false;
}
if (!isModEnabled('blockedlog')) {
if (!$ignoremodule && !isModEnabled('blockedlog')) {
return false;
}
@@ -140,16 +157,15 @@ function isALNEQualifiedVersion()
/**
* Return if the application is executed with the LNE features on.
* This function is used to disabled some features like disabling custom receipts, or showing the mandatory information "Certified LNE"
* on tickets when it is not true.
* Return if the application is executed with the LNE requirements on.
* This function can be used to disable some features like custom receipts, or to enable others like showing the information "Certified LNE".
*
* @return boolean True or false
* @return boolean True or false
*/
function isALNERunningVersion()
{
// For Debug help: Constant set by developer to force all LNE restrictions even if country is not France so we can test them on any dev instance.
// Note that you can force, with this option, to enabling of the LNE restrictions but you can't force the disabling of the LNE restriction.
// Note that you can force, with this option, the enabling of the LNE restrictions, but there is no way to force the disabling of the LNE restriction.
if (defined('CERTIF_LNE') && (int) constant('CERTIF_LNE') === 2) {
return true;
}

View File

@@ -541,22 +541,29 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
if (!$error) {
$db->begin();
$dayinyear = dol_print_date($object->datep, '%m%d');
$dayinmonth = dol_print_date($object->datep, '%d');
$dayinweek = dol_print_date($object->datep, '%w');
$selectedrecurrulefreq = 'no';
$selectedrecurrulebyyearmonthday = '';
$selectedrecurrulebymonthday = '';
$selectedrecurrulebyday = '';
$object->recurrule = GETPOSTISSET('recurrulefreq') ? "FREQ=".GETPOST('recurrulefreq', 'alpha') : "";
$object->recurrule .= (GETPOST('recurrulefreq', 'alpha') == 'YEARLY') ? "_BYYEARMONTHDAY".((int) $dayinyear) : "";
$object->recurrule .= (GETPOST('recurrulefreq', 'alpha') == 'MONTHLY') ? "_BYMONTHDAY".((int) $dayinmonth) : "";
$object->recurrule .= (GETPOST('recurrulefreq', 'alpha') == 'WEEKLY') ? "_BYDAY".((int) $dayinweek) : "";
$reg1 = [];
$reg2 = [];
$reg3 = [];
$reg4 = [];
if ($object->recurrule && preg_match('/FREQ=([A-Z]+)/i', $object->recurrule, $reg1)) {
$selectedrecurrulefreq = $reg1[1];
}
if ($object->recurrule && preg_match('/FREQ=YEARLY.*BYYEARMONTHDAY(\d+)/i', $object->recurrule, $reg4)) {
$selectedrecurrulebyyearmonthday = (int) $reg4[1];
}
if ($object->recurrule && preg_match('/FREQ=MONTHLY.*BYMONTHDAY(\d+)/i', $object->recurrule, $reg2)) {
$selectedrecurrulebymonthday = (int) $reg2[1];
}
@@ -689,10 +696,9 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
if ($res <= 0) {
// If error
$db->rollback();
$langs->load("errors");
$error = $langs->trans('ErrorReminderActionCommCreation');
setEventMessages($error, null, 'errors');
$error++;
setEventMessages($langs->trans('ErrorReminderActionCommCreation'), null, 'errors');
$action = 'create';
$donotclearsession = 1;
break;
@@ -707,13 +713,6 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
*/
$moreparam .= ($moreparam ? '&' : '').'disabledefaultvalues=1';
if ($error) {
$db->rollback();
} else {
$db->commit();
}
// if (!empty($backtopage)) {
// dol_syslog("Back to ".$backtopage.($moreparam ? (preg_match('/\?/', $backtopage) ? '&'.$moreparam : '?'.$moreparam) : ''));
// header("Location: ".$backtopage.($moreparam ? (preg_match('/\?/', $backtopage) ? '&'.$moreparam : '?'.$moreparam) : ''));
@@ -725,23 +724,24 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
// exit;
} else {
// If error
$db->rollback();
$langs->load("errors");
$error = $langs->trans($object->error);
setEventMessages($error, null, 'errors');
$error++;
setEventMessages($langs->trans($object->error), null, 'errors');
$action = 'create';
$donotclearsession = 1;
}
} else {
$db->rollback();
$error++;
setEventMessages($object->error, $object->errors, 'errors');
$action = 'create';
$donotclearsession = 1;
}
// Manage other events in case of recurring event
if (!$error && $eventisrecurring) {
$dayoffset = 0;
$monthoffset = 0;
$yearoffset = 0;
// We set first date of recurrence and offsets
if ($selectedrecurrulefreq == 'WEEKLY' && !empty($selectedrecurrulebyday)) {
$firstdatearray = dol_get_first_day_week(GETPOSTINT("apday"), GETPOSTINT("apmonth"), GETPOSTINT("apyear"));
@@ -749,12 +749,21 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
$datep = dol_time_plus_duree($datep, $selectedrecurrulebyday + 6, 'd');//We begin the week after
$dayoffset = 7;
$monthoffset = 0;
$yearoffset = 0;
} elseif ($selectedrecurrulefreq == 'MONTHLY' && !empty($selectedrecurrulebymonthday)) {
$firstday = $selectedrecurrulebymonthday;
$firstmonth = GETPOST("apday") > $selectedrecurrulebymonthday ? GETPOSTINT("apmonth") + 1 : GETPOSTINT("apmonth");//We begin the week after
$firstmonth = GETPOST("apday") > $selectedrecurrulebymonthday ? GETPOSTINT("apmonth") + 1 : GETPOSTINT("apmonth");//We begin the month after
$datep = dol_mktime($fulldayevent ? 0 : GETPOSTINT("aphour"), $fulldayevent ? 0 : GETPOSTINT("apmin"), $fulldayevent ? 0 : GETPOSTINT("apsec"), $firstmonth, $firstday, GETPOSTINT("apyear"), $tzforfullday ? $tzforfullday : 'tzuserrel');
$datep = dol_time_plus_duree($datep, 1, 'm');//We begin the month after
$dayoffset = 0;
$monthoffset = 1;
$yearoffset = 0;
} elseif ($selectedrecurrulefreq == 'YEARLY' && !empty($selectedrecurrulebyyearmonthday)) {
$datep = dol_mktime($fulldayevent ? 0 : GETPOSTINT("aphour"), $fulldayevent ? 0 : GETPOSTINT("apmin"), $fulldayevent ? 0 : GETPOSTINT("apsec"), GETPOSTINT("apmonth"), GETPOSTINT("apday"), GETPOSTINT("apyear"), $tzforfullday ? $tzforfullday : 'tzuserrel');
$datep = dol_time_plus_duree($datep, 1, 'y');//We begin the year after
$dayoffset = 0;
$monthoffset = 0;
$yearoffset = 1;
} else {
$error++;
}
@@ -809,10 +818,9 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
if ($res <= 0) {
// If error
$db->rollback();
$error++;
$langs->load("errors");
$error = $langs->trans('ErrorReminderActionCommCreation');
setEventMessages($error, null, 'errors');
setEventMessages($langs->trans('ErrorReminderActionCommCreation'), null, 'errors');
$action = 'create';
$donotclearsession = 1;
break;
@@ -826,15 +834,9 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
$moreparam .= ($moreparam ? '&' : '').'search_filtert='.$object->userownerid;
*/
$moreparam .= ($moreparam ? '&' : '').'disabledefaultvalues=1';
if ($error) {
$db->rollback();
} else {
$db->commit();
}
} else {
// If error
$db->rollback();
$error++;
$langs->load("errors");
$error = $langs->trans($finalobject->error);
setEventMessages($error, null, 'errors');
@@ -842,7 +844,7 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
$donotclearsession = 1;
}
} else {
$db->rollback();
$error++;
setEventMessages($finalobject->error, $finalobject->errors, 'errors');
$action = 'create';
$donotclearsession = 1;
@@ -856,10 +858,19 @@ if (empty($reshook) && $action == 'add' && $usercancreate) {
// increment date for recurrent events
$datep = dol_time_plus_duree($datep, $dayoffset, 'd');
$datep = dol_time_plus_duree($datep, $monthoffset, 'm'); // @phan-suppress-current-line PhanPluginSuspiciousParamOrder
$datep = dol_time_plus_duree($datep, $yearoffset, 'y'); // @phan-suppress-current-line PhanPluginSuspiciousParamOrder
$datef = dol_time_plus_duree($datef, $dayoffset, 'd');
$datef = dol_time_plus_duree($datef, $monthoffset, 'm'); // @phan-suppress-current-line PhanPluginSuspiciousParamOrder
$datef = dol_time_plus_duree($datef, $yearoffset, 'y'); // @phan-suppress-current-line PhanPluginSuspiciousParamOrder
}
}
if ($error) {
$db->rollback();
} else {
$db->commit();
}
if (!empty($backtopage) && !$error) {
dol_syslog("Back to ".$backtopage.($moreparam ? (preg_match('/\?/', $backtopage) ? '&'.$moreparam : '?'.$moreparam) : ''));
header("Location: ".$backtopage.($moreparam ? (preg_match('/\?/', $backtopage) ? '&'.$moreparam : '?'.$moreparam) : ''));
@@ -1335,6 +1346,7 @@ $formproject = new FormProjets($db);
$arrayrecurrulefreq = array(
'no' => $langs->trans("OnceOnly"),
'YEARLY' => $langs->trans("EveryYear"),
'MONTHLY' => $langs->trans("EveryMonth"),
'WEEKLY' => $langs->trans("EveryWeek")
// 'DAILY'=>$langs->trans("EveryDay")
@@ -1521,9 +1533,11 @@ if ($action == 'create') {
print '<input type="hidden" name="recurid" value="'.(empty($object->recurid) ? '' : $object->recurid).'">';
$selectedrecurrulefreq = 'no';
$selectedrecurrulebyyearmonthday = '';
$selectedrecurrulebymonthday = '';
$selectedrecurrulebyday = '';
$object->recurrule = GETPOSTISSET('recurrulefreq') ? "FREQ=".GETPOST('recurrulefreq', 'alpha') : "";
$object->recurrule .= GETPOSTISSET('BYYEARMONTHDAY') ? "_BYYEARMONTHDAY".GETPOST('BYYEARMONTHDAY', 'alpha') : "";
$object->recurrule .= GETPOSTISSET('BYMONTHDAY') ? "_BYMONTHDAY".GETPOST('BYMONTHDAY', 'alpha') : "";
$object->recurrule .= GETPOSTISSET('BYDAY') ? "_BYDAY".GETPOST('BYDAY', 'alpha') : "";
@@ -1532,6 +1546,9 @@ if ($action == 'create') {
if ($object->recurrule && preg_match('/FREQ=([A-Z]+)/i', $object->recurrule, $reg)) {
$selectedrecurrulefreq = $reg[1];
}
if ($object->recurrule && preg_match('/FREQ=YEARLY.*BYYEARMONTHDAY(\d+)/i', $object->recurrule, $reg)) {
$selectedrecurrulebyyearmonthday = (int) $reg[1];
}
if ($object->recurrule && preg_match('/FREQ=MONTHLY.*BYMONTHDAY(\d+)/i', $object->recurrule, $reg)) {
$selectedrecurrulebymonthday = (int) $reg[1];
}
@@ -1571,16 +1588,25 @@ if ($action == 'create') {
console.log("reg1: " + "<?php echo $selectedrecurrulefreq; ?>");
console.log("reg2: " + "<?php echo $selectedrecurrulebymonthday; ?>");
console.log("reg3: " + "<?php echo $selectedrecurrulebyday; ?>");
console.log("reg4: " + "<?php echo $selectedrecurrulebyyearmonthday; ?>");
console.log("selectedrulefreq: " + "<?php echo $selectedrecurrulefreq; ?>");
if (jQuery("#recurrulefreq").val() == 'MONTHLY') {
if (jQuery("#recurrulefreq").val() == 'YEARLY') {
/* jQuery(".repeateventBYYEARMONTHDAY").css("display", "inline-block"); */ /* use this instead of show because we want inline-block and not block */
jQuery(".repeateventlimitdate").css("display", "inline-block");
jQuery(".repeateventBYMONTHDAY").hide();
jQuery(".repeateventBYDAY").hide();
} else if (jQuery("#recurrulefreq").val() == 'MONTHLY') {
/* jQuery(".repeateventBYMONTHDAY").css("display", "inline-block"); */ /* use this instead of show because we want inline-block and not block */
jQuery(".repeateventlimitdate").css("display", "inline-block");
jQuery(".repeateventBYYEARMONTHDAY").hide();
jQuery(".repeateventBYDAY").hide();
} else if (jQuery("#recurrulefreq").val() == 'WEEKLY') {
jQuery(".repeateventBYYEARMONTHDAY").hide();
jQuery(".repeateventBYMONTHDAY").hide();
/* jQuery(".repeateventBYDAY").css("display", "inline-block"); */ /* use this instead of show because we want inline-block and not block */
jQuery(".repeateventlimitdate").css("display", "inline-block");
} else {
jQuery(".repeateventBYYEARMONTHDAY").hide();
jQuery(".repeateventBYMONTHDAY").hide();
jQuery(".repeateventBYDAY").hide();
jQuery(".repeateventlimitdate").hide();
@@ -1616,7 +1642,7 @@ if ($action == 'create') {
print '<table class="border centpercent nobottom">';
// Assigned to user
print '<tr><td class="tdtop nowrap titlefieldcreate"><span>'.$langs->trans("ActionAffectedTo").'</span></td><td>';
print '<tr><td class="nowrap titlefieldcreate"><span>'.$langs->trans("ActionAffectedTo").'</span></td><td>';
$listofuserid = [];
$listofcontactid = [];
$listofotherid = [];
@@ -2248,7 +2274,7 @@ if ($id > 0 && $action != 'create') {
$listofcontactid = $object->socpeopleassigned; // Contact assigned
$listofotherid = $object->otherassigned; // Other undefined email (not used yet)
print '<tr><td class="tdtop nowrap fieldrequired">'.$langs->trans("ActionAssignedTo").'</td><td>';
print '<tr><td class="nowrap fieldrequired">'.$langs->trans("ActionAssignedTo").'</td><td>';
print '<div class="assignedtouser">';
print $form->select_dolusers_forevent(($action == 'create' ? 'add' : 'update'), 'assignedtouser', 1, [], 0, '', [], '0', 0, 0, 'u.statut:<>:0', 1, $listofuserid, $listofcontactid, $listofotherid, (int) $caneditdateorowner);
print '</div>';
@@ -2723,6 +2749,9 @@ if ($id > 0 && $action != 'create') {
if (preg_match('/FREQ=MONTHLY_BYMONTHDAY(\d+)/', $object->recurrule, $reg)) {
print $langs->trans("EveryMonth").' <span class="opacitymedium small">('.$langs->trans("DayOfMonth").' '.$reg[1].' - '.$langs->trans("Until").' '.dol_print_date($object->recurdateend, 'day').')</span>';
}
if (preg_match('/FREQ=YEARLY_BYYEARMONTHDAY(\d+)/', $object->recurrule, $reg)) {
print $langs->trans("EveryYear").' <span class="opacitymedium small">('.$langs->trans("DayOfYear").' '.$reg[1].' - '.$langs->trans("Until").' '.dol_print_date($object->recurdateend, 'day').')</span>';
}
print '</td></tr>';
}

View File

@@ -5588,7 +5588,8 @@ class Facture extends CommonInvoice
$this->fk_incoterms = 0;
$this->location_incoterms = '';
$this->pos_print_counter = 3; // Already printed 3 times
$this->pos_print_counter = 0; // Already printed 0 times
$this->email_sent_counter = 0; // Already sent by email 0 times
$this->status = 0;

View File

@@ -7124,14 +7124,14 @@ function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullp
/**
* Load a title with picto
*
* @param string $title Title to show (HTML sanitized content). Can be a string with a <br> as a substring.
* @param string $morehtmlright Added message to show on right
* @param string $picto Icon to use before title (should be a 32x32 transparent png file)
* @param string $title Title to show (HTML sanitized content). Can be a string with a <br> as a second string shown under the fmain title.
* @param string $morehtmlright Added message to show on right
* @param string $picto Icon to use before title (should be a 32x32 transparent png file)
* @param int<0,1> $pictoisfullpath 1=Icon name is a full absolute url of image
* @param string $id To force an id on html objects
* @param string $morecssontable More css on table
* @param string $morehtmlcenter Added message to show on center
* @param string $morecssonpicto More css on picto
* @param string $id To force an id on html objects
* @param string $morecssontable More css on table
* @param string $morehtmlcenter Added message to show on center
* @param string $morecssonpicto More css on picto
* @return string
* @see print_barre_liste()
*/

View File

@@ -782,9 +782,10 @@ class DolibarrModules // Can not be abstract, because we need to instantiate it
/**
* Gives the translated module description if translation exists in admin.lang or the default module description
*
* @return string Translated module description
* @param int<0,1> $foruseinpopupdesc If 1, we return a short description for use into popup window
* @return string Translated module description
*/
public function getDesc()
public function getDesc($foruseinpopupdesc = 0)
{
global $langs;
$langs->load("admin");

View File

@@ -23,6 +23,8 @@
* \brief Description and activation file for the module BlockedLog
*/
include_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php';
include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
/**
* Class to describe a BlockedLog module
@@ -51,6 +53,7 @@ class modBlockedLog extends DolibarrModules
// Module label (no space allowed), used if translation string 'ModuleXXXName' not found (where XXX is value of numeric property 'numero' of module)
$this->name = preg_replace('/^mod/i', '', get_class($this));
$this->description = "Enable a log on some business events into an unalterable log. This module may be mandatory for some countries.";
// Possible values for version are: 'development', 'experimental', 'dolibarr' or version
$this->version = 'dolibarr';
// Key used in llx_const table to save module status enabled/disabled (where MYMODULE is value of property name of module in uppercase)
@@ -173,6 +176,15 @@ class modBlockedLog extends DolibarrModules
include_once DOL_DOCUMENT_ROOT.'/core/lib/security.lib.php';
include_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
// Check that the HTTPS is forced
global $dolibarr_main_force_https;
if (isALNEQualifiedVersion(0, 1) && empty($dolibarr_main_force_https)) {
$this->error = 'Error: The HTTPS must be forced by setting the $dolibarr_main_force_https into Dolibarr conf/conf.php file to allow the use of this module in France.';
return 0;
}
// Create HMAC if it does not exists yet
$hmac_encoded_secret_key = getDolGlobalString('BLOCKEDLOG_HMAC_KEY');
if (empty($hmac_encoded_secret_key)) {
@@ -291,4 +303,30 @@ class modBlockedLog extends DolibarrModules
return $this->_remove($sql, $options);
}
/**
* Overwrite the common getDesc() method
*
* @param int<0,1> $foruseinpopupdesc If 1, we return a short description for use into popup window
* @return string Translated module description
*/
public function getDesc($foruseinpopupdesc = 0)
{
global $langs;
$langs->load("admin");
// If module description translation exists
$s = $langs->transnoentitiesnoconv("Module".$this->numero."Desc");
if ($foruseinpopupdesc) {
$langs->load("blockedlog");
$s .= '<br><br>';
if (isALNEQualifiedVersion(1, 1)) {
$s .= info_admin($langs->trans("UnalterableLogTool1FR"), 0, 0, 'warning');
}
}
return $s;
}
}

View File

@@ -171,8 +171,10 @@ OnceOnly=Once only
EveryDay=Every day
EveryWeek=Every week
EveryMonth=Every month
EveryYear=Every year
DayOfMonth=Day of month
DayOfWeek=Day of week
DayOfYear=Day of year
DateStartPlusOne=Date start + 1 hour
SetAllEventsToTodo=Set all events to todo
SetAllEventsToInProgress=Set all events to in progress

View File

@@ -4,7 +4,7 @@ Fingerprints=Archived events and fingerprints
FingerprintsDesc=This is the tool to browse or extract the unalterable logs. Unalterable logs are generated and store into a dedicated table of the systel, in real time when a business event appears.
ArchivesDesc=You can use this tool to export Unalterable Logs into archive files. They are kept into the system but you should download and copy them into external supports.
UnalterableLogTool1=The law of some countries (like in France when doing B2C), ask that you do it on a regular basis.
UnalterableLogTool1FR=In france, you MUST do it at least, once every year, we recommand to do it once per month.
UnalterableLogTool1FR=In France, you MUST generate archives, at least, once every year, we recommand to do it once per month. Also, you MUST keep these archives for at least 7 years. According to the law, It is the user's responsibility to store their archives on media that guarantee their preservation, including in the event of hardware failure, so we recommand to store them on at least 2 different physical supports.
UnalterableLogTool2=This unalterable Logs contains the history of unalterable data. It is the responsibility of the application user to ensure system backups, in order to comply with legal obligations. If you decide to secure your unalterable logs by exporting and storing archives files instead of a full database backup (tab <b>%s</b>), it is recommended to store the exported archive files in different places (Secured cloud, External USB drive, ...).
UnalterableLogTool3=Note that, there is no feature to purge this log and every change tried to be done directly into this log (by a hacker for example) will be reported with a non-valid fingerprint.
FingerprintsDesc2=If you really need to purge this table because you used your application for a demo/test purpose and want to clean your data to start your production, you can ask your reseller or integrator to reset your database (all your data will be removed).
@@ -50,6 +50,7 @@ TypeOfEvent=Type of event
TotalForAction=Total for event %s
SecretKey=Secret key
ErrorPeriodMustBePastToAllowExport=Export into archives is allowed only if period is completely past
RegistrationNumber=Registration number
## logTypes
logBILL_DELETE=Customer invoice logically deleted

View File

@@ -3731,18 +3731,18 @@ if (!function_exists("llxFooter")) {
$forceping = GETPOSTINT('forceping');
if (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || $forceping) {
$hash_unique_id = dol_hash('dolibarr'.$conf->file->instance_unique_id, 'sha256'); // Note: if the global salt changes, this hash changes too so ping may be counted twice. We don't mind. It is for statistics purpose only.
$hash_unique_id_ping = dol_hash('dolibarr'.$conf->file->instance_unique_id, 'sha256', 1);
$constanttosavelastko = 'MAIN_LAST_PING_KO_DATE';
$constanttosavefirstok = 'MAIN_FIRST_PING_OK_DATE';
$constanttosavefirstokid = 'MAIN_FIRST_PING_OK_ID';
if (!getDolGlobalString($constanttosavefirstok)
|| (!empty($conf->file->instance_unique_id) && (($hash_unique_id.' - '.DOL_VERSION) != getDolGlobalString($constanttosavefirstokid)) && (getDolGlobalString($constanttosavefirstokid) != 'disabled'))
|| (!empty($conf->file->instance_unique_id) && (($hash_unique_id_ping.' - '.DOL_VERSION) != getDolGlobalString($constanttosavefirstokid)) && (getDolGlobalString($constanttosavefirstokid) != 'disabled'))
|| $forceping) {
// No ping done if we are into an alpha version
if (strpos('alpha', DOL_VERSION) > 0 && !$forceping) {
print "\n<!-- NO JS CODE TO ENABLE the anonymous Ping. It is an alpha version -->\n";
} elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id]) || $forceping) { // Cookie is set when we uncheck the checkbox in the installation wizard.
} elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id_ping]) || $forceping) { // Cookie is set when we uncheck the checkbox in the installation wizard.
// Output code for ping
include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
@@ -3760,28 +3760,29 @@ if (!function_exists("llxFooter")) {
}
}
// Add code to force the registration if not yet done but ready (in case past submission failed)
// You can use &forceregistration=1 in parameters to force the call if the call was already sent.
// Add code to force the registration of the use of the BlockedLog module if not yet done but ready (in case past submission failed)
// You can use &forceregistration=1 in parameters to force also the recall if the call was already sent.
$forceregistration = GETPOSTINT('forceregistration');
if (isModEnabled('blockedlog') && (($_SERVER["PHP_SELF"] == DOL_URL_ROOT.'/index.php') || $forceregistration)) {
include_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
if (!isALNEQualifiedVersion()) {
print "\n<!-- NO JS CODE TO ENABLE the registration. Not a LNE qualified version -->\n";
} elseif (!isRegistrationRecorded()) {
} elseif (!isRegistrationDataSaved()) {
print "\n<!-- NO JS CODE TO ENABLE the registration. Registration data not saved -->\n";
} else {
$hash_unique_id = dol_hash('dolibarr'.$conf->file->instance_unique_id, 'sha256'); // Note: if the global salt changes, this hash changes too so ping may be counted twice. We don't mind. It is for statistics purpose only.
$hash_unique_id_registration = dol_hash('dolibarr'.$conf->file->instance_unique_id, 'sha256', 1); // Same than getHashUniqueIdOfRegistration()
$constanttosavelastko = 'MAIN_LAST_REGISTRATION_KO_DATE';
$constanttosavefirstok = 'MAIN_FIRST_REGISTRATION_OK_DATE';
$constanttosavefirstokid = 'MAIN_FIRST_REGISTRATION_OK_ID';
if (!getDolGlobalString($constanttosavefirstok)
|| (!empty($conf->file->instance_unique_id) && ($hash_unique_id.' - '.DOL_VERSION != getDolGlobalString($constanttosavefirstokid)) && (getDolGlobalString($constanttosavefirstokid) != 'disabled'))
|| (!empty($conf->file->instance_unique_id) && ($hash_unique_id_registration.' - '.DOL_VERSION != getDolGlobalString($constanttosavefirstokid)) && (getDolGlobalString($constanttosavefirstokid) != 'disabled'))
|| $forceregistration) {
// No ping done if we are into an alpha version
if (strpos('alpha', DOL_VERSION) > 0 && !$forceregistration) {
print "\n<!-- NO JS CODE TO ENABLE the registration. It is an alpha version -->\n";
} elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id]) || $forceregistration) { // Cookie is set when we uncheck the checkbox in the installation wizard.
// No registration done if we are into an alpha or beta version
if ((strpos('alpha', DOL_VERSION) > 0 || strpos('beta', DOL_VERSION) > 0) && !$forceregistration) {
print "\n<!-- NO JS CODE TO ENABLE the registration. It is an alpha or beta version -->\n";
} elseif (empty($_COOKIE['DOLINSTALLNOPING_'.$hash_unique_id_registration]) || $forceregistration) { // Cookie is set when we uncheck the checkbox in the installation wizard.
// Output code for ping
include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';

View File

@@ -42,6 +42,6 @@ define('DOL_MAJOR_VERSION', '23');
define('DOL_VERSION', constant('DOL_MAJOR_VERSION').'.'.constant('DOL_MINOR_VERSION'));
// DOL_VERSION is now a.b.c-alpha, a.b.c-beta, a.b.c-rcX or a.b.c
if (!defined('CERTIF_LNE')) {
define('CERTIF_LNE', '1'); // Set to 1 if the beta version is a candidate for certification or if the stable version has been certified. Use 2 for debug to force LNE features.
}
// Set to 1 if the beta version is a just a candidate for certification (not yet certified) or if the stable version has been certified.
// Use 2 to force LNE featuresfro debug purposes
define('CERTIF_LNE', '1');

View File

@@ -91,6 +91,13 @@ class NumberingModulesTest extends CommonClassTest
print __METHOD__." is_erasable=".$result."\n";
$this->assertGreaterThanOrEqual(1, $result, 'Test for is_erasable, 1st invoice'); // Can be deleted
// We emulate print on invoice 3 times
$localobject->pos_print_counter = 3;
$result = $localobject->is_erasable();
print __METHOD__." is_erasable=".$result."\n";
$this->assertGreaterThanOrEqual(-6, $result, 'Test for is_erasable, 1st invoice already printed'); // Can be deleted
$localobject2 = new Facture($db);
$localobject2->initAsSpecimen();
$localobject2->fetch_thirdparty();