diff --git a/ChangeLog b/ChangeLog
index 98c22d90bf7..48ba5daa562 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,27 @@
English Dolibarr ChangeLog
--------------------------------------------------------------
+***** ChangeLog for 13.0.4 compared to 13.0.2 *****
+
+FIX: Allow disabling of a module (not a dangerous action) even if there is problem with token (due to bugged modules).
+FIX: 13.0 - fatal - missing inclusion of ajax.lib.php for calling `ajax_autocompleter()`
+FIX: #17919 pictures in docs.
+FIX: #18006
+FIX: Accountancy - if we define a date start, automatic binding try to continue to solve old binding
+FIX: Accoutancy Limit date payment not registered on purchases operations
+FIX: Can't edit replacement invoice
+FIX: deposit can create credit note in payment conf
+FIX: division by zero on create
+FIX: holiday: balances not updated correctly with pgsql because of case sensitivity field
+FIX: holiday: status filter parameter has been renamed but not in links it was used
+FIX: List and Create Companies Left Menus
+FIX: method exists
+FIX: need to add payment sum to getlibstatus function in object linked block
+FIX: permission to close a proposal when using advanced permissions
+FIX: Problem of z-index with popup and top menu
+FIX: same thing on supplier orders
+FIX: Status of invoice when making a replacement invoice
+FIX: update contact birthday alert
***** ChangeLog for 14.0.0 compared to 13.0.0 *****
diff --git a/htdocs/blockedlog/class/blockedlog.class.php b/htdocs/blockedlog/class/blockedlog.class.php
index 112456480e1..a7be2fb3630 100644
--- a/htdocs/blockedlog/class/blockedlog.class.php
+++ b/htdocs/blockedlog/class/blockedlog.class.php
@@ -549,110 +549,111 @@ class BlockedLog
$totalamount = 0;
- if (!is_array($object->amounts) && $object->amount) {
- $object->amounts = array($object->id => $object->amount);
- }
+ // Loop on each invoice payment amount
+ if (is_array($object->amounts) && !empty($object->amounts)) {
+ $paymentpartnumber = 0;
+ foreach ($object->amounts as $objid => $amount) {
+ if (empty($amount)) {
+ continue;
+ }
- $paymentpartnumber = 0;
- foreach ($object->amounts as $objid => $amount) {
- if (empty($amount)) {
- continue;
- }
+ $totalamount += $amount;
- $totalamount += $amount;
+ $tmpobject = null;
+ if ($this->element == 'payment_supplier') {
+ include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
+ $tmpobject = new FactureFournisseur($this->db);
+ } elseif ($this->element == 'payment') {
+ include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
+ $tmpobject = new Facture($this->db);
+ } elseif ($this->element == 'payment_donation') {
+ include_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php';
+ $tmpobject = new Don($this->db);
+ } elseif ($this->element == 'payment_various') {
+ include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php';
+ $tmpobject = new PaymentVarious($this->db);
+ }
- $tmpobject = null;
- if ($this->element == 'payment_supplier') {
- include_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.facture.class.php';
- $tmpobject = new FactureFournisseur($this->db);
- } elseif ($this->element == 'payment') {
- include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
- $tmpobject = new Facture($this->db);
- } elseif ($this->element == 'payment_donation') {
- include_once DOL_DOCUMENT_ROOT.'/don/class/don.class.php';
- $tmpobject = new Don($this->db);
- } elseif ($this->element == 'payment_various') {
- include_once DOL_DOCUMENT_ROOT.'/compta/bank/class/paymentvarious.class.php';
- $tmpobject = new PaymentVarious($this->db);
- }
+ if (!is_object($tmpobject)) {
+ continue;
+ }
- if (!is_object($tmpobject)) {
- continue;
- }
+ $result = $tmpobject->fetch($objid);
- $result = $tmpobject->fetch($objid);
-
- if ($result <= 0) {
- $this->error = $tmpobject->error;
- $this->errors = $tmpobject->errors;
- dol_syslog("Failed to fetch object with id ".$objid, LOG_ERR);
- return -1;
- }
-
- $paymentpart = new stdClass();
- $paymentpart->amount = $amount;
-
- if (!in_array($this->element, array('payment_donation', 'payment_various'))) {
- $result = $tmpobject->fetch_thirdparty();
- if ($result == 0) {
- $this->error = 'Failed to fetch thirdparty for object with id '.$tmpobject->id;
- $this->errors[] = $this->error;
- dol_syslog("Failed to fetch thirdparty for object with id ".$tmpobject->id, LOG_ERR);
- return -1;
- } elseif ($result < 0) {
+ if ($result <= 0) {
$this->error = $tmpobject->error;
$this->errors = $tmpobject->errors;
+ dol_syslog("Failed to fetch object with id ".$objid, LOG_ERR);
return -1;
}
- $paymentpart->thirdparty = new stdClass();
- foreach ($tmpobject->thirdparty as $key => $value) {
- if (in_array($key, $arrayoffieldstoexclude)) {
- continue; // Discard some properties
- }
- if (!in_array($key, array(
- 'name', 'name_alias', 'ref_ext', 'address', 'zip', 'town', 'state_code', 'country_code', 'idprof1', 'idprof2', 'idprof3', 'idprof4', 'idprof5', 'idprof6', 'phone', 'fax', 'email', 'barcode',
- 'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur'
- ))) {
- continue; // Discard if not into a dedicated list
- }
- if (!is_object($value) && !is_null($value) && $value !== '') {
- $paymentpart->thirdparty->{$key} = $value;
- }
- }
- }
+ $paymentpart = new stdClass();
+ $paymentpart->amount = $amount;
- // Init object to avoid warnings
- if ($this->element == 'payment_donation') {
- $paymentpart->donation = new stdClass();
- } else {
- $paymentpart->invoice = new stdClass();
- }
+ if (!in_array($this->element, array('payment_donation', 'payment_various'))) {
+ $result = $tmpobject->fetch_thirdparty();
+ if ($result == 0) {
+ $this->error = 'Failed to fetch thirdparty for object with id '.$tmpobject->id;
+ $this->errors[] = $this->error;
+ dol_syslog("Failed to fetch thirdparty for object with id ".$tmpobject->id, LOG_ERR);
+ return -1;
+ } elseif ($result < 0) {
+ $this->error = $tmpobject->error;
+ $this->errors = $tmpobject->errors;
+ return -1;
+ }
- if ($this->element != 'payment_various') {
- foreach ($tmpobject as $key => $value) {
- if (in_array($key, $arrayoffieldstoexclude)) {
- continue; // Discard some properties
- }
- if (!in_array($key, array(
- 'ref', 'ref_client', 'ref_supplier', 'date', 'datef', 'type', 'total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2', 'revenuestamp', 'datepointoftax', 'note_public'
- ))) {
- continue; // Discard if not into a dedicated list
- }
- if (!is_object($value) && !is_null($value) && $value !== '') {
- if ($this->element == 'payment_donation') {
- $paymentpart->donation->{$key} = $value;
- } elseif ($this->element == 'payment_various') {
- $paymentpart->various->{$key} = $value;
- } else {
- $paymentpart->invoice->{$key} = $value;
+ $paymentpart->thirdparty = new stdClass();
+ foreach ($tmpobject->thirdparty as $key => $value) {
+ if (in_array($key, $arrayoffieldstoexclude)) {
+ continue; // Discard some properties
+ }
+ if (!in_array($key, array(
+ 'name', 'name_alias', 'ref_ext', 'address', 'zip', 'town', 'state_code', 'country_code', 'idprof1', 'idprof2', 'idprof3', 'idprof4', 'idprof5', 'idprof6', 'phone', 'fax', 'email', 'barcode',
+ 'tva_intra', 'localtax1_assuj', 'localtax1_value', 'localtax2_assuj', 'localtax2_value', 'managers', 'capital', 'typent_code', 'forme_juridique_code', 'code_client', 'code_fournisseur'
+ ))) {
+ continue; // Discard if not into a dedicated list
+ }
+ if (!is_object($value) && !is_null($value) && $value !== '') {
+ $paymentpart->thirdparty->{$key} = $value;
}
}
}
- $paymentpartnumber++; // first payment will be 1
- $this->object_data->payment_part[$paymentpartnumber] = $paymentpart;
+ // Init object to avoid warnings
+ if ($this->element == 'payment_donation') {
+ $paymentpart->donation = new stdClass();
+ } else {
+ $paymentpart->invoice = new stdClass();
+ }
+
+ if ($this->element != 'payment_various') {
+ foreach ($tmpobject as $key => $value) {
+ if (in_array($key, $arrayoffieldstoexclude)) {
+ continue; // Discard some properties
+ }
+ if (!in_array($key, array(
+ 'ref', 'ref_client', 'ref_supplier', 'date', 'datef', 'type', 'total_ht', 'total_tva', 'total_ttc', 'localtax1', 'localtax2', 'revenuestamp', 'datepointoftax', 'note_public'
+ ))) {
+ continue; // Discard if not into a dedicated list
+ }
+ if (!is_object($value) && !is_null($value) && $value !== '') {
+ if ($this->element == 'payment_donation') {
+ $paymentpart->donation->{$key} = $value;
+ } elseif ($this->element == 'payment_various') {
+ $paymentpart->various->{$key} = $value;
+ } else {
+ $paymentpart->invoice->{$key} = $value;
+ }
+ }
+ }
+
+ $paymentpartnumber++; // first payment will be 1
+ $this->object_data->payment_part[$paymentpartnumber] = $paymentpart;
+ }
}
+ } elseif (!empty($object->amount)) {
+ $totalamount = $object->amount;
}
$this->object_data->amount = $totalamount;
diff --git a/htdocs/compta/index.php b/htdocs/compta/index.php
index 8db217747ef..b1ad18e6461 100644
--- a/htdocs/compta/index.php
+++ b/htdocs/compta/index.php
@@ -226,7 +226,7 @@ if (!empty($conf->facture->enabled) && !empty($user->rights->facture->lire)) {
print $thirdpartystatic->getNomUrl(1, 'customer', 44);
print '';
if (!empty($conf->global->MAIN_SHOW_HT_ON_SUMMARY)) {
- print '
'.price($obj->total_ht).' | ';
+ print ''.price($obj->total_ht).' | ';
}
print ''.price($obj->total_ttc).' | ';
print ''.dol_print_date($db->jdate($obj->tms), 'day').' | ';
diff --git a/htdocs/core/boxes/box_factures.php b/htdocs/core/boxes/box_factures.php
index 2a97d738cd8..12382ccb583 100644
--- a/htdocs/core/boxes/box_factures.php
+++ b/htdocs/core/boxes/box_factures.php
@@ -180,7 +180,7 @@ class box_factures extends ModeleBoxes
);
$this->info_box_contents[$line][] = array(
- 'td' => 'class="right nowraponall"',
+ 'td' => 'class="right nowraponall amount"',
'text' => price($objp->total_ht, 0, $langs, 0, -1, -1, $conf->currency),
);
diff --git a/htdocs/core/boxes/box_factures_imp.php b/htdocs/core/boxes/box_factures_imp.php
index 391fc2e7cd0..400ae910749 100644
--- a/htdocs/core/boxes/box_factures_imp.php
+++ b/htdocs/core/boxes/box_factures_imp.php
@@ -182,7 +182,7 @@ class box_factures_imp extends ModeleBoxes
);
$this->info_box_contents[$line][] = array(
- 'td' => 'class="nowraponall right"',
+ 'td' => 'class="nowraponall right amount"',
'text' => price($objp->total_ht, 0, $langs, 0, -1, -1, $conf->currency),
);
diff --git a/htdocs/core/class/hookmanager.class.php b/htdocs/core/class/hookmanager.class.php
index cef41497381..5e24ee03fa4 100644
--- a/htdocs/core/class/hookmanager.class.php
+++ b/htdocs/core/class/hookmanager.class.php
@@ -272,8 +272,10 @@ class HookManager
$parameters['currentcontext'] = $context;
// Hooks that must return int (hooks with type 'addreplace')
if ($hooktype == 'addreplace') {
- $resaction += $actionclassinstance->$method($parameters, $object, $action, $this); // $object and $action can be changed by method ($object->id during creation for example or $action to go back to other action for example)
- if ($resaction < 0 || !empty($actionclassinstance->error) || (!empty($actionclassinstance->errors) && count($actionclassinstance->errors) > 0)) {
+ $resactiontmp = $actionclassinstance->$method($parameters, $object, $action, $this); // $object and $action can be changed by method ($object->id during creation for example or $action to go back to other action for example)
+ $resaction += $resactiontmp;
+
+ if ($resactiontmp < 0 || !empty($actionclassinstance->error) || (!empty($actionclassinstance->errors) && count($actionclassinstance->errors) > 0)) {
$error++;
$this->error = $actionclassinstance->error;
$this->errors = array_merge($this->errors, (array) $actionclassinstance->errors);
@@ -281,13 +283,22 @@ class HookManager
}
if (isset($actionclassinstance->results) && is_array($actionclassinstance->results)) {
- $this->resArray = array_merge($this->resArray, $actionclassinstance->results);
+ if ($resactiontmp > 0) {
+ $this->resArray = $actionclassinstance->results;
+ } else {
+ $this->resArray = array_merge($this->resArray, $actionclassinstance->results);
+ }
}
if (!empty($actionclassinstance->resprints)) {
- $this->resPrint .= $actionclassinstance->resprints;
+ if ($resactiontmp > 0) {
+ $this->resPrint = $actionclassinstance->resprints;
+ } else {
+ $this->resPrint .= $actionclassinstance->resprints;
+ }
}
} else {
// Generic hooks that return a string or array (printLeftBlock, formAddObjectLine, formBuilddocOptions, ...)
+
// TODO. this test should be done into the method of hook by returning nothing
if (is_array($parameters) && !empty($parameters['special_code']) && $parameters['special_code'] > 3 && $parameters['special_code'] != $actionclassinstance->module_number) {
continue;
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index c4a8452d1d1..a53162b7f2c 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -8487,8 +8487,10 @@ function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type,
if (!empty($hookmanager)) {
$parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head);
$reshook = $hookmanager->executeHooks('completeTabsHead', $parameters);
- if ($reshook > 0) {
+ if ($reshook > 0) { // Hook ask to replace completely the array
$head = $hookmanager->resArray;
+ } else { // Hook
+ $head = array_merge($head, $hookmanager->resArray);
}
$h = count($head);
}
diff --git a/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php
index cab0702c5c1..741cc4d09bc 100644
--- a/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php
+++ b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php
@@ -118,6 +118,8 @@ class InterfaceActionsBlockedLog extends DolibarrTriggers
foreach ($object->amounts as $amount) {
$amounts += price2num($amount);
}
+ } elseif (!empty($object->amount)) {
+ $amounts = $object->amount;
}
} elseif (strpos($action, 'PAYMENT') !== false && !in_array($action, array('PAYMENT_ADD_TO_BANK'))) {
$qualified++;
diff --git a/htdocs/install/mysql/migration/12.0.0-13.0.0.sql b/htdocs/install/mysql/migration/12.0.0-13.0.0.sql
index 1ebb8e380a7..29c2aad9bd4 100644
--- a/htdocs/install/mysql/migration/12.0.0-13.0.0.sql
+++ b/htdocs/install/mysql/migration/12.0.0-13.0.0.sql
@@ -367,7 +367,7 @@ ALTER TABLE llx_actioncomm_reminder ADD UNIQUE uk_actioncomm_reminder_unique (fk
ALTER TABLE llx_actioncomm_reminder ADD INDEX idx_actioncomm_reminder_status (status);
-
+ALTER TABLE llx_inventorydet ADD COLUMN fk_warehouse integer DEFAULT 0;
ALTER TABLE llx_inventorydet ADD UNIQUE uk_inventorydet(fk_inventory, fk_warehouse, fk_product, batch);
ALTER TABLE llx_commandedet ADD COLUMN ref_ext varchar(255) AFTER label;
diff --git a/htdocs/install/mysql/tables/llx_facture.sql b/htdocs/install/mysql/tables/llx_facture.sql
index acb43444ad2..ecdc44915bb 100644
--- a/htdocs/install/mysql/tables/llx_facture.sql
+++ b/htdocs/install/mysql/tables/llx_facture.sql
@@ -86,7 +86,7 @@ create table llx_facture
fk_incoterms integer, -- for incoterms
location_incoterms varchar(255), -- for incoterms
- fk_mode_transport integer, -- for intracomm report
+ fk_transport_mode integer, -- for intracomm report
situation_cycle_ref smallint, -- situation cycle reference
situation_counter smallint, -- situation counter
diff --git a/htdocs/install/mysql/tables/llx_facture_fourn.sql b/htdocs/install/mysql/tables/llx_facture_fourn.sql
index e5e78f87028..1b7401898ab 100644
--- a/htdocs/install/mysql/tables/llx_facture_fourn.sql
+++ b/htdocs/install/mysql/tables/llx_facture_fourn.sql
@@ -74,7 +74,7 @@ create table llx_facture_fourn
fk_incoterms integer, -- for incoterms
location_incoterms varchar(255), -- for incoterms
- fk_mode_transport integer, -- for intracomm report
+ fk_transport_mode integer, -- for intracomm report
model_pdf varchar(255),
last_main_doc varchar(255), -- relative filepath+filename of last main generated document
diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang
index 3ed7441afa5..cb97af5c183 100644
--- a/htdocs/langs/en_US/errors.lang
+++ b/htdocs/langs/en_US/errors.lang
@@ -264,6 +264,7 @@ ErrorAnAmountWithoutTaxIsRequired=Error, amount is mandatory
ErrorAPercentIsRequired=Error, please fill in the percentage correctly
ErrorYouMustFirstSetupYourChartOfAccount=You must first setup your chart of account
ErrorFailedToFindEmailTemplate=Failed to find template with code name %s
+ErrorDurationForServiceNotDefinedCantCalculateHourlyPrice=Duration not defined on service. No way to calculate the hourly price.
# Warnings
WarningParamUploadMaxFileSizeHigherThanPostMaxSize=Your PHP parameter upload_max_filesize (%s) is higher than PHP parameter post_max_size (%s). This is not a consistent setup.
diff --git a/htdocs/product/card.php b/htdocs/product/card.php
index 01a2d9acfdd..2472c764d7e 100644
--- a/htdocs/product/card.php
+++ b/htdocs/product/card.php
@@ -1234,7 +1234,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
if ($type == 1) {
print '| '.$langs->trans("Duration").' | ';
print '';
- print $formproduct->selectMeasuringUnits("duration_unit", "time", GETPOST('duration_value', 'alpha'), 0, 1);
+ print $formproduct->selectMeasuringUnits("duration_unit", "time", (GETPOSTISSET('duration_value') ? GETPOSTISSET('duration_value', 'alpha') : 'h'), 0, 1);
print ' |
';
}
diff --git a/htdocs/product/class/html.formproduct.class.php b/htdocs/product/class/html.formproduct.class.php
index 5be93a9b9d1..555bc08d03e 100644
--- a/htdocs/product/class/html.formproduct.class.php
+++ b/htdocs/product/class/html.formproduct.class.php
@@ -414,7 +414,7 @@ class FormProduct
dol_print_error($db);
return -1;
} else {
- $return .= '