From d7cc76b94a34e4442db9614287d71b8f1ddbe540 Mon Sep 17 00:00:00 2001 From: ptibogxiv Date: Mon, 26 Aug 2019 20:10:00 +0200 Subject: [PATCH 1/9] NEW purchase_prices API --- htdocs/product/class/api_products.class.php | 53 +++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/htdocs/product/class/api_products.class.php b/htdocs/product/class/api_products.class.php index 756bbde5b29..c0d415165c0 100644 --- a/htdocs/product/class/api_products.class.php +++ b/htdocs/product/class/api_products.class.php @@ -467,6 +467,59 @@ class Products extends DolibarrApi ); } + /** + * Get purchase prices for a product + * + * Return an array with product information. + * TODO implement getting a product by ref or by $ref_ext + * + * @param int $id ID of product + * @param string $ref Ref of element + * @param string $ref_ext Ref ext of element + * @param string $barcode Barcode of element + * @param int $includestockdata Load also information about stock (slower) + * @return array|mixed Data without useless information + * + * @url GET {id}/purchase_prices + * + * @throws 401 + * @throws 403 + * @throws 404 + * + */ + public function getPurchasePrices($id, $ref = '', $ref_ext = '', $barcode = '', $includestockdata = 0) + { + if (empty($id) && empty($ref) && empty($ref_ext) && empty($barcode)) { + throw new RestException(400, 'bad value for parameter id, ref, ref_ext or barcode'); + } + + $id = (empty($id)?0:$id); + + if(! DolibarrApiAccess::$user->rights->produit->lire) { + throw new RestException(403); + } + + $result = $this->product->fetch($id, $ref, $ref_ext, $barcode); + if(! $result ) { + throw new RestException(404, 'Product not found'); + } + + if(! DolibarrApi::_checkAccessToResource('product', $this->product->id)) { + throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login); + } + + if ($includestockdata) { + $this->product->load_stock(); + } + + if($result) { + $this->product = new ProductFournisseur($this->db); + $this->product->fetch($id, $ref); + $this->product->list_product_fournisseur_price($id, $sortfield, $sortorder, 0, 0); + } + + return $this->_cleanObjectDatas($this->product); + } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore /** From 75152cb2100c6872afcd1b739a80f9105c959927 Mon Sep 17 00:00:00 2001 From: Tim Otte Date: Tue, 27 Aug 2019 16:22:16 +0200 Subject: [PATCH 2/9] Added odt support to supplier orders --- htdocs/core/modules/modFournisseur.class.php | 27 + ...doc_generic_supplier_order_odt.modules.php | 517 ++++++++++++++++++ .../doctemplates/supplier_orders/index.html | 0 .../template_supplier_order.odt | Bin 0 -> 20174 bytes 4 files changed, 544 insertions(+) create mode 100644 htdocs/core/modules/supplier_order/pdf/doc_generic_supplier_order_odt.modules.php create mode 100644 htdocs/install/doctemplates/supplier_orders/index.html create mode 100644 htdocs/install/doctemplates/supplier_orders/template_supplier_order.odt diff --git a/htdocs/core/modules/modFournisseur.class.php b/htdocs/core/modules/modFournisseur.class.php index 8ba2f173ffd..fb108fb83ba 100644 --- a/htdocs/core/modules/modFournisseur.class.php +++ b/htdocs/core/modules/modFournisseur.class.php @@ -111,6 +111,13 @@ class modFournisseur extends DolibarrModules $this->const[$r][4] = 0; $r++; + $this->const[$r][0] = "SUPPLIER_ORDER_ADDON_PDF_ODT_PATH"; + $this->const[$r][1] = "chaine"; + $this->const[$r][2] = "DOL_DATA_ROOT/doctemplates/supplier_orders"; + $this->const[$r][3] = ''; + $this->const[$r][4] = 0; + $r++; + // Boxes $this->boxes = array( 0=>array('file'=>'box_graph_invoices_supplier_permonth.php','enabledbydefaulton'=>'Home'), @@ -629,6 +636,26 @@ class modFournisseur extends DolibarrModules $this->remove($options); + //ODT template + $src=DOL_DOCUMENT_ROOT.'/install/doctemplates/supplier_orders/template_supplier_order.odt'; + $dirodt=DOL_DATA_ROOT.'/doctemplates/supplier_orders'; + $dest=$dirodt.'/template_supplier_order.odt'; + + file_put_contents("test.txt", var_export($dest, true)); + + if (file_exists($src) && ! file_exists($dest)) + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + dol_mkdir($dirodt); + $result=dol_copy($src, $dest, 0, 0); + if ($result < 0) + { + $langs->load("errors"); + $this->error=$langs->trans('ErrorFailToCopyFile', $src, $dest); + return 0; + } + } + $sql = array( "DELETE FROM ".MAIN_DB_PREFIX."document_model WHERE nom = '".$this->db->escape($this->const[0][2])."' AND type = 'order_supplier' AND entity = ".$conf->entity, "INSERT INTO ".MAIN_DB_PREFIX."document_model (nom, type, entity) VALUES('".$this->db->escape($this->const[0][2])."','order_supplier',".$conf->entity.")", diff --git a/htdocs/core/modules/supplier_order/pdf/doc_generic_supplier_order_odt.modules.php b/htdocs/core/modules/supplier_order/pdf/doc_generic_supplier_order_odt.modules.php new file mode 100644 index 00000000000..08711154819 --- /dev/null +++ b/htdocs/core/modules/supplier_order/pdf/doc_generic_supplier_order_odt.modules.php @@ -0,0 +1,517 @@ + + * Copyright (C) 2012 Juanjo Menent + * Copyright (C) 2014 Marcos García + * Copyright (C) 2016 Charlie Benke + * Copyright (C) 2018-2019 Philippe Grand + * Copyright (C) 2018 Frédéric France + * Copyright (C) 2019 Tim Otte + * + * 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 . + * or see http://www.gnu.org/ + */ + +/** + * \file htdocs/core/modules/supplier_order/pdf/doc_generic_supplier_order_odt.modules.php + * \ingroup commande + * \brief File of class to build ODT documents for supplier orders + */ + +require_once DOL_DOCUMENT_ROOT.'/core/modules/supplier_order/modules_commandefournisseur.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/doc.lib.php'; + + +/** + * Class to build documents using ODF templates generator + */ +class doc_generic_supplier_order_odt extends ModelePDFSuppliersOrders +{ + /** + * Issuer + * @var Societe + */ + public $issuer; + + /** + * @var array Minimum version of PHP required by module. + * e.g.: PHP ≥ 5.5 = array(5, 5) + */ + public $phpmin = array(5, 5); + + /** + * @var string Dolibarr version of the loaded document + */ + public $version = 'dolibarr'; + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + global $conf, $langs, $mysoc; + + // Load translation files required by the page + $langs->loadLangs(array("main","companies")); + + $this->db = $db; + $this->name = "ODT templates"; + $this->description = $langs->trans("DocumentModelOdt"); + $this->scandir = 'SUPPLIER_ORDER_ADDON_PDF_ODT_PATH'; // Name of constant that is used to save list of directories to scan + + // Dimension page pour format A4 + $this->type = 'odt'; + $this->page_largeur = 0; + $this->page_hauteur = 0; + $this->format = array($this->page_largeur,$this->page_hauteur); + $this->marge_gauche=0; + $this->marge_droite=0; + $this->marge_haute=0; + $this->marge_basse=0; + + $this->option_logo = 1; // Affiche logo + $this->option_tva = 0; // Gere option tva COMMANDE_TVAOPTION + $this->option_modereg = 0; // Affiche mode reglement + $this->option_condreg = 0; // Affiche conditions reglement + $this->option_codeproduitservice = 0; // Affiche code produit-service + $this->option_multilang = 1; // Dispo en plusieurs langues + $this->option_escompte = 0; // Affiche si il y a eu escompte + $this->option_credit_note = 0; // Support credit notes + $this->option_freetext = 1; // Support add of a personalised text + $this->option_draft_watermark = 0; // Support add of a watermark on drafts + + // Recupere issuer + $this->issuer=$mysoc; + if (! $this->issuer->country_code) $this->issuer->country_code=substr($langs->defaultlang, -2); // By default if not defined + } + + + /** + * Return description of a module + * + * @param Translate $langs Lang object to use for output + * @return string Description + */ + public function info($langs) + { + global $conf,$langs; + + // Load translation files required by the page + $langs->loadLangs(array("errors","companies")); + + $form = new Form($this->db); + + $texte = $this->description.".
\n"; + $texte.= '
'; + $texte.= ''; + $texte.= ''; + $texte.= ''; + $texte.= ''; + + // List of directories area + $texte.= ''; + + $texte.= ''; + $texte.= ''; + + $texte.= '
'; + $texttitle=$langs->trans("ListOfDirectories"); + $listofdir=explode(',', preg_replace('/[\r\n]+/', ',', trim($conf->global->SUPPLIER_ORDER_ADDON_PDF_ODT_PATH))); + $listoffiles=array(); + foreach($listofdir as $key=>$tmpdir) + { + $tmpdir=trim($tmpdir); + $tmpdir=preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir); + if (! $tmpdir) { + unset($listofdir[$key]); continue; + } + if (! is_dir($tmpdir)) $texttitle.=img_warning($langs->trans("ErrorDirNotFound", $tmpdir), 0); + else + { + $tmpfiles=dol_dir_list($tmpdir, 'files', 0, '\.(ods|odt)'); + if (count($tmpfiles)) $listoffiles=array_merge($listoffiles, $tmpfiles); + } + } + $texthelp=$langs->trans("ListOfDirectoriesForModelGenODT"); + // Add list of substitution keys + $texthelp.='
'.$langs->trans("FollowingSubstitutionKeysCanBeUsed").'
'; + $texthelp.=$langs->transnoentitiesnoconv("FullListOnOnlineDocumentation"); // This contains an url, we don't modify it + + $texte.= $form->textwithpicto($texttitle, $texthelp, 1, 'help', '', 1); + $texte.= '
'; + $texte.= ''; + $texte.= '
'; + $texte.= ''; + $texte.= '
'; + + // Scan directories + $nbofiles=count($listoffiles); + if (! empty($conf->global->COMMANDE_ADDON_PDF_ODT_PATH)) + { + $texte.=$langs->trans("NumberOfModelFilesFound").': '; + //$texte.=$nbofiles?'':''; + $texte.=count($listoffiles); + //$texte.=$nbofiles?'':''; + $texte.=''; + } + + if ($nbofiles) + { + $texte.='
'; + $texte.= $langs->trans("ExampleOfDirectoriesForModelGen"); + $texte.= '
'; + $texte.= '
'; + + return $texte; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Function to build a document on disk using the generic odt module. + * + * @param Commande $object Object source to build document + * @param Translate $outputlangs Lang output object + * @param string $srctemplatepath Full path of source filename for generator using a template file + * @param int $hidedetails Do not show line details + * @param int $hidedesc Do not show desc + * @param int $hideref Do not show ref + * @return int 1 if OK, <=0 if KO + */ + public function write_file($object, $outputlangs, $srctemplatepath, $hidedetails = 0, $hidedesc = 0, $hideref = 0) + { + // phpcs:enable + global $user,$langs,$conf,$mysoc,$hookmanager; + + if (empty($srctemplatepath)) + { + dol_syslog("doc_generic_odt::write_file parameter srctemplatepath empty", LOG_WARNING); + return -1; + } + + // Add odtgeneration hook + if (! is_object($hookmanager)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager=new HookManager($this->db); + } + $hookmanager->initHooks(array('odtgeneration')); + global $action; + + if (! is_object($outputlangs)) $outputlangs=$langs; + $sav_charset_output=$outputlangs->charset_output; + $outputlangs->charset_output='UTF-8'; + + $outputlangs->loadLangs(array("main", "dict", "companies", "bills")); + + if ($conf->fournisseur->commande->dir_output) + { + $object->fetch_thirdparty(); + + if ($object->specimen) + { + $dir = $conf->fournisseur->commande->dir_output; + $file = $dir . "/SPECIMEN.pdf"; + } + else + { + $objectref = dol_sanitizeFileName($object->ref); + $objectrefsupplier = dol_sanitizeFileName($object->ref_supplier); + $dir = $conf->fournisseur->commande->dir_output . '/'. $objectref; + $file = $dir . "/" . $objectref . ".pdf"; + if (! empty($conf->global->SUPPLIER_REF_IN_NAME)) $file = $dir . "/" . $objectref . ($objectrefsupplier?"_".$objectrefsupplier:"").".pdf"; + } + + if (! file_exists($dir)) + { + if (dol_mkdir($dir) < 0) + { + $this->error=$langs->transnoentities("ErrorCanNotCreateDir", $dir); + return -1; + } + } + + if (file_exists($dir)) + { + //print "srctemplatepath=".$srctemplatepath; // Src filename + $newfile=basename($srctemplatepath); + $newfiletmp=preg_replace('/\.od(t|s)/i', '', $newfile); + $newfiletmp=preg_replace('/template_/i', '', $newfiletmp); + $newfiletmp=preg_replace('/modele_/i', '', $newfiletmp); + $newfiletmp=$objectref.'_'.$newfiletmp; + //$file=$dir.'/'.$newfiletmp.'.'.dol_print_date(dol_now(),'%Y%m%d%H%M%S').'.odt'; + // Get extension (ods or odt) + $newfileformat=substr($newfile, strrpos($newfile, '.')+1); + if ( ! empty($conf->global->MAIN_DOC_USE_TIMING)) + { + $format=$conf->global->MAIN_DOC_USE_TIMING; + if ($format == '1') $format='%Y%m%d%H%M%S'; + $filename=$newfiletmp.'-'.dol_print_date(dol_now(), $format).'.'.$newfileformat; + } + else + { + $filename=$newfiletmp.'.'.$newfileformat; + } + $file=$dir.'/'.$filename; + //print "newdir=".$dir; + //print "newfile=".$newfile; + //print "file=".$file; + //print "conf->societe->dir_temp=".$conf->societe->dir_temp; + + dol_mkdir($conf->commande->dir_temp); + + + // If CUSTOMER contact defined on order, we use it + $usecontact=false; + $arrayidcontact=$object->getIdContact('external', 'CUSTOMER'); + if (count($arrayidcontact) > 0) + { + $usecontact=true; + $result=$object->fetch_contact($arrayidcontact[0]); + } + + // Recipient name + $contactobject=null; + if (! empty($usecontact)) + { + // On peut utiliser le nom de la societe du contact + if (! empty($conf->global->MAIN_USE_COMPANY_NAME_OF_CONTACT)) $socobject = $object->contact; + else { + $socobject = $object->thirdparty; + // if we have a CUSTOMER contact and we dont use it as recipient we store the contact object for later use + $contactobject = $object->contact; + } + } + else + { + $socobject=$object->thirdparty; + } + + // Make substitution + $substitutionarray=array( + '__FROM_NAME__' => $this->issuer->name, + '__FROM_EMAIL__' => $this->issuer->email, + '__TOTAL_TTC__' => $object->total_ttc, + '__TOTAL_HT__' => $object->total_ht, + '__TOTAL_VAT__' => $object->total_vat + ); + complete_substitutions_array($substitutionarray, $langs, $object); + // Call the ODTSubstitution hook + $parameters=array('file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$substitutionarray); + $reshook=$hookmanager->executeHooks('ODTSubstitution', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + + // Line of free text + $newfreetext=''; + $paramfreetext='ORDER_FREE_TEXT'; + if (! empty($conf->global->$paramfreetext)) + { + $newfreetext=make_substitutions($conf->global->$paramfreetext, $substitutionarray); + } + + // Open and load template + require_once ODTPHP_PATH.'odf.php'; + try { + $odfHandler = new odf( + $srctemplatepath, + array( + 'PATH_TO_TMP' => $conf->fournisseur->dir_temp, + 'ZIP_PROXY' => 'PclZipProxy', // PhpZipProxy or PclZipProxy. Got "bad compression method" error when using PhpZipProxy. + 'DELIMITER_LEFT' => '{', + 'DELIMITER_RIGHT' => '}' + ) + ); + } + catch(Exception $e) + { + $this->error=$e->getMessage(); + dol_syslog($e->getMessage(), LOG_INFO); + return -1; + } + // After construction $odfHandler->contentXml contains content and + // [!-- BEGIN row.lines --]*[!-- END row.lines --] has been replaced by + // [!-- BEGIN lines --]*[!-- END lines --] + //print html_entity_decode($odfHandler->__toString()); + //print exit; + + + // Make substitutions into odt of freetext + try { + $odfHandler->setVars('free_text', $newfreetext, true, 'UTF-8'); + } + catch(OdfException $e) + { + dol_syslog($e->getMessage(), LOG_INFO); + } + + // Define substitution array + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + $array_object_from_properties=$this->get_substitutionarray_each_var_object($object, $outputlangs); + $array_objet=$this->get_substitutionarray_object($object, $outputlangs); + $array_user=$this->get_substitutionarray_user($user, $outputlangs); + $array_soc=$this->get_substitutionarray_mysoc($mysoc, $outputlangs); + $array_thirdparty=$this->get_substitutionarray_thirdparty($socobject, $outputlangs); + $array_other=$this->get_substitutionarray_other($outputlangs); + // retrieve contact information for use in object as contact_xxx tags + $array_thirdparty_contact = array(); + if ($usecontact && is_object($contactobject)) $array_thirdparty_contact=$this->get_substitutionarray_contact($contactobject, $outputlangs, 'contact'); + + $tmparray = array_merge($substitutionarray, $array_object_from_properties, $array_user, $array_soc, $array_thirdparty, $array_objet, $array_other, $array_thirdparty_contact); + complete_substitutions_array($tmparray, $outputlangs, $object); + + // Call the ODTSubstitution hook + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); + $reshook=$hookmanager->executeHooks('ODTSubstitution', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + + foreach($tmparray as $key=>$value) + { + try { + if (preg_match('/logo$/', $key)) // Image + { + if (file_exists($value)) $odfHandler->setImage($key, $value); + else $odfHandler->setVars($key, 'ErrorFileNotFound', true, 'UTF-8'); + } + else // Text + { + $odfHandler->setVars($key, $value, true, 'UTF-8'); + } + } + catch(OdfException $e) + { + dol_syslog($e->getMessage(), LOG_INFO); + } + } + // Replace tags of lines + try + { + $foundtagforlines = 1; + try { + $listlines = $odfHandler->setSegment('lines'); + } + catch(OdfException $e) + { + // We may arrive here if tags for lines not present into template + $foundtagforlines = 0; + dol_syslog($e->getMessage(), LOG_INFO); + } + if ($foundtagforlines) + { + foreach ($object->lines as $line) + { + $tmparray=$this->get_substitutionarray_lines($line, $outputlangs); + complete_substitutions_array($tmparray, $outputlangs, $object, $line, "completesubstitutionarray_lines"); + // Call the ODTSubstitutionLine hook + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray,'line'=>$line); + $reshook=$hookmanager->executeHooks('ODTSubstitutionLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + foreach($tmparray as $key => $val) + { + try + { + $listlines->setVars($key, $val, true, 'UTF-8'); + } + catch(OdfException $e) + { + dol_syslog($e->getMessage(), LOG_INFO); + } + catch(SegmentException $e) + { + dol_syslog($e->getMessage(), LOG_INFO); + } + } + $listlines->merge(); + } + $odfHandler->mergeSegment($listlines); + } + } + catch(OdfException $e) + { + $this->error=$e->getMessage(); + dol_syslog($this->error, LOG_WARNING); + return -1; + } + + // Replace labels translated + $tmparray=$outputlangs->get_translations_for_substitutions(); + foreach($tmparray as $key=>$value) + { + try { + $odfHandler->setVars($key, $value, true, 'UTF-8'); + } + catch(OdfException $e) + { + dol_syslog($e->getMessage(), LOG_INFO); + } + } + + // Call the beforeODTSave hook + + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); + $reshook=$hookmanager->executeHooks('beforeODTSave', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + + // Write new file + if (!empty($conf->global->MAIN_ODT_AS_PDF)) { + try { + $odfHandler->exportAsAttachedPDF($file); + }catch (Exception $e){ + $this->error=$e->getMessage(); + dol_syslog($e->getMessage(), LOG_INFO); + return -1; + } + } + else { + try { + $odfHandler->saveToDisk($file); + } catch (Exception $e) { + $this->error=$e->getMessage(); + dol_syslog($e->getMessage(), LOG_INFO); + return -1; + } + } + + $parameters=array('odfHandler'=>&$odfHandler,'file'=>$file,'object'=>$object,'outputlangs'=>$outputlangs,'substitutionarray'=>&$tmparray); + $reshook=$hookmanager->executeHooks('afterODTCreation', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks + + if (! empty($conf->global->MAIN_UMASK)) + @chmod($file, octdec($conf->global->MAIN_UMASK)); + + $odfHandler=null; // Destroy object + + $this->result = array('fullpath'=>$file); + + return 1; // Success + } + else + { + $this->error=$langs->transnoentities("ErrorCanNotCreateDir", $dir); + return -1; + } + } + + return -1; + } +} \ No newline at end of file diff --git a/htdocs/install/doctemplates/supplier_orders/index.html b/htdocs/install/doctemplates/supplier_orders/index.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/htdocs/install/doctemplates/supplier_orders/template_supplier_order.odt b/htdocs/install/doctemplates/supplier_orders/template_supplier_order.odt new file mode 100644 index 0000000000000000000000000000000000000000..b9f5162316d8591210d22fe93963c8fab43593a0 GIT binary patch literal 20174 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 n{PL(=CgqnPuB`JS4 Date: Tue, 27 Aug 2019 16:36:06 +0200 Subject: [PATCH 3/9] Added stickler yml to succeed in pull request --- .stickler.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .stickler.yml diff --git a/.stickler.yml b/.stickler.yml new file mode 100644 index 00000000000..4f7c7d06c27 --- /dev/null +++ b/.stickler.yml @@ -0,0 +1,10 @@ +--- +linters: + phpcs: + standard: 'dev/setup/codesniffer/ruleset.xml' + extensions: 'php' + tab_width: 4 + fixer: true + +fixers: + enable: true \ No newline at end of file From f80e19c1260b8afe21ead1ba20124ca860880817 Mon Sep 17 00:00:00 2001 From: ptibogxiv Date: Tue, 27 Aug 2019 17:46:05 +0200 Subject: [PATCH 4/9] Fix var $amount --- htdocs/societe/paymentmodes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/societe/paymentmodes.php b/htdocs/societe/paymentmodes.php index e20b76b1349..3731cfe07fa 100644 --- a/htdocs/societe/paymentmodes.php +++ b/htdocs/societe/paymentmodes.php @@ -1319,7 +1319,7 @@ if ($socid && $action != 'edit' && $action != 'create' && $action != 'editcard' $arrayzerounitcurrency=array('BIF', 'CLP', 'DJF', 'GNF', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'); if (! in_array($cpt->currency, $arrayzerounitcurrency)) $amount = $cpt->amount / 100; else $amount = $cpt->amount; - print ''.$langs->trans("Available").''.price(amount, 0, '', 1, - 1, - 1, strtoupper($cpt->currency)).' '.$langs->trans("Currency".strtoupper($cpt->currency)).''; + print ''.$langs->trans("Available").''.price($amount, 0, '', 1, - 1, - 1, strtoupper($cpt->currency)).' '.$langs->trans("Currency".strtoupper($cpt->currency)).''; } } From d096d8abba06cee2834b45d62b4f1345a1214907 Mon Sep 17 00:00:00 2001 From: andreubisquerra Date: Tue, 27 Aug 2019 21:57:21 +0200 Subject: [PATCH 5/9] Set free-text item default vat in TakePOS --- htdocs/takepos/invoice.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/htdocs/takepos/invoice.php b/htdocs/takepos/invoice.php index d2e28572331..2e9029a45ab 100644 --- a/htdocs/takepos/invoice.php +++ b/htdocs/takepos/invoice.php @@ -273,7 +273,9 @@ if ($action == "addline") } if ($action == "freezone") { - $invoice->addline($desc, $number, 1, $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS, 0, 0, 0, 0, '', 0, 0, 0, '', 'TTC', $number, 0, -1, 0, '', 0, 0, null, 0, '', 0, 100, '', null, 0); + $customer = new Societe($db); + $customer->fetch($invoice->socid); + $invoice->addline($desc, $number, 1, get_default_tva($mysoc, $customer), 0, 0, 0, 0, '', 0, 0, 0, '', 'TTC', $number, 0, -1, 0, '', 0, 0, null, 0, '', 0, 100, '', null, 0); $invoice->fetch($placeid); } From abb0a681fc96849761542b3051867c4bd5a53660 Mon Sep 17 00:00:00 2001 From: Regis Houssin Date: Fri, 30 Aug 2019 20:20:54 +0200 Subject: [PATCH 6/9] FIX possibility to bypass captcha if it has been validated otherwise --- htdocs/main.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index 1e92838a0d7..9883f95ad31 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -491,7 +491,7 @@ if (! defined('NOLOGIN')) } // Verification security graphic code - if (GETPOST("username", "alpha", 2) && ! empty($conf->global->MAIN_SECURITY_ENABLECAPTCHA)) + if (GETPOST("username", "alpha", 2) && ! empty($conf->global->MAIN_SECURITY_ENABLECAPTCHA) && ! isset($_SESSION['dol_bypass_antispam'])) { $sessionkey = 'dol_antispam_value'; $ok=(array_key_exists($sessionkey, $_SESSION) === true && (strtolower($_SESSION[$sessionkey]) == strtolower($_POST['code']))); From f945ec57718808bb974efe6893e9ac88aec53720 Mon Sep 17 00:00:00 2001 From: Abbes Bahfir Date: Mon, 2 Sep 2019 21:18:57 +0100 Subject: [PATCH 7/9] fix: convert posted price formatted values to numeric --- htdocs/product/fournisseurs.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/product/fournisseurs.php b/htdocs/product/fournisseurs.php index 587d494a51f..cb56ff63cfc 100644 --- a/htdocs/product/fournisseurs.php +++ b/htdocs/product/fournisseurs.php @@ -196,7 +196,7 @@ if (empty($reshook)) $langs->load("errors"); setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Supplier")), null, 'errors'); } - if ($_POST["price"] < 0 || $_POST["price"] == '') + if (price2num($_POST["price"]) < 0 || $_POST["price"] == '') { if ($price_expression === '') // Return error of missing price only if price_expression not set { @@ -215,12 +215,12 @@ if (empty($reshook)) $langs->load("errors"); setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Currency")), null, 'errors'); } - if ($_POST["multicurrency_tx"] <= 0 || $_POST["multicurrency_tx"] == '') { + if (price2num($_POST["multicurrency_tx"]) <= 0 || $_POST["multicurrency_tx"] == '') { $error++; $langs->load("errors"); setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("CurrencyRate")), null, 'errors'); } - if ($_POST["multicurrency_price"] < 0 || $_POST["multicurrency_price"] == '') { + if (price2num($_POST["multicurrency_price"]) < 0 || $_POST["multicurrency_price"] == '') { $error++; $langs->load("errors"); setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("PriceCurrency")), null, 'errors'); From 443f16bddfc60a3aea825389fc17f167e7eddac5 Mon Sep 17 00:00:00 2001 From: Tim Otte Date: Tue, 3 Sep 2019 10:36:00 +0200 Subject: [PATCH 8/9] Removed debug output --- htdocs/core/modules/modFournisseur.class.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/htdocs/core/modules/modFournisseur.class.php b/htdocs/core/modules/modFournisseur.class.php index fb108fb83ba..0553fe3a1fa 100644 --- a/htdocs/core/modules/modFournisseur.class.php +++ b/htdocs/core/modules/modFournisseur.class.php @@ -641,8 +641,6 @@ class modFournisseur extends DolibarrModules $dirodt=DOL_DATA_ROOT.'/doctemplates/supplier_orders'; $dest=$dirodt.'/template_supplier_order.odt'; - file_put_contents("test.txt", var_export($dest, true)); - if (file_exists($src) && ! file_exists($dest)) { require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; From 0f02a621ba0860d495abe3e937da3fef32ec596c Mon Sep 17 00:00:00 2001 From: Tim Otte Date: Tue, 3 Sep 2019 10:39:11 +0200 Subject: [PATCH 9/9] Seriously? One newline failed the stickler ci test? --- .../pdf/doc_generic_supplier_order_odt.modules.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/modules/supplier_order/pdf/doc_generic_supplier_order_odt.modules.php b/htdocs/core/modules/supplier_order/pdf/doc_generic_supplier_order_odt.modules.php index 08711154819..d9e9112ec54 100644 --- a/htdocs/core/modules/supplier_order/pdf/doc_generic_supplier_order_odt.modules.php +++ b/htdocs/core/modules/supplier_order/pdf/doc_generic_supplier_order_odt.modules.php @@ -514,4 +514,4 @@ class doc_generic_supplier_order_odt extends ModelePDFSuppliersOrders return -1; } -} \ No newline at end of file +}