Maxi debug blockedlog module

This commit is contained in:
Laurent Destailleur 2026-01-03 16:36:12 +01:00
parent 08f3fbd5fd
commit eaa3976b2c
10 changed files with 117 additions and 298 deletions

View file

@ -183,6 +183,7 @@ NEW: API User - Remove user from group (#35453)
NEW: Implement listTimespent method in api_projects.class.php (#36093)
NEW: qual fixes on api contract (#36066)
NEW: stock API GET movement (#36193)
NEW: action to clone ticket
WARNING:

View file

@ -155,232 +155,6 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x'
$search_array_options = array();
}
if (GETPOST('downloadcsv', 'alpha')) {
$error = 0;
$previoushash = '';
$firstid = '';
if (! (GETPOSTINT('yeartoexport') > 0)) {
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Year")), null, "errors");
$error++;
} else {
$dates = dol_get_first_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') > 0 ? GETPOSTINT('monthtoexport') : 1);
$datee = dol_get_last_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') > 0 ? GETPOSTINT('monthtoexport') : 12);
if ($datee >= dol_now()) {
setEventMessages($langs->trans("ErrorPeriodMustBePastToAllowExport"), null, "errors");
$error++;
}
// Get the ID of the first line qualified
$sql = "SELECT rowid, date_creation, tms, user_fullname, action, amounts, amounts_taxexcl, element, fk_object, date_object, ref_object, signature, fk_user, object_data";
$sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
$sql .= " WHERE entity = ".((int) $conf->entity);
// For unalterable log, we are using the date of creation of the log. Note that a bookkeeper may decide to dispatch an invoice
// on different periods for example to manage depreciation.
if (GETPOSTINT('monthtoexport') > 0 || GETPOSTINT('yeartoexport') > 0) {
$dates = dol_get_first_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') ? GETPOSTINT('monthtoexport') : 1);
$datee = dol_get_last_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') ? GETPOSTINT('monthtoexport') : 12);
$sql .= " AND date_creation BETWEEN '".$db->idate($dates)."' AND '".$db->idate($datee)."'";
}
$sql .= " ORDER BY rowid ASC"; // Required so we get the first one
$sql .= $db->plimit(1);
$res = $db->query($sql);
if ($res) {
// Make the first fetch to get first line
$obj = $db->fetch_object($res);
if ($obj) {
$firstid = $obj->rowid;
$previoushash = $block_static->getPreviousHash(0, $firstid);
} else { // If not data found for filter, we do not need previoushash neither firstid
$firstid = '';
$previoushash = 'nodata';
}
} else {
$error++;
setEventMessages($db->lasterror, null, 'errors');
}
}
if (! $error) {
// We record the export as a new line into the unalterable logs
require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
$b = new BlockedLog($db);
$object = new stdClass();
$object->id = 0;
$object->element = 'module';
$object->ref = 'systemevent';
$object->entity = $conf->entity;
$object->date = dol_now();
$object->label = 'Export unalterable logs - Period: year='.GETPOSTINT('yeartoexport').(GETPOSTINT('monthtoexport') ? ' month='.GETPOSTINT('monthtoexport') : '');
$action = 'BLOCKEDLOG_EXPORT';
$result = $b->setObjectData($object, $action, 0, $user, null);
//var_dump($b); exit;
if ($result < 0) {
setEventMessages('Failed to insert the export int the unalterable log', null, 'errors');
$error++;
}
$res = $b->create($user);
if ($res < 0) {
setEventMessages('Failed to insert the export int the unalterable log', null, 'errors');
$error++;
}
}
if (!$error) {
// Now restart request with all data, si without the limit(1) in sql request
$sql = "SELECT rowid, date_creation, tms, user_fullname, action, amounts, amounts_taxexcl, element, fk_object, date_object, ref_object,";
$sql .= " signature, fk_user, object_data, object_version, object_format, debuginfo";
$sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
$sql .= " WHERE entity = ".((int) $conf->entity);
if (GETPOSTINT('monthtoexport') > 0 || GETPOSTINT('yeartoexport') > 0) {
$dates = dol_get_first_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') ? GETPOSTINT('monthtoexport') : 1);
$datee = dol_get_last_day(GETPOSTINT('yeartoexport'), GETPOSTINT('monthtoexport') ? GETPOSTINT('monthtoexport') : 12);
$sql .= " AND date_creation BETWEEN '".$db->idate($dates)."' AND '".$db->idate($datee)."'";
}
$sql .= " ORDER BY rowid ASC"; // Required so later we can use the parameter $previoushash of checkSignature()
$resql = $db->query($sql);
if ($resql) {
$nameofdownoadedfile = "unalterable-log-archive-".$dolibarr_main_db_name."-".(GETPOSTINT('yeartoexport') > 0 ? GETPOSTINT('yeartoexport').(GETPOSTINT('monthtoexport') > 0 ? sprintf("%02d", GETPOSTINT('monthtoexport')) : '').'-' : '').dol_print_date(dol_now(), 'dayhourlog', 'gmt').'UTC-DONOTMODIFY';
$tmpfile = $conf->admin->dir_temp.'/unalterable-log-archive-tmp-'.$user->id.'.csv';
$fh = fopen($tmpfile, 'w');
// Print line with title
fwrite($fh, $langs->transnoentities('Id')
.';'.$langs->transnoentities('Date')
.';'.$langs->transnoentities('User')
.';'.$langs->transnoentities('Action')
.';'.$langs->transnoentities('Element')
.';'.$langs->transnoentities('Amounts')
.';'.$langs->transnoentities('ObjectId')
.';'.$langs->transnoentities('Date')
.';'.$langs->transnoentities('Ref')
.';'.$langs->transnoentities('Fingerprint')
.';'.$langs->transnoentities('Status')
.';'.$langs->transnoentities('Note')
.';'.$langs->transnoentities('Version')
.';'.$langs->transnoentities('FullData')
.';'.$langs->transnoentities('DebugInfo')
."\n");
$loweridinerror = 0;
$i = 0;
while ($obj = $db->fetch_object($resql)) {
// We set here all data used into signature calculation (see checkSignature method) and more
// IMPORTANT: We must have here, the same rule for transformation of data than into the fetch method (db->jdate for date, ...)
$block_static->id = $obj->rowid;
$block_static->entity = $obj->entity;
$block_static->date_creation = $db->jdate($obj->date_creation); // TODO Use gmt
$block_static->amounts_taxexcl = (float) $obj->amounts_taxexcl; // Database store value with 8 digits, we cut ending 0 them with (float)
$block_static->amounts = (float) $obj->amounts; // Database store value with 8 digits, we cut ending 0 them with (float)
$block_static->action = $obj->action;
$block_static->date_object = $db->jdate($obj->date_object); // TODO Use gmt ?
$block_static->ref_object = $obj->ref_object;
$block_static->user_fullname = $obj->user_fullname;
$block_static->object_data = $block_static->dolDecodeBlockedData($obj->object_data);
// Old hash + Previous fields concatenated = signature
$block_static->signature = $obj->signature;
$block_static->element = $obj->element; // Not in signature
$block_static->fk_object = $obj->fk_object; // Not in signature
$block_static->fk_user = $obj->fk_user; // Not in signature
$block_static->date_modification = $db->jdate($obj->tms); // Not in signature
$block_static->object_version = $obj->object_version; // Not in signature
$block_static->object_format = $obj->object_format; // Not in signature
$block_static->certified = ($obj->certified == 1);
$block_static->linktoref = $obj->linktoref;
$block_static->linktype = $obj->linktype;
$block_static->debuginfo = $obj->debuginfo;
//var_dump($block->id.' '.$block->signature, $block->object_data);
$checksignature = $block_static->checkSignature($previoushash); // If $previoushash is not defined, checkSignature will search it
if ($checksignature) {
$statusofrecord = 'Valid';
if ($loweridinerror > 0) {
$statusofrecordnote = 'ValidButFoundAPreviousKO';
} else {
$statusofrecordnote = '';
}
} else {
$statusofrecord = 'KO';
$statusofrecordnote = 'LineCorruptedOrNotMatchingPreviousOne';
$loweridinerror = $obj->rowid;
}
if ($i == 0) {
$statusofrecordnote = $langs->trans("PreviousFingerprint").': '.$previoushash.($statusofrecordnote ? ' - '.$statusofrecordnote : '');
}
fwrite($fh, $block_static->id
.';'.$block_static->date_creation
.';"'.str_replace('"', '""', $block_static->user_fullname).'";'
.$block_static->action
.';'.$block_static->element
.';'.$block_static->amounts // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
.';'.$block_static->fk_object
.';'.$block_static->date_object
.';"'.str_replace('"', '""', $block_static->ref_object).'";"'
.$block_static->signature.'";'
.$statusofrecord
.';'.$statusofrecordnote
.';'.$block_static->object_version
.';"'.str_replace('"', '""', $obj->object_data).'"' // We must the string to decode into object with dolDecodeBlockedData
.';"'.str_replace('"', '""', $block_static->debuginfo).'"'
."\n");
// Set new previous hash for next fetch
$previoushash = $obj->signature;
$i++;
}
fclose($fh);
// Calculate the md5 of the file (the last line has a return line)
$md5value = md5_file($tmpfile);
// Now add a signature to check integrity at end of file
file_put_contents($tmpfile, 'END - md5='.$md5value, FILE_APPEND);
dolChmod($tmpfile);
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"".$nameofdownoadedfile.".csv\"");
readfile($tmpfile);
exit;
} else {
setEventMessages($db->lasterror, null, 'errors');
}
}
}
/*
* View
@ -534,12 +308,12 @@ print '</td>';
// User
print '<td class="liste_titre">';
print $form->select_dolusers($search_fk_user, 'search_fk_user', 1, null, 0, '', '', '0', 0, 0, '', 0, '', 'maxwidth150');
print $form->select_dolusers($search_fk_user, 'search_fk_user', 1, null, 0, '', '', '0', 0, 0, '', 0, '', 'maxwidth100');
print '</td>';
// Module source
print '<td class="liste_titre">';
print $form->multiselectarray('search_module_source', $block_static->trackedmodules, $search_code, 0, 0, 'maxwidth150', 1);
print $form->multiselectarray('search_module_source', $block_static->trackedmodules, $search_module_source, 0, 0, 'maxwidth150', 1);
print '</td>';
// Actions code
@ -566,7 +340,7 @@ print $form->selectarray('search_showonlyerrors', $array, $search_showonlyerrors
print '</td>';
// Link to debug information object
if (getDolGlobalString('MAIN_FEATURES_LEVEL') > 0) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
if (getDolGlobalString("BLOCKEDLOG_DEBUG")) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
print '<td class="liste_titre"></td>';
}
@ -589,13 +363,14 @@ if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
print getTitleFieldOfList($langs->trans('#'), 0, $_SERVER["PHP_SELF"], 'rowid', '', $param, '', $sortfield, $sortorder, 'minwidth50 ')."\n";
print getTitleFieldOfList($langs->trans('Date'), 0, $_SERVER["PHP_SELF"], 'date_creation', '', $param, '', $sortfield, $sortorder, '')."\n";
print getTitleFieldOfList($langs->trans('Author'), 0, $_SERVER["PHP_SELF"], 'user_fullname', '', $param, '', $sortfield, $sortorder, '')."\n";
print getTitleFieldOfList($langs->trans('POS'), 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, '')."\n";
print getTitleFieldOfList($langs->trans('Action'), 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, '')."\n";
print getTitleFieldOfList($langs->trans('Ref'), 0, $_SERVER["PHP_SELF"], 'ref_object', '', $param, '', $sortfield, $sortorder, '')."\n";
print getTitleFieldOfList($langs->trans('Amount'), 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ', 0, $langs->trans("TotalTTCIfInvoiceSeeCompleteDataForDetail").'<br>'.$langs->trans("AmountInCurrency", getDolCurrency()))."\n";
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";
if (getDolGlobalString('MAIN_FEATURES_LEVEL') > 0) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
if (getDolGlobalString("BLOCKEDLOG_DEBUG")) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
print getTitleFieldOfList('', 0, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, '')."\n";
}
// Action column
@ -670,27 +445,31 @@ if (is_array($blocks)) {
}
// ID
print '<td>'.dol_escape_htmltag((string) $block->id).'</td>';
print '<td>'.dolPrintHTML((string) $block->id).'</td>';
// Date
print '<td class="nowraponall">'.dol_print_date($block->date_creation, 'dayhour').'</td>';
// User
print '<td class="tdoverflowmax200" title="'.dol_escape_htmltag($block->user_fullname).'">';
print '<td class="tdoverflowmax200" title="'.dolPrintHTMLForAttribute($block->user_fullname).'">';
//print $block->getUser()
print dol_escape_htmltag($block->user_fullname);
print dolPrintHTML($block->user_fullname);
print '</td>';
// ModulePOS
$labelofmodulesource = $block->module_source;
print '<td class="tdoverflowmax250" title="'.dolPrintHTMLForAttribute($labelofmodulesource).'">'.dolPrintHTML($labelofmodulesource).'</td>';
// Action
$labelofaction = $langs->transnoentitiesnoconv('log'.$block->action);
print '<td class="tdoverflowmax250" title="'.dol_escape_htmltag($labelofaction).'">'.dolPrintHTML($labelofaction).'</td>';
print '<td class="tdoverflowmax250" title="'.dolPrintHTMLForAttribute($labelofaction).'">'.dolPrintHTML($labelofaction).'</td>';
// Ref
print '<td class="nowraponall">';
print '<td class="nowraponall"><div class="smallheight">';
if (!empty($block->ref_object)) {
print dol_escape_htmltag($block->ref_object);
if ($block->linktype && $block->linktoref) {
if ($block->linktype == 'paymentof') {
if ($block->linktype == 'payment') {
print '<br><span class="opacitymedium small">'.$langs->trans("PaymentOf").' '.$block->linktoref.'</span>';
}
if ($block->linktype == 'replacedby') {
@ -700,7 +479,7 @@ if (is_array($blocks)) {
} else {
// Ref not stored
}
print '</td>';
print '</div></td>';
//$tmpobj = json_decode($block->object_data);
@ -810,6 +589,9 @@ if (is_array($blocks)) {
print '<td class="tdoverflowmax200">';
print '</td>';
// Module source
print '<td></td>';
// Action
print '<td></td>';
@ -857,7 +639,7 @@ if (is_array($blocks)) {
print '</td>';
// Link to debug information object
if (getDolGlobalString('MAIN_FEATURES_LEVEL') > 0) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
if (getDolGlobalString("BLOCKEDLOG_DEBUG")) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
print '<td class="tdoverflowmax150"'.(preg_match('/<a/', $object_link) ? '' : 'title="'.dol_escape_htmltag(dol_string_nohtmltag($object_link.($object_link_title ? ' - '.$object_link_title : ''))).'"').'>';
print '</td>';
}

View file

@ -161,9 +161,14 @@ function formatObject($objtoshow, $prefix)
'nom' => 'Name',
'name' => 'Name',
'email' => 'Email',
'address' => 'Address',
'town' => 'Town',
'state_code' => 'State',
'fk_pays' => 'Country',
'fk_typent' => 'CompanyType',
'revenuestamp' => 'RevenueStamp',
'code_client' => 'CustomerCode',
'code_fournisseur' => 'SupplierCode',
'capital' => 'Capital',
'localtax1_assuj' => 'UseLocalTax1',
'localtax2_assuj' => 'UseLocalTax2',

View file

@ -206,7 +206,7 @@ class BlockedLog
$sep = 0;
$this->trackedmodules = array('' => 'None');
$this->trackedmodules = array('0' => 'None');
if (isModEnabled('takepos')) {
$this->trackedmodules['takepos'] = 'TakePOS';
}
@ -475,15 +475,15 @@ class BlockedLog
/**
* Populate properties of an unalterable log entry from object data.
* This populates ->object_data but also other fields like ->action, ->amounts_taxexcl, ->amounts and ->linktoref and ->linktype
* This populates ->object_data but also other fields like ->action, ->module_source, ->amounts_taxexcl, ->amounts and ->linktoref and ->linktype
* It also populates some debug info like ->element and ->fk_object
*
* @param CommonObject|stdClass $object Object to store
* @param string $action Action code
* @param float|int $amounts amounts (incl tax)
* @param ?User $fuser User object (forced)
* @param float|int|null $amounts_taxexcl amounts (excl tax or null if not relevant)
* @return int<-1,-1>|int<1,1> >0 if OK, <0 if KO
* @param CommonObject|stdClass $object Object to store
* @param string $action Action code
* @param float|int $amounts amounts (incl tax)
* @param ?User $fuser User object (forced)
* @param float|int|null $amounts_taxexcl amounts (excl tax or null if not relevant)
* @return int<-1,-1>|int<1,1> Return >0 if OK, <0 if KO
*/
public function setObjectData(&$object, $action, $amounts, $fuser = null, $amounts_taxexcl = null)
{
@ -504,25 +504,6 @@ class BlockedLog
if ($object->element == 'payment' || $object->element == 'payment_supplier') {
'@phan-var-force Paiement|PaiementFourn $object';
$this->date_object = empty($object->datepaye) ? $object->date : $object->datepaye;
if ($object->element == 'payment') {
$this->linktype = 'payment_invoice';
$this->linktoref = '';
foreach ($this->amounts as $fk_invoice => $amount) {
$invoice = new Facture($this->db);
if ($invoice->fetch($fk_invoice) > 0) {
$this->linktoref .= ($this->linktoref ? ',' : '').$invoice->ref;
}
}
} elseif ($object->element == 'payment_supplier') {
$this->linktype = 'payment_supplier_invoice';
$this->linktoref = '';
foreach ($this->amounts as $fk_invoice => $amount) {
$invoice = new FactureFournisseur($this->db);
if ($invoice->fetch($fk_invoice) > 0) {
$this->linktoref .= ($this->linktoref ? ',' : '').$invoice->ref;
}
}
}
} elseif ($object->element == 'payment_salary') {
'@phan-var-force PaymentSalary $object';
$this->date_object = $object->datev;
@ -536,7 +517,6 @@ class BlockedLog
/** var CashControl $object */
'@phan-var-force CashControl $object';
$this->date_object = $object->date_creation;
$this->module_source = $object->posmodule;
} elseif (property_exists($object, 'date')) {
// Generic case
$this->date_object = $object->date; // @phan-suppress-current-line PhanUndeclaredProperty
@ -550,24 +530,22 @@ class BlockedLog
'@phan-var-force FactureFournisseur $object';
if ($object->type == FactureFournisseur::TYPE_CREDIT_NOTE) {
$invoice = new FactureFournisseur($this->db);
$invoice->fetch($this->fk_facture_source);
$invoice->fetch($object->fk_facture_source);
if ($invoice->id > 0) {
$this->linktype = 'credit_note_of';
$this->linktoref = $invoice->ref;
}
//$this->module_source = $invoice->module_source;
}
}
if ($object->element == 'facture') {
'@phan-var-force Facture $object';
if ($object->type == Facture::TYPE_CREDIT_NOTE) {
$invoice = new Facture($this->db);
$invoice->fetch($this->fk_facture_source);
$invoice->fetch($object->fk_facture_source);
if ($invoice->id > 0) {
$this->linktype = 'credit_note_of';
$this->linktoref = $invoice->ref;
}
$this->module_source = $invoice->module_source;
}
}
@ -699,6 +677,8 @@ class BlockedLog
// Field specific to object
if ($this->element == 'facture') {
'@phan-var-force Facture $object';
$this->module_source = $object->module_source;
foreach ($object as $key => $value) {
if (in_array($key, $arrayoffieldstoexclude)) {
continue; // Discard some properties
@ -832,8 +812,13 @@ class BlockedLog
$totalamount = 0;
$this->linktype = $this->element;
$this->linktoref = '';
// Loop on each invoice payment amount (the payment_part)
if (is_array($object->amounts) && !empty($object->amounts)) {
// Loop on each invoice the payment is part of to set the linktoref and the module_source
$originofpayment = null;
$paymentpartnumber = 0;
foreach ($object->amounts as $objid => $amount) {
if (empty($amount)) {
@ -870,6 +855,18 @@ class BlockedLog
return -1;
}
$this->linktoref .= ($this->linktoref ? ',' : '').$tmpobject->ref;
// Set the ->module_source of payment from origin object if relevant
if (property_exists($tmpobject, 'module_source')) {
if (is_null($originofpayment)) {
$originofpayment = $tmpobject->module_source;
} elseif ($originofpayment != $invoice->module_source) {
$originofpayment = 'mix'; // the payment is on several invoices with different origins
} else {
$originofpayment = (string) $invoice->module_source;
}
}
$paymentpart = new stdClass();
$paymentpart->amount = $amount;
@ -955,6 +952,8 @@ class BlockedLog
$this->object_data->payment_part[$paymentpartnumber] = $paymentpart;
}
}
$this->module_source = (string) $originofpayment;
} elseif (!empty($object->amount)) {
$totalamount = $object->amount;
}
@ -1038,8 +1037,8 @@ class BlockedLog
return -1;
}
$sql = "SELECT b.rowid, b.date_creation, b.signature, b.amounts_taxexcl, b.amounts, b.action, b.module_source, b.element, b.fk_object, b.entity,";
$sql .= " b.certified, b.tms, b.fk_user, b.user_fullname, b.date_object, b.ref_object, b.object_data, b.object_version, b.object_format";
$sql = "SELECT b.rowid, b.date_creation, b.action, b.module_source, b.amounts_taxexcl, b.amounts, b.element, b.fk_object, b.entity,";
$sql .= " b.certified, b.tms, b.fk_user, b.user_fullname, b.date_object, b.ref_object, b.linktoref, b.linktype, b.object_data, b.object_version, b.object_format, b.signature";
$sql .= " FROM ".MAIN_DB_PREFIX."blockedlog as b";
if ($id) {
$sql .= " WHERE b.rowid = ".((int) $id);
@ -1064,6 +1063,8 @@ class BlockedLog
$this->fk_object = $obj->fk_object;
$this->date_object = $this->db->jdate($obj->date_object); // jdate(date_object) is UTC
$this->ref_object = $obj->ref_object;
$this->linktoref = $obj->linktoref;
$this->linktype = $obj->linktype;
$this->fk_user = $obj->fk_user;
$this->user_fullname = $obj->user_fullname;
@ -1241,6 +1242,8 @@ class BlockedLog
$sql .= " fk_object,";
$sql .= " date_object,";
$sql .= " ref_object,";
$sql .= " linktoref,";
$sql .= " linktype,";
$sql .= " object_data,";
$sql .= " object_version,";
$sql .= " object_format,";
@ -1260,6 +1263,8 @@ class BlockedLog
$sql .= (int) $this->fk_object.",";
$sql .= "'".$this->db->idate($this->date_object)."',";
$sql .= "'".$this->db->escape($this->ref_object)."',";
$sql .= ($this->linktoref ? "'".$this->db->escape($this->linktoref)."'" : "null").",";
$sql .= ($this->linktoref ? "'".$this->db->escape($this->linktype)."'" : "null").",";
$sql .= "'".$this->db->escape($this->dolEncodeBlockedData($this->object_data))."',";
$sql .= "'".$this->db->escape($this->object_version)."',";
$sql .= "'".$this->db->escape($this->object_format)."',";
@ -1495,21 +1500,22 @@ class BlockedLog
/**
* Return array of log objects (with criteria)
*
* @param string $element Element to search
* @param string|int $fk_object Id of object to search. Can be a UFS search criteria.
* @param int<0,max> $limit Max number of element, 0 for all
* @param string $sortfield Sort field
* @param string $sortorder Sort order
* @param int $search_fk_user Id of user(s)
* @param int $search_start Start time limit
* @param int $search_end End time limit
* @param string $search_ref Search ref
* @param string $search_amount Search amount
* @param string|string[] $search_code Search code
* @param string $search_signature Search signature
* @return BlockedLog[]|int<-2,-1> Array of object log or <0 if error
* @param string $element Element to search
* @param string|int $fk_object Id of object to search. Can be a UFS search criteria.
* @param int<0,max> $limit Max number of element, 0 for all
* @param string $sortfield Sort field
* @param string $sortorder Sort order
* @param int $search_fk_user Id of user(s)
* @param int $search_start Start time limit
* @param int $search_end End time limit
* @param string $search_ref Search ref
* @param string $search_amount Search amount
* @param string|string[] $search_code Search code
* @param string $search_signature Search signature
* @param string $search_module_source Search on module source
* @return BlockedLog[]|int<-2,-1> Array of object log or <0 if error
*/
public function getLog($element, $fk_object, $limit = 0, $sortfield = '', $sortorder = '', $search_fk_user = -1, $search_start = -1, $search_end = -1, $search_ref = '', $search_amount = '', $search_code = '', $search_signature = '')
public function getLog($element, $fk_object, $limit = 0, $sortfield = '', $sortorder = '', $search_fk_user = -1, $search_start = -1, $search_end = -1, $search_ref = '', $search_amount = '', $search_code = '', $search_signature = '', $search_module_source = '')
{
global $conf;
//global $cachedlogs;
@ -1562,6 +1568,27 @@ class BlockedLog
$sql .= natural_search("action", $search_code, 3);
}
}
if (is_array($search_module_source)) {
if (!empty($search_module_source)) {
$sql .= " AND (";
if (in_array('0', $search_module_source)) {
$sql .= "module_source = ''";
unset($search_module_source[0]);
if (!empty($search_module_source)) {
$sql .= " OR ";
}
}
if (!empty($search_module_source)) {
$sql .= natural_search("module_source", implode(',', $search_module_source), 3, 1);
}
$sql .= " OR module_source = 'mix'"; // When a payment was reocrd and payment was on an invoice with different origins (pos and not pos)
$sql .= ")";
}
} else {
if ($search_module_source != '' && $search_module_source != '-1') {
$sql .= natural_search("module_source", $search_module_source, 3);
}
}
$sql .= $this->db->order($sortfield, $sortorder);
$sql .= $this->db->plimit($limit + 1); // We want more, because we will stop into loop later with error if we reach max

View file

@ -830,7 +830,7 @@ if ($result >= 0) {
print img_picto("Auto fill", 'rightarrow.png');
print '</button>';
}
print '<input '.$min.' '.$max.' type="text" class="multicurrency_amount" name="'.$namef.'" value="'.GETPOST($namef).'">';
print '<input '.$min.' '.$max.' type="text" class="multicurrency_amount maxwidth100" name="'.$namef.'" value="'.GETPOST($namef).'">';
print '<input type="hidden" class="multicurrency_remain" name="'.$nameRemain.'" value="'.$multicurrency_remaintopay.'">';
} else {
print '<input type="text" class="maxwidth75" name="'.$namef.'_disabled" value="'.(GETPOST($namef) != '0' ? GETPOST($namef) : '').'" disabled>';
@ -903,7 +903,7 @@ if ($result >= 0) {
print img_picto("Auto fill", 'rightarrow.png');
print '</button>';
}
print '<input '.$max.' '.$min.' type="text" size="8" class="amount" name="'.$namef.'" value="'.dol_escape_htmltag(GETPOST($namef)).'">'; // class is required to be used by javascript callForResult();
print '<input '.$max.' '.$min.' type="text" class="amount maxwidth100" name="'.$namef.'" value="'.dol_escape_htmltag(GETPOST($namef)).'">'; // class is required to be used by javascript callForResult();
print '<input type="hidden" class="remain" name="'.$nameRemain.'" value="'.$remaintopay.'">';
} else {
print '<input type="text" class="maxwidth75" name="'.$namef.'_disabled" value="'.dol_escape_htmltag(GETPOST($namef)).'" disabled>';

View file

@ -13204,7 +13204,7 @@ function natural_search($fields, $value, $mode = 0, $nofirstand = 0, $sqltoadd =
$listofcodes = '';
foreach ($tmparray as $val) {
$val = trim($val);
if ($val) {
if ($val) { // TODO Test with if ($val !== '') {
$listofcodes .= ($listofcodes ? ',' : '');
$listofcodes .= "'" . $db->escape($val) . "'";
}

View file

@ -18,20 +18,20 @@
CREATE TABLE llx_blockedlog
(
rowid integer AUTO_INCREMENT PRIMARY KEY, -- Automatic sequence ID
-- fields included into signature
rowid integer AUTO_INCREMENT PRIMARY KEY, -- field included into line signature
entity integer DEFAULT 1 NOT NULL, -- field included into line signature
date_creation datetime, -- field included into line signature
action varchar(50), -- The type of event. field included into line signature
module_source varchar(32) DEFAULT '', -- If the event was recorded from a POS module or another module
module_source varchar(32) DEFAULT '', -- field included into line signature. If the event was recorded from a POS module or another module.
action varchar(50), -- field included into line signature. The type of event.
entity integer DEFAULT 1 NOT NULL, -- field included into line signature. For future usage of multi-entity.
date_creation datetime, -- field included into line signature. Date and time of event.
amounts double(24,8) NOT NULL, -- field included into line signature (denormalized data from object_data)
amounts_taxexcl double(24,8) NULL, -- field included into line signature (denormalized data from object_data)
ref_object varchar(255), -- field included into line signature (denormalized data from object_data)
date_object datetime, -- field included into line signature (denormalized data from object_data)
user_fullname varchar(255), -- field included into line signature (denormalized data from object_data)
user_fullname varchar(255), -- field included into line signature. User recording the event.
linktoref varchar(255), -- field included into line signature. Link to another ref_object.
linktype varchar(16), -- field included into line signature. Link type.
object_data mediumtext, -- field included into line signature
linktoref varchar(255), -- Link to ref. field included into line signature
linktype varchar(16), -- Link type. field included into line signature
-- the signature of line
signature varchar(100) NOT NULL, -- the hash of the key for signature with previous hash before
-- fields used for debug only

View file

@ -681,6 +681,7 @@ MentionCategoryOfOperations0=Delivery of goods
MentionCategoryOfOperations1=Provision of services
MentionCategoryOfOperations2=Mixed - Delivery of goods & provision of services
Salaries=Salaries
InvoiceType=Invoice type
InvoiceSubtype=Invoice Subtype
SalaryInvoice=Salary
BillsAndSalaries=Bills & Salaries

View file

@ -47,7 +47,7 @@ YourCountryCode=Your country code
DisablingBlockedLogIsNotallowedOnceUsedExceptOnFullreset=Disabling %s module is not allowed once it has been used, except for a full database reset. Ask your integrator or reseller if you need such a reset.
LNECertifiedPOSSystem=LNE Certified POS system
InvoiceGeneratedWithLNECertifiedPOSSystem=Invoice generated with a LNE certified POS system
TotalTTCIfInvoiceSeeCompleteDataForDetail=Amount of event. Total including tax if invoice. See the column "complete data" for details.
TotalTTCIfInvoiceSeeCompleteDataForDetail=Event amount. Total including tax if invoice. See the column "complete data" for details.
TypeOfEvent=Type of event
TotalForAction=Total for event %s
SecretKey=Secret key

View file

@ -657,6 +657,9 @@ div.userimg.notfirst {
.center.inline-block.dateheight {
line-height: 1.2em;
}
.smallheight {
line-height: 1em;
}
/* Used by timesheets */