diff --git a/ChangeLog b/ChangeLog
index 5b296aa88e2..b54d8df615e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -228,8 +228,9 @@ Following changes may create regressions for some external modules, but were nec
* If your database is PostgreSQL, you must use version 9.1.0 or more (Dolibarr need the SQL function CONCAT)
* If your database is MySQL or MariaDB, you need at least version 5.1
* Function set_price_level() has been renamed into setPriceLevel() to follow camelcase rules
-* removed deprecated substitution key __REFCLIENT__ (replaced with __REF_CLIENT__)
+* Removed deprecated substitution key __REFCLIENT__ (replaced with __REF_CLIENT__)
* Removed constant MAIN_COUNTRIES_IN_EEC. You can now set if country is in Europe or not from the dictionary of countries.
+* v14 seems to work correctly on PHP v8 but it generates a lot of verbose warnings. Currently, v14 i snot yet officialy supported with PHP 8.
***** ChangeLog for 13.0.4 compared to 13.0.3 *****
diff --git a/htdocs/accountancy/admin/index.php b/htdocs/accountancy/admin/index.php
index 98320613bfc..f8c0c95d3c1 100644
--- a/htdocs/accountancy/admin/index.php
+++ b/htdocs/accountancy/admin/index.php
@@ -91,7 +91,6 @@ if ($action == 'update') {
if (!$error) {
foreach ($list as $constname) {
$constvalue = GETPOST($constname, 'alpha');
- var_dump($constname);
if (!dolibarr_set_const($db, $constname, $constvalue, 'chaine', 0, '', $conf->entity)) {
$error++;
}
diff --git a/htdocs/accountancy/bookkeeping/list.php b/htdocs/accountancy/bookkeeping/list.php
index ce91219c306..bc260c77ed3 100644
--- a/htdocs/accountancy/bookkeeping/list.php
+++ b/htdocs/accountancy/bookkeeping/list.php
@@ -576,7 +576,7 @@ if (!empty($sortfield)) {
// Export into a file with format defined into setup (FEC, CSV, ...)
// Must be after definition of $sql
if ($action == 'export_fileconfirm' && $user->rights->accounting->mouvements->export) {
- // TODO Replace the fetchAll + ->export later that consume too much memory on large export with the query($sql) and loop on each line to export them.
+ // TODO Replace the fetchAll to get all ->line followed by call to ->export(). It consumew too much memory on large export. Replace this with the query($sql) and loop on each line to export them.
$result = $object->fetchAll($sortorder, $sortfield, 0, 0, $filter, 'AND', (empty($conf->global->ACCOUNTING_REEXPORT) ? 0 : 1));
if ($result < 0) {
@@ -822,7 +822,7 @@ if (!empty($arrayfields['t.piece_num']['checked'])) {
// Code journal
if (!empty($arrayfields['t.code_journal']['checked'])) {
print '
';
diff --git a/htdocs/accountancy/class/accountancyexport.class.php b/htdocs/accountancy/class/accountancyexport.class.php
index f007208cf57..a96c672608a 100644
--- a/htdocs/accountancy/class/accountancyexport.class.php
+++ b/htdocs/accountancy/class/accountancyexport.class.php
@@ -5,7 +5,7 @@
* Copyright (C) 2015 Florian Henry
* Copyright (C) 2015 Raphaël Doursenaud
* Copyright (C) 2016 Pierre-Henry Favre
- * Copyright (C) 2016-2020 Alexandre Spangaro
+ * Copyright (C) 2016-2021 Alexandre Spangaro
* Copyright (C) 2013-2017 Olivier Geffroy
* Copyright (C) 2017 Elarifr. Ari Elbaz
* Copyright (C) 2017-2019 Frédéric France
@@ -466,34 +466,43 @@ class AccountancyExport
}
/**
- * Export format : CIEL
+ * Export format : CIEL (Format XIMPORT)
+ * Format since 2003 compatible CIEL version > 2002 / Sage50
+ * Last review for this format : 2021/07/28 Alexandre Spangaro (aspangaro@open-dsi.fr)
+ *
+ * Help : https://sage50c.online-help.sage.fr/aide-technique/
+ * In sage software | Use menu : "Exchange" > "Importing entries..."
+ *
+ * If you want to force filename to "XIMPORT.TXT" for automatically import file present in a directory :
+ * use constant ACCOUNTING_EXPORT_XIMPORT_FORCE_FILENAME
*
* @param array $TData data
* @return void
*/
public function exportCiel(&$TData)
{
- global $conf;
-
$end_line = "\r\n";
$i = 1;
- $date_ecriture = dol_print_date(dol_now(), $conf->global->ACCOUNTING_EXPORT_DATE); // format must be yyyymmdd
+
foreach ($TData as $data) {
- $code_compta = $data->numero_compte;
+ $code_compta = length_accountg($data->numero_compte);
if (!empty($data->subledger_account)) {
- $code_compta = $data->subledger_account;
+ $code_compta = length_accounta($data->subledger_account);
}
+ $date_document = dol_print_date($data->doc_date, '%Y%m%d');
+ $date_echeance = dol_print_date($data->date_lim_reglement, '%Y%m%d');
+
$Tab = array();
- $Tab['num_ecriture'] = str_pad($i, 5);
+ $Tab['num_ecriture'] = str_pad($data->piece_num, 5);
$Tab['code_journal'] = str_pad($data->code_journal, 2);
- $Tab['date_ecriture'] = $date_ecriture;
- $Tab['date_ope'] = dol_print_date($data->doc_date, $conf->global->ACCOUNTING_EXPORT_DATE);
- $Tab['num_piece'] = str_pad(self::trunc($data->piece_num, 12), 12);
+ $Tab['date_ecriture'] = str_pad($date_document, 8, ' ', STR_PAD_LEFT);
+ $Tab['date_echeance'] = str_pad($date_echeance, 8, ' ', STR_PAD_LEFT);
+ $Tab['num_piece'] = str_pad(self::trunc($data->doc_ref, 12), 12);
$Tab['num_compte'] = str_pad(self::trunc($code_compta, 11), 11);
$Tab['libelle_ecriture'] = str_pad(self::trunc(dol_string_unaccent($data->doc_ref).dol_string_unaccent($data->label_operation), 25), 25);
- $Tab['montant'] = str_pad(abs($data->debit - $data->credit), 13, ' ', STR_PAD_LEFT);
+ $Tab['montant'] = str_pad(price2fec(abs($data->debit - $data->credit)), 13, ' ', STR_PAD_LEFT);
$Tab['type_montant'] = str_pad($data->sens, 1);
$Tab['vide'] = str_repeat(' ', 18);
$Tab['intitule_compte'] = str_pad(self::trunc(dol_string_unaccent($data->label_operation), 34), 34);
diff --git a/htdocs/accountancy/journal/bankjournal.php b/htdocs/accountancy/journal/bankjournal.php
index 34e533974fc..bdda4583d54 100644
--- a/htdocs/accountancy/journal/bankjournal.php
+++ b/htdocs/accountancy/journal/bankjournal.php
@@ -177,7 +177,7 @@ $accountingjournalstatic->fetch($id_journal);
$journal = $accountingjournalstatic->code;
$journal_label = $accountingjournalstatic->label;
-
+//print $sql;
dol_syslog("accountancy/journal/bankjournal.php", LOG_DEBUG);
$result = $db->query($sql);
if ($result) {
@@ -252,6 +252,7 @@ if ($result) {
);
// Set accountancy code for user
+ // $obj->accountancy_code is the accountancy_code of table u=user but it is defined only if a link with type 'user' exists)
$compta_user = (!empty($obj->accountancy_code) ? $obj->accountancy_code : '');
$tabuser[$obj->rowid] = array(
@@ -277,7 +278,7 @@ if ($result) {
$tabpay[$obj->rowid]["lib"] = dol_trunc($obj->label, 60);
}
- // Load of url links to the line into llx_bank
+ // Load of url links to the line into llx_bank (so load llx_bank_url)
$links = $object->get_url($obj->rowid); // Get an array('url'=>, 'url_id'=>, 'label'=>, 'type'=> 'fk_bank'=> )
// By default
@@ -287,7 +288,7 @@ if ($result) {
// get_url may return -1 which is not traversable
if (is_array($links) && count($links) > 0) {
- // Now loop on each link of record in bank.
+ // Now loop on each link of record in bank (code similar to bankentries_list.php)
foreach ($links as $key => $val) {
if (in_array($links[$key]['type'], array('sc', 'payment_sc', 'payment', 'payment_supplier', 'payment_vat', 'payment_expensereport', 'banktransfert', 'payment_donation', 'member', 'payment_loan', 'payment_salary', 'payment_various'))) {
// So we excluded 'company' and 'user' here. We want only payment lines
@@ -302,6 +303,7 @@ if ($result) {
}
}
+ // Special case to ask later to add more request to get information for old links without company link.
if ($links[$key]['type'] == 'withdraw') {
$tabmoreinfo[$obj->rowid]['withdraw'] = 1;
}
@@ -401,6 +403,44 @@ if ($result) {
$paymentsalstatic->label = $links[$key]['label'];
$tabpay[$obj->rowid]["lib"] .= ' '.$paymentsalstatic->getNomUrl(2);
$tabpay[$obj->rowid]["paymentsalid"] = $paymentsalstatic->id;
+
+ // This part of code is no more required. it is here to solve case where a link were missing (ith v14.0.0) and keep writing in accountancy complete.
+ // Note: A better way to fix this is to delete payement of salary and recreate it, or to fix the bookkeeping table manually after.
+ if (!empty($conf->global->ACCOUNTANCY_AUTOFIX_MISSING_LINK_TO_USEr_ON_SALARY_BANK_PAYMENT)) {
+ $tmpsalary = new Salary($db);
+ $tmpsalary->fetch($paymentsalstatic->id);
+ $tmpsalary->fetch_user($tmpsalary->fk_user);
+
+ $userstatic->id = $tmpsalary->user->id;
+ $userstatic->name = $tmpsalary->user->name;
+ $userstatic->email = $tmpsalary->user->email;
+ $userstatic->firstname = $tmpsalary->user->firstname;
+ $userstatic->lastname = $tmpsalary->user->lastname;
+ $userstatic->statut = $tmpsalary->user->statut;
+ $userstatic->accountancy_code = $tmpsalary->user->accountancy_code;
+
+ if ($userstatic->id > 0) {
+ $tabpay[$obj->rowid]["soclib"] = $userstatic->getNomUrl(1, 'accountancy', 0);
+ } else {
+ $tabpay[$obj->rowid]["soclib"] = '???'; // Should not happen
+ }
+
+ if (empty($obj->typeop_user)) { // Add test to avoid to add amount twice if a link already exists also on user.
+ $compta_user = $userstatic->accountancy_code;
+ if ($compta_user) {
+ $tabtp[$obj->rowid][$compta_user] += $obj->amount;
+ $tabuser[$obj->rowid] = array(
+ 'id' => $userstatic->id,
+ 'name' => dolGetFirstLastname($userstatic->firstname, $userstatic->lastname),
+ 'lastname' => $userstatic->lastname,
+ 'firstname' => $userstatic->firstname,
+ 'email' => $userstatic->email,
+ 'accountancy_code' => $compta_user,
+ 'status' => $userstatic->statut
+ );
+ }
+ }
+ }
} elseif ($links[$key]['type'] == 'payment_expensereport') {
$paymentexpensereportstatic->id = $links[$key]['url_id'];
$tabpay[$obj->rowid]["lib"] .= $paymentexpensereportstatic->getNomUrl(2);
@@ -468,7 +508,7 @@ if ($result) {
}
}
- // If no links were found to know the amount on thirdparty, we init it to account 'NotDefined'.
+ // If no links were found to know the amount on thirdparty/user, we init it to account 'NotDefined'.
if (empty($tabtp[$obj->rowid])) {
$tabtp[$obj->rowid]['NotDefined'] = $tabbq[$obj->rowid][$compta_bank];
}
@@ -1162,9 +1202,9 @@ if (empty($action) || $action == 'view') {
if ($tabtype[$key] == 'unknown') {
// We will accept writing, but into a waiting account
if (empty($conf->global->ACCOUNTING_ACCOUNT_SUSPENSE) || $conf->global->ACCOUNTING_ACCOUNT_SUSPENSE == '-1') {
- print ''.$langs->trans('UnknownAccountForThirdpartyAndWaitingAccountNotDefinedBlocking').'';
+ print ''.$langs->trans('UnknownAccountForThirdpartyAndWaitingAccountNotDefinedBlocking').'';
} else {
- print ''.$langs->trans('UnknownAccountForThirdparty', length_accountg($conf->global->ACCOUNTING_ACCOUNT_SUSPENSE)).''; // We will use a waiting account
+ print ''.$langs->trans('UnknownAccountForThirdparty', length_accountg($conf->global->ACCOUNTING_ACCOUNT_SUSPENSE)).''; // We will use a waiting account
}
} else {
// We will refuse writing
@@ -1187,7 +1227,7 @@ if (empty($action) || $action == 'view') {
if ($tabtype[$key] == 'member') {
$errorstring = 'MainAccountForSubscriptionPaymentNotDefined';
}
- print ''.$langs->trans($errorstring).'';
+ print ''.$langs->trans($errorstring).'';
}
} else {
print $accounttoshow;
@@ -1196,7 +1236,7 @@ if (empty($action) || $action == 'view') {
// Subledger account
print "
";
- if (in_array($tabtype[$key], array('payment', 'payment_supplier', 'payment_expensereport', 'payment_salary', 'payment_various'))) { // Type of payment with subledger
+ if (in_array($tabtype[$key], array('payment', 'payment_supplier', 'payment_expensereport', 'payment_salary', 'payment_various'))) { // Type of payments that uses a subledger
$accounttoshowsubledger = length_accounta($k);
if ($accounttoshow != $accounttoshowsubledger) {
if (empty($accounttoshowsubledger) || $accounttoshowsubledger == 'NotDefined') {
@@ -1205,14 +1245,14 @@ if (empty($action) || $action == 'view') {
var_dump($tabbq[$key]);*/
//print ''.$langs->trans("ThirdpartyAccountNotDefined").'';
if (!empty($tabcompany[$key]['code_compta'])) {
- if (in_array($tabtype[$key], array('payment_various'))) {
+ if (in_array($tabtype[$key], array('payment_various', 'payment_salary'))) {
// For such case, if subledger is not defined, we won't use subledger accounts.
- print ''.$langs->trans("ThirdpartyAccountNotDefinedOrThirdPartyUnknownSubledgerIgnored").'';
+ print ''.$langs->trans("ThirdpartyAccountNotDefinedOrThirdPartyUnknownSubledgerIgnored").'';
} else {
- print ''.$langs->trans("ThirdpartyAccountNotDefinedOrThirdPartyUnknown", $tabcompany[$key]['code_compta']).'';
+ print ''.$langs->trans("ThirdpartyAccountNotDefinedOrThirdPartyUnknown", $tabcompany[$key]['code_compta']).'';
}
} else {
- print ''.$langs->trans("ThirdpartyAccountNotDefinedOrThirdPartyUnknownBlocking").'';
+ print ''.$langs->trans("ThirdpartyAccountNotDefinedOrThirdPartyUnknownBlocking").'';
}
} else {
print $accounttoshowsubledger;
@@ -1220,10 +1260,15 @@ if (empty($action) || $action == 'view') {
}
}
print "
";
+
print "
".$reflabel."
";
+
print '
'.$val["type_payment"]."
";
+
print '
'.($mt < 0 ? price(-$mt) : '')."
";
+
print '
'.($mt >= 0 ? price($mt) : '')."
";
+
print "
";
}
}
diff --git a/htdocs/accountancy/tpl/export_journal.tpl.php b/htdocs/accountancy/tpl/export_journal.tpl.php
index 1700d391f54..ecb0f7a6b09 100644
--- a/htdocs/accountancy/tpl/export_journal.tpl.php
+++ b/htdocs/accountancy/tpl/export_journal.tpl.php
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+// $formatexportset ùust be defined
+
// Protection to avoid direct call of template
if (empty($conf) || !is_object($conf)) {
print "Error, template page can't be called as URL";
@@ -56,6 +58,8 @@ if ($accountancyexport->getFormatCode($formatexportset) == $accountancyexport::$
$endaccountingperiod = dol_print_date(dol_get_last_day($tmparray['year'], $tmparray['mon']), 'dayxcard');
$completefilename = $siren."FEC".$endaccountingperiod.".txt";
+} elseif ($accountancyexport->getFormatCode($formatexportset) == $accountancyexport::$EXPORT_TYPE_CIEL && $type_export == "general_ledger" && !empty($conf->global->ACCOUNTING_EXPORT_XIMPORT_FORCE_FILENAME)) {
+ $completefilename = "XIMPORT.TXT";
} else {
$completefilename = ($code ? $code."_" : "").($prefix ? $prefix."_" : "").$filename.($nodateexport ? "" : $date_export).".".$format;
}
diff --git a/htdocs/admin/clicktodial.php b/htdocs/admin/clicktodial.php
index 25ada4d2d89..d6525335ad8 100644
--- a/htdocs/admin/clicktodial.php
+++ b/htdocs/admin/clicktodial.php
@@ -134,7 +134,7 @@ if (!empty($conf->global->CLICKTODIAL_URL)) {
print '';
print $langs->trans("LinkToTestClickToDial", $user->login).' : ';
print '';
- print '';
+ print '';
print '';
$setupcomplete = 1;
@@ -149,7 +149,7 @@ if (!empty($conf->global->CLICKTODIAL_URL)) {
}
if ($setupcomplete) {
- print $langs->trans("LinkToTest", $user->login).': '.dol_print_phone($phonefortest, '', 0, 0, 'AC_TEL');
+ print $langs->trans("LinkToTest", $user->login).': '.dol_print_phone($phonefortest, '', 0, 0, 'AC_TEL', '', 1);
} else {
$langs->load("errors");
print '
';
+ }
+ }
+ }
+ }
+ }
+ $resaction .= ($resaction ? ' ' : $resaction);
+ $resaction .= '' . $langs->trans("ResultOfMailSending") . ': ' . "\n";
+ $resaction .= $langs->trans("NbSelected") . ': ' . count($toselect) . "\n ";
+ $resaction .= $langs->trans("NbIgnored") . ': ' . ($nbignored ? $nbignored : 0) . "\n ";
+ $resaction .= $langs->trans("NbSent") . ': ' . ($nbsent ? $nbsent : 0) . "\n ";
+
+ if ($nbsent) {
+ $action = ''; // Do not show form post if there was at least one successfull sent
+ //setEventMessages($langs->trans("EMailSentToNRecipients", $nbsent.'/'.count($toselect)), null, 'mesgs');
+ setEventMessages($langs->trans("EMailSentForNElements", $nbsent . '/' . count($toselect)), null, 'mesgs');
+ setEventMessages($resaction, null, 'mesgs');
+ } else {
+ //setEventMessages($langs->trans("EMailSentToNRecipients", 0), null, 'warnings'); // May be object has no generated PDF file
+ setEventMessages($resaction, null, 'warnings');
+ }
+
+ $action = 'list';
+ $massaction = '';
+}
+
+
+
+$parameters['toselect'] = $toselect;
+$parameters['uploaddir'] = $uploaddir;
+$parameters['massaction'] = $massaction;
+$parameters['diroutputmassaction'] = isset($diroutputmassaction) ? $diroutputmassaction : null;
+
+$reshook = $hookmanager->executeHooks('doMassActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) {
+ setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+}
diff --git a/htdocs/eventorganization/tpl/linkedobjectblock.tpl.php b/htdocs/eventorganization/tpl/linkedobjectblock.tpl.php
new file mode 100644
index 00000000000..f2bb6f0c3ae
--- /dev/null
+++ b/htdocs/eventorganization/tpl/linkedobjectblock.tpl.php
@@ -0,0 +1,61 @@
+
+ * Copyright (C) 2013 Juanjo Menent
+ * Copyright (C) 2014 Marcos García
+ *
+ * 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
+ * along with this program. If not, see .
+ */
+
+// Protection to avoid direct call of template
+if (empty($conf) || !is_object($conf)) {
+ print "Error, template page can't be called as URL";
+ exit;
+}
+
+echo "\n";
+
+global $user;
+
+$langs = $GLOBALS['langs'];
+$linkedObjectBlock = $GLOBALS['linkedObjectBlock'];
+$langs->load("eventorganization");
+
+$total = 0;
+foreach ($linkedObjectBlock as $key => $objectlink) {
+ echo '
__(Sincerely)__ __USER_SIGNATURE__', null, '1', null);
diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang
index 27a6b3337a4..771f0a83e7b 100644
--- a/htdocs/langs/en_US/accountancy.lang
+++ b/htdocs/langs/en_US/accountancy.lang
@@ -246,9 +246,9 @@ DescThirdPartyReport=Consult here the list of third-party customers and vendors
ListAccounts=List of the accounting accounts
UnknownAccountForThirdparty=Unknown third-party account. We will use %s
UnknownAccountForThirdpartyBlocking=Unknown third-party account. Blocking error
-ThirdpartyAccountNotDefinedOrThirdPartyUnknown=Third-party account not defined or third party unknown. We will use %s
+ThirdpartyAccountNotDefinedOrThirdPartyUnknown=Subledger account not defined or third party or user unknown. We will use %s
ThirdpartyAccountNotDefinedOrThirdPartyUnknownSubledgerIgnored=Third-party unknown and subledger not defined on the payment. We will keep the subledger account value empty.
-ThirdpartyAccountNotDefinedOrThirdPartyUnknownBlocking=Third-party account not defined or third party unknown. Blocking error.
+ThirdpartyAccountNotDefinedOrThirdPartyUnknownBlocking=Subledger account not defined or third party or user unknown. Blocking error.
UnknownAccountForThirdpartyAndWaitingAccountNotDefinedBlocking=Unknown third-party account and waiting account not defined. Blocking error
PaymentsNotLinkedToProduct=Payment not linked to any product / service
OpeningBalance=Opening balance
@@ -339,7 +339,7 @@ Modelcsv_normal=Classic export
Modelcsv_CEGID=Export for CEGID Expert Comptabilité
Modelcsv_COALA=Export for Sage Coala
Modelcsv_bob50=Export for Sage BOB 50
-Modelcsv_ciel=Export for Sage Ciel Compta or Compta Evolution
+Modelcsv_ciel=Export for Sage50, Ciel Compta or Compta Evo. (Format XIMPORT)
Modelcsv_quadratus=Export for Quadratus QuadraCompta
Modelcsv_ebp=Export for EBP
Modelcsv_cogilog=Export for Cogilog
diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang
index aca631eef99..76f5c18b70b 100644
--- a/htdocs/langs/en_US/admin.lang
+++ b/htdocs/langs/en_US/admin.lang
@@ -2148,4 +2148,5 @@ RandomlySelectedIfSeveral=Randomly selected if several pictures are available
DatabasePasswordObfuscated=Database password is obfuscated in conf file
DatabasePasswordNotObfuscated=Database password is NOT obfuscated in conf file
APIsAreNotEnabled=APIs modules are not enabled
-YouShouldSetThisToOff=You should set this to 0 or off
\ No newline at end of file
+YouShouldSetThisToOff=You should set this to 0 or off
+InstallAndUpgradeLockedBy=Install and upgrades are locked by the file %s
\ No newline at end of file
diff --git a/htdocs/langs/en_US/eventorganization.lang b/htdocs/langs/en_US/eventorganization.lang
index f361e6bc192..67a242e640a 100644
--- a/htdocs/langs/en_US/eventorganization.lang
+++ b/htdocs/langs/en_US/eventorganization.lang
@@ -119,6 +119,8 @@ EventType = Event type
LabelOfBooth=Booth label
LabelOfconference=Conference label
ConferenceIsNotConfirmed=Subcription not available, conference is not confirmed yet
+DateMustBeBeforeThan=%s must be before %s
+DateMustBeAfterThan=%s must be after %s
#
# Vote page
diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang
index 24c86cc885a..1c0dd638eeb 100644
--- a/htdocs/langs/en_US/mails.lang
+++ b/htdocs/langs/en_US/mails.lang
@@ -162,8 +162,8 @@ AdvTgtDeleteFilter=Delete filter
AdvTgtSaveFilter=Save filter
AdvTgtCreateFilter=Create filter
AdvTgtOrCreateNewFilter=Name of new filter
-NoContactWithCategoryFound=No contact/address with a category found
-NoContactLinkedToThirdpartieWithCategoryFound=No contact/address with a category found
+NoContactWithCategoryFound=No category found linked to some contacts/addresses
+NoContactLinkedToThirdpartieWithCategoryFound=No category found linked to some thirdparties
OutGoingEmailSetup=Outgoing emails
InGoingEmailSetup=Incoming emails
OutGoingEmailSetupForEmailing=Outgoing emails (for module %s)
diff --git a/htdocs/langs/en_US/ticket.lang b/htdocs/langs/en_US/ticket.lang
index 3c074688b8e..0b54b1d5fa8 100644
--- a/htdocs/langs/en_US/ticket.lang
+++ b/htdocs/langs/en_US/ticket.lang
@@ -66,7 +66,7 @@ NeedMoreInformation=Waiting for reporter feedback
NeedMoreInformationShort=Waiting for feedback
Answered=Answered
Waiting=Waiting
-Closed=Closed
+SolvedClosed=Solved
Deleted=Deleted
# Dict
@@ -186,9 +186,11 @@ TicketSeverity=Severity
ShowTicket=See ticket
RelatedTickets=Related tickets
TicketAddIntervention=Create intervention
-CloseTicket=Close ticket
-CloseATicket=Close a ticket
+CloseTicket=Close|Solve ticket
+AbandonTicket=Abandon ticket
+CloseATicket=Close|Solve a ticket
ConfirmCloseAticket=Confirm ticket closing
+ConfirmAbandonTicket=Do you confirm the closing of the ticket to status 'Abandonned'
ConfirmDeleteTicket=Please confirm ticket deleting
TicketDeletedSuccess=Ticket deleted with success
TicketMarkedAsClosed=Ticket marked as closed
diff --git a/htdocs/langs/fr_FR/eventorganization.lang b/htdocs/langs/fr_FR/eventorganization.lang
index 7ef2977adc3..ef7de0fd710 100644
--- a/htdocs/langs/fr_FR/eventorganization.lang
+++ b/htdocs/langs/fr_FR/eventorganization.lang
@@ -40,8 +40,8 @@ EVENTORGANIZATION_TEMPLATE_EMAIL_ASK_CONF = Modèle de courriel à envoyer aprè
EVENTORGANIZATION_TEMPLATE_EMAIL_ASK_BOOTH = Modèle de courriel à envoyer après avoir reçu une suggestion d'un stand.
EVENTORGANIZATION_TEMPLATE_EMAIL_AFT_SUBS_BOOTH = Modèle de courriel à envoyer après paiement d'une inscription à un stand.
EVENTORGANIZATION_TEMPLATE_EMAIL_AFT_SUBS_EVENT = Modèle de courriel à envoyer après paiement d'une inscription à un événement.
-EVENTORGANIZATION_TEMPLATE_EMAIL_BULK_SPEAKER = Modèle de courriel pour action de masse aux participants
-EVENTORGANIZATION_TEMPLATE_EMAIL_BULK_ATTENDES = Modèle de courriel pour action de masse aux intervenants
+EVENTORGANIZATION_TEMPLATE_EMAIL_BULK_SPEAKER = Modèle de courriel pour action de masse aux intervenants
+EVENTORGANIZATION_TEMPLATE_EMAIL_BULK_ATTENDES = Modèle de courriel pour action de masse aux participants
EVENTORGANIZATION_FILTERATTENDEES_CAT = Filtrer la liste de sélection des tiers dans la fiche/le formulaire de création des participants avec/selon la catégorie
EVENTORGANIZATION_FILTERATTENDEES_TYPE = Filtrer la liste de sélection des tiers dans la fiche/le formulaire de création des participants avec le type de client
diff --git a/htdocs/langs/zh_TW/opensurvey.lang b/htdocs/langs/zh_TW/opensurvey.lang
index 51a4283c721..ff927bfc863 100644
--- a/htdocs/langs/zh_TW/opensurvey.lang
+++ b/htdocs/langs/zh_TW/opensurvey.lang
@@ -1,61 +1,61 @@
# Dolibarr language file - Source file is en_US - opensurvey
-Survey=Poll
-Surveys=Polls
-OrganizeYourMeetingEasily=Organize your meetings and polls easily. First select the type of poll...
-NewSurvey=New poll
-OpenSurveyArea=Polls area
-AddACommentForPoll=You can add a comment into poll...
-AddComment=Add comment
-CreatePoll=Create poll
-PollTitle=Poll title
-ToReceiveEMailForEachVote=Receive an email for each vote
-TypeDate=Type date
-TypeClassic=Type standard
-OpenSurveyStep2=Select your dates among the free days (grey). The selected days are green. You can unselect a day previously selected by clicking again on it
-RemoveAllDays=Remove all days
-CopyHoursOfFirstDay=Copy hours of first day
-RemoveAllHours=Remove all hours
-SelectedDays=Selected days
-TheBestChoice=The best choice currently is
-TheBestChoices=The best choices currently are
-with=with
-OpenSurveyHowTo=If you agree to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.
-CommentsOfVoters=Comments of voters
-ConfirmRemovalOfPoll=Are you sure you want to remove this poll (and all votes)
-RemovePoll=Remove poll
-UrlForSurvey=URL to communicate to get a direct access to poll
-PollOnChoice=You are creating a poll to make a multi-choice for a poll. First enter all possible choices for your poll:
-CreateSurveyDate=Create a date poll
-CreateSurveyStandard=Create a standard poll
-CheckBox=Simple checkbox
-YesNoList=List (empty/yes/no)
-PourContreList=List (empty/for/against)
-AddNewColumn=Add new column
-TitleChoice=Choice label
-ExportSpreadsheet=Export result spreadsheet
-ExpireDate=極限日期
-NbOfSurveys=Number of polls
-NbOfVoters=No. of voters
-SurveyResults=Results
-PollAdminDesc=You are allowed to change all vote lines of this poll with button "Edit". You can, as well, remove a column or a line with %s. You can also add a new column with %s.
-5MoreChoices=5 more choices
-Against=Against
-YouAreInivitedToVote=You are invited to vote for this poll
-VoteNameAlreadyExists=This name was already used for this poll
-AddADate=Add a date
-AddStartHour=Add start hour
-AddEndHour=Add end hour
-votes=vote(s)
-NoCommentYet=No comments have been posted for this poll yet
-CanComment=Voters can comment in the poll
-CanSeeOthersVote=Voters can see other people's vote
-SelectDayDesc=For each selected day, you can choose, or not, meeting hours in the following format: - empty, - "8h", "8H" or "8:00" to give a meeting's start hour, - "8-11", "8h-11h", "8H-11H" or "8:00-11:00" to give a meeting's start and end hour, - "8h15-11h15", "8H15-11H15" or "8:15-11:15" for the same thing but with minutes.
-BackToCurrentMonth=Back to current month
-ErrorOpenSurveyFillFirstSection=You haven't filled the first section of the poll creation
-ErrorOpenSurveyOneChoice=Enter at least one choice
-ErrorInsertingComment=There was an error while inserting your comment
-MoreChoices=Enter more choices for the voters
-SurveyExpiredInfo=The poll has been closed or voting delay has expired.
-EmailSomeoneVoted=%s has filled a line.\nYou can find your poll at the link: \n%s
-ShowSurvey=Show survey
-UserMustBeSameThanUserUsedToVote=You must have voted and use the same user name that the one used to vote, to post a comment
+Survey=調查
+Surveys=調查
+OrganizeYourMeetingEasily=輕鬆組織會議和調查。首先選擇調查的類型...
+NewSurvey=新調查
+OpenSurveyArea=調查區
+AddACommentForPoll=您可以在調查中加入評論...
+AddComment=增加評論
+CreatePoll=建立調查
+PollTitle=調查標題
+ToReceiveEMailForEachVote=每次投票都會收到一封電子郵件
+TypeDate=日期類型
+TypeClassic=標準類型
+OpenSurveyStep2=在空閒日中選擇日期(灰色)。所選的日期為綠色。您可以通過再次點擊來取消先前選擇的日期
+RemoveAllDays=全部刪除
+CopyHoursOfFirstDay=複制第一天的時間
+RemoveAllHours=刪除所有時間
+SelectedDays=已選擇的日子
+TheBestChoice=目前最好的選擇是
+TheBestChoices=目前最好的選擇是
+with=與
+OpenSurveyHowTo=如果您同意在此次調查中投票,則必須給出自己的名字,選擇最適合您的值,並使用該行末尾的加號按鈕進行驗證。
+CommentsOfVoters=投票人的評論
+ConfirmRemovalOfPoll=您確定要刪除此調查(以及所有投票)嗎?
+RemovePoll=刪除調查
+UrlForSurvey=調查的直接網址
+PollOnChoice=您正在建立多項選擇的調查。首先為您的調查輸入所有可能的選擇:
+CreateSurveyDate=建立日期調查
+CreateSurveyStandard=建立標準調查
+CheckBox=簡易勾選框
+YesNoList=清單(空/是/否)
+PourContreList=清單(空/支持/反對)
+AddNewColumn=增加新欄位
+TitleChoice=選擇標籤
+ExportSpreadsheet=匯出結果表格
+ExpireDate=調查截止日
+NbOfSurveys=投票次數
+NbOfVoters=投票人數
+SurveyResults=結果
+PollAdminDesc=您可以使用“編輯”按鈕更改此民意調查的所有投票行。您也可以使用%s刪除列或行。您也可以使用%s增加新列。
+5MoreChoices=再多5個選項
+Against=反對
+YouAreInivitedToVote=您被邀請參加此項調查
+VoteNameAlreadyExists=此名稱已用於此調查
+AddADate=增加日期
+AddStartHour=增加開始時間
+AddEndHour=增加結束時間
+votes=投票
+NoCommentYet=尚未有此調查的評論
+CanComment=投票者可以在調查中發表評論
+CanSeeOthersVote=投票者可以看到其他人的投票
+SelectDayDesc=對於每個選定的日期,您可以依照以下格式選擇是否選擇開會時間: -空的 -“ 8h”,“ 8H”或“ 8:00”指定會議的開始時間, -“ 8-11”,“ 8h-11h”,“ 8H-11H”或“ 8:00-11:00”給出會議的開始和結束時間, -同樣的用“ 8h15-11h15”,“ 8H15-11H15”或“ 8:15-11:15”表示,但要加上分鐘。
+BackToCurrentMonth=回到目前月份
+ErrorOpenSurveyFillFirstSection=您尚未填寫建立調查的第一部分
+ErrorOpenSurveyOneChoice=輸入至少一個選項
+ErrorInsertingComment=插入您的評論時出錯
+MoreChoices=為投票者輸入更多選項
+SurveyExpiredInfo=投票已關閉或投票已到期。
+EmailSomeoneVoted=%s已填滿一行。\n您可以在以下連結找到您的調查:\n%s
+ShowSurvey=顯示調查
+UserMustBeSameThanUserUsedToVote=您必須已投票並使用與投票時相同的用戶名來發表評論
diff --git a/htdocs/product/class/productbatch.class.php b/htdocs/product/class/productbatch.class.php
index f10adee334e..4408afd9ff1 100644
--- a/htdocs/product/class/productbatch.class.php
+++ b/htdocs/product/class/productbatch.class.php
@@ -1,5 +1,5 @@
+/* Copyright (C) 2007-2021 Laurent Destailleur
* Copyright (C) 2013-2014 Cedric GROSS
*
* This program is free software; you can redistribute it and/or modify
@@ -44,8 +44,8 @@ class Productbatch extends CommonObject
public $tms = '';
public $fk_product_stock;
- public $sellby = '';
- public $eatby = '';
+ public $sellby = ''; // dlc
+ public $eatby = ''; // dmd/dluo
public $batch = '';
public $qty;
public $warehouseid;
diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php
index f2432ea25af..9f3390d34af 100644
--- a/htdocs/product/stock/massstockmove.php
+++ b/htdocs/product/stock/massstockmove.php
@@ -209,7 +209,7 @@ if ($action == 'createmovements' && !empty($user->rights->stock->mouvement->cree
);
if ($result1 < 0) {
$error++;
- setEventMessages($product->errors, $product->errorss, 'errors');
+ setEventMessages($product->error, $product->errors, 'errors');
}
// Add stock
@@ -224,7 +224,7 @@ if ($action == 'createmovements' && !empty($user->rights->stock->mouvement->cree
);
if ($result2 < 0) {
$error++;
- setEventMessages($product->errors, $product->errorss, 'errors');
+ setEventMessages($product->error, $product->errors, 'errors');
}
} else {
$arraybatchinfo = $product->loadBatchInfo($batch);
@@ -253,7 +253,7 @@ if ($action == 'createmovements' && !empty($user->rights->stock->mouvement->cree
);
if ($result1 < 0) {
$error++;
- setEventMessages($product->errors, $product->errorss, 'errors');
+ setEventMessages($product->error, $product->errors, 'errors');
}
// Add stock
@@ -271,7 +271,7 @@ if ($action == 'createmovements' && !empty($user->rights->stock->mouvement->cree
);
if ($result2 < 0) {
$error++;
- setEventMessages($product->errors, $product->errorss, 'errors');
+ setEventMessages($product->error, $product->errors, 'errors');
}
}
} else {
diff --git a/htdocs/product/traduction.php b/htdocs/product/traduction.php
index b2c3b0d0b2f..0cae81e36c7 100644
--- a/htdocs/product/traduction.php
+++ b/htdocs/product/traduction.php
@@ -62,98 +62,108 @@ if ($object->id > 0) {
restrictedArea($user, 'produit|service', $fieldvalue, 'product&product', '', '', $fieldtype);
}
+// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
+$hookmanager->initHooks(array('producttranslationcard', 'globalcard'));
+
/*
* Actions
*/
-// retour a l'affichage des traduction si annulation
-if ($cancel == $langs->trans("Cancel")) {
- $action = '';
+$parameters = array('id'=>$id, 'ref'=>$ref);
+$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) {
+ setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
}
-
-if ($action == 'delete' && GETPOST('langtodelete', 'alpha')) {
- $object = new Product($db);
- $object->fetch($id);
- $object->delMultiLangs(GETPOST('langtodelete', 'alpha'), $user);
- setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs');
- $action = '';
-}
-
-// Add translation
-if ($action == 'vadd' && $cancel != $langs->trans("Cancel") && ($user->rights->produit->creer || $user->rights->service->creer)) {
- $object = new Product($db);
- $object->fetch($id);
- $current_lang = $langs->getDefaultLang();
-
- // update de l'objet
- if (GETPOST("forcelangprod") == $current_lang) {
- $object->label = GETPOST("libelle");
- $object->description = dol_htmlcleanlastbr(GETPOST("desc", 'restricthtml'));
- $object->other = dol_htmlcleanlastbr(GETPOST("other", 'restricthtml'));
-
- $object->update($object->id, $user);
- } else {
- $object->multilangs[GETPOST("forcelangprod")]["label"] = GETPOST("libelle");
- $object->multilangs[GETPOST("forcelangprod")]["description"] = dol_htmlcleanlastbr(GETPOST("desc", 'restricthtml'));
- $object->multilangs[GETPOST("forcelangprod")]["other"] = dol_htmlcleanlastbr(GETPOST("other", 'restricthtml'));
- }
-
- // save in database
- if (GETPOST("forcelangprod")) {
- $result = $object->setMultiLangs($user);
- } else {
- $object->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Language"));
- $result = -1;
- }
-
- if ($result > 0) {
+if (empty($reshook)) {
+ // retour a l'affichage des traduction si annulation
+ if ($cancel == $langs->trans("Cancel")) {
$action = '';
- } else {
- $action = 'add';
- setEventMessages($object->error, $object->errors, 'errors');
}
-}
-// Edit translation
-if ($action == 'vedit' && $cancel != $langs->trans("Cancel") && ($user->rights->produit->creer || $user->rights->service->creer)) {
- $object = new Product($db);
- $object->fetch($id);
- $current_lang = $langs->getDefaultLang();
+ if ($action == 'delete' && GETPOST('langtodelete', 'alpha')) {
+ $object = new Product($db);
+ $object->fetch($id);
+ $object->delMultiLangs(GETPOST('langtodelete', 'alpha'), $user);
+ setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs');
+ $action = '';
+ }
- foreach ($object->multilangs as $key => $value) { // enregistrement des nouvelles valeurs dans l'objet
- if ($key == $current_lang) {
- $object->label = GETPOST("libelle-".$key);
- $object->description = dol_htmlcleanlastbr(GETPOST("desc-".$key, 'restricthtml'));
- $object->other = dol_htmlcleanlastbr(GETPOST("other-".$key, 'restricthtml'));
+ // Add translation
+ if ($action == 'vadd' && $cancel != $langs->trans("Cancel") && ($user->rights->produit->creer || $user->rights->service->creer)) {
+ $object = new Product($db);
+ $object->fetch($id);
+ $current_lang = $langs->getDefaultLang();
+
+ // update de l'objet
+ if (GETPOST("forcelangprod") == $current_lang) {
+ $object->label = GETPOST("libelle");
+ $object->description = dol_htmlcleanlastbr(GETPOST("desc", 'restricthtml'));
+ $object->other = dol_htmlcleanlastbr(GETPOST("other", 'restricthtml'));
+
+ $object->update($object->id, $user);
} else {
- $object->multilangs[$key]["label"] = GETPOST("libelle-".$key);
- $object->multilangs[$key]["description"] = dol_htmlcleanlastbr(GETPOST("desc-".$key, 'restricthtml'));
- $object->multilangs[$key]["other"] = dol_htmlcleanlastbr(GETPOST("other-".$key, 'restricthtml'));
+ $object->multilangs[GETPOST("forcelangprod")]["label"] = GETPOST("libelle");
+ $object->multilangs[GETPOST("forcelangprod")]["description"] = dol_htmlcleanlastbr(GETPOST("desc", 'restricthtml'));
+ $object->multilangs[GETPOST("forcelangprod")]["other"] = dol_htmlcleanlastbr(GETPOST("other", 'restricthtml'));
+ }
+
+ // save in database
+ if (GETPOST("forcelangprod")) {
+ $result = $object->setMultiLangs($user);
+ } else {
+ $object->error = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Language"));
+ $result = -1;
+ }
+
+ if ($result > 0) {
+ $action = '';
+ } else {
+ $action = 'add';
+ setEventMessages($object->error, $object->errors, 'errors');
}
}
- $result = $object->setMultiLangs($user);
- if ($result > 0) {
- $action = '';
- } else {
- $action = 'edit';
- setEventMessages($object->error, $object->errors, 'errors');
+ // Edit translation
+ if ($action == 'vedit' && $cancel != $langs->trans("Cancel") && ($user->rights->produit->creer || $user->rights->service->creer)) {
+ $object = new Product($db);
+ $object->fetch($id);
+ $current_lang = $langs->getDefaultLang();
+
+ foreach ($object->multilangs as $key => $value) { // enregistrement des nouvelles valeurs dans l'objet
+ if ($key == $current_lang) {
+ $object->label = GETPOST("libelle-" . $key);
+ $object->description = dol_htmlcleanlastbr(GETPOST("desc-" . $key, 'restricthtml'));
+ $object->other = dol_htmlcleanlastbr(GETPOST("other-" . $key, 'restricthtml'));
+ } else {
+ $object->multilangs[$key]["label"] = GETPOST("libelle-" . $key);
+ $object->multilangs[$key]["description"] = dol_htmlcleanlastbr(GETPOST("desc-" . $key, 'restricthtml'));
+ $object->multilangs[$key]["other"] = dol_htmlcleanlastbr(GETPOST("other-" . $key, 'restricthtml'));
+ }
+ }
+
+ $result = $object->setMultiLangs($user);
+ if ($result > 0) {
+ $action = '';
+ } else {
+ $action = 'edit';
+ setEventMessages($object->error, $object->errors, 'errors');
+ }
}
-}
-// Delete translation
-if ($action == 'vdelete' && $cancel != $langs->trans("Cancel") && ($user->rights->produit->creer || $user->rights->service->creer)) {
- $object = new Product($db);
- $object->fetch($id);
- $langtodelete = GETPOST('langdel', 'alpha');
+ // Delete translation
+ if ($action == 'vdelete' && $cancel != $langs->trans("Cancel") && ($user->rights->produit->creer || $user->rights->service->creer)) {
+ $object = new Product($db);
+ $object->fetch($id);
+ $langtodelete = GETPOST('langdel', 'alpha');
- $result = $object->delMultiLangs($langtodelete, $user);
- if ($result > 0) {
- $action = '';
- } else {
- $action = 'edit';
- setEventMessages($object->error, $object->errors, 'errors');
+ $result = $object->delMultiLangs($langtodelete, $user);
+ if ($result > 0) {
+ $action = '';
+ } else {
+ $action = 'edit';
+ setEventMessages($object->error, $object->errors, 'errors');
+ }
}
}
@@ -216,11 +226,15 @@ print dol_get_fiche_end();
*/
print "\n".'
'."\n";
-if ($action == '') {
- if ($user->rights->produit->creer || $user->rights->service->creer) {
- print ''.$langs->trans("Add").'';
- if ($cnt_trans > 0) {
- print ''.$langs->trans("Update").'';
+$parameters = array();
+$reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
+if (empty($reshook)) {
+ if ($action == '') {
+ if ($user->rights->produit->creer || $user->rights->service->creer) {
+ print '' . $langs->trans("Add") . '';
+ if ($cnt_trans > 0) {
+ print '' . $langs->trans("Update") . '';
+ }
}
}
}
@@ -263,6 +277,9 @@ if ($action == 'edit') {
}
}
+ $parameters = array();
+ $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
+
print ' ';
print '
';
@@ -334,6 +351,9 @@ if ($action == 'add' && ($user->rights->produit->creer || $user->rights->service
}
print '';
+ $parameters = array();
+ $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
+
print dol_get_fiche_end();
print '