Merge remote-tracking branch 'upstream/develop' into ajaxtooltip

This commit is contained in:
Frédéric FRANCE 2023-02-06 20:57:49 +01:00
commit a224d7c824
33 changed files with 71503 additions and 120 deletions

View File

@ -212,7 +212,7 @@ with
* Fix by replacing
if ($res[0] == PDF_TYPE_OBJECT)
with
if ($res && $res[0] == PDF_TYPE_OBJECT)
if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)

View File

@ -23,7 +23,6 @@ Method to encode/decode ZATCA string is available in test/phpunit/BarcodeTest.ph
* FOR QR-Bill in switzerland - Facture-QR
-----------------------------------------
Syntax of QR Code https://www.swiss-qr-invoice.org/fr/
Syntax of QR Code - See file ig-qr-bill-v2.2-fr.pdf (more doc on https://www.swiss-qr-invoice.org/downloads/)
Syntax of complentary field named "structured information of invoice S1": https://www.swiss-qr-invoice.org/downloads/qr-bill-s1-syntax-fr.pdf
To test/validate: https://www.swiss-qr-invoice.org/validator/

File diff suppressed because one or more lines are too long

View File

@ -43,10 +43,6 @@ $rowid = GETPOST('rowid', 'alpha');
$id = 1;
if (!$user->admin) {
accessforbidden();
}
$acts[0] = "activate";
$acts[1] = "disable";
$actl[0] = img_picto($langs->trans("Disabled"), 'switch_off', 'class="size15x"');
@ -125,6 +121,10 @@ $tabfieldcheck[1] = array();
$elementList = array();
$sourceList = array();
if (!$user->admin) {
accessforbidden();
}
/*
* Actions
@ -596,7 +596,9 @@ if ($id) {
print '<td colspan="7" class="right"><a name="'.(!empty($obj->rowid) ? $obj->rowid : $obj->code).'">&nbsp;</a>';
print '<input type="submit" class="button button-edit small" name="actionmodify" value="'.$langs->trans("Modify").'">';
print '&nbsp;<input type="submit" class="button button-cancel small" name="actioncancel" value="'.$langs->trans("Cancel").'"></td>';
print '&nbsp;';
print '<input type="submit" class="button button-cancel small" name="actioncancel" value="'.$langs->trans("Cancel").'">';
print '</td>';
} else {
$tmpaction = 'view';
$parameters = array('fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]);

View File

@ -5,7 +5,7 @@
* Copyright (C) 2005-2015 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
* Copyright (C) 2010-2013 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2011-2022 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2011-2023 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
* Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
@ -660,6 +660,10 @@ if (empty($reshook)) {
$price_ht_devise = '';
$price_ttc = '';
$price_ttc_devise = '';
$pu_ht = '';
$pu_ttc = '';
$pu_ht_devise = '';
$pu_ttc_devise = '';
if (GETPOST('price_ht') !== '') {
$price_ht = price2num(GETPOST('price_ht'), 'MU', 2);

View File

@ -45,6 +45,26 @@ class BankCateg // extends CommonObject
*/
public $label;
/**
* @var DoliDB
*/
protected $db;
/**
* @var string error
*/
public $error;
/**
* @var array errors
*/
public $errors;
/**
* @var array context
*/
public $context;
/**
* Constructor
@ -278,7 +298,7 @@ class BankCateg // extends CommonObject
// Load source object
$object->fetch($fromid);
$object->id = 0;
$object->statut = 0;
// $object->statut = 0;
// Create clone
$object->context['createfromclone'] = 'createfromclone';

View File

@ -9,7 +9,7 @@
* Copyright (C) 2019 Nicolas ZABOURI <info@inovea-conseil.com>
* Copyright (C) 2020 Tobias Sekan <tobias.sekan@startmail.com>
* Copyright (C) 2020 Josep Lluís Amador <joseplluis@lliuretic.cat>
* Copyright (C) 2021 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2021-2023 Frédéric France <frederic.france@netlogic.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -487,7 +487,7 @@ if (isModEnabled('don') && !empty($user->rights->don->lire)) {
print "</tr>\n";
}
} else {
print '<tr class="oddeven"><td colspan="4" class="opacitymedium">'.$langs->trans("None").'</td></tr>';
print '<tr class="oddeven"><td colspan="5" class="opacitymedium">'.$langs->trans("None").'</td></tr>';
}
print '</table></div><br>';
} else {

View File

@ -58,7 +58,7 @@ $hookmanager->initHooks(array('paymentcard', 'globalcard'));
// Load object
include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once.
$result = restrictedArea($user, $object->element, $object->id, 'paiement');
$result = restrictedArea($user, $object->element, $object->id, 'paiement'); // This also test permission on read invoice
// Security check
if ($user->socid) {

View File

@ -453,28 +453,26 @@ if ($search_priv != '0' && $search_priv != '1') {
}
// Search Categories
// Search Contact Categories
// Search Contact Categories
$searchCategoryContactList = $search_categ ? array($search_categ) : array();
$searchCategoryContactOperator = 0;
// Search for tag/category ($searchCategoryContactList is an array of ID)
// Search for tag/category ($searchCategoryContactList is an array of ID)
if (!empty($searchCategoryContactList)) {
$searchCategoryContactSqlList = array();
$listofcategoryid = '';
foreach ($searchCategoryContactList as $searchCategoryContact) {
if (intval($searchCategoryContact) == -2) {
$searchCategoryContactSqlList[] = "NOT EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE s.rowid = ck.fk_socpeople)";
$searchCategoryContactSqlList[] = "NOT EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE p.rowid = ck.fk_socpeople)";
} elseif (intval($searchCategoryContact) > 0) {
if ($searchCategoryContactOperator == 0) {
$searchCategoryContactSqlList[] = " EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE s.rowid = ck.fk_socpeople AND ck.fk_categorie = ".((int) $searchCategoryContact).")";
$searchCategoryContactSqlList[] = " EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE p.rowid = ck.fk_socpeople AND ck.fk_categorie = ".((int) $searchCategoryContact).")";
} else {
$listofcategoryid .= ($listofcategoryid ? ', ' : '') .((int) $searchCategoryContact);
}
}
}
if ($listofcategoryid) {
$searchCategoryContactSqlList[] = " EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE s.rowid = ck.fk_socpeople AND ck.fk_categorie IN (".$db->sanitize($listofcategoryid)."))";
$searchCategoryContactSqlList[] = " EXISTS (SELECT ck.fk_socpeople FROM ".MAIN_DB_PREFIX."categorie_contact as ck WHERE p.rowid = ck.fk_socpeople AND ck.fk_categorie IN (".$db->sanitize($listofcategoryid)."))";
}
if ($searchCategoryContactOperator == 1) {
if (!empty($searchCategoryContactSqlList)) {

View File

@ -1119,7 +1119,7 @@ abstract class CommonDocGenerator
public function getColumnContentXStart($colKey)
{
$colDef = $this->cols[$colKey];
return (isset($colDef['xStartPos']) ? $colDef['xStartPos'] : 0) + $colDef['content']['padding'][3];
return (isset($colDef['xStartPos']) ? $colDef['xStartPos'] : 0) + $colDef['content']['padding'][3];
}
/**

View File

@ -1735,6 +1735,7 @@ abstract class CommonInvoice extends CommonObject
$complementaryinfo .= '/30/'.$this->thirdparty->tva_intra;
}
include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
$bankaccount = new Account($this->db);
// Header
@ -1742,25 +1743,36 @@ abstract class CommonInvoice extends CommonObject
$s .= "SPC\n";
$s .= "0200\n";
$s .= "1\n";
// Info seller
// Info Seller ("Compte / Payable à")
if ($this->fk_account > 0) {
// Bank BAN if country is LI or CH
// TODO Add test on bank iban
// Bank BAN if country is LI or CH. TODO Add a test to check than IBAN start with CH or LI
$bankaccount->fetch($this->fk_account);
$s .= $bankaccount->iban."\n";
} else {
$s .= "\n";
}
// Seller
$s .= "S\n";
$s .= dol_trunc($mysoc->name, 70, 'right', 'UTF-8', 1)."\n";
$addresslinearray = explode("\n", $mysoc->address);
$s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
$s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
$s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
$s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
$s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";
// Final seller
if ($bankaccount->id > 0 && getDolGlobalString('PDF_SWISS_QRCODE_USE_OWNER_OF_ACCOUNT_AS_CREDITOR')) {
// If a bank account is prodived and we ask to use it as creditor, we use the bank address
// TODO In a future, we may always use this address, and if name/address/zip/town/country differs from $mysoc, we can use the address of $mysoc into the final seller field ?
$s .= "S\n";
$s .= dol_trunc($bankaccount->proprio, 70, 'right', 'UTF-8', 1)."\n";
$addresslinearray = explode("\n", $bankaccount->owner_address);
$s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
$s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
/*$s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
$s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
$s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";*/
} else {
$s .= "S\n";
$s .= dol_trunc($mysoc->name, 70, 'right', 'UTF-8', 1)."\n";
$addresslinearray = explode("\n", $mysoc->address);
$s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
$s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
$s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
$s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
$s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";
}
// Final seller (Ultimate seller) ("Créancier final" = "En faveur de")
$s .= "\n";
$s .= "\n";
$s .= "\n";
@ -1782,13 +1794,18 @@ abstract class CommonInvoice extends CommonObject
$s .= dol_trunc($this->thirdparty->country_code, 2, 'right', 'UTF-8', 1)."\n";
// ID of payment
$s .= "NON\n"; // NON or QRR
$s .= "\n"; // QR Code if previous field is QRR
$s .= "\n"; // QR Code reference if previous field is QRR
// Free text
if ($complementaryinfo) {
$s .= $complementaryinfo."\n";
} else {
$s .= "\n";
}
$s .= "EPD\n";
// More text, complementary info
if ($complementaryinfo) {
$s .= $complementaryinfo."\n";
}
$s .= "\n";
//var_dump($s);exit;
return $s;

View File

@ -882,7 +882,7 @@ abstract class CommonObject
}
if ($this->element == 'contact') {
$contactid = $this->id;
$thirdpartyid = empty($object->fk_soc) ? 0 : $object->fk_soc;
$thirdpartyid = empty($this->fk_soc) ? 0 : $this->fk_soc;
}
if ($this->element == 'user') {
$contactid = $this->contact_id;

View File

@ -1975,6 +1975,8 @@ class ExtraFields
if (!empty($extrafield_param) && is_array($extrafield_param)) {
$extrafield_param_list = array_keys($extrafield_param['options']);
}
// Set $extrafield_collapse_display_value (do we have to collapse/expand the group after the separator)
$extrafield_collapse_display_value = -1;
$expand_display = false;
if (is_array($extrafield_param_list) && count($extrafield_param_list) > 0) {
@ -1993,7 +1995,7 @@ class ExtraFields
$out .= '<'.$tagtype_dyn.' '.(!empty($colspan)?'colspan="' . $colspan . '"':'').'>';
// Some js code will be injected here to manage the collapsing of extrafields
// Output the picto
$out .= '<span class="cursorpointer '.($extrafield_collapse_display_value == 0 ? 'fas fa-square opacitymedium' : 'far fa-'.(($expand_display ? 'minus' : 'plus').'-square')).'"></span>';
$out .= '<span class="'.($extrafield_collapse_display_value ? 'cursorpointer ' : '').($extrafield_collapse_display_value == 0 ? 'fas fa-square opacitymedium' : 'far fa-'.(($expand_display ? 'minus' : 'plus').'-square')).'"></span>';
$out .= '&nbsp;';
$out .= '<strong>';
$out .= $langs->trans($this->attributes[$object->table_element]['label'][$key]);

View File

@ -99,12 +99,12 @@ function first_execution() {
function check_events() {
if (Notification.permission === "granted")
{
var newToken = 'notrequired';
var currentToken = 'notrequired';
const allMeta = document.getElementsByTagName("meta");
for (let i = 0; i < allMeta.length; i++) {
if (allMeta[i].getAttribute("name") == 'anti-csrf-token') {
newToken = allMeta[i].getAttribute('content');
console.log("newToken in page = "+newToken);
if (allMeta[i].getAttribute("name") == 'anti-csrf-currenttoken') {
currentToken = allMeta[i].getAttribute('content');
console.log("currentToken in page = "+currentToken);
}
}
time_js_next_test += time_auto_update;
@ -113,7 +113,7 @@ function check_events() {
$.ajax("<?php print DOL_URL_ROOT.'/core/ajax/check_notifications.php'; ?>", {
type: "post", // Usually post or get
async: true,
data: { time_js_next_test: time_js_next_test, forcechecknow: 1, token: newToken },
data: { time_js_next_test: time_js_next_test, forcechecknow: 1, token: currentToken },
dataType: "json",
success: function (result) {
//console.log(result);
@ -181,7 +181,7 @@ function check_events() {
$.ajax("<?php print DOL_URL_ROOT.'/core/ajax/check_notifications.php?action=stopreminder&listofreminderids='; ?>"+listofreminderids, {
type: "POST", // Usually post or get
async: true,
data: { time_js_next_test: time_js_next_test, token: newToken }
data: { time_js_next_test: time_js_next_test, token: currentToken }
});
} else {
console.log("No reminder to do found, next search at "+time_js_next_test);

View File

@ -1132,9 +1132,10 @@ function show_contacts($conf, $langs, $db, $object, $backtopage = '', $showuserl
$extrafieldsobjectkey = $contactstatic->table_element;
include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
$sql = "SELECT t.rowid, t.entity, t.lastname, t.firstname, t.fk_pays as country_id, t.civility, t.poste, t.phone as phone_pro, t.phone_mobile, t.phone_perso, t.fax, t.email, t.socialnetworks, t.statut, t.photo,";
$sql .= " t.civility as civility_id, t.address, t.zip, t.town, t.birthday";
$sql .= ", t.note_private";
$sql = "SELECT t.rowid, t.entity, t.lastname, t.firstname, t.fk_pays as country_id, t.civility, t.poste,";
$sql .= " t.phone as phone_pro, t.phone_mobile, t.phone_perso, t.fax, t.email, t.socialnetworks, t.statut, t.photo, t.fk_soc,";
$sql .= " t.civility as civility_id, t.address, t.zip, t.town, t.birthday,";
$sql .= " t.note_private";
$sql .= " FROM ".MAIN_DB_PREFIX."socpeople as t";
$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople_extrafields as ef on (t.rowid = ef.fk_object)";
$sql .= " WHERE t.fk_soc = ".((int) $object->id);
@ -1300,6 +1301,7 @@ function show_contacts($conf, $langs, $db, $object, $backtopage = '', $showuserl
$contactstatic->email = $obj->email;
$contactstatic->socialnetworks = $obj->socialnetworks;
$contactstatic->photo = $obj->photo;
$contactstatic->fk_soc = $obj->fk_soc;
$contactstatic->entity = $obj->entity;
$country_code = getCountry($obj->country_id, 2);

View File

@ -1328,6 +1328,10 @@ function dol_string_unaccent($str)
{
global $conf;
if (is_null($str)) {
return '';
}
if (utf8_check($str)) {
if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
$transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
@ -7175,6 +7179,10 @@ function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
*/
function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
{
if (is_null($stringtoencode)) {
return '';
}
if (!$nl2brmode) {
return nl2br($stringtoencode, $forxml);
} else {
@ -8948,17 +8956,17 @@ function verifCond($strToEvaluate)
*/
function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1')
{
// Only global variables can be changed by eval function and returned to caller
global $db, $langs, $user, $conf, $website, $websitepage;
global $action, $mainmenu, $leftmenu;
global $rights;
global $object;
global $mysoc;
global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
global $soc; // For backward compatibility
try {
// Only global variables can be changed by eval function and returned to caller
global $db, $langs, $user, $conf, $website, $websitepage;
global $action, $mainmenu, $leftmenu;
global $rights;
global $object;
global $mysoc;
global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
global $soc; // For backward compatibility
// Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
if ($onlysimplestring == '1') {
// We must accept: '1 && getDolGlobalInt("doesnotexist1") && $conf->global->MAIN_FEATURES_LEVEL'
@ -9053,11 +9061,11 @@ function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1'
}
}
} catch (Error $e) {
$error = 'Caught error : ';
$error .= $e->getMessage() . ', ';
$error .= 'Trace : ';
$error .= json_encode($e->getTrace());
error_log($error, 1);
$error = 'Caught error : ';
$error .= $e->getMessage() . ', ';
$error .= 'Trace : ';
$error .= json_encode($e->getTrace());
error_log($error, 1);
}
}

View File

@ -2867,13 +2867,13 @@ function getTaskProgressView($task, $label = true, $progressNumber = true, $hide
// good
$out .= ' <div class="progress-bar '.$progressBarClass.'" style="width: '.floatval($task->progress).'%" title="'.floatval($task->progress).'%">';
if (!empty($task->progress)) {
$out .= ' <div class="progress-bar progress-bar-consumed" style="width: '.floatval($progressCalculated / $task->progress * 100).'%" title="'.floatval($progressCalculated).'%"></div>';
$out .= ' <div class="progress-bar progress-bar-consumed" style="width: '.floatval($progressCalculated / (floatval($task->progress) === 0 ? 1 : $task->progress) * 100).'%" title="'.floatval($progressCalculated).'%"></div>';
}
$out .= ' </div>';
} else {
// bad
$out .= ' <div class="progress-bar progress-bar-consumed-late" style="width: '.floatval($progressCalculated).'%" title="'.floatval($progressCalculated).'%">';
$out .= ' <div class="progress-bar '.$progressBarClass.'" style="width: '.($task->progress ? floatval($task->progress / $progressCalculated * 100).'%' : '1px').'" title="'.floatval($task->progress).'%"></div>';
$out .= ' <div class="progress-bar '.$progressBarClass.'" style="width: '.($task->progress ? floatval($task->progress / (floatval($progressCalculated) === 0 ? 1 : $progressCalculated) * 100).'%' : '1px').'" title="'.floatval($task->progress).'%"></div>';
$out .= ' </div>';
}
$out .= ' </div>';

View File

@ -50,7 +50,7 @@ function shipping_prepare_head($object)
if ($conf->delivery_note->enabled && $user->rights->expedition->delivery->lire) {
// delivery link
$object->fetchObjectLinked($object->id, $object->element);
if (is_array($object->linkedObjectsIds['delivery']) && count($object->linkedObjectsIds['delivery']) > 0) { // If there is a delivery
if (isset($object->linkedObjectsIds['delivery']) && is_array($object->linkedObjectsIds['delivery']) && count($object->linkedObjectsIds['delivery']) > 0) { // If there is a delivery
// Take first one element of array
$tmp = reset($object->linkedObjectsIds['delivery']);

View File

@ -2,7 +2,7 @@
/* Copyright (C) 2012 Nicolas Villa aka Boyquotes http://informetic.fr
* Copyright (C) 2013 Florian Henry <florian.henry@open-concpt.pro>
* Copyright (C) 2013-2016 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2018-2021 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2018-2023 Frédéric France <frederic.france@netlogic.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -46,13 +46,13 @@ $backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');
$securitykey = GETPOST('securitykey', 'alpha');
if (!$user->hasRights('cron', 'create')) {
if (!$user->hasRight('cron', 'create')) {
accessforbidden();
}
$permissiontoadd = $user->hasRights('cron', 'create');
$permissiontoexecute = $user->hasRights('cron', 'execute');
$permissiontodelete = $user->hasRights('cron', 'delete');
$permissiontoadd = $user->hasRight('cron', 'create');
$permissiontoexecute = $user->hasRight('cron', 'execute');
$permissiontodelete = $user->hasRight('cron', 'delete');
/*

View File

@ -283,7 +283,7 @@ if ($action == 'create') {
print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="update_extras_line">';
print '<input type="hidden" name="origin" value="'.$origin.'">';
print '<input type="hidden" name="origin" value="'.$object->origin.'">';
print '<input type="hidden" name="id" value="'.$object->id.'">';
print '<input type="hidden" name="ref" value="'.$object->ref.'">';
@ -568,7 +568,7 @@ if ($action == 'create') {
$description = (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE') ? '' : dol_htmlentitiesbr($object->lines[$i]->description));
//print $description;
print $form->textwithtooltip($text, $description, 3, '', '', $i);
print_date_range($object->lines[$i]->date_start, $object->lines[$i]->date_end);
//print_date_range($object->lines[$i]->date_start, $object->lines[$i]->date_end);
if (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE')) {
print (!empty($object->lines[$i]->description) && $object->lines[$i]->description != $object->lines[$i]->product_label) ? '<br>'.dol_htmlentitiesbr($object->lines[$i]->description) : '';
}
@ -587,7 +587,7 @@ if ($action == 'create') {
print $text.' '.nl2br($object->lines[$i]->description);
}
print_date_range($objp->date_start, $objp->date_end);
//print_date_range($objp->date_start, $objp->date_end);
print "</td>\n";
}

View File

@ -3,7 +3,7 @@
* Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2006-2007 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
* Copyright (C) 2011-2018 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2011-2023 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
*
@ -847,7 +847,7 @@ class Delivery extends CommonObject
$line->product_type = $obj->fk_product_type;
$line->fk_origin_line = $obj->fk_origin_line;
$line->price = $obj->price;
$line->price = $obj->subprice;
$line->total_ht = $obj->total_ht;
// units

View File

@ -337,7 +337,7 @@ if (empty($reshook)) {
// Extrafields
$array_options[$i] = $extrafields->getOptionalsFromPost($object->table_element_line, $i);
// Unset extrafield
if (is_array($extrafields->attributes[$object->table_element_line]['label'])) {
if (isset($extrafields->attributes[$object->table_element_line]['label']) && is_array($extrafields->attributes[$object->table_element_line]['label'])) {
// Get extra fields
foreach ($extrafields->attributes[$object->table_element_line]['label'] as $key => $value) {
unset($_POST["options_".$key]);

View File

@ -53,7 +53,7 @@ $hookmanager->initHooks(array('supplierpaymentcard', 'globalcard'));
// Load object
include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once.
$result = restrictedArea($user, $object->element, $object->id, 'paiementfourn', '');
$result = restrictedArea($user, $object->element, $object->id, 'paiementfourn', ''); // This also test permission on read invoice
// Security check
if ($user->socid) {

View File

@ -1365,7 +1365,7 @@ class tcpdi_parser {
$obj = $this->getObjectVal($obj);
if (isset ($obj[1][1]['/Rotate'])) {
$res = $this->getObjectVal($obj[1][1]['/Rotate']);
if ($res[0] == PDF_TYPE_OBJECT)
if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)
return $res[1];
return $res;
} else {
@ -1373,7 +1373,7 @@ class tcpdi_parser {
return false;
} else {
$res = $this->_getPageRotation($obj[1][1]['/Parent']);
if ($res && $res[0] == PDF_TYPE_OBJECT)
if (isset($res[0]) && $res[0] == PDF_TYPE_OBJECT)
return $res[1];
return $res;
}

View File

@ -1228,7 +1228,7 @@ INSERT INTO llx_c_departements (fk_region, code_departement, ncc, nom) VALUES (6
INSERT INTO llx_c_departements (fk_region, code_departement, ncc, nom) VALUES (601, 'ZH','ZURICH','Zürich');
-- Taiwan Divisions / Provinces / Counties (rowid country=886)
-- Taiwan Divisions / Provinces / Counties (id country=213)
INSERT INTO llx_c_departements (fk_region, code_departement, cheflieu, tncc, nom) VALUES (21301, 'TW-KLU', 'KLU', NULL, '基隆市');
INSERT INTO llx_c_departements (fk_region, code_departement, cheflieu, tncc, nom) VALUES (21301, 'TW-TPE', 'TPE', NULL, '臺北市');
INSERT INTO llx_c_departements (fk_region, code_departement, cheflieu, tncc, nom) VALUES (21301, 'TW-TPH', 'TPH', NULL, '新北市');

View File

@ -141,6 +141,9 @@ ALTER TABLE llx_societe_rib ADD COLUMN state_id integer AFTER default_rib;
ALTER TABLE llx_societe_rib ADD COLUMN fk_country integer AFTER state_id;
ALTER TABLE llx_societe_rib ADD COLUMN currency_code varchar(3) AFTER fk_country;
DELETE FROM llx_societe_rib WHERE fk_soc = 0;
ALTER TABLE llx_societe_rib ADD CONSTRAINT llx_societe_rib_fk_societe FOREIGN KEY (fk_soc) REFERENCES llx_societe(rowid);
ALTER TABLE llx_user_rib ADD COLUMN state_id integer AFTER owner_address;
ALTER TABLE llx_user_rib ADD COLUMN fk_country integer AFTER state_id;
ALTER TABLE llx_user_rib ADD COLUMN currency_code varchar(3) AFTER fk_country;

View File

@ -17,5 +17,5 @@
-- ============================================================================
ALTER TABLE llx_product_attribute_combination_price_level ADD UNIQUE( fk_product_attribute_combination, fk_price_level);
ALTER TABLE llx_product_attribute_combination_price_level ADD UNIQUE INDEX uk_prod_att_comb_price_level(fk_product_attribute_combination, fk_price_level);

View File

@ -501,7 +501,7 @@ if ((!empty($conf->global->MAIN_VERSION_LAST_UPGRADE) && ($conf->global->MAIN_VE
// Creation of a token against CSRF vulnerabilities
if (!defined('NOTOKENRENEWAL') && !defined('NOSESSION')) {
// No token renewal on .css.php, .js.php and .json.php
// No token renewal on .css.php, .js.php and .json.php (even if the NOTOKENRENEWAL was not provided)
if (!preg_match('/\.(css|js|json)\.php$/', $_SERVER["PHP_SELF"])) {
// Rolling token at each call ($_SESSION['token'] contains token of previous page)
if (isset($_SESSION['newtoken'])) {
@ -1589,7 +1589,8 @@ function top_htmlhead($head, $title = '', $disablejs = 0, $disablehead = 0, $arr
print '<meta name="robots" content="'.($disablenoindex ? 'index' : 'noindex').($disablenofollow ? ',follow' : ',nofollow').'">'."\n"; // Do not index
print '<meta name="viewport" content="width=device-width, initial-scale=1.0">'."\n"; // Scale for mobile device
print '<meta name="author" content="Dolibarr Development Team">'."\n";
print '<meta name="anti-csrf-token" content="'.newToken().'">'."\n";
print '<meta name="anti-csrf-newtoken" content="'.newToken().'">'."\n";
print '<meta name="anti-csrf-currenttoken" content="'.currentToken().'">'."\n";
if (getDolGlobalInt('MAIN_FEATURES_LEVEL')) {
print '<meta name="MAIN_FEATURES_LEVEL" content="'.getDolGlobalInt('MAIN_FEATURES_LEVEL').'">'."\n";
}

View File

@ -2241,7 +2241,7 @@ if (preg_match('/^dopayment/', $action)) { // If we choosed/click on the payme
if (!empty($conf->global->STRIPE_USE_INTENT_WITH_AUTOMATIC_CONFIRMATION)) {
$noidempotency_key = (GETPOSTISSET('noidempotency') ? GETPOST('noidempotency', 'int') : 0); // By default noidempotency is unset, so we must use a different tag/ref for each payment. If set, we can pay several times the same tag/ref.
$paymentintent = $stripe->getPaymentIntent($amount, $currency, $tag, 'Stripe payment: '.$fulltag.(is_object($object) ? ' ref='.$object->ref : ''), $object, $stripecu, $stripeacc, $servicestatus, 0, 'automatic', false, null, 0, $noidempotency_key);
$paymentintent = $stripe->getPaymentIntent($amount, $currency, ($tag ? $tag : $fulltag), 'Stripe payment: '.$fulltag.(is_object($object) ? ' ref='.$object->ref : ''), $object, $stripecu, $stripeacc, $servicestatus, 0, 'automatic', false, null, 0, $noidempotency_key);
// The paymentintnent has status 'requires_payment_method' (even if paymentintent was already paid)
//var_dump($paymentintent);
if ($stripe->error) {

View File

@ -101,6 +101,7 @@ if (isModEnabled('stripe')) {
$stripecu = $stripe->getStripeCustomerAccount($object->id, $servicestatus, $site_account); // Get remote Stripe customer 'cus_...' (no remote access to Stripe here)
}
$error = 0;
/*
@ -128,7 +129,7 @@ if (empty($reshook)) {
}
if ($action == 'update') {
// Modification
// Update the bank account
if (!GETPOST('label', 'alpha') || !GETPOST('bank', 'alpha')) {
if (!GETPOST('label', 'alpha')) {
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label")), null, 'errors');
@ -154,6 +155,8 @@ if (empty($reshook)) {
}
if (!$error) {
$companybankaccount->old = dol_clone($companybankaccount);
$companybankaccount->socid = $object->id;
$companybankaccount->bank = GETPOST('bank', 'alpha');
@ -193,6 +196,12 @@ if (empty($reshook)) {
$companybankaccount->setAsDefault($id); // This will make sure there is only one default rib
}
if ($companypaymentmode->old->stripe_card_ref != $companypaymentmode->stripe_card_ref) {
if ($companybankaccount->old->iban != $companybankaccount->iban) {
// TODO If we modified the iban, we must also update the pm_ on Stripe side, or break the link completely ?
}
}
$url = $_SERVER["PHP_SELF"].'?socid='.$object->id;
header('Location: '.$url);
exit;
@ -201,7 +210,7 @@ if (empty($reshook)) {
}
if ($action == 'updatecard') {
// Modification
// Update credit card
if (!GETPOST('label', 'alpha') || !GETPOST('proprio', 'alpha') || !GETPOST('exp_date_month', 'alpha') || !GETPOST('exp_date_year', 'alpha')) {
if (!GETPOST('label', 'alpha')) {
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Label")), null, 'errors');
@ -220,6 +229,8 @@ if (empty($reshook)) {
$companypaymentmode->fetch($id);
if (!$error) {
$companybankaccount->old = dol_clone($companybankaccount);
$companypaymentmode->fk_soc = $object->id;
$companypaymentmode->bank = GETPOST('bank', 'alpha');
@ -247,6 +258,12 @@ if (empty($reshook)) {
$companypaymentmode->setAsDefault($id); // This will make sure there is only one default rib
}
if ($companypaymentmode->old->stripe_card_ref != $companypaymentmode->stripe_card_ref) {
if ($companybankaccount->old->number != $companybankaccount->number) {
// TODO If we modified the card, we must also update the pm_ on Stripe side, or break the link completely ?
}
}
$url = $_SERVER["PHP_SELF"].'?socid='.$object->id;
header('Location: '.$url);
exit;
@ -254,6 +271,7 @@ if (empty($reshook)) {
}
}
// Add bank account
if ($action == 'add') {
$error = 0;
@ -346,6 +364,7 @@ if (empty($reshook)) {
}
}
// Add credit card
if ($action == 'addcard') {
$error = 0;
@ -426,6 +445,7 @@ if (empty($reshook)) {
}
if ($action == 'confirm_deletecard' && GETPOST('confirm', 'alpha') == 'yes') {
// Delete the credi card
$companypaymentmode = new CompanyPaymentMode($db);
if ($companypaymentmode->fetch($ribid ? $ribid : $id)) {
// TODO This is currently done at bottom of page instead of asking confirm
@ -452,6 +472,7 @@ if (empty($reshook)) {
}
}
if ($action == 'confirm_delete' && GETPOST('confirm', 'alpha') == 'yes') {
// Delete the bank account
$companybankaccount = new CompanyBankAccount($db);
if ($companybankaccount->fetch($ribid ? $ribid : $id)) {
// TODO This is currently done at bottom of page instead of asking confirm
@ -516,6 +537,7 @@ if (empty($reshook)) {
}
}
if ($action == 'synccardtostripe') {
// Create the credit card on Stripe
$companypaymentmode = new CompanyPaymentMode($db);
$companypaymentmode->fetch($id);
@ -543,6 +565,7 @@ if (empty($reshook)) {
}
}
if ($action == 'syncsepatostripe') {
// Create the bank account on Stripe side
$companypaymentmode = new CompanyPaymentMode($db); // Get record in llx_societe_rib
$companypaymentmode->fetch($id);
@ -702,6 +725,7 @@ if (empty($reshook)) {
setEventMessages($e->getMessage(), null, 'errors');
}
} elseif ($action == 'deletecard' && $source) {
// Delete the credit card on Stripe side
try {
if (preg_match('/pm_/', $source)) {
$payment_method = \Stripe\PaymentMethod::retrieve($source, array("stripe_account" => $stripeacc));
@ -733,6 +757,7 @@ if (empty($reshook)) {
setEventMessages($e->getMessage(), null, 'errors');
}
} elseif ($action == 'delete' && $source) {
// Delete the bank account on Stripe side
try {
if (preg_match('/pm_/', $source)) {
$payment_method = \Stripe\PaymentMethod::retrieve($source, array("stripe_account" => $stripeacc));
@ -1905,7 +1930,7 @@ if ($socid && $action == 'edit' && $permissiontoaddupdatepaymentinformation) {
print $form->selectarray("frstrecur", $tblArraychoice, dol_escape_htmltag(GETPOST('frstrecur', 'alpha') ?GETPOST('frstrecur', 'alpha') : $companybankaccount->frstrecur), 0);
print '</td></tr>';
print '<tr><td>'.$langs->trans("StripeID")." ('src_....')</td>";
print '<tr><td>'.$langs->trans("StripeID")." ('pm_...' or 'src_...')</td>";
print '<td><input class="minwidth300" type="text" name="stripe_card_ref" value="'.$companypaymentmode->stripe_card_ref.'"></td></tr>';
print '</table>';
@ -1952,7 +1977,7 @@ if ($socid && $action == 'editcard' && $permissiontoaddupdatepaymentinformation)
print '<tr><td>'.$langs->trans("CVN").'</td>';
print '<td><input size="8" type="text" name="cvn" value="'.$companypaymentmode->cvn.'"></td></tr>';
print '<tr><td>'.$langs->trans("StripeID")." ('card_....')</td>";
print '<tr><td>'.$langs->trans("StripeID")." ('pm_... ir card_....')</td>";
print '<td><input class="minwidth300" type="text" name="stripe_card_ref" value="'.$companypaymentmode->stripe_card_ref.'"></td></tr>';
print '</table>';

View File

@ -465,12 +465,14 @@ class Stripe extends CommonObject
"currency" => $currency_code,
"payment_method_types" => $paymentmethodtypes,
"description" => $description,
"statement_descriptor_suffix" => $descriptor, // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
"statement_descriptor" => $descriptor, // For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
//"save_payment_method" => true,
"setup_future_usage" => "on_session",
"metadata" => $metadata
);
if ($descriptor) {
$dataforintent["statement_descriptor_suffix"] = $descriptor; // For card payment, 22 chars that appears on bank receipt (prefix into stripe setup + this suffix)
$dataforintent["statement_descriptor"] = $descriptor; // For SEPA, it will take only statement_descriptor, not statement_descriptor_suffix
}
if (!is_null($customer)) {
$dataforintent["customer"] = $customer;
}
@ -920,7 +922,7 @@ class Stripe extends CommonObject
global $conf, $user, $langs;
$sepa = null;
$sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix, sa.rum"; // stripe_card_ref is 'src_...' for Stripe SEPA
$sql = "SELECT sa.stripe_card_ref, sa.proprio, sa.iban_prefix as iban, sa.rum"; // stripe_card_ref is 'src_...' for Stripe SEPA
$sql .= " FROM ".MAIN_DB_PREFIX."societe_rib as sa";
$sql .= " WHERE sa.rowid = ".((int) $object->id); // We get record from ID, no need for filter on entity
$sql .= " AND sa.type = 'ban'"; //type ban to get normal bank account of customer (prelevement)
@ -958,40 +960,41 @@ class Stripe extends CommonObject
dol_syslog($this->error, LOG_WARNING);
}
} elseif ($createifnotlinkedtostripe) {
$iban = $obj->iban_prefix; //prefix ?
$iban = $obj->iban;
$ipaddress = getUserRemoteIP();
$metadata = array('dol_version'=>DOL_VERSION, 'dol_entity'=>$conf->entity, 'ipaddress'=>$ipaddress);
if (is_object($object)) {
$metadata['dol_type'] = $object->element;
$metadata['dol_id'] = $object->id;
$metadata['dol_thirdparty_id'] = $soc->id;
}
$description = 'SEPA for IBAN '.$iban;
$dataforcard = array(
'type'=>'sepa_debit',
"sepa_debit" => array('iban' => $iban),
'currency' => strtolower($conf->currency),
'usage' => 'reusable',
'owner' => array(
'billing_details' => array(
'name' => $soc->name,
'email' => !empty($soc->email) ? $soc->email : "",
),
"metadata" => array(
'dol_type'=>$object->element,
'dol_id'=>$object->id,
'dol_version'=>DOL_VERSION,
'dol_entity'=>$conf->entity,
'ipaddress'=>$ipaddress
)
"metadata" => $metadata
);
// Complete owner name
if (!empty($soc->town)) {
$dataforcard['owner']['address']['city']=$soc->town;
$dataforcard['billing_details']['address']['city']=$soc->town;
}
if (!empty($soc->country_code)) {
$dataforcard['owner']['address']['country']=$soc->country_code;
$dataforcard['billing_details']['address']['country']=$soc->country_code;
}
if (!empty($soc->address)) {
$dataforcard['owner']['address']['line1']=$soc->address;
$dataforcard['billing_details']['address']['line1']=$soc->address;
}
if (!empty($soc->zip)) {
$dataforcard['owner']['address']['postal_code']=$soc->zip;
$dataforcard['billing_details']['address']['postal_code']=$soc->zip;
}
if (!empty($soc->state)) {
$dataforcard['owner']['address']['state']=$soc->state;
$dataforcard['billing_details']['address']['state']=$soc->state;
}
//$a = \Stripe\Stripe::getApiKey();
@ -1012,15 +1015,17 @@ class Stripe extends CommonObject
dol_syslog("Try to create sepa_debit with data = ".json_encode($dataforcard));
$s = new \Stripe\StripeClient($stripeacc);
// TODO LMR Deprecated with the new Stripe API and SCA.
// TODO LMR Replace ->create() and ->createSource() and replace with ->getSetupIntent() to then, get the Payment mode with $payment_method = \Stripe\PaymentMethod::retrieve($setupintent->payment_method); ?
$sepa = $s->sources->create($dataforcard);
//var_dump($dataforcard);exit;
$sepa = $s->paymentMethods->create($dataforcard);
if (!$sepa) {
$this->error = 'Creation of sepa_debit on Stripe has failed';
$this->error = 'Creation of payment method sepa_debit on Stripe has failed';
} else {
// link customer and src
$cs = $cu->createSource($cu->id, array('source' => $sepa->id));
//$cs = $this->getSetupIntent($description, $soc, $cu, '', $status);
$dataforintent = array(['description'=> $description, 'payment_method_types' => ['sepa_debit'], 'customer' => $cu->id, 'payment_method' => $sepa->id], 'metadata'=>$metadata);
$cs = $s->setupIntents->create($dataforintent);
//$cs = $s->setupIntents->update($cs->id, ['payment_method' => $sepa->id]);
$cs = $s->setupIntents->confirm($cs->id, ['mandate_data' => ['customer_acceptance' => ['type' => 'offline']]]);
if (!$cs) {
$this->error = 'Link SEPA <-> Customer failed';
} else {
@ -1040,6 +1045,7 @@ class Stripe extends CommonObject
}
}
} catch (Exception $e) {
$sepa = null;
$this->error = $e->getMessage();
dol_syslog($this->error, LOG_WARNING);
}

View File

@ -304,9 +304,8 @@ section.setupsection {
div.tabBar textarea:focus {
border: 1px solid #aaa !important;
}
input:focus:not(.button):not(.buttonwebsite):not(.select2-search__field):not(#top-bookmark-search-input):not(.search_component_input):not(.input-search-takepos),
input:focus:not(.button):not(.buttonwebsite):not(.buttonreset):not(.select2-search__field):not(#top-bookmark-search-input):not(.search_component_input):not(.input-search-takepos),
select:focus, .select2-container--open [aria-expanded="false"].select2-selection--single {
/* div.tabBar input:focus, div.tabBar select:focus { */
border-bottom: 1px solid #666 !important;
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;

View File

@ -345,7 +345,7 @@ if ($mode == 'replacesite') {
$usercanedit = $user->rights->website->write;
$permissiontoadd = $user->rights->website->write; // Used by the include of actions_addupdatedelete.inc.php and actions_linkedfiles
$permissiontodelete = $user->rights->website->delete;
$permissiontodelete = $user->hasRight('website', 'delete');
/*
@ -2930,14 +2930,20 @@ if (!GETPOST('hide_websitemenu')) {
print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("CloneSite")).'" name="createfromclone">';
// Delete website
if ($website->status == $website::STATUS_VALIDATED) {
if (!$permissiontodelete) {
$disabled = ' disabled="disabled"';
$title = $langs->trans("WebsiteMustBeDisabled", $langs->transnoentitiesnoconv($website->LibStatut(0, 0)));
$title = $langs->trans("NotEnoughPermissions");
$url = '#';
} else {
$disabled = '';
$title = $langs->trans("Delete");
$url = $_SERVER["PHP_SELF"].'?action=deletesite&token='.newToken().'&website='.urlencode($website->ref);
if ($website->status == $website::STATUS_VALIDATED) {
$disabled = ' disabled="disabled"';
$title = $langs->trans("WebsiteMustBeDisabled", $langs->transnoentitiesnoconv($website->LibStatut(0, 0)));
$url = '#';
} else {
$disabled = '';
$title = $langs->trans("Delete");
$url = $_SERVER["PHP_SELF"].'?action=deletesite&token='.newToken().'&website='.urlencode($website->ref);
}
}
print '<a href="'.$url.'" class="buttonDelete bordertransp'.($disabled ? ' disabled' : '').'"'.$disabled.' title="'.dol_escape_htmltag($title).'">'.img_picto('', 'delete', 'class=""').'<span class="hideonsmartphone paddingleft">'.$langs->trans("Delete").'</span></a>';