Jelajahi Sumber

Merge remote-tracking branch 'upstream/develop' into develop

Juanjo Menent 9 tahun lalu
induk
melakukan
00496cd49e
56 mengubah file dengan 2299 tambahan dan 687 penghapusan
  1. 4 1
      .travis.yml
  2. 45 15
      htdocs/accountancy/bookkeeping/balance.php
  3. 1 1
      htdocs/accountancy/bookkeeping/list.php
  4. 348 0
      htdocs/accountancy/bookkeeping/listbyaccount.php
  5. 273 207
      htdocs/accountancy/class/bookkeeping.class.php
  6. 6 5
      htdocs/accountancy/tpl/export_journal.tpl.php
  7. 31 1
      htdocs/comm/card.php
  8. 517 150
      htdocs/comm/propal/class/propal.class.php
  9. 450 149
      htdocs/commande/class/commande.class.php
  10. 183 64
      htdocs/compta/facture/class/facture.class.php
  11. 1 1
      htdocs/contact/card.php
  12. 14 1
      htdocs/core/ajax/saveinplace.php
  13. 1 1
      htdocs/core/class/doleditor.class.php
  14. 1 2
      htdocs/core/class/genericobject.class.php
  15. 2 1
      htdocs/core/class/hookmanager.class.php
  16. 2 0
      htdocs/core/lib/company.lib.php
  17. 18 6
      htdocs/core/lib/date.lib.php
  18. 3 3
      htdocs/core/lib/functions.lib.php
  19. 5 0
      htdocs/core/lib/functions2.lib.php
  20. 21 9
      htdocs/core/modules/export/export_csv.modules.php
  21. 5 1
      htdocs/core/modules/modAgenda.class.php
  22. 3 1
      htdocs/core/modules/modCommande.class.php
  23. 9 1
      htdocs/core/modules/modDeplacement.class.php
  24. 3 1
      htdocs/core/modules/modExpedition.class.php
  25. 5 1
      htdocs/core/modules/modFacture.class.php
  26. 7 1
      htdocs/core/modules/modFournisseur.class.php
  27. 3 1
      htdocs/core/modules/modPropale.class.php
  28. 5 2
      htdocs/core/modules/modSociete.class.php
  29. 1 1
      htdocs/core/modules/modWebsites.class.php
  30. 2 0
      htdocs/expedition/card.php
  31. 1 1
      htdocs/exports/class/export.class.php
  32. 22 1
      htdocs/exports/export.php
  33. 1 1
      htdocs/filefunc.inc.php
  34. 0 3
      htdocs/holiday/define_holiday.php
  35. 10 3
      htdocs/holiday/view_log.php
  36. 2 1
      htdocs/install/check.php
  37. 22 4
      htdocs/install/fileconf.php
  38. 35 0
      htdocs/install/mysql/migration/4.0.0-5.0.0.sql
  39. 2 1
      htdocs/install/mysql/tables/llx_ecm_files.sql
  40. 1 0
      htdocs/install/mysql/tables/llx_societe.sql
  41. 1 0
      htdocs/install/mysql/tables/llx_website.sql
  42. 2 0
      htdocs/langs/en_US/website.lang
  43. 1 1
      htdocs/main.inc.php
  44. 14 2
      htdocs/projet/element.php
  45. 3 2
      htdocs/societe/class/societe.class.php
  46. 3 1
      htdocs/theme/eldy/ckeditor/config.js
  47. 6 0
      htdocs/theme/eldy/style.css.php
  48. 3 1
      htdocs/theme/md/ckeditor/config.js
  49. TEMPAT SAMPAH
      htdocs/theme/md/img/info.png
  50. TEMPAT SAMPAH
      htdocs/theme/md/img/title_accountancy.png
  51. TEMPAT SAMPAH
      htdocs/theme/md/img/title_products.png
  52. 9 2
      htdocs/theme/md/style.css.php
  53. 20 2
      htdocs/websites/class/website.class.php
  54. 52 7
      htdocs/websites/index.php
  55. 88 0
      test/phpunit/ExportTest.php
  56. 32 27
      test/phpunit/WebservicesInvoicesTest.php

+ 4 - 1
.travis.yml

@@ -287,10 +287,13 @@ script:
   php upgrade.php 3.9.0 4.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade390400.log
   php upgrade2.php 3.9.0 4.0.0 MAIN_MODULE_API,MAIN_MODULE_SUPPLIERPROPOSAL > $TRAVIS_BUILD_DIR/upgrade390400-2.log
   php step5.php 3.9.0 4.0.0 > $TRAVIS_BUILD_DIR/upgrade390400-3.log
+  php upgrade.php 4.0.0 5.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade400500.log
+  php upgrade2.php 4.0.0 5.0.0 MAIN_MODULE_API,MAIN_MODULE_SUPPLIERPROPOSAL > $TRAVIS_BUILD_DIR/upgrade400500-2.log
+  php step5.php 4.0.0 5.0.0 > $TRAVIS_BUILD_DIR/upgrade400500-3.log
   cd -
   set +e
   echo
-  #cat $TRAVIS_BUILD_DIR/upgrade390400-2.log
+  #cat $TRAVIS_BUILD_DIR/upgrade400500-2.log
   #cat /tmp/dolibarr_install.log
   
 - |

+ 45 - 15
htdocs/accountancy/bookkeeping/balance.php

@@ -23,6 +23,7 @@
  *  \ingroup 	Advanced accountancy
  *  \brief 		Balance of book keeping
  */
+
 require '../../main.inc.php';
 
 // Class
@@ -184,7 +185,7 @@ else {
 	print_liste_field_titre($langs->trans("Labelcompte"), $_SERVER['PHP_SELF'], "t.label_compte", "", $options, "", $sortfield, $sortorder);
 	print_liste_field_titre($langs->trans("Debit"), $_SERVER['PHP_SELF'], "t.debit", "", $options, 'align="right"', $sortfield, $sortorder);
 	print_liste_field_titre($langs->trans("Credit"), $_SERVER['PHP_SELF'], "t.credit", "", $options, 'align="right"', $sortfield, $sortorder);
-	print_liste_field_titre($langs->trans("Solde"), $_SERVER["PHP_SELF"], "", $options, "", 'width="60" align="center"', $sortfield, $sortorder);
+	print_liste_field_titre($langs->trans("Solde"), $_SERVER["PHP_SELF"], "", $options, "", 'align="right"', $sortfield, $sortorder);
 	print_liste_field_titre($langs->trans("Action"), $_SERVER["PHP_SELF"], "", $options, "", 'width="60" align="center"', $sortfield, $sortorder);
 	print "</tr>\n";
 
@@ -213,6 +214,9 @@ else {
 
 	$total_debit = 0;
 	$total_credit = 0;
+  $sous_total_debit = 0;
+  $sous_total_credit = 0;
+  $displayed_account = "";
 
 	foreach ( $object->lines as $line ) {
 		$var = ! $var;
@@ -220,33 +224,59 @@ else {
 		$total_debit += $line->debit;
 		$total_credit += $line->credit;
 		$description = $object->get_compte_desc($line->numero_compte); // Search description of the account
+    $root_account_description = $object->get_compte_racine($line->numero_compte);
 		if(empty($description)){
 			$link = '<a href="../admin/card.php?action=create&compte=' . length_accountg($line->numero_compte) . '">' . img_edit_add() .'</a>';
 		}
 		print '<tr'. $bc[$var].'>';
 
+
+    // Permet d'afficher le compte comptable
+    if ($root_account_description != $displayed_account) {
+
+      // Affiche un Sous-Total par compte comptable
+      if ($displayed_account != "") {
+        print '<tr class="liste_total"><td align="right" colspan="2">'.$langs->trans("SubTotal") . ':</td><td class="nowrap" align="right">'.price($sous_total_debit).'</td><td class="nowrap" align="right">'.price($sous_total_credit).'</td><td class="nowrap" align="right">'.price($sous_total_credit-$sous_total_debit).'</td>';
+        print "<td>&nbsp;</td>\n";
+        print '</tr>';
+      }
+
+      // Affiche le compte comptable en début de ligne
+      print "<tr>";
+    	print '<td colspan="6" style="font-weight:bold; border-bottom: 1pt solid black;">'. $root_account_description .'</td>';
+    	print '</tr>';
+
+      $displayed_account = $root_account_description;
+      $sous_total_debit = 0;
+      $sous_total_credit = 0;
+    }
+
+    // $object->get_compte_racine($line->numero_compte);
+
+
 		print '<td>' . length_accountg($line->numero_compte) . '</td>';
 		print '<td>' . $description . '</td>';
 		print '<td align="right">' .  number_format($line->debit, 2, ',', ' ') . '</td>';
-        print '<td align="right">' . number_format($line->credit, 2, ',', ' ') . '</td>';
-        print '<td align="right">' . number_format($line->credit - $line->debit, 2, ',', ' ') . '</td>';
+    print '<td align="right">' . number_format($line->credit, 2, ',', ' ') . '</td>';
+    print '<td align="right">' . number_format($line->credit - $line->debit, 2, ',', ' ') . '</td>';
 		print '<td align="center">' . $link;
 		print '</td>';
 		print "</tr>\n";
+
+    // Comptabilise le sous-total
+    $sous_total_debit += $line->debit;
+    $sous_total_credit += $line->credit;
+
 	}
 
-	print '<tr class="liste_total">';
-	print '<td></td>';
-	print '<td></td>';
-	print '<td  align="right">';
-	print price($total_debit);
-	print '</td>';
-	print '<td  align="right">';
-	print price($total_credit);
-	print '</td>';
-	print '<td align="right">' . price($total_credit - $total_debit) . '</td>';
-	print '<td align="right"></td>';
-	print '</tr>';
+  print '<tr class="liste_total"><td align="right" colspan="2">'.$langs->trans("SubTotal") . ':</td><td class="nowrap" align="right">'.price($sous_total_debit).'</td><td class="nowrap" align="right">'.price($sous_total_credit).'</td><td class="nowrap" align="right">'.price($sous_total_credit-$sous_total_debit).'</td>';
+  print "<td>&nbsp;</td>\n";
+  print '</tr>';
+
+  print '<tr class="liste_total"><td align="right" colspan="2">'.$langs->trans("AccountBalance") . ':</td><td class="nowrap" align="right">'.price($total_debit).'</td><td class="nowrap" align="right">'.price($total_credit).'</td><td class="nowrap" align="right">'.price($total_credit-$total_debit).'</td>';
+  print "<td>&nbsp;</td>\n";
+  print '</tr>';
+
 
 	print "</table>";
 	print '</form>';

+ 1 - 1
htdocs/accountancy/bookkeeping/list.php

@@ -258,7 +258,6 @@ if ($action == 'export_csv') {
  */
 
 $title_page = $langs->trans("Bookkeeping") . ' ' . dol_print_date($search_date_start) . '-' . dol_print_date($search_date_end);
-
 llxHeader('', $title_page);
 
 // List
@@ -315,6 +314,7 @@ print_barre_liste($title_page, $page, $_SERVER["PHP_SELF"], $options, $sortfield
 
 print '<form method="GET" id="searchFormList" action="' . $_SERVER["PHP_SELF"] . '">';
 print '<div class="tabsAction">' . "\n";
+print '<div class="inline-block divButAction"><a class="butAction" href="./listbyaccount.php">' . $langs->trans("Bookkeeping") . ' ' . strtolower($langs->trans("By")) . ' ' . strtolower($langs->trans("AccountAccounting")) . '</a></div>';
 print '<div class="inline-block divButAction"><input type="submit" name="button_delmvt" class="butAction" value="' . $langs->trans("DelBookKeeping") . '" /></div>';
 print '<div class="inline-block divButAction"><a class="butAction" href="./card.php?action=create">' . $langs->trans("NewAccountingMvt") . '</a></div>';
 print '<div class="inline-block divButAction"><input type="submit" name="button_export_csv" class="butAction" value="' . $langs->trans("Export") . '" /></div>';

+ 348 - 0
htdocs/accountancy/bookkeeping/listbyaccount.php

@@ -0,0 +1,348 @@
+<?php
+/*
+ * Copyright (C) 2016 Neil Orley	<neil.orley@oeris.fr>
+ * largely based on the great work of :
+ *  - Copyright (C) 2013-2016 Olivier Geffroy		<jeff@jeffinfo.com>
+ *  - Copyright (C) 2013-2016 Florian Henry		<florian.henry@open-concept.pro>
+ *  - Copyright (C) 2013-2016 Alexandre Spangaro	<aspangaro.dolibarr@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * \file 		htdocs/accountancy/bookkeeping/listbyaccount.php
+ * \ingroup 	Advanced accountancy
+ * \brief 		List operation of book keeping ordered by account number
+ */
+
+require '../../main.inc.php';
+
+// Class
+require_once DOL_DOCUMENT_ROOT . '/core/lib/accounting.lib.php';
+require_once DOL_DOCUMENT_ROOT . '/accountancy/class/html.formventilation.class.php';
+require_once DOL_DOCUMENT_ROOT . '/accountancy/class/bookkeeping.class.php';
+require_once DOL_DOCUMENT_ROOT . '/core/class/html.formother.class.php';
+
+// Langs
+$langs->load("accountancy");
+
+$page = GETPOST("page");
+$sortorder = GETPOST("sortorder");
+$sortfield = GETPOST("sortfield");
+$action = GETPOST('action', 'alpha');
+$search_date_start = dol_mktime(0, 0, 0, GETPOST('date_startmonth', 'int'), GETPOST('date_startday', 'int'), GETPOST('date_startyear', 'int'));
+$search_date_end = dol_mktime(0, 0, 0, GETPOST('date_endmonth', 'int'), GETPOST('date_endday', 'int'), GETPOST('date_endyear', 'int'));
+$search_doc_date = dol_mktime(0, 0, 0, GETPOST('doc_datemonth', 'int'), GETPOST('doc_dateday', 'int'), GETPOST('doc_dateyear', 'int'));
+
+
+
+$search_accountancy_code = GETPOST("search_accountancy_code");
+
+$search_accountancy_code_start = GETPOST('search_accountancy_code_start', 'alpha');
+if ($search_accountancy_code_start == - 1) {
+	$search_accountancy_code_start = '';
+}
+$search_label_account = GETPOST('search_label_account', 'alpha');
+
+$search_mvt_label = GETPOST('search_mvt_label', 'alpha');
+$search_direction = GETPOST('search_direction', 'alpha');
+$search_ledger_code = GETPOST('search_ledger_code', 'alpha');
+
+$limit = GETPOST('limit') ? GETPOST('limit', 'int') : $conf->liste_limit;
+if ($page == -1) { $page = 0 ; }
+$offset = $limit * $page ;
+$pageprev = $page - 1;
+$pagenext = $page + 1;
+
+$object = new BookKeeping($db);
+
+$formventilation = new FormVentilation($db);
+$formother = new FormOther($db);
+$form = new Form($db);
+
+
+
+
+if (empty($search_date_start)) {
+	$search_date_start = dol_mktime(0, 0, 0, 1, 1, dol_print_date(dol_now(), '%Y'));
+	$search_date_end = dol_mktime(0, 0, 0, 12, 31, dol_print_date(dol_now(), '%Y'));
+}
+if ($sortorder == "")
+	$sortorder = "ASC";
+if ($sortfield == "")
+	$sortfield =  "t.rowid";
+
+
+$options = '';
+$filter = array ();
+
+if (! empty($search_date_start)) {
+	$filter['t.doc_date>='] = $search_date_start;
+	$options .= '&amp;date_startmonth=' . GETPOST('date_startmonth', 'int') . '&amp;date_startday=' . GETPOST('date_startday', 'int') . '&amp;date_startyear=' . GETPOST('date_startyear', 'int');
+}
+if (! empty($search_date_end)) {
+	$filter['t.doc_date<='] = $search_date_end;
+	$options .= '&amp;date_endmonth=' . GETPOST('date_endmonth', 'int') . '&amp;date_endday=' . GETPOST('date_endday', 'int') . '&amp;date_endyear=' . GETPOST('date_endyear', 'int');
+}
+if (! empty($search_doc_date)) {
+	$filter['t.doc_date'] = $search_doc_date;
+	$options .= '&amp;doc_datemonth=' . GETPOST('doc_datemonth', 'int') . '&amp;doc_dateday=' . GETPOST('doc_dateday', 'int') . '&amp;doc_dateyear=' . GETPOST('doc_dateyear', 'int');
+}
+
+
+if (!GETPOST("button_removefilter_x") && !GETPOST("button_removefilter")) // Both test are required to be compatible with all browsers
+{
+  if (! empty($search_accountancy_code_start)) {
+  	$filter['t.numero_compte'] = $search_accountancy_code_start;
+  	$options .= '&amp;search_accountancy_code_start=' . $search_accountancy_code_start;
+  }
+  if (! empty($search_label_account)) {
+  	$filter['t.label_compte'] = $search_label_account;
+  	$options .= '&amp;search_label_account=' . $search_label_account;
+  }
+  if (! empty($search_mvt_label)) {
+  	$filter['t.label_compte'] = $search_mvt_label;
+  	$options .= '&amp;search_mvt_label=' . $search_mvt_label;
+  }
+  if (! empty($search_direction)) {
+  	$filter['t.sens'] = $search_direction;
+  	$options .= '&amp;search_direction=' . $search_direction;
+  }
+  if (! empty($search_ledger_code)) {
+  	$filter['t.code_journal'] = $search_ledger_code;
+  	$options .= '&amp;search_ledger_code=' . $search_ledger_code;
+  }
+}
+
+/*
+ * Action
+ */
+
+if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter")) // Both test are required to be compatible with all browsers
+{
+	$search_doc_date = '';
+	$search_accountancy_code = '';
+	$search_accountancy_code_start = '';
+  $search_label_account = '';
+	$search_mvt_label = '';
+	$search_direction = '';
+	$search_ledger_code = '';
+}
+
+if ($action == 'delmouvconfirm') {
+
+	$mvt_num = GETPOST('mvt_num', 'int');
+
+	if (! empty($mvt_num)) {
+		$result = $object->deleteMvtNum($mvt_num);
+		if ($result < 0) {
+			setEventMessages($object->error, $object->errors, 'errors');
+		}
+		Header("Location: listbyaccount.php");
+		exit();
+	}
+}
+
+
+/*
+ * View
+ */
+
+$title_page = $langs->trans("Bookkeeping") . ' ' . strtolower($langs->trans("By")) . ' ' . $langs->trans("AccountAccounting") . ' ' . dol_print_date($search_date_start) . '-' . dol_print_date($search_date_end);
+
+llxHeader('', $title_page);
+
+// List
+
+$nbtotalofrecords = 0;
+if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) {
+	$nbtotalofrecords = $object->fetchAllByAccount($sortorder, $sortfield, 0, 0, $filter);
+	if ($nbtotalofrecords < 0) {
+		setEventMessages($object->error, $object->errors, 'errors');
+	}
+}
+
+$result = $object->fetchAllByAccount($sortorder, $sortfield, $limit, $offset, $filter);
+if ($result < 0) {
+	setEventMessages($object->error, $object->errors, 'errors');
+}
+$nbtotalofrecords = $result;
+
+if ($action == 'delmouv') {
+	$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?mvt_num=' . GETPOST('mvt_num'), $langs->trans('DeleteMvt'), $langs->trans('ConfirmDeleteMvt'), 'delmouvconfirm', '', 0, 1);
+	print $formconfirm;
+}
+if ($action == 'delbookkeepingyear') {
+
+	$form_question = array ();
+	$delyear = GETPOST('delyear');
+
+	if (empty($delyear)) {
+		$delyear = dol_print_date(dol_now(), '%Y');
+	}
+	$year_array = $formventilation->selectyear_accountancy_bookkepping($delyear, 'delyear', 0, 'array');
+
+	$form_question['delyear'] = array (
+			'name' => 'delyear',
+			'type' => 'select',
+			'label' => $langs->trans('DelYear'),
+			'values' => $year_array,
+			'default' => $delyear
+	);
+
+	$formconfirm = $form->formconfirm($_SERVER["PHP_SELF"], $langs->trans('DeleteMvt'), $langs->trans('ConfirmDeleteMvt'), 'delbookkeepingyearconfirm', $form_question, 0, 1);
+	print $formconfirm;
+}
+
+
+
+
+print '<form method="GET" id="searchFormList" action="' . $_SERVER["PHP_SELF"] . '">';
+
+print_barre_liste($title_page, $page, $_SERVER["PHP_SELF"], $options, $sortfield, $sortorder, '', $result, $nbtotalofrecords,'',0,'','',$limit);
+
+// Reverse sort order
+if ( preg_match('/^asc/i', $sortorder) )
+  $sortorder = "asc";
+else
+  $sortorder = "desc";
+
+print '<div class="tabsAction">' . "\n";
+print '<div class="inline-block divButAction"><a class="butAction" href="./card.php?action=create">' . $langs->trans("NewAccountingMvt") . '</a></div>';
+print '</div>';
+
+print '<table class="noborder" width="100%">';
+print '<tr class="liste_titre">';
+print '<td>' . $langs->trans("AccountAccounting") . '</td>';
+print_liste_field_titre($langs->trans("Docdate"), $_SERVER['PHP_SELF'], "t.doc_date", "", $options, "", $sortfield, $sortorder);
+print_liste_field_titre($langs->trans("Docref"), $_SERVER['PHP_SELF'], "t.doc_ref", "", $options, "", $sortfield, $sortorder);
+print '<td>' . $langs->trans("SuppliersInvoices") . ' / ' . $langs->trans("CustomersInvoices") . '</td>';
+print_liste_field_titre($langs->trans("Debit"), $_SERVER['PHP_SELF'], "t.debit", "", $options, 'align="right"', $sortfield, $sortorder);
+print_liste_field_titre($langs->trans("Credit"), $_SERVER['PHP_SELF'], "t.credit", "", $options, 'align="right"', $sortfield, $sortorder);
+print_liste_field_titre($langs->trans("Codejournal"), $_SERVER['PHP_SELF'], "t.code_journal", "", $options, 'align="center"', $sortfield, $sortorder);
+print_liste_field_titre($langs->trans("Action"), $_SERVER["PHP_SELF"], "", $options, "", 'width="60" align="center"', $sortfield, $sortorder);
+print "</tr>\n";
+
+print '<tr class="liste_titre">';
+print '<form action="' . $_SERVER["PHP_SELF"] . '" method="GET">';
+print '<td width >' . $object->select_account($search_accountancy_code_start, 'search_accountancy_code_start', 1, array (), 1, 1, '') . '</td>';
+print '<td class="liste_titre">';
+print $langs->trans('From') . ': ';
+print $form->select_date($search_date_start, 'date_start', 0, 0, 1);
+print '<br>';
+print $langs->trans('to') . ': ';
+print $form->select_date($search_date_end, 'date_end', 0, 0, 1);
+print '</td>';
+print '<td class="liste_titre"><input type="text" size="7" class="flat" name="search_mvt_label" value="' . $search_mvt_label . '"/></td>';
+print '<td class="liste_titre"><input type="text" size="7" class="flat" name="search_label_account" value="' . $search_label_account . '"/></td>';
+print '<td>&nbsp;</td>';
+print '<td>&nbsp;</td>';
+print '<td  align="right"><input type="text" name="search_ledger_code" size="3" value="' . $search_ledger_code . '"></td>';
+print '<td align="right" colspan="2" class="liste_titre">';
+$searchpitco=$form->showFilterAndCheckAddButtons(0);
+print $searchpitco;
+print '</td>';
+
+print '</tr>';
+
+$var = True;
+
+$total_debit = 0;
+$total_credit = 0;
+$sous_total_debit = 0;
+$sous_total_credit = 0;
+$displayed_account_number = "";
+
+foreach ( $object->lines as $line ) {
+	$var = ! $var;
+
+	$total_debit += $line->debit;
+	$total_credit += $line->credit;
+
+  // Permet d'afficher le compte comptable
+  if (length_accountg($line->numero_compte) != $displayed_account_number) {
+
+    // Affiche un Sous-Total par compte comptable
+    if ($displayed_account_number != "") {
+      print '<tr class="liste_total"><td align="right" colspan="4">'.$langs->trans("SubTotal").':</td><td class="nowrap" align="right">'.price($sous_total_debit).'</td><td class="nowrap" align="right">'.price($sous_total_credit).'</td>';
+      print "<td>&nbsp;</td>\n";
+      print '</tr>';
+    }
+
+    // Affiche le compte comptable en début de ligne
+    print "<tr>";
+  	print '<td colspan="7" style="font-weight:bold; border-bottom: 1pt solid black;">'.length_accountg($line->numero_compte) . ' : ' . $object->get_compte_desc($line->numero_compte).'</td>';
+  	print '</tr>';
+
+    $displayed_account_number = length_accountg($line->numero_compte);
+    $sous_total_debit = 0;
+    $sous_total_credit = 0;
+  }
+
+	print '<tr'. $bc[$var].'>';
+	print '<td>&nbsp;</td>';
+	print '<td align="center">' . dol_print_date($line->doc_date, 'day') . '</td>';
+	print '<td><a href="./card.php?piece_num=' . $line->piece_num . '">' . $line->doc_ref . '</a></td>';
+
+  // Affiche un lien vers la facture client/fournisseur
+  $doc_ref = preg_replace('/\(.*\)/', '', $line->doc_ref);
+  if ($line->doc_type == 'supplier_invoice')
+	 print strlen(length_accounta($line->code_tiers)) == 0 ? '<td><a href="/fourn/facture/list.php?search_ref_supplier=' . $doc_ref . '">' . $line->label_compte . '</a></td>' : '<td><a href="/fourn/facture/list.php?search_ref_supplier=' . $doc_ref . '">' . $line->label_compte . '</a><br /><span style="font-size:0.8em">(' . length_accounta($line->code_tiers) . ')</span></td>';
+  elseif ($line->doc_type == 'customer_invoice')
+    print strlen(length_accounta($line->code_tiers)) == 0 ? '<td><a href="/compta/facture/list.php?search_ref=' . $doc_ref . '">' . $line->label_compte . '</a></td>' : '<td><a href="/compta/facture/list.php?search_ref=' . $doc_ref . '">' . $line->label_compte . '</a><br /><span style="font-size:0.8em">(' . length_accounta($line->code_tiers) . ')</span></td>';
+  else
+    print strlen(length_accounta($line->code_tiers)) == 0 ? '<td>' . $line->label_compte . '</td>' : '<td>' . $line->label_compte . '<br /><span style="font-size:0.8em">(' . length_accounta($line->code_tiers) . ')</span></td>';
+
+
+	print '<td align="right">' . price($line->debit) . '</td>';
+	print '<td align="right">' . price($line->credit) . '</td>';
+	print '<td align="center">' . $line->code_journal . '</td>';
+	print '<td align="center">';
+	print '<a href="./card.php?piece_num=' . $line->piece_num . '">' . img_edit() . '</a>&nbsp;';
+	print '<a href="' . $_SERVER['PHP_SELF'] . '?action=delmouv&mvt_num=' . $line->piece_num . $options . '&page=' . $page . '">' . img_delete() . '</a>';
+	print '</td>';
+	print "</tr>\n";
+
+  // Comptabilise le sous-total
+  $sous_total_debit += $line->debit;
+  $sous_total_credit += $line->credit;
+
+}
+
+// Affiche un Sous-Total du dernier compte comptable affiché
+print '<tr class="liste_total"><td align="right" colspan="4">'.$langs->trans("SubTotal").':</td><td class="nowrap" align="right">'.price($sous_total_debit).'</td><td class="nowrap" align="right">'.price($sous_total_credit).'</td>';
+print "<td>&nbsp;</td>\n";
+print '</tr>';
+
+
+// Affiche le Total
+print '<tr class="liste_total">';
+print '<td align="right" colspan="4">'.$langs->trans("Total").':</td>';
+print '<td  align="right">';
+print price($total_debit);
+print '</td>';
+print '<td  align="right">';
+print price($total_credit);
+print '</td>';
+print '<td colspan="2"></td>';
+print '</tr>';
+
+print "</table>";
+print '</form>';
+
+llxFooter();
+
+
+$db->close();

File diff ditekan karena terlalu besar
+ 273 - 207
htdocs/accountancy/class/bookkeeping.class.php


+ 6 - 5
htdocs/accountancy/tpl/export_journal.tpl.php

@@ -1,5 +1,6 @@
 <?php
 /* Copyright (C) 2015  Alexandre Spangaro	<aspangaro.dolibarr@gmail.com>
+ * Copyright (C) 2016  Charlie Benke		<charlie@patas-monkey.com>
  *
  * 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
@@ -16,12 +17,12 @@
  */
 $prefix = $conf->global->ACCOUNTING_EXPORT_PREFIX_SPEC;
 $format = $conf->global->ACCOUNTING_EXPORT_FORMAT;
+$nodateexport = $conf->global->ACCOUNTING_EXPORT_NO_DATE_IN_FILENAME;
 
 $date_export = dol_print_date($now, '%Y%m%d%H%M%S');
 
 header('Content-Type: text/csv');
-if ($prefix)
-	$filename = $prefix . "_" . "journal_" . $journal . $date_export . "." . $format;
-else
-	$filename = "journal_" . $journal . $date_export . "." . $format;
-header('Content-Disposition: attachment;filename=' . $filename);
+
+$filename = ($prefix?$prefix . "_":""). "journal_" . $journal . ($nodateexport?"":$date_export) . "." . $format;
+
+header('Content-Disposition: attachment;filename=' . $filename);

+ 31 - 1
htdocs/comm/card.php

@@ -46,6 +46,8 @@ if (! empty($conf->adherent->enabled)) require_once DOL_DOCUMENT_ROOT.'/adherent
 if (! empty($conf->ficheinter->enabled)) require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php';
 
 $langs->load("companies");
+$langs->load('banks');
+
 if (! empty($conf->contrat->enabled))  $langs->load("contracts");
 if (! empty($conf->commande->enabled)) $langs->load("orders");
 if (! empty($conf->expedition->enabled)) $langs->load("sendings");
@@ -124,7 +126,15 @@ if (empty($reshook))
 		if ($result < 0) setEventMessages($object->error, $object->errors, 'errors');
 	}
 
-    // customer preferred shipping method
+	// Bank account
+	if ($action == 'setbankaccount' && $user->rights->societe->creer)
+	{
+		$object->fetch($id);
+		$result=$object->setBankAccount(GETPOST('fk_account','int'));
+		if ($result < 0) setEventMessages($object->error, $object->errors, 'errors');
+	}
+	
+	// customer preferred shipping method
     if ($action == 'setshippingmethod' && $user->rights->societe->creer)
     {
         $object->fetch($id);
@@ -337,6 +347,26 @@ if ($id > 0)
 	print "</td>";
 	print '</tr>';
 
+	// Compte bancaire par défaut
+	print '<tr><td class="nowrap">';
+	print '<table width="100%" class="nobordernopadding"><tr><td class="nowrap">';
+	print $langs->trans('BankAccount');
+	print '<td>';
+	if (($action != 'editbankaccount') && $user->rights->societe->creer) print '<td align="right"><a href="'.$_SERVER["PHP_SELF"].'?action=editbankaccount&amp;socid='.$object->id.'">'.img_edit($langs->trans('SetBankAccount'),1).'</a></td>';
+	print '</tr></table>';
+	print '</td><td colspan="3">';
+	if ($action == 'editbankaccount')
+	{
+		$form->formSelectAccount($_SERVER['PHP_SELF'].'?socid='.$object->id,$object->fk_account,'fk_account',1);
+	}
+	else
+	{
+		$form->formSelectAccount($_SERVER['PHP_SELF'].'?socid='.$object->id,$object->fk_account,'none');
+	}
+	print "</td>";
+	print '</tr>';
+
+
 	// Relative discounts (Discounts-Drawbacks-Rebates)
 	print '<tr><td class="nowrap">';
 	print '<table width="100%" class="nobordernopadding"><tr><td class="nowrap">';

File diff ditekan karena terlalu besar
+ 517 - 150
htdocs/comm/propal/class/propal.class.php


File diff ditekan karena terlalu besar
+ 450 - 149
htdocs/commande/class/commande.class.php


+ 183 - 64
htdocs/compta/facture/class/facture.class.php

@@ -59,63 +59,63 @@ class Facture extends CommonInvoice
 	 */
 	protected $table_ref_field = 'facnumber';
 
-	var $socid;
-
-	var $author;
-	var $fk_user_author;
-	var $fk_user_valid;
-	var $date;              // Date invoice
-	var $date_creation;		// Creation date
-	var $date_validation;	// Validation date
-	var $datem;
-	var $ref_client;
-	var $ref_int;
+	public $socid;
+
+	public $author;
+	public $fk_user_author;
+	public $fk_user_valid;
+	public $date;              // Date invoice
+	public $date_creation;		// Creation date
+	public $date_validation;	// Validation date
+	public $datem;
+	public $ref_client;
+	public $ref_int;
 	//Check constants for types
-	var $type = self::TYPE_STANDARD;
+	public $type = self::TYPE_STANDARD;
 
 	//var $amount;
-	var $remise_absolue;
-	var $remise_percent;
-	var $total_ht=0;
-	var $total_tva=0;
-	var $total_ttc=0;
-	var $revenuestamp;
+	public $remise_absolue;
+	public $remise_percent;
+	public $total_ht=0;
+	public $total_tva=0;
+	public $total_ttc=0;
+	public $revenuestamp;
 
 	//! Fermeture apres paiement partiel: discount_vat, badcustomer, abandon
 	//! Fermeture alors que aucun paiement: replaced (si remplace), abandon
-	var $close_code;
+	public $close_code;
 	//! Commentaire si mis a paye sans paiement complet
-	var $close_note;
+	public $close_note;
 	//! 1 if invoice paid COMPLETELY, 0 otherwise (do not use it anymore, use statut and close_code)
-	var $paye;
+	public $paye;
 	//! id of source invoice if replacement invoice or credit note
-	var $fk_facture_source;
-	var $linked_objects=array();
-	var $date_lim_reglement;
-	var $cond_reglement_code;		// Code in llx_c_paiement
-	var $mode_reglement_code;		// Code in llx_c_paiement
-	var $fk_bank;					// Field to store bank id to use when payment mode is withdraw
+	public $fk_facture_source;
+	public $linked_objects=array();
+	public $date_lim_reglement;
+	public $cond_reglement_code;		// Code in llx_c_paiement
+	public $mode_reglement_code;		// Code in llx_c_paiement
+	public $fk_bank;					// Field to store bank id to use when payment mode is withdraw
 	/**
 	 * @deprecated
 	 */
-	var $products=array();
+	public $products=array();
 	/**
 	 * @var FactureLigne[]
 	 */
-	var $lines=array();
-	var $line;
-	var $extraparams=array();
-	var $specimen;
+	public $lines=array();
+	public $line;
+	public $extraparams=array();
+	public $specimen;
 
-	var $fac_rec;
+	public $fac_rec;
 
 	// Multicurrency
-	var $fk_multicurrency;
-	var $multicurrency_code;
-	var $multicurrency_tx;
-	var $multicurrency_total_ht;
-	var $multicurrency_total_tva;
-	var $multicurrency_total_ttc;
+	public $fk_multicurrency;
+	public $multicurrency_code;
+	public $multicurrency_tx;
+	public $multicurrency_total_ht;
+	public $multicurrency_total_tva;
+	public $multicurrency_total_ttc;
 
 	/**
 	 * @var int Situation cycle reference number
@@ -142,6 +142,8 @@ class Facture extends CommonInvoice
 	 */
 	public $tab_next_situation_invoice=array();
 
+	public $oldcopy;
+
     /**
      * Standard invoice
      */
@@ -1486,25 +1488,60 @@ class Facture extends CommonInvoice
 	 *	Set customer ref
 	 *
 	 *	@param     	string	$ref_client		Customer ref
+	 *  @param     	int		$notrigger		1=Does not execute triggers, 0= execuete triggers
 	 *	@return		int						<0 if KO, >0 if OK
 	 */
-	function set_ref_client($ref_client)
+	function set_ref_client($ref_client, $notrigger=0)
 	{
+		$error=0;
+
+		$this->db->begin();
+
 		$sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
 		if (empty($ref_client))
 			$sql .= ' SET ref_client = NULL';
 		else
 			$sql .= ' SET ref_client = \''.$this->db->escape($ref_client).'\'';
 		$sql .= ' WHERE rowid = '.$this->id;
-		if ($this->db->query($sql))
+
+		dol_syslog(__METHOD__.' this->id='.$this->id.', ref_client='.$ref_client, LOG_DEBUG);
+		$resql=$this->db->query($sql);
+		if (!$resql)
+		{
+			$this->errors[]=$this->db->error();
+			$error++;
+		}
+
+		if (! $error)
 		{
 			$this->ref_client = $ref_client;
+		}
+
+		if (! $notrigger && empty($error))
+		{
+			// Call trigger
+			$result=$this->call_trigger('BILL_MODIFY',$user);
+			if ($result < 0) $error++;
+			// End call triggers
+		}
+
+		if (! $error)
+		{
+
+			$this->ref_client = $ref_client;
+
+			$this->db->commit();
 			return 1;
 		}
 		else
 		{
-			dol_print_error($this->db);
-			return -1;
+			foreach($this->errors as $errmsg)
+			{
+				dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
+				$this->error.=($this->error?', '.$errmsg:$errmsg);
+			}
+			$this->db->rollback();
+			return -1*$error;
 		}
 	}
 
@@ -2070,7 +2107,7 @@ class Facture extends CommonInvoice
     					$i++;
     				}
     				if ($final) {
-    					$this->setFinal();
+    					$this->setFinal($user);
     				}
                 }
 			}
@@ -2760,9 +2797,10 @@ class Facture extends CommonInvoice
 	 *
 	 *	@param     	User	$user		User that set discount
 	 *	@param     	double	$remise		Discount
+	 *  @param     	int		$notrigger	1=Does not execute triggers, 0= execuete triggers
 	 *	@return		int 		<0 if ko, >0 if ok
 	 */
-	function set_remise($user, $remise)
+	function set_remise($user, $remise, $notrigger=0)
 	{
 		// Clean parameters
 		if (empty($remise)) $remise=0;
@@ -2771,21 +2809,48 @@ class Facture extends CommonInvoice
 		{
 			$remise=price2num($remise);
 
+			$error=0;
+
+			$this->db->begin();
+
 			$sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
 			$sql.= ' SET remise_percent = '.$remise;
 			$sql.= ' WHERE rowid = '.$this->id;
 			$sql.= ' AND fk_statut = '.self::STATUS_DRAFT;
 
-			if ($this->db->query($sql))
+			dol_syslog(__METHOD__, LOG_DEBUG);
+			$resql=$this->db->query($sql);
+			if (!$resql)
+			{
+				$this->errors[]=$this->db->error();
+				$error++;
+			}
+
+			if (! $notrigger && empty($error))
+			{
+				// Call trigger
+				$result=$this->call_trigger('BILL_MODIFY',$user);
+				if ($result < 0) $error++;
+				// End call triggers
+			}
+
+			if (! $error)
 			{
 				$this->remise_percent = $remise;
 				$this->update_price(1);
+
+				$this->db->commit();
 				return 1;
 			}
 			else
 			{
-				$this->error=$this->db->error();
-				return -1;
+				foreach($this->errors as $errmsg)
+				{
+					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
+					$this->error.=($this->error?', '.$errmsg:$errmsg);
+				}
+				$this->db->rollback();
+				return -1*$error;
 			}
 		}
 	}
@@ -2796,14 +2861,19 @@ class Facture extends CommonInvoice
 	 *
 	 *	@param     	User	$user 		User that set discount
 	 *	@param     	double	$remise		Discount
+	 *  @param     	int		$notrigger	1=Does not execute triggers, 0= execuete triggers
 	 *	@return		int 				<0 if KO, >0 if OK
 	 */
-	function set_remise_absolue($user, $remise)
+	function set_remise_absolue($user, $remise, $notrigger=0)
 	{
 		if (empty($remise)) $remise=0;
 
 		if ($user->rights->facture->creer)
 		{
+			$error=0;
+
+			$this->db->begin();
+
 			$remise=price2num($remise);
 
 			$sql = 'UPDATE '.MAIN_DB_PREFIX.'facture';
@@ -2811,18 +2881,43 @@ class Facture extends CommonInvoice
 			$sql.= ' WHERE rowid = '.$this->id;
 			$sql.= ' AND fk_statut = '.self::STATUS_DRAFT;
 
-			dol_syslog(get_class($this)."::set_remise_absolue", LOG_DEBUG);
+			dol_syslog(__METHOD__, LOG_DEBUG);
+			$resql=$this->db->query($sql);
+			if (!$resql)
+			{
+				$this->errors[]=$this->db->error();
+				$error++;
+			}
 
-			if ($this->db->query($sql))
+			if (! $error)
 			{
+				$this->oldcopy= clone $this;
 				$this->remise_absolue = $remise;
 				$this->update_price(1);
+			}
+
+			if (! $notrigger && empty($error))
+			{
+				// Call trigger
+				$result=$this->call_trigger('BILL_MODIFY',$user);
+				if ($result < 0) $error++;
+				// End call triggers
+			}
+
+			if (! $error)
+			{
+				$this->db->commit();
 				return 1;
 			}
 			else
 			{
-				$this->error=$this->db->error();
-				return -1;
+				foreach($this->errors as $errmsg)
+				{
+					dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
+					$this->error.=($this->error?', '.$errmsg:$errmsg);
+				}
+				$this->db->rollback();
+				return -1*$error;
 			}
 		}
 	}
@@ -3819,25 +3914,49 @@ class Facture extends CommonInvoice
 	/**
 	 * Sets the invoice as a final situation
 	 *
-	 * @return int 1 if ok, -1 if error
+	 *  @param  	User	$user    	Object user
+	 *  @param     	int		$notrigger	1=Does not execute triggers, 0= execuete triggers
+	 *	@return		int 				<0 if KO, >0 if OK
 	 */
-	function setFinal()
+	function setFinal(User $user, $notrigger=0)
 	{
+		$error=0;
 
-        $this->db->begin();
+		$this->db->begin();
 
 		$this->situation_final = 1;
-		$sql = 'update ' . MAIN_DB_PREFIX . 'facture set situation_final = ' . $this->situation_final . ' where rowid = ' . $this->id;
-		$resql = $this->db->query($sql);
-		if ($resql) {
-			// FIXME: call triggers MODIFY because we modify invoice
+		$sql = 'UPDATE ' . MAIN_DB_PREFIX . 'facture SET situation_final = ' . $this->situation_final . ' where rowid = ' . $this->id;
+
+		dol_syslog(__METHOD__, LOG_DEBUG);
+		$resql=$this->db->query($sql);
+		if (!$resql)
+		{
+			$this->errors[]=$this->db->error();
+			$error++;
+		}
+
+		if (! $notrigger && empty($error))
+		{
+			// Call trigger
+			$result=$this->call_trigger('BILL_MODIFY',$user);
+			if ($result < 0) $error++;
+			// End call triggers
+		}
+
+		if (! $error)
+		{
 			$this->db->commit();
 			return 1;
-		} else {
-			$this->error = $this->db->lasterror();
-			dol_syslog(get_class($this) . "::update Error setFinal " . $sql, LOG_ERR);
+		}
+		else
+		{
+			foreach($this->errors as $errmsg)
+			{
+				dol_syslog(__METHOD__.' Error: '.$errmsg, LOG_ERR);
+				$this->error.=($this->error?', '.$errmsg:$errmsg);
+			}
 			$this->db->rollback();
-			return -1;
+			return -1*$error;
 		}
 	}
 

+ 1 - 1
htdocs/contact/card.php

@@ -904,7 +904,7 @@ else
 
             // Statut
             print '<tr><td>'.$langs->trans("Status").'</td>';
-            print '<td>';
+            print '<td colspan="3">';
             print $object->getLibStatut(4);
             print '</td></tr>';
 

+ 14 - 1
htdocs/core/ajax/saveinplace.php

@@ -35,6 +35,19 @@ $element		= GETPOST('element','alpha',2);
 $table_element	= GETPOST('table_element','alpha',2);
 $fk_element		= GETPOST('fk_element','alpha',2);
 
+/* Example:
+field:editval_ref_customer (8 first chars will removed to know name of property)
+element:contrat
+table_element:contrat
+fk_element:4
+type:string
+value:aaa
+loadmethod:
+savemethod:
+savemethodname:
+*/
+
+
 /*
  * View
  */
@@ -81,7 +94,7 @@ if (! empty($field) && ! empty($element) && ! empty($table_element) && ! empty($
 	}
 	else $newelement = $element;
 
-	if (! empty($user->rights->$newelement->creer) || ! empty($user->rights->$newelement->write)
+	if (! empty($user->rights->$newelement->creer) || ! empty($user->rights->$newelement->create) || ! empty($user->rights->$newelement->write)
 	|| (isset($subelement) && (! empty($user->rights->$newelement->$subelement->creer) || ! empty($user->rights->$newelement->$subelement->write)))
 	|| ($element == 'payment' && $user->rights->facture->paiement)
 	|| ($element == 'payment_supplier' && $user->rights->fournisseur->facture->creer))

+ 1 - 1
htdocs/core/class/doleditor.class.php

@@ -99,7 +99,7 @@ class DolEditor
         	$this->editor->Value	= $content;
         	$this->editor->Height   = $height;
         	if (! empty($width)) $this->editor->Width = $width;
-        	$this->editor->ToolbarSet = $shorttoolbarname;
+        	$this->editor->ToolbarSet = $shorttoolbarname;         // Profile of this toolbar set is deinfed into theme/mytheme/ckeditor/config.js
         	$this->editor->Config['AutoDetectLanguage'] = 'true';
         	$this->editor->Config['ToolbarLocation'] = $toolbarlocation ? $toolbarlocation : 'In';
         	$this->editor->Config['ToolbarStartExpanded'] = $toolbarstartexpanded;

+ 1 - 2
htdocs/core/class/genericobject.class.php

@@ -24,8 +24,7 @@ require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
 
 
 /**
- *	\class 		GenericObject
- *	\brief 		Class of a generic business object
+ *	Class of a generic business object
  */
 
 class GenericObject extends CommonObject

+ 2 - 1
htdocs/core/class/hookmanager.class.php

@@ -142,7 +142,8 @@ class HookManager
 				'formObjectOptions',
 				'formattachOptions',
 				'formBuilddocLineOptions',
-				'getIdProfUrl',
+			    'getFormMail',
+			    'getIdProfUrl',
 				'moveUploadedFile',
 			    'pdf_build_address',
 				'pdf_writelinedesc',

+ 2 - 0
htdocs/core/lib/company.lib.php

@@ -79,7 +79,9 @@ function societe_prepare_head(Societe $object)
     if (! empty($conf->global->MAIN_SUPPORT_SHARED_CONTACT_BETWEEN_THIRDPARTIES))
     {
         $head[$h][0] = DOL_URL_ROOT.'/societe/societecontact.php?socid='.$object->id;
+	    $nbContact = count($object->liste_contact(-1,'internal')) + count($object->liste_contact(-1,'external'));
         $head[$h][1] = $langs->trans("ContactsAddresses");
+		if ($nbContact > 0) $head[$h][1].= ' <span class="badge">'.$nbContact.'</span>';
         $head[$h][2] = 'contact';
         $h++;
     }

+ 18 - 6
htdocs/core/lib/date.lib.php

@@ -117,12 +117,24 @@ function dol_time_plus_duree($time, $duration_value, $duration_unit)
 	if ($duration_value == 0)  return $time;
 	if ($duration_unit == 'h') return $time + (3600*$duration_value);
 	if ($duration_unit == 'w') return $time + (3600*24*7*$duration_value);
-	if ($duration_value > 0) $deltastring="+".abs($duration_value);
-	if ($duration_value < 0) $deltastring="-".abs($duration_value);
-	if ($duration_unit == 'd') { $deltastring.=" day"; }
-	if ($duration_unit == 'm') { $deltastring.=" month"; }
-	if ($duration_unit == 'y') { $deltastring.=" year"; }
-	return strtotime($deltastring,$time);
+	
+	$deltastring='P';
+	
+	if ($duration_value > 0){ $deltastring.=abs($duration_value); $sub= false; }
+	if ($duration_value < 0){ $deltastring.=abs($duration_value); $sub= true; }
+	if ($duration_unit == 'd') { $deltastring.="D"; }
+	if ($duration_unit == 'm') { $deltastring.="M"; }
+	if ($duration_unit == 'y') { $deltastring.="Y"; }
+	
+	$date = new DateTime();
+	$date->setTimezone(new DateTimeZone('UTC'));
+	$date->setTimestamp($time);
+	$interval = new DateInterval($deltastring);
+	
+	if($sub) $date->sub($interval);
+	else $date->add( $interval );
+	
+	return $date->getTimestamp();
 }
 
 

+ 3 - 3
htdocs/core/lib/functions.lib.php

@@ -4267,7 +4267,7 @@ function picto_required()
  *	Clean a string from all HTML tags and entities
  *
  *	@param	string	$StringHtml			String to clean
- *	@param	integer	$removelinefeed		Replace also all lines feeds by a space, otherwise only last one are removed
+ *	@param	integer	$removelinefeed		1=Replace also all lines feeds by a space, 0=Only last one are removed
  *  @param  string	$pagecodeto      	Encoding of input/output string
  *	@return string	    				String cleaned
  *
@@ -5257,7 +5257,7 @@ function complete_head_from_modules($conf,$langs,$object,&$head,&$h,$type,$mode=
 						if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
 						{
 							$substitutionarray=array();
-							complete_substitutions_array($substitutionarray,$langs,$object);
+							complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
 							$label=make_substitutions($reg[1], $substitutionarray);
 						}
 						else $label=$langs->trans($values[2]);
@@ -5277,7 +5277,7 @@ function complete_head_from_modules($conf,$langs,$object,&$head,&$h,$type,$mode=
 					if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
 					{
 						$substitutionarray=array();
-						complete_substitutions_array($substitutionarray,$langs,$object);
+						complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
 						$label=make_substitutions($reg[1], $substitutionarray);
 					}
 					else $label=$langs->trans($values[2]);

+ 5 - 0
htdocs/core/lib/functions2.lib.php

@@ -1971,6 +1971,11 @@ function getElementProperties($element_type)
         $module='resource';
         $subelement='dolresource';
     }
+    if ($element_type == 'propaldet') {
+        $classpath = 'comm/propal/class';
+        $module='propal';
+        $subelement='propaleligne';
+    }
     $classfile = strtolower($subelement);
     $classname = ucfirst($subelement);
 

+ 21 - 9
htdocs/core/modules/export/export_csv.modules.php

@@ -206,7 +206,7 @@ class ExportCsv extends ModeleExports
 		foreach($array_selected_sorted as $code => $value)
 		{
 			$newvalue=$outputlangs->transnoentities($array_export_fields_label[$code]);		// newvalue is now $outputlangs->charset_output encoded
-			$newvalue=$this->csv_clean($newvalue,$outputlangs->charset_output);
+			$newvalue=$this->csvClean($newvalue,$outputlangs->charset_output);
 
 			fwrite($this->handle,$newvalue.$this->separator);
 		}
@@ -250,7 +250,7 @@ class ExportCsv extends ModeleExports
 			// Translation newvalue
 			if (preg_match('/^\((.*)\)$/i',$newvalue,$reg)) $newvalue=$outputlangs->transnoentities($reg[1]);
 
-			$newvalue=$this->csv_clean($newvalue,$outputlangs->charset_output);
+			$newvalue=$this->csvClean($newvalue,$outputlangs->charset_output);
 
 			if (preg_match('/^Select:/i', $typefield, $reg) && $typefield = substr($typefield, 7))
 			{
@@ -292,24 +292,36 @@ class ExportCsv extends ModeleExports
 
 	/**
 	 * Clean a cell to respect rules of CSV file cells
+	 * Note: It uses $this->separator
+	 * Note: We keep this function public to be able to test
 	 *
 	 * @param 	string	$newvalue	String to clean
 	 * @param	string	$charset	Input AND Output character set
 	 * @return 	string				Value cleaned
 	 */
-	function csv_clean($newvalue, $charset)
+	public function csvClean($newvalue, $charset)
 	{
+		global $conf;
 		$addquote=0;
+		
 
 		// Rule Dolibarr: No HTML
-		//print $charset.' '.$newvalue."\n";
-		$newvalue=dol_string_nohtmltag($newvalue,1,$charset);
-		//print $charset.' '.$newvalue."\n";
-
-		// Rule 1 CSV: No CR, LF in cells
+   		//print $charset.' '.$newvalue."\n";
+   		//$newvalue=dol_string_nohtmltag($newvalue,0,$charset);
+   		$newvalue=dol_htmlcleanlastbr($newvalue);
+   		//print $charset.' '.$newvalue."\n";
+		
+		// Rule 1 CSV: No CR, LF in cells (except if USE_STRICT_CSV_RULES is on, we can keep record as it is but we must add quotes)
+		$oldvalue=$newvalue;
 		$newvalue=str_replace("\r",'',$newvalue);
 		$newvalue=str_replace("\n",'\n',$newvalue);
-
+		if (! empty($conf->global->USE_STRICT_CSV_RULES) && $oldvalue != $newvalue)
+		{
+			// If strict use of CSV rules, we just add quote
+			$newvalue=$oldvalue;
+			$addquote=1;
+		}
+		
 		// Rule 2 CSV: If value contains ", we must escape with ", and add "
 		if (preg_match('/"/',$newvalue))
 		{

+ 5 - 1
htdocs/core/modules/modAgenda.class.php

@@ -44,7 +44,7 @@ class modAgenda extends DolibarrModules
 	 */
 	function __construct($db)
 	{
-		global $conf;
+		global $conf, $user;
 
 		$this->db = $db;
 		$this->numero = 2400;
@@ -392,10 +392,14 @@ class modAgenda extends DolibarrModules
 		$this->export_sql_start[$r]='SELECT DISTINCT ';
 		$this->export_sql_end[$r]  =' FROM  '.MAIN_DB_PREFIX.'actioncomm as ac';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_actioncomm as cac on ac.fk_action = cac.id';
+		if(!$user->rights->agenda->allactions->read) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'actioncomm_resources acr on ac.id = acr.fk_actioncomm';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'socpeople as sp on ac.fk_contact = sp.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s on ac.fk_soc = s.rowid';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as co on s.fk_pays = co.rowid';
 		$this->export_sql_end[$r] .=' WHERE ac.entity IN ('.getEntity('agenda',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND (sc.fk_user = '.$user->id.' OR ac.fk_soc IS NULL)';
+		if(!$user->rights->agenda->allactions->read) $this->export_sql_end[$r] .=' AND acr.fk_element = '.$user->id;
 		$this->export_sql_end[$r] .=' ORDER BY ac.datep';
 
 	}

+ 3 - 1
htdocs/core/modules/modCommande.class.php

@@ -45,7 +45,7 @@ class modCommande extends DolibarrModules
 	 */
 	function __construct($db)
 	{
-		global $conf;
+		global $conf, $user;
 
 		$this->db = $db;
 		$this->numero = 25;
@@ -191,6 +191,7 @@ class modCommande extends DolibarrModules
 		include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php';
 		$this->export_sql_start[$r]='SELECT DISTINCT ';
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'societe as s';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_departements as d ON s.fk_departement = d.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as co ON s.fk_pays = co.rowid,';
 		$this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'commande as c';
@@ -204,6 +205,7 @@ class modCommande extends DolibarrModules
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product_extrafields as extra3 on p.rowid = extra3.fk_object';
 		$this->export_sql_end[$r] .=' WHERE c.fk_soc = s.rowid AND c.rowid = cd.fk_commande';
 		$this->export_sql_end[$r] .=' AND c.entity IN ('.getEntity('commande',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
 	}
 
 

+ 9 - 1
htdocs/core/modules/modDeplacement.class.php

@@ -39,7 +39,7 @@ class modDeplacement extends DolibarrModules
 	 */
 	function __construct($db)
 	{
-		global $conf;
+		global $conf, $user;
 
 		$this->db = $db;
 		$this->numero = 75 ;
@@ -124,8 +124,16 @@ class modDeplacement extends DolibarrModules
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'user as u';
 		$this->export_sql_end[$r] .=', '.MAIN_DB_PREFIX.'deplacement as d';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON d.fk_soc = s.rowid';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' WHERE d.fk_user = u.rowid';
 		$this->export_sql_end[$r] .=' AND d.entity IN ('.getEntity('deplacement',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND (sc.fk_user = '.$user->id.' OR d.fk_soc IS NULL)';
+		
+		$childids = $user->getAllChildIds();
+		$childids[]=$user->id;
+		
+		if (!$user->rights->deplacement->readall && !$user->rights->deplacement->lire_tous) $sql.=' AND d.fk_user IN ('.join(',',$childids).')';
+		
 	}
 
 

+ 3 - 1
htdocs/core/modules/modExpedition.class.php

@@ -43,7 +43,7 @@ class modExpedition extends DolibarrModules
 	 */
 	function __construct($db)
 	{
-		global $conf;
+		global $conf, $user;
 
 		$this->db = $db;
 		$this->numero = 80;
@@ -257,6 +257,7 @@ class modExpedition extends DolibarrModules
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'expedition as c';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'expedition_extrafields as extra ON c.rowid = extra.fk_object,';
 		$this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'societe as s';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_departements as d ON s.fk_departement = d.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as co ON s.fk_pays = co.rowid,';
 		$this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'expeditiondet as ed';
@@ -271,6 +272,7 @@ class modExpedition extends DolibarrModules
 		}
 		$this->export_sql_end[$r] .=' WHERE c.fk_soc = s.rowid AND c.rowid = ed.fk_expedition AND ed.fk_origin_line = cd.rowid';
 		$this->export_sql_end[$r] .=' AND c.entity IN ('.getEntity('shipment',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
 	}
 
 

+ 5 - 1
htdocs/core/modules/modFacture.class.php

@@ -42,7 +42,7 @@ class modFacture extends DolibarrModules
 	 */
 	function __construct($db)
 	{
-		global $conf;
+		global $conf, $user;
 
 		$this->db = $db;
 		$this->numero = 30;
@@ -205,6 +205,7 @@ class modFacture extends DolibarrModules
 		include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php';
 		$this->export_sql_start[$r]='SELECT DISTINCT ';
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'societe as s';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c on s.fk_pays = c.rowid,';
 		$this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'facture as f';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'projet as pj ON f.fk_projet = pj.rowid';
@@ -217,6 +218,7 @@ class modFacture extends DolibarrModules
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product_extrafields as extra3 on p.rowid = extra3.fk_object';
 		$this->export_sql_end[$r] .=' WHERE f.fk_soc = s.rowid AND f.rowid = fd.fk_facture';
 		$this->export_sql_end[$r] .=' AND f.entity IN ('.getEntity('facture',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
 		$r++;
 
 		$this->export_code[$r]=$this->rights_class.'_'.$r;
@@ -232,6 +234,7 @@ class modFacture extends DolibarrModules
 		include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php';
 		$this->export_sql_start[$r]='SELECT DISTINCT ';
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'societe as s';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c on s.fk_pays = c.rowid,';
 		$this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'facture as f';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'projet as pj ON f.fk_projet = pj.rowid';
@@ -245,6 +248,7 @@ class modFacture extends DolibarrModules
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'bank_account as ba ON ba.rowid = b.fk_account';
 		$this->export_sql_end[$r] .=' WHERE f.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' AND f.entity IN ('.getEntity('facture',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
 		$r++;
 	}
 

+ 7 - 1
htdocs/core/modules/modFournisseur.class.php

@@ -42,7 +42,7 @@ class modFournisseur extends DolibarrModules
 	 */
 	function __construct($db)
 	{
-		global $conf;
+		global $conf, $user;
 
 		$this->db = $db;
 		$this->numero = 40;
@@ -366,6 +366,7 @@ class modFournisseur extends DolibarrModules
 		// End add extra fields line
 		$this->export_sql_start[$r]='SELECT DISTINCT ';
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'societe as s';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON s.fk_pays = c.rowid,';
 		$this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'facture_fourn as f';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'projet as project on (f.fk_projet = project.rowid)';
@@ -375,6 +376,7 @@ class modFournisseur extends DolibarrModules
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product as p on (fd.fk_product = p.rowid)';
 		$this->export_sql_end[$r] .=' WHERE f.fk_soc = s.rowid AND f.rowid = fd.fk_facture_fourn';
 		$this->export_sql_end[$r] .=' AND f.entity IN ('.getEntity('supplier_invoice',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
 
 		$r++;
 		$this->export_code[$r]=$this->rights_class.'_'.$r;
@@ -428,6 +430,7 @@ class modFournisseur extends DolibarrModules
 		// End add extra fields object
 		$this->export_sql_start[$r]='SELECT DISTINCT ';
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'societe as s';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON s.fk_pays = c.rowid,';
 		$this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'facture_fourn as f';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'projet as project on (f.fk_projet = project.rowid)';
@@ -436,6 +439,7 @@ class modFournisseur extends DolibarrModules
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'paiementfourn as p ON pf.fk_paiementfourn = p.rowid';
 		$this->export_sql_end[$r] .=' WHERE f.fk_soc = s.rowid';
         $this->export_sql_end[$r] .=' AND f.entity IN ('.getEntity('supplier_invoice',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
 
 		// Order
 		$r++;
@@ -532,6 +536,7 @@ class modFournisseur extends DolibarrModules
 		// End add extra fields line
 		$this->export_sql_start[$r]='SELECT DISTINCT ';
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'societe as s';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as c ON s.fk_pays = c.rowid,';
 		$this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'commande_fournisseur as f';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'projet as project on (f.fk_projet = project.rowid)';
@@ -543,6 +548,7 @@ class modFournisseur extends DolibarrModules
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product as p on (fd.fk_product = p.rowid)';
 		$this->export_sql_end[$r] .=' WHERE f.fk_soc = s.rowid AND f.rowid = fd.fk_commande';
 		$this->export_sql_end[$r] .=' AND f.entity IN ('.getEntity('supplier_order',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
 	}
 
 

+ 3 - 1
htdocs/core/modules/modPropale.class.php

@@ -43,7 +43,7 @@ class modPropale extends DolibarrModules
 	 */
 	function __construct($db)
 	{
-		global $conf;
+		global $conf, $user;
 
 		$this->db = $db;
 		$this->numero = 20;
@@ -184,6 +184,7 @@ class modPropale extends DolibarrModules
 		include DOL_DOCUMENT_ROOT.'/core/extrafieldsinexport.inc.php';
 		$this->export_sql_start[$r]='SELECT DISTINCT ';
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'societe as s ';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as co ON s.fk_pays = co.rowid,';
 		$this->export_sql_end[$r] .=' '.MAIN_DB_PREFIX.'propal as c';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'projet as pj ON c.fk_projet = pj.rowid';
@@ -196,6 +197,7 @@ class modPropale extends DolibarrModules
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'product_extrafields as extra3 on p.rowid = extra3.fk_object';
 		$this->export_sql_end[$r] .=' WHERE c.fk_soc = s.rowid AND c.rowid = cd.fk_propal';
 		$this->export_sql_end[$r] .=' AND c.entity IN ('.getEntity('propal',1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
 	}
 
 

+ 5 - 2
htdocs/core/modules/modSociete.class.php

@@ -43,7 +43,7 @@ class modSociete extends DolibarrModules
 	 */
 	function __construct($db)
 	{
-		global $conf;
+		global $conf, $user;
 
 		$this->db = $db;
 		$this->numero = 1;
@@ -274,7 +274,8 @@ class modSociete extends DolibarrModules
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_stcomm as st ON s.fk_stcomm = st.id';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid LEFT JOIN '.MAIN_DB_PREFIX.'user as u ON sc.fk_user = u.rowid';
 		$this->export_sql_end[$r] .=' WHERE s.entity IN ('.getEntity('societe', 1).')';
-
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
+		
 		// Export list of contacts and attributes
 		$r++;
 		$this->export_code[$r]=$this->rights_class.'_'.$r;
@@ -295,10 +296,12 @@ class modSociete extends DolibarrModules
         $this->export_sql_start[$r]='SELECT DISTINCT ';
 		$this->export_sql_end[$r]  =' FROM '.MAIN_DB_PREFIX.'socpeople as c';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON c.fk_soc = s.rowid';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'societe_commerciaux as sc ON sc.fk_soc = s.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_departements as d ON c.fk_departement = d.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'c_country as co ON c.fk_pays = co.rowid';
 		$this->export_sql_end[$r] .=' LEFT JOIN '.MAIN_DB_PREFIX.'socpeople_extrafields as extra ON extra.fk_object = c.rowid';
 		$this->export_sql_end[$r] .=' WHERE c.entity IN ('.getEntity("societe", 1).')';
+		if(!$user->rights->societe->client->voir) $this->export_sql_end[$r] .=' AND sc.fk_user = '.$user->id;
 
 
 		// Imports

+ 1 - 1
htdocs/core/modules/modWebsites.class.php

@@ -99,7 +99,7 @@ class modWebsites extends DolibarrModules
 		$this->rights[$r][0] = 10002;
 		$this->rights[$r][1] = 'Create/modify website content';
 		$this->rights[$r][3] = 0;
-		$this->rights[$r][4] = 'create';
+		$this->rights[$r][4] = 'write';
 		$r++;
 
 		$this->rights[$r][0] = 10003;

+ 2 - 0
htdocs/expedition/card.php

@@ -1924,6 +1924,8 @@ else if ($id || $ref)
 		// Tableau des substitutions
 		$formmail->setSubstitFromObject($object);
 		$formmail->substit['__SHIPPINGREF__']=$object->ref;
+		$formmail->substit['__SHIPPINGTRACKNUM__']=$object->tracking_number;
+		$formmail->substit['__SHIPPINGTRACKNUMURL__']=$object->tracking_url;
 
 		//Find the good contact adress
 		if ($typeobject == 'commande' && $object->$typeobject->id && ! empty($conf->commande->enabled))	{

+ 1 - 1
htdocs/exports/class/export.class.php

@@ -526,7 +526,7 @@ class Export
 			return -1;
 		}
 
-		// Creation de la classe d'export du model ExportXXX
+		// Creation of class to export using model ExportXXX
 		$dir = DOL_DOCUMENT_ROOT . "/core/modules/export/";
 		$file = "export_".$model.".modules.php";
 		$classname = "Export".$model;

+ 22 - 1
htdocs/exports/export.php

@@ -638,7 +638,14 @@ if ($step == 2 && $datatoexport)
 
 if ($step == 3 && $datatoexport)
 {
-	llxHeader('',$langs->trans("NewExport"),'EN:Module_Exports_En|FR:Module_Exports|ES:M&oacute;dulo_Exportaciones');
+    if (count($array_selected) < 1)      // This occurs when going back to page after sessecion expired
+    {
+        // Switch to step 2
+        header("Location: ".DOL_URL_ROOT.'/exports/export.php?step=2&datatoexport='.$datatoexport);
+        exit;
+    }
+
+    llxHeader('',$langs->trans("NewExport"),'EN:Module_Exports_En|FR:Module_Exports|ES:M&oacute;dulo_Exportaciones');
 
 	/*
 	 * Affichage onglets
@@ -792,6 +799,13 @@ if ($step == 3 && $datatoexport)
 
 if ($step == 4 && $datatoexport)
 {
+    if (count($array_selected) < 1)     // This occurs when going back to page after sessecion expired
+    {
+        // Switch to step 2
+        header("Location: ".DOL_URL_ROOT.'/exports/export.php?step=2&datatoexport='.$datatoexport);
+        exit;
+    }
+    
     asort($array_selected);
 
     llxHeader('',$langs->trans("NewExport"),'EN:Module_Exports_En|FR:Module_Exports|ES:M&oacute;dulo_Exportaciones');
@@ -1017,6 +1031,13 @@ if ($step == 4 && $datatoexport)
 
 if ($step == 5 && $datatoexport)
 {
+    if (count($array_selected) < 1)      // This occurs when going back to page after sessecion expired
+    {
+        // Switch to step 2
+        header("Location: ".DOL_URL_ROOT.'/exports/export.php?step=2&datatoexport='.$datatoexport);
+        exit;
+    }
+    
 	asort($array_selected);
 
     llxHeader('',$langs->trans("NewExport"),'EN:Module_Exports_En|FR:Module_Exports|ES:M&oacute;dulo_Exportaciones');

+ 1 - 1
htdocs/filefunc.inc.php

@@ -31,7 +31,7 @@
  */
 
 if (! defined('DOL_APPLICATION_TITLE')) define('DOL_APPLICATION_TITLE','Dolibarr');
-if (! defined('DOL_VERSION')) define('DOL_VERSION','4.0.0-rc');
+if (! defined('DOL_VERSION')) define('DOL_VERSION','5.0.0-alpha');
 
 if (! defined('EURO')) define('EURO',chr(128));
 

+ 0 - 3
htdocs/holiday/define_holiday.php

@@ -169,9 +169,6 @@ if ($lastUpdate)
     print '<br>'.$langs->trans("MonthOfLastMonthlyUpdate").': <strong>'.$yearLastUpdate.'-'.$monthLastUpdate.'</strong>'."\n";
 }
 else print $langs->trans('None');
-
-
-
 print "</div><br>\n";
 
 $result = $holiday->updateBalance();	// Create users into table holiday if they don't exists. TODO Remove this whif we use field into table user.

+ 10 - 3
htdocs/holiday/view_log.php

@@ -60,7 +60,14 @@ $log_holiday = $cp->fetchLog('ORDER BY cpl.rowid DESC', " AND date_action BETWEE
 print load_fiche_titre($langs->trans('LogCP'), '<div class="pagination"><ul><li class="pagination"><a href="'.$_SERVER["PHP_SELF"].'?year='.($year-1).'">&lt;</a><li class="pagination"><a href="">'.$langs->trans("Year").' '.$year.'</a></li><li class="pagination"><a href="'.$_SERVER["PHP_SELF"].'?year='.($year+1).'">&gt;</a></li></lu></div>', 'title_hrm.png');
 
 print '<div class="info">'.$langs->trans('LastUpdateCP').': '."\n";
-if ($cp->getConfCP('lastUpdate')) print '<strong>'.dol_print_date($db->jdate($cp->getConfCP('lastUpdate')),'dayhour','tzuser').'</strong>';
+$lastUpdate = $cp->getConfCP('lastUpdate');
+if ($lastUpdate)
+{
+    $monthLastUpdate = $lastUpdate[4].$lastUpdate[5];
+    $yearLastUpdate = $lastUpdate[0].$lastUpdate[1].$lastUpdate[2].$lastUpdate[3];
+    print '<strong>'.dol_print_date($db->jdate($cp->getConfCP('lastUpdate')),'dayhour','tzuser').'</strong>';
+    print '<br>'.$langs->trans("MonthOfLastMonthlyUpdate").': <strong>'.$yearLastUpdate.'-'.$monthLastUpdate.'</strong>'."\n";
+}
 else print $langs->trans('None');
 print "</div><br>\n";
 
@@ -108,8 +115,8 @@ foreach($cp->logs as $logs_CP)
 
 if ($log_holiday == '2')
 {
-    print '<tr>';
-    print '<td colspan="8" '.$bc[false].'>'.$langs->trans('None').'</td>';
+    print '<tr '.$bc[false].'>';
+    print '<td colspan="8" class="opacitymedium">'.$langs->trans('NoRecordFound').'</td>';
     print '</tr>';
 }
 

+ 2 - 1
htdocs/install/check.php

@@ -392,7 +392,8 @@ else
 								array('from'=>'3.6.0', 'to'=>'3.7.0'),
 								array('from'=>'3.7.0', 'to'=>'3.8.0'),
 		                        array('from'=>'3.8.0', 'to'=>'3.9.0'),
-		                        array('from'=>'3.9.0', 'to'=>'4.0.0')
+		                        array('from'=>'3.9.0', 'to'=>'4.0.0'),
+		                        array('from'=>'4.0.0', 'to'=>'5.0.0')
 		);
 
 		$count=0;

+ 22 - 4
htdocs/install/fileconf.php

@@ -369,7 +369,7 @@ if (! empty($force_install_message))
 
 	<tr class="hidesqlite">
 		<td valign="top" class="label"><?php echo $langs->trans("Port"); ?></td>
-		<td valign="top" class="label"><input type="text"
+		<td valign="top" class="label"><input type="text" id="db_port"
 			name="db_port<?php print ($force_install_noedit==2 && $force_install_port)?'_bis':''; ?>"
 			<?php if ($force_install_noedit==2 && $force_install_port) print ' disabled'; ?>
 			value="<?php print (! empty($dolibarr_main_db_port))?$dolibarr_main_db_port:$force_install_port; ?>">
@@ -484,9 +484,27 @@ if (! empty($force_install_message))
 <script type="text/javascript">
 jQuery(document).ready(function() {
 
-	jQuery("#db_type").change(function() {
-		if (jQuery("#db_type").val()=='sqlite' || jQuery("#db_type").val()=='sqlite3') { jQuery(".hidesqlite").hide(); }
-		else  { jQuery(".hidesqlite").show(); }
+	var dbtype = jQuery("#db_type");
+
+	dbtype.change(function () {
+		if (dbtype.val() == 'sqlite' || dbtype.val() == 'sqlite3') {
+			jQuery(".hidesqlite").hide();
+		} else {
+			jQuery(".hidesqlite").show();
+		}
+
+		// Automatically set default database ports and admin user
+		if (dbtype.val() == 'mysql' || dbtype.val() == 'mysqli') {
+			jQuery("#db_port").val(3306);
+			jQuery("#db_user_root").val('root');
+		} else if (dbtype.val() == 'pgsql') {
+			jQuery("#db_port").val(5432);
+			jQuery("#db_user_root").val('postgres');
+		} else if (dbtype.val() == 'mssql') {
+			jQuery("#db_port").val(1433);
+			jQuery("#db_user_root").val('sa');
+		}
+
 	});
 
 	function init_needroot()

+ 35 - 0
htdocs/install/mysql/migration/4.0.0-5.0.0.sql

@@ -0,0 +1,35 @@
+--
+-- Be carefull to requests order.
+-- This file must be loaded by calling /install/index.php page
+-- when current version is 4.0.0 or higher.
+--
+-- To rename a table:       ALTER TABLE llx_table RENAME TO llx_table_new;
+-- To add a column:         ALTER TABLE llx_table ADD COLUMN newcol varchar(60) NOT NULL DEFAULT '0' AFTER existingcol;
+-- To rename a column:      ALTER TABLE llx_table CHANGE COLUMN oldname newname varchar(60);
+-- To drop a column:        ALTER TABLE llx_table DROP COLUMN oldname;
+-- To change type of field: ALTER TABLE llx_table MODIFY COLUMN name varchar(60);
+-- To drop a foreign key:   ALTER TABLE llx_table DROP FOREIGN KEY fk_name;
+-- To drop an index:        -- VMYSQL4.0 DROP INDEX nomindex on llx_table
+-- To drop an index:        -- VPGSQL8.0 DROP INDEX nomindex
+-- To restrict request to Mysql version x.y minimum use -- VMYSQLx.y
+-- To restrict request to Pgsql version x.y minimum use -- VPGSQLx.y
+-- To make pk to be auto increment (mysql):    VMYSQL4.3 ALTER TABLE llx_c_shipment_mode CHANGE COLUMN rowid rowid INTEGER NOT NULL AUTO_INCREMENT;
+-- To make pk to be auto increment (postgres): VPGSQL8.2 NOT POSSIBLE. MUST DELETE/CREATE TABLE
+-- To set a field as NULL:                     VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name DROP NOT NULL;
+-- To set a field as default NULL:             VPGSQL8.2 ALTER TABLE llx_table ALTER COLUMN name SET DEFAULT NULL;
+-- Note: fields with type BLOB/TEXT can't have default value.
+-- -- VPGSQL8.2 DELETE FROM llx_usergroup_user      WHERE fk_user      NOT IN (SELECT rowid from llx_user);
+-- -- VMYSQL4.1 DELETE FROM llx_usergroup_user      WHERE fk_usergroup NOT IN (SELECT rowid from llx_usergroup);
+
+
+ALTER TABLE llx_societe ADD COLUMN fk_account integer;
+
+ALTER TABLE llx_website ADD COLUMN virtualhost varchar(255) after fk_default_home;
+
+ALTER TABLE llx_ecm_files ADD COLUMN gen_or_uploaded varchar(12) after cover; 
+
+DROP TABLE llx_document_generator;
+DROP TABLE llx_ecm_documents;
+
+
+

+ 2 - 1
htdocs/install/mysql/tables/llx_ecm_files.sql

@@ -27,7 +27,8 @@ CREATE TABLE llx_ecm_files
   description		text,
   keywords          text,                           -- list of keywords, separated with comma
   cover             text,                           -- is this file a file to use for a cover
-  extraparams		varchar(255),					-- for stock other parameters with json format
+  gen_or_uploaded   varchar(12),                    -- 'generated' or 'uploaded' 
+  extraparams		varchar(255),					-- for stocking other parameters with json format
   date_c			datetime,
   date_m			timestamp,
   fk_user_c			integer,

+ 1 - 0
htdocs/install/mysql/tables/llx_societe.sql

@@ -47,6 +47,7 @@ create table llx_societe
   town                     varchar(50),                         		-- town
   fk_departement           integer        DEFAULT 0,            		--
   fk_pays                  integer        DEFAULT 0,            		--
+  fk_account               integer        DEFAULT 0,            		--
   phone                    varchar(20),                         		-- phone number
   fax                      varchar(20),                         		-- fax number
   url                      varchar(255),                        		--

+ 1 - 0
htdocs/install/mysql/tables/llx_website.sql

@@ -25,6 +25,7 @@ CREATE TABLE llx_website
 	description   varchar(255),
 	status		  integer,
 	fk_default_home integer, 
+	virtualhost   varchar(255), 
     date_creation     datetime,
     date_modification datetime,
 	tms           timestamp

+ 2 - 0
htdocs/langs/en_US/website.lang

@@ -21,3 +21,5 @@ ViewSiteInNewTab=View site in new tab
 ViewPageInNewTab=View page in new tab
 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 <strong>%s</strong>, define here the virtual hostname so the preview will be done using this direct access instead of Dolibarr URLs wrapper.

+ 1 - 1
htdocs/main.inc.php

@@ -1285,7 +1285,7 @@ function top_htmlhead($head, $title='', $disablejs=0, $disablehead=0, $arrayofjs
                 }
                 print '<script type="text/javascript">';
                 print 'var CKEDITOR_BASEPATH = \''.$pathckeditor.'\';'."\n";
-                print 'var ckeditorConfig = \''.dol_buildpath($themesubdir.'/theme/'.$conf->theme.'/ckeditor/config.js',1).'\';'."\n";		// $themesubdir='' in standard usage
+                print 'var ckeditorConfig = \''.dol_buildpath($themesubdir.'/theme/'.$conf->theme.'/ckeditor/config.js'.($ext?'?'.$ext:''),1).'\';'."\n";		// $themesubdir='' in standard usage
                 print 'var ckeditorFilebrowserBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
                 print 'var ckeditorFilebrowserImageBrowseUrl = \''.DOL_URL_ROOT.'/core/filemanagerdol/browser/default/browser.php?Type=Image&Connector='.DOL_URL_ROOT.'/core/filemanagerdol/connectors/php/connector.php\';'."\n";
                 print '</script>'."\n";

+ 14 - 2
htdocs/projet/element.php

@@ -95,6 +95,8 @@ if ($user->societe_id > 0) $socid=$user->societe_id;
 $result = restrictedArea($user, 'projet', $projectid, 'projet&project');
 
 
+$hookmanager->initHooks(array('projectOverview'));
+
 /*
  *	View
  */
@@ -350,6 +352,15 @@ $listofreferent=array(
 	'test'=>$conf->projet->enabled && $user->rights->projet->lire && $conf->salaries->enabled && empty($conf->global->PROJECT_HIDE_TASKS)),
 );
 
+$parameters=array('listofreferent'=>$listofreferent);
+$resHook = $hookmanager->executeHooks('completeListOfReferent',$parameters,$object,$action);
+
+if(!empty($hookmanager->resArray)) {
+	
+	$listofreferent = array_merge($listofreferent, $hookmanager->resArray);
+	
+}
+
 if ($action=="addelement")
 {
 	$tablename = GETPOST("tablename");
@@ -438,6 +449,7 @@ foreach ($listofreferent as $key => $value)
 		$element = new $classname($db);
 
 		$elementarray = $object->get_element_list($key, $tablename, $datefieldname, $dates, $datee);
+		
 		if (count($elementarray)>0 && is_array($elementarray))
 		{
 			$total_ht = 0;
@@ -453,7 +465,7 @@ foreach ($listofreferent as $key => $value)
 				$element->fetch($idofelement);
 				if ($idofelementuser) $elementuser->fetch($idofelementuser);
 
-				if ($tablename != 'expensereport_det') $element->fetch_thirdparty();
+				if ($tablename != 'expensereport_det' && method_exists($element, 'fetch_thirdparty')) $element->fetch_thirdparty();
 
 				if ($tablename == 'don') $total_ht_by_line=$element->amount;
 				elseif ($tablename == 'projet_task')
@@ -679,7 +691,7 @@ foreach ($listofreferent as $key => $value)
 
 				if ($tablename != 'expensereport_det')
 				{
-					$element->fetch_thirdparty();
+					if(method_exists($element, 'fetch_thirdparty')) $element->fetch_thirdparty();
 				}
 				else
 				{

+ 3 - 2
htdocs/societe/class/societe.class.php

@@ -1025,7 +1025,7 @@ class Societe extends CommonObject
         $sql .= ', s.fk_forme_juridique as forme_juridique_code';
         $sql .= ', s.webservices_url, s.webservices_key';
         $sql .= ', s.code_client, s.code_fournisseur, s.code_compta, s.code_compta_fournisseur, s.parent, s.barcode';
-        $sql .= ', s.fk_departement, s.fk_pays as country_id, s.fk_stcomm, s.remise_client, s.mode_reglement, s.cond_reglement, s.tva_assuj';
+        $sql .= ', s.fk_departement, s.fk_pays as country_id, s.fk_stcomm, s.remise_client, s.mode_reglement, s.cond_reglement, s.fk_account, s.tva_assuj';
         $sql .= ', s.mode_reglement_supplier, s.cond_reglement_supplier, s.localtax1_assuj, s.localtax1_value, s.localtax2_assuj, s.localtax2_value, s.fk_prospectlevel, s.default_lang, s.logo';
         $sql .= ', s.fk_shipping_method';
         $sql .= ', s.outstanding_limit, s.import_key, s.canvas, s.fk_incoterms, s.location_incoterms';
@@ -1157,7 +1157,8 @@ class Societe extends CommonObject
                 $this->mode_reglement_supplier_id 	= $obj->mode_reglement_supplier;
                 $this->cond_reglement_supplier_id 	= $obj->cond_reglement_supplier;
                 $this->shipping_method_id   = ($obj->fk_shipping_method>0)?$obj->fk_shipping_method:null;
-
+				$this->fk_account			= $obj->fk_account;
+				
                 $this->client      = $obj->client;
                 $this->fournisseur = $obj->fournisseur;
 

+ 3 - 1
htdocs/theme/eldy/ckeditor/config.js

@@ -28,7 +28,9 @@ CKEDITOR.editorConfig = function( config )
 		
 	config.toolbar_Full =
 	[
-	    ['Source','-','Save','NewPage','Preview','-','Templates'],
+	    ['Templates','NewPage'],
+	    ['Save'],
+	    ['Source','Maximize','Preview'],
 	    ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],
 	    ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
 	    ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'],

+ 6 - 0
htdocs/theme/eldy/style.css.php

@@ -603,6 +603,12 @@ div.myavailability {
 .minwidth300 { min-width: 300px; }
 .minwidth400 { min-width: 400px; }
 .minwidth500 { min-width: 500px; }
+.minwidth50imp  { min-width: 50px !important; }
+.minwidth100imp { min-width: 100px !important; }
+.minwidth200imp { min-width: 200px !important; }
+.minwidth300imp { min-width: 300px !important; }
+.minwidth400imp { min-width: 400px !important; }
+.minwidth500imp { min-width: 500px !important; }
 .maxwidth100 { max-width: 100px; }
 .maxwidth150 { max-width: 150px; }
 .maxwidth200 { max-width: 200px; }

+ 3 - 1
htdocs/theme/md/ckeditor/config.js

@@ -28,7 +28,9 @@ CKEDITOR.editorConfig = function( config )
 		
 	config.toolbar_Full =
 	[
-	    ['Source','-','Save','NewPage','Preview','-','Templates'],
+	    ['Templates','NewPage'],
+	    ['Save'],
+	    ['Source','Maximize','Preview'],
 	    ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print', 'SpellChecker', 'Scayt'],
 	    ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
 	    ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField'],

TEMPAT SAMPAH
htdocs/theme/md/img/info.png


TEMPAT SAMPAH
htdocs/theme/md/img/title_accountancy.png


TEMPAT SAMPAH
htdocs/theme/md/img/title_products.png


+ 9 - 2
htdocs/theme/md/style.css.php

@@ -601,6 +601,12 @@ div.myavailability {
 .minwidth300 { min-width: 300px; }
 .minwidth400 { min-width: 400px; }
 .minwidth500 { min-width: 500px; }
+.minwidth50imp  { min-width: 50px !important; }
+.minwidth100imp { min-width: 100px !important; }
+.minwidth200imp { min-width: 200px !important; }
+.minwidth300imp { min-width: 300px !important; }
+.minwidth400imp { min-width: 400px !important; }
+.minwidth500imp { min-width: 500px !important; }
 .maxwidth100 { max-width: 100px; }
 .maxwidth150 { max-width: 150px; }
 .maxwidth200 { max-width: 200px; }
@@ -746,7 +752,7 @@ td.showDragHandle {
 	margin-left: 0;
 }
 div.login_block {
-	border-right: none ! important; 
+	/* border-right: none ! important; */
 	top: inherit !important;
 }
 .side-nav {
@@ -771,6 +777,7 @@ div.login_block {
 #id-left {
 	z-index: 201;
 	background: #FFF;
+	border-right: 1px solid rgba(0,0,0,0.3);
 <?php if ((GETPOST('testmenuhider') || ! empty($conf->global->MAIN_TESTMENUHIDER)) && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { ?>
 	top: 50px ! important;
 <?php } else { ?>
@@ -2664,7 +2671,7 @@ div.tabBar .noborder {
 <?php } ?>
 }
 span.boxstatstext {
-	opacity: 0.9;
+	/* opacity: 0.9;  Disabled. This make text on top of left menu in smartphone size */
     line-height: 18px;
 }
 span.boxstatsindicator {

+ 20 - 2
htdocs/websites/class/website.class.php

@@ -85,6 +85,12 @@ class Website extends CommonObject
 	 * @var integer
 	 */
 	public $fk_default_home;
+	/**
+	 * @var string
+	 */
+	public $virtualhost;
+	
+	
 	public $records;
 	
 	/**
@@ -143,7 +149,8 @@ class Website extends CommonObject
 		$sql.= 'ref,';
 		$sql.= 'description,';
 		$sql.= 'status,';
-        $sql.= 'fk_default_home,';
+		$sql.= 'fk_default_home,';
+		$sql.= 'virtualhost,';
 		$sql.= 'date_creation,';
 		$sql.= 'date_modification';
 		
@@ -154,6 +161,7 @@ class Website extends CommonObject
 		$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)."'");
 
@@ -214,6 +222,7 @@ class Website extends CommonObject
 		$sql .= " t.description,";
 		$sql .= " t.status,";
 		$sql .= " t.fk_default_home,";
+		$sql .= " t.virtualhost,";
 		$sql .= " t.date_creation,";
 		$sql .= " t.date_modification,";
 		$sql .= " t.tms";
@@ -237,6 +246,7 @@ class Website extends CommonObject
 				$this->description = $obj->description;
 				$this->status = $obj->status;
 				$this->fk_default_home = $obj->fk_default_home;
+				$this->virtualhost = $obj->virtualhost;
 				$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);
@@ -281,7 +291,8 @@ class Website extends CommonObject
 		$sql .= " t.ref,";
 		$sql .= " t.description,";
 		$sql .= " t.status,";
-		$sql .= " t.fk_default_home,"; 
+		$sql .= " t.fk_default_home,";
+		$sql .= " t.virtualhost,";
 		$sql .= " t.date_creation,";
 		$sql .= " t.date_modification,";
 		$sql .= " t.tms";
@@ -321,6 +332,7 @@ class Website extends CommonObject
 				$line->description = $obj->description;
 				$line->status = $obj->status;
 				$line->fk_default_home = $obj->fk_default_home;
+				$line->virtualhost = $obj->virtualhost;
 				$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);
@@ -380,6 +392,7 @@ class Website extends CommonObject
 		$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->virtualhost:"null").',';
 		$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())."'");
@@ -627,6 +640,7 @@ class Website extends CommonObject
 		$this->description = 'A specimen website';
 		$this->status = '';
 		$this->fk_default_home = null;
+		$this->virtualhost = 'http://myvirtualhost';
 		$this->date_creation = dol_now();
 		$this->date_modification = dol_now();
 		$this->tms = dol_now();
@@ -665,6 +679,10 @@ class WebsiteLine
 	 * @var int
 	 */
 	public $fk_default_home;
+	/**
+	 * @var string
+	 */
+	public $virtualhost;
 	/**
 	 * @var mixed
 	 */

+ 52 - 7
htdocs/websites/index.php

@@ -454,6 +454,8 @@ print '<div class="centpercent websitebar">';
 
 if (count($object->records) > 0)
 {
+    // ***** Part for web sites
+    
     print '<div class="websiteselection">';
     print $langs->trans("Website").': ';
     print '</div>';
@@ -478,15 +480,56 @@ if (count($object->records) > 0)
     }
     $out.='</select>';
     print $out;
-    print '<input type="submit" class="button" name="refreshsite" value="'.$langs->trans("Refresh").'">';
+    print '<input type="submit" class="button" name="refreshsite" value="'.$langs->trans("Load").'">';
 
     if ($website)
     {
-        print ' - '.$langs->trans("RealURL").' ';
         $realurl=$urlwithroot.'/public/websites/index.php?website='.$website;
+        $dataroot=DOL_DATA_ROOT.'/websites/'.$website;
+        if (! empty($object->virtualhost)) $realurl=$object->virtualhost; 
+        // TODO If virtual url defined, we use it
+        
+        /*print ' - '.$langs->trans("RealURL").' ';
         print '<input type="text" name="realurl" value="'.$realurl.'"> ';
-        print '<a href="'.DOL_URL_ROOT.'/public/websites/index.php?website='.$website.'" target="tab'.$website.'">'.$langs->trans("ViewSiteInNewTab").'</a>';
+        print '<a href="'.DOL_URL_ROOT.'/public/websites/index.php?website='.$website.'" target="tab'.$website.'">'.$langs->trans("ViewSiteInNewTab").'</a>';*/
+
+        print ' &nbsp; ';
+
+        print $langs->trans("ViewWebsiteInProduction").': ';
+        print '<input type="text" id="previewsiteurl" class="minwidth200imp" name="previewsite" value="'.$realurl.'">';
+        //print '<input type="submit" class="button" name="previewwebsite" target="tab'.$website.'" value="'.$langs->trans("ViewSiteInNewTab").'">';
+        $htmltext=$langs->trans("SetHereVirtualHost", $dataroot);
+        print $form->textwithpicto('', $htmltext);
+        print '<a class="button" id="previewsite" href="'.DOL_URL_ROOT.'/public/websites/index.php?website='.$website.'" target="tab'.$website.'">'.$langs->trans("ViewSiteInNewTab").'</a>';
+        
+        // Example : Adding jquery code
+        if (! empty($conf->use_javascript_ajax))
+        {
+            print '<script type="text/javascript" language="javascript">
+            jQuery(document).ready(function() {
+            	jQuery("#previewsite").click(function() {
+                    newurl=jQuery("#previewsiteurl").val();
+                    console.log("Open url "+newurl);
+                    /* Save url */
+                    jQuery.ajax({
+                        method: "POST",
+                        url: "'.DOL_URL_ROOT.'/core/ajax/saveinplace.php",
+                        data: {
+                            field: \'editval_virtualhost\',
+                            element: \'websites\',
+                            table_element: \'website\',
+                            fk_element: '.$object->id.',
+                            value: newurl,
+                        },
+                        context: document.body
+                    });
+                    $(this).attr("href",newurl);
+                });
+            });
+            </script>';
+        }
     }
+    
     print '</div>';
 
     // Button for websites
@@ -513,7 +556,8 @@ if (count($object->records) > 0)
     print '</div>';
 
 
-    // Part for pages
+    // ***** Part for pages
+    
     if ($website)
     {
         print '</div>';
@@ -555,7 +599,7 @@ if (count($object->records) > 0)
         else $out.='<option value="-1">&nbsp;</option>';
         $out.='</select>';
         print $out;
-        print '<input type="submit" class="button" name="refreshpage" value="'.$langs->trans("Refresh").'"'.($atleastonepage?'':' disabled="disabled"').'>';
+        print '<input type="submit" class="button" name="refreshpage" value="'.$langs->trans("Load").'"'.($atleastonepage?'':' disabled="disabled"').'>';
         print '<input type="submit" class="buttonDelete" name="delete" value="'.$langs->trans("Delete").'"'.($atleastonepage?'':' disabled="disabled"').'>';
         //print $form->selectarray('page', $array);
         
@@ -563,8 +607,9 @@ if (count($object->records) > 0)
         {
             print ' - '.$langs->trans("RealURL").' ';
             $realurl=$urlwithroot.'/public/websites/index.php?website='.$website.'&page='.$pageid;
-            print '<input type="text" name="realurl" value="'.$realurl.'"> ';
-            print '<a href="'.$realurl.'" target="tab'.$website.'">'.$langs->trans("ViewPageInNewTab").'</a>';
+            print '<input type="text" name="realurl" class="minwidth200imp" disabled="disabled" value="'.$realurl.'"> ';
+            print '<a href="'.$realurl.'" class="button" target="tab'.$website.'">'.$langs->trans("ViewPageInNewTab").'</a>';
+            //print '<input type="submit" class="button" name="previewpage" target="tab'.$website.'"value="'.$langs->trans("ViewPageInNewTab").'">';
         }
         
         print '</div>';

+ 88 - 0
test/phpunit/ExportTest.php

@@ -120,9 +120,96 @@ class ExportTest extends PHPUnit_Framework_TestCase
     }
 
 
+    /**
+     * Other tests
+     *
+     * @return void
+     */
+    public function testExportOther()
+    {
+        global $conf,$user,$langs,$db;
+    
+        $model='csv';
+    
+        // Creation of class to export using model ExportXXX
+        $dir = DOL_DOCUMENT_ROOT . "/core/modules/export/";
+        $file = "export_".$model.".modules.php";
+        $classname = "Export".$model;
+        require_once $dir.$file;
+        $objmodel = new $classname($this->db);
+    
+        // First test without option USE_STRICT_CSV_RULES
+        unset($conf->global->USE_STRICT_CSV_RULES);
+        
+        $valtotest='A simple string';
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, 'A simple string');
+        
+        $valtotest='A string with , and ; inside';
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, '"A string with , and ; inside"');
+        
+        $valtotest='A string with " inside';
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, '"A string with "" inside"');
+
+        $valtotest='A string with " inside and '."\r\n".' carriage returns';
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, '"A string with "" inside and \n carriage returns"');
+        
+        $valtotest='A string with <a href="aaa"><strong>html<br>content</strong></a> inside<br>'."\n";
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, '"A string with <a href=""aaa""><strong>html<br>content</strong></a> inside"');
+        
+        // Same tests with strict mode
+        $conf->global->USE_STRICT_CSV_RULES=1;
+        
+        $valtotest='A simple string';
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, 'A simple string');
+        
+        $valtotest='A string with , and ; inside';
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, '"A string with , and ; inside"');
+        
+        $valtotest='A string with " inside';
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, '"A string with "" inside"');
+        
+        $valtotest='A string with " inside and '."\r\n".' carriage returns';
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, "\"A string with \"\" inside and \r\n carriage returns\"");
+        
+        $valtotest='A string with <a href="aaa"><strong>html<br>content</strong></a> inside<br>'."\n";
+        print __METHOD__." valtotest=".$valtotest."\n";
+        $result = $objmodel->csvClean($valtotest, $langs->charset_output);
+        print __METHOD__." result=".$result."\n";
+        $this->assertEquals($result, '"A string with <a href=""aaa""><strong>html<br>content</strong></a> inside"');
+        
+    }
+    
     /**
      * Test export function for a personalized dataset
      *
+     * @depends	testExportOther
 	 * @return void
      */
     public function testExportPersonalizedExport()
@@ -266,4 +353,5 @@ class ExportTest extends PHPUnit_Framework_TestCase
 
         return true;
     }
+    
 }

+ 32 - 27
test/phpunit/WebservicesInvoicesTest.php

@@ -54,7 +54,8 @@ class WebservicesInvoicesTest extends PHPUnit_Framework_TestCase
 	protected $savlangs;
 	protected $savdb;
 	protected $soapclient;
-	protected $socid;
+	
+	private static $socid;
 	
 	protected $ns = 'http://www.dolibarr.org/ns/';
 	
@@ -72,10 +73,9 @@ class WebservicesInvoicesTest extends PHPUnit_Framework_TestCase
 		$this->savuser=$user;
 		$this->savlangs=$langs;
 		$this->savdb=$db;
-		$WS_DOL_URL = DOL_MAIN_URL_ROOT.'/webservices/server_invoice.php';
-		
 		
 		// Set the WebService URL
+		$WS_DOL_URL = DOL_MAIN_URL_ROOT.'/webservices/server_invoice.php';
 		print __METHOD__." create nusoap_client for URL=".$WS_DOL_URL."\n";
 		$this->soapclient = new nusoap_client($WS_DOL_URL);
 		if ($this->soapclient)
@@ -84,7 +84,28 @@ class WebservicesInvoicesTest extends PHPUnit_Framework_TestCase
 			$this->soapclient->decodeUTF8(false);
 		}
 		
-		// create third_parties, needed to test an invoice
+		print __METHOD__." db->type=".$db->type." user->id=".$user->id;
+		//print " - db ".$db->db;
+		print "\n";
+	}
+
+    public static function setUpBeforeClass()
+    {
+        global $conf,$user,$langs,$db;
+
+		// create a third_party, needed to create an invoice
+		//
+		// The third party is created in setUpBeforeClass() and not in the
+		// constructor to avoid creating several objects (the constructor is
+		// called for each test).
+		//
+		// The third party must be created before beginning the DB transaction
+		// because there is a foreign key constraint between invoices and third
+		// parties (tables: lx_facture and llx_societe) and with MySQL,
+		// constraints are checked immediately, they are not deferred to
+		// transaction commit. So if the invoice is created in the same
+		// transaction than the third party, the FK constraint fails.
+		// See this post for more detail: http://stackoverflow.com/a/5014744/5187108
 		$societe=new Societe($db);
 		$societe->ref='';
 		$societe->name='name';
@@ -97,24 +118,15 @@ class WebservicesInvoicesTest extends PHPUnit_Framework_TestCase
 		$societe->particulier=0;
 		
 		$societe->create($user);
-		
-		$this->socid = $societe->id;
-		
-		print __METHOD__." societe created id=".$societe->id."\n";
 
-		print __METHOD__." db->type=".$db->type." user->id=".$user->id;
-		//print " - db ".$db->db;
-		print "\n";
-	}
+		self::$socid = $societe->id;
+		print __METHOD__." societe created id=".$societe->id."\n";
 
-	// Static methods
-  	public static function setUpBeforeClass()
-    {
-    	global $conf,$user,$langs,$db;
 		$db->begin();	// This is to have all actions inside a transaction even if test launched without suite.
 
-    	print __METHOD__."\n";
+        print __METHOD__."\n";
     }
+
     public static function tearDownAfterClass()
     {
     	global $conf,$user,$langs,$db;
@@ -135,9 +147,8 @@ class WebservicesInvoicesTest extends PHPUnit_Framework_TestCase
 		$user=$this->savuser;
 		$langs=$this->savlangs;
 		$db=$this->savdb;
-				
+
 		print __METHOD__."\n";
-		
     }
 
 	/**
@@ -166,17 +177,11 @@ class WebservicesInvoicesTest extends PHPUnit_Framework_TestCase
 
     	$WS_METHOD  = 'createInvoice';
 
-    	// load societe first
-    	/*$societe=new Societe($db);
-    	$societe->fetch('', '', 'ref-phpunit');
-    	print __METHOD__." societe loaded id=".$societe->id."\n";
-        */
-    	
     	$body = array (
     			"id" => NULL,
 				"ref" => NULL,
 				"ref_ext" => "ref-phpunit-2",
-				"thirdparty_id" => $this->socid,
+				"thirdparty_id" => self::$socid,
 				"fk_user_author" => NULL,
 				"fk_user_valid" => NULL,
 				"date" => "2015-04-19 20:16:53",
@@ -333,7 +338,7 @@ class WebservicesInvoicesTest extends PHPUnit_Framework_TestCase
     		"id" => NULL,
 			"ref" => NULL,
 			"ref_ext" => "ref-phpunit-2",
-			"thirdparty_id" => $this->socid,
+			"thirdparty_id" => self::$socid,
 			"fk_user_author" => NULL,
 			"fk_user_valid" => NULL,
 			"date" => "2015-04-19 20:16:53",

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini