2017-06-10 07:25:32 +00:00
< ? php
2026-08-13 17:58:44 +00:00
/* Copyright ( C ) 2017 ATM Consulting < contact @ atm - consulting . fr >
* Copyright ( C ) 2018 Destailleur Laurent < eldy @ users . sourceforge . net >
* Copyright ( C ) 2024 MDW < mdeweerd @ users . noreply . github . com >
* Copyright ( C ) 2026 Frédéric France < frederic . france @ free . fr >
2017-06-10 07:25:32 +00:00
*
* 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
2019-09-23 19:55:30 +00:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2017-06-10 07:25:32 +00:00
*/
/**
2022-09-01 18:00:26 +00:00
* \file htdocs / blockedlog / lib / blockedlog . lib . php
* \ingroup system
* \brief Library for common blockedlog functions
2017-06-10 07:25:32 +00:00
*/
2026-02-21 10:44:43 +00:00
include_once DOL_DOCUMENT_ROOT . '/blockedlog/versionmod.inc.php' ;
2026-02-19 21:29:20 +00:00
/**
* Define head array for tabs of blockedlog tools setup pages
*
* @ return string Version
*/
function getBlockedLogVersionToShow ()
{
2026-06-19 16:40:19 +00:00
$versionbadge = constant ( 'DOLCERT_VERSION' );
// Protection if we used a past old version not yet certified, we change version shown.
if ( ! constant ( 'CERTIF_LNE' )) { // Hard coded in version
2026-08-13 17:58:44 +00:00
// $versionbadge = preg_replace('/^(\d)\./', '\1b.', $versionbadge); // Not yet required.
2026-08-13 18:14:53 +00:00
}
if ( constant ( 'DOLCERT_NAME' ) != 'BlockedLog' ) { // Can add a prefix namevefore version text for clarity.
$versionbadge = constant ( 'DOLCERT_NAME' ) . '-' . $versionbadge ;
2026-08-13 18:13:30 +00:00
}
2026-06-19 16:40:19 +00:00
return $versionbadge ;
2026-02-19 21:29:20 +00:00
}
2017-06-10 07:25:32 +00:00
/**
* Define head array for tabs of blockedlog tools setup pages
*
2026-04-12 09:59:56 +00:00
* @ param int $withtabsetup Add also the tab " Setup "
2024-09-18 01:27:25 +00:00
* @ return array < array { 0 : string , 1 : string , 2 : string } > Array of head
2017-06-10 07:25:32 +00:00
*/
2025-11-17 12:53:53 +00:00
function blockedlogadmin_prepare_head ( $withtabsetup )
2017-06-10 07:25:32 +00:00
{
2026-02-19 21:29:20 +00:00
global $db , $langs , $conf , $mysoc ;
2017-06-10 07:25:32 +00:00
2025-11-17 12:53:53 +00:00
$langs -> load ( " blockedlog " );
2026-02-09 17:04:59 +00:00
require_once DOL_DOCUMENT_ROOT . '/blockedlog/class/blockedlog.class.php' ;
2026-02-09 17:28:53 +00:00
$param = '' ;
$param .= ( $withtabsetup ? " ?withtab= " . $withtabsetup : " " );
2026-02-15 23:08:14 +00:00
$param .= ( GETPOST ( 'origin' ) ? ( $param ? '&' : '?' ) . 'origin=' . GETPOST ( 'origin' ) : '' );
2026-02-09 17:28:53 +00:00
2017-06-10 07:25:32 +00:00
$h = 0 ;
$head = array ();
2017-06-16 09:51:33 +00:00
2026-03-08 21:27:57 +00:00
if ( ! userIsTaxAuditor ()) {
2026-03-08 12:22:54 +00:00
$head [ $h ][ 0 ] = DOL_URL_ROOT . " /blockedlog/admin/registration.php " . $param ;
$head [ $h ][ 1 ] = $langs -> trans ( " UserRegistration " );
$head [ $h ][ 2 ] = 'registration' ;
$h ++ ;
}
2017-06-16 09:51:33 +00:00
2026-03-08 21:27:57 +00:00
if ( ! userIsTaxAuditor ()) {
2026-03-08 12:22:54 +00:00
$b = new BlockedLog ( $db );
$head [ $h ][ 0 ] = DOL_URL_ROOT . " /blockedlog/admin/blockedlog_list.php " . $param ;
$head [ $h ][ 1 ] = $langs -> trans ( " BrowseBlockedLog " );
if ( $b -> alreadyUsed ()) {
$head [ $h ][ 1 ] .= ( ! getDolGlobalString ( 'MAIN_OPTIMIZEFORTEXTBROWSER' ) ? '<span class="badge marginleftonlyshort">...</span>' : '' );
}
$head [ $h ][ 2 ] = 'fingerprints' ;
$h ++ ;
2017-12-15 14:15:14 +00:00
}
2017-06-16 09:51:33 +00:00
2026-02-09 17:28:53 +00:00
$head [ $h ][ 0 ] = DOL_URL_ROOT . " /blockedlog/admin/blockedlog_archives.php " . $param ;
2025-11-17 12:53:53 +00:00
$head [ $h ][ 1 ] = $langs -> trans ( " Archives " );
2026-04-02 11:56:49 +00:00
// Add badge on nb of files
$block_static = new BlockedLog ( $db );
$upload_dir = getMultidirOutput ( $block_static , 'blockedlog' ) . '/archives' ;
require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php' ;
require_once DOL_DOCUMENT_ROOT . '/core/class/link.class.php' ;
$nbFiles = count ( dol_dir_list ( $upload_dir , 'files' , 0 , '' , '(\.meta|_preview.*\.png)$' ));
$nbLinks = 0 ;
if (( $nbFiles + $nbLinks ) > 0 ) {
$head [ $h ][ 1 ] .= '<span class="badge marginleftonlyshort">' . ( $nbFiles + $nbLinks ) . '</span>' ;
}
2025-11-17 12:53:53 +00:00
$head [ $h ][ 2 ] = 'archives' ;
$h ++ ;
2026-04-02 11:56:49 +00:00
if ( userIsTaxAuditor ()) {
$head [ $h ][ 0 ] = DOL_URL_ROOT . " /blockedlog/admin/blockedlog_control.php " . $param ;
$head [ $h ][ 1 ] = $langs -> trans ( " OtherControl " );
$head [ $h ][ 2 ] = 'control' ;
$h ++ ;
}
2026-02-19 21:29:20 +00:00
if ( $mysoc -> country_code == 'FR' ) {
$head [ $h ][ 0 ] = DOL_URL_ROOT . " /blockedlog/admin/documentation.php " . $param ;
$head [ $h ][ 1 ] = $langs -> trans ( " Documentation " );
$head [ $h ][ 2 ] = 'documentation' ;
$h ++ ;
}
2026-03-08 21:27:57 +00:00
if ( $withtabsetup && ! userIsTaxAuditor ()) {
2026-02-09 17:28:53 +00:00
$head [ $h ][ 0 ] = DOL_URL_ROOT . " /blockedlog/admin/blockedlog.php " . $param ;
2026-02-09 17:04:59 +00:00
$head [ $h ][ 1 ] = $langs -> trans ( " TechnicalInformation " );
$head [ $h ][ 2 ] = 'technicalinfo' ;
$h ++ ;
}
2025-11-17 12:53:53 +00:00
2020-04-10 08:59:32 +00:00
$object = new stdClass ();
2017-06-10 07:25:32 +00:00
2020-03-23 14:54:02 +00:00
// Show more tabs from modules
// Entries must be declared in modules descriptor with line
// $this->tabs = array('entity:+tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to add new tab
// $this->tabs = array('entity:-tabname); to remove a tab
2019-01-27 10:55:16 +00:00
complete_head_from_modules ( $conf , $langs , $object , $head , $h , 'blockedlog' );
2017-06-10 07:25:32 +00:00
2019-01-27 10:55:16 +00:00
complete_head_from_modules ( $conf , $langs , $object , $head , $h , 'blockedlog' , 'remove' );
2017-06-10 07:25:32 +00:00
2020-03-23 14:54:02 +00:00
return $head ;
2017-06-10 07:25:32 +00:00
}
2025-10-22 14:31:47 +00:00
2025-11-17 12:53:53 +00:00
2025-11-11 14:06:57 +00:00
/**
* Return if the KYC mandatory parameters are set
2026-06-07 11:55:52 +00:00
* Must be the same fields than the one defined as mandatory into the registration form .
2025-11-11 14:06:57 +00:00
*
* @ return boolean True or false
*/
2025-12-02 19:29:20 +00:00
function isRegistrationDataSaved ()
2025-11-11 14:06:57 +00:00
{
global $mysoc ;
2025-10-22 14:31:47 +00:00
2025-11-11 14:06:57 +00:00
$companyname = getDolGlobalString ( 'BLOCKEDLOG_REGISTRATION_NAME' , $mysoc -> name );
$companyemail = getDolGlobalString ( 'BLOCKEDLOG_REGISTRATION_EMAIL' , $mysoc -> email );
2026-06-15 16:51:49 +00:00
$companycountrycode = $mysoc -> country_code ;
$companyidprof1 = getDolGlobalString ( 'MAIN_INFO_SIREN' , $mysoc -> idprof1 );
$companyidprof2 = getDolGlobalString ( 'MAIN_INFO_SIRET' , $mysoc -> idprof2 );
2025-11-11 14:06:57 +00:00
//$companytel = getDolGlobalString('BLOCKEDLOG_REGISTRATION_TEL', $mysoc->phone);
2026-06-03 20:12:06 +00:00
if ( empty ( $companyname ) || empty ( $companycountrycode ) || empty ( $companyidprof1 ) || empty ( $companyidprof2 ) || empty ( $companyemail )) {
2025-11-11 14:06:57 +00:00
return false ;
}
2026-02-13 00:22:06 +00:00
/*
2025-11-11 14:06:57 +00:00
$providerset = getDolGlobalString ( 'MAIN_INFO_ITPROVIDER_NAME' ); // Can be 'myself'
if ( empty ( $providerset )) {
return false ;
}
2026-02-13 00:22:06 +00:00
*/
2025-11-11 14:06:57 +00:00
return true ;
}
2025-10-22 14:31:47 +00:00
2025-12-02 19:29:20 +00:00
2026-02-09 17:04:59 +00:00
/**
* Return if the KYC mandatory parameters are set AND pushed / registered centralized server
*
* @ return boolean True or false
*/
function isRegistrationDataSavedAndPushed ()
{
return isRegistrationDataSaved () && ( bool ) getDolGlobalString ( 'MAIN_FIRST_REGISTRATION_OK_DATE' );
}
2025-10-22 14:31:47 +00:00
/**
2026-02-13 00:05:25 +00:00
* Return a hash unique identifier of the registration ( used to identify the registration of instance without disclosing personal data )
2025-10-22 14:31:47 +00:00
*
2026-02-13 00:05:25 +00:00
* @ param string $algo Algorithm to use for hash key
* @ return string Hash unique ID
2025-10-22 14:31:47 +00:00
*/
2026-02-13 00:05:25 +00:00
function getHashUniqueIdOfRegistration ( $algo = 'sha256' )
2025-12-02 19:29:20 +00:00
{
global $conf ;
2026-02-13 00:05:25 +00:00
return dol_hash ( 'dolibarr' . $conf -> file -> instance_unique_id . ( $conf -> entity > 1 ? $conf -> entity : '' ), $algo , 1 );
2025-12-02 19:29:20 +00:00
}
/**
* 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 .
*
2026-04-29 15:12:52 +00:00
* @ param int < 0 , 1 > $ignoredev Set this to 1 to ignore the fact the version is an alpha or beta version ( to avoid return false on such version )
2025-12-02 19:29:20 +00:00
* @ 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 .
2026-04-29 15:12:52 +00:00
* @ return string '' if false , or a string if true
2025-12-02 19:29:20 +00:00
*/
function isALNEQualifiedVersion ( $ignoredev = 0 , $ignoremodule = 0 )
2025-10-22 14:31:47 +00:00
{
global $mysoc ;
2026-04-29 15:12:52 +00:00
// For Dev/Debug purpose: Constant set by developer to force all LNE restrictions even if country is not France so we can test them on any dev instance.
2025-12-02 19:29:20 +00:00
// 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.
2025-10-22 14:31:47 +00:00
if ( defined ( 'CERTIF_LNE' ) && ( int ) constant ( 'CERTIF_LNE' ) === 2 ) {
2026-01-27 00:15:30 +00:00
return 'CERTIF_LNE_IS_2' ;
2025-10-22 14:31:47 +00:00
}
2025-11-12 18:37:19 +00:00
2026-06-16 15:06:46 +00:00
if ( ! $ignoredev && preg_match ( '/\-/' , DOL_VERSION )) { // This is not a stable version (dev version), it can't be the certified versions.
2026-01-27 16:39:02 +00:00
return '' ;
2025-10-22 14:31:47 +00:00
}
if ( $mysoc -> country_code != 'FR' ) {
2026-01-27 16:39:02 +00:00
return '' ;
2025-10-22 14:31:47 +00:00
}
if ( ! defined ( 'CERTIF_LNE' ) || ( int ) constant ( 'CERTIF_LNE' ) === 0 ) {
2026-01-27 16:39:02 +00:00
return '' ;
2025-10-22 14:31:47 +00:00
}
2025-12-02 19:29:20 +00:00
if ( ! $ignoremodule && ! isModEnabled ( 'blockedlog' )) {
2026-01-27 16:39:02 +00:00
return '' ;
2025-10-22 14:31:47 +00:00
}
2025-10-22 16:26:31 +00:00
2026-06-16 15:06:46 +00:00
// All conditions are ok to become a LNE certified version
2026-04-29 15:12:52 +00:00
return ( $ignoredev ? '' : 'NOT_BETA+' ) . 'FR+CERTIF_LNE_IS_1' . ( $ignoremodule ? '' : '+MODENABLED' );
2025-10-28 12:07:19 +00:00
}
/**
2025-12-02 19:29:20 +00:00
* Return if the application is executed with the LNE requirements on .
2026-06-16 15:06:46 +00:00
* This function can be used to block some features like custom receipts , or to enable others like showing the information " Certified LNE " .
2025-10-28 12:07:19 +00:00
*
2026-06-18 14:54:41 +00:00
* @ param int $blockedlogtestalreadydone Test on blockedlog used already done and we suppose it is true .
* @ param int $blockedlogmodulealreadydone Test on blockedlog module already done and we suppose it is true .
* @ return boolean True or false
2025-10-28 12:07:19 +00:00
*/
2026-06-18 14:54:41 +00:00
function isALNERunningVersion ( $blockedlogtestalreadydone = 0 , $blockedlogmodulealreadydone = 0 )
2025-10-28 12:07:19 +00:00
{
2026-06-18 13:31:25 +00:00
// For Debug help: Constant CERTIF_LNE can be set 2 by developers get mode 1 compliant with dev env.
2026-05-26 16:48:53 +00:00
// Note that you can force, with this constant, the enabling of the restrictions,
// but there is no way to force the disabling of a restriction.
2026-06-18 13:31:25 +00:00
if ( defined ( 'CERTIF_LNE' ) && ( int ) constant ( 'CERTIF_LNE' ) === 2 // Value is 2 for debug purpose to enable restriction except https for dev env.
2026-06-18 14:54:41 +00:00
&& ( $blockedlogmodulealreadydone || isModEnabled ( 'blockedlog' ))
&& ( $blockedlogtestalreadydone || isBlockedLogUsed ())) {
2025-10-28 12:07:19 +00:00
return true ;
}
2026-05-26 16:48:53 +00:00
if ( defined ( 'CERTIF_LNE' ) && ( int ) constant ( 'CERTIF_LNE' ) === 1 // Value is 1 when version is certified
2026-06-18 14:54:41 +00:00
&& ( $blockedlogmodulealreadydone || isModEnabled ( 'blockedlog' ))
&& ( $blockedlogtestalreadydone || isBlockedLogUsed ())) {
2025-10-28 12:07:19 +00:00
return true ;
}
return false ;
}
/**
* Return if the blocked log was already used to block some events .
*
* @ param int < 0 , 1 > $ignoresystem Ignore system events for the test
* @ return boolean True if blocked log was already used , false if not
*/
2026-01-01 11:26:35 +00:00
function isBlockedLogUsed ( $ignoresystem = 0 )
2025-10-28 12:07:19 +00:00
{
global $conf , $db ;
$result = true ; // by default restrictions are on, so we can't disable them
2026-01-01 11:26:35 +00:00
// Note: if module on, we suppose it is used, if not, we check in case of it was disabled.
2025-10-28 12:07:19 +00:00
if ( ! isModEnabled ( 'blockedlog' )) {
2026-01-01 11:26:35 +00:00
// Test the cache key
if ( array_key_exists ( 'isblockedlogused' , $conf -> cache )) {
return $conf -> cache [ 'isblockedlogused' . $ignoresystem ];
}
2025-10-28 12:07:19 +00:00
$sql = " SELECT rowid FROM " . MAIN_DB_PREFIX . " blockedlog " ;
2026-06-17 11:31:08 +00:00
$sql .= " WHERE entity = " . (( int ) $conf -> entity ); // Sharing entity in blocked log will never be allowed
2025-10-28 12:07:19 +00:00
if ( $ignoresystem ) {
$sql .= " AND action NOT IN ('MODULE_SET', 'MODULE_RESET') " ;
}
$sql .= $db -> plimit ( 1 );
$resql = $db -> query ( $sql );
if ( $resql !== false ) {
$obj = $db -> fetch_object ( $resql );
if ( ! $obj ) {
$result = false ;
}
} else {
dol_print_error ( $db );
}
2026-01-01 11:26:35 +00:00
$conf -> cache [ 'isblockedlogused' . $ignoresystem ] = $result ;
2025-10-28 12:07:19 +00:00
}
2026-01-01 11:26:35 +00:00
dol_syslog ( " isBlockedLogUsed: ignoresystem= " . $ignoresystem . " returns " . ( string ) $result );
2025-10-28 12:07:19 +00:00
return $result ;
2025-10-22 14:31:47 +00:00
}
2025-10-28 14:12:45 +00:00
/**
* Add legal mention
*
* @ param TCPDF $pdf Object PDF
* @ param Translate $outputlangs Object lang
* @ param Societe $seller Seller company
* @ param int $default_font_size Default font size
* @ param float $posy Y position
* @ param CommonDocGenerator $pdftemplate PDF template
* @ return int 0 if nothing done , 1 if a mention was printed
*/
function pdfCertifMentionblockedLog ( & $pdf , $outputlangs , $seller , $default_font_size , & $posy , $pdftemplate )
{
$result = 0 ;
2026-02-16 03:04:17 +00:00
if ( in_array ( $seller -> country_code , array ( 'FR' ))) {
2025-10-28 14:12:45 +00:00
$outputlangs -> load ( " blockedlog " );
2026-06-16 01:03:39 +00:00
$blockedlog_mention = '' ;
2026-02-16 03:04:17 +00:00
$isalne = isALNEQualifiedVersion (); // If necessary, we could replace with "if isALNERunningVersion()"
if ( $isalne == 'CERTIF_LNE_IS_2' ) {
$blockedlog_mention = $outputlangs -> transnoentitiesnoconv ( " InvoiceGeneratedWithLNECandidatePOSSystem " );
2026-06-15 00:47:12 +00:00
} elseif ( $isalne ) {
2026-02-16 03:04:17 +00:00
$blockedlog_mention = $outputlangs -> transnoentitiesnoconv ( " InvoiceGeneratedWithLNECertifiedPOSSystem " );
}
2025-10-28 14:12:45 +00:00
if ( $blockedlog_mention ) {
$pdf -> SetFont ( '' , '' , $default_font_size - 2 );
$pdf -> SetXY ( $pdftemplate -> marge_gauche , $posy );
$pdf -> MultiCell ( 100 , 3 , $blockedlog_mention , 0 , 'L' , false );
$posy = $pdf -> GetY ();
$result = 1 ;
}
}
return $result ;
}
2026-01-01 11:26:35 +00:00
/**
* sumAmountsForUnalterableEvent
*
2026-01-05 15:43:00 +00:00
* @ param BlockedLog $block Object BlockedLog
* @ param array < string , int > $refinvoicefound Array of ref of invoice already found ( to avoid duplicates . Should be useless but just in case of )
* @ param array < string , array < string , float >> $totalhtamount Array of total per code event and module
* @ param array < string , array < string , float >> $totalvatamount Array of total per code event and module
* @ param array < string , array < string , float >> $totalamount Array of total per code event and module
* @ param float $total_ht Total HT
* @ param float $total_vat Total VAT
* @ param float $total_ttc Total TTC
* @ return int Return > 0
2026-01-01 11:26:35 +00:00
*/
2026-01-03 12:59:09 +00:00
function sumAmountsForUnalterableEvent ( $block , & $refinvoicefound , & $totalhtamount , & $totalvatamount , & $totalamount , & $total_ht , & $total_vat , & $total_ttc )
2026-01-01 11:26:35 +00:00
{
if ( $block -> action == 'BILL_VALIDATE' ) {
$total_ht = $block -> object_data -> total_ht ;
$total_vat = $block -> object_data -> total_tva ;
$total_ttc = $block -> object_data -> total_ttc ;
2026-05-26 10:02:19 +00:00
// Init to avoid warnings if not initialized yet
if ( ! isset ( $totalamount [ $block -> action ][ $block -> module_source ])) {
$totalhtamount [ $block -> action ][ $block -> module_source ] = 0 ;
$totalvatamount [ $block -> action ][ $block -> module_source ] = 0 ;
$totalamount [ $block -> action ][ $block -> module_source ] = 0 ;
}
2026-01-01 11:26:35 +00:00
// We add total for the invoice if "invoice validate event" not yet met.
// If we already met the event for this object, we keep only first one but this should not happen because edition of validated invoice is not allowed on secured versions.
2026-01-03 12:59:09 +00:00
if ( empty ( $refinvoicefound [ $block -> ref_object ])) {
$totalhtamount [ $block -> action ][ $block -> module_source ] += $total_ht ;
$totalvatamount [ $block -> action ][ $block -> module_source ] += $total_vat ;
$totalamount [ $block -> action ][ $block -> module_source ] += $total_ttc ;
2026-01-01 11:26:35 +00:00
}
2026-01-03 12:59:09 +00:00
$refinvoicefound [ $block -> ref_object ] = 1 ;
2026-02-18 22:03:16 +00:00
} elseif ( $block -> action == 'PAYMENT_CUSTOMER_CREATE' || $block -> action == 'PAYMENT_CUSTOMER_DELETE' ) {
2026-01-01 11:26:35 +00:00
$total_ht = $block -> object_data -> amount ;
$total_vat = 0 ;
$total_ttc = $block -> object_data -> amount ;
2026-02-18 22:03:16 +00:00
//$actionkey = $block->action;
$actionkey = 'PAYMENT_CUSTOMER' ;
2026-05-26 10:02:19 +00:00
// Init to avoid warnings if not initialized yet
if ( ! isset ( $totalamount [ $actionkey ][ $block -> module_source ])) {
$totalhtamount [ $actionkey ][ $block -> module_source ] = 0 ;
$totalvatamount [ $actionkey ][ $block -> module_source ] = 0 ;
$totalamount [ $actionkey ][ $block -> module_source ] = 0 ;
}
2026-02-18 22:03:16 +00:00
$totalhtamount [ $actionkey ][ $block -> module_source ] += $total_ht ;
$totalvatamount [ $actionkey ][ $block -> module_source ] += $total_vat ;
$totalamount [ $actionkey ][ $block -> module_source ] += $total_ttc ;
2026-01-01 11:26:35 +00:00
} else {
$total_ttc = $block -> amounts ;
}
return 1 ;
}
2026-02-09 18:11:37 +00:00
2026-06-15 10:33:55 +00:00
/**
2026-06-15 16:51:49 +00:00
* Call remote API service to get the obfuscation key .
2026-06-20 06:07:21 +00:00
* This function is only called by blockedlog -> getObfuscationKey ();
2026-06-15 10:33:55 +00:00
*
* @ param string $idprof1 Counter ID / value of ne record
* @ param string $registrationnumber Registration number
2026-06-16 01:12:07 +00:00
* @ param boolean $force False . Use true for tests .
2026-06-15 11:51:52 +00:00
* @ return string Obfuscationkey or 'ERROR ...' if error .
2026-06-15 10:33:55 +00:00
*/
function callApiToGetObfuscationKey ( $idprof1 , $registrationnumber , $force = false )
{
global $mysoc , $conf ;
$obfuscationkey = 'ERROR' ;
2026-06-20 06:07:21 +00:00
if ( empty ( $idprof1 )) {
dol_syslog ( " callApiToGetObfuscationKey was called with empty idprof1 " , LOG_DEBUG );
return 'ERROR callApiToGetObfuscationKey was called with empty idprof1' ;
}
2026-06-18 14:35:18 +00:00
//if ((isALNERunningVersion(1) || $force) && $mysoc->country_code == 'FR') {
2026-06-15 10:33:55 +00:00
$url_for_ping = getDolGlobalString ( 'MAIN_URL_FOR_PING' , " https://ping.dolibarr.org/ " );
$algo = 'sha256' ;
$hash_unique_id = getHashUniqueIdOfRegistration ( $algo ); // The hash of the unique IDof instance
$t = microtime ( true );
$micro = sprintf ( " %06d " , ( int ) (( $t - floor ( $t )) * 1000000 ));
$data = '' ;
$data .= 'hash_algo=dol_hash-' . urlencode ( $algo );
$data .= '&hash_unique_id=' . urlencode ( $hash_unique_id );
$data .= '&action=dolibarrgetkeyobfuscation' ;
$data .= '&datesys=' . urlencode ( dol_print_date ( dol_now ( 'gmt' ), 'standard' , 'gmt' ) . '.' . $micro );
$data .= '&version=' . ( float ) DOL_VERSION ;
$data .= '&version_full=' . urlencode ( DOL_VERSION );
$data .= '&versionblockedlog=' . ( float ) getBlockedLogVersionToShow ();
$data .= '&versionblockedlog_full=' . urlencode ( getBlockedLogVersionToShow ());
$data .= '&entity=' . ( int ) $conf -> entity ;
2026-06-15 13:34:54 +00:00
$data .= '&company_idprof1=' . urlencode ( dol_sanitizeKeyCode ( $idprof1 ));
2026-06-15 10:41:18 +00:00
$data .= '®istrationnumber=' . urlencode ( dol_sanitizeKeyCode ( $registrationnumber ));
2026-06-15 10:33:55 +00:00
$addheaders = array ();
2026-06-15 16:51:49 +00:00
$timeoutconnect = 3 ;
$timeoutresponse = 3 ;
2026-06-15 10:33:55 +00:00
2026-06-20 01:27:26 +00:00
dol_syslog ( " callApiToGetObfuscationKey call remote URL idprod1= " . dol_sanitizeKeyCode ( $idprof1 ), LOG_DEBUG );
dol_syslog ( " callApiToGetObfuscationKey call remote URL idprod1= " . dol_sanitizeKeyCode ( $idprof1 ), LOG_DEBUG , 0 , '_dolibarrgetkeyobfuscation' );
2026-06-15 10:33:55 +00:00
include_once DOL_DOCUMENT_ROOT . '/core/lib/geturl.lib.php' ;
2026-06-18 14:35:18 +00:00
try {
$tmpresult = getURLContent ( $url_for_ping , 'POST' , $data , 1 , $addheaders , array ( 'https' ), 0 , - 1 , $timeoutconnect , $timeoutresponse , array ());
usleep ( 10000 );
// Add a warning in log in case of error
if ( $tmpresult [ 'http_code' ] == 0 && ! empty ( $tmpresult [ 'curl_error_msg' ])) {
$logerrormessage = 'Error: ' . $tmpresult [ 'curl_error_msg' ];
$obfuscationkey .= ' ' . $tmpresult [ 'curl_error_msg' ];
dol_syslog ( " callApiToGetObfuscationKey result error when getting obfuscation key: " . $logerrormessage , LOG_WARNING );
dol_syslog ( " callApiToGetObfuscationKey result error when getting obfuscation key: " . $logerrormessage , LOG_WARNING , 0 , '_dolibarrgetkeyobfuscation' );
} elseif ( $tmpresult [ 'http_code' ] != 200 ) {
$logerrormessage = 'Error: ' . $tmpresult [ 'http_code' ] . ' ' . $tmpresult [ 'content' ];
$obfuscationkey .= ' ' . $tmpresult [ 'http_code' ] . ' ' . $tmpresult [ 'content' ];
dol_syslog ( " callApiToGetObfuscationKey result error when getting obfuscation key: " . $logerrormessage , LOG_WARNING );
dol_syslog ( " callApiToGetObfuscationKey result error when getting obfuscation key: " . $logerrormessage , LOG_WARNING , 0 , '_dolibarrgetkeyobfuscation' );
} else {
$reg = array ();
if ( preg_match ( '/(DOLOBFUSCKEY.*)/' , $tmpresult [ 'content' ], $reg )) { // gitleaks:allow $tmpresult['content'] may contains text comments before the line 'DOLOBFUSCKEY1...,DOLOBFUSCKEY2...'
$obfuscationkey = $reg [ 1 ];
2026-06-19 13:12:23 +00:00
dol_syslog ( " callApiToGetObfuscationKey we got the remote obfuscation key " , LOG_DEBUG );
dol_syslog ( " callApiToGetObfuscationKey we got the remote obfuscation key " , LOG_DEBUG , 0 , '_dolibarrgetkeyobfuscation' );
2026-06-15 10:33:55 +00:00
} else {
2026-06-18 14:35:18 +00:00
$obfuscationkey .= ' ' . $tmpresult [ 'content' ];
dol_syslog ( " callApiToGetObfuscationKey result error when getting obfuscation key: " . $tmpresult [ 'content' ], LOG_WARNING );
dol_syslog ( " callApiToGetObfuscationKey result error when getting obfuscation key: " . $tmpresult [ 'content' ], LOG_WARNING , 0 , '_dolibarrgetkeyobfuscation' );
2026-06-15 10:33:55 +00:00
}
}
2026-06-18 14:35:18 +00:00
} catch ( Exception $e ) {
$obfuscationkey .= ' ' . $e -> getMessage ();
dol_syslog ( " callApiToGetObfuscationKey result error " . $e -> getMessage (), LOG_ERR );
dol_syslog ( " callApiToGetObfuscationKey result error " . $e -> getMessage (), LOG_ERR , 0 , '_dolibarrgetkeyobfuscation' );
2026-06-15 10:33:55 +00:00
}
return $obfuscationkey ;
}
2026-02-09 18:11:37 +00:00
/**
* Call remote API service to push the last counter and signature
*
2026-03-23 14:45:04 +00:00
* @ param int $id Counter ID / value of ne record
* @ param string $signature Signature of new record
* @ param int $datecreation Date creation of new record
* @ param int $test Add property test to 1 if it is for test
* @ param int $previousid Counter ID / value of previous record
* @ param string $previoussignature Signature of previous record
* @ param int $previousdatecreation Date creation of previous record
* @ return int Return < 0 if KO , 0 if nothing done , > 0 if OK
2026-02-09 18:11:37 +00:00
*/
2026-06-15 10:41:18 +00:00
/*
2026-03-23 14:45:04 +00:00
function callApiToPushCounter ( $id , $signature , $datecreation , $test , $previousid , $previoussignature , $previousdatecreation )
2026-02-09 18:11:37 +00:00
{
global $mysoc , $conf ;
if ( isALNERunningVersion ( 1 ) && $mysoc -> country_code == 'FR' ) {
2026-02-12 21:55:44 +00:00
// Push last rowid + signature to remote dolibarr server
// TODO Do it only for selected events: BILL_VALIDATE ?
2026-02-09 18:11:37 +00:00
2026-02-15 15:57:48 +00:00
// Code here is similar to the one into printCodeForPing(), except that message code/properties/fields may differ.
2026-02-09 18:11:37 +00:00
$url_for_ping = getDolGlobalString ( 'MAIN_URL_FOR_PING' , " https://ping.dolibarr.org/ " );
$algo = 'sha256' ;
2026-03-05 09:38:52 +00:00
$hash_unique_id = getHashUniqueIdOfRegistration ( $algo ); // The hash of the unique IDof instance
$t = microtime ( true );
2026-03-23 14:45:04 +00:00
$micro = sprintf ( " %06d " , ( int ) (( $t - floor ( $t )) * 1000000 ));
2026-02-09 18:11:37 +00:00
2025-01-15 09:25:47 +00:00
$data = '' ;
$data .= 'hash_algo=dol_hash-' . urlencode ( $algo );
2026-02-09 18:11:37 +00:00
$data .= '&hash_unique_id=' . urlencode ( $hash_unique_id );
$data .= '&action=dolibarrpushcounter' ;
2026-03-05 09:38:52 +00:00
$data .= '&datesys=' . urlencode ( dol_print_date ( dol_now ( 'gmt' ), 'standard' , 'gmt' ) . '.' . $micro );
2026-02-09 18:11:37 +00:00
$data .= '&version=' . ( float ) DOL_VERSION ;
$data .= '&version_full=' . urlencode ( DOL_VERSION );
2026-02-19 23:49:35 +00:00
$data .= '&versionblockedlog=' . ( float ) getBlockedLogVersionToShow ();
$data .= '&versionblockedlog_full=' . urlencode ( getBlockedLogVersionToShow ());
2026-02-09 18:11:37 +00:00
$data .= '&entity=' . ( int ) $conf -> entity ;
$data .= '&lastrowid=' . ( int ) $id ;
$data .= '&lastsignature=' . urlencode ( $signature );
2026-03-23 15:12:14 +00:00
$data .= '&lastdatecreation=' . urlencode ( dol_print_date ( $datecreation , 'standard' , 'gmt' ));
2026-02-15 15:57:48 +00:00
$data .= '&previousrowid=' . ( int ) $previousid ;
$data .= '&previoussignature=' . urlencode ( $previoussignature );
2026-03-23 15:12:14 +00:00
$data .= '&previousdatecreation=' . urlencode ( dol_print_date ( $previousdatecreation , 'standard' , 'gmt' ));
2026-02-09 18:11:37 +00:00
if ( $test ) {
$data .= '&test=1' ;
}
$addheaders = array ();
$timeoutconnect = 1 ;
$timeoutresponse = 1 ;
2026-02-12 21:55:44 +00:00
$conf -> global -> BLOCKEDLOG_RANDOMRANGE_FOR_TRACKING = 1 ; // Force probability to 1
2026-02-09 18:11:37 +00:00
// Probability will be between 1/10 by default and 1/1 if const BLOCKEDLOG_RANDOMRANGE_FOR_TRACKING is set to 1. Can't be lower than 1/10.
$BLOCKEDLOG_RANDOMRANGE_FOR_TRACKING = min ( 10 , getDolGlobalInt ( 'BLOCKEDLOG_RANDOMRANGE_FOR_TRACKING' , 10 ));
$random = 1 ;
//$BLOCKEDLOG_RANDOMRANGE_FOR_TRACKING = 1; // To force track at every call
if ( $BLOCKEDLOG_RANDOMRANGE_FOR_TRACKING > 1 ) {
$random = random_int ( 1 , ( int ) $BLOCKEDLOG_RANDOMRANGE_FOR_TRACKING );
}
if ( $random == 1 ) { // 1 chance on BLOCKEDLOG_RANDOMRANGE_FOR_TRACKING
dol_syslog ( " callApiToPushCounter create Record is selected to be remotely pushed for tracking " , LOG_DEBUG );
include_once DOL_DOCUMENT_ROOT . '/core/lib/geturl.lib.php' ;
try {
$tmpresult = getURLContent ( $url_for_ping , 'POST' , $data , 1 , $addheaders , array ( 'https' ), 0 , - 1 , $timeoutconnect , $timeoutresponse , array (), '_dolibarrpushcounter' );
2026-02-24 13:16:34 +00:00
usleep ( 1000 );
2026-02-09 18:11:37 +00:00
// Add a warning in log in case of error
if ( $tmpresult [ 'http_code' ] != 200 ) {
$logerrormessage = 'Error: ' . $tmpresult [ 'http_code' ] . ' ' . $tmpresult [ 'content' ];
dol_syslog ( " callApiToPushCounter create Error when pushing track info: " . $logerrormessage , LOG_WARNING );
}
} catch ( Exception $e ) {
dol_syslog ( " callApiToPushCounter create Error " . $e -> getMessage (), LOG_ERR );
}
} else {
dol_syslog ( " callApiToPushCounter create Record is NOT selected to be remotely pushed for tracking " , LOG_DEBUG );
}
return 1 ;
}
return 0 ;
}
2026-06-15 10:41:18 +00:00
*/
2026-03-08 21:27:57 +00:00
/**
2026-04-02 11:56:49 +00:00
* Return if user is a tax auditor .
* Must be an external user and BLOCKEDLOG_FOR_TAX_AUDITOR must be set to 1 OR
* BLOCKEDLOG_FOR_TAX_AUDITOR must be set to 2
2026-03-08 21:27:57 +00:00
*
* @ return int Return > 0 if user is an external user so must be restricted to archive control feature
*/
function userIsTaxAuditor ()
{
global $user ;
2026-04-02 11:56:49 +00:00
return ((( getDolGlobalString ( 'BLOCKEDLOG_FOR_TAX_AUDITOR' ) && $user -> socid ) || getDolGlobalString ( 'BLOCKEDLOG_FOR_TAX_AUDITOR' ) == '2' ) ? 1 : 0 );
2026-03-08 21:27:57 +00:00
}
2026-04-02 16:51:49 +00:00
/**
* Add some information from the blockedlog module
*
* @ param TCPDF $pdf Object PDF
* @ param Translate $outputlangs Object lang for output
* @ param float $page_height Height of page
* @ param Facture $object Object invoice
* @ param int $w Width for text
* @ param float $posx Pos x
* @ param float $posy Pos y
* @ return void
*/
function pdfWriteBlockedLogSignature ( & $pdf , $outputlangs , $page_height , $object , & $w , & $posx , & $posy )
{
global $db ;
// Transaction ID
if ( isALNERunningVersion () && isModEnabled ( 'blockedlog' )) {
if ( $object -> status > $object :: STATUS_DRAFT ) {
2026-04-20 02:02:51 +00:00
$unalterablelogid = 'UNDEFINED' ; // By default
2026-04-02 16:51:49 +00:00
$sql = " SELECT signature FROM " . MAIN_DB_PREFIX . " blockedlog " ;
$sql .= " WHERE action = 'BILL_VALIDATE' AND element = 'facture' AND ref_object = ' " . $db -> escape ( $object -> ref ) . " ' " ;
$sql .= $db -> order ( 'rowid' , 'DESC' );
$sql .= $db -> plimit ( 1 );
$resql = $db -> query ( $sql );
if ( $resql ) {
$obj = $db -> fetch_object ( $resql );
if ( $obj ) {
$unalterablelogid = $obj -> signature ;
}
}
if ( $unalterablelogid != 'UNDEFINED' ) {
$pdf -> SetXY ( $posx , $posy );
$pdf -> SetTextColor ( 0 , 0 , 60 );
$pdf -> MultiCell ( $w , 3 , $outputlangs -> transnoentities ( " SignatureID " ) . " : " . dol_trunc ( strtoupper ( $unalterablelogid ), 10 ), '' , 'R' );
2026-04-20 02:02:51 +00:00
$posy += 3 ;
2026-04-02 16:51:49 +00:00
}
$isADuplicata = ( $object -> pos_print_counter >= 2 );
if ( $isADuplicata ) {
$pdf -> SetXY ( $posx , $posy );
$pdf -> SetTextColor ( 0 , 0 , 60 );
$pdf -> MultiCell ( $w , 3 , '*** ' . $outputlangs -> trans ( " DUPLICATA " ) . ( getDolGlobalString ( 'TAKEPOS_SHOW_PRINT_COUNTER_ON_RECEIPT' ) ? ' (no ' . ( $object -> pos_print_counter - 1 ) . ')' : '' ) . ' ***' , '' , 'R' );
2026-04-20 02:02:51 +00:00
$posy += 3 ;
2026-04-02 16:51:49 +00:00
}
}
if ( $object -> status == $object :: STATUS_DRAFT ) {
$pdf -> SetXY ( $posx , $posy );
$pdf -> SetTextColor ( 0 , 0 , 60 );
$pdf -> MultiCell ( $w , 3 , '*** ' . strtoupper ( $outputlangs -> trans ( " TemporaryReceipt " )) . ' ***' , '' , 'R' );
2026-04-20 02:02:51 +00:00
$posy += 3 ;
2026-04-02 16:51:49 +00:00
}
}
}
2026-06-17 22:22:28 +00:00
2026-06-18 11:22:36 +00:00
2026-06-17 22:22:28 +00:00
/**
2026-06-19 19:56:51 +00:00
* Migrate an old database to add the . end flag .
2026-06-17 22:22:28 +00:00
*
* @ return int Return - 1 if KO , 1 if OK
*/
function migrate_blockedlog_add_end_file ()
{
2026-06-18 00:34:14 +00:00
global $conf , $db , $langs , $mysoc ;
2026-06-17 22:22:28 +00:00
include_once DOL_DOCUMENT_ROOT . '/blockedlog/class/blockedlog.class.php' ;
$blocklog_static = new BlockedLog ( $db );
// Loop on all entities found in llx_blockedlog
$listofentities = array ();
$sql = " SELECT DISTINCT entity FROM " . MAIN_DB_PREFIX . " blockedlog " ;
$resql = $db -> query ( $sql );
if ( $resql ) {
while ( $obj = $db -> fetch_object ( $resql )) {
$listofentities [] = $obj -> entity ;
}
} else {
print '<tr class="trforrunsql"><td colspan="4">' ;
print '<b>' . $langs -> trans ( 'InitEndFlagFile' ) . " </b>: \n " ;
print 'Error: Failed to get list of entities in blockedlog table' ;
print '</td></tr>' ;
return - 1 ;
}
foreach ( $listofentities as $entity ) {
print '<tr class="trforrunsql"><td colspan="4">' ;
print '<b>' . $langs -> trans ( 'InitEndFlagFile' ) . " (entity = " . $entity . " )</b>: \n " ;
// Load the data of company (country, ...). Erase current one so this function can be used by migration script only.
$conf -> setEntityValues ( $db , $entity );
$mysoc = new Societe ( $db );
$mysoc -> setMysoc ( $conf );
$maxid = 0 ;
$sql = " SELECT MAX(rowid) as maxid FROM " . MAIN_DB_PREFIX . " blockedlog WHERE entity = " . (( int ) $entity );
$resql = $db -> query ( $sql );
if ( $resql ) {
$obj = $db -> fetch_object ( $resql );
if ( $obj ) {
$maxid = $obj -> maxid ;
}
} else {
print 'Error: Failed to get max id of blockedlog table' ;
print '</td></tr>' ;
return - 1 ;
}
$blocklog_static -> fetch ( $maxid );
$lockfile = $blocklog_static -> getEndOfChainFlagFile ();
// Lock acquired, we can now write the new .end file
$stringtowrite = 'BLOCKEDLOGHEAD ' . $blocklog_static -> id . " " . dol_print_date ( $blocklog_static -> date_creation , 'dayhourrfc' , 'gmt' ) . " " . ( string ) $blocklog_static -> signature ;
if ( isALNERunningVersion ( 1 ) && $mysoc -> country_code == 'FR' ) {
$remoteobfuscationkey = $blocklog_static -> getObfuscationKey ();
if ( empty ( $remoteobfuscationkey )) {
print " Error: Failed to get the remote obfuscation key. We can't record the end of chain flag file so we abort the transaction. " ;
print '</td></tr>' ;
return - 1 ;
}
2026-06-18 21:09:26 +00:00
$stringtowriteencoded = dolEncrypt ( $stringtowrite , $remoteobfuscationkey , '' , '' , 'dolobfuscationv1-' . $mysoc -> idprof1 . '-' . $blocklog_static -> id );
2026-06-17 22:22:28 +00:00
} else {
2026-06-18 21:09:26 +00:00
$stringtowriteencoded = dolEncrypt ( $stringtowrite , '' , '' , '' , 'dolcrypt-' . $mysoc -> idprof1 . '-' . $blocklog_static -> id );
2026-06-17 22:22:28 +00:00
}
2026-06-18 21:09:26 +00:00
// Update or create the .end flag file.
if ( defined ( 'BLOCKEDLOG_END_FLAG_IN_A_FILE' )) {
$lockhandle = fopen ( $lockfile , 'w+' );
if ( $lockhandle ) {
if ( fwrite ( $lockhandle , $stringtowriteencoded . " \n " ) === false ) {
print " Cannot write to the blockedlog .end file " . $lockfile ;
print '</td></tr>' ;
return - 1 ;
}
fclose ( $lockhandle ); // Remove the lock
dolChmod ( $lockfile );
} else {
print " Cannot open for writing the blockedlog .end file " . $lockfile ;
2026-06-17 22:22:28 +00:00
print '</td></tr>' ;
return - 1 ;
}
} else {
2026-06-18 21:09:26 +00:00
$sql = " DELETE FROM " . MAIN_DB_PREFIX . " const " ;
$sql .= " WHERE name = ' " . $db -> escape ( basename ( $lockfile )) . " ' AND entity = " . (( int ) $conf -> entity );
$resql = $db -> query ( $sql );
$sql = " INSERT INTO " . MAIN_DB_PREFIX . " const(name, value, type, visible, note, entity) " ;
$sql .= " VALUES(' " . $db -> escape ( basename ( $lockfile )) . " ', ' " . $db -> escape ( $stringtowriteencoded ) . " ', 'chaine', 0, 'Blockedlog end of chain flag', " . (( int ) $conf -> entity ) . " ) " ;
$resql = $db -> query ( $sql );
if ( ! $resql ) {
print " Cannot open for writing the blockedlog .end file " . basename ( $lockfile );
print '</td></tr>' ;
return - 1 ;
}
2026-06-17 22:22:28 +00:00
}
print $langs -> trans ( " Done " );
print '</td></tr>' ;
}
return 1 ;
}