From 18d5ec77509f489d7a7a8d77ccb8f96482fd0f31 Mon Sep 17 00:00:00 2001 From: Florian Mortgat Date: Tue, 16 Jun 2020 10:56:10 +0200 Subject: [PATCH 1/8] 11.0 - FIX - missing currency columns in supplier exports (copy-pasted from modFacture.class.php) --- htdocs/core/modules/modFournisseur.class.php | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/htdocs/core/modules/modFournisseur.class.php b/htdocs/core/modules/modFournisseur.class.php index b8539ad41c1..316000293e7 100644 --- a/htdocs/core/modules/modFournisseur.class.php +++ b/htdocs/core/modules/modFournisseur.class.php @@ -297,6 +297,14 @@ class modFournisseur extends DolibarrModules 'p.ref'=>'ProductRef','p.label'=>'ProductLabel','p.accountancy_code_buy'=>'ProductAccountancyBuyCode','project.rowid'=>'ProjectId', 'project.ref'=>'ProjectRef','project.title'=>'ProjectLabel' ); + if (! empty($conf->multicurrency->enabled)) + { + $this->export_fields_array[$r]['f.multicurrency_code'] = 'Currency'; + $this->export_fields_array[$r]['f.multicurrency_tx'] = 'CurrencyRate'; + $this->export_fields_array[$r]['f.multicurrency_total_ht'] = 'MulticurrencyAmountHT'; + $this->export_fields_array[$r]['f.multicurrency_total_tva'] = 'MulticurrencyAmountVAT'; + $this->export_fields_array[$r]['f.multicurrency_total_ttc'] = 'MulticurrencyAmountTTC'; + } //$this->export_TypeFields_array[$r]=array( // 's.rowid'=>"List:societe:CompanyName",'s.nom'=>'Text','s.address'=>'Text','s.zip'=>'Text','s.town'=>'Text','c.code'=>'Text','s.phone'=>'Text','s.siren'=>'Text','s.siret'=>'Text', // 's.ape'=>'Text','s.idprof4'=>'Text','s.tva_intra'=>'Text','f.ref'=>"Text",'f.datec'=>"Date",'f.datef'=>"Date",'f.total_ht'=>"Numeric",'f.total_ttc'=>"Numeric",'f.total_tva'=>"Numeric", @@ -423,6 +431,14 @@ class modFournisseur extends DolibarrModules 'f.fk_statut'=>'InvoiceStatus','f.note_public'=>"InvoiceNote",'p.rowid'=>'PaymentId','pf.amount'=>'AmountPayment', 'p.datep'=>'DatePayment','p.num_paiement'=>'PaymentNumber','p.fk_bank'=>'IdTransaction','project.rowid'=>'ProjectId','project.ref'=>'ProjectRef','project.title'=>'ProjectLabel' ); + if (! empty($conf->multicurrency->enabled)) + { + $this->export_fields_array[$r]['f.multicurrency_code'] = 'Currency'; + $this->export_fields_array[$r]['f.multicurrency_tx'] = 'CurrencyRate'; + $this->export_fields_array[$r]['f.multicurrency_total_ht'] = 'MulticurrencyAmountHT'; + $this->export_fields_array[$r]['f.multicurrency_total_tva'] = 'MulticurrencyAmountVAT'; + $this->export_fields_array[$r]['f.multicurrency_total_ttc'] = 'MulticurrencyAmountTTC'; + } //$this->export_TypeFields_array[$r]=array( // 's.rowid'=>"List:societe:CompanyName",'s.nom'=>'Text','s.address'=>'Text','s.zip'=>'Text','s.town'=>'Text','c.code'=>'Text','s.phone'=>'Text', // 's.siren'=>'Text','s.siret'=>'Text','s.ape'=>'Text','s.idprof4'=>'Text','s.tva_intra'=>'Text','f.ref'=>"Text",'f.datec'=>"Date",'f.datef'=>"Date", @@ -508,6 +524,14 @@ class modFournisseur extends DolibarrModules 'fd.total_tva'=>"LineTotalVAT",'fd.product_type'=>'TypeOfLineServiceOrProduct','fd.ref'=>'RefSupplier','fd.fk_product'=>'ProductId', 'p.ref'=>'ProductRef','p.label'=>'ProductLabel','project.rowid'=>'ProjectId','project.ref'=>'ProjectRef','project.title'=>'ProjectLabel' ); + if (! empty($conf->multicurrency->enabled)) + { + $this->export_fields_array[$r]['f.multicurrency_code'] = 'Currency'; + $this->export_fields_array[$r]['f.multicurrency_tx'] = 'CurrencyRate'; + $this->export_fields_array[$r]['f.multicurrency_total_ht'] = 'MulticurrencyAmountHT'; + $this->export_fields_array[$r]['f.multicurrency_total_tva'] = 'MulticurrencyAmountVAT'; + $this->export_fields_array[$r]['f.multicurrency_total_ttc'] = 'MulticurrencyAmountTTC'; + } if (empty($conf->global->SUPPLIER_ORDER_3_STEPS_TO_BE_APPROVED)) { unset($this->export_fields_array['f.date_approve2']); From e50d23932254fa1f26772a9920dd1b3c4d81186c Mon Sep 17 00:00:00 2001 From: gauthier Date: Tue, 16 Jun 2020 11:40:01 +0200 Subject: [PATCH 2/8] FIX : force rounding 2 on export ld compta --- htdocs/accountancy/class/accountancyexport.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/accountancy/class/accountancyexport.class.php b/htdocs/accountancy/class/accountancyexport.class.php index 8ecef4cde05..e7ec48944e1 100644 --- a/htdocs/accountancy/class/accountancyexport.class.php +++ b/htdocs/accountancy/class/accountancyexport.class.php @@ -985,7 +985,7 @@ class AccountancyExport print $racine_subledger_account . $separator; // deprecated CPTG & CPTA use instead // MONT - print price(abs($line->montant), 0, '', 1, 2).$separator; + print price(abs($line->montant), 0, '', 1, 2, 2).$separator; // CODC print $line->sens.$separator; // CPTG From 5d76b4781e6638a56780972656e857f61a604abc Mon Sep 17 00:00:00 2001 From: Xebax Date: Sun, 14 Jun 2020 14:42:54 +0200 Subject: [PATCH 3/8] FIX Upload of documents for members using the REST API. --- htdocs/api/class/api_documents.class.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/htdocs/api/class/api_documents.class.php b/htdocs/api/class/api_documents.class.php index a6f48df1026..5ee0945a8f7 100644 --- a/htdocs/api/class/api_documents.class.php +++ b/htdocs/api/class/api_documents.class.php @@ -608,6 +608,12 @@ class Documents extends DolibarrApi require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php'; $object = new ExpenseReport($this->db); } + else if ($modulepart == 'adherent' || $modulepart == 'member') + { + $modulepart='adherent'; + require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; + $object = new Adherent($this->db); + } // TODO Implement additional moduleparts else { From bbbcd5b6b322058b6231b43d65ff121fb2b34c1d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 16 Jun 2020 22:46:36 +0200 Subject: [PATCH 4/8] Fix phpcs --- htdocs/api/class/api_documents.class.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/htdocs/api/class/api_documents.class.php b/htdocs/api/class/api_documents.class.php index 5ee0945a8f7..ce734aadeb5 100644 --- a/htdocs/api/class/api_documents.class.php +++ b/htdocs/api/class/api_documents.class.php @@ -608,9 +608,9 @@ class Documents extends DolibarrApi require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php'; $object = new ExpenseReport($this->db); } - else if ($modulepart == 'adherent' || $modulepart == 'member') + elseif ($modulepart == 'adherent' || $modulepart == 'member') { - $modulepart='adherent'; + $modulepart = 'adherent'; require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; $object = new Adherent($this->db); } @@ -658,6 +658,7 @@ class Documents extends DolibarrApi else { if ($modulepart == 'invoice') $modulepart = 'facture'; + if ($modulepart == 'member') $modulepart = 'adherent'; $relativefile = $subdir; From 3d69a1dc70687da340c373d5d340fbd28fe7cbc5 Mon Sep 17 00:00:00 2001 From: Florian Mortgat Date: Wed, 17 Jun 2020 11:06:31 +0200 Subject: [PATCH 5/8] FIX 11.0 - multicurrency amount not fetched when fetching payments from llx_paiement or llx_paiementfourn --- .../compta/paiement/class/paiement.class.php | 38 ++++++++++--------- htdocs/fourn/class/paiementfourn.class.php | 37 +++++++++--------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/htdocs/compta/paiement/class/paiement.class.php b/htdocs/compta/paiement/class/paiement.class.php index 71c8596e00a..7294274d26b 100644 --- a/htdocs/compta/paiement/class/paiement.class.php +++ b/htdocs/compta/paiement/class/paiement.class.php @@ -68,9 +68,10 @@ class Paiement extends CommonObject */ public $montant; - public $amount; // Total amount of payment - public $amounts=array(); // Array of amounts - public $multicurrency_amounts=array(); // Array of amounts + public $amount; // Total amount of payment (in the main currency) + public $multicurrency_amount; // Total amount of payment (in the currency of the bank account) + public $amounts=array(); // array: invoice ID => amount for that invoice (in the main currency)> + public $multicurrency_amounts=array(); // array: invoice ID => amount for that invoice (in the invoice's currency)> public $author; public $paiementid; // Type of payment. Id saved into fields fk_paiement on llx_paiement public $paiementcode; // Code of payment. @@ -159,7 +160,7 @@ class Paiement extends CommonObject */ public function fetch($id, $ref = '', $fk_bank = '') { - $sql = 'SELECT p.rowid, p.ref, p.datep as dp, p.amount, p.statut, p.ext_payment_id, p.ext_payment_site, p.fk_bank,'; + $sql = 'SELECT p.rowid, p.ref, p.datep as dp, p.amount, p.statut, p.ext_payment_id, p.ext_payment_site, p.fk_bank, p.multicurrency_amount,'; $sql.= ' c.code as type_code, c.libelle as type_label,'; $sql.= ' p.num_paiement as num_payment, p.note,'; $sql.= ' b.fk_account'; @@ -179,20 +180,21 @@ class Paiement extends CommonObject if ($this->db->num_rows($resql)) { $obj = $this->db->fetch_object($resql); - $this->id = $obj->rowid; - $this->ref = $obj->ref?$obj->ref:$obj->rowid; - $this->date = $this->db->jdate($obj->dp); - $this->datepaye = $this->db->jdate($obj->dp); - $this->num_paiement = $obj->num_payment; // deprecated - $this->num_payment = $obj->num_payment; - $this->montant = $obj->amount; // deprecated - $this->amount = $obj->amount; - $this->note = $obj->note; - $this->type_label = $obj->type_label; - $this->type_code = $obj->type_code; - $this->statut = $obj->statut; - $this->ext_payment_id = $obj->ext_payment_id; - $this->ext_payment_site = $obj->ext_payment_site; + $this->id = $obj->rowid; + $this->ref = $obj->ref?$obj->ref:$obj->rowid; + $this->date = $this->db->jdate($obj->dp); + $this->datepaye = $this->db->jdate($obj->dp); + $this->num_paiement = $obj->num_payment; // deprecated + $this->num_payment = $obj->num_payment; + $this->montant = $obj->amount; // deprecated + $this->amount = $obj->amount; + $this->multicurrency_amount = $obj->multicurrency_amount; + $this->note = $obj->note; + $this->type_label = $obj->type_label; + $this->type_code = $obj->type_code; + $this->statut = $obj->statut; + $this->ext_payment_id = $obj->ext_payment_id; + $this->ext_payment_site = $obj->ext_payment_site; $this->bank_account = $obj->fk_account; // deprecated $this->fk_account = $obj->fk_account; diff --git a/htdocs/fourn/class/paiementfourn.class.php b/htdocs/fourn/class/paiementfourn.class.php index b18b054a65b..f8f1c0735b6 100644 --- a/htdocs/fourn/class/paiementfourn.class.php +++ b/htdocs/fourn/class/paiementfourn.class.php @@ -89,7 +89,7 @@ class PaiementFourn extends Paiement { $error=0; - $sql = 'SELECT p.rowid, p.ref, p.entity, p.datep as dp, p.amount, p.statut, p.fk_bank,'; + $sql = 'SELECT p.rowid, p.ref, p.entity, p.datep as dp, p.amount, p.statut, p.fk_bank, p.multicurrency_amount,'; $sql.= ' c.code as paiement_code, c.libelle as paiement_type,'; $sql.= ' p.num_paiement as num_payment, p.note, b.fk_account'; $sql.= ' FROM '.MAIN_DB_PREFIX.'paiementfourn as p'; @@ -111,23 +111,24 @@ class PaiementFourn extends Paiement if ($num > 0) { $obj = $this->db->fetch_object($resql); - $this->id = $obj->rowid; - $this->ref = $obj->ref; - $this->entity = $obj->entity; - $this->date = $this->db->jdate($obj->dp); - $this->datepaye = $this->db->jdate($obj->dp); - $this->num_paiement = $obj->num_payment; - $this->num_payment = $obj->num_payment; - $this->bank_account = $obj->fk_account; - $this->fk_account = $obj->fk_account; - $this->bank_line = $obj->fk_bank; - $this->montant = $obj->amount; - $this->amount = $obj->amount; - $this->note = $obj->note; - $this->note_private = $obj->note; - $this->type_code = $obj->paiement_code; - $this->type_label = $obj->paiement_type; - $this->statut = $obj->statut; + $this->id = $obj->rowid; + $this->ref = $obj->ref; + $this->entity = $obj->entity; + $this->date = $this->db->jdate($obj->dp); + $this->datepaye = $this->db->jdate($obj->dp); + $this->num_paiement = $obj->num_payment; + $this->num_payment = $obj->num_payment; + $this->bank_account = $obj->fk_account; + $this->fk_account = $obj->fk_account; + $this->bank_line = $obj->fk_bank; + $this->montant = $obj->amount; + $this->amount = $obj->amount; + $this->multicurrency_amount = $obj->multicurrency_amount; + $this->note = $obj->note; + $this->note_private = $obj->note; + $this->type_code = $obj->paiement_code; + $this->type_label = $obj->paiement_type; + $this->statut = $obj->statut; $error = 1; } else From 6660923e94aac1bf5be7c4341cdec9906fe281bf Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 17 Jun 2020 13:29:43 +0200 Subject: [PATCH 6/8] FIX Privilege escalation reported by wizlynx WLX-2020-011 --- htdocs/core/lib/security.lib.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/htdocs/core/lib/security.lib.php b/htdocs/core/lib/security.lib.php index 0c8f6fceca5..7c4a10f3a18 100644 --- a/htdocs/core/lib/security.lib.php +++ b/htdocs/core/lib/security.lib.php @@ -277,9 +277,12 @@ function restrictedArea($user, $features, $objectid = 0, $tableandshare = '', $f if (!$readok) accessforbidden(); //print "Read access is ok"; - // Check write permission from module (we need to know write permission to create but also to delete drafts record) + // Check write permission from module (we need to know write permission to create but also to delete drafts record or to upload files) $createok = 1; $nbko = 0; - if (GETPOST('action', 'aZ09') == 'create' || GETPOST('action', 'aZ09') == 'update' || ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete')) + $wemustcheckpermissionforcreate = (GETPOST('sendit', 'alpha') || GETPOST('linkit', 'alpha') || GETPOST('action', 'aZ09') == 'create' || GETPOST('action', 'aZ09') == 'update'); + $wemustcheckpermissionfordeletedraft = ((GETPOST("action", "aZ09") == 'confirm_delete' && GETPOST("confirm", "aZ09") == 'yes') || GETPOST("action", "aZ09") == 'delete'); + + if ($wemustcheckpermissionforcreate || $wemustcheckpermissionfordeletedraft) { foreach ($featuresarray as $feature) { @@ -341,7 +344,7 @@ function restrictedArea($user, $features, $objectid = 0, $tableandshare = '', $f // If a or and at least one ok if (preg_match('/\|/', $features) && $nbko < count($featuresarray)) $createok = 1; - if ((GETPOST('action', 'aZ09') == 'create' || GETPOST('action', 'aZ09') == 'update') && !$createok) accessforbidden(); + if ($wemustcheckpermissionforcreate && !$createok) accessforbidden(); //print "Write access is ok"; } From 23df5596e8ae109cec601b8641a33a1f0361db9e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 17 Jun 2020 13:42:38 +0200 Subject: [PATCH 7/8] FIX vulenrability reported by wizlynx WLX-2020-012 --- htdocs/core/lib/functions.lib.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 37dbcbf5ded..f1ff8ee121b 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -8562,15 +8562,18 @@ function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $u /** * Return if a file can contains executable content * - * @param string $filename File NamedRange + * @param string $filename File name to test * @return boolean True if yes, False if no */ function isAFileWithExecutableContent($filename) { - if (preg_match('/\.(htm|html|js|php|php\d+|phtml|pl|py|cgi|ksh|sh|bash|bat|cmd|wpk|exe|dmg)$/i', $filename)) + if (preg_match('/\.(htm|html|js|phar|php|php\d+|phtml|pht|pl|py|cgi|ksh|sh|shtml|bash|bat|cmd|wpk|exe|dmg)$/i', $filename)) { return true; } + if (preg_match('/^\./', $filename)) { // We consider file starting with a . as dangerous as executable files. For example .htaccess, .xxx + return true; + } return false; } From 6d6abd43203eae86ebe4ee4f3103e86d88a4bd2c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 17 Jun 2020 14:45:15 +0200 Subject: [PATCH 8/8] FIX vulenrability reported by wizlynx WLX-2020-012 --- htdocs/core/actions_linkedfiles.inc.php | 6 ++++-- htdocs/core/lib/files.lib.php | 14 ++++++++++++-- htdocs/core/lib/functions.lib.php | 4 +--- htdocs/langs/en_US/errors.lang | 1 + 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/htdocs/core/actions_linkedfiles.inc.php b/htdocs/core/actions_linkedfiles.inc.php index 90c93f6177b..f0bd9ddce4e 100644 --- a/htdocs/core/actions_linkedfiles.inc.php +++ b/htdocs/core/actions_linkedfiles.inc.php @@ -209,8 +209,10 @@ elseif ($action == 'renamefile' && GETPOST('renamefilesave', 'alpha')) if (empty($reshook)) { - if (! file_exists($destpath)) - { + if (preg_match('/^\./', $filenameto)) { + $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now. + setEventMessages($langs->trans("ErrorFilenameCantStartWithDot", $filenameto), null, 'errors'); + } elseif (! file_exists($destpath)) { $result = dol_move($srcpath, $destpath); if ($result) { diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 6614b22e5ed..23b631bae53 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -324,11 +324,13 @@ function completeFileArrayWithDatabaseInfo(&$filearray, $relativedir) // Complete filearray with properties found into $filearrayindatabase foreach ($filearray as $key => $val) { + $tmpfilename = preg_replace('/\.noexe$/', '', $filearray[$key]['name']); + $found = 0; // Search if it exists into $filearrayindatabase foreach ($filearrayindatabase as $key2 => $val2) { - if ($filearrayindatabase[$key2]['name'] == $filearray[$key]['name']) + if ($filearrayindatabase[$key2]['name'] == $tmpfilename) { $filearray[$key]['position_name'] = ($filearrayindatabase[$key2]['position'] ? $filearrayindatabase[$key2]['position'] : '0').'_'.$filearrayindatabase[$key2]['name']; $filearray[$key]['position'] = $filearrayindatabase[$key2]['position']; @@ -349,7 +351,7 @@ function completeFileArrayWithDatabaseInfo(&$filearray, $relativedir) $filearray[$key]['acl'] = ''; $rel_filename = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $filearray[$key]['fullname']); - if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filetorenameafter)) // If not a tmp file + if (!preg_match('/([\\/]temp[\\/]|[\\/]thumbs|\.meta$)/', $rel_filename)) // If not a tmp file { dol_syslog("list_of_documents We found a file called '".$filearray[$key]['name']."' not indexed into database. We add it"); include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; @@ -1529,6 +1531,7 @@ function dol_add_file_process($upload_dir, $allowoverwrite = 0, $donotupdatesess if (!empty($_FILES[$varfiles])) // For view $_FILES[$varfiles]['error'] { dol_syslog('dol_add_file_process upload_dir='.$upload_dir.' allowoverwrite='.$allowoverwrite.' donotupdatesession='.$donotupdatesession.' savingdocmask='.$savingdocmask, LOG_DEBUG); + if (dol_mkdir($upload_dir) >= 0) { $TFile = $_FILES[$varfiles]; @@ -1554,6 +1557,13 @@ function dol_add_file_process($upload_dir, $allowoverwrite = 0, $donotupdatesess $destfile=preg_replace('/__file__/', $TFile['name'][$i], $savingdocmask); } + $filenameto = basename($destfile); + if (preg_match('/^\./', $filenameto)) { + $langs->load("errors"); // key must be loaded because we can't rely on loading during output, we need var substitution to be done now. + setEventMessages($langs->trans("ErrorFilenameCantStartWithDot", $filenameto), null, 'errors'); + break; + } + // dol_sanitizeFileName the file name and lowercase extension $info = pathinfo($destfull); $destfull = $info['dirname'].'/'.dol_sanitizeFileName($info['filename'].($info['extension']!='' ? ('.'.strtolower($info['extension'])) : '')); diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index f1ff8ee121b..69a31d73d31 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -8571,9 +8571,7 @@ function isAFileWithExecutableContent($filename) { return true; } - if (preg_match('/^\./', $filename)) { // We consider file starting with a . as dangerous as executable files. For example .htaccess, .xxx - return true; - } + return false; } diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang index d145e75bad6..aac47df0d81 100644 --- a/htdocs/langs/en_US/errors.lang +++ b/htdocs/langs/en_US/errors.lang @@ -182,6 +182,7 @@ ErrorBadDefinitionOfMenuArrayInModuleDescriptor=Bad Definition Of Menu Array In ErrorSavingChanges=An error has occurred when saving the changes ErrorWarehouseRequiredIntoShipmentLine=Warehouse is required on the line to ship ErrorFileMustHaveFormat=File must have format %s +ErrorFilenameCantStartWithDot=Filename can't start with a '.' ErrorSupplierCountryIsNotDefined=Country for this vendor is not defined. Correct this first. ErrorsThirdpartyMerge=Failed to merge the two records. Request canceled. ErrorStockIsNotEnoughToAddProductOnOrder=Stock is not enough for product %s to add it into a new order.