From 485ef522d26aee2c0f36984315d4f0a1aa4535d3 Mon Sep 17 00:00:00 2001 From: Anthony Berton Date: Sun, 3 May 2020 19:47:10 +0200 Subject: [PATCH 01/22] =?UTF-8?q?Add=20option=20HT=20TTC=20cr=C3=A9ate=20p?= =?UTF-8?q?roduct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- htdocs/product/admin/product.php | 11 +++++++++++ htdocs/product/card.php | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/htdocs/product/admin/product.php b/htdocs/product/admin/product.php index e1fe84600d1..4841c97f2bc 100644 --- a/htdocs/product/admin/product.php +++ b/htdocs/product/admin/product.php @@ -129,6 +129,9 @@ 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); @@ -598,6 +601,14 @@ if (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_ print ''; } +//Default product price base type +print ''; +print ''.$langs->trans("DefaultPriceType").''; +print ''; +print $form->selectPriceBaseType($conf->global->PRODUCT_PRICE_BASE_TYPE, "price_base_type"); +print ''; +print ''; + // sousproduits activation/desactivation print ''; diff --git a/htdocs/product/card.php b/htdocs/product/card.php index 87e86e8e0bc..87e42b537e3 100644 --- a/htdocs/product/card.php +++ b/htdocs/product/card.php @@ -1222,7 +1222,7 @@ else // Price print ''.$langs->trans("SellingPrice").''; print ''; - print $form->selectPriceBaseType($object->price_base_type, "price_base_type"); + print $form->selectPriceBaseType($conf->global->PRODUCT_PRICE_BASE_TYPE, "price_base_type"); print ''; // Min price From 56a5e8ca3ca28b5bb30f1267898bcdcacb926ed1 Mon Sep 17 00:00:00 2001 From: stickler-ci Date: Sun, 3 May 2020 17:50:10 +0000 Subject: [PATCH 02/22] Fixing style errors. --- htdocs/product/admin/product.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/htdocs/product/admin/product.php b/htdocs/product/admin/product.php index 4841c97f2bc..c0d1b26b93f 100644 --- a/htdocs/product/admin/product.php +++ b/htdocs/product/admin/product.php @@ -131,7 +131,7 @@ 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); @@ -608,7 +608,7 @@ print ''; print $form->selectPriceBaseType($conf->global->PRODUCT_PRICE_BASE_TYPE, "price_base_type"); print ''; print ''; - + // sousproduits activation/desactivation print ''; From c5544714c23f41071db2eb6ca9db9f4dcb48d42a Mon Sep 17 00:00:00 2001 From: Anthony Berton Date: Sun, 3 May 2020 23:10:40 +0200 Subject: [PATCH 03/22] Traslate title param --- htdocs/langs/en_US/products.lang | 1 + htdocs/langs/fr_FR/products.lang | 1 + 2 files changed, 2 insertions(+) diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index a31243a07b6..c0c5cfa0268 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -106,6 +106,7 @@ NoteNotVisibleOnBill=Note (not visible on invoices, proposals...) ServiceLimitedDuration=If product is a service with limited duration: MultiPricesAbility=Multiple price segments per product/service (each customer is in one price segment) MultiPricesNumPrices=Number of prices +DefaultPriceType= Default price type AssociatedProductsAbility=Activate virtual products (kits) AssociatedProducts=Virtual products AssociatedProductsNumber=Number of products composing this virtual product diff --git a/htdocs/langs/fr_FR/products.lang b/htdocs/langs/fr_FR/products.lang index 9219eb2d1cd..c4b720bb7be 100644 --- a/htdocs/langs/fr_FR/products.lang +++ b/htdocs/langs/fr_FR/products.lang @@ -106,6 +106,7 @@ NoteNotVisibleOnBill=Note (non visible sur les factures, propals...) ServiceLimitedDuration=Si produit de type service à durée limitée : MultiPricesAbility=Plusieurs niveaux de prix par produit/service (chaque client est dans un et un seul niveau) MultiPricesNumPrices=Nombre de prix +DefaultPriceType=Type de prix par default AssociatedProductsAbility=Pris en charge des produits virtuels (kits) AssociatedProducts=Produits virtuels AssociatedProductsNumber=Nbre de sous-produits constituant ce produit virtuel From 00b9f8445f1ed5e1defd558c061059e554717d7a Mon Sep 17 00:00:00 2001 From: Anthony Berton Date: Mon, 4 May 2020 17:11:44 +0200 Subject: [PATCH 04/22] Add default const --- htdocs/install/mysql/data/llx_const.sql | 6 ++++++ htdocs/install/mysql/migration/11.0.0-12.0.0.sql | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/htdocs/install/mysql/data/llx_const.sql b/htdocs/install/mysql/data/llx_const.sql index ced4a0d8427..47f13e3585a 100644 --- a/htdocs/install/mysql/data/llx_const.sql +++ b/htdocs/install/mysql/data/llx_const.sql @@ -97,3 +97,9 @@ insert into llx_const (name, value, type, visible, entity) VALUES ('CONTRACT_ADD insert into llx_const (name, value, type, visible, entity) VALUES ('USERGROUP_ADDON_PDF_ODT_PATH', 'DOL_DATA_ROOT/doctemplates/usergroups', 'chaine', 0, 1); insert into llx_const (name, value, type, visible, entity) VALUES ('USER_ADDON_PDF_ODT_PATH', 'DOL_DATA_ROOT/doctemplates/users', 'chaine', 0, 1); + +-- +-- PRODUCTS +-- +INSERT INTO llx_const (rowid, name, entity, value, type, visible, note, tms) VALUES (NULL, 'PRODUCT_PRICE_BASE_TYPE', '1', 'HT', 'string', '0', NULL, CURRENT_TIMESTAMP); + diff --git a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql index 0730511ad99..39baee700e0 100644 --- a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql +++ b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql @@ -275,4 +275,4 @@ ALTER TABLE llx_prelevement_facture ADD COLUMN fk_facture_fourn INTEGER NULL; ALTER TABLE llx_menu MODIFY COLUMN module varchar(255); - +INSERT INTO llx_const (rowid, name, entity, value, type, visible, note, tms) VALUES (NULL, 'PRODUCT_PRICE_BASE_TYPE', '1', 'HT', 'string', '0', NULL, CURRENT_TIMESTAMP); From ddb413c0254c32000c9cd5b3d2f744a5577b0b68 Mon Sep 17 00:00:00 2001 From: Anthony Berton Date: Sat, 16 May 2020 15:37:13 +0200 Subject: [PATCH 05/22] Create 12.0.0-13.0.0.sql --- htdocs/install/mysql/migration/12.0.0-13.0.0.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 htdocs/install/mysql/migration/12.0.0-13.0.0.sql 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 new file mode 100644 index 00000000000..f25cde89ac3 --- /dev/null +++ b/htdocs/install/mysql/migration/12.0.0-13.0.0.sql @@ -0,0 +1 @@ +INSERT INTO llx_const (rowid, name, entity, value, type, visible, note, tms) VALUES (NULL, 'PRODUCT_PRICE_BASE_TYPE', '1', 'HT', 'string', '0', NULL, CURRENT_TIMESTAMP); From e1f8b509616d6a51eec389c759bfb637f97152dc Mon Sep 17 00:00:00 2001 From: Anthony Berton <34568357+bb2a@users.noreply.github.com> Date: Sat, 16 May 2020 15:39:49 +0200 Subject: [PATCH 06/22] Delete 11.0.0-12.0.0.sql --- .../install/mysql/migration/11.0.0-12.0.0.sql | 292 ------------------ 1 file changed, 292 deletions(-) delete mode 100644 htdocs/install/mysql/migration/11.0.0-12.0.0.sql diff --git a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql deleted file mode 100644 index bc07b751f10..00000000000 --- a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql +++ /dev/null @@ -1,292 +0,0 @@ --- --- Be carefull to requests order. --- This file must be loaded by calling /install/index.php page --- when current version is 12.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 v11 - -UPDATE llx_c_units set scale = 3600 where code = 'H' and unit_type = 'time'; -UPDATE llx_c_units set scale = 86400 where code = 'D' and unit_type = 'time'; - -create table llx_commande_fournisseur_dispatch_extrafields -( - rowid integer AUTO_INCREMENT PRIMARY KEY, - tms timestamp, - fk_object integer NOT NULL, -- object id - import_key varchar(14) -- import key -)ENGINE=innodb; - -ALTER TABLE llx_commande_fournisseur_dispatch_extrafields ADD INDEX idx_commande_fournisseur_dispatch_extrafields (fk_object); - -UPDATE llx_accounting_system SET fk_country = NULL, active = 0 WHERE pcg_version = 'SYSCOHADA'; - -create table llx_c_shipment_package_type -( - rowid integer AUTO_INCREMENT PRIMARY KEY, - label varchar(50) NOT NULL, -- Short name - description varchar(255), -- Description - active integer DEFAULT 1 NOT NULL, -- Active or not - entity integer DEFAULT 1 NOT NULL -- Multi company id -)ENGINE=innodb; - -create table llx_facturedet_rec_extrafields -( - rowid integer AUTO_INCREMENT PRIMARY KEY, - tms timestamp, - fk_object integer NOT NULL, -- object id - import_key varchar(14) -- import key -)ENGINE=innodb; - -ALTER TABLE llx_facturedet_rec_extrafields ADD INDEX idx_facturedet_rec_extrafields (fk_object); - - --- For v12 - --- Delete an old index that is duplicated --- VMYSQL4.1 DROP INDEX ix_fk_product_stock on llx_product_batch; --- VPGSQL8.2 DROP INDEX ix_fk_product_stock - -DELETE FROM llx_menu where module='supplier_proposal'; - -UPDATE llx_website SET lang = 'en' WHERE lang like 'en_%'; -UPDATE llx_website SET lang = 'fr' WHERE lang like 'fr_%'; -UPDATE llx_website SET lang = 'es' WHERE lang like 'es_%'; -UPDATE llx_website SET lang = 'de' WHERE lang like 'de_%'; -UPDATE llx_website SET lang = 'it' WHERE lang like 'it_%'; -UPDATE llx_website SET lang = 'pt' WHERE lang like 'pt_%'; -UPDATE llx_website_page SET lang = 'en' WHERE lang like 'en_%'; -UPDATE llx_website_page SET lang = 'fr' WHERE lang like 'fr_%'; -UPDATE llx_website_page SET lang = 'es' WHERE lang like 'es_%'; -UPDATE llx_website_page SET lang = 'de' WHERE lang like 'de_%'; -UPDATE llx_website_page SET lang = 'it' WHERE lang like 'it_%'; -UPDATE llx_website_page SET lang = 'pt' WHERE lang like 'pt_%'; - -ALTER TABLE llx_website ADD COLUMN lang varchar(8); -ALTER TABLE llx_website ADD COLUMN otherlang varchar(255); - -ALTER TABLE llx_website_page ADD COLUMN author_alias varchar(64); - -UPDATE llx_rights_def SET perms = 'order_advance', subperms = 'close' WHERE module = 'commande' AND perms = 'cloturer'; -UPDATE llx_rights_def SET perms = 'propal_advance', subperms = 'close' WHERE module = 'propale' AND perms = 'cloturer'; - -ALTER TABLE llx_holiday_users DROP INDEX uk_holiday_users; -ALTER TABLE llx_holiday_users ADD UNIQUE INDEX uk_holiday_users(fk_user, fk_type); - -ALTER TABLE llx_ticket ADD COLUMN import_key varchar(14); - -ALTER TABLE llx_ticket ADD UNIQUE uk_ticket_ref (ref, entity); -ALTER TABLE llx_ticket ADD INDEX idx_ticket_entity (entity); -ALTER TABLE llx_ticket ADD INDEX idx_ticket_fk_soc (fk_soc); -ALTER TABLE llx_ticket ADD INDEX idx_ticket_fk_user_assign (fk_user_assign); -ALTER TABLE llx_ticket ADD INDEX idx_ticket_fk_project (fk_project); -ALTER TABLE llx_ticket ADD INDEX idx_ticket_fk_statut (fk_statut); - - ---ALTER TABLE llx_facturerec DROP COLUMN vat_src_code; - - --- Migration to the new regions (France) -UPDATE llx_c_regions set nom = 'Centre-Val de Loire' WHERE fk_pays = 1 AND code_region = 24; -insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 27, '21231', 0, 'Bourgogne-Franche-Comté'); -insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 28, '76540', 0, 'Normandie'); -insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 32, '59350', 4, 'Hauts-de-France'); -insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 44, '67482', 2, 'Grand Est'); -insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 75, '33063', 0, 'Nouvelle-Aquitaine'); -insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 76, '31355', 1, 'Occitanie'); -insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 84, '69123', 1, 'Auvergne-Rhône-Alpes'); - -UPDATE llx_c_departements set fk_region = 27 WHERE fk_region = 26 OR fk_region = 43; -UPDATE llx_c_departements set fk_region = 28 WHERE fk_region = 25 OR fk_region = 23; -UPDATE llx_c_departements set fk_region = 32 WHERE fk_region = 22 OR fk_region = 31; -UPDATE llx_c_departements set fk_region = 44 WHERE fk_region = 21 OR fk_region = 41 OR fk_region = 42; -UPDATE llx_c_departements set fk_region = 75 WHERE fk_region = 54 OR fk_region = 74 OR fk_region = 72; -UPDATE llx_c_departements set fk_region = 76 WHERE fk_region = 73 OR fk_region = 91; -UPDATE llx_c_departements set fk_region = 84 WHERE fk_region = 82 OR fk_region = 83; - -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 21; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 22; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 23; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 25; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 26; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 31; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 41; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 42; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 43; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 54; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 72; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 73; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 74; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 82; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 83; -DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 91; - -ALTER TABLE llx_bookmark DROP INDEX uk_bookmark_url; -ALTER TABLE llx_bookmark DROP INDEX uk_bookmark_title; - -ALTER TABLE llx_bookmark MODIFY COLUMN url TEXT; - -ALTER TABLE llx_bookmark ADD UNIQUE uk_bookmark_title (fk_user, entity, title); - - -ALTER TABLE llx_societe_rib ADD COLUMN stripe_account varchar(128); - -create table llx_object_lang -( - rowid integer AUTO_INCREMENT PRIMARY KEY, - fk_object integer DEFAULT 0 NOT NULL, - type_object varchar(32) NOT NULL, -- value found into $object->element - property varchar(32) NOT NULL, - lang varchar(5) DEFAULT 0 NOT NULL, - value text, - import_key varchar(14) DEFAULT NULL -)ENGINE=innodb; - - -ALTER TABLE llx_object_lang ADD UNIQUE INDEX uk_object_lang (fk_object, type_object, property, lang); - - -CREATE TABLE llx_categorie_actioncomm -( - fk_categorie integer NOT NULL, - fk_actioncomm integer NOT NULL, - import_key varchar(14) -) ENGINE=innodb; - -ALTER TABLE llx_categorie_actioncomm ADD PRIMARY KEY pk_categorie_actioncomm (fk_categorie, fk_actioncomm); -ALTER TABLE llx_categorie_actioncomm ADD INDEX idx_categorie_actioncomm_fk_categorie (fk_categorie); -ALTER TABLE llx_categorie_actioncomm ADD INDEX idx_categorie_actioncomm_fk_actioncomm (fk_actioncomm); - -ALTER TABLE llx_categorie_actioncomm ADD CONSTRAINT fk_categorie_actioncomm_categorie_rowid FOREIGN KEY (fk_categorie) REFERENCES llx_categorie (rowid); -ALTER TABLE llx_categorie_actioncomm ADD CONSTRAINT fk_categorie_actioncomm_fk_actioncomm FOREIGN KEY (fk_actioncomm) REFERENCES llx_actioncomm (id); - - -ALTER TABLE llx_accounting_account ADD COLUMN labelshort varchar(255) DEFAULT NULL after label; - -ALTER TABLE llx_subscription ADD COLUMN fk_user_creat integer DEFAULT NULL; -ALTER TABLE llx_subscription ADD COLUMN fk_user_valid integer DEFAULT NULL; - -UPDATE llx_c_forme_juridique set libelle = 'SRL - Société à responsabilité limitée' WHERE code = '201'; - -ALTER TABLE llx_c_country ADD COLUMN eec integer; -UPDATE llx_c_country SET eec = 1 WHERE code IN ('AT','BE','BG','CY','CZ','DE','DK','EE','ES','FI','FR','GB','GR','HR','NL','HU','IE','IM','IT','LT','LU','LV','MC','MT','PL','PT','RO','SE','SK','SI','UK'); - -INSERT INTO llx_accounting_system (fk_country, pcg_version, label, active) VALUES ( 1, 'PCG18-ASSOC', 'French foundation chart of accounts 2018', 1); - -INSERT INTO llx_accounting_system (fk_country, pcg_version, label, active) VALUES ( 1, 'PCGAFR14-DEV', 'The developed farm accountancy french plan 2014', 1); - -INSERT INTO llx_accounting_system (fk_country, pcg_version, label, active) VALUES ( 41, 'AT-BASE', 'Plan Austria', 1); - - - -create table llx_c_ticket_resolution -( - rowid integer AUTO_INCREMENT PRIMARY KEY, - entity integer DEFAULT 1, - code varchar(32) NOT NULL, - pos varchar(32) NOT NULL, - label varchar(128) NOT NULL, - active integer DEFAULT 1, - use_default integer DEFAULT 1, - description varchar(255) -)ENGINE=innodb; - -ALTER TABLE llx_c_ticket_resolution ADD UNIQUE INDEX uk_code (code, entity); - -INSERT INTO llx_c_ticket_resolution (code, pos, label, active, use_default, description) VALUES('SOLVED', '10', 'Solved', 1, 0, NULL); -INSERT INTO llx_c_ticket_resolution (code, pos, label, active, use_default, description) VALUES('CANCELED', '50', 'Canceled', 1, 0, NULL); -INSERT INTO llx_c_ticket_resolution (code, pos, label, active, use_default, description) VALUES('OTHER', '90', 'Other', 1, 0, NULL); - -DELETE FROM llx_const WHERE name = __ENCRYPT('DONATION_ART885')__; - -ALTER TABLE llx_extrafields MODIFY COLUMN printable integer DEFAULT 0; -ALTER TABLE llx_extrafields ADD COLUMN printable integer DEFAULT 0; - -UPDATE llx_const SET name = 'INVOICE_USE_RETAINED_WARRANTY' WHERE name = 'INVOICE_USE_SITUATION_RETAINED_WARRANTY'; - -ALTER TABLE llx_accounting_account DROP COLUMN pcg_subtype; - -ALTER TABLE llx_product ADD COLUMN accountancy_code_buy_intra varchar(32) AFTER accountancy_code_buy; -ALTER TABLE llx_product ADD COLUMN accountancy_code_buy_export varchar(32) AFTER accountancy_code_buy_intra; - -ALTER TABLE llx_entrepot ADD COLUMN fax varchar(20) DEFAULT NULL; -ALTER TABLE llx_entrepot ADD COLUMN phone varchar(20) DEFAULT NULL; - -ALTER TABLE llx_accounting_account ADD COLUMN reconcilable tinyint DEFAULT 0 NOT NULL after active; - -ALTER TABLE llx_categorie MODIFY type integer NOT NULL DEFAULT 1; - -ALTER TABLE llx_societe_remise_except ADD COLUMN vat_src_code varchar(10) DEFAULT ''; - -ALTER TABLE llx_blockedlog MODIFY COLUMN object_data mediumtext; -ALTER TABLE llx_blockedlog ADD COLUMN object_version varchar(32) DEFAULT ''; - -ALTER TABLE llx_product_lot MODIFY COLUMN batch varchar(128); -ALTER TABLE llx_product_batch MODIFY COLUMN batch varchar(128); -ALTER TABLE llx_commande_fournisseur_dispatch MODIFY COLUMN batch varchar(128); -ALTER TABLE llx_stock_mouvement MODIFY COLUMN batch varchar(128); -ALTER TABLE llx_mrp_production MODIFY COLUMN batch varchar(128); -ALTER TABLE llx_mrp_production MODIFY qty real NOT NULL DEFAULT 1; - -create table llx_categorie_website_page -( - fk_categorie integer NOT NULL, - fk_website_page integer NOT NULL, - import_key varchar(14) -)ENGINE=innodb; - -ALTER TABLE llx_categorie_website_page ADD PRIMARY KEY pk_categorie_website_page (fk_categorie, fk_website_page); -ALTER TABLE llx_categorie_website_page ADD INDEX idx_categorie_website_page_fk_categorie (fk_categorie); -ALTER TABLE llx_categorie_website_page ADD INDEX idx_categorie_website_page_fk_website_page (fk_website_page); - -ALTER TABLE llx_categorie_website_page ADD CONSTRAINT fk_categorie_website_page_categorie_rowid FOREIGN KEY (fk_categorie) REFERENCES llx_categorie (rowid); -ALTER TABLE llx_categorie_website_page ADD CONSTRAINT fk_categorie_website_page_website_page_rowid FOREIGN KEY (fk_website_page) REFERENCES llx_website_page (rowid); - -ALTER TABLE llx_categorie ADD COLUMN date_creation datetime; -ALTER TABLE llx_categorie ADD COLUMN tms timestamp; -ALTER TABLE llx_categorie ADD COLUMN fk_user_creat integer; -ALTER TABLE llx_categorie ADD COLUMN fk_user_modif integer; - -ALTER TABLE llx_commandedet ADD CONSTRAINT fk_commandedet_fk_commandefourndet FOREIGN KEY (fk_commandefourndet) REFERENCES llx_commande_fournisseurdet (rowid); - - --- VMYSQL4.3 ALTER TABLE llx_prelevement_facture_demande MODIFY COLUMN fk_facture INTEGER NULL; --- VPGSQL8.2 ALTER TABLE llx_prelevement_facture_demande ALTER COLUMN fk_facture DROP NOT NULL; -ALTER TABLE llx_prelevement_facture_demande ADD COLUMN fk_facture_fourn INTEGER NULL; - --- VMYSQL4.3 ALTER TABLE llx_prelevement_facture MODIFY COLUMN fk_facture INTEGER NULL; --- VPGSQL8.2 ALTER TABLE llx_prelevement_facture ALTER COLUMN fk_facture DROP NOT NULL; -ALTER TABLE llx_prelevement_facture ADD COLUMN fk_facture_fourn INTEGER NULL; - -ALTER TABLE llx_menu MODIFY COLUMN module varchar(255); - -UPDATE llx_actioncomm SET fk_action = 50 where fk_action = 40 AND code = 'TICKET_MSG'; - -ALTER TABLE llx_emailcollector_emailcollector ADD COLUMN hostcharset varchar(16) DEFAULT 'UTF-8'; - -INSERT INTO llx_const (rowid, name, entity, value, type, visible, note, tms) VALUES (NULL, 'PRODUCT_PRICE_BASE_TYPE', '1', 'HT', 'string', '0', NULL, CURRENT_TIMESTAMP); - From 15805562d1867b276665d1750fe7419a9642eee9 Mon Sep 17 00:00:00 2001 From: Anthony Berton Date: Wed, 20 May 2020 14:50:40 +0200 Subject: [PATCH 07/22] Update html.form.class.php --- htdocs/core/class/html.form.class.php | 62 ++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 5fe411c6260..88348edf5d2 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -2610,21 +2610,61 @@ class Form $objp2 = $this->db->fetch_object($result2); if ($objp2) { - $found = 1; - if ($objp2->price_base_type == 'HT') - { - $opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT"); - $outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT"); + if ($price_level != 1 && $objp2->price == 0 && !empty($conf->global->PRODUIT_MULTIPRICES_USELEVEL1_IFLEVELVALISNUL)){ + $sql = "SELECT price, price_ttc, price_base_type, tva_tx"; + $sql .= " FROM ".MAIN_DB_PREFIX."product_price"; + $sql .= " WHERE fk_product='".$objp->rowid."'"; + $sql .= " AND entity IN (".getEntity('productprice').")"; + $sql .= " AND price_level=1"; + $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid. + $sql .= " LIMIT 1"; + dol_syslog(get_class($this).'::constructProductListOption search price for product '.$objp->rowid.' AND level '.$price_level.'', LOG_DEBUG); + $result2 = $this->db->query($sql); + if ($result2) + { + $objp2 = $this->db->fetch_object($result2); + if ($objp2) + { + $found = 1; + if ($objp2->price_base_type == 'HT') + { + //$opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT"); + //$outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT"); + } + else + { + $opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC"); + $outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC"); + } + $outprice_ht = price($objp2->price); + $outprice_ttc = price($objp2->price_ttc); + $outpricebasetype = $objp2->price_base_type; + $outtva_tx = $objp2->tva_tx; + } + } + else + { + dol_print_error($this->db); + } } else { - $opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC"); - $outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC"); + $found = 1; + if ($objp2->price_base_type == 'HT') + { + $opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT"); + $outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT"); + } + else + { + $opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC"); + $outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC"); + } + $outprice_ht = price($objp2->price); + $outprice_ttc = price($objp2->price_ttc); + $outpricebasetype = $objp2->price_base_type; + $outtva_tx = $objp2->tva_tx; } - $outprice_ht = price($objp2->price); - $outprice_ttc = price($objp2->price_ttc); - $outpricebasetype = $objp2->price_base_type; - $outtva_tx = $objp2->tva_tx; } } else From aa275f2a795bbf221d8878ec7f2e09b0f8706335 Mon Sep 17 00:00:00 2001 From: Anthony Berton <34568357+bb2a@users.noreply.github.com> Date: Wed, 20 May 2020 14:58:01 +0200 Subject: [PATCH 08/22] Delete html.form.class.php --- htdocs/core/class/html.form.class.php | 8282 ------------------------- 1 file changed, 8282 deletions(-) delete mode 100644 htdocs/core/class/html.form.class.php diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php deleted file mode 100644 index 008006b8fa5..00000000000 --- a/htdocs/core/class/html.form.class.php +++ /dev/null @@ -1,8282 +0,0 @@ - - * Copyright (C) 2004-2012 Laurent Destailleur - * Copyright (C) 2004 Benoit Mortier - * Copyright (C) 2004 Sebastien Di Cintio - * Copyright (C) 2004 Eric Seigne - * Copyright (C) 2005-2017 Regis Houssin - * Copyright (C) 2006 Andre Cianfarani - * Copyright (C) 2006 Marc Barilley/Ocebo - * Copyright (C) 2007 Franky Van Liedekerke - * Copyright (C) 2007 Patrick Raguin - * Copyright (C) 2010 Juanjo Menent - * Copyright (C) 2010-2019 Philippe Grand - * Copyright (C) 2011 Herve Prot - * 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) 2018 Ferran Marcet - * Copyright (C) 2018-2019 Frédéric France - * Copyright (C) 2018 Nicolas ZABOURI - * Copyright (C) 2018 Christophe Battarel - * Copyright (C) 2018 Josep Lluis Amador - * - * 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/class/html.form.class.php - * \ingroup core - * \brief File of class with all html predefined components - */ - - -/** - * Class to manage generation of HTML components - * Only common components must be here. - * - * TODO Merge all function load_cache_* and loadCache* (except load_cache_vatrates) into one generic function loadCacheTable - */ -class Form -{ - /** - * @var DoliDB Database handler. - */ - public $db; - - /** - * @var string Error code (or message) - */ - public $error = ''; - - /** - * @var string[] Array of error strings - */ - public $errors = array(); - - public $num; - - // Cache arrays - public $cache_types_paiements = array(); - public $cache_conditions_paiements = array(); - public $cache_availability = array(); - public $cache_demand_reason = array(); - public $cache_types_fees = array(); - public $cache_vatrates = array(); - - - /** - * Constructor - * - * @param DoliDB $db Database handler - */ - public function __construct($db) - { - $this->db = $db; - } - - /** - * Output key field for an editable field - * - * @param string $text Text of label or key to translate - * @param string $htmlname Name of select field ('edit' prefix will be added) - * @param string $preselected Value to show/edit (not used in this function) - * @param object $object Object - * @param boolean $perm Permission to allow button to edit parameter. Set it to 0 to have a not edited field. - * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...) - * @param string $moreparam More param to add on a href URL. - * @param int $fieldrequired 1 if we want to show field as mandatory using the "fieldrequired" CSS. - * @param int $notabletag 1=Do not output table tags but output a ':', 2=Do not output table tags and no ':', 3=Do not output table tags but output a ' ' - * @param string $paramid Key of parameter for id ('id', 'socid') - * @param string $help Tooltip help - * @return string HTML edit field - */ - public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '') - { - global $conf, $langs; - - $ret = ''; - - // TODO change for compatibility - if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;/', $typeofdata)) - { - if (!empty($perm)) - { - $tmp = explode(':', $typeofdata); - $ret .= '
'; - if ($fieldrequired) $ret .= ''; - if ($help) { - $ret .= $this->textwithpicto($langs->trans($text), $help); - } else { - $ret .= $langs->trans($text); - } - if ($fieldrequired) $ret .= ''; - $ret .= '
'."\n"; - } - else - { - if ($fieldrequired) $ret .= ''; - if ($help) { - $ret .= $this->textwithpicto($langs->trans($text), $help); - } else { - $ret .= $langs->trans($text); - } - if ($fieldrequired) $ret .= ''; - } - } - else - { - if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= ''; - if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= ''; - if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= '
'; - if ($fieldrequired) $ret .= ''; - if ($help) { - $ret .= $this->textwithpicto($langs->trans($text), $help); - } else { - $ret .= $langs->trans($text); - } - if ($fieldrequired) $ret .= ''; - if (!empty($notabletag)) $ret .= ' '; - if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= ''; - if ($htmlname && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= 'id.$moreparam.'">'.img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1)).''; - if (!empty($notabletag) && $notabletag == 1) $ret .= ' : '; - if (!empty($notabletag) && $notabletag == 3) $ret .= ' '; - if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= '
'; - } - - return $ret; - } - - /** - * Output value of a field for an editable field - * - * @param string $text Text of label (not used in this function) - * @param string $htmlname Name of select field - * @param string $value Value to show/edit - * @param object $object Object - * @param boolean $perm Permission to allow button to edit parameter - * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols%', 'datepicker' ('day' do not work, don't know why), 'dayhour' or 'datepickerhour', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select;xkey:xval,ykey:yval,...') - * @param string $editvalue When in edit mode, use this value as $value instead of value (for example, you can provide here a formated price instead of value). Use '' to use same than $value - * @param object $extObject External object - * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage') - * @param string $moreparam More param to add on the form action href URL - * @param int $notabletag Do no output table tags - * @param string $formatfunc Call a specific function to output field - * @param string $paramid Key of parameter for id ('id', 'socid') - * @return string HTML edit field - */ - public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 0, $formatfunc = '', $paramid = 'id') - { - global $conf, $langs, $db; - - $ret = ''; - - // Check parameters - if (empty($typeofdata)) return 'ErrorBadParameter'; - - // When option to edit inline is activated - if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;|datehourpicker/', $typeofdata)) // TODO add jquery timepicker and support select - { - $ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg); - } - else - { - if (GETPOST('action', 'aZ09') == 'edit'.$htmlname) - { - $ret .= "\n"; - $ret .= '
'; - $ret .= ''; - $ret .= ''; - $ret .= ''; - if (empty($notabletag)) $ret .= ''; - if (empty($notabletag)) $ret .= ''; - - if (empty($notabletag)) $ret .= ''; - - if (empty($notabletag)) $ret .= '
'; - if (preg_match('/^(string|safehtmlstring|email)/', $typeofdata)) - { - $tmp = explode(':', $typeofdata); - $ret .= ''; - } - elseif (preg_match('/^(numeric|amount)/', $typeofdata)) - { - $tmp = explode(':', $typeofdata); - $valuetoshow = price2num($editvalue ? $editvalue : $value); - $ret .= ''; - } - elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) // if wysiwyg is enabled $typeofdata = 'ckeditor' - { - $tmp = explode(':', $typeofdata); - $cols = $tmp[2]; - $morealt = ''; - if (preg_match('/%/', $cols)) - { - $morealt = ' style="width: '.$cols.'"'; - $cols = ''; - } - - $valuetoshow = ($editvalue ? $editvalue : $value); - $ret .= ''; - } - elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') - { - $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form'.$htmlname, 1, 0); - } - elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') - { - $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form'.$htmlname, 1, 0); - } - elseif (preg_match('/^select;/', $typeofdata)) - { - $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata)); - $arraylist = array(); - foreach ($arraydata as $val) - { - $tmp = explode(':', $val); - $tmpkey = str_replace('|', ':', $tmp[0]); - $arraylist[$tmpkey] = $tmp[1]; - } - $ret .= $this->selectarray($htmlname, $arraylist, $value); - } - elseif (preg_match('/^ckeditor/', $typeofdata)) - { - $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser - require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; - $doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), ($tmp[2] ? $tmp[2] : ''), ($tmp[3] ? $tmp[3] : '100'), ($tmp[1] ? $tmp[1] : 'dolibarr_notes'), 'In', ($tmp[5] ? $tmp[5] : 0), (isset($tmp[8]) ? ($tmp[8] ?true:false) : true), true, ($tmp[6] ? $tmp[6] : '20'), ($tmp[7] ? $tmp[7] : '100')); - $ret .= $doleditor->Create(1); - } - if (empty($notabletag)) $ret .= ''; - //else $ret.='
'; - $ret .= ''; - if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) $ret .= '
'."\n"; - $ret .= ''; - if (empty($notabletag)) $ret .= '
'."\n"; - $ret .= '
'."\n"; - } - else - { - if (preg_match('/^(email)/', $typeofdata)) $ret .= dol_print_email($value, 0, 0, 0, 0, 1); - elseif (preg_match('/^(amount|numeric)/', $typeofdata)) $ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : ''); - elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) $ret .= dol_htmlentitiesbr($value); - elseif (preg_match('/^safehtmlstring/', $typeofdata)) $ret .= dol_string_onlythesehtmltags($value); - elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') $ret .= dol_print_date($value, 'day'); - elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') $ret .= dol_print_date($value, 'dayhour'); - elseif (preg_match('/^select;/', $typeofdata)) - { - $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata)); - $arraylist = array(); - foreach ($arraydata as $val) - { - $tmp = explode(':', $val); - $arraylist[$tmp[0]] = $tmp[1]; - } - $ret .= $arraylist[$value]; - } - elseif (preg_match('/^ckeditor/', $typeofdata)) - { - $tmpcontent = dol_htmlentitiesbr($value); - if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) - { - $firstline = preg_replace('/
.*/', '', $tmpcontent); - $firstline = preg_replace('/[\n\r].*/', '', $firstline); - $tmpcontent = $firstline.((strlen($firstline) != strlen($tmpcontent)) ? '...' : ''); - } - // We dont use dol_escape_htmltag to get the html formating active, but this need we must also - // clean data from some dangerous html - $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent)); - } - else { - $ret .= dol_escape_htmltag($value); - } - - if ($formatfunc && method_exists($object, $formatfunc)) - { - $ret = $object->$formatfunc($ret); - } - } - } - return $ret; - } - - /** - * Output edit in place form - * - * @param string $fieldname Name of the field - * @param object $object Object - * @param boolean $perm Permission to allow button to edit parameter. Set it to 0 to have a not edited field. - * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...) - * @param string $check Same coe than $check parameter of GETPOST() - * @param string $morecss More CSS - * @return string HTML code for the edit of alternative language - */ - public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '') - { - global $conf, $langs, $extralanguages; - - $result = ''; - - // List of extra languages - $arrayoflangcode = array(); - if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE; - - if (is_array($arrayoflangcode) && count($arrayoflangcode)) { - if (!is_object($extralanguages)) { - include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php'; - $extralanguages = new ExtraLanguages($this->db); - } - $extralanguages->fetch_name_extralanguages('societe'); - - if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) { - return ''; // No extralang field to show - } - - $result .= ''."\n"; - $result .= '
'; - $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang'); - $result .= $s; - $result .= '
'; - - $result .= ''; - $result .= ''; - } - - return $result; - } - - /** - * Output edit in place form - * - * @param object $object Object - * @param string $value Value to show/edit - * @param string $htmlname DIV ID (field name) - * @param int $condition Condition to edit - * @param string $inputType Type of input ('string', 'numeric', 'datepicker' ('day' do not work, don't know why), 'textarea:rows:cols', 'ckeditor:dolibarr_zzz:width:height:?:1:rows:cols', 'select:loadmethod:savemethod:buttononly') - * @param string $editvalue When in edit mode, use this value as $value instead of value - * @param object $extObject External object - * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage') - * @return string HTML edit in place - */ - protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null) - { - global $conf; - - $out = ''; - - // Check parameters - if (preg_match('/^text/', $inputType)) $value = dol_nl2br($value); - elseif (preg_match('/^numeric/', $inputType)) $value = price($value); - elseif ($inputType == 'day' || $inputType == 'datepicker') $value = dol_print_date($value, 'day'); - - if ($condition) - { - $element = false; - $table_element = false; - $fk_element = false; - $loadmethod = false; - $savemethod = false; - $ext_element = false; - $button_only = false; - $inputOption = ''; - - if (is_object($object)) - { - $element = $object->element; - $table_element = $object->table_element; - $fk_element = $object->id; - } - - if (is_object($extObject)) - { - $ext_element = $extObject->element; - } - - if (preg_match('/^(string|email|numeric)/', $inputType)) - { - $tmp = explode(':', $inputType); - $inputType = $tmp[0]; - if (!empty($tmp[1])) $inputOption = $tmp[1]; - if (!empty($tmp[2])) $savemethod = $tmp[2]; - $out .= ''."\n"; - } - elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) - { - $tmp = explode(':', $inputType); - $inputType = $tmp[0]; - if (!empty($tmp[1])) $inputOption = $tmp[1]; - if (!empty($tmp[2])) $savemethod = $tmp[2]; - - $out .= ''."\n"; // Use for timestamp format - } - elseif (preg_match('/^(select|autocomplete)/', $inputType)) - { - $tmp = explode(':', $inputType); - $inputType = $tmp[0]; $loadmethod = $tmp[1]; - if (!empty($tmp[2])) $savemethod = $tmp[2]; - if (!empty($tmp[3])) $button_only = true; - } - elseif (preg_match('/^textarea/', $inputType)) - { - $tmp = explode(':', $inputType); - $inputType = $tmp[0]; - $rows = (empty($tmp[1]) ? '8' : $tmp[1]); - $cols = (empty($tmp[2]) ? '80' : $tmp[2]); - } - elseif (preg_match('/^ckeditor/', $inputType)) - { - $tmp = explode(':', $inputType); - $inputType = $tmp[0]; $toolbar = $tmp[1]; - if (!empty($tmp[2])) $width = $tmp[2]; - if (!empty($tmp[3])) $heigth = $tmp[3]; - if (!empty($tmp[4])) $savemethod = $tmp[4]; - - if (!empty($conf->fckeditor->enabled)) - { - $out .= ''."\n"; - } - else - { - $inputType = 'textarea'; - } - } - - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - $out .= ''."\n"; - if (!empty($savemethod)) $out .= ''."\n"; - if (!empty($ext_element)) $out .= ''."\n"; - if (!empty($custommsg)) - { - if (is_array($custommsg)) - { - if (!empty($custommsg['success'])) - $out .= ''."\n"; - if (!empty($custommsg['error'])) - $out .= ''."\n"; - } - else - $out .= ''."\n"; - } - if ($inputType == 'textarea') { - $out .= ''."\n"; - $out .= ''."\n"; - } - $out .= ''.$value.''."\n"; - $out .= ''.(!empty($editvalue) ? $editvalue : $value).''."\n"; - } - else - { - $out = $value; - } - - return $out; - } - - /** - * Show a text and picto with tooltip on text or picto. - * Can be called by an instancied $form->textwithtooltip or by a static call Form::textwithtooltip - * - * @param string $text Text to show - * @param string $htmltext HTML content of tooltip. Must be HTML/UTF8 encoded. - * @param int $tooltipon 1=tooltip on text, 2=tooltip on image, 3=tooltip sur les 2 - * @param int $direction -1=image is before, 0=no image, 1=image is after - * @param string $img Html code for image (use img_xxx() function to get it) - * @param string $extracss Add a CSS style to td tags - * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span - * @param string $incbefore Include code before the text - * @param int $noencodehtmltext Do not encode into html entity the htmltext - * @param string $tooltiptrigger ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key) - * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only) - * @return string Code html du tooltip (texte+picto) - * @see textwithpicto() Use thisfunction if you can. - * TODO Move this as static as soon as everybody use textwithpicto or @Form::textwithtooltip - */ - public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0) - { - if ($incbefore) $text = $incbefore.$text; - if (!$htmltext) return $text; - - $tag = 'td'; - if ($notabs == 2) $tag = 'div'; - if ($notabs == 3) $tag = 'span'; - // Sanitize tooltip - $htmltext = str_replace(array("\r", "\n"), '', $htmltext); - - $extrastyle = ''; - if ($direction < 0) { $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : ''); $extrastyle = 'padding: 0px; padding-left: 3px !important;'; } - if ($direction > 0) { $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : ''); $extrastyle = 'padding: 0px; padding-right: 3px !important;'; } - - $classfortooltip = 'classfortooltip'; - - $s = ''; $textfordialog = ''; - - if ($tooltiptrigger == '') - { - $htmltext = str_replace('"', '"', $htmltext); - } - else - { - $classfortooltip = 'classfortooltiponclick'; - $textfordialog .= ''; - } - if ($tooltipon == 2 || $tooltipon == 3) - { - $paramfortooltipimg = ' class="'.$classfortooltip.($notabs != 3 ? ' inline-block' : '').($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'"'; - if ($tooltiptrigger == '') $paramfortooltipimg .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on img tag to store tooltip - else $paramfortooltipimg .= ' dolid="'.$tooltiptrigger.'"'; - } - else $paramfortooltipimg = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag - if ($tooltipon == 1 || $tooltipon == 3) - { - $paramfortooltiptd = ' class="'.($tooltipon == 3 ? 'cursorpointer ' : '').$classfortooltip.' inline-block'.($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'" '; - if ($tooltiptrigger == '') $paramfortooltiptd .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on td tag to store tooltip - else $paramfortooltiptd .= ' dolid="'.$tooltiptrigger.'"'; - } - else $paramfortooltiptd = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag - if (empty($notabs)) $s .= ''; - elseif ($notabs == 2) $s .= '
'; - // Define value if value is before - if ($direction < 0) { - $s .= '<'.$tag.$paramfortooltipimg; - if ($tag == 'td') { - $s .= ' class=valigntop" width="14"'; - } - $s .= '>'.$textfordialog.$img.''; - } - // Use another method to help avoid having a space in value in order to use this value with jquery - // Define label - if ((string) $text != '') $s .= '<'.$tag.$paramfortooltiptd.'>'.$text.''; - // Define value if value is after - if ($direction > 0) { - $s .= '<'.$tag.$paramfortooltipimg; - if ($tag == 'td') $s .= ' class="valignmiddle" width="14"'; - $s .= '>'.$textfordialog.$img.''; - } - if (empty($notabs)) $s .= '
'; - elseif ($notabs == 2) $s .= ''; - - return $s; - } - - /** - * Show a text with a picto and a tooltip on picto - * - * @param string $text Text to show - * @param string $htmltext Content of tooltip - * @param int $direction 1=Icon is after text, -1=Icon is before text, 0=no icon - * @param string $type Type of picto ('info', 'infoclickable', 'help', 'helpclickable', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath or 'none' - * @param string $extracss Add a CSS style to td, div or span tag - * @param int $noencodehtmltext Do not encode into html entity the htmltext - * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span - * @param string $tooltiptrigger ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key, clickable link is on image or on link if param $type='none' or on both if $type='xxxclickable') - * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only) - * @return string HTML code of text, picto, tooltip - */ - public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0) - { - global $conf, $langs; - - $alt = ''; - if ($tooltiptrigger) $alt = $langs->transnoentitiesnoconv("ClickToShowHelp"); - - //For backwards compatibility - if ($type == '0') $type = 'info'; - elseif ($type == '1') $type = 'help'; - - // If info or help with no javascript, show only text - if (empty($conf->use_javascript_ajax)) - { - if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') return $text; - else - { - $alt = $htmltext; - $htmltext = ''; - } - } - - // If info or help with smartphone, show only text (tooltip hover can't works) - if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) - { - if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') return $text; - } - // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone) - //if (! empty($conf->dol_no_mouse_hover) && ! empty($tooltiptrigger)) - //{ - //if ($type == 'info' || $type == 'help') return ''.$text.'''; - //} - - $img = ''; - if ($type == 'info') $img = img_help(0, $alt); - elseif ($type == 'help') $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt); - elseif ($type == 'helpclickable') $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt); - elseif ($type == 'superadmin') $img = img_picto($alt, 'redstar'); - elseif ($type == 'admin') $img = img_picto($alt, 'star'); - elseif ($type == 'warning') $img = img_warning($alt); - elseif ($type != 'none') $img = img_picto($alt, $type); // $type can be an image path - - return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap); - } - - /** - * Generate select HTML to choose massaction - * - * @param string $selected Value auto selected when at least one record is selected. Not a preselected value. Use '0' by default. - * @param array $arrayofaction array('code'=>'label', ...). The code is the key stored into the GETPOST('massaction') when submitting action. - * @param int $alwaysvisible 1=select button always visible - * @param string $name Name for massaction - * @param string $cssclass CSS class used to check for select - * @return string|void Select list - */ - public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect') - { - global $conf, $langs, $hookmanager; - - - $disabled = 0; - $ret = '
'; - $ret .= ''; - - if (empty($conf->dol_optimize_smallscreen)) $ret .= ajax_combobox('.' . $name . 'select'); - - // Warning: if you set submit button to disabled, post using 'Enter' will no more work if there is no another input submit. So we add a hidden button - $ret .= ''; // Hidden button BEFORE so it is the one used when we submit with ENTER. - $ret .= ''; - $ret .= '
'; - - if (!empty($conf->use_javascript_ajax)) - { - $ret .= ' - - '; - } - - return $ret; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return combo list of activated countries, into language of user - * - * @param string $selected Id or Code or Label of preselected country - * @param string $htmlname Name of html select object - * @param string $htmloption More html options on select object - * @param integer $maxlength Max length for labels (0=no limit) - * @param string $morecss More css class - * @param string $usecodeaskey ''=Use id as key (default), 'code3'=Use code on 3 alpha as key, 'code2"=Use code on 2 alpha as key - * @param int $showempty Show empty choice - * @param int $disablefavorites 1=Disable favorites, - * @param int $addspecialentries 1=Add dedicated entries for group of countries (like 'European Economic Community', ...) - * @param array $exclude_country_code Array of country code (iso2) to exclude - * @return string HTML string with select - */ - public function select_country($selected = '', $htmlname = 'country_id', $htmloption = '', $maxlength = 0, $morecss = 'minwidth300', $usecodeaskey = '', $showempty = 1, $disablefavorites = 0, $addspecialentries = 0, $exclude_country_code = array()) - { - // phpcs:enable - global $conf, $langs, $mysoc; - - $langs->load("dict"); - - $out = ''; - $countryArray = array(); - $favorite = array(); - $label = array(); - $atleastonefavorite = 0; - - $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_country"; - $sql .= " WHERE active > 0"; - //$sql.= " ORDER BY code ASC"; - - dol_syslog(get_class($this)."::select_country", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - $out .= ''; - } - else - { - dol_print_error($this->db); - } - - // Make select dynamic - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $out .= ajax_combobox('select'.$htmlname); - - return $out; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return select list of incoterms - * - * @param string $selected Id or Code of preselected incoterm - * @param string $location_incoterms Value of input location - * @param string $page Defined the form action - * @param string $htmlname Name of html select object - * @param string $htmloption Options html on select object - * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification) - * @param array $events Event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) - * @return string HTML string with select and input - */ - public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array()) - { - // phpcs:enable - global $conf, $langs; - - $langs->load("dict"); - - $out = ''; - $incotermArray = array(); - - $sql = "SELECT rowid, code"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_incoterms"; - $sql .= " WHERE active > 0"; - $sql .= " ORDER BY code ASC"; - - dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - if ($conf->use_javascript_ajax && !$forcecombo) - { - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $out .= ajax_combobox($htmlname, $events); - } - - if (!empty($page)) - { - $out .= '
'; - $out .= ''; - $out .= ''; - } - - $out .= ''; - - $out .= ''; - - if (!empty($page)) - { - $out .= '
'; - } - } - else - { - dol_print_error($this->db); - } - - return $out; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of types of lines (product or service) - * Example: 0=product, 1=service, 9=other (for external module) - * - * @param string $selected Preselected type - * @param string $htmlname Name of field in html form - * @param int $showempty Add an empty field - * @param int $hidetext Do not show label 'Type' before combo box (used only if there is at least 2 choices to select) - * @param integer $forceall 1=Force to show products and services in combo list, whatever are activated modules, 0=No force, 2=Force to show only Products, 3=Force to show only services, -1=Force none (and set hidden field to 'service') - * @return void - */ - public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0) - { - // phpcs:enable - global $db, $langs, $user, $conf; - - // If product & services are enabled or both disabled. - if ($forceall == 1 || (empty($forceall) && !empty($conf->product->enabled) && !empty($conf->service->enabled)) - || (empty($forceall) && empty($conf->product->enabled) && empty($conf->service->enabled))) - { - if (empty($hidetext)) print $langs->trans("Type").': '; - print ''; - //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1); - } - if ((empty($forceall) && empty($conf->product->enabled) && !empty($conf->service->enabled)) || $forceall == 3) - { - print $langs->trans("Service"); - print ''; - } - if ((empty($forceall) && !empty($conf->product->enabled) && empty($conf->service->enabled)) || $forceall == 2) - { - print $langs->trans("Product"); - print ''; - } - if ($forceall < 0) // This should happened only for contracts when both predefined product and service are disabled. - { - print ''; // By default we set on service for contract. If CONTRACT_SUPPORT_PRODUCTS is set, forceall should be 1 not -1 - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Load into cache cache_types_fees, array of types of fees - * - * @return int Nb of lines loaded, <0 if KO - */ - public function load_cache_types_fees() - { - // phpcs:enable - global $langs; - - $num = count($this->cache_types_fees); - if ($num > 0) return 0; // Cache already loaded - - dol_syslog(__METHOD__, LOG_DEBUG); - - $langs->load("trips"); - - $sql = "SELECT c.code, c.label"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c"; - $sql .= " WHERE active > 0"; - - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - - while ($i < $num) - { - $obj = $this->db->fetch_object($resql); - - // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut - $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label)); - $this->cache_types_fees[$obj->code] = $label; - $i++; - } - - asort($this->cache_types_fees); - - return $num; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of types of notes - * - * @param string $selected Preselected type - * @param string $htmlname Name of field in form - * @param int $showempty Add an empty field - * @return void - */ - public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0) - { - // phpcs:enable - global $user, $langs; - - dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG); - - $this->load_cache_types_fees(); - - print ''; - if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return HTML code to select a company. - * - * @param int $selected Preselected products - * @param string $htmlname Name of HTML select field (must be unique in page) - * @param int $filter Filter on thirdparty - * @param int $limit Limit on number of returned lines - * @param array $ajaxoptions Options for ajax_autocompleter - * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification) - * @return string Return select box for thirdparty. - * @deprecated 3.8 Use select_company instead. For exemple $form->select_thirdparty(GETPOST('socid'),'socid','',0) => $form->select_company(GETPOST('socid'),'socid','',1,0,0,array(),0) - */ - public function select_thirdparty($selected = '', $htmlname = 'socid', $filter = '', $limit = 20, $ajaxoptions = array(), $forcecombo = 0) - { - // phpcs:enable - return $this->select_thirdparty_list($selected, $htmlname, $filter, 1, 0, $forcecombo, array(), '', 0, $limit); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Output html form to select a third party - * - * @param string $selected Preselected type - * @param string $htmlname Name of field in form - * @param string $filter Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>] are allowed here (example: 's.rowid <> x', 's.client IN (1,3)') - * @param string $showempty Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty') - * @param int $showtype Show third party type in combolist (customer, prospect or supplier) - * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification) - * @param array $events Ajax event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) - * @param int $limit Maximum number of elements - * @param string $morecss Add more css styles to the SELECT component - * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container - * @param string $selected_input_value Value of preselected input text (for use with ajax) - * @param int $hidelabel Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after) - * @param array $ajaxoptions Options for ajax_autocompleter - * @param bool $multiple add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter) - * @return string HTML string with select box for thirdparty. - */ - public function select_company($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $limit = 0, $morecss = 'minwidth100', $moreparam = '', $selected_input_value = '', $hidelabel = 1, $ajaxoptions = array(), $multiple = false) - { - // phpcs:enable - global $conf, $user, $langs; - - $out = ''; - - if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) - { - // No immediate load of all database - $placeholder = ''; - if ($selected && empty($selected_input_value)) - { - require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; - $societetmp = new Societe($this->db); - $societetmp->fetch($selected); - $selected_input_value = $societetmp->name; - unset($societetmp); - } - // mode 1 - $urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&filter='.urlencode($filter).($showtype ? '&showtype='.urlencode($showtype) : ''); - $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); - $out .= ''; - if (empty($hidelabel)) print $langs->trans("RefOrLabel").' : '; - elseif ($hidelabel > 1) { - $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"'; - if ($hidelabel == 2) { - $out .= img_picto($langs->trans("Search"), 'search'); - } - } - $out .= 'global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />'; - if ($hidelabel == 3) { - $out .= img_picto($langs->trans("Search"), 'search'); - } - } - else - { - // Immediate load of all database - $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple); - } - - return $out; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Output html form to select a third party. - * Note, you must use the select_company to get the component to select a third party. This function must only be called by select_company. - * - * @param string $selected Preselected type - * @param string $htmlname Name of field in form - * @param string $filter Optional filters criteras (example: 's.rowid <> x', 's.client in (1,3)') - * @param string $showempty Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty') - * @param int $showtype Show third party type in combolist (customer, prospect or supplier) - * @param int $forcecombo Force to use standard HTML select component without beautification - * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) - * @param string $filterkey Filter on key value - * @param int $outputmode 0=HTML select string, 1=Array - * @param int $limit Limit number of answers - * @param string $morecss Add more css styles to the SELECT component - * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container - * @param bool $multiple add [] in the name of element and add 'multiple' attribut - * @return string HTML string with - */ - public function select_thirdparty_list($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $filterkey = '', $outputmode = 0, $limit = 0, $morecss = 'minwidth100', $moreparam = '', $multiple = false) - { - // phpcs:enable - global $conf, $user, $langs; - - $out = ''; - $num = 0; - $outarray = array(); - - if ($selected === '') $selected = array(); - elseif (!is_array($selected)) $selected = array($selected); - - // Clean $filter that may contains sql conditions so sql code - if (function_exists('testSqlAndScriptInject')) { - if (testSqlAndScriptInject($filter, 3) > 0) { - $filter = ''; - } - } - - // On recherche les societes - $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.client, s.fournisseur, s.code_client, s.code_fournisseur"; - - if ($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST) { - $sql .= ", s.address, s.zip, s.town"; - $sql .= ", dictp.code as country_code"; - } - - $sql .= " FROM ".MAIN_DB_PREFIX."societe as s"; - if (!$user->rights->societe->client->voir && !$user->socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; - if ($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST) { - $sql .= " LEFT OUTER JOIN ".MAIN_DB_PREFIX."c_country as dictp ON dictp.rowid=s.fk_pays"; - } - $sql .= " WHERE s.entity IN (".getEntity('societe').")"; - if (!empty($user->socid)) $sql .= " AND s.rowid = ".$user->socid; - if ($filter) $sql .= " AND (".$filter.")"; - if (!$user->rights->societe->client->voir && !$user->socid) $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id; - if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) $sql .= " AND s.status <> 0"; - // Add criteria - if ($filterkey && $filterkey != '') - { - $sql .= " AND ("; - $prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on - // For natural search - $scrit = explode(' ', $filterkey); - $i = 0; - if (count($scrit) > 1) $sql .= "("; - foreach ($scrit as $crit) { - if ($i > 0) $sql .= " AND "; - $sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')"; - $i++; - } - if (count($scrit) > 1) $sql .= ")"; - if (!empty($conf->barcode->enabled)) - { - $sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'"; - } - $sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'"; - $sql .= ")"; - } - $sql .= $this->db->order("nom", "ASC"); - $sql .= $this->db->plimit($limit, 0); - - // Build output string - dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - if (!$forcecombo) - { - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $out .= ajax_combobox($htmlname, $events, $conf->global->COMPANY_USE_SEARCH_TO_SELECT); - } - - // Construct $out and $outarray - $out .= ''."\n"; - } - else - { - dol_print_error($this->db); - } - - $this->result = array('nbofthirdparties'=>$num); - - if ($outputmode) return $outarray; - return $out; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return HTML combo list of absolute discounts - * - * @param string $selected Id remise fixe pre-selectionnee - * @param string $htmlname Nom champ formulaire - * @param string $filter Criteres optionnels de filtre - * @param int $socid Id of thirdparty - * @param int $maxvalue Max value for lines that can be selected - * @return int Return number of qualifed lines in list - */ - public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0) - { - // phpcs:enable - global $langs, $conf; - - // On recherche les remises - $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,"; - $sql .= " re.description, re.fk_facture_source"; - $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re"; - $sql .= " WHERE re.fk_soc = ".(int) $socid; - $sql .= " AND re.entity = ".$conf->entity; - if ($filter) $sql .= " AND ".$filter; - $sql .= " ORDER BY re.description ASC"; - - dol_syslog(get_class($this)."::select_remises", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - print ''; - return $qualifiedlines; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of all contacts (for a third party or all) - * - * @param int $socid Id ot third party or 0 for all - * @param string $selected Id contact pre-selectionne - * @param string $htmlname Name of HTML field ('none' for a not editable field) - * @param int $showempty 0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list - * @param string $exclude List of contacts id to exclude - * @param string $limitto Disable answers that are not id in this array list - * @param integer $showfunction Add function into label - * @param string $moreclass Add more class to class style - * @param integer $showsoc Add company into label - * @param int $forcecombo Force to use combo box - * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) - * @param bool $options_only Return options only (for ajax treatment) - * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container - * @param string $htmlid Html id to use instead of htmlname - * @return int <0 if KO, Nb of contact in list if OK - * @deprected You can use selectcontacts directly (warning order of param was changed) - */ - public function select_contacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $showsoc = 0, $forcecombo = 0, $events = array(), $options_only = false, $moreparam = '', $htmlid = '') - { - // phpcs:enable - print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $moreclass, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid); - return $this->num; - } - - /** - * Return HTML code of the SELECT of list of all contacts (for a third party or all). - * This also set the number of contacts found into $this->num - * - * @since 9.0 Add afterSelectContactOptions hook - * - * @param int $socid Id ot third party or 0 for all or -1 for empty list - * @param array|int $selected Array of ID of pre-selected contact id - * @param string $htmlname Name of HTML field ('none' for a not editable field) - * @param int $showempty 0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list - * @param string $exclude List of contacts id to exclude - * @param string $limitto Disable answers that are not id in this array list - * @param integer $showfunction Add function into label - * @param string $moreclass Add more class to class style - * @param bool $options_only Return options only (for ajax treatment) - * @param integer $showsoc Add company into label - * @param int $forcecombo Force to use combo box (so no ajax beautify effect) - * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) - * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container - * @param string $htmlid Html id to use instead of htmlname - * @param bool $multiple add [] in the name of element and add 'multiple' attribut - * @return int <0 if KO, Nb of contact in list if OK - */ - public function selectcontacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $options_only = false, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false) - { - global $conf, $langs, $hookmanager, $action; - - $langs->load('companies'); - - if (empty($htmlid)) $htmlid = $htmlname; - - if ($selected === '') $selected = array(); - elseif (!is_array($selected)) $selected = array($selected); - $out = ''; - - if (!is_object($hookmanager)) - { - include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; - $hookmanager = new HookManager($this->db); - } - - // We search third parties - $sql = "SELECT sp.rowid, sp.lastname, sp.statut, sp.firstname, sp.poste"; - if ($showsoc > 0) $sql .= " , s.nom as company"; - $sql .= " FROM ".MAIN_DB_PREFIX."socpeople as sp"; - if ($showsoc > 0) $sql .= " LEFT OUTER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid=sp.fk_soc"; - $sql .= " WHERE sp.entity IN (".getEntity('socpeople').")"; - if ($socid > 0 || $socid == -1) $sql .= " AND sp.fk_soc=".$socid; - if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) $sql .= " AND sp.statut <> 0"; - $sql .= " ORDER BY sp.lastname ASC"; - - dol_syslog(get_class($this)."::select_contacts", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - - if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) - { - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $out .= ajax_combobox($htmlid, $events, $conf->global->CONTACT_USE_SEARCH_TO_SELECT); - } - - if ($htmlname != 'none' && !$options_only) $out .= ''; - } - - $this->num = $num; - return $out; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return the HTML select list of users - * - * @param string $selected Id user preselected - * @param string $htmlname Field name in form - * @param int $show_empty 0=liste sans valeur nulle, 1=ajoute valeur inconnue - * @param array $exclude Array list of users id to exclude - * @param int $disabled If select list must be disabled - * @param array|string $include Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me - * @param int $enableonly Array list of users id to be enabled. All other must be disabled - * @param string $force_entity '0' or Ids of environment to force - * @return void - * @deprecated Use select_dolusers instead - * @see select_dolusers() - */ - public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0') - { - // phpcs:enable - print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return select list of users - * - * @param string $selected User id or user object of user preselected. If 0 or < -2, we use id of current user. If -1, keep unselected (if empty is allowed) - * @param string $htmlname Field name in form - * @param int $show_empty 0=list with no empty value, 1=add also an empty value into list - * @param array $exclude Array list of users id to exclude - * @param int $disabled If select list must be disabled - * @param array|string $include Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me - * @param array $enableonly Array list of users id to be enabled. If defined, it means that others will be disabled - * @param string $force_entity '0' or Ids of environment to force - * @param int $maxlength Maximum length of string into list (0=no limit) - * @param int $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status - * @param string $morefilter Add more filters into sql request (Example: 'employee = 1') - * @param integer $show_every 0=default list, 1=add also a value "Everybody" at beginning of list - * @param string $enableonlytext If option $enableonlytext is set, we use this text to explain into label why record is disabled. Not used if enableonly is empty. - * @param string $morecss More css - * @param int $noactive Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on). - * @param int $outputmode 0=HTML select string, 1=Array - * @param bool $multiple add [] in the name of element and add 'multiple' attribut - * @return string HTML select string - * @see select_dolgroups() - */ - public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $noactive = 0, $outputmode = 0, $multiple = false) - { - // phpcs:enable - global $conf, $user, $langs, $hookmanager; - - // If no preselected user defined, we take current user - if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) $selected = $user->id; - - if ($selected === '') $selected = array(); - elseif (!is_array($selected)) $selected = array($selected); - - $excludeUsers = null; - $includeUsers = null; - - // Permettre l'exclusion d'utilisateurs - if (is_array($exclude)) $excludeUsers = implode(",", $exclude); - // Permettre l'inclusion d'utilisateurs - if (is_array($include)) $includeUsers = implode(",", $include); - elseif ($include == 'hierarchy') - { - // Build list includeUsers to have only hierarchy - $includeUsers = implode(",", $user->getAllChildIds(0)); - } - elseif ($include == 'hierarchyme') - { - // Build list includeUsers to have only hierarchy and current user - $includeUsers = implode(",", $user->getAllChildIds(1)); - } - - $out = ''; - $outarray = array(); - - // Forge request to select users - $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut, u.login, u.admin, u.entity"; - if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) - { - $sql .= ", e.label"; - } - $sql .= " FROM ".MAIN_DB_PREFIX."user as u"; - if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) - { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid=u.entity"; - if ($force_entity) $sql .= " WHERE u.entity IN (0,".$force_entity.")"; - else $sql .= " WHERE u.entity IS NOT NULL"; - } - else - { - if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) - { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ug"; - $sql .= " ON ug.fk_user = u.rowid"; - $sql .= " WHERE ug.entity = ".$conf->entity; - } - else - { - $sql .= " WHERE u.entity IN (0,".$conf->entity.")"; - } - } - if (!empty($user->socid)) $sql .= " AND u.fk_soc = ".$user->socid; - if (is_array($exclude) && $excludeUsers) $sql .= " AND u.rowid NOT IN (".$excludeUsers.")"; - if ($includeUsers) $sql .= " AND u.rowid IN (".$includeUsers.")"; - if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $noactive) $sql .= " AND u.statut <> 0"; - if (!empty($morefilter)) $sql .= " ".$morefilter; - - //Add hook to filter on user (for exemple on usergroup define in custom modules) - $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action); - if (!empty($reshook)) $sql .= $hookmanager->resPrint; - - if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname - { - $sql .= " ORDER BY u.firstname ASC"; - } - else - { - $sql .= " ORDER BY u.lastname ASC"; - } - - dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - if ($num) - { - // Enhance with select2 - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $out .= ajax_combobox($htmlname); - - // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined - $out .= ''; - $out .= ''; - } - $out .= ''; - } - else - { - dol_print_error($this->db); - } - - if ($outputmode) return $outarray; - return $out; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return select list of users. Selected users are stored into session. - * List of users are provided into $_SESSION['assignedtouser']. - * - * @param string $action Value for $action - * @param string $htmlname Field name in form - * @param int $show_empty 0=list without the empty value, 1=add empty value - * @param array $exclude Array list of users id to exclude - * @param int $disabled If select list must be disabled - * @param array $include Array list of users id to include or 'hierarchy' to have only supervised users - * @param array $enableonly Array list of users id to be enabled. All other must be disabled - * @param int $force_entity '0' or Ids of environment to force - * @param int $maxlength Maximum length of string into list (0=no limit) - * @param int $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status - * @param string $morefilter Add more filters into sql request - * @param int $showproperties Show properties of each attendees - * @param array $listofuserid Array with properties of each user - * @param array $listofcontactid Array with properties of each contact - * @param array $listofotherid Array with properties of each other contact - * @return string HTML select string - * @see select_dolgroups() - */ - public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array()) - { - // phpcs:enable - global $conf, $user, $langs; - - $userstatic = new User($this->db); - $out = ''; - - // Method with no ajax - //$out.='
'; - if ($action == 'view') - { - $out .= ''; - } - else - { - $out .= ''; - $out .= ''; - $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter); - $out .= ' '; - $out .= '
'; - } - $assignedtouser = array(); - if (!empty($_SESSION['assignedtouser'])) - { - $assignedtouser = json_decode($_SESSION['assignedtouser'], true); - } - $nbassignetouser = count($assignedtouser); - - if ($nbassignetouser && $action != 'view') $out .= '
'; - if ($nbassignetouser) $out .= '
    '; - $i = 0; $ownerid = 0; - foreach ($assignedtouser as $key => $value) - { - if ($value['id'] == $ownerid) continue; - - $out .= '
  • '; - $userstatic->fetch($value['id']); - $out .= $userstatic->getNomUrl(-1); - if ($i == 0) { $ownerid = $value['id']; $out .= ' ('.$langs->trans("Owner").')'; } - if ($nbassignetouser > 1 && $action != 'view') - { - $out .= ' '; - } - // Show my availability - if ($showproperties) - { - if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) - { - $out .= '
    '; - $out .= ' - '.$langs->trans("Availability").': '.$langs->trans("Busy"); - $out .= '
    '; - } - } - //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional")); - //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy")); - - $out .= '
  • '; - $i++; - } - if ($nbassignetouser) $out .= '
'; - - //$out.='
'; - return $out; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of products for customer in Ajax if Ajax activated or go to select_produits_list - * - * @param int $selected Preselected products - * @param string $htmlname Name of HTML select field (must be unique in page) - * @param int $filtertype Filter on product type (''=nofilter, 0=product, 1=service) - * @param int $limit Limit on number of returned lines - * @param int $price_level Level of price to show - * @param int $status Sell status -1=Return all products, 0=Products not on sell, 1=Products on sell - * @param int $finished 2=all, 1=finished, 0=raw material - * @param string $selected_input_value Value of preselected input text (for use with ajax) - * @param int $hidelabel Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after) - * @param array $ajaxoptions Options for ajax_autocompleter - * @param int $socid Thirdparty Id (to get also price dedicated to this customer) - * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. - * @param int $forcecombo Force to use combo box - * @param string $morecss Add more css on select - * @param int $hidepriceinlabel 1=Hide prices in label - * @param string $warehouseStatus Warehouse status filter to count the quantity in stock. Following comma separated filter options can be used - * 'warehouseopen' = count products from open warehouses, - * 'warehouseclosed' = count products from closed warehouses, - * 'warehouseinternal' = count products from warehouses for internal correct/transfer only - * @param array $selected_combinations Selected combinations. Format: array([attrid] => attrval, [...]) - * @return void - */ - public function select_produits($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $status = 1, $finished = 2, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $selected_combinations = array()) - { - // phpcs:enable - global $langs, $conf; - - // check parameters - $price_level = (!empty($price_level) ? $price_level : 0); - if (is_null($ajaxoptions)) $ajaxoptions = array(); - - if (strval($filtertype) === '' && (!empty($conf->product->enabled) || !empty($conf->service->enabled))) { - if (!empty($conf->product->enabled) && empty($conf->service->enabled)) { - $filtertype = '0'; - } - elseif (empty($conf->product->enabled) && !empty($conf->service->enabled)) { - $filtertype = '1'; - } - } - - if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) - { - $placeholder = ''; - - if ($selected && empty($selected_input_value)) - { - require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; - $producttmpselect = new Product($this->db); - $producttmpselect->fetch($selected); - $selected_input_value = $producttmpselect->ref; - unset($producttmpselect); - } - // handle case where product or service module is disabled + no filter specified - if ($filtertype == '') - { - if (empty($conf->product->enabled)) { // when product module is disabled, show services only - $filtertype = 1; - } - elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only - $filtertype = 0; - } - } - // mode=1 means customers products - $urloption = 'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus; - //Price by customer - if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) { - $urloption .= '&socid='.$socid; - } - print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); - - if (!empty($conf->variants->enabled)) { - ?> - - trans("RefOrLabel").' : '; - elseif ($hidelabel > 1) { - $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"'; - if ($hidelabel == 2) { - print img_picto($langs->trans("Search"), 'search'); - } - } - print 'global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />'; - if ($hidelabel == 3) { - print img_picto($langs->trans("Search"), 'search'); - } - } - else - { - print $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus); - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of products for a customer - * - * @param int $selected Preselected product - * @param string $htmlname Name of select html - * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service) - * @param int $limit Limit on number of returned lines - * @param int $price_level Level of price to show - * @param string $filterkey Filter on product - * @param int $status -1=Return all products, 0=Products not on sell, 1=Products on sell - * @param int $finished Filter on finished field: 2=No filter - * @param int $outputmode 0=HTML select string, 1=Array - * @param int $socid Thirdparty Id (to get also price dedicated to this customer) - * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. - * @param int $forcecombo Force to use combo box - * @param string $morecss Add more css on select - * @param int $hidepriceinlabel 1=Hide prices in label - * @param string $warehouseStatus Warehouse status filter to group/count stock. Following comma separated filter options can be used. - * 'warehouseopen' = count products from open warehouses, - * 'warehouseclosed' = count products from closed warehouses, - * 'warehouseinternal' = count products from warehouses for internal correct/transfer only - * @return array Array of keys for json - */ - public function select_produits_list($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '') - { - // phpcs:enable - global $langs, $conf, $user, $db; - - $out = ''; - $outarray = array(); - - // Units - if ($conf->global->PRODUCT_USE_UNITS) { - $langs->load('other'); - } - - $warehouseStatusArray = array(); - if (!empty($warehouseStatus)) - { - require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; - if (preg_match('/warehouseclosed/', $warehouseStatus)) - { - $warehouseStatusArray[] = Entrepot::STATUS_CLOSED; - } - if (preg_match('/warehouseopen/', $warehouseStatus)) - { - $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL; - } - if (preg_match('/warehouseinternal/', $warehouseStatus)) - { - $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL; - } - } - - $selectFields = " p.rowid, p.ref, p.label, p.description, p.barcode, p.fk_country, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.duration, p.fk_price_expression"; - if (count($warehouseStatusArray)) - { - $selectFieldsGrouped = ", sum(".$db->ifsql("e.statut IS NULL", "0", "ps.reel").") as stock"; // e.statut is null if there is no record in stock - } - else - { - $selectFieldsGrouped = ", ".$db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock"; - } - - $sql = "SELECT "; - $sql .= $selectFields.$selectFieldsGrouped; - - if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) - { - //Product category - $sql .= ", (SELECT ".MAIN_DB_PREFIX."categorie_product.fk_categorie - FROM ".MAIN_DB_PREFIX."categorie_product - WHERE ".MAIN_DB_PREFIX."categorie_product.fk_product=p.rowid - LIMIT 1 - ) AS categorie_product_id "; - } - - //Price by customer - 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"; - } - // Units - if (!empty($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"; - $selectFields .= ', unit_long, 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'; - } - - // Multilang : we add translation - if (!empty($conf->global->MAIN_MULTILANGS)) - { - $sql .= ", pl.label as label_translated"; - $selectFields .= ", label_translated"; - } - // Price by quantity - if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) - { - $sql .= ", (SELECT pp.rowid FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid"; - if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) $sql .= " AND price_level=".$price_level; - $sql .= " ORDER BY date_price"; - $sql .= " DESC LIMIT 1) as price_rowid"; - $sql .= ", (SELECT pp.price_by_qty FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid"; // price_by_qty is 1 if some prices by qty exists in subtable - if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) $sql .= " AND price_level=".$price_level; - $sql .= " ORDER BY date_price"; - $sql .= " DESC LIMIT 1) as price_by_qty"; - $selectFields .= ", price_rowid, price_by_qty"; - } - $sql .= " FROM ".MAIN_DB_PREFIX."product as p"; - if (count($warehouseStatusArray)) - { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_product = p.rowid"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")"; - $sql .= ' AND e.statut IN ('.$this->db->escape(implode(',', $warehouseStatusArray)).')'; // Return line if product is inside the selected stock. If not, an empty line will be returned so we will count 0. - } - - // include search in supplier ref - if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) - { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product"; - } - - //Price by customer - if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_customer_price as pcp ON pcp.fk_soc=".$socid." AND pcp.fk_product=p.rowid"; - } - // Units - if (!empty($conf->global->PRODUCT_USE_UNITS)) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit"; - } - // 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='".$langs->getDefaultLang()."'"; - } - - if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac.fk_product_child = p.rowid"; - } - - $sql .= ' WHERE p.entity IN ('.getEntity('product').')'; - - if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) { - $sql .= " AND pac.rowid IS NULL"; - } - - if ($finished == 0) - { - $sql .= " AND p.finished = ".$finished; - } - elseif ($finished == 1) - { - $sql .= " AND p.finished = ".$finished; - if ($status >= 0) $sql .= " AND p.tosell = ".$status; - } - elseif ($status >= 0) - { - $sql .= " AND p.tosell = ".$status; - } - // Filter by product type - if (strval($filtertype) != '') $sql .= " AND p.fk_product_type = ".$filtertype; - elseif (empty($conf->product->enabled)) { // when product module is disabled, show services only - $sql .= " AND p.fk_product_type = 1"; - } - elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only - $sql .= " AND p.fk_product_type = 0"; - } - // Add criteria on ref/label - if ($filterkey != '') - { - $sql .= ' AND ('; - $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on - // For natural search - $scrit = explode(' ', $filterkey); - $i = 0; - if (count($scrit) > 1) $sql .= "("; - foreach ($scrit as $crit) - { - if ($i > 0) $sql .= " AND "; - $sql .= "(p.ref LIKE '".$db->escape($prefix.$crit)."%' OR p.label LIKE '".$db->escape($prefix.$crit)."%'"; - if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " OR pl.label LIKE '".$db->escape($prefix.$crit)."%'"; - if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) - { - $sql .= " OR p.description LIKE '".$db->escape($prefix.$crit)."%'"; - if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " OR pl.description LIKE '".$db->escape($prefix.$crit)."%'"; - } - if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) $sql .= " OR pfp.ref_fourn LIKE '".$db->escape($prefix.$crit)."%'"; - $sql .= ")"; - $i++; - } - if (count($scrit) > 1) $sql .= ")"; - if (!empty($conf->barcode->enabled)) $sql .= " OR p.barcode LIKE '".$db->escape($prefix.$filterkey)."%'"; - $sql .= ')'; - } - if (count($warehouseStatusArray)) - { - $sql .= ' GROUP BY'.$selectFields; - } - - //Sort by category - if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) - { - $sql .= " ORDER BY categorie_product_id "; - //ASC OR DESC order - ($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC"; - } - else - { - $sql .= $db->order("p.ref"); - } - - $sql .= $db->plimit($limit, 0); - - // Build output string - dol_syslog(get_class($this)."::select_produits_list search product", LOG_DEBUG); - $result = $this->db->query($sql); - if ($result) - { - require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; - require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; - require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; - - $num = $this->db->num_rows($result); - - $events = null; - - if (!$forcecombo) - { - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $out .= ajax_combobox($htmlname, $events, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT); - } - - $out .= ''; - - $this->db->free($result); - - if (empty($outputmode)) return $out; - return $outarray; - } - else - { - dol_print_error($db); - } - } - - /** - * constructProductListOption. - * This define value for &$opt and &$optJson. - * - * @param resource $objp Resultset of fetch - * @param string $opt Option (var used for returned value in string option format) - * @param string $optJson Option (var used for returned value in json format) - * @param int $price_level Price level - * @param string $selected Preselected value - * @param int $hidepriceinlabel Hide price in label - * @param string $filterkey Filter key to highlight - * @param int $novirtualstock Do not load virtual stock, even if slow option STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO is on. - * @return void - */ - protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0) - { - global $langs, $conf, $user, $db; - - $outkey = ''; - $outval = ''; - $outref = ''; - $outlabel = ''; - $outdesc = ''; - $outbarcode = ''; - $outorigin = ''; - $outtype = ''; - $outprice_ht = ''; - $outprice_ttc = ''; - $outpricebasetype = ''; - $outtva_tx = ''; - $outqty = 1; - $outdiscount = 0; - - $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO); - - $label = $objp->label; - if (!empty($objp->label_translated)) $label = $objp->label_translated; - if (!empty($filterkey) && $filterkey != '') $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $label, 1); - - $outkey = $objp->rowid; - $outref = $objp->ref; - $outlabel = $objp->label; - $outdesc = $objp->description; - $outbarcode = $objp->barcode; - $outorigin = $objp->fk_country; - $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid; - - $outtype = $objp->fk_product_type; - $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : ''; - $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : ''; - - // Units - $outvalUnits = ''; - if (!empty($conf->global->PRODUCT_USE_UNITS)) { - if (!empty($objp->unit_short)) { - $outvalUnits .= ' - '.$objp->unit_short; - } - } - if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) { - if (!empty($objp->weight) && $objp->weight_units !== null) { - $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs); - $outvalUnits .= ' - '.$unitToShow; - } - if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) { - $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units); - $outvalUnits .= ' - '.$unitToShow; - } - if (!empty($objp->surface) && $objp->surface_units !== null) { - $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs); - $outvalUnits .= ' - '.$unitToShow; - } - if (!empty($objp->volume) && $objp->volume_units !== null) { - $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs); - $outvalUnits .= ' - '.$unitToShow; - } - } - if ($outdurationvalue && $outdurationunit) { - $da = array( - 'h' => $langs->trans('Hour'), - 'd' => $langs->trans('Day'), - 'w' => $langs->trans('Week'), - 'm' => $langs->trans('Month'), - 'y' => $langs->trans('Year') - ); - if (isset($da[$outdurationunit])) { - $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : '')); - } - } - - $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); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs_list) - * - * @param int $socid Id third party - * @param string $selected Preselected product - * @param string $htmlname Name of HTML Select - * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service) - * @param string $filtre For a SQL filter - * @param array $ajaxoptions Options for ajax_autocompleter - * @param int $hidelabel Hide label (0=no, 1=yes) - * @param int $alsoproductwithnosupplierprice 1=Add also product without supplier prices - * @param string $morecss More CSS - * @return void - */ - public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '') - { - // phpcs:enable - global $langs, $conf; - global $price_level, $status, $finished; - - $selected_input_value = ''; - if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) - { - if ($selected > 0) - { - require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; - $producttmpselect = new Product($this->db); - $producttmpselect->fetch($selected); - $selected_input_value = $producttmpselect->ref; - unset($producttmpselect); - } - - // mode=2 means suppliers products - $urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice; - print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); - print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').''; - } - else - { - print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', -1, 0, 0, $alsoproductwithnosupplierprice, $morecss); - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of suppliers products - * - * @param int $socid Id societe fournisseur (0 pour aucun filtre) - * @param int $selected Product price pre-selected (must be 'id' in product_fournisseur_price or 'idprod_IDPROD') - * @param string $htmlname Nom de la zone select - * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service) - * @param string $filtre Pour filtre sql - * @param string $filterkey Filtre des produits - * @param int $statut -1=Return all products, 0=Products not on sell, 1=Products on sell (not used here, a filter on tobuy is already hard coded in request) - * @param int $outputmode 0=HTML select string, 1=Array - * @param int $limit Limit of line number - * @param int $alsoproductwithnosupplierprice 1=Add also product without supplier prices - * @param string $morecss Add more CSS - * @return array Array of keys for json - */ - public function select_produits_fournisseurs_list($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $filterkey = '', $statut = -1, $outputmode = 0, $limit = 100, $alsoproductwithnosupplierprice = 0, $morecss = '') - { - // phpcs:enable - global $langs, $conf, $db; - - $out = ''; - $outarray = array(); - - $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO); - - $langs->load('stocks'); - // Units - if ($conf->global->PRODUCT_USE_UNITS) { - $langs->load('other'); - } - - $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type,"; - $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"; - // 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"; - } - if (!empty($conf->barcode->enabled)) $sql .= ", pfp.barcode"; - $sql .= " FROM ".MAIN_DB_PREFIX."product as p"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )"; - if ($socid) $sql .= " AND pfp.fk_soc = ".$socid; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid"; - // Units - if ($conf->global->PRODUCT_USE_UNITS) { - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit"; - } - $sql .= " WHERE p.entity IN (".getEntity('product').")"; - $sql .= " AND p.tobuy = 1"; - if (strval($filtertype) != '') $sql .= " AND p.fk_product_type=".$this->db->escape($filtertype); - if (!empty($filtre)) $sql .= " ".$filtre; - // Add criteria on ref/label - if ($filterkey != '') - { - $sql .= ' AND ('; - $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on - // For natural search - $scrit = explode(' ', $filterkey); - $i = 0; - if (count($scrit) > 1) $sql .= "("; - 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)."%')"; - $i++; - } - if (count($scrit) > 1) $sql .= ")"; - if (!empty($conf->barcode->enabled)) { - $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'"; - $sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'"; - } - $sql .= ')'; - } - $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC"; - $sql .= $db->plimit($limit, 0); - - // Build output string - - dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG); - $result = $this->db->query($sql); - if ($result) - { - require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; - require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; - - $num = $this->db->num_rows($result); - - //$out.=''; - if (!$selected) $out .= ''; - else $out .= ''; - - $i = 0; - while ($i < $num) - { - $objp = $this->db->fetch_object($result); - - $outkey = $objp->idprodfournprice; // id in table of price - if (!$outkey && $alsoproductwithnosupplierprice) $outkey = 'idprod_'.$objp->rowid; // id of product - - $outref = $objp->ref; - $outval = ''; - $outbarcode = $objp->barcode; - $outqty = 1; - $outdiscount = 0; - $outtype = $objp->fk_product_type; - $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : ''; - $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : ''; - - // Units - $outvalUnits = ''; - if ($conf->global->PRODUCT_USE_UNITS) { - if (!empty($objp->unit_short)) { - $outvalUnits .= ' - '.$objp->unit_short; - } - if (!empty($objp->weight) && $objp->weight_units !== null) { - $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs); - $outvalUnits .= ' - '.$unitToShow; - } - if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) { - $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units); - $outvalUnits .= ' - '.$unitToShow; - } - if (!empty($objp->surface) && $objp->surface_units !== null) { - $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs); - $outvalUnits .= ' - '.$unitToShow; - } - if (!empty($objp->volume) && $objp->volume_units !== null) { - $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs); - $outvalUnits .= ' - '.$unitToShow; - } - if ($outdurationvalue && $outdurationunit) { - $da = array( - 'h' => $langs->trans('Hour'), - 'd' => $langs->trans('Day'), - 'w' => $langs->trans('Week'), - 'm' => $langs->trans('Month'), - 'y' => $langs->trans('Year') - ); - if (isset($da[$outdurationunit])) { - $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : '')); - } - } - } - - $objRef = $objp->ref; - if ($filterkey && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $objRef, 1); - $objRefFourn = $objp->ref_fourn; - if ($filterkey && $filterkey != '') $objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $objRefFourn, 1); - $label = $objp->label; - if ($filterkey && $filterkey != '') $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $label, 1); - - $optlabel = $objp->ref; - if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) { - $optlabel .= ' ('.$objp->ref_fourn.')'; - } - if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) { - $optlabel .= ' ('.$outbarcode.')'; - } - $optlabel .= ' - '.dol_trunc($label, $maxlengtharticle); - - $outvallabel = $objRef; - if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) { - $outvallabel .= ' ('.$objRefFourn.')'; - } - if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) { - $outvallabel .= ' ('.$outbarcode.')'; - } - $outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle); - - // Units - $optlabel .= $outvalUnits; - $outvallabel .= $outvalUnits; - - if (!empty($objp->idprodfournprice)) - { - $outqty = $objp->quantity; - $outdiscount = $objp->remise_percent; - if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) { - $prod_supplier = new ProductFournisseur($this->db); - $prod_supplier->product_fourn_price_id = $objp->idprodfournprice; - $prod_supplier->id = $objp->fk_product; - $prod_supplier->fourn_qty = $objp->quantity; - $prod_supplier->fourn_tva_tx = $objp->tva_tx; - $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression; - $priceparser = new PriceParser($this->db); - $price_result = $priceparser->parseProductSupplier($prod_supplier); - if ($price_result >= 0) { - $objp->fprice = $price_result; - if ($objp->quantity >= 1) - { - $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice - } - } - } - if ($objp->quantity == 1) - { - $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/"; - $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/"; - $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding - $outvallabel .= $langs->transnoentities("Unit"); - } - else - { - $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity; - $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity; - $optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding - $outvallabel .= ' '.$langs->transnoentities("Units"); - } - - if ($objp->quantity > 1) - { - $optlabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding - $outvallabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding - } - if ($objp->remise_percent >= 1) - { - $optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %'; - $outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %'; - } - if ($objp->duration) - { - $optlabel .= " - ".$objp->duration; - $outvallabel .= " - ".$objp->duration; - } - if (!$socid) - { - $optlabel .= " - ".dol_trunc($objp->name, 8); - $outvallabel .= " - ".dol_trunc($objp->name, 8); - } - if ($objp->supplier_reputation) - { - //TODO dictionary - $reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier')); - - $optlabel .= " - ".$reputations[$objp->supplier_reputation]; - $outvallabel .= " - ".$reputations[$objp->supplier_reputation]; - } - } - else - { - if (empty($alsoproductwithnosupplierprice)) // No supplier price defined for couple product/supplier - { - $optlabel .= " - ".$langs->trans("NoPriceDefinedForThisSupplier").''; - $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier"); - } - else // No supplier price defined for product, even on other suppliers - { - $optlabel .= " - ".$langs->trans("NoPriceDefinedForThisSupplier").''; - $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier"); - } - } - - $opt = '\n"; - - - // Add new entry - // "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))); - // 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é)" - // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false) - //} - //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval)); - //$outval=array('label'=>'ppp (fff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)'); - //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval)); - - $i++; - } - $out .= ''; - - $this->db->free($result); - - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $out .= ajax_combobox($htmlname); - - if (empty($outputmode)) return $out; - return $outarray; - } - else - { - dol_print_error($this->db); - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of suppliers prices for a product - * - * @param int $productid Id of product - * @param string $htmlname Name of HTML field - * @param int $selected_supplier Pre-selected supplier if more than 1 result - * @return string - */ - public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '') - { - // phpcs:enable - global $langs, $conf; - - $langs->load('stocks'); - - $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,"; - $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,"; - $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name"; - $sql .= " FROM ".MAIN_DB_PREFIX."product as p"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid"; - $sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")"; - $sql .= " AND p.tobuy = 1"; - $sql .= " AND s.fournisseur = 1"; - $sql .= " AND p.rowid = ".$productid; - $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC"; - - dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG); - $result = $this->db->query($sql); - - if ($result) - { - $num = $this->db->num_rows($result); - - $form = ''; - $this->db->free($result); - return $form; - } - else - { - dol_print_error($this->db); - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of delivery address - * - * @param string $selected Id contact pre-selectionn - * @param int $socid Id of company - * @param string $htmlname Name of HTML field - * @param int $showempty Add an empty field - * @return integer|null - */ - public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0) - { - // phpcs:enable - // looking for users - $sql = "SELECT a.rowid, a.label"; - $sql .= " FROM ".MAIN_DB_PREFIX."societe_address as a"; - $sql .= " WHERE a.fk_soc = ".$socid; - $sql .= " ORDER BY a.label ASC"; - - dol_syslog(get_class($this)."::select_address", LOG_DEBUG); - $resql = $this->db->query($sql); - if ($resql) - { - print ''; - return $num; - } - else - { - dol_print_error($this->db); - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Load into cache list of payment terms - * - * @return int Nb of lines loaded, <0 if KO - */ - public function load_cache_conditions_paiements() - { - // phpcs:enable - global $langs; - - $num = count($this->cache_conditions_paiements); - if ($num > 0) return 0; // Cache already loaded - - dol_syslog(__METHOD__, LOG_DEBUG); - - $sql = "SELECT rowid, code, libelle as label"; - $sql .= " FROM ".MAIN_DB_PREFIX.'c_payment_term'; - $sql .= " WHERE entity IN (".getEntity('c_payment_term').")"; - $sql .= " AND active > 0"; - $sql .= " ORDER BY sortorder"; - - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - while ($i < $num) - { - $obj = $this->db->fetch_object($resql); - - // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut - $label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : '')); - $this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code; - $this->cache_conditions_paiements[$obj->rowid]['label'] = $label; - $i++; - } - - //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table - - return $num; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Charge dans cache la liste des délais de livraison possibles - * - * @return int Nb of lines loaded, <0 if KO - */ - public function load_cache_availability() - { - // phpcs:enable - global $langs; - - $num = count($this->cache_availability); - if ($num > 0) return 0; // Cache already loaded - - dol_syslog(__METHOD__, LOG_DEBUG); - - $langs->load('propal'); - - $sql = "SELECT rowid, code, label"; - $sql .= " FROM ".MAIN_DB_PREFIX.'c_availability'; - $sql .= " WHERE active > 0"; - - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - while ($i < $num) - { - $obj = $this->db->fetch_object($resql); - - // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut - $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; - $i++; - } - - $this->cache_availability = dol_sort_array($this->cache_availability, 'label', 'asc', 0, 0, 1); - - return $num; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - /** - * Retourne la liste des types de delais de livraison possibles - * - * @param int $selected Id du type de delais pre-selectionne - * @param string $htmlname Nom de la zone select - * @param string $filtertype To add a filter - * @param int $addempty Add empty entry - * @return void - */ - public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0) - { - global $langs, $user; - - $this->load_cache_availability(); - - dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG); - - print ''; - if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); - } - - /** - * Load into cache cache_demand_reason, array of input reasons - * - * @return int Nb of lines loaded, <0 if KO - */ - public function loadCacheInputReason() - { - global $langs; - - $num = count($this->cache_demand_reason); - if ($num > 0) return 0; // Cache already loaded - - $sql = "SELECT rowid, code, label"; - $sql .= " FROM ".MAIN_DB_PREFIX.'c_input_reason'; - $sql .= " WHERE active > 0"; - - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - $tmparray = array(); - while ($i < $num) - { - $obj = $this->db->fetch_object($resql); - - // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut - $label = ($obj->label != '-' ? $obj->label : ''); - if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) $label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work - if ($langs->trans($obj->code) != $obj->code) $label = $langs->trans($obj->code); // So translation key SRC_XXX will work - - $tmparray[$obj->rowid]['id'] = $obj->rowid; - $tmparray[$obj->rowid]['code'] = $obj->code; - $tmparray[$obj->rowid]['label'] = $label; - $i++; - } - - $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1); - - unset($tmparray); - return $num; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - /** - * Return list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...) - * List found into table c_input_reason loaded by loadCacheInputReason - * - * @param int $selected Id or code of type origin to select by default - * @param string $htmlname Nom de la zone select - * @param string $exclude To exclude a code value (Example: SRC_PROP) - * @param int $addempty Add an empty entry - * @return void - */ - public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0) - { - global $langs, $user; - - $this->loadCacheInputReason(); - - print ''; - if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Charge dans cache la liste des types de paiements possibles - * - * @return int Nb of lines loaded, <0 if KO - */ - public function load_cache_types_paiements() - { - // phpcs:enable - global $langs; - - $num = count($this->cache_types_paiements); - if ($num > 0) return $num; // Cache already loaded - - dol_syslog(__METHOD__, LOG_DEBUG); - - $this->cache_types_paiements = array(); - - $sql = "SELECT id, code, libelle as label, type, active"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_paiement"; - $sql .= " WHERE entity IN (".getEntity('c_paiement').")"; - //if ($active >= 0) $sql.= " AND active = ".$active; - - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - $i = 0; - while ($i < $num) - { - $obj = $this->db->fetch_object($resql); - - // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut - $label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : '')); - $this->cache_types_paiements[$obj->id]['id'] = $obj->id; - $this->cache_types_paiements[$obj->id]['code'] = $obj->code; - $this->cache_types_paiements[$obj->id]['label'] = $label; - $this->cache_types_paiements[$obj->id]['type'] = $obj->type; - $this->cache_types_paiements[$obj->id]['active'] = $obj->active; - $i++; - } - - $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1); - - return $num; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of payment modes. - * Constant MAIN_DEFAULT_PAYMENT_TERM_ID can used to set default value but scope is all application, probably not what you want. - * See instead to force the default value by the caller. - * - * @param int $selected Id of payment term to preselect by default - * @param string $htmlname Nom de la zone select - * @param int $filtertype Not used - * @param int $addempty Add an empty entry - * @param int $noinfoadmin 0=Add admin info, 1=Disable admin info - * @param string $morecss Add more CSS on select tag - * @return void - */ - public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '') - { - // phpcs:enable - global $langs, $user, $conf; - - dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG); - - $this->load_cache_conditions_paiements(); - - // Set default value if not already set by caller - if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID; - - print ''; - if ($user->admin && empty($noinfoadmin)) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of payment methods - * Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value but scope is all application, probably not what you want. - * - * @param string $selected Id or code or preselected payment mode - * @param string $htmlname Name of select field - * @param string $filtertype To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz)) - * @param int $format 0=id+label, 1=code+code, 2=code+label, 3=id+code - * @param int $empty 1=can be empty, 0 otherwise - * @param int $noadmininfo 0=Add admin info, 1=Disable admin info - * @param int $maxlength Max length of label - * @param int $active Active or not, -1 = all - * @param string $morecss Add more CSS on select tag - * @return void - */ - public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '') - { - // phpcs:enable - global $langs, $user, $conf; - - dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG); - - $filterarray = array(); - if ($filtertype == 'CRDT') $filterarray = array(0, 2, 3); - elseif ($filtertype == 'DBIT') $filterarray = array(1, 2, 3); - elseif ($filtertype != '' && $filtertype != '-1') $filterarray = explode(',', $filtertype); - - $this->load_cache_types_paiements(); - - // Set default value if not already set by caller - if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID; - - print ''; - if ($user->admin && !$noadmininfo) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); - } - - - /** - * Selection HT or TTC - * - * @param string $selected Id pre-selectionne - * @param string $htmlname Nom de la zone select - * @return string Code of HTML select to chose tax or not - */ - public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type') - { - global $langs; - - $return = ''; - - $return .= ''; - - return $return; - } - - /** - * Return a HTML select list of shipping mode - * - * @param string $selected Id shipping mode pre-selected - * @param string $htmlname Name of select zone - * @param string $filtre To filter list - * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. - * @param string $moreattrib To add more attribute on select - * @return void - */ - public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '') - { - global $langs, $conf, $user; - - $langs->load("admin"); - $langs->load("deliveries"); - - $sql = "SELECT rowid, code, libelle as label"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode"; - $sql .= " WHERE active > 0"; - if ($filtre) $sql .= " AND ".$filtre; - $sql .= " ORDER BY libelle ASC"; - - dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG); - $result = $this->db->query($sql); - if ($result) { - $num = $this->db->num_rows($result); - $i = 0; - if ($num) { - print '"; - if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); - } else { - print $langs->trans("NoShippingMethodDefined"); - } - } else { - dol_print_error($this->db); - } - } - - /** - * Display form to select shipping mode - * - * @param string $page Page - * @param int $selected Id of shipping mode - * @param string $htmlname Name of select html field - * @param int $addempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. - * @return void - */ - public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0) - { - global $langs, $db; - - $langs->load("deliveries"); - - if ($htmlname != "none") { - print '
'; - print ''; - print ''; - $this->selectShippingMethod($selected, $htmlname, '', $addempty); - print ''; - print '
'; - } else { - if ($selected) { - $code = $langs->getLabelFromKey($db, $selected, 'c_shipment_mode', 'rowid', 'code'); - print $langs->trans("SendingMethod".strtoupper($code)); - } else { - print " "; - } - } - } - - /** - * Creates HTML last in cycle situation invoices selector - * - * @param string $selected Preselected ID - * @param int $socid Company ID - * - * @return string HTML select - */ - public function selectSituationInvoices($selected = '', $socid = 0) - { - global $langs; - - $langs->load('bills'); - - $opt = ''; - $sql = 'SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'facture'; - $sql .= ' WHERE entity IN ('.getEntity('invoice').')'; - $sql .= ' AND situation_counter>=1'; - $sql .= ' ORDER by situation_cycle_ref, situation_counter desc'; - $resql = $this->db->query($sql); - if ($resql && $this->db->num_rows($resql) > 0) { - // Last seen cycle - $ref = 0; - while ($obj = $this->db->fetch_object($resql)) { - //Same company ? - if ($socid == $obj->fk_soc) { - //Same cycle ? - if ($obj->situation_cycle_ref != $ref) { - // Just seen this cycle - $ref = $obj->situation_cycle_ref; - //not final ? - if ($obj->situation_final != 1) { - //Not prov? - if (substr($obj->ref, 1, 4) != 'PROV') { - if ($selected == $obj->rowid) { - $opt .= ''; - } else { - $opt .= ''; - } - } - } - } - } - } - } - else - { - dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR); - } - if ($opt == '') - { - $opt = ''; - } - return $opt; - } - - /** - * Creates HTML units selector (code => label) - * - * @param string $selected Preselected Unit ID - * @param string $htmlname Select name - * @param int $showempty Add a nempty line - * @return string HTML select - */ - public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0) - { - global $langs; - - $langs->load('products'); - - $return = ''; - } - return $return; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return a HTML select list of bank accounts - * - * @param string $selected Id account pre-selected - * @param string $htmlname Name of select zone - * @param int $status Status of searched accounts (0=open, 1=closed, 2=both) - * @param string $filtre To filter list - * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. - * @param string $moreattrib To add more attribute on select - * @param int $showcurrency Show currency in label - * @param string $morecss More CSS - * @return int <0 if error, Num of bank account found if OK (0, 1, 2, ...) - */ - public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '') - { - // phpcs:enable - global $langs, $conf; - - $langs->load("admin"); - $num = 0; - - $sql = "SELECT rowid, label, bank, clos as status, currency_code"; - $sql .= " FROM ".MAIN_DB_PREFIX."bank_account"; - $sql .= " WHERE entity IN (".getEntity('bank_account').")"; - if ($status != 2) $sql .= " AND clos = ".(int) $status; - if ($filtre) $sql .= " AND ".$filtre; - $sql .= " ORDER BY label"; - - dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG); - $result = $this->db->query($sql); - if ($result) - { - $num = $this->db->num_rows($result); - $i = 0; - if ($num) - { - print '"; - } - else - { - if ($status == 0) print ''.$langs->trans("NoActiveBankAccountDefined").''; - else print ''.$langs->trans("NoBankAccountFound").''; - } - } - else { - dol_print_error($this->db); - } - - return $num; - } - - /** - * Return a HTML select list of establishment - * - * @param string $selected Id establishment pre-selected - * @param string $htmlname Name of select zone - * @param int $status Status of searched establishment (0=open, 1=closed, 2=both) - * @param string $filtre To filter list - * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. - * @param string $moreattrib To add more attribute on select - * @return int <0 if error, Num of establishment found if OK (0, 1, 2, ...) - */ - public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '') - { - // phpcs:enable - global $langs, $conf; - - $langs->load("admin"); - $num = 0; - - $sql = "SELECT rowid, name, fk_country, status, entity"; - $sql .= " FROM ".MAIN_DB_PREFIX."establishment"; - $sql .= " WHERE 1=1"; - if ($status != 2) $sql .= " AND status = ".(int) $status; - if ($filtre) $sql .= " AND ".$filtre; - $sql .= " ORDER BY name"; - - dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG); - $result = $this->db->query($sql); - if ($result) - { - $num = $this->db->num_rows($result); - $i = 0; - if ($num) - { - print '"; - } - else - { - if ($status == 0) print ''.$langs->trans("NoActiveEstablishmentDefined").''; - else print ''.$langs->trans("NoEstablishmentFound").''; - } - } - else { - dol_print_error($this->db); - } - } - - /** - * Display form to select bank account - * - * @param string $page Page - * @param int $selected Id of bank account - * @param string $htmlname Name of select html field - * @param int $addempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. - * @return void - */ - public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0) - { - global $langs; - if ($htmlname != "none") { - print '
'; - print ''; - print ''; - $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty); - if ($nbaccountfound > 0) print ''; - print '
'; - } else { - $langs->load('banks'); - - if ($selected) { - require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; - $bankstatic = new Account($this->db); - $result = $bankstatic->fetch($selected); - if ($result) print $bankstatic->getNomUrl(1); - } else { - print " "; - } - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Return list of categories having choosed type - * - * @param string|int $type Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated. - * @param string $selected Id of category preselected or 'auto' (autoselect category if there is only one element) - * @param string $htmlname HTML field name - * @param int $maxlength Maximum length for labels - * @param int|string|array $markafterid Keep only or removed all categories including the leaf $markafterid in category tree (exclude) or Keep only of category is inside the leaf starting with this id. - * $markafterid can be an : - * - int (id of category) - * - string (categories ids seprated by comma) - * - array (list of categories ids) - * @param int $outputmode 0=HTML select string, 1=Array - * @param int $include [=0] Removed or 1=Keep only - * @param string $morecss More CSS - * @return string - * @see select_categories() - */ - public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '') - { - // phpcs:enable - global $conf, $langs; - $langs->load("categories"); - - include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; - - // For backward compatibility - if (is_numeric($type)) - { - dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING); - } - - if ($type === Categorie::TYPE_BANK_LINE) - { - // TODO Move this into common category feature - $cate_arbo = array(); - $sql = "SELECT c.label, c.rowid"; - $sql .= " FROM ".MAIN_DB_PREFIX."bank_categ as c"; - $sql .= " WHERE entity = ".$conf->entity; - $sql .= " ORDER BY c.label"; - $result = $this->db->query($sql); - if ($result) - { - $num = $this->db->num_rows($result); - $i = 0; - while ($i < $num) - { - $objp = $this->db->fetch_object($result); - if ($objp) $cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label); - $i++; - } - $this->db->free($result); - } - else dol_print_error($this->db); - } - else - { - $cat = new Categorie($this->db); - $cate_arbo = $cat->get_full_arbo($type, $markafterid, $include); - } - - $output = ''; - $output .= "\n"; - - if ($outputmode) return $outarray; - return $output; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show a confirmation HTML form or AJAX popup - * - * @param string $page Url of page to call if confirmation is OK - * @param string $title Title - * @param string $question Question - * @param string $action Action - * @param array $formquestion An array with forms complementary inputs - * @param string $selectedchoice "" or "no" or "yes" - * @param int $useajax 0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=preoutput confirm box with div id=dialog-confirm-xxx - * @param int $height Force height of box - * @param int $width Force width of box - * @return void - * @deprecated - * @see formconfirm() - */ - public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500) - { - // phpcs:enable - dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING); - print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width); - } - - /** - * Show a confirmation HTML form or AJAX popup. - * Easiest way to use this is with useajax=1. - * If you use useajax='xxx', you must also add jquery code to trigger opening of box (with correct parameters) - * just after calling this method. For example: - * print ''."\n"; - * - * @param string $page Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated) - * @param string $title Title - * @param string $question Question - * @param string $action Action - * @param array $formquestion An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , )) - * type can be 'hidden', 'text', 'password', 'checkbox', 'radio', 'date', 'morecss', ... - * @param string $selectedchoice '' or 'no', or 'yes' or '1' or '0' - * @param int|string $useajax 0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=Yes and preoutput confirm box with div id=dialog-confirm-xxx - * @param int $height Force height of box (0 = auto) - * @param int $width Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones. - * @param int $disableformtag 1=Disable form tag. Can be used if we are already inside a
section. - * @return string HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form - */ - public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0) - { - global $langs, $conf; - global $useglobalvars; - - $more = ''; - $formconfirm = ''; - $inputok = array(); - $inputko = array(); - - // Clean parameters - $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice; - if ($conf->browser->layout == 'phone') $width = '95%'; - - // Set height automatically if not defined - if (empty($height)) { - $height = 210; - if (is_array($formquestion) && count($formquestion) > 2) { - $height += ((count($formquestion) - 2) * 24); - } - } - - if (is_array($formquestion) && !empty($formquestion)) - { - // First add hidden fields and value - foreach ($formquestion as $key => $input) - { - if (is_array($input) && !empty($input)) - { - if ($input['type'] == 'hidden') - { - $more .= ''."\n"; - } - } - } - - // Now add questions - $moreonecolumn = ''; - $more .= '
'."\n"; - foreach ($formquestion as $key => $input) - { - if (is_array($input) && !empty($input)) - { - $size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : ''); - $moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : ''); - $morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : ''); - - if ($input['type'] == 'text') - { - $more .= '
'.$input['label'].'
'."\n"; - } - elseif ($input['type'] == 'password') - { - $more .= '
'.$input['label'].'
'."\n"; - } - elseif ($input['type'] == 'select') - { - $more .= '
'; - if (!empty($input['label'])) $more .= $input['label'].'
'; - $more .= $this->selectarray($input['name'], $input['values'], $input['default'], 1, 0, 0, $moreattr, 0, 0, 0, '', $morecss); - $more .= '
'."\n"; - } - elseif ($input['type'] == 'checkbox') - { - $more .= '
'; - $more .= '
'.$input['label'].'
'; - $more .= ' $selval) - { - $more .= '
'; - if ($i == 0) $more .= '
'.$input['label'].'
'; - else $more .= '
 
'; - $more .= '
'.$input['label'].'
'; - $more .= '
'; - $more .= $this->selectDate($input['value'], $input['name'], 0, 0, 0, '', 1, 0); - $more .= '
'."\n"; - $formquestion[] = array('name'=>$input['name'].'day'); - $formquestion[] = array('name'=>$input['name'].'month'); - $formquestion[] = array('name'=>$input['name'].'year'); - $formquestion[] = array('name'=>$input['name'].'hour'); - $formquestion[] = array('name'=>$input['name'].'min'); - } - elseif ($input['type'] == 'other') - { - $more .= '
'; - if (!empty($input['label'])) $more .= $input['label'].'
'; - $more .= $input['value']; - $more .= '
'."\n"; - } - - elseif ($input['type'] == 'onecolumn') - { - $moreonecolumn .= '
'; - $moreonecolumn .= $input['value']; - $moreonecolumn .= '
'."\n"; - } - } - } - $more .= '
'."\n"; - $more .= $moreonecolumn; - } - - // JQUI method dialog is broken with jmobile, we use standard HTML. - // Note: When using dol_use_jmobile or no js, you must also check code for button use a GET url with action=xxx and check that you also output the confirm code when action=xxx - // See page product/card.php for example - if (!empty($conf->dol_use_jmobile)) $useajax = 0; - if (empty($conf->use_javascript_ajax)) $useajax = 0; - - if ($useajax) - { - $autoOpen = true; - $dialogconfirm = 'dialog-confirm'; - $button = ''; - if (!is_numeric($useajax)) - { - $button = $useajax; - $useajax = 1; - $autoOpen = false; - $dialogconfirm .= '-'.$button; - } - $pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.$action.'&confirm=yes'; - $pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'confirm=no' : ''); - // Add input fields into list of fields to read during submit (inputok and inputko) - if (is_array($formquestion)) - { - foreach ($formquestion as $key => $input) - { - //print "xx ".$key." rr ".is_array($input)."
\n"; - if (is_array($input) && isset($input['name'])) array_push($inputok, $input['name']); - if (isset($input['inputko']) && $input['inputko'] == 1) array_push($inputko, $input['name']); - } - } - // Show JQuery confirm box. Note that global var $useglobalvars is used inside this template - $formconfirm .= ''."\n"; - - $formconfirm .= "\n\n"; - $formconfirm .= ''; - $formconfirm .= "\n"; - } - else - { - $formconfirm .= "\n\n"; - - if (empty($disableformtag)) $formconfirm .= ''."\n"; - - $formconfirm .= ''."\n"; - $formconfirm .= ''."\n"; - - $formconfirm .= ''."\n"; - - // Line title - $formconfirm .= ''."\n"; - - // Line text - if (!empty($formquestion['text'])) { - $formconfirm .= ''."\n"; - } - - // Line form fields - if ($more) - { - $formconfirm .= ''."\n"; - } - - // Line with question - $formconfirm .= ''; - $formconfirm .= ''; - $formconfirm .= ''; - $formconfirm .= ''; - $formconfirm .= ''."\n"; - - $formconfirm .= '
'.img_picto('', 'recent').' '.$title.'
'.$formquestion['text'].'
'."\n"; - $formconfirm .= $more; - $formconfirm .= '
'.$question.''; - $formconfirm .= $this->selectyesno("confirm", $newselectedchoice); - $formconfirm .= '
'."\n"; - - if (empty($disableformtag)) $formconfirm .= "\n"; - $formconfirm .= '
'; - - $formconfirm .= "\n"; - } - - return $formconfirm; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show a form to select a project - * - * @param int $page Page - * @param int $socid Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id) - * @param int $selected Id pre-selected project - * @param string $htmlname Name of select field - * @param int $discard_closed Discard closed projects (0=Keep,1=hide completely except $selected,2=Disable) - * @param int $maxlength Max length - * @param int $forcefocus Force focus on field (works with javascript only) - * @param int $nooutput No print is done. String is returned. - * @return string Return html content - */ - public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0) - { - // phpcs:enable - global $langs; - - require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php'; - require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php'; - - $out = ''; - - $formproject = new FormProjets($this->db); - - $langs->load("project"); - if ($htmlname != "none") - { - $out .= "\n"; - $out .= '
'; - $out .= ''; - $out .= ''; - $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1); - $out .= ''; - $out .= '
'; - } - else - { - if ($selected) - { - $projet = new Project($this->db); - $projet->fetch($selected); - //print ''.$projet->title.''; - $out .= $projet->getNomUrl(0, '', 1); - } - else - { - $out .= " "; - } - } - - if (empty($nooutput)) - { - print $out; - return ''; - } - return $out; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show a form to select payment conditions - * - * @param int $page Page - * @param string $selected Id condition pre-selectionne - * @param string $htmlname Name of select html field - * @param int $addempty Add empty entry - * @return void - */ - public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0) - { - // phpcs:enable - global $langs; - if ($htmlname != "none") - { - print '
'; - print ''; - print ''; - $this->select_conditions_paiements($selected, $htmlname, -1, $addempty); - print ''; - print '
'; - } - else - { - if ($selected) - { - $this->load_cache_conditions_paiements(); - print $this->cache_conditions_paiements[$selected]['label']; - } else { - print " "; - } - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show a form to select a delivery delay - * - * @param int $page Page - * @param string $selected Id condition pre-selectionne - * @param string $htmlname Name of select html field - * @param int $addempty Ajoute entree vide - * @return void - */ - public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0) - { - // phpcs:enable - global $langs; - if ($htmlname != "none") - { - print '
'; - print ''; - print ''; - $this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty); - print ''; - print '
'; - } - else - { - if ($selected) - { - $this->load_cache_availability(); - print $this->cache_availability[$selected]['label']; - } else { - print " "; - } - } - } - - /** - * Output HTML form to select list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...) - * List found into table c_input_reason loaded by loadCacheInputReason - * - * @param string $page Page - * @param string $selected Id condition pre-selectionne - * @param string $htmlname Name of select html field - * @param int $addempty Add empty entry - * @return void - */ - public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0) - { - global $langs; - if ($htmlname != "none") - { - print '
'; - print ''; - print ''; - $this->selectInputReason($selected, $htmlname, -1, $addempty); - print ''; - print '
'; - } - else - { - if ($selected) - { - $this->loadCacheInputReason(); - foreach ($this->cache_demand_reason as $key => $val) - { - if ($val['id'] == $selected) - { - print $val['label']; - break; - } - } - } else { - print " "; - } - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show a form + html select a date - * - * @param string $page Page - * @param string $selected Date preselected - * @param string $htmlname Html name of date input fields or 'none' - * @param int $displayhour Display hour selector - * @param int $displaymin Display minutes selector - * @param int $nooutput 1=No print output, return string - * @return string - * @see selectDate() - */ - public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0) - { - // phpcs:enable - global $langs; - - $ret = ''; - - if ($htmlname != "none") - { - $ret .= '
'; - $ret .= ''; - $ret .= ''; - $ret .= ''; - $ret .= ''; - $ret .= ''; - $ret .= '
'; - $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0); - $ret .= '
'; - } - else - { - if ($displayhour) $ret .= dol_print_date($selected, 'dayhour'); - else $ret .= dol_print_date($selected, 'day'); - } - - if (empty($nooutput)) print $ret; - return $ret; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show a select form to choose a user - * - * @param string $page Page - * @param string $selected Id of user preselected - * @param string $htmlname Name of input html field. If 'none', we just output the user link. - * @param array $exclude List of users id to exclude - * @param array $include List of users id to include - * @return void - */ - public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '') - { - // phpcs:enable - global $langs; - - if ($htmlname != "none") - { - print '
'; - print ''; - print ''; - print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include); - print ''; - print '
'; - } - else - { - if ($selected) - { - require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; - $theuser = new User($this->db); - $theuser->fetch($selected); - print $theuser->getNomUrl(1); - } else { - print " "; - } - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show form with payment mode - * - * @param string $page Page - * @param int $selected Id mode pre-selectionne - * @param string $htmlname Name of select html field - * @param string $filtertype To filter on field type in llx_c_paiement (array('code'=>xx,'label'=>zz)) - * @param int $active Active or not, -1 = all - * @param int $addempty 1=Add empty entry - * @return void - */ - public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0) - { - // phpcs:enable - global $langs; - if ($htmlname != "none") - { - print '
'; - print ''; - print ''; - $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active); - print ''; - print '
'; - } - else - { - if ($selected) - { - $this->load_cache_types_paiements(); - print $this->cache_types_paiements[$selected]['label']; - } else { - print " "; - } - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show form with multicurrency code - * - * @param string $page Page - * @param string $selected code pre-selectionne - * @param string $htmlname Name of select html field - * @return void - */ - public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code') - { - // phpcs:enable - global $langs; - if ($htmlname != "none") - { - print '
'; - print ''; - print ''; - print $this->selectMultiCurrency($selected, $htmlname, 0); - print ''; - print '
'; - } - else - { - dol_include_once('/core/lib/company.lib.php'); - print !empty($selected) ? currency_name($selected, 1) : ' '; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show form with multicurrency rate - * - * @param string $page Page - * @param double $rate Current rate - * @param string $htmlname Name of select html field - * @param string $currency Currency code to explain the rate - * @return void - */ - public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '') - { - // phpcs:enable - global $langs, $mysoc, $conf; - - if ($htmlname != "none") - { - print '
'; - print ''; - print ''; - print ' '; - print ' '; - print ''; - print '
'; - } - else - { - if (!empty($rate)) - { - print price($rate, 1, $langs, 1, 0); - if ($currency && $rate != 1) print '   ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')'; - } - else - { - print 1; - } - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show a select box with available absolute discounts - * - * @param string $page Page URL where form is shown - * @param int $selected Value pre-selected - * @param string $htmlname Name of SELECT component. If 'none', not changeable. Example 'remise_id'. - * @param int $socid Third party id - * @param float $amount Total amount available - * @param string $filter SQL filter on discounts - * @param int $maxvalue Max value for lines that can be selected - * @param string $more More string to add - * @param int $hidelist 1=Hide list - * @param int $discount_type 0 => customer discount, 1 => supplier discount - * @return void - */ - public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0) - { - // phpcs:enable - global $conf, $langs; - if ($htmlname != "none") - { - print '
'; - print ''; - print ''; - print '
'; - if (!empty($discount_type)) { - if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) - { - if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice - else $translationKey = 'HasCreditNoteFromSupplier'; - } - else - { - if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") $translationKey = 'HasAbsoluteDiscountFromSupplier'; - else $translationKey = 'HasCreditNoteFromSupplier'; - } - } else { - if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) - { - if (!$filter || $filter == "fk_facture_source IS NULL") $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice - else $translationKey = 'CompanyHasCreditNote'; - } - else - { - if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") $translationKey = 'CompanyHasAbsoluteDiscount'; - else $translationKey = 'CompanyHasCreditNote'; - } - } - print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency)); - if (empty($hidelist)) print ': '; - print '
'; - if (empty($hidelist)) - { - print '
'; - $newfilter = 'discount_type='.intval($discount_type); - if (!empty($discount_type)) { - $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available - } else { - $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available - } - if ($filter) $newfilter .= ' AND ('.$filter.')'; - $nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue); - if ($nbqualifiedlines > 0) - { - print '   '; - } - print '
'; - } - if ($more) - { - print '
'; - print $more; - print '
'; - } - print '
'; - } - else - { - if ($selected) - { - print $selected; - } - else - { - print "0"; - } - } - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show forms to select a contact - * - * @param string $page Page - * @param Societe $societe Filter on third party - * @param int $selected Id contact pre-selectionne - * @param string $htmlname Name of HTML select. If 'none', we just show contact link. - * @return void - */ - public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid') - { - // phpcs:enable - global $langs, $conf; - - if ($htmlname != "none") - { - print '
'; - print ''; - print ''; - print ''; - print ''; - print ''; - print '
'; - $num = $this->select_contacts($societe->id, $selected, $htmlname); - if ($num == 0) - { - $addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress")); - print ''.$addcontact.''; - } - print '
'; - } - else - { - if ($selected) - { - require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; - $contact = new Contact($this->db); - $contact->fetch($selected); - print $contact->getFullName($langs); - } else { - print " "; - } - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Output html select to select thirdparty - * - * @param string $page Page - * @param string $selected Id preselected - * @param string $htmlname Name of HTML select - * @param string $filter optional filters criteras - * @param int $showempty Add an empty field - * @param int $showtype Show third party type in combolist (customer, prospect or supplier) - * @param int $forcecombo Force to use combo box - * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) - * @param int $nooutput No print output. Return it only. - * @return void|string - */ - public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0) - { - // phpcs:enable - global $langs; - - $out = ''; - if ($htmlname != "none") - { - $out .= '
'; - $out .= ''; - $out .= ''; - $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events); - $out .= ''; - $out .= '
'; - } - else - { - if ($selected) - { - require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; - $soc = new Societe($this->db); - $soc->fetch($selected); - $out .= $soc->getNomUrl($langs); - } - else - { - $out .= " "; - } - } - - if ($nooutput) return $out; - else print $out; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Retourne la liste des devises, dans la langue de l'utilisateur - * - * @param string $selected preselected currency code - * @param string $htmlname name of HTML select list - * @deprecated - * @return void - */ - public function select_currency($selected = '', $htmlname = 'currency_id') - { - // phpcs:enable - print $this->selectCurrency($selected, $htmlname); - } - - /** - * Retourne la liste des devises, dans la langue de l'utilisateur - * - * @param string $selected preselected currency code - * @param string $htmlname name of HTML select list - * @param string $mode 0 = Add currency symbol into label, 1 = Add 3 letter iso code - * @return string - */ - public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0) - { - global $conf, $langs, $user; - - $langs->loadCacheCurrencies(''); - - $out = ''; - - if ($selected == 'euro' || $selected == 'euros') $selected = 'EUR'; // Pour compatibilite - - $out .= ''; - if ($user->admin) $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); - - // Make select dynamic - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $out .= ajax_combobox($htmlname); - - return $out; - } - - /** - * Return array of currencies in user language - * - * @param string $selected preselected currency code - * @param string $htmlname name of HTML select list - * @param integer $useempty 1=Add empty line - * @return string - */ - public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0) - { - global $db, $conf, $langs, $user; - - $langs->loadCacheCurrencies(''); // Load ->cache_currencies - - $TCurrency = array(); - - $sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'multicurrency'; - $sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')"; - $resql = $db->query($sql); - if ($resql) - { - while ($obj = $db->fetch_object($resql)) $TCurrency[$obj->code] = $obj->code; - } - - $out = ''; - $out .= ''; - // Make select dynamic - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $out .= ajax_combobox($htmlname); - - return $out; - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Load into the cache vat rates of a country - * - * @param string $country_code Country code with quotes ("'CA'", or "'CA,IN,...'") - * @return int Nb of loaded lines, 0 if already loaded, <0 if KO - */ - public function load_cache_vatrates($country_code) - { - // phpcs:enable - global $langs; - - $num = count($this->cache_vatrates); - if ($num > 0) return $num; // Cache already loaded - - dol_syslog(__METHOD__, LOG_DEBUG); - - $sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly"; - $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; - $sql .= " WHERE t.fk_pays = c.rowid"; - $sql .= " AND t.active > 0"; - $sql .= " AND c.code IN (".$country_code.")"; - $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC"; - - $resql = $this->db->query($sql); - if ($resql) - { - $num = $this->db->num_rows($resql); - if ($num) - { - for ($i = 0; $i < $num; $i++) - { - $obj = $this->db->fetch_object($resql); - $this->cache_vatrates[$i]['rowid'] = $obj->rowid; - $this->cache_vatrates[$i]['code'] = $obj->code; - $this->cache_vatrates[$i]['txtva'] = $obj->taux; - $this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly; - $this->cache_vatrates[$i]['localtax1'] = $obj->localtax1; - $this->cache_vatrates[$i]['localtax1_type'] = $obj->localtax1_type; - $this->cache_vatrates[$i]['localtax2'] = $obj->localtax2; - $this->cache_vatrates[$i]['localtax2_type'] = $obj->localtax1_type; - - $this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or * - $this->cache_vatrates[$i]['labelallrates'] = $obj->taux.'/'.($obj->localtax1 ? $obj->localtax1 : '0').'/'.($obj->localtax2 ? $obj->localtax2 : '0').($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label - $positiverates = ''; - if ($obj->taux) $positiverates .= ($positiverates ? '/' : '').$obj->taux; - if ($obj->localtax1) $positiverates .= ($positiverates ? '/' : '').$obj->localtax1; - if ($obj->localtax2) $positiverates .= ($positiverates ? '/' : '').$obj->localtax2; - if (empty($positiverates)) $positiverates = '0'; - $this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label - } - - return $num; - } - else - { - $this->error = ''.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).''; - return -1; - } - } - else - { - $this->error = ''.$this->db->error().''; - return -2; - } - } - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Output an HTML select vat rate. - * The name of this function should be selectVat. We keep bad name for compatibility purpose. - * - * @param string $htmlname Name of HTML select field - * @param float|string $selectedrate Force preselected vat rate. Can be '8.5' or '8.5 (NOO)' for example. Use '' for no forcing. - * @param Societe $societe_vendeuse Thirdparty seller - * @param Societe $societe_acheteuse Thirdparty buyer - * @param int $idprod Id product. O if unknown of NA. - * @param int $info_bits Miscellaneous information on line (1 for NPR) - * @param int|string $type ''=Unknown, 0=Product, 1=Service (Used if idprod not defined) - * Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle. - * Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle. - * Si (vendeur et acheteur dans Communaute europeenne) et bien vendu = moyen de transports neuf (auto, bateau, avion), TVA par defaut=0 (La TVA doit etre paye par l'acheteur au centre d'impots de son pays et non au vendeur). Fin de regle. - * Si vendeur et acheteur dans Communauté européenne et acheteur= particulier alors TVA par défaut=TVA du produit vendu. Fin de règle. - * Si vendeur et acheteur dans Communauté européenne et acheteur= entreprise alors TVA par défaut=0. Fin de règle. - * Sinon la TVA proposee par defaut=0. Fin de regle. - * @param bool $options_only Return HTML options lines only (for ajax treatment) - * @param int $mode 0=Use vat rate as key in combo list, 1=Add VAT code after vat rate into key, -1=Use id of vat line as key - * @return string - */ - public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0) - { - // phpcs:enable - global $langs, $conf, $mysoc; - - $langs->load('errors'); - - $return = ''; - - // Define defaultnpr, defaultttx and defaultcode - $defaultnpr = ($info_bits & 0x01); - $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr); - $defaulttx = str_replace('*', '', $selectedrate); - $defaultcode = ''; - if (preg_match('/\((.*)\)/', $defaulttx, $reg)) - { - $defaultcode = $reg[1]; - $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx); - } - //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode); - - // Check parameters - if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) - { - if ($societe_vendeuse->id == $mysoc->id) - { - $return .= ''.$langs->trans("ErrorYourCountryIsNotDefined").''; - } - else - { - $return .= ''.$langs->trans("ErrorSupplierCountryIsNotDefined").''; - } - return $return; - } - - //var_dump($societe_acheteuse); - //print "name=$name, selectedrate=$selectedrate, seller=".$societe_vendeuse->country_code." buyer=".$societe_acheteuse->country_code." buyer is company=".$societe_acheteuse->isACompany()." idprod=$idprod, info_bits=$info_bits type=$type"; - //exit; - - // Define list of countries to use to search VAT rates to show - // First we defined code_country to use to find list - if (is_object($societe_vendeuse)) - { - $code_country = "'".$societe_vendeuse->country_code."'"; - } - else - { - $code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente - } - if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) // If option to have vat for end customer for services is on - { - require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; - if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) - { - // We also add the buyer - if (is_numeric($type)) - { - if ($type == 1) // We know product is a service - { - $code_country .= ",'".$societe_acheteuse->country_code."'"; - } - } - elseif (!$idprod) // We don't know type of product - { - $code_country .= ",'".$societe_acheteuse->country_code."'"; - } - else - { - $prodstatic = new Product($this->db); - $prodstatic->fetch($idprod); - if ($prodstatic->type == Product::TYPE_SERVICE) // We know product is a service - { - $code_country .= ",'".$societe_acheteuse->country_code."'"; - } - } - } - } - - // Now we get list - $num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error - - if ($num > 0) - { - // Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '') - if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) - { - $tmpthirdparty = new Societe($this->db); - $defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod); - $defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod); - if (preg_match('/\((.*)\)/', $defaulttx, $reg)) { - $defaultcode = $reg[1]; - $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx); - } - if (empty($defaulttx)) $defaultnpr = 0; - } - - // Si taux par defaut n'a pu etre determine, on prend dernier de la liste. - // Comme ils sont tries par ordre croissant, dernier = plus eleve = taux courant - if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) - { - if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) $defaulttx = $this->cache_vatrates[$num - 1]['txtva']; - else $defaulttx = ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS == 'none' ? '' : $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS); - } - - // Disabled if seller is not subject to VAT - $disabled = false; $title = ''; - if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") - { - // Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead - // of using supplier invoices (this is a very bad idea !) - if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) - { - $title = ' title="'.$langs->trans('VATIsNotUsed').'"'; - $disabled = true; - } - } - - if (!$options_only) $return .= ''; - } - else - { - $return .= $this->error; - } - - $this->num = $num; - return $return; - } - - - // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps - /** - * Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes. - * Fields are preselected with : - * - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM') - * - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location) - * - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1) - * - * @param integer $set_time Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2). - * @param string $prefix Prefix for fields name - * @param int $h 1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty - * @param int $m 1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty - * @param int $empty 0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only - * @param string $form_name Not used - * @param int $d 1=Show days, month, years - * @param int $addnowlink Add a link "Now" - * @param int $nooutput Do not output html string but return it - * @param int $disabled Disable input fields - * @param int $fullday When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59 - * @param string $addplusone Add a link "+1 hour". Value must be name of another select_date field. - * @param datetime $adddateof Add a link "Date of invoice" using the following date. - * @return string|void Nothing or string if nooutput is 1 - * @deprecated - * @see selectDate(), form_date(), select_month(), select_year(), select_dayofweek() - */ - public function select_date($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $nooutput = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '') - { - // phpcs:enable - $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof); - if (!empty($nooutput)) { - return $retstring; - } - print $retstring; - return; - } - - /** - * Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes. - * Fields are preselected with : - * - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM') - * - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location) - * - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1) - * - * @param integer $set_time Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2). - * @param string $prefix Prefix for fields name - * @param int $h 1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty - * @param int $m 1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty - * @param int $empty 0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only - * @param string $form_name Not used - * @param int $d 1=Show days, month, years - * @param int $addnowlink Add a link "Now", 1 with server time, 2 with local computer time - * @param int $disabled Disable input fields - * @param int $fullday When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59 - * @param string $addplusone Add a link "+1 hour". Value must be name of another selectDate field. - * @param datetime $adddateof Add a link "Date of ..." using the following date. See also $labeladddateof for the label used. - * @param string $openinghours Specify hour start and hour end for the select ex 8,20 - * @param int $stepminutes Specify step for minutes between 1 and 30 - * @param string $labeladddateof Label to use for the $adddateof parameter. - * @return string Html for selectDate - * @see form_date(), select_month(), select_year(), select_dayofweek() - */ - public function selectDate($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '', $openinghours = '', $stepminutes = 1, $labeladddateof = '') - { - global $conf, $langs; - - $retstring = ''; - - if ($prefix == '') $prefix = 're'; - if ($h == '') $h = 0; - if ($m == '') $m = 0; - $emptydate = 0; - $emptyhours = 0; - if ($stepminutes <= 0 || $stepminutes > 30) $stepminutes = 1; - if ($empty == 1) { $emptydate = 1; $emptyhours = 1; } - if ($empty == 2) { $emptydate = 0; $emptyhours = 1; } - $orig_set_time = $set_time; - - if ($set_time === '' && $emptydate == 0) - { - include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; - $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone - } - - // Analysis of the pre-selection date - if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) // deprecated usage - { - // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' - $syear = (!empty($reg[1]) ? $reg[1] : ''); - $smonth = (!empty($reg[2]) ? $reg[2] : ''); - $sday = (!empty($reg[3]) ? $reg[3] : ''); - $shour = (!empty($reg[4]) ? $reg[4] : ''); - $smin = (!empty($reg[5]) ? $reg[5] : ''); - } - elseif (strval($set_time) != '' && $set_time != -1) - { - // set_time est un timestamps (0 possible) - $syear = dol_print_date($set_time, "%Y"); - $smonth = dol_print_date($set_time, "%m"); - $sday = dol_print_date($set_time, "%d"); - if ($orig_set_time != '') - { - $shour = dol_print_date($set_time, "%H"); - $smin = dol_print_date($set_time, "%M"); - $ssec = dol_print_date($set_time, "%S"); - } - else - { - $shour = ''; - $smin = ''; - $ssec = ''; - } - } - else - { - // Date est '' ou vaut -1 - $syear = ''; - $smonth = ''; - $sday = ''; - $shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR; - $smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN; - $ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC; - } - if ($h == 3) $shour = ''; - if ($m == 3) $smin = ''; - - // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery' - $usecalendar = 'combo'; - if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) { - $usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR); - } - - if ($d) - { - // Show date with popup - if ($usecalendar != 'combo') - { - $formated_date = ''; - //print "e".$set_time." t ".$conf->format_date_short; - if (strval($set_time) != '' && $set_time != -1) - { - //$formated_date=dol_print_date($set_time,$conf->format_date_short); - $formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput")); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript - } - - // Calendrier popup version eldy - if ($usecalendar == "eldy") - { - // Zone de saisie manuelle de la date - $retstring .= 'trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript - $retstring .= '>'; - - // Icone calendrier - if (!$disabled) - { - $retstring .= ''; - } - else $retstring .= ''; - - $retstring .= ''."\n"; - $retstring .= ''."\n"; - $retstring .= ''."\n"; - } - elseif ($usecalendar == 'jquery') - { - if (!$disabled) - { - // Output javascript for datepicker - $retstring .= ""; - } - - // Zone de saisie manuelle de la date - $retstring .= '
'; - $retstring .= 'trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript - $retstring .= '>'; - - // Icone calendrier - if (!$disabled) - { - /* Not required. Managed by option buttonImage of jquery - $retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"'); - $retstring.="";*/ - } - else - { - $retstring .= ''; - } - - $retstring .= '
'; - $retstring .= ''."\n"; - $retstring .= ''."\n"; - $retstring .= ''."\n"; - } - else - { - $retstring .= "Bad value of MAIN_POPUP_CALENDAR"; - } - } - // Show date with combo selects - else - { - //$retstring.='
'; - // Day - $retstring .= ''; - - if ($emptydate || $set_time == -1) - { - $retstring .= ''; - } - - for ($day = 1; $day <= 31; $day++) - { - $retstring .= ''; - } - - $retstring .= ""; - - $retstring .= ''; - if ($emptydate || $set_time == -1) - { - $retstring .= ''; - } - - // Month - for ($month = 1; $month <= 12; $month++) - { - $retstring .= '"; - } - $retstring .= ""; - - // Year - if ($emptydate || $set_time == -1) - { - $retstring .= ''; - } - else - { - $retstring .= ''; - - for ($year = $syear - 10; $year < $syear + 10; $year++) - { - $retstring .= ''; - } - $retstring .= "\n"; - } - //$retstring.='
'; - } - } - - if ($d && $h) $retstring .= ($h == 2 ? '
' : ' '); - - if ($h) - { - $hourstart = 0; - $hourend = 24; - if ($openinghours != '') { - $openinghours = explode(',', $openinghours); - $hourstart = $openinghours[0]; - $hourend = $openinghours[1]; - if ($hourend < $hourstart) $hourend = $hourstart; - } - // Show hour - $retstring .= ''; - if ($emptyhours) $retstring .= ''; - for ($hour = $hourstart; $hour < $hourend; $hour++) - { - if (strlen($hour) < 2) $hour = "0".$hour; - $retstring .= ''; - } - $retstring .= ''; - if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":"; - } - - if ($m) - { - // Show minutes - $retstring .= ''; - if ($emptyhours) $retstring .= ''; - for ($min = 0; $min < 60; $min += $stepminutes) - { - if (strlen($min) < 2) $min = "0".$min; - $retstring .= ''; - } - $retstring .= ''; - - $retstring .= ''; - } - - // Add a "Now" link - if ($conf->use_javascript_ajax && $addnowlink) - { - // Script which will be inserted in the onClick of the "Now" link - $reset_scripts = ""; - if ($addnowlink == 2) // local computer time - { - // pad add leading 0 on numbers - $reset_scripts .= "Number.prototype.pad = function(size) { - var s = String(this); - while (s.length < (size || 2)) {s = '0' + s;} - return s; - }; - var d = new Date();"; - } - - // Generate the date part, depending on the use or not of the javascript calendar - if ($addnowlink == 1) // server time expressed in user time setup - { - $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date(dol_now(), 'day', 'tzuser').'\');'; - $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date(dol_now(), '%d', 'tzuser').'\');'; - $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date(dol_now(), '%m', 'tzuser').'\');'; - $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date(dol_now(), '%Y', 'tzuser').'\');'; - } - elseif ($addnowlink == 2) - { - $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));'; - $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());'; - $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);'; - $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());'; - } - /*if ($usecalendar == "eldy") - { - $base=DOL_URL_ROOT.'/core/'; - $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');'; - } - else - { - $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); '; - $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); '; - $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); '; - }*/ - // Update the hour part - if ($h) - { - if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; - //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); '; - if ($addnowlink == 1) - { - $reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date(dol_now(), '%H', 'tzuser').'\');'; - } - elseif ($addnowlink == 2) - { - $reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(d.getHours().pad());'; - } - - if ($fullday) $reset_scripts .= ' } '; - } - // Update the minute part - if ($m) - { - if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; - //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); '; - if ($addnowlink == 1) - { - $reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date(dol_now(), '%M', 'tzuser').'\');'; - } - elseif ($addnowlink == 2) - { - $reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(d.getMinutes().pad());'; - } - if ($fullday) $reset_scripts .= ' } '; - } - // If reset_scripts is not empty, print the link with the reset_scripts in the onClick - if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) - { - $retstring .= ' '; - } - } - - // Add a "Plus one hour" link - if ($conf->use_javascript_ajax && $addplusone) - { - // Script which will be inserted in the onClick of the "Add plusone" link - $reset_scripts = ""; - - // Generate the date part, depending on the use or not of the javascript calendar - $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date(dol_now(), 'day').'\');'; - $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date(dol_now(), '%d').'\');'; - $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date(dol_now(), '%m').'\');'; - $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date(dol_now(), '%Y').'\');'; - // Update the hour part - if ($h) - { - if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; - $reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date(dol_now(), '%H').'\');'; - if ($fullday) $reset_scripts .= ' } '; - } - // Update the minute part - if ($m) - { - if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; - $reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date(dol_now(), '%M').'\');'; - if ($fullday) $reset_scripts .= ' } '; - } - // If reset_scripts is not empty, print the link with the reset_scripts in the onClick - if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) - { - $retstring .= ' '; - } - } - - // Add a "Plus one hour" link - if ($conf->use_javascript_ajax && $adddateof) - { - $tmparray = dol_getdate($adddateof); - if (empty($labeladddateof)) $labeladddateof = $langs->trans("DateInvoice"); - $retstring .= ' - '; - $out .= ''; - $out .= '
'; - - return $out; - } - - /** - * Return HTML to show the search and clear seach button - * - * @param string $cssclass CSS class - * @param int $calljsfunction 0=default. 1=call function initCheckForSelect() after changing status of checkboxes - * @param string $massactionname Mass action button name that will launch an action on the selected items - * @return string - */ - public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction") - { - global $conf, $langs; - - $out = ''; - $id = uniqid(); - if (!empty($conf->use_javascript_ajax)) $out .= '
'; - $out .= ''; - - return $out; - } - - /** - * Return HTML to show the search and clear seach button - * - * @param int $addcheckuncheckall Add the check all/uncheck all checkbox (use javascript) and code to manage this - * @param string $cssclass CSS class - * @param int $calljsfunction 0=default. 1=call function initCheckForSelect() after changing status of checkboxes - * @param string $massactionname Mass action name - * @return string - */ - public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction") - { - $out = $this->showFilterButtons(); - if ($addcheckuncheckall) - { - $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname); - } - return $out; - } - - /** - * Return HTML to show the select of expense categories - * - * @param string $selected preselected category - * @param string $htmlname name of HTML select list - * @param integer $useempty 1=Add empty line - * @param array $excludeid id to exclude - * @param string $target htmlname of target select to bind event - * @param int $default_selected default category to select if fk_c_type_fees change = EX_KME - * @param array $params param to give - * @return string - */ - public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array()) - { - global $db, $conf, $langs, $user; - - $out = ''; - $sql = 'SELECT rowid, label FROM '.MAIN_DB_PREFIX.'c_exp_tax_cat WHERE active = 1'; - $sql .= ' AND entity IN (0,'.getEntity('exp_tax_cat').')'; - if (!empty($excludeid)) $sql .= ' AND rowid NOT IN ('.implode(',', $excludeid).')'; - $sql .= ' ORDER BY label'; - - $resql = $db->query($sql); - if ($resql) - { - $out = ''; - if (!empty($htmlname) && $user->admin) $out .= ' '.info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); - - if (!empty($target)) - { - $sql = "SELECT c.id FROM ".MAIN_DB_PREFIX."c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1"; - $resql = $db->query($sql); - if ($resql) - { - if ($db->num_rows($resql) > 0) - { - $obj = $db->fetch_object($resql); - $out .= ''; - } - } - } - } - else - { - dol_print_error($db); - } - - return $out; - } - - /** - * Return HTML to show the select ranges of expense range - * - * @param string $selected preselected category - * @param string $htmlname name of HTML select list - * @param integer $useempty 1=Add empty line - * @return string - */ - public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0) - { - global $db, $conf, $langs; - - $out = ''; - $sql = 'SELECT rowid, range_ik FROM '.MAIN_DB_PREFIX.'c_exp_tax_range'; - $sql .= ' WHERE entity = '.$conf->entity.' AND active = 1'; - - $resql = $db->query($sql); - if ($resql) - { - $out = ''; - } - else - { - dol_print_error($db); - } - - return $out; - } - - /** - * Return HTML to show a select of expense - * - * @param string $selected preselected category - * @param string $htmlname name of HTML select list - * @param integer $useempty 1=Add empty choice - * @param integer $allchoice 1=Add all choice - * @param integer $useid 0=use 'code' as key, 1=use 'id' as key - * @return string - */ - public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0) - { - global $db, $langs; - - $out = ''; - $sql = 'SELECT id, code, label FROM '.MAIN_DB_PREFIX.'c_type_fees'; - $sql .= ' WHERE active = 1'; - - $resql = $db->query($sql); - if ($resql) - { - $out = ''; - } - else - { - dol_print_error($db); - } - - return $out; - } - - /** - * Output a combo list with invoices qualified for a third party - * - * @param int $socid Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id) - * @param int $selected Id invoice preselected - * @param string $htmlname Name of HTML select - * @param int $maxlength Maximum length of label - * @param int $option_only Return only html options lines without the select tag - * @param string $show_empty Add an empty line ('1' or string to show for empty line) - * @param int $discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable) - * @param int $forcefocus Force focus on field (works with javascript only) - * @param int $disabled Disabled - * @param string $morecss More css added to the select component - * @param string $projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids. - * @param string $showproject 'all' = Show project info, ''=Hide project info - * @param User $usertofilter User object to use for filtering - * @return int Nbr of project if OK, <0 if KO - */ - public function selectInvoice($socid = -1, $selected = '', $htmlname = 'invoiceid', $maxlength = 24, $option_only = 0, $show_empty = '1', $discard_closed = 0, $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500', $projectsListId = '', $showproject = 'all', $usertofilter = null) - { - global $user, $conf, $langs; - - require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; - - if (is_null($usertofilter)) - { - $usertofilter = $user; - } - - $out = ''; - - $hideunselectables = false; - if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) $hideunselectables = true; - - if (empty($projectsListId)) - { - if (empty($usertofilter->rights->projet->all->lire)) - { - $projectstatic = new Project($this->db); - $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1); - } - } - - // Search all projects - $sql = 'SELECT f.rowid, f.ref as fref, "nolabel" as flabel, p.rowid as pid, f.ref, - p.title, p.fk_soc, p.fk_statut, p.public,'; - $sql .= ' s.nom as name'; - $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as p'; - $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc,'; - $sql .= ' '.MAIN_DB_PREFIX.'facture as f'; - $sql .= " WHERE p.entity IN (".getEntity('project').")"; - $sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement - //if ($projectsListId) $sql.= " AND p.rowid IN (".$projectsListId.")"; - //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)"; - //if ($socid > 0) $sql.= " AND (p.fk_soc=".$socid." OR p.fk_soc IS NULL)"; - $sql .= " GROUP BY f.ref ORDER BY p.ref, f.ref ASC"; - - $resql = $this->db->query($sql); - if ($resql) - { - // Use select2 selector - if (!empty($conf->use_javascript_ajax)) - { - include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; - $comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus); - $out .= $comboenhancement; - $morecss = 'minwidth200imp maxwidth500'; - } - - if (empty($option_only)) { - $out .= ''; - } - - print $out; - - $this->db->free($resql); - return $num; - } - else - { - dol_print_error($this->db); - return -1; - } - } - - /** - * Output the component to make advanced search criteries - * - * @param array $arrayofcriterias Array of available search criterias. Example: array($object->element => $object->fields, 'otherfamily' => otherarrayoffields, ...) - * @param array $search_component_params Array of selected search criterias - * @param array $arrayofinputfieldsalreadyoutput Array of input fields already inform. The component will not generate a hidden input field if it is in this list. - * @return string HTML component for advanced search - */ - public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array()) - { - global $conf, $langs; - - $ret = ''; - - $ret .= '
'; - //$ret .= ''; - $ret .= ''; - $ret .= ''; - $ret .= $langs->trans("Filters"); - $ret .= ''; - //$ret .= ''; - $ret .= '
'; - $texttoshow = '
'.$langs->trans("Search").'
'; - - $ret .= '
'.$texttoshow.'
'; - $ret .= '
'; - $ret .= ''; - // For compatibility with forms that show themself the search criteria in addition of this component, we output the fields - foreach ($arrayofcriterias as $criterias) { - foreach ($criterias as $criteriafamilykey => $criteriafamilyval) { - if (in_array('search_'.$criteriafamilykey, $arrayofinputfieldsalreadyoutput)) continue; - if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) continue; - if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) { - $ret .= ''; - $ret .= ''; - $ret .= ''; - $ret .= ''; - $ret .= ''; - $ret .= ''; - $ret .= ''; - $ret .= ''; - } - else { - $ret .= ''; - } - } - } - $ret .= '
'; - - - return $ret; - } -} From 0ea8d9f651433f62d185e0bc660518cac49ff99c Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Tue, 26 May 2020 00:35:01 +0200 Subject: [PATCH 09/22] Update products.lang --- htdocs/langs/en_US/products.lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index c0c5cfa0268..aa3666a3a73 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -106,7 +106,7 @@ NoteNotVisibleOnBill=Note (not visible on invoices, proposals...) ServiceLimitedDuration=If product is a service with limited duration: MultiPricesAbility=Multiple price segments per product/service (each customer is in one price segment) MultiPricesNumPrices=Number of prices -DefaultPriceType= Default price type +DefaultPriceType=Base of prices per default (with versus without tax) AssociatedProductsAbility=Activate virtual products (kits) AssociatedProducts=Virtual products AssociatedProductsNumber=Number of products composing this virtual product From 7da6a063f977dbb5569b2d80455649d8d640a685 Mon Sep 17 00:00:00 2001 From: ptibogxiv Date: Thu, 4 Jun 2020 16:46:36 +0200 Subject: [PATCH 10/22] Update llx_subscription.sql --- htdocs/install/mysql/tables/llx_subscription.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/install/mysql/tables/llx_subscription.sql b/htdocs/install/mysql/tables/llx_subscription.sql index a3562f12c57..03d24c68f6e 100644 --- a/htdocs/install/mysql/tables/llx_subscription.sql +++ b/htdocs/install/mysql/tables/llx_subscription.sql @@ -24,7 +24,7 @@ create table llx_subscription fk_adherent integer, fk_type integer, dateadh datetime, - datef date, + datef datetime, subscription double(24,8), fk_bank integer DEFAULT NULL, fk_user_creat integer DEFAULT NULL, From 319710d6aa7df937a10ad34aa606f035a55b4b35 Mon Sep 17 00:00:00 2001 From: ptibogxiv Date: Thu, 4 Jun 2020 16:50:24 +0200 Subject: [PATCH 11/22] Update 12.0.0-13.0.0.sql --- htdocs/install/mysql/migration/12.0.0-13.0.0.sql | 1 + 1 file changed, 1 insertion(+) 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 58d28dcd6a3..163fec01b3f 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 @@ -43,3 +43,4 @@ UPDATE llx_establishment SET ref = rowid WHERE ref IS NULL; ALTER TABLE llx_establishment MODIFY COLUMN ref varchar(30) NOT NULL; ALTER TABLE llx_establishment MODIFY COLUMN name varchar(128); +ALTER TABLE llx_subscription MODIFY COLUMN datef DATETIME; From 37007d6d1a14d6219b309734f97317d1ad278e52 Mon Sep 17 00:00:00 2001 From: ptibogxiv Date: Sun, 7 Jun 2020 16:12:28 +0200 Subject: [PATCH 12/22] Update subscription.class.php --- htdocs/adherents/class/subscription.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/adherents/class/subscription.class.php b/htdocs/adherents/class/subscription.class.php index cdc70cf0cd1..5a75fdbae2e 100644 --- a/htdocs/adherents/class/subscription.class.php +++ b/htdocs/adherents/class/subscription.class.php @@ -99,7 +99,7 @@ class Subscription extends CommonObject 'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>20), 'fk_adherent' =>array('type'=>'integer', 'label'=>'Member', 'enabled'=>1, 'visible'=>-1, 'position'=>25), 'dateadh' =>array('type'=>'datetime', 'label'=>'DateSubscription', 'enabled'=>1, 'visible'=>-1, 'position'=>30), - 'datef' =>array('type'=>'date', 'label'=>'DateEndSubscription', 'enabled'=>1, 'visible'=>-1, 'position'=>35), + 'datef' =>array('type'=>'datetime', 'label'=>'DateEndSubscription', 'enabled'=>1, 'visible'=>-1, 'position'=>35), 'subscription' =>array('type'=>'double(24,8)', 'label'=>'Amount', 'enabled'=>1, 'visible'=>-1, 'position'=>40, 'isameasure'=>1), 'fk_bank' =>array('type'=>'integer', 'label'=>'BankId', 'enabled'=>1, 'visible'=>-1, 'position'=>45), 'note' =>array('type'=>'text', 'label'=>'Note', 'enabled'=>1, 'visible'=>-1, 'position'=>50), From 9d438278444657d35c58a37742467fc6ca26119b Mon Sep 17 00:00:00 2001 From: Anthony Berton Date: Thu, 11 Jun 2020 15:18:10 +0200 Subject: [PATCH 13/22] Revert "Delete html.form.class.php" This reverts commit aa275f2a795bbf221d8878ec7f2e09b0f8706335. --- htdocs/core/class/html.form.class.php | 8282 +++++++++++++++++++++++++ 1 file changed, 8282 insertions(+) create mode 100644 htdocs/core/class/html.form.class.php diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php new file mode 100644 index 00000000000..008006b8fa5 --- /dev/null +++ b/htdocs/core/class/html.form.class.php @@ -0,0 +1,8282 @@ + + * Copyright (C) 2004-2012 Laurent Destailleur + * Copyright (C) 2004 Benoit Mortier + * Copyright (C) 2004 Sebastien Di Cintio + * Copyright (C) 2004 Eric Seigne + * Copyright (C) 2005-2017 Regis Houssin + * Copyright (C) 2006 Andre Cianfarani + * Copyright (C) 2006 Marc Barilley/Ocebo + * Copyright (C) 2007 Franky Van Liedekerke + * Copyright (C) 2007 Patrick Raguin + * Copyright (C) 2010 Juanjo Menent + * Copyright (C) 2010-2019 Philippe Grand + * Copyright (C) 2011 Herve Prot + * 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) 2018 Ferran Marcet + * Copyright (C) 2018-2019 Frédéric France + * Copyright (C) 2018 Nicolas ZABOURI + * Copyright (C) 2018 Christophe Battarel + * Copyright (C) 2018 Josep Lluis Amador + * + * 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/class/html.form.class.php + * \ingroup core + * \brief File of class with all html predefined components + */ + + +/** + * Class to manage generation of HTML components + * Only common components must be here. + * + * TODO Merge all function load_cache_* and loadCache* (except load_cache_vatrates) into one generic function loadCacheTable + */ +class Form +{ + /** + * @var DoliDB Database handler. + */ + public $db; + + /** + * @var string Error code (or message) + */ + public $error = ''; + + /** + * @var string[] Array of error strings + */ + public $errors = array(); + + public $num; + + // Cache arrays + public $cache_types_paiements = array(); + public $cache_conditions_paiements = array(); + public $cache_availability = array(); + public $cache_demand_reason = array(); + public $cache_types_fees = array(); + public $cache_vatrates = array(); + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + } + + /** + * Output key field for an editable field + * + * @param string $text Text of label or key to translate + * @param string $htmlname Name of select field ('edit' prefix will be added) + * @param string $preselected Value to show/edit (not used in this function) + * @param object $object Object + * @param boolean $perm Permission to allow button to edit parameter. Set it to 0 to have a not edited field. + * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...) + * @param string $moreparam More param to add on a href URL. + * @param int $fieldrequired 1 if we want to show field as mandatory using the "fieldrequired" CSS. + * @param int $notabletag 1=Do not output table tags but output a ':', 2=Do not output table tags and no ':', 3=Do not output table tags but output a ' ' + * @param string $paramid Key of parameter for id ('id', 'socid') + * @param string $help Tooltip help + * @return string HTML edit field + */ + public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '') + { + global $conf, $langs; + + $ret = ''; + + // TODO change for compatibility + if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;/', $typeofdata)) + { + if (!empty($perm)) + { + $tmp = explode(':', $typeofdata); + $ret .= '
'; + if ($fieldrequired) $ret .= ''; + if ($help) { + $ret .= $this->textwithpicto($langs->trans($text), $help); + } else { + $ret .= $langs->trans($text); + } + if ($fieldrequired) $ret .= ''; + $ret .= '
'."\n"; + } + else + { + if ($fieldrequired) $ret .= ''; + if ($help) { + $ret .= $this->textwithpicto($langs->trans($text), $help); + } else { + $ret .= $langs->trans($text); + } + if ($fieldrequired) $ret .= ''; + } + } + else + { + if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= ''; + if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= ''; + if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= '
'; + if ($fieldrequired) $ret .= ''; + if ($help) { + $ret .= $this->textwithpicto($langs->trans($text), $help); + } else { + $ret .= $langs->trans($text); + } + if ($fieldrequired) $ret .= ''; + if (!empty($notabletag)) $ret .= ' '; + if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= ''; + if ($htmlname && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= 'id.$moreparam.'">'.img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1)).''; + if (!empty($notabletag) && $notabletag == 1) $ret .= ' : '; + if (!empty($notabletag) && $notabletag == 3) $ret .= ' '; + if (empty($notabletag) && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) $ret .= '
'; + } + + return $ret; + } + + /** + * Output value of a field for an editable field + * + * @param string $text Text of label (not used in this function) + * @param string $htmlname Name of select field + * @param string $value Value to show/edit + * @param object $object Object + * @param boolean $perm Permission to allow button to edit parameter + * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols%', 'datepicker' ('day' do not work, don't know why), 'dayhour' or 'datepickerhour', 'ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols', 'select;xkey:xval,ykey:yval,...') + * @param string $editvalue When in edit mode, use this value as $value instead of value (for example, you can provide here a formated price instead of value). Use '' to use same than $value + * @param object $extObject External object + * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage') + * @param string $moreparam More param to add on the form action href URL + * @param int $notabletag Do no output table tags + * @param string $formatfunc Call a specific function to output field + * @param string $paramid Key of parameter for id ('id', 'socid') + * @return string HTML edit field + */ + public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 0, $formatfunc = '', $paramid = 'id') + { + global $conf, $langs, $db; + + $ret = ''; + + // Check parameters + if (empty($typeofdata)) return 'ErrorBadParameter'; + + // When option to edit inline is activated + if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;|datehourpicker/', $typeofdata)) // TODO add jquery timepicker and support select + { + $ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg); + } + else + { + if (GETPOST('action', 'aZ09') == 'edit'.$htmlname) + { + $ret .= "\n"; + $ret .= '
'; + $ret .= ''; + $ret .= ''; + $ret .= ''; + if (empty($notabletag)) $ret .= ''; + if (empty($notabletag)) $ret .= ''; + + if (empty($notabletag)) $ret .= ''; + + if (empty($notabletag)) $ret .= '
'; + if (preg_match('/^(string|safehtmlstring|email)/', $typeofdata)) + { + $tmp = explode(':', $typeofdata); + $ret .= ''; + } + elseif (preg_match('/^(numeric|amount)/', $typeofdata)) + { + $tmp = explode(':', $typeofdata); + $valuetoshow = price2num($editvalue ? $editvalue : $value); + $ret .= ''; + } + elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) // if wysiwyg is enabled $typeofdata = 'ckeditor' + { + $tmp = explode(':', $typeofdata); + $cols = $tmp[2]; + $morealt = ''; + if (preg_match('/%/', $cols)) + { + $morealt = ' style="width: '.$cols.'"'; + $cols = ''; + } + + $valuetoshow = ($editvalue ? $editvalue : $value); + $ret .= ''; + } + elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') + { + $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form'.$htmlname, 1, 0); + } + elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') + { + $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form'.$htmlname, 1, 0); + } + elseif (preg_match('/^select;/', $typeofdata)) + { + $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata)); + $arraylist = array(); + foreach ($arraydata as $val) + { + $tmp = explode(':', $val); + $tmpkey = str_replace('|', ':', $tmp[0]); + $arraylist[$tmpkey] = $tmp[1]; + } + $ret .= $this->selectarray($htmlname, $arraylist, $value); + } + elseif (preg_match('/^ckeditor/', $typeofdata)) + { + $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser + require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; + $doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), ($tmp[2] ? $tmp[2] : ''), ($tmp[3] ? $tmp[3] : '100'), ($tmp[1] ? $tmp[1] : 'dolibarr_notes'), 'In', ($tmp[5] ? $tmp[5] : 0), (isset($tmp[8]) ? ($tmp[8] ?true:false) : true), true, ($tmp[6] ? $tmp[6] : '20'), ($tmp[7] ? $tmp[7] : '100')); + $ret .= $doleditor->Create(1); + } + if (empty($notabletag)) $ret .= ''; + //else $ret.='
'; + $ret .= ''; + if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) $ret .= '
'."\n"; + $ret .= ''; + if (empty($notabletag)) $ret .= '
'."\n"; + $ret .= '
'."\n"; + } + else + { + if (preg_match('/^(email)/', $typeofdata)) $ret .= dol_print_email($value, 0, 0, 0, 0, 1); + elseif (preg_match('/^(amount|numeric)/', $typeofdata)) $ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : ''); + elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) $ret .= dol_htmlentitiesbr($value); + elseif (preg_match('/^safehtmlstring/', $typeofdata)) $ret .= dol_string_onlythesehtmltags($value); + elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') $ret .= dol_print_date($value, 'day'); + elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') $ret .= dol_print_date($value, 'dayhour'); + elseif (preg_match('/^select;/', $typeofdata)) + { + $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata)); + $arraylist = array(); + foreach ($arraydata as $val) + { + $tmp = explode(':', $val); + $arraylist[$tmp[0]] = $tmp[1]; + } + $ret .= $arraylist[$value]; + } + elseif (preg_match('/^ckeditor/', $typeofdata)) + { + $tmpcontent = dol_htmlentitiesbr($value); + if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) + { + $firstline = preg_replace('/
.*/', '', $tmpcontent); + $firstline = preg_replace('/[\n\r].*/', '', $firstline); + $tmpcontent = $firstline.((strlen($firstline) != strlen($tmpcontent)) ? '...' : ''); + } + // We dont use dol_escape_htmltag to get the html formating active, but this need we must also + // clean data from some dangerous html + $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent)); + } + else { + $ret .= dol_escape_htmltag($value); + } + + if ($formatfunc && method_exists($object, $formatfunc)) + { + $ret = $object->$formatfunc($ret); + } + } + } + return $ret; + } + + /** + * Output edit in place form + * + * @param string $fieldname Name of the field + * @param object $object Object + * @param boolean $perm Permission to allow button to edit parameter. Set it to 0 to have a not edited field. + * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...) + * @param string $check Same coe than $check parameter of GETPOST() + * @param string $morecss More CSS + * @return string HTML code for the edit of alternative language + */ + public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '') + { + global $conf, $langs, $extralanguages; + + $result = ''; + + // List of extra languages + $arrayoflangcode = array(); + if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE; + + if (is_array($arrayoflangcode) && count($arrayoflangcode)) { + if (!is_object($extralanguages)) { + include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php'; + $extralanguages = new ExtraLanguages($this->db); + } + $extralanguages->fetch_name_extralanguages('societe'); + + if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) { + return ''; // No extralang field to show + } + + $result .= ''."\n"; + $result .= '
'; + $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang'); + $result .= $s; + $result .= '
'; + + $result .= ''; + $result .= ''; + } + + return $result; + } + + /** + * Output edit in place form + * + * @param object $object Object + * @param string $value Value to show/edit + * @param string $htmlname DIV ID (field name) + * @param int $condition Condition to edit + * @param string $inputType Type of input ('string', 'numeric', 'datepicker' ('day' do not work, don't know why), 'textarea:rows:cols', 'ckeditor:dolibarr_zzz:width:height:?:1:rows:cols', 'select:loadmethod:savemethod:buttononly') + * @param string $editvalue When in edit mode, use this value as $value instead of value + * @param object $extObject External object + * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage') + * @return string HTML edit in place + */ + protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null) + { + global $conf; + + $out = ''; + + // Check parameters + if (preg_match('/^text/', $inputType)) $value = dol_nl2br($value); + elseif (preg_match('/^numeric/', $inputType)) $value = price($value); + elseif ($inputType == 'day' || $inputType == 'datepicker') $value = dol_print_date($value, 'day'); + + if ($condition) + { + $element = false; + $table_element = false; + $fk_element = false; + $loadmethod = false; + $savemethod = false; + $ext_element = false; + $button_only = false; + $inputOption = ''; + + if (is_object($object)) + { + $element = $object->element; + $table_element = $object->table_element; + $fk_element = $object->id; + } + + if (is_object($extObject)) + { + $ext_element = $extObject->element; + } + + if (preg_match('/^(string|email|numeric)/', $inputType)) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; + if (!empty($tmp[1])) $inputOption = $tmp[1]; + if (!empty($tmp[2])) $savemethod = $tmp[2]; + $out .= ''."\n"; + } + elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; + if (!empty($tmp[1])) $inputOption = $tmp[1]; + if (!empty($tmp[2])) $savemethod = $tmp[2]; + + $out .= ''."\n"; // Use for timestamp format + } + elseif (preg_match('/^(select|autocomplete)/', $inputType)) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; $loadmethod = $tmp[1]; + if (!empty($tmp[2])) $savemethod = $tmp[2]; + if (!empty($tmp[3])) $button_only = true; + } + elseif (preg_match('/^textarea/', $inputType)) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; + $rows = (empty($tmp[1]) ? '8' : $tmp[1]); + $cols = (empty($tmp[2]) ? '80' : $tmp[2]); + } + elseif (preg_match('/^ckeditor/', $inputType)) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; $toolbar = $tmp[1]; + if (!empty($tmp[2])) $width = $tmp[2]; + if (!empty($tmp[3])) $heigth = $tmp[3]; + if (!empty($tmp[4])) $savemethod = $tmp[4]; + + if (!empty($conf->fckeditor->enabled)) + { + $out .= ''."\n"; + } + else + { + $inputType = 'textarea'; + } + } + + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + if (!empty($savemethod)) $out .= ''."\n"; + if (!empty($ext_element)) $out .= ''."\n"; + if (!empty($custommsg)) + { + if (is_array($custommsg)) + { + if (!empty($custommsg['success'])) + $out .= ''."\n"; + if (!empty($custommsg['error'])) + $out .= ''."\n"; + } + else + $out .= ''."\n"; + } + if ($inputType == 'textarea') { + $out .= ''."\n"; + $out .= ''."\n"; + } + $out .= ''.$value.''."\n"; + $out .= ''.(!empty($editvalue) ? $editvalue : $value).''."\n"; + } + else + { + $out = $value; + } + + return $out; + } + + /** + * Show a text and picto with tooltip on text or picto. + * Can be called by an instancied $form->textwithtooltip or by a static call Form::textwithtooltip + * + * @param string $text Text to show + * @param string $htmltext HTML content of tooltip. Must be HTML/UTF8 encoded. + * @param int $tooltipon 1=tooltip on text, 2=tooltip on image, 3=tooltip sur les 2 + * @param int $direction -1=image is before, 0=no image, 1=image is after + * @param string $img Html code for image (use img_xxx() function to get it) + * @param string $extracss Add a CSS style to td tags + * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span + * @param string $incbefore Include code before the text + * @param int $noencodehtmltext Do not encode into html entity the htmltext + * @param string $tooltiptrigger ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key) + * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only) + * @return string Code html du tooltip (texte+picto) + * @see textwithpicto() Use thisfunction if you can. + * TODO Move this as static as soon as everybody use textwithpicto or @Form::textwithtooltip + */ + public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0) + { + if ($incbefore) $text = $incbefore.$text; + if (!$htmltext) return $text; + + $tag = 'td'; + if ($notabs == 2) $tag = 'div'; + if ($notabs == 3) $tag = 'span'; + // Sanitize tooltip + $htmltext = str_replace(array("\r", "\n"), '', $htmltext); + + $extrastyle = ''; + if ($direction < 0) { $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : ''); $extrastyle = 'padding: 0px; padding-left: 3px !important;'; } + if ($direction > 0) { $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : ''); $extrastyle = 'padding: 0px; padding-right: 3px !important;'; } + + $classfortooltip = 'classfortooltip'; + + $s = ''; $textfordialog = ''; + + if ($tooltiptrigger == '') + { + $htmltext = str_replace('"', '"', $htmltext); + } + else + { + $classfortooltip = 'classfortooltiponclick'; + $textfordialog .= ''; + } + if ($tooltipon == 2 || $tooltipon == 3) + { + $paramfortooltipimg = ' class="'.$classfortooltip.($notabs != 3 ? ' inline-block' : '').($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'"'; + if ($tooltiptrigger == '') $paramfortooltipimg .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on img tag to store tooltip + else $paramfortooltipimg .= ' dolid="'.$tooltiptrigger.'"'; + } + else $paramfortooltipimg = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag + if ($tooltipon == 1 || $tooltipon == 3) + { + $paramfortooltiptd = ' class="'.($tooltipon == 3 ? 'cursorpointer ' : '').$classfortooltip.' inline-block'.($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'" '; + if ($tooltiptrigger == '') $paramfortooltiptd .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on td tag to store tooltip + else $paramfortooltiptd .= ' dolid="'.$tooltiptrigger.'"'; + } + else $paramfortooltiptd = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag + if (empty($notabs)) $s .= ''; + elseif ($notabs == 2) $s .= '
'; + // Define value if value is before + if ($direction < 0) { + $s .= '<'.$tag.$paramfortooltipimg; + if ($tag == 'td') { + $s .= ' class=valigntop" width="14"'; + } + $s .= '>'.$textfordialog.$img.''; + } + // Use another method to help avoid having a space in value in order to use this value with jquery + // Define label + if ((string) $text != '') $s .= '<'.$tag.$paramfortooltiptd.'>'.$text.''; + // Define value if value is after + if ($direction > 0) { + $s .= '<'.$tag.$paramfortooltipimg; + if ($tag == 'td') $s .= ' class="valignmiddle" width="14"'; + $s .= '>'.$textfordialog.$img.''; + } + if (empty($notabs)) $s .= '
'; + elseif ($notabs == 2) $s .= '
'; + + return $s; + } + + /** + * Show a text with a picto and a tooltip on picto + * + * @param string $text Text to show + * @param string $htmltext Content of tooltip + * @param int $direction 1=Icon is after text, -1=Icon is before text, 0=no icon + * @param string $type Type of picto ('info', 'infoclickable', 'help', 'helpclickable', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath or 'none' + * @param string $extracss Add a CSS style to td, div or span tag + * @param int $noencodehtmltext Do not encode into html entity the htmltext + * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span + * @param string $tooltiptrigger ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key, clickable link is on image or on link if param $type='none' or on both if $type='xxxclickable') + * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only) + * @return string HTML code of text, picto, tooltip + */ + public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0) + { + global $conf, $langs; + + $alt = ''; + if ($tooltiptrigger) $alt = $langs->transnoentitiesnoconv("ClickToShowHelp"); + + //For backwards compatibility + if ($type == '0') $type = 'info'; + elseif ($type == '1') $type = 'help'; + + // If info or help with no javascript, show only text + if (empty($conf->use_javascript_ajax)) + { + if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') return $text; + else + { + $alt = $htmltext; + $htmltext = ''; + } + } + + // If info or help with smartphone, show only text (tooltip hover can't works) + if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) + { + if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') return $text; + } + // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone) + //if (! empty($conf->dol_no_mouse_hover) && ! empty($tooltiptrigger)) + //{ + //if ($type == 'info' || $type == 'help') return ''.$text.'''; + //} + + $img = ''; + if ($type == 'info') $img = img_help(0, $alt); + elseif ($type == 'help') $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt); + elseif ($type == 'helpclickable') $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt); + elseif ($type == 'superadmin') $img = img_picto($alt, 'redstar'); + elseif ($type == 'admin') $img = img_picto($alt, 'star'); + elseif ($type == 'warning') $img = img_warning($alt); + elseif ($type != 'none') $img = img_picto($alt, $type); // $type can be an image path + + return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap); + } + + /** + * Generate select HTML to choose massaction + * + * @param string $selected Value auto selected when at least one record is selected. Not a preselected value. Use '0' by default. + * @param array $arrayofaction array('code'=>'label', ...). The code is the key stored into the GETPOST('massaction') when submitting action. + * @param int $alwaysvisible 1=select button always visible + * @param string $name Name for massaction + * @param string $cssclass CSS class used to check for select + * @return string|void Select list + */ + public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect') + { + global $conf, $langs, $hookmanager; + + + $disabled = 0; + $ret = '
'; + $ret .= ''; + + if (empty($conf->dol_optimize_smallscreen)) $ret .= ajax_combobox('.' . $name . 'select'); + + // Warning: if you set submit button to disabled, post using 'Enter' will no more work if there is no another input submit. So we add a hidden button + $ret .= ''; // Hidden button BEFORE so it is the one used when we submit with ENTER. + $ret .= ''; + $ret .= '
'; + + if (!empty($conf->use_javascript_ajax)) + { + $ret .= ' + + '; + } + + return $ret; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return combo list of activated countries, into language of user + * + * @param string $selected Id or Code or Label of preselected country + * @param string $htmlname Name of html select object + * @param string $htmloption More html options on select object + * @param integer $maxlength Max length for labels (0=no limit) + * @param string $morecss More css class + * @param string $usecodeaskey ''=Use id as key (default), 'code3'=Use code on 3 alpha as key, 'code2"=Use code on 2 alpha as key + * @param int $showempty Show empty choice + * @param int $disablefavorites 1=Disable favorites, + * @param int $addspecialentries 1=Add dedicated entries for group of countries (like 'European Economic Community', ...) + * @param array $exclude_country_code Array of country code (iso2) to exclude + * @return string HTML string with select + */ + public function select_country($selected = '', $htmlname = 'country_id', $htmloption = '', $maxlength = 0, $morecss = 'minwidth300', $usecodeaskey = '', $showempty = 1, $disablefavorites = 0, $addspecialentries = 0, $exclude_country_code = array()) + { + // phpcs:enable + global $conf, $langs, $mysoc; + + $langs->load("dict"); + + $out = ''; + $countryArray = array(); + $favorite = array(); + $label = array(); + $atleastonefavorite = 0; + + $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_country"; + $sql .= " WHERE active > 0"; + //$sql.= " ORDER BY code ASC"; + + dol_syslog(get_class($this)."::select_country", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + $out .= ''; + } + else + { + dol_print_error($this->db); + } + + // Make select dynamic + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox('select'.$htmlname); + + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return select list of incoterms + * + * @param string $selected Id or Code of preselected incoterm + * @param string $location_incoterms Value of input location + * @param string $page Defined the form action + * @param string $htmlname Name of html select object + * @param string $htmloption Options html on select object + * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification) + * @param array $events Event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @return string HTML string with select and input + */ + public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array()) + { + // phpcs:enable + global $conf, $langs; + + $langs->load("dict"); + + $out = ''; + $incotermArray = array(); + + $sql = "SELECT rowid, code"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_incoterms"; + $sql .= " WHERE active > 0"; + $sql .= " ORDER BY code ASC"; + + dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + if ($conf->use_javascript_ajax && !$forcecombo) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname, $events); + } + + if (!empty($page)) + { + $out .= '
'; + $out .= ''; + $out .= ''; + } + + $out .= ''; + + $out .= ''; + + if (!empty($page)) + { + $out .= '
'; + } + } + else + { + dol_print_error($this->db); + } + + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of types of lines (product or service) + * Example: 0=product, 1=service, 9=other (for external module) + * + * @param string $selected Preselected type + * @param string $htmlname Name of field in html form + * @param int $showempty Add an empty field + * @param int $hidetext Do not show label 'Type' before combo box (used only if there is at least 2 choices to select) + * @param integer $forceall 1=Force to show products and services in combo list, whatever are activated modules, 0=No force, 2=Force to show only Products, 3=Force to show only services, -1=Force none (and set hidden field to 'service') + * @return void + */ + public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0) + { + // phpcs:enable + global $db, $langs, $user, $conf; + + // If product & services are enabled or both disabled. + if ($forceall == 1 || (empty($forceall) && !empty($conf->product->enabled) && !empty($conf->service->enabled)) + || (empty($forceall) && empty($conf->product->enabled) && empty($conf->service->enabled))) + { + if (empty($hidetext)) print $langs->trans("Type").': '; + print ''; + //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1); + } + if ((empty($forceall) && empty($conf->product->enabled) && !empty($conf->service->enabled)) || $forceall == 3) + { + print $langs->trans("Service"); + print ''; + } + if ((empty($forceall) && !empty($conf->product->enabled) && empty($conf->service->enabled)) || $forceall == 2) + { + print $langs->trans("Product"); + print ''; + } + if ($forceall < 0) // This should happened only for contracts when both predefined product and service are disabled. + { + print ''; // By default we set on service for contract. If CONTRACT_SUPPORT_PRODUCTS is set, forceall should be 1 not -1 + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Load into cache cache_types_fees, array of types of fees + * + * @return int Nb of lines loaded, <0 if KO + */ + public function load_cache_types_fees() + { + // phpcs:enable + global $langs; + + $num = count($this->cache_types_fees); + if ($num > 0) return 0; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $langs->load("trips"); + + $sql = "SELECT c.code, c.label"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c"; + $sql .= " WHERE active > 0"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label)); + $this->cache_types_fees[$obj->code] = $label; + $i++; + } + + asort($this->cache_types_fees); + + return $num; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of types of notes + * + * @param string $selected Preselected type + * @param string $htmlname Name of field in form + * @param int $showempty Add an empty field + * @return void + */ + public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0) + { + // phpcs:enable + global $user, $langs; + + dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG); + + $this->load_cache_types_fees(); + + print ''; + if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return HTML code to select a company. + * + * @param int $selected Preselected products + * @param string $htmlname Name of HTML select field (must be unique in page) + * @param int $filter Filter on thirdparty + * @param int $limit Limit on number of returned lines + * @param array $ajaxoptions Options for ajax_autocompleter + * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification) + * @return string Return select box for thirdparty. + * @deprecated 3.8 Use select_company instead. For exemple $form->select_thirdparty(GETPOST('socid'),'socid','',0) => $form->select_company(GETPOST('socid'),'socid','',1,0,0,array(),0) + */ + public function select_thirdparty($selected = '', $htmlname = 'socid', $filter = '', $limit = 20, $ajaxoptions = array(), $forcecombo = 0) + { + // phpcs:enable + return $this->select_thirdparty_list($selected, $htmlname, $filter, 1, 0, $forcecombo, array(), '', 0, $limit); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output html form to select a third party + * + * @param string $selected Preselected type + * @param string $htmlname Name of field in form + * @param string $filter Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>] are allowed here (example: 's.rowid <> x', 's.client IN (1,3)') + * @param string $showempty Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty') + * @param int $showtype Show third party type in combolist (customer, prospect or supplier) + * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification) + * @param array $events Ajax event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param int $limit Maximum number of elements + * @param string $morecss Add more css styles to the SELECT component + * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container + * @param string $selected_input_value Value of preselected input text (for use with ajax) + * @param int $hidelabel Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after) + * @param array $ajaxoptions Options for ajax_autocompleter + * @param bool $multiple add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter) + * @return string HTML string with select box for thirdparty. + */ + public function select_company($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $limit = 0, $morecss = 'minwidth100', $moreparam = '', $selected_input_value = '', $hidelabel = 1, $ajaxoptions = array(), $multiple = false) + { + // phpcs:enable + global $conf, $user, $langs; + + $out = ''; + + if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) + { + // No immediate load of all database + $placeholder = ''; + if ($selected && empty($selected_input_value)) + { + require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; + $societetmp = new Societe($this->db); + $societetmp->fetch($selected); + $selected_input_value = $societetmp->name; + unset($societetmp); + } + // mode 1 + $urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&filter='.urlencode($filter).($showtype ? '&showtype='.urlencode($showtype) : ''); + $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); + $out .= ''; + if (empty($hidelabel)) print $langs->trans("RefOrLabel").' : '; + elseif ($hidelabel > 1) { + $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"'; + if ($hidelabel == 2) { + $out .= img_picto($langs->trans("Search"), 'search'); + } + } + $out .= 'global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />'; + if ($hidelabel == 3) { + $out .= img_picto($langs->trans("Search"), 'search'); + } + } + else + { + // Immediate load of all database + $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple); + } + + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output html form to select a third party. + * Note, you must use the select_company to get the component to select a third party. This function must only be called by select_company. + * + * @param string $selected Preselected type + * @param string $htmlname Name of field in form + * @param string $filter Optional filters criteras (example: 's.rowid <> x', 's.client in (1,3)') + * @param string $showempty Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty') + * @param int $showtype Show third party type in combolist (customer, prospect or supplier) + * @param int $forcecombo Force to use standard HTML select component without beautification + * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param string $filterkey Filter on key value + * @param int $outputmode 0=HTML select string, 1=Array + * @param int $limit Limit number of answers + * @param string $morecss Add more css styles to the SELECT component + * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container + * @param bool $multiple add [] in the name of element and add 'multiple' attribut + * @return string HTML string with + */ + public function select_thirdparty_list($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $filterkey = '', $outputmode = 0, $limit = 0, $morecss = 'minwidth100', $moreparam = '', $multiple = false) + { + // phpcs:enable + global $conf, $user, $langs; + + $out = ''; + $num = 0; + $outarray = array(); + + if ($selected === '') $selected = array(); + elseif (!is_array($selected)) $selected = array($selected); + + // Clean $filter that may contains sql conditions so sql code + if (function_exists('testSqlAndScriptInject')) { + if (testSqlAndScriptInject($filter, 3) > 0) { + $filter = ''; + } + } + + // On recherche les societes + $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.client, s.fournisseur, s.code_client, s.code_fournisseur"; + + if ($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST) { + $sql .= ", s.address, s.zip, s.town"; + $sql .= ", dictp.code as country_code"; + } + + $sql .= " FROM ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$user->socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + if ($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST) { + $sql .= " LEFT OUTER JOIN ".MAIN_DB_PREFIX."c_country as dictp ON dictp.rowid=s.fk_pays"; + } + $sql .= " WHERE s.entity IN (".getEntity('societe').")"; + if (!empty($user->socid)) $sql .= " AND s.rowid = ".$user->socid; + if ($filter) $sql .= " AND (".$filter.")"; + if (!$user->rights->societe->client->voir && !$user->socid) $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id; + if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) $sql .= " AND s.status <> 0"; + // Add criteria + if ($filterkey && $filterkey != '') + { + $sql .= " AND ("; + $prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on + // For natural search + $scrit = explode(' ', $filterkey); + $i = 0; + if (count($scrit) > 1) $sql .= "("; + foreach ($scrit as $crit) { + if ($i > 0) $sql .= " AND "; + $sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')"; + $i++; + } + if (count($scrit) > 1) $sql .= ")"; + if (!empty($conf->barcode->enabled)) + { + $sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'"; + } + $sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'"; + $sql .= ")"; + } + $sql .= $this->db->order("nom", "ASC"); + $sql .= $this->db->plimit($limit, 0); + + // Build output string + dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + if (!$forcecombo) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname, $events, $conf->global->COMPANY_USE_SEARCH_TO_SELECT); + } + + // Construct $out and $outarray + $out .= ''."\n"; + } + else + { + dol_print_error($this->db); + } + + $this->result = array('nbofthirdparties'=>$num); + + if ($outputmode) return $outarray; + return $out; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return HTML combo list of absolute discounts + * + * @param string $selected Id remise fixe pre-selectionnee + * @param string $htmlname Nom champ formulaire + * @param string $filter Criteres optionnels de filtre + * @param int $socid Id of thirdparty + * @param int $maxvalue Max value for lines that can be selected + * @return int Return number of qualifed lines in list + */ + public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0) + { + // phpcs:enable + global $langs, $conf; + + // On recherche les remises + $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,"; + $sql .= " re.description, re.fk_facture_source"; + $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re"; + $sql .= " WHERE re.fk_soc = ".(int) $socid; + $sql .= " AND re.entity = ".$conf->entity; + if ($filter) $sql .= " AND ".$filter; + $sql .= " ORDER BY re.description ASC"; + + dol_syslog(get_class($this)."::select_remises", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + print ''; + return $qualifiedlines; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of all contacts (for a third party or all) + * + * @param int $socid Id ot third party or 0 for all + * @param string $selected Id contact pre-selectionne + * @param string $htmlname Name of HTML field ('none' for a not editable field) + * @param int $showempty 0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list + * @param string $exclude List of contacts id to exclude + * @param string $limitto Disable answers that are not id in this array list + * @param integer $showfunction Add function into label + * @param string $moreclass Add more class to class style + * @param integer $showsoc Add company into label + * @param int $forcecombo Force to use combo box + * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param bool $options_only Return options only (for ajax treatment) + * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container + * @param string $htmlid Html id to use instead of htmlname + * @return int <0 if KO, Nb of contact in list if OK + * @deprected You can use selectcontacts directly (warning order of param was changed) + */ + public function select_contacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $showsoc = 0, $forcecombo = 0, $events = array(), $options_only = false, $moreparam = '', $htmlid = '') + { + // phpcs:enable + print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $moreclass, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid); + return $this->num; + } + + /** + * Return HTML code of the SELECT of list of all contacts (for a third party or all). + * This also set the number of contacts found into $this->num + * + * @since 9.0 Add afterSelectContactOptions hook + * + * @param int $socid Id ot third party or 0 for all or -1 for empty list + * @param array|int $selected Array of ID of pre-selected contact id + * @param string $htmlname Name of HTML field ('none' for a not editable field) + * @param int $showempty 0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list + * @param string $exclude List of contacts id to exclude + * @param string $limitto Disable answers that are not id in this array list + * @param integer $showfunction Add function into label + * @param string $moreclass Add more class to class style + * @param bool $options_only Return options only (for ajax treatment) + * @param integer $showsoc Add company into label + * @param int $forcecombo Force to use combo box (so no ajax beautify effect) + * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container + * @param string $htmlid Html id to use instead of htmlname + * @param bool $multiple add [] in the name of element and add 'multiple' attribut + * @return int <0 if KO, Nb of contact in list if OK + */ + public function selectcontacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $options_only = false, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false) + { + global $conf, $langs, $hookmanager, $action; + + $langs->load('companies'); + + if (empty($htmlid)) $htmlid = $htmlname; + + if ($selected === '') $selected = array(); + elseif (!is_array($selected)) $selected = array($selected); + $out = ''; + + if (!is_object($hookmanager)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager = new HookManager($this->db); + } + + // We search third parties + $sql = "SELECT sp.rowid, sp.lastname, sp.statut, sp.firstname, sp.poste"; + if ($showsoc > 0) $sql .= " , s.nom as company"; + $sql .= " FROM ".MAIN_DB_PREFIX."socpeople as sp"; + if ($showsoc > 0) $sql .= " LEFT OUTER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid=sp.fk_soc"; + $sql .= " WHERE sp.entity IN (".getEntity('socpeople').")"; + if ($socid > 0 || $socid == -1) $sql .= " AND sp.fk_soc=".$socid; + if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) $sql .= " AND sp.statut <> 0"; + $sql .= " ORDER BY sp.lastname ASC"; + + dol_syslog(get_class($this)."::select_contacts", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + + if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlid, $events, $conf->global->CONTACT_USE_SEARCH_TO_SELECT); + } + + if ($htmlname != 'none' && !$options_only) $out .= ''; + } + + $this->num = $num; + return $out; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return the HTML select list of users + * + * @param string $selected Id user preselected + * @param string $htmlname Field name in form + * @param int $show_empty 0=liste sans valeur nulle, 1=ajoute valeur inconnue + * @param array $exclude Array list of users id to exclude + * @param int $disabled If select list must be disabled + * @param array|string $include Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me + * @param int $enableonly Array list of users id to be enabled. All other must be disabled + * @param string $force_entity '0' or Ids of environment to force + * @return void + * @deprecated Use select_dolusers instead + * @see select_dolusers() + */ + public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0') + { + // phpcs:enable + print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return select list of users + * + * @param string $selected User id or user object of user preselected. If 0 or < -2, we use id of current user. If -1, keep unselected (if empty is allowed) + * @param string $htmlname Field name in form + * @param int $show_empty 0=list with no empty value, 1=add also an empty value into list + * @param array $exclude Array list of users id to exclude + * @param int $disabled If select list must be disabled + * @param array|string $include Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me + * @param array $enableonly Array list of users id to be enabled. If defined, it means that others will be disabled + * @param string $force_entity '0' or Ids of environment to force + * @param int $maxlength Maximum length of string into list (0=no limit) + * @param int $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status + * @param string $morefilter Add more filters into sql request (Example: 'employee = 1') + * @param integer $show_every 0=default list, 1=add also a value "Everybody" at beginning of list + * @param string $enableonlytext If option $enableonlytext is set, we use this text to explain into label why record is disabled. Not used if enableonly is empty. + * @param string $morecss More css + * @param int $noactive Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on). + * @param int $outputmode 0=HTML select string, 1=Array + * @param bool $multiple add [] in the name of element and add 'multiple' attribut + * @return string HTML select string + * @see select_dolgroups() + */ + public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $noactive = 0, $outputmode = 0, $multiple = false) + { + // phpcs:enable + global $conf, $user, $langs, $hookmanager; + + // If no preselected user defined, we take current user + if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) $selected = $user->id; + + if ($selected === '') $selected = array(); + elseif (!is_array($selected)) $selected = array($selected); + + $excludeUsers = null; + $includeUsers = null; + + // Permettre l'exclusion d'utilisateurs + if (is_array($exclude)) $excludeUsers = implode(",", $exclude); + // Permettre l'inclusion d'utilisateurs + if (is_array($include)) $includeUsers = implode(",", $include); + elseif ($include == 'hierarchy') + { + // Build list includeUsers to have only hierarchy + $includeUsers = implode(",", $user->getAllChildIds(0)); + } + elseif ($include == 'hierarchyme') + { + // Build list includeUsers to have only hierarchy and current user + $includeUsers = implode(",", $user->getAllChildIds(1)); + } + + $out = ''; + $outarray = array(); + + // Forge request to select users + $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut, u.login, u.admin, u.entity"; + if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) + { + $sql .= ", e.label"; + } + $sql .= " FROM ".MAIN_DB_PREFIX."user as u"; + if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) + { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid=u.entity"; + if ($force_entity) $sql .= " WHERE u.entity IN (0,".$force_entity.")"; + else $sql .= " WHERE u.entity IS NOT NULL"; + } + else + { + if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) + { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ug"; + $sql .= " ON ug.fk_user = u.rowid"; + $sql .= " WHERE ug.entity = ".$conf->entity; + } + else + { + $sql .= " WHERE u.entity IN (0,".$conf->entity.")"; + } + } + if (!empty($user->socid)) $sql .= " AND u.fk_soc = ".$user->socid; + if (is_array($exclude) && $excludeUsers) $sql .= " AND u.rowid NOT IN (".$excludeUsers.")"; + if ($includeUsers) $sql .= " AND u.rowid IN (".$includeUsers.")"; + if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $noactive) $sql .= " AND u.statut <> 0"; + if (!empty($morefilter)) $sql .= " ".$morefilter; + + //Add hook to filter on user (for exemple on usergroup define in custom modules) + $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action); + if (!empty($reshook)) $sql .= $hookmanager->resPrint; + + if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname + { + $sql .= " ORDER BY u.firstname ASC"; + } + else + { + $sql .= " ORDER BY u.lastname ASC"; + } + + dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + if ($num) + { + // Enhance with select2 + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname); + + // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined + $out .= ''; + $out .= ''; + } + $out .= ''; + } + else + { + dol_print_error($this->db); + } + + if ($outputmode) return $outarray; + return $out; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return select list of users. Selected users are stored into session. + * List of users are provided into $_SESSION['assignedtouser']. + * + * @param string $action Value for $action + * @param string $htmlname Field name in form + * @param int $show_empty 0=list without the empty value, 1=add empty value + * @param array $exclude Array list of users id to exclude + * @param int $disabled If select list must be disabled + * @param array $include Array list of users id to include or 'hierarchy' to have only supervised users + * @param array $enableonly Array list of users id to be enabled. All other must be disabled + * @param int $force_entity '0' or Ids of environment to force + * @param int $maxlength Maximum length of string into list (0=no limit) + * @param int $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status + * @param string $morefilter Add more filters into sql request + * @param int $showproperties Show properties of each attendees + * @param array $listofuserid Array with properties of each user + * @param array $listofcontactid Array with properties of each contact + * @param array $listofotherid Array with properties of each other contact + * @return string HTML select string + * @see select_dolgroups() + */ + public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array()) + { + // phpcs:enable + global $conf, $user, $langs; + + $userstatic = new User($this->db); + $out = ''; + + // Method with no ajax + //$out.='
'; + if ($action == 'view') + { + $out .= ''; + } + else + { + $out .= ''; + $out .= ''; + $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter); + $out .= ' '; + $out .= '
'; + } + $assignedtouser = array(); + if (!empty($_SESSION['assignedtouser'])) + { + $assignedtouser = json_decode($_SESSION['assignedtouser'], true); + } + $nbassignetouser = count($assignedtouser); + + if ($nbassignetouser && $action != 'view') $out .= '
'; + if ($nbassignetouser) $out .= '
    '; + $i = 0; $ownerid = 0; + foreach ($assignedtouser as $key => $value) + { + if ($value['id'] == $ownerid) continue; + + $out .= '
  • '; + $userstatic->fetch($value['id']); + $out .= $userstatic->getNomUrl(-1); + if ($i == 0) { $ownerid = $value['id']; $out .= ' ('.$langs->trans("Owner").')'; } + if ($nbassignetouser > 1 && $action != 'view') + { + $out .= ' '; + } + // Show my availability + if ($showproperties) + { + if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) + { + $out .= '
    '; + $out .= ' - '.$langs->trans("Availability").': '.$langs->trans("Busy"); + $out .= '
    '; + } + } + //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional")); + //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy")); + + $out .= '
  • '; + $i++; + } + if ($nbassignetouser) $out .= '
'; + + //$out.='
'; + return $out; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of products for customer in Ajax if Ajax activated or go to select_produits_list + * + * @param int $selected Preselected products + * @param string $htmlname Name of HTML select field (must be unique in page) + * @param int $filtertype Filter on product type (''=nofilter, 0=product, 1=service) + * @param int $limit Limit on number of returned lines + * @param int $price_level Level of price to show + * @param int $status Sell status -1=Return all products, 0=Products not on sell, 1=Products on sell + * @param int $finished 2=all, 1=finished, 0=raw material + * @param string $selected_input_value Value of preselected input text (for use with ajax) + * @param int $hidelabel Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after) + * @param array $ajaxoptions Options for ajax_autocompleter + * @param int $socid Thirdparty Id (to get also price dedicated to this customer) + * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. + * @param int $forcecombo Force to use combo box + * @param string $morecss Add more css on select + * @param int $hidepriceinlabel 1=Hide prices in label + * @param string $warehouseStatus Warehouse status filter to count the quantity in stock. Following comma separated filter options can be used + * 'warehouseopen' = count products from open warehouses, + * 'warehouseclosed' = count products from closed warehouses, + * 'warehouseinternal' = count products from warehouses for internal correct/transfer only + * @param array $selected_combinations Selected combinations. Format: array([attrid] => attrval, [...]) + * @return void + */ + public function select_produits($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $status = 1, $finished = 2, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $selected_combinations = array()) + { + // phpcs:enable + global $langs, $conf; + + // check parameters + $price_level = (!empty($price_level) ? $price_level : 0); + if (is_null($ajaxoptions)) $ajaxoptions = array(); + + if (strval($filtertype) === '' && (!empty($conf->product->enabled) || !empty($conf->service->enabled))) { + if (!empty($conf->product->enabled) && empty($conf->service->enabled)) { + $filtertype = '0'; + } + elseif (empty($conf->product->enabled) && !empty($conf->service->enabled)) { + $filtertype = '1'; + } + } + + if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) + { + $placeholder = ''; + + if ($selected && empty($selected_input_value)) + { + require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + $producttmpselect = new Product($this->db); + $producttmpselect->fetch($selected); + $selected_input_value = $producttmpselect->ref; + unset($producttmpselect); + } + // handle case where product or service module is disabled + no filter specified + if ($filtertype == '') + { + if (empty($conf->product->enabled)) { // when product module is disabled, show services only + $filtertype = 1; + } + elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only + $filtertype = 0; + } + } + // mode=1 means customers products + $urloption = 'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus; + //Price by customer + if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) { + $urloption .= '&socid='.$socid; + } + print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); + + if (!empty($conf->variants->enabled)) { + ?> + + trans("RefOrLabel").' : '; + elseif ($hidelabel > 1) { + $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"'; + if ($hidelabel == 2) { + print img_picto($langs->trans("Search"), 'search'); + } + } + print 'global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />'; + if ($hidelabel == 3) { + print img_picto($langs->trans("Search"), 'search'); + } + } + else + { + print $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus); + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of products for a customer + * + * @param int $selected Preselected product + * @param string $htmlname Name of select html + * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service) + * @param int $limit Limit on number of returned lines + * @param int $price_level Level of price to show + * @param string $filterkey Filter on product + * @param int $status -1=Return all products, 0=Products not on sell, 1=Products on sell + * @param int $finished Filter on finished field: 2=No filter + * @param int $outputmode 0=HTML select string, 1=Array + * @param int $socid Thirdparty Id (to get also price dedicated to this customer) + * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. + * @param int $forcecombo Force to use combo box + * @param string $morecss Add more css on select + * @param int $hidepriceinlabel 1=Hide prices in label + * @param string $warehouseStatus Warehouse status filter to group/count stock. Following comma separated filter options can be used. + * 'warehouseopen' = count products from open warehouses, + * 'warehouseclosed' = count products from closed warehouses, + * 'warehouseinternal' = count products from warehouses for internal correct/transfer only + * @return array Array of keys for json + */ + public function select_produits_list($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '') + { + // phpcs:enable + global $langs, $conf, $user, $db; + + $out = ''; + $outarray = array(); + + // Units + if ($conf->global->PRODUCT_USE_UNITS) { + $langs->load('other'); + } + + $warehouseStatusArray = array(); + if (!empty($warehouseStatus)) + { + require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; + if (preg_match('/warehouseclosed/', $warehouseStatus)) + { + $warehouseStatusArray[] = Entrepot::STATUS_CLOSED; + } + if (preg_match('/warehouseopen/', $warehouseStatus)) + { + $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL; + } + if (preg_match('/warehouseinternal/', $warehouseStatus)) + { + $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL; + } + } + + $selectFields = " p.rowid, p.ref, p.label, p.description, p.barcode, p.fk_country, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.duration, p.fk_price_expression"; + if (count($warehouseStatusArray)) + { + $selectFieldsGrouped = ", sum(".$db->ifsql("e.statut IS NULL", "0", "ps.reel").") as stock"; // e.statut is null if there is no record in stock + } + else + { + $selectFieldsGrouped = ", ".$db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock"; + } + + $sql = "SELECT "; + $sql .= $selectFields.$selectFieldsGrouped; + + if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) + { + //Product category + $sql .= ", (SELECT ".MAIN_DB_PREFIX."categorie_product.fk_categorie + FROM ".MAIN_DB_PREFIX."categorie_product + WHERE ".MAIN_DB_PREFIX."categorie_product.fk_product=p.rowid + LIMIT 1 + ) AS categorie_product_id "; + } + + //Price by customer + 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"; + } + // Units + if (!empty($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"; + $selectFields .= ', unit_long, 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'; + } + + // Multilang : we add translation + if (!empty($conf->global->MAIN_MULTILANGS)) + { + $sql .= ", pl.label as label_translated"; + $selectFields .= ", label_translated"; + } + // Price by quantity + if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) + { + $sql .= ", (SELECT pp.rowid FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid"; + if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) $sql .= " AND price_level=".$price_level; + $sql .= " ORDER BY date_price"; + $sql .= " DESC LIMIT 1) as price_rowid"; + $sql .= ", (SELECT pp.price_by_qty FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid"; // price_by_qty is 1 if some prices by qty exists in subtable + if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) $sql .= " AND price_level=".$price_level; + $sql .= " ORDER BY date_price"; + $sql .= " DESC LIMIT 1) as price_by_qty"; + $selectFields .= ", price_rowid, price_by_qty"; + } + $sql .= " FROM ".MAIN_DB_PREFIX."product as p"; + if (count($warehouseStatusArray)) + { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_product = p.rowid"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")"; + $sql .= ' AND e.statut IN ('.$this->db->escape(implode(',', $warehouseStatusArray)).')'; // Return line if product is inside the selected stock. If not, an empty line will be returned so we will count 0. + } + + // include search in supplier ref + if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) + { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product"; + } + + //Price by customer + if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_customer_price as pcp ON pcp.fk_soc=".$socid." AND pcp.fk_product=p.rowid"; + } + // Units + if (!empty($conf->global->PRODUCT_USE_UNITS)) { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit"; + } + // 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='".$langs->getDefaultLang()."'"; + } + + if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac.fk_product_child = p.rowid"; + } + + $sql .= ' WHERE p.entity IN ('.getEntity('product').')'; + + if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) { + $sql .= " AND pac.rowid IS NULL"; + } + + if ($finished == 0) + { + $sql .= " AND p.finished = ".$finished; + } + elseif ($finished == 1) + { + $sql .= " AND p.finished = ".$finished; + if ($status >= 0) $sql .= " AND p.tosell = ".$status; + } + elseif ($status >= 0) + { + $sql .= " AND p.tosell = ".$status; + } + // Filter by product type + if (strval($filtertype) != '') $sql .= " AND p.fk_product_type = ".$filtertype; + elseif (empty($conf->product->enabled)) { // when product module is disabled, show services only + $sql .= " AND p.fk_product_type = 1"; + } + elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only + $sql .= " AND p.fk_product_type = 0"; + } + // Add criteria on ref/label + if ($filterkey != '') + { + $sql .= ' AND ('; + $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on + // For natural search + $scrit = explode(' ', $filterkey); + $i = 0; + if (count($scrit) > 1) $sql .= "("; + foreach ($scrit as $crit) + { + if ($i > 0) $sql .= " AND "; + $sql .= "(p.ref LIKE '".$db->escape($prefix.$crit)."%' OR p.label LIKE '".$db->escape($prefix.$crit)."%'"; + if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " OR pl.label LIKE '".$db->escape($prefix.$crit)."%'"; + if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) + { + $sql .= " OR p.description LIKE '".$db->escape($prefix.$crit)."%'"; + if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " OR pl.description LIKE '".$db->escape($prefix.$crit)."%'"; + } + if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) $sql .= " OR pfp.ref_fourn LIKE '".$db->escape($prefix.$crit)."%'"; + $sql .= ")"; + $i++; + } + if (count($scrit) > 1) $sql .= ")"; + if (!empty($conf->barcode->enabled)) $sql .= " OR p.barcode LIKE '".$db->escape($prefix.$filterkey)."%'"; + $sql .= ')'; + } + if (count($warehouseStatusArray)) + { + $sql .= ' GROUP BY'.$selectFields; + } + + //Sort by category + if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) + { + $sql .= " ORDER BY categorie_product_id "; + //ASC OR DESC order + ($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC"; + } + else + { + $sql .= $db->order("p.ref"); + } + + $sql .= $db->plimit($limit, 0); + + // Build output string + dol_syslog(get_class($this)."::select_produits_list search product", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; + + $num = $this->db->num_rows($result); + + $events = null; + + if (!$forcecombo) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname, $events, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT); + } + + $out .= ''; + + $this->db->free($result); + + if (empty($outputmode)) return $out; + return $outarray; + } + else + { + dol_print_error($db); + } + } + + /** + * constructProductListOption. + * This define value for &$opt and &$optJson. + * + * @param resource $objp Resultset of fetch + * @param string $opt Option (var used for returned value in string option format) + * @param string $optJson Option (var used for returned value in json format) + * @param int $price_level Price level + * @param string $selected Preselected value + * @param int $hidepriceinlabel Hide price in label + * @param string $filterkey Filter key to highlight + * @param int $novirtualstock Do not load virtual stock, even if slow option STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO is on. + * @return void + */ + protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0) + { + global $langs, $conf, $user, $db; + + $outkey = ''; + $outval = ''; + $outref = ''; + $outlabel = ''; + $outdesc = ''; + $outbarcode = ''; + $outorigin = ''; + $outtype = ''; + $outprice_ht = ''; + $outprice_ttc = ''; + $outpricebasetype = ''; + $outtva_tx = ''; + $outqty = 1; + $outdiscount = 0; + + $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO); + + $label = $objp->label; + if (!empty($objp->label_translated)) $label = $objp->label_translated; + if (!empty($filterkey) && $filterkey != '') $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $label, 1); + + $outkey = $objp->rowid; + $outref = $objp->ref; + $outlabel = $objp->label; + $outdesc = $objp->description; + $outbarcode = $objp->barcode; + $outorigin = $objp->fk_country; + $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid; + + $outtype = $objp->fk_product_type; + $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : ''; + $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : ''; + + // Units + $outvalUnits = ''; + if (!empty($conf->global->PRODUCT_USE_UNITS)) { + if (!empty($objp->unit_short)) { + $outvalUnits .= ' - '.$objp->unit_short; + } + } + if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) { + if (!empty($objp->weight) && $objp->weight_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) { + $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units); + $outvalUnits .= ' - '.$unitToShow; + } + if (!empty($objp->surface) && $objp->surface_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if (!empty($objp->volume) && $objp->volume_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + } + if ($outdurationvalue && $outdurationunit) { + $da = array( + 'h' => $langs->trans('Hour'), + 'd' => $langs->trans('Day'), + 'w' => $langs->trans('Week'), + 'm' => $langs->trans('Month'), + 'y' => $langs->trans('Year') + ); + if (isset($da[$outdurationunit])) { + $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : '')); + } + } + + $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); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs_list) + * + * @param int $socid Id third party + * @param string $selected Preselected product + * @param string $htmlname Name of HTML Select + * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service) + * @param string $filtre For a SQL filter + * @param array $ajaxoptions Options for ajax_autocompleter + * @param int $hidelabel Hide label (0=no, 1=yes) + * @param int $alsoproductwithnosupplierprice 1=Add also product without supplier prices + * @param string $morecss More CSS + * @return void + */ + public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '') + { + // phpcs:enable + global $langs, $conf; + global $price_level, $status, $finished; + + $selected_input_value = ''; + if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) + { + if ($selected > 0) + { + require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + $producttmpselect = new Product($this->db); + $producttmpselect->fetch($selected); + $selected_input_value = $producttmpselect->ref; + unset($producttmpselect); + } + + // mode=2 means suppliers products + $urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice; + print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); + print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').''; + } + else + { + print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', -1, 0, 0, $alsoproductwithnosupplierprice, $morecss); + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of suppliers products + * + * @param int $socid Id societe fournisseur (0 pour aucun filtre) + * @param int $selected Product price pre-selected (must be 'id' in product_fournisseur_price or 'idprod_IDPROD') + * @param string $htmlname Nom de la zone select + * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service) + * @param string $filtre Pour filtre sql + * @param string $filterkey Filtre des produits + * @param int $statut -1=Return all products, 0=Products not on sell, 1=Products on sell (not used here, a filter on tobuy is already hard coded in request) + * @param int $outputmode 0=HTML select string, 1=Array + * @param int $limit Limit of line number + * @param int $alsoproductwithnosupplierprice 1=Add also product without supplier prices + * @param string $morecss Add more CSS + * @return array Array of keys for json + */ + public function select_produits_fournisseurs_list($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $filterkey = '', $statut = -1, $outputmode = 0, $limit = 100, $alsoproductwithnosupplierprice = 0, $morecss = '') + { + // phpcs:enable + global $langs, $conf, $db; + + $out = ''; + $outarray = array(); + + $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO); + + $langs->load('stocks'); + // Units + if ($conf->global->PRODUCT_USE_UNITS) { + $langs->load('other'); + } + + $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type,"; + $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"; + // 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"; + } + if (!empty($conf->barcode->enabled)) $sql .= ", pfp.barcode"; + $sql .= " FROM ".MAIN_DB_PREFIX."product as p"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )"; + if ($socid) $sql .= " AND pfp.fk_soc = ".$socid; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid"; + // Units + if ($conf->global->PRODUCT_USE_UNITS) { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit"; + } + $sql .= " WHERE p.entity IN (".getEntity('product').")"; + $sql .= " AND p.tobuy = 1"; + if (strval($filtertype) != '') $sql .= " AND p.fk_product_type=".$this->db->escape($filtertype); + if (!empty($filtre)) $sql .= " ".$filtre; + // Add criteria on ref/label + if ($filterkey != '') + { + $sql .= ' AND ('; + $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on + // For natural search + $scrit = explode(' ', $filterkey); + $i = 0; + if (count($scrit) > 1) $sql .= "("; + 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)."%')"; + $i++; + } + if (count($scrit) > 1) $sql .= ")"; + if (!empty($conf->barcode->enabled)) { + $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'"; + $sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'"; + } + $sql .= ')'; + } + $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC"; + $sql .= $db->plimit($limit, 0); + + // Build output string + + dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; + + $num = $this->db->num_rows($result); + + //$out.=''; + if (!$selected) $out .= ''; + else $out .= ''; + + $i = 0; + while ($i < $num) + { + $objp = $this->db->fetch_object($result); + + $outkey = $objp->idprodfournprice; // id in table of price + if (!$outkey && $alsoproductwithnosupplierprice) $outkey = 'idprod_'.$objp->rowid; // id of product + + $outref = $objp->ref; + $outval = ''; + $outbarcode = $objp->barcode; + $outqty = 1; + $outdiscount = 0; + $outtype = $objp->fk_product_type; + $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : ''; + $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : ''; + + // Units + $outvalUnits = ''; + if ($conf->global->PRODUCT_USE_UNITS) { + if (!empty($objp->unit_short)) { + $outvalUnits .= ' - '.$objp->unit_short; + } + if (!empty($objp->weight) && $objp->weight_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) { + $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units); + $outvalUnits .= ' - '.$unitToShow; + } + if (!empty($objp->surface) && $objp->surface_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if (!empty($objp->volume) && $objp->volume_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if ($outdurationvalue && $outdurationunit) { + $da = array( + 'h' => $langs->trans('Hour'), + 'd' => $langs->trans('Day'), + 'w' => $langs->trans('Week'), + 'm' => $langs->trans('Month'), + 'y' => $langs->trans('Year') + ); + if (isset($da[$outdurationunit])) { + $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : '')); + } + } + } + + $objRef = $objp->ref; + if ($filterkey && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $objRef, 1); + $objRefFourn = $objp->ref_fourn; + if ($filterkey && $filterkey != '') $objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $objRefFourn, 1); + $label = $objp->label; + if ($filterkey && $filterkey != '') $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $label, 1); + + $optlabel = $objp->ref; + if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) { + $optlabel .= ' ('.$objp->ref_fourn.')'; + } + if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) { + $optlabel .= ' ('.$outbarcode.')'; + } + $optlabel .= ' - '.dol_trunc($label, $maxlengtharticle); + + $outvallabel = $objRef; + if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) { + $outvallabel .= ' ('.$objRefFourn.')'; + } + if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) { + $outvallabel .= ' ('.$outbarcode.')'; + } + $outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle); + + // Units + $optlabel .= $outvalUnits; + $outvallabel .= $outvalUnits; + + if (!empty($objp->idprodfournprice)) + { + $outqty = $objp->quantity; + $outdiscount = $objp->remise_percent; + if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) { + $prod_supplier = new ProductFournisseur($this->db); + $prod_supplier->product_fourn_price_id = $objp->idprodfournprice; + $prod_supplier->id = $objp->fk_product; + $prod_supplier->fourn_qty = $objp->quantity; + $prod_supplier->fourn_tva_tx = $objp->tva_tx; + $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression; + $priceparser = new PriceParser($this->db); + $price_result = $priceparser->parseProductSupplier($prod_supplier); + if ($price_result >= 0) { + $objp->fprice = $price_result; + if ($objp->quantity >= 1) + { + $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice + } + } + } + if ($objp->quantity == 1) + { + $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/"; + $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/"; + $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding + $outvallabel .= $langs->transnoentities("Unit"); + } + else + { + $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity; + $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity; + $optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding + $outvallabel .= ' '.$langs->transnoentities("Units"); + } + + if ($objp->quantity > 1) + { + $optlabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding + $outvallabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding + } + if ($objp->remise_percent >= 1) + { + $optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %'; + $outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %'; + } + if ($objp->duration) + { + $optlabel .= " - ".$objp->duration; + $outvallabel .= " - ".$objp->duration; + } + if (!$socid) + { + $optlabel .= " - ".dol_trunc($objp->name, 8); + $outvallabel .= " - ".dol_trunc($objp->name, 8); + } + if ($objp->supplier_reputation) + { + //TODO dictionary + $reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier')); + + $optlabel .= " - ".$reputations[$objp->supplier_reputation]; + $outvallabel .= " - ".$reputations[$objp->supplier_reputation]; + } + } + else + { + if (empty($alsoproductwithnosupplierprice)) // No supplier price defined for couple product/supplier + { + $optlabel .= " - ".$langs->trans("NoPriceDefinedForThisSupplier").''; + $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier"); + } + else // No supplier price defined for product, even on other suppliers + { + $optlabel .= " - ".$langs->trans("NoPriceDefinedForThisSupplier").''; + $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier"); + } + } + + $opt = '\n"; + + + // Add new entry + // "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))); + // 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é)" + // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false) + //} + //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval)); + //$outval=array('label'=>'ppp (fff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)'); + //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval)); + + $i++; + } + $out .= ''; + + $this->db->free($result); + + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname); + + if (empty($outputmode)) return $out; + return $outarray; + } + else + { + dol_print_error($this->db); + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of suppliers prices for a product + * + * @param int $productid Id of product + * @param string $htmlname Name of HTML field + * @param int $selected_supplier Pre-selected supplier if more than 1 result + * @return string + */ + public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '') + { + // phpcs:enable + global $langs, $conf; + + $langs->load('stocks'); + + $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,"; + $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,"; + $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name"; + $sql .= " FROM ".MAIN_DB_PREFIX."product as p"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid"; + $sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")"; + $sql .= " AND p.tobuy = 1"; + $sql .= " AND s.fournisseur = 1"; + $sql .= " AND p.rowid = ".$productid; + $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC"; + + dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG); + $result = $this->db->query($sql); + + if ($result) + { + $num = $this->db->num_rows($result); + + $form = ''; + $this->db->free($result); + return $form; + } + else + { + dol_print_error($this->db); + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of delivery address + * + * @param string $selected Id contact pre-selectionn + * @param int $socid Id of company + * @param string $htmlname Name of HTML field + * @param int $showempty Add an empty field + * @return integer|null + */ + public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0) + { + // phpcs:enable + // looking for users + $sql = "SELECT a.rowid, a.label"; + $sql .= " FROM ".MAIN_DB_PREFIX."societe_address as a"; + $sql .= " WHERE a.fk_soc = ".$socid; + $sql .= " ORDER BY a.label ASC"; + + dol_syslog(get_class($this)."::select_address", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + print ''; + return $num; + } + else + { + dol_print_error($this->db); + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Load into cache list of payment terms + * + * @return int Nb of lines loaded, <0 if KO + */ + public function load_cache_conditions_paiements() + { + // phpcs:enable + global $langs; + + $num = count($this->cache_conditions_paiements); + if ($num > 0) return 0; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $sql = "SELECT rowid, code, libelle as label"; + $sql .= " FROM ".MAIN_DB_PREFIX.'c_payment_term'; + $sql .= " WHERE entity IN (".getEntity('c_payment_term').")"; + $sql .= " AND active > 0"; + $sql .= " ORDER BY sortorder"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : '')); + $this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code; + $this->cache_conditions_paiements[$obj->rowid]['label'] = $label; + $i++; + } + + //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table + + return $num; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Charge dans cache la liste des délais de livraison possibles + * + * @return int Nb of lines loaded, <0 if KO + */ + public function load_cache_availability() + { + // phpcs:enable + global $langs; + + $num = count($this->cache_availability); + if ($num > 0) return 0; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $langs->load('propal'); + + $sql = "SELECT rowid, code, label"; + $sql .= " FROM ".MAIN_DB_PREFIX.'c_availability'; + $sql .= " WHERE active > 0"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $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; + $i++; + } + + $this->cache_availability = dol_sort_array($this->cache_availability, 'label', 'asc', 0, 0, 1); + + return $num; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + /** + * Retourne la liste des types de delais de livraison possibles + * + * @param int $selected Id du type de delais pre-selectionne + * @param string $htmlname Nom de la zone select + * @param string $filtertype To add a filter + * @param int $addempty Add empty entry + * @return void + */ + public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0) + { + global $langs, $user; + + $this->load_cache_availability(); + + dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG); + + print ''; + if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + /** + * Load into cache cache_demand_reason, array of input reasons + * + * @return int Nb of lines loaded, <0 if KO + */ + public function loadCacheInputReason() + { + global $langs; + + $num = count($this->cache_demand_reason); + if ($num > 0) return 0; // Cache already loaded + + $sql = "SELECT rowid, code, label"; + $sql .= " FROM ".MAIN_DB_PREFIX.'c_input_reason'; + $sql .= " WHERE active > 0"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + $tmparray = array(); + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($obj->label != '-' ? $obj->label : ''); + if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) $label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work + if ($langs->trans($obj->code) != $obj->code) $label = $langs->trans($obj->code); // So translation key SRC_XXX will work + + $tmparray[$obj->rowid]['id'] = $obj->rowid; + $tmparray[$obj->rowid]['code'] = $obj->code; + $tmparray[$obj->rowid]['label'] = $label; + $i++; + } + + $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1); + + unset($tmparray); + return $num; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + /** + * Return list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...) + * List found into table c_input_reason loaded by loadCacheInputReason + * + * @param int $selected Id or code of type origin to select by default + * @param string $htmlname Nom de la zone select + * @param string $exclude To exclude a code value (Example: SRC_PROP) + * @param int $addempty Add an empty entry + * @return void + */ + public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0) + { + global $langs, $user; + + $this->loadCacheInputReason(); + + print ''; + if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Charge dans cache la liste des types de paiements possibles + * + * @return int Nb of lines loaded, <0 if KO + */ + public function load_cache_types_paiements() + { + // phpcs:enable + global $langs; + + $num = count($this->cache_types_paiements); + if ($num > 0) return $num; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $this->cache_types_paiements = array(); + + $sql = "SELECT id, code, libelle as label, type, active"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_paiement"; + $sql .= " WHERE entity IN (".getEntity('c_paiement').")"; + //if ($active >= 0) $sql.= " AND active = ".$active; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : '')); + $this->cache_types_paiements[$obj->id]['id'] = $obj->id; + $this->cache_types_paiements[$obj->id]['code'] = $obj->code; + $this->cache_types_paiements[$obj->id]['label'] = $label; + $this->cache_types_paiements[$obj->id]['type'] = $obj->type; + $this->cache_types_paiements[$obj->id]['active'] = $obj->active; + $i++; + } + + $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1); + + return $num; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of payment modes. + * Constant MAIN_DEFAULT_PAYMENT_TERM_ID can used to set default value but scope is all application, probably not what you want. + * See instead to force the default value by the caller. + * + * @param int $selected Id of payment term to preselect by default + * @param string $htmlname Nom de la zone select + * @param int $filtertype Not used + * @param int $addempty Add an empty entry + * @param int $noinfoadmin 0=Add admin info, 1=Disable admin info + * @param string $morecss Add more CSS on select tag + * @return void + */ + public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '') + { + // phpcs:enable + global $langs, $user, $conf; + + dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG); + + $this->load_cache_conditions_paiements(); + + // Set default value if not already set by caller + if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID; + + print ''; + if ($user->admin && empty($noinfoadmin)) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of payment methods + * Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value but scope is all application, probably not what you want. + * + * @param string $selected Id or code or preselected payment mode + * @param string $htmlname Name of select field + * @param string $filtertype To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz)) + * @param int $format 0=id+label, 1=code+code, 2=code+label, 3=id+code + * @param int $empty 1=can be empty, 0 otherwise + * @param int $noadmininfo 0=Add admin info, 1=Disable admin info + * @param int $maxlength Max length of label + * @param int $active Active or not, -1 = all + * @param string $morecss Add more CSS on select tag + * @return void + */ + public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '') + { + // phpcs:enable + global $langs, $user, $conf; + + dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG); + + $filterarray = array(); + if ($filtertype == 'CRDT') $filterarray = array(0, 2, 3); + elseif ($filtertype == 'DBIT') $filterarray = array(1, 2, 3); + elseif ($filtertype != '' && $filtertype != '-1') $filterarray = explode(',', $filtertype); + + $this->load_cache_types_paiements(); + + // Set default value if not already set by caller + if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID; + + print ''; + if ($user->admin && !$noadmininfo) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + + /** + * Selection HT or TTC + * + * @param string $selected Id pre-selectionne + * @param string $htmlname Nom de la zone select + * @return string Code of HTML select to chose tax or not + */ + public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type') + { + global $langs; + + $return = ''; + + $return .= ''; + + return $return; + } + + /** + * Return a HTML select list of shipping mode + * + * @param string $selected Id shipping mode pre-selected + * @param string $htmlname Name of select zone + * @param string $filtre To filter list + * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @param string $moreattrib To add more attribute on select + * @return void + */ + public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '') + { + global $langs, $conf, $user; + + $langs->load("admin"); + $langs->load("deliveries"); + + $sql = "SELECT rowid, code, libelle as label"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode"; + $sql .= " WHERE active > 0"; + if ($filtre) $sql .= " AND ".$filtre; + $sql .= " ORDER BY libelle ASC"; + + dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) { + $num = $this->db->num_rows($result); + $i = 0; + if ($num) { + print '"; + if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } else { + print $langs->trans("NoShippingMethodDefined"); + } + } else { + dol_print_error($this->db); + } + } + + /** + * Display form to select shipping mode + * + * @param string $page Page + * @param int $selected Id of shipping mode + * @param string $htmlname Name of select html field + * @param int $addempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @return void + */ + public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0) + { + global $langs, $db; + + $langs->load("deliveries"); + + if ($htmlname != "none") { + print '
'; + print ''; + print ''; + $this->selectShippingMethod($selected, $htmlname, '', $addempty); + print ''; + print '
'; + } else { + if ($selected) { + $code = $langs->getLabelFromKey($db, $selected, 'c_shipment_mode', 'rowid', 'code'); + print $langs->trans("SendingMethod".strtoupper($code)); + } else { + print " "; + } + } + } + + /** + * Creates HTML last in cycle situation invoices selector + * + * @param string $selected Preselected ID + * @param int $socid Company ID + * + * @return string HTML select + */ + public function selectSituationInvoices($selected = '', $socid = 0) + { + global $langs; + + $langs->load('bills'); + + $opt = ''; + $sql = 'SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'facture'; + $sql .= ' WHERE entity IN ('.getEntity('invoice').')'; + $sql .= ' AND situation_counter>=1'; + $sql .= ' ORDER by situation_cycle_ref, situation_counter desc'; + $resql = $this->db->query($sql); + if ($resql && $this->db->num_rows($resql) > 0) { + // Last seen cycle + $ref = 0; + while ($obj = $this->db->fetch_object($resql)) { + //Same company ? + if ($socid == $obj->fk_soc) { + //Same cycle ? + if ($obj->situation_cycle_ref != $ref) { + // Just seen this cycle + $ref = $obj->situation_cycle_ref; + //not final ? + if ($obj->situation_final != 1) { + //Not prov? + if (substr($obj->ref, 1, 4) != 'PROV') { + if ($selected == $obj->rowid) { + $opt .= ''; + } else { + $opt .= ''; + } + } + } + } + } + } + } + else + { + dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR); + } + if ($opt == '') + { + $opt = ''; + } + return $opt; + } + + /** + * Creates HTML units selector (code => label) + * + * @param string $selected Preselected Unit ID + * @param string $htmlname Select name + * @param int $showempty Add a nempty line + * @return string HTML select + */ + public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0) + { + global $langs; + + $langs->load('products'); + + $return = ''; + } + return $return; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return a HTML select list of bank accounts + * + * @param string $selected Id account pre-selected + * @param string $htmlname Name of select zone + * @param int $status Status of searched accounts (0=open, 1=closed, 2=both) + * @param string $filtre To filter list + * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @param string $moreattrib To add more attribute on select + * @param int $showcurrency Show currency in label + * @param string $morecss More CSS + * @return int <0 if error, Num of bank account found if OK (0, 1, 2, ...) + */ + public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '') + { + // phpcs:enable + global $langs, $conf; + + $langs->load("admin"); + $num = 0; + + $sql = "SELECT rowid, label, bank, clos as status, currency_code"; + $sql .= " FROM ".MAIN_DB_PREFIX."bank_account"; + $sql .= " WHERE entity IN (".getEntity('bank_account').")"; + if ($status != 2) $sql .= " AND clos = ".(int) $status; + if ($filtre) $sql .= " AND ".$filtre; + $sql .= " ORDER BY label"; + + dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + $num = $this->db->num_rows($result); + $i = 0; + if ($num) + { + print '"; + } + else + { + if ($status == 0) print ''.$langs->trans("NoActiveBankAccountDefined").''; + else print ''.$langs->trans("NoBankAccountFound").''; + } + } + else { + dol_print_error($this->db); + } + + return $num; + } + + /** + * Return a HTML select list of establishment + * + * @param string $selected Id establishment pre-selected + * @param string $htmlname Name of select zone + * @param int $status Status of searched establishment (0=open, 1=closed, 2=both) + * @param string $filtre To filter list + * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @param string $moreattrib To add more attribute on select + * @return int <0 if error, Num of establishment found if OK (0, 1, 2, ...) + */ + public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '') + { + // phpcs:enable + global $langs, $conf; + + $langs->load("admin"); + $num = 0; + + $sql = "SELECT rowid, name, fk_country, status, entity"; + $sql .= " FROM ".MAIN_DB_PREFIX."establishment"; + $sql .= " WHERE 1=1"; + if ($status != 2) $sql .= " AND status = ".(int) $status; + if ($filtre) $sql .= " AND ".$filtre; + $sql .= " ORDER BY name"; + + dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + $num = $this->db->num_rows($result); + $i = 0; + if ($num) + { + print '"; + } + else + { + if ($status == 0) print ''.$langs->trans("NoActiveEstablishmentDefined").''; + else print ''.$langs->trans("NoEstablishmentFound").''; + } + } + else { + dol_print_error($this->db); + } + } + + /** + * Display form to select bank account + * + * @param string $page Page + * @param int $selected Id of bank account + * @param string $htmlname Name of select html field + * @param int $addempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @return void + */ + public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0) + { + global $langs; + if ($htmlname != "none") { + print '
'; + print ''; + print ''; + $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty); + if ($nbaccountfound > 0) print ''; + print '
'; + } else { + $langs->load('banks'); + + if ($selected) { + require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; + $bankstatic = new Account($this->db); + $result = $bankstatic->fetch($selected); + if ($result) print $bankstatic->getNomUrl(1); + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of categories having choosed type + * + * @param string|int $type Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated. + * @param string $selected Id of category preselected or 'auto' (autoselect category if there is only one element) + * @param string $htmlname HTML field name + * @param int $maxlength Maximum length for labels + * @param int|string|array $markafterid Keep only or removed all categories including the leaf $markafterid in category tree (exclude) or Keep only of category is inside the leaf starting with this id. + * $markafterid can be an : + * - int (id of category) + * - string (categories ids seprated by comma) + * - array (list of categories ids) + * @param int $outputmode 0=HTML select string, 1=Array + * @param int $include [=0] Removed or 1=Keep only + * @param string $morecss More CSS + * @return string + * @see select_categories() + */ + public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '') + { + // phpcs:enable + global $conf, $langs; + $langs->load("categories"); + + include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + + // For backward compatibility + if (is_numeric($type)) + { + dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING); + } + + if ($type === Categorie::TYPE_BANK_LINE) + { + // TODO Move this into common category feature + $cate_arbo = array(); + $sql = "SELECT c.label, c.rowid"; + $sql .= " FROM ".MAIN_DB_PREFIX."bank_categ as c"; + $sql .= " WHERE entity = ".$conf->entity; + $sql .= " ORDER BY c.label"; + $result = $this->db->query($sql); + if ($result) + { + $num = $this->db->num_rows($result); + $i = 0; + while ($i < $num) + { + $objp = $this->db->fetch_object($result); + if ($objp) $cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label); + $i++; + } + $this->db->free($result); + } + else dol_print_error($this->db); + } + else + { + $cat = new Categorie($this->db); + $cate_arbo = $cat->get_full_arbo($type, $markafterid, $include); + } + + $output = ''; + $output .= "\n"; + + if ($outputmode) return $outarray; + return $output; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a confirmation HTML form or AJAX popup + * + * @param string $page Url of page to call if confirmation is OK + * @param string $title Title + * @param string $question Question + * @param string $action Action + * @param array $formquestion An array with forms complementary inputs + * @param string $selectedchoice "" or "no" or "yes" + * @param int $useajax 0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=preoutput confirm box with div id=dialog-confirm-xxx + * @param int $height Force height of box + * @param int $width Force width of box + * @return void + * @deprecated + * @see formconfirm() + */ + public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500) + { + // phpcs:enable + dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING); + print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width); + } + + /** + * Show a confirmation HTML form or AJAX popup. + * Easiest way to use this is with useajax=1. + * If you use useajax='xxx', you must also add jquery code to trigger opening of box (with correct parameters) + * just after calling this method. For example: + * print ''."\n"; + * + * @param string $page Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated) + * @param string $title Title + * @param string $question Question + * @param string $action Action + * @param array $formquestion An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , )) + * type can be 'hidden', 'text', 'password', 'checkbox', 'radio', 'date', 'morecss', ... + * @param string $selectedchoice '' or 'no', or 'yes' or '1' or '0' + * @param int|string $useajax 0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=Yes and preoutput confirm box with div id=dialog-confirm-xxx + * @param int $height Force height of box (0 = auto) + * @param int $width Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones. + * @param int $disableformtag 1=Disable form tag. Can be used if we are already inside a
section. + * @return string HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form + */ + public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0) + { + global $langs, $conf; + global $useglobalvars; + + $more = ''; + $formconfirm = ''; + $inputok = array(); + $inputko = array(); + + // Clean parameters + $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice; + if ($conf->browser->layout == 'phone') $width = '95%'; + + // Set height automatically if not defined + if (empty($height)) { + $height = 210; + if (is_array($formquestion) && count($formquestion) > 2) { + $height += ((count($formquestion) - 2) * 24); + } + } + + if (is_array($formquestion) && !empty($formquestion)) + { + // First add hidden fields and value + foreach ($formquestion as $key => $input) + { + if (is_array($input) && !empty($input)) + { + if ($input['type'] == 'hidden') + { + $more .= ''."\n"; + } + } + } + + // Now add questions + $moreonecolumn = ''; + $more .= '
'."\n"; + foreach ($formquestion as $key => $input) + { + if (is_array($input) && !empty($input)) + { + $size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : ''); + $moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : ''); + $morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : ''); + + if ($input['type'] == 'text') + { + $more .= '
'.$input['label'].'
'."\n"; + } + elseif ($input['type'] == 'password') + { + $more .= '
'.$input['label'].'
'."\n"; + } + elseif ($input['type'] == 'select') + { + $more .= '
'; + if (!empty($input['label'])) $more .= $input['label'].'
'; + $more .= $this->selectarray($input['name'], $input['values'], $input['default'], 1, 0, 0, $moreattr, 0, 0, 0, '', $morecss); + $more .= '
'."\n"; + } + elseif ($input['type'] == 'checkbox') + { + $more .= '
'; + $more .= '
'.$input['label'].'
'; + $more .= ' $selval) + { + $more .= '
'; + if ($i == 0) $more .= '
'.$input['label'].'
'; + else $more .= '
 
'; + $more .= '
'.$input['label'].'
'; + $more .= '
'; + $more .= $this->selectDate($input['value'], $input['name'], 0, 0, 0, '', 1, 0); + $more .= '
'."\n"; + $formquestion[] = array('name'=>$input['name'].'day'); + $formquestion[] = array('name'=>$input['name'].'month'); + $formquestion[] = array('name'=>$input['name'].'year'); + $formquestion[] = array('name'=>$input['name'].'hour'); + $formquestion[] = array('name'=>$input['name'].'min'); + } + elseif ($input['type'] == 'other') + { + $more .= '
'; + if (!empty($input['label'])) $more .= $input['label'].'
'; + $more .= $input['value']; + $more .= '
'."\n"; + } + + elseif ($input['type'] == 'onecolumn') + { + $moreonecolumn .= '
'; + $moreonecolumn .= $input['value']; + $moreonecolumn .= '
'."\n"; + } + } + } + $more .= '
'."\n"; + $more .= $moreonecolumn; + } + + // JQUI method dialog is broken with jmobile, we use standard HTML. + // Note: When using dol_use_jmobile or no js, you must also check code for button use a GET url with action=xxx and check that you also output the confirm code when action=xxx + // See page product/card.php for example + if (!empty($conf->dol_use_jmobile)) $useajax = 0; + if (empty($conf->use_javascript_ajax)) $useajax = 0; + + if ($useajax) + { + $autoOpen = true; + $dialogconfirm = 'dialog-confirm'; + $button = ''; + if (!is_numeric($useajax)) + { + $button = $useajax; + $useajax = 1; + $autoOpen = false; + $dialogconfirm .= '-'.$button; + } + $pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.$action.'&confirm=yes'; + $pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'confirm=no' : ''); + // Add input fields into list of fields to read during submit (inputok and inputko) + if (is_array($formquestion)) + { + foreach ($formquestion as $key => $input) + { + //print "xx ".$key." rr ".is_array($input)."
\n"; + if (is_array($input) && isset($input['name'])) array_push($inputok, $input['name']); + if (isset($input['inputko']) && $input['inputko'] == 1) array_push($inputko, $input['name']); + } + } + // Show JQuery confirm box. Note that global var $useglobalvars is used inside this template + $formconfirm .= ''."\n"; + + $formconfirm .= "\n\n"; + $formconfirm .= ''; + $formconfirm .= "\n"; + } + else + { + $formconfirm .= "\n\n"; + + if (empty($disableformtag)) $formconfirm .= ''."\n"; + + $formconfirm .= ''."\n"; + $formconfirm .= ''."\n"; + + $formconfirm .= ''."\n"; + + // Line title + $formconfirm .= ''."\n"; + + // Line text + if (!empty($formquestion['text'])) { + $formconfirm .= ''."\n"; + } + + // Line form fields + if ($more) + { + $formconfirm .= ''."\n"; + } + + // Line with question + $formconfirm .= ''; + $formconfirm .= ''; + $formconfirm .= ''; + $formconfirm .= ''; + $formconfirm .= ''."\n"; + + $formconfirm .= '
'.img_picto('', 'recent').' '.$title.'
'.$formquestion['text'].'
'."\n"; + $formconfirm .= $more; + $formconfirm .= '
'.$question.''; + $formconfirm .= $this->selectyesno("confirm", $newselectedchoice); + $formconfirm .= '
'."\n"; + + if (empty($disableformtag)) $formconfirm .= "\n"; + $formconfirm .= '
'; + + $formconfirm .= "\n"; + } + + return $formconfirm; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a form to select a project + * + * @param int $page Page + * @param int $socid Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id) + * @param int $selected Id pre-selected project + * @param string $htmlname Name of select field + * @param int $discard_closed Discard closed projects (0=Keep,1=hide completely except $selected,2=Disable) + * @param int $maxlength Max length + * @param int $forcefocus Force focus on field (works with javascript only) + * @param int $nooutput No print is done. String is returned. + * @return string Return html content + */ + public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0) + { + // phpcs:enable + global $langs; + + require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php'; + + $out = ''; + + $formproject = new FormProjets($this->db); + + $langs->load("project"); + if ($htmlname != "none") + { + $out .= "\n"; + $out .= '
'; + $out .= ''; + $out .= ''; + $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1); + $out .= ''; + $out .= '
'; + } + else + { + if ($selected) + { + $projet = new Project($this->db); + $projet->fetch($selected); + //print ''.$projet->title.''; + $out .= $projet->getNomUrl(0, '', 1); + } + else + { + $out .= " "; + } + } + + if (empty($nooutput)) + { + print $out; + return ''; + } + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a form to select payment conditions + * + * @param int $page Page + * @param string $selected Id condition pre-selectionne + * @param string $htmlname Name of select html field + * @param int $addempty Add empty entry + * @return void + */ + public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0) + { + // phpcs:enable + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + $this->select_conditions_paiements($selected, $htmlname, -1, $addempty); + print ''; + print '
'; + } + else + { + if ($selected) + { + $this->load_cache_conditions_paiements(); + print $this->cache_conditions_paiements[$selected]['label']; + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a form to select a delivery delay + * + * @param int $page Page + * @param string $selected Id condition pre-selectionne + * @param string $htmlname Name of select html field + * @param int $addempty Ajoute entree vide + * @return void + */ + public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0) + { + // phpcs:enable + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + $this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty); + print ''; + print '
'; + } + else + { + if ($selected) + { + $this->load_cache_availability(); + print $this->cache_availability[$selected]['label']; + } else { + print " "; + } + } + } + + /** + * Output HTML form to select list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...) + * List found into table c_input_reason loaded by loadCacheInputReason + * + * @param string $page Page + * @param string $selected Id condition pre-selectionne + * @param string $htmlname Name of select html field + * @param int $addempty Add empty entry + * @return void + */ + public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0) + { + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + $this->selectInputReason($selected, $htmlname, -1, $addempty); + print ''; + print '
'; + } + else + { + if ($selected) + { + $this->loadCacheInputReason(); + foreach ($this->cache_demand_reason as $key => $val) + { + if ($val['id'] == $selected) + { + print $val['label']; + break; + } + } + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a form + html select a date + * + * @param string $page Page + * @param string $selected Date preselected + * @param string $htmlname Html name of date input fields or 'none' + * @param int $displayhour Display hour selector + * @param int $displaymin Display minutes selector + * @param int $nooutput 1=No print output, return string + * @return string + * @see selectDate() + */ + public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0) + { + // phpcs:enable + global $langs; + + $ret = ''; + + if ($htmlname != "none") + { + $ret .= '
'; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= '
'; + $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0); + $ret .= '
'; + } + else + { + if ($displayhour) $ret .= dol_print_date($selected, 'dayhour'); + else $ret .= dol_print_date($selected, 'day'); + } + + if (empty($nooutput)) print $ret; + return $ret; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a select form to choose a user + * + * @param string $page Page + * @param string $selected Id of user preselected + * @param string $htmlname Name of input html field. If 'none', we just output the user link. + * @param array $exclude List of users id to exclude + * @param array $include List of users id to include + * @return void + */ + public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '') + { + // phpcs:enable + global $langs; + + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include); + print ''; + print '
'; + } + else + { + if ($selected) + { + require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; + $theuser = new User($this->db); + $theuser->fetch($selected); + print $theuser->getNomUrl(1); + } else { + print " "; + } + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show form with payment mode + * + * @param string $page Page + * @param int $selected Id mode pre-selectionne + * @param string $htmlname Name of select html field + * @param string $filtertype To filter on field type in llx_c_paiement (array('code'=>xx,'label'=>zz)) + * @param int $active Active or not, -1 = all + * @param int $addempty 1=Add empty entry + * @return void + */ + public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0) + { + // phpcs:enable + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active); + print ''; + print '
'; + } + else + { + if ($selected) + { + $this->load_cache_types_paiements(); + print $this->cache_types_paiements[$selected]['label']; + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show form with multicurrency code + * + * @param string $page Page + * @param string $selected code pre-selectionne + * @param string $htmlname Name of select html field + * @return void + */ + public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code') + { + // phpcs:enable + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print $this->selectMultiCurrency($selected, $htmlname, 0); + print ''; + print '
'; + } + else + { + dol_include_once('/core/lib/company.lib.php'); + print !empty($selected) ? currency_name($selected, 1) : ' '; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show form with multicurrency rate + * + * @param string $page Page + * @param double $rate Current rate + * @param string $htmlname Name of select html field + * @param string $currency Currency code to explain the rate + * @return void + */ + public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '') + { + // phpcs:enable + global $langs, $mysoc, $conf; + + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print ' '; + print ' '; + print ''; + print '
'; + } + else + { + if (!empty($rate)) + { + print price($rate, 1, $langs, 1, 0); + if ($currency && $rate != 1) print '   ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')'; + } + else + { + print 1; + } + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a select box with available absolute discounts + * + * @param string $page Page URL where form is shown + * @param int $selected Value pre-selected + * @param string $htmlname Name of SELECT component. If 'none', not changeable. Example 'remise_id'. + * @param int $socid Third party id + * @param float $amount Total amount available + * @param string $filter SQL filter on discounts + * @param int $maxvalue Max value for lines that can be selected + * @param string $more More string to add + * @param int $hidelist 1=Hide list + * @param int $discount_type 0 => customer discount, 1 => supplier discount + * @return void + */ + public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0) + { + // phpcs:enable + global $conf, $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print '
'; + if (!empty($discount_type)) { + if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) + { + if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice + else $translationKey = 'HasCreditNoteFromSupplier'; + } + else + { + if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") $translationKey = 'HasAbsoluteDiscountFromSupplier'; + else $translationKey = 'HasCreditNoteFromSupplier'; + } + } else { + if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) + { + if (!$filter || $filter == "fk_facture_source IS NULL") $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice + else $translationKey = 'CompanyHasCreditNote'; + } + else + { + if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") $translationKey = 'CompanyHasAbsoluteDiscount'; + else $translationKey = 'CompanyHasCreditNote'; + } + } + print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency)); + if (empty($hidelist)) print ': '; + print '
'; + if (empty($hidelist)) + { + print '
'; + $newfilter = 'discount_type='.intval($discount_type); + if (!empty($discount_type)) { + $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available + } else { + $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available + } + if ($filter) $newfilter .= ' AND ('.$filter.')'; + $nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue); + if ($nbqualifiedlines > 0) + { + print '   '; + } + print '
'; + } + if ($more) + { + print '
'; + print $more; + print '
'; + } + print '
'; + } + else + { + if ($selected) + { + print $selected; + } + else + { + print "0"; + } + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show forms to select a contact + * + * @param string $page Page + * @param Societe $societe Filter on third party + * @param int $selected Id contact pre-selectionne + * @param string $htmlname Name of HTML select. If 'none', we just show contact link. + * @return void + */ + public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid') + { + // phpcs:enable + global $langs, $conf; + + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print ''; + print ''; + print ''; + print '
'; + $num = $this->select_contacts($societe->id, $selected, $htmlname); + if ($num == 0) + { + $addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress")); + print ''.$addcontact.''; + } + print '
'; + } + else + { + if ($selected) + { + require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; + $contact = new Contact($this->db); + $contact->fetch($selected); + print $contact->getFullName($langs); + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output html select to select thirdparty + * + * @param string $page Page + * @param string $selected Id preselected + * @param string $htmlname Name of HTML select + * @param string $filter optional filters criteras + * @param int $showempty Add an empty field + * @param int $showtype Show third party type in combolist (customer, prospect or supplier) + * @param int $forcecombo Force to use combo box + * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param int $nooutput No print output. Return it only. + * @return void|string + */ + public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0) + { + // phpcs:enable + global $langs; + + $out = ''; + if ($htmlname != "none") + { + $out .= '
'; + $out .= ''; + $out .= ''; + $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events); + $out .= ''; + $out .= '
'; + } + else + { + if ($selected) + { + require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; + $soc = new Societe($this->db); + $soc->fetch($selected); + $out .= $soc->getNomUrl($langs); + } + else + { + $out .= " "; + } + } + + if ($nooutput) return $out; + else print $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Retourne la liste des devises, dans la langue de l'utilisateur + * + * @param string $selected preselected currency code + * @param string $htmlname name of HTML select list + * @deprecated + * @return void + */ + public function select_currency($selected = '', $htmlname = 'currency_id') + { + // phpcs:enable + print $this->selectCurrency($selected, $htmlname); + } + + /** + * Retourne la liste des devises, dans la langue de l'utilisateur + * + * @param string $selected preselected currency code + * @param string $htmlname name of HTML select list + * @param string $mode 0 = Add currency symbol into label, 1 = Add 3 letter iso code + * @return string + */ + public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0) + { + global $conf, $langs, $user; + + $langs->loadCacheCurrencies(''); + + $out = ''; + + if ($selected == 'euro' || $selected == 'euros') $selected = 'EUR'; // Pour compatibilite + + $out .= ''; + if ($user->admin) $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + + // Make select dynamic + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname); + + return $out; + } + + /** + * Return array of currencies in user language + * + * @param string $selected preselected currency code + * @param string $htmlname name of HTML select list + * @param integer $useempty 1=Add empty line + * @return string + */ + public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0) + { + global $db, $conf, $langs, $user; + + $langs->loadCacheCurrencies(''); // Load ->cache_currencies + + $TCurrency = array(); + + $sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'multicurrency'; + $sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')"; + $resql = $db->query($sql); + if ($resql) + { + while ($obj = $db->fetch_object($resql)) $TCurrency[$obj->code] = $obj->code; + } + + $out = ''; + $out .= ''; + // Make select dynamic + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname); + + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Load into the cache vat rates of a country + * + * @param string $country_code Country code with quotes ("'CA'", or "'CA,IN,...'") + * @return int Nb of loaded lines, 0 if already loaded, <0 if KO + */ + public function load_cache_vatrates($country_code) + { + // phpcs:enable + global $langs; + + $num = count($this->cache_vatrates); + if ($num > 0) return $num; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; + $sql .= " WHERE t.fk_pays = c.rowid"; + $sql .= " AND t.active > 0"; + $sql .= " AND c.code IN (".$country_code.")"; + $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + if ($num) + { + for ($i = 0; $i < $num; $i++) + { + $obj = $this->db->fetch_object($resql); + $this->cache_vatrates[$i]['rowid'] = $obj->rowid; + $this->cache_vatrates[$i]['code'] = $obj->code; + $this->cache_vatrates[$i]['txtva'] = $obj->taux; + $this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly; + $this->cache_vatrates[$i]['localtax1'] = $obj->localtax1; + $this->cache_vatrates[$i]['localtax1_type'] = $obj->localtax1_type; + $this->cache_vatrates[$i]['localtax2'] = $obj->localtax2; + $this->cache_vatrates[$i]['localtax2_type'] = $obj->localtax1_type; + + $this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or * + $this->cache_vatrates[$i]['labelallrates'] = $obj->taux.'/'.($obj->localtax1 ? $obj->localtax1 : '0').'/'.($obj->localtax2 ? $obj->localtax2 : '0').($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label + $positiverates = ''; + if ($obj->taux) $positiverates .= ($positiverates ? '/' : '').$obj->taux; + if ($obj->localtax1) $positiverates .= ($positiverates ? '/' : '').$obj->localtax1; + if ($obj->localtax2) $positiverates .= ($positiverates ? '/' : '').$obj->localtax2; + if (empty($positiverates)) $positiverates = '0'; + $this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label + } + + return $num; + } + else + { + $this->error = ''.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).''; + return -1; + } + } + else + { + $this->error = ''.$this->db->error().''; + return -2; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output an HTML select vat rate. + * The name of this function should be selectVat. We keep bad name for compatibility purpose. + * + * @param string $htmlname Name of HTML select field + * @param float|string $selectedrate Force preselected vat rate. Can be '8.5' or '8.5 (NOO)' for example. Use '' for no forcing. + * @param Societe $societe_vendeuse Thirdparty seller + * @param Societe $societe_acheteuse Thirdparty buyer + * @param int $idprod Id product. O if unknown of NA. + * @param int $info_bits Miscellaneous information on line (1 for NPR) + * @param int|string $type ''=Unknown, 0=Product, 1=Service (Used if idprod not defined) + * Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle. + * Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle. + * Si (vendeur et acheteur dans Communaute europeenne) et bien vendu = moyen de transports neuf (auto, bateau, avion), TVA par defaut=0 (La TVA doit etre paye par l'acheteur au centre d'impots de son pays et non au vendeur). Fin de regle. + * Si vendeur et acheteur dans Communauté européenne et acheteur= particulier alors TVA par défaut=TVA du produit vendu. Fin de règle. + * Si vendeur et acheteur dans Communauté européenne et acheteur= entreprise alors TVA par défaut=0. Fin de règle. + * Sinon la TVA proposee par defaut=0. Fin de regle. + * @param bool $options_only Return HTML options lines only (for ajax treatment) + * @param int $mode 0=Use vat rate as key in combo list, 1=Add VAT code after vat rate into key, -1=Use id of vat line as key + * @return string + */ + public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0) + { + // phpcs:enable + global $langs, $conf, $mysoc; + + $langs->load('errors'); + + $return = ''; + + // Define defaultnpr, defaultttx and defaultcode + $defaultnpr = ($info_bits & 0x01); + $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr); + $defaulttx = str_replace('*', '', $selectedrate); + $defaultcode = ''; + if (preg_match('/\((.*)\)/', $defaulttx, $reg)) + { + $defaultcode = $reg[1]; + $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx); + } + //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode); + + // Check parameters + if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) + { + if ($societe_vendeuse->id == $mysoc->id) + { + $return .= ''.$langs->trans("ErrorYourCountryIsNotDefined").''; + } + else + { + $return .= ''.$langs->trans("ErrorSupplierCountryIsNotDefined").''; + } + return $return; + } + + //var_dump($societe_acheteuse); + //print "name=$name, selectedrate=$selectedrate, seller=".$societe_vendeuse->country_code." buyer=".$societe_acheteuse->country_code." buyer is company=".$societe_acheteuse->isACompany()." idprod=$idprod, info_bits=$info_bits type=$type"; + //exit; + + // Define list of countries to use to search VAT rates to show + // First we defined code_country to use to find list + if (is_object($societe_vendeuse)) + { + $code_country = "'".$societe_vendeuse->country_code."'"; + } + else + { + $code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente + } + if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) // If option to have vat for end customer for services is on + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; + if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) + { + // We also add the buyer + if (is_numeric($type)) + { + if ($type == 1) // We know product is a service + { + $code_country .= ",'".$societe_acheteuse->country_code."'"; + } + } + elseif (!$idprod) // We don't know type of product + { + $code_country .= ",'".$societe_acheteuse->country_code."'"; + } + else + { + $prodstatic = new Product($this->db); + $prodstatic->fetch($idprod); + if ($prodstatic->type == Product::TYPE_SERVICE) // We know product is a service + { + $code_country .= ",'".$societe_acheteuse->country_code."'"; + } + } + } + } + + // Now we get list + $num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error + + if ($num > 0) + { + // Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '') + if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) + { + $tmpthirdparty = new Societe($this->db); + $defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod); + $defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod); + if (preg_match('/\((.*)\)/', $defaulttx, $reg)) { + $defaultcode = $reg[1]; + $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx); + } + if (empty($defaulttx)) $defaultnpr = 0; + } + + // Si taux par defaut n'a pu etre determine, on prend dernier de la liste. + // Comme ils sont tries par ordre croissant, dernier = plus eleve = taux courant + if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) + { + if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) $defaulttx = $this->cache_vatrates[$num - 1]['txtva']; + else $defaulttx = ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS == 'none' ? '' : $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS); + } + + // Disabled if seller is not subject to VAT + $disabled = false; $title = ''; + if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") + { + // Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead + // of using supplier invoices (this is a very bad idea !) + if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) + { + $title = ' title="'.$langs->trans('VATIsNotUsed').'"'; + $disabled = true; + } + } + + if (!$options_only) $return .= ''; + } + else + { + $return .= $this->error; + } + + $this->num = $num; + return $return; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes. + * Fields are preselected with : + * - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM') + * - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location) + * - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1) + * + * @param integer $set_time Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2). + * @param string $prefix Prefix for fields name + * @param int $h 1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty + * @param int $m 1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty + * @param int $empty 0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only + * @param string $form_name Not used + * @param int $d 1=Show days, month, years + * @param int $addnowlink Add a link "Now" + * @param int $nooutput Do not output html string but return it + * @param int $disabled Disable input fields + * @param int $fullday When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59 + * @param string $addplusone Add a link "+1 hour". Value must be name of another select_date field. + * @param datetime $adddateof Add a link "Date of invoice" using the following date. + * @return string|void Nothing or string if nooutput is 1 + * @deprecated + * @see selectDate(), form_date(), select_month(), select_year(), select_dayofweek() + */ + public function select_date($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $nooutput = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '') + { + // phpcs:enable + $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof); + if (!empty($nooutput)) { + return $retstring; + } + print $retstring; + return; + } + + /** + * Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes. + * Fields are preselected with : + * - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM') + * - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location) + * - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1) + * + * @param integer $set_time Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2). + * @param string $prefix Prefix for fields name + * @param int $h 1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty + * @param int $m 1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty + * @param int $empty 0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only + * @param string $form_name Not used + * @param int $d 1=Show days, month, years + * @param int $addnowlink Add a link "Now", 1 with server time, 2 with local computer time + * @param int $disabled Disable input fields + * @param int $fullday When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59 + * @param string $addplusone Add a link "+1 hour". Value must be name of another selectDate field. + * @param datetime $adddateof Add a link "Date of ..." using the following date. See also $labeladddateof for the label used. + * @param string $openinghours Specify hour start and hour end for the select ex 8,20 + * @param int $stepminutes Specify step for minutes between 1 and 30 + * @param string $labeladddateof Label to use for the $adddateof parameter. + * @return string Html for selectDate + * @see form_date(), select_month(), select_year(), select_dayofweek() + */ + public function selectDate($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '', $openinghours = '', $stepminutes = 1, $labeladddateof = '') + { + global $conf, $langs; + + $retstring = ''; + + if ($prefix == '') $prefix = 're'; + if ($h == '') $h = 0; + if ($m == '') $m = 0; + $emptydate = 0; + $emptyhours = 0; + if ($stepminutes <= 0 || $stepminutes > 30) $stepminutes = 1; + if ($empty == 1) { $emptydate = 1; $emptyhours = 1; } + if ($empty == 2) { $emptydate = 0; $emptyhours = 1; } + $orig_set_time = $set_time; + + if ($set_time === '' && $emptydate == 0) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; + $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone + } + + // Analysis of the pre-selection date + if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) // deprecated usage + { + // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' + $syear = (!empty($reg[1]) ? $reg[1] : ''); + $smonth = (!empty($reg[2]) ? $reg[2] : ''); + $sday = (!empty($reg[3]) ? $reg[3] : ''); + $shour = (!empty($reg[4]) ? $reg[4] : ''); + $smin = (!empty($reg[5]) ? $reg[5] : ''); + } + elseif (strval($set_time) != '' && $set_time != -1) + { + // set_time est un timestamps (0 possible) + $syear = dol_print_date($set_time, "%Y"); + $smonth = dol_print_date($set_time, "%m"); + $sday = dol_print_date($set_time, "%d"); + if ($orig_set_time != '') + { + $shour = dol_print_date($set_time, "%H"); + $smin = dol_print_date($set_time, "%M"); + $ssec = dol_print_date($set_time, "%S"); + } + else + { + $shour = ''; + $smin = ''; + $ssec = ''; + } + } + else + { + // Date est '' ou vaut -1 + $syear = ''; + $smonth = ''; + $sday = ''; + $shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR; + $smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN; + $ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC; + } + if ($h == 3) $shour = ''; + if ($m == 3) $smin = ''; + + // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery' + $usecalendar = 'combo'; + if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) { + $usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR); + } + + if ($d) + { + // Show date with popup + if ($usecalendar != 'combo') + { + $formated_date = ''; + //print "e".$set_time." t ".$conf->format_date_short; + if (strval($set_time) != '' && $set_time != -1) + { + //$formated_date=dol_print_date($set_time,$conf->format_date_short); + $formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput")); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript + } + + // Calendrier popup version eldy + if ($usecalendar == "eldy") + { + // Zone de saisie manuelle de la date + $retstring .= 'trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript + $retstring .= '>'; + + // Icone calendrier + if (!$disabled) + { + $retstring .= ''; + } + else $retstring .= ''; + + $retstring .= ''."\n"; + $retstring .= ''."\n"; + $retstring .= ''."\n"; + } + elseif ($usecalendar == 'jquery') + { + if (!$disabled) + { + // Output javascript for datepicker + $retstring .= ""; + } + + // Zone de saisie manuelle de la date + $retstring .= '
'; + $retstring .= 'trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript + $retstring .= '>'; + + // Icone calendrier + if (!$disabled) + { + /* Not required. Managed by option buttonImage of jquery + $retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"'); + $retstring.="";*/ + } + else + { + $retstring .= ''; + } + + $retstring .= '
'; + $retstring .= ''."\n"; + $retstring .= ''."\n"; + $retstring .= ''."\n"; + } + else + { + $retstring .= "Bad value of MAIN_POPUP_CALENDAR"; + } + } + // Show date with combo selects + else + { + //$retstring.='
'; + // Day + $retstring .= ''; + + if ($emptydate || $set_time == -1) + { + $retstring .= ''; + } + + for ($day = 1; $day <= 31; $day++) + { + $retstring .= ''; + } + + $retstring .= ""; + + $retstring .= ''; + if ($emptydate || $set_time == -1) + { + $retstring .= ''; + } + + // Month + for ($month = 1; $month <= 12; $month++) + { + $retstring .= '"; + } + $retstring .= ""; + + // Year + if ($emptydate || $set_time == -1) + { + $retstring .= ''; + } + else + { + $retstring .= ''; + + for ($year = $syear - 10; $year < $syear + 10; $year++) + { + $retstring .= ''; + } + $retstring .= "\n"; + } + //$retstring.='
'; + } + } + + if ($d && $h) $retstring .= ($h == 2 ? '
' : ' '); + + if ($h) + { + $hourstart = 0; + $hourend = 24; + if ($openinghours != '') { + $openinghours = explode(',', $openinghours); + $hourstart = $openinghours[0]; + $hourend = $openinghours[1]; + if ($hourend < $hourstart) $hourend = $hourstart; + } + // Show hour + $retstring .= ''; + if ($emptyhours) $retstring .= ''; + for ($hour = $hourstart; $hour < $hourend; $hour++) + { + if (strlen($hour) < 2) $hour = "0".$hour; + $retstring .= ''; + } + $retstring .= ''; + if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":"; + } + + if ($m) + { + // Show minutes + $retstring .= ''; + if ($emptyhours) $retstring .= ''; + for ($min = 0; $min < 60; $min += $stepminutes) + { + if (strlen($min) < 2) $min = "0".$min; + $retstring .= ''; + } + $retstring .= ''; + + $retstring .= ''; + } + + // Add a "Now" link + if ($conf->use_javascript_ajax && $addnowlink) + { + // Script which will be inserted in the onClick of the "Now" link + $reset_scripts = ""; + if ($addnowlink == 2) // local computer time + { + // pad add leading 0 on numbers + $reset_scripts .= "Number.prototype.pad = function(size) { + var s = String(this); + while (s.length < (size || 2)) {s = '0' + s;} + return s; + }; + var d = new Date();"; + } + + // Generate the date part, depending on the use or not of the javascript calendar + if ($addnowlink == 1) // server time expressed in user time setup + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date(dol_now(), 'day', 'tzuser').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date(dol_now(), '%d', 'tzuser').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date(dol_now(), '%m', 'tzuser').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date(dol_now(), '%Y', 'tzuser').'\');'; + } + elseif ($addnowlink == 2) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());'; + } + /*if ($usecalendar == "eldy") + { + $base=DOL_URL_ROOT.'/core/'; + $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');'; + } + else + { + $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); '; + $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); '; + $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); '; + }*/ + // Update the hour part + if ($h) + { + if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; + //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); '; + if ($addnowlink == 1) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date(dol_now(), '%H', 'tzuser').'\');'; + } + elseif ($addnowlink == 2) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(d.getHours().pad());'; + } + + if ($fullday) $reset_scripts .= ' } '; + } + // Update the minute part + if ($m) + { + if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; + //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); '; + if ($addnowlink == 1) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date(dol_now(), '%M', 'tzuser').'\');'; + } + elseif ($addnowlink == 2) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(d.getMinutes().pad());'; + } + if ($fullday) $reset_scripts .= ' } '; + } + // If reset_scripts is not empty, print the link with the reset_scripts in the onClick + if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) + { + $retstring .= ' '; + } + } + + // Add a "Plus one hour" link + if ($conf->use_javascript_ajax && $addplusone) + { + // Script which will be inserted in the onClick of the "Add plusone" link + $reset_scripts = ""; + + // Generate the date part, depending on the use or not of the javascript calendar + $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date(dol_now(), 'day').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date(dol_now(), '%d').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date(dol_now(), '%m').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date(dol_now(), '%Y').'\');'; + // Update the hour part + if ($h) + { + if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; + $reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date(dol_now(), '%H').'\');'; + if ($fullday) $reset_scripts .= ' } '; + } + // Update the minute part + if ($m) + { + if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; + $reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date(dol_now(), '%M').'\');'; + if ($fullday) $reset_scripts .= ' } '; + } + // If reset_scripts is not empty, print the link with the reset_scripts in the onClick + if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) + { + $retstring .= ' '; + } + } + + // Add a "Plus one hour" link + if ($conf->use_javascript_ajax && $adddateof) + { + $tmparray = dol_getdate($adddateof); + if (empty($labeladddateof)) $labeladddateof = $langs->trans("DateInvoice"); + $retstring .= ' - '; + $out .= ''; + $out .= '
'; + + return $out; + } + + /** + * Return HTML to show the search and clear seach button + * + * @param string $cssclass CSS class + * @param int $calljsfunction 0=default. 1=call function initCheckForSelect() after changing status of checkboxes + * @param string $massactionname Mass action button name that will launch an action on the selected items + * @return string + */ + public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction") + { + global $conf, $langs; + + $out = ''; + $id = uniqid(); + if (!empty($conf->use_javascript_ajax)) $out .= '
'; + $out .= ''; + + return $out; + } + + /** + * Return HTML to show the search and clear seach button + * + * @param int $addcheckuncheckall Add the check all/uncheck all checkbox (use javascript) and code to manage this + * @param string $cssclass CSS class + * @param int $calljsfunction 0=default. 1=call function initCheckForSelect() after changing status of checkboxes + * @param string $massactionname Mass action name + * @return string + */ + public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction") + { + $out = $this->showFilterButtons(); + if ($addcheckuncheckall) + { + $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname); + } + return $out; + } + + /** + * Return HTML to show the select of expense categories + * + * @param string $selected preselected category + * @param string $htmlname name of HTML select list + * @param integer $useempty 1=Add empty line + * @param array $excludeid id to exclude + * @param string $target htmlname of target select to bind event + * @param int $default_selected default category to select if fk_c_type_fees change = EX_KME + * @param array $params param to give + * @return string + */ + public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array()) + { + global $db, $conf, $langs, $user; + + $out = ''; + $sql = 'SELECT rowid, label FROM '.MAIN_DB_PREFIX.'c_exp_tax_cat WHERE active = 1'; + $sql .= ' AND entity IN (0,'.getEntity('exp_tax_cat').')'; + if (!empty($excludeid)) $sql .= ' AND rowid NOT IN ('.implode(',', $excludeid).')'; + $sql .= ' ORDER BY label'; + + $resql = $db->query($sql); + if ($resql) + { + $out = ''; + if (!empty($htmlname) && $user->admin) $out .= ' '.info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + + if (!empty($target)) + { + $sql = "SELECT c.id FROM ".MAIN_DB_PREFIX."c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1"; + $resql = $db->query($sql); + if ($resql) + { + if ($db->num_rows($resql) > 0) + { + $obj = $db->fetch_object($resql); + $out .= ''; + } + } + } + } + else + { + dol_print_error($db); + } + + return $out; + } + + /** + * Return HTML to show the select ranges of expense range + * + * @param string $selected preselected category + * @param string $htmlname name of HTML select list + * @param integer $useempty 1=Add empty line + * @return string + */ + public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0) + { + global $db, $conf, $langs; + + $out = ''; + $sql = 'SELECT rowid, range_ik FROM '.MAIN_DB_PREFIX.'c_exp_tax_range'; + $sql .= ' WHERE entity = '.$conf->entity.' AND active = 1'; + + $resql = $db->query($sql); + if ($resql) + { + $out = ''; + } + else + { + dol_print_error($db); + } + + return $out; + } + + /** + * Return HTML to show a select of expense + * + * @param string $selected preselected category + * @param string $htmlname name of HTML select list + * @param integer $useempty 1=Add empty choice + * @param integer $allchoice 1=Add all choice + * @param integer $useid 0=use 'code' as key, 1=use 'id' as key + * @return string + */ + public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0) + { + global $db, $langs; + + $out = ''; + $sql = 'SELECT id, code, label FROM '.MAIN_DB_PREFIX.'c_type_fees'; + $sql .= ' WHERE active = 1'; + + $resql = $db->query($sql); + if ($resql) + { + $out = ''; + } + else + { + dol_print_error($db); + } + + return $out; + } + + /** + * Output a combo list with invoices qualified for a third party + * + * @param int $socid Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id) + * @param int $selected Id invoice preselected + * @param string $htmlname Name of HTML select + * @param int $maxlength Maximum length of label + * @param int $option_only Return only html options lines without the select tag + * @param string $show_empty Add an empty line ('1' or string to show for empty line) + * @param int $discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable) + * @param int $forcefocus Force focus on field (works with javascript only) + * @param int $disabled Disabled + * @param string $morecss More css added to the select component + * @param string $projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids. + * @param string $showproject 'all' = Show project info, ''=Hide project info + * @param User $usertofilter User object to use for filtering + * @return int Nbr of project if OK, <0 if KO + */ + public function selectInvoice($socid = -1, $selected = '', $htmlname = 'invoiceid', $maxlength = 24, $option_only = 0, $show_empty = '1', $discard_closed = 0, $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500', $projectsListId = '', $showproject = 'all', $usertofilter = null) + { + global $user, $conf, $langs; + + require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; + + if (is_null($usertofilter)) + { + $usertofilter = $user; + } + + $out = ''; + + $hideunselectables = false; + if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) $hideunselectables = true; + + if (empty($projectsListId)) + { + if (empty($usertofilter->rights->projet->all->lire)) + { + $projectstatic = new Project($this->db); + $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1); + } + } + + // Search all projects + $sql = 'SELECT f.rowid, f.ref as fref, "nolabel" as flabel, p.rowid as pid, f.ref, + p.title, p.fk_soc, p.fk_statut, p.public,'; + $sql .= ' s.nom as name'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as p'; + $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc,'; + $sql .= ' '.MAIN_DB_PREFIX.'facture as f'; + $sql .= " WHERE p.entity IN (".getEntity('project').")"; + $sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement + //if ($projectsListId) $sql.= " AND p.rowid IN (".$projectsListId.")"; + //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)"; + //if ($socid > 0) $sql.= " AND (p.fk_soc=".$socid." OR p.fk_soc IS NULL)"; + $sql .= " GROUP BY f.ref ORDER BY p.ref, f.ref ASC"; + + $resql = $this->db->query($sql); + if ($resql) + { + // Use select2 selector + if (!empty($conf->use_javascript_ajax)) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus); + $out .= $comboenhancement; + $morecss = 'minwidth200imp maxwidth500'; + } + + if (empty($option_only)) { + $out .= ''; + } + + print $out; + + $this->db->free($resql); + return $num; + } + else + { + dol_print_error($this->db); + return -1; + } + } + + /** + * Output the component to make advanced search criteries + * + * @param array $arrayofcriterias Array of available search criterias. Example: array($object->element => $object->fields, 'otherfamily' => otherarrayoffields, ...) + * @param array $search_component_params Array of selected search criterias + * @param array $arrayofinputfieldsalreadyoutput Array of input fields already inform. The component will not generate a hidden input field if it is in this list. + * @return string HTML component for advanced search + */ + public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array()) + { + global $conf, $langs; + + $ret = ''; + + $ret .= '
'; + //$ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= $langs->trans("Filters"); + $ret .= ''; + //$ret .= ''; + $ret .= '
'; + $texttoshow = '
'.$langs->trans("Search").'
'; + + $ret .= '
'.$texttoshow.'
'; + $ret .= '
'; + $ret .= ''; + // For compatibility with forms that show themself the search criteria in addition of this component, we output the fields + foreach ($arrayofcriterias as $criterias) { + foreach ($criterias as $criteriafamilykey => $criteriafamilyval) { + if (in_array('search_'.$criteriafamilykey, $arrayofinputfieldsalreadyoutput)) continue; + if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) continue; + if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) { + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + } + else { + $ret .= ''; + } + } + } + $ret .= '
'; + + + return $ret; + } +} From 848c9df537004ce048f06fb4876321802e0c11ea Mon Sep 17 00:00:00 2001 From: Anthony Berton <34568357+bb2a@users.noreply.github.com> Date: Thu, 11 Jun 2020 15:38:05 +0200 Subject: [PATCH 14/22] Add files via upload --- .../install/mysql/migration/11.0.0-12.0.0.sql | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 htdocs/install/mysql/migration/11.0.0-12.0.0.sql diff --git a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql new file mode 100644 index 00000000000..b7ac95b8cea --- /dev/null +++ b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql @@ -0,0 +1,310 @@ +-- +-- Be carefull to requests order. +-- This file must be loaded by calling /install/index.php page +-- when current version is 12.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 v11 + +UPDATE llx_c_units set scale = 3600 where code = 'H' and unit_type = 'time'; +UPDATE llx_c_units set scale = 86400 where code = 'D' and unit_type = 'time'; + +create table llx_commande_fournisseur_dispatch_extrafields +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + fk_object integer NOT NULL, -- object id + import_key varchar(14) -- import key +)ENGINE=innodb; + +ALTER TABLE llx_commande_fournisseur_dispatch_extrafields ADD INDEX idx_commande_fournisseur_dispatch_extrafields (fk_object); + +UPDATE llx_accounting_system SET fk_country = NULL, active = 0 WHERE pcg_version = 'SYSCOHADA'; + +create table llx_c_shipment_package_type +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + label varchar(50) NOT NULL, -- Short name + description varchar(255), -- Description + active integer DEFAULT 1 NOT NULL, -- Active or not + entity integer DEFAULT 1 NOT NULL -- Multi company id +)ENGINE=innodb; + +create table llx_facturedet_rec_extrafields +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + tms timestamp, + fk_object integer NOT NULL, -- object id + import_key varchar(14) -- import key +)ENGINE=innodb; + +ALTER TABLE llx_facturedet_rec_extrafields ADD INDEX idx_facturedet_rec_extrafields (fk_object); + +ALTER TABLE llx_facture_rec MODIFY COLUMN titre varchar(200) NOT NULL; + +-- This var is per entity now, so we remove const if global if exists +delete from llx_const where name in ('PROJECT_HIDE_TASKS', 'MAIN_BUGTRACK_ENABLELINK', 'MAIN_HELP_DISABLELINK') and entity = 0; + +-- For v12 + +ALTER TABLE llx_prelevement_bons ADD COLUMN type varchar(16) DEFAULT 'debit-order'; + +ALTER TABLE llx_ecm_files MODIFY COLUMN src_object_type varchar(64); + +ALTER TABLE llx_document_model MODIFY COLUMN type varchar(64); + + +-- Delete an old index that is duplicated +-- VMYSQL4.1 DROP INDEX ix_fk_product_stock on llx_product_batch; +-- VPGSQL8.2 DROP INDEX ix_fk_product_stock + +ALTER TABLE llx_actioncomm DROP COLUMN punctual; + +DELETE FROM llx_menu where module='supplier_proposal'; + +UPDATE llx_website SET lang = 'en' WHERE lang like 'en_%'; +UPDATE llx_website SET lang = 'fr' WHERE lang like 'fr_%'; +UPDATE llx_website SET lang = 'es' WHERE lang like 'es_%'; +UPDATE llx_website SET lang = 'de' WHERE lang like 'de_%'; +UPDATE llx_website SET lang = 'it' WHERE lang like 'it_%'; +UPDATE llx_website SET lang = 'pt' WHERE lang like 'pt_%'; +UPDATE llx_website_page SET lang = 'en' WHERE lang like 'en_%'; +UPDATE llx_website_page SET lang = 'fr' WHERE lang like 'fr_%'; +UPDATE llx_website_page SET lang = 'es' WHERE lang like 'es_%'; +UPDATE llx_website_page SET lang = 'de' WHERE lang like 'de_%'; +UPDATE llx_website_page SET lang = 'it' WHERE lang like 'it_%'; +UPDATE llx_website_page SET lang = 'pt' WHERE lang like 'pt_%'; + +ALTER TABLE llx_website ADD COLUMN lang varchar(8); +ALTER TABLE llx_website ADD COLUMN otherlang varchar(255); + +ALTER TABLE llx_website_page ADD COLUMN author_alias varchar(64); + +UPDATE llx_rights_def SET perms = 'order_advance', subperms = 'close' WHERE module = 'commande' AND perms = 'cloturer'; +UPDATE llx_rights_def SET perms = 'propal_advance', subperms = 'close' WHERE module = 'propale' AND perms = 'cloturer'; + +ALTER TABLE llx_holiday_users DROP INDEX uk_holiday_users; +ALTER TABLE llx_holiday_users ADD UNIQUE INDEX uk_holiday_users(fk_user, fk_type); + +ALTER TABLE llx_ticket ADD COLUMN import_key varchar(14); + +ALTER TABLE llx_ticket ADD UNIQUE uk_ticket_ref (ref, entity); +ALTER TABLE llx_ticket ADD INDEX idx_ticket_entity (entity); +ALTER TABLE llx_ticket ADD INDEX idx_ticket_fk_soc (fk_soc); +ALTER TABLE llx_ticket ADD INDEX idx_ticket_fk_user_assign (fk_user_assign); +ALTER TABLE llx_ticket ADD INDEX idx_ticket_fk_project (fk_project); +ALTER TABLE llx_ticket ADD INDEX idx_ticket_fk_statut (fk_statut); + + +--ALTER TABLE llx_facturerec DROP COLUMN vat_src_code; + + +-- Migration to the new regions (France) +UPDATE llx_c_regions set nom = 'Centre-Val de Loire' WHERE fk_pays = 1 AND code_region = 24; +insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 27, '21231', 0, 'Bourgogne-Franche-Comté'); +insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 28, '76540', 0, 'Normandie'); +insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 32, '59350', 4, 'Hauts-de-France'); +insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 44, '67482', 2, 'Grand Est'); +insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 75, '33063', 0, 'Nouvelle-Aquitaine'); +insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 76, '31355', 1, 'Occitanie'); +insert into llx_c_regions (fk_pays,code_region,cheflieu,tncc,nom) values (1, 84, '69123', 1, 'Auvergne-Rhône-Alpes'); + +UPDATE llx_c_departements set fk_region = 27 WHERE fk_region = 26 OR fk_region = 43; +UPDATE llx_c_departements set fk_region = 28 WHERE fk_region = 25 OR fk_region = 23; +UPDATE llx_c_departements set fk_region = 32 WHERE fk_region = 22 OR fk_region = 31; +UPDATE llx_c_departements set fk_region = 44 WHERE fk_region = 21 OR fk_region = 41 OR fk_region = 42; +UPDATE llx_c_departements set fk_region = 75 WHERE fk_region = 54 OR fk_region = 74 OR fk_region = 72; +UPDATE llx_c_departements set fk_region = 76 WHERE fk_region = 73 OR fk_region = 91; +UPDATE llx_c_departements set fk_region = 84 WHERE fk_region = 82 OR fk_region = 83; + +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 21; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 22; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 23; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 25; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 26; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 31; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 41; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 42; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 43; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 54; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 72; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 73; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 74; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 82; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 83; +DELETE FROM llx_c_regions WHERE fk_pays = 1 AND code_region = 91; + +ALTER TABLE llx_bookmark DROP INDEX uk_bookmark_url; +ALTER TABLE llx_bookmark DROP INDEX uk_bookmark_title; + +ALTER TABLE llx_bookmark MODIFY COLUMN url TEXT; + +ALTER TABLE llx_bookmark ADD UNIQUE uk_bookmark_title (fk_user, entity, title); + +ALTER TABLE llx_societe_rib MODIFY COLUMN owner_address varchar(255); +ALTER TABLE llx_societe_rib MODIFY COLUMN default_rib smallint NOT NULL DEFAULT 0; + +ALTER TABLE llx_societe_rib ADD COLUMN stripe_account varchar(128); + +create table llx_object_lang +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + fk_object integer DEFAULT 0 NOT NULL, + type_object varchar(32) NOT NULL, -- value found into $object->element + property varchar(32) NOT NULL, + lang varchar(5) DEFAULT 0 NOT NULL, + value text, + import_key varchar(14) DEFAULT NULL +)ENGINE=innodb; + + +ALTER TABLE llx_object_lang ADD UNIQUE INDEX uk_object_lang (fk_object, type_object, property, lang); + + +CREATE TABLE llx_categorie_actioncomm +( + fk_categorie integer NOT NULL, + fk_actioncomm integer NOT NULL, + import_key varchar(14) +) ENGINE=innodb; + +ALTER TABLE llx_categorie_actioncomm ADD PRIMARY KEY pk_categorie_actioncomm (fk_categorie, fk_actioncomm); +ALTER TABLE llx_categorie_actioncomm ADD INDEX idx_categorie_actioncomm_fk_categorie (fk_categorie); +ALTER TABLE llx_categorie_actioncomm ADD INDEX idx_categorie_actioncomm_fk_actioncomm (fk_actioncomm); + +ALTER TABLE llx_categorie_actioncomm ADD CONSTRAINT fk_categorie_actioncomm_categorie_rowid FOREIGN KEY (fk_categorie) REFERENCES llx_categorie (rowid); +ALTER TABLE llx_categorie_actioncomm ADD CONSTRAINT fk_categorie_actioncomm_fk_actioncomm FOREIGN KEY (fk_actioncomm) REFERENCES llx_actioncomm (id); + + +ALTER TABLE llx_accounting_account ADD COLUMN labelshort varchar(255) DEFAULT NULL after label; + +ALTER TABLE llx_subscription ADD COLUMN fk_user_creat integer DEFAULT NULL; +ALTER TABLE llx_subscription ADD COLUMN fk_user_valid integer DEFAULT NULL; + +UPDATE llx_c_forme_juridique set libelle = 'SRL - Société à responsabilité limitée' WHERE code = '201'; + +ALTER TABLE llx_c_country ADD COLUMN eec integer; +UPDATE llx_c_country SET eec = 1 WHERE code IN ('AT','BE','BG','CY','CZ','DE','DK','EE','ES','FI','FR','GB','GR','HR','NL','HU','IE','IM','IT','LT','LU','LV','MC','MT','PL','PT','RO','SE','SK','SI','UK'); + +INSERT INTO llx_accounting_system (fk_country, pcg_version, label, active) VALUES ( 1, 'PCG18-ASSOC', 'French foundation chart of accounts 2018', 1); + +INSERT INTO llx_accounting_system (fk_country, pcg_version, label, active) VALUES ( 1, 'PCGAFR14-DEV', 'The developed farm accountancy french plan 2014', 1); + +INSERT INTO llx_accounting_system (fk_country, pcg_version, label, active) VALUES ( 41, 'AT-BASE', 'Plan Austria', 1); + + + +create table llx_c_ticket_resolution +( + rowid integer AUTO_INCREMENT PRIMARY KEY, + entity integer DEFAULT 1, + code varchar(32) NOT NULL, + pos varchar(32) NOT NULL, + label varchar(128) NOT NULL, + active integer DEFAULT 1, + use_default integer DEFAULT 1, + description varchar(255) +)ENGINE=innodb; + +ALTER TABLE llx_c_ticket_resolution ADD UNIQUE INDEX uk_code (code, entity); + +INSERT INTO llx_c_ticket_resolution (code, pos, label, active, use_default, description) VALUES('SOLVED', '10', 'Solved', 1, 0, NULL); +INSERT INTO llx_c_ticket_resolution (code, pos, label, active, use_default, description) VALUES('CANCELED', '50', 'Canceled', 1, 0, NULL); +INSERT INTO llx_c_ticket_resolution (code, pos, label, active, use_default, description) VALUES('OTHER', '90', 'Other', 1, 0, NULL); + +DELETE FROM llx_const WHERE name = __ENCRYPT('DONATION_ART885')__; + +ALTER TABLE llx_extrafields MODIFY COLUMN printable integer DEFAULT 0; +ALTER TABLE llx_extrafields ADD COLUMN printable integer DEFAULT 0; + +UPDATE llx_const SET name = 'INVOICE_USE_RETAINED_WARRANTY' WHERE name = 'INVOICE_USE_SITUATION_RETAINED_WARRANTY'; + +ALTER TABLE llx_accounting_account DROP COLUMN pcg_subtype; + +ALTER TABLE llx_product ADD COLUMN accountancy_code_buy_intra varchar(32) AFTER accountancy_code_buy; +ALTER TABLE llx_product ADD COLUMN accountancy_code_buy_export varchar(32) AFTER accountancy_code_buy_intra; + +ALTER TABLE llx_entrepot ADD COLUMN fax varchar(20) DEFAULT NULL; +ALTER TABLE llx_entrepot ADD COLUMN phone varchar(20) DEFAULT NULL; + +ALTER TABLE llx_accounting_account ADD COLUMN reconcilable tinyint DEFAULT 0 NOT NULL after active; + +ALTER TABLE llx_categorie MODIFY type integer NOT NULL DEFAULT 1; + +ALTER TABLE llx_societe_remise_except ADD COLUMN vat_src_code varchar(10) DEFAULT ''; + +ALTER TABLE llx_blockedlog MODIFY COLUMN object_data mediumtext; +ALTER TABLE llx_blockedlog ADD COLUMN object_version varchar(32) DEFAULT ''; + +ALTER TABLE llx_product_lot MODIFY COLUMN batch varchar(128); +ALTER TABLE llx_product_batch MODIFY COLUMN batch varchar(128); +ALTER TABLE llx_expeditiondet_batch MODIFY COLUMN batch varchar(128); +ALTER TABLE llx_commande_fournisseur_dispatch MODIFY COLUMN batch varchar(128); +ALTER TABLE llx_stock_mouvement MODIFY COLUMN batch varchar(128); +ALTER TABLE llx_mrp_production MODIFY COLUMN batch varchar(128); +ALTER TABLE llx_mrp_production MODIFY qty real NOT NULL DEFAULT 1; + +create table llx_categorie_website_page +( + fk_categorie integer NOT NULL, + fk_website_page integer NOT NULL, + import_key varchar(14) +)ENGINE=innodb; + +ALTER TABLE llx_categorie_website_page ADD PRIMARY KEY pk_categorie_website_page (fk_categorie, fk_website_page); +ALTER TABLE llx_categorie_website_page ADD INDEX idx_categorie_website_page_fk_categorie (fk_categorie); +ALTER TABLE llx_categorie_website_page ADD INDEX idx_categorie_website_page_fk_website_page (fk_website_page); + +ALTER TABLE llx_categorie_website_page ADD CONSTRAINT fk_categorie_website_page_categorie_rowid FOREIGN KEY (fk_categorie) REFERENCES llx_categorie (rowid); +ALTER TABLE llx_categorie_website_page ADD CONSTRAINT fk_categorie_website_page_website_page_rowid FOREIGN KEY (fk_website_page) REFERENCES llx_website_page (rowid); + +ALTER TABLE llx_categorie ADD COLUMN date_creation datetime; +ALTER TABLE llx_categorie ADD COLUMN tms timestamp; +ALTER TABLE llx_categorie ADD COLUMN fk_user_creat integer; +ALTER TABLE llx_categorie ADD COLUMN fk_user_modif integer; + +ALTER TABLE llx_commandedet ADD CONSTRAINT fk_commandedet_fk_commandefourndet FOREIGN KEY (fk_commandefourndet) REFERENCES llx_commande_fournisseurdet (rowid); + + +-- VMYSQL4.3 ALTER TABLE llx_prelevement_facture_demande MODIFY COLUMN fk_facture INTEGER NULL; +-- VPGSQL8.2 ALTER TABLE llx_prelevement_facture_demande ALTER COLUMN fk_facture DROP NOT NULL; +ALTER TABLE llx_prelevement_facture_demande ADD COLUMN fk_facture_fourn INTEGER NULL; + +-- VMYSQL4.3 ALTER TABLE llx_prelevement_facture MODIFY COLUMN fk_facture INTEGER NULL; +-- VPGSQL8.2 ALTER TABLE llx_prelevement_facture ALTER COLUMN fk_facture DROP NOT NULL; +ALTER TABLE llx_prelevement_facture ADD COLUMN fk_facture_fourn INTEGER NULL; + +ALTER TABLE llx_menu MODIFY COLUMN module varchar(255); + +UPDATE llx_actioncomm SET fk_action = 50 where fk_action = 40 AND code = 'TICKET_MSG'; + +ALTER TABLE llx_emailcollector_emailcollector ADD COLUMN hostcharset varchar(16) DEFAULT 'UTF-8'; + +ALTER TABLE llx_adherent_type MODIFY subscription varchar(3) NOT NULL DEFAULT '1'; +ALTER TABLE llx_adherent_type MODIFY vote varchar(3) NOT NULL DEFAULT '1'; + + From 182a34616f7b13e45d93b165a746b4e2fe3a4cea Mon Sep 17 00:00:00 2001 From: Anthony Berton <34568357+bb2a@users.noreply.github.com> Date: Thu, 11 Jun 2020 15:39:05 +0200 Subject: [PATCH 15/22] Add files via upload --- htdocs/core/class/html.form.class.php | 163 ++++++++++---------------- 1 file changed, 62 insertions(+), 101 deletions(-) diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 008006b8fa5..0d1295b0429 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -368,6 +368,8 @@ class Form $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"'); $resultforextrlang .= $s; + + // TODO Use the showInputField() method of ExtraLanguages object if ($typeofdata == 'textarea') { $resultforextrlang .= ''; - } - elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') + } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') { $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form'.$htmlname, 1, 0); - } - elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') + } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') { $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form'.$htmlname, 1, 0); - } - elseif (preg_match('/^select;/', $typeofdata)) + } elseif (preg_match('/^select;/', $typeofdata)) { $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata)); $arraylist = array(); @@ -251,8 +240,7 @@ class Form $arraylist[$tmpkey] = $tmp[1]; } $ret .= $this->selectarray($htmlname, $arraylist, $value); - } - elseif (preg_match('/^ckeditor/', $typeofdata)) + } elseif (preg_match('/^ckeditor/', $typeofdata)) { $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; @@ -270,9 +258,7 @@ class Form if (empty($notabletag)) $ret .= ''."\n"; $ret .= ''."\n"; - } - else - { + } else { if (preg_match('/^(email)/', $typeofdata)) $ret .= dol_print_email($value, 0, 0, 0, 0, 1); elseif (preg_match('/^(amount|numeric)/', $typeofdata)) $ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : ''); elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) $ret .= dol_htmlentitiesbr($value); @@ -289,8 +275,7 @@ class Form $arraylist[$tmp[0]] = $tmp[1]; } $ret .= $arraylist[$value]; - } - elseif (preg_match('/^ckeditor/', $typeofdata)) + } elseif (preg_match('/^ckeditor/', $typeofdata)) { $tmpcontent = dol_htmlentitiesbr($value); if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) @@ -302,8 +287,7 @@ class Form // We dont use dol_escape_htmltag to get the html formating active, but this need we must also // clean data from some dangerous html $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent)); - } - else { + } else { $ret .= dol_escape_htmltag($value); } @@ -368,6 +352,8 @@ class Form $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"'); $resultforextrlang .= $s; + + // TODO Use the showInputField() method of ExtraLanguages object if ($typeofdata == 'textarea') { $resultforextrlang .= ''; + } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') + { + $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form'.$htmlname, 1, 0); + } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') + { + $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form'.$htmlname, 1, 0); + } elseif (preg_match('/^select;/', $typeofdata)) + { + $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata)); + $arraylist = array(); + foreach ($arraydata as $val) + { + $tmp = explode(':', $val); + $tmpkey = str_replace('|', ':', $tmp[0]); + $arraylist[$tmpkey] = $tmp[1]; + } + $ret .= $this->selectarray($htmlname, $arraylist, $value); + } elseif (preg_match('/^ckeditor/', $typeofdata)) + { + $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser + require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; + $doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), ($tmp[2] ? $tmp[2] : ''), ($tmp[3] ? $tmp[3] : '100'), ($tmp[1] ? $tmp[1] : 'dolibarr_notes'), 'In', ($tmp[5] ? $tmp[5] : 0), (isset($tmp[8]) ? ($tmp[8] ?true:false) : true), true, ($tmp[6] ? $tmp[6] : '20'), ($tmp[7] ? $tmp[7] : '100')); + $ret .= $doleditor->Create(1); + } + if (empty($notabletag)) $ret .= ''; + + if (empty($notabletag)) $ret .= ''; + //else $ret.='
'; + $ret .= ''; + if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) $ret .= '
'."\n"; + $ret .= ''; + if (empty($notabletag)) $ret .= ''; + + if (empty($notabletag)) $ret .= ''."\n"; + $ret .= ''."\n"; + } else { + if (preg_match('/^(email)/', $typeofdata)) $ret .= dol_print_email($value, 0, 0, 0, 0, 1); + elseif (preg_match('/^(amount|numeric)/', $typeofdata)) $ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : ''); + elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) $ret .= dol_htmlentitiesbr($value); + elseif (preg_match('/^safehtmlstring/', $typeofdata)) $ret .= dol_string_onlythesehtmltags($value); + elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') $ret .= dol_print_date($value, 'day'); + elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') $ret .= dol_print_date($value, 'dayhour'); + elseif (preg_match('/^select;/', $typeofdata)) + { + $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata)); + $arraylist = array(); + foreach ($arraydata as $val) + { + $tmp = explode(':', $val); + $arraylist[$tmp[0]] = $tmp[1]; + } + $ret .= $arraylist[$value]; + } elseif (preg_match('/^ckeditor/', $typeofdata)) + { + $tmpcontent = dol_htmlentitiesbr($value); + if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) + { + $firstline = preg_replace('/
.*/', '', $tmpcontent); + $firstline = preg_replace('/[\n\r].*/', '', $firstline); + $tmpcontent = $firstline.((strlen($firstline) != strlen($tmpcontent)) ? '...' : ''); + } + // We dont use dol_escape_htmltag to get the html formating active, but this need we must also + // clean data from some dangerous html + $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent)); + } else { + $ret .= dol_escape_htmltag($value); + } + + if ($formatfunc && method_exists($object, $formatfunc)) + { + $ret = $object->$formatfunc($ret); + } + } + } + return $ret; + } + + /** + * Output edit in place form + * + * @param string $fieldname Name of the field + * @param object $object Object + * @param boolean $perm Permission to allow button to edit parameter. Set it to 0 to have a not edited field. + * @param string $typeofdata Type of data ('string' by default, 'email', 'amount:99', 'numeric:99', 'text' or 'textarea:rows:cols', 'datepicker' ('day' do not work, don't know why), 'ckeditor:dolibarr_zzz:width:height:savemethod:1:rows:cols', 'select;xxx[:class]'...) + * @param string $check Same coe than $check parameter of GETPOST() + * @param string $morecss More CSS + * @return string HTML code for the edit of alternative language + */ + public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '') + { + global $conf, $langs, $extralanguages; + + $result = ''; + + // List of extra languages + $arrayoflangcode = array(); + if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE; + + if (is_array($arrayoflangcode) && count($arrayoflangcode)) { + if (!is_object($extralanguages)) { + include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php'; + $extralanguages = new ExtraLanguages($this->db); + } + $extralanguages->fetch_name_extralanguages('societe'); + + if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) { + return ''; // No extralang field to show + } + + $result .= ''."\n"; + $result .= '
'; + $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang'); + $result .= $s; + $result .= '
'; + + $result .= ''; + $result .= ''; + } + + return $result; + } + + /** + * Output edit in place form + * + * @param object $object Object + * @param string $value Value to show/edit + * @param string $htmlname DIV ID (field name) + * @param int $condition Condition to edit + * @param string $inputType Type of input ('string', 'numeric', 'datepicker' ('day' do not work, don't know why), 'textarea:rows:cols', 'ckeditor:dolibarr_zzz:width:height:?:1:rows:cols', 'select:loadmethod:savemethod:buttononly') + * @param string $editvalue When in edit mode, use this value as $value instead of value + * @param object $extObject External object + * @param mixed $custommsg String or Array of custom messages : eg array('success' => 'MyMessage', 'error' => 'MyMessage') + * @return string HTML edit in place + */ + protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null) + { + global $conf; + + $out = ''; + + // Check parameters + if (preg_match('/^text/', $inputType)) $value = dol_nl2br($value); + elseif (preg_match('/^numeric/', $inputType)) $value = price($value); + elseif ($inputType == 'day' || $inputType == 'datepicker') $value = dol_print_date($value, 'day'); + + if ($condition) + { + $element = false; + $table_element = false; + $fk_element = false; + $loadmethod = false; + $savemethod = false; + $ext_element = false; + $button_only = false; + $inputOption = ''; + + if (is_object($object)) + { + $element = $object->element; + $table_element = $object->table_element; + $fk_element = $object->id; + } + + if (is_object($extObject)) + { + $ext_element = $extObject->element; + } + + if (preg_match('/^(string|email|numeric)/', $inputType)) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; + if (!empty($tmp[1])) $inputOption = $tmp[1]; + if (!empty($tmp[2])) $savemethod = $tmp[2]; + $out .= ''."\n"; + } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; + if (!empty($tmp[1])) $inputOption = $tmp[1]; + if (!empty($tmp[2])) $savemethod = $tmp[2]; + + $out .= ''."\n"; // Use for timestamp format + } elseif (preg_match('/^(select|autocomplete)/', $inputType)) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; $loadmethod = $tmp[1]; + if (!empty($tmp[2])) $savemethod = $tmp[2]; + if (!empty($tmp[3])) $button_only = true; + } elseif (preg_match('/^textarea/', $inputType)) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; + $rows = (empty($tmp[1]) ? '8' : $tmp[1]); + $cols = (empty($tmp[2]) ? '80' : $tmp[2]); + } elseif (preg_match('/^ckeditor/', $inputType)) + { + $tmp = explode(':', $inputType); + $inputType = $tmp[0]; $toolbar = $tmp[1]; + if (!empty($tmp[2])) $width = $tmp[2]; + if (!empty($tmp[3])) $heigth = $tmp[3]; + if (!empty($tmp[4])) $savemethod = $tmp[4]; + + if (!empty($conf->fckeditor->enabled)) + { + $out .= ''."\n"; + } else { + $inputType = 'textarea'; + } + } + + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + $out .= ''."\n"; + if (!empty($savemethod)) $out .= ''."\n"; + if (!empty($ext_element)) $out .= ''."\n"; + if (!empty($custommsg)) + { + if (is_array($custommsg)) + { + if (!empty($custommsg['success'])) + $out .= ''."\n"; + if (!empty($custommsg['error'])) + $out .= ''."\n"; + } else $out .= ''."\n"; + } + if ($inputType == 'textarea') { + $out .= ''."\n"; + $out .= ''."\n"; + } + $out .= ''.$value.''."\n"; + $out .= ''.(!empty($editvalue) ? $editvalue : $value).''."\n"; + } else { + $out = $value; + } + + return $out; + } + + /** + * Show a text and picto with tooltip on text or picto. + * Can be called by an instancied $form->textwithtooltip or by a static call Form::textwithtooltip + * + * @param string $text Text to show + * @param string $htmltext HTML content of tooltip. Must be HTML/UTF8 encoded. + * @param int $tooltipon 1=tooltip on text, 2=tooltip on image, 3=tooltip sur les 2 + * @param int $direction -1=image is before, 0=no image, 1=image is after + * @param string $img Html code for image (use img_xxx() function to get it) + * @param string $extracss Add a CSS style to td tags + * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span + * @param string $incbefore Include code before the text + * @param int $noencodehtmltext Do not encode into html entity the htmltext + * @param string $tooltiptrigger ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key) + * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only) + * @return string Code html du tooltip (texte+picto) + * @see textwithpicto() Use thisfunction if you can. + * TODO Move this as static as soon as everybody use textwithpicto or @Form::textwithtooltip + */ + public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0) + { + if ($incbefore) $text = $incbefore.$text; + if (!$htmltext) return $text; + + $tag = 'td'; + if ($notabs == 2) $tag = 'div'; + if ($notabs == 3) $tag = 'span'; + // Sanitize tooltip + $htmltext = str_replace(array("\r", "\n"), '', $htmltext); + + $extrastyle = ''; + if ($direction < 0) { $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : ''); $extrastyle = 'padding: 0px; padding-left: 3px !important;'; } + if ($direction > 0) { $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : ''); $extrastyle = 'padding: 0px; padding-right: 3px !important;'; } + + $classfortooltip = 'classfortooltip'; + + $s = ''; $textfordialog = ''; + + if ($tooltiptrigger == '') + { + $htmltext = str_replace('"', '"', $htmltext); + } else { + $classfortooltip = 'classfortooltiponclick'; + $textfordialog .= ''; + } + if ($tooltipon == 2 || $tooltipon == 3) + { + $paramfortooltipimg = ' class="'.$classfortooltip.($notabs != 3 ? ' inline-block' : '').($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'"'; + if ($tooltiptrigger == '') $paramfortooltipimg .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on img tag to store tooltip + else $paramfortooltipimg .= ' dolid="'.$tooltiptrigger.'"'; + } else $paramfortooltipimg = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag + if ($tooltipon == 1 || $tooltipon == 3) + { + $paramfortooltiptd = ' class="'.($tooltipon == 3 ? 'cursorpointer ' : '').$classfortooltip.' inline-block'.($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'" '; + if ($tooltiptrigger == '') $paramfortooltiptd .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on td tag to store tooltip + else $paramfortooltiptd .= ' dolid="'.$tooltiptrigger.'"'; + } else $paramfortooltiptd = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag + if (empty($notabs)) $s .= ''; + elseif ($notabs == 2) $s .= '
'; + // Define value if value is before + if ($direction < 0) { + $s .= '<'.$tag.$paramfortooltipimg; + if ($tag == 'td') { + $s .= ' class=valigntop" width="14"'; + } + $s .= '>'.$textfordialog.$img.''; + } + // Use another method to help avoid having a space in value in order to use this value with jquery + // Define label + if ((string) $text != '') $s .= '<'.$tag.$paramfortooltiptd.'>'.$text.''; + // Define value if value is after + if ($direction > 0) { + $s .= '<'.$tag.$paramfortooltipimg; + if ($tag == 'td') $s .= ' class="valignmiddle" width="14"'; + $s .= '>'.$textfordialog.$img.''; + } + if (empty($notabs)) $s .= '
'; + elseif ($notabs == 2) $s .= '
'; + + return $s; + } + + /** + * Show a text with a picto and a tooltip on picto + * + * @param string $text Text to show + * @param string $htmltext Content of tooltip + * @param int $direction 1=Icon is after text, -1=Icon is before text, 0=no icon + * @param string $type Type of picto ('info', 'infoclickable', 'help', 'helpclickable', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath or 'none' + * @param string $extracss Add a CSS style to td, div or span tag + * @param int $noencodehtmltext Do not encode into html entity the htmltext + * @param int $notabs 0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span + * @param string $tooltiptrigger ''=Tooltip on hover, 'abc'=Tooltip on click (abc is a unique key, clickable link is on image or on link if param $type='none' or on both if $type='xxxclickable') + * @param int $forcenowrap Force no wrap between text and picto (works with notabs=2 only) + * @return string HTML code of text, picto, tooltip + */ + public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0) + { + global $conf, $langs; + + $alt = ''; + if ($tooltiptrigger) $alt = $langs->transnoentitiesnoconv("ClickToShowHelp"); + + //For backwards compatibility + if ($type == '0') $type = 'info'; + elseif ($type == '1') $type = 'help'; + + // If info or help with no javascript, show only text + if (empty($conf->use_javascript_ajax)) + { + if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') return $text; + else { + $alt = $htmltext; + $htmltext = ''; + } + } + + // If info or help with smartphone, show only text (tooltip hover can't works) + if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) + { + if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') return $text; + } + // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone) + //if (! empty($conf->dol_no_mouse_hover) && ! empty($tooltiptrigger)) + //{ + //if ($type == 'info' || $type == 'help') return ''.$text.'''; + //} + + $img = ''; + if ($type == 'info') $img = img_help(0, $alt); + elseif ($type == 'help') $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt); + elseif ($type == 'helpclickable') $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt); + elseif ($type == 'superadmin') $img = img_picto($alt, 'redstar'); + elseif ($type == 'admin') $img = img_picto($alt, 'star'); + elseif ($type == 'warning') $img = img_warning($alt); + elseif ($type != 'none') $img = img_picto($alt, $type); // $type can be an image path + + return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap); + } + + /** + * Generate select HTML to choose massaction + * + * @param string $selected Value auto selected when at least one record is selected. Not a preselected value. Use '0' by default. + * @param array $arrayofaction array('code'=>'label', ...). The code is the key stored into the GETPOST('massaction') when submitting action. + * @param int $alwaysvisible 1=select button always visible + * @param string $name Name for massaction + * @param string $cssclass CSS class used to check for select + * @return string|void Select list + */ + public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect') + { + global $conf, $langs, $hookmanager; + + + $disabled = 0; + $ret = '
'; + $ret .= ''; + + if (empty($conf->dol_optimize_smallscreen)) $ret .= ajax_combobox('.' . $name . 'select'); + + // Warning: if you set submit button to disabled, post using 'Enter' will no more work if there is no another input submit. So we add a hidden button + $ret .= ''; // Hidden button BEFORE so it is the one used when we submit with ENTER. + $ret .= ''; + $ret .= '
'; + + if (!empty($conf->use_javascript_ajax)) + { + $ret .= ' + + '; + } + + return $ret; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return combo list of activated countries, into language of user + * + * @param string $selected Id or Code or Label of preselected country + * @param string $htmlname Name of html select object + * @param string $htmloption More html options on select object + * @param integer $maxlength Max length for labels (0=no limit) + * @param string $morecss More css class + * @param string $usecodeaskey ''=Use id as key (default), 'code3'=Use code on 3 alpha as key, 'code2"=Use code on 2 alpha as key + * @param int $showempty Show empty choice + * @param int $disablefavorites 1=Disable favorites, + * @param int $addspecialentries 1=Add dedicated entries for group of countries (like 'European Economic Community', ...) + * @param array $exclude_country_code Array of country code (iso2) to exclude + * @return string HTML string with select + */ + public function select_country($selected = '', $htmlname = 'country_id', $htmloption = '', $maxlength = 0, $morecss = 'minwidth300', $usecodeaskey = '', $showempty = 1, $disablefavorites = 0, $addspecialentries = 0, $exclude_country_code = array()) + { + // phpcs:enable + global $conf, $langs, $mysoc; + + $langs->load("dict"); + + $out = ''; + $countryArray = array(); + $favorite = array(); + $label = array(); + $atleastonefavorite = 0; + + $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_country"; + $sql .= " WHERE active > 0"; + //$sql.= " ORDER BY code ASC"; + + dol_syslog(get_class($this)."::select_country", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + $out .= ''; + } else { + dol_print_error($this->db); + } + + // Make select dynamic + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox('select'.$htmlname); + + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return select list of incoterms + * + * @param string $selected Id or Code of preselected incoterm + * @param string $location_incoterms Value of input location + * @param string $page Defined the form action + * @param string $htmlname Name of html select object + * @param string $htmloption Options html on select object + * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification) + * @param array $events Event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @return string HTML string with select and input + */ + public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array()) + { + // phpcs:enable + global $conf, $langs; + + $langs->load("dict"); + + $out = ''; + $incotermArray = array(); + + $sql = "SELECT rowid, code"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_incoterms"; + $sql .= " WHERE active > 0"; + $sql .= " ORDER BY code ASC"; + + dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + if ($conf->use_javascript_ajax && !$forcecombo) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname, $events); + } + + if (!empty($page)) + { + $out .= '
'; + $out .= ''; + $out .= ''; + } + + $out .= ''; + + $out .= ''; + + if (!empty($page)) + { + $out .= '
'; + } + } else { + dol_print_error($this->db); + } + + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of types of lines (product or service) + * Example: 0=product, 1=service, 9=other (for external module) + * + * @param string $selected Preselected type + * @param string $htmlname Name of field in html form + * @param int $showempty Add an empty field + * @param int $hidetext Do not show label 'Type' before combo box (used only if there is at least 2 choices to select) + * @param integer $forceall 1=Force to show products and services in combo list, whatever are activated modules, 0=No force, 2=Force to show only Products, 3=Force to show only services, -1=Force none (and set hidden field to 'service') + * @return void + */ + public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0) + { + // phpcs:enable + global $db, $langs, $user, $conf; + + // If product & services are enabled or both disabled. + if ($forceall == 1 || (empty($forceall) && !empty($conf->product->enabled) && !empty($conf->service->enabled)) + || (empty($forceall) && empty($conf->product->enabled) && empty($conf->service->enabled))) + { + if (empty($hidetext)) print $langs->trans("Type").': '; + print ''; + //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1); + } + if ((empty($forceall) && empty($conf->product->enabled) && !empty($conf->service->enabled)) || $forceall == 3) + { + print $langs->trans("Service"); + print ''; + } + if ((empty($forceall) && !empty($conf->product->enabled) && empty($conf->service->enabled)) || $forceall == 2) + { + print $langs->trans("Product"); + print ''; + } + if ($forceall < 0) // This should happened only for contracts when both predefined product and service are disabled. + { + print ''; // By default we set on service for contract. If CONTRACT_SUPPORT_PRODUCTS is set, forceall should be 1 not -1 + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Load into cache cache_types_fees, array of types of fees + * + * @return int Nb of lines loaded, <0 if KO + */ + public function load_cache_types_fees() + { + // phpcs:enable + global $langs; + + $num = count($this->cache_types_fees); + if ($num > 0) return 0; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $langs->load("trips"); + + $sql = "SELECT c.code, c.label"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c"; + $sql .= " WHERE active > 0"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label)); + $this->cache_types_fees[$obj->code] = $label; + $i++; + } + + asort($this->cache_types_fees); + + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of types of notes + * + * @param string $selected Preselected type + * @param string $htmlname Name of field in form + * @param int $showempty Add an empty field + * @return void + */ + public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0) + { + // phpcs:enable + global $user, $langs; + + dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG); + + $this->load_cache_types_fees(); + + print ''; + if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return HTML code to select a company. + * + * @param int $selected Preselected products + * @param string $htmlname Name of HTML select field (must be unique in page) + * @param int $filter Filter on thirdparty + * @param int $limit Limit on number of returned lines + * @param array $ajaxoptions Options for ajax_autocompleter + * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification) + * @return string Return select box for thirdparty. + * @deprecated 3.8 Use select_company instead. For exemple $form->select_thirdparty(GETPOST('socid'),'socid','',0) => $form->select_company(GETPOST('socid'),'socid','',1,0,0,array(),0) + */ + public function select_thirdparty($selected = '', $htmlname = 'socid', $filter = '', $limit = 20, $ajaxoptions = array(), $forcecombo = 0) + { + // phpcs:enable + return $this->select_thirdparty_list($selected, $htmlname, $filter, 1, 0, $forcecombo, array(), '', 0, $limit); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output html form to select a third party + * + * @param string $selected Preselected type + * @param string $htmlname Name of field in form + * @param string $filter Optional filters criteras. WARNING: To avoid SQL injection, only few chars [.a-z0-9 =<>] are allowed here (example: 's.rowid <> x', 's.client IN (1,3)') + * @param string $showempty Add an empty field (Can be '1' or text key to use on empty line like 'SelectThirdParty') + * @param int $showtype Show third party type in combolist (customer, prospect or supplier) + * @param int $forcecombo Force to load all values and output a standard combobox (with no beautification) + * @param array $events Ajax event options to run on change. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param int $limit Maximum number of elements + * @param string $morecss Add more css styles to the SELECT component + * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container + * @param string $selected_input_value Value of preselected input text (for use with ajax) + * @param int $hidelabel Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after) + * @param array $ajaxoptions Options for ajax_autocompleter + * @param bool $multiple add [] in the name of element and add 'multiple' attribut (not working with ajax_autocompleter) + * @return string HTML string with select box for thirdparty. + */ + public function select_company($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $limit = 0, $morecss = 'minwidth100', $moreparam = '', $selected_input_value = '', $hidelabel = 1, $ajaxoptions = array(), $multiple = false) + { + // phpcs:enable + global $conf, $user, $langs; + + $out = ''; + + if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) + { + // No immediate load of all database + $placeholder = ''; + if ($selected && empty($selected_input_value)) + { + require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; + $societetmp = new Societe($this->db); + $societetmp->fetch($selected); + $selected_input_value = $societetmp->name; + unset($societetmp); + } + // mode 1 + $urloption = 'htmlname='.urlencode($htmlname).'&outjson=1&filter='.urlencode($filter).($showtype ? '&showtype='.urlencode($showtype) : ''); + $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); + $out .= ''; + if (empty($hidelabel)) print $langs->trans("RefOrLabel").' : '; + elseif ($hidelabel > 1) { + $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"'; + if ($hidelabel == 2) { + $out .= img_picto($langs->trans("Search"), 'search'); + } + } + $out .= 'global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />'; + if ($hidelabel == 3) { + $out .= img_picto($langs->trans("Search"), 'search'); + } + } else { + // Immediate load of all database + $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple); + } + + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output html form to select a third party. + * Note, you must use the select_company to get the component to select a third party. This function must only be called by select_company. + * + * @param string $selected Preselected type + * @param string $htmlname Name of field in form + * @param string $filter Optional filters criteras (example: 's.rowid <> x', 's.client in (1,3)') + * @param string $showempty Add an empty field (Can be '1' or text to use on empty line like 'SelectThirdParty') + * @param int $showtype Show third party type in combolist (customer, prospect or supplier) + * @param int $forcecombo Force to use standard HTML select component without beautification + * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param string $filterkey Filter on key value + * @param int $outputmode 0=HTML select string, 1=Array + * @param int $limit Limit number of answers + * @param string $morecss Add more css styles to the SELECT component + * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container + * @param bool $multiple add [] in the name of element and add 'multiple' attribut + * @return string HTML string with + */ + public function select_thirdparty_list($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $filterkey = '', $outputmode = 0, $limit = 0, $morecss = 'minwidth100', $moreparam = '', $multiple = false) + { + // phpcs:enable + global $conf, $user, $langs; + + $out = ''; + $num = 0; + $outarray = array(); + + if ($selected === '') $selected = array(); + elseif (!is_array($selected)) $selected = array($selected); + + // Clean $filter that may contains sql conditions so sql code + if (function_exists('testSqlAndScriptInject')) { + if (testSqlAndScriptInject($filter, 3) > 0) { + $filter = ''; + } + } + + // On recherche les societes + $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.client, s.fournisseur, s.code_client, s.code_fournisseur"; + + if ($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST) { + $sql .= ", s.address, s.zip, s.town"; + $sql .= ", dictp.code as country_code"; + } + + $sql .= " FROM ".MAIN_DB_PREFIX."societe as s"; + if (!$user->rights->societe->client->voir && !$user->socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + if ($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST) { + $sql .= " LEFT OUTER JOIN ".MAIN_DB_PREFIX."c_country as dictp ON dictp.rowid=s.fk_pays"; + } + $sql .= " WHERE s.entity IN (".getEntity('societe').")"; + if (!empty($user->socid)) $sql .= " AND s.rowid = ".$user->socid; + if ($filter) $sql .= " AND (".$filter.")"; + if (!$user->rights->societe->client->voir && !$user->socid) $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".$user->id; + if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) $sql .= " AND s.status <> 0"; + // Add criteria + if ($filterkey && $filterkey != '') + { + $sql .= " AND ("; + $prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on + // For natural search + $scrit = explode(' ', $filterkey); + $i = 0; + if (count($scrit) > 1) $sql .= "("; + foreach ($scrit as $crit) { + if ($i > 0) $sql .= " AND "; + $sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')"; + $i++; + } + if (count($scrit) > 1) $sql .= ")"; + if (!empty($conf->barcode->enabled)) + { + $sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'"; + } + $sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'"; + $sql .= ")"; + } + $sql .= $this->db->order("nom", "ASC"); + $sql .= $this->db->plimit($limit, 0); + + // Build output string + dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + if (!$forcecombo) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname, $events, $conf->global->COMPANY_USE_SEARCH_TO_SELECT); + } + + // Construct $out and $outarray + $out .= ''."\n"; + } else { + dol_print_error($this->db); + } + + $this->result = array('nbofthirdparties'=>$num); + + if ($outputmode) return $outarray; + return $out; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return HTML combo list of absolute discounts + * + * @param string $selected Id remise fixe pre-selectionnee + * @param string $htmlname Nom champ formulaire + * @param string $filter Criteres optionnels de filtre + * @param int $socid Id of thirdparty + * @param int $maxvalue Max value for lines that can be selected + * @return int Return number of qualifed lines in list + */ + public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0) + { + // phpcs:enable + global $langs, $conf; + + // On recherche les remises + $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,"; + $sql .= " re.description, re.fk_facture_source"; + $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re"; + $sql .= " WHERE re.fk_soc = ".(int) $socid; + $sql .= " AND re.entity = ".$conf->entity; + if ($filter) $sql .= " AND ".$filter; + $sql .= " ORDER BY re.description ASC"; + + dol_syslog(get_class($this)."::select_remises", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + print ''; + return $qualifiedlines; + } else { + dol_print_error($this->db); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of all contacts (for a third party or all) + * + * @param int $socid Id ot third party or 0 for all + * @param string $selected Id contact pre-selectionne + * @param string $htmlname Name of HTML field ('none' for a not editable field) + * @param int $showempty 0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list + * @param string $exclude List of contacts id to exclude + * @param string $limitto Disable answers that are not id in this array list + * @param integer $showfunction Add function into label + * @param string $moreclass Add more class to class style + * @param integer $showsoc Add company into label + * @param int $forcecombo Force to use combo box + * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param bool $options_only Return options only (for ajax treatment) + * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container + * @param string $htmlid Html id to use instead of htmlname + * @return int <0 if KO, Nb of contact in list if OK + * @deprected You can use selectcontacts directly (warning order of param was changed) + */ + public function select_contacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $showsoc = 0, $forcecombo = 0, $events = array(), $options_only = false, $moreparam = '', $htmlid = '') + { + // phpcs:enable + print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $moreclass, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid); + return $this->num; + } + + /** + * Return HTML code of the SELECT of list of all contacts (for a third party or all). + * This also set the number of contacts found into $this->num + * + * @since 9.0 Add afterSelectContactOptions hook + * + * @param int $socid Id ot third party or 0 for all or -1 for empty list + * @param array|int $selected Array of ID of pre-selected contact id + * @param string $htmlname Name of HTML field ('none' for a not editable field) + * @param int $showempty 0=no empty value, 1=add an empty value, 2=add line 'Internal' (used by user edit), 3=add an empty value only if more than one record into list + * @param string $exclude List of contacts id to exclude + * @param string $limitto Disable answers that are not id in this array list + * @param integer $showfunction Add function into label + * @param string $moreclass Add more class to class style + * @param bool $options_only Return options only (for ajax treatment) + * @param integer $showsoc Add company into label + * @param int $forcecombo Force to use combo box (so no ajax beautify effect) + * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param string $moreparam Add more parameters onto the select tag. For example 'style="width: 95%"' to avoid select2 component to go over parent container + * @param string $htmlid Html id to use instead of htmlname + * @param bool $multiple add [] in the name of element and add 'multiple' attribut + * @return int <0 if KO, Nb of contact in list if OK + */ + public function selectcontacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $moreclass = '', $options_only = false, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false) + { + global $conf, $langs, $hookmanager, $action; + + $langs->load('companies'); + + if (empty($htmlid)) $htmlid = $htmlname; + + if ($selected === '') $selected = array(); + elseif (!is_array($selected)) $selected = array($selected); + $out = ''; + + if (!is_object($hookmanager)) + { + include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php'; + $hookmanager = new HookManager($this->db); + } + + // We search third parties + $sql = "SELECT sp.rowid, sp.lastname, sp.statut, sp.firstname, sp.poste"; + if ($showsoc > 0) $sql .= " , s.nom as company"; + $sql .= " FROM ".MAIN_DB_PREFIX."socpeople as sp"; + if ($showsoc > 0) $sql .= " LEFT OUTER JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid=sp.fk_soc"; + $sql .= " WHERE sp.entity IN (".getEntity('socpeople').")"; + if ($socid > 0 || $socid == -1) $sql .= " AND sp.fk_soc=".$socid; + if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) $sql .= " AND sp.statut <> 0"; + $sql .= " ORDER BY sp.lastname ASC"; + + dol_syslog(get_class($this)."::select_contacts", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + + if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlid, $events, $conf->global->CONTACT_USE_SEARCH_TO_SELECT); + } + + if ($htmlname != 'none' && !$options_only) $out .= ''; + } + + $this->num = $num; + return $out; + } else { + dol_print_error($this->db); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return the HTML select list of users + * + * @param string $selected Id user preselected + * @param string $htmlname Field name in form + * @param int $show_empty 0=liste sans valeur nulle, 1=ajoute valeur inconnue + * @param array $exclude Array list of users id to exclude + * @param int $disabled If select list must be disabled + * @param array|string $include Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me + * @param int $enableonly Array list of users id to be enabled. All other must be disabled + * @param string $force_entity '0' or Ids of environment to force + * @return void + * @deprecated Use select_dolusers instead + * @see select_dolusers() + */ + public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0') + { + // phpcs:enable + print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return select list of users + * + * @param string $selected User id or user object of user preselected. If 0 or < -2, we use id of current user. If -1, keep unselected (if empty is allowed) + * @param string $htmlname Field name in form + * @param int $show_empty 0=list with no empty value, 1=add also an empty value into list + * @param array $exclude Array list of users id to exclude + * @param int $disabled If select list must be disabled + * @param array|string $include Array list of users id to include. User '' for all users or 'hierarchy' to have only supervised users or 'hierarchyme' to have supervised + me + * @param array $enableonly Array list of users id to be enabled. If defined, it means that others will be disabled + * @param string $force_entity '0' or Ids of environment to force + * @param int $maxlength Maximum length of string into list (0=no limit) + * @param int $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status + * @param string $morefilter Add more filters into sql request (Example: 'employee = 1') + * @param integer $show_every 0=default list, 1=add also a value "Everybody" at beginning of list + * @param string $enableonlytext If option $enableonlytext is set, we use this text to explain into label why record is disabled. Not used if enableonly is empty. + * @param string $morecss More css + * @param int $noactive Show only active users (this will also happened whatever is this option if USER_HIDE_INACTIVE_IN_COMBOBOX is on). + * @param int $outputmode 0=HTML select string, 1=Array + * @param bool $multiple add [] in the name of element and add 'multiple' attribut + * @return string HTML select string + * @see select_dolgroups() + */ + public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $noactive = 0, $outputmode = 0, $multiple = false) + { + // phpcs:enable + global $conf, $user, $langs, $hookmanager; + + // If no preselected user defined, we take current user + if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) $selected = $user->id; + + if ($selected === '') $selected = array(); + elseif (!is_array($selected)) $selected = array($selected); + + $excludeUsers = null; + $includeUsers = null; + + // Permettre l'exclusion d'utilisateurs + if (is_array($exclude)) $excludeUsers = implode(",", $exclude); + // Permettre l'inclusion d'utilisateurs + if (is_array($include)) $includeUsers = implode(",", $include); + elseif ($include == 'hierarchy') + { + // Build list includeUsers to have only hierarchy + $includeUsers = implode(",", $user->getAllChildIds(0)); + } elseif ($include == 'hierarchyme') + { + // Build list includeUsers to have only hierarchy and current user + $includeUsers = implode(",", $user->getAllChildIds(1)); + } + + $out = ''; + $outarray = array(); + + // Forge request to select users + $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut, u.login, u.admin, u.entity"; + if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) + { + $sql .= ", e.label"; + } + $sql .= " FROM ".MAIN_DB_PREFIX."user as u"; + if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) + { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entity as e ON e.rowid=u.entity"; + if ($force_entity) $sql .= " WHERE u.entity IN (0,".$force_entity.")"; + else $sql .= " WHERE u.entity IS NOT NULL"; + } else { + if (!empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) + { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."usergroup_user as ug"; + $sql .= " ON ug.fk_user = u.rowid"; + $sql .= " WHERE ug.entity = ".$conf->entity; + } else { + $sql .= " WHERE u.entity IN (0,".$conf->entity.")"; + } + } + if (!empty($user->socid)) $sql .= " AND u.fk_soc = ".$user->socid; + if (is_array($exclude) && $excludeUsers) $sql .= " AND u.rowid NOT IN (".$excludeUsers.")"; + if ($includeUsers) $sql .= " AND u.rowid IN (".$includeUsers.")"; + if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $noactive) $sql .= " AND u.statut <> 0"; + if (!empty($morefilter)) $sql .= " ".$morefilter; + + //Add hook to filter on user (for exemple on usergroup define in custom modules) + $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action); + if (!empty($reshook)) $sql .= $hookmanager->resPrint; + + if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname + { + $sql .= " ORDER BY u.firstname ASC"; + } else { + $sql .= " ORDER BY u.lastname ASC"; + } + + dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + if ($num) + { + // Enhance with select2 + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname); + + // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined + $out .= ''; + $out .= ''; + } + $out .= ''; + } else { + dol_print_error($this->db); + } + + if ($outputmode) return $outarray; + return $out; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return select list of users. Selected users are stored into session. + * List of users are provided into $_SESSION['assignedtouser']. + * + * @param string $action Value for $action + * @param string $htmlname Field name in form + * @param int $show_empty 0=list without the empty value, 1=add empty value + * @param array $exclude Array list of users id to exclude + * @param int $disabled If select list must be disabled + * @param array $include Array list of users id to include or 'hierarchy' to have only supervised users + * @param array $enableonly Array list of users id to be enabled. All other must be disabled + * @param int $force_entity '0' or Ids of environment to force + * @param int $maxlength Maximum length of string into list (0=no limit) + * @param int $showstatus 0=show user status only if status is disabled, 1=always show user status into label, -1=never show user status + * @param string $morefilter Add more filters into sql request + * @param int $showproperties Show properties of each attendees + * @param array $listofuserid Array with properties of each user + * @param array $listofcontactid Array with properties of each contact + * @param array $listofotherid Array with properties of each other contact + * @return string HTML select string + * @see select_dolgroups() + */ + public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array()) + { + // phpcs:enable + global $conf, $user, $langs; + + $userstatic = new User($this->db); + $out = ''; + + // Method with no ajax + //$out.='
'; + if ($action == 'view') + { + $out .= ''; + } else { + $out .= ''; + $out .= ''; + $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter); + $out .= ' '; + $out .= '
'; + } + $assignedtouser = array(); + if (!empty($_SESSION['assignedtouser'])) + { + $assignedtouser = json_decode($_SESSION['assignedtouser'], true); + } + $nbassignetouser = count($assignedtouser); + + if ($nbassignetouser && $action != 'view') $out .= '
'; + if ($nbassignetouser) $out .= '
    '; + $i = 0; $ownerid = 0; + foreach ($assignedtouser as $key => $value) + { + if ($value['id'] == $ownerid) continue; + + $out .= '
  • '; + $userstatic->fetch($value['id']); + $out .= $userstatic->getNomUrl(-1); + if ($i == 0) { $ownerid = $value['id']; $out .= ' ('.$langs->trans("Owner").')'; } + if ($nbassignetouser > 1 && $action != 'view') + { + $out .= ' '; + } + // Show my availability + if ($showproperties) + { + if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) + { + $out .= '
    '; + $out .= ' - '.$langs->trans("Availability").': '.$langs->trans("Busy"); + $out .= '
    '; + } + } + //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional")); + //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy")); + + $out .= '
  • '; + $i++; + } + if ($nbassignetouser) $out .= '
'; + + //$out.='
'; + return $out; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of products for customer in Ajax if Ajax activated or go to select_produits_list + * + * @param int $selected Preselected products + * @param string $htmlname Name of HTML select field (must be unique in page) + * @param int $filtertype Filter on product type (''=nofilter, 0=product, 1=service) + * @param int $limit Limit on number of returned lines + * @param int $price_level Level of price to show + * @param int $status Sell status -1=Return all products, 0=Products not on sell, 1=Products on sell + * @param int $finished 2=all, 1=finished, 0=raw material + * @param string $selected_input_value Value of preselected input text (for use with ajax) + * @param int $hidelabel Hide label (0=no, 1=yes, 2=show search icon (before) and placeholder, 3 search icon after) + * @param array $ajaxoptions Options for ajax_autocompleter + * @param int $socid Thirdparty Id (to get also price dedicated to this customer) + * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. + * @param int $forcecombo Force to use combo box + * @param string $morecss Add more css on select + * @param int $hidepriceinlabel 1=Hide prices in label + * @param string $warehouseStatus Warehouse status filter to count the quantity in stock. Following comma separated filter options can be used + * 'warehouseopen' = count products from open warehouses, + * 'warehouseclosed' = count products from closed warehouses, + * 'warehouseinternal' = count products from warehouses for internal correct/transfer only + * @param array $selected_combinations Selected combinations. Format: array([attrid] => attrval, [...]) + * @return void + */ + public function select_produits($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $status = 1, $finished = 2, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $selected_combinations = array()) + { + // phpcs:enable + global $langs, $conf; + + // check parameters + $price_level = (!empty($price_level) ? $price_level : 0); + if (is_null($ajaxoptions)) $ajaxoptions = array(); + + if (strval($filtertype) === '' && (!empty($conf->product->enabled) || !empty($conf->service->enabled))) { + if (!empty($conf->product->enabled) && empty($conf->service->enabled)) { + $filtertype = '0'; + } elseif (empty($conf->product->enabled) && !empty($conf->service->enabled)) { + $filtertype = '1'; + } + } + + if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) + { + $placeholder = ''; + + if ($selected && empty($selected_input_value)) + { + require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + $producttmpselect = new Product($this->db); + $producttmpselect->fetch($selected); + $selected_input_value = $producttmpselect->ref; + unset($producttmpselect); + } + // handle case where product or service module is disabled + no filter specified + if ($filtertype == '') + { + if (empty($conf->product->enabled)) { // when product module is disabled, show services only + $filtertype = 1; + } elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only + $filtertype = 0; + } + } + // mode=1 means customers products + $urloption = 'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus; + //Price by customer + if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) { + $urloption .= '&socid='.$socid; + } + print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); + + if (!empty($conf->variants->enabled)) { + ?> + + trans("RefOrLabel").' : '; + elseif ($hidelabel > 1) { + $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"'; + if ($hidelabel == 2) { + print img_picto($langs->trans("Search"), 'search'); + } + } + print 'global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />'; + if ($hidelabel == 3) { + print img_picto($langs->trans("Search"), 'search'); + } + } else { + print $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus); + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of products for a customer + * + * @param int $selected Preselected product + * @param string $htmlname Name of select html + * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service) + * @param int $limit Limit on number of returned lines + * @param int $price_level Level of price to show + * @param string $filterkey Filter on product + * @param int $status -1=Return all products, 0=Products not on sell, 1=Products on sell + * @param int $finished Filter on finished field: 2=No filter + * @param int $outputmode 0=HTML select string, 1=Array + * @param int $socid Thirdparty Id (to get also price dedicated to this customer) + * @param string $showempty '' to not show empty line. Translation key to show an empty line. '1' show empty line with no text. + * @param int $forcecombo Force to use combo box + * @param string $morecss Add more css on select + * @param int $hidepriceinlabel 1=Hide prices in label + * @param string $warehouseStatus Warehouse status filter to group/count stock. Following comma separated filter options can be used. + * 'warehouseopen' = count products from open warehouses, + * 'warehouseclosed' = count products from closed warehouses, + * 'warehouseinternal' = count products from warehouses for internal correct/transfer only + * @return array Array of keys for json + */ + public function select_produits_list($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '') + { + // phpcs:enable + global $langs, $conf, $user, $db; + + $out = ''; + $outarray = array(); + + // Units + if ($conf->global->PRODUCT_USE_UNITS) { + $langs->load('other'); + } + + $warehouseStatusArray = array(); + if (!empty($warehouseStatus)) + { + require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; + if (preg_match('/warehouseclosed/', $warehouseStatus)) + { + $warehouseStatusArray[] = Entrepot::STATUS_CLOSED; + } + if (preg_match('/warehouseopen/', $warehouseStatus)) + { + $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL; + } + if (preg_match('/warehouseinternal/', $warehouseStatus)) + { + $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL; + } + } + + $selectFields = " p.rowid, p.ref, p.label, p.description, p.barcode, p.fk_country, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.duration, p.fk_price_expression"; + if (count($warehouseStatusArray)) + { + $selectFieldsGrouped = ", sum(".$db->ifsql("e.statut IS NULL", "0", "ps.reel").") as stock"; // e.statut is null if there is no record in stock + } else { + $selectFieldsGrouped = ", ".$db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock"; + } + + $sql = "SELECT "; + $sql .= $selectFields.$selectFieldsGrouped; + + if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) + { + //Product category + $sql .= ", (SELECT ".MAIN_DB_PREFIX."categorie_product.fk_categorie + FROM ".MAIN_DB_PREFIX."categorie_product + WHERE ".MAIN_DB_PREFIX."categorie_product.fk_product=p.rowid + LIMIT 1 + ) AS categorie_product_id "; + } + + //Price by customer + 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"; + } + // Units + if (!empty($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"; + $selectFields .= ', unit_long, 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'; + } + + // Multilang : we add translation + if (!empty($conf->global->MAIN_MULTILANGS)) + { + $sql .= ", pl.label as label_translated"; + $selectFields .= ", label_translated"; + } + // Price by quantity + if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) + { + $sql .= ", (SELECT pp.rowid FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid"; + if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) $sql .= " AND price_level=".$price_level; + $sql .= " ORDER BY date_price"; + $sql .= " DESC LIMIT 1) as price_rowid"; + $sql .= ", (SELECT pp.price_by_qty FROM ".MAIN_DB_PREFIX."product_price as pp WHERE pp.fk_product = p.rowid"; // price_by_qty is 1 if some prices by qty exists in subtable + if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) $sql .= " AND price_level=".$price_level; + $sql .= " ORDER BY date_price"; + $sql .= " DESC LIMIT 1) as price_by_qty"; + $selectFields .= ", price_rowid, price_by_qty"; + } + $sql .= " FROM ".MAIN_DB_PREFIX."product as p"; + if (count($warehouseStatusArray)) + { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_stock as ps on ps.fk_product = p.rowid"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")"; + $sql .= ' AND e.statut IN ('.$this->db->escape(implode(',', $warehouseStatusArray)).')'; // Return line if product is inside the selected stock. If not, an empty line will be returned so we will count 0. + } + + // include search in supplier ref + if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) + { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product"; + } + + //Price by customer + if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_customer_price as pcp ON pcp.fk_soc=".$socid." AND pcp.fk_product=p.rowid"; + } + // Units + if (!empty($conf->global->PRODUCT_USE_UNITS)) { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit"; + } + // 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='".$langs->getDefaultLang()."'"; + } + + if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_attribute_combination pac ON pac.fk_product_child = p.rowid"; + } + + $sql .= ' WHERE p.entity IN ('.getEntity('product').')'; + + if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) { + $sql .= " AND pac.rowid IS NULL"; + } + + if ($finished == 0) + { + $sql .= " AND p.finished = ".$finished; + } elseif ($finished == 1) + { + $sql .= " AND p.finished = ".$finished; + if ($status >= 0) $sql .= " AND p.tosell = ".$status; + } elseif ($status >= 0) + { + $sql .= " AND p.tosell = ".$status; + } + // Filter by product type + if (strval($filtertype) != '') $sql .= " AND p.fk_product_type = ".$filtertype; + elseif (empty($conf->product->enabled)) { // when product module is disabled, show services only + $sql .= " AND p.fk_product_type = 1"; + } elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only + $sql .= " AND p.fk_product_type = 0"; + } + // Add criteria on ref/label + if ($filterkey != '') + { + $sql .= ' AND ('; + $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on + // For natural search + $scrit = explode(' ', $filterkey); + $i = 0; + if (count($scrit) > 1) $sql .= "("; + foreach ($scrit as $crit) + { + if ($i > 0) $sql .= " AND "; + $sql .= "(p.ref LIKE '".$db->escape($prefix.$crit)."%' OR p.label LIKE '".$db->escape($prefix.$crit)."%'"; + if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " OR pl.label LIKE '".$db->escape($prefix.$crit)."%'"; + if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) + { + $sql .= " OR p.description LIKE '".$db->escape($prefix.$crit)."%'"; + if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " OR pl.description LIKE '".$db->escape($prefix.$crit)."%'"; + } + if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) $sql .= " OR pfp.ref_fourn LIKE '".$db->escape($prefix.$crit)."%'"; + $sql .= ")"; + $i++; + } + if (count($scrit) > 1) $sql .= ")"; + if (!empty($conf->barcode->enabled)) $sql .= " OR p.barcode LIKE '".$db->escape($prefix.$filterkey)."%'"; + $sql .= ')'; + } + if (count($warehouseStatusArray)) + { + $sql .= ' GROUP BY'.$selectFields; + } + + //Sort by category + if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) + { + $sql .= " ORDER BY categorie_product_id "; + //ASC OR DESC order + ($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC"; + } else { + $sql .= $db->order("p.ref"); + } + + $sql .= $db->plimit($limit, 0); + + // Build output string + dol_syslog(get_class($this)."::select_produits_list search product", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; + + $num = $this->db->num_rows($result); + + $events = null; + + if (!$forcecombo) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname, $events, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT); + } + + $out .= ''; + + $this->db->free($result); + + if (empty($outputmode)) return $out; + return $outarray; + } else { + dol_print_error($db); + } + } + + /** + * constructProductListOption. + * This define value for &$opt and &$optJson. + * + * @param resource $objp Resultset of fetch + * @param string $opt Option (var used for returned value in string option format) + * @param string $optJson Option (var used for returned value in json format) + * @param int $price_level Price level + * @param string $selected Preselected value + * @param int $hidepriceinlabel Hide price in label + * @param string $filterkey Filter key to highlight + * @param int $novirtualstock Do not load virtual stock, even if slow option STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO is on. + * @return void + */ + protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0) + { + global $langs, $conf, $user, $db; + + $outkey = ''; + $outval = ''; + $outref = ''; + $outlabel = ''; + $outdesc = ''; + $outbarcode = ''; + $outorigin = ''; + $outtype = ''; + $outprice_ht = ''; + $outprice_ttc = ''; + $outpricebasetype = ''; + $outtva_tx = ''; + $outqty = 1; + $outdiscount = 0; + + $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO); + + $label = $objp->label; + if (!empty($objp->label_translated)) $label = $objp->label_translated; + if (!empty($filterkey) && $filterkey != '') $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $label, 1); + + $outkey = $objp->rowid; + $outref = $objp->ref; + $outlabel = $objp->label; + $outdesc = $objp->description; + $outbarcode = $objp->barcode; + $outorigin = $objp->fk_country; + $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid; + + $outtype = $objp->fk_product_type; + $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : ''; + $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : ''; + + // Units + $outvalUnits = ''; + if (!empty($conf->global->PRODUCT_USE_UNITS)) { + if (!empty($objp->unit_short)) { + $outvalUnits .= ' - '.$objp->unit_short; + } + } + if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) { + if (!empty($objp->weight) && $objp->weight_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) { + $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units); + $outvalUnits .= ' - '.$unitToShow; + } + if (!empty($objp->surface) && $objp->surface_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if (!empty($objp->volume) && $objp->volume_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + } + if ($outdurationvalue && $outdurationunit) { + $da = array( + 'h' => $langs->trans('Hour'), + 'd' => $langs->trans('Day'), + 'w' => $langs->trans('Week'), + 'm' => $langs->trans('Month'), + 'y' => $langs->trans('Year') + ); + if (isset($da[$outdurationunit])) { + $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : '')); + } + } + + $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); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs_list) + * + * @param int $socid Id third party + * @param string $selected Preselected product + * @param string $htmlname Name of HTML Select + * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service) + * @param string $filtre For a SQL filter + * @param array $ajaxoptions Options for ajax_autocompleter + * @param int $hidelabel Hide label (0=no, 1=yes) + * @param int $alsoproductwithnosupplierprice 1=Add also product without supplier prices + * @param string $morecss More CSS + * @return void + */ + public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '') + { + // phpcs:enable + global $langs, $conf; + global $price_level, $status, $finished; + + $selected_input_value = ''; + if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) + { + if ($selected > 0) + { + require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + $producttmpselect = new Product($this->db); + $producttmpselect->fetch($selected); + $selected_input_value = $producttmpselect->ref; + unset($producttmpselect); + } + + // mode=2 means suppliers products + $urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice; + print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions); + print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').''; + } else { + print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', -1, 0, 0, $alsoproductwithnosupplierprice, $morecss); + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of suppliers products + * + * @param int $socid Id societe fournisseur (0 pour aucun filtre) + * @param int $selected Product price pre-selected (must be 'id' in product_fournisseur_price or 'idprod_IDPROD') + * @param string $htmlname Nom de la zone select + * @param string $filtertype Filter on product type (''=nofilter, 0=product, 1=service) + * @param string $filtre Pour filtre sql + * @param string $filterkey Filtre des produits + * @param int $statut -1=Return all products, 0=Products not on sell, 1=Products on sell (not used here, a filter on tobuy is already hard coded in request) + * @param int $outputmode 0=HTML select string, 1=Array + * @param int $limit Limit of line number + * @param int $alsoproductwithnosupplierprice 1=Add also product without supplier prices + * @param string $morecss Add more CSS + * @return array Array of keys for json + */ + public function select_produits_fournisseurs_list($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $filterkey = '', $statut = -1, $outputmode = 0, $limit = 100, $alsoproductwithnosupplierprice = 0, $morecss = '') + { + // phpcs:enable + global $langs, $conf, $db; + + $out = ''; + $outarray = array(); + + $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO); + + $langs->load('stocks'); + // Units + if ($conf->global->PRODUCT_USE_UNITS) { + $langs->load('other'); + } + + $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type,"; + $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"; + // 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"; + } + if (!empty($conf->barcode->enabled)) $sql .= ", pfp.barcode"; + $sql .= " FROM ".MAIN_DB_PREFIX."product as p"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )"; + if ($socid) $sql .= " AND pfp.fk_soc = ".$socid; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid"; + // Units + if ($conf->global->PRODUCT_USE_UNITS) { + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_units u ON u.rowid = p.fk_unit"; + } + $sql .= " WHERE p.entity IN (".getEntity('product').")"; + $sql .= " AND p.tobuy = 1"; + if (strval($filtertype) != '') $sql .= " AND p.fk_product_type=".$this->db->escape($filtertype); + if (!empty($filtre)) $sql .= " ".$filtre; + // Add criteria on ref/label + if ($filterkey != '') + { + $sql .= ' AND ('; + $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on + // For natural search + $scrit = explode(' ', $filterkey); + $i = 0; + if (count($scrit) > 1) $sql .= "("; + 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)."%')"; + $i++; + } + if (count($scrit) > 1) $sql .= ")"; + if (!empty($conf->barcode->enabled)) { + $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'"; + $sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'"; + } + $sql .= ')'; + } + $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC"; + $sql .= $db->plimit($limit, 0); + + // Build output string + + dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; + require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php'; + + $num = $this->db->num_rows($result); + + //$out.=''; + if (!$selected) $out .= ''; + else $out .= ''; + + $i = 0; + while ($i < $num) + { + $objp = $this->db->fetch_object($result); + + $outkey = $objp->idprodfournprice; // id in table of price + if (!$outkey && $alsoproductwithnosupplierprice) $outkey = 'idprod_'.$objp->rowid; // id of product + + $outref = $objp->ref; + $outval = ''; + $outbarcode = $objp->barcode; + $outqty = 1; + $outdiscount = 0; + $outtype = $objp->fk_product_type; + $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : ''; + $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : ''; + + // Units + $outvalUnits = ''; + if ($conf->global->PRODUCT_USE_UNITS) { + if (!empty($objp->unit_short)) { + $outvalUnits .= ' - '.$objp->unit_short; + } + if (!empty($objp->weight) && $objp->weight_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) { + $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units); + $outvalUnits .= ' - '.$unitToShow; + } + if (!empty($objp->surface) && $objp->surface_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if (!empty($objp->volume) && $objp->volume_units !== null) { + $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs); + $outvalUnits .= ' - '.$unitToShow; + } + if ($outdurationvalue && $outdurationunit) { + $da = array( + 'h' => $langs->trans('Hour'), + 'd' => $langs->trans('Day'), + 'w' => $langs->trans('Week'), + 'm' => $langs->trans('Month'), + 'y' => $langs->trans('Year') + ); + if (isset($da[$outdurationunit])) { + $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : '')); + } + } + } + + $objRef = $objp->ref; + if ($filterkey && $filterkey != '') $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $objRef, 1); + $objRefFourn = $objp->ref_fourn; + if ($filterkey && $filterkey != '') $objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $objRefFourn, 1); + $label = $objp->label; + if ($filterkey && $filterkey != '') $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '$1', $label, 1); + + $optlabel = $objp->ref; + if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) { + $optlabel .= ' ('.$objp->ref_fourn.')'; + } + if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) { + $optlabel .= ' ('.$outbarcode.')'; + } + $optlabel .= ' - '.dol_trunc($label, $maxlengtharticle); + + $outvallabel = $objRef; + if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) { + $outvallabel .= ' ('.$objRefFourn.')'; + } + if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) { + $outvallabel .= ' ('.$outbarcode.')'; + } + $outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle); + + // Units + $optlabel .= $outvalUnits; + $outvallabel .= $outvalUnits; + + if (!empty($objp->idprodfournprice)) + { + $outqty = $objp->quantity; + $outdiscount = $objp->remise_percent; + if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) { + $prod_supplier = new ProductFournisseur($this->db); + $prod_supplier->product_fourn_price_id = $objp->idprodfournprice; + $prod_supplier->id = $objp->fk_product; + $prod_supplier->fourn_qty = $objp->quantity; + $prod_supplier->fourn_tva_tx = $objp->tva_tx; + $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression; + $priceparser = new PriceParser($this->db); + $price_result = $priceparser->parseProductSupplier($prod_supplier); + if ($price_result >= 0) { + $objp->fprice = $price_result; + if ($objp->quantity >= 1) + { + $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice + } + } + } + if ($objp->quantity == 1) + { + $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/"; + $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/"; + $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding + $outvallabel .= $langs->transnoentities("Unit"); + } else { + $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity; + $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity; + $optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding + $outvallabel .= ' '.$langs->transnoentities("Units"); + } + + if ($objp->quantity > 1) + { + $optlabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding + $outvallabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding + } + if ($objp->remise_percent >= 1) + { + $optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %'; + $outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %'; + } + if ($objp->duration) + { + $optlabel .= " - ".$objp->duration; + $outvallabel .= " - ".$objp->duration; + } + if (!$socid) + { + $optlabel .= " - ".dol_trunc($objp->name, 8); + $outvallabel .= " - ".dol_trunc($objp->name, 8); + } + if ($objp->supplier_reputation) + { + //TODO dictionary + $reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier')); + + $optlabel .= " - ".$reputations[$objp->supplier_reputation]; + $outvallabel .= " - ".$reputations[$objp->supplier_reputation]; + } + } else { + if (empty($alsoproductwithnosupplierprice)) // No supplier price defined for couple product/supplier + { + $optlabel .= " - ".$langs->trans("NoPriceDefinedForThisSupplier").''; + $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier"); + } else // No supplier price defined for product, even on other suppliers + { + $optlabel .= " - ".$langs->trans("NoPriceDefinedForThisSupplier").''; + $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier"); + } + } + + $opt = '\n"; + + + // Add new entry + // "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))); + // 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é)" + // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false) + //} + //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval)); + //$outval=array('label'=>'ppp (fff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)'); + //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval)); + + $i++; + } + $out .= ''; + + $this->db->free($result); + + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname); + + if (empty($outputmode)) return $out; + return $outarray; + } else { + dol_print_error($this->db); + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of suppliers prices for a product + * + * @param int $productid Id of product + * @param string $htmlname Name of HTML field + * @param int $selected_supplier Pre-selected supplier if more than 1 result + * @return string + */ + public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '') + { + // phpcs:enable + global $langs, $conf; + + $langs->load('stocks'); + + $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,"; + $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,"; + $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name"; + $sql .= " FROM ".MAIN_DB_PREFIX."product as p"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product"; + $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid"; + $sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")"; + $sql .= " AND p.tobuy = 1"; + $sql .= " AND s.fournisseur = 1"; + $sql .= " AND p.rowid = ".$productid; + $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC"; + + dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG); + $result = $this->db->query($sql); + + if ($result) + { + $num = $this->db->num_rows($result); + + $form = ''; + $this->db->free($result); + return $form; + } else { + dol_print_error($this->db); + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of delivery address + * + * @param string $selected Id contact pre-selectionn + * @param int $socid Id of company + * @param string $htmlname Name of HTML field + * @param int $showempty Add an empty field + * @return integer|null + */ + public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0) + { + // phpcs:enable + // looking for users + $sql = "SELECT a.rowid, a.label"; + $sql .= " FROM ".MAIN_DB_PREFIX."societe_address as a"; + $sql .= " WHERE a.fk_soc = ".$socid; + $sql .= " ORDER BY a.label ASC"; + + dol_syslog(get_class($this)."::select_address", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + print ''; + return $num; + } else { + dol_print_error($this->db); + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Load into cache list of payment terms + * + * @return int Nb of lines loaded, <0 if KO + */ + public function load_cache_conditions_paiements() + { + // phpcs:enable + global $langs; + + $num = count($this->cache_conditions_paiements); + if ($num > 0) return 0; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $sql = "SELECT rowid, code, libelle as label"; + $sql .= " FROM ".MAIN_DB_PREFIX.'c_payment_term'; + $sql .= " WHERE entity IN (".getEntity('c_payment_term').")"; + $sql .= " AND active > 0"; + $sql .= " ORDER BY sortorder"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : '')); + $this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code; + $this->cache_conditions_paiements[$obj->rowid]['label'] = $label; + $i++; + } + + //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table + + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Charge dans cache la liste des délais de livraison possibles + * + * @return int Nb of lines loaded, <0 if KO + */ + public function load_cache_availability() + { + // phpcs:enable + global $langs; + + $num = count($this->cache_availability); + if ($num > 0) return 0; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $langs->load('propal'); + + $sql = "SELECT rowid, code, label"; + $sql .= " FROM ".MAIN_DB_PREFIX.'c_availability'; + $sql .= " WHERE active > 0"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $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; + $i++; + } + + $this->cache_availability = dol_sort_array($this->cache_availability, 'label', 'asc', 0, 0, 1); + + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + + /** + * Retourne la liste des types de delais de livraison possibles + * + * @param int $selected Id du type de delais pre-selectionne + * @param string $htmlname Nom de la zone select + * @param string $filtertype To add a filter + * @param int $addempty Add empty entry + * @return void + */ + public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0) + { + global $langs, $user; + + $this->load_cache_availability(); + + dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG); + + print ''; + if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + /** + * Load into cache cache_demand_reason, array of input reasons + * + * @return int Nb of lines loaded, <0 if KO + */ + public function loadCacheInputReason() + { + global $langs; + + $num = count($this->cache_demand_reason); + if ($num > 0) return 0; // Cache already loaded + + $sql = "SELECT rowid, code, label"; + $sql .= " FROM ".MAIN_DB_PREFIX.'c_input_reason'; + $sql .= " WHERE active > 0"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + $tmparray = array(); + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($obj->label != '-' ? $obj->label : ''); + if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) $label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work + if ($langs->trans($obj->code) != $obj->code) $label = $langs->trans($obj->code); // So translation key SRC_XXX will work + + $tmparray[$obj->rowid]['id'] = $obj->rowid; + $tmparray[$obj->rowid]['code'] = $obj->code; + $tmparray[$obj->rowid]['label'] = $label; + $i++; + } + + $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1); + + unset($tmparray); + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + + /** + * Return list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...) + * List found into table c_input_reason loaded by loadCacheInputReason + * + * @param int $selected Id or code of type origin to select by default + * @param string $htmlname Nom de la zone select + * @param string $exclude To exclude a code value (Example: SRC_PROP) + * @param int $addempty Add an empty entry + * @return void + */ + public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0) + { + global $langs, $user; + + $this->loadCacheInputReason(); + + print ''; + if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Charge dans cache la liste des types de paiements possibles + * + * @return int Nb of lines loaded, <0 if KO + */ + public function load_cache_types_paiements() + { + // phpcs:enable + global $langs; + + $num = count($this->cache_types_paiements); + if ($num > 0) return $num; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $this->cache_types_paiements = array(); + + $sql = "SELECT id, code, libelle as label, type, active"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_paiement"; + $sql .= " WHERE entity IN (".getEntity('c_paiement').")"; + //if ($active >= 0) $sql.= " AND active = ".$active; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + + // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut + $label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : '')); + $this->cache_types_paiements[$obj->id]['id'] = $obj->id; + $this->cache_types_paiements[$obj->id]['code'] = $obj->code; + $this->cache_types_paiements[$obj->id]['label'] = $label; + $this->cache_types_paiements[$obj->id]['type'] = $obj->type; + $this->cache_types_paiements[$obj->id]['active'] = $obj->active; + $i++; + } + + $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1); + + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of payment modes. + * Constant MAIN_DEFAULT_PAYMENT_TERM_ID can used to set default value but scope is all application, probably not what you want. + * See instead to force the default value by the caller. + * + * @param int $selected Id of payment term to preselect by default + * @param string $htmlname Nom de la zone select + * @param int $filtertype Not used + * @param int $addempty Add an empty entry + * @param int $noinfoadmin 0=Add admin info, 1=Disable admin info + * @param string $morecss Add more CSS on select tag + * @return void + */ + public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '') + { + // phpcs:enable + global $langs, $user, $conf; + + dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG); + + $this->load_cache_conditions_paiements(); + + // Set default value if not already set by caller + if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID; + + print ''; + if ($user->admin && empty($noinfoadmin)) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of payment methods + * Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value but scope is all application, probably not what you want. + * + * @param string $selected Id or code or preselected payment mode + * @param string $htmlname Name of select field + * @param string $filtertype To filter on field type in llx_c_paiement ('CRDT' or 'DBIT' or array('code'=>xx,'label'=>zz)) + * @param int $format 0=id+label, 1=code+code, 2=code+label, 3=id+code + * @param int $empty 1=can be empty, 0 otherwise + * @param int $noadmininfo 0=Add admin info, 1=Disable admin info + * @param int $maxlength Max length of label + * @param int $active Active or not, -1 = all + * @param string $morecss Add more CSS on select tag + * @return void + */ + public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '') + { + // phpcs:enable + global $langs, $user, $conf; + + dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG); + + $filterarray = array(); + if ($filtertype == 'CRDT') $filterarray = array(0, 2, 3); + elseif ($filtertype == 'DBIT') $filterarray = array(1, 2, 3); + elseif ($filtertype != '' && $filtertype != '-1') $filterarray = explode(',', $filtertype); + + $this->load_cache_types_paiements(); + + // Set default value if not already set by caller + if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID; + + print ''; + if ($user->admin && !$noadmininfo) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } + + + /** + * Selection HT or TTC + * + * @param string $selected Id pre-selectionne + * @param string $htmlname Nom de la zone select + * @return string Code of HTML select to chose tax or not + */ + public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type') + { + global $langs; + + $return = ''; + + $return .= ''; + + return $return; + } + + /** + * Return a HTML select list of shipping mode + * + * @param string $selected Id shipping mode pre-selected + * @param string $htmlname Name of select zone + * @param string $filtre To filter list + * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @param string $moreattrib To add more attribute on select + * @return void + */ + public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '') + { + global $langs, $conf, $user; + + $langs->load("admin"); + $langs->load("deliveries"); + + $sql = "SELECT rowid, code, libelle as label"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_shipment_mode"; + $sql .= " WHERE active > 0"; + if ($filtre) $sql .= " AND ".$filtre; + $sql .= " ORDER BY libelle ASC"; + + dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) { + $num = $this->db->num_rows($result); + $i = 0; + if ($num) { + print '"; + if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + } else { + print $langs->trans("NoShippingMethodDefined"); + } + } else { + dol_print_error($this->db); + } + } + + /** + * Display form to select shipping mode + * + * @param string $page Page + * @param int $selected Id of shipping mode + * @param string $htmlname Name of select html field + * @param int $addempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @return void + */ + public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0) + { + global $langs, $db; + + $langs->load("deliveries"); + + if ($htmlname != "none") { + print '
'; + print ''; + print ''; + $this->selectShippingMethod($selected, $htmlname, '', $addempty); + print ''; + print '
'; + } else { + if ($selected) { + $code = $langs->getLabelFromKey($db, $selected, 'c_shipment_mode', 'rowid', 'code'); + print $langs->trans("SendingMethod".strtoupper($code)); + } else { + print " "; + } + } + } + + /** + * Creates HTML last in cycle situation invoices selector + * + * @param string $selected Preselected ID + * @param int $socid Company ID + * + * @return string HTML select + */ + public function selectSituationInvoices($selected = '', $socid = 0) + { + global $langs; + + $langs->load('bills'); + + $opt = ''; + $sql = 'SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'facture'; + $sql .= ' WHERE entity IN ('.getEntity('invoice').')'; + $sql .= ' AND situation_counter>=1'; + $sql .= ' AND fk_soc = '.(int) $socid; + $sql .= ' AND type <> 2'; + $sql .= ' ORDER by situation_cycle_ref, situation_counter desc'; + $resql = $this->db->query($sql); + + if ($resql && $this->db->num_rows($resql) > 0) { + // Last seen cycle + $ref = 0; + while ($obj = $this->db->fetch_object($resql)) { + //Same cycle ? + if ($obj->situation_cycle_ref != $ref) { + // Just seen this cycle + $ref = $obj->situation_cycle_ref; + //not final ? + if ($obj->situation_final != 1) { + //Not prov? + if (substr($obj->ref, 1, 4) != 'PROV') { + if ($selected == $obj->rowid) { + $opt .= ''; + } else { + $opt .= ''; + } + } + } + } + } + } else { + dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR); + } + if ($opt == '') + { + $opt = ''; + } + return $opt; + } + + /** + * Creates HTML units selector (code => label) + * + * @param string $selected Preselected Unit ID + * @param string $htmlname Select name + * @param int $showempty Add a nempty line + * @return string HTML select + */ + public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0) + { + global $langs; + + $langs->load('products'); + + $return = ''; + } + return $return; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return a HTML select list of bank accounts + * + * @param string $selected Id account pre-selected + * @param string $htmlname Name of select zone + * @param int $status Status of searched accounts (0=open, 1=closed, 2=both) + * @param string $filtre To filter list + * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @param string $moreattrib To add more attribute on select + * @param int $showcurrency Show currency in label + * @param string $morecss More CSS + * @return int <0 if error, Num of bank account found if OK (0, 1, 2, ...) + */ + public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '') + { + // phpcs:enable + global $langs, $conf; + + $langs->load("admin"); + $num = 0; + + $sql = "SELECT rowid, label, bank, clos as status, currency_code"; + $sql .= " FROM ".MAIN_DB_PREFIX."bank_account"; + $sql .= " WHERE entity IN (".getEntity('bank_account').")"; + if ($status != 2) $sql .= " AND clos = ".(int) $status; + if ($filtre) $sql .= " AND ".$filtre; + $sql .= " ORDER BY label"; + + dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + $num = $this->db->num_rows($result); + $i = 0; + if ($num) + { + print '"; + } else { + if ($status == 0) print ''.$langs->trans("NoActiveBankAccountDefined").''; + else print ''.$langs->trans("NoBankAccountFound").''; + } + } else { + dol_print_error($this->db); + } + + return $num; + } + + /** + * Return a HTML select list of establishment + * + * @param string $selected Id establishment pre-selected + * @param string $htmlname Name of select zone + * @param int $status Status of searched establishment (0=open, 1=closed, 2=both) + * @param string $filtre To filter list + * @param int $useempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @param string $moreattrib To add more attribute on select + * @return int <0 if error, Num of establishment found if OK (0, 1, 2, ...) + */ + public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '') + { + // phpcs:enable + global $langs, $conf; + + $langs->load("admin"); + $num = 0; + + $sql = "SELECT rowid, name, fk_country, status, entity"; + $sql .= " FROM ".MAIN_DB_PREFIX."establishment"; + $sql .= " WHERE 1=1"; + if ($status != 2) $sql .= " AND status = ".(int) $status; + if ($filtre) $sql .= " AND ".$filtre; + $sql .= " ORDER BY name"; + + dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) + { + $num = $this->db->num_rows($result); + $i = 0; + if ($num) + { + print '"; + } else { + if ($status == 0) print ''.$langs->trans("NoActiveEstablishmentDefined").''; + else print ''.$langs->trans("NoEstablishmentFound").''; + } + } else { + dol_print_error($this->db); + } + } + + /** + * Display form to select bank account + * + * @param string $page Page + * @param int $selected Id of bank account + * @param string $htmlname Name of select html field + * @param int $addempty 1=Add an empty value in list, 2=Add an empty value in list only if there is more than 2 entries. + * @return void + */ + public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0) + { + global $langs; + if ($htmlname != "none") { + print '
'; + print ''; + print ''; + $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty); + if ($nbaccountfound > 0) print ''; + print '
'; + } else { + $langs->load('banks'); + + if ($selected) { + require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; + $bankstatic = new Account($this->db); + $result = $bankstatic->fetch($selected); + if ($result) print $bankstatic->getNomUrl(1); + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Return list of categories having choosed type + * + * @param string|int $type Type of category ('customer', 'supplier', 'contact', 'product', 'member'). Old mode (0, 1, 2, ...) is deprecated. + * @param string $selected Id of category preselected or 'auto' (autoselect category if there is only one element). Not used if $outputmode = 1. + * @param string $htmlname HTML field name + * @param int $maxlength Maximum length for labels + * @param int|string|array $markafterid Keep only or removed all categories including the leaf $markafterid in category tree (exclude) or Keep only of category is inside the leaf starting with this id. + * $markafterid can be an : + * - int (id of category) + * - string (categories ids seprated by comma) + * - array (list of categories ids) + * @param int $outputmode 0=HTML select string, 1=Array + * @param int $include [=0] Removed or 1=Keep only + * @param string $morecss More CSS + * @return string + * @see select_categories() + */ + public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '') + { + // phpcs:enable + global $conf, $langs; + $langs->load("categories"); + + include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; + + // For backward compatibility + if (is_numeric($type)) + { + dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING); + } + + if ($type === Categorie::TYPE_BANK_LINE) + { + // TODO Move this into common category feature + $cate_arbo = array(); + $sql = "SELECT c.label, c.rowid"; + $sql .= " FROM ".MAIN_DB_PREFIX."bank_categ as c"; + $sql .= " WHERE entity = ".$conf->entity; + $sql .= " ORDER BY c.label"; + $result = $this->db->query($sql); + if ($result) + { + $num = $this->db->num_rows($result); + $i = 0; + while ($i < $num) + { + $objp = $this->db->fetch_object($result); + if ($objp) $cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label); + $i++; + } + $this->db->free($result); + } else dol_print_error($this->db); + } else { + $cat = new Categorie($this->db); + $cate_arbo = $cat->get_full_arbo($type, $markafterid, $include); + } + + $output = ''; + $output .= "\n"; + + if ($outputmode) return $outarray; + return $output; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a confirmation HTML form or AJAX popup + * + * @param string $page Url of page to call if confirmation is OK + * @param string $title Title + * @param string $question Question + * @param string $action Action + * @param array $formquestion An array with forms complementary inputs + * @param string $selectedchoice "" or "no" or "yes" + * @param int $useajax 0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=preoutput confirm box with div id=dialog-confirm-xxx + * @param int $height Force height of box + * @param int $width Force width of box + * @return void + * @deprecated + * @see formconfirm() + */ + public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500) + { + // phpcs:enable + dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING); + print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width); + } + + /** + * Show a confirmation HTML form or AJAX popup. + * Easiest way to use this is with useajax=1. + * If you use useajax='xxx', you must also add jquery code to trigger opening of box (with correct parameters) + * just after calling this method. For example: + * print ''."\n"; + * + * @param string $page Url of page to call if confirmation is OK. Can contains parameters (param 'action' and 'confirm' will be reformated) + * @param string $title Title + * @param string $question Question + * @param string $action Action + * @param array $formquestion An array with complementary inputs to add into forms: array(array('label'=> ,'type'=> , )) + * type can be 'hidden', 'text', 'password', 'checkbox', 'radio', 'date', 'morecss', ... + * @param string $selectedchoice '' or 'no', or 'yes' or '1' or '0' + * @param int|string $useajax 0=No, 1=Yes, 2=Yes but submit page with &confirm=no if choice is No, 'xxx'=Yes and preoutput confirm box with div id=dialog-confirm-xxx + * @param int $height Force height of box (0 = auto) + * @param int $width Force width of box ('999' or '90%'). Ignored and forced to 90% on smartphones. + * @param int $disableformtag 1=Disable form tag. Can be used if we are already inside a
section. + * @return string HTML ajax code if a confirm ajax popup is required, Pure HTML code if it's an html form + */ + public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0) + { + global $langs, $conf; + + $more = ''; + $formconfirm = ''; + $inputok = array(); + $inputko = array(); + + // Clean parameters + $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice; + if ($conf->browser->layout == 'phone') $width = '95%'; + + // Set height automatically if not defined + if (empty($height)) { + $height = 210; + if (is_array($formquestion) && count($formquestion) > 2) { + $height += ((count($formquestion) - 2) * 24); + } + } + + if (is_array($formquestion) && !empty($formquestion)) + { + // First add hidden fields and value + foreach ($formquestion as $key => $input) + { + if (is_array($input) && !empty($input)) + { + if ($input['type'] == 'hidden') + { + $more .= ''."\n"; + } + } + } + + // Now add questions + $moreonecolumn = ''; + $more .= '
'."\n"; + foreach ($formquestion as $key => $input) + { + if (is_array($input) && !empty($input)) + { + $size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : ''); + $moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : ''); + $morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : ''); + + if ($input['type'] == 'text') + { + $more .= '
'.$input['label'].'
'."\n"; + } elseif ($input['type'] == 'password') + { + $more .= '
'.$input['label'].'
'."\n"; + } elseif ($input['type'] == 'select') + { + $more .= '
'; + if (!empty($input['label'])) $more .= $input['label'].'
'; + $more .= $this->selectarray($input['name'], $input['values'], $input['default'], 1, 0, 0, $moreattr, 0, 0, 0, '', $morecss); + $more .= '
'."\n"; + } elseif ($input['type'] == 'checkbox') + { + $more .= '
'; + $more .= '
'.$input['label'].'
'; + $more .= ' $selval) + { + $more .= '
'; + if ($i == 0) $more .= '
'.$input['label'].'
'; + else $more .= '
 
'; + $more .= '
'.$input['label'].'
'; + $more .= '
'; + $more .= $this->selectDate($input['value'], $input['name'], 0, 0, 0, '', 1, 0); + $more .= '
'."\n"; + $formquestion[] = array('name'=>$input['name'].'day'); + $formquestion[] = array('name'=>$input['name'].'month'); + $formquestion[] = array('name'=>$input['name'].'year'); + $formquestion[] = array('name'=>$input['name'].'hour'); + $formquestion[] = array('name'=>$input['name'].'min'); + } elseif ($input['type'] == 'other') + { + $more .= '
'; + if (!empty($input['label'])) $more .= $input['label'].'
'; + $more .= $input['value']; + $more .= '
'."\n"; + } elseif ($input['type'] == 'onecolumn') + { + $moreonecolumn .= '
'; + $moreonecolumn .= $input['value']; + $moreonecolumn .= '
'."\n"; + } + } + } + $more .= '
'."\n"; + $more .= $moreonecolumn; + } + + // JQUI method dialog is broken with jmobile, we use standard HTML. + // Note: When using dol_use_jmobile or no js, you must also check code for button use a GET url with action=xxx and check that you also output the confirm code when action=xxx + // See page product/card.php for example + if (!empty($conf->dol_use_jmobile)) $useajax = 0; + if (empty($conf->use_javascript_ajax)) $useajax = 0; + + if ($useajax) + { + $autoOpen = true; + $dialogconfirm = 'dialog-confirm'; + $button = ''; + if (!is_numeric($useajax)) + { + $button = $useajax; + $useajax = 1; + $autoOpen = false; + $dialogconfirm .= '-'.$button; + } + $pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.$action.'&confirm=yes'; + $pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'confirm=no' : ''); + // Add input fields into list of fields to read during submit (inputok and inputko) + if (is_array($formquestion)) + { + foreach ($formquestion as $key => $input) + { + //print "xx ".$key." rr ".is_array($input)."
\n"; + if (is_array($input) && isset($input['name'])) array_push($inputok, $input['name']); + if (isset($input['inputko']) && $input['inputko'] == 1) array_push($inputko, $input['name']); + } + } + // Show JQuery confirm box. + $formconfirm .= ''."\n"; + + $formconfirm .= "\n\n"; + $formconfirm .= ''; + $formconfirm .= "\n"; + } else { + $formconfirm .= "\n\n"; + + if (empty($disableformtag)) $formconfirm .= ''."\n"; + + $formconfirm .= ''."\n"; + $formconfirm .= ''."\n"; + + $formconfirm .= ''."\n"; + + // Line title + $formconfirm .= ''."\n"; + + // Line text + if (!empty($formquestion['text'])) { + $formconfirm .= ''."\n"; + } + + // Line form fields + if ($more) + { + $formconfirm .= ''."\n"; + } + + // Line with question + $formconfirm .= ''; + $formconfirm .= ''; + $formconfirm .= ''; + $formconfirm .= ''; + $formconfirm .= ''."\n"; + + $formconfirm .= '
'.img_picto('', 'recent').' '.$title.'
'.$formquestion['text'].'
'."\n"; + $formconfirm .= $more; + $formconfirm .= '
'.$question.''; + $formconfirm .= $this->selectyesno("confirm", $newselectedchoice); + $formconfirm .= '
'."\n"; + + if (empty($disableformtag)) $formconfirm .= "\n"; + $formconfirm .= '
'; + + $formconfirm .= "\n"; + } + + return $formconfirm; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a form to select a project + * + * @param int $page Page + * @param int $socid Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id) + * @param int $selected Id pre-selected project + * @param string $htmlname Name of select field + * @param int $discard_closed Discard closed projects (0=Keep,1=hide completely except $selected,2=Disable) + * @param int $maxlength Max length + * @param int $forcefocus Force focus on field (works with javascript only) + * @param int $nooutput No print is done. String is returned. + * @return string Return html content + */ + public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0) + { + // phpcs:enable + global $langs; + + require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php'; + require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php'; + + $out = ''; + + $formproject = new FormProjets($this->db); + + $langs->load("project"); + if ($htmlname != "none") + { + $out .= "\n"; + $out .= '
'; + $out .= ''; + $out .= ''; + $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1); + $out .= ''; + $out .= '
'; + } else { + if ($selected) + { + $projet = new Project($this->db); + $projet->fetch($selected); + //print ''.$projet->title.''; + $out .= $projet->getNomUrl(0, '', 1); + } else { + $out .= " "; + } + } + + if (empty($nooutput)) + { + print $out; + return ''; + } + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a form to select payment conditions + * + * @param int $page Page + * @param string $selected Id condition pre-selectionne + * @param string $htmlname Name of select html field + * @param int $addempty Add empty entry + * @return void + */ + public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0) + { + // phpcs:enable + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + $this->select_conditions_paiements($selected, $htmlname, -1, $addempty); + print ''; + print '
'; + } else { + if ($selected) + { + $this->load_cache_conditions_paiements(); + print $this->cache_conditions_paiements[$selected]['label']; + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a form to select a delivery delay + * + * @param int $page Page + * @param string $selected Id condition pre-selectionne + * @param string $htmlname Name of select html field + * @param int $addempty Ajoute entree vide + * @return void + */ + public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0) + { + // phpcs:enable + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + $this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty); + print ''; + print '
'; + } else { + if ($selected) + { + $this->load_cache_availability(); + print $this->cache_availability[$selected]['label']; + } else { + print " "; + } + } + } + + /** + * Output HTML form to select list of input reason (events that triggered an object creation, like after sending an emailing, making an advert, ...) + * List found into table c_input_reason loaded by loadCacheInputReason + * + * @param string $page Page + * @param string $selected Id condition pre-selectionne + * @param string $htmlname Name of select html field + * @param int $addempty Add empty entry + * @return void + */ + public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0) + { + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + $this->selectInputReason($selected, $htmlname, -1, $addempty); + print ''; + print '
'; + } else { + if ($selected) + { + $this->loadCacheInputReason(); + foreach ($this->cache_demand_reason as $key => $val) + { + if ($val['id'] == $selected) + { + print $val['label']; + break; + } + } + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a form + html select a date + * + * @param string $page Page + * @param string $selected Date preselected + * @param string $htmlname Html name of date input fields or 'none' + * @param int $displayhour Display hour selector + * @param int $displaymin Display minutes selector + * @param int $nooutput 1=No print output, return string + * @return string + * @see selectDate() + */ + public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0) + { + // phpcs:enable + global $langs; + + $ret = ''; + + if ($htmlname != "none") + { + $ret .= '
'; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= '
'; + $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0); + $ret .= '
'; + } else { + if ($displayhour) $ret .= dol_print_date($selected, 'dayhour'); + else $ret .= dol_print_date($selected, 'day'); + } + + if (empty($nooutput)) print $ret; + return $ret; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a select form to choose a user + * + * @param string $page Page + * @param string $selected Id of user preselected + * @param string $htmlname Name of input html field. If 'none', we just output the user link. + * @param array $exclude List of users id to exclude + * @param array $include List of users id to include + * @return void + */ + public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '') + { + // phpcs:enable + global $langs; + + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include); + print ''; + print '
'; + } else { + if ($selected) + { + require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php'; + $theuser = new User($this->db); + $theuser->fetch($selected); + print $theuser->getNomUrl(1); + } else { + print " "; + } + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show form with payment mode + * + * @param string $page Page + * @param int $selected Id mode pre-selectionne + * @param string $htmlname Name of select html field + * @param string $filtertype To filter on field type in llx_c_paiement (array('code'=>xx,'label'=>zz)) + * @param int $active Active or not, -1 = all + * @param int $addempty 1=Add empty entry + * @return void + */ + public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0) + { + // phpcs:enable + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active); + print ''; + print '
'; + } else { + if ($selected) + { + $this->load_cache_types_paiements(); + print $this->cache_types_paiements[$selected]['label']; + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show form with multicurrency code + * + * @param string $page Page + * @param string $selected code pre-selectionne + * @param string $htmlname Name of select html field + * @return void + */ + public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code') + { + // phpcs:enable + global $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print $this->selectMultiCurrency($selected, $htmlname, 0); + print ''; + print '
'; + } else { + dol_include_once('/core/lib/company.lib.php'); + print !empty($selected) ? currency_name($selected, 1) : ' '; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show form with multicurrency rate + * + * @param string $page Page + * @param double $rate Current rate + * @param string $htmlname Name of select html field + * @param string $currency Currency code to explain the rate + * @return void + */ + public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '') + { + // phpcs:enable + global $langs, $mysoc, $conf; + + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print ' '; + print ' '; + print ''; + print '
'; + } else { + if (!empty($rate)) + { + print price($rate, 1, $langs, 1, 0); + if ($currency && $rate != 1) print '   ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')'; + } else { + print 1; + } + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a select box with available absolute discounts + * + * @param string $page Page URL where form is shown + * @param int $selected Value pre-selected + * @param string $htmlname Name of SELECT component. If 'none', not changeable. Example 'remise_id'. + * @param int $socid Third party id + * @param float $amount Total amount available + * @param string $filter SQL filter on discounts + * @param int $maxvalue Max value for lines that can be selected + * @param string $more More string to add + * @param int $hidelist 1=Hide list + * @param int $discount_type 0 => customer discount, 1 => supplier discount + * @return void + */ + public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0) + { + // phpcs:enable + global $conf, $langs; + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print '
'; + if (!empty($discount_type)) { + if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) + { + if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice + else $translationKey = 'HasCreditNoteFromSupplier'; + } else { + if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") $translationKey = 'HasAbsoluteDiscountFromSupplier'; + else $translationKey = 'HasCreditNoteFromSupplier'; + } + } else { + if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) + { + if (!$filter || $filter == "fk_facture_source IS NULL") $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice + else $translationKey = 'CompanyHasCreditNote'; + } else { + if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") $translationKey = 'CompanyHasAbsoluteDiscount'; + else $translationKey = 'CompanyHasCreditNote'; + } + } + print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency)); + if (empty($hidelist)) print ': '; + print '
'; + if (empty($hidelist)) + { + print '
'; + $newfilter = 'discount_type='.intval($discount_type); + if (!empty($discount_type)) { + $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available + } else { + $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available + } + if ($filter) $newfilter .= ' AND ('.$filter.')'; + $nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue); + if ($nbqualifiedlines > 0) + { + print '   '; + } + print '
'; + } + if ($more) + { + print '
'; + print $more; + print '
'; + } + print '
'; + } else { + if ($selected) + { + print $selected; + } else { + print "0"; + } + } + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show forms to select a contact + * + * @param string $page Page + * @param Societe $societe Filter on third party + * @param int $selected Id contact pre-selectionne + * @param string $htmlname Name of HTML select. If 'none', we just show contact link. + * @return void + */ + public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid') + { + // phpcs:enable + global $langs, $conf; + + if ($htmlname != "none") + { + print '
'; + print ''; + print ''; + print ''; + print ''; + print ''; + print '
'; + $num = $this->select_contacts($societe->id, $selected, $htmlname); + if ($num == 0) + { + $addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress")); + print ''.$addcontact.''; + } + print '
'; + } else { + if ($selected) + { + require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; + $contact = new Contact($this->db); + $contact->fetch($selected); + print $contact->getFullName($langs); + } else { + print " "; + } + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output html select to select thirdparty + * + * @param string $page Page + * @param string $selected Id preselected + * @param string $htmlname Name of HTML select + * @param string $filter optional filters criteras + * @param int $showempty Add an empty field + * @param int $showtype Show third party type in combolist (customer, prospect or supplier) + * @param int $forcecombo Force to use combo box + * @param array $events Event options. Example: array(array('method'=>'getContacts', 'url'=>dol_buildpath('/core/ajax/contacts.php',1), 'htmlname'=>'contactid', 'params'=>array('add-customer-contact'=>'disabled'))) + * @param int $nooutput No print output. Return it only. + * @return void|string + */ + public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0) + { + // phpcs:enable + global $langs; + + $out = ''; + if ($htmlname != "none") + { + $out .= '
'; + $out .= ''; + $out .= ''; + $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events); + $out .= ''; + $out .= '
'; + } else { + if ($selected) + { + require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; + $soc = new Societe($this->db); + $soc->fetch($selected); + $out .= $soc->getNomUrl($langs); + } else { + $out .= " "; + } + } + + if ($nooutput) return $out; + else print $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Retourne la liste des devises, dans la langue de l'utilisateur + * + * @param string $selected preselected currency code + * @param string $htmlname name of HTML select list + * @deprecated + * @return void + */ + public function select_currency($selected = '', $htmlname = 'currency_id') + { + // phpcs:enable + print $this->selectCurrency($selected, $htmlname); + } + + /** + * Retourne la liste des devises, dans la langue de l'utilisateur + * + * @param string $selected preselected currency code + * @param string $htmlname name of HTML select list + * @param string $mode 0 = Add currency symbol into label, 1 = Add 3 letter iso code + * @return string + */ + public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0) + { + global $conf, $langs, $user; + + $langs->loadCacheCurrencies(''); + + $out = ''; + + if ($selected == 'euro' || $selected == 'euros') $selected = 'EUR'; // Pour compatibilite + + $out .= ''; + if ($user->admin) $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + + // Make select dynamic + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname); + + return $out; + } + + /** + * Return array of currencies in user language + * + * @param string $selected preselected currency code + * @param string $htmlname name of HTML select list + * @param integer $useempty 1=Add empty line + * @return string + */ + public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0) + { + global $db, $conf, $langs, $user; + + $langs->loadCacheCurrencies(''); // Load ->cache_currencies + + $TCurrency = array(); + + $sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'multicurrency'; + $sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')"; + $resql = $db->query($sql); + if ($resql) + { + while ($obj = $db->fetch_object($resql)) $TCurrency[$obj->code] = $obj->code; + } + + $out = ''; + $out .= ''; + // Make select dynamic + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $out .= ajax_combobox($htmlname); + + return $out; + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Load into the cache vat rates of a country + * + * @param string $country_code Country code with quotes ("'CA'", or "'CA,IN,...'") + * @return int Nb of loaded lines, 0 if already loaded, <0 if KO + */ + public function load_cache_vatrates($country_code) + { + // phpcs:enable + global $langs; + + $num = count($this->cache_vatrates); + if ($num > 0) return $num; // Cache already loaded + + dol_syslog(__METHOD__, LOG_DEBUG); + + $sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly"; + $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c"; + $sql .= " WHERE t.fk_pays = c.rowid"; + $sql .= " AND t.active > 0"; + $sql .= " AND c.code IN (".$country_code.")"; + $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC"; + + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + if ($num) + { + for ($i = 0; $i < $num; $i++) + { + $obj = $this->db->fetch_object($resql); + $this->cache_vatrates[$i]['rowid'] = $obj->rowid; + $this->cache_vatrates[$i]['code'] = $obj->code; + $this->cache_vatrates[$i]['txtva'] = $obj->taux; + $this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly; + $this->cache_vatrates[$i]['localtax1'] = $obj->localtax1; + $this->cache_vatrates[$i]['localtax1_type'] = $obj->localtax1_type; + $this->cache_vatrates[$i]['localtax2'] = $obj->localtax2; + $this->cache_vatrates[$i]['localtax2_type'] = $obj->localtax1_type; + + $this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or * + $this->cache_vatrates[$i]['labelallrates'] = $obj->taux.'/'.($obj->localtax1 ? $obj->localtax1 : '0').'/'.($obj->localtax2 ? $obj->localtax2 : '0').($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label + $positiverates = ''; + if ($obj->taux) $positiverates .= ($positiverates ? '/' : '').$obj->taux; + if ($obj->localtax1) $positiverates .= ($positiverates ? '/' : '').$obj->localtax1; + if ($obj->localtax2) $positiverates .= ($positiverates ? '/' : '').$obj->localtax2; + if (empty($positiverates)) $positiverates = '0'; + $this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label + } + + return $num; + } else { + $this->error = ''.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).''; + return -1; + } + } else { + $this->error = ''.$this->db->error().''; + return -2; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Output an HTML select vat rate. + * The name of this function should be selectVat. We keep bad name for compatibility purpose. + * + * @param string $htmlname Name of HTML select field + * @param float|string $selectedrate Force preselected vat rate. Can be '8.5' or '8.5 (NOO)' for example. Use '' for no forcing. + * @param Societe $societe_vendeuse Thirdparty seller + * @param Societe $societe_acheteuse Thirdparty buyer + * @param int $idprod Id product. O if unknown of NA. + * @param int $info_bits Miscellaneous information on line (1 for NPR) + * @param int|string $type ''=Unknown, 0=Product, 1=Service (Used if idprod not defined) + * Si vendeur non assujeti a TVA, TVA par defaut=0. Fin de regle. + * Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle. + * Si (vendeur et acheteur dans Communaute europeenne) et bien vendu = moyen de transports neuf (auto, bateau, avion), TVA par defaut=0 (La TVA doit etre paye par l'acheteur au centre d'impots de son pays et non au vendeur). Fin de regle. + * Si vendeur et acheteur dans Communauté européenne et acheteur= particulier alors TVA par défaut=TVA du produit vendu. Fin de règle. + * Si vendeur et acheteur dans Communauté européenne et acheteur= entreprise alors TVA par défaut=0. Fin de règle. + * Sinon la TVA proposee par defaut=0. Fin de regle. + * @param bool $options_only Return HTML options lines only (for ajax treatment) + * @param int $mode 0=Use vat rate as key in combo list, 1=Add VAT code after vat rate into key, -1=Use id of vat line as key + * @return string + */ + public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0) + { + // phpcs:enable + global $langs, $conf, $mysoc; + + $langs->load('errors'); + + $return = ''; + + // Define defaultnpr, defaultttx and defaultcode + $defaultnpr = ($info_bits & 0x01); + $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr); + $defaulttx = str_replace('*', '', $selectedrate); + $defaultcode = ''; + if (preg_match('/\((.*)\)/', $defaulttx, $reg)) + { + $defaultcode = $reg[1]; + $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx); + } + //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode); + + // Check parameters + if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) + { + if ($societe_vendeuse->id == $mysoc->id) + { + $return .= ''.$langs->trans("ErrorYourCountryIsNotDefined").''; + } else { + $return .= ''.$langs->trans("ErrorSupplierCountryIsNotDefined").''; + } + return $return; + } + + //var_dump($societe_acheteuse); + //print "name=$name, selectedrate=$selectedrate, seller=".$societe_vendeuse->country_code." buyer=".$societe_acheteuse->country_code." buyer is company=".$societe_acheteuse->isACompany()." idprod=$idprod, info_bits=$info_bits type=$type"; + //exit; + + // Define list of countries to use to search VAT rates to show + // First we defined code_country to use to find list + if (is_object($societe_vendeuse)) + { + $code_country = "'".$societe_vendeuse->country_code."'"; + } else { + $code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente + } + if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) // If option to have vat for end customer for services is on + { + require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; + if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) + { + // We also add the buyer + if (is_numeric($type)) + { + if ($type == 1) // We know product is a service + { + $code_country .= ",'".$societe_acheteuse->country_code."'"; + } + } elseif (!$idprod) // We don't know type of product + { + $code_country .= ",'".$societe_acheteuse->country_code."'"; + } else { + $prodstatic = new Product($this->db); + $prodstatic->fetch($idprod); + if ($prodstatic->type == Product::TYPE_SERVICE) // We know product is a service + { + $code_country .= ",'".$societe_acheteuse->country_code."'"; + } + } + } + } + + // Now we get list + $num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error + + if ($num > 0) + { + // Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '') + if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) + { + $tmpthirdparty = new Societe($this->db); + $defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod); + $defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod); + if (preg_match('/\((.*)\)/', $defaulttx, $reg)) { + $defaultcode = $reg[1]; + $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx); + } + if (empty($defaulttx)) $defaultnpr = 0; + } + + // Si taux par defaut n'a pu etre determine, on prend dernier de la liste. + // Comme ils sont tries par ordre croissant, dernier = plus eleve = taux courant + if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) + { + if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) $defaulttx = $this->cache_vatrates[$num - 1]['txtva']; + else $defaulttx = ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS == 'none' ? '' : $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS); + } + + // Disabled if seller is not subject to VAT + $disabled = false; $title = ''; + if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") + { + // Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead + // of using supplier invoices (this is a very bad idea !) + if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) + { + $title = ' title="'.$langs->trans('VATIsNotUsed').'"'; + $disabled = true; + } + } + + if (!$options_only) $return .= ''; + } else { + $return .= $this->error; + } + + $this->num = $num; + return $return; + } + + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes. + * Fields are preselected with : + * - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM') + * - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location) + * - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1) + * + * @param integer $set_time Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2). + * @param string $prefix Prefix for fields name + * @param int $h 1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty + * @param int $m 1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty + * @param int $empty 0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only + * @param string $form_name Not used + * @param int $d 1=Show days, month, years + * @param int $addnowlink Add a link "Now" + * @param int $nooutput Do not output html string but return it + * @param int $disabled Disable input fields + * @param int $fullday When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59 + * @param string $addplusone Add a link "+1 hour". Value must be name of another select_date field. + * @param datetime $adddateof Add a link "Date of invoice" using the following date. + * @return string|void Nothing or string if nooutput is 1 + * @deprecated + * @see selectDate(), form_date(), select_month(), select_year(), select_dayofweek() + */ + public function select_date($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $nooutput = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '') + { + // phpcs:enable + $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof); + if (!empty($nooutput)) { + return $retstring; + } + print $retstring; + return; + } + + /** + * Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minutes. + * Fields are preselected with : + * - set_time date (must be a local PHP server timestamp or string date with format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM') + * - local date in user area, if set_time is '' (so if set_time is '', output may differs when done from two different location) + * - Empty (fields empty), if set_time is -1 (in this case, parameter empty must also have value 1) + * + * @param integer $set_time Pre-selected date (must be a local PHP server timestamp), -1 to keep date not preselected, '' to use current date with 00:00 hour (Parameter 'empty' must be 0 or 2). + * @param string $prefix Prefix for fields name + * @param int $h 1 or 2=Show also hours (2=hours on a new line), -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show hour always empty + * @param int $m 1=Show also minutes, -1 has same effect but hour and minutes are prefilled with 23:59 if date is empty, 3 show minutes always empty + * @param int $empty 0=Fields required, 1=Empty inputs are allowed, 2=Empty inputs are allowed for hours only + * @param string $form_name Not used + * @param int $d 1=Show days, month, years + * @param int $addnowlink Add a link "Now", 1 with server time, 2 with local computer time + * @param int $disabled Disable input fields + * @param int $fullday When a checkbox with this html name is on, hour and day are set with 00:00 or 23:59 + * @param string $addplusone Add a link "+1 hour". Value must be name of another selectDate field. + * @param datetime $adddateof Add a link "Date of ..." using the following date. See also $labeladddateof for the label used. + * @param string $openinghours Specify hour start and hour end for the select ex 8,20 + * @param int $stepminutes Specify step for minutes between 1 and 30 + * @param string $labeladddateof Label to use for the $adddateof parameter. + * @return string Html for selectDate + * @see form_date(), select_month(), select_year(), select_dayofweek() + */ + public function selectDate($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '', $openinghours = '', $stepminutes = 1, $labeladddateof = '') + { + global $conf, $langs; + + $retstring = ''; + + if ($prefix == '') $prefix = 're'; + if ($h == '') $h = 0; + if ($m == '') $m = 0; + $emptydate = 0; + $emptyhours = 0; + if ($stepminutes <= 0 || $stepminutes > 30) $stepminutes = 1; + if ($empty == 1) { $emptydate = 1; $emptyhours = 1; } + if ($empty == 2) { $emptydate = 0; $emptyhours = 1; } + $orig_set_time = $set_time; + + if ($set_time === '' && $emptydate == 0) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; + $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone + } + + // Analysis of the pre-selection date + if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) // deprecated usage + { + // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' + $syear = (!empty($reg[1]) ? $reg[1] : ''); + $smonth = (!empty($reg[2]) ? $reg[2] : ''); + $sday = (!empty($reg[3]) ? $reg[3] : ''); + $shour = (!empty($reg[4]) ? $reg[4] : ''); + $smin = (!empty($reg[5]) ? $reg[5] : ''); + } elseif (strval($set_time) != '' && $set_time != -1) + { + // set_time est un timestamps (0 possible) + $syear = dol_print_date($set_time, "%Y"); + $smonth = dol_print_date($set_time, "%m"); + $sday = dol_print_date($set_time, "%d"); + if ($orig_set_time != '') + { + $shour = dol_print_date($set_time, "%H"); + $smin = dol_print_date($set_time, "%M"); + $ssec = dol_print_date($set_time, "%S"); + } else { + $shour = ''; + $smin = ''; + $ssec = ''; + } + } else { + // Date est '' ou vaut -1 + $syear = ''; + $smonth = ''; + $sday = ''; + $shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR; + $smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN; + $ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC; + } + if ($h == 3) $shour = ''; + if ($m == 3) $smin = ''; + + // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery' + $usecalendar = 'combo'; + if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) { + $usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR); + } + + if ($d) + { + // Show date with popup + if ($usecalendar != 'combo') + { + $formated_date = ''; + //print "e".$set_time." t ".$conf->format_date_short; + if (strval($set_time) != '' && $set_time != -1) + { + //$formated_date=dol_print_date($set_time,$conf->format_date_short); + $formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput")); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript + } + + // Calendrier popup version eldy + if ($usecalendar == "eldy") + { + // Zone de saisie manuelle de la date + $retstring .= 'trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript + $retstring .= '>'; + + // Icone calendrier + if (!$disabled) + { + $retstring .= ''; + } else $retstring .= ''; + + $retstring .= ''."\n"; + $retstring .= ''."\n"; + $retstring .= ''."\n"; + } elseif ($usecalendar == 'jquery') + { + if (!$disabled) + { + // Output javascript for datepicker + $retstring .= ""; + } + + // Zone de saisie manuelle de la date + $retstring .= '
'; + $retstring .= 'trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript + $retstring .= '>'; + + // Icone calendrier + if (!$disabled) + { + /* Not required. Managed by option buttonImage of jquery + $retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"'); + $retstring.="";*/ + } else { + $retstring .= ''; + } + + $retstring .= '
'; + $retstring .= ''."\n"; + $retstring .= ''."\n"; + $retstring .= ''."\n"; + } else { + $retstring .= "Bad value of MAIN_POPUP_CALENDAR"; + } + } + // Show date with combo selects + else { + //$retstring.='
'; + // Day + $retstring .= ''; + + if ($emptydate || $set_time == -1) + { + $retstring .= ''; + } + + for ($day = 1; $day <= 31; $day++) + { + $retstring .= ''; + } + + $retstring .= ""; + + $retstring .= ''; + if ($emptydate || $set_time == -1) + { + $retstring .= ''; + } + + // Month + for ($month = 1; $month <= 12; $month++) + { + $retstring .= '"; + } + $retstring .= ""; + + // Year + if ($emptydate || $set_time == -1) + { + $retstring .= ''; + } else { + $retstring .= ''; + + for ($year = $syear - 10; $year < $syear + 10; $year++) + { + $retstring .= ''; + } + $retstring .= "\n"; + } + //$retstring.='
'; + } + } + + if ($d && $h) $retstring .= ($h == 2 ? '
' : ' '); + + if ($h) + { + $hourstart = 0; + $hourend = 24; + if ($openinghours != '') { + $openinghours = explode(',', $openinghours); + $hourstart = $openinghours[0]; + $hourend = $openinghours[1]; + if ($hourend < $hourstart) $hourend = $hourstart; + } + // Show hour + $retstring .= ''; + if ($emptyhours) $retstring .= ''; + for ($hour = $hourstart; $hour < $hourend; $hour++) + { + if (strlen($hour) < 2) $hour = "0".$hour; + $retstring .= ''; + } + $retstring .= ''; + if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":"; + } + + if ($m) + { + // Show minutes + $retstring .= ''; + if ($emptyhours) $retstring .= ''; + for ($min = 0; $min < 60; $min += $stepminutes) + { + if (strlen($min) < 2) $min = "0".$min; + $retstring .= ''; + } + $retstring .= ''; + + $retstring .= ''; + } + + // Add a "Now" link + if ($conf->use_javascript_ajax && $addnowlink) + { + // Script which will be inserted in the onClick of the "Now" link + $reset_scripts = ""; + if ($addnowlink == 2) // local computer time + { + // pad add leading 0 on numbers + $reset_scripts .= "Number.prototype.pad = function(size) { + var s = String(this); + while (s.length < (size || 2)) {s = '0' + s;} + return s; + }; + var d = new Date();"; + } + + // Generate the date part, depending on the use or not of the javascript calendar + if ($addnowlink == 1) // server time expressed in user time setup + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date(dol_now(), 'day', 'tzuser').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date(dol_now(), '%d', 'tzuser').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date(dol_now(), '%m', 'tzuser').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date(dol_now(), '%Y', 'tzuser').'\');'; + } elseif ($addnowlink == 2) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());'; + } + /*if ($usecalendar == "eldy") + { + $base=DOL_URL_ROOT.'/core/'; + $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');'; + } + else + { + $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); '; + $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); '; + $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); '; + }*/ + // Update the hour part + if ($h) + { + if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; + //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); '; + if ($addnowlink == 1) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date(dol_now(), '%H', 'tzuser').'\');'; + } elseif ($addnowlink == 2) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(d.getHours().pad());'; + } + + if ($fullday) $reset_scripts .= ' } '; + } + // Update the minute part + if ($m) + { + if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; + //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); '; + if ($addnowlink == 1) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date(dol_now(), '%M', 'tzuser').'\');'; + } elseif ($addnowlink == 2) + { + $reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(d.getMinutes().pad());'; + } + if ($fullday) $reset_scripts .= ' } '; + } + // If reset_scripts is not empty, print the link with the reset_scripts in the onClick + if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) + { + $retstring .= ' '; + } + } + + // Add a "Plus one hour" link + if ($conf->use_javascript_ajax && $addplusone) + { + // Script which will be inserted in the onClick of the "Add plusone" link + $reset_scripts = ""; + + // Generate the date part, depending on the use or not of the javascript calendar + $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date(dol_now(), 'day').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.dol_print_date(dol_now(), '%d').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.dol_print_date(dol_now(), '%m').'\');'; + $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.dol_print_date(dol_now(), '%Y').'\');'; + // Update the hour part + if ($h) + { + if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; + $reset_scripts .= 'jQuery(\'#'.$prefix.'hour\').val(\''.dol_print_date(dol_now(), '%H').'\');'; + if ($fullday) $reset_scripts .= ' } '; + } + // Update the minute part + if ($m) + { + if ($fullday) $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {"; + $reset_scripts .= 'jQuery(\'#'.$prefix.'min\').val(\''.dol_print_date(dol_now(), '%M').'\');'; + if ($fullday) $reset_scripts .= ' } '; + } + // If reset_scripts is not empty, print the link with the reset_scripts in the onClick + if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) + { + $retstring .= ' '; + } + } + + // Add a "Plus one hour" link + if ($conf->use_javascript_ajax && $adddateof) + { + $tmparray = dol_getdate($adddateof); + if (empty($labeladddateof)) $labeladddateof = $langs->trans("DateInvoice"); + $retstring .= ' - '; + $out .= ''; + $out .= '
'; + + return $out; + } + + /** + * Return HTML to show the search and clear seach button + * + * @param string $cssclass CSS class + * @param int $calljsfunction 0=default. 1=call function initCheckForSelect() after changing status of checkboxes + * @param string $massactionname Mass action button name that will launch an action on the selected items + * @return string + */ + public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction") + { + global $conf, $langs; + + $out = ''; + $id = uniqid(); + if (!empty($conf->use_javascript_ajax)) $out .= '
'; + $out .= ''; + + return $out; + } + + /** + * Return HTML to show the search and clear seach button + * + * @param int $addcheckuncheckall Add the check all/uncheck all checkbox (use javascript) and code to manage this + * @param string $cssclass CSS class + * @param int $calljsfunction 0=default. 1=call function initCheckForSelect() after changing status of checkboxes + * @param string $massactionname Mass action name + * @return string + */ + public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction") + { + $out = $this->showFilterButtons(); + if ($addcheckuncheckall) + { + $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname); + } + return $out; + } + + /** + * Return HTML to show the select of expense categories + * + * @param string $selected preselected category + * @param string $htmlname name of HTML select list + * @param integer $useempty 1=Add empty line + * @param array $excludeid id to exclude + * @param string $target htmlname of target select to bind event + * @param int $default_selected default category to select if fk_c_type_fees change = EX_KME + * @param array $params param to give + * @return string + */ + public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array()) + { + global $db, $conf, $langs, $user; + + $out = ''; + $sql = 'SELECT rowid, label FROM '.MAIN_DB_PREFIX.'c_exp_tax_cat WHERE active = 1'; + $sql .= ' AND entity IN (0,'.getEntity('exp_tax_cat').')'; + if (!empty($excludeid)) $sql .= ' AND rowid NOT IN ('.implode(',', $excludeid).')'; + $sql .= ' ORDER BY label'; + + $resql = $db->query($sql); + if ($resql) + { + $out = ''; + if (!empty($htmlname) && $user->admin) $out .= ' '.info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1); + + if (!empty($target)) + { + $sql = "SELECT c.id FROM ".MAIN_DB_PREFIX."c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1"; + $resql = $db->query($sql); + if ($resql) + { + if ($db->num_rows($resql) > 0) + { + $obj = $db->fetch_object($resql); + $out .= ''; + } + } + } + } else { + dol_print_error($db); + } + + return $out; + } + + /** + * Return HTML to show the select ranges of expense range + * + * @param string $selected preselected category + * @param string $htmlname name of HTML select list + * @param integer $useempty 1=Add empty line + * @return string + */ + public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0) + { + global $db, $conf, $langs; + + $out = ''; + $sql = 'SELECT rowid, range_ik FROM '.MAIN_DB_PREFIX.'c_exp_tax_range'; + $sql .= ' WHERE entity = '.$conf->entity.' AND active = 1'; + + $resql = $db->query($sql); + if ($resql) + { + $out = ''; + } else { + dol_print_error($db); + } + + return $out; + } + + /** + * Return HTML to show a select of expense + * + * @param string $selected preselected category + * @param string $htmlname name of HTML select list + * @param integer $useempty 1=Add empty choice + * @param integer $allchoice 1=Add all choice + * @param integer $useid 0=use 'code' as key, 1=use 'id' as key + * @return string + */ + public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0) + { + global $db, $langs; + + $out = ''; + $sql = 'SELECT id, code, label FROM '.MAIN_DB_PREFIX.'c_type_fees'; + $sql .= ' WHERE active = 1'; + + $resql = $db->query($sql); + if ($resql) + { + $out = ''; + } else { + dol_print_error($db); + } + + return $out; + } + + /** + * Output a combo list with invoices qualified for a third party + * + * @param int $socid Id third party (-1=all, 0=only projects not linked to a third party, id=projects not linked or linked to third party id) + * @param int $selected Id invoice preselected + * @param string $htmlname Name of HTML select + * @param int $maxlength Maximum length of label + * @param int $option_only Return only html options lines without the select tag + * @param string $show_empty Add an empty line ('1' or string to show for empty line) + * @param int $discard_closed Discard closed projects (0=Keep,1=hide completely,2=Disable) + * @param int $forcefocus Force focus on field (works with javascript only) + * @param int $disabled Disabled + * @param string $morecss More css added to the select component + * @param string $projectsListId ''=Automatic filter on project allowed. List of id=Filter on project ids. + * @param string $showproject 'all' = Show project info, ''=Hide project info + * @param User $usertofilter User object to use for filtering + * @return int Nbr of project if OK, <0 if KO + */ + public function selectInvoice($socid = -1, $selected = '', $htmlname = 'invoiceid', $maxlength = 24, $option_only = 0, $show_empty = '1', $discard_closed = 0, $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500', $projectsListId = '', $showproject = 'all', $usertofilter = null) + { + global $user, $conf, $langs; + + require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; + + if (is_null($usertofilter)) + { + $usertofilter = $user; + } + + $out = ''; + + $hideunselectables = false; + if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) $hideunselectables = true; + + if (empty($projectsListId)) + { + if (empty($usertofilter->rights->projet->all->lire)) + { + $projectstatic = new Project($this->db); + $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1); + } + } + + // Search all projects + $sql = 'SELECT f.rowid, f.ref as fref, "nolabel" as flabel, p.rowid as pid, f.ref, + p.title, p.fk_soc, p.fk_statut, p.public,'; + $sql .= ' s.nom as name'; + $sql .= ' FROM '.MAIN_DB_PREFIX.'projet as p'; + $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc,'; + $sql .= ' '.MAIN_DB_PREFIX.'facture as f'; + $sql .= " WHERE p.entity IN (".getEntity('project').")"; + $sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement + //if ($projectsListId) $sql.= " AND p.rowid IN (".$projectsListId.")"; + //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)"; + //if ($socid > 0) $sql.= " AND (p.fk_soc=".$socid." OR p.fk_soc IS NULL)"; + $sql .= " GROUP BY f.ref ORDER BY p.ref, f.ref ASC"; + + $resql = $this->db->query($sql); + if ($resql) + { + // Use select2 selector + if (!empty($conf->use_javascript_ajax)) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; + $comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus); + $out .= $comboenhancement; + $morecss = 'minwidth200imp maxwidth500'; + } + + if (empty($option_only)) { + $out .= ''; + } + + print $out; + + $this->db->free($resql); + return $num; + } else { + dol_print_error($this->db); + return -1; + } + } + + /** + * Output the component to make advanced search criteries + * + * @param array $arrayofcriterias Array of available search criterias. Example: array($object->element => $object->fields, 'otherfamily' => otherarrayoffields, ...) + * @param array $search_component_params Array of selected search criterias + * @param array $arrayofinputfieldsalreadyoutput Array of input fields already inform. The component will not generate a hidden input field if it is in this list. + * @return string HTML component for advanced search + */ + public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array()) + { + global $conf, $langs; + + $ret = ''; + + $ret .= '
'; + //$ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= $langs->trans("Filters"); + $ret .= ''; + //$ret .= ''; + $ret .= '
'; + $texttoshow = '
'.$langs->trans("Search").'
'; + + $ret .= '
'.$texttoshow.'
'; + $ret .= '
'; + $ret .= ''; + // For compatibility with forms that show themself the search criteria in addition of this component, we output the fields + foreach ($arrayofcriterias as $criterias) { + foreach ($criterias as $criteriafamilykey => $criteriafamilyval) { + if (in_array('search_'.$criteriafamilykey, $arrayofinputfieldsalreadyoutput)) continue; + if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) continue; + if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) { + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + $ret .= ''; + } else { + $ret .= ''; + } + } + } + $ret .= '
'; + + + return $ret; + } +} From c00b55c9804979f61f37c5e1cdbcfd31b7b199c2 Mon Sep 17 00:00:00 2001 From: Anthony Berton <34568357+bb2a@users.noreply.github.com> Date: Thu, 11 Jun 2020 18:45:35 +0200 Subject: [PATCH 19/22] Update 11.0.0-12.0.0.sql --- htdocs/install/mysql/migration/11.0.0-12.0.0.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql index 9cf154e1c11..a9099ee49c0 100644 --- a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql +++ b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql @@ -67,7 +67,6 @@ ALTER TABLE llx_facturedet_rec_extrafields ADD INDEX idx_facturedet_rec_extrafie ALTER TABLE llx_facture_rec MODIFY COLUMN titre varchar(200) NOT NULL; -- This var is per entity now, so we remove const if global if exists - delete from llx_const where name in ('PROJECT_HIDE_TASKS', 'MAIN_BUGTRACK_ENABLELINK', 'MAIN_HELP_DISABLELINK') and entity = 0; -- For v12 From c27eb5e623dd96158e8a1bdf257d654aac934b7d Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 12 Jun 2020 01:03:34 +0200 Subject: [PATCH 20/22] Update llx_const.sql --- htdocs/install/mysql/data/llx_const.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/install/mysql/data/llx_const.sql b/htdocs/install/mysql/data/llx_const.sql index 47f13e3585a..99da23f53a5 100644 --- a/htdocs/install/mysql/data/llx_const.sql +++ b/htdocs/install/mysql/data/llx_const.sql @@ -101,5 +101,5 @@ insert into llx_const (name, value, type, visible, entity) VALUES ('USER_ADDON_P -- -- PRODUCTS -- -INSERT INTO llx_const (rowid, name, entity, value, type, visible, note, tms) VALUES (NULL, 'PRODUCT_PRICE_BASE_TYPE', '1', 'HT', 'string', '0', NULL, CURRENT_TIMESTAMP); +INSERT INTO llx_const (name, entity, value, type, visible) VALUES ('PRODUCT_PRICE_BASE_TYPE', 0, 'HT', 'string', 0); From dc87d892043d7c451ca92e0fc2bc2ff0fb4d50a8 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 12 Jun 2020 01:03:58 +0200 Subject: [PATCH 21/22] Update 12.0.0-13.0.0.sql --- htdocs/install/mysql/migration/12.0.0-13.0.0.sql | 2 +- 1 file changed, 1 insertion(+), 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 d4f4a9a645b..14c176b1c20 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 @@ -46,7 +46,7 @@ UPDATE llx_establishment SET ref = rowid WHERE ref IS NULL; ALTER TABLE llx_establishment MODIFY COLUMN ref varchar(30) NOT NULL; ALTER TABLE llx_establishment MODIFY COLUMN name varchar(128); -INSERT INTO llx_const (name, entity, value, type, visible, note) VALUES ('PRODUCT_PRICE_BASE_TYPE', '0', 'HT', 'string', '0', NULL); +INSERT INTO llx_const (name, entity, value, type, visible) VALUES ('PRODUCT_PRICE_BASE_TYPE', 0, 'HT', 'string', 0); ALTER TABLE llx_subscription MODIFY COLUMN datef DATETIME; From cc9a964b5d243a94b66f21a5c88cc1ef062ed3db Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Fri, 12 Jun 2020 01:58:47 +0200 Subject: [PATCH 22/22] Update products.lang --- htdocs/langs/en_US/products.lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htdocs/langs/en_US/products.lang b/htdocs/langs/en_US/products.lang index 2f719d5cee1..a1bbc45f970 100644 --- a/htdocs/langs/en_US/products.lang +++ b/htdocs/langs/en_US/products.lang @@ -106,7 +106,7 @@ NoteNotVisibleOnBill=Note (not visible on invoices, proposals...) ServiceLimitedDuration=If product is a service with limited duration: 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) +DefaultPriceType=Base of prices per default (with versus without tax) when adding new sale prices AssociatedProductsAbility=Activate virtual products (kits) AssociatedProducts=Virtual products AssociatedProductsNumber=Number of products composing this virtual product