Browse Source

Merge branch '9.0' of github.com:Dolibarr/dolibarr into 9.0

Laurent Destailleur 6 years ago
parent
commit
c95b915db3
100 changed files with 3301 additions and 1010 deletions
  1. 32 18
      .travis.yml
  2. 101 4
      ChangeLog
  3. 3 3
      README.md
  4. 7 8
      build/README
  5. 1 1
      build/exe/doliwamp/README
  6. 1 1
      build/exe/doliwamp/doliwamp.iss
  7. 3 2
      build/makepack-dolibarr.pl
  8. 2 2
      build/makepack-howto.txt
  9. 1 0
      build/travis-ci/apache.conf
  10. 0 226
      dev/initdemo/mysqldump_dolibarr_9.0.0.sql
  11. 5 0
      dev/initdemo/savedemo.sh
  12. 0 12
      dev/resources/dbmodel/dolibarr.uml
  13. 0 53
      dev/resources/dbmodel/dolibarr.umldi
  14. BIN
      dev/resources/dbmodel/dolibarr_schema.mwb
  15. 16 1
      dev/setup/codesniffer/ruleset.xml
  16. 3 3
      dev/tools/fixdosfiles.sh
  17. 0 1
      htdocs/accountancy/bookkeeping/list.php
  18. 3 0
      htdocs/accountancy/class/accountancyexport.class.php
  19. 19 19
      htdocs/accountancy/class/accountingaccount.class.php
  20. 5 1
      htdocs/accountancy/class/bookkeeping.class.php
  21. 89 46
      htdocs/accountancy/journal/bankjournal.php
  22. 4 4
      htdocs/adherents/card.php
  23. 20 17
      htdocs/adherents/class/adherent.class.php
  24. 44 3
      htdocs/adherents/list.php
  25. 20 2
      htdocs/adherents/subscription.php
  26. 396 184
      htdocs/adherents/subscription/list.php
  27. 3 3
      htdocs/adherents/type.php
  28. 2 1
      htdocs/admin/company.php
  29. 20 2
      htdocs/admin/dav.php
  30. 4 2
      htdocs/admin/defaultvalues.php
  31. 11 11
      htdocs/admin/dict.php
  32. 1 1
      htdocs/admin/dolistore/class/dolistore.class.php
  33. 24 5
      htdocs/admin/emailcollector_card.php
  34. 52 42
      htdocs/admin/emailcollector_list.php
  35. 53 51
      htdocs/admin/mails_templates.php
  36. 46 21
      htdocs/admin/menus/edit.php
  37. 0 1
      htdocs/admin/system/filecheck.php
  38. 2 0
      htdocs/asset/class/asset.class.php
  39. 37 1
      htdocs/blockedlog/class/blockedlog.class.php
  40. 30 19
      htdocs/cashdesk/admin/cashdesk.php
  41. 1 2
      htdocs/cashdesk/class/Facturation.class.php
  42. 29 0
      htdocs/cashdesk/index.php
  43. 1 2
      htdocs/cashdesk/tpl/liste_articles.tpl.php
  44. 2 2
      htdocs/comm/action/card.php
  45. 2 3
      htdocs/comm/action/class/actioncomm.class.php
  46. 13 0
      htdocs/comm/action/list.php
  47. 2 2
      htdocs/comm/card.php
  48. 4 2
      htdocs/comm/mailing/cibles.php
  49. 1 3
      htdocs/comm/mailing/class/mailing.class.php
  50. 1 1
      htdocs/comm/propal/card.php
  51. 38 32
      htdocs/comm/propal/class/propal.class.php
  52. 1 1
      htdocs/comm/propal/list.php
  53. 1 1
      htdocs/commande/card.php
  54. 1 2
      htdocs/commande/class/commande.class.php
  55. 1 1
      htdocs/compta/bank/class/account.class.php
  56. 1 2
      htdocs/compta/bank/class/bankcateg.class.php
  57. 14 4
      htdocs/compta/bank/ligne.php
  58. 38 39
      htdocs/compta/bank/treso.php
  59. 575 0
      htdocs/compta/cashcontrol/cashcontrol_card.php
  60. 573 0
      htdocs/compta/cashcontrol/cashcontrol_list.php
  61. 429 0
      htdocs/compta/cashcontrol/class/cashcontrol.class.php
  62. 286 0
      htdocs/compta/cashcontrol/report.php
  63. 3 3
      htdocs/compta/facture/card.php
  64. 1 1
      htdocs/compta/facture/class/api_invoices.class.php
  65. 1 4
      htdocs/compta/facture/class/facture.class.php
  66. 3 4
      htdocs/compta/facture/class/paymentterm.class.php
  67. 2 2
      htdocs/compta/facture/fiche-rec.php
  68. 3 3
      htdocs/compta/facture/list.php
  69. 2 2
      htdocs/compta/facture/tpl/linkedobjectblock.tpl.php
  70. 21 11
      htdocs/compta/facture/tpl/linkedobjectblockForRec.tpl.php
  71. 6 4
      htdocs/compta/paiement/card.php
  72. 40 6
      htdocs/compta/paiement/class/paiement.class.php
  73. 1 0
      htdocs/compta/prelevement/class/bonprelevement.class.php
  74. 4 1
      htdocs/compta/sociales/class/cchargesociales.class.php
  75. 2 3
      htdocs/compta/sociales/class/paymentsocialcontribution.class.php
  76. 1 1
      htdocs/compta/sociales/list.php
  77. 3 3
      htdocs/compta/tva/index.php
  78. 1 1
      htdocs/compta/tva/list.php
  79. 14 19
      htdocs/contact/card.php
  80. 1 1
      htdocs/contact/list.php
  81. 2 3
      htdocs/contrat/class/contrat.class.php
  82. 8 1
      htdocs/core/actions_addupdatedelete.inc.php
  83. 5 2
      htdocs/core/actions_linkedfiles.inc.php
  84. 21 7
      htdocs/core/actions_massactions.inc.php
  85. 2 1
      htdocs/core/actions_sendmails.inc.php
  86. 3 3
      htdocs/core/boxes/box_produits.php
  87. 2 2
      htdocs/core/boxes/box_services_contracts.php
  88. 4 5
      htdocs/core/class/commonobject.class.php
  89. 4 1
      htdocs/core/class/ctyperesource.class.php
  90. 2 0
      htdocs/core/class/emailsenderprofile.class.php
  91. 2 2
      htdocs/core/class/events.class.php
  92. 40 24
      htdocs/core/class/html.form.class.php
  93. 1 1
      htdocs/core/class/html.formactions.class.php
  94. 2 2
      htdocs/core/class/html.formcompany.class.php
  95. 4 2
      htdocs/core/class/html.formfile.class.php
  96. 7 7
      htdocs/core/class/html.formprojet.class.php
  97. 3 1
      htdocs/core/class/interfaces.class.php
  98. 0 1
      htdocs/core/class/menubase.class.php
  99. 4 4
      htdocs/core/class/stats.class.php
  100. 2 2
      htdocs/core/js/lib_foot.js.php

+ 32 - 18
.travis.yml

@@ -37,6 +37,7 @@ php:
 - '7.0'
 - '7.1'
 - '7.2'
+- '7.3'
 - nightly
 
 env:
@@ -59,6 +60,7 @@ matrix:
   fast_finish: true
   allow_failures:
   - php: nightly
+  - php: '7.3'
   # We exclude some combinations not usefull to save Travis CPU
   exclude:
   - php: '5.5'
@@ -77,6 +79,8 @@ matrix:
     env: DB=postgresql
   - php: '7.1'
     env: DB=postgresql
+  - php: '7.3'
+    env: DB=postgresql
   - php: nightly
     env: DB=postgresql
 
@@ -130,7 +134,7 @@ install:
   if [ "$TRAVIS_PHP_VERSION" = '5.6' ] || [ "$TRAVIS_PHP_VERSION" = '7.0' ] || [ "$TRAVIS_PHP_VERSION" = '7.1' ]; then
     composer -n require phpunit/phpunit ^5
   fi
-  if [ "$TRAVIS_PHP_VERSION" = '7.2' ] || [ "$TRAVIS_PHP_VERSION" = 'nightly' ]; then
+  if [ "$TRAVIS_PHP_VERSION" = '7.2' ] || [ "$TRAVIS_PHP_VERSION" = '7.3' ] || [ "$TRAVIS_PHP_VERSION" = 'nightly' ]; then
     composer -n require phpunit/phpunit ^5
   fi
   echo
@@ -165,13 +169,9 @@ before_script:
     echo "Set timezone"
     echo 'date.timezone = "Europe/Paris"' >> ~/.phpenv/versions/$PHP_VERSION_NAME/etc/php.ini
     if [ "$TRAVIS_PHP_VERSION" = '5.4' ]; then
-      #echo
-      #echo "Enabling APC for PHP <= 5.4"
-      #   Documentation says it should be available for PHP <= 5.6 but it's not for 5.5 and 5.6!
-      #echo 'extension = apc.so' >> ~/.phpenv/versions/$PHP_VERSION_NAME/etc/php.ini
+      # Documentation says it should be available for all PHP versions but it's not for 5.5 and 5.6, 7.0, 7.1, 7.2 and nightly!
       echo
       echo "Enabling Memcached for PHP <= 5.4"
-      #   Documentation says it should be available for all PHP versions but it's not for 5.5 and 5.6, 7.0, 7.1, 7.2 and nightly!
       echo 'extension = memcached.so' >> ~/.phpenv/versions/$PHP_VERSION_NAME/etc/php.ini
     fi
     phpenv rehash
@@ -211,8 +211,14 @@ before_script:
       mysql -D travis < dev/initdemo/mysqldump_dolibarr_3.5.0.sql
     fi
     if [ "$DB" = 'postgresql' ]; then
-      #pgloader mysql://root:pass@127.0.0.1/dolibarr_35 postgresql://dolibarrowner:dolibarrownerpass@127.0.0.1/dolibarr_dev
+      #pgloader mysql://root:pass@127.0.0.1/dolibarr_9 postgresql://dolibarrowner:dolibarrownerpass@127.0.0.1/dolibarr_dev
+      echo pgloader mysql://root@127.0.0.1/travis postgresql:///travis
       pgloader mysql://root@127.0.0.1/travis postgresql:///travis
+      echo 'ALTER SEQUENCE llx_accountingaccount_rowid_seq RENAME TO llx_accounting_account_rowid_seq' | psql travis
+      echo 'ALTER SEQUENCE llx_accounting_account_rowid_seq RESTART WITH 1000001;' | psql travis
+      #echo 'select * from INFORMATION_SCHEMA.COLUMNS where table_name = 'llx_accountingaccount' | psql travis
+      #echo 'select * from information_schema.table_constraints;' | psql travis
+      #echo 'ALTER TABLE "llx_accounting_account" DROP CONSTRAINT "idx_16390_primary"' | psql travis
     fi
     # TODO: SQLite
     echo
@@ -229,9 +235,11 @@ before_script:
     echo '$'dolibarr_main_db_user=\'travis\'';' >> $CONF_FILE
     if [ "$DB" = 'mysql' ] || [ "$DB" = 'mariadb' ]; then
       echo '$'dolibarr_main_db_type=\'mysqli\'';' >> $CONF_FILE
+      echo '$'dolibarr_main_db_port=\'3306\'';' >> $CONF_FILE
     fi
     if [ "$DB" = 'postgresql' ]; then
       echo '$'dolibarr_main_db_type=\'pgsql\'';' >> $CONF_FILE
+      echo '$'dolibarr_main_db_port=\'5432\'';' >> $CONF_FILE
     fi
     # TODO: SQLite
     echo '$'dolibarr_main_authentication=\'dolibarr\'';' >> $CONF_FILE
@@ -253,7 +261,7 @@ before_script:
   # enable php-fpm
   - sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf
   - |
-    if [ "$TRAVIS_PHP_VERSION" = '7.0' ] || [ "$TRAVIS_PHP_VERSION" = '7.1' ] || [ "$TRAVIS_PHP_VERSION" = '7.2' ] || [ "$TRAVIS_PHP_VERSION" = 'nightly' ]; then
+    if [ "$TRAVIS_PHP_VERSION" = '7.0' ] || [ "$TRAVIS_PHP_VERSION" = '7.1' ] || [ "$TRAVIS_PHP_VERSION" = '7.2' ] || [ "$TRAVIS_PHP_VERSION" = '7.3' ] || [ "$TRAVIS_PHP_VERSION" = 'nightly' ]; then
       # Copy the included pool
       sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf
     fi
@@ -276,11 +284,12 @@ before_script:
 script:
 - |
   echo "Checking webserver availability by a wget -O - http://127.0.0.1"
-  # Ensure we catch errors
-  set -e
+  # Ensure we stop on error with set -e
+  set +e
   # The wget should return a page with line '<meta name="generator" content="Dolibarr installer">
   wget -O - http://127.0.0.1 > test.html
   head test.html
+  sudo cat /var/log/apache2/travis_error_log
   set +e
   echo
 
@@ -309,7 +318,7 @@ script:
 
 - |
   echo "Upgrading Dolibarr"
-  # Ensure we catch errors
+  # Ensure we catch errors. Set this to +e if you want to go to the end to see log files.
   set +e
   cd htdocs/install
   php upgrade.php 3.5.0 3.6.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade350360.log
@@ -325,7 +334,7 @@ script:
   php upgrade2.php 3.8.0 3.9.0 > $TRAVIS_BUILD_DIR/upgrade380390-2.log
   php step5.php 3.8.0 3.9.0 > $TRAVIS_BUILD_DIR/upgrade380390-3.log
   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 upgrade2.php 3.9.0 4.0.0 > $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 > $TRAVIS_BUILD_DIR/upgrade400500-2.log
@@ -334,18 +343,22 @@ script:
   php upgrade2.php 5.0.0 6.0.0 > $TRAVIS_BUILD_DIR/upgrade500600-2.log
   php step5.php 5.0.0 6.0.0 > $TRAVIS_BUILD_DIR/upgrade500600-3.log
   php upgrade.php 6.0.0 7.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade600700.log
-  php upgrade2.php 6.0.0 7.0.0 MAIN_MODULE_WEBSITE,MAIN_MODULE_SUPPLIERPROPOSAL > $TRAVIS_BUILD_DIR/upgrade600700-2.log
+  php upgrade2.php 6.0.0 7.0.0 > $TRAVIS_BUILD_DIR/upgrade600700-2.log
   php step5.php 6.0.0 7.0.0 > $TRAVIS_BUILD_DIR/upgrade600700-3.log
   php upgrade.php 7.0.0 8.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade700800.log
-  php upgrade2.php 7.0.0 8.0.0 MAIN_MODULE_TICKETSUP > $TRAVIS_BUILD_DIR/upgrade700800-2.log
+  php upgrade2.php 7.0.0 8.0.0 > $TRAVIS_BUILD_DIR/upgrade700800-2.log
   php step5.php 7.0.0 8.0.0 > $TRAVIS_BUILD_DIR/upgrade700800-3.log
   php upgrade.php 8.0.0 9.0.0 ignoredbversion > $TRAVIS_BUILD_DIR/upgrade800900.log
   php upgrade2.php 8.0.0 9.0.0 > $TRAVIS_BUILD_DIR/upgrade800900-2.log
   php step5.php 8.0.0 9.0.0 > $TRAVIS_BUILD_DIR/upgrade800900-3.log
+  # Enable modules not enabled into original dump
+  php upgrade2.php 0.0.0 0.0.0 MAIN_MODULE_API,MAIN_MODULE_SUPPLIERPROPOSAL,MAIN_MODULE_WEBSITE,MAIN_MODULE_TICKETSUP,MAIN_MODULE_ACCOUNTING > $TRAVIS_BUILD_DIR/enablemodule.log
+  echo $?
   cd -
   set +e
   echo
   #cat /tmp/dolibarr_install.log
+  cat $TRAVIS_BUILD_DIR/enablemodule.log
 
 - |
   echo "Unit testing"
@@ -358,9 +371,10 @@ script:
 
 after_script:
 - |
-  echo "After script - Output 50 latest lines of dolibarr.log"
+  echo "After script - Output lines of dolibarr.log"
   ls $TRAVIS_BUILD_DIR/documents
-  tail -n 50 $TRAVIS_BUILD_DIR/documents/dolibarr.log
+  #cat $TRAVIS_BUILD_DIR/documents/dolibarr.log
+  sudo tail -n 50 $TRAVIS_BUILD_DIR/documents/dolibarr.log
 
 after_success:
 - |
@@ -369,7 +383,7 @@ after_success:
 after_failure:
 - |
   echo Failure detected, so we show samples of log to help diagnose
-  # This part of code is executed only if previous commande that fails are enclosed with set +e
+  # This part of code is executed only if previous command that fails are enclosed with set +e
   # Upgrade log files
   for ficlog in `ls $TRAVIS_BUILD_DIR/*.log`
   do
@@ -378,7 +392,7 @@ after_failure:
   done
   # Apache log file
   echo "Debugging informations for file apache error.log"
-  sudo cat /var/log/apache2/error.log
+  sudo cat /var/log/apache2/travis_error_log
   if [ "$DEBUG" = true ]; then
     # Dolibarr log file
     echo "Debugging informations for file dolibarr.log (latest 50 lines)"

+ 101 - 4
ChangeLog

@@ -36,10 +36,8 @@ NEW: Add option PDF_DISABLE_MYCOMPANY_LOGO to disable logo on PDF
 NEW: add option PROPOSAL_AUTO_ADD_AUTHOR_AS_CONTACT
 NEW: Add option to display thirdparty adress in combolist
 NEW: Add option to swap sender/recipient address on PDF
-NEW: Add option to display thirdparty adress in combolist
-NEW: Add project on pament of salaries
+NEW: Add project on payment of salaries
 NEW: Add SHIPPING_PDF_HIDE_WEIGHT_AND_VOLUME and
-NEW: Add somes hooks in bank planned entries
 NEW: Add supplier ref in item reception page
 NEW: Advanced permission to ignore price min
 NEW: Allow to enter a timespent with a numeric value
@@ -80,11 +78,13 @@ NEW: tag odt line_product_ref_fourn for supplier doc lines
 NEW: The binding step in accountancy has a country filter with autocompletion
 NEW: Top menu is always on screen with MD theme.
 NEW: Withdraw request massaction can include already partially paid invoices
-
+NEW: Option "Simplify interface for blind persons"
+NEW: Generic cash fence feature (compatible with several POS modules)
 
 For developers:
 NEW: Add lib for multiselect with checkboxes
 NEW: Add function isValidMXRecord
+NEW: Add somes hooks in bank planned entries
 NEW: Add hook changeRoundingMode in update_price
 NEW: Add hook formconfirm to contractcard
 NEW: Add hook for virtual stock
@@ -121,6 +121,71 @@ Following changes may create regressions for some external modules, but were nec
 * Remove the no more used and deprecated dol_print_graph function
 
 
+***** ChangeLog for 8.0.4 compared to 8.0.3 *****
+FIX: #10030 better german chart
+FIX: #10036
+FIX: #10080 Supplier translations are in english
+FIX: #10183 using backport of fix done in 9.0
+FIX: #10218 Bad redirection after deleting a user or group
+FIX: #3234
+FIX: #6580
+FIX: #8741
+FIX: #9629 #9625
+FIX: #9971
+FIX: avoid Class 'AdherentType' not found
+FIX: Can relaunch install on v8
+FIX: Can't create a thirdparty from member if customer code is mandatory.
+FIX: Can't delete a line of minimal stock per warehouse
+FIX: check if "entity" is already defined in "$param"
+FIX: contact/address tab issue when changing company
+FIX: contact/adress tab: when changing company ajax combo, the first contact change is not taken into account
+FIX: CVE-2018-19799
+FIX: CVE-2018-19992
+FIX: CVE-2018-19993
+FIX: CVE-2018-19994
+FIX: CVE-2018-19995 and CVE-2018-19998
+FIX: Error reported when creation of thirdparty from member fails
+FIX: export only prices of the current entity !
+FIX: Extrafields on shipment module
+FIX: filter on product category doesn't work
+FIX: form actions: select_type_actions could be too small + bad $db init
+FIX: form actions: select_type_actions could be too small + bad  init
+FIX: fourn payment modes musn't be available on customer docs
+FIX: Function updatePrice with wrong parameters
+FIX: hidden extrafield
+FIX: if qty is 0
+FIX: If we change customer/supplier rule we can't edit old thirdparty.
+FIX: lang not loaded
+FIX: Lines are not inserted correctly if VAT have code
+FIX: marge sign
+FIX: Method setValid not found
+FIX: Migration do not create not used table
+FIX: missing action "edit" for the hook
+FIX: missing field "visible"
+FIX: Missing last month on vat report per month
+FIX: mode is only customer in stats fichinter
+FIX: OppStatusShort doesn't exists
+FIX: Remote ip detection was wrong with proxy (example: cloudflare)
+FIX: Removed not use table
+FIX: Replenishment with option STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE
+FIX: responsive
+FIX: Same on customer card
+FIX: same on lines
+FIX: screen size fall
+FIX: Select first mail model by default
+FIX: slow SQL query on creating a new supplier invoice
+FIX: sql query performance on list_qualified_avoir_supplier_invoices.
+FIX: supplier order list keep socid
+FIX: Same on customer card
+FIX: same on lines
+FIX: screen size fall
+FIX: Select first mail model by default
+FIX: slow SQL query on creating a new supplier invoice
+FIX: sql query performance on list_qualified_avoir_supplier_invoices.
+FIX: supplier order list keep socid
+FIX: Vendor translations are in english
+FIX: Warning: count()
+FIX: We want to be able to send PDF of paid invoices
 
 ***** ChangeLog for 8.0.3 compared to 8.0.2 *****
 FIX: #9161
@@ -459,6 +524,38 @@ Following changes may create regressions for some external modules, but were nec
   where {TYPE} is contact type code (BILLING, SHIPPING, CUSTOMER, ... see contact type dictionnary). 
 
 
+***** ChangeLog for 7.0.5 compared to 7.0.4 *****
+FIX: #3234
+FIX: #6580
+FIX: #8741
+FIX: #9934
+FIX: avoid Class 'AdherentType' not found
+FIX: Can't create a thirdparty from member if customer code is mandatory.
+FIX: Can't generate invoice pdf
+FIX: contact/adress tab: when changing company ajax combo, the first contact change is not taken into account
+FIX: Error generating ODT when option to use contact on doc on
+FIX: Error reported when creation of thirdparty from member fails
+FIX: filter on product category doesn't work
+FIX: form actions: select_type_actions could be too small + bad  init
+FIX: fourn payment modes musn't be available on customer docs
+FIX: Function updatePrice with wrong parameters
+FIX: If we change customer/supplier rule we can't edit old thirdparty.
+FIX: Interface regression for bind people. Fix option MAIN_OPTIMIZEFORTEXTBROWSER
+FIX: Lines are not inserted correctly if VAT have code
+FIX: OppStatusShort doesn't exists
+FIX: pdf typhon: order reference duplicate
+FIX: propal pdf: missing parenthesis for customs code
+FIX: Same on customer card
+FIX: same on lines
+FIX: Select first mail model by default
+FIX: sql query performance on list_qualified_avoir_supplier_invoices.
+FIX: task time screen: last fix was overkill
+FIX: task time screen: prevent users with access to all project from assigning to tasks they're not allowed to do
+FIX: use discount with multicurrency
+FIX: Variable name
+FIX: We want to be able to send PDF of paid invoices
+FIX: When delete a product, llx_product_association rows are not deleted
+FIX: wrong occurence number of contract on contact card, we must only count externals
 
 ***** ChangeLog for 7.0.4 compared to 7.0.3 *****
 FIX: #8984 button create expense report

+ 3 - 3
README.md

@@ -3,9 +3,9 @@
 ![Downloads per day](https://img.shields.io/sourceforge/dm/dolibarr.svg)
 [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
 
-|6|7|8|develop|
-|----------|----------|----------|----------|
-|![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/6.0.svg)|![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/7.0.svg)|![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/8.0.svg)|![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/develop.svg)|
+|6|7|8|9|develop|
+|----------|----------|----------|----------|----------|
+|![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/6.0.svg)|![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/7.0.svg)|![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/8.0.svg)|![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/9.0.svg)|![Build status](https://img.shields.io/travis/Dolibarr/dolibarr/develop.svg)|
 
 Dolibarr ERP & CRM is a modern software package to manage your organization's activity (contacts, suppliers, invoices, orders, stocks, agenda…).
 

+ 7 - 8
build/README

@@ -16,18 +16,20 @@ Note: Prerequisites to build tgz, debian, rpm package
 > apt-get install tar dpkg dpatch p7zip-full rpm zip
 
 Note: Prerequisites to build autoexe DoliWamp package:
+> apt-get install wine q4wine
+> Launch "wine cmd" to check a drive Z: pointing to / exists.
 > Install InnoSetup
    For example by running isetup-5.3.9.exe (http://www.jrsoftware.org)
 > Install WampServer into "C:\Program Files\Wamp"
-   For example by running wampserver2.2e-php5.4.3-httpd-2.4.2-mysql5.5.24-x64.exe (http://www.wampserver.com)
-> Install WampServer addon to have versions: Apache2.2.11, Mysql5.0.45, Php5.3.0
-   For example by running WampServer2-APACHE2211.exe (http://www.wampserver.com)
+   For example by running wampserver2.5-Apache-2.4.9-Mysql-5.6.17-php5.5.12-32b.exe (http://www.wampserver.com)
+> Install WampServer addon to have versions: Mysql5.0.45
    For example by running WampServer2-MYSQL5045.exe (http://www.wampserver.com)
-   For example by running WampServer2-PHP530.exe (http://www.wampserver.com)
 > To build from Windows (running from makepack-dolibarr.pl script is however
   recommanded), open file build/exe/doliwamp.iss and click on button "Compile".
   The .exe file will be build into directory build.
-
+> Add path to ISCC into PATH windows var:
+  Launch wine cmd, then regedit and add entry int HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment\PATH
+  
 - To build a theme package, launch the script
 > perl makepack-dolibarrtheme.pl
 
@@ -46,9 +48,6 @@ generated packages will not contains this "build" directory.
 
 We can find in "build", following sub-directories:
 
-* aps:
-To build APS package.
-
 * debian:
 To build Debian package.
 

+ 1 - 1
build/exe/doliwamp/README

@@ -5,4 +5,4 @@ DOLIWAMP Package tools
 
 This directory contains files used by makepack-dolibarr.pl
 script to build the all-in-on .EXE package DoliWamp, ready
-to be distributedt (for Windows).
+to be distributed (for Windows).

+ 1 - 1
build/exe/doliwamp/doliwamp.iss

@@ -32,7 +32,7 @@ AppPublisherURL=https://www.nltechno.com
 AppSupportURL=https://www.dolibarr.org
 AppUpdatesURL=https://www.dolibarr.org
 AppComments=DoliWamp includes Dolibarr, Apache, PHP and Mysql softwares.
-AppCopyright=Copyright (C) 2008-2018 Laurent Destailleur (NLTechno), Fabian Rodriguez (Le Goût du Libre)
+AppCopyright=Copyright (C) 2008-2019 Laurent Destailleur (NLTechno), Fabian Rodriguez (Le Goût du Libre)
 DefaultDirName=c:\dolibarr
 DefaultGroupName=Dolibarr
 ;LicenseFile=COPYING

+ 3 - 2
build/makepack-dolibarr.pl

@@ -36,7 +36,7 @@ $PUBLISHBETARC="dolibarr\@vmprod1.dolibarr.org:/home/dolibarr/dolibarr.org/httpd
 "RPM_FEDORA"=>"rpmbuild",
 "RPM_MANDRIVA"=>"rpmbuild",
 "RPM_OPENSUSE"=>"rpmbuild",
-"DEB"=>"dpkg",
+"DEB"=>"dpkg dpatch",
 "FLATPACK"=>"flatpack",
 "EXEDOLIWAMP"=>"ISCC.exe",
 "SNAPSHOT"=>"tar"
@@ -589,6 +589,7 @@ if ($nboftargetok) {
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/phpoffice/phpexcel/Examples`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/phpoffice/phpexcel/unitTests`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/phpoffice/phpexcel/license.md`;
+        $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/sabre/sabre/dav/tests`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/stripe/LICENSE`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/tcpdf/fonts/dejavu-fonts-ttf-*`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/tcpdf/fonts/freefont-*`;
@@ -1057,7 +1058,7 @@ if ($nboftargetok) {
     		$ret=`cat "$SOURCE/build/exe/doliwamp/doliwamp.iss" | sed -e 's/__FILENAMEEXEDOLIWAMP__/$FILENAMEEXEDOLIWAMP/g' > "$SOURCE/build/exe/doliwamp/doliwamp.tmp.iss"`;
 
     		print "Compil exe $FILENAMEEXEDOLIWAMP.exe file from iss file \"$SOURCEBACK\\build\\exe\\doliwamp\\doliwamp.tmp.iss\"\n";
-    		$cmd= "ISCC.exe \"Z:$SOURCEBACK\\build\\exe\\doliwamp\\doliwamp.tmp.iss\"";
+    		$cmd= "wine ISCC.exe \"Z:$SOURCEBACK\\build\\exe\\doliwamp\\doliwamp.tmp.iss\"";
 			print "$cmd\n";
 			$ret= `$cmd`;
 			#print "$ret\n";

+ 2 - 2
build/makepack-howto.txt

@@ -43,9 +43,9 @@ To generate a changelog of a maintenance version x.y.z, you can do "cd ~/git/dol
 - Run makepack-dolibarr.pl to generate all packages.
 
 - Check content of built packages.
-- Move build files into www.dolibarr.org web site
-  (/home/dolibarr/wwwroot/files/stable).
 
+- Run makepack-dolibarr.pl again with option to publish files on 
+  dolibarr foundation server (Dir /home/dolibarr/wwwroot/files/stable on www.dolibarr.org).
 - Run makepack-dolibarr.pl again with option to publish files on 
   sourceforge. This will also add official tag.
 - Edit symbolic links in directory "/home/dolibarr/wwwroot/files/stable/xxx"

+ 1 - 0
build/travis-ci/apache.conf

@@ -1,5 +1,6 @@
 <VirtualHost *:80>
   DocumentRoot %TRAVIS_BUILD_DIR%/htdocs
+  ErrorLog /var/log/apache2/travis_error_log
 
   <Directory "%TRAVIS_BUILD_DIR%/htdocs/">
     Options FollowSymLinks MultiViews ExecCGI

File diff suppressed because it is too large
+ 0 - 226
dev/initdemo/mysqldump_dolibarr_9.0.0.sql


+ 5 - 0
dev/initdemo/savedemo.sh

@@ -191,6 +191,9 @@ export list="
 	--ignore-table=$base.llx_c_dolicloud_plans
 	--ignore-table=$base.llx_c_pays
 	--ignore-table=$base.llx_c_source
+	--ignore-table=$base.llx_c_ticketsup_category
+	--ignore-table=$base.llx_c_ticketsup_severity
+	--ignore-table=$base.llx_c_ticketsup_type
 	--ignore-table=$base.llx_cabinetmed_c_banques
 	--ignore-table=$base.llx_cabinetmed_c_ccam
 	--ignore-table=$base.llx_cabinetmed_c_examconclusion
@@ -248,6 +251,8 @@ export list="
 	--ignore-table=$base.llx_residence
 	--ignore-table=$base.llx_residence_building
 	--ignore-table=$base.llx_residence_building_links
+	--ignore-table=$base.llx_societe_rib2
+	--ignore-table=$base.llx_ticketsup
 	--ignore-table=$base.llx_ultimatepdf
 	--ignore-table=$base.llx_update_modules
 	--ignore-table=$base.llx_ventilation_achat

+ 0 - 12
dev/resources/dbmodel/dolibarr.uml

@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<uml:Model xmi:version="2.1" xmlns:xmi="http://schema.omg.org/spec/XMI/2.1" xmlns:uml="http://www.eclipse.org/uml2/2.1.0/UML" xmi:id="_qb8akM37EdqwVrslYOdUDA">
-  <packagedElement xmi:type="uml:Package" xmi:id="_w8IxIM37EdqwVrslYOdUDA" name="dolibarr">
-    <packagedElement xmi:type="uml:Package" xmi:id="_T5aZ4FK9Ed60vaZbVikprw" name="OrdersModule"/>
-    <packagedElement xmi:type="uml:Package" xmi:id="_ZhuGMlK9Ed60vaZbVikprw" name="InvoiceModule"/>
-    <packagedElement xmi:type="uml:Package" xmi:id="_cf3SUlK9Ed60vaZbVikprw" name="UserModule"/>
-    <packagedElement xmi:type="uml:Package" xmi:id="_maM0glK9Ed60vaZbVikprw" name="FoundationModule"/>
-    <packagedElement xmi:type="uml:Package" xmi:id="_oqTO4lK9Ed60vaZbVikprw" name="ContractsModule"/>
-    <packagedElement xmi:type="uml:Package" xmi:id="_rEq_klK9Ed60vaZbVikprw" name="ProposalsModule"/>
-    <packagedElement xmi:type="uml:Package" xmi:id="_vxYmUlK9Ed60vaZbVikprw" name="EMailingManagement"/>
-  </packagedElement>
-</uml:Model>

+ 0 - 53
dev/resources/dbmodel/dolibarr.umldi

@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<diagrams:Diagrams xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:di="http://www.topcased.org/DI/1.0" xmlns:diagrams="http://www.topcased.org/Diagrams/1.0" xmi:id="_SI6FkFK9Ed60vaZbVikprw" activeDiagram="_SI6FkVK9Ed60vaZbVikprw">
-  <model href="dolibarr.uml#_w8IxIM37EdqwVrslYOdUDA"/>
-  <diagrams xmi:id="_SI6FkVK9Ed60vaZbVikprw" position="0,0" size="100,100" name="dolibarr" viewport="0,0">
-    <property xmi:id="_SJ9OcFK9Ed60vaZbVikprw" key="pageFormatName" value="A4"/>
-    <property xmi:id="_SJ9OcVK9Ed60vaZbVikprw" key="diagramWidth" value="840"/>
-    <property xmi:id="_SJ9OclK9Ed60vaZbVikprw" key="diagramHeight" value="1188"/>
-    <property xmi:id="_SJ9Oc1K9Ed60vaZbVikprw" key="pageMarginName" value="Small Margin"/>
-    <property xmi:id="_SJ9OdFK9Ed60vaZbVikprw" key="diagramTopMargin" value="20"/>
-    <property xmi:id="_SJ9OdVK9Ed60vaZbVikprw" key="diagramBottomMargin" value="20"/>
-    <property xmi:id="_SJ9OdlK9Ed60vaZbVikprw" key="diagramLeftMargin" value="20"/>
-    <property xmi:id="_SJ9Od1K9Ed60vaZbVikprw" key="diagramRightMargin" value="20"/>
-    <property xmi:id="_SJ9OeFK9Ed60vaZbVikprw" key="orientation" value="portrait"/>
-    <semanticModel xsi:type="di:EMFSemanticModelBridge" xmi:id="_SI6FklK9Ed60vaZbVikprw" presentation="org.topcased.modeler.uml.classdiagram">
-      <element href="dolibarr.uml#_w8IxIM37EdqwVrslYOdUDA"/>
-    </semanticModel>
-    <contained xsi:type="di:GraphNode" xmi:id="_T2b-YFK9Ed60vaZbVikprw" position="15,190" size="176,81">
-      <semanticModel xsi:type="di:EMFSemanticModelBridge" xmi:id="_T2b-YVK9Ed60vaZbVikprw" presentation="default">
-        <element href="dolibarr.uml#_T5aZ4FK9Ed60vaZbVikprw"/>
-      </semanticModel>
-    </contained>
-    <contained xsi:type="di:GraphNode" xmi:id="_ZhuGMFK9Ed60vaZbVikprw" position="15,118" size="177,68">
-      <semanticModel xsi:type="di:EMFSemanticModelBridge" xmi:id="_ZhuGMVK9Ed60vaZbVikprw" presentation="default">
-        <element href="dolibarr.uml#_ZhuGMlK9Ed60vaZbVikprw"/>
-      </semanticModel>
-    </contained>
-    <contained xsi:type="di:GraphNode" xmi:id="_cf3SUFK9Ed60vaZbVikprw" position="15,10" size="174,81">
-      <semanticModel xsi:type="di:EMFSemanticModelBridge" xmi:id="_cf3SUVK9Ed60vaZbVikprw" presentation="default">
-        <element href="dolibarr.uml#_cf3SUlK9Ed60vaZbVikprw"/>
-      </semanticModel>
-    </contained>
-    <contained xsi:type="di:GraphNode" xmi:id="_maM0gFK9Ed60vaZbVikprw" position="291,118" size="145,73">
-      <semanticModel xsi:type="di:EMFSemanticModelBridge" xmi:id="_maM0gVK9Ed60vaZbVikprw" presentation="default">
-        <element href="dolibarr.uml#_maM0glK9Ed60vaZbVikprw"/>
-      </semanticModel>
-    </contained>
-    <contained xsi:type="di:GraphNode" xmi:id="_oqTO4FK9Ed60vaZbVikprw" position="15,274" size="179,80">
-      <semanticModel xsi:type="di:EMFSemanticModelBridge" xmi:id="_oqTO4VK9Ed60vaZbVikprw" presentation="default">
-        <element href="dolibarr.uml#_oqTO4lK9Ed60vaZbVikprw"/>
-      </semanticModel>
-    </contained>
-    <contained xsi:type="di:GraphNode" xmi:id="_rEq_kFK9Ed60vaZbVikprw" position="15,358" size="181,73">
-      <semanticModel xsi:type="di:EMFSemanticModelBridge" xmi:id="_rEq_kVK9Ed60vaZbVikprw" presentation="default">
-        <element href="dolibarr.uml#_rEq_klK9Ed60vaZbVikprw"/>
-      </semanticModel>
-    </contained>
-    <contained xsi:type="di:GraphNode" xmi:id="_vxYmUFK9Ed60vaZbVikprw" position="15,490" size="181,80">
-      <semanticModel xsi:type="di:EMFSemanticModelBridge" xmi:id="_vxYmUVK9Ed60vaZbVikprw" presentation="default">
-        <element href="dolibarr.uml#_vxYmUlK9Ed60vaZbVikprw"/>
-      </semanticModel>
-    </contained>
-  </diagrams>
-</diagrams:Diagrams>

BIN
dev/resources/dbmodel/dolibarr_schema.mwb


+ 16 - 1
dev/setup/codesniffer/ruleset.xml

@@ -50,7 +50,22 @@
     <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter.Found">
 		<severity>0</severity>
     </rule>
-
+    <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed">
+		<severity>0</severity>
+    </rule>
+    <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed">
+		<severity>0</severity>
+    </rule>
+    <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass">
+		<severity>0</severity>
+    </rule>
+    <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed">
+		<severity>0</severity>
+    </rule>
+    <rule ref="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed">
+		<severity>0</severity>
+    </rule>
+    
     <rule ref="Generic.CodeAnalysis.UselessOverridingMethod" />
 
     <!--

+ 3 - 3
dev/tools/fixdosfiles.sh

@@ -17,14 +17,14 @@ fi
 # To detec
 if [ "x$1" = "xlist" ]
 then
-	find . \( -iname "functions" -o -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" -o -iname "*.pml" \) -exec file "{}" + | grep CRLF
-#	find . \( -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" \) -exec file "{}" + | grep -v 'htdocs\/includes' | grep CRLF
+	find . \( -iname "functions" -o -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" -o -iname "*.pml" \) -exec file "{}" + | grep -v 'documents\/website' | grep CRLF
+#	find . \( -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" \) -exec file "{}" + | grep -v 'documents\/website' | grep -v 'htdocs\/includes' | grep CRLF
 fi
 
 # To convert
 if [ "x$1" = "xfix" ]
 then
-	for fic in `find . \( -iname "functions" -o -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" -o -iname "*.pml" \) -exec file "{}" + | grep CRLF | awk -F':' '{ print $1 }' `
+	for fic in `find . \( -iname "functions" -o -iname "*.md" -o -iname "*.html" -o -iname "*.htm" -o -iname "*.php" -o -iname "*.sh" -o -iname "*.cml" -o -iname "*.css" -o -iname "*.js" -o -iname "*.lang" -o -iname "*.pl" -o -iname "*.sql" -o -iname "*.txt" -o -iname "*.xml" -o -iname "*.pml" \) -exec file "{}" + | grep -v 'documents\/website' | grep CRLF | awk -F':' '{ print $1 }' `
 	do
 		echo "Fix file $fic"
 		dos2unix "$fic"

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

@@ -641,7 +641,6 @@ if ($num > 0)
 			$object->id = $line->id;
 			$object->piece_num = $line->piece_num;
 			print $object->getNomUrl(1,'',0,'',1);
-			//print '<a href="./card.php?piece_num=' . $line->piece_num . '&save_lastsearch_values=1">' . $line->piece_num . '</a>';
 			print '</td>';
 			if (! $i) $totalarray['nbfield']++;
 		}

+ 3 - 0
htdocs/accountancy/class/accountancyexport.class.php

@@ -572,8 +572,10 @@ class AccountancyExport
 
 			if (empty($line->subledger_account)) {
 				print length_accountg($line->numero_compte) . $separator;
+				print $line->label_compte . $separator;
 			} else {
 				print length_accounta($line->subledger_account) . $separator;
+				print $line->subledger_label . $separator;
 			}
 
 			print $line->doc_ref . $separator;
@@ -581,6 +583,7 @@ class AccountancyExport
 			print price($line->credit) . $separator;
 			print price($line->montant) . $separator;
 			print $line->sens . $separator;
+			print $line->lettering_code . $separator;
 			print $line->code_journal;
 			print $end_line;
 		}

+ 19 - 19
htdocs/accountancy/class/accountingaccount.class.php

@@ -165,7 +165,7 @@ class AccountingAccount extends CommonObject
 			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_accounting_category as ca ON a.fk_accounting_category = ca.rowid";
 			$sql .= " WHERE";
 			if ($rowid) {
-				$sql .= " a.rowid = '" . $rowid . "'";
+				$sql .= " a.rowid = " . (int) $rowid;
 			} elseif ($account_number) {
 				$sql .= " a.account_number = '" . $this->db->escape($account_number) . "'";
 			}
@@ -212,9 +212,9 @@ class AccountingAccount extends CommonObject
 	/**
 	 * Insert new accounting account in chart of accounts
 	 *
-	 * @param User $user Use making action
-	 * @param int $notrigger Disable triggers
-	 * @return int <0 if KO, >0 if OK
+	 * @param  User    $user       User making action
+	 * @param  int     $notrigger  Disable triggers
+	 * @return int                 <0 if KO, >0 if OK
 	 */
     function create($user, $notrigger = 0)
     {
@@ -273,11 +273,11 @@ class AccountingAccount extends CommonObject
 		$sql .= ", " . (empty($this->pcg_type) ? 'NULL' : "'" . $this->db->escape($this->pcg_type) . "'");
 		$sql .= ", " . (empty($this->pcg_subtype) ? 'NULL' : "'" . $this->db->escape($this->pcg_subtype) . "'");
 		$sql .= ", " . (empty($this->account_number) ? 'NULL' : "'" . $this->db->escape($this->account_number) . "'");
-		$sql .= ", " . (empty($this->account_parent) ? '0' : "'" . $this->db->escape($this->account_parent) . "'");
-		$sql .= ", " . (empty($this->label) ? 'NULL' : "'" . $this->db->escape($this->label) . "'");
-		$sql .= ", " . (empty($this->account_category) ? 0 : $this->db->escape($this->account_category));
+		$sql .= ", " . (empty($this->account_parent) ? 0 : (int) $this->account_parent);
+		$sql .= ", " . (empty($this->label) ? "''" : "'" . $this->db->escape($this->label) . "'");
+		$sql .= ", " . (empty($this->account_category) ? 0 : (int) $this->account_category);
 		$sql .= ", " . $user->id;
-		$sql .= ", " . (! isset($this->active) ? 'NULL' : $this->db->escape($this->active));
+		$sql .= ", " . (int) $this->active;
 		$sql .= ")";
 
 		$this->db->begin();
@@ -285,7 +285,7 @@ class AccountingAccount extends CommonObject
 		dol_syslog(get_class($this) . "::create sql=" . $sql, LOG_DEBUG);
 		$resql = $this->db->query($sql);
 		if (! $resql) {
-			$error ++;
+			$error++;
 			$this->errors[] = "Error " . $this->db->lasterror();
 		}
 
@@ -307,12 +307,12 @@ class AccountingAccount extends CommonObject
 
 		// Commit or rollback
 		if ($error) {
-			foreach ( $this->errors as $errmsg ) {
+			foreach ($this->errors as $errmsg) {
 				dol_syslog(get_class($this) . "::create " . $errmsg, LOG_ERR);
 				$this->error .= ($this->error ? ', ' . $errmsg : $errmsg);
 			}
 			$this->db->rollback();
-			return - 1 * $error;
+			return -1 * $error;
 		} else {
 			$this->db->commit();
 			return $this->id;
@@ -344,11 +344,11 @@ class AccountingAccount extends CommonObject
 		$sql .= " , pcg_type = " . ($this->pcg_type ? "'" . $this->db->escape($this->pcg_type) . "'" : "null");
 		$sql .= " , pcg_subtype = " . ($this->pcg_subtype ? "'" . $this->db->escape($this->pcg_subtype) . "'" : "null");
 		$sql .= " , account_number = '" . $this->db->escape($this->account_number) . "'";
-		$sql .= " , account_parent = '" . $this->db->escape($this->account_parent) . "'";
-		$sql .= " , label = " . ($this->label ? "'" . $this->db->escape($this->label) . "'" : "null");
-		$sql .= " , fk_accounting_category = " . (empty($this->account_category) ? 0 : $this->db->escape($this->account_category));
+		$sql .= " , account_parent = " . (int) $this->account_parent;
+		$sql .= " , label = " . ($this->label ? "'" . $this->db->escape($this->label) . "'" : "''");
+		$sql .= " , fk_accounting_category = " . (empty($this->account_category) ? 0 : (int) $this->account_category);
 		$sql .= " , fk_user_modif = " . $user->id;
-		$sql .= " , active = " . $this->active;
+		$sql .= " , active = " . (int) $this->active;
 		$sql .= " WHERE rowid = " . $this->id;
 
 		dol_syslog(get_class($this) . "::update sql=" . $sql, LOG_DEBUG);
@@ -373,10 +373,10 @@ class AccountingAccount extends CommonObject
 		global $langs;
 
 		$sql = "(SELECT fk_code_ventilation FROM " . MAIN_DB_PREFIX . "facturedet";
-		$sql .= " WHERE  fk_code_ventilation=" . $this->id . ")";
-		$sql .= "UNION";
-		$sql .= "(SELECT fk_code_ventilation FROM " . MAIN_DB_PREFIX . "facture_fourn_det";
-		$sql .= " WHERE  fk_code_ventilation=" . $this->id . ")";
+		$sql.= " WHERE fk_code_ventilation=" . $this->id . ")";
+		$sql.= "UNION";
+		$sql.= " (SELECT fk_code_ventilation FROM " . MAIN_DB_PREFIX . "facture_fourn_det";
+		$sql.= " WHERE fk_code_ventilation=" . $this->id . ")";
 
 		dol_syslog(get_class($this) . "::checkUsage sql=" . $sql, LOG_DEBUG);
 		$resql = $this->db->query($sql);

+ 5 - 1
htdocs/accountancy/class/bookkeeping.class.php

@@ -1418,6 +1418,7 @@ class BookKeeping extends CommonObject
 		dol_syslog(__METHOD__, LOG_DEBUG);
 
 		global $user;
+
 		$error = 0;
 		$object = new BookKeeping($this->db);
 
@@ -1432,6 +1433,7 @@ class BookKeeping extends CommonObject
 		// ...
 
 		// Create clone
+		$object->context['createfromclone'] = 'createfromclone';
 		$result = $object->create($user);
 
 		// Other options
@@ -1441,6 +1443,8 @@ class BookKeeping extends CommonObject
 			dol_syslog(__METHOD__ . ' ' . join(',', $this->errors), LOG_ERR);
 		}
 
+		unset($object->context['createfromclone']);
+
 		// End
 		if (! $error) {
 			$this->db->commit();
@@ -1449,7 +1453,7 @@ class BookKeeping extends CommonObject
 		} else {
 			$this->db->rollback();
 
-			return - 1;
+			return -1;
 		}
 	}
 

+ 89 - 46
htdocs/accountancy/journal/bankjournal.php

@@ -4,10 +4,11 @@
  * Copyright (C) 2011       Juanjo Menent           <jmenent@2byte.es>
  * Copyright (C) 2012       Regis Houssin           <regis.houssin@inodbox.com>
  * Copyright (C) 2013       Christophe Battarel     <christophe.battarel@altairis.fr>
- * Copyright (C) 2013-2018  Alexandre Spangaro      <aspangaro@zendsi.com>
+ * Copyright (C) 2013-2018  Alexandre Spangaro      <aspangaro@open-dsi.fr>
  * Copyright (C) 2013-2014  Florian Henry           <florian.henry@open-concept.pro>
  * Copyright (C) 2013-2014  Olivier Geffroy         <jeff@jeffinfo.com>
  * Copyright (C) 2017-2018  Frédéric France         <frederic.france@netlogic.fr>
+ * Copyright (C) 2018		Ferran Marcet		    <fmarcet@2byte.es>
  *
  * 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
@@ -56,9 +57,10 @@ require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/paymentvarious.class.php';
 require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
 require_once DOL_DOCUMENT_ROOT . '/loan/class/loan.class.php';
 require_once DOL_DOCUMENT_ROOT . '/loan/class/paymentloan.class.php';
+require_once DOL_DOCUMENT_ROOT . '/adherents/class/subscription.class.php';
 
 // Load translation files required by the page
-$langs->loadLangs(array("companies","other","compta","banks","bills","donations","loan","accountancy","trips","salaries","hrm"));
+$langs->loadLangs(array("companies","other","compta","banks","bills","donations","loan","accountancy","trips","salaries","hrm","members"));
 
 // Multi journal
 $id_journal = GETPOST('id_journal', 'int');
@@ -149,6 +151,7 @@ $paymentexpensereportstatic = new PaymentExpenseReport($db);
 $paymentvariousstatic = new PaymentVarious($db);
 $paymentloanstatic = new PaymentLoan($db);
 $accountLinestatic=new AccountLine($db);
+$paymentsubscriptionstatic = new Subscription($db);
 
 $accountingaccount = new AccountingAccount($db);
 
@@ -167,12 +170,13 @@ if ($result) {
 	//print $sql;
 
 	// Variables
-	$account_supplier = (! empty($conf->global->ACCOUNTING_ACCOUNT_SUPPLIER) ? $conf->global->ACCOUNTING_ACCOUNT_SUPPLIER : 'NotDefined');	// NotDefined is a reserved word
-	$account_customer = (! empty($conf->global->ACCOUNTING_ACCOUNT_CUSTOMER) ? $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER : 'NotDefined');	// NotDefined is a reserved word
-	$account_employee = (! empty($conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT) ? $conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT : 'NotDefined');	// NotDefined is a reserved word
-	$account_pay_vat = (! empty($conf->global->ACCOUNTING_VAT_PAY_ACCOUNT) ? $conf->global->ACCOUNTING_VAT_PAY_ACCOUNT : 'NotDefined');	// NotDefined is a reserved word
-	$account_pay_donation = (! empty($conf->global->DONATION_ACCOUNTINGACCOUNT) ? $conf->global->DONATION_ACCOUNTINGACCOUNT : 'NotDefined');	// NotDefined is a reserved word
-	$account_transfer = (! empty($conf->global->ACCOUNTING_ACCOUNT_TRANSFER_CASH) ? $conf->global->ACCOUNTING_ACCOUNT_TRANSFER_CASH : 'NotDefined');	// NotDefined is a reserved word
+	$account_supplier			= (! empty($conf->global->ACCOUNTING_ACCOUNT_SUPPLIER) ? $conf->global->ACCOUNTING_ACCOUNT_SUPPLIER : 'NotDefined');	// NotDefined is a reserved word
+	$account_customer			= (! empty($conf->global->ACCOUNTING_ACCOUNT_CUSTOMER) ? $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER : 'NotDefined');	// NotDefined is a reserved word
+	$account_employee			= (! empty($conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT) ? $conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT : 'NotDefined');	// NotDefined is a reserved word
+	$account_pay_vat			= (! empty($conf->global->ACCOUNTING_VAT_PAY_ACCOUNT) ? $conf->global->ACCOUNTING_VAT_PAY_ACCOUNT : 'NotDefined');	// NotDefined is a reserved word
+	$account_pay_donation		= (! empty($conf->global->DONATION_ACCOUNTINGACCOUNT) ? $conf->global->DONATION_ACCOUNTINGACCOUNT : 'NotDefined');	// NotDefined is a reserved word
+	$account_pay_subscription	= (! empty($conf->global->ADHERENT_SUBSCRIPTION_ACCOUNTINGACCOUNT) ? $conf->global->ADHERENT_SUBSCRIPTION_ACCOUNTINGACCOUNT : 'NotDefined');	// NotDefined is a reserved word
+	$account_transfer			= (! empty($conf->global->ACCOUNTING_ACCOUNT_TRANSFER_CASH) ? $conf->global->ACCOUNTING_ACCOUNT_TRANSFER_CASH : 'NotDefined');	// NotDefined is a reserved word
 
 	$tabcompany = array();
 	$tabuser = array();
@@ -263,7 +267,7 @@ if ($result) {
 			// Now loop on each link of record in bank.
 			foreach ($links as $key => $val) {
 
-				if (in_array($links[$key]['type'], array('sc', 'payment_sc', 'payment', 'payment_supplier', 'payment_vat', 'payment_expensereport', 'banktransfert', 'payment_donation', 'payment_loan', 'payment_salary', 'payment_various')))
+				if (in_array($links[$key]['type'], array('sc', 'payment_sc', 'payment', 'payment_supplier', 'payment_vat', 'payment_expensereport', 'banktransfert', 'payment_donation', 'member', 'payment_loan', 'payment_salary', 'payment_various')))
 				{
 					// So we excluded 'company' and 'user' here. We want only payment lines
 
@@ -341,6 +345,14 @@ if ($result) {
 					$tabpay[$obj->rowid]["lib"] .= ' ' . $paymentdonstatic->getNomUrl(2);
 					$tabpay[$obj->rowid]["paymentdonationid"] = $paymentdonstatic->id;
 					$tabtp[$obj->rowid][$account_pay_donation] += $obj->amount;
+				} else if ($links[$key]['type'] == 'member') {
+					$paymentsubscriptionstatic->id = $links[$key]['url_id'];
+					$paymentsubscriptionstatic->ref = $links[$key]['url_id'];
+					$paymentsubscriptionstatic->label = $links[$key]['label'];
+					$tabpay[$obj->rowid]["lib"] .= ' ' . $paymentsubscriptionstatic->getNomUrl(2);
+					$tabpay[$obj->rowid]["paymentsubscriptionid"] = $paymentsubscriptionstatic->id;
+					$paymentsubscriptionstatic->fetch($paymentsubscriptionstatic->id);
+					$tabtp[$obj->rowid][$account_pay_subscription] += $obj->amount;
 				} else if ($links[$key]['type'] == 'payment_vat') {				// Payment VAT
 					$paymentvatstatic->id = $links[$key]['url_id'];
 					$paymentvatstatic->ref = $links[$key]['url_id'];
@@ -374,6 +386,18 @@ if ($result) {
 					$tabpay[$obj->rowid]["lib"] .= ' ' . $paymentloanstatic->getNomUrl(2);
 					$tabpay[$obj->rowid]["paymentloanid"] = $paymentloanstatic->id;
 					//$tabtp[$obj->rowid][$account_pay_loan] += $obj->amount;
+					$sqlmid = 'SELECT pl.amount_capital, pl.amount_insurance, pl.amount_interest, l.accountancy_account_capital, l.accountancy_account_insurance, l.accountancy_account_interest';
+					$sqlmid.= ' FROM '.MAIN_DB_PREFIX.'payment_loan as pl, '.MAIN_DB_PREFIX.'loan as l';
+					$sqlmid.= ' WHERE l.rowid = pl.fk_loan AND pl.fk_bank = '.$obj->rowid;
+
+					dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
+					$resultmid = $db->query($sqlmid);
+					if ($resultmid) {
+						$objmid = $db->fetch_object($resultmid);
+						$tabtp[$obj->rowid][$objmid->accountancy_account_capital] -= $objmid->amount_capital;
+						$tabtp[$obj->rowid][$objmid->accountancy_account_insurance] -= $objmid->amount_insurance;
+						$tabtp[$obj->rowid][$objmid->accountancy_account_interest] -= $objmid->amount_interest;
+					}
 				} else if ($links[$key]['type'] == 'banktransfert') {
 					$accountLinestatic->fetch($links[$key]['url_id']);
 					$tabpay[$obj->rowid]["lib"] .= ' '.$langs->trans("BankTransfer").'- ' .$accountLinestatic ->getNomUrl(1);
@@ -573,35 +597,42 @@ if (! $error && $action == 'writebookkeeping') {
 							$bookkeeping->subledger_label = '';
 							$bookkeeping->numero_compte = $k;
 
-							$accountingaccount->fetch($k, null, true);
+							$accountingaccount->fetch(null, $k, true);
 							$bookkeeping->label_compte = $accountingaccount->label;
 						} else if ($tabtype[$key] == 'payment_donation') {
 							$bookkeeping->subledger_account = '';
 							$bookkeeping->subledger_label = '';
 							$bookkeeping->numero_compte = $k;
 
-							$accountingaccount->fetch($k, null, true);
+							$accountingaccount->fetch(null, $k, true);
+							$bookkeeping->label_compte = $accountingaccount->label;
+						} else if ($tabtype[$key] == 'member') {
+							$bookkeeping->subledger_account = '';
+							$bookkeeping->subledger_label = '';
+							$bookkeeping->numero_compte = $k;
+
+							$accountingaccount->fetch(null, $k, true);
 							$bookkeeping->label_compte = $accountingaccount->label;
 						} else if ($tabtype[$key] == 'payment_loan') {
 							$bookkeeping->subledger_account = '';
 							$bookkeeping->subledger_label = '';
 							$bookkeeping->numero_compte = $k;
 
-							$accountingaccount->fetch($k, null, true);
+							$accountingaccount->fetch(null, $k, true);
 							$bookkeeping->label_compte = $accountingaccount->label;
 						} else if ($tabtype[$key] == 'payment_various') {
 							$bookkeeping->subledger_account = '';
 							$bookkeeping->subledger_label = '';
 							$bookkeeping->numero_compte = $k;
 
-							$accountingaccount->fetch($k, null, true);
+							$accountingaccount->fetch(null, $k, true);
 							$bookkeeping->label_compte = $accountingaccount->label;
 						} else if ($tabtype[$key] == 'banktransfert') {
 							$bookkeeping->subledger_account = '';
 							$bookkeeping->subledger_label = '';
 							$bookkeeping->numero_compte = $k;
 
-							$accountingaccount->fetch($k, null, true);
+							$accountingaccount->fetch(null, $k, true);
 							$bookkeeping->label_compte = $accountingaccount->label;
 						} else {
 							if ($tabtype[$key] == 'unknown')	// Unknown transaction, we will use a waiting account for thirdparty.
@@ -891,7 +922,7 @@ if (empty($action) || $action == 'view') {
 
 
 	// Test that setup is complete
-	$sql = 'SELECT COUNT(rowid) as nb FROM '.MAIN_DB_PREFIX.'bank_account WHERE fk_accountancy_journal IS NULL';
+	$sql = 'SELECT COUNT(rowid) as nb FROM '.MAIN_DB_PREFIX.'bank_account WHERE fk_accountancy_journal IS NULL AND clos=0';
 	$resql = $db->query($sql);
 	if ($resql)
 	{
@@ -915,9 +946,18 @@ if (empty($action) || $action == 'view') {
 
 
 	print '<div class="tabsAction tabsActionNoBottom">';
+
 	if (! empty($conf->global->ACCOUNTING_ENABLE_EXPORT_DRAFT_JOURNAL)) print '<input type="button" class="butAction" name="exportcsv" value="' . $langs->trans("ExportDraftJournal") . '" onclick="launch_export();" />';
-	if ($in_bookkeeping == 'notyet') print '<input type="button" class="butAction" name="writebookkeeping" value="' . $langs->trans("WriteBookKeeping") . '" onclick="writebookkeeping();" />';
-	else print '<a class="butActionRefused" name="writebookkeeping">' . $langs->trans("WriteBookKeeping") . '</a>';
+
+	if (empty($conf->global->ACCOUNTING_ACCOUNT_CUSTOMER) || $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER == '-1'
+	    || empty($conf->global->ACCOUNTING_ACCOUNT_SUPPLIER) || $conf->global->ACCOUNTING_ACCOUNT_SUPPLIER == '-1'
+	    || empty($conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT) || $conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT == '-1') {
+	   print '<input type="button" class="butActionRefused classfortooltip" title="'.dol_escape_htmltag($langs->trans("SomeMandatoryStepsOfSetupWereNotDone")).'" value="' . $langs->trans("WriteBookKeeping") . '" />';
+	}
+	else {
+	    if ($in_bookkeeping == 'notyet') print '<input type="button" class="butAction" name="writebookkeeping" value="' . $langs->trans("WriteBookKeeping") . '" onclick="writebookkeeping();" />';
+	   else print '<a class="butActionRefused classfortooltip" name="writebookkeeping">' . $langs->trans("WriteBookKeeping") . '</a>';
+	}
 	print '</div>';
 
 	// TODO Avoid using js. We can use a direct link with $param
@@ -1027,11 +1067,13 @@ if (empty($action) || $action == 'view') {
 					print "<td>";
 					$account_ledger = $k;
 					// Try to force general ledger account depending on type
-					if ($tabtype[$key] == 'payment') $account_ledger = $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER;
-					if ($tabtype[$key] == 'payment_supplier') $account_ledger = $conf->global->ACCOUNTING_ACCOUNT_SUPPLIER;
-					if ($tabtype[$key] == 'payment_expensereport') $account_ledger = $conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT;
-					if ($tabtype[$key] == 'payment_salary') $account_ledger = $conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT;
-					if ($tabtype[$key] == 'payment_vat') $account_ledger = $conf->global->ACCOUNTING_VAT_PAY_ACCOUNT;
+					if ($tabtype[$key] == 'payment')				$account_ledger = $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER;
+					if ($tabtype[$key] == 'payment_supplier')		$account_ledger = $conf->global->ACCOUNTING_ACCOUNT_SUPPLIER;
+					if ($tabtype[$key] == 'payment_expensereport')	$account_ledger = $conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT;
+					if ($tabtype[$key] == 'payment_salary')			$account_ledger = $conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT;
+					if ($tabtype[$key] == 'payment_vat')			$account_ledger = $conf->global->ACCOUNTING_VAT_PAY_ACCOUNT;
+					if ($tabtype[$key] == 'member')					$account_ledger = $conf->global->ADHERENT_SUBSCRIPTION_ACCOUNTINGACCOUNT;
+
 					$accounttoshow = length_accounta($account_ledger);
 					if (empty($accounttoshow) || $accounttoshow == 'NotDefined')
 					{
@@ -1051,11 +1093,12 @@ if (empty($action) || $action == 'view') {
 						{
 							// We will refuse writing
 							$errorstring='UnknownAccountForThirdpartyBlocking';
-							if ($tabtype[$key] == 'payment') $errorstring='MainAccountForCustomersNotDefined';
-							if ($tabtype[$key] == 'payment_supplier') $errorstring='MainAccountForSuppliersNotDefined';
-							if ($tabtype[$key] == 'payment_expensereport') $errorstring='MainAccountForUsersNotDefined';
-							if ($tabtype[$key] == 'payment_salary') $errorstring='MainAccountForUsersNotDefined';
-							if ($tabtype[$key] == 'payment_vat') $errorstring='MainAccountForVatPaymentNotDefined';
+							if ($tabtype[$key] == 'payment')				$errorstring='MainAccountForCustomersNotDefined';
+							if ($tabtype[$key] == 'payment_supplier')		$errorstring='MainAccountForSuppliersNotDefined';
+							if ($tabtype[$key] == 'payment_expensereport')	$errorstring='MainAccountForUsersNotDefined';
+							if ($tabtype[$key] == 'payment_salary')			$errorstring='MainAccountForUsersNotDefined';
+							if ($tabtype[$key] == 'payment_vat')			$errorstring='MainAccountForVatPaymentNotDefined';
+							if ($tabtype[$key] == 'member')					$errorstring='MainAccountForSubscriptionPaymentNotDefined';
 							print '<span class="error">'.$langs->trans($errorstring).'</span>';
 						}
 					}
@@ -1152,93 +1195,93 @@ function getSourceDocRef($val, $typerecord)
 	// WE MUST HAVE SAME REF FOR ALL LINES WE WILL RECORD INTO THE BOOKKEEPING
 	$ref = $val['ref'];
 	if ($ref == '(SupplierInvoicePayment)' || $ref == '(SupplierInvoicePaymentBack)') {
-		$ref = $langs->trans('Supplier');
+		$ref = $langs->transnoentitiesnoconv('Supplier');
 	}
 	if ($ref == '(CustomerInvoicePayment)' || $ref == '(CustomerInvoicePaymentBack)') {
-		$ref = $langs->trans('Customer');
+		$ref = $langs->transnoentitiesnoconv('Customer');
 	}
 	if ($ref == '(SocialContributionPayment)') {
-		$ref = $langs->trans('SocialContribution');
+		$ref = $langs->transnoentitiesnoconv('SocialContribution');
 	}
 	if ($ref == '(DonationPayment)') {
-		$ref = $langs->trans('Donation');
+		$ref = $langs->transnoentitiesnoconv('Donation');
 	}
 	if ($ref == '(SubscriptionPayment)') {
-		$ref = $langs->trans('Subscription');
+		$ref = $langs->transnoentitiesnoconv('Subscription');
 	}
 	if ($ref == '(ExpenseReportPayment)') {
-		$ref = $langs->trans('Employee');
+		$ref = $langs->transnoentitiesnoconv('Employee');
 	}
 	if ($ref == '(LoanPayment)') {
-		$ref = $langs->trans('Loan');
+		$ref = $langs->transnoentitiesnoconv('Loan');
 	}
 	if ($ref == '(payment_salary)') {
-		$ref = $langs->trans('Employee');
+		$ref = $langs->transnoentitiesnoconv('Employee');
 	}
 
 	$sqlmid = '';
 	if ($typerecord == 'payment')
 	{
-		$sqlmid = 'SELECT payfac.fk_facture as id, f.facnumber as ref';
+		$sqlmid = 'SELECT payfac.fk_facture as id, f.ref as ref';
 		$sqlmid .= " FROM ".MAIN_DB_PREFIX."paiement_facture as payfac, ".MAIN_DB_PREFIX."facture as f";
 		$sqlmid .= " WHERE payfac.fk_facture = f.rowid AND payfac.fk_paiement=" . $val["paymentid"];
-		$ref = $langs->trans("Invoice");
+		$ref = $langs->transnoentitiesnoconv("Invoice");
 	}
 	elseif ($typerecord == 'payment_supplier')
 	{
 		$sqlmid = 'SELECT payfac.fk_facturefourn as id, f.ref';
 		$sqlmid .= " FROM " . MAIN_DB_PREFIX . "paiementfourn_facturefourn as payfac, ".MAIN_DB_PREFIX."facture_fourn as f";
 		$sqlmid .= " WHERE payfac.fk_facturefourn = f.rowid AND payfac.fk_paiementfourn=" . $val["paymentsupplierid"];
-		$ref = $langs->trans("SupplierInvoice");
+		$ref = $langs->transnoentitiesnoconv("SupplierInvoice");
 	}
 	elseif ($typerecord == 'payment_expensereport')
 	{
 		$sqlmid = 'SELECT e.rowid as id, e.ref';
 		$sqlmid .= " FROM " . MAIN_DB_PREFIX . "payment_expensereport as pe, " . MAIN_DB_PREFIX . "expensereport as e";
 		$sqlmid .= " WHERE pe.rowid=" . $val["paymentexpensereport"]." AND pe.fk_expensereport = e.rowid";
-		$ref = $langs->trans("ExpenseReport");
+		$ref = $langs->transnoentitiesnoconv("ExpenseReport");
 	}
 	elseif ($typerecord == 'payment_salary')
 	{
 		$sqlmid = 'SELECT s.rowid as ref';
 		$sqlmid .= " FROM " . MAIN_DB_PREFIX . "payment_salary as s";
 		$sqlmid .= " WHERE s.rowid=" . $val["paymentsalid"];
-		$ref = $langs->trans("SalaryPayment");
+		$ref = $langs->transnoentitiesnoconv("SalaryPayment");
 	}
 	elseif ($typerecord == 'sc')
 	{
 		$sqlmid = 'SELECT sc.rowid as ref';
 		$sqlmid .= " FROM " . MAIN_DB_PREFIX . "paiementcharge as sc";
 		$sqlmid .= " WHERE sc.rowid=" . $val["paymentscid"];
-		$ref = $langs->trans("SocialContribution");
+		$ref = $langs->transnoentitiesnoconv("SocialContribution");
 	}
 	elseif ($typerecord == 'payment_vat')
 	{
 		$sqlmid = 'SELECT v.rowid as ref';
 		$sqlmid .= " FROM " . MAIN_DB_PREFIX . "tva as v";
 		$sqlmid .= " WHERE v.rowid=" . $val["paymentvatid"];
-		$ref = $langs->trans("PaymentVat");
+		$ref = $langs->transnoentitiesnoconv("PaymentVat");
 	}
 	elseif ($typerecord == 'payment_donation')
 	{
 		$sqlmid = 'SELECT payd.fk_donation as ref';
 		$sqlmid .= " FROM " . MAIN_DB_PREFIX . "payment_donation as payd";
 		$sqlmid .= " WHERE payd.fk_donation=" . $val["paymentdonationid"];
-		$ref = $langs->trans("Donation");
+		$ref = $langs->transnoentitiesnoconv("Donation");
 	}
 	elseif ($typerecord == 'payment_loan')
 	{
 		$sqlmid = 'SELECT l.rowid as ref';
 		$sqlmid .= " FROM " . MAIN_DB_PREFIX . "payment_loan as l";
 		$sqlmid .= " WHERE l.rowid=" . $val["paymentloanid"];
-		$ref = $langs->trans("LoanPayment");
+		$ref = $langs->transnoentitiesnoconv("LoanPayment");
 	}
 	elseif ($typerecord == 'payment_various')
 	{
 		$sqlmid = 'SELECT v.rowid as ref';
 		$sqlmid .= " FROM " . MAIN_DB_PREFIX . "payment_various as v";
 		$sqlmid .= " WHERE v.rowid=" . $val["paymentvariousid"];
-		$ref = $langs->trans("VariousPayment");
+		$ref = $langs->transnoentitiesnoconv("VariousPayment");
 	}
 	// Add warning
 	if (empty($sqlmid))
@@ -1260,6 +1303,6 @@ function getSourceDocRef($val, $typerecord)
 		else dol_print_error($db);
 	}
 
-	$ref = dol_trunc($langs->trans("BankId").' '.$val['fk_bank'].' - '.$ref, 295);	// 295 + 3 dots (...) is < than max size of 300
+	$ref = dol_trunc($langs->transnoentitiesnoconv("BankId").' '.$val['fk_bank'].' - '.$ref, 295);	// 295 + 3 dots (...) is < than max size of 300
 	return $ref;
 }

+ 4 - 4
htdocs/adherents/card.php

@@ -431,7 +431,7 @@ if (empty($reshook))
 		}
 
 		$typeid=GETPOST("typeid",'int');
-		$civility_id=GETPOST("civility_id",'int');
+		$civility_id=GETPOST("civility_id",'alpha');
 		$lastname=GETPOST("lastname",'alpha');
 		$firstname=GETPOST("firstname",'alpha');
 		$societe=GETPOST("societe",'alpha');
@@ -959,14 +959,14 @@ else
 
 		// Address
 		print '<tr><td class="tdtop">'.$langs->trans("Address").'</td><td>';
-		print '<textarea name="address" wrap="soft" class="quatrevingtpercent" rows="2">'.(GETPOST('address','alpha')?GETPOST('address','alpha'):$object->address).'</textarea>';
+		print '<textarea name="address" wrap="soft" class="quatrevingtpercent" rows="2">'.(GETPOST('address','alphanohtml')?GETPOST('address','alphanohtml'):$object->address).'</textarea>';
 		print '</td></tr>';
 
 		// Zip / Town
 		print '<tr><td>'.$langs->trans("Zip").' / '.$langs->trans("Town").'</td><td>';
-		print $formcompany->select_ziptown((GETPOST('zipcode','alpha')?GETPOST('zipcode','alpha'):$object->zip),'zipcode',array('town','selectcountry_id','state_id'),6);
+		print $formcompany->select_ziptown((GETPOST('zipcode','alphanohtml')?GETPOST('zipcode','alphanohtml'):$object->zip),'zipcode',array('town','selectcountry_id','state_id'),6);
 		print ' ';
-		print $formcompany->select_ziptown((GETPOST('town','alpha')?GETPOST('town','alpha'):$object->town),'town',array('zipcode','selectcountry_id','state_id'));
+		print $formcompany->select_ziptown((GETPOST('town','alphanohtml')?GETPOST('town','alphanohtml'):$object->town),'town',array('zipcode','selectcountry_id','state_id'));
 		print '</td></tr>';
 
 		// Country

+ 20 - 17
htdocs/adherents/class/adherent.class.php

@@ -324,23 +324,25 @@ class Adherent extends CommonObject
 
 		// Substitutions
 		$substitutionarray=array(
+		    '__ID__'=>$this->id,
+		    '__MEMBER_ID__'=>$this->id,
 			'__CIVILITY__'=>$this->getCivilityLabel(),
-			'__FIRSTNAME__'=>$msgishtml?dol_htmlentitiesbr($this->firstname):$this->firstname,
-			'__LASTNAME__'=>$msgishtml?dol_htmlentitiesbr($this->lastname):$this->lastname,
+			'__FIRSTNAME__'=>$msgishtml?dol_htmlentitiesbr($this->firstname):($this->firstname?$this->firstname:''),
+			'__LASTNAME__'=>$msgishtml?dol_htmlentitiesbr($this->lastname):($this->lastname?$this->lastname:''),
 			'__FULLNAME__'=>$msgishtml?dol_htmlentitiesbr($this->getFullName($langs)):$this->getFullName($langs),
-			'__COMPANY__'=>$msgishtml?dol_htmlentitiesbr($this->societe):$this->societe,
-			'__ADDRESS__'=>$msgishtml?dol_htmlentitiesbr($this->address):$this->address,
-			'__ZIP__'=>$msgishtml?dol_htmlentitiesbr($this->zip):$this->zip,
-			'__TOWN__'=>$msgishtml?dol_htmlentitiesbr($this->town):$this->town,
-			'__COUNTRY__'=>$msgishtml?dol_htmlentitiesbr($this->country):$this->country,
-			'__EMAIL__'=>$msgishtml?dol_htmlentitiesbr($this->email):$this->email,
-			'__BIRTH__'=>$msgishtml?dol_htmlentitiesbr($birthday):$birthday,
-			'__PHOTO__'=>$msgishtml?dol_htmlentitiesbr($this->photo):$this->photo,
-			'__LOGIN__'=>$msgishtml?dol_htmlentitiesbr($this->login):$this->login,
-			'__PASSWORD__'=>$msgishtml?dol_htmlentitiesbr($this->pass):$this->pass,
-			'__PHONE__'=>$msgishtml?dol_htmlentitiesbr($this->phone):$this->phone,
-			'__PHONEPRO__'=>$msgishtml?dol_htmlentitiesbr($this->phone_perso):$this->phone_perso,
-			'__PHONEMOBILE__'=>$msgishtml?dol_htmlentitiesbr($this->phone_mobile):$this->phone_mobile,
+			'__COMPANY__'=>$msgishtml?dol_htmlentitiesbr($this->societe):($this->societe?$this->societe:''),
+			'__ADDRESS__'=>$msgishtml?dol_htmlentitiesbr($this->address):($this->address?$this->address:''),
+			'__ZIP__'=>$msgishtml?dol_htmlentitiesbr($this->zip):($this->zip?$this->zip:''),
+			'__TOWN__'=>$msgishtml?dol_htmlentitiesbr($this->town):($this->town?$this->town:''),
+			'__COUNTRY__'=>$msgishtml?dol_htmlentitiesbr($this->country):($this->country?$this->country:''),
+			'__EMAIL__'=>$msgishtml?dol_htmlentitiesbr($this->email):($this->email?$this->email:''),
+			'__BIRTH__'=>$msgishtml?dol_htmlentitiesbr($birthday):($birthday?$birthday:''),
+			'__PHOTO__'=>$msgishtml?dol_htmlentitiesbr($this->photo):($this->photo?$this->photo:''),
+			'__LOGIN__'=>$msgishtml?dol_htmlentitiesbr($this->login):($this->login?$this->login:''),
+			'__PASSWORD__'=>$msgishtml?dol_htmlentitiesbr($this->pass):($this->pass?$this->pass:''),
+			'__PHONE__'=>$msgishtml?dol_htmlentitiesbr($this->phone):($this->phone?$this->phone:''),
+			'__PHONEPRO__'=>$msgishtml?dol_htmlentitiesbr($this->phone_perso):($this->phone_perso?$this->phone_perso:''),
+			'__PHONEMOBILE__'=>$msgishtml?dol_htmlentitiesbr($this->phone_mobile):($this->phone_mobile?$this->phone_mobile:'')
 		);
 
 		complete_substitutions_array($substitutionarray, $langs, $this);
@@ -697,13 +699,14 @@ class Adherent extends CommonObject
 						$lthirdparty->phone=$this->phone;
 						$lthirdparty->state_id=$this->state_id;
 						$lthirdparty->country_id=$this->country_id;
-						$lthirdparty->country_id=$this->country_id;
 						//$lthirdparty->phone_mobile=$this->phone_mobile;
 
-						$result=$lthirdparty->update($this->fk_soc,$user,0,1,1,'update');	// Use sync to 0 to avoid cyclic updates
+						$result=$lthirdparty->update($this->fk_soc, $user, 0, 1, 1, 'update');	// Use sync to 0 to avoid cyclic updates
+
 						if ($result < 0)
 						{
 							$this->error=$lthirdparty->error;
+							$this->errors=$lthirdparty->errors;
 							dol_syslog(get_class($this)."::update ".$this->error,LOG_ERR);
 							$error++;
 						}

+ 44 - 3
htdocs/adherents/list.php

@@ -79,8 +79,9 @@ $pagenext = $page + 1;
 if (! $sortorder) { $sortorder=($filter=='outofdate'?"DESC":"ASC"); }
 if (! $sortfield) { $sortfield=($filter=='outofdate'?"d.datefin":"d.lastname"); }
 
-// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
 $object = new Adherent($db);
+
+// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
 $hookmanager->initHooks(array('memberlist'));
 $extrafields = new ExtraFields($db);
 
@@ -348,8 +349,8 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
 
 // List of mass actions available
 $arrayofmassactions =  array(
-//    'presend'=>$langs->trans("SendByMail"),
-//    'builddoc'=>$langs->trans("PDFMerge"),
+	//'presend'=>$langs->trans("SendByMail"),
+	//'builddoc'=>$langs->trans("PDFMerge"),
 );
 if ($user->rights->adherent->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete");
 if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array();
@@ -603,6 +604,7 @@ print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="c
 print "</tr>\n";
 
 $i = 0;
+$totalarray=array();
 while ($i < min($num, $limit))
 {
 	$obj = $db->fetch_object($resql);
@@ -631,6 +633,7 @@ while ($i < min($num, $limit))
 	if (! empty($conf->global->MAIN_SHOW_TECHNICAL_ID))
 	{
 		print '<td align="center">'.$obj->rowid.'</td>';
+		if (! $i) $totalarray['nbfield']++;
 	}
 
 	// Ref
@@ -639,6 +642,7 @@ while ($i < min($num, $limit))
 		print "<td>";
 		print $memberstatic->getNomUrl(-1, 0, 'card', 'ref');
 		print "</td>\n";
+		if (! $i) $totalarray['nbfield']++;
 	}
 	// Civility
 	if (! empty($arrayfields['d.civility']['checked']))
@@ -646,6 +650,7 @@ while ($i < min($num, $limit))
 		print "<td>";
 		print $obj->civility;
 		print "</td>\n";
+		if (! $i) $totalarray['nbfield']++;
 	}
 	// Firstname
 	if (! empty($arrayfields['d.firstname']['checked']))
@@ -653,6 +658,7 @@ while ($i < min($num, $limit))
 		print "<td>";
 		print $obj->firstname;
 		print "</td>\n";
+		if (! $i) $totalarray['nbfield']++;
 	}
 	// Lastname
 	if (! empty($arrayfields['d.lastname']['checked']))
@@ -660,6 +666,7 @@ while ($i < min($num, $limit))
 		print "<td>";
 		print $obj->lastname;
 		print "</td>\n";
+		if (! $i) $totalarray['nbfield']++;
 	}
 	// Company
 	if (! empty($arrayfields['d.company']['checked']))
@@ -672,11 +679,13 @@ while ($i < min($num, $limit))
 	if (! empty($arrayfields['d.login']['checked']))
 	{
 		print "<td>".$obj->login."</td>\n";
+		if (! $i) $totalarray['nbfield']++;
 	}
 	// Moral/Physique
 	if (! empty($arrayfields['d.morphy']['checked']))
 	{
 		print "<td>".$memberstatic->getmorphylib($obj->morphy)."</td>\n";
+		if (! $i) $totalarray['nbfield']++;
 	}
 	// Type label
 	if (! empty($arrayfields['t.libelle']['checked']))
@@ -686,6 +695,7 @@ while ($i < min($num, $limit))
 		print '<td class="nowrap">';
 		print $membertypestatic->getNomUrl(1,32);
 		print '</td>';
+		if (! $i) $totalarray['nbfield']++;
 	}
 	// Address
 	if (! empty($arrayfields['d.address']['checked']))
@@ -693,6 +703,7 @@ while ($i < min($num, $limit))
 		print '<td class="nocellnopadd">';
 		print $obj->address;
 		print '</td>';
+		if (! $i) $totalarray['nbfield']++;
 	}
 	// Zip
 	if (! empty($arrayfields['d.zip']['checked']))
@@ -828,6 +839,36 @@ while ($i < min($num, $limit))
 	$i++;
 }
 
+// Show total line
+if (isset($totalarray['pos']))
+{
+	print '<tr class="liste_total">';
+	$i=0;
+	while ($i < $totalarray['nbfield'])
+	{
+		$i++;
+		if (! empty($totalarray['pos'][$i]))  print '<td align="right">'.price($totalarray['val'][$totalarray['pos'][$i]]).'</td>';
+		else
+		{
+			if ($i == 1)
+			{
+				if ($num < $limit) print '<td align="left">'.$langs->trans("Total").'</td>';
+				else print '<td align="left">'.$langs->trans("Totalforthispage").'</td>';
+			}
+			else print '<td></td>';
+		}
+	}
+	print '</tr>';
+}
+
+// If no record found
+if ($num == 0)
+{
+	$colspan=1;
+	foreach($arrayfields as $key => $val) { if (! empty($val['checked'])) $colspan++; }
+	print '<tr><td colspan="'.$colspan.'" class="opacitymedium">'.$langs->trans("NoRecordFound").'</td></tr>';
+}
+
 $db->free($resql);
 
 $parameters=array('sql' => $sql);

+ 20 - 2
htdocs/adherents/subscription.php

@@ -121,7 +121,7 @@ if ($action == 'confirm_create_thirdparty' && $confirm == 'yes' && $user->rights
 	{
 		// Creation of thirdparty
 		$company = new Societe($db);
-		$result=$company->create_from_member($object, GETPOST('companyname', 'alpha'), GETPOST('companyalias', 'alpha'));
+		$result=$company->create_from_member($object, GETPOST('companyname', 'alpha'), GETPOST('companyalias', 'alpha'), GETPOST('customercode', 'alpha'));
 
 		if ($result < 0)
 		{
@@ -348,7 +348,7 @@ if ($user->rights->adherent->cotisation->creer && $action == 'subscription' && !
         if (! $error)
         {
             // Send confirmation Email
-            if ($object->email && $sendalsoemail)
+            if ($object->email && $sendalsoemail)   // $object is 'Adherent'
             {
             	$subject = '';
             	$msg= '';
@@ -361,6 +361,7 @@ if ($user->rights->adherent->cotisation->creer && $action == 'subscription' && !
             	$outputlangs->setDefaultLang(empty($object->thirdparty->default_lang) ? $mysoc->default_lang : $object->thirdparty->default_lang);
             	// Load traductions files requiredby by page
             	$outputlangs->loadLangs(array("main", "members"));
+
             	// Get email content from template
             	$arraydefaultmessage=null;
             	$labeltouse = $conf->global->ADHERENT_EMAIL_TEMPLATE_SUBSCRIPTION;
@@ -856,6 +857,23 @@ if ($rowid > 0)
 				array('label' => $langs->trans("NameToCreate"), 'type' => 'text', 'name' => 'companyname', 'value' => $companyname, 'morecss' => 'minwidth300', 'moreattr' => 'maxlength="128"'),
 				array('label' => $langs->trans("AliasNames"), 'type' => 'text', 'name' => 'companyalias', 'value' => $companyalias, 'morecss' => 'minwidth300', 'moreattr' => 'maxlength="128"')
 			);
+			// If customer code was forced to "required", we ask it at creation to avoid error later
+			if (! empty($conf->global->MAIN_COMPANY_CODE_ALWAYS_REQUIRED))
+			{
+				$tmpcompany = new Societe($db);
+				$tmpcompany->name=$companyname;
+                $tmpcompany->get_codeclient($tmpcompany, 0);
+				$customercode = $tmpcompany->code_client;
+				$formquestion[]=array(
+                    'label' => $langs->trans("CustomerCode"),
+                    'type' => 'text',
+                    'name' => 'customercode',
+                    'value' => $customercode,
+                    'morecss' => 'minwidth300',
+                    'moreattr' => 'maxlength="128"',
+                );
+			}
+			// @TODO Add other extrafields mandatory for thirdparty creation
 
 			print $form->formconfirm($_SERVER["PHP_SELF"]."?rowid=".$object->id,$langs->trans("CreateDolibarrThirdParty"),$langs->trans("ConfirmCreateThirdParty"),"confirm_create_thirdparty",$formquestion,1);
 		}

+ 396 - 184
htdocs/adherents/subscription/list.php

@@ -28,12 +28,18 @@ require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
 require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php';
 require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
 
-$langs->load("members");
+$langs->loadLangs(array("members","companies"));
 
-$filter=$_GET["filter"];
-$statut=isset($_GET["statut"])?$_GET["statut"]:1;
+$action=GETPOST('action','aZ09');
+$massaction=GETPOST('massaction','alpha');
+$confirm=GETPOST('confirm','alpha');
+$toselect = GETPOST('toselect', 'array');
+
+$filter=GETPOST("filter","alpha");
+$statut=(GETPOSTISSET("statut")?GETPOST("statut","alpha"):1);
 $search_ref=GETPOST('search_ref','alpha');
 $search_lastname=GETPOST('search_lastname','alpha');
+$search_firstname=GETPOST('search_firstname','alpha');
 $search_login=GETPOST('search_login','alpha');
 $search_note=GETPOST('search_note','alpha');
 $search_account=GETPOST('search_account','int');
@@ -67,6 +73,20 @@ $search_array_options=$extrafields->getOptionalsFromPost($object->table_element,
 $fieldstosearchall = array(
 );
 $arrayfields=array(
+	'd.ref'=>array('label'=>$langs->trans("Ref"), 'checked'=>1),
+	'd.lastname'=>array('label'=>$langs->trans("Lastname"), 'checked'=>1),
+	'd.firstname'=>array('label'=>$langs->trans("Firstname"), 'checked'=>1),
+	'd.login'=>array('label'=>$langs->trans("Login"), 'checked'=>1),
+	't.libelle'=>array('label'=>$langs->trans("Type"), 'checked'=>1),
+	'd.bank'=>array('label'=>$langs->trans("BankAccount"), 'checked'=>1, 'enabled'=>(! empty($conf->banque->enabled))),
+	/*'d.note_public'=>array('label'=>$langs->trans("NotePublic"), 'checked'=>0),
+	 'd.note_private'=>array('label'=>$langs->trans("NotePrivate"), 'checked'=>0),*/
+	'd.datedebut'=>array('label'=>$langs->trans("DateSubscription"), 'checked'=>1, 'position'=>100),
+	'd.datefin'=>array('label'=>$langs->trans("EndSubscription"), 'checked'=>1, 'position'=>101),
+	'd.amount'=>array('label'=>$langs->trans("Amount"), 'checked'=>1, 'position'=>102),
+	'd.datec'=>array('label'=>$langs->trans("DateCreation"), 'checked'=>0, 'position'=>500),
+	'd.tms'=>array('label'=>$langs->trans("DateModificationShort"), 'checked'=>0, 'position'=>500),
+//	'd.statut'=>array('label'=>$langs->trans("Status"), 'checked'=>1, 'position'=>1000)
 );
 
 // Security check
@@ -74,7 +94,7 @@ $result=restrictedArea($user,'adherent','','','cotisation');
 
 
 /*
- *	Actions
+ * Actions
  */
 
 if (GETPOST('cancel','alpha')) { $action='list'; $massaction=''; }
@@ -111,15 +131,16 @@ if (empty($reshook))
  */
 
 $form=new Form($db);
+$subscription=new Subscription($db);
+$adherent=new Adherent($db);
+$accountstatic=new Account($db);
 
-llxHeader('',$langs->trans("ListOfSubscriptions"),'EN:Module_Foundations|FR:Module_Adh&eacute;rents|ES:M&oacute;dulo_Miembros');
-
+$now=dol_now();
 
 // List of subscriptions
 $sql = "SELECT d.rowid, d.login, d.firstname, d.lastname, d.societe, d.photo,";
 $sql.= " c.rowid as crowid, c.subscription,";
-$sql.= " c.dateadh,";
-$sql.= " c.datef,";
+$sql.= " c.dateadh, c.datef, c.datec as date_creation, c.tms as date_update,";
 $sql.= " c.fk_bank as bank, c.note,";
 $sql.= " b.fk_account";
 $sql.= " FROM ".MAIN_DB_PREFIX."adherent as d, ".MAIN_DB_PREFIX."subscription as c";
@@ -136,253 +157,444 @@ if ($search_ref)
 	if (is_numeric($search_ref)) $sql.= " AND (c.rowid = ".$db->escape($search_ref).")";
 	else $sql.=" AND 1 = 2";    // Always wrong
 }
-if ($search_lastname) $sql.= natural_search(array('d.firstname','d.lastname','d.societe'), $search_lastname);
+if ($search_lastname) $sql.= natural_search(array('d.lastname','d.societe'), $search_lastname);
+if ($search_firstname) $sql.= natural_search(array('d.firstname'), $search_firstname);
 if ($search_login) $sql.= natural_search('c.subscription', $search_login);
 if ($search_note)  $sql.= natural_search('c.note', $search_note);
-if ($search_account > 0) $sql.= " AND b.fk_account = ".$search_account;
+if ($search_account > 0) $sql.= " AND b.fk_account = ".urldecode($search_account);
 if ($search_amount) $sql.= natural_search('c.subscription', $search_amount, 1);
+
+// Add where from extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
+
+// Add where from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListWhere',$parameters);    // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+
 $sql.= $db->order($sortfield,$sortorder);
 
+// Count total nb of records with no order and no limits
 $nbtotalofrecords = '';
 if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
 {
-    $result = $db->query($sql);
-    $nbtotalofrecords = $db->num_rows($result);
+    $resql = $db->query($sql);
+    if ($resql) $nbtotalofrecords = $db->num_rows($resql);
+    else dol_print_error($db);
     if (($page * $limit) > $nbtotalofrecords)	// if total resultset is smaller then paging size (filtering), goto and load page 0
     {
     	$page = 0;
     	$offset = 0;
     }
 }
-
+// Add limit
 $sql.= $db->plimit($limit+1, $offset);
 
 $result = $db->query($sql);
-if ($result)
-{
-    $num = $db->num_rows($result);
-
-	$arrayofselected=is_array($toselect)?$toselect:array();
-
-	$i = 0;
-
-    $title=$langs->trans("ListOfSubscriptions");
-    if (! empty($date_select)) $title.=' ('.$langs->trans("Year").' '.$date_select.')';
-
-    $param='';
-    if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.$contextpage;
-    if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.$limit;
-    if ($statut != '')    $param.="&statut=".$statut;
-    if ($date_select)     $param.="&date_select=".$date_select;
-    if ($search_lastname) $param.="&search_lastname=".$search_lastname;
-	if ($search_login)    $param.="&search_login=".$search_login;
-	if ($search_acount)   $param.="&search_account=".$search_account;
-	if ($search_amount)   $param.="&search_amount=".$search_amount;
-	if ($optioncss != '') $param.='&optioncss='.$optioncss;
-
-	// List of mass actions available
-	$arrayofmassactions =  array(
-	    //'presend'=>$langs->trans("SendByMail"),
-	    //'builddoc'=>$langs->trans("PDFMerge"),
-	);
-	if ($user->rights->adherent->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete");
-	if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array();
-	$massactionbutton=$form->selectMassAction('', $arrayofmassactions);
-
-	$newcardbutton='';
-	if ($user->rights->adherent->cotisation->creer)
-	{
-		$newcardbutton='<a class="butActionNew" href="'.DOL_URL_ROOT.'/adherents/list.php?status=-1,1"><span class="valignmiddle">'.$langs->trans('NewSubscription').'</span>';
-		$newcardbutton.= '<span class="fa fa-plus-circle valignmiddle"></span>';
-		$newcardbutton.= '</a>';
-	}
+if (! $result)
+{
+	dol_print_error($db);
+	exit;
+}
 
-    print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
-    if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
-    print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
-	print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
-    print '<input type="hidden" name="view" value="'.dol_escape_htmltag($view).'">';
-    print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
-    print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
-    print '<input type="hidden" name="page" value="'.$page.'">';
-    print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
-
-    print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_generic.png', 0, $newcardbutton, '', $limit);
-
-	$topicmail="Information";
-	$modelmail="subscription";
-	$objecttmp=new Subscription($db);
-	$trackid='sub'.$object->id;
-	include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php';
-
-	if ($sall)
-	{
-		print $langs->trans("Filter")." (".$langs->trans("Ref").", ".$langs->trans("Lastname").", ".$langs->trans("Firstname").", ".$langs->trans("EMail").", ".$langs->trans("Address")." ".$langs->trans("or")." ".$langs->trans("Town")."): ".$sall;
-	}
+$num = $db->num_rows($result);
 
-    $moreforfilter = '';
+$arrayofselected=is_array($toselect)?$toselect:array();
 
-    $varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage;
-    $selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage);	// This also change content of $arrayfields
-    if ($massactionbutton) $selectedfields.=$form->showCheckAddButtons('checkforselect', 1);
+if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $sall)
+{
+	$obj = $db->fetch_object($resql);
+	$id = $obj->rowid;
+	header("Location: ".DOL_URL_ROOT.'/adherents/subscription/card.php?id='.$id);
+	exit;
+}
 
-    print '<div class="div-table-responsive">';
-    print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
+llxHeader('',$langs->trans("ListOfSubscriptions"),'EN:Module_Foundations|FR:Module_Adh&eacute;rents|ES:M&oacute;dulo_Miembros');
 
-	// Line for filters fields
-	print '<tr class="liste_titre_filter">';
+$i = 0;
+
+$title=$langs->trans("ListOfSubscriptions");
+if (! empty($date_select)) $title.=' ('.$langs->trans("Year").' '.$date_select.')';
+
+$param='';
+if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage);
+if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit);
+if ($statut != '')    $param.="&statut=".urlencode($statut);
+if ($date_select)     $param.="&date_select=".urlencode($date_select);
+if ($search_lastname) $param.="&search_lastname=".urlencode($search_lastname);
+if ($search_login)    $param.="&search_login=".urlencode($search_login);
+if ($search_acount)   $param.="&search_account=".urlencode($search_account);
+if ($search_amount)   $param.="&search_amount=".urlencode($search_amount);
+if ($optioncss != '') $param.='&optioncss='.urlencode($optioncss);
+// Add $param from extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
+
+// List of mass actions available
+$arrayofmassactions =  array(
+	//'presend'=>$langs->trans("SendByMail"),
+	//'builddoc'=>$langs->trans("PDFMerge"),
+);
+if ($user->rights->adherent->supprimer) $arrayofmassactions['predelete']=$langs->trans("Delete");
+if (in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array();
+$massactionbutton=$form->selectMassAction('', $arrayofmassactions);
 
+$newcardbutton='';
+if ($user->rights->adherent->cotisation->creer)
+{
+	$newcardbutton='<a class="butActionNew" href="'.DOL_URL_ROOT.'/adherents/list.php?status=-1,1"><span class="valignmiddle">'.$langs->trans('NewSubscription').'</span>';
+	$newcardbutton.= '<span class="fa fa-plus-circle valignmiddle"></span>';
+	$newcardbutton.= '</a>';
+}
+
+print '<form method="POST" id="searchFormList" action="'.$_SERVER["PHP_SELF"].'">';
+if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
+print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
+print '<input type="hidden" name="action" value="list">';
+print '<input type="hidden" name="view" value="'.dol_escape_htmltag($view).'">';
+print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
+print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
+print '<input type="hidden" name="page" value="'.$page.'">';
+print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
+
+print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_generic.png', 0, $newcardbutton, '', $limit);
+
+$topicmail="Information";
+$modelmail="subscription";
+$objecttmp=new Subscription($db);
+$trackid='sub'.$object->id;
+include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php';
+
+if ($sall)
+{
+	foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val);
+	print '<div class="divsearchfieldfilter">'.$langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall).'</div>';
+}
+
+$moreforfilter = '';
+
+$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage;
+$selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage);	// This also change content of $arrayfields
+if ($massactionbutton) $selectedfields.=$form->showCheckAddButtons('checkforselect', 1);
+
+print '<div class="div-table-responsive">';
+print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
+
+
+// Line for filters fields
+print '<tr class="liste_titre_filter">';
+
+// Line numbering
+if (! empty($conf->global->MAIN_SHOW_TECHNICAL_ID))
+{
+	print '<td class="liste_titre">&nbsp;</td>';
+}
+
+// Ref
+if (! empty($arrayfields['d.ref']['checked']))
+{
 	print '<td class="liste_titre" align="left">';
 	print '<input class="flat" type="text" name="search_ref" value="'.dol_escape_htmltag($search_ref).'" size="4"></td>';
+}
+
+if (! empty($arrayfields['d.lastname']['checked']))
+{
+	print '<td class="liste_titre" align="left">';
+	print '<input class="flat" type="text" name="search_lastname" value="'.dol_escape_htmltag($search_lastname).'" size="7"></td>';
+}
 
+if (! empty($arrayfields['d.firstname']['checked']))
+{
 	print '<td class="liste_titre" align="left">';
-	print '<input class="flat" type="text" name="search_lastname" value="'.dol_escape_htmltag($search_lastname).'" size="12"></td>';
+	print '<input class="flat" type="text" name="search_firstname" value="'.dol_escape_htmltag($search_firstname).'" size="12"></td>';
+}
 
+if (! empty($arrayfields['d.login']['checked']))
+{
 	print '<td class="liste_titre" align="left">';
 	print '<input class="flat" type="text" name="search_login" value="'.dol_escape_htmltag($search_login).'" size="7"></td>';
+}
 
-	print '<td class="liste_titre" align="left">';
-	print '<input class="flat" type="text" name="search_note" value="'.dol_escape_htmltag($search_note).'" size="7"></td>';
+if (! empty($arrayfields['t.libelle']['checked']))
+{
+	print '<td class="liste_titre">';
+	print '';
+	print '</td>';
+}
 
-    if (! empty($conf->banque->enabled))
-    {
-		print '<td class="liste_titre">';
-		print $form->select_comptes($search_account, 'search_account', 0, '', 1);
-		print '</td>';
-    }
+if (! empty($arrayfields['d.bank']['checked']))
+{
+	print '<td class="liste_titre">';
+	print $form->select_comptes($search_account, 'search_account', 0, '', 1);
+	print '</td>';
+}
 
+if (! empty($arrayfields['d.date_debut']['checked']))
+{
 	print '<td class="liste_titre">&nbsp;</td>';
+}
 
+if (! empty($arrayfields['d.date_fin']['checked']))
+{
 	print '<td class="liste_titre">&nbsp;</td>';
+}
 
+if (! empty($arrayfields['d.amount']['checked']))
+{
 	print '<td align="right" class="liste_titre">';
 	print '<input class="flat" type="text" name="search_amount" value="'.dol_escape_htmltag($search_amount).'" size="4">';
 	print '</td>';
+}
+// Extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php';
+
+// Fields from hook
+$parameters=array('arrayfields'=>$arrayfields);
+$reshook=$hookmanager->executeHooks('printFieldListOption',$parameters);    // Note that $action and $object may have been modified by hook
+print $hookmanager->resPrint;
+// Date creation
+if (! empty($arrayfields['d.datec']['checked']))
+{
+	print '<td class="liste_titre">';
+	print '</td>';
+}
+// Date modification
+if (! empty($arrayfields['d.tms']['checked']))
+{
+	print '<td class="liste_titre">';
+	print '</td>';
+}
 
-    // Action column
-    print '<td class="liste_titre" align="right">';
-    $searchpicto=$form->showFilterButtons();
-    print $searchpicto;
-    print '</td>';
+// Action column
+print '<td class="liste_titre" align="right">';
+$searchpicto=$form->showFilterButtons();
+print $searchpicto;
+print '</td>';
 
-	print "</tr>\n";
+print "</tr>\n";
 
 
-	print '<tr class="liste_titre">';
+print '<tr class="liste_titre">';
+if (! empty($arrayfields['d.ref']['checked']))
+{
 	print_liste_field_titre("Ref",$_SERVER["PHP_SELF"],"c.rowid",$param,"","",$sortfield,$sortorder);
-	print_liste_field_titre("Name",$_SERVER["PHP_SELF"],"d.lastname",$param,"","",$sortfield,$sortorder);
+}
+if (! empty($arrayfields['d.lastname']['checked']))
+{
+	print_liste_field_titre("LastName",$_SERVER["PHP_SELF"],"d.lastname",$param,"","",$sortfield,$sortorder);
+}
+if (! empty($arrayfields['d.firstname']['checked']))
+{
+	print_liste_field_titre("FirstName",$_SERVER["PHP_SELF"],"d.firstname",$param,"","",$sortfield,$sortorder);
+}
+if (! empty($arrayfields['d.login']['checked']))
+{
 	print_liste_field_titre("Login",$_SERVER["PHP_SELF"],"d.login",$param,"","",$sortfield,$sortorder);
+}
+if (! empty($arrayfields['t.libelle']['checked']))
+{
 	print_liste_field_titre("Label",$_SERVER["PHP_SELF"],"c.note",$param,"",'align="left"',$sortfield,$sortorder);
-	if (! empty($conf->banque->enabled))
-	{
-	    print_liste_field_titre("Account",$_SERVER["PHP_SELF"],"b.fk_account",$pram,"","",$sortfield,$sortorder);
-	}
+}
+if (! empty($arrayfields['d.bank']['checked']))
+{
+	print_liste_field_titre("Account",$_SERVER["PHP_SELF"],"b.fk_account",$pram,"","",$sortfield,$sortorder);
+}
+if (! empty($arrayfields['d.date_debut']['checked']))
+{
 	print_liste_field_titre("Date",$_SERVER["PHP_SELF"],"c.dateadh",$param,"",'align="center"',$sortfield,$sortorder);
+}
+if (! empty($arrayfields['d.date_fin']['checked']))
+{
 	print_liste_field_titre("DateEnd",$_SERVER["PHP_SELF"],"c.datef",$param,"",'align="center"',$sortfield,$sortorder);
+}
+if (! empty($arrayfields['d.amount']['checked']))
+{
 	print_liste_field_titre("Amount",$_SERVER["PHP_SELF"],"c.subscription",$param,"",'align="right"',$sortfield,$sortorder);
-	//print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="center"',$sortfield,$sortorder,'maxwidthsearch ');
-	print_liste_field_titre('', $_SERVER["PHP_SELF"],"",'','','align="center"',$sortfield,$sortorder,'maxwidthsearch ');
-	print "</tr>\n";
+}
+// Extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php';
+
+// Hook fields
+$parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder);
+$reshook=$hookmanager->executeHooks('printFieldListTitle',$parameters);    // Note that $action and $object may have been modified by hook
+print $hookmanager->resPrint;
+if (! empty($arrayfields['d.datec']['checked']))     print_liste_field_titre($arrayfields['d.datec']['label'],$_SERVER["PHP_SELF"],"d.datec","",$param,'align="center" class="nowrap"',$sortfield,$sortorder);
+if (! empty($arrayfields['d.tms']['checked']))       print_liste_field_titre($arrayfields['d.tms']['label'],$_SERVER["PHP_SELF"],"d.tms","",$param,'align="center" class="nowrap"',$sortfield,$sortorder);
+print_liste_field_titre($selectedfields, $_SERVER["PHP_SELF"],"",'','','align="center"',$sortfield,$sortorder,'maxwidthsearch ');
+print "</tr>\n";
+
+
+$total=0;
+$totalarray=array();
+while ($i < min($num, $limit))
+{
+	$obj = $db->fetch_object($result);
+	$total+=$obj->subscription;
 
+	$subscription->ref=$obj->crowid;
+	$subscription->id=$obj->crowid;
 
-    // Static objects
-    $subscription=new Subscription($db);
-    $adherent=new Adherent($db);
-    $accountstatic=new Account($db);
+	$adherent->lastname=$obj->lastname;
+	$adherent->firstname=$obj->firstname;
+	$adherent->ref=$obj->rowid;
+	$adherent->id=$obj->rowid;
+	$adherent->statut=$obj->statut;
+	$adherent->login=$obj->login;
+	$adherent->photo=$obj->photo;
 
-    $total=0;
-    while ($i < min($num, $limit))
-    {
-        $obj = $db->fetch_object($result);
-        $total+=$obj->subscription;
 
-        $subscription->ref=$obj->crowid;
-        $subscription->id=$obj->crowid;
-
-        $adherent->lastname=$obj->lastname;
-        $adherent->firstname=$obj->firstname;
-        $adherent->ref=$obj->rowid;
-        $adherent->id=$obj->rowid;
-        $adherent->statut=$obj->statut;
-        $adherent->login=$obj->login;
-        $adherent->photo=$obj->photo;
+	print '<tr class="oddeven">';
 
+	// Ref
+	if (! empty($arrayfields['d.ref']['checked']))
+	{
+		print '<td>'.$subscription->getNomUrl(1).'</td>';
+		if (! $i) $totalarray['nbfield']++;
+	}
 
+	// Lastname
+	if (! empty($arrayfields['d.lastname']['checked']))
+	{
+		$adherent->firstname = '';
+		print '<td>'.$adherent->getNomUrl(-1).'</td>';
+		$adherent->firstname = $obj->firstname;
+		if (! $i) $totalarray['nbfield']++;
+	}
+	// Firstname
+	if (! empty($arrayfields['d.firstname']['checked']))
+	{
+		print '<td>'.$adherent->firstname.'</td>';
+		if (! $i) $totalarray['nbfield']++;
+	}
 
-        print '<tr class="oddeven">';
+	// Login
+	if (! empty($arrayfields['d.login']['checked']))
+	{
+		print '<td>'.$adherent->login.'</td>';
+		if (! $i) $totalarray['nbfield']++;
+	}
 
-        // Ref
-        print '<td>'.$subscription->getNomUrl(1).'</td>';
+	// Label
+	if (! empty($arrayfields['t.libelle']['checked']))
+	{
+		print '<td>';
+		print dol_trunc($obj->note,128);
+		print '</td>';
+		if (! $i) $totalarray['nbfield']++;
+	}
 
-        // Lastname
-        print '<td>'.$adherent->getNomUrl(-1).'</td>';
+	// Banque
+	if (! empty($arrayfields['d.bank']['checked']))
+	{
+		print "<td>";
+		if ($obj->fk_account > 0)
+		{
+			$accountstatic->id=$obj->fk_account;
+			$accountstatic->fetch($obj->fk_account);
+			//$accountstatic->label=$obj->label;
+			print $accountstatic->getNomUrl(1);
+		}
+		print "</td>\n";
+		if (! $i) $totalarray['nbfield']++;
+	}
 
-        // Login
-        print '<td>'.$adherent->login.'</td>';
+	// Date start
+	if (! empty($arrayfields['d.date_start']['checked']))
+	{
+		print '<td align="center">'.dol_print_date($db->jdate($obj->dateadh),'day')."</td>\n";
+		if (! $i) $totalarray['nbfield']++;
+	}
+	// Date end
+	if (! empty($arrayfields['d.date_end']['checked']))
+	{
+		print '<td align="center">'.dol_print_date($db->jdate($obj->datef),'day')."</td>\n";
+		if (! $i) $totalarray['nbfield']++;
+	}
+	// Price
+	if (! empty($arrayfields['d.amount']['checked']))
+	{
+		print '<td align="right">'.price($obj->subscription).'</td>';
+		if (! $i) $totalarray['nbfield']++;
+		if (! $i) $totalarray['pos'][$totalarray['nbfield']]='d.amount';
+		$totalarray['val']['d.amount'] += $obj->subscription;
+	}
+	// Extra fields
+	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php';
+	// Fields from hook
+	$parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj);
+	$reshook=$hookmanager->executeHooks('printFieldListValue',$parameters);    // Note that $action and $object may have been modified by hook
+	print $hookmanager->resPrint;
+	// Date creation
+	if (! empty($arrayfields['d.datec']['checked']))
+	{
+		print '<td align="center" class="nowrap">';
+		print dol_print_date($db->jdate($obj->date_creation), 'dayhour', 'tzuser');
+		print '</td>';
+		if (! $i) $totalarray['nbfield']++;
+	}
+	// Date modification
+	if (! empty($arrayfields['d.tms']['checked']))
+	{
+		print '<td align="center" class="nowrap">';
+		print dol_print_date($db->jdate($obj->date_update), 'dayhour', 'tzuser');
+		print '</td>';
+		if (! $i) $totalarray['nbfield']++;
+	}
+	// Action column
+	print '<td align="center">';
+	if ($massactionbutton || $massaction)   // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
+	{
+		$selected=0;
+		if (in_array($obj->rowid, $arrayofselected)) $selected=1;
+		print '<input id="cb'.$obj->rowid.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$obj->rowid.'"'.($selected?' checked="checked"':'').'>';
+	}
+	print '</td>';
+	if (! $i) $totalarray['nbfield']++;
 
-        // Libelle
-        print '<td>';
-        print dol_trunc($obj->note,32);
-        print '</td>';
+	print "</tr>\n";
+	$i++;
+}
 
-		// Banque
-		if (! empty($conf->banque->enabled))
+// Show total line
+if (isset($totalarray['pos']))
+{
+	print '<tr class="liste_total">';
+	$i=0;
+	while ($i < $totalarray['nbfield'])
+	{
+		$i++;
+		if (! empty($totalarray['pos'][$i]))  print '<td align="right">'.price($totalarray['val'][$totalarray['pos'][$i]]).'</td>';
+		else
 		{
-			if ($obj->fk_account > 0)
-			{
-				$accountstatic->id=$obj->fk_account;
-				$accountstatic->fetch($obj->fk_account);
-				//$accountstatic->label=$obj->label;
-				print '<td>'.$accountstatic->getNomUrl(1).'</td>';
-			}
-			else
+			if ($i == 1)
 			{
-				print "<td>";
-				print "</td>\n";
+				if ($num < $limit) print '<td align="left">'.$langs->trans("Total").'</td>';
+				else print '<td align="left">'.$langs->trans("Totalforthispage").'</td>';
 			}
+			else print '<td></td>';
 		}
+	}
+	print '</tr>';
+}
 
-        // Date start
-        print '<td align="center">'.dol_print_date($db->jdate($obj->dateadh),'day')."</td>\n";
-
-        // Date end
-        print '<td align="center">'.dol_print_date($db->jdate($obj->datef),'day')."</td>\n";
-
-        // Price
-        print '<td align="right">'.price($obj->subscription).'</td>';
-
-        print '<td></td>';
-
-        print "</tr>";
+// If no record found
+if ($num == 0)
+{
+	$colspan=1;
+	foreach($arrayfields as $key => $val) { if (! empty($val['checked'])) $colspan++; }
+	print '<tr><td colspan="'.$colspan.'" class="opacitymedium">'.$langs->trans("NoRecordFound").'</td></tr>';
+}
 
-        $i++;
-    }
+$db->free($resql);
 
-    // Total
+$parameters=array('sql' => $sql);
+$reshook=$hookmanager->executeHooks('printFieldListFooter',$parameters);    // Note that $action and $object may have been modified by hook
+print $hookmanager->resPrint;
 
-    print '<tr class="liste_total">';
-    print "<td>".$langs->trans("Total")."</td>\n";
-    print "<td align=\"right\">&nbsp;</td>\n";
-    print "<td align=\"right\">&nbsp;</td>\n";
-    print "<td align=\"right\">&nbsp;</td>\n";
-    if (! empty($conf->banque->enabled))
-    {
-        print '<td>&nbsp;</td>';
-    }
-   	print '<td>&nbsp;</td>';
-   	print '<td>&nbsp;</td>';
-   	print '<td align="right">'.price($total)."</td>\n";
-   	print '<td></td>';
-    print "</tr>\n";
+print "</table>";
+print '</div>';
+print '</form>';
 
-    print "</table>";
-    print '</div>';
-	print '</form>';
-}
-else
-{
-    dol_print_error($db);
-}
 
 // End of page
 llxFooter();

+ 3 - 3
htdocs/adherents/type.php

@@ -42,7 +42,7 @@ $backtopage = GETPOST('backtopage','alpha');
 $search_lastname	= GETPOST('search_lastname','alpha');
 $search_login		= GETPOST('search_login','alpha');
 $search_email		= GETPOST('search_email','alpha');
-$type				= GETPOST('type','alpha');
+$type				= GETPOST('type','intcomma');
 $status				= GETPOST('status','alpha');
 
 $limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit;
@@ -59,8 +59,8 @@ if (! $sortfield) {  $sortfield="d.lastname"; }
 $label=GETPOST("label","alpha");
 $subscription=GETPOST("subscription","int");
 $vote=GETPOST("vote","int");
-$comment=GETPOST("comment");
-$mail_valid=GETPOST("mail_valid");
+$comment=GETPOST("comment",'alphanohtml');
+$mail_valid=GETPOST("mail_valid",'none');
 
 // Security check
 $result=restrictedArea($user,'adherent',$rowid,'adherent_type');

+ 2 - 1
htdocs/admin/company.php

@@ -773,10 +773,11 @@ else
 
 
 	print '<tr class="oddeven"><td>'.$langs->trans("CompanyCurrency").'</td><td>';
-	print currency_name($conf->currency,1);
+	print currency_name($conf->currency,0);
 	print ' ('.$conf->currency;
 	print ($conf->currency != $langs->getCurrencySymbol($conf->currency) ? ' - '.$langs->getCurrencySymbol($conf->currency) : '');
 	print ')';
+	print ' - '.$langs->trans("PriceFormatInCurrentLanguage", $langs->defaultlang).' : '.price(price2num('99.333333333','MT'), 1, $langs, 1, -1, -1, $conf->currency);
 	print '</td></tr>';
 
 

+ 20 - 2
htdocs/admin/dav.php

@@ -83,7 +83,16 @@ if ($action == 'edit')
 
 		print '<tr class="oddeven"><td>';
 		print $form->textwithpicto($langs->trans($key), $langs->trans($key.'Tooltip'));
-		print '</td><td><input name="'.$key.'"  class="flat '.(empty($val['css'])?'minwidth200':$val['css']).'" value="' . $conf->global->$key . '"></td></tr>';
+		print '</td><td>';
+		if ($key == 'DAV_ALLOW_PUBLIC_DIR' || $key == 'DAV_ALLOW_ECM_DIR')
+		{
+			print $form->selectyesno($key, $conf->global->$key, 1);
+		}
+		else
+		{
+			print '<input name="'.$key.'"  class="flat '.(empty($val['css'])?'minwidth200':$val['css']).'" value="' . $conf->global->$key . '">';
+		}
+		print '</td></tr>';
 	}
 
 	print '</table>';
@@ -104,7 +113,16 @@ else
 	{
 		print '<tr class="oddeven"><td>';
 		print $form->textwithpicto($langs->trans($key),$langs->trans($key.'Tooltip'));
-		print '</td><td>' . $conf->global->$key . '</td></tr>';
+		print '</td><td>';
+		if ($key == 'DAV_ALLOW_PUBLIC_DIR' || $key == 'DAV_ALLOW_ECM_DIR')
+		{
+			print yn($conf->global->$key);
+		}
+		else
+		{
+			print $conf->global->$key;
+		}
+		print '</td></tr>';
 	}
 
 	print '</table>';

+ 4 - 2
htdocs/admin/defaultvalues.php

@@ -383,8 +383,10 @@ if ($result)
     		print '</td>';
 		}
 
-		// Multicompany
-		print '<td></td>';
+		if (! empty($conf->multicompany->enabled) && !$user->entity)
+		{
+		    print '<td></td>';
+		}
 
 		// Actions
 		print '<td align="center">';

+ 11 - 11
htdocs/admin/dict.php

@@ -793,7 +793,7 @@ if (GETPOST('actionadd') || GETPOST('actionmodify'))
             else $sql.="'".$db->escape($_POST[$listfieldvalue[$i]])."'";
             $i++;
         }
-        $sql.= " WHERE ".$rowidcol." = '".$rowid."'";
+        $sql.= " WHERE ".$rowidcol." = '".$db->escape($rowid)."'";
         if (in_array('entity', $listfieldmodify)) $sql.= " AND entity = '".getEntity($tabname[$id])."'";
 
         dol_syslog("actionmodify", LOG_DEBUG);
@@ -817,7 +817,7 @@ if ($action == 'confirm_delete' && $confirm == 'yes')       // delete
     if ($tabrowid[$id]) { $rowidcol=$tabrowid[$id]; }
     else { $rowidcol="rowid"; }
 
-    $sql = "DELETE FROM ".$tabname[$id]." WHERE ".$rowidcol."='".$rowid."'".($entity != '' ? " AND entity = " . (int) $entity : '');
+    $sql = "DELETE FROM ".$tabname[$id]." WHERE ".$rowidcol."='".$db->escape($rowid)."'".($entity != '' ? " AND entity = " . (int) $entity : '');
 
     dol_syslog("delete", LOG_DEBUG);
     $result = $db->query($sql);
@@ -841,7 +841,7 @@ if ($action == $acts[0])
     else { $rowidcol="rowid"; }
 
     if ($rowid) {
-    	$sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE ".$rowidcol."='".$rowid."'".($entity != '' ? " AND entity = " . (int) $entity : '');
+        $sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE ".$rowidcol."='".$db->escape($rowid)."'".($entity != '' ? " AND entity = " . (int) $entity : '');
     }
     elseif ($code) {
     	$sql = "UPDATE ".$tabname[$id]." SET active = 1 WHERE code='".dol_escape_htmltag($code)."'".($entity != '' ? " AND entity = " . (int) $entity : '');
@@ -861,7 +861,7 @@ if ($action == $acts[1])
     else { $rowidcol="rowid"; }
 
     if ($rowid) {
-    	$sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE ".$rowidcol."='".$rowid."'".($entity != '' ? " AND entity = " . (int) $entity : '');
+        $sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE ".$rowidcol."='".$db->escape($rowid)."'".($entity != '' ? " AND entity = " . (int) $entity : '');
     }
     elseif ($code) {
     	$sql = "UPDATE ".$tabname[$id]." SET active = 0 WHERE code='".dol_escape_htmltag($code)."'".($entity != '' ? " AND entity = " . (int) $entity : '');
@@ -881,7 +881,7 @@ if ($action == 'activate_favorite')
     else { $rowidcol="rowid"; }
 
     if ($rowid) {
-    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE ".$rowidcol."='".$rowid."'".($entity != '' ? " AND entity = " . (int) $entity : '');
+        $sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE ".$rowidcol."='".$db->escape($rowid)."'".($entity != '' ? " AND entity = " . (int) $entity : '');
     }
     elseif ($code) {
     	$sql = "UPDATE ".$tabname[$id]." SET favorite = 1 WHERE code='".dol_escape_htmltag($code)."'".($entity != '' ? " AND entity = " . (int) $entity : '');
@@ -901,7 +901,7 @@ if ($action == 'disable_favorite')
     else { $rowidcol="rowid"; }
 
     if ($rowid) {
-    	$sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE ".$rowidcol."='".$rowid."'".($entity != '' ? " AND entity = " . (int) $entity : '');
+        $sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE ".$rowidcol."='".$db->escape($rowid)."'".($entity != '' ? " AND entity = " . (int) $entity : '');
     }
     elseif ($code) {
     	$sql = "UPDATE ".$tabname[$id]." SET favorite = 0 WHERE code='".dol_escape_htmltag($code)."'".($entity != '' ? " AND entity = " . (int) $entity : '');
@@ -966,7 +966,7 @@ if (GETPOST('from')) $paramwithsearch.= '&from='.urlencode(GETPOST('from','alpha
 // Confirmation de la suppression de la ligne
 if ($action == 'delete')
 {
-    print $form->formconfirm($_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'rowid='.$rowid.'&code='.urlencode($code).$paramwithsearch, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_delete','',0,1);
+    print $form->formconfirm($_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'rowid='.urlencode($rowid).'&code='.urlencode($code).$paramwithsearch, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_delete','',0,1);
 }
 //var_dump($elementList);
 
@@ -1373,8 +1373,8 @@ if ($id)
 
                     print '<td colspan="3" align="center">';
                     print '<div name="'.(! empty($obj->rowid)?$obj->rowid:$obj->code).'"></div>';
-                    print '<input type="hidden" name="page" value="'.$page.'">';
-                    print '<input type="hidden" name="rowid" value="'.$rowid.'">';
+                    print '<input type="hidden" name="page" value="'.dol_escape_htmltag($page).'">';
+                    print '<input type="hidden" name="rowid" value="'.dol_escape_htmltag($rowid).'">';
                     if (! is_null($withentity))
                     	print '<input type="hidden" name="entity" value="'.$withentity.'">';
                     print '<input type="submit" class="button" name="actionmodify" value="'.$langs->trans("Modify").'">';
@@ -1628,14 +1628,14 @@ if ($id)
                     if ($id == 4)
 					{
 						print '<td align="center" class="nowrap">';
-						if ($iserasable) print '<a href="'.$url.'action='.$acts[$obj->favorite].'_favorite">'.$actl[$obj->favorite].'</a>';
+						if ($iserasable) print '<a class="reposition" href="'.$url.'action='.$acts[$obj->favorite].'_favorite">'.$actl[$obj->favorite].'</a>';
 						else print $langs->trans("AlwaysActive");
 						print '</td>';
 					}
 
                     // Active
                     print '<td align="center" class="nowrap">';
-                    if ($canbedisabled) print '<a href="'.$url.'action='.$acts[$obj->active].'">'.$actl[$obj->active].'</a>';
+                    if ($canbedisabled) print '<a class="reposition" href="'.$url.'action='.$acts[$obj->active].'">'.$actl[$obj->active].'</a>';
                     else
                  	{
                  		if (in_array($obj->code, array('AC_OTH','AC_OTH_AUTO'))) print $langs->trans("AlwaysActive");

+ 1 - 1
htdocs/admin/dolistore/class/dolistore.class.php

@@ -65,7 +65,7 @@ class Dolistore
 
 		$langtmp    = explode('_', $langs->defaultlang);
 		$lang       = $langtmp[0];
-		$lang_array = array('en'=>1, 'fr'=>2, 'es'=>3, 'it'=>4, 'de'=>5);	// Into table ps_lang of Prestashop - 1
+		$lang_array = array('en'=>0, 'fr'=>1, 'es'=>2, 'it'=>3, 'de'=>4);	// Into table ps_lang of Prestashop - 1
 		if (! in_array($lang, array_keys($lang_array))) $lang = 'en';
 		$this->lang = $lang_array[$lang];
 	}

+ 24 - 5
htdocs/admin/emailcollector_card.php

@@ -380,11 +380,24 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
 	$sourcedir = $object->source_directory;
 	$targetdir = ($object->target_directory ? $object->target_directory : '');			// Can be '[Gmail]/Trash' or 'mytag'
 
-	$connectstringserver = $object->getConnectStringIMAP();
-	$connectstringsource = $connectstringserver.imap_utf7_encode($sourcedir);
-	$connectstringtarget = $connectstringserver.imap_utf7_encode($targetdir);
+	$connection = null;
+	$connectstringserver = '';
+	$connectstringsource = '';
+	$connectstringtarget = '';
+
+	if (function_exists('imap_open'))
+	{
+		$connectstringserver = $object->getConnectStringIMAP();
+		$connectstringsource = $connectstringserver.imap_utf7_encode($sourcedir);
+		$connectstringtarget = $connectstringserver.imap_utf7_encode($targetdir);
+
+		$connection = imap_open($connectstringsource, $object->user, $object->password);
+	}
+	else
+	{
+		$morehtml .= 'IMAP functions not available on your PHP';
+	}
 
-	$connection = imap_open($connectstringsource, $object->user, $object->password);
 	if (! $connection)
 	{
 		$morehtml .= 'Failed to open IMAP connection '.$connectstringsource;
@@ -395,7 +408,10 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
 		$morehtml .= imap_num_msg($connection);
 	}
 
-	imap_close($connection);
+	if ($connection)
+	{
+		imap_close($connection);
+	}
 
 	dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref.'<div class="refidno">'.$morehtml.'</div>', '', 0, '', '', 0, '');
 
@@ -471,6 +487,9 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
 	print $form->selectarray('operationtype', $arrayoftypes, '', 1, 0, 0, '', 1);
 	print '</td><td>';
 	print '<input type="text" name="operationparam">';
+	$htmltext=$langs->transnoentitiesnoconv("OperationParamDesc");
+	//var_dump($htmltext);
+	print $form->textwithpicto('', $htmltext);
 	print '</td>';
 	print '<td></td>';
 	print '<td align="right"><input type="submit" name="addoperation" id="addoperation" class="flat button" value="'.$langs->trans("Add").'"></td>';

+ 52 - 42
htdocs/admin/emailcollector_list.php

@@ -120,8 +120,6 @@ $arrayfields = dol_sort_array($arrayfields, 'position');
 
 /*
  * Actions
- *
- * Put here all code to do according to value of "$action" parameter
  */
 
 if (GETPOST('cancel','alpha')) { $action='list'; $massaction=''; }
@@ -165,8 +163,6 @@ if (empty($reshook))
 
 /*
  * View
- *
- * Put here all code to render page
  */
 
 $form=new Form($db);
@@ -212,20 +208,20 @@ $reshook=$hookmanager->executeHooks('printFieldListWhere', $parameters, $object)
 $sql.=$hookmanager->resPrint;
 
 /* If a group by is required
- $sql.= " GROUP BY "
- foreach($object->fields as $key => $val)
- {
- $sql.='t.'.$key.', ';
- }
- // Add fields from extrafields
- if (! empty($extrafields->attributes[$object->table_element]['label'])) {
- foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.', ' : '');
- // Add where from hooks
- $parameters=array();
- $reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters);    // Note that $action and $object may have been modified by hook
- $sql.=$hookmanager->resPrint;
- $sql=preg_replace('/, $/','', $sql);
- */
+$sql.= " GROUP BY "
+foreach($object->fields as $key => $val)
+{
+	$sql.='t.'.$key.', ';
+}
+// Add fields from extrafields
+if (! empty($extrafields->attributes[$object->table_element]['label'])) {
+	foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.', ' : '');
+// Add where from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters);    // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+$sql=preg_replace('/, $/','', $sql);
+*/
 
 $sql.=$db->order($sortfield,$sortorder);
 
@@ -305,8 +301,8 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
 
 // List of mass actions available
 $arrayofmassactions =  array(
-//'presend'=>$langs->trans("SendByMail"),
-//'builddoc'=>$langs->trans("PDFMerge"),
+	//'presend'=>$langs->trans("SendByMail"),
+	//'builddoc'=>$langs->trans("PDFMerge"),
 );
 if ($user->rights->emailcollector->delete) $arrayofmassactions['predelete']=$langs->trans("Delete");
 if (GETPOST('nomassaction','int') || in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array();
@@ -324,7 +320,7 @@ print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
 
 $newcardbutton='';
 //if ($user->rights->emailcollector->creer)
-	//{
+//{
 $newcardbutton='<a class="butActionNew" href="emailcollector_card.php?action=create&backtopage='.urlencode($_SERVER['PHP_SELF']).'"><span class="valignmiddle">'.$langs->trans('New').'</span>';
 $newcardbutton.= '<span class="fa fa-plus-circle valignmiddle"></span>';
 $newcardbutton.= '</a>';
@@ -333,6 +329,10 @@ $newcardbutton.= '</a>';
 print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_companies', 0, $newcardbutton, '', $limit);
 
 // Add code for pre mass action (confirmation or email presend form)
+/*$topicmail="";
+$modelmail="";
+$objecttmp=new EmailCollector($db);
+$trackid='xxxx'.$object->id;*/
 include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php';
 
 if ($sall)
@@ -343,8 +343,8 @@ if ($sall)
 
 $moreforfilter = '';
 /*$moreforfilter.='<div class="divsearchfield">';
- $moreforfilter.= $langs->trans('MyFilter') . ': <input type="text" name="search_myfield" value="'.dol_escape_htmltag($search_myfield).'">';
- $moreforfilter.= '</div>';*/
+$moreforfilter.= $langs->trans('MyFilter') . ': <input type="text" name="search_myfield" value="'.dol_escape_htmltag($search_myfield).'">';
+$moreforfilter.= '</div>';*/
 
 $parameters=array();
 $reshook=$hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object);    // Note that $action and $object may have been modified by hook
@@ -371,11 +371,11 @@ print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"")
 print '<tr class="liste_titre">';
 foreach($object->fields as $key => $val)
 {
-	$align='';
-	if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center';
-	if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap';
-	if ($key == 'status') $align.=($align?' ':'').'center';
-	if (! empty($arrayfields['t.'.$key]['checked'])) print '<td class="liste_titre'.($align?' '.$align:'').'"><input type="text" class="flat maxwidth75" name="search_'.$key.'" value="'.dol_escape_htmltag($search[$key]).'"></td>';
+    $cssforfield='';
+    if (in_array($val['type'], array('date','datetime','timestamp'))) $cssforfield.=($cssforfield?' ':'').'center';
+    if (in_array($val['type'], array('timestamp'))) $cssforfield.=($cssforfield?' ':'').'nowrap';
+    if ($key == 'status') $cssforfield.=($cssforfield?' ':'').'center';
+    if (! empty($arrayfields['t.'.$key]['checked'])) print '<td class="liste_titre'.($cssforfield?' '.$cssforfield:'').'"><input type="text" class="flat maxwidth75" name="search_'.$key.'" value="'.dol_escape_htmltag($search[$key]).'"></td>';
 }
 // Extra fields
 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php';
@@ -397,11 +397,14 @@ print '</tr>'."\n";
 print '<tr class="liste_titre">';
 foreach($object->fields as $key => $val)
 {
-	$align='';
-	if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center';
-	if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap';
-	if ($key == 'status') $align.=($align?' ':'').'center';
-	if (! empty($arrayfields['t.'.$key]['checked'])) print getTitleFieldOfList($arrayfields['t.'.$key]['label'], 0, $_SERVER['PHP_SELF'], 't.'.$key, '', $param, ($align?'class="'.$align.'"':''), $sortfield, $sortorder, $align.' ')."\n";
+    $cssforfield='';
+    if (in_array($val['type'], array('date','datetime','timestamp'))) $cssforfield.=($cssforfield?' ':'').'center';
+    if (in_array($val['type'], array('timestamp'))) $cssforfield.=($cssforfield?' ':'').'nowrap';
+    if ($key == 'status') $cssforfield.=($cssforfield?' ':'').'center';
+    if (! empty($arrayfields['t.'.$key]['checked']))
+    {
+        print getTitleFieldOfList($arrayfields['t.'.$key]['label'], 0, $_SERVER['PHP_SELF'], 't.'.$key, '', $param, ($cssforfield?'class="'.$cssforfield.'"':''), $sortfield, $sortorder, ($cssforfield?$cssforfield.' ':''))."\n";
+    }
 }
 // Extra fields
 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php';
@@ -444,15 +447,22 @@ while ($i < min($num, $limit))
 	print '<tr class="oddeven">';
 	foreach($object->fields as $key => $val)
 	{
-		$align='';
-		if (in_array($val['type'], array('date','datetime','timestamp'))) $align.=($align?' ':'').'center';
-		if (in_array($val['type'], array('timestamp'))) $align.=($align?' ':'').'nowrap';
-		if ($key == 'status') $align.=($align?' ':'').'center';
-		if (! empty($arrayfields['t.'.$key]['checked']))
-		{
-			print '<td';
-			if ($align) print ' class="'.$align.'"';
-			print '>';
+	    $cssforfield='';
+	    if (in_array($val['type'], array('date','datetime','timestamp'))) $cssforfield.=($cssforfield?' ':'').'center';
+	    elseif ($key == 'status') $cssforfield.=($cssforfield?' ':'').'center';
+	    
+	    if (in_array($val['type'], array('timestamp'))) $cssforfield.=($cssforfield?' ':'').'nowrap';
+	    elseif ($key == 'ref') $cssforfield.=($cssforfield?' ':'').'nowrap';
+	    
+	    if (! empty($arrayfields['t.'.$key]['checked']))
+	    {
+	        print '<td';
+	        if ($cssforfield || $val['css']) print ' class="';
+	        print $cssforfield;
+	        if ($cssforfield && $val['css']) print ' ';
+	        print $val['css'];
+	        if ($cssforfield || $val['css']) print '"';
+	        print '>';
 			print $object->showOutputField($val, $key, $obj->$key, '');
 			print '</td>';
 			if (! $i) $totalarray['nbfield']++;

+ 53 - 51
htdocs/admin/mails_templates.php

@@ -75,7 +75,7 @@ $offset = $listlimit * $page ;
 $pageprev = $page - 1;
 $pagenext = $page + 1;
 
-if (empty($sortfield)) $sortfield='label, lang, position';
+if (empty($sortfield)) $sortfield='type_template, lang, position, label';
 if (empty($sortorder)) $sortorder='ASC';
 
 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
@@ -85,10 +85,6 @@ $hookmanager->initHooks(array('emailtemplates'));
 $tabname=array();
 $tabname[25]= MAIN_DB_PREFIX."c_email_templates";
 
-// Criteria to sort dictionaries
-$tabsqlsort=array();
-$tabsqlsort[25]="label ASC, lang ASC, position ASC";
-
 // Nom des champs en resultat de select pour affichage du dictionnaire
 $tabfield=array();
 $tabfield[25]= "label,lang,type_template,fk_user,private,position,topic,joinfiles,content";
@@ -448,8 +444,6 @@ $sql.=$db->plimit($listlimit+1,$offset);
 $fieldlist=explode(',',$tabfield[$id]);
 
 // Form to add a new line
-$alabelisused=0;
-
 print '<form action="'.$_SERVER['PHP_SELF'].'?id='.$id.'" method="POST">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print '<input type="hidden" name="from" value="'.dol_escape_htmltag(GETPOST('from','alpha')).'">';
@@ -461,38 +455,37 @@ print '<table class="noborder" width="100%">';
 print '<tr class="liste_titre">';
 foreach ($fieldlist as $field => $value)
 {
-        // Determine le nom du champ par rapport aux noms possibles
-        // dans les dictionnaires de donnees
-        $valuetoshow=ucfirst($fieldlist[$field]);   // Par defaut
-        $valuetoshow=$langs->trans($valuetoshow);   // try to translate
-        $align="left";
-        if ($fieldlist[$field]=='fk_user')         { $valuetoshow=$langs->trans("Owner");}
-        if ($fieldlist[$field]=='lang')            { $valuetoshow=(empty($conf->global->MAIN_MULTILANGS) ? '&nbsp;' : $langs->trans("Language")); }
-        if ($fieldlist[$field]=='type')            { $valuetoshow=$langs->trans("Type"); }
-        if ($fieldlist[$field]=='code')            { $valuetoshow=$langs->trans("Code"); }
-        if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') { $valuetoshow=$langs->trans("Code"); }
-        if ($fieldlist[$field]=='type_template')   { $valuetoshow=$langs->trans("TypeOfTemplate"); }
-    	if ($fieldlist[$field]=='private')         { $align='center'; }
-    	if ($fieldlist[$field]=='position')        { $align='center'; }
-
-    	if ($fieldlist[$field]=='topic')           { $valuetoshow=''; }
-    	if ($fieldlist[$field]=='joinfiles')       { $valuetoshow=''; }
-    	if ($fieldlist[$field]=='content')         { $valuetoshow=''; }
-    	if ($fieldlist[$field]=='content_lines')   { $valuetoshow=''; }
-
-        if ($valuetoshow != '')
-        {
-            print '<td align="'.$align.'">';
-        	if (! empty($tabhelp[$id][$value]) && preg_match('/^http(s*):/i',$tabhelp[$id][$value])) print '<a href="'.$tabhelp[$id][$value].'" target="_blank">'.$valuetoshow.' '.img_help(1,$valuetoshow).'</a>';
-        	else if (! empty($tabhelp[$id][$value]))
-        	{
-        	    if (in_array($value, array('topic'))) print $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2, $value);   // Tooltip on click
-        	    else print $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2);                             // Tooltip on hover
-        	}
-        	else print $valuetoshow;
-            print '</td>';
-         }
-         if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') $alabelisused=1;
+	// Determine le nom du champ par rapport aux noms possibles
+	// dans les dictionnaires de donnees
+	$valuetoshow=ucfirst($fieldlist[$field]);   // Par defaut
+	$valuetoshow=$langs->trans($valuetoshow);   // try to translate
+	$align="left";
+	if ($fieldlist[$field]=='fk_user')         { $valuetoshow=$langs->trans("Owner");}
+	if ($fieldlist[$field]=='lang')            { $valuetoshow=(empty($conf->global->MAIN_MULTILANGS) ? '&nbsp;' : $langs->trans("Language")); }
+	if ($fieldlist[$field]=='type')            { $valuetoshow=$langs->trans("Type"); }
+	if ($fieldlist[$field]=='code')            { $valuetoshow=$langs->trans("Code"); }
+	if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') { $valuetoshow=$langs->trans("Code"); }
+	if ($fieldlist[$field]=='type_template')   { $valuetoshow=$langs->trans("TypeOfTemplate"); }
+	if ($fieldlist[$field]=='private')         { $align='center'; }
+	if ($fieldlist[$field]=='position')        { $align='center'; }
+
+	if ($fieldlist[$field]=='topic')           { $valuetoshow=''; }
+	if ($fieldlist[$field]=='joinfiles')       { $valuetoshow=''; }
+	if ($fieldlist[$field]=='content')         { $valuetoshow=''; }
+	if ($fieldlist[$field]=='content_lines')   { $valuetoshow=''; }
+
+	if ($valuetoshow != '')
+	{
+		print '<td align="'.$align.'">';
+		if (! empty($tabhelp[$id][$value]) && preg_match('/^http(s*):/i',$tabhelp[$id][$value])) print '<a href="'.$tabhelp[$id][$value].'" target="_blank">'.$valuetoshow.' '.img_help(1,$valuetoshow).'</a>';
+		else if (! empty($tabhelp[$id][$value]))
+		{
+			if (in_array($value, array('topic'))) print $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2, $value);   // Tooltip on click
+			else print $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2);                             // Tooltip on hover
+		}
+		else print $valuetoshow;
+		print '</td>';
+	}
 }
 print '<td>';
 print '<input type="hidden" name="id" value="' . $id . '">';
@@ -535,6 +528,7 @@ print '<td align="right">';
 print '</td>';
 print "</tr>";
 
+// Show fields for topic, join files and body
 $fieldsforcontent = array('topic', 'joinfiles', 'content');
 if (! empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES)) { $fieldsforcontent = array('content','content_lines'); }
 foreach ($fieldsforcontent as $tmpfieldlist)
@@ -612,10 +606,16 @@ if ($resql)
     $i = 0;
 
     $param = '&id='.$id;
+    if ($search_label)             $param.= '&search_label='.urlencode($search_label);
+    if ($search_lang > 0)          $param.= '&search_lang='.urlencode($search_lang);
+    if ($search_type_template > 0) $param.= '&search_type_template='.urlencode($search_type_template);
+    if ($search_fk_user > 0)       $param.= '&search_fk_user='.urlencode($search_fk_user);
+    if ($search_topic)             $param.= '&search_topic='.urlencode($search_topic);
+
     $paramwithsearch = $param;
-    if ($sortorder) $paramwithsearch.= '&sortorder='.$sortorder;
-    if ($sortfield) $paramwithsearch.= '&sortfield='.$sortfield;
-    if (GETPOST('from')) $paramwithsearch.= '&from='.GETPOST('from','alpha');
+    if ($sortorder) $paramwithsearch.= '&sortorder='.urlencode($sortorder);
+    if ($sortfield) $paramwithsearch.= '&sortfield='.urlencode($sortfield);
+    if (GETPOST('from','alpha')) $paramwithsearch.= '&from='.urlencode(GETPOST('from','alpha'));
 
     // There is several pages
     if ($num > $listlimit)
@@ -631,11 +631,11 @@ if ($resql)
     $filterfound=0;
     foreach ($fieldlist as $field => $value)
     {
-        if ($value == 'label') print '<td class="liste_titre"><input type="text" name="search_label" value="'.dol_escape_htmltag($search_label).'"></td>';
+        if ($value == 'label') print '<td class="liste_titre"><input type="text" name="search_label" class="maxwidth100" value="'.dol_escape_htmltag($search_label).'"></td>';
         elseif ($value == 'lang')
         {
         	print '<td class="liste_titre">';
-        	print $formadmin->select_language($search_lang, 'search_lang', 0, null, 1, 0, 0, 'maxwidth150');
+        	print $formadmin->select_language($search_lang, 'search_lang', 0, null, 1, 0, 0, 'maxwidth100');
         	print '</td>';
         }
         elseif ($value == 'fk_user')
@@ -644,13 +644,13 @@ if ($resql)
         	$restrictid=array();
         	if (! $user->admin) $restrictid=array($user->id);
         	//var_dump($restrictid);
-        	print $form->select_dolusers($search_fk_user, 'search_fk_user', 1, null, 0, 'hierarchyme', null, 0, 0, 1, '', 0, '', 'maxwidth200');
+        	print $form->select_dolusers($search_fk_user, 'search_fk_user', 1, null, 0, 'hierarchyme', null, 0, 0, 1, '', 0, '', 'maxwidth100');
         	print '</td>';
         }
         elseif ($value == 'topic') print '<td class="liste_titre"><input type="text" name="search_topic" value="'.dol_escape_htmltag($search_topic).'"></td>';
         elseif ($value == 'type_template')
         {
-        	print '<td class="liste_titre">'.$form->selectarray('search_type_template', $elementList, $search_type_template, 1, 0, 0, '', 0, 0, 0, '', 'maxwidth200 maxwidth100onsmartphone').'</td>';
+        	print '<td class="liste_titre">'.$form->selectarray('search_type_template', $elementList, $search_type_template, 1, 0, 0, '', 0, 0, 0, '', 'maxwidth100 maxwidth100onsmartphone').'</td>';
         }
         elseif (! in_array($value, array('content', 'content_lines'))) print '<td class="liste_titre"></td>';
     }
@@ -672,6 +672,7 @@ if ($resql)
         $align="left";
         $sortable=1;
         $valuetoshow='';
+        $forcenowrap=1;
         /*
         $tmparray=getLabelOfField($fieldlist[$field]);
         $showfield=$tmp['showfield'];
@@ -689,7 +690,7 @@ if ($resql)
 		if ($fieldlist[$field]=='private')         { $align='center'; }
 		if ($fieldlist[$field]=='position')        { $align='center'; }
 
-		if ($fieldlist[$field]=='joinfiles')       { $valuetoshow=$langs->trans("FilesAttachedToEmail"); $align='center'; }
+		if ($fieldlist[$field]=='joinfiles')       { $valuetoshow=$langs->trans("FilesAttachedToEmail"); $align='center'; $forcenowrap=0; }
 		if ($fieldlist[$field]=='content')         { $valuetoshow=$langs->trans("Content"); $showfield=0;}
 		if ($fieldlist[$field]=='content_lines')   { $valuetoshow=$langs->trans("ContentLines"); $showfield=0; }
 
@@ -698,8 +699,8 @@ if ($resql)
         {
             if (! empty($tabhelp[$id][$value]))
             {
-                if (in_array($value, array('topic'))) $valuetoshow = $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2, 'tooltip'.$value);   // Tooltip on click
-                else $valuetoshow = $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2, '', 1);	// Tooltip on hover
+            	if (in_array($value, array('topic'))) $valuetoshow = $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2, 'tooltip'.$value, $forcenowrap);   // Tooltip on click
+                else $valuetoshow = $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2, '', $forcenowrap);	// Tooltip on hover
             }
             print getTitleFieldOfList($valuetoshow, 0, $_SERVER["PHP_SELF"], ($sortable?$fieldlist[$field]:''), ($page?'page='.$page.'&':''), $param, "align=".$align, $sortfield, $sortorder);
         }
@@ -1024,17 +1025,18 @@ function fieldList($fieldlist, $obj='', $tabname='', $context='')
 			}
 			else
 			{
-				print $form->selectarray('type_template', $elementList, (! empty($obj->{$fieldlist[$field]})?$obj->{$fieldlist[$field]}:''), 1, 0, 0, '', 0, 0, 0, '', 'maxwidth200');
+				print $form->selectarray('type_template', $elementList, (! empty($obj->{$fieldlist[$field]})?$obj->{$fieldlist[$field]}:''), 1, 0, 0, '', 0, 0, 0, '', 'maxwidth150 maxwidth100onsmartphone');
 			}
 			print '</td>';
 		}
-		elseif ($context == 'add' && in_array($fieldlist[$field], array('topic', 'joinfiles', 'content', 'content_lines'))) continue;
+		elseif ($context == 'add'  && in_array($fieldlist[$field], array('topic', 'joinfiles', 'content', 'content_lines'))) continue;
 		elseif ($context == 'edit' && in_array($fieldlist[$field], array('topic', 'joinfiles', 'content', 'content_lines'))) continue;
 		elseif ($context == 'hide' && in_array($fieldlist[$field], array('topic', 'joinfiles', 'content', 'content_lines'))) continue;
 		else
 		{
 			$size=''; $class=''; $classtd='';
 			if ($fieldlist[$field]=='code') $class='maxwidth100';
+			if ($fieldlist[$field]=='label') $class='maxwidth100';
 			if ($fieldlist[$field]=='private') { $class='maxwidth50'; $classtd='center'; }
 			if ($fieldlist[$field]=='position') { $class='maxwidth50'; $classtd='center'; }
 			if ($fieldlist[$field]=='libelle') $class='quatrevingtpercent';

+ 46 - 21
htdocs/admin/menus/edit.php

@@ -96,6 +96,7 @@ if ($action == 'update')
             $menu->perms=GETPOST('perms','alpha');
             $menu->target=GETPOST('target','alpha');
             $menu->user=GETPOST('user','alpha');
+            $menu->mainmenu=GETPOST('propertymainmenu','alpha');
             if (is_numeric(GETPOST('menuIdParent','alpha')))
             {
             	$menu->fk_menu=GETPOST('menuIdParent','alpha');
@@ -123,14 +124,11 @@ if ($action == 'update')
 	        setEventMessages($menu->error, $menu->errors, 'errors');
         }
         $action = "edit";
-    }
-    else
-    {
+
         header("Location: ".DOL_URL_ROOT."/admin/menus/index.php?menu_handler=".$menu_handler);
         exit;
     }
-
-    if ($_GET['return'])
+    else
     {
         header("Location: ".DOL_URL_ROOT."/admin/menus/index.php?menu_handler=".$menu_handler);
         exit;
@@ -215,6 +213,7 @@ if ($action == 'add')
         $menu->perms=GETPOST('perms','alpha');
         $menu->target=GETPOST('target','alpha');
         $menu->user=GETPOST('user','alpha');
+        $menu->mainmenu=GETPOST('propertymainmenu','alpha');
         if (is_numeric(GETPOST('menuId','int')))
         {
         	$menu->fk_menu=GETPOST('menuId','int');
@@ -289,10 +288,13 @@ if ($action == 'create')
     		{
 				jQuery("#menuId").prop("disabled", true);
 	    		jQuery("#menuId").val(\'\');
+				jQuery("#propertymainmenu").removeAttr("disabled");
+	    		jQuery("#propertymainmenu").val(\'\');
 			}
-    		else
+    		if (jQuery("#topleft").val() == \'left\')
     		{
 				jQuery("#menuId").removeAttr("disabled");
+				jQuery("#propertymainmenu").prop("disabled", true);
     		}
     	}
     	init_topleft();
@@ -338,7 +340,7 @@ if ($action == 'create')
     print '</td>';
     print '<td>'.$langs->trans('DetailMenuHandler').'</td></tr>';
 
-    //User
+    // User
     print '<tr><td class="nowrap fieldrequired">'.$langs->trans('MenuForUsers').'</td>';
     print '<td><select class="flat" name="user">';
     print '<option value="2" selected>'.$langs->trans("AllMenus").'</option>';
@@ -362,9 +364,15 @@ if ($action == 'create')
         print '<option value="left"'.($_POST["type"] && $_POST["type"]=='left'?' selected':'').'>'.$langs->trans('Left').'</option>';
         print '</select>';
     }
-    //	print '<input type="text" size="50" name="type" value="'.$type.'">';
     print '</td><td>'.$langs->trans('DetailType').'</td></tr>';
 
+    // Mainmenu code
+    print '<tr><td class="fieldrequired">'.$langs->trans('MainMenuCode').'</td>';
+   	print '<td><input type="text" class="minwidth300" id="propertymainmenu" name="propertymainmenu" value="'.(GETPOST("propertymainmenu", 'alpha')?GETPOST("propertymainmenu", 'alpha'):'').'"></td>';
+    print '<td>';
+    print $langs->trans("Example").': mytopmenukey';
+    print '</td></tr>';
+
     // MenuId Parent
     print '<tr><td class="fieldrequired">'.$langs->trans('MenuIdParent').'</td>';
     if ($parent_rowid)
@@ -373,23 +381,23 @@ if ($action == 'create')
     }
     else
     {
-        print '<td><input type="text" size="48" id="menuId" name="menuId" value="'.(GETPOST("menuId", 'int')?GETPOST("menuId", 'int'):'').'"></td>';
+        print '<td><input type="text" class="minwidth300" id="menuId" name="menuId" value="'.(GETPOST("menuId", 'int')?GETPOST("menuId", 'int'):'').'"></td>';
     }
     print '<td>'.$langs->trans('DetailMenuIdParent');
     print ', '.$langs->trans("Example").': fk_mainmenu=abc&fk_leftmenu=def';
     print '</td></tr>';
 
     // Title
-    print '<tr><td class="fieldrequired">'.$langs->trans('Title').'</td><td><input type="text" size="30" name="titre" value="'.dol_escape_htmltag(GETPOST("titre",'alpha')).'"></td><td>'.$langs->trans('DetailTitre').'</td></tr>';
+    print '<tr><td class="fieldrequired">'.$langs->trans('Title').'</td><td><input type="text" class="minwidth300" name="titre" value="'.dol_escape_htmltag(GETPOST("titre",'alpha')).'"></td><td>'.$langs->trans('DetailTitre').'</td></tr>';
 
     // URL
-    print '<tr><td class="fieldrequired">'.$langs->trans('URL').'</td><td><input type="text" size="60" name="url" value="'.GETPOST("url",'alpha').'"></td><td>'.$langs->trans('DetailUrl').'</td></tr>';
+    print '<tr><td class="fieldrequired">'.$langs->trans('URL').'</td><td><input type="text" class="minwidth500" name="url" value="'.GETPOST("url",'alpha').'"></td><td>'.$langs->trans('DetailUrl').'</td></tr>';
 
     // Langs
-    print '<tr><td>'.$langs->trans('LangFile').'</td><td><input type="text" size="30" name="langs" value="'.$parent_langs.'"></td><td>'.$langs->trans('DetailLangs').'</td></tr>';
+    print '<tr><td>'.$langs->trans('LangFile').'</td><td><input type="text" class="minwidth300" name="langs" value="'.$parent_langs.'"></td><td>'.$langs->trans('DetailLangs').'</td></tr>';
 
     // Position
-    print '<tr><td>'.$langs->trans('Position').'</td><td><input type="text" size="5" name="position" value="'.dol_escape_htmltag(isset($_POST["position"])?$_POST["position"]:100).'"></td><td>'.$langs->trans('DetailPosition').'</td></tr>';
+    print '<tr><td>'.$langs->trans('Position').'</td><td><input type="text" class="width100" name="position" value="'.dol_escape_htmltag(isset($_POST["position"])?$_POST["position"]:100).'"></td><td>'.$langs->trans('DetailPosition').'</td></tr>';
 
     // Target
     print '<tr><td>'.$langs->trans('Target').'</td><td><select class="flat" name="target">';
@@ -398,10 +406,10 @@ if ($action == 'create')
     print '</select></td></td><td>'.$langs->trans('DetailTarget').'</td></tr>';
 
     // Enabled
-    print '<tr><td>'.$langs->trans('Enabled').'</td><td><input type="text" size="60" name="enabled" value="'.GETPOST("enabled",'alpha').'"></td><td>'.$langs->trans('DetailEnabled').'</td></tr>';
+    print '<tr><td>'.$langs->trans('Enabled').'</td><td><input type="text" class="minwidth500" name="enabled" value="'.(GETPOSTISSET('enabled')?GETPOST("enabled",'alpha'):'1').'"></td><td>'.$langs->trans('DetailEnabled').'</td></tr>';
 
     // Perms
-    print '<tr><td>'.$langs->trans('Rights').'</td><td><input type="text" size="60" name="perms" value="'.GETPOST('perms','alpha').'"></td><td>'.$langs->trans('DetailRight').'</td></tr>';
+    print '<tr><td>'.$langs->trans('Rights').'</td><td><input type="text" class="minwidth500" name="perms" value="'.(GETPOSTISSET('perms')?GETPOST('perms','alpha'):'1').'"></td><td>'.$langs->trans('DetailRight').'</td></tr>';
 
     print '</table>';
 
@@ -455,13 +463,30 @@ elseif ($action == 'edit')
     // Type
     print '<tr><td class="fieldrequired">'.$langs->trans('Type').'</td><td>'.$langs->trans(ucfirst($menu->type)).'</td><td>'.$langs->trans('DetailType').'</td></tr>';
 
+    // Mainmenu code
+    if ($menu->type == 'top')
+    {
+	    print '<tr><td class="fieldrequired">'.$langs->trans('MainMenuCode').'</td>';
+	    /*if ($parent_rowid)
+	     {
+	     print '<td>'.$parent_rowid.'<input type="hidden" name="propertyleftmenu" value="'.$parent_rowid.'"></td>';
+	     }
+	     else
+	     {*/
+	    print '<td><input type="text" class="minwidth300" id="propertymainmenu" name="propertymainmenu" value="'.(GETPOST("propertymainmenu", 'alpha')?GETPOST("propertymainmenu", 'alpha'):$menu->mainmenu).'"></td>';
+	    //}
+	    print '<td>';
+	    print $langs->trans("Example").': mytopmenukey';
+	    print '</td></tr>';
+    }
+
     // MenuId Parent
     print '<tr><td class="fieldrequired">'.$langs->trans('MenuIdParent');
     print '</td>';
     $valtouse=$menu->fk_menu;
     if ($menu->fk_mainmenu) $valtouse='fk_mainmenu='.$menu->fk_mainmenu;
     if ($menu->fk_leftmenu) $valtouse.='&fk_leftmenu='.$menu->fk_leftmenu;
-    print '<td><input type="text" name="menuIdParent" value="'.$valtouse.'" size="48"></td>';
+    print '<td><input type="text" name="menuIdParent" value="'.$valtouse.'" class="minwidth300"></td>';
     print '<td>'.$langs->trans('DetailMenuIdParent');
     print ', '.$langs->trans("Example").': fk_mainmenu=abc&fk_leftmenu=def';
     print '</td></tr>';
@@ -470,16 +495,16 @@ elseif ($action == 'edit')
     //print '<tr><td>'.$langs->trans('Level').'</td><td>'.$menu->level.'</td><td>'.$langs->trans('DetailLevel').'</td></tr>';
 
     // Title
-    print '<tr><td class="fieldrequired">'.$langs->trans('Title').'</td><td><input type="text" size="30" name="titre" value="'.dol_escape_htmltag($menu->titre).'"></td><td>'.$langs->trans('DetailTitre').'</td></tr>';
+    print '<tr><td class="fieldrequired">'.$langs->trans('Title').'</td><td><input type="text" class="minwidth300" name="titre" value="'.dol_escape_htmltag($menu->titre).'"></td><td>'.$langs->trans('DetailTitre').'</td></tr>';
 
     // Url
     print '<tr><td class="fieldrequired">'.$langs->trans('URL').'</td><td><input type="text" class="quatrevingtpercent" name="url" value="'.$menu->url.'"></td><td>'.$langs->trans('DetailUrl').'</td></tr>';
 
     // Langs
-    print '<tr><td>'.$langs->trans('LangFile').'</td><td><input type="text" size="30" name="langs" value="'.dol_escape_htmltag($menu->langs).'"></td><td>'.$langs->trans('DetailLangs').'</td></tr>';
+    print '<tr><td>'.$langs->trans('LangFile').'</td><td><input type="text" class="minwidth300" name="langs" value="'.dol_escape_htmltag($menu->langs).'"></td><td>'.$langs->trans('DetailLangs').'</td></tr>';
 
     // Position
-    print '<tr><td>'.$langs->trans('Position').'</td><td><input type="text" size="5" name="position" value="'.$menu->position.'"></td><td>'.$langs->trans('DetailPosition').'</td></tr>';
+    print '<tr><td>'.$langs->trans('Position').'</td><td><input type="text" class="minwidth100" name="position" value="'.$menu->position.'"></td><td>'.$langs->trans('DetailPosition').'</td></tr>';
 
     // Target
     print '<tr><td>'.$langs->trans('Target').'</td><td><select class="flat" name="target">';
@@ -488,12 +513,12 @@ elseif ($action == 'edit')
     print '</select></td><td>'.$langs->trans('DetailTarget').'</td></tr>';
 
     // Enabled
-    print '<tr><td>'.$langs->trans('Enabled').'</td><td><input type="text" size="60" name="enabled" value="'.dol_escape_htmltag($menu->enabled).'"></td><td>'.$langs->trans('DetailEnabled');
+    print '<tr><td>'.$langs->trans('Enabled').'</td><td><input type="text" class="minwidth500" name="enabled" value="'.dol_escape_htmltag($menu->enabled).'"></td><td>'.$langs->trans('DetailEnabled');
     if (! empty($menu->enabled)) print ' ('.$langs->trans("ConditionIsCurrently").': '.yn(dol_eval($menu->enabled,1)).')';
     print '</td></tr>';
 
     // Perms
-    print '<tr><td>'.$langs->trans('Rights').'</td><td><input type="text" size="60" name="perms" value="'.dol_escape_htmltag($menu->perms).'"></td><td>'.$langs->trans('DetailRight');
+    print '<tr><td>'.$langs->trans('Rights').'</td><td><input type="text" class="minwidth500" name="perms" value="'.dol_escape_htmltag($menu->perms).'"></td><td>'.$langs->trans('DetailRight');
     if (! empty($menu->perms)) print ' ('.$langs->trans("ConditionIsCurrently").': '.yn(dol_eval($menu->perms,1)).')';
     print '</td></tr>';
 

+ 0 - 1
htdocs/admin/system/filecheck.php

@@ -338,7 +338,6 @@ if (! $error && $xml)
                 $out.='<tr class="oddeven">';
                 $out.='<td>'.$i.'</td>' . "\n";
                 $out.='<td>'.$file['filename'];
-                $out.=PHP_OS;
                 if (! preg_match('/^win/i',PHP_OS)) {
                 	$htmltext=$langs->trans("YouCanDeleteFileOnServerWith", 'rm '.DOL_DOCUMENT_ROOT.'/'.$file['filename']);
                 	$out.=' '.$form->textwithpicto('', $htmltext, 1, 'help', '', 0, 2, 'helprm');

+ 2 - 0
htdocs/asset/class/asset.class.php

@@ -238,6 +238,8 @@ class Asset extends CommonObject
 			$this->errors = $object->errors;
 		}
 
+		unset($object->context['createfromclone']);
+
 		// End
 		if (!$error) {
 			$this->db->commit();

+ 37 - 1
htdocs/blockedlog/class/blockedlog.class.php

@@ -172,6 +172,10 @@ class BlockedLog
 		if ($conf->banque->enabled) $this->trackedevents['PAYMENT_VARIOUS_CREATE']='logPAYMENT_VARIOUS_CREATE';
 		if ($conf->banque->enabled) $this->trackedevents['PAYMENT_VARIOUS_MODIFY']='logPAYMENT_VARIOUS_MODIFY';
 		if ($conf->banque->enabled) $this->trackedevents['PAYMENT_VARIOUS_DELETE']='logPAYMENT_VARIOUS_DELETE';
+
+		// $conf->global->BANK_ENABLE_POS_CASHCONTROL must be set to 1 by all POS modules
+		$moduleposenabled = ($conf->cashdesk->enabled || $conf->takepos->enabled || ! empty($conf->global->BANK_ENABLE_POS_CASHCONTROL));
+		if ($moduleposenabled) $this->trackedevents['CASHCONTROL_VALIDATE']='logCASHCONTROL_VALIDATE';
 	}
 
 	/**
@@ -270,6 +274,17 @@ class BlockedLog
 				$this->error++;
 			}
 		}
+		else if($this->element === 'cashcontrol') {
+			require_once DOL_DOCUMENT_ROOT.'/compta/cashcontrol/class/cashcontrol.class.php';
+
+			$object = new CashControl($this->db);
+			if ($object->fetch($this->fk_object)>0) {
+				return $object->getNomUrl(1);
+			}
+			else{
+				$this->error++;
+			}
+		}
 		else if ($this->action == 'MODULE_SET')
 		{
 			return '<i class="opacitymedium">System to track events into unalterable logs were enabled</i>';
@@ -351,6 +366,10 @@ class BlockedLog
 		{
 			$this->date_object = $object->dateh;
 		}
+		elseif ($object->element=='cashcontrol')
+		{
+			$this->date_object = $object->date_creation;
+		}
 		else {
 			$this->date_object = $object->date;
 		}
@@ -364,7 +383,24 @@ class BlockedLog
 
 		// Set object_data
 		$this->object_data=new stdClass();
-		$arrayoffieldstoexclude = array('table_element','fields','ref_previous','ref_next','origin','origin_id','oldcopy','picto','error','modelpdf','table_element_line','linkedObjectsIds','linkedObjects','fk_delivery_address');
+		// Add fields to exclude
+		$arrayoffieldstoexclude = array(
+			'table_element','fields','ref_previous','ref_next','origin','origin_id','oldcopy','picto','error','errors','modelpdf','civility_id','contact','contact_id',
+			'table_element_line','ismultientitymanaged','isextrafieldmanaged',
+			'linkedObjectsIds','linkedObjects','fk_delivery_address',
+			'context',
+		    'projet'          // There is already ->fk_project
+		);
+		// Add more fields to exclude depending on object type
+		if ($this->element == 'cashcontrol')
+		{
+		    $arrayoffieldstoexclude = array_merge($arrayoffieldstoexclude, array(
+		        'name','lastname','firstname','region','region_id','region_code','state','state_id','state_code','country','country_id','country_code',
+		        'total_ht','total_tva','total_ttc','total_localtax1','total_localtax2',
+		        'barcode_type','barcode_type_code','barcode_type_label','barcode_type_coder','mode_reglement_id','cond_reglement_id','mode_reglement','cond_reglement','shipping_method_id',
+		        'fk_incoterms','libelle_incoterms','location_incoterms','lines')
+		    );
+		}
 
 		// Add thirdparty info
 		if (empty($object->thirdparty) && method_exists($object, 'fetch_thirdparty')) $object->fetch_thirdparty();

+ 30 - 19
htdocs/cashdesk/admin/cashdesk.php

@@ -86,7 +86,7 @@ $formproduct=new FormProduct($db);
 llxHeader('',$langs->trans("CashDeskSetup"));
 
 $linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
-print load_fiche_titre($langs->trans("CashDeskSetup"),$linkback,'title_setup');
+print load_fiche_titre($langs->trans("CashDeskSetup").' (SimplePOS)',$linkback,'title_setup');
 print '<br>';
 
 
@@ -95,30 +95,49 @@ print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print '<input type="hidden" name="action" value="set">';
 
+if (! empty($conf->service->enabled))
+{
+	print '<table class="noborder" width="100%">';
+	print '<tr class="liste_titre">';
+	print '<td>'.$langs->trans("Parameters").'</td><td>'.$langs->trans("Value").'</td>';
+	print "</tr>\n";
+
+	print '<tr class="oddeven"><td>';
+	print $langs->trans("CashdeskShowServices");
+	print '<td colspan="2">';
+	print $form->selectyesno("CASHDESK_SERVICES",$conf->global->CASHDESK_SERVICES,1);
+	print "</td></tr>\n";
+
+	print '</table>';
+
+	print '<br>';
+}
+
+
 print '<table class="noborder" width="100%">';
 print '<tr class="liste_titre">';
-print '<td>'.$langs->trans("Parameters").'</td><td>'.$langs->trans("Value").'</td>';
+print '<td>'.$langs->trans("Terminal").' 0</td><td>'.$langs->trans("Value").'</td>';
 print "</tr>\n";
 
 print '<tr class="oddeven"><td width=\"50%\">'.$langs->trans("CashDeskThirdPartyForSell").'</td>';
 print '<td colspan="2">';
-print $form->select_company($conf->global->CASHDESK_ID_THIRDPARTY,'socid','s.client in (1,3) AND s.status = 1',1,0,1,array(),0);
+print $form->select_company($conf->global->CASHDESK_ID_THIRDPARTY,'socid','s.client in (1,3) AND s.status = 1', 1, 0, 0, array(), 0);
 print '</td></tr>';
 if (! empty($conf->banque->enabled))
 {
-	
+
 	print '<tr class="oddeven"><td>'.$langs->trans("CashDeskBankAccountForSell").'</td>';
 	print '<td colspan="2">';
 	$form->select_comptes($conf->global->CASHDESK_ID_BANKACCOUNT_CASH,'CASHDESK_ID_BANKACCOUNT_CASH',0,"courant=2",1);
 	print '</td></tr>';
 
-	
+
 	print '<tr class="oddeven"><td>'.$langs->trans("CashDeskBankAccountForCheque").'</td>';
 	print '<td colspan="2">';
 	$form->select_comptes($conf->global->CASHDESK_ID_BANKACCOUNT_CHEQUE,'CASHDESK_ID_BANKACCOUNT_CHEQUE',0,"courant=1",1);
 	print '</td></tr>';
 
-	
+
 	print '<tr class="oddeven"><td>'.$langs->trans("CashDeskBankAccountForCB").'</td>';
 	print '<td colspan="2">';
 	$form->select_comptes($conf->global->CASHDESK_ID_BANKACCOUNT_CB,'CASHDESK_ID_BANKACCOUNT_CB',0,"courant=1",1);
@@ -127,7 +146,7 @@ if (! empty($conf->banque->enabled))
 
 if (! empty($conf->stock->enabled))
 {
-	
+
 	print '<tr class="oddeven"><td>'.$langs->trans("CashDeskDoNotDecreaseStock").'</td>';	// Force warehouse (this is not a default value)
 	print '<td colspan="2">';
 	if (empty($conf->productbatch->enabled)) {
@@ -138,13 +157,14 @@ if (! empty($conf->stock->enabled))
 	    if (!$conf->global->CASHDESK_NO_DECREASE_STOCK) {
 	       $res = dolibarr_set_const($db,"CASHDESK_NO_DECREASE_STOCK",1,'chaine',0,'',$conf->entity);
 	    }
-	    print $langs->trans('StockDecreaseForPointOfSaleDisabledbyBatch');
+	    print $langs->trans("Yes").'<br>';
+	    print '<span class="opacitymedium">'.$langs->trans('StockDecreaseForPointOfSaleDisabledbyBatch').'</span>';
 	}
 	print '</td></tr>';
 
 	$disabled=$conf->global->CASHDESK_NO_DECREASE_STOCK;
 
-	
+
 	print '<tr class="oddeven"><td>'.$langs->trans("CashDeskIdWareHouse").'</td>';	// Force warehouse (this is not a default value)
 	print '<td colspan="2">';
 	if (! $disabled)
@@ -154,20 +174,11 @@ if (! empty($conf->stock->enabled))
 	}
 	else
 	{
-		print $langs->trans("StockDecreaseForPointOfSaleDisabled");
+		print '<span class="opacitymedium">'.$langs->trans("StockDecreaseForPointOfSaleDisabled").'</span>';
 	}
 	print '</td></tr>';
 }
 
-if (! empty($conf->service->enabled))
-{
-    print '<tr class="oddeven"><td>';
-    print $langs->trans("CashdeskShowServices");
-    print '<td colspan="2">';
-    print $form->selectyesno("CASHDESK_SERVICES",$conf->global->CASHDESK_SERVICES,1);
-    print "</td></tr>\n";
-}
-
 // Use Dolibarr Receipt Printer
 if (! empty($conf->receiptprinter->enabled))
 {

+ 1 - 2
htdocs/cashdesk/class/Facturation.class.php

@@ -215,8 +215,7 @@ class Facturation
         $total_localtax1 = 0;
         $total_localtax2 = 0;
 
-        $tab=array();
-        $tab = $_SESSION['poscart'];
+        $tab = (! empty($_SESSION['poscart'])?$_SESSION['poscart']:array());
 
         $tab_size=count($tab);
         for($i=0;$i < $tab_size;$i++)

+ 29 - 0
htdocs/cashdesk/index.php

@@ -41,6 +41,8 @@ if ( $_SESSION['uid'] > 0 )
 $usertxt=GETPOST('user','',1);
 $err=GETPOST("err");
 
+// Instantiate hooks of thirdparty module only if not already define
+$hookmanager->initHooks(array('cashdeskloginpage'));
 
 /*
  * View
@@ -51,6 +53,15 @@ $formproduct=new FormProduct($db);
 
 $arrayofcss=array('/cashdesk/css/style.css');
 top_htmlhead('','',0,0,'',$arrayofcss);
+
+// Execute hook getLoginPageOptions (for table)
+$parameters=array('entity' => GETPOST('entity','int'));
+$reshook = $hookmanager->executeHooks('getLoginPageOptions',$parameters);    // Note that $action and $object may have been modified by some hooks.
+if (is_array($hookmanager->resArray) && ! empty($hookmanager->resArray)) {
+	$morelogincontent = $hookmanager->resArray; // (deprecated) For compatibility
+} else {
+	$morelogincontent = $hookmanager->resPrint;
+}
 ?>
 
 <body>
@@ -92,6 +103,24 @@ else
 		<td><input name="pwdPassword" class="texte_login" type="password" value="" /></td>
 	</tr>
 
+<?php
+if (! empty($morelogincontent)) {
+	if (is_array($morelogincontent)) {
+		foreach ($morelogincontent as $format => $option)
+		{
+			if ($format == 'table') {
+				echo '<!-- Option by hook -->';
+				echo $option;
+			}
+		}
+	}
+	else {
+		echo '<!-- Option by hook -->';
+		echo $morelogincontent;
+	}
+}
+?>
+
 	<tr>
 		<td colspan="2">
 		&nbsp;

+ 1 - 2
htdocs/cashdesk/tpl/liste_articles.tpl.php

@@ -45,8 +45,7 @@ $societe = new Societe($db);
 $societe->fetch($thirdpartyid);
 /** end add Ditto */
 
-$tab=array();
-$tab = $_SESSION['poscart'];
+$tab = (! empty($_SESSION['poscart'])?$_SESSION['poscart']:array());
 
 $tab_size=count($tab);
 if ($tab_size <= 0) print '<div class="center">'.$langs->trans("NoArticle").'</div><br>';

+ 2 - 2
htdocs/comm/action/card.php

@@ -432,8 +432,8 @@ if ($action == 'update')
 		foreach ($socpeopleassigned as $cid) $object->socpeopleassigned[$cid] = array('id' => $cid);
 		$object->contactid   = GETPOST("contactid",'int');
 		$object->fk_project  = GETPOST("projectid",'int');
-		$object->note        = GETPOST("note");
-		$object->pnote       = GETPOST("note");
+		$object->note        = GETPOST("note","none");	// deprecated
+		$object->note_private= GETPOST("note","none");
 		$object->fk_element	 = GETPOST("fk_element");
 		$object->elementtype = GETPOST("elementtype");
 

+ 2 - 3
htdocs/comm/action/class/actioncomm.class.php

@@ -490,8 +490,6 @@ class ActionComm extends CommonObject
     {
         global $db, $user, $langs, $conf, $hookmanager;
 
-        $this->context['createfromclone']='createfromclone';
-
         $error=0;
         $now=dol_now();
 
@@ -524,7 +522,8 @@ class ActionComm extends CommonObject
 		}
 
         // Create clone
-        $result=$this->create($fuser);
+		$this->context['createfromclone']='createfromclone';
+		$result=$this->create($fuser);
         if ($result < 0) $error++;
 
         if (! $error)

+ 13 - 0
htdocs/comm/action/list.php

@@ -241,8 +241,15 @@ $sql.= " a.fk_contact, a.note, a.percent as percent,";
 $sql.= " a.fk_element, a.elementtype, a.datec, a.tms as datem,";
 $sql.= " c.code as type_code, c.libelle as type_label,";
 $sql.= " sp.lastname, sp.firstname, sp.email, sp.phone, sp.address, sp.phone as phone_pro, sp.phone_mobile, sp.phone_perso, sp.fk_pays as country_id";
+
 // Add fields from extrafields
 foreach ($extrafields->attribute_label as $key => $val) $sql.=($extrafields->attribute_type[$key] != 'separate' ? ",ef.".$key.' as options_'.$key : '');
+
+// Add fields from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListSelect',$parameters);    // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+
 $sql.= " FROM ".MAIN_DB_PREFIX."actioncomm as a";
 $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."actioncomm_extrafields as ef ON (a.id = ef.fk_object) ";
 if (! $user->rights->societe->client->voir && ! $socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON a.fk_soc = sc.fk_soc";
@@ -314,9 +321,15 @@ if ($filtert > 0 || $usergroup > 0)
 if ($dateselect > 0) $sql.= " AND ((a.datep2 >= '".$db->idate($dateselect)."' AND a.datep <= '".$db->idate($dateselect+3600*24-1)."') OR (a.datep2 IS NULL AND a.datep > '".$db->idate($dateselect-3600)."' AND a.datep <= '".$db->idate($dateselect+3600*24-1)."'))";
 if ($datestart > 0) $sql.= " AND a.datep BETWEEN '".$db->idate($datestart)."' AND '".$db->idate($datestart+3600*24-1)."'";
 if ($dateend > 0) $sql.= " AND a.datep2 BETWEEN '".$db->idate($dateend)."' AND '".$db->idate($dateend+3600*24-1)."'";
+
 // Add where from extra fields
 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
 
+// Add where from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListWhere',$parameters);    // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+
 $sql.= $db->order($sortfield,$sortorder);
 
 $nbtotalofrecords = '';

+ 2 - 2
htdocs/comm/card.php

@@ -327,7 +327,7 @@ if ($object->id > 0)
 	print '</td><td>';
 	if ($action == 'editconditions')
 	{
-		$form->form_conditions_reglement($_SERVER['PHP_SELF'].'?socid='.$object->id, $object->cond_reglement_id, 'cond_reglement_id',1);
+		$form->form_conditions_reglement($_SERVER['PHP_SELF'].'?socid='.$object->id, $object->cond_reglement_id, 'cond_reglement_id', 1);
 	}
 	else
 	{
@@ -346,7 +346,7 @@ if ($object->id > 0)
 	print '</td><td>';
 	if ($action == 'editmode')
 	{
-		$form->form_modes_reglement($_SERVER['PHP_SELF'].'?socid='.$object->id,$object->mode_reglement_id,'mode_reglement_id', 'CRDT');
+		$form->form_modes_reglement($_SERVER['PHP_SELF'].'?socid='.$object->id, $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
 	}
 	else
 	{

+ 4 - 2
htdocs/comm/mailing/cibles.php

@@ -329,14 +329,16 @@ if ($object->fetch($id) >= 0)
 				// Si le module mailing est qualifie
 				if ($qualified)
 				{
+					$var = ! $var;
+
 					if ($allowaddtarget)
 					{
-						print '<form class="oddeven tagtr" name="'.$modulename.'" action="'.$_SERVER['PHP_SELF'].'?action=add&id='.$object->id.'&module='.$modulename.'" method="POST" enctype="multipart/form-data">';
+						print '<form '.$bctag[$var].' name="'.$modulename.'" action="'.$_SERVER['PHP_SELF'].'?action=add&id='.$object->id.'&module='.$modulename.'" method="POST" enctype="multipart/form-data">';
 						print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 					}
 					else
 					{
-					    print '<div class="oddeven tagtr">';
+					    print '<div '.$bctag[$var].'>';
 					}
 
 					print '<div class="tagtd">';

+ 1 - 3
htdocs/comm/mailing/class/mailing.class.php

@@ -279,8 +279,6 @@ class Mailing extends CommonObject
 
 		$object=new Mailing($this->db);
 
-		$object->context['createfromclone']='createfromclone';
-
 		$this->db->begin();
 
 		// Load source object
@@ -314,6 +312,7 @@ class Mailing extends CommonObject
 		}
 
 		// Create clone
+		$object->context['createfromclone']='createfromclone';
 		$result=$object->create($user);
 
 		// Other options
@@ -345,7 +344,6 @@ class Mailing extends CommonObject
 				$sql.= " FROM ".MAIN_DB_PREFIX."mailing_cibles ";
 				$sql.= " WHERE fk_mailing = ".$fromid;
 
-				dol_syslog(get_class($this)."::createFromClone", LOG_DEBUG);
 				$result=$this->db->query($sql);
 				if ($result)
 				{

+ 1 - 1
htdocs/comm/propal/card.php

@@ -2105,7 +2105,7 @@ if ($action == 'create')
 	print '</tr></table>';
 	print '</td><td>';
 	if (! empty($object->brouillon) && $action == 'editmode' && $usercancreate) {
-		$form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->mode_reglement_id, 'mode_reglement_id', 'CRDT');
+		$form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
 	} else {
 		$form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->mode_reglement_id, 'none');
 	}

+ 38 - 32
htdocs/comm/propal/class/propal.class.php

@@ -1222,31 +1222,30 @@ class Propal extends CommonObject
 
 		dol_include_once('/projet/class/project.class.php');
 
-		$this->context['createfromclone']='createfromclone';
-
 		$error=0;
 		$now=dol_now();
 
-		$this->db->begin();
+		dol_syslog(__METHOD__, LOG_DEBUG);
 
-		// get extrafields so they will be clone
-		foreach($this->lines as $line)
-			$line->fetch_optionals();
+		$object = new self($this->db);
 
-		// Load dest object
-		$clonedObj = clone $this;
+		$this->db->begin();
+
+		// Load source object
+		$object->fetch($this->id);
+		$object->fetch_lines();
 
 		$objsoc=new Societe($this->db);
 
 		// Change socid if needed
-		if (! empty($socid) && $socid != $clonedObj->socid)
+		if (! empty($socid) && $socid != $object->socid)
 		{
 			if ($objsoc->fetch($socid) > 0)
 			{
-				$clonedObj->socid 				= $objsoc->id;
-				$clonedObj->cond_reglement_id	= (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
-				$clonedObj->mode_reglement_id	= (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
-				$clonedObj->fk_delivery_address	= '';
+			    $object->socid 				= $objsoc->id;
+			    $object->cond_reglement_id	= (! empty($objsoc->cond_reglement_id) ? $objsoc->cond_reglement_id : 0);
+			    $object->mode_reglement_id	= (! empty($objsoc->mode_reglement_id) ? $objsoc->mode_reglement_id : 0);
+			    $object->fk_delivery_address	= '';
 
 				/*if (!empty($conf->projet->enabled))
                 {
@@ -1258,44 +1257,51 @@ class Propal extends CommonObject
     					$clonedObj->fk_project = '';
     				}
                 }*/
-				$clonedObj->fk_project = '';    // A cloned proposal is set by default to no project.
+			    $object->fk_project = '';    // A cloned proposal is set by default to no project.
 			}
 
 			// reset ref_client
-			$clonedObj->ref_client  = '';
+			$object->ref_client  = '';
 
 			// TODO Change product price if multi-prices
 		}
 		else
 		{
-			$objsoc->fetch($clonedObj->socid);
+		    $objsoc->fetch($object->socid);
 		}
 
-		$clonedObj->id=0;
-		$clonedObj->ref='';
-		$clonedObj->statut=self::STATUS_DRAFT;
+		$object->id=0;
+		$object->ref='';
+		$object->statut=self::STATUS_DRAFT;
 
 		// Clear fields
-		$clonedObj->user_author	= $user->id;
-		$clonedObj->user_valid	= '';
-		$clonedObj->date		= $now;
-		$clonedObj->datep		= $now;    // deprecated
-		$clonedObj->fin_validite	= $clonedObj->date + ($clonedObj->duree_validite * 24 * 3600);
-		if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) $clonedObj->ref_client	= '';
+		$object->user_author	= $user->id;
+		$object->user_valid	= '';
+		$object->date		= $now;
+		$object->datep		= $now;    // deprecated
+		$object->fin_validite	= $object->date + ($object->duree_validite * 24 * 3600);
+		if (empty($conf->global->MAIN_KEEP_REF_CUSTOMER_ON_CLONING)) $object->ref_client	= '';
 
 		// Create clone
-		$result=$clonedObj->create($user);
+		$object->context['createfromclone']='createfromclone';
+		$result=$object->create($user);
 		if ($result < 0) $error++;
-		else
+
+		if (! $error)
 		{
 			// copy internal contacts
-			if ($clonedObj->copy_linked_contact($this, 'internal') < 0)
+		    if ($object->copy_linked_contact($this, 'internal') < 0)
+		    {
 				$error++;
+		    }
+		}
 
+		if (! $error)
+		{
 			// copy external contacts if same company
-			elseif ($this->socid == $clonedObj->socid)
+			if ($this->socid == $object->socid)
 			{
-				if ($clonedObj->copy_linked_contact($this, 'external') < 0)
+			    if ($object->copy_linked_contact($this, 'external') < 0)
 					$error++;
 			}
 		}
@@ -1312,13 +1318,13 @@ class Propal extends CommonObject
 			}
 		}
 
-		unset($this->context['createfromclone']);
+		unset($object->context['createfromclone']);
 
 		// End
 		if (! $error)
 		{
 			$this->db->commit();
-			return $clonedObj->id;
+			return $object->id;
 		}
 		else
 		{

+ 1 - 1
htdocs/comm/propal/list.php

@@ -45,7 +45,7 @@ require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
 
 // Load translation files required by the page
-$langs->loadLangs(array('companies','propal','compta','bills','orders','products','deliveries'));
+$langs->loadLangs(array('companies', 'propal', 'compta', 'bills', 'orders', 'products', 'deliveries', 'categories'));
 
 $socid=GETPOST('socid','int');
 

+ 1 - 1
htdocs/commande/card.php

@@ -2191,7 +2191,7 @@ if ($action == 'create' && $user->rights->commande->creer)
 		print '</tr></table>';
 		print '</td><td>';
 		if ($action == 'editmode') {
-			$form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->mode_reglement_id, 'mode_reglement_id', 'CRDT');
+			$form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
 		} else {
 			$form->form_modes_reglement($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->mode_reglement_id, 'none');
 		}

+ 1 - 2
htdocs/commande/class/commande.class.php

@@ -1066,8 +1066,6 @@ class Commande extends CommonOrder
 
 		$error=0;
 
-		$this->context['createfromclone'] = 'createfromclone';
-
 		$this->db->begin();
 
 		// get lines so they will be clone
@@ -1108,6 +1106,7 @@ class Commande extends CommonOrder
 			$this->ref_client         = '';
 
 			// Create clone
+			$this->context['createfromclone'] = 'createfromclone';
 			$result=$this->create($user);
 			if ($result < 0) $error++;
 

+ 1 - 1
htdocs/compta/bank/class/account.class.php

@@ -1471,7 +1471,7 @@ class Account extends CommonObject
 	{
 		$country_code=$this->getCountryCode();
 
-		if (in_array($country_code,array('CH','FR','ES','GA','IT','NC'))) return 1; // France, Spain, Gabon, ...
+		if (in_array($country_code,array('FR','ES','GA','IT','NC'))) return 1; // France, Spain, Gabon, ... - Not valid for CH
 		if (in_array($country_code,array('AU','BE','CA','DE','DK','GR','GB','ID','IE','IR','KR','NL','NZ','UK','US'))) return 2;      // Australia, England...
 		return 0;
 	}

+ 1 - 2
htdocs/compta/bank/class/bankcateg.class.php

@@ -280,8 +280,6 @@ class BankCateg // extends CommonObject
 
 		$object = new BankCateg($this->db);
 
-		$object->context['createfromclone'] = 'createfromclone';
-
 		$this->db->begin();
 
 		// Load source object
@@ -290,6 +288,7 @@ class BankCateg // extends CommonObject
 		$object->statut = 0;
 
 		// Create clone
+		$object->context['createfromclone'] = 'createfromclone';
 		$result = $object->create($user);
 
 		// Other options

+ 14 - 4
htdocs/compta/bank/ligne.php

@@ -341,16 +341,26 @@ if ($result)
             {
                 if ($key) print '<br>';
                 if ($links[$key]['type']=='payment') {
-                    print '<a href="'.DOL_URL_ROOT.'/compta/paiement/card.php?id='.$links[$key]['url_id'].'">';
+                    require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
+                    $paymenttmp=new Paiement($db);
+                    $paymenttmp->fetch($links[$key]['url_id']);
+                    $paymenttmp->ref = $langs->trans("Payment").' '.$paymenttmp->ref;
+                    /*print '<a href="'.DOL_URL_ROOT.'/compta/paiement/card.php?id='.$links[$key]['url_id'].'">';
                     print img_object($langs->trans('ShowPayment'),'payment').' ';
                     print $langs->trans("Payment");
-                    print '</a>';
+                    print '</a>';*/
+                    print $paymenttmp->getNomUrl(1);
                 }
                 else if ($links[$key]['type']=='payment_supplier') {
-                    print '<a href="'.DOL_URL_ROOT.'/fourn/paiement/card.php?id='.$links[$key]['url_id'].'">';
+                    require_once DOL_DOCUMENT_ROOT.'/fourn/class/paiementfourn.class.php';
+                    $paymenttmp=new PaiementFourn($db);
+                    $paymenttmp->fetch($links[$key]['url_id']);
+                    $paymenttmp->ref = $langs->trans("Payment").' '.$paymenttmp->ref;
+                    /*print '<a href="'.DOL_URL_ROOT.'/fourn/paiement/card.php?id='.$links[$key]['url_id'].'">';
                     print img_object($langs->trans('ShowPayment'),'payment').' ';
                     print $langs->trans("Payment");
-                    print '</a>';
+                    print '</a>';*/
+                    print $paymenttmp->getNomUrl(1);
                 }
                 else if ($links[$key]['type']=='company') {
                     $societe=new Societe($db);

+ 38 - 39
htdocs/compta/bank/treso.php

@@ -115,11 +115,10 @@ if ($_REQUEST["account"] || $_REQUEST["ref"])
 	print '<td>'.$langs->trans("ThirdParty").'</td>';
 	print '<td align="right">'.$langs->trans("Debit").'</td>';
 	print '<td align="right">'.$langs->trans("Credit").'</td>';
-	print '<td align="right" width="80">'.$langs->trans("BankBalance").'</td>';
+	print '<td align="right">'.$langs->trans("BankBalance").'</td>';
 	print '</tr>';
 
 	// Current balance
-
 	print '<tr class="liste_total">';
 	print '<td align="left" colspan="5">'.$langs->trans("CurrentBalance").'</td>';
 	print '<td align="right" class="nowrap">'.price($solde).'</td>';
@@ -133,7 +132,7 @@ if ($_REQUEST["account"] || $_REQUEST["ref"])
 
 
 	// Remainder to pay in future
-  $sqls = array();
+	$sqls = array();
 
 	// Customer invoices
 	$sql = "SELECT 'invoice' as family, f.rowid as objid, f.facnumber as ref, f.total_ttc, f.type, f.date_lim_reglement as dlr,";
@@ -142,9 +141,9 @@ if ($_REQUEST["account"] || $_REQUEST["ref"])
 	$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
 	$sql.= " WHERE f.entity = ".$conf->entity;
 	$sql.= " AND f.paye = 0 AND f.fk_statut = 1";	// Not paid
-  $sql.= " AND (f.fk_account IN (0, ".$object->id.") OR f.fk_account IS NULL)"; // Id bank account of invoice
-  $sql.= " ORDER BY dlr ASC";
-  $sqls[] = $sql;
+	$sql.= " AND (f.fk_account IN (0, ".$object->id.") OR f.fk_account IS NULL)"; // Id bank account of invoice
+	$sql.= " ORDER BY dlr ASC";
+	$sqls[] = $sql;
 
 	// Supplier invoices
 	$sql = " SELECT 'invoice_supplier' as family, ff.rowid as objid, ff.ref as ref, ff.ref_supplier as ref_supplier, (-1*ff.total_ttc) as total_ttc, ff.type, ff.date_lim_reglement as dlr,";
@@ -153,51 +152,51 @@ if ($_REQUEST["account"] || $_REQUEST["ref"])
 	$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON ff.fk_soc = s.rowid";
 	$sql.= " WHERE ff.entity = ".$conf->entity;
 	$sql.= " AND ff.paye = 0 AND fk_statut = 1";	// Not paid
-  $sql.= " AND (ff.fk_account IN (0, ".$object->id.") OR ff.fk_account IS NULL)"; // Id bank account of supplier invoice
-  $sql.= " ORDER BY dlr ASC";
-  $sqls[] = $sql;
+	$sql.= " AND (ff.fk_account IN (0, ".$object->id.") OR ff.fk_account IS NULL)"; // Id bank account of supplier invoice
+	$sql.= " ORDER BY dlr ASC";
+	$sqls[] = $sql;
 
 	// Social contributions
 	$sql = " SELECT 'social_contribution' as family, cs.rowid as objid, cs.libelle as ref, (-1*cs.amount) as total_ttc, ccs.libelle as type, cs.date_ech as dlr";
-  $sql.= ", cs.fk_account";
+	$sql.= ", cs.fk_account";
 	$sql.= " FROM ".MAIN_DB_PREFIX."chargesociales as cs";
 	$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_chargesociales as ccs ON cs.fk_type = ccs.id";
 	$sql.= " WHERE cs.entity = ".$conf->entity;
 	$sql.= " AND cs.paye = 0";	// Not paid
-  $sql.= " AND (cs.fk_account IN (0, ".$object->id.") OR cs.fk_account IS NULL)"; // Id bank account of social contribution
+	$sql.= " AND (cs.fk_account IN (0, ".$object->id.") OR cs.fk_account IS NULL)"; // Id bank account of social contribution
 	$sql.= " ORDER BY dlr ASC";
-  $sqls[] = $sql;
+	$sqls[] = $sql;
 
-  // others sql
-  $parameters = array();
-  $reshook = $hookmanager->executeHooks('addMoreSQL', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
-  if(empty($reshook) and isset($hookmanager->resArray['sql'])){
-    $sqls[] = $hookmanager->resArray['sql'];
-  }
+	// others sql
+	$parameters = array();
+	$reshook = $hookmanager->executeHooks('addMoreSQL', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
+	if(empty($reshook) and isset($hookmanager->resArray['sql'])){
+		$sqls[] = $hookmanager->resArray['sql'];
+	}
 
 	$error=0;
 	$tab_sqlobjOrder=array();
 	$tab_sqlobj=array();
 
-  foreach($sqls as $sql){
-    $resql = $db->query($sql);
-    if($resql){
-      while($sqlobj = $db->fetch_object($resql)){
-        $tab_sqlobj[] = $sqlobj;
-        $tab_sqlobjOrder[]= $db->jdate($sqlobj->dlr);
-      }
-      $db->free($resql);
-    }else{
-      $error++;
-    }
-  }
+	foreach($sqls as $sql){
+		$resql = $db->query($sql);
+		if($resql){
+			while($sqlobj = $db->fetch_object($resql)){
+				$tab_sqlobj[] = $sqlobj;
+				$tab_sqlobjOrder[]= $db->jdate($sqlobj->dlr);
+			}
+			$db->free($resql);
+			}else{
+			$error++;
+		}
+	}
 
 	// Sort array
 	if (! $error)
 	{
 		array_multisort($tab_sqlobjOrder,$tab_sqlobj);
 
-		//Apply distinct filter
+		// Apply distinct filter
 		foreach ($tab_sqlobj as $key=>$value) {
 			$tab_sqlobj[$key] = "'" . serialize($value) . "'";
 		}
@@ -262,13 +261,13 @@ if ($_REQUEST["account"] || $_REQUEST["ref"])
 				$paiement = -1*$socialcontribstatic->getSommePaiement();	// Payment already done
 			}
 
-      $parameters = array('obj' => $obj);
-      $reshook = $hookmanager->executeHooks('moreFamily', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
-      if(empty($reshook)){
-        $ref = isset($hookmanager->resArray['ref']) ? $hookmanager->resArray['ref'] : '';
-        $refcomp = isset($hookmanager->resArray['refcomp']) ? $hookmanager->resArray['refcomp'] : '';
-        $paiement = isset($hookmanager->resArray['paiement']) ? $hookmanager->resArray['paiement'] : 0;
-      }
+			$parameters = array('obj' => $obj);
+			$reshook = $hookmanager->executeHooks('moreFamily', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
+			if(empty($reshook)){
+				$ref = isset($hookmanager->resArray['ref']) ? $hookmanager->resArray['ref'] : '';
+				$refcomp = isset($hookmanager->resArray['refcomp']) ? $hookmanager->resArray['refcomp'] : '';
+				$paiement = isset($hookmanager->resArray['paiement']) ? $hookmanager->resArray['paiement'] : 0;
+			}
 
 			$total_ttc = $obj->total_ttc;
 			if ($paiement) $total_ttc = $obj->total_ttc - $paiement;
@@ -307,7 +306,7 @@ if ($_REQUEST["account"] || $_REQUEST["ref"])
     $solde = isset($hookmanager->resArray['solde']) ? $hookmanager->resArray['solde'] : $solde;
 	}
 
-  // solde
+	// solde
 	print '<tr class="liste_total">';
 	print '<td align="left" colspan="5">'.$langs->trans("FutureBalance").' ('.$object->currency_code.')</td>';
 	print '<td align="right" class="nowrap">'.price($solde, 0, $langs, 0, 0, -1, $object->currency_code).'</td>';

+ 575 - 0
htdocs/compta/cashcontrol/cashcontrol_card.php

@@ -0,0 +1,575 @@
+<?php
+/* Copyright (C) 2001-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
+ * Copyright (C) 2004-2013 Laurent Destailleur  <eldy@users.sourceforge.net>
+ * Copyright (C) 2005-2009 Regis Houssin        <regis.houssin@capnetworks.com>
+ * Copyright (C) 2013      Charles-Fr BENKE     <charles.fr@benke.fr>
+ * Copyright (C) 2015      Jean-François Ferry	<jfefe@aternatik.fr>
+ * Copyright (C) 2016      Marcos García        <marcosgdf@gmail.com>
+ * Copyright (C) 2018      Andreu Bisquerra		<jove@bisquerra.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/compta/cashcontrol/cashcontrol_card.php
+ *      \ingroup    cashdesk|takepos
+ *      \brief      Page to show a cash fence
+ */
+
+require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
+require_once DOL_DOCUMENT_ROOT.'/compta/cashcontrol/class/cashcontrol.class.php';
+
+$langs->loadLangs(array("install","cashdesk","admin","banks"));
+
+$id=GETPOST('id','int');
+$ref = GETPOST('ref', 'alpha');
+$action=GETPOST('action','aZ09');
+$categid = GETPOST('categid');
+$label = GETPOST("label");
+
+$now=dol_now();
+$syear = (GETPOSTISSET('closeyear')?GETPOST('closeyear', 'int'):dol_print_date($now, "%Y"));
+$smonth = (GETPOSTISSET('closemonth')?GETPOST('closemonth', 'int'):dol_print_date($now, "%m"));
+$sday = (GETPOSTISSET('closeday')?GETPOST('closeday', 'int'):dol_print_date($now, "%d"));
+
+$limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit;
+$sortfield = GETPOST("sortfield",'alpha');
+$sortorder = GETPOST("sortorder",'alpha');
+$page = GETPOST("page",'int');
+if (empty($page) || $page == -1) { $page = 0; }     // If $page is not defined, or '' or -1
+$offset = $limit * $page;
+$pageprev = $page - 1;
+$pagenext = $page + 1;
+if (! $sortfield) $sortfield='rowid';
+if (! $sortorder) $sortorder='ASC';
+
+// Security check
+if (! $user->rights->cashdesk->use && ! $user->rights->takepos->use)
+{
+	accessforbidden();
+}
+
+$arrayofpaymentmode=array('cash'=>'Cash', 'cheque'=>'Cheque', 'card'=>'CreditCard');
+
+$arrayofposavailable=array();
+if (! empty($conf->cashdesk->enabled)) $arrayofposavailable['cashdesk']=$langs->trans('CashDesk').' (cashdesk)';
+if (! empty($conf->takepos->enabled))  $arrayofposavailable['takepos']=$langs->trans('TakePOS').' (takepos)';
+// TODO Add hook here to allow other POS to add themself
+
+$object= new CashControl($db);
+$extrafields = new ExtraFields($db);
+
+// fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label($object->table_element);
+
+// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
+$hookmanager->initHooks(array('cashcontrolcard','globalcard'));
+
+
+/*
+ * Actions
+ */
+
+$permissiontoadd = ($user->rights->cashdesk->use || $user->rights->takepos->use);
+$permissiontodelete = ($user->rights->cashdesk->use || $user->rights->takepos->use) || ($permissiontoadd && $object->status == 0);
+if (empty($backtopage)) $backtopage = dol_buildpath('/compta/cashcontrol/cashcontrol_card.php',1).'?id='.($id > 0 ? $id : '__ID__');
+$backurlforlist = dol_buildpath('/compta/cashcontrol/cashcontrol_list.php',1);
+$triggermodname = 'CACHCONTROL_MODIFY';	// Name of trigger action code to execute when we modify record
+
+if (empty($conf->global->CASHDESK_ID_BANKACCOUNT_CASH))
+{
+	setEventMessages($langs->trans("CashDesk")." - ".$langs->trans("NotConfigured"), null, 'errors');
+}
+
+
+if (GETPOST('cancel','alpha'))
+{
+	$action = 'create';
+}
+
+if ($action=="start")
+{
+	$error=0;
+	if (! GETPOST('posmodule','alpha') || GETPOST('posmodule','alpha') == '-1')
+	{
+		setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Module")), null, 'errors');
+		$action='create';
+		$error++;
+	}
+	if (GETPOST('posnumber','alpha') == '')
+	{
+		setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("CashDesk")), null, 'errors');
+		$action='create';
+		$error++;
+	}
+	if (! GETPOST('closeyear','alpha') || GETPOST('closeyear','alpha') == '-1')
+	{
+		setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Year")), null, 'errors');
+		$action='create';
+		$error++;
+	}
+}
+elseif ($action=="add")
+{
+	if (GETPOST('opening','alpha') == '')
+	{
+		setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("InitialBankBalance")), null, 'errors');
+		$action='start';
+		$error++;
+	}
+	$error=0;
+	foreach($arrayofpaymentmode as $key=>$val)
+	{
+		if (GETPOST($key.'_amount','alpha') == '')
+		{
+			setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv($val)), null, 'errors');
+			$action='start';
+			$error++;
+		}
+		else
+		{
+			$object->$key = price2num(GETPOST($key.'_amount','alpha'));
+		}
+	}
+
+	if (! $error)
+	{
+		$object->day_close = GETPOST('closeday', 'int');
+		$object->month_close = GETPOST('closemonth', 'int');
+		$object->year_close = GETPOST('closeyear', 'int');
+
+	    $object->opening=price2num(GETPOST('opening','alpha'));
+	    $object->posmodule=GETPOST('posmodule','alpha');
+		$object->posnumber=GETPOST('posnumber','alpha');
+
+		$db->begin();
+
+		$id=$object->create($user);
+
+		if ($id > 0)
+		{
+			$db->commit();
+			$action="view";
+		}
+		else
+		{
+			$db->rollback;
+			$action="view";
+		}
+	}
+}
+
+if ($action=="close")
+{
+	$object->fetch($id);
+
+    $result = $object->valid($user);
+	if ($result <= 0)
+	{
+		setEventMessages($object->error, $object->errors, 'errors');
+	}
+	else
+	{
+		setEventMessages($langs->trans("CashFenceDone"), null);
+	}
+
+    $action="view";
+}
+
+// Action to delete
+if ($action == 'confirm_delete' && ! empty($permissiontodelete))
+{
+    $object->fetch($id);
+
+    if (! ($object->id > 0))
+    {
+        dol_print_error('', 'Error, object must be fetched before being deleted');
+        exit;
+    }
+
+    $result=$object->delete($user);
+    var_dump($result);
+    if ($result > 0)
+    {
+        // Delete OK
+        setEventMessages("RecordDeleted", null, 'mesgs');
+        header("Location: ".$backurlforlist);
+        exit;
+    }
+    else
+    {
+        if (! empty($object->errors)) setEventMessages(null, $object->errors, 'errors');
+        else setEventMessages($object->error, null, 'errors');
+    }
+}
+
+
+/*
+ * View
+ */
+
+$form=new Form($db);
+
+if ($action=="create" || $action=="start")
+{
+	llxHeader();
+
+	$initialbalanceforterminal=array();
+	$theoricalamountforterminal=array();
+	$theoricalnbofinvoiceforterminal=array();
+
+	if (GETPOST('posnumber','alpha') != '' && GETPOST('posnumber','alpha') != '' && GETPOST('posnumber','alpha') != '-1')
+	{
+		$posmodule = GETPOST('posmodule','alpha');
+		$terminalid = GETPOST('posnumber','alpha');
+
+		// Calculate $initialbalanceforterminal for terminal 0
+		foreach($arrayofpaymentmode as $key => $val)
+		{
+			if ($key != 'cash')
+			{
+				$initialbalanceforterminal[$terminalid][$key] = 0;
+				continue;
+			}
+
+			// Get the bank account dedicated to this point of sale module/terminal
+			$bankid = $conf->global->CASHDESK_ID_BANKACCOUNT_CASH;			// This value is ok for 'Terminal 0' for module 'CashDesk' and 'TakePos' (they manage only 1 terminal)
+			// Hook to get the good bank id according to posmodule and posnumber.
+			// @TODO add hook here
+
+			$sql = "SELECT SUM(amount) as total FROM ".MAIN_DB_PREFIX."bank";
+			$sql.= " WHERE fk_account = ".$bankid;
+			if ($syear && ! $smonth)              $sql.= " AND dateo < '".$db->idate(dol_get_first_day($syear, 1))."'";
+			elseif ($syear && $smonth && ! $sday) $sql.= " AND dateo < '".$db->idate(dol_get_first_day($syear, $smonth))."'";
+			elseif ($syear && $smonth && $sday)   $sql.= " AND dateo < '".$db->idate(dol_mktime(0, 0, 0, $smonth, $sday, $syear))."'";
+			else dol_print_error('', 'Year not defined');
+
+			$resql = $db->query($sql);
+			if ($resql)
+			{
+				$obj = $db->fetch_object($resql);
+				if ($obj) $initialbalanceforterminal[$terminalid][$key] = $obj->total;
+			}
+			else dol_print_error($db);
+		}
+
+		// Calculate $theoricalamountforterminal for terminal 0
+		foreach($arrayofpaymentmode as $key => $val)
+		{
+			/*$sql = "SELECT SUM(amount) as total FROM ".MAIN_DB_PREFIX."bank";
+			$sql.= " WHERE fk_account = ".$bankid;*/
+			$sql = "SELECT SUM(pf.amount) as total, COUNT(*) as nb";
+			$sql.= " FROM ".MAIN_DB_PREFIX."paiement_facture as pf, ".MAIN_DB_PREFIX."facture as f, ".MAIN_DB_PREFIX."paiement as p, ".MAIN_DB_PREFIX."c_paiement as cp";
+			$sql.= " WHERE pf.fk_facture = f.rowid AND p.rowid = pf.fk_paiement AND cp.id = p.fk_paiement";
+			$sql.= " AND f.module_source = '".$db->escape($posmodule)."'";
+			$sql.= " AND f.pos_source = '".$db->escape($terminalid)."'";
+			$sql.= " AND f.paye = 1";
+			$sql.= " AND p.entity IN (".getEntity('facture').")";
+			if ($key == 'cash')       $sql.=" AND cp.code = 'LIQ'";
+			elseif ($key == 'cheque') $sql.=" AND cp.code = 'CHQ'";
+			elseif ($key == 'card')   $sql.=" AND cp.code = 'CB'";
+			else
+			{
+				dol_print_error('Value for key = '.$key.' not supported');
+				exit;
+			}
+			if ($syear && ! $smonth)              $sql.= " AND datef BETWEEN '".$db->idate(dol_get_first_day($syear, 1))."' AND '".$db->idate(dol_get_last_day($syear, 12))."'";
+			elseif ($syear && $smonth && ! $sday) $sql.= " AND datef BETWEEN '".$db->idate(dol_get_first_day($syear, $smonth))."' AND '".$db->idate(dol_get_last_day($syear, $smonth))."'";
+			elseif ($syear && $smonth && $sday)   $sql.= " AND datef BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $smonth, $sday, $syear))."' AND '".$db->idate(dol_mktime(23, 59, 59, $smonth, $sday, $syear))."'";
+			else dol_print_error('', 'Year not defined');
+
+			$resql = $db->query($sql);
+			if ($resql)
+			{
+				$theoricalamountforterminal[$terminalid][$key] = $initialbalanceforterminal[$terminalid][$key];
+
+				$obj = $db->fetch_object($resql);
+				if ($obj)
+				{
+					$theoricalamountforterminal[$terminalid][$key] = price2num($theoricalamountforterminal[$terminalid][$key] + $obj->total);
+					$theoricalnbofinvoiceforterminal[$terminalid][$key] = $obj->nb;
+				}
+			}
+			else dol_print_error($db);
+		}
+	}
+
+	print load_fiche_titre($langs->trans("CashControl")." - ".$langs->trans("New"), '', 'title_bank.png');
+
+	print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
+    if ($action == 'start' && GETPOST('posnumber','int') != '' && GETPOST('posnumber','int') != '' && GETPOST('posnumber','int') != '-1')
+    {
+	    print '<input type="hidden" name="action" value="add">';
+    }
+    else
+    {
+    	print '<input type="hidden" name="action" value="start">';
+    }
+    print '<table class="noborder" width="100%">';
+    print '<tr class="liste_titre">';
+    print '<td>'.$langs->trans("Module").'</td>';
+    print '<td>'.$langs->trans("CashDesk").' ID</td>';
+    print '<td>'.$langs->trans("Year").'</td>';
+    print '<td>'.$langs->trans("Month").'</td>';
+    print '<td>'.$langs->trans("Day").'</td>';
+    print '<td></td>';
+    print "</tr>\n";
+
+	$disabled=0;
+	$prefix='close';
+
+    print '<tr class="oddeven">';
+    print '<td>'.$form->selectarray('posmodule', $arrayofposavailable, GETPOST('posmodule','alpha'), (count($arrayofposavailable)>1?1:0)).'</td>';
+    print '<td><input name="posnumber" type="text" class="maxwidth50" value="'.(GETPOSTISSET('posnumber')?GETPOST('posnumber','alpha'):'0').'"></td>';
+	// Year
+	print '<td>';
+	$retstring='<select'.($disabled?' disabled':'').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'year" name="'.$prefix.'year">';
+	for ($year = $syear - 10; $year < $syear + 10 ; $year++)
+	{
+		$retstring.='<option value="'.$year.'"'.($year == $syear ? ' selected':'').'>'.$year.'</option>';
+	}
+	$retstring.="</select>\n";
+	print $retstring;
+	print '</td>';
+	// Month
+	print '<td>';
+	$retstring='<select'.($disabled?' disabled':'').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'month" name="'.$prefix.'month">';
+	$retstring.='<option value="0"></option>';
+	for ($month = 1 ; $month <= 12 ; $month++)
+	{
+		$retstring.='<option value="'.$month.'"'.($month == $smonth?' selected':'').'>';
+		$retstring.=dol_print_date(mktime(12,0,0,$month,1,2000),"%b");
+		$retstring.="</option>";
+	}
+	$retstring.="</select>";
+	print $retstring;
+	print '</td>';
+	// Day
+	print '<td>';
+	$retstring='<select'.($disabled?' disabled':'').' class="flat valignmiddle maxwidth50imp" id="'.$prefix.'day" name="'.$prefix.'day">';
+	$retstring.='<option value="0" selected>&nbsp;</option>';
+	for ($day = 1 ; $day <= 31; $day++)
+	{
+		$retstring.='<option value="'.$day.'"'.($day == $sday ? ' selected':'').'>'.$day.'</option>';
+	}
+	$retstring.="</select>";
+	print $retstring;
+	print '</td>';
+	// Button Start
+	print '<td>';
+	if ($action == 'start' && GETPOST('posnumber') != '' && GETPOST('posnumber') != '' && GETPOST('posnumber') != '-1')
+	{
+		print '';
+	}
+	else
+	{
+		print '<input type="submit" name="add" class="button" value="'.$langs->trans("Start").'">';
+	}
+	print '</td>';
+	print '</table>';
+
+	if ($action == 'start' && GETPOST('posnumber') != '' && GETPOST('posnumber') != '' && GETPOST('posnumber') != '-1')
+	{
+		$posmodule = GETPOST('posmodule','alpha');
+		$terminalid = GETPOST('posnumber','alpha');
+
+		print '<br>';
+
+		print '<table class="noborder" width="100%">';
+
+		print '<tr class="liste_titre">';
+		print '<td></td>';
+		print '<td align="center">'.$langs->trans("InitialBankBalance");
+		//print '<br>'.$langs->trans("TheoricalAmount").'<br>'.$langs->trans("RealAmount");
+		print '</td>';
+		print '<td align="center" class="hide0" colspan="'.count($arrayofpaymentmode).'">';
+		print $langs->trans("AmountAtEndOfPeriod");
+		print '</td>';
+		print '<td></td>';
+		print '</tr>';
+
+		print '<tr class="liste_titre">';
+		print '<td></td>';
+		print '<td align="center">'.$langs->trans("Cash");
+		//print '<br>'.$langs->trans("TheoricalAmount").'<br>'.$langs->trans("RealAmount");
+		print '</td>';
+		$i=0;
+		foreach($arrayofpaymentmode as $key => $val)
+		{
+			print '<td align="center"'.($i == 0 ? ' class="hide0"':'').'>'.$langs->trans($val);
+			//print '<br>'.$langs->trans("TheoricalAmount").'<br>'.$langs->trans("RealAmount");
+			print '</td>';
+			$i++;
+		}
+		print '<td></td>';
+		print '</tr>';
+
+		print '<tr>';
+		// Initial amount
+		print '<td>'.$langs->trans("NbOfInvoices").'</td>';
+		print '<td align="center">';
+		print '</td>';
+		// Amount per payment type
+		$i=0;
+		foreach($arrayofpaymentmode as $key => $val)
+		{
+		    print '<td align="center"'.($i == 0 ? ' class="hide0"':'').'>';
+		    print $theoricalnbofinvoiceforterminal[$terminalid][$key];
+		    print '</td>';
+		    $i++;
+		}
+		// Save
+		print '<td align="center"></td>';
+		print '</tr>';
+
+		print '<tr>';
+		// Initial amount
+		print '<td>'.$langs->trans("TheoricalAmount").'</td>';
+		print '<td align="center">';
+		print price($initialbalanceforterminal[$terminalid]['cash']).'<br>';
+		print '</td>';
+		// Amount per payment type
+		$i=0;
+		foreach($arrayofpaymentmode as $key => $val)
+		{
+			print '<td align="center"'.($i == 0 ? ' class="hide0"':'').'>';
+			print price($theoricalamountforterminal[$terminalid][$key]).'<br>';
+			print '</td>';
+			$i++;
+		}
+		// Save
+		print '<td align="center"></td>';
+		print '</tr>';
+
+		print '<tr>';
+		print '<td>'.$langs->trans("RealAmount").'</td>';
+		// Initial amount
+		print '<td align="center">';
+		print '<input name="opening" type="text" class="maxwidth100 center" value="'.(GETPOSTISSET('opening')?price2num(GETPOST('opening','alpha')):price($initialbalanceforterminal[$terminalid]['cash'])).'">';
+		print '</td>';
+		// Amount per payment type
+		$i=0;
+		foreach($arrayofpaymentmode as $key => $val)
+		{
+			print '<td align="center"'.($i == 0 ? ' class="hide0"':'').'>';
+			print '<input name="'.$key.'_amount" type="text"'.($key == 'cash'?' autofocus':'').' class="maxwidth100 center" value="'.GETPOST($key.'_amount','alpha').'">';
+			print '</td>';
+			$i++;
+		}
+		// Save
+		print '<td align="center">';
+		print '<input type="submit" name="cancel" class="button" value="'.$langs->trans("Cancel").'">';
+		print '<input type="submit" name="add" class="button" value="'.$langs->trans("Save").'">';
+		print '</td>';
+		print '</tr>';
+
+		print '</form>';
+	}
+    print '</form>';
+}
+
+if (empty($action) || $action=="view")
+{
+    $object->fetch($id);
+
+    llxHeader('', $langs->trans("CashControl"));
+
+    $head=array();
+    $head[0][0] = DOL_URL_ROOT.'/compta/cashcontrol/cashcontrol_card.php?id='.$object->id;
+    $head[0][1] = $langs->trans("Card");
+    $head[0][2] = 'cashcontrol';
+
+    dol_fiche_head($head, 'cashcontrol', $langs->trans("CashControl"), -1, 'cashcontrol');
+
+    $linkback = '<a href="' . DOL_URL_ROOT . '/compta/cashcontrol/cashcontrol_list.php?restore_lastsearch_values=1">' . $langs->trans("BackToList") . '</a>';
+
+    $morehtmlref='<div class="refidno">';
+    $morehtmlref.='</div>';
+
+
+    dol_banner_tab($object, 'id', $linkback, 1, 'rowid', 'rowid', $morehtmlref);
+
+    print '<div class="fichecenter">';
+    print '<div class="fichehalfleft">';
+	print '<div class="underbanner clearboth"></div>';
+    print '<table class="border tableforfield" width="100%">';
+
+	print '<tr><td class="titlefield nowrap">';
+	print $langs->trans("Ref");
+	print '</td><td>';
+	print $id;
+	print '</td></tr>';
+
+	print '<tr><td valign="middle">'.$langs->trans("Module").'</td><td>';
+	print $object->posmodule;
+	print "</td></tr>";
+
+	print '<tr><td valign="middle">'.$langs->trans("CashDesk").' ID</td><td>';
+	print $object->posnumber;
+	print "</td></tr>";
+
+	print '<tr><td class="nowrap">';
+	print $langs->trans("Period");
+	print '</td><td>';
+	print $object->year_close."-".$object->month_close."-".$object->day_close;
+	print '</td></tr>';
+
+	print '</table>';
+    print '</div>';
+
+    print '<div class="fichehalfright"><div class="ficheaddleft">';
+	print '<div class="underbanner clearboth"></div>';
+    print '<table class="border tableforfield" width="100%">';
+
+    print '<tr><td class="titlefield nowrap">';
+    print $langs->trans("DateCreationShort");
+    print '</td><td>';
+    print dol_print_date($object->date_creation, 'dayhour');
+    print '</td></tr>';
+
+    print '<tr><td valign="middle">'.$langs->trans("InitialBankBalance").' - '.$langs->trans("Cash").'</td><td>';
+    print price($object->opening, 0, $langs, 1, -1, -1, $conf->currency);
+    print "</td></tr>";
+
+    foreach($arrayofpaymentmode as $key => $val)
+    {
+        print '<tr><td valign="middle">'.$langs->trans($val).'</td><td>';
+    	print price($object->$key, 0, $langs, 1, -1, -1, $conf->currency);
+    	print "</td></tr>";
+    }
+
+	print "</table>\n";
+    print '</div>';
+    print '</div></div>';
+    print '<div style="clear:both"></div>';
+
+    dol_fiche_end();
+
+	print '<div class="tabsAction">';
+	print '<div class="inline-block divButAction"><a target="_blank" class="butAction" href="report.php?id='.$id.'">' . $langs->trans('PrintTicket') . '</a></div>';
+	if ($object->status == CashControl::STATUS_DRAFT)
+	{
+		print '<div class="inline-block divButAction"><a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $id . '&amp;action=close">' . $langs->trans('Close') . '</a></div>';
+
+		print '<div class="inline-block divButAction"><a class="butActionDelete" href="' . $_SERVER["PHP_SELF"] . '?id=' . $id . '&amp;action=confirm_delete">' . $langs->trans('Delete') . '</a></div>';
+	}
+	print '</div>';
+
+	print '<center><iframe src="report.php?id='.$id.'" width="60%" height="800"></iframe></center>';
+}
+
+// End of page
+llxFooter();
+$db->close();

+ 573 - 0
htdocs/compta/cashcontrol/cashcontrol_list.php

@@ -0,0 +1,573 @@
+<?php
+/* Copyright (C) 2007-2017 Laurent Destailleur  <eldy@users.sourceforge.net>
+ * Copyright (C) ---Put here your own copyright and developer email---
+ *
+ * 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       cashcontrol_list.php
+ *		\ingroup    cashdesk|takepos
+ *		\brief      List page for cashcontrol
+ */
+
+//if (! defined('NOREQUIREDB'))              define('NOREQUIREDB','1');					// Do not create database handler $db
+//if (! defined('NOREQUIREUSER'))            define('NOREQUIREUSER','1');				// Do not load object $user
+//if (! defined('NOREQUIRESOC'))             define('NOREQUIRESOC','1');				// Do not load object $mysoc
+//if (! defined('NOREQUIRETRAN'))            define('NOREQUIRETRAN','1');				// Do not load object $langs
+//if (! defined('NOSCANGETFORINJECTION'))    define('NOSCANGETFORINJECTION','1');		// Do not check injection attack on GET parameters
+//if (! defined('NOSCANPOSTFORINJECTION'))   define('NOSCANPOSTFORINJECTION','1');		// Do not check injection attack on POST parameters
+//if (! defined('NOCSRFCHECK'))              define('NOCSRFCHECK','1');					// Do not check CSRF attack (test on referer + on token if option MAIN_SECURITY_CSRF_WITH_TOKEN is on).
+//if (! defined('NOTOKENRENEWAL'))           define('NOTOKENRENEWAL','1');				// Do not roll the Anti CSRF token (used if MAIN_SECURITY_CSRF_WITH_TOKEN is on)
+//if (! defined('NOSTYLECHECK'))             define('NOSTYLECHECK','1');				// Do not check style html tag into posted data
+//if (! defined('NOIPCHECK'))                define('NOIPCHECK','1');					// Do not check IP defined into conf $dolibarr_main_restrict_ip
+//if (! defined('NOREQUIREMENU'))            define('NOREQUIREMENU','1');				// If there is no need to load and show top and left menu
+//if (! defined('NOREQUIREHTML'))            define('NOREQUIREHTML','1');				// If we don't need to load the html.form.class.php
+//if (! defined('NOREQUIREAJAX'))            define('NOREQUIREAJAX','1');       	  	// Do not load ajax.lib.php library
+//if (! defined("NOLOGIN"))                  define("NOLOGIN",'1');						// If this page is public (can be called outside logged session)
+//if (! defined("MAIN_LANG_DEFAULT"))        define('MAIN_LANG_DEFAULT','auto');					// Force lang to a particular value
+//if (! defined("MAIN_AUTHENTICATION_MODE")) define('MAIN_AUTHENTICATION_MODE','aloginmodule');		// Force authentication handler
+//if (! defined("NOREDIRECTBYMAINTOLOGIN"))  define('NOREDIRECTBYMAINTOLOGIN',1);		// The main.inc.php does not make a redirect if not logged, instead show simple error message
+
+require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/compta/cashcontrol/class/cashcontrol.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
+
+// Load translation files required by the page
+$langs->loadLangs(array("banks","other"));
+
+$action     = GETPOST('action','aZ09')?GETPOST('action','aZ09'):'view';				// The action 'add', 'create', 'edit', 'update', 'view', ...
+$massaction = GETPOST('massaction','alpha');											// The bulk action (combo box choice into lists)
+$show_files = GETPOST('show_files','int');												// Show files area generated by bulk actions ?
+$confirm    = GETPOST('confirm','alpha');												// Result of a confirmation
+$cancel     = GETPOST('cancel', 'alpha');												// We click on a Cancel button
+$toselect   = GETPOST('toselect', 'array');												// Array of ids of elements selected into a list
+$contextpage= GETPOST('contextpage','aZ')?GETPOST('contextpage','aZ'):'cashcontrol';    // To manage different context of search
+$backtopage = GETPOST('backtopage','alpha');											// Go back to a dedicated page
+$optioncss  = GETPOST('optioncss','aZ');												// Option for the css output (always '' except when 'print')
+
+$id			= GETPOST('id','int');
+
+// Load variable for pagination
+$limit = GETPOST('limit','int')?GETPOST('limit','int'):$conf->liste_limit;
+$sortfield = GETPOST('sortfield','alpha');
+$sortorder = GETPOST('sortorder','alpha');
+$page = GETPOST('page','int');
+if (empty($page) || $page == -1 || GETPOST('button_search','alpha') || GETPOST('button_removefilter','alpha') || (empty($toselect) && $massaction === '0')) { $page = 0; }     // If $page is not defined, or '' or -1 or if we click on clear filters or if we select empty mass action
+$offset = $limit * $page;
+$pageprev = $page - 1;
+$pagenext = $page + 1;
+//if (! $sortfield) $sortfield="p.date_fin";
+//if (! $sortorder) $sortorder="DESC";
+
+// Initialize technical objects
+$object=new CashControl($db);
+$extrafields = new ExtraFields($db);
+$diroutputmassaction=$conf->monmodule->dir_output . '/temp/massgeneration/'.$user->id;
+$hookmanager->initHooks(array('cashcontrol'));     // Note that conf->hooks_modules contains array
+// Fetch optionals attributes and labels
+$extralabels = $extrafields->fetch_name_optionals_label('cashcontrol');	// Load $extrafields->attributes['cashcontrol']
+$search_array_options=$extrafields->getOptionalsFromPost($object->table_element,'','search_');
+
+// Default sort order (if not yet defined by previous GETPOST)
+if (! $sortfield) $sortfield="t.".key($object->fields);   // Set here default search field. By default 1st field in definition.
+if (! $sortorder) $sortorder="ASC";
+
+// Security check
+$socid=0;
+if ($user->societe_id > 0)	// Protection if external user
+{
+	//$socid = $user->societe_id;
+	accessforbidden();
+}
+//$result = restrictedArea($user, 'monmodule', $id, '');
+
+// Initialize array of search criterias
+$search_all=trim(GETPOST("search_all",'alpha'));
+$search=array();
+foreach($object->fields as $key => $val)
+{
+	if (GETPOST('search_'.$key,'alpha')) $search[$key]=GETPOST('search_'.$key,'alpha');
+}
+
+// List of fields to search into when doing a "search in all"
+$fieldstosearchall = array();
+foreach($object->fields as $key => $val)
+{
+	if ($val['searchall']) $fieldstosearchall['t.'.$key]=$val['label'];
+}
+
+// Definition of fields for list
+$arrayfields=array();
+foreach($object->fields as $key => $val)
+{
+	// If $val['visible']==0, then we never show the field
+	if (! empty($val['visible'])) $arrayfields['t.'.$key]=array('label'=>$val['label'], 'checked'=>(($val['visible']<0)?0:1), 'enabled'=>$val['enabled'], 'position'=>$val['position']);
+}
+// Extra fields
+if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0)
+{
+	foreach($extrafields->attributes[$object->table_element]['label'] as $key => $val)
+	{
+		if (! empty($extrafields->attributes[$object->table_element]['list'][$key]))
+			$arrayfields["ef.".$key]=array('label'=>$extrafields->attributes[$object->table_element]['label'][$key], 'checked'=>(($extrafields->attributes[$object->table_element]['list'][$key]<0)?0:1), 'position'=>$extrafields->attributes[$object->table_element]['pos'][$key], 'enabled'=>(abs($extrafields->attributes[$object->table_element]['list'][$key])!=3 && $extrafields->attributes[$object->table_element]['perms'][$key]));
+	}
+}
+$object->fields = dol_sort_array($object->fields, 'position');
+$arrayfields = dol_sort_array($arrayfields, 'position');
+
+
+
+/*
+ * Actions
+ */
+
+if (GETPOST('cancel','alpha')) { $action='list'; $massaction=''; }
+if (! GETPOST('confirmmassaction','alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { $massaction=''; }
+
+$parameters=array();
+$reshook=$hookmanager->executeHooks('doActions', $parameters, $object, $action);    // Note that $action and $object may have been modified by some hooks
+if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
+
+if (empty($reshook))
+{
+	// Selection of new fields
+	include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
+
+	// Purge search criteria
+	if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') ||GETPOST('button_removefilter','alpha')) // All tests are required to be compatible with all browsers
+	{
+		foreach($object->fields as $key => $val)
+		{
+			$search[$key]='';
+		}
+		$toselect='';
+		$search_array_options=array();
+	}
+	if (GETPOST('button_removefilter_x','alpha') || GETPOST('button_removefilter.x','alpha') || GETPOST('button_removefilter','alpha')
+		|| GETPOST('button_search_x','alpha') || GETPOST('button_search.x','alpha') || GETPOST('button_search','alpha'))
+	{
+		$massaction='';     // Protection to avoid mass action if we force a new search during a mass action confirmation
+	}
+
+	// Mass actions
+	$objectclass='CashControl';
+	$objectlabel='CashControl';
+	$permtoread = ($user->rights->cashdesk->use || $user->rights->takepos->use);
+	$permtodelete = ($user->rights->cashdesk->use || $user->rights->takepos->use);
+
+	//$uploaddir = '';
+	//include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php';
+}
+
+
+
+/*
+ * View
+ */
+
+$form=new Form($db);
+
+$now=dol_now();
+
+//$help_url="EN:Module_pos_cash_fence|FR:Module_pos_cash_fence_FR|ES:Módulo_pos_cash_fence";
+$help_url='';
+$title = $langs->trans('CashControl');
+
+
+// Build and execute select
+// --------------------------------------------------------------------
+$sql = 'SELECT ';
+foreach($object->fields as $key => $val)
+{
+	$sql.='t.'.$key.', ';
+}
+// Add fields from extrafields
+if (! empty($extrafields->attributes[$object->table_element]['label']))
+{
+	foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.' as options_'.$key.', ' : '');
+}
+// Add fields from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListSelect', $parameters, $object);    // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+$sql=preg_replace('/, $/','', $sql);
+$sql.= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t";
+if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)";
+if ($object->ismultientitymanaged == 1) $sql.= " WHERE t.entity IN (".getEntity($object->element).")";
+else $sql.=" WHERE 1 = 1";
+foreach($search as $key => $val)
+{
+	if ($key == 'status' && $search[$key] == -1) continue;
+	$mode_search=(($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key]))?1:0);
+	if ($search[$key] != '') $sql.=natural_search($key, $search[$key], (($key == 'status')?2:$mode_search));
+}
+if ($search_all) $sql.= natural_search(array_keys($fieldstosearchall), $search_all);
+// Add where from extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_sql.tpl.php';
+// Add where from hooks
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldListWhere', $parameters, $object);    // Note that $action and $object may have been modified by hook
+$sql.=$hookmanager->resPrint;
+
+/* If a group by is required
+ $sql.= " GROUP BY "
+ foreach($object->fields as $key => $val)
+ {
+ $sql.='t.'.$key.', ';
+ }
+ // Add fields from extrafields
+ if (! empty($extrafields->attributes[$object->table_element]['label'])) {
+ foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) $sql.=($extrafields->attributes[$object->table_element]['type'][$key] != 'separate' ? "ef.".$key.', ' : '');
+ // Add where from hooks
+ $parameters=array();
+ $reshook=$hookmanager->executeHooks('printFieldListGroupBy',$parameters);    // Note that $action and $object may have been modified by hook
+ $sql.=$hookmanager->resPrint;
+ $sql=preg_replace('/, $/','', $sql);
+ */
+
+$sql.=$db->order($sortfield,$sortorder);
+
+// Count total nb of records
+$nbtotalofrecords = '';
+if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST))
+{
+	$resql = $db->query($sql);
+	$nbtotalofrecords = $db->num_rows($resql);
+	if (($page * $limit) > $nbtotalofrecords)	// if total of record found is smaller than page * limit, goto and load page 0
+	{
+		$page = 0;
+		$offset = 0;
+	}
+}
+// if total of record found is smaller than limit, no need to do paging and to restart another select with limits set.
+if (is_numeric($nbtotalofrecords) && $limit > $nbtotalofrecords)
+{
+	$num = $nbtotalofrecords;
+}
+else
+{
+	$sql.= $db->plimit($limit+1, $offset);
+
+	$resql=$db->query($sql);
+	if (! $resql)
+	{
+		dol_print_error($db);
+		exit;
+	}
+
+	$num = $db->num_rows($resql);
+}
+
+// Direct jump if only one record found
+if ($num == 1 && ! empty($conf->global->MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE) && $search_all)
+{
+	$obj = $db->fetch_object($resql);
+	$id = $obj->rowid;
+	header("Location: ".dol_buildpath('/compta/cashcontrol/cashcontrol_card.php', 1).'?id='.$id);
+	exit;
+}
+
+
+// Output page
+// --------------------------------------------------------------------
+
+llxHeader('', $title, $help_url);
+
+// Example : Adding jquery code
+print '<script type="text/javascript" language="javascript">
+jQuery(document).ready(function() {
+	function init_myfunc()
+	{
+		jQuery("#myid").removeAttr(\'disabled\');
+		jQuery("#myid").attr(\'disabled\',\'disabled\');
+	}
+	init_myfunc();
+	jQuery("#mybutton").click(function() {
+		init_myfunc();
+	});
+});
+</script>';
+
+$arrayofselected=is_array($toselect)?$toselect:array();
+
+$param='';
+if (! empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) $param.='&contextpage='.urlencode($contextpage);
+if ($limit > 0 && $limit != $conf->liste_limit) $param.='&limit='.urlencode($limit);
+foreach($search as $key => $val)
+{
+	$param.= '&search_'.$key.'='.urlencode($search[$key]);
+}
+if ($optioncss != '')     $param.='&optioncss='.urlencode($optioncss);
+// Add $param from extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
+
+// List of mass actions available
+$arrayofmassactions =  array(
+//'presend'=>$langs->trans("SendByMail"),
+//'builddoc'=>$langs->trans("PDFMerge"),
+);
+if ($user->rights->monmodule->delete) $arrayofmassactions['predelete']=$langs->trans("Delete");
+if (GETPOST('nomassaction','int') || in_array($massaction, array('presend','predelete'))) $arrayofmassactions=array();
+$massactionbutton=$form->selectMassAction('', $arrayofmassactions);
+
+print '<form method="POST" id="searchFormList" action="'.$_SERVER["PHP_SELF"].'">';
+if ($optioncss != '') print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
+print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
+print '<input type="hidden" name="action" value="list">';
+print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
+print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
+print '<input type="hidden" name="page" value="'.$page.'">';
+print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
+
+$newcardbutton='';
+//if ($user->rights->monmodule->creer)
+	//{
+$newcardbutton='<a class="butActionNew" href="cashcontrol_card.php?action=create&backtopage='.urlencode($_SERVER['PHP_SELF']).'"><span class="valignmiddle">'.$langs->trans('New').'</span>';
+$newcardbutton.= '<span class="fa fa-plus-circle valignmiddle"></span>';
+$newcardbutton.= '</a>';
+//}
+
+print_barre_liste($title, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'title_companies', 0, $newcardbutton, '', $limit);
+
+// Add code for pre mass action (confirmation or email presend form)
+$topicmail="SendCashControlRef";
+$modelmail="cashcontrol";
+$objecttmp=new CashControl($db);
+$trackid='xxxx'.$object->id;
+include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php';
+
+if ($sall)
+{
+	foreach($fieldstosearchall as $key => $val) $fieldstosearchall[$key]=$langs->trans($val);
+	print '<div class="divsearchfieldfilter">'.$langs->trans("FilterOnInto", $sall) . join(', ',$fieldstosearchall).'</div>';
+}
+
+$moreforfilter = '';
+/*$moreforfilter.='<div class="divsearchfield">';
+ $moreforfilter.= $langs->trans('MyFilter') . ': <input type="text" name="search_myfield" value="'.dol_escape_htmltag($search_myfield).'">';
+ $moreforfilter.= '</div>';*/
+
+$parameters=array();
+$reshook=$hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object);    // Note that $action and $object may have been modified by hook
+if (empty($reshook)) $moreforfilter .= $hookmanager->resPrint;
+else $moreforfilter = $hookmanager->resPrint;
+
+if (! empty($moreforfilter))
+{
+	print '<div class="liste_titre liste_titre_bydiv centpercent">';
+	print $moreforfilter;
+	print '</div>';
+}
+
+$varpage=empty($contextpage)?$_SERVER["PHP_SELF"]:$contextpage;
+$selectedfields=$form->multiSelectArrayWithCheckbox('selectedfields', $arrayfields, $varpage);	// This also change content of $arrayfields
+$selectedfields.=(count($arrayofmassactions) ? $form->showCheckAddButtons('checkforselect', 1) : '');
+
+print '<div class="div-table-responsive">';		// You can use div-table-responsive-no-min if you dont need reserved height for your table
+print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
+
+
+// Fields title search
+// --------------------------------------------------------------------
+print '<tr class="liste_titre">';
+foreach($object->fields as $key => $val)
+{
+	$cssforfield='';
+	if (in_array($val['type'], array('date','datetime','timestamp'))) $cssforfield.=($cssforfield?' ':'').'center';
+	if (in_array($val['type'], array('timestamp'))) $cssforfield.=($cssforfield?' ':'').'nowrap';
+	if ($key == 'status') $cssforfield.=($cssforfield?' ':'').'center';
+	if (! empty($arrayfields['t.'.$key]['checked'])) print '<td class="liste_titre'.($cssforfield?' '.$cssforfield:'').'"><input type="text" class="flat maxwidth75" name="search_'.$key.'" value="'.dol_escape_htmltag($search[$key]).'"></td>';
+}
+// Extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_input.tpl.php';
+
+// Fields from hook
+$parameters=array('arrayfields'=>$arrayfields);
+$reshook=$hookmanager->executeHooks('printFieldListOption', $parameters, $object);    // Note that $action and $object may have been modified by hook
+print $hookmanager->resPrint;
+// Action column
+print '<td class="liste_titre" align="right">';
+$searchpicto=$form->showFilterButtons();
+print $searchpicto;
+print '</td>';
+print '</tr>'."\n";
+
+
+// Fields title label
+// --------------------------------------------------------------------
+print '<tr class="liste_titre">';
+foreach($object->fields as $key => $val)
+{
+	$cssforfield='';
+	if (in_array($val['type'], array('date','datetime','timestamp'))) $cssforfield.=($cssforfield?' ':'').'center';
+	if (in_array($val['type'], array('timestamp'))) $cssforfield.=($cssforfield?' ':'').'nowrap';
+	if ($key == 'status') $cssforfield.=($cssforfield?' ':'').'center';
+	if (! empty($arrayfields['t.'.$key]['checked']))
+	{
+		print getTitleFieldOfList($arrayfields['t.'.$key]['label'], 0, $_SERVER['PHP_SELF'], 't.'.$key, '', $param, ($cssforfield?'class="'.$cssforfield.'"':''), $sortfield, $sortorder, ($cssforfield?$cssforfield.' ':''))."\n";
+	}
+}
+// Extra fields
+include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_title.tpl.php';
+// Hook fields
+$parameters=array('arrayfields'=>$arrayfields,'param'=>$param,'sortfield'=>$sortfield,'sortorder'=>$sortorder);
+$reshook=$hookmanager->executeHooks('printFieldListTitle', $parameters, $object);    // Note that $action and $object may have been modified by hook
+print $hookmanager->resPrint;
+print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"],'','','','align="center"',$sortfield,$sortorder,'maxwidthsearch ')."\n";
+print '</tr>'."\n";
+
+
+// Detect if we need a fetch on each output line
+$needToFetchEachLine=0;
+if (is_array($extrafields->attributes[$object->table_element]['computed']) && count($extrafields->attributes[$object->table_element]['computed']) > 0)
+{
+	foreach ($extrafields->attributes[$object->table_element]['computed'] as $key => $val)
+	{
+		if (preg_match('/\$object/',$val)) $needToFetchEachLine++;  // There is at least one compute field that use $object
+	}
+}
+
+
+// Loop on record
+// --------------------------------------------------------------------
+$i=0;
+$totalarray=array();
+while ($i < min($num, $limit))
+{
+	$obj = $db->fetch_object($resql);
+	if (empty($obj)) break;		// Should not happen
+
+	// Store properties in $object
+	$object->id = $obj->rowid;
+	foreach($object->fields as $key => $val)
+	{
+		if (isset($obj->$key)) $object->$key = $obj->$key;
+	}
+
+	// Show here line of result
+	print '<tr class="oddeven">';
+	foreach($object->fields as $key => $val)
+	{
+		$cssforfield='';
+		if (in_array($val['type'], array('date','datetime','timestamp'))) $cssforfield.=($cssforfield?' ':'').'center';
+		if (in_array($val['type'], array('timestamp'))) $cssforfield.=($cssforfield?' ':'').'nowrap';
+		if ($key == 'status') $cssforfield.=($cssforfield?' ':'').'center';
+		if (! empty($arrayfields['t.'.$key]['checked']))
+		{
+			print '<td';
+			if ($cssforfield || $val['css']) print ' class="';
+			print $cssforfield;
+			if ($cssforfield && $val['css']) print ' ';
+			print $val['css'];
+			if ($cssforfield || $val['css']) print '"';
+			print '>';
+			print $object->showOutputField($val, $key, $obj->$key, '');
+			print '</td>';
+			if (! $i) $totalarray['nbfield']++;
+			if (! empty($val['isameasure']))
+			{
+				if (! $i) $totalarray['pos'][$totalarray['nbfield']]='t.'.$key;
+				$totalarray['val']['t.'.$key] += $obj->$key;
+			}
+		}
+	}
+	// Extra fields
+	include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_print_fields.tpl.php';
+	// Fields from hook
+	$parameters=array('arrayfields'=>$arrayfields, 'obj'=>$obj);
+	$reshook=$hookmanager->executeHooks('printFieldListValue', $parameters, $object);    // Note that $action and $object may have been modified by hook
+	print $hookmanager->resPrint;
+	// Action column
+	print '<td class="nowrap" align="center">';
+	if ($massactionbutton || $massaction)   // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined
+	{
+		$selected=0;
+		if (in_array($obj->rowid, $arrayofselected)) $selected=1;
+		print '<input id="cb'.$obj->rowid.'" class="flat checkforselect" type="checkbox" name="toselect[]" value="'.$obj->rowid.'"'.($selected?' checked="checked"':'').'>';
+	}
+	print '</td>';
+	if (! $i) $totalarray['nbfield']++;
+
+	print '</tr>';
+
+	$i++;
+}
+
+// Show total line
+if (isset($totalarray['pos']))
+{
+	print '<tr class="liste_total">';
+	$i=0;
+	while ($i < $totalarray['nbfield'])
+	{
+		$i++;
+		if (! empty($totalarray['pos'][$i]))  print '<td align="right">'.price($totalarray['val'][$totalarray['pos'][$i]]).'</td>';
+		else
+		{
+			if ($i == 1)
+			{
+				if ($num < $limit) print '<td align="left">'.$langs->trans("Total").'</td>';
+				else print '<td align="left">'.$langs->trans("Totalforthispage").'</td>';
+			}
+			else print '<td></td>';
+		}
+	}
+	print '</tr>';
+}
+
+// If no record found
+if ($num == 0)
+{
+	$colspan=1;
+	foreach($arrayfields as $key => $val) { if (! empty($val['checked'])) $colspan++; }
+	print '<tr><td colspan="'.$colspan.'" class="opacitymedium">'.$langs->trans("NoRecordFound").'</td></tr>';
+}
+
+$db->free($resql);
+
+$parameters=array('arrayfields'=>$arrayfields, 'sql'=>$sql);
+$reshook=$hookmanager->executeHooks('printFieldListFooter', $parameters, $object);    // Note that $action and $object may have been modified by hook
+print $hookmanager->resPrint;
+
+print '</table>'."\n";
+print '</div>'."\n";
+
+print '</form>'."\n";
+
+if (in_array('builddoc',$arrayofmassactions) && ($nbtotalofrecords === '' || $nbtotalofrecords))
+{
+	$hidegeneratedfilelistifempty=1;
+	if ($massaction == 'builddoc' || $action == 'remove_file' || $show_files) $hidegeneratedfilelistifempty=0;
+
+	require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
+	$formfile = new FormFile($db);
+
+	// Show list of available documents
+	$urlsource=$_SERVER['PHP_SELF'].'?sortfield='.$sortfield.'&sortorder='.$sortorder;
+	$urlsource.=str_replace('&amp;','&',$param);
+
+	$filedir=$diroutputmassaction;
+	$genallowed=$user->rights->monmodule->read;
+	$delallowed=$user->rights->monmodule->create;
+
+	print $formfile->showdocuments('massfilesarea_monmodule','',$filedir,$urlsource,0,$delallowed,'',1,1,0,48,1,$param,$title,'','','',null,$hidegeneratedfilelistifempty);
+}
+
+// End of page
+llxFooter();
+$db->close();

+ 429 - 0
htdocs/compta/cashcontrol/class/cashcontrol.class.php

@@ -0,0 +1,429 @@
+<?php
+/* Copyright (C) 2008 Laurent Destailleur  <eldy@users.sourceforge.net>
+ * Copyright (C) 2009 Regis Houssin        <regis.houssin@capnetworks.com>
+ * Copyright (C) 2016 Marcos García        <marcosgdf@gmail.com>
+ * Copyright (C) 2018 Andreu Bisquerra     <jove@bisquerra.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       cashcontrol/class/cashcontrol.class.php
+ * \ingroup    cashdesk|takepos
+ * \brief      This file is CRUD class file (Create/Read/Update/Delete) for cash fence table
+ */
+
+/**
+ *    Class to manage cash fence
+ */
+class CashControl extends CommonObject
+{
+	/**
+	 * @var string ID to identify managed object
+	 */
+	public $element = 'cashcontrol';
+
+	/**
+	 * @var string Name of table without prefix where object is stored
+	 */
+	public $table_element = 'pos_cash_fence';
+
+	/**
+	 * @var int  Does pos_cash_fence support multicompany module ? 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
+	 */
+	public $ismultientitymanaged = 1;
+
+	/**
+	 * @var int  Does pos_cash_fence support extrafields ? 0=No, 1=Yes
+	 */
+	public $isextrafieldmanaged = 0;
+
+	/**
+	 * @var string String with name of icon for pos_cash_fence. Must be the part after the 'object_' into object_pos_cash_fence.png
+	 */
+	public $picto = 'account';
+
+	public $fields=array(
+	'rowid' =>array('type'=>'integer', 'label'=>'ID', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'position'=>10),
+	'entity' =>array('type'=>'integer', 'label'=>'Entity', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>15),
+	'ref' =>array('type'=>'varchar(64)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>1, 'notnull'=>1, 'position'=>18),
+	'posmodule' =>array('type'=>'varchar(30)', 'label'=>'Module', 'enabled'=>1, 'visible'=>1, 'notnul'=>1, 'position'=>19),
+	'posnumber' =>array('type'=>'varchar(30)', 'label'=>'CashDesk', 'enabled'=>1, 'visible'=>1, 'notnul'=>1, 'position'=>20),
+	'label' =>array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>1, 'visible'=>0, 'position'=>24),
+	'opening' =>array('type'=>'double(24,8)', 'label'=>'Opening', 'enabled'=>1, 'visible'=>1, 'position'=>25),
+	'cash' =>array('type'=>'double(24,8)', 'label'=>'Cash', 'enabled'=>1, 'visible'=>1, 'position'=>30),
+	'cheque' =>array('type'=>'double(24,8)', 'label'=>'Cheque', 'enabled'=>1, 'visible'=>1, 'position'=>33),
+	'card' =>array('type'=>'double(24,8)', 'label'=>'CreditCard', 'enabled'=>1, 'visible'=>1, 'position'=>36),
+	'year_close' =>array('type'=>'integer', 'label'=>'Year close', 'enabled'=>1, 'visible'=>1, 'notnul'=>1, 'position'=>50),
+	'month_close' =>array('type'=>'integer', 'label'=>'Month close', 'enabled'=>1, 'visible'=>1, 'position'=>55),
+	'day_close' =>array('type'=>'integer', 'label'=>'Day close', 'enabled'=>1, 'visible'=>1, 'position'=>60),
+	'date_valid' =>array('type'=>'datetime', 'label'=>'DateValid', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>490),
+	'date_creation' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>500),
+	'tms' =>array('type'=>'timestamp', 'label'=>'Tms', 'enabled'=>1, 'visible'=>0, 'notnull'=>1, 'position'=>505),
+	'import_key' =>array('type'=>'varchar(14)', 'label'=>'Import key', 'enabled'=>1, 'visible'=>0, 'position'=>510),
+	'status' => array('type'=>'integer', 'label'=>'Status', 'enabled'=>1, 'visible'=>1, 'position'=>1000, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array('0'=>'Brouillon', '1'=>'Validated')),
+	);
+
+	public $id;
+	public $opening;
+	public $status;
+	public $year_close;
+	public $month_close;
+	public $day_close;
+	public $posmodule;
+	public $posnumber;
+	public $cash;
+	public $cheque;
+	public $card;
+	public $date_valid;
+	public $date_creation;
+	public $date_modification;
+
+	const STATUS_DRAFT = 0;
+	const STATUS_VALIDATED = 1;
+
+
+	/**
+	 * Constructor
+	 *
+	 * @param DoliDB $db Database handler
+	 */
+	public function __construct(DoliDB $db)
+	{
+		$this->db = $db;
+	}
+
+
+	/**
+	 *  Create in database
+	 *
+	 * @param  User $user User that create
+	 * @param  int $notrigger 0=launch triggers after, 1=disable triggers
+	 * @return int <0 if KO, Id of created object if OK
+	 */
+	public function create(User $user, $notrigger = 0)
+	{
+		global $conf;
+
+		$error = 0;
+
+		// Clean data
+		if (empty($this->cash)) $this->cash=0;
+		if (empty($this->cheque)) $this->cheque=0;
+		if (empty($this->card)) $this->card=0;
+
+		// Insert request
+		$sql = "INSERT INTO ".MAIN_DB_PREFIX."pos_cash_fence (";
+		$sql .= "entity";
+		//$sql .= ", ref";
+		$sql .= ", opening";
+		$sql .= ", status";
+		$sql .= ", date_creation";
+		$sql .= ", posmodule";
+		$sql .= ", posnumber";
+		$sql .= ", day_close";
+		$sql .= ", month_close";
+		$sql .= ", year_close";
+		$sql .= ", cash";
+		$sql .= ", cheque";
+		$sql .= ", card";
+		$sql .= ") VALUES (";
+		//$sql .= "'(PROV)', ";
+		$sql .= $conf->entity;
+		$sql .= ", ".(is_numeric($this->opening) ? $this->opening : 0);
+		$sql .= ", 0";										// Draft by default
+		$sql .= ", '".$this->db->idate(dol_now())."'";
+		$sql .= ", '".$this->db->escape($this->posmodule)."'";
+		$sql .= ", '".$this->db->escape($this->posnumber)."'";
+		$sql .= ", ".($this->day_close > 0 ? $this->day_close : "null");
+		$sql .= ", ".($this->month_close > 0 ? $this->month_close : "null");
+		$sql .= ", ".$this->year_close;
+		$sql .= ", ".$this->cash;
+		$sql .= ", ".$this->cheque;
+		$sql .= ", ".$this->card;
+		$sql .= ")";
+
+		$this->db->begin();
+
+		dol_syslog(get_class($this)."::create", LOG_DEBUG);
+		$resql = $this->db->query($sql);
+		if (!$resql) {
+			$error++;
+			$this->errors[] = "Error ".$this->db->lasterror();
+		}
+
+		if (!$error) {
+			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."pos_cash_fence");
+
+			$sql = 'UPDATE '.MAIN_DB_PREFIX.'pos_cash_fence SET ref = rowid where rowid = '.$this->id;
+			$this->db->query($sql);
+		}
+
+		// Commit or rollback
+		if ($error) {
+			foreach ($this->errors as $errmsg) {
+				dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
+				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
+			}
+			$this->db->rollback();
+			return -1 * $error;
+		} else {
+			$this->db->commit();
+			return $this->id;
+		}
+	}
+
+	/**
+	 * Validate cash fence
+	 *
+	 * @param 	User 		$user		User
+	 * @param 	number 		$notrigger	No trigger
+	 * @return 	int						<0 if KO, >0 if OK
+	 */
+	public function valid(User $user, $notrigger = 0)
+	{
+		global $conf,$langs;
+		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
+
+		$error = 0;
+
+		// Protection
+		if ($this->status == self::STATUS_VALIDATED)
+		{
+			dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
+			return 0;
+		}
+
+		/*
+		 $posmodule = $this->posmodule;
+		 if (! empty($user->rights->$posmodule->use))
+		 {
+		 $this->error='NotEnoughPermissions';
+		 dol_syslog(get_class($this)."::valid ".$this->error, LOG_ERR);
+		 return -1;
+		 }
+		 */
+
+		$now=dol_now();
+
+		// Update request
+		$sql = "UPDATE ".MAIN_DB_PREFIX."pos_cash_fence";
+		$sql.= " SET status = ".self::STATUS_VALIDATED.",";
+		$sql.= " date_valid='".$this->db->idate($now)."',";
+		$sql.= " fk_user_valid = ".$user->id;
+		$sql.= " WHERE rowid=".$this->id;
+
+		$this->db->begin();
+
+		dol_syslog(get_class($this)."::close", LOG_DEBUG);
+		$resql = $this->db->query($sql);
+		if (!$resql) {
+			$error++;
+			$this->errors[] = "Error ".$this->db->lasterror();
+		}
+
+		if (!$error) {
+			$this->status = self::STATUS_VALIDATED;
+			$this->date_valid = $now;
+			$this->fk_user_valid = $user->id;
+		}
+
+		if (! $error && ! $notrigger)
+		{
+			// Call trigger
+			$result=$this->call_trigger('CASHCONTROL_VALIDATE', $user);
+			if ($result < 0) $error++;
+			// End call triggers
+		}
+
+		// Commit or rollback
+		if ($error) {
+			foreach ($this->errors as $errmsg) {
+				dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
+				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
+			}
+			$this->db->rollback();
+			return -1 * $error;
+		} else {
+			$this->db->commit();
+			return $this->id;
+		}
+	}
+
+
+	/**
+	 * Load object in memory from the database
+	 *
+	 * @param int    $id   Id object
+	 * @param string $ref  Ref
+	 * @return int         <0 if KO, 0 if not found, >0 if OK
+	 */
+	public function fetch($id, $ref = null)
+	{
+		$result = $this->fetchCommon($id, $ref);
+		if ($result > 0 && ! empty($this->table_element_line)) $this->fetchLines();
+		return $result;
+	}
+
+	/**
+	 * Delete object in database
+	 *
+	 * @param User $user       User that deletes
+	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
+	 * @return int             <0 if KO, >0 if OK
+	 */
+	public function delete(User $user, $notrigger = false)
+	{
+	    return $this->deleteCommon($user, $notrigger);
+	    //return $this->deleteCommon($user, $notrigger, 1);
+	}
+
+	/**
+	 *  Return label of the status
+	 *
+	 *  @param  int		$mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
+	 *  @return	string 			       Label of status
+	 */
+	public function getLibStatut($mode=0)
+	{
+		return $this->LibStatut($this->status, $mode);
+	}
+
+	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
+	/**
+	 *  Return the status
+	 *
+	 *  @param	int		$status        Id status
+	 *  @param  int		$mode          0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
+	 *  @return string 			       Label of status
+	 */
+	public function LibStatut($status, $mode=0)
+	{
+		// phpcs:enable
+		if (empty($this->labelstatus))
+		{
+			global $langs;
+			//$langs->load("mymodule");
+			$this->labelstatus[0] = $langs->trans('Draft');
+			$this->labelstatus[1] = $langs->trans('Closed');
+		}
+
+		if ($mode == 0)
+		{
+			return $this->labelstatus[$status];
+		}
+		elseif ($mode == 1)
+		{
+			return $this->labelstatus[$status];
+		}
+		elseif ($mode == 2)
+		{
+			if ($status == 1) return img_picto($this->labelstatus[$status],'statut6', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status];
+			elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut0', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status];
+		}
+		elseif ($mode == 3)
+		{
+			if ($status == 1) return img_picto($this->labelstatus[$status],'statut6', '', false, 0, 0, '', 'valignmiddle');
+			elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut0', '', false, 0, 0, '', 'valignmiddle');
+		}
+		elseif ($mode == 4)
+		{
+			if ($status == 1) return img_picto($this->labelstatus[$status],'statut6', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status];
+			elseif ($status == 0) return img_picto($this->labelstatus[$status],'statut0', '', false, 0, 0, '', 'valignmiddle').' '.$this->labelstatus[$status];
+		}
+		elseif ($mode == 5)
+		{
+			if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut6', '', false, 0, 0, '', 'valignmiddle');
+			elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut0', '', false, 0, 0, '', 'valignmiddle');
+		}
+		elseif ($mode == 6)
+		{
+			if ($status == 1) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut6', '', false, 0, 0, '', 'valignmiddle');
+			elseif ($status == 0) return $this->labelstatus[$status].' '.img_picto($this->labelstatus[$status],'statut0', '', false, 0, 0, '', 'valignmiddle');
+		}
+	}
+
+	/**
+	 *    Return clicable link of object (with eventually picto)
+	 *
+	 * @param  int    $withpicto             Add picto into link
+	 * @param  string $option                Where point the link ('stock', 'composition', 'category', 'supplier', '')
+	 * @param  int    $maxlength             Maxlength of ref
+	 * @param  int    $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
+	 * @param  int    $notooltip			 No tooltip
+	 * @return string                                String with URL
+	 */
+	public function getNomUrl($withpicto=0, $option='', $maxlength=0, $save_lastsearch_value=-1, $notooltip=0)
+	{
+		global $conf, $langs, $hookmanager;
+		include_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
+
+		$result='';
+		$newref=($this->ref?$this->ref:$this->id);
+		if ($maxlength) { $newref=dol_trunc($newref, $maxlength, 'middle'); }
+
+		$label = '<u>' . $langs->trans("ShowCashFence") . '</u>';
+		$label .= '<br><b>' . $langs->trans('ProductRef') . ':</b> ' . ($this->ref?$this->ref:$this->id);
+
+		$linkclose='';
+		if (empty($notooltip)) {
+			if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
+				$label=$langs->trans("ShowCashFence");
+				$linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
+			}
+
+			$linkclose.= ' title="'.dol_escape_htmltag($label, 1, 1).'"';
+			$linkclose.= ' class="classfortooltip"';
+
+			/*
+			 $hookmanager->initHooks(array('productdao'));
+			 $parameters=array('id'=>$this->id);
+			 $reshook=$hookmanager->executeHooks('getnomurltooltip',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
+			 if ($reshook > 0) $linkclose = $hookmanager->resPrint;
+			 */
+		}
+
+		$url = DOL_URL_ROOT.'/compta/cashcontrol/cashcontrol_card.php?id='.$this->id;
+
+		if ($option !== 'nolink') {
+			// Add param to save lastsearch_values or not
+			$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
+			if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) { $add_save_lastsearch_values=1;
+			}
+			if ($add_save_lastsearch_values) { $url.='&save_lastsearch_values=1';
+			}
+		}
+
+		$linkstart = '<a href="'.$url.'"';
+		$linkstart.=$linkclose.'>';
+		$linkend='</a>';
+
+		$result.=$linkstart;
+		if ($withpicto) {
+			$result.=(img_object(($notooltip?'':$label), 'bank', ($notooltip?'class="paddingright"':'class="paddingright classfortooltip"'), 0, 0, $notooltip?0:1));
+		}
+		$result.= $newref;
+		$result.= $linkend;
+
+		global $action;
+		$hookmanager->initHooks(array('cashfencedao'));
+		$parameters=array('id'=>$this->id, 'getnomurl'=>$result);
+		$reshook=$hookmanager->executeHooks('getNomUrl', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
+		if ($reshook > 0) { $result = $hookmanager->resPrint;
+		} else { $result .= $hookmanager->resPrint;
+		}
+
+		return $result;
+	}
+}

+ 286 - 0
htdocs/compta/cashcontrol/report.php

@@ -0,0 +1,286 @@
+<?php
+/* Copyright (C) 2001-2002  Rodolphe Quiedeville <rodolphe@quiedeville.org>
+ * Copyright (C) 2004-2016  Laurent Destailleur  <eldy@users.sourceforge.net>
+ * Copyright (C) 2005-2010  Regis Houssin        <regis.houssin@capnetworks.com>
+ * Copyright (C) 2012       Vinícius Nogueira    <viniciusvgn@gmail.com>
+ * Copyright (C) 2014       Florian Henry        <florian.henry@open-cooncept.pro>
+ * Copyright (C) 2015       Jean-François Ferry  <jfefe@aternatik.fr>
+ * Copyright (C) 2016       Juanjo Menent        <jmenent@2byte.es>
+ * Copyright (C) 2017       Alexandre Spangaro   <aspangaro@zendsi.com>
+ * Copyright (C) 2018       Andreu Bisquerra	 <jove@bisquerra.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/compta/cashcontrol/report.php
+ *	\ingroup    cashdesk|takepos
+ *	\brief      List of bank transactions
+ */
+
+require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/compta/cashcontrol/class/cashcontrol.class.php';
+require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
+require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
+
+$id = GETPOST('id','int');
+
+$_GET['optioncss']="print";
+include_once 'class/cashcontrol.class.php';
+$cashcontrol= new CashControl($db);
+$cashcontrol->fetch($id);
+
+$limit = GETPOST('limit')?GETPOST('limit','int'):$conf->liste_limit;
+$sortorder='ASC';
+$sortfield='b.datev,b.dateo,b.rowid';
+
+$arrayfields=array(
+    'b.rowid'=>array('label'=>$langs->trans("Ref"), 'checked'=>1),
+    'b.dateo'=>array('label'=>$langs->trans("DateOperationShort"), 'checked'=>1),
+    'b.num_chq'=>array('label'=>$langs->trans("Number"), 'checked'=>1),
+    'ba.ref'=>array('label'=>$langs->trans("BankAccount"), 'checked'=>1),
+    'b.debit'=>array('label'=>$langs->trans("Debit"), 'checked'=>1, 'position'=>600),
+    'b.credit'=>array('label'=>$langs->trans("Credit"), 'checked'=>1, 'position'=>605),
+);
+
+$syear  = $cashcontrol->year_close;
+$smonth = $cashcontrol->month_close;
+$sday   = $cashcontrol->day_close;
+
+$posmodule = $cashcontrol->posmodule;
+$terminalid = $cashcontrol->posnumber;
+
+
+/*
+ * View
+ */
+
+llxHeader('', $langs->trans("CashControl"), '', '', 0, 0, array(), array(), $param);
+
+/*$sql = "SELECT b.rowid, b.dateo as do, b.datev as dv, b.amount, b.label, b.rappro as conciliated, b.num_releve, b.num_chq,";
+$sql.= " b.fk_account, b.fk_type,";
+$sql.= " ba.rowid as bankid, ba.ref as bankref,";
+$sql.= " bu.url_id,";
+$sql.= " f.module_source, f.facnumber as facnumber";
+$sql.= " FROM ";
+//if ($bid) $sql.= MAIN_DB_PREFIX."bank_class as l,";
+$sql.= " ".MAIN_DB_PREFIX."bank_account as ba,";
+$sql.= " ".MAIN_DB_PREFIX."bank as b";
+$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."bank_url as bu ON bu.fk_bank = b.rowid AND type = 'payment'";
+$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as f ON bu.url_id = f.rowid";
+$sql.= " WHERE b.fk_account = ba.rowid";
+// Define filter on invoice
+$sql.= " AND f.module_source = '".$db->escape($cashcontrol->posmodule)."'";
+$sql.= " AND f.pos_source = '".$db->escape($cashcontrol->posnumber)."'";
+$sql.= " AND f.entity IN (".getEntity('facture').")";
+// Define filter on data
+if ($syear && ! $smonth)              $sql.= " AND dateo BETWEEN '".$db->idate(dol_get_first_day($syear, 1))."' AND '".$db->idate(dol_get_last_day($syear, 12))."'";
+elseif ($syear && $smonth && ! $sday) $sql.= " AND dateo BETWEEN '".$db->idate(dol_get_first_day($syear, $smonth))."' AND '".$db->idate(dol_get_last_day($syear, $smonth))."'";
+elseif ($syear && $smonth && $sday)   $sql.= " AND dateo BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $smonth, $sday, $syear))."' AND '".$db->idate(dol_mktime(23, 59, 59, $smonth, $sday, $syear))."'";
+else dol_print_error('', 'Year not defined');
+// Define filter on bank account
+$sql.=" AND (b.fk_account=".$conf->global->CASHDESK_ID_BANKACCOUNT_CASH;
+$sql.=" OR b.fk_account=".$conf->global->CASHDESK_ID_BANKACCOUNT_CB;
+$sql.=" OR b.fk_account=".$conf->global->CASHDESK_ID_BANKACCOUNT_CHEQUE;
+$sql.=")";
+*/
+$sql = "SELECT f.rowid as facid, f.facnumber, f.datef as do, pf.amount as amount, b.fk_account as bankid, cp.code";
+$sql.= " FROM ".MAIN_DB_PREFIX."paiement_facture as pf, ".MAIN_DB_PREFIX."facture as f, ".MAIN_DB_PREFIX."paiement as p, ".MAIN_DB_PREFIX."c_paiement as cp, ".MAIN_DB_PREFIX."bank as b";
+$sql.= " WHERE pf.fk_facture = f.rowid AND p.rowid = pf.fk_paiement AND cp.id = p.fk_paiement AND p.fk_bank = b.rowid";
+$sql.= " AND f.module_source = '".$db->escape($posmodule)."'";
+$sql.= " AND f.pos_source = '".$db->escape($terminalid)."'";
+$sql.= " AND f.paye = 1";
+$sql.= " AND p.entity IN (".getEntity('facture').")";
+/*if ($key == 'cash')       $sql.=" AND cp.code = 'LIQ'";
+elseif ($key == 'cheque') $sql.=" AND cp.code = 'CHQ'";
+elseif ($key == 'card')   $sql.=" AND cp.code = 'CB'";
+else
+{
+	dol_print_error('Value for key = '.$key.' not supported');
+	exit;
+}*/
+if ($syear && ! $smonth)              $sql.= " AND datef BETWEEN '".$db->idate(dol_get_first_day($syear, 1))."' AND '".$db->idate(dol_get_last_day($syear, 12))."'";
+elseif ($syear && $smonth && ! $sday) $sql.= " AND datef BETWEEN '".$db->idate(dol_get_first_day($syear, $smonth))."' AND '".$db->idate(dol_get_last_day($syear, $smonth))."'";
+elseif ($syear && $smonth && $sday)   $sql.= " AND datef BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $smonth, $sday, $syear))."' AND '".$db->idate(dol_mktime(23, 59, 59, $smonth, $sday, $syear))."'";
+else dol_print_error('', 'Year not defined');
+
+$resql = $db->query($sql);
+if ($resql)
+{
+	$num = $db->num_rows($resql);
+	$i = 0;
+
+	print "<center><h2>";
+	if ($cashcontrol->status==2) print $langs->trans("CashControl")." ".$cashcontrol->id;
+	else print $langs->trans("CashControl")." - ".$langs->trans("Draft");
+	print "<br>".$langs->trans("DateCreationShort").": ".dol_print_date($cashcontrol->date_creation, 'dayhour')."</h2></center>";
+
+	$invoicetmp = new Facture($db);
+
+
+	print "<div style='text-align: right'><h2>";
+	print $langs->trans("InitialBankBalance").' - '.$langs->trans("Cash")." : ".price($cashcontrol->opening);
+	print "</h2></div>";
+
+    print '<div class="div-table-responsive">';
+    print '<table class="tagtable liste">'."\n";
+
+	// Fields title
+	print '<tr class="liste_titre">';
+	print_liste_field_titre($arrayfields['b.rowid']['label'],$_SERVER['PHP_SELF'],'b.rowid','',$param,'',$sortfield,$sortorder);
+	print_liste_field_titre($arrayfields['b.dateo']['label'],$_SERVER['PHP_SELF'],'b.dateo','',$param,'align="left"',$sortfield,$sortorder);
+	print_liste_field_titre($arrayfields['ba.ref']['label'],$_SERVER['PHP_SELF'],'ba.ref','',$param,'align="right"',$sortfield,$sortorder);
+	print_liste_field_titre($arrayfields['b.debit']['label'],$_SERVER['PHP_SELF'],'b.amount','',$param,'align="right"',$sortfield,$sortorder);
+	print_liste_field_titre($arrayfields['b.credit']['label'],$_SERVER['PHP_SELF'],'b.amount','',$param,'align="right"',$sortfield,$sortorder);
+	print "</tr>\n";
+
+	$posconciliatecol = 0;
+
+	// Loop on each record
+	$sign = 1;
+	$cash=$bank=$cheque=$other=0;
+
+    $totalarray=array();
+    while ($i < min($num,$limit))
+    {
+        $objp = $db->fetch_object($resql);
+
+        if (empty($cachebankaccount[$objp->bankid]))
+        {
+            $bankaccounttmp = new Account($db);
+            $bankaccounttmp->fetch($objp->bankid);
+            $cachebankaccount[$objp->bankid]=$bankaccounttmp;
+            $bankaccount = $bankaccounttmp;
+        }
+        else
+        {
+            $bankaccount = $cachebankaccount[$objp->bankid];
+        }
+
+		/*if ($first == "yes")
+		{
+			print '<tr class="oddeven">';
+			print '<td>'.$langs->trans("InitialBankBalance").' - '.$langs->trans("Cash").'</td>';
+			print '<td></td><td></td><td></td><td align="right">'.price($cashcontrol->opening).'</td>';
+			print '</tr>';
+			$first = "no";
+		}*/
+
+		print '<tr class="oddeven">';
+
+		// Ref
+        print '<td align="left" class="nowrap">';
+        $invoicetmp->fetch($objp->facid);
+        print $invoicetmp->getNomUrl(1);
+        print '</td>';
+        if (! $i) $totalarray['nbfield']++;
+
+
+        // Date ope
+    	print '<td align="left" class="nowrap">';
+    	print '<span id="dateoperation_'.$objp->rowid.'">'.dol_print_date($db->jdate($objp->do),"day")."</span>";
+    	print "</td>\n";
+        if (! $i) $totalarray['nbfield']++;
+
+    	// Bank account
+        print '<td align="right" class="nowrap">';
+		print $bankaccount->getNomUrl(1);
+		if ($conf->global->CASHDESK_ID_BANKACCOUNT_CASH==$bankaccount->id) $cash+=$objp->amount;
+		elseif ($conf->global->CASHDESK_ID_BANKACCOUNT_CB==$bankaccount->id) $bank+=$objp->amount;
+		elseif ($conf->global->CASHDESK_ID_BANKACCOUNT_CHEQUE==$bankaccount->id) $cheque+=$objp->amount;
+		else $other+=$objp->amount;
+		print "</td>\n";
+        if (! $i) $totalarray['nbfield']++;
+
+    	// Debit
+    	print '<td align="right">';
+    	if ($objp->amount < 0)
+    	{
+    	    print price($objp->amount * -1);
+    	    $totalarray['totaldeb'] += $objp->amount;
+    	}
+    	print "</td>\n";
+    	if (! $i) $totalarray['nbfield']++;
+    	if (! $i) $totalarray['totaldebfield']=$totalarray['nbfield'];
+
+    	// Credit
+    	print '<td align="right">';
+    	if ($objp->amount > 0)
+    	{
+			print price($objp->amount);
+    	    $totalarray['totalcred'] += $objp->amount;
+    	}
+    	print "</td>\n";
+    	if (! $i) $totalarray['nbfield']++;
+    	if (! $i) $totalarray['totalcredfield']=$totalarray['nbfield'];
+
+		print "</tr>";
+
+		$i++;
+	}
+
+	// Show total line
+	if (isset($totalarray['totaldebfield']) || isset($totalarray['totalcredfield']))
+	{
+	    print '<tr class="liste_total">';
+	    $i=0;
+	    while ($i < $totalarray['nbfield'])
+	    {
+	        $i++;
+	        if ($i == 1)
+	        {
+	            if ($num < $limit && empty($offset)) print '<td align="left">'.$langs->trans("Total").'</td>';
+	            else print '<td align="left">'.$langs->trans("Totalforthispage").'</td>';
+	        }
+	        elseif ($totalarray['totaldebfield'] == $i) print '<td align="right">'.price(-1 * $totalarray['totaldeb']).'</td>';
+	        elseif ($totalarray['totalcredfield'] == $i) print '<td align="right">'.price($totalarray['totalcred']).'</td>';
+	        else print '<td></td>';
+	    }
+	    print '</tr>';
+	}
+
+	print "</table>";
+
+	$cash=$cash+$cashcontrol->opening;
+	print "<div style='text-align: right'><h2>";
+	print $langs->trans("Cash").": ".price($cash)."<br><br>";
+	print $langs->trans("PaymentTypeCB").": ".price($bank)."<br><br>";
+	print $langs->trans("PaymentTypeCHQ").": ".price($cheque)."<br><br>";
+	if ($other) print $langs->trans("Other").": ".price($other)."<br><br>";
+	print "</h2></div>";
+
+	//save totals to DB
+	/*
+	$sql = "UPDATE ".MAIN_DB_PREFIX."pos_cash_fence ";
+	$sql .= "SET";
+	$sql .= " cash='".$cash."'";
+    $sql .= ", card='".$bank."'";
+	$sql .= " where rowid=".$id;
+	$db->query($sql);
+	*/
+
+	print "</div>";
+
+    print '</form>';
+
+	$db->free($resql);
+}
+else
+{
+	dol_print_error($db);
+}
+
+llxFooter();
+
+$db->close();

+ 3 - 3
htdocs/compta/facture/card.php

@@ -1699,7 +1699,7 @@ if (empty($reshook))
 			setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
 			$error++;
 		}
-		if ($prod_entry_mode == 'free' && empty($idprod) && (($price_ht < 0 && empty($conf->global->FACTURE_ENABLE_NEGATIVE_LINES)) || $price_ht == '') && $price_ht_devise == '') 	// Unit price can be 0 but not ''
+		if (($prod_entry_mode == 'free' && empty($idprod) && (($price_ht < 0 && empty($conf->global->FACTURE_ENABLE_NEGATIVE_LINES)) || $price_ht == '') && $price_ht_devise == '') && $object->type != Facture::TYPE_CREDIT_NOTE) 	// Unit price can be 0 but not ''
 		{
 			if ($price_ht < 0 && empty($conf->global->FACTURE_ENABLE_NEGATIVE_LINES))
 			{
@@ -2161,7 +2161,7 @@ if (empty($reshook))
 			setEventMessages($langs->trans('ErrorQtyForCustomerInvoiceCantBeNegative'), null, 'errors');
 			$error++;
 		}
-		if (empty($productid) && (($pu_ht < 0 && empty($conf->global->FACTURE_ENABLE_NEGATIVE_LINES)) || $pu_ht == '') && $pu_ht_devise == '') 	// Unit price can be 0 but not ''
+		if ((empty($productid) && (($pu_ht < 0 && empty($conf->global->FACTURE_ENABLE_NEGATIVE_LINES)) || $pu_ht == '') && $pu_ht_devise == '') && $object->type != Facture::TYPE_CREDIT_NOTE) 	// Unit price can be 0 but not ''
 		{
 			if ($pu_ht < 0 && empty($conf->global->FACTURE_ENABLE_NEGATIVE_LINES))
 			{
@@ -3900,7 +3900,7 @@ else if ($id > 0 || ! empty($ref))
 	print '</td><td>';
 	if ($action == 'editmode')
 	{
-		$form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->mode_reglement_id, 'mode_reglement_id', 'CRDT');
+		$form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
 	}
 	else
 	{

+ 1 - 1
htdocs/compta/facture/class/api_invoices.class.php

@@ -454,7 +454,7 @@ class Invoices extends DolibarrApi
              }
         }
 
-        if($this->invoice->update($id, DolibarrApiAccess::$user))
+        if($this->invoice->update(DolibarrApiAccess::$user))
             return $this->get($id);
 
         return false;

+ 1 - 4
htdocs/compta/facture/class/facture.class.php

@@ -951,8 +951,6 @@ class Facture extends CommonInvoice
 
 		$error=0;
 
-		$this->context['createfromclone'] = 'createfromclone';
-
 		$this->db->begin();
 
 		// get extrafields so they will be clone
@@ -962,8 +960,6 @@ class Facture extends CommonInvoice
 		// Load source object
 		$objFrom = clone $this;
 
-
-
 		// Change socid if needed
 		if (! empty($socid) && $socid != $this->socid)
 		{
@@ -1008,6 +1004,7 @@ class Facture extends CommonInvoice
 		}
 
 		// Create clone
+		$this->context['createfromclone'] = 'createfromclone';
 		$result=$this->create($user);
 		if ($result < 0) $error++;
 		else {

+ 3 - 4
htdocs/compta/facture/class/paymentterm.class.php

@@ -178,7 +178,7 @@ class PaymentTerm // extends CommonObject
     	global $langs;
         $sql = "SELECT";
 		$sql.= " t.rowid,";
-		$sql.= " t.entity";
+		$sql.= " t.entity,";
 
 		$sql.= " t.code,";
 		$sql.= " t.sortorder,";
@@ -404,8 +404,6 @@ class PaymentTerm // extends CommonObject
 
 		$object=new PaymentTerm($this->db);
 
-		$object->context['createfromclone'] = 'createfromclone';
-
 		$this->db->begin();
 
 		// Load source object
@@ -417,6 +415,7 @@ class PaymentTerm // extends CommonObject
 		// ...
 
 		// Create clone
+		$object->context['createfromclone'] = 'createfromclone';
 		$result=$object->create($user);
 
 		// Other options
@@ -430,7 +429,7 @@ class PaymentTerm // extends CommonObject
 		//{
 		//}
 
-		unset($this->context['createfromclone']);
+		unset($object->context['createfromclone']);
 
 		// End
 		if (! $error)

+ 2 - 2
htdocs/compta/facture/fiche-rec.php

@@ -1335,11 +1335,11 @@ else
 		print '</td><td>';
 		if ($action == 'editmode')
 		{
-			$form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->mode_reglement_id, 'mode_reglement_id', 'CRDT');
+			$form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->mode_reglement_id, 'mode_reglement_id', 'CRDT', 1, 1);
 		}
 		else
 		{
-			$form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->mode_reglement_id, 'none', 'CRDT');
+			$form->form_modes_reglement($_SERVER['PHP_SELF'].'?facid='.$object->id, $object->mode_reglement_id, 'none');
 		}
 		print '</td></tr>';
 

+ 3 - 3
htdocs/compta/facture/list.php

@@ -50,7 +50,7 @@ require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
 if (! empty($conf->commande->enabled)) require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
 
 // Load translation files required by the page
-$langs->loadLangs(array('bills', 'companies', 'products'));
+$langs->loadLangs(array('bills', 'companies', 'products', 'categories'));
 
 $sall=trim((GETPOST('search_all', 'alphanohtml')!='')?GETPOST('search_all', 'alphanohtml'):GETPOST('sall', 'alphanohtml'));
 $projectid=(GETPOST('projectid')?GETPOST('projectid','int'):0);
@@ -753,7 +753,7 @@ if ($resql)
 		if (! empty($conf->global->MAIN_LIST_FILTER_ON_DAY)) print '<input class="flat valignmiddle" type="text" size="1" maxlength="2" name="search_day_lim" value="'.dol_escape_htmltag($search_day_lim).'">';
 		print '<input class="flat valignmiddle width25" type="text" size="1" maxlength="2" name="search_month_lim" value="'.dol_escape_htmltag($search_month_lim).'">';
 		$formother->select_year($search_year_lim?$search_year_lim:-1,'search_year_lim',1, 20, 5, 0, 0, '', 'widthauto valignmiddle');
-		print '<br><input type="checkbox" name="search_option" value="late"'.($option == 'late'?' checked':'').'> '.$langs->trans("Late");
+		print '<br><input type="checkbox" name="search_option" value="late"'.($option == 'late'?' checked':'').'> '.$langs->trans("Alert");
 		print '</td>';
 	}
 	// Project
@@ -1015,7 +1015,7 @@ if ($resql)
 				print '<td align="center" class="nowrap">'.dol_print_date($datelimit,'day');
 				if ($facturestatic->hasDelay())
 				{
-					print img_warning($langs->trans('Late'));
+				    print img_warning($langs->trans('Alert').' - '.$langs->trans('Late'));
 				}
 				print '</td>';
 				if (! $i) $totalarray['nbfield']++;

+ 2 - 2
htdocs/compta/facture/tpl/linkedobjectblock.tpl.php

@@ -47,8 +47,8 @@ foreach($linkedObjectBlock as $key => $objectlink)
     if ($ilink == count($linkedObjectBlock) && empty($noMoreLinkedObjectBlockAfter) && count($linkedObjectBlock) <= 1) $trclass.=' liste_sub_total';
 ?>
 	<tr class="<?php echo $trclass; ?>" data-element="<?php echo $objectlink->element; ?>"  data-id="<?php echo $objectlink->id; ?>" >
-        <td class="linkedcol-element" ><?php echo $langs->trans("CustomerInvoice"); ?></td>
-        <td class="linkedcol-name" ><?php echo $objectlink->getNomUrl(1); ?></td>
+        <td class="linkedcol-element"><?php echo $langs->trans("CustomerInvoice"); ?></td>
+        <td class="linkedcol-name"><?php echo $objectlink->getNomUrl(1); ?></td>
     	<td class="linkedcol-ref" align="center"><?php echo $objectlink->ref_client; ?></td>
     	<td class="linkedcol-date" align="center"><?php echo dol_print_date($objectlink->date,'day'); ?></td>
     	<td class="linkedcol-amount" align="right"><?php

+ 21 - 11
htdocs/compta/facture/tpl/linkedobjectblockForRec.tpl.php

@@ -47,24 +47,34 @@ foreach($linkedObjectBlock as $key => $objectlink)
     if ($ilink == count($linkedObjectBlock) && empty($noMoreLinkedObjectBlockAfter) && count($linkedObjectBlock) <= 1) $trclass.=' liste_sub_total';
 ?>
 <tr class="<?php echo $trclass; ?>" >
-    <td><?php echo $langs->trans("RepeatableInvoice"); ?></td>
-    <td><?php echo $objectlink->getNomUrl(1); ?></td>
-	<td align="center"></td>
-	<td align="center"><?php echo dol_print_date($objectlink->date_when,'day'); ?></td>
-	<td align="right"><?php
+    <td class="linkedcol-element"><?php echo $langs->trans("RepeatableInvoice"); ?></td>
+    <td class="linkedcol-name"><?php echo $objectlink->getNomUrl(1); ?></td>
+	<td class="linkedcol-ref" align="center"></td>
+	<td class="linkedcol-date" align="center"><?php echo dol_print_date($objectlink->date_when,'day'); ?></td>
+	<td class="linkedcol-amount" align="right"><?php
 		if ($user->rights->facture->lire) {
 			$total = $total + $objectlink->total_ht;
 			echo price($objectlink->total_ht);
 		} ?></td>
-	<td align="right">
-	<?php
-		print $objectlink->getLibStatut(3);
-	?>
-	</td>
-	<td align="right"><a href="<?php echo $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=dellink&dellinkid='.$key; ?>"><?php echo img_picto($langs->transnoentitiesnoconv("RemoveLink"), 'unlink'); ?></a></td>
+	<td class="linkedcol-statut" align="right"><?php echo $objectlink->getLibStatut(3); ?></td>
+	<td class="linkedcol-action" align="right"><a href="<?php echo $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=dellink&dellinkid='.$key; ?>"><?php echo img_picto($langs->transnoentitiesnoconv("RemoveLink"), 'unlink'); ?></a></td>
 </tr>
 <?php
 }
+if (count($linkedObjectBlock) > 1)
+{
+	?>
+    <tr class="liste_total <?php echo (empty($noMoreLinkedObjectBlockAfter)?'liste_sub_total':''); ?>">
+        <td><?php echo $langs->trans("Total"); ?></td>
+        <td></td>
+    	<td align="center"></td>
+    	<td align="center"></td>
+    	<td align="right"><?php echo price($total); ?></td>
+    	<td align="right"></td>
+    	<td align="right"></td>
+    </tr>
+    <?php
+}
 ?>
 
 <!-- END PHP TEMPLATE -->

+ 6 - 4
htdocs/compta/paiement/card.php

@@ -221,7 +221,12 @@ print '</td></tr>';
 
 // Payment type (VIR, LIQ, ...)
 $labeltype=$langs->trans("PaymentType".$object->type_code)!=("PaymentType".$object->type_code)?$langs->trans("PaymentType".$object->type_code):$object->type_libelle;
-print '<tr><td>'.$langs->trans('PaymentMode').'</td><td>'.$labeltype.'</td></tr>';
+print '<tr><td>'.$langs->trans('PaymentMode').'</td><td>'.$labeltype;
+print $object->num_paiement?' - '.$object->num_paiement:'';
+print '</td></tr>';
+
+// Amount
+print '<tr><td>'.$langs->trans('Amount').'</td><td>'.price($object->amount,'',$langs,0,-1,-1,$conf->currency).'</td></tr>';
 
 $disable_delete = 0;
 // Bank account
@@ -302,9 +307,6 @@ print '<tr><td class="tdtop">'.$form->editfieldkey("Comments",'note',$object->no
 print $form->editfieldval("Note",'note',$object->note,$object,$user->rights->facture->paiement,'textarea:'.ROWS_3.':90%');
 print '</td></tr>';
 
-// Amount
-print '<tr><td>'.$langs->trans('Amount').'</td><td>'.price($object->amount,'',$langs,0,-1,-1,$conf->currency).'</td></tr>';
-
 print '</table>';
 
 print '</div>';

+ 40 - 6
htdocs/compta/paiement/class/paiement.class.php

@@ -804,23 +804,55 @@ class Paiement extends CommonObject
     function update_date($date)
     {
         // phpcs:enable
-        if (!empty($date) && $this->statut!=1)
+        $error=0;
+
+        if (!empty($date) && $this->statut != 1)
         {
+            $this->db->begin();
+
+            dol_syslog(get_class($this)."::update_date with date = ".$date, LOG_DEBUG);
+
             $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
             $sql.= " SET datep = '".$this->db->idate($date)."'";
             $sql.= " WHERE rowid = ".$this->id;
 
-            dol_syslog(get_class($this)."::update_date", LOG_DEBUG);
             $result = $this->db->query($sql);
-            if ($result)
+            if (! $result)
+            {
+                $error++;
+                $this->error='Error -1 '.$this->db->error();
+            }
+
+            $type = $this->element;
+
+            $sql = "UPDATE ".MAIN_DB_PREFIX.'bank';
+            $sql.= " SET dateo = '".$this->db->idate($date)."', datev = '".$this->db->idate($date)."'";
+            $sql.= " WHERE rowid IN (SELECT fk_bank FROM ".MAIN_DB_PREFIX."bank_url WHERE type = '".$type."' AND url_id = ".$this->id.")";
+            $sql.= " AND rappro = 0";
+
+            $result = $this->db->query($sql);
+            if (! $result)
             {
-            	$this->datepaye = $date;
+                $error++;
+                $this->error='Error -1 '.$this->db->error();
+            }
+
+            if (! $error)
+            {
+
+            }
+
+            if (! $error)
+            {
+                $this->datepaye = $date;
                 $this->date = $date;
+
+                $this->db->commit();
                 return 0;
             }
             else
             {
-                $this->error='Error -1 '.$this->db->error();
+                $this->db->rollback();
                 return -2;
             }
         }
@@ -1139,7 +1171,9 @@ class Paiement extends CommonObject
 		if (! empty($conf->dol_no_mouse_hover)) $notooltip=1;   // Force disable tooltips
 
 		$result='';
-        $label = $langs->trans("ShowPayment").': '.$this->ref;
+        $label = '<u>'.$langs->trans("ShowPayment").'</u><br>';
+        $label.= '<strong>'.$langs->trans("Ref").':</strong> '.$this->ref;
+        $label.= '<br><strong>'.$langs->trans("Date").':</strong> '.dol_print_date($this->date, 'dayhour');
         if ($mode == 'withlistofinvoices')
         {
             $arraybill = $this->getBillsArray();

+ 1 - 0
htdocs/compta/prelevement/class/bonprelevement.class.php

@@ -1591,6 +1591,7 @@ class BonPrelevement extends CommonObject
 	function EnregDestinataireSEPA($row_code_client, $row_nom, $row_address, $row_zip, $row_town, $row_country_code, $row_cb, $row_cg, $row_cc, $row_somme, $row_facnumber, $row_idfac, $row_iban, $row_bic, $row_datec, $row_drum)
 	{
         // phpcs:enable
+        global $conf;
 		$CrLf = "\n";
 		$Rowing = sprintf("%06d", $row_idfac);
 

+ 4 - 1
htdocs/compta/sociales/class/cchargesociales.class.php

@@ -369,6 +369,7 @@ class Cchargesociales
 		// ...
 
 		// Create clone
+		$this->context['createfromclone'] = 'createfromclone';
 		$result = $object->create($user);
 
 		// Other options
@@ -378,6 +379,8 @@ class Cchargesociales
 			dol_syslog(__METHOD__ . ' ' . join(',', $this->errors), LOG_ERR);
 		}
 
+		unset($this->context['createfromclone']);
+
 		// End
 		if (!$error) {
 			$this->db->commit();
@@ -386,7 +389,7 @@ class Cchargesociales
 		} else {
 			$this->db->rollback();
 
-			return - 1;
+			return -1;
 		}
 	}
 

+ 2 - 3
htdocs/compta/sociales/class/paymentsocialcontribution.class.php

@@ -459,8 +459,6 @@ class PaymentSocialContribution extends CommonObject
 
 		$object=new PaymentSocialContribution($this->db);
 
-		$object->context['createfromclone'] = 'createfromclone';
-
 		$this->db->begin();
 
 		// Load source object
@@ -472,6 +470,7 @@ class PaymentSocialContribution extends CommonObject
 		// ...
 
 		// Create clone
+		$object->context['createfromclone'] = 'createfromclone';
 		$result=$object->create($user);
 
 		// Other options
@@ -488,7 +487,7 @@ class PaymentSocialContribution extends CommonObject
 
 		}
 
-		unset($this->context['createfromclone']);
+		unset($object->context['createfromclone']);
 
 		// End
 		if (! $error)

+ 1 - 1
htdocs/compta/sociales/list.php

@@ -19,7 +19,7 @@
  */
 
 /**
- *   	\file       htdocs/compta/list/index.php
+ *   	\file       htdocs/compta/sociales/list.php
  *		\ingroup    tax
  *		\brief      Page to list all social contributions
  */

+ 3 - 3
htdocs/compta/tva/index.php

@@ -257,7 +257,8 @@ $mend = $tmp['mon'];
 //var_dump($m);
 $total=0; $subtotalcoll=0; $subtotalpaye=0; $subtotal=0;
 $i=0; $mcursor=0;
-while ((($y < $yend) || ($y == $yend && $m < $mend)) && $mcursor < 1000)	// $mcursor is to avoid too large loop
+
+while ((($y < $yend) || ($y == $yend && $m <= $mend)) && $mcursor < 1000)	// $mcursor is to avoid too large loop
 {
 	//$m = $conf->global->SOCIETE_FISCAL_MONTH_START + ($mcursor % 12);
 	if ($m == 13) $y++;
@@ -562,12 +563,11 @@ if (! empty($conf->global->MAIN_FEATURES_LEVEL))
 
     print load_fiche_titre($langs->trans("VATBalance"), '', ''); // need to add translation
 
-    $sql1 = "SELECT SUM(amount) as mm, date_format(f.datev,'%Y') as dm";
+    $sql1 = "SELECT SUM(amount) as mm";
     $sql1 .= " FROM " . MAIN_DB_PREFIX . "tva as f";
     $sql1 .= " WHERE f.entity = " . $conf->entity;
     $sql1 .= " AND f.datev >= '" . $db->idate($date_start) . "'";
     $sql1 .= " AND f.datev <= '" . $db->idate($date_end) . "'";
-    $sql1 .= " GROUP BY dm ORDER BY dm ASC";
 
     $result = $db->query($sql1);
     if ($result) {

+ 1 - 1
htdocs/compta/tva/list.php

@@ -166,7 +166,7 @@ if ($result)
 	print_barre_liste($langs->trans("VATPayments"),$page,$_SERVER["PHP_SELF"],$param,$sortfield,$sortorder,'',$num,$totalnboflines, 'title_accountancy', 0, $newcardbutton, '', $limit);
 
 	print '<div class="div-table-responsive">';
-	print '<table class="noborder" width="100%">';
+	print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
 
 	print '<tr class="liste_titre_filter">';
 	print '<td class="liste_titre"><input type="text" class="flat" size="4" name="search_ref" value="'.dol_escape_htmltag($search_ref).'"></td>';

+ 14 - 19
htdocs/contact/card.php

@@ -9,6 +9,7 @@
  * Copyright (C) 2014       Juanjo Menent           <jmenent@2byte.es>
  * Copyright (C) 2015       Jean-François Ferry     <jfefe@aternatik.fr>
  * Copyright (C) 2018       Frédéric France         <frederic.france@netlogic.fr>
+ * Copyright (C) 2019       Josep Lluís Amador      <joseplluis@lliuretic.cat>
  *
  * 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
@@ -40,9 +41,9 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
-require_once DOL_DOCUMENT_ROOT. '/core/class/html.form.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
 require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
-require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
+require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
 
 // Load translation files required by the page
 $langs->loadLangs(array('companies', 'users', 'other', 'commercial'));
@@ -376,28 +377,22 @@ if (empty($reshook))
 			$ret = $extrafields->setOptionalsFromPost($extralabels,$object);
 			if ($ret < 0) $error++;
 
-            $result = $object->update($contactid, $user);
+			$result = $object->update($contactid, $user);
 
 			if ($result > 0) {
 				// Categories association
-				// First we delete all categories association
-				$sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'categorie_contact';
-				$sql .= ' WHERE fk_socpeople = ' . $object->id;
-				$db->query( $sql );
-
-				// Then we add the associated categories
-				$categories = GETPOST( 'contcats', 'array');
+				$categories = GETPOST('contcats', 'array');
 				$object->setCategories($categories);
 
-                $object->old_lastname='';
-                $object->old_firstname='';
-                $action = 'view';
-            }
-            else
-            {
-                setEventMessages($object->error, $object->errors, 'errors');
-                $action = 'edit';
-            }
+				$object->old_lastname='';
+				$object->old_firstname='';
+				$action = 'view';
+			}
+			else
+			{
+				setEventMessages($object->error, $object->errors, 'errors');
+				$action = 'edit';
+			}
         }
 
         if (! $error && empty($errors))

+ 1 - 1
htdocs/contact/list.php

@@ -36,7 +36,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
 
 // Load translation files required by the page
-$langs->loadLangs(array("companies", "suppliers"));
+$langs->loadLangs(array("companies", "suppliers", "categories"));
 
 $action=GETPOST('action','alpha');
 $massaction=GETPOST('massaction','alpha');

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

@@ -2388,8 +2388,6 @@ class Contrat extends CommonObject
 
 		dol_include_once('/projet/class/project.class.php');
 
-		$this->context['createfromclone'] = 'createfromclone';
-
 		$error = 0;
 
 		$this->fetch($this->id);
@@ -2440,6 +2438,7 @@ class Contrat extends CommonObject
 		}
 
 		// Create clone
+		$clonedObj->context['createfromclone'] = 'createfromclone';
 		$result = $clonedObj->create($user);
 		if ($result < 0) {
 			$error ++;
@@ -2479,7 +2478,7 @@ class Contrat extends CommonObject
 			}
 		}
 
-		unset($this->context['createfromclone']);
+		unset($clonedObj->context['createfromclone']);
 
 		// End
 		if (! $error) {

+ 8 - 1
htdocs/core/actions_addupdatedelete.inc.php

@@ -149,6 +149,7 @@ if ($action == 'update' && ! empty($permissiontoadd))
 if ($action == "update_extras" && ! empty($permissiontoadd))
 {
 	$object->fetch(GETPOST('id','int'));
+
 	$attributekey = GETPOST('attribute','alpha');
 	$attributekeylong = 'options_'.$attributekey;
 	$object->array_options['options_'.$attributekey] = GETPOST($attributekeylong,' alpha');
@@ -169,6 +170,12 @@ if ($action == "update_extras" && ! empty($permissiontoadd))
 // Action to delete
 if ($action == 'confirm_delete' && ! empty($permissiontodelete))
 {
+    if (! ($object->id > 0))
+    {
+        dol_print_error('', 'Error, object must be fetched before being deleted');
+        exit;
+    }
+
 	$result=$object->delete($user);
 	if ($result > 0)
 	{
@@ -195,7 +202,7 @@ if ($action == 'confirm_clone' && $confirm == 'yes' && ! empty($permissiontoadd)
 	{
 		if ($object->id > 0)
 		{
-			// Because createFromClone modifies the object, we must clone it so that we can restore it later
+			// Because createFromClone modifies the object, we must clone it so that we can restore it later if error
 			$orig = clone $object;
 
 			$result=$object->createFromClone($user, $object->id);

+ 5 - 2
htdocs/core/actions_linkedfiles.inc.php

@@ -217,11 +217,14 @@ elseif ($action == 'renamefile' && GETPOST('renamefilesave','alpha'))
 			            {
 			            	// Define if we have to generate thumbs or not
 			            	$generatethumbs = 1;
-			            	if (GETPOST('section_dir')) $generatethumbs=0;
+			            	// When we rename a file from the file manager in ecm, we must not regenerate thumbs (not a problem, we do pass here)
+			            	// When we rename a file from the website module, we must not regenerate thumbs (module = medias in such a case)
+			            	// but when we rename from a tab "Documents", we must regenerate thumbs
+			            	if (GETPOST('modulepart') == 'medias') $generatethumbs=0;
 
 			            	if ($generatethumbs)
 			            	{
-				            	if ($object->id)
+			            		if ($object->id)
 				            	{
 				                	$object->addThumbs($destpath);
 				            	}

+ 21 - 7
htdocs/core/actions_massactions.inc.php

@@ -2,6 +2,7 @@
 /* Copyright (C) 2015-2017 Laurent Destailleur  <eldy@users.sourceforge.net>
  * Copyright (C) 2018	   Nicolas ZABOURI	<info@inovea-conseil.com>
  * Copyright (C) 2018 	   Juanjo Menent  <jmenent@2byte.es>
+ * Copyright (C) 2019 	   Ferran Marcet  <fmarcet@2byte.es>
  *
  * 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
@@ -423,6 +424,8 @@ if (! $error && $massaction == 'confirm_presend')
 							// Insert logs into agenda
 							foreach($listofqualifiedobj as $objid => $objectobj)
 							{
+								dol_syslog("Try to insert email event into agenda for objid=".$objid." => objectobj=".get_class($objectobj));
+
 								/*if ($objectclass == 'Propale') $actiontypecode='AC_PROP';
 	                            if ($objectclass == 'Commande') $actiontypecode='AC_COM';
 	                            if ($objectclass == 'Facture') $actiontypecode='AC_FAC';
@@ -448,14 +451,14 @@ if (! $error && $massaction == 'confirm_presend')
 								$objectobj->elementtype	= $objectobj->element;
 
 								$triggername = strtoupper(get_class($objectobj)) .'_SENTBYMAIL';
-								if ($triggername == 'SOCIETE_SENTBYMAIL')    $triggername = 'COMPANY_SENTBYEMAIL';
-								if ($triggername == 'CONTRAT_SENTBYMAIL')    $triggername = 'CONTRACT_SENTBYEMAIL';
-								if ($triggername == 'COMMANDE_SENTBYMAIL')   $triggername = 'ORDER_SENTBYEMAIL';
+								if ($triggername == 'SOCIETE_SENTBYMAIL')    $triggername = 'COMPANY_SENTBYMAIL';
+								if ($triggername == 'CONTRAT_SENTBYMAIL')    $triggername = 'CONTRACT_SENTBYMAIL';
+								if ($triggername == 'COMMANDE_SENTBYMAIL')   $triggername = 'ORDER_SENTBYMAIL';
 								if ($triggername == 'FACTURE_SENTBYMAIL')    $triggername = 'BILL_SENTBYMAIL';
-								if ($triggername == 'EXPEDITION_SENTBYMAIL') $triggername = 'SHIPPING_SENTBYEMAIL';
+								if ($triggername == 'EXPEDITION_SENTBYMAIL') $triggername = 'SHIPPING_SENTBYMAIL';
 								if ($triggername == 'COMMANDEFOURNISSEUR_SENTBYMAIL') $triggername = 'ORDER_SUPPLIER_SENTBYMAIL';
-								if ($triggername == 'FACTUREFOURNISSEUR_SENTBYMAIL') $triggername = 'BILL_SUPPLIER_SENTBYEMAIL';
-								if ($triggername == 'SUPPLIERPROPOSAL_SENTBYMAIL') $triggername = 'PROPOSAL_SUPPLIER_SENTBYEMAIL';
+								if ($triggername == 'FACTUREFOURNISSEUR_SENTBYMAIL') $triggername = 'BILL_SUPPLIER_SENTBYMAIL';
+								if ($triggername == 'SUPPLIERPROPOSAL_SENTBYMAIL') $triggername = 'PROPOSAL_SUPPLIER_SENTBYMAIL';
 
 								if (! empty($triggername))
 								{
@@ -539,7 +542,9 @@ if ($massaction == 'confirm_createbills')
 		$objecttmp = new Facture($db);
 		if (!empty($createbills_onebythird) && !empty($TFactThird[$cmd->socid])) $objecttmp = $TFactThird[$cmd->socid]; // If option "one bill per third" is set, we use already created order.
 		else {
-
+			// Load extrafields of order
+			$cmd->fetch_optionals();
+			
 			$objecttmp->socid = $cmd->socid;
 			$objecttmp->type = Facture::TYPE_STANDARD;
 			$objecttmp->cond_reglement_id	= $cmd->cond_reglement_id;
@@ -556,6 +561,8 @@ if ($massaction == 'confirm_createbills')
 			$objecttmp->origin    = 'commande';
 			$objecttmp->origin_id = $id_order;
 
+			$objecttmp->array_options = $cmd->array_options;	// Copy extrafields
+
 			$res = $objecttmp->create($user);
 
 			if($res > 0) $nb_bills_created++;
@@ -705,6 +712,7 @@ if ($massaction == 'confirm_createbills')
 	if (! $error && $validate_invoices)
 	{
 		$massaction = $action = 'builddoc';
+
 		foreach($TAllFact as &$objecttmp)
 		{
 			$result = $objecttmp->validate($user);
@@ -716,12 +724,18 @@ if ($massaction == 'confirm_createbills')
 			}
 
 			$id = $objecttmp->id; // For builddoc action
+			$object = $objecttmp;
 
 			// Builddoc
 			$donotredirect = 1;
 			$upload_dir = $conf->facture->dir_output;
 			$permissioncreate=$user->rights->facture->creer;
+
+			// Call action to build doc
+			$savobject = $object;
+      			$object = $objecttmp;
 			include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
+			$object = $savobject;
 		}
 
 		$massaction = $action = 'confirm_createbills';

+ 2 - 1
htdocs/core/actions_sendmails.inc.php

@@ -1,6 +1,7 @@
 <?php
 /* Copyright (C) 2013 Laurent Destailleur  <eldy@users.sourceforge.net>
- *
+*  Copyright (C) 2013 Juanjo Menent		   <jmenent@2byte.es>
+*
 * 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

+ 3 - 3
htdocs/core/boxes/box_produits.php

@@ -42,7 +42,7 @@ class box_produits extends ModeleBoxes
      * @var DoliDB Database handler.
      */
     public $db;
-    
+
 	var $param;
 
 	var $info_box_head = array();
@@ -135,13 +135,13 @@ class box_produits extends ModeleBoxes
 					$productstatic->entity = $objp->entity;
 
 					$this->info_box_contents[$line][] = array(
-                        'td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"',
+                        'td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"',
                         'text' => $productstatic->getNomUrl(1),
                         'asis' => 1,
                     );
 
                     $this->info_box_contents[$line][] = array(
-                        'td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"',
+                        'td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"',
                         'text' => $objp->label,
                     );
 

+ 2 - 2
htdocs/core/boxes/box_services_contracts.php

@@ -190,7 +190,7 @@ class box_services_contracts extends ModeleBoxes
 					}
 
 
-					$this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"',
+					$this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"',
                     'text' => $s,
 					'asis' => 1
                     );
@@ -200,7 +200,7 @@ class box_services_contracts extends ModeleBoxes
 					'asis' => 1
                     );
 
-					$this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax150 maxwidth150onsmartphone"',
+					$this->info_box_contents[$i][] = array('td' => 'class="tdoverflowmax100 maxwidth100onsmartphone"',
                     'text' => $thirdpartytmp->getNomUrl(1),
 					'asis' => 1
                     );

+ 4 - 5
htdocs/core/class/commonobject.class.php

@@ -1777,7 +1777,7 @@ abstract class CommonObject
 			if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
 
 			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
-			$sql .= ' SET '.$fieldname.' = '.$id;
+			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
 			$sql .= ' WHERE rowid='.$this->id;
 
 			if ($this->db->query($sql))
@@ -1962,7 +1962,7 @@ abstract class CommonObject
 			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
 
 			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
-			$sql .= ' SET '.$fieldname.' = '.$id;
+			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
 			$sql .= ' WHERE rowid='.$this->id;
 
 			if ($this->db->query($sql))
@@ -4940,6 +4940,7 @@ abstract class CommonObject
 			   		if ($this->array_options[$key] === '') $mandatorypb=true;
 			   		if ($mandatorypb)
 			   		{
+			   			dol_syslog($this->error);
 			   			$this->errors[]=$langs->trans('ErrorFieldRequired', $attributeLabel);
 			   			return -1;
 			   		}
@@ -5023,8 +5024,6 @@ abstract class CommonObject
 						$new_array_options[$key] = price2num($this->array_options[$key]);
 						break;
 					case 'date':
-						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
-						break;
 					case 'datetime':
 						// If data is a string instead of a timestamp, we convert it
 						if (! is_int($this->array_options[$key])) {
@@ -6270,7 +6269,7 @@ abstract class CommonObject
 			$e = 0;
 			foreach($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
 			{
-				//Show only the key field in params
+				// Show only the key field in params
 				if (is_array($params) && array_key_exists('onlykey',$params) && $key != $params['onlykey']) continue;
 
 				$enabled = 1;

+ 4 - 1
htdocs/core/class/ctyperesource.class.php

@@ -427,6 +427,7 @@ class Ctyperesource
 		// ...
 
 		// Create clone
+		$object->context['createfromclone'] = 'createfromclone';
 		$result = $object->create($user);
 
 		// Other options
@@ -436,6 +437,8 @@ class Ctyperesource
 			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
 		}
 
+		unset($object->context['createfromclone']);
+
 		// End
 		if (!$error) {
 			$this->db->commit();
@@ -444,7 +447,7 @@ class Ctyperesource
 		} else {
 			$this->db->rollback();
 
-			return - 1;
+			return -1;
 		}
 	}
 

+ 2 - 0
htdocs/core/class/emailsenderprofile.class.php

@@ -207,6 +207,8 @@ class EmailSenderProfile extends CommonObject
 			$this->errors = $object->errors;
 		}
 
+		unset($object->context['createfromclone']);
+
 		// End
 		if (!$error) {
 			$this->db->commit();

+ 2 - 2
htdocs/core/class/events.class.php

@@ -76,9 +76,9 @@ class Events // extends CommonObject
 
 	// List of all Audit/Security events supported by triggers
 	public $eventstolog=array(
-		/*array('id'=>'USER_LOGIN',             'test'=>1),
+		array('id'=>'USER_LOGIN',             'test'=>1),
 		array('id'=>'USER_LOGIN_FAILED',      'test'=>1),
-	    array('id'=>'USER_LOGOUT',            'test'=>1),*/
+	    array('id'=>'USER_LOGOUT',            'test'=>1),
 		array('id'=>'USER_CREATE',            'test'=>1),
 		array('id'=>'USER_MODIFY',            'test'=>1),
 		array('id'=>'USER_NEW_PASSWORD',      'test'=>1),

+ 40 - 24
htdocs/core/class/html.form.class.php

@@ -450,7 +450,7 @@ class Form
 		if ($notabs == 2) $tag='div';
 		if ($notabs == 3) $tag='span';
 		// Sanitize tooltip
-		$htmltext=str_replace("\\","\\\\",$htmltext);
+		//$htmltext=str_replace("\\","\\\\",$htmltext);
 		$htmltext=str_replace("\r","",$htmltext);
 		$htmltext=str_replace("\n","",$htmltext);
 
@@ -516,7 +516,7 @@ class Form
 	 *	@param	string	$text				Text to show
 	 *	@param  string	$htmltext	     	Content of tooltip
 	 *	@param	int		$direction			1=Icon is after text, -1=Icon is before text, 0=no icon
-	 * 	@param	string	$type				Type of picto ('info', 'help', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath
+	 * 	@param	string	$type				Type of picto ('info', 'help', 'warning', 'superadmin', 'mypicto@mymodule', ...) or image filepath or 'none'
 	 *  @param  string	$extracss           Add a CSS style to td, div or span tag
 	 *  @param  int		$noencodehtmltext   Do not encode into html entity the htmltext
 	 *  @param	int		$notabs				0=Include table and tr tags, 1=Do not include table and tr tags, 2=use div, 3=use span
@@ -557,12 +557,13 @@ class Form
 			if ($type == 'info' || $type == 'help') return $text;
 		}
 
+		$img='';
 		if ($type == 'info') $img = img_help(0, $alt);
 		elseif ($type == 'help') $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
 		elseif ($type == 'superadmin') $img = img_picto($alt, 'redstar');
 		elseif ($type == 'admin') $img = img_picto($alt, 'star');
 		elseif ($type == 'warning') $img = img_warning($alt);
-		else $img = img_picto($alt, $type);
+		elseif ($type != 'none') $img = img_picto($alt, $type);   // $type can be an image path
 
 		return $this->textwithtooltip($text, $htmltext, (($tooltiptrigger && ! $img)?3:2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
 	}
@@ -1141,8 +1142,8 @@ class Form
 		$sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
 
 		if ($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST) {
-			$sql .= " ,s.address, s.zip, s.town";
-		 	$sql .= " , dictp.code as country_code";
+			$sql .= ", s.address, s.zip, s.town";
+		 	$sql .= ", dictp.code as country_code";
 		}
 
 		$sql.= " FROM (".MAIN_DB_PREFIX ."societe as s";
@@ -1441,9 +1442,10 @@ class Form
 				$out .= ajax_combobox($htmlid, $events, $conf->global->CONTACT_USE_SEARCH_TO_SELECT);
 			}
 
-			if ($htmlname != 'none' || $options_only) $out.= '<select class="flat'.($moreclass?' '.$moreclass:'').'" id="'.$htmlid.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
+			if ($htmlname != 'none' && ! $options_only) $out.= '<select class="flat'.($moreclass?' '.$moreclass:'').'" id="'.$htmlid.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
 			if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) $out.= '<option value="0"'.(in_array(0,$selected)?' selected':'').'>&nbsp;</option>';
 			if ($showempty == 2) $out.= '<option value="0"'.(in_array(0,$selected)?' selected':'').'>'.$langs->trans("Internal").'</option>';
+
 			$num = $this->db->num_rows($resql);
 			$i = 0;
 			if ($num)
@@ -1504,7 +1506,7 @@ class Form
 				$out.= ($socid != -1) ? ($langs->trans($socid?"NoContactDefinedForThirdParty":"NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
 				$out.= '</option>';
 			}
-			if ($htmlname != 'none' || $options_only)
+			if ($htmlname != 'none' && ! $options_only)
 			{
 				$out.= '</select>';
 			}
@@ -2491,9 +2493,10 @@ class Form
 	 *	@param	array	$ajaxoptions	Options for ajax_autocompleter
 	 *  @param	int		$hidelabel		Hide label (0=no, 1=yes)
 	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
+	 *  @param	string	$morecss		More CSS
 	 *	@return	void
 	 */
-	function select_produits_fournisseurs($socid, $selected='', $htmlname='productid', $filtertype='', $filtre='', $ajaxoptions=array(), $hidelabel=0, $alsoproductwithnosupplierprice=0)
+	function select_produits_fournisseurs($socid, $selected='', $htmlname='productid', $filtertype='', $filtre='', $ajaxoptions=array(), $hidelabel=0, $alsoproductwithnosupplierprice=0, $morecss='')
 	{
         // phpcs:enable
 		global $langs,$conf;
@@ -2518,7 +2521,7 @@ class Form
 		}
 		else
 		{
-			print $this->select_produits_fournisseurs_list($socid,$selected,$htmlname,$filtertype,$filtre,'',-1,0,0,$alsoproductwithnosupplierprice);
+			print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', -1, 0, 0, $alsoproductwithnosupplierprice, $morecss);
 		}
 	}
 
@@ -2536,9 +2539,10 @@ class Form
 	 *  @param  int		$outputmode     0=HTML select string, 1=Array
 	 *  @param  int     $limit          Limit of line number
 	 *  @param  int     $alsoproductwithnosupplierprice    1=Add also product without supplier prices
+	 *  @param	string	$morecss		Add more CSS
 	 *  @return array           		Array of keys for json
 	 */
-	function select_produits_fournisseurs_list($socid,$selected='',$htmlname='productid',$filtertype='',$filtre='',$filterkey='',$statut=-1,$outputmode=0,$limit=100,$alsoproductwithnosupplierprice=0)
+	function select_produits_fournisseurs_list($socid,$selected='',$htmlname='productid',$filtertype='',$filtre='',$filterkey='',$statut=-1,$outputmode=0,$limit=100,$alsoproductwithnosupplierprice=0,$morecss='')
 	{
         // phpcs:enable
 		global $langs,$conf,$db;
@@ -2593,7 +2597,7 @@ class Form
 			$num = $this->db->num_rows($result);
 
 			//$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">';	// remove select to have id same with combo and ajax
-			$out.='<select class="flat maxwidthonsmartphone" id="'.$htmlname.'" name="'.$htmlname.'">';
+			$out.='<select class="flat maxwidthonsmartphone'.($morecss?' '.$morecss:'').'" id="'.$htmlname.'" name="'.$htmlname.'">';
 			if (! $selected) $out.='<option value="0" selected>&nbsp;</option>';
 			else $out.='<option value="0">&nbsp;</option>';
 
@@ -4293,9 +4297,10 @@ class Form
 	 *    @param    string	$htmlname    	Name of select html field
 	 *    @param  	string	$filtertype		To filter on field type in llx_c_paiement (array('code'=>xx,'label'=>zz))
 	 *    @param    int     $active         Active or not, -1 = all
+	 *    @param   int     $addempty       1=Add empty entry
 	 *    @return	void
 	 */
-	function form_modes_reglement($page, $selected='', $htmlname='mode_reglement_id', $filtertype='', $active=1)
+	function form_modes_reglement($page, $selected='', $htmlname='mode_reglement_id', $filtertype='', $active=1, $addempty=0)
 	{
         // phpcs:enable
 		global $langs;
@@ -4304,7 +4309,7 @@ class Form
 			print '<form method="POST" action="'.$page.'">';
 			print '<input type="hidden" name="action" value="setmode">';
 			print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
-			$this->select_types_paiements($selected,$htmlname,$filtertype,0,0,0,0,$active);
+			$this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active);
 			print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
 			print '</form>';
 		}
@@ -5671,7 +5676,7 @@ class Form
 	 *  @param  int             $disablebademail	Check if an email is found into value and if not disable and colorize entry
 	 *  @param  int             $nohtmlescape		No html escaping.
 	 * 	@return	string								HTML select string.
-	 *  @see multiselectarray
+	 *  @see multiselectarray, selectArrayAjax, selectArrayFilter
 	 */
 	static function selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='', $addjscombo=0, $moreparamonempty='',$disablebademail=0, $nohtmlescape=0)
 	{
@@ -6001,14 +6006,18 @@ class Form
 	 *  @param	string	$placeholder	String to use as placeholder
 	 *  @param	int		$addjscombo		Add js combo
 	 *	@return	string					HTML multiselect string
-	 *  @see selectarray
+	 *  @see selectarray, selectArrayAjax, selectArrayFilter
 	 */
-	static function multiselectarray($htmlname, $array, $selected=array(), $key_in_label=0, $value_as_key=0, $morecss='', $translate=0, $width=0, $moreattrib='', $elemtype='', $placeholder='', $addjscombo=1)
+	static function multiselectarray($htmlname, $array, $selected=array(), $key_in_label=0, $value_as_key=0, $morecss='', $translate=0, $width=0, $moreattrib='', $elemtype='', $placeholder='', $addjscombo=-1)
 	{
 		global $conf, $langs;
 
 		$out = '';
 
+		if ($addjscombo < 0) {
+		    if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) $addjscombo = 1;
+		    else $addjscombo = 0;
+		}
 
 		// Add code for jquery to use multiselect
 		if (! empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT'))
@@ -6254,7 +6263,7 @@ class Form
 		$hookmanager->initHooks(array('commonobject'));
 		$parameters=array(
 			'morehtmlright' => $morehtmlright,
-		    'compatibleImportElementsList' =>& $compatibleImportElementsList,
+		    'compatibleImportElementsList' => &$compatibleImportElementsList,
 		);
 		$reshook=$hookmanager->executeHooks('showLinkedObjectBlock',$parameters,$object,$action);    // Note that $action and $object may have been modified by hook
 
@@ -6431,6 +6440,7 @@ class Form
 		$hookmanager->initHooks(array('commonobject'));
 		$parameters=array('listofidcompanytoscan' => $listofidcompanytoscan);
 		$reshook=$hookmanager->executeHooks('showLinkToObjectBlock',$parameters,$object,$action);    // Note that $action and $object may have been modified by hook
+
 		if (empty($reshook))
 		{
 			if (is_array($hookmanager->resArray) && count($hookmanager->resArray))
@@ -6454,7 +6464,7 @@ class Form
 
 			if (! empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || ! in_array($key, $excludelinksto)))
 			{
-				print '<div id="'.$key.'list"'.(empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)?' style="display:none"':'').'>';
+				print '<div id="'.$key.'list"'.(empty($conf->use_javascript_ajax)?'':' style="display:none"').'>';
 				$sql = $possiblelink['sql'];
 
 				$resqllist = $this->db->query($sql);
@@ -6463,9 +6473,11 @@ class Form
 					$num = $this->db->num_rows($resqllist);
 					$i = 0;
 
-					print '<br><form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formlinked'.$key.'">';
-					print '<input type="hidden" name="id" value="'.$object->id.'">';
+					print '<br>';
+					print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formlinked'.$key.'">';
 					print '<input type="hidden" name="action" value="addlink">';
+					print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+					print '<input type="hidden" name="id" value="'.$object->id.'">';
 					print '<input type="hidden" name="addlink" value="'.$key.'">';
 					print '<table class="noborder">';
 					print '<tr class="liste_titre">';
@@ -6514,8 +6526,9 @@ class Form
 		{
 			$linktoelem='
     		<dl class="dropdown" id="linktoobjectname">
-    		<dt><a href="#linktoobjectname">'.$langs->trans("LinkTo").'...</a></dt>
-    		<dd>
+    		';
+			if (! empty($conf->use_javascript_ajax)) $linktoelem.='<dt><a href="#linktoobjectname">'.$langs->trans("LinkTo").'...</a></dt>';
+			$linktoelem.='<dd>
     		<div class="multiselectlinkto">
     		<ul class="ulselectedfields">'.$linktoelemlist.'
     		</ul>
@@ -6528,7 +6541,9 @@ class Form
 			$linktoelem='';
 		}
 
-		print '<!-- Add js to show linkto box -->
+		if (! empty($conf->use_javascript_ajax))
+		{
+		  print '<!-- Add js to show linkto box -->
 				<script type="text/javascript" language="javascript">
 				jQuery(document).ready(function() {
 					jQuery(".linkto").click(function() {
@@ -6538,7 +6553,8 @@ class Form
 					});
 				});
 				</script>
-		';
+		  ';
+		}
 
 		return $linktoelem;
 	}

+ 1 - 1
htdocs/core/class/html.formactions.class.php

@@ -243,7 +243,7 @@ class FormActions
 
 	        		print '<tr class="oddeven">';
 	        		// Ref
-					print '<td>'.$ref.'</td>';
+					print '<td class="nowraponall">'.$ref.'</td>';
 					// Onwer
 	        		print '<td>';
 	        		if (! empty($action->userownerid))

+ 2 - 2
htdocs/core/class/html.formcompany.class.php

@@ -622,8 +622,8 @@ class FormCompany
 								runJsCodeForEvent'.$htmlname.'(values);
 							}
 						});
-						/* Clean contact */
-						$("div#s2id_contactid>a>span").html(\'\');
+
+						$(this).trigger("blur");
 					});
 
 					// Function used to execute events when search_htmlname change

+ 4 - 2
htdocs/core/class/html.formfile.class.php

@@ -307,8 +307,10 @@ class FormFile
 			return $this->getDocumentsLink($modulepart, $modulesubdir, $filedir);
 		}
 
-		// Add entity in $param
-		$param.= 'entity='.(!empty($object->entity)?$object->entity:$conf->entity);
+		// Add entity in $param if not already exists
+		if (!preg_match('/entity\=[0-9]+/', $param)) {
+			$param.= 'entity='.(!empty($object->entity)?$object->entity:$conf->entity);
+		}
 
 		$printer=0;
 		if (in_array($modulepart,array('facture','supplier_proposal','propal','proposal','order','commande','expedition', 'commande_fournisseur', 'expensereport','livraison')))	// The direct print feature is implemented only for such elements

+ 7 - 7
htdocs/core/class/html.formprojet.class.php

@@ -334,7 +334,7 @@ class FormProjets
 		$out='';
 
 		$hideunselectables = false;
-		if (! empty($conf->global->CONTRACT_HIDE_UNSELECTABLES)) $hideunselectables = true;
+		if (! empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) $hideunselectables = true;
 
 		if (empty($projectsListId))
 		{
@@ -346,11 +346,11 @@ class FormProjets
 		}
 
 		// Search all projects
-		$sql = 'SELECT t.rowid, t.ref as tref, t.label as tlabel, p.ref, p.title, p.fk_soc, p.fk_statut, p.public,';
+		$sql = 'SELECT t.rowid, t.ref as tref, t.label as tlabel, p.rowid as pid, p.ref, p.title, p.fk_soc, p.fk_statut, p.public,';
 		$sql.= ' s.nom as name';
 		$sql.= ' FROM '.MAIN_DB_PREFIX .'projet as p';
-		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc';
-		$sql.= ', '.MAIN_DB_PREFIX.'projet_task as t';
+		$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'societe as s ON s.rowid = p.fk_soc,';
+		$sql.= ' '.MAIN_DB_PREFIX.'projet_task as t';
 		$sql.= " WHERE p.entity IN (".getEntity('project').")";
 		$sql.= " AND t.fk_projet = p.rowid";
 		if ($projectsListId) $sql.= " AND p.rowid IN (".$projectsListId.")";
@@ -393,7 +393,7 @@ class FormProjets
 					}
 					else
 					{
-						if ($discard_closed == 1 && $obj->fk_statut == 2)
+						if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED)
 						{
 							$i++;
 							continue;
@@ -411,12 +411,12 @@ class FormProjets
 							if ($obj->name) $labeltoshow.=' ('.$obj->name.')';
 
 							$disabled=0;
-							if ($obj->fk_statut == 0)
+							if ($obj->fk_statut == Project::STATUS_DRAFT)
 							{
 								$disabled=1;
 								$labeltoshow.=' - '.$langs->trans("Draft");
 							}
-							else if ($obj->fk_statut == 2)
+							else if ($obj->fk_statut == Project::STATUS_CLOSED)
 							{
 								if ($discard_closed == 2) $disabled=1;
 								$labeltoshow.=' - '.$langs->trans("Closed");

+ 3 - 1
htdocs/core/class/interfaces.class.php

@@ -61,7 +61,7 @@ class Interfaces
      *   @param		string		$action     Trigger event code
      *   @param     object		$object     Objet concerned. Some context information may also be provided into array property object->context.
      *   @param     User		$user       Objet user
-     *   @param     Lang		$langs      Objet lang
+     *   @param     Translate	$langs      Objet lang
      *   @param     Conf		$conf       Objet conf
      *   @return    int         			Nb of triggers ran if no error, -Nb of triggers with errors otherwise.
      */
@@ -86,6 +86,7 @@ class Interfaces
             global $db;
             $user = new User($db);
         }
+        //dol_syslog(get_class($this)."::run_triggers action=".$action." Launch run_triggers", LOG_DEBUG);
 
         $nbfile = $nbtotal = $nbok = $nbko = 0;
 
@@ -94,6 +95,7 @@ class Interfaces
         $orders = array();
 		$i=0;
 
+
 		$dirtriggers=array_merge(array('/core/triggers'),$conf->modules_parts['triggers']);
         foreach($dirtriggers as $reldir)
         {

+ 0 - 1
htdocs/core/class/menubase.class.php

@@ -589,7 +589,6 @@ class Menubase
 
             $a = 0;
             $b = 0;
-            $oldrowid=0;
             while ($a < $numa)
             {
                 //$objm = $this->db->fetch_object($resql);

+ 4 - 4
htdocs/core/class/stats.class.php

@@ -384,16 +384,16 @@ abstract class Stats
 				$row = $this->db->fetch_object($resql);
 				$result[$i]['year'] = $row->year;
 				$result[$i]['nb'] = $row->nb;
-				if($i>0 && $row->nb) $result[$i-1]['nb_diff'] = ($result[$i-1]['nb'] - $row->nb) / $row->nb * 100;
+				if($i>0 && $row->nb>0) $result[$i-1]['nb_diff'] = ($result[$i-1]['nb'] - $row->nb) / $row->nb * 100;
 				$result[$i]['total'] = $row->total;
-				if($i>0 && $row->total) $result[$i-1]['total_diff'] = ($result[$i-1]['total'] - $row->total) / $row->total * 100;
+				if($i>0 && $row->total>0) $result[$i-1]['total_diff'] = ($result[$i-1]['total'] - $row->total) / $row->total * 100;
 				$result[$i]['avg'] = $row->avg;
-				if($i>0 && $row->avg) $result[$i-1]['avg_diff'] = ($result[$i-1]['avg'] - $row->avg) / $row->avg * 100;
+				if($i>0 && $row->avg>0) $result[$i-1]['avg_diff'] = ($result[$i-1]['avg'] - $row->avg) / $row->avg * 100;
 				// For some $sql only
 				if (isset($row->weighted))
 				{
 				    $result[$i]['weighted'] = $row->weighted;
-				    if($i>0 && $row->weighted) $result[$i-1]['avg_weighted'] = ($result[$i-1]['weighted'] - $row->weighted) / $row->weighted * 100;
+				    if($i>0 && $row->weighted>0) $result[$i-1]['avg_weighted'] = ($result[$i-1]['weighted'] - $row->weighted) / $row->weighted * 100;
 				}
 				$i++;
 			}

+ 2 - 2
htdocs/core/js/lib_foot.js.php

@@ -29,14 +29,14 @@ if (! defined('NOREQUIREMENU'))   define('NOREQUIREMENU',1);
 if (! defined('NOREQUIREHTML'))   define('NOREQUIREHTML',1);
 if (! defined('NOREQUIREAJAX'))   define('NOREQUIREAJAX','1');
 
-session_cache_limiter(false);
+session_cache_limiter('public');
 
 require_once '../../main.inc.php';
 
 // Define javascript type
 top_httphead('text/javascript; charset=UTF-8');
 // Important: Following code is to avoid page request by browser and PHP CPU at each Dolibarr page access.
-if (empty($dolibarr_nocache)) header('Cache-Control: max-age=3600, public, must-revalidate');
+if (empty($dolibarr_nocache)) header('Cache-Control: max-age=10800, public, must-revalidate');
 else header('Cache-Control: no-cache');
 
 //var_dump($conf);

Some files were not shown because too many files changed in this diff