From 7c2b5b614f63611e8cfc28cbdfb7977f3054ef2e Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Sun, 25 Oct 2020 19:13:09 +0100 Subject: [PATCH 001/743] NEW: preload product description on selection for customer propal/order/invoice --- htdocs/comm/propal/card.php | 7 +++- htdocs/commande/card.php | 5 +++ htdocs/compta/facture/card.php | 5 +++ htdocs/contrat/card.php | 6 ++++ htdocs/core/tpl/objectline_create.tpl.php | 40 ++++++++++++++++------- htdocs/fourn/facture/card.php | 5 +++ htdocs/product/ajax/products.php | 35 ++++++++++++++++++-- 7 files changed, 87 insertions(+), 16 deletions(-) diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php index 4a4cf3c37dd..3a9fc38fbaf 100644 --- a/htdocs/comm/propal/card.php +++ b/htdocs/comm/propal/card.php @@ -995,11 +995,16 @@ if (empty($reshook)) $outputlangs->setDefaultLang($newlang); } - $desc = (!empty($prod->multilangs [$outputlangs->defaultlang] ["description"])) ? $prod->multilangs [$outputlangs->defaultlang] ["description"] : $prod->description; + $desc = (!empty($prod->multilangs[$outputlangs->defaultlang]["description"])) ? $prod->multilangs[$outputlangs->defaultlang]["description"] : $prod->description; } else { $desc = $prod->description; } + //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time + if ($product_desc==$desc) { + $product_desc=''; + } + if (!empty($product_desc) && !empty($conf->global->MAIN_NO_CONCAT_DESCRIPTION)) $desc = $product_desc; else $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php index 596fe4e94bc..585b85def0b 100644 --- a/htdocs/commande/card.php +++ b/htdocs/commande/card.php @@ -854,6 +854,11 @@ if (empty($reshook)) $desc = $prod->description; } + //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time + if ($product_desc==$desc) { + $product_desc=''; + } + if (!empty($product_desc) && !empty($conf->global->MAIN_NO_CONCAT_DESCRIPTION)) $desc = $product_desc; else $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 94f817114aa..6b56d39ba3f 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -2068,6 +2068,11 @@ if (empty($reshook)) $desc = $prod->description; } + //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time + if ($product_desc==$desc) { + $product_desc=''; + } + if (!empty($product_desc) && !empty($conf->global->MAIN_NO_CONCAT_DESCRIPTION)) $desc = $product_desc; else $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php index 64b3275379a..f3dc0355331 100644 --- a/htdocs/contrat/card.php +++ b/htdocs/contrat/card.php @@ -499,6 +499,12 @@ if (empty($reshook)) } $desc = $prod->description; + + //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time + if ($product_desc==$desc) { + $product_desc=''; + } + if (!empty($product_desc) && !empty($conf->global->MAIN_NO_CONCAT_DESCRIPTION)) $desc = $product_desc; else $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); diff --git a/htdocs/core/tpl/objectline_create.tpl.php b/htdocs/core/tpl/objectline_create.tpl.php index d177ce7637c..ec73bf9725b 100644 --- a/htdocs/core/tpl/objectline_create.tpl.php +++ b/htdocs/core/tpl/objectline_create.tpl.php @@ -514,13 +514,13 @@ if (!empty($usemargins) && $user->rights->margins->creer) global->DISPLAY_MARGIN_RATES)) { ?> $("input[name='np_marginRate']:first").blur(function(e) { - return checkFreeLine(e, "np_marginRate"); + return checkFreeLine(e, "np_marginRate"); }); global->DISPLAY_MARK_RATES)) { ?> $("input[name='np_markRate']:first").blur(function(e) { - return checkFreeLine(e, "np_markRate"); + return checkFreeLine(e, "np_markRate"); }); rights->margins->creer) var rate = $("input[name='"+npRate+"']:first"); if (rate.val() == '') - return true; + return true; if (! $.isNumeric(rate.val().replace(',','.'))) { - alert('trans("rateMustBeNumeric")); ?>'); - e.stopPropagation(); - setTimeout(function () { rate.focus() }, 50); - return false; + alert('trans("rateMustBeNumeric")); ?>'); + e.stopPropagation(); + setTimeout(function () { rate.focus() }, 50); + return false; } if (npRate == "np_markRate" && rate.val() >= 100) { - alert('trans("markRateShouldBeLesserThan100")); ?>'); - e.stopPropagation(); - setTimeout(function () { rate.focus() }, 50); - return false; + alert('trans("markRateShouldBeLesserThan100")); ?>'); + e.stopPropagation(); + setTimeout(function () { rate.focus() }, 50); + return false; } var price = 0; @@ -659,7 +659,6 @@ if (!empty($usemargins) && $user->rights->margins->creer) ?> var pbq = parseInt($('option:selected', this).attr('data-pbq')); /* If product was selected with a HTML select */ if (isNaN(pbq)) { pbq = jQuery('#idprod').attr('data-pbq'); } /* If product was selected with a HTML input with autocomplete */ - //console.log(pbq); if ((jQuery('#idprod').val() > 0 || jQuery('#idprodfournprice').val()) && ! isNaN(pbq) && pbq > 0) { @@ -675,6 +674,23 @@ if (!empty($usemargins) && $user->rights->margins->creer) function(data) { console.log("Load unit price end, we got value "+data.price_ht); jQuery("#price_ht").val(data.price_ht); + global->MAIN_MULTILANGS) && !empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { ?> + var proddesc = data.desc_trans; + + var proddesc = data.desc; + + console.log("Load desciption into text area : "+proddesc); + global->FCKEDITOR_ENABLE_DETAILS)) { ?> + if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") + { + var editor = CKEDITOR.instances['dp_desc']; + if (editor) { + editor.setData(proddesc); + } + } + + jQuery('#dp_desc').text(proddesc); + }, 'json' ); diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index 542a4a09274..45fb10b525a 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -1294,6 +1294,11 @@ if (empty($reshook)) $desc = $productsupplier->desc_supplier; } else $desc = $productsupplier->description; + //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time + if ($product_desc==$desc) { + $product_desc=''; + } + if (trim($product_desc) != trim($desc)) $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); $type = $productsupplier->type; diff --git a/htdocs/product/ajax/products.php b/htdocs/product/ajax/products.php index 7441d88abc0..273eef35300 100644 --- a/htdocs/product/ajax/products.php +++ b/htdocs/product/ajax/products.php @@ -71,7 +71,9 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) { $outref = $object->ref; $outlabel = $object->label; + $outlabel_trans =''; $outdesc = $object->description; + $outdesc_trans =''; $outtype = $object->type; $outqty = 1; $outdiscount = 0; @@ -79,10 +81,25 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) $found = false; $price_level = 1; - if ($socid > 0 && !empty($conf->global->PRODUIT_MULTIPRICES)) { + + if ($socid > 0) { $thirdpartytemp = new Societe($db); $thirdpartytemp->fetch($socid); - $price_level = $thirdpartytemp->price_level; + //Load translation description and label + if (!empty($conf->global->MAIN_MULTILANGS) && !empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { + $newlang = $thirdpartytemp->default_lang; + + if (!empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + $outdesc_trans = (!empty($object->multilangs[$outputlangs->defaultlang]["description"])) ? $object->multilangs[$outputlangs->defaultlang]["description"] : $object->description; + $outlabel_trans = (!empty($object->multilangs[$outputlangs->defaultlang]["label"])) ? $object->multilangs[$outputlangs->defaultlang]["label"] : $object->label; + } + } + + if (!empty($conf->global->PRODUIT_MULTIPRICES)) { + $price_level = $thirdpartytemp->price_level; + } } // Price by qty @@ -158,7 +175,19 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) $outtva_tx = $object->tva_tx; } - $outjson = array('ref' => $outref, 'label' => $outlabel, 'desc' => $outdesc, 'type' => $outtype, 'price_ht' => $outprice_ht, 'price_ttc' => $outprice_ttc, 'pricebasetype' => $outpricebasetype, 'tva_tx' => $outtva_tx, 'qty' => $outqty, 'discount' => $outdiscount); + $outjson = array( + 'ref' => $outref, + 'label' => $outlabel, + 'label_trans' => $outlabel_trans, + 'desc' => $outdesc, + 'desc_trans' => $outdesc_trans, + 'type' => $outtype, + 'price_ht' => $outprice_ht, + 'price_ttc' => $outprice_ttc, + 'pricebasetype' => $outpricebasetype, + 'tva_tx' => $outtva_tx, + 'qty' => $outqty, + 'discount' => $outdiscount); } echo json_encode($outjson); From 925dfd36b5d4559a9075c218d40250fde8ff39ea Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Sun, 25 Oct 2020 19:15:10 +0100 Subject: [PATCH 002/743] syntax --- htdocs/fourn/facture/card.php | 5 ----- htdocs/product/ajax/products.php | 9 +++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index 45fb10b525a..542a4a09274 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -1294,11 +1294,6 @@ if (empty($reshook)) $desc = $productsupplier->desc_supplier; } else $desc = $productsupplier->description; - //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time - if ($product_desc==$desc) { - $product_desc=''; - } - if (trim($product_desc) != trim($desc)) $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); $type = $productsupplier->type; diff --git a/htdocs/product/ajax/products.php b/htdocs/product/ajax/products.php index 273eef35300..c2b698c8e04 100644 --- a/htdocs/product/ajax/products.php +++ b/htdocs/product/ajax/products.php @@ -85,6 +85,11 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) if ($socid > 0) { $thirdpartytemp = new Societe($db); $thirdpartytemp->fetch($socid); + + if (!empty($conf->global->PRODUIT_MULTIPRICES)) { + $price_level = $thirdpartytemp->price_level; + } + //Load translation description and label if (!empty($conf->global->MAIN_MULTILANGS) && !empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { $newlang = $thirdpartytemp->default_lang; @@ -96,10 +101,6 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) $outlabel_trans = (!empty($object->multilangs[$outputlangs->defaultlang]["label"])) ? $object->multilangs[$outputlangs->defaultlang]["label"] : $object->label; } } - - if (!empty($conf->global->PRODUIT_MULTIPRICES)) { - $price_level = $thirdpartytemp->price_level; - } } // Price by qty From f4e63bb0462fde1cc339f1ebd81d61ba6af211b9 Mon Sep 17 00:00:00 2001 From: Marc de Lima Lucio <68746600+marc-dll@users.noreply.github.com> Date: Mon, 26 Oct 2020 10:52:22 +0100 Subject: [PATCH 003/743] NEW: ref in product customer price: add fields in database --- htdocs/install/mysql/migration/12.0.0-13.0.0.sql | 3 +++ htdocs/install/mysql/tables/llx_product_customer_price.sql | 3 ++- htdocs/install/mysql/tables/llx_product_customer_price_log.sql | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) 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 a73ca40d86f..0a3b9379cbf 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 @@ -423,6 +423,9 @@ ALTER TABLE llx_product MODIFY COLUMN finished tinyint DEFAULT NULL; ALTER TABLE llx_product ADD CONSTRAINT fk_product_finished FOREIGN KEY (finished) REFERENCES llx_c_product_nature (code); +ALTER TABLE llx_product_customer_price ADD COLUMN ref_customer varchar(30); +ALTER TABLE llx_product_customer_price_log ADD COLUMN ref_customer varchar(30); + -- MIGRATION TO DO AFTER RENAMING AN OBJECT -- drop constraint diff --git a/htdocs/install/mysql/tables/llx_product_customer_price.sql b/htdocs/install/mysql/tables/llx_product_customer_price.sql index 7cd481c3c75..f0d5edf9c2f 100644 --- a/htdocs/install/mysql/tables/llx_product_customer_price.sql +++ b/htdocs/install/mysql/tables/llx_product_customer_price.sql @@ -27,7 +27,8 @@ create table llx_product_customer_price datec datetime, tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, fk_product integer NOT NULL, - fk_soc integer NOT NULL, + fk_soc integer NOT NULL, + ref_customer varchar(30), price double(24,8) DEFAULT 0, price_ttc double(24,8) DEFAULT 0, price_min double(24,8) DEFAULT 0, diff --git a/htdocs/install/mysql/tables/llx_product_customer_price_log.sql b/htdocs/install/mysql/tables/llx_product_customer_price_log.sql index 4d79353e884..29906fe7ff2 100644 --- a/htdocs/install/mysql/tables/llx_product_customer_price_log.sql +++ b/htdocs/install/mysql/tables/llx_product_customer_price_log.sql @@ -26,7 +26,8 @@ create table llx_product_customer_price_log entity integer DEFAULT 1 NOT NULL, -- multi company id datec datetime, fk_product integer NOT NULL, - fk_soc integer DEFAULT 0 NOT NULL, + fk_soc integer DEFAULT 0 NOT NULL, + ref_customer varchar(30), price double(24,8) DEFAULT 0, price_ttc double(24,8) DEFAULT 0, price_min double(24,8) DEFAULT 0, From 721e931ba7119817694b77f36bd3388430f239c2 Mon Sep 17 00:00:00 2001 From: Marc de Lima Lucio <68746600+marc-dll@users.noreply.github.com> Date: Mon, 26 Oct 2020 11:04:06 +0100 Subject: [PATCH 004/743] NEW: ref in product customer price: add field in CRUD class --- .../class/productcustomerprice.class.php | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/htdocs/product/class/productcustomerprice.class.php b/htdocs/product/class/productcustomerprice.class.php index 25a3c5cd836..49386e8a379 100644 --- a/htdocs/product/class/productcustomerprice.class.php +++ b/htdocs/product/class/productcustomerprice.class.php @@ -54,7 +54,12 @@ class Productcustomerprice extends CommonObject /** * @var int Thirdparty ID */ - public $fk_soc; + public $fk_soc; + + /** + * @var string Customer reference + */ + public $ref_customer; public $price; public $price_ttc; @@ -107,7 +112,9 @@ class Productcustomerprice extends CommonObject if (isset($this->fk_product)) $this->fk_product = trim($this->fk_product); if (isset($this->fk_soc)) - $this->fk_soc = trim($this->fk_soc); + $this->fk_soc = trim($this->fk_soc); + if (isset($this->ref_customer)) + $this->ref_customer = trim($this->ref_customer); if (isset($this->price)) $this->price = trim($this->price); if (isset($this->price_ttc)) @@ -171,6 +178,7 @@ class Productcustomerprice extends CommonObject $sql .= "datec,"; $sql .= "fk_product,"; $sql .= "fk_soc,"; + $sql .= 'ref_customer,'; $sql .= "price,"; $sql .= "price_ttc,"; $sql .= "price_min,"; @@ -190,6 +198,7 @@ class Productcustomerprice extends CommonObject $sql .= " '".$this->db->idate(dol_now())."',"; $sql .= " ".(!isset($this->fk_product) ? 'NULL' : "'".$this->db->escape($this->fk_product)."'").","; $sql .= " ".(!isset($this->fk_soc) ? 'NULL' : "'".$this->db->escape($this->fk_soc)."'").","; + $sql .= " ".(!isset($this->ref_customer) ? 'NULL' : "'".$this->db->escape($this->ref_customer)."'").","; $sql .= " ".(empty($this->price) ? '0' : "'".$this->db->escape($this->price)."'").","; $sql .= " ".(empty($this->price_ttc) ? '0' : "'".$this->db->escape($this->price_ttc)."'").","; $sql .= " ".(empty($this->price_min) ? '0' : "'".$this->db->escape($this->price_min)."'").","; @@ -257,6 +266,7 @@ class Productcustomerprice extends CommonObject $sql .= " t.tms,"; $sql .= " t.fk_product,"; $sql .= " t.fk_soc,"; + $sql .= " t.ref_customer,"; $sql .= " t.price,"; $sql .= " t.price_ttc,"; $sql .= " t.price_min,"; @@ -286,6 +296,7 @@ class Productcustomerprice extends CommonObject $this->tms = $this->db->jdate($obj->tms); $this->fk_product = $obj->fk_product; $this->fk_soc = $obj->fk_soc; + $this->ref_customer = $obj->ref_customer; $this->price = $obj->price; $this->price_ttc = $obj->price_ttc; $this->price_min = $obj->price_min; @@ -334,6 +345,7 @@ class Productcustomerprice extends CommonObject $sql .= " t.tms,"; $sql .= " t.fk_product,"; $sql .= " t.fk_soc,"; + $sql .= " t.ref_customer,"; $sql .= " t.price,"; $sql .= " t.price_ttc,"; $sql .= " t.price_min,"; @@ -393,6 +405,7 @@ class Productcustomerprice extends CommonObject $line->tms = $this->db->jdate($obj->tms); $line->fk_product = $obj->fk_product; $line->fk_soc = $obj->fk_soc; + $line->ref_customer = $obj->ref_customer; $line->price = $obj->price; $line->price_ttc = $obj->price_ttc; $line->price_min = $obj->price_min; @@ -447,6 +460,7 @@ class Productcustomerprice extends CommonObject $sql .= " t.datec,"; $sql .= " t.fk_product,"; $sql .= " t.fk_soc,"; + $sql .= " t.ref_customer,"; $sql .= " t.price,"; $sql .= " t.price_ttc,"; $sql .= " t.price_min,"; @@ -502,6 +516,7 @@ class Productcustomerprice extends CommonObject $line->tms = $this->db->jdate($obj->tms); $line->fk_product = $obj->fk_product; $line->fk_soc = $obj->fk_soc; + $line->ref_customer = $obj->ref_customer; $line->price = $obj->price; $line->price_ttc = $obj->price_ttc; $line->price_min = $obj->price_min; @@ -549,7 +564,9 @@ class Productcustomerprice extends CommonObject if (isset($this->fk_product)) $this->fk_product = trim($this->fk_product); if (isset($this->fk_soc)) - $this->fk_soc = trim($this->fk_soc); + $this->fk_soc = trim($this->fk_soc); + if (isset($this->ref_customer)) + $this->ref_customer = trim($this->ref_customer); if (isset($this->price)) $this->price = trim($this->price); if (isset($this->price_ttc)) @@ -615,6 +632,7 @@ class Productcustomerprice extends CommonObject $sql .= "datec,"; $sql .= "fk_product,"; $sql .= "fk_soc,"; + $sql .= "ref_customer,"; $sql .= "price,"; $sql .= "price_ttc,"; $sql .= "price_min,"; @@ -637,6 +655,7 @@ class Productcustomerprice extends CommonObject $sql .= " t.datec,"; $sql .= " t.fk_product,"; $sql .= " t.fk_soc,"; + $sql .= " t.ref_customer,"; $sql .= " t.price,"; $sql .= " t.price_ttc,"; $sql .= " t.price_min,"; @@ -671,6 +690,7 @@ class Productcustomerprice extends CommonObject $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').","; $sql .= " fk_product=".(isset($this->fk_product) ? $this->fk_product : "null").","; $sql .= " fk_soc=".(isset($this->fk_soc) ? $this->fk_soc : "null").","; + $sql .= " ref_customer=".(isset($this->ref_customer) ? $this->ref_customer : "null").","; $sql .= " price=".(isset($this->price) ? $this->price : "null").","; $sql .= " price_ttc=".(isset($this->price_ttc) ? $this->price_ttc : "null").","; $sql .= " price_min=".(isset($this->price_min) ? $this->price_min : "null").","; @@ -790,6 +810,7 @@ class Productcustomerprice extends CommonObject // If line do not exits then create it $prodsocpricenew = new Productcustomerprice($this->db); $prodsocpricenew->fk_soc = $obj->rowid; + $prodsocpricenew->ref_customer = $obj->ref_customer; $prodsocpricenew->fk_product = $this->fk_product; $prodsocpricenew->price = $this->price; $prodsocpricenew->price_min = $this->price_min; @@ -924,6 +945,7 @@ class Productcustomerprice extends CommonObject $this->tms = ''; $this->fk_product = ''; $this->fk_soc = ''; + $this->ref_customer = ''; $this->price = ''; $this->price_ttc = ''; $this->price_min = ''; @@ -962,6 +984,11 @@ class PriceByCustomerLine */ public $fk_product; + /** + * @var string Customer reference + */ + public $ref_customer; + /** * @var int Thirdparty ID */ From 6a3150028c74851ce256bb8fc8546cac1e2530e9 Mon Sep 17 00:00:00 2001 From: Marc de Lima Lucio <68746600+marc-dll@users.noreply.github.com> Date: Mon, 26 Oct 2020 11:47:01 +0100 Subject: [PATCH 005/743] NEW: ref in product customer price: fix price update SQL --- htdocs/product/class/productcustomerprice.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/product/class/productcustomerprice.class.php b/htdocs/product/class/productcustomerprice.class.php index 49386e8a379..c18d09614e6 100644 --- a/htdocs/product/class/productcustomerprice.class.php +++ b/htdocs/product/class/productcustomerprice.class.php @@ -690,7 +690,7 @@ class Productcustomerprice extends CommonObject $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').","; $sql .= " fk_product=".(isset($this->fk_product) ? $this->fk_product : "null").","; $sql .= " fk_soc=".(isset($this->fk_soc) ? $this->fk_soc : "null").","; - $sql .= " ref_customer=".(isset($this->ref_customer) ? $this->ref_customer : "null").","; + $sql .= " ref_customer=".(isset($this->ref_customer) ? "'" . $this->db->escape($this->ref_customer) . "'" : "null").","; $sql .= " price=".(isset($this->price) ? $this->price : "null").","; $sql .= " price_ttc=".(isset($this->price_ttc) ? $this->price_ttc : "null").","; $sql .= " price_min=".(isset($this->price_min) ? $this->price_min : "null").","; From c2518e92fbb26ce077cb33500965b6cc77528ea8 Mon Sep 17 00:00:00 2001 From: Marc de Lima Lucio <68746600+marc-dll@users.noreply.github.com> Date: Mon, 26 Oct 2020 11:50:04 +0100 Subject: [PATCH 006/743] NEW: ref in product customer price: add field in views --- htdocs/product/price.php | 9 ++++++--- htdocs/societe/price.php | 8 ++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/htdocs/product/price.php b/htdocs/product/price.php index 8d10535ae31..2444fe6468d 100644 --- a/htdocs/product/price.php +++ b/htdocs/product/price.php @@ -1854,6 +1854,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) print ''; print ''.$langs->trans("ThirdParty").''; + print ''.$langs->trans('RefCustomer').''; print ''.$langs->trans("AppliedPricesFrom").''; print ''.$langs->trans("PriceBase").''; print ''.$langs->trans("DefaultTaxRate").''; @@ -1901,6 +1902,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) print ''; print "".$staticsoc->getNomUrl(1).""; + print '' . $line->ref_customer . ''; print "".dol_print_date($line->datec, "dayhour").""; print ''.$langs->trans($line->price_base_type).""; print ''; @@ -1969,7 +1971,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) if (count($prodcustprice->lines) > 0 || $search_soc) { - $colspan = 8; + $colspan = 9; //if ($mysoc->localtax1_assuj == "1" || $mysoc->localtax2_assuj == "1") $colspan++; print ''; @@ -1985,6 +1987,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) print ''; print ''.$langs->trans("ThirdParty").''; + print '' . $langs->trans('RefCustomer') . ''; print ''.$langs->trans("AppliedPricesFrom").''; print ''.$langs->trans("PriceBase").''; print ''.$langs->trans("DefaultTaxRate").''; @@ -2023,8 +2026,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) $total_ttc = $resultarray[2]; print ''; - print "".$langs->trans("Default").""; - print ""; + print '' . $langs->trans('Default') . ''; print ''.$langs->trans($object->price_base_type).""; print ''; @@ -2102,6 +2104,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) print ''; print "".$staticsoc->getNomUrl(1).""; + print '' . $line->ref_customer . ''; print "".dol_print_date($line->datec, "dayhour").""; print ''.$langs->trans($line->price_base_type).""; diff --git a/htdocs/societe/price.php b/htdocs/societe/price.php index 8a65b56b2bf..2275ab251f7 100644 --- a/htdocs/societe/price.php +++ b/htdocs/societe/price.php @@ -461,6 +461,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print ''; print ''.$langs->trans("Product").''; + print '' . $langs->trans('RefCustomer') . ''; print ''.$langs->trans("AppliedPricesFrom").''; print ''.$langs->trans("PriceBase").''; print ''.$langs->trans("VAT").''; @@ -478,6 +479,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { $staticprod->fetch($line->fk_product); print "".$staticprod->getNomUrl(1).""; + print '' . $line->ref_customer . ''; print "".dol_print_date($line->datec, "dayhour").""; print ''.$langs->trans($line->price_base_type).""; @@ -547,6 +549,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print ''; print '' . $langs->trans("Ref") . ''; print '' . $langs->trans("Product") . ''; + print '' . $langs->trans('RefCustomer') . ''; print ''.$langs->trans("AppliedPricesFrom").''; print ''.$langs->trans("PriceBase").''; print ''.$langs->trans("VAT").''; @@ -563,7 +566,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print ''; print ''; print ''; - print ' '; + print ' '; print ''; print ''; print ' '; @@ -586,6 +589,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print "".$staticprod->getNomUrl(1).""; print "" . $staticprod->label .""; + print '' . $line->ref_customer .''; print "".dol_print_date($line->datec, "dayhour").""; print ''.$langs->trans($line->price_base_type).""; @@ -623,7 +627,7 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print "\n"; } } else { - $colspan = 9; + $colspan = 10; if ($user->rights->produit->supprimer || $user->rights->service->supprimer) $colspan += 1; print ''.$langs->trans('None').''; } From 496c31d63ab611e5b67278643823326d0b0f5258 Mon Sep 17 00:00:00 2001 From: Marc de Lima Lucio <68746600+marc-dll@users.noreply.github.com> Date: Mon, 26 Oct 2020 12:18:24 +0100 Subject: [PATCH 007/743] NEW: ref in product customer price: add field in forms and form handlers --- htdocs/product/price.php | 10 ++++++++++ htdocs/societe/price.php | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/htdocs/product/price.php b/htdocs/product/price.php index 2444fe6468d..a150402cfde 100644 --- a/htdocs/product/price.php +++ b/htdocs/product/price.php @@ -499,6 +499,7 @@ if (empty($reshook)) // add price by customer $prodcustprice->fk_soc = GETPOST('socid', 'int'); + $prodcustprice->ref_customer = GETPOST('ref_customer', 'alpha'); $prodcustprice->fk_product = $object->id; $prodcustprice->price = price2num(GETPOST("price"), 'MU'); $prodcustprice->price_min = price2num(GETPOST("price_min"), 'MU'); @@ -600,6 +601,7 @@ if (empty($reshook)) $prodcustprice->fetch(GETPOST('lineid', 'int')); // update price by customer + $prodcustprice->ref_customer = GETPOST('ref_customer', 'alpha'); $prodcustprice->price = price2num(GETPOST("price"), 'MU'); $prodcustprice->price_min = price2num(GETPOST("price_min"), 'MU'); $prodcustprice->price_base_type = GETPOST("price_base_type", 'alpha'); @@ -1662,6 +1664,10 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) print ''; print ''; + // Ref. Customer + print '' . $langs->trans('RefCustomer') . ''; + print ''; + // VAT print ''.$langs->trans("DefaultTaxRate").''; print $form->load_tva("tva_tx", $object->default_vat_code ? $object->tva_tx.' ('.$object->default_vat_code.')' : $object->tva_tx, $mysoc, '', $object->id, $object->tva_npr, $object->type, false, 1); @@ -1748,6 +1754,10 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) print "".$staticsoc->getNomUrl(1).""; print ''; + // Ref. Customer + print '' . $langs->trans('RefCustomer') . ''; + print ''; + // VAT print ''.$langs->trans("DefaultTaxRate").''; print $form->load_tva("tva_tx", $prodcustprice->default_vat_code ? $prodcustprice->tva_tx.' ('.$prodcustprice->default_vat_code.')' : $prodcustprice->tva_tx, $mysoc, '', $object->id, $prodcustprice->recuperableonly, $object->type, false, 1); diff --git a/htdocs/societe/price.php b/htdocs/societe/price.php index 2275ab251f7..6d1d08b17b5 100644 --- a/htdocs/societe/price.php +++ b/htdocs/societe/price.php @@ -88,6 +88,7 @@ if (empty($reshook)) // add price by customer $prodcustprice->fk_soc = $socid; + $prodcustprice->ref_customer = GETPOST('ref_customer', 'alpha'); $prodcustprice->fk_product = GETPOST('prodid', 'int'); $prodcustprice->price = price2num(GETPOST("price"), 'MU'); $prodcustprice->price_min = price2num(GETPOST("price_min"), 'MU'); @@ -162,6 +163,7 @@ if (empty($reshook)) $update_child_soc = GETPOST('updatechildprice'); // update price by customer + $prodcustprice->ref_customer = GETPOST('ref_customer', 'alpha'); $prodcustprice->price = price2num(GETPOST("price"), 'MU'); $prodcustprice->price_min = price2num(GETPOST("price_min"), 'MU'); $prodcustprice->price_base_type = GETPOST("price_base_type", 'alpha'); @@ -294,6 +296,10 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print ''; print ''; + // Ref. Customer + print '' . $langs->trans('RefCustomer') . ''; + print ''; + // VAT print ''.$langs->trans("VATRate").''; print $form->load_tva("tva_tx", $object->tva_tx, $mysoc, '', $object->id, $object->tva_npr, '', false, 1); @@ -372,6 +378,10 @@ if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { print "".$staticprod->getNomUrl(1).""; print ''; + // Ref. Customer + print '' . $langs->trans('RefCustomer') . ''; + print ''; + // VAT print ''.$langs->trans("VATRate").''; print $form->load_tva("tva_tx", $prodcustprice->tva_tx, $mysoc, '', $staticprod->id, $prodcustprice->recuperableonly); From bb850ad0d083e8cc76eeb997acd29dc8779cab31 Mon Sep 17 00:00:00 2001 From: Marc de Lima Lucio <68746600+marc-dll@users.noreply.github.com> Date: Mon, 26 Oct 2020 13:05:07 +0100 Subject: [PATCH 008/743] NEW: ref in product customer price: add ref in PDFs + hidden conf to choose refs to show --- htdocs/core/lib/pdf.lib.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php index 0ff40659daa..9889e3ff220 100644 --- a/htdocs/core/lib/pdf.lib.php +++ b/htdocs/core/lib/pdf.lib.php @@ -1237,6 +1237,10 @@ function pdf_getlinedesc($object, $i, $outputlangs, $hideref = 0, $hidedesc = 0, } else { include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; $prodser = new Product($db); + + if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { + include_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php'; + } } if ($idprod) @@ -1364,6 +1368,32 @@ function pdf_getlinedesc($object, $i, $outputlangs, $hideref = 0, $hidedesc = 0, } } else { $ref_prodserv = $prodser->ref; // Show local ref only + + if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { + $productCustomerPriceStatic = new Productcustomerprice($db); + $filter = array('fk_product' => $idprod, 'fk_soc' => $object->socid); + + $nbCustomerPrices = $productCustomerPriceStatic->fetch_all('', '', 1, 0, $filter); + + if ($nbCustomerPrices > 0) { + $productCustomerPrice = $productCustomerPriceStatic->lines[0]; + + if (! empty($productCustomerPrice->ref_customer)) { + switch($conf->global->PRODUIT_CUSTOMER_PRICES_PDF_REF_MODE) { + case 1: + $ref_prodserv = $productCustomerPrice->ref_customer; + break; + + case 2: + $ref_prodserv = $productCustomerPrice->ref_customer . ' (' . $outputlangs->transnoentitiesnoconv('InternalRef') . ' ' . $ref_prodserv . ')'; + break; + + default: + $ref_prodserv = $ref_prodserv . ' (' . $outputlangs->transnoentitiesnoconv('RefCustomer') . ' ' . $productCustomerPrice->ref_customer . ')'; + } + } + } + } } if (!empty($libelleproduitservice) && !empty($ref_prodserv)) $ref_prodserv .= " - "; From 79ba133a07d5f691fac0ef00f94af5559ac233aa Mon Sep 17 00:00:00 2001 From: Marc de Lima Lucio <68746600+marc-dll@users.noreply.github.com> Date: Mon, 26 Oct 2020 13:05:26 +0100 Subject: [PATCH 009/743] NEW: ref in product customer price: fix missing language key --- htdocs/langs/en_US/main.lang | 1 + 1 file changed, 1 insertion(+) diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index 1096b9857f1..84d399ddde2 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -646,6 +646,7 @@ SupplierPreview=Vendor preview ShowCustomerPreview=Show customer preview ShowSupplierPreview=Show vendor preview RefCustomer=Ref. customer +InternalRef=Internal ref. Currency=Currency InfoAdmin=Information for administrators Undo=Undo From 541dab57371e5b8517bc77f527262bc777b178ea Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Mon, 26 Oct 2020 13:40:05 +0000 Subject: [PATCH 010/743] Fixing style errors. --- htdocs/core/lib/pdf.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php index 9889e3ff220..6d0b3023964 100644 --- a/htdocs/core/lib/pdf.lib.php +++ b/htdocs/core/lib/pdf.lib.php @@ -1379,7 +1379,7 @@ function pdf_getlinedesc($object, $i, $outputlangs, $hideref = 0, $hidedesc = 0, $productCustomerPrice = $productCustomerPriceStatic->lines[0]; if (! empty($productCustomerPrice->ref_customer)) { - switch($conf->global->PRODUIT_CUSTOMER_PRICES_PDF_REF_MODE) { + switch ($conf->global->PRODUIT_CUSTOMER_PRICES_PDF_REF_MODE) { case 1: $ref_prodserv = $productCustomerPrice->ref_customer; break; From 76824c868bdadc3268cfab95a4b49820bfaa224d Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Mon, 26 Oct 2020 16:31:07 +0100 Subject: [PATCH 011/743] clean get price for fourn and labal for supplier coducment --- htdocs/core/class/html.form.class.php | 30 +++++++- htdocs/core/lib/ajax.lib.php | 40 ++++++++--- htdocs/core/tpl/objectline_create.tpl.php | 83 +++++++++++++++++------ htdocs/fourn/commande/card.php | 4 +- htdocs/fourn/facture/card.php | 2 +- htdocs/product/fournisseurs.php | 2 +- 6 files changed, 127 insertions(+), 34 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index bf52ff5871a..397d1a008cf 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -2737,6 +2737,12 @@ class Form $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,"; $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.fk_soc, s.nom as name,"; $sql .= " pfp.supplier_reputation"; + // if we use supplier description of the products + if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) { + $sql .= " ,pfp.desc_fourn as description"; + } else { + $sql .= " ,p.description"; + } // Units if ($conf->global->PRODUCT_USE_UNITS) { $sql .= ", u.label as unit_long, u.short_label as unit_short, p.weight, p.weight_units, p.length, p.length_units, p.width, p.width_units, p.height, p.height_units, p.surface, p.surface_units, p.volume, p.volume_units"; @@ -2766,7 +2772,11 @@ class Form foreach ($scrit as $crit) { if ($i > 0) $sql .= " AND "; - $sql .= "(pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%' OR p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%')"; + $sql .= "(pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%' OR p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'"; + if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) { + $sql .= " OR pfp.desc_fourn LIKE '".$this->db->escape($prefix.$crit)."%'"; + } + $sql .= ")"; $i++; } if (count($scrit) > 1) $sql .= ")"; @@ -2956,8 +2966,9 @@ class Form if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) $opt .= ' disabled'; if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) { - $opt .= ' pbq="'.$objp->idprodfournprice.'" data-pbq="'.$objp->idprodfournprice.'" data-pbqqty="'.$objp->quantity.'" data-pbqup="'.$objp->unitprice.'" data-pbqpercent="'.$objp->remise_percent.'"'; + $opt .= ' data-qty="'.$objp->quantity.'" data-up="'.$objp->unitprice.'" data-discount="'.$outdiscount.'"'; } + $opt .= ' data-description="'.dol_escape_htmltag($objp->description).'"'; $opt .= ' data-html="'.dol_escape_htmltag($optlabel).'"'; $opt .= '>'; @@ -2971,7 +2982,20 @@ class Form // "key" value of json key array is used by jQuery automatically as selected value // "label" value of json key array is used by jQuery automatically as text for combo box $out .= $opt; - array_push($outarray, array('key'=>$outkey, 'value'=>$outref, 'label'=>$outval, 'qty'=>$outqty, 'up'=>$objp->unitprice, 'discount'=>$outdiscount, 'type'=>$outtype, 'duration_value'=>$outdurationvalue, 'duration_unit'=>$outdurationunit, 'disabled'=>(empty($objp->idprodfournprice) ?true:false))); + array_push($outarray, + array('key'=>$outkey, + 'value'=>$outref, + 'label'=>$outval, + 'qty'=>$outqty, + 'up'=>$objp->unitprice, + 'discount'=>$outdiscount, + 'type'=>$outtype, + 'duration_value'=>$outdurationvalue, + 'duration_unit'=>$outdurationunit, + 'disabled'=>(empty($objp->idprodfournprice) ?true:false), + 'description'=>$objp->description + ) + ); // Exemple of var_dump $outarray // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp" // ["label"]=>string(76) "ppp (fff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)" diff --git a/htdocs/core/lib/ajax.lib.php b/htdocs/core/lib/ajax.lib.php index 10fa477600a..9b225967eb1 100644 --- a/htdocs/core/lib/ajax.lib.php +++ b/htdocs/core/lib/ajax.lib.php @@ -134,7 +134,11 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLen } return { label: label, value: item.value, id: item.key, disabled: item.disabled, update: update, textarea: textarea, - pbq: item.pbq, type: item.type, qty: item.qty, discount: item.discount, pricebasetype: item.pricebasetype, price_ht: item.price_ht, price_ttc: item.price_ttc } + pbq: item.pbq, + type: item.type, qty: item.qty, discount: item.discount, + pricebasetype: item.pricebasetype, price_ht: item.price_ht, + price_ttc: item.price_ttc, + up: item.up, description : item.description} })); } else console.error("Error: Ajax url '.$url.($urloption ? '?'.$urloption : '').' has returned an empty page. Should be an empty json array."); @@ -146,14 +150,34 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLen console.log("Call change on input '.$htmlname.' because of select definition of autocomplete select call on input#search_'.$htmlname.'"); console.log("Selected id = "+ui.item.id+" - If this value is null, it means you select a record with key that is null so selection is not effective"); - //console.log(ui.item); - $("#'.$htmlname.'").attr("data-pbq", ui.item.pbq); - $("#'.$htmlname.'").attr("data-pbqup", ui.item.price_ht); - $("#'.$htmlname.'").attr("data-pbqbase", ui.item.pricebasetype); - $("#'.$htmlname.'").attr("data-pbqqty", ui.item.qty); - $("#'.$htmlname.'").attr("data-pbqpercent", ui.item.discount); + console.log(ui.item); + //For supplier price + $("#'.$htmlname.'").attr("data-up", ui.item.up); + $("#'.$htmlname.'").attr("data-discount", ui.item.discount); + $("#'.$htmlname.'").attr("data-qty", ui.item.qty); + $("#'.$htmlname.'").attr("data-description", ui.item.description); - $("#'.$htmlname.'").val(ui.item.id).trigger("change"); // Select new value + //For customer price + '; + + if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY)) { + $script .= ' + $("#' . $htmlname . '").attr("data-pbq", ui.item.pbq); + $("#' . $htmlname . '").attr("data-pbqup", ui.item.price_ht); + $("#' . $htmlname . '").attr("data-pbqbase", ui.item.pricebasetype); + $("#' . $htmlname . '").attr("data-pbqqty", ui.item.qty); + $("#' . $htmlname . '").attr("data-pbqpercent", ui.item.discount); + '; + } else { + $script .= ' + $("#' . $htmlname . '").attr("data-up", ui.item.price_ht); + $("#' . $htmlname . '").attr("data-base", ui.item.pricebasetype); + $("#' . $htmlname . '").attr("data-qty", ui.item.qty); + $("#' . $htmlname . '").attr("data-discount", ui.item.discount); + '; + } + $script .= ' + $("#'.$htmlname.'").val(ui.item.id).trigger("change"); // Select new value // Disable an element if (options.option_disabled) { console.log("Make action option_disabled on #"+options.option_disabled+" with disabled="+ui.item.disabled) diff --git a/htdocs/core/tpl/objectline_create.tpl.php b/htdocs/core/tpl/objectline_create.tpl.php index ec73bf9725b..ba6b4e1bb1b 100644 --- a/htdocs/core/tpl/objectline_create.tpl.php +++ b/htdocs/core/tpl/objectline_create.tpl.php @@ -602,39 +602,39 @@ if (!empty($usemargins) && $user->rights->margins->creer) }); $("#prod_entry_mode_free").on( "click", function() { - setforfree(); + setforfree(); }); $("#select_type").change(function() { - setforfree(); + setforfree(); if (jQuery('#select_type').val() >= 0) { /* focus work on a standard textarea but not if field was replaced with CKEDITOR */ - jQuery('#dp_desc').focus(); - /* focus if CKEDITOR */ - if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") - { - var editor = CKEDITOR.instances['dp_desc']; - if (editor) { editor.focus(); } - } + jQuery('#dp_desc').focus(); + /* focus if CKEDITOR */ + if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") + { + var editor = CKEDITOR.instances['dp_desc']; + if (editor) { editor.focus(); } + } } console.log("Hide/show date according to product type"); if (jQuery('#select_type').val() == '0') { - jQuery('#trlinefordates').hide(); - jQuery('.divlinefordates').hide(); + jQuery('#trlinefordates').hide(); + jQuery('.divlinefordates').hide(); } else { - jQuery('#trlinefordates').show(); - jQuery('.divlinefordates').show(); + jQuery('#trlinefordates').show(); + jQuery('.divlinefordates').show(); } }); $("#prod_entry_mode_predef").on( "click", function() { - console.log("click prod_entry_mode_predef"); - setforpredef(); - jQuery('#trlinefordates').show(); + console.log("click prod_entry_mode_predef"); + setforpredef(); + jQuery('#trlinefordates').show(); }); rights->margins->creer) 'json' ); } - rights->margins->creer) @@ -804,7 +804,10 @@ if (!empty($usemargins) && $user->rights->margins->creer) } ?> - /* To process customer price per quantity (CUSTOMER_PRICE_PER_QTY works only if combo product is not an ajax after x key pressed) */ + global->PRODUIT_CUSTOMER_PRICES_BY_QTY)) + {?> + /* To process customer price per quantity (PRODUIT_CUSTOMER_PRICES_BY_QTY works only if combo product is not an ajax after x key pressed) */ var pbq = parseInt($('option:selected', this).attr('data-pbq')); // When select is done from HTML select if (isNaN(pbq)) { pbq = jQuery('#idprod').attr('data-pbq'); } // When select is done from HTML input with autocomplete var pbqup = parseFloat($('option:selected', this).attr('data-pbqup')); @@ -816,7 +819,7 @@ if (!empty($usemargins) && $user->rights->margins->creer) var pbqpercent = parseFloat($('option:selected', this).attr('data-pbqpercent')); if (isNaN(pbqpercent)) { pbqpercent = jQuery('#idprod').attr('data-pbqpercent'); } - if ((jQuery('#idprod').val() > 0 || jQuery('#idprodfournprice').val()) && ! isNaN(pbq) && pbq > 0) + if ((jQuery('#idprod').val() > 0) && ! isNaN(pbq) && pbq > 0) { var pbqupht = pbqup; /* TODO support of price per qty TTC not yet available */ @@ -832,6 +835,48 @@ if (!empty($usemargins) && $user->rights->margins->creer) jQuery("#remise_percent").val(pbqpercent); } } + + //Deal with supplier + if (jQuery('#idprodfournprice').val() >0) + { + var up = parseFloat($('option:selected', this).attr('data-up')); // When select is done from HTML select + if (isNaN(up)) { up = parseFloat(jQuery('#idprodfournprice').attr('data-up'));} // When select is done from HTML input with autocomplete + + var qty = parseFloat($('option:selected', this).attr('data-qty')); + if (isNaN(qty)) { qty = parseFloat(jQuery('#idprodfournprice').attr('data-qty'));} + + var discount = parseFloat($('option:selected', this).attr('data-discount')); + if (isNaN(discount)) { discount = parseFloat(jQuery('#idprodfournprice').attr('data-discount'));} + + var description = $('option:selected', this).attr('data-description'); + if (typeof description == 'undefined') { description = jQuery('#idprodfournprice').attr('data-description'); } + console.log("We find supplier price :"+up+" qty: "+qty+" discount: "+discount+" for product "+jQuery('#idprodfournprice').val()); + + jQuery("#price_ht").val(up); + if (jQuery("#qty").val() < qty) + { + jQuery("#qty").val(qty); + } + if (jQuery("#remise_percent").val() < discount) + { + jQuery("#remise_percent").val(discount); + } + + console.log("Load desciption into text area : "+description); + global->FCKEDITOR_ENABLE_DETAILS)) { ?> + if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") + { + var editor = CKEDITOR.instances['dp_desc']; + if (editor) { + editor.setData(description); + } + } + + jQuery('#dp_desc').text(description); + + } else { jQuery("#pbq").val(''); diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index e202b3e8efd..e5ddc3b1b75 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -356,7 +356,7 @@ if (empty($reshook)) // Set if we used free entry or predefined product $predef = ''; - $product_desc = (GETPOST('dp_desc') ?GETPOST('dp_desc') : ''); + $product_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : ''); $date_start = dol_mktime(GETPOST('date_start'.$predef.'hour'), GETPOST('date_start'.$predef.'min'), GETPOST('date_start'.$predef.'sec'), GETPOST('date_start'.$predef.'month'), GETPOST('date_start'.$predef.'day'), GETPOST('date_start'.$predef.'year')); $date_end = dol_mktime(GETPOST('date_end'.$predef.'hour'), GETPOST('date_end'.$predef.'min'), GETPOST('date_end'.$predef.'sec'), GETPOST('date_end'.$predef.'month'), GETPOST('date_end'.$predef.'day'), GETPOST('date_end'.$predef.'year')); $prod_entry_mode = GETPOST('prod_entry_mode'); @@ -475,7 +475,7 @@ if (empty($reshook)) $outputlangs = new Translate("", $conf); $outputlangs->setDefaultLang($newlang); } - $desc = (!empty($productsupplier->multilangs [$outputlangs->defaultlang] ["description"])) ? $productsupplier->multilangs [$outputlangs->defaultlang] ["description"] : $productsupplier->description; + $desc = (!empty($productsupplier->multilangs[$outputlangs->defaultlang]["description"])) ? $productsupplier->multilangs[$outputlangs->defaultlang]["description"] : $productsupplier->description; } else { $desc = $productsupplier->description; } diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index c304a4edce7..3f9ea28c420 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -1180,7 +1180,7 @@ if (empty($reshook)) // Set if we used free entry or predefined product $predef = ''; - $product_desc = (GETPOST('dp_desc') ?GETPOST('dp_desc') : ''); + $product_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : ''); $date_start = dol_mktime(GETPOST('date_start'.$predef.'hour'), GETPOST('date_start'.$predef.'min'), GETPOST('date_start'.$predef.'sec'), GETPOST('date_start'.$predef.'month'), GETPOST('date_start'.$predef.'day'), GETPOST('date_start'.$predef.'year')); $date_end = dol_mktime(GETPOST('date_end'.$predef.'hour'), GETPOST('date_end'.$predef.'min'), GETPOST('date_end'.$predef.'sec'), GETPOST('date_end'.$predef.'month'), GETPOST('date_end'.$predef.'day'), GETPOST('date_end'.$predef.'year')); diff --git a/htdocs/product/fournisseurs.php b/htdocs/product/fournisseurs.php index b9a8d6c3b68..41c38c82ba7 100644 --- a/htdocs/product/fournisseurs.php +++ b/htdocs/product/fournisseurs.php @@ -166,7 +166,7 @@ if (empty($reshook)) $price_expression = GETPOST('eid', 'int') ? GETPOST('eid', 'int') : ''; // Discard expression if not in expression mode $delivery_time_days = GETPOST('delivery_time_days', 'int') ? GETPOST('delivery_time_days', 'int') : ''; $supplier_reputation = GETPOST('supplier_reputation'); - $supplier_description = GETPOST('supplier_description', 'alpha'); + $supplier_description = GETPOST('supplier_description', 'restricthtml'); $barcode = GETPOST('barcode', 'alpha'); $fk_barcode_type = GETPOST('fk_barcode_type', 'int'); $packaging = price2num(GETPOST("packaging", 'alphanohtml'), 'MS'); From a53e9077069e4ab467ac94f90a527b8991d3204b Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Mon, 26 Oct 2020 15:32:00 +0000 Subject: [PATCH 012/743] Fixing style errors. --- htdocs/core/tpl/objectline_create.tpl.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/core/tpl/objectline_create.tpl.php b/htdocs/core/tpl/objectline_create.tpl.php index ba6b4e1bb1b..c69b57a90d0 100644 --- a/htdocs/core/tpl/objectline_create.tpl.php +++ b/htdocs/core/tpl/objectline_create.tpl.php @@ -695,7 +695,7 @@ if (!empty($usemargins) && $user->rights->margins->creer) 'json' ); } - rights->margins->creer) @@ -835,7 +835,7 @@ if (!empty($usemargins) && $user->rights->margins->creer) jQuery("#remise_percent").val(pbqpercent); } } - //Deal with supplier From 9aa3368627a052592a342cdeb41dc22d279444fd Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Mon, 26 Oct 2020 16:33:13 +0100 Subject: [PATCH 013/743] syntax --- htdocs/core/lib/ajax.lib.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/lib/ajax.lib.php b/htdocs/core/lib/ajax.lib.php index 9b225967eb1..f8ec1d5ead1 100644 --- a/htdocs/core/lib/ajax.lib.php +++ b/htdocs/core/lib/ajax.lib.php @@ -150,7 +150,7 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLen console.log("Call change on input '.$htmlname.' because of select definition of autocomplete select call on input#search_'.$htmlname.'"); console.log("Selected id = "+ui.item.id+" - If this value is null, it means you select a record with key that is null so selection is not effective"); - console.log(ui.item); + //console.log(ui.item); //For supplier price $("#'.$htmlname.'").attr("data-up", ui.item.up); $("#'.$htmlname.'").attr("data-discount", ui.item.discount); From 2c772cd8bb2b046e5490d3f0f73db6bd9dc2876b Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Tue, 27 Oct 2020 09:52:04 +0100 Subject: [PATCH 014/743] try to make it work for experimental PRODUIT_CUSTOMER_PRICES_BY_QTY option --- htdocs/core/class/html.form.class.php | 45 +++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 397d1a008cf..ff83173be5e 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -2150,7 +2150,9 @@ class Form if (!empty($conf->global->MAIN_MULTILANGS)) { $sql .= ", pl.label as label_translated"; + $sql .= ", pl.description as description_translated"; $selectFields .= ", label_translated"; + $selectFields .= ", description_translated"; } // Price by quantity if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) @@ -2190,7 +2192,18 @@ class Form // Multilang : we add translation if (!empty($conf->global->MAIN_MULTILANGS)) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_lang as pl ON pl.fk_product = p.rowid "; + if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) { + $soc = new Societe($db); + $result = $soc->fetch($socid); + if ($result > 0 && !empty($soc->default_lang)) { + $sql .= " AND pl.lang='" . $this->db->escape($soc->default_lang) . "'"; + } else { + $sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'"; + } + } else { + $sql .= " AND pl.lang='".$this->db->escape($langs->getDefaultLang())."'"; + } } if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) { @@ -2403,7 +2416,9 @@ class Form $outval = ''; $outref = ''; $outlabel = ''; + $outlabel_translated = ''; $outdesc = ''; + $outdesc_translated = ''; $outbarcode = ''; $outorigin = ''; $outtype = ''; @@ -2424,6 +2439,11 @@ class Form $outref = $objp->ref; $outlabel = $objp->label; $outdesc = $objp->description; + if (!empty($conf->global->MAIN_MULTILANGS)) + { + $outlabel_translated = $objp->label_translated; + $outdesc_translated = $objp->description_translated; + } $outbarcode = $objp->barcode; $outorigin = $objp->fk_country; $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid; @@ -2485,6 +2505,10 @@ class Form elseif ($objp->stock <= 0) $opt .= ' class="product_line_stock_too_low"'; } } + if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { + $opt .= ' data-labeltrans="'.$outlabel_translated.'"'; + $opt .= ' data-desctrans="'.dol_escape_htmltag($outdesc_translated).'"'; + } $opt .= '>'; $opt .= $objp->ref; if ($outbarcode) $opt .= ' ('.$outbarcode.')'; @@ -2655,7 +2679,24 @@ class Form } $opt .= "\n"; - $optJson = array('key'=>$outkey, 'value'=>$outref, 'label'=>$outval, 'label2'=>$outlabel, 'desc'=>$outdesc, 'type'=>$outtype, 'price_ht'=>price2num($outprice_ht), 'price_ttc'=>price2num($outprice_ttc), 'pricebasetype'=>$outpricebasetype, 'tva_tx'=>$outtva_tx, 'qty'=>$outqty, 'discount'=>$outdiscount, 'duration_value'=>$outdurationvalue, 'duration_unit'=>$outdurationunit, 'pbq'=>$outpbq); + $optJson = array( + 'key'=>$outkey, + 'value'=>$outref, + 'label'=>$outval, + 'label2'=>$outlabel, + 'desc'=>$outdesc, + 'type'=>$outtype, + 'price_ht'=>price2num($outprice_ht), + 'price_ttc'=>price2num($outprice_ttc), + 'pricebasetype'=>$outpricebasetype, + 'tva_tx'=>$outtva_tx, 'qty'=>$outqty, + 'discount'=>$outdiscount, + 'duration_value'=>$outdurationvalue, + 'duration_unit'=>$outdurationunit, + 'pbq'=>$outpbq, + 'labeltrans'=>$outlabel_translated, + 'desctrans'=>$outdesc_translated + ); } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps From ca2ed2ef74d94d7808154ee8e41cf0f4ec290891 Mon Sep 17 00:00:00 2001 From: Marc de Lima Lucio <68746600+marc-dll@users.noreply.github.com> Date: Wed, 28 Oct 2020 11:04:15 +0100 Subject: [PATCH 015/743] NEW: ref in product customer price: look for products with customer ref --- htdocs/core/class/html.form.class.php | 10 +++++++--- htdocs/core/lib/ajax.lib.php | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index bf52ff5871a..59f98e626a9 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -2137,8 +2137,8 @@ class Form if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) { $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,'; - $sql .= ' pcp.price_base_type as custprice_base_type, pcp.tva_tx as custtva_tx'; - $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx"; + $sql .= ' pcp.price_base_type as custprice_base_type, pcp.tva_tx as custtva_tx, pcp.ref_customer as custref'; + $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custref"; } // Units if (!empty($conf->global->PRODUCT_USE_UNITS)) { @@ -2235,6 +2235,7 @@ class Form if ($i > 0) $sql .= " AND "; $sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'"; if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " OR pl.label LIKE '".$this->db->escape($prefix.$crit)."%'"; + if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && ! empty($socid)) $sql .= " OR pcp.ref_customer LIKE '".$this->db->escape($prefix.$crit)."%'"; if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) { $sql .= " OR p.description LIKE '".$this->db->escape($prefix.$crit)."%'"; @@ -2422,6 +2423,7 @@ class Form $outkey = $objp->rowid; $outref = $objp->ref; + $outrefcust = empty($objp->custref) ? '' : $objp->custref; $outlabel = $objp->label; $outdesc = $objp->description; $outbarcode = $objp->barcode; @@ -2487,11 +2489,13 @@ class Form } $opt .= '>'; $opt .= $objp->ref; + if (! empty($objp->custref)) $opt.= ' (' . $objp->custref . ')'; if ($outbarcode) $opt .= ' ('.$outbarcode.')'; $opt .= ' - '.dol_trunc($label, $maxlengtharticle); if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) $opt .= ' ('.getCountry($outorigin, 1).')'; $objRef = $objp->ref; + if (! empty($objp->custref)) $objRef .= ' (' . $objp->custref . ')'; if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $objRef, 1); $outval .= $objRef; if ($outbarcode) $outval .= ' ('.$outbarcode.')'; @@ -2655,7 +2659,7 @@ class Form } $opt .= "\n"; - $optJson = array('key'=>$outkey, 'value'=>$outref, 'label'=>$outval, 'label2'=>$outlabel, 'desc'=>$outdesc, 'type'=>$outtype, 'price_ht'=>price2num($outprice_ht), 'price_ttc'=>price2num($outprice_ttc), 'pricebasetype'=>$outpricebasetype, 'tva_tx'=>$outtva_tx, 'qty'=>$outqty, 'discount'=>$outdiscount, 'duration_value'=>$outdurationvalue, 'duration_unit'=>$outdurationunit, 'pbq'=>$outpbq); + $optJson = array('key'=>$outkey, 'value'=>$outref, 'label'=>$outval, 'label2'=>$outlabel, 'desc'=>$outdesc, 'type'=>$outtype, 'price_ht'=>price2num($outprice_ht), 'price_ttc'=>price2num($outprice_ttc), 'pricebasetype'=>$outpricebasetype, 'tva_tx'=>$outtva_tx, 'qty'=>$outqty, 'discount'=>$outdiscount, 'duration_value'=>$outdurationvalue, 'duration_unit'=>$outdurationunit, 'pbq'=>$outpbq, 'ref_customer'=>$outrefcust); } // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps diff --git a/htdocs/core/lib/ajax.lib.php b/htdocs/core/lib/ajax.lib.php index 10fa477600a..0c7a8ecc312 100644 --- a/htdocs/core/lib/ajax.lib.php +++ b/htdocs/core/lib/ajax.lib.php @@ -134,7 +134,7 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLen } return { label: label, value: item.value, id: item.key, disabled: item.disabled, update: update, textarea: textarea, - pbq: item.pbq, type: item.type, qty: item.qty, discount: item.discount, pricebasetype: item.pricebasetype, price_ht: item.price_ht, price_ttc: item.price_ttc } + pbq: item.pbq, type: item.type, qty: item.qty, discount: item.discount, pricebasetype: item.pricebasetype, price_ht: item.price_ht, price_ttc: item.price_ttc, ref_customer: item.ref_customer } })); } else console.error("Error: Ajax url '.$url.($urloption ? '?'.$urloption : '').' has returned an empty page. Should be an empty json array."); @@ -152,6 +152,7 @@ function ajax_autocompleter($selected, $htmlname, $url, $urloption = '', $minLen $("#'.$htmlname.'").attr("data-pbqbase", ui.item.pricebasetype); $("#'.$htmlname.'").attr("data-pbqqty", ui.item.qty); $("#'.$htmlname.'").attr("data-pbqpercent", ui.item.discount); + $("#'.$htmlname.'").attr("data-ref-customer", ui.item.ref_customer); $("#'.$htmlname.'").val(ui.item.id).trigger("change"); // Select new value // Disable an element From d77a0b40a8923049710455d633911416cd66ada8 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Wed, 28 Oct 2020 16:37:41 +0100 Subject: [PATCH 016/743] tested and approuve feaure #15036 --- htdocs/comm/propal/card.php | 4 +- htdocs/commande/card.php | 4 +- htdocs/compta/facture/card.php | 4 +- htdocs/contrat/card.php | 4 +- htdocs/core/tpl/objectline_create.tpl.php | 65 +++++++++++++++++------ htdocs/fourn/commande/card.php | 8 ++- htdocs/fourn/facture/card.php | 7 ++- htdocs/langs/en_US/admin.lang | 4 +- htdocs/product/admin/product.php | 23 ++++++++ htdocs/product/ajax/products.php | 3 ++ 10 files changed, 98 insertions(+), 28 deletions(-) diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php index f03986d471c..47dd722b5b1 100644 --- a/htdocs/comm/propal/card.php +++ b/htdocs/comm/propal/card.php @@ -793,7 +793,7 @@ if (empty($reshook)) } elseif ($action == 'addline' && $usercancreate) { // Add line // Set if we used free entry or predefined product $predef = ''; - $product_desc = (GETPOSTISSET('dp_desc') ?GETPOST('dp_desc', 'restricthtml') : ''); + $product_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : ''); $price_ht = price2num(GETPOST('price_ht')); $price_ht_devise = price2num(GETPOST('multicurrency_price_ht')); $prod_entry_mode = GETPOST('prod_entry_mode'); @@ -1001,7 +1001,7 @@ if (empty($reshook)) } //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time - if ($product_desc==$desc) { + if ($product_desc==$desc && !empty($conf->global->PRODUIT_AUTOFILL_DESC)) { $product_desc=''; } diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php index 6572a383532..1bdce17f564 100644 --- a/htdocs/commande/card.php +++ b/htdocs/commande/card.php @@ -640,7 +640,7 @@ if (empty($reshook)) // Set if we used free entry or predefined product $predef = ''; - $product_desc = (GETPOST('dp_desc') ?GETPOST('dp_desc') : ''); + $product_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : ''); $price_ht = GETPOST('price_ht'); $price_ht_devise = GETPOST('multicurrency_price_ht'); $prod_entry_mode = GETPOST('prod_entry_mode'); @@ -855,7 +855,7 @@ if (empty($reshook)) } //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time - if ($product_desc==$desc) { + if ($product_desc==$desc && !empty($conf->global->PRODUIT_AUTOFILL_DESC)) { $product_desc=''; } diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 80e60dd8784..4602e968220 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -1902,7 +1902,7 @@ if (empty($reshook)) // Set if we used free entry or predefined product $predef = ''; - $product_desc = (GETPOST('dp_desc', 'none') ?GETPOST('dp_desc', 'restricthtml') : ''); + $product_desc =(GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : ''); $price_ht = price2num(GETPOST('price_ht')); $price_ht_devise = price2num(GETPOST('multicurrency_price_ht')); $prod_entry_mode = GETPOST('prod_entry_mode', 'alpha'); @@ -2073,7 +2073,7 @@ if (empty($reshook)) } //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time - if ($product_desc==$desc) { + if ($product_desc==$desc && !empty($conf->global->PRODUIT_AUTOFILL_DESC)) { $product_desc=''; } diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php index 034709ef110..3265e2f5866 100644 --- a/htdocs/contrat/card.php +++ b/htdocs/contrat/card.php @@ -383,7 +383,7 @@ if (empty($reshook)) { // Set if we used free entry or predefined product $predef = ''; - $product_desc = (GETPOST('dp_desc') ?GETPOST('dp_desc') : ''); + $product_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : ''); $price_ht = price2num(GETPOST('price_ht')); $price_ht_devise = price2num(GETPOST('multicurrency_price_ht')); if (GETPOST('prod_entry_mode', 'alpha') == 'free') @@ -501,7 +501,7 @@ if (empty($reshook)) $desc = $prod->description; //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time - if ($product_desc==$desc) { + if ($product_desc==$desc && !empty($conf->global->PRODUIT_AUTOFILL_DESC)) { $product_desc=''; } diff --git a/htdocs/core/tpl/objectline_create.tpl.php b/htdocs/core/tpl/objectline_create.tpl.php index c69b57a90d0..f1faf50e991 100644 --- a/htdocs/core/tpl/objectline_create.tpl.php +++ b/htdocs/core/tpl/objectline_create.tpl.php @@ -674,13 +674,18 @@ if (!empty($usemargins) && $user->rights->margins->creer) function(data) { console.log("Load unit price end, we got value "+data.price_ht); jQuery("#price_ht").val(data.price_ht); - global->MAIN_MULTILANGS) && !empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { ?> + global->PRODUIT_AUTOFILL_DESC)) { + if (!empty($conf->global->MAIN_MULTILANGS) && !empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { ?> var proddesc = data.desc_trans; - + var proddesc = data.desc; - + console.log("Load desciption into text area : "+proddesc); - global->FCKEDITOR_ENABLE_DETAILS)) { ?> + global->FCKEDITOR_ENABLE_DETAILS)) { ?> if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") { var editor = CKEDITOR.instances['dp_desc']; @@ -688,9 +693,13 @@ if (!empty($usemargins) && $user->rights->margins->creer) editor.setData(proddesc); } } - + jQuery('#dp_desc').text(proddesc); - + + }, 'json' ); @@ -834,7 +843,7 @@ if (!empty($usemargins) && $user->rights->margins->creer) { jQuery("#remise_percent").val(pbqpercent); } - } + } else { jQuery("#pbq").val(''); } @@ -850,8 +859,6 @@ if (!empty($usemargins) && $user->rights->margins->creer) var discount = parseFloat($('option:selected', this).attr('data-discount')); if (isNaN(discount)) { discount = parseFloat(jQuery('#idprodfournprice').attr('data-discount'));} - var description = $('option:selected', this).attr('data-description'); - if (typeof description == 'undefined') { description = jQuery('#idprodfournprice').attr('data-description'); } console.log("We find supplier price :"+up+" qty: "+qty+" discount: "+discount+" for product "+jQuery('#idprodfournprice').val()); jQuery("#price_ht").val(up); @@ -864,24 +871,48 @@ if (!empty($usemargins) && $user->rights->margins->creer) jQuery("#remise_percent").val(discount); } + global->PRODUIT_AUTOFILL_DESC)) { + ?> + var description = $('option:selected', this).attr('data-description'); + if (typeof description == 'undefined') { description = jQuery('#idprodfournprice').attr('data-description'); } + console.log("Load desciption into text area : "+description); - global->FCKEDITOR_ENABLE_DETAILS)) { ?> + global->FCKEDITOR_ENABLE_DETAILS)) { ?> if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") { var editor = CKEDITOR.instances['dp_desc']; if (editor) { - editor.setData(description); + editor.setData(description); } } - + jQuery('#dp_desc').text(description); - - } - else - { - jQuery("#pbq").val(''); + + } else if (jQuery('#idprodfournprice').length > 0) { + global->PRODUIT_AUTOFILL_DESC)) { + if (!empty($conf->global->FCKEDITOR_ENABLE_DETAILS)) { ?> + if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") + { + var editor = CKEDITOR.instances['dp_desc']; + if (editor) { + editor.setData(''); + } + } + + jQuery('#dp_desc').text(''); + } + /* To set focus */ if (jQuery('#idprod').val() > 0 || jQuery('#idprodfournprice').val() > 0) { diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index 42817e2ea28..f7c963f5e29 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -484,7 +484,13 @@ if (empty($reshook)) $desc = $productsupplier->desc_supplier; } - if (trim($product_desc) != trim($desc)) $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); + //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time + if ($product_desc==$desc && !empty($conf->global->PRODUIT_AUTOFILL_DESC)) { + $product_desc=''; + } + + if (!empty($product_desc) && !empty($conf->global->MAIN_NO_CONCAT_DESCRIPTION)) $desc = $product_desc; + if (!empty($product_desc) && trim($product_desc) != trim($desc)) $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); $type = $productsupplier->type; if ($price_ht != '' || $price_ht_devise != '') { diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index 9849e3b6223..a6bc58879c1 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -1294,7 +1294,12 @@ if (empty($reshook)) $desc = $productsupplier->desc_supplier; } else $desc = $productsupplier->description; - if (trim($product_desc) != trim($desc)) $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); + //If text set in desc is the same as product descpription (as now it's preloaded) whe add it only one time + if ($product_desc==$desc && !empty($conf->global->PRODUIT_AUTOFILL_DESC)) { + $product_desc=''; + } + if (!empty($product_desc) && !empty($conf->global->MAIN_NO_CONCAT_DESCRIPTION)) $desc = $product_desc; + if (!empty($product_desc) && trim($product_desc) != trim($desc)) $desc = dol_concatdesc($desc, $product_desc, '', !empty($conf->global->MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION)); $type = $productsupplier->type; if ($price_ht != '' || $price_ht_devise != '') { diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index e47390b9ce0..c2bcdef711f 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -1587,6 +1587,8 @@ ServiceSetup=Services module setup ProductServiceSetup=Products and Services modules setup NumberOfProductShowInSelect=Maximum number of products to show in combo select lists (0=no limit) ViewProductDescInFormAbility=Display product descriptions in forms (otherwise shown in a tooltip popup) +DoNotAddProductDescAtAddLines=Do not add product description (from product card) on submit add lines on forms +OnProductSelectAddProductDesc=On product selection in forms get the production description into description line MergePropalProductCard=Activate in product/service Attached Files tab an option to merge product PDF document to proposal PDF azur if product/service is in the proposal ViewProductDescInThirdpartyLanguageAbility=Display products descriptions in the language of the third party UseSearchToSelectProductTooltip=Also if you have a large number of products (> 100 000), you can increase speed by setting constant PRODUCT_DONOTSEARCH_ANYWHERE to 1 in Setup->Other. Search will then be limited to start of string. @@ -2067,4 +2069,4 @@ TemplateDeleted=Template deleted MailToSendEventPush=Event reminder email SwitchThisForABetterSecurity=Switching this value to %s is recommended for more security DictionaryProductNature= Nature of product -CountryIfSpecificToOneCountry=Country (if specific to a given country) \ No newline at end of file +CountryIfSpecificToOneCountry=Country (if specific to a given country) diff --git a/htdocs/product/admin/product.php b/htdocs/product/admin/product.php index 313a27884ff..e7003c6ee47 100644 --- a/htdocs/product/admin/product.php +++ b/htdocs/product/admin/product.php @@ -145,6 +145,12 @@ if ($action == 'other') $value = GETPOST('activate_useProdFournDesc', 'alpha'); $res = dolibarr_set_const($db, "PRODUIT_FOURN_TEXTS", $value, 'chaine', 0, '', $conf->entity); + $value = GETPOST('activate_FillProductDescAuto', 'alpha'); + $res = dolibarr_set_const($db, "PRODUIT_AUTOFILL_DESC", $value, 'chaine', 0, '', $conf->entity); + + $value = GETPOST('activate_DoNotAddStdProductDescOnAddLine', 'alpha'); + $res = dolibarr_set_const($db, "MAIN_NO_CONCAT_DESCRIPTION", $value, 'chaine', 0, '', $conf->entity); + if ($value) { $sql_test = "SELECT count(desc_fourn) as cpt FROM ".MAIN_DB_PREFIX."product_fournisseur_price WHERE 1"; $resql = $db->query($sql_test); @@ -630,6 +636,23 @@ print $form->selectyesno("activate_viewProdDescInForm", $conf->global->PRODUIT_D print ''; print ''; +// Do Not Add Product description on add lines +print ''; +print ''.$langs->trans("DoNotAddProductDescAtAddLines").''; +print ''; +print $form->selectyesno("activate_DoNotAddStdProductDescOnAddLine", $conf->global->MAIN_NO_CONCAT_DESCRIPTION, 1); +print ''; +print ''; + +// Do Not Add Product description on add lines +print ''; +print ''.$langs->trans("OnProductSelectAddProductDesc").''; +print ''; +print $form->selectyesno("activate_FillProductDescAuto", $conf->global->PRODUIT_AUTOFILL_DESC, 1); +print ''; +print ''; + + // Activate propal merge produt card /* Kept as hidden feature only. PRODUIT_PDF_MERGE_PROPAL can be added manually. Still did not understand how this feature works. diff --git a/htdocs/product/ajax/products.php b/htdocs/product/ajax/products.php index c2b698c8e04..40249e4041e 100644 --- a/htdocs/product/ajax/products.php +++ b/htdocs/product/ajax/products.php @@ -99,6 +99,9 @@ if (!empty($action) && $action == 'fetch' && !empty($id)) $outputlangs->setDefaultLang($newlang); $outdesc_trans = (!empty($object->multilangs[$outputlangs->defaultlang]["description"])) ? $object->multilangs[$outputlangs->defaultlang]["description"] : $object->description; $outlabel_trans = (!empty($object->multilangs[$outputlangs->defaultlang]["label"])) ? $object->multilangs[$outputlangs->defaultlang]["label"] : $object->label; + } else { + $outdesc_trans = $object->description; + $outlabel_trans = $object->label; } } } From 88b570926f7ab66c829d979545a8b700445128bd Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Wed, 28 Oct 2020 15:38:31 +0000 Subject: [PATCH 017/743] Fixing style errors. --- htdocs/core/tpl/objectline_create.tpl.php | 26 +++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/htdocs/core/tpl/objectline_create.tpl.php b/htdocs/core/tpl/objectline_create.tpl.php index f1faf50e991..bc04e7b6614 100644 --- a/htdocs/core/tpl/objectline_create.tpl.php +++ b/htdocs/core/tpl/objectline_create.tpl.php @@ -678,13 +678,13 @@ if (!empty($usemargins) && $user->rights->margins->creer) if (!empty($conf->global->PRODUIT_AUTOFILL_DESC)) { if (!empty($conf->global->MAIN_MULTILANGS) && !empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { ?> var proddesc = data.desc_trans; - var proddesc = data.desc; - console.log("Load desciption into text area : "+proddesc); - global->FCKEDITOR_ENABLE_DETAILS)) { ?> if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") { @@ -693,12 +693,12 @@ if (!empty($usemargins) && $user->rights->margins->creer) editor.setData(proddesc); } } - jQuery('#dp_desc').text(proddesc); - - }, 'json' @@ -873,12 +873,12 @@ if (!empty($usemargins) && $user->rights->margins->creer) global->PRODUIT_AUTOFILL_DESC)) { - ?> + ?> var description = $('option:selected', this).attr('data-description'); if (typeof description == 'undefined') { description = jQuery('#idprodfournprice').attr('data-description'); } console.log("Load desciption into text area : "+description); - global->FCKEDITOR_ENABLE_DETAILS)) { ?> if (typeof CKEDITOR == "object" && typeof CKEDITOR.instances != "undefined") { @@ -887,10 +887,10 @@ if (!empty($usemargins) && $user->rights->margins->creer) editor.setData(description); } } - jQuery('#dp_desc').text(description); - } else if (jQuery('#idprodfournprice').length > 0) { @@ -904,10 +904,10 @@ if (!empty($usemargins) && $user->rights->margins->creer) editor.setData(''); } } - + jQuery('#dp_desc').text(''); - } From 518a225f7baa8f577877392562f1906aa86723ab Mon Sep 17 00:00:00 2001 From: jcp Date: Tue, 3 Nov 2020 19:48:23 +0100 Subject: [PATCH 018/743] NEW massaction for BankTransfer --- htdocs/fourn/facture/list.php | 101 ++++++++++++++++++++++++++++ htdocs/langs/en_US/withdrawals.lang | 1 + 2 files changed, 102 insertions(+) diff --git a/htdocs/fourn/facture/list.php b/htdocs/fourn/facture/list.php index 0ebfd024658..c1c6329cb43 100644 --- a/htdocs/fourn/facture/list.php +++ b/htdocs/fourn/facture/list.php @@ -270,6 +270,103 @@ if (empty($reshook)) } +if ($massaction == 'transfer_request') +{ + $langs->load("withdrawals"); + + if (!$user->rights->paymentbybanktransfer->create) + { + $error++; + setEventMessages($langs->trans("NotEnoughPermissions"), null, 'errors'); + } + else + { + //Checking error + $error = 0; + + $arrayofselected = is_array($toselect) ? $toselect : array(); + $listofbills = array(); + foreach ($arrayofselected as $toselectid) + { + $objecttmp = new FactureFournisseur($db); + $result = $objecttmp->fetch($toselectid); + if ($result > 0) + { + $totalpaye = $objecttmp->getSommePaiement(); + $totalcreditnotes = $objecttmp->getSumCreditNotesUsed(); + $totaldeposits = $objecttmp->getSumDepositsUsed(); + $objecttmp->resteapayer = price2num($objecttmp->total_ttc - $totalpaye - $totalcreditnotes - $totaldeposits, 'MT'); + if ($objecttmp->paye || $objecttmp->resteapayer == 0) { + $error++; + setEventMessages($objecttmp->ref.' '.$langs->trans("AlreadyPaid"), $objecttmp->errors, 'errors'); + } elseif ($objecttmp->resteapayer < 0) { + $error++; + setEventMessages($objecttmp->ref.' '.$langs->trans("AmountMustBePositive"), $objecttmp->errors, 'errors'); + } + if (!($objecttmp->statut > FactureFournisseur::STATUS_DRAFT)) { + $error++; + setEventMessages($objecttmp->ref.' '.$langs->trans("Draft"), $objecttmp->errors, 'errors'); + } + + $rsql = "SELECT pfd.rowid, pfd.traite, pfd.date_demande as date_demande"; + $rsql .= " , pfd.date_traite as date_traite"; + $rsql .= " , pfd.amount"; + $rsql .= " , u.rowid as user_id, u.lastname, u.firstname, u.login"; + $rsql .= " FROM ".MAIN_DB_PREFIX."prelevement_facture_demande as pfd"; + $rsql .= " , ".MAIN_DB_PREFIX."user as u"; + $rsql .= " WHERE fk_facture_fourn = ".$objecttmp->id; + $rsql .= " AND pfd.fk_user_demande = u.rowid"; + $rsql .= " AND pfd.traite = 0"; + $rsql .= " ORDER BY pfd.date_demande DESC"; + + $result_sql = $db->query($rsql); + if ($result_sql) + { + $numprlv = $db->num_rows($result_sql); + } + + if ($numprlv > 0) { + $error++; + setEventMessages($objecttmp->ref.' '.$langs->trans("RequestAlreadyDone"), $objecttmp->errors, 'warnings'); + } + elseif (!empty($objecttmp->mode_reglement_code) && $objecttmp->mode_reglement_code != 'VIR') { + $error++; + setEventMessages($objecttmp->ref.' '.$langs->trans("BadPaymentMethod"), $objecttmp->errors, 'errors'); + } + else { + $listofbills[] = $objecttmp; // $listofbills will only contains invoices with good payment method and no request already done + } + } + } + + //Massive withdraw request for request with no errors + if (!empty($listofbills)) + { + $nbwithdrawrequestok = 0; + foreach ($listofbills as $aBill) + { + $db->begin(); + $result = $aBill->demande_prelevement($user, $aBill->resteapayer, 'bank-transfer', 'supplier_invoice'); + if ($result > 0) + { + $db->commit(); + $nbwithdrawrequestok++; + } + else + { + $db->rollback(); + setEventMessages($aBill->error, $aBill->errors, 'errors'); + } + } + if ($nbwithdrawrequestok > 0) + { + setEventMessages($langs->trans("BankTransferRequestsDone", $nbwithdrawrequestok), null, 'mesgs'); + } + } + } +} + + /* * View */ @@ -505,6 +602,10 @@ if ($resql) //'builddoc'=>$langs->trans("PDFMerge"), //'presend'=>$langs->trans("SendByMail"), ); + if ($conf->paymentbybanktransfer->enabled) { + $langs->load("withdrawals"); + $arrayofmassactions['transfer_request'] = $langs->trans("MakeBankTransferOrder"); + } //if($user->rights->fournisseur->facture->creer) $arrayofmassactions['createbills']=$langs->trans("CreateInvoiceForThisCustomer"); if ($user->rights->fournisseur->facture->supprimer) $arrayofmassactions['predelete'] = ''.$langs->trans("Delete"); if (in_array($massaction, array('presend', 'predelete', 'createbills'))) $arrayofmassactions = array(); diff --git a/htdocs/langs/en_US/withdrawals.lang b/htdocs/langs/en_US/withdrawals.lang index a18ff7a8d31..1b8f2441b33 100644 --- a/htdocs/langs/en_US/withdrawals.lang +++ b/htdocs/langs/en_US/withdrawals.lang @@ -42,6 +42,7 @@ LastWithdrawalReceipt=Latest %s direct debit receipts MakeWithdrawRequest=Make a direct debit payment request MakeBankTransferOrder=Make a credit transfer request WithdrawRequestsDone=%s direct debit payment requests recorded +BankTransferRequestsDone=%s credit transfer requests recorded ThirdPartyBankCode=Third-party bank code NoInvoiceCouldBeWithdrawed=No invoice debited successfully. Check that invoices are on companies with a valid IBAN and that IBAN has a UMR (Unique Mandate Reference) with mode %s. ClassCredited=Classify credited From 61bca50fa62c0b51127e5802e3926f031afb9812 Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Thu, 5 Nov 2020 16:56:28 +0100 Subject: [PATCH 019/743] NEW: Dictionary for availability - Add column position --- htdocs/admin/dict.php | 14 ++++---- htdocs/core/class/html.form.class.php | 7 ++-- .../install/mysql/data/llx_c_availability.sql | 9 ++--- .../install/mysql/migration/13.0.0-14.0.0.sql | 35 +++++++++++++++++++ .../mysql/tables/llx_c_availability.sql | 5 +-- 5 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 htdocs/install/mysql/migration/13.0.0-14.0.0.sql diff --git a/htdocs/admin/dict.php b/htdocs/admin/dict.php index 43ce69f6e02..adacc0d7b8e 100644 --- a/htdocs/admin/dict.php +++ b/htdocs/admin/dict.php @@ -8,7 +8,7 @@ * Copyright (C) 2011 Remy Younes * Copyright (C) 2012-2015 Marcos García * Copyright (C) 2012 Christophe Battarel - * Copyright (C) 2011-2019 Alexandre Spangaro + * Copyright (C) 2011-2020 Alexandre Spangaro * Copyright (C) 2015 Ferran Marcet * Copyright (C) 2016 Raphaël Doursenaud * Copyright (C) 2019-2020 Frédéric France @@ -205,7 +205,7 @@ $tabsql[17] = "SELECT id as rowid, code, label, accountancy_code, active FR $tabsql[18] = "SELECT rowid as rowid, code, libelle, tracking, active FROM ".MAIN_DB_PREFIX."c_shipment_mode"; $tabsql[19] = "SELECT id as rowid, code, libelle, active FROM ".MAIN_DB_PREFIX."c_effectif"; $tabsql[20] = "SELECT rowid as rowid, code, libelle, active FROM ".MAIN_DB_PREFIX."c_input_method"; -$tabsql[21] = "SELECT c.rowid as rowid, code, label, active FROM ".MAIN_DB_PREFIX."c_availability AS c"; +$tabsql[21] = "SELECT c.rowid as rowid, c.code, c.label, c.active, c.position FROM ".MAIN_DB_PREFIX."c_availability AS c"; $tabsql[22] = "SELECT rowid as rowid, code, label, active FROM ".MAIN_DB_PREFIX."c_input_reason"; $tabsql[23] = "SELECT t.rowid as rowid, t.taux, t.revenuestamp_type, c.label as country, c.code as country_code, t.fk_pays as country_id, t.note, t.active, t.accountancy_code_sell, t.accountancy_code_buy FROM ".MAIN_DB_PREFIX."c_revenuestamp as t, ".MAIN_DB_PREFIX."c_country as c WHERE t.fk_pays=c.rowid"; $tabsql[24] = "SELECT rowid as rowid, code, label, active FROM ".MAIN_DB_PREFIX."c_type_resource"; @@ -250,7 +250,7 @@ $tabsqlsort[17] = "code ASC"; $tabsqlsort[18] = "code ASC, libelle ASC"; $tabsqlsort[19] = "id ASC"; $tabsqlsort[20] = "code ASC, libelle ASC"; -$tabsqlsort[21] = "code ASC, label ASC"; +$tabsqlsort[21] = "code ASC, label ASC, position ASC"; $tabsqlsort[22] = "code ASC, label ASC"; $tabsqlsort[23] = "country ASC, taux ASC"; $tabsqlsort[24] = "code ASC, label ASC"; @@ -295,7 +295,7 @@ $tabfield[17] = "code,label,accountancy_code"; $tabfield[18] = "code,libelle,tracking"; $tabfield[19] = "code,libelle"; $tabfield[20] = "code,libelle"; -$tabfield[21] = "code,label"; +$tabfield[21] = "code,label,position"; $tabfield[22] = "code,label"; $tabfield[23] = "country_id,country,taux,revenuestamp_type,accountancy_code_sell,accountancy_code_buy,note"; $tabfield[24] = "code,label"; @@ -340,7 +340,7 @@ $tabfieldvalue[17] = "code,label,accountancy_code"; $tabfieldvalue[18] = "code,libelle,tracking"; $tabfieldvalue[19] = "code,libelle"; $tabfieldvalue[20] = "code,libelle"; -$tabfieldvalue[21] = "code,label"; +$tabfieldvalue[21] = "code,label,position"; $tabfieldvalue[22] = "code,label"; $tabfieldvalue[23] = "country,taux,revenuestamp_type,accountancy_code_sell,accountancy_code_buy,note"; $tabfieldvalue[24] = "code,label"; @@ -385,7 +385,7 @@ $tabfieldinsert[17] = "code,label,accountancy_code"; $tabfieldinsert[18] = "code,libelle,tracking"; $tabfieldinsert[19] = "code,libelle"; $tabfieldinsert[20] = "code,libelle"; -$tabfieldinsert[21] = "code,label"; +$tabfieldinsert[21] = "code,label,position"; $tabfieldinsert[22] = "code,label"; $tabfieldinsert[23] = "fk_pays,taux,revenuestamp_type,accountancy_code_sell,accountancy_code_buy,note"; $tabfieldinsert[24] = "code,label"; @@ -523,7 +523,7 @@ $tabhelp[17] = array('code'=>$langs->trans("EnterAnyCode")); $tabhelp[18] = array('code'=>$langs->trans("EnterAnyCode"), 'tracking'=>$langs->trans("UrlTrackingDesc")); $tabhelp[19] = array('code'=>$langs->trans("EnterAnyCode")); $tabhelp[20] = array('code'=>$langs->trans("EnterAnyCode")); -$tabhelp[21] = array('code'=>$langs->trans("EnterAnyCode")); +$tabhelp[21] = array('code'=>$langs->trans("EnterAnyCode"), 'position'=>$langs->trans("PositionIntoComboList")); $tabhelp[22] = array('code'=>$langs->trans("EnterAnyCode")); $tabhelp[23] = array('revenuestamp_type'=>'FixedOrPercent'); $tabhelp[24] = array('code'=>$langs->trans("EnterAnyCode")); diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 5c457a2a28c..fac12b48d52 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -15,7 +15,7 @@ * Copyright (C) 2012-2016 Marcos García * Copyright (C) 2012 Cedric Salvador * Copyright (C) 2012-2015 Raphaël Doursenaud - * Copyright (C) 2014 Alexandre Spangaro + * Copyright (C) 2014-2020 Alexandre Spangaro * Copyright (C) 2018 Ferran Marcet * Copyright (C) 2018-2019 Frédéric France * Copyright (C) 2018 Nicolas ZABOURI @@ -3218,7 +3218,7 @@ class Form $langs->load('propal'); - $sql = "SELECT rowid, code, label"; + $sql = "SELECT rowid, code, label, position"; $sql .= " FROM ".MAIN_DB_PREFIX.'c_availability'; $sql .= " WHERE active > 0"; @@ -3235,10 +3235,11 @@ class Form $label = ($langs->trans("AvailabilityType".$obj->code) != ("AvailabilityType".$obj->code) ? $langs->trans("AvailabilityType".$obj->code) : ($obj->label != '-' ? $obj->label : '')); $this->cache_availability[$obj->rowid]['code'] = $obj->code; $this->cache_availability[$obj->rowid]['label'] = $label; + $this->cache_availability[$obj->rowid]['position'] = $obj->position; $i++; } - $this->cache_availability = dol_sort_array($this->cache_availability, 'label', 'asc', 0, 0, 1); + $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1); return $num; } else { diff --git a/htdocs/install/mysql/data/llx_c_availability.sql b/htdocs/install/mysql/data/llx_c_availability.sql index 7d834aa2809..2e06d1de8b3 100644 --- a/htdocs/install/mysql/data/llx_c_availability.sql +++ b/htdocs/install/mysql/data/llx_c_availability.sql @@ -1,4 +1,5 @@ -- Copyright (C) 2011 Philippe GRAND +-- Copyright (C) 2020 Alexandre SPANGARO -- -- 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 @@ -25,7 +26,7 @@ -- delete from llx_c_availability; -INSERT INTO llx_c_availability (rowid,code,label,active) VALUES (1, 'AV_NOW', 'Immediate', 1); -INSERT INTO llx_c_availability (rowid,code,label,active) VALUES (2, 'AV_1W', '1 week', 1); -INSERT INTO llx_c_availability (rowid,code,label,active) VALUES (3, 'AV_2W', '2 weeks', 1); -INSERT INTO llx_c_availability (rowid,code,label,active) VALUES (4, 'AV_3W', '3 weeks', 1); +INSERT INTO llx_c_availability (rowid,code,label,active,position) VALUES (1, 'AV_NOW', 'Immediate', 1, 10); +INSERT INTO llx_c_availability (rowid,code,label,active,position) VALUES (2, 'AV_1W', '1 week', 1, 20); +INSERT INTO llx_c_availability (rowid,code,label,active,position) VALUES (3, 'AV_2W', '2 weeks', 1, 30); +INSERT INTO llx_c_availability (rowid,code,label,active,position) VALUES (4, 'AV_3W', '3 weeks', 1, 40); diff --git a/htdocs/install/mysql/migration/13.0.0-14.0.0.sql b/htdocs/install/mysql/migration/13.0.0-14.0.0.sql new file mode 100644 index 00000000000..58a3f962e3c --- /dev/null +++ b/htdocs/install/mysql/migration/13.0.0-14.0.0.sql @@ -0,0 +1,35 @@ +-- +-- Be carefull to requests order. +-- This file must be loaded by calling /install/index.php page +-- when current version is 14.0.0 or higher. +-- +-- To restrict request to Mysql version x.y minimum use -- VMYSQLx.y +-- To restrict request to Pgsql version x.y minimum use -- VPGSQLx.y +-- To rename a table: ALTER TABLE llx_table RENAME TO llx_table_new; +-- To add a column: ALTER TABLE llx_table ADD COLUMN newcol varchar(60) NOT NULL DEFAULT '0' AFTER existingcol; +-- To rename a column: ALTER TABLE llx_table CHANGE COLUMN oldname newname varchar(60); +-- To drop a column: ALTER TABLE llx_table DROP COLUMN oldname; +-- To change type of field: ALTER TABLE llx_table MODIFY COLUMN name varchar(60); +-- To drop a foreign key: ALTER TABLE llx_table DROP FOREIGN KEY fk_name; +-- To create a unique index ALTER TABLE llx_table ADD UNIQUE INDEX uk_table_field (field); +-- To drop an index: -- VMYSQL4.1 DROP INDEX nomindex on llx_table +-- To drop an index: -- VPGSQL8.2 DROP INDEX nomindex +-- To make pk to be auto increment (mysql): -- VMYSQL4.3 ALTER TABLE llx_table CHANGE COLUMN rowid rowid INTEGER NOT NULL AUTO_INCREMENT; +-- To make pk to be auto increment (postgres): +-- -- VPGSQL8.2 CREATE SEQUENCE llx_table_rowid_seq OWNED BY llx_table.rowid; +-- -- VPGSQL8.2 ALTER TABLE llx_table ADD PRIMARY KEY (rowid); +-- -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN rowid SET DEFAULT nextval('llx_table_rowid_seq'); +-- -- VPGSQL8.2 SELECT setval('llx_table_rowid_seq', MAX(rowid)) FROM llx_table; +-- To set a field as NULL: -- VMYSQL4.3 ALTER TABLE llx_table MODIFY COLUMN name varchar(60) NULL; +-- To set a field as NULL: -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name DROP NOT NULL; +-- To set a field as NOT NULL: -- VMYSQL4.3 ALTER TABLE llx_table MODIFY COLUMN name varchar(60) NOT NULL; +-- To set a field as NOT NULL: -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name SET NOT NULL; +-- To set a field as default NULL: -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name SET DEFAULT NULL; +-- Note: fields with type BLOB/TEXT can't have default value. + + +-- Missing in v13 or lower + +-- For v14 + +ALTER TABLE llx_c_availability ADD COLUMN position integer NOT NULL DEFAULT 0; diff --git a/htdocs/install/mysql/tables/llx_c_availability.sql b/htdocs/install/mysql/tables/llx_c_availability.sql index cef03abe548..4cdc8d6203c 100644 --- a/htdocs/install/mysql/tables/llx_c_availability.sql +++ b/htdocs/install/mysql/tables/llx_c_availability.sql @@ -1,5 +1,6 @@ -- ======================================================================== -- Copyright (C) 2011 Philippe GRAND +-- Copyright (C) 2020 Alexandre SPANGARO -- -- 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 @@ -21,6 +22,6 @@ create table llx_c_availability rowid integer AUTO_INCREMENT PRIMARY KEY, code varchar(30) NOT NULL, label varchar(60) NOT NULL, - active tinyint DEFAULT 1 NOT NULL - + active tinyint DEFAULT 1 NOT NULL, + position integer NOT NULL DEFAULT 0 )ENGINE=innodb; From 11099132da9783cc3b4b0f7eacec8ee1e9d7f086 Mon Sep 17 00:00:00 2001 From: bahfir abbes Date: Fri, 6 Nov 2020 00:45:30 +0100 Subject: [PATCH 020/743] New: add notification on ACTION_CREATE event --- htdocs/core/class/notify.class.php | 27 ++++++++++++++----- ..._50_modNotification_Notification.class.php | 3 ++- .../mysql/data/llx_c_action_trigger.sql | 2 +- htdocs/langs/en_US/other.lang | 2 ++ htdocs/langs/fr_FR/other.lang | 2 ++ 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/htdocs/core/class/notify.class.php b/htdocs/core/class/notify.class.php index 6547b98471a..fb0ce5a91f5 100644 --- a/htdocs/core/class/notify.class.php +++ b/htdocs/core/class/notify.class.php @@ -81,7 +81,8 @@ class Notify 'EXPENSE_REPORT_VALIDATE', 'EXPENSE_REPORT_APPROVE', 'HOLIDAY_VALIDATE', - 'HOLIDAY_APPROVE' + 'HOLIDAY_APPROVE', + 'ACTION_CREATE' ); @@ -502,12 +503,18 @@ class Notify $object_type = 'holiday'; $mesg = $outputlangs->transnoentitiesnoconv("EMailTextHolidayValidated", $link); break; - case 'HOLIDAY_APPROVE': - $link = ''.$newref.''; - $dir_output = $conf->holiday->dir_output; - $object_type = 'holiday'; - $mesg = $outputlangs->transnoentitiesnoconv("EMailTextHolidayApproved", $link); - break; + case 'HOLIDAY_APPROVE': + $link = ''.$newref.''; + $dir_output = $conf->holiday->dir_output; + $object_type = 'holiday'; + $mesg = $outputlangs->transnoentitiesnoconv("EMailTextHolidayApproved", $link); + break; + case 'ACTION_CREATE': + $link = ''.$newref.''; + $dir_output = $conf->agenda->dir_output; + $object_type = 'action'; + $mesg = $outputlangs->transnoentitiesnoconv("EMailTextActionAdded", $link); + break; } $ref = dol_sanitizeFileName($newref); $pdf_path = $dir_output."/".$ref."/".$ref.".pdf"; @@ -718,6 +725,12 @@ class Notify $object_type = 'holiday'; $mesg = $langs->transnoentitiesnoconv("EMailTextHolidayApproved", $link); break; + case 'ACTION_CREATE': + $link = ''.$newref.''; + $dir_output = $conf->agenda->dir_output; + $object_type = 'action'; + $mesg = $langs->transnoentitiesnoconv("EMailTextActionAdded", $link); + break; } $ref = dol_sanitizeFileName($newref); $pdf_path = $dir_output."/".$ref."/".$ref.".pdf"; diff --git a/htdocs/core/triggers/interface_50_modNotification_Notification.class.php b/htdocs/core/triggers/interface_50_modNotification_Notification.class.php index c5a4f450e42..a6078796478 100644 --- a/htdocs/core/triggers/interface_50_modNotification_Notification.class.php +++ b/htdocs/core/triggers/interface_50_modNotification_Notification.class.php @@ -46,7 +46,8 @@ class InterfaceNotification extends DolibarrTriggers 'EXPENSE_REPORT_VALIDATE', 'EXPENSE_REPORT_APPROVE', 'HOLIDAY_VALIDATE', - 'HOLIDAY_APPROVE' + 'HOLIDAY_APPROVE', + 'ACTION_CREATE' ); /** diff --git a/htdocs/install/mysql/data/llx_c_action_trigger.sql b/htdocs/install/mysql/data/llx_c_action_trigger.sql index eeca137dbc0..c72a8ab7f40 100644 --- a/htdocs/install/mysql/data/llx_c_action_trigger.sql +++ b/htdocs/install/mysql/data/llx_c_action_trigger.sql @@ -145,4 +145,4 @@ insert into llx_c_action_trigger (code,label,description,elementtype,rang) value insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('TASK_CREATE','Task created','Executed when a project task is created','project',150); insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('TASK_MODIFY','Task modified','Executed when a project task is modified','project',151); insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('TASK_DELETE','Task deleted','Executed when a project task is deleted','project',152); - +insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('ACTION_CREATE','Action added','Executed when an action is added to the agenda','agenda',700); diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index 7495291cf3f..40f4f019793 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -78,6 +78,7 @@ Notify_EXPENSE_REPORT_VALIDATE=Expense report validated (approval required) Notify_EXPENSE_REPORT_APPROVE=Expense report approved Notify_HOLIDAY_VALIDATE=Leave request validated (approval required) Notify_HOLIDAY_APPROVE=Leave request approved +Notify_ACTION_CREATE=Added action to Agenda SeeModuleSetup=See setup of module %s NbOfAttachedFiles=Number of attached files/documents TotalSizeOfAttachedFiles=Total size of attached files/documents @@ -215,6 +216,7 @@ EMailTextExpenseReportValidated=Expense report %s has been validated. EMailTextExpenseReportApproved=Expense report %s has been approved. EMailTextHolidayValidated=Leave request %s has been validated. EMailTextHolidayApproved=Leave request %s has been approved. +EMailTextActionAdded=The action %s has been added to the Agenda. ImportedWithSet=Importation data set DolibarrNotification=Automatic notification ResizeDesc=Enter new width OR new height. Ratio will be kept during resizing... diff --git a/htdocs/langs/fr_FR/other.lang b/htdocs/langs/fr_FR/other.lang index 0a5abbfbf3a..5120fd52e67 100644 --- a/htdocs/langs/fr_FR/other.lang +++ b/htdocs/langs/fr_FR/other.lang @@ -78,6 +78,7 @@ Notify_EXPENSE_REPORT_VALIDATE=Note de frais validée (approbation requise) Notify_EXPENSE_REPORT_APPROVE=Note de frais approuvé Notify_HOLIDAY_VALIDATE=Demande de congé validée (approbation requise) Notify_HOLIDAY_APPROVE=Demande de congé approuvée +Notify_ACTION_CREATE=Ajout d'action à l'agenda SeeModuleSetup=Voir la configuration du module %s NbOfAttachedFiles=Nombre de fichiers/documents liés TotalSizeOfAttachedFiles=Taille totale fichiers/documents liés @@ -218,6 +219,7 @@ EMailTextExpenseReportValidated=La note de frais %s a été validée. EMailTextExpenseReportApproved=La note de frais %s a été approuvée. EMailTextHolidayValidated=La demande de congé %s a été validée. EMailTextHolidayApproved=La demande de congé %s a été approuvée. +EMailTextActionAdded=L'action %s a été ajoutée à l'Agenda. ImportedWithSet=Lot d'importation (Import key) DolibarrNotification=Notification automatique ResizeDesc=Entrer la nouvelle largeur OU la nouvelle hauteur. Le ratio d'aspect est conservé lors du redimensionnement… From 1bdd617feb8edb25e07ebdf0c520dee4489e821c Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Sat, 7 Nov 2020 15:31:27 +0100 Subject: [PATCH 021/743] NEW: Multiselect ledger account code filter on book keeping list --- htdocs/accountancy/admin/index.php | 20 ++++- htdocs/accountancy/bookkeeping/list.php | 14 +++- .../core/class/html.formaccounting.class.php | 82 ++++++++++++++++++- htdocs/core/modules/modAccounting.class.php | 6 ++ htdocs/langs/en_US/accountancy.lang | 2 +- htdocs/langs/fr_FR/accountancy.lang | 2 +- 6 files changed, 117 insertions(+), 9 deletions(-) diff --git a/htdocs/accountancy/admin/index.php b/htdocs/accountancy/admin/index.php index 516af988334..92c2df3ba0e 100644 --- a/htdocs/accountancy/admin/index.php +++ b/htdocs/accountancy/admin/index.php @@ -48,6 +48,7 @@ $action = GETPOST('action', 'aZ09'); $list = array( 'ACCOUNTING_LENGTH_GACCOUNT', 'ACCOUNTING_LENGTH_AACCOUNT', + 'ACCOUNTING_LIMIT_LIST_VENTILATION' // 'ACCOUNTING_LENGTH_DESCRIPTION', // adjust size displayed for lines description for dol_trunc // 'ACCOUNTING_LENGTH_DESCRIPTION_ACCOUNT', // adjust size displayed for select account description for dol_trunc ); @@ -248,7 +249,7 @@ if (!empty($conf->global->ACCOUNTING_ENABLE_EXPORT_DRAFT_JOURNAL)) { print ''; */ -print ''; +/*print ''; print ''.$langs->trans("BANK_DISABLE_DIRECT_INPUT").''; if (!empty($conf->global->BANK_DISABLE_DIRECT_INPUT)) { print ''; @@ -259,7 +260,7 @@ if (!empty($conf->global->BANK_DISABLE_DIRECT_INPUT)) { print img_picto($langs->trans("Disabled"), 'switch_off'); print ''; } -print ''; +print '';*/ print ''; print ''.$langs->trans("ACCOUNTANCY_COMBO_FOR_AUX").''; @@ -313,6 +314,21 @@ print ''; print ''.$langs->trans('BindingOptions').''; print "\n"; + +// TO DO Mutualize code for yes/no constants +print ''; +print ''.$langs->trans("").''; +if (!empty($conf->global->ACCOUNTING_LIST_SORT_VENTILATION_TODO)) { + print ''; + print img_picto($langs->trans("Activated"), 'switch_on'); + print ''; +} else { + print ''; + print img_picto($langs->trans("Disabled"), 'switch_off'); + print ''; +} +print ''; + // TO DO Mutualize code for yes/no constants print ''; print ''.$langs->trans("ACCOUNTING_LIST_SORT_VENTILATION_TODO").''; diff --git a/htdocs/accountancy/bookkeeping/list.php b/htdocs/accountancy/bookkeeping/list.php index 6d663edcf4e..7219136c2da 100644 --- a/htdocs/accountancy/bookkeeping/list.php +++ b/htdocs/accountancy/bookkeeping/list.php @@ -85,7 +85,7 @@ $search_mvt_label = GETPOST('search_mvt_label', 'alpha'); $search_direction = GETPOST('search_direction', 'alpha'); $search_debit = GETPOST('search_debit', 'alpha'); $search_credit = GETPOST('search_credit', 'alpha'); -$search_ledger_code = GETPOST('search_ledger_code', 'alpha'); +$search_ledger_code = GETPOST('search_ledger_code', 'array'); $search_lettering_code = GETPOST('search_lettering_code', 'alpha'); $search_not_reconciled = GETPOST('search_reconciled_option', 'alpha'); @@ -192,7 +192,7 @@ if (empty($reshook)) $search_accountancy_aux_code_end = ''; $search_mvt_label = ''; $search_direction = ''; - $search_ledger_code = ''; + $search_ledger_code = array(); $search_date_start = ''; $search_date_end = ''; $search_date_creation_start = ''; @@ -267,7 +267,9 @@ if (empty($reshook)) } if (!empty($search_ledger_code)) { $filter['t.code_journal'] = $search_ledger_code; - $param .= '&search_ledger_code='.urlencode($search_ledger_code); + foreach ($search_ledger_code as $code) { + $param .= '&search_ledger_code[]='.urlencode($code); + } } if (!empty($search_mvt_num)) { $filter['t.piece_num'] = $search_mvt_num; @@ -447,6 +449,8 @@ if (count($filter) > 0) { $sqlwhere[] = natural_search($key, $value, 1, 1); } elseif ($key == 't.reconciled_option') { $sqlwhere[] = 't.lettering_code IS NULL'; + } elseif ($key == 't.code_journal' && !empty($value)) { + $sqlwhere[] = natural_search("t.code_journal", join(',', $value), 3, 1); } else { $sqlwhere[] = natural_search($key, $value, 0, 1); } @@ -756,7 +760,9 @@ if (!empty($arrayfields['t.lettering_code']['checked'])) // Code journal if (!empty($arrayfields['t.code_journal']['checked'])) { - print ''; + print ''; + print $formaccounting->multi_select_journal($search_ledger_code, 'search_ledger_code', 0, 1, 1, 1); + print ''; } // Fields from hook diff --git a/htdocs/core/class/html.formaccounting.class.php b/htdocs/core/class/html.formaccounting.class.php index d6f7b269da1..373501a7adf 100644 --- a/htdocs/core/class/html.formaccounting.class.php +++ b/htdocs/core/class/html.formaccounting.class.php @@ -59,7 +59,7 @@ class FormAccounting extends Form /** * Return list of journals with label by nature * - * @param string $selectid Preselected pcg_type + * @param string $selectid Preselected journal code * @param string $htmlname Name of field in html form * @param int $nature Limit the list to a particular type of journals (1:various operations / 2:sale / 3:purchase / 4:bank / 9: has-new) * @param int $showempty Add an empty field @@ -136,6 +136,86 @@ class FormAccounting extends Form return $out; } + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of journals with label by nature + * + * @param array $selectids Preselected journal code + * @param string $htmlname Name of field in html form + * @param int $nature Limit the list to a particular type of journals (1:various operations / 2:sale / 3:purchase / 4:bank / 9: has-new) + * @param int $showempty Add an empty field + * @param int $select_in 0=selectid value is the journal rowid (default) or 1=selectid is journal code + * @param int $select_out Set value returned by select. 0=rowid (default), 1=code + * @param string $morecss More css non HTML object + * @param string $usecache Key to use to store result into a cache. Next call with same key will reuse the cache. + * @param int $disabledajaxcombo Disable ajax combo box. + * @return string String with HTML select + */ + public function multi_select_journal($selectedIds=array(), $htmlname = 'journal', $nature = 0, $showempty = 0, $select_in = 0, $select_out = 0, $morecss = '', $usecache = '', $disabledajaxcombo = 0) + { + // phpcs:enable + global $conf, $langs; + + $out = ''; + + $options = array(); + if ($usecache && !empty($this->options_cache[$usecache])) + { + $options = $this->options_cache[$usecache]; + $selected = $selectedIds; + } else { + $sql = "SELECT rowid, code, label, nature, entity, active"; + $sql .= " FROM ".MAIN_DB_PREFIX."accounting_journal"; + $sql .= " WHERE active = 1"; + $sql .= " AND entity = ".$conf->entity; + if ($nature && is_numeric($nature)) $sql .= " AND nature = ".$nature; + $sql .= " ORDER BY code"; + + dol_syslog(get_class($this)."::multi_select_journal", LOG_DEBUG); + $resql = $this->db->query($sql); + + if (!$resql) { + $this->error = "Error ".$this->db->lasterror(); + dol_syslog(get_class($this)."::multi_select_journal ".$this->error, LOG_ERR); + return -1; + } + + $selected = array(); + $langs->load('accountancy'); + while ($obj = $this->db->fetch_object($resql)) + { + $label = $langs->trans($obj->label); + + $select_value_in = $obj->rowid; + $select_value_out = $obj->rowid; + + // Try to guess if we have found default value + if ($select_in == 1) { + $select_value_in = $obj->code; + } + if ($select_out == 1) { + $select_value_out = $obj->code; + } + // Remember guy's we store in database llx_accounting_bookkeeping the code of accounting_journal and not the rowid + if (!empty($selectedIds) && in_array($select_value_in, $selectedIds)) { + //var_dump("Found ".$selectid." ".$select_value_in); + $selected[] = $select_value_out; + } + $options[$select_value_out] = $label; + } + $this->db->free($resql); + + if ($usecache) + { + $this->options_cache[$usecache] = $options; + } + } + + $out .= Form::multiselectarray($htmlname, $options, $selected, $showempty, 0, $morecss, 0, 0, 0, 'code_journal', '',($disabledajaxcombo ? 0 : 1)); + + return $out; + } + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps /** * Return list of accounting category. diff --git a/htdocs/core/modules/modAccounting.class.php b/htdocs/core/modules/modAccounting.class.php index 811dd57544e..7eff70422ee 100644 --- a/htdocs/core/modules/modAccounting.class.php +++ b/htdocs/core/modules/modAccounting.class.php @@ -154,6 +154,12 @@ class modAccounting extends DolibarrModules "csv", "", 0, 'current', 0 ); + $this->const[14] = array( + "ACCOUNTING_LIMIT_LIST_VENTILATION", + "chaine", + "50", + "", 0, 'current', 0 + ); // Tabs $this->tabs = array(); diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 1d75fa153e8..c35d85e7bcb 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -144,7 +144,7 @@ NotVentilatedinAccount=Not bound to the accounting account XLineSuccessfullyBinded=%s products/services successfully bound to an accounting account XLineFailedToBeBinded=%s products/services were not bound to any accounting account -ACCOUNTING_LIMIT_LIST_VENTILATION=Number of elements to bind shown by page (maximum recommended: 50) +ACCOUNTING_LIMIT_LIST_VENTILATION=Number of elements to on list and bind shown by page (maximum recommended: 50) ACCOUNTING_LIST_SORT_VENTILATION_TODO=Begin the sorting of the page "Binding to do" by the most recent elements ACCOUNTING_LIST_SORT_VENTILATION_DONE=Begin the sorting of the page "Binding done" by the most recent elements diff --git a/htdocs/langs/fr_FR/accountancy.lang b/htdocs/langs/fr_FR/accountancy.lang index 6381caebbec..5309adfae93 100644 --- a/htdocs/langs/fr_FR/accountancy.lang +++ b/htdocs/langs/fr_FR/accountancy.lang @@ -137,7 +137,7 @@ NotVentilatedinAccount=Non lié au compte comptable XLineSuccessfullyBinded=%s produits/service correctement liés à un compte comptable XLineFailedToBeBinded=%s produits/services n'ont pu être liés à un compte comptable -ACCOUNTING_LIMIT_LIST_VENTILATION=Nombre d'éléments à lier représentés par page (maximum recommandé: 50) +ACCOUNTING_LIMIT_LIST_VENTILATION=Nombre de ligne des listes et d'éléments à lier représentés par page (maximum recommandé: 50) ACCOUNTING_LIST_SORT_VENTILATION_TODO=Commencez le tri de la page "Lien à réaliser" par les éléments les plus récents ACCOUNTING_LIST_SORT_VENTILATION_DONE=Commencez le tri de la page "Liens réalisés" par les éléments les plus récents From c9373e318ed68b9944074e16b743a2ca66e0d8d2 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Sat, 7 Nov 2020 15:33:23 +0100 Subject: [PATCH 022/743] fix admin --- htdocs/accountancy/admin/index.php | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/htdocs/accountancy/admin/index.php b/htdocs/accountancy/admin/index.php index 92c2df3ba0e..6a33d009765 100644 --- a/htdocs/accountancy/admin/index.php +++ b/htdocs/accountancy/admin/index.php @@ -249,7 +249,7 @@ if (!empty($conf->global->ACCOUNTING_ENABLE_EXPORT_DRAFT_JOURNAL)) { print ''; */ -/*print ''; +print ''; print ''.$langs->trans("BANK_DISABLE_DIRECT_INPUT").''; if (!empty($conf->global->BANK_DISABLE_DIRECT_INPUT)) { print ''; @@ -260,7 +260,7 @@ if (!empty($conf->global->BANK_DISABLE_DIRECT_INPUT)) { print img_picto($langs->trans("Disabled"), 'switch_off'); print ''; } -print '';*/ +print ''; print ''; print ''.$langs->trans("ACCOUNTANCY_COMBO_FOR_AUX").''; @@ -314,21 +314,6 @@ print ''; print ''.$langs->trans('BindingOptions').''; print "\n"; - -// TO DO Mutualize code for yes/no constants -print ''; -print ''.$langs->trans("").''; -if (!empty($conf->global->ACCOUNTING_LIST_SORT_VENTILATION_TODO)) { - print ''; - print img_picto($langs->trans("Activated"), 'switch_on'); - print ''; -} else { - print ''; - print img_picto($langs->trans("Disabled"), 'switch_off'); - print ''; -} -print ''; - // TO DO Mutualize code for yes/no constants print ''; print ''.$langs->trans("ACCOUNTING_LIST_SORT_VENTILATION_TODO").''; From 259462b53a2f372a1094a820d42ece8642d7fc64 Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Sat, 7 Nov 2020 14:36:28 +0000 Subject: [PATCH 023/743] Fixing style errors. --- htdocs/core/class/html.formaccounting.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/core/class/html.formaccounting.class.php b/htdocs/core/class/html.formaccounting.class.php index 373501a7adf..14f97d23ebf 100644 --- a/htdocs/core/class/html.formaccounting.class.php +++ b/htdocs/core/class/html.formaccounting.class.php @@ -151,7 +151,7 @@ class FormAccounting extends Form * @param int $disabledajaxcombo Disable ajax combo box. * @return string String with HTML select */ - public function multi_select_journal($selectedIds=array(), $htmlname = 'journal', $nature = 0, $showempty = 0, $select_in = 0, $select_out = 0, $morecss = '', $usecache = '', $disabledajaxcombo = 0) + public function multi_select_journal($selectedIds = array(), $htmlname = 'journal', $nature = 0, $showempty = 0, $select_in = 0, $select_out = 0, $morecss = '', $usecache = '', $disabledajaxcombo = 0) { // phpcs:enable global $conf, $langs; @@ -211,7 +211,7 @@ class FormAccounting extends Form } } - $out .= Form::multiselectarray($htmlname, $options, $selected, $showempty, 0, $morecss, 0, 0, 0, 'code_journal', '',($disabledajaxcombo ? 0 : 1)); + $out .= Form::multiselectarray($htmlname, $options, $selected, $showempty, 0, $morecss, 0, 0, 0, 'code_journal', '', ($disabledajaxcombo ? 0 : 1)); return $out; } From 58cb63adf74c8648037b5067605f9770de4f34d5 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Sat, 7 Nov 2020 15:48:12 +0100 Subject: [PATCH 024/743] fix stockler --- htdocs/core/class/html.formaccounting.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/core/class/html.formaccounting.class.php b/htdocs/core/class/html.formaccounting.class.php index 373501a7adf..08268d6bb32 100644 --- a/htdocs/core/class/html.formaccounting.class.php +++ b/htdocs/core/class/html.formaccounting.class.php @@ -140,7 +140,7 @@ class FormAccounting extends Form /** * Return list of journals with label by nature * - * @param array $selectids Preselected journal code + * @param array $selectedIds Preselected journal code array * @param string $htmlname Name of field in html form * @param int $nature Limit the list to a particular type of journals (1:various operations / 2:sale / 3:purchase / 4:bank / 9: has-new) * @param int $showempty Add an empty field From 50d530733e7450ff3481baead7cbfb51a9e9fd26 Mon Sep 17 00:00:00 2001 From: jcp Date: Mon, 9 Nov 2020 13:17:24 +0100 Subject: [PATCH 025/743] NEW: ICS fields moved into bank account table --- htdocs/admin/paymentbybanktransfer.php | 6 ++- htdocs/admin/prelevement.php | 9 ++++- htdocs/compta/bank/card.php | 22 ++++++++++- htdocs/compta/bank/class/account.class.php | 20 +++++++++- htdocs/compta/paymentbybanktransfer/index.php | 2 +- .../class/bonprelevement.class.php | 7 ++-- htdocs/compta/prelevement/create.php | 39 +++++++++++++++---- htdocs/core/lib/prelevement.lib.php | 18 ++++++--- .../bank/doc/pdf_sepamandate.modules.php | 2 +- htdocs/langs/en_US/withdrawals.lang | 6 ++- 10 files changed, 107 insertions(+), 24 deletions(-) diff --git a/htdocs/admin/paymentbybanktransfer.php b/htdocs/admin/paymentbybanktransfer.php index 6a419d39e53..5449e927a4f 100644 --- a/htdocs/admin/paymentbybanktransfer.php +++ b/htdocs/admin/paymentbybanktransfer.php @@ -71,10 +71,10 @@ if ($action == "set") if (! $res > 0) $error++; */ } else $error++; - +/* Moved to account $res = dolibarr_set_const($db, "PAYMENTBYBANKTRANSFER_ICS", GETPOST("PAYMENTBYBANKTRANSFER_ICS"), 'chaine', 0, '', $conf->entity); if (!$res > 0) $error++; - +*/ if (GETPOST("PAYMENTBYBANKTRANSFER_USER") > 0) { $res = dolibarr_set_const($db, "PAYMENTBYBANKTRANSFER_USER", GETPOST("PAYMENTBYBANKTRANSFER_USER"), 'chaine', 0, '', $conf->entity); @@ -156,11 +156,13 @@ print ''; $form->select_comptes($conf->global->PAYMENTBYBANKTRANSFER_ID_BANKACCOUNT, 'PAYMENTBYBANKTRANSFER_ID_BANKACCOUNT', 0, "courant=1", 1); print ''; +/* Moved to bank account data // ICS print ''.$langs->trans("ICS").''; print ''; print ''; print ''; +*/ //User print ''.$langs->trans("ResponsibleUser").''; diff --git a/htdocs/admin/prelevement.php b/htdocs/admin/prelevement.php index c16b5c30c47..601cefc1dd2 100644 --- a/htdocs/admin/prelevement.php +++ b/htdocs/admin/prelevement.php @@ -70,11 +70,14 @@ if ($action == "set") $res = dolibarr_set_const($db, "PRELEVEMENT_RAISON_SOCIALE", $account->proprio,'chaine',0,'',$conf->entity); if (! $res > 0) $error++; */ + } else $error++; +/* Moved to account + $res = dolibarr_set_const($db, "PRELEVEMENT_ICS", GETPOST("PRELEVEMENT_ICS"), 'chaine', 0, '', $conf->entity); if (!$res > 0) $error++; - +*/ if (GETPOST("PRELEVEMENT_USER") > 0) { $res = dolibarr_set_const($db, "PRELEVEMENT_USER", GETPOST("PRELEVEMENT_USER"), 'chaine', 0, '', $conf->entity); @@ -149,13 +152,16 @@ print ''.$langs->trans("Parameter").''; print ''.$langs->trans("Value").''; print ""; + // Bank account (from Banks module) print ''.$langs->trans("BankToReceiveWithdraw").''; print ''; $form->select_comptes($conf->global->PRELEVEMENT_ID_BANKACCOUNT, 'PRELEVEMENT_ID_BANKACCOUNT', 0, "courant=1", 1); print ''; +/* Moved to bank account data // ICS + print ''; $htmltext = $langs->trans("AskThisIDToYourBank"); print $form->textwithpicto($langs->trans("ICS"), $htmltext); @@ -164,6 +170,7 @@ print ''; print ''; print ''; print ''; +*/ //User print ''.$langs->trans("ResponsibleUser").''; diff --git a/htdocs/compta/bank/card.php b/htdocs/compta/bank/card.php index 8b086516351..83940f4ac43 100644 --- a/htdocs/compta/bank/card.php +++ b/htdocs/compta/bank/card.php @@ -94,6 +94,9 @@ if ($action == 'add') $object->proprio = trim(GETPOST("proprio", 'alphanohtml')); $object->owner_address = trim(GETPOST("owner_address", 'nohtml')); + + $object->ics = trim($_POST["ics"]); + $object->ics_transfer = trim($_POST["ics_transfer"]); $account_number = GETPOST('account_number', 'alphanohtml'); if (empty($account_number) || $account_number == '-1') @@ -195,6 +198,9 @@ if ($action == 'update') $object->proprio = trim(GETPOST("proprio", 'alphanohtml')); $object->owner_address = trim(GETPOST("owner_address", 'nohtml')); + + $object->ics = trim($_POST["ics"]); + $object->ics_transfer = trim($_POST["ics_transfer"]); $account_number = GETPOST('account_number', 'alpha'); if (empty($account_number) || $account_number == '-1') @@ -730,6 +736,14 @@ if ($action == 'create') } } print ''; + + print ''.$langs->trans("ICS").''; + print ''.$object->ics.''; + print ''; + + print ''.$langs->trans("ICSTransfer").''; + print ''.$object->ics_transfer.''; + print ''; print ''.$langs->trans("BankAccountDomiciliation").''; print nl2br($object->domiciliation); @@ -1010,7 +1024,13 @@ if ($action == 'create') print ''.$langs->trans($bickey).''; print ''; - + + print ''.$langs->trans("ICS").''; + print ''; + + print ''.$langs->trans("ICSTransfer").''; + print ''; + print ''.$langs->trans("BankAccountDomiciliation").''; print '', null, 'warnings'); setEventMessages($langs->trans("MailingNeedCommand2"), null, 'warnings'); $action = ''; - } elseif ($conf->global->MAILING_LIMIT_SENDBYWEB < 0) - { + } elseif ($conf->global->MAILING_LIMIT_SENDBYWEB < 0) { setEventMessages($langs->trans("NotEnoughPermissions"), null, 'warnings'); $action = ''; } else { $upload_dir = $conf->mailing->dir_output."/".get_exdir($object->id, 2, 0, 1, $object, 'mailing'); - if ($object->statut == 0) - { + if ($object->statut == 0) { dol_print_error('', 'ErrorMailIsNotValidated'); exit; } @@ -137,7 +135,9 @@ if (empty($reshook)) $errorsto = $object->email_errorsto; // Is the message in html $msgishtml = -1; // Unknown by default - if (preg_match('/[\s\t]*/i', $message)) $msgishtml = 1; + if (preg_match('/[\s\t]*/i', $message)) { + $msgishtml = 1; + } // Warning, we must not use begin-commit transaction here // because we want to save update for each mail sent. @@ -153,12 +153,10 @@ if (empty($reshook)) dol_syslog("card.php: select targets", LOG_DEBUG); $resql = $db->query($sql); - if ($resql) - { + if ($resql) { $num = $db->num_rows($resql); // Number of possible recipients - if ($num) - { + if ($num) { dol_syslog("comm/mailing/card.php: nb of targets = ".$num, LOG_DEBUG); $now = dol_now(); @@ -166,16 +164,14 @@ if (empty($reshook)) // Positioning date of start sending $sql = "UPDATE ".MAIN_DB_PREFIX."mailing SET date_envoi='".$db->idate($now)."' WHERE rowid=".$object->id; $resql2 = $db->query($sql); - if (!$resql2) - { + if (!$resql2) { dol_print_error($db); } // Loop on each email and send it $i = 0; - while ($i < $num && $i < $conf->global->MAILING_LIMIT_SENDBYWEB) - { + while ($i < $num && $i < $conf->global->MAILING_LIMIT_SENDBYWEB) { // Here code is common with same loop ino mailing-send.php $res = 1; $now = dol_now(); @@ -215,14 +211,18 @@ if (empty($reshook)) $substitutionarray['__UNSUBSCRIBE__'] = ''.$langs->trans("MailUnsubcribe").''; $onlinepaymentenabled = 0; - if (!empty($conf->paypal->enabled)) $onlinepaymentenabled++; - if (!empty($conf->paybox->enabled)) $onlinepaymentenabled++; - if (!empty($conf->stripe->enabled)) $onlinepaymentenabled++; - if ($onlinepaymentenabled && !empty($conf->global->PAYMENT_SECURITY_TOKEN)) - { + if (!empty($conf->paypal->enabled)) { + $onlinepaymentenabled++; + } + if (!empty($conf->paybox->enabled)) { + $onlinepaymentenabled++; + } + if (!empty($conf->stripe->enabled)) { + $onlinepaymentenabled++; + } + if ($onlinepaymentenabled && !empty($conf->global->PAYMENT_SECURITY_TOKEN)) { $substitutionarray['__SECUREKEYPAYMENT__'] = dol_hash($conf->global->PAYMENT_SECURITY_TOKEN, 2); - if (empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) - { + if (empty($conf->global->PAYMENT_SECURITY_TOKEN_UNIQUE)) { $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = dol_hash($conf->global->PAYMENT_SECURITY_TOKEN, 2); $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = dol_hash($conf->global->PAYMENT_SECURITY_TOKEN, 2); $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = dol_hash($conf->global->PAYMENT_SECURITY_TOKEN, 2); @@ -235,21 +235,32 @@ if (empty($reshook)) } } /* For backward compatibility, deprecated */ - if (!empty($conf->paypal->enabled) && !empty($conf->global->PAYPAL_SECURITY_TOKEN)) - { + if (!empty($conf->paypal->enabled) && !empty($conf->global->PAYPAL_SECURITY_TOKEN)) { $substitutionarray['__SECUREKEYPAYPAL__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN, 2); - if (empty($conf->global->PAYPAL_SECURITY_TOKEN_UNIQUE)) $substitutionarray['__SECUREKEYPAYPAL_MEMBER__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN, 2); - else $substitutionarray['__SECUREKEYPAYPAL_MEMBER__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN.'membersubscription'.$obj->source_id, 2); + if (empty($conf->global->PAYPAL_SECURITY_TOKEN_UNIQUE)) { + $substitutionarray['__SECUREKEYPAYPAL_MEMBER__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN, 2); + } else { + $substitutionarray['__SECUREKEYPAYPAL_MEMBER__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN.'membersubscription'.$obj->source_id, 2); + } - if (empty($conf->global->PAYPAL_SECURITY_TOKEN_UNIQUE)) $substitutionarray['__SECUREKEYPAYPAL_ORDER__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN, 2); - else $substitutionarray['__SECUREKEYPAYPAL_ORDER__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN.'order'.$obj->source_id, 2); + if (empty($conf->global->PAYPAL_SECURITY_TOKEN_UNIQUE)) { + $substitutionarray['__SECUREKEYPAYPAL_ORDER__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN, 2); + } else { + $substitutionarray['__SECUREKEYPAYPAL_ORDER__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN.'order'.$obj->source_id, 2); + } - if (empty($conf->global->PAYPAL_SECURITY_TOKEN_UNIQUE)) $substitutionarray['__SECUREKEYPAYPAL_INVOICE__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN, 2); - else $substitutionarray['__SECUREKEYPAYPAL_INVOICE__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN.'invoice'.$obj->source_id, 2); + if (empty($conf->global->PAYPAL_SECURITY_TOKEN_UNIQUE)) { + $substitutionarray['__SECUREKEYPAYPAL_INVOICE__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN, 2); + } else { + $substitutionarray['__SECUREKEYPAYPAL_INVOICE__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN.'invoice'.$obj->source_id, 2); + } - if (empty($conf->global->PAYPAL_SECURITY_TOKEN_UNIQUE)) $substitutionarray['__SECUREKEYPAYPAL_CONTRACTLINE__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN, 2); - else $substitutionarray['__SECUREKEYPAYPAL_CONTRACTLINE__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN.'contractline'.$obj->source_id, 2); + if (empty($conf->global->PAYPAL_SECURITY_TOKEN_UNIQUE)) { + $substitutionarray['__SECUREKEYPAYPAL_CONTRACTLINE__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN, 2); + } else { + $substitutionarray['__SECUREKEYPAYPAL_CONTRACTLINE__'] = dol_hash($conf->global->PAYPAL_SECURITY_TOKEN.'contractline'.$obj->source_id, 2); + } } //$substitutionisok=true; @@ -263,10 +274,8 @@ if (empty($reshook)) $arr_css = array(); $listofpaths = dol_dir_list($upload_dir, 'all', 0, '', '', 'name', SORT_ASC, 0); - if (count($listofpaths)) - { - foreach ($listofpaths as $key => $val) - { + if (count($listofpaths)) { + foreach ($listofpaths as $key => $val) { $arr_file[] = $listofpaths[$key]['fullname']; $arr_mime[] = dol_mimetype($listofpaths[$key]['name']); $arr_name[] = $listofpaths[$key]['name']; @@ -277,8 +286,7 @@ if (empty($reshook)) $trackid = 'emailing-'.$obj->fk_mailing.'-'.$obj->rowid; $mail = new CMailFile($newsubject, $sendto, $from, $newmessage, $arr_file, $arr_mime, $arr_name, '', '', 0, $msgishtml, $errorsto, $arr_css, $trackid, '', 'emailing'); - if ($mail->error) - { + if ($mail->error) { $res = 0; } /*if (! $substitutionisok) @@ -288,13 +296,11 @@ if (empty($reshook)) }*/ // Send mail - if ($res) - { + if ($res) { $res = $mail->sendfile(); } - if ($res) - { + if ($res) { // Mail successful $nbok++; @@ -303,19 +309,16 @@ if (empty($reshook)) $sql = "UPDATE ".MAIN_DB_PREFIX."mailing_cibles"; $sql .= " SET statut=1, date_envoi='".$db->idate($now)."' WHERE rowid=".$obj->rowid; $resql2 = $db->query($sql); - if (!$resql2) - { + if (!$resql2) { dol_print_error($db); } else { //if cheack read is use then update prospect contact status - if (strpos($message, '__CHECK_READ__') !== false) - { + if (strpos($message, '__CHECK_READ__') !== false) { //Update status communication of thirdparty prospect $sql = "UPDATE ".MAIN_DB_PREFIX."societe SET fk_stcomm=2 WHERE rowid IN (SELECT source_id FROM ".MAIN_DB_PREFIX."mailing_cibles WHERE rowid=".$obj->rowid.")"; dol_syslog("card.php: set prospect thirdparty status", LOG_DEBUG); $resql2 = $db->query($sql); - if (!$resql2) - { + if (!$resql2) { dol_print_error($db); } @@ -324,15 +327,13 @@ if (empty($reshook)) dol_syslog("card.php: set prospect contact status", LOG_DEBUG); $resql2 = $db->query($sql); - if (!$resql2) - { + if (!$resql2) { dol_print_error($db); } } } - if (!empty($conf->global->MAILING_DELAY)) - { + if (!empty($conf->global->MAILING_DELAY)) { dol_syslog("Wait a delay of MAILING_DELAY=".$conf->global->MAILING_DELAY); sleep($conf->global->MAILING_DELAY); } @@ -347,8 +348,7 @@ if (empty($reshook)) $sql = "UPDATE ".MAIN_DB_PREFIX."mailing_cibles"; $sql .= " SET statut=-1, error_text='".$db->escape($mail->error)."', date_envoi='".$db->idate($now)."' WHERE rowid=".$obj->rowid; $resql2 = $db->query($sql); - if (!$resql2) - { + if (!$resql2) { dol_print_error($db); } } @@ -360,14 +360,15 @@ if (empty($reshook)) } // Loop finished, set global statut of mail - if ($nbko > 0) - { + if ($nbko > 0) { $statut = 2; // Status 'sent partially' (because at least one error) - if ($nbok > 0) setEventMessages($langs->transnoentitiesnoconv("EMailSentToNRecipients", $nbok), null, 'mesgs'); - else setEventMessages($langs->transnoentitiesnoconv("EMailSentToNRecipients", $nbok), null, 'mesgs'); + if ($nbok > 0) { + setEventMessages($langs->transnoentitiesnoconv("EMailSentToNRecipients", $nbok), null, 'mesgs'); + } else { + setEventMessages($langs->transnoentitiesnoconv("EMailSentToNRecipients", $nbok), null, 'mesgs'); + } } else { - if ($nbok >= $num) - { + if ($nbok >= $num) { $statut = 3; // Send to everybody setEventMessages($langs->transnoentitiesnoconv("EMailSentToNRecipients", $nbok), null, 'mesgs'); } else { @@ -379,8 +380,7 @@ if (empty($reshook)) $sql = "UPDATE ".MAIN_DB_PREFIX."mailing SET statut=".$statut." WHERE rowid=".$object->id; dol_syslog("comm/mailing/card.php: update global status", LOG_DEBUG); $resql2 = $db->query($sql); - if (!$resql2) - { + if (!$resql2) { dol_print_error($db); } } else { @@ -393,24 +393,23 @@ if (empty($reshook)) } // Action send test emailing - if ($action == 'send' && empty($_POST["cancel"])) - { + if ($action == 'send' && empty($_POST["cancel"])) { $error = 0; $upload_dir = $conf->mailing->dir_output."/".get_exdir($object->id, 2, 0, 1, $object, 'mailing'); $object->sendto = $_POST["sendto"]; - if (!$object->sendto) - { + if (!$object->sendto) { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("MailTo")), null, 'errors'); $error++; } - if (!$error) - { + if (!$error) { // Is the message in html $msgishtml = -1; // Unknow by default - if (preg_match('/[\s\t]*/i', $object->body)) $msgishtml = 1; + if (preg_match('/[\s\t]*/i', $object->body)) { + $msgishtml = 1; + } // other are set at begin of page $object->substitutionarrayfortest['__EMAIL__'] = $object->sendto; @@ -427,15 +426,17 @@ if (empty($reshook)) $arr_css = array(); // Add CSS - if (!empty($object->bgcolor)) $arr_css['bgcolor'] = (preg_match('/^#/', $object->bgcolor) ? '' : '#').$object->bgcolor; - if (!empty($object->bgimage)) $arr_css['bgimage'] = $object->bgimage; + if (!empty($object->bgcolor)) { + $arr_css['bgcolor'] = (preg_match('/^#/', $object->bgcolor) ? '' : '#').$object->bgcolor; + } + if (!empty($object->bgimage)) { + $arr_css['bgimage'] = $object->bgimage; + } // Attached files $listofpaths = dol_dir_list($upload_dir, 'all', 0, '', '', 'name', SORT_ASC, 0); - if (count($listofpaths)) - { - foreach ($listofpaths as $key => $val) - { + if (count($listofpaths)) { + foreach ($listofpaths as $key => $val) { $arr_file[] = $listofpaths[$key]['fullname']; $arr_mime[] = dol_mimetype($listofpaths[$key]['name']); $arr_name[] = $listofpaths[$key]['name']; @@ -446,8 +447,7 @@ if (empty($reshook)) $mailfile = new CMailFile($tmpsujet, $object->sendto, $object->email_from, $tmpbody, $arr_file, $arr_mime, $arr_name, '', '', 0, $msgishtml, $object->email_errorsto, $arr_css, $trackid, '', 'emailing'); $result = $mailfile->sendfile(); - if ($result) - { + if ($result) { setEventMessages($langs->trans("MailSuccessfulySent", $mailfile->getValidAddress($object->email_from, 2), $mailfile->getValidAddress($object->sendto, 2)), null, 'mesgs'); $action = ''; } else { @@ -458,18 +458,17 @@ if (empty($reshook)) } // Action add emailing - if ($action == 'add') - { + if ($action == 'add') { $mesgs = array(); - $object->email_from = GETPOST("from", "none"); // Must allow 'name ' - $object->email_replyto = GETPOST("replyto", "none"); // Must allow 'name ' - $object->email_errorsto = GETPOST("errorsto", "none"); // Must allow 'name ' - $object->title = GETPOST("title"); - $object->sujet = GETPOST("sujet"); - $object->body = GETPOST("bodyemail", 'restricthtml'); - $object->bgcolor = GETPOST("bgcolor"); - $object->bgimage = GETPOST("bgimage"); + $object->email_from = (string) GETPOST("from", "none"); // Must allow 'name ' + $object->email_replyto = (string) GETPOST("replyto", "none"); // Must allow 'name ' + $object->email_errorsto = (string) GETPOST("errorsto", "none"); // Must allow 'name ' + $object->title = (string) GETPOST("title"); + $object->sujet = (string) GETPOST("sujet"); + $object->body = (string) GETPOST("bodyemail", 'restricthtml'); + $object->bgcolor = (string) GETPOST("bgcolor"); + $object->bgimage = (string) GETPOST("bgimage"); if (!$object->title) { $mesgs[] = $langs->trans("ErrorFieldRequired", $langs->transnoentities("MailTitle")); @@ -481,10 +480,8 @@ if (empty($reshook)) $mesgs[] = $langs->trans("ErrorFieldRequired", $langs->transnoentities("MailMessage")); } - if (!count($mesgs)) - { - if ($object->create($user) >= 0) - { + if (!count($mesgs)) { + if ($object->create($user) >= 0) { header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); exit; } @@ -496,24 +493,25 @@ if (empty($reshook)) } // Action update description of emailing - if ($action == 'settitle' || $action == 'setemail_from' || $action == 'setreplyto' || $action == 'setemail_errorsto') - { + if ($action == 'settitle' || $action == 'setemail_from' || $action == 'setreplyto' || $action == 'setemail_errorsto') { $upload_dir = $conf->mailing->dir_output."/".get_exdir($object->id, 2, 0, 1, $object, 'mailing'); - if ($action == 'settitle') $object->title = trim(GETPOST('title', 'alpha')); - elseif ($action == 'setemail_from') $object->email_from = trim(GETPOST('email_from', 'none')); // Must allow 'name ' - elseif ($action == 'setemail_replyto') $object->email_replyto = trim(GETPOST('email_replyto', 'none')); // Must allow 'name ' - elseif ($action == 'setemail_errorsto') $object->email_errorsto = trim(GETPOST('email_errorsto', 'none')); // Must allow 'name ' - elseif ($action == 'settitle' && empty($object->title)) { + if ($action == 'settitle') { + $object->title = trim(GETPOST('title', 'alpha')); + } elseif ($action == 'setemail_from') { + $object->email_from = trim(GETPOST('email_from', 'none')); // Must allow 'name ' + } elseif ($action == 'setemail_replyto') { + $object->email_replyto = trim(GETPOST('email_replyto', 'none')); // Must allow 'name ' + } elseif ($action == 'setemail_errorsto') { + $object->email_errorsto = trim(GETPOST('email_errorsto', 'none')); // Must allow 'name ' + } elseif ($action == 'settitle' && empty($object->title)) { $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentities("MailTitle")); } elseif ($action == 'setfrom' && empty($object->email_from)) { $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentities("MailFrom")); } - if (!$mesg) - { - if ($object->update($user) >= 0) - { + if (!$mesg) { + if ($object->update($user) >= 0) { header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); exit; } @@ -527,8 +525,7 @@ if (empty($reshook)) /* * Add file in email form */ - if (!empty($_POST['addfile'])) - { + if (!empty($_POST['addfile'])) { $upload_dir = $conf->mailing->dir_output."/".get_exdir($object->id, 2, 0, 1, $object, 'mailing'); require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; @@ -540,8 +537,7 @@ if (empty($reshook)) } // Action of file remove - if (!empty($_POST["removedfile"])) - { + if (!empty($_POST["removedfile"])) { $upload_dir = $conf->mailing->dir_output."/".get_exdir($object->id, 2, 0, 1, $object, 'mailing'); require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; @@ -552,20 +548,18 @@ if (empty($reshook)) } // Action of emailing update - if ($action == 'update' && empty($_POST["removedfile"]) && empty($_POST["cancel"])) - { + if ($action == 'update' && empty($_POST["removedfile"]) && empty($_POST["cancel"])) { require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; $isupload = 0; - if (!$isupload) - { + if (!$isupload) { $mesgs = array(); - $object->sujet = GETPOST("sujet"); - $object->body = GETPOST("bodyemail", 'restricthtml'); - $object->bgcolor = GETPOST("bgcolor"); - $object->bgimage = GETPOST("bgimage"); + $object->sujet = (string) GETPOST("sujet"); + $object->body = (string) GETPOST("bodyemail", 'restricthtml'); + $object->bgcolor = (string) GETPOST("bgcolor"); + $object->bgimage = (string) GETPOST("bgimage"); if (!$object->sujet) { $mesgs[] = $langs->trans("ErrorFieldRequired", $langs->transnoentities("MailTopic")); @@ -574,10 +568,8 @@ if (empty($reshook)) $mesgs[] = $langs->trans("ErrorFieldRequired", $langs->transnoentities("MailMessage")); } - if (!count($mesgs)) - { - if ($object->update($user) >= 0) - { + if (!count($mesgs)) { + if ($object->update($user) >= 0) { header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); exit; } @@ -592,10 +584,8 @@ if (empty($reshook)) } // Action of validation confirmation - if ($action == 'confirm_valid' && $confirm == 'yes') - { - if ($object->id > 0) - { + if ($action == 'confirm_valid' && $confirm == 'yes') { + if ($object->id > 0) { $object->valid($user); setEventMessages($langs->trans("MailingSuccessfullyValidated"), null, 'mesgs'); header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); @@ -606,13 +596,10 @@ if (empty($reshook)) } // Action of validation confirmation - if ($action == 'confirm_settodraft' && $confirm == 'yes') - { - if ($object->id > 0) - { + if ($action == 'confirm_settodraft' && $confirm == 'yes') { + if ($object->id > 0) { $result = $object->setStatut(0); - if ($result > 0) - { + if ($result > 0) { //setEventMessages($langs->trans("MailingSuccessfullyValidated"), null, 'mesgs'); header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); exit; @@ -625,20 +612,16 @@ if (empty($reshook)) } // Resend - if ($action == 'confirm_reset' && $confirm == 'yes') - { - if ($object->id > 0) - { + if ($action == 'confirm_reset' && $confirm == 'yes') { + if ($object->id > 0) { $db->begin(); $result = $object->valid($user); - if ($result > 0) - { + if ($result > 0) { $result = $object->reset_targets_status($user); } - if ($result > 0) - { + if ($result > 0) { $db->commit(); header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); exit; @@ -652,18 +635,15 @@ if (empty($reshook)) } // Action of delete confirmation - if ($action == 'confirm_delete' && $confirm == 'yes') - { - if ($object->delete($object->id)) - { + if ($action == 'confirm_delete' && $confirm == 'yes') { + if ($object->delete($object->id)) { $url = (!empty($urlfrom) ? $urlfrom : 'list.php'); header("Location: ".$url); exit; } } - if (!empty($_POST["cancel"])) - { + if (!empty($_POST["cancel"])) { $action = ''; } } @@ -677,24 +657,30 @@ $form = new Form($db); $htmlother = new FormOther($db); $help_url = 'EN:Module_EMailing|FR:Module_Mailing|ES:Módulo_Mailing'; -llxHeader('', $langs->trans("Mailing"), $help_url, '', 0, 0, +llxHeader( + '', + $langs->trans("Mailing"), + $help_url, + '', + 0, + 0, array( - '/includes/ace/src/ace.js', - '/includes/ace/src/ext-statusbar.js', - '/includes/ace/src/ext-language_tools.js', - //'/includes/ace/src/ext-chromevox.js' - ), array()); + '/includes/ace/src/ace.js', + '/includes/ace/src/ext-statusbar.js', + '/includes/ace/src/ext-language_tools.js', + //'/includes/ace/src/ext-chromevox.js' + ), + array() +); -if ($action == 'create') -{ +if ($action == 'create') { // EMailing in creation mode print '
'."\n"; print ''; print ''; $htmltext = ''.$langs->trans("FollowingConstantsWillBeSubstituted").':
'; - foreach ($object->substitutionarray as $key => $val) - { + foreach ($object->substitutionarray as $key => $val) { $htmltext .= $key.' = '.$langs->trans($val).'
'; } $htmltext .= '
'; @@ -718,8 +704,7 @@ if ($action == 'create') $parameters = array(); $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; - if (empty($reshook)) - { + if (empty($reshook)) { print $object->showOptionals($extrafields, 'edit'); } @@ -747,79 +732,80 @@ if ($action == 'create') print '
'; } else { - if ($object->id > 0) - { + if ($object->id > 0) { $upload_dir = $conf->mailing->dir_output."/".get_exdir($object->id, 2, 0, 1, $object, 'mailing'); $head = emailing_prepare_head($object); - // Confirmation back to draft - if ($action == 'settodraft') - { + if ($action == 'settodraft') { + // Confirmation back to draft print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id, $langs->trans("SetToDraft"), $langs->trans("ConfirmUnvalidateEmailing"), "confirm_settodraft", '', '', 1); - } - // Confirmation of mailing validation - if ($action == 'valid') - { + } elseif ($action == 'valid') { + // Confirmation of mailing validation print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id, $langs->trans("ValidMailing"), $langs->trans("ConfirmValidMailing"), "confirm_valid", '', '', 1); - } // Confirm reset - elseif ($action == 'reset') - { + } elseif ($action == 'reset') { + // Confirm reset print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id, $langs->trans("ResetMailing"), $langs->trans("ConfirmResetMailing", $object->ref), "confirm_reset", '', '', 2); - } // Confirm delete - elseif ($action == 'delete') - { + } elseif ($action == 'delete') { + // Confirm delete print $form->formconfirm($_SERVER["PHP_SELF"]."?id=".$object->id.(!empty($urlfrom) ? '&urlfrom='.urlencode($urlfrom) : ''), $langs->trans("DeleteAMailing"), $langs->trans("ConfirmDeleteMailing"), "confirm_delete", '', '', 1); } - - if ($action != 'edit' && $action != 'edithtml') - { + if ($action != 'edit' && $action != 'edithtml') { print dol_get_fiche_head($head, 'card', $langs->trans("Mailing"), -1, 'email'); /* * View mode mailing */ - if ($action == 'sendall') - { + if ($action == 'sendall') { // Define message to recommand from command line $sendingmode = $conf->global->EMAILING_MAIL_SENDMODE; - if (empty($sendingmode)) $sendingmode = $conf->global->MAIN_MAIL_SENDMODE; - if (empty($sendingmode)) $sendingmode = 'mail'; // If not defined, we use php mail function + if (empty($sendingmode)) { + $sendingmode = $conf->global->MAIN_MAIL_SENDMODE; + } + if (empty($sendingmode)) { + $sendingmode = 'mail'; // If not defined, we use php mail function + } // MAILING_NO_USING_PHPMAIL may be defined or not. // MAILING_LIMIT_SENDBYWEB is always defined to something != 0 (-1=forbidden). // MAILING_LIMIT_SENDBYCLI may be defined ot not (-1=forbidden, 0 or undefined=no limit). - if (!empty($conf->global->MAILING_NO_USING_PHPMAIL) && $sendingmode == 'mail') - { + if (!empty($conf->global->MAILING_NO_USING_PHPMAIL) && $sendingmode == 'mail') { // EMailing feature may be a spam problem, so when you host several users/instance, having this option may force each user to use their own SMTP agent. // You ensure that every user is using its own SMTP server when using the mass emailing module. $linktoadminemailbefore = ''; $linktoadminemailend = ''; setEventMessages($langs->trans("MailSendSetupIs", $listofmethods[$sendingmode]), null, 'warnings'); setEventMessages($langs->trans("MailSendSetupIs2", $linktoadminemailbefore, $linktoadminemailend, $langs->transnoentitiesnoconv("MAIN_MAIL_SENDMODE"), $listofmethods['smtps']), null, 'warnings'); - if (!empty($conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS)) setEventMessages($langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS), null, 'warnings'); + if (!empty($conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS)) { + setEventMessages($langs->trans("MailSendSetupIs3", $conf->global->MAILING_SMTP_SETUP_EMAILS_FOR_QUESTIONS), null, 'warnings'); + } $_GET["action"] = ''; - } elseif ($conf->global->MAILING_LIMIT_SENDBYWEB < 0) - { - if (!empty($conf->global->MAILING_LIMIT_WARNING_PHPMAIL) && $sendingmode == 'mail') setEventMessages($langs->transnoentitiesnoconv($conf->global->MAILING_LIMIT_WARNING_PHPMAIL), null, 'warnings'); - if (!empty($conf->global->MAILING_LIMIT_WARNING_NOPHPMAIL) && $sendingmode != 'mail') setEventMessages($langs->transnoentitiesnoconv($conf->global->MAILING_LIMIT_WARNING_NOPHPMAIL), null, 'warnings'); + } elseif ($conf->global->MAILING_LIMIT_SENDBYWEB < 0) { + if (!empty($conf->global->MAILING_LIMIT_WARNING_PHPMAIL) && $sendingmode == 'mail') { + setEventMessages($langs->transnoentitiesnoconv($conf->global->MAILING_LIMIT_WARNING_PHPMAIL), null, 'warnings'); + } + if (!empty($conf->global->MAILING_LIMIT_WARNING_NOPHPMAIL) && $sendingmode != 'mail') { + setEventMessages($langs->transnoentitiesnoconv($conf->global->MAILING_LIMIT_WARNING_NOPHPMAIL), null, 'warnings'); + } // The feature is forbidden from GUI, we show just message to use from command line. setEventMessages($langs->trans("MailingNeedCommand"), null, 'warnings'); setEventMessages('', null, 'warnings'); - if ($conf->file->mailing_limit_sendbyweb != '-1') // MAILING_LIMIT_SENDBYWEB was set to -1 in database, but it is allowed ot increase it. - { + if ($conf->file->mailing_limit_sendbyweb != '-1') { // MAILING_LIMIT_SENDBYWEB was set to -1 in database, but it is allowed ot increase it. setEventMessages($langs->trans("MailingNeedCommand2"), null, 'warnings'); // You can send online with constant... } $_GET["action"] = ''; } else { - if (!empty($conf->global->MAILING_LIMIT_WARNING_PHPMAIL) && $sendingmode == 'mail') setEventMessages($langs->transnoentitiesnoconv($conf->global->MAILING_LIMIT_WARNING_PHPMAIL), null, 'warnings'); - if (!empty($conf->global->MAILING_LIMIT_WARNING_NOPHPMAIL) && $sendingmode != 'mail') setEventMessages($langs->transnoentitiesnoconv($conf->global->MAILING_LIMIT_WARNING_NOPHPMAIL), null, 'warnings'); + if (!empty($conf->global->MAILING_LIMIT_WARNING_PHPMAIL) && $sendingmode == 'mail') { + setEventMessages($langs->transnoentitiesnoconv($conf->global->MAILING_LIMIT_WARNING_PHPMAIL), null, 'warnings'); + } + if (!empty($conf->global->MAILING_LIMIT_WARNING_NOPHPMAIL) && $sendingmode != 'mail') { + setEventMessages($langs->transnoentitiesnoconv($conf->global->MAILING_LIMIT_WARNING_NOPHPMAIL), null, 'warnings'); + } $text = ''; - if ($conf->global->MAILING_LIMIT_SENDBYCLI >= 0) - { + if ($conf->global->MAILING_LIMIT_SENDBYCLI >= 0) { $text .= $langs->trans("MailingNeedCommand"); $text .= '
'; $text .= '

'; @@ -834,13 +820,14 @@ if ($action == 'create') $morehtmlright = ''; $nbtry = $nbok = 0; - if ($object->statut == 2 || $object->statut == 3) - { + if ($object->statut == 2 || $object->statut == 3) { $nbtry = $object->countNbOfTargets('alreadysent'); $nbko = $object->countNbOfTargets('alreadysentko'); $morehtmlright .= ' ('.$nbtry.'/'.$object->nbemail; - if ($nbko) $morehtmlright .= ' - '.$nbko.' '.$langs->trans("Error"); + if ($nbko) { + $morehtmlright .= ' - '.$nbko.' '.$langs->trans("Error"); + } $morehtmlright .= ')   '; } @@ -894,21 +881,19 @@ if ($action == 'create') print $langs->trans("TotalNbOfDistinctRecipients"); print ''; $nbemail = ($object->nbemail ? $object->nbemail : 0); - if (is_numeric($nbemail)) - { + if (is_numeric($nbemail)) { $text = ''; - if ((!empty($conf->global->MAILING_LIMIT_SENDBYWEB) && $conf->global->MAILING_LIMIT_SENDBYWEB < $nbemail) && ($object->statut == 1 || ($object->statut == 2 && $nbtry < $nbemail))) - { - if ($conf->global->MAILING_LIMIT_SENDBYWEB > 0) - { + if ((!empty($conf->global->MAILING_LIMIT_SENDBYWEB) && $conf->global->MAILING_LIMIT_SENDBYWEB < $nbemail) && ($object->statut == 1 || ($object->statut == 2 && $nbtry < $nbemail))) { + if ($conf->global->MAILING_LIMIT_SENDBYWEB > 0) { $text .= $langs->trans('LimitSendingEmailing', $conf->global->MAILING_LIMIT_SENDBYWEB); } else { $text .= $langs->trans('SendingFromWebInterfaceIsNotAllowed'); } } - if (empty($nbemail)) $nbemail .= ' '.img_warning('').' '.$langs->trans("NoTargetYet").''; - if ($text) - { + if (empty($nbemail)) { + $nbemail .= ' '.img_warning('').' '.$langs->trans("NoTargetYet").''; + } + if ($text) { print $form->textwithpicto($nbemail, $text, 1, 'warning'); } else { print $nbemail; @@ -927,8 +912,7 @@ if ($action == 'create') // Clone confirmation - if ($action == 'clone') - { + if ($action == 'clone') { // Create an array for form $formquestion = array( 'text' => $langs->trans("ConfirmClone"), @@ -943,81 +927,67 @@ if ($action == 'create') * Actions Buttons */ - if (GETPOST('cancel', 'alpha') || $confirm == 'no' || $action == '' || in_array($action, array('settodraft', 'valid', 'delete', 'sendall', 'clone', 'test'))) - { + if (GETPOST('cancel', 'alpha') || $confirm == 'no' || $action == '' || in_array($action, array('settodraft', 'valid', 'delete', 'sendall', 'clone', 'test'))) { print "\n\n
\n"; - if (($object->statut == 1) && ($user->rights->mailing->valider || $object->fk_user_valid == $user->id)) - { + if (($object->statut == 1) && ($user->rights->mailing->valider || $object->fk_user_valid == $user->id)) { print ''.$langs->trans("SetToDraft").''; } - if (($object->statut == 0 || $object->statut == 1 || $object->statut == 2) && $user->rights->mailing->creer) - { - if (!empty($conf->fckeditor->enabled) && !empty($conf->global->FCKEDITOR_ENABLE_MAILING)) - { + if (($object->statut == 0 || $object->statut == 1 || $object->statut == 2) && $user->rights->mailing->creer) { + if (!empty($conf->fckeditor->enabled) && !empty($conf->global->FCKEDITOR_ENABLE_MAILING)) { print ''.$langs->trans("EditWithEditor").''; } else { print ''.$langs->trans("EditWithTextEditor").''; } - if (!empty($conf->use_javascript_ajax)) print ''.$langs->trans("EditHTMLSource").''; + if (!empty($conf->use_javascript_ajax)) { + print ''.$langs->trans("EditHTMLSource").''; + } } //print ''.$langs->trans("PreviewMailing").''; - if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !$user->rights->mailing->mailing_advance->send) - { + if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !$user->rights->mailing->mailing_advance->send) { print ''.$langs->trans("TestMailing").''; } else { print ''.$langs->trans("TestMailing").''; } - if ($object->statut == 0) - { - if ($object->nbemail <= 0) - { + if ($object->statut == 0) { + if ($object->nbemail <= 0) { print ''.$langs->trans("ValidMailing").''; - } elseif (empty($user->rights->mailing->valider)) - { + } elseif (empty($user->rights->mailing->valider)) { print ''.$langs->trans("ValidMailing").''; } else { print ''.$langs->trans("ValidMailing").''; } } - if (($object->statut == 1 || $object->statut == 2) && $object->nbemail > 0 && $user->rights->mailing->valider) - { - if ($conf->global->MAILING_LIMIT_SENDBYWEB < 0) - { + if (($object->statut == 1 || $object->statut == 2) && $object->nbemail > 0 && $user->rights->mailing->valider) { + if ($conf->global->MAILING_LIMIT_SENDBYWEB < 0) { print ''.$langs->trans("SendMailing").''; - } elseif (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !$user->rights->mailing->mailing_advance->send) - { + } elseif (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !$user->rights->mailing->mailing_advance->send) { print ''.$langs->trans("SendMailing").''; } else { print ''.$langs->trans("SendMailing").''; } } - if ($user->rights->mailing->creer) - { + if ($user->rights->mailing->creer) { print ''.$langs->trans("ToClone").''; } - if (($object->statut == 2 || $object->statut == 3) && $user->rights->mailing->valider) - { - if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !$user->rights->mailing->mailing_advance->send) - { + if (($object->statut == 2 || $object->statut == 3) && $user->rights->mailing->valider) { + if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !$user->rights->mailing->mailing_advance->send) { print ''.$langs->trans("ResetMailing").''; } else { print ''.$langs->trans("ResetMailing").''; } } - if (($object->statut <= 1 && $user->rights->mailing->creer) || $user->rights->mailing->supprimer) - { - if ($object->statut > 0 && (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !$user->rights->mailing->mailing_advance->delete)) - { + if (($object->statut <= 1 && $user->rights->mailing->creer) || $user->rights->mailing->supprimer) { + if ($object->statut > 0 && (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !$user->rights->mailing->mailing_advance->delete)) { print ''.$langs->trans("DeleteMailing").''; } else { print ''.$langs->trans("DeleteMailing").''; @@ -1028,8 +998,7 @@ if ($action == 'create') } // Display of the TEST form - if ($action == 'test') - { + if ($action == 'test') { print '
'; print load_fiche_titre($langs->trans("TestMailing")); @@ -1071,8 +1040,7 @@ if ($action == 'create') $htmltext = ''.$langs->trans("FollowingConstantsWillBeSubstituted").':
'; - foreach ($object->substitutionarray as $key => $val) - { + foreach ($object->substitutionarray as $key => $val) { $htmltext .= $key.' = '.$langs->trans($val).'
'; } $htmltext .= '
'; @@ -1091,10 +1059,8 @@ if ($action == 'create') print ''.$langs->trans("MailFile").''; // List of files $listofpaths = dol_dir_list($upload_dir, 'all', 0, '', '', 'name', SORT_ASC, 0); - if (count($listofpaths)) - { - foreach ($listofpaths as $key => $val) - { + if (count($listofpaths)) { + foreach ($listofpaths as $key => $val) { print img_mime($listofpaths[$key]['name']).' '.$listofpaths[$key]['name']; print '
'; } @@ -1105,21 +1071,22 @@ if ($action == 'create') // Background color /*print ''.$langs->trans("BackgroundColorByDefault").''; - print $htmlother->selectColor($object->bgcolor,'bgcolor','',0); - print '';*/ + print $htmlother->selectColor($object->bgcolor,'bgcolor','',0); + print '';*/ print ''; // Message print '
'; - if (empty($object->bgcolor) || strtolower($object->bgcolor) == 'ffffff') // CKEditor does not apply the color of the div into its content area - { + if (empty($object->bgcolor) || strtolower($object->bgcolor) == 'ffffff') { // CKEditor does not apply the color of the div into its content area $readonly = 1; // wysiwyg editor require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; $doleditor = new DolEditor('bodyemail', $object->body, '', 600, 'dolibarr_mailings', '', false, true, empty($conf->global->FCKEDITOR_ENABLE_MAILING) ? 0 : 1, 20, '90%', $readonly); $doleditor->Create(); - } else print dol_htmlentitiesbr($object->body); + } else { + print dol_htmlentitiesbr($object->body); + } print '
'; print dol_get_fiche_end(); @@ -1133,7 +1100,9 @@ if ($action == 'create') $linkback = ''.$langs->trans("BackToList").''; $morehtmlright = ''; - if ($object->statut == 2) $morehtmlright .= ' ('.$object->countNbOfTargets('alreadysent').'/'.$object->nbemail.') '; + if ($object->statut == 2) { + $morehtmlright .= ' ('.$object->countNbOfTargets('alreadysent').'/'.$object->nbemail.') '; + } dol_banner_tab($object, 'id', $linkback, 1, 'rowid', 'ref', '', '', 0, '', $morehtmlright); @@ -1161,21 +1130,19 @@ if ($action == 'create') print $langs->trans("TotalNbOfDistinctRecipients"); print ''; $nbemail = ($object->nbemail ? $object->nbemail : 0); - if (is_numeric($nbemail)) - { + if (is_numeric($nbemail)) { $text = ''; - if ((!empty($conf->global->MAILING_LIMIT_SENDBYWEB) && $conf->global->MAILING_LIMIT_SENDBYWEB < $nbemail) && ($object->statut == 1 || $object->statut == 2)) - { - if ($conf->global->MAILING_LIMIT_SENDBYWEB > 0) - { + if ((!empty($conf->global->MAILING_LIMIT_SENDBYWEB) && $conf->global->MAILING_LIMIT_SENDBYWEB < $nbemail) && ($object->statut == 1 || $object->statut == 2)) { + if ($conf->global->MAILING_LIMIT_SENDBYWEB > 0) { $text .= $langs->trans('LimitSendingEmailing', $conf->global->MAILING_LIMIT_SENDBYWEB); } else { $text .= $langs->trans('SendingFromWebInterfaceIsNotAllowed'); } } - if (empty($nbemail)) $nbemail .= ' '.img_warning('').' '.$langs->trans("NoTargetYet").''; - if ($text) - { + if (empty($nbemail)) { + $nbemail .= ' '.img_warning('').' '.$langs->trans("NoTargetYet").''; + } + if ($text) { print $form->textwithpicto($nbemail, $text, 1, 'warning'); } else { print $nbemail; @@ -1187,8 +1154,7 @@ if ($action == 'create') $parameters = array(); $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; - if (empty($reshook)) - { + if (empty($reshook)) { print $object->showOptionals($extrafields, 'edit', $parameters); } @@ -1207,8 +1173,7 @@ if ($action == 'create') print ''; $htmltext = ''.$langs->trans("FollowingConstantsWillBeSubstituted").':
'; - foreach ($object->substitutionarray as $key => $val) - { + foreach ($object->substitutionarray as $key => $val) { $htmltext .= $key.' = '.$langs->trans($val).'
'; } $htmltext .= '
'; @@ -1242,10 +1207,8 @@ if ($action == 'create') $out .= ' });'; $out .= '})'; $out .= ''."\n"; - if (count($listofpaths)) - { - foreach ($listofpaths as $key => $val) - { + if (count($listofpaths)) { + foreach ($listofpaths as $key => $val) { $out .= '
'; $out .= img_mime($listofpaths[$key]['name']).' '.$listofpaths[$key]['name']; $out .= ' '; @@ -1271,15 +1234,13 @@ if ($action == 'create') // Message print '
'; - if ($action == 'edit') - { + if ($action == 'edit') { // wysiwyg editor require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; $doleditor = new DolEditor('bodyemail', $object->body, '', 600, 'dolibarr_mailings', '', true, true, $conf->global->FCKEDITOR_ENABLE_MAILING, 20, '90%'); $doleditor->Create(); } - if ($action == 'edithtml') - { + if ($action == 'edithtml') { // HTML source editor require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; $doleditor = new DolEditor('bodyemail', $object->body, '', 600, 'dolibarr_mailings', '', true, true, 'ace', 20, '90%'); From 391e643eb7381cece0612ed406f490f5ba0698d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Fri, 27 Nov 2020 20:33:40 +0100 Subject: [PATCH 055/743] doxygen --- htdocs/comm/mailing/class/mailing.class.php | 20 ++++++++++++++++++-- htdocs/comm/mailing/info.php | 19 ++++++++----------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/htdocs/comm/mailing/class/mailing.class.php b/htdocs/comm/mailing/class/mailing.class.php index 2f2b16bd119..b93030d0262 100644 --- a/htdocs/comm/mailing/class/mailing.class.php +++ b/htdocs/comm/mailing/class/mailing.class.php @@ -118,36 +118,48 @@ class Mailing extends CommonObject /** * @var int id of user create - * @deprecated */ public $user_creation; /** * @var int id of user create + * @deprecated */ public $user_creat; /** * @var int id of user validate - * @deprecated */ public $user_validation; /** * @var int id of user validate + * @deprecated */ public $user_valid; /** * @var integer|string date_creation + * @deprecated */ public $date_creat; + /** + * @var integer|string date_creation + */ + public $date_creation; + /** * @var int date validate + * @deprecated */ public $date_valid; + /** + * @var int date validate + */ + public $date_validation; + /** * @var array extraparams */ @@ -314,10 +326,14 @@ class Mailing extends CommonObject $this->email_errorsto = $obj->email_errorsto; $this->user_creat = $obj->fk_user_creat; + $this->user_creation = $obj->fk_user_creat; $this->user_valid = $obj->fk_user_valid; + $this->user_validation = $obj->fk_user_valid; $this->date_creat = $this->db->jdate($obj->date_creat); + $this->date_creation = $this->db->jdate($obj->date_creat); $this->date_valid = $this->db->jdate($obj->date_valid); + $this->date_validation = $this->db->jdate($obj->date_valid); $this->date_envoi = $this->db->jdate($obj->date_envoi); $this->extraparams = (array) json_decode($obj->extraparams, true); diff --git a/htdocs/comm/mailing/info.php b/htdocs/comm/mailing/info.php index 199a5caad8b..4bd39309701 100644 --- a/htdocs/comm/mailing/info.php +++ b/htdocs/comm/mailing/info.php @@ -33,8 +33,9 @@ $id = GETPOST('id', 'int'); $langs->load("mails"); // Security check -if (!$user->rights->mailing->lire || $user->socid > 0) -accessforbidden(); +if (!$user->rights->mailing->lire || $user->socid > 0) { + accessforbidden(); +} @@ -48,8 +49,7 @@ $form = new Form($db); $object = new Mailing($db); -if ($object->fetch($id) >= 0) -{ +if ($object->fetch($id) >= 0) { $head = emailing_prepare_head($object); print dol_get_fiche_head($head, 'info', $langs->trans("Mailing"), -1, 'email'); @@ -58,13 +58,14 @@ if ($object->fetch($id) >= 0) $morehtmlright = ''; $nbtry = $nbok = 0; - if ($object->statut == 2 || $object->statut == 3) - { + if ($object->statut == 2 || $object->statut == 3) { $nbtry = $object->countNbOfTargets('alreadysent'); $nbko = $object->countNbOfTargets('alreadysentko'); $morehtmlright .= ' ('.$nbtry.'/'.$object->nbemail; - if ($nbko) $morehtmlright .= ' - '.$nbko.' '.$langs->trans("Error"); + if ($nbko) { + $morehtmlright .= ' - '.$nbko.' '.$langs->trans("Error"); + } $morehtmlright .= ')   '; } @@ -73,10 +74,6 @@ if ($object->fetch($id) >= 0) print '

'; //print '
'; - $object->user_creation = $object->user_creat; - $object->date_creation = $object->date_creat; - $object->user_validation = $object->user_valid; - $object->date_validation = $object->date_valid; dol_print_object_info($object, 0); //print '
'; From f801dd7d662cc08b5928e258d612fe600e39cc94 Mon Sep 17 00:00:00 2001 From: Christian Foellmann Date: Mon, 30 Nov 2020 14:54:44 +0100 Subject: [PATCH 056/743] PR toggle FCKeditor on public/private notes --- htdocs/admin/fckeditor.php | 58 ++++++++++++++++-------------- htdocs/comm/propal/card.php | 4 +-- htdocs/commande/card.php | 4 +-- htdocs/compta/deplacement/card.php | 8 ++--- htdocs/compta/facture/card-rec.php | 4 +-- htdocs/compta/facture/card.php | 4 +-- htdocs/contact/card.php | 4 +-- htdocs/contrat/card.php | 4 +-- htdocs/don/card.php | 4 +-- htdocs/expedition/card.php | 4 +-- htdocs/expensereport/card.php | 4 +-- htdocs/fichinter/card.php | 4 +-- htdocs/fourn/commande/card.php | 4 +-- htdocs/fourn/facture/card.php | 4 +-- htdocs/loan/card.php | 4 +-- htdocs/reception/card.php | 4 +-- 16 files changed, 64 insertions(+), 58 deletions(-) diff --git a/htdocs/admin/fckeditor.php b/htdocs/admin/fckeditor.php index bc6b0dc33ac..6523ec833b3 100644 --- a/htdocs/admin/fckeditor.php +++ b/htdocs/admin/fckeditor.php @@ -21,7 +21,7 @@ /** * \file htdocs/admin/fckeditor.php * \ingroup fckeditor - * \brief Page d'activation du module FCKeditor dans les autres modules + * \brief Activation page for the FCKeditor module in the other modules */ require '../main.inc.php'; @@ -43,35 +43,41 @@ $mode = GETPOST('mode') ?GETPOST('mode', 'alpha') : 'dolibarr_notes'; if (!$user->admin) accessforbidden(); -// Constante et traduction de la description du module +// Constant and translation of the module description $modules = array( -'SOCIETE' => 'FCKeditorForCompany', -'PRODUCTDESC' => 'FCKeditorForProduct', -'DETAILS' => 'FCKeditorForProductDetails', -'USERSIGN' => 'FCKeditorForUserSignature', -'MAILING' => 'FCKeditorForMailing', -'MAIL' => 'FCKeditorForMail', -'TICKET' => 'FCKeditorForTicket' + 'SOCIETE' => 'FCKeditorForCompany', + 'PRODUCTDESC' => 'FCKeditorForProduct', + 'DETAILS' => 'FCKeditorForProductDetails', + 'USERSIGN' => 'FCKeditorForUserSignature', + 'MAILING' => 'FCKeditorForMailing', + 'MAIL' => 'FCKeditorForMail', + 'TICKET' => 'FCKeditorForTicket', + 'NOTE_PUBLIC' => 'FCKeditorForNotePublic', + 'NOTE_PRIVATE' => 'FCKeditorForNotePrivate', ); -// Conditions pour que l'option soit proposee +// Conditions for the option to be offered $conditions = array( -'SOCIETE' => 1, -'PRODUCTDESC' => (!empty($conf->product->enabled) || !empty($conf->service->enabled)), -'DETAILS' => (!empty($conf->facture->enabled) || !empty($conf->propal->enabled) || !empty($conf->commande->enabled) || !empty($conf->supplier_proposal->enabled) || !empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)), -'USERSIGN' => 1, -'MAILING' => !empty($conf->mailing->enabled), -'MAIL' => (!empty($conf->facture->enabled) || !empty($conf->propal->enabled) || !empty($conf->commande->enabled)), -'TICKET' => !empty($conf->ticket->enabled) + 'SOCIETE' => 1, + 'PRODUCTDESC' => (!empty($conf->product->enabled) || !empty($conf->service->enabled)), + 'DETAILS' => (!empty($conf->facture->enabled) || !empty($conf->propal->enabled) || !empty($conf->commande->enabled) || !empty($conf->supplier_proposal->enabled) || !empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)), + 'USERSIGN' => 1, + 'MAILING' => !empty($conf->mailing->enabled), + 'MAIL' => (!empty($conf->facture->enabled) || !empty($conf->propal->enabled) || !empty($conf->commande->enabled)), + 'TICKET' => !empty($conf->ticket->enabled), + 'NOTE_PUBLIC' => 1, + 'NOTE_PRIVATE' => 1, ); // Picto $picto = array( -'SOCIETE' => 'generic', -'PRODUCTDESC' => 'product', -'DETAILS' => 'product', -'USERSIGN' => 'user', -'MAILING' => 'email', -'MAIL' => 'email', -'TICKET' => 'ticket' + 'SOCIETE' => 'generic', + 'PRODUCTDESC' => 'product', + 'DETAILS' => 'product', + 'USERSIGN' => 'user', + 'MAILING' => 'email', + 'MAIL' => 'email', + 'TICKET' => 'ticket', + 'NOTE_PUBLIC' => 'generic', + 'NOTE_PRIVATE' => 'generic', ); @@ -85,7 +91,7 @@ foreach ($modules as $const => $desc) if ($action == 'activate_'.strtolower($const)) { dolibarr_set_const($db, "FCKEDITOR_ENABLE_".$const, "1", 'chaine', 0, '', $conf->entity); - // Si fckeditor est active dans la description produit/service, on l'active dans les formulaires + // If fckeditor is active in the product/service description, it is activated in the forms if ($const == 'PRODUCTDESC' && !empty($conf->global->PRODUIT_DESC_IN_FORM)) { dolibarr_set_const($db, "FCKEDITOR_ENABLE_DETAILS", "1", 'chaine', 0, '', $conf->entity); @@ -154,7 +160,7 @@ if (empty($conf->use_javascript_ajax)) // Modules foreach ($modules as $const => $desc) { - // Si condition non remplie, on ne propose pas l'option + // If this condition is not met, the option is not offered if (!$conditions[$const]) continue; print ''; diff --git a/htdocs/comm/propal/card.php b/htdocs/comm/propal/card.php index 4742aebfde0..464f6aca20a 100644 --- a/htdocs/comm/propal/card.php +++ b/htdocs/comm/propal/card.php @@ -1659,7 +1659,7 @@ if ($action == 'create') print ''.$langs->trans('NotePublic').''; print ''; $note_public = $object->getDefaultCreateValueFor('note_public', (is_object($objectsrc) ? $objectsrc->note_public : null)); - $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); // Private note @@ -1669,7 +1669,7 @@ if ($action == 'create') print ''.$langs->trans('NotePrivate').''; print ''; $note_private = $object->getDefaultCreateValueFor('note_private', ((!empty($origin) && !empty($originid) && is_object($objectsrc)) ? $objectsrc->note_private : null)); - $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); // print ' print ''; diff --git a/htdocs/commande/card.php b/htdocs/commande/card.php index 16e53e0a23b..f0a6d601a42 100644 --- a/htdocs/commande/card.php +++ b/htdocs/commande/card.php @@ -1734,7 +1734,7 @@ if ($action == 'create' && $usercancreate) print ''.$langs->trans('NotePublic').''; print ''; - $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); // print ''; print ''; @@ -1745,7 +1745,7 @@ if ($action == 'create' && $usercancreate) print ''.$langs->trans('NotePrivate').''; print ''; - $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); // print ''; print ''; diff --git a/htdocs/compta/deplacement/card.php b/htdocs/compta/deplacement/card.php index 0a18d68b5db..f1e34e99985 100644 --- a/htdocs/compta/deplacement/card.php +++ b/htdocs/compta/deplacement/card.php @@ -252,7 +252,7 @@ if ($action == 'create') print ''.$langs->trans('NotePublic').''; print ''; - $doleditor = new DolEditor('note_public', GETPOST('note_public', 'restricthtml'), '', 200, 'dolibarr_notes', 'In', false, true, true, ROWS_8, '90%'); + $doleditor = new DolEditor('note_public', GETPOST('note_public', 'restricthtml'), '', 200, 'dolibarr_notes', 'In', false, true, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_8, '90%'); print $doleditor->Create(1); print ''; @@ -264,7 +264,7 @@ if ($action == 'create') print ''.$langs->trans('NotePrivate').''; print ''; - $doleditor = new DolEditor('note_private', GETPOST('note_private', 'restricthtml'), '', 200, 'dolibarr_notes', 'In', false, true, true, ROWS_8, '90%'); + $doleditor = new DolEditor('note_private', GETPOST('note_private', 'restricthtml'), '', 200, 'dolibarr_notes', 'In', false, true, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_8, '90%'); print $doleditor->Create(1); print ''; @@ -349,7 +349,7 @@ if ($action == 'create') print ''.$langs->trans("NotePublic").''; print ''; - $doleditor = new DolEditor('note_public', $object->note_public, '', 200, 'dolibarr_notes', 'In', false, true, true, ROWS_8, '90%'); + $doleditor = new DolEditor('note_public', $object->note_public, '', 200, 'dolibarr_notes', 'In', false, true, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_8, '90%'); print $doleditor->Create(1); print ""; @@ -360,7 +360,7 @@ if ($action == 'create') print ''.$langs->trans("NotePrivate").''; print ''; - $doleditor = new DolEditor('note_private', $object->note_private, '', 200, 'dolibarr_notes', 'In', false, true, true, ROWS_8, '90%'); + $doleditor = new DolEditor('note_private', $object->note_private, '', 200, 'dolibarr_notes', 'In', false, true, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_8, '90%'); print $doleditor->Create(1); print ""; diff --git a/htdocs/compta/facture/card-rec.php b/htdocs/compta/facture/card-rec.php index 4f754fbd173..4c25d3df652 100644 --- a/htdocs/compta/facture/card-rec.php +++ b/htdocs/compta/facture/card-rec.php @@ -998,7 +998,7 @@ if ($action == 'create') print $form->textwithpicto($langs->trans('NotePublic'), $htmltext, 1, 'help', '', 0, 2, 'notepublic'); print ''; print ''; - $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); // Private note @@ -1009,7 +1009,7 @@ if ($action == 'create') print $form->textwithpicto($langs->trans('NotePrivate'), $htmltext, 1, 'help', '', 0, 2, 'noteprivate'); print ''; print ''; - $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); // print ' print ''; diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index def74e173a3..37f236e875a 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -3534,7 +3534,7 @@ if ($action == 'create') print $form->textwithpicto($langs->trans('NotePublic'), $htmltext); print ''; print ''; - $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); // Private note @@ -3545,7 +3545,7 @@ if ($action == 'create') print $form->textwithpicto($langs->trans('NotePrivate'), $htmltext); print ''; print ''; - $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); // print ' print ''; diff --git a/htdocs/contact/card.php b/htdocs/contact/card.php index 752bdc4d642..23297438e19 100644 --- a/htdocs/contact/card.php +++ b/htdocs/contact/card.php @@ -1129,13 +1129,13 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) // Note Public print ''; - $doleditor = new DolEditor('note_public', $object->note_public, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $object->note_public, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; // Note Private print ''; - $doleditor = new DolEditor('note_private', $object->note_private, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $object->note_private, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php index a92ac8a1e73..dcca2fe816c 100644 --- a/htdocs/contrat/card.php +++ b/htdocs/contrat/card.php @@ -1174,14 +1174,14 @@ if ($action == 'create') } print ''.$langs->trans("NotePublic").''; - $doleditor = new DolEditor('note_public', $note_public, '', '100', 'dolibarr_notes', 'In', 1, true, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $note_public, '', '100', 'dolibarr_notes', 'In', 1, true, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; if (empty($user->socid)) { print ''.$langs->trans("NotePrivate").''; - $doleditor = new DolEditor('note_private', $note_private, '', '100', 'dolibarr_notes', 'In', 1, true, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $note_private, '', '100', 'dolibarr_notes', 'In', 1, true, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; } diff --git a/htdocs/don/card.php b/htdocs/don/card.php index c55751e913f..cd3175cdce0 100644 --- a/htdocs/don/card.php +++ b/htdocs/don/card.php @@ -443,7 +443,7 @@ if ($action == 'create') print ''.$langs->trans('NotePublic').''; print ''; - $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; @@ -453,7 +453,7 @@ if ($action == 'create') print ''.$langs->trans('NotePrivate').''; print ''; - $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; } diff --git a/htdocs/expedition/card.php b/htdocs/expedition/card.php index fd1e38e2438..a0541120ec6 100644 --- a/htdocs/expedition/card.php +++ b/htdocs/expedition/card.php @@ -942,7 +942,7 @@ if ($action == 'create') // Note Public print ''.$langs->trans("NotePublic").''; print ''; - $doleditor = new DolEditor('note_public', $object->note_public, '', 60, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $object->note_public, '', 60, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ""; @@ -951,7 +951,7 @@ if ($action == 'create') { print ''.$langs->trans("NotePrivate").''; print ''; - $doleditor = new DolEditor('note_private', $object->note_private, '', 60, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $object->note_private, '', 60, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ""; } diff --git a/htdocs/expensereport/card.php b/htdocs/expensereport/card.php index 83e1173fcb4..71f190a08f9 100644 --- a/htdocs/expensereport/card.php +++ b/htdocs/expensereport/card.php @@ -1457,7 +1457,7 @@ if ($action == 'create') print ''.$langs->trans('NotePublic').''; print ''; - $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; @@ -1467,7 +1467,7 @@ if ($action == 'create') print ''.$langs->trans('NotePrivate').''; print ''; - $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; } diff --git a/htdocs/fichinter/card.php b/htdocs/fichinter/card.php index 07c0146c2a8..f96bab63e2f 100644 --- a/htdocs/fichinter/card.php +++ b/htdocs/fichinter/card.php @@ -941,7 +941,7 @@ if ($action == 'create') print ''; print ''.$langs->trans('NotePublic').''; print ''; - $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $note_public, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); //print ''; print ''; @@ -952,7 +952,7 @@ if ($action == 'create') print ''; print ''.$langs->trans('NotePrivate').''; print ''; - $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $note_private, '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); //print ''; print ''; diff --git a/htdocs/fourn/commande/card.php b/htdocs/fourn/commande/card.php index cf6543c96e0..470a5cf0b0c 100644 --- a/htdocs/fourn/commande/card.php +++ b/htdocs/fourn/commande/card.php @@ -1708,7 +1708,7 @@ if ($action == 'create') print ''.$langs->trans('NotePublic').''; print ''; - $doleditor = new DolEditor('note_public', isset($note_public) ? $note_public : GETPOST('note_public', 'restricthtml'), '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', isset($note_public) ? $note_public : GETPOST('note_public', 'restricthtml'), '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; //print ''; @@ -1716,7 +1716,7 @@ if ($action == 'create') print ''.$langs->trans('NotePrivate').''; print ''; - $doleditor = new DolEditor('note_private', isset($note_private) ? $note_private : GETPOST('note_private', 'restricthtml'), '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', isset($note_private) ? $note_private : GETPOST('note_private', 'restricthtml'), '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; //print ''; diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index b439e5cad45..d9a8be9903c 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -2109,7 +2109,7 @@ if ($action == 'create') // Public note print ''.$langs->trans('NotePublic').''; print ''; - $doleditor = new DolEditor('note_public', (GETPOSTISSET('note_public') ?GETPOST('note_public', 'restricthtml') : $note_public), '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', (GETPOSTISSET('note_public') ?GETPOST('note_public', 'restricthtml') : $note_public), '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; // print ''; @@ -2118,7 +2118,7 @@ if ($action == 'create') // Private note print ''.$langs->trans('NotePrivate').''; print ''; - $doleditor = new DolEditor('note_private', (GETPOSTISSET('note_private') ?GETPOST('note_private', 'restricthtml') : $note_private), '', 80, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', (GETPOSTISSET('note_private') ?GETPOST('note_private', 'restricthtml') : $note_private), '', 80, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ''; // print ''; diff --git a/htdocs/loan/card.php b/htdocs/loan/card.php index 17454356c76..29cae07e11d 100644 --- a/htdocs/loan/card.php +++ b/htdocs/loan/card.php @@ -315,7 +315,7 @@ if ($action == 'create') print ''.$langs->trans('NotePrivate').''; print ''; - $doleditor = new DolEditor('note_private', GETPOST('note_private', 'alpha'), '', 160, 'dolibarr_notes', 'In', false, true, true, ROWS_6, '90%'); + $doleditor = new DolEditor('note_private', GETPOST('note_private', 'alpha'), '', 160, 'dolibarr_notes', 'In', false, true, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_6, '90%'); print $doleditor->Create(1); print ''; @@ -324,7 +324,7 @@ if ($action == 'create') print ''; print ''.$langs->trans('NotePublic').''; print ''; - $doleditor = new DolEditor('note_public', GETPOST('note_public', 'alpha'), '', 160, 'dolibarr_notes', 'In', false, true, true, ROWS_6, '90%'); + $doleditor = new DolEditor('note_public', GETPOST('note_public', 'alpha'), '', 160, 'dolibarr_notes', 'In', false, true, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_6, '90%'); print $doleditor->Create(1); print ''; diff --git a/htdocs/reception/card.php b/htdocs/reception/card.php index 3bac7265606..0cf53b7dd4f 100644 --- a/htdocs/reception/card.php +++ b/htdocs/reception/card.php @@ -785,7 +785,7 @@ if ($action == 'create') // Note Public print ''.$langs->trans("NotePublic").''; print ''; - $doleditor = new DolEditor('note_public', $object->note_public, '', 60, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_public', $object->note_public, '', 60, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PUBLIC) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ""; @@ -794,7 +794,7 @@ if ($action == 'create') { print ''.$langs->trans("NotePrivate").''; print ''; - $doleditor = new DolEditor('note_private', $object->note_private, '', 60, 'dolibarr_notes', 'In', 0, false, true, ROWS_3, '90%'); + $doleditor = new DolEditor('note_private', $object->note_private, '', 60, 'dolibarr_notes', 'In', 0, false, empty($conf->global->FCKEDITOR_ENABLE_NOTE_PRIVATE) ? 0 : 1, ROWS_3, '90%'); print $doleditor->Create(1); print ""; } From 6b5125fed08651b29894b6497af414d97b508156 Mon Sep 17 00:00:00 2001 From: lmarcouiller Date: Tue, 1 Dec 2020 14:50:12 +0100 Subject: [PATCH 057/743] dev widget state_board, add in user class and clean index --- .../core/boxes/box_dolibarr_state_board.php | 337 ++++++++++++++++++ htdocs/core/modules/modUser.class.php | 3 +- htdocs/index.php | 259 -------------- 3 files changed, 339 insertions(+), 260 deletions(-) create mode 100644 htdocs/core/boxes/box_dolibarr_state_board.php diff --git a/htdocs/core/boxes/box_dolibarr_state_board.php b/htdocs/core/boxes/box_dolibarr_state_board.php new file mode 100644 index 00000000000..e96dc795581 --- /dev/null +++ b/htdocs/core/boxes/box_dolibarr_state_board.php @@ -0,0 +1,337 @@ + + * Copyright (C) 2004-2010 Laurent Destailleur + * Copyright (C) 2005-2009 Regis Houssin + * Copyright (C) 2015 Frederic France + * + * 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 . + */ + +/** + * \file htdocs/core/boxes/box_dolibarr_state_board.php + * \ingroup + * \brief Module Dolibarr state base + */ + +use Symfony\Component\VarDumper\VarDumper; + +include_once DOL_DOCUMENT_ROOT . '/core/boxes/modules_boxes.php'; +include_once(DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php'); + + +/** + * Class to manage the box to show last thirdparties + */ +class box_dolibarr_state_board extends ModeleBoxes +{ + public $boxcode = "dolibarrstatebox"; + public $boximg = "box_user"; + public $boxlabel = "BoxDolibarrStateBoard"; + public $depends = array("user"); + + /** + * @var DoliDB Database handler. + */ + public $db; + + public $enabled = 1; + + public $info_box_head = array(); + public $info_box_contents = array(); + + + /** + * Constructor + * + * @param DoliDB $db Database handler + * @param string $param More parameters + */ + public function __construct($db, $param = '') + { + global $conf, $user; + + $this->db = $db; + + // disable box for such cases + if (!empty($conf->global->SOCIETE_DISABLE_CUSTOMERS)) $this->enabled = 0; // disabled by this option + + $this->hidden = !($user->rights->societe->lire && empty($user->socid)); + } + + /** + * Load data for box to show them later + * + * @param int $max Maximum number of records to load + * @return void + */ + public function loadBox($max = 5) + { + global $user, $langs, $conf; + $langs->load("boxes"); + + $this->max = $max; + $this->info_box_head = array('text' => $langs->trans("DolibarrStateBoard")); + + if (empty($user->socid) && empty($conf->global->MAIN_DISABLE_GLOBAL_BOXSTATS)) + { + $hookmanager = new HookManager($this->db); + $hookmanager->initHooks(array('index')); + $boxstatItems = array(); + $boxstatFromHook = ''; + $boxstatFromHook = $hookmanager->resPrint; + $boxstat = ''; + + $keys = array( + 'users', + 'members', + 'expensereports', + 'holidays', + 'customers', + 'prospects', + 'suppliers', + 'contacts', + 'products', + 'services', + 'projects', + 'proposals', + 'orders', + 'invoices', + 'donations', + 'supplier_proposals', + 'supplier_orders', + 'supplier_invoices', + 'contracts', + 'interventions', + 'ticket' + ); + $conditions = array( + 'users' => $user->rights->user->user->lire, + 'members' => !empty($conf->adherent->enabled) && $user->rights->adherent->lire, + 'customers' => !empty($conf->societe->enabled) && $user->rights->societe->lire && empty($conf->global->SOCIETE_DISABLE_CUSTOMERS) && empty($conf->global->SOCIETE_DISABLE_CUSTOMERS_STATS), + 'prospects' => !empty($conf->societe->enabled) && $user->rights->societe->lire && empty($conf->global->SOCIETE_DISABLE_PROSPECTS) && empty($conf->global->SOCIETE_DISABLE_PROSPECTS_STATS), + 'suppliers' => !empty($conf->fournisseur->enabled) && $user->rights->fournisseur->lire && empty($conf->global->SOCIETE_DISABLE_SUPPLIERS_STATS), + 'contacts' => !empty($conf->societe->enabled) && $user->rights->societe->contact->lire, + 'products' => !empty($conf->product->enabled) && $user->rights->produit->lire, + 'services' => !empty($conf->service->enabled) && $user->rights->service->lire, + 'proposals' => !empty($conf->propal->enabled) && $user->rights->propale->lire, + 'orders' => !empty($conf->commande->enabled) && $user->rights->commande->lire, + 'invoices' => !empty($conf->facture->enabled) && $user->rights->facture->lire, + 'donations' => !empty($conf->don->enabled) && $user->rights->don->lire, + 'contracts' => !empty($conf->contrat->enabled) && $user->rights->contrat->lire, + 'interventions' => !empty($conf->ficheinter->enabled) && $user->rights->ficheinter->lire, + 'supplier_orders' => !empty($conf->supplier_order->enabled) && $user->rights->fournisseur->commande->lire && empty($conf->global->SOCIETE_DISABLE_SUPPLIERS_ORDERS_STATS), + 'supplier_invoices' => !empty($conf->supplier_invoice->enabled) && $user->rights->fournisseur->facture->lire && empty($conf->global->SOCIETE_DISABLE_SUPPLIERS_INVOICES_STATS), + 'supplier_proposals' => !empty($conf->supplier_proposal->enabled) && $user->rights->supplier_proposal->lire && empty($conf->global->SOCIETE_DISABLE_SUPPLIERS_PROPOSAL_STATS), + 'projects' => !empty($conf->projet->enabled) && $user->rights->projet->lire, + 'expensereports' => !empty($conf->expensereport->enabled) && $user->rights->expensereport->lire, + 'holidays' => !empty($conf->holiday->enabled) && $user->rights->holiday->read, + 'ticket' => !empty($conf->ticket->enabled) && $user->rights->ticket->read + ); + $classes = array( + 'users' => 'User', + 'members' => 'Adherent', + 'customers' => 'Client', + 'prospects' => 'Client', + 'suppliers' => 'Fournisseur', + 'contacts' => 'Contact', + 'products' => 'Product', + 'services' => 'ProductService', + 'proposals' => 'Propal', + 'orders' => 'Commande', + 'invoices' => 'Facture', + 'donations' => 'Don', + 'contracts' => 'Contrat', + 'interventions' => 'Fichinter', + 'supplier_orders' => 'CommandeFournisseur', + 'supplier_invoices' => 'FactureFournisseur', + 'supplier_proposals' => 'SupplierProposal', + 'projects' => 'Project', + 'expensereports' => 'ExpenseReport', + 'holidays' => 'Holiday', + 'ticket' => 'Ticket', + ); + $includes = array( + 'users' => DOL_DOCUMENT_ROOT . "/user/class/user.class.php", + 'members' => DOL_DOCUMENT_ROOT . "/adherents/class/adherent.class.php", + 'customers' => DOL_DOCUMENT_ROOT . "/societe/class/client.class.php", + 'prospects' => DOL_DOCUMENT_ROOT . "/societe/class/client.class.php", + 'suppliers' => DOL_DOCUMENT_ROOT . "/fourn/class/fournisseur.class.php", + 'contacts' => DOL_DOCUMENT_ROOT . "/contact/class/contact.class.php", + 'products' => DOL_DOCUMENT_ROOT . "/product/class/product.class.php", + 'services' => DOL_DOCUMENT_ROOT . "/product/class/product.class.php", + 'proposals' => DOL_DOCUMENT_ROOT . "/comm/propal/class/propal.class.php", + 'orders' => DOL_DOCUMENT_ROOT . "/commande/class/commande.class.php", + 'invoices' => DOL_DOCUMENT_ROOT . "/compta/facture/class/facture.class.php", + 'donations' => DOL_DOCUMENT_ROOT . "/don/class/don.class.php", + 'contracts' => DOL_DOCUMENT_ROOT . "/contrat/class/contrat.class.php", + 'interventions' => DOL_DOCUMENT_ROOT . "/fichinter/class/fichinter.class.php", + 'supplier_orders' => DOL_DOCUMENT_ROOT . "/fourn/class/fournisseur.commande.class.php", + 'supplier_invoices' => DOL_DOCUMENT_ROOT . "/fourn/class/fournisseur.facture.class.php", + 'supplier_proposals' => DOL_DOCUMENT_ROOT . "/supplier_proposal/class/supplier_proposal.class.php", + 'projects' => DOL_DOCUMENT_ROOT . "/projet/class/project.class.php", + 'expensereports' => DOL_DOCUMENT_ROOT . "/expensereport/class/expensereport.class.php", + 'holidays' => DOL_DOCUMENT_ROOT . "/holiday/class/holiday.class.php", + 'ticket' => DOL_DOCUMENT_ROOT . "/ticket/class/ticket.class.php" + ); + $links = array( + 'users' => DOL_URL_ROOT . '/user/list.php', + 'members' => DOL_URL_ROOT . '/adherents/list.php?statut=1&mainmenu=members', + 'customers' => DOL_URL_ROOT . '/societe/list.php?type=c&mainmenu=companies', + 'prospects' => DOL_URL_ROOT . '/societe/list.php?type=p&mainmenu=companies', + 'suppliers' => DOL_URL_ROOT . '/societe/list.php?type=f&mainmenu=companies', + 'contacts' => DOL_URL_ROOT . '/contact/list.php?mainmenu=companies', + 'products' => DOL_URL_ROOT . '/product/list.php?type=0&mainmenu=products', + 'services' => DOL_URL_ROOT . '/product/list.php?type=1&mainmenu=products', + 'proposals' => DOL_URL_ROOT . '/comm/propal/list.php?mainmenu=commercial&leftmenu=propals', + 'orders' => DOL_URL_ROOT . '/commande/list.php?mainmenu=commercial&leftmenu=orders', + 'invoices' => DOL_URL_ROOT . '/compta/facture/list.php?mainmenu=billing&leftmenu=customers_bills', + 'donations' => DOL_URL_ROOT . '/don/list.php?leftmenu=donations', + 'contracts' => DOL_URL_ROOT . '/contrat/list.php?mainmenu=commercial&leftmenu=contracts', + 'interventions' => DOL_URL_ROOT . '/fichinter/list.php?mainmenu=commercial&leftmenu=ficheinter', + 'supplier_orders' => DOL_URL_ROOT . '/fourn/commande/list.php?mainmenu=commercial&leftmenu=orders_suppliers', + 'supplier_invoices' => DOL_URL_ROOT . '/fourn/facture/list.php?mainmenu=billing&leftmenu=suppliers_bills', + 'supplier_proposals' => DOL_URL_ROOT . '/supplier_proposal/list.php?mainmenu=commercial&leftmenu=', + 'projects' => DOL_URL_ROOT . '/projet/list.php?mainmenu=project', + 'expensereports' => DOL_URL_ROOT . '/expensereport/list.php?mainmenu=hrm&leftmenu=expensereport', + 'holidays' => DOL_URL_ROOT . '/holiday/list.php?mainmenu=hrm&leftmenu=holiday', + 'ticket' => DOL_URL_ROOT . '/ticket/list.php?leftmenu=ticket' + ); + $titres = array( + 'users' => "Users", + 'members' => "Members", + 'customers' => "ThirdPartyCustomersStats", + 'prospects' => "ThirdPartyProspectsStats", + 'suppliers' => "Suppliers", + 'contacts' => "Contacts", + 'products' => "Products", + 'services' => "Services", + 'proposals' => "CommercialProposalsShort", + 'orders' => "CustomersOrders", + 'invoices' => "BillsCustomers", + 'donations' => "Donations", + 'contracts' => "Contracts", + 'interventions' => "Interventions", + 'supplier_orders' => "SuppliersOrders", + 'supplier_invoices' => "SuppliersInvoices", + 'supplier_proposals' => "SupplierProposalShort", + 'projects' => "Projects", + 'expensereports' => "ExpenseReports", + 'holidays' => "Holidays", + 'ticket' => "Ticket", + ); + $langfile = array( + 'customers' => "companies", + 'contacts' => "companies", + 'services' => "products", + 'proposals' => "propal", + 'invoices' => "bills", + 'supplier_orders' => "orders", + 'supplier_invoices' => "bills", + 'supplier_proposals' => 'supplier_proposal', + 'expensereports' => "trips", + 'holidays' => "holiday", + ); + $boardloaded = array(); + + foreach ($keys as $val) + { + if ($conditions[$val]) + { + $boxstatItem = ''; + $class = $classes[$val]; + // Search in cache if load_state_board is already realized + $classkeyforcache = $class; + if ($classkeyforcache == 'ProductService') $classkeyforcache = 'Product'; // ProductService use same load_state_board than Product + + if (!isset($boardloaded[$classkeyforcache]) || !is_object($boardloaded[$classkeyforcache])) + { + include_once $includes[$val]; // Loading a class cost around 1Mb + + $board = new $class($this->db); + $board->load_state_board(); + $boardloaded[$class] = $board; + } else { + $board = $boardloaded[$classkeyforcache]; + } + + $langs->load(empty($langfile[$val]) ? $val : $langfile[$val]); + + $text = $langs->trans($titres[$val]); + $boxstatItem .= ''; + $boxstatItem .= '
'; + $boxstatItem .= '' . $text . '
'; + $boxstatItem .= '' . img_object("", $board->picto, 'class="inline-block"') . ' ' . ($board->nb[$val] ? $board->nb[$val] : 0) . ''; + $boxstatItem .= '
'; + $boxstatItem .= '
'; + + $boxstatItems[$val] = $boxstatItem; + } + } + + if (!empty($boxstatFromHook) || !empty($boxstatItems)) + { + $boxstat .= $boxstatFromHook; + + if (is_array($boxstatItems) && count($boxstatItems) > 0) + { + $boxstat .= implode('', $boxstatItems); + } + + $boxstat .= '
'; + $boxstat .= '
'; + $boxstat .= '
'; + $boxstat .= '
'; + $boxstat .= '
'; + $boxstat .= '
'; + $boxstat .= '
'; + $boxstat .= '
'; + + $boxstat .= ''; + $boxstat .= ''; + $boxstat .= '
'; + + $this->info_box_contents[0][0] = array( + 'td' => '', + 'textnoformat' => $boxstat + ); + + } else { + $this->info_box_contents[0][0] = array( + 'td' => 'class="nohover center"', + 'maxlength' => 500, + 'text' => ($this->db->error() . ' sql=' . $sql) + ); + } + } else { + $this->info_box_contents[0][0] = array( + 'td' => '', + 'text' => $langs->trans("ReadPermissionNotAllowed") + ); + } + } + + + /** + * Method to show box + * + * @param array $head Array with properties of box title + * @param array $contents Array with properties of box lines + * @param int $nooutput No print, only return string + * @return string + */ + public function showBox($head = null, $contents = null, $nooutput = 0) + { + return parent::showBox($this->info_box_head, $this->info_box_contents, $nooutput); + } +} diff --git a/htdocs/core/modules/modUser.class.php b/htdocs/core/modules/modUser.class.php index 4b397df65e4..dba5f864e8f 100644 --- a/htdocs/core/modules/modUser.class.php +++ b/htdocs/core/modules/modUser.class.php @@ -78,7 +78,8 @@ class modUser extends DolibarrModules // Boxes $this->boxes = array( 0=>array('file'=>'box_lastlogin.php', 'enabledbydefaulton'=>'Home'), - 1=>array('file'=>'box_birthdays.php', 'enabledbydefaulton'=>'Home') + 1=>array('file'=>'box_birthdays.php', 'enabledbydefaulton'=>'Home'), + 2=>array('file'=>'box_dolibarr_state_board.php', 'enabledbydefaulton'=>'Home') ); // Permissions diff --git a/htdocs/index.php b/htdocs/index.php index 887baa04e78..0347a793d42 100644 --- a/htdocs/index.php +++ b/htdocs/index.php @@ -114,219 +114,6 @@ $boxstatFromHook = ''; // Load translation files required by page $langs->loadLangs(array('commercial', 'bills', 'orders', 'contracts')); -// Load global statistics of objects -if (empty($user->socid) && empty($conf->global->MAIN_DISABLE_GLOBAL_BOXSTATS)) -{ - $object = new stdClass(); - $parameters = array(); - $action = ''; - $reshook = $hookmanager->executeHooks('addStatisticLine', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks - $boxstatFromHook = $hookmanager->resPrint; - - if (empty($reshook)) - { - // Cle array returned by the method load_state_board for each line - $keys = array( - 'users', - 'members', - 'expensereports', - 'holidays', - 'customers', - 'prospects', - 'suppliers', - 'contacts', - 'products', - 'services', - 'projects', - 'proposals', - 'orders', - 'invoices', - 'donations', - 'supplier_proposals', - 'supplier_orders', - 'supplier_invoices', - 'contracts', - 'interventions', - 'ticket' - ); - - // Condition to be checked for each display line dashboard - $conditions = array( - 'users' => $user->rights->user->user->lire, - 'members' => !empty($conf->adherent->enabled) && $user->rights->adherent->lire, - 'customers' => !empty($conf->societe->enabled) && $user->rights->societe->lire && empty($conf->global->SOCIETE_DISABLE_CUSTOMERS) && empty($conf->global->SOCIETE_DISABLE_CUSTOMERS_STATS), - 'prospects' => !empty($conf->societe->enabled) && $user->rights->societe->lire && empty($conf->global->SOCIETE_DISABLE_PROSPECTS) && empty($conf->global->SOCIETE_DISABLE_PROSPECTS_STATS), - 'suppliers' => !empty($conf->fournisseur->enabled) && $user->rights->fournisseur->lire && empty($conf->global->SOCIETE_DISABLE_SUPPLIERS_STATS), - 'contacts' => !empty($conf->societe->enabled) && $user->rights->societe->contact->lire, - 'products' => !empty($conf->product->enabled) && $user->rights->produit->lire, - 'services' => !empty($conf->service->enabled) && $user->rights->service->lire, - 'proposals' => !empty($conf->propal->enabled) && $user->rights->propale->lire, - 'orders' => !empty($conf->commande->enabled) && $user->rights->commande->lire, - 'invoices' => !empty($conf->facture->enabled) && $user->rights->facture->lire, - 'donations' => !empty($conf->don->enabled) && $user->rights->don->lire, - 'contracts' => !empty($conf->contrat->enabled) && $user->rights->contrat->lire, - 'interventions' => !empty($conf->ficheinter->enabled) && $user->rights->ficheinter->lire, - 'supplier_orders' => !empty($conf->supplier_order->enabled) && $user->rights->fournisseur->commande->lire && empty($conf->global->SOCIETE_DISABLE_SUPPLIERS_ORDERS_STATS), - 'supplier_invoices' => !empty($conf->supplier_invoice->enabled) && $user->rights->fournisseur->facture->lire && empty($conf->global->SOCIETE_DISABLE_SUPPLIERS_INVOICES_STATS), - 'supplier_proposals' => !empty($conf->supplier_proposal->enabled) && $user->rights->supplier_proposal->lire && empty($conf->global->SOCIETE_DISABLE_SUPPLIERS_PROPOSAL_STATS), - 'projects' => !empty($conf->projet->enabled) && $user->rights->projet->lire, - 'expensereports' => !empty($conf->expensereport->enabled) && $user->rights->expensereport->lire, - 'holidays' => !empty($conf->holiday->enabled) && $user->rights->holiday->read, - 'ticket' => !empty($conf->ticket->enabled) && $user->rights->ticket->read - ); - // Class file containing the method load_state_board for each line - $includes = array( - 'users' => DOL_DOCUMENT_ROOT."/user/class/user.class.php", - 'members' => DOL_DOCUMENT_ROOT."/adherents/class/adherent.class.php", - 'customers' => DOL_DOCUMENT_ROOT."/societe/class/client.class.php", - 'prospects' => DOL_DOCUMENT_ROOT."/societe/class/client.class.php", - 'suppliers' => DOL_DOCUMENT_ROOT."/fourn/class/fournisseur.class.php", - 'contacts' => DOL_DOCUMENT_ROOT."/contact/class/contact.class.php", - 'products' => DOL_DOCUMENT_ROOT."/product/class/product.class.php", - 'services' => DOL_DOCUMENT_ROOT."/product/class/product.class.php", - 'proposals' => DOL_DOCUMENT_ROOT."/comm/propal/class/propal.class.php", - 'orders' => DOL_DOCUMENT_ROOT."/commande/class/commande.class.php", - 'invoices' => DOL_DOCUMENT_ROOT."/compta/facture/class/facture.class.php", - 'donations' => DOL_DOCUMENT_ROOT."/don/class/don.class.php", - 'contracts' => DOL_DOCUMENT_ROOT."/contrat/class/contrat.class.php", - 'interventions' => DOL_DOCUMENT_ROOT."/fichinter/class/fichinter.class.php", - 'supplier_orders' => DOL_DOCUMENT_ROOT."/fourn/class/fournisseur.commande.class.php", - 'supplier_invoices' => DOL_DOCUMENT_ROOT."/fourn/class/fournisseur.facture.class.php", - 'supplier_proposals' => DOL_DOCUMENT_ROOT."/supplier_proposal/class/supplier_proposal.class.php", - 'projects' => DOL_DOCUMENT_ROOT."/projet/class/project.class.php", - 'expensereports' => DOL_DOCUMENT_ROOT."/expensereport/class/expensereport.class.php", - 'holidays' => DOL_DOCUMENT_ROOT."/holiday/class/holiday.class.php", - 'ticket' => DOL_DOCUMENT_ROOT."/ticket/class/ticket.class.php" - ); - // Name class containing the method load_state_board for each line - $classes = array( - 'users' => 'User', - 'members' => 'Adherent', - 'customers' => 'Client', - 'prospects' => 'Client', - 'suppliers' => 'Fournisseur', - 'contacts' => 'Contact', - 'products' => 'Product', - 'services' => 'ProductService', - 'proposals' => 'Propal', - 'orders' => 'Commande', - 'invoices' => 'Facture', - 'donations' => 'Don', - 'contracts' => 'Contrat', - 'interventions' => 'Fichinter', - 'supplier_orders' => 'CommandeFournisseur', - 'supplier_invoices' => 'FactureFournisseur', - 'supplier_proposals' => 'SupplierProposal', - 'projects' => 'Project', - 'expensereports' => 'ExpenseReport', - 'holidays' => 'Holiday', - 'ticket' => 'Ticket', - ); - // Translation keyword - $titres = array( - 'users' => "Users", - 'members' => "Members", - 'customers' => "ThirdPartyCustomersStats", - 'prospects' => "ThirdPartyProspectsStats", - 'suppliers' => "Suppliers", - 'contacts' => "Contacts", - 'products' => "Products", - 'services' => "Services", - 'proposals' => "CommercialProposalsShort", - 'orders' => "CustomersOrders", - 'invoices' => "BillsCustomers", - 'donations' => "Donations", - 'contracts' => "Contracts", - 'interventions' => "Interventions", - 'supplier_orders' => "SuppliersOrders", - 'supplier_invoices' => "SuppliersInvoices", - 'supplier_proposals' => "SupplierProposalShort", - 'projects' => "Projects", - 'expensereports' => "ExpenseReports", - 'holidays' => "Holidays", - 'ticket' => "Ticket", - ); - // Dashboard Link lines - $links = array( - 'users' => DOL_URL_ROOT.'/user/list.php', - 'members' => DOL_URL_ROOT.'/adherents/list.php?statut=1&mainmenu=members', - 'customers' => DOL_URL_ROOT.'/societe/list.php?type=c&mainmenu=companies', - 'prospects' => DOL_URL_ROOT.'/societe/list.php?type=p&mainmenu=companies', - 'suppliers' => DOL_URL_ROOT.'/societe/list.php?type=f&mainmenu=companies', - 'contacts' => DOL_URL_ROOT.'/contact/list.php?mainmenu=companies', - 'products' => DOL_URL_ROOT.'/product/list.php?type=0&mainmenu=products', - 'services' => DOL_URL_ROOT.'/product/list.php?type=1&mainmenu=products', - 'proposals' => DOL_URL_ROOT.'/comm/propal/list.php?mainmenu=commercial&leftmenu=propals', - 'orders' => DOL_URL_ROOT.'/commande/list.php?mainmenu=commercial&leftmenu=orders', - 'invoices' => DOL_URL_ROOT.'/compta/facture/list.php?mainmenu=billing&leftmenu=customers_bills', - 'donations' => DOL_URL_ROOT.'/don/list.php?leftmenu=donations', - 'contracts' => DOL_URL_ROOT.'/contrat/list.php?mainmenu=commercial&leftmenu=contracts', - 'interventions' => DOL_URL_ROOT.'/fichinter/list.php?mainmenu=commercial&leftmenu=ficheinter', - 'supplier_orders' => DOL_URL_ROOT.'/fourn/commande/list.php?mainmenu=commercial&leftmenu=orders_suppliers', - 'supplier_invoices' => DOL_URL_ROOT.'/fourn/facture/list.php?mainmenu=billing&leftmenu=suppliers_bills', - 'supplier_proposals' => DOL_URL_ROOT.'/supplier_proposal/list.php?mainmenu=commercial&leftmenu=', - 'projects' => DOL_URL_ROOT.'/projet/list.php?mainmenu=project', - 'expensereports' => DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&leftmenu=expensereport', - 'holidays' => DOL_URL_ROOT.'/holiday/list.php?mainmenu=hrm&leftmenu=holiday', - 'ticket' => DOL_URL_ROOT.'/ticket/list.php?leftmenu=ticket' - ); - // Translation lang files - $langfile = array( - 'customers' => "companies", - 'contacts' => "companies", - 'services' => "products", - 'proposals' => "propal", - 'invoices' => "bills", - 'supplier_orders' => "orders", - 'supplier_invoices' => "bills", - 'supplier_proposals' => 'supplier_proposal', - 'expensereports' => "trips", - 'holidays' => "holiday", - ); - - - // Loop and displays each line of table - $boardloaded = array(); - foreach ($keys as $val) - { - if ($conditions[$val]) - { - $boxstatItem = ''; - $class = $classes[$val]; - // Search in cache if load_state_board is already realized - $classkeyforcache = $class; - if ($classkeyforcache == 'ProductService') $classkeyforcache = 'Product'; // ProductService use same load_state_board than Product - - if (!isset($boardloaded[$classkeyforcache]) || !is_object($boardloaded[$classkeyforcache])) - { - include_once $includes[$val]; // Loading a class cost around 1Mb - - $board = new $class($db); - $board->load_state_board(); - $boardloaded[$class] = $board; - } else { - $board = $boardloaded[$classkeyforcache]; - } - - $langs->load(empty($langfile[$val]) ? $val : $langfile[$val]); - - $text = $langs->trans($titres[$val]); - $boxstatItem .= ''; - $boxstatItem .= '
'; - $boxstatItem .= ''.$text.'
'; - $boxstatItem .= ''.img_object("", $board->picto, 'class="inline-block"').' '.($board->nb[$val] ? $board->nb[$val] : 0).''; - $boxstatItem .= '
'; - $boxstatItem .= '
'; - - $boxstatItems[$val] = $boxstatItem; - } - } - } -} - - - - // Dolibarr Working Board with weather if (empty($conf->global->MAIN_DISABLE_GLOBAL_WORKBOARD)) { @@ -903,54 +690,8 @@ $boxlist .= $resultboxes['boxlista']; $boxlist .= '
'; - -if (empty($user->socid) && empty($conf->global->MAIN_DISABLE_GLOBAL_BOXSTATS)) -{ - // Remove allready present info in new dash board - if (!empty($conf->global->MAIN_INCLUDE_GLOBAL_STATS_IN_OPENED_DASHBOARD) && is_array($boxstatItems) && count($boxstatItems) > 0) { - foreach ($boxstatItems as $boxstatItemKey => $boxstatItemHtml) { - if (in_array($boxstatItemKey, $globalStatInTopOpenedDashBoard)) { - unset($boxstatItems[$boxstatItemKey]); - } - } - } - - if (!empty($boxstatFromHook) || !empty($boxstatItems)) { - $boxstat .= ''."\n"; - $boxstat .= '
'; - $boxstat .= ''; - $boxstat .= ''; - $boxstat .= ''; - $boxstat .= ''; - $boxstat .= ''; - $boxstat .= '
'; - $boxstat .= '
'.$langs->trans("DolibarrStateBoard").'
'; - $boxstat .= '
'; - - $boxstat .= $boxstatFromHook; - - if (is_array($boxstatItems) && count($boxstatItems) > 0) - { - $boxstat .= implode('', $boxstatItems); - } - - $boxstat .= '
'; - $boxstat .= '
'; - $boxstat .= '
'; - $boxstat .= '
'; - $boxstat .= '
'; - $boxstat .= '
'; - $boxstat .= '
'; - $boxstat .= '
'; - - $boxstat .= '
'; - $boxstat .= '
'; - } -} - $boxlist .= '
'; -$boxlist .= $boxstat; $boxlist .= $resultboxes['boxlistb']; $boxlist .= '
'; From bca89fc204adc8238776ea2cc109df3a39a2aee2 Mon Sep 17 00:00:00 2001 From: lmarcouiller Date: Tue, 1 Dec 2020 15:57:15 +0100 Subject: [PATCH 058/743] add dolibarr_state_board in migration --- htdocs/install/mysql/migration/12.0.0-13.0.0.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 18802301e5a..960dee1eb56 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 @@ -439,7 +439,8 @@ INSERT INTO llx_c_product_nature (code, label, active) VALUES (1, 'Finished', 1) ALTER TABLE llx_product MODIFY COLUMN finished tinyint DEFAULT NULL; ALTER TABLE llx_product ADD CONSTRAINT fk_product_finished FOREIGN KEY (finished) REFERENCES llx_c_product_nature (code); - +INSERT INTO llx_boxes_def (file) VALUES ('box_dolibarr_state_board.php'); +INSERT INTO llx_boxes (box_id, position, box_order, maxline, params) VALUES ((SELECT b.rowid FROM llx_boxes_def as b WHERE file = 'box_dolibarr_state_board.php'), 0, 0, NULL, NULL); -- MIGRATION TO DO AFTER RENAMING AN OBJECT -- drop constraint From 95bc0ff54db8df7529f01df4747b0ca71ddb321b Mon Sep 17 00:00:00 2001 From: lmarcouiller Date: Tue, 1 Dec 2020 15:59:55 +0100 Subject: [PATCH 059/743] update dolibarr_state_board bug fix --- htdocs/core/boxes/box_dolibarr_state_board.php | 1 - 1 file changed, 1 deletion(-) diff --git a/htdocs/core/boxes/box_dolibarr_state_board.php b/htdocs/core/boxes/box_dolibarr_state_board.php index e96dc795581..463af26c88e 100644 --- a/htdocs/core/boxes/box_dolibarr_state_board.php +++ b/htdocs/core/boxes/box_dolibarr_state_board.php @@ -299,7 +299,6 @@ class box_dolibarr_state_board extends ModeleBoxes $boxstat .= ''; $boxstat .= ''; - $boxstat .= '
'; $this->info_box_contents[0][0] = array( 'td' => '', From 5c3c3d760858ca27bb3edef7402ac64a3906d9bf Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Tue, 1 Dec 2020 15:05:11 +0000 Subject: [PATCH 060/743] Fixing style errors. --- htdocs/core/boxes/box_dolibarr_state_board.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/htdocs/core/boxes/box_dolibarr_state_board.php b/htdocs/core/boxes/box_dolibarr_state_board.php index 463af26c88e..6f9190dda1e 100644 --- a/htdocs/core/boxes/box_dolibarr_state_board.php +++ b/htdocs/core/boxes/box_dolibarr_state_board.php @@ -20,14 +20,14 @@ /** * \file htdocs/core/boxes/box_dolibarr_state_board.php - * \ingroup + * \ingroup * \brief Module Dolibarr state base */ use Symfony\Component\VarDumper\VarDumper; include_once DOL_DOCUMENT_ROOT . '/core/boxes/modules_boxes.php'; -include_once(DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php'); +include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php'; /** @@ -304,7 +304,6 @@ class box_dolibarr_state_board extends ModeleBoxes 'td' => '', 'textnoformat' => $boxstat ); - } else { $this->info_box_contents[0][0] = array( 'td' => 'class="nohover center"', From 647294e88e5203c00da072e61d1dba812214b4bd Mon Sep 17 00:00:00 2001 From: lmarcouiller Date: Thu, 3 Dec 2020 11:04:24 +0100 Subject: [PATCH 061/743] fix #15384 migration --- htdocs/install/mysql/migration/12.0.0-13.0.0.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 960dee1eb56..541bb6f58be 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 @@ -439,8 +439,6 @@ INSERT INTO llx_c_product_nature (code, label, active) VALUES (1, 'Finished', 1) ALTER TABLE llx_product MODIFY COLUMN finished tinyint DEFAULT NULL; ALTER TABLE llx_product ADD CONSTRAINT fk_product_finished FOREIGN KEY (finished) REFERENCES llx_c_product_nature (code); -INSERT INTO llx_boxes_def (file) VALUES ('box_dolibarr_state_board.php'); -INSERT INTO llx_boxes (box_id, position, box_order, maxline, params) VALUES ((SELECT b.rowid FROM llx_boxes_def as b WHERE file = 'box_dolibarr_state_board.php'), 0, 0, NULL, NULL); -- MIGRATION TO DO AFTER RENAMING AN OBJECT -- drop constraint @@ -543,3 +541,4 @@ CREATE TABLE llx_session( user_agent varchar(128) NULL )ENGINE=innodb; +INSERT INTO llx_boxes_def (file) VALUES ('box_dolibarr_state_board.php'); From 13f985d28085753683212eff42fd8e39cbcd2974 Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Fri, 4 Dec 2020 04:13:42 +0100 Subject: [PATCH 062/743] NEW: Accountancy - Introduce "Level3" - Define account directly in thirdparty --- htdocs/accountancy/customer/index.php | 16 ++- htdocs/accountancy/customer/list.php | 38 ++++-- htdocs/accountancy/supplier/index.php | 14 +- htdocs/accountancy/supplier/list.php | 32 ++++- .../install/mysql/migration/13.0.0-14.0.0.sql | 36 +++++ htdocs/install/mysql/tables/llx_societe.sql | 3 + htdocs/langs/en_US/accountancy.lang | 1 + htdocs/societe/card.php | 124 +++++++++++++++++- htdocs/societe/class/societe.class.php | 101 +++++++++++++- 9 files changed, 338 insertions(+), 27 deletions(-) create mode 100644 htdocs/install/mysql/migration/13.0.0-14.0.0.sql diff --git a/htdocs/accountancy/customer/index.php b/htdocs/accountancy/customer/index.php index 1a5098bda38..3634fba2e79 100644 --- a/htdocs/accountancy/customer/index.php +++ b/htdocs/accountancy/customer/index.php @@ -1,7 +1,7 @@ * Copyright (C) 2013-2014 Florian Henry - * Copyright (C) 2013-2017 Alexandre Spangaro + * Copyright (C) 2013-2020 Alexandre Spangaro * Copyright (C) 2014 Juanjo Menent * Copyright (C) 2015 Jean-François Ferry * @@ -123,9 +123,9 @@ if ($action == 'validatehistory') { $sql .= " l.rowid, l.fk_product, l.description, l.total_ht, l.fk_code_ventilation, l.product_type as type_l, l.tva_tx as tva_tx_line, l.vat_src_code,"; $sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.fk_product_type as type, p.tva_tx as tva_tx_prod,"; $sql .= " p.accountancy_code_sell as code_sell, p.accountancy_code_sell_intra as code_sell_intra, p.accountancy_code_sell_export as code_sell_export,"; - $sql .= " aa.rowid as aarowid, aa2.rowid as aarowid_intra, aa3.rowid as aarowid_export,"; + $sql .= " aa.rowid as aarowid, aa2.rowid as aarowid_intra, aa3.rowid as aarowid_export, aa4.rowid as aarowid_thirdparty,"; $sql .= " co.code as country_code, co.label as country_label,"; - $sql .= " s.tva_intra"; + $sql .= " s.tva_intra, s.accountancy_code_sell as company_code_sell"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = f.fk_soc"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as co ON co.rowid = s.fk_pays "; @@ -134,6 +134,7 @@ if ($action == 'validatehistory') { $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa ON p.accountancy_code_sell = aa.account_number AND aa.active = 1 AND aa.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa.entity = ".$conf->entity; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa2 ON p.accountancy_code_sell_intra = aa2.account_number AND aa2.active = 1 AND aa2.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa2.entity = ".$conf->entity; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa3 ON p.accountancy_code_sell_export = aa3.account_number AND aa3.active = 1 AND aa3.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa3.entity = ".$conf->entity; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa4 ON s.accountancy_code_sell = aa4.account_number AND aa4.active = 1 AND aa4.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa4.entity = ".$conf->entity; $sql .= " WHERE f.fk_statut > 0 AND l.fk_code_ventilation <= 0"; $sql .= " AND l.product_type <= 2"; @@ -153,7 +154,7 @@ if ($action == 'validatehistory') { $isBuyerInEEC = isInEEC($objp); - // Search suggested account for product/service (similar code exists in page list.php to make manual binding) + // Level 2: Search suggested account for product/service (similar code exists in page list.php to make manual binding) $suggestedaccountingaccountfor = ''; if (($objp->country_code == $mysoc->country_code) || empty($objp->country_code)) { // If buyer in same country than seller (if not defined, we assume it is same country) $objp->code_sell_p = $objp->code_sell; @@ -179,6 +180,13 @@ if ($action == 'validatehistory') { } } + // Level 3: Search suggested account for this thirdparty (similar code exists in page index.php to make automatic binding) + if (!empty($objp->company_code_sell)) { + $objp->code_sell_t = $objp->company_code_sell; + $objp->aarowid_suggest = $objp->aarowid_thirdparty; + $suggestedaccountingaccountfor = ''; + } + if ($objp->aarowid_suggest > 0) { $sqlupdate = "UPDATE ".MAIN_DB_PREFIX."facturedet"; diff --git a/htdocs/accountancy/customer/list.php b/htdocs/accountancy/customer/list.php index eb2743258d7..88464dcf95d 100644 --- a/htdocs/accountancy/customer/list.php +++ b/htdocs/accountancy/customer/list.php @@ -211,9 +211,9 @@ $sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, $sql .= " p.accountancy_code_sell as code_sell, p.accountancy_code_sell_intra as code_sell_intra, p.accountancy_code_sell_export as code_sell_export,"; $sql .= " p.accountancy_code_buy as code_buy, p.accountancy_code_buy_intra as code_buy_intra, p.accountancy_code_buy_export as code_buy_export,"; $sql .= " p.tosell as status, p.tobuy as status_buy,"; -$sql .= " aa.rowid as aarowid, aa2.rowid as aarowid_intra, aa3.rowid as aarowid_export,"; +$sql .= " aa.rowid as aarowid, aa2.rowid as aarowid_intra, aa3.rowid as aarowid_export, aa4.rowid as aarowid_thirdparty,"; $sql .= " co.code as country_code, co.label as country_label,"; -$sql .= " s.rowid as socid, s.nom as name, s.tva_intra, s.email, s.town, s.zip, s.fk_pays, s.client, s.fournisseur, s.code_client, s.code_fournisseur, s.code_compta as code_compta_client, s.code_compta_fournisseur"; +$sql .= " s.rowid as socid, s.nom as name, s.tva_intra, s.email, s.town, s.zip, s.fk_pays, s.client, s.fournisseur, s.code_client, s.code_fournisseur, s.code_compta as code_compta_client, s.code_compta_fournisseur, s.accountancy_code_sell as company_code_sell"; $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; @@ -225,6 +225,7 @@ $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = l.fk_product"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa ON p.accountancy_code_sell = aa.account_number AND aa.active = 1 AND aa.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa.entity = ".$conf->entity; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa2 ON p.accountancy_code_sell_intra = aa2.account_number AND aa2.active = 1 AND aa2.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa2.entity = ".$conf->entity; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa3 ON p.accountancy_code_sell_export = aa3.account_number AND aa3.active = 1 AND aa3.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa3.entity = ".$conf->entity; +$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa4 ON s.accountancy_code_sell = aa4.account_number AND aa4.active = 1 AND aa4.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa4.entity = ".$conf->entity; $sql .= " WHERE f.fk_statut > 0 AND l.fk_code_ventilation <= 0"; $sql .= " AND l.product_type <= 2"; // Define begin binding date @@ -456,11 +457,12 @@ if ($result) { $facture_static->type = $objp->ftype; $code_sell_p_notset = ''; + $code_sell_t_notset = ''; $objp->aarowid_suggest = ''; // Will be set later $isBuyerInEEC = isInEEC($objp); - // Search suggested default account for product/service + // Level 1: Search suggested default account for product/service $suggestedaccountingaccountbydefaultfor = ''; if ($objp->type_l == 1) { if ($objp->country_code == $mysoc->country_code || empty($objp->country_code)) { // If buyer in same country than seller (if not defined, we assume it is same country) @@ -503,7 +505,7 @@ if ($result) { } if ($objp->code_sell_l == -1) $objp->code_sell_l = ''; - // Search suggested account for product/service (similar code exists in page index.php to make automatic binding) + // Level 2: Search suggested account for product/service (similar code exists in page index.php to make automatic binding) $suggestedaccountingaccountfor = ''; if (($objp->country_code == $mysoc->country_code) || empty($objp->country_code)) { // If buyer in same country than seller (if not defined, we assume it is same country) $objp->code_sell_p = $objp->code_sell; @@ -529,6 +531,13 @@ if ($result) { } } + // Level 3: Search suggested account for this thirdparty (similar code exists in page index.php to make automatic binding) + if (!empty($objp->company_code_sell)) { + $objp->code_sell_t = $objp->company_code_sell; + $objp->aarowid_suggest = $objp->aarowid_thirdparty; + $suggestedaccountingaccountfor = ''; + } + // Manage Deposit if ($objp->description == "(DEPOSIT)") { $accountdeposittoventilated = new AccountingAccount($db); @@ -547,6 +556,7 @@ if ($result) { // $objp->code_sell_l is now default code of product/service // $objp->code_sell_p is now code of product/service + // $objp->code_sell_t is now code of thirdparty print ''; @@ -596,7 +606,7 @@ if ($result) { // Found accounts print ''; - $s = ''.(($objp->type_l == 1) ? $langs->trans("DefaultForService") : $langs->trans("DefaultForProduct")).': '; + $s = '1. '.(($objp->type_l == 1) ? $langs->trans("DefaultForService") : $langs->trans("DefaultForProduct")).': '; $shelp = ''; if ($suggestedaccountingaccountbydefaultfor == 'eec') $shelp .= $langs->trans("SaleEEC"); elseif ($suggestedaccountingaccountbydefaultfor == 'export') $shelp .= $langs->trans("SaleExport"); @@ -605,7 +615,7 @@ if ($result) { if ($objp->product_id > 0) { print '
'; - $s = ''.(($objp->type_l == 1) ? $langs->trans("ThisService") : $langs->trans("ThisProduct")).': '; + $s = '2. '.(($objp->type_l == 1) ? $langs->trans("ThisService") : $langs->trans("ThisProduct")).': '; $shelp = ''; if ($suggestedaccountingaccountfor == 'eec') $shelp = $langs->trans("SaleEEC"); elseif ($suggestedaccountingaccountfor == 'eecwithvat') $shelp = $langs->trans("SaleEECWithVAT"); @@ -613,16 +623,23 @@ if ($result) { elseif ($suggestedaccountingaccountfor == 'export') $shelp = $langs->trans("SaleExport"); $s .= (empty($objp->code_sell_p) ? ''.$langs->trans("NotDefined").'' : length_accountg($objp->code_sell_p)); print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); + } else { + print '
'; + $s = '2. '.(($objp->type_l == 1) ? $langs->trans("ThisService") : $langs->trans("ThisProduct")).': '; + $shelp = ''; + $s .= $langs->trans("NotDefined"); + print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); } + print '
'; + $s = '3. '.$langs->trans("DefaultForThirdparty").': '; + $shelp = ''; + $s .= ($objp->code_sell_t > 0 ? length_accountg($objp->code_sell_t) : ''.$langs->trans("NotDefined").''); + print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); print ''; // Suggested accounting account - // $objp->code_sell_l = default (it takes the country into consideration), $objp->code_sell_p is value for product (it takes the country into consideration too) print ''; $suggestedid = $objp->aarowid_suggest; - /*var_dump($suggestedid); - var_dump($objp->code_sell_p); - var_dump($objp->code_sell_l);*/ if (empty($suggestedid) && empty($objp->code_sell_p) && !empty($objp->code_sell_l) && empty($conf->global->ACCOUNTANCY_DO_NOT_AUTOFILL_ACCOUNT_WITH_GENERIC)) { if (empty($accountingaccount_codetotid_cache[$objp->code_sell_l])) @@ -642,7 +659,6 @@ if ($result) { // Column with checkbox print ''; - //var_dump($objp->aarowid);var_dump($objp->aarowid_intra);var_dump($objp->aarowid_export);var_dump($objp->aarowid_suggest); $ischecked = $objp->aarowid_suggest; if ($suggestedaccountingaccountfor == 'eecwithoutvatnumber') $ischecked = 0; print ''; diff --git a/htdocs/accountancy/supplier/index.php b/htdocs/accountancy/supplier/index.php index 0dbe09fa468..9dd097f3d0c 100644 --- a/htdocs/accountancy/supplier/index.php +++ b/htdocs/accountancy/supplier/index.php @@ -120,9 +120,9 @@ if ($action == 'validatehistory') { $sql .= " l.rowid, l.fk_product, l.description, l.total_ht, l.fk_code_ventilation, l.product_type as type_l, l.tva_tx as tva_tx_line, l.vat_src_code,"; $sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.fk_product_type as type,"; $sql .= " p.accountancy_code_buy as code_buy, p.accountancy_code_buy_intra as code_buy_intra, p.accountancy_code_buy_export as code_buy_export, p.tva_tx as tva_tx_prod,"; - $sql .= " aa.rowid as aarowid, aa2.rowid as aarowid_intra, aa3.rowid as aarowid_export,"; + $sql .= " aa.rowid as aarowid, aa2.rowid as aarowid_intra, aa3.rowid as aarowid_export, aa4.rowid as aarowid_thirdparty,"; $sql .= " co.code as country_code, co.label as country_label,"; - $sql .= " s.tva_intra"; + $sql .= " s.tva_intra, s.accountancy_code_buy as company_code_buy"; $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = f.fk_soc"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_country as co ON co.rowid = s.fk_pays "; @@ -131,6 +131,7 @@ if ($action == 'validatehistory') { $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa ON p.accountancy_code_buy = aa.account_number AND aa.active = 1 AND aa.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa.entity = ".$conf->entity; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa2 ON p.accountancy_code_buy_intra = aa2.account_number AND aa2.active = 1 AND aa2.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa2.entity = ".$conf->entity; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa3 ON p.accountancy_code_buy_export = aa3.account_number AND aa3.active = 1 AND aa3.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa3.entity = ".$conf->entity; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa4 ON s.accountancy_code_buy = aa4.account_number AND aa4.active = 1 AND aa4.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa4.entity = ".$conf->entity; $sql .= " WHERE f.fk_statut > 0 AND l.fk_code_ventilation <= 0"; $sql .= " AND l.product_type <= 2"; @@ -151,7 +152,7 @@ if ($action == 'validatehistory') { $isSellerInEEC = isInEEC($objp); - // Search suggested account for product/service + // Level 2: Search suggested account for product/service (similar code exists in page list.php to make manual binding) $suggestedaccountingaccountfor = ''; if (($objp->country_code == $mysoc->country_code) || empty($objp->country_code)) { // If buyer in same country than seller (if not defined, we assume it is same country) $objp->code_buy_p = $objp->code_buy; @@ -169,6 +170,13 @@ if ($action == 'validatehistory') { } } + // Level 3: Search suggested account for this thirdparty (similar code exists in page index.php to make automatic binding) + if (!empty($objp->company_code_buy)) { + $objp->code_buy_t = $objp->company_code_buy; + $objp->aarowid_suggest = $objp->aarowid_thirdparty; + $suggestedaccountingaccountfor = ''; + } + if ($objp->aarowid_suggest > 0) { $sqlupdate = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det"; diff --git a/htdocs/accountancy/supplier/list.php b/htdocs/accountancy/supplier/list.php index cbdecd2e85e..6f1f4c25a9f 100644 --- a/htdocs/accountancy/supplier/list.php +++ b/htdocs/accountancy/supplier/list.php @@ -215,9 +215,9 @@ $sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, $sql .= " p.accountancy_code_sell as code_sell, p.accountancy_code_sell_intra as code_sell_intra, p.accountancy_code_sell_export as code_sell_export,"; $sql .= " p.accountancy_code_buy as code_buy, p.accountancy_code_buy_intra as code_buy_intra, p.accountancy_code_buy_export as code_buy_export,"; $sql .= " p.tosell as status, p.tobuy as status_buy,"; -$sql .= " aa.rowid as aarowid, aa2.rowid as aarowid_intra, aa3.rowid as aarowid_export,"; +$sql .= " aa.rowid as aarowid, aa2.rowid as aarowid_intra, aa3.rowid as aarowid_export, aa4.rowid as aarowid_thirdparty,"; $sql .= " co.code as country_code, co.label as country_label,"; -$sql .= " s.rowid as socid, s.nom as name, s.tva_intra, s.email, s.town, s.zip, s.fk_pays, s.client, s.fournisseur, s.code_client, s.code_fournisseur, s.code_compta as code_compta_client, s.code_compta_fournisseur"; +$sql .= " s.rowid as socid, s.nom as name, s.tva_intra, s.email, s.town, s.zip, s.fk_pays, s.client, s.fournisseur, s.code_client, s.code_fournisseur, s.code_compta as code_compta_client, s.code_compta_fournisseur, s.accountancy_code_buy as company_code_buy"; $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; @@ -229,6 +229,7 @@ $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = l.fk_product"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa ON p.accountancy_code_buy = aa.account_number AND aa.active = 1 AND aa.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa.entity = ".$conf->entity; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa2 ON p.accountancy_code_buy_intra = aa2.account_number AND aa2.active = 1 AND aa2.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa2.entity = ".$conf->entity; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa3 ON p.accountancy_code_buy_export = aa3.account_number AND aa3.active = 1 AND aa3.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa3.entity = ".$conf->entity; +$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."accounting_account as aa4 ON s.accountancy_code_buy = aa4.account_number AND aa4.active = 1 AND aa4.fk_pcg_version = '".$db->escape($chartaccountcode)."' AND aa4.entity = ".$conf->entity; $sql .= " WHERE f.fk_statut > 0 AND l.fk_code_ventilation <= 0"; $sql .= " AND l.product_type <= 2"; // Define begin binding date @@ -460,10 +461,12 @@ if ($result) { $facturefourn_static->label = $objp->invoice_label; $code_buy_p_notset = ''; + $code_buy_t_notset = ''; $objp->aarowid_suggest = ''; // Will be set later $isSellerInEEC = isInEEC($objp); + // Level 1: Search suggested default account for product/service $suggestedaccountingaccountbydefaultfor = ''; if ($objp->type_l == 1) { if ($objp->country_code == $mysoc->country_code || empty($objp->country_code)) { // If buyer in same country than seller (if not defined, we assume it is same country) @@ -494,7 +497,7 @@ if ($result) { } if ($objp->code_sell_l == -1) $objp->code_sell_l = ''; - // Search suggested account for product/service + // Level 2: Search suggested account for product/service (similar code exists in page index.php to make automatic binding) $suggestedaccountingaccountfor = ''; if (($objp->country_code == $mysoc->country_code) || empty($objp->country_code)) { // If buyer in same country than seller (if not defined, we assume it is same country) $objp->code_buy_p = $objp->code_buy; @@ -512,6 +515,13 @@ if ($result) { } } + // Level 3: Search suggested account for this thirdparty (similar code exists in page index.php to make automatic binding) + if (!empty($objp->company_code_buy)) { + $objp->code_buy_t = $objp->company_code_buy; + $objp->aarowid_suggest = $objp->aarowid_thirdparty; + $suggestedaccountingaccountfor = ''; + } + if (!empty($objp->code_buy_p)) { // Value was defined previously } else { @@ -521,6 +531,7 @@ if ($result) { // $objp->code_buy_l is now default code of product/service // $objp->code_buy_p is now code of product/service + // $objp->code_buy_t is now code of thirdparty print ''; @@ -576,7 +587,7 @@ if ($result) { // Found accounts print ''; - $s = ''.(($objp->type_l == 1) ? $langs->trans("DefaultForService") : $langs->trans("DefaultForProduct")).': '; + $s = '1. '.(($objp->type_l == 1) ? $langs->trans("DefaultForService") : $langs->trans("DefaultForProduct")).': '; $shelp = ''; if ($suggestedaccountingaccountbydefaultfor == 'eec') $shelp .= $langs->trans("SaleEEC"); elseif ($suggestedaccountingaccountbydefaultfor == 'export') $shelp .= $langs->trans("SaleExport"); @@ -585,13 +596,24 @@ if ($result) { if ($objp->product_id > 0) { print '
'; - $s = ''.(($objp->type_l == 1) ? $langs->trans("ThisService") : $langs->trans("ThisProduct")).': '; + $s = '2. '.(($objp->type_l == 1) ? $langs->trans("ThisService") : $langs->trans("ThisProduct")).': '; $shelp = ''; if ($suggestedaccountingaccountfor == 'eec') $shelp = $langs->trans("SaleEEC"); elseif ($suggestedaccountingaccountfor == 'export') $shelp = $langs->trans("SaleExport"); $s .= (empty($objp->code_buy_p) ? ''.$langs->trans("NotDefined").'' : length_accountg($objp->code_buy_p)); print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); + } else { + print '
'; + $s = '2. '.(($objp->type_l == 1) ? $langs->trans("ThisService") : $langs->trans("ThisProduct")).': '; + $shelp = ''; + $s .= $langs->trans("NotDefined"); + print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); } + print '
'; + $s = '3. '.$langs->trans("DefaultForThirdparty").': '; + $shelp = ''; + $s .= ($objp->code_buy_t > 0 ? length_accountg($objp->code_buy_t) : ''.$langs->trans("NotDefined").''); + print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); print ''; // Suggested accounting account diff --git a/htdocs/install/mysql/migration/13.0.0-14.0.0.sql b/htdocs/install/mysql/migration/13.0.0-14.0.0.sql new file mode 100644 index 00000000000..706ead91506 --- /dev/null +++ b/htdocs/install/mysql/migration/13.0.0-14.0.0.sql @@ -0,0 +1,36 @@ +-- +-- Be carefull to requests order. +-- This file must be loaded by calling /install/index.php page +-- when current version is 14.0.0 or higher. +-- +-- To restrict request to Mysql version x.y minimum use -- VMYSQLx.y +-- To restrict request to Pgsql version x.y minimum use -- VPGSQLx.y +-- To rename a table: ALTER TABLE llx_table RENAME TO llx_table_new; +-- To add a column: ALTER TABLE llx_table ADD COLUMN newcol varchar(60) NOT NULL DEFAULT '0' AFTER existingcol; +-- To rename a column: ALTER TABLE llx_table CHANGE COLUMN oldname newname varchar(60); +-- To drop a column: ALTER TABLE llx_table DROP COLUMN oldname; +-- To change type of field: ALTER TABLE llx_table MODIFY COLUMN name varchar(60); +-- To drop a foreign key: ALTER TABLE llx_table DROP FOREIGN KEY fk_name; +-- To create a unique index ALTER TABLE llx_table ADD UNIQUE INDEX uk_table_field (field); +-- To drop an index: -- VMYSQL4.1 DROP INDEX nomindex on llx_table +-- To drop an index: -- VPGSQL8.2 DROP INDEX nomindex +-- To make pk to be auto increment (mysql): -- VMYSQL4.3 ALTER TABLE llx_table CHANGE COLUMN rowid rowid INTEGER NOT NULL AUTO_INCREMENT; +-- To make pk to be auto increment (postgres): +-- -- VPGSQL8.2 CREATE SEQUENCE llx_table_rowid_seq OWNED BY llx_table.rowid; +-- -- VPGSQL8.2 ALTER TABLE llx_table ADD PRIMARY KEY (rowid); +-- -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN rowid SET DEFAULT nextval('llx_table_rowid_seq'); +-- -- VPGSQL8.2 SELECT setval('llx_table_rowid_seq', MAX(rowid)) FROM llx_table; +-- To set a field as NULL: -- VMYSQL4.3 ALTER TABLE llx_table MODIFY COLUMN name varchar(60) NULL; +-- To set a field as NULL: -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name DROP NOT NULL; +-- To set a field as NOT NULL: -- VMYSQL4.3 ALTER TABLE llx_table MODIFY COLUMN name varchar(60) NOT NULL; +-- To set a field as NOT NULL: -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name SET NOT NULL; +-- To set a field as default NULL: -- VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name SET DEFAULT NULL; +-- Note: fields with type BLOB/TEXT can't have default value. + + +-- Missing in v13 + +-- For v14 + +ALTER TABLE llx_societe ADD COLUMN accountancy_code_sell varchar(32) AFTER webservices_key; +ALTER TABLE llx_societe ADD COLUMN accountancy_code_buy varchar(32) AFTER accountancy_code_sell; diff --git a/htdocs/install/mysql/tables/llx_societe.sql b/htdocs/install/mysql/tables/llx_societe.sql index ea02b7be8ad..27503d82727 100644 --- a/htdocs/install/mysql/tables/llx_societe.sql +++ b/htdocs/install/mysql/tables/llx_societe.sql @@ -116,6 +116,9 @@ create table llx_societe webservices_url varchar(255), -- supplier webservice url webservices_key varchar(128), -- supplier webservice key + accountancy_code_sell varchar(32), -- Selling accountancy code + accountancy_code_buy varchar(32), -- Buying accountancy code + tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- last modification date datec datetime, -- creation date fk_user_creat integer NULL, -- utilisateur qui a cree l'info diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 3211a0b62df..32f7949f3ef 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -16,6 +16,7 @@ ThisService=This service ThisProduct=This product DefaultForService=Default for service DefaultForProduct=Default for product +DefaultForThirdparty=Default for thirdparty CantSuggest=Can't suggest AccountancySetupDoneFromAccountancyMenu=Most setup of the accountancy is done from the menu %s ConfigAccountingExpert=Configuration of the module accounting (double entry) diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php index 7d7dcbe86c3..c49d6115cc5 100644 --- a/htdocs/societe/card.php +++ b/htdocs/societe/card.php @@ -46,6 +46,9 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; if (!empty($conf->adherent->enabled)) require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; +if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; +if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php'; +if (! empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php'; $langs->loadLangs(array("companies", "commercial", "bills", "banks", "users")); if (!empty($conf->adherent->enabled)) $langs->load("members"); @@ -476,6 +479,9 @@ if (empty($reshook)) $object->webservices_url = GETPOST('webservices_url', 'custom', 0, FILTER_SANITIZE_URL); $object->webservices_key = GETPOST('webservices_key', 'san_alpha'); + $object->accountancy_code_sell = GETPOST('accountancy_code_sell', 'alpha'); + $object->accountancy_code_buy = GETPOST('accountancy_code_buy', 'alpha'); + // Incoterms if (!empty($conf->incoterm->enabled)) { @@ -890,6 +896,7 @@ $form = new Form($db); $formfile = new FormFile($db); $formadmin = new FormAdmin($db); $formcompany = new FormCompany($db); +if (! empty($conf->accounting->enabled)) $formaccounting = new FormAccounting($db); if ($socid > 0 && empty($object->id)) { @@ -1030,6 +1037,9 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) $object->commercial_id = GETPOST('commercial_id', 'int'); $object->default_lang = GETPOST('default_lang'); + $object->accountancy_code_sell = GETPOST('accountancy_code_sell', 'alpha'); + $object->accountancy_code_buy = GETPOST('accountancy_code_buy', 'alpha'); + $object->logo = (isset($_FILES['photo']) ?dol_sanitizeFileName($_FILES['photo']['name']) : ''); // Gestion du logo de la société @@ -1546,6 +1556,47 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) print ''."\n"; + // Accountancy codes + print ''; + + if (! empty($conf->accounting->enabled)) + { + // Accountancy_code_sell + print ''; + print ''; + + // Accountancy_code_buy + print ''; + print ''; + } + else // For external software + { + // Accountancy_code_sell + print ''; + print ''; + + // Accountancy_code_buy + print ''; + print ''; + } + print '
'.$langs->trans("ProductAccountancySellCode").''; + if($type == 0) { + $accountancy_code_sell = (GETPOST('accountancy_code_sell', 'alpha')?(GETPOST('accountancy_code_sell', 'alpha')):$conf->global->ACCOUNTING_PRODUCT_SOLD_ACCOUNT); + } else { + $accountancy_code_sell = (GETPOST('accountancy_code_sell', 'alpha')?(GETPOST('accountancy_code_sell', 'alpha')):$conf->global->ACCOUNTING_SERVICE_SOLD_ACCOUNT); + } + print $formaccounting->select_account($accountancy_code_sell, 'accountancy_code_sell', 1, null, 1, 1, ''); + print '
'.$langs->trans("ProductAccountancyBuyCode").''; + if($type == 0) { + $accountancy_code_buy = (GETPOST('accountancy_code_buy', 'alpha')?(GETPOST('accountancy_code_buy', 'alpha')):$conf->global->ACCOUNTING_PRODUCT_BUY_ACCOUNT); + } else { + $accountancy_code_buy = (GETPOST('accountancy_code_buy', 'alpha')?(GETPOST('accountancy_code_buy', 'alpha')):$conf->global->ACCOUNTING_SERVICE_BUY_ACCOUNT); + } + print $formaccounting->select_account($accountancy_code_buy, 'accountancy_code_buy', 1, null, 1, 1, ''); + print '
'.$langs->trans("ProductAccountancySellCode").''; + print '
'.$langs->trans("ProductAccountancyBuyCode").''; + print '
'; + print dol_get_fiche_end(); print '
'; @@ -1661,6 +1712,9 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) $object->webservices_url = GETPOST('webservices_url', 'custom', 0, FILTER_SANITIZE_URL); $object->webservices_key = GETPOST('webservices_key', 'san_alpha'); + $object->accountancy_code_sell = GETPOST('accountancy_code_sell', 'alpha'); + $object->accountancy_code_buy = GETPOST('accountancy_code_buy', 'alpha'); + //Incoterms if (!empty($conf->incoterm->enabled)) { @@ -2188,10 +2242,42 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) print $form->multiselectarray('commercial', $userlist, $arrayselected, null, null, null, null, "90%"); print ''; + print ''; + + print '
'; + + print ''; + + if (! empty($conf->accounting->enabled)) + { + // Accountancy_code_sell + print ''; + print ''; + + // Accountancy_code_buy + print ''; + print ''; + } + else // For external software + { + // Accountancy_code_sell + print ''; + print ''; + + // Accountancy_code_buy + print ''; + print ''; + } print '
'.$langs->trans("ProductAccountancySellCode").''; + print $formaccounting->select_account($object->accountancy_code_sell, 'accountancy_code_sell', 1, '', 1, 1); + print '
'.$langs->trans("ProductAccountancyBuyCode").''; + print $formaccounting->select_account($object->accountancy_code_buy, 'accountancy_code_buy', 1, '', 1, 1); + print '
'.$langs->trans("ProductAccountancySellCode").''; + print '
'.$langs->trans("ProductAccountancyBuyCode").''; + print '
'; print '
'; - print dol_get_fiche_end(); + print dol_get_fiche_end(); print '
'; print ''; @@ -2550,6 +2636,42 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) print ''; } + // Accountancy sell code + print ''; + print $langs->trans("ProductAccountancySellCode"); + print ''; + if (! empty($conf->accounting->enabled)) + { + if (! empty($object->accountancy_code_sell)) + { + $accountingaccount = new AccountingAccount($db); + $accountingaccount->fetch('', $object->accountancy_code_sell, 1); + + print $accountingaccount->getNomUrl(0, 1, 1, '', 1); + } + } else { + print $object->accountancy_code_sell; + } + print ''; + + // Accountancy buy code + print ''; + print $langs->trans("ProductAccountancyBuyCode"); + print ''; + if (! empty($conf->accounting->enabled)) + { + if (! empty($object->accountancy_code_buy)) + { + $accountingaccount2 = new AccountingAccount($db); + $accountingaccount2->fetch('', $object->accountancy_code_buy, 1); + + print $accountingaccount2->getNomUrl(0, 1, 1, '', 1); + } + } else { + print $object->accountancy_code_buy; + } + print ''; + // Other attributes $parameters = array('socid'=>$socid, 'colspan' => ' colspan="3"', 'colspanvalue' => '3'); include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php'; diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index 73c41aeb782..b3b3497dcf5 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -693,6 +693,15 @@ class Societe extends CommonObject */ public $logo_squarred_mini; + /** + * @var string Accountancy account for sales + */ + public $accountancy_code_sell; + + /** + * @var string Accountancy account for bought + */ + public $accountancy_code_buy; // Multicurrency /** @@ -792,6 +801,9 @@ class Societe extends CommonObject if (empty($this->fournisseur)) $this->fournisseur = 0; $this->import_key = trim($this->import_key); + $this->accountancy_code_buy = trim($this->accountancy_code_buy); + $this->accountancy_code_sell= trim($this->accountancy_code_sell); + if (!empty($this->multicurrency_code)) $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code); if (empty($this->fk_multicurrency)) { @@ -817,8 +829,24 @@ class Societe extends CommonObject { $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity); - $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe (nom, name_alias, entity, datec, fk_user_creat, canvas, status, ref_ext, fk_stcomm, fk_incoterms, location_incoterms ,import_key, fk_multicurrency, multicurrency_code)"; - $sql .= " VALUES ('".$this->db->escape($this->name)."', '".$this->db->escape($this->name_alias)."', ".$this->db->escape($this->entity).", '".$this->db->idate($now)."'"; + $sql = "INSERT INTO ".MAIN_DB_PREFIX."societe ("; + $sql .= "nom"; + $sql .= ", name_alias"; + $sql .= ", entity"; + $sql .= ", datec"; + $sql .= ", fk_user_creat"; + $sql .= ", canvas"; + $sql .= ", status"; + $sql .= ", ref_ext"; + $sql .= ", fk_stcomm"; + $sql .= ", fk_incoterms"; + $sql .= ", location_incoterms"; + $sql .= ", import_key"; + $sql .= ", fk_multicurrency"; + $sql .= ", multicurrency_code"; + $sql .= ", accountancy_code_buy"; + $sql .= ", accountancy_code_sell"; + $sql .= ") VALUES ('".$this->db->escape($this->name)."', '".$this->db->escape($this->name_alias)."', ".$this->db->escape($this->entity).", '".$this->db->idate($now)."'"; $sql .= ", ".(!empty($user->id) ? ((int) $user->id) : "null"); $sql .= ", ".(!empty($this->canvas) ? "'".$this->db->escape($this->canvas)."'" : "null"); $sql .= ", ".$this->status; @@ -828,7 +856,10 @@ class Societe extends CommonObject $sql .= ", '".$this->db->escape($this->location_incoterms)."'"; $sql .= ", ".(!empty($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null"); $sql .= ", ".(int) $this->fk_multicurrency; - $sql .= ", '".$this->db->escape($this->multicurrency_code)."')"; + $sql .= ", '".$this->db->escape($this->multicurrency_code)."'"; + $sql .= ", '".$this->db->escape($this->accountancy_code_buy)."'"; + $sql .= ", '".$this->db->escape($this->accountancy_code_sell)."'"; + $sql .= ")"; dol_syslog(get_class($this)."::create", LOG_DEBUG); $result = $this->db->query($sql); @@ -1196,6 +1227,9 @@ class Societe extends CommonObject $this->webservices_url = $this->webservices_url ?clean_url($this->webservices_url, 0) : ''; $this->webservices_key = trim($this->webservices_key); + $this->accountancy_code_buy = trim($this->accountancy_code_buy); + $this->accountancy_code_sell= trim($this->accountancy_code_sell); + //Incoterms $this->fk_incoterms = (int) $this->fk_incoterms; $this->location_incoterms = trim($this->location_incoterms); @@ -1320,6 +1354,9 @@ class Societe extends CommonObject $sql .= ",supplier_order_min_amount= ".($this->supplier_order_min_amount != '' ? $this->supplier_order_min_amount : 'null'); $sql .= ",fk_prospectlevel='".$this->db->escape($this->fk_prospectlevel)."'"; + $sql.= ", accountancy_code_buy = '" . $this->db->escape($this->accountancy_code_buy)."'"; + $sql.= ", accountancy_code_sell= '" . $this->db->escape($this->accountancy_code_sell)."'"; + $sql .= ",webservices_url = ".(!empty($this->webservices_url) ? "'".$this->db->escape($this->webservices_url)."'" : "null"); $sql .= ",webservices_key = ".(!empty($this->webservices_key) ? "'".$this->db->escape($this->webservices_key)."'" : "null"); @@ -1505,6 +1542,7 @@ class Societe extends CommonObject $sql .= ', s.fk_effectif as effectif_id'; $sql .= ', s.fk_forme_juridique as forme_juridique_code'; $sql .= ', s.webservices_url, s.webservices_key'; + $sql .= ', s.accountancy_code_buy, s.accountancy_code_sell'; $sql .= ', s.code_client, s.code_fournisseur, s.code_compta, s.code_compta_fournisseur, s.parent, s.barcode'; $sql .= ', s.fk_departement as state_id, s.fk_pays as country_id, s.fk_stcomm, s.remise_supplier, s.mode_reglement, s.cond_reglement, s.transport_mode'; $sql .= ', s.fk_account, s.tva_assuj'; @@ -1668,6 +1706,9 @@ class Societe extends CommonObject $this->webservices_url = $obj->webservices_url; $this->webservices_key = $obj->webservices_key; + $this->accountancy_code_buy = $obj->accountancy_code_buy; + $this->accountancy_code_sell = $obj->accountancy_code_sell; + $this->outstanding_limit = $obj->outstanding_limit; $this->order_min_amount = $obj->order_min_amount; $this->supplier_order_min_amount = $obj->supplier_order_min_amount; @@ -4403,4 +4444,58 @@ class Societe extends CommonObject return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables); } + + /** + * Sets an accountancy code for a thirdparty. + * Also calls COMPANY_MODIFY trigger when modified + * + * @param string $type It can be only 'buy' or 'sell' + * @param string $value Accountancy code + * @return int <0 KO >0 OK + */ + public function setAccountancyCode($type, $value) + { + global $user, $langs, $conf; + + $this->db->begin(); + + if ($type == 'buy') { + $field = 'accountancy_code_buy'; + } elseif ($type == 'sell') { + $field = 'accountancy_code_sell'; + } else { + return -1; + } + + $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET "; + $sql.= "$field = '".$this->db->escape($value)."'"; + $sql.= " WHERE rowid = ".$this->id; + + dol_syslog(get_class($this)."::".__FUNCTION__." sql=".$sql, LOG_DEBUG); + $resql = $this->db->query($sql); + + if ($resql) { + // Call triggers + include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + $interface=new Interfaces($this->db); + $result=$interface->run_triggers('COMPANY_MODIFY', $this, $user, $langs, $conf); + if ($result < 0) { + $this->errors=$interface->errors; + $this->db->rollback(); + return -1; + } + // End call triggers + + $this->$field = $value; + + $this->db->commit(); + return 1; + } + else + { + $this->error=$this->db->lasterror(); + $this->db->rollback(); + return -1; + } + } } From d6ac26dd8595d256b31ae5a599b2e6156975ae66 Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Fri, 4 Dec 2020 03:23:48 +0000 Subject: [PATCH 063/743] Fixing style errors. --- htdocs/societe/card.php | 4 ++-- htdocs/societe/class/societe.class.php | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php index c49d6115cc5..0e64117d632 100644 --- a/htdocs/societe/card.php +++ b/htdocs/societe/card.php @@ -1564,7 +1564,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) // Accountancy_code_sell print ''.$langs->trans("ProductAccountancySellCode").''; print ''; - if($type == 0) { + if ($type == 0) { $accountancy_code_sell = (GETPOST('accountancy_code_sell', 'alpha')?(GETPOST('accountancy_code_sell', 'alpha')):$conf->global->ACCOUNTING_PRODUCT_SOLD_ACCOUNT); } else { $accountancy_code_sell = (GETPOST('accountancy_code_sell', 'alpha')?(GETPOST('accountancy_code_sell', 'alpha')):$conf->global->ACCOUNTING_SERVICE_SOLD_ACCOUNT); @@ -1575,7 +1575,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) // Accountancy_code_buy print ''.$langs->trans("ProductAccountancyBuyCode").''; print ''; - if($type == 0) { + if ($type == 0) { $accountancy_code_buy = (GETPOST('accountancy_code_buy', 'alpha')?(GETPOST('accountancy_code_buy', 'alpha')):$conf->global->ACCOUNTING_PRODUCT_BUY_ACCOUNT); } else { $accountancy_code_buy = (GETPOST('accountancy_code_buy', 'alpha')?(GETPOST('accountancy_code_buy', 'alpha')):$conf->global->ACCOUNTING_SERVICE_BUY_ACCOUNT); diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index b3b3497dcf5..c8fc2f58e74 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -4491,8 +4491,7 @@ class Societe extends CommonObject $this->db->commit(); return 1; } - else - { + else { $this->error=$this->db->lasterror(); $this->db->rollback(); return -1; From 401d61dd91984cb4120b5ad33b1127bc8633189e Mon Sep 17 00:00:00 2001 From: lmarcouiller Date: Tue, 8 Dec 2020 09:05:31 +0100 Subject: [PATCH 064/743] resolve auto import by IDE --- htdocs/core/boxes/box_dolibarr_state_board.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/htdocs/core/boxes/box_dolibarr_state_board.php b/htdocs/core/boxes/box_dolibarr_state_board.php index 6f9190dda1e..a8e00eef5eb 100644 --- a/htdocs/core/boxes/box_dolibarr_state_board.php +++ b/htdocs/core/boxes/box_dolibarr_state_board.php @@ -24,8 +24,6 @@ * \brief Module Dolibarr state base */ -use Symfony\Component\VarDumper\VarDumper; - include_once DOL_DOCUMENT_ROOT . '/core/boxes/modules_boxes.php'; include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php'; From 54fad02936cfc37624128117a54c22d9e48bd7db Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Tue, 8 Dec 2020 13:55:51 +0100 Subject: [PATCH 065/743] Fix PR --- htdocs/accountancy/customer/list.php | 2 +- htdocs/accountancy/supplier/list.php | 2 +- htdocs/langs/en_US/accountancy.lang | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/htdocs/accountancy/customer/list.php b/htdocs/accountancy/customer/list.php index 88464dcf95d..cb47e436cbd 100644 --- a/htdocs/accountancy/customer/list.php +++ b/htdocs/accountancy/customer/list.php @@ -631,7 +631,7 @@ if ($result) { print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); } print '
'; - $s = '3. '.$langs->trans("DefaultForThirdparty").': '; + $s = '3. '.(($objp->type_l == 1) ? $langs->trans("ServiceForThisThirdparty") : $langs->trans("ProductForThisThirdparty")).': '; $shelp = ''; $s .= ($objp->code_sell_t > 0 ? length_accountg($objp->code_sell_t) : ''.$langs->trans("NotDefined").''); print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); diff --git a/htdocs/accountancy/supplier/list.php b/htdocs/accountancy/supplier/list.php index 6f1f4c25a9f..919130e80d3 100644 --- a/htdocs/accountancy/supplier/list.php +++ b/htdocs/accountancy/supplier/list.php @@ -610,7 +610,7 @@ if ($result) { print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); } print '
'; - $s = '3. '.$langs->trans("DefaultForThirdparty").': '; + $s = '3. '.(($objp->type_l == 1) ? $langs->trans("ServiceForThisThirdparty") : $langs->trans("ProductForThisThirdparty")).': '; $shelp = ''; $s .= ($objp->code_buy_t > 0 ? length_accountg($objp->code_buy_t) : ''.$langs->trans("NotDefined").''); print $form->textwithpicto($s, $shelp, 1, 'help', '', 0, 2, '', 1); diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 32f7949f3ef..d0fbc1644b3 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -16,7 +16,8 @@ ThisService=This service ThisProduct=This product DefaultForService=Default for service DefaultForProduct=Default for product -DefaultForThirdparty=Default for thirdparty +ProductForThisThirdparty=Product for this thirdparty +ServiceForThisThirdparty=Service for this thirdparty CantSuggest=Can't suggest AccountancySetupDoneFromAccountancyMenu=Most setup of the accountancy is done from the menu %s ConfigAccountingExpert=Configuration of the module accounting (double entry) From f161b47cba0eae757700aa88c8c59b938e46432e Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Thu, 10 Dec 2020 01:45:08 +0100 Subject: [PATCH 066/743] Debug v13 --- ChangeLog | 6 +++--- htdocs/theme/eldy/info-box.inc.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 20b3a514342..fbb36f221c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -56,7 +56,6 @@ NEW: add option TAKEPOS_CAN_FORCE_BANK_ACCOUNT_DURING_PAYMENT NEW: add option to define a default warehouse at user level NEW: add option to include products without alert in replenish NEW: add order by lastname and firstname by default in get sales representatives -NEW: add param to not show links when output tags NEW: add PDF document templates for warehouses (list of stock) NEW: add a prospect status for the contact with managment of custom icon NEW: add public note on products ; this also partially fix the #14342 @@ -79,7 +78,7 @@ NEW: appearance tab in TakePOS with more visual parameters NEW: better currency rate editor NEW: can build vendor invoice from vendor orders NEW: can change a product in lines of a recurring invoice or contract -NEW: can change size of logo on PDF documents +NEW: can set the size of the logo on PDF documents NEW: can change VAT rate of all lines of a draft object in one step NEW: can define date range of validity of a login during creation NEW: can disable, from edit page, the whole web site @@ -188,7 +187,7 @@ NEW: show line number on intervention card (via MAIN_VIEW_LINE_NUMBER) NEW: Add some fields to link website page to an other object NEW: fill ECM src object fields in dol_add_file_process NEW: conf to allow to show the full tree in warehouse popup -NEW: can use THEME_DARKMODEENABLED=2 for a preview of theme in dark mode +NEW: can use parameter ?THEME_DARKMODEENABLED=2 for a preview of theme in dark mode NEW: can force the antivirus from conf file or autoprepend ini setup NEW: can add event to log into blockedlog module with a constant NEW: add property cssview when declaring fields of an object @@ -212,6 +211,7 @@ NEW: add a message in error_log after detection of SQL or script injection NEW: add validation of MX domain for emails NEW: calculate the virtual stock in transverse mode ( not on getEntity('commande'), ... but on getEntity('stock') ) NEW: Graphics can be horizontal bars +NEW: add param to not show links when output tags APIs NEW: API get contacts list of a given order diff --git a/htdocs/theme/eldy/info-box.inc.php b/htdocs/theme/eldy/info-box.inc.php index 1bb6ae7c67e..2da8de5cff3 100644 --- a/htdocs/theme/eldy/info-box.inc.php +++ b/htdocs/theme/eldy/info-box.inc.php @@ -25,7 +25,7 @@ if (!defined('ISLOADEDBYSTEELSHEET')) die('Must be call by steelsheet'); ?> .info-box.info-box-sm { min-height: 80px; margin-bottom: 10px; - background: #fff; + /* background: #fff; */ } .opened-dash-board-wrap .info-box.info-box-sm { border-radius: 0 0 0 20px; From 44d3bc2e646292a09d858a7ba73d1679b82ec24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20FRANCE?= Date: Mon, 14 Dec 2020 08:39:08 +0100 Subject: [PATCH 067/743] add work --- htdocs/intracommreport/card.php | 55 +++++++++++-------- .../class/intracommreport.class.php | 47 +++++++++++++--- htdocs/intracommreport/list.php | 4 +- 3 files changed, 75 insertions(+), 31 deletions(-) diff --git a/htdocs/intracommreport/card.php b/htdocs/intracommreport/card.php index f0a4bd5f070..077f793d649 100644 --- a/htdocs/intracommreport/card.php +++ b/htdocs/intracommreport/card.php @@ -27,28 +27,41 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions.lib.php'; require_once DOL_DOCUMENT_ROOT.'/intracommreport/class/intracommreport.class.php'; $langs->loadLangs(array("intracommreport")); - +var_dump($_POST); $action = GETPOST('action'); -$exporttype = GETPOST('exporttype'); // DEB ou DES -if (empty($exporttype)) { - $exporttype = 'deb'; -} - -$form = new Form($db); -$formother = new FormOther($db); -$year = GETPOST('year'); -$month = GETPOST('month'); +$exporttype = GETPOSTISSET('exporttype') ? GETPOST('exporttype', 'alphanohtml') : 'deb'; // DEB ou DES +$year = GETPOSTINT('year'); +$month = GETPOSTINT('month'); $label = (string) GETPOST('label', 'alphanohtml'); -$type_declaration = GETPOSTINT('type'); +$type_declaration = (string) GETPOST('type_declaration', 'alphanohtml'); $backtopage = GETPOST('backtopage', 'alpha'); $declaration = array( "deb" => $langs->trans("DEB"), "des" => $langs->trans("DES"), ); +$typeOfDeclaration = array( + "introduction" => $langs->trans("Introduction"), + "expedition" => $langs->trans("Expedition"), +); +$object = new IntracommReport($db); +if ($id > 0) { + $object->fetch($id); +} +$form = new Form($db); +$formother = new FormOther($db); + +// Initialize technical object to manage hooks. Note that conf->hooks_modules contains array +$hookmanager->initHooks(array('intracommcard', 'globalcard')); /* * Actions */ +$parameters = array('id' => $id); +// Note that $action and $object may have been modified by some hooks +$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); +if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); +} if ($user->rights->intracommreport->delete && $action == 'confirm_delete' && $confirm == 'yes') { $result = $object->delete($id, $user); @@ -72,16 +85,16 @@ if ($action == 'add' && $user->rights->intracommreport->write) { $object->subscription = (int) $subscription; // Fill array 'array_options' with data from add form - $ret = $extrafields->setOptionalsFromPost($extralabels, $object); - if ($ret < 0) { - $error++; - } + // $ret = $extrafields->setOptionalsFromPost($extralabels, $object); + // if ($ret < 0) { + // $error++; + // } if (empty($object->label)) { $error++; setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Label")), null, 'errors'); } else { - $sql = "SELECT libelle FROM ".MAIN_DB_PREFIX."adherent_type WHERE libelle='".$db->escape($object->label)."'"; + $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."intracommreport WHERE ref='".$db->escape($object->label)."'"; $result = $db->query($sql); if ($result) { $num = $db->num_rows($result); @@ -96,7 +109,7 @@ if ($action == 'add' && $user->rights->intracommreport->write) { if (!$error) { $id = $object->create($user); if ($id > 0) { - header("Location: ".$_SERVER["PHP_SELF"]); + header("Location: ".$_SERVER["PHP_SELF"].'?id='.$id); exit; } else { setEventMessages($object->error, $object->errors, 'errors'); @@ -145,10 +158,8 @@ if ($action == 'create') { print ''; // Type of declaration - $typeOfDeclaration["introduction"] = $langs->trans("Introduction"); - $typeOfDeclaration["expedition"] = $langs->trans("Expedition"); print ''.$langs->trans("TypeOfDeclaration")."\n"; - print $form->selectarray("type_declaration", $typeOfDeclaration, GETPOST('type_declaration', 'alpha') ?GETPOST('type_declaration', 'alpha') : $object->type_declaration, 0); + print $form->selectarray("type_declaration", $typeOfDeclaration, GETPOST('type_declaration', 'alpha') ? GETPOST('type_declaration', 'alpha') : $object->type_declaration, 0); print "\n"; print ''; @@ -177,9 +188,9 @@ if ($id > 0 && $action != 'edit') { /* * Show tabs */ - $head = intracommreport_prepare_head($object); + //$head = intracommreport_prepare_head($object); - print dol_get_fiche_head($head, 'general', $langs->trans("IntracommReport"), -1, 'user'); + print dol_get_fiche_head("", 'general', $langs->trans("IntracommReport"), -1, 'user'); // Confirm remove report if ($action == 'delete') { diff --git a/htdocs/intracommreport/class/intracommreport.class.php b/htdocs/intracommreport/class/intracommreport.class.php index d1145dcf806..0cf5a71c27a 100644 --- a/htdocs/intracommreport/class/intracommreport.class.php +++ b/htdocs/intracommreport/class/intracommreport.class.php @@ -81,13 +81,46 @@ class IntracommReport extends CommonObject $this->exporttype = 'deb'; } + /** + * Fonction create + * @param User $user User + * @param int $notrigger notrigger + * @return int + */ + public function create($user, $notrigger = 0) + { + return 1; + } + + /** + * Fonction fetch + * @param int $id object ID + * @return int + */ + public function fetch($id) + { + return 1; + } + + /** + * Fonction delete + * @param int $id object ID + * @param User $user User + * @param int $notrigger notrigger + * @return int + */ + public function delete($id, $user, $notrigger = 0) + { + return 1; + } + /** * Generate XML file * - * @param int $mode O for create, R for regenerate (Look always 0 ment toujours 0 within the framework of XML exchanges according to documentation) - * @param string $type Declaration type by default - introduction or expedition (always 'expedition' for Des) - * @param string $period_reference Period of reference - * @return void + * @param int $mode O for create, R for regenerate (Look always 0 ment toujours 0 within the framework of XML exchanges according to documentation) + * @param string $type Declaration type by default - introduction or expedition (always 'expedition' for Des) + * @param string $period_reference Period of reference + * @return SimpleXMLElement|int */ public function getXML($mode = 'O', $type = 'introduction', $period_reference = '') { @@ -149,7 +182,7 @@ class IntracommReport extends CommonObject * @param int $period_year Year of declaration * @param int $period_month Month of declaration * @param string $type_declaration Declaration type by default - introduction or expedition (always 'expedition' for Des) - * @return void + * @return SimpleXMLElement|int */ public function getXMLDes($period_year, $period_month, $type_declaration = 'expedition') { @@ -246,7 +279,7 @@ class IntracommReport extends CommonObject * @param string $type Declaration type by default - introduction or expedition (always 'expedition' for Des) * @param int $period_reference Reference declaration * @param string $exporttype deb=DEB, des=DES - * @return int <0 if KO, >0 if OK + * @return string <0 if KO, >0 if OK */ public function getSQLFactLines($type, $period_reference, $exporttype = 'deb') { @@ -325,7 +358,7 @@ class IntracommReport extends CommonObject * Add item for DES * * @param SimpleXMLElement $declaration Reference declaration - * @param esurce $res Result of request SQL + * @param Resource $res Result of request SQL * @param int $i Line Id * @return void */ diff --git a/htdocs/intracommreport/list.php b/htdocs/intracommreport/list.php index 1530e07d84e..d3346e344f4 100644 --- a/htdocs/intracommreport/list.php +++ b/htdocs/intracommreport/list.php @@ -121,8 +121,8 @@ $isInEEC = isInEEC($mysoc); // Definition of fields for lists $arrayfields = array( - 'i.ref'=>array('label'=>$langs->trans("Ref"), 'checked'=>1), - 'i.label'=>array('label'=>$langs->trans("Label"), 'checked'=>1), + 'i.ref' => array('label'=>$langs->trans("Ref"), 'checked'=>1), + 'i.label' => array('label'=>$langs->trans("Label"), 'checked'=>1), 'i.fk_product_type'=>array('label'=>$langs->trans("Type"), 'checked'=>0, 'enabled'=>(!empty($conf->produit->enabled) && !empty($conf->service->enabled))), ); /* From 5e4cc7fcfd6dee57b2c5e048b023c97b0fd3692b Mon Sep 17 00:00:00 2001 From: Stephane Lesage Date: Sun, 13 Dec 2020 19:50:35 +0100 Subject: [PATCH 068/743] companycard-directedit-typent --- htdocs/core/class/html.formcompany.class.php | 41 +++++++++++ htdocs/societe/card.php | 75 ++++++++++---------- htdocs/societe/class/societe.class.php | 25 +++++++ 3 files changed, 104 insertions(+), 37 deletions(-) diff --git a/htdocs/core/class/html.formcompany.class.php b/htdocs/core/class/html.formcompany.class.php index 0b4c4e499e3..1cd2ed5315c 100644 --- a/htdocs/core/class/html.formcompany.class.php +++ b/htdocs/core/class/html.formcompany.class.php @@ -1008,4 +1008,45 @@ class FormCompany extends Form return $out; } + + /** + * Output html select to select third-party type + * + * @param string $page Page + * @param string $selected Id preselected + * @param string $htmlname Name of HTML select + * @param string $filter optional filters criteras + * @param int $nooutput No print output. Return it only. + * @return void|string + */ + public function formThirdpartyType($page, $selected = '', $htmlname = 'socid', $filter = '', $nooutput = 0) + { + // phpcs:enable + global $langs; + + $out = ''; + if ($htmlname != "none") + { + $out .= '
'; + $out .= ''; + $out .= ''; + $sortparam = (empty($conf->global->SOCIETE_SORT_ON_TYPEENT) ? 'ASC' : $conf->global->SOCIETE_SORT_ON_TYPEENT); // NONE means we keep sort of original array, so we sort on position. ASC, means next function will sort on label. + $out .= $this->selectarray($htmlname, $this->typent_array(0, $filter), $selected, 0, 0, 0, '', 0, 0, 0, $sortparam, '', 1); + $out .= ''; + $out .= '
'; + } else { + if ($selected) + { + $arr = $this->typent_array(0); + $typent = $arr[$selected]; + $out .= $typent; + } else { + $out .= " "; + } + } + + if ($nooutput) return $out; + else print $out; + } + } diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php index ab2cddb940d..e1b77c8dfb2 100644 --- a/htdocs/societe/card.php +++ b/htdocs/societe/card.php @@ -838,18 +838,25 @@ if (empty($reshook)) } } + // Set third-party type + if ($action == 'set_thirdpartytype' && $user->rights->societe->creer) + { + $object->fetch($socid); + $result = $object->setThirdpartyType(GETPOST('typent_id', 'int')); + } + + // Set incoterm + if ($action == 'set_incoterms' && $user->rights->societe->creer && !empty($conf->incoterm->enabled)) + { + $object->fetch($socid); + $result = $object->setIncoterms(GETPOST('incoterm_id', 'int'), GETPOST('location_incoterms', 'alpha')); + } + // Set parent company if ($action == 'set_thirdparty' && $user->rights->societe->creer) { $object->fetch($socid); - $result = $object->set_parent(GETPOST('editparentcompany', 'int')); - } - - // Set incoterm - if ($action == 'set_incoterms' && !empty($conf->incoterm->enabled)) - { - $object->fetch($socid); - $result = $object->setIncoterms(GETPOST('incoterm_id', 'int'), GETPOST('location_incoterms', 'alpha')); + $result = $object->set_parent(GETPOST('parent_id', 'int')); } // Set sales representatives @@ -2455,18 +2462,24 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) } else { print ' '; } - print ''; - print ''; + print ''; - // Type + Workforce/Staff - $arr = $formcompany->typent_array(1); - $object->typent = $arr[$object->typent_code]; - print ''.$langs->trans("ThirdPartyType").''.$object->typent.''; + // Third-Party Type + print ''; + print ''; + if ($action != 'editthirdpartytype' && $user->rights->societe->creer) print ''; + print '
'.$langs->trans('ThirdPartyType').'id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'
'; + print ''; + $html_name = ($action == 'editthirdpartytype') ? 'typent_id' : 'none'; + $formcompany->formThirdpartyType($_SERVER['PHP_SELF'].'?socid='.$object->id, $object->typent_id, $html_name, ''); + print ''; + + // Workforce/Staff print ''.$langs->trans("Workforce").''.$object->effectif.''; print ''; - print '
'; + print '
'; print '
'; @@ -2518,14 +2531,10 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) if (!empty($conf->incoterm->enabled)) { print ''; - print '
'; - print $langs->trans('IncotermLabel'); - print ''; - if ($user->rights->societe->creer) print ''.img_edit('', 1).''; - else print ' '; - print '
'; - print ''; - print ''; + print ''; + if ($action != 'editincoterm' && $user->rights->societe->creer) print ''; + print '
'.$langs->trans('IncotermLabel').''.img_edit('', 1).'
'; + print ''; if ($action != 'editincoterm') { print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1); @@ -2553,20 +2562,13 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) if (empty($conf->global->SOCIETE_DISABLE_PARENTCOMPANY)) { print ''; - print ''; - if ($action != 'editparentcompany') print ''; + print '
'; - print $langs->trans('ParentCompany'); - print 'id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'
'; + if ($action != 'editparentcompany' && $user->rights->societe->creer) print ''; print '
'.$langs->trans('ParentCompany').'id.'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'
'; print ''; - if ($action == 'editparentcompany') - { - $form->form_thirdparty($_SERVER['PHP_SELF'].'?socid='.$object->id, $object->parent, 'editparentcompany', 's.rowid <> '.$object->id, 1); - } else { - $form->form_thirdparty($_SERVER['PHP_SELF'].'?socid='.$object->id, $object->parent, 'none', 's.rowid <> '.$object->id, 1); - } - print ''; - print ''; + $html_name = ($action == 'editparentcompany') ? 'parent_id' : 'none'; + $form->form_thirdparty($_SERVER['PHP_SELF'].'?socid='.$object->id, $object->parent, $html_name, 's.rowid <> '.$object->id, 1); + print ''; } // Sales representative @@ -2587,8 +2589,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) } else { print ''.$langs->trans("ThirdpartyNotLinkedToMember").''; } - print ''; - print "\n"; + print "\n"; } // Webservices url/key diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php index f18a6e1b9a8..f8796739701 100644 --- a/htdocs/societe/class/societe.class.php +++ b/htdocs/societe/class/societe.class.php @@ -4355,6 +4355,31 @@ class Societe extends CommonObject return $error ? -1 : 1; } + /** + * Define third-party type of current company + * + * @param int $typent_id third party type rowid in llx_c_typent + * @return int <0 if KO, >0 if OK + */ + public function setThirdpartyType($typent_id) + { + if ($this->id) + { + $sql = "UPDATE ".MAIN_DB_PREFIX."societe"; + $sql .= " SET fk_typent = ".($typent_id > 0 ? $typent_id : "null"); + $sql .= " WHERE rowid = ".$this->id; + dol_syslog(get_class($this).'::setThirdpartyType', LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + $this->typent_id = $typent_id; + $this->typent_code = dol_getIdFromCode($db, $this->$typent_id, 'c_typent', 'id', 'code'); + return 1; + } else { + return -1; + } + } else return -1; + } /** * Function used to replace a thirdparty id with another one. From d32d3060ccfc757fef8d89a5d74d94c36def8253 Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Mon, 14 Dec 2020 09:15:06 +0000 Subject: [PATCH 069/743] Fixing style errors. --- htdocs/core/class/html.formcompany.class.php | 1 - 1 file changed, 1 deletion(-) diff --git a/htdocs/core/class/html.formcompany.class.php b/htdocs/core/class/html.formcompany.class.php index 1cd2ed5315c..26e184eb614 100644 --- a/htdocs/core/class/html.formcompany.class.php +++ b/htdocs/core/class/html.formcompany.class.php @@ -1048,5 +1048,4 @@ class FormCompany extends Form if ($nooutput) return $out; else print $out; } - } From 5f9b53004ba56fa7a11c76323f74f993bc810515 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 14 Dec 2020 17:40:40 +0100 Subject: [PATCH 070/743] Fix warning --- htdocs/core/lib/functions.lib.php | 5 ++++- htdocs/core/modules/facture/doc/pdf_crabe.modules.php | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 37791d1e72d..023ade28052 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -6626,6 +6626,7 @@ function complete_substitutions_array(&$substitutionarray, $outputlangs, $object $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_'); foreach ($substitfiles as $substitfile) { + $reg = array(); if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) { $module = $reg[1]; @@ -6635,7 +6636,9 @@ function complete_substitutions_array(&$substitutionarray, $outputlangs, $object require_once $dir.$substitfile['name']; // Call the user's function, and only if it is defined $function_name = $module."_".$callfunc; - if (function_exists($function_name)) $function_name($substitutionarray, $outputlangs, $object, $parameters); + if (function_exists($function_name)) { + $function_name($substitutionarray, $outputlangs, $object, $parameters); + } } } } diff --git a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php index 771325da38a..a5ef291da9f 100644 --- a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php @@ -468,6 +468,7 @@ class pdf_crabe extends ModelePDFFactures $substitutionarray = pdf_getSubstitutionArray($outputlangs, null, $object); complete_substitutions_array($substitutionarray, $outputlangs, $object); + $notetoshow = make_substitutions($notetoshow, $substitutionarray, $outputlangs); $notetoshow = convertBackOfficeMediasLinksToPublicLinks($notetoshow); From a64f30d23e0e0573108932fe170fa0df2f2b5dea Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 00:16:58 +0100 Subject: [PATCH 071/743] Fix message --- htdocs/website/index.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/htdocs/website/index.php b/htdocs/website/index.php index 99d00c019ad..9cada0bc506 100644 --- a/htdocs/website/index.php +++ b/htdocs/website/index.php @@ -2453,7 +2453,8 @@ if (!GETPOST('hide_websitemenu')) $htmltext .= $langs->trans("SetHereVirtualHost", $dataroot); $htmltext .= '
'; $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("ReadPerm"), DOL_DOCUMENT_ROOT); - $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("WritePerm"), DOL_DATA_ROOT.'/website
'.DOL_DATA_ROOT.'/medias'); + $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("WritePerm"), '{s1}'); + $htmltext = str_replace('{s1}', DOL_DATA_ROOT.'/website
'.DOL_DATA_ROOT.'/medias', $htmltext); $examplewithapache = '#php_admin_value open_basedir /tmp/:'.DOL_DOCUMENT_ROOT.':'.DOL_DATA_ROOT.':/dev/urandom'."\n"; $examplewithapache .= ''."\n"; @@ -2783,7 +2784,8 @@ if (!GETPOST('hide_websitemenu')) $htmltext = $langs->trans("PreviewSiteServedByDolibarr", $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $realpage, $dataroot); $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("ReadPerm"), DOL_DOCUMENT_ROOT); - $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("WritePerm"), DOL_DATA_ROOT.'/website
'.DOL_DATA_ROOT.'/medias'); + $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("WritePerm"), '{s1}'); + $htmltext = str_replace('{s1}', DOL_DATA_ROOT.'/website
'.DOL_DATA_ROOT.'/medias', $htmltext); print '
'; print ''; @@ -3061,8 +3063,9 @@ if ($action == 'editcss') $htmltext = $langs->trans("SetHereVirtualHost", DOL_DATA_ROOT.'/website/{s1}'.$websitekey.'{s2}'); $htmltext = str_replace(array('{s1}', '{s2}'), array('', ''), $htmltext); $htmltext .= '
'; - $htmltext .= '
'.$langs->transnoentitiesnoconv("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("ReadPerm"), DOL_DOCUMENT_ROOT); - $htmltext .= '
'.$langs->transnoentitiesnoconv("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("WritePerm"), DOL_DATA_ROOT.'/website
'.DOL_DATA_ROOT.'/medias'); + $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("ReadPerm"), DOL_DOCUMENT_ROOT); + $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("WritePerm"), '{s1}'); + $htmltext = str_replace('{s1}', DOL_DATA_ROOT.'/website
'.DOL_DATA_ROOT.'/medias', $htmltext); print $form->textwithpicto($langs->trans('Virtualhost'), $htmltext, 1, 'help', '', 0, 2, 'virtualhosttooltip'); print ''; @@ -3228,7 +3231,8 @@ if ($action == 'createsite') $htmltext = $langs->trans("SetHereVirtualHost", DOL_DATA_ROOT.'/website/websiteref'); $htmltext .= '
'; $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("ReadPerm"), DOL_DOCUMENT_ROOT); - $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("WritePerm"), DOL_DATA_ROOT.'/website
'.DOL_DATA_ROOT.'/medias'); + $htmltext .= '
'.$langs->trans("CheckVirtualHostPerms", $langs->transnoentitiesnoconv("WritePerm"), '{s1}'); + $htmltext = str_replace('{s1}', DOL_DATA_ROOT.'/website
'.DOL_DATA_ROOT.'/medias', $htmltext); print $form->textwithpicto($langs->trans('Virtualhost'), $htmltext, 1, 'help', '', 0, 2, 'virtualhosttooltip'); print ''; From de37c0bff086c098735f52adb3735a9d4e6cbfe7 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 01:59:18 +0100 Subject: [PATCH 072/743] Fix Warning --- htdocs/supplier_proposal/class/supplier_proposal.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/supplier_proposal/class/supplier_proposal.class.php b/htdocs/supplier_proposal/class/supplier_proposal.class.php index 7a1e10eed3f..b022c76d0d8 100644 --- a/htdocs/supplier_proposal/class/supplier_proposal.class.php +++ b/htdocs/supplier_proposal/class/supplier_proposal.class.php @@ -1006,7 +1006,7 @@ class SupplierProposal extends CommonObject $fk_parent_line, $this->lines[$i]->fk_fournprice, $this->lines[$i]->pa_ht, - $this->lines[$i]->label, + empty($this->lines[$i]->label) ? '' : $this->lines[$i]->label, // deprecated $this->lines[$i]->array_options, $this->lines[$i]->ref_fourn, $this->lines[$i]->fk_unit, From d3ba87b09aaa945451759a843fa0d519af065b33 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 02:32:56 +0100 Subject: [PATCH 073/743] Better gantt --- htdocs/core/boxes/box_funnel_of_prospection.php | 2 ++ htdocs/theme/eldy/global.inc.php | 3 +++ htdocs/theme/md/style.css.php | 4 +++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/htdocs/core/boxes/box_funnel_of_prospection.php b/htdocs/core/boxes/box_funnel_of_prospection.php index a50426e1b29..6ffce6d781b 100644 --- a/htdocs/core/boxes/box_funnel_of_prospection.php +++ b/htdocs/core/boxes/box_funnel_of_prospection.php @@ -61,6 +61,8 @@ class box_funnel_of_prospection extends ModeleBoxes $this->db = $db; + $this->enabled = ($conf->global->MAIN_FEATURES_LEVEL >= 1); // Not enabled by default, still need some work + $this->hidden = !($user->rights->projet->lire); } diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php index 1b104ce95dd..40440f4fc41 100644 --- a/htdocs/theme/eldy/global.inc.php +++ b/htdocs/theme/eldy/global.inc.php @@ -5105,6 +5105,9 @@ td.gminorheading { max-width: 40px !important; width: 40px !important; } +td.gtaskheading.gstartdate, td.gtaskheading.genddate { + white-space: break-spaces; +} /* ============================================================================== */ diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index ad0b03366db..003416a5baf 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -4997,7 +4997,9 @@ td.gminorheading { max-width: 40px !important; width: 40px !important; } - +td.gtaskheading.gstartdate, td.gtaskheading.genddate { + white-space: break-spaces; +} /* ============================================================================== */ From 506659a9d21cbc1e31219a6d6fc39ad41362fde5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 03:55:46 +0100 Subject: [PATCH 074/743] Update jsGant lib to 2.7.3 --- COPYRIGHT | 2 +- htdocs/includes/jsgantt/CONTRIBUTING.md | 2 +- htdocs/includes/jsgantt/LICENSE | 6 +- htdocs/includes/jsgantt/README.md | 148 +- htdocs/includes/jsgantt/home-bg.jpg | Bin 380517 -> 0 bytes htdocs/includes/jsgantt/index.html | 1167 ---- htdocs/includes/jsgantt/jsgantt.css | 991 ++- htdocs/includes/jsgantt/jsgantt.js | 7587 +++++++++++++++-------- htdocs/includes/jsgantt/main.css | 126 - htdocs/includes/jsgantt/main.js | 85 - htdocs/includes/jsgantt/project.xml | 131 - htdocs/projet/ganttchart.inc.php | 12 +- htdocs/projet/ganttview.php | 3 +- htdocs/theme/eldy/global.inc.php | 14 +- htdocs/theme/md/btn.inc.php | 37 +- htdocs/theme/md/style.css.php | 20 +- 16 files changed, 6115 insertions(+), 4216 deletions(-) delete mode 100644 htdocs/includes/jsgantt/home-bg.jpg delete mode 100644 htdocs/includes/jsgantt/index.html delete mode 100644 htdocs/includes/jsgantt/main.css delete mode 100644 htdocs/includes/jsgantt/main.js delete mode 100644 htdocs/includes/jsgantt/project.xml diff --git a/COPYRIGHT b/COPYRIGHT index 5eab2669ce9..10b06b86811 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -52,7 +52,7 @@ jQuery jqueryFileTree 1.0.1 GPL and MIT License Yes jQuery jquerytreeview 1.4.1 MIT License Yes JS library for filetree jQuery TableDnD 0.6 GPL and MIT License Yes JS library plugin TableDnD (to reorder table rows) jQuery Timepicker 1.1.0 GPL and MIT License Yes JS library Timepicker addon for Datepicker -jsGanttImproved 1.7.5.4 BSD License Yes JS library (to build Gantt reports) +jsGanttImproved 2.7.3 BSD License Yes JS library (to build Gantt reports) JsTimezoneDetect 1.0.6 MIT License Yes JS library to detect user timezone SwaggerUI 2.0.24 GPL-2+ Yes JS library to offer the REST API explorer diff --git a/htdocs/includes/jsgantt/CONTRIBUTING.md b/htdocs/includes/jsgantt/CONTRIBUTING.md index fa7bb2ca8d5..3f0d74a0c03 100644 --- a/htdocs/includes/jsgantt/CONTRIBUTING.md +++ b/htdocs/includes/jsgantt/CONTRIBUTING.md @@ -177,4 +177,4 @@ You can find out more detailed information about contributing in the [Documentat [github]: https://github.com/jsGanttImproved/jsgantt-improved [js-style-guide]: https://google.github.io/styleguide/jsguide.html [closing-issues]: https://help.github.com/articles/closing-issues-via-commit-messages/ -[docs]: https://github.com/jsGanttImproved/jsgantt-improved/wiki/Documentation +[docs]: https://github.com/jsGanttImproved/jsgantt-improved/wiki/Documentation \ No newline at end of file diff --git a/htdocs/includes/jsgantt/LICENSE b/htdocs/includes/jsgantt/LICENSE index bcfabfb5aa7..d384a6b01be 100644 --- a/htdocs/includes/jsgantt/LICENSE +++ b/htdocs/includes/jsgantt/LICENSE @@ -1,5 +1,4 @@ -* Copyright (c) 2013-2017, Paul Geldart, Eduardo Rodrigues and Ricardo Cardoso. -* All rights reserved. +* Copyright (c) 2013-2018, Paul Geldart, Eduardo Rodrigues and Ricardo Cardoso. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,7 +25,6 @@ This project is based on jsGantt 1.2, the original project license follows: * Copyright (c) 2008, Shlomy Gantz/BlueBrick Inc. -* All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -48,4 +46,4 @@ This project is based on jsGantt 1.2, the original project license follows: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/htdocs/includes/jsgantt/README.md b/htdocs/includes/jsgantt/README.md index 802580640ba..a628b6f12b9 100644 --- a/htdocs/includes/jsgantt/README.md +++ b/htdocs/includes/jsgantt/README.md @@ -1,21 +1,151 @@ -A fully featured gantt chart component built entirely in Javascript, CSS and AJAX. It is lightweight and there is no need of external libraries or additional images. +[![Build Status](https://travis-ci.com/jsGanttImproved/jsgantt-improved.svg?branch=master)](https://travis-ci.com/jsGanttImproved/jsgantt-improved) -You can view a live example at https://jsGanttImproved.github.io/jsgantt-improved -Features include: +A fully featured gantt chart component built entirely in Javascript, CSS and AJAX. It is lightweight and there is no need of external libraries or additional images. + + +![Demo Image](/docs/demo.gif) + + +Start using with including the files `jsgantt.js` and `jsgantt.css` that are inside `docs/` folder. + +Or install and use in JS + +`npm install jsgantt-improved` + +Import in your JS `import {JSGantt} from 'jsgantt-improved';` + +See the [FULL DOCUMENTATION](./Documentation.md) for more details in all features. + +For **Angular** use the component [ng-gantt](https://github.com/jsGanttImproved/ng-gantt) + +For **React** use the component [react-jsgantt](https://github.com/jsGanttImproved/react-jsgantt) + + +For **Vue** , see this example: https://stackblitz.com/edit/vue-jsgantt + + +For **.NET** , see this example: [.NET Documentation](./docs/DotNet.md) + + +## Example + + +You can view a Solo live example at: + +* https://jsganttimproved.github.io/jsgantt-improved/docs/demo.html + +Or use a live coding example at Codenpen: + +* https://codepen.io/mariomol/pen/mQzBPV + + +## Easy to Use + +```html + + + +
+ + +``` + +## Features + * Tasks & Collapsible Task Groups - * Dependencies + * Dependencies and Highlight when hover a task + * Edit data in gantt table with list of responsible * Task Completion - * Task Styling + * Table with Additional Columns + * Task Styling or as HTML tags * Milestones * Resources + * Costs + * Plan Start and End Dates + * Gantt with Planned vs Executed * Dynamic Loading of Tasks * Dynamic change of format: Hour, Day, Week, Month, Quarter - * Load Gantt from XML + * Load Gantt from JSON and XML * From external files (including experimental support for MS Project XML files) * From JavaScript Strings - * Support for Internationalization (all hard coded strings can be overridden) + * Support for Internationalization -Project forked as I was unable to contact the original maintainers. This work was done to support a personal project that didn't warrant a more heavyweight system (e.g. the dojo toolkit gantt chart features). +## Documentation + +See the [Documentation](./Documentation.md) wiki page or the included ``docs/index.html`` file for instructions on use. + +Project based on https://code.google.com/p/jsgantt/. + + +## Want to Collaborate? + +Its easy to get it set: + +* Clone this repo +* Install lib dependencies: `npm i` +* Install global dependencies: `npm i -g browserify nodemon onchange` +* Run the demo, This will start a `localhost:8080` with a live example: `npm start`. +* Use `npm run watch` or do your change in `src` and restart this command refresh the changes. + +For testing use `npm run test` with e2e tests. + +Or help us donating... + +[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=S7B43P63C5QEN) -See the [Documentation](https://github.com/jsGanttImproved/jsgantt-improved/wiki/Documentation) wiki page or the included ``index.html`` file for instructions on use. diff --git a/htdocs/includes/jsgantt/home-bg.jpg b/htdocs/includes/jsgantt/home-bg.jpg deleted file mode 100644 index 9a035005681df8b4b293ffce45a9f67011ab6b43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 380517 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%0DX@Tm-D1ulJgdOKRAm{&*c^>A&OBys7O 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 ^30 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

wV{=`991lkJ-RBO-SG*De=;MA&XHW zDomS57@%FU8q}OXEl2QGWLR;aWV~g44Z@N!Yr7~VIJ%eANtVI(MNeEk+L)l+$Pb1I z)6tz*%s=byKRtmyoxPd5#Kz|5=AY&PI&QF=jJ}<&w4e%kxz(8}@IX1^35nXJhg((p zFWYH46?g5mJM4B^-F8!ieO` zM}do``JNr;3(~r*4Fk1IiWj_JA3Mit?>rNASGD5~8^4Zz4IXO`kylzvDCB<>=(8gs zA_c-4Xje9hN$qR=eRR%Wa%28-kK}m(7L^nmVu=XO_sE8ZVsy_xS9TWFhg06tCkCw? z??tHpV*2wmb)10iFyX}Wakbk#nxh-7lqN1j=mJr|!lO%>kacjlJY>)4rlH^0Sz4RE zb((!c7puRip>{vfJ$Pn)I6Z@TZVd>EJ|g@z*op{x6q7&>Q+78d3SMDdX#$48632~x zms!nsz~b7*=R4Z61e*5P@@2#JF0~w7I4SI1`wN&M*BER<;3S0FqjdCKNBzVQ+>W|k zqg-C(y-E7B={TE?F85xB5`(*`4M~ME!+uVeYW`QH$nrgV_V*c1d-xd$wnA&Uk2;uN zRc(9C^oiH~7b*s6g|3XdHPJ&ln%=X472#;BIlk>?UhAkceBUd?*^Jno03l!z4V z{9yp3D--i39vPKS`zTu3@_+6x{mto7FyqNo5ul*)Mof%@KKjAo?o+Si3MnF7a5h-X z`CN`+jXTRqe&A9%9<3ik2MtgDw5>JKu2E<+LB|%}`#iZ2IMyE52^1!a5TV-w zJ&Q6kI0#I>$Y7h9AJp+g{81z5A&j$nM}sGtX}*NLq!hh&Iu%RG8oruJ4iQ>}rn~8| z7F+qXn0o%E?=3MRwi1Eq5>Vv+>PxjLk7N-y@7Yu*omc+BO>{cH`Z7uoR}59k_P>Ajq+d0CIs6Xli*(IkI3Q0XL$ z2!ZL<&yMgwRJTJQr4}Ua=eQ5b&ur;G;5m>t0bMkz)tWT&nj#PUCmbCE8U)q_s=R*- z5bTqqQZ1PB=qw^SKOL7Jfb_*?46Ozmf@|edy^rB%(MMV23Kw+v2{l8KEj`I9@lp0ZS;3LReb0cTO9V7|Ps)Ic zvVl7c7>mt0D11K=!3S3y3u9PSBvKC7(fXG&y(i_Zg@ zfE?4NtOv;ZDv)B}?JRwkUyY^_Ax$I7kYj_WXeJw4;lO>F$8v17YrnW|FxdAOgLmeV zD>jR;#GxNVHjf~W5k1a`zoJB-*jc;Thz*tqV;vEmR+BY*t3{=Y_f`8YGw*K^ZvD$< zB>IGS`jV|-Y5TMeu%se6{RP-0R+Xt6$67_9Y@ zda{@(Z)smuV2Mz2F3J)YMjhaCpm3x2?HNe$At|rBl|~Souu-=2@D%Iz--}*aLp;N* z)BQk)3TsBWW6J$FodJhz&c`=Ar?A0J7d99}p-bb(n9xUzyV=aT*9H|#q-4$sZt<=h z(Ix|N&Mb&?VQjgRx!Bl^E30tZnMzRVi1Iz7LsOnrh!Km=tN%-=5mW-kclhOm6P80% zjQ4E!`7`f?7^sZk2b4wz9ao4lZ$?Agh3LFjHIjixPXs4veKp6Usdg@?6Q0+(o2DjC zqLF_xT;I(Xrz$=Nnbo|@Ch4!tH^`yHqZG`aW>}cQY*JV_+#1Z!p#@hhg|yvk8jJ4| z9ok2bJK5Ryx62&2-i_mbRCVmMSF2N8kshOL25yAx`*S-|!8F3^4yIICN=mD2@({6@ zh}EPpMA$@C%%yCa7>N8-ypxnnpOg*N20q;XO9<-0wEav!qK`7J9g-pmIN7WqsvG~X z4o7p45{W64q5GsjD+Xi@3-r=eg(oP-es7;WUC_(DyyyL8|3U}7sEgT0LwBBi-1K&x zS3=9SBAR0r_{DC|M#P~FJjB#S$AzNVNCttl=7nb1+9vhNzIs@W=_; z!dBP?=q1gM)BjagXRSE0u__lg9=+c^OG-FM#;BLcwYI1DG~Y;eJq;DY7r)S;Ad47X zLU4Fe*KMnVBbAGAZU6sJ@6)*oG?$?Hya#0j&Ed(s_JtFWbANsu6f0kX$YI4pS`i_E zI3g!#8UcAg%1Dezz~BI*L}CJV>vJc5N{Pl_7g{1e(0tBrbE#S*gr5o^I|_=rd~#fw z5Q;3FhM~zv3kBIzG zmU63)My&xa6oiP)UggUt54AT3*^`GOxRdWj2pYm*MQXre^L&2+i%YxPFb1(Z$h*A~#ftlefOQ8iFg)u37aS@Fzsk0n#=C-fUEN2)>_AAUh zggel_#Yyj1y-DpOb#sB~zQ-iIAOLX6qL@Si{n)J)zrTSH-FhdSf@ecr1C1^y;T6p% z&gi|#25)bl-;T)BggGv9os4Gw0B6Bjio_Q1MgBtB>W|>9#zrZPG^S{N4HOx726QNU zts3g<{o~)RP&pmEVxyKz&!H7(clb1X`P(slS=UV-_M}AhW}Nn|S;0Qc18#I}DZ&QtU%Hd zKYGH;GR{1CIIt6!aFLWSNo%&!h9vIECXZ*!VNeO2PmD6`!TZ5lUC*(L>xjC$LupaV zKKXlsAh^s`ynrL6#+)dtPBk(w_cI$S_Ph|58#GsatiUy?T%lZEfIvepnZN5VNr1D*rT z=pvm{LERg=QiF4_FdRks1x{Izgq8Jy8e{aa#t@~^@#sRz1_q8YQ=r@kW&Cid`p?5> zTRJ)ZIzCfmARF(qDF;Jb(MUfU`GUcP=jR7>hY^-i3GF>2ceSigvcKa`iENXQKXSf`5jiaQL61h6KRh zFq>1&>a&!gwu{tBWq{jU)6-p)QS^gAe%tFO_+?bN1dx_wnhPx@<~Xu0;GLWTz!^`C zvQdmIlL~c=@FeM-ou=-+$YvZ>)3d!q?@=FV^V2^cR)U>LuK%O|JTEj zf-^3B(<3X}LZ)-lZ+qYO`+xWO{yh&5 zBw;1lXYak%UgK{ON6q^lh(0a<3pq7qZ^fDZo%K$hQ@x*lv-5T1wRg(9m^Jp}Zi-7e z&K=2KO#0Bp3Vv5MmKxd;R$PZy>(m_Ar_0q_KZ+r2wm65*+<0w{AoMK=z=od819(gh z=NciF^uL4jB0?80z`F#%5rZ6`xN_LOg+i` zp!iY|@*C}0q9<5RK}N$9reuA2l;hIF!c&n4{GRnNo>qvLuWOU%9<2ABLpYq^9x#8p za>KtC%qk(#PdXjhMAB%xlb+|Tk$p5{-sLw-=8>oTdv?ww#(v??()NM!U8i;XZ)Pqx zg~h~D&aK#8Sn*4jv9lg;F4e<8l0FFU0M^;|XlynKICceGBi?yiuK_D+-1GQ@-i6o% zKm>kRU*)=SLdfuL$Lku!E^;RSUglBSw|4fjn2)K`12lkdqR35&`6z*VyBpd1n^BI{__GmALaVva*$g(+Z zXBw&~w3=_Z#vg@|haZn!_Sx|5n!Ac4M7oT3W;fjZ+i}>f=GQfDaqn^NJRf|&PXX7{ zv}-8cgWtsY@_P&QLt=cDX1E$(fzy`DSA_Xu@nCmG7sDVx%9RR3fwVE_0v<8bcS?W+ z!4v$XrQk-Wq6?0jCj@bYR{P#Hf2^+}f(!^bB>f;#XidM-UBF4LG$Pwz0E7zPJg50k zR#??Q$NYfkly_z|IOnqEjc~w9N;}Bp5c1B?kODphpL!ZO@ZlD*THpdRBL7Z-i9?|K zMb2Kn67&K-zg7Zx?)>$#Dn_d^M&u{?eW(q%6qQEm={Q8?P=EY^_1_#iSk5q^nEld{ z=2qZY{!d0FhkYQxK2sSWUJVjSKpPKx(1#?9s%i6%V~!6IWH=#<()RaJ(~r zLMgXu?nU!zE!1(x?dk4v&jAPg*T)Nw&ZV}E6{nwP5WT_V-Sd68Y9lN$THasWKmx2z zta|*%-OG;S`i$$x{!JO2#FMV8(e1sO<20AI?~?;xH`$rrh8|zm^QfxlDUhQx_Wm*b zgjxK|fs)TkWZ=j%Z6fa&65SHdr#7w{KM~^Pwdx1my{)I?V>a&x?p@Wn&{ABf#eevM z$bC&k8eRsH@vR7IyK}G38#^Z!jvGF1Rsm7^!@Keku&q^M;cHa<0%X&E1u8GWGQxFI zY8F=qr-QQz^sRpP$JHIFKCvMe-t0KO-MX=C$nE;~{HxP_AAYwfi~q@0#DmHDDEQME zJ_|qLc^7TV7bHFjI)_?zR-6u-RCAi)v|{VdMN|AH&0nS)sXzZZb%)FSkrjT5n-{L{ zKldYXkB0+D4Ju;F<}8#0B!-k%g0gFAnwWJ!z{F&qqOJs^c~QmX8H%v)P>`cS^nw3h=tbL6#rxEpl#(faiqKY@3^ zLhjm>omfuFFdo?TV+i;-s?j2@1-s75eF*#+Be`XOt&My2i@Op5Lx_tX5>JV03uYlF zzR30s_=vN-RwrHiMt0H%I9w^{ub;Jg2$r<=#FQG4;4f#5_jxm>Y~^!%Ntw zi+GP>_Fn64wf`#X$~zBB5>sYwodDHB+^R!ed(8-7C1ho?x&M5kD`1Nf#$q6Q#7!1Y zfV$MqFh^`__rwAsB}}EO1?UFwyPZ3gK%D6N#y{AJAcZidMJxe_&Z`A9z*Uf_0R|jA z(ax;{cXRg(Ux^+&S=`*E)WIO%Haz(Q2|c8ZZ|v`ZR{{kdaAy@t*$Iwn$IWIgP*<&+ z9)5v+#hJ_UXM7v-4DcP$ma*~=gqW-h)IZenZh@}_-yu0d|DcP$D4=v#k;SN7v|8no zTiCL&0`27kS1U2bzxTeQ_JKyi85chD(GJF0M?p8Jg<#;f z<23`PVI^$*?h_(V7OS8`X(2`5eEAg{X%P$GnPMz6(8N zfQv)}-2b$oI`C~*0=MwPX1a5k`-#E=JpG|Oym|5IeHGJlM%ZhAc`@6Wx}~`-C2!u) z{H+JRed|7Ro!gtPs%ZC-qw^WTE1vxuA~@#QS2ItG{|u2#{(*fG09CErFlUan4}E2M z^KtK1_fISLjIiq}&Q4m-&B)c`$F6e#_J>1Ur1|(gfDKHP?B2MGzPJ#$fM}@t!W1Gf z*E}_UzXtfzPJ~G_oo5mVUW9+R0ttjqCZIX*VqNmr=lf3lSu#0y;m`B#Prv?(@(-<4mn$33I_z7^Wc%HiWPxSc&vgxaQZ%_I8SeNs^M0@v=IyU^2 zH1wd#)ACQ`1j26PojSqejgyW|4J%jDJSJ!S6S*6~rJ>SL;zDpDE&n=q zI%&bPuRAy1bhBZb2L6dWpWqzSc+q}Vbmn37Dx;!jchM{MgJ~ z$RmY*Q~kJLn%f-4j=rq--j|mP|B-qF!R-b2jIZrl=Ct&D!kJ@3mj@o+TT-^V`pG|% zmk`3v`WWU@8b*+d`2>Q~+ku(gZ(6Qz>IYB$;R+?=(P3jVgM z_Nni0#{wOPu5VZ!BnuSw-Ans<{QiQJk^hjpOxghc2Az1M+v$#*uLeFBdwsLz{-MkB zl28A&p-{Q)+08$%<`V)-HU3B2!rFY0RVkG8r_Khni|yRT->hJ90s)?Wfvt&R=K z2MgoWw5;xI{EEwj|8r$6-pQubbsoEQ>#Ktw2Wswi-b2;lC)k5km|$Hso$Cy3n?)EP3t zjD7AFwr<1cm5VNvto~<#2HrBVE5Fz8hmVpEGNwNEZuh+}f46yZSiyc5f*aw((*Dmi z%PA=T!y^}-LY{nc%7u+yi+q05eO4M9lusadI|V(A4LVP7@%sO7X~N`iC-26o7q`ss zp8Rdz+92;h0%4=eEVn=~p7Tf34Y42PyA%F1a_}3B<)i3~t8B^_Utd_db@^{zf#9Ve z!slSq1Q9j_l}+qB|1rTUHgMJl(hrx@+(SFuFD*FGbkgA@zwGsDGT60*NwL8t?k*+M zemQq1a^8lZ^Q#HRH~tU#q%AuQobQbBaf^WJ=)A80yj&1FW!45lfqPK?r!TATxs=2b z^4(T|ukZrzSd?r`esHgLC-!vHl^;&{E-G;bD|Nu-p%Y=2(}9#nOR8myTuv@JAMbMB ziQwf#@R?mVam6celJ@16FS^~X&fRx;QY>Ky_}9iDLh!kI2c?pCZdwUp(nf;U>cD)r z**mk=-L1HVmXQ{0UPRb<>d6v9e*BMaZh@2B3E#9VN#4DG(`a1q7X&YGFNDDJEB{(~ z{>CT${eN(*`_|>p3=GS6B9yESD{unx$i>%SNbz;b`6J;YGP+*HbqzNbi3Ylx^4E%X6ADa3c58q6H<>1D)Mx zmG5>b_@(Ibjur9KTGDAoSz7+{zWfq5m-8+&OE#Q;=kswe^3mcrg3M{kR=42NCBa}? zwrz15xmIP3s$FuVTk@Ipj(O`}8v}pLBh1)+@+aR#f%i6lMIfA88tAlo5@E3m7-x?* z;j;}cb8WI!iszCivxoQiOq%w4jZ;v`214-sB*FZv=O(Xsb-3U>;j^uqz=~_0-#vfe zs4MO2^sg_a7#|&8_$1ix{jIRbpo*8x{t+*x4P1Zde!XpDn9rUQYXXC2#m+0qntPex za%w}6*OzlYaSxm{dD6}o13~Vemi(1-@7T@2B_Fj0oyw0rb#YHHs6NhLlpL|R`-^m4 z0U_u@zf1n=;GHf!Ewm@{LF~}7oqfbto&sAR(tlogFgxhs|m9RA35#uT65T?cmMUNZb1c3gtN(OhzEiuxdoly=#sxt zC(|A-IaF}$uO~iqmx5Rq?<4>?-TY}yeFt%{l$VUg@uO<-!kAHV{ z$C4lOV<#2t^;Vs^yk^O7zWWzEU44D^7c|2chfnjSd=>cm9aZ+NbAqJErDXN2`~xQv zV#M!eyLjAxfGyX}-Eh9`z?9AFexJ1dvmIhD_wYZ*hw18{Dibs^7ro$|-u9_nb1DwBg|tq${A61diMD?YyT z*o$=9m&3nqU)lTlijEnV3)~4)ox;BCCse%H>}WphasB+e`LoZh3-sDON&k*~)oYno z&E}@J5fR^oJw23PGT+5(`jpdduJ^us@n}kM-KVSoLD0jEgx19co8z4>x?UUl17F&c zaUEnV@CYB}J55^c6n6IG-v|pg=OrC}8FyE}-5B9M<-B{))J2y!$KJW^Vm+4cLfCfj z%dRJi#V6*@$akJh@O8N&9V(E;4G<-7{`Fk6b#gGcpQnUbir+N4$w*K2tb$!XbL+xR z{;=Y&*p;%?D<@521je7ei40#qcSUn-Xzgm3HH)U4b^FcfyYSTgXMC1kV_J7Qg8_ioLkM^vM+Wb%B>QEpU&Gh}~~C>(xd=ZnSt-XK-WN#@}~s zDk03E{`(Mt_{r>-wu{~O_ikG}<7k)7jgf3?D@IefHe#hV!1l(k(xwj+Fcqz3*IlY1^VTZH~ajYrgea?Bc%U#|~GQ zm1ExoH&XE~@87vVs4d{%`z>s>Y68f>-bc#W{dHiLALTy@` zl{@F$iad)WoEy%IXx{q~)88ImN-Y~~_(SnJg?U0#wYuAqvqEw~Mo;@x8Z5xZ#^Lo% z4V3mVoeAgGxyp+=jnw;A__b<4ZdPd>iNeuAzM|2mmT{e!%c&Te!O65V+RXpOjd=3b&iH5mW_Vjk*f4 z1q|nFyg`+)Mcve(V@TGcDt=8qL)WMo8mtW6WYoGQ` zA#&~dSJqKDs=_EoxKr`kY^XdGRZbOUiWA^2YEf~YI3ZCt#tqqdt4o_p>6IDV*{Ch^ zLS|EWGuF&uzON^eA+;nK`cj&TiCYk5fR$Re>aAKbe11TD~8YwAU16>AJ)`sU30j_6%v@7Voej1#>8|ie#l#LhCBi<+ss%Y&@~b zZi$idRhnYXeLYUgQehX_naDyeg;n`Xz!ZmaOrT?j;J8uYy@`ZF$xKIMRO(Hb+@z$H z(UltcBgU<2HrG|DHK4B|({Bo;M}^cBR*s7Qn@(J#Z{sB}3Z;H%uJ>$hF{PRQLRvY< z(2niq+QxpF6X4ltuq|s4nJXTcw?D!}^kLjF<{qTEOsr!c@nhoI^JQKCBBiuM+l#!> zq9;YGLlqL6j z`iyxdMPi?0EK0g3QHD{gW!}1Qtz_8BFe1J7G-dxYtI32^bI>eclfN}8I4#=hXea^` z4EPV&vKWZc_A|&eP!8EJ2H#j|mnh9R7kN8_f}0wSD_=RXrMhA?T{(a^X;jV>(j}IP zL;=7wV(|-``w8!n7_Cfo9L3>nYrhV+em!XOqxaUS1j9{|-rF)T8@-t51QRF8r}tTs zOclzP!C>nOjfPdB`iAA;8^lCkeuvq9vj~=;600=HLjCUGJ*hX|e zvsNQ8U&LoZNy-8c1|%XK1`=B30UC;uo+Rzo~5a3r)yTFu`m_g4k2b-vry3e+8Td{6*VPoLQK_ zgc9rIQA6F|HW{BsFh%{*eK}p>aPMdXMJQl%DA~4BvnzceQfks(qrAM^y(_^S@O{UC-iA?UaLL%r)LPW{-5V#z_y~&O{A_3T3wJ0FL zy=Sj@N;9@QgPe>1(c^5E1~@}`?=>P#*?I1Gv@3;KsYsykC389EjZI4VJG=$DxZB6n z)Y(Psp2H2TGH!2a)1G0Bdq4@|UaLovDuigSmT;+X0+xE>kl%YWh8t7NdJ63?wBa7( zdVGC4jq2Mu9n#Xo+4R_yB->yi3D|R165f~Fc8zzQjYp3ls^%!l*!VoEuY@ZXUeO)l z6knxN6U9mT4&`%ve7Kfbq_&KTKYdO2t+0Nf6+x9KDrRhZr|vu>VBr*ESy@ZVjXj}- z+N#JQ%TV98c`uL9mpFEI0ykrF5+puXUe?AhV%WH-E3&0^Ty58~Gp?dm#;H1MPW$eP@hBB4yjjs6Y*ZYW%AF_o)K#Zd{K^0|3yM9}2u-O* zq_`_RI{<=-hb+LG(Dl5Q+-H&TjM+%9mKP=~ol|4HR+Wo4_N#@hA_qP}&lM+048Er_ z@fTeuKMCTNx)Z3g*migY5$`%|7$V7g# z%&Ev#K^hU`CFWpr^3HkG@%bm-LlJfnOA;i12Au0PaHrKsd=jrPQvif**4x=*I;I)X zi(9M3XA`T3@q*YdHGV<;#x$zcd^%z&MeF~>ft29Z`oN>z&PPYSAnL0)A zNzWoqc9(5DRIW01-?dS4!Dtgh z36;atWxJwLUdko_ZORp`bX^4!1K>_q3)1tlpKMhnY0wdsIamG>UCt8G>Xc;^50ARG zoPHZqq&L8!X&l}ojujQdNDGa)$|zYBVX+{U7E0yiRV@9zw&D!jc+x^C*1j!8jbw>j zm4s9$i`PMPSgAHeNXk6X-KLiuDqI~J_oXoPXMH2zznYqq1S!{+R{Hu!1pqhn(59zx z`QGZ^{P31-+EaqDrbm9z*C5e4#6Yr8!i7a^9DLQNqzz*2w7zk0Id)=76{~QMM2s$H zjhRGg+IcULSJnzgX>lae_>O^)OgJ=#9ib1u7-xksjo3LfY}oRxk*vxMzh+e#g)}LP zQDN2$EBVzsjpJP}HSgXUOol-v%^Cf@wQLkunq^|o(UKaA!!NOy-vzb02CGh`3V?5) zX-u0Ttn=d{T@W|3P*Hb0I;<~J6p>(>q8kk1SF)JeHc`}s)e#Z=JzLvr%$G<+#E1y`)=Sh5bM%ale^IQkv$4Ie49ooe z_jz8)Tdk4O7yqhV*x5eiwPZ-gb&ZK8JJu}SA0Pd^+ZqrME+Rw>SM;Tkn>oh2Q%qM9 z{FB<^NEGU-fkA0Xq{@efwp#Xdj$8g<4rjnzO}UsJg_iPb3wP!BWuttnRD=#OK%}ai zw}64Aaqcp87(JnCE{95=$l30xd`G^!HyabjQRQXi2O=Oz{DD44k*krgO4S5eS^LZ? z7@4O?ijw;=WlfcD8Pc1Q;H7qAToQU)qm?HKt~_?!u`$}&>;cn+Dk}Jf#yF9X>yaIu z*{zP-&fyAc1~CJ16?;h=3Px6YQ_AKkGcI`Y5r`VH^A+-(nHt+!`B92JATr0I$YU-P zRzbO#6}3@s(0SJanh<3+WljWlDa?|upa$9e_0?^)4da8h<_w@RoMN?Y-x*LZkM2g9 z>I$QHyKL(LIag~;%Jv(=YL_*1QzA>_yH#-Y#$u|wHGNXE&%`+B^F@iArNY_1#+PXm zoId=TMyAWi%B&ZFxrsV^gFXyGFi3+WZeir8IQ<5+V+sp?eu$wR)>NcACnL4^b`()rz&R$DX`yOVZ{;Rds76f=%qaCe)}gD;AF_Vk z4{9B17!lN4x04-nw68fyk;gMVTTU=@EK*`sU2)<-0(trZgAqvr$SM!~$PL(g%&h+N z8hkf(`SZc9m8!gfk*ID|1#J^Hbb1fIXnm#~6-(LTxW@vV(yx81GZ;@@M&~9NEI5#7 zYc)LiH4wuw84vYGgZ5#LG)|6%_k}vafg~G6*s(cB_7rFlRN}gUJ5lUFXM z#^}qf7?#0ZGmL?$H7eiY(V$$ECp+ zfyFf0zd%fAh9Hq`+i$Bfq&$rjJ5FJR81>b}dM3{=gc|!9wRM~cX<^}0Q1X;11yy%p zd6@ayl}2rGB-wiunTL}-&J5u76wg(TK2BB6*rz5{9@YYFQK?$Sqr}Q)fblZVkdAh@ zk*#>DPFof2XM`XCC5jH3u}9gQ1~3hd!=p{*58p`8+L{LCE&U}P;?LRHw%lS1m5%>S z*!>ObHvgSC#o=wVy-U+qcx02F+Zfh^e`0UB^GY0IS&_vGDTiIit_8aPak18Z#}cIq08^%$%8{OTXB0T zV}gBGFllIcz;x{XctDi)w}`@*gA?icCma?!)-ocA9W~h+1Ng#S)EjyBg*HOef}yEt^OCwpm4Q8X`c-&A6E8O z)v1Qn=x?d3a0*PlFvcBAOFH;EJxB5iq%1;7>)2FK;SE`_=^COkr(b*TsC}?Gnpz|c zv+n`@{gyRh)R>^1oxM`R5$$uVzEyM1>!?X$(i-jhf-&(fsB>H?fD)Xd6`9tkrNs1N zCBl#oH*+RhZsUqgcqa1VXuC<`0|e>b6iR!j?4)?FP^iB_3>}r^-9nR%ulUafElh(7 zn7*8hw^?bN1ln53G?BJQ+L99w2{7Ez)UKTu!!Ts|v3YWy=9)5f(>jO-BNo&nH%U`3 z5e#U?uyM4MsnXQAWOnLO-8+fxW-4ITeCTd%8U+sW`Pc4$fq!@M>6cXcm<6Y5Uz@>3-Ii2*Q5(2QKr!$q>i5Z&SQxh;2qgL zB_=dHBnHA%QVbjaWZW-9sETwh(u(@pz$~+fr%6nMK`=cFzqsMd**|=+BBcG#H17)o;;p%gkd0Y$D2cjX@9w0wiqoeD0>iTmb(A9r zf;Pq;51%|b2sOYl!IT?2ULJgS#6gax2(r?bFZ4$c3QSB%Sm7Y;ElZH*DICgu(kA;d zna>1mZ~a4QAhs?c{(^v*(r(HQ(W{%Nni}sl_*XIYHR2I6vP`|udZ8iT;X_UYJi!Li`Mzc}FA82WK)MH%Q(#H~K zg5sRhia4u+3``u2s>+GoV8vNN;yF2a4IU-&q%tJ$Ig#fb2}LP6e-m;f>)DJu9i!4l zjg2UDor3LEi7Yk0TVUHWVbECfGCykJu!8CQfM#@w;Tuc_*n8k!waR|LIUT z%CCw-o^>d7Jt6=pq!MUUX1fUN7Q2(p#`To~_s08;Z@-<0x(IYAmRdi8z4m`RPS)2USjHhQvlH|#5lVH>f}(%V^;zz-MI^ykrj zbtW%L+9b8w7$&;j*hG!Y10^lZEaaJIC#@<>rXF{YXwwQFCsCk=@>?b_DJ{L>9?|Hi zu~+RVdg)5akhbtzbs4Hw=~0j~>U5Sj#O8)}dheyl^H4iOZ#5O}5GoUkVd1M|Vrogq z(gEK`DD_oe_rEaRt_)L;EA4#nL1dobz@zPF-{rRPR6KHDt8LXEtZ5RYM}mF@yr0ag zqfPZAQjeT;S5Bqq)Vs%ul*Z}Gs^~1+vK;BYqem?p%B9pbYBBXt$9jC%v2hCumyGtS zD$^uTl5HO14*J_PM@F4hqECoWza}LkPrLrrI15#&ERqneXVn=T2$da?43Hi59;owL z-9}ip2~i5G$Y@;z6vOdUEFY(4_o)VLA-yKO_)+3tjxsw@N~`uHmMt?^!hhv}-lc5J z&yONL{489{bC5D`wj1r=prtGl$!314Hw{WUW3Yv7oeF$ zAltomN=D|mM%xIWZ2EW5$+!_GKgC@&4J5Wf-yrtE{dGq|k~FT*6?r7(J>(T?9(n+3 z)5}efm40fmY-p?2B4uf<#Ly6G@{`Z4X&9!rdcJhs=ADCzWv94N`idqNUY9rWv^zUm zWn?t!1h^~aI*wjMI-}7EN}pL~p1j9!X@FDjm+oxD`wB9fF2_*kDTjOBQb*Q~C3aX< zEhGo+>U(K1C1(Ksx8EB)iB$?h3M1d9jl3C@j|Tv5-d!10AUG)WaU@?L3Dj8 z|G@`Z9c58$6aO*|>2)gdAdMM?6yWd{3u8=m6ZPD<|;Y`#dlkxKM4H~qrE z)GI1aWo34noFbEQl-C#5ic zgJ_j)M3G6!Rr8R4)lj5wxe17-`DRCY5kzuUp+DuQG%}Cy;EHldXfOJ+qL?Ds-w>^U zveC$z6$41O>T<7*zEhm|S4M#2$V)wLXQ@Uo{;k4=8Z)&x(!Bdfdiv5!#O)fRBv~(a zdbG+Mn8LlK;#Cf0=gF%&1IU;(JP)bnCghM7`yZ38f~mb4KJm|wE36De$B9q$v8F+>kWouwM)~XU)&RZOiSXQX4`$p(ptSqiq z)`2rTcW4%sRwEXV9_Wj-s_xk53&t4oxjnF_odQCjSzrT758Hvk{>(FW_r~M!#B~NkHPC=?RhEvBYGYZBHQa@I&6_pquO4|*HHG!M& zXhs}K=>~|Ttd@-XgCyQKYN+Q!Tv@1gmQJS z6-tQzwvoHaLJ=`dO!3XuLN?>fpSbrJ9HIo3Sens7k7yZO+p6QZC~vaokDz5eNO9sV zz2WDA($!mDSQr}O1LMmjX?p5XhcE_TF5l<~hmViy_4aX6tVSbhWSKP6mDPHEy8Mo& zjCxXy0W9K9elhE|B7D@?U=+_2L`rBh7`~CgP7BqRvWzwp{7@W|P9uHdm_y$yPc&9L?NgQ5wXjw{P>U&O59_+{NlWhkY1CHGE$U7oUT4GSU0 z_mNZ!@7a%VauqBwkHwtvS7ZvS?)GZ5W~TT8`^NlEO7ju1FUIf_|H~nuy8UyYW|KiAbFn2@`%d3Eog#w3y@Emo!!Dg1wo;zsn zAZk;MC^6y%waZbAvz!9e0H1#8z_R7q>~0&!(iTVKH4iVWOj#S5kf+U~deC2dBVnLM zGzEnBB5diN>#u8vhgDT1C{GbJV70!nXMKuL#iadoPMWdbpc+YhN6hj$Xk~VNqt)1n zz2^JlSc9E?@hG{+hRqg8BS9zU$g*h9VYJ-}um!1b7XlaickrKSR7HvwZ@6~eLjbsT z3k1}tv}<-s0}zA2Zzz-wkKi0K7anPFdrM6QEXW5Z-;AEJjZ$_DyIO9s9C$rDs%Ti{ ziLpg3#fSV|$41Pph#y;v#6RX8xcM?1t)g6e7iBuMXn?mzm#IvmaPZggff@ziMv6 zxYV|_@9phmrP_*dxKx_pO}JJp0pMB0z1AOrgWc@N)lRhJF==w8pMpXfW=69{t&Rll zGRpFtpRB~2(vWseT?;@C^_c8GvJSL+v!`=tR|vVe-EH~n(1Brr;gxc)wHyhAOVCw} zn`ff@x4R48VnR4 z87C+((tDn6W4*^*$?zeh>XFUB4Vh4(s>nNSXDT!GN}@lT#g~sNdVpx%I|AwXSM2Q+ zp4P`k6ju*X^LDm#j%wd?-%zPxq4)59JE+Z-$j-av$*G|abRwcmTLFqTs~`b%{Pw5ObWgcjz<#^`h@lC%cwW2`rx=TBt4 zX+QAQ%1ehNRUXbwQxY0%I>^0JAUFOG+Z8IGO!sgDsnO%6WydaT%TyMhOsfDSQ~Gn7w$kh4B!p4IE&0 z7QbcVEMF^>SMs@nHK3r~>Ivo(ImsIaVW_IfqAjbfX{klSO{V??p2}J9o!N1TN{xY% zJ~ff)pw2Unsd+iq)QZi~V|7N&h$uaASUlQjmV~tZi95E>yYh_h=i#YuPt}GFi+L^4 zXklcnxh=d!IyFoi4!G3Dko(__sQ;T#VH>r@u-}$xRCbmHWm>IW7TXcJ ztR;E}XSE%!s_wm)=fRo40pyBgbg#|MvRY*bwQQc4>JQa5SskFbiw#V|v-aFJo9m`r ziw%2dGaS)Zn*KTp@j@>%MXK;TtMh1wQB*A}%25qpnE-ACQxp|C7r;Z|gzYC#kC~rm z1~Btnx}yf2Xts||TU1dG(Wjc0uXA!n+GqYr{E?g9Wp5UpIpZ@xw$8Aj{mM#b!N`e> z`O-iggf{^`uDDx8$boA7@a0-^_9*jE^sY)%rCzaHUrgF|+`B)a*#wMnWu$oc?%M{v ziA=fH(9s^l@kx% zmVZw#&oQg$)KaZr)v@+Hrp%Pci@NkjCY)7kh!hM-eU(o*wdat_P5AfyHpVlZj&*C? z1G&{OJ^Wg-wXL|Wth$*o5DgP=S&7O4Zgs~@2@&p%L{lti*nqWH1L-oC?SIKdQNs% zVne%h^W*?Gi5d>+J(VVE71{lED_pX}XG~|_ANVx8Rxn_5K5tfXIBUpV z0Nva56r)mI0pgj5V=Q9$^bBj(3)%=Qdza!~Etw~d=Pn#lMQ!^!8Fu2-s~}-A5$0so z<4mzoWpnAXXf)+kaXP{r5hR0P7#Et(W;K{zyc+cEu-y-6X);UmAYb6=aV7eiwD+*$ z_`Zu;on@#^4NJaAtGM8(j4}RAAONjy#TZLtM7c@Sn*rf+JpiQB(l%*S_03SP9cM^u z`So)j)aVPuTBuT-)}Y!2g=ed(8mt;SLz+QX=Hr~4BRETa-A-?xNFOb}v?_)vo$bKs zy}~-G7@2Zd$Lh6_rI~iTZXsy<8pmB8UX!JFSe3n6F-%PMrnNv^;#xEt^D6z&;CiC#BW6PvZ{#U*H{*Luls0I0VLpsZ-lVN*DRv0_ zqYBA}EDtr7-T|ciFSGqo5m!u&%ZnR$XN{wUOrfT+ezH$wde+;uQ3$78LfnKF^{8J7 z*BBU0uwP8ND<$j%hj`DZFixmg+{1~t&{3R(s$Vi{yyU>IDCSDpyFi&3RXTzWax;_=KY1${)czw=bCeVka|N*~_e#LEF#rl&y zArfWe71Cxknk(Ibge+9{s@|dGXMa(b!H%kQ69i!lGX*=E9tPTPwn?U8>2mnm#qyY# zW-L=AIB3Yc!P@uUvqPDFqvEB$+7w&kLwQ0?%8c^l)mWDiXWD!irtFNqXVUyG!PX=G z!QAzNv~Ts&bEa2Px^vyYr;PWQzgwv5lwWL=c&j5!d!82_D)iHflN6Efv9$ND4{h*n zDbUO1FK*PXW}MpIBk;;vO_JC&htA*ue)fy6_Smitok*f`*(%@ukDCqPW89oOx99&g3O z@|_KO>@6bK2(|XaQKiggwoY&yWu1ob1V0L8Mr5RYA=85yg+kHq(L4RNQLDCB3ya%9 zf{*{PZW~jP&x5pnow>7ztl#12cpNQdmh?bcD_0> zm@A7?j$UebSvEnJk4k!DEzt_H`q`rvv5c=pIwZao#&QbOh-c3kbU-L-NUphqP7aT~kwG3@W#WU^SN zYrPZvE4elbahn+@nn(AHb7?v|hk8Y20-vjVts>(bO0va4iK1tNBG)>-oZ6=dgu5QI zSOa8Q5#{}R-jh`3u^LMIWqdO`xJVx*S;Y02OlvA;ojTa%ak|^CUFcc7?bwESe(7uf zFttW9(L9e;n|9tR_VK{*Sjl;`{r1pNMI$uk?5|?M(X-Vw}ACY@aL|<3E*Amq}Vn*P=Dq%TM8vl^RO3-zQ}lRCufvjF=g%6)cpByE z1M&wm;|_6)?b`|LsE+k$udx@= zAK!z7_VP7)B!rlf9DCPh9H)r211gGm>z?XR6eh}Z5q&G&F`GMR!@1rQ+@phbR329o z9XZ?AQ3=ah93W|H&X}hx5`w4;>+Xc7-x4ZOtHo*)uezAnVFMa+b$nu7Zy^sMu~`UP zMD4NfQ;Lm>R9LbL*?DoSRm)e3RdeoAsYTr;p#6)hjeFHNH}SPcgAMcHW;%dWO2$xs z4&&K-Vrf*r!g9PALVBxmJ#q%3EM$sui@e5RBPqm7=Uxz(*om^j6bP<3AMLM|a-y<< zY$AU5a(zCoN4k)XR?N4wZa?Zu?ZkItO>ef053f+=p*(EfsW;Ln^d7i}``(e&ZFaFa#!#?kf{F+pQV+XP12^kreW}t;q;#Dk0HJkOM z@7dEMjVav7>S=$7-cUaoqs9{_3?|d2Zd53=&kZH^Z9h{k&<7xrolCPJhnD{p)<1>C|f`cqP9y2`SDUb{Vqcg7o}QVnSrLoEuuqE%5dxo-3fo zUX%ACF+r&}^ru~mXLLLVCFRC@LUuz$v2)3e!J~m0`vymh^~92a5l%RivoGmc)PzSv zbofub@?9m!F=Q@1&nPmAQv~j{mPFxOqe*c$u`0vO`EI}R$AtSbO7vh`VPNR~y)Z2M zk;2p0uw2GHeV@sxRYq&mcMFWX7O zF}H)H7G;q%WlEczwzra5wX^)y#NNj_BxWVWU-K3E|8VuLaZQ}*|FEvRQdcGI&)rot zn6~c9?ys$A(Iz2~>DIPv|EsWFyX#sxXl+TYl@J1kLo%a_mF-p&UD^VYFtv6~4@zpO zhC|3mX^99V(kRga8E}9B0t`84CNtCL?(=_MJ@4`cCZEZDxUTQ%BGC{3HU11yH)~Od z6885{Q@J+_kGK4t85af>rnj@~%sn%D}-!*Jee= z;{s=MKbJ7+W-~s+k-7H=Cm6+ggL`{&d_gB^L9hDJOi)*Tuepi;w0Xe}=6TOB0$obJ z&~*6m_8YZqQcM&e``F=y_>X$O>9 zQMq-x_08PG!f}Vgb+}u$$O9CdoYs7_LX5c=( zTn%TX`FSiC@?le)5(;wImMdO>oN^Vs88=z&!MK_sro5V&E>FUwGwN_SlCFjnIqOLg zhBMM0M}TXmLf}!SmixP0MPEeti%z)k@_%E+aUHXZr?Q(L3_BnX`88=2A5UGL)zH_p z-Mg{PcQU>}E?xhJUO5a-UUh`eI(YF!9?>izCIrJ*CJz1KhV+Kak>oeui6sWsR=D-6 z$tveg!k*qga9m%gcD))6IH1opU#t`$k_MaZZ$dJm3NRVVw+MO^li@tEB)G+~LoM=sx%x^LhJ%W8~8LnOFd0Sa6 z8Ql|2^r&|fe3r0T6>^GqqOPyIT!s^AkA;X|P;-jF&r-F}8x8-Q;)|4bg=ylnwX?|x z1GXoKDTp&XlD9uXQ;2TB<;i`D(!;AcAy5SxPrYt&lQKRL>u!=PfRl0;qlv1x*0=So zAEze8xpv7K`w!2KPCDjI(YW@l(o?>ze^bkUC0>Y{lA-f6iHTBjqrMH~4H7o#_KdZL zi)fbzU)??XdIF0w9Yb4mn!mpmp!v3NL5^kK8b2h(YpSp8tfS747}1VMFWWY~<8 zOQ8M|k6e7gNGcN6boOg6XVav-azyCtOPsXawM{8J&eCrtonVdXh3~ z$#C&nvBE=0T%W6G&YAS6s>4NDT#1dh3&DFllrDnmJO(sCIj0M5vK=n6nj|$lW}PvO zrR|ck`{JC2`jTtf3#R^#$>Tq~*gGaPCKlaFwbbkvoBC?hCROFuvk4v|{IXop3! zK)9+jnBrJRggg`==xq%5sxFUN7>#RiKZf@C(b^83G?<^kiIgU=J@-C+II+ytR3_c27gOkphT5na!xAdNwUcwJe*%`u zFK|NgHXAC8Izri1M5k{Sdwz&Y5dDP|SU~XlA=>yqY)OEH3tkafkV7STBk>JvF=Y8{ zx+WZ=xgsFrL%%QqhQt*Ov|Sax_q_=L++*}83PVpa#oAU-7$u~7d?B6ne-KJEYzSjn zlK+dKa(4V;9QOFa9$)7KRs(QAMuXD>EnynRhCypyfX1rBkOCZ}hHx)x0s0Emf9! z82nn=T$(-sh)4X~_eS^nZKR2CiGFWMa?m4O{_~ac7p>}mr?AayT#Sku+M2nY@b*d9S?2Ii ziJ{3IMsv>QxEhit*%3w|dIB#(qzM_aAU##AX}}(fyw*@(oDqTC$)=Q1geLY-{w{~s zsJ5}xKT6O>P&CQY|CX*^dh?C$4~TLW7bF|Y8rqu++QP5^=R~@3`MX0?7n}k-tEq*M ze6MmOp#>(UFdEv*zfogBF$7x>8|mC$n#XdQS41eaO?+&eY4msM;2pLfm&o4jlQ~1G zAS%RC&5DBbmYmPEA^%M-8E?v3C^*2wSP$~IQL1iy*X#oNzR~~Wu%YQ z4u_L{^FV@{R^@yo;T`zb;(&*kNZTrI8ms8LymbJok+}@kO-uf@;(}{%xSarG%ASO_ zP}^^;*l9WzZ|hB*tDfrwX=Bcimi^_6m8s*lFhD>0%m04%Ekw-8<2$ zd!AFro8HeQx|+dIGeRz}a$Wp&82g~hl(EHxg-*z_rLoejT+Ke3)B>kkIo;27pXlc$ z?!OkIn~+rWbcL@|46nZGwZk@1&P7A6;QqIuAQpxotise0xl==$gFKBC>;X+QsFRQU zbcL4Ohz*cUyU6TV7rgAO=dW6HIR|Ze1#cE2hyB1zhu3v9A;Ff_nid0)BN6%D{BoNNh>7>5rlX&R@-@$goi061CtR>^f5u!p} zPTK7zGDX5o>2(rMP*XoL@g0smt@U-PQL;j9S1;5h!+SA3;+Zjla%Y-v*e1@?R8aFn zK-lk)_=`e_MSoQ|E!6$6@c-6CkI+hA5Gu$Ptv4YCkIx5VO6w%^fCIbpGEWA3W2nWG z8WKz~WB^;veO)TOLDe<-Y@LNQckfDQhRRl14KX3l`n`JY+7Vx{!K7RmW7U}Y_uLsk zdg++#GS}6C&%XCN?tn8A1dxpqR>y{m`1*rx8|NQzdbT=SJy^$D`9k)85_Yb+XqZss zl7r~`L$}2f06p5Kbh1Ay91a!x&5MnVotu}FMLTEG11XSb*QCw3nY%v23(6IDkp!Lg z-A}fxc2=q7qno+_Tv+2kVFtIzdV^@eg@fjBI^1}|tJNl7Gd4Olyz7ECI9U0KyS7;& zySjXvGq+#9@yzP0{%%KBPFGaIsmy_TdK1C&D0CMf@If zAfmBxF@}RadLa=aU{)s~3|}0W$<~l^`FlLJMI?=7#dGSc6dgYEsJZZ+q3jt$id&#QUk`etUl@T!DviNCIhAZh|i^fh*Gf=CcvR=KF?a zFGs`Qsd^r$2hC4A|H1;IZ+?Q75^D-==7y~m8-Z^*G=4Yk?@_;- zM&|oRk$2=He)qaVOJg!$jsXoCTYQbuoR3D*V(FwYw)d0tnqj7BO4A#cUb@g{9)8$4 zN~=XwUwuu*Pz14WpA=yk?YCq#pr~GLh^34ak zdLuPcP&)}_rmw6Y@9^)ip*d|>cH9fg|2=`f6oL_Xte8F68T@S3GJz++MX6KHQ9-a? z7gNhMId}i?aB?QrG0e6Fl*9j8e&iHrkbZ@qWJikL_M{=Hr@g#1U&fxej9W!m&?p1w z$9+^(rxdw;OrM?uclrqRe zPnIaO0hPnaJCG#5QI)Di5O+KqU7i0yKAc>RFObh4A?0K!mPbk+t{VE&7NOL;RY*RS z_@?@rbIE1+X+Llg5+uAY6l>jpDo^eXU0S zU>;$3t$c=F*73w%#h?Ixzgg9dzWkd%IHl_5|5F;}V&dkMR})t1Q%4j}w6Qwt0Y5(0 zeJ%Fq^*csJeFyAo?>p^)mQi>DH^9@~d0B8iP_0*z`Xo1D=Or=8%&A49+mZ6)57^{6tMkk83{Kd0!d2++jZewEAT%V zwq*lKT%D76d~R1}t7u|dSP+6XKL!;O>^%(np70X zhWxkC4MSWtT#jvub9QX^9IOBB-hfU@FexGKqc}?z;OPW)pKO%GDqZH=V4fDZ$_7e( zD0&8`!m#Gc(k%)yOpKc!l`a+wsUB^^NjRM$cloRu1hwDG<3Ay*%_|I|<%$hI8xUW9A>G@L^l*mX z{s7`*H~FKayI1s7&m5J2|a3dky9Tm1J5;|5E>O zHw&U&UpR2Nn4Lnqycy7*`Q8@w@jpb=dI;V<&VF69_NHo)E6O~wO>$K9+!uqMC(d-E z)?wTGjB-6tF(j@D$jb&48x7;=Xk0av+;QDMI-b_t#$c*0kuc{PheUxm%F%-)q6u9= z$BM4d$6AE+{2g(HH@I_xg2Ls+!^al3-YL#Ft7xM+MOb}R!px>mpsOpA6 z6v75~M4hgnF&J^f#$1u*_x?B{7?H^>JTQn`|!?a_Y;(V{ZgKB9TbyaMu}s6(5%tkF6$PL5H_6?9nza7jm8! zx0{$+`ztQ<;lez1D&L~+i^#2L@wA@;OpqrG{clpWg+H{M_s-O;TGE3){)0l2XV zYXUDJ2el;QjtJojWm`^5E z#VD$CWiWMrPg&R}fzy>YisCL$Vk%B-cQvKXh8ApS61x!ii2J*u0K8lo+cGa)mdjn^ z|JEJ!Tsw9M#_RmN*Kx@2a`}KX11MPJ;H6K>9Ra^F(BQVq>&t)>c>*@hqkoK7q5NB0 zDTdn+g^H3I7lC^0Wg6N`MaLFhAko-<+DDRwjU8N8)sIVb_2I^{IN$1HG{%)VW(HY^ za$CEMTELV0W>hYR+q!I~%cwXMCJKR`X&6kG%C(ZnAqI3^n!s?nGK6HN4^ZR&TfeaM zv3dk(eKOFxXPTmOA^rzJ4m*A#q{t9Wbn7L^N2Xr}+it`olV1L`xOL-#Q<`WO*LXT% zs#r3)2%m*3W!?vC&awnR=neOmK9O`$ADkd&6(W?}YHje0M!&5f#-;Geb||enNb)-A zz)6>Df!~Aq9DhEQw-876f3k!{Br%IGd14lyh76e`S$&UVh zW~SF4p5r&R-qB|##8jbjCW6@hacm+72*jM4AT(Sg?{J%OIz2YN(erNaswyXBFG8LR zrLKC!1_BoX{c3d3=(O5f2?Frey7qa{D~hfosda(81Ktu{D>Y*Sh@#xK_$&sYiD<{7 ztJ>H6`jH((_O=OiZBwo~e!wrquL7WR*C=A%uebU^w`u)GTXN9lJgOWfNnYj}rpsd2 z%D$8E=y?jsJhu6F_eiynX?pcdf{0|+IZ?|&G^V|#_!a!qGOZ^JU%gJToo4*#W23k#JP^=AG-fhS+E<=Ihvf$Kk}fc)TTufHU#@Dn}ze51Lptb z72J`~kiO#ny>FBo9K(8iSd_mHdU#`HoDKUuMGKLK*KWnFyS+Qs7yS}C2js~*Qd$)+ zK@zGWI`yB9G$*w+dR)1NVfpG39CT=4MFru1;Dbgz3Ww9R%hF(=uG`3C}H^ho$8Q?j@~h{ z8Ju;7)-8`>LtE$Acf4>+N=6P4Ta#Va9}7ATz+ijlR(oy`LM{3cuSxk64#_LiG*;|- zS>wvdQ;J73MO(16lfYL?k*MxLOvUB^t zUVm=fpzQOW%7C5QW`;L*OeV)OWq`NN8Upe;1MQ*0@sUQ)BnqLTS{L-Ta^AyDlfD{g z{Tm)_upY_ZC~tovq%INQK;zmcOm{Mu$fmjxc-< zq#Z?dq>Zx9+##pQ!2v_d;=vF;&2}tGCe~cuCYkX<)SvuVai8x#n#)<|bRFcmEqQ9k zZDO_~qRg-Nei26KF@<&^jBatDAm9;$d20mtd&g=1lxa=d#M{&C4gp1TZX0f@&yHb4c<;6wieriY-P zdKKNCxY9Sn0i0=j48r?Rsoz!M)$Se`M=l{IZuEi3+HLf+GLFvFS_eJCV6Q_YSkF$0 zmVQ+dfGfJw7<47Ae7lE>PRk+%btkfF_NP&_&62HHUxNK%@Kno;tH4q_Pou)?hd@Dz z!ihprK7YO|N^{x_!3eMVvY&~B<6$fZ#j1|@;yS*ei&MWZ=o_|Uc5;deD(0h}I2GXn zu8%m3+w^}$1-QTdC+*oRjI+Cd#2_sMA*-Qo&m`lrX6)w^E4d(}iVj=_hosWYv#K4g z7=yc59tQ&pYY-|?HvJt}F-m;4*uif^UU%7}ep5duWpaTCo`ivE63;9He%Y|RsE!l(HZ)J+GuFwyWhb; zH;QH!`P;aPn^3l$LJ2vVxS`=&4)3c?pBJwowyJEkOmPE1%3LjRm1G8%iw)!~j&?MN zvQem`*~0{w#d@f~>eNJF9L@ay}y%o+P783Gg2Jx z@;rYymT!+u{WN7}#b=UiAVf@X=WFykTvkvH%&CJkKzN3uHAm3*#Bz;qm@YnsKW1A9 zdllN`W5VdEKN966n4m`Mpzlwx-ZjzKsALI)CVd;Q;7HIiA1{l&&*-wh;om|X3lqQL zHbh3b4ALJdlFoz#)jg2_lsi}P;IIR*tw|AZOgOX)yQn7As-t%1^!=rBs)Uxexg=~E zAbkeC&nn18NcbQUq)h=87#o=s4~OGi>4?1%%~16P*fJ;S=mzLYQV>JtgHAZIK>C4L z!>5N!@vAMD7JZ?OviK?4rT4<$+0$bHX=^Vhn5eD;bbm@W)*`m2$w{))k1>T`6;Ekd zt(Jpp{9UG7P$MYmx~-M+Psg{$fwh7KCg2hL78Q#xpmoFY^d}KG9ULpEK5ot8Gs-9O)PY!5LHiOeT~Q$XFog<}f72Eu1s0Ip+&e z;x0z9D1x$M@m*}|o!BJhvqUS1LPS(20(BgnjY4z7H1jG7ElL5FodzTbGz&q3M9 zlaJioB%E=^iTr-g9Q4A|lF3AIF6nR^C!NOlQ9zRJ72U5_bi0Hl>-KXdzme0FJL=Hm z#8VqZS7g8`)B#x`)-7zvtAp0fzl@VDDlNql&A85g!d5O$E^m6Ry)< zni9{y2Y5;k^qoxsuA_oDw_Ie!!F5{u?xj+iN!<$SIbH}n*91ciXUCq|{cw`k(U={cbq+a}hIWOgM?s*Y=@=i$L zX)#z{wUtGk&X+_hSfOPmw4FH7$^jh1Rj-9IQ=mGBvXG1Q0h4%vd!qmtM)#p`8#wd> zyu)=nu$<3snGfC&>a3Fk;zLd8)8w?@a3kur9XWJmp3xR!iexov?@wz9vv0!Bse<47 z14`K?BO6_eH@DM zS)f8So1KdXp3&+y0u0?;D&E*tp+56F=R0eEH7_?jFE?46F4+8Db?`iKe_8WJ*XL_f z&l$C4ksI1pT>nD-_!lO9vUKeKkYGOYtyNt>)`8dl@f$LC!#!ux`6=oimDpCEy+1s3 zbM7#{9>)L9W|%q>Au!*xfQU@-P!_a@Fw7g|*6_wSgS}sTX*Ti$!;_*JZ+;6}mji11 za)#q^S1wpLvqc3c7~gSGE~BPjRBk~wL>RUcIkhV$@P%0;(UjlnDMT&7UJlVc<(L>r zCNGZBFj&-Qz}N!S|Fz)!xP(;$I1xFM7L$45X9Q!H{P-I&27?4o8a{ofMCuO)$d&g* z|Jj)UcQ~LujMyr^vZE$HPv))MyW)hcndjZTxzb+)5&v`CI0+NdulsQ0vR zhsRwxH`BxVyg&QBR}w2D853Nuqn}gj_ii*E#6&=)ty#C+Z^>v^1lDOBhg?aw5^_7* zxuD_G9ZcrBBG&**ri3pJa&_s)dQQ?MMpdj8;z zj#di8YJUA}rk{IsBPJj%4xWNNYN z+p0Du6Ej1aNbv%oESsJuKS-;c3-$<85D9VM$RIH`DYsNy@LNJT(jWdSqsJ0Gr-&QM zSCwefZr{sN;vBQ8B7P7qXeyuS?F&)mWOMEvbmu4IX#sg|NrNSGy|u49>;!1+@O}0t zhGPG z4gsBEM_2^%@)&C#0_@PO8a-T5Lg8onosda1pe=4pW~bUo82_uX+bDR5!+og5fc|iM z9`E=R9`!&U0!1fbMa+)9RHVxtj&VsJg zYmGtXPCQz3=esAj5fX4a49icEgST5pop8@WM5_c*UOLl%=COPIS&`BVO~|X+yrz>^XxTBO;LEZ{&L>D&n?D3_;f1wjqf7EDcwx*J z+%mW3oIAl%Bcta1(J8lu(ex}bO6c63T~195WOv1~TdY+Nk@E~M0w)2CamoLLRHg+@ zFw6nR=kzB1I9aYHzINhmt0pFf3A9$GA*%`Ful4PD1EVb`CoAaH-U?Vsn&8A;PE5NsCTQ1<6fB=uen4#!YALO+u8OEN zRqLZtay1#0b^{ls9cj7mr|q5Gwc!94N18?R^i?gQ{E7X0bVnt_B8s@OZ`D;tWPrNt zxg3L@8b?xr&AuIKi}i^~fjR7bL7tt~ym3=-BLWeTxGH$NpMiiW$q#OL{pMVr!?E8Uu=ju*V!=?S5~ zXO`oIEZa@z+$TAxpn`I}s8@RCHl~wA@i6dVpqSqXeTbGy5bjBtUOvjf#Wd}0C7+r6 zM#$P4f&o=FiR^R!!~Ge;QuR zr_G=0EJ;`Neib_=-GjhYG}3bBYhk|zL@eE!?ymOT+_~MhZkoLLZ$WR!v*)8kR#k2I zyq2!;B`V(B=9CadZ=)&NIdcItnqx$OVOdJ*0C5h3j@5H_oDgKuYByHQKVm&irEgk1 z=#ByqJhk(NgqG-bh+8a|g+bSrcfn-sLMokd^d@3b$34-@-r{8b#J5l#QOV*(yQjBR z{hFA-9;-eDCCWQYhn#08&jakSnN=v9Xq6suxfZ84k^MPs4Z| z1jJ<;1#Th;U%~B#q)dJfQ{|i?vARwT@$_7!b}<@}fZn#d`AKI5UaK(-aZ&YD9^*Hr z<6xT8Q!(bd)shi<9))Gu5~qZE+;qVu!=uhtwPF0I{FF}OYWGE*_GPM-d&cPj0@lp8 z1wES&?0z|&ygEq+WLc_l<~(la5exd$IJJ<0`dJ9ZhE!qP_~3w7^RO?*EFxQ?yZ$V3 zPp_)v*EH2xZQbu@n?+3~R#-y&nnP#P&Zr^aB64a|k8J^8H9_P-Fg)+%YY7``5znyP z3_SrIU$KZt@l;U2{%l8&;eWNH6KEFdZiI_$*Ln{2mzjtTDEdEPZW`Y`+T?GdNR_t_GO-56a|MST_Jt}n#XDP&_Ol#D>0r)0*J zj_z!^TsG-w*?!#cS?p(QB&7n|HFx`-AwV|4&09HX&l85+!)y_&=7RJcox#W8eJs8_ z=<;lhjW}T`M;moHpQ??lV80^+OP=H4cEk*jltW~M!(Een32u#nEe@RJ8~P~#Il+~@ z90$x$UAt_0G`(tU(xWZXvRyQR*3D7N_M4M~4<#n(hfTWH;wKz#JLw`9OLg9*>Iy_P zy#EJvXq2~5^wipXI+h=+bZ*=UTOMzbT(u!Y67gD$+eU=3F&;2nK3+Ec0-)f^4msn>IbMOkTMa z*kl+}hOQ%KIb@_n$YEF$sj=-X6&qe&XL`61J7{-?%V)>B5dciPi{b6$w%Y+5Q5-fV0hWrkjAP zh8*3%7AF6!%@y$!9m=BFFn<}<_ zj$Z&&RDf4+sy54uCzwo2GU(!SegYms(+Uq%{Vh9wsyTW|hiyJ$>bU$snI-xnI-H- zTzZ$WjLR_)yw32U#)vw4RDCE^8=BliAn;Z4jH6s^>D$&+9zzP0RQz=Cypihs~WFC)CF60I#HO=~gdffFK!^ zm=Hnt1^v0P3Lou7Hu+ljwRO>NCAbYWXf%+`bxioM59m7**Y&^&n<6Ytn29pg=%uT! zB#=ag3$g|w6F6(@3igsH!E*aAY@BiRD1BuyK~r%`cLE~Hm#}D8ck}BndFq6sA!+oO zLRA6P+;t5LtZdd(*o7{?CnJN5ADAfJKn@#fn^sfv?Cd?8z<2BS+N2u{)=+;nb9BT3 z^5CS#MdNBk1>RCi1bO-8Q}ckY3nkHNPtu3E-61XC<^UGt7K$s>Ompyx`B zk-D#h;c&n27139Rw$rs2_0Kb;Q0=s}uKRA?mBNehGHLD_@b=u#!yP%Fu*?wKA+P1#p>rw8Zwp-J>_cNTK9OK<3nAW!q++y`|o9G zdO9hHUwQNicJpJLNYLhyo_r9GmjUA(%}H@(4+N}P)SRw-PjGLQwP9GTvF)4``0R{|ZD%@W_-e5lBz|&5SU}n+hN>}_W{?Rp@|n0%Tj_aT+WRNp~m~R>(E}XA5!Nr&6b=dxrJ{u z1x;h)z!60sa-|F#qCobVs2p&G`9@x!K7m<0zJT@2PxXDB)xpd@(W8#IXYK_EgS*yz z>~1VuJ>zf;e>s9%(*KT86s>6&f>h$~4~^kXY2%bxV{%O>dt-2gz}GfJQObhlIt_X~ z07L@4h|9{x;x_VT4ha?XMtua?Sost79RZ5K$Cb|jb2EqR1TtIsk4qF32hD_?PYn*L zf&T;GXx=w(_=hEDPD&sikFpw>oj}s$HU zE&I#`q9(Tfu|-v+`Wb8q-l4&IyU{-`tT=BBMQ8^7aEcAIgz5o$ncEiXPZ6(X2~W_( zsSOv^ul>SP9LrcN%xI&j+xHcPw)Tw93{?$91riyY}v;N%XwGr!9yUeVdi`wEC3F zF^Y*cVA&-rx8JWxTty3rH|vr<@guX)xl8-RqR0Pw%^OA z#WSCDYtqui4^L12Pdab2Avh_g4neu7ZK^sr!9lu+AWK5bxW7{i0(L z6@|$$?g;^cCL}{UYsH493Sz$|JU54UsR9pC!8^^s5O*)Y=g_w`www zP*AiN?TxoRyhEQkr3_7=kRa>UNVPP?`YdxOu=JL;pzmUWg&23{`GQUpR43#sIDw_= zn`At{{&aRr>7FmM;m`W9x2tF>xk&c8s!yyp*PoP>AN2QrN-{)M;|f$|VdQHJ{fVE5 z>zYkYZMCGzGLh&9BhkzE#YQ6qf|?4A*~ZhU4uL z;L~yz*HmQ@a(K0moYM?BkE8mHh;>6X?Yp6P^HkaQribbap{0%OA1-~ttE#~D*#&mi zcwobtnyHNS+_D``pwi#;o67YW)V-MWM%GpMSEY)hzeZCM^{~Jkl3sDV6Wg8^7$N=( zp7E2EGmiP08wJ)^KT&s~CEd6rA+TYHpC-r<$@0UqzSb;CV(o;*a@nK%j@(KR=IK^>TbzRf;R*^%bj*84FecJ}k ztAafdm1pz@qqvvbIcZjMUGGL|aOPr=l`Li{moMs~hBs4H!5csidV+ZN<#1x!2Mo{^ zH;HiBQ4o|ZFUY5%CyeaUhYQUfbA)mQ7!}69^V5d-amh7T+VM%Upjl(oJscEdsZm$; zyiIe7+bVH60?)Bz(z`BKg#I?~#EI2=fobp`X4eK=q4?)}Men3h-J@#^)tw8kIRwDb zCbbzd-i(kt3O60M9PU9hGZ%v1lgp-cdw`(+VSHdAeZXd;@HrJl1n!~x;U5m!bQP{h zq}SbBlPgSh!Ubn%yc?wTg0b6iv->z%>0SP&DeQ^OfJhbpqoPEMKMG9B3>$cs(USWO z`#P=FFH;|l#7J$unfijoGd7nhflyO~TssvxFc@Q+54xae_S z=zfMB?7%LH=eYYTx(wRncEGGslRYnP7mn_b`evFlxGH9+Wo2Y7iC)xe7SXJg1Y|wZ zHZ<*bxI}N?JYPDI2GWMg4;!F$sL$y?W`uQ8zvGeIyB&Q))P~!#1YwN$<@t<8SC&wTJUNeKq-`GG(ykQ! zC9hl!-5mdM$%C2|ctW2^nIXvJ+8v)3ik3R-s%sJznQHf?39xnwZu`c_s!@BVO$8Lk z5=x#vsl;Vc8aYAAEq@MaJZ_*j3R+2V6oO3ZpxC;jjZ}lI@m%(JLK+F0v2?;-4yQ@! z^sRS2xm?vG;7X^3nxR7iALRt~?3JLuCQbvLHb2GcP9wN1LNdlzgP|B;@G>`NNJD>> zF_sHP5Sf`i;0Uavvhcm>u(~?vjxOYb*whxP*CJdWe{IxCP_6$YdX9o~(3yTa z7$&mg4!eC>lMH^Ykklq-M%f0I-B_{dl9ohzP{(p@^b!FT)I||Z^ry?Q85Y;YkEv4c zCW4Ia5=^bH-1Fz1(O3n{odT!qaD&pJ98R=jcc#SvSq`OS1(2^>XWNPuIl>gT9%^|k z>%f3RKu(l9#m(?K(SIrjpgv}NWF3^4?2P()?~?|S_@6ALV{qKpw3Re`LDW6z*pmM2 z7ycgt>R^Z{f!<;;y^L$?UCd+#1bM95wINFUC*#$Be*tCzMf(YPUPH^w7GsiGNTwqY z@~B486Z$FqQf>LH57*!5gpet1S%6EY&Q-%m$qkp*lvDHf$U9OUJPlw+e z?>hs4-@RuieT4DeqU6jS82YGCm)$mb{cJAr8{Zzd&G|t6gKF6sNBTdiT@QgUBO1G^ z>}v`ZlMA}?;oLoV%9G$l9reu*U9)e2qz#j{RIw0# z?@UeY{-9xW#?wb1>6VYvo4D)_pB}6Kr?GR1v_?4l_|#$BV-4&7`MaAnNUz6$a5cla2{xBSg{Mz;n!C5)8#z1_!IL2iRTXmN$r&pmwz z%czI)CJ_In`Tsgb9&@-di_qQiQ5#z$oS z5na^aLWk3RX-y+_7uPU|qKXl9Uq}dJxeT>Gi0ypeZA1&2b!o><8@e60&VXyl94ol5 zYduO>2+gutsyE)r{*JQe5Ecu$sPyFBl=)BD#z0`}?NsD-QZr;K)S@r{nhFfabL%3o zk;f9E52!o)0-y6S+_U8k+xlZZ>7=U~B_<<43b%8A>AX$NsaJWh6SjjJ2hk1T0!)yn zOKg4pjya*=!*_3%Sr*Y$8*{K=#;LhT`lkW;v)8sPQGT>8p!N`@|g3t1mf)N zhrijP?4_-;HLuVu5`S<}gw9P`Zv~~~&pOOU@W;?y)BV?ai|;BOnQG%A2Zs_mSN13b z_&YwSFZlKvBimt9I)1662j(XF(5-MqTvu>*x*_`2sBq(2fhRjkXky-?p6+XIRwjSf ztxxW*KF%UpO|6dF)i;uj&b8VNu^4UF#KzCq_g5v5=*x~VD)^pu**mur<;Rzfup_@Z zpJ3FQnn~vZe|YG8Hj(wo`l2> zM{rT$a811F+6-Ns@jJ0yoQtPCzTmaBdK_Sp(_`hLzugB0%Eo-KRiLQo!jDTT*_12Z z0vCFn+yV;3LcgqQEIX%J=Wgpsw2R3Va=;o#@q@ClD`8VAA_}_LlWAtzz^FHtNsdU@OT)0A3GN(?;&|d zqFiJ&or7Ns)@{vC~w(e310opQ5wVA2Ogo3l$7{q4=$n24w^!jvNV z5+s9f^8sIY5C%BHLQWS;MdNCZ2W|Or$@FVtlI7p{#NFCIUpCRSH~vZ z#36sYuI$DUtj=w?(9*iSbsgA3ep891`^z4cKG=W{Cr>gF^&KCm3a|`zK^csy#k>`O zA?5JKm`aN_)^TNVa-!tVy$+J*XpxcOb6fUR2ZOGC=$9hYf22vBH%*dIQyX&n`JWG^ zJq%}89^qi>uO=B|kJC}_`0u?(@J&8$Lx?1|zQ;TN%sFvRdJa=vxW(#>2ZxjyQ_q*3 zsr+%t79*HxQ>}QT8U8qERNgG=2nIhPcM|V-e%9fr!tUOA1rsNXck1%st!snt?anSf z#$UjP=N6vhcX;+Rw@bzb7~3PFEbp>7oFfR&7VYTs^5x{L$?y8TY0H<$V2^{VF6e?D7~3^GV-5arEX*rsO>_pnLAT z9@-Li4L9#TPQDg681|gmfGPreq)y#a>T#;(Nd_QcXLDIHpzJC0Y{2h4YIw)-dw#h> zl(6vDwq6v3AiX59LbqB}4d33D%Y6o3taI+fiqo5|}a*5;$u+n`h-eT#CmyJdQ<@5VA1OPvt(Rr{?3TC zW`D=op;y7pIjjR(oLodKikQjhvbLMtxhXY0=*Wq2a*LCEMh6n1lKS?^+L^9OzMcemzPMW0EIR zL#j_XspR3VZfVxnYS8xx>;1->Kf<}ycUIm^P4&p9*d}!aou&YTK}OzM)cnPleoqL} zuBbWq20&;2!#1>X;u(i4lqjnyz&w0{$3ROM6haHAX5MrJ03MktJTuAvTx$ekV; zaCKvzYQwpSetIZtie4VJmFdA{XF4?Uw`O#gXHHqAM?yVJyf*BC4qapG!$a^C~3t7i)2}ZCt~O-ZHDx&W7Kn1 zZf%PE3~y03VMeQ~nASC`;yN6z8GBRfyL~Myrrs8Av;gQO1^+D;ijzMF8ns2~Q@uo4 zQB?yLuUU1npwZB&+;Z#_?c09CLMb_|G!#z#@sU0>&rH;XchD{KySR=04|{*Mz$Td#@Fu37FXi3o2F|cY>IoAl?ZWu4JGGgwAe{usBy?ws z&<_d!comU1HDR>m|FS4N11c0WTd-^dNr);P@kmD4KRUF-y`88?BZO3Zu1xKbe-QuL z{eA|5__^+c+G-?qRC(O?2IbIyGBp`*467bw8yNp>iJ@@{Ip;s+?~h4(vYTOwh#Y>d z{m9tdc!B4h;;0u*n)i=f6;Bvie5LXV-|cNV&I`oRFtt$JrM2u4Ng%_|ny6?h0xuyN z)4E)hzcNg)Uwe2M&>1SQM4X8FN2VDW5VRkP>IYFkH|}C@Ihv&TZRk}_-C_PgPs7q| z>^yd2$e!B~EmF(X7+pM&K9%z&+v+ctTU)ljadoakMabE*YQpZ?S4M+Fd;U2iL zlM62`m66i95e=o%-`vWUnKk@zzoRfq5UFQAjcw6A1~KQXBHyD>rHKL9Pp+8=ZT_a9pJ zm}LcwpiR5p82)&by+?|gvTUSz_>`UJ9`FY@aE%8GAO7`=0ag$iZ)u|u8)BUL8Sh6j z)W0ku;&c7sQ{5l4l?09Xbz3xAtI38$k!|h(vo)+~9|Hfxe?z?aSNO+iXPQ{@NVIWn8P2auas3o`u#}$P>c7hT3A&;|pwYz2+XUHL&lEoHd;jDEkaNS` z%Vbr)ai{VXhYf8KR2wJT<@in0AGNN^SsKX<huM7Dh*IVyyjL^PPzW6BTF)TsqV6CLIscgxWeQ3!06#kaA4Hh1wX zFbIG^&!*g%>NCz6426mYHcemmRmxqhC>SAJ_@tuWAK|1qHQfCH$0#~1XylOHjgY4Y zqZ&CaPwDp=y#rp~tJ-I^tkVT4BEQ@G@pPasmN=0=9{*Zkq^j>X17237ia%Mi4z8O)mqV|Jbr`>5={HxNfN!n$@?B84jPJX_#+;`OG8It z$$FYa5StbIo}Wc0LwY=4RH6Jd$*mGcI7R|OCWd8!D2GY&$zinKI+8hmc?OPO7TT1z zD`=nfm4qX9BByR){D-`a4k zcr1!S{rf(>%5K@9_OX)6m*|Z744LggL@`phhgDLm0?35{bXQ9|0f>p{QvRb}$d(H_ zkYNLPC-&V4W3FPagRY+%)6;A_q)py2SnZVb0%@=x&gv)WC^_n;5IR5HH|Mam*g*>* zOAV{ps~lX(%8C(C$H}2>?vL8(b_l^u=$V@6|4oA6pBBGul-BfI=!L6<1P8)10vn#W z>c-pVYzfU~TjP7I8SVI%DSXU_)@?mZE>(CLb^{O!q@;~UKT|m3wEg?gqK$8gyI3Gz zxGphfwlzNEfGse*q?_TvQ$3OZUQsXohuCK`UlQtpy`^mD7uM5C^=$H!=sU8}* z*Sj7lK(^=@*q>0wwWR$%U>0xxWl>EB_}OFw9sgv?@q zQSJs>hBV3e&$LQ(|J7pvNxR~-ZD;w|pJkOYhRYH;0#YM15g z402=;%Z8c%aBUu7AdGMRG+lvWr(9AOeAVvl3>D{V5K8qa-JIHVYEbo>`uO z$Yl%FoRrkJ4f@$vis$lGdX{XlUg-O3Xeu}2tPWyH@wb)}Ax|SW&I;y!06Wv`L5;GT z4PT{LYvy3bp1E<4^D$KI09y<-^Z0LNA@NW?YLoF`tKc%M^iYOOHW_L}YXQ#^k^h9B z2ZD=s70^YbCuR2_`UizSD}$a%C&a!?69EJnTrWb`$4M_9ea8D#1jH)gLcu2VX^6*w`))n+f_8V8MyNVI z18t0~xkOMf+V7E+JG;u)rq4y0lxMFlI=G-&Z3PmEh%53`I6A!{Mv^hTFh0UtwEzBW z+H&4srs4Z6jIS|gq)o-&=Z@RGlx>+L)a$-x{w67;$shw|TurGtJ8+oM|lGSH`j z3zkNmq8e~AVujzqHI51}({^bYez1QllmuNUH9CXAh-bbwNRv?x6kF`# zS;wuy`I8Q-4J1<>B@>5K0fDiVTB1WT=vucojJ$@`Z8JE4*RQ zLW5P*xFJcd@qsAr`dcF<4XduZFf~5IBC?|8aWkiVAB4Sr&?m|`7#~_vQMFO{2u}2< zWNw{CWFOizJl;#hpIf6_-twixq3g8q3)X8BnBRf!xYfs;s6L1-v&K#+E1WuhBbQ~f zhWaqH%|`d!R|IESq@a|V1mc){{-LHTYQLq zW^MO#-3w*tD{Zm%>~Og;MVg^EQ+5tUUy(b&8Ec+lp|cJ?#qFfITZoj6vWWluq1e7= zEEfjdHU6%2Demv!u)Ngg;2zZU!0$aH6I&@$gvAaF_wePz?ukgHBYI9F9A|M;;viOc z&y^CVmi@+{Y46Jw@9J6dR9Zl3b~XC1v6hryVG+5;JLLBDNG{wCV5CJ&IUTs|K^YU* z)BTPnnRnai!v{Qu8P^R+g|!6xlh%V9ZSBIC-|V9KWYi3-Q+{yXXCyQ##~C7C&2Gpg zh@K?I1sq8&9MYt;?AJ{es-mzolg%%O1SZh4_!BeFhD8jO<}VbFVbv9)8N98a8kC9; z(dwXGBUla^*C*_cwO(*m`)KuLrrqN@Rqj;4WP&-1kBYVXpXvfbQ%#A;XFWoeElciz z&%y|n9_%`6diM^cRtFhb(Y|i)cpqpC4EA;SirJ(TT$AA7Dc1{f#|ZH1dw0;H&Tjoq zdK#XuaBWY!c&~9$QTTpfXMPTPsG+aGaL_kTOsoA`{(BG7D%1QN(nz#GHxDkkk&?&$ zesjK{JK|hh?%Y27vVHsq1Ec*@XluvIeJ4I4{=8;ma#r4|Pgi7bvR;6iB&w*tv$%7_ z-wlsxRv03Af)psLjuK9i`PrZ0N3I(@Av@)8TW{BQwD--+*Q<@AkI*V}?!m91v)pAE zg3jGLM5oaPAbKadQPdlb!pIdO>3NXiW;A;|C#gyAuU~z|Soo-zRiDx<_l>{i0V1R^ zRs?S5oJQ@94u`1QP_oolw2$&$rZwOM2O#i#NYM&k9L$n{u!9cwg zU7xFgVbUFCb<1tG_(34A`~ZPYbhJjC9?)mbfIvXW1DeC25qOL;W~gkLfEHoHIDQtsy5MgTikr+P9kS>B>;_GF|YTWwkV)gW+zm zL#T=T_+wYAP~{A(oW^QT@BUx0V^PO_XQO?c#UI8!G;Aw{C zDG%T{cnO=@f&*F`%OFnu?Ajt{M+mt=IBRLLT~q07;aGt3IWTmxko1BjFQ8NL-Qa#Z zCH$Hh^K46g7-ZAGyD7sXEpjv|1v91_`-T48aJM$&Pi}4zTlUPKH{bq1_W^q8CD+x> z9ly)595vopa#X(CQMl0AF|pIf33rw&!zB|gm9ZQ&qdG&JXs||MC+<7+vkLsD;^(i% zig>I1eFlTy|Jk3m78@>AI<6#XEnD|ybXc@b=LsN9WxjeeDt{FEYGq=GXAge`sEm`G zqaU>6kkN1_SmT^4N`}8JTF#akeQJ)dHr^xmQu{mXWY!JE2(xfMXsvWRJs?2$AbTh= z;El3Smc>OwQb;0${A}(%65rHL9v`OS@+F4np9tGUG_0Ir(Bw9!>DyFDZiMQVppS!|O^VSI ztYYZ$AJSN`*-wIXl7kJaashx-ZqUG&xh=bctJG!K0W#;DUAL+y7RkuK(CHoA--b

S<>f-E>Ll;EhB**c>;}S+@Ruj)z1AJBe(bObZI!FZ+sOIQneP7!V zogp&Ep4iLNe?}oq;Tj8~zQ4O#dfs-QFv7-M?+||{kAV5@O7Cd5cR$f` zI%LgRQ069Tk?>BMAn6o;ZYo;1zY@;DU&xG5Y^kJ*@K-z&#*Qj*z9TKs*EOt(I*g+p zw10mWv5wCx=rs$WY*94z+N%G8CRgfbPBZR|SbiPfbe_Qh8AgMB8UcCdzz2j-L1|)I zcQ(Q!Zo(ruBh`BIJ(4E&1&bc@H#+_F$|BNs#C3gs11Mfr94}7Zn$xmk-QtT%fu=jN zGxpBppBoMZH>LqqeT-0UJ(%q7%^4r2mHzV$h6^I}3Me6|;)!m)>Eyr&lkhXBB^4&NA2cui1_$sJnSI|w0d z?kMBi%cP>f6KrGA%b5#m4zVmKxa3H{HlqZm9)0zCwO}fH-XK%n9AqJ6lrZ6b`!muE^oEk*ac|E&2~;Ti!ZUL=xYB zS4YNx{caxW{NK7 z-?S0U8aV4u-D(X@Ubf_%EL%xT<{^@L#;H>kLMK$GMc9F47*G91cVNP{J?>t5>{j>~ z`t~kEnN_S2y814})C}+T%aq@x@2b8?mxYS&ZXZ4KSD83WD-FIP!d*M33Nt`#eh7wIS7+*{Qo`{~ z6334k3CurEmy!-e&UtCSa0uR&tE_LFMvIzWU6T1t90Tt@WJyEM>Ng62sKyneQovG` zi&&bnY-f$gOjICxbKd?nsIFfXLUg~Em@QfBo_hb}Y9MibQGD>xbp)WjVd?6a@lfKY zzrUP`Vg=QQq6)|0&I;8ITFuEEU4Us8lT(|G*Ho#Z>IYlTwX^vW$0_V3PweW8*@i%# zre^tPqQS?q+HOI}(1M1dPakQXA*9+|wE|lwI9{V~LmufBO2Vg9Zk8&)3*&$eRA>K@N!d z+5(YQ4|9g*pgCWkpr?Xn;_?V|YS58{rB5p;;m&T0aNyUr^yTRuzT1HIFs#)0a9jUD zuv>9&wCAGs26%8vF`%&~8av^C&NT!i^t+S|5^KQF#U<`hZhHSy?;mP)7C1@=afwh>2-b`HTJ5&@<+Zs^k+W8xPKVXqEq zR&XK?Ny#fP5$KWNI0+n_Ldq%{s!j~p1*B=M*MEbpke!K&Lg|nsuCjUM;Q^+}16^=xUSxsuUUC`x>Y9zgl^Ev0JaG0C{)UK(rL~RQ!CUot|@Jvm3|zwEgniuU|SyO8!;YwfEC#!?pMN z_Ul9xqyI>0PFl6{nNtb>DXxm&+VWm~aM7vGbEiTpNx19J*dn=RHosDPgE5(lPLgV4 zVtv8fSvKClRmOps)}JDWB@)VB++;|_e?3Kdm?57#@B&b%~|F8@9 z7+Pe!!o}SgYT_x-8N5r9z3BvsZ`96($31%OWNuVbiJlGluVT_>!_kfwrQu!ke3Vh> z*cGbT`JrCh;%fv2fnlQfyJU&*mQ&hk;`jThpB~qF>wVU3a|`Wi#=oB7_JC-GB_oe| z$DM1}%lkrb#aS`eRxKN+fc2m0Bq2BxvL5RuY;#PE<13q|G2T){#;2=KDFc&rQ}Wx% zpr!>{gIgJvWExrzE5HRLd3$8z@M;c^APmCq?~5MO|a^d_)B9yju{o*04PBdsDP2R(c%PS|$$|ol^YXh=(jy+s#7Y{D)-V|lwld9h?9h-%v zk4I(5|E+$;@TPTwleRu6ePh6cOHija4DH)Gwaw>=fIP^$i-HfM-K5Qbmn@4!b+7V< zSxxTs>i`1X>W7lJ9&)$YR;UE1G|aNse2a7%m~?rrM#Ms@IfH+DTj9c`%FsQC8 z>`+OGX84AD5HsCiOA`tky8s``3boB!=KH!8HWum#5l_99Q8~G*#$M@)7Dwpc%I`3p z;16LJj)PxRTIE4tB8>z=abt zi=;}cqrV=0X4em9D=T;SBLvbiECR<+4?r8y8g{3$Lcn$yrU{MU^w!VFARnJYXOzOf zW=ve2|D$76MR8M)EGuF%(DnNqNUC{h?B?*QAeNbwp}^A@O8?k;G*b~2SH6ir?YSom z8E`=-9wE{rf6!@n+Z@h1x8G#hCY5?vipc8-TE6BwC?bm8iWX(7b`dQ-W6jK|Oxf#U zkcU$im0wsUn=MpW&=m2F>GoD0pLdkqV2~vQB9r@`V3d>_x4o#8NNb1=0Rip-vy}*a zzwfqoAhg686Q;#aoYib9c2;}rlWe$Vp!O&yU90qg`FiS^ou92&Aq8Hh!45 z#=Jed!d#Q9nhRRj@`cleqP@LA`e1kcxrZe{b@&*b8^jZ${?Adsr)nMq4Su0zOX|Ah z4g9MINoQ!!#)tNhcSmNDbA=-3@BZkRQKuUe1Ae~|-_ma7N$`wgcm@a!5w$)b3RUb0 zelzYGH`|c2nvTRy2UI*%N5FL~6gV04HifgZcKx6kdtPZL=ZMG6M!bb@C4A&@Vur`^{Jp;!6EtxWof5B1 ziN6E1*K+|^s+rA{u+fyvbOi92*x9h5pFlY&CWN~p4z$n#mR7a}Qj4u@|bfMW5P@ddF$q%Z)ogOp!3|^gY*UQCuZG|JVBxx1tu&jh&g0{AC6SCNk2hczMhA?{h4h><2PbDn10{cao691f&X>$zxMLD7?)s~jhQTbU5~qxXPM&t~SgGA=Ebj2KBUP|ejK(km>@Nm5Nvor6X& z)7zC1g###!1s>a7<)ft$vfCQiPR&jZGpSCR3^*lKS+395!&y$mBt}u<%DHp*Z;XMY z(Rk*^?fie1yF*;kaP&iQ)2`HnaD?WVgGE_qw?Dx%BnHTIY3Rtz*s^?~QkUkRjIRIZ z9pYFz=h98;Ow>r~M1wK*f|(n+3XO1%8I) z--1C)>q=IT)*#JXtLILKS&h~@`iXb^-GvRmEOKGemL5l);~*ymy)%c^4CV@__JU$vd3rj7&z|}oWCpH5*pS<^8^4d5SFe^b^521TUO;`cHhlQ8Ns5u0r zQ@pM4e6co0DmL>Zja>FC!noXGh1Ews=&;M*P6&ckCC0(o4q85*=!Q4TU6YhVVv&3g zi3U_5Qi@BlOuso(b3pI}-Oyp-p*MQsTh3i}ku4BXcaa*B?0u%Uxu5A4_jh*lKk)r@ zqIBY4^14HJJ+FEXe{QZlanpKu`)sMT^YGq*(Ix{7my0H<)}Q{E2Sn5IX?A0uw(afK z-ZYqu0I-EEDPW7`)y zh>D%4sV~at_AWx6XBB3`54shJ{(y+)V>H)U5aa~O6%=F*PH#`o@nDJGMz7g7M`a6$ zM%HnqT4>DT9|Cw`aLi)4Uh>U5-<)}%hM6ijdFgBo5@ z$zP!J#1VUXl*NC|?+IAiBhipCT@`EqL$L_%7|!n5a3?$x z4lGBTN zyGl;DD7#ry-(rwRLZhE-MLLkF^J?nX7`%VF7vM0dwcOo7t^NQ?Ni;ndl>lr@8Y`$P3g+$R{w*< zk)?{@UcD={(fIcWk5bjX4y4KRuWW0mp;Ilz60FI8a>E|=JkVh*E%B9k!#w<#MX$b7 zwsA=5?(~VNDzk1)wIwz6P9!R`N+e91E!O7e^5ZI^oLY*x%uzhRv}>kz+;YAtc79gn z)OXHbk>7*I0m4{8=v&UM9-p8?kAv!M8rku2NdnGuv-oJSgw5LHKK;qa7sTHhhUV8S z`R3H7>$bh(hmz2B#BVoR|4o|b{l~ibIj6I_5b20s^YKJJTrJy+^D~`&{iRVt)A3aYNbnP}-*IQLZJ%d~aFPX`#_Y;5S@lbmgpd1I z5}dbqRC|FI%I8j@r({G;f604jUwD{oH8nnn2v8Fw^7{YnJmc-}4}avarF)F-fcUc& z{$&%xcPZ8&Il1&jxWb^dHT};PQC7Y%?9?sn;Eb85dgG??{{<|LAyPcf#ZnHS7j338 z;aw}&y}5(uAPl6~bH!3N?XQ_Bk0|}_Na>o+fgxwJK-+_CG~JxdUSW)X4wdSpc-lny zr*j5>1IjCUp6R3i;)~A{qZn`^dRuZnTQVtaVdT4<;t#jA;WreWeUx_fS@rXGf3i*D z^LvKiZ%UVaWM2{#`lDhoK0Nr(^m6&D4oHcft+(yag0$%N#qF9W9B5CUF|nms?G0Ni zEJ@U$>jsWVQC3YPv~F7xlu&^=HqG_HOV`;#88VoCbjoC%<>R0p*z?|Sxw8r!bdpLB zc6eqRY|vzbr%84tIS*cD>w!6p84WRG8`8Q#wTjhBO~}C|+KO0@J6L9$GidUkr`(dh zQYunFh5O&@gDYUEDFSyfjC3I6WErmKwM5&z!V6#+3bUhi%bK&j*3sj=(UM?G&w9gR zR(##n9v@ad!rx$a`Zq+;?8@ZbE%CRM)+TW3cv1Ce400Zpv?r-MN7U zHLgtgD=UDSkT$dWP|`O179P@UoRwrBJU9&DxGiQ5!^NfiWuUVLBd>sa@XR^W?M7yr zT*kB=eQ_GhV_+D{n4-fewYT#uyZwRpH80ywtjMG!fK(ZIl4~rY&44T{OEp^agtt!u z73OZ*>J0zrIOL3wX_bWw0b;d6+4BZe?QgiDpt;vNC3`C4Mmz6$ZF%w~MZZbTFM7lI zoy+{nQE4;l$alz(gw-*&Xr<2{h2JtgzkTcYbqi5zii4H)iAuUhU zUBBw+zdrHFEob~b{@OjC#T~0YF+K;-j4ez0E^YW-Nt~Jo7b<$z8@K%q9#?-3iH$CpRvUb{?iJq>HS=$!8~ZLYA0^d8sT7_2+fF^XT z+ph?W!Fufe+?+IN{jDjeS{FsKUyVukNS1G>SEw3UWmO_=%wN{ zMIK$xHlqn>-$}g*08(ns*yq|bZk z8QeH9vDC02%Mcqp4K1@(K&FwvtZ*u0t!T@W$uFy@Hcucb3qAZ{sME4aH5yhKUHvcK zl;qlvTD9g3)@ug^F2q)}A5OBw#>_0|h3 zVwoe9qk3v$)YBP@2Ys8E#^?X`Q1M#}ek`+H)nfm-*H_?6{8qih7GCyb&7+#)s)+fc zJ9l?Plg8TGbPzgdpu;1Xv$V>qV=LLVMsm8(O*0XYD~-R!=PNO$;;x&SqBRv3q^^1s zZLH|Ju&Zjc!yfbQ`7XAH>YYdPbKI;7P^j7p{pW2{99xEhG}bm*3V)}9WD5ck>ap&D zMh5FOs0MNC+J+m(WuDpKRnt^=Nt)_$gSNce5Fnaj;_DttS-feRBuN>dzh(W~p~oL6 zoTed4IqUCyXs$|C4uh{~PTebC^}G*h=Bzc27C!vw>lyPOnC>_~o=NPR`?zjP8Oz5<^NnOVu~|=7CHK%g8zLYzT>^tdwS3B`%VuFn;z`f z#cj;9+MDZIwdC81ki>+WGn3?%S<%N^gG{$|-tXEB6LPxd*}p2SUl3lRU3wu|h46MRXUozS;f zUsM^t$l0r1`pcp>oksnQ$!E-Kj>7F8{Uj{-d>69KJcvKu(Pwm=9NB}A!3~y;W;;yq zcYVfiE9S7Rw*c1CnidZ@haMC0O?EEag&~ui>0RrAp^Y>UD;w9VL(p6AZ-}OjFLq=c z^!M)#6MvzVA>PcZftBqmE)PJVl5dCkoFQXpK;muRaVFaDjaCcWHQU8nN>u>SA%#-;H`ReqDp zcTW6X<+$Afzewy3kq7tR=C+J`YLbO6ar72XSokm;ZHoxw-v&erA_fqyb}@Hk_i zq*vt4_W9B|tjjY&q8R_bTu%E0+SCbZ{yYT(k5}M^5Y|D?#`W4GL-orsgs;Lu7Uig+ z4jv;QT!bSrs}`8A7Dz$YcG_)Wk|u+X7;jT{m515ud)!Bq6f-x&0sAfkz|?~k7Tv_; zXU{AoH5v|lC6_&N6Uo7I<+j@Q>~Tyyvd!bj*zf(ZcGpZ|JLw}F1mS$OCIP%-hRkeH z@%Zt7MGa&CSrtu}fuig4@7P!Z`^Kj_3MKTO z-2S*8>h~Swwz6q;<(`WI;1WeHD)e_Z>;z{XdS%kfYyolz4&Y;KVGf;AtIIaX)L>pj zYyE?PRBfgP^+gzNymgl|$z@Mq5=S)S=Z91`h{678`M7OH(LXbB|I>CkF7l4BDisij zZ{?*2_zA2Mg?qg6i+%U<8sr5 z|GHFxy@Z?a`E%VT-vyGRHu3jp zasN4lFQY}ORzZ5SA+T{z`YkGUTsj1L4bo??_xjY0s_+DeKr@~87bma+w+nY#T&vl!R`SMhIZ}-3C zw}5bTbn9M4X+&V41M>LS!j*?o>`ycNq*w=r_m%LTRehBKbOXAymnrw9JZ-8)S0QE` z&Dzi7ZY1rx*!MtjZ<^n6Sa}4;vlh4=*P}s^x959kijXMX_-{39LNMR-EO0gOMZd}B zpRkSgOcc!)iyrSZrNm2jZo5Pre+VFGD}c0W0ndcNsU;70sXZ~=LVZw7T_2Asg;52z zzKeTX?z$h==TeY-vZ{iLDPNhRSA(--Ce<3=m#*@fy_~c~@#E5a;4iSK0_1lN?47~* z!8r!>4Ran4orK9cqh_AYBSk#%r;=Qu?E)RV?K|i!9v_?mq-slnE~1>7U*@|j0aSk( zR9wLvea1YB2uupz{S7AYefyR}!}E4X&T^)YW~l)zXR@%~EXbcH0*@ zXBR*?E7s>z?D5uzh&KFYVMjj}AM?wi&!r7Q8Ub4Huxy1g3}$eYGBg@H;}}$M=xAh% z=1QUgIZP-|Frl4HHIjLEv#m!U{Gt-HQ>IFKep!@4+*zzQRn#*|OT+E(%+~+#8||%< z97aSmoz6mvl*0IA zZ4dH1CWs^Bi-Hz@2UheUY_d&3tyD>`j(amYy`V75rqsTk$sdi^ zqDNK#@-Jx0DB>TtB0|n&bIu**!@GguAGEme+m6Y5W~-_uFK3Ww*~lCMgRLI_qP<-+P#QXF8sroI*vA3{Dc6cHA4Nc;%S?cY;RShhNb>h+9J0Q* zS-2Az+3h>ar#A1@AE`KyoGwX^L}rMogy3(()rzsbR(v<%RKhtZXYD4No)bToU>{lzMJfcml)STQG6A!+>MuK2#!N>jNp_M1OSGjt zyn5Cy`uj1femu%ke(eppdHU^c|nQeYS6ai??q3xpDmAyb$?2sgTGB47tKILoi_JJD$B!^B`ze!?|M8|tiA)7 zvH1z$2!~q4g|8#3IKGVR3b(&EDipUfUc4O8HoC+E=Vp^3v>Bv4W=`zOPF;%DIhtOl zRi7^0|KdD0=`h6jl#o|#rAA-+KTF6g$4Sj?2g*|S#iMwdFmiA`%AF5 zFXX>it`tvB*tu%|kAEc|6!ew5XyL5nqXoQZ#T+h6H1GDAZVdgGT=IM6Y+<5B8MK#9 zR7T}EVkuIMRz->@HvtSMK@GFTc)#QP_u6fo3Vc(pT(SDb<9o#6xu{y%4zCCiCS#OV zaiABET!g!^%zBIufTeL7^f@HELk!e1#Aid4yYR({<@z(mn`OtsM0weU`gVE0lBM#+5pUxWGI7wa(MY7f2VCX@PP-JhU#AyO%ZOP-zOW}-`p8Yt8H!j`=qK_*Q3Q_ zUhN9X@C0p$RK!O5(EphWm1Ygw_yn9*F~M1pWrx2 zl*+nO;UU;og9SZ*N(#NMT(N;>1roie>w_IJl^U*+Y4zCIv@NLv9;N%Ibgshe_^LG7 z)=eVocYy!DE{!hASnX0BmiqJx^$(3rjR2DSlzeL6CHV$()3CXY=of&*xrQfR^Zz z4_PYVj*ny4$B$0)iv{q7A@OLx`uGp!X3UuRaThOI$CqE$LI?0m>5^X-rK!@0Mq15^ zDsa8C6C~Bk2Jz94oOLaw|Jbju(;M%>$>V?kCH#)Vb~&b;P>N)JdDTY3P!->k(C0T* z`nIPMK7Sa{e`Y#e!rbJfxrIXBS;oo5{j!L<61MRjIst|E6!le7fR#u}>(pHdP1T*9 zp`p8US0Nw0<$gZ(E}mM1njp?fMaUzCuU^bt! z2PA_*IUa`u)uAomUmK=3XDzsuV0 z6#A_wE*%B)Z~hi+2N7T^X@1cQG{}qa=pq&lPE?s%RA#c*58lIqm;~&E83DP(YTq@a z8T^LCQ&AB$J?u0YoKi2dz&1UvNq2z|K?G^HbR&>y6vV)^CZI?QcU7RTnvgaHUG&ew16t9@Ai9b zv+Z&I5%#+gjZ{`1Wy=X;+5=JDgro6xHgbf=R0m#oWZwI&*Uo(K%$(UgSbJO+OV4}z z8MjaUvzOnw^ImtIQ1$Sl=ZGCQe(n;+Y`l9zR|3m@4~8G?8^3!?vmMgFXl&}hDZomm zCZZ|X)3`ebBj8e;z4X>IRQeC@^f@13qG}`%DUWIS=Sl;E)$3=Kls_~GzhrHKtKdUj z?=$L;H@0H6$&Cfo-)iC_6MM#LowdH=m1JdEm(G&^oMXu#HJ$PPLH6TSQzS1<8Hpn+ zJDmUU5kewE2Hk;?0~>yz`JgXBY9wE0rw%*JHdXVS1sy7Nk`_kU&2nhnhv^=89BGOt z=DMUeS-+Exze6+f*9W3ltIY=9@i8m1tnnD%?VRwO85ssgZ%s}OT^yN;DqAZ6$h?VP zD|wTY^mRt;V8~{Rx#*r!o)^K**sWqEF2)YD5{E%dbRf^sz-I~Kf@dQ*Q~7-E$N*nP zGZ;2E-jtdibFDZVNt;QH3-QdmJ8k`LpZx9^d&~^{g5Zme1Q8Qxb1ikKZDnh8>=ea5#Sdf~irs6@Cs>e_D>DndvmfGuRPyVl9Ss(Lz}{18cw{S)Ny5a?BfmvuiXT0aqwz40C$&Vz-y#zF?wGQl2Rf z%tRQ+f1|4PqJ{t=03}m9`%^1&<|xWSHY%}9qrZ!opUJ*dL0*FAlR9nE{!inwyMp$;{psSHY~sh$j!XhnkfVGZ zmwqrBHfMw>O}hOFPuD$G*CUDRAYzAU`RJ=vX|$f7Q34?G=yMxn5H{+(V5@S4uTLoO zLo__=?2-&m7GkHJK9wJ^{sQq|Ejp35twn`(bQ)Bh^?e_F#FooOiRNl_TcdW&;ItgY zWQ?y^S*zN8lyRd)Rd|m)@kF;qg2}&K$Yh}p*Ton(3@bv`NumiDq^#jO&tuzT`p+sy z88zo*X-^8=7_JAiu0mM9Yn_61%X;#p)t!)R@H=}Cno*Eh!a;F###~i?KyC^ep!QQGL6F@l*{aiQL{kdb` zO#oXT&4j}aRTQ+}-wn~qJ6PLnunS2qqeKT_zlZj%8u0%87hagEv`&t27BM30f6~#1 zshySP*0gMqYWLR)Dz0bOtc=^Uu;r2}wrqwf9tDx z`HEGP#I#!fNttWX?wdK?+_ie*oPV@FSzk9@#y%u-e_N-2!RFZFH*yn$jeY%(SAQTS#=-Eu|zGYdyqnmDt6VQXo@I zB}E|9nwAs;3~_)V4KU<)W->E=AHUbt>*Bv&f!xpY+&|v;>s^+VO}QwKdAB8$SS*n~ zFh@ADuiW1!J;H4y6d$CQwiUc8Zl>VLrWdXs;L|9}{fMM~8gO({%EjT;A{WM8S!3;8 z7_>aq^}o}OlKSAEGj}}vyCqlp9v!{(A&lgIF;kO5y}5Vv$T4UC5draNHKll$9PV}JV}xPJIk;vhgudVx-sr<6l`Fc z+cAAH)Ggn+e}0NGQP%y=06ceE0CViml>9+@{ET8{KLLJ+$7twS{zhF#Zpg3X8# z_px15bLv#OxER9r?|r3&YI^_Pjuy>?qEwcUMoRJWSg4Z+n&nW~5I>3{DX=ycTE`+#+x~Cq zq_k4Q$zoJ6T&^bXEYDJ1JE^Q{R<8;=_AOHK)1Y6fZn$GOvBS@#B3_ripg$O8ZeyEI zMsxoVOl@|RZT*ao#1*ZMddS!JpCpdY3TEUAbt4j|ig zJb5V|8IJ&~zxe@KNG7BUjRr}ZQVf;7IxTD;Bj$KkG1r&2duOVKRf4quKsP7B#^>I+ z&_sFZwmo;sMb&txKovyJ?+n{VLmuyImlBbv^fDP2+P@LBAz$*qj5ZZlxd^lcA365i zcZQN&wuV6}>OQYyoMDA&0#moh4h~S)$GEA!eoSAn-N$Q2WnNsLk-&XO)st}3BJvqh znE)U%9@vbQRi4f!a_7jBr?ah*_aXb^u3(<_QC}$1lnFR9`1#mxH4zthA||o!R8R&U zv|U1q35BkDtr@oXRPbzyzIg{;lz&o96=YmrbdgzaDlUwi@8frsa_*T&Pb=r_H*1sR zIfc8*taM-Qw?6f7+V1TK-wglam+-Iu<0&d5S{JfjdSx?(zCKW;jOJR^PhC4&Rb@y6 z+%zobMbd=Yy!3Z^^XnWb3ju2CIfr23=z`VozE2KcGK21Y9=Y?LGHuC)aZ$RCM*kJs zp?d&02R9fsXk2q-7*q8G2l3MMyzkh~6^&ap)kue8H+p4Be{Lz6*igcLTZ)c}TPlR6 z{pLnKtj?Xo?Z|D(62I?Kqj+Kt3DoMZ+TkeI4azWD~TPk{IbmLIy zoW%g2wLqI=$(N4t_^=i^KIN@ow$CGb?MAqxz1=4Be#_yRG1tNdjXbT%&qpY0rj86^ z%HhZd#qe@4 zmn->?kO>tgiHn@&3$NX#z%~K&-oV+js^u!$MSQt}6g3>nyHe6e2Hj^)o({OCgR_ZP zo;$zPI=S}f_qt>-fX~xKr_tRvHHS2q0;l-)P;B}ITBHVL1Tm87&kN4&9fPsW?pJa) zy5$z}(0U9FMtrU}2;0&L00TT){Wzz*#bliS%(;}pd~MV+?fyCWsjH?!ZCVVAqN*1% z&QVL#sxr8U>zYmVA39oq7?Q~(b>#8BcC;czS$`KAj9k0Vz>v)X2%3&QTy*+}RqVn} ziw0|vi;axr4s-wGSN@REgL@)VYAN;jwHaO#na$%F@!O+cemz2LjA}tS6r;{O;Fx4{ z?WSlb(%afrUQd3SyhWWLW~&ZLbfBzmYt$?+JN7 zT*}b&NIbhNCk|sh@vSfX_Y4tZa~I*s?oF}iQf?hwxA#Mqe|aoOHh~Wr&u7$Qt!9ql zBX0|)cj%mnNN5BVZ!v3Jq1N1@tjoz<*i~4)BdI-bT%8;Ap^7G}qTMf@n?cHfC#mr6G73LQQkE&s(J*9s!Tfrz5&H|^4CDokJquH{ zaekFSovS^fl<1OjbyxXLe;p3I%PJ|O8iINUy|b2sqfnc^#q2MU8W1=0nTWLz$`iE^%G=xzuK>ZL8c=Yf|+ z+lj%B%bI@UGExPk`z4jG=Mud)IB&}D3t3)I+?As6HGTbtD@n>K)g57T(PlS{m#&JO z)MzpQ6aZ0($9M;#h{Sy{mKRSnJuxlMrJAhkd8L;ea+v5%`75Zb-xBGs)*IH>4HQHR zsdBC(f)^v{**(F&@2C)94e0zW%Yn z-Sk_PK|X`z1C8@FP_{BVkH5To{cq+{+Z#c4o`Hyq2ED3}UUkyg73gMdBs6i&VK3*n z1b%z!S-PqEpkM^mR!MqpOU7~L34Ba5>lS2Vutv(b?Za5+A%l1rr^cnH3l8a33J-G5 zvV3MkT0Jr1od}+jEy-;^GbY+dz)bf}rWXDSf_q(i(oy^EQPOHu&Q6sJFr>DAu0H!z z-itvE1?$Epr)50d&=71Gj*TjCVU5#dm4nWBt}Iup6^^F#nb4aBpk>uV^z<|XA$50KbT=L7b!`N?aU}un0hK)kZiydOc?41Br!{za z4QL(sOfAbP^jqa;JVY(=M-DRdf-)sXe;Jv^n+lP^RPG$hb%Wn|ihq$eQ7d1_A#$18 zoZyqbMP%REC@YjsB=OWumySoQSgBdP6)gqzpLx`f=U)Pg^QxW+$&>XsM^Sqg66mQm zPYXMfQ;2R+2Yj+S9;0?qY%eT;^%PiFY3D&_8ZDCqup7HC0+2CdsXDYd{2uY`zWy3w zA6KwN#_;iS%NUz2I*7Z~VP(d>=q68ttX@c)nNCW{KczCJ81l23ni!02Ov_Q_52y~+ zMA^o?68g)Haj(hLQ^+Ubdv^tmxiidkuREVO?$%P8<{fa7ei$I=e7Wn=H4!K`V9&f9 zpTRF`sHP}e4OdkmMtR;0ZFJe>q_ou(yKT@iC2B}{Pxd0O%UGc^#Cq!Q3v!(xZaNF) z%niH{iXnv@1r5m}?X9AfL_gN!av3tp9&No-<0=D-7d3S|NlRHaa?ok4P;{CLI?g5o z-3tO3RA#3AqOwK8AU!F}C21SRSgKVAMp~cjOE45GZVPyq2Jp7Id&f`PdN3*0x=RD%fLiDXswc3ny(p&m$n*D7WpGCafGxw zM!ojHuTfOcW4-DhM#xiX4>ITmZt zCaLFoqeNi64%Op3 zror`uZpk~5eR@d-Lciic~b1%04t_+(Tjn_4_| z_$#H0h%8~1tI0c4OP4xPtvq+omCcY%=g0`H?@uOP;T>RNm^SR4 zzq?(TZ;rSooDbrWp-A7+52u{$TYch3?W51#wIF>0J`N$5$cv^S^xD9BF@e z!-!-UE8g?@nCjNs71h5|Au7MCzQeTUedsyNW#Gd4c$#7hU@qPu0?r$W+S|_}Jy1mK zA+_^6S-o_HtBJ(u8M?EKhd!ZzftPT4$p&(#^hhvHJ?|3L#Jt?bl!FW;1jPa1BlRa9 zD>d~cg`H1>_Cieu7;d!nOc}g4cv4C@K+EAOSnVGiprAMTN%{#n=0{5u7gm%*b4^|r zvuCI1`tNZp1_jIlB5MC!i3d2OFGlW{*jOlnAin(~wO8?Xb%8P}l~OIvh|rN~@)@&O z8roK-UrL9K97h9WMPI~gx{%9J$QpQ;uvRZYTZAUuOB#jX0v%&Ybol2f!_*-a?h7Mi z@G7|FD`SWi&YPrWXHvHRiPyLz5sqTLGdmn+7*G*iguSh4u6xN#$nN^(l}I9Q>S{33 zgeX3k3i{nr`A!j3AW{z*G+-ZkWY&MEPjDdHU@q~V>i~x36-GjdpQbpm>ML%P@6M8w zJ4XkpuHU)D`7u&HY3Pw^G$*e0#i`HaFk?QX?w8(EI_Hl%@4SvHDoxl+O8oiWeyn&W zXIGT$^QqaexD>~1zJH`@F&QuKYy1~na`9<6S1zN14VNqlbnk;@?Jn?lF10UmZ&!n2 zAX(E|Z2IJ?a9eF^xi%U4*9?Ihb*1(k_p5V2l|Mj*)3I$Q3VNtwcsHLge&JkLbl0)! zaSoUW{IKffDtvKy6h*dYWxcMvhe+An0dLn@b>_WSs^LGUuw4~eN&HpE(*%&U700yR z+{bi3rDqxW83O^`$fhWyE!YtmI(yvT`w;@v(aJnJGej(1Gh`R2Lvbyv6;8`F?R?Cc z&nC{h275iuCo{T^-GibdHfkKKgrQi$dpzSdU7P6h-eM!Dcw=-n={|P9T-*$9YK%su zYrop!%Evr+p!2E64QjxzV;-Z+?)MfYUAR|6bCycl+5P_hA4j~|xgyVp_@ch(9mH@zGq(#rGw_tLO^)8PSro&3Fai)>S# zQqfla!>T`Iyq`C^!TS%`F5XBG=~t*SqOEYb77TadmAR5H%jOLwFs8B@3S=>I<#fb+ zRKq|e?wv2AZw>^A)zJH5@G_)ryN(kAkt; zX0LZKRoQ#?wOCwUMkZN%F?9EtQR1?OkMx)|?5hP$#pys6{VA-E;ln#)0f|5{+|o3OKE^>YADjt-B7Px zgfY~&G|+d`)5{|!V2`l`(l6kyrz%1NsQ_=J=m1$;?NmA7fa;RN4?E2 zgjv<~nMGE$WjHamb=xJxgihvj6GI6I8ZpY{P~j?SZb(VGywIaicY;Livt!6kHXi!qk(R=2|96U~aA^de6=|H*m0N%6HQ_TheX&PNKJn zo+8@W6>HH%&bZ=Ja0*kJilhXXKL02M_+q3WLg*0)oYU9_`J`S|_SZ~t;g75!%H}Ap z)0K82fAF4Y%D)gs>lx*2Batu!F}7V(5i1+z9siDYIr6DN)xaWk6n-`wvfcc}}6;UIGPUzkJ9hca!cfra%zt?Ywr}Tv0CM_-5hbd2ZE;+-Dfth^t#-gMcxTwi2*q>u^D<_ zkn%v?P_e(S;c%qiFy?eTTZXDefgbwy-H#hBP~sm{x`N1guuP=iPR_{W+F4g4?!FJO z(RfG7o_$!tb;11wIig4`aKJ6tN^eoK(gk_Z#ZG7Qv_vPgGbW$4Zs;p|MO>1a$&?ey zr&h<9=|1A~V}|i%|9k51Y}vA~;q6&bfcSD3m8OP+V62p^OD6YDroA3<5&YJuxn`*7 zL!ga>8>r;cdp9>bU+}(MYM#kVzIN96Q~H5S+tlj1)|T66J9`@y4YDcr3~(fHF1r@O zxz?8o3>%F36NXL$Am)LZ)WV-@;fwEZn*Jk)Py63pO+TjKrds_dr(VeH`@ZQje z%h(mA>-E!x{In=V7wD^>&rmr53^#~v>df(&w@H$cT_P@`V+W+Kg5OFb=h0PCw2gAraMoz*P@rjE>Bau6%r z+Sf1W-nrt;#o91qHv`MFw#eUJ#!9Bs;1^REGV;CFh2)|O)YeF3ATnYYaUV2yghFT0 z(ocp*!m?0&iOtz}<iqa4)_)BCq2}T$YzDO+(?sjLy7(gQfA8j}w=^3DS-XV!hgkPb3RB|rYicYU}D^fTG z`~*%NTrL0YOijVeqeJ1l47E?)g?+Zdp8O~@=q(bDS3Jube{gq3P0*tYrGST>>Wl z(mYzVt>&7(HV*e|m|%{#3;7ak)z|2F3x8v27qF2io4p0)oS-SIg}lQCntMH50l0+l zVONHyIy;*{8a^F!bsH`0@_K-U2(*V>Ua62acs5&9Sjk<|P&2@F_%>(fz-7Ry&7PE$4db8{^U8uydcSMgy;A|c&iCNw+!~j- z{AdTU*|qMp<{jdMFyW0PMlY_nGS7jnhQq*HV_saCsh)^SnoD|+H~T2LY1T;|pYEO3 zk{wzZX>WO2KwwE(ppa;a_(83K+Qsftn=2yd9&$?@((frbVLykWhzJl-g(EHx%fTFi zPVR@=#G(k3of6`7$W;&AHi3kH>tA|97{cWmVN|^HLlAIkPL3%qy*9T0(*PKych&YK z@#q$%$Ot{sX~^5Xz<6e1xUmYlM_)U3b{wf>C@b35pLVKVV_9G|{|cr*O_|QMmROl2 ztM2FbSc`U(_c^Hu*(M^}uy{UdR!TL1Wsvze zc?Yt-J7d;Ffpy%Rlv-ZAzUXN%Ex8v~=*-CVQdv}O1_+2=$}BN6XG(xDj6D`RfjvCW zQJ|3LfT!&)&?JQL(0REW?T%597t&8!10Pc#*HDq4(%7}nGi&#n@UH1HuT7-`oI-3c zWLGVd+_M1Er9#OBO7Zbp@QlHi97+_aKFy@*a&G$IA z*Xtx7M50jTlLp|#l3^E6qc%ID7=f&5p8}*q3^IlA*t6HUf0EMsykGHf1w1LEySwu4 zk9mWRryd;Ocj`cw6TRh`!t`Z^7>R!p-(u(bxT`N`$#5yy!sWv|sUHU`5 zGko1FJ}& zVaVqE$G7=W!aI*(-v9$gND=8}rO()qPiP02-}Mw$w#vB}ev3&8`0_BI)M*lsHD`d| z)&Z(4hyXemxDfXwcJaVRTrm#*{c)PU3CzTog*i#~n+qv@2B*rNyWA7k*MeR8i*oz< z*BPV~bd_^8S@4+-Cd@hq)S{3-1n~O zp=aqV_5U%W>4@F_=fliVHuN;$6JN=gd>SVf#@eGZDPXin81v}XW0|<^CT==E!lA@l z!EY&LJ>f>4Bt{l`H-pV2^gQYdj*l8jzmotWUQVAo=Do@83zj5o9BRW?=FElI9smUE z4S7Q2jpc>nuH@GX4BjdM2|iOt=c9Vc5}@|psp+`)wN03>e?gu;8O;9J+XO77$2qeS zH#}3BTMz{&qNGlaY%2s`X}R~aBzRfMm())RmmWTC05fgH%^53!uT-h04N{76WBaFK zi);eKR1Pi@xFQhWIo{wU9*hpJ<7MUt44zbVfDLu$j{@KRHNQ zmWb|%MdPi1=L6d!MSlzQI>L=!vq2ik7cl60`X!n+?wWM(ESH$!j4$D7G0>`kLm2Wb z;WvZy84O(6d7rbG)8!IpFV9oA9$jSEK$P7WrUh4h*+Ffb7if{u^sACrQ6f(K&S^^Fn6 z0HI_+u(9b^1SxDfH$(mDF}P%J(W1t!^vO;CU3_B0;^Scex<42?^Femm!i&QLo#&Zs z(V;i~WaRb2t{c@swm&EnHEWg#ZB!llaP*lS&?JR;T9R`f7*mwW<`|hjZ&K$1HOxDk zk@L71%fA-!7@2t`PDH^BXXwY|qM2UeV;XytQDUH*xg-s8jLNRa@4z}2Lz#m7BtG1S z4e6`5{;+DiueHI3I%+r*G%QJL4Gy*oM9+}HrgL#PVa2|kFWLN^VNLwHPB7g%riVsa zSPn9CzgRyVr zdoRx`T^Yr+kc!?;A@VxGs!4LjLuN6QLQ$`o75_37iV&`q0{tO<&HH*)LqAd_U z)%R0_g9L>8mJ#Xt#^?IXZJDoaaot7E9TBg&Rr|L5Z)@k4_>0+{uTPE5Zabalom}h9 zYdRxKdo1VS(^}otsbFnRC%8hdW%i?)mYi3=s+)4RE^1J5pLNJJ*Iy~z_$v6#(dh$` zI8!%CE}?Bwb>bg~S6J|CJYs;;=8wKC7mMamhZ#GXQb}M}d;h9<3emHS||q>S%AG zgnWtZiz}luukd}ro(2BUtFCBSdl>$F4sWYpQ0KP5byr`> zqh!Zjo>?xu^;lK&Q|GZW!Z%q8ll}RnakZ%o`nLh@kG;L2#C2!04=;9knVo+R0LE;R zeJ?R4Jd@)uzb+0lqbv&D{nvSJ0Ms1o^VtGEhZ1=3yDwRfY0P1pAkH*lFS>?NeK7pA zp7dC2jwx>tmZV(sPs^P=-Dp#Ty$2b-lt^_v5yt%KY zq^cq0+#VPO@c(y6D&i#Vp9oqBSoB1f4N}nJt$V;#1vF&m2K!DcW#4Ut5ODUVVJ1bO zgvq{Sib2+o#BT(%F3N$RR84)H-7_H1u)o%4?zJdi`+8yP@zU`6a-H6?SOR*l0@=DK zZvE7JWy7j{H>dvSgm>6~wJ5q*#1rP4{(&HI1wZA!>>cd86j?dKX_|kD?Psd9Hy}CK2p!#M?ib2xWwp6oGWni>%ZYD-BYi-LEHoJmoBQW~{k5=W-L(96B0f1S zsj8dtPr-5?dSU{VSFjnfwlC2y~P9n~>IfHC)MMF~m*u}>XTcP3i#S1wbFQcGW zR&G8ebLUe>$v{*~IrdpPa?cbbi!#2{S9+;%=E=s>P(fy8BXr&pxLV()fzwLzQnhaL zp|9wB^81@aAK56Vt@T26(+6Wvi=W}LbW_dCHLo4hcbp-NGMapAd^$G_T-mS_WMu4% zT2F$&;emK3_t-ZcMLBVMT4Q3jpJEucABDt|9R6N$f=z&VlwRO=NHx@_jBL!qW>C(b zWmfS5nAn%o7KYeU2CO|5trSHyQ6~=)P(h+&_=Gbz)!owGgt!fuWc{aL6J?*DY%5Dd zTx;-9?7kmkKA2M2y%7Vnj|H{ZpG$$6Cnv~{xd6KgPlckaM9zWpWHOO3zIO>LyRWM`d$>feU3doD2FS|Dw<*1$> zaF>)V&X}m+>E@IwQz#SfcI=T$XTW8S!F#Q;{g5F~T5L(;wh)+lj>P0Ytok3XzBh91 zG_v>4;HE84iH^N^TSrFbfc@@dFgMYQh-vCv9)5fJ|AJym%0wknh9>}5B%TCnV=Fz% zMFgw@>iT}qFMo9Z>Y{R%_eHoNwPu67 zIHOHI`MFp|I738%IUeynyYrDA5mS_V#~?A9Y`4DG1TOPB@F9>$=tG04#B(CgKiLLqw@+4qXCLt)iAV0Hjw;Qn0xL%Z#ne_n-XJ z{oakX{ZZa1-6dxx(}K`NzA4gOD=$h44KARkfSy4|!a>T2qBkCvQGu9ej%~}=reE6N zQc>-ocpLuKU7q_$K-{JstH*nYjHT|<5h)nZ5Y0!YUEqackzxQPHXAM@Xjj63x5aF8 zk&ZK(_qT!n`aCY`%#TW|u-~Z;55~pAHGnAi1oPL;q07Q={bmk2wg2{o#1;DJ5@cU~ z;lV59zcE|~Fizw1)dmNa?S!7>-qSiYLDR?s6%O&6SIt{2!Bs+IkA0DT#8EnrO{!y* zqWr)9xYr8GlO7NQg~6qVNQrp{H-cGzGoAUjBeW|shLxU+aJ>^Jg9Aq(H;sH__|N>C z87=G~VDN;B%Z~mNT;Hqo>KHi5mRGa1>G`Y%kIN@7zp*w+Gv43|=8XH_SlY0gn@T0l zo3b}it1v*FWC_wmB`c;_BZpCWeW1!Q$XN9XE!yJDT1TSIIBitj@_K$^6bFhO+9HdG z>Sv)QG9ey6i4R`tun;$hNnRb|pAw&fG1RL&3sS2D|E$I_u%O3`_$WI*{$fn7Q?4IuKJFBq^)OmQUQ2GEGHKp zECT9q&GF@pF`VqdhR4#?$$11KI5nn+x8H~Dl?8YGSen`g-}X96@?bU9GZIUY@9*`G zicLv12QN0s)u2D#V6tzATBfGTQylVFeeCTu!9lQ#lOB;S;l#b(%sMX(26cO0MC6Ne z#?__Q=ieLeOgFr;a?V<=I@>6%(Zr@_JVmh8%-NONi2Le}zdn5M#ir{`qD;D=`kCzf z{&lX;*?dfK$KPhAk&S&!zR=w&doSr_7y-()K=QqXal<;Oc~?!a^;2f+EB1o;>l|Gk zBJPj8GeGA!A}OWMs|NOj3_`%%KR&v6(r&NXxB@!f=jdHPXvXqwQyzM=XH8}%;_T1! zdp9e*`O%b^4upC_m>aPr2>;t04Av%v$=VyetKDi%f0F99NxH+y-)XMob|F?NkAAu6 z2rF+o6N)C+5ZT*3`5Y*Uzp8?|7w9KWjJT;vV_h)cr6sAC?P=7zm=*F!11EmkCs*fb+^;Bg;XInT{l+x$(vcWw-a`kk)>7dbq5pNf5P~&I zjlPfYq|hpPYUD7e0LyuO0`+Fkl3n$LsW}%g>MXfW?vahwr49)Ux_j!KY2cSfDK3KW z{P(>)assR#K_5om&uj#=Hncg-+AB>}4c()c&dl+xz`q{aXzP)JDQaHCi&t zD8tp15*lhMEEJyq`H9X@0-Syix$IXrQmEZWE)6wSt2PzH+RAFatv$>^3U_qiODjruNWUWdRDd5)63JKR0geJHouT)fGmieGe^ih| z#1MBP>L^2OK{?0@ET()xVobyZwOOMSsaaBcm68~Bv$`e+WHM^O+Xeo(vRn2W$>5T2 zNE7`HleORfH)IW8v0;v-RrgkM+-BloGo%^`<~Gao#5BNOS7dZ`Gb(D3NfVbqi>jMc zurHp{D`RvYQY;qxf=y8V*I;GEmC^(~w$qbI$ucH}vJ1e_NplbkE~Sz%EEZK?38F1e z%S(-{F2$$6a4r|MbK7G#P(^>C8naW&n$*WKb0wx&L9J@+&4x_iqLToPSTn0kZyYja zU@5lxIBnp6y+Oys+oLcTFBcq4L$l|mvC}a^AG^BnP~C&G%nd&+oxb#rG>Y9teY1bz zueIaFU+aI{TkbDm(!OGrUNOI$A&t9#18p@}ZJ|9DHF6UB!DxaZE{ z-6sa!*)ONWV%4O)`l_EIQA$BZRy%v1k>QRS5S-|>Af?=b*oLvah$kvVTd8!QE}*1_ z$N`fx$OuUG9G?khUjTw?Xn-A!>^e>Q>nu3Tof8gMFA*|T5$FfUF zSn~qzav07yPpC!tAX8gf3F4mMps#v#x{ek4=(715$J)F}Az#Qnm$|6dXU_-7ZfIN9 z-Y$hb2h<6_kD;R8Xq`IY){t9zct*%KXu@O1Gejt)2sQ!+<+0a)rYg*KALR2DSm@ND zUuFDu&vI8fl!HI2d3?R)#*lf{Zf381FjsnTIL!Vcf_vp<2cmDtXH&{8fl%t&^a;!P zc*QO?a;;TU*Zv)*-w;=WaYVXXH|I(qR##W%JL(GhT znMTAO`AljpQ`>X>!_0=Cv`wSF2&i9j+pIWHvNC@~;A>V>`F`PuP-H%-x_sB*6j z)K9I3j?vBmp_>$^{hSzCvALB%=bQ0OycNoAu(;-~uws$8?0&6GJWMZh{t@R1XK$ve zzTcMfz+%%Y9B zb0-5%)0b^sk<>8iP|h2itl_&F`2bgqK!xoI8hYC8*h$uNQ5oy0z|ZOU_&!fj04LPuDRQ-iQA z?f?IQKo}-MTx=c{z%olgyB36Q#&<~RkKR{CU~@J0iYGM@ z;qo&a+Losx5_N0nj#|u*bTh)d`7cpk(WM+Za@omLota|E#dbVMC682gGng~rOt5;> z+P5RXMW-EnpgkMt(Su_^LIMG2Hg1PeCx`>RJ~?{o)$Q+MMQB@a+|ZlicFA8P{-yGD zMV4)U4*Mq4BZI$;K1Y52=(y~4s^}rpxhtE)=giKh-biH>B8rNn+!m0e|D?LNz4xp4 zuv(67CKsg1X-CECet=xr`oxm33vZc(bDP&Vh6~|-PQ-1rLObO^>>NDel$#Lo zV?V4~jR_|J*NMjvBv;l8M&Badf&0wuE*$U^LxN)-cqxN`k*|}(sP(Xn27g?n6grK< z_Xf|M9b>sdzNhPA6L4*tu7%{VjiSuMBa4vRa5>l7BV$&Tc&!hCEXcjfvosny!6X_r zQQ0owto{=8sIAaqzjlEPm<=aZ9naNVq)bgl@!08T?(P`e30_ujJf*ppA)Q}ymyl2L zrezinxdNAMjBtjgx9+lR@eq zOZhNEK$~|eceDL{aVA)=Q=3mVydU3bn*bCFG5+&w94sa55nvPRTJODAPuJ1q*(oM{ z_h4f>58IqWbYZp_{xI`!zbTC#vU#YbzJsx(4CX;^tIBj!i=HC8s_C4v55qk4_E0~T zU5H+%Ok=Ux-I<8WW0vcf@ot6J*vl~iad%sjI$eiUK@%wqJ_HlCxhNG_2}nQ&1|>>x zGqaD!?8~qti#CRYA65afVVcTx8yI|;f-b1z=_&*~&?Y}&q3*&lYDn;n>#NaIck?iE zYb>~w@LTd}Fd#zX2GKa@x~DpufPgg*j`MPhK@64R|2!PTZ?0jLX>%i&INN;6=zvdS za?`~rbhAY~L)&M6gL{Q+Bzx)aIf_%1c=rwp#v>jj5eQj2u#73-8%qYk_xI<{%L-2cEIcKY@iBT;?3&V}SS|FmO0>7e%iJWiL;1#wnAb%8|ahZWl5?z)K z7Id#)u4R;^)X+RF?pX7vHCGU)%~M)`6j+Q=a-^ATgEXp|!AICaW1}d=mqpI!7uWU~ zu9oW!hSN`6`>^fpzZrDt?u?l`*NT66_MDAm5bNE?B_a)O(0@ksK(EMG1D8;V8GLE! z8s(PblxKawO?!fU?jyw+4Nlj_RQNKyp9vl_lpPaXLlBBT_m&TLwVZ#{SobsC^ zD+8fW$Ow$k*)2VpEl@3?VyOK_w8H$dK}?!qSeInd<#*ch%@ERcxPuen zrj~Y=5)g4|%W{PX65H)ApB&yOlu=%+H5lg2*edF_wxUSh>yi;+rnb$!V=+jFPaiTy z@WGFXbnF_1k6c#WAK#6xkzgO}nDYzbDoO9|ikX1RP{1=3eCc)^5t@?Q43eZzK8_t{b`N?)~-`m*PtV0k+(>C9fRcc3&K6 zVc=SP^825ybR_5?XXf11{+&&@du|M6~0Sg6us zFD(5=mU)tuY8U0nj<>da2X-5>okRVUXQ};5<1}A8#aUQz5x)7iq*lmM6a?KIbYPdJ zsWlKQqX#J3J9RMf4ORagaGX(`H@}fH_>ONqO5oJi@Jfv?-o2 zt0dtJ)7zxg>h5z5P(@zgFRTOt_#QY*8l;IOT=C65A_xt^#FvU&DL-Fi>LQs<&ZIX< zU@D-zng%s{50i{IbOh!Ct2~siMvmskM}-OI2;23hGvHz-mpoKcGc&%k&mQFkU2;~q z;2H+UFq=xqom_Zd1oxpMKic(WKXQk9IZVvn7nz7YWyqrj)^U*vxt2M)pg5P{HK^wi zFw;(gKgx zN?p2)1Y;BZrb@s~U7cYBNfH|MPsG(NiBiAX!n;z?8*G_D()7V3vkT0*L*6IA!&C+i zCC|*PN70~bh{pNkFeXAfxT>%e5M+*?k=Lnr2p0OUkEY%fE07!yy9RetknHp0shn8w zPRKqu{f2~!q~J2Wpg(qHW>*bU;>h}9j#)>}UHTYrH6rKFvNbEo<>XlTURSvEKc|V0 zP5($3w;j6gg5xNA@XKO(timks%wD%zpSN45V`h^xfzo-X>ewSmujDK(KZgVNw#t#l zY~{l>L#Iv1xi^vQz&A+o5D&feGZS8D5*d17@fiCt*VCnAFVDI)v-N)UmWqV9DUWOp zW{^fl#t*BWP*HxZR1m2d0k1y%7L`)~Z9Qay^3h$nZ`9yK!vNTHGQt|2^Z~*HzNP^S zu&X)oI`goe@*2gR%LQEjI-AUhm`3X1z`&0xIH{arp+_U=oBQk=6JXolzf_9O)S|nb z7%rPxsF-wp3fBLx1tBn!E%kr$DxaJr&c*X+yL~lY7WICG~;!Ei}Cb1F+m!yh#M4V!YKrA_I|6KeCL?lT1@^8on5QLx& z12jDrJ(#0vOPBcPx953-EDM(9({P9=Y1`} z{qcIFyxpY?l5sh48|c{9^m6KPxXKWHL%$^{NkPCU^Qm6~|0Q=BJx?w0kyk-4RdMHQ zHgIF~>}dvPk8@atUXzA= zOfTwUUa^RqxBH$?8CTY{xE(Jh_I@y`K0u2v1j0_a<&33FwGsSz)s#`rTwaUr3B-Zb z4hibv`y+AyTgZsk;Ld4$-uMmzbs!D(n#xcS08;Z>a){w@#NlBORQ#W#=BnWo&X2sM1BH>{qhkjs7DBEB~WA{28ydI0#w8xn0@az1u#m!?xJbF$74 z>7DN(xWe1!R<9TmTO1d6k1?r42d4P2aSF@63BJlGG&oLxSpaJ6*o~v$C!m8scGLv5 z(2yl)w?&v|s(&m^D_IqtXqA^nun5=iR<>0}Qbr-4*Yw{Y?jTzQB+X}SyOeL&8?d9- zf1J^(4`zaE$<*y*(`P(wg_m+k*!C5yuZ_Gm}q2i4EFEe&>fZa-g9&q8koj~AB^y;oo9)HE*=$ERcjx%l{McU?UYbPY$o zgY+PC3lSNhXa05-{b+o&Qe0H}uMY15zPLN&G=t6rBrokohb26g01Ns?Tlt>=Ev0tY zsSBPG@##7iIP>V_nCAi&?C_aVsZEBW+HJxlFLSoeQ5Den;Wce!caOB1dOi^9QaiJ$ zfshwqGe0q`Bx~lEvC;&~pQ=Gmbw&g{DD)p>a@+`xVnC?JO#$!86s(Y6CQScqlVQJM zDUa2{a6Qi;$K#x^j#@D6xNGeJDl$kn`Pa+R9O;$ke$PmPO$CoFV6H8XV;@%&BM&A3 z@$#LXZmSmgl)sp|L<~O{rAmi=J|_t>$|4}2aK ztIbqTc!8cvUixHU8ac*g7tq&gzL|9{3%lruFyb0od?-oN1m$s8sa?3_%2oG{diUjp zWRf@EriKR$5z`2Ic3i*hWwmz>O|kmHNc*IMem7>sGc$h4y&5 zN)1fA?vd?wD|)ac;YX&f-Ll;(Y>&HZU4gV^muPJXA!7KE%xG!Nb}PiLt%wk&R@bz( z5-qKWgp5cfB9M_rjWm#f4lslOLo)d_$;|ZodamnxU3&S4UL;QPzVGus_x*hCucQbH z(-U(JhuAlP;K;>0%(B^*vbPvnG`7IJm{tQU3+ZoL$5jfLK-1uYjx``tHrIl74D^QP zl#(*0j}3%eZpDDs;JGr#-AE$qJssHSK%2Oxo1TR8wl(~|k{~RTlf>tJIjY>%NedhF zaV>%#Wu!L~u{7cixQ&8H%0bQRJ^oX^X3Wuym&`lfUDsyJ!3xQ*XsN&rnPAj{o(Lu^ z3fq6_qBIMKybFUC=7TmjMsi)pK9OPB%q1r^+)I)_qkrqiHibw`7M3b^o*SpE3T0D7}QB#SP&n zpM9}Sr=|?$@rJG6jTfb+qN;jYaTD-yM?&a!GSrOaR+M#2(+g-$;7wgQ^YdSN1bicI z4o2^oGRwh;upi_OY!!Y82?ZEFJwpZPKcp5bFzE8)Sk%MXN?Gx1AZlj_no+XjmL5*y z^)-!aCd2}ZB!{`{kiT$$m$ddSQ0t86l*FsA*;)N+q1at>>=t*6^!f=3%=VQ z*0Wm5ViUJJv~Tx6n58d;$~4ivC0D^MY9Z3)7r&eJKNm z`9ckaKOawdgNGEv37IOm6z?bkFMNZCnOH+M1hllvU44jt)*7_-{=P=@C8#8ZHTCm21phY|TMKo?efvwbLDdu| z)`A(}-WqKrNk7499(JvqnPXth7v%~5w@cT3iY+s#T&xj<`Be0FG(Z5qS}k4SP(v4I%9?89nMvt)2&KZG^F{^%cwAWDG_Pp2wOn1Zg%wkYe%; zGVbyLv+`RaUIN6Y9Ax)dM^fXIP$L=`zjBxp0OxA>B|xIG1Ln80BRah_2#%-)Krlyk zZP3xVVyB~n=IGa=<55s__x2BT2@1tYAa(+v0y5$^DNdrB(XI$sH`0t3QA&{0dBhHD zjuh7mcv~s<7I1Ikw3FLyJ@BB>d|;QKy|*|ds1zsK5b$u5_suYkt*9X&Pnuc3U08-U zXz1}#{P-#WiQYp(w|y5013E^tJ2k0VW-C(APCTl<}~UmgWy=Hf;HL#1(aW!_33>xlW#j8~Ci5(u+T#}mkdbRda5eJ|I~BQH)3-7H&B zCJc1pm%_8zPt;beL3nzyP1F44WkKSDvAPBXKQL}eF}gJ2abNtkvTqM0M7hzK>P#+v z>US-81KfG|kiLqN8b(??NdcycWpX?6!GjxNqSknqG~GKI>jpAo z=mnnjTH7zWE`u+s>41we6gDFTbw-@%p;&FKkIsQrF4X|tk?!(o;)pXY;&n0c9t?iY zm+>aMogKsS;X>L89zzsWSd2X}5I`1-ObgpR;0G!!IcU$j(-OM(=y zt2AlIN945KMW@W0B=YEQAC7AOJvl}juLzqs?Osho@uet;{~AK$Lx^s*ss zt7){iV?BPG7rS}Mw>zq%H-2B~n}W5ivbYMqJg8fGP108BKkN@WWHZeJC&13v;frZf z(DWRJv1?loFqV^NhJlhLWKLC8O@f8Upl!q@p%m=LYhw@&o;_x9Dai0QfZi; zJvc9@0#;WUWBp+#?RABsYcva4u07~Wf}Y3GPBPtSI)xMiQ>eck(A(A|lvVmbvj zBA~&ksE}yG;1LXft$;4@8`>KaL6idrQlDbvj2H#dA=uVc9#EWE?DODob%q3R9*AH* zrxNRkQZZIwrDHfog)mpR5`~>~1jWe@LNRYVkit`N2||9!$K&coKW6y~CZYnl>t3EhaDEN!HNY{l1o$*cZ2fJ_uSVfy$itN zEy_Fg!UcAKT<*UO44Pv-9bVRLO3XOaYZN4 zGxlMuSP@p*hj|tj{!s(IaS9-&*mi%RvBP&NOaay8l>-h?i9imqP<=#+7uWKlRo5Nr zQ=u|cMufoFATQpGnQwtpLP1iz|BjlCKad*I?-e${=3o}@Fn->=f0D;7qmvYhtWs_S z!Rvx9{RtuSWdQ2+Wf%^O4S_4x5dmq<7RuhnEiJswD?Y>Hs=2aqi_=oSd9XTmko8~F z+pg-bzHY2RT330v>g{}Ua%i#&B}b{FB=w^FOIm9K%_2ll7G6(d@DAt#lKRuvn@Rs- zslSDmuzcx%N5FEkPlYrY^dz?9F4C>JuKe{8o!Fmn+@_hV7LTwIru93%1*xLK{3U39 zYtWIpCAO#+$LQ&|IPONQT#wXwG~YkfwnG+!c3~aUa1NAw2;>5ynSKF{uyMg`XTl!T z$LX}2LxP6vrKS{@k1eHSgn_%au&&Q{q0DZEmIsyY+FV}feCg;o6&0oN3~zXkbQ*)( zGUlAd%Mq0uYrS)OW>|gA6T7{#!m(x++7|ehyux=~=q?Yr(r9dlSQEXzY{t|8cfin7 zy}{Lce0-Fn87%$9MENJ5ZFfk=YY0jpl^`;(1LUw&p_=unsAK|f0`-G#3i^GG@hg5Lr0KnBnr4&IvUeHjI=Ze}lR4L} zXeW4GHWi4b+~4&PO=*2sE5p&r@u8oo#P&K?j>P*K_TH!hD&mfzJ09a$)eYbQH-!kI zR45o&-Bsn;MC0D?hzb4C6@^u_#p0T|ilsp(6tAMj7FVO+IqS=`!K&~CACK+p+FL?E zYt@|A$~r2*eAjY)F$&;NRRtG6@0EY%zbO$frho`8316Y#Wk70UIZ04u`@F*xiBu0s z5fgiIHy|ndGC3brsTxI)ZGxuLA?E*`oQiR<;v4XxyC@2rjX`wBDUil1mh9DFu;j-j z%Nq6(d$|4kIlN8Z1!9I5<}%p3e5XTWG{|%ikW4r!l*_2214TtRUc8Q{IdrM$A=q)B%pWCWm5oNX&uWVEt-LxR=GPQgb6xOJy3 zUzCl`ysBm~JQ(8xX!a%Wd28h+RMpeytBu{iN?g@Fa;$TxY z_YEa}M9;wJf*#es9Y>EM84dD$WLk-9VknykrY``VjvBr-F%@#C|+2o73HA{hab?TxlU_=|Hlh@`x^lK?&h+;HXl34+L#NpN%sLXIukl zf6wtUJo%A@>$Br5if2y zfIr-)3L)C~#QUomVWkjb04CT79Ic73K1e@pbbsw@dn5an3TvK7t&Kw36*mb1t1g?{G0iBdqk|-Hg6674JR8vj(XKGp0P^` zk?J^jO~8rGG+h^WwRX~SY9IidV2`YC0dl(ddW^-X!^iPfYjMB=)F%%)VDQQ5^b?V!yv`Fg=}jJg#4YH znIIByU1T5t1U}&RK;;>xkq#*VvaO6TU=;oBG>6(k!f29?-u1nRN9gF_aOu}n z*c+MrFJS(ReeXFYf={hju6<$)M`NLTT28!ArXom#zXyv1$7~I~Y9D@-jD+deC_1)+ zK$lX{sPn|Uj5PUte1<&J#C$G1mH`p0HWJgE^%J_zCO<1AWNf>AHb&DC-hegOzZ|O= ziH!N=_)Q(ZfIXSq$N0jeKu@WGJxE1Q5;i790iiCpAl|RyG0)97<^(E#mS`#Aq(1Tf z!fK8=&N%d>@pRWM<%B9Lz`$*|nJIfC8Oy^N05#YBxWwy%3qWKwvM3d$u#~+^u?1<* zWdIb^X_IWEhkRAm04?vPX_mdkYy0d2Mhgn%lV?!4?crN`S?z?V(fF=2`Fq{qQ~=kk zC-!qpzy7KajjQckl)D#StwH3l8Vx-L(zGOEHFr*5vmQH#!cZ6C4)1A94tffS(WsYy zfAbV^^8rIKL$K^9R$p`!8x1^Q8$OpJ}=mc z3S?uv$_=!@P-s>wy(1nLV=*D|JlMa^T&MSQaFO`ImKeH%3{}(7_>8?l<1_ji?KOJS z{Uk|4B@h2m!Vl4PN3vu@LluMO&WlO)_vLMAKzAU_x`WfdO$su#*|+nuyuzMP49o@3 z{@?kWg&SRNj1hMMlPlaMjUl)>B3N8L?ov?D#I>=3hxn^PBsv&p3japjM&a||PWVUS ze&#yYucbPq1b?b4(5orUwG1hOESb1*=8j_#4#kJ)O=QT^ z3Jrb-b#U4qtiTLqHv@T1QW6pLXa{qLHJ-ld$eyD*5EHM4*BP)ZNBeQKF?{WtyMY~5 zj};$HZTWU+s`{L0lQX)6JKt&w*BcyxP#<<82YjQpWtRA20NxiKX@Z-raPs;>z?MXSZ75s8r2aWbUSu zMzOgo;0A|-BlBChk(fiMt6B1aq-jV3-hXk8Lz4RMC~CP(qxsOhV|TNaUDUGO=7N!M z^+p!LVVU2dp={OK<9QU^0hy&#^}KQtrJb)t#}Ur>=pjm@0j}?3;J{daCX;4(G%Z^| z`1kC0^jXS0m)Deql&bEKG&>rl8>*_}UlLc_XLl!c3OR@ph&n$kV#)oi5&gBQ*Pctg zG{aFC$M)qk=#=TZLH$gg?y>r>;~V!WQBeKaX@9snipGq$i;+yPok%CzuFO6hbu5jm z*k@U2TSu{fo2c2d~sn+^axu@8(nFq3j-MM3=;LJSXLtXQa zSBB8MNoI{SQl{y)QPwT64M?Fx-5{%~b{$ir8Q<~BCp+6udwtj! zV0QK(buy_DM056h_a>0jKQKRuwbI*ZDTQFzsxJcTKr@!_nD{-XRK8d#le1Aqi);)H2wJgS5J#fTcEw#P@Y5vh4A$^+B#vCsYL0u^!{7-U zn|dR53QPgizjJNj@POgB#acM?59$@S9Ai9gfa&v<-i&J-~P1{y0N_=qJZ0A%UE7fs3Qd=euig zh$I9tKH<3vX)Gi>&rodqN;0OTq(kKAfXoV|ITRrDO0=1X0lZO3r1u_g;2f1B*na>^ z5^1@0CE2RIezvzw>id{ziwH7~dM@f)P#Yq(%3&{^cC#06V>akB>zK&Q)y0w@msmrf z8j@$d9n-1cKgzAyd`je<9mMfjGm&?l)?vk$$A19bV4Bk-bI-!0v_?F|^g$=T2@0dY zSAL)IdM?DX=VRK%ijY0&#BsHgw!^ z`3U;vK{T|CvUDs9`wqmIsg`S-z@tCVZItRDc-{IPbQH)B`hZ?4JDH_$Rm(5PVtm@t zTl&+E?i7uqY|-z*-;n~!vdQJ`n}(h?4_}uzzTD$Nn~xhlabA4HmTnlv$9W zqn~uXC!A8bj=kmDO{ADKmL&SrT1Hl$>nES zOK3-0l%qE|EfM%6`y9&}^k0ECK>k(qDaW+_RAh9RlHmS!MLwP;wvQ8EO{}U0puK~tgrAknjBn*=x-&AP7?A$_?H+DY zo{%}K4tssxfB%y|6zQ_+^jUGM@6>U#g`}}IKXzX_u{nki3TP)q|2eLHj9-qRIeQDt zs_&(Cndhn$eD_X(1e?vVvT5p!pLIbpd*A*vz5XyGuN>V}MINQoK>W-^Ca^4^C+DSk zMx(Fy8lp%EG&;20`j7~>WOxZm6F_&7)f9eZQ8-Z*s{|{doRVf!ta}bEUhNBK(b4Q? z`7E-VP&By!x7@l00zq1_5^jMnd-}lS(s&4O)SH(-9iD17Pon5@{mb%yr)jxtI13DE}ZqOg(hGGvuDi$M>Ot%akiwP2I*0rDF& zVIKhZVCcND6}C@f82qdCkFD`FHC+qcB*Tsg($FI1JAq)Xr;aB(gA+GIfE-}jJ`}8P zsyQ{UQ#nevsfO)@ogB~Q+0P{Zx*#2uRuS0HTx4;j?b^0VAICI&Q!PJzyhXnfxys0J zyRxUhS(VfV7t1*SpjiK%k4 zWzN->O0!C7cU9Tx#N92GX(Yo#&ZF`Dc&XMuz;*Kh%A^fzj~wvPzW6kLVgbo7gz~1% z$w4(E*Nmrhh|9oD^|cI{?=LF2*oGxJlv7>tt#T~?$0hA+@HPPC$uYors3X&|cxG$W zvIES~QJ{`qV3mk|~82&%}x0Oy4T3Fn4X zZgG+C)J;2dSM=@ZSLmn0=+~Nd9O(8RF~!>IzD$lAL$YE9=8C0{Y;MTELVNpZE5$6@ z2qV_D0E3LLjLvRA1O?qk3uE^e7(Y;XWWOIMh%N7*ISWZ=cHhp%3)=LXh^t87nmr(gG+#zG zPTixFeGvS%sLkHuiOiHd>jAeR%v>;B6aNa7TJ1CXrD2T_TAAKl*68r2vy^-_;b7s#r?5#LpD)s(pNb$(e*w_ zi@tgksnO2OA=%fTA!Y30b3Fb$VOVXt`(6)rPzCFqXG@x%UMd$J4Gs1LaEYcf9O z?gO%)9b z^}dfhhQGi?)C_7fWYara#u`qkhBd~^+PF#*{@ZP(lpVOn5+g~U!F&Ibkn=%bS7iKg zy}bHPvfSYqS`}Ze2MeTjgB<)|z(Pe~qp25um0L;HYiB9@P19Val{G9^I$9EBY?uGY zD6FDUb9SHR4Er5<>7Rb$*5Y^^akotwnBbh)d4X9~olz=b@myson65~Wlqa68iUmwM z&F6h8;yT~`vmE$$lm>3in_19=U|wkkn<+kjK+eSNybkRIGMARL`}-Ho5@wY@s5s+q zNeWgM$xk}OWk{!$BKzK)VXqwi1*9;$u$!)Vr+LehR$8KRq35?*=1w&xcZL`Nl}60l zv}w0RYl#rS$WJmiHh|C~RBqd8jn7GxE;J_|1V`WUzfo6&jP{xohsU$jPM5UoP3p%k zcLUS&I&v4y1wk@&fzeX%6Qmn1@WvO+{gFWfjL5s<;TfL`Qa>Ep*=Db1+PLTg#3&`_ zK4A)lD^(28ZZ&Bse(VK#TZc{lf}@kUeG1U<7YF0H2@c!RpNK7sCWM?rvi8Uy%9wYt zcHs0FKCXpx04Bw-=Ya?i31rD&(wk@Bqc%P049$RSCZ>84U&|{;#2%uL9ytY)pfk)L zgQ-*3?#P<|Jiwbufuv{9mo4gY_Vg)l3rH0RXx#A)M66%LUMM_7@ z&?x~|Ib#Bcqw9b(cYD&Jv=kgZhH$$Ntp$JB=pYZ;+vZ_$6XP{QH(F>i12j8udK!$S zU}tJbwplM70zuR2$qIeuk(sw#GdV&AT0bemY45gE)q;LoMnISYSLHUO<*>+M@0fLTQlaoV*OZm{P0=W25{nVu-)twpv30TEMO?0wxtKoBzkrUwM>(2xkw1Tp ziS8|ZoGIBd=d0eq??>D1Flwg5@$qZOi{&&iS$Q3qEH(LBO1*^PCHoYycW+DQgot4buRh0B!LGP=W&o|azJRV+jHTuhN>(z~Xr zvxzpPq-DN865A(asr{eZCr`1xL0%N-+HSq!B;eH=q`W_>jr}L#)Z&ek<4k>l*`Kip z5rFf5m5auY$pAi*L07clmvzQxZZfkumHYwLEbHXj>&yWPJsQ_vt)4%g^Egz<6y2^w zR#Uae%M0o57UAo_;SXBM);fwb@r0>c0fY}-oz7cxrU`t&u{4S5)#L zPO0uxZ|Am;QXR|c)EB5{0Je~Mk61II=eed?f z5HI$IV?s6e0`Qp@^mTfmbL>eynnBfUXs)jGB+NL^h3cF~|LW)(cgUjpn!>7G^Ib5Q zlVyhcd6nexXR?}1dO4N`dwfhOWl?6$k zwI$U}mwNyNp?3-~!P@<)urm@(%U60QVhK~rta2I1i8is0X~ERKpwpo~n`3;RXPRx| z4$2cQiA_KO3y;St$qBUKH4U#FACay2 zFFx0{EirW$H!cx@N1zzLuAiIG%2X^HCJrMgv~S25#p2rMZHuMU9i zF6p-1I@QE>o~lr}GbTYAL$4}30!+`1g*sqn4vS;K3i|P2Yt3`upQfd#$g^?%l|HCY zl*hJqqT|Z2d_6`n+{vt7`mFqs-AtGmhu2s0?EG<7Jk=|=g5&s3;Edh| zWb7&`HG{D(mE$tkI;$2?P80)b{!QL8i`j(ds}E+K3WP`L3@R&@n|hdnz8&IQWxh}x zQC4h3Gz;Fz%t9*TgmC%hKJ}Soz9D;XOjfdOa9+!Y9%Vh*X zTH5~cq`&tFBYm-|A~!gV?aDHwP0l>`ekY5cP>#^lhq+B(iOK~B(qi~4Xpk+uR8>pS z3%Tm<&Y+-uCgZS-B(;+q(vZ3A5Bh?3vOh>w$M*^=-;i``MjkBFlqryNUzTTRG)#0g z>}ykpHAmh%Vx@iHd(@WMHn*5g#?qnog{uc^ajPr~7PO=dEo3*IMXvyZ*wl}a? zopoFr7z19fS8)bUx`Yh};(=zOi(dpcX!%OX)N8O~8p4%!6)D~MeqBR@f-+Hx5iS-< zm1j+)xQuPhrt%||XA+`xBhXPy;)t`1uXw!|gau|C-TXaa2x@}S=$N>4h<|HzkP6-O z2SHTOM1y~X!}Dzk^-+FsR-&XmtdK!X%A$h<%t$w!O_csx7toc7i7*33D*GL)Jlc0{ z@J4kEUMsArosYQdTHBNGU!)TX>3%G?$)n+b9o(K$E<~r=<-8s!jRRsr%NV`~a2!xX z+z3w)PCx~ko;mJv;zsl0L6O|{jBip@`pE_5RI|KA06h1kzGNZ#3Xwp7(WWBf1|5E2 zs51s2rs?Jl3z{JJ`>B6MOia`l(=iX>+pdu~^dL4}uwb8`Q#~CLyX>JgX%I<=JrQ3Y zoI5Z<(P5a$%L#P8H_{sy>dWI|sWR(yCD2j?)>hkN_jM*}Z8cG9GTSGmS|x7*eWDP& z*KD4+nMo6p5(ApfL0_ol`BEmcU`{zj&?zOX0J}k&Vvi%^Oy)R+)fvrqJg#SB3VcH{ z3$DO0^i~%7$eYNvh`1fFYc{-C@sPdxmM(BAXI&4oN%e7HltI1^r!e;uj+p*dAA zms`B9nqw(RgP%Uh64i#VRgNE! z{UbD(QeM)}$s9W`tA7LNLqnm1Kw*|2Jo^`E-fndMuJeYJ6m4}H6A zkxfM~5_o`KIhu};<1wd#`Qkq+Gz)M?Z(BoVc5RE+@a*XGW&1vpZ_$3YHm~mST$x2e z>yTNxdY!%CyGLuGxf?qhwv?pGuB6rqx4|s<hEM8B?f+;clu0gQ&S(0 zFHT*~Oib-qhtyVNGw-ba= zk_~(Qtc5^iQg0GLqxDCw|4u@MIA2oBkmj=$dsn=2^Gv7p3q)T*e7KnflXTLYGCXN% zGdHkjiOldgb}EFYJihU*gylnXgFSJ5-F8jA>k%lSw4!E;$K@Hv|Fw02jtq2H=|?!M z>L4vgh@*)}m6%z-_7A_jGniS|TIDLq(pt}oNfY)t?c$rk_`>e!mO{C~c_~SO-(0y- z6GL{ht{!{(ik|De?;9PeoSc^AD{n6N49`6aP7R4JSDB+>Llut;&~e;iCQBB8uhvI@ z>E!A-!3HC{TC<4GW3tTSp?f+V#kHUG3-ISS8V?WtxMWUkBts#Q#CO)c8q7e`KdI1= zY^QWNy;k7HqR-Ed6X!SRv$DufK_S{Q<-3%0zNPm=;0p$pLZWp8thmRJ^-)jf4;5Uk z;-0<`fFzIFeYid1!)aT+8?uDSIXQ1slp7GYi(>-5SPCwO3+QgNWugNp^Wu;uA|Dt~ zH6D*dnHr#1Oifid(64w=aVJjpX^DZLlVXBjBNDNT9QFvJ_s%O(1hqKS!VXIMgW@!H z98@>Br4W89lZFou35LJ~+#GeWDj(Kmynt^C6ipaI2|;MeD3$lACG%Gi{c@E8uh+XyJik zps4qpXx%83kM78#&E44@qg0?8ZqU|b#r&O_0rY50(~=$Q4EJn-&(Usr97xiAbG@K{ zF4xjFnIp!W%g<~@u?V2ptF$TLHf@3EB%|f2OA+E+4#LRZ@7Cbi(A(-L)nSXfjHk^% z^MTPmFOhZH^>Uxxg*H;PfPgT;WPDVFme`4r%qaAOD?>UsM1yN+PcX?;k_s*^OAw$9 zM&+ZMjx1J+#CgD+Jw{mIJWE6<7rtVsBWnZeg5*c8)s$g%G&b$$RK5K{+Hyt?;>0?6 zb8ar(UvNCV9=tm@LhFUpd!XJK6ChtSdJ#N7bZ5%WMmpIU5|(v1r- z{jRT_6oxO)Nf)~WdDSRAKh!ON>dS5HOL%vB)_i9&<=N$WZsOilb>w;_R!E%xK9I8X zU26mkwq%D^anbSFUxeZYDF1~UM8nRWbaRyNWl_y%!YhLqwH~N zi=$dzi0$yMO3eQ(@cJKrLWO$br^+{tC4Sqw;qgfSNuU4R;p-FjeSg?`+YZ{tk*BS= z%&YOb9Q%FE-&J%oeP!F*H^+2{z}Vk_q^~9}YHeIJ>9oSM8jvAVcT%m#0A*n=^MzHf zCjM=OUDkK7@>*uyxcBDhPsYn4)RCW7F<6Ux_OMTETgk& z>fn=uV`^ef-KQ*!IKCR5aJKl?c$-gxP{aDNix*YIi^|bxta5qL^)MS>s~p2}n!o!= z`7>MTg7XuaTI7|#mzyjDr?7H|Eop1>V`tZ7<~8E=TY*F}abav(?Om2w*KD+h_L_R@ zCwdsL^TVo{6AwLV?!y#y*no^f{#&ZJa%}-5n*n5H`jUSIu6TQ9Vh6Mcu3WK6n$^9$ z1ApIB6=jaDeN=PAKVYo;{2fR)+Q0gWbUYQQE%SRC$bTVQZ?BB`&)ZiBV{c)D6H-q# z_iZme7x;ip0pGja2x zII7!w!dM`Ubo183*Yt|k=Sq3TWia(<3L5LDh?>h1y^Y)vr(bR<^E<>xJ-T&%V11{g zuGWq+7}3;-7QkeWz^xJRx|FY_Tpw=_1;oE5EM67Mz8wTRf#XjUws57eob9yRA9%9t zKQ8$%(CP0avb*axQLViz0snB4!s!j=@Q@%5iM9Nmrx6Z z-BWYIZlD-x{K0W83BOX)q6x6LimanSBLD(+$g8=g<)O}S>~|vW#ICl1`GNAk_mwHD z<6=W`C-wgC7$bd#YExWo4(?M9Yc1@(&pF7Z$mU>+VSyJ9 z>@WswoER=)N#Y z)85G7?!}4dZK+CdUMN>LNfUEA9Xe&+Z-mMdD5Wes1s-B-K86+NDGQbGE`ZNMGg_OE z%i-U|8T+E=dE~hax_d%4Gn0|3p;=8k=L2@r8O@>~zbzBx1?~#~w$+~TGV(59>5wxA z)r*F8ZF5w8mafyfKQtr}k&N{pIiMU>T=*u+eB1$&V$yKeGB^rdj1nEXL?|Z$C)K1&HlR-iSi_yTzIVN`{$9((ttZ`Fd!{VSmudO-rEaaZs@M7Ucoey_t zuIUi^zg_tk9nf($6%P2z!LT`s-PU%@PXDyzJw$u7$Rpl}iSfiOLoq#0xVgf(U)R^DoXmW(ZPafTr zP+~m9F#t=su7Y}%H$ZBli&e17i7CBK@yUBR2jIQeIfZ(PXv(p7mz z@^bgO?{oL{Ha_&r+V@YDK1zK4rMPNt=9D>kCw~6F)ctnoR)fQye)pcfL0udp%r880 zt|SUV@#Jx~{}eV^6+>Od{)_PLAk~0=!n~7!^9-s!|ZHR3`wu_jwDvjlI5UBdt^JUkunYy_^Pq zA`PZD-%GJ#rLPY{bFe#}c6ahJlvCN{1exJ@QLIdS1OZk4mv9GjLa-rL1arTqE#*(; zYW=Y!EQ~AMW%33QZR-+#jup@r7vQRa#5Wl3NEWMr_sVU==fMp^QAB8Xcq{NRq-_SX zKQIKkD^@1?!+0m(z6+@o$;%xJ`P&i;#zKx_c|C^6=%7lpRfy>JxC`m$!ALD|Nc@2f z$CEGEff}iRE{FVQ=`TGy;f|Yuf={MjAIMn^{}RcfkLt>Wx?4k0Nj$`c8QeT|lEl)& zKFm0)zxY9z0R3(vCYJqHVFVH0WRWnYt&)zgZDpY_s6?NMF4O>6C^X>8DTV%AUI(Yi z;~xHX6D4_MYIe0 zB-`FX^)T1mV~4f#o&3XRLZD(+q$k$I1BaVd1Jhx9@w-$tK_sq-X1i#-LE%oB`15ha zd5a|o=Q+nqFN_@kkDXvddB=UReGYx~{Jd)_NM*@u$SVf9$lQ51t4-S4X0Eg(1|>rc|S@nN2| z0>AArwp`umcut=|TkUek2H{zU>pc&z6T75fBx}I{1fp~!T`wNpK0|)7cFkl-K+u3> z%sD;#KP4JlV}#Dm*v4{odv2vDiBYfDab>e%IRj>RqrS#jsis%`ED##-$?hZG)h%7T zG6r6Jt5c@R4#_d6z*qMDxFoPj=mkAIGdudn#j6#{yF`vH-ys^be^Ir@^V-I~byeH% zzEA#n%Kj$=>$t;3n+|-cUaXe+Qs3qB9p63b#Mb&|6Bpnt{nEp}?c>wG;=@q!CF;55 zZ|dc1j337Liaj^CP39q1HQv}eqwk!4oNy2e&RrLUfo zdPb#x{DHp44X*AwZcCtT9R-IDaMbHZ)nP^Bfg1egYBv6IemJC&rPi$dRd+gBu}`@i(7?xcN^2$-4?Ub%*ZR9Rv>0$8g#-qRp4kKOjx0C}d@>3?zE4O#vD9#_% zV&}WFrIfk$#HwfGKllmB#5uZVq3~yu68N}%L656$uss}-VXLnhLgmG0*SA*jHss~+ zD-JlnZeSYBI-N~~d zRJ$zdqyJaw@&<{9J94Pq=^=|XAS8`SDAda>nlP*^9Cau<(&0aC!?>W_{PYNCD7nD< z?e55`I6DwHG8KGfOKiG>Bfb#nK?FU6=U=&@oq;dnW&oN%i#5CLATb=Q!L$!BS2rih ze+Hl-8#WJ0qOnq?a${7%Q zBZJYw8u3d{W``mf#&7Z%5gaWH_%oOX5djIP1Zr1rv_buxysebx>7RQD%0{@JwD%B? z?ljS7hgMRpgTA8#u@RVuX!u;2^mWVV+BkE^X(Vj6AD7%xf7k-*tW3kskA1*k0kS2W zu}RRWAoeV1!nf&QB}>dJQ5Cq$5#c?ckf$ND*RL${xxiD8iTY&xs1LZ+1*s3*-%J0r zrW&^RU44S>pVYn}>_%3f?ahbW9|TZ0PC!x5!IkR6u`FTJ=#7G}DkP_Go)aQ=D5D-S}uS@^U~Cua4oDptFvvzJ{6uF{)lI7e8;TzZwC%Be9-tE?BAvC-J~^xSy=nv_ zRKzt+U}YvtG#n7E3&LN0hyJ*9qf!D)l=`oiRY_klE^qb^HCBV zM(>I42ZJpjjs*-HN6oj34K-p0@IlU$<)u*6LbVVY^Y}g6N9<`P4b34_Xi3YV7Y~OX zOE|mY(#TB+`FB}tub&x!H$KDzo=}}J{sVS@i2KTPOpA|2rib!^(LpwwQ~;8 zZQ@dT?ziRCCuQrb;D{uB*NhX&`MBWFw%RQ0oxzxJW!~3N85jABSz;BhLTy-%*8KA3 z&?w;VZ5A-gbNU~bxH)eV7#3fiK(^I@ljo?o!GBWQ`x#c0zfN4~@(LIP?jL_6a1wNg z87tl49YAw_Tmn#GN;|xjR!$l2{8_-yRHo3GU`EepO0y75QzM`^U1bF@;bADpGlOV@ zRENQEibGNo0Jj&Vb3A5C{jG@R%SoshX|_A2?85{Z_Ju-&SA4KV78i?>G%J~?0C*~& z!T7jHmT2c(;Ktni$ z0Ehg}NzRGi+dsa4_Mg&*-1q(dab2(LVBbxR@;D^=<_(~L%*?K1G_dK>)%X1WNv^RE zh$=D8dG-Biyy6)?WXjDgTU}^9MuU)Jh0So(ujf+t)3Wr>hx!<~El{bsE2jdpX<3T4 zS?`mYoI$FsO`0C)+avKN9ts6kjC0=el`XQhKL~ZtMe}*sNH8k6tAcA&)Q-Y};+StC z0ZdvD;TbwbnEnA)UAzOg&+-r-TY_yz-cPgGz)21=1AoqhWO_Y1i-&fP-gXabM4nhO z$07O6OeOJp*atExn2po3qi27!)>^=5vu*kpIcfjzqdC5!Lyx7xiBIx>e|;EBOJ;k} ziPzh$b|Kq!Uu+iclYJu3rHcG$p>fR3Bf4jSb%K`J+gDf}mddGfh+!!k6MQxM9@9m` zeTop^O>(!_AGJ8axfM?&2hG1o;vpPxdnD3~Ag_rpCK3IzpI$jU*q;aG!cGfk1<9Z| z-Gl6As15AAV8N-Kz3g3;>_1|E#tsH;lJ4V!t#}G_;AN6Yg7To5 z49rNmN*2;#LEWVi8i@wtUg8J2t&COBz>MQI*AP))Tr6iaXcQ-OI#GP_z)x$&hCt(s zBDua!SMoz_Zv;KVF2(OVhFZd4lqg@%-n)XwkOPUAS@BV;bT87xvZ5((3$KkFw9bWE zqxL3pGtm@`zkZexW)>Ee&Ed-cCF#qH~geFOT`1?jDB>;?^NWs*^lVICvS z*93oB)7`7?7AF5I57CNL?gm%Ff$4!6ib?QyfYCk$%JO`n8_1SrHV>Lf}|$ICSkf20ct;~-B)uA&FBPD=>&Br<`0oD!|v1mJm3G9Xeh^5$Wv1i<)Y?b z0Rx;dgo%?xZr!~9YPW+7T93qH)S>ro<|MK2t62V9HrIiU(fE;}P)ejaJmxW&K8BsR z{6tL z6ec^Rlv(MfU!LmFlEZLu)HKq4;;3-QlQh6;9|#!R(T8+OZ%rk%!7Er3_#iPixt_Ne zVa0Gk=YNv?H>l-4wVaGtMPxfz5{x9ikred7(kqlFM14tomXt*Sz^1Uh_ltZrg#cC< zeV01$DtvUg2aM)mi3IzTSNmZ*RnXhg`weP5;kAmb$qFE?c?^klP;oj|=vW6vt>DC? zXoLIfwG?$VdDvms)+b(mBUd}THYz=j@JjvR7YtxKX(tuSA-kYUY47Q0QI2rIYP44# zYxp5bvnfxK{j}?;^4Ef~1wOr=lT)$!s;1z@K6&;M3i0;#?HT{u0buPQAh`F9Z$9|# z><#KNqAa}f;FwRp9Bg&)l$8EKa9qFwluaS&y01eC;Lk}oZWD${bcd!Z#%2$*^$&uV z_7p#ng=8K?r7K7t2XH)}vpQm6e}P&7ODmxKdCb13?kHtD1GPTYOq`5bpKFeZ!M8x+ z2-1_FUPqkzRpQF48$2&B8W0w6emzzm`XPr#QrShiExkROE0;(@GLBM|&keolFJ_S;Ucx7P<{jg?| z@tr*08uATvAdgK7QW3QIzWQBswZoBKm}8W4<<74t{j8yg+L}6rH0%by_L2ib0boW* z+rex}f+6r;5R-#-{%lq~$ZJm^B;#viGD6#%F?Yehy7FWW6M8&eF$}9URWX;oW8~#D zC-^6$GsN;LN(NFQTcK9SC_%HWQyd0FbgGtZozO%W;}t0GfU8Xt#PpqV@ms!%sKij+ zpOuGo-!6YF<_4x(wTI9?bHi1mF6BYsB6>Z%;L@KAYof^t3@V(O?s8ikw}@uo(s@{| zL57X;NgAwD`?{qA7*;BuLf)SyS4vmZ06m?QNA0$?__TuTv#I`$?TEGp^o|asO(GDu zM12)wVgbCZ*OrJwtcLb+D%nzgh@Iky2V;(Pz8sjY%a@=T0t(*9SD9YX3v$JTOyw6EA^_>)sZV?JSsTjSd|lrJdR|b^l-S^$Uah-Ry?<2$F1=oCf`*o08nxBN# ztRS<(mdw=NoObV2%yDreC$Cho?)f2dX?a@cTb!?K@Ho~*Bq_J)8JIei5aXUiC)WN z;FrDy+7^dB0TWEhoJ2H)`mF(R4dL};UF<_rAa*Fh8>jye?-YW=Pi%o_?YNOwp--ErEEnij|)j+HK$ zL_`rFIg2C;1@Ne-dvSb%o@YfqdsY?<2NJN5S#gFy}QtqbOpQ{LmR?Ru?_jHH~`lkpF? z(6AgDz=kUVOT3#b=1af@nVGy44=V`8m1=|sZ=lS*Qy%(D*y>OcY|HeF?ok4YM&7anV9WeBDl0lxM;TkB^_`mWe3ACX0SV+fTT^ zj4P}~enJfwzQX_ag!@vm1b8?Syekg|vDUCv6os^{Uhor~B*mDE6OW0WegLYrRbR??W(Q46R)0trLfWC zmcWKWOM(hgdW+oN_G-*ls5dM7AzSY?Zb2!EfT2-wsQCo!p>tx8#OF{nxFyGZFM%XQP*9C*l zUOL)s1_IFTae4b0V6hmDfo~V6HH@g~=6y8nda*ZxPyeA$Qy!<{0^xL`x=P{Ttpn>L z*^bIv>U7tmw)d@^-jPb|1wY~UC8Ku^3 zC@1$WDh#ahH60sse}FgW2DbJ-v{Wvps|o8qJzxYLf24(}e3|sO4woj&)~lHvGG2Rx zFpXD4*a7s5fTr+7-?&SUHq_7ZVQC}Z{RHXOT1<2tKuAPCt(oZ>OF{scU|n7rF|c6l zS}ISIB4fW+^fSDa!%-f$)mk?*29l&w#Y|Ga)@oJmv2hKl8W>i&)>%`3x7^ig(?|$- zKtYdTW*9I|-H>?<`6IZFcx{JHxy^6}x`CS26jjr?l`|K9S+zU=S303cx0@Fa@P zQ3%9qzK>2fq*=2ECuEwuzE$gpd320z)&WAe?q0LB`O>cG19h&pv8fXrgE>ufG8#A8 zj!1T|hLTcCr?1VdSROGmAaw+84?D?=g&^7l&*^JnJLEnGt_p9R&XJ7GY&WkWxiKc} zo{d*`)b6%bfvX*As`u2NW7498x$-?TTf97PE9`XguAHNmlsbQz;SQS+x@-yBC-Qrl z7Q(Vm{t~Um3ZQAL-C{Z%8gnI;OZ`vriUbQkx5T$Vm3|rBn<#?GFmXQJIz;=s*BtyR z5v8}FXSreBtI2AbzJUlNI7bns{4~{DU{yYcOj2KxQGk7$YkOm zwRoBSgbh}bK0i*t>%aoHZ<_8QMuIfJ?kwLfC~S9LKf&@(4H=k@x1RmZz0AN-fF!?v zRg++m_UIiTk|3fqNrTi13lAZ^MuXov4+2qjU|bmkNGKt>Uh@6DhKCHYTdK`7apT{^ zInUfAEjmF(;dzF91nm|208ta>`);&Cg8t&AdLKv*dErQ3&h)MlN%vW~D$(`r*HPps zxu23en^+ZXRzs)(*yc)vX}TYd8hN>|TeIai$PTn|BGnE6XGk-s5T<`@$H@kqqqT2> zl&Teqsks19Mv*z+uBcAJvfVb33xe1|%1J%4i{2 zYFKW7aH_?!_Vz`&|Ex?s&uBnomfrdHE|4}Q>2>&W9ZE9nkH;D50E|r_$E3k%qNg8m zjcLJ}69ahvOC1YQEAL9E2R7A?D(NX3qU%;t*xnCg^8BR#9B;%E(e^L+N6<5U=e@&? z58MJn48zbRhl(&+Z_tl@b^mS~%(VNh3LgpLQt-6~w#QwoTR|R%u-majS?@+t zPOfZjH^T>P@4g`DNU^5bO^vwmfvLLR@lu1~ewea1PU_ni%N6K3M4Z^xIf6DSPtl1K zp7K`uhaf@C3YDSfQ<>DKF`g7VOe>Q~MfUXdt}2Zrr1i6a#XgXVYpC`tY zoj7DlFaRSIN@9)(sDYt$T1X?@!(jfui6r~y=0r0^+B_q}ZiwL@_kL5|-9$=a>Z218 zjF+~b-iGbRNtk#OboLu3JaKH|Pp_OK&8btaeg2V&U~k5F5L+J%`_euGyraRHD!Mi~ z5?)A@eX;F{CqCo-mDgOnC;#nty6$$@5@$B0k6a7(2Od6nE^6iE0PR`$hSE020lQT) z;7JYh8a}r@DZ2|AuAnav`1t*`<Q9)wQ$$Q-& zN>Hn(fAxyETTA+_t8!$22Mc;qjq-zprhBTXK91xj33(el3FiZwfDQ@0CIvVRDtNc; zkk=mu$Q!KNJZB#=fNzORjg-H9Aj$e$9N(*1^um;*F5XHcSnF)dCy%&w(Yd6@D^rKFF8W?W>9(*4`U$W619oek6q{kthHUREJKvFiDk@oB~ zOv)n+1*t>S2JqK{W|U^s)K6=6;SL{5*3PTI?ebC>>L1ovFXGhq>Ky$}_@zFLz%qhb za`mF#RAnPblbv-H*ds6G`3A>Z`-4|S!?{9UMh;vsa6VhA6y+p$PGdQXfC13!kCfaG zGKjaTDiL?b(zZu9OENSc2A%$y&d0%27ko*j+ zdHG_6-iq*~9*}<-sx?$#Zm;z@hdAme&-9-NC=ILftn!*5k#}3+LP_igFvz^T4&?ax zRI_7>1MbB9n9ER2$1$r^^f@kUy3K3k*ZMjlamadiIoV%yb=Dz$s5(S8tgrD+K!m%0 z%&l)6s^tH$pwOYF^x;xP-{yBu_PtsswG6ekwCjVd@BCe_A>Gko4!HCl;?I{esa=@J zOyc>{=l>9F=?>BLns#YaE4R7)hOIJ?O z^k$`A-+W^#${pyB!i`MVx&f7YAU%VQxwx*_64NzpP0etl&&EmU zz~&$cA9yoG%3+u)7O31{)dW{3TCTrJOa2y@h41E`4?EJW`BGlz!%wB8_^+Z}l_?=Q zw$Hdd5|4%pm+kItrRA^|;?$ItRZKEpdeLIRsbsAJ#YpQqq0AXY{wj1uMQ$xY;?^R# zJ{a9KXqcNQ)kI-@2z@g?-@a~oMpGLP68KQT#-uP)g{M6_pdcwiKJ33BjX(!LD*FL4 zXK1|@mJ~DPY?ALg8lvkUR}7fd^f=6+p}s_vE6Y!cP3{djsJSFCp~X6NOqdE1{Dd`B=B8(Q=| zJGbC|+WI6d_F<+(t^$2%lgA=YAg5h_%YmQx@y4Rc!%@__jYwwdC%?=(CIK6b9M4W% zfAQo!s2Zql^Ne9s%#BUil=|LDHaT>2KBTb9L4}F{4Hht#L^8+63;0Nz;6o+bU7{ps z$lQmL`=ggsiQlRj;Iql*h#`I(lqE?Rg5l8_D3zyxwUKETY@s@R(NS80A*NUAL9Yz? zNc5ByyrI@23h|A_QNqTzX#0{(bKIEzDFq#mjm1N5X!4^?rbXZF_w5)bnkehx2S-{% zqpMjdHLa>Vg7}TAS%Vp9S4n5xOr@kLn$Wdt(a;4 zy-k;>y7)@8zciwxbMtvh9dsLn;D}?xZS^;7o5%nuCQf8EG!83mVW2%Y5=oY~3@2X< zkNa&cShMp_ww|PjXeW0nR{W=UTY@+bppFxY%RjJu&_G4v!s#d+o^p14bKX}D9;AtgUrZXc(&&3tvjwsvl%6U%5_Z6SiCJb<caMtytYV5gHE=}LcA1|k%yp~qE zw?Ek19mrLLzKsW4?>%qe%(DXky5zQ4{;&HEqvoSJ(Wq!>vFz0I!%@$SZ*~U0M$Z+t zHRyUFiG*l&kRT|C2iteeB>Dr?)uTrhD!2QTVgP6jsc$kQjdM#XYd!zp3pORZOIDDq zMZRYSNKfk;B?;*}_i-znzTpd!gr60V%^AS(-My|P34^KQ-h^-B!5S(PLLA$B{P+-O zw-A4K8aYqqdB4NkJGHQZtsT-8z|5#E4kmSyk_Y19AWx=JDf8nzz*ia0cNFRXa=B|p zEf)uODieM`j*m!CykgI60=u<`>uw|_v42(I83dPkJ!ri((!Ai6?0510ofO)(rK9+c zdzpbgg|*-xS87l^rdT9D7R<*bphFwMrwc#A=Nc|FtCiO4!3jl!W8ZzwRYfGhy0$9n z;=LR3G{9(ZM`Lk?|M7F_!9I#-({SCviT|w=JPCt=YOn=6xDaumLjlNkj6kw=;7HHl z7CN30cJJ7CtfZN8Y!NbA2Y<&a3zB)Hzg{BteYXAZb)$Md=#s5IM3%?3&H?nYgX8zN zcKszY8SqbW|hGS@#SHIvf4u~eO-@yma^l&6%T13;xZ=Gd8O z|CBDw;;Z7*^zS}`GKJDCLkuYpTo~21Vu9mG)J9TS9JR|Gql$h$OF!@s+qTL z#*SH!;(uu!LZzog?|tOX2_JeVTcutb&*g(;R z028I#eAg84OB&`$XkBPj^~(?k`CnpoZZ;l2_~s)h$gaH}3O5A9w#O?;p0k0xpSaHc z<&f@~cm+T=51?wn|NBS9KYOCdFWg?bDk@i3pUaadLy+y&%@PaV$F_mqxc3UWha!dX zpYtNyO5dx%@`B-#8>{_1Uhd&23lmW1iYy3plop@2R{?r55~0X}2LYklS_F2F^{%RI zweFmx2(*DhxA8Z-v^pfZ@=AD^3Qyvmc+4|hPC{9KK|LTIfm109k;`W+FUWD|B1;os z9tSEh70km@{FdpB==JDLMBu~IamN#*af$;$1-lh|@Ml>Dj_eGuH5p1|V?LqEPGe(^LF_LP%X);(J@xXG7UgnxBx9p|OoXAMZXuKS|8y6YJe8 zNJl-$eSr3l?py2Xb_jpt1E|8pr#X+iHYPgz(=J;xuBV6pJw-NoR~v#sH1~^o$H-(i z9jb)(Y^Z-LdD7C%7wkMXoKj$Wt|6<1bZXPu66Hts_&-!vNinfYX|nZfx*?mYBnJSL z+)i?Lqh2+DoFBz!ASXk!A8kXG!#eAuoMm6P>a&YlVuzC!%i^ z6(IO6pK>5ovgiMdK=|G65p=2&26h+7b%#Z<XP%*kmFdI7*ObR00c+Z1!houvj+2eie z{~9=e<8;b)KWv(`zSNlpaezlMZ4~5441G{BIuwrp#J5`~8f=44u*KH7w6?vft%-5n z6eMd7ENvALGdcMXn<^V}K7e0PjiS$mDG*NZ2quK%xBmfYl9HBaZ5=&#Ii*vDmG#!Y|crxj8H*b?a^FfdZ0q^KPWBu0KC#@Z*W+ao0FL&;D)&>xW$;! zG{ZwF=8%uQx8;`w2f*l%@c2K*$LC)yC&$_7Ec-m#W|y5NfsCLpkCBm|t_wd&yCo?| zep@>Cc%0%vB`$74!kpI6rWS!^E$$3HwD}%VSxu()48f)y73ZGHbkE=hs67HT)%^Xe z!kQ<$IfIz&RZ~p<842Jof)?&rN{GjU4srf?hw}^NCG^$GGR?r#=}hMDkE~7sq4qI@ zxTT+&ZTZ4Lg=_*)r}3J`AD_YUpGnk2CqQNM+dD^d6p*pd0`cx$v(dA4Ahp_XTKQ3G z+3h{!!g{x1E=FqKwL0Ff&CbqFK~Ke?X5`RQ9Di^yP?4Z=Lz>jPXkK>VLr1OP+2pGC zf3WGyriE&B1KGS-Q2K+=^E@vv>+PtG@0mEjd%&%AMlZJH&mOThc9Jtm_4<~wO~s{?349|OUT zMNrl}>Z8YOfDBv-T|WyIe&qcw6u&Hp_mj75Tu3Zi4#g;?rO^!;f8eM!wK3=u zSfWZCkx)8GZJgaTbh_THZ4HRQ%cG3gus5}f8a}{;K8lU+qMtS7GF80s@_P5;Q0?zV zN%x`x53!nZgg()S@RTz|e*&}%M2SS8cN;-~yA{M@7!&BxcNAoS&~*rmem{`~KUA+6 zV5!%g1K<{)gP9mXw(-!*U00NNA;oHO+>=81q9yQ;azd~W**whBNyYN80(Uf=n6%=+ zgcOgqTW@FUZet(^|twJ8Megcs}^iVlguvCC`izVWJGn|hASz`n2W4@MWu-6UL#n>g!Ru-j|YNJ*8l73V21$o$~tQA#I53P&XId?foN$1U=r!Z*?v z`hP_mV&6>>Zqnaz=HyT~9w$xDJLZqgK|el_PIoEzYCGEe(29VML=^<%DqO&f0W zMN#}wtW8~gU8{a7B_j??N!{1GA4Us1gAOri=^W{5lIF7#tn)U^G~ZrX0X+G_B~4GQ z!s-BCji1(hWf^#B#C@o((7AKxbZw(5ErTHH$ZEcb_xA}%Ot#0|7Zp+YcZp-kwA&D{ za6Y_igg&9ED|EL9_G0^jSN#k})5C`!4&>y5D~;srht8|7zP9GrRbi>uu#^KGecrIIbaQ`4+vj4>(BU#m=M-9C>OI|E3tyramAQ>ER zph6Nea`gSu*Wv~Qb$tIR|6q|Nx2v6s%v=4 zzrLC=M9U_Jp1om|&4rJ-kdoRQQh%O>g|8|vy6dL#5jVUQhAYQvKzZL9wtJYL)-=5z z-UK$CeJgn!Mz+a&Rvl&UKX?j~gzw;bk8Q8Jtq8E!ZOdI%tC~V-fnY$%H`+#O$vPLG z3cTKp4>Q$UB=s|FXQ zaLIR~c@99dFU|qcz@fHFIIj05C=c85_&Dtb&^d`7mvJ&20%>|HAjg^|JI9E&qm1&$ z7J!*?3+buu_}S0;=A$9OLJimd?NSNm3Pu3Fd&d2ra=r7HqtgYwqd1T=J`tB^T60By zJl~IfcpsUTCm#-8gAr@!dYskBJ|L?dJ_{npq;0bm(O7AUuK)xKEK$l+o?mA}1arogvY!pn@e^QOc#wo@_^Me~6Y_Dgqi`g%cuS;;-6W31qo1Gn`*FPtdh=Tr z>~*^`c_WhZXc>5{?ozHQCMs+c?$jIHzK%HbOdYLIc&+y0Cth<;2y5g;68*d3vEjAX zG<9ktr(ZMWnSTE~!%4MFO{o@^%3t{Pn|lfFKQx^l2efDRiJj{T2=uk%b9O0eI92w; zEmvpBZdm`qOv*v;)S-fxyH~@jIc!(iHsc9}fm``Hfk3&bBNz|5G~+;$7CIK3z(OeYu^z5h1oZ2*Jvv)!ho*+7T=3W4u z2u^iK@N|Y@J0Rja)4XgYsqR=d28Z{!14F3xGN`o!6*iClaB#$+j97VpH-c|{;FAgl z%Ja=6=I4@V8c0=~0wSBstI!n|)bqL)Rwe3)19wpiW|kbnb3x@LF_*Em_9#};^$wS04(z@if->)lz2$W&Y(Nb^M=&c<(m zZpd$AwDyb5X7vCAWmB%)Fn)ivtJY^st@7Zqr)AvVN%-1}Y}#QPN1@xf(@HD7e)Lyi zd~$H627dK)Zf^fk1J|Xr;Krphj&B*~kL){RaW`hP*a7ZN=_$M|NnFX3K|r2}M+5l< zJi{%pXQ0!Z4bNSYfH^mMi6SprKa)?oRE|lKXCTF%nX6}iMFfW{qEpB*Zq43*Nk|TV zcf&^omJO<{To;^==nV63Y>DefK8Wo;j*n2?AEt2Syn{Yms@yE3es{Dh<>n?RYm*N{ z+Wu^Uwayqnl`!B#2?ki^xn$pJ@aSS+i z>A?i_LbA?No={u^r?T-cdds{Yw<&Q*F{|5WSPdA`;y^ar>3+|eC{-gIW&imV@zkzF zxy+gyxJQrGuFjBd{!r*C8btdb7jBKU_P(^ywb7!ML7(d>2-VScgo3kz%!{tDbx-# z9w7hp2Goilf?chVar9`jS?H2DvCRCs$U&@g4k-zG6JN}Mz-XE@gUkt6K@J+b3ALN~ zW>JN1@M@`u9?!>s5uBO&^17YisHt#8lD8?9QvgW@0QY_!{zg1#YeXb)4%Keb${}f% zvV|y8&=vNJ^*lV0e5w1$c;Erlz66qT zmusdyB?FQ^;4;ep-%?(6yi^YHp5EvV41cTS^Gx>T2n$Kda*cU%X|r7zQ+jXu+%eT? zVkL)#_)ngb7dkC%l1*m#r!^UkSJWK`z)U!|3dr6rF8GWNiBfWw6LlG@Pq&?HoIxgX z)KTl;f4A6frqJjG^rOVa+kfT_nr_AB%Bj4b)w0p91-f?Eup&EsFnn$2f(L&DZPGgW zRlfJcx|P>a4*L9@PhuE+L9${Q3Y2qz^fW(1k;74!2Ulw@|H5Z!cw!Bmhe%1zkVnG3 zGx$Ba!7Yh<3~EKN z4r6HjbxA*PNq}H4FjB(O)dpck^Z+}c2+SuxPpknIB;l5T&<{rr%L{JTihKIc^{s}! zBc<3~t<@)2k2s*gp_4(vcAP70HEfK{ybK0?sKh!q4_bK;@?{dSH-AzRjex2;3p0(6b_Qxt>)NZN^P?DDoPx_> z++hS$xqJuIJVRPQOoKEBpm^b4RI#aTXEMJ=5h2L9JOWfOZCx(x7S=YeOt6ybM1;z` z+3e*YM*|m8+BMHl zIXuI)HSpix*?6dvv#lor6Uq+@vb$^J5D!{Q$Ukr0$%w*)w|^_#ugNGwWof{S15_oF zjXSA|RBhkS+X`{W{nUM&96wRaV%A)0dup+K(CXaEvm72)26ov7T?1!XnB(wJAHKKe z#OL;aBAQUQMqc=0O;N?{RrFU~bH4uC?VCE=`()P&{?d0e*xQ+;ao^jp~v_3`j2!KF3sD`$EQ zDIX~QGsJ~ej25Q&#!&uDiDNN71OEY?ll}UR!6+aEdlKLZ?9Pw!2v#wJ)cN!vZ{LmD1A*a zkyHJS&F{lHLk>x8g8Z(J?QH0bi>(~F3cpt)O6)iuPHVsRUz4=5G)a6YUxzerb7U!m z8)LpxbaH25wDD$2(2@`NQD9g3bq>O7CV0)z4rQ=`Gxvw;Xxv5WVZawMtA0s*hnIpa7yIWhoQf}z;Nk=`#h*C(<$xukt>oAis`Gy_OqRgeLd zZvk8Xioyk0Ca9>dViiX2(v&FcrOhhB-8Wq|IzYf3RA{b_PKZRO#N_N&NOGJVSaxSd zeRZR-v`ta(Djt(xtF6AlM?21cKG+hjsR$f32qFHJqj;hJ!Y(h^3!d;>(~4)!B6QEK zoaCX!N3}Ux`>D5dxxNbyu7AXyR2&wj^c8kyj{Ehwm5K7vHE*py>8HuVA)@U!J8A^l zOT5mzlJ$<^m1i>EnfJC&2!- zvcmp7vcE1tg_4gg$DxusN}+|j@hxt#uYm$NwQWH&pSuLAtv(=FwyYw`Sf@d_n>b_b z1St#mlJElxfb^Kb2YycXVH6$5OYkpeu_&2F2A2DARJV9YAFJ}_P}TX*yc!J^x3^lHR`edGjv;v)azoh?KgLwU3+#@$h+G4 zocn9F=>Bi3o>?MSj-IEkHvMyFeVi#TyR!wBR$j@2X>D|9;`Ko3tXjl^OT?Tb8@|&p z3ykRapmUf-)Q7LlRL)TT?!Fy?f%A90czP~bj=6ULURN9;5nE)j7R7@rP!egnsFuH*=QRaOebuTgU z?&Y<`?exa~s#~l;(Xn5{_nZiH`rl{sqf7Ne9b$B_FZ=c36*6C9Qk$OzgNWt^;2}sp zt^M^w-n~!q+8(gCS)Z{XJNq^%@5ioDMfr7aEGFm6x(|M^QT)-Zml`BOx$X@G8hWC>hqLmD?z7EcC{bStx;IYw&3s1RrlP!0aCD7MT(?Yy!?(j zQ}(@_IK}NJ6X@~Q;d8bUIA0x}sQz?MA1f-z@zQWBmi5;A55Gm*kSpK?Yzsam?hOw1 zLFeWbeUFArS*jIvZm%scTaaAd^*$IYE8_m^-C+f+>JcUjk)pUfJ9pK$J-r`nOsN&J zplceGy@pB++)oqQfephke4yIT9-w_Y7xoyatW==%KUC!NgsRRt3Y0#;`1Ba6iPTNz z959axe!XRUkT6Wj_qNawCk;Q}`t84x_FioXI9BAGC8 zy{pSquugP4CBr&GfUPk|P@#v0uA+q(ejwK$iH9`NC#;qWjv{bCL4UnZ(G&b18i%z0Il~ybyYZDRVq2vn%O^cAl5@Dl{#TBjCTtdMpL&5%^FI`iF z?dfJg7#iCJ6XUYszWJ1>wL$OM9|NkU_dwlv&9`heIk}|nqtIvyVxht=#q#_9?x22o zSOaNewb`B4A(V>{ZSdj&AdQVF$r!AROvI1^&Rn2TF7cxmE zsbbR>V?rwx2VxRAap7o-ok^`paq>N9t1D?7bka}9G4E+<1%F_#vX-_bNR#JfI8Bd^ zsJs?&qGG+@V%MYi!awFgxAAnM8{5k$Ee>zX==~u?2DOG_PVXRKCKQ`^>5f}JY)yq4 z6|^91I8m@w_ti9oGzaGv7H*#Y#q-mtQ=e2jxH+5F{npm?JKr1Rp|`~Hmp|T}^&Yh4 z+)g&k?L*;zHl(U>>Bm1YP$Hn;x=su>o&$L%^A%xhe;g^@GbBtFSch(dyKZmm zmEaAH$_6#YHFa@Zo01FG`fXR;)MUp1U;H<xSEBtbO$X`${6A`BWI@}{I>CDEPmG>gT| zp`ZI^eqI^;K4^eGBOMDqy~n>3_|RUEIdr{zbao$S-B2Jd)iHuorAUGv9+6i-Y0IUD?GY)2oPqV6?GiZfN8Y--#!Rg~Tu z(j3wiYVS_X*PA&Rtv^--4K-P(wU^_`H$8=ChRDGFQU-cM6z?CHX5r&4VP5VLf9U>x zo_+S-^0<5mdnlKSaW($!O5?h%UyzVy6gO99{YT2<@)^CO4K;XpXG-YVlhLFO%Mulk*cV&5DH-SyNl`^aY2JITA!T;jx?+&q(+vyH&NOHr=^dKL z?VI1$Z+s?bbqgW%VQlR%xAfDyU(xF(!*Na9*9#k8`S(><>Q`Xyb%7S^?<-fQAd2Tj zQ-v2M&#k_Ny{qy*b1=|*zPx1PG_}qfE~roZL!oB{JlRy zI@)#%eZnEP_d6qke!(WWzGd5s3S12~Q|xa@y>1{OFPawz1JSdFwE=n2Y}!3x+{-{y z$-fsGMJeKqv2n4r4Q)SyKRU>cIBtP3*Zs2+35d3>DYHy&!b~H_Q)1ID!~ne!1DOC* zZft}CvSE!xdq?CNl4`6l(25Y2WVVx^*7-8v0^__Lxj#aVlvA;d)^>24i=jh)y4NNbTdfX{$VY1HE19T17YLSY z)ycBXPQ5NCnbI~Wy|Ym|6(g{uCMzwE+H;L?46hB2_5foj%&mDCJH2CUI~SB#F9D8b zu9tuQp7VF=rvG}KdSGVX@IChXTCc6_czH|9$-ZFkZ$@Lg6Fjbw;>f>QkajqDWGC`F z=rP0Yq&6PFGO)+z_CCJxyDdrSApWNymiNJTcY<9o3?lH2Py|7O(g zt^&p38a254)|b9kVY_m%+HOHlB~>%!G4;Njnv{n$(BsQ-h2z!fSyHl9Nqszs+kLK2 zXYE)Sk~O}U?OH19e>6N8^|=SY8QlrsuR-*f|}8Kb;bGKBmV%(KKLlAS_Y zZrj(XYescl8)(8zSylX0ga6!5Yi^KK!mX)Iiu2Br-dg$$T?-ykpH6U&MOn98q~^9PR_)TJew$oN32tc4o_71@M3Ubfibqm^LvS{3W$r3TCG;igU3c3 zbIAcLm)-eJ+6oKomLLZ@`n!!aXd!k3Y$i3kpX=SCKwBFt!c}mN@qc1;k;m1!sLqJ| zUusuOa3&&Xa(lZY_vk81Mu*z+i*h^%#!~+DOHW=+Mrf$7s+#uQM}5lY;12<3iPu=R z^SK{%TZco?b#$;b<>bi#HgAx&~!a+Huxujk{`Q@&* zKZyo!mrox$qgcE2(I4IW$K3-D2$pTf;P*5V+zuqf!P;c`tK3?J`;5bGYHw2n?@T3< zUDd!FcmT7`Md{#m8foHZLPJ&ghYZVg93R^Gp!z~RlvT?HjqyH(d>%9zH@t*Rvx&ch zVUknTE&a(t-v8zM>a9G7doeeMF_i0D90*^q99W`XiyYhVl9|W*Prp~_f#sA0D zxyLiz|Noy3s2tLfj5$RImqV1z*b0Rbovy2sNXj}IMl*JBC`)0X5^Yu~>Z(YtD`bqw z2wNf}Y&499nVFq;eczwoU%!9c+_>F#d-Hz3U$5uu`FuR?4?=H_u$uxO*hSe`>I!*)Gm9wHgK>%I~pOj3@MF$En#s zy!-4zmfnOHqFw%t0EL5t)^AeD)EDX(aqw54UtHVm7fwX|z3`@X6>3l|S4q?o$^88M zS#}s6Al4xaaFQkVVU>3x@RXFk%ts1vXtG<0K-Bs;O1Yheaty@y1$4x!@y`7+y0ah(I75#f^2zGYXS( zJ>)k9WWN4ApKzi@p_n0b(Au;cv~WT#h9s)%R7AQ1Q;8@(5dEi2kwSZ2S>aHwBv;Bj zIL@0fU;6V4pDZXy+v5{0uCFwicw8?PfgjxL0}{-Ji2(|&OZm8Hc!E{~=1hiX(B?EA zeWKAW!N1vnSeQ7ioK~D>ZK(_cjC|}xtq*i?bQJDK^dRs)#kWa|(LhS=e>(XzLeyJ1 zo57w1i2LlWFwt+2w_4Iv*Daz|Rn_r;`g|Bb-m?ol&%>x-=`6g3F+eLYVV7WsvBA8F z!N0GKYwMa9lOkxe8g~E;Q>SL|peiw#Lk50evFN3J9-DyitZ-FU1|p^nFFGa>RNoi% zP1tP7*9&`b5O*bhkPcT=73yRzqR+pftYY1;8=kyd4~Tj4EJGV>r-uK{wj?MPQ$Ske z_GeZ9>C`X<_0&-`2Za86#RP^AsM;HVMu|M{ax5!uBzaVusM6VUdGXBXW-=oxi8;}G z02YwfM!`$oX%Wbk(pDhqW41zrxUqg$jF`G;oXExr5QPJ1$MzZ3C$&(9@Mw`Z5Kw-y zUJZr?oWLHVB#VHeI5)8k526TbHVWu+H5>dru9qU!GX6+nx+_C8VqQc<=MsO$FRD7C zQ$Q?T02Fw|+7X{PS+jlxhIX%Thd?@5J}#7zfYi0hoCwCE$rdH?lq#YhsPzEOt+8T#x*zzdA)vrmAlaf~0N z5m@=b&3R9neu2H_CB@!x+|>I&9kQD5F>o$SZ``wYWJf=)$fcbQOe$3OPnVgg)d(|k zWy@mMW;4J3Yn?M_lY&NW_i;bs5IDe;GbNF{zn+{ZSI|xxC-aYoKiQm02BpU_8f8mg z1TRTvO5o@@+2{BuHg4h8S2YQZ$tRIsY!p&Q9GQ+Vjxw3(+=%h0R?flH_(rxPv#k+` zU|AAP%K#zJta2rr^N!8;(pchK`Wv4BCaM$wAw)RLPk{;&0OJe7(R1OPD7P`9Ls&S= z;@zax^r#jQNWslZ3$-?sX_ZZzOl@`O1uz*P0NJAoue$^Y@I8n5+kyuvEfV*px+w%y z;W(dgLkVDcpcMV)9u}NB$xc!O?9oH?o0EDK8F1 z%yiz;Mzf8qWR-I^>Mt-uiacxFX;ffT+rT(%74l(gFBGOCTI6P^YAFFZLUkiV(a8s2)2nC%cNq(kpicusKM3QGWd z*eo5m@L@qI0EcAyB#>rX7Nk6mK=@U>v|OQiI4RdAsqnJUniftQ_*Q~I(#@{voo>5Q zZqKKygbUs=I2ap6^yvb4Z~*O(h}eW-pB6K~mc-ryG)kc&{;5o0t&FYKGD54FCKHTc z&=U^Mi}0^WFD0rC_bQ1G@e|fD=cfl78pde7#)3sHl-$*z4WkuG+5BHzF9PWsU&!d4 zZfV*Ya9Cv_o=-qcVz}45lYwiFKn%$mb?JCCG1pRwX~2*2%Z(x7@HYcuT1ew=B(SfW zjB3!8u{_5Gh7yEniqUM`o2;o-|ATHIBBO!OEp7dfsH|wmtH}V^0tmFl~17kNZG83TKUE5r}z=YFq^Ur_&;&fzP@Ak?W-- zB7nFeUP=c*Q@F3bVUiCd+@s2QDKG_6~6l z3rdsqqaT>x18(UoE5@X!h=)Lcp^O_$#z$FhP_IP7SyDw)j0n3Dfn^Oa9+2NKPC`WI zD{y$NCy@$}4#lW2AHZsl6}VTDJDP7RD=mpWbLz)87wO|DsiK+vn#MMWtE(@dmGVHe zMkJW+v(OKr`7MowzLt7Y6)T34j7=KLnc@Y;al*b1V7D%3@^pa)U08HdueKLnDRfiP z7;ho}vndudda0`)X-za50t0@}T_E9CP6@n;VM!%GX67fszi>J_i=n>;8ppdYTwrH& zRev%wZlc7m+n0+gv9*hi$&=@BM8{r*9}|aLj3q(EGId?=g6I~vcJ3yTM;TGN_6i7! z>I(Prf6$sl05kG99FA41DuPH<&43}|!c zD?_}x-3f6SZd$Y?alQeaMeA5@&e4a|%j9x60~B-7*bNEUg6ttanr}$#?!?f@WHAlg znbT?(Ti$z)%^Rn=^fnwZKFKuOE?HEQ!s^H04_t$JLfBQ!tT=N1$A8K`2|WeIkdbMH z{B!VTELqkiaIfG!Yj;3PM_riT_?e`U(XsDg{9l(dK2uktX=_>YF_U*dKZs@0C3c#T z#qvjGBAgo5;tu~Lx{-Wgg}}orZWP%Z+6iPwd#Z=eU_6tbmr=f3ev z4ZO2axc_ugfmUGIlV+1w>U)ZWEgCore>&E@JDHs$7#CDP2x%i~YW$U@0O}aC@g!ew z3x<@!UV>x!o_tXOOrg;}DG$R)mrE?X=MWLO?2mIT z0&)qks6*PitV2i42Y_>%fhLK-8E8zQsr$0r{@B~7Zu_o9Z zgcW(_7!*lrMF@;cP?99@Jdei|lHaIE;w7ZT4z(6YbdF;-9To{_?>$Mmp`to=49l~^ zOU*kca;Lpp)HJ%6gjTYhwPA@kw3JJ$3}7xPucVC$Wf~ly?2r>_cL2E#Ba}4N9{>+3 zDqmg>ZX%=zsZnx;@IM`QCF0+OQD6pPG}3ZmwM0ut1rsS?(c<2JX;44zsiT zCTfyM{4MTfD{qwd3QD-NR1PLQR#>=;tKugS2m+;Gj`Gx!AFi6_RRC+T9^Uw^JV~@T zkN4vA^u>w*#fO%FsHZ)RCRPGDs^VvZispIPeO=%rwv+^7$>f)8${&dgRpt`aKcZUD zPP=HMMKH?*c+nk|=O_W| zF{)avJ`SnRW_^vQRm-S0=vnnHucPILitg^syM?)4z45j>%a=qsEkxenux^){gc7kM zK=`gN1<4U*cmVdM0AMCXLyd$6(F}+Xz+{~sV#F|<2@Mipnr01%q~ylH;{+-|gwS4# z6+lAPwCWJTZwjUclvF%00Yo7fLj)N@syTH_eZ90Qg-ELu@z}kU)U%`>Rbm~9!zb00 z5X{RpW#EOPMJL3B`xFsnsI84b%aYK*lPp9mC$mKWi+7SF=~M9sJBJ#eWejL}(>nn$ z31Dd{=mu0TKW9yvTz*)tw5o+CsQ)?OAkqM=*cW4N!(+dY5sw4_w0M?J9*w&|K{8ds z>F+*xjFmYnN`o#1e}XOn9M)=y*SK7+TmtxRW%hhgc_8UDBU~{-NE!|0`~Yru%Qu>_ zr!fxD@B%0Ns9$mdRtfMh*^++E5J=Q$3^YP5GUFBn4_xJA(lK^~_Y-y)E!bS~9SP_h z$IneS$rZ9@;M^fsms<-!$njq-EiB?mP+UI^)rr}I7V>O>6t0$;>7o~$#j34zArW2~| zksVsVWqhF0aX^d1cD)6eb3`=_sLjcdYrbLC&wVgILgnjx=QHTfdYDluds&V9ZvCSm z8vynmyhaPFf#_0due8AyFh@NF>#eJkESvq*G+6t!rqobDvlLOz%YbKF<8T@{xN{sl z#VavydqM}BtCxMs_ymmKegbfZ3Ir-+RwLU3;k_DGlPiG*BJdgq$!=zYe<;+?j1}Xo z@Wv603`~MM4U3XRo&=LFrFL>W8Kz*t8woG0h!GM;1U!m+X{cPlSfvFX z1v=PL`Zr?@jleFqkZo3@EtSY2U13U>3rMU0;t-~VkpkB#`IM@@u9HdJ6_3JRCLWLV`OhgmS`ZW$8)#UellwbkKi<+CV1vn?sF~! z0%7^tIt8@GoPm$ySyBLb@_hYU(P-~9dq{DWSk?E06VN+@$52@T65YR|z176ZiEe_Y zfFBdPTV2;As$lF9aGc?Ml28>L1LGpYGI0bPI7-wZRgV-`H^>$2Tzv?PFo)zFQDd_gphyw-*;8l-teH zLU{x_BNu!_&iFu!(tr{qii#T~aWmtvLhJ4YlYb_tmt>VmG4-5Ybj=>~{@)Fj7bJM&rp}L|!*=`Go^A&Ta#ODK zpe?(?6hof*bjxym37xi`Ie1;V)JtLcPYK|%F>2HZ&bazL^ak=YimH6y^nmNMrvgXF@)qn8VK*jj|=5MpT=}4KwN_70zhSe z8|#HJP;WR2pn7N9C*kXa8FCEql)H(7pr;8H0Y(x)*$YSgwk8U&x$1=8J*aOIiMdqH zdnuI@mQ#5OMU3VHKB5a#h{XuSTyPs;&nD&){xsr-B)Tu4I6goY(imHi8;g3w=$lc< zXQ#E$ky_R;Un4G#wv?%ixbgGIag;==KnrqGrEDIsiNk={7CeE+qS2VAI|fGEV~9~O zjcx}o^b$#nW-yn=dASFO)&D58X#XtJ_S2oQq zQH2(OaW&@>GoGV0P{d))0!mjMkLWB1&W8net}Pt?_GPqL@H>WqD1y^?xdrki1diL? zX@wOe!>LG)7fgGnTSh^{8-Zkvx zaWs|7f+&<=aIjK}D1m8k44-Tz5WFT=XkyT+>stig8YnQ<EVTPQHR0LNZAWYfIzYLNth%%9pJ6$9>=WVux`AbTlkn~#Ij2@!Tw%_jk3 z27t*-FYOLd;t%q#u`RX4(2+Kbrb|$G`~{%fG2~N}=q%#2#!G{s$IK%mDsxpLQCsL7 z%0-|64XY*>>T3`NXcF56bW=)|_7oTP^2dlFDaMd-%@VO>I379iQ$w|!fR?z|SODfs z0R6#tJta|?x8n^rS8(Ax3*J4GH!OVP0MM4jV7rD2JPSwF$Ptw~x>mIr6(*$-=Ol~7 zf&?MYN!##*Goc!y04@ux!qVjckO~{aE>qVtN0f1u1B4W}t;2AWjHv}Rh(@!l2dpm* zBKV)8-{{2|uSS3v*Gxv7a3AM;GS*~Bzi5E`QWY;|gAS`!%RtXW2N_<#=_*i*+~tCI zNC2|CqyexoO6{GoPiUn;0)8SNDx6TDcp*Lv_Q2!_%lH%DRiVJykvfN}Kh~zO@(v|s0c)av;reijE}~9qXI%BL;xglXETO! zsh74NFg@{Ylj=7NvngGo5K!$n#=tcr;wuB;#Lp;{T~|WJ2vEhSV3CWlD`yWfRb$Yx zuXZ%3jbSXVUOwCmfv&U!ef&<}Qbx2!!HUr;Z+Abe#oamm0b|8}R>Xr-M|mw7LqLis z<~CZf22m-}49dFQk<3EUIDZi+vYZ%X^@P%#hSzo>m=ZRTHohcl>!g_o8MGnos`^@* zpHlme2pG=EhZPDy-ZjRm>i|a5r-)wxN-}4Xcuhn+(Zy+$kZmJt0!`WaodX}N! zXbvkk1HUeXV~9YSo1~tz7N>y`3H9L4nvUl~oCudbbsZtBmz@8107%Hlpbfb%DAh~O zm=Z2v2WKE>)QM?UvT8o5u#)zw4~vT#Ckz$;(!haq8Q8o=d}DZ2&IeSrOdr|TU!>9k z#whVYXypX+kQe;;INXC3HX%MGMan<_kA96oz$$XnVZcaRDPh zldC1|*f>Jw3mnA@=`zGm-PtieJ+fA=V3Ha03xWw2q}vy@I697?>i(yL-0?dz)>#8b z3k;8(%q_tlzVxO60I_+gNkTXb#aNgGxX7LY2rnnfbZkliV>8UlanNrpq9efJ;ZtDj zj!p>#qTnWU34pv0a;4b3qZrR+xKu)<%z(=q(9>+6_mENjlHHQ8QK4Z7;P65U^5nyy zkbON0^*oHHn=(cCPiHm4h3DQtpsS@!@A*SZ>_#;KA6MBab+2Q0mQNJp-A`ipiFGWe zU?624IBHx%Gsx`x{ru2Qay8im+gtMGpZFd6eTYAX6O=GU7;y;78MGFMz&MUG_$ z^OsT|;f=o!b5&B`*S!hpyn~Py(ea5@9y9V}z)=_`VlPZI#LH$F)q%%%`Xb|P`E#*#f1 zn!;wDv6e1Da8!m_MB&V|7)Z)=CY=3E>sTlUtOrGS5u7~iNJ6c#$ySr%|8ykef5jwk5%>{4L{c-`_8y(o zyPo6)2ihdvz{C{cQfa}Z@urA3F7+2xdTUDv6;Biai%Q^3>nzcV(^=>V)&I;A9ZX^j zBo|YCn}8)ix_DKJu~y)P0Ae^5?#8(?YS5CPRDhllOPHVMk*5LN_f(8VWlR9vP3QeG z8oLN>akMa~Ha8}OQ^H&JD>|zEq;d6hZD?hiM(M>}UN(m(-a%{OjL_FWo6h0-0_t1S zEbCu|=$&w3O(E@#BvZsG@KPd&J24Nmgv#Dzh$@a}BTi+Ya)XmZf|A;Z;qs7ZO^T-0 zPvtGG{uc-ra0Q4men*w!x1z~dO|A0kyhc*8JX1{}Mggz8Rvj3pqAYq(UZdkJ!=ay7 zV&zam3NeQjK?2_d&-P=)PwI;#jK=pZ65ewW(D0^1yhWjw@N8m@aIqbW9%a`*W6{yy zH0?$R7<*7vC*^&@8N$cl1c-oK3LNDM-%(J^h!};6P&d)<6^~vT*3=>xqBjXR1`XA( zp;gR<$cY>#TNeNXEkbFXBH9qGvbTVM-ox=DiM|5yG5CT}Rur1RTEYQ(gG3hb4@HsE zb3s5>H}W`AMn&}|p+t*_V8|PZZK7VPx-xH(Kc+3iC$r!cRbr|LsGifXYU+HP4~2o_ z(FTrtGPvjOBqwNhBOJGLxNu<$!O?=Lvq~~}%ffh{HbA)eWf(oBa5A*Gi>_yKP5_4# zXervO+ZcTd;SGlqqXD+PbVM+lk=6zhs%`TZ?0OYtOL;6?n98sDfcv6S9UbEIAG*`Y5$2^N-oj=(tKr z%ix>m;)ZKK%GqFNFz>ey%F-uWg^Te)6bPH|;A-+Z$>ee$QKv$!&Jyk6J=4%(fRKXs zQu7uVUV2QiYnHe*h2yMR%>U~PoCcJEv>9{eh;|P@2TmgL8otTnsNMqtaDkZJOQMx{ zFyR(?J-kl0h|cvjP<6FM94z@95jD*LpZrdA5>Vs>gtM0Dk}f>z6@tbpEv>>t!i7C` zLv6qxBB2ZW)#BRI4DUI}@D$dAn*vi9z5*UGVBRnO?ijU99v(+@V-K?Y8sW!-7PSOX zo8_EzP_(!0lVEPjONFV$HwykbPrZKNf+mp;H0vcxWZ_2ZROZtJW(|S1v+Av?(Sf*bv#nc@KeShJ-+T-7Nm>K2^kv zuxY#LEp5Qpq5jwCb}oqovTe_A$uiB zJjS_?I6TqwP!V;KJ{c$51p$fPsxkhynFMj^pH7s9ET{d{Ew<#52skHrc zcY6846^<=T2F0E&Ge&P>_E|~zxyfUnQS6?wf=vCe|8(pm?B$Z?_O9O%AI21M3yw{( zH-+pV2DG`KIEiAOmJak0;f+}KM{UqkGzI87utO)*ZNGv32gQ%T_$DaP5+G1`T?Y}o zg^B^>eS?yL{nJMOJGux}xJL~HoU~SOiWZ<4jfY^5Cv&xRUj;?uEAD-OL)^FV;#3$}g_cqqY1L`&zX5eP zK2kzA2?d`#1_ff35A$*%_`(vzEi`F@lL44T>ik>YjwCpGFD$@KI%(}V@Z$k7c>xh( zsP36SM})Xk0K3I@nJD6jur%1bJgkn>MhKJ4&w(VQ<3gz%s2=N*0836D9B8~(0paK@ zjSAbTB1FkrR8<1Pa|8?+ddtLg(Gu(CWkmQVA)IDu287IH#(#=a&E+JF#7-;4g%Zjf zK4QpF8LtUD@c#u(Rga2hiWK#nGjvV;I&MlxQWqLdxTld7uDQ~NixaaKd+BId_Xd|< zpez89=EuYg&Z8B)>UQ;{sMj3|9T|bxF}m46G}T&_+PK^TB1+;m9#;~H4D%?@5-sf+ zJqADaM9P6ffQye?jCl5`rGVH^90X#tk0^$RD&UmKvPr49C!6JKw3$X#uI{$dy6fy6 zs~|*x3mO1{z+!|CgCUZt)LLZ?heqRxhS&nxM3EBAnAR|^$t#gA;7T^){+>CpwhlSTqM6-)r#P3NS zdx8mMhRIdDP+%4JT;P#&vQd@*<$^FsCxo)&&(VfiFfS-#a&+VzF+$7~H>f8^(kKM1 z3h>ZLQBb!QvI~biq5+3>peZ1|XsDGXW-~Noz_sE^LApA!}EK{(lo9gpH@Hn z_tUiu+7h=Grj)LW9Shk#&FdRPgT1-ENMgySd zOR;DU3!SGxrL?J0UrD5b>CEd(liq;f6!-v8L^o^Lo53C-;`p;F3}$Iog^cJ!>PZ?L zz0mdSUgD?Nj5qY?tDTJVA=)>#X{5OfY!Y+M zX);LD41|7|8HrK8WWkI85(6?GgA0>{t~@rvQJ@&ktdKD|?uD~UiWTrsAZH>QD-v^9 z^c69gXc)H4O9PK?*P|-+cD>e_a8(4%3f|7ayb&i6s)pL$%sG~1T z0uF+31lp?b_&IPT3!m+|gsyWm<3FLfbSfId39q8Z)pqm1n;v7t$<2(TRF}g5bC{G* zhcjZVO^kOldFWdl2!ZjD=NvZ!0>KGE&Pg~D@%=r>q^2**>1;BMK%P;&0M z<$^e6QSp47#FH^WS`?)dqDpF=OHPhkj!`rrRCWoauUe~bMhvat%xwAaTy&O}Z_)2K ztBEDCJNl))3tW>r?1`Zn83n1|`+Hce#ei(+Y9dSt>mJs|qY&*~BTSB=Kq6Q;dy+mL zw`BZDAk=`MI6)g~5m5HE0sqin!K{;rTm`?ddIl+RJSXx4(g4H?Dn9Jf>@b3ZC3W;O zTBQ;NP&_}!;qb=S(UQ8X@)Q+8(UuU~%)%`Y9r=P$rWIl2oE>&bO?#>d_UO*^#_a&u z*gugI^s7$_7oD}VHbNt~$REy=%&-b;=<*?;PrxE(B+N?P1tn{$XZXOr`8zf`=C=^% z5FcHy0_T-hPV|@vlw7#6yDA|Lc}AvLZe>M)PAP8&sp=_2?>YcdhZxEf5tv#>*wTb5 zcNO<$Wds;CO@s2-agQ0H*~%iu6RLdbTXUbW3ht(qk%+yqpzg42>a)xm&Ts8UX*k7CRQnh$tei3)=zMyx!8`#89wDO531O`H+WfbvmrHC4o zbB@V45hq@RO#*r_ggPbuG|6#6302Lh6Djog)C-en8XNHuPnu?~;C)o%-9^0K0->bE z0Vm)xwB zw&a|=KbBE=bH}1&P}UP7)1THnZw~Pg$VL1|qR@q_KN*o@5MCD(#HM|uw~T|LWgPH* zMmR+o(>&O+IHK^bTXdgObhV!xfoY%BCQ6wzu^R)|Je0c!fS)V?r=IboDXIoej${cX z9Qz-wCjFs@u?lYZo4DN+6B7O-B(>wN#4+ddZy(shm&vjTK%CQda!oiI3&#@V?a zeYgDJaf&@Qyv`=u?W@tY$x915)c3oL4Odq<{}V)5F=h71!})Ip?w8|RZ3;A~G z*oBjOp_XLpc>H_nnXR?92al|TFWDEaH`T#z{cgJ~!nK%m?@pts)2D99_3qz$Y)FW} zsDGnxKaG2i%}aN{yxei~Y?i)*b6*;#j7y!XkE^zOyB=JglN z?tVM4?U?J?J14AckD;eB0=-rVz-mEfYn9EBTWOOe*7XnjAr~Cl;`PH!st|s|UEk-M zOw;{*qajXZ#r_BGUI<C+o2=&i7( z9fxqu`g^ly&;Om)lrU-jyKQ15axEsP+cRG7G7gv{RQ&wfW%wXTQ zCgkD|;+RG7`E+7|TfpWJd9dfexM|0#kvAp|f9|o>Pd_{R$gXaUne*mS{R`z8#v`Bm zY>zK9eawqFZkzJ`a!J&!X9MFA{n*u2j&?im+MDXkp8@-y8id-Si&8*-TkWCU=L0g} zKIz@SXvpM#=mXRz``3`{U-$~#e6Aa`a(tIAq_xHT+tl1*!NbvveY^f}p!i}#m|+Jd z#iM!8iiPWs_v;1U_tmw3d8=nQ1lrbYYwgO^{*~hWa=l}!PR=T)H*3G9+1gX9;vSuZ zxtSSc@J%NKwtqNHCWJ1uNLeV}SZ##$f0aIL`)^4BQb?B*$_o`ihO)F*tS zc4Nn;fwryl<)wYM_pj93rFm4u%mSX*+VCxVb#6ZN{p%o6jsZ3~o>)@tM27CnDyUVnP9NP!S?N#sgC;>*oQiws<(DNxo?ejmRT132x)cvBmL^_ z+sN+E51@=6?*PLhHmt}=v)VD^Dtj%)RwVP!=umd zhM2CoP^k;+!G4mN?)OdR<8BCD3_thg$5hJ>waCEjAGgWUX%B?uRB;wUAdYQ zxQza@U=`~63M6aZ`kou{g5esW3&1{B$V`tt{Jp#`DLiq0#ow>l)+?t_T}>j$-y9F; zw0+r6kM3L3Ih1BRx)SW#cOE0KK~%wR`sDebI^fed%jkl-7FPd~rP1yfWX!bmwXM5mDst16Ce) zhyVo7?}2&e>U!6QB3oruvE@L|-L6zcy5cF>5O~%;1sARi#v4Z(eEfXWVT0Fsvu$P< z=Sx1D0H*i<`CB*=c~)%>B#pPl-)(ggZP)&L$1hb0RyhP>#jwMDl*48NI%WmMeqV3A zH^3mb7Kx4RP}27nPab@6?s$_PO{PN&7@6Cz|BYKR4fhV(1~xy~vV=>wO8nctHOqd? z1kwDT&d)zU^jeTxLi_q-;lXPR-Q+fD?bc_k;g?=m)Ywl zeXC3^w$2v*v)}3Aa80uS=`($c9?TbPYUVVw=|UXx;PL=Rc16naK^#8kXh#r;|Sa@j=?eieLVn-?libbJV5d+q(!tQ=J3D z2ZCC=hT|i*#Xh$;{MjFwmF;y#!QD}vF|c%}+(+t=Q*goV&9d#5BP-YW7l)7a-e9!2 zc4Y_OKd0jt{7fh7DeX+>8G{%2rbauX`O@JN9;1Wa!BepA7j>%UC|JghS@t8PXiLw{8kER5$e2)>flxURb0M^@i1ip6H<8aL#b9wDdh{(aQUcI_VBG;-9puZL1?+E#i(-lHxA zcT3D+e|onEidb`t)w}*B?Qso)Xtk~BT_eYe~v((&usTK=Hx}f*aOB`L} z;XcjE*u4%f;{HIp249ivTYG1;x!EIE=R{&J&_XBdG>o;$etY0ax@ps|6|FFT+l@~2 zf^UjYpi|aU1=iMFJV=4qSyMxdFU+&3w_f{BwnYJYm4E|Vi&*o+YQ^!@zB7J-l*Rmg z)kXjM>bed1zA7=Xnz)_$_hibX4ZmeKT8gcUeNZ z$5uMOCui98=(Wc~PSI!g;kF!e=vk2yr>}eIdT8{a)yHfEr&$|&&9tJ|M} z3f<^G4>)}pK)$_b5()NSpFDHC$;<%fz4qvorTxrhmJzFBBk4?>*PT0mxyM9i|8d7K zHWa6qSA9$$;rKYz#=8jvyz37K+t0a++k$I_WVv;#KXYP}J!nGRm;QF<)L$F!#Lpc` zHz>_E`4H;6^~zJTglxuj;N>ZLaifTH@i}ECv&G4D@HyGjih64BS3U=!O2}G}-#TZMb|`SeaPbYDgNGhGE53E#H1I|0y=|P;5rt`cL>YdRK6LX?NreU+f=o*c;1b z2OhOp*?oYj^4cfCuiv>vj9uH#Id3pGi+q=@^);G$=O4IA@kdgCZaMUCfnTfMmTf28V?j~%uF@3#MecJ}d-z9X%LTgOAB)DxHTb(O%|9-#5QvZ(U|?NOP4qO|^*WO^sKN zEs9_6=XH{;@WO*_qz3r-sfS1QpTQei{}lk=g)ci%G@`e5&_?(mSLfco?KvH3&&jB~o7AA&_g8HB*Koazq=0>h-SMn>-(=*`-=}R5_K$WK={sHa zHBP>o(+NSIC#WjWMRfR(r@^0sRI~aL;Y}0ICdH?ucnni&r~MJiJu(${=C!L;r}$974Cxk+uB>tT;0?@^cb+&qrL@ z%D$-*X8yqqQ1-c(7cjzg9{e3qVI0TuGjIH_4(Kb)E(Un?=JO2-f-U@8c9|vp)qUmV z=V;&j8Rwq#ndMHV7Xl7Ro%a+hchw0L4;vEB&0XztaQbD%_1Al0|G>QY0sYpY`whA} zobAU#9DP0$D^zm8qE$8jPrL|RNfV0x{*Ijn>*-8%TJDI6{l3;UYsZN#7t(yqK24Pc zUUoIRSgD^M@AVIt6dN4=s99?JM zxU6>L)Dm}}-QO)&{oidjSnjggOY(gMx~r8e|IPE;vp30h*UfBVlhMY<5A4iNiaK^r z{7hz=y8NG$*KsMbd97l|wrlFJ8P^)q59#c{%e>QT#QV)ZNbwyzH-y ze*F#|^hgan5%%g%XEPfE@moc&xgWC+t#PMCRWT z^&-;!Aj#g<@#8VNfdA$C5uDHcTdCU%R=hZt>1ccv!Ysa_8?i<=o40cRXu%zz*z5qg zo949t)eJcJ#uRRio@%C?V70w9v12RC#|!UKZSiaRI%)PiuJmHtI@7I>iK&0-JvX(y zB+Alz|0FB@!9Bw}?TU*OXp%thF_)@8wp>qIAPBYv`9997=!&19mNrtp!Ui5g!B=$s zcLt^H$;zMK*BYDOb1LWaV#)idhdsf94HGwJbD2R)&shX7WOx2KcWld!^VgYo6Ybcm zct-=ksky3=yU1hrA5#5WW+FacT7`BCbv|I!uY65A{L5s&%fN=q+g9v_QAlWo(}~qt zSvNknfBiIms(F`w%7(jp2QnxI$4VbrKaTw->?}&{`*M6=wEw<)TR#L|EXn^IgB$0;^WxH}9BJA+L1yEc^JN@X!Ti)YR_wpnVq?=b$E&QSwCZ z(=YzXVBUX`y0)!{dGgxUuff3Nk~Um(6n`X9<6m~b6i_s7aw>_O{{f< zdp_<1Y|OV_V1GFh**km;|M6VY5BZL@o4R!S=Q90vKal^f$HLQlr)U#4`mPOs*xm=D zn?>R33hT#*m@8?Jzj;ZzMaTE0=i5QNlhB=C9TvoiB4J*|^yk$aGfVT+^X}V>4>jBF zOov@eck}6s^=f_BXX$(W7qUAf=BxiVaQ*VUi9eCi3wc3ZzbqzN@x`vE4m=3S-#EWd z8|dHh%zwFpblm{sHf4IzoMb4zd*pV3_3EzCe@E|M=&~Lo%1@}Z2EZh0=$`bpOUl?b z019=EO*TputvzX24KB=EAJ!F-H3QHEZ@QLUt8S|pn?ksHl&4#r*mZ@H=W(Fxcobr2 zugxgSJm;deq6avOfvBmGzE0f@h z>m}pwzGj*mp{_S)e0Vwdyo-V=>%VzmjnxL-+T_0jpn=8q%hs*YG~G$Yk8iaNOmf`& zJMb~`6fM4U#jOvqrN?fmwtYV8`J2c%PGkSdy7U0tWUPy-dQQU3V2ma__XM zMgN(;Q09JLujzW(5waaw2Xb)daMRk}Cis;Qn)lfGPJx>x0w@9?lgn6E@wRG0xrbG%N*c5#Gl->qa-Rg zSMLt8>V@I!{HL&(>otX47Y_!O?%dMh`~KDu9kZ<7JOOg(c`>#pQ0?Xt#aLP9zp6Dv z-aIB9ynR=Onq;;tHg2pAHIDy&KQ&wZstMhJ4D?~M+uS5^cbd+gRFUX>o7L%m&Z2joazFm|v>N!vbcGI}xA ztYcl@(adKO_eYXfNeZB->@cU=q=Bv5v>wfyUS8O`HJ6^}}tHn6(i?K=3^quq{uqWfX=dv=4OYVtGH+g2gdUcIA&fNW- z^dOIZe*qU_M4{8&Hbkd^givEAVD7T;t+QZAT9AqE z6ngf%%CIoTIFWnS{e5g^viHJv`&Ng)x&qIyyebdSyKvB{LjQ;O%1WE~Q*}*T@3$3C zgZ>?ldSXM?!#+9I`sJlP((&xBMv>3`;TcEvuFMljCgi|hCTD$OPmJ9O?l!rLzRLfJdo=lSAS29H^OWx`ITt9ALD|GeM>Q0-D$t>*@3CwRb{r;BKsp( zJ$g(sS3Y=qjTLhD_KN$<3?dUYu0j_8GN#C7$W2ej??Y{&^uCR!9E0S^_6NETgx|WK z6?*HtSJCO$rMJHu-#Y$dY#B2D_56yt6|o=mAXcYtuh^P9mUWYeZfRI^ac{O2*SwUU zg4{5Uw2JK-S6H`4TkSaM5&8z6ziJ$unQAiv6J~jjvAYpTC6}ftp=qbK7@P)|7Q34S7hF=e>yKx?nDmDP?%r0Os@~x* zERfy{vWI}Gz6Q6qOw$LDWhdh5E)`9``ahD+GpNb7i`D@G2?zudq)6x?ROuyw(0dI{ z1VuzZntakr=)Fk~RRW<&QB;Zr6p>ydT@gj;O=#gd@4iZ~|_Q5+Oc<5&GMFf}-ciP<}D`Vyu8_ zcU7{0*LmQqEC!%W;G3>Gg(6_-lj>2_x6i!)snj^!(mE1!|7-WGn%wI>KFJEd61LFE zTebtW3ePWFc}{ikyPs(gQz-{hKoJ{5a^;)njo|CUT0HwoIt$Hob|>jR=Rs=;K@!^D zfyUqVCWxDeI~m;2X4~wS675~iRF_~|;gLrFB%l06m?;WEkCheHi&47PD1s_#k-~cx zZV=C6mNv9V$XC*)e*BMGZWHH3z0pCr1-2Jv_>_AYTmG>#mFx5|l@je&n|l7R z;k#>x854Y3Tr-pB4!nU_O>;o}*)HS+ zXef&@wQ!NP5zp&jSz?7H1|E(+s;i2O-U#1w3;mOjU|wxu zb3c>C*F`Mrf+S2^k!kVqi~UgiAggt%19^7Aq*|Yx6DUSwpY9!kAgiPF^Ax@_@Z~ZW z!>#EFm8xtb-#eIRT7L)=wAC0DfMqrIi)kw1W16||jyL2L zc+akdjkvQLXF`}X2$lOV_63iM2&8@aXW2sAMrR>xb{P_#8a(iOnpi_0xApOmmM#9C zxkhfz#U)<3B}5%;h5fmyk7uB3-2jtcqIhJ9}tKG%1_JKe^@TkjSbmV!@fJ5?oqzyH^U99HDX_U`2 zB$YHhQ*S5GKsCKcE-?b59ZWz)eyD$K#aXASth?^7FI};(&(f-{o=Nk4ekB51SZ;MB z<)kQa3ojg8uKl z65eDI9xbZ~3w&g+BA9S7Hq&<|EM3gTAYBb7NkS*e7em$FrieIdv=bXIkj z+1FP3s3l}jn}nF_^K~0?tM=i%mdd1&eG7K(;9}}E!w(BLczAj-Rt@t|jjEM}k2|ts zv4HUE4L?ek>SRht>qROsTW9wOqmTc^s{GdWC>=J$-A!u|(b{`f*4!G=KAGA8B)vzw z)aII7rC+-%>S7BxP8o~4<^9Ijht-q0(ved_Ni_)H!`L-=2-IVNqo66fmMQ-V7ftVH zxM+;^x)CP$xv=~Xf6Y&S3ck~gIDKN&w?FKzt>@fH$pGja)DB17S>b}>4Tu6yk_E}e z8QytY7X7|UylxqTL&hQn&fyn>!tTj{2lIkw{=y0pODU?3L86Cp1byw8Znj$M_fe^7 z&~jf7*x&AAxzI04DH5!t3;Gt=6wJiQ<3;+KALs95A22uWUM|;?pGq&ShSC?GF}csJ zu~_h~|H-4N!8v;4L1BNcN!Vqrhnu+u<~v1F=Y4&V{vtgm3%a?_mLC&z&EQiM&jqqh zUmKgidJMsQ5(@YaFhsmB8vWV*Sk4AgiXTbWX~&aRz1vbmWpxRMa5vjimAqqh$DUJk zvq}TYQP37I!}aa_{8g5#UgO$a_G1Yi=m{sc?pB;=h;$wNI-L6anV@90>CAnBgHFi7 zQbbfzE$zyRqpBO7BZO5{feDtf-i^ANY>w_>@6n3Y0lU9M-tg$XbA$9ai9Y-Fe*l{w z_r9MPQVSxk;T3_-fdkj(@J}>18}-Ula9g<^{Z+b;n5K#*5T9gY)Yf9$ z`Q$`0b5jV@Ax{u}vJhlMdUBg!GhE{$&MvE8w>tv6#S%H=hgZ*8q!t2 ziMBUACf)ClZ(ep#0WC`+T|7(m>&CF&aI1mI$t;D6#fxUi_bpdcoDZ_og>pkZzQ1$U zzF$K%U=j+bO?Q$Vrnc*mohJKFw^yxqEo_J6nV4XacTZv*1LL*#*aooy@0?|OgKtwq z;8{72()7iG3=RYS&-i#(VCUj{)VUvw8m*M{M$7BOjhsU|o-pH%a;Ka!7slKe<-cJP z-tgCpmx5+gKmMy?(U7+spwdv6p|!~qiTm7}(RHa~3WS@?4qHV?tm(I9n??=mN12-n zGjl5Pel()vF|FPgbLlDJrC3N-{p>&v75J7f72KpTnawjTtWG*=r-Os-sMLQ)*HH- zA!Erv3u4Uf$y}6r6SiAi?{t?1HRg--0MINERWi_y&_v8r$61`!IyPbkP*2ysJA9n=rCK?)HjRO14{>31 zz6>@!>@06=Br1FvIK`Na52GOA%A4a;0otRmlh-UzS-_jf*0rGxklG z+EEkir?4WrSC;|R6KWum>)D6%YefST?Bq)qwYA*z=~51={buV({2!J+VyfEWEz6kR z{_!b%J8=TM{R->1W)Nqr@HJfB=yf zM)7pAa!zu0K9=uE$?b$jK>h<*X<5+P0#+m7TjbL=E25_PO`%CYeInagfSVl<(0$v* zP`p=fgtQi@KcaHI;=wJ$>@i?cg@i{8INtfV7}L3T$Hwl#cfMMLNO8ZeP5Dnc&6y&N z3qQ(*au};kzVgdn=U!QQr>*`?=yUXXY6)5#fN}sA65(uX zkY^}|yhBebr$ZsyYozv%Dt_k`3@QjY%)#bEDMe35x^N-(*$pH13mVnph2W3Q<~OHD z9~HT}-n`r55QiHW#;ZxnYG=k==wz=Kf*10i_0Se0QMBqSA#WNAJ^YKe65CZL1>8Zd zpACdMe%oq)9A)JZnmbO;>U~32nob?GGQ`q<1+2W|W|d3~WH~I+F^cJjjzK&AwG19%@AdP9L9{1`p%qf-j|i( zx$HdCU+yogOsX8lZOUq;r5GMG2V`Q}w4tpjjsjoyC`fDO@3DQ|A@O0#u@$tJv0~f4 zv++j^7pbSZlz(LXQz=+|NSQxMcFgi+|6Yb&%e-&Eb64R5cWQ$jw+0MMMuth8`S&-4 zz4Kp!g_-$!eDY&Lw*^uiy%v;z&BsT{P&xO>Ensecam5%l1K-l2J0BSCK9XSz5f9oY z&#kBE0h-#Rt5pq9wOr|PT-*5YX1g`D4ZAB%K_rn&SA@cfMxy9 z&)941P?;y5`Zxnz4XM(Nw#ew0IAactd?p*b&xz(4_hdvs{o>A@X%?y_@ZhKRI3^Ty zo$6C0aLjcPG`q1}am->Ekc*$ID-r`|u}W1U zTX-BY;^gZa57Dx~ZyFT5rYn8sS+hn#y4Dnrj% zI{cRRuWHK5%jV`?h0?Q{itA-ylO~--mG^G9raseg)rQEplV@Uz8)|x+7h@B&s>~hh zXX5yz-6zAjv4Rd_8}1FH@)}LS8Kty9f`O?$L{;9Zie#=`>lzaDhrWu=Lq;KTh?m}= zxm{wwh=68Q4J?YyA7HbNWcOJkFlMjuS zOZ{R$9bte*Fg9i(ha{)sphr>()`CdWo`=m=0fvsuJ@b%jN|9VtE=(!=6!mPP7V~)a z0e>d%KWA!1=7_I*;ixYL_eA-Fchzd-47c{!#X4Wq4j@K-ds~Njw>RIf3}4_T*JPY& zR_E?52aO_Z1n8C|BUh4{Pi&2K0LQds8a|o<7i}PuptpBKc}^k>lB}-I@#LXuC=&*A6ptZQ8ocZBH{w3A2&Y>TKK-_{BOC3a3G%jOQJihbi05( zP@!$91?b1hr`}OfEp=BUqJytcIrv3PuUPA*VF;Gy#R*Gp?&c@K`h&?uM4Xr4_oEvPS)I3an~&GusUDq|?|z&hnzdauz5Q zd^qc@+!=5kc_^W=6f?&+J&s;uyq)Xw{a1m9{3Gc5MZg#@O^U$I-os7h@W+yYN3KBa?cdnBzh%25y@lWJ%|`8EQ=Kih{{uAIGm`X82lIBN zbaaRBQM-l+d;AYD1j+f6QL<1cM&oP?y+re)5V@72XDl-$Kc1dp6s2q=0S00tlTVNN zzIr)qgQq-t#S%J@rB+7I8AGcjJD@h%~yQaem&ayqWAK$?++IdQTFL|l=67K zY)ncFV}u|2Niw1KG~QUPA0+Lu&M&PQW5-O8=yC?pKZ0o?An}OXlI!Y+#J8y*Z!nBKo4D48Km9KEeXpM91@Qo)b?1F2dd81BuFbq$qpv=?TV=aH(lduVD?j)QECxmG+;O~AsGUBnS1!%`zFx;(`jaVouMLO(hp$z8P@G= zt@7pJd3x75>u=Gh&S_A5%J~Hwo2TLD^`^y4V4rCx8Y>mFui)k%{(508DrnOH*WGN{ zG~^9>#_}Z|FRW~W-WRDW(7jMJl}4^~sdmC=@U=aD$vXo7qU(2^pA2zw0%)6%R33J-;lpX8aIV6=u`8_>58wAjq4#<}jf^pE;6 zesLeDuG$Yo!ZudQ3F45gu_sIG0kK$JeNeAG0u!fU|Exr+lkD%BvYSJr}0%lHe+ zU#8|I%qBpiTx%!^i;@Ie{2>XajQN7u1q**{5lcjhI1<9`l< zyIlP-r&?XgndMFg7VNwd3*7o#L|f523QM)IHjH~Oo-n@OXIotKRc6^N$QsD<4UT+0 z9bsK~KF@pYValN=D~`9GwCu<>#=z9%+92RQJTa*8RFKf+8X9=4+QY#u3IF$$Our}O zU6ILHR$KOw+~oPzA@V#|g-B~iUA^rqp730Yd45{Y5Xte4PbXw@`+zcr?YYR*H!X64 z`rf-fl5Z;3?f+RQ+wFVHsGdxK0u>{buFLJytv~L6#oj7nFAYL95M&A_4<-_I1M2+0 zd0jjDm~4$p!BAPf92n*|KZ7U#^cP0U`?*J^c+{k5?o_uNA#)*-t{Ja;low_l-%7>i z|GNigtJ6NJkC>=Yk9jBvFI$2uNd|84T-T6<;L0|I@@5aBiF{YOCsCgd2Tn1w;fTH0 zuvz5aZ>DcVra(VC&iO<(*(i|9!RT-e*T1rgzvWf*BRmpL;}qAVny7fWRwvH_#GOB- zb69M)cJOIS14C55VbZcsRV7*6ivDO19z^hr<5dUea+A3rR0>P~L-772Jj`ws?fNQ%1b#Gyw!3fso?#J{CZ90kcVWRhIr4o<`yD2<|8{ej-b{KASDjP^U5@G z<4q62(%EI;IXl(zu~Dahb4s2BR9y1R$qffQ7a4-KwK43fU3XQlqk7A4l93LZAQ}|0 zN6po~IgNNvvWnX9vD9GEuGM`i*bMp72uZS36|??rVZL{W8QSpFWg;mYj@-+cfR_xj z?sQVDIg=3C>qL1I|BeT zCvInO*yB>7q=k4-mJd4rF!e0=VyaXfbLO1~?7> zGp!mvxqoT-Vpz-92YQi-g&T^+80)N+yl5U$i$hh9#I~g#?BX7L`5N4zHJUi2y2wt$ z;I;8y`*A^ZM%dyl?P0%ma9vBo1^-JqLHw!+=84Q9O|Q=a$>>Ka!ieJ?q?k~JkgKHp>gS`)4Eir9KX zC2Dsd@u4nmW`>r`&sbb@4>TtTlf^B*3RydGqe`@q=~GDQO~b z#bwJX!&oPGAL`XVq>090F8a=Dp>9Tir{r#+!bp6)m>jfI!Pu#Mf$x?2@x)S1`Ri=u z;r#?yVQ$)SZIs88(oLcN07nX=Z`oISEE-cdo>g-@M6KTNW*PS5e`ZDlTe`>0f?&|K z){9>=6P9`qpOIMpt!r~^gp+N<^Fu+6Hqk1pk_=>`R_e(bYhzJa39e2&dVprMoDYkU zocw>Cuffce;sTbwJB&UF0#TDyNKPJ%-KSe~A7)jQR@eB=5pl4U;~q2GlO`|ecOCRwJ%p}T?rbK%asnKqHIy;A1U1y0Xy?zn+v zNfQlC3B&sA2O<*R$gQxs*?V;5_-CV%%e!A)?PQ(MQ{79o3dO} zTWVQ2Mut3US!8(eb?yQG@+d&mZs>*jfJJsYLSOf(+P2QZ(_lf zVu_TX`nkW(R3S}36P$~f-!{SH$LRh2&T!BPrmg6k?)Sr;8odA=Zp$ zA8PPdUZ^(i<9^A`Pm9JP(JH20KL>R(Vp<)B4_?dI5B4-?{CZ#);1BA^{CLBW_WhF+ z=66QZ6a12Nz>}r#86`DrSOFE>)j0kB$6;hGuZgBwt2*j$JtZ(uY)6nr%^;%gUS#S% z@iTnb423=+<#E(UdP)g z-~_#^Cuw^Ox4J#ZFs(364MhTLhZa`pk^<>PL;u&0SmM?f9Pr6pd*7W%y<9;%j|9aJ zrJCA)j`<)!B4u8ffh(QYV9&0U;c59h7Un+?zgC8C@kbNWc^(WfB)}pCM3VIIMRU+6 zKEQB85S{S$0Cj6n^Y=D}hpV1C$$&fwaYtzj6NQT5cz+i8x1Egu?g*=N3~9p6tURv+ zuonhYePUX}*n7*Kh2{Uono=k^#-NhcozI0YJt>W=m2cj|My_cwK-Z)_Bh)+1>0ce| zGuy2hFn^bv$=mnV#`MmD_18mKZ{2_sYU}m3;t?_Y|S2Y(CcD5GZSW1iXYXLJu2_HSr+e@1N}2~J)@!YOK!{*w)Oc%U_H%ay4JwSE!rP;Q{6%85@AAV+k4p2` zDYx%^@aI-S{5;beZE7NEew*EW==y13;0Joc3zJ#|sadJ*s6?J9&?tVxWjs7V*7l9G zW|_3kE=qbjd<2>Z45qGwqmX7(xpr168#5f?0ZPc9(g~*ENH$#2vkIfZi|yXB6KfaC z#{LFhM~-Y*k7$|%#f;LhzK`YHNg{S~vaE`XLB8S7i_fYnPEabb0+OO%$)i?X# zcIvarrq6+CMX#iY9K+a@XzzMr2Xc9dqU z*_E=(EK|hY`3gcSd%&CBYo?o6Lcdqvz3ab5EqKPzDX?6K_{!(S23R&m&mY%2HYtqL zmGfwo$tP^ZwWpT5EX2ny60+GW0divLnF7CcKUF7A$8&6DlS!Cv#0~G_o*u#vRPS=! zUAoK+(J*JY%Oc|;_Sf5@Q(9)$A}mnRpF^JWK=NV}vl=H{Oyy$Vs_7RMOkFs+>(AwS zq-tY{+90gXL;>#Sq29W#|6hAovU+%#$2p0lbz<0V3>>t2Cr{OUZAE_PUYFgo!XfJ^Mi%YYEa~i? zVxiY$UpJ+w$x+84H_teHA}_%;sbZE%NiK)7Bk-h}hfv2t_(8IRZ}#*`nwf*r`x@qo zN^2!AtW2jfoxlStMferMb^w}xd6BBDqMNo36Bpi^!p;j+%K0DEO6Ep6?b7C_kOr>8TJQ3d3{X!q331QziWpl zA2nwP?Hwdzj=R3P8KxZa^?o?na`SQmgd_OR%a@`IMc)(-$sVFId--EHpK3ycIOd2? zE;!QEuetesAXxn&x0upw$D?v-)pK@lXrpsxcMAE3GcPQrO2T{yE4YX}M7lpUUn#~f zu{cIzR3xHUO>S{#<-l41A30zpXkd-{%yuHweB+RJV!rTyPdQIe7e+=K7Y?$=&?xyf zi}hle@>6qJ?wsAI$aZd?v7c81$EWoRQIcw(msgTs*RsVY^5n=co^?`iE_Dn*KC~f4|I;V=ST)M?_z4W!DDgQY$C`S z)bY{uDIyiAPlguj(4)dDT?vPmzO9KaZPjLWcY=dsHkxS%jwK?BwS;-b9?fW&-lpSE zI73{DRNv@c^+RuUEcIldQ=8%~vL1F8r9KEK?s7087;S5Uei$wcYXeD;hA-=hn|X#| zNXwl?%?GI7{Q4dT+PnSqwP4>HZ*c-_$0DBvbW#dNKOI=AuK#1&l@W`T?{+EP8|@hr z4)E0Q`d0uy2jsO+(czN^)=(BrjrGyurSkt$f1ks|z-~?#0?K>e7dNYi{uj&o)lPG~ z82Ak^SS0rs^6otJX$)(`{kd-1L9Dqa&94f_P`>v=iTOf*YK!!?MY>ZZs32KyF_(h-albj&)*0Kbg#nWyYae>@4|uRFj?6SijNT&Dh{;cs zz(Bsk6UuOztD5@r@jQQuj@y6>^RT1F!OOdjZhb{py5zUN$xW@4#eUu)onE8K9(_v> zxA5$?PMSzPxtznCD}yXjy{vBj-snjl!R2hxBvHjB99!wSUVv<mqfWzb4ZHRJC)K7x_CiVgEOmb0bafS|&85kjRAv1w1W zBA!JQ^;A_|7gz1O?(1YcUeU%H--qpNb+RmavfS%{OuYzP4&ezCDYqKT#bPIK!(C5VV-Cvoe27LhCQa(msU=A7W&4?J z)_$Rix62>g2cDb1#?S)T*Ab!YBkKcFc>9E#OHiE2>YtgT5!wUXg|Lk<*N!*ZJrq81LYsTbe)~f#ieNXJZ?ba;c zr0!+{o6LYJG&}VppqARzVQBhJn{pRpVKGY<<_F)|DWL?JcRbC2204B*1`29=DYpEk z--|>+DA81;ig=LV?z45}`oi4?8=K@FZD9pN+Agdu}i znr*BrwKC;asJYs@4xai_Z9EKS&D|%VH$BF}?u77kgAKz|YAtQiYR>9mp-pNm`g{DJ zb+v0!hI`$6^!T2mXWL;1*VVD2f*=f&7sF=BN=#WmvkP|uooVweR6f^w>9(%kte|OZ zvrtv?oAtbRx*s+${bWjjRq--bBn{t3|a+Fg2A0RxbcKrhLdFMdDXu+4Tb|J9W&8Sy> zA2xq<;K%yUOKcTXfj{zr@iajvA25He@$utpt4{js^60fZPUzwFAOFL$YFHm!$?Ma# z`7Hlir(X~3G2?lPrc<2%?s~lGqr$)*6o++?L$BKTx@yar@U0Zt7>Hc{kVg@5{ba{jMCt+HkqM+TaO;Gue8( zQH+u0azaSHc(-|A?b>2W`Ql^#a#FD>bWgDstR{pZ8m0cq&rMuCCtSqVCO0%>V;^h} z;FZuaN-UDvLOWBOq;K(}^g@;a&1|5}Tw$2leOmMmW_I)3fP z7^*Y1xM15<;W|>EiX>krBFzI+`oRr%Fc2M&FRWy32DqakB9G`4mIdP|KxEp5I|l2; zvrj{bq=0dTh?@ao!q>8=3=LdX2!37Jik(gJ@UYCg@W$n5<+*GtDaYItZBE}RdQdN# zinn?foYJsq{*jWt@K_D>sPiS1*id_S&$;inUsVvz-2PJMQtSyPA?*D~wH5~;`4JRl zy{cJDP;gM=6b@TkaYHaPC?F|}IITu!NNl|gLij}TBZWy2RJ(6a2>f$7KRiI;-v6>*b{lAx3VDHE`QTrKn9l;o*_yg7N z%%U@|=n{;C@pEZX|Mud3{mhjZFYM4C8ni5y%l*1kwGdU`5b&MUC#*vvl7S17LX4&Y zErD+5)yDNoFiTG8Vzu}zzf+^LQ0wp}OT1S5LV6*$>65NX$*SvCE#l9`%ic1w?fC|P zk8-^?gS+iwM0>Ta)vSPwehcK(eH!bII$6!Q{NsgzP}Fnz8kb%JRyh3r-l}@bqYO!g z;jRT;;$w`nn8>mGzPUP||Fnd&vHH>84e#nAGXV*3WDONXE>p^|K+u#gUR!^4h;iW1 zHvWUiWgSWyykiC%>BVIS&A)MKe9o1i?ueGhm@ULTs&}>3h`S-hn}MQngcQiDC6{{; zY1q0#@CC}fJm%kIAI?jCQoD3Dasa7)o>-S}z&OtUAv4i~r^{|0^izwh1CGbv{J%h? zw@%$p+~ZvS157an)(fYkADEED+MOC5O#>Jt`&FVWRWffDW|y*LeRp$zoi|PslgZC} znoLA*71ymYulS>ibptNTMeT@g(bv!#e1a{OO)0viG98^OtR`;?tpc`4=AfVKcdZwJ zSp2*WTS=v$v*tQm`e9EGYCK7E8!v@=qD%yu33A@L{my+LuZ7heFDD+9#i;N1SJooF zN|;lvsr0!(ATq}wTWlYgJoLvHFtY4ZRKPk!;#YWwFkQNzM8s^#n>$^B$r!e1F4>1a zR*Xx^^>djxtUM}zR%+Y?&Cdv%-RZuS9Hbx8Bs!XR5FoU$8n!)~1$d#0?Es+4IPRf~ zTi}a8GdE51s@h86OqSiHKyiy)g+ohc0@#CcEQ7?!~X>w%2YjhnU36 z*&ZK@q>a(FJ|ejeXwd7IdD6fz0(;_Won~rVhW`QNpkusZ)S`G?iu#gV$9;uzQ+Caf z&O_vtX{wPa_**Dj($^(E@fe3>KlqYJ(+EpPu8^f=HbG~GVQYe1rJp5qCJ5?|sD#GF zsk;m;myLCPs{!-X$-h?JJ1DAP4jIxo+eM2M>rO}aU60@!`wvjF5~4w=@rZrOca4Ox zQ9a3HcxRAC(ZZL^H4mNH8AjI2TJr)n@b-eUzXNP5kuH)w&OVG@v&bE^4shEpsY)a^|Fl zo(aVI8V~B=CHDvxZ-5w zH;TA?y$Kdk40px$ps6EGNWpiuUiV22Y8VAK!WcGwt~q;UHKgN_)2M7y1D{)&U0jR| z9{Rh3e+gyiBkLLXDCgilf}}WFW6CG5FpE+tQi_9(2e>rGzYJlVcTYDy#%zs}D=|8KX4)kvo zz+WSJfedh}a0}xRXiMk{r%~!bUcRRLs62V`*L-tPoQOP;OWaZ0p}r5exjKSj0Y)4g zv;8kkwS#lmEu6Vu*@rtorJFmp$yY8`Y?eKl4hOSac&h#NU z|J@r6&uM@e2_mbT&bQqR#m1ctZX600Q|$+gD$&&SMm*B!0L0wzS6llk|MrZ>x00tt z`{5e<7Tx)f@O2QFpSLFmTxgI#SjRp^!#R{$XF4F7c1QRCA+TU8^fFv=JNh~=Xqi%$ zkP4aYSKFt2B}0w$P)C^)m*?v)Rtvm3`x@`3_4s@4$I0)$9`&U4VrdzzUStWHLW4PND=!>8xhOAxS)Hn=_*1bKgdWu+`so0taC(9 z$`92Y-I1yLufZuBr0X;}Vvu7xE^>QWq+cvHMP27-D) z8sD$lU2pQ5<2tX1yfpW)6q^|1-Zae)VnnSogl(Bx;NbO|^|mr$sp5O&z~}gT6OlV; zwLNoru5@)3&{J@2nSDvGL5(?^=NDk687JyX!A3({m&84<7d7sr{9W3JCEK?hd!9cw zMsfKiu!DH-eWAA4{9P}6YteGQ*00deS#H@m%u6x~eX^s+k2YIyaSmP6_A`3HI)!DV z=^YzfzEeq+GP7Y+m{DfnYn(vPxZq2I^&ZG_~|klGZ3Li z&&g({dJfBOnR^HpE)kng8aSmGQ&JxE4;W)#3xydEb0ztzLzM*8YqUGGnG3i5R9mFF z1+ zo~0}W{`RBZP;MP8a%NjG64FkTaYF*@2iAmy)U`|)^5rsMqrZ-6jKsNETYc2SFZskI z76#rA2nfilstre#`Y*nWC?hh%4KML6rBRkORZS>B;K=yQyPDz zw}y|^`7Ec-_3PqocB<8((&#*W677?6jd9S!1d2H5E;l`Hg0Z$sL=l*(^17Y>I0TSi zQ#udA-9$=TM#ZKR^~9s!|<@IF#j8xB$9i#&g1cJJgcf zvIVyDj7Cq?)Pl~lS}8dud{-Yd0ie^XfU$;?rN4YL$79n`;(Q{Z9Z_u?e?L-Q^XD@Z zc0KVj>g+0-9a8+4{K=GD%fo2H3+bC(N&R;rXRw%M8Je}F5ltkE;g)oV;;9T+UJL~Y z9CrQ)0;#xGVfqG=dU?oc-%yWy`L)sJGi=9WqijVOVw0+4nzA& zwx|L}*(ipy+%eGO;E)B@kMWG-{>S6GM`gdwb>)TWu_2F^v+T(U-ezJ(jMxQ^$HzVS z23?6l8@Kk*Wp;Gmq*a2M@>ksjj#fG?5C$SNyChY0yBMvvtLjH=j}#6n zxPFMY7Npvz*<8M+!KXc`>q+8i4~Cy!x|nE&y14gB{r7EnQYNx=g~v?n`!QeZ4d{d3jd zo$>SU#%J@yi}Oot^c5W7$npKaD7$eu4j$d}lYrFFw0Dor&Zo@bw}}ItO~s*$W!2-u z7@`>9kLPj@J1^Z}!fN4o*31E7+@A24xt#T2i%`wkl;!1$lxgNsy&aPV1zZ&!zd}5H z!fAs!v1zB|Am^?pqg=;k=4-oT-X_H#oE}^_N7;yo5q}VePL(|mY4A~<^E?rzZ*F)a z-eMOB?OW4&78ii@#m+E))j{vp(oYjza%1T`Yn@Lpv8tR)e-Wzfw^m~o*7kBOTo%hb z7{x<3`D|hYB2d8k(mksDqa7YtPMTfn>Y{s{621aA$Og#vMU(~lm#(K;gSj5mx=Z5+ zv6eT{4?g{q9cn+GZn<9eN*Tm8oL$yhT3nA*M2q{)$X>2IGAAKLPDV$_yN1u9pPnl> ztj7aQ`)3}%!DlQ?w+w5R7Et56AH?%bgm0?zPjlbZ+vc%puufjz#hc1F3!Ax9qUI+_8n+(ENAJGB$IL@a<_p%ReUwE}PdA zuff*5ns%55(JJ%e^}#`gHLKs8vYtsfw(a}fnK;pLf93uMuDZr8q0SyXu8t8Bu944N z`Q&pDX)3m`l4M*wEePkDa$$8B@CrRVEj=EP?Zf8${0~6zZP$?Nqu2iIzAJbZ1|9}4 z9~A!k{JHAx7(KysKB7A$f1hFinh;j5%#k?k^_%DxXfcs%Ec^i-6TNy+zb87K?EB3I z1=LTy;+`oli$1xVk<@Wg#5VUvxCne5o6z%K-Y)xP0t+&CGVVn`3>}M*nI0y3l~BiP z43P(qe;z{FaUb`sANB0wlo+|pbPXdcC-UuQs;Y|tGvL%a{X^m!blxf)_Y`bA%y{jE zWBqksJ$QlY1TOkp%~QRT zg@;SCG2D|+#L=vNgo*tJp#1)TohI}6vUTuIwPG(r5f2NFs)Y@WIT7RAm+z1-K@ddV z6a}WL%U+~q(PsSzuqy`uAiR`T7!{qBC~6q<@jcR|oyF9nNT&lJ+@ znsM3}WFES4=d-`8nVPwt=vs2Vnl|CW|6xDrEpf5~-PyejJ0#6S2Ji_KH&rJ(v&iOqhM1wWt*aU_xG;J&wQ`ZcW-L5-!t~t9`_^ zO`O6C>*_c}rYNg;5Gb=o<`22|jff1Pq06`SHjU)WeoGXSaARKbzDKe2rm@9BL^`R1 z2&m0-I`gs!`7+lJwN(Xef+s#@oTc0S{*S63F{xz8~C(%4t7+P)sVkyo$>e=QP zIo+Y(hDRALm+78Jv~YoXQ=cI3@_F3dU5D!WYb;@DM2IY}>S{TP#x?!u*-&ZyR(`$H zMH4}r7n;J_7YA6Nu)HE3p$hV8?H@KjUCX{f`&!0>p&!4LJ!WO&xoOUnSulx)3b5#a zAdrQXJOKEk3#6fpo)4~vkvqNQI|)V7m1JiDm0vR($REg!GXqY9D6`!Xf_<>qlE&%9 zl{iL&`~UT}U3_GgkBFdwU4g$uSw%yE=UUsRaR$6FdIor*Y0<|e{vjULYeQmAJYM^~ zt^%Lm7~0!nBttgvu`U&q@Pc&asK~T`Vk?lrwhipf^urA_$biEtj@`pfd>U;iOw}!1 zbS=l%KEphp;~EH8U)T!|Br(>7kR;`#t@d8D5FGe)AB9H^ahNIgitm~wAv*#|5> z*phxTl|3ydbfw!UPe~ro7*iBTE?xksI!E6xBVIj^We49TlSr3zWHcL9Q|H)|)hCQl z3%}t2QK?TrW$O+_KU06NLO&Q$ZDcs}Gk9K2?IIZ06Ha6PY%%@qD`wVP&dw%G9qksb z%-Xb^nC)Efa)Mig+5~;R4D1U0eRimH?e#TgxseP(5#0iiszRC7o@PSl<=%>gU zUs>r16KBV%ERPR5vN(eMTK*6==V5$8XXvdf{!f?P9ltAYrb35|9G=m4cHc~npJq*o zw+7#+l?%ZBFIKSY-%l#dBN6F(cnHyimv8U%s%nff{$*WhqNJ2kj4iA-(?k2murv2H z4Yn=!2!pKXgj*3kER)FH z`k$d|LPKBq`>&SGEW1C4R%gB+vEX;vF7_~j`Pw0*tcc*ktOW}|>MRu&Mp`Q&AZqH8 z0fYja0qvwd&PD8+JgK8+V}Wom)6LqAG}X4wLDspnW=`=Nn+APM{83qVr`@z$b8*(y ztmX=~A~_}#>^a`(Mg2DSRk6)Y0L|RM*2&nyubNw!c22CrdI;ccSFVk|lRyKoK5db4 zT-tjSnDw?%rQ%VdFd@L{`V2SP15sSe_LgHfvqQXwe=@(a88Gy4M;Acv`6lG0S`KGm zo@aT5f~mOzjg%e9zV0-zp%Cf+Tfxb};sc%wA5>r@XXqGjW)ID>1Vzb|J-qXruFPb^ zIY)_+LaZr>F#zWYjQx-QP4a{C-s&Wf{M9uVjIk<9@CKKVp~ixUhZINN;f9 zEF;Ov)DGHPQ8U3OrPGa8BghJ_s+;*4n8h|Np-^Y0D+e#gYG>3zg=i1sqBV>1k5Q1W z%H~rsyLo^|ND8WO`;T31E`N42?yjV2PX^fOKCOJ2zsJJgAA_o?=#RhuM+aU6=F4z! z4`e3_;mAqP0+PrS)>#&2srgnJC=sVZ#u+Rm5h!jvX++J3obx!rp;Q(g&<>*?K;Q#g zUf~Ge49woJluY_kLbY#I8(}|6i`n5)gW9WUPEy6hPuOOHd2x>P&b!qo`AvwPKzxzY zdLf@j6@c?pcJka@Bwn!Bgumz)H#|#RH%QW^87Dug)A(S`8lO;rXY@(*4>k-$8kVJs zQ+gmeb%D8}BU30F8zjZnZ^f{MumwspNl~k6egOJpCg-2$auC|>Dku-g-6#htHKgz& z&I_IC?JUj&PxEu2Otfm*RD#`sHvd;6L z?Kf=yL6E2sf(9`|V(%^Xh!Io?Nfj=e*AAJdfjj;KP39hY+qSo6Ep>@&?e;rcz`i26;%b06`osZ=TKL zP9@}Y)BgboG%7K~_YN1KG=i~>od#aUP=}dh9>$AYnpyVWypT%t4-zR2ns02I!mr4j z<9rm=^FDiSBp*0v_0d+?l0j8O-n{_s1OSw-$8C)1@4ZN@C%jV)ajI^8#4c2nX<8Vv z(gY-!-yUpN_ScOM&R1O7`6No{-XK4LZT!U$KV9uTT`?V>bjcTK_*jzdJREB1$ZeLLW?9QfD{ROVb%rm8OX-5L}?9a$*@C)?Fitm>NT!ou1xoiM|Z}`!Grlo zyOzBPTDtX$^1Nwv5VxeeQHYMJ6>*-_ zoH#iaSL5P?4rVu~vogob1{wKe)iI0jr49kP9WsP@pv_PxysG^-fh2bMrvt(> zZ=eMY!E}2)VR?HM$z?%}o|RMgT6+Eg?DIHVB6#gj+xt;V^@<~0`qVv7az{orLlwkl zmWUqX8JjQXOA9QH=C_LFEU(;KUBdDjHXN{92*(a|L$WGRsnMq5xbNPvev9|&wG-<( z7FDBnUZK$|Z?W?^RCxXFC>SImth7h-UfW?p+nm#QUByP@wHbx{evR-Z<98Z}{C`AbLuIy4q zM=mCwF;hG7y65bIRHHY!(AkOg=#!l0K{h@}S%ITruYtKd-l$%_%v8V2_Ki5VE_vzb z{h8D3=57Lqd6~ekO*0KvrX5w*$M@jaW}6|p_3+Mq?A#|)JJ)1#ckD~ z6<0lmC{?n_bNZONp?{rlj0_0o=C(oi3{R_<6<^nJ=xc}mz4P~gMd;f?3s#6`GYt56 zld?84lGHNi1!yK#&-wH0;-7vLSdR~$FsFNjKoQ_|_!c6sKb4-QGx2Ja zNd^FM@x8Gn@FD9h`Qi`8B8jWs_qic~!a`GYs!0;*NI~D6qtp5yCUz#USy1^8cp%n$ z`E>X9of|8{&22#52MLrIkIZ*8DLU%19>t<8rT?X~!H5csagW$FmNme)Ua@+n z@W_<9EK0Cf;zWKIo)#-;C|P=t)_THvE4|Xgb7{prGdImDvx{c5ry&D-SwTaqM~L5y zpdh`)T9@?~p(XU<>u&stIz>PLwwpgR$uc2C@NJDXK`Mx+QgM&lpe`3j=Y7twfoXd!+4}U(G&J3>~0t#UF=ur!gR2 znG{#4*+&Xk0Gfr!R>sdDcmk~Tgdh#mGj zzn1qJfi(f-`DIV}hMU_IH8Vb?Vy0DPxaZE`^uAoNhwrVu=KW5-?i{#-P;xdKa8&+y z7g$Wl9|Tom4F&s5tI47(r+JG4xk_XGw$)-aX2gNI z;37o<@dMvv(k^G}8}%k}v9S2xS6r~F-M}$4Du(&r(|E%CwBzsPycMD34SweL@fWG4 zx=*6-{oFo`-_{LhSv-T0c{+7d4R=EGbc?LNo~DfkZ|IWu_M@%HH^nMeLo`py=|m;N zMZZF7uZk}@u%qAUcAQ}r9ogj8&Li8%PtW7yF8UK&VxQoO=c$tIkL96d4lPE=hCkw| z%^DRV5*mh+xmzU4!_>p1)tt%=rs2=W=HQG(_aNUl?OQlbOIY#dpc1c?# ze2Tl#@wwaU60emNjLmbs5f3aas zqU06-BwyOw2m!h|ePv9sN7!q9KXibhR4V;>`G4%g@;F<%#FC1i;Q%+GfFE=ok~S(P z0Ta>5@6KOcJ6r&#^~9@3X3u&wNcX*%OfUHE+s{Z>JKvlhQ1U!GhryWz(4bk8yJ%(Q zM?wW(4>lXJ8W5#t@^~#@>)o%#s_Ld7wM_qgSf+ulblR6@LLPi6BiBhptzWYBk_o5y z^8#ga4tgB+yOw6acpJ*^AP%l#O%NAuqseF>ynvQ-G zrf`&$N2_Y>GFI6JomVh5D@y003UvO}U@_+iN38M*Q-3JdgSl1Z@XEo88I@Vp6t^n6 z-?WO6WSmcS0HkSxW3>KwZ1OgNptFt{9m?R7y`I3gb;H`ZVpIxC8x4i7>J_p0R3N+~ z+r2K6a%FUA@J~Z7T@LPDZI5Ue&wweRLPaz{2SU>{L`03M@lhB%% zOs~oWIt{|rqjMzpS#scicLUpc-dq9CcK>)5G0OjN+(d!>!PtSgsuM;_-|f=EoHD5q z$rtwey}SqQH308eedo__Io!&(n3PE+WE{+HU2hqBXE&To4`A~;7)}0= zLV1Uqzh`!7Cs7iN?Z*LRi;hHUn7CA*bymM}$2wBxSB4z^3*qMVrtDs`PP$9Ui4viW zZ&L0@8cJFXC^1H-R}8}FvA~c*HB4@osr&k+B08k48@W)n3}dgHPJW_0NX*UX__Dll zl(m0!Z+>s|>~PP$B>1#UFeWN@@yrsEP5xpfL;+JdoYU%Y7_+k=)P^@tUT6IVk#2?S zlo*VgSe!jM-Q9NtZ-!bEiO#5)af{TRuliQP$>?Wfp@32`NJ!XHDc)_e7G}nk#XU^A_yc?lBmKok~@9|1dI4s<>U(EdOjRbjF~L^Np=c z-be!ZI8Zu&kGi6o=Ixc>arw{s;DC;;WUsET_FhWpm(fha37Yf~b&#F_B&k{8s+ouE z&7lN8S$&MqT9f9$(oK}_E*ts+oUKdYJjU=b%Xw@+dHts-kyf)0sd+0}=LE-q z+1?nhJsw9DdTh#DTEthEgWP!^B+1V$0TmVX0jA!|y}k3}^9jc8+qEq{(@1!J(AMa; z_0TljuY~79e|ZWUqel|owZ{DMinEZs-l~1|jl0oX^RE>u#>V0gIR7%0nnk@XePfmf z7T5o?F_^x^ms$omIK0r+6BG?(_L`Jotj=Xr(nyglx;U#40F%qEhdr6VIN(hZ( z+Ok91jt+;NIzpNCveK0lTf$5yaFBa9exe+@+QC3ZWdk?I&rB_Sd(Mq|(@!(6vy0hk zx#Uq*-O$aig+qq?IZRuqc|e9fq2``?)+}{yn=we}M{QXB@T3f_y)Yevhq5gebz$ZO zc;A(gWc~?SBKc|QX*e(?J^Ju+t7rO?C!0YRU-s^Reh8ez6eZ`rwYza~0feSOt~CwTL|`0-(tVw;juU7N^5bm{v&z*iVb zz`$Io%G-`P5FA=5XbBZ76((Mi;P6>cK-enM7&*jDe1P#6HD6)m3|iXdF+%S^3Vr-W z`35)Usig9RYm{38wZiP9a_PqU0rCmjoBe0`MKDVy!F`l#mY;1}M9>>5UGVVo7oQq7 z>&rVj8vFwO32@x%VzQv8Sl;ElVh@)X0ghu=af|Da1Bt4_djk55bkA+9d=u_z6$~QC zFDSLcN}ABwP+d=HQ`u>h>@pjppqEV7)c4^-o_7%d7DW~(EwCxte2nGQ3GFTD25~*UF6=}i zRi4@eh)M=!i#h$-!6v0Nin(Oe|0l+#WF>w zP{S8O!dGV|HR?tt?y{(!d? z!P`e?ab@BFw4{(mK|Y|*LCpDF08*kxxGx&CQcd0ds&iPl_$T4X?IHNq9UnYJ5KAAg zyRk~`|CVK{YPJzeh*(RBkm*RX?E*Mj<|=~x1d;W54`BvRd8OGJ`>_5V4t==fOmWkv z;Gy4|`)nd3P9HmdX?50zZpXaOr>QWI*z_r=NLl`6n?c8KO4uSKf7g1Ce|#8brPvg- z-sCc$$qBM`PI;7yFE-M1VV850$=G`vP?>peO2x-z-m}fHZyZAJLMdpZTZgtu7s1%~ z8#H~34q>=j-`&@NkIi;NFn8y7!ZN5S2OCZzB?5@Z-#G?_Jk3F$&i@Q;_X_8avv3UN z%KyMpE@H6O%DJ2p$&y`Dhnj2Quj=zO16zC+A4n)^7Kf&UeO+D`@_oQ0YS9GFMeCOG z8p3=Gd4~Y*=^#5Zq?J+Prni5WqKccOeZRfnOj&y4wnt}t-cH2P>3j;$kJz0Gu}7Zfv5;^52^e`bS(tdv&&rjbE;1Mal4mNU)W4`O(H;_!XS2t$T!{j+DBV*qHb zm#1fJ2-p@YXmv4{9%21S^Z-WYv|ZQL=K0ej^Y!^zs)Ma1V%$ag}H6V)ybI ziNe58rjU7NKexAY{M(TVv(IJF(=h{9f_kiia}HNDmwJzF_g3Gx2@Nxkl_l;N)o(s_ z-kIqVMG!WATlADu-p!?dwkM5qCw`AxyR*CBK5Xzcg8lci96?Y)YBkuv=a>BJuNy}a z;ow!a#q`ea1JkUZfZL7MRZzOd+K|@M`q4^tqQu(cbrAsx*Z2}f)8}pPUWrD$En96O zhb6x_uBdL)QJJh6VLBAhMMdQc)TT^p#bq-@{N=$aGQRzj$4og)scw1pzOKt<7Zz<+ zuA!bJxNw;dI)W;9HLTIGvsUBN&1f*q81P0@LSo#40rg^0MQkSUDrTHs&>AGiP@a^4e!poaR!(hT*-#1+5`+Mp5P;c777Y=2gnOWh7q1k6{bckN7NInMh zHyp}IHDU|<^>dR<3qlF50{TfVLJtmPE>?Z#pk1fk_(LFCka#HPb>1zwx^q#O6sP4X zZZya>kk9S9^sHzjb4=WTm8G?+1bv>+T%5^WNG#Gx+aEJPQ;tIF*N$;{~k6!)^{wqL!#E(CG2)_nF1|e)n5DJU#lB^Fd5{gX9V+y5fr@?Hh2#)9WlV##LNm*v&Niv7d;`8$K*H^V-80#>ZT z{Zt(ha?e_T%QIg;RoxI?pEz)ic6|BGF%C9L#OJU&wBfte8$a5LXHS%FSNOL-Ki#fqc#E}{bnDo!ILp7Aw9AC)}oE7CA9 z60Vvark2e=+k=?nSQ}D)m@s*mh&|J0s^4F<&bgDr9c0G2Qw(k239MB~ z4yw^)K8f`()e3nhCgN{7%Z~0m&J6A3lrNLW@0i5UK>mt;G2y>LU||1aCjZJn@fnB8 z7Lo=gy2dCm!>!&7h11REV4JH)0PRb^euichTV>SHG|MU=S)XB#6(4!LO_Yu9UD43g z9U!QDrox9hzI(ba&1YratHpV^Q>b_BD^cg+#}}7cm&U<~`PLuuMea-J_Folv^gqDH z{?C0C^rh6%gCRwaKWl2QJ_w#s$=#oAnQu>qdCbcz3_2w~WM4B}oee!cUT=T3Og;?! z`A~pW2Zz`}4Q&V(K1qInN;$JV~ zpM`BXBxw9%@R4)iuVpr*kmPoA#HViN z77;Q?J>T1KgNSeTbOD@M5YFln9|^y|R6z#SeKI+`r;pd`2x9VmM&eJDf58feDhjCg z&ibRDHE$&1*?*KM4Myuo(HIF5dN3D^Fq2dQ?n|ZT!51jlEom7taAF6SnrJ6vGlM7a z>Y>T^75S+~iP+Xne#)3t6{DEfPwdlKMjKota+%RlHPQtmC;HrsvpEa7beB}R@siqx zt!CjNmsQ#JGfGL%24MoXGnx!SH1E@UE9{7dBD9V~?G#yj41~WNn_liH0=rJ=$VMug z_<~uY7+p{=zg`Hvp2%;ZtW9+cW=gXr;@(id(`+^3Yefn(o}F)h%sqGgize<^7o0!H z0Uza3f_E|6pV?XU%>hE=gckYC(wgt`*Jn(pf%__$e*iuvaK?)G{p!8y27ygi{BvlC z+y|Dyz4wX&W&?`Vq!9PCw8j`#jqhgGamOoYGlcPa-t#*6u#K#Ht2igK9LeuW*avQxa+)9Mp}_O<`+dEVIn7z|JgYx*P;zEp`sW3= zw}0xcY6cJMK50@S;x2b%sEpJ90Imw2E`Kb#&V1zWYN+`#$qMz+MbgW5=N&#ms?+lB zm1_DlHD!Li5Pq`31CuLA9_A(pTUvz*`?chr>P+FJYdlzaiewKuxtX?LX=*a(lc3O3 z{^!3ZnL)-~GbV{Dxm|YMZYn7mq?P7KwYS1)f_M6#2lkLw?(T3E8=|WHqv{RURUsN_Zg?whXjshxliU?XDXgIV7C~ z1~%%RtP};H>?SEW5c^7*(`R7d1%##ajT~(Y493teB$LQdu}zQstNVsk|A^$hwn{RK?>iZ{Xf9u)vIxyPrMa$&f#k$`L`pX z`yx1;`uLOnWf*j$Z8hDhP_7vEcM~;q@TYFQnEe?L(x*P6H2&_wvbeE0VBwTyzb5Te z^t^N~*};(pUEM#4t8yDmcv1sQe(kAFDc!7i|2^iLQJl~UH~ufMy{dk^t;xBSi;LCk zY8&Ky3NLWY1u!$Z`K)B&CrE;;>q60s-y>F3iyn}Eeif`fD$?TMuP`BX+ScrF-+Kc5 z?@bv0LB^yEw{?M*X=yLcz5(|sd1-3wciaLX33WGu)D$g1H`Li#!^JC>UZZ z$vWJ@WbAtlhO9UCtuwdg0X9;o3Nkf-VFiPz`?GxLiT463e(`3*9WxgFEU$N4FbXTK z;@_jHeDgb5yNMP`B!9yiKWqu;4sJeXdz%Yb9CK#%gJJb6;fbR{3wd~BBy`5c?0QQ$ zTxT%!&=UHMca@Dzw4hT+^zQ9z@x3>As$4T#D)$7?q4mE_1H5M4EVJmQZD*_p?}2Dp z@n|KNHRllQG^Vt?N&}YL-mly!J-ECdYmKrU$>o)W!$nE-A7Ar{CZ@B9M%ITSt0{JD zl_}R^XqZ;ZVs3-*bs+)nI%9tSy#%&9I850zQ)xTFHO!VK>Gz87SKF%-I7FvC)oGLH zOpE=Q5?YQBIV97O&odUdFBHAT>jpIDxC4+Mu%Q4(#nsPR^@e-FMB4BQG{~i`$F8 zbypM)m(LZ?1tr~We0Ih*4$HckAgYey?p2MBVJAYZY)PJc6+xl$2tnFwFoIi1A@)?``|;9MJzss1&6E!)Tw@BHPmJ5tVF{Stbb5^B+?* z!VF)C_0{Qi{A(^Qw21*FCs_kczW5RUVDlR?!Ja6as!1iNa z{B?ebFeB0E;$-_i=#>aDi=~Xg`|g0B_;&niLKOeO8A=&#xjWHh0r?xd?>HJ0w!b08 z1VEW|dI3HeHXBXPO+KqXomWDM1~c-ZwQd+LZ!m{zmQ`ucrr4~evWbjR0q$Ra7wR~S zcJOJ)$twmUl&&+`7=nP4>=m?xjaGvjp%9+*Q6EAzRRHEzg+n@+5j!N1b>gnI661Iu z+~qC?ic3k+DAGVgi%GO;Yj~Eh&UpPvZ7Yb@Ol@wtSesKs);PmGHK?bmvc5zU{8|3| zRFDv!f3^P@4VSAVHvZq@cg^!dbFS{}bY-RK%{NuW@f94m{p3xkbjxdu8sbl9T}}>v z?j1fmQmf0od9h_mLe?L|>FN`&>=ytnO zKI2tyHu324W9v3Nq<$uE5aqBtWG31Vf0`%yR!gI}Upegc65HkoM)8z#`{J2DVzW$X z`F0wU`r{0NEr0so0p7gJ`^01q$96ufy7zuU-9#VRh{#?WF1%z#7mnSsdNhRO^)t!+ z5@Yq+X$%JzNkV85KD4lZ6@^QxP6?Pw$0Q4QPK_I|&*cn#FO+*4G-aw{Rx-B}q(p{A zK2$O6zS>ifI=d|F*ATo8-PCc3;G;D*=NpzmhZb~JU_Ga7L5!Y4p?|a7(+lCXFesRa zf~BX$%%#=X90{{kLWb{G`5N;4rv6gL=22G5KkW2L4Xwj&BHby~Z9NCFan55Rig%6L zfgNQ_%q2_7h(JG7?%!AN-tA(-(wpQ%yU?b3`K&2=)1h$`?%nRr=bKW?orAfeW^0rO zbZ5N4v-Ib0Cg&W&q(0NF4{M;{e^ohE0}cG*BqIa_!JF}U36<~s>w)oG zc?8HmJDiRE4&n|StuM2-<}}vHQPf<532&~NYRSJ2Sh*nBn_7Y;6*hYlBwnZ*-)Dg8 zO_Cnke2{<`Reb6LUVAEHvmzWT65nm)X=MiU2b^|v;tc1UF5t%h0Kmzflf%7D*VO_} zDTRcRboo%>njgT-&|_RZm~P%hvX&>YmWN_4;k082X*Zy=_;H#e3!qT19JvLOR}4tH6MXAF}THx0{nGQ#w$C z`1~4j<^A`o!^X7_9l-i;A9AXinIs4)av}P3OKz?%{R8+H{yOU(&1D}FNhMl0jZPiM zv6$`}^f!lF|6R>-;HfwiOuwjw;WIL*Q;aLWiG`Bcag~;D!sjDRrrb6lMd-4(3%t|4 zMn|W&Rr~dNMAgET8%9mQa~Ge(UFVI{U>&8?sCSIHqkBiXgHLy2bieZKRX%b++K6#f zyD6kUUvt5B7$~%|z#q6;!Usj2dq)%@gg zFN`ECJvGt^P}vmysnjztf8r-*%WF0Fvvc}AP?&G>*koZh>h!F;(4GH3lRa|rl3oF5rk<6r z3Og<=D=Uc=I)S)7#nju)7IPGI-L&I9qyw{JZ~cHCigC5yXwmp4M9mC_e0px5C$={# zFUrsQg2_9UqQ!^r32w+X$sWY9=6#0Ux)sd{WL_4lpZritD_MJX!#+VHs0*7WC>8=@ z2pRqaLQ^u@Q{Qvjm0AJxp!~V1dLZAGvMkG8-c37Xt-=D004wYeOntsbyW$y`6yziQ zMfBbSa)^BvuzG3Ulubw}V*;x+r+e3wTC27S&`5Zn#?A*4U*q2ov6O5zk)kSq<-0y{8PE1b9(qwu+;pAnR=9F+4ev< zS*o^wN+}`-NRFX84;_&eX1C)qDVQDYh|^g4=ZXXFKHPIUmv}Pnr5Qqg?&i|7+T9es z!D4pvZBB7~rGdXw&`vKErMo(WY$!Wf?Ite@#yT73CTUa|XMNi}-Hvx%eap}e0{=0J z8q^=Kl@9PnRBHTY-iS7B)_L8LyWEu5K4WIevsyActTw#%jTvGO->~E9{m{*sYnw4j zX84mxdShFPrSdM+Y>VyF__S@@BUieqDT*)w!8O78KQyW^v8k5?@&G_sgA?XW3eTu^zzB6HS31lTaj!i=$raF<}GZCoGu~Q?-F1;R57iO_G z+Hbq<_I<+B`V|rC2IsK1j!E%cEgrbqlO(3-E(^cSzhm$GHm>>>B{G-87!j!=qAQ;K zwb9M2yeD@|E_i0U8#I^-5_o@Fz|L1?&0@~{cH}sDd<8Qw>9o{9tgG7iq@T@vRVz-H z+jN?(j49#7%5zv}wta4z!kGcFCYl6y`tzbnfO2W6i*vyh)8O&PEj#Vw+s@n~e~Lw% zc=l1+mx(IQqVDXo(Isi%B`&@8(z#U!jFKS78kk}I^aZScXhrIABQm#iP`IkqSOp^@ zrC6XCkNgV-00k1%RUYIwwWiHIMvDHC5UHcJ7ATTr4QO`uVy`KQVj5JGPRbP|0agt1 z-d!zCy#6}V;nv)yL%^_JMIu8(bD^&{Jr-x<#OL7r-;|}@(c3%#cZRQ(FdGCW$C|}# zMq|(?MzH`TC$PJs@b7$*?pfz2?W*Owq7qNm`c2E&_}3fke(c(K42}$Y!=E=i4$vOh z&xu$Jm|0H0#wVvI{F)TJoLZCRZB6?;g|4{7H3_JF3coXz)Pso=#lu+-X*qN? z3r~iY(Fg3Lst&)+GOwz)q~upm#>4{Sa@Vb&pLZeudXXX2)h`|Aj*wILuQB_Z`a~XU z&h4I3Bl4DAVIi=8$Vir>mXx)a&Z7GP z$eyiz|NCJLYqs(}lay!X@P|IOiQam$a!aaWnShXwy6~v9Qx~=DlqPwMDr%gYI(Aek z!3MK-ek}0JAPSrKXE*FqNpK-HoXwYCg2rhqT$mAkQ)mt^t!J;1PnEr(Xc(=e3)|G3 zaI~WZW;yGd6$&QjVsJgXtPXBjJB%fc>iE^=m8@R{#U{?Q%&7V{FN2(F5gQD_K%x8y zj^0ar_-NNiMPUT+V?rOIkAHmtW)X_bfMujGKvvMzlcaNb@a3;%%c4&@+VPKYgxdp8 z{q2hH8?icQ2K$RDB}h*CRuIT(%z~TcWtXYazX$)0-3&N05MJM% zg|oL3kz2bf8pevz&tMu9Ck{6yUiaCY*N5{PlU}=-R_f#ha&s=9j_JMrtzFjTxwvRu zCPjUfdQKl{h${P;gbwWs&BXJOGpcG4Iv3!=*WyvOLq4Sk z#~-+4IIqK{hNpR3lN6pC8J#wj7#D2y{QPLb3$!%6N{h{6BDIHo(tJCrF$>&(&<@xn z(&Ew?iKrfAmAapPn(0zr+xO3e_XIKUmVV-}kKRvv{$Enlv99?V1Gc+~+am;|7As9= z9VsO8iMMoAe@lWvDtl?=)DLp&R@rBCazMf!&Md)|=W3;=d5Ajy0^m~QE= zxVc||S82pYHBU99L5?QQ(1#p`7UlogR(^?O7tPJS`q}Hz2wHDrXS-QrLnKAKlk87md1K$bhfMNB z&nC09UdFT9!+l@|({9lAq0WN0a4AJYBbrA`=m^nS(1d74N7?+{?kif}@<|LizH^k4 z!IUAL9%sM5U4nn-MLZci701U6s?NIyiZ#6Mf%^E9HtcU6d`RP%nY($ITYNXvl+rB9 zD3GxfYw>BKh_qIg0@Z7i{*GuO(WrkG4|{sYg%0Cu+>%B80b%d>+eyofwc+sd>e-c* z6HzcTS>b2Sg8|&8EQ3h*Z!=;jjyKrEgKr4Ur9ODV^AGTmrr9WH-(<{=$O9WS`Hw4q zOkqG>6DA3fG0K+^me$S!dsqbQN@s9iR-;e3c14+9q}23@Dw(E?sYZcMj`a{Pf9Ui5 zfwyU7LC*P~EHNGPs%J}P+toflK0X^c_qt$+;`TO#CG%1ySWK`^VkKzb;Sk3*oDn^SH#Bb-yA??}N&m$ti4|Di;Bld}*=fMg-IK5NK zUiaWh$`jtBKj+prFooqH0LdLXniRpa{9a}}$M2VLgt2f-r85*c<{!rVd&y{a-*gF| zViFTsnHPG8IcDWQ56w>oz;`i)XK~ljuO}sKG;<=swJshuZO5PTt1_*dKwgPPK`2M_ zWDk$kiq5NZFTSV07UcR$13}+!cLs z3Zi{t%H|b;>^f4pF20)_jjk2E{k;T%eqWX8sE- z(`<{%(7DQ|#LND@{ZDB@H#zl*E<8lQngGEhx!KbG(&LNyOR}Yp z8>HL&)e{=TvDX}XxfnA|)%~cV!Yj!yS_J4b%F>FeLmp0;#K!DHIrBKoF6_U%z)rKV zpDcP2DT=`}A$((`-GJwpVBxY@B_r_L)uKfd8cmEO}q{ZaA>u8O?*P8I-Jco8^{dDb$73IFstu_*lmTHCtx>gSTUlT|>E$_1KUvtCENi1(xesRpu@4 zODLS0zn7)qUsMtcsMt+%{?tT^8te*w>iXP4k8YrAhcvT;T}NiZuopot)u40&FO^?Z!63t`Fk>VvXPoO2Vk?*kosI6_DNhRkA%Rr z+%LZ?kC*bK{nY(dIQ~YR+!>CuyLh07w(g)KfLuGUzQg(p5_TwPoGopq>qle->#PE}!j8;&b`w?3{oACCPmH7}_p-PxzA9)q>zuv}xCj zVryz?t$lN{6<{v3R}Z@mq#xfyS()NC+IHSibplGXA@i2 zHVNIz!f4}fu@eRpjR5WiB}J%_#-e^zNW%XN!wGwx?Yf8eArjTtka5MUr2el{iwA0Y zL$TipGNjTk(x!$6jpl|jnPod#`D(FdHXRH%^(I5aSyCS7i#l#gAkWM-3d_ClcklK= zbre0#3GA4p70CTgR_|*rYJGv}3+Q7cFCDW@C9ZetzIHxbUvg|<7oKbSsI?*t_f)2+-jMz>eknl`oYqkmg+!@4!B=eeP8kEhp zTH(~+(#VWZN2KOGZvE&&T>qsZwp8rB?b=ggwZy2gRe?zVIxv?a#ce-^B^goo=tY1l z^(G<2K?2U#G3VTzQx-9LmkU-_7LjgxLU+l_@05MXll-c1EyRLC!Ivg^dyD@fdT2%TOBr5B z_YDlc-5c}KbhNO6)a@MEoWGC+0Vyd)l*As3{*KIXA9uo0VMgynEZ3zex+&B#O2|#Z z+n%?I0H@mTu~`+ddU}`#RSyFid%$fy2%Ije9!$+b`L)L*?gdMJpf$A zl}5Tu)DCbm{_j68>IqJG5IxK8d-!e1YSG^s1-2asfE~jHSWPvZEWsqgp-Dv4+SqN| ztXbvbsL@Yjcg2*iw|0+zaATBiU@6?zE#N1}Z|CU+Y|KM zASz^cH9p<9@;beMXiW3QRUWfV+>@EH)Vi(^c9rbk>eY6>>Z}XsHjM>b4R6@f)xKX>> zEFN%EZXc$~GwcER^0`<`s9m0k@_aa@VBmq9Db9C#UD6X!^batWSMFUG{-uJy?E4fq z!LNBK%`>*_lf)}|?3FuTgtOB2V&AoPKr_zNTb z>%Bxa_KS;Bb9j?{O2g(6eY65R^=tx(-{!OqU4jIJS{h_*AK(}2IOd)HlPpl9^|Lof zt&&ZP%rNb~nx$8?+qABn%?>D)wh`5Cv z;uN1oAc7}Xg`_*QQ>cOn6%C!n`2OSL&4d4K0{ir>J`hjZ&sBn!|E!+G>mma#7PuFQ z>ZhlDx4%cn#8kz0Owqq>ce7t$Vw!+AUXuZ3GMl7J2i>V-J|IR84LL)_l0FpfWu&hT zs~;bV25zpf#t-AS)srpk-_o>7+4QiD(SsE;>v$F=qg2N79rEf=fg5#Zd#OwoY*LQ; z<~$N_`Vt_5ZY3Q&3QDp*NWv6dQS4}~?3CgG*O*xFXi!K|S*h73?>6j9MLt;rRq9*^ zk6YR{7J-3&g2m^9#POOUCUgYh>H*eEQuU>w@_eI2o)d4wp?d@Nlu!EyId)vmabblO zP8oECljT*|JJQ5ziPFoRHni?QKc0fUbLOFSR{PTFvRC$Rd8&V>i+D@QJ2CGe9UJt) z{s{}oQXT8X>a$F(wnnX?5BHF)5zhGH{F(`dSa$64Q`x^2TdMlmQ;|73E4)7@Gz01K zvQj*5c}cfh*{%xZ?90FUa4XXsXVSqkhM?_~IX_)2ir_)|h+}IwlfSS^f}-$A4XCmn zd$5Rgo_60P7E$7%PUk{)^SCW*I^WH~x5C#&H#V<@<2vWHsES0jm_HR7v(W_0>VqCJ zfyx>@P>mpda(Z4pz6!0?@@x0QY9((dQX#Uk{x!oPzA%oaQfll3jD|r?hd~#-+xvbxiI51ityRBav@$7|F7xfa#qx66^U% zGSx@oq=Z>}hX<%_Z359XD9f1W>J)IP4p7?7H!BGVsPc&Glk^$}4JDeDh`@f__6nhS z@Tfef&rM=@Pj2AiUGbHjo5SZcU;)s4>w)78a6gI?_fO7 zn~E^+`#N&mAsSONY}7)2#SmY3AeS&-8;wp1{w{L!sE=T4#JsTks6F30q4H6ynMU%! z!Se1wev$r$f?SXYZZ*o@>UA_kHy9ZdwQmS(8DRgr2HWn38pU}m2VxR;V{QSjhrC;j zj4V^AYLu8+QogIkzFW~Lnwm?06yIKt{!=D~d*?j+`v#>HT6~XgxYU9jxN4;zk+JXv;4;jBt{v}#9ST;- z0c7i)M)43+R_dsq5e0K&YZJN5mTcR%P!-|-0F|7|?dTqKz~$juYpGX{Li_E9QiR4T zZ-T@-3657OJ$JK*TCPh{Xs))Wd6;-i414IsFw8{JC9FWoEx2@d%k&Syz|vhm@khbk zZP7|(uK|e%k;c%&H1bX!uM%kVH28hqJqOSeWYFOjWT4%Vc7mZ^sh4f*z06Oqb4jOP z)gXhJ0E5EcHY?D3FBRy&F=%W6Q~os4npXufGszqKn=HWonh4_+8)h=i(qBCu5x(|Q zQ5bruhe1(bv#>$*1HgL;1WdUh+k(P;D7YmS8c??7mwDYGi};tZNlsxfwtFT}HHNrj zY}9D0_(c|g=|u$9nZgT;W?UzELuj|QA{l2BM$#gmA30S2<~0KanLhj+(d#Usq(|>( zCW*wIIMAQ6nG7!3Z;^rA8eYIpu(n!YjZ1UzrkM0+bSb>)w zgBV*V-)hFm#`YTzpPkiNy9@aZyUPFX1p{7V7*X8{AnN?;mEPW2^@R`|+-DG~pLHC% ziE0zjn&x`Rk}2O9Tf!jBow1a&d>)R{HQJGW_Y}d_+f9RR=Yk6$eruZv5??GYbO|4q z=%7Y!GV4H>O3$~u6-*7EIhy!*2vy{MfR!W0Tr*6)deoR0nTyI+HfRZGt6u1{DmG<> zs%r$QY;tJ{nYWKHN>>rG;GXiqCt=**?owaPPG!XQNW zRQX1FB0IgV&&yja*alP0qSNXg5}}c)1GG1)p0QiQ!sn80=-P4V#{swg2+DUq4<7Yf zL}D|Kanf`*l&&d3;_(3IBhTobq-;l@LCF&W6|rH`?()%n>@jn_W`t@uE8Mj1pL~j=os*BAm0!(^S$(evDUdsl}Ti9 zX+0J;z+z$bjiOZG^qk9Q}6pjbQKPrZuzPNT50;0%+19 zkug*b++)YEjgc3&f4UBp2Nlv6C>o%K@J@#%3+yP=9ELJ%$bz4hFoVwZ7n_@EtjNE;TnP0Sl_MP6pAYs&f}5JiRAKM71Bkqw~x z82o1T!DQg^#Rmk^MKd%#Ov;15??M_$AmBY0<m2) z=mBypl;6Go;pHJ?^){Jnx@kW0qeZPXe{X3YEc);9^2JWt(hiG0u@BZOZ0hZQ%!*3# zczvD#F&g@-M!1 zRO<omkEYb^;)hzpw_--WS*|Tq%v3A%e;}mjHZ70;d})qWoI3zeVyLNpQTvX3IDV zIe}w(d<{aZpi?f0%?g-4QK>U!N3D|60tE;8Y;q6ZFFxLW)Qt=iJbAnQPd=_w&w~AG zcY0YXB0Xx(CH;{HruZvCE^-wvX0JBLvJ`8VzCw~T$)C6|U(M%-M0DaDyeR?ijiE|m zDjj^qFX313PRK*Cn?k0;Kgc_BE_=mr@ti)xM5Zwk#iJHpG)>O9n5*r#C2MS_@Cuy1 zH9%iU6L|AU71-LhwH-$P@TJl;Om!jY`m??59)d&7G+{gRN9^P0#$f%p!>yROylv9p zKSOgsDA2u#YGXYZ!DouOe2CVe0%D%)ciua|{0|^#4c{o_kM4W%GOWk3+B6&{@OXDU zx1Ic^x$MJy)Q=gy^p(7_E!7iDSolA)&OZ$Lk|J0AIi=I?3NSqEJk5M@UDBxxw3TUv zG01A@@yN?!J0#dYdL#yjjA1FMtSL}rJ+-YzeGsQ-Q=TM)IkuKA>2$kdD?ChX)Q-Je z@p&B&O8rH8qdLbvsU(J*bMIX~m3iOXpk3{iiFcPpF;y1?3$B#|%@PNXmH>!UZC^gN zi=8L)N%gyBg0H!dbkzZyobU~9R(ZH+6fNs6f%KRv;#iG`C)+sWlf#OGRMVrYPj;Xd zLT8wTZ*=;O;_@d4wkv??nu0y+Z$l(uAF-U!YG^v03#|>ju@!cLBTB=c~NreZ0-oW1HGRTPz+q zcZD-d|8c=8gxFrW0rQJxQR{!JYUE%+IZ5;wxhr6R5rK=%b;B z!y0jLnXqv`4Hh1F`@rp?>5sbR#hDjV(Pa{uOCt@U}cZ25#Gtrw?0(bmlYtEJp=+i2Ti5AMyP z@yG;-5EqMZjQW_U8DEHjU?tg$Ord>(rw+1kZgGtC~oD=aOEU+^KprZZ;SqTKK1XhYxkfw z%lat_z~;QQ$mT<0r}wM|(cAIN7v2~)!DY4ltC#{sV2>&j7b9w!kkTU;Y^4^wuq@`= zp96xq)A0MIp+}$~o z>|%E7GsUkqn#j}VnPvVhpwGOj_j_K^W(MCx4V2$PU9jSbX*Mi*Y8e(BWeFpxF0ECn zu)Q#Y5I$ZRH+3%YF0j@@Uh*UT~MUpsn zXM8|^Sg;{KDa`OHBRae%sk68^Kl6|(!u`MqR!TfBF^CMYvwlnQgOX# z>*lfch&MWSW#p^-Z=Vj0vFQhlRKfpRjf~Exn|6cvfwtbr2s;R4q$)-dLmycixNIA5qlyO7=Unn+ zbkTJ<x%oQWN>7iQ1(@cC z(f01UKX%mhzE>y)hez-I{fR%eU*_cJbcq(AZG2iP2isV?bS|b(z$lN%ArLu%mru-~ z)@%+eT*)rD2Ap%o1&v#ohs%IDv#Bk&9eW89>#ArO?k7 z%9i`MUsO$ektQIyax*%WReW~Hb!U)$!@Ss=~a*e0hWx>to z;>_HimTT2_j7!_((l%c{k#&1tGGAML@2adJmfP@W9qYbLNpdv{Kc0J89)yeaGODQa z4jfwvPWKZ8Yjn-t^VB|cc@YK<^eordY2*98yd?Xo;tUwPCUqaNr1hNpbSD07%ZTmF zls7Z=jDu`6?mv5ZtnjO51>FI*l)XUu?`x7m0X22}l(cT0q?VvML>q&@rt^~xIqfn{ z24Lh|Z8etKw#<&t*melz!eW}P_|G+vXYZ8){RSD_N0>*@7mj3r_ilgPGzsjWaN z0xNr^yLE#9TXH|jcN6oQeMUUbcr)%^cAlu0gP8IDA!Fp(J2qyVZ6kd~`~pyA2J=Q5 zQ%0DRz}D#n)4UHPS2Sf?Z)>5f%_!dqrFJe_#XIj{WkE@UTnc&2d`%E%g^!z2hmOsH zGZ$EbbwI*WLjUL#%sv>G_Gj2IK3|?1iX9XnbK993%rK!v zWt(!7ft$~#@Y5$U)!Hjrnt5+^OWtwD&BzwwvNx+OhPH);veB_NwKW#+goU5;jp>&x`cv zZ-1DI{@HO=(J4H|Crcf;5iiGXaKqw^!N6cNuL7lSBQJulU`Oi!s{e)CRAWH&L`Qu4 zK5NBUr%sNDp{L6=@BPHzrVvO}w6#Q0;%5GJRY6gmj%IrNn0U?aE0TUlV0D28?E(mY zUV+!6?E;SMNz4iRG8ZcG&RNDci?l6OlIly5Rpd?9orrU0jMFMv9HfnZc29=3S3Rsc zALSzzYgw3m)^1xC*IH#H*}(`c?zo>0>|f!aDYb1pZoX;*T{HUq{H+-(?`k$Y5?5i2 zyyOj7H;2z}^a*-|?2AOa>)+WntEkOgvtlDTH0O%yt9$2A1y4YK+Ww|8t72eGDZPcQ zrmH0#Lvr)9L+U%H;sUZ*BB52FR~@n9=b#pe4AbE(P;Ybp5nECRm%;NU=$-N zD$wcXS0++x)6$x`OE4DvZYJ9Cp(Cc}EWs7->NY=R3lZ_Y(w+zC;|&h6`?wrGvzi~- zocT%Gz&*CNBpL~EaAL(j98?H2*p9k}d3i>)H@B|Xip8w2x(%5yAvnY0i}$P?!W6~c z%`kZmHO-mt=z!YZS(@!J2bB^ij-xFNmg-<_>7jd{p(rJ5-)=&(l&*>skU3Js7sR5G zgk`n!DGP|W*2>cq){WVIpqI^(%-(fH0sNq`hw;SAJnQe-`N+%<@hYc>Pe{TDP6?P*9dqww+C# z_;00A=2w`n+_Qv6co+D@eD}Cg_Ugi8v<;$US0`&n-Bvsl5OesrWarCAJ{kpUgs0?E zm#!q-&^HE4dIdMC>hmhTQ@&_TNNTPJ*Bo=5a*&F)<38!xEHOwYF=dC6lHWY6v@9n_ zP7PuklmOd;(+1^HT-iteu{(U{Z87l3yF zvzaau%2UqQiTbtViYm#lLxf4|hwfYX61!+LU;W|E@7AGkC)b^Vt$C_5wY{3Pq=2R> zEA-knTlPx-mD!%?otY4|h$p4GCF08KqU#jGlssew7pTL3blHX1&EBQebSTO9XWU@~ zl&*@#Ex*aqT|9_s)JF_rV!Jdd#JE*2j>rTn@dP_^l%#Pd^&?`Qednkd6Yy>INF<#V|(#mVHaZ0Y$&=JweEf=uy&l@EKt~q64 z9dv=oW}cctvwHv2RV!otF=BU_j<7m=LeWmMT0URImhDj_q3@D&A^hZZp!{1?!ym|9 zakgo#H)`KU^*2!^2|_Zasunl%xc*LysOiD#VuT)%+>^J|(%W)ae=#Z-XxlL#mtU@T z_$>rtyP?~>FA3L>^OP~{(+S&K{>47rgx{W-+iqL* zmo}CPqDQhjyt!M9orx~v-6Rs;Q+n!#B=Rt1oO zFz`@IXZ4mU!aVJ!kwAo3F8^xV9Z8G{^Sl-pfZ;0orw9_>y z7<$lWnMU;Pmiain9>aHSkqc%1@}I;HqI9v*rOHaHncm!OMZtv7up?j=!n~SRJ-Z!hDW zc*kNK|BErE`e(=1U>6Dy$O-SEh82e-i^-U#8cM5OTFcKIcy=(~vV`1l%;eH4liF*p z&p{T7%*PL^{dY3!ftVS;{UJ^Ch;TyXLoo7wGXmS`z3w+jwS7A<`k)yhwW7dM`{!~1 zP+nGp*Sz89OW!<+>)2%{|6{iP|DTyie~yD8$T@@JnAT^q?&Ja z@l8kYUf}}WpD(1@94)}BWs&2->&rklSd7QpDDBoCwp(*xbjriNuC#<_1=+iQ1ubV} z4T>aWP$Bo{n@tiUus}1^_z>XC-5=a~o^`!D>x-H3W@w;kG+pq6sDibRFs%lLr??No z-dxNl-4QQ!t&xt3mae{B+SpCE84?0I9($|7uDc^IU9T|F;S!~*Ofem{TavNj8rsYS zX-1a3C6!x+3=_*QhZIP2>~K!Cu^C#jsoJ|S#ZH-s{La+?H;``1*&E1Z>WPj`SHrQ=H6QJ1KKgsB&91R4?z_s!JD4<;QlrqGqk z4EpkZQz+GjG;)~4l%YFxwsA@j+Zif5cU2OleRK~LR-y+RwwtQd#aOQ?;n zUp)s(&xbU{!q(|pr7L?}?`sCOHc~yL)d!m{MG6d*66V#POE53PSssZ|?o$uunv&_TUe{&r6O#xkxXeZzg|EV_0 z3i5<|^0ZjXT_k6AX!nvDp2E38vDo3ti3)Cly-JhLM(jLIXgAZ`!m_I0iK-Q;eN%K8lrW=kx4h;sGXgw@bm%$}D+NwiqxnsoKm zj#FhMUfHIlSj@Gic$#BPl(Ru}G?DTmn-vc1%aOk0&icizy=^UXt73 zuL}2(j;+bsw3jVux60t86h%{hr_9Bq>2GW;zaD%^aDQhVCyCc-`lQA^=*9g_m5!gv zKF4}dpLfm5sT3un=D(bKoS6llwVPM4O1FU^eR9(a)_1ZrdoD8hRYzt|H_79vp5T0$ zAFS;~D5giE4P^|$rTI(J`yLOzGBdO!VsYDe@~v;4E_vX9F}PC5#v z(Rn{-HYA(fY@WYUxDEO;D&XlIh%3(55H>U!7?Vp1Aw(pQf7*G=?R@RPHT13zjvfQ~ zr+L1%ivCWu9@%>L+-J6?Sc2tkk6T=@t?kMOpQ?suP*$P#e6*bc1S-ZbUYLuF3-8bg zWhh1CJaHL_aUZ^ae@`vnw*?Zu{w&=;@FmE9{~utb^bcbY@vdt5v=mC8yz|GJtHdi! zz~$$`vQI+T+eyJ8D=pp$D8V8zK^W(*hE-`KLeqSF-+vgRARjwBjB;&)`Xgv9$@0l? zU(oCy75*xZq6zWzq9Eh>!*hkw=Y8Ee)3wVC7S#zK(d_d`FnZUFw2%?#VV{N35Cs7n z>IIc!^q4))PgTA1j)Bkxpz#M@VRrVo0 zg|?o#nRMxvGcvzs@llEH~f%&6|xth2PR0D5ESwF7C8bqT4~ zdkgS|`c2lE=Py4(O7A`-OwGl@*D+6u1NiYQUiADmcB!%HuwEIQ=!QSbQ`xS-RX)JMI?5ZyFuaam84W)sgh#z*2Q%$%bHJ$;%#z%0fQ^ z4a2Q&5$y&ymqVkKQgmL%#SGmZ(@pu37DGt?qsrLW`^?7H(vVg;XjZoGH$YfM&DY?I zev}v0jX*;WzaviF$-Vi{)rp8}50E)e!jW~_k~>?c=bg<$&ayeQ1qj7e_ME280c2)$ z05PFO;W0!XT~8H^V4C-3LLNH~4_2(Swe9M^(;)XOrhK)LeLpk*1j_0wtkG^9+GT4} z3g8k@9I)a;$SJc4+RJ=t6W7k=P`_*wUe)e1RP5uyE)Q-IiJjxBDeIKF8sET<=mO@+ zk!1ax!1sVBtY^nog#qqMUW7G5>A@w1?k|k}8A-!w=>n4sy&ar-Thh3r?AaHfpFEaO z9n`fgJz-L`kn7;KpTU)`88x4hEvm5JNB!X8K`FP9zLkWQ}o$7RKJa;5-NJYqxfo2x^K z7N?5>c{bS5{ITA>Kh_;$`6`g29FPalPdICv+Z13>8Q`8)LY{qBl{z#hWM~V;lOPF? zHJj$mZXmix3DrY{kG==|)yN_X0~W_GX$CImVv2IOE*l4=2t8D+;MAG-dT{q#dhWYa zF*dkV_26O??s1G`u_Jmf9+UYPX6zZN z1Ivh&{Zrk>NO>aJ`)h2TYAH&SgNiMI%LzpPJec>$RV+t|A@T{^My;qk{s`@5A?sBw z^~(-QVX(99Q-6tU-*Rv4N8R9$PL{gx;X*s|y@eDGcjT_3qgrCrIPH*)O5Ob1a&~){R zK>al%Ey_`!KK~`jYWDT`o!0ZgzqX5`zH+AGMKvt%X3o8E(I(%^&XDSR))pqNw6mNx z_5h+CuHN|d2Pb|lq_V@}fw&_U`2=AOXfFtkV3Q-ztbRTi6tNe}f8lqWr#`=5sG{RT z)lBuYv5zQMR((5}xyFhZ+8WcqY?qGtDikv;?3c!qs9 z+jBRJKceX4)nqseu+P?&3UA&^SjI>6ULRq#rI0!d>m#i)fn}T{QP;$6fLU4e4pe&wWYzHCM^b zBw{Zm1JeOo&)MGA{f2a)>CqEb+OjM>)gI0GLO#P`jopwN%H{SoIUHN$QFb;~y!bz= zrRdJ6kyF4uT-c;CttXVbV3^F?&5-jSoJZhWcX-Cyef#2!tD64@ATA%+vr0%r zsBLnHvXRej#|cIEc;VP;%Eb!EA}v6+K2{+wGS8=AVbyQW+G0Yqu#Xye3(x;Pc{&UA z)sR_3Kw4)YR{!{qO|ie#xA<0?u89fxntu)MXD)!6d&%D*qK~Y@-L7amW;KMj`x$5E z?W^dkShpzFmK$nvZpLt0CGZQF34~pWfZI*L%+(JgkMAG6{zTf4%iHM*rRBSDCL+Gj zZoP)W?Ht6VmJZ@~cF=)}JjD3x_2&Ak)pMxOA|#V$D+*a9 z@b1{eB_x(KhNYpeg`X_9e9@ZPEZISFFW{Jo`G}Q-vnwZTgEVLPvA7Gg5}WtXJO>r6 zk8+HHNpPv-EhFZ3ya(x)5f947AZap%a7>Viy+LYiYn}xn}-4yAHSl^a>oyz z%lxNt*OFV zo}KsWvsp|(jEF17FAq_G#GqH@1%HV7AWknkz7e$UW3ojHX885DDGTAh1b%u z2C{xI?V}S}X@4Y6gODo@BAqMItHmg>>zJ*6rR6U^hm|S#I{kS#5#%lN6e!t#l&+o_ z<*lp?y1#)mxQD_$FsO#hh3P+_{|~@mWHJ5QFDUV^CcTeLbFTBuL&d6H-HCzE= zWc_OE3)(BzhpNgM`g1&2a_31jGYz;|Y^BdxOU`%Sm1u8DAH?VdmtPO>Re>_amz#9g zsy+Ub(3Xv!VFj{_M@ZXs22;pK_+Qtku&0Ie9ID+o)9494<9;|pk7j8VxNj`xur)5I zHCF?ZK33sw*;)l&!9*#p62fL)CPFyMRr>i!D(-Mf>nx9CD_s6wVF@I$k26M`dFfp8 zy8)~*gLhKkKw=@~20uEtxBn8t!CbA>NQu5DkVfYWGkho!WiI>~W)kZBs487IJ}>=u zc-zG=PH8~8>7f(zl!={X#QHctKQJ%(b2!J)3>jI}dK>>Qmr#7*)yxDt$ z@-SV5T^N~vz!dmldw5AVz>f7&0P&l6oHxS!nr^4q#5T|_3j=6CEiYzJAfCi4gpr7$ zs1VcEfNQ*@BCuY|YPlDn&{SVFrL-{7`_auC<@`W9_jq*CfK4yfbekn)7(?Nbr_mN# zPfMU-3Mx0O_#Q%z!i;_NP=oRsP08D?PI=EN+pPZ6B)e^z;(T$ts~E#=xGPTA^NqR? zibG6U8`exVuT_y%fHMw1(`~G&-eC?^*pIXJG7a=_uNFi*w<1;1cT%VZZ+yL z{hYuryWFiMog>L2#A3M67KkWekc+LD9+_DosPz*aX2AB>P-!baU+;gw-Yz)>Lh{QB z(Rvfe#^$tag9*GXpg%{ZKgm)J*{Q0Ow^@qS?9|LT(csLx>E={C?XuSu9whNc_EOD^ zd#J%q_kMG0$?FI!7;6(Gw=0H_t2M=B6nP#`21w#N^>7YTg8 zX#ym;KY&7f2Lx@;TtcE;A8gV_$M-bvND_+ z9ffiqnzL1ZsomjNfCX={o|0a?MYtf!crbMSL!o~de8oY}cJbO^o&%)+zWruiNogmW z^{aB3mm_PT3hcFp=Q`K?2EGpX1bJf{{tQ(b_jV%OD`eUAunQ+Gg%6QfGW4GlTbuP% z*PcNy)!o8a8rvygz39aX3XG(6us^5Al+U0!xl zHKxIP&z#He0I~zr^pSofkvlGX2G|KRFMz10^y`2}st9?nFY$1JWjnb}qIa@7f>^aW z(EqxkeQ8cyx)RjN6f?4qvsjBC*SO$zxZ!(VEXHUfL8Z!=qyl}mN~xIFa7r2Q8njgz zjhKbPRV#ZXnE$YjZkeTL=7|oX{P#W!SWyTHqUyk!$6FlNPNXRN_-}|8qsnmCIh#(X zJNBDy=75)*R?%TF_n2*@kg#(HoO{V@5Kp9Cu^fi<_;N*BCUeC9XTI&$Oeh9OUWH_M&ri&|w$X!Z!qpc9?!0JTwMg>ZCmY)MGYcAF-& znn>*$BqS`9tbBb@6GVtb`wFj5lWUq=71$My9<=EH7C1R``l|nq8xHdQc_QQ|;hYRJ zQ`o>y{|`{k%WIRf7WFr@uvSlWU@6$Tf zjcsjh-!F*WYwg!-O7D7BOy$ay#!J;kTRzmj=xx$-?Wq>_KW@n0c`xHmYq3I~y2oG7 zBGKlSu0f_YY|JI`^<^(V--{gYV_i9K!O4(H^HV74k-7f9porB2zuQUc)(k36!8L#5 z-x`-Hgp5smi8D4H6brJU&zj_F9nuQ{v>NrUh#OhqIk^{YYr-DC@*ff+no;^xd&Iju z_KQO|V+pBon{%Bf+&{EBu?@F3TMgMH%Xwj2+Bl0gE~pZ07QLgu_;x?V z81BpT?d^sUa@IuszA7d>?D`*o*KtZz;-GCup~#F1;%P~MprC8)>YaZtPCA=wrw|#4 zX}%2pXqL+Pctd&Pri=-EZ^eX=HJdmA%VR0{NN1$q1;Oz)Bbc?pMH~fFCnv=WbV>ZD zj#lqGs5jtUC@kG7rBhoe?U2WG4*{Y#sc6~YnY$p6jm|%ZWF!e{cGxumY6+c)X5`+N zmp--{56@Mb9|R|y%_*6Ew4T75J`3~l^Zqe%T+QME^vQ5DbqKvL1y+*87OU3i3WtP= zr*(rdc~V2sxYK`h3S53>?26;l+~rU7ua85Xkf%b7g+^==ZKQs1l?i5e_*4mjm)PuJ zxPTo6UD!|D1GKgH{m)C~QiA&G2;NO2*6r&YV7mkcLvK27Ku1ea)N`t&CT~@4BSh68 zo@u?_7Jc|zl1tOFglz0tQepUr46^zfLIBe$0D7v^w5$|MD++S1lzQqJ|sS$u9 zOz!PJe1X~kjMYvx_iEi(y8fc_*vDS3^W*mAT-bZxJpHj(7s*zcxgCMuJgq{Y4rNl} zoBL-AUtq3q(S9SIHVoMSEW6|b84w@hy*x~guZt}pL%4~B^TS1wzvJ@`#XD7tfzEEf zOwVvH8H6zuHoltQv9K@m7i=1s@Wam(LxDD^!KLJJn~<4>VLQ&KLatv2%Z2$uQoJdG zO!K7JF>!WQ-5*A=0_d&MD{fgjv(sd`_nUFdir=-fF61<>)2d6p5~P4h0Wd{Z`^k5E zlpfqfq<1f!hMBX|%=FZVj*zY-D%N}ctX*m}o*meMZ~^QR@uU|Ta1%itV3o-djCXdJ zzFs2{b6@8VpGy^IS3+uUf_82gf}Ydi{1$rVL1S^4A!3~mBAviJt(ODt^+0j@dkjH; zWp(K$7h7szwLixx+q_9m|7?22*|7-Qa$i;|xj5JJY!H_<;u;sfd^%?eJ2yklw=S5k zeALYQRmsrUVVB7Ydw`SkTO>H%332)Cc1#^4uQbr$Izb@rd?@ z3jWOk^gpxUXq<-G0F>B>XRh`oIO9;Mg5_8eC+#YP&~C|Wv2UG4_!cic+IX&&qz@r!$p z+6|bBCCqN3EJ0MQShw{_*9y+_oU-9-zIU|9N&>f=vYOET83tl>*=sU8llRGe#&09*b`QMzDhnc+Zu znzCy9GA#D zdJ2>ql;tVR?r=)K-`)4jU6#>pSJS-ccZ)aDnmn*EQ|zEusZ=eX#VU(*dR+M!#Z$uJ zVfdvYsQT8kg$A*BD2O(48boNaOp?jw33pxhan@Cnwdrohgd0@n@lKsTC*5yb?{ats6Ng5K%JQ%}w3pF|F*G_HHjCIz6nj z?Pfh=YmOrjU9u`YE4w-Zk4sQ9`z+NSM8d_7*9!}~=RYj$Ir3oC2?+947DGj4fLJEd zuvgV3uOmn+)sd$ktJ$tYL?s^>V_q(BFNToKr@>AlRsAqA)7AmHe!1-YE_i-wp|ok! z=iP3Y_TN?2`@V~~Ix28$yQ7^x@d=+^awu_{P5s&74&!`>yXE>GW5&X+7~D5Y8cx3q zm+qf0WJ?L@rHubf?ec1*7h5kUC?Qp84Z@0gz=*%S{{x7EZyJ`_bS}@C-p^|7UG0{G zUF0n>J@hT6aJtp(NeA9tO%}1)G+gdp?$Ne#sAp+SdfGE9qmg``G-av2Sol4h9TW0n z2xD+~e-0sXyCRmi&t%Q~4}|#{$3`d#Ldz^z?I>ldLcyJE`pHQWIk__2@N5t0rZPy$ zy?sS~9L~xHsxns7Zq#7a&;~r?G8F7b@?aU@y*3+`Zfh5J2h)zhni7`DqTPvPb^{`= zyR+tf2;oimn$GuS-|Vs)7FUH;(FdA31>LE)zwy43-99lrNg1^K#HYYMq~ph^ry(s* zES*8_<5H}*2WQJISZsMrFTRz+$>6U58%O|jKv>=*36rLph?S@$i9GV$z2Y+(dn0tt zf|ASk*voM+aqG_a&hy{Brq`^_jB@^BOcy=DH-5ZgYi<0R8z@}^uS%|P=5%7=b>@Ct ztXy`(+q@@3ZzYxW7q-bPr0+?XL7iat=7Dy)nB4H%V#5{upqlJ8MM`++-7M};f|{o) zlT!VPO&sfd#elmNfhj2cuo^URMuhbq`XIfst-B6#wL+XlZ#{1)O|{N&O1;pg35lmg z{T}_8C}p!KSO1v3Zjjx&E4t1`UEnqWQ?c}lNlNKd1v9178c+|-(EgJ_Vd|I+2Ft;3 zDSZWMcm;=@oSf$aUSIfcmkfCWPBBXWx{?@xn3J53Zw-N=3Sdtts2W+)4Xp&=dCVfK zWk3BB{C4sE6xL(wy0-HB%v!Aw2q%S^7l{kct{6cKkMSZyBM|eMs5@DOsc!8Ies6N+ ziOSeqtWxxeu%6ixJ>lSe_g3tSZ?NA79$%73}?KLCngo;CL7c#nV9=R(rY2cP9%nSZIQ|1Z;dQZCYL;TXsIwAlNXq|&)&UEHXM>dbl&IsurMi(ao@0pV^*D*^izvWL*D=Rkih_HAxoW-^7C{< z!HF-O8ql11^M&K}_h$B0d_uuiQPV?*Tt4bp!h6y`fx&m?b)KeR(@!!w; z3WfZ3cA46}>)LfoRtraBt=gOYcz*Lk<7o~gkY49(P<@MxA{sQ;Bm5=LaR^Il-=jP0 zvREXR(^*@>i@DYnzjA5t>-ZiJTjoT2PVLocUu9#%*9kS6A<9k z(x=o<3`x<-1lH{ei*b`J+`4n=>v{xn1`jTlBqvpZfqIlHFkeRXC0`}o$~mLqEq)F*+&H;WuD z-(|UBv{bY6T5i)+-)>Yl<2gA`;)|5v&IYNusKRKmTQxHkP56u-{|t08l10HvtfTIS zr->hXgzlRS-b23pv#s;f_2?%<@OO(@G(+G#(8{JPSECmm3F}cF6J;Ckc> zjk(7i%2N8-0l}tF*;te7)#t<~d&}6WzN^={Kjv_e3lWsk9tYS(iwd*!b>O@9r$oR~ zWYJVW@KcmivhDlHVWNkP$5rGUYfn*qVbxvi6FKasHt5wiWGz7kWVDKLkpHI3UPS+C#9~r)jo_$_`?O?jSg?n2YCU^{_JF-Dd ziudwY4{Xw2Ha4y=_gV!<{QM9cLC-%j&0_N)EJ!}!h6fkCrFV>!|91R*#ae+Ec)qKd z0&RZkZ?;`!-no=t#i8c8C3GQB+6pBWCB8HNbq-&J6-Va_%glc|4^7>f(ydph6?>LT z4J>LKI?~nGQ#OVas>&5D|xp z1Y2o%V1F1a5W^(3Q!+Uoacuf_rI^mri8nC4E0Qyr0HaX(OzZq5x1xiRL>o!K-B_H> z=DwsQm8&k!6~GKj5oF6{Ce&5oZrbpdc1$;84Y1LXGN6@ z3(wGPY7&bCS?6ljWR#c9n3U=T&+$q33?tRa)iaF)c$uVm-xXDE$bzPjoYNOdo~8>+ ztw?!9wHFk?kJI348VgP2_gVmtzw&@8j?VigFC; z&EM=voaM4y>A2ebK%A-m2f4IC>j+p_t3t_jIgk{lT@x1(lgWQYx3@r)car~W6u3}{KHU$1j)mht>op|k70kwJzmKoGn(7n@NG`OMmE z-Pj!9DWbUFv0p&FHpWtPFep3sxkPI4Je9GmP5oUPEuY)GBL1K;tBjpm0Y0Qgg5y{)+g3XrW*jWRAZi}pOELY#ZKI|{4;qY(ovBOE zi!rO=iv8|>n~1(`e`^Sz#U#`wi<;lo3)8!I^KXSS?3Ma*RF;q48?gXsCyLzv0CKZI z9fBVm^2otkvk+w(^m`wk=~V%WTZ9W=j;Wkf^)>`Oa)c5*v^LvyrnGz_b1&a8{sGZ3?K4fL17fL@9u@fLjI)q{J4 zDM|18;E<5lB-DsCC$Kd6G>HjE=E7)Fl%o*-NrkHyto^;RuzU_a2E0jQezGdzfkI>s z<4I)O&9hyCLY72({QxUO-#)IX$%~RZiel60kH(tch4aCNPF0zlJSq2fE=!|nHDl|P z{}wr0-Iq88PPviBX4|9P_W#$=nLjf9KX7~)=H3iBhMD__F=Whr%zb}~l9;nnxvDvH z8;Q+a6LaP$m7{|~j$xRqe2Wg}SfbpbRA2wX`-k`Y^?p5{&&MP8XD%Wv3FP&u7_X1U zh`RT!z_Q-alq*E6I$#TlRo>;Se(H`)cD4C21zs(ds$2; z#I{dvhheSuKkW)s34I&04^rXn`WUd8bFB4sx*>gNZO-3oN676ItL<}r^RZ`q;(szB ztN#FdVKEwRnER7do&`#ov2w9wrZ{xp<$r9(TyGu1xkUY{j`4H3m!4Iyijz_>EiS=O z2)B4DR3)kJp_^!G%mqqi7B&@NCNILTJS&$+J|ZzjmoRiUihVbUVnoiq%I1)Pe5JwH{oY=muM;nay4>ua zk=?O2uDOlGg6YYR;XY==xvzkfz)ATm`MwxEzpcy7zTCbMBOd-s7Pl1|RP#}n8i>mZ zX}a)h+LtAmL%jrRuox9;N z2PzGCs06_%s(la=?u${W8{n0B&Cw?09Il#kisQxQvNVm}2)!lC+PoA$95Um7@{032 zk^BjrBl(nl(>`tPO01AzzipNgOF<@3xhF);)lu8<+s!lHeNLW+a8|xyF1EKnn9a53 zp2lJKZSs>@UQm@K@a}fT9psT*$S~%JD>CX4=2g^)+9i_S3YW?~vvzuVs##yPw1~4t zm9Kwt6B{LOz8h297p2N+H_(^ptNiAAyx(eX4vHAG|G9Skt6WB>K+x`Qwo>N`&?JUH z4GDM(bVeR!h*j~q^cwx(hOFqUSsej0bP9N_hJ|$38I9feUG>gb4pEq(#{}xlh4sak zD4~7BG5J-D_*kaIH}(kWlONy9_w?w$j3%CthYSz18qAY+n!hE6R9*^vmSbYWnxcHp z`vk$x3l6`ynEx!fv1wGum+2T!yOrzT@^Kgu&C=u`;QYr6crR(1_C%2Qeze9Gb>#v3 zcQMfx$y(gsHs`wJ-hn^JaVsn?8Vpj6;Mr^G5-21y-ynRCbUIn2Zws4;f}l1TE8%|# zKbK(<-7Q7C#`{oZsi$dSv6rnshEwtqU^k>_*$YJKcM#!-Ua^0RQR+IlL*ks2%X&}} zr94cniN)0J78&szTrvBBqB(}Y+2xQ2>$`*g8zl+yBs%st4!mtdKtFJ*;n3E1p2I9V zbqZ;>dnHwUjs_#{X>Q^j1tCi||4n3r<;ZumWhu+}64wath1WtCYz4&?#80IucZRGv zUh$Qywrd9|#^Fm1xC!_2qN*0bR=uS;m276K6nLf+Y-{5|iq6@nBZ zW!BSlaNW?D6mErEz|p*G<7ymFiMf7LEJ|z!XR-}HE7-ynS)Y<$e{HC~3HZ1aU*rkU z`)Mi&ZsSVAu+wBs=9wiDzMO-Zmhb5ibUgaXGKV<*rb`w4r=lF`l7BwxB~9U{@bgKb z#*!>b&o|fiOZM)(uQiKH3TWAu-AnCstAML)y}f6Oi^v3tJfop=!DVtTMrl8BmwR>sj>P z2f4qG6OrfHDoEiUzvi9@A_1X%kcOe2kPr-;$fG>eMwT%PQ2lXL!F`*@tWIT|T{5DL zKRDbPjXQ!zFKnjoYUXA8y&{qgh2a(?>4?+A8JJj15oGT_>=Vzdo?SzcheAoN$3j$| zb4%pP>CB(Tg1S2|`#>=*H-AUs!eA?Ho zs%m2_GOsLVASfpWbPL(lALD<$RD#NsYYYZa&g=;GTInr~@hf7sUXlEY7oNd&w?q&O z-rxF#4o#>Qvv{jGIVankyP&9?NzNDeR#84_Bt&y)ka`sbFm}`4ftl&Ya9tyqpoCg!JvVexMcBzvDL*4H)no4A1ew<5 zHG&~np#AP^-H6IYM~qBMt`a%$OOG;4*}5xbl)v-QUu_#Lly+)gaAwPKbo`t5ge#hI z_p&Ppau1?|p|iI-3ZHRHKw<1q3?ut$i=g9jgP65XrNt+%Vf6)z>tXy+Jnen-O_wL} zyS1;0Q*6E4rXS@olyN8a8mZr9)aAT=h<^O*w~3hA-wq@jd(+W%wrs8-cWVU5F*UjnIZ)}o z^Ug=jeZPZhL&_5zELooM_C`v{j#xh51s3sdKD3GGN+=0{FNxnV#Bhyi2!Q)nJ^;UA zbBZz{Of_gGV85%@?(T`lp?QJbpl0}Cfkwp_Y_!+m%q?9JE!S_m<rWAl8Xv#=t>4c-WArgIo#Smt z6>G^o!Aa@e7ac2V(!HS{{vICV;v7??G2H|024<*|?RQ0W z%>!e@(8-zYOn07g(}LQ-NSWo*^8*vf{iMk1+wp9TY>j6VYWTJeLoXwh0{v6d?eH?c z6siic$i=&ITr7N;b-qO$U_GzLa{ZNs6<$PeCijeYE;RqIZ28{Pww>0PYRo}}^hACo z(y9zgzqPcA7d~<+=?>j~J!#)OTp+;_CU?XD>?9F&q0+#?S3KgY#_s2Y7*1XEdgfc0@nPNK5yP zZ>AkPNn~L1qxy$1N=hhlcxLY5G*Tv{`9Yw1Hj~9M1ITnzBI$rQJs8}Qc`FYB%$!@F zC?q@eF1D+NufsiCnMG`6b$uGoC;#W30HBFnxg}09s49|auI@7lFONq0Ch(83?xT+u zW6@J_?iG_NY}~%?!zpp9c26&)TpW&ob372w zhm@J^);V@#SR!zo)o8nd#w^_((*G={H~_8p|Z=sU!t~t7C}vppFSb0w_Se5{sXqoo|fIOB?hqDYnZEcru3?{cL>*C1=U! zp1PP}CD1^3l;}f~dIITGmPnat81t4}xcOQN_o)u;%nBL$*tTJtd;dWmOgOjOdC4Y2 zesb9mMVn)mx+R<1bVfI6*DNl&ci}Lj9)g=x13ShuX6H$V)$C3pQ5|rvoP0^BAxD-& z1t!&wjrpAVe45`=tnq*I^&fbC?|$1NwiotI$%DdpRCrDpapnmA90Zuba4=Y{TlR&` z?$@kp*cWsaM{Ezlc8$&Ss6%|6B9B>Wn(!l*b+PANc8r*$xCY6UzxGWAAg-M$Y<+s5oG1#{0r_wyed62 zUtbCGpQ7g-b93?6Sv~-+r~RYxkQAiT%_y@2I z?no*Z{{;Zc=LSRcDtVHq&V6HB%+)m_wj#i7QJZ0@WAowe_pL)As#Dgs2&A`o=kv0^ z1VO1rwM)b{E3qwlNse0@e0&|v&5mBUPELuh^)Raz z;BIvs&&RXd{I9e^TR}%Sy4_5TTgQBi;gd2L;)iKd+`}ep3I)B z6`7{IW63kY?(;(ftZE4p|CGBs z6vxq3kx-)b^qLpiriK48KLXhQOSp`}q^L4-iBGC8VeBxDrADvwF@Y1BQNNQP2E=LU z(T5R19_6ANo?ZU{J&@&&9q*k>MOBr4sef}N=3~sSV1x#Ql69QHbO{e{QYG(=`e);R z5%TlK)+d{zA}2oHQ`kY;PoT8U1^Y9(65*?(LR^x5Vz+$My@c$7IL<*KzmBB_bIz|I zS;%BzbvK)g8gBiN_7%omnSkTt8#TPS?nh??I@w{G7jgMOMRygvuSbT~KY%N=N19df zQ4;n{uvg|@q)Yp*o(BIF(3WMJp)58%zm9Ilp)4K3qihdLqrGi)UZ^xPpa?fUG_L(s zeqKsB?5r+<6V6}GgrK;Ek)J>ZK*l;CINW z-t<&1Ku>cBLBvDH(E%F1)`EXs?)Nw#-hC_$Ar8VWr>8rRL(j zOAD}Jok&-1)iCHA>t#3a$gHg{Ak8k)^5rBo`g}vuehwNO7MU4_ko+$v%XxeE2<^{& z4V*JC1LZFK4z9H`&rnKV_;z_}O)3$dWS4eWSUI6@#%k-sBqAlT$xHr{ez5XZ1fL>0 zK^T*9`KfraS={mgfoTm#04jI$Rk>VSjdySzBDxmF!^eCUH$$O{qmvdlNE^U5tl+6Y3?Y&^7N>(Fh10BORHcU$biy4|wLN9-AXvjP?-LxY zohzh9@Xq-*GDGT?E`3VK@j%8`e1B*QZfkP0x2dC1akKbchS8+g;iU+Hf$vJ7TV3~t zbgg&!VpU7zled0Kl_?jM#tH@Brd`F{0Q9-aDD$*Q?<6k8fSkS$9ULy2!d4*;IiqY@ z_nlLr3a+*vQ!}jgZe5PcrMijsLH2rwQF6a0oL3iCf)z5{V|M(ti`Y(Y!E-De@BiST zY~7o@#8!Z^Ihv~sLgj_e#ce6P=x8ydB6@E+yP_!VZoh9@3usv04vlQtlnSwBh z-|QOTqc;5-dw2|yD`Cjg^h zBP7sUj4#8ZZ6+gn{6XazP<~?KcLEa4x|}?!iaU2MwC@qz-0nB-nz5j%#6p&4CC2}A zKx4=@Px@0OZ(Y!0+Or_b?odx#^UTl6fSL8C^k@H#`-t6yF1{OmoOHb%E2!JobV1ne zA^k@;Pqs2zA^mjAvj@Y4>lIUyeooMx@ed#wtP@Va3Ku-IhP)G4;HQ}z zGdvrU5Vhk-d|7w3*3b)4N9~y*uRMeHS}qsmJZS&Ca=plV|Erm!8Y7Oj_h#y}akoj+ z7F@>1b(~j#jEKCN3EC?hA9uf_Q?BLg;w9uP$gL>yV!y0#Q-oCf7KGOny=q!Q?4B6x z+8n4jbo-E5-*YL$pS(e1PBrD-PqBO-r~gJQLX{%7m&#wU`}a4JWwl?S6p3V? zUdKapKk~PcX#dd5%)$j*f?ovkF3+yXyFt8iI{1Tlw#KYI@D z#1Ost`-y_~FBIkbE+@4F!6IW%1qwud17pWSg(3P#>Yd*N4b=j_fZbl#x<`Eib;3;`40VlnFvlAtZP2#$Y@*1zQ_&1zlUQ3rOCZ+Cdm>|^n&lM?YTH&m$ ziS~BK{?L;R;psCUo#m4*C2w7hK32^$O$fYMcK9D=Hbt&a{z=}QFJj)XDt09hb*}y@ zO%TR9r`X?Wloc>$koAQ%^x**8(QRSE8tMtZk!dY$5r$CwwOwnr$=~b8G`UD9A+Q=N zo@|=8=Yh6%HeoW7&t^H2ONyS<2}xHW+(e4%K~<-Nk+%wQTF3tY=)b1=@r%lG@Kvs- zXzNh=c%ZT|R`9k=fMkJc+;jj^Ub(p4p0DtinlyR=3zI~rf!S@#%W(1}>3-egg4(t= zG$EclaxL!RTJn-ShwK#Bd!92Ig15L)xu2O+_u2dDWrZ__0T(@V?DRQ4Dw6h`$13@T zfCu2>b2wY)=lb7ET!UGhz7q}L|V?W9}uzW}XewAAv=@+|e zYrXZEZz@`*qkHnnd(q)%J}I32rD-L^xbuDpWjEAU=m!%)HT!L;+o6T@KlVxC!v1Bq z8?05OsYKl(4&}HO!LrtY$cVJCkKKOKc_DDDEt7D?=6dD!;b)=?kQ)4OY6at>d3)T_ zh~Y{7RT93wfXSq2yhx)P5GgxJBosTQicl*jth%T$V*Dg_U%Kj<1kEpSk0odkC#O=F zaWS>wSb>|`HKs;0%MFUo56rQ>(!Z)^xEb>+grRk+1qxvqL3>YJ9jTt?I+2;&NfN}L z?arq?@Cj#hGGK-lTt}9#yke$@h#N(cs2~+P$jn|$(B7EF%lgl$8u$C-iIs^XJ0%J}Gh;XHzAD?Cig74>n7$z>!Vfgast@-<$86}30}(Ol6YTm511G={ z|AxHeyCwebdf@5x!c538DM93Y@a2LZ9$1GIL2lh&kFU!-|0=i=mSc5eQ`4MmYTP*o zkj4xdsph#fonwQ(ei^>1UB2C~CtVi()3`2V@D22Ix{x&Uwv^nb(LNBR#qO#3gjygH z32AGcbIcehH;yO@uqsr5$G4@X?ETrZxZ-duKG!8by%PorTvW6B=!mtHzr%qF*x|^b zK;_Yy%#p>&@||MlocLe6#$)BS1$>BXv9(;LDQ4($i)kjf@4W^anmXxN=IgHel4DQ! zkP(3DPS>yJ(CQCN{T!urulnYPtX5UBwI|OSTmb3mcB&m(TRycCWg^Fq0XMruu)%sAS31e;x+Xoc}~NXC@R_J1iqOvvtK{#)I1P?m{dwIy4FAc&v_l_QX3N;T_Vbk1 zP2rHU{B8#*9y?+#RzW|NU?1<(dPPR? zi4ixdqgwYisXw6*KtCpGl)Mudu1gN;hvnkt8snav&JVsQT8p1KUtgUlB$Y;dW~4 zo9#?Ew~}Akl1xZ)UR|Z5jM~nQY=!@-T!SC?g74X=ialOe`R@mBCB8++p-ZX{a#|jy zI-}IA9MIE8*}Y8P{7AVF+FK!Kja?H`jgwa}}GI^L|CR$MUWYyO6~c;Q$--yA?jQ zENfE*XPCzjvbRP!g?0XHk+lTP* z`i(fL1(n3sC?jkPDNex4dm=mV&Wuvs^F73${Yd$&pP;i3?z>KjyGZ6T3}ekMBTW3W z{7V^Y*@KbB-PL6!#f5|z`Q9C+8i0;-Wa~EO(Q7Fw==I?ec{ihpfQg!s0)kL7sq1d- zE~B2rD`7z!7MCz^a5NB@6F@5|MUTZJ$BBeaf#PnZlRku09R}$xr>R3&AN-@P1dWOm0kkni!W-8=9TmsOZ`S_eDO3(7dZo$BC^3aBb z9F!8m#`vRtlZQMZ5Vucn2*{zl(yISW-`=5nXI3p|wVOY_Ieu@gJVsf%(<1yEN;S9B zaGGf`O>Wl2|Li+ez!f23_n2Tqh~T(qi5p3VEr1oY&inQ_lhtzSLW(o-2#>O@D2=z4 z^;qA1_(Jrnz#^Hv@tA4;5VVK>fXunS+j7HVY5ns2LH}-PQ zuhK#M%l&*iUae+LdVel54!$-qA?MG&&SL6izQl)xN0{L?fG#qwCU9dYXWIR8$K91& zA8gTE2dY9S`yb61jT!11Y#(OvGeGGeHc03)!D?s6rV!j2S(QCo%Ag->FV`bO$dKnUyfjOdR6-9$-*%fi0wR| zk^gGanizfW{Fj%~y~uBsZL;!0{GryvUXJ!VJ$R@Sr6x7VK#Ty?Q`Aj=Koyoi!>QYXFLEWDcPQSYZ)cqy;#|S8NRutEdyfV>Qtfqve!rcf^Ah!_f9J! zZvK$L#j;OZDVe@b?ex#u(c`4P+}AyS&~3qqLt*76?|)(h4EsHoJ+#TG8v)zpeN}bq?=P+>}MLx z^-1WE>LZljT&bl9(N->(JhKs>v+Cz2*(n8$8c+Cd=f?lR`v&md z8lwtxb+SxdwB=k^v3hCf9rE}ayZijm6EL&BVAS9~Py9I@uRNh5%*#>4a|K^cd&Ksv z^0kc?4p8_HFR&zhf+t-hE9Mm2;Bhxc2HjnmU@~r)A#*bW)zPZ|sasYGxYZ`BR%MUD zImpIHF>|{mI%Vs6a^bjGrsbla@&r~vNb66=w*Y#%RiONV`7JzQ{E9vy7fZG# znpba~_Y>40Yj5{}td0m-&pM&z&jba|OV_@vJzr3(6khRtm2b{V4iD#%?>#4M|>2PBts**=9;w-Nwc3wHcnW zIxSQ{lqU6;-cH)uDX6x_s)>>OjR{GiCmx6V#&XqSpqvyS&Um*_a_P;6k7tcS`+)sgni!W&v~`?t(Eq3m_dcmG8VuSps@UA^<=-uxH4}3_qB_5Ulszg6kRvO~ zhGPI_jCGN+F78V@P_)(ngp*rF0*(38Y)o+i51Yl!{v@W|5V8@onD}YAuQl~GsF^g( z8eB9=9UsbIZ{i==UH+in>3T?e%wI;W=$Ye|xP?oFzqO(7oIiI>PG7VtHdyC}-O$U0 zo>^(&*7)WPO-MVY`-b~q+~WGWVd@)Bb9v_9h#pp3jJO`qZiTquzg{zep$Qa}E@SRM zksG_v6|TaOs(phgap6*8jjbI^2)t{AK?{wjW#q7rAB05BYpq@=8sv{*=j1wb?i#kR zh(;cdDee|!!z9it+p*#~2YS`Gi%jPj?8yHq$ml@VW?0&LC~Gf+?-NQaUfBvUS4KXY zakF@&^B&pK>FeIB%+|ELv`>Cz^_hvR>$IdQvS&%u$Y8BZ$fRP}E&hPx|1D$6b+z^E-a8b>8-C4|p|@f8 z^D{Lp6(|co`5&7f;NF|)XwV#A<3gRiD@>6pcCvm<-03Z$I5Ubyemnuv}z{vVI^OXd7!6v2nhS&}l#%I^>J_N?O%;tOYo zmY=c6+Z+9MyhZkJm&e=~xf&5v2aVeA+;M9DpFdi1#dmPNKV%aLY_Hqst~6^cTZ%ku z_ILV}xssUqXr8;BjJHaogvyDxND1uuAW}YG;FtcnyKZ-J_tG&-<~U4YGL{rN&#Tjlbm5%&>2KahLX3lw0Ta&%LqAN%S`C7sRTI6RB6`mD<9^z zp`puP_Sd`%Yfp(^?s)rDnD`MYkS%Q=_$iet#O7q}(K0&VT)cYW7j!`0415=;vD*1l z=|sXD%y#g48tg;m1}Yp^QQ~5aJ>eya{tV0Q`l&9R!l!Al-9rOMHdD~k%at3i3ZJuBs} zdSJ_9XoUJ+NUL3b`p&DI+$NYL$y*`CA2oa*`!G=P$OtK)glG=o!+ec}H;&ijI~k*4$?CYI|Yu8;`s)9yD?vJy^w zm$EoY&sy(Ro`I^r^H2l$*Tqs_+*VF~=X#noSw@7w8sRV%c8{`+_$bZYiO1(((xXnU z%57N17{r4ksQl~Ub{l{B^wTBA!@pw*HaEwudF&LbFPAU=DvHFXmTrm{HIvpA<(YFO z?%)yOB9p_J@gX}Jdxi`S786gYqPnr4TxfEFqHXrp)l=;w1D#JI$)2bhQ>W07ugmeH znAPBhvGARk#7j~W1o`Kp4Qs7;9JBh)z%mBBV9#t6OkC$RetLY{)E0J$`D79p4X?V_ zr(-8+tN?>TML5{D;wN6lN4}zti20G?2C3-V)0Rk4Hsrd~EzED~9psc;IpbDY9BqTR z3$FCn>K{W_qz^$w10l3c%+KF>pQ}XgmdtFK5o2=&Ypf+eT|pz5t4Rq1hN*O{&!3?V=}6m!keLH&y_ulQgHY1$wEA1rAmQv2Ffp|+ylAH;X8l&2@GaOaH+Jm1 z#;cm%PFj@g)9xo8sDv%qQ9d@xJ@&_c1f%9Ux3=4YcX$lgsvWH!Fp_ZTh@c(lk^c?j zv()GZo>83^G8|>OstYS@L(F6aYS;K9b{W2pI&F8t=u=WAg3x6fiSQJ+kLfCv`Ii?* z3pR_u1uqMvKTvH$8dICxLW$XKyDIq&tw;3Nohfz-<55~Zm{$8+r0D3_5JCn|8NBsMNtPk>1i zSC$R;oS@^^19kAsSSleFXS%rd=f94$P1SGy+;e|L#U{qd9boKTVK-8OO0^{dD@t0p z{tuuERPAZqINvT24$Pa@wQ7DAvc)Ov(B8n@i^$gx@j-L1W;3%DKe)X*MVsTKaSWPf z0nQj3D*Jf~dP6vEwpS(K2B*nDRUm;q3aV9-FTK_Eq(G%oGY9@g)5R*F#Wew7ibhLg zLZna@{`s#-sP9w-{1GKP;4MUQ_YHc!mAy*D;Z5jJR7&DIZQNiuF4j2+A~7;?@p%$f zQ-0&n1(CT5o@^4OqAw zQ5J>#*88MSa;Qux(J8V&?qyM=!Zxvs*v#qb-@X!a@KCdA5SUTbyYK^i%EfvV2^<5+GJ3=&i4K z8L!;w3Za#K((1)=4WPi4frkEcQpA0_gp$y3Ks2MmC(NsoqTD=u2m<~p*S75_V%8#b=Kym{(cK%PYhZDmX*V}33Eey zU7Y)URGhZ0@WS;-}J() z27c2z269s?6N(LiUT3Wm?%J$Vd>a@g=^k9e!(EOQY!*oV*DDPA5?}5H`(y5{#Td>oWkIDnalLbzc?zgEwxw$i z{*Y%=8431hlTrZd4ebGjjvBhT9N|uhsDFSj5*G#zNu0U5=-gNXWF@)WZ5!l}au=fO zqGfbP0Q~6@4m7uHuJND;RE|C^&HF#TRGpCz3B_%}NV{J;pi;$U@ykCmV(~`zPlgj9 z1@pn&yh-~h$9VUv6&!dW5^o8Q10T1@4ZDJO{57oq^g&p&YVVYq&L$o<<6Vl-6kvKD yM7C;?0!y&ty-x?{^^o(x-T?>O{z95LrbsTUsFhibS?>qoznnXd;9_t7&Hf*PZyrPd diff --git a/htdocs/includes/jsgantt/index.html b/htdocs/includes/jsgantt/index.html deleted file mode 100644 index 9c73737d828..00000000000 --- a/htdocs/includes/jsgantt/index.html +++ /dev/null @@ -1,1167 +0,0 @@ - - - - - - - - jsGantt Improved - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - -
-
-
-

jsGanttImproved

-

latest v1.7.5.4

- Download - View on GitHub -
-
-
-
- -

-

100% HTML + CSS + JavaScript Gantt Chart

-
- -

-

Completely FREE

-
- -

-

Open source

-
-
- -
-
-
-
-
- -
-
-
- -
-

Demo

-
- - -
-
- -
-
- -
-
- -
-

Features

-

jsGanttImproved is a fully featured gantt chart component built entirely in Javascript, CSS and AJAX. It is lightweight and there is no need of external libraries or additional images.

-
-
-

Basic Features

-
    -
  • Tasks & Collapsible Task Groups
  • -
  • Multiple Dependencies
  • -
  • Task Completion
  • -
  • Task Style
  • -
  • Milestones
  • -
  • Resources
  • -
-
-
-

Advanced Features

-
    -
  • Dynamic Loading of Tasks
  • -
  • Dynamic change of format: Hour, Day, Week, Month, Quarter
  • -
  • - Load Gantt from: -
      -
    • External XML files (including experimental support for Microsoft Project XML files)
    • -
    • JavaScript strings
    • -
    -
  • -
  • Export Gantt as XML string
  • -
  • Support for internationalization
  • -
-
-
-

Current Known Issues:

-
    -
  • If the browser is viewing the page at anything other than 100% zoom then bars may not be sized or positioned correctly.
  • -
-

Changelog:

-

Check the full list of changes on GitHub releases page.

-

v1.7.5.4:

-
    -
  • Left part of the chart is now created first on JavaScript and establishes the left floating reference
  • -
  • Minimum width updated to 632px so that the left part of the chart has a fixed width of 532px and the right part fills out the rest with a minimum of 100px
  • -
-

v1.7.5.3:

-
    -
  • Fixed group completion percentage that now is a weighted average
  • -
  • Start and end dates specified on standard group tasks will now be respected if they fall outside of the calculated group date range
  • -
  • Fixed problem that would occur if Tool Tips were disabled
  • -
  • Moved example dates forward so the current date marker is visible
  • -
-

v1.7.5:

-
    -
  • Project Migrated to GitHub
  • -
  • Instantiating a new JSGantt.TaskItem will now also accept Date objects for start and end dates
  • -
  • Fixed old Internet Explorer compatibilty broken by v1.7
  • -
  • Fixed bug in Iso week date format
  • -
  • Changed to solid arrows on dependency lines to be more printer friendly
  • -
  • Code refactoring and clean up
  • -
-

v1.7:

-
    -
  • - Fixed nasty long-standing bug where the first Gantt chart created must be stored in a javascript variable named "g" -
      -
    • NOTE: This required a change in the method to instatiate a JSGantt.TaskItem object to pass the related chart.
    • -
    • A temporary fix is included that still assumes the use of "g" for the chart if the chart object is not passed, this will be removed in v1.8
    • -
    -
  • -
  • Altered XML export functionality so that dates are output in the specified input format for the chart
  • -
  • Added method to read XML directly from an input string
  • -
  • Prevented creation of a task with a duplicate "unique" ID
  • -
  • Fixed bug where attempting to remove the first task defined would prevent the chart from redrawing
  • -
  • Some general code clean up
  • -
-
- -
-

Usage

-

Following the steps below you will be able to get create a basic Gantt Chart. If you notice any bugs, please post them to GitHub issues.

-
    -
  1. - Include JSGantt CSS and Javascript -
    <link rel="stylesheet" type="text/css" href="jsgantt.css" />
    -<script language="javascript" src="jsgantt.js"></script>
    -
  2. -
  3. - Create a div element to hold the gantt chart -
    <div style="position:relative" class="gantt" id="GanttChartDIV"></div>
    -
  4. -
  5. - Start a <script> block -
    <script type="text/javascript">
    -
  6. -
  7. - Instantiate JSGantt using GanttChart() -
    var g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'), 'day');
    -

    Method definition: - GanttChart(pDiv, pFormat) -

    -

    - - - - - - - - -
    pDiv:(required) this is a DIV object created in HTML
    pFormat:(required) - used to indicate whether chart should be drawn in "hour", "day", "week", "month", or "quarter" format

    -
  8. -
  9. -

    Customize the look and feel using configuration methods (see Configuration Options)

    -
  10. -
  11. - Add Tasks -
      -
    • - using AddTaskItem() -
      g.AddTaskItem(new JSGantt.TaskItem(1, 'Define Chart API','',          '',          'ggroupblack','', 0, 'Brian', 0,  1,0,1,'','','Some Notes text',g));
      -g.AddTaskItem(new JSGantt.TaskItem(11,'Chart Object',    '2016-02-20','2016-02-20','gmilestone', '', 1, 'Shlomy',100,0,1,1,'','','',g));
      -                
      -

      Method definition: - TaskItem(pID, pName, pStart, pEnd, pColor, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGantt) -

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      pID:(required) a unique numeric ID used to identify each row
      pName:(required) the task Label
      pStart:(required) the task start date, can enter empty date ('') for groups. You can also enter specific time (2016-02-20 12:00) for additional precision.
      pEnd:(required) the task end date, can enter empty date ('') for groups
      pClass:(required) the css class for this task
      pLink:(optional) any http link to be displayed in tool tip as the "More information" link.
      pMile:(optional) indicates whether this is a milestone task - Numeric; 1 = milestone, 0 = not milestone
      pRes:(optional) resource name
      pComp:(required) completion percent, numeric
      pGroup:(optional) indicates whether this is a group task (parent) - Numeric; 0 = normal task, 1 = standard group task, 2 = combined group task*
      pParent:(required) identifies a parent pID, this causes this task to be a child of identified task. Numeric, top level tasks should have pParent set to 0
      pOpen:(required) indicates whether a standard group task is open when chart is first drawn. Value must be set for all items but is only used by standard group tasks. Numeric, 1 = open, 0 = closed
      pDepend: - (optional) comma separated list of id's this task is dependent on. A line will be drawn from each listed task to this item
      Each id can optionally be followed by a dependency type suffix. Valid values are: -
      'FS' - Finish to Start (default if suffix is omitted)
      'SF' - Start to Finish
      'SS' - Start to Start
      'FF' - Finish to Finish
      - If present the suffix must be added directly to the id e.g. '123SS' -
      pCaption:(optional) caption that will be added after task bar if CaptionType set to "Caption"
      pNotes:(optional) Detailed task information that will be displayed in tool tip for this task
      pGantt:(required) javascript JSGantt.GanttChart object from which to take settings. Defaults to "g" for backwards compatibility
      -

      * Combined group tasks show all sub-tasks on one row. The information displayed in the task list and row caption are taken from the parent task. Tool tips are generated individually for each sub-task from its own information. Milestones are not valid as sub-tasks of a combined group task and will not be displayed. No bounds checking of start and end dates of sub-tasks is performed therefore it is possible for these task bars to overlap. Dependencies can be set to and from sub-tasks only.

      -
    • -
    • - using parseXML() with an external XML file -
      JSGantt.parseXML("project.xml",g);
      -

      Method definition: - JSGantt.parseXML(pFile, pGanttObj) -

      - - - - - - - - - -
      pFile:(required) this is the filename of the XML
      pGanttObj:(required) a GanttChart object returned by a call to JSGantt.GanttChart()
      -

      The structure of the native XML file:

      -
      <project>
      -  <task>
      -    <pID>25</pID>
      -    <pName>WCF Changes</pName>
      -    <pStart></pStart>
      -    <pEnd></pEnd>
      -    <pClass>gtaskred</pClass>
      -    <pLink></pLink>
      -    <pMile>0</pMile>
      -    <pRes></pRes>
      -    <pComp>0</pComp>
      -    <pGroup>1</pGroup>
      -    <pParent>2</pParent>
      -    <pOpen>1</pOpen>
      -    <pDepend>2,24</pDepend>
      -    <pCaption>A caption</pCaption>
      -    <pNotes>Text - can include limited HTML</pNotes>
      -  </task>
      -</project>
      -

      Field definitions are as described for the parameters to TaskItem above. The pClass element is optional in XML files and will default to "ggroupblack" for group tasks, "gtaskblue" for normal tasks and "gmilestone" for milestones. The pGantt element is not required for XML import.

      -

      JSGannt Improved will also test the provided XML file to see if it appears to be in Microsoft Project XML format. If so an attempt will be made to load up the project. This feature is experimental, the import is best effort and not guaranteed. Once loaded the project as interpreted by JSGantt Improved can be extracted using the XML Export methods provided.

      -
    • -
    • - using parseXMLString() with XML held in a javascript string object -
      JSGantt.parseXMLString("<project><task>...</task></project>",g);
      -

      Method definition: - JSGantt.parseXMLString(pStr, pGanttObj) -

      - - - - - - - - - -
      pStr:(required) this is a javascript String containing XML
      pGanttObj:(required) a GanttChart object returned by a call to JSGantt.GanttChart()
      -

      The XML provided will be parsed in exactly the same way as the contents of an external XML file and hence must match the format as described for JSGantt.parseXML() above

      -
    • -
    -
  12. -
  13. -

    Call Draw()

    -
    g.Draw();
    -
  14. -
  15. -

    Close the <script> block

    -
    </script>
    -
  16. -
-

It is possible to add items to the chart in realtime via javascript using either direct method calls or additional XML files. - It is also possible to delete tasks using RemoveTaskItem() method. -

-
g.RemoveTaskItem(11);
-

Method definition: - RemoveTaskItem(pID) -

- - - - - -
pID:(required) the unique numeric ID of the item to be removed
-

If the task removed is a group item, all child tasks will also be removed.

-

After adding or removing tasks a call to "g.Draw()" must be made to redraw the chart.

-

Configuration Options

-

Switches

-

Many of the features of jsGanttImproved can be customised through the use of setter methods available on the GanttChart object returned by a call to JSGantt.GanttChart()

-

The following options take a single numeric parameter; a value of 1 will enable the describe functionality, 0 will disable it

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setUseToolTip():Controls the display of tool tip boxes, defaults to 1 (enabled)
setUseFade():Controls use of the fade effect when showing/hiding tool tips, defaults to 1 (enabled)
setUseMove():Controls use of the sliding effect when changing between different task tool tips, defaults to 1 (enabled)
setUseRowHlt():Controls the use of row mouseover highlighting, defaults to 1 (enabled)
setUseSort():Controls whether the task list is sorted into parent task / start time order or is simply displayed in the order created, defaults to 1 (sort enabled)
setShowRes():Controls whether the Resource column is displayed in the task list, defaults to 1 (show column)
setShowDur():Controls whether the Task Duration column is displayed in the task list, defaults to 1 (show column)
setShowComp():Controls whether the Percentage Complete column is displayed in the task list, defaults to 1 (show column)
setShowStartDate():Controls whether the Task Start Date column is displayed in the task list, defaults to 1 (show column)
setShowEndDate():Controls whether the Task End Date column is displayed in the task list, defaults to 1 (show column)
setShowTaskInfoRes():Controls whether the Resource information is displayed in the task tool tip, defaults to 1 (show information)
setShowTaskInfoDur():Controls whether the Task Duration information is displayed in the task tool tip, defaults to 1 (show information)
setShowTaskInfoComp():Controls whether the Percentage Complete information is displayed in the task tool tip, defaults to 1 (show information)
setShowTaskInfoStartDate():Controls whether the Task Start Date information is displayed in the task tool tip, defaults to 1 (show information)
setShowTaskInfoEndDate():Controls whether the Task End Date information is displayed in the task tool tip, defaults to 1 (show information)
setShowTaskInfoLink():Controls whether the More Information link is displayed in the task tool tip, defaults to 0 (do NOT show link)
setShowTaskInfoNotes():Controls whether the Additional Notes data is displayed in the task tool tip, defaults to 1 (show notes)
setShowEndWeekDate():Controls whether the major heading in "Day" view displays the week end-date in the appropriate format (see below), defaults to 1 (show date)
setShowDeps():Controls display of dependancy lines, defaults to 1 (show dependencies)
-

Key Values

-

The following options enable functionality using a set of specific key values

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setShowSelector():Controls where the format selector is displayed, accepts multiple parameters.
Valid parameter values are "Top", "Bottom".
Defaults to "Top".
setFormatArr():Controls which format options are shown in the format selector, accepts multiple parameters.
Valid parameter values are "Hour", "Day", "Week", "Month", "Quarter".
Defaults to all valid values.
setCaptionType():Controls which task field to use as a caption on the Gantt Chart task bar, accepts a single parameter.
Valid parameter values are "None", "Caption", "Resource", "Duration", "Complete".
Defaults to "None"
setDateInputFormat():Defines the input format used for dates in task creation, accepts a single parameter.
Valid parameter values are "yyyy-mm-dd", "dd/mm/yyyy", "mm/dd/yyyy".
Defaults to "yyyy-mm-dd"
setScrollTo():Sets the date the Gantt Chart will be scrolled to, specified in the date input format set by setDateInputFormat() above. Also accepts the special value "today"
Defaults to minimum display date
setUseSingleCell():Sets the threshold total number of cells at which the task list will use a single table cell for each row rather than one cell per period. Useful to improve performance on large charts. A value of 0 disables this functionality (always use multiple cells), defaults to 25000
setLang():Sets translation to use when drawing the chart. Defaults to "en" as this is the only language provided in the base installation (see internationalization below for details on how to add more translations.)
-

Layout

-

Most of the look and feel of the Gantt Chart can be controlled using CSS however, as the length of a task bar is determined by column width, the following methods take a single numeric parameter that defines the appropriate column width in pixels.

-

Note that the task bar sizing code assumes the use of collapsed table borders 1px wide.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setHourColWidth():Width of Gantt Chart columns in pixels when drawn in "Hour" format. Defaults to 18.
setDayColWidth():Width of Gantt Chart columns in pixels when drawn in "Day" format. Defaults to 18.
setWeekColWidth():Width of Gantt Chart columns in pixels when drawn in "Week" format. Defaults to 36.
setMonthColWidth():Width of Gantt Chart columns in pixels when drawn in "Month" format. Defaults to 36.
setQuarterColWidth():Width of Gantt Chart columns in pixels when drawn in "Quarter" format, although not mandatory it is recommended that this be set to a value divisible by 3. Defaults to 18.
setRowHeight():Height of Gantt Chart rows in pixels. Used to route dependency lines near end points. Defaults to 20.
setMinGpLen():Group tasks have their task bars embellished with end points, this value specifies the width of one of these end points in pixels. A short task bar's length will be rounded up to display either a single or both endpoints correctly. Defaults to 8.
-

Display Date Formats

-

Date display formats can be individually controlled. The methods used to set these display formats each take a single format string parameter. The format string can be made up of the following components (case sensitive)

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
h:Hour (1-12)
hh:Hour (01-12)
pm:am/pm indicator
PM:AM/PM indicator
H:Hour (0-23)
HH:Hour (01-23)
mi:Minutes (1-59)
MI:Minutes (01-59)
d:Day (1-31)
dd:Day (01-31)
day:Abbreviated day of week
DAY:Day of week
m:Month (1-12)
mm:Month (01-12)
mon:Abbreviated month text
month:Full month text
yy:Year, excluding century
yyyy:Year
q:Quarter (1-4)
qq:Quarter (Q1-Q4)
w:ISO Week number (1-53)
ww:ISO Week number (01-53)
week:Full ISO Week date format
-

separated by one of the following characters: "/\-.,'<space>:

-

Any text between separators that does not match one of the components above will be checked using a case insensitive match for a valid internationalized string (see internationalization below). If the value is still not found the text will be output unchanged.

-
-

The available date display methods are

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setDateTaskTableDisplayFormat():Date format used for start and end dates in the main task list. Defaults to 'dd/mm/yyyy'.
setDateTaskDisplayFormat():Date format used for start and end dates in task tool tips. Defaults to 'dd month yyyy'.
setHourMajorDateDisplayFormat():Date format used for Gantt Chart major date headings displayed in "Hour" format. Defaults to 'day dd month yyyy'.
setDayMajorDateDisplayFormat():Date format used for Gantt Chart major date headings displayed in "Day" format. Defaults to 'dd/mm/yyyy'.
setWeekMajorDateDisplayFormat():Date format used for Gantt Chart major date headings displayed in "Week" format. Defaults to 'yyyy'.
setMonthMajorDateDisplayFormat():Date format used for Gantt Chart major date headings displayed in "Month" format. Defaults to 'yyyy'.
setQuarterMajorDateDisplayFormat():Date format used for Gantt Chart major date headings displayed in "Year" format. Defaults to 'yyyy'.
setHourMinorDateDisplayFormat():Date format used for Gantt Chart minor date headings displayed in "Hour" format. Defaults to 'HH'.
setDayMinorDateDisplayFormat():Date format used for Gantt Chart minor date headings displayed in "Day" format. Defaults to 'dd'.
setWeekMinjorDateDisplayFormat():Date format used for Gantt Chart minor date headings displayed in "Week" format. Defaults to 'dd/mm'.
setMonthMinorDateDisplayFormat():Date format used for Gantt Chart minor date headings displayed in "Month" format. Defaults to 'mon'.
setQuarterMinorDateDisplayFormat():Date format used for Gantt Chart minor date headings displayed in "Year" format. Defaults to 'qq'.
-

Internationalization

-

jsGanttImproved only provides English text however all hard coded strings can be replaced by calling the addLang() method available on the GanttChart object returned by a call to JSGantt.GanttChart()

-

The addLang() method takes two parameters. The first is a string identifier for the language, the second is a javascript object containing all the replacement text pairs, the default English settings are:

-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
january:January
february:February
march:March
april:April
maylong:May
june:June
july:July
august:August
september:September
october:October
november:November
december:December
jan:Jan
feb:Feb
mar:Mar
apr:Apr
may:May
jun:Jun
jul:Jul
aug:Aug
sep:Sep
oct:Oct
nov:Nov
dec:Dec
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
sunday:Sunday
monday:Monday
tuesday:Tuesday
wednesday:Wednesday
thursday:Thursday
friday:Friday
saturday:Saturday
sun:Sun
mon:Mon
tue:Tue
wed:Wed
thu:Thu
fri:Fri
sat:Sat
resource:Resource
duration:Duration
comp:% Comp.
completion:Completion
startdate:Start Date
enddate:End Date
moreinfo:More Information
notes:Notes
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
format:Format
hour:Hour
day:Day
week:Week
month:Month
quarter:Quarter
hours:Hours
days:Days
weeks:Weeks
months:Months
quarters:Quarters
hr:Hr
dy:Day
wk:Wk
mth:Mth
qtr:Qtr
hrs:Hrs
dys:Days
wks:Wks
mths:Mths
qtrs:Qtrs
-
-
-

When adding a language any translations that are not provided will use the default English language value. This provides a simple way to override default strings e.g.

-
g.addLang('en2', {'format':'Select', 'comp':'Complete'});
-

would create a language called 'en2' where the text in the format selector was "Select" rather than "Format" and the header for the Percentage Complete column in the task list is "Complete" rather than "% Comp."

-

Once a translation has been added a call must be made to setLang() with the appropriate langage identifier before calling Draw().

-

Example Options

-

The configuration options used in the example chart above are:

-
g.setCaptionType('Complete');  // Set to Show Caption (None,Caption,Resource,Duration,Complete)
-g.setQuarterColWidth(36);
-g.setDateTaskDisplayFormat('day dd month yyyy'); // Shown in tool tip box
-g.setDayMajorDateDisplayFormat('mon yyyy - Week ww'); // Set format to display dates in the "Major" header of the "Day" view
-g.setWeekMinorDateDisplayFormat('dd mon'); // Set format to display dates in the "Minor" header of the "Week" view
-g.setShowTaskInfoLink(1); //Show link in tool tip (0/1)
-g.setShowEndWeekDate(0); // Show/Hide the date for the last day of the week in header for daily view (1/0)
-g.setUseSingleCell(10000); // Set the threshold at which we will only use one cell per table row (0 disables).  Helps with rendering performance for large charts.
-g.setFormatArr('Day', 'Week', 'Month', 'Quarter'); // Even with setUseSingleCell using Hour format on such a large chart can cause issues in some browsers
-        
-

Putting all this information together the final code to produce the chart above is as follows:

-
<link rel="stylesheet" type="text/css" href="jsgantt.css" />
-<script language="javascript" src="jsgantt.js"></script>
-<div style="position:relative" class="gantt" id="GanttChartDIV"></div>
-<script>
-
-var g = new JSGantt.GanttChart('g',document.getElementById('GanttChartDIV'), 'day');
-
-if( g.getDivId() != null ) {
-g.setCaptionType('Complete');
-g.setQuarterColWidth(36);
-g.setDateTaskDisplayFormat('day dd month yyyy');
-g.setDayMajorDateDisplayFormat('mon yyyy - Week ww');
-g.setWeekMinorDateDisplayFormat('dd mon');
-g.setShowTaskInfoLink(1);
-g.setShowEndWeekDate(0);
-g.setUseSingleCell(10000);
-g.setFormatArr('Day', 'Week', 'Month', 'Quarter');
-
-g.AddTaskItem(new JSGantt.TaskItem(1,   'Define Chart API',     '',           '',          'ggroupblack',  '',       0, 'Brian',    0,   1, 0,  1, '',      '',      'Some Notes text', g ));
-g.AddTaskItem(new JSGantt.TaskItem(11,  'Chart Object',         '2016-02-20','2016-02-20', 'gmilestone',   '',       1, 'Shlomy',   100, 0, 1,  1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(12,  'Task Objects',         '',           '',          'ggroupblack',  '',       0, 'Shlomy',   40,  1, 1,  1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(121, 'Constructor Proc',     '2016-02-21','2016-03-09', 'gtaskblue',    '',       0, 'Brian T.', 60,  0, 12, 1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(122, 'Task Variables',       '2016-03-06','2016-03-11', 'gtaskred',     '',       0, 'Brian',    60,  0, 12, 1, 121,     '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(123, 'Task by Minute/Hour',  '2016-03-09','2016-03-14 12:00', 'gtaskyellow', '',  0, 'Ilan',     60,  0, 12, 1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(124, 'Task Functions',       '2016-03-09','2016-03-29', 'gtaskred',     '',       0, 'Anyone',   60,  0, 12, 1, '123SS', 'This is a caption', null, g));
-g.AddTaskItem(new JSGantt.TaskItem(2,   'Create HTML Shell',    '2016-03-24','2016-03-24', 'gtaskyellow',  '',       0, 'Brian',    20,  0, 0,  1, 122,     '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(3,   'Code Javascript',      '',           '',          'ggroupblack',  '',       0, 'Brian',    0,   1, 0,  1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(31,  'Define Variables',     '2016-02-25','2016-03-17', 'gtaskpurple',  '',       0, 'Brian',    30,  0, 3,  1, '',      'Caption 1','',   g));
-g.AddTaskItem(new JSGantt.TaskItem(32,  'Calculate Chart Size', '2016-03-15','2016-03-24', 'gtaskgreen',   '',       0, 'Shlomy',   40,  0, 3,  1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(33,  'Draw Task Items',      '',           '',          'ggroupblack',  '',       0, 'Someone',  40,  2, 3,  1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(332, 'Task Label Table',     '2016-03-06','2016-03-09', 'gtaskblue',    '',       0, 'Brian',    60,  0, 33, 1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(333, 'Task Scrolling Grid',  '2016-03-11','2016-03-20', 'gtaskblue',    '',       0, 'Brian',    0,   0, 33, 1, '332',   '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(34,  'Draw Task Bars',       '',           '',          'ggroupblack',  '',       0, 'Anybody',  60,  1, 3,  0, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(341, 'Loop each Task',       '2016-03-26','2016-04-11', 'gtaskred',     '',       0, 'Brian',    60,  0, 34, 1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(342, 'Calculate Start/Stop', '2016-04-12','2016-05-18', 'gtaskpink',    '',       0, 'Brian',    60,  0, 34, 1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(343, 'Draw Task Div',        '2016-05-13','2016-05-17', 'gtaskred',     '',       0, 'Brian',    60,  0, 34, 1, '',      '',      '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(344, 'Draw Completion Div',  '2016-05-17','2016-06-04', 'gtaskred',     '',       0, 'Brian',    60,  0, 34, 1, "342,343",'',     '',      g));
-g.AddTaskItem(new JSGantt.TaskItem(35,  'Make Updates',         '2016-07-17','2017-09-04', 'gtaskpurple',  '',       0, 'Brian',    30,  0, 3,  1, '333',   '',      '',      g));
-
-g.Draw();
-}
-else
-{
-alert("Error, unable to create Gantt Chart");
-}
-
-</script>
-

XML Export

-

The following methods can be used to extract details of tasks in the project in XML format

-

Method definition: getXMLProject()

-

Returns a string containing the entire project in JSGantt Improved XML format. Dates will be exported in the currently defined input format as set by setDateInputFormat()

-

Method definition: getXMLTask(pID, pIdx)

- - - - - - - - - -
pID:(required) the numeric ID that identifies the task to extract
pIdx:(optional) Boolean - if present and set to "true" the number passed in the pID parameter is treated as an array index for the task list rather than an ID
-

Returns a string containing the specified task item in JSGantt Improved XML format. Dates will be exported in the currently defined input format as set by setDateInputFormat()

-
- -
-

Credits

-
-
- Eduardo Rodrigues -
-

Eduardo Rodrigues

-

Developer

-
- -
-
-
-
- Ricardo Cardoso -
-

Ricardo Cardoso

-

Developer

-
- -
-
-
-
-
- - - - - - - diff --git a/htdocs/includes/jsgantt/jsgantt.css b/htdocs/includes/jsgantt/jsgantt.css index f8f58017cca..f5c2d2971f8 100644 --- a/htdocs/includes/jsgantt/jsgantt.css +++ b/htdocs/includes/jsgantt/jsgantt.css @@ -1,228 +1,793 @@ -/* Sample CSS for jsGanttImproved v1.7.5.4 */ -div.gantt { font-family:tahoma, arial, verdana, Sans-serif; font-size:10px; color: #656565; } +/* Sample CSS for jsGanttImproved v1.8.0 */ -.gantt table { border-collapse: collapse; } -.gantt td { padding: 0px; } +div.gantt { + font-family: tahoma, arial, verdana, Sans-serif; + font-size: 10px; + color: #656565; +} + +.gantt table { + border-collapse: collapse; +} + +.gantt td { + padding: 0px; +} /* cell defaults */ +.headweekends div { + font-size: 8px; + width: 100% !important; + margin: 0; +} + .gmajorheading, .gminorheading, .gminorheadingwkend, .gtaskcell, -.gtaskcellwkend { height: 19px; font-size: 12px; border: #efefef 1px solid; text-align: center; cursor: default } -.gtasklist { height: 19px; min-width: 5px; max-width: 5px; width: 5px; border: #efefef 1px solid; border-right: none; } /* all three width values set just to make sure - helps resizing code */ +.gtaskcellcurrent, +.gtaskcellwkend { + height: 19px; + font-size: 12px; + border: #efefef 1px solid; + text-align: center; + cursor: default +} + + +.gtasklist { + height: 19px; + min-width: 5px; + max-width: 5px; + width: 5px; + border: #efefef 1px solid; + border-right: none; +} + +.gtasknolist-label{ + padding: 10px 50px; +} +/* all three width values set just to make sure - helps resizing code */ /* Additional values for some cell elements */ + .gtaskheading, .gmajorheading, -.gminorheading { background-color: #ffffff; font-weight: bold; font-size: 9px; white-space: nowrap; } +.gminorheading { + background-color: #ffffff; + font-weight: bold; + font-size: 9px; + white-space: nowrap; +} + .gtaskcellwkend, -.gminorheadingwkend { background-color: #f7f7f7; font-weight: bold; font-size: 9px; white-space: nowrap; } -td.gtaskcell { text-align: left } -td.gspanning { border-left: none; border-right: none; } -.gtaskcelldiv { position: relative; } +.gtaskcellcurrent, +.gminorheadingwkend { + background-color: #f7f7f7; + font-weight: bold; + font-size: 9px; + white-space: nowrap; +} + +.gtaskcellcurrent { + background-color: #e1e0f7; +} + +td.gtaskcell { + text-align: left; +} + +td.gspanning { + border-left: none; + border-right: none; +} + +.gtaskcelldiv { + position: relative; +} /* Task list defaults */ + .gtaskheading, .gname, .gtaskname, -.gresource, -.gduration, -.gpccomplete, +.gres, +.gdur, +.gcomp, .gstartdate, -.genddate { height: 18px; white-space: nowrap; border: #efefef 1px solid; } +.gplanstartdate, +.gplanenddate, +.gcost, +.genddate { + height: 18px; + white-space: nowrap; + border: #efefef 1px solid; +} + +.gtaskheading div, +.gname div, +.gtaskname div, +.gres div, +.gdur div, +.gcomp div, +.gstartdate div, +.gplanstartdate div, +.gplanenddate div, +.gcost div, +.genddate div { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.gtaskbarcontainer.gplan { + position: absolute !important; + top: 0px; + opacity: 0.3; + background: white; + z-index: 10000; + border: 1px solid black; +} /* Additional values for some task list elements */ -.gresource, -.gduration, -.gpccomplete, -.gstartdate div, /* needed for IE8 */ -.gstartdate { text-align: center; min-width: 70px; max-width: 70px; width: 70px; font-size: 10px; } -.genddate div, /* needed for IE8 */ -.genddate { text-align: center; min-width: 70px; max-width: 70px; width: 70px; font-size: 10px; } -.gtaskheading { text-align: center; } -.gtaskname div, /* needed for IE8 */ -.gtaskname { min-width: 170px; max-width: 170px; width: 170px; font-size: 9px; border-left: none; } -.gselector { text-align: left; white-space: nowrap; min-width: 170px; max-width: 170px; width: 170px; } +/* needed for IE8 */ + +.gstartdate div, +.gplanstartdate, +.gplanenddate, +.gstartdate, +.genddate { + text-align: center; + min-width: 105px; + width: 105px; + max-width: 105px; + font-size: 10px; +} + +.gtaskheading { + text-align: center; +} + +.gtaskname div, +/* needed for IE8 */ + +.gtaskname { + min-width: 170px; + max-width: 170px; + width: 170px; + font-size: 9px; + border-left: none; +} + +.gtaskheading, +.gtaskname { + text-overflow: ellipsis; + overflow: hidden; +} + +.gtaskname.gtaskeditable div, +.gtaskname.gtaskeditable { + overflow: unset; +} + +.gselector { + text-align: left; + white-space: nowrap; + min-width: 170px; + max-width: 170px; + width: 170px; +} + +.gformlabel { + position: relative; + top: 0px; + cursor: pointer; + border: #ffffff 1px solid; + margin-left: 2px; + padding-left: 2px; + padding-right: 2px; +} -.gformlabel { position:relative; top:0px; cursor:pointer; border: #ffffff 1px solid; margin-left: 2px; padding-left: 2px; padding-right: 2px; } span.gformlabel:hover, -span.gselected { background-color: #dbecff; border: #cccccc 1px solid;} +span.gselected { + background-color: #dbecff; + border: #cccccc 1px solid; +} -span.gfoldercollapse { color:#000000; cursor:pointer; font-weight:bold; font-size: 12px; font-family: Courier, "Courier New", monospace; } +span.gfoldercollapse { + color: #000000; + cursor: pointer; + font-weight: bold; + font-size: 12px; + font-family: Courier, "Courier New", monospace; +} .gtasktableh, -.gtasktable { border-right: #efefef 1px solid; } -.gcharttable { border: #efefef 1px solid; } /* for some reason firefox needs this */ +.gtasktable { + border-right: #efefef 1px solid; + width: 100%; +} + +.gcharttableh, +.gtasktableh, +.gtasktable, +.gcharttable { + border-collapse: collapse; +} + +.gcharttable, +.gcharttableh { + table-layout: fixed; +} + +.gcharttable { + border: #efefef 1px solid; +} + +/* for some reason firefox needs this */ /* Differentiate Group, Milestone and Ordinary task items (applied to row) */ -.ggroupitem { background-color: #fbfbfb; font-weight: bold; } + +.ggroupitem { + background-color: #fbfbfb; + font-weight: bold; +} + .gmileitem, -.glineitem { background-color: #ffffff; } +.glineitem { + background-color: #ffffff; +} /* highlight row (applied to row) */ -.gitemhighlight td { background-image: none; background-color: #fffde5;} + +.gitemhighlight td { + background-image: none; + background-color: #fffde5; +} /* task bar caption text styles */ + .gmilecaption, .ggroupcaption, -.gcaption { font-weight: normal; font-size: 9px; text-align: left; white-space: nowrap; top:1px; position: absolute; top:2px; } +.gcaption { + font-weight: normal; + font-size: 9px; + text-align: left; + white-space: nowrap; + top: 1px; + position: absolute; + top: 2px; +} .ggroupcaption, -.gcaption { right: -126px; } +.gcaption { + right: -126px; +} /* Task complete %age bar shared attributes */ -.gtaskcomplete { float:left; overflow: hidden; } + +.gtaskcomplete { + float: left; + overflow: hidden; +} /* Task complete %age bar */ -.gtaskcomplete { height:5px; background-color:#000000; margin-top:4px; opacity:0.4; filter: alpha(opacity=40); } + +.gtaskcomplete { + height: 5px; + background-color: #000000; + margin-top: 4px; + opacity: 0.4; + filter: alpha(opacity=40); +} /* Milestones */ -.gmilestone { font-size: 14px; position: absolute; top: -2px; } -.gmdtop { top: 2px; overflow: hidden; width:0px; height:0px; border-bottom: 5px solid black; border-left: 5px solid transparent; border-top: 5px solid transparent; border-right: 5px solid transparent;} -.gmdbottom { top: 2px; overflow: hidden; width:0px; height:0px; border-top: 5px solid black; border-left: 5px solid transparent; border-bottom: 5px solid transparent; border-right: 5px solid transparent;} + +.gmilestone { + font-size: 14px; + position: absolute; + top: -2px; +} + +.gmdtop { + top: 2px; + overflow: hidden; + width: 0px; + height: 0px; + border-bottom: 5px solid black; + border-left: 5px solid transparent; + border-top: 5px solid transparent; + border-right: 5px solid transparent; +} + +.gmdbottom { + top: 2px; + overflow: hidden; + width: 0px; + height: 0px; + border-top: 5px solid black; + border-left: 5px solid transparent; + border-bottom: 5px solid transparent; + border-right: 5px solid transparent; +} /* Task bar shared attributes */ + .ggroupblack, .gtaskblue, .gtaskred, .gtaskgreen, .gtaskyellow, .gtaskpurple, -.gtaskpink { height: 13px; filter: alpha(opacity=90); opacity:0.9; margin-top: 1px; } +.gtaskpink { + height: 13px; + filter: alpha(opacity=90); + opacity: 0.9; + margin-top: 1px; +} /* Task bars - ggroupblack is set as the default class on the task if it is undefined */ -.ggroupblack { height: 7px; background: #000000; margin-top: 2px; } -.ggroupblackendpointleft { overflow: hidden; width:0px; height:0px; top: 2px; border-top: 4px solid black; border-left: 4px solid transparent; border-bottom: 4px solid transparent; border-right: 4px solid transparent; float: left; } -.ggroupblackendpointright { overflow: hidden; width:0px; height:0px; top: 2px; border-top: 4px solid black; border-left: 4px solid transparent; border-bottom: 4px solid transparent; border-right: 4px solid transparent; float: right; } -.ggroupblackcomplete { float:left; overflow: hidden; height:3px; filter: alpha(opacity=80); opacity:0.8; background-color:#777777; margin-top:2px; margin-bottom: 2px; } + +.ggroupblack { + height: 7px; + background: #000000; + margin-top: 2px; +} + +.ggroupblackendpointleft { + overflow: hidden; + width: 0px; + height: 0px; + top: 2px; + border-top: 4px solid black; + border-left: 4px solid transparent; + border-bottom: 4px solid transparent; + border-right: 4px solid transparent; + float: left; +} + +.ggroupblackendpointright { + overflow: hidden; + width: 0px; + height: 0px; + top: 2px; + border-top: 4px solid black; + border-left: 4px solid transparent; + border-bottom: 4px solid transparent; + border-right: 4px solid transparent; + float: right; +} + +.ggroupblackcomplete { + float: left; + overflow: hidden; + height: 3px; + filter: alpha(opacity=80); + opacity: 0.8; + background-color: #777777; + margin-top: 2px; + margin-bottom: 2px; +} + .gtaskblue { - background: rgb(58,132,195); /* Old browsers */ - background: linear-gradient(to bottom, rgba(58,132,195,1) 0%,rgba(65,154,214,1) 20%,rgba(75,184,240,1) 40%,rgba(58,139,194,1) 70%,rgba(38,85,139,1) 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4bb8f0', endColorstr='#3a84c3',GradientType=0 ); /* IE6-9 */ + background: rgb(58, 132, 195); + /* Old browsers */ + background: linear-gradient(to bottom, rgba(58, 132, 195, 1) 0%, rgba(65, 154, 214, 1) 20%, rgba(75, 184, 240, 1) 40%, rgba(58, 139, 194, 1) 70%, rgba(38, 85, 139, 1) 100%); + /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#4bb8f0', endColorstr='#3a84c3', GradientType=0); + /* IE6-9 */ } + .gtaskred { - background: rgb(196,58,58); /* Old browsers */ - background: linear-gradient(to bottom, rgba(196,58,58,1) 0%,rgba(211,65,65,1) 20%,rgba(239,76,76,1) 40%,rgba(196,58,58,1) 70%,rgba(135,37,37,1) 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ef4c4c', endColorstr='#c43a3a',GradientType=0 ); /* IE6-9 */ + background: rgb(196, 58, 58); + /* Old browsers */ + background: linear-gradient(to bottom, rgba(196, 58, 58, 1) 0%, rgba(211, 65, 65, 1) 20%, rgba(239, 76, 76, 1) 40%, rgba(196, 58, 58, 1) 70%, rgba(135, 37, 37, 1) 100%); + /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ef4c4c', endColorstr='#c43a3a', GradientType=0); + /* IE6-9 */ } + .gtaskgreen { - background: rgb(80,193,58); /* Old browsers */ - background: linear-gradient(to bottom, rgba(80,193,58,1) 0%,rgba(88,209,64,1) 20%,rgba(102,237,75,1) 40%,rgba(80,193,58,1) 70%,rgba(53,132,37,1) 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#66ED4B', endColorstr='#50c13a',GradientType=0 ); /* IE6-9 */ + background: rgb(80, 193, 58); + /* Old browsers */ + background: linear-gradient(to bottom, rgba(80, 193, 58, 1) 0%, rgba(88, 209, 64, 1) 20%, rgba(102, 237, 75, 1) 40%, rgba(80, 193, 58, 1) 70%, rgba(53, 132, 37, 1) 100%); + /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#66ED4B', endColorstr='#50c13a', GradientType=0); + /* IE6-9 */ } + .gtaskyellow { - background: rgb(247,228,56); /* Old browsers */ - background: linear-gradient(to bottom, rgba(247,228,56,1) 0%,rgba(239,239,55,1) 20%,rgba(255,255,58,1) 40%,rgba(242,236,55,1) 70%,rgba(241,218,54,1) 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffff3a', endColorstr='#f7e438',GradientType=0 ); /* IE6-9 */ + background: rgb(247, 228, 56); + /* Old browsers */ + background: linear-gradient(to bottom, rgba(247, 228, 56, 1) 0%, rgba(239, 239, 55, 1) 20%, rgba(255, 255, 58, 1) 40%, rgba(242, 236, 55, 1) 70%, rgba(241, 218, 54, 1) 100%); + /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff3a', endColorstr='#f7e438', GradientType=0); + /* IE6-9 */ } + .gtaskpurple { - background: rgb(193,58,193); /* Old browsers */ - background: linear-gradient(to bottom, rgba(193,58,193,1) 0%,rgba(211,65,211,1) 20%,rgba(239,76,239,1) 40%,rgba(193,58,193,1) 70%,rgba(137,38,137,1) 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ef4cef', endColorstr='#892689',GradientType=0 ); /* IE6-9 */ + background: rgb(193, 58, 193); + /* Old browsers */ + background: linear-gradient(to bottom, rgba(193, 58, 193, 1) 0%, rgba(211, 65, 211, 1) 20%, rgba(239, 76, 239, 1) 40%, rgba(193, 58, 193, 1) 70%, rgba(137, 38, 137, 1) 100%); + /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ef4cef', endColorstr='#892689', GradientType=0); + /* IE6-9 */ } + .gtaskpink { - background: rgb(249,177,245); /* Old browsers */ - background: linear-gradient(to bottom, rgba(249,177,245,1) 0%,rgba(247,192,243,1) 20%,rgba(247,202,244,1) 40%,rgba(249,192,246,1) 70%,rgba(252,174,247,1) 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7caf4', endColorstr='#fcaef7',GradientType=0 ); /* IE6-9 */ + background: rgb(249, 177, 245); + /* Old browsers */ + background: linear-gradient(to bottom, rgba(249, 177, 245, 1) 0%, rgba(247, 192, 243, 1) 20%, rgba(247, 202, 244, 1) 40%, rgba(249, 192, 246, 1) 70%, rgba(252, 174, 247, 1) 100%); + /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f7caf4', endColorstr='#fcaef7', GradientType=0); + /* IE6-9 */ } + .gtaskbluecomplete, .gtaskredcomplete, .gtaskgreencomplete, .gtaskyellowcomplete, .gtaskpurplecomplete, -.gtaskpinkcomplete { float:left; overflow: hidden; height:5px; filter: alpha(opacity=40); opacity:0.4; background-color: #000000; margin-top:4px; } - -/* Printer friendly styles - we could use these all the time but they are not as pretty! */ -/* note that "@media print" is not supported in IE6 or 7. Fully patched IE8 should be OK */ -@media print { - .ggroupblack { height:0px; border-top: 7px solid; border-color: #000000; } - .gtaskblue { height:0px; border-top: 13px solid; border-color: rgb(58,132,195); } - .gtaskred { height:0px; border-top: 13px solid; border-color: rgb(196,58,58); } - .gtaskgreen { height:0px; border-top: 13px solid; border-color: rgb(80,193,58); } - .gtaskyellow { height:0px; border-top: 13px solid; border-color: rgb(247,228,56); } - .gtaskpurple { height:0px; border-top: 13px solid; border-color: rgb(193,58,193); } - .gtaskpink { height:0px; border-top: 13px solid; border-color: rgb(249,177,245); } - - .gtaskbluecomplete, - .gtaskredcomplete, - .gtaskgreencomplete, - .gtaskyellowcomplete, - .gtaskpurplecomplete, - .gtaskpinkcomplete { height:0px; filter: alpha(opacity=40); opacity:0.4; margin-top: -9px; border-top: 5px solid; border-color: #000000; } - .ggroupblackcomplete { height: 0px; filter: alpha(opacity=80); opacity:0.8; margin-top:-5px; border-top:3px solid; border-color:#777777; } +.gtaskpinkcomplete { + float: left; + overflow: hidden; + height: 5px; + filter: alpha(opacity=40); + opacity: 0.4; + background-color: #000000; + margin-top: 4px; } /* END Task bar styles */ -.glinev { border-left: 1px solid; width: 0px; } -.glineh { border-top: 1px solid; height: 0px; } + +.glinev { + border-left: 1px solid; + width: 0px; +} + +.glineh { + border-top: 1px solid; + height: 0px; +} .gDepFS, .gDepSS, .gDepSF, -.gDepFF { border-color: #ff0000; } +.gDepFF { + border-color: #ff0000; +} + .gDepFSArw, -.gDepSSArw { overflow: hidden; width:0px; height:0px; border-bottom: 4px solid transparent; border-left: 4px solid #ff0000; border-top: 4px solid transparent; border-right: 4px solid transparent;} +.gDepSSArw { + overflow: hidden; + width: 0px; + height: 0px; + border-bottom: 4px solid transparent; + border-left: 4px solid #ff0000; + border-top: 4px solid transparent; + border-right: 4px solid transparent; +} + .gDepFFArw, -.gDepSFArw { overflow: hidden; width:0px; height:0px; border-bottom: 4px solid transparent; border-left: 4px solid transparent; border-top: 4px solid transparent; border-right: 4px solid #ff0000;} -.gCurDate { border-color: #0000ff; } +.gDepSFArw { + overflow: hidden; + width: 0px; + height: 0px; + border-bottom: 4px solid transparent; + border-left: 4px solid transparent; + border-top: 4px solid transparent; + border-right: 4px solid #ff0000; +} +.gCurDate { + border-color: #0000ff; +} -div.gtaskbarcontainer { z-index: 1; position: absolute; top: 0px } +div.gtaskbarcontainer { + z-index: 1; + position: absolute; + top: 0px +} -.JSGanttToolTip {position: absolute; display: block; z-index: 2;} -.JSGanttToolTipcont {font-family: tahoma, arial, verdana; font-size: 10px; display: block; background: #ffffff; color: #656565} -.gTaskInfo {background: #dbecff; width: 400px; border: #656565 1px solid; border-radius: 10px; padding: 4px 6px 4px 6px; float: left;} -.gTtTitle {display: block; font-size: 12px; font-weight: bold; color: #404040; margin-left: 4px; margin-bottom: 1em;} -.gTaskLabel {font-size: 11px; font-weight: bold; color: #656565; margin-left: 4px;} -.gTaskText {position:absolute; left: 90px; padding-top: 1px; font-size: 10px; font-weight: normal; color: #656565;} -.gTaskNotes {font-size: 11px; font-weight: normal; color: #323232; padding: 0 15px; display: block;} -.gTIn {padding-top: 10px;} +.textbar { + word-break: break-all; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 40%; + display: inline-block; +} -.gantt { min-width: 632px; /* 2x LC width */ } -.gchartcontainer { /* padding-left: 532px; LC width */ line-height: 1; /* Overrides inherited CSS (e.g. from Bootstrap) */ } -.gcontainercol { position: relative; } /* Add a max-height value here if wanted */ -.glistgrid { width: 532px; /* LC width */ float: left; /* LC width */ padding-right: 0px; padding-left: 0px; padding-bottom: 0px; padding-top: 0px; background-color: #ffffff; overflow: hidden; } -.glistlbl { width: 532px; /* LC width */ float: left; /* LC width */ padding-right: 0px; padding-left: 0px; padding-bottom: 0px; padding-top: 0px; background-color: #ffffff; overflow: hidden; } -.glabelfooter { clear: both; } -.ggridfooter { clear: both; } +.JSGanttToolTip { + position: absolute; + display: block; + z-index: 10000; +} -.rhscrpad { width: 150px; position: absolute; top: 0px; height: 1px; } +.JSGanttToolTipcont { + font-family: tahoma, arial, verdana; + font-size: 10px; + display: block; + background: #ffffff; + color: #656565 +} -.gchartgrid { padding-right: 0px; padding-left: 0px; padding-bottom: 0px; padding-top: 0px; background-color: #ffffff; position: relative; overflow: auto; min-height: 0%; } -.gchartlbl { padding-right: 0px; padding-left: 0px; padding-bottom: 0px; padding-top: 0px; background-color: #ffffff; position: relative; overflow: hidden; } +.gTaskInfo { + background: #dbecff; + width: 400px; + border: #656565 1px solid; + border-radius: 10px; + padding: 4px 6px 4px 6px; + float: left; +} + +.gTtTitle { + display: block; + font-size: 12px; + font-weight: bold; + color: #404040; + margin-left: 4px; + margin-bottom: 1em; +} + +.gTaskLabel { + font-size: 11px; + font-weight: bold; + color: #656565; + margin-left: 4px; +} + +.gTaskText { + position: absolute; + left: 90px; + padding-top: 1px; + font-size: 10px; + font-weight: normal; + color: #656565; +} + +.gTaskNotes { + font-size: 11px; + font-weight: normal; + color: #323232; + padding: 0 15px; + display: block; +} + +.gTIn { + padding-top: 10px; +} + +.gantt { + min-width: 632px; + /* 2x LC width */ +} + +.gchartcontainer { + /* padding-left: 532px; LC width */ + line-height: 1; + /* Overrides inherited CSS (e.g. from Bootstrap) */ + display: flex; +} + +.gcontainercol { + position: relative; +} + +/* Add a max-height value here if wanted */ + +.glistgrid { + min-width: 132px; + /* LC width */ + float: left; + /* LC width */ + padding-right: 0px; + padding-left: 0px; + padding-bottom: 0px; + padding-top: 0px; + background-color: #ffffff; + overflow: hidden; +} + +.glistlbl { + min-width: 132px; + /* LC width */ + float: left; + /* LC width */ + padding-right: 0px; + padding-left: 0px; + padding-bottom: 0px; + padding-top: 0px; + background-color: #ffffff; + overflow: hidden; + flex: none; +} + +.glistlbl, +.gchartgrid { + display: flex; + flex-direction: column; +} + +.glabelfooter { + clear: both; +} + +.ggridfooter { + clear: both; +} + +.rhscrpad { + width: 150px; + position: absolute; + top: 0px; + height: 1px; +} + +.gchartgrid { + padding-right: 0px; + padding-left: 0px; + padding-bottom: 0px; + padding-top: 0px; + background-color: #ffffff; + position: relative; + /* overflow: auto; */ + overflow: unset; + overflow-y: auto; + min-height: 0%; +} + +.gchartlbl { + padding-right: 0px; + padding-left: 0px; + padding-bottom: 0px; + padding-top: 0px; + background-color: #ffffff; + position: relative; + overflow: hidden; + flex: none; +} /* Old Internet Explorer version hacks */ -.gantt { _height: 100% } /* otherwise the chart disappears! */ -div .gantt { /* _width: 1064px; ie6 fixed width */ } + +.gantt { + _height: 100% +} + +/* otherwise the chart disappears! */ + +div .gantt { + /* _width: 1064px; ie6 fixed width */ +} + div.gchartlbl, -div.gchartgrid {/* _width: 532px; */} /* ie6 fixed width */ +div.gchartgrid { + /* _width: 532px; */ +} + +/* ie6 fixed width */ + div.glistlbl, div.glistgrid { - *right: 0px; /* ie7 pulls the content too far left with the negative margin */ - _right: 532px; /* but ie6 fixed width needs this */ - _margin-left: -532px; /* ie6 fixed width */ - float: left; + *right: 0px; + /* ie7 pulls the content too far left with the negative margin */ + _right: 532px; + /* but ie6 fixed width needs this */ + _margin-left: -532px; + /* ie6 fixed width */ + float: left; } -div.gchartgrid { *padding-bottom: 20px; *overflow-y: hidden; } /* variable height design, no need for vertical scroll */ -td.gmajorheading div { *overflow: hidden; } /* stops resizing fixed width columns if the text is too wide */ -td.gspanning div { *overflow: hidden; } /* stops resizing fixed width columns if the text is too wide */ + +div.gchartgrid { + *padding-bottom: 20px; + *overflow-y: hidden; +} + +/* variable height design, no need for vertical scroll */ + +td.gmajorheading div { + *overflow: hidden; +} + +/* stops resizing fixed width columns if the text is too wide */ + +td.gspanning div { + *overflow: hidden; +} + +/* stops resizing fixed width columns if the text is too wide */ /* border transparency tricks */ -.ggroupblackendpointleft { _border-top: 4px solid black; _border-left: 4px solid pink; _border-bottom: 4px solid pink; _border-right: 4px solid pink; _filter: chroma(color=pink); } -.ggroupblackendpointright { _border-top: 4px solid black; _border-left: 4px solid pink; _border-bottom: 4px solid pink; _border-right: 4px solid pink;_filter: chroma(color=pink); } -.gmdtop { _border-left: 5px solid pink; _border-top: 5px solid pink; _border-right: 5px solid pink; _filter: chroma(color=pink);} -.gmdbottom { _border-left: 5px solid pink; _border-bottom: 5px solid pink; _border-right: 5px solid pink; _filter: chroma(color=pink);} +.ggroupblackendpointleft { + _border-top: 4px solid black; + _border-left: 4px solid pink; + _border-bottom: 4px solid pink; + _border-right: 4px solid pink; + _filter: chroma(color=pink); +} + +.ggroupblackendpointright { + _border-top: 4px solid black; + _border-left: 4px solid pink; + _border-bottom: 4px solid pink; + _border-right: 4px solid pink; + _filter: chroma(color=pink); +} + +.gmdtop { + _border-left: 5px solid pink; + _border-top: 5px solid pink; + _border-right: 5px solid pink; + _filter: chroma(color=pink); +} + +.gmdbottom { + _border-left: 5px solid pink; + _border-bottom: 5px solid pink; + _border-right: 5px solid pink; + _filter: chroma(color=pink); +} .gDepFSArw, -.gDepSSArw { _border-bottom: 4px solid pink; _border-top: 4px solid pink; _border-right: 4px solid pink; _filter: chroma(color=pink);} +.gDepSSArw { + _border-bottom: 4px solid pink; + _border-top: 4px solid pink; + _border-right: 4px solid pink; + _filter: chroma(color=pink); +} + .gDepFFArw, -.gDepSFArw { _border-bottom: 4px solid pink; _border-left: 4px solid pink; _border-top: 4px solid pink; _filter: chroma(color=pink);} +.gDepSFArw { + _border-bottom: 4px solid pink; + _border-left: 4px solid pink; + _border-top: 4px solid pink; + _filter: chroma(color=pink); +} /* Workaround for odd bug in old versions of Opera - no other browser needs this */ -.glinediv {position: absolute; top: 0px; left: 0px;} + +.glinediv { + position: absolute; + top: 0px; + left: 0px; + z-index: 1px; +} + +.gantt-inputtable { + max-width: 100%; + max-height: 16px; +} + +.gadditional { + border: #efefef 1px solid; +} + +.gadditional, +.gres, +.gdur, +.gcomp, +.gcost { + text-align: center; + width: 70px; + max-width: 70px; + min-width: 70px; + font-size: 10px; +} + +@media print { + + /* All your print styles go here */ + html, + .gchartgrid { + overflow: unset !important; + } + + .glistgrid { + float: none !important; + } +} /* if using setUseSingleCell(1) the following is a suggested set of CSS3 styles to recreate the table grid - won't work on old browsers .ggrouphour td, @@ -241,3 +806,177 @@ td.gspanning div { *overflow: hidden; } /* stops resizing fixed width columns if .gmilequarter td, .gitemquarter td { background-size: 19px 1px; background-image: linear-gradient(to left, #efefef, transparent 1px, transparent 18px); width: 100%; height: 19px; } */ + +.gmain { + height: 100%; + + display: flex; + flex-direction: column; +} + +.gmainright { + overflow: hidden; + flex: auto; +} + +.gmainleft { + overflow: hidden; + flex: 0 0 50%; +} + +.gtasktableh tr, +.gcharttable tr, +.gcharttableh tr, +.gtasktable tr { + height: 22px !important; +} + +.gtasktableouterwrapper { + overflow: hidden; +} + +.gtasktablewrapper { + overflow-y: scroll; + flex: auto; + height: 100%; +} + +.chartgrid { + overflow: auto; + flex: auto; +} + +/* .gtasktable { + margin-bottom: 14px; +} */ + +/* Printer friendly styles - we could use these all the time but they are not as pretty! */ + +/* note that "@media print" is not supported in IE6 or 7. Fully patched IE8 should be OK */ + +@media print { + .ggroupblack { + height: 0px; + border-top: 7px solid; + border-color: #000000; + } + + .gtaskblue { + height: 0px; + border-top: 13px solid; + border-color: rgb(58, 132, 195); + } + + .gtaskred { + height: 0px; + border-top: 13px solid; + border-color: rgb(196, 58, 58); + } + + .gtaskgreen { + height: 0px; + border-top: 13px solid; + border-color: rgb(80, 193, 58); + } + + .gtaskyellow { + height: 0px; + border-top: 13px solid; + border-color: rgb(247, 228, 56); + } + + .gtaskpurple { + height: 0px; + border-top: 13px solid; + border-color: rgb(193, 58, 193); + } + + .gtaskpink { + height: 0px; + border-top: 13px solid; + border-color: rgb(249, 177, 245); + } + + .gtaskbluecomplete, + .gtaskredcomplete, + .gtaskgreencomplete, + .gtaskyellowcomplete, + .gtaskpurplecomplete, + .gtaskpinkcomplete { + height: 0px; + filter: alpha(opacity=40); + opacity: 0.4; + margin-top: -9px; + border-top: 5px solid; + border-color: #000000; + } + + .ggroupblackcomplete { + height: 0px; + filter: alpha(opacity=80); + opacity: 0.8; + margin-top: -5px; + border-top: 3px solid; + border-color: #777777; + } + + .gmainright, + .gmainleft { + overflow: hidden; + width: auto; + } + + table { + page-break-after: auto + } + + tr { + page-break-inside: avoid; + page-break-after: auto + } + + td { + page-break-inside: avoid; + page-break-after: auto + } + + thead { + display: table-header-group + } + + tfoot { + display: table-footer-group + } +} + +/* Fix for OSx trackpad hiding the scroll bars*/ +.frame::-webkit-scrollbar { + -webkit-appearance: none; +} + +.frame::-webkit-scrollbar:vertical { + width: 11px; +} + +.frame::-webkit-scrollbar:horizontal { + height: 11px; +} + +.frame::-webkit-scrollbar-thumb { + border-radius: 8px; + border: 2px solid white; + /* should match background, can't be transparent */ + background-color: rgba(0, 0, 0, .5); +} + +.frame::-webkit-scrollbar-track { + background-color: #fff; + border-radius: 8px; +} + +.gscrollbar-calculation-container { + visibility: hidden; + overflow: scroll; + -ms-overflow-style: scrollbar; + display: block; +} \ No newline at end of file diff --git a/htdocs/includes/jsgantt/jsgantt.js b/htdocs/includes/jsgantt/jsgantt.js index 04921b92405..be9a20611f2 100644 --- a/htdocs/includes/jsgantt/jsgantt.js +++ b/htdocs/includes/jsgantt/jsgantt.js @@ -1,2577 +1,5064 @@ -/* - _ ___ _ _ _____ _ - (_)___ / _ \__ _ _ __ | |_| |_ \_ \_ __ ___ _ __ _ __ _____ _____ __| | - | / __| / /_\/ _` | '_ \| __| __| / /\/ '_ ` _ \| '_ \| '__/ _ \ \ / / _ \/ _` | - | \__ \/ /_\\ (_| | | | | |_| |_/\/ /_ | | | | | | |_) | | | (_) \ V / __/ (_| | - _/ |___/\____/\__,_|_| |_|\__|\__\____/ |_| |_| |_| .__/|_| \___/ \_/ \___|\__,_| - |__/ |_| - jsGanttImproved 1.7.5.4 - - The current version of this code can be found at https://github.com/jsGanttImproved/jsgantt-improved/ - - * Copyright (c) 2013-2017, Paul Geldart, Eduardo Rodrigues and Ricardo Cardoso. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Paul Geldart, Eduardo Rodrigues and Ricardo Cardoso nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY PAUL GELDART, EDUARDO RODRIGUES AND RICARDO CARDOSO ''AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL PAUL GELDART, EDUARDO RODRIGUES AND RICARDO CARDOSO BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - This project is based on jsGantt 1.2, (which can be obtained from - https://code.google.com/p/jsgantt/) and remains under the original BSD license. - The original project license follows: - - Copyright (c) 2009, Shlomy Gantz BlueBrick Inc. All rights reserved. - - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Shlomy Gantz or BlueBrick Inc. nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY SHLOMY GANTZ/BLUEBRICK INC. ''AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL SHLOMY GANTZ/BLUEBRICK INC. BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -var JSGantt; if (!JSGantt) JSGantt={}; - -var vBenchTime=new Date().getTime(); - -JSGantt.isIE=function () -{ - if(typeof document.all!='undefined') - { - if ('pageXOffset' in window) return false; // give IE9 and above the benefit of the doubt! - else return true; - } - else return false; -}; - -JSGantt.TaskItem=function(pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGantt) -{ - - var vID=parseInt(document.createTextNode(pID).data); - var vName=document.createTextNode(pName).data; - var vStart=new Date(0); - var vEnd=new Date(0); - var vGroupMinStart=null; - var vGroupMinEnd=null; - var vClass=document.createTextNode(pClass).data; - var vLink=document.createTextNode(pLink).data; - var vMile=parseInt(document.createTextNode(pMile).data); - var vRes=document.createTextNode(pRes).data; - var vComp=parseFloat(document.createTextNode(pComp).data); - var vGroup=parseInt(document.createTextNode(pGroup).data); - var vParent=document.createTextNode(pParent).data; - var vOpen=(vGroup==2)?1:parseInt(document.createTextNode(pOpen).data); - var vDepend=new Array(); - var vDependType=new Array(); - var vCaption=document.createTextNode(pCaption).data; - var vDuration=''; - var vLevel=0; - var vNumKid=0; - var vWeight=0; - var vVisible=1; - var vSortIdx=0; - var vToDelete=false; - var x1, y1, x2, y2; - var vNotes; - var vParItem=null; - var vCellDiv=null; - var vGantt=(pGantt instanceof JSGantt.GanttChart)? pGantt : g; //hack for backwards compatibility - var vBarDiv=null; - var vTaskDiv=null; - var vListChildRow=null; - var vChildRow=null; - var vGroupSpan=null; - - vNotes=document.createElement('span'); - vNotes.className='gTaskNotes'; - if (pNotes!=null) - { - vNotes.innerHTML=pNotes; - JSGantt.stripUnwanted(vNotes); - } - - if (pStart!=null && pStart!='') - { - vStart=(pStart instanceof Date)?pStart:JSGantt.parseDateStr(document.createTextNode(pStart).data,vGantt.getDateInputFormat()); - vGroupMinStart=vStart; - } - - if (pEnd!=null && pEnd!='') - { - vEnd =(pEnd instanceof Date)?pEnd:JSGantt.parseDateStr(document.createTextNode(pEnd).data,vGantt.getDateInputFormat()); - vGroupMinEnd=vEnd; - } - - if (pDepend!=null) - { - var vDependStr=pDepend+''; - var vDepList=vDependStr.split(','); - var n=vDepList.length; - - for(var k=0;k1) - { - vFormatArr[j++]=arguments[i].toLowerCase(); - var vRegExp=new RegExp('(?:^|\s)'+arguments[i]+'(?!\S)', 'g'); - vValidFormats=vValidFormats.replace(vRegExp, ''); - } - } - }; - this.setShowRes=function(pVal){vShowRes=pVal;}; - this.setShowDur=function(pVal){vShowDur=pVal;}; - this.setShowComp=function(pVal){vShowComp=pVal;}; - this.setShowStartDate=function(pVal){vShowStartDate=pVal;}; - this.setShowEndDate=function(pVal){vShowEndDate=pVal;}; - this.setShowTaskInfoRes=function(pVal){vShowTaskInfoRes=pVal;}; - this.setShowTaskInfoDur=function(pVal){vShowTaskInfoDur=pVal;}; - this.setShowTaskInfoComp=function(pVal){vShowTaskInfoComp=pVal;}; - this.setShowTaskInfoStartDate=function(pVal){vShowTaskInfoStartDate=pVal;}; - this.setShowTaskInfoEndDate=function(pVal){vShowTaskInfoEndDate=pVal;}; - this.setShowTaskInfoNotes=function(pVal){vShowTaskInfoNotes=pVal;}; - this.setShowTaskInfoLink=function(pVal){vShowTaskInfoLink=pVal;}; - this.setShowEndWeekDate=function(pVal){vShowEndWeekDate=pVal;}; - this.setShowSelector=function() - { - var vValidSelectors='top bottom'; - vShowSelector=new Array(); - for(var i=0, j=0; i1) - { - vShowSelector[j++]=arguments[i].toLowerCase(); - var vRegExp=new RegExp('(?:^|\s)'+arguments[i]+'(?!\S)', 'g'); - vValidSelectors=vValidSelectors.replace(vRegExp, ''); - } - } - }; - this.setShowDeps=function(pVal){vShowDeps=pVal;}; - this.setDateInputFormat=function(pVal){vDateInputFormat=pVal;}; - this.setDateTaskTableDisplayFormat=function(pVal){vDateTaskTableDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setDateTaskDisplayFormat=function(pVal){vDateTaskDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setHourMajorDateDisplayFormat=function(pVal){vHourMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setHourMinorDateDisplayFormat=function(pVal){vHourMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setDayMajorDateDisplayFormat=function(pVal){vDayMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setDayMinorDateDisplayFormat=function(pVal){vDayMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setWeekMajorDateDisplayFormat=function(pVal){vWeekMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setWeekMinorDateDisplayFormat=function(pVal){vWeekMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setMonthMajorDateDisplayFormat=function(pVal){vMonthMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setMonthMinorDateDisplayFormat=function(pVal){vMonthMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setQuarterMajorDateDisplayFormat=function(pVal){vQuarterMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setQuarterMinorDateDisplayFormat=function(pVal){vQuarterMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);}; - this.setCaptionType=function(pType){vCaptionType=pType;}; - this.setFormat=function(pFormat) - { - vFormat=pFormat; - this.Draw(); - }; - this.setMinGpLen=function(pMinGpLen){vMinGpLen=pMinGpLen;}; - this.setScrollTo=function(pDate){vScrollTo=pDate;}; - this.setHourColWidth=function(pWidth){vHourColWidth=pWidth;}; - this.setDayColWidth=function(pWidth){vDayColWidth=pWidth;}; - this.setWeekColWidth=function(pWidth){vWeekColWidth=pWidth;}; - this.setMonthColWidth=function(pWidth){vMonthColWidth=pWidth;}; - this.setQuarterColWidth=function(pWidth){vQuarterColWidth=pWidth;}; - this.setRowHeight=function(pHeight){vRowHeight=pHeight;}; - this.setLang=function(pLang){if(vLangs[pLang])vLang=pLang;}; - this.setChartBody=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vChartBody=pDiv;}; - this.setChartHead=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vChartHead=pDiv;}; - this.setListBody=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vListBody=pDiv;}; - this.setChartTable=function(pTable){if(typeof HTMLTableElement !== 'function' || pTable instanceof HTMLTableElement)vChartTable=pTable;}; - this.setLines=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vLines=pDiv;}; - this.setTimer=function(pVal){vTimer=pVal*1;}; - this.addLang=function(pLang, pVals){ - if(!vLangs[pLang]) - { - vLangs[pLang]=new Object(); - for(var vKey in vLangs['en'])vLangs[pLang][vKey]=(pVals[vKey])?document.createTextNode(pVals[vKey]).data:vLangs['en'][vKey]; - } - }; - - this.getDivId=function(){return vDivId;}; - this.getUseFade=function(){return vUseFade;}; - this.getUseMove=function(){return vUseMove;}; - this.getUseRowHlt=function(){return vUseRowHlt;}; - this.getUseToolTip=function(){return vUseToolTip;}; - this.getUseSort=function(){return vUseSort;}; - this.getUseSingleCell=function(){return vUseSingleCell;}; - this.getFormatArr=function(){return vFormatArr;}; - this.getShowRes=function(){return vShowRes;}; - this.getShowDur=function(){return vShowDur;}; - this.getShowComp=function(){return vShowComp;}; - this.getShowStartDate=function(){return vShowStartDate;}; - this.getShowEndDate=function(){return vShowEndDate;}; - this.getShowTaskInfoRes=function(){return vShowTaskInfoRes;}; - this.getShowTaskInfoDur=function(){return vShowTaskInfoDur;}; - this.getShowTaskInfoComp=function(){return vShowTaskInfoComp;}; - this.getShowTaskInfoStartDate=function(){return vShowTaskInfoStartDate;}; - this.getShowTaskInfoEndDate=function(){return vShowTaskInfoEndDate;}; - this.getShowTaskInfoNotes=function(){return vShowTaskInfoNotes;}; - this.getShowTaskInfoLink=function(){return vShowTaskInfoLink;}; - this.getShowEndWeekDate=function(){return vShowEndWeekDate;}; - this.getShowSelector=function(){return vShowSelector;}; - this.getShowDeps=function(){return vShowDeps;}; - this.getDateInputFormat=function(){return vDateInputFormat;}; - this.getDateTaskTableDisplayFormat=function(){return vDateTaskTableDisplayFormat;}; - this.getDateTaskDisplayFormat=function(){return vDateTaskDisplayFormat;}; - this.getHourMajorDateDisplayFormat=function(){return vHourMajorDateDisplayFormat;}; - this.getHourMinorDateDisplayFormat=function(){return vHourMinorDateDisplayFormat;}; - this.getDayMajorDateDisplayFormat=function(){return vDayMajorDateDisplayFormat;}; - this.getDayMinorDateDisplayFormat=function(){return vDayMinorDateDisplayFormat;}; - this.getWeekMajorDateDisplayFormat=function(){return vWeekMajorDateDisplayFormat;}; - this.getWeekMinorDateDisplayFormat=function(){return vWeekMinorDateDisplayFormat;}; - this.getMonthMajorDateDisplayFormat=function(){return vMonthMajorDateDisplayFormat;}; - this.getMonthMinorDateDisplayFormat=function(){return vMonthMinorDateDisplayFormat;}; - this.getQuarterMajorDateDisplayFormat=function(){return vQuarterMajorDateDisplayFormat;}; - this.getQuarterMinorDateDisplayFormat=function(){return vQuarterMinorDateDisplayFormat;}; - this.getCaptionType=function(){return vCaptionType;}; - this.getMinGpLen=function(){return vMinGpLen;}; - this.getScrollTo=function(){return vScrollTo;}; - this.getHourColWidth=function(){return vHourColWidth;}; - this.getDayColWidth=function(){return vDayColWidth;}; - this.getWeekColWidth=function(){return vWeekColWidth;}; - this.getMonthColWidth=function(){return vMonthColWidth;}; - this.getQuarterColWidth=function(){return vQuarterColWidth;}; - this.getRowHeight=function(){return vRowHeight;}; - this.getChartBody=function(){return vChartBody;}; - this.getChartHead=function(){return vChartHead;}; - this.getListBody=function(){return vListBody;}; - this.getChartTable=function(){return vChartTable;}; - this.getLines=function(){return vLines;}; - this.getTimer=function(){return vTimer;}; - - this.CalcTaskXY=function() - { - var vID; - var vList=this.getList(); - var vBarDiv; - var vTaskDiv; - var vParDiv; - var vLeft, vTop, vWidth; - var vHeight=Math.floor((this.getRowHeight()/2)); - - for(var i=0; i=x2 && y1!=y2) vBend=true; - break; - } - - if (vBend) - { - this.sLine(x1,y1,x1+vShort,y1,pClass); - this.sLine(x1+vShort,y1,x1+vShort,y2-vRow,pClass); - this.sLine(x1+vShort,y2-vRow,x2-(vShort*2),y2-vRow,pClass); - this.sLine(x2-(vShort*2),y2-vRow,x2-(vShort*2),y2,pClass); - this.sLine(x2-(vShort*2),y2,x2-(1*vDir),y2,pClass); - } - else if (y1!=y2) - { - this.sLine(x1,y1,x1+vShort,y1,pClass); - this.sLine(x1+vShort,y1,x1+vShort,y2,pClass); - this.sLine(x1+vShort,y2,x2-(1*vDir),y2,pClass); - } - else this.sLine(x1,y1,x2-(1*vDir),y2,pClass); - - var vTmpDiv=this.sLine(x2,y2,x2-3-((vDir<0)?1:0),y2-3-((vDir<0)?1:0),pClass+"Arw"); - vTmpDiv.style.width='0px'; - vTmpDiv.style.height='0px'; - }; - - this.DrawDependencies=function() - { - if (this.getShowDeps()==1) - { - //First recalculate the x,y - this.CalcTaskXY(); - this.clearDependencies(); - - var vList=this.getList(); - for(var i=0; i0 && vList[i].getVisible()==1) - { - for(var k=0;k=0 && vList[vTask].getGroup()!=2) - { - if(vList[vTask].getVisible()==1) - { - if(vDependType[k]=='SS')this.drawDependency(vList[vTask].getStartX()-1,vList[vTask].getStartY(),vList[i].getStartX()-1,vList[i].getStartY(),'SS','gDepSS'); - else if(vDependType[k]=='FF')this.drawDependency(vList[vTask].getEndX(),vList[vTask].getEndY(),vList[i].getEndX(),vList[i].getEndY(),'FF','gDepFF'); - else if(vDependType[k]=='SF')this.drawDependency(vList[vTask].getStartX()-1,vList[vTask].getStartY(),vList[i].getEndX(),vList[i].getEndY(),'SF','gDepSF'); - else if(vDependType[k]=='FS')this.drawDependency(vList[vTask].getEndX(),vList[vTask].getEndY(),vList[i].getStartX()-1,vList[i].getStartY(),'FS','gDepFS'); - } - } - } - } - } - } - // draw the current date line - if (vTodayPx>=0) this.sLine(vTodayPx, 0, vTodayPx, this.getChartTable().offsetHeight-1, 'gCurDate'); - }; - - this.getArrayLocationByID=function(pId) - { - var vList=this.getList(); - for(var i=0; i0) - { - // Process all tasks, reset parent date and completion % if task list has altered - if (vProcessNeeded) JSGantt.processRows(vTaskList, 0, -1, 1, 1, this.getUseSort()); - vProcessNeeded=false; - - // get overall min/max dates plus padding - vMinDate=JSGantt.getMinDate(vTaskList, vFormat); - vMaxDate=JSGantt.getMaxDate(vTaskList, vFormat); - - // Calculate chart width variables. - if(vFormat=='day') vColWidth=vDayColWidth; - else if(vFormat=='week') vColWidth=vWeekColWidth; - else if(vFormat=='month') vColWidth=vMonthColWidth; - else if(vFormat=='quarter') vColWidth=vQuarterColWidth; - else if(vFormat=='hour') vColWidth=vHourColWidth; - - // DRAW the Left-side of the chart (names, resources, comp%) - var vLeftHeader=document.createDocumentFragment(); - - var vTmpDiv=this.newNode(vLeftHeader, 'div', vDivId+'glisthead', 'glistlbl gcontainercol'); - var vTmpTab=this.newNode(vTmpDiv, 'table', null, 'gtasktableh'); - var vTmpTBody=this.newNode(vTmpTab, 'tbody'); - var vTmpRow=this.newNode(vTmpTBody, 'tr'); - this.newNode(vTmpRow, 'td', null, 'gtasklist', '\u00A0'); - var vTmpCell=this.newNode(vTmpRow, 'td', null, 'gspanning gtaskname'); - vTmpCell.appendChild(this.drawSelector('top')); - if(vShowRes==1)this.newNode(vTmpRow, 'td', null, 'gspanning gresource', '\u00A0'); - if(vShowDur==1)this.newNode(vTmpRow, 'td', null, 'gspanning gduration', '\u00A0'); - if(vShowComp==1)this.newNode(vTmpRow, 'td', null, 'gspanning gpccomplete', '\u00A0'); - if(vShowStartDate==1)this.newNode(vTmpRow, 'td', null, 'gspanning gstartdate', '\u00A0'); - if(vShowEndDate==1)this.newNode(vTmpRow, 'td', null, 'gspanning genddate', '\u00A0'); - - vTmpRow=this.newNode(vTmpTBody, 'tr'); - this.newNode(vTmpRow, 'td', null, 'gtasklist', '\u00A0'); - this.newNode(vTmpRow, 'td', null, 'gtaskname', '\u00A0'); - if(vShowRes==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading gresource', vLangs[vLang]['resource']); - if(vShowDur==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading gduration', vLangs[vLang]['duration']); - if(vShowComp==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading gpccomplete', vLangs[vLang]['comp']); - if(vShowStartDate==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading gstartdate', vLangs[vLang]['startdate']); - if(vShowEndDate==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading genddate', vLangs[vLang]['enddate']); - - var vLeftTable=document.createDocumentFragment(); - var vTmpDiv2=this.newNode(vLeftTable, 'div', vDivId+'glistbody', 'glistgrid gcontainercol'); - this.setListBody(vTmpDiv2); - vTmpTab=this.newNode(vTmpDiv2, 'table', null, 'gtasktable'); - vTmpTBody=this.newNode(vTmpTab, 'tbody'); - - for(i=0; i1) - { - vTmpDate.setDate(vTmpDate.getDate()+1); - } - } - else if(vFormat=='quarter') - { - if(vTmpDate<=vMaxDate) - { - vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass); - this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vQuarterMinorDateDisplayFormat,vLangs[vLang]), vColWidth); - vNumCols++; - } - - vTmpDate.setDate(vTmpDate.getDate()+81); - - while(vTmpDate.getDate()>1) vTmpDate.setDate(vTmpDate.getDate()+1); - } - else if(vFormat=='hour') - { - for(i=vTmpDate.getHours();i<24;i++) - { - vTmpDate.setHours(i);//works around daylight savings but may look a little odd on days where the clock goes forward - if(vTmpDate<=vMaxDate) - { - vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass); - this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vHourMinorDateDisplayFormat,vLangs[vLang]), vColWidth); - vNumCols++; - } - } - vTmpDate.setHours(0); - vTmpDate.setDate(vTmpDate.getDate()+1); - } - } - vDateRow=vTmpRow; - - vTaskLeftPx=(vNumCols *(vColWidth+1))+1; - - if(vUseSingleCell!=0 && vUseSingleCell<(vNumCols*vNumRows))vSingleCell=true; - - this.newNode(vTmpDiv, 'div', null, 'rhscrpad', null, null, vTaskLeftPx+1); - - vTmpDiv=this.newNode(vRightHeader, 'div', null, 'glabelfooter'); - - var vRightTable=document.createDocumentFragment(); - vTmpDiv=this.newNode(vRightTable, 'div', vDivId+'gchartbody', 'gchartgrid gcontainercol'); - this.setChartBody(vTmpDiv); - vTmpTab=this.newNode(vTmpDiv, 'table', vDivId+'chartTable', 'gcharttable', null, vTaskLeftPx); - this.setChartTable(vTmpTab); - this.newNode(vTmpDiv, 'div', null, 'rhscrpad', null, null, vTaskLeftPx+1); - vTmpTBody=this.newNode(vTmpTab, 'tbody'); - - // Draw each row - - var i=0; - var j=0; - for(i=0; ivMinGpLen && vTaskWidth=vMinGpLen*2) this.newNode(vTmpDiv, 'div', null, vTaskList[i].getClass() +'endpointright'); - - vCaptClass='ggroupcaption'; - } - - if(!vSingleCell && !vComb) - { - vCellFormat=''; - for(j=0; j=(new Date()).getTime()) vTodayPx=JSGantt.getOffset(vMinDate, new Date(), vColWidth, vFormat); - else vTodayPx=-1; - this.DrawDependencies(); - } - }; //this.draw - - this.mouseOver=function(pObj1, pObj2) - { - if (this.getUseRowHlt()) - { - pObj1.className+=' gitemhighlight'; - pObj2.className+=' gitemhighlight'; - } - }; - - this.mouseOut=function(pObj1, pObj2) - { - if (this.getUseRowHlt()) - { - pObj1.className=pObj1.className.replace(/(?:^|\s)gitemhighlight(?!\S)/g, ''); - pObj2.className=pObj2.className.replace(/(?:^|\s)gitemhighlight(?!\S)/g, ''); - } - }; - - this.drawSelector=function(pPos) - { - var vOutput=document.createDocumentFragment(); - var vDisplay=false; - - for (var i=0; i=0 && vIdx'; - vTask+=''+vTaskList[vIdx].getName()+''; - vTask+=''+JSGantt.formatDateStr(vTaskList[vIdx].getStart(),vOutFrmt,vLangs[vLang])+''; - vTask+=''+JSGantt.formatDateStr(vTaskList[vIdx].getEnd(),vOutFrmt,vLangs[vLang])+''; - vTask+=''+vTaskList[vIdx].getClass()+''; - vTask+=''+vTaskList[vIdx].getLink()+''; - vTask+=''+vTaskList[vIdx].getMile()+''; - if(vTaskList[vIdx].getResource()!='\u00A0') vTask+=''+vTaskList[vIdx].getResource()+''; - vTask+=''+vTaskList[vIdx].getCompVal()+''; - vTask+=''+vTaskList[vIdx].getGroup()+''; - vTask+=''+vTaskList[vIdx].getParent()+''; - vTask+=''+vTaskList[vIdx].getOpen()+''; - vTask+=''; - var vDepList=vTaskList[vIdx].getDepend(); - for (i=0;i0)vTask+=','; - if(vDepList[i]>0)vTask+=vDepList[i]+vTaskList[vIdx].getDepType()[i]; - } - vTask+=''; - vTask+=''+vTaskList[vIdx].getCaption()+''; - - var vTmpFrag=document.createDocumentFragment(); - var vTmpDiv=this.newNode(vTmpFrag, 'div', null, null,vTaskList[vIdx].getNotes().innerHTML); - vTask+=''+vTmpDiv.innerHTML+''; - vTask+=''; - } - return vTask; - }; - if (vDiv && vDiv.nodeName.toLowerCase()=='div') vDivId=vDiv.id; +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JSGantt = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i 1) { + vTmpDate.setDate(vTmpDate.getDate() + 1); + } + } + else if (this.vFormat == 'quarter') { + if (vTmpDate <= vMaxDate) { + var vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, vMinorHeaderCellClass); + draw_utils_1.newNode(vTmpCell, 'div', null, null, date_utils_1.formatDateStr(vTmpDate, this.vQuarterMinorDateDisplayFormat, this.vLangs[this.vLang]), vColWidth); + vNumCols++; + } + vTmpDate.setDate(vTmpDate.getDate() + 81); + while (vTmpDate.getDate() > 1) + vTmpDate.setDate(vTmpDate.getDate() + 1); + } + else if (this.vFormat == 'hour') { + for (var i = vTmpDate.getHours(); i < 24; i++) { + vTmpDate.setHours(i); //works around daylight savings but may look a little odd on days where the clock goes forward + if (vTmpDate <= vMaxDate) { + var vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, vMinorHeaderCellClass); + draw_utils_1.newNode(vTmpCell, 'div', null, null, date_utils_1.formatDateStr(vTmpDate, this.vHourMinorDateDisplayFormat, this.vLangs[this.vLang]), vColWidth); + vNumCols++; + } + } + vTmpDate.setHours(0); + vTmpDate.setDate(vTmpDate.getDate() + 1); + } + } + var vDateRow = vTmpRow; + // Calculate size of grids : Plus 3 because 1 border left + 2 of paddings + var vTaskLeftPx = (vNumCols * (vColWidth + 3)) + 1; + // Fix a small space at the end for day + if (this.vFormat === 'day') { + vTaskLeftPx += 2; + } + vTmpTab.style.width = vTaskLeftPx + 'px'; // Ensure that the headings has exactly the same width as the chart grid + // const vTaskPlanLeftPx = (vNumCols * (vColWidth + 3)) + 1; + var vSingleCell = false; + if (this.vUseSingleCell !== 0 && this.vUseSingleCell < (vNumCols * vNumRows)) + vSingleCell = true; + draw_utils_1.newNode(vTmpDiv, 'div', null, 'rhscrpad', null, null, vTaskLeftPx + 1); + vTmpDiv = draw_utils_1.newNode(vRightHeader, 'div', null, 'glabelfooter'); + return { gChartLbl: gChartLbl, vTaskLeftPx: vTaskLeftPx, vSingleCell: vSingleCell, vDateRow: vDateRow, vRightHeader: vRightHeader, vNumCols: vNumCols }; + }; + /** + * + * DRAW CHART BODY + * + */ + this.drawCharBody = function (vTaskLeftPx, vTmpContentTabWrapper, gChartLbl, gListLbl, vMinDate, vMaxDate, vSingleCell, vNumCols, vColWidth, vDateRow) { + var vRightTable = document.createDocumentFragment(); + var vTmpDiv = draw_utils_1.newNode(vRightTable, 'div', this.vDivId + 'gchartbody', 'gchartgrid gcontainercol'); + this.setChartBody(vTmpDiv); + var vTmpTab = draw_utils_1.newNode(vTmpDiv, 'table', this.vDivId + 'chartTable', 'gcharttable', null, vTaskLeftPx); + this.setChartTable(vTmpTab); + draw_utils_1.newNode(vTmpDiv, 'div', null, 'rhscrpad', null, null, vTaskLeftPx + 1); + var vTmpTBody = draw_utils_1.newNode(vTmpTab, 'tbody'); + var vTmpTFoot = draw_utils_1.newNode(vTmpTab, 'tfoot'); + events_1.syncScroll([vTmpContentTabWrapper, vTmpDiv], 'scrollTop'); + events_1.syncScroll([gChartLbl, vTmpDiv], 'scrollLeft'); + events_1.syncScroll([vTmpContentTabWrapper, gListLbl], 'scrollLeft'); + // Draw each row + var i = 0; + var j = 0; + var bd; + if (this.vDebug) { + bd = new Date(); + console.info('before tasks loop', bd); + } + for (i = 0; i < this.vTaskList.length; i++) { + var curTaskStart = this.vTaskList[i].getStart() ? this.vTaskList[i].getStart() : this.vTaskList[i].getPlanStart(); + var curTaskEnd = this.vTaskList[i].getEnd() ? this.vTaskList[i].getEnd() : this.vTaskList[i].getPlanEnd(); + var vTaskLeftPx_1 = general_utils_1.getOffset(vMinDate, curTaskStart, vColWidth, this.vFormat, this.vShowWeekends); + var vTaskRightPx = general_utils_1.getOffset(curTaskStart, curTaskEnd, vColWidth, this.vFormat, this.vShowWeekends); + var curTaskPlanStart = void 0, curTaskPlanEnd = void 0; + curTaskPlanStart = this.vTaskList[i].getPlanStart(); + curTaskPlanEnd = this.vTaskList[i].getPlanEnd(); + var vTaskPlanLeftPx = 0; + var vTaskPlanRightPx = 0; + if (curTaskPlanStart && curTaskPlanEnd) { + vTaskPlanLeftPx = general_utils_1.getOffset(vMinDate, curTaskPlanStart, vColWidth, this.vFormat, this.vShowWeekends); + vTaskPlanRightPx = general_utils_1.getOffset(curTaskPlanStart, curTaskPlanEnd, vColWidth, this.vFormat, this.vShowWeekends); + } + var vID = this.vTaskList[i].getID(); + var vComb = (this.vTaskList[i].getParItem() && this.vTaskList[i].getParItem().getGroup() == 2); + var vCellFormat = ''; + var vTmpDiv_1 = null; + var vTmpItem = this.vTaskList[i]; + var vCaptClass = null; + // set cell width only for first row because of table-layout:fixed + var taskCellWidth = i === 0 ? vColWidth : null; + if (this.vTaskList[i].getMile() && !vComb) { + var vTmpRow = draw_utils_1.newNode(vTmpTBody, 'tr', this.vDivId + 'childrow_' + vID, 'gmileitem gmile' + this.vFormat, null, null, null, ((this.vTaskList[i].getVisible() == 0) ? 'none' : null)); + this.vTaskList[i].setChildRow(vTmpRow); + events_1.addThisRowListeners(this, this.vTaskList[i].getListChildRow(), vTmpRow); + var vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gtaskcell gtaskcellmile', null, vColWidth, null, null, null); + vTmpDiv_1 = draw_utils_1.newNode(vTmpCell, 'div', null, 'gtaskcelldiv', '\u00A0\u00A0'); + vTmpDiv_1 = draw_utils_1.newNode(vTmpDiv_1, 'div', this.vDivId + 'bardiv_' + vID, 'gtaskbarcontainer', null, 12, vTaskLeftPx_1 + vTaskRightPx - 6); + this.vTaskList[i].setBarDiv(vTmpDiv_1); + var vTmpDiv2 = draw_utils_1.newNode(vTmpDiv_1, 'div', this.vDivId + 'taskbar_' + vID, this.vTaskList[i].getClass(), null, 12); + this.vTaskList[i].setTaskDiv(vTmpDiv2); + if (this.vTaskList[i].getCompVal() < 100) + vTmpDiv2.appendChild(document.createTextNode('\u25CA')); + else { + vTmpDiv2 = draw_utils_1.newNode(vTmpDiv2, 'div', null, 'gmilediamond'); + draw_utils_1.newNode(vTmpDiv2, 'div', null, 'gmdtop'); + draw_utils_1.newNode(vTmpDiv2, 'div', null, 'gmdbottom'); + } + vCaptClass = 'gmilecaption'; + if (!vSingleCell && !vComb) { + this.drawColsChart(vNumCols, vTmpRow, taskCellWidth, vMinDate, vMaxDate); + } + } + else { + var vTaskWidth = vTaskRightPx; + // Draw Group Bar which has outer div with inner group div + // and several small divs to left and right to create angled-end indicators + if (this.vTaskList[i].getGroup()) { + vTaskWidth = (vTaskWidth > this.vMinGpLen && vTaskWidth < this.vMinGpLen * 2) ? this.vMinGpLen * 2 : vTaskWidth; // Expand to show two end points + vTaskWidth = (vTaskWidth < this.vMinGpLen) ? this.vMinGpLen : vTaskWidth; // expand to show one end point + var vTmpRow = draw_utils_1.newNode(vTmpTBody, 'tr', this.vDivId + 'childrow_' + vID, ((this.vTaskList[i].getGroup() == 2) ? 'glineitem gitem' : 'ggroupitem ggroup') + this.vFormat, null, null, null, ((this.vTaskList[i].getVisible() == 0) ? 'none' : null)); + this.vTaskList[i].setChildRow(vTmpRow); + events_1.addThisRowListeners(this, this.vTaskList[i].getListChildRow(), vTmpRow); + var vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gtaskcell gtaskcellbar', null, vColWidth, null, null); + vTmpDiv_1 = draw_utils_1.newNode(vTmpCell, 'div', null, 'gtaskcelldiv', '\u00A0\u00A0'); + this.vTaskList[i].setCellDiv(vTmpDiv_1); + if (this.vTaskList[i].getGroup() == 1) { + vTmpDiv_1 = draw_utils_1.newNode(vTmpDiv_1, 'div', this.vDivId + 'bardiv_' + vID, 'gtaskbarcontainer', null, vTaskWidth, vTaskLeftPx_1); + this.vTaskList[i].setBarDiv(vTmpDiv_1); + var vTmpDiv2 = draw_utils_1.newNode(vTmpDiv_1, 'div', this.vDivId + 'taskbar_' + vID, this.vTaskList[i].getClass(), null, vTaskWidth); + this.vTaskList[i].setTaskDiv(vTmpDiv2); + draw_utils_1.newNode(vTmpDiv2, 'div', this.vDivId + 'complete_' + vID, this.vTaskList[i].getClass() + 'complete', null, this.vTaskList[i].getCompStr()); + draw_utils_1.newNode(vTmpDiv_1, 'div', null, this.vTaskList[i].getClass() + 'endpointleft'); + if (vTaskWidth >= this.vMinGpLen * 2) + draw_utils_1.newNode(vTmpDiv_1, 'div', null, this.vTaskList[i].getClass() + 'endpointright'); + vCaptClass = 'ggroupcaption'; + } + if (!vSingleCell && !vComb) { + this.drawColsChart(vNumCols, vTmpRow, taskCellWidth, vMinDate, vMaxDate); + } + } + else { + vTaskWidth = (vTaskWidth <= 0) ? 1 : vTaskWidth; + /** + * DRAW THE BOXES FOR GANTT + */ + var vTmpDivCell = void 0, vTmpRow = void 0; + if (vComb) { + vTmpDivCell = vTmpDiv_1 = this.vTaskList[i].getParItem().getCellDiv(); + } + else { + // Draw Task Bar which has colored bar div + vTmpRow = draw_utils_1.newNode(vTmpTBody, 'tr', this.vDivId + 'childrow_' + vID, 'glineitem gitem' + this.vFormat, null, null, null, ((this.vTaskList[i].getVisible() == 0) ? 'none' : null)); + this.vTaskList[i].setChildRow(vTmpRow); + events_1.addThisRowListeners(this, this.vTaskList[i].getListChildRow(), vTmpRow); + var vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gtaskcell gtaskcellcolorbar', null, taskCellWidth, null, null); + vTmpDivCell = vTmpDiv_1 = draw_utils_1.newNode(vTmpCell, 'div', null, 'gtaskcelldiv', '\u00A0\u00A0'); + } + // DRAW TASK BAR + vTmpDiv_1 = draw_utils_1.newNode(vTmpDiv_1, 'div', this.vDivId + 'bardiv_' + vID, 'gtaskbarcontainer', null, vTaskWidth, vTaskLeftPx_1); + this.vTaskList[i].setBarDiv(vTmpDiv_1); + var vTmpDiv2 = void 0; + if (this.vTaskList[i].getStartVar()) { + // textbar + vTmpDiv2 = draw_utils_1.newNode(vTmpDiv_1, 'div', this.vDivId + 'taskbar_' + vID, this.vTaskList[i].getClass(), null, vTaskWidth); + if (this.vTaskList[i].getBarText()) { + draw_utils_1.newNode(vTmpDiv2, 'span', this.vDivId + 'tasktextbar_' + vID, 'textbar', this.vTaskList[i].getBarText(), this.vTaskList[i].getCompRestStr()); + } + this.vTaskList[i].setTaskDiv(vTmpDiv2); + } + // PLANNED + // If exist and one of them are different, show plan bar... show if there is no real vStart as well (just plan dates) + if (vTaskPlanLeftPx && ((vTaskPlanLeftPx != vTaskLeftPx_1 || vTaskPlanRightPx != vTaskRightPx) || !this.vTaskList[i].getStartVar())) { + var vTmpPlanDiv = draw_utils_1.newNode(vTmpDivCell, 'div', this.vDivId + 'bardiv_' + vID, 'gtaskbarcontainer gplan', null, vTaskPlanRightPx, vTaskPlanLeftPx); + var vTmpPlanDiv2 = draw_utils_1.newNode(vTmpPlanDiv, 'div', this.vDivId + 'taskbar_' + vID, this.vTaskList[i].getClass() + ' gplan', null, vTaskPlanRightPx); + this.vTaskList[i].setPlanTaskDiv(vTmpPlanDiv2); + } + // and opaque completion div + if (vTmpDiv2) { + draw_utils_1.newNode(vTmpDiv2, 'div', this.vDivId + 'complete_' + vID, this.vTaskList[i].getClass() + 'complete', null, this.vTaskList[i].getCompStr()); + } + // caption + if (vComb) + vTmpItem = this.vTaskList[i].getParItem(); + if (!vComb || (vComb && this.vTaskList[i].getParItem().getEnd() == this.vTaskList[i].getEnd())) + vCaptClass = 'gcaption'; + // Background cells + if (!vSingleCell && !vComb && vTmpRow) { + this.drawColsChart(vNumCols, vTmpRow, taskCellWidth, vMinDate, vMaxDate); + } + } + } + if (this.getCaptionType() && vCaptClass !== null) { + var vCaptionStr = void 0; + switch (this.getCaptionType()) { + case 'Caption': + vCaptionStr = vTmpItem.getCaption(); + break; + case 'Resource': + vCaptionStr = vTmpItem.getResource(); + break; + case 'Duration': + vCaptionStr = vTmpItem.getDuration(this.vFormat, this.vLangs[this.vLang]); + break; + case 'Complete': + vCaptionStr = vTmpItem.getCompStr(); + break; + } + draw_utils_1.newNode(vTmpDiv_1, 'div', null, vCaptClass, vCaptionStr, 120, (vCaptClass == 'gmilecaption') ? 12 : 0); + } + // Add Task Info div for tooltip + if (this.vTaskList[i].getTaskDiv() && vTmpDiv_1) { + var vTmpDiv2 = draw_utils_1.newNode(vTmpDiv_1, 'div', this.vDivId + 'tt' + vID, null, null, null, null, 'none'); + var _a = this.createTaskInfo(this.vTaskList[i], this.vTooltipTemplate), component = _a.component, callback = _a.callback; + vTmpDiv2.appendChild(component); + events_1.addTooltipListeners(this, this.vTaskList[i].getTaskDiv(), vTmpDiv2, callback); + } + // Add Plan Task Info div for tooltip + if (this.vTaskList[i].getPlanTaskDiv() && vTmpDiv_1) { + var vTmpDiv2 = draw_utils_1.newNode(vTmpDiv_1, 'div', this.vDivId + 'tt' + vID, null, null, null, null, 'none'); + var _b = this.createTaskInfo(this.vTaskList[i], this.vTooltipTemplate), component = _b.component, callback = _b.callback; + vTmpDiv2.appendChild(component); + events_1.addTooltipListeners(this, this.vTaskList[i].getPlanTaskDiv(), vTmpDiv2, callback); + } + } + // Include the footer with the days/week/month... + if (vSingleCell) { + var vTmpTFootTRow = draw_utils_1.newNode(vTmpTFoot, 'tr'); + var vTmpTFootTCell = draw_utils_1.newNode(vTmpTFootTRow, 'td', null, null, null, '100%'); + var vTmpTFootTCellTable = draw_utils_1.newNode(vTmpTFootTCell, 'table', null, 'gcharttableh', null, '100%'); + var vTmpTFootTCellTableTBody = draw_utils_1.newNode(vTmpTFootTCellTable, 'tbody'); + vTmpTFootTCellTableTBody.appendChild(vDateRow.cloneNode(true)); + } + else { + vTmpTFoot.appendChild(vDateRow.cloneNode(true)); + } + return { vRightTable: vRightTable }; + }; + this.drawColsChart = function (vNumCols, vTmpRow, taskCellWidth, pStartDate, pEndDate) { + if (pStartDate === void 0) { pStartDate = null; } + if (pEndDate === void 0) { pEndDate = null; } + var columnCurrentDay = null; + // Find the Current day cell to put a different class + if (this.vShowWeekends !== false && pStartDate && pEndDate && (this.vFormat == 'day' || this.vFormat == 'week')) { + var curTaskStart = new Date(pStartDate.getTime()); + var curTaskEnd = new Date(); + var onePeriod = 3600000; + if (this.vFormat == 'day') { + onePeriod *= 24; + } + else if (this.vFormat == 'week') { + onePeriod *= 24 * 7; + } + columnCurrentDay = Math.floor(general_utils_1.calculateCurrentDateOffset(curTaskStart, curTaskEnd) / onePeriod) - 1; + } + for (var j = 0; j < vNumCols - 1; j++) { + var vCellFormat = 'gtaskcell gtaskcellcols'; + if (this.vShowWeekends !== false && this.vFormat == 'day' && ((j % 7 == 4) || (j % 7 == 5))) { + vCellFormat = 'gtaskcellwkend'; + } + //When is the column is the current day/week,give a different class + else if ((this.vFormat == 'week' || this.vFormat == 'day') && j === columnCurrentDay) { + vCellFormat = 'gtaskcellcurrent'; + } + draw_utils_1.newNode(vTmpRow, 'td', null, vCellFormat, '\u00A0\u00A0', taskCellWidth); + } + }; + /** + * + * + * DRAWING PROCESS + * + * vTaskRightPx,vTaskWidth,vTaskPlanLeftPx,vTaskPlanRightPx,vID + */ + this.Draw = function () { + var vMaxDate = new Date(); + var vMinDate = new Date(); + var vColWidth = 0; + var bd; + if (this.vEvents && this.vEvents.beforeDraw) { + this.vEvents.beforeDraw(); + } + if (this.vDebug) { + bd = new Date(); + console.info('before draw', bd); + } + // Process all tasks, reset parent date and completion % if task list has altered + if (this.vProcessNeeded) + task_1.processRows(this.vTaskList, 0, -1, 1, 1, this.getUseSort(), this.vDebug); + this.vProcessNeeded = false; + // get overall min/max dates plus padding + vMinDate = date_utils_1.getMinDate(this.vTaskList, this.vFormat, this.getMinDate() && date_utils_1.coerceDate(this.getMinDate())); + vMaxDate = date_utils_1.getMaxDate(this.vTaskList, this.vFormat, this.getMaxDate() && date_utils_1.coerceDate(this.getMaxDate())); + // Calculate chart width variables. + if (this.vFormat == 'day') + vColWidth = this.vDayColWidth; + else if (this.vFormat == 'week') + vColWidth = this.vWeekColWidth; + else if (this.vFormat == 'month') + vColWidth = this.vMonthColWidth; + else if (this.vFormat == 'quarter') + vColWidth = this.vQuarterColWidth; + else if (this.vFormat == 'hour') + vColWidth = this.vHourColWidth; + // DRAW the Left-side of the chart (names, resources, comp%) + var vLeftHeader = document.createDocumentFragment(); + /** + * LIST HEAD + */ + var gListLbl = this.drawListHead(vLeftHeader); + /** + * LIST BODY + */ + var _a = this.drawListBody(vLeftHeader), vNumRows = _a.vNumRows, vTmpContentTabWrapper = _a.vTmpContentTabWrapper; + /** + * CHART HEAD + */ + var _b = this.drawChartHead(vMinDate, vMaxDate, vColWidth, vNumRows), gChartLbl = _b.gChartLbl, vTaskLeftPx = _b.vTaskLeftPx, vSingleCell = _b.vSingleCell, vRightHeader = _b.vRightHeader, vDateRow = _b.vDateRow, vNumCols = _b.vNumCols; + /** + * CHART GRID + */ + var vRightTable = this.drawCharBody(vTaskLeftPx, vTmpContentTabWrapper, gChartLbl, gListLbl, vMinDate, vMaxDate, vSingleCell, vNumCols, vColWidth, vDateRow).vRightTable; + if (this.vDebug) { + var ad = new Date(); + console.info('after tasks loop', ad, (ad.getTime() - bd.getTime())); + } + // MAIN VIEW: Appending all generated components to main view + while (this.vDiv.hasChildNodes()) + this.vDiv.removeChild(this.vDiv.firstChild); + var vTmpDiv = draw_utils_1.newNode(this.vDiv, 'div', null, 'gchartcontainer'); + vTmpDiv.style.height = this.vTotalHeight; + var leftvTmpDiv = draw_utils_1.newNode(vTmpDiv, 'div', null, 'gmain gmainleft'); + leftvTmpDiv.appendChild(vLeftHeader); + // leftvTmpDiv.appendChild(vLeftTable); + var rightvTmpDiv = draw_utils_1.newNode(vTmpDiv, 'div', null, 'gmain gmainright'); + rightvTmpDiv.appendChild(vRightHeader); + rightvTmpDiv.appendChild(vRightTable); + vTmpDiv.appendChild(leftvTmpDiv); + vTmpDiv.appendChild(rightvTmpDiv); + draw_utils_1.newNode(vTmpDiv, 'div', null, 'ggridfooter'); + var vTmpDiv2 = draw_utils_1.newNode(this.getChartBody(), 'div', this.vDivId + 'Lines', 'glinediv'); + if (this.vEvents.onLineContainerHover && typeof this.vEvents.onLineContainerHover === 'function') { + events_1.addListener('mouseover', this.vEvents.onLineContainerHover, vTmpDiv2); + events_1.addListener('mouseout', this.vEvents.onLineContainerHover, vTmpDiv2); + } + vTmpDiv2.style.visibility = 'hidden'; + this.setLines(vTmpDiv2); + /* Quick hack to show the generated HTML on older browsers + let tmpGenSrc=document.createElement('textarea'); + tmpGenSrc.appendChild(document.createTextNode(vTmpDiv.innerHTML)); + vDiv.appendChild(tmpGenSrc); + //*/ + // LISTENERS: Now all the content exists, register scroll listeners + events_1.addScrollListeners(this); + // SCROLL: now check if we are actually scrolling the pane + if (this.vScrollTo != '') { + var vScrollDate = new Date(vMinDate.getTime()); + var vScrollPx = 0; + if (this.vScrollTo.substr && this.vScrollTo.substr(0, 2) == 'px') { + vScrollPx = parseInt(this.vScrollTo.substr(2)); + } + else { + if (this.vScrollTo === 'today') { + vScrollDate = new Date(); + } + else if (this.vScrollTo instanceof Date) { + vScrollDate = this.vScrollTo; + } + else { + vScrollDate = date_utils_1.parseDateStr(this.vScrollTo, this.getDateInputFormat()); + } + if (this.vFormat == 'hour') + vScrollDate.setMinutes(0, 0, 0); + else + vScrollDate.setHours(0, 0, 0, 0); + vScrollPx = general_utils_1.getOffset(vMinDate, vScrollDate, vColWidth, this.vFormat, this.vShowWeekends) - 30; + } + this.getChartBody().scrollLeft = vScrollPx; + } + if (vMinDate.getTime() <= (new Date()).getTime() && vMaxDate.getTime() >= (new Date()).getTime()) { + this.vTodayPx = general_utils_1.getOffset(vMinDate, new Date(), vColWidth, this.vFormat, this.vShowWeekends); + } + else + this.vTodayPx = -1; + // DEPENDENCIES: Draw lines of Dependencies + var bdd; + if (this.vDebug) { + bdd = new Date(); + console.info('before DrawDependencies', bdd); + } + if (this.vEvents && typeof this.vEvents.beforeLineDraw === 'function') { + this.vEvents.beforeLineDraw(); + } + this.DrawDependencies(this.vDebug); + events_1.addListenerDependencies(this.vLineOptions); + // EVENTS + if (this.vEvents && typeof this.vEvents.afterLineDraw === 'function') { + this.vEvents.afterLineDraw(); + } + if (this.vDebug) { + var ad = new Date(); + console.info('after DrawDependencies', ad, (ad.getTime() - bdd.getTime())); + } + this.drawComplete(vMinDate, vColWidth, bd); + }; + /** + * Actions after all the render process + */ + this.drawComplete = function (vMinDate, vColWidth, bd) { + if (this.vDebug) { + var ad = new Date(); + console.info('after draw', ad, (ad.getTime() - bd.getTime())); + } + events_1.updateGridHeaderWidth(this); + this.chartRowDateToX = function (date) { + return general_utils_1.getOffset(vMinDate, date, vColWidth, this.vFormat, this.vShowWeekends); + }; + if (this.vEvents && this.vEvents.afterDraw) { + this.vEvents.afterDraw(); + } + }; + if (this.vDiv && this.vDiv.nodeName && this.vDiv.nodeName.toLowerCase() == 'div') + this.vDivId = this.vDiv.id; }; //GanttChart -JSGantt.updateFlyingObj=function (e, pGanttChartObj, pTimer) { - var vCurTopBuf=3; - var vCurLeftBuf=5; - var vCurBotBuf=3; - var vCurRightBuf=15; - var vMouseX=(e)?e.clientX:window.event.clientX; - var vMouseY=(e)?e.clientY:window.event.clientY; - var vViewportX=document.documentElement.clientWidth||document.getElementsByTagName('body')[0].clientWidth; - var vViewportY=document.documentElement.clientHeight||document.getElementsByTagName('body')[0].clientHeight; - var vNewX=vMouseX; - var vNewY=vMouseY; - - if (navigator.appName.toLowerCase ()=='microsoft internet explorer') { - // the clientX and clientY properties include the left and top borders of the client area - vMouseX-=document.documentElement.clientLeft; - vMouseY-=document.documentElement.clientTop; - - var vZoomFactor=JSGantt.getZoomFactor (); - if (vZoomFactor!=1) {// IE 7 at non-default zoom level - vMouseX=Math.round (vMouseX / vZoomFactor); - vMouseY=Math.round (vMouseY / vZoomFactor); - } - } - - var vScrollPos=JSGantt.getScrollPositions(); - - /* Code for positioned right of the mouse by default*/ - /* - if (vMouseX+vCurRightBuf+pGanttChartObj.vTool.offsetWidth>vViewportX) - { - if (vMouseX-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth<0) vNewX=vScrollPos.x; - else vNewX=vMouseX+vScrollPos.x-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth; - } - else vNewX=vMouseX+vScrollPos.x+vCurRightBuf; - */ - - /* Code for positioned left of the mouse by default */ - if (vMouseX-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth<0) - { - if (vMouseX+vCurRightBuf+pGanttChartObj.vTool.offsetWidth>vViewportX) vNewX=vScrollPos.x; - else vNewX=vMouseX+vScrollPos.x+vCurRightBuf; - } - else vNewX=vMouseX+vScrollPos.x-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth; - - /* Code for positioned below the mouse by default */ - if (vMouseY+vCurBotBuf+pGanttChartObj.vTool.offsetHeight>vViewportY) - { - if (vMouseY-vCurTopBuf-pGanttChartObj.vTool.offsetHeight<0) vNewY=vScrollPos.y; - else vNewY=vMouseY+vScrollPos.y-vCurTopBuf-pGanttChartObj.vTool.offsetHeight; - } - else vNewY=vMouseY+vScrollPos.y+vCurBotBuf; - - /* Code for positioned above the mouse by default */ - /* - if (vMouseY-vCurTopBuf-pGanttChartObj.vTool.offsetHeight<0) - { - if (vMouseY+vCurBotBuf+pGanttChartObj.vTool.offsetHeight>vViewportY) vNewY=vScrollPos.y; - else vNewY=vMouseY+vScrollPos.y+vCurBotBuf; - } - else vNewY=vMouseY+vScrollPos.y-vCurTopBuf-pGanttChartObj.vTool.offsetHeight; - */ - - if (pGanttChartObj.getUseMove()) - { - clearInterval(pGanttChartObj.vTool.moveInterval); - pGanttChartObj.vTool.moveInterval=setInterval(function(){JSGantt.moveToolTip(vNewX, vNewY, pGanttChartObj.vTool, pTimer);},pTimer); - } - else - { - pGanttChartObj.vTool.style.left=vNewX +'px'; - pGanttChartObj.vTool.style.top=vNewY +'px'; - } +},{"./draw_columns":3,"./draw_dependencies":4,"./events":5,"./lang":8,"./options":9,"./task":10,"./utils/date_utils":11,"./utils/draw_utils":12,"./utils/general_utils":13,"./xml":14}],3:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var date_utils_1 = require("./utils/date_utils"); +var task_1 = require("./task"); +var events_1 = require("./events"); +var draw_utils_1 = require("./utils/draw_utils"); +exports.COLUMN_ORDER = [ + 'vShowRes', + 'vShowDur', + 'vShowComp', + 'vShowStartDate', + 'vShowEndDate', + 'vShowPlanStartDate', + 'vShowPlanEndDate', + 'vShowCost', + 'vAdditionalHeaders', + 'vShowAddEntries' +]; +var COLUMNS_TYPES = { + 'vShowRes': 'res', + 'vShowDur': 'dur', + 'vShowComp': 'comp', + 'vShowStartDate': 'startdate', + 'vShowEndDate': 'enddate', + 'vShowPlanStartDate': 'planstartdate', + 'vShowPlanEndDate': 'planenddate', + 'vShowCost': 'cost', + 'vShowAddEntries': 'addentries' +}; +exports.draw_header = function (column, i, vTmpRow, vTaskList, vEditable, vEventsChange, vEvents, vDateTaskTableDisplayFormat, vAdditionalHeaders, vFormat, vLangs, vLang, vResources, Draw) { + var vTmpCell, vTmpDiv; + if ('vShowRes' === column) { + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gres'); + var text = draw_utils_1.makeInput(vTaskList[i].getResource(), vEditable, 'resource', vTaskList[i].getResource(), vResources); + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, text); + var callback = function (task, e) { return task.setResource(e.target.value); }; + events_1.addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'res', Draw, 'change'); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'res'); + } + if ('vShowDur' === column) { + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gdur'); + var text = draw_utils_1.makeInput(vTaskList[i].getDuration(vFormat, vLangs[vLang]), vEditable, 'text', vTaskList[i].getDuration()); + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, text); + var callback = function (task, e) { return task.setDuration(e.target.value); }; + events_1.addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'dur', Draw); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'dur'); + } + if ('vShowComp' === column) { + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gcomp'); + var text = draw_utils_1.makeInput(vTaskList[i].getCompStr(), vEditable, 'percentage', vTaskList[i].getCompVal()); + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, text); + var callback = function (task, e) { task.setComp(e.target.value); task.setCompVal(e.target.value); }; + events_1.addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'comp', Draw); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'comp'); + } + if ('vShowStartDate' === column) { + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gstartdate'); + var v = date_utils_1.formatDateStr(vTaskList[i].getStartVar(), vDateTaskTableDisplayFormat, vLangs[vLang]); + var text = draw_utils_1.makeInput(v, vEditable, 'date', vTaskList[i].getStartVar()); + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, text); + var callback = function (task, e) { return task.setStart(e.target.value); }; + events_1.addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'start', Draw); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'start'); + } + if ('vShowEndDate' === column) { + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'genddate'); + var v = date_utils_1.formatDateStr(vTaskList[i].getEndVar(), vDateTaskTableDisplayFormat, vLangs[vLang]); + var text = draw_utils_1.makeInput(v, vEditable, 'date', vTaskList[i].getEndVar()); + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, text); + var callback = function (task, e) { return task.setEnd(e.target.value); }; + events_1.addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'end', Draw); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'end'); + } + if ('vShowPlanStartDate' === column) { + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gplanstartdate'); + var v = vTaskList[i].getPlanStart() ? date_utils_1.formatDateStr(vTaskList[i].getPlanStart(), vDateTaskTableDisplayFormat, vLangs[vLang]) : ''; + var text = draw_utils_1.makeInput(v, vEditable, 'date', vTaskList[i].getPlanStart()); + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, text); + var callback = function (task, e) { return task.setPlanStart(e.target.value); }; + events_1.addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'planstart', Draw); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'planstart'); + } + if ('vShowPlanEndDate' === column) { + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gplanenddate'); + var v = vTaskList[i].getPlanEnd() ? date_utils_1.formatDateStr(vTaskList[i].getPlanEnd(), vDateTaskTableDisplayFormat, vLangs[vLang]) : ''; + var text = draw_utils_1.makeInput(v, vEditable, 'date', vTaskList[i].getPlanEnd()); + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, text); + var callback = function (task, e) { return task.setPlanEnd(e.target.value); }; + events_1.addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'planend', Draw); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'planend'); + } + if ('vShowCost' === column) { + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gcost'); + var text = draw_utils_1.makeInput(vTaskList[i].getCost(), vEditable, 'cost'); + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, text); + var callback = function (task, e) { return task.setCost(e.target.value); }; + events_1.addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'cost', Draw); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'cost'); + } + if ('vAdditionalHeaders' === column && vAdditionalHeaders) { + for (var key in vAdditionalHeaders) { + var header = vAdditionalHeaders[key]; + var css = header.class ? header.class : "gadditional-" + key; + var data = vTaskList[i].getDataObject(); + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, "gadditional " + css); + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, data ? data[key] : ''); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], "additional_" + key); + // const callback = (task, e) => task.setCost(e.target.value); + // addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'costdate'); + } + } + if ('vShowAddEntries' === column) { + vTmpCell = draw_utils_1.newNode(vTmpRow, 'td', null, 'gaddentries'); + var button = ""; + vTmpDiv = draw_utils_1.newNode(vTmpCell, 'div', null, null, button); + var callback = function (task, e) { + task_1.AddTaskItemObject({ + vParent: task.getParent() + }); + }; + events_1.addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'addentries', Draw.bind(this)); + events_1.addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'addentries'); + } +}; +exports.draw_bottom = function (column, vTmpRow, vAdditionalHeaders) { + if ('vAdditionalHeaders' === column && vAdditionalHeaders) { + for (var key in vAdditionalHeaders) { + var header = vAdditionalHeaders[key]; + var css = header.class ? header.class : "gadditional-" + key; + draw_utils_1.newNode(vTmpRow, 'td', null, "gspanning gadditional " + css, '\u00A0'); + } + } + else { + var type = COLUMNS_TYPES[column]; + draw_utils_1.newNode(vTmpRow, 'td', null, "gspanning g" + type, '\u00A0'); + } +}; +// export const draw_list_headings = function (column, vTmpRow, vAdditionalHeaders, vEvents) { +// let nodeCreated; +// if ('vAdditionalHeaders' === column && vAdditionalHeaders) { +// for (const key in vAdditionalHeaders) { +// const header = vAdditionalHeaders[key]; +// const css = header.class ? header.class : `gadditional-${key}`; +// newNode(vTmpRow, 'td', null, `gspanning gadditional ${css}`, '\u00A0'); +// } +// } else { +// const type = COLUMNS_TYPES[column]; +// nodeCreated = newNode(vTmpRow, 'td', null, `gspanning g${type}`, '\u00A0'); +// addListenerClickCell(nodeCreated, vEvents, { hader: true, column }, type); +// } +// } +exports.draw_task_headings = function (column, vTmpRow, vLangs, vLang, vAdditionalHeaders, vEvents) { + var nodeCreated; + if ('vAdditionalHeaders' === column && vAdditionalHeaders) { + for (var key in vAdditionalHeaders) { + var header = vAdditionalHeaders[key]; + var text = header.translate ? vLangs[vLang][header.translate] : header.title; + var css = header.class ? header.class : "gadditional-" + key; + nodeCreated = draw_utils_1.newNode(vTmpRow, 'td', null, "gtaskheading gadditional " + css, text); + } + } + else { + var type = COLUMNS_TYPES[column]; + nodeCreated = draw_utils_1.newNode(vTmpRow, 'td', null, "gtaskheading g" + type, vLangs[vLang][type]); + events_1.addListenerClickCell(nodeCreated, vEvents, { hader: true, column: column }, type); + } }; -JSGantt.showToolTip=function(pGanttChartObj, e, pContents, pWidth, pTimer){ - var vTtDivId=pGanttChartObj.getDivId()+'JSGanttToolTip'; - var vMaxW=500; - var vMaxAlpha=100; - var vShowing=pContents.id; - - if(pGanttChartObj.getUseToolTip()) - { - if(pGanttChartObj.vTool==null){ - pGanttChartObj.vTool=document.createElement('div'); - pGanttChartObj.vTool.id=vTtDivId; - pGanttChartObj.vTool.className='JSGanttToolTip'; - pGanttChartObj.vTool.vToolCont=document.createElement('div'); - pGanttChartObj.vTool.vToolCont.id=vTtDivId+'cont'; - pGanttChartObj.vTool.vToolCont.className='JSGanttToolTipcont'; - pGanttChartObj.vTool.vToolCont.setAttribute('showing',''); - pGanttChartObj.vTool.appendChild(pGanttChartObj.vTool.vToolCont); - document.body.appendChild(pGanttChartObj.vTool); - pGanttChartObj.vTool.style.opacity=0; - pGanttChartObj.vTool.setAttribute('currentOpacity',0); - pGanttChartObj.vTool.setAttribute('fadeIncrement',10); - pGanttChartObj.vTool.setAttribute('moveSpeed',10); - pGanttChartObj.vTool.style.filter='alpha(opacity=0)'; - pGanttChartObj.vTool.style.visibility='hidden'; - pGanttChartObj.vTool.style.left=Math.floor(((e)?e.clientX:window.event.clientX)/2)+'px'; - pGanttChartObj.vTool.style.top=Math.floor(((e)?e.clientY:window.event.clientY)/2)+'px'; - JSGantt.addListener('mouseover', function () {clearTimeout(pGanttChartObj.vTool.delayTimeout);}, pGanttChartObj.vTool); - JSGantt.addListener('mouseout', function () {JSGantt.delayedHide(pGanttChartObj, pGanttChartObj.vTool, pTimer);}, pGanttChartObj.vTool); - } - clearTimeout(pGanttChartObj.vTool.delayTimeout); - if(pGanttChartObj.vTool.vToolCont.getAttribute('showing')!=vShowing || pGanttChartObj.vTool.style.visibility!='visible') - { - if (pGanttChartObj.vTool.vToolCont.getAttribute('showing')!=vShowing) - { - pGanttChartObj.vTool.vToolCont.setAttribute('showing',vShowing); - - pGanttChartObj.vTool.vToolCont.innerHTML=pContents.innerHTML; - // as we are allowing arbitrary HTML we should remove any tag ids to prevent duplication - JSGantt.stripIds(pGanttChartObj.vTool.vToolCont); - } - - pGanttChartObj.vTool.style.visibility='visible'; - // Rather than follow the mouse just have it stay put - JSGantt.updateFlyingObj(e, pGanttChartObj, pTimer); - pGanttChartObj.vTool.style.width=(pWidth)? pWidth+'px' : 'auto'; - if(!pWidth && JSGantt.isIE()){ - pGanttChartObj.vTool.style.width=pGanttChartObj.vTool.offsetWidth; - } - if(pGanttChartObj.vTool.offsetWidth>vMaxW){pGanttChartObj.vTool.style.width=vMaxW+'px';} - } - - if (pGanttChartObj.getUseFade()) - { - clearInterval(pGanttChartObj.vTool.fadeInterval); - pGanttChartObj.vTool.fadeInterval=setInterval(function(){JSGantt.fadeToolTip(1, pGanttChartObj.vTool, vMaxAlpha);},pTimer); - } - else - { - pGanttChartObj.vTool.style.opacity=vMaxAlpha * 0.01; - pGanttChartObj.vTool.style.filter='alpha(opacity='+vMaxAlpha+')'; - } - } -}; - -JSGantt.stripIds=function(pNode){ - for(var i=0; i=0 && pList[i].getID()==pID)vCurItem=pList[i]; - } - - for(i=0; ivMaxDate) - { - vMaxDate=pList[i].getEnd(); - vMaxSet=1; - } - - vNumKid++; - vWeight+=pList[i].getEnd()-pList[i].getStart()+1; - vCompSum+=pList[i].getCompVal()*(pList[i].getEnd()-pList[i].getStart()+1); - pList[i].setSortIdx(i*pList.length); - } - } - - if(pRow>=0) - { - if(pList[pRow].getGroupMinStart()!=null && pList[pRow].getGroupMinStart()vMaxDate) - { - vMaxDate=pList[pRow].getGroupMinEnd(); - } - pList[pRow].setStart(vMinDate); - pList[pRow].setEnd(vMaxDate); - pList[pRow].setNumKid(vNumKid); - pList[pRow].setWeight(vWeight); - pList[pRow].setCompVal(Math.ceil(vCompSum/vWeight)); - } - - if (pID==0 && pUseSort==1) - { - JSGantt.sortTasks(pList, 0, 0); - pList.sort(function(a,b){return a.getSortIdx()-b.getSortIdx();}); - } - if (pID==0 && pUseSort!=1) // Need to sort combined tasks regardless - { - for(i=0; i0) - { - sortArr.sort(function(a,b){ var i=a.getStart().getTime()-b.getStart().getTime(); - if (i==0) i=a.getEnd().getTime()-b.getEnd().getTime(); - if (i==0) return a.getID()-b.getID(); - else return i; }); - } - - for (var j=0; j1) vDate.setDate(vDate.getDate()-1); - } - else if (pFormat=='quarter') - { - vDate.setDate(vDate.getDate()-31); - if(vDate.getMonth()==0 || vDate.getMonth()==1 || vDate.getMonth()==2) - vDate.setFullYear(vDate.getFullYear(), 0, 1); - else if(vDate.getMonth()==3 || vDate.getMonth()==4 || vDate.getMonth()==5) - vDate.setFullYear(vDate.getFullYear(), 3, 1); - else if(vDate.getMonth()==6 || vDate.getMonth()==7 || vDate.getMonth()==8) - vDate.setFullYear(vDate.getFullYear(), 6, 1); - else if(vDate.getMonth()==9 || vDate.getMonth()==10 || vDate.getMonth()==11) - vDate.setFullYear(vDate.getFullYear(), 9, 1); - } - else if (pFormat=='hour') - { - vDate.setHours(vDate.getHours()-1); - while(vDate.getHours()%6!=0) vDate.setHours(vDate.getHours()-1); - } - - if(pFormat=='hour')vDate.setMinutes(0,0); - else vDate.setHours(0,0,0); - return(vDate); -}; - -// Used to determine the maximum date of all tasks and set upper bound based on format -JSGantt.getMaxDate=function (pList, pFormat) -{ - var vDate=new Date(); - - vDate.setTime(pList[0].getEnd().getTime()); - - // Parse all Task End dates to find max - for(var i=0; ivDate.getTime()) vDate.setTime(pList[i].getEnd().getTime()); - } - - // Adjust max date to specific format boundaries (end of week or end of month) - if (pFormat=='day') - { - vDate.setDate(vDate.getDate()+1); - - while(vDate.getDay()%7!=0) vDate.setDate(vDate.getDate()+1); - } - else if (pFormat=='week') - { - //For weeks, what is the last logical boundary? - vDate.setDate(vDate.getDate()+1); - - while(vDate.getDay()%7!=0) vDate.setDate(vDate.getDate()+1); - } - else if (pFormat=='month') - { - // Set to last day of current Month - while(vDate.getDate()>1) vDate.setDate(vDate.getDate()+1); - vDate.setDate(vDate.getDate()-1); - } - else if (pFormat=='quarter') - { - // Set to last day of current Quarter - if(vDate.getMonth()==0 || vDate.getMonth()==1 || vDate.getMonth()==2) - vDate.setFullYear(vDate.getFullYear(), 2, 31); - else if(vDate.getMonth()==3 || vDate.getMonth()==4 || vDate.getMonth()==5) - vDate.setFullYear(vDate.getFullYear(), 5, 30); - else if(vDate.getMonth()==6 || vDate.getMonth()==7 || vDate.getMonth()==8) - vDate.setFullYear(vDate.getFullYear(), 8, 30); - else if(vDate.getMonth()==9 || vDate.getMonth()==10 || vDate.getMonth()==11) - vDate.setFullYear(vDate.getFullYear(), 11, 31); - } - else if (pFormat=='hour') - { - if(vDate.getHours()==0)vDate.setDate(vDate.getDate()+1); - vDate.setHours(vDate.getHours()+1); - - while(vDate.getHours()%6!=5) vDate.setHours(vDate.getHours()+1); - } - return(vDate); -}; - -// This function finds the document id of the specified object -JSGantt.findObj=function (theObj, theDoc) -{ - var p, i, foundObj; - if(!theDoc) theDoc=document; - if(document.getElementById) foundObj=document.getElementById(theObj); - return foundObj; -}; - -JSGantt.changeFormat=function(pFormat,ganttObj) -{ - if(ganttObj) ganttObj.setFormat(pFormat); - else alert('Chart undefined'); +},{"./events":5,"./task":10,"./utils/date_utils":11,"./utils/draw_utils":12}],4:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.drawDependency = function (x1, y1, x2, y2, pType, pClass) { + var vDir = 1; + var vBend = false; + var vShort = 4; + var vRow = Math.floor(this.getRowHeight() / 2); + if (y2 < y1) + vRow *= -1; + switch (pType) { + case 'SF': + vShort *= -1; + if (x1 - 10 <= x2 && y1 != y2) + vBend = true; + vDir = -1; + break; + case 'SS': + if (x1 < x2) + vShort *= -1; + else + vShort = x2 - x1 - (2 * vShort); + break; + case 'FF': + if (x1 <= x2) + vShort = x2 - x1 + (2 * vShort); + vDir = -1; + break; + default: + if (x1 + 10 >= x2 && y1 != y2) + vBend = true; + break; + } + if (vBend) { + this.sLine(x1, y1, x1 + vShort, y1, pClass); + this.sLine(x1 + vShort, y1, x1 + vShort, y2 - vRow, pClass); + this.sLine(x1 + vShort, y2 - vRow, x2 - (vShort * 2), y2 - vRow, pClass); + this.sLine(x2 - (vShort * 2), y2 - vRow, x2 - (vShort * 2), y2, pClass); + this.sLine(x2 - (vShort * 2), y2, x2 - (1 * vDir), y2, pClass); + } + else if (y1 != y2) { + this.sLine(x1, y1, x1 + vShort, y1, pClass); + this.sLine(x1 + vShort, y1, x1 + vShort, y2, pClass); + this.sLine(x1 + vShort, y2, x2 - (1 * vDir), y2, pClass); + } + else + this.sLine(x1, y1, x2 - (1 * vDir), y2, pClass); + var vTmpDiv = this.sLine(x2, y2, x2 - 3 - ((vDir < 0) ? 1 : 0), y2 - 3 - ((vDir < 0) ? 1 : 0), pClass + "Arw"); + vTmpDiv.style.width = '0px'; + vTmpDiv.style.height = '0px'; +}; +exports.DrawDependencies = function (vDebug) { + if (vDebug === void 0) { vDebug = false; } + if (this.getShowDeps() == 1) { + this.CalcTaskXY(); //First recalculate the x,y + this.clearDependencies(); + var vList = this.getList(); + for (var i = 0; i < vList.length; i++) { + var vDepend = vList[i].getDepend(); + var vDependType = vList[i].getDepType(); + var n = vDepend.length; + if (n > 0 && vList[i].getVisible() == 1) { + for (var k = 0; k < n; k++) { + var vTask = this.getArrayLocationByID(vDepend[k]); + if (vTask >= 0 && vList[vTask].getGroup() != 2) { + if (vList[vTask].getVisible() == 1) { + if (vDebug) { + console.info("init drawDependency ", vList[vTask].getID(), new Date()); + } + var cssClass = 'gDepId' + vList[vTask].getID() + + ' ' + 'gDepNextId' + vList[i].getID(); + var dependedData = vList[vTask].getDataObject(); + var nextDependedData = vList[i].getDataObject(); + if (dependedData && dependedData.pID && nextDependedData && nextDependedData.pID) { + cssClass += ' gDepDataId' + dependedData.pID + ' ' + 'gDepNextDataId' + nextDependedData.pID; + } + if (vDependType[k] == 'SS') + this.drawDependency(vList[vTask].getStartX() - 1, vList[vTask].getStartY(), vList[i].getStartX() - 1, vList[i].getStartY(), 'SS', cssClass + ' gDepSS'); + else if (vDependType[k] == 'FF') + this.drawDependency(vList[vTask].getEndX(), vList[vTask].getEndY(), vList[i].getEndX(), vList[i].getEndY(), 'FF', cssClass + ' gDepFF'); + else if (vDependType[k] == 'SF') + this.drawDependency(vList[vTask].getStartX() - 1, vList[vTask].getStartY(), vList[i].getEndX(), vList[i].getEndY(), 'SF', cssClass + ' gDepSF'); + else if (vDependType[k] == 'FS') + this.drawDependency(vList[vTask].getEndX(), vList[vTask].getEndY(), vList[i].getStartX() - 1, vList[i].getStartY(), 'FS', cssClass + ' gDepFS'); + } + } + } + } + } + } + // draw the current date line + if (this.vTodayPx >= 0) { + this.sLine(this.vTodayPx, 0, this.vTodayPx, this.getChartTable().offsetHeight - 1, 'gCurDate'); + } }; +},{}],5:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var general_utils_1 = require("./utils/general_utils"); // Function to open/close and hide/show children of specified task -JSGantt.folder=function (pID,ganttObj) -{ - var vList=ganttObj.getList(); - var vDivId=ganttObj.getDivId(); - - ganttObj.clearDependencies(); // clear these first so slow rendering doesn't look odd - - for(var i=0; i vMaxW) { + pGanttChartObj.vTool.style.width = vMaxW + 'px'; + } + } + if (pGanttChartObj.getUseFade()) { + clearInterval(pGanttChartObj.vTool.fadeInterval); + pGanttChartObj.vTool.fadeInterval = setInterval(function () { general_utils_1.fadeToolTip(1, pGanttChartObj.vTool, vMaxAlpha); }, pTimer); + } + else { + pGanttChartObj.vTool.style.opacity = vMaxAlpha * 0.01; + pGanttChartObj.vTool.style.filter = 'alpha(opacity=' + vMaxAlpha + ')'; + } + } +}; +exports.addListener = function (eventName, handler, control) { + // Check if control is a string + if (control === String(control)) + control = general_utils_1.findObj(control); + if (control.addEventListener) //Standard W3C + { + return control.addEventListener(eventName, handler, false); + } + else if (control.attachEvent) //IExplore + { + return control.attachEvent('on' + eventName, handler); + } + else { + return false; + } +}; +exports.removeListener = function (eventName, handler, control) { + // Check if control is a string + if (control === String(control)) + control = general_utils_1.findObj(control); + if (control.removeEventListener) { + //Standard W3C + return control.removeEventListener(eventName, handler, false); + } + else if (control.detachEvent) { + //IExplore + return control.attachEvent('on' + eventName, handler); + } + else { + return false; + } +}; +exports.syncScroll = function (elements, attrName) { + var syncFlags = new Map(elements.map(function (e) { return [e, false]; })); + function scrollEvent(e) { + if (!syncFlags.get(e.target)) { + for (var _i = 0, elements_2 = elements; _i < elements_2.length; _i++) { + var el = elements_2[_i]; + if (el !== e.target) { + syncFlags.set(el, true); + el[attrName] = e.target[attrName]; + } + } + } + syncFlags.set(e.target, false); + } + for (var _i = 0, elements_1 = elements; _i < elements_1.length; _i++) { + var el = elements_1[_i]; + el.addEventListener('scroll', scrollEvent); + } +}; +exports.addTooltipListeners = function (pGanttChart, pObj1, pObj2, callback) { + var isShowingTooltip = false; + exports.addListener('mouseover', function (e) { + if (isShowingTooltip || !callback) { + exports.showToolTip(pGanttChart, e, pObj2, null, pGanttChart.getTimer()); + } + else if (callback) { + isShowingTooltip = true; + var promise = callback(); + exports.showToolTip(pGanttChart, e, pObj2, null, pGanttChart.getTimer()); + if (promise && promise.then) { + promise.then(function () { + if (pGanttChart.vTool.vToolCont.getAttribute('showing') === pObj2.id && + pGanttChart.vTool.style.visibility === 'visible') { + exports.showToolTip(pGanttChart, e, pObj2, null, pGanttChart.getTimer()); + } + }); + } + } + }, pObj1); + exports.addListener('mouseout', function (e) { + var outTo = e.relatedTarget; + if (general_utils_1.isParentElementOrSelf(outTo, pObj1) || (pGanttChart.vTool && general_utils_1.isParentElementOrSelf(outTo, pGanttChart.vTool))) { + // not actually out + } + else { + isShowingTooltip = false; + } + general_utils_1.delayedHide(pGanttChart, pGanttChart.vTool, pGanttChart.getTimer()); + }, pObj1); +}; +exports.addThisRowListeners = function (pGanttChart, pObj1, pObj2) { + exports.addListener('mouseover', function () { pGanttChart.mouseOver(pObj1, pObj2); }, pObj1); + exports.addListener('mouseover', function () { pGanttChart.mouseOver(pObj1, pObj2); }, pObj2); + exports.addListener('mouseout', function () { pGanttChart.mouseOut(pObj1, pObj2); }, pObj1); + exports.addListener('mouseout', function () { pGanttChart.mouseOut(pObj1, pObj2); }, pObj2); +}; +exports.updateGridHeaderWidth = function (pGanttChart) { + var head = pGanttChart.getChartHead(); + var body = pGanttChart.getChartBody(); + if (!head || !body) + return; + var isScrollVisible = body.scrollHeight > body.clientHeight; + if (isScrollVisible) { + head.style.width = "calc(100% - " + general_utils_1.getScrollbarWidth() + "px)"; + } + else { + head.style.width = '100%'; + } +}; +exports.addFolderListeners = function (pGanttChart, pObj, pID) { + exports.addListener('click', function () { + exports.folder(pID, pGanttChart); + exports.updateGridHeaderWidth(pGanttChart); + }, pObj); +}; +exports.addFormatListeners = function (pGanttChart, pFormat, pObj) { + exports.addListener('click', function () { general_utils_1.changeFormat(pFormat, pGanttChart); }, pObj); +}; +exports.addScrollListeners = function (pGanttChart) { + exports.addListener('resize', function () { pGanttChart.getChartHead().scrollLeft = pGanttChart.getChartBody().scrollLeft; }, window); + exports.addListener('resize', function () { + pGanttChart.getListBody().scrollTop = pGanttChart.getChartBody().scrollTop; + }, window); +}; +exports.addListenerClickCell = function (vTmpCell, vEvents, task, column) { + exports.addListener('click', function (e) { + if (e.target.classList.contains('gfoldercollapse') === false && + vEvents[column] && typeof vEvents[column] === 'function') { + vEvents[column](task, e, vTmpCell, column); + } + }, vTmpCell); +}; +exports.addListenerInputCell = function (vTmpCell, vEventsChange, callback, tasks, index, column, draw, event) { + if (draw === void 0) { draw = null; } + if (event === void 0) { event = 'blur'; } + var task = tasks[index]; + if (vTmpCell.children[0] && vTmpCell.children[0].children && vTmpCell.children[0].children[0]) { + var tagName = vTmpCell.children[0].children[0].tagName; + var selectInputOrButton = tagName === 'SELECT' || tagName === 'INPUT' || tagName === 'BUTTON'; + if (selectInputOrButton) { + exports.addListener(event, function (e) { + if (callback) { + callback(task, e); + } + if (vEventsChange[column] && typeof vEventsChange[column] === 'function') { + var q = vEventsChange[column](tasks, task, e, vTmpCell, vColumnsNames[column]); + if (q && q.then) { + q.then(function (e) { return draw(); }); + } + else { + draw(); + } + } + else { + draw(); + } + }, vTmpCell.children[0].children[0]); + } + } +}; +exports.addListenerDependencies = function (vLineOptions) { + var elements = document.querySelectorAll('.gtaskbarcontainer'); + for (var i = 0; i < elements.length; i++) { + var taskDiv = elements[i]; + taskDiv.addEventListener('mouseover', function (e) { + toggleDependencies(e, vLineOptions); + }); + taskDiv.addEventListener('mouseout', function (e) { + toggleDependencies(e, vLineOptions); + }); + } +}; +var toggleDependencies = function (e, vLineOptions) { + var target = e.currentTarget; + var ids = target.getAttribute('id').split('_'); + var style = vLineOptions && vLineOptions.borderStyleHover !== undefined ? vLineOptions.hoverStyle : 'groove'; + if (e.type === 'mouseout') { + style = ''; + } + if (ids.length > 1) { + var frameZones = Array.from(document.querySelectorAll(".gDepId" + ids[1])); + frameZones.forEach(function (c) { + c.style.borderStyle = style; + }); + // document.querySelectorAll(`.gDepId${ids[1]}`).forEach((c: any) => { + // c.style.borderStyle = style; + // }); + } +}; +var vColumnsNames = { + taskname: 'pName', + res: 'pRes', + dur: '', + comp: 'pComp', + start: 'pStart', + end: 'pEnd', + planstart: 'pPlanStart', + planend: 'pPlanEnd', + link: 'pLink', + cost: 'pCost', + mile: 'pMile', + group: 'pGroup', + parent: 'pParent', + open: 'pOpen', + depend: 'pDepend', + caption: 'pCaption', + note: 'pNotes' }; +},{"./utils/general_utils":13}],6:[function(require,module,exports){ +"use strict"; +/* + * Copyright (c) 2013-2018, Paul Geldart, Eduardo Rodrigues, Ricardo Cardoso and Mario Mol. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of AUTHORS nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL AUTHORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + This project is based on jsGantt 1.2, (which can be obtained from + https://code.google.com/p/jsgantt/) and remains under the original BSD license. + Copyright (c) 2009, Shlomy Gantz BlueBrick Inc. +*/ +Object.defineProperty(exports, "__esModule", { value: true }); +var events_1 = require("./events"); +var general_utils_1 = require("./utils/general_utils"); +var xml_1 = require("./xml"); +var task_1 = require("./task"); +var draw_1 = require("./draw"); +var json_1 = require("./json"); +var date_utils_1 = require("./utils/date_utils"); +if (!exports.JSGantt) + exports.JSGantt = {}; +exports.JSGantt.isIE = general_utils_1.isIE; +exports.JSGantt.TaskItem = task_1.TaskItem; +exports.JSGantt.GanttChart = draw_1.GanttChart; +exports.JSGantt.updateFlyingObj = general_utils_1.updateFlyingObj; +exports.JSGantt.showToolTip = events_1.showToolTip; +exports.JSGantt.stripIds = general_utils_1.stripIds; +exports.JSGantt.stripUnwanted = general_utils_1.stripUnwanted; +exports.JSGantt.delayedHide = general_utils_1.delayedHide; +exports.JSGantt.hideToolTip = general_utils_1.hideToolTip; +exports.JSGantt.fadeToolTip = general_utils_1.fadeToolTip; +exports.JSGantt.moveToolTip = general_utils_1.moveToolTip; +exports.JSGantt.getZoomFactor = general_utils_1.getZoomFactor; +exports.JSGantt.getOffset = general_utils_1.getOffset; +exports.JSGantt.getScrollPositions = general_utils_1.getScrollPositions; +exports.JSGantt.processRows = task_1.processRows; +exports.JSGantt.sortTasks = task_1.sortTasks; +// Used to determine the minimum date of all tasks and set lower bound based on format +exports.JSGantt.getMinDate = date_utils_1.getMinDate; +// Used to determine the maximum date of all tasks and set upper bound based on format +exports.JSGantt.getMaxDate = date_utils_1.getMaxDate; +// This function finds the document id of the specified object +exports.JSGantt.findObj = general_utils_1.findObj; +exports.JSGantt.changeFormat = general_utils_1.changeFormat; +// Tasks +exports.JSGantt.folder = events_1.folder; +exports.JSGantt.hide = events_1.hide; +exports.JSGantt.show = events_1.show; +exports.JSGantt.taskLink = task_1.taskLink; +exports.JSGantt.parseDateStr = date_utils_1.parseDateStr; +exports.JSGantt.formatDateStr = date_utils_1.formatDateStr; +exports.JSGantt.parseDateFormatStr = date_utils_1.parseDateFormatStr; +// XML +exports.JSGantt.parseXML = xml_1.parseXML; +exports.JSGantt.parseXMLString = xml_1.parseXMLString; +exports.JSGantt.findXMLNode = xml_1.findXMLNode; +exports.JSGantt.getXMLNodeValue = xml_1.getXMLNodeValue; +exports.JSGantt.AddXMLTask = xml_1.AddXMLTask; +// JSON +exports.JSGantt.parseJSON = json_1.parseJSON; +exports.JSGantt.parseJSONString = json_1.parseJSONString; +exports.JSGantt.addJSONTask = json_1.addJSONTask; +exports.JSGantt.benchMark = general_utils_1.benchMark; +exports.JSGantt.getIsoWeek = date_utils_1.getIsoWeek; +exports.JSGantt.addListener = events_1.addListener; +exports.JSGantt.addTooltipListeners = events_1.addTooltipListeners; +exports.JSGantt.addThisRowListeners = events_1.addThisRowListeners; +exports.JSGantt.addFolderListeners = events_1.addFolderListeners; +exports.JSGantt.addFormatListeners = events_1.addFormatListeners; +exports.JSGantt.addScrollListeners = events_1.addScrollListeners; +exports.JSGantt.criticalPath = general_utils_1.criticalPath; + +},{"./draw":2,"./events":5,"./json":7,"./task":10,"./utils/date_utils":11,"./utils/general_utils":13,"./xml":14}],7:[function(require,module,exports){ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var task_1 = require("./task"); +var general_utils_1 = require("./utils/general_utils"); +/** + * + * @param pFile + * @param pGanttlet + */ +exports.parseJSON = function (pFile, pGanttVar, vDebug, redrawAfter) { + if (vDebug === void 0) { vDebug = false; } + if (redrawAfter === void 0) { redrawAfter = true; } + return __awaiter(this, void 0, void 0, function () { + var jsonObj, bd, ad; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, general_utils_1.makeRequest(pFile, true, true)]; + case 1: + jsonObj = _a.sent(); + if (vDebug) { + bd = new Date(); + console.info('before jsonparse', bd); + } + exports.addJSONTask(pGanttVar, jsonObj); + if (this.vDebug) { + ad = new Date(); + console.info('after addJSONTask', ad, (ad.getTime() - bd.getTime())); + } + if (redrawAfter) { + pGanttVar.Draw(); + } + return [2 /*return*/, jsonObj]; + } + }); + }); +}; +exports.parseJSONString = function (pStr, pGanttVar) { + exports.addJSONTask(pGanttVar, JSON.parse(pStr)); +}; +exports.addJSONTask = function (pGanttVar, pJsonObj) { + for (var index = 0; index < pJsonObj.length; index++) { + var id = void 0; + var name_1 = void 0; + var start = void 0; + var end = void 0; + var planstart = void 0; + var planend = void 0; + var itemClass = void 0; + var link = ''; + var milestone = 0; + var resourceName = ''; + var completion = void 0; + var group = 0; + var parent_1 = void 0; + var open_1 = void 0; + var dependsOn = ''; + var caption = ''; + var notes = ''; + var cost = void 0; + var duration = ''; + var bartext = ''; + var additionalObject = {}; + for (var prop in pJsonObj[index]) { + var property = prop; + var value = pJsonObj[index][property]; + switch (property.toLowerCase()) { + case 'pid': + case 'id': + id = value; + break; + case 'pname': + case 'name': + name_1 = value; + break; + case 'pstart': + case 'start': + start = value; + break; + case 'pend': + case 'end': + end = value; + break; + case 'pplanstart': + case 'planstart': + planstart = value; + break; + case 'pplanend': + case 'planend': + planend = value; + break; + case 'pclass': + case 'class': + itemClass = value; + break; + case 'plink': + case 'link': + link = value; + break; + case 'pmile': + case 'mile': + milestone = value; + break; + case 'pres': + case 'res': + resourceName = value; + break; + case 'pcomp': + case 'comp': + completion = value; + break; + case 'pgroup': + case 'group': + group = value; + break; + case 'pparent': + case 'parent': + parent_1 = value; + break; + case 'popen': + case 'open': + open_1 = value; + break; + case 'pdepend': + case 'depend': + dependsOn = value; + break; + case 'pcaption': + case 'caption': + caption = value; + break; + case 'pnotes': + case 'notes': + notes = value; + break; + case 'pcost': + case 'cost': + cost = value; + break; + case 'duration': + case 'pduration': + duration = value; + break; + case 'bartext': + case 'pbartext': + bartext = value; + break; + default: + additionalObject[property.toLowerCase()] = value; + } + } + //if (id != undefined && !isNaN(parseInt(id)) && isFinite(id) && name && start && end && itemClass && completion != undefined && !isNaN(parseFloat(completion)) && isFinite(completion) && !isNaN(parseInt(parent)) && isFinite(parent)) { + pGanttVar.AddTaskItem(new task_1.TaskItem(id, name_1, start, end, itemClass, link, milestone, resourceName, completion, group, parent_1, open_1, dependsOn, caption, notes, pGanttVar, cost, planstart, planend, duration, bartext, additionalObject)); + //} + } +}; + +},{"./task":10,"./utils/general_utils":13}],8:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var es = { + 'january': 'Enero', + 'february': 'Febrero', + 'march': 'Marzo', + 'april': 'Abril', + 'maylong': 'Mayo', + 'june': 'Junio', + 'july': 'Julio', + 'august': 'Agosto', + 'september': 'Septiembre', + 'october': 'Octubre', + 'november': 'Noviembre', + 'december': 'Diciembre', + 'jan': 'Ene', + 'feb': 'Feb', + 'mar': 'Mar', + 'apr': 'Abr', + 'may': 'May', + 'jun': 'Jun', + 'jul': 'Jul', + 'aug': 'Ago', + 'sep': 'Sep', + 'oct': 'Oct', + 'nov': 'Nov', + 'dec': 'Dic', + 'sunday': 'Domingo', + 'monday': 'Lunes', + 'tuesday': 'Martes', + 'wednesday': 'Miércoles', + 'thursday': 'Jueves', + 'friday': 'Viernes', + 'saturday': 'Sábado', + 'sun': ' Dom', + 'mon': ' Lun', + 'tue': ' Mar', + 'wed': ' Mie', + 'thu': ' Jue', + 'fri': ' Vie', + 'sat': ' Sab', + 'res': 'Recurso', + 'dur': 'Duración', + 'comp': '% Compl.', + 'completion': 'Completado', + 'startdate': 'Inicio', + 'planstartdate': 'Inicio Planificado', + 'cost': 'Coste', + 'enddate': 'Fin', + 'planenddate': 'Fin Planificado', + 'moreinfo': 'Más Información', + 'nodata': 'No tasks found', + 'notes': 'Notas', + 'format': 'Formato', + 'hour': 'Hora', + 'day': 'Día', + 'week': 'Semana', + 'month': 'Mes', + 'quarter': 'Trimestre', + 'hours': 'Horas', + 'days': 'Días', + 'weeks': 'Semanas', + 'months': 'Meses', + 'quarters': 'Trimestres', + 'hr': 'h', + 'dy': 'Día', + 'wk': 'Sem.', + 'mth': 'Mes', + 'qtr': 'Trim.', + 'hrs': 'h', + 'dys': 'Días', + 'wks': 'Sem.', + 'mths': 'Meses', + 'qtrs': 'Trim.', + 'tooltipLoading': 'Cargando...' +}; +exports.es = es; +var en = { + 'format': 'Format', + 'hour': 'Hour', + 'day': 'Day', + 'week': 'Week', + 'month': 'Month', + 'quarter': 'Quarter', + 'hours': 'Hours', + 'days': 'Days', + 'weeks': 'Weeks', + 'months': 'Months', + 'quarters': 'Quarters', + 'hr': 'Hr', + 'dy': 'Day', + 'wk': 'Wk', + 'mth': 'Mth', + 'qtr': 'Qtr', + 'hrs': 'Hrs', + 'dys': 'Days', + 'wks': 'Wks', + 'mths': 'Mths', + 'qtrs': 'Qtrs', + 'res': 'Resource', + 'dur': 'Duration', + 'comp': '% Comp.', + 'completion': 'Completion', + 'startdate': 'Start Date', + 'planstartdate': 'Plan Start Date', + 'enddate': 'End Date', + 'planenddate': 'Plan End Date', + 'cost': 'Cost', + 'moreinfo': 'More Information', + 'nodata': 'No tasks found', + 'notes': 'Notes', + 'january': 'January', + 'february': 'February', + 'march': 'March', + 'april': 'April', + 'maylong': 'May', + 'june': 'June', + 'july': 'July', + 'august': 'August', + 'september': 'September', + 'october': 'October', + 'november': 'November', + 'december': 'December', + 'jan': 'Jan', + 'feb': 'Feb', + 'mar': 'Mar', + 'apr': 'Apr', + 'may': 'May', + 'jun': 'Jun', + 'jul': 'Jul', + 'aug': 'Aug', + 'sep': 'Sep', + 'oct': 'Oct', + 'nov': 'Nov', + 'dec': 'Dec', + 'sunday': 'Sunday', + 'monday': 'Monday', + 'tuesday': 'Tuesday', + 'wednesday': 'Wednesday', + 'thursday': 'Thursday', + 'friday': 'Friday', + 'saturday': 'Saturday', + 'sun': 'Sun', + 'mon': 'Mon', + 'tue': 'Tue', + 'wed': 'Wed', + 'thu': 'Thu', + 'fri': 'Fri', + 'sat': 'Sat', + 'tooltipLoading': 'Loading...' +}; +exports.en = en; +var de = { + 'format': 'Ansicht', + 'hour': 'Stunde', + 'day': 'Tag', + 'week': 'Woche', + 'month': 'Monat', + 'quarter': 'Quartal', + 'hours': 'Stunden', + 'days': 'Tage', + 'weeks': 'Wochen', + 'months': 'Monate', + 'quarters': 'Quartale', + 'hr': 'h', + 'dy': 'T', + 'wk': 'W', + 'mth': 'M', + 'qtr': 'Q', + 'hrs': 'Std', + 'dys': 'Tage', + 'wks': 'Wochen', + 'mths': 'Monate', + 'qtrs': 'Quartal', + 'res': 'Resource', + 'dur': 'Dauer', + 'comp': '%Fertig', + 'completion': 'Fertigstellung', + 'startdate': 'Erste Buchu', + 'planstartdate': 'Erste Buchu Plan', + 'enddate': 'Letzte Buchung', + 'planenddate': 'Plan Letzte Buchung', + 'cost': 'Cost', + 'moreinfo': 'Weitere Infos', + 'nodata': 'No tasks found', + 'notes': 'Anmerkung', + 'january': 'Jänner', + 'february': 'Februar', + 'march': 'März', + 'april': 'April', + 'maylong': 'Mai', + 'june': 'Juni', + 'july': 'Juli', + 'august': 'August', + 'september': 'September', + 'october': 'Oktober', + 'november': 'November', + 'december': 'Dezember', + 'jan': 'Jan', + 'feb': 'Feb', + 'mar': 'Mar', + 'apr': 'Apr', + 'may': 'Mai', + 'jun': 'Jun', + 'jul': 'Jul', + 'aug': 'Aug', + 'sep': 'Sep', + 'oct': 'Okt', + 'nov': 'Nov', + 'dec': 'Dez', + 'sunday': 'Sonntag', + 'monday': 'Montag', + 'tuesday': 'Dienstag', + 'wednesday': 'Mittwoch', + 'thursday': 'Donnerstag', + 'friday': 'Freitag', + 'saturday': 'Samstag', + 'sun': 'So', + 'mon': 'Mo', 'tue': 'Di', 'wed': 'Mi', 'thu': 'Do', 'fri': 'Fr', 'sat': 'Sa' +}; +exports.de = de; +var pt = { + 'hours': 'Horas', + 'days': 'Dias', + 'weeks': 'Weeks', + 'months': 'Months', + 'quarters': 'Quarters', + 'format': 'Formato', + 'hour': 'Hora', + 'day': 'Dia', + 'week': 'Semana', + 'month': 'Mês', + 'quarter': 'Trimestre', + 'hr': 'hr', + 'dy': 'dia', + 'wk': 'sem.', + 'mth': 'mês', + 'qtr': 'qtr', + 'hrs': 'hrs', + 'dys': 'dias', + 'wks': 'sem.', + 'mths': 'meses', + 'qtrs': 'qtrs', + 'completion': 'Terminado', + 'comp': '% Completado', + 'moreinfo': 'Mais informações', + 'nodata': 'Sem atividades', + 'notes': 'Notas', + 'res': 'Responsável', + 'dur': 'Duração', + 'startdate': 'Data inicial', + 'planstartdate': 'Plan Data inicial', + 'enddate': 'Data final', + 'planenddate': 'Plan Data final', + 'cost': 'Custo', + 'jan': 'Jan', + 'feb': 'Fev', + 'mar': 'Mar', + 'apr': 'Abr', + 'may': 'Mai', + 'jun': 'Jun', + 'jul': 'Jul', + 'aug': 'Ago', + 'sep': 'Set', + 'oct': 'Out', + 'nov': 'Nov', + 'dec': 'Dez', + 'january': 'Janeiro', + 'february': 'Fevereiro', + 'march': 'Março', + 'april': 'Abril', + 'maylong': 'Maio', + 'june': 'Junho', + 'july': 'Julho', + 'august': 'Agosto', + 'september': 'Setembro', + 'october': 'Outubro', + 'november': 'Novembro', + 'december': 'Dezembro', + 'sun': 'Dom', + 'mon': 'Seg', + 'tue': 'Ter', + 'wed': 'Qua', + 'thu': 'Qui', + 'fri': 'Sex', + 'sat': 'Sab' +}; +exports.pt = pt; +var ru = { + 'january': 'Январь', + 'february': 'Февраль', + 'march': 'Март', + 'april': 'Апрель', + 'maylong': 'Май', + 'june': 'Июнь', + 'july': 'Июль', + 'august': 'Август', 'september': 'Сентябрь', + 'october': 'Октябрь', + 'november': 'Ноябрь', + 'december': 'Декабрь', + 'jan': 'Янв', + 'feb': 'Фев', + 'mar': 'Мар', + 'apr': 'Апр', + 'may': 'Май', + 'jun': 'Июн', + 'jul': 'Июл', + 'aug': 'Авг', + 'sep': 'Сен', + 'oct': 'Окт', + 'nov': 'Ноя', + 'dec': 'Дек', + 'sunday': 'Воскресенье', + 'monday': 'Понедельник', + 'tuesday': 'Вторник', + 'wednesday': 'Среда', + 'thursday': 'Четверг', + 'friday': 'Пятница', + 'saturday': 'Суббота', + 'sun': ' Вс', + 'mon': ' Пн', + 'tue': ' Вт', + 'wed': ' Ср', + 'thu': ' Чт', + 'fri': ' Пт', + 'sat': ' Сб', + 'res': 'Ресурс', + 'dur': 'Длительность', + 'comp': '% выполнения', + 'completion': 'Выполнено', + 'startdate': 'Нач. дата', + 'planstartdate': 'Plan Нач. дата', + 'enddate': 'Кон. дата', + 'planenddate': 'Plan Кон. дата', + 'cost': 'Cost', + 'moreinfo': 'Детали', + 'nodata': 'No tasks found', + 'notes': 'Заметки', + 'format': 'Формат', + 'hour': 'Час', + 'day': 'День', + 'week': 'Неделя', + 'month': 'Месяц', + 'quarter': 'Кварт', + 'hours': 'Часов', + 'days': 'Дней', + 'weeks': 'Недель', + 'months': 'Месяцев', + 'quarters': 'Кварталов', + 'hr': 'ч.', + 'dy': 'дн.', + 'wk': 'нед.', + 'mth': 'мес.', + 'qtr': 'кв.', + 'hrs': 'ч.', + 'dys': 'дн.', + 'wks': 'нед.', + 'mths': 'мес.', + 'qtrs': 'кв.', + 'tooltipLoading': 'Загрузка...' +}; +exports.ru = ru; +/** + * Mois : http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=3619 + Jours : http://bdl.oqlf.gouv.qc.ca/bdl/gabarit_bdl.asp?id=3617 + */ +var fr = { + 'january': 'Janvier', + 'february': 'Février', + 'march': 'Mars', + 'april': 'Avril', + 'maylong': 'Mai', + 'june': 'Juin', + 'july': 'Juillet', + 'august': 'Août', + 'september': 'Septembre', + 'october': 'Octobre', + 'november': 'Novembre', + 'december': 'Décembre', + 'jan': 'Janv', + 'feb': 'Févr', + 'mar': 'Mars', + 'apr': 'Avr', + 'may': 'Mai', + 'jun': 'Juin', + 'jul': 'Juil', + 'aug': 'Août', + 'sep': 'Sept', + 'oct': 'Oct', + 'nov': 'Nov', + 'dec': 'Déc', + 'sunday': 'Dimanche', + 'monday': 'Lundi', + 'tuesday': 'Mardi', + 'wednesday': 'Mercredi', + 'thursday': 'Jeudi', + 'friday': 'Vendredi', + 'saturday': 'Samedi', + 'sun': 'Dim', + 'mon': 'Lun', + 'tue': 'Mar', + 'wed': 'Mer', + 'thu': 'Jeu', + 'fri': 'Ven', + 'sat': 'Sam', + 'res': 'Ressource', + 'dur': 'Durée', + 'comp': '% Term.', + 'completion': 'Terminé', + 'startdate': 'Début', + 'planstartdate': 'Plan Début', + 'enddate': 'Fin', + 'planenddate': 'Plan Fin', + 'cost': 'Cost', + 'moreinfo': "Plus d'informations", + 'nodata': 'No tasks found', + 'notes': 'Notes', + 'format': 'Format', + 'hour': 'Heure', + 'day': 'Jour', + 'week': 'Semaine', + 'month': 'Mois', + 'quarter': 'Trimestre', + 'hours': 'Heures', + 'days': 'Jours', + 'weeks': 'Semaines', + 'months': 'Mois', + 'quarters': 'Trimestres', + 'hr': 'h', + 'dy': 'j', + 'wk': 'sem', + 'mth': 'mois', + 'qtr': 'tri', + 'hrs': 'h', + 'dys': 'j', + 'wks': 'sem', + 'mths': 'mois', + 'qtrs': 'tri' +}; +exports.fr = fr; +var cn = { + 'january': '一月', + 'february': '二月', + 'march': '三月', + 'april': '四月', + 'maylong': '五月', + 'june': '六月', + 'july': '七月', + 'august': '八月', + 'september': '九月', + 'october': '十月', + 'november': '十一月', + 'december': '十二月', + 'jan': '一月', + 'feb': '二月', + 'mar': '三月', + 'apr': '四月', + 'may': '五月', + 'jun': '六月', + 'jul': '七月', + 'aug': '八月', + 'sep': '九月', + 'oct': '十月', + 'nov': '十一月', + 'dec': '十二月', + 'sunday': '星期日', + 'monday': '星期一', + 'tuesday': '星期二', + 'wednesday': '星期三', + 'thursday': '星期四', + 'friday': '星期五', + 'saturday': '星期六', + 'sun': '星期日', + 'mon': '星期一', + 'tue': '星期二', + 'wed': '星期三', + 'thu': '星期四', + 'fri': '星期五', + 'sat': '星期六', + 'res': '資源', + 'dur': '時程', + 'comp': '達成率', + 'completion': '達成', + 'startdate': '起始日期', + 'planstartdate': '計劃起始日期', + 'enddate': '截止日期', + 'planenddate': '計劃截止日期', + 'cost': '成本', + 'moreinfo': "更多資訊", + 'nodata': 'No tasks found', + 'notes': '備註', + 'format': '格式', + 'hour': '時', + 'day': '日', + 'week': '星期', + 'month': '月', + 'quarter': '季', + 'hours': '小時', + 'days': '天', + 'weeks': '週', + 'months': '月', + 'quarters': '季', + 'hr': '小時', + 'dy': '天', + 'wk': '週', + 'mth': '月', + 'qtr': '季', + 'hrs': '小時', + 'dys': '天', + 'wks': '週', + 'mths': '月', + 'qtrs': '季' +}; +exports.cn = cn; +var sv = { + 'format': 'Filter', + 'hour': 'Timme', + 'day': 'Dag', + 'week': 'Vecka', + 'month': 'Månad', + 'quarter': 'Kvartal', + 'hours': 'Timmar', + 'days': 'Dagar', + 'weeks': 'Veckor', + 'months': 'Månader', + 'quarters': 'Kvartal', + 'hr': 'Timme', + 'dy': 'Dag', + 'wk': 'Vecka', + 'mth': 'Månad', + 'qtr': 'Q', + 'hrs': 'Timmar', + 'dys': 'Dagar', + 'wks': 'Veckor', + 'mths': 'Månader', + 'qtrs': 'Q', + 'res': 'Resurs', + 'dur': 'Tidsåtgång', + 'comp': '% klart', + 'completion': 'Klart', + 'startdate': 'Startdatum', + 'planstartdate': 'Planerad startdatum', + 'enddate': 'Slutdatum', + 'planenddate': 'Planerad slutdatum', + 'cost': 'Kostnad', + 'moreinfo': 'Mer Information', + 'nodata': 'No tasks found', + 'notes': 'Notes', + 'january': 'januari', + 'february': 'februari', + 'march': 'mars', + 'april': 'april', + 'maylong': 'maj', + 'june': 'juni', + 'july': 'juli', + 'august': 'augusti', + 'september': 'september', + 'october': 'oktober', + 'november': 'november', + 'december': 'december', + 'jan': 'jan', + 'feb': 'feb', + 'mar': 'mar', + 'apr': 'apr', + 'may': 'maj', + 'jun': 'jun', + 'jul': 'jul', + 'aug': 'aug', + 'sep': 'sep', + 'oct': 'okt', + 'nov': 'nov', + 'dec': 'dec', + 'sunday': 'söndag', + 'monday': 'måndag', + 'tuesday': 'tisdag', + 'wednesday': 'onsdag', + 'thursday': 'torsdag', + 'friday': 'fredag', + 'saturday': 'lördag', + 'sun': 'sön', + 'mon': 'mån', + 'tue': 'tis', + 'wed': 'ons', + 'thu': 'tor', + 'fri': 'fre', + 'sat': 'lör' +}; +exports.sv = sv; +var nl = { + 'format': 'Format', + 'hour': 'Uur', + 'day': 'Dag', + 'week': 'Week', + 'month': 'Maand', + 'quarter': 'Kwartaal', + 'hours': 'Uren', + 'days': 'Dagen', + 'weeks': 'Weken', + 'months': 'Maanden', + 'quarters': 'Kwartalen', + 'hr': 'uur', + 'dy': 'dag', + 'wk': 'wk', + 'mth': 'mnd', + 'qtr': 'kw', + 'hrs': 'uren', + 'dys': 'dagen', + 'wks': 'weken', + 'mths': 'maanden', + 'qtrs': 'kwartalen', + 'res': 'Resource', + 'dur': 'Doorlooptijd', + 'comp': '% gereed', + 'completion': 'Gereed', + 'startdate': 'Startdatum', + 'planstartdate': 'Geplande startdatum', + 'enddate': 'Einddatum', + 'planenddate': 'Geplande einddatum', + 'cost': 'Kosten', + 'moreinfo': 'Meer informatie', + 'nodata': 'No tasks found', + 'notes': 'Notities', + 'january': 'januari', + 'february': 'februari', + 'march': 'maart', + 'april': 'april', + 'maylong': 'mei', + 'june': 'juni', + 'july': 'juli', + 'august': 'augustus', + 'september': 'september', + 'october': 'oktober', + 'november': 'november', + 'december': 'december', + 'jan': 'jan', + 'feb': 'feb', + 'mar': 'mrt', + 'apr': 'apr', + 'may': 'mei', + 'jun': 'jun', + 'jul': 'jul', + 'aug': 'aug', + 'sep': 'sep', + 'oct': 'okt', + 'nov': 'nov', + 'dec': 'dec', + 'sunday': 'zondag', + 'monday': 'maandag', + 'tuesday': 'dinsdag', + 'wednesday': 'woensdag', + 'thursday': 'donderdag', + 'friday': 'vrijdag', + 'saturday': 'zaterdag', + 'sun': 'zo', + 'mon': 'ma', + 'tue': 'di', + 'wed': 'wo', + 'thu': 'do', + 'fri': 'vr', + 'sat': 'za' +}; +exports.nl = nl; +var id = { + 'format': 'Format', + 'hour': 'Jam', + 'day': 'Hari', + 'week': 'Minggu', + 'month': 'Bulan', + 'quarter': 'Kuartal', + 'hours': 'Jam', + 'days': 'Hari', + 'weeks': 'Minggu', + 'months': 'Bulan', + 'quarters': 'Kuartal', + 'hr': 'Jam', + 'dy': 'Hari', + 'wk': 'Min', + 'mth': 'Bln', + 'qtr': 'Krtl', + 'hrs': 'Jam', + 'dys': 'Hari', + 'wks': 'Min', + 'mths': 'Bln', + 'qtrs': 'Krtl', + 'res': 'Sumber Daya', + 'dur': 'Durasi', + 'comp': '% Penyelesaian', + 'completion': 'Penyelesaian', + 'startdate': 'Tanggal Mulai', + 'planstartdate': 'Perencanaan Tanggal Mulai', + 'enddate': 'Tanggal Akhir', + 'planenddate': 'Perencanaan Tanggal Akhir', + 'cost': 'Biaya', + 'moreinfo': 'Informasi Lebih Lanjut', + 'nodata': 'No tasks found', + 'notes': 'Catatan', + 'january': 'Januari', + 'february': 'Februari', + 'march': 'Maret', + 'april': 'April', + 'maylong': 'Mei', + 'june': 'Juni', + 'july': 'Juli', + 'august': 'Agustus', + 'september': 'September', + 'october': 'Oktober', + 'november': 'November', + 'december': 'Desember', + 'jan': 'Jan', + 'feb': 'Feb', + 'mar': 'Mar', + 'apr': 'Apr', + 'may': 'Mei', + 'jun': 'Jun', + 'jul': 'Jul', + 'aug': 'Agu', + 'sep': 'Sep', + 'oct': 'Okt', + 'nov': 'Nov', + 'dec': 'Des', + 'sunday': 'Minggu', + 'monday': 'Senin', + 'tuesday': 'Selasa', + 'wednesday': 'Rabu', + 'thursday': 'Kamis', + 'friday': 'Jumat', + 'saturday': 'Sabtu', + 'sun': 'Min', + 'mon': 'Sen', + 'tue': 'Sel', + 'wed': 'Rab', + 'thu': 'Kam', + 'fri': 'Jum', + 'sat': 'Sab' +}; +exports.id = id; +var tr = { + 'format': 'Biçim', + 'hour': 'Saat', + 'day': 'Gün', + 'week': 'Hafta', + 'month': 'Ay', + 'quarter': 'Çeyrek Yıl', + 'hours': 'Saat', + 'days': 'Gün', + 'weeks': 'Hafta', + 'months': 'Ay', + 'quarters': 'Çeyrek Yıl', + 'hr': 'Saat', + 'dy': 'Gün', + 'wk': 'Hft', + 'mth': 'Ay', + 'qtr': 'Çyrk', + 'hrs': 'Saat', + 'dys': 'Gün', + 'wks': 'Hft', + 'mths': 'Ay', + 'qtrs': 'Çyrk', + 'res': 'Kaynak', + 'dur': 'Süre', + 'comp': '% Tamamlanma.', + 'completion': 'Tamamlanma', + 'startdate': 'Başlangıç Tarihi', + 'planstartdate': 'Plan Başlama Tarihi', + 'enddate': 'Bitiş Tarihi', + 'planenddate': 'Plan Bitiş Tarihi', + 'cost': 'Tutar', + 'moreinfo': 'Daha Fazla Bilgi', + 'nodata': 'No tasks found', + 'notes': 'Notlar', + 'january': 'Ocak', + 'february': 'Şubat', + 'march': 'Mart', + 'april': 'Nisan', + 'maylong': 'Mayıs', + 'june': 'Haziran', + 'july': 'Temmuz', + 'august': 'Ağustos', + 'september': 'Eylül', + 'october': 'Ekim', + 'november': 'Kasım', + 'december': 'Aralık', + 'jan': 'Oca', + 'feb': 'Şub', + 'mar': 'Mar', + 'apr': 'Nis', + 'may': 'May', + 'jun': 'Haz', + 'jul': 'Tem', + 'aug': 'Ağu', + 'sep': 'Eyl', + 'oct': 'Eki', + 'nov': 'Kas', + 'dec': 'Ara', + 'sunday': 'Pazar', + 'monday': 'Pazartesi', + 'tuesday': 'Salı', + 'wednesday': 'Çarşamba', + 'thursday': 'Perşembe', + 'friday': 'Cuma', + 'saturday': 'Cumartesi', + 'sun': 'Paz', + 'mon': 'Pzt', + 'tue': 'Sal', + 'wed': 'Çrş', + 'thu': 'Prş', + 'fri': 'Cum', + 'sat': 'Cmt' +}; +exports.tr = tr; +var ja = { + 'format': 'タイムライン表示', + 'hour': '時', + 'day': '日', + 'week': '週', + 'month': '月', + 'quarter': '四半期', + 'hours': '時間', + 'days': '日間', + 'weeks': '週間', + 'months': '月間', + 'quarters': '四半期', + 'hr': '時', + 'dy': '日', + 'wk': '週', + 'mth': '月', + 'qtr': '四', + 'hrs': '時間', + 'dys': '日間', + 'wks': '週間', + 'mths': '月間', + 'qtrs': '四半期', + 'res': 'リソース', + 'dur': '期間', + 'comp': '進捗率', + 'completion': '進捗率', + 'startdate': '開始日', + 'planstartdate': '予定開始日', + 'enddate': '期日', + 'planenddate': '予定期日', + 'cost': 'コスト', + 'moreinfo': '詳細', + 'nodata': 'No tasks found', + 'notes': 'ノート', + 'january': '1月', + 'february': '2月', + 'march': '3月', + 'april': '4月', + 'maylong': '5月', + 'june': '6月', + 'july': '7月', + 'august': '8月', + 'september': '9月', + 'october': '10月', + 'november': '11月', + 'december': '12月', + 'jan': '1月', + 'feb': '2月', + 'mar': '3月', + 'apr': '4月', + 'may': '5月', + 'jun': '6月', + 'jul': '7月', + 'aug': '8月', + 'sep': '9月', + 'oct': '10月', + 'nov': '11月', + 'dec': '12月', + 'sunday': '日曜日', + 'monday': '月曜日', + 'tuesday': '火曜日', + 'wednesday': '水曜日', + 'thursday': '木曜日', + 'friday': '金曜日', + 'saturday': '土曜日', + 'sun': '日', + 'mon': '月', + 'tue': '火', + 'wed': '水', + 'thu': '木', + 'fri': '金', + 'sat': '土', + 'tooltipLoading': 'ローディング中...' +}; +exports.ja = ja; +var cs = { + 'format': 'Zobrazení', + 'hour': 'Hodina', + 'day': 'Den', + 'week': 'Týden', + 'month': 'Měsíc', + 'quarter': 'Kvartál', + 'hours': 'Hodiny', + 'days': 'Dni', + 'weeks': 'Týdny', + 'months': 'Měsíce', + 'quarters': 'Kvartály', + 'hr': 'Ho', + 'dy': 'Den', + 'wk': 'Tyd', + 'mth': 'Měs', + 'qtr': 'Kvar', + 'hrs': 'Ho', + 'dys': 'Dni', + 'wks': 'Tyd', + 'mths': 'Měs', + 'qtrs': 'Kvar', + 'res': 'Přiřazeno', + 'dur': 'Trvání', + 'comp': '% Hotovo', + 'completion': 'Hotovo', + 'startdate': 'Start', + 'planstartdate': 'Plánovaný start', + 'enddate': 'Konec', + 'planenddate': 'Plánovaný konec', + 'cost': 'Náklady', + 'moreinfo': 'Více informací', + 'nodata': 'No tasks found', + 'notes': 'Poznámky', + 'january': 'Leden', + 'february': 'Únor', + 'march': 'Březen', + 'april': 'Duben', + 'maylong': 'Květen', + 'june': 'Červen', + 'july': 'Červenec', + 'august': 'Srpen', + 'september': 'Září', + 'october': 'Říjen', + 'november': 'Listopad', + 'december': 'Prosinec', + 'jan': 'Led', + 'feb': 'Úno', + 'mar': 'Bře', + 'apr': 'Dub', + 'may': 'Kvě', + 'jun': 'Čer', + 'jul': 'Čvc', + 'aug': 'Srp', + 'sep': 'Zář', + 'oct': 'Říj', + 'nov': 'Lis', + 'dec': 'Pro', + 'sunday': 'Neděle', + 'monday': 'Pondělí', + 'tuesday': 'Úterý', + 'wednesday': 'Středa', + 'thursday': 'Čtvrtek', + 'friday': 'Pátek', + 'saturday': 'Sobota', + 'sun': 'Ne', + 'mon': 'Po', + 'tue': 'Út', + 'wed': 'St', + 'thu': 'Čt', + 'fri': 'Pa', + 'sat': 'So', + 'tooltipLoading': 'Nahrávám...' +}; +exports.cs = cs; +var hu = { + 'format': 'Formátum', + 'hour': 'Óra', + 'day': 'Nap', + 'week': 'Hét', + 'month': 'Hónap', + 'quarter': 'Negyedév ', + 'hours': 'Órák', + 'days': 'Nap', + 'weeks': 'Hét', + 'months': 'Hónap', + 'quarters': 'Negyedév', + 'hr': 'Ó', + 'dy': 'Nap', + 'wk': 'Hét', + 'mth': 'Hó', + 'qtr': 'NÉ', + 'hrs': 'Óra', + 'dys': 'Nap', + 'wks': 'Hét', + 'mths': 'Hó', + 'qtrs': 'NÉ', + 'res': 'Erőforrás', + 'dur': 'Időtartam', + 'comp': '% Kész', + 'completion': 'Elkészült', + 'startdate': 'Kezdés', + 'planstartdate': 'Tervezett kezdés', + 'enddate': 'Befejezés', + 'planenddate': 'Tervezett befejezés', + 'cost': 'Költség', + 'moreinfo': 'További információ', + 'nodata': 'No tasks found', + 'notes': 'Jegyzetek', + 'january': 'Január', + 'february': 'Február', + 'march': 'Március', + 'april': 'Április', + 'maylong': 'Május', + 'june': 'Június', + 'july': 'Július', + 'august': 'Augusztus', + 'september': 'Szeptember', + 'october': 'Október', + 'november': 'November', + 'december': 'December', + 'jan': 'Jan', + 'feb': 'Feb', + 'mar': 'Már', + 'apr': 'Ápr', + 'may': 'Máj', + 'jun': 'Jún', + 'jul': 'Júl', + 'aug': 'Aug', + 'sep': 'Szep', + 'oct': 'Okt', + 'nov': 'Nov', + 'dec': 'Dec', + 'sunday': 'Vasárnap', + 'monday': 'Hétfő', + 'tuesday': 'Kedd', + 'wednesday': 'Szerda', + 'thursday': 'Csütörtök', + 'friday': 'Péntek', + 'saturday': 'Szombat', + 'sun': 'Vas', + 'mon': 'Hé', + 'tue': 'Ke', + 'wed': 'Sze', + 'thu': 'Csü', + 'fri': 'Pén', + 'sat': 'Szo', + 'tooltipLoading': 'Belöltés...' +}; +exports.hu = hu; +var ko = { + 'format': '구분', + 'hour': '시', + 'day': '일', + 'week': '주', + 'month': '월', + 'quarter': '분기', + 'hours': '시', + 'days': '일', + 'weeks': '주', + 'months': '월', + 'quarters': '분기', + 'hr': '시', + 'dy': '일', + 'wk': '주', + 'mth': '월', + 'qtr': '분기', + 'hrs': '시', + 'dys': '일', + 'wks': '주', + 'mths': '월', + 'qtrs': '분기', + 'res': '이름', + 'dur': '기간', + 'comp': '% ', + 'completion': '완료', + 'startdate': '시작일자', + 'planstartdate': '계획 시작일자', + 'enddate': '종료일자', + 'planenddate': '계획 종료일자', + 'cost': '비용', + 'moreinfo': '더 많은 정보', + 'nodata': 'No tasks found', + 'notes': '비고', + 'january': '1월', + 'february': '2월', + 'march': '3월', + 'april': '4월', + 'maylong': '5월', + 'june': '6월', + 'july': '7월', + 'august': '8월', + 'september': '9월', + 'october': '10월', + 'november': '11월', + 'december': '12월', + 'jan': '1', + 'feb': '2', + 'mar': '3', + 'apr': '4', + 'may': '5', + 'jun': '6', + 'jul': '7', + 'aug': '8', + 'sep': '9', + 'oct': '10', + 'nov': '11', + 'dec': '12', + 'sunday': '일요일', + 'monday': '월요일', + 'tuesday': '화요일', + 'wednesday': '수요일', + 'thursday': '목요일', + 'friday': '금요일', + 'saturday': '토요일', + 'sun': '일', + 'mon': '월', + 'tue': '화', + 'wed': '수', + 'thu': '목', + 'fri': '금', + 'sat': '토', + 'tooltipLoading': '로딩중...' +}; +exports.ko = ko; + +},{}],9:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var date_utils_1 = require("./utils/date_utils"); +var draw_columns_1 = require("./draw_columns"); +exports.includeGetSet = function () { + /** + * SETTERS + */ + this.setOptions = function (options) { + var keys = Object.keys(options); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var val = options[key]; + if (key === 'vResources' || key === 'vColumnOrder') { + // ev = `this.set${key.substr(1)}(val)`; + this['set' + key.substr(1)](val); + } + else if (val instanceof Array) { + // ev = `this.set${key.substr(1)}(...val)`; + this['set' + key.substr(1)].apply(this, val); + } + else { + // ev = `this.set${key.substr(1)}(val)`; + this['set' + key.substr(1)](val); + } + } + }; + this.setUseFade = function (pVal) { this.vUseFade = pVal; }; + this.setUseMove = function (pVal) { this.vUseMove = pVal; }; + this.setUseRowHlt = function (pVal) { this.vUseRowHlt = pVal; }; + this.setUseToolTip = function (pVal) { this.vUseToolTip = pVal; }; + this.setUseSort = function (pVal) { this.vUseSort = pVal; }; + this.setUseSingleCell = function (pVal) { this.vUseSingleCell = pVal * 1; }; + this.setFormatArr = function () { + var vValidFormats = 'hour day week month quarter'; + this.vFormatArr = new Array(); + for (var i = 0, j = 0; i < arguments.length; i++) { + if (vValidFormats.indexOf(arguments[i].toLowerCase()) != -1 && arguments[i].length > 1) { + this.vFormatArr[j++] = arguments[i].toLowerCase(); + var vRegExp = new RegExp('(?:^|\s)' + arguments[i] + '(?!\S)', 'g'); + vValidFormats = vValidFormats.replace(vRegExp, ''); + } + } + }; + this.setShowRes = function (pVal) { this.vShowRes = pVal; }; + this.setShowDur = function (pVal) { this.vShowDur = pVal; }; + this.setShowComp = function (pVal) { this.vShowComp = pVal; }; + this.setShowStartDate = function (pVal) { this.vShowStartDate = pVal; }; + this.setShowEndDate = function (pVal) { this.vShowEndDate = pVal; }; + this.setShowPlanStartDate = function (pVal) { this.vShowPlanStartDate = pVal; }; + this.setShowPlanEndDate = function (pVal) { this.vShowPlanEndDate = pVal; }; + this.setShowCost = function (pVal) { this.vShowCost = pVal; }; + this.setShowAddEntries = function (pVal) { this.vShowAddEntries = pVal; }; + this.setShowTaskInfoRes = function (pVal) { this.vShowTaskInfoRes = pVal; }; + this.setShowTaskInfoDur = function (pVal) { this.vShowTaskInfoDur = pVal; }; + this.setShowTaskInfoComp = function (pVal) { this.vShowTaskInfoComp = pVal; }; + this.setShowTaskInfoStartDate = function (pVal) { this.vShowTaskInfoStartDate = pVal; }; + this.setShowTaskInfoEndDate = function (pVal) { this.vShowTaskInfoEndDate = pVal; }; + this.setShowTaskInfoNotes = function (pVal) { this.vShowTaskInfoNotes = pVal; }; + this.setShowTaskInfoLink = function (pVal) { this.vShowTaskInfoLink = pVal; }; + this.setShowEndWeekDate = function (pVal) { this.vShowEndWeekDate = pVal; }; + this.setShowWeekends = function (pVal) { this.vShowWeekends = pVal; }; + this.setShowSelector = function () { + var vValidSelectors = 'top bottom'; + this.vShowSelector = new Array(); + for (var i = 0, j = 0; i < arguments.length; i++) { + if (vValidSelectors.indexOf(arguments[i].toLowerCase()) != -1 && arguments[i].length > 1) { + this.vShowSelector[j++] = arguments[i].toLowerCase(); + var vRegExp = new RegExp('(?:^|\s)' + arguments[i] + '(?!\S)', 'g'); + vValidSelectors = vValidSelectors.replace(vRegExp, ''); + } + } + }; + this.setShowDeps = function (pVal) { this.vShowDeps = pVal; }; + this.setDateInputFormat = function (pVal) { this.vDateInputFormat = pVal; }; + this.setDateTaskTableDisplayFormat = function (pVal) { this.vDateTaskTableDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setDateTaskDisplayFormat = function (pVal) { this.vDateTaskDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setHourMajorDateDisplayFormat = function (pVal) { this.vHourMajorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setHourMinorDateDisplayFormat = function (pVal) { this.vHourMinorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setDayMajorDateDisplayFormat = function (pVal) { this.vDayMajorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setDayMinorDateDisplayFormat = function (pVal) { this.vDayMinorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setWeekMajorDateDisplayFormat = function (pVal) { this.vWeekMajorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setWeekMinorDateDisplayFormat = function (pVal) { this.vWeekMinorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setMonthMajorDateDisplayFormat = function (pVal) { this.vMonthMajorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setMonthMinorDateDisplayFormat = function (pVal) { this.vMonthMinorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setQuarterMajorDateDisplayFormat = function (pVal) { this.vQuarterMajorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setQuarterMinorDateDisplayFormat = function (pVal) { this.vQuarterMinorDateDisplayFormat = date_utils_1.parseDateFormatStr(pVal); }; + this.setCaptionType = function (pType) { this.vCaptionType = pType; }; + this.setFormat = function (pFormat) { + this.vFormat = pFormat; + this.Draw(); + }; + this.setWorkingDays = function (workingDays) { this.vWorkingDays = workingDays; }; + this.setMinGpLen = function (pMinGpLen) { this.vMinGpLen = pMinGpLen; }; + this.setScrollTo = function (pDate) { this.vScrollTo = pDate; }; + this.setHourColWidth = function (pWidth) { this.vHourColWidth = pWidth; }; + this.setDayColWidth = function (pWidth) { this.vDayColWidth = pWidth; }; + this.setWeekColWidth = function (pWidth) { this.vWeekColWidth = pWidth; }; + this.setMonthColWidth = function (pWidth) { this.vMonthColWidth = pWidth; }; + this.setQuarterColWidth = function (pWidth) { this.vQuarterColWidth = pWidth; }; + this.setRowHeight = function (pHeight) { this.vRowHeight = pHeight; }; + this.setLang = function (pLang) { if (this.vLangs[pLang]) + this.vLang = pLang; }; + this.setChartBody = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) + this.vChartBody = pDiv; }; + this.setChartHead = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) + this.vChartHead = pDiv; }; + this.setListBody = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) + this.vListBody = pDiv; }; + this.setChartTable = function (pTable) { if (typeof HTMLTableElement !== 'function' || pTable instanceof HTMLTableElement) + this.vChartTable = pTable; }; + this.setLines = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) + this.vLines = pDiv; }; + this.setLineOptions = function (lineOptions) { this.vLineOptions = lineOptions; }; + this.setTimer = function (pVal) { this.vTimer = pVal * 1; }; + this.setTooltipDelay = function (pVal) { this.vTooltipDelay = pVal * 1; }; + this.setTooltipTemplate = function (pVal) { this.vTooltipTemplate = pVal; }; + this.setMinDate = function (pVal) { this.vMinDate = pVal; }; + this.setMaxDate = function (pVal) { this.vMaxDate = pVal; }; + this.addLang = function (pLang, pVals) { + if (!this.vLangs[pLang]) { + this.vLangs[pLang] = new Object(); + for (var vKey in this.vLangs['en']) + this.vLangs[pLang][vKey] = (pVals[vKey]) ? document.createTextNode(pVals[vKey]).data : this.vLangs['en'][vKey]; + } + }; + this.setTotalHeight = function (pVal) { this.vTotalHeight = pVal; }; + // EVENTS + this.setEvents = function (pEvents) { this.vEvents = pEvents; }; + this.setEventsChange = function (pEventsChange) { this.vEventsChange = pEventsChange; }; + this.setEventClickRow = function (fn) { this.vEventClickRow = fn; }; + this.setEventClickCollapse = function (fn) { this.vEventClickCollapse = fn; }; + this.setResources = function (resources) { this.vResources = resources; }; + this.setAdditionalHeaders = function (headers) { this.vAdditionalHeaders = headers; }; + this.setColumnOrder = function (order) { this.vColumnOrder = order; }; + this.setEditable = function (editable) { this.vEditable = editable; }; + this.setDebug = function (debug) { this.vDebug = debug; }; + /** + * GETTERS + */ + this.getDivId = function () { return this.vDivId; }; + this.getUseFade = function () { return this.vUseFade; }; + this.getUseMove = function () { return this.vUseMove; }; + this.getUseRowHlt = function () { return this.vUseRowHlt; }; + this.getUseToolTip = function () { return this.vUseToolTip; }; + this.getUseSort = function () { return this.vUseSort; }; + this.getUseSingleCell = function () { return this.vUseSingleCell; }; + this.getFormatArr = function () { return this.vFormatArr; }; + this.getShowRes = function () { return this.vShowRes; }; + this.getShowDur = function () { return this.vShowDur; }; + this.getShowComp = function () { return this.vShowComp; }; + this.getShowStartDate = function () { return this.vShowStartDate; }; + this.getShowEndDate = function () { return this.vShowEndDate; }; + this.getShowPlanStartDate = function () { return this.vShowPlanStartDate; }; + this.getShowPlanEndDate = function () { return this.vShowPlanEndDate; }; + this.getShowCost = function () { return this.vShowCost; }; + this.getShowAddEntries = function () { return this.vShowAddEntries; }; + this.getShowTaskInfoRes = function () { return this.vShowTaskInfoRes; }; + this.getShowTaskInfoDur = function () { return this.vShowTaskInfoDur; }; + this.getShowTaskInfoComp = function () { return this.vShowTaskInfoComp; }; + this.getShowTaskInfoStartDate = function () { return this.vShowTaskInfoStartDate; }; + this.getShowTaskInfoEndDate = function () { return this.vShowTaskInfoEndDate; }; + this.getShowTaskInfoNotes = function () { return this.vShowTaskInfoNotes; }; + this.getShowTaskInfoLink = function () { return this.vShowTaskInfoLink; }; + this.getShowEndWeekDate = function () { return this.vShowEndWeekDate; }; + this.getShowWeekends = function () { return this.vShowWeekends; }; + this.getShowSelector = function () { return this.vShowSelector; }; + this.getShowDeps = function () { return this.vShowDeps; }; + this.getDateInputFormat = function () { return this.vDateInputFormat; }; + this.getDateTaskTableDisplayFormat = function () { return this.vDateTaskTableDisplayFormat; }; + this.getDateTaskDisplayFormat = function () { return this.vDateTaskDisplayFormat; }; + this.getHourMajorDateDisplayFormat = function () { return this.vHourMajorDateDisplayFormat; }; + this.getHourMinorDateDisplayFormat = function () { return this.vHourMinorDateDisplayFormat; }; + this.getDayMajorDateDisplayFormat = function () { return this.vDayMajorDateDisplayFormat; }; + this.getDayMinorDateDisplayFormat = function () { return this.vDayMinorDateDisplayFormat; }; + this.getWeekMajorDateDisplayFormat = function () { return this.vWeekMajorDateDisplayFormat; }; + this.getWeekMinorDateDisplayFormat = function () { return this.vWeekMinorDateDisplayFormat; }; + this.getMonthMajorDateDisplayFormat = function () { return this.vMonthMajorDateDisplayFormat; }; + this.getMonthMinorDateDisplayFormat = function () { return this.vMonthMinorDateDisplayFormat; }; + this.getQuarterMajorDateDisplayFormat = function () { return this.vQuarterMajorDateDisplayFormat; }; + this.getQuarterMinorDateDisplayFormat = function () { return this.vQuarterMinorDateDisplayFormat; }; + this.getCaptionType = function () { return this.vCaptionType; }; + this.getMinGpLen = function () { return this.vMinGpLen; }; + this.getScrollTo = function () { return this.vScrollTo; }; + this.getHourColWidth = function () { return this.vHourColWidth; }; + this.getDayColWidth = function () { return this.vDayColWidth; }; + this.getWeekColWidth = function () { return this.vWeekColWidth; }; + this.getMonthColWidth = function () { return this.vMonthColWidth; }; + this.getQuarterColWidth = function () { return this.vQuarterColWidth; }; + this.getRowHeight = function () { return this.vRowHeight; }; + this.getChartBody = function () { return this.vChartBody; }; + this.getChartHead = function () { return this.vChartHead; }; + this.getListBody = function () { return this.vListBody; }; + this.getChartTable = function () { return this.vChartTable; }; + this.getLines = function () { return this.vLines; }; + this.getTimer = function () { return this.vTimer; }; + this.getMinDate = function () { return this.vMinDate; }; + this.getMaxDate = function () { return this.vMaxDate; }; + this.getTooltipDelay = function () { return this.vTooltipDelay; }; + this.getList = function () { return this.vTaskList; }; + //EVENTS + this.getEventsClickCell = function () { return this.vEvents; }; + this.getEventsChange = function () { return this.vEventsChange; }; + this.getEventClickRow = function () { return this.vEventClickRow; }; + this.getEventClickCollapse = function () { return this.vEventClickCollapse; }; + this.getResources = function () { return this.vResources; }; + this.getAdditionalHeaders = function () { return this.vAdditionalHeaders; }; + this.getColumnOrder = function () { return this.vColumnOrder || draw_columns_1.COLUMN_ORDER; }; +}; + +},{"./draw_columns":3,"./utils/date_utils":11}],10:[function(require,module,exports){ +"use strict"; +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +var general_utils_1 = require("./utils/general_utils"); +var draw_utils_1 = require("./utils/draw_utils"); +var date_utils_1 = require("./utils/date_utils"); // function to open window to display task link -JSGantt.taskLink=function(pRef,pWidth,pHeight) -{ - - if(pWidth) var vWidth =pWidth; else vWidth =400; - if(pHeight) var vHeight=pHeight; else vHeight=400; - - var OpenWindow=window.open(pRef, 'newwin', 'height='+vHeight+',width='+vWidth); +exports.taskLink = function (pRef, pWidth, pHeight) { + var vWidth, vHeight; + if (pWidth) + vWidth = pWidth; + else + vWidth = 400; + if (pHeight) + vHeight = pHeight; + else + vHeight = 400; + window.open(pRef, 'newwin', 'height=' + vHeight + ',width=' + vWidth); // let OpenWindow = +}; +exports.sortTasks = function (pList, pID, pIdx) { + if (pList.length < 2) { + return pIdx; + } + var sortIdx = pIdx; + var sortArr = new Array(); + for (var i = 0; i < pList.length; i++) { + if (pList[i].getParent() == pID) + sortArr.push(pList[i]); + } + if (sortArr.length > 0) { + sortArr.sort(function (a, b) { + var i = a.getStart().getTime() - b.getStart().getTime(); + if (i == 0) + i = a.getEnd().getTime() - b.getEnd().getTime(); + if (i == 0) + return a.getID() - b.getID(); + else + return i; + }); + } + for (var j = 0; j < sortArr.length; j++) { + for (var i = 0; i < pList.length; i++) { + if (pList[i].getID() == sortArr[j].getID()) { + pList[i].setSortIdx(sortIdx++); + sortIdx = exports.sortTasks(pList, pList[i].getID(), sortIdx); + } + } + } + return sortIdx; +}; +exports.TaskItemObject = function (object) { + var pDataObject = __assign({}, object); + general_utils_1.internalProperties.forEach(function (property) { + delete pDataObject[property]; + }); + return new exports.TaskItem(object.pID, object.pName, object.pStart, object.pEnd, object.pClass, object.pLink, object.pMile, object.pRes, object.pComp, object.pGroup, object.pParent, object.pOpen, object.pDepend, object.pCaption, object.pNotes, object.pGantt, object.pCost, object.pPlanStart, object.pPlanEnd, object.pDuration, object.pBarText, object); +}; +exports.TaskItem = function (pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGantt, pCost, pPlanStart, pPlanEnd, pDuration, pBarText, pDataObject) { + if (pCost === void 0) { pCost = null; } + if (pPlanStart === void 0) { pPlanStart = null; } + if (pPlanEnd === void 0) { pPlanEnd = null; } + if (pDuration === void 0) { pDuration = null; } + if (pBarText === void 0) { pBarText = null; } + if (pDataObject === void 0) { pDataObject = null; } + var vGantt = pGantt ? pGantt : this; + var _id = document.createTextNode(pID).data; + var vID = general_utils_1.hashKey(document.createTextNode(pID).data); + var vName = document.createTextNode(pName).data; + var vStart = null; + var vEnd = null; + var vPlanStart = null; + var vPlanEnd = null; + var vGroupMinStart = null; + var vGroupMinEnd = null; + var vGroupMinPlanStart = null; + var vGroupMinPlanEnd = null; + var vClass = document.createTextNode(pClass).data; + var vLink = document.createTextNode(pLink).data; + var vMile = parseInt(document.createTextNode(pMile).data); + var vRes = document.createTextNode(pRes).data; + var vComp = parseFloat(document.createTextNode(pComp).data); + var vCost = parseInt(document.createTextNode(pCost).data); + var vGroup = parseInt(document.createTextNode(pGroup).data); + var vDataObject = pDataObject; + var vCompVal; + var parent = document.createTextNode(pParent).data; + if (parent && parent !== '0') { + parent = general_utils_1.hashKey(parent).toString(); + } + var vParent = parent; + var vOpen = (vGroup == 2) ? 1 : parseInt(document.createTextNode(pOpen).data); + var vDepend = new Array(); + var vDependType = new Array(); + var vCaption = document.createTextNode(pCaption).data; + var vDuration = pDuration || ''; + var vBarText = pBarText || ''; + var vLevel = 0; + var vNumKid = 0; + var vWeight = 0; + var vVisible = 1; + var vSortIdx = 0; + var vToDelete = false; + var x1, y1, x2, y2; + var vNotes; + var vParItem = null; + var vCellDiv = null; + var vBarDiv = null; + var vTaskDiv = null; + var vPlanTaskDiv = null; + var vListChildRow = null; + var vChildRow = null; + var vGroupSpan = null; + vNotes = document.createElement('span'); + vNotes.className = 'gTaskNotes'; + if (pNotes != null) { + vNotes.innerHTML = pNotes; + general_utils_1.stripUnwanted(vNotes); + } + if (pStart != null && pStart != '') { + vStart = (pStart instanceof Date) ? pStart : date_utils_1.parseDateStr(document.createTextNode(pStart).data, vGantt.getDateInputFormat()); + vGroupMinStart = vStart; + } + if (pEnd != null && pEnd != '') { + vEnd = (pEnd instanceof Date) ? pEnd : date_utils_1.parseDateStr(document.createTextNode(pEnd).data, vGantt.getDateInputFormat()); + vGroupMinEnd = vEnd; + } + if (pPlanStart != null && pPlanStart != '') { + vPlanStart = (pPlanStart instanceof Date) ? pPlanStart : date_utils_1.parseDateStr(document.createTextNode(pPlanStart).data, vGantt.getDateInputFormat()); + vGroupMinPlanStart = vPlanStart; + } + if (pPlanEnd != null && pPlanEnd != '') { + vPlanEnd = (pPlanEnd instanceof Date) ? pPlanEnd : date_utils_1.parseDateStr(document.createTextNode(pPlanEnd).data, vGantt.getDateInputFormat()); + vGroupMinPlanEnd = vPlanEnd; + } + if (pDepend != null) { + var vDependStr = pDepend + ''; + var vDepList = vDependStr.split(','); + var n = vDepList.length; + for (var k = 0; k < n; k++) { + if (vDepList[k].toUpperCase().endsWith('SS')) { + vDepend[k] = vDepList[k].substring(0, vDepList[k].length - 2); + vDependType[k] = 'SS'; + } + else if (vDepList[k].toUpperCase().endsWith('FF')) { + vDepend[k] = vDepList[k].substring(0, vDepList[k].length - 2); + vDependType[k] = 'FF'; + } + else if (vDepList[k].toUpperCase().endsWith('SF')) { + vDepend[k] = vDepList[k].substring(0, vDepList[k].length - 2); + vDependType[k] = 'SF'; + } + else if (vDepList[k].toUpperCase().endsWith('FS')) { + vDepend[k] = vDepList[k].substring(0, vDepList[k].length - 2); + vDependType[k] = 'FS'; + } + else { + vDepend[k] = vDepList[k]; + vDependType[k] = 'FS'; + } + if (vDepend[k]) { + vDepend[k] = general_utils_1.hashKey(vDepend[k]).toString(); + } + } + } + this.getID = function () { return vID; }; + this.getOriginalID = function () { return _id; }; + this.getGantt = function () { return vGantt; }; + this.getName = function () { return vName; }; + this.getStart = function () { + if (vStart) + return vStart; + else if (vPlanStart) + return vPlanStart; + else + return new Date(); + }; + this.getStartVar = function () { + return vStart; + }; + this.getEnd = function () { + if (vEnd) + return vEnd; + else if (vPlanEnd) + return vPlanEnd; + else if (vStart && vDuration) { + var date = new Date(vStart); + var vUnits = vDuration.split(' '); + var value = parseInt(vUnits[0]); + switch (vUnits[1]) { + case 'hour': + date.setMinutes(date.getMinutes() + (value * 60)); + break; + case 'day': + date.setMinutes(date.getMinutes() + (value * 60 * 24)); + break; + case 'week': + date.setMinutes(date.getMinutes() + (value * 60 * 24 * 7)); + break; + case 'month': + date.setMonth(date.getMonth() + (value)); + break; + case 'quarter': + date.setMonth(date.getMonth() + (value * 3)); + break; + } + return date; + } + else + return new Date(); + }; + this.getEndVar = function () { + return vEnd; + }; + this.getPlanStart = function () { return vPlanStart ? vPlanStart : vStart; }; + this.getPlanEnd = function () { return vPlanEnd ? vPlanEnd : vEnd; }; + this.getCost = function () { return vCost; }; + this.getGroupMinStart = function () { return vGroupMinStart; }; + this.getGroupMinEnd = function () { return vGroupMinEnd; }; + this.getGroupMinPlanStart = function () { return vGroupMinPlanStart; }; + this.getGroupMinPlanEnd = function () { return vGroupMinPlanEnd; }; + this.getClass = function () { return vClass; }; + this.getLink = function () { return vLink; }; + this.getMile = function () { return vMile; }; + this.getDepend = function () { + if (vDepend) + return vDepend; + else + return null; + }; + this.getDataObject = function () { return vDataObject; }; + this.getDepType = function () { if (vDependType) + return vDependType; + else + return null; }; + this.getCaption = function () { if (vCaption) + return vCaption; + else + return ''; }; + this.getResource = function () { if (vRes) + return vRes; + else + return '\u00A0'; }; + this.getCompVal = function () { if (vComp) + return vComp; + else if (vCompVal) + return vCompVal; + else + return 0; }; + this.getCompStr = function () { if (vComp) + return vComp + '%'; + else if (vCompVal) + return vCompVal + '%'; + else + return ''; }; + this.getCompRestStr = function () { if (vComp) + return (100 - vComp) + '%'; + else if (vCompVal) + return (100 - vCompVal) + '%'; + else + return ''; }; + this.getNotes = function () { return vNotes; }; + this.getSortIdx = function () { return vSortIdx; }; + this.getToDelete = function () { return vToDelete; }; + this.getDuration = function (pFormat, pLang) { + if (vMile) { + vDuration = '-'; + } + else if (!vEnd && !vStart && vPlanStart && vPlanEnd) { + return calculateVDuration(pFormat, pLang, this.getPlanStart(), this.getPlanEnd()); + } + else if (!vEnd && vDuration) { + return vDuration; + } + else { + vDuration = calculateVDuration(pFormat, pLang, this.getStart(), this.getEnd()); + } + return vDuration; + }; + function calculateVDuration(pFormat, pLang, start, end) { + var vDuration; + var vUnits = null; + switch (pFormat) { + case 'week': + vUnits = 'day'; + break; + case 'month': + vUnits = 'week'; + break; + case 'quarter': + vUnits = 'month'; + break; + default: + vUnits = pFormat; + break; + } + // let vTaskEnd = new Date(this.getEnd().getTime()); + // if ((vTaskEnd.getTime() - (vTaskEnd.getTimezoneOffset() * 60000)) % (86400000) == 0) { + // vTaskEnd = new Date(vTaskEnd.getFullYear(), vTaskEnd.getMonth(), vTaskEnd.getDate() + 1, vTaskEnd.getHours(), vTaskEnd.getMinutes(), vTaskEnd.getSeconds()); + // } + // let tmpPer = (getOffset(this.getStart(), vTaskEnd, 999, vUnits)) / 1000; + var hours = (end.getTime() - start.getTime()) / 1000 / 60 / 60; + var tmpPer; + switch (vUnits) { + case 'hour': + tmpPer = Math.round(hours); + vDuration = tmpPer + ' ' + ((tmpPer != 1) ? pLang['hrs'] : pLang['hr']); + break; + case 'day': + tmpPer = Math.round(hours / 24); + vDuration = tmpPer + ' ' + ((tmpPer != 1) ? pLang['dys'] : pLang['dy']); + break; + case 'week': + tmpPer = Math.round(hours / 24 / 7); + vDuration = tmpPer + ' ' + ((tmpPer != 1) ? pLang['wks'] : pLang['wk']); + break; + case 'month': + tmpPer = Math.round(hours / 24 / 7 / 30); + vDuration = tmpPer + ' ' + ((tmpPer != 1) ? pLang['mths'] : pLang['mth']); + break; + case 'quarter': + tmpPer = Math.round(hours / 24 / 7 / 30 / 3); + vDuration = tmpPer + ' ' + ((tmpPer != 1) ? pLang['qtrs'] : pLang['qtr']); + break; + } + return vDuration; + } + this.getBarText = function () { return vBarText; }; + this.getParent = function () { return vParent; }; + this.getGroup = function () { return vGroup; }; + this.getOpen = function () { return vOpen; }; + this.getLevel = function () { return vLevel; }; + this.getNumKids = function () { return vNumKid; }; + this.getWeight = function () { return vWeight; }; + this.getStartX = function () { return x1; }; + this.getStartY = function () { return y1; }; + this.getEndX = function () { return x2; }; + this.getEndY = function () { return y2; }; + this.getVisible = function () { return vVisible; }; + this.getParItem = function () { return vParItem; }; + this.getCellDiv = function () { return vCellDiv; }; + this.getBarDiv = function () { return vBarDiv; }; + this.getTaskDiv = function () { return vTaskDiv; }; + this.getPlanTaskDiv = function () { return vPlanTaskDiv; }; + this.getChildRow = function () { return vChildRow; }; + this.getListChildRow = function () { return vListChildRow; }; + this.getGroupSpan = function () { return vGroupSpan; }; + this.setName = function (pName) { vName = pName; }; + this.setNotes = function (pNotes) { vNotes = pNotes; }; + this.setClass = function (pClass) { vClass = pClass; }; + this.setCost = function (pCost) { vCost = pCost; }; + this.setResource = function (pRes) { vRes = pRes; }; + this.setDuration = function (pDuration) { vDuration = pDuration; }; + this.setDataObject = function (pDataObject) { vDataObject = pDataObject; }; + this.setStart = function (pStart) { + if (pStart instanceof Date) { + vStart = pStart; + } + else { + var temp = new Date(pStart); + if (temp instanceof Date && !isNaN(temp.valueOf())) { + vStart = temp; + } + } + }; + this.setEnd = function (pEnd) { + if (pEnd instanceof Date) { + vEnd = pEnd; + } + else { + var temp = new Date(pEnd); + if (temp instanceof Date && !isNaN(temp.valueOf())) { + vEnd = temp; + } + } + }; + this.setPlanStart = function (pPlanStart) { + if (pPlanStart instanceof Date) + vPlanStart = pPlanStart; + else + vPlanStart = new Date(pPlanStart); + }; + this.setPlanEnd = function (pPlanEnd) { + if (pPlanEnd instanceof Date) + vPlanEnd = pPlanEnd; + else + vPlanEnd = new Date(pPlanEnd); + }; + this.setGroupMinStart = function (pStart) { if (pStart instanceof Date) + vGroupMinStart = pStart; }; + this.setGroupMinEnd = function (pEnd) { if (pEnd instanceof Date) + vGroupMinEnd = pEnd; }; + this.setLevel = function (pLevel) { vLevel = parseInt(document.createTextNode(pLevel).data); }; + this.setNumKid = function (pNumKid) { vNumKid = parseInt(document.createTextNode(pNumKid).data); }; + this.setWeight = function (pWeight) { vWeight = parseInt(document.createTextNode(pWeight).data); }; + this.setCompVal = function (pCompVal) { vCompVal = parseFloat(document.createTextNode(pCompVal).data); }; + this.setComp = function (pComp) { + vComp = parseInt(document.createTextNode(pComp).data); + }; + this.setStartX = function (pX) { x1 = parseInt(document.createTextNode(pX).data); }; + this.setStartY = function (pY) { y1 = parseInt(document.createTextNode(pY).data); }; + this.setEndX = function (pX) { x2 = parseInt(document.createTextNode(pX).data); }; + this.setEndY = function (pY) { y2 = parseInt(document.createTextNode(pY).data); }; + this.setOpen = function (pOpen) { vOpen = parseInt(document.createTextNode(pOpen).data); }; + this.setVisible = function (pVisible) { vVisible = parseInt(document.createTextNode(pVisible).data); }; + this.setSortIdx = function (pSortIdx) { vSortIdx = parseInt(document.createTextNode(pSortIdx).data); }; + this.setToDelete = function (pToDelete) { if (pToDelete) + vToDelete = true; + else + vToDelete = false; }; + this.setParItem = function (pParItem) { if (pParItem) + vParItem = pParItem; }; + this.setCellDiv = function (pCellDiv) { if (typeof HTMLDivElement !== 'function' || pCellDiv instanceof HTMLDivElement) + vCellDiv = pCellDiv; }; //"typeof HTMLDivElement !== 'function'" to play nice with ie6 and 7 + this.setGroup = function (pGroup) { + if (pGroup === true || pGroup === 'true') { + vGroup = 1; + } + else if (pGroup === false || pGroup === 'false') { + vGroup = 0; + } + else { + vGroup = parseInt(document.createTextNode(pGroup).data); + } + }; + this.setBarText = function (pBarText) { if (pBarText) + vBarText = pBarText; }; + this.setBarDiv = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) + vBarDiv = pDiv; }; + this.setTaskDiv = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) + vTaskDiv = pDiv; }; + this.setPlanTaskDiv = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) + vPlanTaskDiv = pDiv; }; + this.setChildRow = function (pRow) { if (typeof HTMLTableRowElement !== 'function' || pRow instanceof HTMLTableRowElement) + vChildRow = pRow; }; + this.setListChildRow = function (pRow) { if (typeof HTMLTableRowElement !== 'function' || pRow instanceof HTMLTableRowElement) + vListChildRow = pRow; }; + this.setGroupSpan = function (pSpan) { if (typeof HTMLSpanElement !== 'function' || pSpan instanceof HTMLSpanElement) + vGroupSpan = pSpan; }; + this.getAllData = function () { + return { + pID: vID, + pName: vName, + pStart: vStart, + pEnd: vEnd, + pPlanStart: vPlanStart, + pPlanEnd: vPlanEnd, + pGroupMinStart: vGroupMinStart, + pGroupMinEnd: vGroupMinEnd, + pClass: vClass, + pLink: vLink, + pMile: vMile, + pRes: vRes, + pComp: vComp, + pCost: vCost, + pGroup: vGroup, + pDataObjec: vDataObject + }; + }; +}; +/** + * @param pTask + * @param templateStrOrFn template string or function(task). In any case parameters in template string are substituted. + * If string - just a static template. + * If function(task): string - per task template. Can return null|undefined to fallback to default template. + * If function(task): Promise) - async per task template. Tooltip will show 'Loading...' if promise is not yet complete. + * Otherwise returned template will be handled in the same manner as in other cases. + */ +exports.createTaskInfo = function (pTask, templateStrOrFn) { + var _this = this; + if (templateStrOrFn === void 0) { templateStrOrFn = null; } + var vTmpDiv; + var vTaskInfoBox = document.createDocumentFragment(); + var vTaskInfo = draw_utils_1.newNode(vTaskInfoBox, 'div', null, 'gTaskInfo'); + var setupTemplate = function (template) { + vTaskInfo.innerHTML = ""; + if (template) { + var allData_1 = pTask.getAllData(); + general_utils_1.internalProperties.forEach(function (key) { + var lang; + if (general_utils_1.internalPropertiesLang[key]) { + lang = _this.vLangs[_this.vLang][general_utils_1.internalPropertiesLang[key]]; + } + if (!lang) { + lang = key; + } + var val = allData_1[key]; + template = template.replace("{{" + key + "}}", val); + if (lang) { + template = template.replace("{{Lang:" + key + "}}", lang); + } + else { + template = template.replace("{{Lang:" + key + "}}", key); + } + }); + draw_utils_1.newNode(vTaskInfo, 'span', null, 'gTtTemplate', template); + } + else { + draw_utils_1.newNode(vTaskInfo, 'span', null, 'gTtTitle', pTask.getName()); + if (_this.vShowTaskInfoStartDate == 1) { + vTmpDiv = draw_utils_1.newNode(vTaskInfo, 'div', null, 'gTILine gTIsd'); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskLabel', _this.vLangs[_this.vLang]['startdate'] + ': '); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskText', date_utils_1.formatDateStr(pTask.getStart(), _this.vDateTaskDisplayFormat, _this.vLangs[_this.vLang])); + } + if (_this.vShowTaskInfoEndDate == 1) { + vTmpDiv = draw_utils_1.newNode(vTaskInfo, 'div', null, 'gTILine gTIed'); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskLabel', _this.vLangs[_this.vLang]['enddate'] + ': '); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskText', date_utils_1.formatDateStr(pTask.getEnd(), _this.vDateTaskDisplayFormat, _this.vLangs[_this.vLang])); + } + if (_this.vShowTaskInfoDur == 1 && !pTask.getMile()) { + vTmpDiv = draw_utils_1.newNode(vTaskInfo, 'div', null, 'gTILine gTId'); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskLabel', _this.vLangs[_this.vLang]['duration'] + ': '); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskText', pTask.getDuration(_this.vFormat, _this.vLangs[_this.vLang])); + } + if (_this.vShowTaskInfoComp == 1) { + vTmpDiv = draw_utils_1.newNode(vTaskInfo, 'div', null, 'gTILine gTIc'); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskLabel', _this.vLangs[_this.vLang]['completion'] + ': '); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskText', pTask.getCompStr()); + } + if (_this.vShowTaskInfoRes == 1) { + vTmpDiv = draw_utils_1.newNode(vTaskInfo, 'div', null, 'gTILine gTIr'); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskLabel', _this.vLangs[_this.vLang]['resource'] + ': '); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskText', pTask.getResource()); + } + if (_this.vShowTaskInfoLink == 1 && pTask.getLink() != '') { + vTmpDiv = draw_utils_1.newNode(vTaskInfo, 'div', null, 'gTILine gTIl'); + var vTmpNode = draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskLabel'); + vTmpNode = draw_utils_1.newNode(vTmpNode, 'a', null, 'gTaskText', _this.vLangs[_this.vLang]['moreinfo']); + vTmpNode.setAttribute('href', pTask.getLink()); + } + if (_this.vShowTaskInfoNotes == 1) { + vTmpDiv = draw_utils_1.newNode(vTaskInfo, 'div', null, 'gTILine gTIn'); + draw_utils_1.newNode(vTmpDiv, 'span', null, 'gTaskLabel', _this.vLangs[_this.vLang]['notes'] + ': '); + if (pTask.getNotes()) + vTmpDiv.appendChild(pTask.getNotes()); + } + } + }; + var callback; + if (typeof templateStrOrFn === 'function') { + callback = function () { + var strOrPromise = templateStrOrFn(pTask); + if (!strOrPromise || typeof strOrPromise === 'string') { + setupTemplate(strOrPromise); + } + else if (strOrPromise.then) { + setupTemplate(_this.vLangs[_this.vLang]['tooltipLoading'] || _this.vLangs['en']['tooltipLoading']); + return strOrPromise.then(setupTemplate); + } + }; + } + else { + setupTemplate(templateStrOrFn); + } + return { component: vTaskInfoBox, callback: callback }; +}; +exports.AddTaskItem = function (value) { + var vExists = false; + for (var i = 0; i < this.vTaskList.length; i++) { + if (this.vTaskList[i].getID() == value.getID()) { + i = this.vTaskList.length; + vExists = true; + } + } + if (!vExists) { + this.vTaskList.push(value); + this.vProcessNeeded = true; + } +}; +exports.AddTaskItemObject = function (object) { + if (!object.pGantt) { + object.pGantt = this; + } + return this.AddTaskItem(exports.TaskItemObject(object)); +}; +exports.RemoveTaskItem = function (pID) { + // simply mark the task for removal at this point - actually remove it next time we re-draw the chart + for (var i = 0; i < this.vTaskList.length; i++) { + if (this.vTaskList[i].getID() == pID) + this.vTaskList[i].setToDelete(true); + else if (this.vTaskList[i].getParent() == pID) + this.RemoveTaskItem(this.vTaskList[i].getID()); + } + this.vProcessNeeded = true; +}; +exports.ClearTasks = function () { + var _this = this; + this.vTaskList.map(function (task) { return _this.RemoveTaskItem(task.getID()); }); + this.vProcessNeeded = true; +}; +// Recursively process task tree ... set min, max dates of parent tasks and identfy task level. +exports.processRows = function (pList, pID, pRow, pLevel, pOpen, pUseSort, vDebug) { + if (vDebug === void 0) { vDebug = false; } + var vMinDate = null; + var vMaxDate = null; + var vMinPlanDate = null; + var vMaxPlanDate = null; + var vVisible = pOpen; + var vCurItem = null; + var vCompSum = 0; + var vMinSet = 0; + var vMaxSet = 0; + var vMinPlanSet = 0; + var vMaxPlanSet = 0; + var vNumKid = 0; + var vWeight = 0; + var vLevel = pLevel; + var vList = pList; + var vComb = false; + var i = 0; + for (i = 0; i < pList.length; i++) { + if (pList[i].getToDelete()) { + pList.splice(i, 1); + i--; + } + if (i >= 0 && pList[i].getID() == pID) + vCurItem = pList[i]; + } + for (i = 0; i < pList.length; i++) { + if (pList[i].getParent() == pID) { + vVisible = pOpen; + pList[i].setParItem(vCurItem); + pList[i].setVisible(vVisible); + if (vVisible == 1 && pList[i].getOpen() == 0) + vVisible = 0; + if (pList[i].getMile() && pList[i].getParItem() && pList[i].getParItem().getGroup() == 2) { //remove milestones owned by combined groups + pList.splice(i, 1); + i--; + continue; + } + pList[i].setLevel(vLevel); + if (pList[i].getGroup()) { + if (pList[i].getParItem() && pList[i].getParItem().getGroup() == 2) + pList[i].setGroup(2); + exports.processRows(vList, pList[i].getID(), i, vLevel + 1, vVisible, 0); + } + if (pList[i].getStartVar() && (vMinSet == 0 || pList[i].getStartVar() < vMinDate)) { + vMinDate = pList[i].getStartVar(); + vMinSet = 1; + } + if (pList[i].getEndVar() && (vMaxSet == 0 || pList[i].getEndVar() > vMaxDate)) { + vMaxDate = pList[i].getEndVar(); + vMaxSet = 1; + } + if (vMinPlanSet == 0 || pList[i].getPlanStart() < vMinPlanDate) { + vMinPlanDate = pList[i].getPlanStart(); + vMinPlanSet = 1; + } + if (vMaxPlanSet == 0 || pList[i].getPlanEnd() > vMaxPlanDate) { + vMaxPlanDate = pList[i].getPlanEnd(); + vMaxPlanSet = 1; + } + vNumKid++; + vWeight += pList[i].getEnd() - pList[i].getStart() + 1; + vCompSum += pList[i].getCompVal() * (pList[i].getEnd() - pList[i].getStart() + 1); + pList[i].setSortIdx(i * pList.length); + } + } + if (pRow >= 0) { + if (pList[pRow].getGroupMinStart() != null && pList[pRow].getGroupMinStart() < vMinDate) { + vMinDate = pList[pRow].getGroupMinStart(); + } + if (pList[pRow].getGroupMinEnd() != null && pList[pRow].getGroupMinEnd() > vMaxDate) { + vMaxDate = pList[pRow].getGroupMinEnd(); + } + if (vMinDate) { + pList[pRow].setStart(vMinDate); + } + if (vMaxDate) { + pList[pRow].setEnd(vMaxDate); + } + if (pList[pRow].getGroupMinPlanStart() != null && pList[pRow].getGroupMinPlanStart() < vMinPlanDate) { + vMinPlanDate = pList[pRow].getGroupMinPlanStart(); + } + if (pList[pRow].getGroupMinPlanEnd() != null && pList[pRow].getGroupMinPlanEnd() > vMaxPlanDate) { + vMaxPlanDate = pList[pRow].getGroupMinPlanEnd(); + } + if (vMinPlanDate) { + pList[pRow].setPlanStart(vMinPlanDate); + } + if (vMaxPlanDate) { + pList[pRow].setPlanEnd(vMaxPlanDate); + } + pList[pRow].setNumKid(vNumKid); + pList[pRow].setWeight(vWeight); + pList[pRow].setCompVal(Math.ceil(vCompSum / vWeight)); + } + if (pID == 0 && pUseSort == 1) { + var bd = void 0; + if (vDebug) { + bd = new Date(); + console.info('before afterTasks', bd); + } + exports.sortTasks(pList, 0, 0); + if (vDebug) { + var ad = new Date(); + console.info('after afterTasks', ad, (ad.getTime() - bd.getTime())); + } + pList.sort(function (a, b) { return a.getSortIdx() - b.getSortIdx(); }); + } + if (pID == 0 && pUseSort != 1) // Need to sort combined tasks regardless + { + for (i = 0; i < pList.length; i++) { + if (pList[i].getGroup() == 2) { + vComb = true; + var bd = void 0; + if (vDebug) { + bd = new Date(); + console.info('before sortTasks', bd); + } + exports.sortTasks(pList, pList[i].getID(), pList[i].getSortIdx() + 1); + if (vDebug) { + var ad = new Date(); + console.info('after sortTasks', ad, (ad.getTime() - bd.getTime())); + } + } + } + if (vComb == true) + pList.sort(function (a, b) { return a.getSortIdx() - b.getSortIdx(); }); + } }; -JSGantt.parseDateStr=function(pDateStr,pFormatStr) -{ - var vDate=new Date(); - var vDateParts=pDateStr.split(/[^0-9]/); - if (pDateStr.length>=10 && vDateParts.length>=3) - { - while(vDateParts.length<5)vDateParts.push(0); - - switch(pFormatStr) - { - case 'mm/dd/yyyy': - vDate=new Date(vDateParts[2], vDateParts[0]-1, vDateParts[1], vDateParts[3], vDateParts[4]); - break; - case 'dd/mm/yyyy': - vDate=new Date(vDateParts[2], vDateParts[1]-1, vDateParts[0], vDateParts[3], vDateParts[4]); - break; - case 'yyyy-mm-dd': - vDate=new Date(vDateParts[0], vDateParts[1]-1, vDateParts[2], vDateParts[3], vDateParts[4]); - break; - } - } - return(vDate); +},{"./utils/date_utils":11,"./utils/draw_utils":12,"./utils/general_utils":13}],11:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * DATES + */ +exports.getMinDate = function (pList, pFormat, pMinDate) { + var vDate = new Date(); + if (pList.length <= 0) + return pMinDate || vDate; + vDate.setTime((pMinDate && pMinDate.getTime()) || pList[0].getStart().getTime()); + // Parse all Task Start dates to find min + for (var i = 0; i < pList.length; i++) { + if (pList[i].getStart().getTime() < vDate.getTime()) + vDate.setTime(pList[i].getStart().getTime()); + if (pList[i].getPlanStart() && pList[i].getPlanStart().getTime() < vDate.getTime()) + vDate.setTime(pList[i].getPlanStart().getTime()); + } + // Adjust min date to specific format boundaries (first of week or first of month) + if (pFormat == 'day') { + vDate.setDate(vDate.getDate() - 1); + while (vDate.getDay() % 7 != 1) + vDate.setDate(vDate.getDate() - 1); + } + else if (pFormat == 'week') { + vDate.setDate(vDate.getDate() - 1); + while (vDate.getDay() % 7 != 1) + vDate.setDate(vDate.getDate() - 1); + } + else if (pFormat == 'month') { + vDate.setDate(vDate.getDate() - 15); + while (vDate.getDate() > 1) + vDate.setDate(vDate.getDate() - 1); + } + else if (pFormat == 'quarter') { + vDate.setDate(vDate.getDate() - 31); + if (vDate.getMonth() == 0 || vDate.getMonth() == 1 || vDate.getMonth() == 2) + vDate.setFullYear(vDate.getFullYear(), 0, 1); + else if (vDate.getMonth() == 3 || vDate.getMonth() == 4 || vDate.getMonth() == 5) + vDate.setFullYear(vDate.getFullYear(), 3, 1); + else if (vDate.getMonth() == 6 || vDate.getMonth() == 7 || vDate.getMonth() == 8) + vDate.setFullYear(vDate.getFullYear(), 6, 1); + else if (vDate.getMonth() == 9 || vDate.getMonth() == 10 || vDate.getMonth() == 11) + vDate.setFullYear(vDate.getFullYear(), 9, 1); + } + else if (pFormat == 'hour') { + vDate.setHours(vDate.getHours() - 1); + while (vDate.getHours() % 6 != 0) + vDate.setHours(vDate.getHours() - 1); + } + if (pFormat == 'hour') + vDate.setMinutes(0, 0); + else + vDate.setHours(0, 0, 0); + return (vDate); +}; +exports.getMaxDate = function (pList, pFormat, pMaxDate) { + var vDate = new Date(); + if (pList.length <= 0) + return pMaxDate || vDate; + vDate.setTime((pMaxDate && pMaxDate.getTime()) || pList[0].getEnd().getTime()); + // Parse all Task End dates to find max + for (var i = 0; i < pList.length; i++) { + if (pList[i].getEnd().getTime() > vDate.getTime()) + vDate.setTime(pList[i].getEnd().getTime()); + if (pList[i].getPlanEnd() && pList[i].getPlanEnd().getTime() > vDate.getTime()) + vDate.setTime(pList[i].getPlanEnd().getTime()); + } + // Adjust max date to specific format boundaries (end of week or end of month) + if (pFormat == 'day') { + vDate.setDate(vDate.getDate() + 1); + while (vDate.getDay() % 7 != 0) + vDate.setDate(vDate.getDate() + 1); + } + else if (pFormat == 'week') { + //For weeks, what is the last logical boundary? + vDate.setDate(vDate.getDate() + 1); + while (vDate.getDay() % 7 != 0) + vDate.setDate(vDate.getDate() + 1); + } + else if (pFormat == 'month') { + // Set to last day of current Month + while (vDate.getDate() > 1) + vDate.setDate(vDate.getDate() + 1); + vDate.setDate(vDate.getDate() - 1); + } + else if (pFormat == 'quarter') { + // Set to last day of current Quarter + if (vDate.getMonth() == 0 || vDate.getMonth() == 1 || vDate.getMonth() == 2) + vDate.setFullYear(vDate.getFullYear(), 2, 31); + else if (vDate.getMonth() == 3 || vDate.getMonth() == 4 || vDate.getMonth() == 5) + vDate.setFullYear(vDate.getFullYear(), 5, 30); + else if (vDate.getMonth() == 6 || vDate.getMonth() == 7 || vDate.getMonth() == 8) + vDate.setFullYear(vDate.getFullYear(), 8, 30); + else if (vDate.getMonth() == 9 || vDate.getMonth() == 10 || vDate.getMonth() == 11) + vDate.setFullYear(vDate.getFullYear(), 11, 31); + } + else if (pFormat == 'hour') { + if (vDate.getHours() == 0) + vDate.setDate(vDate.getDate() + 1); + vDate.setHours(vDate.getHours() + 1); + while (vDate.getHours() % 6 != 5) + vDate.setHours(vDate.getHours() + 1); + } + return (vDate); +}; +exports.coerceDate = function (date) { + if (date instanceof Date) { + return date; + } + else { + var temp = new Date(date); + if (temp instanceof Date && !isNaN(temp.valueOf())) { + return temp; + } + } +}; +exports.parseDateStr = function (pDateStr, pFormatStr) { + var vDate = new Date(); + var vDateParts = pDateStr.split(/[^0-9]/); + if (pDateStr.length >= 10 && vDateParts.length >= 3) { + while (vDateParts.length < 5) + vDateParts.push(0); + switch (pFormatStr) { + case 'mm/dd/yyyy': + vDate = new Date(vDateParts[2], vDateParts[0] - 1, vDateParts[1], vDateParts[3], vDateParts[4]); + break; + case 'dd/mm/yyyy': + vDate = new Date(vDateParts[2], vDateParts[1] - 1, vDateParts[0], vDateParts[3], vDateParts[4]); + break; + case 'yyyy-mm-dd': + vDate = new Date(vDateParts[0], vDateParts[1] - 1, vDateParts[2], vDateParts[3], vDateParts[4]); + break; + case 'yyyy-mm-dd HH:MI:SS': + vDate = new Date(vDateParts[0], vDateParts[1] - 1, vDateParts[2], vDateParts[3], vDateParts[4], vDateParts[5]); + break; + } + } + return (vDate); +}; +exports.formatDateStr = function (pDate, pDateFormatArr, pL) { + // Fix on issue #303 - getXMLTask is passing null as pDates + if (!pDate) { + return; + } + var vDateStr = ''; + var vYear2Str = pDate.getFullYear().toString().substring(2, 4); + var vMonthStr = (pDate.getMonth() + 1) + ''; + var vMonthArr = new Array(pL['january'], pL['february'], pL['march'], pL['april'], pL['maylong'], pL['june'], pL['july'], pL['august'], pL['september'], pL['october'], pL['november'], pL['december']); + var vDayArr = new Array(pL['sunday'], pL['monday'], pL['tuesday'], pL['wednesday'], pL['thursday'], pL['friday'], pL['saturday']); + var vMthArr = new Array(pL['jan'], pL['feb'], pL['mar'], pL['apr'], pL['may'], pL['jun'], pL['jul'], pL['aug'], pL['sep'], pL['oct'], pL['nov'], pL['dec']); + var vDyArr = new Array(pL['sun'], pL['mon'], pL['tue'], pL['wed'], pL['thu'], pL['fri'], pL['sat']); + for (var i = 0; i < pDateFormatArr.length; i++) { + switch (pDateFormatArr[i]) { + case 'dd': + if (pDate.getDate() < 10) + vDateStr += '0'; // now fall through + case 'd': + vDateStr += pDate.getDate(); + break; + case 'day': + vDateStr += vDyArr[pDate.getDay()]; + break; + case 'DAY': + vDateStr += vDayArr[pDate.getDay()]; + break; + case 'mm': + if (parseInt(vMonthStr, 10) < 10) + vDateStr += '0'; // now fall through + case 'm': + vDateStr += vMonthStr; + break; + case 'mon': + vDateStr += vMthArr[pDate.getMonth()]; + break; + case 'month': + vDateStr += vMonthArr[pDate.getMonth()]; + break; + case 'yyyy': + vDateStr += pDate.getFullYear(); + break; + case 'yy': + vDateStr += vYear2Str; + break; + case 'qq': + vDateStr += pL['qtr']; // now fall through + case 'q': + vDateStr += Math.floor(pDate.getMonth() / 3) + 1; + break; + case 'hh': + if ((((pDate.getHours() % 12) == 0) ? 12 : pDate.getHours() % 12) < 10) + vDateStr += '0'; // now fall through + case 'h': + vDateStr += ((pDate.getHours() % 12) == 0) ? 12 : pDate.getHours() % 12; + break; + case 'HH': + if ((pDate.getHours()) < 10) + vDateStr += '0'; // now fall through + case 'H': + vDateStr += (pDate.getHours()); + break; + case 'MI': + if (pDate.getMinutes() < 10) + vDateStr += '0'; // now fall through + case 'mi': + vDateStr += pDate.getMinutes(); + break; + case 'SS': + if (pDate.getSeconds() < 10) + vDateStr += '0'; // now fall through + case 'ss': + vDateStr += pDate.getSeconds(); + break; + case 'pm': + vDateStr += ((pDate.getHours()) < 12) ? 'am' : 'pm'; + break; + case 'PM': + vDateStr += ((pDate.getHours()) < 12) ? 'AM' : 'PM'; + break; + case 'ww': + if (exports.getIsoWeek(pDate) < 10) + vDateStr += '0'; // now fall through + case 'w': + vDateStr += exports.getIsoWeek(pDate); + break; + case 'week': + var vWeekNum = exports.getIsoWeek(pDate); + var vYear = pDate.getFullYear(); + var vDayOfWeek = (pDate.getDay() == 0) ? 7 : pDate.getDay(); + if (vWeekNum >= 52 && parseInt(vMonthStr, 10) === 1) + vYear--; + if (vWeekNum == 1 && parseInt(vMonthStr, 10) === 12) + vYear++; + if (vWeekNum < 10) + vWeekNum = parseInt('0' + vWeekNum, 10); + vDateStr += vYear + '-W' + vWeekNum + '-' + vDayOfWeek; + break; + default: + if (pL[pDateFormatArr[i].toLowerCase()]) + vDateStr += pL[pDateFormatArr[i].toLowerCase()]; + else + vDateStr += pDateFormatArr[i]; + break; + } + } + return vDateStr; +}; +exports.parseDateFormatStr = function (pFormatStr) { + var vComponantStr = ''; + var vCurrChar = ''; + var vSeparators = new RegExp('[\/\\ -.,\'":]'); + var vDateFormatArray = new Array(); + for (var i = 0; i < pFormatStr.length; i++) { + vCurrChar = pFormatStr.charAt(i); + if ((vCurrChar.match(vSeparators)) || (i + 1 == pFormatStr.length)) // separator or end of string + { + if ((i + 1 == pFormatStr.length) && (!(vCurrChar.match(vSeparators)))) // at end of string add any non-separator chars to the current component + { + vComponantStr += vCurrChar; + } + vDateFormatArray.push(vComponantStr); + if (vCurrChar.match(vSeparators)) + vDateFormatArray.push(vCurrChar); + vComponantStr = ''; + } + else { + vComponantStr += vCurrChar; + } + } + return vDateFormatArray; +}; +/** + * We have to compare against the monday of the first week of the year containing 04 jan *not* 01/01 + * 60*60*24*1000=86400000 + * @param pDate + */ +exports.getIsoWeek = function (pDate) { + var dayMiliseconds = 86400000; + var keyDay = new Date(pDate.getFullYear(), 0, 4, 0, 0, 0); + var keyDayOfWeek = (keyDay.getDay() == 0) ? 6 : keyDay.getDay() - 1; // define monday as 0 + var firstMondayYearTime = keyDay.getTime() - (keyDayOfWeek * dayMiliseconds); + var thisDate = new Date(pDate.getFullYear(), pDate.getMonth(), pDate.getDate(), 0, 0, 0); // This at 00:00:00 + var thisTime = thisDate.getTime(); + var daysFromFirstMonday = Math.round(((thisTime - firstMondayYearTime) / dayMiliseconds)); + var lastWeek = 99; + var thisWeek = 99; + var firstMondayYear = new Date(firstMondayYearTime); + thisWeek = Math.ceil((daysFromFirstMonday + 1) / 7); + if (thisWeek <= 0) + thisWeek = exports.getIsoWeek(new Date(pDate.getFullYear() - 1, 11, 31, 0, 0, 0)); + else if (thisWeek == 53 && (new Date(pDate.getFullYear(), 0, 1, 0, 0, 0)).getDay() != 4 && (new Date(pDate.getFullYear(), 11, 31, 0, 0, 0)).getDay() != 4) + thisWeek = 1; + return thisWeek; }; -JSGantt.formatDateStr=function(pDate, pDateFormatArr, pL) -{ - var vDateStr=''; - - var vYear2Str=pDate.getFullYear().toString().substring(2,4); - var vMonthStr=(pDate.getMonth()+1)+''; - var vMonthArr=new Array(pL['january'],pL['february'],pL['march'],pL['april'],pL['maylong'],pL['june'],pL['july'],pL['august'],pL['september'],pL['october'],pL['november'],pL['december']); - var vDayArr=new Array(pL['sunday'],pL['monday'],pL['tuesday'],pL['wednesday'],pL['thursday'],pL['friday'],pL['saturday']); - var vMthArr=new Array(pL['jan'],pL['feb'],pL['mar'],pL['apr'],pL['may'],pL['jun'],pL['jul'],pL['aug'],pL['sep'],pL['oct'],pL['nov'],pL['dec']); - var vDyArr=new Array(pL['sun'],pL['mon'],pL['tue'],pL['wed'],pL['thu'],pL['fri'],pL['sat']); - - for (var i=0; i=52 && vMonthStr==1) vYear--; - if (vWeekNum==1 && vMonthStr==12) vYear++; - if (vWeekNum<10) vWeekNum='0'+vWeekNum; - - vDateStr+=vYear+'-W'+vWeekNum+'-'+vDayOfWeek; - break; - default: - if (pL[pDateFormatArr[i].toLowerCase()]) vDateStr+=pL[pDateFormatArr[i].toLowerCase()]; - else vDateStr+=pDateFormatArr[i]; - break; - } - } - return vDateStr; +},{}],12:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var events_1 = require("../events"); +exports.makeInput = function (formattedValue, editable, type, value, choices) { + if (type === void 0) { type = 'text'; } + if (value === void 0) { value = null; } + if (choices === void 0) { choices = null; } + if (!value) { + value = formattedValue; + } + if (editable) { + switch (type) { + case 'date': + // Take timezone into account before converting to ISO String + value = value ? new Date(value.getTime() - (value.getTimezoneOffset() * 60000)).toISOString().split('T')[0] : ''; + return ""; + case 'resource': + if (choices) { + var found = choices.filter(function (c) { return c.id == value || c.name == value; }); + if (found && found.length > 0) { + value = found[0].id; + } + else { + choices.push({ id: value, name: value }); + } + return ""; + } + else { + return ""; + } + case 'cost': + return ""; + default: + return ""; + } + } + else { + return formattedValue; + } +}; +exports.newNode = function (pParent, pNodeType, pId, pClass, pText, pWidth, pLeft, pDisplay, pColspan, pAttribs) { + if (pId === void 0) { pId = null; } + if (pClass === void 0) { pClass = null; } + if (pText === void 0) { pText = null; } + if (pWidth === void 0) { pWidth = null; } + if (pLeft === void 0) { pLeft = null; } + if (pDisplay === void 0) { pDisplay = null; } + if (pColspan === void 0) { pColspan = null; } + if (pAttribs === void 0) { pAttribs = null; } + var vNewNode = pParent.appendChild(document.createElement(pNodeType)); + if (pAttribs) { + for (var i = 0; i + 1 < pAttribs.length; i += 2) { + vNewNode.setAttribute(pAttribs[i], pAttribs[i + 1]); + } + } + if (pId) + vNewNode.id = pId; // I wish I could do this with setAttribute but older IEs don't play nice + if (pClass) + vNewNode.className = pClass; + if (pWidth) + vNewNode.style.width = (isNaN(pWidth * 1)) ? pWidth : pWidth + 'px'; + if (pLeft) + vNewNode.style.left = (isNaN(pLeft * 1)) ? pLeft : pLeft + 'px'; + if (pText) { + if (pText.indexOf && pText.indexOf('<') === -1) { + vNewNode.appendChild(document.createTextNode(pText)); + } + else { + vNewNode.insertAdjacentHTML('beforeend', pText); + } + } + if (pDisplay) + vNewNode.style.display = pDisplay; + if (pColspan) + vNewNode.colSpan = pColspan; + return vNewNode; +}; +exports.getArrayLocationByID = function (pId) { + var vList = this.getList(); + for (var i = 0; i < vList.length; i++) { + if (vList[i].getID() == pId) + return i; + } + return -1; +}; +exports.CalcTaskXY = function () { + var vID; + var vList = this.getList(); + var vBarDiv; + var vTaskDiv; + var vParDiv; + var vLeft, vTop, vWidth; + var vHeight = Math.floor((this.getRowHeight() / 2)); + for (var i = 0; i < vList.length; i++) { + vID = vList[i].getID(); + vBarDiv = vList[i].getBarDiv(); + vTaskDiv = vList[i].getTaskDiv(); + if ((vList[i].getParItem() && vList[i].getParItem().getGroup() == 2)) { + vParDiv = vList[i].getParItem().getChildRow(); + } + else + vParDiv = vList[i].getChildRow(); + if (vBarDiv) { + vList[i].setStartX(vBarDiv.offsetLeft + 1); + vList[i].setStartY(vParDiv.offsetTop + vBarDiv.offsetTop + vHeight - 1); + vList[i].setEndX(vBarDiv.offsetLeft + vBarDiv.offsetWidth + 1); + vList[i].setEndY(vParDiv.offsetTop + vBarDiv.offsetTop + vHeight - 1); + } + } +}; +exports.sLine = function (x1, y1, x2, y2, pClass) { + var vLeft = Math.min(x1, x2); + var vTop = Math.min(y1, y2); + var vWid = Math.abs(x2 - x1) + 1; + var vHgt = Math.abs(y2 - y1) + 1; + var vTmpDiv = document.createElement('div'); + vTmpDiv.id = this.vDivId + 'line' + this.vDepId++; + vTmpDiv.style.position = 'absolute'; + vTmpDiv.style.overflow = 'hidden'; + vTmpDiv.style.zIndex = '0'; + vTmpDiv.style.left = vLeft + 'px'; + vTmpDiv.style.top = vTop + 'px'; + vTmpDiv.style.width = vWid + 'px'; + vTmpDiv.style.height = vHgt + 'px'; + vTmpDiv.style.visibility = 'visible'; + if (vWid == 1) + vTmpDiv.className = 'glinev'; + else + vTmpDiv.className = 'glineh'; + if (pClass) + vTmpDiv.className += ' ' + pClass; + this.getLines().appendChild(vTmpDiv); + if (this.vEvents.onLineDraw && typeof this.vEvents.onLineDraw === 'function') { + this.vEvents.onLineDraw(vTmpDiv); + } + return vTmpDiv; +}; +exports.drawSelector = function (pPos) { + var vOutput = document.createDocumentFragment(); + var vDisplay = false; + for (var i = 0; i < this.vShowSelector.length && !vDisplay; i++) { + if (this.vShowSelector[i].toLowerCase() == pPos.toLowerCase()) + vDisplay = true; + } + if (vDisplay) { + var vTmpDiv = exports.newNode(vOutput, 'div', null, 'gselector', this.vLangs[this.vLang]['format'] + ':'); + if (this.vFormatArr.join().toLowerCase().indexOf('hour') != -1) + events_1.addFormatListeners(this, 'hour', exports.newNode(vTmpDiv, 'span', this.vDivId + 'formathour' + pPos, 'gformlabel' + ((this.vFormat == 'hour') ? ' gselected' : ''), this.vLangs[this.vLang]['hour'])); + if (this.vFormatArr.join().toLowerCase().indexOf('day') != -1) + events_1.addFormatListeners(this, 'day', exports.newNode(vTmpDiv, 'span', this.vDivId + 'formatday' + pPos, 'gformlabel' + ((this.vFormat == 'day') ? ' gselected' : ''), this.vLangs[this.vLang]['day'])); + if (this.vFormatArr.join().toLowerCase().indexOf('week') != -1) + events_1.addFormatListeners(this, 'week', exports.newNode(vTmpDiv, 'span', this.vDivId + 'formatweek' + pPos, 'gformlabel' + ((this.vFormat == 'week') ? ' gselected' : ''), this.vLangs[this.vLang]['week'])); + if (this.vFormatArr.join().toLowerCase().indexOf('month') != -1) + events_1.addFormatListeners(this, 'month', exports.newNode(vTmpDiv, 'span', this.vDivId + 'formatmonth' + pPos, 'gformlabel' + ((this.vFormat == 'month') ? ' gselected' : ''), this.vLangs[this.vLang]['month'])); + if (this.vFormatArr.join().toLowerCase().indexOf('quarter') != -1) + events_1.addFormatListeners(this, 'quarter', exports.newNode(vTmpDiv, 'span', this.vDivId + 'formatquarter' + pPos, 'gformlabel' + ((this.vFormat == 'quarter') ? ' gselected' : ''), this.vLangs[this.vLang]['quarter'])); + } + else { + exports.newNode(vOutput, 'div', null, 'gselector'); + } + return vOutput; }; -JSGantt.parseDateFormatStr=function(pFormatStr) -{ - var vDateStr=''; - var vComponantStr=''; - var vCurrChar=''; - var vSeparators=new RegExp('[\/\\ -.,\'":]'); - var vDateFormatArray=new Array(); - - for (var i=0; i>> 0; +}; +exports.hashKey = function (key) { + return this.hashString(key); +}; +exports.criticalPath = function (tasks) { + var path = {}; + // calculate duration + tasks.forEach(function (task) { + task.duration = new Date(task.pEnd).getTime() - new Date(task.pStart).getTime(); + }); + tasks.forEach(function (task) { + if (!path[task.pID]) { + path[task.pID] = task; + } + if (!path[task.pParent]) { + path[task.pParent] = { + childrens: [] + }; + } + if (!path[task.pID].childrens) { + path[task.pID].childrens = []; + } + path[task.pParent].childrens.push(task); + var max = path[task.pParent].childrens[0].duration; + path[task.pParent].childrens.forEach(function (t) { + if (t.duration > max) { + max = t.duration; + } + }); + path[task.pParent].duration = max; + }); + var finalNodes = { 0: path[0] }; + var node = path[0]; + var _loop_1 = function () { + if (node.childrens.length > 0) { + var found_1 = node.childrens[0]; + var max_1 = found_1.duration; + node.childrens.forEach(function (c) { + if (c.duration > max_1) { + found_1 = c; + max_1 = c.duration; + } + }); + finalNodes[found_1.pID] = found_1; + node = found_1; + } + else { + node = null; + } + }; + while (node) { + _loop_1(); + } +}; +function isParentElementOrSelf(child, parent) { + while (child) { + if (child === parent) + return true; + child = child.parentElement; + } +} +exports.isParentElementOrSelf = isParentElementOrSelf; +exports.updateFlyingObj = function (e, pGanttChartObj, pTimer) { + var vCurTopBuf = 3; + var vCurLeftBuf = 5; + var vCurBotBuf = 3; + var vCurRightBuf = 15; + var vMouseX = (e) ? e.clientX : window.event.clientX; + var vMouseY = (e) ? e.clientY : window.event.clientY; + var vViewportX = document.documentElement.clientWidth || document.getElementsByTagName('body')[0].clientWidth; + var vViewportY = document.documentElement.clientHeight || document.getElementsByTagName('body')[0].clientHeight; + var vNewX = vMouseX; + var vNewY = vMouseY; + var screenX = screen.availWidth || window.innerWidth; + var screenY = screen.availHeight || window.innerHeight; + var vOldX = parseInt(pGanttChartObj.vTool.style.left); + var vOldY = parseInt(pGanttChartObj.vTool.style.top); + if (navigator.appName.toLowerCase() == 'microsoft internet explorer') { + // the clientX and clientY properties include the left and top borders of the client area + vMouseX -= document.documentElement.clientLeft; + vMouseY -= document.documentElement.clientTop; + var vZoomFactor = exports.getZoomFactor(); + if (vZoomFactor != 1) { // IE 7 at non-default zoom level + vMouseX = Math.round(vMouseX / vZoomFactor); + vMouseY = Math.round(vMouseY / vZoomFactor); + } + } + var vScrollPos = exports.getScrollPositions(); + /* Code for positioned right of the mouse by default*/ + /* + if (vMouseX+vCurRightBuf+pGanttChartObj.vTool.offsetWidth>vViewportX) + { + if (vMouseX-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth<0) vNewX=vScrollPos.x; + else vNewX=vMouseX+vScrollPos.x-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth; + } + else vNewX=vMouseX+vScrollPos.x+vCurRightBuf; + */ + /* Code for positioned left of the mouse by default */ + if (vMouseX - vCurLeftBuf - pGanttChartObj.vTool.offsetWidth < 0) { + if (vMouseX + vCurRightBuf + pGanttChartObj.vTool.offsetWidth > vViewportX) + vNewX = vScrollPos.x; + else + vNewX = vMouseX + vScrollPos.x + vCurRightBuf; + } + else + vNewX = vMouseX + vScrollPos.x - vCurLeftBuf - pGanttChartObj.vTool.offsetWidth; + /* Code for positioned below the mouse by default */ + if (vMouseY + vCurBotBuf + pGanttChartObj.vTool.offsetHeight > vViewportY) { + if (vMouseY - vCurTopBuf - pGanttChartObj.vTool.offsetHeight < 0) + vNewY = vScrollPos.y; + else + vNewY = vMouseY + vScrollPos.y - vCurTopBuf - pGanttChartObj.vTool.offsetHeight; + } + else + vNewY = vMouseY + vScrollPos.y + vCurBotBuf; + /* Code for positioned above the mouse by default */ + /* + if (vMouseY-vCurTopBuf-pGanttChartObj.vTool.offsetHeight<0) + { + if (vMouseY+vCurBotBuf+pGanttChartObj.vTool.offsetHeight>vViewportY) vNewY=vScrollPos.y; + else vNewY=vMouseY+vScrollPos.y+vCurBotBuf; + } + else vNewY=vMouseY+vScrollPos.y-vCurTopBuf-pGanttChartObj.vTool.offsetHeight; + */ + var outViewport = Math.abs(vOldX - vNewX) > screenX || Math.abs(vOldY - vNewY) > screenY; + if (pGanttChartObj.getUseMove() && !outViewport) { + clearInterval(pGanttChartObj.vTool.moveInterval); + pGanttChartObj.vTool.moveInterval = setInterval(function () { exports.moveToolTip(vNewX, vNewY, pGanttChartObj.vTool, pTimer); }, pTimer); + } + else { + pGanttChartObj.vTool.style.left = vNewX + 'px'; + pGanttChartObj.vTool.style.top = vNewY + 'px'; + } +}; +exports.moveToolTip = function (pNewX, pNewY, pTool, timer) { + var vSpeed = parseInt(pTool.getAttribute('moveSpeed')); + var vOldX = parseInt(pTool.style.left); + var vOldY = parseInt(pTool.style.top); + if (pTool.style.visibility != 'visible') { + pTool.style.left = pNewX + 'px'; + pTool.style.top = pNewY + 'px'; + clearInterval(pTool.moveInterval); + } + else { + if (pNewX != vOldX && pNewY != vOldY) { + vOldX += Math.ceil((pNewX - vOldX) / vSpeed); + vOldY += Math.ceil((pNewY - vOldY) / vSpeed); + pTool.style.left = vOldX + 'px'; + pTool.style.top = vOldY + 'px'; + } + else { + clearInterval(pTool.moveInterval); + } + } +}; +exports.makeRequest = function (pFile, json, vDebug) { + if (json === void 0) { json = true; } + if (vDebug === void 0) { vDebug = false; } + if (window.fetch) { + var f = fetch(pFile); + if (json) { + return f.then(function (res) { return res.json(); }); + } + else { + return f; + } + } + else { + return exports.makeRequestOldBrowsers(pFile, vDebug) + .then(function (xhttp) { + if (json) { + var jsonObj = JSON.parse(xhttp.response); + return jsonObj; + } + else { + var xmlDoc = xhttp.responseXML; + return xmlDoc; + } + }); + } +}; +exports.makeRequestOldBrowsers = function (pFile, vDebug) { + if (vDebug === void 0) { vDebug = false; } + return new Promise(function (resolve, reject) { + var bd; + if (vDebug) { + bd = new Date(); + console.info('before jsonparse', bd); + } + var xhttp; + if (window.XMLHttpRequest) { + xhttp = new XMLHttpRequest(); + } + else { // IE 5/6 + xhttp = new window.ActiveXObject('Microsoft.XMLHTTP'); + } + xhttp.open('GET', pFile, true); + xhttp.send(null); + xhttp.onload = function (e) { + if (xhttp.readyState === 4) { + if (xhttp.status === 200) { + // resolve(xhttp.responseText); + } + else { + console.error(xhttp.statusText); + } + if (vDebug) { + bd = new Date(); + console.info('before jsonparse', bd); + } + resolve(xhttp); + } + }; + xhttp.onerror = function (e) { + reject(xhttp.statusText); + }; + }); +}; +exports.calculateStartEndFromDepend = function (tasksList) { }; -JSGantt.parseXML=function(pFile,pGanttVar) -{ - if (window.XMLHttpRequest) { - var xhttp=new XMLHttpRequest(); - } else { // IE 5/6 - xhttp=new ActiveXObject('Microsoft.XMLHTTP'); - } - - xhttp.open('GET', pFile, false); - xhttp.send(null); - var xmlDoc=xhttp.responseXML; - - JSGantt.AddXMLTask(pGanttVar, xmlDoc); +},{}],14:[function(require,module,exports){ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var task_1 = require("./task"); +var date_utils_1 = require("./utils/date_utils"); +var draw_utils_1 = require("./utils/draw_utils"); +var general_utils_1 = require("./utils/general_utils"); +exports.parseXML = function (pFile, pGanttVar) { + return general_utils_1.makeRequest(pFile, false, false) + .then(function (xmlDoc) { + exports.AddXMLTask(pGanttVar, xmlDoc); + }); }; - -JSGantt.parseXMLString=function(pStr,pGanttVar) -{ - if (typeof window.DOMParser!='undefined') { - var xmlDoc =(new window.DOMParser()).parseFromString(pStr, 'text/xml'); - } else if (typeof window.ActiveXObject!='undefined' && - new window.ActiveXObject('Microsoft.XMLDOM')) { - xmlDoc=new window.ActiveXObject('Microsoft.XMLDOM'); - xmlDoc.async='false'; - xmlDoc.loadXML(pStr); - } - - JSGantt.AddXMLTask(pGanttVar, xmlDoc); +exports.parseXMLString = function (pStr, pGanttVar) { + var xmlDoc; + if (typeof window.DOMParser != 'undefined') { + xmlDoc = (new window.DOMParser()).parseFromString(pStr, 'text/xml'); + } + else if (typeof window.ActiveXObject != 'undefined' && + new window.ActiveXObject('Microsoft.XMLDOM')) { + xmlDoc = new window.ActiveXObject('Microsoft.XMLDOM'); + xmlDoc.async = 'false'; + xmlDoc.loadXML(pStr); + } + exports.AddXMLTask(pGanttVar, xmlDoc); }; - - -JSGantt.findXMLNode=function(pRoot,pNodeName) -{ - var vRetValue; - - try {vRetValue=pRoot.getElementsByTagName(pNodeName); - } catch (error) { ; } // do nothing, we'll return undefined - - return vRetValue; +exports.findXMLNode = function (pRoot, pNodeName) { + var vRetValue; + try { + vRetValue = pRoot.getElementsByTagName(pNodeName); + } + catch (error) { + ; + } // do nothing, we'll return undefined + return vRetValue; }; - // pType can be 1=numeric, 2=String, all other values just return raw data -JSGantt.getXMLNodeValue=function(pRoot,pNodeName,pType,pDefault) -{ - var vRetValue; - - try {vRetValue=pRoot.getElementsByTagName(pNodeName)[0].childNodes[0].nodeValue; - } catch (error) - { - if (typeof pDefault!='undefined')vRetValue=pDefault; - } - - if (typeof vRetValue!='undefined' && vRetValue!=null) - { - if (pType==1)vRetValue*=1; - else if (pType==2)vRetValue=vRetValue.toString(); - } - return vRetValue; +exports.getXMLNodeValue = function (pRoot, pNodeName, pType, pDefault) { + var vRetValue; + try { + vRetValue = pRoot.getElementsByTagName(pNodeName)[0].childNodes[0].nodeValue; + } + catch (error) { + if (typeof pDefault != 'undefined') + vRetValue = pDefault; + } + if (typeof vRetValue != 'undefined' && vRetValue != null) { + if (pType == 1) + vRetValue *= 1; + else if (pType == 2) + vRetValue = vRetValue.toString(); + } + return vRetValue; +}; +exports.AddXMLTask = function (pGanttVar, pXmlDoc) { + var project = ''; + var Task; + var n = 0; + var m = 0; + var i = 0; + var j = 0; + var k = 0; + var maxPID = 0; + var ass = new Array(); + var assRes = new Array(); + var res = new Array(); + var pars = new Array(); + var projNode = exports.findXMLNode(pXmlDoc, 'Project'); + if (typeof projNode != 'undefined' && projNode.length > 0) { + project = projNode[0].getAttribute('xmlns'); + } + if (project == 'http://schemas.microsoft.com/project') { + pGanttVar.setDateInputFormat('yyyy-mm-dd'); + Task = exports.findXMLNode(pXmlDoc, 'Task'); + if (typeof Task == 'undefined') + n = 0; + else + n = Task.length; + var resources = exports.findXMLNode(pXmlDoc, 'Resource'); + if (typeof resources == 'undefined') { + n = 0; + m = 0; + } + else + m = resources.length; + for (i = 0; i < m; i++) { + var resname = exports.getXMLNodeValue(resources[i], 'Name', 2, ''); + var uid = exports.getXMLNodeValue(resources[i], 'UID', 1, -1); + if (resname.length > 0 && uid > 0) + res[uid] = resname; + } + var assignments = exports.findXMLNode(pXmlDoc, 'Assignment'); + if (typeof assignments == 'undefined') + j = 0; + else + j = assignments.length; + for (i = 0; i < j; i++) { + var uid = void 0; + var resUID = exports.getXMLNodeValue(assignments[i], 'ResourceUID', 1, -1); + uid = exports.getXMLNodeValue(assignments[i], 'TaskUID', 1, -1); + if (uid > 0) { + if (resUID > 0) + assRes[uid] = res[resUID]; + ass[uid] = assignments[i]; + } + } + // Store information about parent UIDs in an easily searchable form + for (i = 0; i < n; i++) { + var uid = void 0; + uid = exports.getXMLNodeValue(Task[i], 'UID', 1, 0); + var vOutlineNumber = void 0; + if (uid != 0) + vOutlineNumber = exports.getXMLNodeValue(Task[i], 'OutlineNumber', 2, '0'); + if (uid > 0) + pars[vOutlineNumber] = uid; + if (uid > maxPID) + maxPID = uid; + } + for (i = 0; i < n; i++) { + // optional parameters may not have an entry + // Task ID must NOT be zero otherwise it will be skipped + var pID = exports.getXMLNodeValue(Task[i], 'UID', 1, 0); + if (pID != 0) { + var pName = exports.getXMLNodeValue(Task[i], 'Name', 2, 'No Task Name'); + var pStart = exports.getXMLNodeValue(Task[i], 'Start', 2, ''); + var pEnd = exports.getXMLNodeValue(Task[i], 'Finish', 2, ''); + var pPlanStart = exports.getXMLNodeValue(Task[i], 'PlanStart', 2, ''); + var pPlanEnd = exports.getXMLNodeValue(Task[i], 'PlanFinish', 2, ''); + var pDuration = exports.getXMLNodeValue(Task[i], 'Duration', 2, ''); + var pLink = exports.getXMLNodeValue(Task[i], 'HyperlinkAddress', 2, ''); + var pMile = exports.getXMLNodeValue(Task[i], 'Milestone', 1, 0); + var pComp = exports.getXMLNodeValue(Task[i], 'PercentWorkComplete', 1, 0); + var pCost = exports.getXMLNodeValue(Task[i], 'Cost', 2, 0); + var pGroup = exports.getXMLNodeValue(Task[i], 'Summary', 1, 0); + var pParent = 0; + var vOutlineLevel = exports.getXMLNodeValue(Task[i], 'OutlineLevel', 1, 0); + var vOutlineNumber = void 0; + if (vOutlineLevel > 1) { + vOutlineNumber = exports.getXMLNodeValue(Task[i], 'OutlineNumber', 2, '0'); + pParent = pars[vOutlineNumber.substr(0, vOutlineNumber.lastIndexOf('.'))]; + } + var pNotes = void 0; + try { + pNotes = Task[i].getElementsByTagName('Notes')[0].childNodes[1].nodeValue; //this should be a CDATA node + } + catch (error) { + pNotes = ''; + } + var pRes = void 0; + if (typeof assRes[pID] != 'undefined') + pRes = assRes[pID]; + else + pRes = ''; + var predecessors = exports.findXMLNode(Task[i], 'PredecessorLink'); + if (typeof predecessors == 'undefined') + j = 0; + else + j = predecessors.length; + var pDepend = ''; + for (k = 0; k < j; k++) { + var depUID = exports.getXMLNodeValue(predecessors[k], 'PredecessorUID', 1, -1); + var depType = exports.getXMLNodeValue(predecessors[k], 'Type', 1, 1); + if (depUID > 0) { + if (pDepend.length > 0) + pDepend += ','; + switch (depType) { + case 0: + pDepend += depUID + 'FF'; + break; + case 1: + pDepend += depUID + 'FS'; + break; + case 2: + pDepend += depUID + 'SF'; + break; + case 3: + pDepend += depUID + 'SS'; + break; + default: + pDepend += depUID + 'FS'; + break; + } + } + } + var pOpen = 1; + var pCaption = ''; + var pClass = void 0; + if (pGroup > 0) + pClass = 'ggroupblack'; + else if (pMile > 0) + pClass = 'gmilestone'; + else + pClass = 'gtaskblue'; + // check for split tasks + var splits = exports.findXMLNode(ass[pID], 'TimephasedData'); + if (typeof splits == 'undefined') + j = 0; + else + j = splits.length; + var vSplitStart = pStart; + var vSplitEnd = pEnd; + var vSubCreated = false; + var vDepend = pDepend.replace(/,*[0-9]+[FS]F/g, ''); + for (k = 0; k < j; k++) { + var vDuration = exports.getXMLNodeValue(splits[k], 'Value', 2, '0'); + //remove all text + vDuration = '0' + vDuration.replace(/\D/g, ''); + vDuration *= 1; + if ((vDuration == 0 && !vSubCreated) || (k + 1 == j && pGroup == 2)) { + // No time booked in the given period (or last entry) + // Make sure the parent task is set as a combined group + pGroup = 2; + // Handle last loop + if (k + 1 == j) + vDepend = pDepend.replace(/,*[0-9]+[FS]S/g, ''); + // Now create a subtask + maxPID++; + vSplitEnd = exports.getXMLNodeValue(splits[k], (k + 1 == j) ? 'Finish' : 'Start', 2, ''); + pGanttVar.AddTaskItem(new task_1.TaskItem(maxPID, pName, vSplitStart, vSplitEnd, 'gtaskblue', pLink, pMile, pRes, pComp, 0, pID, pOpen, vDepend, pCaption, pNotes, pGanttVar, pCost, pPlanStart, pPlanEnd, pDuration)); + vSubCreated = true; + vDepend = ''; + } + else if (vDuration != 0 && vSubCreated) { + vSplitStart = exports.getXMLNodeValue(splits[k], 'Start', 2, ''); + vSubCreated = false; + } + } + if (vSubCreated) + pDepend = ''; + // Finally add the task + pGanttVar.AddTaskItem(new task_1.TaskItem(pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGanttVar, pCost, pPlanStart, pPlanEnd, pDuration)); + } + } + } + else { + Task = pXmlDoc.getElementsByTagName('task'); + n = Task.length; + for (i = 0; i < n; i++) { + // optional parameters may not have an entry + // Task ID must NOT be zero otherwise it will be skipped + var pID = exports.getXMLNodeValue(Task[i], 'pID', 1, 0); + if (pID != 0) { + var pName = exports.getXMLNodeValue(Task[i], 'pName', 2, 'No Task Name'); + var pStart = exports.getXMLNodeValue(Task[i], 'pStart', 2, ''); + var pEnd = exports.getXMLNodeValue(Task[i], 'pEnd', 2, ''); + var pPlanStart = exports.getXMLNodeValue(Task[i], 'pPlanStart', 2, ''); + var pPlanEnd = exports.getXMLNodeValue(Task[i], 'pPlanEnd', 2, ''); + var pDuration = exports.getXMLNodeValue(Task[i], 'pDuration', 2, ''); + var pLink = exports.getXMLNodeValue(Task[i], 'pLink', 2, ''); + var pMile = exports.getXMLNodeValue(Task[i], 'pMile', 1, 0); + var pComp = exports.getXMLNodeValue(Task[i], 'pComp', 1, 0); + var pCost = exports.getXMLNodeValue(Task[i], 'pCost', 2, 0); + var pGroup = exports.getXMLNodeValue(Task[i], 'pGroup', 1, 0); + var pParent = exports.getXMLNodeValue(Task[i], 'pParent', 1, 0); + var pRes = exports.getXMLNodeValue(Task[i], 'pRes', 2, ''); + var pOpen = exports.getXMLNodeValue(Task[i], 'pOpen', 1, 1); + var pDepend = exports.getXMLNodeValue(Task[i], 'pDepend', 2, ''); + var pCaption = exports.getXMLNodeValue(Task[i], 'pCaption', 2, ''); + var pNotes = exports.getXMLNodeValue(Task[i], 'pNotes', 2, ''); + var pClass = exports.getXMLNodeValue(Task[i], 'pClass', 2, ''); + if (typeof pClass == 'undefined') { + if (pGroup > 0) + pClass = 'ggroupblack'; + else if (pMile > 0) + pClass = 'gmilestone'; + else + pClass = 'gtaskblue'; + } + // Finally add the task + pGanttVar.AddTaskItem(new task_1.TaskItem(pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGanttVar, pCost, pPlanStart, pPlanEnd, pDuration)); + } + } + } +}; +exports.getXMLProject = function () { + var vProject = ''; + for (var i = 0; i < this.vTaskList.length; i++) { + vProject += this.getXMLTask(i, true); + } + vProject += ''; + return vProject; +}; +exports.getXMLTask = function (pID, pIdx) { + var i = 0; + var vIdx = -1; + var vTask = ''; + var vOutFrmt = date_utils_1.parseDateFormatStr(this.getDateInputFormat() + ' HH:MI:SS'); + if (pIdx === true) + vIdx = pID; + else { + for (i = 0; i < this.vTaskList.length; i++) { + if (this.vTaskList[i].getID() == pID) { + vIdx = i; + break; + } + } + } + if (vIdx >= 0 && vIdx < this.vTaskList.length) { + /* Simplest way to return case sensitive node names is to just build a string */ + vTask = ''; + vTask += '' + this.vTaskList[vIdx].getID() + ''; + vTask += '' + this.vTaskList[vIdx].getName() + ''; + vTask += '' + date_utils_1.formatDateStr(this.vTaskList[vIdx].getStart(), vOutFrmt, this.vLangs[this.vLang]) + ''; + vTask += '' + date_utils_1.formatDateStr(this.vTaskList[vIdx].getEnd(), vOutFrmt, this.vLangs[this.vLang]) + ''; + vTask += '' + date_utils_1.formatDateStr(this.vTaskList[vIdx].getPlanStart(), vOutFrmt, this.vLangs[this.vLang]) + ''; + vTask += '' + date_utils_1.formatDateStr(this.vTaskList[vIdx].getPlanEnd(), vOutFrmt, this.vLangs[this.vLang]) + ''; + vTask += '' + this.vTaskList[vIdx].getDuration() + ''; + vTask += '' + this.vTaskList[vIdx].getClass() + ''; + vTask += '' + this.vTaskList[vIdx].getLink() + ''; + vTask += '' + this.vTaskList[vIdx].getMile() + ''; + if (this.vTaskList[vIdx].getResource() != '\u00A0') + vTask += '' + this.vTaskList[vIdx].getResource() + ''; + vTask += '' + this.vTaskList[vIdx].getCompVal() + ''; + vTask += '' + this.vTaskList[vIdx].getCost() + ''; + vTask += '' + this.vTaskList[vIdx].getGroup() + ''; + vTask += '' + this.vTaskList[vIdx].getParent() + ''; + vTask += '' + this.vTaskList[vIdx].getOpen() + ''; + vTask += ''; + var vDepList = this.vTaskList[vIdx].getDepend(); + for (i = 0; i < vDepList.length; i++) { + if (i > 0) + vTask += ','; + if (vDepList[i] > 0) + vTask += vDepList[i] + this.vTaskList[vIdx].getDepType()[i]; + } + vTask += ''; + vTask += '' + this.vTaskList[vIdx].getCaption() + ''; + var vTmpFrag = document.createDocumentFragment(); + var vTmpDiv = draw_utils_1.newNode(vTmpFrag, 'div', null, null, this.vTaskList[vIdx].getNotes().innerHTML); + vTask += '' + vTmpDiv.innerHTML + ''; + vTask += ''; + } + return vTask; }; -JSGantt.AddXMLTask=function(pGanttVar, pXmlDoc) -{ - var project=''; - var vMSP=false; - var Task; - var n=0; - var m=0; - var i=0; - var j=0; - var k=0; - var maxPID=0; - var ass=new Array(); - var assRes=new Array(); - var res=new Array(); - var pars=new Array(); - - var projNode=JSGantt.findXMLNode(pXmlDoc,'Project'); - if (typeof projNode!='undefined' && projNode.length>0) project=projNode[0].getAttribute('xmlns'); - - if(project=='http://schemas.microsoft.com/project') - { - vMSP=true; - pGanttVar.setDateInputFormat('yyyy-mm-dd'); - Task=JSGantt.findXMLNode(pXmlDoc,'Task'); - if (typeof Task=='undefined')n=0; - else n=Task.length; - - var resources=JSGantt.findXMLNode(pXmlDoc,'Resource'); - if (typeof resources=='undefined'){n=0; m=0;} - else m=resources.length; - - for(i=0;i0 && uid>0) res[uid]=resname; - } - - var assignments=JSGantt.findXMLNode(pXmlDoc,'Assignment'); - if (typeof assignments=='undefined') j=0; - else j=assignments.length; - - for(i=0;i0) - { - if (resUID>0) assRes[uid]=res[resUID]; - ass[uid]=assignments[i]; - } - } - - // Store information about parent UIDs in an easily searchable form - for(i=0;i0) pars[vOutlineNumber]=uid; - if (uid>maxPID)maxPID=uid; - } - - for(i=0;i1) - { - vOutlineNumber=JSGantt.getXMLNodeValue(Task[i],'OutlineNumber',2,'0'); - pParent=pars[vOutlineNumber.substr(0, vOutlineNumber.lastIndexOf('.'))]; - } - - try {var pNotes=Task[i].getElementsByTagName('Notes')[0].childNodes[1].nodeValue; //this should be a CDATA node - } catch (error) - {pNotes ='';} - - if(typeof assRes[pID]!='undefined') var pRes=assRes[pID]; - else pRes=''; - - var predecessors=JSGantt.findXMLNode(Task[i],'PredecessorLink'); - if (typeof predecessors=='undefined') j=0; - else j=predecessors.length; - var pDepend=''; - - for(k=0;k0) - { - if (pDepend.length>0)pDepend+=','; - switch(depType) - { - case 0: pDepend+=depUID+'FF'; break; - case 1: pDepend+=depUID+'FS'; break; - case 2: pDepend+=depUID+'SF'; break; - case 3: pDepend+=depUID+'SS'; break; - default: pDepend+=depUID+'FS'; break; - } - } - } - - var pOpen=1; - var pCaption=''; - - if(pGroup>0) var pClass ='ggroupblack'; - else if(pMile>0) pClass ='gmilestone'; - else pClass ='gtaskblue'; - - // check for split tasks - - var splits=JSGantt.findXMLNode(ass[pID],'TimephasedData'); - if (typeof splits=='undefined') j=0; - else j=splits.length; - - var vSplitStart=pStart; - var vSplitEnd=pEnd; - var vSubCreated=false; - var vDepend=pDepend.replace(/,*[0-9]+[FS]F/g,''); - - for(k=0;k0) pClass ='ggroupblack'; - else if(pMile>0) pClass ='gmilestone'; - else pClass ='gtaskblue'; - } - - // Finally add the task - pGanttVar.AddTaskItem(new JSGantt.TaskItem(pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGanttVar)); - } - } - } -}; - - -JSGantt.benchMark=function(pItem) -{ - var vEndTime=new Date().getTime(); - alert(pItem+': Elapsed time: '+((vEndTime-vBenchTime)/1000)+' seconds.'); - vBenchTime=new Date().getTime(); -}; - -JSGantt.getIsoWeek=function(pDate){ - // We have to compare against the monday of the first week of the year containing 04 jan *not* 01/01 - // 60*60*24*1000=86400000 - var dayMiliseconds=86400000; - var keyDay=new Date(pDate.getFullYear(),0,4,0,0,0); - var keyDayOfWeek=(keyDay.getDay()==0) ? 6 : keyDay.getDay()-1; // define monday as 0 - var firstMondayYearTime=keyDay.getTime()-(keyDayOfWeek * dayMiliseconds); - var thisDate=new Date(pDate.getFullYear(), pDate.getMonth(),pDate.getDate(),0,0,0); // This at 00:00:00 - var thisTime=thisDate.getTime(); - var daysFromFirstMonday=Math.round(((thisTime-firstMondayYearTime) / dayMiliseconds)); - var lastWeek=99; - var thisWeek=99; - - var firstMondayYear=new Date(firstMondayYearTime); - - thisWeek=Math.ceil((daysFromFirstMonday+1)/7); - - if (thisWeek<=0) thisWeek=JSGantt.getIsoWeek(new Date(pDate.getFullYear()-1,11,31,0,0,0)); - else if (thisWeek==53 && (new Date(pDate.getFullYear(),0,1,0,0,0)).getDay()!=4 && (new Date(pDate.getFullYear(),11,31,0,0,0)).getDay()!=4) thisWeek=1; - return thisWeek; -}; - -JSGantt.addListener=function (eventName, handler, control) -{ - // Check if control is a string - if (control===String(control)) control=JSGantt.findObj(control); - - if(control.addEventListener) //Standard W3C - { - return control.addEventListener(eventName, handler, false); - } - else if (control.attachEvent) //IExplore - { - return control.attachEvent('on'+eventName, handler); - } - else - { - return false; - } -}; - -JSGantt.addTooltipListeners=function(pGanttChart, pObj1, pObj2) -{ - JSGantt.addListener('mouseover', function (e) {JSGantt.showToolTip(pGanttChart, e, pObj2, null, pGanttChart.getTimer());}, pObj1); - JSGantt.addListener('mouseout', function (e) {JSGantt.delayedHide(pGanttChart, pGanttChart.vTool, pGanttChart.getTimer());}, pObj1); -}; - -JSGantt.addThisRowListeners=function(pGanttChart, pObj1, pObj2) -{ - JSGantt.addListener('mouseover', function () {pGanttChart.mouseOver(pObj1, pObj2);}, pObj1); - JSGantt.addListener('mouseover', function () {pGanttChart.mouseOver(pObj1, pObj2);}, pObj2); - JSGantt.addListener('mouseout', function () {pGanttChart.mouseOut(pObj1, pObj2);}, pObj1); - JSGantt.addListener('mouseout', function () {pGanttChart.mouseOut(pObj1, pObj2);}, pObj2); -}; - -JSGantt.addFolderListeners=function(pGanttChart, pObj, pID) -{ - JSGantt.addListener('click', function () {JSGantt.folder(pID, pGanttChart);}, pObj); -}; - -JSGantt.addFormatListeners=function(pGanttChart, pFormat, pObj) -{ - JSGantt.addListener('click', function () {JSGantt.changeFormat(pFormat, pGanttChart);}, pObj); -}; - -JSGantt.addScrollListeners=function(pGanttChart) -{ - JSGantt.addListener('scroll', function () {pGanttChart.getChartBody().scrollTop=pGanttChart.getListBody().scrollTop;}, pGanttChart.getListBody()); - JSGantt.addListener('scroll', function () {pGanttChart.getListBody().scrollTop=pGanttChart.getChartBody().scrollTop;}, pGanttChart.getChartBody()); - JSGantt.addListener('scroll', function () {pGanttChart.getChartHead().scrollLeft=pGanttChart.getChartBody().scrollLeft;}, pGanttChart.getChartBody()); - JSGantt.addListener('scroll', function () {pGanttChart.getChartBody().scrollLeft=pGanttChart.getChartHead().scrollLeft;}, pGanttChart.getChartHead()); - JSGantt.addListener('resize', function () {pGanttChart.getChartHead().scrollLeft=pGanttChart.getChartBody().scrollLeft;}, window); - JSGantt.addListener('resize', function () {pGanttChart.getListBody().scrollTop=pGanttChart.getChartBody().scrollTop;}, window); -}; +},{"./task":10,"./utils/date_utils":11,"./utils/draw_utils":12,"./utils/general_utils":13}]},{},[1])(1) +}); \ No newline at end of file diff --git a/htdocs/includes/jsgantt/main.css b/htdocs/includes/jsgantt/main.css deleted file mode 100644 index 544192b1b2d..00000000000 --- a/htdocs/includes/jsgantt/main.css +++ /dev/null @@ -1,126 +0,0 @@ -body { - position: relative; -} - -.product-name { - font-family: Satisfy; -} - -#my-navbar-nav { - font-size: 16px; -} - -#my-nav-brand { - font-size: 24px; - margin: 2px 40px 0 10px; - color: #ffffff; -} - -#home { - background: #323232 url(home-bg.jpg) center 0 no-repeat; - background-attachment: fixed; - background-size: cover; - min-height: 660px; - color: #ffffff; -} -#home-title { - font-size: 70px; - margin-top: 180px; -} -#home-subtitle { - font-size: 36px; - margin-top: 40px; - margin-bottom: 50px; -} -#learn-more { - padding: 0; - width: 50px; - height: 50px; - border-radius: 50%; - font-size: 38px; - margin-top: 60px; -} -@media screen and (max-width: 991px) { - #learn-more { - display: none; - } -} - -.section { - margin: 0 15vw; - padding: 5px 0 0; -} - -h1 { - font-family: Kelly Slab; - font-size: 56px; - margin: 50px 0 20px; -} - -h2 { - font-family: Kelly Slab; - margin: 20px 0 10px; -} - -h3 { - font-family: Kelly Slab; -} -#embedded-Gantt, #external-Gantt { -} - -.code-block { - background-color: #222222; -} - -.contact-card { - max-width: 200px; - margin: auto; -} - -.contact-link { - font-size: 28px; -} - -.footer { - margin-top: 10px; - padding-top: 10px; - border-top: solid #bbbbbb 1px; -} - -#slide-card { - background: transparent; - border: none; - margin: 180px 50px 50px; -} -#slide-dots { - background: transparent; - border: none; -} -.dot { - cursor:pointer; - height: 13px; - width: 13px; - margin: 0 2px; - background-color: #ffffff; - opacity: 0.3; - border-radius: 50%; - display: inline-block; - -webkit-transition: all .5s; /* Safari */ - transition: all .5s; -} -.dot:hover { - opacity: 1; -} -.dot.active { - opacity: 1; -} -.slide { - width: 100%; - height: 150px; - font-size: 20px; - display: none; - opacity: 0; -} -.slide-icon { - font-size: 60px; -} diff --git a/htdocs/includes/jsgantt/main.js b/htdocs/includes/jsgantt/main.js deleted file mode 100644 index 53f5f8f0c10..00000000000 --- a/htdocs/includes/jsgantt/main.js +++ /dev/null @@ -1,85 +0,0 @@ -$(document).ready(function() { - - // Scrollspy changes navbar active links - $("body").scrollspy({target:"#my-navbar-nav", offset:50}); - - // Smooth scroll - $("a").click(function(event) { - if (this.hash !== "") { - event.preventDefault(); - var linkOffset = 0; - if ($.inArray(this.hash,["#options","#xmlExport","#optionsLanguage","#setDayMajorDateDisplayFormat"]) != -1) { - linkOffset = -25; - } - $("html, body").animate({ - scrollTop: $(this.hash).offset().top - $(".navbar").height() + linkOffset - }, 600); - } - }); - - // Demo buttons - $("#embedded-Gantt").hide(0); - $("#external-Gantt").hide(0); - - $(".btn-demo").click(function() { - if ($(this).html().indexOf("Embedded Code") != -1) { - if ($("#external-Gantt").is(":visible")) { - $("#external-Gantt").animate({ - height: "toggle", - opacity: "toggle"}, 300, function () { - $("#embedded-Gantt").animate({ - height: "toggle", - opacity: "toggle"}, 600 - ); - } - ); - $(".btn-demo:nth-child(2)").removeClass("active"); - } else { - $("#embedded-Gantt").animate({ - height: "toggle", - opacity: "toggle"}, 600 - ); - } - } else { - if ($("#embedded-Gantt").is(":visible")) { - $("#embedded-Gantt").animate({ - height: "toggle", - opacity: "toggle"}, 300, function() { - $("#external-Gantt").animate({ - height: "toggle", - opacity: "toggle"}, 600 - ); - } - ); - $(".btn-demo:nth-child(1)").removeClass("active"); - } else { - $("#external-Gantt").animate({ - height: "toggle", - opacity: "toggle"}, 600 - ); - } - } - }); - - // Slideshow - var slideIndex = 0; - carousel(); - - function carousel() { - var i; - var x = document.getElementsByClassName("slide"); - var d = document.getElementsByClassName("dot"); - for (i = 0; i < x.length; i++) { - x[i].style.display = "none"; - } - slideIndex++; - if (slideIndex > x.length) {slideIndex = 1} - x[slideIndex-1].style.display = "inline-block"; - $(".slide:nth-child(" + (slideIndex).toString() + ")").animate({ - opacity: 1 - }, 500); - $(".dot").removeClass("active"); - $(".dot:nth-child(" + (slideIndex).toString() + ")").addClass("active"); - setTimeout(carousel, 2000); // Change image every 2 seconds - } - }); diff --git a/htdocs/includes/jsgantt/project.xml b/htdocs/includes/jsgantt/project.xml deleted file mode 100644 index 80637aa7cae..00000000000 --- a/htdocs/includes/jsgantt/project.xml +++ /dev/null @@ -1,131 +0,0 @@ - - - - 10 - WCF Changes - - - ggroupblack - - 0 - - 0 - 1 - 0 - 1 - - - - 20 - Move to WCF from remoting - 2017-05-11 09:00 - 2017-05-15 - gtaskblue - - 0 - Paul - 10 - 0 - 10 - 1 - - Paul - This text is only available in tool tips - - - 30 - add Auditing - 2017-05-18 10:30 - 2017-05-20 12:00 - gtaskblue - - 0 - Eduardo - 50 - 0 - 10 - 1 - 20 - Eduardo - - - 40 - Yet another task - 2017-05-24 - 2017-05-25 - gtaskblue - - 0 - Ricardo - 30 - 0 - 0 - 1 - 20,30 - Ricardo - - - 50 - Another Group - - - ggroupblack - - 0 - - 0 - 1 - 0 - 1 - - - - 60 - Move to GitHub - 2017-05-14 09:00 - 2017-05-16 - gtaskblue - - 0 - Ricardo - 10 - 0 - 50 - 1 - - Ricardo - This text is only available in tool tips - - - 70 - Updating files - 2017-05-18 10:30 - 2017-05-21 12:00 - gtaskred - - 0 - Paul - 50 - 0 - 50 - 1 - 60 - Paul - - - 80 - Yet another task - 2017-05-23 - 2017-05-25 - gtaskyellow - - 0 - Eduardo - 30 - 0 - 50 - 1 - 60,70 - Eduardo - - diff --git a/htdocs/projet/ganttchart.inc.php b/htdocs/projet/ganttchart.inc.php index 457345d2fe3..bacd7607f82 100644 --- a/htdocs/projet/ganttchart.inc.php +++ b/htdocs/projet/ganttchart.inc.php @@ -126,9 +126,14 @@ if (g.getDivId() != null) $projecttmp = new Project($db); $projecttmp->fetch($t['task_project_id']); $tmpt = array( - 'task_id'=> '-'.$t['task_project_id'], 'task_alternate_id'=> '-'.$t['task_project_id'], 'task_name'=>$projecttmp->ref.' '.$projecttmp->title, 'task_resources'=>'', - 'task_start_date'=>'', 'task_end_date'=>'', - 'task_is_group'=>1, 'task_position'=>0, 'task_css'=>'ggroupblack', 'task_milestone'=> 0, 'task_parent'=>0, 'task_parent_alternate_id'=>0, 'task_notes'=>'', + 'task_id'=> '-'.$t['task_project_id'], + 'task_alternate_id'=> '-'.$t['task_project_id'], + 'task_name'=>$projecttmp->ref.' '.$projecttmp->title, + 'task_resources'=>'', + 'task_start_date'=>'', + 'task_end_date'=>'', + 'task_is_group'=>1, 'task_position'=>0, 'task_css'=>'ggroupblack', 'task_milestone'=> 0, 'task_parent'=>0, 'task_parent_alternate_id'=>0, + 'task_notes'=>'', 'task_planned_workload'=>0 ); constructGanttLine($tasks, $tmpt, array(), 0, $t['task_project_id']); @@ -180,6 +185,7 @@ function constructGanttLine($tarr, $task, $task_dependencies, $level = 0, $proje $end_date = dol_print_date($end_date, $dateformatinput2); // Resources $resources = $task["task_resources"]; + // Define depend (ex: "", "4,13", ...) $depend = ''; $count = 0; diff --git a/htdocs/projet/ganttview.php b/htdocs/projet/ganttview.php index eeb2fcb7659..8b2f41554d3 100644 --- a/htdocs/projet/ganttview.php +++ b/htdocs/projet/ganttview.php @@ -334,10 +334,11 @@ if (count($tasksarray) > 0) } } } - //if ($s) $tasks[$taskcursor]['task_resources']=''.$langs->trans("List").''; + /* For JSGanttImproved */ //if ($s) $tasks[$taskcursor]['task_resources']=implode(',',$idofusers); $tasks[$taskcursor]['task_resources'] = $s; + if ($s) $tasks[$taskcursor]['task_resources'] = ''.$langs->trans("List").''; //print "xxx".$val->id.$tasks[$taskcursor]['task_resources']; $tasks[$taskcursor]['note'] = $task->note_public; $taskcursor++; diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php index 40440f4fc41..24751dfb9d7 100644 --- a/htdocs/theme/eldy/global.inc.php +++ b/htdocs/theme/eldy/global.inc.php @@ -5095,10 +5095,15 @@ td.gminorheading { .glistlbl, .glistgrid { width: 582px !important; } -.gtaskname div, .gtaskname { +/*.gtaskname div, .gtaskname { min-width: 250px !important; max-width: 250px !important; width: 250px !important; +}*/ +.gtaskname div, .gtaskname { + min-width: 250px !important; + max-width: unset !important; + width: unset !important; } .gpccomplete div, .gpccomplete { min-width: 40px !important; @@ -5108,7 +5113,12 @@ td.gminorheading { td.gtaskheading.gstartdate, td.gtaskheading.genddate { white-space: break-spaces; } - +.gtasktableh tr:nth-child(2) td:nth-child(2), .gtasktableh tr:nth-child(2) td:nth-child(3), .gtasktableh tr:nth-child(2) td:nth-child(4), .gtasktableh tr:nth-child(2) td:nth-child(5), .gtasktableh tr:nth-child(2) td:nth-child(6), .gtasktableh tr:nth-child(2) td:nth-child(7) { + color: transparent !important; + border-left: none; + border-right: none; + border-top: none; +} /* ============================================================================== */ /* jFileTree */ diff --git a/htdocs/theme/md/btn.inc.php b/htdocs/theme/md/btn.inc.php index 1982fb113b9..1b27e7eeafb 100644 --- a/htdocs/theme/md/btn.inc.php +++ b/htdocs/theme/md/btn.inc.php @@ -359,12 +359,41 @@ div.pagination .btnTitle:hover .btnTitle-label{ display: block; } - - - +/* rule to reduce top menu - 2nd reduction: Reduce width of top menu icons again */ +@media only screen and (max-width: global->THEME_ELDY_WITDHOFFSET_FOR_REDUC2) ? round($nbtopmenuentries * 69, 0) + 130 : $conf->global->THEME_ELDY_WITDHOFFSET_FOR_REDUC2; ?>px) /* reduction 2 */ +{ + .butAction, .butActionRefused, .butActionDelete { + font-size: 0.95em; + } + .btnTitle, a.btnTitle { + display: inline-block; + padding: 4px 4px 4px 4px; + min-width: unset; + } +} global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin)) { ?> .butActionRefused, .butActionNewRefused, .btnTitle.refused { display: none !important; } - + + +/* + * BTN LINK + */ + +.btn-link{ + margin-right: 5px; + border: 1px solid #ddd; + color: #333; + padding: 5px 10px; + border-radius:1em; + text-decoration: none !important; +} + +.btn-link:hover{ + background-color: #ddd; + border: 1px solid #ddd; +} + diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index 003416a5baf..f1084f5a537 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -549,8 +549,8 @@ input.removedfile { } input[type=file ] { background-color: transparent; border-top: none; border-left: none; border-right: none; box-shadow: none; } -input[type=checkbox] { background-color: transparent; border: none; box-shadow: none; } -input[type=radio] { background-color: transparent; border: none; box-shadow: none; } +input[type=checkbox] { background-color: transparent; border: none; box-shadow: none; vertical-align: middle; } +input[type=radio] { background-color: transparent; border: none; box-shadow: none; vertical-align: middle; } input[type=image] { background-color: transparent; border: none; box-shadow: none; } input:-webkit-autofill { background-color: #FBFFEA !important; @@ -1899,6 +1899,11 @@ img.photoref, div.photoref { width: 80px; object-fit: contain; } + +div.photoref .fa, div.photoref .fas, div.photoref .far { + font-size: 2.5em; +} + img.fitcontain { object-fit: contain; } @@ -4071,7 +4076,6 @@ div.titre { padding-bottom: 5px; text-transform: uppercase; /* text-shadow: 1px 1px 2px #FFFFFF; */ - dol_optimize_smallscreen) ? '' : 'margin-top: 4px;'); ?> } div.titre { color: var(--colortexttitlenotab); @@ -5000,6 +5004,12 @@ td.gminorheading { td.gtaskheading.gstartdate, td.gtaskheading.genddate { white-space: break-spaces; } +.gtasktableh tr:nth-child(2) td:nth-child(2), .gtasktableh tr:nth-child(2) td:nth-child(3), .gtasktableh tr:nth-child(2) td:nth-child(4), .gtasktableh tr:nth-child(2) td:nth-child(5), .gtasktableh tr:nth-child(2) td:nth-child(6), .gtasktableh tr:nth-child(2) td:nth-child(7) { + color: transparent !important; + border-left: none; + border-right: none; + border-top: none; +} /* ============================================================================== */ @@ -6484,12 +6494,10 @@ div.phpdebugbar-widgets-templates a.phpdebugbar-widgets-editor-link:before padding-right: 5px; } img.photoref, div.photoref { - border: none; + border: 1px solid rgba(0, 0, 0, 0.2); -webkit-box-shadow: none; box-shadow: none; padding: 4px; - height: 20px; - width: 20px; object-fit: contain; } From 64fa2120b2e6e577e66fd1e93e5035a61c510af7 Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Tue, 15 Dec 2020 08:40:14 +0100 Subject: [PATCH 075/743] FIX: Link to subaccount ledger --- htdocs/accountancy/bookkeeping/listbyaccount.php | 2 +- htdocs/langs/en_US/accountancy.lang | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/accountancy/bookkeeping/listbyaccount.php b/htdocs/accountancy/bookkeeping/listbyaccount.php index 18661f8a887..cd15eea52be 100644 --- a/htdocs/accountancy/bookkeeping/listbyaccount.php +++ b/htdocs/accountancy/bookkeeping/listbyaccount.php @@ -380,7 +380,7 @@ $parameters = array(); $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook if (empty($reshook)) { $newcardbutton = dolGetButtonTitle($langs->trans('ViewFlatList'), '', 'fa fa-list paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/list.php?'.$param); - $newcardbutton .= dolGetButtonTitle($langs->trans('VueByAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbyaccount.php?'.$param, '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); + $newcardbutton .= dolGetButtonTitle($langs->trans('VueBySubAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbysubaccount.php?'.$param, '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); $newcardbutton .= '   '; diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 7b406af0c79..14bebea22df 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -50,6 +50,7 @@ CountriesExceptMe=All countries except %s AccountantFiles=Export source documents ExportAccountingSourceDocHelp=With this tool, you can export the source events (list and PDFs) that were used to generate your accountancy. To export your journals, use the menu entry %s - %s. VueByAccountAccounting=View by accounting account +VueBySubAccountAccounting=View by accounting subaccount MainAccountForCustomersNotDefined=Main accounting account for customers not defined in setup MainAccountForSuppliersNotDefined=Main accounting account for vendors not defined in setup From 943f1022c01da67fe383134efb6f5d33092dc1da Mon Sep 17 00:00:00 2001 From: zuiko Date: Tue, 15 Dec 2020 09:26:27 +0100 Subject: [PATCH 076/743] Update fournisseurs.php Product: purchase price tab, the supplier price table with its list of products is not "responsive", as a result, the right-clickable areas, including the "Add purchase price" button, disappear to the right of the screens that are not full HD which forces the user to drag his window with the mouse. The solution is to change the CSS class of the table to adopt the CSS class of the equivalent table of the selling price tab, which is "responsive" to it. We therefore replace : print ''; by : print '
'; Knowing that the class is used in 24 files, there might be other places where this class poses the same ergonomic problem. I don't know enough Dolibarr to generalize the correction ... --- htdocs/product/fournisseurs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/product/fournisseurs.php b/htdocs/product/fournisseurs.php index 3e43937e697..b6f09346385 100644 --- a/htdocs/product/fournisseurs.php +++ b/htdocs/product/fournisseurs.php @@ -877,7 +877,7 @@ SCRIPT; // Suppliers list title print '
'; - print '
'; + print '
'; $param = "&id=".$object->id; From 31e5709e0bd4545f534340f16c2b8c001fe16e66 Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Tue, 15 Dec 2020 09:32:54 +0100 Subject: [PATCH 077/743] Revert "FIX: Link to subaccount ledger" This reverts commit 64fa2120b2e6e577e66fd1e93e5035a61c510af7. --- htdocs/accountancy/bookkeeping/listbyaccount.php | 2 +- htdocs/langs/en_US/accountancy.lang | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/htdocs/accountancy/bookkeeping/listbyaccount.php b/htdocs/accountancy/bookkeeping/listbyaccount.php index cd15eea52be..18661f8a887 100644 --- a/htdocs/accountancy/bookkeeping/listbyaccount.php +++ b/htdocs/accountancy/bookkeeping/listbyaccount.php @@ -380,7 +380,7 @@ $parameters = array(); $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook if (empty($reshook)) { $newcardbutton = dolGetButtonTitle($langs->trans('ViewFlatList'), '', 'fa fa-list paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/list.php?'.$param); - $newcardbutton .= dolGetButtonTitle($langs->trans('VueBySubAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbysubaccount.php?'.$param, '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); + $newcardbutton .= dolGetButtonTitle($langs->trans('VueByAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbyaccount.php?'.$param, '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); $newcardbutton .= '   '; diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 14bebea22df..7b406af0c79 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -50,7 +50,6 @@ CountriesExceptMe=All countries except %s AccountantFiles=Export source documents ExportAccountingSourceDocHelp=With this tool, you can export the source events (list and PDFs) that were used to generate your accountancy. To export your journals, use the menu entry %s - %s. VueByAccountAccounting=View by accounting account -VueBySubAccountAccounting=View by accounting subaccount MainAccountForCustomersNotDefined=Main accounting account for customers not defined in setup MainAccountForSuppliersNotDefined=Main accounting account for vendors not defined in setup From 5ec7b01fb4817badfeb51205caa6a1960c12306c Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Tue, 15 Dec 2020 09:34:46 +0100 Subject: [PATCH 078/743] FIX: Link to subaccount ledger --- htdocs/accountancy/bookkeeping/listbyaccount.php | 2 +- htdocs/langs/en_US/accountancy.lang | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/htdocs/accountancy/bookkeeping/listbyaccount.php b/htdocs/accountancy/bookkeeping/listbyaccount.php index 18661f8a887..cd15eea52be 100644 --- a/htdocs/accountancy/bookkeeping/listbyaccount.php +++ b/htdocs/accountancy/bookkeeping/listbyaccount.php @@ -380,7 +380,7 @@ $parameters = array(); $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook if (empty($reshook)) { $newcardbutton = dolGetButtonTitle($langs->trans('ViewFlatList'), '', 'fa fa-list paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/list.php?'.$param); - $newcardbutton .= dolGetButtonTitle($langs->trans('VueByAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbyaccount.php?'.$param, '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); + $newcardbutton .= dolGetButtonTitle($langs->trans('VueBySubAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbysubaccount.php?'.$param, '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); $newcardbutton .= '   '; diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 41a4cfa59d5..82338c8f98d 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -48,6 +48,7 @@ CountriesExceptMe=All countries except %s AccountantFiles=Export source documents ExportAccountingSourceDocHelp=With this tool, you can export the source events (list and PDFs) that were used to generate your accountancy. To export your journals, use the menu entry %s - %s. VueByAccountAccounting=View by accounting account +VueBySubAccountAccounting=View by accounting subaccount MainAccountForCustomersNotDefined=Main accounting account for customers not defined in setup MainAccountForSuppliersNotDefined=Main accounting account for vendors not defined in setup From 1624b8636f402d916d6a49312e3e71400372af6d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 10:43:19 +0100 Subject: [PATCH 079/743] Show timeone --- htdocs/core/boxes/box_scheduled_jobs.php | 4 ++-- htdocs/cron/card.php | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/htdocs/core/boxes/box_scheduled_jobs.php b/htdocs/core/boxes/box_scheduled_jobs.php index 81e73f4e0c1..23d520e9a8a 100644 --- a/htdocs/core/boxes/box_scheduled_jobs.php +++ b/htdocs/core/boxes/box_scheduled_jobs.php @@ -71,7 +71,7 @@ class box_scheduled_jobs extends ModeleBoxes */ public function loadBox($max = 5) { - global $user, $langs, $conf; + global $user, $langs, $conf, $form; $langs->load("cron"); $this->info_box_head = array('text' => $langs->trans("BoxScheduledJobs", $max)); @@ -151,7 +151,7 @@ class box_scheduled_jobs extends ModeleBoxes ); $this->info_box_contents[$line][] = array( 'td' => 'class="right"', - 'textnoformat' => dol_print_date($resultarray[$line][2], "dayhoursec") + 'textnoformat' => (empty($resultarray[$line][2]) ? '' : $form->textwithpicto(dol_print_date($resultarray[$line][2], "dayhoursec"), $langs->trans("CurrentTimeZone"))) ); $this->info_box_contents[$line][] = array( 'td' => 'class="right" ', diff --git a/htdocs/cron/card.php b/htdocs/cron/card.php index b0e5936d326..facb5976f85 100644 --- a/htdocs/cron/card.php +++ b/htdocs/cron/card.php @@ -623,12 +623,12 @@ if (($action == "create") || ($action == "edit")) print '"; print ""; print ""; print '"; print ''; print ' '; print "\n"; -$var = true; foreach ($dirproduct as $dirroot) { $dir = dol_buildpath($dirroot, 0); @@ -340,7 +339,6 @@ foreach ($dirproduct as $dirroot) if ($modCodeProduct->version == 'development' && $conf->global->MAIN_FEATURES_LEVEL < 2) continue; if ($modCodeProduct->version == 'experimental' && $conf->global->MAIN_FEATURES_LEVEL < 1) continue; - $var = !$var; print ''."\n"; print ''."\n"; print ''."\n"; @@ -537,17 +535,35 @@ print ''."\n"; print ''."\n"; -/* - * Other parameters - */ +// Enable kits (subproducts) -$rowspan = 4; -if (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) $rowspan++; -if (empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) $rowspan++; -if (!empty($conf->global->MAIN_MULTILANGS)) $rowspan++; -if (!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)) $rowspan++; +print ''; +print ''; +print ''; +print ''; +// Enable variants + +print ''; +print ''; +print ''; +print ''; + + +// Rule for price + print ''; if (empty($conf->multicompany->enabled)) { @@ -575,7 +591,7 @@ if (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_ print ''; } -//Default product price base type +// Default product price base type print ''; print ''; print ''; print ''; -// sousproduits activation/desactivation - -print ''; -print ''; -print ''; -print ''; - -// Utilisation formulaire Ajax sur choix produit +// Use Ajax form to select a product print ''; print ''; From 2ae1aac5a46b53cf7df7c2e449aa02ba81fc3610 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 11:18:29 +0100 Subject: [PATCH 083/743] Fix setup --- htdocs/core/class/html.form.class.php | 8 ++++---- htdocs/langs/en_US/admin.lang | 3 ++- htdocs/langs/en_US/modulebuilder.lang | 1 - htdocs/modulebuilder/index.php | 2 +- htdocs/product/admin/product.php | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index fdcdc89e53e..57e48242ce3 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -3611,15 +3611,14 @@ class Form * * @param string $selected Id pre-selectionne * @param string $htmlname Nom de la zone select + * @param string $addjscombo Add js combo * @return string Code of HTML select to chose tax or not */ - public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type') + public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0) { global $langs; - $return = ''; - - $return .= ''; $options = array( 'HT'=>$langs->trans("HT"), 'TTC'=>$langs->trans("TTC") @@ -3635,6 +3634,7 @@ class Form $return .= ''; } $return .= ''; + if ($addjscombo) $return .= ajax_combobox('select_'.$htmlname); return $return; } diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index d314c44aa0e..8e087ae6110 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -56,7 +56,8 @@ GUISetup=Display SetupArea=Setup UploadNewTemplate=Upload new template(s) FormToTestFileUploadForm=Form to test file upload (according to setup) -ModuleMustBeEnabled=Module %s must be enabled +ModuleMustBeEnabled=The module/application %s must be enabled +ModuleIsEnabled=The module/application %s has been enabled IfModuleEnabled=Note: yes is effective only if module %s is enabled RemoveLock=Remove/rename file %s if it exists, to allow usage of the Update/Install tool. RestoreLock=Restore file %s, with read permission only, to disable any further use of the Update/Install tool. diff --git a/htdocs/langs/en_US/modulebuilder.lang b/htdocs/langs/en_US/modulebuilder.lang index 6b61f8c06c1..6f8fdcad996 100644 --- a/htdocs/langs/en_US/modulebuilder.lang +++ b/htdocs/langs/en_US/modulebuilder.lang @@ -126,7 +126,6 @@ UseSpecificEditorURL = Use a specific editor URL UseSpecificFamily = Use a specific family UseSpecificAuthor = Use a specific author UseSpecificVersion = Use a specific initial version -ModuleMustBeEnabled=The module/application must be enabled first IncludeRefGeneration=The reference of object must be generated automatically IncludeRefGenerationHelp=Check this if you want to include code to manage the generation automatically of the reference IncludeDocGeneration=I want to generate some documents from the object diff --git a/htdocs/modulebuilder/index.php b/htdocs/modulebuilder/index.php index babce9eba15..fba9b0d4855 100644 --- a/htdocs/modulebuilder/index.php +++ b/htdocs/modulebuilder/index.php @@ -2385,7 +2385,7 @@ if ($module == 'initmodule') print '   '; if (empty($conf->global->$const_name)) // If module is not activated { - print ''.$langs->trans("GoToApiExplorer").''; + print ''.$langs->trans("GoToApiExplorer").''; } else { print ''.$langs->trans("GoToApiExplorer").''; } diff --git a/htdocs/product/admin/product.php b/htdocs/product/admin/product.php index af158278079..6cd727d33b8 100644 --- a/htdocs/product/admin/product.php +++ b/htdocs/product/admin/product.php @@ -594,7 +594,7 @@ if (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_ // Default product price base type print ''; print ''; -print ''; print ''; From 50b27239a7dcb80b882d7998701931280a404fcd Mon Sep 17 00:00:00 2001 From: ATM john Date: Tue, 15 Dec 2020 11:34:04 +0100 Subject: [PATCH 084/743] Fix missing hook for project overview control --- htdocs/projet/class/project.class.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/htdocs/projet/class/project.class.php b/htdocs/projet/class/project.class.php index a0b6e38423b..335b06a0456 100644 --- a/htdocs/projet/class/project.class.php +++ b/htdocs/projet/class/project.class.php @@ -576,6 +576,9 @@ class Project extends CommonObject public function get_element_list($type, $tablename, $datefieldname = '', $dates = '', $datee = '', $projectkey = 'fk_projet') { // phpcs:enable + + global $hookmanager; + $elements = array(); if ($this->id <= 0) return $elements; @@ -630,6 +633,21 @@ class Project extends CommonObject if (empty($datefieldname)) return 'Error this object has no date field defined'; $sql .= " AND (".$datefieldname." <= '".$this->db->idate($datee)."' OR ".$datefieldname." IS NULL)"; } + + $hookmanager->initHooks(array('projectdao')); + $parameters = array( + 'sql'=>$sql, + 'type' => $type, + 'tablename' => $tablename, + 'datefieldname' => $datefieldname, + 'dates' => $dates, + 'datee' => $datee, + 'fk_projet' => $projectkey + ); + $reshook = $hookmanager->executeHooks('getElementList', $parameters, $object, $action); + if ($reshook > 0) $sql = $hookmanager->resPrint; + else $sql .= $hookmanager->resPrint; + if (!$sql) return -1; //print $sql; From a2bd92037183f7d6885e684e7f4d0a5d1f475caa Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 11:39:18 +0100 Subject: [PATCH 085/743] Fix translation of units --- htdocs/core/lib/product.lib.php | 24 ++++++++++------- .../product/doc/pdf_standard.modules.php | 19 +++++++++----- htdocs/langs/en_US/other.lang | 2 +- htdocs/product/class/product.class.php | 26 +++++++++---------- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/htdocs/core/lib/product.lib.php b/htdocs/core/lib/product.lib.php index 909e51dc6d7..9ce8d84d7e3 100644 --- a/htdocs/core/lib/product.lib.php +++ b/htdocs/core/lib/product.lib.php @@ -546,16 +546,17 @@ function show_stats_for_company($product, $socid) * Return translation label of a unit key. * Function kept for backward compatibility. * - * @param string $scale Scale of unit: '0', '-3', '6', ... - * @param string $measuring_style Style of unit: weight, volume,... - * @param int $unit ID of unit (rowid in llx_c_units table) - * @param int $use_short_label 1=Use short label ('g' instead of 'gram'). Short labels are not translated. - * @return string Unit string + * @param string $scale Scale of unit: '0', '-3', '6', ... + * @param string $measuring_style Style of unit: weight, volume,... + * @param int $unit ID of unit (rowid in llx_c_units table) + * @param int $use_short_label 1=Use short label ('g' instead of 'gram'). Short labels are not translated. + * @param Translate $outputlangs Language object + * @return string Unit string * @see measuringUnitString() formproduct->selectMeasuringUnits() */ -function measuring_units_string($scale = '', $measuring_style = '', $unit = 0, $use_short_label = 0) +function measuring_units_string($scale = '', $measuring_style = '', $unit = 0, $use_short_label = 0, $outputlangs = null) { - return measuringUnitString($unit, $measuring_style, $scale, $use_short_label); + return measuringUnitString($unit, $measuring_style, $scale, $use_short_label, $outputlangs); } /** @@ -565,14 +566,19 @@ function measuring_units_string($scale = '', $measuring_style = '', $unit = 0, $ * @param string $measuring_style Style of unit: 'weight', 'volume', ..., '' = 'net_measure' for option PRODUCT_ADD_NET_MEASURE * @param string $scale Scale of unit: '0', '-3', '6', ... * @param int $use_short_label 1=Use short label ('g' instead of 'gram'). Short labels are not translated. + * @param Translate $outputlangs Language object * @return string Unit string * @see formproduct->selectMeasuringUnits() */ -function measuringUnitString($unit, $measuring_style = '', $scale = '', $use_short_label = 0) +function measuringUnitString($unit, $measuring_style = '', $scale = '', $use_short_label = 0, $outputlangs = null) { global $langs, $db; global $measuring_unit_cache; + if (empty($outputlangs)) { + $outputlangs = $langs; + } + if (empty($measuring_unit_cache[$unit.'_'.$measuring_style.'_'.$scale.'_'.$use_short_label])) { require_once DOL_DOCUMENT_ROOT.'/core/class/cunits.class.php'; @@ -605,7 +611,7 @@ function measuringUnitString($unit, $measuring_style = '', $scale = '', $use_sho } else { if (is_array($measuringUnits->records) && count($measuringUnits->records) > 0) { if ($use_short_label) $labeltoreturn = $measuringUnits->records[key($measuringUnits->records)]->short_label; - else $labeltoreturn = $langs->transnoentitiesnoconv($measuringUnits->records[key($measuringUnits->records)]->label); + else $labeltoreturn = $outputlangs->transnoentitiesnoconv($measuringUnits->records[key($measuringUnits->records)]->label); } else { $labeltoreturn = ''; } diff --git a/htdocs/core/modules/product/doc/pdf_standard.modules.php b/htdocs/core/modules/product/doc/pdf_standard.modules.php index 82143123c40..dcd8419bca6 100644 --- a/htdocs/core/modules/product/doc/pdf_standard.modules.php +++ b/htdocs/core/modules/product/doc/pdf_standard.modules.php @@ -265,7 +265,7 @@ class pdf_standard extends ModelePDFProduct $tab_height = 130; $tab_height_newpage = 150; - // + // Label of product $pdf->SetFont('', 'B', $default_font_size); $pdf->writeHTMLCell(190, 3, $this->marge_gauche, $tab_top, dol_htmlentitiesbr($object->label), 0, 1); $nexY = $pdf->GetY(); @@ -276,29 +276,34 @@ class pdf_standard extends ModelePDFProduct $nexY += 5; + $outputlangs->load("other"); if ($object->weight) { - $pdf->writeHTMLCell(190, 3, $this->marge_gauche, $nexY, $langs->trans("Weight").': '.dol_htmlentitiesbr($object->weight), 0, 1); + $texttoshow = $langs->trans("Weight").': '.dol_htmlentitiesbr($object->weight); + if (isset($object->weight_units)) $texttoshow .= ' '.measuring_units_string($object->weight_units, 'weight', 0, 0, $outputlangs); + $pdf->writeHTMLCell(190, 3, $this->marge_gauche, $nexY, $texttoshow, 0, 1); $nexY = $pdf->GetY(); } if ($object->weight) { - $pdf->writeHTMLCell(190, 3, $this->marge_gauche, $nexY, $langs->trans("Length").' x '.$langs->trans("Width").' x '.$langs->trans("Height").': '.($object->length != '' ? $object->length : '?').' x '.($object->width != '' ? $object->width : '?').' x '.($object->height != '' ? $object->height : '?'), 0, 1); + $texttoshow = $langs->trans("Length").' x '.$langs->trans("Width").' x '.$langs->trans("Height").': '.($object->length != '' ? $object->length : '?').' x '.($object->width != '' ? $object->width : '?').' x '.($object->height != '' ? $object->height : '?'); + $pdf->writeHTMLCell(190, 3, $this->marge_gauche, $nexY, $texttoshow, 0, 1); $nexY = $pdf->GetY(); } if ($object->surface) { - $pdf->writeHTMLCell(190, 3, $this->marge_gauche, $nexY, $langs->trans("Area").': '.dol_htmlentitiesbr($object->surface), 0, 1); + $texttoshow = $langs->trans("Area").': '.dol_htmlentitiesbr($object->surface); + $pdf->writeHTMLCell(190, 3, $this->marge_gauche, $nexY, $texttoshow, 0, 1); $nexY = $pdf->GetY(); } if ($object->volume) { - $pdf->writeHTMLCell(190, 3, $this->marge_gauche, $nexY, $langs->trans("Volume").': '.dol_htmlentitiesbr($object->volume), 0, 1); + $texttoshow = $langs->trans("Volume").': '.dol_htmlentitiesbr($object->volume); + $pdf->writeHTMLCell(190, 3, $this->marge_gauche, $nexY, $texttoshow, 0, 1); $nexY = $pdf->GetY(); } - - // Affiche notes + // Show notes // TODO There is no public note on product yet $notetoshow = empty($object->note_public) ? '' : $object->note_public; if (!empty($conf->global->MAIN_ADD_SALE_REP_SIGNATURE_IN_NOTE)) diff --git a/htdocs/langs/en_US/other.lang b/htdocs/langs/en_US/other.lang index 7495291cf3f..011352f1dcd 100644 --- a/htdocs/langs/en_US/other.lang +++ b/htdocs/langs/en_US/other.lang @@ -138,7 +138,7 @@ Right=Right CalculatedWeight=Calculated weight CalculatedVolume=Calculated volume Weight=Weight -WeightUnitton=tonne +WeightUnitton=ton WeightUnitkg=kg WeightUnitg=g WeightUnitmg=mg diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index bcf32cdce26..7d0d82ec147 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -280,20 +280,20 @@ class Product extends CommonObject //! Metric of products public $weight; - public $weight_units; + public $weight_units; // scale -3, 0, 3, 6 public $length; - public $length_units; + public $length_units; // scale -3, 0, 3, 6 public $width; - public $width_units; + public $width_units; // scale -3, 0, 3, 6 public $height; - public $height_units; + public $height_units; // scale -3, 0, 3, 6 public $surface; - public $surface_units; + public $surface_units; // scale -3, 0, 3, 6 public $volume; - public $volume_units; + public $volume_units; // scale -3, 0, 3, 6 public $net_measure; - public $net_measure_units; + public $net_measure_units; // scale -3, 0, 3, 6 public $accountancy_code_sell; public $accountancy_code_sell_intra; @@ -5387,19 +5387,19 @@ class Product extends CommonObject $this->date_modification = $now; $this->weight = 4; - $this->weight_unit = 1; + $this->weight_units = 3; $this->length = 5; - $this->length_unit = 1; + $this->length_units = 1; $this->width = 6; - $this->width_unit = 0; + $this->width_units = 0; $this->height = null; - $this->height_unit = null; + $this->height_units = null; $this->surface = 30; - $this->surface_unit = 0; + $this->surface_units = 0; $this->volume = 300; - $this->volume_unit = 0; + $this->volume_units = 0; $this->barcode = -1; // Create barcode automatically } From cee63335a20b3bb52e46129318454a406e42cf8a Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 13:38:21 +0100 Subject: [PATCH 086/743] FIX #15754 Mor complete PR --- htdocs/accountancy/bookkeeping/list.php | 1 + .../accountancy/bookkeeping/listbyaccount.php | 41 +++++++++++----- .../bookkeeping/listbysubaccount.php | 49 ++++++++++++++++--- htdocs/langs/en_US/accountancy.lang | 4 +- htdocs/theme/eldy/global.inc.php | 7 +++ htdocs/theme/md/style.css.php | 7 +++ 6 files changed, 89 insertions(+), 20 deletions(-) diff --git a/htdocs/accountancy/bookkeeping/list.php b/htdocs/accountancy/bookkeeping/list.php index 92de49d66ed..b3cec7710a4 100644 --- a/htdocs/accountancy/bookkeeping/list.php +++ b/htdocs/accountancy/bookkeeping/list.php @@ -642,6 +642,7 @@ if (empty($reshook)) { $newcardbutton .= dolGetButtonTitle($langs->trans('ViewFlatList'), '', 'fa fa-list paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/list.php?'.$param, '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); $newcardbutton .= dolGetButtonTitle($langs->trans('GroupByAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbyaccount.php?'.$param, '', 1, array('morecss' => 'marginleftonly')); + $newcardbutton .= dolGetButtonTitle($langs->trans('GroupBySubAccountAccounting'), '', 'fa fa-align-left vmirror paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbysubaccount.php', '', 1, array('morecss' => 'marginleftonly')); $url = './card.php?action=create'; if (!empty($socid)) $url .= '&socid='.$socid; diff --git a/htdocs/accountancy/bookkeeping/listbyaccount.php b/htdocs/accountancy/bookkeeping/listbyaccount.php index 18661f8a887..7b8467f60bd 100644 --- a/htdocs/accountancy/bookkeeping/listbyaccount.php +++ b/htdocs/accountancy/bookkeeping/listbyaccount.php @@ -39,8 +39,14 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; $langs->loadLangs(array("accountancy", "compta")); $action = GETPOST('action', 'aZ09'); -$search_date_start = dol_mktime(0, 0, 0, GETPOST('search_date_startmonth', 'int'), GETPOST('search_date_startday', 'int'), GETPOST('search_date_startyear', 'int')); -$search_date_end = dol_mktime(0, 0, 0, GETPOST('search_date_endmonth', 'int'), GETPOST('search_date_endday', 'int'), GETPOST('search_date_endyear', 'int')); +$search_date_startyear = GETPOST('search_date_startyear', 'int'); +$search_date_startmonth = GETPOST('search_date_startmonth', 'int'); +$search_date_startday = GETPOST('search_date_startday', 'int'); +$search_date_endyear = GETPOST('search_date_endyear', 'int'); +$search_date_endmonth = GETPOST('search_date_endmonth', 'int'); +$search_date_endday = GETPOST('search_date_endday', 'int'); +$search_date_start = dol_mktime(0, 0, 0, $search_date_startmonth, $search_date_startday, $search_date_startyear); +$search_date_end = dol_mktime(0, 0, 0, $search_date_endmonth, $search_date_endday, $search_date_endyear); $search_doc_date = dol_mktime(0, 0, 0, GETPOST('doc_datemonth', 'int'), GETPOST('doc_dateday', 'int'), GETPOST('doc_dateyear', 'int')); $search_accountancy_code = GETPOST("search_accountancy_code"); @@ -126,9 +132,24 @@ $arrayfields = array( if (empty($conf->global->ACCOUNTING_ENABLE_LETTERING)) unset($arrayfields['t.lettering_code']); +if ($search_date_start && empty($search_date_startyear)) { + $tmparray = dol_getdate($search_date_start); + $search_date_startyear = $tmparray['year']; + $search_date_startmonth = $tmparray['mon']; + $search_date_startday = $tmparray['mday']; +} +if ($search_date_end && empty($search_date_endyear)) { + $tmparray = dol_getdate($search_date_end); + $search_date_endyear = $tmparray['year']; + $search_date_endmonth = $tmparray['mon']; + $search_date_endday = $tmparray['mday']; +} + + /* * Action */ + if (GETPOST('cancel', 'alpha')) { $action = 'list'; $massaction = ''; } if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction = ''; } @@ -172,11 +193,11 @@ if (empty($reshook)) if (!empty($search_date_start)) { $filter['t.doc_date>='] = $search_date_start; - $param .= '&search_date_startmonth='.GETPOST('search_date_startmonth', 'int').'&search_date_startday='.GETPOST('search_date_startday', 'int').'&search_date_startyear='.GETPOST('search_date_startyear', 'int'); + $param .= '&search_date_startmonth='.$search_date_startmonth.'&search_date_startday='.$search_date_startday.'&search_date_startyear='.$search_date_startyear; } if (!empty($search_date_end)) { $filter['t.doc_date<='] = $search_date_end; - $param .= '&search_date_endmonth='.GETPOST('search_date_endmonth', 'int').'&search_date_endday='.GETPOST('search_date_endday', 'int').'&search_date_endyear='.GETPOST('search_date_endyear', 'int'); + $param .= '&search_date_endmonth='.$search_date_endmonth.'&search_date_endday='.$search_date_endday.'&search_date_endyear='.$search_date_endyear; } if (!empty($search_doc_date)) { $filter['t.doc_date'] = $search_doc_date; @@ -242,7 +263,7 @@ if ($action == 'delbookkeeping' && $user->rights->accounting->mouvements->suppri } // Make a redirect to avoid to launch the delete later after a back button - header("Location: listbyaccount.php".($param ? '?'.$param : '')); + header("Location: ".$_SERVER["PHP_SELF"].($param ? '?'.$param : '')); exit; } } @@ -267,7 +288,7 @@ if ($action == 'delbookkeepingyearconfirm' && $user->rights->accounting->mouveme } // Make a redirect to avoid to launch the delete later after a back button - header("Location: listbyaccount.php".($param ? '?'.$param : '')); + header("Location: ".$_SERVER["PHP_SELF"].($param ? '?'.$param : '')); exit; } else { setEventMessages("NoRecordDeleted", null, 'warnings'); @@ -284,7 +305,7 @@ if ($action == 'delmouvconfirm' && $user->rights->accounting->mouvements->suppri setEventMessages($langs->trans("RecordDeleted"), null, 'mesgs'); } - header("Location: listbyaccount.php?noreset=1".($param ? '&'.$param : '')); + header("Location: ".$_SERVER["PHP_SELF"]."?noreset=1".($param ? '&'.$param : '')); exit; } } @@ -303,7 +324,6 @@ $title_page = $langs->trans("Operations").' - '.$langs->trans("VueByAccountAccou llxHeader('', $title_page); - // List $nbtotalofrecords = ''; if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) { @@ -380,9 +400,8 @@ $parameters = array(); $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook if (empty($reshook)) { $newcardbutton = dolGetButtonTitle($langs->trans('ViewFlatList'), '', 'fa fa-list paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/list.php?'.$param); - $newcardbutton .= dolGetButtonTitle($langs->trans('VueByAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbyaccount.php?'.$param, '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); - - $newcardbutton .= '   '; + $newcardbutton .= dolGetButtonTitle($langs->trans('GroupByAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbyaccount.php?'.$param, '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); + $newcardbutton .= dolGetButtonTitle($langs->trans('GroupBySubAccountAccounting'), '', 'fa fa-align-left vmirror paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbysubaccount.php', '', 1, array('morecss' => 'marginleftonly')); $newcardbutton .= dolGetButtonTitle($langs->trans('NewAccountingMvt'), '', 'fa fa-plus-circle paddingleft', DOL_URL_ROOT.'/accountancy/bookkeeping/card.php?action=create'); } diff --git a/htdocs/accountancy/bookkeeping/listbysubaccount.php b/htdocs/accountancy/bookkeeping/listbysubaccount.php index 3bc0351b09b..48c449df43e 100644 --- a/htdocs/accountancy/bookkeeping/listbysubaccount.php +++ b/htdocs/accountancy/bookkeeping/listbysubaccount.php @@ -39,8 +39,14 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; $langs->loadLangs(array("accountancy", "compta")); $action = GETPOST('action', 'aZ09'); -$search_date_start = dol_mktime(0, 0, 0, GETPOST('search_date_startmonth', 'int'), GETPOST('search_date_startday', 'int'), GETPOST('search_date_startyear', 'int')); -$search_date_end = dol_mktime(0, 0, 0, GETPOST('search_date_endmonth', 'int'), GETPOST('search_date_endday', 'int'), GETPOST('search_date_endyear', 'int')); +$search_date_startyear = GETPOST('search_date_startyear', 'int'); +$search_date_startmonth = GETPOST('search_date_startmonth', 'int'); +$search_date_startday = GETPOST('search_date_startday', 'int'); +$search_date_endyear = GETPOST('search_date_endyear', 'int'); +$search_date_endmonth = GETPOST('search_date_endmonth', 'int'); +$search_date_endday = GETPOST('search_date_endday', 'int'); +$search_date_start = dol_mktime(0, 0, 0, $search_date_startmonth, $search_date_startday, $search_date_startyear); +$search_date_end = dol_mktime(0, 0, 0, $search_date_endmonth, $search_date_endday, $search_date_endyear); $search_doc_date = dol_mktime(0, 0, 0, GETPOST('doc_datemonth', 'int'), GETPOST('doc_dateday', 'int'), GETPOST('doc_dateyear', 'int')); $search_accountancy_code = GETPOST("search_accountancy_code"); @@ -126,6 +132,20 @@ $arrayfields = array( if (empty($conf->global->ACCOUNTING_ENABLE_LETTERING)) unset($arrayfields['t.lettering_code']); +if ($search_date_start && empty($search_date_startyear)) { + $tmparray = dol_getdate($search_date_start); + $search_date_startyear = $tmparray['year']; + $search_date_startmonth = $tmparray['mon']; + $search_date_startday = $tmparray['mday']; +} +if ($search_date_end && empty($search_date_endyear)) { + $tmparray = dol_getdate($search_date_end); + $search_date_endyear = $tmparray['year']; + $search_date_endmonth = $tmparray['mon']; + $search_date_endday = $tmparray['mday']; +} + + /* * Action */ @@ -172,11 +192,11 @@ if (empty($reshook)) if (!empty($search_date_start)) { $filter['t.doc_date>='] = $search_date_start; - $param .= '&search_date_startmonth='.GETPOST('search_date_startmonth', 'int').'&search_date_startday='.GETPOST('search_date_startday', 'int').'&search_date_startyear='.GETPOST('search_date_startyear', 'int'); + $param .= '&search_date_startmonth='.$search_date_startmonth.'&search_date_startday='.$search_date_startday.'&search_date_startyear='.$search_date_startyear; } if (!empty($search_date_end)) { $filter['t.doc_date<='] = $search_date_end; - $param .= '&search_date_endmonth='.GETPOST('search_date_endmonth', 'int').'&search_date_endday='.GETPOST('search_date_endday', 'int').'&search_date_endyear='.GETPOST('search_date_endyear', 'int'); + $param .= '&search_date_endmonth='.$search_date_endmonth.'&search_date_endday='.$search_date_endday.'&search_date_endyear='.$search_date_endyear; } if (!empty($search_doc_date)) { $filter['t.doc_date'] = $search_doc_date; @@ -242,7 +262,7 @@ if ($action == 'delbookkeeping' && $user->rights->accounting->mouvements->suppri } // Make a redirect to avoid to launch the delete later after a back button - header("Location: listbyaccount.php".($param ? '?'.$param : '')); + header("Location: ".$_SERVER["PHP_SELF"].($param ? '?'.$param : '')); exit; } } @@ -380,7 +400,10 @@ print ''; $parameters = array(); $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been modified by hook if (empty($reshook)) { - $newcardbutton = dolGetButtonTitle($langs->trans('ViewAccountList'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbyaccount.php', '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); + $newcardbutton = dolGetButtonTitle($langs->trans('ViewFlatList'), '', 'fa fa-list paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/list.php?'.$param); + $newcardbutton .= dolGetButtonTitle($langs->trans('GroupByAccountAccounting'), '', 'fa fa-stream paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbyaccount.php', '', 1, array('morecss' => 'marginleftonly')); + $newcardbutton .= dolGetButtonTitle($langs->trans('GroupBySubAccountAccounting'), '', 'fa fa-align-left vmirror paddingleft imgforviewmode', DOL_URL_ROOT.'/accountancy/bookkeeping/listbysubaccount.php', '', 1, array('morecss' => 'marginleftonly btnTitleSelected')); + $newcardbutton .= dolGetButtonTitle($langs->trans('NewAccountingMvt'), '', 'fa fa-plus-circle paddingleft', DOL_URL_ROOT.'/accountancy/bookkeeping/card.php?action=create'); } @@ -389,6 +412,8 @@ if ($limit > 0 && $limit != $conf->liste_limit) $param .= '&limit='.urlencode($l print_barre_liste($title_page, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $result, $nbtotalofrecords, 'title_accountancy', 0, $newcardbutton, '', $limit); +print info_admin($langs->trans("WarningRecordWithoutSubledgerAreExcluded")); + $varpage = empty($contextpage) ? $_SERVER["PHP_SELF"] : $contextpage; $selectedfields = $form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage); // This also change content of $arrayfields if ($massactionbutton) $selectedfields .= $form->showCheckAddButtons('checkforselect', 1); @@ -554,8 +579,16 @@ while ($i < min($num, $limit)) // Show the break account print ""; print ''; print ''; diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 41a4cfa59d5..2a53a171962 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -207,7 +207,8 @@ JournalLabel=Journal label NumPiece=Piece number TransactionNumShort=Num. transaction AccountingCategory=Personalized groups -GroupByAccountAccounting=Group by accounting account +GroupByAccountAccounting=Group by general ledger account +GroupBySubAccountAccounting=Group by subledger account AccountingAccountGroupsDesc=You can define here some groups of accounting account. They will be used for personalized accounting reports. ByAccounts=By accounts ByPredefinedAccountGroups=By predefined groups @@ -294,6 +295,7 @@ Accounted=Accounted in ledger NotYetAccounted=Not yet accounted in ledger ShowTutorial=Show Tutorial NotReconciled=Not reconciled +WarningRecordWithoutSubledgerAreExcluded=Warning, all operations without subledger account defined are filtered and excluded from this view ## Admin BindingOptions=Binding options diff --git a/htdocs/theme/eldy/global.inc.php b/htdocs/theme/eldy/global.inc.php index 24751dfb9d7..1dc8f27b644 100644 --- a/htdocs/theme/eldy/global.inc.php +++ b/htdocs/theme/eldy/global.inc.php @@ -396,6 +396,13 @@ input.pageplusone { color: #000; } +.vmirror { + transform: scale(1, -1); +} +.hmirror { + transform: scale(-1, 1); +} + select:invalid { color: gray; } diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php index f1084f5a537..7b4ecf08e8e 100644 --- a/htdocs/theme/md/style.css.php +++ b/htdocs/theme/md/style.css.php @@ -528,6 +528,13 @@ input.pageplusone { color: #000; } +.vmirror { + transform: scale(1, -1); +} +.hmirror { + transform: scale(-1, 1); +} + select:invalid { color: gray; } From 479e742d0c219c8c09eee2d987374f3ba5b27bb5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 13:46:13 +0100 Subject: [PATCH 087/743] FIX #15753 --- htdocs/compta/cashcontrol/report.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/htdocs/compta/cashcontrol/report.php b/htdocs/compta/cashcontrol/report.php index 515067900e3..04cdea52f7f 100644 --- a/htdocs/compta/cashcontrol/report.php +++ b/htdocs/compta/cashcontrol/report.php @@ -133,7 +133,8 @@ if ($resql) { print "

"; if ($cashcontrol->status != $cashcontrol::STATUS_DRAFT) print $langs->trans("CashControl")." ".$cashcontrol->id; else print $langs->trans("CashControl")." - ".$langs->trans("Draft"); - print "
".$langs->trans("DateCreationShort").": ".dol_print_date($cashcontrol->date_creation, 'dayhour')."

"; + print "
".$langs->trans("DateCreationShort").": ".dol_print_date($cashcontrol->date_creation, 'dayhour'); + print ""; $invoicetmp = new Facture($db); @@ -156,8 +157,6 @@ if ($resql) { print_liste_field_titre($arrayfields['b.credit']['label'], $_SERVER['PHP_SELF'], 'b.amount', '', $param, '', $sortfield, $sortorder, 'right '); print "\n"; - $posconciliatecol = 0; - // Loop on each record $sign = 1; $cash = $bank = $cheque = $other = 0; @@ -263,7 +262,7 @@ if ($resql) { print "
'; print $langs->trans('CronDtStart').""; - if (!empty($object->datestart)) {print dol_print_date($object->datestart, 'dayhoursec'); } + if (!empty($object->datestart)) {print $form->textwithpicto(dol_print_date($object->datestart, 'dayhoursec'), $langs->trans("CurrentTimeZone")); } print "
"; print $langs->trans('CronDtEnd').""; - if (!empty($object->dateend)) {print dol_print_date($object->dateend, 'dayhoursec'); } + if (!empty($object->dateend)) {print $form->textwithpicto(dol_print_date($object->dateend, 'dayhoursec'), $langs->trans("CurrentTimeZone")); } print "
"; @@ -653,7 +653,7 @@ if (($action == "create") || ($action == "edit")) print ' ('.$langs->trans('CronFrom').')'; print ""; if (!$object->status) print $langs->trans("Disabled"); - elseif (!empty($object->datenextrun)) { print img_picto('', 'object_calendarday').' '.dol_print_date($object->datenextrun, 'dayhoursec'); } else { print $langs->trans('CronNone'); } + elseif (!empty($object->datenextrun)) { print img_picto('', 'object_calendarday').' '.$form->textwithpicto(dol_print_date($object->datenextrun, 'dayhoursec'), $langs->trans("CurrentTimeZone")); } else { print $langs->trans('CronNone'); } if ($object->status == Cronjob::STATUS_ENABLED) { if ($object->maxrun && $object->nbrun >= $object->maxrun) print img_warning($langs->trans("MaxRunReached")); @@ -672,12 +672,12 @@ if (($action == "create") || ($action == "edit")) print '
'; print $langs->trans('CronDtLastLaunch').""; - if (!empty($object->datelastrun)) {print dol_print_date($object->datelastrun, 'dayhoursec'); } else {print $langs->trans('CronNone'); } + if (!empty($object->datelastrun)) {print $form->textwithpicto(dol_print_date($object->datelastrun, 'dayhoursec'), $langs->trans("CurrentTimeZone")); } else {print $langs->trans('CronNone'); } print "
'; print $langs->trans('CronDtLastResult').""; - if (!empty($object->datelastresult)) {print dol_print_date($object->datelastresult, 'dayhoursec'); } else {print $langs->trans('CronNone'); } + if (!empty($object->datelastresult)) {print $form->textwithpicto(dol_print_date($object->datelastresult, 'dayhoursec'), $langs->trans("CurrentTimeZone")); } else {print $langs->trans('CronNone'); } print "
'; From f2a4f880ca8fbdf89014fcaa35097f2a208f3f54 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 10:49:07 +0100 Subject: [PATCH 080/743] Fix warning --- htdocs/core/class/dolgraph.class.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/htdocs/core/class/dolgraph.class.php b/htdocs/core/class/dolgraph.class.php index 36638be33c1..4495c5ae50d 100644 --- a/htdocs/core/class/dolgraph.class.php +++ b/htdocs/core/class/dolgraph.class.php @@ -1215,6 +1215,7 @@ class DolGraph } else { $textoflegend = $this->Legend[$i]; } + if ($usecolorvariantforgroupby) { $newcolor = $this->datacolor[$arrayofgroupslegend[$i]['stacknum']]; // If we change the stack @@ -1243,6 +1244,7 @@ class DolGraph $bordercolor = 'rgb(' . $newcolor[0] . ', ' . $newcolor[1] . ', ' . $newcolor[2] . ')'; } else { // We do not use a 'group by' if ($isfunnel) { + $bordercolor == 'null'; if (is_array($this->datacolor[$i])) { $color = 'rgb(' . $this->datacolor[$i][0] . ', ' . $this->datacolor[$i][1] . ', ' . $this->datacolor[$i][2] . ', 0.9)'; // If datacolor is array(R, G, B) } else { @@ -1262,9 +1264,9 @@ class DolGraph if (strpos($tmp, '-') !== false) $bordercolor = '#' . str_replace('-', '', $tmp); // If $val is '-123' else $bordercolor = 'null'; // If $val is '123' or '#123' } - $bordercolor == 'null' ? "'rgba(0,0,0,0.2)'" : "'" . $bordercolor . "'"; } } + $bordercolor == 'null' ? "'rgba(0,0,0,0.2)'" : "'" . $bordercolor . "'"; } else { $color = 'rgb('.$this->datacolor[$i][0].', '.$this->datacolor[$i][1].', '.$this->datacolor[$i][2].', 0.9)'; $bordercolor = $color; From 32741dd6f67c080c753d321ef0329ec901941212 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 10:51:35 +0100 Subject: [PATCH 081/743] Fix warning --- .../supplier_proposal/class/supplier_proposal.class.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/htdocs/supplier_proposal/class/supplier_proposal.class.php b/htdocs/supplier_proposal/class/supplier_proposal.class.php index b022c76d0d8..cf8e712a103 100644 --- a/htdocs/supplier_proposal/class/supplier_proposal.class.php +++ b/htdocs/supplier_proposal/class/supplier_proposal.class.php @@ -547,8 +547,8 @@ class SupplierProposal extends CommonObject $this->line->tva_tx = $txtva; $this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0); $this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0); - $this->line->localtax1_type = $localtaxes_type[0]; - $this->line->localtax2_type = $localtaxes_type[2]; + $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0]; + $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2]; $this->line->fk_product = $fk_product; $this->line->remise_percent = $remise_percent; $this->line->subprice = $pu_ht; @@ -736,8 +736,8 @@ class SupplierProposal extends CommonObject $this->line->tva_tx = $txtva; $this->line->localtax1_tx = $txlocaltax1; $this->line->localtax2_tx = $txlocaltax2; - $this->line->localtax1_type = $localtaxes_type[0]; - $this->line->localtax2_type = $localtaxes_type[2]; + $this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0]; + $this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2]; $this->line->remise_percent = $remise_percent; $this->line->subprice = $pu; $this->line->info_bits = $info_bits; From 31693eed21ffa7fd8784e42527cb76ec7a0910d3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 11:10:35 +0100 Subject: [PATCH 082/743] Fix setup was not clear --- htdocs/langs/am_ET/products.lang | 2 +- htdocs/langs/en_US/admin.lang | 1 + htdocs/langs/en_US/products.lang | 3 +- htdocs/product/admin/product.php | 53 ++++++++++++++++++-------------- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/htdocs/langs/am_ET/products.lang b/htdocs/langs/am_ET/products.lang index 97db059594f..18af7f2f252 100644 --- a/htdocs/langs/am_ET/products.lang +++ b/htdocs/langs/am_ET/products.lang @@ -108,7 +108,7 @@ FillWithLastServiceDates=Fill with last service line dates MultiPricesAbility=Multiple price segments per product/service (each customer is in one price segment) MultiPricesNumPrices=Number of prices DefaultPriceType=Base of prices per default (with versus without tax) when adding new sale prices -AssociatedProductsAbility=Activate kits (virtual products) +AssociatedProductsAbility=Enable Kits (set of other products) AssociatedProducts=Kits AssociatedProductsNumber=Number of products composing this kit ParentProductsNumber=Number of parent packaging product diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang index b6784b75835..d314c44aa0e 100644 --- a/htdocs/langs/en_US/admin.lang +++ b/htdocs/langs/en_US/admin.lang @@ -56,6 +56,7 @@ GUISetup=Display SetupArea=Setup UploadNewTemplate=Upload new template(s) FormToTestFileUploadForm=Form to test file upload (according to setup) +ModuleMustBeEnabled=Module %s must be enabled IfModuleEnabled=Note: yes is effective only if module %s is enabled RemoveLock=Remove/rename file %s if it exists, to allow usage of the Update/Install tool. RestoreLock=Restore file %s, with read permission only, to disable any further use of the Update/Install tool. diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index 8c30b2e02c1..f0c4a5362b8 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -108,7 +108,8 @@ FillWithLastServiceDates=Fill with last service line dates MultiPricesAbility=Multiple price segments per product/service (each customer is in one price segment) MultiPricesNumPrices=Number of prices DefaultPriceType=Base of prices per default (with versus without tax) when adding new sale prices -AssociatedProductsAbility=Activate kits (virtual products) +AssociatedProductsAbility=Enable Kits (set of other products) +VariantsAbility=Enable Variants (variations of products, for example color, size) AssociatedProducts=Kits AssociatedProductsNumber=Number of products composing this kit ParentProductsNumber=Number of parent packaging product diff --git a/htdocs/product/admin/product.php b/htdocs/product/admin/product.php index 16c3c849ce6..af158278079 100644 --- a/htdocs/product/admin/product.php +++ b/htdocs/product/admin/product.php @@ -127,8 +127,8 @@ if ($action == 'other') $value = GETPOST('price_base_type', 'alpha'); $res = dolibarr_set_const($db, "PRODUCT_PRICE_BASE_TYPE", $value, 'chaine', 0, '', $conf->entity); - $value = GETPOST('PRODUIT_SOUSPRODUITS', 'alpha'); - $res = dolibarr_set_const($db, "PRODUIT_SOUSPRODUITS", $value, 'chaine', 0, '', $conf->entity); + /*$value = GETPOST('PRODUIT_SOUSPRODUITS', 'alpha'); + $res = dolibarr_set_const($db, "PRODUIT_SOUSPRODUITS", $value, 'chaine', 0, '', $conf->entity);*/ $value = GETPOST('activate_viewProdDescInForm', 'alpha'); $res = dolibarr_set_const($db, "PRODUIT_DESC_IN_FORM", $value, 'chaine', 0, '', $conf->entity); @@ -312,7 +312,6 @@ print ' '.$langs->trans("Status").''.$langs->trans("ShortInfo").'
'.$modCodeProduct->name.''.$modCodeProduct->info($langs).''.$langs->trans("Value").'
'.$langs->trans("AssociatedProductsAbility").''; +print ajax_constantonoff("PRODUIT_SOUSPRODUITS", array(), $conf->entity, 0, 0, 1, 0); +//print $form->selectyesno("PRODUIT_SOUSPRODUITS", $conf->global->PRODUIT_SOUSPRODUITS, 1); +print '
'.$langs->trans("VariantsAbility").''; +//print ajax_constantonoff("PRODUIT_SOUSPRODUITS", array(), $conf->entity, 0, 0, 1, 0); +//print $form->selectyesno("PRODUIT_SOUSPRODUITS", $conf->global->PRODUIT_SOUSPRODUITS, 1); +if (empty($conf->variants->enabled)) { + print ''.$langs->trans("ModuleMustBeEnabled", $langs->transnoentitiesnoconv("Module610Name")).''; +} else { + print yn(1).' ('.$langs->trans("ModuleIsEnabled", $langs->transnoentitiesnoconv("Module610Name")).')'; +} +print '
'.$langs->trans("DefaultPriceType").''; @@ -583,16 +599,7 @@ print $form->selectPriceBaseType($conf->global->PRODUCT_PRICE_BASE_TYPE, "price_ print '
'.$langs->trans("AssociatedProductsAbility").''; -print $form->selectyesno("PRODUIT_SOUSPRODUITS", $conf->global->PRODUIT_SOUSPRODUITS, 1); -print '
'.$form->textwithpicto($langs->trans("UseSearchToSelectProduct"), $langs->trans('UseSearchToSelectProductTooltip'), 1).'
'.$langs->trans("DefaultPriceType").''; +print ''; print $form->selectPriceBaseType($conf->global->PRODUCT_PRICE_BASE_TYPE, "price_base_type"); print '
'; - if ($line->subledger_account != "" && $line->subledger_account != '-1') print length_accounta($line->subledger_account).' : '.$object->get_compte_desc($line->numero_compte); - else print ''.$langs->trans("Unknown").''; + if ($line->subledger_account != "" && $line->subledger_account != '-1') { + print $object->get_compte_desc($line->numero_compte).' : '.length_accounta($line->subledger_account); + } else { + // Should not happen: subledger account must be null or a non empty value + print ''.$langs->trans("Unknown"); + if ($line->subledger_label) print ' ('.$line->subledger_label.')'; + $htmltext = 'EmptyStringForSubledgerAccountButSubledgerLabelDefined'; + print $form->textwithpicto('', $htmltext); + print ''; + } print '
"; //$cash = $amountpertype['LIQ'] + $cashcontrol->opening; - $cash = $cash + $cashcontrol->opening; + $cash = price2num($cash + $cashcontrol->opening, 'MT'); print "

"; print $langs->trans("Cash").": ".price($cash); From 1f6f4742d56ea3d708e82df8846397305c8c577d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 14:01:25 +0100 Subject: [PATCH 088/743] Trans --- htdocs/langs/en_US/companies.lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/langs/en_US/companies.lang b/htdocs/langs/en_US/companies.lang index 8a18520c2fc..99bed13179f 100644 --- a/htdocs/langs/en_US/companies.lang +++ b/htdocs/langs/en_US/companies.lang @@ -358,7 +358,7 @@ VATIntraCheckableOnEUSite=Check the intra-Community VAT ID on the European Commi VATIntraManualCheck=You can also check manually on the European Commission website %s ErrorVATCheckMS_UNAVAILABLE=Check not possible. Check service is not provided by the member state (%s). NorProspectNorCustomer=Not prospect, nor customer -JuridicalStatus=Legal Entity Type +JuridicalStatus=Business entity type Workforce=Workforce Staff=Employees ProspectLevelShort=Potential From 37fe5bb3efc4aa986fa97ef22b37fb40bac3cd05 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 15:18:01 +0100 Subject: [PATCH 089/743] Solve blocking feature. Can increase stock of a Kit without changing subproduct stock. --- htdocs/langs/en_US/main.lang | 1 + htdocs/langs/en_US/stocks.lang | 1 + htdocs/product/class/product.class.php | 16 ++-- htdocs/product/composition/card.php | 18 ++--- .../stock/class/mouvementstock.class.php | 41 ++++++----- htdocs/product/stock/product.php | 73 ++++++++++++------- htdocs/product/stock/stockatdate.php | 10 ++- .../product/stock/tpl/stockcorrection.tpl.php | 15 +++- .../product/stock/tpl/stocktransfer.tpl.php | 5 +- 9 files changed, 109 insertions(+), 71 deletions(-) diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index c32e4d01ddd..73c9964000e 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -29,6 +29,7 @@ AvailableVariables=Available substitution variables NoTranslation=No translation Translation=Translation EmptySearchString=Enter non empty search criterias +EnterADateCriteria=Enter a date criteria NoRecordFound=No record found NoRecordDeleted=No record deleted NotEnoughDataYet=Not enough data diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index d2c1b2cb382..1b54e53d6de 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -240,3 +240,4 @@ InventoryRealQtyHelp=Set value to 0 to reset qty
Keep field empty, or remove UpdateByScaning=Update by scaning UpdateByScaningProductBarcode=Update by scan (product barcode) UpdateByScaningLot=Update by scan (lot|serial barcode) +DisableStockChangeOfSubProduct=Deactivate the stock change for all the subproducts of this Kit during this movement. \ No newline at end of file diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 7d0d82ec147..e1b25b9e3b7 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -4229,9 +4229,9 @@ class Product extends CommonObject // phpcs:enable $this->res = array(); if (isset($this->sousprods) && is_array($this->sousprods)) { - foreach ($this->sousprods as $prod_name => $desc_product) - { - if (is_array($desc_product)) { $this->fetch_prod_arbo($desc_product, "", $multiply, 1, $this->id); + foreach ($this->sousprods as $prod_name => $desc_product) { + if (is_array($desc_product)) { + $this->fetch_prod_arbo($desc_product, "", $multiply, 1, $this->id); } } } @@ -4777,9 +4777,10 @@ class Product extends CommonObject * @param string $inventorycode Inventory code * @param string $origin_element Origin element type * @param int $origin_id Origin id of element + * @param int $disablestockchangeforsubproduct Disable stock change for sub-products of kit (usefull only if product is a subproduct) * @return int <0 if KO, >0 if OK */ - public function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label = '', $price = 0, $inventorycode = '', $origin_element = '', $origin_id = null) + public function correct_stock($user, $id_entrepot, $nbpiece, $movement, $label = '', $price = 0, $inventorycode = '', $origin_element = '', $origin_id = null, $disablestockchangeforsubproduct = 0) { // phpcs:enable if ($id_entrepot) { @@ -4792,7 +4793,7 @@ class Product extends CommonObject $movementstock = new MouvementStock($this->db); $movementstock->setOrigin($origin_element, $origin_id); // Set ->origin and ->origin->id - $result = $movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode); + $result = $movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode, '', '', '', '', false, 0, $disablestockchangeforsubproduct); if ($result >= 0) { $this->db->commit(); @@ -4823,9 +4824,10 @@ class Product extends CommonObject * @param string $inventorycode Inventory code * @param string $origin_element Origin element type * @param int $origin_id Origin id of element + * @param int $disablestockchangeforsubproduct Disable stock change for sub-products of kit (usefull only if product is a subproduct) * @return int <0 if KO, >0 if OK */ - public function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label = '', $price = 0, $dlc = '', $dluo = '', $lot = '', $inventorycode = '', $origin_element = '', $origin_id = null) + public function correct_stock_batch($user, $id_entrepot, $nbpiece, $movement, $label = '', $price = 0, $dlc = '', $dluo = '', $lot = '', $inventorycode = '', $origin_element = '', $origin_id = null, $disablestockchangeforsubproduct = 0) { // phpcs:enable if ($id_entrepot) { @@ -4838,7 +4840,7 @@ class Product extends CommonObject $movementstock = new MouvementStock($this->db); $movementstock->setOrigin($origin_element, $origin_id); - $result = $movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode, '', $dlc, $dluo, $lot); + $result = $movementstock->_create($user, $this->id, $id_entrepot, $op[$movement], $movement, $price, $label, $inventorycode, '', $dlc, $dluo, $lot, false, 0, $disablestockchangeforsubproduct); if ($result >= 0) { $this->db->commit(); diff --git a/htdocs/product/composition/card.php b/htdocs/product/composition/card.php index 13ef4582d20..8459fa3e479 100644 --- a/htdocs/product/composition/card.php +++ b/htdocs/product/composition/card.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2017 Laurent Destailleur + * Copyright (C) 2004-2020 Laurent Destailleur * Copyright (C) 2005 Eric Seigne * Copyright (C) 2005-2018 Regis Houssin * Copyright (C) 2006 Andre Cianfarani @@ -137,7 +137,7 @@ if ($action == 'search') $current_lang = $langs->getDefaultLang(); $sql = 'SELECT DISTINCT p.rowid, p.ref, p.label, p.fk_product_type as type, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,'; - $sql .= ' p.fk_product_type, p.tms as datem'; + $sql .= ' p.fk_product_type, p.tms as datem, p.tobatch'; if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= ', pl.label as labelm, pl.description as descriptionm'; $sql .= ' FROM '.MAIN_DB_PREFIX.'product as p'; $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_product as cp ON p.rowid = cp.fk_product'; @@ -204,7 +204,7 @@ if ($id > 0 || !empty($ref)) dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref', '', '', '', 0, '', '', 0); - if ($object->type != Product::TYPE_SERVICE || empty($conf->global->PRODUIT_MULTIPRICES)) + if ($object->type != Product::TYPE_SERVICE || !empty($conf->global->STOCK_SUPPORTS_SERVICES) || empty($conf->global->PRODUIT_MULTIPRICES)) { print '
'; print '
'; @@ -320,14 +320,11 @@ if ($id > 0 || !empty($ref)) print ''."\n"; $totalsell = 0; - if (count($prods_arbo)) - { - foreach ($prods_arbo as $value) - { + if (count($prods_arbo)) { + foreach ($prods_arbo as $value) { $productstatic->fetch($value['id']); - if ($value['level'] <= 1) - { + if ($value['level'] <= 1) { print ''; $notdefined = 0; @@ -380,7 +377,7 @@ if ($id > 0 || !empty($ref)) // Qty + IncDec if ($user->rights->produit->creer || $user->rights->service->creer) { - print ''; + print ''; print ''; } else { print ''.$nb_of_subproduct.''; @@ -566,6 +563,7 @@ if ($id > 0 || !empty($ref)) $productstatic->label = $objp->label; $productstatic->type = $objp->type; $productstatic->entity = $objp->entity; + $productstatic->status_batch = $objp->tobatch; print ''.$productstatic->getNomUrl(1, '', 24).''; $labeltoshow = $objp->label; diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index c97c56b2887..631cedd0a25 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -128,26 +128,27 @@ class MouvementStock extends CommonObject * Add a movement of stock (in one direction only). * $this->origin can be also be set to save the source object of movement. * - * @param User $user User object - * @param int $fk_product Id of product - * @param int $entrepot_id Id of warehouse - * @param int $qty Qty of movement (can be <0 or >0 depending on parameter type) - * @param int $type Direction of movement: - * 0=input (stock increase by a stock transfer), 1=output (stock decrease by a stock transfer), - * 2=output (stock decrease), 3=input (stock increase) - * Note that qty should be > 0 with 0 or 3, < 0 with 1 or 2. - * @param int $price Unit price HT of product, used to calculate average weighted price (AWP or PMP in french). If 0, average weighted price is not changed. - * @param string $label Label of stock movement - * @param string $inventorycode Inventory code - * @param string $datem Force date of movement - * @param integer $eatby eat-by date. Will be used if lot does not exists yet and will be created. - * @param integer $sellby sell-by date. Will be used if lot does not exists yet and will be created. - * @param string $batch batch number - * @param boolean $skip_batch If set to true, stock movement is done without impacting batch record + * @param User $user User object + * @param int $fk_product Id of product + * @param int $entrepot_id Id of warehouse + * @param int $qty Qty of movement (can be <0 or >0 depending on parameter type) + * @param int $type Direction of movement: + * 0=input (stock increase by a stock transfer), 1=output (stock decrease by a stock transfer), + * 2=output (stock decrease), 3=input (stock increase) + * Note that qty should be > 0 with 0 or 3, < 0 with 1 or 2. + * @param int $price Unit price HT of product, used to calculate average weighted price (AWP or PMP in french). If 0, average weighted price is not changed. + * @param string $label Label of stock movement + * @param string $inventorycode Inventory code + * @param string $datem Force date of movement + * @param integer $eatby eat-by date. Will be used if lot does not exists yet and will be created. + * @param integer $sellby sell-by date. Will be used if lot does not exists yet and will be created. + * @param string $batch batch number + * @param boolean $skip_batch If set to true, stock movement is done without impacting batch record * @param int $id_product_batch Id product_batch (when skip_batch is false and we already know which record of product_batch to use) - * @return int <0 if KO, 0 if fk_product is null or product id does not exists, >0 if OK + * @param int $disablestockchangeforsubproduct Disable stock change for sub-products of kit (usefull only if product is a subproduct) + * @return int <0 if KO, 0 if fk_product is null or product id does not exists, >0 if OK */ - public function _create($user, $fk_product, $entrepot_id, $qty, $type, $price = 0, $label = '', $inventorycode = '', $datem = '', $eatby = '', $sellby = '', $batch = '', $skip_batch = false, $id_product_batch = 0) + public function _create($user, $fk_product, $entrepot_id, $qty, $type, $price = 0, $label = '', $inventorycode = '', $datem = '', $eatby = '', $sellby = '', $batch = '', $skip_batch = false, $id_product_batch = 0, $disablestockchangeforsubproduct = 0) { // phpcs:disable global $conf, $langs; @@ -571,9 +572,9 @@ class MouvementStock extends CommonObject } // Add movement for sub products (recursive call) - if (!$error && !empty($conf->global->PRODUIT_SOUSPRODUITS) && empty($conf->global->INDEPENDANT_SUBPRODUCT_STOCK)) + if (!$error && !empty($conf->global->PRODUIT_SOUSPRODUITS) && empty($conf->global->INDEPENDANT_SUBPRODUCT_STOCK) && empty($disablestockchangeforsubproduct)) { - $error = $this->_createSubProduct($user, $fk_product, $entrepot_id, $qty, $type, 0, $label, $inventorycode); // we use 0 as price, because pmp is not changed for subproduct + $error = $this->_createSubProduct($user, $fk_product, $entrepot_id, $qty, $type, 0, $label, $inventorycode); // we use 0 as price, because AWP must not change for subproduct } if ($movestock && !$error) diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php index 0ea05495e5a..89df906feda 100644 --- a/htdocs/product/stock/product.php +++ b/htdocs/product/stock/product.php @@ -108,6 +108,8 @@ if (!empty($canvas)) // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context $hookmanager->initHooks(array('stockproductcard', 'globalcard')); +$error = 0; + /* * Actions @@ -221,17 +223,14 @@ if ($action == "correct_stock" && !$cancel) } } - if (!$error) - { + if (!$error) { $priceunit = price2num(GETPOST("unitprice")); $nbpiece = price2num(GETPOST("nbpiece", 'alphanohtml')); - if (is_numeric($nbpiece) && $nbpiece != 0 && $id) - { + if (is_numeric($nbpiece) && $nbpiece != 0 && $id) { $origin_element = ''; $origin_id = null; - if (GETPOST('projectid', 'int')) - { + if (GETPOST('projectid', 'int')) { $origin_element = 'project'; $origin_id = GETPOST('projectid', 'int'); } @@ -240,8 +239,13 @@ if ($action == "correct_stock" && !$cancel) $object = new Product($db); $result = $object->fetch($id); } - if ($object->hasbatch()) - { + + $disablestockchangeforsubproduct = 0; + if (GETPOST('disablesubproductstockchange')) { + $disablestockchangeforsubproduct = 1; + } + + if ($object->hasbatch()) { $result = $object->correct_stock_batch( $user, GETPOST("id_entrepot", 'int'), @@ -254,7 +258,8 @@ if ($action == "correct_stock" && !$cancel) $batchnumber, GETPOST('inventorycode', 'alphanohtml'), $origin_element, - $origin_id + $origin_id, + $disablestockchangeforsubproduct ); // We do not change value of stock for a correction } else { $result = $object->correct_stock( @@ -266,14 +271,13 @@ if ($action == "correct_stock" && !$cancel) $priceunit, GETPOST('inventorycode', 'alphanohtml'), $origin_element, - $origin_id + $origin_id, + $disablestockchangeforsubproduct ); // We do not change value of stock for a correction } - if ($result > 0) - { - if ($backtopage) - { + if ($result > 0) { + if ($backtopage) { header("Location: ".$backtopage); exit; } else { @@ -535,12 +539,14 @@ if ($id > 0 || $ref) dol_banner_tab($object, 'ref', $linkback, $shownav, 'ref'); - print '
'; - - print '
'; - print ''; - if (!$variants) { + print '
'; + + print '
'; + print '
'; + + print '
'; + if ($conf->productbatch->enabled) { print ''; } - // Stock alert threshold - print ''; - // Hook formObject $parameters = array(); $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook print $hookmanager->resPrint; + print '
'.$langs->trans("ManageLotSerial").''; print $object->getLibStatut(0, 2); @@ -604,16 +610,23 @@ if ($id > 0 || $ref) print '
'.$form->editfieldkey($form->textwithpicto($langs->trans("StockLimit"), $langs->trans("StockLimitDesc"), 1), 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer).''; - print $form->editfieldval("StockLimit", 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer, 'string'); - print '
'; + + print '
'; + print '
'; + + print ''; + + // Stock alert threshold + print ''; + // Desired stock print '"; } - } - print "
'.$form->editfieldkey($form->textwithpicto($langs->trans("StockLimit"), $langs->trans("StockLimitDesc"), 1), 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer).''; + print $form->editfieldval("StockLimit", 'seuil_stock_alerte', $object->seuil_stock_alerte, $object, $user->rights->produit->creer, 'string'); + print '
'.$form->editfieldkey($form->textwithpicto($langs->trans("DesiredStock"), $langs->trans("DesiredStockDesc"), 1), 'desiredstock', $object->desiredstock, $object, $user->rights->produit->creer); print ''; @@ -727,11 +740,15 @@ if ($id > 0 || $ref) } print "
"; - print '
'; - print '
'; + print ""; + + print '
'; + print '
'; + print '

'; + + print '
'; + } print dol_get_fiche_end(); } diff --git a/htdocs/product/stock/stockatdate.php b/htdocs/product/stock/stockatdate.php index f48ac986d20..91159984a35 100644 --- a/htdocs/product/stock/stockatdate.php +++ b/htdocs/product/stock/stockatdate.php @@ -347,10 +347,14 @@ print ''; print '
'; print ''.$langs->trans('Date').' '.$form->selectDate(($date ? $date : -1), 'date'); -print '   '.$langs->trans('Product').' '; +print '   '; +print img_picto('', 'product').' '; +print $langs->trans('Product').' '; $form->select_produits($productid, 'productid', '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'maxwidth300'); -print '   '.$langs->trans('Warehouse').' '; +print '   '; +print img_picto('', 'stock').' '; +print $langs->trans('Warehouse').' '; print $formproduct->selectWarehouses((GETPOSTISSET('fk_warehouse') ? $fk_warehouse : 'ifone'), 'fk_warehouse', '', 1); print '
'; @@ -558,7 +562,7 @@ print $hookmanager->resPrint; if (empty($date) || ! $dateIsValid) { $colspan = 6; if ($mode == 'future') $colspan++; - print ''.$langs->trans("EmptySearchString").''; + print ''.$langs->trans("EnterADateCriteria").''; } print ''; diff --git a/htdocs/product/stock/tpl/stockcorrection.tpl.php b/htdocs/product/stock/tpl/stockcorrection.tpl.php index 0885e9ef777..4f88b448239 100644 --- a/htdocs/product/stock/tpl/stockcorrection.tpl.php +++ b/htdocs/product/stock/tpl/stockcorrection.tpl.php @@ -70,7 +70,7 @@ if ($object->element == 'product') { print ''; $ident = (GETPOST("dwid") ?GETPOST("dwid", 'int') : (GETPOST('id_entrepot') ? GETPOST('id_entrepot', 'int') : ($object->element == 'product' && $object->fk_default_warehouse ? $object->fk_default_warehouse : 'ifone'))); if (empty($ident) && !empty($conf->global->MAIN_DEFAULT_WAREHOUSE)) $ident = $conf->global->MAIN_DEFAULT_WAREHOUSE; - print $formproduct->selectWarehouses($ident, 'id_entrepot', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, null, 'minwidth100'); + print img_picto('', 'stock').$formproduct->selectWarehouses($ident, 'id_entrepot', 'warehouseopen,warehouseinternal', 1, 0, 0, '', 0, 0, null, 'minwidth100'); print '   '; print ''; @@ -91,6 +92,17 @@ print ''.$langs->trans("NumberOfUnit").''; print ''; print ''; +// If product is a Kit, we ask if we must disable stock change of subproducts +if (!empty($conf->global->PRODUIT_SOUSPRODUITS) && $object->element == 'product' && $object->hasFatherOrChild(1)) { + print ''; + print ''; + print ''; + print ''; + print ' '; + print ''; + print ''; +} + // Serial / Eat-by date if (!empty($conf->productbatch->enabled) && (($object->element == 'product' && $object->hasbatch()) @@ -126,6 +138,7 @@ if (!empty($conf->projet->enabled)) { print ''.$langs->trans('Project').''; print ''; + print img_picto('', 'project'); $formproject->select_projects(-1, '', 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 0, 0, 'maxwidth300'); print ''; } diff --git a/htdocs/product/stock/tpl/stocktransfer.tpl.php b/htdocs/product/stock/tpl/stocktransfer.tpl.php index 7e159a35b32..20445445e8e 100644 --- a/htdocs/product/stock/tpl/stocktransfer.tpl.php +++ b/htdocs/product/stock/tpl/stocktransfer.tpl.php @@ -71,18 +71,19 @@ print ''; if ($object->element == 'product') { print ''.$langs->trans("WarehouseSource").''; print ''; - print $formproduct->selectWarehouses((GETPOST("dwid") ?GETPOST("dwid", 'int') : (GETPOST('id_entrepot') ?GETPOST('id_entrepot', 'int') : ($object->element == 'product' && $object->fk_default_warehouse ? $object->fk_default_warehouse : 'ifone'))), 'id_entrepot', 'warehouseopen,warehouseinternal', 1); + print img_picto('', 'stock').$formproduct->selectWarehouses((GETPOST("dwid") ?GETPOST("dwid", 'int') : (GETPOST('id_entrepot') ?GETPOST('id_entrepot', 'int') : ($object->element == 'product' && $object->fk_default_warehouse ? $object->fk_default_warehouse : 'ifone'))), 'id_entrepot', 'warehouseopen,warehouseinternal', 1); print ''; } if ($object->element == 'stock') { print ''.$langs->trans("Product").''; print ''; + print img_picto('', 'product'); $form->select_produits(GETPOST('product_id', 'int'), 'product_id', (empty($conf->global->STOCK_SUPPORTS_SERVICES) ? '0' : ''), 0, 0, -1, 2, '', 0, null, 0, 1, 0, 'maxwidth500'); print ''; } print ''.$langs->trans("WarehouseTarget").''; -print $formproduct->selectWarehouses(GETPOST('id_entrepot_destination'), 'id_entrepot_destination', 'warehouseopen,warehouseinternal', 1); +print img_picto('', 'stock').$formproduct->selectWarehouses(GETPOST('id_entrepot_destination'), 'id_entrepot_destination', 'warehouseopen,warehouseinternal', 1); print ''; print ''.$langs->trans("NumberOfUnit").''; print ''; From a5b86f74ce62e2a0f9b91af89bad9fa48777a985 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 15 Dec 2020 15:18:41 +0100 Subject: [PATCH 090/743] Update doc --- ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/ChangeLog b/ChangeLog index 82f82c0b051..8b3b3c97157 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,7 @@ NEW: Accountancy - change menu disposition NEW: Accountancy - on transfers, select the periodicity by default NEW: Accountancy - Add export for Gestinum (v3 & v5) NEW: new currency rate editor +NEW: Solve blocking feature. Can increase stock of a Kit without changing subproduct stock. NEW: add a widget to show the customers with outstanding limits reached NEW: add 2 rules for emailcollector: Message send/not sent from Dolibarr NEW: add a counter of number of words for pages in website module From 574f1f0336e43205cd1c5059d51e986d5dfab9f9 Mon Sep 17 00:00:00 2001 From: StephaneLesage Date: Sat, 21 Nov 2020 22:40:31 +0100 Subject: [PATCH 091/743] Add edit/delete action icons on categories list pages + classes for icons + format color as in latest commits --- htdocs/categories/edit.php | 23 +++++++++++++---- htdocs/categories/index.php | 40 +++++++++++++++++++++-------- htdocs/categories/viewcat.php | 48 ++++++++++++++++++++++++++--------- 3 files changed, 83 insertions(+), 28 deletions(-) diff --git a/htdocs/categories/edit.php b/htdocs/categories/edit.php index f4b7e12cc30..acb0aef2793 100644 --- a/htdocs/categories/edit.php +++ b/htdocs/categories/edit.php @@ -39,6 +39,7 @@ $type = GETPOST('type', 'aZ09'); // Can be int or string $action = (GETPOST('action', 'aZ09') ?GETPOST('action', 'aZ09') : 'edit'); $confirm = GETPOST('confirm'); $cancel = GETPOST('cancel', 'alpha'); +$backtopage = GETPOST('backtopage', 'alpha'); $socid = (int) GETPOST('socid', 'int'); $label = (string) GETPOST('label', 'alphanohtml'); @@ -74,8 +75,13 @@ $error = 0; */ if ($cancel) { - header('Location: '.DOL_URL_ROOT.'/categories/viewcat.php?id='.$object->id.'&type='.$type); - exit; + if ($backtopage) { + header("Location: ".$backtopage); + exit; + } else { + header('Location: '.DOL_URL_ROOT.'/categories/viewcat.php?id='.$object->id.'&type='.$type); + exit; + } } // Action mise a jour d'une categorie @@ -98,9 +104,15 @@ if ($action == 'update' && $user->rights->categorie->creer) { $ret = $extrafields->setOptionalsFromPost(null, $object); if ($ret < 0) $error++; - if (!$error && $object->update($user) > 0) { - header('Location: '.DOL_URL_ROOT.'/categories/viewcat.php?id='.$object->id.'&type='.$type); - exit; + if (!$error && $object->update($user) > 0) + { + if ($backtopage) { + header("Location: ".$backtopage); + exit; + } else { + header('Location: '.DOL_URL_ROOT.'/categories/viewcat.php?id='.$object->id.'&type='.$type); + exit; + } } else { setEventMessages($object->error, $object->errors, 'errors'); } @@ -131,6 +143,7 @@ print ''; print ''; print ''; print ''; +print ''; print dol_get_fiche_head(''); diff --git a/htdocs/categories/index.php b/htdocs/categories/index.php index 5adc7bbb17b..ff897db86a7 100644 --- a/htdocs/categories/index.php +++ b/htdocs/categories/index.php @@ -109,7 +109,7 @@ if (empty($nosearch)) { foreach ($cats as $cat) { - $color = $categstatic->color ? ' style="background: #'.$categstatic->color.';"' : ' style="background: #bbb"'; + $color = $categstatic->color ? ' style="background: #'.sprintf("%06s", $categstatic->color).';"' : ' style="background: #bbb"'; print "\t".''."\n"; print "\t\t"; @@ -160,26 +160,45 @@ foreach ($fulltree as $key => $val) $categstatic->ref = $val['label']; $categstatic->color = $val['color']; $categstatic->type = $type; - $li = $categstatic->getNomUrl(1, '', 60, $moreparam.'&backtolist='.urlencode($_SERVER["PHP_SELF"].'?type='.$type.$moreparam)); $desc = dol_htmlcleanlastbr($val['description']); $counter = ''; - if ($conf->global->CATEGORY_SHOW_COUNTS) { // we need only a count of the elements, so it is enough to consume only the id's from the database - $elements = $categstatic->getObjectsInCateg($type, 1); + $elements = $type == Categorie::TYPE_ACCOUNT + ? $categstatic->getObjectsInCateg("account", 1) // Categorie::TYPE_ACCOUNT is "bank_account" instead of "account" + : $categstatic->getObjectsInCateg($type, 1); + $counter = "".(is_countable($elements) ? count($elements) : '0').""; } $color = $categstatic->color ? ' style="background: #'.sprintf("%06s", $categstatic->color).';"' : ' style="background: #bbb"'; + $li = $categstatic->getNomUrl(1, '', 60, '&backtolist='.urlencode($_SERVER["PHP_SELF"].'?type='.$type.$moreparam)); - $data[] = array( - 'rowid'=>$val['rowid'], - 'fk_menu'=>$val['fk_parent'], - 'entry'=>''.$counter. - '
'.$li.''.img_view().'
' - ); + $entry = ''; + $entry .= ''; + + $entry .= ''; + + $entry .= $counter; + + $entry .= ''; + $entry .= ''; + $entry .= ''; + + $entry .= ''; + $entry .= '
'; + $entry .= ''.$li.''; + $entry .= ''; + $entry .= ''.img_view().''; + $entry .= ''; + $entry .= ''.img_edit().''; + $entry .= ''; + $entry .= ''.img_delete().''; + $entry .= '
'; + + $data[] = array('rowid' => $val['rowid'], 'fk_menu' => $val['fk_parent'], 'entry' => $entry); } @@ -194,7 +213,6 @@ if (!empty($conf->use_javascript_ajax)) print ''; $nbofentries = (count($data) - 1); - if ($nbofentries > 0) { print ''; diff --git a/htdocs/categories/viewcat.php b/htdocs/categories/viewcat.php index 864de6e2e98..b376b3d3320 100644 --- a/htdocs/categories/viewcat.php +++ b/htdocs/categories/viewcat.php @@ -104,6 +104,14 @@ if ($objecttype != $type) { * Actions */ +if ($confirm == 'no') +{ + if ($backtopage) { + header("Location: ".$backtopage); + exit; + } +} + $parameters = array(); $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks // Remove element from category @@ -164,8 +172,13 @@ if ($user->rights->categorie->supprimer && $action == 'confirm_delete' && $confi { if ($object->delete($user) >= 0) { - header("Location: ".DOL_URL_ROOT.'/categories/index.php?type='.$type); - exit; + if ($backtopage) { + header("Location: ".$backtopage); + exit; + } else { + header("Location: ".DOL_URL_ROOT.'/categories/index.php?type='.$type); + exit; + } } else { setEventMessages($object->error, $object->errors, 'errors'); } @@ -250,7 +263,11 @@ dol_banner_tab($object, 'label', $linkback, ($user->socid ? 0 : 1), 'label', 'la if ($action == 'delete') { - print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&type='.$type, $langs->trans('DeleteCategory'), $langs->trans('ConfirmDeleteCategory'), 'confirm_delete', '', '', 1); + if ($backtopage) { + print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&type='.$type.'&backtopage='.urlencode($backtopage), $langs->trans('DeleteCategory'), $langs->trans('ConfirmDeleteCategory'), 'confirm_delete', '', '', 2); + } else { + print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&type='.$type, $langs->trans('DeleteCategory'), $langs->trans('ConfirmDeleteCategory'), 'confirm_delete', '', '', 1); + } } print '
'; @@ -294,7 +311,7 @@ if ($user->rights->categorie->creer) if ($user->rights->categorie->supprimer) { - print 'id.'&type='.$type.'">'.$langs->trans("Delete").''; + print 'id.'&type='.$type.'&backtolist='.urlencode($backtolist).'">'.$langs->trans("Delete").''; } print "
"; @@ -376,8 +393,7 @@ if ($cats < 0) $categstatic->type = $type; $desc = dol_htmlcleanlastbr($val['description']); - $counter = 0; - + $counter = ''; if ($conf->global->CATEGORY_SHOW_COUNTS) { // we need only a count of the elements, so it is enough to consume only the id's from the database @@ -385,22 +401,29 @@ if ($cats < 0) ? $categstatic->getObjectsInCateg("account", 1) // Categorie::TYPE_ACCOUNT is "bank_account" instead of "account" : $categstatic->getObjectsInCateg($type, 1); - $counter = is_countable($elements) ? count($elements) : 0; + $counter = "".(is_countable($elements) ? count($elements) : '0').""; } - $color = $categstatic->color ? ' style="background: #'.$categstatic->color.';"' : ' style="background: #aaa"'; + $color = $categstatic->color ? ' style="background: #'.sprintf("%06s", $categstatic->color).';"' : ' style="background: #bbb"'; + $li = $categstatic->getNomUrl(1, '', 60, '&backtolist='.urlencode($_SERVER["PHP_SELF"].'?id='.$id.'&type='.$type)); $entry = ''; $entry .= ''; $entry .= ''; - $entry .= ''; + $entry .= $counter; $entry .= ''; + $entry .= ''; + $entry .= ''; $entry .= ''; @@ -409,7 +432,8 @@ if ($cats < 0) $data[] = array('rowid' => $val['rowid'], 'fk_menu' => $val['fk_parent'], 'entry' => $entry); } - if ((count($data) - 1)) + $nbofentries = (count($data) - 1); + if ($nbofentries > 0) { require_once DOL_DOCUMENT_ROOT.'/core/lib/treeview.lib.php'; print ''; From 695eacf099e8d4b8cde3412e74467fa13f81b22b Mon Sep 17 00:00:00 2001 From: Stephane Lesage Date: Tue, 1 Dec 2020 19:30:09 +0100 Subject: [PATCH 092/743] Create/Edit extra-field with multi-line text area for computed value --- htdocs/core/tpl/admin_extrafields_add.tpl.php | 7 +++++-- htdocs/core/tpl/admin_extrafields_edit.tpl.php | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/htdocs/core/tpl/admin_extrafields_add.tpl.php b/htdocs/core/tpl/admin_extrafields_add.tpl.php index 2a9438f4413..de16915fe3c 100644 --- a/htdocs/core/tpl/admin_extrafields_add.tpl.php +++ b/htdocs/core/tpl/admin_extrafields_add.tpl.php @@ -182,11 +182,14 @@ $listofexamplesforlink = 'Societe:societe/class/societe.class.php
Contact:con
+global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) { ?> - + - + + + diff --git a/htdocs/core/tpl/admin_extrafields_edit.tpl.php b/htdocs/core/tpl/admin_extrafields_edit.tpl.php index 8990f75951d..9e893bf93e3 100644 --- a/htdocs/core/tpl/admin_extrafields_edit.tpl.php +++ b/htdocs/core/tpl/admin_extrafields_edit.tpl.php @@ -265,11 +265,14 @@ if (in_array($type, array_keys($typewecanchangeinto))) +global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) { ?> - + - + + + From 7853e77d3f61d971af9f09707a54629f5fff5b10 Mon Sep 17 00:00:00 2001 From: Stephane Lesage Date: Mon, 30 Nov 2020 11:01:56 +0100 Subject: [PATCH 093/743] Export/Import categories missing fields and clean --- htdocs/categories/class/categorie.class.php | 8 +- htdocs/core/modules/modCategorie.class.php | 667 +++++++++++--------- htdocs/langs/en_US/categories.lang | 16 +- htdocs/langs/fr_FR/categories.lang | 10 +- 4 files changed, 408 insertions(+), 293 deletions(-) diff --git a/htdocs/categories/class/categorie.class.php b/htdocs/categories/class/categorie.class.php index f55b3403546..e49e0ae8838 100644 --- a/htdocs/categories/class/categorie.class.php +++ b/htdocs/categories/class/categorie.class.php @@ -98,6 +98,7 @@ class Categorie extends CommonObject 8 => 'bank_line', 9 => 'warehouse', 10 => 'actioncomm', + 11 => 'website_page' ); /** @@ -221,7 +222,6 @@ class Categorie extends CommonObject /** * @var string Category type * - * @see Categorie::TYPE_ACCOUNT * @see Categorie::TYPE_PRODUCT * @see Categorie::TYPE_SUPPLIER * @see Categorie::TYPE_CUSTOMER @@ -229,9 +229,11 @@ class Categorie extends CommonObject * @see Categorie::TYPE_CONTACT * @see Categorie::TYPE_USER * @see Categorie::TYPE_PROJECT + * @see Categorie::TYPE_ACCOUNT * @see Categorie::TYPE_BANK_LINE - * @see Categorie::TYPE_WAREHOUSE - * @see Categorie::TYPE_ACTIONCOMM + * @see Categorie::TYPE_WAREHOUSE + * @see Categorie::TYPE_ACTIONCOMM + * @see Categorie::TYPE_WEBSITE_PAGE */ public $type; diff --git a/htdocs/core/modules/modCategorie.class.php b/htdocs/core/modules/modCategorie.class.php index bb6c36b353c..d2218d33483 100644 --- a/htdocs/core/modules/modCategorie.class.php +++ b/htdocs/core/modules/modCategorie.class.php @@ -2,6 +2,7 @@ /* Copyright (C) 2005 Matthieu Valleton * Copyright (C) 2005-2014 Laurent Destailleur * Copyright (C) 2012-2016 Juanjo Menent + * Copyright (C) 2020 Stéphane Lesage * * 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 @@ -25,6 +26,7 @@ * \brief Fichier de description et activation du module Categorie */ include_once DOL_DOCUMENT_ROOT.'/core/modules/DolibarrModules.class.php'; +include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; /** @@ -64,7 +66,7 @@ class modCategorie extends DolibarrModules // Config pages $this->config_page_url = array('categorie.php@categories'); - $this->langfiles = array("products", "companies", "categories", "members"); + $this->langfiles = array("products", "companies", "categories", "members", "stocks", "website"); // Constants $this->const = array(); @@ -116,265 +118,297 @@ class modCategorie extends DolibarrModules //-------- $r = 0; + // All Categories List $r++; - $this->export_code[$r] = 'category_'.$r; - $this->export_label[$r] = 'CatSupList'; - $this->export_icon[$r] = 'category'; - $this->export_enabled[$r] = '!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)'; - $this->export_permission[$r] = array(array("categorie", "lire"), array("fournisseur", "lire")); - $this->export_fields_array[$r] = array( - 'u.rowid'=>"CategId", 'u.label'=>"Label", 'u.description'=>"Description", 's.rowid'=>'IdThirdParty', 's.nom'=>'Name', 's.prefix_comm'=>"Prefix", - 's.client'=>"Customer", 's.datec'=>"DateCreation", 's.tms'=>"DateLastModification", 's.code_client'=>"CustomerCode", 's.address'=>"Address", - 's.zip'=>"Zip", 's.town'=>"Town", 'c.label'=>"Country", 'c.code'=>"CountryCode", 's.phone'=>"Phone", 's.fax'=>"Fax", 's.url'=>"Url", 's.email'=>"Email", - 's.siret'=>"ProfId1", 's.siren'=>"ProfId2", 's.ape'=>"ProfId3", 's.idprof4'=>"ProfId4", 's.tva_intra'=>"VATIntraShort", 's.capital'=>"Capital", - 's.note_public'=>"NotePublic" - ); - $this->export_TypeFields_array[$r] = array( - 'u.label'=>"Text", 'u.description'=>"Text", 's.rowid'=>'List:societe:nom', 's.nom'=>'Text', 's.prefix_comm'=>"Text", 's.client'=>"Text", 's.datec'=>"Date", - 's.tms'=>"Date", 's.code_client'=>"Text", 's.address'=>"Text", 's.zip'=>"Text", 's.town'=>"Text", 'c.label'=>"List:c_country:label:label", 'c.code'=>"Text", - 's.phone'=>"Text", 's.fax'=>"Text", 's.url'=>"Text", 's.email'=>"Text", 's.siret'=>"Text", 's.siren'=>"Text", 's.ape'=>"Text", 's.idprof4'=>"Text", - 's.tva_intra'=>"Text", 's.capital'=>"Numeric", 's.note_public'=>"Text" - ); - $this->export_entities_array[$r] = array( - 's.rowid'=>'company', 's.nom'=>'company', 's.prefix_comm'=>"company", 's.client'=>"company", 's.datec'=>"company", 's.tms'=>"company", - 's.code_client'=>"company", 's.address'=>"company", 's.zip'=>"company", 's.town'=>"company", 'c.label'=>"company", 'c.code'=>"company", - 's.phone'=>"company", 's.fax'=>"company", 's.url'=>"company", 's.email'=>"company", 's.siret'=>"company", 's.siren'=>"company", 's.ape'=>"company", - 's.idprof4'=>"company", 's.tva_intra'=>"company", 's.capital'=>"company", 's.note_public'=>"company" - ); // We define here only fields that use another picto + $this->export_code[$r] = $this->rights_class.'_list'; + $this->export_label[$r] = 'CatList'; + $this->export_icon[$r] = $this->picto; + $this->export_enabled[$r] = 'true'; + $this->export_permission[$r] = array(array("categorie", "lire")); + + $typeexample = ""; + if (!empty($conf->product->enabled) || !empty($conf->service->enabled)) { $typeexample .= ($typeexample ? "/" : "")."0=Product-Service"; } + if (!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)) { $typeexample .= ($typeexample ? "/" : "")."1=Supplier"; } + if (!empty($conf->societe->enabled)) { $typeexample .= ($typeexample ? "/" : "")."2=Customer-Prospect"; } + if (!empty($conf->adherent->enabled)) { $typeexample .= ($typeexample ? "/" : "")."3=Member"; } + if (!empty($conf->societe->enabled)) { $typeexample .= ($typeexample ? "/" : "")."4=Contact"; } + if (!empty($conf->bank->enabled)) { $typeexample .= ($typeexample ? "/" : "")."5=Bank account"; } + if (!empty($conf->projet->enabled)) { $typeexample .= ($typeexample ? "/" : "")."6=Project"; } + if (!empty($conf->user->enabled)) { $typeexample .= ($typeexample ? "/" : "")."7=User"; } + if (!empty($conf->bank->enabled)) { $typeexample .= ($typeexample ? "/" : "")."8=Bank line"; } + if (!empty($conf->stock->enabled)) { $typeexample .= ($typeexample ? "/" : "")."9=Warehouse"; } + if (!empty($conf->agenda->enabled)) { $typeexample .= ($typeexample ? "/" : "")."10=Agenda event"; } + if (!empty($conf->website->enabled)) { $typeexample .= ($typeexample ? "/" : "")."11=Website page"; } + + $this->export_fields_array[$r] = array('cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.type'=>"Type ".$typeexample, 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", 'pcat.label'=>"ParentCategoryLabel" ); + $this->export_TypeFields_array[$r] = array('cat.label'=>"Text", 'cat.type'=>"Numeric", 'cat.description'=>"Text", 'cat.fk_parent'=>'List:categorie:label:rowid', 'pcat.label'=>'Text' ); + $this->export_entities_array[$r] = array(); // We define here only fields that use another picto + $this->export_sql_start[$r] = 'SELECT DISTINCT '; - $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as u, '; - $this->export_sql_end[$r] .= MAIN_DB_PREFIX.'categorie_fournisseur as cf, '; - $this->export_sql_end[$r] .= MAIN_DB_PREFIX.'societe as s LEFT JOIN '.MAIN_DB_PREFIX.'c_typent as t ON s.fk_typent = t.id LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON s.fk_pays = c.rowid LEFT JOIN '.MAIN_DB_PREFIX.'c_effectif as ce ON s.fk_effectif = ce.id LEFT JOIN '.MAIN_DB_PREFIX.'c_forme_juridique as cfj ON s.fk_forme_juridique = cfj.code'; - $this->export_sql_end[$r] .= ' WHERE u.rowid = cf.fk_categorie AND cf.fk_soc = s.rowid'; - $this->export_sql_end[$r] .= ' AND u.entity IN ('.getEntity('category').')'; - $this->export_sql_end[$r] .= ' AND u.type = 1'; // Supplier categories + $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as cat'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie as pcat ON pcat.rowid = cat.fk_parent'; + $this->export_sql_end[$r] .= ' WHERE cat.entity IN ('.getEntity('category').')'; + // 0 Products $r++; - $this->export_code[$r] = 'category_'.$r; - $this->export_label[$r] = 'CatCusList'; - $this->export_icon[$r] = 'category'; - $this->export_enabled[$r] = '$conf->societe->enabled'; - $this->export_permission[$r] = array(array("categorie", "lire"), array("societe", "lire")); - $this->export_fields_array[$r] = array( - 'u.rowid'=>"CategId", 'u.label'=>"Label", 'u.description'=>"Description", 's.rowid'=>'IdThirdParty', 's.nom'=>'Name', 's.prefix_comm'=>"Prefix", - 's.client'=>"Customer", 's.datec'=>"DateCreation", 's.tms'=>"DateLastModification", 's.code_client'=>"CustomerCode", 's.address'=>"Address", - 's.zip'=>"Zip", 's.town'=>"Town", 'c.label'=>"Country", 'c.code'=>"CountryCode", 's.phone'=>"Phone", 's.fax'=>"Fax", 's.url'=>"Url", 's.email'=>"Email", - 's.siret'=>"ProfId1", 's.siren'=>"ProfId2", 's.ape'=>"ProfId3", 's.idprof4'=>"ProfId4", 's.tva_intra'=>"VATIntraShort", 's.capital'=>"Capital", - 's.note_public'=>"NotePublic", 's.fk_prospectlevel'=>'ProspectLevel', 's.fk_stcomm'=>'ProspectStatus' - ); - $this->export_TypeFields_array[$r] = array( - 'u.label'=>"Text", 'u.description'=>"Text", 's.rowid'=>'List:societe:nom', 's.nom'=>'Text', 's.prefix_comm'=>"Text", 's.client'=>"Text", - 's.datec'=>"Date", 's.tms'=>"Date", 's.code_client'=>"Text", 's.address'=>"Text", 's.zip'=>"Text", 's.town'=>"Text", 'c.label'=>"List:c_country:label:label", - 'c.code'=>"Text", 's.phone'=>"Text", 's.fax'=>"Text", 's.url'=>"Text", 's.email'=>"Text", 's.siret'=>"Text", 's.siren'=>"Text", 's.ape'=>"Text", - 's.idprof4'=>"Text", 's.tva_intra'=>"Text", 's.capital'=>"Numeric", 's.note_public'=>"Text", 's.fk_prospectlevel'=>'List:c_prospectlevel:label:code', - 's.fk_stcomm'=>'List:c_stcomm:libelle:code' - ); - $this->export_entities_array[$r] = array( - 's.rowid'=>'company', 's.nom'=>'company', 's.prefix_comm'=>"company", 's.client'=>"company", 's.datec'=>"company", 's.tms'=>"company", - 's.code_client'=>"company", 's.address'=>"company", 's.zip'=>"company", 's.town'=>"company", 'c.label'=>"company", 'c.code'=>"company", - 's.phone'=>"company", 's.fax'=>"company", 's.url'=>"company", 's.email'=>"company", 's.siret'=>"company", 's.siren'=>"company", 's.ape'=>"company", - 's.idprof4'=>"company", 's.tva_intra'=>"company", 's.capital'=>"company", 's.note_public'=>"company", 's.fk_prospectlevel'=>'company', - 's.fk_stcomm'=>'company' - ); // We define here only fields that use another picto + $this->export_code[$r] = $this->rights_class.'_0_'.Categorie::$MAP_ID_TO_CODE[0]; + $this->export_label[$r] = 'CatProdList'; + $this->export_icon[$r] = $this->picto; + $this->export_enabled[$r] = '!empty($conf->product->enabled) || !empty($conf->service->abled)'; + $this->export_permission[$r] = array(array("categorie", "lire"), array("produit", "export")); + $this->export_fields_array[$r] = array('cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", 'p.rowid'=>'ProductId', 'p.ref'=>'Ref', 'p.label'=>'Label'); + $this->export_TypeFields_array[$r] = array('cat.label'=>"Text", 'cat.description'=>"Text", 'cat.fk_parent'=>'List:categorie:label:rowid', 'p.ref'=>'Text', 'p.label'=>'Text'); + $this->export_entities_array[$r] = array('p.rowid'=>'product', 'p.ref'=>'product', 'p.label'=>'product'); // We define here only fields that use another picto - $keyforselect = 'societe'; $keyforelement = 'company'; $keyforaliasextra = 'extrasoc'; + $keyforselect = 'product'; $keyforelement = 'product'; $keyforaliasextra = 'extra'; include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; $this->export_sql_start[$r] = 'SELECT DISTINCT '; - $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as u, '; - $this->export_sql_end[$r] .= MAIN_DB_PREFIX.'categorie_societe as cf, '; - $this->export_sql_end[$r] .= MAIN_DB_PREFIX.'societe as s'; - $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_typent as t ON s.fk_typent = t.id LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON s.fk_pays = c.rowid LEFT JOIN '.MAIN_DB_PREFIX.'c_effectif as ce ON s.fk_effectif = ce.id LEFT JOIN '.MAIN_DB_PREFIX.'c_forme_juridique as cfj ON s.fk_forme_juridique = cfj.code'; - $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe_extrafields as extrasoc ON s.rowid = extrasoc.fk_object '; - $this->export_sql_end[$r] .= ' WHERE u.rowid = cf.fk_categorie AND cf.fk_soc = s.rowid'; - $this->export_sql_end[$r] .= ' AND u.entity IN ('.getEntity('category').')'; - $this->export_sql_end[$r] .= ' AND u.type = 2'; // Customer/Prospect categories + $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as cat'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'categorie_product as cp ON cp.fk_categorie = cat.rowid'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'product as p ON p.rowid = cp.fk_product'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_extrafields as extra ON extra.fk_object = p.rowid'; + $this->export_sql_end[$r] .= ' WHERE cat.entity IN ('.getEntity('category').')'; + $this->export_sql_end[$r] .= ' AND cat.type = 0'; + // 1 Suppliers $r++; - $this->export_code[$r] = 'category_'.$r; - $this->export_label[$r] = 'CatProdList'; - $this->export_icon[$r] = 'category'; - $this->export_enabled[$r] = '$conf->product->enabled || $conf->service->enabled'; - $this->export_permission[$r] = array(array("categorie", "lire"), array("produit", "lire")); - $this->export_fields_array[$r] = array('u.rowid'=>"CategId", 'u.label'=>"Label", 'u.description'=>"Description", 'p.rowid'=>'ProductId', 'p.ref'=>'Ref'); - $this->export_TypeFields_array[$r] = array('u.label'=>"Text", 'u.description'=>"Text", 'p.ref'=>'Text'); - $this->export_entities_array[$r] = array('p.rowid'=>'product', 'p.ref'=>'product'); // We define here only fields that use another picto - $this->export_sql_start[$r] = 'SELECT DISTINCT '; - $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as u, '.MAIN_DB_PREFIX.'categorie_product as cp, '.MAIN_DB_PREFIX.'product as p'; - $this->export_sql_end[$r] .= ' WHERE u.rowid = cp.fk_categorie AND cp.fk_product = p.rowid'; - $this->export_sql_end[$r] .= ' AND u.entity IN ('.getEntity('category').')'; - $this->export_sql_end[$r] .= ' AND u.type = 0'; // Supplier categories - - $r++; - $this->export_code[$r] = 'category_'.$r; - $this->export_label[$r] = 'CatMemberList'; - $this->export_icon[$r] = 'category'; - $this->export_enabled[$r] = '$conf->adherent->enabled'; - $this->export_permission[$r] = array(array("categorie", "lire"), array("adherent", "lire")); - $this->export_fields_array[$r] = array('u.rowid'=>"CategId", 'u.label'=>"Label", 'u.description'=>"Description", 'p.rowid'=>'MemberId', 'p.lastname'=>'LastName', 'p.firstname'=>'Firstname'); - $this->export_TypeFields_array[$r] = array('u.label'=>"Text", 'u.description'=>"Text", 'p.lastname'=>'Text', 'p.firstname'=>'Text'); - $this->export_entities_array[$r] = array('p.rowid'=>'member', 'p.lastname'=>'member', 'p.firstname'=>'member'); // We define here only fields that use another picto - $this->export_sql_start[$r] = 'SELECT DISTINCT '; - $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as u, '.MAIN_DB_PREFIX.'categorie_member as cp, '.MAIN_DB_PREFIX.'adherent as p'; - $this->export_sql_end[$r] .= ' WHERE u.rowid = cp.fk_categorie AND cp.fk_member = p.rowid'; - $this->export_sql_end[$r] .= ' AND u.entity IN ('.getEntity('category').')'; - $this->export_sql_end[$r] .= ' AND u.type = 3'; // Member categories - - $r++; - $this->export_code[$r] = 'category_'.$r; - $this->export_label[$r] = 'CatContactList'; - $this->export_icon[$r] = 'category'; - $this->export_enabled[$r] = '$conf->societe->enabled'; - $this->export_permission[$r] = array(array("categorie", "lire"), array("societe", "lire")); + $this->export_code[$r] = $this->rights_class.'_1_'.Categorie::$MAP_ID_TO_CODE[1]; + $this->export_label[$r] = 'CatSupList'; + $this->export_icon[$r] = $this->picto; + $this->export_enabled[$r] = '!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)'; + $this->export_permission[$r] = array(array("categorie", "lire"), array("fournisseur", "lire")); $this->export_fields_array[$r] = array( - 'u.rowid' => "CategId", - 'u.label' => "Label", - 'u.description' => "Description", - 'p.rowid' => 'ContactId', - 'p.civility' => 'UserTitle', - 'p.lastname' => 'LastName', - 'p.firstname' => 'Firstname', - 'p.address' => 'Address', - 'p.zip' => 'Zip', - 'p.town' => 'Town', - 'country.code' => 'CountryCode', - 'country.label' => 'Country', - 'p.birthday' => 'DateOfBirth', - 'p.poste' => 'PostOrFunction', - 'p.phone' => 'Phone', - 'p.phone_perso' => 'PhonePerso', - 'p.phone_mobile' => 'PhoneMobile', - 'p.fax' => 'Fax', - 'p.email' => 'Email', - 'p.note_private' => 'NotePrivate', - 'p.note_public' => 'NotePublic', - 'p.statut' => 'Status', - 's.nom'=>"Name", - 's.client'=>"Customer", - 's.fournisseur'=>"Supplier", - 's.status'=>"Status", - 's.address'=>"Address", - 's.zip'=>"Zip", - 's.town'=>"Town", - 's.phone'=>"Phone", - 's.fax'=>"Fax", - 's.url'=>"Url", - 's.email'=>"Email" + 'cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", + 's.rowid'=>'IdThirdParty', 's.nom'=>'Name', 's.prefix_comm'=>"Prefix", 's.fournisseur'=>"Supplier", 's.datec'=>"DateCreation", 's.tms'=>"DateLastModification", 's.code_fournisseur'=>"SupplierCode", + 's.address'=>"Address", 's.zip'=>"Zip", 's.town'=>"Town", 'c.label'=>"Country", 'c.code'=>"CountryCode", + 's.phone'=>"Phone", 's.fax'=>"Fax", 's.url'=>"Url", 's.email'=>"Email", + 's.siret'=>"ProfId1", 's.siren'=>"ProfId2", 's.ape'=>"ProfId3", 's.idprof4'=>"ProfId4", 's.tva_intra'=>"VATIntraShort", 's.capital'=>"Capital", 's.note_public'=>"NotePublic", + 't.libelle'=>'ThirdPartyType' ); $this->export_TypeFields_array[$r] = array( - 'u.label' => "Text", - 'u.description' => "Text", - 'p.lastname' => 'Text', - 'p.firstname' => 'Text', - 'p.statut'=>"Numeric", - 's.nom'=>"Text", - 's.status'=>"Text", - 's.address'=>"Text", - 's.zip'=>"Text", - 's.town'=>"Text", - 's.phone'=>"Text", - 's.fax'=>"Text", - 's.url'=>"Text", - 's.email'=>"Text" + 'cat.label'=>"Text", 'cat.description'=>"Text", 'cat.fk_parent'=>'List:categorie:label:rowid', + 's.rowid'=>'List:societe:nom', 's.nom'=>'Text', 's.prefix_comm'=>"Text", 's.fournisseur'=>"Text", 's.datec'=>"Date", 's.tms'=>"Date", 's.code_fournisseur'=>"Text", + 's.address'=>"Text", 's.zip'=>"Text", 's.town'=>"Text", 'c.label'=>"List:c_country:label:label", 'c.code'=>"Text", + 's.phone'=>"Text", 's.fax'=>"Text", 's.url'=>"Text", 's.email'=>"Text", + 's.siret'=>"Text", 's.siren'=>"Text", 's.ape'=>"Text", 's.idprof4'=>"Text", 's.tva_intra'=>"Text", 's.capital'=>"Numeric", 's.note_public'=>"Text", + 't.libelle'=>'List:c_typent:libelle:code' ); $this->export_entities_array[$r] = array( - 'u.rowid' => "category", - 'u.label' => "category", - 'u.description' => "category", - 'p.rowid' => 'contact', - 'p.civility' => 'contact', - 'p.lastname' => 'contact', - 'p.firstname' => 'contact', - 'p.address' => 'contact', - 'p.zip' => 'contact', - 'p.town' => 'contact', - 'country.code' => 'contact', - 'country.label' => 'contact', - 'p.birthday' => 'contact', - 'p.poste' => 'contact', - 'p.phone' => 'contact', - 'p.phone_perso' => 'contact', - 'p.phone_mobile' => 'contact', - 'p.fax' => 'contact', - 'p.email' => 'contact', - 'p.note_private' => 'contact', - 'p.note_public' => 'contact', - 'p.statut' => 'contact', - 's.nom'=>"company", - 's.client'=>"company", - 's.fournisseur'=>"company", - 's.status'=>"company", - 's.address'=>"company", - 's.zip'=>"company", - 's.town'=>"company", - 's.phone'=>"company", - 's.fax'=>"company", - 's.url'=>"company", - 's.email'=>"company" + 's.rowid'=>'company', 's.nom'=>'company', 's.prefix_comm'=>"company", 's.fournisseur'=>"company", 's.datec'=>"company", 's.tms'=>"company", 's.code_fournisseur'=>"company", + 's.address'=>"company", 's.zip'=>"company", 's.town'=>"company", 'c.label'=>"company", 'c.code'=>"company", + 's.phone'=>"company", 's.fax'=>"company", 's.url'=>"company", 's.email'=>"company", + 's.siret'=>"company", 's.siren'=>"company", 's.ape'=>"company", 's.idprof4'=>"company", 's.tva_intra'=>"company", 's.capital'=>"company", 's.note_public'=>"company", + 't.libelle'=>'company' ); // We define here only fields that use another picto - // Add extra fields - $sql = "SELECT name, label, type, param FROM ".MAIN_DB_PREFIX."extrafields WHERE elementtype = 'socpeople' AND entity IN (0, ".$conf->entity.")"; - $resql = $this->db->query($sql); - if ($resql) // This can fail when class is used on old database (during migration for example) - { - while ($obj = $this->db->fetch_object($resql)) - { - $fieldname = 'extra.'.$obj->name; - $fieldlabel = ucfirst($obj->label); - $typeFilter = "Text"; - switch ($obj->type) - { - case 'int': - case 'double': - case 'price': - $typeFilter = "Numeric"; - break; - case 'date': - case 'datetime': - $typeFilter = "Date"; - break; - case 'boolean': - $typeFilter = "Boolean"; - break; - case 'sellist': - $typeFilter = "List:".$obj->param; - break; - case 'select': - $typeFilter = "Select:".$obj->param; - break; - } - $this->export_fields_array[$r][$fieldname] = $fieldlabel; - $this->export_TypeFields_array[$r][$fieldname] = $typeFilter; - $this->export_entities_array[$r][$fieldname] = 'contact'; - } - } - // End add axtra fields + $keyforselect = 'societe'; $keyforelement = 'company'; $keyforaliasextra = 'extra'; + include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; $this->export_sql_start[$r] = 'SELECT DISTINCT '; - $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as u, '.MAIN_DB_PREFIX.'categorie_contact as cp, '.MAIN_DB_PREFIX.'socpeople as p'; - $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as country ON p.fk_pays = country.rowid'; - $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc'; + $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as cat'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'categorie_fournisseur as cf ON cf.fk_categorie = cat.rowid'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = cf.fk_soc'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe_extrafields as extra ON s.rowid = extra.fk_object'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON s.fk_pays = c.rowid'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_typent as t ON s.fk_typent = t.id'; + $this->export_sql_end[$r] .= ' WHERE cat.entity IN ('.getEntity('category').')'; + $this->export_sql_end[$r] .= ' AND cat.type = 1'; + + // 2 Customers/Prospects + $r++; + $this->export_code[$r] = $this->rights_class.'_2_'.Categorie::$MAP_ID_TO_CODE[2]; + $this->export_label[$r] = 'CatCusList'; + $this->export_icon[$r] = $this->picto; + $this->export_enabled[$r] = '!empty($conf->societe->enabled)'; + $this->export_permission[$r] = array(array("categorie", "lire"), array("societe", "export")); + $this->export_fields_array[$r] = array( + 'cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", + 's.rowid'=>'IdThirdParty', 's.nom'=>'Name', 's.prefix_comm'=>"Prefix", 's.client'=>"Customer", 's.datec'=>"DateCreation", 's.tms'=>"DateLastModification", 's.code_client'=>"CustomerCode", + 's.address'=>"Address", 's.zip'=>"Zip", 's.town'=>"Town", 'c.label'=>"Country", 'c.code'=>"CountryCode", + 's.phone'=>"Phone", 's.fax'=>"Fax", 's.url'=>"Url", 's.email'=>"Email", + 's.siret'=>"ProfId1", 's.siren'=>"ProfId2", 's.ape'=>"ProfId3", 's.idprof4'=>"ProfId4", 's.tva_intra'=>"VATIntraShort", 's.capital'=>"Capital", 's.note_public'=>"NotePublic", + 't.libelle'=>'ThirdPartyType', 'pl.code'=>'ProspectLevel', 'st.code'=>'ProspectStatus' + ); + $this->export_TypeFields_array[$r] = array( + 'cat.label'=>"Text", 'cat.description'=>"Text", 'cat.fk_parent'=>'List:categorie:label:rowid', + 's.rowid'=>'List:societe:nom', 's.nom'=>'Text', 's.prefix_comm'=>"Text", 's.client'=>"Text", 's.datec'=>"Date", 's.tms'=>"Date", 's.code_client'=>"Text", + 's.address'=>"Text", 's.zip'=>"Text", 's.town'=>"Text", 'c.label'=>"List:c_country:label:label", 'c.code'=>"Text", + 's.phone'=>"Text", 's.fax'=>"Text", 's.url'=>"Text", 's.email'=>"Text", + 's.siret'=>"Text", 's.siren'=>"Text", 's.ape'=>"Text", 's.idprof4'=>"Text", 's.tva_intra'=>"Text", 's.capital'=>"Numeric", 's.note_public'=>"Text", + 't.libelle'=>'List:c_typent:libelle:code', 'pl.code'=>'List:c_prospectlevel:label:code', 'st.code'=>'List:c_stcomm:libelle:code' + ); + $this->export_entities_array[$r] = array( + 's.rowid'=>'company', 's.nom'=>'company', 's.prefix_comm'=>"company", 's.client'=>"company", 's.datec'=>"company", 's.tms'=>"company", 's.code_client'=>"company", + 's.address'=>"company", 's.zip'=>"company", 's.town'=>"company", 'c.label'=>"company", 'c.code'=>"company", + 's.phone'=>"company", 's.fax'=>"company", 's.url'=>"company", 's.email'=>"company", + 's.siret'=>"company", 's.siren'=>"company", 's.ape'=>"company", 's.idprof4'=>"company", 's.tva_intra'=>"company", 's.capital'=>"company", 's.note_public'=>"company", + 't.libelle'=>'company', 'pl.code'=>'company', 'st.code'=>'company' + ); // We define here only fields that use another picto + + $keyforselect = 'societe'; $keyforelement = 'company'; $keyforaliasextra = 'extra'; + include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; + + $this->export_sql_start[$r] = 'SELECT DISTINCT '; + $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as cat'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'categorie_societe as cs ON cs.fk_categorie = cat.rowid'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = cs.fk_soc'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe_extrafields as extra ON s.rowid = extra.fk_object'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON s.fk_pays = c.rowid'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_typent as t ON s.fk_typent = t.id'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_prospectlevel as pl ON s.fk_prospectlevel = pl.code'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_stcomm as st ON s.fk_stcomm = st.id'; + $this->export_sql_end[$r] .= ' WHERE cat.entity IN ('.getEntity('category').')'; + $this->export_sql_end[$r] .= ' AND cat.type = 2'; + + // 3 Members + $r++; + $this->export_code[$r] = $this->rights_class.'_3_'.Categorie::$MAP_ID_TO_CODE[3]; + $this->export_label[$r] = 'CatMemberList'; + $this->export_icon[$r] = $this->picto; + $this->export_enabled[$r] = '!empty($conf->adherent->enabled)'; + $this->export_permission[$r] = array(array("categorie", "lire"), array("adherent", "export")); + $this->export_fields_array[$r] = array('cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", 'p.rowid'=>'MemberId', 'p.lastname'=>'LastName', 'p.firstname'=>'Firstname'); + $this->export_TypeFields_array[$r] = array('cat.label'=>"Text", 'cat.description'=>"Text", 'cat.fk_parent'=>'List:categorie:label:rowid', 'p.lastname'=>'Text', 'p.firstname'=>'Text'); + $this->export_entities_array[$r] = array('p.rowid'=>'member', 'p.lastname'=>'member', 'p.firstname'=>'member'); // We define here only fields that use another picto + + $keyforselect = 'adherent'; $keyforelement = 'member'; $keyforaliasextra = 'extra'; + include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; + + $this->export_sql_start[$r] = 'SELECT DISTINCT '; + $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as cat'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'categorie_member as cm ON cm.fk_categorie = cat.rowid'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'adherent as p ON p.rowid = cm.fk_member'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'adherent_extrafields as extra ON cat.rowid = extra.fk_object '; + $this->export_sql_end[$r] .= ' WHERE cat.entity IN ('.getEntity('category').')'; + $this->export_sql_end[$r] .= ' AND cat.type = 3'; + + // 4 Contacts + $r++; + $this->export_code[$r] = $this->rights_class.'_4_'.Categorie::$MAP_ID_TO_CODE[4]; + $this->export_label[$r] = 'CatContactList'; + $this->export_icon[$r] = $this->picto; + $this->export_enabled[$r] = '!empty($conf->societe->enabled)'; + $this->export_permission[$r] = array(array("categorie", "lire"), array("societe", "contact", "export")); + $this->export_fields_array[$r] = array( + 'cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", + 'p.rowid' => 'ContactId', 'civ.label' => 'UserTitle', 'p.lastname' => 'LastName', 'p.firstname' => 'Firstname', + 'p.address' => 'Address', 'p.zip' => 'Zip', 'p.town' => 'Town', 'c.code' => 'CountryCode', 'c.label' => 'Country', + 'p.birthday' => 'DateOfBirth', 'p.poste' => 'PostOrFunction', + 'p.phone' => 'Phone', 'p.phone_perso' => 'PhonePerso', 'p.phone_mobile' => 'PhoneMobile', 'p.fax' => 'Fax', 'p.email' => 'Email', + 'p.note_private' => 'NotePrivate', 'p.note_public' => 'NotePublic', 'p.statut' => 'Status', + 's.nom'=>"Name", 's.client'=>"Customer", 's.fournisseur'=>"Supplier", 's.status'=>"Status", + 's.address'=>"Address", 's.zip'=>"Zip", 's.town'=>"Town", + 's.phone'=>"Phone", 's.fax'=>"Fax", 's.url'=>"Url", 's.email'=>"Email" + ); + $this->export_TypeFields_array[$r] = array( + 'cat.label'=>"Text", 'cat.description'=>"Text", 'cat.fk_parent'=>'List:categorie:label:rowid', + 'civ.label' => 'List:c_civility:label:label', 'p.lastname' => 'Text', 'p.firstname' => 'Text', + 'p.address' => 'Text', 'p.zip' => 'Text', 'p.town' => 'Text', 'c.code' => 'Text', 'c.label' => 'List:c_country:label:label', + 'p.birthday' => 'Date', 'p.poste' => 'Text', + 'p.phone' => 'Text', 'p.phone_perso' => 'Text', 'p.phone_mobile' => 'Text', 'p.fax' => 'Text', 'p.email' => 'Text', + 'p.note_private' => 'Text', 'p.note_public' => 'Text', 'p.statut' => 'Boolean', + 's.nom'=>"Text", 's.client'=>"Boolean", 's.fournisseur'=>"Boolean", 's.status'=>"Boolean", + 's.address'=>"Text", 's.zip'=>"Text", 's.town'=>"Text", + 's.phone'=>"Text", 's.fax'=>"Text", 's.url'=>"Text", 's.email'=>"Text" + ); + $this->export_entities_array[$r] = array( + 'p.rowid' => 'contact', 'civ.label' => 'contact', 'p.lastname' => 'contact', 'p.firstname' => 'contact', + 'p.address' => 'contact', 'p.zip' => 'contact', 'p.town' => 'contact', 'c.code' => 'contact', 'c.label' => 'contact', + 'p.birthday' => 'contact', 'p.poste' => 'contact', + 'p.phone' => 'contact', 'p.phone_perso' => 'contact', 'p.phone_mobile' => 'contact', 'p.fax' => 'contact', 'p.email' => 'contact', + 'p.note_private' => 'contact', 'p.note_public' => 'contact', 'p.statut' => 'contact', + 's.nom'=>"company", 's.client'=>"company", 's.fournisseur'=>"company", 's.status'=>"company", + 's.address'=>"company", 's.zip'=>"company", 's.town'=>"company", + 's.phone'=>"company", 's.fax'=>"company", 's.url'=>"company", 's.email'=>"company" + ); // We define here only fields that use another picto + + $keyforselect = 'socpeople'; $keyforelement = 'contact'; $keyforaliasextra = 'extra'; + include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; + + $this->export_sql_start[$r] = 'SELECT DISTINCT '; + $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as cat'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'categorie_contact as cc ON cc.fk_categorie = cat.rowid'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'socpeople as p ON p.rowid = cc.fk_socpeople'; $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'socpeople_extrafields as extra ON extra.fk_object = p.rowid'; - $this->export_sql_end[$r] .= ' WHERE u.rowid = cp.fk_categorie AND cp.fk_socpeople = p.rowid AND u.entity IN ('.getEntity('category').')'; - $this->export_sql_end[$r] .= ' AND u.type = 4'; // contact categories + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_civility as civ ON civ.rowid = p.civility'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON c.rowid = p.fk_pays'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc'; + $this->export_sql_end[$r] .= ' WHERE cat.entity IN ('.getEntity('category').')'; + $this->export_sql_end[$r] .= ' AND cat.type = 4'; + + // 5 Bank accounts, TODO ? + + // 6 Projects + $r++; + $this->export_code[$r] = $this->rights_class.'_6_'.Categorie::$MAP_ID_TO_CODE[6]; + $this->export_label[$r] = 'CatProjectsList'; + $this->export_icon[$r] = $this->picto; + $this->export_enabled[$r] = '!empty($conf->projet->enabled)'; + $this->export_permission[$r] = array(array("categorie", "lire"), array("projet", "export")); + $this->export_fields_array[$r] = array('cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", 'p.rowid'=>'ProjectId', 'p.ref'=>'Ref', 's.rowid'=>"IdThirdParty", 's.nom'=>"Name"); + $this->export_TypeFields_array[$r] = array('cat.label'=>"Text", 'cat.description'=>"Text", 'cat.fk_parent'=>'List:categorie:label:rowid', 'p.ref'=>'Text', 's.rowid'=>"List:societe:nom:rowid", 's.nom'=>"Text"); + $this->export_entities_array[$r] = array('p.rowid'=>'project', 'p.ref'=>'project', 's.rowid'=>"company", 's.nom'=>"company"); // We define here only fields that use another picto + + $keyforselect = 'projet'; $keyforelement = 'project'; $keyforaliasextra = 'extra'; + include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; + + $this->export_sql_start[$r] = 'SELECT DISTINCT '; + $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as cat'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'categorie_project as cp ON cp.fk_categorie = cat.rowid'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'projet as p ON p.rowid = cp.fk_project'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet_extrafields as extra ON extra.fk_object = p.rowid'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc'; + $this->export_sql_end[$r] .= ' WHERE cat.entity IN ('.getEntity('category').')'; + $this->export_sql_end[$r] .= ' AND cat.type = 6'; + + // 7 Users + $r++; + $this->export_code[$r] = $this->rights_class.'_7_'.Categorie::$MAP_ID_TO_CODE[7]; + $this->export_label[$r] = 'CatUsersList'; + $this->export_icon[$r] = $this->picto; + $this->export_enabled[$r] = '!empty($conf->user->enabled)'; + $this->export_permission[$r] = array(array("categorie", "lire"), array("user", "export")); + $this->export_fields_array[$r] = array('cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", 'p.rowid'=>'TechnicalID', 'p.login'=>'Login', 'p.lastname'=>'Lastname', 'p.firstname'=>'Firstname'); + $this->export_TypeFields_array[$r] = array('cat.label'=>"Text", 'cat.description'=>"Text", 'cat.fk_parent'=>'List:categorie:label:rowid', 'p.login'=>'Text', 'p.lastname'=>'Text', 'p.firstname'=>'Text'); + $this->export_entities_array[$r] = array('p.rowid'=>'user', 'p.login'=>'user', 'p.lastname'=>'user', 'p.firstname'=>'user'); // We define here only fields that use another picto + + $keyforselect = 'user'; $keyforelement = 'user'; $keyforaliasextra = 'extra'; + include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php'; + + $this->export_sql_start[$r] = 'SELECT DISTINCT '; + $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as cat'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'categorie_user as cu ON cu.fk_categorie = cat.rowid'; + $this->export_sql_end[$r] .= ' INNER JOIN '.MAIN_DB_PREFIX.'user as p ON p.rowid = cu.fk_user'; + $this->export_sql_end[$r] .= ' LEFT JOIN '.MAIN_DB_PREFIX.'user_extrafields as extra ON extra.fk_object = p.rowid'; + $this->export_sql_end[$r] .= ' WHERE cat.entity IN ('.getEntity('category').')'; + $this->export_sql_end[$r] .= ' AND cat.type = 7'; + + // 8 Bank Lines, TODO ? + + // 9 Warehouses, TODO ? + + // 10 Agenda Events, TODO ? + + // 11 Website Pages, TODO ? // Imports //-------- $r = 0; + // Categories $r++; - $this->import_code[$r] = $this->rights_class.'_'.$r; + $this->import_code[$r] = $this->rights_class.'_list'; $this->import_label[$r] = "CatList"; // Translation key $this->import_icon[$r] = $this->picto; $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon $this->import_tables_array[$r] = array('ca'=>MAIN_DB_PREFIX.'categorie'); $this->import_fields_array[$r] = array( 'ca.label'=>"Label*", 'ca.type'=>"Type*", 'ca.description'=>"Description", - 'ca.fk_parent' => 'Parent' + 'ca.fk_parent' => 'ParentCategory' ); - $this->import_regex_array[$r] = array('ca.type'=>'^\d+$'); + $this->import_regex_array[$r] = array('ca.type'=>'^(0|1|2|3|4|5|6|7|8|9|10|11)$'); $this->import_convertvalue_array[$r] = array( 'ca.fk_parent' => array( 'rule' => 'fetchidfromcodeandlabel', @@ -385,21 +419,18 @@ class modCategorie extends DolibarrModules 'codefromfield' => 'ca.type' ) ); - $typeexample = ""; - if (!empty($conf->product->enabled)) { $typeexample .= ($typeexample ? "/" : "")."0=Product"; } - if (!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)) { $typeexample .= ($typeexample ? "/" : "")."1=Supplier"; } - if (!empty($conf->societe->enabled)) { $typeexample .= ($typeexample ? "/" : "")."2=Customer-Prospect"; } - if (!empty($conf->adherent->enabled)) { $typeexample .= ($typeexample ? "/" : "")."3=Member"; } - $this->import_examplevalues_array[$r] = array( - 'ca.label'=>"Supplier Category", 'ca.type'=>$typeexample, 'ca.description'=>"My Category description", - 'ca.fk_parent' => '0' - ); + $this->import_examplevalues_array[$r] = array( + 'ca.label'=>"My Category Label", 'ca.type'=>$typeexample, 'ca.description'=>"My Category description", // $typeexample built above in exports + 'ca.fk_parent' => 'rowid or label' + ); + $this->import_updatekeys_array[$r] = array('ca.label'=>'Label'); + + // 0 Products if (!empty($conf->product->enabled)) { - //Products $r++; - $this->import_code[$r] = $this->rights_class.'_'.$r; + $this->import_code[$r] = $this->rights_class.'_0_'.Categorie::$MAP_ID_TO_CODE[0]; $this->import_label[$r] = "CatProdLinks"; // Translation key $this->import_icon[$r] = $this->picto; $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon @@ -409,57 +440,16 @@ class modCategorie extends DolibarrModules $this->import_convertvalue_array[$r] = array( 'cp.fk_categorie'=>array('rule'=>'fetchidfromref', 'classfile'=>'/categories/class/categorie.class.php', 'class'=>'Categorie', 'method'=>'fetch', 'element'=>'category'), - 'cp.fk_product'=>array('rule'=>'fetchidfromref', 'classfile'=>'/product/class/product.class.php', 'class'=>'Product', 'method'=>'fetch', 'element'=>'product') + 'cp.fk_product'=>array('rule'=>'fetchidfromref', 'classfile'=>'/product/class/product.class.php', 'class'=>'Product', 'method'=>'fetch', 'element'=>'Product') ); - $this->import_examplevalues_array[$r] = array('cp.fk_categorie'=>"Imported category", 'cp.fk_product'=>"PREF123456"); - } - - if (!empty($conf->societe->enabled)) - { - // Customers - $r++; - $this->import_code[$r] = $this->rights_class.'_'.$r; - $this->import_label[$r] = "CatCusLinks"; // Translation key - $this->import_icon[$r] = $this->picto; - $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon - $this->import_tables_array[$r] = array('cs'=>MAIN_DB_PREFIX.'categorie_societe'); - $this->import_fields_array[$r] = array('cs.fk_categorie'=>"Category*", 'cs.fk_soc'=>"ThirdParty*"); - $this->import_regex_array[$r] = array( - 'cs.fk_categorie'=>'rowid@'.MAIN_DB_PREFIX.'categorie:type=2', - 'cs.fk_soc'=>'rowid@'.MAIN_DB_PREFIX.'societe:client>0' - ); - - $this->import_convertvalue_array[$r] = array( - 'cs.fk_categorie'=>array('rule'=>'fetchidfromref', 'classfile'=>'/categories/class/categorie.class.php', 'class'=>'Categorie', 'method'=>'fetch', 'element'=>'category'), - 'cs.fk_soc'=>array('rule'=>'fetchidfromref', 'classfile'=>'/societe/class/societe.class.php', 'class'=>'Societe', 'method'=>'fetch', 'element'=>'ThirdParty') - ); - $this->import_examplevalues_array[$r] = array('cs.fk_categorie'=>"Imported category", 'cs.fk_soc'=>"MyBigCompany"); - - // Contacts/Addresses - $r++; - $this->import_code[$r] = $this->rights_class.'_'.$r; - $this->import_label[$r] = "CatContactsLinks"; // Translation key - $this->import_icon[$r] = $this->picto; - $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon - $this->import_tables_array[$r] = array('cs'=>MAIN_DB_PREFIX.'categorie_contact'); - $this->import_fields_array[$r] = array('cs.fk_categorie'=>"Category*", 'cs.fk_socpeople'=>"Contact ID*"); - $this->import_regex_array[$r] = array( - 'cs.fk_categorie'=>'rowid@'.MAIN_DB_PREFIX.'categorie:type=4' - //'cs.fk_socpeople'=>'rowid@'.MAIN_DB_PREFIX.'socpeople' - ); - - $this->import_convertvalue_array[$r] = array( - 'cs.fk_categorie'=>array('rule'=>'fetchidfromref', 'classfile'=>'/categories/class/categorie.class.php', 'class'=>'Categorie', 'method'=>'fetch', 'element'=>'category') - //'cs.fk_socpeople'=>array('rule'=>'fetchidfromref','classfile'=>'/contact/class/contact.class.php','class'=>'Contact','method'=>'fetch','element'=>'Contact') - ); - $this->import_examplevalues_array[$r] = array('cs.fk_categorie'=>"Imported category", 'cs.fk_socpeople'=>"123"); + $this->import_examplevalues_array[$r] = array('cp.fk_categorie'=>"rowid or label", 'cp.fk_product'=>"rowid or ref"); } + // 1 Suppliers if (!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)) { - // Suppliers $r++; - $this->import_code[$r] = $this->rights_class.'_'.$r; + $this->import_code[$r] = $this->rights_class.'_1_'.Categorie::$MAP_ID_TO_CODE[1]; $this->import_label[$r] = "CatSupLinks"; // Translation key $this->import_icon[$r] = $this->picto; $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon @@ -474,8 +464,119 @@ class modCategorie extends DolibarrModules 'cs.fk_categorie'=>array('rule'=>'fetchidfromref', 'classfile'=>'/categories/class/categorie.class.php', 'class'=>'Categorie', 'method'=>'fetch', 'element'=>'category'), 'cs.fk_soc'=>array('rule'=>'fetchidfromref', 'classfile'=>'/societe/class/societe.class.php', 'class'=>'Societe', 'method'=>'fetch', 'element'=>'ThirdParty') ); - $this->import_examplevalues_array[$r] = array('cs.fk_categorie'=>"Imported category", 'cs.fk_soc'=>"MyBigCompany"); + $this->import_examplevalues_array[$r] = array('cs.fk_categorie'=>"rowid or label", 'cs.fk_soc'=>"rowid or ref"); } + + // 2 Customers + if (!empty($conf->societe->enabled)) + { + $r++; + $this->import_code[$r] = $this->rights_class.'_2_'.Categorie::$MAP_ID_TO_CODE[2]; + $this->import_label[$r] = "CatCusLinks"; // Translation key + $this->import_icon[$r] = $this->picto; + $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon + $this->import_tables_array[$r] = array('cs'=>MAIN_DB_PREFIX.'categorie_societe'); + $this->import_fields_array[$r] = array('cs.fk_categorie'=>"Category*", 'cs.fk_soc'=>"Customer*"); + $this->import_regex_array[$r] = array( + 'cs.fk_categorie'=>'rowid@'.MAIN_DB_PREFIX.'categorie:type=2', + 'cs.fk_soc'=>'rowid@'.MAIN_DB_PREFIX.'societe:client>0' + ); + + $this->import_convertvalue_array[$r] = array( + 'cs.fk_categorie'=>array('rule'=>'fetchidfromref', 'classfile'=>'/categories/class/categorie.class.php', 'class'=>'Categorie', 'method'=>'fetch', 'element'=>'category'), + 'cs.fk_soc'=>array('rule'=>'fetchidfromref', 'classfile'=>'/societe/class/societe.class.php', 'class'=>'Societe', 'method'=>'fetch', 'element'=>'ThirdParty') + ); + $this->import_examplevalues_array[$r] = array('cs.fk_categorie'=>"rowid or label", 'cs.fk_soc'=>"rowid or ref"); + } + + // 3 Members + if (!empty($conf->adherent->enabled)) + { + $r++; + $this->import_code[$r] = $this->rights_class.'_3_'.Categorie::$MAP_ID_TO_CODE[3]; + $this->import_label[$r] = "CatMembersLinks"; // Translation key + $this->import_icon[$r] = $this->picto; + $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon + $this->import_tables_array[$r] = array('cm'=>MAIN_DB_PREFIX.'categorie_contact'); + $this->import_fields_array[$r] = array('cm.fk_categorie'=>"Category*", 'cm.fk_member'=>"Member*"); + $this->import_regex_array[$r] = array('cm.fk_categorie'=>'rowid@'.MAIN_DB_PREFIX.'categorie:type=3'); + + $this->import_convertvalue_array[$r] = array( + 'cs.fk_categorie'=>array('rule'=>'fetchidfromref', 'classfile'=>'/categories/class/categorie.class.php', 'class'=>'Categorie', 'method'=>'fetch', 'element'=>'category'), + 'cs.fk_member'=>array('rule'=>'fetchidfromref','classfile'=>'/adherents/class/adherent.class.php','class'=>'Adherent','method'=>'fetch','element'=>'Member') + ); + $this->import_examplevalues_array[$r] = array('cs.fk_categorie'=>"rowid or label", 'cs.fk_member'=>"rowid or ref"); + } + + // 4 Contacts/Addresses + if (!empty($conf->societe->enabled)) + { + $r++; + $this->import_code[$r] = $this->rights_class.'_4_'.Categorie::$MAP_ID_TO_CODE[4]; + $this->import_label[$r] = "CatContactsLinks"; // Translation key + $this->import_icon[$r] = $this->picto; + $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon + $this->import_tables_array[$r] = array('cc'=>MAIN_DB_PREFIX.'categorie_contact'); + $this->import_fields_array[$r] = array('cc.fk_categorie'=>"Category*", 'cc.fk_socpeople'=>"IdContact*"); + $this->import_regex_array[$r] = array( + 'cc.fk_categorie'=>'rowid@'.MAIN_DB_PREFIX.'categorie:type=4' + //'cc.fk_socpeople'=>'rowid@'.MAIN_DB_PREFIX.'socpeople' + ); + + $this->import_convertvalue_array[$r] = array( + 'cc.fk_categorie'=>array('rule'=>'fetchidfromref', 'classfile'=>'/categories/class/categorie.class.php', 'class'=>'Categorie', 'method'=>'fetch', 'element'=>'category'), + //'cc.fk_socpeople'=>array('rule'=>'fetchidfromref','classfile'=>'/contact/class/contact.class.php','class'=>'Contact','method'=>'fetch','element'=>'Contact') + ); + $this->import_examplevalues_array[$r] = array('cc.fk_categorie'=>"rowid or label", 'cc.fk_socpeople'=>"rowid"); + } + + // 5 Bank accounts, TODO ? + + // 6 Projects + if (!empty($conf->projet->enabled)) + { + $r++; + $this->import_code[$r] = $this->rights_class.'_6_'.Categorie::$MAP_ID_TO_CODE[6]; + $this->import_label[$r] = "CatProjectsLinks"; // Translation key + $this->import_icon[$r] = $this->picto; + $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon + $this->import_tables_array[$r] = array('cp'=>MAIN_DB_PREFIX.'categorie_project'); + $this->import_fields_array[$r] = array('cp.fk_categorie'=>"Category*", 'cp.fk_project'=>"Project*"); + $this->import_regex_array[$r] = array('cp.fk_categorie'=>'rowid@'.MAIN_DB_PREFIX.'categorie:type=6'); + + $this->import_convertvalue_array[$r] = array( + 'cs.fk_categorie'=>array('rule'=>'fetchidfromref', 'classfile'=>'/categories/class/categorie.class.php', 'class'=>'Categorie', 'method'=>'fetch', 'element'=>'category'), + 'cs.fk_project'=>array('rule'=>'fetchidfromref','classfile'=>'/projet/class/project.class.php','class'=>'Project','method'=>'fetch','element'=>'Project') + ); + $this->import_examplevalues_array[$r] = array('cp.fk_categorie'=>"rowid or label", 'cp.fk_project'=>"rowid or ref"); + } + + // 7 Users + if (!empty($conf->user->enabled)) + { + $r++; + $this->import_code[$r] = $this->rights_class.'_7_'.Categorie::$MAP_ID_TO_CODE[7]; + $this->import_label[$r] = "CatUsersLinks"; // Translation key + $this->import_icon[$r] = $this->picto; + $this->import_entities_array[$r] = array(); // We define here only fields that use another icon that the one defined into import_icon + $this->import_tables_array[$r] = array('cu'=>MAIN_DB_PREFIX.'categorie_user'); + $this->import_fields_array[$r] = array('cu.fk_categorie'=>"Category*", 'cu.fk_user'=>"User*"); + $this->import_regex_array[$r] = array('cu.fk_categorie'=>'rowid@'.MAIN_DB_PREFIX.'categorie:type=7'); + + $this->import_convertvalue_array[$r] = array( + 'cu.fk_categorie'=>array('rule'=>'fetchidfromref', 'classfile'=>'/categories/class/categorie.class.php', 'class'=>'Categorie', 'method'=>'fetch', 'element'=>'category'), + 'cu.fk_user'=>array('rule'=>'fetchidfromref','classfile'=>'/user/class/user.class.php','class'=>'User','method'=>'fetch','element'=>'User') + ); + $this->import_examplevalues_array[$r] = array('cu.fk_categorie'=>"rowid or label", 'cu.fk_user'=>"rowid or login"); + } + + // 8 Bank Lines, TODO ? + + // 9 Warehouses, TODO ? + + // 10 Agenda Events, TODO ? + + // 11 Website Pages, TODO ? } diff --git a/htdocs/langs/en_US/categories.lang b/htdocs/langs/en_US/categories.lang index ba37a43b4ec..36d1d3e545a 100644 --- a/htdocs/langs/en_US/categories.lang +++ b/htdocs/langs/en_US/categories.lang @@ -65,16 +65,22 @@ UsersCategoriesShort=Users tags/categories StockCategoriesShort=Warehouse tags/categories ThisCategoryHasNoItems=This category does not contain any items. CategId=Tag/category id -CatSupList=List of vendor tags/categories -CatCusList=List of customer/prospect tags/categories +ParentCategory=Parent tag/category +ParentCategoryLabel=Label of parent tag/category +CatSupList=List of vendors tags/categories +CatCusList=List of customers/prospects tags/categories CatProdList=List of products tags/categories CatMemberList=List of members tags/categories -CatContactList=List of contact tags/categories -CatSupLinks=Links between suppliers and tags/categories +CatContactList=List of contacts tags/categories +CatProjectsList=List of projects tags/categories +CatUsersList=List of users tags/categories +CatSupLinks=Links between vendors and tags/categories CatCusLinks=Links between customers/prospects and tags/categories CatContactsLinks=Links between contacts/addresses and tags/categories CatProdLinks=Links between products/services and tags/categories -CatProJectLinks=Links between projects and tags/categories +CatMembersLinks=Links between members and tags/categories +CatProjectsLinks=Links between projects and tags/categories +CatUsersLinks=Links between users and tags/categories DeleteFromCat=Remove from tags/category ExtraFieldsCategories=Complementary attributes CategoriesSetup=Tags/categories setup diff --git a/htdocs/langs/fr_FR/categories.lang b/htdocs/langs/fr_FR/categories.lang index 580e98a38e9..f69d8573827 100644 --- a/htdocs/langs/fr_FR/categories.lang +++ b/htdocs/langs/fr_FR/categories.lang @@ -65,16 +65,22 @@ UsersCategoriesShort=Tags utlisateurs StockCategoriesShort=Tags/catégories d’entrepôt ThisCategoryHasNoItems=Cette catégorie ne contient aucun élément. CategId=ID du(de la) tag/catégorie +ParentCategory=Tag/catégorie parent +ParentCategoryLabel=Libellé du/de la tag/catégorie parent CatSupList=Liste des tags/catégories de fournisseurs CatCusList=Liste des tags/catégories de clients/prospects CatProdList=Liste des tags/catégories de produits/services CatMemberList=Liste des tags/catégories de membres -CatContactList=Liste des tags/catégories de contact +CatContactList=Liste des tags/catégories de contacts +CatProjectsList=Liste des tags/catégories de projets +CatUsersList=Liste des tags/catégories d'utilisateurs CatSupLinks=Liens entre fournisseurs et tags/catégories CatCusLinks=Liens entre clients/prospects et tags/catégories CatContactsLinks=Liens entre contacts/adresses et tags/catégories CatProdLinks=Liens entre produits/services et tags/catégories -CatProJectLinks=Liens entre projets et tags/catégories +CatMembersLinks=Liens entre membres et tags/catégories +CatProjectsLinks=Liens entre projets et tags/catégories +CatUsersLinks=Liens entre utilisateurs et tags/catégories DeleteFromCat=Enlever des tags/catégories ExtraFieldsCategories=Attributs supplémentaires CategoriesSetup=Configuration des tags/catégories From 7749138a810a01470cb9327455b4f7be82498195 Mon Sep 17 00:00:00 2001 From: StephaneLesage Date: Sat, 21 Nov 2020 13:28:37 +0100 Subject: [PATCH 094/743] Fix /categories/edit.php which used type parameter instead of object's type Do not read type parameter Security check, display error and exit Get type from category object --- htdocs/categories/edit.php | 9 ++++++--- htdocs/categories/info.php | 7 ++++--- htdocs/categories/photos.php | 7 ++++--- htdocs/categories/traduction.php | 4 ++-- htdocs/categories/viewcat.php | 14 ++------------ 5 files changed, 18 insertions(+), 23 deletions(-) diff --git a/htdocs/categories/edit.php b/htdocs/categories/edit.php index f4b7e12cc30..ba04cde0b54 100644 --- a/htdocs/categories/edit.php +++ b/htdocs/categories/edit.php @@ -35,7 +35,6 @@ $langs->load("categories"); $id = GETPOST('id', 'int'); $ref = GETPOST('ref', 'alphanohtml'); -$type = GETPOST('type', 'aZ09'); // Can be int or string $action = (GETPOST('action', 'aZ09') ?GETPOST('action', 'aZ09') : 'edit'); $confirm = GETPOST('confirm'); $cancel = GETPOST('cancel', 'alpha'); @@ -56,10 +55,14 @@ if ($id == "") { $result = restrictedArea($user, 'categorie', $id, '&category'); $object = new Categorie($db); -if ($id > 0) { - $result = $object->fetch($id); +$result = $object->fetch($id, $label); +if ($result <= 0) { + dol_print_error($db, $object->error); exit; } +$type = $object->type; +if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type]; // For backward compatibility + $extrafields = new ExtraFields($db); $extrafields->fetch_name_optionals_label($object->table_element); diff --git a/htdocs/categories/info.php b/htdocs/categories/info.php index f64f3be8f9e..2dd0476c5cd 100644 --- a/htdocs/categories/info.php +++ b/htdocs/categories/info.php @@ -43,10 +43,11 @@ if ($user->socid) $socid = $user->socid; $result = restrictedArea($user, 'categorie', $id, '&category'); $object = new Categorie($db); -if (!$object->fetch($id) > 0) { - dol_print_error($db); - exit; +$result = $object->fetch($id); +if ($result <= 0) { + dol_print_error($db, $object->error); exit; } + $type = $object->type; if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type]; // For backward compatibility diff --git a/htdocs/categories/photos.php b/htdocs/categories/photos.php index 73c7664f6c2..d09cf595620 100644 --- a/htdocs/categories/photos.php +++ b/htdocs/categories/photos.php @@ -38,7 +38,6 @@ $langs->loadlangs(array('categories', 'bills')); $id = GETPOST('id', 'int'); $label = GETPOST('label', 'alpha'); -$type = GETPOST('type'); $action = GETPOST('action', 'aZ09'); $confirm = GETPOST('confirm'); @@ -52,7 +51,7 @@ if ($id == '' && $label == '') $result = restrictedArea($user, 'categorie', $id, '&category'); $object = new Categorie($db); -$result = $object->fetch($id, $label, $type); +$result = $object->fetch($id, $label); if ($result <= 0) { dol_print_error($db, $object->error); exit; } @@ -60,10 +59,12 @@ $object->fetch_optionals(); if ($result <= 0) { dol_print_error($db, $object->error); exit; } -$upload_dir = $conf->categorie->multidir_output[$object->entity]; +$type = $object->type; if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type]; // For backward compatibility +$upload_dir = $conf->categorie->multidir_output[$object->entity]; + /* * Actions */ diff --git a/htdocs/categories/traduction.php b/htdocs/categories/traduction.php index 423819c49ac..825f52f5b80 100644 --- a/htdocs/categories/traduction.php +++ b/htdocs/categories/traduction.php @@ -39,7 +39,6 @@ $id = GETPOST('id', 'int'); $label = GETPOST('label', 'alpha'); $action = GETPOST('action', 'aZ09'); $cancel = GETPOST('cancel', 'alpha'); -$type = GETPOST('type', 'aZ09'); if ($id == '' && $label == '') { @@ -51,7 +50,7 @@ if ($id == '' && $label == '') $result = restrictedArea($user, 'categorie', $id, '&category'); $object = new Categorie($db); -$result = $object->fetch($id, $label, $type); +$result = $object->fetch($id, $label); if ($result <= 0) { dol_print_error($db, $object->error); exit; } @@ -60,6 +59,7 @@ if ($result <= 0) { dol_print_error($db, $object->error); exit; } +$type = $object->type; if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type]; // For backward compatibility /* diff --git a/htdocs/categories/viewcat.php b/htdocs/categories/viewcat.php index 864de6e2e98..5503a7977be 100644 --- a/htdocs/categories/viewcat.php +++ b/htdocs/categories/viewcat.php @@ -38,7 +38,6 @@ $langs->load("categories"); $id = GETPOST('id', 'int'); $label = GETPOST('label', 'alpha'); -$type = GETPOST('type', 'aZ09'); $removeelem = GETPOST('removeelem', 'int'); $elemid = GETPOST('elemid', 'int'); @@ -73,7 +72,7 @@ if ($id == "" && $label == "") $result = restrictedArea($user, 'categorie', $id, '&category'); $object = new Categorie($db); -$result = $object->fetch($id, $label, $type); +$result = $object->fetch($id, $label); if ($result <= 0) { dol_print_error($db, $object->error); exit; } @@ -82,10 +81,7 @@ if ($result <= 0) { dol_print_error($db, $object->error); exit; } -$objecttype = $object->type; -if (is_numeric($objecttype)) $objecttype = Categorie::$MAP_ID_TO_CODE[$objecttype]; -if ($type === '') $type = $objecttype; - +$type = $object->type; if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type]; // For backward compatibility $extrafields = new ExtraFields($db); @@ -94,12 +90,6 @@ $extrafields->fetch_name_optionals_label($object->table_element); // Initialize technical object to manage hooks. Note that conf->hooks_modules contains array array $hookmanager->initHooks(array('categorycard', 'globalcard')); -// Protection when type provided is not similare to type of category -if ($objecttype != $type) { - print 'Error: Value for type parameter does not match value of the type of the category with id='.$id; - exit; -} - /* * Actions */ From 49225f3f57a2196915c2942cb43b2b1b8f79edbd Mon Sep 17 00:00:00 2001 From: StephaneLesage Date: Sat, 21 Nov 2020 15:57:11 +0100 Subject: [PATCH 095/743] Fix /categories/info.php navigation --- htdocs/categories/info.php | 7 ++++--- htdocs/categories/photos.php | 6 ------ htdocs/categories/traduction.php | 4 ---- htdocs/categories/viewcat.php | 7 +------ htdocs/core/lib/categories.lib.php | 2 +- 5 files changed, 6 insertions(+), 20 deletions(-) diff --git a/htdocs/categories/info.php b/htdocs/categories/info.php index 2dd0476c5cd..536b0c20a2c 100644 --- a/htdocs/categories/info.php +++ b/htdocs/categories/info.php @@ -37,13 +37,14 @@ $langs->loadLangs(array('categories', 'sendings')); $socid = 0; $id = GETPOST('id', 'int'); +$label = GETPOST('label', 'alpha'); // Security check if ($user->socid) $socid = $user->socid; $result = restrictedArea($user, 'categorie', $id, '&category'); $object = new Categorie($db); -$result = $object->fetch($id); +$result = $object->fetch($id, $label); if ($result <= 0) { dol_print_error($db, $object->error); exit; } @@ -64,11 +65,11 @@ llxHeader('', $langs->trans('Categories'), ''); $title = Categorie::$MAP_TYPE_TITLE_AREA[$type]; $head = categories_prepare_head($object, $type); - print dol_get_fiche_head($head, 'info', $langs->trans($title), -1, 'category'); + $backtolist = (GETPOST('backtolist') ? GETPOST('backtolist') : DOL_URL_ROOT.'/categories/index.php?leftmenu=cat&type='.$type); $linkback = ''.$langs->trans("BackToList").''; -$object->next_prev_filter = ' type = '.$type; +$object->next_prev_filter = ' type = '.$object->type; $object->ref = $object->label; $morehtmlref = '
'.$langs->trans("Root").' >> '; $ways = $object->print_all_ways(" >> ", '', 1); diff --git a/htdocs/categories/photos.php b/htdocs/categories/photos.php index d09cf595620..6bb5d79980c 100644 --- a/htdocs/categories/photos.php +++ b/htdocs/categories/photos.php @@ -55,10 +55,6 @@ $result = $object->fetch($id, $label); if ($result <= 0) { dol_print_error($db, $object->error); exit; } -$object->fetch_optionals(); -if ($result <= 0) { - dol_print_error($db, $object->error); exit; -} $type = $object->type; if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type]; // For backward compatibility @@ -116,8 +112,6 @@ if ($object->id) $title = Categorie::$MAP_TYPE_TITLE_AREA[$type]; $head = categories_prepare_head($object, $type); - - print dol_get_fiche_head($head, 'photos', $langs->trans($title), -1, 'category'); $linkback = ''.$langs->trans("BackToList").''; diff --git a/htdocs/categories/traduction.php b/htdocs/categories/traduction.php index 825f52f5b80..898484d0d51 100644 --- a/htdocs/categories/traduction.php +++ b/htdocs/categories/traduction.php @@ -54,10 +54,6 @@ $result = $object->fetch($id, $label); if ($result <= 0) { dol_print_error($db, $object->error); exit; } -$object->fetch_optionals(); -if ($result <= 0) { - dol_print_error($db, $object->error); exit; -} $type = $object->type; if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type]; // For backward compatibility diff --git a/htdocs/categories/viewcat.php b/htdocs/categories/viewcat.php index 5503a7977be..20f54a40788 100644 --- a/htdocs/categories/viewcat.php +++ b/htdocs/categories/viewcat.php @@ -76,10 +76,6 @@ $result = $object->fetch($id, $label); if ($result <= 0) { dol_print_error($db, $object->error); exit; } -$object->fetch_optionals(); -if ($result <= 0) { - dol_print_error($db, $object->error); exit; -} $type = $object->type; if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type]; // For backward compatibility @@ -216,9 +212,8 @@ llxHeader("", $langs->trans("Categories"), $helpurl, '', 0, 0, $arrayofjs, $arra $title = Categorie::$MAP_TYPE_TITLE_AREA[$type]; $head = categories_prepare_head($object, $type); - - print dol_get_fiche_head($head, 'card', $langs->trans($title), -1, 'category'); + $backtolist = (GETPOST('backtolist') ? GETPOST('backtolist') : DOL_URL_ROOT.'/categories/index.php?leftmenu=cat&type='.$type); $linkback = ''.$langs->trans("BackToList").''; $object->next_prev_filter = ' type = '.$object->type; diff --git a/htdocs/core/lib/categories.lib.php b/htdocs/core/lib/categories.lib.php index e900e6ff424..f6942cb5265 100644 --- a/htdocs/core/lib/categories.lib.php +++ b/htdocs/core/lib/categories.lib.php @@ -57,7 +57,7 @@ function categories_prepare_head(Categorie $object, $type) $h++; } - $head[$h][0] = DOL_URL_ROOT.'/categories/info.php?id='.$object->id; + $head[$h][0] = DOL_URL_ROOT.'/categories/info.php?id='.$object->id.'&type='.$type; $head[$h][1] = $langs->trans("Info"); $head[$h][2] = 'info'; $h++; From 7a93c11ae0e2473918397646bdccbe09ab7256cd Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Tue, 15 Dec 2020 22:01:33 +0100 Subject: [PATCH 096/743] FIX : dupliacete customer or supplier code must be error dipslayed with new code proposed --- htdocs/societe/card.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php index b8af6777daa..56793184f65 100644 --- a/htdocs/societe/card.php +++ b/htdocs/societe/card.php @@ -634,11 +634,16 @@ if (empty($reshook)) } else { - if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') // TODO Sometime errors on duplicate on profid and not on code, so we must manage this case + if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS' || ($result==-3 && in_array('ErrorCustomerCodeAlreadyUsed', $object->errors))) // TODO Sometime errors on duplicate on profid and not on code, so we must manage this case + { + $duplicate_code_error = true; + $object->code_client = null; + } + + if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS' || ($result==-3 && in_array('ErrorSupplierCodeAlreadyUsed',$object->errors))) // TODO Sometime errors on duplicate on profid and not on code, so we must manage this case { $duplicate_code_error = true; $object->code_fournisseur = null; - $object->code_client = null; } setEventMessages($object->error, $object->errors, 'errors'); From b80bd8d1f8af4ced9e3c978ed41f5dbc605169b0 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Tue, 15 Dec 2020 22:03:15 +0100 Subject: [PATCH 097/743] FIX : dupliacete customer or supplier code must be error dipslayed with new code proposed --- htdocs/societe/card.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php index 56793184f65..93e0196f1ae 100644 --- a/htdocs/societe/card.php +++ b/htdocs/societe/card.php @@ -634,18 +634,22 @@ if (empty($reshook)) } else { - if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS' || ($result==-3 && in_array('ErrorCustomerCodeAlreadyUsed', $object->errors))) // TODO Sometime errors on duplicate on profid and not on code, so we must manage this case + if ($result==-3 && in_array('ErrorCustomerCodeAlreadyUsed', $object->errors)) { $duplicate_code_error = true; $object->code_client = null; } - if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS' || ($result==-3 && in_array('ErrorSupplierCodeAlreadyUsed',$object->errors))) // TODO Sometime errors on duplicate on profid and not on code, so we must manage this case + if ($result==-3 && in_array('ErrorSupplierCodeAlreadyUsed',$object->errors)) { $duplicate_code_error = true; $object->code_fournisseur = null; } + if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') { + $duplicate_code_error = true; + } + setEventMessages($object->error, $object->errors, 'errors'); $error++; } From 9ca2e98a662a16bf57fb51f62729c1e6e4bca443 Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Tue, 15 Dec 2020 21:12:52 +0000 Subject: [PATCH 098/743] Fixing style errors. --- htdocs/societe/card.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/societe/card.php b/htdocs/societe/card.php index 93e0196f1ae..d79a5c1a9e0 100644 --- a/htdocs/societe/card.php +++ b/htdocs/societe/card.php @@ -640,7 +640,7 @@ if (empty($reshook)) $object->code_client = null; } - if ($result==-3 && in_array('ErrorSupplierCodeAlreadyUsed',$object->errors)) + if ($result==-3 && in_array('ErrorSupplierCodeAlreadyUsed', $object->errors)) { $duplicate_code_error = true; $object->code_fournisseur = null; From 5fe8c599d22c9c261b8cd4a5727c9bd6cf9499cb Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 00:28:57 +0100 Subject: [PATCH 099/743] Fix warning --- htdocs/comm/propal/class/propal.class.php | 1 + htdocs/supplier_proposal/class/supplier_proposal.class.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/htdocs/comm/propal/class/propal.class.php b/htdocs/comm/propal/class/propal.class.php index 274c04b0698..f572f6110bf 100644 --- a/htdocs/comm/propal/class/propal.class.php +++ b/htdocs/comm/propal/class/propal.class.php @@ -598,6 +598,7 @@ class Propal extends CommonObject $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc); // Clean vat code + $reg = array(); $vat_src_code = ''; if (preg_match('/\((.*)\)/', $txtva, $reg)) { diff --git a/htdocs/supplier_proposal/class/supplier_proposal.class.php b/htdocs/supplier_proposal/class/supplier_proposal.class.php index cf8e712a103..022c5a13354 100644 --- a/htdocs/supplier_proposal/class/supplier_proposal.class.php +++ b/htdocs/supplier_proposal/class/supplier_proposal.class.php @@ -593,8 +593,8 @@ class SupplierProposal extends CommonObject // Mise en option de la ligne if (empty($qty) && empty($special_code)) $this->line->special_code = 3; - if (is_array($array_option) && count($array_option) > 0) { - $this->line->array_options = $array_option; + if (is_array($array_options) && count($array_options) > 0) { + $this->line->array_options = $array_options; } $result = $this->line->insert(); From 834058a81669ab94c965e94ddfd82adb021ecf77 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 02:33:21 +0100 Subject: [PATCH 100/743] Debug api to create a document --- htdocs/api/class/api_documents.class.php | 44 ++++++++------- htdocs/core/lib/files.lib.php | 2 +- htdocs/core/lib/geturl.lib.php | 2 +- test/phpunit/AllTests.php | 2 + test/phpunit/RestAPIDocumentTest.php | 69 +++++++++++++++++++----- 5 files changed, 85 insertions(+), 34 deletions(-) diff --git a/htdocs/api/class/api_documents.class.php b/htdocs/api/class/api_documents.class.php index da9545aa248..bdf86a5c2c9 100644 --- a/htdocs/api/class/api_documents.class.php +++ b/htdocs/api/class/api_documents.class.php @@ -535,13 +535,14 @@ class Documents extends DolibarrApi * Test sample for supplier invoice: { "filename": "mynewfile.txt", "modulepart": "supplier_invoice", "ref": "FA1701-001", "subdir": "", "filecontent": "content text", "fileencoding": "", "overwriteifexists": "0" }. * Test sample for medias file: { "filename": "mynewfile.txt", "modulepart": "medias", "ref": "", "subdir": "image/mywebsite", "filecontent": "Y29udGVudCB0ZXh0Cg==", "fileencoding": "base64", "overwriteifexists": "0" }. * - * @param string $filename Name of file to create ('FA1705-0123.txt') - * @param string $modulepart Name of module or area concerned by file upload ('facture', 'project', 'project_task', ...) - * @param string $ref Reference of object (This will define subdir automatically and store submited file into it) - * @param string $subdir Subdirectory (Only if ref not provided) - * @param string $filecontent File content (string with file content. An empty file will be created if this parameter is not provided) - * @param string $fileencoding File encoding (''=no encoding, 'base64'=Base 64) - * @param int $overwriteifexists Overwrite file if exists (1 by default) + * @param string $filename Name of file to create ('FA1705-0123.txt') + * @param string $modulepart Name of module or area concerned by file upload ('facture', 'project', 'project_task', ...) + * @param string $ref Reference of object (This will define subdir automatically and store submited file into it) + * @param string $subdir Subdirectory (Only if ref not provided) + * @param string $filecontent File content (string with file content. An empty file will be created if this parameter is not provided) + * @param string $fileencoding File encoding (''=no encoding, 'base64'=Base 64) + * @param int $overwriteifexists Overwrite file if exists (1 by default) + * @param int $createdirifnotexists Create subdirectories if the doesn't exists (1 by default) * @return string * * @throws RestException 400 @@ -551,7 +552,7 @@ class Documents extends DolibarrApi * * @url POST /upload */ - public function post($filename, $modulepart, $ref = '', $subdir = '', $filecontent = '', $fileencoding = '', $overwriteifexists = 0) + public function post($filename, $modulepart, $ref = '', $subdir = '', $filecontent = '', $fileencoding = '', $overwriteifexists = 0, $createdirifnotexists = 1) { global $db, $conf; @@ -578,6 +579,8 @@ class Documents extends DolibarrApi // Define $uploadir $object = null; $entity = DolibarrApiAccess::$user->entity; + if (empty($entity)) $entity = 1; + if ($ref) { $tmpreldir = ''; @@ -663,8 +666,7 @@ class Documents extends DolibarrApi } } - if (!($object->id > 0)) - { + if (!($object->id > 0)) { throw new RestException(404, 'The object '.$modulepart." with ref '".$ref."' was not found."); } @@ -681,29 +683,32 @@ class Documents extends DolibarrApi if (empty($upload_dir) || $upload_dir == '/') { - throw new RestException(500, 'This value of modulepart does not support yet usage of ref. Check modulepart parameter or try to use subdir parameter instead of ref.'); + throw new RestException(500, 'This value of modulepart ('.$modulepart.') does not support yet usage of ref. Check modulepart parameter or try to use subdir parameter instead of ref.'); } } else { if ($modulepart == 'invoice') $modulepart = 'facture'; if ($modulepart == 'member') $modulepart = 'adherent'; $relativefile = $subdir; - $tmp = dol_check_secure_access_document($modulepart, $relativefile, $entity, DolibarrApiAccess::$user, '', 'write'); $upload_dir = $tmp['original_file']; // No dirname here, tmp['original_file'] is already the dir because dol_check_secure_access_document was called with param original_file that is only the dir - if (empty($upload_dir) || $upload_dir == '/') - { - throw new RestException(500, 'This value of modulepart does not support yet usage of ref. Check modulepart parameter or try to use subdir parameter instead of ref.'); + if (empty($upload_dir) || $upload_dir == '/') { + if (!empty($tmp['error'])) { + throw new RestException(401, 'Error returned by dol_check_secure_access_document: '.$tmp['error']); + } else { + throw new RestException(500, 'This value of modulepart ('.$modulepart.') is not allowed with this value of subdir ('.$relativefile.')'); + } } } // $original_file here is still value of filename without any dir. $upload_dir = dol_sanitizePathName($upload_dir); - if (dol_mkdir($upload_dir) < 0) // needed by products - { - throw new RestException(500, 'Error while trying to create directory.'); + if (!empty($createdirifnotexists)) { + if (dol_mkdir($upload_dir) < 0) { // needed by products + throw new RestException(500, 'Error while trying to create directory.'); + } } $destfile = $upload_dir.'/'.$original_file; @@ -715,8 +720,7 @@ class Documents extends DolibarrApi throw new RestException(401, 'Directory not exists : '.dirname($destfile)); } - if (!$overwriteifexists && dol_is_file($destfile)) - { + if (!$overwriteifexists && dol_is_file($destfile)) { throw new RestException(500, "File with name '".$original_file."' already exists."); } diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 0be1cfbc3bc..22545b78cf8 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -2231,7 +2231,7 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, // Fix modulepart if ($modulepart == 'users') $modulepart = 'user'; - dol_syslog('modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity); + dol_syslog('dol_check_secure_access_document modulepart='.$modulepart.' original_file='.$original_file.' entity='.$entity); // We define $accessallowed and $sqlprotectagainstexternals $accessallowed = 0; diff --git a/htdocs/core/lib/geturl.lib.php b/htdocs/core/lib/geturl.lib.php index 8cfc08222cd..7e93623f3c1 100644 --- a/htdocs/core/lib/geturl.lib.php +++ b/htdocs/core/lib/geturl.lib.php @@ -33,7 +33,7 @@ * @param string[] $addheaders Array of string to add into header. Example: ('Accept: application/xrds+xml', ....) * @param string[] $allowedschemes List of schemes that are allowed ('http' + 'https' only by default) * @param int $localurl 0=Only external URL are possible, 1=Only local URL, 2=Both external and local URL are allowed. - * @return array Returns an associative array containing the response from the server array('content'=>response,'curl_error_no'=>errno,'curl_error_msg'=>errmsg...) + * @return array Returns an associative array containing the response from the server array('content'=>response, 'curl_error_no'=>errno, 'curl_error_msg'=>errmsg...) */ function getURLContent($url, $postorget = 'GET', $param = '', $followlocation = 1, $addheaders = array(), $allowedschemes = array('http', 'https'), $localurl = 0) { diff --git a/test/phpunit/AllTests.php b/test/phpunit/AllTests.php index 163bf320075..ce7f1074721 100644 --- a/test/phpunit/AllTests.php +++ b/test/phpunit/AllTests.php @@ -216,6 +216,8 @@ class AllTests require_once dirname(__FILE__).'/RestAPIUserTest.php'; $suite->addTestSuite('RestAPIUserTest'); + require_once dirname(__FILE__).'/RestAPIDocumentTest.php'; + $suite->addTestSuite('RestAPIDocumentTest'); // Test only with php7.2 or less //if ((float) phpversion() < 7.3) diff --git a/test/phpunit/RestAPIDocumentTest.php b/test/phpunit/RestAPIDocumentTest.php index 8dbaafab018..f45e0437ce5 100644 --- a/test/phpunit/RestAPIDocumentTest.php +++ b/test/phpunit/RestAPIDocumentTest.php @@ -152,35 +152,43 @@ class RestAPIDocumentTest extends PHPUnit\Framework\TestCase { global $conf,$user,$langs,$db; - $url = $this->api_url.'/documents/?api_key='.$this->api_key; + $url = $this->api_url.'/documents/upload?api_key='.$this->api_key; echo __METHOD__.' Request POST url='.$url."\n"; // Send to non existent directory - dol_delete_dir_recursive(DOL_DATA_ROOT.'/medias/tmpphpunit'); + dol_delete_dir_recursive(DOL_DATA_ROOT.'/medias/tmpphpunit/tmpphpunit1'); //$data = '{ "filename": "mynewfile.txt", "modulepart": "medias", "ref": "", "subdir": "mysubdir1/mysubdir2", "filecontent": "content text", "fileencoding": "" }'; $data = array( 'filename'=>"mynewfile.txt", 'modulepart'=>"medias", - 'ref'=>"", - 'subdir'=>"tmpphpunit/tmpphpunit2", + 'subdir'=>"tmpphpunit/tmpphpunit1", 'filecontent'=>"content text", - 'fileencoding'=>"" + 'fileencoding'=>"", + 'overwriteifexists'=>0, + 'createdirifnotexists'=>0 ); - $result = getURLContent($url, 'POST', $data, 1, array(), array('http', 'https'), 2); + $param = ''; + foreach($data as $key => $val) { + $param .= '&'.$key.'='.urlencode($val); + } + + $result = getURLContent($url, 'POST', $param, 1, array(), array('http', 'https'), 2); echo __METHOD__.' Result for sending document: '.var_export($result, true)."\n"; echo __METHOD__.' curl_error_no: '.$result['curl_error_no']."\n"; $object = json_decode($result['content'], true); - $this->assertNotNull($object, 'Parsing of json result must no be null'); - $this->assertEquals('401', $object['error']['code']); + $this->assertNotNull($object, 'Parsing of json result must not be null'); + $this->assertEquals('401', $result['http_code'], 'Return code is not 401'); + $this->assertEquals('401', empty($object['error']['code']) ? '' : $object['error']['code'], 'Error code is not 401'); // Send to existent directory + dol_delete_dir_recursive(DOL_DATA_ROOT.'/medias/tmpphpunit/tmpphpunit2'); dol_mkdir(DOL_DATA_ROOT.'/medias/tmpphpunit/tmpphpunit2'); $data = array( @@ -189,16 +197,53 @@ class RestAPIDocumentTest extends PHPUnit\Framework\TestCase 'ref'=>"", 'subdir'=>"tmpphpunit/tmpphpunit2", 'filecontent'=>"content text", - 'fileencoding'=>"" + 'fileencoding'=>"", + 'overwriteifexists'=>0, + 'createdirifnotexists'=>0 ); - $result2 = getURLContent($url, 'POST', $data, 1, array(), array('http', 'https'), 2); + $param = ''; + foreach($data as $key => $val) { + $param .= '&'.$key.'='.urlencode($val); + } + + $result2 = getURLContent($url, 'POST', $param, 1, array(), array('http', 'https'), 2); echo __METHOD__.' Result for sending document: '.var_export($result2, true)."\n"; echo __METHOD__.' curl_error_no: '.$result2['curl_error_no']."\n"; $object2 = json_decode($result2['content'], true); - $this->assertNotNull($object2, 'Parsing of json result must no be null'); + //$this->assertNotNull($object2, 'Parsing of json result must not be null'); + $this->assertEquals('200', $result2['http_code'], 'Return code must be 200'); $this->assertEquals($result2['curl_error_no'], ''); - $this->assertEquals($result2['content'], 'true'); + $this->assertEquals($object2, 'mynewfile.txt', 'Must contains basename of file'); + + + dol_delete_dir_recursive(DOL_DATA_ROOT.'/medias/tmpphpunit/tmpphpunit3'); + + $data = array( + 'filename'=>"mynewfile.txt", + 'modulepart'=>"medias", + 'ref'=>"", + 'subdir'=>"tmpphpunit/tmpphpunit3", + 'filecontent'=>"content text", + 'fileencoding'=>"", + 'overwriteifexists'=>0, + 'createdirifnotexists'=>1 + ); + + $param = ''; + foreach($data as $key => $val) { + $param .= '&'.$key.'='.urlencode($val); + } + + $result3 = getURLContent($url, 'POST', $param, 1, array(), array('http', 'https'), 2); + echo __METHOD__.' Result for sending document: '.var_export($result3, true)."\n"; + echo __METHOD__.' curl_error_no: '.$result3['curl_error_no']."\n"; + $object3 = json_decode($result3['content'], true); + //$this->assertNotNull($object2, 'Parsing of json result must not be null'); + $this->assertEquals('200', $result3['http_code'], 'Return code must be 200'); + $this->assertEquals($result3['curl_error_no'], ''); + $this->assertEquals($object3, 'mynewfile.txt', 'Must contains basename of file'); + dol_delete_dir_recursive(DOL_DATA_ROOT.'/medias/tmpphpunit'); } From 69061e422bab02386d0c6bf02f01befe3cb3f87b Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 02:44:48 +0100 Subject: [PATCH 101/743] Removed warning --- htdocs/commande/class/commande.class.php | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php index 9ea05a13065..00cba2055f7 100644 --- a/htdocs/commande/class/commande.class.php +++ b/htdocs/commande/class/commande.class.php @@ -492,26 +492,21 @@ class Commande extends CommonOrder $error++; } - if (!$error) - { + if (!$error) { // If stock is incremented on validate order, we must increment it - if ($result >= 0 && !empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) - { + if ($result >= 0 && !empty($conf->stock->enabled) && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) { require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php'; $langs->load("agenda"); // Loop on each line $cpt = count($this->lines); - for ($i = 0; $i < $cpt; $i++) - { - if ($this->lines[$i]->fk_product > 0) - { + for ($i = 0; $i < $cpt; $i++) { + if ($this->lines[$i]->fk_product > 0) { $mouvP = new MouvementStock($this->db); $mouvP->origin = &$this; // We decrement stock of product (and sub-products) $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("OrderValidatedInDolibarr", $num)); - if ($result < 0) - { + if ($result < 0) { $error++; $this->error = $mouvP->error; } @@ -521,16 +516,14 @@ class Commande extends CommonOrder } } - if (!$error && !$notrigger) - { + if (!$error && !$notrigger) { // Call trigger $result = $this->call_trigger('ORDER_VALIDATE', $user); if ($result < 0) $error++; // End call triggers } - if (!$error) - { + if (!$error) { $this->oldref = $this->ref; // Rename directory if dir was a temporary ref From a03de433be4e962a7af509d945781e26cf572e0c Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Wed, 16 Dec 2020 05:03:37 +0100 Subject: [PATCH 102/743] FIX: Accountancy - Quadra export format - Manage negative amount --- .../class/accountancyexport.class.php | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/htdocs/accountancy/class/accountancyexport.class.php b/htdocs/accountancy/class/accountancyexport.class.php index f15a2a0c704..899eddc0e56 100644 --- a/htdocs/accountancy/class/accountancyexport.class.php +++ b/htdocs/accountancy/class/accountancyexport.class.php @@ -505,9 +505,9 @@ class AccountancyExport $end_line = "\r\n"; - //We should use dol_now function not time however this is wrong date to transfert in accounting - //$date_ecriture = dol_print_date(dol_now(), $conf->global->ACCOUNTING_EXPORT_DATE); // format must be ddmmyy - //$date_ecriture = dol_print_date(time(), $conf->global->ACCOUNTING_EXPORT_DATE); // format must be ddmmyy + // We should use dol_now function not time however this is wrong date to transfert in accounting + // $date_ecriture = dol_print_date(dol_now(), $conf->global->ACCOUNTING_EXPORT_DATE); // format must be ddmmyy + // $date_ecriture = dol_print_date(time(), $conf->global->ACCOUNTING_EXPORT_DATE); // format must be ddmmyy foreach ($TData as $data) { $code_compta = $data->numero_compte; if (!empty($data->subledger_account)) @@ -519,36 +519,46 @@ class AccountancyExport $Tab['code_journal'] = str_pad(self::trunc($data->code_journal, 2), 2); $Tab['folio'] = '000'; - //We use invoice date $data->doc_date not $date_ecriture which is the transfert date - //maybe we should set an option for customer who prefer to keep in accounting software the tranfert date instead of invoice date ? + // We use invoice date $data->doc_date not $date_ecriture which is the transfert date + // maybe we should set an option for customer who prefer to keep in accounting software the tranfert date instead of invoice date ? //$Tab['date_ecriture'] = $date_ecriture; $Tab['date_ecriture'] = dol_print_date($data->doc_date, '%d%m%y'); $Tab['filler'] = ' '; $Tab['libelle_ecriture'] = str_pad(self::trunc(dol_string_unaccent($data->doc_ref).' '.dol_string_unaccent($data->label_operation), 20), 20); - $Tab['sens'] = $data->sens; // C or D - $Tab['signe_montant'] = '+'; - //elarifr le montant doit etre en centimes sans point decimal ! - $Tab['montant'] = str_pad(abs($data->montant * 100), 12, '0', STR_PAD_LEFT); // TODO manage negative amount - // $Tab['montant'] = str_pad(abs($data->montant), 12, '0', STR_PAD_LEFT); // TODO manage negative amount + // Credit invoice - invert sens + if ($data->montant < 0) { + if ($data->sens == 'C') { + $Tab['sens'] = 'D'; + } else { + $Tab['sens'] = 'C'; + } + $Tab['signe_montant'] = '-'; + } else { + $Tab['sens'] = $data->sens; // C or D + $Tab['signe_montant'] = '+'; + } + + // The amount must be in centimes without decimal points. + $Tab['montant'] = str_pad(abs($data->montant * 100), 12, '0', STR_PAD_LEFT); $Tab['contrepartie'] = str_repeat(' ', 8); - // elarifr: date format must be fixed format : 6 char ddmmyy = %d%m%yand not defined by user / dolibarr setting + // Force date format : %d%m%y if (!empty($data->date_echeance)) { //$Tab['date_echeance'] = dol_print_date($data->date_echeance, $conf->global->ACCOUNTING_EXPORT_DATE); - $Tab['date_echeance'] = dol_print_date($data->date_echeance, '%d%m%y'); // elarifr: format must be ddmmyy + $Tab['date_echeance'] = dol_print_date($data->date_echeance, '%d%m%y'); // Format must be ddmmyy } else { $Tab['date_echeance'] = '000000'; } - //elarifr please keep quadra named field lettrage(2) + codestat(3) instead of fake lettrage(5) - //$Tab['lettrage'] = str_repeat(' ', 5); + // Please keep quadra named field lettrage(2) + codestat(3) instead of fake lettrage(5) + // $Tab['lettrage'] = str_repeat(' ', 5); $Tab['lettrage'] = str_repeat(' ', 2); $Tab['codestat'] = str_repeat(' ', 3); $Tab['num_piece'] = str_pad(self::trunc($data->piece_num, 5), 5); - //elarifr keep correct quadra named field instead of anon filler - //$Tab['filler2'] = str_repeat(' ', 20); + // Keep correct quadra named field instead of anon filler + // $Tab['filler2'] = str_repeat(' ', 20); $Tab['affaire'] = str_repeat(' ', 10); $Tab['quantity1'] = str_repeat(' ', 10); $Tab['num_piece2'] = str_pad(self::trunc($data->piece_num, 8), 8); @@ -556,17 +566,17 @@ class AccountancyExport $Tab['code_journal2'] = str_pad(self::trunc($data->code_journal, 3), 3); $Tab['filler3'] = str_repeat(' ', 3); - //elarifr keep correct quadra named field instead of anon filler libelle_ecriture2 is 30 char not 32 !!!! - //as we use utf8, we must remove accent to have only one ascii char instead of utf8 2 chars for specials that report wrong line size that will exceed import format spec - //todo we should filter more than only accent to avoid wrong line size - //TODO: remove invoice number doc_ref in libelle, - //TODO: we should offer an option for customer to build the libelle using invoice number / name / date in accounting software + // Keep correct quadra named field instead of anon filler libelle_ecriture2 is 30 char not 32 !!!! + // as we use utf8, we must remove accent to have only one ascii char instead of utf8 2 chars for specials that report wrong line size that will exceed import format spec + // TODO: we should filter more than only accent to avoid wrong line size + // TODO: remove invoice number doc_ref in libelle, + // TODO: we should offer an option for customer to build the libelle using invoice number / name / date in accounting software //$Tab['libelle_ecriture2'] = str_pad(self::trunc(dol_string_unaccent($data->doc_ref) . ' ' . dol_string_unaccent($data->label_operation), 30), 30); $Tab['libelle_ecriture2'] = str_pad(self::trunc(dol_string_unaccent($data->label_operation), 30), 30); $Tab['codetva'] = str_repeat(' ', 2); - //elarifr we need to keep the 10 lastest number of invoice doc_ref not the beginning part that is the unusefull almost same part - //$Tab['num_piece3'] = str_pad(self::trunc($data->piece_num, 10), 10); + // We need to keep the 10 lastest number of invoice doc_ref not the beginning part that is the unusefull almost same part + // $Tab['num_piece3'] = str_pad(self::trunc($data->piece_num, 10), 10); $Tab['num_piece3'] = substr(self::trunc($data->doc_ref, 20), -10); $Tab['filler4'] = str_repeat(' ', 73); From ca40d5e52ada5507534c91a107374b5c0219a42b Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Wed, 16 Dec 2020 05:16:08 +0100 Subject: [PATCH 103/743] NEW: Accountancy - Introduce FEC "2" Format to resolve conflict with some software --- .../class/accountancyexport.class.php | 109 ++++++++++++++++++ htdocs/langs/en_US/accountancy.lang | 1 + 2 files changed, 110 insertions(+) diff --git a/htdocs/accountancy/class/accountancyexport.class.php b/htdocs/accountancy/class/accountancyexport.class.php index f15a2a0c704..b9c4cc9c8ff 100644 --- a/htdocs/accountancy/class/accountancyexport.class.php +++ b/htdocs/accountancy/class/accountancyexport.class.php @@ -60,6 +60,7 @@ class AccountancyExport public static $EXPORT_TYPE_GESTINUMV3 = 130; public static $EXPORT_TYPE_GESTINUMV5 = 135; public static $EXPORT_TYPE_FEC = 1000; + public static $EXPORT_TYPE_FEC2 = 1010; /** @@ -121,6 +122,7 @@ class AccountancyExport self::$EXPORT_TYPE_GESTINUMV3 => $langs->trans('Modelcsv_Gestinum_v3'), self::$EXPORT_TYPE_GESTINUMV5 => $langs->trans('Modelcsv_Gestinum_v5'), self::$EXPORT_TYPE_FEC => $langs->trans('Modelcsv_FEC'), + self::$EXPORT_TYPE_FEC2 => $langs->trans('Modelcsv_FEC2'), ); ksort($listofexporttypes, SORT_NUMERIC); @@ -155,6 +157,7 @@ class AccountancyExport self::$EXPORT_TYPE_GESTINUMV3 => 'gestinumv3', self::$EXPORT_TYPE_GESTINUMV5 => 'gestinumv5', self::$EXPORT_TYPE_FEC => 'fec', + self::$EXPORT_TYPE_FEC2 => 'fec2', ); return $formatcode[$type]; @@ -234,6 +237,10 @@ class AccountancyExport 'label' => $langs->trans('Modelcsv_FEC'), 'ACCOUNTING_EXPORT_FORMAT' => 'txt', ), + self::$EXPORT_TYPE_FEC2 => array( + 'label' => $langs->trans('Modelcsv_FEC2'), + 'ACCOUNTING_EXPORT_FORMAT' => 'txt', + ), ), 'cr'=> array( '1' => $langs->trans("Unix"), @@ -322,6 +329,9 @@ class AccountancyExport case self::$EXPORT_TYPE_FEC : $this->exportFEC($TData); break; + case self::$EXPORT_TYPE_FEC2 : + $this->exportFEC2($TData); + break; default: $this->errors[] = $langs->trans('accountancy_error_modelnotfound'); break; @@ -891,6 +901,105 @@ class AccountancyExport } } + /** + * Export format : FEC2 + * + * @param array $objectLines data + * @return void + */ + public function exportFEC2($objectLines) + { + $separator = "\t"; + $end_line = "\r\n"; + + print "JournalCode".$separator; + print "JournalLib".$separator; + print "EcritureNum".$separator; + print "EcritureDate".$separator; + print "CompteNum".$separator; + print "CompteLib".$separator; + print "CompAuxNum".$separator; + print "CompAuxLib".$separator; + print "PieceRef".$separator; + print "PieceDate".$separator; + print "EcritureLib".$separator; + print "Debit".$separator; + print "Credit".$separator; + print "EcritureLet".$separator; + print "DateLet".$separator; + print "ValidDate".$separator; + print "Montantdevise".$separator; + print "Idevise"; + print $end_line; + + foreach ($objectLines as $line) { + if ($line->debit == 0 && $line->credit == 0) { + unset($array[$line]); + } else { + $date_creation = dol_print_date($line->date_creation, '%Y%m%d'); + $date_document = dol_print_date($line->doc_date, '%Y%m%d'); + $date_lettering = dol_print_date($line->date_lettering, '%Y%m%d'); + $date_validation = dol_print_date($line->date_validated, '%Y%m%d'); + + // FEC:JournalCode + print $line->code_journal . $separator; + + // FEC:JournalLib + print $line->journal_label . $separator; + + // FEC:EcritureNum + print $line->piece_num . $separator; + + // FEC:EcritureDate + print $date_creation . $separator; + + // FEC:CompteNum + print $line->numero_compte . $separator; + + // FEC:CompteLib + print dol_string_unaccent($line->label_compte) . $separator; + + // FEC:CompAuxNum + print $line->subledger_account . $separator; + + // FEC:CompAuxLib + print dol_string_unaccent($line->subledger_label) . $separator; + + // FEC:PieceRef + print $line->doc_ref . $separator; + + // FEC:PieceDate + print $date_document . $separator; + + // FEC:EcritureLib + print dol_string_unaccent($line->label_operation) . $separator; + + // FEC:Debit + print price2fec($line->debit) . $separator; + + // FEC:Credit + print price2fec($line->credit) . $separator; + + // FEC:EcritureLet + print $line->lettering_code . $separator; + + // FEC:DateLet + print $date_lettering . $separator; + + // FEC:ValidDate + print $date_validation . $separator; + + // FEC:Montantdevise + print $line->multicurrency_amount . $separator; + + // FEC:Idevise + print $line->multicurrency_code; + + print $end_line; + } + } + } + /** * Export format : SAGE50SWISS * diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 2a53a171962..83d20f59581 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -340,6 +340,7 @@ Modelcsv_LDCompta10=Export for LD Compta (v10 & higher) Modelcsv_openconcerto=Export for OpenConcerto (Test) Modelcsv_configurable=Export CSV Configurable Modelcsv_FEC=Export FEC +Modelcsv_FEC2=Export FEC (With dates generation writing / document reversed) Modelcsv_Sage50_Swiss=Export for Sage 50 Switzerland Modelcsv_winfic=Export Winfic - eWinfic - WinSis Compta Modelcsv_Gestinumv3=Export for Gestinum (v3) From b01a9d6cc307911886869ba8a4aeffee205d4b48 Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Wed, 16 Dec 2020 05:32:36 +0100 Subject: [PATCH 104/743] FIX: Accountancy - Reconciled options not working on ledger et subaccount ledger --- .../accountancy/bookkeeping/listbyaccount.php | 26 +++++++++++++---- .../bookkeeping/listbysubaccount.php | 28 ++++++++++++++----- .../accountancy/class/bookkeeping.class.php | 4 +++ 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/htdocs/accountancy/bookkeeping/listbyaccount.php b/htdocs/accountancy/bookkeeping/listbyaccount.php index 7b8467f60bd..52bedeb73f2 100644 --- a/htdocs/accountancy/bookkeeping/listbyaccount.php +++ b/htdocs/accountancy/bookkeeping/listbyaccount.php @@ -543,8 +543,15 @@ while ($i < min($num, $limit)) // Is it a break ? if ($accountg != $displayed_account_number || !isset($displayed_account_number)) { - $colspan = $totalarray['nbfield'] - 3; - $colspanend = $totalarray['nbfield'] - 7; + if (empty($conf->global->ACCOUNTING_ENABLE_LETTERING) || empty($arrayfields['t.lettering_code']['checked'])) { + $colnumber = 3; + $colnumberend = 7; + } else { + $colnumber = 4; + $colnumberend = 7; + } + $colspan = $totalarray['nbfield'] - $colnumber; + $colspanend = $totalarray['nbfield'] - $colnumberend; // Show a subtotal by accounting account if (isset($displayed_account_number)) { print '
'; @@ -665,11 +672,11 @@ while ($i < min($num, $limit)) // Other type } - print '"; $texte .= '
'; - $entry .= ''.$categstatic->getNomUrl(1, '', 60).''; + $entry .= ''.$li.''; $entry .= ''.$counter.''; - $entry .= ''.img_view().''; + $entry .= ''.img_view().''; + $entry .= ''; + $entry .= ''.img_edit().''; + $entry .= ''; + $entry .= ''.img_delete().''; $entry .= '
trans("LanguageFile"); ?>
textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc"), 1, 'help', '', 0, 2, 'tooltipcompute'); ?>
textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc"), 1, 'help', '', 0, 2, 'tooltipcompute'); ?>
textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc")).$form->textwithpicto($langs->trans("Computedpersistent"), $langs->trans("ComputedpersistentDesc"), 1, 'warning'); ?>
textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc")).$form->textwithpicto($langs->trans("Computedpersistent"), $langs->trans("ComputedpersistentDesc"), 1, 'warning'); ?>
trans("DefaultValue").' ('.$langs->trans("Database").')'; ?>
trans("LanguageFile"); ?>
textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc"), 1, 'help', '', 0, 2, 'tooltipcompute'); ?>
textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc"), 1, 'help', '', 0, 2, 'tooltipcompute'); ?>
textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc")).$form->textwithpicto($langs->trans("Computedpersistent"), $langs->trans("ComputedpersistentDesc"), 1, 'warning'); ?>
textwithpicto($langs->trans("ComputedFormula"), $langs->trans("ComputedFormulaDesc")).$form->textwithpicto($langs->trans("Computedpersistent"), $langs->trans("ComputedpersistentDesc"), 1, 'warning'); ?>
trans("DefaultValue").' ('.$langs->trans("Database").')'; ?>
'; + print ''; print ''; // Picto + Ref - print ''; print ''; print ''; diff --git a/htdocs/accountancy/bookkeeping/listbysubaccount.php b/htdocs/accountancy/bookkeeping/listbysubaccount.php index 48c449df43e..2bdf5e11d09 100644 --- a/htdocs/accountancy/bookkeeping/listbysubaccount.php +++ b/htdocs/accountancy/bookkeeping/listbysubaccount.php @@ -410,7 +410,7 @@ if (empty($reshook)) { if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param .= '&contextpage='.urlencode($contextpage); if ($limit > 0 && $limit != $conf->liste_limit) $param .= '&limit='.urlencode($limit); -print_barre_liste($title_page, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $result, $nbtotalofrecords, 'title_accountancy', 0, $newcardbutton, '', $limit); +print_barre_liste($title_page, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $result, $nbtotalofrecords, 'title_accountancy', 0, $newcardbutton, '', $limit, 0, 0, 1); print info_admin($langs->trans("WarningRecordWithoutSubledgerAreExcluded")); @@ -546,8 +546,15 @@ while ($i < min($num, $limit)) // Is it a break ? if ($accountg != $displayed_account_number || !isset($displayed_account_number)) { - $colspan = $totalarray['nbfield'] - 3; - $colspanend = $totalarray['nbfield'] - 7; + if (empty($conf->global->ACCOUNTING_ENABLE_LETTERING) || empty($arrayfields['t.lettering_code']['checked'])) { + $colnumber = 3; + $colnumberend = 7; + } else { + $colnumber = 4; + $colnumberend = 7; + } + $colspan = $totalarray['nbfield'] - $colnumber; + $colspanend = $totalarray['nbfield'] - $colnumberend; // Show a subtotal by accounting account if (isset($displayed_account_number)) { print ''; @@ -676,11 +683,11 @@ while ($i < min($num, $limit)) // Other type } - print '
'; + print ''; if ($line->doc_type == 'customer_invoice' || $line->doc_type == 'supplier_invoice' || $line->doc_type == 'expense_report') { @@ -744,8 +751,15 @@ while ($i < min($num, $limit)) } // Show sub-total of last shown account -$colspan = $totalarray['nbfield'] - 3; -$colspanend = $totalarray['nbfield'] - 8; +if (empty($conf->global->ACCOUNTING_ENABLE_LETTERING) || empty($arrayfields['t.lettering_code']['checked'])) { + $colnumber = 3; + $colnumberend = 7; +} else { + $colnumber = 4; + $colnumberend = 7; +} +$colspan = $totalarray['nbfield'] - $colnumber; +$colspanend = $totalarray['nbfield'] - $colnumberend; print '
'.$langs->trans("TotalForAccount").' '.$accountg.':'.price($sous_total_debit).'
'; + print ''; print ''; // Picto + Ref - print ''; print ''; print ''; diff --git a/htdocs/accountancy/class/bookkeeping.class.php b/htdocs/accountancy/class/bookkeeping.class.php index 8312b155b8b..72428762459 100644 --- a/htdocs/accountancy/class/bookkeeping.class.php +++ b/htdocs/accountancy/class/bookkeeping.class.php @@ -825,6 +825,10 @@ class BookKeeping extends CommonObject $sqlwhere[] = $key.'\''.$this->db->idate($value).'\''; } elseif ($key == 't.credit' || $key == 't.debit') { $sqlwhere[] = natural_search($key, $value, 1, 1); + } elseif ($key == 't.reconciled_option') { + $sqlwhere[] = 't.lettering_code IS NULL'; + } elseif ($key == 't.code_journal' && !empty($value)) { + $sqlwhere[] = natural_search("t.code_journal", join(',', $value), 3, 1); } else { $sqlwhere[] = natural_search($key, $value, 0, 1); } From c5ee976c7ea63ec1c7ea7b430fba788187eac600 Mon Sep 17 00:00:00 2001 From: Alexandre SPANGARO Date: Wed, 16 Dec 2020 06:38:17 +0100 Subject: [PATCH 105/743] NEW: Accountancy - In ledger & journals, show link on bank transaction --- htdocs/accountancy/bookkeeping/list.php | 9 +++++++++ htdocs/accountancy/bookkeeping/listbyaccount.php | 9 +++++++++ htdocs/accountancy/bookkeeping/listbysubaccount.php | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/htdocs/accountancy/bookkeeping/list.php b/htdocs/accountancy/bookkeeping/list.php index b3cec7710a4..ebd768de1dc 100644 --- a/htdocs/accountancy/bookkeeping/list.php +++ b/htdocs/accountancy/bookkeeping/list.php @@ -937,6 +937,11 @@ while ($i < min($num, $limit)) $filedir = $conf->expensereport->dir_output.'/'.dol_sanitizeFileName($line->doc_ref); $urlsource = $_SERVER['PHP_SELF'].'?id='.$objectstatic->id; $documentlink = $formfile->getDocumentsLink($objectstatic->element, $filename, $filedir); + } elseif ($line->doc_type == 'bank') + { + require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; + $objectstatic = new AccountLine($db); + $objectstatic->fetch($line->fk_doc); } else { // Other type } @@ -951,6 +956,10 @@ while ($i < min($num, $limit)) { print $objectstatic->getNomUrl(1, '', 0, 0, '', 0, -1, 1); print $documentlink; + } elseif ($line->doc_type == 'bank') { + print $objectstatic->getNomUrl(1); + $bank_ref = strstr($line->doc_ref, '-'); + print " " . $bank_ref; } else { print $line->doc_ref; } diff --git a/htdocs/accountancy/bookkeeping/listbyaccount.php b/htdocs/accountancy/bookkeeping/listbyaccount.php index 7b8467f60bd..21ae4d9a22f 100644 --- a/htdocs/accountancy/bookkeeping/listbyaccount.php +++ b/htdocs/accountancy/bookkeeping/listbyaccount.php @@ -661,6 +661,11 @@ while ($i < min($num, $limit)) $filedir = $conf->expensereport->dir_output.'/'.dol_sanitizeFileName($line->doc_ref); $urlsource = $_SERVER['PHP_SELF'].'?id='.$objectstatic->id; $documentlink = $formfile->getDocumentsLink($objectstatic->element, $filename, $filedir); + } elseif ($line->doc_type == 'bank') + { + require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; + $objectstatic = new AccountLine($db); + $objectstatic->fetch($line->fk_doc); } else { // Other type } @@ -675,6 +680,10 @@ while ($i < min($num, $limit)) { print $objectstatic->getNomUrl(1, '', 0, 0, '', 0, -1, 1); print $documentlink; + } elseif ($line->doc_type == 'bank') { + print $objectstatic->getNomUrl(1); + $bank_ref = strstr($line->doc_ref, '-'); + print " " . $bank_ref; } else { print $line->doc_ref; } diff --git a/htdocs/accountancy/bookkeeping/listbysubaccount.php b/htdocs/accountancy/bookkeeping/listbysubaccount.php index 48c449df43e..535f1e94937 100644 --- a/htdocs/accountancy/bookkeeping/listbysubaccount.php +++ b/htdocs/accountancy/bookkeeping/listbysubaccount.php @@ -672,6 +672,11 @@ while ($i < min($num, $limit)) $filedir = $conf->expensereport->dir_output.'/'.dol_sanitizeFileName($line->doc_ref); $urlsource = $_SERVER['PHP_SELF'].'?id='.$objectstatic->id; $documentlink = $formfile->getDocumentsLink($objectstatic->element, $filename, $filedir); + } elseif ($line->doc_type == 'bank') + { + require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; + $objectstatic = new AccountLine($db); + $objectstatic->fetch($line->fk_doc); } else { // Other type } @@ -686,6 +691,10 @@ while ($i < min($num, $limit)) { print $objectstatic->getNomUrl(1, '', 0, 0, '', 0, -1, 1); print $documentlink; + } elseif ($line->doc_type == 'bank') { + print $objectstatic->getNomUrl(1); + $bank_ref = strstr($line->doc_ref, '-'); + print " " . $bank_ref; } else { print $line->doc_ref; } From 02a15e03f7f3501bc3cc48786b95f8874a86ddf3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 10:37:34 +0100 Subject: [PATCH 106/743] Fix phpunit --- test/phpunit/RestAPIDocumentTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/phpunit/RestAPIDocumentTest.php b/test/phpunit/RestAPIDocumentTest.php index f45e0437ce5..e38f1fbae12 100644 --- a/test/phpunit/RestAPIDocumentTest.php +++ b/test/phpunit/RestAPIDocumentTest.php @@ -173,7 +173,7 @@ class RestAPIDocumentTest extends PHPUnit\Framework\TestCase ); $param = ''; - foreach($data as $key => $val) { + foreach ($data as $key => $val) { $param .= '&'.$key.'='.urlencode($val); } @@ -203,7 +203,7 @@ class RestAPIDocumentTest extends PHPUnit\Framework\TestCase ); $param = ''; - foreach($data as $key => $val) { + foreach ($data as $key => $val) { $param .= '&'.$key.'='.urlencode($val); } @@ -231,7 +231,7 @@ class RestAPIDocumentTest extends PHPUnit\Framework\TestCase ); $param = ''; - foreach($data as $key => $val) { + foreach ($data as $key => $val) { $param .= '&'.$key.'='.urlencode($val); } From 1b69b1c2fed2a347794a3ae0781dfead7cbb2ffa Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 10:39:10 +0100 Subject: [PATCH 107/743] Fix phpcs --- htdocs/commande/class/commande.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/commande/class/commande.class.php b/htdocs/commande/class/commande.class.php index 00cba2055f7..4ededdeacaf 100644 --- a/htdocs/commande/class/commande.class.php +++ b/htdocs/commande/class/commande.class.php @@ -624,7 +624,7 @@ class Commande extends CommonOrder } // If stock is decremented on validate order, we must reincrement it - if (!empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) + if (!empty($conf->stock->enabled) && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) { $result = 0; @@ -810,7 +810,7 @@ class Commande extends CommonOrder if ($this->db->query($sql)) { // If stock is decremented on validate order, we must reincrement it - if (!empty($conf->stock->enabled) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) + if (!empty($conf->stock->enabled) && !empty($conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER) && $conf->global->STOCK_CALCULATE_ON_VALIDATE_ORDER == 1) { require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php'; $langs->load("agenda"); From cb2c1f3ecef1537f40617b4c2796b2a5a7c5f923 Mon Sep 17 00:00:00 2001 From: atm-lena Date: Wed, 16 Dec 2020 10:54:32 +0100 Subject: [PATCH 108/743] FIX Facture Situation Out : status condition --- htdocs/compta/facture/card.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index fb5f7a6ac98..aa07de4867e 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -2534,7 +2534,7 @@ if (empty($reshook)) { $object->fetch($id, '', '', '', true); - if ($object->statut == Facture::STATUS_VALIDATED + if (in_array($object->statut, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED)) && $object->type == Facture::TYPE_SITUATION && $usercancreate && !$objectidnext @@ -3872,7 +3872,7 @@ elseif ($id > 0 || !empty($ref)) $label = $langs->trans("ConfirmOuting"); $formquestion = array(); // remove situation from cycle - if ($object->statut == Facture::STATUS_VALIDATED + if (in_array($object->statut, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED)) && $usercancreate && !$objectidnext && $object->is_last_in_cycle() @@ -5387,7 +5387,7 @@ elseif ($id > 0 || !empty($ref)) } // Remove situation from cycle - if ($object->statut > Facture::STATUS_DRAFT + if (in_array($object->statut, array(Facture::STATUS_CLOSED, Facture::STATUS_VALIDATED)) && $object->type == Facture::TYPE_SITUATION && $usercancreate && !$objectidnext From bf01ad8790237fb28c39c12e6798b3c3567eaeb7 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 10:55:33 +0100 Subject: [PATCH 109/743] Fix warning --- htdocs/compta/facture/class/facture.class.php | 6 +++--- htdocs/fourn/class/fournisseur.facture.class.php | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index 5f7bf4fcf77..ec55810e2da 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -868,7 +868,7 @@ class Facture extends CommonInvoice $line->situation_percent, $line->fk_prev_id, $line->fk_unit, - $line->pu_ht_devise, + $line->multicurrency_subprice, $line->ref_ext ); if ($result < 0) @@ -968,7 +968,7 @@ class Facture extends CommonInvoice $_facrec->lines[$i]->situation_percent, '', $_facrec->lines[$i]->fk_unit, - $_facrec->lines[$i]->pu_ht_devise + $_facrec->lines[$i]->multicurrency_subprice ); if ($result_insert < 0) @@ -2971,7 +2971,7 @@ class Facture extends CommonInvoice * @param int $situation_percent Situation advance percentage * @param int $fk_prev_id Previous situation line id reference * @param string $fk_unit Code of the unit to use. Null to use the default one - * @param double $pu_ht_devise Unit price in currency + * @param double $pu_ht_devise Unit price in foreign currency * @param string $ref_ext External reference of the line * @return int <0 if KO, Id of line if OK */ diff --git a/htdocs/fourn/class/fournisseur.facture.class.php b/htdocs/fourn/class/fournisseur.facture.class.php index e3b65d8a8de..43075ba6760 100644 --- a/htdocs/fourn/class/fournisseur.facture.class.php +++ b/htdocs/fourn/class/fournisseur.facture.class.php @@ -398,7 +398,7 @@ class FactureFournisseur extends CommonInvoice $sql .= ", '".$this->db->escape($this->ref_supplier)."'"; $sql .= ", ".$conf->entity; $sql .= ", '".$this->db->escape($this->type)."'"; - $sql .= ", '".$this->db->escape($this->label ? $this->label : $this->libelle)."'"; + $sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'"; $sql .= ", ".$this->socid; $sql .= ", '".$this->db->idate($now)."'"; $sql .= ", '".$this->db->idate($this->date)."'"; @@ -497,7 +497,7 @@ class FactureFournisseur extends CommonInvoice $this->lines[$i]->date_end, $this->lines[$i]->array_options, $this->lines[$i]->fk_unit, - $this->lines[$i]->pu_ht_devise + $this->lines[$i]->multicurrency_subprice ); } else { $this->error = $this->db->lasterror(); @@ -2467,7 +2467,7 @@ class FactureFournisseur extends CommonInvoice $xnbp = 0; while ($xnbp < $nbp) { - $line = new FactureLigne($this->db); + $line = new SupplierInvoiceLine($this->db); $line->desc = $langs->trans("Description")." ".$xnbp; $line->qty = 1; $line->subprice = 100; @@ -2807,8 +2807,7 @@ class SupplierInvoiceLine extends CommonObjectLine public $fk_facture_fourn; /** - * Product label - * This field may contains label of product (when invoice create from order) + * This field may contains label of line (when invoice create from order) * @var string */ public $label; @@ -2961,6 +2960,7 @@ class SupplierInvoiceLine extends CommonObjectLine public $multicurrency_total_tva; public $multicurrency_total_ttc; + /** * Constructor * From 2c7d8723202d988cd984d019525f950ea513fed4 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 11:07:34 +0100 Subject: [PATCH 110/743] Fix warning --- htdocs/compta/facture/class/facture-rec.class.php | 10 +++++----- htdocs/core/lib/functions.lib.php | 2 +- htdocs/fourn/class/fournisseur.facture.class.php | 14 ++++++++------ 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/htdocs/compta/facture/class/facture-rec.class.php b/htdocs/compta/facture/class/facture-rec.class.php index 48e2ad574ed..c9157f7b43d 100644 --- a/htdocs/compta/facture/class/facture-rec.class.php +++ b/htdocs/compta/facture/class/facture-rec.class.php @@ -215,7 +215,7 @@ class FactureRec extends CommonInvoice $now = dol_now(); // Clean parameters - $this->titre = trim($this->titre); // deprecated + $this->titre = trim(isset($this->titre) ? $this->titre : $this->title); // deprecated $this->title = trim($this->title); $this->usenewprice = empty($this->usenewprice) ? 0 : $this->usenewprice; if (empty($this->suspended)) $this->suspended = 0; @@ -366,7 +366,7 @@ class FactureRec extends CommonInvoice } // Add object linked - if (!$error && $this->id && is_array($this->linked_objects) && !empty($this->linked_objects)) + if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) { foreach ($this->linked_objects as $origin => $tmp_origin_id) { @@ -702,7 +702,7 @@ class FactureRec extends CommonInvoice $line->total_ht = $objp->total_ht; $line->total_tva = $objp->total_tva; $line->total_ttc = $objp->total_ttc; - $line->code_ventilation = $objp->fk_code_ventilation; + //$line->code_ventilation = $objp->fk_code_ventilation; $line->fk_fournprice = $objp->fk_fournprice; $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht); $line->pa_ht = $marginInfos[0]; @@ -1996,7 +1996,7 @@ class FactureLigneRec extends CommonInvoiceLine $this->localtax1_type = $objp->localtax1_type; $this->localtax2_type = $objp->localtax2_type; $this->remise_percent = $objp->remise_percent; - $this->fk_remise_except = $objp->fk_remise_except; + //$this->fk_remise_except = $objp->fk_remise_except; $this->fk_product = $objp->fk_product; $this->date_start_fill = $objp->date_start_fill; $this->date_end_fill = $objp->date_end_fill; @@ -2004,7 +2004,7 @@ class FactureLigneRec extends CommonInvoiceLine $this->total_ht = $objp->total_ht; $this->total_tva = $objp->total_tva; $this->total_ttc = $objp->total_ttc; - $this->code_ventilation = $objp->fk_code_ventilation; + //$this->code_ventilation = $objp->fk_code_ventilation; $this->rang = $objp->rang; $this->special_code = $objp->special_code; $this->fk_unit = $objp->fk_unit; diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 4fd44ee6ba3..998cddea0f9 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -5191,7 +5191,7 @@ function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisi $sql .= ", ".MAIN_DB_PREFIX."c_country as c"; if ($mysoc->country_code == 'ES') $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // local tax in spain use the buyer country ?? - else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'"; + else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape(empty($seller->country_code) ? $mysoc->country_code : $seller->country_code)."'"; $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1"; if ($vatratecode) $sql .= " AND t.code = '".$db->escape($vatratecode)."'"; } diff --git a/htdocs/fourn/class/fournisseur.facture.class.php b/htdocs/fourn/class/fournisseur.facture.class.php index 43075ba6760..40ffb9024c5 100644 --- a/htdocs/fourn/class/fournisseur.facture.class.php +++ b/htdocs/fourn/class/fournisseur.facture.class.php @@ -1856,6 +1856,7 @@ class FactureFournisseur extends CommonInvoice public function updateline($id, $desc, $pu, $vatrate, $txlocaltax1 = 0, $txlocaltax2 = 0, $qty = 1, $idproduct = 0, $price_base_type = 'HT', $info_bits = 0, $type = 0, $remise_percent = 0, $notrigger = false, $date_start = '', $date_end = '', $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $ref_supplier = '') { global $mysoc, $langs; + dol_syslog(get_class($this)."::updateline $id,$desc,$pu,$vatrate,$qty,$idproduct,$price_base_type,$info_bits,$type,$remise_percent,$notrigger,$date_start,$date_end,$fk_unit,$pu_ht_devise,$ref_supplier", LOG_DEBUG); include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php'; @@ -2809,6 +2810,7 @@ class SupplierInvoiceLine extends CommonObjectLine /** * This field may contains label of line (when invoice create from order) * @var string + * @deprecated */ public $label; @@ -3010,17 +3012,17 @@ class SupplierInvoiceLine extends CommonObjectLine $this->date_end = $obj->date_end; $this->product_ref = $obj->product_ref; $this->ref_supplier = $obj->ref_supplier; - $this->libelle = $obj->label; - $this->label = $obj->label; $this->product_desc = $obj->product_desc; - $this->subprice = $obj->pu_ht; - $this->pu_ht = $obj->pu_ht; + + $this->subprice = $obj->pu_ht; + $this->pu_ht = $obj->pu_ht; $this->pu_ttc = $obj->pu_ttc; $this->tva_tx = $obj->tva_tx; $this->localtax1_tx = $obj->localtax1_tx; $this->localtax2_tx = $obj->localtax2_tx; - $this->localtax1_type = $obj->localtax1_type; - $this->localtax2_type = $obj->localtax2_type; + $this->localtax1_type = $obj->localtax1_type; + $this->localtax2_type = $obj->localtax2_type; + $this->qty = $obj->qty; $this->remise_percent = $obj->remise_percent; $this->tva = $obj->total_tva; // deprecated From c6f83aaed72a25228047b9f2afb452ac3f86d8ad Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 11:21:48 +0100 Subject: [PATCH 111/743] Fix warning --- htdocs/compta/bank/class/account.class.php | 36 ++++++++++++++-------- test/phpunit/BankAccountTest.php | 4 ++- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/htdocs/compta/bank/class/account.class.php b/htdocs/compta/bank/class/account.class.php index e93491ceee4..b2d499c5f34 100644 --- a/htdocs/compta/bank/class/account.class.php +++ b/htdocs/compta/bank/class/account.class.php @@ -1741,13 +1741,6 @@ class AccountLine extends CommonObject public $amount; - /** - * @var string bank transaction lines label - */ - public $label; - - public $note; - /** * @var int ID */ @@ -1763,11 +1756,6 @@ class AccountLine extends CommonObject */ public $fk_type; - public $rappro; // Is it conciliated - public $num_releve; // If conciliated, what is bank statement - public $num_chq; // Num of cheque - public $bank_chq; // Bank of cheque - /** * @var int ID of cheque receipt */ @@ -1778,16 +1766,40 @@ class AccountLine extends CommonObject */ public $fk_account; + /** + * @var string Ref of bank account + */ + public $bank_account_ref; + /** * @var string Label of bank account */ public $bank_account_label; + /** + * @var string Bank account numero + */ + public $numero_compte; + /** * @var string Name of check issuer */ public $emetteur; + public $rappro; // Is it conciliated + public $num_releve; // If conciliated, what is bank statement + public $num_chq; // Num of cheque + public $bank_chq; // Bank of cheque + + /** + * @var string bank transaction lines label + */ + public $label; + + public $note; + + + /** * Constructor * diff --git a/test/phpunit/BankAccountTest.php b/test/phpunit/BankAccountTest.php index 1007db02dad..fd92d242b31 100644 --- a/test/phpunit/BankAccountTest.php +++ b/test/phpunit/BankAccountTest.php @@ -200,8 +200,10 @@ class BankAccountTest extends PHPUnit\Framework\TestCase */ $localobject->info($localobject->id); + + $result = $localobject->needIBAN(); //print __METHOD__." localobject->date_creation=".$localobject->date_creation."\n"; - //$this->assertNotEquals($localobject->date_creation, ''); + $this->assertEquals(1, $result); return $localobject->id; } From 5facd49a6c2ee4370783cffc8f37c10c25b71b61 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 11:33:31 +0100 Subject: [PATCH 112/743] Fix warnings --- htdocs/compta/sociales/class/chargesociales.class.php | 6 +++--- htdocs/product/stock/class/entrepot.class.php | 3 +-- test/phpunit/EntrepotTest.php | 3 ++- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/htdocs/compta/sociales/class/chargesociales.class.php b/htdocs/compta/sociales/class/chargesociales.class.php index 2ea2497efbb..ffb2d04e155 100644 --- a/htdocs/compta/sociales/class/chargesociales.class.php +++ b/htdocs/compta/sociales/class/chargesociales.class.php @@ -512,13 +512,13 @@ class ChargeSociales extends CommonObject * Return a link to the object card (with optionaly the picto) * * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) - * @param int $maxlen Max length of label + * @param string $option On what the link point to ('nolink', ...) * @param int $notooltip 1=Disable tooltip * @param int $short 1=Return just URL * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking * @return string String with link */ - public function getNomUrl($withpicto = 0, $maxlen = 0, $notooltip = 0, $short = 0, $save_lastsearch_value = -1) + public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $short = 0, $save_lastsearch_value = -1) { global $langs, $conf, $user, $form; @@ -569,7 +569,7 @@ class ChargeSociales extends CommonObject $result .= $linkstart; if ($withpicto) $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); - if ($withpicto != 2) $result .= ($maxlen ?dol_trunc($this->ref, $maxlen) : $this->ref); + if ($withpicto != 2) $result .= $this->ref; $result .= $linkend; return $result; diff --git a/htdocs/product/stock/class/entrepot.class.php b/htdocs/product/stock/class/entrepot.class.php index 14e25bad513..d7a52cd1910 100644 --- a/htdocs/product/stock/class/entrepot.class.php +++ b/htdocs/product/stock/class/entrepot.class.php @@ -166,8 +166,7 @@ class Entrepot extends CommonObject $this->db = $db; $this->statuts[self::STATUS_CLOSED] = 'Closed2'; - if ($conf->global->ENTREPOT_EXTRA_STATUS) - { + if (!empty($conf->global->ENTREPOT_EXTRA_STATUS)) { $this->statuts[self::STATUS_OPEN_ALL] = 'OpenAll'; $this->statuts[self::STATUS_OPEN_INTERNAL] = 'OpenInternal'; } else { diff --git a/test/phpunit/EntrepotTest.php b/test/phpunit/EntrepotTest.php index 27fd3dabbf3..8cad99243cc 100644 --- a/test/phpunit/EntrepotTest.php +++ b/test/phpunit/EntrepotTest.php @@ -219,7 +219,8 @@ class EntrepotTest extends PHPUnit\Framework\TestCase $langs=$this->savlangs; $db=$this->savdb; - //$this->assertLessThan(1, 0); + $result = $localobject->get_full_arbo(); + $this->assertEquals('WAREHOUSE SPECIMEN', $result); return $localobject->id; } From 371a4609055ef3661620ebba9ef69d6ecf9e8012 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 12:26:01 +0100 Subject: [PATCH 113/743] More PHPUnit for movements --- htdocs/product/class/product.class.php | 2 +- htdocs/product/class/productbatch.class.php | 9 ++- .../stock/class/mouvementstock.class.php | 43 +++++++------- test/phpunit/MouvementStockTest.php | 56 ++++++++++++++----- 4 files changed, 70 insertions(+), 40 deletions(-) diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index e1b25b9e3b7..a6d676a0f7b 100644 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -4893,7 +4893,7 @@ class Product extends CommonObject $sql .= " WHERE w.entity IN (".getEntity('stock').")"; $sql .= " AND w.rowid = ps.fk_entrepot"; $sql .= " AND ps.fk_product = ".$this->id; - if ($conf->global->ENTREPOT_EXTRA_STATUS && count($warehouseStatus)) { + if (!empty($conf->global->ENTREPOT_EXTRA_STATUS) && count($warehouseStatus)) { $sql .= " AND w.statut IN (".$this->db->sanitize($this->db->escape(implode(',', $warehouseStatus))).")"; } diff --git a/htdocs/product/class/productbatch.class.php b/htdocs/product/class/productbatch.class.php index f5a91ff2839..a97437f6a45 100644 --- a/htdocs/product/class/productbatch.class.php +++ b/htdocs/product/class/productbatch.class.php @@ -89,19 +89,18 @@ class Productbatch extends CommonObject // Insert request $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_batch ("; $sql .= "fk_product_stock,"; - $sql .= "sellby,"; - $sql .= "eatby,"; + $sql .= "sellby,"; // no more used + $sql .= "eatby,"; // no more used $sql .= "batch,"; $sql .= "qty,"; $sql .= "import_key"; $sql .= ") VALUES ("; $sql .= " ".(!isset($this->fk_product_stock) ? 'NULL' : $this->fk_product_stock).","; - $sql .= " ".(!isset($this->sellby) || dol_strlen($this->sellby) == 0 ? 'NULL' : "'".$this->db->idate($this->sellby)."'").","; - $sql .= " ".(!isset($this->eatby) || dol_strlen($this->eatby) == 0 ? 'NULL' : "'".$this->db->idate($this->eatby)."'").","; + $sql .= " ".(!isset($this->sellby) || dol_strlen($this->sellby) == 0 ? 'NULL' : "'".$this->db->idate($this->sellby)."'").","; // no more used + $sql .= " ".(!isset($this->eatby) || dol_strlen($this->eatby) == 0 ? 'NULL' : "'".$this->db->idate($this->eatby)."'").","; // no more used $sql .= " ".(!isset($this->batch) ? 'NULL' : "'".$this->db->escape($this->batch)."'").","; $sql .= " ".(!isset($this->qty) ? 'NULL' : $this->qty).","; $sql .= " ".(!isset($this->import_key) ? 'NULL' : "'".$this->db->escape($this->import_key)."'").""; - $sql .= ")"; $this->db->begin(); diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 631cedd0a25..a23846c3e08 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -140,8 +140,8 @@ class MouvementStock extends CommonObject * @param string $label Label of stock movement * @param string $inventorycode Inventory code * @param string $datem Force date of movement - * @param integer $eatby eat-by date. Will be used if lot does not exists yet and will be created. - * @param integer $sellby sell-by date. Will be used if lot does not exists yet and will be created. + * @param integer|string $eatby eat-by date. Will be used if lot does not exists yet and will be created. + * @param integer|string $sellby sell-by date. Will be used if lot does not exists yet and will be created. * @param string $batch batch number * @param boolean $skip_batch If set to true, stock movement is done without impacting batch record * @param int $id_product_batch Id product_batch (when skip_batch is false and we already know which record of product_batch to use) @@ -166,13 +166,14 @@ class MouvementStock extends CommonObject // Check parameters if (empty($fk_product)) return 0; - if ($eatby < 0) - { + + if (is_numeric($eatby) && $eatby < 0) { + dol_syslog(get_class($this)."::_create start ErrorBadValueForParameterEatBy eatby = ".$eatby); $this->errors[] = 'ErrorBadValueForParameterEatBy'; return -1; } - if ($sellby < 0) - { + if (is_numeric($sellby) && $sellby < 0) { + dol_syslog(get_class($this)."::_create start ErrorBadValueForParameterSellBy sellby = ".$sellby); $this->errors[] = 'ErrorBadValueForParameterSellBy'; return -1; } @@ -786,19 +787,19 @@ class MouvementStock extends CommonObject /** * Increase stock for product and subproducts * - * @param User $user Object user - * @param int $fk_product Id product - * @param int $entrepot_id Warehouse id - * @param int $qty Quantity - * @param int $price Price - * @param string $label Label of stock movement - * @param integer $eatby eat-by date - * @param integer $sellby sell-by date - * @param string $batch batch number - * @param string $datem Force date of movement - * @param int $id_product_batch Id product_batch - * @param string $inventorycode Inventory code - * @return int <0 if KO, >0 if OK + * @param User $user Object user + * @param int $fk_product Id product + * @param int $entrepot_id Warehouse id + * @param int $qty Quantity + * @param int $price Price + * @param string $label Label of stock movement + * @param integer|string $eatby eat-by date + * @param integer|string $sellby sell-by date + * @param string $batch batch number + * @param string $datem Force date of movement + * @param int $id_product_batch Id product_batch + * @param string $inventorycode Inventory code + * @return int <0 if KO, >0 if OK */ public function reception($user, $fk_product, $entrepot_id, $qty, $price = 0, $label = '', $eatby = '', $sellby = '', $batch = '', $datem = '', $id_product_batch = 0, $inventorycode = '') { @@ -918,8 +919,8 @@ class MouvementStock extends CommonObject } else { // product_batch record not found $pdluo->fk_product_stock = $vfk_product_stock; $pdluo->qty = $qty; - $pdluo->eatby = $veatby; - $pdluo->sellby = $vsellby; + $pdluo->eatby = empty($dluo['eatby']) ? '' : $dluo['eatby']; // No more used. Now eatby date is store in table of lot, no more into prouct_batch table. + $pdluo->sellby = empty($dluo['sellby']) ? '' : $dluo['sellby']; // No more used. Now sellby date is store in table of lot, no more into prouct_batch table. $pdluo->batch = $vbatchnumber; $result = $pdluo->create($user, 1); diff --git a/test/phpunit/MouvementStockTest.php b/test/phpunit/MouvementStockTest.php index 23b6b2b0111..4570db5a1d5 100644 --- a/test/phpunit/MouvementStockTest.php +++ b/test/phpunit/MouvementStockTest.php @@ -130,7 +130,7 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase /** * testMouvementCreate * - * @return int + * @return MouvementStock */ public function testMouvementCreate() { @@ -141,6 +141,13 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase $db=$this->savdb; // We create a product for tests + $product0=new Product($db); + $product0->initAsSpecimen(); + $product0->ref.=' 0'; + $product0->label.=' 0'; + $product0->status_batch = 1; + $product0id=$product0->create($user); + $product1=new Product($db); $product1->initAsSpecimen(); $product1->ref.=' 1'; @@ -154,6 +161,13 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase $product2id=$product2->create($user); // We create a product for tests + $warehouse0=new Entrepot($db); + $warehouse0->initAsSpecimen(); + $warehouse0->label.=' 0'; + $warehouse0->description.=' 0'; + $warehouse0->statut = 0; + $warehouse0id=$warehouse0->create($user); + $warehouse1=new Entrepot($db); $warehouse1->initAsSpecimen(); $warehouse1->label.=' 1'; @@ -168,32 +182,49 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase $localobject=new MouvementStock($this->savdb); + $datetest1 = dol_mktime(0, 0, 0, 1, 1, 2000); + $datetest2 = dol_mktime(0, 0, 0, 1, 2, 2000); + + // Create an input movement movement (type = 3) with value for eatby date and a lot + $result=$localobject->reception($user, $product0id, $warehouse0id, 5, 999, 'Movement for unit test with batch', $datetest1, $datetest1, 'anotyetuselotnumberA', '', 0, 'Inventory Code Test with batch'); + print __METHOD__." result=".$result."\n"; + $this->assertGreaterThan(0, $result, 'Failed to create a movement with a lot number of product with status_batch=1'); + + $result=$localobject->reception($user, $product0id, $warehouse0id, 5, 999, 'Movement for unit test with batch', $datetest1, $datetest1, 'anotyetuselotnumberB', '', 0, 'Inventory Code Test with batch'); + print __METHOD__." result=".$result."\n"; + $this->assertGreaterThan(0, $result, 'Test to check we can create a movement a eatby dare different when lot number is different'); + + // Create same input movement movement (type = 3) with same lot but a different value of eatby date + $result=$localobject->reception($user, $product0id, $warehouse0id, 5, 999, 'Movement for unit test with batch', $datetest2, $datetest1, 'anotyetuselotnumberA', '', 0, 'Inventory Code Test with batch'); + print __METHOD__." result=".$result."\n"; + $this->assertEquals(-3, $result, 'Test to check we can t create a movement for a lot with a different eatby date'); + // Do a list of movement into warehouse 1 // Create an input movement (type = 3) of price 9.9 -> should update PMP to 9.9 $result=$localobject->reception($user, $product1id, $warehouse1id, 10, 9.9, 'Movement for unit test 1', '', '', '', '', 0, 'Inventory Code Test'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); + $this->assertGreaterThan(0, $result, 'Return code of 0 was expected for the reception test 1'); // Create an input movement (type = 3) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->reception($user, $product1id, $warehouse1id, 10, 9.7, 'Movement for unit test 2', '', '', '', '', 0, 'Inventory Code Test'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); + $this->assertGreaterThan(0, $result); // Create an output movement (type = 2) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->livraison($user, $product1id, $warehouse1id, 5, 999, 'Movement for unit test 3', '', '', '', '', 0, 'Inventory Code Test'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); + $this->assertGreaterThan(0, $result); // Create an output movement (type = 1) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->_create($user, $product1id, $warehouse1id, 1, 0, 0, 'Input from transfer', 'Transfert X'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); + $this->assertGreaterThan(0, $result); // Create an output movement (type = 1) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->_create($user, $product1id, $warehouse1id, -2, 1, 0, 'Output from transfer', 'Transfert Y'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); + $this->assertGreaterThan(0, $result); // Do same but into warehouse 2 @@ -201,28 +232,27 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase // Create an input movement (type = 3) of price 9.9 -> should update PMP to 9.9 $result=$localobject->reception($user, $product1id, $warehouse2id, 10, 9.9, 'Movement for unit test 1 wh 2', '', '', '', '', 0, 'Inventory Code Test 2'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); + $this->assertGreaterThan(0, $result); // Create an input movement (type = 3) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->reception($user, $product1id, $warehouse2id, 10, 9.7, 'Movement for unit test 2 wh 2', '', '', '', '', 0, 'Inventory Code Test 2'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); + $this->assertGreaterThan(0, $result); // Create an output movement (type = 2) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->livraison($user, $product1id, $warehouse2id, 5, 999, 'Movement for unit test 3 wh 2', '', '', '', '', 0, 'Inventory Code Test 2'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); + $this->assertGreaterThan(0, $result); // Create an output movement (type = 1) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->_create($user, $product1id, $warehouse2id, 1, 0, 0, 'Input from transfer wh 2', 'Transfert X 2'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); + $this->assertGreaterThan(0, $result); // Create an output movement (type = 1) of price 9.7 -> should update PMP to 9.9/9.7 = 9.8 $result=$localobject->_create($user, $product1id, $warehouse2id, -2, 1, 0, 'Output from transfer wh 2', 'Transfert Y 2'); print __METHOD__." result=".$result."\n"; - $this->assertLessThan($result, 0); - + $this->assertGreaterThan(0, $result); return $localobject; } @@ -231,7 +261,7 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase * testMouvementCheck * * @param MouvementStock $localobject Movement object we created - * @return int + * @return MouvementStock * * @depends testMouvementCreate * The depends says test is run only if previous is ok From d781d78c4e960cea252fcdfe91a9289283ecdad7 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 12:53:31 +0100 Subject: [PATCH 114/743] Fix warning --- htdocs/categories/class/categorie.class.php | 4 ++-- test/phpunit/CategorieTest.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/htdocs/categories/class/categorie.class.php b/htdocs/categories/class/categorie.class.php index f55b3403546..63400aec45c 100644 --- a/htdocs/categories/class/categorie.class.php +++ b/htdocs/categories/class/categorie.class.php @@ -312,7 +312,7 @@ class Categorie extends CommonObject // Check parameters if (empty($id) && empty($label) && empty($ref_ext)) return -1; - if (!is_numeric($type)) $type = $this->MAP_ID[$type]; + if (!is_null($type) && !is_numeric($type)) $type = $this->MAP_ID[$type]; $sql = "SELECT rowid, fk_parent, entity, label, description, color, fk_soc, visible, type, ref_ext"; $sql .= ", date_creation, tms, fk_user_creat, fk_user_modif"; @@ -1058,7 +1058,7 @@ class Categorie extends CommonObject $current_lang = $langs->getDefaultLang(); // Init $this->cats array - $sql = "SELECT DISTINCT c.rowid, c.label, c.description, c.color, c.fk_parent, c.visible"; // Distinct reduce pb with old tables with duplicates + $sql = "SELECT DISTINCT c.rowid, c.label, c.ref_ext, c.description, c.color, c.fk_parent, c.visible"; // Distinct reduce pb with old tables with duplicates if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= ", t.label as label_trans, t.description as description_trans"; $sql .= " FROM ".MAIN_DB_PREFIX."categorie as c"; if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."categorie_lang as t ON t.fk_category=c.rowid AND t.lang='".$this->db->escape($current_lang)."'"; diff --git a/test/phpunit/CategorieTest.php b/test/phpunit/CategorieTest.php index 189ef10ecdc..11aff6b1a79 100644 --- a/test/phpunit/CategorieTest.php +++ b/test/phpunit/CategorieTest.php @@ -203,6 +203,7 @@ class CategorieTest extends PHPUnit\Framework\TestCase $result=$localobject2->create($user); $cat = new Categorie($this->savdb); $cat->id = $catid; + $cat->type = 0; $result=$cat->add_type($localobject2, "product"); print __METHOD__." result=".$result."\n"; From 4df0efa43d20cf4ed9470a10b50b2e02517078b7 Mon Sep 17 00:00:00 2001 From: Corentin Giroud-Argoud Date: Wed, 16 Dec 2020 10:47:28 +0100 Subject: [PATCH 115/743] [New] Add sendMail hook in subscription Added sendMail hook in subscription, before a mail is sent to confirm a subscription. It makes it more flexible for modules to interact with it. --- htdocs/adherents/subscription.php | 108 +++++++++++++++++------------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/htdocs/adherents/subscription.php b/htdocs/adherents/subscription.php index 1c4e42da1fe..a00c52b88e5 100644 --- a/htdocs/adherents/subscription.php +++ b/htdocs/adherents/subscription.php @@ -300,57 +300,75 @@ if ($user->rights->adherent->cotisation->creer && $action == 'subscription' && ! if (!$error) { // Send confirmation Email if ($object->email && $sendalsoemail) { // $object is 'Adherent' - $subject = ''; - $msg = ''; - - // Send subscription email - include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; - $formmail = new FormMail($db); - // Set output language - $outputlangs = new Translate('', $conf); - $outputlangs->setDefaultLang(empty($object->thirdparty->default_lang) ? $mysoc->default_lang : $object->thirdparty->default_lang); - // Load traductions files required by page - $outputlangs->loadLangs(array("main", "members")); - - // Get email content from template - $arraydefaultmessage = null; - $labeltouse = $conf->global->ADHERENT_EMAIL_TEMPLATE_SUBSCRIPTION; - - if (!empty($labeltouse)) $arraydefaultmessage = $formmail->getEMailTemplate($db, 'member', $user, $outputlangs, 0, 1, $labeltouse); - - if (!empty($labeltouse) && is_object($arraydefaultmessage) && $arraydefaultmessage->id > 0) { - $subject = $arraydefaultmessage->topic; - $msg = $arraydefaultmessage->content; + $parameters = array( + 'datesubscription' => $datesubscription, + 'amount' => $amount, + 'ccountid' => $accountid, + 'operation' => $operation, + 'label' => $label, + 'num_chq' => $num_chq, + 'emetteur_nom' => $emetteur_nom, + 'emetteur_banque' => $emetteur_banque, + 'datesubend' => $datesubend + ); + $reshook = $hookmanager->executeHooks('sendMail', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks + if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); } - $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); - complete_substitutions_array($substitutionarray, $outputlangs, $object); - $subjecttosend = make_substitutions($subject, $substitutionarray, $outputlangs); - $texttosend = make_substitutions(dol_concatdesc($msg, $adht->getMailOnSubscription()), $substitutionarray, $outputlangs); + if (empty($reshook)) { + $subject = ''; + $msg = ''; - // Attach a file ? - $file = ''; - $listofpaths = array(); - $listofnames = array(); - $listofmimes = array(); - if (is_object($object->invoice) && (!is_object($arraydefaultmessage) || intval($arraydefaultmessage->joinfiles))) { - $invoicediroutput = $conf->facture->dir_output; - $fileparams = dol_most_recent_file($invoicediroutput.'/'.$object->invoice->ref, preg_quote($object->invoice->ref, '/').'[^\-]+'); - $file = $fileparams['fullname']; + // Send subscription email + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; + $formmail = new FormMail($db); + // Set output language + $outputlangs = new Translate('', $conf); + $outputlangs->setDefaultLang(empty($object->thirdparty->default_lang) ? $mysoc->default_lang : $object->thirdparty->default_lang); + // Load traductions files required by page + $outputlangs->loadLangs(array("main", "members")); - $listofpaths = array($file); - $listofnames = array(basename($file)); - $listofmimes = array(dol_mimetype($file)); - } + // Get email content from template + $arraydefaultmessage = null; + $labeltouse = $conf->global->ADHERENT_EMAIL_TEMPLATE_SUBSCRIPTION; - $moreinheader = 'X-Dolibarr-Info: send_an_email by adherents/subscription.php'."\r\n"; + if (!empty($labeltouse)) $arraydefaultmessage = $formmail->getEMailTemplate($db, 'member', $user, $outputlangs, 0, 1, $labeltouse); - $result = $object->send_an_email($texttosend, $subjecttosend, $listofpaths, $listofmimes, $listofnames, "", "", 0, -1, '', $moreinheader); - if ($result < 0) { - $errmsg = $object->error; - setEventMessages($object->error, $object->errors, 'errors'); - } else { - setEventMessages($langs->trans("EmailSentToMember", $object->email), null, 'mesgs'); + if (!empty($labeltouse) && is_object($arraydefaultmessage) && $arraydefaultmessage->id > 0) { + $subject = $arraydefaultmessage->topic; + $msg = $arraydefaultmessage->content; + } + + $substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $object); + complete_substitutions_array($substitutionarray, $outputlangs, $object); + $subjecttosend = make_substitutions($subject, $substitutionarray, $outputlangs); + $texttosend = make_substitutions(dol_concatdesc($msg, $adht->getMailOnSubscription()), $substitutionarray, $outputlangs); + + // Attach a file ? + $file = ''; + $listofpaths = array(); + $listofnames = array(); + $listofmimes = array(); + if (is_object($object->invoice) && (!is_object($arraydefaultmessage) || intval($arraydefaultmessage->joinfiles))) { + $invoicediroutput = $conf->facture->dir_output; + $fileparams = dol_most_recent_file($invoicediroutput.'/'.$object->invoice->ref, preg_quote($object->invoice->ref, '/').'[^\-]+'); + $file = $fileparams['fullname']; + + $listofpaths = array($file); + $listofnames = array(basename($file)); + $listofmimes = array(dol_mimetype($file)); + } + + $moreinheader = 'X-Dolibarr-Info: send_an_email by adherents/subscription.php'."\r\n"; + + $result = $object->send_an_email($texttosend, $subjecttosend, $listofpaths, $listofmimes, $listofnames, "", "", 0, -1, '', $moreinheader); + if ($result < 0) { + $errmsg = $object->error; + setEventMessages($object->error, $object->errors, 'errors'); + } else { + setEventMessages($langs->trans("EmailSentToMember", $object->email), null, 'mesgs'); + } } } else { setEventMessages($langs->trans("NoEmailSentToMember"), null, 'mesgs'); From a5a070705b91f67ad7b647aabe03c97f474de5b0 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 14:53:04 +0100 Subject: [PATCH 116/743] Add test for phpunit env --- test/phpunit/MouvementStockTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/phpunit/MouvementStockTest.php b/test/phpunit/MouvementStockTest.php index 4570db5a1d5..1f27b4c71de 100644 --- a/test/phpunit/MouvementStockTest.php +++ b/test/phpunit/MouvementStockTest.php @@ -115,6 +115,8 @@ class MouvementStockTest extends PHPUnit\Framework\TestCase $langs=$this->savlangs; $db=$this->savdb; + if (empty($conf->productbatch->enabled)) { print "\n".__METHOD__." module Lot/Serial must be enabled.\n"; die(); } + print __METHOD__."\n"; } /** From 6f75e24fd704210a707731b81b32517816d50980 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 15:10:11 +0100 Subject: [PATCH 117/743] Fix better error management in migration --- .travis.yml | 3 ++- htdocs/install/upgrade2.php | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index a3e35dcfdab..bda2a9e9634 100644 --- a/.travis.yml +++ b/.travis.yml @@ -400,7 +400,8 @@ script: php step5.php 12.0.0 13.0.0 > $TRAVIS_BUILD_DIR/upgrade12001300-3.log # Enable modules not enabled into original dump - php upgrade2.php 0.0.0 0.0.0 MAIN_MODULE_API,MAIN_MODULE_SUPPLIERPROPOSAL,MAIN_MODULE_WEBSITE,MAIN_MODULE_TICKET,MAIN_MODULE_ACCOUNTING,MAIN_MODULE_MRP,MAIN_MODULE_RECRUITMENT > $TRAVIS_BUILD_DIR/enablemodule.log + php upgrade2.php 0.0.0 0.0.0 MAIN_MODULE_API,MAIN_MODULE_SUPPLIERPROPOSAL,MAIN_MODULE_WEBSITE,MAIN_MODULE_TICKET,MAIN_MODULE_ACCOUNTING,MAIN_MODULE_MRP > $TRAVIS_BUILD_DIR/enablemodule.log + php upgrade2.php 0.0.0 0.0.0 MAIN_MODULE_RECEPTION,MAIN_MODULE_RECRUITMENT > $TRAVIS_BUILD_DIR/enablemodule.log echo $? cd - set +e diff --git a/htdocs/install/upgrade2.php b/htdocs/install/upgrade2.php index 2d84e234cf2..babb47de91a 100644 --- a/htdocs/install/upgrade2.php +++ b/htdocs/install/upgrade2.php @@ -509,7 +509,11 @@ if (!GETPOST('action', 'aZ09') || preg_match('/upgrade/i', GETPOST('action', 'aZ { $listofmodules[$value] = 'forceactivate'; } - migrate_reload_modules($db, $langs, $conf, $listofmodules, 1); + + $resultreloadmodules = migrate_reload_modules($db, $langs, $conf, $listofmodules, 1); + if ($resultreloadmodules < 0) { + $error++; + } } @@ -4283,7 +4287,7 @@ function migrate_delete_old_dir($db, $langs, $conf) * @param Conf $conf Object conf * @param array $listofmodule List of modules * @param int $force 1=Reload module even if not already loaded - * @return void + * @return int <0 if KO, >0 if OK */ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $force = 0) { @@ -4497,12 +4501,15 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } else { - dolibarr_install_syslog('Failed to include '.strtolower($moduletoreloadshort).'/core/modules/mod'.$moduletoreloadshort.'.class.php'); + dolibarr_install_syslog('Failed to include '.strtolower($moduletoreloadshort).'/core/modules/mod'.$moduletoreloadshort.'.class.php', LOG_ERR); + print "Error, can't find module with name ".$moduletoreload."\n"; + return -1; } } } else { - dolibarr_install_syslog("Error, can't find module with name ".$moduletoreload, LOG_WARNING); - print "Error, can't find module with name ".$moduletoreload; + dolibarr_install_syslog("Error, can't find module with name ".$moduletoreload, LOG_ERR); + print "Error, can't find module with name ".$moduletoreload."\n"; + return -1; } } @@ -4516,6 +4523,8 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo print ''; } } + + return 1; } From 0a38f9e8aac7676d4e052660e541dccd3a65942f Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 15:16:23 +0100 Subject: [PATCH 118/743] Fix add missing token --- htdocs/multicurrency/multicurrency_rate.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/htdocs/multicurrency/multicurrency_rate.php b/htdocs/multicurrency/multicurrency_rate.php index ffab711e1cd..eae788b0587 100644 --- a/htdocs/multicurrency/multicurrency_rate.php +++ b/htdocs/multicurrency/multicurrency_rate.php @@ -241,6 +241,8 @@ if ($action != "updateRate") { $form = new Form($db); print '
'; + print ''; + print '
'; + print ''; if ($line->doc_type == 'customer_invoice' || $line->doc_type == 'supplier_invoice' || $line->doc_type == 'expense_report') { @@ -755,8 +762,15 @@ while ($i < min($num, $limit)) } // Show sub-total of last shown account -$colspan = $totalarray['nbfield'] - 3; -$colspanend = $totalarray['nbfield'] - 8; +if (empty($conf->global->ACCOUNTING_ENABLE_LETTERING) || empty($arrayfields['t.lettering_code']['checked'])) { + $colnumber = 3; + $colnumberend = 7; +} else { + $colnumber = 4; + $colnumberend = 7; +} +$colspan = $totalarray['nbfield'] - $colnumber; +$colspanend = $totalarray['nbfield'] - $colnumberend; print '
'.$langs->trans("TotalForAccount").' '.$accountg.':'.price($sous_total_debit).'
'; print ' '; @@ -286,6 +288,8 @@ if ($action == "updateRate") { $form = new Form($db); print ''; + print ''; + print '
'.$langs->trans('Date').'
'; print ' '; print ''; print ''; $total_revenue_ht = 0; +$balance_ht = 0; +$balance_ttc = 0; foreach ($listofreferent as $key => $value) { + $parameters = array( + 'total_revenue_ht' =>& $total_revenue_ht, + 'balance_ht' =>& $balance_ht, + 'balance_ttc' =>& $balance_ttc, + 'key' => $key, + 'value' =>& $value, + 'dates' => $dates, + 'datee' => $datee + ); + $reshook = $hookmanager->executeHooks('printOverviewProfit', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } + elseif ($reshook > 0){ + print $hookmanager->resPrint; + continue; + } + $name = $langs->trans($value['name']); $title = $value['title']; $classname = $value['class']; @@ -849,6 +869,22 @@ print '
'; // Detail foreach ($listofreferent as $key => $value) { + + $parameters = array( + 'key' => $key, + 'value' =>& $value, + 'dates' => $dates, + 'datee' => $datee + ); + $reshook = $hookmanager->executeHooks('printOverviewDetail', $parameters, $object, $action); // Note that $action and $object may have been modified by hook + if ($reshook < 0) { + setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); + } + elseif ($reshook > 0){ + print $hookmanager->resPrint; + continue; + } + $title = $value['title']; $classname = $value['class']; $tablename = $value['table']; From e61b71bc93801a8771d4c0eead1f37838c201fa1 Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Wed, 16 Dec 2020 15:40:38 +0000 Subject: [PATCH 123/743] Fixing style errors. --- htdocs/projet/element.php | 1 - 1 file changed, 1 deletion(-) diff --git a/htdocs/projet/element.php b/htdocs/projet/element.php index 975b8fcdbed..b334688558b 100644 --- a/htdocs/projet/element.php +++ b/htdocs/projet/element.php @@ -869,7 +869,6 @@ print '
'; // Detail foreach ($listofreferent as $key => $value) { - $parameters = array( 'key' => $key, 'value' =>& $value, From e4c853b162002338a4076a354f030301ff2288a3 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 17:00:14 +0100 Subject: [PATCH 124/743] Enable module product batch --- htdocs/install/upgrade2.php | 68 +++++++++++++++---------------------- 1 file changed, 28 insertions(+), 40 deletions(-) diff --git a/htdocs/install/upgrade2.php b/htdocs/install/upgrade2.php index babb47de91a..ba24cf3a496 100644 --- a/htdocs/install/upgrade2.php +++ b/htdocs/install/upgrade2.php @@ -4295,14 +4295,12 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo dolibarr_install_syslog("upgrade2::migrate_reload_modules force=".$force.", listofmodule=".join(',', array_keys($listofmodule))); - foreach ($listofmodule as $moduletoreload => $reloadmode) // reloadmodule can be 'noboxes', 'newboxdefonly', 'forceactivate' - { + foreach ($listofmodule as $moduletoreload => $reloadmode) { // reloadmodule can be 'noboxes', 'newboxdefonly', 'forceactivate' if (empty($moduletoreload) || (empty($conf->global->$moduletoreload) && !$force)) continue; // Discard reload if module not enabled $mod = null; - if ($moduletoreload == 'MAIN_MODULE_AGENDA') - { + if ($moduletoreload == 'MAIN_MODULE_AGENDA') { dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Agenda module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modAgenda.class.php'; if ($res) { @@ -4310,8 +4308,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo $mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_API') - { + } elseif ($moduletoreload == 'MAIN_MODULE_API') { dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Rest API module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modApi.class.php'; if ($res) { @@ -4319,8 +4316,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_BARCODE') - { + } elseif ($moduletoreload == 'MAIN_MODULE_BARCODE') { dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Barcode module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modBarcode.class.php'; if ($res) { @@ -4328,8 +4324,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo $mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_CRON') - { + } elseif ($moduletoreload == 'MAIN_MODULE_CRON') { dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Cron module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modCron.class.php'; if ($res) { @@ -4337,8 +4332,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo $mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_SOCIETE') - { + } elseif ($moduletoreload == 'MAIN_MODULE_SOCIETE') { dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Societe module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modSociete.class.php'; if ($res) { @@ -4346,8 +4340,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo $mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_PRODUIT') // Permission has changed into 2.7 - { + } elseif ($moduletoreload == 'MAIN_MODULE_PRODUIT') { // Permission has changed into 2.7 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Produit module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modProduct.class.php'; if ($res) { @@ -4355,8 +4348,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_SERVICE') // Permission has changed into 2.7 - { + } elseif ($moduletoreload == 'MAIN_MODULE_SERVICE') { // Permission has changed into 2.7 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Service module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modService.class.php'; if ($res) { @@ -4364,8 +4356,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_COMMANDE') // Permission has changed into 2.9 - { + } elseif ($moduletoreload == 'MAIN_MODULE_COMMANDE') { // Permission has changed into 2.9 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Commande module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modCommande.class.php'; if ($res) { @@ -4373,8 +4364,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_FACTURE') // Permission has changed into 2.9 - { + } elseif ($moduletoreload == 'MAIN_MODULE_FACTURE') { // Permission has changed into 2.9 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Facture module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modFacture.class.php'; if ($res) { @@ -4382,8 +4372,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_FOURNISSEUR') // Permission has changed into 2.9 - { + } elseif ($moduletoreload == 'MAIN_MODULE_FOURNISSEUR') { // Permission has changed into 2.9 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Fournisseur module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modFournisseur.class.php'; if ($res) { @@ -4391,8 +4380,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_HOLIDAY') // Permission and tabs has changed into 3.8 - { + } elseif ($moduletoreload == 'MAIN_MODULE_HOLIDAY') { // Permission and tabs has changed into 3.8 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Leave Request module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modHoliday.class.php'; if ($res) { @@ -4400,8 +4388,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo $mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_DEPLACEMENT') // Permission has changed into 3.0 - { + } elseif ($moduletoreload == 'MAIN_MODULE_DEPLACEMENT') { // Permission has changed into 3.0 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Deplacement module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modDeplacement.class.php'; if ($res) { @@ -4409,8 +4396,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_EXPENSEREPORT') - { + } elseif ($moduletoreload == 'MAIN_MODULE_EXPENSEREPORT') { dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Expense Report module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modExpenseReport.class.php'; if ($res) { @@ -4418,8 +4404,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_DON') // Permission has changed into 3.0 - { + } elseif ($moduletoreload == 'MAIN_MODULE_DON') { // Permission has changed into 3.0 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Don module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modDon.class.php'; if ($res) { @@ -4427,8 +4412,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo //$mod->remove('noboxes'); $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_ECM') // Permission has changed into 3.0 and 3.1 - { + } elseif ($moduletoreload == 'MAIN_MODULE_ECM') { // Permission has changed into 3.0 and 3.1 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate ECM module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modECM.class.php'; if ($res) { @@ -4436,8 +4420,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo $mod->remove('noboxes'); // We need to remove because a permission id has been removed $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_PAYBOX') // Permission has changed into 3.0 - { + } elseif ($moduletoreload == 'MAIN_MODULE_PAYBOX') { // Permission has changed into 3.0 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Paybox module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modPaybox.class.php'; if ($res) { @@ -4445,8 +4428,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo $mod->remove('noboxes'); // We need to remove because id of module has changed $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_SUPPLIERPROPOSAL') // Module after 3.5 - { + } elseif ($moduletoreload == 'MAIN_MODULE_SUPPLIERPROPOSAL') { // Module after 3.5 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Supplier Proposal module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modSupplierProposal.class.php'; if ($res) { @@ -4454,8 +4436,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo $mod->remove('noboxes'); // We need to remove because id of module has changed $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_OPENSURVEY') // Permission has changed into 3.0 - { + } elseif ($moduletoreload == 'MAIN_MODULE_OPENSURVEY') { // Permission has changed into 3.0 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Opensurvey module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modOpenSurvey.class.php'; if ($res) { @@ -4463,8 +4444,15 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo $mod->remove('noboxes'); // We need to remove because menu entries has changed $mod->init($reloadmode); } - } elseif ($moduletoreload == 'MAIN_MODULE_TAKEPOS') // Permission has changed into 10.0 - { + } elseif ($moduletoreload == 'MAIN_MODULE_PRODUCTBATCH') { // Permission has changed into 10.0 + dolibarr_install_syslog("upgrade2::migrate_reload_modules ProductBatch module"); + $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modProductBatch.class.php'; + if ($res) { + $mod = new modProductBatch($db); + $mod->remove('noboxes'); // We need to remove because menu entries has changed + $mod->init($reloadmode); + } + } elseif ($moduletoreload == 'MAIN_MODULE_TAKEPOS') { // Permission has changed into 10.0 dolibarr_install_syslog("upgrade2::migrate_reload_modules Reactivate Takepos module"); $res = @include_once DOL_DOCUMENT_ROOT.'/core/modules/modTakePos.class.php'; if ($res) { From ec8fd130f4e7d0ce0d8bff990e5de3176c1283c8 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 17:33:40 +0100 Subject: [PATCH 125/743] Update doc --- nightwatch.conf.js | 2 +- package.json | 2 +- test/acceptance/README.md | 32 +++++++++++++++++++++----------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/nightwatch.conf.js b/nightwatch.conf.js index e72febea918..38178aadf92 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -1,5 +1,5 @@ const admin_username = process.env.ADMIN_USERNAME || 'admin'; -const admin_password = process.env.ADMIN_PASSWORD || 'password'; +const admin_password = process.env.ADMIN_PASSWORD || 'admin'; const launch_url = process.env.LAUNCH_URL || 'http://localhost/dolibarr/htdocs/'; module.exports = { page_objects_path : './test/acceptance/pageObjects/', // jshint ignore:line diff --git a/package.json b/package.json index 56f8ced1d4b..02c9b2abcb9 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "devDependencies": { "cucumber": "^6.0.5", - "nightwatch": "^1.4.3", + "nightwatch": "^1.5.1", "nightwatch-api": "^3.0.1" }, "scripts": { diff --git a/test/acceptance/README.md b/test/acceptance/README.md index ada096c0b69..141370208d0 100644 --- a/test/acceptance/README.md +++ b/test/acceptance/README.md @@ -2,14 +2,21 @@ ### Run Selenium -Selenium has been used for automating the browser. +Create a working directory + `mkdir selenium; cd selenium;` + +Selenium has been used for automating the browser. + [Download](https://www.selenium.dev/downloads/) the `latest stable version` of the `Selenium standalone server JAR file`. + `wget https://selenium-release.storage.googleapis.com/3.141/selenium-server-standalone-3.141.59.jar` + Also [download](https://chromedriver.chromium.org/downloads) the `latest stable version` of `Chrome Driver`. Once you have downloaded Chrome Driver, you need to unzip it by running the following command: + `wget https://chromedriver.storage.googleapis.com/86.0.4240.22/chromedriver_linux64.zip` `unzip chromedriver_linux64.zip` Once you have unzipped it, you need to move the *chromedriver* file (shared library) and place it inside the same folder where you have placed the Selenium standalone server file. @@ -18,10 +25,9 @@ Now we can run selenium by two ways: * Start selenium server with a command which usually looks like: - `java -jar selenium-server-standalone-.jar -port 4444` + `java -jar selenium-server-standalone-*.jar -port 4444` - -* Run selenium in docker with +* Or run selenium in docker with `docker run -d -p 4444:4444 -p 5900:5900 -v /dev/shm:/dev/shm selenium/standalone-chrome-debug` @@ -34,16 +40,20 @@ Now we can run selenium by two ways: * Install *yarn*. For example on Ubuntu: ``` - apt install yarnpkg + sudo apt install yarnpkg ``` -* Install *nodejs* libraries. For example on Ubuntu: +* Install *npm* tools to manage *nodejs* libraries. For example on Ubuntu: ``` apt install npm - npm install cucumber - npm install nightwatch-api - npm install nightwatch + ``` + +* Go into the git local repository of the Dolibarr version to test. + + ``` + cd ~/git/dolibarr + npm install cucumber nightwatch-api nightwatch npm update ``` @@ -61,9 +71,9 @@ Now we can run selenium by two ways: `LAUNCH_URL=''; ADMIN_USERNAME=''; ADMIN_PASSWORD='';` - `yarn run test:e2e test/acceptance/features/` + `yarnpkg run test:e2e test/acceptance/features/` - For example: `yarn run test:e2e test/acceptance/features/WebUI/addUsers.feature` + For example: `yarnpkg run test:e2e test/acceptance/features/WebUI/addUsers.feature` Note: The script to run all the acceptance tests is specified in `scripts` object of `package.json` file inside the project's root directory as : From 21c2fae00cc37b446dd156c52272d29b7eee1ecb Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 17:41:18 +0100 Subject: [PATCH 126/743] Fix setup of antivirus --- htdocs/admin/security_file.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/admin/security_file.php b/htdocs/admin/security_file.php index 2669e741c6e..ea5ea2983fd 100644 --- a/htdocs/admin/security_file.php +++ b/htdocs/admin/security_file.php @@ -153,7 +153,7 @@ if (ini_get('safe_mode') && !empty($conf->global->MAIN_ANTIVIRUS_COMMAND)) dol_syslog("safe_mode is on, basedir is ".$basedir.", safe_mode_exec_dir is ".ini_get('safe_mode_exec_dir'), LOG_WARNING); } } -print ''; +print ''; if (defined('MAIN_ANTIVIRUS_COMMAND')) { print '
'.$langs->trans("ValueIsForcedBySystem").''; } @@ -167,7 +167,7 @@ print '
'; print '
'.$langs->trans('Date').''; From e7dda7bb4f0f06c851adf512c787b706b97ff24d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 15:46:54 +0100 Subject: [PATCH 119/743] Move help into tooltip help section --- htdocs/core/modules/modCategorie.class.php | 27 +++++++++++----------- htdocs/langs/en_US/categories.lang | 1 + 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/htdocs/core/modules/modCategorie.class.php b/htdocs/core/modules/modCategorie.class.php index d2218d33483..7dae96253bb 100644 --- a/htdocs/core/modules/modCategorie.class.php +++ b/htdocs/core/modules/modCategorie.class.php @@ -121,28 +121,29 @@ class modCategorie extends DolibarrModules // All Categories List $r++; $this->export_code[$r] = $this->rights_class.'_list'; - $this->export_label[$r] = 'CatList'; + $this->export_label[$r] = 'CatListAll'; $this->export_icon[$r] = $this->picto; $this->export_enabled[$r] = 'true'; $this->export_permission[$r] = array(array("categorie", "lire")); $typeexample = ""; - if (!empty($conf->product->enabled) || !empty($conf->service->enabled)) { $typeexample .= ($typeexample ? "/" : "")."0=Product-Service"; } + if (!empty($conf->product->enabled) || !empty($conf->service->enabled)) { $typeexample .= ($typeexample ? " / " : "")."0=Product-Service"; } if (!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled)) { $typeexample .= ($typeexample ? "/" : "")."1=Supplier"; } - if (!empty($conf->societe->enabled)) { $typeexample .= ($typeexample ? "/" : "")."2=Customer-Prospect"; } - if (!empty($conf->adherent->enabled)) { $typeexample .= ($typeexample ? "/" : "")."3=Member"; } - if (!empty($conf->societe->enabled)) { $typeexample .= ($typeexample ? "/" : "")."4=Contact"; } - if (!empty($conf->bank->enabled)) { $typeexample .= ($typeexample ? "/" : "")."5=Bank account"; } - if (!empty($conf->projet->enabled)) { $typeexample .= ($typeexample ? "/" : "")."6=Project"; } - if (!empty($conf->user->enabled)) { $typeexample .= ($typeexample ? "/" : "")."7=User"; } - if (!empty($conf->bank->enabled)) { $typeexample .= ($typeexample ? "/" : "")."8=Bank line"; } - if (!empty($conf->stock->enabled)) { $typeexample .= ($typeexample ? "/" : "")."9=Warehouse"; } - if (!empty($conf->agenda->enabled)) { $typeexample .= ($typeexample ? "/" : "")."10=Agenda event"; } - if (!empty($conf->website->enabled)) { $typeexample .= ($typeexample ? "/" : "")."11=Website page"; } + if (!empty($conf->societe->enabled)) { $typeexample .= ($typeexample ? " / " : "")."2=Customer-Prospect"; } + if (!empty($conf->adherent->enabled)) { $typeexample .= ($typeexample ? " / " : "")."3=Member"; } + if (!empty($conf->societe->enabled)) { $typeexample .= ($typeexample ? " / " : "")."4=Contact"; } + if (!empty($conf->bank->enabled)) { $typeexample .= ($typeexample ? " / " : "")."5=Bank account"; } + if (!empty($conf->projet->enabled)) { $typeexample .= ($typeexample ? " / " : "")."6=Project"; } + if (!empty($conf->user->enabled)) { $typeexample .= ($typeexample ? " / " : "")."7=User"; } + if (!empty($conf->bank->enabled)) { $typeexample .= ($typeexample ? " / " : "")."8=Bank line"; } + if (!empty($conf->stock->enabled)) { $typeexample .= ($typeexample ? " / " : "")."9=Warehouse"; } + if (!empty($conf->agenda->enabled)) { $typeexample .= ($typeexample ? " / " : "")."10=Agenda event"; } + if (!empty($conf->website->enabled)) { $typeexample .= ($typeexample ? " / " : "")."11=Website page"; } - $this->export_fields_array[$r] = array('cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.type'=>"Type ".$typeexample, 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", 'pcat.label'=>"ParentCategoryLabel" ); + $this->export_fields_array[$r] = array('cat.rowid'=>"CategId", 'cat.label'=>"Label", 'cat.type'=>"Type", 'cat.description'=>"Description", 'cat.fk_parent'=>"ParentCategory", 'pcat.label'=>"ParentCategoryLabel" ); $this->export_TypeFields_array[$r] = array('cat.label'=>"Text", 'cat.type'=>"Numeric", 'cat.description'=>"Text", 'cat.fk_parent'=>'List:categorie:label:rowid', 'pcat.label'=>'Text' ); $this->export_entities_array[$r] = array(); // We define here only fields that use another picto + $this->export_help_array[$r] = array('cat.type'=>$typeexample); $this->export_sql_start[$r] = 'SELECT DISTINCT '; $this->export_sql_end[$r] = ' FROM '.MAIN_DB_PREFIX.'categorie as cat'; diff --git a/htdocs/langs/en_US/categories.lang b/htdocs/langs/en_US/categories.lang index 36d1d3e545a..4ddf0d6093f 100644 --- a/htdocs/langs/en_US/categories.lang +++ b/htdocs/langs/en_US/categories.lang @@ -19,6 +19,7 @@ ProjectsCategoriesArea=Projects tags/categories area UsersCategoriesArea=Users tags/categories area SubCats=Sub-categories CatList=List of tags/categories +CatListAll=List of tags/categories (all types) NewCategory=New tag/category ModifCat=Modify tag/category CatCreated=Tag/category created From 1f9c597f269fef235e4404429cccc50ead6b0611 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 15:57:52 +0100 Subject: [PATCH 120/743] Fix default fields in list --- htdocs/societe/list.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/societe/list.php b/htdocs/societe/list.php index 71f0f08cc9d..2e24f355b37 100644 --- a/htdocs/societe/list.php +++ b/htdocs/societe/list.php @@ -187,8 +187,8 @@ $arrayfields = array( 's.code_fournisseur'=>array('label'=>"SupplierCodeShort", 'position'=>11, 'checked'=>$checkedsuppliercode, 'enabled'=>(!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled))), 's.code_compta'=>array('label'=>"CustomerAccountancyCodeShort", 'position'=>13, 'checked'=>$checkedcustomeraccountcode), 's.code_compta_fournisseur'=>array('label'=>"SupplierAccountancyCodeShort", 'position'=>14, 'checked'=>$checkedsupplieraccountcode, 'enabled'=>(!empty($conf->fournisseur->enabled) && empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) || !empty($conf->supplier_order->enabled) || !empty($conf->supplier_invoice->enabled))), - 's.address'=>array('label'=>"Address", 'position'=>19, 'checked'=>1), - 's.town'=>array('label'=>"Town", 'position'=>20, 'checked'=>1), + 's.address'=>array('label'=>"Address", 'position'=>19, 'checked'=>0), + 's.town'=>array('label'=>"Town", 'position'=>20, 'checked'=>0), 's.zip'=>array('label'=>"Zip", 'position'=>21, 'checked'=>1), 'state.nom'=>array('label'=>"State", 'position'=>22, 'checked'=>0), 'region.nom'=>array('label'=>"Region", 'position'=>23, 'checked'=>0), From 40ae1808de3e502f0bff5209e53b066901610565 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 16:06:33 +0100 Subject: [PATCH 121/743] Look and fell v13 --- htdocs/exports/export.php | 11 +++++------ htdocs/imports/import.php | 13 ++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/htdocs/exports/export.php b/htdocs/exports/export.php index b02ca2e10de..c9db970cd0b 100644 --- a/htdocs/exports/export.php +++ b/htdocs/exports/export.php @@ -429,8 +429,7 @@ if ($step == 1 || !$datatoexport) $hselected = $h; $h++; - print dol_get_fiche_head($head, $hselected, $langs->trans("NewExport"), -1); - + print dol_get_fiche_head($head, $hselected, '', -1); print '
'.$langs->trans("SelectExportDataSet").'

'; @@ -492,7 +491,7 @@ if ($step == 2 && $datatoexport) $hselected = $h; $h++; - print dol_get_fiche_head($head, $hselected, $langs->trans("NewExport"), -2); + print dol_get_fiche_head($head, $hselected, '', -2); print '
'; print '
'; @@ -688,7 +687,7 @@ if ($step == 3 && $datatoexport) $hselected = $h; $h++; - print dol_get_fiche_head($head, $hselected, $langs->trans("NewExport"), -2); + print dol_get_fiche_head($head, $hselected, '', -2); print '
'; print '
'; @@ -875,7 +874,7 @@ if ($step == 4 && $datatoexport) $hselected = $h; $h++; - print dol_get_fiche_head($head, $hselected, $langs->trans("NewExport"), -2); + print dol_get_fiche_head($head, $hselected, '', -2); print '
'; print '
'; @@ -1134,7 +1133,7 @@ if ($step == 5 && $datatoexport) $hselected = $h; $h++; - print dol_get_fiche_head($head, $hselected, $langs->trans("NewExport"), -2); + print dol_get_fiche_head($head, $hselected, '', -2); /* * Confirmation suppression fichier diff --git a/htdocs/imports/import.php b/htdocs/imports/import.php index 60134ab9b95..fbb17e15280 100644 --- a/htdocs/imports/import.php +++ b/htdocs/imports/import.php @@ -392,8 +392,7 @@ if ($step == 1 || !$datatoimport) $head = import_prepare_head($param, 1); - print dol_get_fiche_head($head, 'step1', $langs->trans("NewImport"), -1); - + print dol_get_fiche_head($head, 'step1', '', -1); print '
'.$langs->trans("SelectImportDataSet").'

'; @@ -454,7 +453,7 @@ if ($step == 2 && $datatoimport) $head = import_prepare_head($param, 2); - print dol_get_fiche_head($head, 'step2', $langs->trans("NewImport"), -2); + print dol_get_fiche_head($head, 'step2', '', -2); print '
'; print '
'; @@ -540,7 +539,7 @@ if ($step == 3 && $datatoimport) $head = import_prepare_head($param, 3); - print dol_get_fiche_head($head, 'step3', $langs->trans("NewImport"), -2); + print dol_get_fiche_head($head, 'step3', '', -2); /* * Confirm delete file @@ -835,7 +834,7 @@ if ($step == 4 && $datatoimport) $head = import_prepare_head($param, 4); - print dol_get_fiche_head($head, 'step4', $langs->trans("NewImport"), -2); + print dol_get_fiche_head($head, 'step4', '', -2); print '
'; print '
'; @@ -1306,7 +1305,7 @@ if ($step == 5 && $datatoimport) print ''; // step 5 print ''; // step 5 - print dol_get_fiche_head($head, 'step5', $langs->trans("NewImport"), -2); + print dol_get_fiche_head($head, 'step5', '', -2); print '
'; print '
'; @@ -1730,7 +1729,7 @@ if ($step == 6 && $datatoimport) $head = import_prepare_head($param, 6); - print dol_get_fiche_head($head, 'step6', $langs->trans("NewImport"), -1); + print dol_get_fiche_head($head, 'step6', '', -1); print '
'; print '
'; From 1f29d4f5e8c47144db678abd0bf835e513f3964a Mon Sep 17 00:00:00 2001 From: ATM john Date: Wed, 16 Dec 2020 16:25:30 +0100 Subject: [PATCH 122/743] Add missing hooks --- htdocs/projet/element.php | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/htdocs/projet/element.php b/htdocs/projet/element.php index c513123c9bf..975b8fcdbed 100644 --- a/htdocs/projet/element.php +++ b/htdocs/projet/element.php @@ -652,9 +652,29 @@ print '
'.$langs->trans("AmountTTC").'
'.$langs->trans("AntiVirusParam").'
'; print ''.$langs->trans("AntiVirusParamExample").''; print '
'; -print ''; +print ''; if (defined('MAIN_ANTIVIRUS_PARAM')) { print '
'.$langs->trans("ValueIsForcedBySystem").''; } From 82710e54a87a86aad87367c5fbdc2c647d02fe58 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Wed, 16 Dec 2020 18:10:40 +0100 Subject: [PATCH 127/743] Fix get custom odt templates --- htdocs/core/lib/files.lib.php | 5 +++++ htdocs/core/lib/functions.lib.php | 2 +- .../commande/doc/doc_generic_order_odt.modules.php | 8 ++++++-- .../facture/doc/doc_generic_invoice_odt.modules.php | 5 +++-- .../propale/doc/doc_generic_proposal_odt.modules.php | 6 ++++-- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 22545b78cf8..c43a7eb890c 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -2260,6 +2260,11 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, $accessallowed = ($user->admin && basename($original_file) == $original_file && preg_match('/^dolibarr.*\.log$/', basename($original_file))); $original_file = $dolibarr_main_data_root.'/'.$original_file; } // Wrapping for *.log files, like when used with url http://.../document.php?modulepart=logs&file=dolibarr.log + elseif ($modulepart == 'doctemplates' && !empty($dolibarr_main_data_root)) + { + $accessallowed = $user->admin; + $original_file = $dolibarr_main_data_root.'/doctemplates/'.$original_file; + } // Wrapping for *.zip files, like when used with url http://.../document.php?modulepart=packages&file=module_myfile.zip elseif ($modulepart == 'doctemplateswebsite' && !empty($dolibarr_main_data_root)) { $accessallowed = ($fuser->rights->website->write && preg_match('/\.jpg$/i', basename($original_file))); diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 998cddea0f9..04e096c4e3f 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -3210,7 +3210,7 @@ function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $ '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected', 'accountancy', 'account', 'accountline', 'action', 'add', 'address', 'bank_account', 'barcode', 'bank', 'bill', 'billa', 'billr', 'billd', 'bookmark', 'bom', 'building', 'cash-register', 'category', 'check', 'clock', 'close_title', 'company', 'contact', 'contract', 'cron', 'cubes', - 'delete', 'dolly', 'dollyrevert', 'donation', 'edit', 'ellipsis-h', 'email', 'eraser', 'external-link-alt', 'external-link-square-alt', + 'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'edit', 'ellipsis-h', 'email', 'eraser', 'external-link-alt', 'external-link-square-alt', 'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'folder', 'folder-open', 'globe', 'globe-americas', 'grip', 'grip_title', 'group', 'help', 'holiday', 'intervention', 'label', 'language', 'list', 'listlight', 'lot', diff --git a/htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php b/htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php index 3165760b541..2b71f6896f5 100644 --- a/htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php +++ b/htdocs/core/modules/commande/doc/doc_generic_order_odt.modules.php @@ -169,10 +169,14 @@ class doc_generic_order_odt extends ModelePDFCommandes if ($nbofiles) { - $texte .= '

\n"; /* @@ -377,6 +374,7 @@ if ($resql) } +print '
'; print "\n"; print "\n"; print ''; @@ -501,6 +499,7 @@ foreach ($dirmodels as $reldir) } print '
'.$langs->trans("Name").'
'; +print '
'; /* diff --git a/htdocs/admin/ticket.php b/htdocs/admin/ticket.php index c1546f1da2c..f7e72f53e7e 100644 --- a/htdocs/admin/ticket.php +++ b/htdocs/admin/ticket.php @@ -44,10 +44,13 @@ $type = 'ticket'; $error = 0; + /* * Actions */ +include DOL_DOCUMENT_ROOT.'/core/actions_setmoduleoptions.inc.php'; + if ($action == 'updateMask') { $maskconstticket = GETPOST('maskconstticket', 'alpha'); $maskticket = GETPOST('maskticket', 'alpha'); @@ -66,7 +69,37 @@ if ($action == 'updateMask') { } else { setEventMessages($langs->trans("Error"), null, 'errors'); } -} elseif ($action == 'setmod') { +} + +// Activate a model +elseif ($action == 'set') { + $ret = addDocumentModel($value, $type, $label, $scandir); +} elseif ($action == 'del') { + $ret = delDocumentModel($value, $type); + if ($ret > 0) + { + if ($conf->global->TICKET_ADDON_PDF == "$value") dolibarr_del_const($db, 'TICKET_ADDON_PDF', $conf->entity); + } +} + +// Set default model +elseif ($action == 'setdoc') { + if (dolibarr_set_const($db, "TICKET_ADDON_PDF", $value, 'chaine', 0, '', $conf->entity)) + { + // The constant that was read before the new set + // We therefore requires a variable to have a coherent view + $conf->global->TICKET_ADDON_PDF = $value; + } + + // On active le modele + $ret = delDocumentModel($value, $type); + if ($ret > 0) + { + $ret = addDocumentModel($value, $type, $label, $scandir); + } +} + +elseif ($action == 'setmod') { // TODO Verifier si module numerotation choisi peut etre active // par appel methode canBeActivated @@ -190,7 +223,7 @@ print dol_get_fiche_end(); /* - * Projects Numbering model + * Tickets numbering model */ print load_fiche_titre($langs->trans("TicketNumberingModules"), '', ''); @@ -291,6 +324,162 @@ print '
'; print ''; print '
'; + + +/* + * Document templates generators + */ + +print load_fiche_titre($langs->trans("TicketsModelModule"), '', ''); + +// Load array def with activated templates +$def = array(); +$sql = "SELECT nom"; +$sql .= " FROM ".MAIN_DB_PREFIX."document_model"; +$sql .= " WHERE type = '".$db->escape($type)."'"; +$sql .= " AND entity = ".$conf->entity; +$resql = $db->query($sql); +if ($resql) +{ + $i = 0; + $num_rows = $db->num_rows($resql); + while ($i < $num_rows) + { + $array = $db->fetch_array($resql); + array_push($def, $array[0]); + $i++; + } +} else { + dol_print_error($db); +} + + +print '
'; +print "\n"; +print "\n"; +print ''; +print ''; +print '\n"; +print '\n"; +print ''; +print ''; +print "\n"; + +clearstatcache(); + +foreach ($dirmodels as $reldir) +{ + foreach (array('', '/doc') as $valdir) + { + $realpath = $reldir."core/modules/ticket".$valdir; + $dir = dol_buildpath($realpath); + + if (is_dir($dir)) + { + $handle = opendir($dir); + if (is_resource($handle)) + { + while (($file = readdir($handle)) !== false) + { + $filelist[] = $file; + } + closedir($handle); + arsort($filelist); + + foreach ($filelist as $file) + { + if (preg_match('/\.modules\.php$/i', $file) && preg_match('/^(pdf_|doc_)/', $file)) + { + if (file_exists($dir.'/'.$file)) + { + $name = substr($file, 4, dol_strlen($file) - 16); + $classname = substr($file, 0, dol_strlen($file) - 12); + + require_once $dir.'/'.$file; + $module = new $classname($db); + + $modulequalified = 1; + if ($module->version == 'development' && $conf->global->MAIN_FEATURES_LEVEL < 2) $modulequalified = 0; + if ($module->version == 'experimental' && $conf->global->MAIN_FEATURES_LEVEL < 1) $modulequalified = 0; + + if ($modulequalified) + { + print ''; + + // Active + if (in_array($name, $def)) + { + print ''; + } else { + print '"; + } + + // Default + print ''; + + // Info + $htmltooltip = ''.$langs->trans("Name").': '.$module->name; + $htmltooltip .= '
'.$langs->trans("Type").': '.($module->type ? $module->type : $langs->trans("Unknown")); + if ($module->type == 'pdf') + { + $htmltooltip .= '
'.$langs->trans("Width").'/'.$langs->trans("Height").': '.$module->page_largeur.'/'.$module->page_hauteur; + } + $htmltooltip .= '
'.$langs->trans("Path").': '.preg_replace('/^\//', '', $realpath).'/'.$file; + + $htmltooltip .= '

'.$langs->trans("FeaturesSupported").':'; + $htmltooltip .= '
'.$langs->trans("Logo").': '.yn($module->option_logo, 1, 1); + $htmltooltip .= '
'.$langs->trans("MultiLanguage").': '.yn($module->option_multilang, 1, 1); + //$htmltooltip .= '
'.$langs->trans("Discounts").': '.yn($module->option_escompte,1,1); + //$htmltooltip .= '
'.$langs->trans("CreditNote").': '.yn($module->option_credit_note,1,1); + //$htmltooltip .= '
'.$langs->trans("WatermarkOnDraftOrders").': '.yn($module->option_draft_watermark, 1, 1); + + + print ''; + + // Preview + print ''; + + print "\n"; + } + } + } + } + } + } + } +} + +print '
'.$langs->trans("Name").''.$langs->trans("Description").''.$langs->trans("Status")."'.$langs->trans("Default")."'.$langs->trans("ShortInfo").''.$langs->trans("Preview").'
'; + print (empty($module->name) ? $name : $module->name); + print "\n"; + if (method_exists($module, 'info')) print $module->info($langs); + else print $module->description; + print ''."\n"; + print ''; + print img_picto($langs->trans("Enabled"), 'switch_on'); + print ''; + print ''."\n"; + print 'scandir.'&label='.urlencode($module->name).'">'.img_picto($langs->trans("Disabled"), 'switch_off').''; + print "'; + if ($conf->global->TICKET_ADDON_PDF == $name) + { + print img_picto($langs->trans("Default"), 'on'); + } else { + print 'scandir.'&label='.urlencode($module->name).'" alt="'.$langs->trans("Default").'">'.img_picto($langs->trans("Disabled"), 'off').''; + } + print ''; + print $form->textwithpicto('', $htmltooltip, 1, 0); + print ''; + if ($module->type == 'pdf') + { + print ''.img_object($langs->trans("Preview"), 'pdf').''; + } else { + print img_object($langs->trans("PreviewNotAvailable"), 'generic'); + } + print '
'; +print '

'; + + if (!$conf->use_javascript_ajax) { print ''; print ''; diff --git a/htdocs/core/modules/bom/doc/doc_generic_bom_odt.modules.php b/htdocs/core/modules/bom/doc/doc_generic_bom_odt.modules.php index b5e1fc80a1d..18f2d405acf 100644 --- a/htdocs/core/modules/bom/doc/doc_generic_bom_odt.modules.php +++ b/htdocs/core/modules/bom/doc/doc_generic_bom_odt.modules.php @@ -162,10 +162,10 @@ class doc_generic_bom_odt extends ModelePDFBom if ($nbofiles) { - $texte .= '
'; } - $texte .= '