';
$choice .= ''.$langs->trans("Upgrade").' '.$newversionfrom.$newversionfrombis.' -> '.$newversionto.' ';
- $choice .= '';
+ $choice .= ' ';
$choice .= $langs->trans("UpgradeDesc");
if ($recommended_choice)
diff --git a/htdocs/install/default.css b/htdocs/install/default.css
index 257f2d99af8..9725b06b8bc 100644
--- a/htdocs/install/default.css
+++ b/htdocs/install/default.css
@@ -1,5 +1,5 @@
/* Copyright (C) 2004 Rodolphe Quiedeville
- * Copyright (C) 2009-2016 Laurent Destailleur
+ * Copyright (C) 2009-2017 Laurent Destailleur
*
* 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
@@ -210,7 +210,9 @@ table.listofchoices, tr.listofchoices, td.listofchoices {
tr.listofchoices {
height: 42px;
}
-
+.listofchoicesdesc {
+ color: #999 !important;
+}
.blinkwait {
font-weight: bold;
text-decoration:blink !important;
@@ -244,9 +246,8 @@ font.warning {
div.error {
color: #550000;
font-weight: bold;
- padding: 0.2em 0.2em 0.2em 0.2em;
+ padding: 0.2em 0.2em 0.2em 0;
margin: 0.5em 0 0.5em 0;
- border: 1px solid #6C7C8B;
}
font.error {
color: #550000;
diff --git a/htdocs/install/fileconf.php b/htdocs/install/fileconf.php
index 2b8f57d9d3c..cdd639ad50b 100644
--- a/htdocs/install/fileconf.php
+++ b/htdocs/install/fileconf.php
@@ -45,13 +45,13 @@ dolibarr_install_syslog("--- fileconf: entering fileconf.php page");
// install.forced.php into directory htdocs/install (This is the case with some wizard
// installer like DoliWamp, DoliMamp or DoliBuntu).
// We first init "forced values" to nothing.
-if (! isset($force_install_noedit)) $force_install_noedit=''; // 1=To block var specific to distrib, 2 to block all technical parameters
+if (! isset($force_install_noedit)) $force_install_noedit=''; // 1=To block vars specific to distrib, 2 to block all technical parameters
if (! isset($force_install_type)) $force_install_type='';
if (! isset($force_install_dbserver)) $force_install_dbserver='';
if (! isset($force_install_port)) $force_install_port='';
if (! isset($force_install_database)) $force_install_database='';
-if (! isset($force_install_prefix)) $force_install_prefix='';
-if (! isset($force_install_createdatabase)) $force_install_createdatabase='';
+if (! isset($force_install_prefix)) $force_install_prefix='';
+if (! isset($force_install_createdatabase)) $force_install_createdatabase='';
if (! isset($force_install_databaselogin)) $force_install_databaselogin='';
if (! isset($force_install_databasepass)) $force_install_databasepass='';
if (! isset($force_install_databaserootlogin)) $force_install_databaserootlogin='';
@@ -324,7 +324,7 @@ if (! empty($force_install_message))
?>
>
diff --git a/htdocs/install/mysql/data/llx_c_action_trigger.sql b/htdocs/install/mysql/data/llx_c_action_trigger.sql
index 9aadb0c083e..d25963e8c3a 100644
--- a/htdocs/install/mysql/data/llx_c_action_trigger.sql
+++ b/htdocs/install/mysql/data/llx_c_action_trigger.sql
@@ -78,6 +78,10 @@ insert into llx_c_action_trigger (code,label,description,elementtype,rang) value
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('FICHINTER_SENTBYMAIL','Intervention sent by mail','Executed when a intervention is sent by mail','ficheinter',35);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PRODUCT_CREATE','Product or service created','Executed when a product or sevice is created','product',40);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PRODUCT_DELETE','Product or service deleted','Executed when a product or sevice is deleted','product',42);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_CREATE','Expense report created','Executed when an expense report is created','expensereport',201);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_VALIDATE','Expense report validated','Executed when an expense report is validated','expensereport',202);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_APPROVE','Expense report approved','Executed when an expense report is approved','expensereport',203);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_PAYED','Expense report billed','Executed when an expense report is set as billed','expensereport',204);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PROJECT_VALIDATE','Project validation','Executed when a project is validated','project',141);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PROJECT_DELETE','Project deleted','Executed when a project is deleted','project',143);
-- actions not enabled by default (no constant created for that) when we enable module agenda
diff --git a/htdocs/install/mysql/data/llx_c_hrm_department.sql b/htdocs/install/mysql/data/llx_c_hrm_department.sql
index e6e4921f372..c4865ad9e30 100644
--- a/htdocs/install/mysql/data/llx_c_hrm_department.sql
+++ b/htdocs/install/mysql/data/llx_c_hrm_department.sql
@@ -24,20 +24,16 @@
--
INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(1, 5,'MANAGEMENT', 'Management', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(2, 10,'GESTION', 'Gestion', 1);
INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(3, 15,'TRAINING', 'Training', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(4, 20,'IT', 'Inform. Technology (IT)', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(5, 25,'MARKETING', 'Marketing', 1);
+INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(4, 20,'IT', 'Inform. Technology (IT)', 0);
+INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(5, 25,'MARKETING', 'Marketing', 0);
INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(6, 30,'SALES', 'Sales', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(7, 35,'LEGAL', 'Legal', 1);
+INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(7, 35,'LEGAL', 'Legal', 0);
INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(8, 40,'FINANCIAL', 'Financial accounting', 1);
INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(9, 45,'HUMANRES', 'Human resources', 1);
INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(10, 50,'PURCHASING', 'Purchasing', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(11, 55,'SERVICES', 'Services', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(12, 60,'CUSTOMSERV', 'Customer service', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(13, 65,'CONSULTING', 'Consulting', 1);
+INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(12, 60,'CUSTOMSERV', 'Customer service', 0);
INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(14, 70,'LOGISTIC', 'Logistics', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(15, 75,'CONSTRUCT', 'Engineering/design', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(16, 80,'PRODUCTION', 'Manufacturing', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(17, 85,'QUALITY', 'Quality assurance', 1);
-INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(18, 85,'MAINT', 'Plant assurance', 1);
+INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(15, 75,'CONSTRUCT', 'Engineering/design', 0);
+INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(16, 80,'PRODUCTION', 'Production', 1);
+INSERT INTO llx_c_hrm_department (rowid, pos, code, label, active) VALUES(17, 85,'QUALITY', 'Quality assurance', 0);
\ No newline at end of file
diff --git a/htdocs/install/mysql/data/llx_c_tva.sql b/htdocs/install/mysql/data/llx_c_tva.sql
index 81c72eccb8f..3b401abd37d 100644
--- a/htdocs/install/mysql/data/llx_c_tva.sql
+++ b/htdocs/install/mysql/data/llx_c_tva.sql
@@ -96,12 +96,14 @@ insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) values (80
-- FRANCE (id country=1)
insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) values ( 11, 1, '20','0','VAT standard rate (France hors DOM-TOM)',1);
-insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) values ( 12, 1, '8.5','0','VAT standard rate (DOM sauf Guyane et Saint-Martin)',0);
-insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) values ( 13, 1, '8.5','1','VAT standard rate (DOM sauf Guyane et Saint-Martin), non perçu par le vendeur mais récupérable par acheteur',0);
insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) values ( 14, 1, '5.5','0','VAT reduced rate (France hors DOM-TOM)',1);
insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) values ( 15, 1, '0','0','VAT Rate 0 ou non applicable',1);
insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) values ( 16, 1, '2.1','0','VAT super-reduced rate',1);
insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) values ( 17, 1, '10','0','VAT reduced rate',1);
+insert into llx_c_tva(fk_pays,taux,code,recuperableonly,note,active) values (1, '8.5', '85', '0','VAT standard rate (DOM sauf Guyane et Saint-Martin)',0);
+insert into llx_c_tva(fk_pays,taux,code,recuperableonly,note,active) values (1, '8.5', '85NPR', '1','VAT standard rate (DOM sauf Guyane et Saint-Martin), non perçu par le vendeur mais récupérable par acheteur',0);
+insert into llx_c_tva(fk_pays,taux,code,recuperableonly,localtax1,localtax1_type,note,active) values (1, '8.5', '85NPROM', '1', 2, 3, 'VAT standard rate (DOM sauf Guyane et Saint-Martin), NPR, Octroi de Mer',0);
+insert into llx_c_tva(fk_pays,taux,code,recuperableonly,localtax1,localtax1_type,localtax2,localtax2_type,note,active) values (1, '8.5', '85NPROMOMR', '1', 2, 3, 2.5, 3, 'VAT standard rate (DOM sauf Guyane et Saint-Martin), NPR, Octroi de Mer et Octroi de Mer Regional',0);
-- GERMANY (id country=5)
insert into llx_c_tva(rowid,fk_pays,taux,recuperableonly,note,active) values ( 51, 5, '19.0','0','allgemeine Ust.',1);
diff --git a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql
index e73c0df2add..1982ef6aff8 100644
--- a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql
+++ b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql
@@ -42,10 +42,22 @@ ALTER TABLE llx_ecm_files ADD UNIQUE INDEX uk_ecm_files (filepath, filename, ent
ALTER TABLE llx_ecm_files ADD INDEX idx_ecm_files_label (label);
+ALTER TABLE llx_holiday ADD COLUMN import_key varchar(14);
+ALTER TABLE llx_holiday ADD COLUMN extraparams varchar(255);
+
+ALTER TABLE llx_expensereport ADD COLUMN import_key varchar(14);
+ALTER TABLE llx_expensereport ADD COLUMN extraparams varchar(255);
+
+
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PRODUCT_CREATE','Product or service created','Executed when a product or sevice is created','product',30);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PRODUCT_MODIFY','Product or service modified','Executed when a product or sevice is modified','product',30);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PRODUCT_DELETE','Product or service deleted','Executed when a product or sevice is deleted','product',30);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_CREATE','Expense report created','Executed when an expense report is created','expense_report',201);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_VALIDATE','Expense report validated','Executed when an expense report is validated','expense_report',202);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_APPROVE','Expense report approved','Executed when an expense report is approved','expense_report',203);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_PAYED','Expense report billed','Executed when an expense report is set as billed','expense_report',204);
+
ALTER TABLE llx_c_email_templates ADD COLUMN content_lines text;
ALTER TABLE llx_loan ADD COLUMN fk_projet integer DEFAULT NULL;
@@ -111,6 +123,16 @@ INSERT INTO llx_accounting_journal (rowid, code, label, nature, active) VALUES (
ALTER TABLE llx_paiementfourn ADD COLUMN model_pdf varchar(255);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_CREATE','Expense report created','Executed when an expense report is created','expensereport',201);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_VALIDATE','Expense report validated','Executed when an expense report is validated','expensereport',202);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_APPROVE','Expense report approved','Executed when an expense report is approved','expensereport',203);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('EXPENSE_REPORT_PAYED','Expense report billed','Executed when an expense report is set as billed','expensereport',204);
+
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('HOLIDAY_CREATE' ,'Leave request created','Executed when a leave request is created','holiday',221);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('HOLIDAY_VALIDATE','Leave request validated','Executed when a leave request is validated','holiday',222);
+insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('HOLIDAY_APPROVE' ,'Leave request approved','Executed when a leave request is approved','holiday',223);
+
+
ALTER TABLE llx_societe_remise_except ADD COLUMN fk_invoice_supplier_line integer;
ALTER TABLE llx_societe_remise_except ADD COLUMN fk_invoice_supplier integer;
ALTER TABLE llx_societe_remise_except ADD COLUMN fk_invoice_supplier_source integer;
@@ -119,6 +141,8 @@ ALTER TABLE llx_societe_remise_except ADD CONSTRAINT fk_soc_remise_fk_invoice_su
ALTER TABLE llx_societe_remise_except ADD CONSTRAINT fk_societe_remise_fk_invoice_supplier FOREIGN KEY (fk_invoice_supplier) REFERENCES llx_facture_fourn (rowid);
ALTER TABLE llx_societe_remise_except ADD CONSTRAINT fk_societe_remise_fk_invoice_supplier_source FOREIGN KEY (fk_invoice_supplier) REFERENCES llx_facture_fourn (rowid);
+ALTER TABLE llx_facture_rec ADD COLUMN vat_src_code varchar(10) DEFAULT '';
+
UPDATE llx_const set value='moono-lisa' where value = 'moono' AND name = 'FCKEDITOR_SKIN';
ALTER TABLE llx_product_price ADD COLUMN default_vat_code varchar(10) after tva_tx;
@@ -137,6 +161,9 @@ ALTER TABLE llx_chargesociales ADD COLUMN fk_projet integer DEFAULT NULL;
ALTER TABLE llx_cronjob ADD COLUMN processing integer NOT NULL DEFAULT 0;
+ALTER TABLE llx_website ADD COLUMN fk_user_create integer;
+ALTER TABLE llx_website ADD COLUMN fk_user_modif integer;
+
create table llx_payment_various
(
@@ -173,4 +200,51 @@ create table llx_default_values
ALTER TABLE llx_default_values ADD UNIQUE INDEX uk_default_values(type, entity, user_id, page, param);
+ALTER TABLE llx_supplier_proposaldet ADD INDEX idx_supplier_proposaldet_fk_supplier_proposal (fk_supplier_proposal);
+ALTER TABLE llx_supplier_proposaldet ADD INDEX idx_supplier_proposaldet_fk_product (fk_product);
+ALTER TABLE llx_supplier_proposaldet ADD CONSTRAINT fk_supplier_proposaldet_fk_unit FOREIGN KEY (fk_unit) REFERENCES llx_c_units (rowid);
+ALTER TABLE llx_supplier_proposaldet ADD CONSTRAINT fk_supplier_proposaldet_fk_supplier_proposal FOREIGN KEY (fk_supplier_proposal) REFERENCES llx_supplier_proposal (rowid);
+
+-- NEW inventory module
+CREATE TABLE llx_inventory
+(
+rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
+datec datetime DEFAULT NULL,
+tms timestamp,
+fk_warehouse integer DEFAULT 0,
+entity integer DEFAULT 0,
+status integer DEFAULT 0,
+title varchar(255) NOT NULL,
+date_inventory datetime DEFAULT NULL
+)
+ENGINE=InnoDB;
+
+CREATE TABLE llx_inventorydet
+(
+rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
+datec datetime DEFAULT NULL,
+tms timestamp,
+fk_inventory integer DEFAULT 0,
+fk_warehouse integer DEFAULT 0,
+fk_product integer DEFAULT 0,
+batch varchar(30) DEFAULT NULL,
+qty_view double DEFAULT NULL,
+qty_stock double DEFAULT NULL,
+qty_regulated double DEFAULT NULL,
+pmp double DEFAULT 0,
+pa double DEFAULT 0,
+new_pmp double DEFAULT 0
+)
+ENGINE=InnoDB;
+
+ALTER TABLE llx_inventory ADD INDEX idx_inventory_tms (tms);
+ALTER TABLE llx_inventory ADD INDEX idx_inventory_datec (datec);
+ALTER TABLE llx_inventorydet ADD INDEX idx_inventorydet_tms (tms);
+ALTER TABLE llx_inventorydet ADD INDEX idx_inventorydet_datec (datec);
+ALTER TABLE llx_inventorydet ADD INDEX idx_inventorydet_fk_inventory (fk_inventory);
+
+insert into llx_c_tva(fk_pays,taux,code,recuperableonly,note,active) values (1, '8.5', '85', '0','VAT standard rate (DOM sauf Guyane et Saint-Martin)',0);
+insert into llx_c_tva(fk_pays,taux,code,recuperableonly,note,active) values (1, '8.5', '85NPR', '1','VAT standard rate (DOM sauf Guyane et Saint-Martin), non perçu par le vendeur mais récupérable par acheteur',0);
+insert into llx_c_tva(fk_pays,taux,code,recuperableonly,localtax1,localtax1_type,note,active) values (1, '8.5', '85NPROM', '1', 2, 3, 'VAT standard rate (DOM sauf Guyane et Saint-Martin), NPR, Octroi de Mer',0);
+insert into llx_c_tva(fk_pays,taux,code,recuperableonly,localtax1,localtax1_type,localtax2,localtax2_type,note,active) values (1, '8.5', '85NPROMOMR', '1', 2, 3, 2.5, 3, 'VAT standard rate (DOM sauf Guyane et Saint-Martin), NPR, Octroi de Mer et Octroi de Mer Regional',0);
diff --git a/htdocs/install/mysql/migration/repair.sql b/htdocs/install/mysql/migration/repair.sql
index 03c1ab07dfd..32696ecf593 100755
--- a/htdocs/install/mysql/migration/repair.sql
+++ b/htdocs/install/mysql/migration/repair.sql
@@ -15,8 +15,8 @@
-- Requests to change character set and collation of a column
--- ALTER TABLE llx_accountingaccount MODIFY account_number VARCHAR(20) CHARACTER SET utf8;
--- ALTER TABLE llx_accountingaccount MODIFY account_number VARCHAR(20) COLLATE utf8_unicode_ci;
+-- ALTER TABLE llx_accounting_account MODIFY account_number VARCHAR(20) CHARACTER SET utf8;
+-- ALTER TABLE llx_accounting_account MODIFY account_number VARCHAR(20) COLLATE utf8_unicode_ci;
-- You can check with "show full columns from llx_accountingaccount";
diff --git a/htdocs/install/mysql/tables/llx_facture_rec.sql b/htdocs/install/mysql/tables/llx_facture_rec.sql
index d0d79ef57dc..0e25100386b 100644
--- a/htdocs/install/mysql/tables/llx_facture_rec.sql
+++ b/htdocs/install/mysql/tables/llx_facture_rec.sql
@@ -32,6 +32,7 @@ create table llx_facture_rec
remise_percent real DEFAULT 0,
remise_absolue real DEFAULT 0,
+ vat_src_code varchar(10) DEFAULT '', -- Vat code used as source of vat fields. Not strict foreign key here.
tva double(24,8) DEFAULT 0,
localtax1 double(24,8) DEFAULT 0, -- amount localtax1
localtax2 double(24,8) DEFAULT 0, -- amount localtax2
diff --git a/htdocs/install/mysql/tables/llx_holiday.sql b/htdocs/install/mysql/tables/llx_holiday.sql
index 2a981444df3..2091dea9541 100644
--- a/htdocs/install/mysql/tables/llx_holiday.sql
+++ b/htdocs/install/mysql/tables/llx_holiday.sql
@@ -40,6 +40,8 @@ fk_user_cancel integer DEFAULT NULL,
detail_refuse varchar( 250 ) DEFAULT NULL,
note_private text,
note_public text,
-tms timestamp
+tms timestamp,
+import_key varchar(14),
+extraparams varchar(255) -- for other parameters with json format
)
ENGINE=innodb;
diff --git a/htdocs/install/mysql/tables/llx_inventory.key.sql b/htdocs/install/mysql/tables/llx_inventory.key.sql
new file mode 100644
index 00000000000..bf76381e108
--- /dev/null
+++ b/htdocs/install/mysql/tables/llx_inventory.key.sql
@@ -0,0 +1,21 @@
+-- ===================================================================
+-- Copyright (C) 2012 Laurent Destailleur
+-- Copyright (C) 2017 ATM Consulting
+--
+-- 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 .
+--
+-- ===================================================================
+
+ALTER TABLE llx_inventory ADD INDEX idx_inventory_tms (tms);
+ALTER TABLE llx_inventory ADD INDEX idx_inventory_datec (datec);
diff --git a/htdocs/install/mysql/tables/llx_inventory.sql b/htdocs/install/mysql/tables/llx_inventory.sql
new file mode 100644
index 00000000000..f906699f5dd
--- /dev/null
+++ b/htdocs/install/mysql/tables/llx_inventory.sql
@@ -0,0 +1,31 @@
+-- ===================================================================
+-- Copyright (C) 2012 Laurent Destailleur
+-- Copyright (C) 2017 ATM Consulting
+--
+-- 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 .
+--
+-- ===================================================================
+
+CREATE TABLE llx_inventory
+(
+rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
+datec datetime DEFAULT NULL,
+tms timestamp,
+fk_warehouse integer DEFAULT 0,
+entity integer DEFAULT 0,
+status integer DEFAULT 0,
+title varchar(255) NOT NULL,
+date_inventory datetime DEFAULT NULL
+)
+ENGINE=InnoDB;
diff --git a/htdocs/install/mysql/tables/llx_inventorydet.key.sql b/htdocs/install/mysql/tables/llx_inventorydet.key.sql
new file mode 100644
index 00000000000..3cef44ba52a
--- /dev/null
+++ b/htdocs/install/mysql/tables/llx_inventorydet.key.sql
@@ -0,0 +1,22 @@
+-- ===================================================================
+-- Copyright (C) 2012 Laurent Destailleur
+-- Copyright (C) 2017 ATM Consulting
+--
+-- 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 .
+--
+-- ===================================================================
+
+ALTER TABLE llx_inventorydet ADD INDEX idx_inventorydet_tms (tms);
+ALTER TABLE llx_inventorydet ADD INDEX idx_inventorydet_datec (datec);
+ALTER TABLE llx_inventorydet ADD INDEX idx_inventorydet_fk_inventory (fk_inventory);
diff --git a/htdocs/install/mysql/tables/llx_inventorydet.sql b/htdocs/install/mysql/tables/llx_inventorydet.sql
new file mode 100644
index 00000000000..ce40d03939c
--- /dev/null
+++ b/htdocs/install/mysql/tables/llx_inventorydet.sql
@@ -0,0 +1,36 @@
+-- ===================================================================
+-- Copyright (C) 2012 Laurent Destailleur
+-- Copyright (C) 2017 ATM Consulting
+--
+-- 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 .
+--
+-- ===================================================================
+
+CREATE TABLE llx_inventorydet
+(
+rowid integer NOT NULL AUTO_INCREMENT PRIMARY KEY,
+datec datetime DEFAULT NULL,
+tms timestamp,
+fk_inventory integer DEFAULT 0,
+fk_warehouse integer DEFAULT 0,
+fk_product integer DEFAULT 0,
+batch varchar(30) DEFAULT NULL, -- Lot or serial number
+qty_view double DEFAULT NULL, -- must be filled once regulation is done
+qty_stock double DEFAULT NULL, -- can be filled during draft edition
+qty_regulated double DEFAULT NULL, -- must be filled once regulation is done
+pmp double DEFAULT 0,
+pa double DEFAULT 0,
+new_pmp double DEFAULT 0
+)
+ENGINE=InnoDB;
diff --git a/htdocs/install/mysql/tables/llx_supplier_proposaldet.key.sql b/htdocs/install/mysql/tables/llx_supplier_proposaldet.key.sql
new file mode 100644
index 00000000000..e6af3c7e515
--- /dev/null
+++ b/htdocs/install/mysql/tables/llx_supplier_proposaldet.key.sql
@@ -0,0 +1,26 @@
+-- ===================================================================
+-- Copyright (C) 2009-2011 Regis Houssin
+-- Copyright (C) 2012 Cédric Salvador
+--
+-- 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 .
+--
+-- ===================================================================
+
+
+
+ALTER TABLE llx_supplier_proposaldet ADD INDEX idx_supplier_proposaldet_fk_supplier_proposal (fk_supplier_proposal);
+ALTER TABLE llx_supplier_proposaldet ADD INDEX idx_supplier_proposaldet_fk_product (fk_product);
+
+ALTER TABLE llx_supplier_proposaldet ADD CONSTRAINT fk_supplier_proposaldet_fk_unit FOREIGN KEY (fk_unit) REFERENCES llx_c_units (rowid);
+ALTER TABLE llx_supplier_proposaldet ADD CONSTRAINT fk_supplier_proposaldet_fk_supplier_proposal FOREIGN KEY (fk_supplier_proposal) REFERENCES llx_supplier_proposal (rowid);
diff --git a/htdocs/install/mysql/tables/llx_website.sql b/htdocs/install/mysql/tables/llx_website.sql
index 331e6b085bd..84a9dd47188 100644
--- a/htdocs/install/mysql/tables/llx_website.sql
+++ b/htdocs/install/mysql/tables/llx_website.sql
@@ -26,7 +26,8 @@ CREATE TABLE llx_website
status integer,
fk_default_home integer,
virtualhost varchar(255),
- date_creation datetime,
- date_modification datetime,
- tms timestamp
+ fk_user_create integer,
+ fk_user_modif integer,
+ date_creation datetime,
+ tms timestamp
) ENGINE=innodb;
diff --git a/htdocs/install/mysql/tables/llx_website_pages.sql b/htdocs/install/mysql/tables/llx_website_pages.sql
index 982420dd507..69b6c417528 100644
--- a/htdocs/install/mysql/tables/llx_website_pages.sql
+++ b/htdocs/install/mysql/tables/llx_website_pages.sql
@@ -27,6 +27,8 @@ CREATE TABLE llx_website_page
keywords varchar(255),
content mediumtext, -- text is not enough in size
status integer,
- date_creation datetime,
- tms timestamp
+ fk_user_create integer,
+ fk_user_modif integer,
+ date_creation datetime,
+ tms timestamp
) ENGINE=innodb;
diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang
index 42c5c519d2c..bd1e41c5464 100644
--- a/htdocs/langs/en_US/accountancy.lang
+++ b/htdocs/langs/en_US/accountancy.lang
@@ -36,15 +36,15 @@ AccountancyAreaDescActionOnceBis=Next steps should be done to save you time in f
AccountancyAreaDescActionFreq=The following actions are usually executed every month, week or day for very large companies...
AccountancyAreaDescChartModel=STEP %s: Create a model of chart of account from menu %s
AccountancyAreaDescChart=STEP %s: Create or check content of your chart of account from menu %s
-AccountancyAreaDescBank=STEP %s: Check the binding between bank accounts and accounting account is done. Complete missing bindings. For this, go on the card of each financial account. You can start from page %s.
-AccountancyAreaDescVat=STEP %s: Check the binding between vat rates and accounting account is done. Complete missing bindings. You can set accounting accounts to use for each VAT from page %s.
-AccountancyAreaDescExpenseReport=STEP %s: Check the binding between type of expense report and accounting account is done. Complete missing bindings. You can set accounting accounts to use for each VAT from page %s.
-AccountancyAreaDescSal=STEP %s: Check the binding between salaries payment and accounting account is done. Complete missing bindings. For this you can use the menu entry %s.
-AccountancyAreaDescContrib=STEP %s: Check the binding between special expences (miscellaneous taxes) and accounting account is done. Complete missing bindings. For this you can use the menu entry %s.
-AccountancyAreaDescDonation=STEP %s: Check the binding between donation and accounting account is done. Complete missing bindings. You can set the account dedicated for that from the menu entry %s.
-AccountancyAreaDescMisc=STEP %s: Check the default binding between miscellaneous transaction lines and accounting account is done. Complete missing bindings. For this you can use the menu entry %s.
-AccountancyAreaDescProd=STEP %s: Check the binding between products/services and accounting account is done. Complete missing bindings. For this you can use the menu entry %s.
-AccountancyAreaDescLoan=STEP %s: Check the binding between loans payment and accounting account is done. Complete missing bindings. For this you can use the menu entry %s.
+AccountancyAreaDescVat=STEP %s: Define accounting accounts for each VAT Rates. For this you can use the menu entry %s.
+AccountancyAreaDescExpenseReport=STEP %s: Define default accounting accounts for type of expense report. For this you can use the menu entry %s.
+AccountancyAreaDescSal=STEP %s: Define default accounting accounts for payment of salaries. For this you can use the menu entry %s.
+AccountancyAreaDescContrib=STEP %s: Define default accounting accounts for special expences (miscellaneous taxes). For this you can use the menu entry %s.
+AccountancyAreaDescDonation=STEP %s: Define default accounting accounts for donation. For this you can use the menu entry %s.
+AccountancyAreaDescMisc=STEP %s: Define default accounting accounts for miscellaneous transactions. For this you can use the menu entry %s.
+AccountancyAreaDescLoan=STEP %s: Define default accounting accounts for loans. For this you can use the menu entry %s.
+AccountancyAreaDescBank=STEP %s: Define accounting accounts for each bank and financial accounts. For this, go on the card of each financial account. You can start from page %s.
+AccountancyAreaDescProd=STEP %s: Define accounting accounts on your products. For this you can use the menu entry %s.
AccountancyAreaDescCustomer=STEP %s: Check the binding between existing customer invoice lines and accounting account is done, so application will be able to journalize transactions in General Ledger in one click. Complete missing bindings. For this you can use the menu entry %s.
AccountancyAreaDescSupplier=STEP %s: Check the binding between existing supplier invoice lines and accounting account is done, so application will be able to journalize transactions in General Ledger in one click. Complete missing bindings. For this you can use the menu entry %s.
@@ -189,8 +189,8 @@ ErrorAccountancyCodeIsAlreadyUse=Error, you cannot delete this accounting accoun
MvtNotCorrectlyBalanced=Mouvement not correctly balanced. Credit = %s. Debit = %s
FicheVentilation=Binding card
GeneralLedgerIsWritten=Transactions are written in the general ledger
-GeneralLedgerSomeRecordWasNotRecorded=Some of the transactions could not be recorded.
-NoNewRecordSaved=No new record saved
+GeneralLedgerSomeRecordWasNotRecorded=Some of the transactions could not be dispatched. If there is no other error message, this is probably because they were already dispatched.
+NoNewRecordSaved=No new record dispatched
ListOfProductsWithoutAccountingAccount=List of products not bound to any accounting account
ChangeBinding=Change the binding
diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang
index 4653a22d814..1356a980164 100644
--- a/htdocs/langs/en_US/admin.lang
+++ b/htdocs/langs/en_US/admin.lang
@@ -305,12 +305,12 @@ LastActivationDate=Latest activation date
UpdateServerOffline=Update server offline
WithCounter=Manage a counter
GenericMaskCodes=You may enter any numbering mask. In this mask, the following tags could be used:{000000} corresponds to a number which will be incremented on each %s. Enter as many zeros as the desired length of the counter. The counter will be completed by zeros from the left in order to have as many zeros as the mask. {000000+000} same as previous but an offset corresponding to the number to the right of the + sign is applied starting on first %s. {000000@x} same as previous but the counter is reset to zero when month x is reached (x between 1 and 12, or 0 to use the early months of fiscal year defined in your configuration, or 99 to reset to zero every month). If this option is used and x is 2 or higher, then sequence {yy}{mm} or {yyyy}{mm} is also required. {dd} day (01 to 31).{mm} month (01 to 12).{yy} , {yyyy} or {y} year over 2, 4 or 1 numbers.
-GenericMaskCodes2={cccc} the client code on n characters{cccc000} the client code on n characters is followed by a counter dedicated for customer. This counter dedicated to customer is reset at same time than global counter.{tttt} The code of third party type on n characters (see dictionary-thirdparty types).
+GenericMaskCodes2={cccc} the client code on n characters{cccc000} the client code on n characters is followed by a counter dedicated for customer. This counter dedicated to customer is reset at same time than global counter.{tttt} The code of third party type on n characters (see menu Home - Setup - Dictionary - Types of third parties). If you add this tag, the counter will be different for each type of third party.
GenericMaskCodes3=All other characters in the mask will remain intact. Spaces are not allowed.
-GenericMaskCodes4a=Example on the 99th %s of the third party TheCompany done 2007-01-31:
+GenericMaskCodes4a=Example on the 99th %s of the third party TheCompany, with date 2007-01-31:
GenericMaskCodes4b=Example on third party created on 2007-03-01:
GenericMaskCodes4c=Example on product created on 2007-03-01:
-GenericMaskCodes5=ABC{yy}{mm}-{000000} will give ABC0701-000099 {0000+100@1}-ZZZ/{dd}/XXX will give 0199-ZZZ/31/XXX
+GenericMaskCodes5=ABC{yy}{mm}-{000000} will give ABC0701-000099 {0000+100@1}-ZZZ/{dd}/XXX will give 0199-ZZZ/31/XXX IN{yy}{mm}-{0000}-{t} will give IN0701-0099-A if the type of company is 'Responsable Inscripto' with code for type that is 'A_RI'
GenericNumRefModelDesc=Returns a customizable number according to a defined mask.
ServerAvailableOnIPOrPort=Server is available at address %s on port %s
ServerNotAvailableOnIPOrPort=Server is not available at address %s on port %s
@@ -427,11 +427,16 @@ WarningPHPMail=WARNING: Some email providers (like Yahoo) does not allow you to
ClickToShowDescription=Click to show description
DependsOn=This module need the module(s)
RequiredBy=This module is required by module(s)
-TheKeyIsTheNameOfHtmlField=The key is the name of the html field. This need to have technical knowledges to read the content of the HTML page to get the key name of a field.
-PageUrlForDefaultValues=You must enter here the relative url of the page. Examples:
+TheKeyIsTheNameOfHtmlField=This is the name of the HTML field. This need to have technical knowledges to read the content of the HTML page to get the key name of a field.
+PageUrlForDefaultValues=You must enter here the relative url of the page. If you include parameters in URL, the default values will be effective if all parameters are set to same value. Examples:
PageUrlForDefaultValuesCreate= For form to create a new thirdparty, it is %s
PageUrlForDefaultValuesList= For page that list thirdparties, it is %s
GoIntoTranslationMenuToChangeThis=A translation has been found for the key with this code, so to change this value, you must edit it fom Home-Setup-translation.
+WarningSettingSortOrder=Warning, setting a default sort order may result in a technical error when going on the list page if field is an unknown field. If you experience such an error, come back to this page to remove the default sort order and restore default behavior.
+Field=Field
+ProductDocumentTemplates=Document templates to generate product document
+FreeLegalTextOnExpenseReports=Free legal text on expense reports
+WatermarkOnDraftExpenseReports=Watermark on draft expense reports
# Modules
Module0Name=Users & groups
Module0Desc=Users / Employees and Groups management
@@ -1091,6 +1096,7 @@ CurrentTranslationString=Current translation string
WarningAtLeastKeyOrTranslationRequired=A search criteria is required at least for key or translation string
NewTranslationStringToShow=New translation string to show
OriginalValueWas=The original translation is overwritten. Original value was: %s
+TransKeyWithoutOriginalValue=You forced a new translation for the translation key '%s ' that does not exists in any language files
TotalNumberOfActivatedModules=Total number of activated feature modules: %s / %s
YouMustEnableOneModule=You must at least enable 1 module
ClassNotFoundIntoPathWarning=Class %s not found into PHP path
@@ -1498,7 +1504,7 @@ AGENDA_NOTIFICATION=Enable event notification on user browsers when event date i
AGENDA_NOTIFICATION_SOUND=Enable sound notification
##### Clicktodial #####
ClickToDialSetup=Click To Dial module setup
-ClickToDialUrlDesc=Url called when a click on phone picto is done. In URL, you can use tags__PHONETO__ that will be replaced with the phone number of person to call__PHONEFROM__ that will be replaced with phone number of calling person (yours)__LOGIN__ that will be replaced with your clicktodial login (defined on your user card)__PASS__ that will be replaced with your clicktodial password (defined on your user card).
+ClickToDialUrlDesc=Url called when a click on phone picto is done. In URL, you can use tags__PHONETO__ that will be replaced with the phone number of person to call__PHONEFROM__ that will be replaced with phone number of calling person (yours)__LOGIN__ that will be replaced with clicktodial login (defined on user card)__PASS__ that will be replaced with clicktodial password (defined on user card).
ClickToDialDesc=This module allows to make phone numbers clickable. A click on this icon will call make your phone to call the phone number. This can be used to call a call center system from Dolibarr that can call the phone number on a SIP system for example.
ClickToDialUseTelLink=Use just a link "tel:" on phone numbers
ClickToDialUseTelLinkDesc=Use this method if your users have a softphone or a software interface installed on same computer than the browser, and called when you click on a link in your browser that start with "tel:". If you need a full server solution (no need of local software installation), you must set this to "No" and fill next field.
@@ -1527,7 +1533,7 @@ EndPointIs=SOAP clients must send their requests to the Dolibarr endpoint availa
ApiSetup=API module setup
ApiDesc=By enabling this module, Dolibarr become a REST server to provide miscellaneous web services.
ApiProductionMode=Enable production mode (this will activate use of a cache for services management)
-ApiExporerIs=You can explore the APIs at url
+ApiExporerIs=You can explore and test the APIs at URL
OnlyActiveElementsAreExposed=Only elements from enabled modules are exposed
ApiKey=Key for API
WarningAPIExplorerDisabled=The API explorer has been disabled. API explorer is not required to provide API services. It is a tool for developer to find/test REST APIs. If you need this tool, go into setup of module API REST to activate it.
diff --git a/htdocs/langs/en_US/bills.lang b/htdocs/langs/en_US/bills.lang
index 7432f0e2412..189ef5c1fe2 100644
--- a/htdocs/langs/en_US/bills.lang
+++ b/htdocs/langs/en_US/bills.lang
@@ -447,7 +447,7 @@ CantRemovePaymentWithOneInvoicePaid=Can't remove payment since there is at least
ExpectedToPay=Expected payment
CantRemoveConciliatedPayment=Can't remove conciliated payment
PayedByThisPayment=Paid by this payment
-ClosePaidInvoicesAutomatically=Classify "Paid" all standard, situation or replacement invoices entirely paid.
+ClosePaidInvoicesAutomatically=Classify "Paid" all standard, down payment or replacement invoices entirely paid.
ClosePaidCreditNotesAutomatically=Classify "Paid" all credit notes entirely paid back.
ClosePaidContributionsAutomatically=Classify "Paid" all social or fiscal contributions entirely paid.
AllCompletelyPayedInvoiceWillBeClosed=All invoice with no remain to pay will be automatically closed to status "Paid".
diff --git a/htdocs/langs/en_US/bookmarks.lang b/htdocs/langs/en_US/bookmarks.lang
index d15ed3e1148..9d2003f34d3 100644
--- a/htdocs/langs/en_US/bookmarks.lang
+++ b/htdocs/langs/en_US/bookmarks.lang
@@ -12,7 +12,7 @@ BookmarkTargetNewWindowShort=New window
BookmarkTargetReplaceWindowShort=Current window
BookmarkTitle=Bookmark title
UrlOrLink=URL
-BehaviourOnClick=Behaviour when a URL is clicked
+BehaviourOnClick=Behaviour when a bookmark URL is selected
CreateBookmark=Create bookmark
SetHereATitleForLink=Set a title for the bookmark
UseAnExternalHttpLinkOrRelativeDolibarrLink=Use an external http URL or a relative Dolibarr URL
diff --git a/htdocs/langs/en_US/companies.lang b/htdocs/langs/en_US/companies.lang
index 38d7a81ab9a..12d1ea188e6 100644
--- a/htdocs/langs/en_US/companies.lang
+++ b/htdocs/langs/en_US/companies.lang
@@ -77,10 +77,10 @@ VATIsNotUsed=VAT is not used
CopyAddressFromSoc=Fill address with third party address
ThirdpartyNotCustomerNotSupplierSoNoRef=Thirdparty neither customer nor supplier, no available refering objects
PaymentBankAccount=Payment bank account
-OverAllProposals=Total proposals
-OverAllOrders=Total orders
-OverAllInvoices=Total invoices
-OverAllSupplierProposals=Total price requests
+OverAllProposals=Proposals
+OverAllOrders=Orders
+OverAllInvoices=Invoices
+OverAllSupplierProposals=Price requests
##### Local Taxes #####
LocalTax1IsUsed=Use second tax
LocalTax1IsUsedES= RE is used
diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang
index 2350a90fe88..4f700109c33 100644
--- a/htdocs/langs/en_US/errors.lang
+++ b/htdocs/langs/en_US/errors.lang
@@ -177,7 +177,8 @@ ErrorStockIsNotEnoughToAddProductOnShipment=Stock is not enough for product %s t
ErrorStockIsNotEnoughToAddProductOnProposal=Stock is not enough for product %s to add it into a new proposal.
ErrorFailedToLoadLoginFileForMode=Failed to get the login key for mode '%s'.
ErrorModuleNotFound=File of module was not found.
-ErrorFieldAccountNotDefinedForBankLine=Value for Accounting account not defined for source bank line %s
+ErrorFieldAccountNotDefinedForBankLine=Value for Accounting account not defined for source line id %s (%s)
+ErrorFieldAccountNotDefinedForInvoiceLine=Value for Accounting account not defined for invoice id %s (%s)
ErrorBankStatementNameMustFollowRegex=Error, bank statement name must follow the following syntax rule %s
ErrorPhpMailDelivery=Check that you don't use a too high number of recipients and that your email content is not similar to a Spam. Ask also your administrator to check firewall and server logs files for a more complete information.
ErrorUserNotAssignedToTask=User must be assigned to task to be able to enter time consumed.
@@ -206,3 +207,4 @@ WarningPaymentDateLowerThanInvoiceDate=Payment date (%s) is earlier than invoice
WarningTooManyDataPleaseUseMoreFilters=Too many data (more than %s lines). Please use more filters or set the constant %s to a higher limit.
WarningSomeLinesWithNullHourlyRate=Some times were recorded by some users while their hourly rate was not defined. A value of 0 %s per hour was used but this may result in wrong valuation of time spent.
WarningYourLoginWasModifiedPleaseLogin=Your login was modified. For security purpose you will have to login with your new login before next action.
+WarningAnEntryAlreadyExistForTransKey=An entry already exists for the translation key for this language
\ No newline at end of file
diff --git a/htdocs/langs/en_US/mails.lang b/htdocs/langs/en_US/mails.lang
index 18f42982ca8..1e4f1446fa7 100644
--- a/htdocs/langs/en_US/mails.lang
+++ b/htdocs/langs/en_US/mails.lang
@@ -35,7 +35,7 @@ MailingStatusSentPartialy=Sent partialy
MailingStatusSentCompletely=Sent completely
MailingStatusError=Error
MailingStatusNotSent=Not sent
-MailSuccessfulySent=Email successfully accepted for delivery (from %s to %s)
+MailSuccessfulySent=Email (from %s to %s) successfully accepted for delivery
MailingSuccessfullyValidated=EMailing successfully validated
MailUnsubcribe=Unsubscribe
MailingStatusNotContact=Don't contact anymore
diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang
index 71788071a2d..bc7c864b279 100644
--- a/htdocs/langs/en_US/main.lang
+++ b/htdocs/langs/en_US/main.lang
@@ -647,6 +647,7 @@ FreeLineOfType=Free entry of type
CloneMainAttributes=Clone object with its main attributes
PDFMerge=PDF Merge
Merge=Merge
+DocumentModelStandardPDF=Standard PDF template
PrintContentArea=Show page to print main content area
MenuManager=Menu manager
WarningYouAreInMaintenanceMode=Warning, you are in a maintenance mode, so only login %s is allowed to use application at the moment.
@@ -766,6 +767,9 @@ Download=Download
ActualizeCurrency=Update currency rate
Fiscalyear=Fiscal year
ModuleBuilder=Module Builder
+SetMultiCurrencyCode=Set currency
+BulkActions=Bulk actions
+ClickToShowHelp=Click to show tooltip help
# Week day
Monday=Monday
Tuesday=Tuesday
@@ -822,6 +826,4 @@ SearchIntoInterventions=Interventions
SearchIntoContracts=Contracts
SearchIntoCustomerShipments=Customer shipments
SearchIntoExpenseReports=Expense reports
-SearchIntoLeaves=Leaves
-
-BulkActions=Bulk actions
\ No newline at end of file
+SearchIntoLeaves=Leaves
\ No newline at end of file
diff --git a/htdocs/langs/en_US/resource.lang b/htdocs/langs/en_US/resource.lang
index f95121db351..5a907f6ba23 100644
--- a/htdocs/langs/en_US/resource.lang
+++ b/htdocs/langs/en_US/resource.lang
@@ -29,3 +29,8 @@ RessourceSuccessfullyDeleted=Resource successfully deleted
DictionaryResourceType=Type of resources
SelectResource=Select resource
+
+IdResource=Id resource
+AssetNumber=Serial number
+ResourceTypeCode=Resource type code
+ImportDataset_resource_1=Resources
diff --git a/htdocs/langs/en_US/sendings.lang b/htdocs/langs/en_US/sendings.lang
index ef6fe44856b..fcd28cc9f56 100644
--- a/htdocs/langs/en_US/sendings.lang
+++ b/htdocs/langs/en_US/sendings.lang
@@ -37,7 +37,6 @@ SendingSheet=Shipment sheet
ConfirmDeleteSending=Are you sure you want to delete this shipment?
ConfirmValidateSending=Are you sure you want to validate this shipment with reference %s ?
ConfirmCancelSending=Are you sure you want to cancel this shipment?
-DocumentModelSimple=Simple document model
DocumentModelMerou=Merou A5 model
WarningNoQtyLeftToSend=Warning, no products waiting to be shipped.
StatsOnShipmentsOnlyValidated=Statistics conducted on shipments only validated. Date used is date of validation of shipment (planed delivery date is not always known).
@@ -53,7 +52,7 @@ ShipmentCreationIsDoneFromOrder=For the moment, creation of a new shipment is do
ShipmentLine=Shipment line
ProductQtyInCustomersOrdersRunning=Product quantity into open customers orders
ProductQtyInSuppliersOrdersRunning=Product quantity into open suppliers orders
-ProductQtyInShipmentAlreadySent=Product quantity from oped customer order already sent
+ProductQtyInShipmentAlreadySent=Product quantity from open customer order already sent
ProductQtyInSuppliersShipmentAlreadyRecevied=Product quantity from open supplier order already received
NoProductToShipFoundIntoStock=No product to ship found into warehouse %s . Correct stock or go back to choose another warehouse.
WeightVolShort=Weight/Vol.
diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang
index aeb83b925bd..9c9f82f6efb 100644
--- a/htdocs/langs/en_US/stocks.lang
+++ b/htdocs/langs/en_US/stocks.lang
@@ -146,3 +146,50 @@ ProductStockWarehouseUpdated=Stock limit for alert and desired optimal stock cor
ProductStockWarehouseDeleted=Stock limit for alert and desired optimal stock correctly deleted
AddNewProductStockWarehouse=Set new limit for alert and desired optimal stock
AddStockLocationLine=Decrease quantity then click to add another warehouse for this product
+InventoryDate=Inventory date
+NewInventory=New inventory
+inventorySetup = Inventory Setup
+inventoryCreatePermission=Create new inventory
+inventoryReadPermission=View inventories
+inventoryWritePermission=Update inventories
+inventoryValidatePermission=Validate inventory
+inventoryTitle=Inventory
+inventoryListTitle=Inventories
+inventoryListEmpty=No inventory in progress
+inventoryCreateDelete=Create/Delete inventory
+inventoryCreate=Create new
+inventoryEdit=Edit
+inventoryValidate=Validated
+inventoryDraft=Running
+inventorySelectWarehouse=Warehouse choice
+inventoryConfirmCreate=Create
+inventoryOfWarehouse=Inventory for warehouse : %s
+inventoryErrorQtyAdd=Error : one quantity is leaser than zero
+inventoryMvtStock=By inventory
+inventoryWarningProductAlreadyExists=This product is already into list
+SelectCategory=Category filter
+SelectFournisseur=Supplier filter
+inventoryOnDate=Inventory
+INVENTORY_DISABLE_VIRTUAL=Allow to not destock child product from a kit on inventory
+INVENTORY_USE_MIN_PA_IF_NO_LAST_PA=Use the buy price if no last buy price can be found
+INVENTORY_USE_INVENTORY_DATE_FROM_DATEMVT=Stock mouvment have date of inventory
+inventoryChangePMPPermission=Allow to change PMP value for a product
+ColumnNewPMP=New unit PMP
+OnlyProdsInStock=Do not add product without stock
+TheoricalQty=Theorique qty
+TheoricalValue=Theorique qty
+LastPA=Last BP
+CurrentPA=Curent BP
+RealQty=Real Qty
+RealValue=Real Value
+RegulatedQty=Regulated Qty
+AddInventoryProduct=Add product to inventory
+AddProduct=Add
+ApplyPMP=Apply PMP
+FlushInventory=Flush inventory
+ConfirmFlushInventory=Do you confirm this action ?
+InventoryFlushed=Inventory flushed
+ExitEditMode=Exit edition
+inventoryDeleteLine=Delete line
+RegulateStock=Regulate Stock
+ListInventory=List
diff --git a/htdocs/langs/en_US/website.lang b/htdocs/langs/en_US/website.lang
index fcb09c8d5c1..03069d6f649 100644
--- a/htdocs/langs/en_US/website.lang
+++ b/htdocs/langs/en_US/website.lang
@@ -24,5 +24,5 @@ SetAsHomePage=Set as Home page
RealURL=Real URL
ViewWebsiteInProduction=View web site using home URLs
SetHereVirtualHost=If you can set, on your web server, a dedicated virtual host with a root directory on %s , define here the virtual hostname so the preview can be done also using this direct web server access and not only using Dolibarr server.
-PreviewSiteServedByWebServer=Preview %s in a new tab. The %s will be served by an external web server (like Apache, Nginx, IIS). You must instal and setup this server before. URL of %s served by external server:%s
-PreviewSiteServedByDolibarr=Preview %s in a new tab. The %s will be served by Dolibarr server so it does not need any extra web server (like Apache, Nginx, IIS) to be installed. The inconvenient is that URL of pages are not user friendly and start with path of your Dolibarr. URL of %s served by Dolibarr:%s
+PreviewSiteServedByWebServer=Preview %s in a new tab. The %s will be served by an external web server (like Apache, Nginx, IIS). You must install and setup this server before to point to directory:%s URL served by external server:%s
+PreviewSiteServedByDolibarr=Preview %s in a new tab. The %s will be served by Dolibarr server so it does not need any extra web server (like Apache, Nginx, IIS) to be installed. The inconvenient is that URL of pages are not user friendly and start with path of your Dolibarr. URL served by Dolibarr:%s To use your own external web server to serve this web site, create a virtual host on your web server that point on directory%s then enter the name of this virtual server and clicking on the other preview button.
diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php
index 72a5f39e02c..ab92df9ca12 100644
--- a/htdocs/main.inc.php
+++ b/htdocs/main.inc.php
@@ -1271,9 +1271,10 @@ function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs
{
$enablebrowsernotif=false;
if (! empty($conf->agenda->enabled) && ! empty($conf->global->AGENDA_NOTIFICATION)) $enablebrowsernotif=true;
+ if ($conf->browser->layout == 'phone') $enablebrowsernotif=false;
if ($enablebrowsernotif)
{
- print ''."\n";
+ print ''."\n";
print ''."\n";
}
}
@@ -1865,7 +1866,7 @@ if (! function_exists("llxFooter"))
dol_htmloutput_events();
// Core error message
- if (defined("MAIN_CORE_ERROR") && constant("MAIN_CORE_ERROR") == 1)
+ if (! empty($conf->global->MAIN_CORE_ERROR))
{
// Ajax version
if ($conf->use_javascript_ajax)
diff --git a/htdocs/master.inc.php b/htdocs/master.inc.php
index ebb99eb9807..61e51999479 100644
--- a/htdocs/master.inc.php
+++ b/htdocs/master.inc.php
@@ -180,7 +180,7 @@ if (! defined('NOREQUIREDB'))
//print "Will work with data into entity instance number '".$conf->entity."'";
- // Here we read database (llx_const table and llx_default_values) and define $conf->global->XXX var.
+ // Here we read database (llx_const table) and define $conf->global->XXX var.
$conf->setValues($db);
}
diff --git a/htdocs/modulebuilder/skeletons/skeleton_card.php b/htdocs/modulebuilder/skeletons/skeleton_card.php
index f0a9dd23c26..42fce48e2cb 100644
--- a/htdocs/modulebuilder/skeletons/skeleton_card.php
+++ b/htdocs/modulebuilder/skeletons/skeleton_card.php
@@ -109,9 +109,9 @@ if (empty($reshook))
}
// Action to add record
- if ($action == 'add')
+ if ($action == 'add' && ! empty($user->rights->mymodule->create))
{
- if (GETPOST('cancel'))
+ if ($cancel)
{
$urltogo=$backtopage?$backtopage:dol_buildpath('/mymodule/list.php',1);
header("Location: ".$urltogo);
@@ -154,7 +154,7 @@ if (empty($reshook))
}
// Action to update record
- if ($action == 'update')
+ if ($action == 'update' && ! empty($user->rights->mymodule->create))
{
$error=0;
@@ -189,7 +189,7 @@ if (empty($reshook))
}
// Action to delete
- if ($action == 'confirm_delete')
+ if ($action == 'confirm_delete' && ! empty($user->rights->mymodule->delete))
{
$result=$object->delete($user);
if ($result > 0)
@@ -216,10 +216,10 @@ if (empty($reshook))
* Put here all code to build page
****************************************************/
-llxHeader('','MyPageName','');
-
$form=new Form($db);
+llxHeader('','MyPageName','');
+
// Put here content of your page
diff --git a/htdocs/modulebuilder/skeletons/skeleton_class.class.php b/htdocs/modulebuilder/skeletons/skeleton_class.class.php
index 01b48c35f75..6805a697043 100644
--- a/htdocs/modulebuilder/skeletons/skeleton_class.class.php
+++ b/htdocs/modulebuilder/skeletons/skeleton_class.class.php
@@ -1,5 +1,5 @@
+/* Copyright (C) 2007-2017 Laurent Destailleur
* Copyright (C) 2014-2016 Juanjo Menent
* Copyright (C) 2015 Florian Henry
* Copyright (C) 2015 Raphaël Doursenaud
@@ -20,10 +20,9 @@
*/
/**
- * \file dev/skeletons/skeleton_class.class.php
- * \ingroup mymodule othermodule1 othermodule2
- * \brief This file is an example for a CRUD class file (Create/Read/Update/Delete)
- * Put some comments here
+ * \file modulebuilder/skeletons/skeleton_class.class.php
+ * \ingroup mymodule othermodule1 othermodule2
+ * \brief This file is an example for a CRUD class file (Create/Read/Update/Delete)
*/
// Put here all includes required by your class file
@@ -34,26 +33,27 @@ require_once DOL_DOCUMENT_ROOT . '/core/class/commonobject.class.php';
/**
* Class Skeleton_Class
*
- * Put here description of your class
- *
- * @see CommonObject
+ * Put here description of your class.
*/
class Skeleton_Class extends CommonObject
{
/**
- * @var string Id to identify managed objects
+ * @var string Id to identify managed object
*/
public $element = 'skeleton';
/**
* @var string Name of table without prefix where object is stored
*/
public $table_element = 'skeleton';
-
- /**
- * @var Skeleton_ClassLine[] Lines
- */
- public $lines = array();
-
+ /**
+ * @var array Array with all fields and their property
+ */
+ public $picto = 'generic';
+ /**
+ * @var array Array with all fields and their property
+ */
+ public $fields;
+
/**
* @var mixed Sample property 1
*/
@@ -62,8 +62,21 @@ class Skeleton_Class extends CommonObject
* @var mixed Sample property 2
*/
public $prop2;
+
//...
-
+
+ protected $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
+
+ public $table_element_line = 'skeletondet';
+ public $class_element_line = 'SkeletonLine';
+ public $fk_element = 'fk_skeleton';
+ /**
+ * @var Skeleton_ClassLine[] Lines
+ */
+ public $lines = array();
+
+
+
/**
* Constructor
*
@@ -289,10 +302,10 @@ class Skeleton_Class extends CommonObject
*/
public function update(User $user, $notrigger = false)
{
- $error = 0;
-
dol_syslog(__METHOD__, LOG_DEBUG);
+ $error = 0;
+
// Clean parameters
if (isset($this->prop1)) {
$this->prop1 = trim($this->prop1);
diff --git a/htdocs/product/admin/product.php b/htdocs/product/admin/product.php
index 3582f8b25b6..5183eeb3382 100644
--- a/htdocs/product/admin/product.php
+++ b/htdocs/product/admin/product.php
@@ -512,10 +512,7 @@ foreach ($dirmodels as $reldir)
}
$htmltooltip.=''.$langs->trans("FeaturesSupported").': ';
$htmltooltip.=' '.$langs->trans("Logo").': '.yn($module->option_logo,1,1);
- $htmltooltip.=' '.$langs->trans("PaymentMode").': '.yn($module->option_modereg,1,1);
- $htmltooltip.=' '.$langs->trans("PaymentConditions").': '.yn($module->option_condreg,1,1);
$htmltooltip.=' '.$langs->trans("MultiLanguage").': '.yn($module->option_multilang,1,1);
- $htmltooltip.=' '.$langs->trans("WatermarkOnDraftOrders").': '.yn($module->option_draft_watermark,1,1);
print '';
diff --git a/htdocs/product/card.php b/htdocs/product/card.php
index 536bcece10f..71f7cd8e7f6 100644
--- a/htdocs/product/card.php
+++ b/htdocs/product/card.php
@@ -1801,60 +1801,61 @@ if (($action == 'clone' && (empty($conf->use_javascript_ajax) || ! empty($conf->
/* Barre d'action */
/* */
/* ************************************************************************** */
-
-print "\n".''."\n";
-
-$parameters=array();
-$reshook=$hookmanager->executeHooks('addMoreActionsButtons',$parameters,$object,$action); // Note that $action and $object may have been modified by hook
-if (empty($reshook))
+if ($action != 'create' && $action != 'edit')
{
- if (($object->type == Product::TYPE_PRODUCT && $user->rights->produit->creer ) ||
- ($object->type == Product::TYPE_SERVICE && $user->rights->service->creer))
+ print "\n".'
'."\n";
+
+ $parameters=array();
+ $reshook=$hookmanager->executeHooks('addMoreActionsButtons',$parameters,$object,$action); // Note that $action and $object may have been modified by hook
+ if (empty($reshook))
{
- if (! isset($object->no_button_edit) || $object->no_button_edit <> 1) print '
';
-
- if (! isset($object->no_button_copy) || $object->no_button_copy <> 1)
+ if (($object->type == Product::TYPE_PRODUCT && $user->rights->produit->creer ) ||
+ ($object->type == Product::TYPE_SERVICE && $user->rights->service->creer))
{
- if (! empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))
+ if (! isset($object->no_button_edit) || $object->no_button_edit <> 1) print '
';
+
+ if (! isset($object->no_button_copy) || $object->no_button_copy <> 1)
{
- print '
'.$langs->trans('ToClone').'
'."\n";
- }
- else
- {
- print '
';
+ if (! empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))
+ {
+ print '
'.$langs->trans('ToClone').'
'."\n";
+ }
+ else
+ {
+ print '
';
+ }
}
}
- }
- $object_is_used = $object->isObjectUsed($object->id);
-
- if (($object->type == Product::TYPE_PRODUCT && $user->rights->produit->supprimer)
- || ($object->type == Product::TYPE_SERVICE && $user->rights->service->supprimer))
- {
- if (empty($object_is_used) && (! isset($object->no_button_delete) || $object->no_button_delete <> 1))
+ $object_is_used = $object->isObjectUsed($object->id);
+
+ if (($object->type == Product::TYPE_PRODUCT && $user->rights->produit->supprimer)
+ || ($object->type == Product::TYPE_SERVICE && $user->rights->service->supprimer))
{
- if (! empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))
+ if (empty($object_is_used) && (! isset($object->no_button_delete) || $object->no_button_delete <> 1))
{
- print '
'.$langs->trans('Delete').'
'."\n";
+ if (! empty($conf->use_javascript_ajax) && empty($conf->dol_use_jmobile))
+ {
+ print '
'.$langs->trans('Delete').'
'."\n";
+ }
+ else
+ {
+ print '
';
+ }
}
else
- {
- print '
';
+ {
+ print '
';
}
}
else
- {
- print '
';
+ {
+ print '
';
}
}
- else
- {
- print '
';
- }
+
+ print "\n
\n";
}
-print "\n
\n";
-
-
/*
* All the "Add to" areas
*/
@@ -1974,7 +1975,7 @@ if (! empty($conf->global->PRODUCT_ADD_FORM_ADD_TO) && $object->id && ($action =
* Documents generes
*/
-if ($action != 'edit' && $action != 'delete')
+if ($action != 'create' && $action != 'edit' && $action != 'delete')
{
print '';
print '
'; // ancre
diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php
index e25ad186369..76a609e0a44 100644
--- a/htdocs/product/class/product.class.php
+++ b/htdocs/product/class/product.class.php
@@ -4367,14 +4367,31 @@ class Product extends CommonObject
$this->id=0;
$this->ref = 'PRODUCT_SPEC';
$this->label = 'PRODUCT SPECIMEN';
- $this->description = 'PRODUCT SPECIMEN '.dol_print_date($now,'dayhourlog');
+ $this->description = 'This is description of this product specimen that was created the '.dol_print_date($now,'dayhourlog').'.';
$this->specimen=1;
$this->country_id=1;
$this->tosell=1;
$this->tobuy=1;
$this->tobatch=0;
$this->note='This is a comment (private)';
+ $this->date_creation = $now;
+ $this->date_modification = $now;
+
+ $this->weight = 4;
+ $this->weight_unit = 1;
+ $this->length = 5;
+ $this->length_unit = 1;
+ $this->width = 6;
+ $this->width_unit = 0;
+ $this->height = null;
+ $this->height_unit = null;
+
+ $this->surface = 30;
+ $this->surface_unit = 0;
+ $this->volume = 300;
+ $this->volume_unit = 0;
+
$this->barcode=-1; // Create barcode automatically
}
diff --git a/htdocs/product/inventory/ajax/ajax.inventory.php b/htdocs/product/inventory/ajax/ajax.inventory.php
new file mode 100644
index 00000000000..4884d7ab065
--- /dev/null
+++ b/htdocs/product/inventory/ajax/ajax.inventory.php
@@ -0,0 +1,51 @@
+rights->stock->creer)) { echo -1; exit; }
+
+ $fk_det_inventory = GETPOST('fk_det_inventory');
+
+ $det = new Inventorydet($db);
+ if( $det->fetch( $fk_det_inventory))
+ {
+ $det->qty_view+=GETPOST('qty');
+ $res = $det->update($user);
+
+ echo $det->qty_view;
+ }
+ else
+ {
+ echo -2;
+ }
+
+ break;
+
+ case 'pmp':
+ if (empty($user->rights->stock->creer) || empty($user->rights->stock->changePMP)) { echo -1; exit; }
+
+ $fk_det_inventory = GETPOST('fk_det_inventory');
+
+ $det = new Inventorydet($db);
+ if( $det->fetch( $fk_det_inventory))
+ {
+ $det->new_pmp=price2num(GETPOST('pmp'));
+ $det->update($user);
+
+ echo $det->new_pmp;
+ }
+ else
+ {
+ echo -2;
+ }
+
+ break;
+ }
+
diff --git a/htdocs/product/inventory/card.php b/htdocs/product/inventory/card.php
new file mode 100644
index 00000000000..34e79da8a69
--- /dev/null
+++ b/htdocs/product/inventory/card.php
@@ -0,0 +1,682 @@
+
+ *
+ * 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/inventory/card.php
+ * \ingroup product
+ * \brief File of class to manage inventory
+ */
+
+require_once '../../main.inc.php';
+
+require_once DOL_DOCUMENT_ROOT.'/product/inventory/listview.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+include_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
+require_once DOL_DOCUMENT_ROOT.'/product/inventory/class/inventory.class.php';
+require_once DOL_DOCUMENT_ROOT.'/product/inventory/lib/inventory.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
+
+
+$langs->load('stock');
+$langs->load('inventory');
+
+$id=GETPOST('id', 'int');
+$ref=GETPOST('ref', 'alpha');
+$action=(GETPOST('action','alpha') ? GETPOST('action','alpha') : 'view');
+$cancel=GETPOST('cancel');
+$confirm=GETPOST('confirm','alpha');
+$socid=GETPOST('socid','int');
+if (! empty($user->societe_id)) $socid=$user->societe_id;
+
+if (empty($action) && empty($id) && empty($ref)) $action='view';
+
+// Protection if external user
+if ($user->societe_id > 0)
+{
+ //accessforbidden();
+}
+$result = restrictedArea($user, 'stock', $id);
+
+
+$object = new Inventory($db);
+$extrafields = new ExtraFields($db);
+
+// fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label($object->table_element);
+
+// Load object
+include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals
+
+// Initialize technical object to manage hooks of modules. Note that conf->hooks_modules contains array array
+$hookmanager->initHooks(array('inventorycard'));
+
+
+
+/*
+ * Actions
+ */
+
+$parameters=array('id'=>$id, 'ref'=>$ref, 'objcanvas'=>$objcanvas);
+$reshook=$hookmanager->executeHooks('doActions',$parameters,$object,$action); // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+if (empty($reshook))
+{
+ if ($cancel)
+ {
+ if ($action != 'addlink')
+ {
+ $urltogo=$backtopage?$backtopage:dol_buildpath('/product/inventory/list.php',1);
+ header("Location: ".$urltogo);
+ exit;
+ }
+ if ($id > 0 || ! empty($ref)) $ret = $object->fetch($id,$ref);
+ $action='';
+ }
+
+ if ($action == 'confirmCreate')
+ {
+ if (empty($user->rights->stock->creer)) accessforbidden();
+
+ if ($cancel)
+ {
+ $urltogo=$backtopage?$backtopage:dol_buildpath('/product/inventory/list.php',1);
+ header("Location: ".$urltogo);
+ exit;
+ }
+
+ $error=0;
+
+ $object->setValues($_POST);
+
+ $fk_inventory = $object->create($user);
+ if ($fk_inventory>0)
+ {
+ $fk_category = (int) GETPOST('fk_category');
+ $fk_supplier = (int) GETPOST('fk_supplier');
+ $fk_warehouse = (int) GETPOST('fk_warehouse');
+ $only_prods_in_stock = (int) GETPOST('OnlyProdsInStock');
+
+ $object->addProductsFor($fk_warehouse,$fk_category,$fk_supplier,$only_prods_in_stock);
+ $object->update($user);
+
+ header('Location: '.dol_buildpath('/product/inventory/card.php?id='.$object->id.'&action=edit', 1));
+ exit;
+ }
+ else
+ {
+ setEventMessage($object->error,'errors');
+ header('Location: '.dol_buildpath('/product/inventory/card.php?action=create', 1));
+ exit;
+ }
+ }
+
+ switch($action) {
+ case 'save':
+ if (!$user->rights->stock->creer) accessforbidden();
+
+
+ $id = GETPOST('id');
+
+ $object = new Inventory($db);
+ $object->fetch($id);
+
+ $object->setValues($_REQUEST);
+
+ if ($object->errors)
+ {
+ setEventMessage($object->errors, 'errors');
+ $action = 'edit';
+ }
+ else
+ {
+ $object->udpate($user);
+ header('Location: '.dol_buildpath('/product/inventory/card.php?id='.$object->getId().'&action=view', 1));
+ exit;
+ }
+
+ break;
+
+ case 'confirm_regulate':
+ if (!$user->rights->stock->creer) accessforbidden();
+ $id = GETPOST('id');
+
+ $object = new Inventory($db);
+ $object->fetch($id);
+
+ if($object->status == 0) {
+ $object->status = 1;
+ $object->update($user);
+
+ $action='view';
+ }
+ else {
+ $action='view';
+ }
+
+ break;
+
+ case 'confirm_changePMP':
+
+ $id = GETPOST('id');
+
+ $object = new Inventory($db);
+ $object->fetch( $id );
+
+ $object->changePMP($user);
+
+ $action='view';
+
+ break;
+
+ case 'add_line':
+ if (!$user->rights->stock->creer) accessforbidden();
+
+ $id = GETPOST('id');
+ $fk_warehouse = GETPOST('fk_warehouse');
+
+ $object = new Inventory($db);
+ $object->fetch( $id );
+
+ $fk_product = GETPOST('fk_product');
+ if ($fk_product>0)
+ {
+ $product = new Product($db);
+ if($product->fetch($fk_product)<=0 || $product->type != 0) {
+ setEventMessage($langs->trans('ThisIsNotAProduct'),'errors');
+ }
+ else{
+
+ //Check product not already exists
+ $alreadyExists = false;
+ if(!empty($object->Inventorydet)) {
+ foreach ($object->Inventorydet as $invdet)
+ {
+ if ($invdet->fk_product == $product->id
+ && $invdet->fk_warehouse == $fk_warehouse)
+ {
+ $alreadyExists = true;
+ break;
+ }
+ }
+ }
+ if (!$alreadyExists)
+ {
+ if($object->addProduct($product->id, $fk_warehouse)) {
+ setEventMessage($langs->trans('ProductAdded'));
+ }
+ }
+ else
+ {
+ setEventMessage($langs->trans('inventoryWarningProductAlreadyExists'), 'warnings');
+ }
+
+ }
+
+ $object->update($user);
+ $object->sortDet();
+ }
+
+ $action='edit';
+
+ break;
+
+ case 'confirm_delete_line':
+ if (!$user->rights->stock->creer) accessforbidden();
+
+
+ //Cette action devrais se faire uniquement si le status de l'inventaire est à 0 mais aucune vérif
+ $rowid = GETPOST('rowid');
+ $objectdet = new Inventorydet($db);
+ if($objectdet->fetch($rowid)>0) {
+ $objectdet->delete($user);
+ setEventMessage("ProductDeletedFromInventory");
+ }
+ $id = GETPOST('id');
+ $object = new Inventory($db);
+ $object->fetch( $id);
+
+ $action='edit';
+
+ break;
+ case 'confirm_flush':
+ if (!$user->rights->stock->creer) accessforbidden();
+
+
+ $id = GETPOST('id');
+
+ $object = new Inventory($db);
+ $object->fetch($id);
+
+ $object->deleteAllLine($user);
+
+ setEventMessage($langs->trans('InventoryFlushed'));
+
+ $action='edit';
+
+ break;
+ case 'confirm_delete':
+ if (!$user->rights->stock->supprimer) accessforbidden();
+
+ $id = GETPOST('id');
+
+ $object = new Inventory($db);
+ $object->fetch($id);
+
+ $object->delete($user);
+
+ setEventMessage($langs->trans('InventoryDeleted'));
+
+ header('Location: '.dol_buildpath('/inventory/list.php', 1));
+ exit;
+
+ break;
+ /*case 'exportCSV':
+
+ $id = GETPOST('id');
+
+ $object = new Inventory($db);
+ $object->fetch($id);
+
+ _exportCSV($object);
+
+ exit;
+ break;
+ */
+ }
+}
+
+
+/*
+ * Views
+ */
+
+$form=new Form($db);
+
+llxHeader('',$langs->trans('Inventory'),'','');
+
+if ($action == 'create')
+{
+ if (empty($user->rights->stock->creer)) accessforbidden();
+
+ print load_fiche_titre($langs->trans("NewInventory"));
+
+ echo '
';
+ echo '
';
+
+ dol_fiche_head();
+
+ $formproduct = new FormProduct($db);
+
+ ?>
+
+ ';
+ print '
';
+ print ' ';
+ print '
';
+ print '
';
+
+ echo '';
+
+}
+
+if ($action == 'view' || $action == 'edit' || empty($action))
+{
+ $object = new Inventory($db);
+ $object->fetch($id);
+
+ if($action == 'changePMP')
+ {
+ print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id, $langs->trans('ApplyNewPMP'), $langs->trans('ConfirmApplyNewPMP', $object->getTitle()), 'confirm_changePMP', array(),'no',1);
+ }
+ else if($action == 'flush')
+ {
+ print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id,$langs->trans('FlushInventory'),$langs->trans('ConfirmFlushInventory',$object->getTitle()),'confirm_flush',array(),'no',1);
+ }
+ else if($action == 'delete')
+ {
+ print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id,$langs->trans('Delete'),$langs->trans('ConfirmDelete',$object->getTitle()),'confirm_delete',array(),'no',1);
+ }
+ else if($action == 'delete_line')
+ {
+ print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&rowid='.GETPOST('rowid'),$langs->trans('DeleteLine'),$langs->trans('ConfirmDeleteLine',$object->getTitle()),'confirm_delete_line',array(),'no',1);
+ }
+ else if($action == 'regulate')
+ {
+ print $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id,$langs->trans('RegulateStock'),$langs->trans('ConfirmRegulateStock',$object->getTitle()),'confirm_regulate',array(),'no',1);
+ }
+
+ $warehouse = new Entrepot($db);
+ $warehouse->fetch($object->fk_warehouse);
+
+ print dol_get_fiche_head(inventoryPrepareHead($object, $langs->trans('inventoryOfWarehouse', $warehouse->libelle), empty($action) ? '': '&action='.$action));
+
+ $lines = array();
+ card_line($object, $lines, $action);
+
+ print '
'.$langs->trans('inventoryOnDate')." ".$object->getDate('date_inventory').' ';
+
+ $objectTPL = array(
+ 'id'=> $object->id
+ ,'date_cre' => $object->getDate('date_cre', 'd/m/Y')
+ ,'date_maj' => $object->getDate('date_maj', 'd/m/Y H:i')
+ ,'fk_warehouse' => $object->fk_warehouse
+ ,'status' => $object->status
+ ,'entity' => $object->entity
+ ,'amount' => price( round($object->amount,2) )
+ ,'amount_actual'=>price (round($object->amount_actual,2))
+
+ );
+
+ $can_validate = !empty($user->rights->stock->validate);
+ $view_url = dol_buildpath('/product/inventory/card.php', 1);
+
+ $view = array(
+ 'mode' => $action
+ ,'url' => dol_buildpath('/product/inventory/card.php', 1)
+ ,'can_validate' => (int) $user->rights->stock->validate
+ ,'is_already_validate' => (int) $object->status
+ ,'token'=>$_SESSION['newtoken']
+ );
+
+ include './tpl/inventory.tpl.php';
+}
+
+// End of page
+llxFooter();
+$db->close();
+
+
+
+function card_line(&$inventory, &$lines, $mode)
+{
+ global $db,$langs,$user,$conf;
+ $inventory->amount_actual = 0;
+
+ $TCacheEntrepot = array();
+
+ foreach ($inventory->Inventorydet as $k => $Inventorydet)
+ {
+ $product = & $Inventorydet->product;
+ $stock = $Inventorydet->qty_stock;
+
+ $pmp = $Inventorydet->pmp;
+ $pmp_actual = $pmp * $stock;
+ $inventory->amount_actual+=$pmp_actual;
+
+ $last_pa = $Inventorydet->pa;
+ $current_pa = $Inventorydet->current_pa;
+
+ $e = new Entrepot($db);
+ if(!empty($TCacheEntrepot[$Inventorydet->fk_warehouse])) $e = $TCacheEntrepot[$Inventorydet->fk_warehouse];
+ elseif($e->fetch($Inventorydet->fk_warehouse) > 0) $TCacheEntrepot[$e->id] = $e;
+
+ $qtytoadd = GETPOST('qty_to_add', 'array');
+ $qty = (float) $qtytoadd[$k];
+
+ $lines[]=array(
+ 'produit' => $product->getNomUrl(1).' - '.$product->label,
+ 'entrepot'=>$e->getNomUrl(1),
+ 'barcode' => $product->barcode,
+ 'qty' =>($mode == 'edit' ? '
'.img_picto($langs->trans('Add'), 'edit_add').' ' : '' ),
+ 'qty_view' => ($Inventorydet->qty_view ? $Inventorydet->qty_view : 0),
+ 'qty_stock' => $stock,
+ 'qty_regulated' => ($Inventorydet->qty_regulated ? $Inventorydet->qty_regulated : 0),
+ 'action' => ($user->rights->stock->write && $mode=='edit' ? '
'.img_picto($langs->trans('inventoryDeleteLine'), 'delete').' ' : ''),
+ 'pmp_stock'=>round($pmp_actual,2),
+ 'pmp_actual'=> round($pmp * $Inventorydet->qty_view,2),
+ 'pmp_new'=>(!empty($user->rights->stock->changePMP) && $mode == 'edit' ? '
'.img_picto($langs->trans('Save'), 'bt-save.png@inventory').' ' : price($Inventorydet->new_pmp)),
+ 'pa_stock'=>round($last_pa * $stock,2),
+ 'pa_actual'=>round($last_pa * $Inventorydet->qty_view,2),
+ 'current_pa_stock'=>round($current_pa * $stock,2),
+ 'current_pa_actual'=>round($current_pa * $Inventorydet->qty_view,2),
+ 'k'=>$k,
+ 'id'=>$Inventorydet->id
+ );
+ }
+
+}
+
+
+/*
+function _exportCSV(&$inventory)
+{
+ global $conf;
+
+ header('Content-Type: application/octet-stream');
+ header('Content-disposition: attachment; filename=inventory-'. $inventory->getId().'-'.date('Ymd-His').'.csv');
+ header('Pragma: no-cache');
+ header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
+ header('Expires: 0');
+
+ echo 'Ref;Label;barcode;qty theorique;PMP;dernier PA;';
+ if(!empty($conf->global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)) echo 'PA courant;';
+ echo 'qty réelle;PMP;dernier PA;';
+ if(!empty($conf->global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)) echo 'PA courant;';
+ echo 'qty regulée;'."\r\n";
+
+ foreach ($inventory->Inventorydet as $k => $Inventorydet)
+ {
+ $product = & $Inventorydet->product;
+ $stock = $Inventorydet->qty_stock;
+
+ $pmp = $Inventorydet->pmp;
+ $pmp_actual = $pmp * $stock;
+ $inventory->amount_actual+=$pmp_actual;
+
+ $last_pa = $Inventorydet->pa;
+ $current_pa = $Inventorydet->current_pa;
+
+ if(!empty($conf->global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)) {
+ $row=array(
+ 'produit' => $product->ref
+ ,'label'=>$product->label
+ ,'barcode' => $product->barcode
+ ,'qty_stock' => $stock
+ ,'pmp_stock'=>round($pmp_actual,2)
+ ,'pa_stock'=>round($last_pa * $stock,2)
+ ,'current_pa_stock'=>round($current_pa * $stock,2)
+ ,'qty_view' => $Inventorydet->qty_view ? $Inventorydet->qty_view : 0
+ ,'pmp_actual'=>round($pmp * $Inventorydet->qty_view,2)
+ ,'pa_actual'=>round($last_pa * $Inventorydet->qty_view,2)
+ ,'current_pa_actual'=>round($current_pa * $Inventorydet->qty_view,2)
+ ,'qty_regulated' => $Inventorydet->qty_regulated ? $Inventorydet->qty_regulated : 0
+
+ );
+
+ }
+ else{
+ $row=array(
+ 'produit' => $product->ref
+ ,'label'=>$product->label
+ ,'barcode' => $product->barcode
+ ,'qty_stock' => $stock
+ ,'pmp_stock'=>round($pmp_actual,2)
+ ,'pa_stock'=>round($last_pa * $stock,2)
+ ,'qty_view' => $Inventorydet->qty_view ? $Inventorydet->qty_view : 0
+ ,'pmp_actual'=>round($pmp * $Inventorydet->qty_view,2)
+ ,'pa_actual'=>round($last_pa * $Inventorydet->qty_view,2)
+
+ ,'qty_regulated' => $Inventorydet->qty_regulated ? $Inventorydet->qty_regulated : 0
+
+ );
+
+ }
+
+
+ echo '"'.implode('";"', $row).'"'."\r\n";
+
+ }
+
+ exit;
+}
+*/
+
+function _footerList($view,$total_pmp,$total_pmp_actual,$total_pa,$total_pa_actual, $total_current_pa,$total_current_pa_actual)
+{
+ global $conf,$user,$langs;
+
+ if ($view['can_validate'] == 1) { ?>
+
+
+ barcode->enabled)) { ?>
+
+
+
+
+ global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)){
+ echo ''.price($total_current_pa).' ';
+ }
+ ?>
+
+
+ rights->stock->changePMP)) {
+ echo ' ';
+ }
+ ?>
+
+ global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)){
+ echo ''.price($total_current_pa_actual).' ';
+ }
+ ?>
+
+
+
+
+
+
+
+
+ Produit
+ trans('Warehouse'); ?>
+ barcode->enabled)) { ?>
+ trans('Barcode'); ?>
+
+
+ trans('TheoricalQty'); ?>
+ global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)){
+ echo ''.$langs->trans('TheoricalValue').' ';
+ }
+ else {
+ echo ''.$langs->trans('TheoricalValue').' ';
+ }
+
+ ?>
+
+
+ trans('RealQty'); ?>
+
+
+ global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)) $colspan++;
+ if(!empty($conf->global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)) $colspan++;
+
+ echo ''.$langs->trans('RealValue').' ';
+
+ ?>
+
+ trans('RegulatedQty'); ?>
+
+
+ #
+
+
+
+
+
+
+ trans('PMP'); ?>
+ trans('LastPA'); ?>
+ global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)){
+ echo ''.$langs->trans('CurrentPA').' ';
+ }
+
+ ?>
+
+ trans('PMP'); ?>
+ rights->stock->changePMP)) {
+ echo ''.$langs->trans('ColumnNewPMP').' ';
+ }
+ ?>
+ trans('LastPA'); ?>
+ global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)){
+ echo ''.$langs->trans('CurrentPA').' ';
+ }
+
+ ?>
+
+
+
+
+
+
+ *
+ * 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/inventory/class/product.class.php
+ * \ingroup product
+ * \brief File of class to manage predefined products stock
+ */
+
+require_once DOL_DOCUMENT_ROOT.'/core/class/coreobject.class.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+
+/**
+ * Class to manage inventories
+ */
+class Inventory extends CoreObject
+{
+ public $element='inventory';
+ public $table_element='inventory';
+ public $fk_element='fk_inventory';
+ protected $childtables=array('inventorydet'); // To test if we can delete object
+ protected $isnolinkedbythird = 1; // No field fk_soc
+ protected $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
+
+ /**
+ * Warehouse Id
+ * @var int
+ */
+ public $fk_warehouse;
+ /**
+ * Entity Id
+ * @var int
+ */
+ public $entity;
+
+ /**
+ * Status
+ * @var int
+ */
+ public $status;
+ /**
+ * Inventory Date
+ * @var date
+ */
+ public $date_inventory;
+ /**
+ * Inventory Title
+ * @var string
+ */
+ public $title;
+
+ /**
+ * Attribute object linked with database
+ * @var array
+ */
+ protected $fields=array(
+ 'fk_warehouse'=>array('type'=>'integer','index'=>true)
+ ,'entity'=>array('type'=>'integer','index'=>true)
+ ,'status'=>array('type'=>'integer','index'=>true)
+ ,'date_inventory'=>array('type'=>'date')
+ ,'title'=>array('type'=>'string')
+ );
+
+ /**
+ * Constructor
+ *
+ * @param DoliDB $db Database handler
+ */
+ public function __construct(DoliDB &$db)
+ {
+ global $conf;
+
+ parent::__construct($db);
+ parent::init();
+
+ $this->status = 0;
+ $this->entity = $conf->entity;
+ $this->errors = array();
+ $this->amount = 0;
+ }
+
+ /**
+ * Function to sort children object
+ */
+ public function sortDet()
+ {
+ if(!empty($this->Inventorydet)) usort($this->Inventorydet, array('Inventory', 'customSort'));
+ }
+
+ /**
+ * Get object and children from database
+ *
+ * @param int $id Id of object to load
+ * @param bool $loadChild used to load children from database
+ * @return int >0 if OK, <0 if KO, 0 if not found
+ */
+ public function fetch($id, $loadChild = true)
+ {
+ if(!$loadChild) $this->withChild = false;
+
+ $res = parent::fetch($id, $loadChild);
+ if($res > 0)
+ {
+ $this->sortDet();
+ $this->amount = 0;
+ if(!empty($this->Inventorydet ))
+ {
+ foreach($this->Inventorydet as &$det)
+ {
+ $this->amount += $det->qty_view * $det->pmp;
+ }
+ }
+ }
+
+ return $res;
+ }
+
+ /**
+ * Custom function call by usort
+ *
+ * @param Inventorydet $objA first Inventorydet object
+ * @param Inventorydet $objB second Inventorydet object
+ * @return int
+ */
+ private function customSort(&$objA, &$objB)
+ {
+ $r = strcmp(strtoupper(trim($objA->product->ref)), strtoupper(trim($objB->product->ref)));
+
+ if ($r < 0) $r = -1;
+ elseif ($r > 0) $r = 1;
+ else $r = 0;
+
+ return $r;
+ }
+
+ /**
+ * @param User $user user object
+ * @return int
+ */
+ public function changePMP(User &$user)
+ {
+ $error = 0;
+ $this->db->begin();
+
+ if(!empty($this->Inventorydet))
+ {
+ foreach ($this->Inventorydet as $k => &$Inventorydet)
+ {
+ if($Inventorydet->new_pmp>0)
+ {
+ $Inventorydet->pmp = $Inventorydet->new_pmp;
+ $Inventorydet->new_pmp = 0;
+
+ $res = $this->db->query('UPDATE '.MAIN_DB_PREFIX.'product as p SET pmp = '.$Inventorydet->pmp.' WHERE rowid = '.$Inventorydet->fk_product );
+ if (!$res)
+ {
+ $error++;
+ $this->error = $this->db->lasterror();
+ $this->errors[] = $this->db->lasterror();
+ }
+ }
+ }
+ }
+
+ $res = parent::update($user);
+ if (!$res)
+ {
+ $error++;
+ $this->error = $this->db->lasterror();
+ $this->errors[] = $this->db->lasterror();
+ }
+
+
+ if (!$error)
+ {
+ $this->db->commit();
+ return 1;
+ }
+ else
+ {
+ $this->db->rollback();
+ return -1;
+ }
+ }
+
+ /**
+ * Function to update object or create or delete if needed
+ *
+ * @param User $user user object
+ * @return < 0 if ko, > 0 if ok
+ */
+ public function update(User &$user)
+ {
+ $error = 0;
+ $this->db->begin();
+
+ // if we valid the inventory we save the stock at the same time
+ if ($this->status)
+ {
+ $res = $this->regulate();
+ if ($res < 0)
+ {
+ $error++;
+ $this->error = $this->db->lasterror();
+ $this->errors[] = $this->db->lasterror();
+ }
+ }
+
+ $res = parent::update($user);
+ if (!$res)
+ {
+ $error++;
+ $this->error = $this->db->lasterror();
+ $this->errors[] = $this->db->lasterror();
+ }
+
+ if (!$error)
+ {
+ $this->db->commit();
+ return $this->id;
+ }
+ else
+ {
+ $this->db->rollback();
+ return -1;
+ }
+ }
+
+ /**
+ * Function to update current object
+ *
+ * @param array $Tab Array of values
+ * @return int
+ */
+ public function setValues(&$Tab)
+ {
+ global $langs;
+
+ if (isset($Tab['qty_to_add']))
+ {
+ foreach ($Tab['qty_to_add'] as $k => $qty)
+ {
+ $qty = (float) price2num($qty);
+
+ if ($qty < 0)
+ {
+ $this->errors[] = $langs->trans('inventoryErrorQtyAdd');
+ return -1;
+ }
+
+ $product = new Product($this->db);
+ $product->fetch($this->Inventorydet[$k]->fk_product);
+
+ $this->Inventorydet[$k]->pmp = $product->pmp;
+ $this->Inventorydet[$k]->qty_view += $qty;
+ }
+ }
+
+ return parent::setValues($Tab);
+ }
+
+ /**
+ * Function to delete all Inventorydet
+ *
+ * @param User $user user object
+ * @return < 0 if ko, > 0 if ok
+ */
+ public function deleteAllLine(User &$user)
+ {
+ foreach($this->Inventorydet as &$det)
+ {
+ $det->to_delete = true;
+ }
+
+ $res = $this->update($user);
+
+ if ($res > 0) $this->Inventorydet = array();
+ else return -1;
+ }
+
+ /**
+ * Function to add Inventorydet
+ *
+ * @param int $fk_product fk_product of Inventorydet
+ * @param int $fk_warehouse fk_warehouse target
+ * @return bool
+ */
+ public function addProduct($fk_product, $fk_warehouse=0)
+ {
+ $k = $this->addChild('Inventorydet');
+ $det = &$this->Inventorydet[$k];
+
+ $det->fk_inventory = $this->id;
+ $det->fk_product = $fk_product;
+ $det->fk_warehouse = empty($fk_warehouse) ? $this->fk_warehouse : $fk_warehouse;
+
+ $det->load_product();
+
+ $date = $this->getDate('date_inventory', 'Y-m-d');
+ if(empty($date)) $date = $this->getDate('datec', 'Y-m-d');
+ $det->setStockDate($date, $fk_warehouse);
+
+ return true;
+ }
+
+ /**
+ * Duplication method product to add datem
+ * Adjust stock in a warehouse for product
+ *
+ * @param int $fk_product id of product
+ * @param int $fk_warehouse id of warehouse
+ * @param double $nbpiece nb of units
+ * @param int $movement 0 = add, 1 = remove
+ * @param string $label Label of stock movement
+ * @param double $price Unit price HT of product, used to calculate average weighted price (PMP in french). If 0, average weighted price is not changed.
+ * @param string $inventorycode Inventory code
+ * @return int <0 if KO, >0 if OK
+ */
+ public function correctStock($fk_product, $fk_warehouse, $nbpiece, $movement, $label='', $price=0, $inventorycode='')
+ {
+ global $conf, $user;
+
+ if ($fk_warehouse)
+ {
+ $this->db->begin();
+
+ require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php';
+
+ $op[0] = "+".trim($nbpiece);
+ $op[1] = "-".trim($nbpiece);
+
+ $datem = empty($conf->global->INVENTORY_USE_INVENTORY_DATE_FROM_DATEMVT) ? dol_now() : $this->date_inventory;
+
+ $movementstock=new MouvementStock($this->db);
+ $movementstock->origin = new stdClass();
+ $movementstock->origin->element = 'inventory';
+ $movementstock->origin->id = $this->id;
+ $result=$movementstock->_create($user,$fk_product,$fk_warehouse,$op[$movement],$movement,$price,$label,$inventorycode, $datem);
+
+ if ($result >= 0)
+ {
+ $this->db->commit();
+ return 1;
+ }
+ else
+ {
+ $this->error=$movementstock->error;
+ $this->errors=$movementstock->errors;
+
+ $this->db->rollback();
+ return -1;
+ }
+ }
+ }
+
+ /**
+ * Function to regulate stock
+ *
+ * @return int
+ */
+ public function regulate()
+ {
+ global $langs,$conf;
+
+ if($conf->global->INVENTORY_DISABLE_VIRTUAL)
+ {
+ $pdt_virtuel = false;
+ // Test if virtual product is enabled
+ if($conf->global->PRODUIT_SOUSPRODUITS)
+ {
+ $pdt_virtuel = true;
+ $conf->global->PRODUIT_SOUSPRODUITS = 0;
+ }
+ }
+
+ foreach ($this->Inventorydet as $k => $Inventorydet)
+ {
+ $product = new Product($this->db);
+ $product->fetch($Inventorydet->fk_product);
+
+ if ($Inventorydet->qty_view != $Inventorydet->qty_stock)
+ {
+ $Inventorydet->qty_regulated = $Inventorydet->qty_view - $Inventorydet->qty_stock;
+ $nbpiece = abs($Inventorydet->qty_regulated);
+ $movement = (int) ($Inventorydet->qty_view < $Inventorydet->qty_stock); // 0 = add ; 1 = remove
+
+ //$href = dol_buildpath('/inventory/inventory.php?id='.$this->id.'&action=view', 1);
+
+ $res = $this->correctStock($product->id, $Inventorydet->fk_warehouse, $nbpiece, $movement, $langs->trans('inventoryMvtStock'));
+ if ($res < 0) return -1;
+ }
+ }
+
+ if($conf->global->INVENTORY_DISABLE_VIRTUAL)
+ {
+ // Test if virtual product was enabled before regulate
+ if($pdt_virtuel) $conf->global->PRODUIT_SOUSPRODUITS = 1;
+ }
+
+ return 1;
+ }
+
+ /**
+ * Get the title
+ * @return string
+ */
+ public function getTitle()
+ {
+ global $langs;
+
+ return !empty($this->title) ? $this->title : $langs->trans('inventoryTitle').' '.$this->id;
+ }
+
+
+ /**
+ * Return clicable link of object (with eventually picto)
+ *
+ * @param int $withpicto Add picto into link
+ * @return string
+ */
+ public function getNomUrl($withpicto = 1)
+ {
+ return '
'.($withpicto ? img_picto('','object_list.png','',0).' ' : '').$this->getTitle().' ';
+ }
+
+ /**
+ * Function to add products by default from warehouse and children
+ *
+ * @param int $fk_warehouse id of warehouse
+ * @param int $fk_category id of category
+ * @param int $fk_supplier id of supplier
+ * @param int $only_prods_in_stock only product with stock
+ *
+ * @return int
+ */
+ public function addProductsFor($fk_warehouse,$fk_category=0,$fk_supplier=0,$only_prods_in_stock=0)
+ {
+ $warehouse = new Entrepot($this->db);
+ $warehouse->fetch($fk_warehouse);
+ $TChildWarehouses = array($fk_warehouse);
+ $warehouse->get_children_warehouses($fk_warehouse, $TChildWarehouses);
+
+ $sql = 'SELECT ps.fk_product, ps.fk_entrepot';
+ $sql.= ' FROM '.MAIN_DB_PREFIX.'product_stock ps';
+ $sql.= ' INNER JOIN '.MAIN_DB_PREFIX.'product p ON (p.rowid = ps.fk_product)';
+ $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_product cp ON (cp.fk_product = p.rowid)';
+ $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_fournisseur_price pfp ON (pfp.fk_product = p.rowid)';
+ $sql.= ' WHERE ps.fk_entrepot IN ('.implode(', ', $TChildWarehouses).')';
+
+ if ($fk_category>0) $sql.= ' AND cp.fk_categorie='.$fk_category;
+ if ($fk_supplier>0) $sql.= ' AND pfp.fk_soc = '.$fk_supplier;
+ if (!empty($only_prods_in_stock)) $sql.= ' AND ps.reel > 0';
+
+ $sql.=' GROUP BY ps.fk_product, ps.fk_entrepot ORDER BY p.ref ASC,p.label ASC';
+
+ $res = $this->db->query($sql);
+ if($res)
+ {
+ while($obj = $this->db->fetch_object($res))
+ {
+ $this->addProduct($obj->fk_product, $obj->fk_entrepot);
+ }
+
+ return 1;
+ }
+ else
+ {
+ $this->error = $this->db->lasterror();
+ $this->errors[] = $this->db->lasterror();
+ return -1;
+ }
+ }
+
+ /**
+ * Return clicable link of inventory object
+ *
+ * @param int $id id of inventory
+ * @param int $withpicto Add picto into link
+ * @return string
+ */
+ static function getLink($id, $withpicto=1)
+ {
+ global $langs,$db;
+
+ $inventory = new Inventory($db);
+ if($inventory->fetch($id, false) > 0) return $inventory->getNomUrl($withpicto);
+ else return $langs->trans('InventoryUnableToFetchObject');
+ }
+
+ /**
+ * Function to get the sql select of inventory
+ *
+ * @param string $type 'All' to get all data
+ * @return string
+ */
+ static function getSQL($type)
+ {
+ global $conf;
+
+ $sql = '';
+ if($type == 'All')
+ {
+ $sql = 'SELECT i.rowid,i.title, e.label, i.date_inventory, i.fk_warehouse, i.datec, i.tms, i.status';
+ $sql.= ' FROM '.MAIN_DB_PREFIX.'inventory i';
+ $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'entrepot e ON (e.rowid = i.fk_warehouse)';
+ $sql.= ' WHERE i.entity IN ('.getEntity('inventory', 1).')';
+ }
+
+ return $sql;
+ }
+}
+
+class Inventorydet extends CoreObject
+{
+ public $element='inventorydet';
+ public $table_element='inventorydet';
+ protected $isnolinkedbythird = 1; // No field fk_soc
+ protected $ismultientitymanaged = 0; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
+
+ public $fk_inventory;
+ public $fk_warehouse;
+ public $fk_product;
+ public $entity;
+ public $qty_view;
+ public $qty_stock;
+ public $qty_regulated;
+ public $pmp;
+ public $pa;
+ public $new_pmp;
+
+ protected $fields=array(
+ 'fk_inventory'=>array('type'=>'int')
+ ,'fk_warehouse'=>array('type'=>'int')
+ ,'fk_product'=>array('type'=>'int')
+ ,'entity'=>array('type'=>'int')
+ ,'qty_view'=>array('type'=>'float')
+ ,'qty_stock'=>array('type'=>'float')
+ ,'qty_regulated'=>array('type'=>'float')
+ ,'pmp'=>array('type'=>'float')
+ ,'pa'=>array('type'=>'float')
+ ,'new_pmp'=>array('type'=>'float')
+ );
+
+ /**
+ * Constructor
+ *
+ * @param DoliDB $db Database handler
+ */
+ function __construct(DoliDB &$db)
+ {
+ global $conf;
+
+ parent::__construct($db);
+ parent::init();
+
+ $this->entity = $conf->entity;
+ $this->errors = array();
+
+ $this->product = null;
+ $this->current_pa = 0;
+ }
+
+ /**
+ * Get object and children from database
+ *
+ * @param int $id id of inventorydet object
+ * @param bool $loadChild load children
+ * @return int
+ */
+ function fetch($id, $loadChild = true)
+ {
+ $res = parent::fetch($id);
+ $this->load_product();
+ $this->fetch_current_pa();
+
+ return $res;
+ }
+
+ /**
+ * Function to get the unit buy price
+ *
+ * @return bool
+ */
+ function fetch_current_pa()
+ {
+ global $db,$conf;
+
+ if(empty($conf->global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)) return false;
+
+ if($this->pa > 0)
+ {
+ $this->current_pa = $this->pa;
+ }
+ else
+ {
+ dol_include_once('/fourn/class/fournisseur.product.class.php');
+ $p= new ProductFournisseur($db);
+ $p->find_min_price_product_fournisseur($this->fk_product);
+
+ if($p->fourn_qty>0) $this->current_pa = $p->fourn_price / $p->fourn_qty;
+ }
+
+ return true;
+ }
+
+ /**
+ * Function to set pa attribute from date en fk_warehouse
+ *
+ * @param date $date date value
+ * @param int $fk_warehouse fk_warehouse target
+ */
+ function setStockDate($date, $fk_warehouse)
+ {
+ list($pmp, $stock) = $this->getPmpStockFromDate($date, $fk_warehouse);
+
+ $this->qty_stock = $stock;
+ $this->pmp = $pmp;
+
+ $last_pa = 0;
+ $sql = 'SELECT price FROM '.MAIN_DB_PREFIX.'stock_mouvement';
+ $sql.= ' WHERE fk_entrepot = '.$fk_warehouse;
+ $sql.= ' AND fk_product = '.$this->fk_product;
+ $sql.= ' AND (origintype=\'order_supplier\' || origintype=\'invoice_supplier\')';
+ $sql.= ' AND price > 0';
+ $sql.= ' AND datem <= \''.$date.' 23:59:59\'';
+ $sql.= ' ORDER BY datem DESC LIMIT 1';
+
+ $res = $this->db->query($sql);
+ if($res && $obj = $this->db->fetch_object($res))
+ {
+ $last_pa = $obj->price;
+ }
+
+ $this->pa = $last_pa;
+ }
+
+
+ /**
+ * Get the last pmp and last stock from date and warehouse
+ *
+ * @param date $date date to check
+ * @param int $fk_warehouse id of warehouse
+ * @return array
+ */
+ function getPmpStockFromDate($date, $fk_warehouse)
+ {
+ $res = $this->product->load_stock();
+
+ if($res>0)
+ {
+ $stock = isset($this->product->stock_warehouse[$fk_warehouse]->real) ? $this->product->stock_warehouse[$fk_warehouse]->real : 0;
+ $pmp = $this->product->pmp;
+ }
+
+ //All Stock mouvement between now and inventory date
+ $sql = 'SELECT value, price';
+ $sql.= ' FROM '.MAIN_DB_PREFIX.'stock_mouvement';
+ $sql.= ' WHERE fk_product = '.$this->product->id;
+ $sql.= ' AND fk_entrepot = '.$fk_warehouse;
+ $sql.= ' AND datem > \''.date('Y-m-d 23:59:59', strtotime($date)).'\'';
+ $sql.= ' ORDER BY datem DESC';
+
+ $res = $this->db->query($sql);
+
+ $laststock = $stock;
+ $lastpmp = $pmp;
+
+ if($res)
+ {
+ while($mouvement = $this->db->fetch_object($res))
+ {
+ $price = ($mouvement->price > 0 && $mouvement->value > 0) ? $mouvement->price : $lastpmp;
+ $stock_value = $laststock * $lastpmp;
+ $laststock -= $mouvement->value;
+ $last_stock_value = $stock_value - ($mouvement->value * $price);
+ $lastpmp = ($laststock != 0) ? $last_stock_value / $laststock : $lastpmp;
+ }
+ }
+
+ return array($lastpmp, $laststock);
+ }
+
+ /**
+ * Fetch the product linked with the line
+ * @return void
+ */
+ function load_product()
+ {
+ global $db;
+
+ if($this->fk_product>0)
+ {
+ $this->product = new Product($db);
+ $this->product->fetch($this->fk_product);
+ }
+ }
+}
diff --git a/htdocs/product/inventory/img/bt-save.png b/htdocs/product/inventory/img/bt-save.png
new file mode 100644
index 00000000000..f20d292ca91
Binary files /dev/null and b/htdocs/product/inventory/img/bt-save.png differ
diff --git a/htdocs/product/inventory/img/inventory.png b/htdocs/product/inventory/img/inventory.png
new file mode 100644
index 00000000000..e54ad0eef7d
Binary files /dev/null and b/htdocs/product/inventory/img/inventory.png differ
diff --git a/htdocs/product/inventory/img/plus.png b/htdocs/product/inventory/img/plus.png
new file mode 100644
index 00000000000..b7072a96960
Binary files /dev/null and b/htdocs/product/inventory/img/plus.png differ
diff --git a/htdocs/product/inventory/img/plus16.png b/htdocs/product/inventory/img/plus16.png
new file mode 100644
index 00000000000..ad17ac89391
Binary files /dev/null and b/htdocs/product/inventory/img/plus16.png differ
diff --git a/htdocs/product/inventory/lib/inventory.lib.php b/htdocs/product/inventory/lib/inventory.lib.php
new file mode 100644
index 00000000000..aa8c9c9677d
--- /dev/null
+++ b/htdocs/product/inventory/lib/inventory.lib.php
@@ -0,0 +1,104 @@
+
+ * Copyright (C) 2015 ATM Consulting
+ *
+ * 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 lib/inventory.lib.php
+ * \ingroup inventory
+ * \brief This file is an example module library
+ */
+
+/**
+ * Define head array for tabs of inventory tools setup pages
+ *
+ * @return Array of head
+ */
+function inventoryAdminPrepareHead()
+{
+ global $langs, $conf;
+
+ $langs->load("inventory");
+
+ $h = 0;
+ $head = array();
+
+ $head[$h][0] = DOL_URL_ROOT."/admin/inventory.php";
+ $head[$h][1] = $langs->trans("Parameters");
+ $head[$h][2] = 'settings';
+ $h++;
+
+
+ // Show more tabs from modules
+ // Entries must be declared in modules descriptor with line
+ //$this->tabs = array(
+ // 'entity:+tabname:Title:@inventory:/inventory/mypage.php?id=__ID__'
+ //); // to add new tab
+ //$this->tabs = array(
+ // 'entity:-tabname:Title:@inventory:/inventory/mypage.php?id=__ID__'
+ //); // to remove a tab
+ complete_head_from_modules($conf, $langs, $object, $head, $h, 'inventory');
+
+ return $head;
+}
+
+function inventoryPrepareHead(&$inventory, $title='Inventory', $get='')
+{
+ global $langs;
+
+ return array(
+ array(dol_buildpath('/product/inventory/card.php?id='.$inventory->id.$get, 1), $langs->trans($title),'inventory')
+ );
+}
+
+
+
+function inventorySelectProducts(&$inventory)
+{
+ global $conf,$db,$langs;
+
+ $except_product_id = array();
+
+ foreach ($inventory->Inventorydet as $Inventorydet)
+ {
+ $except_product_id[] = $Inventorydet->fk_product;
+ }
+
+ ob_start();
+ $form = new Form($db);
+ $form->select_produits(-1, 'fk_product');
+
+ $TChildWarehouses = array($inventory->fk_warehouse);
+ $e = new Entrepot($db);
+ $e->fetch($inventory->fk_warehouse);
+ if(method_exists($e, 'get_children_warehouses')) $e->get_children_warehouses($e->id, $TChildWarehouses);
+
+ $Tab = array();
+ $sql = 'SELECT rowid, label
+ FROM '.MAIN_DB_PREFIX.'entrepot WHERE rowid IN('.implode(', ', $TChildWarehouses).')';
+ if(method_exists($e, 'get_children_warehouses')) $sql.= ' ORDER BY fk_parent';
+ $resql = $db->query($sql);
+ while($res = $db->fetch_object($resql)) {
+ $Tab[$res->rowid] = $res->label;
+ }
+ print ' ';
+ print $langs->trans('Warehouse').' : '.$form::selectarray('fk_warehouse', $Tab);
+
+ $select_html = ob_get_clean();
+
+ return $select_html;
+}
+
diff --git a/htdocs/product/inventory/list.php b/htdocs/product/inventory/list.php
new file mode 100644
index 00000000000..57545b07948
--- /dev/null
+++ b/htdocs/product/inventory/list.php
@@ -0,0 +1,134 @@
+
+ *
+ * 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/inventory/list.php
+ * \ingroup product
+ * \brief File of class to manage inventory
+ */
+
+require_once '../../main.inc.php';
+
+require_once DOL_DOCUMENT_ROOT.'/product/inventory/listview.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
+include_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
+require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
+require_once DOL_DOCUMENT_ROOT.'/product/inventory/class/inventory.class.php';
+require_once DOL_DOCUMENT_ROOT.'/product/inventory/lib/inventory.lib.php';
+
+$langs->load("stock");
+$langs->load("inventory");
+
+$limit = GETPOST("limit")?GETPOST("limit","int"):$conf->liste_limit;
+$sortfield = GETPOST("sortfield",'alpha');
+$sortorder = GETPOST("sortorder",'alpha');
+$page = (GETPOST("page",'int')?GETPOST("page", 'int'):0);
+if ($page == -1) { $page = 0; }
+$offset = $limit * $page;
+$pageprev = $page - 1;
+$pagenext = $page + 1;
+if (! $sortfield) $sortfield="i.title";
+if (! $sortorder) $sortorder="ASC";
+
+if (empty($user->rights->stock->lire)) accessforbidden();
+
+
+/*
+ * Actions
+ */
+
+// None
+
+
+/*
+ * View
+ */
+
+llxHeader('',$langs->trans('inventoryListTitle'),'','');
+
+echo '';
+
+llxFooter('');
+$db->close();
\ No newline at end of file
diff --git a/htdocs/product/inventory/listview.class.php b/htdocs/product/inventory/listview.class.php
new file mode 100644
index 00000000000..51855629686
--- /dev/null
+++ b/htdocs/product/inventory/listview.class.php
@@ -0,0 +1,1032 @@
+
+
+ This program and all files within this directory and sub directory
+ 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 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 .
+ */
+
+/**
+ * Class to manage the lists view
+ */
+class Listview
+{
+ /**
+ * Constructor
+ *
+ * @param DoliDB $db Database handler
+ * @param string $id html id
+ */
+ function __construct(&$db, $id)
+ {
+ $this->db = &$db;
+ $this->id = $id;
+ $this->TTotalTmp=array();
+ $this->sql = '';
+ $this->form = null;
+ $this->totalRowToShow=0;
+ $this->totalRow=0;
+ }
+
+ /**
+ * Function to init fields
+ *
+ * @param array $TParam array of configuration of list
+ * @return bool
+ */
+ private function init(&$TParam)
+ {
+ global $conf, $langs, $user;
+
+ if(!isset($TParam['hide'])) $TParam['hide']=array();
+ if(!isset($TParam['link'])) $TParam['link']=array();
+ if(!isset($TParam['type'])) $TParam['type']=array();
+ if(!isset($TParam['orderby']['noOrder'])) $TParam['orderby']['noOrder']=array();
+ if(!isset($TParam['allow-fields-select'])) $TParam['allow-fields-select'] = 0;
+
+ if(!isset($TParam['list']))$TParam['list']=array();
+ $TParam['list'] = array_merge(array(
+ 'messageNothing'=>$langs->trans('ListMessageNothingToShow')
+ ,'noheader'=>0
+ ,'useBottomPagination'=>0
+ ,'image'=>''
+ ,'title'=>$langs->trans('List')
+ ,'orderDown'=>''
+ ,'orderUp'=>''
+ ,'id'=>$this->id
+ ,'head_search'=>''
+ ,'export'=>array()
+ ,'view_type'=>''
+ ),$TParam['list']);
+
+ if (empty($TParam['limit'])) $TParam['limit'] = array();
+
+ $page = GETPOST('page');
+ if (!empty($page)) $TParam['limit']['page'] = $page+1; // TODO dolibarr start page at 0 instead 1
+
+ $TParam['limit'] = array_merge(array('page'=>1, 'nbLine' => $conf->liste_limit, 'global'=>0), $TParam['limit']);
+
+ if (GETPOST('sortfield'))
+ {
+ $TParam['sortfield'] = GETPOST('sortfield');
+ $TParam['sortorder'] = GETPOST('sortorder');
+ }
+
+ include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
+ $this->form = new Form($this->db);
+ }
+
+
+ /**
+ * Function to know if we can search on null value
+ * @param string $key field name
+ * @param array $TParam array of configuration
+ * @return bool
+ */
+ private function getSearchNull($key, &$TParam)
+ {
+ return !empty($TParam['search'][$key]['allow_is_null']);
+ }
+
+ /**
+ * @param string $key field name
+ * @param string $TParam array of configuration
+ * @return array
+ */
+ private function getSearchKey($key, &$TParam)
+ {
+ $TPrefixe = array();
+ if(!empty($TParam['search'][$key]['table']))
+ {
+ if (!is_array($TParam['search'][$key]['table'])) $TParam['search'][$key]['table'] = array($TParam['search'][$key]['table']);
+
+ foreach ($TParam['search'][$key]['table'] as $prefix_table)
+ {
+ $TPrefixe[] = $prefix_table.'.';
+ }
+ }
+
+ $TKey=array();
+ if(!empty($TParam['search'][$key]['field']))
+ {
+ if (!is_array($TParam['search'][$key]['field'])) $TParam['search'][$key]['field'] = array($TParam['search'][$key]['field']);
+
+ foreach ($TParam['search'][$key]['field'] as $i => $field)
+ {
+ $prefixe = !empty($TPrefixe[$i]) ? $TPrefixe[$i] : $TPrefixe[0];
+ $TKey[] = $prefixe. $field ;
+ }
+ }
+ else
+ {
+ $TKey[] = $TPrefixe[0].$key;
+ }
+
+ return $TKey;
+ }
+
+ /**
+ * @param timestamp $date date to convert
+ * @return int|string Date TMS or ''
+ */
+ private function dateToSQLDate($date)
+ {
+ return $this->db->idate($date);
+ }
+
+
+ /**
+ * @param string $TSQLMore contain some additional sql instructions
+ * @param string $value date with read format
+ * @param string $sKey field name
+ */
+ private function addSqlFromTypeDate(&$TSQLMore, &$value, $sKey)
+ {
+ if(is_array($value))
+ {
+ $TSQLDate=array();
+ if(!empty($value['start']))
+ {
+// $valueDeb = $this->dateToSQLDate($value['start'].' 00:00:00');
+ $TSQLDate[]=$sKey." >= '".$value['start']."'" ;
+ }
+
+ if(!empty($value['end']))
+ {
+// $valueFin = $this->dateToSQLDate($value['end'].' 23:59:59');
+ $TSQLDate[]=$sKey." <= '".$value['end']."'" ;
+ }
+
+ if(!empty($TSQLDate)) $TSQLMore[] = implode(' AND ', $TSQLDate);
+ }
+ else
+ {
+// $value = $this->dateToSQLDate($value);
+ $TSQLMore[]=$sKey." LIKE '".$value."%'" ;
+ }
+ }
+
+
+ /**
+ * @param string $TSQLMore contain some additional sql instructions
+ * @param string $value value to filter
+ * @param string $TParam array of configuration
+ * @param string $sKey field name
+ * @param string $key reference of sKey to find value into TParam
+ * @return bool
+ */
+ private function addSqlFromOther(&$TSQLMore, &$value, &$TParam, $sKey, $key)
+ {
+ // Do not use empty() function, statut 0 exist
+ if ($value == '') return false;
+ elseif($value==-1) return false;
+
+ if(isset($TParam['operator'][$key]))
+ {
+ if($TParam['operator'][$key] == '<' || $TParam['operator'][$key] == '>' || $TParam['operator'][$key]=='=')
+ {
+ $TSQLMore[] = $sKey . ' ' . $TParam['operator'][$key] . ' "' . $value . '"';
+ }
+ elseif ($TParam['operator'][$key]=='IN')
+ {
+ $TSQLMore[] = $sKey . ' ' . $TParam['operator'][$key] . ' (' . $value . ')';
+ }
+ else
+ {
+ if(strpos($value,'%')===false) $value = '%'.$value.'%';
+ $TSQLMore[]=$sKey." LIKE '".addslashes($value)."'" ;
+ }
+ }
+ else
+ {
+ if(strpos($value,'%')===false) $value = '%'.$value.'%';
+ $TSQLMore[]=$sKey." LIKE '".addslashes($value)."'" ;
+ }
+
+ return true;
+ }
+
+
+ /**
+ * @param string $sql standard select sql
+ * @param string $TParam array of configuration
+ * @return string
+ */
+ private function search($sql, &$TParam)
+ {
+ $ListPOST = GETPOST('Listview');
+
+ if (!GETPOST("button_removefilter_x") && !GETPOST("button_removefilter.x") && !GETPOST("button_removefilter"))
+ {
+ foreach ($TParam['search'] as $field => $info)
+ {
+ $TsKey = $this->getSearchKey($field, $TParam);
+ $TSQLMore = array();
+ $allow_is_null = $this->getSearchNull($field,$TParam);
+
+ foreach ($TsKey as $i => &$sKey)
+ {
+ $value = '';
+ if (isset($ListPOST[$this->id]['search'][$field])) $value = $ListPOST[$this->id]['search'][$field];
+
+ if ($allow_is_null && !empty($ListPOST[$this->id]['search_on_null'][$field]))
+ {
+ $TSQLMore[] = $sKey.' IS NULL ';
+ $value = '';
+ }
+
+ if (isset($TParam['type'][$field]) && ($TParam['type'][$field]==='date' || $TParam['type'][$field]==='datetime'))
+ {
+ $k = 'Listview_'.$this->id.'_search_'.$field;
+ if ($info['search_type'] === 'calendars')
+ {
+ $value = array();
+
+ $timestart = dol_mktime(0, 0, 0, GETPOST($k.'_startmonth'), GETPOST($k.'_startday'), GETPOST($k.'_startyear'));
+ if ($timestart) $value['start'] = date('Y-m-d', $timestart);
+
+ $timeend = dol_mktime(23, 59, 59, GETPOST($k.'_endmonth'), GETPOST($k.'_endday'), GETPOST($k.'_endyear'));
+ if ($timeend) $value['end'] = date('Y-m-d', $timeend);
+ }
+ else
+ {
+ $time = dol_mktime(12, 0, 0, GETPOST($k.'month'), GETPOST($k.'day'), GETPOST($k.'year'));
+ if ($time) $value = date('Y-m-d', $time);
+ }
+
+ if (!empty($value)) $this->addSqlFromTypeDate($TSQLMore, $value, $sKey);
+ }
+ else
+ {
+ $this->addSqlFromOther($TSQLMore, $value, $TParam, $sKey, $field);
+ }
+ }
+
+ if (!empty($TSQLMore))
+ {
+ $sql.=' AND ( '.implode(' OR ',$TSQLMore).' ) ';
+ }
+ }
+ }
+
+ if ($sqlGROUPBY!='') $sql.=' GROUP BY '.$sqlGROUPBY;
+
+ return $sql;
+ }
+
+ /**
+ * @param string $sql standard select sql
+ * @param string $TParam array of configuration
+ * @return string
+ */
+ public function render($sql, $TParam=array())
+ {
+ global $conf;
+
+ $TField=array();
+
+ $this->init($TParam);
+
+ $THeader = $this->initHeader($TParam);
+
+ $sql = $this->search($sql,$TParam);
+ $sql.= $this->db->order($TParam['param']['sortfield'], $TParam['param']['sortorder']);
+
+ $nbtotalofrecords = '';
+ if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
+ {
+ $result = $this->db->query($sql);
+ $nbtotalofrecords = $this->db->num_rows($result);
+ }
+ $sql.= $this->db->plimit($TParam['param']['limit'] + 1, $TParam['param']['offset']);
+
+ $this->parse_sql($THeader, $TField, $TParam, $sql);
+
+ list($TTotal, $TTotalGroup)=$this->get_total($TField, $TParam);
+
+ return $this->renderList($THeader, $TField, $TTotal, $TTotalGroup, $TParam);
+ }
+
+ /**
+ * @param string $THeader the configuration of header
+ * @param string $TParam array of configuration
+ * @return array
+ */
+ private function setSearch(&$THeader, &$TParam)
+ {
+ global $langs, $form;
+
+ if(empty($TParam['search'])) return array();
+
+ $TSearch=array();
+
+ $nb_search_in_bar = 0;
+
+ if(!empty($TParam['search']))
+ {
+ foreach($THeader as $key => $libelle)
+ {
+ if(empty($TSearch[$key]))$TSearch[$key]='';
+ }
+ }
+
+ $ListPOST = GETPOST('Listview');
+ $removeFilter = (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETPOST("button_removefilter"));
+ foreach($TParam['search'] as $key => $param_search)
+ {
+ $value = isset($ListPOST[$this->id]['search'][$key]) ? $ListPOST[$this->id]['search'][$key] : '';
+ if ($removeFilter) $value = '';
+
+ $typeRecherche = (is_array($param_search) && isset($param_search['search_type'])) ? $param_search['search_type'] : $param_search;
+
+ if(is_array($typeRecherche))
+ {
+ $fsearch=$form->selectarray('Listview['.$this->id.'][search]['.$key.']', $typeRecherche,$value,1);
+ }
+ else if($typeRecherche==='calendar')
+ {
+ if (!$removeFilter) $value = GETPOST('Listview_'.$this->id.'_search_'.$key) ? mktime(0,0,0, (int) GETPOST('Listview_'.$this->id.'_search_'.$key.'month'), (int) GETPOST('Listview_'.$this->id.'_search_'.$key.'day'), (int) GETPOST('Listview_'.$this->id.'_search_'.$key.'year') ) : '';
+
+ $fsearch = $form->select_date($value, 'Listview_'.$this->id.'_search_'.$key,0, 0, 1, "", 1, 0, 1);
+ }
+ else if($typeRecherche==='calendars')
+ {
+ $value_start = $value_end = '';
+ if (!$removeFilter)
+ {
+ $value_start = GETPOST('Listview_'.$this->id.'_search_'.$key.'_start') ? mktime(0,0,0, (int) GETPOST('Listview_'.$this->id.'_search_'.$key.'_startmonth'), (int) GETPOST('Listview_'.$this->id.'_search_'.$key.'_startday'), (int) GETPOST('Listview_'.$this->id.'_search_'.$key.'_startyear') ) : '';
+ $value_end = GETPOST('Listview_'.$this->id.'_search_'.$key.'_end') ? mktime(0,0,0, (int) GETPOST('Listview_'.$this->id.'_search_'.$key.'_endmonth'), (int) GETPOST('Listview_'.$this->id.'_search_'.$key.'_endday'), (int) GETPOST('Listview_'.$this->id.'_search_'.$key.'_endyear') ) : '';
+ }
+
+ $fsearch = $form->select_date($value_start, 'Listview_'.$this->id.'_search_'.$key.'_start',0, 0, 1, "", 1, 0, 1)
+ . $form->select_date($value_end, 'Listview_'.$this->id.'_search_'.$key.'_end',0, 0, 1, "", 1, 0, 1);
+
+ }
+ else if(is_string($typeRecherche))
+ {
+ $fsearch=$TParam['search'][$key];
+ }
+ else
+ {
+ $fsearch=' ';
+ }
+
+ if(!empty($param_search['allow_is_null']))
+ {
+ $valueNull = isset($ListPOST[$this->id]['search_on_null'][$key]) ? 1 : 0;
+ $fsearch.=' '.$form->checkbox1('', 'Listview['.$this->id.'][search_on_null]['.$key.']',1, $valueNull,' onclick=" if($(this).is(\':checked\')){ $(this).prev().val(\'\'); }" ').img_help(1, $langs->trans('SearchOnNUllValue'));
+ }
+
+ if(!empty($THeader[$key]))
+ {
+ $TSearch[$key] = $fsearch;
+ $nb_search_in_bar++;
+ }
+ else
+ {
+ $label = !empty($TParam['title'][$key]) ? $TParam['title'][$key] : $key ;
+ $TParam['list']['head_search'].= ''.$label.' ';
+// $TParam['list']['head_search'].=''.$label.' '.$fsearch.'
';
+ }
+ }
+
+ $search_button = ' '.img_search().' ';
+
+ if(!empty($TParam['list']['head_search']))
+ {
+ $TParam['list']['head_search']=''.$search_button.'
'.$TParam['list']['head_search'];
+ }
+
+ if($nb_search_in_bar>0)
+ {
+ end($TSearch);
+ list($key,$v) = each($TSearch);
+ $TSearch[$key].=$search_button;
+ }
+ else
+ {
+ $TSearch=array();
+ }
+
+ return $TSearch;
+ }
+
+ /**
+ * Function to analyse and calculate the total from a column
+ *
+ * @param string $TField TField
+ * @param string $TParam TParam
+ * @return array
+ */
+ private function get_total(&$TField, &$TParam)
+ {
+ $TTotal=$TTotalGroup=array();
+
+ if(!empty($TParam['math']) && !empty($TField[0]))
+ {
+ foreach($TField[0] as $field=>$value)
+ {
+ $TTotal[$field]='';
+ $TTotalGroup[$field] = '';
+ }
+
+ foreach($TParam['math'] as $field=>$typeMath)
+ {
+ if(is_array($typeMath))
+ {
+ $targetField = $typeMath[1];
+ $typeMath = $typeMath[0];
+ }
+ else
+ {
+ $targetField = $field;
+ }
+
+ if($typeMath == 'groupsum')
+ {
+ $TTotalGroup[$field] = array('target'=>$targetField, 'values'=> $this->TTotalTmp['@groupsum'][$targetField]);
+ }
+ else if($typeMath=='average')
+ {
+ $TTotal[$field]=array_sum($this->TTotalTmp[$targetField]) / count($this->TTotalTmp[$targetField]);
+ }
+ elseif($typeMath=='count')
+ {
+ $TTotal[$field]=count($this->TTotalTmp[$targetField]);
+ }
+ else
+ {
+ $TTotal[$field]=array_sum($this->TTotalTmp[$targetField]);
+ }
+ }
+ }
+
+ return array($TTotal,$TTotalGroup);
+ }
+
+ /**
+ * @return string
+ */
+ /*
+ private function getJS()
+ {
+ $javaScript = '';
+
+ return $javaScript;
+ }
+ */
+
+ /**
+ * @param string $TParam TParam
+ * @param string $TField TField
+ * @param string $THeader THeader
+ * @return array
+ */
+ private function setExport(&$TParam, $TField, $THeader)
+ {
+ global $langs;
+
+ $Tab=array();
+ if(!empty($TParam['export']))
+ {
+ $token = GETPOST('token');
+ if(empty($token)) $token = md5($this->id.time().rand(1,9999));
+
+ $_SESSION['token_list_'.$token] = gzdeflate( serialize( array(
+ 'title'=>$this->title,
+ 'sql'=>$this->sql,
+ 'TBind'=>$this->TBind,
+ 'TChamps'=>$TField,
+ 'TEntete'=>$THeader
+ )));
+
+ foreach($TParam['export'] as $mode_export)
+ {
+ $Tab[] = array(
+ 'label'=>$langs->trans('Export'.$mode_export),
+ 'url'=>dol_buildpath('/abricot/downlist.php',1),
+ 'mode'=>$mode_export,
+ 'token'=>$token,
+ 'session_name'=>session_name()
+ );
+ }
+
+ }
+
+ return $Tab;
+ }
+
+ /**
+ * @param string $TField TField
+ * @param string $TTotalGroup TTotalGroup
+ * @return array
+ */
+ private function addTotalGroup($TField, $TTotalGroup)
+ {
+ global $langs;
+
+ $Tab=array();
+ $proto_total_line = array();
+ $tagbase = $old_tagbase = null;
+ $addGroupLine = false;
+
+ foreach($TField as $k=>&$line)
+ {
+ if(empty($proto_total_line))
+ {
+ foreach($line as $field=>$value)
+ {
+ $proto_total_line[$field] = '';
+ }
+ $group_line = $proto_total_line;
+ }
+
+ $addGroupLine = false;
+
+ $tagbase = '';
+ foreach($line as $field=>$value)
+ {
+ if(!empty($TTotalGroup[$field]))
+ {
+ $tagbase.=$value.'|';
+ $group_line[$field] = ''.(empty($value) ? $langs->trans('Empty') : $value ).' :
';
+ $group_line[$TTotalGroup[$field]['target']] = ''.price($TTotalGroup[$field]['values'][$value]).'
';
+ $addGroupLine = true;
+ }
+ }
+
+ if(!is_null($old_tagbase) && $old_tagbase!=$tagbase && $addGroupLine)
+ {
+ $Tab[] = $previous_group_line;
+ }
+
+ $old_tagbase = $tagbase;
+ $previous_group_line = $group_line;
+ $group_line = $proto_total_line;
+
+ $Tab[] = $line;
+ }
+
+ if($addGroupLine)
+ {
+ $Tab[] = $previous_group_line;
+ }
+
+ return $Tab;
+ }
+
+ /**
+ * @param string $THeader THeader
+ * @param string $TField TField
+ * @param string $TTotal TTotal
+ * @param string $TTotalGroup TTotalGroup
+ * @param string $TParam TParam
+ * @return string
+ */
+ private function renderList(&$THeader, &$TField, &$TTotal, &$TTotalGroup, &$TParam)
+ {
+ global $bc;
+
+ $TSearch = $this->setSearch($THeader, $TParam);
+ $TExport = $this->setExport($TParam, $TField, $THeader);
+ $TField = $this->addTotalGroup($TField,$TTotalGroup);
+
+ //$out = $this->getJS();
+
+ $dolibarr_decalage = $this->totalRow > $this->totalRowToShow ? 1 : 0;
+ ob_start();
+ print_barre_liste($TParam['list']['title'], $TParam['limit']['page']-1, $_SERVER["PHP_SELF"], '&'.$TParam['list']['param_url'], $TParam['sortfield'], $TParam['sortorder'], '', $this->totalRowToShow+$dolibarr_decalage, $this->totalRow, $TParam['list']['image'], 0, '', '', $TParam['limit']['nbLine']);
+ $out .= ob_get_clean();
+
+
+ $out.= '';
+
+ if(count($TSearch)>0)
+ {
+ $out.='';
+
+ foreach ($THeader as $field => $head)
+ {
+ if ($field === 'selectedfields')
+ {
+ $out.= ''.$this->form->showFilterAndCheckAddButtons(0).' ';
+ }
+ else
+ {
+ $moreattrib = 'style="width:'.$head['width'].';text-align:'.$head['text-align'].'"';
+ $out .= ''.$TSearch[$field].' ';
+ }
+ }
+
+ $out.=' ';
+ }
+
+ $out.= '';
+ foreach($THeader as $field => $head)
+ {
+ $moreattrib = '';
+ $search = '';
+ $prefix = '';
+
+ if ($field === 'selectedfields')
+ {
+ $moreattrib = 'align="right" ';
+ $prefix = 'maxwidthsearch ';
+ }
+
+ if (empty($head['width'])) $head['width'] = 'auto';
+ if (!empty($head['width']) && !empty($head['text-align'])) $moreattrib .= 'style="width:'.$head['width'].';text-align:'.$head['text-align'].'"';
+
+ if (isset($TParam['search'][$field]['search_type']) && $TParam['search'][$field]['search_type'] !== false)
+ {
+ $TsKey = $this->getSearchKey($field, $TParam);
+ if (!empty($TsKey)) $search = implode(',', $TsKey);
+ else $search = $field;
+ }
+
+ $out .= getTitleFieldOfList($head['label'], 0, $_SERVER["PHP_SELF"], $search, '', $moreparam, $moreattrib, $TParam['sortfield'], $TParam['sortorder'], $prefix);
+ $out .= $head['more'];
+ }
+
+ //$out .= '-- ';
+ $out .= ' ';
+
+ $out.=' ';
+
+ if(empty($TField))
+ {
+ if (!empty($TParam['list']['messageNothing'])) $out .= ''.$TParam['list']['messageNothing'].' ';
+ }
+ else
+ {
+ $var=true;
+ $line_number = 0;
+ foreach($TField as $fields)
+ {
+ if($this->in_view($TParam, $line_number))
+ {
+ $var=!$var;
+ $out.=' ';
+
+ foreach ($THeader as $field => $head)
+ {
+ $moreattrib = 'style="width:'.$head['width'].';text-align:'.$head['text-align'].'"';
+ $out.=''.$fields[$field].' ';
+ }
+
+ $out.=' ';
+ }
+
+ $line_number++;
+ }
+
+ $out.=' ';
+
+ if (!empty($TParam['list']['haveTotal']))
+ {
+ $out.='';
+
+ foreach ($THeader as $field => $head)
+ {
+ if (isset($TTotal[$field]))
+ {
+ $moreattrib = 'style="width:'.$head['width'].';text-align:'.$head['text-align'].'"';
+ $out.=''.price($TTotal[$field]).' ';
+ }
+ }
+
+ $out.=' ';
+ }
+ }
+
+ $out .= '
';
+
+ return $out;
+ }
+
+ /**
+ * @param string $db Db
+ * @param string $TField TField
+ * @param string $TParam TParam
+ */
+ public function renderArray(&$db, $TField, $TParam=array())
+ {
+ $this->typeRender = 'array';
+
+ $TField=array();
+
+ $this->init($TParam);
+ $THeader = $this->initHeader($TParam);
+
+ $this->parse_array($THeader, $TField, $TParam);
+ list($TTotal, $TTotalGroup)=$this->get_total($TField, $TParam);
+
+ $this->renderList($THeader, $TField, $TTotal, $TTotalGroup, $TParam);
+ }
+
+
+ /**
+ * @param string $THeader THeader
+ * @param string $TField TField
+ * @param string $TParam TParam
+ * @return bool
+ */
+ private function parse_array(&$THeader, &$TField, &$TParam)
+ {
+ $this->totalRow = count($TField);
+
+ $this->THideFlip = array_flip($TParam['hide']);
+ $this->TTotalTmp=array();
+
+ if (empty($TField)) return false;
+
+ foreach($TField as $row)
+ {
+ $this->set_line($TField, $TParam, $row);
+ }
+ }
+
+
+ private function initHeader(&$TParam)
+ {
+ global $user,$conf;
+
+ $THeader = array();
+
+ $TField=$TFieldVisibility=array();
+ foreach ($TParam['title'] as $field => $value)
+ {
+ $TField[$field]=true;
+ }
+
+ $contextpage=md5($_SERVER['PHP_SELF']);
+ if(!empty($TParam['allow-field-select']))
+ {
+ $selectedfields = GETPOST('Listview'.$this->id.'_selectedfields');
+
+ if(!empty($selectedfields))
+ {
+ include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
+ $tabparam['MAIN_SELECTEDFIELDS_'.$contextpage] = $selectedfields;
+ $result=dol_set_user_param($this->db, $conf, $user, $tabparam);
+ }
+
+ $tmpvar='MAIN_SELECTEDFIELDS_'.$contextpage;
+ if (! empty($user->conf->{$tmpvar}))
+ {
+ $tmparray = explode(',', $user->conf->{$tmpvar});
+ $TParam['hide'] = array();
+ foreach($TField as $field => $dummy)
+ {
+ $label = $TParam['title'][$field];
+ if(!in_array($field, $tmparray))
+ {
+ $TParam['hide'][] = $field;
+ $visible = 0;
+ }
+ else
+ {
+ $visible = 1;
+ }
+
+ $TFieldVisibility[$field] = array(
+ 'label'=>$label
+ ,'checked'=>$visible
+ );
+ }
+ }
+ else
+ {
+ foreach($TField as $field=>$dummy)
+ {
+ $label = isset($TParam['title'][$field]) ? $TParam['title'][$field] : $field;
+ $visible = (!in_array($field, $TParam['hide'])) ? 1 : 0;
+ $TFieldVisibility[$field]=array(
+ 'label'=>$label,
+ 'checked'=>$visible
+ );
+ }
+ }
+
+ $selectedfields = $this->form->multiSelectArrayWithCheckbox('Listview'.$this->id.'_selectedfields', $TFieldVisibility, $contextpage); // This also change content of $arrayfields_0
+ }
+
+ foreach ($TParam['title'] as $field => $label)
+ {
+ $visible = (!in_array($field, $TParam['hide'])) ? 1 : 0;
+ if($visible)
+ {
+ $THeader[$field] = array(
+ 'label'=>$label,
+ 'order'=>(in_array($field, $TParam['orderby']['noOrder']) ? 0 : 1),
+ 'width'=>(!empty($TParam['size']['width'][$field]) ? $TParam['size']['width'][$field] : 'auto'),
+ 'text-align'=>(!empty($TParam['position']['text-align'][$field]) ? $TParam['position']['text-align'][$field] : 'auto'),
+ 'more'=>''
+ );
+ }
+ }
+
+ if(!empty($selectedfields))
+ {
+ $THeader['selectedfields']['label']=''.$selectedfields.'
';
+ }
+
+ return $THeader;
+ }
+
+ /**
+ * @param string $TParam TParam
+ * @param string $line_number aaa
+ * @return bool
+ */
+ private function in_view(&$TParam, $line_number)
+ {
+ global $conf;
+
+ if(!empty($_REQUEST['get-all-for-export'])) return true;
+
+ $page_number = !empty($TParam['limit']['page']) ? $TParam['limit']['page'] : 1;
+ $line_per_page = !empty($TParam['limit']['nbLine']) ? $TParam['limit']['nbLine'] : $conf->liste_limit;
+
+ $start = ($page_number-1) * $line_per_page;
+ $end = ($page_number* $line_per_page) -1;
+
+ if($line_number>=$start && $line_number<=$end) return true;
+ else return false;
+ }
+
+ /**
+ * @param string $TField TField
+ * @param string $TParam TParam
+ * @param string $currentLine aaa
+ */
+ private function set_line(&$TField, &$TParam, $currentLine)
+ {
+ global $conf;
+
+ $line_number = count($TField);
+
+ if($this->in_view($TParam,$line_number))
+ {
+ $this->totalRowToShow++;
+ $row=array(); $trans = array();
+ foreach($currentLine as $field=>$value)
+ {
+ if(is_object($value))
+ {
+ if(get_class($value)=='stdClass') {$value=print_r($value, true);}
+ else $value=(string) $value;
+ }
+
+ $trans['@'.$field.'@'] = $value;
+
+ if(!empty($TParam['math'][$field]))
+ {
+ $float_value = (double) strip_tags($value);
+ $this->TTotalTmp[$field][] = $float_value;
+ }
+
+ if(!in_array($field,$TParam['hide']))
+ {
+ $row[$field]=$value;
+
+ if(isset($TParam['eval'][$field]) && in_array($field,array_keys($row)))
+ {
+ $strToEval = 'return '.strtr( $TParam['eval'][$field], array_merge( $trans, array('@val@'=>$row[$field]) )).';';
+ $row[$field] = eval($strToEval);
+ }
+
+ if(isset($TParam['type'][$field]) && !isset($TParam['eval'][$field]))
+ {
+ if($TParam['type'][$field]=='date' || $TParam['type'][$field]=='datetime' )
+ {
+ if($row[$field] != '0000-00-00 00:00:00' && $row[$field] != '1000-01-01 00:00:00' && $row[$field] != '0000-00-00' && !empty($row[$field]))
+ {
+ if($TParam['type'][$field]=='datetime')$row[$field] = dol_print_date(strtotime($row[$field]),'dayhoursec');
+ else $row[$field] = dol_print_date(strtotime($row[$field]),'day');
+ }
+ else
+ {
+ $row[$field] = '';
+ }
+ }
+
+ if($TParam['type'][$field]=='hour') { $row[$field] = date('H:i', strtotime($row[$field])); }
+ if($TParam['type'][$field]=='money') { $row[$field] = ''.price($row[$field],0,'',1,-1,2).'
'; }
+ if($TParam['type'][$field]=='number') { $row[$field] = ''.price($row[$field]).'
'; }
+ if($TParam['type'][$field]=='integer') { $row[$field] = ''.((int) $row[$field]).'
'; }
+ }
+
+ if(isset($TParam['link'][$field]))
+ {
+ if(empty($row[$field]) && $row[$field]!==0 && $row[$field]!=='0')$row[$field]='(vide)';
+ $row[$field]= strtr( $TParam['link'][$field], array_merge( $trans, array('@val@'=>$row[$field]))) ;
+ }
+
+ if(isset($TParam['translate'][$field]))
+ {
+ if(isset($TParam['translate'][$field][''])) unset($TParam['translate'][$field]['']);
+
+ $row[$field] = strtr( $row[$field], $TParam['translate'][$field]);
+ }
+ }
+ }
+ }
+ else
+ {
+ $row = array();
+
+ foreach($currentLine as $field=>&$value)
+ {
+ if(!isset($this->THideFlip[$field]))
+ {
+ if(isset($TParam['math'][$field]) && !empty($TParam['math'][$field]))
+ {
+ $float_value = (double) strip_tags($value);
+ $this->TTotalTmp[$field][] = $float_value;
+ }
+
+ $row[$field] = $value;
+ }
+ }
+ }
+
+ if(!empty($TParam['math'][$field]))
+ {
+ foreach($row as $field=>$value)
+ {
+ if(!empty($TParam['math'][$field]) && is_array($TParam['math'][$field]))
+ {
+ $toField = $TParam['math'][$field][1];
+ $float_value = (double) strip_tags($row[$toField]);
+ $this->TTotalTmp['@groupsum'][$toField][ $row[$field] ] += $float_value;
+ }
+ }
+ }
+
+ $TField[] = $row;
+ }
+
+ /**
+ * @param string $sql sql
+ * @param string $TParam TParam
+ * @return string
+ */
+ private function limitSQL($sql, &$TParam)
+ {
+ if(!empty($TParam['limit']['global']) && strpos($sql,'LIMIT ')===false )
+ {
+ $sql.=' LIMIT '.(int) $TParam['limit']['global'];
+ }
+
+ return $sql;
+ }
+
+ /**
+ * @param string $THeader THeader
+ * @param string $TField TField
+ * @param string $TParam TParam
+ * @param string $sql sql
+ */
+ private function parse_sql(&$THeader, &$TField, &$TParam, $sql)
+ {
+ $this->sql = $this->limitSQL($sql, $TParam);
+
+ $this->TTotalTmp=array();
+ $this->THideFlip = array_flip($TParam['hide']);
+
+ $res = $this->db->query($this->sql);
+ if($res!==false)
+ {
+ $this->totalRow = $this->db->num_rows($res);
+ dol_syslog(get_class($this)."::parse_sql id=".$this->id." sql=".$this->sql, LOG_DEBUG);
+
+ while($currentLine = $this->db->fetch_object($res))
+ {
+ $this->set_line($TField, $TParam, $currentLine);
+ }
+ }
+ else
+ {
+ dol_syslog(get_class($this)."::parse_sql id=".$this->id." sql=".$this->sql, LOG_ERR);
+ }
+ }
+}
diff --git a/htdocs/product/inventory/tpl/inventory.tpl.php b/htdocs/product/inventory/tpl/inventory.tpl.php
new file mode 100644
index 00000000000..2a1c3bc3ee4
--- /dev/null
+++ b/htdocs/product/inventory/tpl/inventory.tpl.php
@@ -0,0 +1,205 @@
+
+
+status != 1) { ?>
+ trans('AddInventoryProduct'); ?> :
+
+
+
+
+Date de création : getDate('datec') ?>
+ Dernière mise à jour : getDate('tms') ?>
+
+
+
diff --git a/htdocs/societe/agenda.php b/htdocs/societe/agenda.php
index e1dc1c12671..32d13356654 100644
--- a/htdocs/societe/agenda.php
+++ b/htdocs/societe/agenda.php
@@ -83,7 +83,7 @@ if (empty($reshook))
}
// Purge search criteria
- if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETPOST("button_removefilter")) // All test are required to be compatible with all browsers
+ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETPOST("button_removefilter")) // All tests are required to be compatible with all browsers
{
$actioncode='';
$search_agenda_label='';
@@ -180,12 +180,6 @@ if ($socid > 0)
print load_fiche_titre($langs->trans("ActionsOnCompany"),'','');
- // List of todo actions
- //show_actions_todo($conf,$langs,$db,$object,null,0,$actioncode);
-
- // List of done actions
- //show_actions_done($conf,$langs,$db,$object,null,0,$actioncode);
-
// List of all actions
$filters=array();
$filters['search_agenda_label']=$search_agenda_label;
diff --git a/htdocs/societe/class/societe.class.php b/htdocs/societe/class/societe.class.php
index 74aef09e7a9..0581ed1960e 100644
--- a/htdocs/societe/class/societe.class.php
+++ b/htdocs/societe/class/societe.class.php
@@ -3326,18 +3326,6 @@ class Societe extends CommonObject
return "Error, mode/status not found";
}
- /**
- * Set commnunication level
- *
- * @param User $user User making change
- * @return int <0 if KO, >0 if OK
- * @deprecated Use update function instead
- */
- function set_commnucation_level($user)
- {
- return $this->update($this->id, $user);
- }
-
/**
* Set outstanding value
*
diff --git a/htdocs/societe/societecontact.php b/htdocs/societe/societecontact.php
index 2bda75867dc..b5ffc87517f 100644
--- a/htdocs/societe/societecontact.php
+++ b/htdocs/societe/societecontact.php
@@ -107,13 +107,14 @@ else if ($action == 'deletecontact' && $user->rights->societe->creer)
dol_print_error($db);
}
}
-
+/*
else if ($action == 'setaddress' && $user->rights->societe->creer)
{
$object->fetch($id);
$result=$object->setDeliveryAddress($_POST['fk_address']);
if ($result < 0) dol_print_error($db,$object->error);
-}
+}*/
+
/*
* View
diff --git a/htdocs/theme/eldy/img/save.png b/htdocs/theme/eldy/img/save.png
new file mode 100644
index 00000000000..eca2d92eccc
Binary files /dev/null and b/htdocs/theme/eldy/img/save.png differ
diff --git a/htdocs/theme/eldy/style.css.php b/htdocs/theme/eldy/style.css.php
index 5c7f1f446ae..d0fae6d89a8 100644
--- a/htdocs/theme/eldy/style.css.php
+++ b/htdocs/theme/eldy/style.css.php
@@ -253,7 +253,7 @@ a.tab { font-weight: bold !important; }
a:link, a:visited, a:hover, a:active { font-family: ; font-weight: normal; color: rgb(); text-decoration: none; }
a:hover { text-decoration: underline; color: rgb(); }
a.commonlink { color: rgb() !important; text-decoration: none; }
-
+th.liste_titre a div div:hover, th.liste_titre_sel a div div:hover { text-decoration: underline; }
input, input.flat, textarea, textarea.flat, form.flat select, select, select.flat, .dataTables_length label select {
background-color: #FFF;
}
@@ -1973,6 +1973,10 @@ div.tabBar {
width: auto;
background: rgb();
}
+div.tabBar div.titre {
+ padding-top: 10px;
+}
+
div.tabBarWithBottom {
padding-bottom: 18px;
border-bottom: 1px solid #aaa;
@@ -2490,6 +2494,10 @@ div.pagination li.pagination .active {
text-decoration: underline;
box-shadow: none;
}
+.paginationafterarrows .nohover {
+ box-shadow: none !important;
+}
+
div.pagination li.paginationafterarrows {
margin-left: 10px;
}
@@ -2994,6 +3002,9 @@ td.legendLabel { padding: 2px 2px 2px 0 !important; }
margin-bottom: 2px;
margin-top: 10px;
}
+.photowithborder {
+ border: 1px solid #f0f0f0;
+}
.photointooltip {
margin-top: 6px;
margin-bottom: 6px;
diff --git a/htdocs/theme/md/img/save.png b/htdocs/theme/md/img/save.png
new file mode 100644
index 00000000000..eca2d92eccc
Binary files /dev/null and b/htdocs/theme/md/img/save.png differ
diff --git a/htdocs/theme/md/style.css.php b/htdocs/theme/md/style.css.php
index 8cc95628561..48670ffb637 100644
--- a/htdocs/theme/md/style.css.php
+++ b/htdocs/theme/md/style.css.php
@@ -1992,6 +1992,9 @@ div.tabBar {
padding-bottom: 12px;
border-bottom: 1px solid #aaa;
}
+div.tabBar div.titre {
+ padding-top: 10px;
+}
div.tabBarWithBottom {
padding-bottom: 18px;
border-bottom: 1px solid #aaa;
@@ -2561,6 +2564,9 @@ div.pagination li.pagination .active {
text-decoration: underline;
box-shadow: none;
}
+.paginationafterarrows .nohover {
+ box-shadow: none !important;
+}
div.pagination li.paginationafterarrows {
margin-left: 10px;
}
@@ -3109,10 +3115,8 @@ td.legendLabel { padding: 2px 2px 2px 0 !important; }
margin-bottom: 2px;
margin-top: 2px;
}
-.photowithmargin {
-/* -webkit-box-shadow: 0px 0px 3px #777;
- -moz-box-shadow: 0px 0px 3px #777;
- box-shadow: 0px 0px 3px #777;*/
+.photowithborder {
+ border: 1px solid #f0f0f0;
}
.photointoolitp {
margin-top: 8px;
diff --git a/htdocs/user/class/user.class.php b/htdocs/user/class/user.class.php
index a3006d6ebcd..f23c8aca0b6 100644
--- a/htdocs/user/class/user.class.php
+++ b/htdocs/user/class/user.class.php
@@ -376,7 +376,7 @@ class User extends CommonObject
{
if (! empty($obj->page) && ! empty($obj->type) && ! empty($obj->param))
{
- $user->default_values[$obj->page][$obj->type][$obj->param]=$obj->value;
+ $this->default_values[$obj->page][$obj->type][$obj->param]=$obj->value;
}
}
$this->db->free($resql);
@@ -387,7 +387,7 @@ class User extends CommonObject
return -3;
}
}
-
+
return 1;
}
diff --git a/htdocs/user/param_ihm.php b/htdocs/user/param_ihm.php
index 473c06e7368..9ffeb05f280 100644
--- a/htdocs/user/param_ihm.php
+++ b/htdocs/user/param_ihm.php
@@ -181,7 +181,7 @@ if ($action == 'edit')
if ($action == 'edit')
{
- dol_fiche_head($head, 'guisetup', $title, 0, 'user');
+ dol_fiche_head($head, 'guisetup', $title, -1, 'user');
$linkback = '';
diff --git a/htdocs/websites/class/website.class.php b/htdocs/websites/class/website.class.php
index 51a61d90112..a77079c94a6 100644
--- a/htdocs/websites/class/website.class.php
+++ b/htdocs/websites/class/website.class.php
@@ -73,10 +73,6 @@ class Website extends CommonObject
* @var mixed
*/
public $date_creation;
- /**
- * @var mixed
- */
- public $date_modification;
/**
* @var mixed
*/
@@ -123,7 +119,6 @@ class Website extends CommonObject
$error = 0;
// Clean parameters
-
if (isset($this->entity)) {
$this->entity = trim($this->entity);
}
@@ -136,36 +131,30 @@ class Website extends CommonObject
if (isset($this->status)) {
$this->status = trim($this->status);
}
-
-
+ if (empty($this->date_creation)) $this->date_creation = dol_now();
// Check parameters
// Put here code to add control on parameters values
// Insert request
$sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . '(';
-
$sql.= 'entity,';
$sql.= 'ref,';
$sql.= 'description,';
$sql.= 'status,';
$sql.= 'fk_default_home,';
$sql.= 'virtualhost,';
- $sql.= 'date_creation,';
- $sql.= 'date_modification';
-
+ $sql.= 'fk_user_create';
+ $sql.= 'date_creation';
$sql .= ') VALUES (';
-
$sql .= ' '.(! isset($this->entity)?'NULL':$this->entity).',';
$sql .= ' '.(! isset($this->ref)?'NULL':"'".$this->db->escape($this->ref)."'").',';
$sql .= ' '.(! isset($this->description)?'NULL':"'".$this->db->escape($this->description)."'").',';
$sql .= ' '.(! isset($this->status)?'NULL':$this->status).',';
$sql .= ' '.(! isset($this->fk_default_home)?'NULL':$this->fk_default_home).',';
$sql .= ' '.(! isset($this->virtualhost)?'NULL':$this->virtualhost).',';
- $sql .= ' '.(! isset($this->date_creation) || dol_strlen($this->date_creation)==0?'NULL':"'".$this->db->idate($this->date_creation)."'").',';
- $sql .= ' '.(! isset($this->date_modification) || dol_strlen($this->date_modification)==0?'NULL':"'".$this->db->idate($this->date_modification)."'");
-
-
+ $sql .= ' '.(! isset($this->fk_user_create)?$user->id:$this->fk_user_create).',';
+ $sql .= ' '.(! isset($this->date_creation) || dol_strlen($this->date_creation)==0?'NULL':"'".$this->db->idate($this->date_creation)."'");
$sql .= ')';
$this->db->begin();
@@ -223,8 +212,9 @@ class Website extends CommonObject
$sql .= " t.status,";
$sql .= " t.fk_default_home,";
$sql .= " t.virtualhost,";
+ $sql .= " t.fk_user_create,";
+ $sql .= " t.fk_user_modif,";
$sql .= " t.date_creation,";
- $sql .= " t.date_modification,";
$sql .= " t.tms";
$sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
if (null !== $ref) {
@@ -247,11 +237,10 @@ class Website extends CommonObject
$this->status = $obj->status;
$this->fk_default_home = $obj->fk_default_home;
$this->virtualhost = $obj->virtualhost;
+ $this->fk_user_create = $obj->fk_user_create;
+ $this->fk_user_modif = $obj->fk_user_modif;
$this->date_creation = $this->db->jdate($obj->date_creation);
- $this->date_modification = $this->db->jdate($obj->date_modification);
$this->tms = $this->db->jdate($obj->tms);
-
-
}
$this->db->free($resql);
@@ -292,8 +281,9 @@ class Website extends CommonObject
$sql .= " t.status,";
$sql .= " t.fk_default_home,";
$sql .= " t.virtualhost,";
+ $sql .= " t.fk_user_create,";
+ $sql .= " t.fk_user_modif,";
$sql .= " t.date_creation,";
- $sql .= " t.date_modification,";
$sql .= " t.tms";
$sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element. ' as t';
@@ -331,8 +321,9 @@ class Website extends CommonObject
$line->status = $obj->status;
$line->fk_default_home = $obj->fk_default_home;
$line->virtualhost = $obj->virtualhost;
+ $this->fk_user_create = $obj->fk_user_create;
+ $this->fk_user_modif = $obj->fk_user_modif;
$line->date_creation = $this->db->jdate($obj->date_creation);
- $line->date_modification = $this->db->jdate($obj->date_modification);
$line->tms = $this->db->jdate($obj->tms);
$this->records[$line->id] = $line;
@@ -377,25 +368,20 @@ class Website extends CommonObject
$this->status = trim($this->status);
}
-
-
// Check parameters
// Put here code to add a control on parameters values
// Update request
$sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET';
-
$sql .= ' entity = '.(isset($this->entity)?$this->entity:"null").',';
$sql .= ' ref = '.(isset($this->ref)?"'".$this->db->escape($this->ref)."'":"null").',';
$sql .= ' description = '.(isset($this->description)?"'".$this->db->escape($this->description)."'":"null").',';
$sql .= ' status = '.(isset($this->status)?$this->status:"null").',';
$sql .= ' fk_default_home = '.(($this->fk_default_home > 0)?$this->fk_default_home:"null").',';
$sql .= ' virtualhost = '.(($this->virtualhost != '')?"'".$this->db->escape($this->virtualhost)."'":"null").',';
+ $sql .= ' fk_user_modif = '.(! isset($this->fk_user_modif) ? $user->id : $this->fk_user_modif).',';
$sql .= ' date_creation = '.(! isset($this->date_creation) || dol_strlen($this->date_creation) != 0 ? "'".$this->db->idate($this->date_creation)."'" : 'null').',';
- $sql .= ' date_modification = '.(! isset($this->date_modification) || dol_strlen($this->date_modification) != 0 ? "'".$this->db->idate($this->date_modification)."'" : 'null').',';
$sql .= ' tms = '.(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : "'".$this->db->idate(dol_now())."'");
-
-
$sql .= ' WHERE rowid=' . $this->id;
$this->db->begin();
@@ -631,6 +617,8 @@ class Website extends CommonObject
*/
public function initAsSpecimen()
{
+ global $user;
+
$this->id = 0;
$this->entity = 1;
@@ -639,8 +627,9 @@ class Website extends CommonObject
$this->status = '';
$this->fk_default_home = null;
$this->virtualhost = 'http://myvirtualhost';
+ $this->fk_user_create = $user->id;
+ $this->fk_user_modif = $user->id;
$this->date_creation = dol_now();
- $this->date_modification = dol_now();
$this->tms = dol_now();
diff --git a/htdocs/websites/class/websitepage.class.php b/htdocs/websites/class/websitepage.class.php
index 883b6495c53..c80d20fe44b 100644
--- a/htdocs/websites/class/websitepage.class.php
+++ b/htdocs/websites/class/websitepage.class.php
@@ -181,7 +181,7 @@ class WebsitePage extends CommonObject
/**
* Load object in memory from the database
*
- * @param int $id Id object
+ * @param int $id Id object. If this is 0, the default page of website_id will be used, if not defined, the first one. found
* @param string $website_id Web site id
* @param string $page Page name
*
@@ -205,13 +205,16 @@ class WebsitePage extends CommonObject
$sql .= " t.tms as date_modification";
$sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
+ //$sql .= ' WHERE entity IN ('.getEntity('website', 1).')'; // entity is on website level
+ $sql .= ' WHERE 1 = 1';
if (null !== $website_id) {
- $sql .= ' WHERE t.fk_website = ' . '\'' . $website_id . '\'';
- $sql .= ' AND t.pageurl = ' . '\'' . $page . '\'';
+ $sql .= " AND t.fk_website = '" . $this->db->escape($website_id) . "'";
+ if ($page) $sql .= " AND t.pageurl = '" . $this->db->escape($page) . "'";
} else {
- $sql .= ' WHERE t.rowid = ' . $id;
+ $sql .= ' AND t.rowid = ' . $id;
}
-
+ $sql .= $this->db->plimit(1);
+
$resql = $this->db->query($sql);
if ($resql) {
$numrows = $this->db->num_rows($resql);
diff --git a/htdocs/websites/index.php b/htdocs/websites/index.php
index b55f81f5b7f..b78a172e01f 100644
--- a/htdocs/websites/index.php
+++ b/htdocs/websites/index.php
@@ -438,16 +438,34 @@ if ($action == 'updatemeta')
// Update page
if ($action == 'updatecontent' || GETPOST('refreshsite') || GETPOST('refreshpage') || GETPOST('preview'))
{
- $db->begin();
$object->fetch(0, $website);
+ /*if (GETPOST('savevirtualhost') && $object->virtualhost != GETPOST('previewsite'))
+ {
+ $object->virtualhost = GETPOST('previewsite', 'alpha');
+ $object->update($user);
+ }*/
+
$objectpage->fk_website = $object->id;
- $res = $objectpage->fetch($pageid, $object->fk_website);
+ if ($pageid > 0)
+ {
+ $res = $objectpage->fetch($pageid);
+ }
+ else
+ {
+ $res = $objectpage->fetch($object->fk_default_home);
+ if (! $res > 0)
+ {
+ $res = $objectpage->fetch(0, $object->fk_website);
+ }
+ }
if ($res > 0)
{
if ($action == 'updatecontent')
{
+ $db->begin();
+
$objectpage->content = GETPOST('PAGE_CONTENT');
// Clean data. We remove all the head section.
@@ -632,7 +650,7 @@ if (count($object->records) > 0)
// List of websites
print '';
$out='';
- $out.='
';
+ $out.='';
if (empty($object->records)) $out.=' ';
// Loop on each sites
$i=0;
@@ -654,9 +672,9 @@ if (count($object->records) > 0)
if ($website)
{
- $realurl=$urlwithroot.'/public/websites/index.php?website='.$website;
+ $virtualurl='';
$dataroot=DOL_DATA_ROOT.'/websites/'.$website;
- if (! empty($object->virtualhost)) $realurl=$object->virtualhost;
+ if (! empty($object->virtualhost)) $virtualurl=$object->virtualhost;
}
if ($website && $action == 'preview')
@@ -680,20 +698,23 @@ if (count($object->records) > 0)
if ($action == 'preview')
{
print '';
- print ' ';
+ print ' ';
//print ' ';
$htmltext=$langs->trans("SetHereVirtualHost", $dataroot);
print $form->textwithpicto('', $htmltext);
print '
';
- $urlext=$realurl;
- $urlint=DOL_URL_ROOT.'/public/websites/index.php?website='.$website;
- print '';
- print $form->textwithpicto('', $langs->trans("PreviewSiteServedByWebServer", $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $urlext), 1, 'preview_ext');
- print ' ';
+ $urlext=$virtualurl;
+ $urlint=$urlwithroot.'/public/websites/index.php?website='.$website;
+ //if (! empty($object->virtualhost))
+ //{
+ print 'transnoentitiesnoconv("Site"), $langs->transnoentitiesnoconv("Site"), $dataroot, $urlext)).'">';
+ print $form->textwithpicto('', $langs->trans("PreviewSiteServedByWebServer", $langs->transnoentitiesnoconv("Site"), $langs->transnoentitiesnoconv("Site"), $dataroot, $urlext?$urlext:$langs->trans("VirtualHostUrlNotDefined")), 1, 'preview_ext');
+ print ' ';
+ //}
- print '';
- print $form->textwithpicto('', $langs->trans("PreviewSiteServedByDolibarr", $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $urlint), 1, 'preview');
+ print ' transnoentitiesnoconv("Site"), $langs->transnoentitiesnoconv("Site"), $urlint)).'">';
+ print $form->textwithpicto('', $langs->trans("PreviewSiteServedByDolibarr", $langs->transnoentitiesnoconv("Site"), $langs->transnoentitiesnoconv("Site"), $urlint, $dataroot), 1, 'preview');
print ' ';
}
@@ -803,13 +824,22 @@ if (count($object->records) > 0)
print $form->textwithpicto('', $htmltext);
print ' ';
- $urlext=$realurl.'/'.$pagealias.'.php';
- print '';
- print $form->textwithpicto('', $langs->trans("PreviewSiteServedByWebServer", $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $urlext), 1, 'preview_ext');
- print ' ';
+ if (! empty($object->virtualhost))
+ {
+ $urlext=$virtualurl.'/'.$pagealias.'.php';
+ print 'transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $dataroot, $urlext)).'">';
+ print $form->textwithpicto('', $langs->trans("PreviewSiteServedByWebServer", $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $dataroot, $urlext?$urlext:$langs->trans("VirtualHostUrlNotDefined")), 1, 'preview_ext');
+ print ' ';
+ }
+ else
+ {
+ print '';
+ print $form->textwithpicto('', $langs->trans("PreviewSiteServedByWebServer", $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $dataroot, $urlext?$urlext:$langs->trans("VirtualHostUrlNotDefined")), 1, 'preview_ext');
+ print ' ';
+ }
- print '';
- print $form->textwithpicto('', $langs->trans("PreviewSiteServedByDolibarr", $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $realpage), 1, 'preview');
+ print ' transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $realpage)).'">';
+ print $form->textwithpicto('', $langs->trans("PreviewSiteServedByDolibarr", $langs->transnoentitiesnoconv("Page"), $langs->transnoentitiesnoconv("Page"), $realpage, $dataroot), 1, 'preview');
print ' '; // View page in new Tab
//print ' ';
diff --git a/scripts/emailings/mailing-send.php b/scripts/emailings/mailing-send.php
index 27cc22448e6..6465e765cfe 100755
--- a/scripts/emailings/mailing-send.php
+++ b/scripts/emailings/mailing-send.php
@@ -41,11 +41,12 @@ if (! isset($argv[1]) || ! $argv[1]) {
exit(-1);
}
$id=$argv[1];
-if (! isset($argv[2]) || !empty($argv[2])) $login = $argv[2];
+if (isset($argv[2]) || !empty($argv[2])) $login = $argv[2];
else $login = '';
require_once ($path."../../htdocs/master.inc.php");
require_once (DOL_DOCUMENT_ROOT."/core/class/CMailFile.class.php");
+require_once (DOL_DOCUMENT_ROOT."/comm/mailing/class/mailing.class.php");
// Global variables
@@ -70,9 +71,8 @@ $user = new User($db);
// for signature, we use user send as parameter
if (! empty($login)) $user->fetch('',$login);
-// We get list of emailing to process
-$sql = "SELECT m.rowid, m.titre, m.sujet, m.body,";
-$sql.= " m.email_from, m.email_replyto, m.email_errorsto";
+// We get list of emailing id to process
+$sql = "SELECT m.rowid";
$sql.= " FROM ".MAIN_DB_PREFIX."mailing as m";
$sql.= " WHERE m.statut IN (1,2)";
if ($id != 'all')
@@ -96,12 +96,15 @@ if ($resql)
dol_syslog("Process mailing with id ".$obj->rowid);
print "Process mailing with id ".$obj->rowid."\n";
- $id = $obj->rowid;
- $subject = $obj->sujet;
- $message = $obj->body;
- $from = $obj->email_from;
- $replyto = $obj->email_replyto;
- $errorsto = $obj->email_errorsto;
+ $emailing = new Mailing($db);
+ $emailing->fetch($obj->rowid);
+
+ $id = $emailing->id;
+ $subject = $emailing->sujet;
+ $message = $emailing->body;
+ $from = $emailing->email_from;
+ $replyto = $emailing->email_replyto;
+ $errorsto = $emailing->email_errorsto;
// Le message est-il en html
$msgishtml=-1; // Unknown by default
if (preg_match('/[\s\t]*/i',$message)) $msgishtml=1;
@@ -232,6 +235,24 @@ if ($resql)
dol_syslog("ok for emailing id ".$id." #".$i.($mail->error?' - '.$mail->error:''), LOG_DEBUG);
+ // Note: If emailing is 100 000 targets, 100 000 entries are added, so we don't enter events for each target here
+ // We must union table llx_mailing_taget for event tab OR enter 1 event with a special table link (id of email in event)
+ // Run trigger
+ /*
+ if ($obj2->source_type == 'contact')
+ {
+ $emailing->sendtoid = $obj2->source_id;
+ }
+ if ($obj2->source_type == 'thirdparty')
+ {
+ $emailing->socid = $obj2->source_id;
+ }
+ // Call trigger
+ $result=$emailing->call_trigger('EMAILING_SENTBYMAIL',$user);
+ if ($result < 0) $error++;
+ // End call triggers
+ */
+
$sqlok ="UPDATE ".MAIN_DB_PREFIX."mailing_cibles";
$sqlok.=" SET statut=1, date_envoi='".$db->idate($now)."' WHERE rowid=".$obj2->rowid;
$resqlok=$db->query($sqlok);