Passed
Push — master ( 4e88da...5feb35 )
by Alxarafe
26:36
created
Helpers/DolUtils.php 1 patch
Indentation   +93 added lines, -93 removed lines patch added patch discarded remove patch
@@ -258,7 +258,7 @@  discard block
 block discarded – undo
258 258
      */
259 259
     static function dol_shutdown()
260 260
     {
261
-       // global Globals::$conf, $user, Globals::$langs, $db;
261
+        // global Globals::$conf, $user, Globals::$langs, $db;
262 262
         $disconnectdone = false;
263 263
         $depth = 0;
264 264
         if (is_object($db) && !empty($db->connected)) {
@@ -305,7 +305,7 @@  discard block
 block discarded – undo
305 305
      */
306 306
     static function GETPOST($paramname, $check = 'none', $method = 0, $filter = null, $options = null, $noreplace = 0)
307 307
     {
308
-       // global $mysoc, $user, Globals::$conf;
308
+        // global $mysoc, $user, Globals::$conf;
309 309
 
310 310
         if (empty($paramname))
311 311
             return 'BadFirstParameterForDolUtils::GETPOST';
@@ -364,7 +364,7 @@  discard block
 block discarded – undo
364 364
             elseif (!isset($_GET['sortfield'])) { // If we did a click on a field to sort, we do no apply default values. Same if option MAIN_ENABLE_DEFAULT_VALUES is not set
365 365
                 if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
366 366
                     // Search default value from $object->field
367
-                   // global $object;
367
+                    // global $object;
368 368
                     if (is_object($object) && isset($object->fields[$paramname]['default'])) {
369 369
                         $out = $object->fields[$paramname]['default'];
370 370
                     }
@@ -707,7 +707,7 @@  discard block
 block discarded – undo
707 707
      */
708 708
     static function dol_include_once($relpath, $classname = '')
709 709
     {
710
-       // global Globals::$conf, Globals::$langs, $user, $mysoc;   // Do not remove this. They must be defined for files we include. Other globals var must be retreived with $GLOBALS['var']
710
+        // global Globals::$conf, Globals::$langs, $user, $mysoc;   // Do not remove this. They must be defined for files we include. Other globals var must be retreived with $GLOBALS['var']
711 711
 
712 712
         $fullpath = dol_buildpath($relpath);
713 713
 
@@ -735,7 +735,7 @@  discard block
 block discarded – undo
735 735
      */
736 736
     static function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
737 737
     {
738
-       // global Globals::$conf;
738
+        // global Globals::$conf;
739 739
 
740 740
         $path = preg_replace('/^\//', '', $path);
741 741
 
@@ -777,7 +777,7 @@  discard block
 block discarded – undo
777 777
             foreach (Globals::$conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
778 778
                 if ($key == 'main') {
779 779
                     if ($type == 3) {
780
-                       // global $dolibarr_main_url_root;
780
+                        // global $dolibarr_main_url_root;
781 781
                         // Define $urlwithroot
782 782
 
783 783
                         // $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_BASE_URI, '/') . '$/i', '', trim($dolibarr_main_url_root));
@@ -801,7 +801,7 @@  discard block
 block discarded – undo
801 801
                             $res = (preg_match('/^http/i', Globals::$conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT) . Globals::$conf->file->dol_url_root[$key] . '/' . $path;
802 802
                         }
803 803
                         if ($type == 3) {
804
-                           // global $dolibarr_main_url_root;
804
+                            // global $dolibarr_main_url_root;
805 805
                             // Define $urlwithroot
806 806
                             $urlwithouturlroot = preg_replace('/' . preg_quote(DOL_BASE_URI, '/') . '$/i', '', trim($dolibarr_main_url_root));
807 807
                             $urlwithroot = $urlwithouturlroot . DOL_BASE_URI;  // This is to use external domain name found into config file
@@ -1071,7 +1071,7 @@  discard block
 block discarded – undo
1071 1071
      */
1072 1072
     static function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '')
1073 1073
     {
1074
-       // global Globals::$conf, $user;
1074
+        // global Globals::$conf, $user;
1075 1075
 // If syslog module enabled
1076 1076
         if (empty(Globals::$conf->syslog->enabled))
1077 1077
             return;
@@ -1177,7 +1177,7 @@  discard block
 block discarded – undo
1177 1177
      */
1178 1178
     static function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '')
1179 1179
     {
1180
-       // global Globals::$conf, Globals::$langs, $hookmanager;
1180
+        // global Globals::$conf, Globals::$langs, $hookmanager;
1181 1181
 
1182 1182
         $out = "\n" . '<div class="tabs" data-role="controlgroup" data-type="horizontal">' . "\n";
1183 1183
 
@@ -1360,7 +1360,7 @@  discard block
 block discarded – undo
1360 1360
      */
1361 1361
     static function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
1362 1362
     {
1363
-       // global Globals::$conf, $form, $user, Globals::$langs;
1363
+        // global Globals::$conf, $form, $user, Globals::$langs;
1364 1364
 
1365 1365
         $error = 0;
1366 1366
 
@@ -1639,7 +1639,7 @@  discard block
 block discarded – undo
1639 1639
      */
1640 1640
     static function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
1641 1641
     {
1642
-       // global Globals::$conf, Globals::$langs;
1642
+        // global Globals::$conf, Globals::$langs;
1643 1643
         $ret = '';
1644 1644
         if ($fieldrequired)
1645 1645
             $ret .= '<span class="fieldrequired">';
@@ -1662,7 +1662,7 @@  discard block
 block discarded – undo
1662 1662
      */
1663 1663
     static function dol_bc($var, $moreclass = '')
1664 1664
     {
1665
-       // global $bc;
1665
+        // global $bc;
1666 1666
         $ret = ' ' . $bc[$var];
1667 1667
         if ($moreclass)
1668 1668
             $ret = preg_replace('/class=\"/', 'class="' . $moreclass . ' ', $ret);
@@ -1682,7 +1682,7 @@  discard block
 block discarded – undo
1682 1682
      */
1683 1683
     static function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0)
1684 1684
     {
1685
-       // global Globals::$conf, Globals::$langs;
1685
+        // global Globals::$conf, Globals::$langs;
1686 1686
 
1687 1687
         $ret = '';
1688 1688
         $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR');    // See also MAIN_FORCE_STATE_INTO_ADDRESS
@@ -1773,7 +1773,7 @@  discard block
 block discarded – undo
1773 1773
      */
1774 1774
     static function dol_print_date($time, $format = '', $tzoutput = 'tzserver', $outputlangs = '', $encodetooutput = false)
1775 1775
     {
1776
-       // global Globals::$conf, Globals::$langs;
1776
+        // global Globals::$conf, Globals::$langs;
1777 1777
 // Clean parameters
1778 1778
         $to_gmt = false;
1779 1779
         $offsettz = $offsetdst = 0;
@@ -1984,7 +1984,7 @@  discard block
 block discarded – undo
1984 1984
      */
1985 1985
     static function dol_getdate($timestamp, $fast = false)
1986 1986
     {
1987
-       // global Globals::$conf;
1987
+        // global Globals::$conf;
1988 1988
 
1989 1989
         $usealternatemethod = false;
1990 1990
         if ($timestamp <= 0)
@@ -2022,7 +2022,7 @@  discard block
 block discarded – undo
2022 2022
      */
2023 2023
     static function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = false, $check = 1)
2024 2024
     {
2025
-       // global Globals::$conf;
2025
+        // global Globals::$conf;
2026 2026
 //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2027 2027
 // Clean parameters
2028 2028
         if ($hour == -1 || empty($hour))
@@ -2134,7 +2134,7 @@  discard block
 block discarded – undo
2134 2134
      */
2135 2135
     static function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
2136 2136
     {
2137
-       // global Globals::$conf, Globals::$langs;
2137
+        // global Globals::$conf, Globals::$langs;
2138 2138
         $level = 1024;
2139 2139
 
2140 2140
         if (!empty(Globals::$conf->dol_optimize_smallscreen))
@@ -2171,7 +2171,7 @@  discard block
 block discarded – undo
2171 2171
      */
2172 2172
     static function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0)
2173 2173
     {
2174
-       // global Globals::$langs;
2174
+        // global Globals::$langs;
2175 2175
 
2176 2176
         if (empty($url))
2177 2177
             return '';
@@ -2205,7 +2205,7 @@  discard block
 block discarded – undo
2205 2205
      */
2206 2206
     static function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
2207 2207
     {
2208
-       // global Globals::$conf, $user, Globals::$langs, $hookmanager;
2208
+        // global Globals::$conf, $user, Globals::$langs, $hookmanager;
2209 2209
 
2210 2210
         $newemail = $email;
2211 2211
 
@@ -2262,7 +2262,7 @@  discard block
 block discarded – undo
2262 2262
      */
2263 2263
     static function dol_print_socialnetworks($value, $cid, $socid, $type)
2264 2264
     {
2265
-       // global Globals::$conf, $user, Globals::$langs;
2265
+        // global Globals::$conf, $user, Globals::$langs;
2266 2266
 
2267 2267
         $newskype = $value;
2268 2268
 
@@ -2317,7 +2317,7 @@  discard block
 block discarded – undo
2317 2317
      */
2318 2318
     static function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
2319 2319
     {
2320
-       // global Globals::$conf, $user, Globals::$langs, $mysoc, $hookmanager;
2320
+        // global Globals::$conf, $user, Globals::$langs, $mysoc, $hookmanager;
2321 2321
 // Clean phone parameter
2322 2322
         $phone = preg_replace("/[\s.-]/", "", trim($phone));
2323 2323
         if (empty($phone)) {
@@ -2581,7 +2581,7 @@  discard block
 block discarded – undo
2581 2581
      */
2582 2582
     static function dol_print_ip($ip, $mode = 0)
2583 2583
     {
2584
-       // global Globals::$conf, Globals::$langs;
2584
+        // global Globals::$conf, Globals::$langs;
2585 2585
 
2586 2586
         $ret = '';
2587 2587
 
@@ -2626,7 +2626,7 @@  discard block
 block discarded – undo
2626 2626
      */
2627 2627
     static function dolGetCountryCodeFromIp($ip)
2628 2628
     {
2629
-       // global Globals::$conf;
2629
+        // global Globals::$conf;
2630 2630
 
2631 2631
         $countrycode = '';
2632 2632
 
@@ -2653,7 +2653,7 @@  discard block
 block discarded – undo
2653 2653
      */
2654 2654
     static function dol_user_country()
2655 2655
     {
2656
-       // global Globals::$conf, Globals::$langs, $user;
2656
+        // global Globals::$conf, Globals::$langs, $user;
2657 2657
 //$ret=$user->xxx;
2658 2658
         $ret = '';
2659 2659
         if (!empty(Globals::$conf->geoipmaxmind->enabled)) {
@@ -2683,7 +2683,7 @@  discard block
 block discarded – undo
2683 2683
      */
2684 2684
     static function dol_print_address($address, $htmlid, $mode, $id, $noprint = 0, $charfornl = '')
2685 2685
     {
2686
-       // global Globals::$conf, $user, Globals::$langs, $hookmanager;
2686
+        // global Globals::$conf, $user, Globals::$langs, $hookmanager;
2687 2687
 
2688 2688
         $out = '';
2689 2689
 
@@ -2815,7 +2815,7 @@  discard block
 block discarded – undo
2815 2815
      */
2816 2816
     static function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0)
2817 2817
     {
2818
-       // global Globals::$langs;
2818
+        // global Globals::$langs;
2819 2819
 
2820 2820
         if (empty($stringencoding))
2821 2821
             $stringencoding = Globals::$langs->charset_output;
@@ -2852,7 +2852,7 @@  discard block
 block discarded – undo
2852 2852
      */
2853 2853
     static function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
2854 2854
     {
2855
-       // global Globals::$conf;
2855
+        // global Globals::$conf;
2856 2856
 
2857 2857
         if ($size == 0 || !empty(Globals::$conf->global->MAIN_DISABLE_TRUNC))
2858 2858
             return $string;
@@ -2918,7 +2918,7 @@  discard block
 block discarded – undo
2918 2918
      */
2919 2919
     static function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '')
2920 2920
     {
2921
-       // global Globals::$conf, Globals::$langs;
2921
+        // global Globals::$conf, Globals::$langs;
2922 2922
 // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_BASE_URI/theme/$conf->theme/img/$picto
2923 2923
 //$url = DOL_BASE_URI;
2924 2924
         $url = DOL_BASE_URI;
@@ -3103,7 +3103,7 @@  discard block
 block discarded – undo
3103 3103
      */
3104 3104
     static function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
3105 3105
     {
3106
-       // global Globals::$conf;
3106
+        // global Globals::$conf;
3107 3107
 
3108 3108
         if (!preg_match('/(\.png|\.gif)$/i', $picto))
3109 3109
             $picto .= '.png';
@@ -3126,7 +3126,7 @@  discard block
 block discarded – undo
3126 3126
      */
3127 3127
     static function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
3128 3128
     {
3129
-       // global Globals::$conf;
3129
+        // global Globals::$conf;
3130 3130
 
3131 3131
         if (!preg_match('/(\.png|\.gif)$/i', $picto))
3132 3132
             $picto .= '.png';
@@ -3157,7 +3157,7 @@  discard block
 block discarded – undo
3157 3157
      */
3158 3158
     static function img_action($titlealt, $numaction)
3159 3159
     {
3160
-       // global Globals::$conf, Globals::$langs;
3160
+        // global Globals::$conf, Globals::$langs;
3161 3161
 
3162 3162
         if (empty($titlealt) || $titlealt == 'default') {
3163 3163
             if ($numaction == '-1' || $numaction == 'ST_NO') {
@@ -3195,7 +3195,7 @@  discard block
 block discarded – undo
3195 3195
      */
3196 3196
     static function img_pdf($titlealt = 'default', $size = 3)
3197 3197
     {
3198
-       // global Globals::$conf, Globals::$langs;
3198
+        // global Globals::$conf, Globals::$langs;
3199 3199
 
3200 3200
         if ($titlealt == 'default')
3201 3201
             $titlealt = Globals::$langs->trans('Show');
@@ -3212,7 +3212,7 @@  discard block
 block discarded – undo
3212 3212
      */
3213 3213
     static function img_edit_add($titlealt = 'default', $other = '')
3214 3214
     {
3215
-       // global Globals::$conf, Globals::$langs;
3215
+        // global Globals::$conf, Globals::$langs;
3216 3216
 
3217 3217
         if ($titlealt == 'default')
3218 3218
             $titlealt = Globals::$langs->trans('Add');
@@ -3229,7 +3229,7 @@  discard block
 block discarded – undo
3229 3229
      */
3230 3230
     static function img_edit_remove($titlealt = 'default', $other = '')
3231 3231
     {
3232
-       // global Globals::$conf, Globals::$langs;
3232
+        // global Globals::$conf, Globals::$langs;
3233 3233
 
3234 3234
         if ($titlealt == 'default')
3235 3235
             $titlealt = Globals::$langs->trans('Remove');
@@ -3247,7 +3247,7 @@  discard block
 block discarded – undo
3247 3247
      */
3248 3248
     static function img_edit($titlealt = 'default', $float = 0, $other = 'class="pictoedit"')
3249 3249
     {
3250
-       // global Globals::$conf, Globals::$langs;
3250
+        // global Globals::$conf, Globals::$langs;
3251 3251
 
3252 3252
         if ($titlealt == 'default')
3253 3253
             $titlealt = Globals::$langs->trans('Modify');
@@ -3265,7 +3265,7 @@  discard block
 block discarded – undo
3265 3265
      */
3266 3266
     static function img_view($titlealt = 'default', $float = 0, $other = '')
3267 3267
     {
3268
-       // global Globals::$conf, Globals::$langs;
3268
+        // global Globals::$conf, Globals::$langs;
3269 3269
 
3270 3270
         if ($titlealt == 'default')
3271 3271
             $titlealt = Globals::$langs->trans('View');
@@ -3284,7 +3284,7 @@  discard block
 block discarded – undo
3284 3284
      */
3285 3285
     static function img_delete($titlealt = 'default', $other = 'class="pictodelete"')
3286 3286
     {
3287
-       // global Globals::$conf, Globals::$langs;
3287
+        // global Globals::$conf, Globals::$langs;
3288 3288
 
3289 3289
         if ($titlealt == 'default')
3290 3290
             $titlealt = Globals::$langs->trans('Delete');
@@ -3302,7 +3302,7 @@  discard block
 block discarded – undo
3302 3302
      */
3303 3303
     static function img_printer($titlealt = "default", $other = '')
3304 3304
     {
3305
-       // global Globals::$conf, Globals::$langs;
3305
+        // global Globals::$conf, Globals::$langs;
3306 3306
         if ($titlealt == "default")
3307 3307
             $titlealt = Globals::$langs->trans("Print");
3308 3308
         return img_picto($titlealt, 'printer.png', $other);
@@ -3317,7 +3317,7 @@  discard block
 block discarded – undo
3317 3317
      */
3318 3318
     static function img_split($titlealt = 'default', $other = 'class="pictosplit"')
3319 3319
     {
3320
-       // global Globals::$conf, Globals::$langs;
3320
+        // global Globals::$conf, Globals::$langs;
3321 3321
 
3322 3322
         if ($titlealt == 'default')
3323 3323
             $titlealt = Globals::$langs->trans('Split');
@@ -3334,7 +3334,7 @@  discard block
 block discarded – undo
3334 3334
      */
3335 3335
     static function img_help($usehelpcursor = 1, $usealttitle = 1)
3336 3336
     {
3337
-       // global Globals::$conf, Globals::$langs;
3337
+        // global Globals::$conf, Globals::$langs;
3338 3338
 
3339 3339
         if ($usealttitle) {
3340 3340
             if (is_string($usealttitle))
@@ -3354,7 +3354,7 @@  discard block
 block discarded – undo
3354 3354
      */
3355 3355
     static function img_info($titlealt = 'default')
3356 3356
     {
3357
-       // global Globals::$conf, Globals::$langs;
3357
+        // global Globals::$conf, Globals::$langs;
3358 3358
 
3359 3359
         if ($titlealt == 'default')
3360 3360
             $titlealt = Globals::$langs->trans('Informations');
@@ -3371,7 +3371,7 @@  discard block
 block discarded – undo
3371 3371
      */
3372 3372
     static function img_warning($titlealt = 'default', $moreatt = '')
3373 3373
     {
3374
-       // global Globals::$conf, Globals::$langs;
3374
+        // global Globals::$conf, Globals::$langs;
3375 3375
 
3376 3376
         if ($titlealt == 'default')
3377 3377
             $titlealt = Globals::$langs->trans('Warning');
@@ -3388,7 +3388,7 @@  discard block
 block discarded – undo
3388 3388
      */
3389 3389
     static function img_error($titlealt = 'default')
3390 3390
     {
3391
-       // global Globals::$conf, Globals::$langs;
3391
+        // global Globals::$conf, Globals::$langs;
3392 3392
 
3393 3393
         if ($titlealt == 'default')
3394 3394
             $titlealt = Globals::$langs->trans('Error');
@@ -3405,7 +3405,7 @@  discard block
 block discarded – undo
3405 3405
      */
3406 3406
     static function img_next($titlealt = 'default', $moreatt = '')
3407 3407
     {
3408
-       // global Globals::$conf, Globals::$langs;
3408
+        // global Globals::$conf, Globals::$langs;
3409 3409
 
3410 3410
         if ($titlealt == 'default')
3411 3411
             $titlealt = Globals::$langs->trans('Next');
@@ -3423,7 +3423,7 @@  discard block
 block discarded – undo
3423 3423
      */
3424 3424
     static function img_previous($titlealt = 'default', $moreatt = '')
3425 3425
     {
3426
-       // global Globals::$conf, Globals::$langs;
3426
+        // global Globals::$conf, Globals::$langs;
3427 3427
 
3428 3428
         if ($titlealt == 'default')
3429 3429
             $titlealt = Globals::$langs->trans('Previous');
@@ -3442,7 +3442,7 @@  discard block
 block discarded – undo
3442 3442
      */
3443 3443
     static function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
3444 3444
     {
3445
-       // global Globals::$conf, Globals::$langs;
3445
+        // global Globals::$conf, Globals::$langs;
3446 3446
 
3447 3447
         if ($titlealt == 'default')
3448 3448
             $titlealt = Globals::$langs->trans('Down');
@@ -3460,7 +3460,7 @@  discard block
 block discarded – undo
3460 3460
      */
3461 3461
     static function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
3462 3462
     {
3463
-       // global Globals::$conf, Globals::$langs;
3463
+        // global Globals::$conf, Globals::$langs;
3464 3464
 
3465 3465
         if ($titlealt == 'default')
3466 3466
             $titlealt = Globals::$langs->trans('Up');
@@ -3478,7 +3478,7 @@  discard block
 block discarded – undo
3478 3478
      */
3479 3479
     static function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
3480 3480
     {
3481
-       // global Globals::$conf, Globals::$langs;
3481
+        // global Globals::$conf, Globals::$langs;
3482 3482
 
3483 3483
         if ($titlealt == 'default')
3484 3484
             $titlealt = Globals::$langs->trans('Left');
@@ -3496,7 +3496,7 @@  discard block
 block discarded – undo
3496 3496
      */
3497 3497
     static function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
3498 3498
     {
3499
-       // global Globals::$conf, Globals::$langs;
3499
+        // global Globals::$conf, Globals::$langs;
3500 3500
 
3501 3501
         if ($titlealt == 'default')
3502 3502
             $titlealt = Globals::$langs->trans('Right');
@@ -3513,7 +3513,7 @@  discard block
 block discarded – undo
3513 3513
      */
3514 3514
     static function img_allow($allow, $titlealt = 'default')
3515 3515
     {
3516
-       // global Globals::$conf, Globals::$langs;
3516
+        // global Globals::$conf, Globals::$langs;
3517 3517
 
3518 3518
         if ($titlealt == 'default')
3519 3519
             $titlealt = Globals::$langs->trans('Active');
@@ -3588,7 +3588,7 @@  discard block
 block discarded – undo
3588 3588
     {
3589 3589
         dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
3590 3590
 
3591
-       // global Globals::$conf, Globals::$langs;
3591
+        // global Globals::$conf, Globals::$langs;
3592 3592
 
3593 3593
         if ($titlealt == 'default')
3594 3594
             $titlealt = Globals::$langs->trans('Call');
@@ -3610,7 +3610,7 @@  discard block
 block discarded – undo
3610 3610
      */
3611 3611
     static function img_search($titlealt = 'default', $other = '')
3612 3612
     {
3613
-       // global Globals::$conf, Globals::$langs;
3613
+        // global Globals::$conf, Globals::$langs;
3614 3614
 
3615 3615
         if ($titlealt == 'default')
3616 3616
             $titlealt = Globals::$langs->trans('Search');
@@ -3632,7 +3632,7 @@  discard block
 block discarded – undo
3632 3632
      */
3633 3633
     static function img_searchclear($titlealt = 'default', $other = '')
3634 3634
     {
3635
-       // global Globals::$conf, Globals::$langs;
3635
+        // global Globals::$conf, Globals::$langs;
3636 3636
 
3637 3637
         if ($titlealt == 'default')
3638 3638
             $titlealt = Globals::$langs->trans('Search');
@@ -3657,7 +3657,7 @@  discard block
 block discarded – undo
3657 3657
      */
3658 3658
     static function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = '')
3659 3659
     {
3660
-       // global Globals::$conf, Globals::$langs;
3660
+        // global Globals::$conf, Globals::$langs;
3661 3661
 
3662 3662
         if ($infoonimgalt) {
3663 3663
             return img_picto($text, 'info', 'class="hideonsmartphone' . ($morecss ? ' ' . $morecss : '') . '"');
@@ -3680,7 +3680,7 @@  discard block
 block discarded – undo
3680 3680
      */
3681 3681
     static function dol_print_error($db = '', $error = '', $errors = null)
3682 3682
     {
3683
-       // global Globals::$conf, Globals::$langs, $argv;
3683
+        // global Globals::$conf, Globals::$langs, $argv;
3684 3684
         // global $dolibarr_main_prod;
3685 3685
 
3686 3686
         $out = '';
@@ -3799,7 +3799,7 @@  discard block
 block discarded – undo
3799 3799
      */
3800 3800
     static function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
3801 3801
     {
3802
-       // global Globals::$langs, Globals::$conf;
3802
+        // global Globals::$langs, Globals::$conf;
3803 3803
 
3804 3804
         if (empty($email))
3805 3805
             $email = Globals::$conf->global->MAIN_INFO_SOCIETE_MAIL;
@@ -3858,7 +3858,7 @@  discard block
 block discarded – undo
3858 3858
      */
3859 3859
     static function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '')
3860 3860
     {
3861
-       // global Globals::$conf, Globals::$langs, $form;
3861
+        // global Globals::$conf, Globals::$langs, $form;
3862 3862
 //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
3863 3863
 
3864 3864
         $sortorder = strtoupper($sortorder);
@@ -3997,7 +3997,7 @@  discard block
 block discarded – undo
3997 3997
      */
3998 3998
     static function load_fiche_titre($titre, $morehtmlright = '', $picto = 'title_generic.png', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
3999 3999
     {
4000
-       // global Globals::$conf;
4000
+        // global Globals::$conf;
4001 4001
 
4002 4002
         $return = '';
4003 4003
 
@@ -4047,7 +4047,7 @@  discard block
 block discarded – undo
4047 4047
      */
4048 4048
     static function print_barre_liste($titre, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'title_generic.png', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $hideselectlimit = 0, $hidenavigation = 0)
4049 4049
     {
4050
-       // global Globals::$conf, Globals::$langs;
4050
+        // global Globals::$conf, Globals::$langs;
4051 4051
 
4052 4052
         $savlimit = $limit;
4053 4053
         $savtotalnboflines = $totalnboflines;
@@ -4160,7 +4160,7 @@  discard block
 block discarded – undo
4160 4160
      */
4161 4161
     static function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0)
4162 4162
     {
4163
-       // global Globals::$conf, Globals::$langs;
4163
+        // global Globals::$conf, Globals::$langs;
4164 4164
 
4165 4165
         print '<div class="pagination"><ul>';
4166 4166
         if ((int) $limit >= 0 && empty($hideselectlimit)) {
@@ -4283,7 +4283,7 @@  discard block
 block discarded – undo
4283 4283
      */
4284 4284
     static function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
4285 4285
     {
4286
-       // global Globals::$langs, Globals::$conf;
4286
+        // global Globals::$langs, Globals::$conf;
4287 4287
 // Clean parameters
4288 4288
         if (empty($amount))
4289 4289
             $amount = 0; // To have a numeric value if amount not defined or = ''
@@ -4376,7 +4376,7 @@  discard block
 block discarded – undo
4376 4376
      */
4377 4377
     static function price2num($amount, $rounding = '', $alreadysqlnb = 0)
4378 4378
     {
4379
-       // global Globals::$langs, Globals::$conf;
4379
+        // global Globals::$langs, Globals::$conf;
4380 4380
 // Round PHP static function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
4381 4381
 // Numbers must be '1234.56'
4382 4382
 // Decimal delimiter for PHP and database SQL requests must be '.'
@@ -4499,7 +4499,7 @@  discard block
 block discarded – undo
4499 4499
      */
4500 4500
     static function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
4501 4501
     {
4502
-       // global $db, Globals::$conf, $mysoc;
4502
+        // global $db, Globals::$conf, $mysoc;
4503 4503
 
4504 4504
         if (empty($thirdparty_seller) || !is_object($thirdparty_seller))
4505 4505
             $thirdparty_seller = $mysoc;
@@ -4639,7 +4639,7 @@  discard block
 block discarded – undo
4639 4639
      */
4640 4640
     static function get_localtax_by_third($local)
4641 4641
     {
4642
-       // global $db, $mysoc;
4642
+        // global $db, $mysoc;
4643 4643
         $sql = "SELECT t.localtax1, t.localtax2 ";
4644 4644
         $sql .= " FROM " . MAIN_DB_PREFIX . "c_tva as t inner join " . MAIN_DB_PREFIX . "c_country as c ON c.rowid=t.fk_pays";
4645 4645
         $sql .= " WHERE c.code = '" . $mysoc->country_code . "' AND t.active = 1 AND t.taux=(";
@@ -4672,7 +4672,7 @@  discard block
 block discarded – undo
4672 4672
      */
4673 4673
     static function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
4674 4674
     {
4675
-       // global $db, $mysoc;
4675
+        // global $db, $mysoc;
4676 4676
 
4677 4677
         dol_syslog("getTaxesFromId vat id or rate = " . $vatrate);
4678 4678
 
@@ -4729,7 +4729,7 @@  discard block
 block discarded – undo
4729 4729
      */
4730 4730
     static function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
4731 4731
     {
4732
-       // global $db, $mysoc;
4732
+        // global $db, $mysoc;
4733 4733
 
4734 4734
         dol_syslog("getLocalTaxesFromRate vatrate=" . $vatrate . " local=" . $local);
4735 4735
 
@@ -4783,7 +4783,7 @@  discard block
 block discarded – undo
4783 4783
      */
4784 4784
     static function get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice = 0)
4785 4785
     {
4786
-       // global $db, Globals::$conf, $mysoc;
4786
+        // global $db, Globals::$conf, $mysoc;
4787 4787
 
4788 4788
         require_once DOL_BASE_PATH . '/product/class/product.class.php';
4789 4789
 
@@ -4854,7 +4854,7 @@  discard block
 block discarded – undo
4854 4854
      */
4855 4855
     static function get_product_localtax_for_country($idprod, $local, $thirdparty_seller)
4856 4856
     {
4857
-       // global $db, $mysoc;
4857
+        // global $db, $mysoc;
4858 4858
 
4859 4859
         if (!class_exists('Product')) {
4860 4860
             require_once DOL_BASE_PATH . 'product/class/product.class.php';
@@ -4922,7 +4922,7 @@  discard block
 block discarded – undo
4922 4922
      */
4923 4923
     static function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
4924 4924
     {
4925
-       // global Globals::$conf;
4925
+        // global Globals::$conf;
4926 4926
 
4927 4927
         require_once DOL_BASE_PATH . '/core/lib/company.lib.php';
4928 4928
 
@@ -4997,7 +4997,7 @@  discard block
 block discarded – undo
4997 4997
      */
4998 4998
     static function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
4999 4999
     {
5000
-       // global $db;
5000
+        // global $db;
5001 5001
 
5002 5002
         if ($idprodfournprice > 0) {
5003 5003
             if (!class_exists('ProductFournisseur'))
@@ -5032,7 +5032,7 @@  discard block
 block discarded – undo
5032 5032
      */
5033 5033
     static function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
5034 5034
     {
5035
-       // global $mysoc;
5035
+        // global $mysoc;
5036 5036
 
5037 5037
         if (!is_object($thirdparty_seller))
5038 5038
             return -1;
@@ -5077,7 +5077,7 @@  discard block
 block discarded – undo
5077 5077
      */
5078 5078
     static function yn($yesno, $case = 1, $color = 0)
5079 5079
     {
5080
-       // global Globals::$langs;
5080
+        // global Globals::$langs;
5081 5081
         $result = 'unknown';
5082 5082
         $classname = '';
5083 5083
         if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') {  // A mettre avant test sur no a cause du == 0
@@ -5127,7 +5127,7 @@  discard block
 block discarded – undo
5127 5127
      */
5128 5128
     static function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart)
5129 5129
     {
5130
-       // global Globals::$conf;
5130
+        // global Globals::$conf;
5131 5131
 
5132 5132
         $path = '';
5133 5133
 
@@ -5175,7 +5175,7 @@  discard block
 block discarded – undo
5175 5175
      */
5176 5176
     static function dol_mkdir($dir, $dataroot = '', $newmask = null)
5177 5177
     {
5178
-       // global Globals::$conf;
5178
+        // global Globals::$conf;
5179 5179
 
5180 5180
         dol_syslog("functions.lib::dol_mkdir: dir=" . $dir, LOG_INFO);
5181 5181
 
@@ -5722,7 +5722,7 @@  discard block
 block discarded – undo
5722 5722
      */
5723 5723
     static function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null)
5724 5724
     {
5725
-       // global $db, Globals::$conf, $mysoc, $user, $extrafields;
5725
+        // global $db, Globals::$conf, $mysoc, $user, $extrafields;
5726 5726
 
5727 5727
         $substitutionarray = array();
5728 5728
 
@@ -6028,7 +6028,7 @@  discard block
 block discarded – undo
6028 6028
      */
6029 6029
     static function make_substitutions($text, $substitutionarray, $outputlangs = null)
6030 6030
     {
6031
-       // global Globals::$conf, Globals::$langs;
6031
+        // global Globals::$conf, Globals::$langs;
6032 6032
 
6033 6033
         if (!is_array($substitutionarray))
6034 6034
             return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
@@ -6094,7 +6094,7 @@  discard block
 block discarded – undo
6094 6094
      */
6095 6095
     static function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
6096 6096
     {
6097
-       // global Globals::$conf, $user;
6097
+        // global Globals::$conf, $user;
6098 6098
 
6099 6099
         require_once DOL_BASE_PATH . '/core/lib/files.lib.php';
6100 6100
 
@@ -6164,7 +6164,7 @@  discard block
 block discarded – undo
6164 6164
      */
6165 6165
     static function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1)
6166 6166
     {
6167
-       // global Globals::$langs;
6167
+        // global Globals::$langs;
6168 6168
 
6169 6169
         $out = '';
6170 6170
 
@@ -6194,7 +6194,7 @@  discard block
 block discarded – undo
6194 6194
      */
6195 6195
     static function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
6196 6196
     {
6197
-       // global Globals::$conf;
6197
+        // global Globals::$conf;
6198 6198
 
6199 6199
         $ret = '';
6200 6200
 // If order not defined, we use the setup
@@ -6318,7 +6318,7 @@  discard block
 block discarded – undo
6318 6318
      */
6319 6319
     static function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0)
6320 6320
     {
6321
-       // global Globals::$conf, Globals::$langs;
6321
+        // global Globals::$conf, Globals::$langs;
6322 6322
 
6323 6323
         $ret = 0;
6324 6324
         $return = '';
@@ -6554,7 +6554,7 @@  discard block
 block discarded – undo
6554 6554
      */
6555 6555
     function dol_osencode($str)
6556 6556
     {
6557
-       // global Globals::$conf;
6557
+        // global Globals::$conf;
6558 6558
 
6559 6559
         $tmp = ini_get("unicode.filesystem_encoding");      // Disponible avec PHP 6.0
6560 6560
         if (empty($tmp) && !empty($_SERVER["WINDIR"]))
@@ -6584,7 +6584,7 @@  discard block
 block discarded – undo
6584 6584
      */
6585 6585
     static function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0)
6586 6586
     {
6587
-       // global $cache_codes;
6587
+        // global $cache_codes;
6588 6588
 // If key empty
6589 6589
         if ($key == '')
6590 6590
             return '';
@@ -6625,7 +6625,7 @@  discard block
 block discarded – undo
6625 6625
      */
6626 6626
     static function verifCond($strRights)
6627 6627
     {
6628
-       // global $user, Globals::$conf, Globals::$langs;
6628
+        // global $user, Globals::$conf, Globals::$langs;
6629 6629
         // global $leftmenu;
6630 6630
         // global $rights;    // To export to dol_eval function
6631 6631
 //print $strRights."<br>\n";
@@ -6691,7 +6691,7 @@  discard block
 block discarded – undo
6691 6691
      */
6692 6692
     static function picto_from_langcode($codelang, $moreatt = '')
6693 6693
     {
6694
-       // global Globals::$langs;
6694
+        // global Globals::$langs;
6695 6695
 
6696 6696
         if (empty($codelang))
6697 6697
             return '';
@@ -6726,7 +6726,7 @@  discard block
 block discarded – undo
6726 6726
      */
6727 6727
     static function getLanguageCodeFromCountryCode($countrycode)
6728 6728
     {
6729
-       // global $mysoc;
6729
+        // global $mysoc;
6730 6730
 
6731 6731
         if (strtoupper($countrycode) == 'MQ')
6732 6732
             return 'fr_CA';
@@ -6960,7 +6960,7 @@  discard block
 block discarded – undo
6960 6960
      */
6961 6961
     static function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add')
6962 6962
     {
6963
-       // global $hookmanager;
6963
+        // global $hookmanager;
6964 6964
 
6965 6965
         if (isset(Globals::$conf->modules_parts['tabs'][$type]) && is_array(Globals::$conf->modules_parts['tabs'][$type])) {
6966 6966
             foreach (Globals::$conf->modules_parts['tabs'][$type] as $value) {
@@ -7049,7 +7049,7 @@  discard block
 block discarded – undo
7049 7049
      */
7050 7050
     static function printCommonFooter($zone = 'private')
7051 7051
     {
7052
-       // global Globals::$conf, $hookmanager, $user;
7052
+        // global Globals::$conf, $hookmanager, $user;
7053 7053
         // global $action;
7054 7054
         // global $micro_start_time;
7055 7055
 
@@ -7289,7 +7289,7 @@  discard block
 block discarded – undo
7289 7289
      */
7290 7290
     static function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
7291 7291
     {
7292
-       // global $db, Globals::$langs;
7292
+        // global $db, Globals::$langs;
7293 7293
 
7294 7294
         $value = trim($value);
7295 7295
 
@@ -7422,7 +7422,7 @@  discard block
 block discarded – undo
7422 7422
      */
7423 7423
     static function showDirectDownloadLink($object)
7424 7424
     {
7425
-       // global Globals::$conf, Globals::$langs;
7425
+        // global Globals::$conf, Globals::$langs;
7426 7426
 
7427 7427
         $out = '';
7428 7428
         $url = $object->getLastMainDocLink($object->element);
@@ -7484,7 +7484,7 @@  discard block
 block discarded – undo
7484 7484
      */
7485 7485
     static function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
7486 7486
     {
7487
-       // global Globals::$conf, Globals::$langs;
7487
+        // global Globals::$conf, Globals::$langs;
7488 7488
 
7489 7489
         if (empty(Globals::$conf->use_javascript_ajax))
7490 7490
             return '';
@@ -7517,7 +7517,7 @@  discard block
 block discarded – undo
7517 7517
      */
7518 7518
     static function ajax_autoselect($htmlname, $addlink = '')
7519 7519
     {
7520
-       // global Globals::$langs;
7520
+        // global Globals::$langs;
7521 7521
         $out = '<script type="text/javascript">
7522 7522
                jQuery(document).ready(static function () {
7523 7523
 				    jQuery("#' . $htmlname . '").click(function() { jQuery(this).select(); } );
@@ -7901,7 +7901,7 @@  discard block
 block discarded – undo
7901 7901
      */
7902 7902
     static function getDictvalue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
7903 7903
     {
7904
-       // global $dictvalues, $db, Globals::$langs;
7904
+        // global $dictvalues, $db, Globals::$langs;
7905 7905
 
7906 7906
         if (!isset($dictvalues[$tablename])) {
7907 7907
             $dictvalues[$tablename] = array();
@@ -7969,7 +7969,7 @@  discard block
 block discarded – undo
7969 7969
      */
7970 7970
     static function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
7971 7971
     {
7972
-       // global Globals::$conf;
7972
+        // global Globals::$conf;
7973 7973
 //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
7974 7974
 //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
7975 7975
         if (empty($menuentry['enabled']))
Please login to merge, or discard this patch.
Base/User.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2963,7 +2963,7 @@
 block discarded – undo
2963 2963
     function load_state_board()
2964 2964
     {
2965 2965
 // phpcs:enable
2966
-       // global $conf;
2966
+        // global $conf;
2967 2967
 
2968 2968
         $this->nb = array();
2969 2969
 
Please login to merge, or discard this patch.
Base/Menu.php 1 patch
Indentation   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -33,11 +33,11 @@  discard block
 block discarded – undo
33 33
     var $liste;
34 34
 
35 35
     /**
36
-	 *	Constructor
36
+     *	Constructor
37 37
      */
38 38
     function __construct()
39 39
     {
40
-      	$this->liste = array();
40
+            $this->liste = array();
41 41
     }
42 42
 
43 43
     /**
@@ -69,7 +69,7 @@  discard block
 block discarded – undo
69 69
      */
70 70
     function add($url, $titre, $level=0, $enabled=1, $target='',$mainmenu='',$leftmenu='',$position=0, $id='', $idsel='', $classname='', $prefix='')
71 71
     {
72
-    	$this->liste[]=array('url'=>$url,'titre'=>$titre,'level'=>$level,'enabled'=>$enabled,'target'=>$target,'mainmenu'=>$mainmenu,'leftmenu'=>$leftmenu, 'position'=>$position, 'id'=>$id, 'idsel'=>$idsel, 'classname'=>$classname, 'prefix'=>$prefix);
72
+        $this->liste[]=array('url'=>$url,'titre'=>$titre,'level'=>$level,'enabled'=>$enabled,'target'=>$target,'mainmenu'=>$mainmenu,'leftmenu'=>$leftmenu, 'position'=>$position, 'id'=>$id, 'idsel'=>$idsel, 'classname'=>$classname, 'prefix'=>$prefix);
73 73
     }
74 74
 
75 75
     /**
Please login to merge, or discard this patch.
Base/CommonObject.php 1 patch
Indentation   +7078 added lines, -7078 removed lines patch added patch discarded remove patch
@@ -21,7382 +21,7382 @@
 block discarded – undo
21 21
 
22 22
 abstract class CommonObject
23 23
 {
24
-	/**
24
+    /**
25 25
      * @var int The object identifier
26 26
      */
27
-	public $id;
28
-
29
-	/**
30
-	 * @var string 		Error string
31
-	 * @see             errors
32
-	 */
33
-	public $error;
34
-
35
-	/**
36
-	 * @var string[]	Array of error strings
37
-	 */
38
-	public $errors=array();
39
-
40
-	/**
41
-	 * @var string ID to identify managed object
42
-	 */
43
-	public $element;
44
-
45
-	/**
46
-	 * @var string Name of table without prefix where object is stored
47
-	 */
48
-	public $table_element;
49
-
50
-	/**
51
-	 * @var int    Name of subtable line
52
-	 */
53
-	public $table_element_line='';
54
-
55
-	/**
56
-	 * @var string		Key value used to track if data is coming from import wizard
57
-	 */
58
-	public $import_key;
59
-
60
-	/**
61
-	 * @var mixed		Contains data to manage extrafields
62
-	 */
63
-	public $array_options=array();
64
-
65
-	/**
66
-	 * @var int[][]		Array of linked objects ids. Loaded by ->fetchObjectLinked
67
-	 */
68
-	public $linkedObjectsIds;
69
-
70
-	/**
71
-	 * @var mixed		Array of linked objects. Loaded by ->fetchObjectLinked
72
-	 */
73
-	public $linkedObjects;
74
-
75
-	/**
76
-	 * @var Object      To store a cloned copy of object before to edit it and keep track of old properties
77
-	 */
78
-	public $oldcopy;
79
-
80
-	/**
81
-	 * @var string		Column name of the ref field.
82
-	 */
83
-	protected $table_ref_field = '';
84
-
85
-
86
-
87
-	// Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
88
-
89
-	/**
90
-	 * @var array<string,mixed>		Can be used to pass information when only object is provided to method
91
-	 */
92
-	public $context=array();
93
-
94
-	/**
95
-	 * @var string		Contains canvas name if record is an alternative canvas record
96
-	 */
97
-	public $canvas;
98
-
99
-	/**
100
-	 * @var Project The related project
101
-	 * @see fetch_projet()
102
-	 */
103
-	public $project;
104
-
105
-	/**
106
-	 * @var int The related project ID
107
-	 * @see setProject(), project
108
-	 */
109
-	public $fk_project;
110
-
111
-	/**
112
-	 * @deprecated
113
-	 * @see project
114
-	 */
115
-	public $projet;
116
-
117
-	/**
118
-	 * @var Contact a related contact
119
-	 * @see fetch_contact()
120
-	 */
121
-	public $contact;
122
-
123
-	/**
124
-	 * @var int The related contact ID
125
-	 * @see fetch_contact()
126
-	 */
127
-	public $contact_id;
128
-
129
-	/**
130
-	 * @var Societe A related thirdparty
131
-	 * @see fetch_thirdparty()
132
-	 */
133
-	public $thirdparty;
134
-
135
-	/**
136
-	 * @var User A related user
137
-	 * @see fetch_user()
138
-	 */
139
-	public $user;
140
-
141
-	/**
142
-	 * @var string 	The type of originating object ('commande', 'facture', ...)
143
-	 * @see fetch_origin()
144
-	 */
145
-	public $origin;
146
-
147
-	/**
148
-	 * @var int 	The id of originating object
149
-	 * @see fetch_origin()
150
-	 */
151
-	public $origin_id;
152
-
153
-	/**
154
-	 * @var string The object's reference
155
-	 */
156
-	public $ref;
157
-
158
-	/**
159
-	 * @var string The object's previous reference
160
-	 */
161
-	public $ref_previous;
162
-
163
-	/**
164
-	 * @var string The object's next reference
165
-	 */
166
-	public $ref_next;
167
-
168
-	/**
169
-	 * @var string An external reference for the object
170
-	 */
171
-	public $ref_ext;
172
-
173
-	/**
174
-	 * @var int The object's status
175
-	 * @see setStatut()
176
-	 */
177
-	public $statut;
178
-
179
-	/**
180
-	 * @var string
181
-	 * @see getFullAddress()
182
-	 */
183
-	public $country;
184
-
185
-	/**
186
-	 * @var int
187
-	 * @see getFullAddress(), country
188
-	 */
189
-	public $country_id;
190
-
191
-	/**
192
-	 * @var string
193
-	 * @see getFullAddress(), isInEEC(), country
194
-	 */
195
-    public $country_code;
27
+    public $id;
196 28
 
197 29
     /**
198
-	 * @var string
199
-	 * @see getFullAddress()
200
-	 */
201
-	public $state;
30
+     * @var string 		Error string
31
+     * @see             errors
32
+     */
33
+    public $error;
202 34
 
203
-	/**
204
-	 * @var int
205
-	 * @see getFullAddress(), state
206
-	 */
207
-	public $state_id;
35
+    /**
36
+     * @var string[]	Array of error strings
37
+     */
38
+    public $errors=array();
208 39
 
209
-	/**
210
-	 * @var string
211
-	 * @see getFullAddress(), state
212
-	 */
213
-    public $state_code;
40
+    /**
41
+     * @var string ID to identify managed object
42
+     */
43
+    public $element;
214 44
 
215 45
     /**
216
-	 * @var string
217
-	 * @see getFullAddress(), region
218
-	 */
219
-	public $region;
46
+     * @var string Name of table without prefix where object is stored
47
+     */
48
+    public $table_element;
220 49
 
221
-	/**
222
-	 * @var string
223
-	 * @see getFullAddress(), region
224
-	 */
225
-    public $region_code;
50
+    /**
51
+     * @var int    Name of subtable line
52
+     */
53
+    public $table_element_line='';
226 54
 
227
-	/**
228
-	 * @var int
229
-	 * @see fetch_barcode()
230
-	 */
231
-	public $barcode_type;
232
-
233
-	/**
234
-	 * @var string
235
-	 * @see fetch_barcode(), barcode_type
236
-	 */
237
-	public $barcode_type_code;
238
-
239
-	/**
240
-	 * @var string
241
-	 * @see fetch_barcode(), barcode_type
242
-	 */
243
-	public $barcode_type_label;
244
-
245
-	/**
246
-	 * @var string
247
-	 * @see fetch_barcode(), barcode_type
248
-	 */
249
-	public $barcode_type_coder;
250
-
251
-	/**
252
-	 * @var int Payment method ID (cheque, cash, ...)
253
-	 * @see setPaymentMethods()
254
-	 */
255
-	public $mode_reglement_id;
256
-
257
-	/**
258
-	 * @var int Payment terms ID
259
-	 * @see setPaymentTerms()
260
-	 */
261
-	public $cond_reglement_id;
262
-
263
-	/**
264
-	 * @var int Payment terms ID
265
-	 * @deprecated Kept for compatibility
266
-	 * @see cond_reglement_id;
267
-	 */
268
-	public $cond_reglement;
269
-
270
-	/**
271
-	 * @var int Delivery address ID
272
-	 * @deprecated
273
-	 * @see setDeliveryAddress()
274
-	 */
275
-	public $fk_delivery_address;
276
-
277
-	/**
278
-	 * @var int Shipping method ID
279
-	 * @see setShippingMethod()
280
-	 */
281
-	public $shipping_method_id;
282
-
283
-	/**
284
-	 * @var string
285
-	 * @see SetDocModel()
286
-	 */
287
-	public $modelpdf;
288
-
289
-	/**
290
-	 * @var int Bank account ID
291
-	 * @see SetBankAccount()
292
-	 */
293
-	public $fk_account;
294
-
295
-	/**
296
-	 * @var string Public note
297
-	 * @see update_note()
298
-	 */
299
-	public $note_public;
300
-
301
-	/**
302
-	 * @var string Private note
303
-	 * @see update_note()
304
-	 */
305
-	public $note_private;
306
-
307
-	/**
308
-	 * @deprecated
309
-	 * @see note_public
310
-	 */
311
-	public $note;
312
-
313
-	/**
314
-	 * @var float Total amount before taxes
315
-	 * @see update_price()
316
-	 */
317
-	public $total_ht;
318
-
319
-	/**
320
-	 * @var float Total VAT amount
321
-	 * @see update_price()
322
-	 */
323
-	public $total_tva;
324
-
325
-	/**
326
-	 * @var float Total local tax 1 amount
327
-	 * @see update_price()
328
-	 */
329
-	public $total_localtax1;
330
-
331
-	/**
332
-	 * @var float Total local tax 2 amount
333
-	 * @see update_price()
334
-	 */
335
-	public $total_localtax2;
336
-
337
-	/**
338
-	 * @var float Total amount with taxes
339
-	 * @see update_price()
340
-	 */
341
-	public $total_ttc;
342
-
343
-	/**
344
-	 * @var CommonObjectLine[]
345
-	 */
346
-	public $lines;
347
-
348
-	/**
349
-	 * @var mixed		Contains comments
350
-	 * @see fetchComments()
351
-	 */
352
-	public $comments=array();
353
-
354
-	/**
355
-	 * @var int
356
-	 * @see setIncoterms()
357
-	 */
358
-	public $fk_incoterms;
359
-
360
-	/**
361
-	 * @var string
362
-	 * @see SetIncoterms()
363
-	 */
364
-	public $libelle_incoterms;
365
-
366
-	/**
367
-	 * @var string
368
-	 * @see display_incoterms()
369
-	 */
370
-	public $location_incoterms;
371
-
372
-	public $name;
373
-	public $lastname;
374
-	public $firstname;
375
-	public $civility_id;
376
-
377
-	// Dates
378
-	public $date_creation;			// Date creation
379
-	public $date_validation;		// Date validation
380
-	public $date_modification;		// Date last change (tms field)
381
-
382
-
383
-
384
-	// No constructor as it is an abstract class
385
-
386
-	/**
387
-	 * Check an object id/ref exists
388
-	 * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
389
-	 *
390
-	 *  @param	string	$element   	String of element ('product', 'facture', ...)
391
-	 *  @param	int		$id      	Id of object
392
-	 *  @param  string	$ref     	Ref of object to check
393
-	 *  @param	string	$ref_ext	Ref ext of object to check
394
-	 *  @return int     			<0 if KO, 0 if OK but not found, >0 if OK and exists
395
-	 */
396
-	static function isExistingObject($element, $id, $ref='', $ref_ext='')
397
-	{
398
-		global $db,$conf;
399
-
400
-		$sql = "SELECT rowid, ref, ref_ext";
401
-		$sql.= " FROM ".MAIN_DB_PREFIX.$element;
402
-		$sql.= " WHERE entity IN (".getEntity($element).")" ;
403
-
404
-		if ($id > 0) $sql.= " AND rowid = ".$db->escape($id);
405
-		else if ($ref) $sql.= " AND ref = '".$db->escape($ref)."'";
406
-		else if ($ref_ext) $sql.= " AND ref_ext = '".$db->escape($ref_ext)."'";
407
-		else {
408
-			$error='ErrorWrongParameters';
409
-			dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
410
-			return -1;
411
-		}
412
-		if ($ref || $ref_ext) $sql.= " AND entity = ".$conf->entity;
413
-
414
-		dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
415
-		$resql = $db->query($sql);
416
-		if ($resql)
417
-		{
418
-			$num=$db->num_rows($resql);
419
-			if ($num > 0) return 1;
420
-			else return 0;
421
-		}
422
-		return -1;
423
-	}
424
-
425
-	/**
426
-	 * Method to output saved errors
427
-	 *
428
-	 * @return	string		String with errors
429
-	 */
430
-	function errorsToString()
431
-	{
432
-		return $this->error.(is_array($this->errors)?(($this->error!=''?', ':'').join(', ',$this->errors)):'');
433
-	}
434
-
435
-	/**
436
-	 *	Return full name (civility+' '+name+' '+lastname)
437
-	 *
438
-	 *	@param	Translate	$langs			Language object for translation of civility (used only if option is 1)
439
-	 *	@param	int			$option			0=No option, 1=Add civility
440
-	 * 	@param	int			$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
441
-	 * 	@param	int			$maxlen			Maximum length
442
-	 * 	@return	string						String with full name
443
-	 */
444
-	function getFullName($langs,$option=0,$nameorder=-1,$maxlen=0)
445
-	{
446
-		//print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
447
-		$lastname=$this->lastname;
448
-		$firstname=$this->firstname;
449
-		if (empty($lastname))  $lastname=(isset($this->lastname)?$this->lastname:(isset($this->name)?$this->name:(isset($this->nom)?$this->nom:(isset($this->societe)?$this->societe:(isset($this->company)?$this->company:'')))));
450
-
451
-		$ret='';
452
-		if ($option && $this->civility_id)
453
-		{
454
-			if ($langs->transnoentitiesnoconv("Civility".$this->civility_id)!="Civility".$this->civility_id) $ret.=$langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
455
-			else $ret.=$this->civility_id.' ';
456
-		}
457
-
458
-		$ret .= DolUtils::dolGetFirstLastname($firstname, $lastname, $nameorder);
55
+    /**
56
+     * @var string		Key value used to track if data is coming from import wizard
57
+     */
58
+    public $import_key;
459 59
 
460
-        return DolUtils::dol_trunc($ret, $maxlen);
461
-    }
60
+    /**
61
+     * @var mixed		Contains data to manage extrafields
62
+     */
63
+    public $array_options=array();
462 64
 
463
-	/**
464
-	 * 	Return full address of contact
465
-	 *
466
-	 * 	@param		int			$withcountry		1=Add country into address string
467
-	 *  @param		string		$sep				Separator to use to build string
468
-	 *  @param		int		    $withregion			1=Add region into address string
469
-	 *	@return		string							Full address string
470
-	 */
471
-	function getFullAddress($withcountry=0, $sep="\n", $withregion=0)
472
-	{
473
-		if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
474
-		{
475
-			require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
476
-			$tmparray=getCountry($this->country_id,'all');
477
-			$this->country_code=$tmparray['code'];
478
-			$this->country     =$tmparray['label'];
479
-		}
65
+    /**
66
+     * @var int[][]		Array of linked objects ids. Loaded by ->fetchObjectLinked
67
+     */
68
+    public $linkedObjectsIds;
480 69
 
481
-        if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_cpde)))
482
-    	{
483
-    		require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
484
-    		$tmparray=getState($this->state_id,'all',0,1);
485
-			$this->state_code   =$tmparray['code'];
486
-			$this->state        =$tmparray['label'];
487
-			$this->region_code  =$tmparray['region_code'];
488
-			$this->region       =$tmparray['region'];
489
-        }
490
-
491
-		return dol_format_address($this, $withcountry, $sep);
492
-	}
493
-
494
-
495
-	/**
496
-	 * 	Return full address for banner
497
-	 *
498
-	 * 	@param		string		$htmlkey            HTML id to make banner content unique
499
-	 *  @param      Object      $object				Object (thirdparty, thirdparty of contact for contact, null for a member)
500
-	 *	@return		string							Full address string
501
-	 */
502
-	function getBannerAddress($htmlkey, $object)
503
-	{
504
-		global $conf, $langs;
505
-
506
-		$countriesusingstate=array('AU','US','IN','GB','ES','UK','TR');    // See also option MAIN_FORCE_STATE_INTO_ADDRESS
507
-
508
-		$contactid=0;
509
-		$thirdpartyid=0;
510
-		if ($this->element == 'societe')
511
-		{
512
-			$thirdpartyid=$this->id;
513
-		}
514
-		if ($this->element == 'contact')
515
-		{
516
-			$contactid=$this->id;
517
-			$thirdpartyid=$object->fk_soc;
518
-		}
519
-		if ($this->element == 'user')
520
-		{
521
-			$contactid=$this->contact_id;
522
-			$thirdpartyid=$object->fk_soc;
523
-		}
524
-
525
-		$out='<!-- BEGIN part to show address block -->';
526
-
527
-		$outdone=0;
528
-		$coords = $this->getFullAddress(1,', ',$conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT);
529
-		if ($coords)
530
-		{
531
-			if (! empty($conf->use_javascript_ajax))
532
-			{
533
-				$namecoords = $this->getFullName($langs,1).'<br>'.$coords;
534
-				// hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
535
-				$out.='<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
536
-				$out.=img_picto($langs->trans("Address"), 'object_address.png');
537
-				$out.='</a> ';
538
-			}
539
-			$out.=dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
540
-			$outdone++;
541
-		}
542
-
543
-		if (! in_array($this->country_code,$countriesusingstate) && empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)   // If MAIN_FORCE_STATE_INTO_ADDRESS is on, state is already returned previously with getFullAddress
544
-				&& empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
545
-		{
546
-            if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
547
-                $out.=($outdone?' - ':'').$this->region.' - '.$this->state;
548
-            }
549
-            else {
550
-                $out.=($outdone?' - ':'').$this->state;
551
-            }
552
-			$outdone++;
553
-		}
554
-
555
-		if (! empty($this->phone) || ! empty($this->phone_pro) || ! empty($this->phone_mobile) || ! empty($this->phone_perso) || ! empty($this->fax) || ! empty($this->office_phone) || ! empty($this->user_mobile) || ! empty($this->office_fax)) $out.=($outdone?'<br>':'');
556
-		if (! empty($this->phone) && empty($this->phone_pro)) {		// For objects that store pro phone into ->phone
557
-			$out.=dol_print_phone($this->phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
558
-		}
559
-		if (! empty($this->phone_pro)) {
560
-			$out.=dol_print_phone($this->phone_pro,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
561
-		}
562
-		if (! empty($this->phone_mobile)) {
563
-			$out.=dol_print_phone($this->phone_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
564
-		}
565
-		if (! empty($this->phone_perso)) {
566
-			$out.=dol_print_phone($this->phone_perso,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePerso")); $outdone++;
567
-		}
568
-		if (! empty($this->office_phone)) {
569
-			$out.=dol_print_phone($this->office_phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
570
-		}
571
-		if (! empty($this->user_mobile)) {
572
-			$out.=dol_print_phone($this->user_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
573
-		}
574
-		if (! empty($this->fax)) {
575
-			$out.=dol_print_phone($this->fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
576
-		}
577
-		if (! empty($this->office_fax)) {
578
-			$out.=dol_print_phone($this->office_fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
579
-		}
580
-
581
-		$out.='<div style="clear: both;"></div>';
582
-		$outdone=0;
583
-		if (! empty($this->email))
584
-		{
585
-			$out.=dol_print_email($this->email,$this->id,$object->id,'AC_EMAIL',0,0,1);
586
-			$outdone++;
587
-		}
588
-		if (! empty($this->url))
589
-		{
590
-			$out.=dol_print_url($this->url,'_goout',0,1);
591
-			$outdone++;
592
-		}
593
-		$out.='<div style="clear: both;">';
594
-		if (! empty($conf->socialnetworks->enabled))
595
-		{
596
-			if ($this->skype) $out.=dol_print_socialnetworks($this->skype,$this->id,$object->id,'skype');
597
-			$outdone++;
598
-			if ($this->jabberid) $out.=dol_print_socialnetworks($this->jabberid,$this->id,$object->id,'jabber');
599
-			$outdone++;
600
-			if ($this->twitter) $out.=dol_print_socialnetworks($this->twitter,$this->id,$object->id,'twitter');
601
-			$outdone++;
602
-			if ($this->facebook) $out.=dol_print_socialnetworks($this->facebook,$this->id,$object->id,'facebook');
603
-			$outdone++;
604
-		}
605
-		$out.='</div>';
606
-
607
-		$out.='<!-- END Part to show address block -->';
608
-
609
-		return $out;
610
-	}
611
-
612
-	/**
613
-	 * Return the link of last main doc file for direct public download.
614
-	 *
615
-	 * @param	string	$modulepart			Module related to document
616
-	 * @param	int		$initsharekey		Init the share key if it was not yet defined
617
-	 * @param	int		$relativelink		0=Return full external link, 1=Return link relative to root of file
618
-	 * @return	string						Link or empty string if there is no download link
619
-	 */
620
-	function getLastMainDocLink($modulepart, $initsharekey=0, $relativelink=0)
621
-	{
622
-		global $user, $dolibarr_main_url_root;
623
-
624
-		if (empty($this->last_main_doc))
625
-		{
626
-			return '';		// No way to known which document name to use
627
-		}
628
-
629
-		include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
630
-		$ecmfile=new EcmFiles($this->db);
631
-		$result = $ecmfile->fetch(0, '', $this->last_main_doc);
632
-		if ($result < 0)
633
-		{
634
-			$this->error = $ecmfile->error;
635
-			$this->errors = $ecmfile->errors;
636
-			return -1;
637
-		}
638
-
639
-		if (empty($ecmfile->id))
640
-		{
641
-			// Add entry into index
642
-			if ($initsharekey)
643
-			{
644
-				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
645
-				// TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first
646
-				/*
647
-				$ecmfile->filepath = $rel_dir;
648
-				$ecmfile->filename = $filename;
649
-				$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
650
-				$ecmfile->fullpath_orig = '';
651
-				$ecmfile->gen_or_uploaded = 'generated';
652
-				$ecmfile->description = '';    // indexed content
653
-				$ecmfile->keyword = '';        // keyword content
654
-				$ecmfile->share = getRandomPassword(true);
655
-				$result = $ecmfile->create($user);
656
-				if ($result < 0)
657
-				{
658
-					$this->error = $ecmfile->error;
659
-					$this->errors = $ecmfile->errors;
660
-				}
661
-				*/
662
-			}
663
-			else return '';
664
-		}
665
-		elseif (empty($ecmfile->share))
666
-		{
667
-			// Add entry into index
668
-			if ($initsharekey)
669
-			{
670
-				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
671
-				$ecmfile->share = getRandomPassword(true);
672
-				$ecmfile->update($user);
673
-			}
674
-			else return '';
675
-		}
676
-
677
-		// Define $urlwithroot
678
-		$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
679
-		$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
680
-		//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
681
-
682
-		$forcedownload=0;
683
-
684
-		$paramlink='';
685
-		//if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart;		// For sharing with hash (so public files), modulepart is not required.
686
-		//if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; 					// For sharing with hash (so public files), entity is not required.
687
-		//$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath);								// No need of name of file for public link, we will use the hash
688
-		if (! empty($ecmfile->share)) $paramlink.=($paramlink?'&':'').'hashp='.$ecmfile->share;			// Hash for public share
689
-		if ($forcedownload) $paramlink.=($paramlink?'&':'').'attachment=1';
690
-
691
-		if ($relativelink)
692
-		{
693
-			$linktoreturn='document.php'.($paramlink?'?'.$paramlink:'');
694
-		}
695
-		else
696
-		{
697
-			$linktoreturn=$urlwithroot.'/document.php'.($paramlink?'?'.$paramlink:'');
698
-		}
699
-
700
-		// Here $ecmfile->share is defined
701
-		return $linktoreturn;
702
-	}
70
+    /**
71
+     * @var mixed		Array of linked objects. Loaded by ->fetchObjectLinked
72
+     */
73
+    public $linkedObjects;
703 74
 
75
+    /**
76
+     * @var Object      To store a cloned copy of object before to edit it and keep track of old properties
77
+     */
78
+    public $oldcopy;
704 79
 
705
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
706
-	/**
707
-	 *  Add a link between element $this->element and a contact
708
-	 *
709
-	 *  @param	int		$fk_socpeople       Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
710
-	 *  @param 	int		$type_contact 		Type of contact (code or id). Must be id or code found into table llx_c_type_contact. For example: SALESREPFOLL
711
-	 *  @param  string	$source             external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
712
-	 *  @param  int		$notrigger			Disable all triggers
713
-	 *  @return int                 		<0 if KO, >0 if OK
714
-	 */
715
-	function add_contact($fk_socpeople, $type_contact, $source='external',$notrigger=0)
716
-	{
717
-        // phpcs:enable
718
-		global $user,$langs;
719
-
720
-
721
-		dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
722
-
723
-		// Check parameters
724
-		if ($fk_socpeople <= 0)
725
-		{
726
-			$langs->load("errors");
727
-			$this->error=$langs->trans("ErrorWrongValueForParameterX","1");
728
-			dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
729
-			return -1;
730
-		}
731
-		if (! $type_contact)
732
-		{
733
-			$langs->load("errors");
734
-			$this->error=$langs->trans("ErrorWrongValueForParameterX","2");
735
-			dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
736
-			return -2;
737
-		}
738
-
739
-		$id_type_contact=0;
740
-		if (is_numeric($type_contact))
741
-		{
742
-			$id_type_contact=$type_contact;
743
-		}
744
-		else
745
-		{
746
-			// We look for id type_contact
747
-			$sql = "SELECT tc.rowid";
748
-			$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
749
-			$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
750
-			$sql.= " AND tc.source='".$this->db->escape($source)."'";
751
-			$sql.= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
752
-			//print $sql;
753
-			$resql=$this->db->query($sql);
754
-			if ($resql)
755
-			{
756
-				$obj = $this->db->fetch_object($resql);
757
-				if ($obj) $id_type_contact=$obj->rowid;
758
-			}
759
-		}
760
-
761
-		if ($id_type_contact == 0)
762
-		{
763
-			$this->error='CODE_NOT_VALID_FOR_THIS_ELEMENT';
764
-			dol_syslog("CODE_NOT_VALID_FOR_THIS_ELEMENT: Code type of contact '".$type_contact."' does not exists or is not active for element ".$this->element.", we can ignore it");
765
-			return -3;
766
-		}
767
-
768
-		$datecreate = dol_now();
769
-
770
-		// Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
771
-		$TListeContacts=$this->liste_contact(-1, $source);
772
-		$already_added=false;
773
-		if(!empty($TListeContacts)) {
774
-			foreach($TListeContacts as $array_contact) {
775
-				if($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
776
-					$already_added=true;
777
-					break;
778
-				}
779
-			}
780
-		}
80
+    /**
81
+     * @var string		Column name of the ref field.
82
+     */
83
+    protected $table_ref_field = '';
781 84
 
782
-		if(!$already_added) {
783 85
 
784
-			$this->db->begin();
785 86
 
786
-			// Insert into database
787
-			$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
788
-			$sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
789
-			$sql.= " VALUES (".$this->id.", ".$fk_socpeople." , " ;
790
-			$sql.= "'".$this->db->idate($datecreate)."'";
791
-			$sql.= ", 4, ". $id_type_contact;
792
-			$sql.= ")";
87
+    // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
793 88
 
794
-			$resql=$this->db->query($sql);
795
-			if ($resql)
796
-			{
797
-				if (! $notrigger)
798
-				{
799
-					$result=$this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
800
-					if ($result < 0)
801
-					{
802
-						$this->db->rollback();
803
-						return -1;
804
-					}
805
-				}
89
+    /**
90
+     * @var array<string,mixed>		Can be used to pass information when only object is provided to method
91
+     */
92
+    public $context=array();
806 93
 
807
-				$this->db->commit();
808
-				return 1;
809
-			}
810
-			else
811
-			{
812
-				if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
813
-				{
814
-					$this->error=$this->db->errno();
815
-					$this->db->rollback();
816
-					echo 'err rollback';
817
-					return -2;
818
-				}
819
-				else
820
-				{
821
-					$this->error=$this->db->error();
822
-					$this->db->rollback();
823
-					return -1;
824
-				}
825
-			}
826
-		} else return 0;
827
-	}
94
+    /**
95
+     * @var string		Contains canvas name if record is an alternative canvas record
96
+     */
97
+    public $canvas;
828 98
 
829
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
830
-	/**
831
-	 *    Copy contact from one element to current
832
-	 *
833
-	 *    @param    CommonObject    $objFrom    Source element
834
-	 *    @param    string          $source     Nature of contact ('internal' or 'external')
835
-	 *    @return   int                         >0 if OK, <0 if KO
836
-	 */
837
-	function copy_linked_contact($objFrom, $source='internal')
838
-	{
839
-        // phpcs:enable
840
-		$contacts = $objFrom->liste_contact(-1, $source);
841
-		foreach($contacts as $contact)
842
-		{
843
-			if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
844
-			{
845
-				$this->error=$this->db->lasterror();
846
-				return -1;
847
-			}
848
-		}
849
-		return 1;
850
-	}
99
+    /**
100
+     * @var Project The related project
101
+     * @see fetch_projet()
102
+     */
103
+    public $project;
851 104
 
852
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
853
-	/**
854
-	 *      Update a link to contact line
855
-	 *
856
-	 *      @param	int		$rowid              Id of line contact-element
857
-	 * 		@param	int		$statut	            New status of link
858
-	 *      @param  int		$type_contact_id    Id of contact type (not modified if 0)
859
-	 *      @param  int		$fk_socpeople	    Id of soc_people to update (not modified if 0)
860
-	 *      @return int                 		<0 if KO, >= 0 if OK
861
-	 */
862
-	function update_contact($rowid, $statut, $type_contact_id=0, $fk_socpeople=0)
863
-	{
864
-        // phpcs:enable
865
-		// Insert into database
866
-		$sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
867
-		$sql.= " statut = ".$statut;
868
-		if ($type_contact_id) $sql.= ", fk_c_type_contact = '".$type_contact_id ."'";
869
-		if ($fk_socpeople) $sql.= ", fk_socpeople = '".$fk_socpeople ."'";
870
-		$sql.= " where rowid = ".$rowid;
871
-		$resql=$this->db->query($sql);
872
-		if ($resql)
873
-		{
874
-			return 0;
875
-		}
876
-		else
877
-		{
878
-			$this->error=$this->db->lasterror();
879
-			return -1;
880
-		}
881
-	}
105
+    /**
106
+     * @var int The related project ID
107
+     * @see setProject(), project
108
+     */
109
+    public $fk_project;
882 110
 
883
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
884
-	/**
885
-	 *    Delete a link to contact line
886
-	 *
887
-	 *    @param	int		$rowid			Id of contact link line to delete
888
-	 *    @param	int		$notrigger		Disable all triggers
889
-	 *    @return   int						>0 if OK, <0 if KO
890
-	 */
891
-	function delete_contact($rowid, $notrigger=0)
892
-	{
893
-        // phpcs:enable
894
-		global $user;
111
+    /**
112
+     * @deprecated
113
+     * @see project
114
+     */
115
+    public $projet;
895 116
 
117
+    /**
118
+     * @var Contact a related contact
119
+     * @see fetch_contact()
120
+     */
121
+    public $contact;
896 122
 
897
-		$this->db->begin();
123
+    /**
124
+     * @var int The related contact ID
125
+     * @see fetch_contact()
126
+     */
127
+    public $contact_id;
898 128
 
899
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
900
-		$sql.= " WHERE rowid =".$rowid;
129
+    /**
130
+     * @var Societe A related thirdparty
131
+     * @see fetch_thirdparty()
132
+     */
133
+    public $thirdparty;
901 134
 
902
-		dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
903
-		if ($this->db->query($sql))
904
-		{
905
-			if (! $notrigger)
906
-			{
907
-				$result=$this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
908
-				if ($result < 0) { $this->db->rollback(); return -1; }
909
-			}
910
-
911
-			$this->db->commit();
912
-			return 1;
913
-		}
914
-		else
915
-		{
916
-			$this->error=$this->db->lasterror();
917
-			$this->db->rollback();
918
-			return -1;
919
-		}
920
-	}
135
+    /**
136
+     * @var User A related user
137
+     * @see fetch_user()
138
+     */
139
+    public $user;
921 140
 
922
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
923
-	/**
924
-	 *    Delete all links between an object $this and all its contacts
925
-	 *
926
-	 *	  @param	string	$source		'' or 'internal' or 'external'
927
-	 *	  @param	string	$code		Type of contact (code or id)
928
-	 *    @return   int					>0 if OK, <0 if KO
929
-	 */
930
-	function delete_linked_contact($source='',$code='')
931
-	{
932
-        // phpcs:enable
933
-		$temp = array();
934
-		$typeContact = $this->liste_type_contact($source,'',0,0,$code);
935
-
936
-		foreach($typeContact as $key => $value)
937
-		{
938
-			array_push($temp,$key);
939
-		}
940
-		$listId = implode(",", $temp);
941
-
942
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
943
-		$sql.= " WHERE element_id = ".$this->id;
944
-		if ($listId)
945
-			$sql.= " AND fk_c_type_contact IN (".$listId.")";
946
-
947
-		dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
948
-		if ($this->db->query($sql))
949
-		{
950
-			return 1;
951
-		}
952
-		else
953
-		{
954
-			$this->error=$this->db->lasterror();
955
-			return -1;
956
-		}
957
-	}
141
+    /**
142
+     * @var string 	The type of originating object ('commande', 'facture', ...)
143
+     * @see fetch_origin()
144
+     */
145
+    public $origin;
958 146
 
959
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
960
-	/**
961
-	 *    Get array of all contacts for an object
962
-	 *
963
-	 *    @param	int			$statut		Status of links to get (-1=all)
964
-	 *    @param	string		$source		Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
965
-	 *    @param	int         $list       0:Return array contains all properties, 1:Return array contains just id
966
-	 *    @param    string      $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
967
-	 *    @return	array|int		        Array of contacts, -1 if error
968
-	 */
969
-	function liste_contact($statut=-1,$source='external',$list=0,$code='')
970
-	{
971
-        // phpcs:enable
972
-		global $langs;
973
-
974
-		$tab=array();
975
-
976
-		$sql = "SELECT ec.rowid, ec.statut as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact";    // This field contains id of llx_socpeople or id of llx_user
977
-		if ($source == 'internal') $sql.=", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
978
-		if ($source == 'external' || $source == 'thirdparty') $sql.=", t.fk_soc as socid, t.statut as statuscontact";
979
-		$sql.= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
980
-		$sql.= ", tc.source, tc.element, tc.code, tc.libelle";
981
-		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
982
-		$sql.= ", ".MAIN_DB_PREFIX."element_contact ec";
983
-		if ($source == 'internal') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
984
-		if ($source == 'external'|| $source == 'thirdparty') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
985
-		$sql.= " WHERE ec.element_id =".$this->id;
986
-		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
987
-		$sql.= " AND tc.element='".$this->db->escape($this->element)."'";
988
-		if ($code) $sql.= " AND tc.code = '".$this->db->escape($code)."'";
989
-		if ($source == 'internal') $sql.= " AND tc.source = 'internal'";
990
-		if ($source == 'external' || $source == 'thirdparty') $sql.= " AND tc.source = 'external'";
991
-		$sql.= " AND tc.active=1";
992
-		if ($statut >= 0) $sql.= " AND ec.statut = '".$statut."'";
993
-		$sql.=" ORDER BY t.lastname ASC";
994
-
995
-		dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
996
-		$resql=$this->db->query($sql);
997
-		if ($resql)
998
-		{
999
-			$num=$this->db->num_rows($resql);
1000
-			$i=0;
1001
-			while ($i < $num)
1002
-			{
1003
-				$obj = $this->db->fetch_object($resql);
147
+    /**
148
+     * @var int 	The id of originating object
149
+     * @see fetch_origin()
150
+     */
151
+    public $origin_id;
1004 152
 
1005
-				if (! $list)
1006
-				{
1007
-					$transkey="TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1008
-					$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1009
-					$tab[$i]=array('source'=>$obj->source,'socid'=>$obj->socid,'id'=>$obj->id,
1010
-								   'nom'=>$obj->lastname,      // For backward compatibility
1011
-								   'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
1012
-								   'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
1013
-				}
1014
-				else
1015
-				{
1016
-					$tab[$i]=$obj->id;
1017
-				}
153
+    /**
154
+     * @var string The object's reference
155
+     */
156
+    public $ref;
1018 157
 
1019
-				$i++;
1020
-			}
1021
-
1022
-			return $tab;
1023
-		}
1024
-		else
1025
-		{
1026
-			$this->error=$this->db->lasterror();
1027
-			dol_print_error($this->db);
1028
-			return -1;
1029
-		}
1030
-	}
1031
-
1032
-
1033
-	/**
1034
-	 * 		Update status of a contact linked to object
1035
-	 *
1036
-	 * 		@param	int		$rowid		Id of link between object and contact
1037
-	 * 		@return	int					<0 if KO, >=0 if OK
1038
-	 */
1039
-	function swapContactStatus($rowid)
1040
-	{
1041
-		$sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1042
-		$sql.= " tc.code, tc.libelle";
1043
-		//$sql.= ", s.fk_soc";
1044
-		$sql.= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
1045
-		//$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as s ON ec.fk_socpeople=s.rowid";	// Si contact de type external, alors il est lie a une societe
1046
-		$sql.= " WHERE ec.rowid =".$rowid;
1047
-		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
1048
-		$sql.= " AND tc.element = '".$this->db->escape($this->element)."'";
1049
-
1050
-		dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1051
-		$resql=$this->db->query($sql);
1052
-		if ($resql)
1053
-		{
1054
-			$obj = $this->db->fetch_object($resql);
1055
-			$newstatut = ($obj->statut == 4) ? 5 : 4;
1056
-			$result = $this->update_contact($rowid, $newstatut);
1057
-			$this->db->free($resql);
1058
-			return $result;
1059
-		}
1060
-		else
1061
-		{
1062
-			$this->error=$this->db->error();
1063
-			dol_print_error($this->db);
1064
-			return -1;
1065
-		}
1066
-	}
158
+    /**
159
+     * @var string The object's previous reference
160
+     */
161
+    public $ref_previous;
1067 162
 
1068
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1069
-	/**
1070
-	 *      Return array with list of possible values for type of contacts
1071
-	 *
1072
-	 *      @param	string	$source     'internal', 'external' or 'all'
1073
-	 *      @param	string	$order		Sort order by : 'position', 'code', 'rowid'...
1074
-	 *      @param  int		$option     0=Return array id->label, 1=Return array code->label
1075
-	 *      @param  int		$activeonly 0=all status of contact, 1=only the active
1076
-	 *		@param	string	$code		Type of contact (Example: 'CUSTOMER', 'SERVICE')
1077
-	 *      @return array       		Array list of type of contacts (id->label if option=0, code->label if option=1)
1078
-	 */
1079
-	function liste_type_contact($source='internal', $order='position', $option=0, $activeonly=0, $code='')
1080
-	{
1081
-        // phpcs:enable
1082
-		global $langs;
1083
-
1084
-		if (empty($order)) $order='position';
1085
-		if ($order == 'position') $order.=',code';
1086
-
1087
-		$tab = array();
1088
-		$sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1089
-		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1090
-		$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
1091
-		if ($activeonly == 1) $sql.= " AND tc.active=1"; // only the active types
1092
-		if (! empty($source) && $source != 'all') $sql.= " AND tc.source='".$this->db->escape($source)."'";
1093
-		if (! empty($code)) $sql.= " AND tc.code='".$this->db->escape($code)."'";
1094
-		$sql.= $this->db->order($order,'ASC');
1095
-
1096
-		//print "sql=".$sql;
1097
-		$resql=$this->db->query($sql);
1098
-		if ($resql)
1099
-		{
1100
-			$num=$this->db->num_rows($resql);
1101
-			$i=0;
1102
-			while ($i < $num)
1103
-			{
1104
-				$obj = $this->db->fetch_object($resql);
1105
-
1106
-				$transkey="TypeContact_".$this->element."_".$source."_".$obj->code;
1107
-				$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1108
-				if (empty($option)) $tab[$obj->rowid]=$libelle_type;
1109
-				else $tab[$obj->code]=$libelle_type;
1110
-				$i++;
1111
-			}
1112
-			return $tab;
1113
-		}
1114
-		else
1115
-		{
1116
-			$this->error=$this->db->lasterror();
1117
-			//dol_print_error($this->db);
1118
-			return null;
1119
-		}
1120
-	}
1121
-
1122
-	/**
1123
-	 *      Return id of contacts for a source and a contact code.
1124
-	 *      Example: contact client de facturation ('external', 'BILLING')
1125
-	 *      Example: contact client de livraison ('external', 'SHIPPING')
1126
-	 *      Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
1127
-	 *
1128
-	 *		@param	string	$source		'external' or 'internal'
1129
-	 *		@param	string	$code		'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
1130
-	 *		@param	int		$status		limited to a certain status
1131
-	 *      @return array       		List of id for such contacts
1132
-	 */
1133
-	function getIdContact($source,$code,$status=0)
1134
-	{
1135
-		global $conf;
1136
-
1137
-		$result=array();
1138
-		$i=0;
1139
-		//cas particulier pour les expeditions
1140
-		if($this->element=='shipping' && $this->origin_id != 0) {
1141
-			$id=$this->origin_id;
1142
-			$element='commande';
1143
-        } else if($this->element=='reception' && $this->origin_id != 0) {
1144
-            $id=$this->origin_id;
1145
-            $element='order_supplier';
1146
-		} else {
1147
-			$id=$this->id;
1148
-			$element=$this->element;
1149
-		}
1150
-
1151
-		$sql = "SELECT ec.fk_socpeople";
1152
-		$sql.= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
1153
-		if ($source == 'internal') $sql.= " ".MAIN_DB_PREFIX."user as c,";
1154
-		if ($source == 'external') $sql.= " ".MAIN_DB_PREFIX."socpeople as c,";
1155
-		$sql.= " ".MAIN_DB_PREFIX."c_type_contact as tc";
1156
-		$sql.= " WHERE ec.element_id = ".$id;
1157
-		$sql.= " AND ec.fk_socpeople = c.rowid";
1158
-		if ($source == 'internal') $sql.= " AND c.entity IN (".getEntity('user').")";
1159
-		if ($source == 'external') $sql.= " AND c.entity IN (".getEntity('societe').")";
1160
-		$sql.= " AND ec.fk_c_type_contact = tc.rowid";
1161
-		$sql.= " AND tc.element = '".$element."'";
1162
-		$sql.= " AND tc.source = '".$source."'";
1163
-		$sql.= " AND tc.code = '".$code."'";
1164
-		$sql.= " AND tc.active = 1";
1165
-		if ($status) $sql.= " AND ec.statut = ".$status;
1166
-
1167
-		dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1168
-		$resql=$this->db->query($sql);
1169
-		if ($resql)
1170
-		{
1171
-			while ($obj = $this->db->fetch_object($resql))
1172
-			{
1173
-				$result[$i]=$obj->fk_socpeople;
1174
-				$i++;
1175
-			}
1176
-		}
1177
-		else
1178
-		{
1179
-			$this->error=$this->db->error();
1180
-			return null;
1181
-		}
1182
-
1183
-		return $result;
1184
-	}
163
+    /**
164
+     * @var string The object's next reference
165
+     */
166
+    public $ref_next;
1185 167
 
1186
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1187
-	/**
1188
-	 *		Load object contact with id=$this->contactid into $this->contact
1189
-	 *
1190
-	 *		@param	int		$contactid      Id du contact. Use this->contactid if empty.
1191
-	 *		@return	int						<0 if KO, >0 if OK
1192
-	 */
1193
-	function fetch_contact($contactid=null)
1194
-	{
1195
-        // phpcs:enable
1196
-		if (empty($contactid)) $contactid=$this->contactid;
168
+    /**
169
+     * @var string An external reference for the object
170
+     */
171
+    public $ref_ext;
1197 172
 
1198
-		if (empty($contactid)) return 0;
173
+    /**
174
+     * @var int The object's status
175
+     * @see setStatut()
176
+     */
177
+    public $statut;
1199 178
 
1200
-		require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1201
-		$contact = new Contact($this->db);
1202
-		$result=$contact->fetch($contactid);
1203
-		$this->contact = $contact;
1204
-		return $result;
1205
-	}
179
+    /**
180
+     * @var string
181
+     * @see getFullAddress()
182
+     */
183
+    public $country;
1206 184
 
1207
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1208
-	/**
1209
-	 *    	Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
1210
-	 *
1211
-	 *		@param		int		$force_thirdparty_id	Force thirdparty id
1212
-	 *		@return		int								<0 if KO, >0 if OK
1213
-	 */
1214
-	function fetch_thirdparty($force_thirdparty_id=0)
1215
-	{
1216
-        // phpcs:enable
1217
-		global $conf;
185
+    /**
186
+     * @var int
187
+     * @see getFullAddress(), country
188
+     */
189
+    public $country_id;
1218 190
 
1219
-		if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
1220
-			return 0;
191
+    /**
192
+     * @var string
193
+     * @see getFullAddress(), isInEEC(), country
194
+     */
195
+    public $country_code;
1221 196
 
1222
-		require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
197
+    /**
198
+     * @var string
199
+     * @see getFullAddress()
200
+     */
201
+    public $state;
1223 202
 
1224
-		$idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1225
-		if ($force_thirdparty_id)
1226
-			$idtofetch = $force_thirdparty_id;
203
+    /**
204
+     * @var int
205
+     * @see getFullAddress(), state
206
+     */
207
+    public $state_id;
1227 208
 
1228
-		if ($idtofetch) {
1229
-			$thirdparty = new Societe($this->db);
1230
-			$result = $thirdparty->fetch($idtofetch);
1231
-			$this->thirdparty = $thirdparty;
209
+    /**
210
+     * @var string
211
+     * @see getFullAddress(), state
212
+     */
213
+    public $state_code;
1232 214
 
1233
-			// Use first price level if level not defined for third party
1234
-			if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1235
-				$this->thirdparty->price_level = 1;
1236
-			}
215
+    /**
216
+     * @var string
217
+     * @see getFullAddress(), region
218
+     */
219
+    public $region;
1237 220
 
1238
-			return $result;
1239
-		} else
1240
-			return -1;
1241
-	}
221
+    /**
222
+     * @var string
223
+     * @see getFullAddress(), region
224
+     */
225
+    public $region_code;
1242 226
 
227
+    /**
228
+     * @var int
229
+     * @see fetch_barcode()
230
+     */
231
+    public $barcode_type;
1243 232
 
1244
-	/**
1245
-	 * Looks for an object with ref matching the wildcard provided
1246
-	 * It does only work when $this->table_ref_field is set
1247
-	 *
1248
-	 * @param string $ref Wildcard
1249
-	 * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
1250
-	 */
1251
-	public function fetchOneLike($ref)
1252
-	{
1253
-		if (!$this->table_ref_field) {
1254
-			return 0;
1255
-		}
233
+    /**
234
+     * @var string
235
+     * @see fetch_barcode(), barcode_type
236
+     */
237
+    public $barcode_type_code;
1256 238
 
1257
-		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
239
+    /**
240
+     * @var string
241
+     * @see fetch_barcode(), barcode_type
242
+     */
243
+    public $barcode_type_label;
1258 244
 
1259
-		$query = $this->db->query($sql);
245
+    /**
246
+     * @var string
247
+     * @see fetch_barcode(), barcode_type
248
+     */
249
+    public $barcode_type_coder;
1260 250
 
1261
-		if (!$this->db->num_rows($query)) {
1262
-			return 0;
1263
-		}
251
+    /**
252
+     * @var int Payment method ID (cheque, cash, ...)
253
+     * @see setPaymentMethods()
254
+     */
255
+    public $mode_reglement_id;
1264 256
 
1265
-		$result = $this->db->fetch_object($query);
257
+    /**
258
+     * @var int Payment terms ID
259
+     * @see setPaymentTerms()
260
+     */
261
+    public $cond_reglement_id;
1266 262
 
1267
-		return $this->fetch($result->rowid);
1268
-	}
263
+    /**
264
+     * @var int Payment terms ID
265
+     * @deprecated Kept for compatibility
266
+     * @see cond_reglement_id;
267
+     */
268
+    public $cond_reglement;
1269 269
 
1270
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1271
-	/**
1272
-	 *	Load data for barcode into properties ->barcode_type*
1273
-	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1274
-	 *  if it is not defined, ->element must be defined to know default barcode type.
1275
-	 *
1276
-	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1277
-	 */
1278
-	function fetch_barcode()
1279
-	{
1280
-        // phpcs:enable
1281
-		global $conf;
270
+    /**
271
+     * @var int Delivery address ID
272
+     * @deprecated
273
+     * @see setDeliveryAddress()
274
+     */
275
+    public $fk_delivery_address;
1282 276
 
1283
-		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
277
+    /**
278
+     * @var int Shipping method ID
279
+     * @see setShippingMethod()
280
+     */
281
+    public $shipping_method_id;
1284 282
 
1285
-		$idtype=$this->barcode_type;
1286
-		if (empty($idtype) && $idtype != '0')	// If type of barcode no set, we try to guess. If set to '0' it means we forced to have type remain not defined
1287
-		{
1288
-			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1289
-			else if ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1290
-			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1291
-		}
283
+    /**
284
+     * @var string
285
+     * @see SetDocModel()
286
+     */
287
+    public $modelpdf;
1292 288
 
1293
-		if ($idtype > 0)
1294
-		{
1295
-			if (empty($this->barcode_type) || empty($this->barcode_type_code) || empty($this->barcode_type_label) || empty($this->barcode_type_coder))    // If data not already loaded
1296
-			{
1297
-				$sql = "SELECT rowid, code, libelle as label, coder";
1298
-				$sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1299
-				$sql.= " WHERE rowid = ".$idtype;
1300
-				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1301
-				$resql = $this->db->query($sql);
1302
-				if ($resql)
1303
-				{
1304
-					$obj = $this->db->fetch_object($resql);
1305
-					$this->barcode_type       = $obj->rowid;
1306
-					$this->barcode_type_code  = $obj->code;
1307
-					$this->barcode_type_label = $obj->label;
1308
-					$this->barcode_type_coder = $obj->coder;
1309
-					return 1;
1310
-				}
1311
-				else
1312
-				{
1313
-					dol_print_error($this->db);
1314
-					return -1;
1315
-				}
1316
-			}
1317
-		}
1318
-		return 0;
1319
-	}
289
+    /**
290
+     * @var int Bank account ID
291
+     * @see SetBankAccount()
292
+     */
293
+    public $fk_account;
1320 294
 
1321
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1322
-	/**
1323
-	 *		Load the project with id $this->fk_project into this->project
1324
-	 *
1325
-	 *		@return		int			<0 if KO, >=0 if OK
1326
-	 */
1327
-	function fetch_projet()
1328
-	{
1329
-        // phpcs:enable
1330
-		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
295
+    /**
296
+     * @var string Public note
297
+     * @see update_note()
298
+     */
299
+    public $note_public;
1331 300
 
1332
-		if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet;	// For backward compatibility
1333
-		if (empty($this->fk_project)) return 0;
301
+    /**
302
+     * @var string Private note
303
+     * @see update_note()
304
+     */
305
+    public $note_private;
1334 306
 
1335
-		$project = new Project($this->db);
1336
-		$result = $project->fetch($this->fk_project);
307
+    /**
308
+     * @deprecated
309
+     * @see note_public
310
+     */
311
+    public $note;
1337 312
 
1338
-		$this->projet = $project;	// deprecated
1339
-		$this->project = $project;
1340
-		return $result;
1341
-	}
313
+    /**
314
+     * @var float Total amount before taxes
315
+     * @see update_price()
316
+     */
317
+    public $total_ht;
1342 318
 
1343
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1344
-	/**
1345
-	 *		Load the product with id $this->fk_product into this->product
1346
-	 *
1347
-	 *		@return		int			<0 if KO, >=0 if OK
1348
-	 */
1349
-	function fetch_product()
1350
-	{
1351
-        // phpcs:enable
1352
-		include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
319
+    /**
320
+     * @var float Total VAT amount
321
+     * @see update_price()
322
+     */
323
+    public $total_tva;
1353 324
 
1354
-		if (empty($this->fk_product)) return 0;
325
+    /**
326
+     * @var float Total local tax 1 amount
327
+     * @see update_price()
328
+     */
329
+    public $total_localtax1;
1355 330
 
1356
-		$product = new Product($this->db);
1357
-		$result = $product->fetch($this->fk_product);
331
+    /**
332
+     * @var float Total local tax 2 amount
333
+     * @see update_price()
334
+     */
335
+    public $total_localtax2;
1358 336
 
1359
-		$this->product = $product;
1360
-		return $result;
1361
-	}
337
+    /**
338
+     * @var float Total amount with taxes
339
+     * @see update_price()
340
+     */
341
+    public $total_ttc;
1362 342
 
1363
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1364
-	/**
1365
-	 *		Load the user with id $userid into this->user
1366
-	 *
1367
-	 *		@param	int		$userid 		Id du contact
1368
-	 *		@return	int						<0 if KO, >0 if OK
1369
-	 */
1370
-	function fetch_user($userid)
1371
-	{
1372
-        // phpcs:enable
1373
-		$user = new User($this->db);
1374
-		$result=$user->fetch($userid);
1375
-		$this->user = $user;
1376
-		return $result;
1377
-	}
343
+    /**
344
+     * @var CommonObjectLine[]
345
+     */
346
+    public $lines;
1378 347
 
1379
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1380
-	/**
1381
-	 *	Read linked origin object
1382
-	 *
1383
-	 *	@return		void
1384
-	 */
1385
-	function fetch_origin()
1386
-	{
1387
-        // phpcs:enable
1388
-		if ($this->origin == 'shipping') $this->origin = 'expedition';
1389
-		if ($this->origin == 'delivery') $this->origin = 'livraison';
1390
-        if ($this->origin == 'order_supplier') $this->origin = 'commandeFournisseur';
348
+    /**
349
+     * @var mixed		Contains comments
350
+     * @see fetchComments()
351
+     */
352
+    public $comments=array();
1391 353
 
1392
-		$origin = $this->origin;
354
+    /**
355
+     * @var int
356
+     * @see setIncoterms()
357
+     */
358
+    public $fk_incoterms;
1393 359
 
1394
-		$classname = ucfirst($origin);
1395
-		$this->$origin = new $classname($this->db);
1396
-		$this->$origin->fetch($this->origin_id);
1397
-	}
360
+    /**
361
+     * @var string
362
+     * @see SetIncoterms()
363
+     */
364
+    public $libelle_incoterms;
1398 365
 
1399
-	/**
1400
-     *  Load object from specific field
1401
-     *
1402
-     *  @param	string	$table		Table element or element line
1403
-     *  @param	string	$field		Field selected
1404
-     *  @param	string	$key		Import key
1405
-     *  @param	string	$element	Element name
1406
-     *	@return	int					<0 if KO, >0 if OK
366
+    /**
367
+     * @var string
368
+     * @see display_incoterms()
1407 369
      */
1408
-	function fetchObjectFrom($table, $field, $key, $element = null)
1409
-	{
1410
-		global $conf;
1411
-
1412
-		$result=false;
1413
-
1414
-		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
1415
-		$sql.= " WHERE ".$field." = '".$key."'";
1416
-		if (! empty($element)) {
1417
-			$sql.= " AND entity IN (".getEntity($element).")";
1418
-		} else {
1419
-			$sql.= " AND entity = ".$conf->entity;
1420
-		}
1421
-
1422
-		dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1423
-		$resql = $this->db->query($sql);
1424
-		if ($resql)
1425
-		{
1426
-			$row = $this->db->fetch_row($resql);
1427
-			// Test for avoid error -1
1428
-			if ($row[0] > 0) {
1429
-				$result = $this->fetch($row[0]);
1430
-			}
1431
-		}
1432
-
1433
-		return $result;
1434
-	}
1435
-
1436
-	/**
1437
-	 *	Getter generic. Load value from a specific field
1438
-	 *
1439
-	 *	@param	string	$table		Table of element or element line
1440
-	 *	@param	int		$id			Element id
1441
-	 *	@param	string	$field		Field selected
1442
-	 *	@return	int					<0 if KO, >0 if OK
1443
-	 */
1444
-	function getValueFrom($table, $id, $field)
1445
-	{
1446
-		$result=false;
1447
-		if (!empty($id) && !empty($field) && !empty($table)) {
1448
-			$sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
1449
-			$sql.= " WHERE rowid = ".$id;
1450
-
1451
-			dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1452
-			$resql = $this->db->query($sql);
1453
-			if ($resql)
1454
-			{
1455
-				$row = $this->db->fetch_row($resql);
1456
-				$result = $row[0];
1457
-			}
1458
-		}
1459
-		return $result;
1460
-	}
1461
-
1462
-	/**
1463
-	 *	Setter generic. Update a specific field into database.
1464
-	 *  Warning: Trigger is run only if param trigkey is provided.
1465
-	 *
1466
-	 *	@param	string		$field			Field to update
1467
-	 *	@param	mixed		$value			New value
1468
-	 *	@param	string		$table			To force other table element or element line (should not be used)
1469
-	 *	@param	int			$id				To force other object id (should not be used)
1470
-	 *	@param	string		$format			Data format ('text', 'date'). 'text' is used if not defined
1471
-	 *	@param	string		$id_field		To force rowid field name. 'rowid' is used if not defined
1472
-	 *	@param	User|string	$fuser			Update the user of last update field with this user. If not provided, current user is used except if value is 'none'
1473
-	 *  @param  string      $trigkey    	Trigger key to run (in most cases something like 'XXX_MODIFY')
1474
-	 *  @param	string		$fk_user_field	Name of field to save user id making change
1475
-	 *	@return	int							<0 if KO, >0 if OK
1476
-	 *  @see updateExtraField
1477
-	 */
1478
-	function setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $fuser=null, $trigkey='', $fk_user_field='fk_user_modif')
1479
-	{
1480
-		global $user,$langs,$conf;
1481
-
1482
-		if (empty($table)) 	  $table=$this->table_element;
1483
-		if (empty($id))    	  $id=$this->id;
1484
-		if (empty($format))   $format='text';
1485
-		if (empty($id_field)) $id_field='rowid';
1486
-
1487
-		$error=0;
1488
-
1489
-		$this->db->begin();
1490
-
1491
-		// Special case
1492
-		if ($table == 'product' && $field == 'note_private') $field='note';
1493
-		if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) $fk_user_field = 'fk_user_mod';
1494
-
1495
-		$sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
1496
-
1497
-		if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'";
1498
-		else if ($format == 'int') $sql.= $field." = ".$this->db->escape($value);
1499
-		else if ($format == 'date') $sql.= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
1500
-
1501
-		if ($fk_user_field)
1502
-		{
1503
-			if (! empty($fuser) && is_object($fuser)) $sql.=", ".$fk_user_field." = ".$fuser->id;
1504
-			elseif (empty($fuser) || $fuser != 'none') $sql.=", ".$fk_user_field." = ".$user->id;
1505
-		}
1506
-
1507
-		$sql.= " WHERE ".$id_field." = ".$id;
1508
-
1509
-		dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG);
1510
-		$resql = $this->db->query($sql);
1511
-		if ($resql)
1512
-		{
1513
-			if ($trigkey)
1514
-			{
1515
-				// call trigger with updated object values
1516
-				if (empty($this->fields) && method_exists($this, 'fetch'))
1517
-				{
1518
-					$result = $this->fetch($id);
1519
-				}
1520
-				else
1521
-				{
1522
-					$result = $this->fetchCommon($id);
1523
-				}
1524
-				if ($result >= 0) $result=$this->call_trigger($trigkey, (! empty($fuser) && is_object($fuser)) ? $fuser : $user);   // This may set this->errors
1525
-				if ($result < 0) $error++;
1526
-			}
370
+    public $location_incoterms;
1527 371
 
1528
-			if (! $error)
1529
-			{
1530
-				if (property_exists($this, $field)) $this->$field = $value;
1531
-				$this->db->commit();
1532
-				return 1;
1533
-			}
1534
-			else
1535
-			{
1536
-				$this->db->rollback();
1537
-				return -2;
1538
-			}
1539
-		}
1540
-		else
1541
-		{
1542
-			$this->error=$this->db->lasterror();
1543
-			$this->db->rollback();
1544
-			return -1;
1545
-		}
1546
-	}
372
+    public $name;
373
+    public $lastname;
374
+    public $firstname;
375
+    public $civility_id;
1547 376
 
1548
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1549
-	/**
1550
-	 *      Load properties id_previous and id_next by comparing $fieldid with $this->ref
1551
-	 *
1552
-	 *      @param	string	$filter		Optional filter. Example: " AND (t.field1 = 'aa' OR t.field2 = 'bb')"
1553
-	 *	 	@param  string	$fieldid   	Name of field to use for the select MAX and MIN
1554
-	 *		@param	int		$nodbprefix	Do not include DB prefix to forge table name
1555
-	 *      @return int         		<0 if KO, >0 if OK
1556
-	 */
1557
-	function load_previous_next_ref($filter, $fieldid, $nodbprefix=0)
1558
-	{
1559
-        // phpcs:enable
1560
-		global $conf, $user;
1561
-
1562
-		if (! $this->table_element)
1563
-		{
1564
-			dol_print_error('',get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
1565
-			return -1;
1566
-		}
1567
-		if ($fieldid == 'none') return 1;
1568
-
1569
-		// Security on socid
1570
-		$socid = 0;
1571
-		if ($user->societe_id > 0) $socid = $user->societe_id;
1572
-
1573
-		// this->ismultientitymanaged contains
1574
-		// 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
1575
-		$alias = 's';
1576
-		if ($this->element == 'societe') $alias = 'te';
1577
-
1578
-		$sql = "SELECT MAX(te.".$fieldid.")";
1579
-		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1580
-		if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1581
-			$sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1582
-		}
1583
-		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1584
-		else if ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to socid
1585
-		else if ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid";	// If we need to link to societe to limit select to socid
1586
-		if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid)  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1587
-		$sql.= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1588
-		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1589
-		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1590
-		if (! empty($filter))
1591
-		{
1592
-			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1593
-			$sql.=$filter;
1594
-		}
1595
-		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1596
-		else if ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to socid
1597
-		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1598
-			if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1599
-				if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1600
-					$sql.= " AND te.entity IS NOT NULL"; // Show all users
1601
-				} else {
1602
-					$sql.= " AND ug.fk_user = te.rowid";
1603
-					$sql.= " AND ug.entity IN (".getEntity($this->element).")";
1604
-				}
1605
-			} else {
1606
-				$sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1607
-			}
1608
-		}
1609
-		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1610
-		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1611
-		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1612
-		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1613
-
1614
-		$result = $this->db->query($sql);
1615
-		if (! $result)
1616
-		{
1617
-			$this->error=$this->db->lasterror();
1618
-			return -1;
1619
-		}
1620
-		$row = $this->db->fetch_row($result);
1621
-		$this->ref_previous = $row[0];
1622
-
1623
-
1624
-		$sql = "SELECT MIN(te.".$fieldid.")";
1625
-		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1626
-		if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1627
-			$sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1628
-		}
1629
-		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1630
-		else if ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to socid
1631
-		else if ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid";	// If we need to link to societe to limit select to socid
1632
-		if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1633
-		$sql.= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1634
-		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1635
-		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1636
-		if (! empty($filter))
1637
-		{
1638
-			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1639
-			$sql.=$filter;
1640
-		}
1641
-		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1642
-		else if ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to socid
1643
-		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1644
-			if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1645
-				if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1646
-					$sql.= " AND te.entity IS NOT NULL"; // Show all users
1647
-				} else {
1648
-					$sql.= " AND ug.fk_user = te.rowid";
1649
-					$sql.= " AND ug.entity IN (".getEntity($this->element).")";
1650
-				}
1651
-			} else {
1652
-				$sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1653
-			}
1654
-		}
1655
-		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1656
-		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1657
-		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1658
-		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1659
-		// Rem: Bug in some mysql version: SELECT MIN(rowid) FROM llx_socpeople WHERE rowid > 1 when one row in database with rowid=1, returns 1 instead of null
1660
-
1661
-		$result = $this->db->query($sql);
1662
-		if (! $result)
1663
-		{
1664
-			$this->error=$this->db->lasterror();
1665
-			return -2;
1666
-		}
1667
-		$row = $this->db->fetch_row($result);
1668
-		$this->ref_next = $row[0];
1669
-
1670
-		return 1;
1671
-	}
1672
-
1673
-
1674
-	/**
1675
-	 *      Return list of id of contacts of object
1676
-	 *
1677
-	 *      @param	string	$source     Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
1678
-	 *      @return array				Array of id of contacts (if source=external or internal)
1679
-	 * 									Array of id of third parties with at least one contact on object (if source=thirdparty)
1680
-	 */
1681
-	function getListContactId($source='external')
1682
-	{
1683
-		$contactAlreadySelected = array();
1684
-		$tab = $this->liste_contact(-1,$source);
1685
-		$num=count($tab);
1686
-		$i = 0;
1687
-		while ($i < $num)
1688
-		{
1689
-			if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
1690
-			else  $contactAlreadySelected[$i] = $tab[$i]['id'];
1691
-			$i++;
1692
-		}
1693
-		return $contactAlreadySelected;
1694
-	}
1695
-
1696
-
1697
-	/**
1698
-	 *	Link element with a project
1699
-	 *
1700
-	 *	@param     	int		$projectid		Project id to link element to
1701
-	 *	@return		int						<0 if KO, >0 if OK
1702
-	 */
1703
-	function setProject($projectid)
1704
-	{
1705
-		if (! $this->table_element)
1706
-		{
1707
-			dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined",LOG_ERR);
1708
-			return -1;
1709
-		}
1710
-
1711
-		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1712
-		if ($this->table_element == 'actioncomm')
1713
-		{
1714
-			if ($projectid) $sql.= ' SET fk_project = '.$projectid;
1715
-			else $sql.= ' SET fk_project = NULL';
1716
-			$sql.= ' WHERE id = '.$this->id;
1717
-		}
1718
-		else
1719
-		{
1720
-			if ($projectid) $sql.= ' SET fk_projet = '.$projectid;
1721
-			else $sql.= ' SET fk_projet = NULL';
1722
-			$sql.= ' WHERE rowid = '.$this->id;
1723
-		}
1724
-
1725
-		dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
1726
-		if ($this->db->query($sql))
1727
-		{
1728
-			$this->fk_project = $projectid;
1729
-			return 1;
1730
-		}
1731
-		else
1732
-		{
1733
-			dol_print_error($this->db);
1734
-			return -1;
1735
-		}
1736
-	}
1737
-
1738
-	/**
1739
-	 *  Change the payments methods
1740
-	 *
1741
-	 *  @param		int		$id		Id of new payment method
1742
-	 *  @return		int				>0 if OK, <0 if KO
1743
-	 */
1744
-	function setPaymentMethods($id)
1745
-	{
1746
-		dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
1747
-		if ($this->statut >= 0 || $this->element == 'societe')
1748
-		{
1749
-			// TODO uniformize field name
1750
-			$fieldname = 'fk_mode_reglement';
1751
-			if ($this->element == 'societe') $fieldname = 'mode_reglement';
1752
-			if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
1753
-
1754
-			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1755
-			$sql .= ' SET '.$fieldname.' = '.$id;
1756
-			$sql .= ' WHERE rowid='.$this->id;
1757
-
1758
-			if ($this->db->query($sql))
1759
-			{
1760
-				$this->mode_reglement_id = $id;
1761
-				// for supplier
1762
-				if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
1763
-				return 1;
1764
-			}
1765
-			else
1766
-			{
1767
-				dol_syslog(get_class($this).'::setPaymentMethods Erreur '.$sql.' - '.$this->db->error());
1768
-				$this->error=$this->db->error();
1769
-				return -1;
1770
-			}
1771
-		}
1772
-		else
1773
-		{
1774
-			dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
1775
-			$this->error='Status of the object is incompatible '.$this->statut;
1776
-			return -2;
1777
-		}
1778
-	}
1779
-
1780
-	/**
1781
-	 *  Change the multicurrency code
1782
-	 *
1783
-	 *  @param		string	$code	multicurrency code
1784
-	 *  @return		int				>0 if OK, <0 if KO
1785
-	 */
1786
-	function setMulticurrencyCode($code)
1787
-	{
1788
-		dol_syslog(get_class($this).'::setMulticurrencyCode('.$id.')');
1789
-		if ($this->statut >= 0 || $this->element == 'societe')
1790
-		{
1791
-			$fieldname = 'multicurrency_code';
1792
-
1793
-			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1794
-			$sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'";
1795
-			$sql .= ' WHERE rowid='.$this->id;
1796
-
1797
-			if ($this->db->query($sql))
1798
-			{
1799
-				$this->multicurrency_code = $code;
377
+    // Dates
378
+    public $date_creation;			// Date creation
379
+    public $date_validation;		// Date validation
380
+    public $date_modification;		// Date last change (tms field)
1800 381
 
1801
-				list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
1802
-				if ($rate) $this->setMulticurrencyRate($rate,2);
1803 382
 
1804
-				return 1;
1805
-			}
1806
-			else
1807
-			{
1808
-				dol_syslog(get_class($this).'::setMulticurrencyCode Erreur '.$sql.' - '.$this->db->error());
1809
-				$this->error=$this->db->error();
1810
-				return -1;
1811
-			}
1812
-		}
1813
-		else
1814
-		{
1815
-			dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
1816
-			$this->error='Status of the object is incompatible '.$this->statut;
1817
-			return -2;
1818
-		}
1819
-	}
1820
-
1821
-	/**
1822
-	 *  Change the multicurrency rate
1823
-	 *
1824
-	 *  @param		double	$rate	multicurrency rate
1825
-	 *  @param		int		$mode	mode 1 : amounts in company currency will be recalculated, mode 2 : amounts in foreign currency
1826
-	 *  @return		int				>0 if OK, <0 if KO
1827
-	 */
1828
-	function setMulticurrencyRate($rate, $mode=1)
1829
-	{
1830
-		dol_syslog(get_class($this).'::setMulticurrencyRate('.$id.')');
1831
-		if ($this->statut >= 0 || $this->element == 'societe')
1832
-		{
1833
-			$fieldname = 'multicurrency_tx';
1834
-
1835
-			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1836
-			$sql .= ' SET '.$fieldname.' = '.$rate;
1837
-			$sql .= ' WHERE rowid='.$this->id;
1838
-
1839
-			if ($this->db->query($sql))
1840
-			{
1841
-				$this->multicurrency_tx = $rate;
1842 383
 
1843
-				// Update line price
1844
-				if (!empty($this->lines))
1845
-				{
1846
-					foreach ($this->lines as &$line)
1847
-					{
1848
-						if($mode == 1) {
1849
-							$line->subprice = 0;
1850
-						}
384
+    // No constructor as it is an abstract class
1851 385
 
1852
-						switch ($this->element) {
1853
-							case 'propal':
1854
-								$this->updateline(
1855
-									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1856
-									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1857
-									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
1858
-									$line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1859
-								);
1860
-								break;
1861
-							case 'commande':
1862
-								$this->updateline(
1863
-									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1864
-									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
1865
-									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1866
-									$line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1867
-								);
1868
-								break;
1869
-							case 'facture':
1870
-								$this->updateline(
1871
-									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1872
-									$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
1873
-									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1874
-									$line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
1875
-								);
1876
-								break;
1877
-							case 'supplier_proposal':
1878
-								$this->updateline(
1879
-									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1880
-									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1881
-									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
1882
-									$line->ref_fourn, $line->multicurrency_subprice
1883
-								);
1884
-								break;
1885
-							case 'order_supplier':
1886
-								$this->updateline(
1887
-									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1888
-									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
1889
-									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1890
-								);
1891
-								break;
1892
-							case 'invoice_supplier':
1893
-								$this->updateline(
1894
-									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
1895
-									$line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
1896
-									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1897
-								);
1898
-								break;
1899
-							default:
1900
-								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
1901
-								break;
1902
-						}
1903
-					}
1904
-				}
386
+    /**
387
+     * Check an object id/ref exists
388
+     * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
389
+     *
390
+     *  @param	string	$element   	String of element ('product', 'facture', ...)
391
+     *  @param	int		$id      	Id of object
392
+     *  @param  string	$ref     	Ref of object to check
393
+     *  @param	string	$ref_ext	Ref ext of object to check
394
+     *  @return int     			<0 if KO, 0 if OK but not found, >0 if OK and exists
395
+     */
396
+    static function isExistingObject($element, $id, $ref='', $ref_ext='')
397
+    {
398
+        global $db,$conf;
399
+
400
+        $sql = "SELECT rowid, ref, ref_ext";
401
+        $sql.= " FROM ".MAIN_DB_PREFIX.$element;
402
+        $sql.= " WHERE entity IN (".getEntity($element).")" ;
403
+
404
+        if ($id > 0) $sql.= " AND rowid = ".$db->escape($id);
405
+        else if ($ref) $sql.= " AND ref = '".$db->escape($ref)."'";
406
+        else if ($ref_ext) $sql.= " AND ref_ext = '".$db->escape($ref_ext)."'";
407
+        else {
408
+            $error='ErrorWrongParameters';
409
+            dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
410
+            return -1;
411
+        }
412
+        if ($ref || $ref_ext) $sql.= " AND entity = ".$conf->entity;
1905 413
 
1906
-				return 1;
1907
-			}
1908
-			else
1909
-			{
1910
-				dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
1911
-				$this->error=$this->db->error();
1912
-				return -1;
1913
-			}
1914
-		}
1915
-		else
1916
-		{
1917
-			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
1918
-			$this->error='Status of the object is incompatible '.$this->statut;
1919
-			return -2;
1920
-		}
1921
-	}
1922
-
1923
-	/**
1924
-	 *  Change the payments terms
1925
-	 *
1926
-	 *  @param		int		$id		Id of new payment terms
1927
-	 *  @return		int				>0 if OK, <0 if KO
1928
-	 */
1929
-	function setPaymentTerms($id)
1930
-	{
1931
-		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
1932
-		if ($this->statut >= 0 || $this->element == 'societe')
1933
-		{
1934
-			// TODO uniformize field name
1935
-			$fieldname = 'fk_cond_reglement';
1936
-			if ($this->element == 'societe') $fieldname = 'cond_reglement';
1937
-			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
1938
-
1939
-			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1940
-			$sql .= ' SET '.$fieldname.' = '.$id;
1941
-			$sql .= ' WHERE rowid='.$this->id;
1942
-
1943
-			if ($this->db->query($sql))
1944
-			{
1945
-				$this->cond_reglement_id = $id;
1946
-				// for supplier
1947
-				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
1948
-				$this->cond_reglement = $id;	// for compatibility
1949
-				return 1;
1950
-			}
1951
-			else
1952
-			{
1953
-				dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
1954
-				$this->error=$this->db->error();
1955
-				return -1;
1956
-			}
1957
-		}
1958
-		else
1959
-		{
1960
-			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
1961
-			$this->error='Status of the object is incompatible '.$this->statut;
1962
-			return -2;
1963
-		}
1964
-	}
1965
-
1966
-	/**
1967
-	 *	Define delivery address
1968
-	 *  @deprecated
1969
-	 *
1970
-	 *	@param      int		$id		Address id
1971
-	 *	@return     int				<0 si ko, >0 si ok
1972
-	 */
1973
-	function setDeliveryAddress($id)
1974
-	{
1975
-		$fieldname = 'fk_delivery_address';
1976
-		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
1977
-
1978
-		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
1979
-		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
1980
-
1981
-		if ($this->db->query($sql))
1982
-		{
1983
-			$this->fk_delivery_address = $id;
1984
-			return 1;
1985
-		}
1986
-		else
1987
-		{
1988
-			$this->error=$this->db->error();
1989
-			dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
1990
-			return -1;
1991
-		}
1992
-	}
1993
-
1994
-
1995
-	/**
1996
-	 *  Change the shipping method
1997
-	 *
1998
-	 *  @param      int     $shipping_method_id     Id of shipping method
1999
-     *  @param      bool    $notrigger              false=launch triggers after, true=disable triggers
2000
-     *  @param      User	$userused               Object user
2001
-	 *
2002
-	 *  @return     int              1 if OK, 0 if KO
2003
-	 */
2004
-	function setShippingMethod($shipping_method_id, $notrigger=false, $userused=null)
2005
-	{
2006
-        global $user;
414
+        dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
415
+        $resql = $db->query($sql);
416
+        if ($resql)
417
+        {
418
+            $num=$db->num_rows($resql);
419
+            if ($num > 0) return 1;
420
+            else return 0;
421
+        }
422
+        return -1;
423
+    }
2007 424
 
2008
-        if (empty($userused)) $userused=$user;
425
+    /**
426
+     * Method to output saved errors
427
+     *
428
+     * @return	string		String with errors
429
+     */
430
+    function errorsToString()
431
+    {
432
+        return $this->error.(is_array($this->errors)?(($this->error!=''?', ':'').join(', ',$this->errors)):'');
433
+    }
2009 434
 
2010
-        $error = 0;
435
+    /**
436
+     *	Return full name (civility+' '+name+' '+lastname)
437
+     *
438
+     *	@param	Translate	$langs			Language object for translation of civility (used only if option is 1)
439
+     *	@param	int			$option			0=No option, 1=Add civility
440
+     * 	@param	int			$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
441
+     * 	@param	int			$maxlen			Maximum length
442
+     * 	@return	string						String with full name
443
+     */
444
+    function getFullName($langs,$option=0,$nameorder=-1,$maxlen=0)
445
+    {
446
+        //print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
447
+        $lastname=$this->lastname;
448
+        $firstname=$this->firstname;
449
+        if (empty($lastname))  $lastname=(isset($this->lastname)?$this->lastname:(isset($this->name)?$this->name:(isset($this->nom)?$this->nom:(isset($this->societe)?$this->societe:(isset($this->company)?$this->company:'')))));
2011 450
 
2012
-		if (! $this->table_element) {
2013
-			dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined",LOG_ERR);
2014
-			return -1;
2015
-		}
451
+        $ret='';
452
+        if ($option && $this->civility_id)
453
+        {
454
+            if ($langs->transnoentitiesnoconv("Civility".$this->civility_id)!="Civility".$this->civility_id) $ret.=$langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
455
+            else $ret.=$this->civility_id.' ';
456
+        }
2016 457
 
2017
-        $this->db->begin();
458
+        $ret .= DolUtils::dolGetFirstLastname($firstname, $lastname, $nameorder);
2018 459
 
2019
-		if ($shipping_method_id<0) $shipping_method_id='NULL';
2020
-		dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
460
+        return DolUtils::dol_trunc($ret, $maxlen);
461
+    }
2021 462
 
2022
-		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2023
-		$sql.= " SET fk_shipping_method = ".$shipping_method_id;
2024
-		$sql.= " WHERE rowid=".$this->id;
2025
-        $resql = $this->db->query($sql);
2026
-		if (! $resql) {
2027
-			dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2028
-			$this->error = $this->db->lasterror();
2029
-			$error++;
2030
-        } else {
2031
-            if (!$notrigger)
463
+    /**
464
+     * 	Return full address of contact
465
+     *
466
+     * 	@param		int			$withcountry		1=Add country into address string
467
+     *  @param		string		$sep				Separator to use to build string
468
+     *  @param		int		    $withregion			1=Add region into address string
469
+     *	@return		string							Full address string
470
+     */
471
+    function getFullAddress($withcountry=0, $sep="\n", $withregion=0)
472
+    {
473
+        if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
474
+        {
475
+            require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
476
+            $tmparray=getCountry($this->country_id,'all');
477
+            $this->country_code=$tmparray['code'];
478
+            $this->country     =$tmparray['label'];
479
+        }
480
+
481
+        if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_cpde)))
482
+        {
483
+            require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
484
+            $tmparray=getState($this->state_id,'all',0,1);
485
+            $this->state_code   =$tmparray['code'];
486
+            $this->state        =$tmparray['label'];
487
+            $this->region_code  =$tmparray['region_code'];
488
+            $this->region       =$tmparray['region'];
489
+        }
490
+
491
+        return dol_format_address($this, $withcountry, $sep);
492
+    }
493
+
494
+
495
+    /**
496
+     * 	Return full address for banner
497
+     *
498
+     * 	@param		string		$htmlkey            HTML id to make banner content unique
499
+     *  @param      Object      $object				Object (thirdparty, thirdparty of contact for contact, null for a member)
500
+     *	@return		string							Full address string
501
+     */
502
+    function getBannerAddress($htmlkey, $object)
503
+    {
504
+        global $conf, $langs;
505
+
506
+        $countriesusingstate=array('AU','US','IN','GB','ES','UK','TR');    // See also option MAIN_FORCE_STATE_INTO_ADDRESS
507
+
508
+        $contactid=0;
509
+        $thirdpartyid=0;
510
+        if ($this->element == 'societe')
511
+        {
512
+            $thirdpartyid=$this->id;
513
+        }
514
+        if ($this->element == 'contact')
515
+        {
516
+            $contactid=$this->id;
517
+            $thirdpartyid=$object->fk_soc;
518
+        }
519
+        if ($this->element == 'user')
520
+        {
521
+            $contactid=$this->contact_id;
522
+            $thirdpartyid=$object->fk_soc;
523
+        }
524
+
525
+        $out='<!-- BEGIN part to show address block -->';
526
+
527
+        $outdone=0;
528
+        $coords = $this->getFullAddress(1,', ',$conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT);
529
+        if ($coords)
530
+        {
531
+            if (! empty($conf->use_javascript_ajax))
2032 532
             {
2033
-                // Call trigger
2034
-                $this->context=array('shippingmethodupdate'=>1);
2035
-                $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2036
-                if ($result < 0) $error++;
2037
-                // End call trigger
533
+                $namecoords = $this->getFullName($langs,1).'<br>'.$coords;
534
+                // hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
535
+                $out.='<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
536
+                $out.=img_picto($langs->trans("Address"), 'object_address.png');
537
+                $out.='</a> ';
2038 538
             }
539
+            $out.=dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
540
+            $outdone++;
2039 541
         }
2040
-        if ($error)
542
+
543
+        if (! in_array($this->country_code,$countriesusingstate) && empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)   // If MAIN_FORCE_STATE_INTO_ADDRESS is on, state is already returned previously with getFullAddress
544
+                && empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
2041 545
         {
2042
-            $this->db->rollback();
2043
-            return -1;
2044
-        } else {
2045
-            $this->shipping_method_id = ($shipping_method_id=='NULL')?null:$shipping_method_id;
2046
-            $this->db->commit();
2047
-            return 1;
546
+            if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
547
+                $out.=($outdone?' - ':'').$this->region.' - '.$this->state;
548
+            }
549
+            else {
550
+                $out.=($outdone?' - ':'').$this->state;
551
+            }
552
+            $outdone++;
2048 553
         }
2049
-	}
2050
-
2051
-
2052
-	/**
2053
-	 *  Change the warehouse
2054
-	 *
2055
-	 *  @param      int     $warehouse_id     Id of warehouse
2056
-	 *  @return     int              1 if OK, 0 if KO
2057
-	 */
2058
-	function setWarehouse($warehouse_id)
2059
-	{
2060
-		if (! $this->table_element) {
2061
-			dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined",LOG_ERR);
2062
-			return -1;
2063
-		}
2064
-		if ($warehouse_id<0) $warehouse_id='NULL';
2065
-		dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2066
-
2067
-		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2068
-		$sql.= " SET fk_warehouse = ".$warehouse_id;
2069
-		$sql.= " WHERE rowid=".$this->id;
2070
-
2071
-		if ($this->db->query($sql)) {
2072
-			$this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
2073
-			return 1;
2074
-		} else {
2075
-			dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2076
-			$this->error=$this->db->error();
2077
-			return 0;
2078
-		}
2079
-	}
2080
-
2081
-
2082
-	/**
2083
-	 *		Set last model used by doc generator
2084
-	 *
2085
-	 *		@param		User	$user		User object that make change
2086
-	 *		@param		string	$modelpdf	Modele name
2087
-	 *		@return		int					<0 if KO, >0 if OK
2088
-	 */
2089
-	function setDocModel($user, $modelpdf)
2090
-	{
2091
-		if (! $this->table_element)
2092
-		{
2093
-			dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined",LOG_ERR);
2094
-			return -1;
2095
-		}
2096
-
2097
-		$newmodelpdf=dol_trunc($modelpdf,255);
2098
-
2099
-		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2100
-		$sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2101
-		$sql.= " WHERE rowid = ".$this->id;
2102
-		// if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
2103
-		// if ($this->element == 'propal')  $sql.= " AND fk_statut = 0";
2104
-
2105
-		dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2106
-		$resql=$this->db->query($sql);
2107
-		if ($resql)
2108
-		{
2109
-			$this->modelpdf=$modelpdf;
2110
-			return 1;
2111
-		}
2112
-		else
2113
-		{
2114
-			dol_print_error($this->db);
2115
-			return 0;
2116
-		}
2117
-	}
2118
-
2119
-
2120
-	/**
2121
-	 *  Change the bank account
2122
-	 *
2123
-	 *  @param		int		$fk_account		Id of bank account
2124
-	 *  @param      bool    $notrigger      false=launch triggers after, true=disable triggers
2125
-	 *  @param      User	$userused		Object user
2126
-	 *  @return		int				1 if OK, 0 if KO
2127
-	 */
2128
-	function setBankAccount($fk_account, $notrigger=false, $userused=null)
2129
-	{
2130
-        global $user;
2131 554
 
2132
-        if (empty($userused)) $userused=$user;
555
+        if (! empty($this->phone) || ! empty($this->phone_pro) || ! empty($this->phone_mobile) || ! empty($this->phone_perso) || ! empty($this->fax) || ! empty($this->office_phone) || ! empty($this->user_mobile) || ! empty($this->office_fax)) $out.=($outdone?'<br>':'');
556
+        if (! empty($this->phone) && empty($this->phone_pro)) {		// For objects that store pro phone into ->phone
557
+            $out.=dol_print_phone($this->phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
558
+        }
559
+        if (! empty($this->phone_pro)) {
560
+            $out.=dol_print_phone($this->phone_pro,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
561
+        }
562
+        if (! empty($this->phone_mobile)) {
563
+            $out.=dol_print_phone($this->phone_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
564
+        }
565
+        if (! empty($this->phone_perso)) {
566
+            $out.=dol_print_phone($this->phone_perso,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePerso")); $outdone++;
567
+        }
568
+        if (! empty($this->office_phone)) {
569
+            $out.=dol_print_phone($this->office_phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
570
+        }
571
+        if (! empty($this->user_mobile)) {
572
+            $out.=dol_print_phone($this->user_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
573
+        }
574
+        if (! empty($this->fax)) {
575
+            $out.=dol_print_phone($this->fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
576
+        }
577
+        if (! empty($this->office_fax)) {
578
+            $out.=dol_print_phone($this->office_fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
579
+        }
2133 580
 
2134
-        $error = 0;
581
+        $out.='<div style="clear: both;"></div>';
582
+        $outdone=0;
583
+        if (! empty($this->email))
584
+        {
585
+            $out.=dol_print_email($this->email,$this->id,$object->id,'AC_EMAIL',0,0,1);
586
+            $outdone++;
587
+        }
588
+        if (! empty($this->url))
589
+        {
590
+            $out.=dol_print_url($this->url,'_goout',0,1);
591
+            $outdone++;
592
+        }
593
+        $out.='<div style="clear: both;">';
594
+        if (! empty($conf->socialnetworks->enabled))
595
+        {
596
+            if ($this->skype) $out.=dol_print_socialnetworks($this->skype,$this->id,$object->id,'skype');
597
+            $outdone++;
598
+            if ($this->jabberid) $out.=dol_print_socialnetworks($this->jabberid,$this->id,$object->id,'jabber');
599
+            $outdone++;
600
+            if ($this->twitter) $out.=dol_print_socialnetworks($this->twitter,$this->id,$object->id,'twitter');
601
+            $outdone++;
602
+            if ($this->facebook) $out.=dol_print_socialnetworks($this->facebook,$this->id,$object->id,'facebook');
603
+            $outdone++;
604
+        }
605
+        $out.='</div>';
2135 606
 
2136
-		if (! $this->table_element) {
2137
-			dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined",LOG_ERR);
2138
-			return -1;
2139
-		}
2140
-        $this->db->begin();
607
+        $out.='<!-- END Part to show address block -->';
608
+
609
+        return $out;
610
+    }
2141 611
 
2142
-		if ($fk_account<0) $fk_account='NULL';
2143
-		dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
612
+    /**
613
+     * Return the link of last main doc file for direct public download.
614
+     *
615
+     * @param	string	$modulepart			Module related to document
616
+     * @param	int		$initsharekey		Init the share key if it was not yet defined
617
+     * @param	int		$relativelink		0=Return full external link, 1=Return link relative to root of file
618
+     * @return	string						Link or empty string if there is no download link
619
+     */
620
+    function getLastMainDocLink($modulepart, $initsharekey=0, $relativelink=0)
621
+    {
622
+        global $user, $dolibarr_main_url_root;
2144 623
 
2145
-		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2146
-		$sql.= " SET fk_account = ".$fk_account;
2147
-		$sql.= " WHERE rowid=".$this->id;
624
+        if (empty($this->last_main_doc))
625
+        {
626
+            return '';		// No way to known which document name to use
627
+        }
2148 628
 
2149
-        $resql = $this->db->query($sql);
2150
-        if (! $resql)
629
+        include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
630
+        $ecmfile=new EcmFiles($this->db);
631
+        $result = $ecmfile->fetch(0, '', $this->last_main_doc);
632
+        if ($result < 0)
2151 633
         {
2152
-            dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2153
-            $this->error = $this->db->lasterror();
2154
-            $error++;
634
+            $this->error = $ecmfile->error;
635
+            $this->errors = $ecmfile->errors;
636
+            return -1;
2155 637
         }
2156
-        else
638
+
639
+        if (empty($ecmfile->id))
2157 640
         {
2158
-            if (!$notrigger)
641
+            // Add entry into index
642
+            if ($initsharekey)
2159 643
             {
2160
-                // Call trigger
2161
-                $this->context=array('bankaccountupdate'=>1);
2162
-                $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2163
-                if ($result < 0) $error++;
2164
-                // End call trigger
644
+                require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
645
+                // TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first
646
+                /*
647
+				$ecmfile->filepath = $rel_dir;
648
+				$ecmfile->filename = $filename;
649
+				$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
650
+				$ecmfile->fullpath_orig = '';
651
+				$ecmfile->gen_or_uploaded = 'generated';
652
+				$ecmfile->description = '';    // indexed content
653
+				$ecmfile->keyword = '';        // keyword content
654
+				$ecmfile->share = getRandomPassword(true);
655
+				$result = $ecmfile->create($user);
656
+				if ($result < 0)
657
+				{
658
+					$this->error = $ecmfile->error;
659
+					$this->errors = $ecmfile->errors;
660
+				}
661
+				*/
2165 662
             }
663
+            else return '';
2166 664
         }
2167
-        if ($error)
665
+        elseif (empty($ecmfile->share))
2168 666
         {
2169
-            $this->db->rollback();
2170
-            return -1;
667
+            // Add entry into index
668
+            if ($initsharekey)
669
+            {
670
+                require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
671
+                $ecmfile->share = getRandomPassword(true);
672
+                $ecmfile->update($user);
673
+            }
674
+            else return '';
675
+        }
676
+
677
+        // Define $urlwithroot
678
+        $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
679
+        $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
680
+        //$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
681
+
682
+        $forcedownload=0;
683
+
684
+        $paramlink='';
685
+        //if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart;		// For sharing with hash (so public files), modulepart is not required.
686
+        //if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; 					// For sharing with hash (so public files), entity is not required.
687
+        //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath);								// No need of name of file for public link, we will use the hash
688
+        if (! empty($ecmfile->share)) $paramlink.=($paramlink?'&':'').'hashp='.$ecmfile->share;			// Hash for public share
689
+        if ($forcedownload) $paramlink.=($paramlink?'&':'').'attachment=1';
690
+
691
+        if ($relativelink)
692
+        {
693
+            $linktoreturn='document.php'.($paramlink?'?'.$paramlink:'');
2171 694
         }
2172 695
         else
2173 696
         {
2174
-            $this->fk_account = ($fk_account=='NULL')?null:$fk_account;
2175
-            $this->db->commit();
2176
-            return 1;
697
+            $linktoreturn=$urlwithroot.'/document.php'.($paramlink?'?'.$paramlink:'');
2177 698
         }
2178
-    }
2179 699
 
700
+        // Here $ecmfile->share is defined
701
+        return $linktoreturn;
702
+    }
2180 703
 
2181
-	// TODO: Move line related operations to CommonObjectLine?
2182 704
 
2183 705
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2184
-	/**
2185
-	 *  Save a new position (field rang) for details lines.
2186
-	 *  You can choose to set position for lines with already a position or lines without any position defined.
2187
-	 *
2188
-	 * 	@param		boolean		$renum			   True to renum all already ordered lines, false to renum only not already ordered lines.
2189
-	 * 	@param		string		$rowidorder		   ASC or DESC
2190
-	 * 	@param		boolean		$fk_parent_line    Table with fk_parent_line field or not
2191
-	 * 	@return		int                            <0 if KO, >0 if OK
2192
-	 */
2193
-	function line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
2194
-	{
706
+    /**
707
+     *  Add a link between element $this->element and a contact
708
+     *
709
+     *  @param	int		$fk_socpeople       Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
710
+     *  @param 	int		$type_contact 		Type of contact (code or id). Must be id or code found into table llx_c_type_contact. For example: SALESREPFOLL
711
+     *  @param  string	$source             external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
712
+     *  @param  int		$notrigger			Disable all triggers
713
+     *  @return int                 		<0 if KO, >0 if OK
714
+     */
715
+    function add_contact($fk_socpeople, $type_contact, $source='external',$notrigger=0)
716
+    {
2195 717
         // phpcs:enable
2196
-		if (! $this->table_element_line)
2197
-		{
2198
-			dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined",LOG_ERR);
2199
-			return -1;
2200
-		}
2201
-		if (! $this->fk_element)
2202
-		{
2203
-			dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined",LOG_ERR);
2204
-			return -1;
2205
-		}
2206
-
2207
-		// Count number of lines to reorder (according to choice $renum)
2208
-		$nl=0;
2209
-		$sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2210
-		$sql.= ' WHERE '.$this->fk_element.'='.$this->id;
2211
-		if (! $renum) $sql.= ' AND rang = 0';
2212
-		if ($renum) $sql.= ' AND rang <> 0';
2213
-
2214
-		dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
2215
-		$resql = $this->db->query($sql);
2216
-		if ($resql)
2217
-		{
2218
-			$row = $this->db->fetch_row($resql);
2219
-			$nl = $row[0];
2220
-		}
2221
-		else dol_print_error($this->db);
2222
-		if ($nl > 0)
2223
-		{
2224
-			// The goal of this part is to reorder all lines, with all children lines sharing the same
2225
-			// counter that parents.
2226
-			$rows=array();
2227
-
2228
-			// We first search all lines that are parent lines (for multilevel details lines)
2229
-			$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2230
-			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2231
-			if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
2232
-			$sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
2233
-
2234
-			dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
2235
-			$resql = $this->db->query($sql);
2236
-			if ($resql)
2237
-			{
2238
-				$i=0;
2239
-				$num = $this->db->num_rows($resql);
2240
-				while ($i < $num)
2241
-				{
2242
-					$row = $this->db->fetch_row($resql);
2243
-					$rows[] = $row[0];	// Add parent line into array rows
2244
-					$childrens = $this->getChildrenOfLine($row[0]);
2245
-					if (! empty($childrens))
2246
-					{
2247
-						foreach($childrens as $child)
2248
-						{
2249
-							array_push($rows, $child);
2250
-						}
2251
-					}
2252
-					$i++;
2253
-				}
718
+        global $user,$langs;
2254 719
 
2255
-				// Now we set a new number for each lines (parent and children with children included into parent tree)
2256
-				if (! empty($rows))
2257
-				{
2258
-					foreach($rows as $key => $row)
2259
-					{
2260
-						$this->updateRangOfLine($row, ($key+1));
2261
-					}
2262
-				}
2263
-			}
2264
-			else
2265
-			{
2266
-				dol_print_error($this->db);
2267
-			}
2268
-		}
2269
-		return 1;
2270
-	}
2271
-
2272
-	/**
2273
-	 * 	Get children of line
2274
-	 *
2275
-	 * 	@param	int		$id		Id of parent line
2276
-	 * 	@return	array			Array with list of children lines id
2277
-	 */
2278
-	function getChildrenOfLine($id)
2279
-	{
2280
-		$rows=array();
2281
-
2282
-		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2283
-		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2284
-		$sql.= ' AND fk_parent_line = '.$id;
2285
-		$sql.= ' ORDER BY rang ASC';
2286
-
2287
-		dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
2288
-		$resql = $this->db->query($sql);
2289
-		if ($resql)
2290
-		{
2291
-			$i=0;
2292
-			$num = $this->db->num_rows($resql);
2293
-			while ($i < $num)
2294
-			{
2295
-				$row = $this->db->fetch_row($resql);
2296
-				$rows[$i] = $row[0];
2297
-				$i++;
2298
-			}
2299
-		}
2300 720
 
2301
-		return $rows;
2302
-	}
721
+        dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
2303 722
 
2304
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2305
-	/**
2306
-	 * 	Update a line to have a lower rank
2307
-	 *
2308
-	 * 	@param 	int			$rowid				Id of line
2309
-	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2310
-	 * 	@return	void
2311
-	 */
2312
-	function line_up($rowid, $fk_parent_line=true)
2313
-	{
2314
-        // phpcs:enable
2315
-		$this->line_order(false, 'ASC', $fk_parent_line);
723
+        // Check parameters
724
+        if ($fk_socpeople <= 0)
725
+        {
726
+            $langs->load("errors");
727
+            $this->error=$langs->trans("ErrorWrongValueForParameterX","1");
728
+            dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
729
+            return -1;
730
+        }
731
+        if (! $type_contact)
732
+        {
733
+            $langs->load("errors");
734
+            $this->error=$langs->trans("ErrorWrongValueForParameterX","2");
735
+            dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
736
+            return -2;
737
+        }
2316 738
 
2317
-		// Get rang of line
2318
-		$rang = $this->getRangOfLine($rowid);
739
+        $id_type_contact=0;
740
+        if (is_numeric($type_contact))
741
+        {
742
+            $id_type_contact=$type_contact;
743
+        }
744
+        else
745
+        {
746
+            // We look for id type_contact
747
+            $sql = "SELECT tc.rowid";
748
+            $sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
749
+            $sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
750
+            $sql.= " AND tc.source='".$this->db->escape($source)."'";
751
+            $sql.= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
752
+            //print $sql;
753
+            $resql=$this->db->query($sql);
754
+            if ($resql)
755
+            {
756
+                $obj = $this->db->fetch_object($resql);
757
+                if ($obj) $id_type_contact=$obj->rowid;
758
+            }
759
+        }
2319 760
 
2320
-		// Update position of line
2321
-		$this->updateLineUp($rowid, $rang);
2322
-	}
761
+        if ($id_type_contact == 0)
762
+        {
763
+            $this->error='CODE_NOT_VALID_FOR_THIS_ELEMENT';
764
+            dol_syslog("CODE_NOT_VALID_FOR_THIS_ELEMENT: Code type of contact '".$type_contact."' does not exists or is not active for element ".$this->element.", we can ignore it");
765
+            return -3;
766
+        }
2323 767
 
2324
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2325
-	/**
2326
-	 * 	Update a line to have a higher rank
2327
-	 *
2328
-	 * 	@param	int			$rowid				Id of line
2329
-	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2330
-	 * 	@return	void
2331
-	 */
2332
-	function line_down($rowid, $fk_parent_line=true)
2333
-	{
2334
-        // phpcs:enable
2335
-		$this->line_order(false, 'ASC', $fk_parent_line);
2336
-
2337
-		// Get rang of line
2338
-		$rang = $this->getRangOfLine($rowid);
2339
-
2340
-		// Get max value for rang
2341
-		$max = $this->line_max();
2342
-
2343
-		// Update position of line
2344
-		$this->updateLineDown($rowid, $rang, $max);
2345
-	}
2346
-
2347
-	/**
2348
-	 * 	Update position of line (rang)
2349
-	 *
2350
-	 * 	@param	int		$rowid		Id of line
2351
-	 * 	@param	int		$rang		Position
2352
-	 * 	@return	void
2353
-	 */
2354
-	function updateRangOfLine($rowid,$rang)
2355
-	{
2356
-		$fieldposition = 'rang';
2357
-		if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2358
-
2359
-		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2360
-		$sql.= ' WHERE rowid = '.$rowid;
2361
-
2362
-		dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
2363
-		if (! $this->db->query($sql))
2364
-		{
2365
-			dol_print_error($this->db);
2366
-		}
2367
-	}
768
+        $datecreate = dol_now();
769
+
770
+        // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
771
+        $TListeContacts=$this->liste_contact(-1, $source);
772
+        $already_added=false;
773
+        if(!empty($TListeContacts)) {
774
+            foreach($TListeContacts as $array_contact) {
775
+                if($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
776
+                    $already_added=true;
777
+                    break;
778
+                }
779
+            }
780
+        }
2368 781
 
2369
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2370
-	/**
2371
-	 * 	Update position of line with ajax (rang)
2372
-	 *
2373
-	 * 	@param	array	$rows	Array of rows
2374
-	 * 	@return	void
2375
-	 */
2376
-	function line_ajaxorder($rows)
2377
-	{
2378
-        // phpcs:enable
2379
-		$num = count($rows);
2380
-		for ($i = 0 ; $i < $num ; $i++)
2381
-		{
2382
-			$this->updateRangOfLine($rows[$i], ($i+1));
2383
-		}
2384
-	}
2385
-
2386
-	/**
2387
-	 * 	Update position of line up (rang)
2388
-	 *
2389
-	 * 	@param	int		$rowid		Id of line
2390
-	 * 	@param	int		$rang		Position
2391
-	 * 	@return	void
2392
-	 */
2393
-	function updateLineUp($rowid,$rang)
2394
-	{
2395
-		if ($rang > 1)
2396
-		{
2397
-			$fieldposition = 'rang';
2398
-			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2399
-
2400
-			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang ;
2401
-			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2402
-			$sql.= ' AND rang = '.($rang - 1);
2403
-			if ($this->db->query($sql) )
2404
-			{
2405
-				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang - 1);
2406
-				$sql.= ' WHERE rowid = '.$rowid;
2407
-				if (! $this->db->query($sql) )
2408
-				{
2409
-					dol_print_error($this->db);
2410
-				}
2411
-			}
2412
-			else
2413
-			{
2414
-				dol_print_error($this->db);
2415
-			}
2416
-		}
2417
-	}
2418
-
2419
-	/**
2420
-	 * 	Update position of line down (rang)
2421
-	 *
2422
-	 * 	@param	int		$rowid		Id of line
2423
-	 * 	@param	int		$rang		Position
2424
-	 * 	@param	int		$max		Max
2425
-	 * 	@return	void
2426
-	 */
2427
-	function updateLineDown($rowid,$rang,$max)
2428
-	{
2429
-		if ($rang < $max)
2430
-		{
2431
-			$fieldposition = 'rang';
2432
-			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2433
-
2434
-			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2435
-			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2436
-			$sql.= ' AND rang = '.($rang+1);
2437
-			if ($this->db->query($sql) )
2438
-			{
2439
-				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang+1);
2440
-				$sql.= ' WHERE rowid = '.$rowid;
2441
-				if (! $this->db->query($sql) )
2442
-				{
2443
-					dol_print_error($this->db);
2444
-				}
2445
-			}
2446
-			else
2447
-			{
2448
-				dol_print_error($this->db);
2449
-			}
2450
-		}
2451
-	}
2452
-
2453
-	/**
2454
-	 * 	Get position of line (rang)
2455
-	 *
2456
-	 * 	@param		int		$rowid		Id of line
2457
-	 *  @return		int     			Value of rang in table of lines
2458
-	 */
2459
-	function getRangOfLine($rowid)
2460
-	{
2461
-		$sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2462
-		$sql.= ' WHERE rowid ='.$rowid;
2463
-
2464
-		dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2465
-		$resql = $this->db->query($sql);
2466
-		if ($resql)
2467
-		{
2468
-			$row = $this->db->fetch_row($resql);
2469
-			return $row[0];
2470
-		}
2471
-	}
2472
-
2473
-	/**
2474
-	 * 	Get rowid of the line relative to its position
2475
-	 *
2476
-	 * 	@param		int		$rang		Rang value
2477
-	 *  @return     int     			Rowid of the line
2478
-	 */
2479
-	function getIdOfLine($rang)
2480
-	{
2481
-		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2482
-		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2483
-		$sql.= ' AND rang = '.$rang;
2484
-		$resql = $this->db->query($sql);
2485
-		if ($resql)
2486
-		{
2487
-			$row = $this->db->fetch_row($resql);
2488
-			return $row[0];
2489
-		}
2490
-	}
782
+        if(!$already_added) {
2491 783
 
2492
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2493
-	/**
2494
-	 * 	Get max value used for position of line (rang)
2495
-	 *
2496
-	 * 	@param		int		$fk_parent_line		Parent line id
2497
-	 *  @return     int  			   			Max value of rang in table of lines
2498
-	 */
2499
-	function line_max($fk_parent_line=0)
2500
-	{
2501
-        // phpcs:enable
2502
-		// Search the last rang with fk_parent_line
2503
-		if ($fk_parent_line)
2504
-		{
2505
-			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2506
-			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2507
-			$sql.= ' AND fk_parent_line = '.$fk_parent_line;
2508
-
2509
-			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2510
-			$resql = $this->db->query($sql);
2511
-			if ($resql)
2512
-			{
2513
-				$row = $this->db->fetch_row($resql);
2514
-				if (! empty($row[0]))
2515
-				{
2516
-					return $row[0];
2517
-				}
2518
-				else
2519
-				{
2520
-					return $this->getRangOfLine($fk_parent_line);
2521
-				}
2522
-			}
2523
-		}
2524
-		// If not, search the last rang of element
2525
-		else
2526
-		{
2527
-			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2528
-			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2529
-
2530
-			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2531
-			$resql = $this->db->query($sql);
2532
-			if ($resql)
2533
-			{
2534
-				$row = $this->db->fetch_row($resql);
2535
-				return $row[0];
2536
-			}
2537
-		}
2538
-	}
784
+            $this->db->begin();
2539 785
 
2540
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2541
-	/**
2542
-	 *  Update external ref of element
2543
-	 *
2544
-	 *  @param      string		$ref_ext	Update field ref_ext
2545
-	 *  @return     int      		   		<0 if KO, >0 if OK
2546
-	 */
2547
-	function update_ref_ext($ref_ext)
2548
-	{
2549
-        // phpcs:enable
2550
-		if (! $this->table_element)
2551
-		{
2552
-			dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2553
-			return -1;
2554
-		}
2555
-
2556
-		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2557
-		$sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2558
-		$sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2559
-
2560
-		dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2561
-		if ($this->db->query($sql))
2562
-		{
2563
-			$this->ref_ext = $ref_ext;
2564
-			return 1;
2565
-		}
2566
-		else
2567
-		{
2568
-			$this->error=$this->db->error();
2569
-			return -1;
2570
-		}
2571
-	}
786
+            // Insert into database
787
+            $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
788
+            $sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
789
+            $sql.= " VALUES (".$this->id.", ".$fk_socpeople." , " ;
790
+            $sql.= "'".$this->db->idate($datecreate)."'";
791
+            $sql.= ", 4, ". $id_type_contact;
792
+            $sql.= ")";
793
+
794
+            $resql=$this->db->query($sql);
795
+            if ($resql)
796
+            {
797
+                if (! $notrigger)
798
+                {
799
+                    $result=$this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
800
+                    if ($result < 0)
801
+                    {
802
+                        $this->db->rollback();
803
+                        return -1;
804
+                    }
805
+                }
806
+
807
+                $this->db->commit();
808
+                return 1;
809
+            }
810
+            else
811
+            {
812
+                if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
813
+                {
814
+                    $this->error=$this->db->errno();
815
+                    $this->db->rollback();
816
+                    echo 'err rollback';
817
+                    return -2;
818
+                }
819
+                else
820
+                {
821
+                    $this->error=$this->db->error();
822
+                    $this->db->rollback();
823
+                    return -1;
824
+                }
825
+            }
826
+        } else return 0;
827
+    }
2572 828
 
2573 829
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2574
-	/**
2575
-	 *  Update note of element
2576
-	 *
2577
-	 *  @param      string		$note		New value for note
2578
-	 *  @param		string		$suffix		'', '_public' or '_private'
2579
-	 *  @return     int      		   		<0 if KO, >0 if OK
2580
-	 */
2581
-	function update_note($note, $suffix='')
2582
-	{
830
+    /**
831
+     *    Copy contact from one element to current
832
+     *
833
+     *    @param    CommonObject    $objFrom    Source element
834
+     *    @param    string          $source     Nature of contact ('internal' or 'external')
835
+     *    @return   int                         >0 if OK, <0 if KO
836
+     */
837
+    function copy_linked_contact($objFrom, $source='internal')
838
+    {
2583 839
         // phpcs:enable
2584
-		global $user;
2585
-
2586
-		if (! $this->table_element)
2587
-		{
2588
-			$this->error='update_note was called on objet with property table_element not defined';
2589
-			dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2590
-			return -1;
2591
-		}
2592
-		if (! in_array($suffix,array('','_public','_private')))
2593
-		{
2594
-			$this->error='update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
2595
-			dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2596
-			return -2;
2597
-		}
2598
-		// Special cas
2599
-		//var_dump($this->table_element);exit;
2600
-		if ($this->table_element == 'product') $suffix='';
2601
-
2602
-		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2603
-		$sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2604
-		$sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2605
-		$sql.= " WHERE rowid =". $this->id;
2606
-
2607
-		dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2608
-		if ($this->db->query($sql))
2609
-		{
2610
-			if ($suffix == '_public') $this->note_public = $note;
2611
-			else if ($suffix == '_private') $this->note_private = $note;
2612
-			else
2613
-			{
2614
-				$this->note = $note;      // deprecated
2615
-				$this->note_private = $note;
2616
-			}
2617
-			return 1;
2618
-		}
2619
-		else
2620
-		{
2621
-			$this->error=$this->db->lasterror();
2622
-			return -1;
2623
-		}
2624
-	}
840
+        $contacts = $objFrom->liste_contact(-1, $source);
841
+        foreach($contacts as $contact)
842
+        {
843
+            if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
844
+            {
845
+                $this->error=$this->db->lasterror();
846
+                return -1;
847
+            }
848
+        }
849
+        return 1;
850
+    }
2625 851
 
2626 852
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2627
-	/**
2628
-	 * 	Update public note (kept for backward compatibility)
2629
-	 *
2630
-	 * @param      string		$note		New value for note
2631
-	 * @return     int      		   		<0 if KO, >0 if OK
2632
-	 * @deprecated
2633
-	 * @see update_note()
2634
-	 */
2635
-	function update_note_public($note)
2636
-	{
853
+    /**
854
+     *      Update a link to contact line
855
+     *
856
+     *      @param	int		$rowid              Id of line contact-element
857
+     * 		@param	int		$statut	            New status of link
858
+     *      @param  int		$type_contact_id    Id of contact type (not modified if 0)
859
+     *      @param  int		$fk_socpeople	    Id of soc_people to update (not modified if 0)
860
+     *      @return int                 		<0 if KO, >= 0 if OK
861
+     */
862
+    function update_contact($rowid, $statut, $type_contact_id=0, $fk_socpeople=0)
863
+    {
2637 864
         // phpcs:enable
2638
-		return $this->update_note($note,'_public');
2639
-	}
865
+        // Insert into database
866
+        $sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
867
+        $sql.= " statut = ".$statut;
868
+        if ($type_contact_id) $sql.= ", fk_c_type_contact = '".$type_contact_id ."'";
869
+        if ($fk_socpeople) $sql.= ", fk_socpeople = '".$fk_socpeople ."'";
870
+        $sql.= " where rowid = ".$rowid;
871
+        $resql=$this->db->query($sql);
872
+        if ($resql)
873
+        {
874
+            return 0;
875
+        }
876
+        else
877
+        {
878
+            $this->error=$this->db->lasterror();
879
+            return -1;
880
+        }
881
+    }
2640 882
 
2641 883
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2642
-	/**
2643
-	 *	Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
2644
-	 *  Must be called at end of methods addline or updateline.
2645
-	 *
2646
-	 *	@param	int		$exclspec          	>0 = Exclude special product (product_type=9)
2647
-	 *  @param  string	$roundingadjust    	'none'=Do nothing, 'auto'=Use default method (MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND if defined, or '0'), '0'=Force mode total of rounding, '1'=Force mode rounding of total
2648
-	 *  @param	int		$nodatabaseupdate	1=Do not update database. Update only properties of object.
2649
-	 *  @param	Societe	$seller				If roundingadjust is '0' or '1' or maybe 'auto', it means we recalculate total for lines before calculating total for object and for this, we need seller object.
2650
-	 *	@return	int    			           	<0 if KO, >0 if OK
2651
-	 */
2652
-	function update_price($exclspec=0,$roundingadjust='none',$nodatabaseupdate=0,$seller=null)
2653
-	{
884
+    /**
885
+     *    Delete a link to contact line
886
+     *
887
+     *    @param	int		$rowid			Id of contact link line to delete
888
+     *    @param	int		$notrigger		Disable all triggers
889
+     *    @return   int						>0 if OK, <0 if KO
890
+     */
891
+    function delete_contact($rowid, $notrigger=0)
892
+    {
2654 893
         // phpcs:enable
2655
-		global $conf, $hookmanager, $action;
2656
-
2657
-		// Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
2658
-		$MODULE = "";
2659
-		if ($this->element == 'propal')
2660
-			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
2661
-		elseif ($this->element == 'order')
2662
-			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
2663
-		elseif ($this->element == 'facture')
2664
-			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
2665
-		elseif ($this->element == 'facture_fourn')
2666
-			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
2667
-		elseif ($this->element == 'order_supplier')
2668
-			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
2669
-		elseif ($this->element == 'supplier_proposal')
2670
-			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
2671
-
2672
-		if (! empty($MODULE)) {
2673
-			if (! empty($conf->global->$MODULE)) {
2674
-				$modsactivated = explode(',', $conf->global->$MODULE);
2675
-				foreach ($modsactivated as $mod) {
2676
-					if ($conf->$mod->enabled)
2677
-						return 1; // update was disabled by specific setup
2678
-				}
2679
-			}
2680
-		}
2681
-
2682
-		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2683
-
2684
-		if ($roundingadjust == '-1') $roundingadjust='auto';	// For backward compatibility
2685
-
2686
-		$forcedroundingmode=$roundingadjust;
2687
-		if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2688
-		elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2689
-
2690
-		$error=0;
2691
-
2692
-		$multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2693
-
2694
-		// Define constants to find lines to sum
2695
-		$fieldtva='total_tva';
2696
-		$fieldlocaltax1='total_localtax1';
2697
-		$fieldlocaltax2='total_localtax2';
2698
-		$fieldup='subprice';
2699
-		if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2700
-		{
2701
-			$fieldtva='tva';
2702
-			$fieldup='pu_ht';
2703
-		}
2704
-		if ($this->element == 'expensereport')
2705
-		{
2706
-			$fieldup='value_unit';
2707
-		}
2708
-
2709
-		$sql = 'SELECT rowid, qty, '.$fieldup.' as up, remise_percent, total_ht, '.$fieldtva.' as total_tva, total_ttc, '.$fieldlocaltax1.' as total_localtax1, '.$fieldlocaltax2.' as total_localtax2,';
2710
-		$sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2711
-			if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2712
-			$sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2713
-		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2714
-		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2715
-		if ($exclspec)
2716
-		{
2717
-			$product_field='product_type';
2718
-			if ($this->table_element_line == 'contratdet') $product_field='';    // contratdet table has no product_type field
2719
-			if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2720
-		}
2721
-		$sql.= ' ORDER by rowid';	// We want to be sure to always use same order of line to not change lines differently when option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND is used
2722
-
2723
-		dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2724
-		$resql = $this->db->query($sql);
2725
-		if ($resql)
2726
-		{
2727
-			$this->total_ht  = 0;
2728
-			$this->total_tva = 0;
2729
-			$this->total_localtax1 = 0;
2730
-			$this->total_localtax2 = 0;
2731
-			$this->total_ttc = 0;
2732
-			$total_ht_by_vats  = array();
2733
-			$total_tva_by_vats = array();
2734
-			$total_ttc_by_vats = array();
2735
-			$this->multicurrency_total_ht	= 0;
2736
-			$this->multicurrency_total_tva	= 0;
2737
-			$this->multicurrency_total_ttc	= 0;
2738
-
2739
-			$num = $this->db->num_rows($resql);
2740
-			$i = 0;
2741
-			while ($i < $num)
2742
-			{
2743
-				$obj = $this->db->fetch_object($resql);
2744
-
2745
-				// Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2746
-				$parameters=array('fk_element' => $obj->rowid);
2747
-				$reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2748
-
2749
-				if (empty($reshook) && $forcedroundingmode == '0')	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
2750
-				{
2751
-					$localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2752
-					$tmpcal=calcul_price_total($obj->qty, $obj->up, $obj->remise_percent, $obj->vatrate, $obj->localtax1_tx, $obj->localtax2_tx, 0, 'HT', $obj->info_bits, $obj->product_type, $seller, $localtax_array, (isset($obj->situation_percent) ? $obj->situation_percent : 100), $multicurrency_tx);
2753
-					$diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2754
-					if ($diff)
2755
-					{
2756
-						$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2757
-						dol_syslog('We found unconsistent data into detailed line (difference of '.$diff.') for line rowid = '.$obj->rowid." (total vat of line calculated=".$tmpcal[1].", database=".$obj->total_tva."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
2758
-								$resqlfix=$this->db->query($sqlfix);
2759
-								if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2760
-								$obj->total_tva = $tmpcal[1];
2761
-								$obj->total_ttc = $tmpcal[2];
2762
-						//
2763
-					}
2764
-				}
894
+        global $user;
2765 895
 
2766
-				$this->total_ht        += $obj->total_ht;		// The field visible at end of line detail
2767
-				$this->total_tva       += $obj->total_tva;
2768
-				$this->total_localtax1 += $obj->total_localtax1;
2769
-				$this->total_localtax2 += $obj->total_localtax2;
2770
-				$this->total_ttc       += $obj->total_ttc;
2771
-				$this->multicurrency_total_ht        += $obj->multicurrency_total_ht;		// The field visible at end of line detail
2772
-				$this->multicurrency_total_tva       += $obj->multicurrency_total_tva;
2773
-				$this->multicurrency_total_ttc       += $obj->multicurrency_total_ttc;
2774
-
2775
-				if (! isset($total_ht_by_vats[$obj->vatrate]))  $total_ht_by_vats[$obj->vatrate]=0;
2776
-				if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2777
-				if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2778
-				$total_ht_by_vats[$obj->vatrate]  += $obj->total_ht;
2779
-				$total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2780
-				$total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2781
-
2782
-				if ($forcedroundingmode == '1')	// Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
2783
-				{
2784
-					$tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
2785
-					$diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
2786
-					//print 'Line '.$i.' rowid='.$obj->rowid.' vat_rate='.$obj->vatrate.' total_ht='.$obj->total_ht.' total_tva='.$obj->total_tva.' total_ttc='.$obj->total_ttc.' total_ht_by_vats='.$total_ht_by_vats[$obj->vatrate].' total_tva_by_vats='.$total_tva_by_vats[$obj->vatrate].' (new calculation = '.$tmpvat.') total_ttc_by_vats='.$total_ttc_by_vats[$obj->vatrate].($diff?" => DIFF":"")."<br>\n";
2787
-					if ($diff)
2788
-					{
2789
-						if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
2790
-						$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".($obj->total_tva - $diff).", total_ttc = ".($obj->total_ttc - $diff)." WHERE rowid = ".$obj->rowid;
2791
-						dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
2792
-								$resqlfix=$this->db->query($sqlfix);
2793
-								if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2794
-								$this->total_tva -= $diff;
2795
-								$this->total_ttc -= $diff;
2796
-								$total_tva_by_vats[$obj->vatrate] -= $diff;
2797
-								$total_ttc_by_vats[$obj->vatrate] -= $diff;
2798
-					}
2799
-				}
2800 896
 
2801
-				$i++;
2802
-			}
897
+        $this->db->begin();
2803 898
 
2804
-			// Add revenue stamp to total
2805
-			$this->total_ttc       			+= isset($this->revenuestamp)?$this->revenuestamp:0;
2806
-			$this->multicurrency_total_ttc  += isset($this->revenuestamp)?($this->revenuestamp * $multicurrency_tx):0;
899
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
900
+        $sql.= " WHERE rowid =".$rowid;
2807 901
 
2808
-			// Situations totals
2809
-			if ($this->situation_cycle_ref && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE )
2810
-			{
2811
-				$prev_sits = $this->get_prev_sits();
2812
-
2813
-				foreach ($prev_sits as $sit) {				// $sit is an object Facture loaded with a fetch.
2814
-					$this->total_ht -= $sit->total_ht;
2815
-					$this->total_tva -= $sit->total_tva;
2816
-					$this->total_localtax1 -= $sit->total_localtax1;
2817
-					$this->total_localtax2 -= $sit->total_localtax2;
2818
-					$this->total_ttc -= $sit->total_ttc;
2819
-					$this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
2820
-					$this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
2821
-					$this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
2822
-				}
2823
-			}
2824
-
2825
-			$this->db->free($resql);
2826
-
2827
-			// Now update global field total_ht, total_ttc and tva
2828
-			$fieldht='total_ht';
2829
-			$fieldtva='tva';
2830
-			$fieldlocaltax1='localtax1';
2831
-			$fieldlocaltax2='localtax2';
2832
-			$fieldttc='total_ttc';
2833
-			// Specific code for backward compatibility with old field names
2834
-			if ($this->element == 'facture' || $this->element == 'facturerec')             $fieldht='total';
2835
-			if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
2836
-			if ($this->element == 'propal')                                                $fieldttc='total';
2837
-			if ($this->element == 'expensereport')                                         $fieldtva='total_tva';
2838
-			if ($this->element == 'supplier_proposal')                                     $fieldttc='total';
2839
-
2840
-			if (empty($nodatabaseupdate))
2841
-			{
2842
-				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2843
-				$sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
2844
-				$sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
2845
-				$sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
2846
-				$sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
2847
-				$sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
2848
-						$sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
2849
-						$sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
2850
-						$sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
2851
-				$sql .= ' WHERE rowid = '.$this->id;
2852
-
2853
-
2854
-				dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2855
-				$resql=$this->db->query($sql);
2856
-				if (! $resql)
2857
-				{
2858
-					$error++;
2859
-					$this->error=$this->db->lasterror();
2860
-					$this->errors[]=$this->db->lasterror();
2861
-				}
2862
-			}
902
+        dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
903
+        if ($this->db->query($sql))
904
+        {
905
+            if (! $notrigger)
906
+            {
907
+                $result=$this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
908
+                if ($result < 0) { $this->db->rollback(); return -1; }
909
+            }
2863 910
 
2864
-			if (! $error)
2865
-			{
2866
-				return 1;
2867
-			}
2868
-			else
2869
-			{
2870
-				return -1;
2871
-			}
2872
-		}
2873
-		else
2874
-		{
2875
-			dol_print_error($this->db,'Bad request in update_price');
2876
-			return -1;
2877
-		}
2878
-	}
911
+            $this->db->commit();
912
+            return 1;
913
+        }
914
+        else
915
+        {
916
+            $this->error=$this->db->lasterror();
917
+            $this->db->rollback();
918
+            return -1;
919
+        }
920
+    }
2879 921
 
2880 922
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2881
-	/**
2882
-	 *	Add objects linked in llx_element_element.
2883
-	 *
2884
-	 *	@param		string	$origin		Linked element type
2885
-	 *	@param		int		$origin_id	Linked element id
2886
-	 *	@return		int					<=0 if KO, >0 if OK
2887
-	 *	@see		fetchObjectLinked, updateObjectLinked, deleteObjectLinked
2888
-	 */
2889
-	function add_object_linked($origin=null, $origin_id=null)
2890
-	{
923
+    /**
924
+     *    Delete all links between an object $this and all its contacts
925
+     *
926
+     *	  @param	string	$source		'' or 'internal' or 'external'
927
+     *	  @param	string	$code		Type of contact (code or id)
928
+     *    @return   int					>0 if OK, <0 if KO
929
+     */
930
+    function delete_linked_contact($source='',$code='')
931
+    {
2891 932
         // phpcs:enable
2892
-		$origin = (! empty($origin) ? $origin : $this->origin);
2893
-		$origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
2894
-
2895
-		// Special case
2896
-		if ($origin == 'order') $origin='commande';
2897
-		if ($origin == 'invoice') $origin='facture';
2898
-		if ($origin == 'invoice_template') $origin='facturerec';
2899
-    	if ($origin == 'supplierorder') $origin='order_supplier';
2900
-		$this->db->begin();
2901
-
2902
-		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
2903
-		$sql.= "fk_source";
2904
-		$sql.= ", sourcetype";
2905
-		$sql.= ", fk_target";
2906
-		$sql.= ", targettype";
2907
-		$sql.= ") VALUES (";
2908
-		$sql.= $origin_id;
2909
-		$sql.= ", '".$this->db->escape($origin)."'";
2910
-		$sql.= ", ".$this->id;
2911
-		$sql.= ", '".$this->db->escape($this->element)."'";
2912
-		$sql.= ")";
2913
-
2914
-		dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
2915
-		if ($this->db->query($sql))
2916
-	  	{
2917
-	  		$this->db->commit();
2918
-	  		return 1;
2919
-	  	}
2920
-	  	else
2921
-	  	{
2922
-	  		$this->error=$this->db->lasterror();
2923
-	  		$this->db->rollback();
2924
-	  		return 0;
2925
-	  	}
2926
-	}
2927
-
2928
-	/**
2929
-	 *	Fetch array of objects linked to current object (object of enabled modules only). Links are loaded into
2930
-	 *		this->linkedObjectsIds array and
2931
-	 *		this->linkedObjects array if $loadalsoobjects = 1
2932
-	 *  Possible usage for parameters:
2933
-	 *  - all parameters empty -> we look all link to current object (current object can be source or target)
2934
-	 *  - source id+type -> will get target list linked to source
2935
-	 *  - target id+type -> will get source list linked to target
2936
-	 *  - source id+type + target type -> will get target list of the type
2937
-	 *  - target id+type + target source -> will get source list of the type
2938
-	 *
2939
-	 *	@param	int		$sourceid			Object source id (if not defined, id of object)
2940
-	 *	@param  string	$sourcetype			Object source type (if not defined, element name of object)
2941
-	 *	@param  int		$targetid			Object target id (if not defined, id of object)
2942
-	 *	@param  string	$targettype			Object target type (if not defined, elemennt name of object)
2943
-	 *	@param  string	$clause				'OR' or 'AND' clause used when both source id and target id are provided
2944
-	 *  @param  int		$alsosametype		0=Return only links to object that differs from source type. 1=Include also link to objects of same type.
2945
-	 *  @param  string	$orderby			SQL 'ORDER BY' clause
2946
-	 *  @param	int		$loadalsoobjects	Load also array this->linkedObjects (Use 0 to increase performances)
2947
-	 *	@return int							<0 if KO, >0 if OK
2948
-	 *  @see	add_object_linked, updateObjectLinked, deleteObjectLinked
2949
-	 */
2950
-	function fetchObjectLinked($sourceid=null,$sourcetype='',$targetid=null,$targettype='',$clause='OR',$alsosametype=1,$orderby='sourcetype',$loadalsoobjects=1)
2951
-	{
2952
-		global $conf;
2953
-
2954
-		$this->linkedObjectsIds=array();
2955
-		$this->linkedObjects=array();
2956
-
2957
-		$justsource=false;
2958
-		$justtarget=false;
2959
-		$withtargettype=false;
2960
-		$withsourcetype=false;
2961
-
2962
-		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
2963
-		{
2964
-			$justsource=true;  // the source (id and type) is a search criteria
2965
-			if (! empty($targettype)) $withtargettype=true;
2966
-		}
2967
-		if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
2968
-		{
2969
-			$justtarget=true;  // the target (id and type) is a search criteria
2970
-			if (! empty($sourcetype)) $withsourcetype=true;
2971
-		}
2972
-
2973
-		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
2974
-		$targetid = (! empty($targetid) ? $targetid : $this->id);
2975
-		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
2976
-		$targettype = (! empty($targettype) ? $targettype : $this->element);
2977
-
2978
-		/*if (empty($sourceid) && empty($targetid))
2979
-		 {
2980
-		 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
2981
-		 return -1;
2982
-		 }*/
933
+        $temp = array();
934
+        $typeContact = $this->liste_type_contact($source,'',0,0,$code);
2983 935
 
2984
-		// Links between objects are stored in table element_element
2985
-		$sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
2986
-		$sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
2987
-		$sql.= " WHERE ";
2988
-		if ($justsource || $justtarget)
2989
-		{
2990
-			if ($justsource)
2991
-			{
2992
-				$sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
2993
-				if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
2994
-			}
2995
-			else if ($justtarget)
2996
-			{
2997
-				$sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
2998
-				if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
2999
-			}
3000
-		}
3001
-		else
3002
-		{
3003
-			$sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
3004
-			$sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
3005
-		}
3006
-		$sql .= ' ORDER BY '.$orderby;
3007
-
3008
-		dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3009
-		$resql = $this->db->query($sql);
3010
-		if ($resql)
3011
-		{
3012
-			$num = $this->db->num_rows($resql);
3013
-			$i = 0;
3014
-			while ($i < $num)
3015
-			{
3016
-				$obj = $this->db->fetch_object($resql);
3017
-				if ($justsource || $justtarget)
3018
-				{
3019
-					if ($justsource)
3020
-					{
3021
-						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3022
-					}
3023
-					else if ($justtarget)
3024
-					{
3025
-						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3026
-					}
3027
-				}
3028
-				else
3029
-				{
3030
-					if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
3031
-					{
3032
-						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3033
-					}
3034
-					if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
3035
-					{
3036
-						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3037
-					}
3038
-				}
3039
-				$i++;
3040
-			}
936
+        foreach($typeContact as $key => $value)
937
+        {
938
+            array_push($temp,$key);
939
+        }
940
+        $listId = implode(",", $temp);
3041 941
 
3042
-			if (! empty($this->linkedObjectsIds))
3043
-			{
3044
-				$tmparray = $this->linkedObjectsIds;
3045
-				foreach($tmparray as $objecttype => $objectids)       // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
3046
-				{
3047
-					// Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
3048
-					$module = $element = $subelement = $objecttype;
3049
-					if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
3050
-						&& preg_match('/^([^_]+)_([^_]+)/i',$objecttype,$regs))
3051
-					{
3052
-						$module = $element = $regs[1];
3053
-						$subelement = $regs[2];
3054
-					}
3055
-
3056
-					$classpath = $element.'/class';
3057
-					// To work with non standard classpath or module name
3058
-					if ($objecttype == 'facture')			{
3059
-						$classpath = 'compta/facture/class';
3060
-					}
3061
-					else if ($objecttype == 'facturerec')			{
3062
-						$classpath = 'compta/facture/class'; $module = 'facture';
3063
-					}
3064
-					else if ($objecttype == 'propal')			{
3065
-						$classpath = 'comm/propal/class';
3066
-					}
3067
-					else if ($objecttype == 'supplier_proposal')			{
3068
-						$classpath = 'supplier_proposal/class';
3069
-					}
3070
-					else if ($objecttype == 'shipping')			{
3071
-						$classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
3072
-					}
3073
-					else if ($objecttype == 'delivery')			{
3074
-						$classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
3075
-					}
3076
-					else if ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier')	{
3077
-						$classpath = 'fourn/class'; $module = 'fournisseur';
3078
-					}
3079
-					else if ($objecttype == 'fichinter')			{
3080
-						$classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
3081
-					}
3082
-					else if ($objecttype == 'subscription')			{
3083
-						$classpath = 'adherents/class'; $module = 'adherent';
3084
-					}
3085
-
3086
-					// Set classfile
3087
-					$classfile = strtolower($subelement); $classname = ucfirst($subelement);
3088
-
3089
-					if ($objecttype == 'order') {
3090
-						$classfile = 'commande'; $classname = 'Commande';
3091
-					}
3092
-					else if ($objecttype == 'invoice_supplier') {
3093
-						$classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
3094
-					}
3095
-					else if ($objecttype == 'order_supplier')   {
3096
-						$classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
3097
-					}
3098
-					else if ($objecttype == 'supplier_proposal')   {
3099
-						$classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
3100
-					}
3101
-					else if ($objecttype == 'facturerec')   {
3102
-						$classfile = 'facture-rec'; $classname = 'FactureRec';
3103
-					}
3104
-					else if ($objecttype == 'subscription')   {
3105
-						$classfile = 'subscription'; $classname = 'Subscription';
3106
-					}
3107
-
3108
-					// Here $module, $classfile and $classname are set
3109
-					if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
3110
-					{
3111
-						if ($loadalsoobjects)
3112
-						{
3113
-							dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
3114
-							//print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
3115
-							if (class_exists($classname))
3116
-							{
3117
-								foreach($objectids as $i => $objectid)	// $i is rowid into llx_element_element
3118
-								{
3119
-									$object = new $classname($this->db);
3120
-									$ret = $object->fetch($objectid);
3121
-									if ($ret >= 0)
3122
-									{
3123
-										$this->linkedObjects[$objecttype][$i] = $object;
3124
-									}
3125
-								}
3126
-							}
3127
-						}
3128
-					}
3129
-					else
3130
-					{
3131
-						unset($this->linkedObjectsIds[$objecttype]);
3132
-					}
3133
-				}
3134
-			}
3135
-			return 1;
3136
-		}
3137
-		else
3138
-		{
3139
-			dol_print_error($this->db);
3140
-			return -1;
3141
-		}
3142
-	}
3143
-
3144
-	/**
3145
-	 *	Update object linked of a current object
3146
-	 *
3147
-	 *	@param	int		$sourceid		Object source id
3148
-	 *	@param  string	$sourcetype		Object source type
3149
-	 *	@param  int		$targetid		Object target id
3150
-	 *	@param  string	$targettype		Object target type
3151
-	 *	@return							int	>0 if OK, <0 if KO
3152
-	 *	@see	add_object_linked, fetObjectLinked, deleteObjectLinked
3153
-	 */
3154
-	function updateObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='')
3155
-	{
3156
-		$updatesource=false;
3157
-		$updatetarget=false;
3158
-
3159
-		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
3160
-		else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
3161
-
3162
-		$sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
3163
-		if ($updatesource)
3164
-		{
3165
-			$sql.= "fk_source = ".$sourceid;
3166
-			$sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
3167
-			$sql.= " WHERE fk_target = ".$this->id;
3168
-			$sql.= " AND targettype = '".$this->db->escape($this->element)."'";
3169
-		}
3170
-		else if ($updatetarget)
3171
-		{
3172
-			$sql.= "fk_target = ".$targetid;
3173
-			$sql.= ", targettype = '".$this->db->escape($targettype)."'";
3174
-			$sql.= " WHERE fk_source = ".$this->id;
3175
-			$sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
3176
-		}
3177
-
3178
-		dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
3179
-		if ($this->db->query($sql))
3180
-		{
3181
-			return 1;
3182
-		}
3183
-		else
3184
-		{
3185
-			$this->error=$this->db->lasterror();
3186
-			return -1;
3187
-		}
3188
-	}
3189
-
3190
-	/**
3191
-	 *	Delete all links between an object $this
3192
-	 *
3193
-	 *	@param	int		$sourceid		Object source id
3194
-	 *	@param  string	$sourcetype		Object source type
3195
-	 *	@param  int		$targetid		Object target id
3196
-	 *	@param  string	$targettype		Object target type
3197
-	 *  @param	int		$rowid			Row id of line to delete. If defined, other parameters are not used.
3198
-	 *	@return     					int	>0 if OK, <0 if KO
3199
-	 *	@see	add_object_linked, updateObjectLinked, fetchObjectLinked
3200
-	 */
3201
-	function deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
3202
-	{
3203
-		$deletesource=false;
3204
-		$deletetarget=false;
3205
-
3206
-		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
3207
-		else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
3208
-
3209
-		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3210
-		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3211
-		$targetid = (! empty($targetid) ? $targetid : $this->id);
3212
-		$targettype = (! empty($targettype) ? $targettype : $this->element);
3213
-
3214
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
3215
-		$sql.= " WHERE";
3216
-		if ($rowid > 0)
3217
-		{
3218
-			$sql.=" rowid = ".$rowid;
3219
-		}
3220
-		else
3221
-		{
3222
-			if ($deletesource)
3223
-			{
3224
-				$sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3225
-				$sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
3226
-			}
3227
-			else if ($deletetarget)
3228
-			{
3229
-				$sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3230
-				$sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
3231
-			}
3232
-			else
3233
-			{
3234
-				$sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
3235
-				$sql.= " OR";
3236
-				$sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
3237
-			}
3238
-		}
3239
-
3240
-		dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
3241
-		if ($this->db->query($sql))
3242
-		{
3243
-			return 1;
3244
-		}
3245
-		else
3246
-		{
3247
-			$this->error=$this->db->lasterror();
3248
-			$this->errors[]=$this->error;
3249
-			return -1;
3250
-		}
3251
-	}
3252
-
3253
-	/**
3254
-	 *      Set status of an object
3255
-	 *
3256
-	 *      @param	int		$status			Status to set
3257
-	 *      @param	int		$elementId		Id of element to force (use this->id by default)
3258
-	 *      @param	string	$elementType	Type of element to force (use this->table_element by default)
3259
-	 *      @param	string	$trigkey		Trigger key to use for trigger
3260
-	 *      @return int						<0 if KO, >0 if OK
3261
-	 */
3262
-	function setStatut($status, $elementId=null, $elementType='', $trigkey='')
3263
-	{
3264
-		global $user,$langs,$conf;
3265
-
3266
-		$savElementId=$elementId;  // To be used later to know if we were using the method using the id of this or not.
3267
-
3268
-		$elementId = (!empty($elementId)?$elementId:$this->id);
3269
-		$elementTable = (!empty($elementType)?$elementType:$this->table_element);
3270
-
3271
-		$this->db->begin();
3272
-
3273
-		$fieldstatus="fk_statut";
3274
-		if ($elementTable == 'facture_rec') $fieldstatus="suspended";
3275
-		if ($elementTable == 'mailing') $fieldstatus="statut";
3276
-		if ($elementTable == 'cronjob') $fieldstatus="status";
3277
-		if ($elementTable == 'user') $fieldstatus="statut";
3278
-		if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
3279
-		if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
3280
-
3281
-		$sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
3282
-		$sql.= " SET ".$fieldstatus." = ".$status;
3283
-		// If status = 1 = validated, update also fk_user_valid
3284
-		if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
3285
-		$sql.= " WHERE rowid=".$elementId;
3286
-
3287
-		dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
3288
-		if ($this->db->query($sql))
3289
-		{
3290
-			$error = 0;
3291
-
3292
-			// Try autoset of trigkey
3293
-			if (empty($trigkey))
3294
-			{
3295
-				if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN';   // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
3296
-				if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
3297
-				if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE';  // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
3298
-				if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
3299
-				if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
3300
-				if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
3301
-			}
3302
-
3303
-			if ($trigkey)
3304
-			{
3305
-				// Appel des triggers
3306
-				include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
3307
-				$interface=new Interfaces($this->db);
3308
-				$result=$interface->run_triggers($trigkey,$this,$user,$langs,$conf);
3309
-				if ($result < 0) {
3310
-					$error++; $this->errors=$interface->errors;
3311
-				}
3312
-				// Fin appel triggers
3313
-			}
942
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
943
+        $sql.= " WHERE element_id = ".$this->id;
944
+        if ($listId)
945
+            $sql.= " AND fk_c_type_contact IN (".$listId.")";
3314 946
 
3315
-			if (! $error)
3316
-			{
3317
-				$this->db->commit();
947
+        dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
948
+        if ($this->db->query($sql))
949
+        {
950
+            return 1;
951
+        }
952
+        else
953
+        {
954
+            $this->error=$this->db->lasterror();
955
+            return -1;
956
+        }
957
+    }
3318 958
 
3319
-				if (empty($savElementId))    // If the element we update was $this (so $elementId is null)
3320
-				{
3321
-					$this->statut = $status;
3322
-					$this->status = $status;
3323
-				}
959
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
960
+    /**
961
+     *    Get array of all contacts for an object
962
+     *
963
+     *    @param	int			$statut		Status of links to get (-1=all)
964
+     *    @param	string		$source		Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
965
+     *    @param	int         $list       0:Return array contains all properties, 1:Return array contains just id
966
+     *    @param    string      $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
967
+     *    @return	array|int		        Array of contacts, -1 if error
968
+     */
969
+    function liste_contact($statut=-1,$source='external',$list=0,$code='')
970
+    {
971
+        // phpcs:enable
972
+        global $langs;
973
+
974
+        $tab=array();
975
+
976
+        $sql = "SELECT ec.rowid, ec.statut as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact";    // This field contains id of llx_socpeople or id of llx_user
977
+        if ($source == 'internal') $sql.=", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
978
+        if ($source == 'external' || $source == 'thirdparty') $sql.=", t.fk_soc as socid, t.statut as statuscontact";
979
+        $sql.= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
980
+        $sql.= ", tc.source, tc.element, tc.code, tc.libelle";
981
+        $sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
982
+        $sql.= ", ".MAIN_DB_PREFIX."element_contact ec";
983
+        if ($source == 'internal') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
984
+        if ($source == 'external'|| $source == 'thirdparty') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
985
+        $sql.= " WHERE ec.element_id =".$this->id;
986
+        $sql.= " AND ec.fk_c_type_contact=tc.rowid";
987
+        $sql.= " AND tc.element='".$this->db->escape($this->element)."'";
988
+        if ($code) $sql.= " AND tc.code = '".$this->db->escape($code)."'";
989
+        if ($source == 'internal') $sql.= " AND tc.source = 'internal'";
990
+        if ($source == 'external' || $source == 'thirdparty') $sql.= " AND tc.source = 'external'";
991
+        $sql.= " AND tc.active=1";
992
+        if ($statut >= 0) $sql.= " AND ec.statut = '".$statut."'";
993
+        $sql.=" ORDER BY t.lastname ASC";
994
+
995
+        dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
996
+        $resql=$this->db->query($sql);
997
+        if ($resql)
998
+        {
999
+            $num=$this->db->num_rows($resql);
1000
+            $i=0;
1001
+            while ($i < $num)
1002
+            {
1003
+                $obj = $this->db->fetch_object($resql);
1004
+
1005
+                if (! $list)
1006
+                {
1007
+                    $transkey="TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1008
+                    $libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1009
+                    $tab[$i]=array('source'=>$obj->source,'socid'=>$obj->socid,'id'=>$obj->id,
1010
+                                    'nom'=>$obj->lastname,      // For backward compatibility
1011
+                                    'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
1012
+                                    'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
1013
+                }
1014
+                else
1015
+                {
1016
+                    $tab[$i]=$obj->id;
1017
+                }
1018
+
1019
+                $i++;
1020
+            }
3324 1021
 
3325
-				return 1;
3326
-			}
3327
-			else
3328
-			{
3329
-				$this->db->rollback();
3330
-				dol_syslog(get_class($this)."::setStatus ".$this->error,LOG_ERR);
3331
-				return -1;
3332
-			}
3333
-		}
3334
-		else
3335
-		{
3336
-			$this->error=$this->db->lasterror();
3337
-			$this->db->rollback();
3338
-			return -1;
3339
-		}
3340
-	}
3341
-
3342
-
3343
-	/**
3344
-	 *  Load type of canvas of an object if it exists
3345
-	 *
3346
-	 *  @param      int		$id     Record id
3347
-	 *  @param      string	$ref    Record ref
3348
-	 *  @return		int				<0 if KO, 0 if nothing done, >0 if OK
3349
-	 */
3350
-	function getCanvas($id=0,$ref='')
3351
-	{
3352
-		global $conf;
3353
-
3354
-		if (empty($id) && empty($ref)) return 0;
3355
-		if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0;    // To increase speed. Not enabled by default.
3356
-
3357
-		// Clean parameters
3358
-		$ref = trim($ref);
3359
-
3360
-		$sql = "SELECT rowid, canvas";
3361
-		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
3362
-		$sql.= " WHERE entity IN (".getEntity($this->element).")";
3363
-		if (! empty($id))  $sql.= " AND rowid = ".$id;
3364
-		if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
3365
-
3366
-		$resql = $this->db->query($sql);
3367
-		if ($resql)
3368
-		{
3369
-			$obj = $this->db->fetch_object($resql);
3370
-			if ($obj)
3371
-			{
3372
-				$this->canvas   = $obj->canvas;
3373
-				return 1;
3374
-			}
3375
-			else return 0;
3376
-		}
3377
-		else
3378
-		{
3379
-			dol_print_error($this->db);
3380
-			return -1;
3381
-		}
3382
-	}
3383
-
3384
-
3385
-	/**
3386
-	 * 	Get special code of a line
3387
-	 *
3388
-	 * 	@param	int		$lineid		Id of line
3389
-	 * 	@return	int					Special code
3390
-	 */
3391
-	function getSpecialCode($lineid)
3392
-	{
3393
-		$sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3394
-		$sql.= ' WHERE rowid = '.$lineid;
3395
-		$resql = $this->db->query($sql);
3396
-		if ($resql)
3397
-		{
3398
-			$row = $this->db->fetch_row($resql);
3399
-			return $row[0];
3400
-		}
3401
-	}
3402
-
3403
-	/**
3404
-	 *  Function to check if an object is used by others.
3405
-	 *  Check is done into this->childtables. There is no check into llx_element_element.
3406
-	 *
3407
-	 *  @param	int		$id			Force id of object
3408
-	 *  @return	int					<0 if KO, 0 if not used, >0 if already used
3409
-	 */
3410
-	function isObjectUsed($id=0)
3411
-	{
3412
-		global $langs;
3413
-
3414
-		if (empty($id)) $id=$this->id;
3415
-
3416
-		// Check parameters
3417
-		if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
3418
-		{
3419
-			dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3420
-			return -1;
3421
-		}
3422
-
3423
-		$arraytoscan = $this->childtables;
3424
-		// For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3425
-		$tmparray=array_keys($this->childtables);
3426
-		if (is_numeric($tmparray[0]))
3427
-		{
3428
-			$arraytoscan = array_flip($this->childtables);
3429
-		}
3430
-
3431
-		// Test if child exists
3432
-		$haschild=0;
3433
-		foreach($arraytoscan as $table => $elementname)
3434
-		{
3435
-			//print $id.'-'.$table.'-'.$elementname.'<br>';
3436
-			// Check if third party can be deleted
3437
-			$sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3438
-			$sql.= " WHERE ".$this->fk_element." = ".$id;
3439
-			$resql=$this->db->query($sql);
3440
-			if ($resql)
3441
-			{
3442
-				$obj=$this->db->fetch_object($resql);
3443
-				if ($obj->nb > 0)
3444
-				{
3445
-					$langs->load("errors");
3446
-					//print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3447
-					$haschild += $obj->nb;
3448
-					if (is_numeric($elementname))	// old usage
3449
-					{
3450
-						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table);
3451
-					}
3452
-					else	// new usage: $elementname=Translation key
3453
-					{
3454
-						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3455
-					}
3456
-					break;    // We found at least one, we stop here
3457
-				}
3458
-			}
3459
-			else
3460
-			{
3461
-				$this->errors[]=$this->db->lasterror();
3462
-				return -1;
3463
-			}
3464
-		}
3465
-		if ($haschild > 0)
3466
-		{
3467
-			$this->errors[]="ErrorRecordHasChildren";
3468
-			return $haschild;
3469
-		}
3470
-		else return 0;
3471
-	}
3472
-
3473
-	/**
3474
-	 *  Function to say how many lines object contains
3475
-	 *
3476
-	 *	@param	int		$predefined		-1=All, 0=Count free product/service only, 1=Count predefined product/service only, 2=Count predefined product, 3=Count predefined service
3477
-	 *  @return	int						<0 if KO, 0 if no predefined products, nb of lines with predefined products if found
3478
-	 */
3479
-	function hasProductsOrServices($predefined=-1)
3480
-	{
3481
-		$nb=0;
3482
-
3483
-		foreach($this->lines as $key => $val)
3484
-		{
3485
-			$qualified=0;
3486
-			if ($predefined == -1) $qualified=1;
3487
-			if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3488
-			if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3489
-			if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3490
-			if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3491
-			if ($qualified) $nb++;
3492
-		}
3493
-		dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3494
-		return $nb;
3495
-	}
3496
-
3497
-	/**
3498
-	 * Function that returns the total amount HT of discounts applied for all lines.
3499
-	 *
3500
-	 * @return 	float
3501
-	 */
3502
-	function getTotalDiscount()
3503
-	{
3504
-		$total_discount=0.00;
3505
-
3506
-		$sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3507
-		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3508
-		$sql.= " WHERE ".$this->fk_element." = ".$this->id;
3509
-
3510
-		dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3511
-		$resql = $this->db->query($sql);
3512
-		if ($resql)
3513
-		{
3514
-			$num=$this->db->num_rows($resql);
3515
-			$i=0;
3516
-			while ($i < $num)
3517
-			{
3518
-				$obj = $this->db->fetch_object($resql);
3519
-
3520
-				$pu_ht = $obj->pu_ht;
3521
-				$qty= $obj->qty;
3522
-				$total_ht = $obj->total_ht;
3523
-
3524
-				$total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3525
-				$total_discount += $total_discount_line;
3526
-
3527
-				$i++;
3528
-			}
3529
-		}
3530
-
3531
-		//print $total_discount; exit;
3532
-		return price2num($total_discount);
3533
-	}
3534
-
3535
-
3536
-	/**
3537
-	 * Return into unit=0, the calculated total of weight and volume of all lines * qty
3538
-	 * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
3539
-	 *
3540
-	 * @return  array                           array('weight'=>...,'volume'=>...)
3541
-	 */
3542
-	function getTotalWeightVolume()
3543
-	{
3544
-		$totalWeight = 0;
3545
-		$totalVolume = 0;
3546
-		// defined for shipment only
3547
-		$totalOrdered = '';
3548
-		// defined for shipment only
3549
-		$totalToShip = '';
3550
-
3551
-		foreach ($this->lines as $line)
3552
-		{
3553
-			if (isset($line->qty_asked))
3554
-			{
3555
-				if (empty($totalOrdered)) $totalOrdered=0;  // Avoid warning because $totalOrdered is ''
3556
-				$totalOrdered+=$line->qty_asked;    // defined for shipment only
3557
-			}
3558
-			if (isset($line->qty_shipped))
3559
-			{
3560
-				if (empty($totalToShip)) $totalToShip=0;    // Avoid warning because $totalToShip is ''
3561
-				$totalToShip+=$line->qty_shipped;   // defined for shipment only
3562
-            }else if ($line->element == 'commandefournisseurdispatch' && isset($line->qty))
1022
+            return $tab;
1023
+        }
1024
+        else
1025
+        {
1026
+            $this->error=$this->db->lasterror();
1027
+            dol_print_error($this->db);
1028
+            return -1;
1029
+        }
1030
+    }
1031
+
1032
+
1033
+    /**
1034
+     * 		Update status of a contact linked to object
1035
+     *
1036
+     * 		@param	int		$rowid		Id of link between object and contact
1037
+     * 		@return	int					<0 if KO, >=0 if OK
1038
+     */
1039
+    function swapContactStatus($rowid)
1040
+    {
1041
+        $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1042
+        $sql.= " tc.code, tc.libelle";
1043
+        //$sql.= ", s.fk_soc";
1044
+        $sql.= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
1045
+        //$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as s ON ec.fk_socpeople=s.rowid";	// Si contact de type external, alors il est lie a une societe
1046
+        $sql.= " WHERE ec.rowid =".$rowid;
1047
+        $sql.= " AND ec.fk_c_type_contact=tc.rowid";
1048
+        $sql.= " AND tc.element = '".$this->db->escape($this->element)."'";
1049
+
1050
+        dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1051
+        $resql=$this->db->query($sql);
1052
+        if ($resql)
1053
+        {
1054
+            $obj = $this->db->fetch_object($resql);
1055
+            $newstatut = ($obj->statut == 4) ? 5 : 4;
1056
+            $result = $this->update_contact($rowid, $newstatut);
1057
+            $this->db->free($resql);
1058
+            return $result;
1059
+        }
1060
+        else
1061
+        {
1062
+            $this->error=$this->db->error();
1063
+            dol_print_error($this->db);
1064
+            return -1;
1065
+        }
1066
+    }
1067
+
1068
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1069
+    /**
1070
+     *      Return array with list of possible values for type of contacts
1071
+     *
1072
+     *      @param	string	$source     'internal', 'external' or 'all'
1073
+     *      @param	string	$order		Sort order by : 'position', 'code', 'rowid'...
1074
+     *      @param  int		$option     0=Return array id->label, 1=Return array code->label
1075
+     *      @param  int		$activeonly 0=all status of contact, 1=only the active
1076
+     *		@param	string	$code		Type of contact (Example: 'CUSTOMER', 'SERVICE')
1077
+     *      @return array       		Array list of type of contacts (id->label if option=0, code->label if option=1)
1078
+     */
1079
+    function liste_type_contact($source='internal', $order='position', $option=0, $activeonly=0, $code='')
1080
+    {
1081
+        // phpcs:enable
1082
+        global $langs;
1083
+
1084
+        if (empty($order)) $order='position';
1085
+        if ($order == 'position') $order.=',code';
1086
+
1087
+        $tab = array();
1088
+        $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1089
+        $sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1090
+        $sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
1091
+        if ($activeonly == 1) $sql.= " AND tc.active=1"; // only the active types
1092
+        if (! empty($source) && $source != 'all') $sql.= " AND tc.source='".$this->db->escape($source)."'";
1093
+        if (! empty($code)) $sql.= " AND tc.code='".$this->db->escape($code)."'";
1094
+        $sql.= $this->db->order($order,'ASC');
1095
+
1096
+        //print "sql=".$sql;
1097
+        $resql=$this->db->query($sql);
1098
+        if ($resql)
1099
+        {
1100
+            $num=$this->db->num_rows($resql);
1101
+            $i=0;
1102
+            while ($i < $num)
3563 1103
             {
3564
-                if (empty($totalToShip)) $totalToShip=0;
3565
-                $totalToShip+=$line->qty;   // defined for reception only
3566
-			}
3567
-
3568
-			// Define qty, weight, volume, weight_units, volume_units
3569
-			if ($this->element == 'shipping') {
3570
-				// for shipments
3571
-				$qty = $line->qty_shipped ? $line->qty_shipped : 0;
3572
-			}
3573
-			else {
3574
-				$qty = $line->qty ? $line->qty : 0;
3575
-			}
3576
-
3577
-			$weight = $line->weight ? $line->weight : 0;
3578
-            ($weight==0 && !empty($line->product->weight))? $weight=$line->product->weight: 0;
3579
-			$volume = $line->volume ? $line->volume : 0;
3580
-			($volume==0 && !empty($line->product->volume))? $volume=$line->product->volume: 0;
1104
+                $obj = $this->db->fetch_object($resql);
3581 1105
 
3582
-			$weight_units=$line->weight_units;
3583
-			($weight_units==0 && !empty($line->product->weight_units))? $weight_units=$line->product->weight_units: 0;
3584
-			$volume_units=$line->volume_units;
3585
-			($volume_units==0 && !empty($line->product->volume_units))? $volume_units=$line->product->volume_units: 0;
1106
+                $transkey="TypeContact_".$this->element."_".$source."_".$obj->code;
1107
+                $libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1108
+                if (empty($option)) $tab[$obj->rowid]=$libelle_type;
1109
+                else $tab[$obj->code]=$libelle_type;
1110
+                $i++;
1111
+            }
1112
+            return $tab;
1113
+        }
1114
+        else
1115
+        {
1116
+            $this->error=$this->db->lasterror();
1117
+            //dol_print_error($this->db);
1118
+            return null;
1119
+        }
1120
+    }
3586 1121
 
3587
-			$weightUnit=0;
3588
-			$volumeUnit=0;
3589
-			if (! empty($weight_units)) $weightUnit = $weight_units;
3590
-			if (! empty($volume_units)) $volumeUnit = $volume_units;
1122
+    /**
1123
+     *      Return id of contacts for a source and a contact code.
1124
+     *      Example: contact client de facturation ('external', 'BILLING')
1125
+     *      Example: contact client de livraison ('external', 'SHIPPING')
1126
+     *      Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
1127
+     *
1128
+     *		@param	string	$source		'external' or 'internal'
1129
+     *		@param	string	$code		'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
1130
+     *		@param	int		$status		limited to a certain status
1131
+     *      @return array       		List of id for such contacts
1132
+     */
1133
+    function getIdContact($source,$code,$status=0)
1134
+    {
1135
+        global $conf;
3591 1136
 
3592
-			if (empty($totalWeight)) $totalWeight=0;  // Avoid warning because $totalWeight is ''
3593
-			if (empty($totalVolume)) $totalVolume=0;  // Avoid warning because $totalVolume is ''
1137
+        $result=array();
1138
+        $i=0;
1139
+        //cas particulier pour les expeditions
1140
+        if($this->element=='shipping' && $this->origin_id != 0) {
1141
+            $id=$this->origin_id;
1142
+            $element='commande';
1143
+        } else if($this->element=='reception' && $this->origin_id != 0) {
1144
+            $id=$this->origin_id;
1145
+            $element='order_supplier';
1146
+        } else {
1147
+            $id=$this->id;
1148
+            $element=$this->element;
1149
+        }
3594 1150
 
3595
-			//var_dump($line->volume_units);
3596
-			if ($weight_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3597
-			{
3598
-				$trueWeightUnit=pow(10, $weightUnit);
3599
-				$totalWeight += $weight * $qty * $trueWeightUnit;
3600
-			}
3601
-			else {
3602
-		if ($weight_units == 99) {
3603
-			// conversion 1 Pound = 0.45359237 KG
3604
-			$trueWeightUnit = 0.45359237;
3605
-			$totalWeight += $weight * $qty * $trueWeightUnit;
3606
-		} elseif ($weight_units == 98) {
3607
-			// conversion 1 Ounce = 0.0283495 KG
3608
-			$trueWeightUnit = 0.0283495;
3609
-			$totalWeight += $weight * $qty * $trueWeightUnit;
3610
-		}
3611
-		else
3612
-					$totalWeight += $weight * $qty;   // This may be wrong if we mix different units
3613
-			}
3614
-			if ($volume_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3615
-			{
3616
-				//print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3617
-				$trueVolumeUnit=pow(10, $volumeUnit);
3618
-				//print $line->volume;
3619
-				$totalVolume += $volume * $qty * $trueVolumeUnit;
3620
-			}
3621
-			else
3622
-			{
3623
-				$totalVolume += $volume * $qty;   // This may be wrong if we mix different units
3624
-			}
3625
-		}
3626
-
3627
-		return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3628
-	}
3629
-
3630
-
3631
-	/**
3632
-	 *	Set extra parameters
3633
-	 *
3634
-	 *	@return	int      <0 if KO, >0 if OK
3635
-	 */
3636
-	function setExtraParameters()
3637
-	{
3638
-		$this->db->begin();
3639
-
3640
-		$extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3641
-
3642
-		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3643
-		$sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3644
-		$sql.= " WHERE rowid = ".$this->id;
3645
-
3646
-		dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3647
-		$resql = $this->db->query($sql);
3648
-		if (! $resql)
3649
-		{
3650
-			$this->error=$this->db->lasterror();
3651
-			$this->db->rollback();
3652
-			return -1;
3653
-		}
3654
-		else
3655
-		{
3656
-			$this->db->commit();
3657
-			return 1;
3658
-		}
3659
-	}
1151
+        $sql = "SELECT ec.fk_socpeople";
1152
+        $sql.= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
1153
+        if ($source == 'internal') $sql.= " ".MAIN_DB_PREFIX."user as c,";
1154
+        if ($source == 'external') $sql.= " ".MAIN_DB_PREFIX."socpeople as c,";
1155
+        $sql.= " ".MAIN_DB_PREFIX."c_type_contact as tc";
1156
+        $sql.= " WHERE ec.element_id = ".$id;
1157
+        $sql.= " AND ec.fk_socpeople = c.rowid";
1158
+        if ($source == 'internal') $sql.= " AND c.entity IN (".getEntity('user').")";
1159
+        if ($source == 'external') $sql.= " AND c.entity IN (".getEntity('societe').")";
1160
+        $sql.= " AND ec.fk_c_type_contact = tc.rowid";
1161
+        $sql.= " AND tc.element = '".$element."'";
1162
+        $sql.= " AND tc.source = '".$source."'";
1163
+        $sql.= " AND tc.code = '".$code."'";
1164
+        $sql.= " AND tc.active = 1";
1165
+        if ($status) $sql.= " AND ec.statut = ".$status;
1166
+
1167
+        dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1168
+        $resql=$this->db->query($sql);
1169
+        if ($resql)
1170
+        {
1171
+            while ($obj = $this->db->fetch_object($resql))
1172
+            {
1173
+                $result[$i]=$obj->fk_socpeople;
1174
+                $i++;
1175
+            }
1176
+        }
1177
+        else
1178
+        {
1179
+            $this->error=$this->db->error();
1180
+            return null;
1181
+        }
3660 1182
 
1183
+        return $result;
1184
+    }
3661 1185
 
3662 1186
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3663
-	/**
3664
-	 *    Return incoterms informations
3665
-	 *    TODO Use a cache for label get
3666
-	 *
3667
-	 *    @return	string	incoterms info
3668
-	 */
3669
-	function display_incoterms()
3670
-	{
1187
+    /**
1188
+     *		Load object contact with id=$this->contactid into $this->contact
1189
+     *
1190
+     *		@param	int		$contactid      Id du contact. Use this->contactid if empty.
1191
+     *		@return	int						<0 if KO, >0 if OK
1192
+     */
1193
+    function fetch_contact($contactid=null)
1194
+    {
3671 1195
         // phpcs:enable
3672
-		$out = '';
3673
-		$this->libelle_incoterms = '';
3674
-		if (!empty($this->fk_incoterms))
3675
-		{
3676
-			$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3677
-			$result = $this->db->query($sql);
3678
-			if ($result)
3679
-			{
3680
-				$res = $this->db->fetch_object($result);
3681
-				$out .= $res->code;
3682
-			}
3683
-		}
3684
-
3685
-		$out .= (($res->code && $this->location_incoterms)?' - ':'').$this->location_incoterms;
3686
-
3687
-		return $out;
3688
-	}
3689
-
3690
-	/**
3691
-	 *    Return incoterms informations for pdf display
3692
-	 *
3693
-	 *    @return	string		incoterms info
3694
-	 */
3695
-	function getIncotermsForPDF()
3696
-	{
3697
-		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3698
-		$resql = $this->db->query($sql);
3699
-		if ($resql)
3700
-		{
3701
-			$num = $this->db->num_rows($resql);
3702
-			if ($num > 0)
3703
-			{
3704
-				$res = $this->db->fetch_object($resql);
3705
-				return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3706
-			}
3707
-			else
3708
-			{
3709
-				return '';
3710
-			}
3711
-		}
3712
-		else
3713
-		{
3714
-			$this->errors[] = $this->db->lasterror();
3715
-			return false;
3716
-		}
3717
-	}
3718
-
3719
-	/**
3720
-	 *    Define incoterms values of current object
3721
-	 *
3722
-	 *    @param	int		$id_incoterm     Id of incoterm to set or '' to remove
3723
-	 * 	  @param 	string  $location		 location of incoterm
3724
-	 *    @return	int     		<0 if KO, >0 if OK
3725
-	 */
3726
-	function setIncoterms($id_incoterm, $location)
3727
-	{
3728
-		if ($this->id && $this->table_element)
3729
-		{
3730
-			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3731
-			$sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3732
-			$sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3733
-			$sql.= " WHERE rowid = " . $this->id;
3734
-			dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3735
-			$resql=$this->db->query($sql);
3736
-			if ($resql)
3737
-			{
3738
-				$this->fk_incoterms = $id_incoterm;
3739
-				$this->location_incoterms = $location;
1196
+        if (empty($contactid)) $contactid=$this->contactid;
3740 1197
 
3741
-				$sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3742
-				$res = $this->db->query($sql);
3743
-				if ($res)
3744
-				{
3745
-					$obj = $this->db->fetch_object($res);
3746
-					$this->libelle_incoterms = $obj->libelle;
3747
-				}
3748
-				return 1;
3749
-			}
3750
-			else
3751
-			{
3752
-				$this->errors[] = $this->db->lasterror();
3753
-				return -1;
3754
-			}
3755
-		}
3756
-		else return -1;
3757
-	}
3758
-
3759
-
3760
-	// --------------------
3761
-	// TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3762
-	// --------------------
3763
-
3764
-	/* This is to show add lines */
3765
-
3766
-	/**
3767
-	 *	Show add free and predefined products/services form
3768
-	 *
3769
-	 *  @param	int		        $dateSelector       1=Show also date range input fields
3770
-	 *  @param	Societe			$seller				Object thirdparty who sell
3771
-	 *  @param	Societe			$buyer				Object thirdparty who buy
3772
-	 *	@return	void
3773
-	 */
3774
-	function formAddObjectLine($dateSelector, $seller, $buyer)
3775
-	{
3776
-		global $conf,$user,$langs,$object,$hookmanager;
3777
-		global $form,$bcnd,$var;
3778
-
3779
-		// Line extrafield
3780
-		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3781
-		$extrafieldsline = new ExtraFields($this->db);
3782
-		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3783
-
3784
-		// Output template part (modules that overwrite templates must declare this into descriptor)
3785
-		// Use global variables + $dateSelector + $seller and $buyer
3786
-		$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3787
-		foreach($dirtpls as $reldir)
3788
-		{
3789
-			$tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
3790
-			if (empty($conf->file->strict_mode)) {
3791
-				$res=@include $tpl;
3792
-			} else {
3793
-				$res=include $tpl; // for debug
3794
-			}
3795
-			if ($res) break;
3796
-		}
3797
-	}
3798
-
3799
-
3800
-
3801
-	/* This is to show array of line of details */
3802
-
3803
-
3804
-	/**
3805
-	 *	Return HTML table for object lines
3806
-	 *	TODO Move this into an output class file (htmlline.class.php)
3807
-	 *	If lines are into a template, title must also be into a template
3808
-	 *	But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
3809
-	 *
3810
-	 *	@param	string		$action				Action code
3811
-	 *	@param  string		$seller            	Object of seller third party
3812
-	 *	@param  string  	$buyer             	Object of buyer third party
3813
-	 *	@param	int			$selected		   	Object line selected
3814
-	 *	@param  int	    	$dateSelector      	1=Show also date range input fields
3815
-	 *	@return	void
3816
-	 */
3817
-	function printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0)
3818
-	{
3819
-		global $conf, $hookmanager, $langs, $user;
3820
-		// TODO We should not use global var for this !
3821
-		global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
3822
-
3823
-		// Define usemargins
3824
-		$usemargins=0;
3825
-		if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element,array('facture','propal','commande'))) $usemargins=1;
3826
-
3827
-		$num = count($this->lines);
3828
-
3829
-		// Line extrafield
3830
-		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3831
-		$extrafieldsline = new ExtraFields($this->db);
3832
-		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3833
-
3834
-		$parameters = array('num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3835
-		$reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3836
-		if (empty($reshook))
3837
-		{
3838
-			// Title line
3839
-		    print "<thead>\n";
3840
-
3841
-			print '<tr class="liste_titre nodrag nodrop">';
3842
-
3843
-			// Adds a line numbering column
3844
-			if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) print '<td class="linecolnum" align="center" width="5">&nbsp;</td>';
3845
-
3846
-			// Description
3847
-			print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
3848
-
3849
-			if ($this->element == 'supplier_proposal' || $this->element == 'order_supplier' || $this->element == 'invoice_supplier')
3850
-			{
3851
-				print '<td class="linerefsupplier"><span id="title_fourn_ref">'.$langs->trans("SupplierRef").'</span></td>';
3852
-			}
1198
+        if (empty($contactid)) return 0;
3853 1199
 
3854
-			// VAT
3855
-			print '<td class="linecolvat" align="right" width="80">'.$langs->trans('VAT').'</td>';
1200
+        require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1201
+        $contact = new Contact($this->db);
1202
+        $result=$contact->fetch($contactid);
1203
+        $this->contact = $contact;
1204
+        return $result;
1205
+    }
3856 1206
 
3857
-			// Price HT
3858
-			print '<td class="linecoluht" align="right" width="80">'.$langs->trans('PriceUHT').'</td>';
1207
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1208
+    /**
1209
+     *    	Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
1210
+     *
1211
+     *		@param		int		$force_thirdparty_id	Force thirdparty id
1212
+     *		@return		int								<0 if KO, >0 if OK
1213
+     */
1214
+    function fetch_thirdparty($force_thirdparty_id=0)
1215
+    {
1216
+        // phpcs:enable
1217
+        global $conf;
3859 1218
 
3860
-			// Multicurrency
3861
-			if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoluht_currency" align="right" width="80">'.$langs->trans('PriceUHTCurrency', $this->multicurrency_code).'</td>';
1219
+        if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
1220
+            return 0;
3862 1221
 
3863
-			if ($inputalsopricewithtax) print '<td align="right" width="80">'.$langs->trans('PriceUTTC').'</td>';
1222
+        require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
3864 1223
 
3865
-			// Qty
3866
-			print '<td class="linecolqty" align="right">'.$langs->trans('Qty').'</td>';
1224
+        $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1225
+        if ($force_thirdparty_id)
1226
+            $idtofetch = $force_thirdparty_id;
3867 1227
 
3868
-			if($conf->global->PRODUCT_USE_UNITS)
3869
-			{
3870
-				print '<td class="linecoluseunit" align="left">'.$langs->trans('Unit').'</td>';
3871
-			}
1228
+        if ($idtofetch) {
1229
+            $thirdparty = new Societe($this->db);
1230
+            $result = $thirdparty->fetch($idtofetch);
1231
+            $this->thirdparty = $thirdparty;
3872 1232
 
3873
-			// Reduction short
3874
-			print '<td class="linecoldiscount" align="right">'.$langs->trans('ReductionShort').'</td>';
1233
+            // Use first price level if level not defined for third party
1234
+            if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1235
+                $this->thirdparty->price_level = 1;
1236
+            }
3875 1237
 
3876
-			if ($this->situation_cycle_ref) {
3877
-				print '<td class="linecolcycleref" align="right">' . $langs->trans('Progress') . '</td>';
3878
-			}
1238
+            return $result;
1239
+        } else
1240
+            return -1;
1241
+    }
3879 1242
 
3880
-			if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id))
3881
-			{
3882
-				if (!empty($user->rights->margins->creer))
3883
-				{
3884
-					if ($conf->global->MARGIN_TYPE == "1")
3885
-						print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('BuyingPrice').'</td>';
3886
-					else
3887
-						print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('CostPrice').'</td>';
3888
-				}
3889 1243
 
3890
-				if (! empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous)
3891
-					print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarginRate').'</td>';
3892
-				if (! empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous)
3893
-					print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarkRate').'</td>';
3894
-			}
1244
+    /**
1245
+     * Looks for an object with ref matching the wildcard provided
1246
+     * It does only work when $this->table_ref_field is set
1247
+     *
1248
+     * @param string $ref Wildcard
1249
+     * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
1250
+     */
1251
+    public function fetchOneLike($ref)
1252
+    {
1253
+        if (!$this->table_ref_field) {
1254
+            return 0;
1255
+        }
3895 1256
 
3896
-			// Total HT
3897
-			print '<td class="linecolht" align="right">'.$langs->trans('TotalHTShort').'</td>';
1257
+        $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
3898 1258
 
3899
-			// Multicurrency
3900
-			if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoltotalht_currency" align="right">'.$langs->trans('TotalHTShortCurrency', $this->multicurrency_code).'</td>';
1259
+        $query = $this->db->query($sql);
3901 1260
 
3902
-			if ($outputalsopricetotalwithtax) print '<td align="right" width="80">'.$langs->trans('TotalTTCShort').'</td>';
1261
+        if (!$this->db->num_rows($query)) {
1262
+            return 0;
1263
+        }
3903 1264
 
3904
-			print '<td class="linecoledit"></td>';  // No width to allow autodim
1265
+        $result = $this->db->fetch_object($query);
3905 1266
 
3906
-			print '<td class="linecoldelete" width="10"></td>';
1267
+        return $this->fetch($result->rowid);
1268
+    }
3907 1269
 
3908
-			print '<td class="linecolmove" width="10"></td>';
1270
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1271
+    /**
1272
+     *	Load data for barcode into properties ->barcode_type*
1273
+     *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1274
+     *  if it is not defined, ->element must be defined to know default barcode type.
1275
+     *
1276
+     *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1277
+     */
1278
+    function fetch_barcode()
1279
+    {
1280
+        // phpcs:enable
1281
+        global $conf;
3909 1282
 
3910
-			if($action == 'selectlines')
3911
-			{
3912
-			    print '<td class="linecolcheckall" align="center">';
3913
-			    print '<input type="checkbox" class="linecheckboxtoggle" />';
3914
-			    print '<script type="text/javascript">$(document).ready(function() {$(".linecheckboxtoggle").click(function() {var checkBoxes = $(".linecheckbox");checkBoxes.prop("checked", this.checked);})});</script>';
3915
-			    print '</td>';
3916
-			}
3917
-
3918
-			print "</tr>\n";
3919
-			print "</thead>\n";
3920
-		}
3921
-
3922
-		$var = true;
3923
-		$i	 = 0;
3924
-
3925
-		print "<tbody>\n";
3926
-		foreach ($this->lines as $line)
3927
-		{
3928
-			//Line extrafield
3929
-			$line->fetch_optionals();
3930
-
3931
-			//if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3932
-			if (is_object($hookmanager))   // Old code is commented on preceding line.
3933
-			{
3934
-				if (empty($line->fk_parent_line))
3935
-				{
3936
-					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3937
-					$reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3938
-				}
3939
-				else
3940
-				{
3941
-					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline, 'fk_parent_line'=>$line->fk_parent_line);
3942
-					$reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3943
-				}
3944
-			}
3945
-			if (empty($reshook))
3946
-			{
3947
-				$this->printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected,$extrafieldsline);
3948
-			}
3949
-
3950
-			$i++;
3951
-		}
3952
-		print "</tbody>\n";
3953
-	}
3954
-
3955
-	/**
3956
-	 *	Return HTML content of a detail line
3957
-	 *	TODO Move this into an output class file (htmlline.class.php)
3958
-	 *
3959
-	 *	@param	string		$action				GET/POST action
3960
-	 *	@param CommonObjectLine $line		       	Selected object line to output
3961
-	 *	@param  string	    $var               	Is it a an odd line (true)
3962
-	 *	@param  int		    $num               	Number of line (0)
3963
-	 *	@param  int		    $i					I
3964
-	 *	@param  int		    $dateSelector      	1=Show also date range input fields
3965
-	 *	@param  string	    $seller            	Object of seller third party
3966
-	 *	@param  string	    $buyer             	Object of buyer third party
3967
-	 *	@param	int			$selected		   	Object line selected
3968
-	 *  @param  int			$extrafieldsline	Object of extrafield line attribute
3969
-	 *	@return	void
3970
-	 */
3971
-	function printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected=0,$extrafieldsline=0)
3972
-	{
3973
-		global $conf,$langs,$user,$object,$hookmanager;
3974
-		global $form,$bc,$bcdd;
3975
-		global $object_rights, $disableedit, $disablemove, $disableremove;   // TODO We should not use global var for this !
3976
-
3977
-		$object_rights = $this->getRights();
3978
-
3979
-		$element=$this->element;
3980
-
3981
-		$text=''; $description=''; $type=0;
3982
-
3983
-		// Show product and description
3984
-		$type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
3985
-		// Try to enhance type detection using date_start and date_end for free lines where type was not saved.
3986
-		if (! empty($line->date_start)) $type=1; // deprecated
3987
-		if (! empty($line->date_end)) $type=1; // deprecated
3988
-
3989
-		// Ligne en mode visu
3990
-		if ($action != 'editline' || $selected != $line->id)
3991
-		{
3992
-			// Product
3993
-			if ($line->fk_product > 0)
3994
-			{
3995
-				$product_static = new Product($this->db);
3996
-				$product_static->fetch($line->fk_product);
1283
+        dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
3997 1284
 
3998
-				$product_static->ref = $line->ref; //can change ref in hook
3999
-				$product_static->label = $line->label; //can change label in hook
4000
-				$text=$product_static->getNomUrl(1);
1285
+        $idtype=$this->barcode_type;
1286
+        if (empty($idtype) && $idtype != '0')	// If type of barcode no set, we try to guess. If set to '0' it means we forced to have type remain not defined
1287
+        {
1288
+            if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1289
+            else if ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1290
+            else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1291
+        }
4001 1292
 
4002
-				// Define output language and label
4003
-				if (! empty($conf->global->MAIN_MULTILANGS))
4004
-				{
4005
-					if (! is_object($this->thirdparty))
4006
-					{
4007
-						dol_print_error('','Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
4008
-						return;
4009
-					}
4010
-
4011
-					$prod = new Product($this->db);
4012
-					$prod->fetch($line->fk_product);
4013
-
4014
-					$outputlangs = $langs;
4015
-					$newlang='';
4016
-					if (empty($newlang) && GETPOST('lang_id','aZ09')) $newlang=GETPOST('lang_id','aZ09');
4017
-					if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang)) $newlang=$this->thirdparty->default_lang;		// For language to language of customer
4018
-					if (! empty($newlang))
4019
-					{
4020
-						$outputlangs = new Translate("",$conf);
4021
-						$outputlangs->setDefaultLang($newlang);
4022
-					}
4023
-
4024
-					$label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
4025
-				}
4026
-				else
4027
-				{
4028
-					$label = $line->product_label;
4029
-				}
1293
+        if ($idtype > 0)
1294
+        {
1295
+            if (empty($this->barcode_type) || empty($this->barcode_type_code) || empty($this->barcode_type_label) || empty($this->barcode_type_coder))    // If data not already loaded
1296
+            {
1297
+                $sql = "SELECT rowid, code, libelle as label, coder";
1298
+                $sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1299
+                $sql.= " WHERE rowid = ".$idtype;
1300
+                dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1301
+                $resql = $this->db->query($sql);
1302
+                if ($resql)
1303
+                {
1304
+                    $obj = $this->db->fetch_object($resql);
1305
+                    $this->barcode_type       = $obj->rowid;
1306
+                    $this->barcode_type_code  = $obj->code;
1307
+                    $this->barcode_type_label = $obj->label;
1308
+                    $this->barcode_type_coder = $obj->coder;
1309
+                    return 1;
1310
+                }
1311
+                else
1312
+                {
1313
+                    dol_print_error($this->db);
1314
+                    return -1;
1315
+                }
1316
+            }
1317
+        }
1318
+        return 0;
1319
+    }
4030 1320
 
4031
-				$text.= ' - '.(! empty($line->label)?$line->label:$label);
4032
-				$description.=(! empty($conf->global->PRODUIT_DESC_IN_FORM)?'':dol_htmlentitiesbr($line->description));	// Description is what to show on popup. We shown nothing if already into desc.
4033
-			}
1321
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1322
+    /**
1323
+     *		Load the project with id $this->fk_project into this->project
1324
+     *
1325
+     *		@return		int			<0 if KO, >=0 if OK
1326
+     */
1327
+    function fetch_projet()
1328
+    {
1329
+        // phpcs:enable
1330
+        include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
4034 1331
 
4035
-			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
1332
+        if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet;	// For backward compatibility
1333
+        if (empty($this->fk_project)) return 0;
4036 1334
 
4037
-			// Output template part (modules that overwrite templates must declare this into descriptor)
4038
-			// Use global variables + $dateSelector + $seller and $buyer
4039
-			$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4040
-			foreach($dirtpls as $reldir)
4041
-			{
4042
-				$tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
4043
-				if (empty($conf->file->strict_mode)) {
4044
-					$res=@include $tpl;
4045
-				} else {
4046
-					$res=include $tpl; // for debug
4047
-				}
4048
-				if ($res) break;
4049
-			}
4050
-		}
4051
-
4052
-		// Ligne en mode update
4053
-		if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
4054
-		{
4055
-			$label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
4056
-			$placeholder=' placeholder="'.$langs->trans("Label").'"';
4057
-
4058
-			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4059
-
4060
-			// Output template part (modules that overwrite templates must declare this into descriptor)
4061
-			// Use global variables + $dateSelector + $seller and $buyer
4062
-			$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4063
-			foreach($dirtpls as $reldir)
4064
-			{
4065
-				$tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
4066
-				if (empty($conf->file->strict_mode)) {
4067
-					$res=@include $tpl;
4068
-				} else {
4069
-					$res=include $tpl; // for debug
4070
-				}
4071
-				if ($res) break;
4072
-			}
4073
-		}
4074
-	}
4075
-
4076
-
4077
-	/* This is to show array of line of details of source object */
4078
-
4079
-
4080
-	/**
4081
-	 * 	Return HTML table table of source object lines
4082
-	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4083
-	 *  If lines are into a template, title must also be into a template
4084
-	 *  But for the moment we don't know if it's possible, so we keep the method available on overloaded objects.
4085
-	 *
4086
-	 *	@param	string		$restrictlist		''=All lines, 'services'=Restrict to services only
4087
-	 *  @return	void
4088
-	 */
4089
-	function printOriginLinesList($restrictlist='')
4090
-	{
4091
-		global $langs, $hookmanager, $conf;
4092
-
4093
-		print '<tr class="liste_titre">';
4094
-		print '<td>'.$langs->trans('Ref').'</td>';
4095
-		print '<td>'.$langs->trans('Description').'</td>';
4096
-		print '<td align="right">'.$langs->trans('VATRate').'</td>';
4097
-		print '<td align="right">'.$langs->trans('PriceUHT').'</td>';
4098
-		if (!empty($conf->multicurrency->enabled)) print '<td align="right">'.$langs->trans('PriceUHTCurrency').'</td>';
4099
-		print '<td align="right">'.$langs->trans('Qty').'</td>';
4100
-		if($conf->global->PRODUCT_USE_UNITS)
4101
-		{
4102
-			print '<td align="left">'.$langs->trans('Unit').'</td>';
4103
-		}
4104
-		print '<td align="right">'.$langs->trans('ReductionShort').'</td></tr>';
4105
-
4106
-		$var = true;
4107
-		$i	 = 0;
4108
-
4109
-		if (! empty($this->lines))
4110
-		{
4111
-			foreach ($this->lines as $line)
4112
-			{
4113
-				if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4114
-				{
4115
-					if (empty($line->fk_parent_line))
4116
-					{
4117
-						$parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
4118
-						$action='';
4119
-						$hookmanager->executeHooks('printOriginObjectLine',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
4120
-					}
4121
-				}
4122
-				else
4123
-				{
4124
-					$this->printOriginLine($line, $var, $restrictlist);
4125
-				}
1335
+        $project = new Project($this->db);
1336
+        $result = $project->fetch($this->fk_project);
4126 1337
 
4127
-				$i++;
4128
-			}
4129
-		}
4130
-	}
4131
-
4132
-	/**
4133
-	 * 	Return HTML with a line of table array of source object lines
4134
-	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4135
-	 *  If lines are into a template, title must also be into a template
4136
-	 *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
4137
-	 *
4138
-	 * 	@param	CommonObjectLine	$line				Line
4139
-	 * 	@param	string				$var				Var
4140
-	 *	@param	string				$restrictlist		''=All lines, 'services'=Restrict to services only (strike line if not)
4141
-	 * 	@return	void
4142
-	 */
4143
-	function printOriginLine($line, $var, $restrictlist='')
4144
-	{
4145
-		global $langs, $conf;
4146
-
4147
-		//var_dump($line);
4148
-		if (!empty($line->date_start))
4149
-		{
4150
-			$date_start=$line->date_start;
4151
-		}
4152
-		else
4153
-		{
4154
-			$date_start=$line->date_debut_prevue;
4155
-			if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
4156
-		}
4157
-		if (!empty($line->date_end))
4158
-		{
4159
-			$date_end=$line->date_end;
4160
-		}
4161
-		else
4162
-		{
4163
-			$date_end=$line->date_fin_prevue;
4164
-			if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
4165
-		}
4166
-
4167
-		$this->tpl['label'] = '';
4168
-		if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
4169
-
4170
-		if (($line->info_bits & 2) == 2)  // TODO Not sure this is used for source object
4171
-		{
4172
-			$discount=new DiscountAbsolute($this->db);
4173
-			$discount->fk_soc = $this->socid;
4174
-			$this->tpl['label'].= $discount->getNomUrl(0,'discount');
4175
-		}
4176
-		else if (! empty($line->fk_product))
4177
-		{
4178
-			$productstatic = new Product($this->db);
4179
-			$productstatic->id = $line->fk_product;
4180
-			$productstatic->ref = $line->ref;
4181
-			$productstatic->type = $line->fk_product_type;
4182
-            if(empty($productstatic->ref)){
4183
-				$line->fetch_product();
4184
-				$productstatic = $line->product;
4185
-			}
4186
-			
4187
-			$this->tpl['label'].= $productstatic->getNomUrl(1);
4188
-			$this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
4189
-			// Dates
4190
-			if ($line->product_type == 1 && ($date_start || $date_end))
4191
-			{
4192
-				$this->tpl['label'].= get_date_range($date_start,$date_end);
4193
-			}
4194
-		}
4195
-		else
4196
-		{
4197
-			$this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''),'service') : img_object($langs->trans(''),'product')));
4198
-			if (!empty($line->desc)) {
4199
-				$this->tpl['label'].=$line->desc;
4200
-			}else {
4201
-				$this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
4202
-			}
4203
-			
4204
-			// Dates
4205
-			if ($line->product_type == 1 && ($date_start || $date_end))
4206
-			{
4207
-				$this->tpl['label'].= get_date_range($date_start,$date_end);
4208
-			}
4209
-		}
1338
+        $this->projet = $project;	// deprecated
1339
+        $this->project = $project;
1340
+        return $result;
1341
+    }
4210 1342
 
4211
-		if (! empty($line->desc))
4212
-		{
4213
-			if ($line->desc == '(CREDIT_NOTE)')  // TODO Not sure this is used for source object
4214
-			{
4215
-				$discount=new DiscountAbsolute($this->db);
4216
-				$discount->fetch($line->fk_remise_except);
4217
-				$this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote",$discount->getNomUrl(0));
4218
-			}
4219
-			elseif ($line->desc == '(DEPOSIT)')  // TODO Not sure this is used for source object
4220
-			{
4221
-				$discount=new DiscountAbsolute($this->db);
4222
-				$discount->fetch($line->fk_remise_except);
4223
-				$this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit",$discount->getNomUrl(0));
4224
-			}
4225
-			elseif ($line->desc == '(EXCESS RECEIVED)')
4226
-			{
4227
-				$discount=new DiscountAbsolute($this->db);
4228
-				$discount->fetch($line->fk_remise_except);
4229
-				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0));
4230
-			}
4231
-			elseif ($line->desc == '(EXCESS PAID)')
4232
-			{
4233
-				$discount=new DiscountAbsolute($this->db);
4234
-				$discount->fetch($line->fk_remise_except);
4235
-				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid",$discount->getNomUrl(0));
4236
-			}
4237
-			else
4238
-			{
4239
-				$this->tpl['description'] = dol_trunc($line->desc,60);
4240
-			}
4241
-		}
4242
-		else
4243
-		{
4244
-			$this->tpl['description'] = '&nbsp;';
4245
-		}
1343
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1344
+    /**
1345
+     *		Load the product with id $this->fk_product into this->product
1346
+     *
1347
+     *		@return		int			<0 if KO, >=0 if OK
1348
+     */
1349
+    function fetch_product()
1350
+    {
1351
+        // phpcs:enable
1352
+        include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
4246 1353
 
4247
-        // VAT Rate
4248
-        $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
4249
-        $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
4250
-        if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
1354
+        if (empty($this->fk_product)) return 0;
4251 1355
 
4252
-		$this->tpl['price'] = price($line->subprice);
4253
-		$this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
4254
-		$this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
4255
-		if ($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
4256
-		$this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
4257
-
4258
-		// Is the line strike or not
4259
-		$this->tpl['strike']=0;
4260
-		if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike']=1;
4261
-
4262
-		// Output template part (modules that overwrite templates must declare this into descriptor)
4263
-		// Use global variables + $dateSelector + $seller and $buyer
4264
-		$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4265
-		foreach($dirtpls as $reldir)
4266
-		{
4267
-			$tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
4268
-			if (empty($conf->file->strict_mode)) {
4269
-				$res=@include $tpl;
4270
-			} else {
4271
-				$res=include $tpl; // for debug
4272
-			}
4273
-			if ($res) break;
4274
-		}
4275
-	}
1356
+        $product = new Product($this->db);
1357
+        $result = $product->fetch($this->fk_product);
4276 1358
 
1359
+        $this->product = $product;
1360
+        return $result;
1361
+    }
4277 1362
 
4278 1363
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4279
-	/**
4280
-	 *	Add resources to the current object : add entry into llx_element_resources
4281
-	 *	Need $this->element & $this->id
4282
-	 *
4283
-	 *	@param		int		$resource_id		Resource id
4284
-	 *	@param		string	$resource_type		'resource'
4285
-	 *	@param		int		$busy				Busy or not
4286
-	 *	@param		int		$mandatory			Mandatory or not
4287
-	 *	@return		int							<=0 if KO, >0 if OK
4288
-	 */
4289
-	function add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
4290
-	{
1364
+    /**
1365
+     *		Load the user with id $userid into this->user
1366
+     *
1367
+     *		@param	int		$userid 		Id du contact
1368
+     *		@return	int						<0 if KO, >0 if OK
1369
+     */
1370
+    function fetch_user($userid)
1371
+    {
4291 1372
         // phpcs:enable
4292
-		$this->db->begin();
4293
-
4294
-		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
4295
-		$sql.= "resource_id";
4296
-		$sql.= ", resource_type";
4297
-		$sql.= ", element_id";
4298
-		$sql.= ", element_type";
4299
-		$sql.= ", busy";
4300
-		$sql.= ", mandatory";
4301
-		$sql.= ") VALUES (";
4302
-		$sql.= $resource_id;
4303
-		$sql.= ", '".$this->db->escape($resource_type)."'";
4304
-		$sql.= ", '".$this->db->escape($this->id)."'";
4305
-		$sql.= ", '".$this->db->escape($this->element)."'";
4306
-		$sql.= ", '".$this->db->escape($busy)."'";
4307
-		$sql.= ", '".$this->db->escape($mandatory)."'";
4308
-		$sql.= ")";
4309
-
4310
-		dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
4311
-		if ($this->db->query($sql))
4312
-		{
4313
-			$this->db->commit();
4314
-			return 1;
4315
-		}
4316
-		else
4317
-		{
4318
-			$this->error=$this->db->lasterror();
4319
-			$this->db->rollback();
4320
-			return  0;
4321
-		}
4322
-	}
1373
+        $user = new User($this->db);
1374
+        $result=$user->fetch($userid);
1375
+        $this->user = $user;
1376
+        return $result;
1377
+    }
4323 1378
 
4324 1379
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4325
-	/**
4326
-	 *    Delete a link to resource line
4327
-	 *
4328
-	 *    @param	int		$rowid			Id of resource line to delete
4329
-	 *    @param	int		$element		element name (for trigger) TODO: use $this->element into commonobject class
4330
-	 *    @param	int		$notrigger		Disable all triggers
4331
-	 *    @return   int						>0 if OK, <0 if KO
4332
-	 */
4333
-	function delete_resource($rowid, $element, $notrigger=0)
4334
-	{
1380
+    /**
1381
+     *	Read linked origin object
1382
+     *
1383
+     *	@return		void
1384
+     */
1385
+    function fetch_origin()
1386
+    {
4335 1387
         // phpcs:enable
4336
-		global $user;
1388
+        if ($this->origin == 'shipping') $this->origin = 'expedition';
1389
+        if ($this->origin == 'delivery') $this->origin = 'livraison';
1390
+        if ($this->origin == 'order_supplier') $this->origin = 'commandeFournisseur';
4337 1391
 
4338
-		$this->db->begin();
1392
+        $origin = $this->origin;
4339 1393
 
4340
-		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
4341
-		$sql.= " WHERE rowid=".$rowid;
1394
+        $classname = ucfirst($origin);
1395
+        $this->$origin = new $classname($this->db);
1396
+        $this->$origin->fetch($this->origin_id);
1397
+    }
4342 1398
 
4343
-		dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
1399
+    /**
1400
+     *  Load object from specific field
1401
+     *
1402
+     *  @param	string	$table		Table element or element line
1403
+     *  @param	string	$field		Field selected
1404
+     *  @param	string	$key		Import key
1405
+     *  @param	string	$element	Element name
1406
+     *	@return	int					<0 if KO, >0 if OK
1407
+     */
1408
+    function fetchObjectFrom($table, $field, $key, $element = null)
1409
+    {
1410
+        global $conf;
4344 1411
 
4345
-		$resql=$this->db->query($sql);
4346
-		if (! $resql)
4347
-		{
4348
-			$this->error=$this->db->lasterror();
4349
-			$this->db->rollback();
4350
-			return -1;
4351
-		}
4352
-		else
4353
-		{
4354
-			if (! $notrigger)
4355
-			{
4356
-				$result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
4357
-				if ($result < 0) { $this->db->rollback(); return -1; }
4358
-			}
4359
-			$this->db->commit();
4360
-			return 1;
4361
-		}
4362
-	}
4363
-
4364
-
4365
-	/**
4366
-	 * Overwrite magic function to solve problem of cloning object that are kept as references
4367
-	 *
4368
-	 * @return void
4369
-	 */
4370
-	function __clone()
4371
-	{
4372
-		// Force a copy of this->lines, otherwise it will point to same object.
4373
-		if (isset($this->lines) && is_array($this->lines))
4374
-		{
4375
-			$nboflines=count($this->lines);
4376
-			for($i=0; $i < $nboflines; $i++)
4377
-			{
4378
-				$this->lines[$i] = clone $this->lines[$i];
4379
-			}
4380
-		}
4381
-	}
4382
-
4383
-	/**
4384
-	 * Common function for all objects extending CommonObject for generating documents
4385
-	 *
4386
-	 * @param 	string 		$modelspath 	Relative folder where generators are placed
4387
-	 * @param 	string 		$modele 		Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf') for example.
4388
-	 * @param 	Translate 	$outputlangs 	Output language to use
4389
-	 * @param 	int 		$hidedetails 	1 to hide details. 0 by default
4390
-	 * @param 	int 		$hidedesc 		1 to hide product description. 0 by default
4391
-	 * @param 	int 		$hideref 		1 to hide product reference. 0 by default
4392
-	 * @param   null|array  $moreparams     Array to provide more information
4393
-	 * @return 	int 						>0 if OK, <0 if KO
4394
-	 * @see	addFileIntoDatabaseIndex
4395
-	 */
4396
-	protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
4397
-	{
4398
-		global $conf, $langs, $user;
4399
-
4400
-		$srctemplatepath='';
4401
-
4402
-		// Increase limit for PDF build
4403
-		$err=error_reporting();
4404
-		error_reporting(0);
4405
-		@set_time_limit(120);
4406
-		error_reporting($err);
4407
-
4408
-		// If selected model is a filename template (then $modele="modelname" or "modelname:filename")
4409
-		$tmp=explode(':',$modele,2);
4410
-		if (! empty($tmp[1]))
4411
-		{
4412
-			$modele=$tmp[0];
4413
-			$srctemplatepath=$tmp[1];
4414
-		}
4415
-
4416
-		// Search template files
4417
-		$file=''; $classname=''; $filefound=0;
4418
-		$dirmodels=array('/');
4419
-		if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels,$conf->modules_parts['models']);
4420
-		foreach($dirmodels as $reldir)
4421
-		{
4422
-			foreach(array('doc','pdf') as $prefix)
4423
-			{
4424
-				if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php";     // Member module use prefix_module.class.php
4425
-				else $file = $prefix."_".$modele.".modules.php";
1412
+        $result=false;
4426 1413
 
4427
-				// On verifie l'emplacement du modele
4428
-				$file=dol_buildpath($reldir.$modelspath.$file,0);
4429
-				if (file_exists($file))
4430
-				{
4431
-					$filefound=1;
4432
-					$classname=$prefix.'_'.$modele;
4433
-					break;
4434
-				}
4435
-			}
4436
-			if ($filefound) break;
4437
-		}
1414
+        $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
1415
+        $sql.= " WHERE ".$field." = '".$key."'";
1416
+        if (! empty($element)) {
1417
+            $sql.= " AND entity IN (".getEntity($element).")";
1418
+        } else {
1419
+            $sql.= " AND entity = ".$conf->entity;
1420
+        }
4438 1421
 
4439
-		// If generator was found
4440
-		if ($filefound)
4441
-		{
4442
-			global $db;  // Required to solve a conception default in commonstickergenerator.class.php making an include of code using $db
1422
+        dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1423
+        $resql = $this->db->query($sql);
1424
+        if ($resql)
1425
+        {
1426
+            $row = $this->db->fetch_row($resql);
1427
+            // Test for avoid error -1
1428
+            if ($row[0] > 0) {
1429
+                $result = $this->fetch($row[0]);
1430
+            }
1431
+        }
4443 1432
 
4444
-			require_once $file;
1433
+        return $result;
1434
+    }
4445 1435
 
4446
-			$obj = new $classname($this->db);
1436
+    /**
1437
+     *	Getter generic. Load value from a specific field
1438
+     *
1439
+     *	@param	string	$table		Table of element or element line
1440
+     *	@param	int		$id			Element id
1441
+     *	@param	string	$field		Field selected
1442
+     *	@return	int					<0 if KO, >0 if OK
1443
+     */
1444
+    function getValueFrom($table, $id, $field)
1445
+    {
1446
+        $result=false;
1447
+        if (!empty($id) && !empty($field) && !empty($table)) {
1448
+            $sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
1449
+            $sql.= " WHERE rowid = ".$id;
1450
+
1451
+            dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1452
+            $resql = $this->db->query($sql);
1453
+            if ($resql)
1454
+            {
1455
+                $row = $this->db->fetch_row($resql);
1456
+                $result = $row[0];
1457
+            }
1458
+        }
1459
+        return $result;
1460
+    }
4447 1461
 
4448
-			// If generator is ODT, we must have srctemplatepath defined, if not we set it.
4449
-			if ($obj->type == 'odt' && empty($srctemplatepath))
4450
-			{
4451
-				$varfortemplatedir=$obj->scandir;
4452
-				if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
4453
-				{
4454
-					$dirtoscan=$conf->global->$varfortemplatedir;
4455
-
4456
-					$listoffiles=array();
4457
-
4458
-					// Now we add first model found in directories scanned
4459
-					$listofdir=explode(',',$dirtoscan);
4460
-					foreach($listofdir as $key => $tmpdir)
4461
-					{
4462
-						$tmpdir=trim($tmpdir);
4463
-						$tmpdir=preg_replace('/DOL_DATA_ROOT/',DOL_DATA_ROOT,$tmpdir);
4464
-						if (! $tmpdir) { unset($listofdir[$key]); continue; }
4465
-						if (is_dir($tmpdir))
4466
-						{
4467
-							$tmpfiles=dol_dir_list($tmpdir,'files',0,'\.od(s|t)$','','name',SORT_ASC,0);
4468
-							if (count($tmpfiles)) $listoffiles=array_merge($listoffiles,$tmpfiles);
4469
-						}
4470
-					}
4471
-
4472
-					if (count($listoffiles))
4473
-					{
4474
-						foreach($listoffiles as $record)
4475
-						{
4476
-							$srctemplatepath=$record['fullname'];
4477
-							break;
4478
-						}
4479
-					}
4480
-				}
1462
+    /**
1463
+     *	Setter generic. Update a specific field into database.
1464
+     *  Warning: Trigger is run only if param trigkey is provided.
1465
+     *
1466
+     *	@param	string		$field			Field to update
1467
+     *	@param	mixed		$value			New value
1468
+     *	@param	string		$table			To force other table element or element line (should not be used)
1469
+     *	@param	int			$id				To force other object id (should not be used)
1470
+     *	@param	string		$format			Data format ('text', 'date'). 'text' is used if not defined
1471
+     *	@param	string		$id_field		To force rowid field name. 'rowid' is used if not defined
1472
+     *	@param	User|string	$fuser			Update the user of last update field with this user. If not provided, current user is used except if value is 'none'
1473
+     *  @param  string      $trigkey    	Trigger key to run (in most cases something like 'XXX_MODIFY')
1474
+     *  @param	string		$fk_user_field	Name of field to save user id making change
1475
+     *	@return	int							<0 if KO, >0 if OK
1476
+     *  @see updateExtraField
1477
+     */
1478
+    function setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $fuser=null, $trigkey='', $fk_user_field='fk_user_modif')
1479
+    {
1480
+        global $user,$langs,$conf;
4481 1481
 
4482
-				if (empty($srctemplatepath))
4483
-				{
4484
-					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4485
-					return -1;
4486
-				}
4487
-			}
1482
+        if (empty($table)) 	  $table=$this->table_element;
1483
+        if (empty($id))    	  $id=$this->id;
1484
+        if (empty($format))   $format='text';
1485
+        if (empty($id_field)) $id_field='rowid';
4488 1486
 
4489
-			if ($obj->type == 'odt' && ! empty($srctemplatepath))
4490
-			{
4491
-				if (! dol_is_file($srctemplatepath))
4492
-				{
4493
-					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4494
-					return -1;
4495
-				}
4496
-			}
1487
+        $error=0;
4497 1488
 
4498
-			// We save charset_output to restore it because write_file can change it if needed for
4499
-			// output format that does not support UTF8.
4500
-			$sav_charset_output=$outputlangs->charset_output;
1489
+        $this->db->begin();
4501 1490
 
4502
-			if (in_array(get_class($this), array('Adherent')))
4503
-			{
4504
-				$arrayofrecords = array();   // The write_file of templates of adherent class need this var
4505
-				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4506
-			}
4507
-			else
4508
-			{
4509
-				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4510
-			}
4511
-			// After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
1491
+        // Special case
1492
+        if ($table == 'product' && $field == 'note_private') $field='note';
1493
+        if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) $fk_user_field = 'fk_user_mod';
4512 1494
 
4513
-			if ($resultwritefile > 0)
4514
-			{
4515
-				$outputlangs->charset_output=$sav_charset_output;
4516
-
4517
-				// We delete old preview
4518
-				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4519
-				dol_delete_preview($this);
4520
-
4521
-				// Index file in database
4522
-				if (! empty($obj->result['fullpath']))
4523
-				{
4524
-					$destfull = $obj->result['fullpath'];
4525
-					$upload_dir = dirname($destfull);
4526
-					$destfile = basename($destfull);
4527
-					$rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $upload_dir);
4528
-
4529
-					if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir))     // If not a tmp dir
4530
-					{
4531
-						$filename = basename($destfile);
4532
-						$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4533
-						$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4534
-
4535
-						include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4536
-						$ecmfile=new EcmFiles($this->db);
4537
-						$result = $ecmfile->fetch(0, '', ($rel_dir?$rel_dir.'/':'').$filename);
4538
-
4539
-						// Set the public "share" key
4540
-						$setsharekey = false;
4541
-						if ($this->element == 'propal')
4542
-						{
4543
-							$useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL;	// Replace this with 1 when feature to make online signature is ok
4544
-							if ($useonlinesignature) $setsharekey=true;
4545
-							if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4546
-						}
4547
-						if ($this->element == 'commande'     && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD))        $setsharekey=true;
4548
-						if ($this->element == 'facture'      && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD))      $setsharekey=true;
4549
-						if ($this->element == 'bank_account' && ! empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4550
-
4551
-						if ($setsharekey)
4552
-						{
4553
-							if (empty($ecmfile->share))	// Because object not found or share not set yet
4554
-							{
4555
-								require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4556
-								$ecmfile->share = getRandomPassword(true);
4557
-							}
4558
-						}
1495
+        $sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
4559 1496
 
4560
-						if ($result > 0)
4561
-						{
4562
-							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4563
-							$ecmfile->fullpath_orig = '';
4564
-							$ecmfile->gen_or_uploaded = 'generated';
4565
-							$ecmfile->description = '';    // indexed content
4566
-							$ecmfile->keyword = '';        // keyword content
4567
-							$result = $ecmfile->update($user);
4568
-							if ($result < 0)
4569
-							{
4570
-								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4571
-							}
4572
-						}
4573
-						else
4574
-						{
4575
-							$ecmfile->entity = $conf->entity;
4576
-							$ecmfile->filepath = $rel_dir;
4577
-							$ecmfile->filename = $filename;
4578
-							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4579
-							$ecmfile->fullpath_orig = '';
4580
-							$ecmfile->gen_or_uploaded = 'generated';
4581
-							$ecmfile->description = '';    // indexed content
4582
-							$ecmfile->keyword = '';        // keyword content
4583
-							$ecmfile->src_object_type = $this->table_element;
4584
-							$ecmfile->src_object_id   = $this->id;
4585
-
4586
-							$result = $ecmfile->create($user);
4587
-							if ($result < 0)
4588
-							{
4589
-								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4590
-							}
4591
-						}
1497
+        if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'";
1498
+        else if ($format == 'int') $sql.= $field." = ".$this->db->escape($value);
1499
+        else if ($format == 'date') $sql.= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
4592 1500
 
4593
-						/*$this->result['fullname']=$destfull;
4594
-						$this->result['filepath']=$ecmfile->filepath;
4595
-						$this->result['filename']=$ecmfile->filename;*/
4596
-						//var_dump($obj->update_main_doc_field);exit;
4597
-
4598
-						// Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set)
4599
-						$update_main_doc_field=0;
4600
-						if (! empty($obj->update_main_doc_field)) $update_main_doc_field=1;
4601
-						if ($update_main_doc_field && ! empty($this->table_element))
4602
-						{
4603
-							$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4604
-							$sql.= ' WHERE rowid = '.$this->id;
4605
-							$resql = $this->db->query($sql);
4606
-							if (! $resql) dol_print_error($this->db);
4607
-						}
4608
-					}
4609
-				}
4610
-				else
4611
-				{
4612
-					dol_syslog('Method ->write_file was called on object '.get_class($obj).' and return a success but the return array ->result["fullpath"] was not set.', LOG_WARNING);
4613
-				}
1501
+        if ($fk_user_field)
1502
+        {
1503
+            if (! empty($fuser) && is_object($fuser)) $sql.=", ".$fk_user_field." = ".$fuser->id;
1504
+            elseif (empty($fuser) || $fuser != 'none') $sql.=", ".$fk_user_field." = ".$user->id;
1505
+        }
4614 1506
 
4615
-				// Success in building document. We build meta file.
4616
-				dol_meta_create($this);
1507
+        $sql.= " WHERE ".$id_field." = ".$id;
4617 1508
 
4618
-				return 1;
4619
-			}
4620
-			else
4621
-			{
4622
-				$outputlangs->charset_output=$sav_charset_output;
4623
-				dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4624
-				return -1;
4625
-			}
4626
-		}
4627
-		else
4628
-		{
4629
-			$this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file);
4630
-			dol_print_error('',$this->error);
4631
-			return -1;
4632
-		}
4633
-	}
4634
-
4635
-	/**
4636
-	 *  Build thumb
4637
-	 *  @TODO Move this into files.lib.php
4638
-	 *
4639
-	 *  @param      string	$file           Path file in UTF8 to original file to create thumbs from.
4640
-	 *	@return		void
4641
-	 */
4642
-	function addThumbs($file)
4643
-	{
4644
-		global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4645
-
4646
-		require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';		// This define also $maxwidthsmall, $quality, ...
4647
-
4648
-		$file_osencoded=dol_osencode($file);
4649
-		if (file_exists($file_osencoded))
4650
-		{
4651
-			// Create small thumbs for company (Ratio is near 16/9)
4652
-			// Used on logon for example
4653
-			vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4654
-
4655
-			// Create mini thumbs for company (Ratio is near 16/9)
4656
-			// Used on menu or for setup page for example
4657
-			vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4658
-		}
4659
-	}
4660
-
4661
-
4662
-	/* Functions common to commonobject and commonobjectline */
4663
-
4664
-	/* For default values */
4665
-
4666
-	/**
4667
-	 * Return the default value to use for a field when showing the create form of object.
4668
-	 * Return values in this order:
4669
-	 * 1) If parameter is available into POST, we return it first.
4670
-	 * 2) If not but an alternate value was provided as parameter of function, we return it.
4671
-	 * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
4672
-	 * 4) Return value found into database (TODO No yet implemented)
4673
-	 *
4674
-	 * @param   string              $fieldname          Name of field
4675
-	 * @param   string              $alternatevalue     Alternate value to use
4676
-	 * @return  string|string[]                         Default value (can be an array if the GETPOST return an array)
4677
-	 **/
4678
-	function getDefaultCreateValueFor($fieldname, $alternatevalue=null)
4679
-	{
4680
-		global $conf, $_POST;
4681
-
4682
-		// If param here has been posted, we use this value first.
4683
-		if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4684
-
4685
-		if (isset($alternatevalue)) return $alternatevalue;
4686
-
4687
-		$newelement=$this->element;
4688
-		if ($newelement == 'facture') $newelement='invoice';
4689
-		if ($newelement == 'commande') $newelement='order';
4690
-		if (empty($newelement))
4691
-		{
4692
-			dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4693
-			return '';
4694
-		}
4695
-
4696
-		$keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4697
-		//var_dump($keyforfieldname);
4698
-		if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4699
-
4700
-		// TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4701
-	}
4702
-
4703
-
4704
-	/* For triggers */
1509
+        dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG);
1510
+        $resql = $this->db->query($sql);
1511
+        if ($resql)
1512
+        {
1513
+            if ($trigkey)
1514
+            {
1515
+                // call trigger with updated object values
1516
+                if (empty($this->fields) && method_exists($this, 'fetch'))
1517
+                {
1518
+                    $result = $this->fetch($id);
1519
+                }
1520
+                else
1521
+                {
1522
+                    $result = $this->fetchCommon($id);
1523
+                }
1524
+                if ($result >= 0) $result=$this->call_trigger($trigkey, (! empty($fuser) && is_object($fuser)) ? $fuser : $user);   // This may set this->errors
1525
+                if ($result < 0) $error++;
1526
+            }
4705 1527
 
1528
+            if (! $error)
1529
+            {
1530
+                if (property_exists($this, $field)) $this->$field = $value;
1531
+                $this->db->commit();
1532
+                return 1;
1533
+            }
1534
+            else
1535
+            {
1536
+                $this->db->rollback();
1537
+                return -2;
1538
+            }
1539
+        }
1540
+        else
1541
+        {
1542
+            $this->error=$this->db->lasterror();
1543
+            $this->db->rollback();
1544
+            return -1;
1545
+        }
1546
+    }
4706 1547
 
4707 1548
     // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4708
-	/**
4709
-	 * Call trigger based on this instance.
4710
-	 * Some context information may also be provided into array property this->context.
4711
-	 * NB:  Error from trigger are stacked in interface->errors
4712
-	 * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
4713
-	 *
4714
-	 * @param   string    $trigger_name   trigger's name to execute
4715
-	 * @param   User      $user           Object user
4716
-	 * @return  int                       Result of run_triggers
4717
-	 */
4718
-	function call_trigger($trigger_name, $user)
4719
-	{
1549
+    /**
1550
+     *      Load properties id_previous and id_next by comparing $fieldid with $this->ref
1551
+     *
1552
+     *      @param	string	$filter		Optional filter. Example: " AND (t.field1 = 'aa' OR t.field2 = 'bb')"
1553
+     *	 	@param  string	$fieldid   	Name of field to use for the select MAX and MIN
1554
+     *		@param	int		$nodbprefix	Do not include DB prefix to forge table name
1555
+     *      @return int         		<0 if KO, >0 if OK
1556
+     */
1557
+    function load_previous_next_ref($filter, $fieldid, $nodbprefix=0)
1558
+    {
4720 1559
         // phpcs:enable
4721
-		global $langs,$conf;
1560
+        global $conf, $user;
4722 1561
 
4723
-		include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4724
-		$interface=new Interfaces($this->db);
4725
-		$result=$interface->run_triggers($trigger_name,$this,$user,$langs,$conf);
4726
-
4727
-		if ($result < 0)
4728
-		{
4729
-			if (!empty($this->errors))
4730
-			{
4731
-				$this->errors=array_unique(array_merge($this->errors,$interface->errors));   // We use array_unique because when a trigger call another trigger on same object, this->errors is added twice.
4732
-			}
4733
-			else
4734
-			{
4735
-				$this->errors=$interface->errors;
4736
-			}
4737
-		}
4738
-		return $result;
4739
-	}
1562
+        if (! $this->table_element)
1563
+        {
1564
+            dol_print_error('',get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
1565
+            return -1;
1566
+        }
1567
+        if ($fieldid == 'none') return 1;
4740 1568
 
1569
+        // Security on socid
1570
+        $socid = 0;
1571
+        if ($user->societe_id > 0) $socid = $user->societe_id;
4741 1572
 
4742
-	/* Functions for extrafields */
1573
+        // this->ismultientitymanaged contains
1574
+        // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
1575
+        $alias = 's';
1576
+        if ($this->element == 'societe') $alias = 'te';
4743 1577
 
1578
+        $sql = "SELECT MAX(te.".$fieldid.")";
1579
+        $sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1580
+        if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1581
+            $sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1582
+        }
1583
+        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1584
+        else if ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to socid
1585
+        else if ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid";	// If we need to link to societe to limit select to socid
1586
+        if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid)  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1587
+        $sql.= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1588
+        if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1589
+        if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1590
+        if (! empty($filter))
1591
+        {
1592
+            if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1593
+            $sql.=$filter;
1594
+        }
1595
+        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1596
+        else if ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to socid
1597
+        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1598
+            if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1599
+                if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1600
+                    $sql.= " AND te.entity IS NOT NULL"; // Show all users
1601
+                } else {
1602
+                    $sql.= " AND ug.fk_user = te.rowid";
1603
+                    $sql.= " AND ug.entity IN (".getEntity($this->element).")";
1604
+                }
1605
+            } else {
1606
+                $sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1607
+            }
1608
+        }
1609
+        if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1610
+        if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1611
+        if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1612
+        //print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
4744 1613
 
4745
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4746
-	/**
4747
-	 *  Function to get extra fields of an object into $this->array_options
4748
-	 *  This method is in most cases called by method fetch of objects but you can call it separately.
4749
-	 *
4750
-	 *  @param	int		$rowid			Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
4751
-	 *  @param  array	$optionsArray   Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
4752
-	 *  @return	int						<0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded
4753
-	 */
4754
-	function fetch_optionals($rowid=null, $optionsArray=null)
4755
-	{
4756
-        // phpcs:enable
4757
-		if (empty($rowid)) $rowid=$this->id;
4758
-
4759
-		// To avoid SQL errors. Probably not the better solution though
4760
-		if (!$this->table_element) {
4761
-			return 0;
4762
-		}
4763
-
4764
-		$this->array_options=array();
4765
-
4766
-		if (! is_array($optionsArray))
4767
-		{
4768
-			// If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4769
-			// TODO Use of existing $extrafield is not yet ready (must mutualize code that use extrafields in form first)
4770
-			// global $extrafields;
4771
-			//if (! is_object($extrafields))
4772
-			//{
4773
-				// require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4774
-            $extrafields = new ExtraFields();
4775
-            //}
1614
+        $result = $this->db->query($sql);
1615
+        if (! $result)
1616
+        {
1617
+            $this->error=$this->db->lasterror();
1618
+            return -1;
1619
+        }
1620
+        $row = $this->db->fetch_row($result);
1621
+        $this->ref_previous = $row[0];
4776 1622
 
4777
-			// Load array of extrafields for elementype = $this->table_element
4778
-			if (empty($extrafields->attributes[$this->table_element]['loaded']))
4779
-			{
4780
-				$extrafields->fetch_name_optionals_label($this->table_element);
4781
-			}
4782
-			$optionsArray = (! empty($extrafields->attributes[$this->table_element]['label'])?$extrafields->attributes[$this->table_element]['label']:null);
4783
-		}
4784
-		else
4785
-		{
4786
-			global $extrafields;
4787
-			dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
4788
-		}
4789
-
4790
-		$table_element = $this->table_element;
4791
-		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4792
-
4793
-		// Request to get complementary values
4794
-		if (is_array($optionsArray) && count($optionsArray) > 0)
4795
-		{
4796
-			$sql = "SELECT rowid";
4797
-			foreach ($optionsArray as $name => $label)
4798
-			{
4799
-				if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate')
4800
-				{
4801
-					$sql.= ", ".$name;
4802
-				}
4803
-			}
4804
-			$sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
4805
-			$sql.= " WHERE fk_object = ".$rowid;
4806 1623
 
4807
-			//dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG);		// Too verbose
4808
-			$resql=$this->db->query($sql);
4809
-			if ($resql)
4810
-			{
4811
-				$this->array_options = array();
4812
-				$numrows=$this->db->num_rows($resql);
4813
-				if ($numrows)
4814
-				{
4815
-					$tab = $this->db->fetch_array($resql);
4816
-
4817
-					foreach ($tab as $key => $value)
4818
-					{
4819
-						// Test fetch_array ! is_int($key) because fetch_array result is a mix table with Key as alpha and Key as int (depend db engine)
4820
-						if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
4821
-						{
4822
-							// we can add this attribute to object
4823
-							if (! empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
4824
-							{
4825
-								//var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
4826
-								$this->array_options["options_".$key]=$this->db->jdate($value);
4827
-							}
4828
-							else
4829
-							{
4830
-								$this->array_options["options_".$key]=$value;
4831
-							}
1624
+        $sql = "SELECT MIN(te.".$fieldid.")";
1625
+        $sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1626
+        if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1627
+            $sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1628
+        }
1629
+        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1630
+        else if ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to socid
1631
+        else if ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid";	// If we need to link to societe to limit select to socid
1632
+        if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1633
+        $sql.= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1634
+        if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1635
+        if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1636
+        if (! empty($filter))
1637
+        {
1638
+            if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1639
+            $sql.=$filter;
1640
+        }
1641
+        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1642
+        else if ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to socid
1643
+        if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1644
+            if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1645
+                if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1646
+                    $sql.= " AND te.entity IS NOT NULL"; // Show all users
1647
+                } else {
1648
+                    $sql.= " AND ug.fk_user = te.rowid";
1649
+                    $sql.= " AND ug.entity IN (".getEntity($this->element).")";
1650
+                }
1651
+            } else {
1652
+                $sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1653
+            }
1654
+        }
1655
+        if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1656
+        if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1657
+        if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1658
+        //print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1659
+        // Rem: Bug in some mysql version: SELECT MIN(rowid) FROM llx_socpeople WHERE rowid > 1 when one row in database with rowid=1, returns 1 instead of null
1660
+
1661
+        $result = $this->db->query($sql);
1662
+        if (! $result)
1663
+        {
1664
+            $this->error=$this->db->lasterror();
1665
+            return -2;
1666
+        }
1667
+        $row = $this->db->fetch_row($result);
1668
+        $this->ref_next = $row[0];
4832 1669
 
4833
-							//var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
4834
-						}
4835
-					}
4836
-				}
1670
+        return 1;
1671
+    }
4837 1672
 
4838
-				$this->db->free($resql);
4839 1673
 
4840
-				if ($numrows) return $numrows;
4841
-				else return 0;
4842
-			}
4843
-			else
4844
-			{
4845
-				dol_print_error($this->db);
4846
-				return -1;
4847
-			}
4848
-		}
4849
-		return 0;
4850
-	}
4851
-
4852
-	/**
4853
-	 *	Delete all extra fields values for the current object.
4854
-	 *
4855
-	 *  @return	int		<0 if KO, >0 if OK
4856
-	 */
4857
-	function deleteExtraFields()
4858
-	{
4859
-		$this->db->begin();
4860
-
4861
-		$table_element = $this->table_element;
4862
-		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4863
-
4864
-		$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4865
-		dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
4866
-		$resql=$this->db->query($sql_del);
4867
-		if (! $resql)
4868
-		{
4869
-			$this->error=$this->db->lasterror();
4870
-			$this->db->rollback();
4871
-			return -1;
4872
-		}
4873
-		else
4874
-		{
4875
-			$this->db->commit();
4876
-			return 1;
4877
-		}
4878
-	}
4879
-
4880
-	/**
4881
-	 *	Add/Update all extra fields values for the current object.
4882
-	 *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4883
-	 *  This function delete record with all extrafields and insert them again from the array $this->array_options.
4884
-	 *
4885
-	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
4886
-	 *  @param	User		$userused		Object user
4887
-	 *  @return int 						-1=error, O=did nothing, 1=OK
4888
-	 *  @see updateExtraField, setValueFrom
4889
-	 */
4890
-	function insertExtraFields($trigger='', $userused=null)
4891
-	{
4892
-		global $conf,$langs,$user;
4893
-
4894
-		if (empty($userused)) $userused=$user;
4895
-
4896
-		$error=0;
4897
-
4898
-		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4899
-
4900
-		if (! empty($this->array_options))
4901
-		{
4902
-			// Check parameters
4903
-			$langs->load('admin');
4904
-			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4905
-			$extrafields = new ExtraFields($this->db);
4906
-			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4907
-
4908
-			//Eliminate copied source object extra_fields that do not exist in target object
4909
-			$new_array_options=array();
4910
-			foreach ($this->array_options as $key => $value) {
4911
-				if (in_array(substr($key,8), array_keys($target_extrafields)))	// We remove the 'options_' from $key for test
4912
-					$new_array_options[$key] = $value;
4913
-				elseif (in_array($key, array_keys($target_extrafields)))		// We test on $key that does not contains the 'options_' prefix
4914
-					$new_array_options['options_'.$key] = $value;
4915
-			}
4916
-
4917
-			foreach($new_array_options as $key => $value)
4918
-			{
4919
-			   	$attributeKey      = substr($key,8);   // Remove 'options_' prefix
4920
-			   	$attributeType     = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
4921
-			   	$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
4922
-			   	$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
4923
-			   	$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
4924
-
4925
-			   	if ($attributeRequired)
4926
-			   	{
4927
-			   		$mandatorypb=false;
4928
-			   		if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb=true;
4929
-			   		if ($this->array_options[$key] === '') $mandatorypb=true;
4930
-			   		if ($mandatorypb)
4931
-			   		{
4932
-			   			dol_syslog($this->error);
4933
-			   			$this->errors[]=$langs->trans('ErrorFieldRequired', $attributeLabel);
4934
-			   			return -1;
4935
-			   		}
4936
-			   	}
4937
-
4938
-				//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
4939
-				//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
4940
-
4941
-			   	switch ($attributeType)
4942
-			   	{
4943
-			   		case 'int':
4944
-			  			if (!is_numeric($value) && $value!='')
4945
-			   			{
4946
-			   				$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
4947
-			   				return -1;
4948
-			  			}
4949
-			   			elseif ($value=='')
4950
-			   			{
4951
-			   				$new_array_options[$key] = null;
4952
-			   			}
4953
-			 			break;
4954
-					case 'double':
4955
-						$value = price2num($value);
4956
-						if (!is_numeric($value) && $value!='')
4957
-						{
4958
-							dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
4959
-							$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
4960
-							return -1;
4961
-						}
4962
-						elseif ($value=='')
4963
-						{
4964
-							$new_array_options[$key] = null;
4965
-						}
4966
-						//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
4967
-						$new_array_options[$key] = $value;
4968
-						break;
4969
-			 		/*case 'select':	// Not required, we chosed value='0' for undefined values
4970
-             			if ($value=='-1')
4971
-             			{
4972
-             				$this->array_options[$key] = null;
4973
-             			}
4974
-             			break;*/
4975
-			   		case 'password':
4976
-			   			$algo='';
4977
-			   			if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']))
4978
-			   			{
4979
-			   				// If there is an encryption choice, we use it to crypt data before insert
4980
-			   				$tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
4981
-			   				$algo=reset($tmparrays);
4982
-			   				if ($algo != '')
4983
-			   				{
4984
-			   					//global $action;		// $action may be 'create', 'update', 'update_extras'...
4985
-			   					//var_dump($action);
4986
-			   					//var_dump($this->oldcopy);exit;
4987
-			   					if (is_object($this->oldcopy))		// If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
4988
-			   					{
4989
-			   						//var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
4990
-				   					if ($this->array_options[$key] == $this->oldcopy->array_options[$key])	// If old value crypted in database is same than submited new value, it means we don't change it, so we don't update.
4991
-				   					{
4992
-				   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
4993
-				   					}
4994
-									else
4995
-									{
4996
-										// var_dump($algo);
4997
-										$newvalue = dol_hash($this->array_options[$key], $algo);
4998
-										$new_array_options[$key] = $newvalue;
4999
-									}
5000
-			   					}
5001
-			   					else
5002
-			   					{
5003
-			   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
5004
-			   					}
5005
-			   				}
5006
-			   			}
5007
-			   			else	// Common usage
5008
-			   			{
5009
-			   				$new_array_options[$key] = $this->array_options[$key];
5010
-			   			}
5011
-			   			break;
5012
-			   		case 'price':
5013
-						$new_array_options[$key] = price2num($this->array_options[$key]);
5014
-						break;
5015
-					case 'date':
5016
-						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5017
-						break;
5018
-					case 'datetime':
5019
-						// If data is a string instead of a timestamp, we convert it
5020
-						if (! is_int($this->array_options[$key])) {
5021
-							$this->array_options[$key] = strtotime($this->array_options[$key]);
5022
-						}
5023
-						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5024
-						break;
5025
-		   			case 'link':
5026
-						$param_list=array_keys($attributeParam['options']);
5027
-						// 0 : ObjectName
5028
-						// 1 : classPath
5029
-						$InfoFieldList = explode(":", $param_list[0]);
5030
-						dol_include_once($InfoFieldList[1]);
5031
-						if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5032
-						{
5033
-							if ($value == '-1')	// -1 is key for no defined in combo list of objects
5034
-							{
5035
-								$new_array_options[$key]='';
5036
-							}
5037
-							elseif ($value)
5038
-							{
5039
-								$object = new $InfoFieldList[0]($this->db);
5040
-								if (is_numeric($value)) $res=$object->fetch($value);
5041
-								else $res=$object->fetch('',$value);
5042
-
5043
-								if ($res > 0) $new_array_options[$key]=$object->id;
5044
-								else
5045
-								{
5046
-									$this->error="Id/Ref '".$value."' for object '".$object->element."' not found";
5047
-									$this->db->rollback();
5048
-									return -1;
5049
-								}
5050
-							}
5051
-						}
5052
-						else
5053
-						{
5054
-							dol_syslog('Error bad setup of extrafield', LOG_WARNING);
5055
-						}
5056
-						break;
5057
-			   	}
5058
-			}
1674
+    /**
1675
+     *      Return list of id of contacts of object
1676
+     *
1677
+     *      @param	string	$source     Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
1678
+     *      @return array				Array of id of contacts (if source=external or internal)
1679
+     * 									Array of id of third parties with at least one contact on object (if source=thirdparty)
1680
+     */
1681
+    function getListContactId($source='external')
1682
+    {
1683
+        $contactAlreadySelected = array();
1684
+        $tab = $this->liste_contact(-1,$source);
1685
+        $num=count($tab);
1686
+        $i = 0;
1687
+        while ($i < $num)
1688
+        {
1689
+            if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
1690
+            else  $contactAlreadySelected[$i] = $tab[$i]['id'];
1691
+            $i++;
1692
+        }
1693
+        return $contactAlreadySelected;
1694
+    }
5059 1695
 
5060
-			$this->db->begin();
5061 1696
 
5062
-			$table_element = $this->table_element;
5063
-			if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
1697
+    /**
1698
+     *	Link element with a project
1699
+     *
1700
+     *	@param     	int		$projectid		Project id to link element to
1701
+     *	@return		int						<0 if KO, >0 if OK
1702
+     */
1703
+    function setProject($projectid)
1704
+    {
1705
+        if (! $this->table_element)
1706
+        {
1707
+            dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined",LOG_ERR);
1708
+            return -1;
1709
+        }
5064 1710
 
5065
-			$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5066
-			dol_syslog(get_class($this)."::insertExtraFields delete", LOG_DEBUG);
5067
-			$this->db->query($sql_del);
1711
+        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1712
+        if ($this->table_element == 'actioncomm')
1713
+        {
1714
+            if ($projectid) $sql.= ' SET fk_project = '.$projectid;
1715
+            else $sql.= ' SET fk_project = NULL';
1716
+            $sql.= ' WHERE id = '.$this->id;
1717
+        }
1718
+        else
1719
+        {
1720
+            if ($projectid) $sql.= ' SET fk_projet = '.$projectid;
1721
+            else $sql.= ' SET fk_projet = NULL';
1722
+            $sql.= ' WHERE rowid = '.$this->id;
1723
+        }
5068 1724
 
5069
-			$sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
5070
-			foreach($new_array_options as $key => $value)
5071
-			{
5072
-				$attributeKey = substr($key,8);   // Remove 'options_' prefix
5073
-				// Add field of attribut
5074
-				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator
5075
-					$sql.=",".$attributeKey;
5076
-			}
5077
-			$sql .= ") VALUES (".$this->id;
5078
-
5079
-			foreach($new_array_options as $key => $value)
5080
-			{
5081
-				$attributeKey = substr($key,8);   // Remove 'options_' prefix
5082
-				// Add field of attribute
5083
-				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator)
5084
-				{
5085
-					if ($new_array_options[$key] != '')
5086
-					{
5087
-						$sql.=",'".$this->db->escape($new_array_options[$key])."'";
5088
-					}
5089
-					else
5090
-					{
5091
-						$sql.=",null";
5092
-					}
5093
-				}
5094
-			}
5095
-			$sql.=")";
1725
+        dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
1726
+        if ($this->db->query($sql))
1727
+        {
1728
+            $this->fk_project = $projectid;
1729
+            return 1;
1730
+        }
1731
+        else
1732
+        {
1733
+            dol_print_error($this->db);
1734
+            return -1;
1735
+        }
1736
+    }
5096 1737
 
5097
-			dol_syslog(get_class($this)."::insertExtraFields insert", LOG_DEBUG);
5098
-			$resql = $this->db->query($sql);
5099
-			if (! $resql)
5100
-			{
5101
-				$this->error=$this->db->lasterror();
5102
-				$error++;
5103
-			}
1738
+    /**
1739
+     *  Change the payments methods
1740
+     *
1741
+     *  @param		int		$id		Id of new payment method
1742
+     *  @return		int				>0 if OK, <0 if KO
1743
+     */
1744
+    function setPaymentMethods($id)
1745
+    {
1746
+        dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
1747
+        if ($this->statut >= 0 || $this->element == 'societe')
1748
+        {
1749
+            // TODO uniformize field name
1750
+            $fieldname = 'fk_mode_reglement';
1751
+            if ($this->element == 'societe') $fieldname = 'mode_reglement';
1752
+            if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
5104 1753
 
5105
-			if (! $error && $trigger)
5106
-			{
5107
-				// Call trigger
5108
-				$this->context=array('extrafieldaddupdate'=>1);
5109
-				$result=$this->call_trigger($trigger, $userused);
5110
-				if ($result < 0) $error++;
5111
-				// End call trigger
5112
-			}
5113
-
5114
-			if ($error)
5115
-			{
5116
-				$this->db->rollback();
5117
-				return -1;
5118
-			}
5119
-			else
5120
-			{
5121
-				$this->db->commit();
5122
-				return 1;
5123
-			}
5124
-		}
5125
-		else return 0;
5126
-	}
5127
-
5128
-	/**
5129
-	 *	Update an extra field value for the current object.
5130
-	 *  Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
5131
-	 *
5132
-	 *  @param  string      $key    		Key of the extrafield (without starting 'options_')
5133
-	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
5134
-	 *  @param	User		$userused		Object user
5135
-	 *  @return int                 		-1=error, O=did nothing, 1=OK
5136
-	 *  @see setValueFrom, insertExtraFields
5137
-	 */
5138
-	function updateExtraField($key, $trigger=null, $userused=null)
5139
-	{
5140
-		global $conf,$langs,$user;
5141
-
5142
-		if (empty($userused)) $userused=$user;
5143
-
5144
-		$error=0;
5145
-
5146
-		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
5147
-
5148
-		if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
5149
-		{
5150
-			// Check parameters
5151
-			$langs->load('admin');
5152
-			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5153
-			$extrafields = new ExtraFields($this->db);
5154
-			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
5155
-
5156
-			$value=$this->array_options["options_".$key];
5157
-
5158
-			$attributeType     = $extrafields->attributes[$this->table_element]['type'][$key];
5159
-			$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$key];
5160
-			$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$key];
5161
-			$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
5162
-
5163
-			//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5164
-			//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5165
-
5166
-			switch ($attributeType)
5167
-			{
5168
-				case 'int':
5169
-					if (!is_numeric($value) && $value!='')
5170
-					{
5171
-						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
5172
-						return -1;
5173
-					}
5174
-					elseif ($value=='')
5175
-					{
5176
-						$this->array_options["options_".$key] = null;
5177
-					}
5178
-					break;
5179
-				case 'double':
5180
-					$value = price2num($value);
5181
-					if (!is_numeric($value) && $value!='')
5182
-					{
5183
-						dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5184
-						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5185
-						return -1;
5186
-					}
5187
-					elseif ($value=='')
5188
-					{
5189
-						$this->array_options["options_".$key] = null;
5190
-					}
5191
-					//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5192
-					$this->array_options["options_".$key] = $value;
5193
-					break;
5194
-			 	/*case 'select':	// Not required, we chosed value='0' for undefined values
5195
-             		if ($value=='-1')
5196
-             		{
5197
-             			$this->array_options[$key] = null;
5198
-             		}
5199
-             		break;*/
5200
-				case 'price':
5201
-					$this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
5202
-					break;
5203
-				case 'date':
5204
-					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5205
-					break;
5206
-				case 'datetime':
5207
-					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5208
-					break;
5209
-				case 'link':
5210
-					$param_list=array_keys($attributeParam['options']);
5211
-					// 0 : ObjectName
5212
-					// 1 : classPath
5213
-					$InfoFieldList = explode(":", $param_list[0]);
5214
-					dol_include_once($InfoFieldList[1]);
5215
-					if ($value)
5216
-					{
5217
-						$object = new $InfoFieldList[0]($this->db);
5218
-						$object->fetch(0,$value);
5219
-						$this->array_options["options_".$key]=$object->id;
5220
-					}
5221
-					break;
5222
-			}
5223
-
5224
-			$this->db->begin();
5225
-			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
5226
-			$sql .= " WHERE fk_object = ".$this->id;
5227
-			$resql = $this->db->query($sql);
5228
-			if (! $resql)
5229
-			{
5230
-				$error++;
5231
-				$this->error=$this->db->lasterror();
5232
-			}
1754
+            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1755
+            $sql .= ' SET '.$fieldname.' = '.$id;
1756
+            $sql .= ' WHERE rowid='.$this->id;
5233 1757
 
5234
-			if (! $error && $trigger)
5235
-			{
5236
-				// Call trigger
5237
-				$this->context=array('extrafieldupdate'=>1);
5238
-				$result=$this->call_trigger($trigger, $userused);
5239
-				if ($result < 0) $error++;
5240
-				// End call trigger
5241
-			}
5242
-
5243
-			if ($error)
5244
-			{
5245
-				dol_syslog(get_class($this) . "::".__METHOD__ . $this->error, LOG_ERR);
5246
-				$this->db->rollback();
5247
-				return -1;
5248
-			}
5249
-			else
5250
-			{
5251
-				$this->db->commit();
5252
-				return 1;
5253
-			}
5254
-		}
5255
-		else return 0;
5256
-	}
5257
-
5258
-
5259
-	/**
5260
-	 * Return HTML string to put an input field into a page
5261
-	 * Code very similar with showInputField of extra fields
5262
-	 *
5263
-	 * @param  array   		$val	       Array of properties for field to show
5264
-	 * @param  string  		$key           Key of attribute
5265
-	 * @param  string  		$value         Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value)
5266
-	 * @param  string  		$moreparam     To add more parameters on html input tag
5267
-	 * @param  string  		$keysuffix     Prefix string to add into name and id of field (can be used to avoid duplicate names)
5268
-	 * @param  string  		$keyprefix     Suffix string to add into name and id of field (can be used to avoid duplicate names)
5269
-	 * @param  string|int		$morecss       Value for css to define style/length of field. May also be a numeric.
5270
-	 * @return string
5271
-	 */
5272
-	function showInputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $morecss=0)
5273
-	{
5274
-		global $conf,$langs,$form;
5275
-
5276
-		if (! is_object($form))
5277
-		{
5278
-			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5279
-			$form=new Form($this->db);
5280
-		}
5281
-
5282
-		$val=$this->fields[$key];
5283
-
5284
-		$out='';
5285
-        $type='';
5286
-        $param = array();
5287
-        $param['options']=array();
5288
-        $size =$this->fields[$key]['size'];
5289
-        // Because we work on extrafields
5290
-        if(preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)){
5291
-            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5292
-            $type ='link';
5293
-        } elseif(preg_match('/^link:(.*):(.*)/i', $val['type'], $reg)) {
5294
-            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5295
-            $type ='link';
5296
-        } elseif(preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5297
-            $param['options']=array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4]=>'N');
5298
-            $type ='sellist';
5299
-        } elseif(preg_match('/varchar\((\d+)\)/', $val['type'],$reg)) {
5300
-            $param['options']=array();
5301
-            $type ='varchar';
5302
-            $size=$reg[1];
5303
-        } elseif(preg_match('/varchar/', $val['type'])) {
5304
-            $param['options']=array();
5305
-            $type ='varchar';
5306
-        } elseif(is_array($this->fields[$key]['arrayofkeyval'])) {
5307
-            $param['options']=$this->fields[$key]['arrayofkeyval'];
5308
-            $type ='select';
5309
-        } else {
5310
-            $param['options']=array();
5311
-            $type =$this->fields[$key]['type'];
1758
+            if ($this->db->query($sql))
1759
+            {
1760
+                $this->mode_reglement_id = $id;
1761
+                // for supplier
1762
+                if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
1763
+                return 1;
1764
+            }
1765
+            else
1766
+            {
1767
+                dol_syslog(get_class($this).'::setPaymentMethods Erreur '.$sql.' - '.$this->db->error());
1768
+                $this->error=$this->db->error();
1769
+                return -1;
1770
+            }
5312 1771
         }
1772
+        else
1773
+        {
1774
+            dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
1775
+            $this->error='Status of the object is incompatible '.$this->statut;
1776
+            return -2;
1777
+        }
1778
+    }
5313 1779
 
5314
-		$label=$this->fields[$key]['label'];
5315
-		//$elementtype=$this->fields[$key]['elementtype'];	// Seems not used
5316
-		$default=$this->fields[$key]['default'];
5317
-		$computed=$this->fields[$key]['computed'];
5318
-		$unique=$this->fields[$key]['unique'];
5319
-		$required=$this->fields[$key]['required'];
5320
-
5321
-		$langfile=$this->fields[$key]['langfile'];
5322
-		$list=$this->fields[$key]['list'];
5323
-		$hidden=abs($this->fields[$key]['visible'])!=1?1:0;
1780
+    /**
1781
+     *  Change the multicurrency code
1782
+     *
1783
+     *  @param		string	$code	multicurrency code
1784
+     *  @return		int				>0 if OK, <0 if KO
1785
+     */
1786
+    function setMulticurrencyCode($code)
1787
+    {
1788
+        dol_syslog(get_class($this).'::setMulticurrencyCode('.$id.')');
1789
+        if ($this->statut >= 0 || $this->element == 'societe')
1790
+        {
1791
+            $fieldname = 'multicurrency_code';
5324 1792
 
5325
-		$objectid = $this->id;
1793
+            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1794
+            $sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'";
1795
+            $sql .= ' WHERE rowid='.$this->id;
5326 1796
 
1797
+            if ($this->db->query($sql))
1798
+            {
1799
+                $this->multicurrency_code = $code;
5327 1800
 
5328
-		if ($computed)
5329
-		{
5330
-			if (! preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
5331
-			else return '';
5332
-		}
1801
+                list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
1802
+                if ($rate) $this->setMulticurrencyRate($rate,2);
5333 1803
 
1804
+                return 1;
1805
+            }
1806
+            else
1807
+            {
1808
+                dol_syslog(get_class($this).'::setMulticurrencyCode Erreur '.$sql.' - '.$this->db->error());
1809
+                $this->error=$this->db->error();
1810
+                return -1;
1811
+            }
1812
+        }
1813
+        else
1814
+        {
1815
+            dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
1816
+            $this->error='Status of the object is incompatible '.$this->statut;
1817
+            return -2;
1818
+        }
1819
+    }
5334 1820
 
5335
-		// Use in priority showsize from parameters, then $val['css'] then autodefine
5336
-		if (empty($morecss) && ! empty($val['css']))
5337
-		{
5338
-			$showsize = $val['css'];
5339
-		}
5340
-		if (empty($morecss))
5341
-		{
5342
-			if ($type == 'date')
5343
-			{
5344
-				$morecss = 'minwidth100imp';
5345
-			}
5346
-			elseif ($type == 'datetime')
5347
-			{
5348
-				$morecss = 'minwidth200imp';
5349
-			}
5350
-			elseif (in_array($type,array('int','integer','price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/',$type))
5351
-			{
5352
-				$morecss = 'maxwidth75';
5353
-                        }elseif ($type == 'url')
5354
-			{
5355
-				$morecss='minwidth400';
5356
-			}
5357
-			elseif ($type == 'boolean')
5358
-			{
5359
-				$morecss='';
5360
-			}
5361
-			else
5362
-			{
5363
-				if (round($size) < 12)
5364
-				{
5365
-					$morecss = 'minwidth100';
5366
-				}
5367
-				else if (round($size) <= 48)
5368
-				{
5369
-					$morecss = 'minwidth200';
5370
-				}
5371
-				else
5372
-				{
5373
-					$morecss = 'minwidth400';
5374
-				}
5375
-			}
5376
-		}
5377
-
5378
-		if (in_array($type,array('date','datetime')))
5379
-		{
5380
-			$tmp=explode(',',$size);
5381
-			$newsize=$tmp[0];
5382
-
5383
-			$showtime = in_array($type,array('datetime')) ? 1 : 0;
5384
-
5385
-			// Do not show current date when field not required (see selectDate() method)
5386
-			if (!$required && $value == '') $value = '-1';
5387
-
5388
-			// TODO Must also support $moreparam
5389
-			$out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
5390
-		}
5391
-		elseif (in_array($type,array('int','integer')))
5392
-		{
5393
-			$tmp=explode(',',$size);
5394
-			$newsize=$tmp[0];
5395
-			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5396
-		}
5397
-		elseif (preg_match('/varchar/', $type))
5398
-		{
5399
-			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$size.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5400
-		}
5401
-		elseif (in_array($type, array('mail', 'phone', 'url')))
5402
-		{
5403
-			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5404
-		}
5405
-		elseif ($type == 'text')
5406
-		{
5407
-			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5408
-			{
5409
-				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5410
-				$doleditor=new DolEditor($keyprefix.$key.$keysuffix,$value,'',200,'dolibarr_notes','In',false,false,false,ROWS_5,'90%');
5411
-				$out=$doleditor->Create(1);
5412
-			}
5413
-			else
5414
-			{
5415
-				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5416
-			}
5417
-		}
5418
-		elseif ($type == 'html')
5419
-		{
5420
-			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5421
-			{
5422
-				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5423
-				$doleditor=new DolEditor($keyprefix.$key.$keysuffix,$value,'',200,'dolibarr_notes','In',false,false,! empty($conf->fckeditor->enabled) && $conf->global->FCKEDITOR_ENABLE_SOCIETE,ROWS_5,'90%');
5424
-				$out=$doleditor->Create(1);
5425
-			}
5426
-			else
5427
-			{
5428
-				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5429
-			}
5430
-		}
5431
-		elseif ($type == 'boolean')
5432
-		{
5433
-			$checked='';
5434
-			if (!empty($value)) {
5435
-				$checked=' checked value="1" ';
5436
-			} else {
5437
-				$checked=' value="1" ';
5438
-			}
5439
-			$out='<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam?$moreparam:'').'>';
5440
-		}
5441
-		elseif ($type == 'price')
5442
-		{
5443
-			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5444
-				$value=price($value);
5445
-			}
5446
-			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> '.$langs->getCurrencySymbol($conf->currency);
5447
-		}
5448
-		elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/',$type))
5449
-		{
5450
-			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5451
-				$value=price($value);
5452
-			}
5453
-			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> ';
5454
-		}
5455
-		elseif ($type == 'select')
5456
-		{
5457
-			$out = '';
5458
-			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5459
-			{
5460
-				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5461
-				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5462
-			}
1821
+    /**
1822
+     *  Change the multicurrency rate
1823
+     *
1824
+     *  @param		double	$rate	multicurrency rate
1825
+     *  @param		int		$mode	mode 1 : amounts in company currency will be recalculated, mode 2 : amounts in foreign currency
1826
+     *  @return		int				>0 if OK, <0 if KO
1827
+     */
1828
+    function setMulticurrencyRate($rate, $mode=1)
1829
+    {
1830
+        dol_syslog(get_class($this).'::setMulticurrencyRate('.$id.')');
1831
+        if ($this->statut >= 0 || $this->element == 'societe')
1832
+        {
1833
+            $fieldname = 'multicurrency_tx';
5463 1834
 
5464
-			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5465
-                if((! isset($this->fields[$key]['default'])) ||($this->fields[$key]['notnull']!=1))$out.='<option value="0">&nbsp;</option>';
5466
-			foreach ($param['options'] as $key => $val)
5467
-			{
5468
-				if ((string) $key == '') continue;
5469
-				list($val, $parent) = explode('|', $val);
5470
-				$out.='<option value="'.$key.'"';
5471
-				$out.= (((string) $value == (string) $key)?' selected':'');
5472
-				$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5473
-				$out.='>'.$val.'</option>';
5474
-			}
5475
-			$out.='</select>';
5476
-		}
5477
-		elseif ($type == 'sellist')
5478
-		{
5479
-			$out = '';
5480
-			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5481
-			{
5482
-				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5483
-				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5484
-			}
1835
+            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1836
+            $sql .= ' SET '.$fieldname.' = '.$rate;
1837
+            $sql .= ' WHERE rowid='.$this->id;
5485 1838
 
5486
-			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5487
-			if (is_array($param['options']))
5488
-			{
5489
-				$param_list=array_keys($param['options']);
5490
-				$InfoFieldList = explode(":", $param_list[0]);
5491
-				$parentName='';
5492
-				$parentField='';
5493
-				// 0 : tableName
5494
-				// 1 : label field name
5495
-				// 2 : key fields name (if differ of rowid)
5496
-				// 3 : key field parent (for dependent lists)
5497
-				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5498
-				$keyList=(empty($InfoFieldList[2])?'rowid':$InfoFieldList[2].' as rowid');
5499
-
5500
-
5501
-				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4]))
5502
-				{
5503
-					if (strpos($InfoFieldList[4], 'extra.') !== false)
5504
-					{
5505
-						$keyList='main.'.$InfoFieldList[2].' as rowid';
5506
-					} else {
5507
-						$keyList=$InfoFieldList[2].' as rowid';
5508
-					}
5509
-				}
5510
-				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3]))
5511
-				{
5512
-					list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
5513
-					$keyList.= ', '.$parentField;
5514
-				}
1839
+            if ($this->db->query($sql))
1840
+            {
1841
+                $this->multicurrency_tx = $rate;
1842
+
1843
+                // Update line price
1844
+                if (!empty($this->lines))
1845
+                {
1846
+                    foreach ($this->lines as &$line)
1847
+                    {
1848
+                        if($mode == 1) {
1849
+                            $line->subprice = 0;
1850
+                        }
1851
+
1852
+                        switch ($this->element) {
1853
+                            case 'propal':
1854
+                                $this->updateline(
1855
+                                    $line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1856
+                                    ($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1857
+                                    $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
1858
+                                    $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1859
+                                );
1860
+                                break;
1861
+                            case 'commande':
1862
+                                $this->updateline(
1863
+                                    $line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1864
+                                    $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
1865
+                                    $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1866
+                                    $line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1867
+                                );
1868
+                                break;
1869
+                            case 'facture':
1870
+                                $this->updateline(
1871
+                                    $line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1872
+                                    $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
1873
+                                    $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1874
+                                    $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
1875
+                                );
1876
+                                break;
1877
+                            case 'supplier_proposal':
1878
+                                $this->updateline(
1879
+                                    $line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1880
+                                    ($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1881
+                                    $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
1882
+                                    $line->ref_fourn, $line->multicurrency_subprice
1883
+                                );
1884
+                                break;
1885
+                            case 'order_supplier':
1886
+                                $this->updateline(
1887
+                                    $line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1888
+                                    $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
1889
+                                    $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1890
+                                );
1891
+                                break;
1892
+                            case 'invoice_supplier':
1893
+                                $this->updateline(
1894
+                                    $line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
1895
+                                    $line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
1896
+                                    $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1897
+                                );
1898
+                                break;
1899
+                            default:
1900
+                                dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
1901
+                                break;
1902
+                        }
1903
+                    }
1904
+                }
1905
+
1906
+                return 1;
1907
+            }
1908
+            else
1909
+            {
1910
+                dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
1911
+                $this->error=$this->db->error();
1912
+                return -1;
1913
+            }
1914
+        }
1915
+        else
1916
+        {
1917
+            dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
1918
+            $this->error='Status of the object is incompatible '.$this->statut;
1919
+            return -2;
1920
+        }
1921
+    }
5515 1922
 
5516
-				$fields_label = explode('|',$InfoFieldList[1]);
5517
-				if (is_array($fields_label))
5518
-				{
5519
-					$keyList .=', ';
5520
-					$keyList .= implode(', ', $fields_label);
5521
-				}
1923
+    /**
1924
+     *  Change the payments terms
1925
+     *
1926
+     *  @param		int		$id		Id of new payment terms
1927
+     *  @return		int				>0 if OK, <0 if KO
1928
+     */
1929
+    function setPaymentTerms($id)
1930
+    {
1931
+        dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
1932
+        if ($this->statut >= 0 || $this->element == 'societe')
1933
+        {
1934
+            // TODO uniformize field name
1935
+            $fieldname = 'fk_cond_reglement';
1936
+            if ($this->element == 'societe') $fieldname = 'cond_reglement';
1937
+            if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
5522 1938
 
5523
-				$sqlwhere='';
5524
-				$sql = 'SELECT '.$keyList;
5525
-				$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
5526
-				if (!empty($InfoFieldList[4]))
5527
-				{
5528
-					// can use SELECT request
5529
-					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5530
-						$InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
5531
-					}
5532
-
5533
-					// current object id can be use into filter
5534
-					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5535
-						$InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
5536
-					} else {
5537
-						$InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
5538
-					}
5539
-					//We have to join on extrafield table
5540
-					if (strpos($InfoFieldList[4], 'extra')!==false)
5541
-					{
5542
-						$sql.= ' as main, '.MAIN_DB_PREFIX .$InfoFieldList[0].'_extrafields as extra';
5543
-						$sqlwhere.= ' WHERE extra.fk_object=main.'.$InfoFieldList[2]. ' AND '.$InfoFieldList[4];
5544
-					}
5545
-					else
5546
-					{
5547
-						$sqlwhere.= ' WHERE '.$InfoFieldList[4];
5548
-					}
5549
-				}
5550
-				else
5551
-				{
5552
-					$sqlwhere.= ' WHERE 1=1';
5553
-				}
5554
-				// Some tables may have field, some other not. For the moment we disable it.
5555
-				if (in_array($InfoFieldList[0],array('tablewithentity')))
5556
-				{
5557
-					$sqlwhere.= ' AND entity = '.$conf->entity;
5558
-				}
5559
-				$sql.=$sqlwhere;
5560
-				//print $sql;
1939
+            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1940
+            $sql .= ' SET '.$fieldname.' = '.$id;
1941
+            $sql .= ' WHERE rowid='.$this->id;
5561 1942
 
5562
-				$sql .= ' ORDER BY ' . implode(', ', $fields_label);
1943
+            if ($this->db->query($sql))
1944
+            {
1945
+                $this->cond_reglement_id = $id;
1946
+                // for supplier
1947
+                if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
1948
+                $this->cond_reglement = $id;	// for compatibility
1949
+                return 1;
1950
+            }
1951
+            else
1952
+            {
1953
+                dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
1954
+                $this->error=$this->db->error();
1955
+                return -1;
1956
+            }
1957
+        }
1958
+        else
1959
+        {
1960
+            dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
1961
+            $this->error='Status of the object is incompatible '.$this->statut;
1962
+            return -2;
1963
+        }
1964
+    }
5563 1965
 
5564
-				dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
5565
-				$resql = $this->db->query($sql);
5566
-				if ($resql)
5567
-				{
5568
-					$out.='<option value="0">&nbsp;</option>';
5569
-					$num = $this->db->num_rows($resql);
5570
-					$i = 0;
5571
-					while ($i < $num)
5572
-					{
5573
-						$labeltoshow='';
5574
-						$obj = $this->db->fetch_object($resql);
5575
-
5576
-						// Several field into label (eq table:code|libelle:rowid)
5577
-						$notrans = false;
5578
-						$fields_label = explode('|',$InfoFieldList[1]);
5579
-						if (is_array($fields_label))
5580
-						{
5581
-							$notrans = true;
5582
-							foreach ($fields_label as $field_toshow)
5583
-							{
5584
-								$labeltoshow.= $obj->$field_toshow.' ';
5585
-							}
5586
-						}
5587
-						else
5588
-						{
5589
-							$labeltoshow=$obj->{$InfoFieldList[1]};
5590
-						}
5591
-						$labeltoshow=dol_trunc($labeltoshow,45);
5592
-
5593
-						if ($value == $obj->rowid)
5594
-						{
5595
-							foreach ($fields_label as $field_toshow)
5596
-							{
5597
-								$translabel=$langs->trans($obj->$field_toshow);
5598
-								if ($translabel!=$obj->$field_toshow) {
5599
-									$labeltoshow=dol_trunc($translabel,18).' ';
5600
-								}else {
5601
-									$labeltoshow=dol_trunc($obj->$field_toshow,18).' ';
5602
-								}
1966
+    /**
1967
+     *	Define delivery address
1968
+     *  @deprecated
1969
+     *
1970
+     *	@param      int		$id		Address id
1971
+     *	@return     int				<0 si ko, >0 si ok
1972
+     */
1973
+    function setDeliveryAddress($id)
1974
+    {
1975
+        $fieldname = 'fk_delivery_address';
1976
+        if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
1977
+
1978
+        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
1979
+        $sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
1980
+
1981
+        if ($this->db->query($sql))
1982
+        {
1983
+            $this->fk_delivery_address = $id;
1984
+            return 1;
1985
+        }
1986
+        else
1987
+        {
1988
+            $this->error=$this->db->error();
1989
+            dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
1990
+            return -1;
1991
+        }
1992
+    }
1993
+
1994
+
1995
+    /**
1996
+     *  Change the shipping method
1997
+     *
1998
+     *  @param      int     $shipping_method_id     Id of shipping method
1999
+     *  @param      bool    $notrigger              false=launch triggers after, true=disable triggers
2000
+     *  @param      User	$userused               Object user
2001
+     *
2002
+     *  @return     int              1 if OK, 0 if KO
2003
+     */
2004
+    function setShippingMethod($shipping_method_id, $notrigger=false, $userused=null)
2005
+    {
2006
+        global $user;
2007
+
2008
+        if (empty($userused)) $userused=$user;
2009
+
2010
+        $error = 0;
2011
+
2012
+        if (! $this->table_element) {
2013
+            dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined",LOG_ERR);
2014
+            return -1;
2015
+        }
2016
+
2017
+        $this->db->begin();
2018
+
2019
+        if ($shipping_method_id<0) $shipping_method_id='NULL';
2020
+        dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2021
+
2022
+        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2023
+        $sql.= " SET fk_shipping_method = ".$shipping_method_id;
2024
+        $sql.= " WHERE rowid=".$this->id;
2025
+        $resql = $this->db->query($sql);
2026
+        if (! $resql) {
2027
+            dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2028
+            $this->error = $this->db->lasterror();
2029
+            $error++;
2030
+        } else {
2031
+            if (!$notrigger)
2032
+            {
2033
+                // Call trigger
2034
+                $this->context=array('shippingmethodupdate'=>1);
2035
+                $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2036
+                if ($result < 0) $error++;
2037
+                // End call trigger
2038
+            }
2039
+        }
2040
+        if ($error)
2041
+        {
2042
+            $this->db->rollback();
2043
+            return -1;
2044
+        } else {
2045
+            $this->shipping_method_id = ($shipping_method_id=='NULL')?null:$shipping_method_id;
2046
+            $this->db->commit();
2047
+            return 1;
2048
+        }
2049
+    }
2050
+
2051
+
2052
+    /**
2053
+     *  Change the warehouse
2054
+     *
2055
+     *  @param      int     $warehouse_id     Id of warehouse
2056
+     *  @return     int              1 if OK, 0 if KO
2057
+     */
2058
+    function setWarehouse($warehouse_id)
2059
+    {
2060
+        if (! $this->table_element) {
2061
+            dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined",LOG_ERR);
2062
+            return -1;
2063
+        }
2064
+        if ($warehouse_id<0) $warehouse_id='NULL';
2065
+        dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2066
+
2067
+        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2068
+        $sql.= " SET fk_warehouse = ".$warehouse_id;
2069
+        $sql.= " WHERE rowid=".$this->id;
2070
+
2071
+        if ($this->db->query($sql)) {
2072
+            $this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
2073
+            return 1;
2074
+        } else {
2075
+            dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2076
+            $this->error=$this->db->error();
2077
+            return 0;
2078
+        }
2079
+    }
2080
+
2081
+
2082
+    /**
2083
+     *		Set last model used by doc generator
2084
+     *
2085
+     *		@param		User	$user		User object that make change
2086
+     *		@param		string	$modelpdf	Modele name
2087
+     *		@return		int					<0 if KO, >0 if OK
2088
+     */
2089
+    function setDocModel($user, $modelpdf)
2090
+    {
2091
+        if (! $this->table_element)
2092
+        {
2093
+            dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined",LOG_ERR);
2094
+            return -1;
2095
+        }
2096
+
2097
+        $newmodelpdf=dol_trunc($modelpdf,255);
2098
+
2099
+        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2100
+        $sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2101
+        $sql.= " WHERE rowid = ".$this->id;
2102
+        // if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
2103
+        // if ($this->element == 'propal')  $sql.= " AND fk_statut = 0";
2104
+
2105
+        dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2106
+        $resql=$this->db->query($sql);
2107
+        if ($resql)
2108
+        {
2109
+            $this->modelpdf=$modelpdf;
2110
+            return 1;
2111
+        }
2112
+        else
2113
+        {
2114
+            dol_print_error($this->db);
2115
+            return 0;
2116
+        }
2117
+    }
2118
+
2119
+
2120
+    /**
2121
+     *  Change the bank account
2122
+     *
2123
+     *  @param		int		$fk_account		Id of bank account
2124
+     *  @param      bool    $notrigger      false=launch triggers after, true=disable triggers
2125
+     *  @param      User	$userused		Object user
2126
+     *  @return		int				1 if OK, 0 if KO
2127
+     */
2128
+    function setBankAccount($fk_account, $notrigger=false, $userused=null)
2129
+    {
2130
+        global $user;
2131
+
2132
+        if (empty($userused)) $userused=$user;
2133
+
2134
+        $error = 0;
2135
+
2136
+        if (! $this->table_element) {
2137
+            dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined",LOG_ERR);
2138
+            return -1;
2139
+        }
2140
+        $this->db->begin();
2141
+
2142
+        if ($fk_account<0) $fk_account='NULL';
2143
+        dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2144
+
2145
+        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2146
+        $sql.= " SET fk_account = ".$fk_account;
2147
+        $sql.= " WHERE rowid=".$this->id;
2148
+
2149
+        $resql = $this->db->query($sql);
2150
+        if (! $resql)
2151
+        {
2152
+            dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2153
+            $this->error = $this->db->lasterror();
2154
+            $error++;
2155
+        }
2156
+        else
2157
+        {
2158
+            if (!$notrigger)
2159
+            {
2160
+                // Call trigger
2161
+                $this->context=array('bankaccountupdate'=>1);
2162
+                $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2163
+                if ($result < 0) $error++;
2164
+                // End call trigger
2165
+            }
2166
+        }
2167
+        if ($error)
2168
+        {
2169
+            $this->db->rollback();
2170
+            return -1;
2171
+        }
2172
+        else
2173
+        {
2174
+            $this->fk_account = ($fk_account=='NULL')?null:$fk_account;
2175
+            $this->db->commit();
2176
+            return 1;
2177
+        }
2178
+    }
2179
+
2180
+
2181
+    // TODO: Move line related operations to CommonObjectLine?
2182
+
2183
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2184
+    /**
2185
+     *  Save a new position (field rang) for details lines.
2186
+     *  You can choose to set position for lines with already a position or lines without any position defined.
2187
+     *
2188
+     * 	@param		boolean		$renum			   True to renum all already ordered lines, false to renum only not already ordered lines.
2189
+     * 	@param		string		$rowidorder		   ASC or DESC
2190
+     * 	@param		boolean		$fk_parent_line    Table with fk_parent_line field or not
2191
+     * 	@return		int                            <0 if KO, >0 if OK
2192
+     */
2193
+    function line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
2194
+    {
2195
+        // phpcs:enable
2196
+        if (! $this->table_element_line)
2197
+        {
2198
+            dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined",LOG_ERR);
2199
+            return -1;
2200
+        }
2201
+        if (! $this->fk_element)
2202
+        {
2203
+            dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined",LOG_ERR);
2204
+            return -1;
2205
+        }
2206
+
2207
+        // Count number of lines to reorder (according to choice $renum)
2208
+        $nl=0;
2209
+        $sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2210
+        $sql.= ' WHERE '.$this->fk_element.'='.$this->id;
2211
+        if (! $renum) $sql.= ' AND rang = 0';
2212
+        if ($renum) $sql.= ' AND rang <> 0';
2213
+
2214
+        dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
2215
+        $resql = $this->db->query($sql);
2216
+        if ($resql)
2217
+        {
2218
+            $row = $this->db->fetch_row($resql);
2219
+            $nl = $row[0];
2220
+        }
2221
+        else dol_print_error($this->db);
2222
+        if ($nl > 0)
2223
+        {
2224
+            // The goal of this part is to reorder all lines, with all children lines sharing the same
2225
+            // counter that parents.
2226
+            $rows=array();
2227
+
2228
+            // We first search all lines that are parent lines (for multilevel details lines)
2229
+            $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2230
+            $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2231
+            if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
2232
+            $sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
2233
+
2234
+            dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
2235
+            $resql = $this->db->query($sql);
2236
+            if ($resql)
2237
+            {
2238
+                $i=0;
2239
+                $num = $this->db->num_rows($resql);
2240
+                while ($i < $num)
2241
+                {
2242
+                    $row = $this->db->fetch_row($resql);
2243
+                    $rows[] = $row[0];	// Add parent line into array rows
2244
+                    $childrens = $this->getChildrenOfLine($row[0]);
2245
+                    if (! empty($childrens))
2246
+                    {
2247
+                        foreach($childrens as $child)
2248
+                        {
2249
+                            array_push($rows, $child);
2250
+                        }
2251
+                    }
2252
+                    $i++;
2253
+                }
2254
+
2255
+                // Now we set a new number for each lines (parent and children with children included into parent tree)
2256
+                if (! empty($rows))
2257
+                {
2258
+                    foreach($rows as $key => $row)
2259
+                    {
2260
+                        $this->updateRangOfLine($row, ($key+1));
2261
+                    }
2262
+                }
2263
+            }
2264
+            else
2265
+            {
2266
+                dol_print_error($this->db);
2267
+            }
2268
+        }
2269
+        return 1;
2270
+    }
2271
+
2272
+    /**
2273
+     * 	Get children of line
2274
+     *
2275
+     * 	@param	int		$id		Id of parent line
2276
+     * 	@return	array			Array with list of children lines id
2277
+     */
2278
+    function getChildrenOfLine($id)
2279
+    {
2280
+        $rows=array();
2281
+
2282
+        $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2283
+        $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2284
+        $sql.= ' AND fk_parent_line = '.$id;
2285
+        $sql.= ' ORDER BY rang ASC';
2286
+
2287
+        dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
2288
+        $resql = $this->db->query($sql);
2289
+        if ($resql)
2290
+        {
2291
+            $i=0;
2292
+            $num = $this->db->num_rows($resql);
2293
+            while ($i < $num)
2294
+            {
2295
+                $row = $this->db->fetch_row($resql);
2296
+                $rows[$i] = $row[0];
2297
+                $i++;
2298
+            }
2299
+        }
2300
+
2301
+        return $rows;
2302
+    }
2303
+
2304
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2305
+    /**
2306
+     * 	Update a line to have a lower rank
2307
+     *
2308
+     * 	@param 	int			$rowid				Id of line
2309
+     * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2310
+     * 	@return	void
2311
+     */
2312
+    function line_up($rowid, $fk_parent_line=true)
2313
+    {
2314
+        // phpcs:enable
2315
+        $this->line_order(false, 'ASC', $fk_parent_line);
2316
+
2317
+        // Get rang of line
2318
+        $rang = $this->getRangOfLine($rowid);
2319
+
2320
+        // Update position of line
2321
+        $this->updateLineUp($rowid, $rang);
2322
+    }
2323
+
2324
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2325
+    /**
2326
+     * 	Update a line to have a higher rank
2327
+     *
2328
+     * 	@param	int			$rowid				Id of line
2329
+     * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2330
+     * 	@return	void
2331
+     */
2332
+    function line_down($rowid, $fk_parent_line=true)
2333
+    {
2334
+        // phpcs:enable
2335
+        $this->line_order(false, 'ASC', $fk_parent_line);
2336
+
2337
+        // Get rang of line
2338
+        $rang = $this->getRangOfLine($rowid);
2339
+
2340
+        // Get max value for rang
2341
+        $max = $this->line_max();
2342
+
2343
+        // Update position of line
2344
+        $this->updateLineDown($rowid, $rang, $max);
2345
+    }
2346
+
2347
+    /**
2348
+     * 	Update position of line (rang)
2349
+     *
2350
+     * 	@param	int		$rowid		Id of line
2351
+     * 	@param	int		$rang		Position
2352
+     * 	@return	void
2353
+     */
2354
+    function updateRangOfLine($rowid,$rang)
2355
+    {
2356
+        $fieldposition = 'rang';
2357
+        if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2358
+
2359
+        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2360
+        $sql.= ' WHERE rowid = '.$rowid;
2361
+
2362
+        dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
2363
+        if (! $this->db->query($sql))
2364
+        {
2365
+            dol_print_error($this->db);
2366
+        }
2367
+    }
2368
+
2369
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2370
+    /**
2371
+     * 	Update position of line with ajax (rang)
2372
+     *
2373
+     * 	@param	array	$rows	Array of rows
2374
+     * 	@return	void
2375
+     */
2376
+    function line_ajaxorder($rows)
2377
+    {
2378
+        // phpcs:enable
2379
+        $num = count($rows);
2380
+        for ($i = 0 ; $i < $num ; $i++)
2381
+        {
2382
+            $this->updateRangOfLine($rows[$i], ($i+1));
2383
+        }
2384
+    }
2385
+
2386
+    /**
2387
+     * 	Update position of line up (rang)
2388
+     *
2389
+     * 	@param	int		$rowid		Id of line
2390
+     * 	@param	int		$rang		Position
2391
+     * 	@return	void
2392
+     */
2393
+    function updateLineUp($rowid,$rang)
2394
+    {
2395
+        if ($rang > 1)
2396
+        {
2397
+            $fieldposition = 'rang';
2398
+            if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2399
+
2400
+            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang ;
2401
+            $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2402
+            $sql.= ' AND rang = '.($rang - 1);
2403
+            if ($this->db->query($sql) )
2404
+            {
2405
+                $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang - 1);
2406
+                $sql.= ' WHERE rowid = '.$rowid;
2407
+                if (! $this->db->query($sql) )
2408
+                {
2409
+                    dol_print_error($this->db);
2410
+                }
2411
+            }
2412
+            else
2413
+            {
2414
+                dol_print_error($this->db);
2415
+            }
2416
+        }
2417
+    }
2418
+
2419
+    /**
2420
+     * 	Update position of line down (rang)
2421
+     *
2422
+     * 	@param	int		$rowid		Id of line
2423
+     * 	@param	int		$rang		Position
2424
+     * 	@param	int		$max		Max
2425
+     * 	@return	void
2426
+     */
2427
+    function updateLineDown($rowid,$rang,$max)
2428
+    {
2429
+        if ($rang < $max)
2430
+        {
2431
+            $fieldposition = 'rang';
2432
+            if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2433
+
2434
+            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2435
+            $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2436
+            $sql.= ' AND rang = '.($rang+1);
2437
+            if ($this->db->query($sql) )
2438
+            {
2439
+                $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang+1);
2440
+                $sql.= ' WHERE rowid = '.$rowid;
2441
+                if (! $this->db->query($sql) )
2442
+                {
2443
+                    dol_print_error($this->db);
2444
+                }
2445
+            }
2446
+            else
2447
+            {
2448
+                dol_print_error($this->db);
2449
+            }
2450
+        }
2451
+    }
2452
+
2453
+    /**
2454
+     * 	Get position of line (rang)
2455
+     *
2456
+     * 	@param		int		$rowid		Id of line
2457
+     *  @return		int     			Value of rang in table of lines
2458
+     */
2459
+    function getRangOfLine($rowid)
2460
+    {
2461
+        $sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2462
+        $sql.= ' WHERE rowid ='.$rowid;
2463
+
2464
+        dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2465
+        $resql = $this->db->query($sql);
2466
+        if ($resql)
2467
+        {
2468
+            $row = $this->db->fetch_row($resql);
2469
+            return $row[0];
2470
+        }
2471
+    }
2472
+
2473
+    /**
2474
+     * 	Get rowid of the line relative to its position
2475
+     *
2476
+     * 	@param		int		$rang		Rang value
2477
+     *  @return     int     			Rowid of the line
2478
+     */
2479
+    function getIdOfLine($rang)
2480
+    {
2481
+        $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2482
+        $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2483
+        $sql.= ' AND rang = '.$rang;
2484
+        $resql = $this->db->query($sql);
2485
+        if ($resql)
2486
+        {
2487
+            $row = $this->db->fetch_row($resql);
2488
+            return $row[0];
2489
+        }
2490
+    }
2491
+
2492
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2493
+    /**
2494
+     * 	Get max value used for position of line (rang)
2495
+     *
2496
+     * 	@param		int		$fk_parent_line		Parent line id
2497
+     *  @return     int  			   			Max value of rang in table of lines
2498
+     */
2499
+    function line_max($fk_parent_line=0)
2500
+    {
2501
+        // phpcs:enable
2502
+        // Search the last rang with fk_parent_line
2503
+        if ($fk_parent_line)
2504
+        {
2505
+            $sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2506
+            $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2507
+            $sql.= ' AND fk_parent_line = '.$fk_parent_line;
2508
+
2509
+            dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2510
+            $resql = $this->db->query($sql);
2511
+            if ($resql)
2512
+            {
2513
+                $row = $this->db->fetch_row($resql);
2514
+                if (! empty($row[0]))
2515
+                {
2516
+                    return $row[0];
2517
+                }
2518
+                else
2519
+                {
2520
+                    return $this->getRangOfLine($fk_parent_line);
2521
+                }
2522
+            }
2523
+        }
2524
+        // If not, search the last rang of element
2525
+        else
2526
+        {
2527
+            $sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2528
+            $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2529
+
2530
+            dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2531
+            $resql = $this->db->query($sql);
2532
+            if ($resql)
2533
+            {
2534
+                $row = $this->db->fetch_row($resql);
2535
+                return $row[0];
2536
+            }
2537
+        }
2538
+    }
2539
+
2540
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2541
+    /**
2542
+     *  Update external ref of element
2543
+     *
2544
+     *  @param      string		$ref_ext	Update field ref_ext
2545
+     *  @return     int      		   		<0 if KO, >0 if OK
2546
+     */
2547
+    function update_ref_ext($ref_ext)
2548
+    {
2549
+        // phpcs:enable
2550
+        if (! $this->table_element)
2551
+        {
2552
+            dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2553
+            return -1;
2554
+        }
2555
+
2556
+        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2557
+        $sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2558
+        $sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2559
+
2560
+        dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2561
+        if ($this->db->query($sql))
2562
+        {
2563
+            $this->ref_ext = $ref_ext;
2564
+            return 1;
2565
+        }
2566
+        else
2567
+        {
2568
+            $this->error=$this->db->error();
2569
+            return -1;
2570
+        }
2571
+    }
2572
+
2573
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2574
+    /**
2575
+     *  Update note of element
2576
+     *
2577
+     *  @param      string		$note		New value for note
2578
+     *  @param		string		$suffix		'', '_public' or '_private'
2579
+     *  @return     int      		   		<0 if KO, >0 if OK
2580
+     */
2581
+    function update_note($note, $suffix='')
2582
+    {
2583
+        // phpcs:enable
2584
+        global $user;
2585
+
2586
+        if (! $this->table_element)
2587
+        {
2588
+            $this->error='update_note was called on objet with property table_element not defined';
2589
+            dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2590
+            return -1;
2591
+        }
2592
+        if (! in_array($suffix,array('','_public','_private')))
2593
+        {
2594
+            $this->error='update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
2595
+            dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2596
+            return -2;
2597
+        }
2598
+        // Special cas
2599
+        //var_dump($this->table_element);exit;
2600
+        if ($this->table_element == 'product') $suffix='';
2601
+
2602
+        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2603
+        $sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2604
+        $sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2605
+        $sql.= " WHERE rowid =". $this->id;
2606
+
2607
+        dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2608
+        if ($this->db->query($sql))
2609
+        {
2610
+            if ($suffix == '_public') $this->note_public = $note;
2611
+            else if ($suffix == '_private') $this->note_private = $note;
2612
+            else
2613
+            {
2614
+                $this->note = $note;      // deprecated
2615
+                $this->note_private = $note;
2616
+            }
2617
+            return 1;
2618
+        }
2619
+        else
2620
+        {
2621
+            $this->error=$this->db->lasterror();
2622
+            return -1;
2623
+        }
2624
+    }
2625
+
2626
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2627
+    /**
2628
+     * 	Update public note (kept for backward compatibility)
2629
+     *
2630
+     * @param      string		$note		New value for note
2631
+     * @return     int      		   		<0 if KO, >0 if OK
2632
+     * @deprecated
2633
+     * @see update_note()
2634
+     */
2635
+    function update_note_public($note)
2636
+    {
2637
+        // phpcs:enable
2638
+        return $this->update_note($note,'_public');
2639
+    }
2640
+
2641
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2642
+    /**
2643
+     *	Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
2644
+     *  Must be called at end of methods addline or updateline.
2645
+     *
2646
+     *	@param	int		$exclspec          	>0 = Exclude special product (product_type=9)
2647
+     *  @param  string	$roundingadjust    	'none'=Do nothing, 'auto'=Use default method (MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND if defined, or '0'), '0'=Force mode total of rounding, '1'=Force mode rounding of total
2648
+     *  @param	int		$nodatabaseupdate	1=Do not update database. Update only properties of object.
2649
+     *  @param	Societe	$seller				If roundingadjust is '0' or '1' or maybe 'auto', it means we recalculate total for lines before calculating total for object and for this, we need seller object.
2650
+     *	@return	int    			           	<0 if KO, >0 if OK
2651
+     */
2652
+    function update_price($exclspec=0,$roundingadjust='none',$nodatabaseupdate=0,$seller=null)
2653
+    {
2654
+        // phpcs:enable
2655
+        global $conf, $hookmanager, $action;
2656
+
2657
+        // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
2658
+        $MODULE = "";
2659
+        if ($this->element == 'propal')
2660
+            $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
2661
+        elseif ($this->element == 'order')
2662
+            $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
2663
+        elseif ($this->element == 'facture')
2664
+            $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
2665
+        elseif ($this->element == 'facture_fourn')
2666
+            $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
2667
+        elseif ($this->element == 'order_supplier')
2668
+            $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
2669
+        elseif ($this->element == 'supplier_proposal')
2670
+            $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
2671
+
2672
+        if (! empty($MODULE)) {
2673
+            if (! empty($conf->global->$MODULE)) {
2674
+                $modsactivated = explode(',', $conf->global->$MODULE);
2675
+                foreach ($modsactivated as $mod) {
2676
+                    if ($conf->$mod->enabled)
2677
+                        return 1; // update was disabled by specific setup
2678
+                }
2679
+            }
2680
+        }
2681
+
2682
+        include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2683
+
2684
+        if ($roundingadjust == '-1') $roundingadjust='auto';	// For backward compatibility
2685
+
2686
+        $forcedroundingmode=$roundingadjust;
2687
+        if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2688
+        elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2689
+
2690
+        $error=0;
2691
+
2692
+        $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2693
+
2694
+        // Define constants to find lines to sum
2695
+        $fieldtva='total_tva';
2696
+        $fieldlocaltax1='total_localtax1';
2697
+        $fieldlocaltax2='total_localtax2';
2698
+        $fieldup='subprice';
2699
+        if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2700
+        {
2701
+            $fieldtva='tva';
2702
+            $fieldup='pu_ht';
2703
+        }
2704
+        if ($this->element == 'expensereport')
2705
+        {
2706
+            $fieldup='value_unit';
2707
+        }
2708
+
2709
+        $sql = 'SELECT rowid, qty, '.$fieldup.' as up, remise_percent, total_ht, '.$fieldtva.' as total_tva, total_ttc, '.$fieldlocaltax1.' as total_localtax1, '.$fieldlocaltax2.' as total_localtax2,';
2710
+        $sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2711
+            if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2712
+            $sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2713
+        $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2714
+        $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2715
+        if ($exclspec)
2716
+        {
2717
+            $product_field='product_type';
2718
+            if ($this->table_element_line == 'contratdet') $product_field='';    // contratdet table has no product_type field
2719
+            if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2720
+        }
2721
+        $sql.= ' ORDER by rowid';	// We want to be sure to always use same order of line to not change lines differently when option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND is used
2722
+
2723
+        dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2724
+        $resql = $this->db->query($sql);
2725
+        if ($resql)
2726
+        {
2727
+            $this->total_ht  = 0;
2728
+            $this->total_tva = 0;
2729
+            $this->total_localtax1 = 0;
2730
+            $this->total_localtax2 = 0;
2731
+            $this->total_ttc = 0;
2732
+            $total_ht_by_vats  = array();
2733
+            $total_tva_by_vats = array();
2734
+            $total_ttc_by_vats = array();
2735
+            $this->multicurrency_total_ht	= 0;
2736
+            $this->multicurrency_total_tva	= 0;
2737
+            $this->multicurrency_total_ttc	= 0;
2738
+
2739
+            $num = $this->db->num_rows($resql);
2740
+            $i = 0;
2741
+            while ($i < $num)
2742
+            {
2743
+                $obj = $this->db->fetch_object($resql);
2744
+
2745
+                // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2746
+                $parameters=array('fk_element' => $obj->rowid);
2747
+                $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2748
+
2749
+                if (empty($reshook) && $forcedroundingmode == '0')	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
2750
+                {
2751
+                    $localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2752
+                    $tmpcal=calcul_price_total($obj->qty, $obj->up, $obj->remise_percent, $obj->vatrate, $obj->localtax1_tx, $obj->localtax2_tx, 0, 'HT', $obj->info_bits, $obj->product_type, $seller, $localtax_array, (isset($obj->situation_percent) ? $obj->situation_percent : 100), $multicurrency_tx);
2753
+                    $diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2754
+                    if ($diff)
2755
+                    {
2756
+                        $sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2757
+                        dol_syslog('We found unconsistent data into detailed line (difference of '.$diff.') for line rowid = '.$obj->rowid." (total vat of line calculated=".$tmpcal[1].", database=".$obj->total_tva."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
2758
+                                $resqlfix=$this->db->query($sqlfix);
2759
+                                if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2760
+                                $obj->total_tva = $tmpcal[1];
2761
+                                $obj->total_ttc = $tmpcal[2];
2762
+                        //
2763
+                    }
2764
+                }
2765
+
2766
+                $this->total_ht        += $obj->total_ht;		// The field visible at end of line detail
2767
+                $this->total_tva       += $obj->total_tva;
2768
+                $this->total_localtax1 += $obj->total_localtax1;
2769
+                $this->total_localtax2 += $obj->total_localtax2;
2770
+                $this->total_ttc       += $obj->total_ttc;
2771
+                $this->multicurrency_total_ht        += $obj->multicurrency_total_ht;		// The field visible at end of line detail
2772
+                $this->multicurrency_total_tva       += $obj->multicurrency_total_tva;
2773
+                $this->multicurrency_total_ttc       += $obj->multicurrency_total_ttc;
2774
+
2775
+                if (! isset($total_ht_by_vats[$obj->vatrate]))  $total_ht_by_vats[$obj->vatrate]=0;
2776
+                if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2777
+                if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2778
+                $total_ht_by_vats[$obj->vatrate]  += $obj->total_ht;
2779
+                $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2780
+                $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2781
+
2782
+                if ($forcedroundingmode == '1')	// Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
2783
+                {
2784
+                    $tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
2785
+                    $diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
2786
+                    //print 'Line '.$i.' rowid='.$obj->rowid.' vat_rate='.$obj->vatrate.' total_ht='.$obj->total_ht.' total_tva='.$obj->total_tva.' total_ttc='.$obj->total_ttc.' total_ht_by_vats='.$total_ht_by_vats[$obj->vatrate].' total_tva_by_vats='.$total_tva_by_vats[$obj->vatrate].' (new calculation = '.$tmpvat.') total_ttc_by_vats='.$total_ttc_by_vats[$obj->vatrate].($diff?" => DIFF":"")."<br>\n";
2787
+                    if ($diff)
2788
+                    {
2789
+                        if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
2790
+                        $sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".($obj->total_tva - $diff).", total_ttc = ".($obj->total_ttc - $diff)." WHERE rowid = ".$obj->rowid;
2791
+                        dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
2792
+                                $resqlfix=$this->db->query($sqlfix);
2793
+                                if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2794
+                                $this->total_tva -= $diff;
2795
+                                $this->total_ttc -= $diff;
2796
+                                $total_tva_by_vats[$obj->vatrate] -= $diff;
2797
+                                $total_ttc_by_vats[$obj->vatrate] -= $diff;
2798
+                    }
2799
+                }
2800
+
2801
+                $i++;
2802
+            }
2803
+
2804
+            // Add revenue stamp to total
2805
+            $this->total_ttc       			+= isset($this->revenuestamp)?$this->revenuestamp:0;
2806
+            $this->multicurrency_total_ttc  += isset($this->revenuestamp)?($this->revenuestamp * $multicurrency_tx):0;
2807
+
2808
+            // Situations totals
2809
+            if ($this->situation_cycle_ref && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE )
2810
+            {
2811
+                $prev_sits = $this->get_prev_sits();
2812
+
2813
+                foreach ($prev_sits as $sit) {				// $sit is an object Facture loaded with a fetch.
2814
+                    $this->total_ht -= $sit->total_ht;
2815
+                    $this->total_tva -= $sit->total_tva;
2816
+                    $this->total_localtax1 -= $sit->total_localtax1;
2817
+                    $this->total_localtax2 -= $sit->total_localtax2;
2818
+                    $this->total_ttc -= $sit->total_ttc;
2819
+                    $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
2820
+                    $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
2821
+                    $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
2822
+                }
2823
+            }
2824
+
2825
+            $this->db->free($resql);
2826
+
2827
+            // Now update global field total_ht, total_ttc and tva
2828
+            $fieldht='total_ht';
2829
+            $fieldtva='tva';
2830
+            $fieldlocaltax1='localtax1';
2831
+            $fieldlocaltax2='localtax2';
2832
+            $fieldttc='total_ttc';
2833
+            // Specific code for backward compatibility with old field names
2834
+            if ($this->element == 'facture' || $this->element == 'facturerec')             $fieldht='total';
2835
+            if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
2836
+            if ($this->element == 'propal')                                                $fieldttc='total';
2837
+            if ($this->element == 'expensereport')                                         $fieldtva='total_tva';
2838
+            if ($this->element == 'supplier_proposal')                                     $fieldttc='total';
2839
+
2840
+            if (empty($nodatabaseupdate))
2841
+            {
2842
+                $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2843
+                $sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
2844
+                $sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
2845
+                $sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
2846
+                $sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
2847
+                $sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
2848
+                        $sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
2849
+                        $sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
2850
+                        $sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
2851
+                $sql .= ' WHERE rowid = '.$this->id;
2852
+
2853
+
2854
+                dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2855
+                $resql=$this->db->query($sql);
2856
+                if (! $resql)
2857
+                {
2858
+                    $error++;
2859
+                    $this->error=$this->db->lasterror();
2860
+                    $this->errors[]=$this->db->lasterror();
2861
+                }
2862
+            }
2863
+
2864
+            if (! $error)
2865
+            {
2866
+                return 1;
2867
+            }
2868
+            else
2869
+            {
2870
+                return -1;
2871
+            }
2872
+        }
2873
+        else
2874
+        {
2875
+            dol_print_error($this->db,'Bad request in update_price');
2876
+            return -1;
2877
+        }
2878
+    }
2879
+
2880
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2881
+    /**
2882
+     *	Add objects linked in llx_element_element.
2883
+     *
2884
+     *	@param		string	$origin		Linked element type
2885
+     *	@param		int		$origin_id	Linked element id
2886
+     *	@return		int					<=0 if KO, >0 if OK
2887
+     *	@see		fetchObjectLinked, updateObjectLinked, deleteObjectLinked
2888
+     */
2889
+    function add_object_linked($origin=null, $origin_id=null)
2890
+    {
2891
+        // phpcs:enable
2892
+        $origin = (! empty($origin) ? $origin : $this->origin);
2893
+        $origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
2894
+
2895
+        // Special case
2896
+        if ($origin == 'order') $origin='commande';
2897
+        if ($origin == 'invoice') $origin='facture';
2898
+        if ($origin == 'invoice_template') $origin='facturerec';
2899
+        if ($origin == 'supplierorder') $origin='order_supplier';
2900
+        $this->db->begin();
2901
+
2902
+        $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
2903
+        $sql.= "fk_source";
2904
+        $sql.= ", sourcetype";
2905
+        $sql.= ", fk_target";
2906
+        $sql.= ", targettype";
2907
+        $sql.= ") VALUES (";
2908
+        $sql.= $origin_id;
2909
+        $sql.= ", '".$this->db->escape($origin)."'";
2910
+        $sql.= ", ".$this->id;
2911
+        $sql.= ", '".$this->db->escape($this->element)."'";
2912
+        $sql.= ")";
2913
+
2914
+        dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
2915
+        if ($this->db->query($sql))
2916
+            {
2917
+                $this->db->commit();
2918
+                return 1;
2919
+            }
2920
+            else
2921
+            {
2922
+                $this->error=$this->db->lasterror();
2923
+                $this->db->rollback();
2924
+                return 0;
2925
+            }
2926
+    }
2927
+
2928
+    /**
2929
+     *	Fetch array of objects linked to current object (object of enabled modules only). Links are loaded into
2930
+     *		this->linkedObjectsIds array and
2931
+     *		this->linkedObjects array if $loadalsoobjects = 1
2932
+     *  Possible usage for parameters:
2933
+     *  - all parameters empty -> we look all link to current object (current object can be source or target)
2934
+     *  - source id+type -> will get target list linked to source
2935
+     *  - target id+type -> will get source list linked to target
2936
+     *  - source id+type + target type -> will get target list of the type
2937
+     *  - target id+type + target source -> will get source list of the type
2938
+     *
2939
+     *	@param	int		$sourceid			Object source id (if not defined, id of object)
2940
+     *	@param  string	$sourcetype			Object source type (if not defined, element name of object)
2941
+     *	@param  int		$targetid			Object target id (if not defined, id of object)
2942
+     *	@param  string	$targettype			Object target type (if not defined, elemennt name of object)
2943
+     *	@param  string	$clause				'OR' or 'AND' clause used when both source id and target id are provided
2944
+     *  @param  int		$alsosametype		0=Return only links to object that differs from source type. 1=Include also link to objects of same type.
2945
+     *  @param  string	$orderby			SQL 'ORDER BY' clause
2946
+     *  @param	int		$loadalsoobjects	Load also array this->linkedObjects (Use 0 to increase performances)
2947
+     *	@return int							<0 if KO, >0 if OK
2948
+     *  @see	add_object_linked, updateObjectLinked, deleteObjectLinked
2949
+     */
2950
+    function fetchObjectLinked($sourceid=null,$sourcetype='',$targetid=null,$targettype='',$clause='OR',$alsosametype=1,$orderby='sourcetype',$loadalsoobjects=1)
2951
+    {
2952
+        global $conf;
2953
+
2954
+        $this->linkedObjectsIds=array();
2955
+        $this->linkedObjects=array();
2956
+
2957
+        $justsource=false;
2958
+        $justtarget=false;
2959
+        $withtargettype=false;
2960
+        $withsourcetype=false;
2961
+
2962
+        if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
2963
+        {
2964
+            $justsource=true;  // the source (id and type) is a search criteria
2965
+            if (! empty($targettype)) $withtargettype=true;
2966
+        }
2967
+        if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
2968
+        {
2969
+            $justtarget=true;  // the target (id and type) is a search criteria
2970
+            if (! empty($sourcetype)) $withsourcetype=true;
2971
+        }
2972
+
2973
+        $sourceid = (! empty($sourceid) ? $sourceid : $this->id);
2974
+        $targetid = (! empty($targetid) ? $targetid : $this->id);
2975
+        $sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
2976
+        $targettype = (! empty($targettype) ? $targettype : $this->element);
2977
+
2978
+        /*if (empty($sourceid) && empty($targetid))
2979
+		 {
2980
+		 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
2981
+		 return -1;
2982
+		 }*/
2983
+
2984
+        // Links between objects are stored in table element_element
2985
+        $sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
2986
+        $sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
2987
+        $sql.= " WHERE ";
2988
+        if ($justsource || $justtarget)
2989
+        {
2990
+            if ($justsource)
2991
+            {
2992
+                $sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
2993
+                if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
2994
+            }
2995
+            else if ($justtarget)
2996
+            {
2997
+                $sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
2998
+                if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
2999
+            }
3000
+        }
3001
+        else
3002
+        {
3003
+            $sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
3004
+            $sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
3005
+        }
3006
+        $sql .= ' ORDER BY '.$orderby;
3007
+
3008
+        dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3009
+        $resql = $this->db->query($sql);
3010
+        if ($resql)
3011
+        {
3012
+            $num = $this->db->num_rows($resql);
3013
+            $i = 0;
3014
+            while ($i < $num)
3015
+            {
3016
+                $obj = $this->db->fetch_object($resql);
3017
+                if ($justsource || $justtarget)
3018
+                {
3019
+                    if ($justsource)
3020
+                    {
3021
+                        $this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3022
+                    }
3023
+                    else if ($justtarget)
3024
+                    {
3025
+                        $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3026
+                    }
3027
+                }
3028
+                else
3029
+                {
3030
+                    if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
3031
+                    {
3032
+                        $this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3033
+                    }
3034
+                    if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
3035
+                    {
3036
+                        $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3037
+                    }
3038
+                }
3039
+                $i++;
3040
+            }
3041
+
3042
+            if (! empty($this->linkedObjectsIds))
3043
+            {
3044
+                $tmparray = $this->linkedObjectsIds;
3045
+                foreach($tmparray as $objecttype => $objectids)       // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
3046
+                {
3047
+                    // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
3048
+                    $module = $element = $subelement = $objecttype;
3049
+                    if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
3050
+                        && preg_match('/^([^_]+)_([^_]+)/i',$objecttype,$regs))
3051
+                    {
3052
+                        $module = $element = $regs[1];
3053
+                        $subelement = $regs[2];
3054
+                    }
3055
+
3056
+                    $classpath = $element.'/class';
3057
+                    // To work with non standard classpath or module name
3058
+                    if ($objecttype == 'facture')			{
3059
+                        $classpath = 'compta/facture/class';
3060
+                    }
3061
+                    else if ($objecttype == 'facturerec')			{
3062
+                        $classpath = 'compta/facture/class'; $module = 'facture';
3063
+                    }
3064
+                    else if ($objecttype == 'propal')			{
3065
+                        $classpath = 'comm/propal/class';
3066
+                    }
3067
+                    else if ($objecttype == 'supplier_proposal')			{
3068
+                        $classpath = 'supplier_proposal/class';
3069
+                    }
3070
+                    else if ($objecttype == 'shipping')			{
3071
+                        $classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
3072
+                    }
3073
+                    else if ($objecttype == 'delivery')			{
3074
+                        $classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
3075
+                    }
3076
+                    else if ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier')	{
3077
+                        $classpath = 'fourn/class'; $module = 'fournisseur';
3078
+                    }
3079
+                    else if ($objecttype == 'fichinter')			{
3080
+                        $classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
3081
+                    }
3082
+                    else if ($objecttype == 'subscription')			{
3083
+                        $classpath = 'adherents/class'; $module = 'adherent';
3084
+                    }
3085
+
3086
+                    // Set classfile
3087
+                    $classfile = strtolower($subelement); $classname = ucfirst($subelement);
3088
+
3089
+                    if ($objecttype == 'order') {
3090
+                        $classfile = 'commande'; $classname = 'Commande';
3091
+                    }
3092
+                    else if ($objecttype == 'invoice_supplier') {
3093
+                        $classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
3094
+                    }
3095
+                    else if ($objecttype == 'order_supplier')   {
3096
+                        $classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
3097
+                    }
3098
+                    else if ($objecttype == 'supplier_proposal')   {
3099
+                        $classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
3100
+                    }
3101
+                    else if ($objecttype == 'facturerec')   {
3102
+                        $classfile = 'facture-rec'; $classname = 'FactureRec';
3103
+                    }
3104
+                    else if ($objecttype == 'subscription')   {
3105
+                        $classfile = 'subscription'; $classname = 'Subscription';
3106
+                    }
3107
+
3108
+                    // Here $module, $classfile and $classname are set
3109
+                    if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
3110
+                    {
3111
+                        if ($loadalsoobjects)
3112
+                        {
3113
+                            dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
3114
+                            //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
3115
+                            if (class_exists($classname))
3116
+                            {
3117
+                                foreach($objectids as $i => $objectid)	// $i is rowid into llx_element_element
3118
+                                {
3119
+                                    $object = new $classname($this->db);
3120
+                                    $ret = $object->fetch($objectid);
3121
+                                    if ($ret >= 0)
3122
+                                    {
3123
+                                        $this->linkedObjects[$objecttype][$i] = $object;
3124
+                                    }
3125
+                                }
3126
+                            }
3127
+                        }
3128
+                    }
3129
+                    else
3130
+                    {
3131
+                        unset($this->linkedObjectsIds[$objecttype]);
3132
+                    }
3133
+                }
3134
+            }
3135
+            return 1;
3136
+        }
3137
+        else
3138
+        {
3139
+            dol_print_error($this->db);
3140
+            return -1;
3141
+        }
3142
+    }
3143
+
3144
+    /**
3145
+     *	Update object linked of a current object
3146
+     *
3147
+     *	@param	int		$sourceid		Object source id
3148
+     *	@param  string	$sourcetype		Object source type
3149
+     *	@param  int		$targetid		Object target id
3150
+     *	@param  string	$targettype		Object target type
3151
+     *	@return							int	>0 if OK, <0 if KO
3152
+     *	@see	add_object_linked, fetObjectLinked, deleteObjectLinked
3153
+     */
3154
+    function updateObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='')
3155
+    {
3156
+        $updatesource=false;
3157
+        $updatetarget=false;
3158
+
3159
+        if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
3160
+        else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
3161
+
3162
+        $sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
3163
+        if ($updatesource)
3164
+        {
3165
+            $sql.= "fk_source = ".$sourceid;
3166
+            $sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
3167
+            $sql.= " WHERE fk_target = ".$this->id;
3168
+            $sql.= " AND targettype = '".$this->db->escape($this->element)."'";
3169
+        }
3170
+        else if ($updatetarget)
3171
+        {
3172
+            $sql.= "fk_target = ".$targetid;
3173
+            $sql.= ", targettype = '".$this->db->escape($targettype)."'";
3174
+            $sql.= " WHERE fk_source = ".$this->id;
3175
+            $sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
3176
+        }
3177
+
3178
+        dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
3179
+        if ($this->db->query($sql))
3180
+        {
3181
+            return 1;
3182
+        }
3183
+        else
3184
+        {
3185
+            $this->error=$this->db->lasterror();
3186
+            return -1;
3187
+        }
3188
+    }
3189
+
3190
+    /**
3191
+     *	Delete all links between an object $this
3192
+     *
3193
+     *	@param	int		$sourceid		Object source id
3194
+     *	@param  string	$sourcetype		Object source type
3195
+     *	@param  int		$targetid		Object target id
3196
+     *	@param  string	$targettype		Object target type
3197
+     *  @param	int		$rowid			Row id of line to delete. If defined, other parameters are not used.
3198
+     *	@return     					int	>0 if OK, <0 if KO
3199
+     *	@see	add_object_linked, updateObjectLinked, fetchObjectLinked
3200
+     */
3201
+    function deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
3202
+    {
3203
+        $deletesource=false;
3204
+        $deletetarget=false;
3205
+
3206
+        if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
3207
+        else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
3208
+
3209
+        $sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3210
+        $sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3211
+        $targetid = (! empty($targetid) ? $targetid : $this->id);
3212
+        $targettype = (! empty($targettype) ? $targettype : $this->element);
3213
+
3214
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
3215
+        $sql.= " WHERE";
3216
+        if ($rowid > 0)
3217
+        {
3218
+            $sql.=" rowid = ".$rowid;
3219
+        }
3220
+        else
3221
+        {
3222
+            if ($deletesource)
3223
+            {
3224
+                $sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3225
+                $sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
3226
+            }
3227
+            else if ($deletetarget)
3228
+            {
3229
+                $sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3230
+                $sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
3231
+            }
3232
+            else
3233
+            {
3234
+                $sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
3235
+                $sql.= " OR";
3236
+                $sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
3237
+            }
3238
+        }
3239
+
3240
+        dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
3241
+        if ($this->db->query($sql))
3242
+        {
3243
+            return 1;
3244
+        }
3245
+        else
3246
+        {
3247
+            $this->error=$this->db->lasterror();
3248
+            $this->errors[]=$this->error;
3249
+            return -1;
3250
+        }
3251
+    }
3252
+
3253
+    /**
3254
+     *      Set status of an object
3255
+     *
3256
+     *      @param	int		$status			Status to set
3257
+     *      @param	int		$elementId		Id of element to force (use this->id by default)
3258
+     *      @param	string	$elementType	Type of element to force (use this->table_element by default)
3259
+     *      @param	string	$trigkey		Trigger key to use for trigger
3260
+     *      @return int						<0 if KO, >0 if OK
3261
+     */
3262
+    function setStatut($status, $elementId=null, $elementType='', $trigkey='')
3263
+    {
3264
+        global $user,$langs,$conf;
3265
+
3266
+        $savElementId=$elementId;  // To be used later to know if we were using the method using the id of this or not.
3267
+
3268
+        $elementId = (!empty($elementId)?$elementId:$this->id);
3269
+        $elementTable = (!empty($elementType)?$elementType:$this->table_element);
3270
+
3271
+        $this->db->begin();
3272
+
3273
+        $fieldstatus="fk_statut";
3274
+        if ($elementTable == 'facture_rec') $fieldstatus="suspended";
3275
+        if ($elementTable == 'mailing') $fieldstatus="statut";
3276
+        if ($elementTable == 'cronjob') $fieldstatus="status";
3277
+        if ($elementTable == 'user') $fieldstatus="statut";
3278
+        if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
3279
+        if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
3280
+
3281
+        $sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
3282
+        $sql.= " SET ".$fieldstatus." = ".$status;
3283
+        // If status = 1 = validated, update also fk_user_valid
3284
+        if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
3285
+        $sql.= " WHERE rowid=".$elementId;
3286
+
3287
+        dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
3288
+        if ($this->db->query($sql))
3289
+        {
3290
+            $error = 0;
3291
+
3292
+            // Try autoset of trigkey
3293
+            if (empty($trigkey))
3294
+            {
3295
+                if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN';   // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
3296
+                if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
3297
+                if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE';  // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
3298
+                if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
3299
+                if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
3300
+                if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
3301
+            }
3302
+
3303
+            if ($trigkey)
3304
+            {
3305
+                // Appel des triggers
3306
+                include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
3307
+                $interface=new Interfaces($this->db);
3308
+                $result=$interface->run_triggers($trigkey,$this,$user,$langs,$conf);
3309
+                if ($result < 0) {
3310
+                    $error++; $this->errors=$interface->errors;
3311
+                }
3312
+                // Fin appel triggers
3313
+            }
3314
+
3315
+            if (! $error)
3316
+            {
3317
+                $this->db->commit();
3318
+
3319
+                if (empty($savElementId))    // If the element we update was $this (so $elementId is null)
3320
+                {
3321
+                    $this->statut = $status;
3322
+                    $this->status = $status;
3323
+                }
3324
+
3325
+                return 1;
3326
+            }
3327
+            else
3328
+            {
3329
+                $this->db->rollback();
3330
+                dol_syslog(get_class($this)."::setStatus ".$this->error,LOG_ERR);
3331
+                return -1;
3332
+            }
3333
+        }
3334
+        else
3335
+        {
3336
+            $this->error=$this->db->lasterror();
3337
+            $this->db->rollback();
3338
+            return -1;
3339
+        }
3340
+    }
3341
+
3342
+
3343
+    /**
3344
+     *  Load type of canvas of an object if it exists
3345
+     *
3346
+     *  @param      int		$id     Record id
3347
+     *  @param      string	$ref    Record ref
3348
+     *  @return		int				<0 if KO, 0 if nothing done, >0 if OK
3349
+     */
3350
+    function getCanvas($id=0,$ref='')
3351
+    {
3352
+        global $conf;
3353
+
3354
+        if (empty($id) && empty($ref)) return 0;
3355
+        if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0;    // To increase speed. Not enabled by default.
3356
+
3357
+        // Clean parameters
3358
+        $ref = trim($ref);
3359
+
3360
+        $sql = "SELECT rowid, canvas";
3361
+        $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
3362
+        $sql.= " WHERE entity IN (".getEntity($this->element).")";
3363
+        if (! empty($id))  $sql.= " AND rowid = ".$id;
3364
+        if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
3365
+
3366
+        $resql = $this->db->query($sql);
3367
+        if ($resql)
3368
+        {
3369
+            $obj = $this->db->fetch_object($resql);
3370
+            if ($obj)
3371
+            {
3372
+                $this->canvas   = $obj->canvas;
3373
+                return 1;
3374
+            }
3375
+            else return 0;
3376
+        }
3377
+        else
3378
+        {
3379
+            dol_print_error($this->db);
3380
+            return -1;
3381
+        }
3382
+    }
3383
+
3384
+
3385
+    /**
3386
+     * 	Get special code of a line
3387
+     *
3388
+     * 	@param	int		$lineid		Id of line
3389
+     * 	@return	int					Special code
3390
+     */
3391
+    function getSpecialCode($lineid)
3392
+    {
3393
+        $sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3394
+        $sql.= ' WHERE rowid = '.$lineid;
3395
+        $resql = $this->db->query($sql);
3396
+        if ($resql)
3397
+        {
3398
+            $row = $this->db->fetch_row($resql);
3399
+            return $row[0];
3400
+        }
3401
+    }
3402
+
3403
+    /**
3404
+     *  Function to check if an object is used by others.
3405
+     *  Check is done into this->childtables. There is no check into llx_element_element.
3406
+     *
3407
+     *  @param	int		$id			Force id of object
3408
+     *  @return	int					<0 if KO, 0 if not used, >0 if already used
3409
+     */
3410
+    function isObjectUsed($id=0)
3411
+    {
3412
+        global $langs;
3413
+
3414
+        if (empty($id)) $id=$this->id;
3415
+
3416
+        // Check parameters
3417
+        if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
3418
+        {
3419
+            dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3420
+            return -1;
3421
+        }
3422
+
3423
+        $arraytoscan = $this->childtables;
3424
+        // For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3425
+        $tmparray=array_keys($this->childtables);
3426
+        if (is_numeric($tmparray[0]))
3427
+        {
3428
+            $arraytoscan = array_flip($this->childtables);
3429
+        }
3430
+
3431
+        // Test if child exists
3432
+        $haschild=0;
3433
+        foreach($arraytoscan as $table => $elementname)
3434
+        {
3435
+            //print $id.'-'.$table.'-'.$elementname.'<br>';
3436
+            // Check if third party can be deleted
3437
+            $sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3438
+            $sql.= " WHERE ".$this->fk_element." = ".$id;
3439
+            $resql=$this->db->query($sql);
3440
+            if ($resql)
3441
+            {
3442
+                $obj=$this->db->fetch_object($resql);
3443
+                if ($obj->nb > 0)
3444
+                {
3445
+                    $langs->load("errors");
3446
+                    //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3447
+                    $haschild += $obj->nb;
3448
+                    if (is_numeric($elementname))	// old usage
3449
+                    {
3450
+                        $this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table);
3451
+                    }
3452
+                    else	// new usage: $elementname=Translation key
3453
+                    {
3454
+                        $this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3455
+                    }
3456
+                    break;    // We found at least one, we stop here
3457
+                }
3458
+            }
3459
+            else
3460
+            {
3461
+                $this->errors[]=$this->db->lasterror();
3462
+                return -1;
3463
+            }
3464
+        }
3465
+        if ($haschild > 0)
3466
+        {
3467
+            $this->errors[]="ErrorRecordHasChildren";
3468
+            return $haschild;
3469
+        }
3470
+        else return 0;
3471
+    }
3472
+
3473
+    /**
3474
+     *  Function to say how many lines object contains
3475
+     *
3476
+     *	@param	int		$predefined		-1=All, 0=Count free product/service only, 1=Count predefined product/service only, 2=Count predefined product, 3=Count predefined service
3477
+     *  @return	int						<0 if KO, 0 if no predefined products, nb of lines with predefined products if found
3478
+     */
3479
+    function hasProductsOrServices($predefined=-1)
3480
+    {
3481
+        $nb=0;
3482
+
3483
+        foreach($this->lines as $key => $val)
3484
+        {
3485
+            $qualified=0;
3486
+            if ($predefined == -1) $qualified=1;
3487
+            if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3488
+            if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3489
+            if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3490
+            if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3491
+            if ($qualified) $nb++;
3492
+        }
3493
+        dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3494
+        return $nb;
3495
+    }
3496
+
3497
+    /**
3498
+     * Function that returns the total amount HT of discounts applied for all lines.
3499
+     *
3500
+     * @return 	float
3501
+     */
3502
+    function getTotalDiscount()
3503
+    {
3504
+        $total_discount=0.00;
3505
+
3506
+        $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3507
+        $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3508
+        $sql.= " WHERE ".$this->fk_element." = ".$this->id;
3509
+
3510
+        dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3511
+        $resql = $this->db->query($sql);
3512
+        if ($resql)
3513
+        {
3514
+            $num=$this->db->num_rows($resql);
3515
+            $i=0;
3516
+            while ($i < $num)
3517
+            {
3518
+                $obj = $this->db->fetch_object($resql);
3519
+
3520
+                $pu_ht = $obj->pu_ht;
3521
+                $qty= $obj->qty;
3522
+                $total_ht = $obj->total_ht;
3523
+
3524
+                $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3525
+                $total_discount += $total_discount_line;
3526
+
3527
+                $i++;
3528
+            }
3529
+        }
3530
+
3531
+        //print $total_discount; exit;
3532
+        return price2num($total_discount);
3533
+    }
3534
+
3535
+
3536
+    /**
3537
+     * Return into unit=0, the calculated total of weight and volume of all lines * qty
3538
+     * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
3539
+     *
3540
+     * @return  array                           array('weight'=>...,'volume'=>...)
3541
+     */
3542
+    function getTotalWeightVolume()
3543
+    {
3544
+        $totalWeight = 0;
3545
+        $totalVolume = 0;
3546
+        // defined for shipment only
3547
+        $totalOrdered = '';
3548
+        // defined for shipment only
3549
+        $totalToShip = '';
3550
+
3551
+        foreach ($this->lines as $line)
3552
+        {
3553
+            if (isset($line->qty_asked))
3554
+            {
3555
+                if (empty($totalOrdered)) $totalOrdered=0;  // Avoid warning because $totalOrdered is ''
3556
+                $totalOrdered+=$line->qty_asked;    // defined for shipment only
3557
+            }
3558
+            if (isset($line->qty_shipped))
3559
+            {
3560
+                if (empty($totalToShip)) $totalToShip=0;    // Avoid warning because $totalToShip is ''
3561
+                $totalToShip+=$line->qty_shipped;   // defined for shipment only
3562
+            }else if ($line->element == 'commandefournisseurdispatch' && isset($line->qty))
3563
+            {
3564
+                if (empty($totalToShip)) $totalToShip=0;
3565
+                $totalToShip+=$line->qty;   // defined for reception only
3566
+            }
3567
+
3568
+            // Define qty, weight, volume, weight_units, volume_units
3569
+            if ($this->element == 'shipping') {
3570
+                // for shipments
3571
+                $qty = $line->qty_shipped ? $line->qty_shipped : 0;
3572
+            }
3573
+            else {
3574
+                $qty = $line->qty ? $line->qty : 0;
3575
+            }
3576
+
3577
+            $weight = $line->weight ? $line->weight : 0;
3578
+            ($weight==0 && !empty($line->product->weight))? $weight=$line->product->weight: 0;
3579
+            $volume = $line->volume ? $line->volume : 0;
3580
+            ($volume==0 && !empty($line->product->volume))? $volume=$line->product->volume: 0;
3581
+
3582
+            $weight_units=$line->weight_units;
3583
+            ($weight_units==0 && !empty($line->product->weight_units))? $weight_units=$line->product->weight_units: 0;
3584
+            $volume_units=$line->volume_units;
3585
+            ($volume_units==0 && !empty($line->product->volume_units))? $volume_units=$line->product->volume_units: 0;
3586
+
3587
+            $weightUnit=0;
3588
+            $volumeUnit=0;
3589
+            if (! empty($weight_units)) $weightUnit = $weight_units;
3590
+            if (! empty($volume_units)) $volumeUnit = $volume_units;
3591
+
3592
+            if (empty($totalWeight)) $totalWeight=0;  // Avoid warning because $totalWeight is ''
3593
+            if (empty($totalVolume)) $totalVolume=0;  // Avoid warning because $totalVolume is ''
3594
+
3595
+            //var_dump($line->volume_units);
3596
+            if ($weight_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3597
+            {
3598
+                $trueWeightUnit=pow(10, $weightUnit);
3599
+                $totalWeight += $weight * $qty * $trueWeightUnit;
3600
+            }
3601
+            else {
3602
+        if ($weight_units == 99) {
3603
+            // conversion 1 Pound = 0.45359237 KG
3604
+            $trueWeightUnit = 0.45359237;
3605
+            $totalWeight += $weight * $qty * $trueWeightUnit;
3606
+        } elseif ($weight_units == 98) {
3607
+            // conversion 1 Ounce = 0.0283495 KG
3608
+            $trueWeightUnit = 0.0283495;
3609
+            $totalWeight += $weight * $qty * $trueWeightUnit;
3610
+        }
3611
+        else
3612
+                    $totalWeight += $weight * $qty;   // This may be wrong if we mix different units
3613
+            }
3614
+            if ($volume_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3615
+            {
3616
+                //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3617
+                $trueVolumeUnit=pow(10, $volumeUnit);
3618
+                //print $line->volume;
3619
+                $totalVolume += $volume * $qty * $trueVolumeUnit;
3620
+            }
3621
+            else
3622
+            {
3623
+                $totalVolume += $volume * $qty;   // This may be wrong if we mix different units
3624
+            }
3625
+        }
3626
+
3627
+        return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3628
+    }
3629
+
3630
+
3631
+    /**
3632
+     *	Set extra parameters
3633
+     *
3634
+     *	@return	int      <0 if KO, >0 if OK
3635
+     */
3636
+    function setExtraParameters()
3637
+    {
3638
+        $this->db->begin();
3639
+
3640
+        $extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3641
+
3642
+        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3643
+        $sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3644
+        $sql.= " WHERE rowid = ".$this->id;
3645
+
3646
+        dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3647
+        $resql = $this->db->query($sql);
3648
+        if (! $resql)
3649
+        {
3650
+            $this->error=$this->db->lasterror();
3651
+            $this->db->rollback();
3652
+            return -1;
3653
+        }
3654
+        else
3655
+        {
3656
+            $this->db->commit();
3657
+            return 1;
3658
+        }
3659
+    }
3660
+
3661
+
3662
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3663
+    /**
3664
+     *    Return incoterms informations
3665
+     *    TODO Use a cache for label get
3666
+     *
3667
+     *    @return	string	incoterms info
3668
+     */
3669
+    function display_incoterms()
3670
+    {
3671
+        // phpcs:enable
3672
+        $out = '';
3673
+        $this->libelle_incoterms = '';
3674
+        if (!empty($this->fk_incoterms))
3675
+        {
3676
+            $sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3677
+            $result = $this->db->query($sql);
3678
+            if ($result)
3679
+            {
3680
+                $res = $this->db->fetch_object($result);
3681
+                $out .= $res->code;
3682
+            }
3683
+        }
3684
+
3685
+        $out .= (($res->code && $this->location_incoterms)?' - ':'').$this->location_incoterms;
3686
+
3687
+        return $out;
3688
+    }
3689
+
3690
+    /**
3691
+     *    Return incoterms informations for pdf display
3692
+     *
3693
+     *    @return	string		incoterms info
3694
+     */
3695
+    function getIncotermsForPDF()
3696
+    {
3697
+        $sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3698
+        $resql = $this->db->query($sql);
3699
+        if ($resql)
3700
+        {
3701
+            $num = $this->db->num_rows($resql);
3702
+            if ($num > 0)
3703
+            {
3704
+                $res = $this->db->fetch_object($resql);
3705
+                return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3706
+            }
3707
+            else
3708
+            {
3709
+                return '';
3710
+            }
3711
+        }
3712
+        else
3713
+        {
3714
+            $this->errors[] = $this->db->lasterror();
3715
+            return false;
3716
+        }
3717
+    }
3718
+
3719
+    /**
3720
+     *    Define incoterms values of current object
3721
+     *
3722
+     *    @param	int		$id_incoterm     Id of incoterm to set or '' to remove
3723
+     * 	  @param 	string  $location		 location of incoterm
3724
+     *    @return	int     		<0 if KO, >0 if OK
3725
+     */
3726
+    function setIncoterms($id_incoterm, $location)
3727
+    {
3728
+        if ($this->id && $this->table_element)
3729
+        {
3730
+            $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3731
+            $sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3732
+            $sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3733
+            $sql.= " WHERE rowid = " . $this->id;
3734
+            dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3735
+            $resql=$this->db->query($sql);
3736
+            if ($resql)
3737
+            {
3738
+                $this->fk_incoterms = $id_incoterm;
3739
+                $this->location_incoterms = $location;
3740
+
3741
+                $sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3742
+                $res = $this->db->query($sql);
3743
+                if ($res)
3744
+                {
3745
+                    $obj = $this->db->fetch_object($res);
3746
+                    $this->libelle_incoterms = $obj->libelle;
3747
+                }
3748
+                return 1;
3749
+            }
3750
+            else
3751
+            {
3752
+                $this->errors[] = $this->db->lasterror();
3753
+                return -1;
3754
+            }
3755
+        }
3756
+        else return -1;
3757
+    }
3758
+
3759
+
3760
+    // --------------------
3761
+    // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3762
+    // --------------------
3763
+
3764
+    /* This is to show add lines */
3765
+
3766
+    /**
3767
+     *	Show add free and predefined products/services form
3768
+     *
3769
+     *  @param	int		        $dateSelector       1=Show also date range input fields
3770
+     *  @param	Societe			$seller				Object thirdparty who sell
3771
+     *  @param	Societe			$buyer				Object thirdparty who buy
3772
+     *	@return	void
3773
+     */
3774
+    function formAddObjectLine($dateSelector, $seller, $buyer)
3775
+    {
3776
+        global $conf,$user,$langs,$object,$hookmanager;
3777
+        global $form,$bcnd,$var;
3778
+
3779
+        // Line extrafield
3780
+        require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3781
+        $extrafieldsline = new ExtraFields($this->db);
3782
+        $extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3783
+
3784
+        // Output template part (modules that overwrite templates must declare this into descriptor)
3785
+        // Use global variables + $dateSelector + $seller and $buyer
3786
+        $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3787
+        foreach($dirtpls as $reldir)
3788
+        {
3789
+            $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
3790
+            if (empty($conf->file->strict_mode)) {
3791
+                $res=@include $tpl;
3792
+            } else {
3793
+                $res=include $tpl; // for debug
3794
+            }
3795
+            if ($res) break;
3796
+        }
3797
+    }
3798
+
3799
+
3800
+
3801
+    /* This is to show array of line of details */
3802
+
3803
+
3804
+    /**
3805
+     *	Return HTML table for object lines
3806
+     *	TODO Move this into an output class file (htmlline.class.php)
3807
+     *	If lines are into a template, title must also be into a template
3808
+     *	But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
3809
+     *
3810
+     *	@param	string		$action				Action code
3811
+     *	@param  string		$seller            	Object of seller third party
3812
+     *	@param  string  	$buyer             	Object of buyer third party
3813
+     *	@param	int			$selected		   	Object line selected
3814
+     *	@param  int	    	$dateSelector      	1=Show also date range input fields
3815
+     *	@return	void
3816
+     */
3817
+    function printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0)
3818
+    {
3819
+        global $conf, $hookmanager, $langs, $user;
3820
+        // TODO We should not use global var for this !
3821
+        global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
3822
+
3823
+        // Define usemargins
3824
+        $usemargins=0;
3825
+        if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element,array('facture','propal','commande'))) $usemargins=1;
3826
+
3827
+        $num = count($this->lines);
3828
+
3829
+        // Line extrafield
3830
+        require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3831
+        $extrafieldsline = new ExtraFields($this->db);
3832
+        $extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3833
+
3834
+        $parameters = array('num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3835
+        $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3836
+        if (empty($reshook))
3837
+        {
3838
+            // Title line
3839
+            print "<thead>\n";
3840
+
3841
+            print '<tr class="liste_titre nodrag nodrop">';
3842
+
3843
+            // Adds a line numbering column
3844
+            if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) print '<td class="linecolnum" align="center" width="5">&nbsp;</td>';
3845
+
3846
+            // Description
3847
+            print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
3848
+
3849
+            if ($this->element == 'supplier_proposal' || $this->element == 'order_supplier' || $this->element == 'invoice_supplier')
3850
+            {
3851
+                print '<td class="linerefsupplier"><span id="title_fourn_ref">'.$langs->trans("SupplierRef").'</span></td>';
3852
+            }
3853
+
3854
+            // VAT
3855
+            print '<td class="linecolvat" align="right" width="80">'.$langs->trans('VAT').'</td>';
3856
+
3857
+            // Price HT
3858
+            print '<td class="linecoluht" align="right" width="80">'.$langs->trans('PriceUHT').'</td>';
3859
+
3860
+            // Multicurrency
3861
+            if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoluht_currency" align="right" width="80">'.$langs->trans('PriceUHTCurrency', $this->multicurrency_code).'</td>';
3862
+
3863
+            if ($inputalsopricewithtax) print '<td align="right" width="80">'.$langs->trans('PriceUTTC').'</td>';
3864
+
3865
+            // Qty
3866
+            print '<td class="linecolqty" align="right">'.$langs->trans('Qty').'</td>';
3867
+
3868
+            if($conf->global->PRODUCT_USE_UNITS)
3869
+            {
3870
+                print '<td class="linecoluseunit" align="left">'.$langs->trans('Unit').'</td>';
3871
+            }
3872
+
3873
+            // Reduction short
3874
+            print '<td class="linecoldiscount" align="right">'.$langs->trans('ReductionShort').'</td>';
3875
+
3876
+            if ($this->situation_cycle_ref) {
3877
+                print '<td class="linecolcycleref" align="right">' . $langs->trans('Progress') . '</td>';
3878
+            }
3879
+
3880
+            if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id))
3881
+            {
3882
+                if (!empty($user->rights->margins->creer))
3883
+                {
3884
+                    if ($conf->global->MARGIN_TYPE == "1")
3885
+                        print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('BuyingPrice').'</td>';
3886
+                    else
3887
+                        print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('CostPrice').'</td>';
3888
+                }
3889
+
3890
+                if (! empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous)
3891
+                    print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarginRate').'</td>';
3892
+                if (! empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous)
3893
+                    print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarkRate').'</td>';
3894
+            }
3895
+
3896
+            // Total HT
3897
+            print '<td class="linecolht" align="right">'.$langs->trans('TotalHTShort').'</td>';
3898
+
3899
+            // Multicurrency
3900
+            if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoltotalht_currency" align="right">'.$langs->trans('TotalHTShortCurrency', $this->multicurrency_code).'</td>';
3901
+
3902
+            if ($outputalsopricetotalwithtax) print '<td align="right" width="80">'.$langs->trans('TotalTTCShort').'</td>';
3903
+
3904
+            print '<td class="linecoledit"></td>';  // No width to allow autodim
3905
+
3906
+            print '<td class="linecoldelete" width="10"></td>';
3907
+
3908
+            print '<td class="linecolmove" width="10"></td>';
3909
+
3910
+            if($action == 'selectlines')
3911
+            {
3912
+                print '<td class="linecolcheckall" align="center">';
3913
+                print '<input type="checkbox" class="linecheckboxtoggle" />';
3914
+                print '<script type="text/javascript">$(document).ready(function() {$(".linecheckboxtoggle").click(function() {var checkBoxes = $(".linecheckbox");checkBoxes.prop("checked", this.checked);})});</script>';
3915
+                print '</td>';
3916
+            }
3917
+
3918
+            print "</tr>\n";
3919
+            print "</thead>\n";
3920
+        }
3921
+
3922
+        $var = true;
3923
+        $i	 = 0;
3924
+
3925
+        print "<tbody>\n";
3926
+        foreach ($this->lines as $line)
3927
+        {
3928
+            //Line extrafield
3929
+            $line->fetch_optionals();
3930
+
3931
+            //if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3932
+            if (is_object($hookmanager))   // Old code is commented on preceding line.
3933
+            {
3934
+                if (empty($line->fk_parent_line))
3935
+                {
3936
+                    $parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3937
+                    $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3938
+                }
3939
+                else
3940
+                {
3941
+                    $parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline, 'fk_parent_line'=>$line->fk_parent_line);
3942
+                    $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3943
+                }
3944
+            }
3945
+            if (empty($reshook))
3946
+            {
3947
+                $this->printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected,$extrafieldsline);
3948
+            }
3949
+
3950
+            $i++;
3951
+        }
3952
+        print "</tbody>\n";
3953
+    }
3954
+
3955
+    /**
3956
+     *	Return HTML content of a detail line
3957
+     *	TODO Move this into an output class file (htmlline.class.php)
3958
+     *
3959
+     *	@param	string		$action				GET/POST action
3960
+     *	@param CommonObjectLine $line		       	Selected object line to output
3961
+     *	@param  string	    $var               	Is it a an odd line (true)
3962
+     *	@param  int		    $num               	Number of line (0)
3963
+     *	@param  int		    $i					I
3964
+     *	@param  int		    $dateSelector      	1=Show also date range input fields
3965
+     *	@param  string	    $seller            	Object of seller third party
3966
+     *	@param  string	    $buyer             	Object of buyer third party
3967
+     *	@param	int			$selected		   	Object line selected
3968
+     *  @param  int			$extrafieldsline	Object of extrafield line attribute
3969
+     *	@return	void
3970
+     */
3971
+    function printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected=0,$extrafieldsline=0)
3972
+    {
3973
+        global $conf,$langs,$user,$object,$hookmanager;
3974
+        global $form,$bc,$bcdd;
3975
+        global $object_rights, $disableedit, $disablemove, $disableremove;   // TODO We should not use global var for this !
3976
+
3977
+        $object_rights = $this->getRights();
3978
+
3979
+        $element=$this->element;
3980
+
3981
+        $text=''; $description=''; $type=0;
3982
+
3983
+        // Show product and description
3984
+        $type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
3985
+        // Try to enhance type detection using date_start and date_end for free lines where type was not saved.
3986
+        if (! empty($line->date_start)) $type=1; // deprecated
3987
+        if (! empty($line->date_end)) $type=1; // deprecated
3988
+
3989
+        // Ligne en mode visu
3990
+        if ($action != 'editline' || $selected != $line->id)
3991
+        {
3992
+            // Product
3993
+            if ($line->fk_product > 0)
3994
+            {
3995
+                $product_static = new Product($this->db);
3996
+                $product_static->fetch($line->fk_product);
3997
+
3998
+                $product_static->ref = $line->ref; //can change ref in hook
3999
+                $product_static->label = $line->label; //can change label in hook
4000
+                $text=$product_static->getNomUrl(1);
4001
+
4002
+                // Define output language and label
4003
+                if (! empty($conf->global->MAIN_MULTILANGS))
4004
+                {
4005
+                    if (! is_object($this->thirdparty))
4006
+                    {
4007
+                        dol_print_error('','Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
4008
+                        return;
4009
+                    }
4010
+
4011
+                    $prod = new Product($this->db);
4012
+                    $prod->fetch($line->fk_product);
4013
+
4014
+                    $outputlangs = $langs;
4015
+                    $newlang='';
4016
+                    if (empty($newlang) && GETPOST('lang_id','aZ09')) $newlang=GETPOST('lang_id','aZ09');
4017
+                    if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang)) $newlang=$this->thirdparty->default_lang;		// For language to language of customer
4018
+                    if (! empty($newlang))
4019
+                    {
4020
+                        $outputlangs = new Translate("",$conf);
4021
+                        $outputlangs->setDefaultLang($newlang);
4022
+                    }
4023
+
4024
+                    $label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
4025
+                }
4026
+                else
4027
+                {
4028
+                    $label = $line->product_label;
4029
+                }
4030
+
4031
+                $text.= ' - '.(! empty($line->label)?$line->label:$label);
4032
+                $description.=(! empty($conf->global->PRODUIT_DESC_IN_FORM)?'':dol_htmlentitiesbr($line->description));	// Description is what to show on popup. We shown nothing if already into desc.
4033
+            }
4034
+
4035
+            $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4036
+
4037
+            // Output template part (modules that overwrite templates must declare this into descriptor)
4038
+            // Use global variables + $dateSelector + $seller and $buyer
4039
+            $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4040
+            foreach($dirtpls as $reldir)
4041
+            {
4042
+                $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
4043
+                if (empty($conf->file->strict_mode)) {
4044
+                    $res=@include $tpl;
4045
+                } else {
4046
+                    $res=include $tpl; // for debug
4047
+                }
4048
+                if ($res) break;
4049
+            }
4050
+        }
4051
+
4052
+        // Ligne en mode update
4053
+        if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
4054
+        {
4055
+            $label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
4056
+            $placeholder=' placeholder="'.$langs->trans("Label").'"';
4057
+
4058
+            $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4059
+
4060
+            // Output template part (modules that overwrite templates must declare this into descriptor)
4061
+            // Use global variables + $dateSelector + $seller and $buyer
4062
+            $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4063
+            foreach($dirtpls as $reldir)
4064
+            {
4065
+                $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
4066
+                if (empty($conf->file->strict_mode)) {
4067
+                    $res=@include $tpl;
4068
+                } else {
4069
+                    $res=include $tpl; // for debug
4070
+                }
4071
+                if ($res) break;
4072
+            }
4073
+        }
4074
+    }
4075
+
4076
+
4077
+    /* This is to show array of line of details of source object */
4078
+
4079
+
4080
+    /**
4081
+     * 	Return HTML table table of source object lines
4082
+     *  TODO Move this and previous function into output html class file (htmlline.class.php).
4083
+     *  If lines are into a template, title must also be into a template
4084
+     *  But for the moment we don't know if it's possible, so we keep the method available on overloaded objects.
4085
+     *
4086
+     *	@param	string		$restrictlist		''=All lines, 'services'=Restrict to services only
4087
+     *  @return	void
4088
+     */
4089
+    function printOriginLinesList($restrictlist='')
4090
+    {
4091
+        global $langs, $hookmanager, $conf;
4092
+
4093
+        print '<tr class="liste_titre">';
4094
+        print '<td>'.$langs->trans('Ref').'</td>';
4095
+        print '<td>'.$langs->trans('Description').'</td>';
4096
+        print '<td align="right">'.$langs->trans('VATRate').'</td>';
4097
+        print '<td align="right">'.$langs->trans('PriceUHT').'</td>';
4098
+        if (!empty($conf->multicurrency->enabled)) print '<td align="right">'.$langs->trans('PriceUHTCurrency').'</td>';
4099
+        print '<td align="right">'.$langs->trans('Qty').'</td>';
4100
+        if($conf->global->PRODUCT_USE_UNITS)
4101
+        {
4102
+            print '<td align="left">'.$langs->trans('Unit').'</td>';
4103
+        }
4104
+        print '<td align="right">'.$langs->trans('ReductionShort').'</td></tr>';
4105
+
4106
+        $var = true;
4107
+        $i	 = 0;
4108
+
4109
+        if (! empty($this->lines))
4110
+        {
4111
+            foreach ($this->lines as $line)
4112
+            {
4113
+                if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4114
+                {
4115
+                    if (empty($line->fk_parent_line))
4116
+                    {
4117
+                        $parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
4118
+                        $action='';
4119
+                        $hookmanager->executeHooks('printOriginObjectLine',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
4120
+                    }
4121
+                }
4122
+                else
4123
+                {
4124
+                    $this->printOriginLine($line, $var, $restrictlist);
4125
+                }
4126
+
4127
+                $i++;
4128
+            }
4129
+        }
4130
+    }
4131
+
4132
+    /**
4133
+     * 	Return HTML with a line of table array of source object lines
4134
+     *  TODO Move this and previous function into output html class file (htmlline.class.php).
4135
+     *  If lines are into a template, title must also be into a template
4136
+     *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
4137
+     *
4138
+     * 	@param	CommonObjectLine	$line				Line
4139
+     * 	@param	string				$var				Var
4140
+     *	@param	string				$restrictlist		''=All lines, 'services'=Restrict to services only (strike line if not)
4141
+     * 	@return	void
4142
+     */
4143
+    function printOriginLine($line, $var, $restrictlist='')
4144
+    {
4145
+        global $langs, $conf;
4146
+
4147
+        //var_dump($line);
4148
+        if (!empty($line->date_start))
4149
+        {
4150
+            $date_start=$line->date_start;
4151
+        }
4152
+        else
4153
+        {
4154
+            $date_start=$line->date_debut_prevue;
4155
+            if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
4156
+        }
4157
+        if (!empty($line->date_end))
4158
+        {
4159
+            $date_end=$line->date_end;
4160
+        }
4161
+        else
4162
+        {
4163
+            $date_end=$line->date_fin_prevue;
4164
+            if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
4165
+        }
4166
+
4167
+        $this->tpl['label'] = '';
4168
+        if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
4169
+
4170
+        if (($line->info_bits & 2) == 2)  // TODO Not sure this is used for source object
4171
+        {
4172
+            $discount=new DiscountAbsolute($this->db);
4173
+            $discount->fk_soc = $this->socid;
4174
+            $this->tpl['label'].= $discount->getNomUrl(0,'discount');
4175
+        }
4176
+        else if (! empty($line->fk_product))
4177
+        {
4178
+            $productstatic = new Product($this->db);
4179
+            $productstatic->id = $line->fk_product;
4180
+            $productstatic->ref = $line->ref;
4181
+            $productstatic->type = $line->fk_product_type;
4182
+            if(empty($productstatic->ref)){
4183
+                $line->fetch_product();
4184
+                $productstatic = $line->product;
4185
+            }
4186
+			
4187
+            $this->tpl['label'].= $productstatic->getNomUrl(1);
4188
+            $this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
4189
+            // Dates
4190
+            if ($line->product_type == 1 && ($date_start || $date_end))
4191
+            {
4192
+                $this->tpl['label'].= get_date_range($date_start,$date_end);
4193
+            }
4194
+        }
4195
+        else
4196
+        {
4197
+            $this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''),'service') : img_object($langs->trans(''),'product')));
4198
+            if (!empty($line->desc)) {
4199
+                $this->tpl['label'].=$line->desc;
4200
+            }else {
4201
+                $this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
4202
+            }
4203
+			
4204
+            // Dates
4205
+            if ($line->product_type == 1 && ($date_start || $date_end))
4206
+            {
4207
+                $this->tpl['label'].= get_date_range($date_start,$date_end);
4208
+            }
4209
+        }
4210
+
4211
+        if (! empty($line->desc))
4212
+        {
4213
+            if ($line->desc == '(CREDIT_NOTE)')  // TODO Not sure this is used for source object
4214
+            {
4215
+                $discount=new DiscountAbsolute($this->db);
4216
+                $discount->fetch($line->fk_remise_except);
4217
+                $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote",$discount->getNomUrl(0));
4218
+            }
4219
+            elseif ($line->desc == '(DEPOSIT)')  // TODO Not sure this is used for source object
4220
+            {
4221
+                $discount=new DiscountAbsolute($this->db);
4222
+                $discount->fetch($line->fk_remise_except);
4223
+                $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit",$discount->getNomUrl(0));
4224
+            }
4225
+            elseif ($line->desc == '(EXCESS RECEIVED)')
4226
+            {
4227
+                $discount=new DiscountAbsolute($this->db);
4228
+                $discount->fetch($line->fk_remise_except);
4229
+                $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0));
4230
+            }
4231
+            elseif ($line->desc == '(EXCESS PAID)')
4232
+            {
4233
+                $discount=new DiscountAbsolute($this->db);
4234
+                $discount->fetch($line->fk_remise_except);
4235
+                $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid",$discount->getNomUrl(0));
4236
+            }
4237
+            else
4238
+            {
4239
+                $this->tpl['description'] = dol_trunc($line->desc,60);
4240
+            }
4241
+        }
4242
+        else
4243
+        {
4244
+            $this->tpl['description'] = '&nbsp;';
4245
+        }
4246
+
4247
+        // VAT Rate
4248
+        $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
4249
+        $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
4250
+        if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
4251
+
4252
+        $this->tpl['price'] = price($line->subprice);
4253
+        $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
4254
+        $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
4255
+        if ($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
4256
+        $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
4257
+
4258
+        // Is the line strike or not
4259
+        $this->tpl['strike']=0;
4260
+        if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike']=1;
4261
+
4262
+        // Output template part (modules that overwrite templates must declare this into descriptor)
4263
+        // Use global variables + $dateSelector + $seller and $buyer
4264
+        $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4265
+        foreach($dirtpls as $reldir)
4266
+        {
4267
+            $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
4268
+            if (empty($conf->file->strict_mode)) {
4269
+                $res=@include $tpl;
4270
+            } else {
4271
+                $res=include $tpl; // for debug
4272
+            }
4273
+            if ($res) break;
4274
+        }
4275
+    }
4276
+
4277
+
4278
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4279
+    /**
4280
+     *	Add resources to the current object : add entry into llx_element_resources
4281
+     *	Need $this->element & $this->id
4282
+     *
4283
+     *	@param		int		$resource_id		Resource id
4284
+     *	@param		string	$resource_type		'resource'
4285
+     *	@param		int		$busy				Busy or not
4286
+     *	@param		int		$mandatory			Mandatory or not
4287
+     *	@return		int							<=0 if KO, >0 if OK
4288
+     */
4289
+    function add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
4290
+    {
4291
+        // phpcs:enable
4292
+        $this->db->begin();
4293
+
4294
+        $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
4295
+        $sql.= "resource_id";
4296
+        $sql.= ", resource_type";
4297
+        $sql.= ", element_id";
4298
+        $sql.= ", element_type";
4299
+        $sql.= ", busy";
4300
+        $sql.= ", mandatory";
4301
+        $sql.= ") VALUES (";
4302
+        $sql.= $resource_id;
4303
+        $sql.= ", '".$this->db->escape($resource_type)."'";
4304
+        $sql.= ", '".$this->db->escape($this->id)."'";
4305
+        $sql.= ", '".$this->db->escape($this->element)."'";
4306
+        $sql.= ", '".$this->db->escape($busy)."'";
4307
+        $sql.= ", '".$this->db->escape($mandatory)."'";
4308
+        $sql.= ")";
4309
+
4310
+        dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
4311
+        if ($this->db->query($sql))
4312
+        {
4313
+            $this->db->commit();
4314
+            return 1;
4315
+        }
4316
+        else
4317
+        {
4318
+            $this->error=$this->db->lasterror();
4319
+            $this->db->rollback();
4320
+            return  0;
4321
+        }
4322
+    }
4323
+
4324
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4325
+    /**
4326
+     *    Delete a link to resource line
4327
+     *
4328
+     *    @param	int		$rowid			Id of resource line to delete
4329
+     *    @param	int		$element		element name (for trigger) TODO: use $this->element into commonobject class
4330
+     *    @param	int		$notrigger		Disable all triggers
4331
+     *    @return   int						>0 if OK, <0 if KO
4332
+     */
4333
+    function delete_resource($rowid, $element, $notrigger=0)
4334
+    {
4335
+        // phpcs:enable
4336
+        global $user;
4337
+
4338
+        $this->db->begin();
4339
+
4340
+        $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
4341
+        $sql.= " WHERE rowid=".$rowid;
4342
+
4343
+        dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
4344
+
4345
+        $resql=$this->db->query($sql);
4346
+        if (! $resql)
4347
+        {
4348
+            $this->error=$this->db->lasterror();
4349
+            $this->db->rollback();
4350
+            return -1;
4351
+        }
4352
+        else
4353
+        {
4354
+            if (! $notrigger)
4355
+            {
4356
+                $result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
4357
+                if ($result < 0) { $this->db->rollback(); return -1; }
4358
+            }
4359
+            $this->db->commit();
4360
+            return 1;
4361
+        }
4362
+    }
4363
+
4364
+
4365
+    /**
4366
+     * Overwrite magic function to solve problem of cloning object that are kept as references
4367
+     *
4368
+     * @return void
4369
+     */
4370
+    function __clone()
4371
+    {
4372
+        // Force a copy of this->lines, otherwise it will point to same object.
4373
+        if (isset($this->lines) && is_array($this->lines))
4374
+        {
4375
+            $nboflines=count($this->lines);
4376
+            for($i=0; $i < $nboflines; $i++)
4377
+            {
4378
+                $this->lines[$i] = clone $this->lines[$i];
4379
+            }
4380
+        }
4381
+    }
4382
+
4383
+    /**
4384
+     * Common function for all objects extending CommonObject for generating documents
4385
+     *
4386
+     * @param 	string 		$modelspath 	Relative folder where generators are placed
4387
+     * @param 	string 		$modele 		Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf') for example.
4388
+     * @param 	Translate 	$outputlangs 	Output language to use
4389
+     * @param 	int 		$hidedetails 	1 to hide details. 0 by default
4390
+     * @param 	int 		$hidedesc 		1 to hide product description. 0 by default
4391
+     * @param 	int 		$hideref 		1 to hide product reference. 0 by default
4392
+     * @param   null|array  $moreparams     Array to provide more information
4393
+     * @return 	int 						>0 if OK, <0 if KO
4394
+     * @see	addFileIntoDatabaseIndex
4395
+     */
4396
+    protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
4397
+    {
4398
+        global $conf, $langs, $user;
4399
+
4400
+        $srctemplatepath='';
4401
+
4402
+        // Increase limit for PDF build
4403
+        $err=error_reporting();
4404
+        error_reporting(0);
4405
+        @set_time_limit(120);
4406
+        error_reporting($err);
4407
+
4408
+        // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
4409
+        $tmp=explode(':',$modele,2);
4410
+        if (! empty($tmp[1]))
4411
+        {
4412
+            $modele=$tmp[0];
4413
+            $srctemplatepath=$tmp[1];
4414
+        }
4415
+
4416
+        // Search template files
4417
+        $file=''; $classname=''; $filefound=0;
4418
+        $dirmodels=array('/');
4419
+        if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels,$conf->modules_parts['models']);
4420
+        foreach($dirmodels as $reldir)
4421
+        {
4422
+            foreach(array('doc','pdf') as $prefix)
4423
+            {
4424
+                if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php";     // Member module use prefix_module.class.php
4425
+                else $file = $prefix."_".$modele.".modules.php";
4426
+
4427
+                // On verifie l'emplacement du modele
4428
+                $file=dol_buildpath($reldir.$modelspath.$file,0);
4429
+                if (file_exists($file))
4430
+                {
4431
+                    $filefound=1;
4432
+                    $classname=$prefix.'_'.$modele;
4433
+                    break;
4434
+                }
4435
+            }
4436
+            if ($filefound) break;
4437
+        }
4438
+
4439
+        // If generator was found
4440
+        if ($filefound)
4441
+        {
4442
+            global $db;  // Required to solve a conception default in commonstickergenerator.class.php making an include of code using $db
4443
+
4444
+            require_once $file;
4445
+
4446
+            $obj = new $classname($this->db);
4447
+
4448
+            // If generator is ODT, we must have srctemplatepath defined, if not we set it.
4449
+            if ($obj->type == 'odt' && empty($srctemplatepath))
4450
+            {
4451
+                $varfortemplatedir=$obj->scandir;
4452
+                if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
4453
+                {
4454
+                    $dirtoscan=$conf->global->$varfortemplatedir;
4455
+
4456
+                    $listoffiles=array();
4457
+
4458
+                    // Now we add first model found in directories scanned
4459
+                    $listofdir=explode(',',$dirtoscan);
4460
+                    foreach($listofdir as $key => $tmpdir)
4461
+                    {
4462
+                        $tmpdir=trim($tmpdir);
4463
+                        $tmpdir=preg_replace('/DOL_DATA_ROOT/',DOL_DATA_ROOT,$tmpdir);
4464
+                        if (! $tmpdir) { unset($listofdir[$key]); continue; }
4465
+                        if (is_dir($tmpdir))
4466
+                        {
4467
+                            $tmpfiles=dol_dir_list($tmpdir,'files',0,'\.od(s|t)$','','name',SORT_ASC,0);
4468
+                            if (count($tmpfiles)) $listoffiles=array_merge($listoffiles,$tmpfiles);
4469
+                        }
4470
+                    }
4471
+
4472
+                    if (count($listoffiles))
4473
+                    {
4474
+                        foreach($listoffiles as $record)
4475
+                        {
4476
+                            $srctemplatepath=$record['fullname'];
4477
+                            break;
4478
+                        }
4479
+                    }
4480
+                }
4481
+
4482
+                if (empty($srctemplatepath))
4483
+                {
4484
+                    $this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4485
+                    return -1;
4486
+                }
4487
+            }
4488
+
4489
+            if ($obj->type == 'odt' && ! empty($srctemplatepath))
4490
+            {
4491
+                if (! dol_is_file($srctemplatepath))
4492
+                {
4493
+                    $this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4494
+                    return -1;
4495
+                }
4496
+            }
4497
+
4498
+            // We save charset_output to restore it because write_file can change it if needed for
4499
+            // output format that does not support UTF8.
4500
+            $sav_charset_output=$outputlangs->charset_output;
4501
+
4502
+            if (in_array(get_class($this), array('Adherent')))
4503
+            {
4504
+                $arrayofrecords = array();   // The write_file of templates of adherent class need this var
4505
+                $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4506
+            }
4507
+            else
4508
+            {
4509
+                $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4510
+            }
4511
+            // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
4512
+
4513
+            if ($resultwritefile > 0)
4514
+            {
4515
+                $outputlangs->charset_output=$sav_charset_output;
4516
+
4517
+                // We delete old preview
4518
+                require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4519
+                dol_delete_preview($this);
4520
+
4521
+                // Index file in database
4522
+                if (! empty($obj->result['fullpath']))
4523
+                {
4524
+                    $destfull = $obj->result['fullpath'];
4525
+                    $upload_dir = dirname($destfull);
4526
+                    $destfile = basename($destfull);
4527
+                    $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $upload_dir);
4528
+
4529
+                    if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir))     // If not a tmp dir
4530
+                    {
4531
+                        $filename = basename($destfile);
4532
+                        $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4533
+                        $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4534
+
4535
+                        include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4536
+                        $ecmfile=new EcmFiles($this->db);
4537
+                        $result = $ecmfile->fetch(0, '', ($rel_dir?$rel_dir.'/':'').$filename);
4538
+
4539
+                        // Set the public "share" key
4540
+                        $setsharekey = false;
4541
+                        if ($this->element == 'propal')
4542
+                        {
4543
+                            $useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL;	// Replace this with 1 when feature to make online signature is ok
4544
+                            if ($useonlinesignature) $setsharekey=true;
4545
+                            if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4546
+                        }
4547
+                        if ($this->element == 'commande'     && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD))        $setsharekey=true;
4548
+                        if ($this->element == 'facture'      && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD))      $setsharekey=true;
4549
+                        if ($this->element == 'bank_account' && ! empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4550
+
4551
+                        if ($setsharekey)
4552
+                        {
4553
+                            if (empty($ecmfile->share))	// Because object not found or share not set yet
4554
+                            {
4555
+                                require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4556
+                                $ecmfile->share = getRandomPassword(true);
4557
+                            }
4558
+                        }
4559
+
4560
+                        if ($result > 0)
4561
+                        {
4562
+                            $ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4563
+                            $ecmfile->fullpath_orig = '';
4564
+                            $ecmfile->gen_or_uploaded = 'generated';
4565
+                            $ecmfile->description = '';    // indexed content
4566
+                            $ecmfile->keyword = '';        // keyword content
4567
+                            $result = $ecmfile->update($user);
4568
+                            if ($result < 0)
4569
+                            {
4570
+                                setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4571
+                            }
4572
+                        }
4573
+                        else
4574
+                        {
4575
+                            $ecmfile->entity = $conf->entity;
4576
+                            $ecmfile->filepath = $rel_dir;
4577
+                            $ecmfile->filename = $filename;
4578
+                            $ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4579
+                            $ecmfile->fullpath_orig = '';
4580
+                            $ecmfile->gen_or_uploaded = 'generated';
4581
+                            $ecmfile->description = '';    // indexed content
4582
+                            $ecmfile->keyword = '';        // keyword content
4583
+                            $ecmfile->src_object_type = $this->table_element;
4584
+                            $ecmfile->src_object_id   = $this->id;
4585
+
4586
+                            $result = $ecmfile->create($user);
4587
+                            if ($result < 0)
4588
+                            {
4589
+                                setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4590
+                            }
4591
+                        }
4592
+
4593
+                        /*$this->result['fullname']=$destfull;
4594
+						$this->result['filepath']=$ecmfile->filepath;
4595
+						$this->result['filename']=$ecmfile->filename;*/
4596
+                        //var_dump($obj->update_main_doc_field);exit;
4597
+
4598
+                        // Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set)
4599
+                        $update_main_doc_field=0;
4600
+                        if (! empty($obj->update_main_doc_field)) $update_main_doc_field=1;
4601
+                        if ($update_main_doc_field && ! empty($this->table_element))
4602
+                        {
4603
+                            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4604
+                            $sql.= ' WHERE rowid = '.$this->id;
4605
+                            $resql = $this->db->query($sql);
4606
+                            if (! $resql) dol_print_error($this->db);
4607
+                        }
4608
+                    }
4609
+                }
4610
+                else
4611
+                {
4612
+                    dol_syslog('Method ->write_file was called on object '.get_class($obj).' and return a success but the return array ->result["fullpath"] was not set.', LOG_WARNING);
4613
+                }
4614
+
4615
+                // Success in building document. We build meta file.
4616
+                dol_meta_create($this);
4617
+
4618
+                return 1;
4619
+            }
4620
+            else
4621
+            {
4622
+                $outputlangs->charset_output=$sav_charset_output;
4623
+                dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4624
+                return -1;
4625
+            }
4626
+        }
4627
+        else
4628
+        {
4629
+            $this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file);
4630
+            dol_print_error('',$this->error);
4631
+            return -1;
4632
+        }
4633
+    }
4634
+
4635
+    /**
4636
+     *  Build thumb
4637
+     *  @TODO Move this into files.lib.php
4638
+     *
4639
+     *  @param      string	$file           Path file in UTF8 to original file to create thumbs from.
4640
+     *	@return		void
4641
+     */
4642
+    function addThumbs($file)
4643
+    {
4644
+        global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4645
+
4646
+        require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';		// This define also $maxwidthsmall, $quality, ...
4647
+
4648
+        $file_osencoded=dol_osencode($file);
4649
+        if (file_exists($file_osencoded))
4650
+        {
4651
+            // Create small thumbs for company (Ratio is near 16/9)
4652
+            // Used on logon for example
4653
+            vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4654
+
4655
+            // Create mini thumbs for company (Ratio is near 16/9)
4656
+            // Used on menu or for setup page for example
4657
+            vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4658
+        }
4659
+    }
4660
+
4661
+
4662
+    /* Functions common to commonobject and commonobjectline */
4663
+
4664
+    /* For default values */
4665
+
4666
+    /**
4667
+     * Return the default value to use for a field when showing the create form of object.
4668
+     * Return values in this order:
4669
+     * 1) If parameter is available into POST, we return it first.
4670
+     * 2) If not but an alternate value was provided as parameter of function, we return it.
4671
+     * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
4672
+     * 4) Return value found into database (TODO No yet implemented)
4673
+     *
4674
+     * @param   string              $fieldname          Name of field
4675
+     * @param   string              $alternatevalue     Alternate value to use
4676
+     * @return  string|string[]                         Default value (can be an array if the GETPOST return an array)
4677
+     **/
4678
+    function getDefaultCreateValueFor($fieldname, $alternatevalue=null)
4679
+    {
4680
+        global $conf, $_POST;
4681
+
4682
+        // If param here has been posted, we use this value first.
4683
+        if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4684
+
4685
+        if (isset($alternatevalue)) return $alternatevalue;
4686
+
4687
+        $newelement=$this->element;
4688
+        if ($newelement == 'facture') $newelement='invoice';
4689
+        if ($newelement == 'commande') $newelement='order';
4690
+        if (empty($newelement))
4691
+        {
4692
+            dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4693
+            return '';
4694
+        }
4695
+
4696
+        $keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4697
+        //var_dump($keyforfieldname);
4698
+        if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4699
+
4700
+        // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4701
+    }
4702
+
4703
+
4704
+    /* For triggers */
4705
+
4706
+
4707
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4708
+    /**
4709
+     * Call trigger based on this instance.
4710
+     * Some context information may also be provided into array property this->context.
4711
+     * NB:  Error from trigger are stacked in interface->errors
4712
+     * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
4713
+     *
4714
+     * @param   string    $trigger_name   trigger's name to execute
4715
+     * @param   User      $user           Object user
4716
+     * @return  int                       Result of run_triggers
4717
+     */
4718
+    function call_trigger($trigger_name, $user)
4719
+    {
4720
+        // phpcs:enable
4721
+        global $langs,$conf;
4722
+
4723
+        include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4724
+        $interface=new Interfaces($this->db);
4725
+        $result=$interface->run_triggers($trigger_name,$this,$user,$langs,$conf);
4726
+
4727
+        if ($result < 0)
4728
+        {
4729
+            if (!empty($this->errors))
4730
+            {
4731
+                $this->errors=array_unique(array_merge($this->errors,$interface->errors));   // We use array_unique because when a trigger call another trigger on same object, this->errors is added twice.
4732
+            }
4733
+            else
4734
+            {
4735
+                $this->errors=$interface->errors;
4736
+            }
4737
+        }
4738
+        return $result;
4739
+    }
4740
+
4741
+
4742
+    /* Functions for extrafields */
4743
+
4744
+
4745
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4746
+    /**
4747
+     *  Function to get extra fields of an object into $this->array_options
4748
+     *  This method is in most cases called by method fetch of objects but you can call it separately.
4749
+     *
4750
+     *  @param	int		$rowid			Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
4751
+     *  @param  array	$optionsArray   Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
4752
+     *  @return	int						<0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded
4753
+     */
4754
+    function fetch_optionals($rowid=null, $optionsArray=null)
4755
+    {
4756
+        // phpcs:enable
4757
+        if (empty($rowid)) $rowid=$this->id;
4758
+
4759
+        // To avoid SQL errors. Probably not the better solution though
4760
+        if (!$this->table_element) {
4761
+            return 0;
4762
+        }
4763
+
4764
+        $this->array_options=array();
4765
+
4766
+        if (! is_array($optionsArray))
4767
+        {
4768
+            // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4769
+            // TODO Use of existing $extrafield is not yet ready (must mutualize code that use extrafields in form first)
4770
+            // global $extrafields;
4771
+            //if (! is_object($extrafields))
4772
+            //{
4773
+                // require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4774
+            $extrafields = new ExtraFields();
4775
+            //}
4776
+
4777
+            // Load array of extrafields for elementype = $this->table_element
4778
+            if (empty($extrafields->attributes[$this->table_element]['loaded']))
4779
+            {
4780
+                $extrafields->fetch_name_optionals_label($this->table_element);
4781
+            }
4782
+            $optionsArray = (! empty($extrafields->attributes[$this->table_element]['label'])?$extrafields->attributes[$this->table_element]['label']:null);
4783
+        }
4784
+        else
4785
+        {
4786
+            global $extrafields;
4787
+            dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
4788
+        }
4789
+
4790
+        $table_element = $this->table_element;
4791
+        if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4792
+
4793
+        // Request to get complementary values
4794
+        if (is_array($optionsArray) && count($optionsArray) > 0)
4795
+        {
4796
+            $sql = "SELECT rowid";
4797
+            foreach ($optionsArray as $name => $label)
4798
+            {
4799
+                if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate')
4800
+                {
4801
+                    $sql.= ", ".$name;
4802
+                }
4803
+            }
4804
+            $sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
4805
+            $sql.= " WHERE fk_object = ".$rowid;
4806
+
4807
+            //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG);		// Too verbose
4808
+            $resql=$this->db->query($sql);
4809
+            if ($resql)
4810
+            {
4811
+                $this->array_options = array();
4812
+                $numrows=$this->db->num_rows($resql);
4813
+                if ($numrows)
4814
+                {
4815
+                    $tab = $this->db->fetch_array($resql);
4816
+
4817
+                    foreach ($tab as $key => $value)
4818
+                    {
4819
+                        // Test fetch_array ! is_int($key) because fetch_array result is a mix table with Key as alpha and Key as int (depend db engine)
4820
+                        if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
4821
+                        {
4822
+                            // we can add this attribute to object
4823
+                            if (! empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
4824
+                            {
4825
+                                //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
4826
+                                $this->array_options["options_".$key]=$this->db->jdate($value);
4827
+                            }
4828
+                            else
4829
+                            {
4830
+                                $this->array_options["options_".$key]=$value;
4831
+                            }
4832
+
4833
+                            //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
4834
+                        }
4835
+                    }
4836
+                }
4837
+
4838
+                $this->db->free($resql);
4839
+
4840
+                if ($numrows) return $numrows;
4841
+                else return 0;
4842
+            }
4843
+            else
4844
+            {
4845
+                dol_print_error($this->db);
4846
+                return -1;
4847
+            }
4848
+        }
4849
+        return 0;
4850
+    }
4851
+
4852
+    /**
4853
+     *	Delete all extra fields values for the current object.
4854
+     *
4855
+     *  @return	int		<0 if KO, >0 if OK
4856
+     */
4857
+    function deleteExtraFields()
4858
+    {
4859
+        $this->db->begin();
4860
+
4861
+        $table_element = $this->table_element;
4862
+        if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4863
+
4864
+        $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4865
+        dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
4866
+        $resql=$this->db->query($sql_del);
4867
+        if (! $resql)
4868
+        {
4869
+            $this->error=$this->db->lasterror();
4870
+            $this->db->rollback();
4871
+            return -1;
4872
+        }
4873
+        else
4874
+        {
4875
+            $this->db->commit();
4876
+            return 1;
4877
+        }
4878
+    }
4879
+
4880
+    /**
4881
+     *	Add/Update all extra fields values for the current object.
4882
+     *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4883
+     *  This function delete record with all extrafields and insert them again from the array $this->array_options.
4884
+     *
4885
+     *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
4886
+     *  @param	User		$userused		Object user
4887
+     *  @return int 						-1=error, O=did nothing, 1=OK
4888
+     *  @see updateExtraField, setValueFrom
4889
+     */
4890
+    function insertExtraFields($trigger='', $userused=null)
4891
+    {
4892
+        global $conf,$langs,$user;
4893
+
4894
+        if (empty($userused)) $userused=$user;
4895
+
4896
+        $error=0;
4897
+
4898
+        if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4899
+
4900
+        if (! empty($this->array_options))
4901
+        {
4902
+            // Check parameters
4903
+            $langs->load('admin');
4904
+            require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4905
+            $extrafields = new ExtraFields($this->db);
4906
+            $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4907
+
4908
+            //Eliminate copied source object extra_fields that do not exist in target object
4909
+            $new_array_options=array();
4910
+            foreach ($this->array_options as $key => $value) {
4911
+                if (in_array(substr($key,8), array_keys($target_extrafields)))	// We remove the 'options_' from $key for test
4912
+                    $new_array_options[$key] = $value;
4913
+                elseif (in_array($key, array_keys($target_extrafields)))		// We test on $key that does not contains the 'options_' prefix
4914
+                    $new_array_options['options_'.$key] = $value;
4915
+            }
4916
+
4917
+            foreach($new_array_options as $key => $value)
4918
+            {
4919
+                    $attributeKey      = substr($key,8);   // Remove 'options_' prefix
4920
+                    $attributeType     = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
4921
+                    $attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
4922
+                    $attributeParam    = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
4923
+                    $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
4924
+
4925
+                    if ($attributeRequired)
4926
+                    {
4927
+                        $mandatorypb=false;
4928
+                        if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb=true;
4929
+                        if ($this->array_options[$key] === '') $mandatorypb=true;
4930
+                        if ($mandatorypb)
4931
+                        {
4932
+                            dol_syslog($this->error);
4933
+                            $this->errors[]=$langs->trans('ErrorFieldRequired', $attributeLabel);
4934
+                            return -1;
4935
+                        }
4936
+                    }
4937
+
4938
+                //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
4939
+                //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
4940
+
4941
+                    switch ($attributeType)
4942
+                    {
4943
+                        case 'int':
4944
+                          if (!is_numeric($value) && $value!='')
4945
+                            {
4946
+                                $this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
4947
+                                return -1;
4948
+                            }
4949
+                            elseif ($value=='')
4950
+                            {
4951
+                                $new_array_options[$key] = null;
4952
+                            }
4953
+                            break;
4954
+                    case 'double':
4955
+                        $value = price2num($value);
4956
+                        if (!is_numeric($value) && $value!='')
4957
+                        {
4958
+                            dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
4959
+                            $this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
4960
+                            return -1;
4961
+                        }
4962
+                        elseif ($value=='')
4963
+                        {
4964
+                            $new_array_options[$key] = null;
4965
+                        }
4966
+                        //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
4967
+                        $new_array_options[$key] = $value;
4968
+                        break;
4969
+                        /*case 'select':	// Not required, we chosed value='0' for undefined values
4970
+             			if ($value=='-1')
4971
+             			{
4972
+             				$this->array_options[$key] = null;
4973
+             			}
4974
+             			break;*/
4975
+                        case 'password':
4976
+                           $algo='';
4977
+                            if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']))
4978
+                            {
4979
+                                // If there is an encryption choice, we use it to crypt data before insert
4980
+                                $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
4981
+                                $algo=reset($tmparrays);
4982
+                                if ($algo != '')
4983
+                                {
4984
+                                    //global $action;		// $action may be 'create', 'update', 'update_extras'...
4985
+                                    //var_dump($action);
4986
+                                    //var_dump($this->oldcopy);exit;
4987
+                                    if (is_object($this->oldcopy))		// If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
4988
+                                    {
4989
+                                        //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
4990
+                                        if ($this->array_options[$key] == $this->oldcopy->array_options[$key])	// If old value crypted in database is same than submited new value, it means we don't change it, so we don't update.
4991
+                                        {
4992
+                                            $new_array_options[$key] = $this->array_options[$key];	// Value is kept
4993
+                                        }
4994
+                                    else
4995
+                                    {
4996
+                                        // var_dump($algo);
4997
+                                        $newvalue = dol_hash($this->array_options[$key], $algo);
4998
+                                        $new_array_options[$key] = $newvalue;
4999
+                                    }
5000
+                                    }
5001
+                                    else
5002
+                                    {
5003
+                                        $new_array_options[$key] = $this->array_options[$key];	// Value is kept
5004
+                                    }
5005
+                                }
5006
+                            }
5007
+                            else	// Common usage
5008
+                            {
5009
+                                $new_array_options[$key] = $this->array_options[$key];
5010
+                            }
5011
+                            break;
5012
+                        case 'price':
5013
+                        $new_array_options[$key] = price2num($this->array_options[$key]);
5014
+                        break;
5015
+                    case 'date':
5016
+                        $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5017
+                        break;
5018
+                    case 'datetime':
5019
+                        // If data is a string instead of a timestamp, we convert it
5020
+                        if (! is_int($this->array_options[$key])) {
5021
+                            $this->array_options[$key] = strtotime($this->array_options[$key]);
5022
+                        }
5023
+                        $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5024
+                        break;
5025
+                        case 'link':
5026
+                        $param_list=array_keys($attributeParam['options']);
5027
+                        // 0 : ObjectName
5028
+                        // 1 : classPath
5029
+                        $InfoFieldList = explode(":", $param_list[0]);
5030
+                        dol_include_once($InfoFieldList[1]);
5031
+                        if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5032
+                        {
5033
+                            if ($value == '-1')	// -1 is key for no defined in combo list of objects
5034
+                            {
5035
+                                $new_array_options[$key]='';
5036
+                            }
5037
+                            elseif ($value)
5038
+                            {
5039
+                                $object = new $InfoFieldList[0]($this->db);
5040
+                                if (is_numeric($value)) $res=$object->fetch($value);
5041
+                                else $res=$object->fetch('',$value);
5042
+
5043
+                                if ($res > 0) $new_array_options[$key]=$object->id;
5044
+                                else
5045
+                                {
5046
+                                    $this->error="Id/Ref '".$value."' for object '".$object->element."' not found";
5047
+                                    $this->db->rollback();
5048
+                                    return -1;
5049
+                                }
5050
+                            }
5051
+                        }
5052
+                        else
5053
+                        {
5054
+                            dol_syslog('Error bad setup of extrafield', LOG_WARNING);
5055
+                        }
5056
+                        break;
5057
+                    }
5058
+            }
5059
+
5060
+            $this->db->begin();
5061
+
5062
+            $table_element = $this->table_element;
5063
+            if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5064
+
5065
+            $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5066
+            dol_syslog(get_class($this)."::insertExtraFields delete", LOG_DEBUG);
5067
+            $this->db->query($sql_del);
5068
+
5069
+            $sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
5070
+            foreach($new_array_options as $key => $value)
5071
+            {
5072
+                $attributeKey = substr($key,8);   // Remove 'options_' prefix
5073
+                // Add field of attribut
5074
+                if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator
5075
+                    $sql.=",".$attributeKey;
5076
+            }
5077
+            $sql .= ") VALUES (".$this->id;
5078
+
5079
+            foreach($new_array_options as $key => $value)
5080
+            {
5081
+                $attributeKey = substr($key,8);   // Remove 'options_' prefix
5082
+                // Add field of attribute
5083
+                if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator)
5084
+                {
5085
+                    if ($new_array_options[$key] != '')
5086
+                    {
5087
+                        $sql.=",'".$this->db->escape($new_array_options[$key])."'";
5088
+                    }
5089
+                    else
5090
+                    {
5091
+                        $sql.=",null";
5092
+                    }
5093
+                }
5094
+            }
5095
+            $sql.=")";
5096
+
5097
+            dol_syslog(get_class($this)."::insertExtraFields insert", LOG_DEBUG);
5098
+            $resql = $this->db->query($sql);
5099
+            if (! $resql)
5100
+            {
5101
+                $this->error=$this->db->lasterror();
5102
+                $error++;
5103
+            }
5104
+
5105
+            if (! $error && $trigger)
5106
+            {
5107
+                // Call trigger
5108
+                $this->context=array('extrafieldaddupdate'=>1);
5109
+                $result=$this->call_trigger($trigger, $userused);
5110
+                if ($result < 0) $error++;
5111
+                // End call trigger
5112
+            }
5113
+
5114
+            if ($error)
5115
+            {
5116
+                $this->db->rollback();
5117
+                return -1;
5118
+            }
5119
+            else
5120
+            {
5121
+                $this->db->commit();
5122
+                return 1;
5123
+            }
5124
+        }
5125
+        else return 0;
5126
+    }
5127
+
5128
+    /**
5129
+     *	Update an extra field value for the current object.
5130
+     *  Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
5131
+     *
5132
+     *  @param  string      $key    		Key of the extrafield (without starting 'options_')
5133
+     *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
5134
+     *  @param	User		$userused		Object user
5135
+     *  @return int                 		-1=error, O=did nothing, 1=OK
5136
+     *  @see setValueFrom, insertExtraFields
5137
+     */
5138
+    function updateExtraField($key, $trigger=null, $userused=null)
5139
+    {
5140
+        global $conf,$langs,$user;
5141
+
5142
+        if (empty($userused)) $userused=$user;
5143
+
5144
+        $error=0;
5145
+
5146
+        if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
5147
+
5148
+        if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
5149
+        {
5150
+            // Check parameters
5151
+            $langs->load('admin');
5152
+            require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5153
+            $extrafields = new ExtraFields($this->db);
5154
+            $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
5155
+
5156
+            $value=$this->array_options["options_".$key];
5157
+
5158
+            $attributeType     = $extrafields->attributes[$this->table_element]['type'][$key];
5159
+            $attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$key];
5160
+            $attributeParam    = $extrafields->attributes[$this->table_element]['param'][$key];
5161
+            $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
5162
+
5163
+            //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5164
+            //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5165
+
5166
+            switch ($attributeType)
5167
+            {
5168
+                case 'int':
5169
+                    if (!is_numeric($value) && $value!='')
5170
+                    {
5171
+                        $this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
5172
+                        return -1;
5173
+                    }
5174
+                    elseif ($value=='')
5175
+                    {
5176
+                        $this->array_options["options_".$key] = null;
5177
+                    }
5178
+                    break;
5179
+                case 'double':
5180
+                    $value = price2num($value);
5181
+                    if (!is_numeric($value) && $value!='')
5182
+                    {
5183
+                        dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5184
+                        $this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5185
+                        return -1;
5186
+                    }
5187
+                    elseif ($value=='')
5188
+                    {
5189
+                        $this->array_options["options_".$key] = null;
5190
+                    }
5191
+                    //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5192
+                    $this->array_options["options_".$key] = $value;
5193
+                    break;
5194
+                    /*case 'select':	// Not required, we chosed value='0' for undefined values
5195
+             		if ($value=='-1')
5196
+             		{
5197
+             			$this->array_options[$key] = null;
5198
+             		}
5199
+             		break;*/
5200
+                case 'price':
5201
+                    $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
5202
+                    break;
5203
+                case 'date':
5204
+                    $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5205
+                    break;
5206
+                case 'datetime':
5207
+                    $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5208
+                    break;
5209
+                case 'link':
5210
+                    $param_list=array_keys($attributeParam['options']);
5211
+                    // 0 : ObjectName
5212
+                    // 1 : classPath
5213
+                    $InfoFieldList = explode(":", $param_list[0]);
5214
+                    dol_include_once($InfoFieldList[1]);
5215
+                    if ($value)
5216
+                    {
5217
+                        $object = new $InfoFieldList[0]($this->db);
5218
+                        $object->fetch(0,$value);
5219
+                        $this->array_options["options_".$key]=$object->id;
5220
+                    }
5221
+                    break;
5222
+            }
5223
+
5224
+            $this->db->begin();
5225
+            $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
5226
+            $sql .= " WHERE fk_object = ".$this->id;
5227
+            $resql = $this->db->query($sql);
5228
+            if (! $resql)
5229
+            {
5230
+                $error++;
5231
+                $this->error=$this->db->lasterror();
5232
+            }
5233
+
5234
+            if (! $error && $trigger)
5235
+            {
5236
+                // Call trigger
5237
+                $this->context=array('extrafieldupdate'=>1);
5238
+                $result=$this->call_trigger($trigger, $userused);
5239
+                if ($result < 0) $error++;
5240
+                // End call trigger
5241
+            }
5242
+
5243
+            if ($error)
5244
+            {
5245
+                dol_syslog(get_class($this) . "::".__METHOD__ . $this->error, LOG_ERR);
5246
+                $this->db->rollback();
5247
+                return -1;
5248
+            }
5249
+            else
5250
+            {
5251
+                $this->db->commit();
5252
+                return 1;
5253
+            }
5254
+        }
5255
+        else return 0;
5256
+    }
5257
+
5258
+
5259
+    /**
5260
+     * Return HTML string to put an input field into a page
5261
+     * Code very similar with showInputField of extra fields
5262
+     *
5263
+     * @param  array   		$val	       Array of properties for field to show
5264
+     * @param  string  		$key           Key of attribute
5265
+     * @param  string  		$value         Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value)
5266
+     * @param  string  		$moreparam     To add more parameters on html input tag
5267
+     * @param  string  		$keysuffix     Prefix string to add into name and id of field (can be used to avoid duplicate names)
5268
+     * @param  string  		$keyprefix     Suffix string to add into name and id of field (can be used to avoid duplicate names)
5269
+     * @param  string|int		$morecss       Value for css to define style/length of field. May also be a numeric.
5270
+     * @return string
5271
+     */
5272
+    function showInputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $morecss=0)
5273
+    {
5274
+        global $conf,$langs,$form;
5275
+
5276
+        if (! is_object($form))
5277
+        {
5278
+            require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5279
+            $form=new Form($this->db);
5280
+        }
5281
+
5282
+        $val=$this->fields[$key];
5283
+
5284
+        $out='';
5285
+        $type='';
5286
+        $param = array();
5287
+        $param['options']=array();
5288
+        $size =$this->fields[$key]['size'];
5289
+        // Because we work on extrafields
5290
+        if(preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)){
5291
+            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5292
+            $type ='link';
5293
+        } elseif(preg_match('/^link:(.*):(.*)/i', $val['type'], $reg)) {
5294
+            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5295
+            $type ='link';
5296
+        } elseif(preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5297
+            $param['options']=array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4]=>'N');
5298
+            $type ='sellist';
5299
+        } elseif(preg_match('/varchar\((\d+)\)/', $val['type'],$reg)) {
5300
+            $param['options']=array();
5301
+            $type ='varchar';
5302
+            $size=$reg[1];
5303
+        } elseif(preg_match('/varchar/', $val['type'])) {
5304
+            $param['options']=array();
5305
+            $type ='varchar';
5306
+        } elseif(is_array($this->fields[$key]['arrayofkeyval'])) {
5307
+            $param['options']=$this->fields[$key]['arrayofkeyval'];
5308
+            $type ='select';
5309
+        } else {
5310
+            $param['options']=array();
5311
+            $type =$this->fields[$key]['type'];
5312
+        }
5313
+
5314
+        $label=$this->fields[$key]['label'];
5315
+        //$elementtype=$this->fields[$key]['elementtype'];	// Seems not used
5316
+        $default=$this->fields[$key]['default'];
5317
+        $computed=$this->fields[$key]['computed'];
5318
+        $unique=$this->fields[$key]['unique'];
5319
+        $required=$this->fields[$key]['required'];
5320
+
5321
+        $langfile=$this->fields[$key]['langfile'];
5322
+        $list=$this->fields[$key]['list'];
5323
+        $hidden=abs($this->fields[$key]['visible'])!=1?1:0;
5324
+
5325
+        $objectid = $this->id;
5326
+
5327
+
5328
+        if ($computed)
5329
+        {
5330
+            if (! preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
5331
+            else return '';
5332
+        }
5333
+
5334
+
5335
+        // Use in priority showsize from parameters, then $val['css'] then autodefine
5336
+        if (empty($morecss) && ! empty($val['css']))
5337
+        {
5338
+            $showsize = $val['css'];
5339
+        }
5340
+        if (empty($morecss))
5341
+        {
5342
+            if ($type == 'date')
5343
+            {
5344
+                $morecss = 'minwidth100imp';
5345
+            }
5346
+            elseif ($type == 'datetime')
5347
+            {
5348
+                $morecss = 'minwidth200imp';
5349
+            }
5350
+            elseif (in_array($type,array('int','integer','price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/',$type))
5351
+            {
5352
+                $morecss = 'maxwidth75';
5353
+                        }elseif ($type == 'url')
5354
+            {
5355
+                $morecss='minwidth400';
5356
+            }
5357
+            elseif ($type == 'boolean')
5358
+            {
5359
+                $morecss='';
5360
+            }
5361
+            else
5362
+            {
5363
+                if (round($size) < 12)
5364
+                {
5365
+                    $morecss = 'minwidth100';
5366
+                }
5367
+                else if (round($size) <= 48)
5368
+                {
5369
+                    $morecss = 'minwidth200';
5370
+                }
5371
+                else
5372
+                {
5373
+                    $morecss = 'minwidth400';
5374
+                }
5375
+            }
5376
+        }
5377
+
5378
+        if (in_array($type,array('date','datetime')))
5379
+        {
5380
+            $tmp=explode(',',$size);
5381
+            $newsize=$tmp[0];
5382
+
5383
+            $showtime = in_array($type,array('datetime')) ? 1 : 0;
5384
+
5385
+            // Do not show current date when field not required (see selectDate() method)
5386
+            if (!$required && $value == '') $value = '-1';
5387
+
5388
+            // TODO Must also support $moreparam
5389
+            $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
5390
+        }
5391
+        elseif (in_array($type,array('int','integer')))
5392
+        {
5393
+            $tmp=explode(',',$size);
5394
+            $newsize=$tmp[0];
5395
+            $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5396
+        }
5397
+        elseif (preg_match('/varchar/', $type))
5398
+        {
5399
+            $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$size.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5400
+        }
5401
+        elseif (in_array($type, array('mail', 'phone', 'url')))
5402
+        {
5403
+            $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5404
+        }
5405
+        elseif ($type == 'text')
5406
+        {
5407
+            if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5408
+            {
5409
+                require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5410
+                $doleditor=new DolEditor($keyprefix.$key.$keysuffix,$value,'',200,'dolibarr_notes','In',false,false,false,ROWS_5,'90%');
5411
+                $out=$doleditor->Create(1);
5412
+            }
5413
+            else
5414
+            {
5415
+                $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5416
+            }
5417
+        }
5418
+        elseif ($type == 'html')
5419
+        {
5420
+            if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5421
+            {
5422
+                require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5423
+                $doleditor=new DolEditor($keyprefix.$key.$keysuffix,$value,'',200,'dolibarr_notes','In',false,false,! empty($conf->fckeditor->enabled) && $conf->global->FCKEDITOR_ENABLE_SOCIETE,ROWS_5,'90%');
5424
+                $out=$doleditor->Create(1);
5425
+            }
5426
+            else
5427
+            {
5428
+                $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5429
+            }
5430
+        }
5431
+        elseif ($type == 'boolean')
5432
+        {
5433
+            $checked='';
5434
+            if (!empty($value)) {
5435
+                $checked=' checked value="1" ';
5436
+            } else {
5437
+                $checked=' value="1" ';
5438
+            }
5439
+            $out='<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam?$moreparam:'').'>';
5440
+        }
5441
+        elseif ($type == 'price')
5442
+        {
5443
+            if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5444
+                $value=price($value);
5445
+            }
5446
+            $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> '.$langs->getCurrencySymbol($conf->currency);
5447
+        }
5448
+        elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/',$type))
5449
+        {
5450
+            if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5451
+                $value=price($value);
5452
+            }
5453
+            $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> ';
5454
+        }
5455
+        elseif ($type == 'select')
5456
+        {
5457
+            $out = '';
5458
+            if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5459
+            {
5460
+                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5461
+                $out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5462
+            }
5463
+
5464
+            $out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5465
+                if((! isset($this->fields[$key]['default'])) ||($this->fields[$key]['notnull']!=1))$out.='<option value="0">&nbsp;</option>';
5466
+            foreach ($param['options'] as $key => $val)
5467
+            {
5468
+                if ((string) $key == '') continue;
5469
+                list($val, $parent) = explode('|', $val);
5470
+                $out.='<option value="'.$key.'"';
5471
+                $out.= (((string) $value == (string) $key)?' selected':'');
5472
+                $out.= (!empty($parent)?' parent="'.$parent.'"':'');
5473
+                $out.='>'.$val.'</option>';
5474
+            }
5475
+            $out.='</select>';
5476
+        }
5477
+        elseif ($type == 'sellist')
5478
+        {
5479
+            $out = '';
5480
+            if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5481
+            {
5482
+                include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5483
+                $out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5484
+            }
5485
+
5486
+            $out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5487
+            if (is_array($param['options']))
5488
+            {
5489
+                $param_list=array_keys($param['options']);
5490
+                $InfoFieldList = explode(":", $param_list[0]);
5491
+                $parentName='';
5492
+                $parentField='';
5493
+                // 0 : tableName
5494
+                // 1 : label field name
5495
+                // 2 : key fields name (if differ of rowid)
5496
+                // 3 : key field parent (for dependent lists)
5497
+                // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5498
+                $keyList=(empty($InfoFieldList[2])?'rowid':$InfoFieldList[2].' as rowid');
5499
+
5500
+
5501
+                if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4]))
5502
+                {
5503
+                    if (strpos($InfoFieldList[4], 'extra.') !== false)
5504
+                    {
5505
+                        $keyList='main.'.$InfoFieldList[2].' as rowid';
5506
+                    } else {
5507
+                        $keyList=$InfoFieldList[2].' as rowid';
5508
+                    }
5509
+                }
5510
+                if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3]))
5511
+                {
5512
+                    list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
5513
+                    $keyList.= ', '.$parentField;
5514
+                }
5515
+
5516
+                $fields_label = explode('|',$InfoFieldList[1]);
5517
+                if (is_array($fields_label))
5518
+                {
5519
+                    $keyList .=', ';
5520
+                    $keyList .= implode(', ', $fields_label);
5521
+                }
5522
+
5523
+                $sqlwhere='';
5524
+                $sql = 'SELECT '.$keyList;
5525
+                $sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
5526
+                if (!empty($InfoFieldList[4]))
5527
+                {
5528
+                    // can use SELECT request
5529
+                    if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5530
+                        $InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
5531
+                    }
5532
+
5533
+                    // current object id can be use into filter
5534
+                    if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5535
+                        $InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
5536
+                    } else {
5537
+                        $InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
5538
+                    }
5539
+                    //We have to join on extrafield table
5540
+                    if (strpos($InfoFieldList[4], 'extra')!==false)
5541
+                    {
5542
+                        $sql.= ' as main, '.MAIN_DB_PREFIX .$InfoFieldList[0].'_extrafields as extra';
5543
+                        $sqlwhere.= ' WHERE extra.fk_object=main.'.$InfoFieldList[2]. ' AND '.$InfoFieldList[4];
5544
+                    }
5545
+                    else
5546
+                    {
5547
+                        $sqlwhere.= ' WHERE '.$InfoFieldList[4];
5548
+                    }
5549
+                }
5550
+                else
5551
+                {
5552
+                    $sqlwhere.= ' WHERE 1=1';
5553
+                }
5554
+                // Some tables may have field, some other not. For the moment we disable it.
5555
+                if (in_array($InfoFieldList[0],array('tablewithentity')))
5556
+                {
5557
+                    $sqlwhere.= ' AND entity = '.$conf->entity;
5558
+                }
5559
+                $sql.=$sqlwhere;
5560
+                //print $sql;
5561
+
5562
+                $sql .= ' ORDER BY ' . implode(', ', $fields_label);
5563
+
5564
+                dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
5565
+                $resql = $this->db->query($sql);
5566
+                if ($resql)
5567
+                {
5568
+                    $out.='<option value="0">&nbsp;</option>';
5569
+                    $num = $this->db->num_rows($resql);
5570
+                    $i = 0;
5571
+                    while ($i < $num)
5572
+                    {
5573
+                        $labeltoshow='';
5574
+                        $obj = $this->db->fetch_object($resql);
5575
+
5576
+                        // Several field into label (eq table:code|libelle:rowid)
5577
+                        $notrans = false;
5578
+                        $fields_label = explode('|',$InfoFieldList[1]);
5579
+                        if (is_array($fields_label))
5580
+                        {
5581
+                            $notrans = true;
5582
+                            foreach ($fields_label as $field_toshow)
5583
+                            {
5584
+                                $labeltoshow.= $obj->$field_toshow.' ';
5585
+                            }
5586
+                        }
5587
+                        else
5588
+                        {
5589
+                            $labeltoshow=$obj->{$InfoFieldList[1]};
5590
+                        }
5591
+                        $labeltoshow=dol_trunc($labeltoshow,45);
5592
+
5593
+                        if ($value == $obj->rowid)
5594
+                        {
5595
+                            foreach ($fields_label as $field_toshow)
5596
+                            {
5597
+                                $translabel=$langs->trans($obj->$field_toshow);
5598
+                                if ($translabel!=$obj->$field_toshow) {
5599
+                                    $labeltoshow=dol_trunc($translabel,18).' ';
5600
+                                }else {
5601
+                                    $labeltoshow=dol_trunc($obj->$field_toshow,18).' ';
5602
+                                }
5603
+                            }
5604
+                            $out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5605
+                        }
5606
+                        else
5607
+                        {
5608
+                            if (! $notrans)
5609
+                            {
5610
+                                $translabel=$langs->trans($obj->{$InfoFieldList[1]});
5611
+                                if ($translabel!=$obj->{$InfoFieldList[1]}) {
5612
+                                    $labeltoshow=dol_trunc($translabel,18);
5613
+                                }
5614
+                                else {
5615
+                                    $labeltoshow=dol_trunc($obj->{$InfoFieldList[1]},18);
5616
+                                }
5617
+                            }
5618
+                            if (empty($labeltoshow)) $labeltoshow='(not defined)';
5619
+                            if ($value==$obj->rowid)
5620
+                            {
5621
+                                $out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5622
+                            }
5623
+
5624
+                            if (!empty($InfoFieldList[3]) && $parentField)
5625
+                            {
5626
+                                $parent = $parentName.':'.$obj->{$parentField};
5627
+                            }
5628
+
5629
+                            $out.='<option value="'.$obj->rowid.'"';
5630
+                            $out.= ($value==$obj->rowid?' selected':'');
5631
+                            $out.= (!empty($parent)?' parent="'.$parent.'"':'');
5632
+                            $out.='>'.$labeltoshow.'</option>';
5633
+                        }
5634
+
5635
+                        $i++;
5636
+                    }
5637
+                    $this->db->free($resql);
5638
+                }
5639
+                else {
5640
+                    print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
5641
+                }
5642
+            }
5643
+            $out.='</select>';
5644
+        }
5645
+        elseif ($type == 'checkbox')
5646
+        {
5647
+            $value_arr=explode(',',$value);
5648
+            $out=$form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options'])?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
5649
+        }
5650
+        elseif ($type == 'radio')
5651
+        {
5652
+            $out='';
5653
+            foreach ($param['options'] as $keyopt => $val)
5654
+            {
5655
+                $out.='<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'');
5656
+                $out.=' value="'.$keyopt.'"';
5657
+                $out.=' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
5658
+                $out.= ($value==$keyopt?'checked':'');
5659
+                $out.='/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
5660
+            }
5661
+        }
5662
+        elseif ($type == 'chkbxlst')
5663
+        {
5664
+            if (is_array($value)) {
5665
+                $value_arr = $value;
5666
+            }
5667
+            else {
5668
+                $value_arr = explode(',', $value);
5669
+            }
5670
+
5671
+            if (is_array($param['options'])) {
5672
+                $param_list = array_keys($param['options']);
5673
+                $InfoFieldList = explode(":", $param_list[0]);
5674
+                $parentName='';
5675
+                $parentField='';
5676
+                // 0 : tableName
5677
+                // 1 : label field name
5678
+                // 2 : key fields name (if differ of rowid)
5679
+                // 3 : key field parent (for dependent lists)
5680
+                // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5681
+                $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
5682
+
5683
+                if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3])) {
5684
+                    list ( $parentName, $parentField ) = explode('|', $InfoFieldList[3]);
5685
+                    $keyList .= ', ' . $parentField;
5686
+                }
5687
+                if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4])) {
5688
+                    if (strpos($InfoFieldList[4], 'extra.') !== false) {
5689
+                        $keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
5690
+                    } else {
5691
+                        $keyList = $InfoFieldList[2] . ' as rowid';
5692
+                    }
5693
+                }
5694
+
5695
+                $fields_label = explode('|', $InfoFieldList[1]);
5696
+                if (is_array($fields_label)) {
5697
+                    $keyList .= ', ';
5698
+                    $keyList .= implode(', ', $fields_label);
5699
+                }
5700
+
5701
+                $sqlwhere = '';
5702
+                $sql = 'SELECT ' . $keyList;
5703
+                $sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5704
+                if (! empty($InfoFieldList[4])) {
5705
+
5706
+                    // can use SELECT request
5707
+                    if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5708
+                        $InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
5709
+                    }
5710
+
5711
+                    // current object id can be use into filter
5712
+                    if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5713
+                        $InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
5714
+                    } else {
5715
+                        $InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
5716
+                    }
5717
+
5718
+                    // We have to join on extrafield table
5719
+                    if (strpos($InfoFieldList[4], 'extra') !== false) {
5720
+                        $sql .= ' as main, ' . MAIN_DB_PREFIX . $InfoFieldList[0] . '_extrafields as extra';
5721
+                        $sqlwhere .= ' WHERE extra.fk_object=main.' . $InfoFieldList[2] . ' AND ' . $InfoFieldList[4];
5722
+                    } else {
5723
+                        $sqlwhere .= ' WHERE ' . $InfoFieldList[4];
5724
+                    }
5725
+                } else {
5726
+                    $sqlwhere .= ' WHERE 1=1';
5727
+                }
5728
+                // Some tables may have field, some other not. For the moment we disable it.
5729
+                if (in_array($InfoFieldList[0], array ('tablewithentity')))
5730
+                {
5731
+                    $sqlwhere .= ' AND entity = ' . $conf->entity;
5732
+                }
5733
+                // $sql.=preg_replace('/^ AND /','',$sqlwhere);
5734
+                // print $sql;
5735
+
5736
+                $sql .= $sqlwhere;
5737
+                dol_syslog(get_class($this) . '::showInputField type=chkbxlst',LOG_DEBUG);
5738
+                $resql = $this->db->query($sql);
5739
+                if ($resql) {
5740
+                    $num = $this->db->num_rows($resql);
5741
+                    $i = 0;
5742
+
5743
+                    $data=array();
5744
+
5745
+                    while ( $i < $num ) {
5746
+                        $labeltoshow = '';
5747
+                        $obj = $this->db->fetch_object($resql);
5748
+
5749
+                        $notrans = false;
5750
+                        // Several field into label (eq table:code|libelle:rowid)
5751
+                        $fields_label = explode('|', $InfoFieldList[1]);
5752
+                        if (is_array($fields_label)) {
5753
+                            $notrans = true;
5754
+                            foreach ( $fields_label as $field_toshow ) {
5755
+                                $labeltoshow .= $obj->$field_toshow . ' ';
5756
+                            }
5757
+                        } else {
5758
+                            $labeltoshow = $obj->{$InfoFieldList[1]};
5759
+                        }
5760
+                        $labeltoshow = dol_trunc($labeltoshow, 45);
5761
+
5762
+                        if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5763
+                            foreach ( $fields_label as $field_toshow ) {
5764
+                                $translabel = $langs->trans($obj->$field_toshow);
5765
+                                if ($translabel != $obj->$field_toshow) {
5766
+                                    $labeltoshow = dol_trunc($translabel, 18) . ' ';
5767
+                                } else {
5768
+                                    $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
5769
+                                }
5770
+                            }
5771
+
5772
+                            $data[$obj->rowid]=$labeltoshow;
5773
+                        } else {
5774
+                            if (! $notrans) {
5775
+                                $translabel = $langs->trans($obj->{$InfoFieldList[1]});
5776
+                                if ($translabel != $obj->{$InfoFieldList[1]}) {
5777
+                                    $labeltoshow = dol_trunc($translabel, 18);
5778
+                                } else {
5779
+                                    $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
5780
+                                }
5781
+                            }
5782
+                            if (empty($labeltoshow))
5783
+                                $labeltoshow = '(not defined)';
5784
+
5785
+                                if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5786
+                                    $data[$obj->rowid]=$labeltoshow;
5787
+                                }
5788
+
5789
+                                if (! empty($InfoFieldList[3]) && $parentField) {
5790
+                                    $parent = $parentName . ':' . $obj->{$parentField};
5791
+                                }
5792
+
5793
+                                $data[$obj->rowid]=$labeltoshow;
5794
+                        }
5795
+
5796
+                        $i ++;
5797
+                    }
5798
+                    $this->db->free($resql);
5799
+
5800
+                    $out=$form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
5801
+                } else {
5802
+                    print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
5803
+                }
5804
+            }
5805
+        }
5806
+        elseif ($type == 'link')
5807
+        {
5808
+            $param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
5809
+            $showempty=(($required && $default != '')?0:1);
5810
+            $out=$form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty);
5811
+            if ($conf->global->MAIN_FEATURES_LEVEL >= 2)
5812
+            {
5813
+                        list($class,$classfile)=explode(':',$param_list[0]);
5814
+                        if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) $url_path=dol_buildpath(dirname(dirname($classfile)).'/card.php',1);
5815
+                        else $url_path=dol_buildpath(dirname(dirname($classfile)).'/'.$class.'_card.php',1);
5816
+                        $out.='<a class="butActionNew" href="'.$url_path.'?action=create&backtopage='.$_SERVER['PHP_SELF'].'"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5817
+                        // TODO Add Javascript code to add input fields contents to new elements urls
5818
+            }
5819
+        }
5820
+        elseif ($type == 'password')
5821
+        {
5822
+            // If prefix is 'search_', field is used as a filter, we use a common text field.
5823
+            $out='<input type="'.($keyprefix=='search_'?'text':'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
5824
+        }
5825
+        elseif ($type == 'array')
5826
+        {
5827
+            $newval = $val;
5828
+            $newval['type'] = 'varchar(256)';
5829
+
5830
+            $out='';
5831
+
5832
+            $inputs = array();
5833
+            if(! empty($value)) {
5834
+                foreach($value as $option) {
5835
+                    $out.= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5836
+                    $out.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $showsize).'<br></span>';
5837
+                }
5838
+            }
5839
+
5840
+            $out.= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5841
+
5842
+            $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5843
+            $newInput.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $showsize).'<br></span>';
5844
+
5845
+            if(! empty($conf->use_javascript_ajax)) {
5846
+                $out.= '
5847
+					<script type="text/javascript">
5848
+					$(document).ready(function() {
5849
+						$("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
5850
+							$("'.dol_escape_js($newInput).'").insertBefore(this);
5851
+						});
5852
+
5853
+						$(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
5854
+							$(this).parent().remove();
5855
+						});
5856
+					});
5857
+					</script>';
5858
+            }
5859
+        }
5860
+        if (!empty($hidden)) {
5861
+            $out='<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
5862
+        }
5863
+        /* Add comments
5864
+		 if ($type == 'date') $out.=' (YYYY-MM-DD)';
5865
+		 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
5866
+		 */
5867
+        return $out;
5868
+    }
5869
+
5870
+    /**
5871
+     * Return HTML string to show a field into a page
5872
+     * Code very similar with showOutputField of extra fields
5873
+     *
5874
+     * @param  array   $val		       Array of properties of field to show
5875
+     * @param  string  $key            Key of attribute
5876
+     * @param  string  $value          Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value)
5877
+     * @param  string  $moreparam      To add more parametes on html input tag
5878
+     * @param  string  $keysuffix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
5879
+     * @param  string  $keyprefix      Suffix string to add into name and id of field (can be used to avoid duplicate names)
5880
+     * @param  mixed   $showsize       Value for css to define size. May also be a numeric.
5881
+     * @return string
5882
+     */
5883
+    function showOutputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $showsize=0)
5884
+    {
5885
+        global $conf,$langs,$form;
5886
+
5887
+        if (! is_object($form))
5888
+        {
5889
+            require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5890
+            $form=new Form($this->db);
5891
+        }
5892
+
5893
+        $objectid = $this->id;
5894
+        $label = $val['label'];
5895
+        $type  = $val['type'];
5896
+        $size  = $val['css'];
5897
+
5898
+        // Convert var to be able to share same code than showOutputField of extrafields
5899
+        if (preg_match('/varchar\((\d+)\)/', $type, $reg))
5900
+        {
5901
+            $type = 'varchar';		// convert varchar(xx) int varchar
5902
+            $size = $reg[1];
5903
+        }
5904
+        elseif (preg_match('/varchar/', $type)) $type = 'varchar';		// convert varchar(xx) int varchar
5905
+        if (is_array($val['arrayofkeyval'])) $type='select';
5906
+        if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
5907
+
5908
+        $default=$val['default'];
5909
+        $computed=$val['computed'];
5910
+        $unique=$val['unique'];
5911
+        $required=$val['required'];
5912
+        $param=$val['param'];
5913
+        if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
5914
+        if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
5915
+        {
5916
+            $type='link';
5917
+            $param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
5918
+        }
5919
+        $langfile=$val['langfile'];
5920
+        $list=$val['list'];
5921
+        $help=$val['help'];
5922
+        $hidden=(($val['visible'] == 0) ? 1 : 0);			// If zero, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
5923
+
5924
+        if ($hidden) return '';
5925
+
5926
+        // If field is a computed field, value must become result of compute
5927
+        if ($computed)
5928
+        {
5929
+            // Make the eval of compute string
5930
+            //var_dump($computed);
5931
+            $value = dol_eval($computed, 1, 0);
5932
+        }
5933
+
5934
+        if (empty($showsize))
5935
+        {
5936
+            if ($type == 'date')
5937
+            {
5938
+                //$showsize=10;
5939
+                $showsize = 'minwidth100imp';
5940
+            }
5941
+            elseif ($type == 'datetime')
5942
+            {
5943
+                //$showsize=19;
5944
+                $showsize = 'minwidth200imp';
5945
+            }
5946
+            elseif (in_array($type,array('int','double','price')))
5947
+            {
5948
+                //$showsize=10;
5949
+                $showsize = 'maxwidth75';
5950
+            }
5951
+            elseif ($type == 'url')
5952
+            {
5953
+                $showsize='minwidth400';
5954
+            }
5955
+            elseif ($type == 'boolean')
5956
+            {
5957
+                $showsize='';
5958
+            }
5959
+            else
5960
+            {
5961
+                if (round($size) < 12)
5962
+                {
5963
+                    $showsize = 'minwidth100';
5964
+                }
5965
+                else if (round($size) <= 48)
5966
+                {
5967
+                    $showsize = 'minwidth200';
5968
+                }
5969
+                else
5970
+                {
5971
+                    //$showsize=48;
5972
+                    $showsize = 'minwidth400';
5973
+                }
5974
+            }
5975
+        }
5976
+
5977
+        // Format output value differently according to properties of field
5978
+        if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value=$this->getNomUrl(1, '', 0, '', 1);
5979
+        elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value=$this->getLibStatut(3);
5980
+        elseif ($type == 'date')
5981
+        {
5982
+            if(! empty($value)) {
5983
+                $value=dol_print_date($value,'day');
5984
+            } else {
5985
+                $value='';
5986
+            }
5987
+        }
5988
+        elseif ($type == 'datetime')
5989
+        {
5990
+            if(! empty($value)) {
5991
+                $value=dol_print_date($value,'dayhour');
5992
+            } else {
5993
+                $value='';
5994
+            }
5995
+        }
5996
+        elseif ($type == 'double')
5997
+        {
5998
+            if (!empty($value)) {
5999
+                $value=price($value);
6000
+            }
6001
+        }
6002
+        elseif ($type == 'boolean')
6003
+        {
6004
+            $checked='';
6005
+            if (!empty($value)) {
6006
+                $checked=' checked ';
6007
+            }
6008
+            $value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly disabled>';
6009
+        }
6010
+        elseif ($type == 'mail')
6011
+        {
6012
+            $value=dol_print_email($value,0,0,0,64,1,1);
6013
+        }
6014
+        elseif ($type == 'url')
6015
+        {
6016
+            $value=dol_print_url($value,'_blank',32,1);
6017
+        }
6018
+        elseif ($type == 'phone')
6019
+        {
6020
+            $value=dol_print_phone($value, '', 0, 0, '', '&nbsp;', 1);
6021
+        }
6022
+        elseif ($type == 'price')
6023
+        {
6024
+            $value=price($value,0,$langs,0,0,-1,$conf->currency);
6025
+        }
6026
+        elseif ($type == 'select')
6027
+        {
6028
+            $value=$param['options'][$value];
6029
+        }
6030
+        elseif ($type == 'sellist')
6031
+        {
6032
+            $param_list=array_keys($param['options']);
6033
+            $InfoFieldList = explode(":", $param_list[0]);
6034
+
6035
+            $selectkey="rowid";
6036
+            $keyList='rowid';
6037
+
6038
+            if (count($InfoFieldList)>=3)
6039
+            {
6040
+                $selectkey = $InfoFieldList[2];
6041
+                $keyList=$InfoFieldList[2].' as rowid';
6042
+            }
6043
+
6044
+            $fields_label = explode('|',$InfoFieldList[1]);
6045
+            if(is_array($fields_label)) {
6046
+                $keyList .=', ';
6047
+                $keyList .= implode(', ', $fields_label);
6048
+            }
6049
+
6050
+            $sql = 'SELECT '.$keyList;
6051
+            $sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
6052
+            if (strpos($InfoFieldList[4], 'extra')!==false)
6053
+            {
6054
+                $sql.= ' as main';
6055
+            }
6056
+            if ($selectkey=='rowid' && empty($value)) {
6057
+                $sql.= " WHERE ".$selectkey."=0";
6058
+            } elseif ($selectkey=='rowid') {
6059
+                $sql.= " WHERE ".$selectkey."=".$this->db->escape($value);
6060
+            }else {
6061
+                $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6062
+            }
6063
+
6064
+            //$sql.= ' AND entity = '.$conf->entity;
6065
+
6066
+            dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
6067
+            $resql = $this->db->query($sql);
6068
+            if ($resql)
6069
+            {
6070
+                $value='';	// value was used, so now we reste it to use it to build final output
6071
+
6072
+                $obj = $this->db->fetch_object($resql);
6073
+
6074
+                // Several field into label (eq table:code|libelle:rowid)
6075
+                $fields_label = explode('|',$InfoFieldList[1]);
6076
+
6077
+                if(is_array($fields_label) && count($fields_label)>1)
6078
+                {
6079
+                    foreach ($fields_label as $field_toshow)
6080
+                    {
6081
+                        $translabel='';
6082
+                        if (!empty($obj->$field_toshow)) {
6083
+                            $translabel=$langs->trans($obj->$field_toshow);
6084
+                        }
6085
+                        if ($translabel!=$field_toshow) {
6086
+                            $value.=dol_trunc($translabel,18).' ';
6087
+                        }else {
6088
+                            $value.=$obj->$field_toshow.' ';
6089
+                        }
6090
+                    }
6091
+                }
6092
+                else
6093
+                {
6094
+                    $translabel='';
6095
+                    if (!empty($obj->{$InfoFieldList[1]})) {
6096
+                        $translabel=$langs->trans($obj->{$InfoFieldList[1]});
6097
+                    }
6098
+                    if ($translabel!=$obj->{$InfoFieldList[1]}) {
6099
+                        $value=dol_trunc($translabel,18);
6100
+                    }else {
6101
+                        $value=$obj->{$InfoFieldList[1]};
6102
+                    }
6103
+                }
6104
+            }
6105
+            else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
6106
+        }
6107
+        elseif ($type == 'radio')
6108
+        {
6109
+            $value=$param['options'][$value];
6110
+        }
6111
+        elseif ($type == 'checkbox')
6112
+        {
6113
+            $value_arr=explode(',',$value);
6114
+            $value='';
6115
+            if (is_array($value_arr) && count($value_arr)>0)
6116
+            {
6117
+                foreach ($value_arr as $keyval=>$valueval) {
6118
+                    $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$param['options'][$valueval].'</li>';
6119
+                }
6120
+                $value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6121
+            }
6122
+        }
6123
+        elseif ($type == 'chkbxlst')
6124
+        {
6125
+            $value_arr = explode(',', $value);
6126
+
6127
+            $param_list = array_keys($param['options']);
6128
+            $InfoFieldList = explode(":", $param_list[0]);
6129
+
6130
+            $selectkey = "rowid";
6131
+            $keyList = 'rowid';
6132
+
6133
+            if (count($InfoFieldList) >= 3) {
6134
+                $selectkey = $InfoFieldList[2];
6135
+                $keyList = $InfoFieldList[2] . ' as rowid';
6136
+            }
6137
+
6138
+            $fields_label = explode('|', $InfoFieldList[1]);
6139
+            if (is_array($fields_label)) {
6140
+                $keyList .= ', ';
6141
+                $keyList .= implode(', ', $fields_label);
6142
+            }
6143
+
6144
+            $sql = 'SELECT ' . $keyList;
6145
+            $sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
6146
+            if (strpos($InfoFieldList[4], 'extra') !== false) {
6147
+                $sql .= ' as main';
6148
+            }
6149
+            // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6150
+            // $sql.= ' AND entity = '.$conf->entity;
6151
+
6152
+            dol_syslog(get_class($this) . ':showOutputField:$type=chkbxlst',LOG_DEBUG);
6153
+            $resql = $this->db->query($sql);
6154
+            if ($resql) {
6155
+                $value = ''; // value was used, so now we reste it to use it to build final output
6156
+                $toprint=array();
6157
+                while ( $obj = $this->db->fetch_object($resql) ) {
6158
+
6159
+                    // Several field into label (eq table:code|libelle:rowid)
6160
+                    $fields_label = explode('|', $InfoFieldList[1]);
6161
+                    if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6162
+                        if (is_array($fields_label) && count($fields_label) > 1) {
6163
+                            foreach ( $fields_label as $field_toshow ) {
6164
+                                $translabel = '';
6165
+                                if (! empty($obj->$field_toshow)) {
6166
+                                    $translabel = $langs->trans($obj->$field_toshow);
6167
+                                }
6168
+                                if ($translabel != $field_toshow) {
6169
+                                    $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6170
+                                } else {
6171
+                                    $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->$field_toshow.'</li>';
6172
+                                }
6173
+                            }
6174
+                        } else {
6175
+                            $translabel = '';
6176
+                            if (! empty($obj->{$InfoFieldList[1]})) {
6177
+                                $translabel = $langs->trans($obj->{$InfoFieldList[1]});
6178
+                            }
6179
+                            if ($translabel != $obj->{$InfoFieldList[1]}) {
6180
+                                $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6181
+                            } else {
6182
+                                $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->{$InfoFieldList[1]}.'</li>';
6183
+                            }
6184
+                        }
6185
+                    }
6186
+                }
6187
+                $value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6188
+            } else {
6189
+                dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
6190
+            }
6191
+        }
6192
+        elseif ($type == 'link')
6193
+        {
6194
+            $out='';
6195
+
6196
+            // only if something to display (perf)
6197
+            if ($value)
6198
+            {
6199
+                $param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
6200
+
6201
+                $InfoFieldList = explode(":", $param_list[0]);
6202
+                $classname=$InfoFieldList[0];
6203
+                $classpath=$InfoFieldList[1];
6204
+                $getnomurlparam=(empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
6205
+                if (! empty($classpath))
6206
+                {
6207
+                    dol_include_once($InfoFieldList[1]);
6208
+                    if ($classname && class_exists($classname))
6209
+                    {
6210
+                        $object = new $classname($this->db);
6211
+                        $object->fetch($value);
6212
+                        $value=$object->getNomUrl($getnomurlparam);
6213
+                    }
6214
+                }
6215
+                else
6216
+                {
6217
+                    dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6218
+                    return 'Error bad setup of extrafield';
6219
+                }
6220
+            }
6221
+            else $value='';
6222
+        }
6223
+        elseif ($type == 'text' || $type == 'html')
6224
+        {
6225
+            $value=dol_htmlentitiesbr($value);
6226
+        }
6227
+        elseif ($type == 'password')
6228
+        {
6229
+            $value=preg_replace('/./i','*',$value);
6230
+        }
6231
+        elseif ($type == 'array')
6232
+        {
6233
+            $value = implode('<br>', $value);
6234
+        }
6235
+
6236
+        //print $type.'-'.$size;
6237
+        $out=$value;
6238
+
6239
+        return $out;
6240
+    }
6241
+
6242
+
6243
+    /**
6244
+     * Function to show lines of extrafields with output datas
6245
+     *
6246
+     * @param 	Extrafields $extrafields    Extrafield Object
6247
+     * @param 	string      $mode           Show output (view) or input (edit) for extrafield
6248
+     * @param 	array       $params         Optional parameters. Example: array('style'=>'class="oddeven"', 'colspan'=>$colspan)
6249
+     * @param 	string      $keysuffix      Suffix string to add after name and id of field (can be used to avoid duplicate names)
6250
+     * @param 	string      $keyprefix      Prefix string to add before name and id of field (can be used to avoid duplicate names)
6251
+     * @param	string		$onetrtd		All fields in same tr td
6252
+     * @return 	string
6253
+     */
6254
+    function showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='', $onetrtd=0)
6255
+    {
6256
+        global $db, $conf, $langs, $action, $form;
6257
+
6258
+        if (! is_object($form)) $form=new Form($db);
6259
+
6260
+        $out = '';
6261
+
6262
+        if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0)
6263
+        {
6264
+            $out .= "\n";
6265
+            $out .= '<!-- showOptionalsInput --> ';
6266
+            $out .= "\n";
6267
+
6268
+            $e = 0;
6269
+            foreach($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
6270
+            {
6271
+                // Show only the key field in params
6272
+                if (is_array($params) && array_key_exists('onlykey',$params) && $key != $params['onlykey']) continue;
6273
+
6274
+                $enabled = 1;
6275
+                if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key]))
6276
+                {
6277
+                    $enabled = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1);
6278
+                }
6279
+
6280
+                $perms = 1;
6281
+                if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key]))
6282
+                {
6283
+                    $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1);
6284
+                }
6285
+
6286
+                if (($mode == 'create' || $mode == 'edit') && abs($enabled) != 1 && abs($enabled) != 3) continue;	// <> -1 and <> 1 and <> 3 = not visible on forms, only on list
6287
+                if (empty($perms)) continue;
6288
+
6289
+                // Load language if required
6290
+                if (! empty($extrafields->attributes[$this->table_element]['langfile'][$key])) $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
6291
+
6292
+                $colspan='3';
6293
+                if (is_array($params) && count($params)>0) {
6294
+                    if (array_key_exists('colspan',$params)) {
6295
+                        $colspan=$params['colspan'];
6296
+                    }
6297
+                }
6298
+
6299
+                switch($mode) {
6300
+                    case "view":
6301
+                        $value=$this->array_options["options_".$key.$keysuffix];
6302
+                        break;
6303
+                    case "edit":
6304
+                        $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, 'none');				// GETPOST can get value from GET, POST or setup of default values.
6305
+                        // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
6306
+                        if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
6307
+                        {
6308
+                            if (is_array($getposttemp)) {
6309
+                                // $getposttemp is an array but following code expects a comma separated string
6310
+                                $value = implode(",", $getposttemp);
6311
+                            } else {
6312
+                                $value = $getposttemp;
6313
+                            }
6314
+                        } else {
6315
+                            $value = $this->array_options["options_" . $key];			// No GET, no POST, no default value, so we take value of object.
6316
+                        }
6317
+                        //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
6318
+                        break;
6319
+                }
6320
+
6321
+                if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate')
6322
+                {
6323
+                    $out .= $extrafields->showSeparator($key, $this);
6324
+                }
6325
+                else
6326
+                {
6327
+                    $csstyle='';
6328
+                    $class=(!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
6329
+                    if (is_array($params) && count($params)>0) {
6330
+                        if (array_key_exists('style',$params)) {
6331
+                            $csstyle=$params['style'];
6332
+                        }
6333
+                    }
6334
+
6335
+                    // add html5 elements
6336
+                    $domData  = ' data-element="extrafield"';
6337
+                    $domData .= ' data-targetelement="'.$this->element.'"';
6338
+                    $domData .= ' data-targetid="'.$this->id.'"';
6339
+
6340
+                    $html_id = !empty($this->id) ? 'extrarow-'.$this->element.'_'.$key.'_'.$this->id : '';
6341
+
6342
+                    $out .= '<tr id="'.$html_id.'" '.$csstyle.' class="'.$class.$this->element.'_extras_'.$key.'" '.$domData.' >';
6343
+
6344
+                    if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0)
6345
+                    {
6346
+                        if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan='0'; }
6347
+                    }
6348
+
6349
+                    if ($action == 'selectlines') { $colspan++; }
6350
+
6351
+                    // Convert date into timestamp format (value in memory must be a timestamp)
6352
+                    if (in_array($extrafields->attributes[$this->table_element]['type'][$key],array('date','datetime')))
6353
+                    {
6354
+                        $datenotinstring = $this->array_options['options_' . $key];
6355
+                        if (! is_numeric($this->array_options['options_' . $key]))	// For backward compatibility
6356
+                        {
6357
+                            $datenotinstring = $this->db->jdate($datenotinstring);
6358
+                        }
6359
+                        $value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?dol_mktime(GETPOST($keyprefix.'options_'.$key.$keysuffix."hour", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."min",'int',3), 0, GETPOST($keyprefix.'options_'.$key.$keysuffix."month",'int',3), GETPOST($keyprefix.'options_'.$key.$keysuffix."day",'int',3), GETPOST($keyprefix.'options_'.$key.$keysuffix."year",'int',3)):$datenotinstring;
6360
+                    }
6361
+                    // Convert float submited string into real php numeric (value in memory must be a php numeric)
6362
+                    if (in_array($extrafields->attributes[$this->table_element]['type'][$key],array('price','double')))
6363
+                    {
6364
+                        $value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?price2num(GETPOST($keyprefix.'options_'.$key.$keysuffix, 'alpha', 3)):$this->array_options['options_'.$key];
6365
+                    }
6366
+
6367
+                    $labeltoshow = $langs->trans($label);
6368
+
6369
+                    $out .= '<td class="titlefield';
6370
+                    if (GETPOST('action','none') == 'create') $out.='create';
6371
+                    if ($mode != 'view' && ! empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired';
6372
+                    $out .= '">';
6373
+                    if (! empty($extrafields->attributes[$object->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $extrafields->attributes[$object->table_element]['help'][$key]);
6374
+                    else $out .= $labeltoshow;
6375
+                    $out .= '</td>';
6376
+
6377
+                    $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
6378
+                    $out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'" '.($colspan?' colspan="'.$colspan.'"':'').'>';
6379
+
6380
+                    switch($mode) {
6381
+                        case "view":
6382
+                            $out .= $extrafields->showOutputField($key, $value);
6383
+                            break;
6384
+                        case "edit":
6385
+                            $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id);
6386
+                            break;
6387
+                    }
6388
+
6389
+                    $out .= '</td>';
6390
+
6391
+                    if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
6392
+                    else $out .= '</tr>';
6393
+                    $e++;
6394
+                }
6395
+            }
6396
+            $out .= "\n";
6397
+            // Add code to manage list depending on others
6398
+            if (! empty($conf->use_javascript_ajax)) {
6399
+                $out .= '
6400
+				<script type="text/javascript">
6401
+				    jQuery(document).ready(function() {
6402
+				    	function showOptions(child_list, parent_list)
6403
+				    	{
6404
+				    		var val = $("select[name=\"options_"+parent_list+"\"]").val();
6405
+				    		var parentVal = parent_list + ":" + val;
6406
+							if(val > 0) {
6407
+					    		$("select[name=\""+child_list+"\"] option[parent]").hide();
6408
+					    		$("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
6409
+							} else {
6410
+								$("select[name=\""+child_list+"\"] option").show();
5603 6411
 							}
5604
-							$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
6412
+				    	}
6413
+						function setListDependencies() {
6414
+					    	jQuery("select option[parent]").parent().each(function() {
6415
+					    		var child_list = $(this).attr("name");
6416
+								var parent = $(this).find("option[parent]:first").attr("parent");
6417
+								var infos = parent.split(":");
6418
+								var parent_list = infos[0];
6419
+								$("select[name=\""+parent_list+"\"]").change(function() {
6420
+									showOptions(child_list, parent_list);
6421
+								});
6422
+					    	});
5605 6423
 						}
5606
-						else
5607
-						{
5608
-							if (! $notrans)
5609
-							{
5610
-								$translabel=$langs->trans($obj->{$InfoFieldList[1]});
5611
-								if ($translabel!=$obj->{$InfoFieldList[1]}) {
5612
-									$labeltoshow=dol_trunc($translabel,18);
5613
-								}
5614
-								else {
5615
-									$labeltoshow=dol_trunc($obj->{$InfoFieldList[1]},18);
5616
-								}
5617
-							}
5618
-							if (empty($labeltoshow)) $labeltoshow='(not defined)';
5619
-							if ($value==$obj->rowid)
5620
-							{
5621
-								$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5622
-							}
5623 6424
 
5624
-							if (!empty($InfoFieldList[3]) && $parentField)
5625
-							{
5626
-								$parent = $parentName.':'.$obj->{$parentField};
5627
-							}
6425
+						setListDependencies();
6426
+				    });
6427
+				</script>'."\n";
6428
+                $out .= '<!-- /showOptionalsInput --> '."\n";
6429
+            }
6430
+        }
6431
+        return $out;
6432
+    }
5628 6433
 
5629
-							$out.='<option value="'.$obj->rowid.'"';
5630
-							$out.= ($value==$obj->rowid?' selected':'');
5631
-							$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5632
-							$out.='>'.$labeltoshow.'</option>';
5633
-						}
5634 6434
 
5635
-						$i++;
5636
-					}
5637
-					$this->db->free($resql);
5638
-				}
5639
-				else {
5640
-					print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
5641
-				}
5642
-			}
5643
-			$out.='</select>';
5644
-		}
5645
-		elseif ($type == 'checkbox')
5646
-		{
5647
-			$value_arr=explode(',',$value);
5648
-			$out=$form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options'])?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
5649
-		}
5650
-		elseif ($type == 'radio')
5651
-		{
5652
-			$out='';
5653
-			foreach ($param['options'] as $keyopt => $val)
5654
-			{
5655
-				$out.='<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'');
5656
-				$out.=' value="'.$keyopt.'"';
5657
-				$out.=' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
5658
-				$out.= ($value==$keyopt?'checked':'');
5659
-				$out.='/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
5660
-			}
5661
-		}
5662
-		elseif ($type == 'chkbxlst')
5663
-		{
5664
-			if (is_array($value)) {
5665
-				$value_arr = $value;
5666
-			}
5667
-			else {
5668
-				$value_arr = explode(',', $value);
5669
-			}
5670
-
5671
-			if (is_array($param['options'])) {
5672
-				$param_list = array_keys($param['options']);
5673
-				$InfoFieldList = explode(":", $param_list[0]);
5674
-				$parentName='';
5675
-				$parentField='';
5676
-				// 0 : tableName
5677
-				// 1 : label field name
5678
-				// 2 : key fields name (if differ of rowid)
5679
-				// 3 : key field parent (for dependent lists)
5680
-				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5681
-				$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
5682
-
5683
-				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3])) {
5684
-					list ( $parentName, $parentField ) = explode('|', $InfoFieldList[3]);
5685
-					$keyList .= ', ' . $parentField;
5686
-				}
5687
-				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4])) {
5688
-					if (strpos($InfoFieldList[4], 'extra.') !== false) {
5689
-						$keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
5690
-					} else {
5691
-						$keyList = $InfoFieldList[2] . ' as rowid';
5692
-					}
5693
-				}
6435
+    /**
6436
+     * Returns the rights used for this class
6437
+     * @return stdClass
6438
+     */
6439
+    public function getRights()
6440
+    {
6441
+        global $user;
5694 6442
 
5695
-				$fields_label = explode('|', $InfoFieldList[1]);
5696
-				if (is_array($fields_label)) {
5697
-					$keyList .= ', ';
5698
-					$keyList .= implode(', ', $fields_label);
5699
-				}
6443
+        $element = $this->element;
6444
+        if ($element == 'facturerec') $element='facture';
5700 6445
 
5701
-				$sqlwhere = '';
5702
-				$sql = 'SELECT ' . $keyList;
5703
-				$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5704
-				if (! empty($InfoFieldList[4])) {
5705
-
5706
-					// can use SELECT request
5707
-					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5708
-						$InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
5709
-					}
5710
-
5711
-					// current object id can be use into filter
5712
-					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5713
-						$InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
5714
-					} else {
5715
-						$InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
5716
-					}
5717
-
5718
-					// We have to join on extrafield table
5719
-					if (strpos($InfoFieldList[4], 'extra') !== false) {
5720
-						$sql .= ' as main, ' . MAIN_DB_PREFIX . $InfoFieldList[0] . '_extrafields as extra';
5721
-						$sqlwhere .= ' WHERE extra.fk_object=main.' . $InfoFieldList[2] . ' AND ' . $InfoFieldList[4];
5722
-					} else {
5723
-						$sqlwhere .= ' WHERE ' . $InfoFieldList[4];
5724
-					}
5725
-				} else {
5726
-					$sqlwhere .= ' WHERE 1=1';
5727
-				}
5728
-				// Some tables may have field, some other not. For the moment we disable it.
5729
-				if (in_array($InfoFieldList[0], array ('tablewithentity')))
5730
-				{
5731
-					$sqlwhere .= ' AND entity = ' . $conf->entity;
5732
-				}
5733
-				// $sql.=preg_replace('/^ AND /','',$sqlwhere);
5734
-				// print $sql;
5735
-
5736
-				$sql .= $sqlwhere;
5737
-				dol_syslog(get_class($this) . '::showInputField type=chkbxlst',LOG_DEBUG);
5738
-				$resql = $this->db->query($sql);
5739
-				if ($resql) {
5740
-					$num = $this->db->num_rows($resql);
5741
-					$i = 0;
5742
-
5743
-					$data=array();
5744
-
5745
-					while ( $i < $num ) {
5746
-						$labeltoshow = '';
5747
-						$obj = $this->db->fetch_object($resql);
5748
-
5749
-						$notrans = false;
5750
-						// Several field into label (eq table:code|libelle:rowid)
5751
-						$fields_label = explode('|', $InfoFieldList[1]);
5752
-						if (is_array($fields_label)) {
5753
-							$notrans = true;
5754
-							foreach ( $fields_label as $field_toshow ) {
5755
-								$labeltoshow .= $obj->$field_toshow . ' ';
5756
-							}
5757
-						} else {
5758
-							$labeltoshow = $obj->{$InfoFieldList[1]};
5759
-						}
5760
-						$labeltoshow = dol_trunc($labeltoshow, 45);
5761
-
5762
-						if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5763
-							foreach ( $fields_label as $field_toshow ) {
5764
-								$translabel = $langs->trans($obj->$field_toshow);
5765
-								if ($translabel != $obj->$field_toshow) {
5766
-									$labeltoshow = dol_trunc($translabel, 18) . ' ';
5767
-								} else {
5768
-									$labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
5769
-								}
5770
-							}
6446
+        return $user->rights->{$element};
6447
+    }
5771 6448
 
5772
-							$data[$obj->rowid]=$labeltoshow;
5773
-						} else {
5774
-							if (! $notrans) {
5775
-								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
5776
-								if ($translabel != $obj->{$InfoFieldList[1]}) {
5777
-									$labeltoshow = dol_trunc($translabel, 18);
5778
-								} else {
5779
-									$labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
5780
-								}
5781
-							}
5782
-							if (empty($labeltoshow))
5783
-								$labeltoshow = '(not defined)';
6449
+    /**
6450
+     * Function used to replace a thirdparty id with another one.
6451
+     * This function is meant to be called from replaceThirdparty with the appropiate tables
6452
+     * Column name fk_soc MUST be used to identify thirdparties
6453
+     *
6454
+     * @param  DoliDB 	   $db 			  Database handler
6455
+     * @param  int 		   $origin_id     Old thirdparty id (the thirdparty to delete)
6456
+     * @param  int 		   $dest_id       New thirdparty id (the thirdparty that will received element of the other)
6457
+     * @param  string[]    $tables        Tables that need to be changed
6458
+     * @param  int         $ignoreerrors  Ignore errors. Return true even if errors. We need this when replacement can fails like for categories (categorie of old thirdparty may already exists on new one)
6459
+     * @return bool						  True if success, False if error
6460
+     */
6461
+    public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
6462
+    {
6463
+        foreach ($tables as $table)
6464
+        {
6465
+            $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
5784 6466
 
5785
-								if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5786
-									$data[$obj->rowid]=$labeltoshow;
5787
-								}
6467
+            if (! $db->query($sql))
6468
+            {
6469
+                if ($ignoreerrors) return true;		// TODO Not enough. If there is A-B on kept thirdarty and B-C on old one, we must get A-B-C after merge. Not A-B.
6470
+                //$this->errors = $db->lasterror();
6471
+                return false;
6472
+            }
6473
+        }
5788 6474
 
5789
-								if (! empty($InfoFieldList[3]) && $parentField) {
5790
-									$parent = $parentName . ':' . $obj->{$parentField};
5791
-								}
6475
+        return true;
6476
+    }
5792 6477
 
5793
-								$data[$obj->rowid]=$labeltoshow;
5794
-						}
6478
+    /**
6479
+     * Get buy price to use for margin calculation. This function is called when buy price is unknown.
6480
+     *	 Set buy price = sell price if ForceBuyingPriceIfNull configured,
6481
+     *   else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
6482
+     *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
6483
+     *	 else set min buy price as buy price
6484
+     *
6485
+     * @param float		$unitPrice		 Product unit price
6486
+     * @param float		$discountPercent Line discount percent
6487
+     * @param int		$fk_product		 Product id
6488
+     * @return	float                    <0 if KO, buyprice if OK
6489
+     */
6490
+    public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
6491
+    {
6492
+        global $conf;
5795 6493
 
5796
-						$i ++;
5797
-					}
5798
-					$this->db->free($resql);
6494
+        $buyPrice = 0;
5799 6495
 
5800
-					$out=$form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
5801
-				} else {
5802
-					print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
5803
-				}
5804
-			}
5805
-		}
5806
-		elseif ($type == 'link')
5807
-		{
5808
-			$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
5809
-			$showempty=(($required && $default != '')?0:1);
5810
-			$out=$form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty);
5811
-			if ($conf->global->MAIN_FEATURES_LEVEL >= 2)
5812
-			{
5813
-            			list($class,$classfile)=explode(':',$param_list[0]);
5814
-            			if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) $url_path=dol_buildpath(dirname(dirname($classfile)).'/card.php',1);
5815
-            			else $url_path=dol_buildpath(dirname(dirname($classfile)).'/'.$class.'_card.php',1);
5816
-            			$out.='<a class="butActionNew" href="'.$url_path.'?action=create&backtopage='.$_SERVER['PHP_SELF'].'"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5817
-            			// TODO Add Javascript code to add input fields contents to new elements urls
5818
-			}
5819
-		}
5820
-		elseif ($type == 'password')
5821
-		{
5822
-			// If prefix is 'search_', field is used as a filter, we use a common text field.
5823
-			$out='<input type="'.($keyprefix=='search_'?'text':'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
5824
-		}
5825
-		elseif ($type == 'array')
5826
-		{
5827
-			$newval = $val;
5828
-			$newval['type'] = 'varchar(256)';
5829
-
5830
-			$out='';
5831
-
5832
-			$inputs = array();
5833
-			if(! empty($value)) {
5834
-				foreach($value as $option) {
5835
-					$out.= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5836
-					$out.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $showsize).'<br></span>';
5837
-				}
5838
-			}
6496
+        if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
6497
+        {
6498
+            $buyPrice = $unitPrice * (1 - $discountPercent / 100);
6499
+        }
6500
+        else
6501
+        {
6502
+            // Get cost price for margin calculation
6503
+            if (! empty($fk_product))
6504
+            {
6505
+                if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
6506
+                {
6507
+                    require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6508
+                    $product = new Product($this->db);
6509
+                    $result = $product->fetch($fk_product);
6510
+                    if ($result <= 0)
6511
+                    {
6512
+                        $this->errors[] = 'ErrorProductIdDoesNotExists';
6513
+                        return -1;
6514
+                    }
6515
+                    if ($product->cost_price > 0)
6516
+                    {
6517
+                        $buyPrice = $product->cost_price;
6518
+                    }
6519
+                    else if ($product->pmp > 0)
6520
+                    {
6521
+                        $buyPrice = $product->pmp;
6522
+                    }
6523
+                }
6524
+                else if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
6525
+                {
6526
+                    require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6527
+                    $product = new Product($this->db);
6528
+                    $result = $product->fetch($fk_product);
6529
+                    if ($result <= 0)
6530
+                    {
6531
+                        $this->errors[] = 'ErrorProductIdDoesNotExists';
6532
+                        return -1;
6533
+                    }
6534
+                    if ($product->pmp > 0)
6535
+                    {
6536
+                        $buyPrice = $product->pmp;
6537
+                    }
6538
+                }
6539
+
6540
+                if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
6541
+                {
6542
+                    require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6543
+                    $productFournisseur = new ProductFournisseur($this->db);
6544
+                    if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
6545
+                    {
6546
+                        $buyPrice = $productFournisseur->fourn_unitprice;
6547
+                    }
6548
+                    else if ($result < 0)
6549
+                    {
6550
+                        $this->errors[] = $productFournisseur->error;
6551
+                        return -2;
6552
+                    }
6553
+                }
6554
+            }
6555
+        }
6556
+        return $buyPrice;
6557
+    }
5839 6558
 
5840
-			$out.= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
6559
+    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
6560
+    /**
6561
+     *  Show photos of an object (nbmax maximum), into several columns
6562
+     *
6563
+     *  @param		string	$modulepart		'product', 'ticket', ...
6564
+     *  @param      string	$sdir        	Directory to scan (full absolute path)
6565
+     *  @param      int		$size        	0=original size, 1='small' use thumbnail if possible
6566
+     *  @param      int		$nbmax       	Nombre maximum de photos (0=pas de max)
6567
+     *  @param      int		$nbbyrow     	Number of image per line or -1 to use div. Used only if size=1.
6568
+     * 	@param		int		$showfilename	1=Show filename
6569
+     * 	@param		int		$showaction		1=Show icon with action links (resize, delete)
6570
+     * 	@param		int		$maxHeight		Max height of original image when size='small' (so we can use original even if small requested). If 0, always use 'small' thumb image.
6571
+     * 	@param		int		$maxWidth		Max width of original image when size='small'
6572
+     *  @param      int     $nolink         Do not add a href link to view enlarged imaged into a new tab
6573
+     *  @param      int     $notitle        Do not add title tag on image
6574
+     *  @param		int		$usesharelink	Use the public shared link of image (if not available, the 'nophoto' image will be shown instead)
6575
+     *  @return     string					Html code to show photo. Number of photos shown is saved in this->nbphoto
6576
+     */
6577
+    function show_photos($modulepart, $sdir, $size=0, $nbmax=0, $nbbyrow=5, $showfilename=0, $showaction=0, $maxHeight=120, $maxWidth=160, $nolink=0, $notitle=0, $usesharelink=0)
6578
+    {
6579
+        // phpcs:enable
6580
+        global $conf,$user,$langs;
5841 6581
 
5842
-			$newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5843
-			$newInput.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $showsize).'<br></span>';
6582
+        include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
6583
+        include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
5844 6584
 
5845
-			if(! empty($conf->use_javascript_ajax)) {
5846
-				$out.= '
5847
-					<script type="text/javascript">
5848
-					$(document).ready(function() {
5849
-						$("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
5850
-							$("'.dol_escape_js($newInput).'").insertBefore(this);
5851
-						});
6585
+        $sortfield='position_name';
6586
+        $sortorder='asc';
5852 6587
 
5853
-						$(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
5854
-							$(this).parent().remove();
5855
-						});
5856
-					});
5857
-					</script>';
5858
-			}
5859
-		}
5860
-		if (!empty($hidden)) {
5861
-			$out='<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
5862
-		}
5863
-		/* Add comments
5864
-		 if ($type == 'date') $out.=' (YYYY-MM-DD)';
5865
-		 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
5866
-		 */
5867
-		return $out;
5868
-	}
5869
-
5870
-	/**
5871
-	 * Return HTML string to show a field into a page
5872
-	 * Code very similar with showOutputField of extra fields
5873
-	 *
5874
-	 * @param  array   $val		       Array of properties of field to show
5875
-	 * @param  string  $key            Key of attribute
5876
-	 * @param  string  $value          Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value)
5877
-	 * @param  string  $moreparam      To add more parametes on html input tag
5878
-	 * @param  string  $keysuffix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
5879
-	 * @param  string  $keyprefix      Suffix string to add into name and id of field (can be used to avoid duplicate names)
5880
-	 * @param  mixed   $showsize       Value for css to define size. May also be a numeric.
5881
-	 * @return string
5882
-	 */
5883
-	function showOutputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $showsize=0)
5884
-	{
5885
-		global $conf,$langs,$form;
5886
-
5887
-		if (! is_object($form))
5888
-		{
5889
-			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5890
-			$form=new Form($this->db);
5891
-		}
5892
-
5893
-		$objectid = $this->id;
5894
-		$label = $val['label'];
5895
-		$type  = $val['type'];
5896
-		$size  = $val['css'];
5897
-
5898
-		// Convert var to be able to share same code than showOutputField of extrafields
5899
-		if (preg_match('/varchar\((\d+)\)/', $type, $reg))
5900
-		{
5901
-			$type = 'varchar';		// convert varchar(xx) int varchar
5902
-			$size = $reg[1];
5903
-		}
5904
-		elseif (preg_match('/varchar/', $type)) $type = 'varchar';		// convert varchar(xx) int varchar
5905
-		if (is_array($val['arrayofkeyval'])) $type='select';
5906
-		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
5907
-
5908
-		$default=$val['default'];
5909
-		$computed=$val['computed'];
5910
-		$unique=$val['unique'];
5911
-		$required=$val['required'];
5912
-		$param=$val['param'];
5913
-		if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
5914
-		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
5915
-		{
5916
-			$type='link';
5917
-			$param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
5918
-		}
5919
-		$langfile=$val['langfile'];
5920
-		$list=$val['list'];
5921
-		$help=$val['help'];
5922
-		$hidden=(($val['visible'] == 0) ? 1 : 0);			// If zero, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
5923
-
5924
-		if ($hidden) return '';
5925
-
5926
-		// If field is a computed field, value must become result of compute
5927
-		if ($computed)
5928
-		{
5929
-			// Make the eval of compute string
5930
-			//var_dump($computed);
5931
-			$value = dol_eval($computed, 1, 0);
5932
-		}
5933
-
5934
-		if (empty($showsize))
5935
-		{
5936
-			if ($type == 'date')
5937
-			{
5938
-				//$showsize=10;
5939
-				$showsize = 'minwidth100imp';
5940
-			}
5941
-			elseif ($type == 'datetime')
5942
-			{
5943
-				//$showsize=19;
5944
-				$showsize = 'minwidth200imp';
5945
-			}
5946
-			elseif (in_array($type,array('int','double','price')))
5947
-			{
5948
-				//$showsize=10;
5949
-				$showsize = 'maxwidth75';
5950
-			}
5951
-			elseif ($type == 'url')
5952
-			{
5953
-				$showsize='minwidth400';
5954
-			}
5955
-			elseif ($type == 'boolean')
5956
-			{
5957
-				$showsize='';
5958
-			}
5959
-			else
5960
-			{
5961
-				if (round($size) < 12)
5962
-				{
5963
-					$showsize = 'minwidth100';
5964
-				}
5965
-				else if (round($size) <= 48)
5966
-				{
5967
-					$showsize = 'minwidth200';
5968
-				}
5969
-				else
5970
-				{
5971
-					//$showsize=48;
5972
-					$showsize = 'minwidth400';
5973
-				}
5974
-			}
5975
-		}
5976
-
5977
-		// Format output value differently according to properties of field
5978
-		if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value=$this->getNomUrl(1, '', 0, '', 1);
5979
-		elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value=$this->getLibStatut(3);
5980
-		elseif ($type == 'date')
5981
-		{
5982
-			if(! empty($value)) {
5983
-				$value=dol_print_date($value,'day');
5984
-			} else {
5985
-				$value='';
5986
-			}
5987
-		}
5988
-		elseif ($type == 'datetime')
5989
-		{
5990
-			if(! empty($value)) {
5991
-				$value=dol_print_date($value,'dayhour');
5992
-			} else {
5993
-				$value='';
5994
-			}
5995
-		}
5996
-		elseif ($type == 'double')
5997
-		{
5998
-			if (!empty($value)) {
5999
-				$value=price($value);
6000
-			}
6001
-		}
6002
-		elseif ($type == 'boolean')
6003
-		{
6004
-			$checked='';
6005
-			if (!empty($value)) {
6006
-				$checked=' checked ';
6007
-			}
6008
-			$value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly disabled>';
6009
-		}
6010
-		elseif ($type == 'mail')
6011
-		{
6012
-			$value=dol_print_email($value,0,0,0,64,1,1);
6013
-		}
6014
-		elseif ($type == 'url')
6015
-		{
6016
-			$value=dol_print_url($value,'_blank',32,1);
6017
-		}
6018
-		elseif ($type == 'phone')
6019
-		{
6020
-			$value=dol_print_phone($value, '', 0, 0, '', '&nbsp;', 1);
6021
-		}
6022
-		elseif ($type == 'price')
6023
-		{
6024
-			$value=price($value,0,$langs,0,0,-1,$conf->currency);
6025
-		}
6026
-		elseif ($type == 'select')
6027
-		{
6028
-			$value=$param['options'][$value];
6029
-		}
6030
-		elseif ($type == 'sellist')
6031
-		{
6032
-			$param_list=array_keys($param['options']);
6033
-			$InfoFieldList = explode(":", $param_list[0]);
6034
-
6035
-			$selectkey="rowid";
6036
-			$keyList='rowid';
6037
-
6038
-			if (count($InfoFieldList)>=3)
6039
-			{
6040
-				$selectkey = $InfoFieldList[2];
6041
-				$keyList=$InfoFieldList[2].' as rowid';
6042
-			}
6043
-
6044
-			$fields_label = explode('|',$InfoFieldList[1]);
6045
-			if(is_array($fields_label)) {
6046
-				$keyList .=', ';
6047
-				$keyList .= implode(', ', $fields_label);
6048
-			}
6049
-
6050
-			$sql = 'SELECT '.$keyList;
6051
-			$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
6052
-			if (strpos($InfoFieldList[4], 'extra')!==false)
6053
-			{
6054
-				$sql.= ' as main';
6055
-			}
6056
-			if ($selectkey=='rowid' && empty($value)) {
6057
-				$sql.= " WHERE ".$selectkey."=0";
6058
-			} elseif ($selectkey=='rowid') {
6059
-				$sql.= " WHERE ".$selectkey."=".$this->db->escape($value);
6060
-			}else {
6061
-				$sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6062
-			}
6063
-
6064
-			//$sql.= ' AND entity = '.$conf->entity;
6065
-
6066
-			dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
6067
-			$resql = $this->db->query($sql);
6068
-			if ($resql)
6069
-			{
6070
-				$value='';	// value was used, so now we reste it to use it to build final output
6588
+        $dir = $sdir . '/';
6589
+        $pdir = '/';
6590
+        if ($modulepart == 'ticket')
6591
+        {
6592
+            $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6593
+            $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6594
+        }
6595
+        else
6596
+        {
6597
+            $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6598
+            $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6599
+        }
6600
+
6601
+        // For backward compatibility
6602
+        if ($modulepart == 'product' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))
6603
+        {
6604
+            $dir = $sdir . '/'. get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/";
6605
+            $pdir = '/' . get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/";
6606
+        }
6607
+
6608
+        // Defined relative dir to DOL_DATA_ROOT
6609
+        $relativedir = '';
6610
+        if ($dir)
6611
+        {
6612
+            $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $dir);
6613
+            $relativedir = preg_replace('/^[\\/]/','',$relativedir);
6614
+            $relativedir = preg_replace('/[\\/]$/','',$relativedir);
6615
+        }
6616
+
6617
+        $dirthumb = $dir.'thumbs/';
6618
+        $pdirthumb = $pdir.'thumbs/';
6619
+
6620
+        $return ='<!-- Photo -->'."\n";
6621
+        $nbphoto=0;
6622
+
6623
+        $filearray=dol_dir_list($dir,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6624
+
6625
+        /*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))    // For backward compatiblity, we scan also old dirs
6626
+		 {
6627
+		 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6628
+		 $filearray=array_merge($filearray, $filearrayold);
6629
+		 }*/
6630
+
6631
+        completeFileArrayWithDatabaseInfo($filearray, $relativedir);
6632
+
6633
+        if (count($filearray))
6634
+        {
6635
+            if ($sortfield && $sortorder)
6636
+            {
6637
+                $filearray=dol_sort_array($filearray, $sortfield, $sortorder);
6638
+            }
6639
+
6640
+            foreach($filearray as $key => $val)
6641
+            {
6642
+                $photo='';
6643
+                $file = $val['name'];
6644
+
6645
+                //if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure file is stored in UTF8 in memory
6646
+
6647
+                //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
6648
+                if (image_format_supported($file) >= 0)
6649
+                {
6650
+                    $nbphoto++;
6651
+                    $photo = $file;
6652
+                    $viewfilename = $file;
6653
+
6654
+                    if ($size == 1 || $size == 'small') {   // Format vignette
6655
+
6656
+                        // Find name of thumb file
6657
+                        $photo_vignette=basename(getImageFileNameForSize($dir.$file, '_small'));
6658
+                        if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette='';
6659
+
6660
+                        // Get filesize of original file
6661
+                        $imgarray=dol_getImageSize($dir.$photo);
6662
+
6663
+                        if ($nbbyrow > 0)
6664
+                        {
6665
+                            if ($nbphoto == 1) $return.= '<table width="100%" valign="top" align="center" border="0" cellpadding="2" cellspacing="2">';
6666
+
6667
+                            if ($nbphoto % $nbbyrow == 1) $return.= '<tr align=center valign=middle border=1>';
6668
+                            $return.= '<td width="'.ceil(100/$nbbyrow).'%" class="photo">';
6669
+                        }
6670
+                        else if ($nbbyrow < 0) $return .= '<div class="inline-block">';
6671
+
6672
+                        $return.= "\n";
6673
+
6674
+                        $relativefile=preg_replace('/^\//', '', $pdir.$photo);
6675
+                        if (empty($nolink))
6676
+                        {
6677
+                            $urladvanced=getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
6678
+                            if ($urladvanced) $return.='<a href="'.$urladvanced.'">';
6679
+                            else $return.= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">';
6680
+                        }
6681
+
6682
+                        // Show image (width height=$maxHeight)
6683
+                        // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
6684
+                        $alt=$langs->transnoentitiesnoconv('File').': '.$relativefile;
6685
+                        $alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
6686
+                        if ($notitle) $alt='';
6687
+
6688
+                        if ($usesharelink)
6689
+                        {
6690
+                            if ($val['share'])
6691
+                            {
6692
+                                if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6693
+                                {
6694
+                                    $return.= '<!-- Show original file (thumb not yet available with shared links) -->';
6695
+                                    $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6696
+                                }
6697
+                                else {
6698
+                                    $return.= '<!-- Show original file -->';
6699
+                                    $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6700
+                                }
6701
+                            }
6702
+                            else
6703
+                            {
6704
+                                $return.= '<!-- Show nophoto file (because file is not shared) -->';
6705
+                                $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
6706
+                            }
6707
+                        }
6708
+                        else
6709
+                        {
6710
+                            if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6711
+                            {
6712
+                                $return.= '<!-- Show thumb -->';
6713
+                                $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
6714
+                            }
6715
+                            else {
6716
+                                $return.= '<!-- Show original file -->';
6717
+                                $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
6718
+                            }
6719
+                        }
6720
+
6721
+                        if (empty($nolink)) $return.= '</a>';
6722
+                        $return.="\n";
6723
+
6724
+                        if ($showfilename) $return.= '<br>'.$viewfilename;
6725
+                        if ($showaction)
6726
+                        {
6727
+                            $return.= '<br>';
6728
+                            // On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
6729
+                            if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight))
6730
+                            {
6731
+                                $return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=addthumb&amp;file='.urlencode($pdir.$viewfilename).'">'.img_picto($langs->trans('GenerateThumb'),'refresh').'&nbsp;&nbsp;</a>';
6732
+                            }
6733
+                            // Special cas for product
6734
+                            if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6735
+                            {
6736
+                                // Link to resize
6737
+                                $return.= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
6738
+
6739
+                                // Link to delete
6740
+                                $return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6741
+                                $return.= img_delete().'</a>';
6742
+                            }
6743
+                        }
6744
+                        $return.= "\n";
6745
+
6746
+                        if ($nbbyrow > 0)
6747
+                        {
6748
+                            $return.= '</td>';
6749
+                            if (($nbphoto % $nbbyrow) == 0) $return.= '</tr>';
6750
+                        }
6751
+                        else if ($nbbyrow < 0) $return.='</div>';
6752
+                    }
6753
+
6754
+                    if (empty($size)) {     // Format origine
6755
+                        $return.= '<img class="photo photowithmargin" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
6756
+
6757
+                        if ($showfilename) $return.= '<br>'.$viewfilename;
6758
+                        if ($showaction)
6759
+                        {
6760
+                            // Special case for product
6761
+                            if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6762
+                            {
6763
+                                // Link to resize
6764
+                                $return.= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
6765
+
6766
+                                // Link to delete
6767
+                                $return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6768
+                                $return.= img_delete().'</a>';
6769
+                            }
6770
+                        }
6771
+                    }
6772
+
6773
+                    // On continue ou on arrete de boucler ?
6774
+                    if ($nbmax && $nbphoto >= $nbmax) break;
6775
+                }
6776
+            }
6777
+
6778
+            if ($size==1 || $size=='small')
6779
+            {
6780
+                if ($nbbyrow > 0)
6781
+                {
6782
+                    // Ferme tableau
6783
+                    while ($nbphoto % $nbbyrow)
6784
+                    {
6785
+                        $return.= '<td width="'.ceil(100/$nbbyrow).'%">&nbsp;</td>';
6786
+                        $nbphoto++;
6787
+                    }
6788
+
6789
+                    if ($nbphoto) $return.= '</table>';
6790
+                }
6791
+            }
6792
+        }
6793
+
6794
+        $this->nbphoto = $nbphoto;
6795
+
6796
+        return $return;
6797
+    }
6798
+
6799
+
6800
+    /**
6801
+     * Function test if type is array
6802
+     *
6803
+     * @param   array   $info   content informations of field
6804
+     * @return                  bool
6805
+     */
6806
+    protected function isArray($info)
6807
+    {
6808
+        if(is_array($info))
6809
+        {
6810
+            if(isset($info['type']) && $info['type']=='array') return true;
6811
+            else return false;
6812
+        }
6813
+        else return false;
6814
+    }
6815
+
6816
+    /**
6817
+     * Function test if type is null
6818
+     *
6819
+     * @param   array   $info   content informations of field
6820
+     * @return                  bool
6821
+     */
6822
+    protected function isNull($info)
6823
+    {
6824
+        if(is_array($info))
6825
+        {
6826
+            if(isset($info['type']) && $info['type']=='null') return true;
6827
+            else return false;
6828
+        }
6829
+        else return false;
6830
+    }
6831
+
6832
+    /**
6833
+     * Function test if type is date
6834
+     *
6835
+     * @param   array   $info   content informations of field
6836
+     * @return                  bool
6837
+     */
6838
+    public function isDate($info)
6839
+    {
6840
+        if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
6841
+        else return false;
6842
+    }
6843
+
6844
+    /**
6845
+     * Function test if type is integer
6846
+     *
6847
+     * @param   array   $info   content informations of field
6848
+     * @return                  bool
6849
+     */
6850
+    public function isInt($info)
6851
+    {
6852
+        if(is_array($info))
6853
+        {
6854
+            if(isset($info['type']) && ($info['type']=='int' || preg_match('/^integer/i',$info['type']) ) ) return true;
6855
+            else return false;
6856
+        }
6857
+        else return false;
6858
+    }
6859
+
6860
+    /**
6861
+     * Function test if type is float
6862
+     *
6863
+     * @param   array   $info   content informations of field
6864
+     * @return                  bool
6865
+     */
6866
+    public function isFloat($info)
6867
+    {
6868
+        if(is_array($info))
6869
+        {
6870
+            if (isset($info['type']) && (preg_match('/^(double|real)/i', $info['type']))) return true;
6871
+            else return false;
6872
+        }
6873
+        else return false;
6874
+    }
6875
+
6876
+    /**
6877
+     * Function test if type is text
6878
+     *
6879
+     * @param   array   $info   content informations of field
6880
+     * @return                  bool
6881
+     */
6882
+    public function isText($info)
6883
+    {
6884
+        if(is_array($info))
6885
+        {
6886
+            if(isset($info['type']) && $info['type']=='text') return true;
6887
+            else return false;
6888
+        }
6889
+        else return false;
6890
+    }
6891
+
6892
+    /**
6893
+     * Function test if is indexed
6894
+     *
6895
+     * @param   array   $info   content informations of field
6896
+     * @return                  bool
6897
+     */
6898
+    protected function isIndex($info)
6899
+    {
6900
+        if(is_array($info))
6901
+        {
6902
+            if(isset($info['index']) && $info['index']==true) return true;
6903
+            else return false;
6904
+        }
6905
+        else return false;
6906
+    }
6907
+
6908
+    /**
6909
+     * Function to prepare the values to insert.
6910
+     * Note $this->${field} are set by the page that make the createCommon or the updateCommon.
6911
+     *
6912
+     * @return array
6913
+     */
6914
+    protected function setSaveQuery()
6915
+    {
6916
+        global $conf;
6917
+
6918
+        $queryarray=array();
6919
+        foreach ($this->fields as $field=>$info)	// Loop on definition of fields
6920
+        {
6921
+            // Depending on field type ('datetime', ...)
6922
+            if($this->isDate($info))
6923
+            {
6924
+                if(empty($this->{$field}))
6925
+                {
6926
+                    $queryarray[$field] = null;
6927
+                }
6928
+                else
6929
+                {
6930
+                    $queryarray[$field] = $this->db->idate($this->{$field});
6931
+                }
6932
+            }
6933
+            else if($this->isArray($info))
6934
+            {
6935
+                if(! empty($this->{$field})) {
6936
+                    if(! is_array($this->{$field})) {
6937
+                        $this->{$field} = array($this->{$field});
6938
+                    }
6939
+                    $queryarray[$field] = serialize($this->{$field});
6940
+                } else {
6941
+                    $queryarray[$field] = null;
6942
+                }
6943
+            }
6944
+            else if($this->isInt($info))
6945
+            {
6946
+                if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field]=$conf->entity;
6947
+                else
6948
+                {
6949
+                    $queryarray[$field] = (int) price2num($this->{$field});
6950
+                    if (empty($queryarray[$field])) $queryarray[$field]=0;		// May be reset to null later if property 'notnull' is -1 for this field.
6951
+                }
6952
+            }
6953
+            else if($this->isFloat($info))
6954
+            {
6955
+                $queryarray[$field] = (double) price2num($this->{$field});
6956
+                if (empty($queryarray[$field])) $queryarray[$field]=0;
6957
+            }
6958
+            else
6959
+            {
6960
+                $queryarray[$field] = $this->{$field};
6961
+            }
6962
+
6963
+            if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
6964
+            if (! empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null;
6965
+        }
6966
+
6967
+        return $queryarray;
6968
+    }
6969
+
6970
+    /**
6971
+     * Function to load data from a SQL pointer into properties of current object $this
6972
+     *
6973
+     * @param   stdClass    $obj    Contain data of object from database
6974
+     * @return void
6975
+     */
6976
+    protected function setVarsFromFetchObj(&$obj)
6977
+    {
6978
+        foreach ($this->fields as $field => $info)
6979
+        {
6980
+            if($this->isDate($info))
6981
+            {
6982
+                if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
6983
+                else $this->{$field} = strtotime($obj->{$field});
6984
+            }
6985
+            elseif($this->isArray($info))
6986
+            {
6987
+                if(! empty($obj->{$field})) {
6988
+                    $this->{$field} = @unserialize($obj->{$field});
6989
+                    // Hack for data not in UTF8
6990
+                    if($this->{$field } === false) @unserialize(utf8_decode($obj->{$field}));
6991
+                } else {
6992
+                    $this->{$field} = array();
6993
+                }
6994
+            }
6995
+            elseif($this->isInt($info))
6996
+            {
6997
+                if ($field == 'rowid') $this->id = (int) $obj->{$field};
6998
+                else $this->{$field} = (int) $obj->{$field};
6999
+            }
7000
+            elseif($this->isFloat($info))
7001
+            {
7002
+                $this->{$field} = (double) $obj->{$field};
7003
+            }
7004
+            elseif($this->isNull($info))
7005
+            {
7006
+                $val = $obj->{$field};
7007
+                // zero is not null
7008
+                $this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
7009
+            }
7010
+            else
7011
+            {
7012
+                $this->{$field} = $obj->{$field};
7013
+            }
7014
+        }
7015
+
7016
+        // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
7017
+        if (! isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id;
7018
+    }
7019
+
7020
+    /**
7021
+     * Function to concat keys of fields
7022
+     *
7023
+     * @return string
7024
+     */
7025
+    protected function getFieldList()
7026
+    {
7027
+        $keys = array_keys($this->fields);
7028
+        return implode(',', $keys);
7029
+    }
7030
+
7031
+    /**
7032
+     * Add quote to field value if necessary
7033
+     *
7034
+     * @param 	string|int	$value			Value to protect
7035
+     * @param	array		$fieldsentry	Properties of field
7036
+     * @return 	string
7037
+     */
7038
+    protected function quote($value, $fieldsentry)
7039
+    {
7040
+        if (is_null($value)) return 'NULL';
7041
+        else if (preg_match('/^(int|double|real)/i', $fieldsentry['type'])) return $this->db->escape("$value");
7042
+        else return "'".$this->db->escape($value)."'";
7043
+    }
7044
+
7045
+
7046
+    /**
7047
+     * Create object into database
7048
+     *
7049
+     * @param  User $user      User that creates
7050
+     * @param  bool $notrigger false=launch triggers after, true=disable triggers
7051
+     * @return int             <0 if KO, Id of created object if OK
7052
+     */
7053
+    public function createCommon(User $user, $notrigger = false)
7054
+    {
7055
+        global $langs;
6071 7056
 
6072
-				$obj = $this->db->fetch_object($resql);
7057
+        $error = 0;
6073 7058
 
6074
-				// Several field into label (eq table:code|libelle:rowid)
6075
-				$fields_label = explode('|',$InfoFieldList[1]);
7059
+        $now=dol_now();
6076 7060
 
6077
-				if(is_array($fields_label) && count($fields_label)>1)
6078
-				{
6079
-					foreach ($fields_label as $field_toshow)
6080
-					{
6081
-						$translabel='';
6082
-						if (!empty($obj->$field_toshow)) {
6083
-							$translabel=$langs->trans($obj->$field_toshow);
6084
-						}
6085
-						if ($translabel!=$field_toshow) {
6086
-							$value.=dol_trunc($translabel,18).' ';
6087
-						}else {
6088
-							$value.=$obj->$field_toshow.' ';
6089
-						}
6090
-					}
6091
-				}
6092
-				else
6093
-				{
6094
-					$translabel='';
6095
-					if (!empty($obj->{$InfoFieldList[1]})) {
6096
-						$translabel=$langs->trans($obj->{$InfoFieldList[1]});
6097
-					}
6098
-					if ($translabel!=$obj->{$InfoFieldList[1]}) {
6099
-						$value=dol_trunc($translabel,18);
6100
-					}else {
6101
-						$value=$obj->{$InfoFieldList[1]};
6102
-					}
6103
-				}
6104
-			}
6105
-			else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
6106
-		}
6107
-		elseif ($type == 'radio')
6108
-		{
6109
-			$value=$param['options'][$value];
6110
-		}
6111
-		elseif ($type == 'checkbox')
6112
-		{
6113
-			$value_arr=explode(',',$value);
6114
-			$value='';
6115
-			if (is_array($value_arr) && count($value_arr)>0)
6116
-			{
6117
-				foreach ($value_arr as $keyval=>$valueval) {
6118
-					$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$param['options'][$valueval].'</li>';
6119
-				}
6120
-				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6121
-			}
6122
-		}
6123
-		elseif ($type == 'chkbxlst')
6124
-		{
6125
-			$value_arr = explode(',', $value);
6126
-
6127
-			$param_list = array_keys($param['options']);
6128
-			$InfoFieldList = explode(":", $param_list[0]);
6129
-
6130
-			$selectkey = "rowid";
6131
-			$keyList = 'rowid';
6132
-
6133
-			if (count($InfoFieldList) >= 3) {
6134
-				$selectkey = $InfoFieldList[2];
6135
-				$keyList = $InfoFieldList[2] . ' as rowid';
6136
-			}
6137
-
6138
-			$fields_label = explode('|', $InfoFieldList[1]);
6139
-			if (is_array($fields_label)) {
6140
-				$keyList .= ', ';
6141
-				$keyList .= implode(', ', $fields_label);
6142
-			}
6143
-
6144
-			$sql = 'SELECT ' . $keyList;
6145
-			$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
6146
-			if (strpos($InfoFieldList[4], 'extra') !== false) {
6147
-				$sql .= ' as main';
6148
-			}
6149
-			// $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6150
-			// $sql.= ' AND entity = '.$conf->entity;
6151
-
6152
-			dol_syslog(get_class($this) . ':showOutputField:$type=chkbxlst',LOG_DEBUG);
6153
-			$resql = $this->db->query($sql);
6154
-			if ($resql) {
6155
-				$value = ''; // value was used, so now we reste it to use it to build final output
6156
-				$toprint=array();
6157
-				while ( $obj = $this->db->fetch_object($resql) ) {
6158
-
6159
-					// Several field into label (eq table:code|libelle:rowid)
6160
-					$fields_label = explode('|', $InfoFieldList[1]);
6161
-					if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6162
-						if (is_array($fields_label) && count($fields_label) > 1) {
6163
-							foreach ( $fields_label as $field_toshow ) {
6164
-								$translabel = '';
6165
-								if (! empty($obj->$field_toshow)) {
6166
-									$translabel = $langs->trans($obj->$field_toshow);
6167
-								}
6168
-								if ($translabel != $field_toshow) {
6169
-									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6170
-								} else {
6171
-									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->$field_toshow.'</li>';
6172
-								}
6173
-							}
6174
-						} else {
6175
-							$translabel = '';
6176
-							if (! empty($obj->{$InfoFieldList[1]})) {
6177
-								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
6178
-							}
6179
-							if ($translabel != $obj->{$InfoFieldList[1]}) {
6180
-								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6181
-							} else {
6182
-								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->{$InfoFieldList[1]}.'</li>';
6183
-							}
6184
-						}
6185
-					}
6186
-				}
6187
-				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6188
-			} else {
6189
-				dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
6190
-			}
6191
-		}
6192
-		elseif ($type == 'link')
6193
-		{
6194
-			$out='';
6195
-
6196
-			// only if something to display (perf)
6197
-			if ($value)
6198
-			{
6199
-				$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
7061
+        $fieldvalues = $this->setSaveQuery();
7062
+        if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now);
7063
+        if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id;
7064
+        unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
6200 7065
 
6201
-				$InfoFieldList = explode(":", $param_list[0]);
6202
-				$classname=$InfoFieldList[0];
6203
-				$classpath=$InfoFieldList[1];
6204
-				$getnomurlparam=(empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
6205
-				if (! empty($classpath))
6206
-				{
6207
-					dol_include_once($InfoFieldList[1]);
6208
-					if ($classname && class_exists($classname))
6209
-					{
6210
-						$object = new $classname($this->db);
6211
-						$object->fetch($value);
6212
-						$value=$object->getNomUrl($getnomurlparam);
6213
-					}
6214
-				}
6215
-				else
6216
-				{
6217
-					dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6218
-					return 'Error bad setup of extrafield';
6219
-				}
6220
-			}
6221
-			else $value='';
6222
-		}
6223
-		elseif ($type == 'text' || $type == 'html')
6224
-		{
6225
-			$value=dol_htmlentitiesbr($value);
6226
-		}
6227
-		elseif ($type == 'password')
6228
-		{
6229
-			$value=preg_replace('/./i','*',$value);
6230
-		}
6231
-		elseif ($type == 'array')
6232
-		{
6233
-			$value = implode('<br>', $value);
6234
-		}
6235
-
6236
-		//print $type.'-'.$size;
6237
-		$out=$value;
6238
-
6239
-		return $out;
6240
-	}
6241
-
6242
-
6243
-	/**
6244
-	 * Function to show lines of extrafields with output datas
6245
-	 *
6246
-	 * @param 	Extrafields $extrafields    Extrafield Object
6247
-	 * @param 	string      $mode           Show output (view) or input (edit) for extrafield
6248
-	 * @param 	array       $params         Optional parameters. Example: array('style'=>'class="oddeven"', 'colspan'=>$colspan)
6249
-	 * @param 	string      $keysuffix      Suffix string to add after name and id of field (can be used to avoid duplicate names)
6250
-	 * @param 	string      $keyprefix      Prefix string to add before name and id of field (can be used to avoid duplicate names)
6251
-	 * @param	string		$onetrtd		All fields in same tr td
6252
-	 * @return 	string
6253
-	 */
6254
-	function showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='', $onetrtd=0)
6255
-	{
6256
-		global $db, $conf, $langs, $action, $form;
6257
-
6258
-		if (! is_object($form)) $form=new Form($db);
6259
-
6260
-		$out = '';
6261
-
6262
-		if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0)
6263
-		{
6264
-			$out .= "\n";
6265
-			$out .= '<!-- showOptionalsInput --> ';
6266
-			$out .= "\n";
6267
-
6268
-			$e = 0;
6269
-			foreach($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
6270
-			{
6271
-				// Show only the key field in params
6272
-				if (is_array($params) && array_key_exists('onlykey',$params) && $key != $params['onlykey']) continue;
7066
+        $keys=array();
7067
+        $values = array();
7068
+        foreach ($fieldvalues as $k => $v) {
7069
+            $keys[$k] = $k;
7070
+            $value = $this->fields[$k];
7071
+            $values[$k] = $this->quote($v, $value);
7072
+        }
6273 7073
 
6274
-				$enabled = 1;
6275
-				if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key]))
6276
-				{
6277
-					$enabled = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1);
6278
-				}
7074
+        // Clean and check mandatory
7075
+        foreach($keys as $key)
7076
+        {
7077
+            // If field is an implicit foreign key field
7078
+            if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';
7079
+            if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';
6279 7080
 
6280
-				$perms = 1;
6281
-				if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key]))
6282
-				{
6283
-					$perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1);
6284
-				}
7081
+            //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7082
+            if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && ! isset($values[$key]) && is_null($val['default']))
7083
+            {
7084
+                $error++;
7085
+                $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7086
+            }
6285 7087
 
6286
-				if (($mode == 'create' || $mode == 'edit') && abs($enabled) != 1 && abs($enabled) != 3) continue;	// <> -1 and <> 1 and <> 3 = not visible on forms, only on list
6287
-				if (empty($perms)) continue;
7088
+            // If field is an implicit foreign key field
7089
+            if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) $values[$key]='null';
7090
+            if (! empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key]='null';
7091
+        }
6288 7092
 
6289
-				// Load language if required
6290
-				if (! empty($extrafields->attributes[$this->table_element]['langfile'][$key])) $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
7093
+        if ($error) return -1;
6291 7094
 
6292
-				$colspan='3';
6293
-				if (is_array($params) && count($params)>0) {
6294
-					if (array_key_exists('colspan',$params)) {
6295
-						$colspan=$params['colspan'];
6296
-					}
6297
-				}
7095
+        $this->db->begin();
6298 7096
 
6299
-				switch($mode) {
6300
-					case "view":
6301
-						$value=$this->array_options["options_".$key.$keysuffix];
6302
-						break;
6303
-					case "edit":
6304
-						$getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, 'none');				// GETPOST can get value from GET, POST or setup of default values.
6305
-						// GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
6306
-						if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
6307
-						{
6308
-							if (is_array($getposttemp)) {
6309
-								// $getposttemp is an array but following code expects a comma separated string
6310
-								$value = implode(",", $getposttemp);
6311
-							} else {
6312
-								$value = $getposttemp;
6313
-							}
6314
-						} else {
6315
-							$value = $this->array_options["options_" . $key];			// No GET, no POST, no default value, so we take value of object.
6316
-						}
6317
-						//var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
6318
-						break;
6319
-				}
7097
+        if (! $error)
7098
+        {
7099
+            $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
7100
+            $sql.= ' ('.implode( ", ", $keys ).')';
7101
+            $sql.= ' VALUES ('.implode( ", ", $values ).')';
7102
+
7103
+            $res = $this->db->query($sql);
7104
+            if ($res===false) {
7105
+                $error++;
7106
+                $this->errors[] = $this->db->lasterror();
7107
+            }
7108
+        }
6320 7109
 
6321
-				if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate')
6322
-				{
6323
-					$out .= $extrafields->showSeparator($key, $this);
6324
-				}
6325
-				else
6326
-				{
6327
-					$csstyle='';
6328
-					$class=(!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
6329
-					if (is_array($params) && count($params)>0) {
6330
-						if (array_key_exists('style',$params)) {
6331
-							$csstyle=$params['style'];
6332
-						}
6333
-					}
7110
+        if (! $error)
7111
+        {
7112
+            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
7113
+        }
6334 7114
 
6335
-					// add html5 elements
6336
-					$domData  = ' data-element="extrafield"';
6337
-					$domData .= ' data-targetelement="'.$this->element.'"';
6338
-					$domData .= ' data-targetid="'.$this->id.'"';
7115
+        // Create extrafields
7116
+        if (! $error)
7117
+        {
7118
+            $result=$this->insertExtraFields();
7119
+            if ($result < 0) $error++;
7120
+        }
6339 7121
 
6340
-					$html_id = !empty($this->id) ? 'extrarow-'.$this->element.'_'.$key.'_'.$this->id : '';
7122
+        // Triggers
7123
+        if (! $error && ! $notrigger)
7124
+        {
7125
+            // Call triggers
7126
+            $result=$this->call_trigger(strtoupper(get_class($this)).'_CREATE',$user);
7127
+            if ($result < 0) { $error++; }
7128
+            // End call triggers
7129
+        }
6341 7130
 
6342
-					$out .= '<tr id="'.$html_id.'" '.$csstyle.' class="'.$class.$this->element.'_extras_'.$key.'" '.$domData.' >';
7131
+        // Commit or rollback
7132
+        if ($error) {
7133
+            $this->db->rollback();
7134
+            return -1;
7135
+        } else {
7136
+            $this->db->commit();
7137
+            return $this->id;
7138
+        }
7139
+    }
6343 7140
 
6344
-					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0)
6345
-					{
6346
-						if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan='0'; }
6347
-					}
6348 7141
 
6349
-					if ($action == 'selectlines') { $colspan++; }
7142
+    /**
7143
+     * Load object in memory from the database
7144
+     *
7145
+     * @param	int    $id				Id object
7146
+     * @param	string $ref				Ref
7147
+     * @param	string	$morewhere		More SQL filters (' AND ...')
7148
+     * @return 	int         			<0 if KO, 0 if not found, >0 if OK
7149
+     */
7150
+    public function fetchCommon($id, $ref = null, $morewhere = '')
7151
+    {
7152
+        if (empty($id) && empty($ref) && empty($morewhere)) return -1;
6350 7153
 
6351
-					// Convert date into timestamp format (value in memory must be a timestamp)
6352
-					if (in_array($extrafields->attributes[$this->table_element]['type'][$key],array('date','datetime')))
6353
-					{
6354
-						$datenotinstring = $this->array_options['options_' . $key];
6355
-						if (! is_numeric($this->array_options['options_' . $key]))	// For backward compatibility
6356
-						{
6357
-							$datenotinstring = $this->db->jdate($datenotinstring);
6358
-						}
6359
-						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?dol_mktime(GETPOST($keyprefix.'options_'.$key.$keysuffix."hour", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."min",'int',3), 0, GETPOST($keyprefix.'options_'.$key.$keysuffix."month",'int',3), GETPOST($keyprefix.'options_'.$key.$keysuffix."day",'int',3), GETPOST($keyprefix.'options_'.$key.$keysuffix."year",'int',3)):$datenotinstring;
6360
-					}
6361
-					// Convert float submited string into real php numeric (value in memory must be a php numeric)
6362
-					if (in_array($extrafields->attributes[$this->table_element]['type'][$key],array('price','double')))
6363
-					{
6364
-						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?price2num(GETPOST($keyprefix.'options_'.$key.$keysuffix, 'alpha', 3)):$this->array_options['options_'.$key];
6365
-					}
6366
-
6367
-					$labeltoshow = $langs->trans($label);
6368
-
6369
-					$out .= '<td class="titlefield';
6370
-					if (GETPOST('action','none') == 'create') $out.='create';
6371
-					if ($mode != 'view' && ! empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired';
6372
-					$out .= '">';
6373
-					if (! empty($extrafields->attributes[$object->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $extrafields->attributes[$object->table_element]['help'][$key]);
6374
-					else $out .= $labeltoshow;
6375
-					$out .= '</td>';
6376
-
6377
-					$html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
6378
-					$out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'" '.($colspan?' colspan="'.$colspan.'"':'').'>';
6379
-
6380
-					switch($mode) {
6381
-						case "view":
6382
-							$out .= $extrafields->showOutputField($key, $value);
6383
-							break;
6384
-						case "edit":
6385
-							$out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id);
6386
-							break;
6387
-					}
6388
-
6389
-					$out .= '</td>';
6390
-
6391
-					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
6392
-					else $out .= '</tr>';
6393
-					$e++;
6394
-				}
6395
-			}
6396
-			$out .= "\n";
6397
-			// Add code to manage list depending on others
6398
-			if (! empty($conf->use_javascript_ajax)) {
6399
-				$out .= '
6400
-				<script type="text/javascript">
6401
-				    jQuery(document).ready(function() {
6402
-				    	function showOptions(child_list, parent_list)
6403
-				    	{
6404
-				    		var val = $("select[name=\"options_"+parent_list+"\"]").val();
6405
-				    		var parentVal = parent_list + ":" + val;
6406
-							if(val > 0) {
6407
-					    		$("select[name=\""+child_list+"\"] option[parent]").hide();
6408
-					    		$("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
6409
-							} else {
6410
-								$("select[name=\""+child_list+"\"] option").show();
6411
-							}
6412
-				    	}
6413
-						function setListDependencies() {
6414
-					    	jQuery("select option[parent]").parent().each(function() {
6415
-					    		var child_list = $(this).attr("name");
6416
-								var parent = $(this).find("option[parent]:first").attr("parent");
6417
-								var infos = parent.split(":");
6418
-								var parent_list = infos[0];
6419
-								$("select[name=\""+parent_list+"\"]").change(function() {
6420
-									showOptions(child_list, parent_list);
6421
-								});
6422
-					    	});
6423
-						}
7154
+        $sql = 'SELECT '.$this->getFieldList();
7155
+        $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
6424 7156
 
6425
-						setListDependencies();
6426
-				    });
6427
-				</script>'."\n";
6428
-				$out .= '<!-- /showOptionalsInput --> '."\n";
6429
-			}
6430
-		}
6431
-		return $out;
6432
-	}
6433
-
6434
-
6435
-	/**
6436
-	 * Returns the rights used for this class
6437
-	 * @return stdClass
6438
-	 */
6439
-	public function getRights()
6440
-	{
6441
-		global $user;
6442
-
6443
-		$element = $this->element;
6444
-		if ($element == 'facturerec') $element='facture';
6445
-
6446
-		return $user->rights->{$element};
6447
-	}
6448
-
6449
-	/**
6450
-	 * Function used to replace a thirdparty id with another one.
6451
-	 * This function is meant to be called from replaceThirdparty with the appropiate tables
6452
-	 * Column name fk_soc MUST be used to identify thirdparties
6453
-	 *
6454
-	 * @param  DoliDB 	   $db 			  Database handler
6455
-	 * @param  int 		   $origin_id     Old thirdparty id (the thirdparty to delete)
6456
-	 * @param  int 		   $dest_id       New thirdparty id (the thirdparty that will received element of the other)
6457
-	 * @param  string[]    $tables        Tables that need to be changed
6458
-	 * @param  int         $ignoreerrors  Ignore errors. Return true even if errors. We need this when replacement can fails like for categories (categorie of old thirdparty may already exists on new one)
6459
-	 * @return bool						  True if success, False if error
6460
-	 */
6461
-	public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
6462
-	{
6463
-		foreach ($tables as $table)
6464
-		{
6465
-			$sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
6466
-
6467
-			if (! $db->query($sql))
6468
-			{
6469
-				if ($ignoreerrors) return true;		// TODO Not enough. If there is A-B on kept thirdarty and B-C on old one, we must get A-B-C after merge. Not A-B.
6470
-				//$this->errors = $db->lasterror();
6471
-				return false;
6472
-			}
6473
-		}
6474
-
6475
-		return true;
6476
-	}
6477
-
6478
-	/**
6479
-	 * Get buy price to use for margin calculation. This function is called when buy price is unknown.
6480
-	 *	 Set buy price = sell price if ForceBuyingPriceIfNull configured,
6481
-	 *   else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
6482
-	 *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
6483
-	 *	 else set min buy price as buy price
6484
-	 *
6485
-	 * @param float		$unitPrice		 Product unit price
6486
-	 * @param float		$discountPercent Line discount percent
6487
-	 * @param int		$fk_product		 Product id
6488
-	 * @return	float                    <0 if KO, buyprice if OK
6489
-	 */
6490
-	public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
6491
-	{
6492
-		global $conf;
6493
-
6494
-		$buyPrice = 0;
6495
-
6496
-		if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
6497
-		{
6498
-			$buyPrice = $unitPrice * (1 - $discountPercent / 100);
6499
-		}
6500
-		else
6501
-		{
6502
-			// Get cost price for margin calculation
6503
-			if (! empty($fk_product))
6504
-			{
6505
-				if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
6506
-				{
6507
-					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6508
-					$product = new Product($this->db);
6509
-					$result = $product->fetch($fk_product);
6510
-					if ($result <= 0)
6511
-					{
6512
-						$this->errors[] = 'ErrorProductIdDoesNotExists';
6513
-						return -1;
6514
-					}
6515
-					if ($product->cost_price > 0)
6516
-					{
6517
-						$buyPrice = $product->cost_price;
6518
-					}
6519
-					else if ($product->pmp > 0)
6520
-					{
6521
-						$buyPrice = $product->pmp;
6522
-					}
6523
-				}
6524
-				else if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
6525
-				{
6526
-					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6527
-					$product = new Product($this->db);
6528
-					$result = $product->fetch($fk_product);
6529
-					if ($result <= 0)
6530
-					{
6531
-						$this->errors[] = 'ErrorProductIdDoesNotExists';
6532
-						return -1;
6533
-					}
6534
-					if ($product->pmp > 0)
6535
-					{
6536
-						$buyPrice = $product->pmp;
6537
-					}
6538
-				}
7157
+        if (!empty($id))  $sql.= ' WHERE rowid = '.$id;
7158
+        elseif (!empty($ref)) $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
7159
+        else $sql.=' WHERE 1 = 1';	// usage with empty id and empty ref is very rare
7160
+        if ($morewhere)   $sql.= $morewhere;
7161
+        $sql.=' LIMIT 1';	// This is a fetch, to be sure to get only one record
6539 7162
 
6540
-				if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
6541
-				{
6542
-					require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6543
-					$productFournisseur = new ProductFournisseur($this->db);
6544
-					if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
6545
-					{
6546
-						$buyPrice = $productFournisseur->fourn_unitprice;
6547
-					}
6548
-					else if ($result < 0)
6549
-					{
6550
-						$this->errors[] = $productFournisseur->error;
6551
-						return -2;
6552
-					}
6553
-				}
6554
-			}
6555
-		}
6556
-		return $buyPrice;
6557
-	}
7163
+        $res = $this->db->query($sql);
7164
+        if ($res)
7165
+        {
7166
+            $obj = $this->db->fetch_object($res);
7167
+            if ($obj)
7168
+            {
7169
+                $this->setVarsFromFetchObj($obj);
7170
+                return $this->id;
7171
+            }
7172
+            else
7173
+            {
7174
+                return 0;
7175
+            }
7176
+        }
7177
+        else
7178
+        {
7179
+            $this->error = $this->db->lasterror();
7180
+            $this->errors[] = $this->error;
7181
+            return -1;
7182
+        }
7183
+    }
6558 7184
 
6559
-    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
6560
-	/**
6561
-	 *  Show photos of an object (nbmax maximum), into several columns
6562
-	 *
6563
-	 *  @param		string	$modulepart		'product', 'ticket', ...
6564
-	 *  @param      string	$sdir        	Directory to scan (full absolute path)
6565
-	 *  @param      int		$size        	0=original size, 1='small' use thumbnail if possible
6566
-	 *  @param      int		$nbmax       	Nombre maximum de photos (0=pas de max)
6567
-	 *  @param      int		$nbbyrow     	Number of image per line or -1 to use div. Used only if size=1.
6568
-	 * 	@param		int		$showfilename	1=Show filename
6569
-	 * 	@param		int		$showaction		1=Show icon with action links (resize, delete)
6570
-	 * 	@param		int		$maxHeight		Max height of original image when size='small' (so we can use original even if small requested). If 0, always use 'small' thumb image.
6571
-	 * 	@param		int		$maxWidth		Max width of original image when size='small'
6572
-	 *  @param      int     $nolink         Do not add a href link to view enlarged imaged into a new tab
6573
-	 *  @param      int     $notitle        Do not add title tag on image
6574
-	 *  @param		int		$usesharelink	Use the public shared link of image (if not available, the 'nophoto' image will be shown instead)
6575
-	 *  @return     string					Html code to show photo. Number of photos shown is saved in this->nbphoto
6576
-	 */
6577
-	function show_photos($modulepart, $sdir, $size=0, $nbmax=0, $nbbyrow=5, $showfilename=0, $showaction=0, $maxHeight=120, $maxWidth=160, $nolink=0, $notitle=0, $usesharelink=0)
6578
-	{
6579
-        // phpcs:enable
6580
-		global $conf,$user,$langs;
6581
-
6582
-		include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
6583
-		include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
6584
-
6585
-		$sortfield='position_name';
6586
-		$sortorder='asc';
6587
-
6588
-		$dir = $sdir . '/';
6589
-		$pdir = '/';
6590
-		if ($modulepart == 'ticket')
6591
-		{
6592
-			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6593
-			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6594
-		}
6595
-		else
6596
-		{
6597
-			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6598
-			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6599
-		}
6600
-
6601
-		// For backward compatibility
6602
-		if ($modulepart == 'product' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))
6603
-		{
6604
-			$dir = $sdir . '/'. get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/";
6605
-			$pdir = '/' . get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/";
6606
-		}
6607
-
6608
-		// Defined relative dir to DOL_DATA_ROOT
6609
-		$relativedir = '';
6610
-		if ($dir)
6611
-		{
6612
-			$relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $dir);
6613
-			$relativedir = preg_replace('/^[\\/]/','',$relativedir);
6614
-			$relativedir = preg_replace('/[\\/]$/','',$relativedir);
6615
-		}
6616
-
6617
-		$dirthumb = $dir.'thumbs/';
6618
-		$pdirthumb = $pdir.'thumbs/';
6619
-
6620
-		$return ='<!-- Photo -->'."\n";
6621
-		$nbphoto=0;
6622
-
6623
-		$filearray=dol_dir_list($dir,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6624
-
6625
-		/*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))    // For backward compatiblity, we scan also old dirs
6626
-		 {
6627
-		 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6628
-		 $filearray=array_merge($filearray, $filearrayold);
6629
-		 }*/
7185
+    /**
7186
+     * Update object into database
7187
+     *
7188
+     * @param  User $user      	User that modifies
7189
+     * @param  bool $notrigger 	false=launch triggers after, true=disable triggers
7190
+     * @return int             	<0 if KO, >0 if OK
7191
+     */
7192
+    public function updateCommon(User $user, $notrigger = false)
7193
+    {
7194
+        global $conf, $langs;
6630 7195
 
6631
-		completeFileArrayWithDatabaseInfo($filearray, $relativedir);
7196
+        $error = 0;
6632 7197
 
6633
-		if (count($filearray))
6634
-		{
6635
-			if ($sortfield && $sortorder)
6636
-			{
6637
-				$filearray=dol_sort_array($filearray, $sortfield, $sortorder);
6638
-			}
7198
+        $now=dol_now();
6639 7199
 
6640
-			foreach($filearray as $key => $val)
6641
-			{
6642
-				$photo='';
6643
-				$file = $val['name'];
7200
+        $fieldvalues = $this->setSaveQuery();
7201
+        if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification']=$this->db->idate($now);
7202
+        if (array_key_exists('fk_user_modif', $fieldvalues) && ! ($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif']=$user->id;
7203
+        unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
6644 7204
 
6645
-				//if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure file is stored in UTF8 in memory
7205
+        $keys=array();
7206
+        $values = array();
7207
+        foreach ($fieldvalues as $k => $v) {
7208
+            $keys[$k] = $k;
7209
+            $value = $this->fields[$k];
7210
+            $values[$k] = $this->quote($v, $value);
7211
+            $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
7212
+        }
6646 7213
 
6647
-				//if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
6648
-				if (image_format_supported($file) >= 0)
6649
-				{
6650
-					$nbphoto++;
6651
-					$photo = $file;
6652
-					$viewfilename = $file;
7214
+        // Clean and check mandatory
7215
+        foreach($keys as $key)
7216
+        {
7217
+            if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';		// This is an implicit foreign key field
7218
+            if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';					// This is an explicit foreign key field
7219
+
7220
+            //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7221
+            /*
7222
+			if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
7223
+			{
7224
+				$error++;
7225
+				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7226
+			}*/
7227
+        }
6653 7228
 
6654
-					if ($size == 1 || $size == 'small') {   // Format vignette
7229
+        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode( ',', $tmp ).' WHERE rowid='.$this->id ;
6655 7230
 
6656
-						// Find name of thumb file
6657
-						$photo_vignette=basename(getImageFileNameForSize($dir.$file, '_small'));
6658
-						if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette='';
7231
+        $this->db->begin();
7232
+        if (! $error)
7233
+        {
7234
+            $res = $this->db->query($sql);
7235
+            if ($res===false)
7236
+            {
7237
+                $error++;
7238
+                $this->errors[] = $this->db->lasterror();
7239
+            }
7240
+        }
6659 7241
 
6660
-						// Get filesize of original file
6661
-						$imgarray=dol_getImageSize($dir.$photo);
7242
+        // Update extrafield
7243
+        if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
7244
+        {
7245
+            $result=$this->insertExtraFields();
7246
+            if ($result < 0)
7247
+            {
7248
+                $error++;
7249
+            }
7250
+        }
6662 7251
 
6663
-						if ($nbbyrow > 0)
6664
-						{
6665
-							if ($nbphoto == 1) $return.= '<table width="100%" valign="top" align="center" border="0" cellpadding="2" cellspacing="2">';
7252
+        // Triggers
7253
+        if (! $error && ! $notrigger)
7254
+        {
7255
+            // Call triggers
7256
+            $result=$this->call_trigger(strtoupper(get_class($this)).'_MODIFY',$user);
7257
+            if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
7258
+            // End call triggers
7259
+        }
6666 7260
 
6667
-							if ($nbphoto % $nbbyrow == 1) $return.= '<tr align=center valign=middle border=1>';
6668
-							$return.= '<td width="'.ceil(100/$nbbyrow).'%" class="photo">';
6669
-						}
6670
-						else if ($nbbyrow < 0) $return .= '<div class="inline-block">';
7261
+        // Commit or rollback
7262
+        if ($error) {
7263
+            $this->db->rollback();
7264
+            return -1;
7265
+        } else {
7266
+            $this->db->commit();
7267
+            return $this->id;
7268
+        }
7269
+    }
6671 7270
 
6672
-						$return.= "\n";
7271
+    /**
7272
+     * Delete object in database
7273
+     *
7274
+     * @param 	User 	$user       			User that deletes
7275
+     * @param 	bool 	$notrigger  			false=launch triggers after, true=disable triggers
7276
+     * @param	int		$forcechilddeletion		0=no, 1=Force deletion of children
7277
+     * @return 	int             				<=0 if KO, >0 if OK
7278
+     */
7279
+    public function deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
7280
+    {
7281
+        $error=0;
6673 7282
 
6674
-						$relativefile=preg_replace('/^\//', '', $pdir.$photo);
6675
-						if (empty($nolink))
6676
-						{
6677
-							$urladvanced=getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
6678
-							if ($urladvanced) $return.='<a href="'.$urladvanced.'">';
6679
-							else $return.= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">';
6680
-						}
7283
+        $this->db->begin();
6681 7284
 
6682
-						// Show image (width height=$maxHeight)
6683
-						// Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
6684
-						$alt=$langs->transnoentitiesnoconv('File').': '.$relativefile;
6685
-						$alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
6686
-						if ($notitle) $alt='';
6687
-
6688
-						if ($usesharelink)
6689
-						{
6690
-							if ($val['share'])
6691
-							{
6692
-								if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6693
-								{
6694
-									$return.= '<!-- Show original file (thumb not yet available with shared links) -->';
6695
-									$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6696
-								}
6697
-								else {
6698
-									$return.= '<!-- Show original file -->';
6699
-									$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6700
-								}
6701
-							}
6702
-							else
6703
-							{
6704
-								$return.= '<!-- Show nophoto file (because file is not shared) -->';
6705
-								$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
6706
-							}
6707
-						}
6708
-						else
6709
-						{
6710
-							if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6711
-							{
6712
-								$return.= '<!-- Show thumb -->';
6713
-								$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
6714
-							}
6715
-							else {
6716
-								$return.= '<!-- Show original file -->';
6717
-								$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
6718
-							}
6719
-						}
7285
+        if ($forcechilddeletion)
7286
+        {
7287
+            foreach($this->childtables as $table)
7288
+            {
7289
+                $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
7290
+                $resql = $this->db->query($sql);
7291
+                if (! $resql)
7292
+                {
7293
+                    $this->error=$this->db->lasterror();
7294
+                    $this->errors[]=$this->error;
7295
+                    $this->db->rollback();
7296
+                    return -1;
7297
+                }
7298
+            }
7299
+        }
7300
+        elseif (! empty($this->fk_element) && ! empty($this->childtables))	// If object has childs linked with a foreign key field, we check all child tables.
7301
+        {
7302
+            $objectisused = $this->isObjectUsed($this->id);
7303
+            if (! empty($objectisused))
7304
+            {
7305
+                dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
7306
+                $this->error='ErrorRecordHasChildren';
7307
+                $this->errors[]=$this->error;
7308
+                $this->db->rollback();
7309
+                return 0;
7310
+            }
7311
+        }
6720 7312
 
6721
-						if (empty($nolink)) $return.= '</a>';
6722
-						$return.="\n";
6723
-
6724
-						if ($showfilename) $return.= '<br>'.$viewfilename;
6725
-						if ($showaction)
6726
-						{
6727
-							$return.= '<br>';
6728
-							// On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
6729
-							if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight))
6730
-							{
6731
-								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=addthumb&amp;file='.urlencode($pdir.$viewfilename).'">'.img_picto($langs->trans('GenerateThumb'),'refresh').'&nbsp;&nbsp;</a>';
6732
-							}
6733
-							// Special cas for product
6734
-							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6735
-							{
6736
-								// Link to resize
6737
-								$return.= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
6738
-
6739
-								// Link to delete
6740
-								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6741
-								$return.= img_delete().'</a>';
6742
-							}
6743
-						}
6744
-						$return.= "\n";
7313
+        if (! $error) {
7314
+            if (! $notrigger) {
7315
+                // Call triggers
7316
+                $result=$this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
7317
+                if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
7318
+                // End call triggers
7319
+            }
7320
+        }
6745 7321
 
6746
-						if ($nbbyrow > 0)
6747
-						{
6748
-							$return.= '</td>';
6749
-							if (($nbphoto % $nbbyrow) == 0) $return.= '</tr>';
6750
-						}
6751
-						else if ($nbbyrow < 0) $return.='</div>';
6752
-					}
6753
-
6754
-					if (empty($size)) {     // Format origine
6755
-						$return.= '<img class="photo photowithmargin" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
6756
-
6757
-						if ($showfilename) $return.= '<br>'.$viewfilename;
6758
-						if ($showaction)
6759
-						{
6760
-							// Special case for product
6761
-							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6762
-							{
6763
-								// Link to resize
6764
-								$return.= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
6765
-
6766
-								// Link to delete
6767
-								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6768
-								$return.= img_delete().'</a>';
6769
-							}
6770
-						}
6771
-					}
7322
+        if (! $error && ! empty($this->isextrafieldmanaged))
7323
+        {
7324
+            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element."_extrafields";
7325
+            $sql.= " WHERE fk_object=" . $this->id;
6772 7326
 
6773
-					// On continue ou on arrete de boucler ?
6774
-					if ($nbmax && $nbphoto >= $nbmax) break;
6775
-				}
6776
-			}
7327
+            $resql = $this->db->query($sql);
7328
+            if (! $resql)
7329
+            {
7330
+                $this->errors[] = $this->db->lasterror();
7331
+                $error++;
7332
+            }
7333
+        }
6777 7334
 
6778
-			if ($size==1 || $size=='small')
6779
-			{
6780
-				if ($nbbyrow > 0)
6781
-				{
6782
-					// Ferme tableau
6783
-					while ($nbphoto % $nbbyrow)
6784
-					{
6785
-						$return.= '<td width="'.ceil(100/$nbbyrow).'%">&nbsp;</td>';
6786
-						$nbphoto++;
6787
-					}
6788
-
6789
-					if ($nbphoto) $return.= '</table>';
6790
-				}
6791
-			}
6792
-		}
6793
-
6794
-		$this->nbphoto = $nbphoto;
6795
-
6796
-		return $return;
6797
-	}
6798
-
6799
-
6800
-	/**
6801
-	 * Function test if type is array
6802
-	 *
6803
-	 * @param   array   $info   content informations of field
6804
-	 * @return                  bool
6805
-	 */
6806
-	protected function isArray($info)
6807
-	{
6808
-		if(is_array($info))
6809
-		{
6810
-			if(isset($info['type']) && $info['type']=='array') return true;
6811
-			else return false;
6812
-		}
6813
-		else return false;
6814
-	}
6815
-
6816
-	/**
6817
-	 * Function test if type is null
6818
-	 *
6819
-	 * @param   array   $info   content informations of field
6820
-	 * @return                  bool
6821
-	 */
6822
-	protected function isNull($info)
6823
-	{
6824
-		if(is_array($info))
6825
-		{
6826
-			if(isset($info['type']) && $info['type']=='null') return true;
6827
-			else return false;
6828
-		}
6829
-		else return false;
6830
-	}
6831
-
6832
-	/**
6833
-	 * Function test if type is date
6834
-	 *
6835
-	 * @param   array   $info   content informations of field
6836
-	 * @return                  bool
6837
-	 */
6838
-	public function isDate($info)
6839
-	{
6840
-		if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
6841
-		else return false;
6842
-	}
6843
-
6844
-	/**
6845
-	 * Function test if type is integer
6846
-	 *
6847
-	 * @param   array   $info   content informations of field
6848
-	 * @return                  bool
6849
-	 */
6850
-	public function isInt($info)
6851
-	{
6852
-		if(is_array($info))
6853
-		{
6854
-			if(isset($info['type']) && ($info['type']=='int' || preg_match('/^integer/i',$info['type']) ) ) return true;
6855
-			else return false;
6856
-		}
6857
-		else return false;
6858
-	}
6859
-
6860
-	/**
6861
-	 * Function test if type is float
6862
-	 *
6863
-	 * @param   array   $info   content informations of field
6864
-	 * @return                  bool
6865
-	 */
6866
-	public function isFloat($info)
6867
-	{
6868
-		if(is_array($info))
6869
-		{
6870
-			if (isset($info['type']) && (preg_match('/^(double|real)/i', $info['type']))) return true;
6871
-			else return false;
6872
-		}
6873
-		else return false;
6874
-	}
6875
-
6876
-	/**
6877
-	 * Function test if type is text
6878
-	 *
6879
-	 * @param   array   $info   content informations of field
6880
-	 * @return                  bool
6881
-	 */
6882
-	public function isText($info)
6883
-	{
6884
-		if(is_array($info))
6885
-		{
6886
-			if(isset($info['type']) && $info['type']=='text') return true;
6887
-			else return false;
6888
-		}
6889
-		else return false;
6890
-	}
6891
-
6892
-	/**
6893
-	 * Function test if is indexed
6894
-	 *
6895
-	 * @param   array   $info   content informations of field
6896
-	 * @return                  bool
6897
-	 */
6898
-	protected function isIndex($info)
6899
-	{
6900
-		if(is_array($info))
6901
-		{
6902
-			if(isset($info['index']) && $info['index']==true) return true;
6903
-			else return false;
6904
-		}
6905
-		else return false;
6906
-	}
6907
-
6908
-	/**
6909
-	 * Function to prepare the values to insert.
6910
-	 * Note $this->${field} are set by the page that make the createCommon or the updateCommon.
6911
-	 *
6912
-	 * @return array
6913
-	 */
6914
-	protected function setSaveQuery()
6915
-	{
6916
-		global $conf;
6917
-
6918
-		$queryarray=array();
6919
-		foreach ($this->fields as $field=>$info)	// Loop on definition of fields
6920
-		{
6921
-			// Depending on field type ('datetime', ...)
6922
-			if($this->isDate($info))
6923
-			{
6924
-				if(empty($this->{$field}))
6925
-				{
6926
-					$queryarray[$field] = null;
6927
-				}
6928
-				else
6929
-				{
6930
-					$queryarray[$field] = $this->db->idate($this->{$field});
6931
-				}
6932
-			}
6933
-			else if($this->isArray($info))
6934
-			{
6935
-				if(! empty($this->{$field})) {
6936
-					if(! is_array($this->{$field})) {
6937
-						$this->{$field} = array($this->{$field});
6938
-					}
6939
-					$queryarray[$field] = serialize($this->{$field});
6940
-				} else {
6941
-					$queryarray[$field] = null;
6942
-				}
6943
-			}
6944
-			else if($this->isInt($info))
6945
-			{
6946
-				if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field]=$conf->entity;
6947
-				else
6948
-				{
6949
-					$queryarray[$field] = (int) price2num($this->{$field});
6950
-					if (empty($queryarray[$field])) $queryarray[$field]=0;		// May be reset to null later if property 'notnull' is -1 for this field.
6951
-				}
6952
-			}
6953
-			else if($this->isFloat($info))
6954
-			{
6955
-				$queryarray[$field] = (double) price2num($this->{$field});
6956
-				if (empty($queryarray[$field])) $queryarray[$field]=0;
6957
-			}
6958
-			else
6959
-			{
6960
-				$queryarray[$field] = $this->{$field};
6961
-			}
7335
+        if (! $error)
7336
+        {
7337
+            $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
6962 7338
 
6963
-			if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
6964
-			if (! empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null;
6965
-		}
7339
+            $res = $this->db->query($sql);
7340
+            if($res===false) {
7341
+                $error++;
7342
+                $this->errors[] = $this->db->lasterror();
7343
+            }
7344
+        }
6966 7345
 
6967
-		return $queryarray;
6968
-	}
7346
+        // Commit or rollback
7347
+        if ($error) {
7348
+            $this->db->rollback();
7349
+            return -1;
7350
+        } else {
7351
+            $this->db->commit();
7352
+            return 1;
7353
+        }
7354
+    }
6969 7355
 
6970
-	/**
6971
-	 * Function to load data from a SQL pointer into properties of current object $this
6972
-	 *
6973
-	 * @param   stdClass    $obj    Contain data of object from database
7356
+    /**
7357
+     * Initialise object with example values
7358
+     * Id must be 0 if object instance is a specimen
7359
+     *
6974 7360
      * @return void
6975
-	 */
6976
-	protected function setVarsFromFetchObj(&$obj)
6977
-	{
6978
-		foreach ($this->fields as $field => $info)
6979
-		{
6980
-			if($this->isDate($info))
6981
-			{
6982
-				if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
6983
-				else $this->{$field} = strtotime($obj->{$field});
6984
-			}
6985
-			elseif($this->isArray($info))
6986
-			{
6987
-				if(! empty($obj->{$field})) {
6988
-					$this->{$field} = @unserialize($obj->{$field});
6989
-					// Hack for data not in UTF8
6990
-					if($this->{$field } === false) @unserialize(utf8_decode($obj->{$field}));
6991
-				} else {
6992
-					$this->{$field} = array();
6993
-				}
6994
-			}
6995
-			elseif($this->isInt($info))
6996
-			{
6997
-				if ($field == 'rowid') $this->id = (int) $obj->{$field};
6998
-				else $this->{$field} = (int) $obj->{$field};
6999
-			}
7000
-			elseif($this->isFloat($info))
7001
-			{
7002
-				$this->{$field} = (double) $obj->{$field};
7003
-			}
7004
-			elseif($this->isNull($info))
7005
-			{
7006
-				$val = $obj->{$field};
7007
-				// zero is not null
7008
-				$this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
7009
-			}
7010
-			else
7011
-			{
7012
-				$this->{$field} = $obj->{$field};
7013
-			}
7014
-		}
7015
-
7016
-		// If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
7017
-		if (! isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id;
7018
-	}
7019
-
7020
-	/**
7021
-	 * Function to concat keys of fields
7022
-	 *
7023
-	 * @return string
7024
-	 */
7025
-	protected function getFieldList()
7026
-	{
7027
-		$keys = array_keys($this->fields);
7028
-		return implode(',', $keys);
7029
-	}
7030
-
7031
-	/**
7032
-	 * Add quote to field value if necessary
7033
-	 *
7034
-	 * @param 	string|int	$value			Value to protect
7035
-	 * @param	array		$fieldsentry	Properties of field
7036
-	 * @return 	string
7037
-	 */
7038
-    protected function quote($value, $fieldsentry)
7361
+     */
7362
+    public function initAsSpecimenCommon()
7039 7363
     {
7040
-		if (is_null($value)) return 'NULL';
7041
-		else if (preg_match('/^(int|double|real)/i', $fieldsentry['type'])) return $this->db->escape("$value");
7042
-		else return "'".$this->db->escape($value)."'";
7043
-	}
7044
-
7045
-
7046
-	/**
7047
-	 * Create object into database
7048
-	 *
7049
-	 * @param  User $user      User that creates
7050
-	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
7051
-	 * @return int             <0 if KO, Id of created object if OK
7052
-	 */
7053
-	public function createCommon(User $user, $notrigger = false)
7054
-	{
7055
-		global $langs;
7056
-
7057
-		$error = 0;
7058
-
7059
-		$now=dol_now();
7060
-
7061
-		$fieldvalues = $this->setSaveQuery();
7062
-		if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now);
7063
-		if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id;
7064
-		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
7065
-
7066
-		$keys=array();
7067
-		$values = array();
7068
-		foreach ($fieldvalues as $k => $v) {
7069
-			$keys[$k] = $k;
7070
-			$value = $this->fields[$k];
7071
-			$values[$k] = $this->quote($v, $value);
7072
-		}
7073
-
7074
-		// Clean and check mandatory
7075
-		foreach($keys as $key)
7076
-		{
7077
-			// If field is an implicit foreign key field
7078
-			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';
7079
-			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';
7080
-
7081
-			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7082
-			if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && ! isset($values[$key]) && is_null($val['default']))
7083
-			{
7084
-				$error++;
7085
-				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7086
-			}
7087
-
7088
-			// If field is an implicit foreign key field
7089
-			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) $values[$key]='null';
7090
-			if (! empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key]='null';
7091
-		}
7092
-
7093
-		if ($error) return -1;
7094
-
7095
-		$this->db->begin();
7364
+        $this->id = 0;
7096 7365
 
7097
-		if (! $error)
7098
-		{
7099
-			$sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
7100
-			$sql.= ' ('.implode( ", ", $keys ).')';
7101
-			$sql.= ' VALUES ('.implode( ", ", $values ).')';
7366
+        // TODO...
7367
+    }
7102 7368
 
7103
-			$res = $this->db->query($sql);
7104
-			if ($res===false) {
7105
-				$error++;
7106
-				$this->errors[] = $this->db->lasterror();
7107
-			}
7108
-		}
7109
-
7110
-		if (! $error)
7111
-		{
7112
-			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
7113
-		}
7114
-
7115
-		// Create extrafields
7116
-		if (! $error)
7117
-		{
7118
-			$result=$this->insertExtraFields();
7119
-			if ($result < 0) $error++;
7120
-		}
7121
-
7122
-		// Triggers
7123
-		if (! $error && ! $notrigger)
7124
-		{
7125
-			// Call triggers
7126
-			$result=$this->call_trigger(strtoupper(get_class($this)).'_CREATE',$user);
7127
-			if ($result < 0) { $error++; }
7128
-			// End call triggers
7129
-		}
7130
-
7131
-		// Commit or rollback
7132
-		if ($error) {
7133
-			$this->db->rollback();
7134
-			return -1;
7135
-		} else {
7136
-			$this->db->commit();
7137
-			return $this->id;
7138
-		}
7139
-	}
7140
-
7141
-
7142
-	/**
7143
-	 * Load object in memory from the database
7144
-	 *
7145
-	 * @param	int    $id				Id object
7146
-	 * @param	string $ref				Ref
7147
-	 * @param	string	$morewhere		More SQL filters (' AND ...')
7148
-	 * @return 	int         			<0 if KO, 0 if not found, >0 if OK
7149
-	 */
7150
-	public function fetchCommon($id, $ref = null, $morewhere = '')
7151
-	{
7152
-		if (empty($id) && empty($ref) && empty($morewhere)) return -1;
7153
-
7154
-		$sql = 'SELECT '.$this->getFieldList();
7155
-		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
7156
-
7157
-		if (!empty($id))  $sql.= ' WHERE rowid = '.$id;
7158
-		elseif (!empty($ref)) $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
7159
-		else $sql.=' WHERE 1 = 1';	// usage with empty id and empty ref is very rare
7160
-		if ($morewhere)   $sql.= $morewhere;
7161
-		$sql.=' LIMIT 1';	// This is a fetch, to be sure to get only one record
7162
-
7163
-		$res = $this->db->query($sql);
7164
-		if ($res)
7165
-		{
7166
-			$obj = $this->db->fetch_object($res);
7167
-			if ($obj)
7168
-			{
7169
-				$this->setVarsFromFetchObj($obj);
7170
-				return $this->id;
7171
-			}
7172
-			else
7173
-			{
7174
-				return 0;
7175
-			}
7176
-		}
7177
-		else
7178
-		{
7179
-			$this->error = $this->db->lasterror();
7180
-			$this->errors[] = $this->error;
7181
-			return -1;
7182
-		}
7183
-	}
7184
-
7185
-	/**
7186
-	 * Update object into database
7187
-	 *
7188
-	 * @param  User $user      	User that modifies
7189
-	 * @param  bool $notrigger 	false=launch triggers after, true=disable triggers
7190
-	 * @return int             	<0 if KO, >0 if OK
7191
-	 */
7192
-	public function updateCommon(User $user, $notrigger = false)
7193
-	{
7194
-		global $conf, $langs;
7195
-
7196
-		$error = 0;
7197
-
7198
-		$now=dol_now();
7199
-
7200
-		$fieldvalues = $this->setSaveQuery();
7201
-		if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification']=$this->db->idate($now);
7202
-		if (array_key_exists('fk_user_modif', $fieldvalues) && ! ($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif']=$user->id;
7203
-		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
7204
-
7205
-		$keys=array();
7206
-		$values = array();
7207
-		foreach ($fieldvalues as $k => $v) {
7208
-			$keys[$k] = $k;
7209
-			$value = $this->fields[$k];
7210
-			$values[$k] = $this->quote($v, $value);
7211
-			$tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
7212
-		}
7213
-
7214
-		// Clean and check mandatory
7215
-		foreach($keys as $key)
7216
-		{
7217
-			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';		// This is an implicit foreign key field
7218
-			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';					// This is an explicit foreign key field
7219
-
7220
-			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7221
-			/*
7222
-			if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
7223
-			{
7224
-				$error++;
7225
-				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7226
-			}*/
7227
-		}
7228 7369
 
7229
-		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode( ',', $tmp ).' WHERE rowid='.$this->id ;
7370
+    /* Part for comments */
7230 7371
 
7231
-		$this->db->begin();
7232
-		if (! $error)
7233
-		{
7234
-			$res = $this->db->query($sql);
7235
-			if ($res===false)
7236
-			{
7237
-				$error++;
7238
-				$this->errors[] = $this->db->lasterror();
7239
-			}
7240
-		}
7241
-
7242
-		// Update extrafield
7243
-		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
7244
-		{
7245
-			$result=$this->insertExtraFields();
7246
-			if ($result < 0)
7247
-			{
7248
-				$error++;
7249
-			}
7250
-		}
7251
-
7252
-		// Triggers
7253
-		if (! $error && ! $notrigger)
7254
-		{
7255
-			// Call triggers
7256
-			$result=$this->call_trigger(strtoupper(get_class($this)).'_MODIFY',$user);
7257
-			if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
7258
-			// End call triggers
7259
-		}
7260
-
7261
-		// Commit or rollback
7262
-		if ($error) {
7263
-			$this->db->rollback();
7264
-			return -1;
7265
-		} else {
7266
-			$this->db->commit();
7267
-			return $this->id;
7268
-		}
7269
-	}
7270
-
7271
-	/**
7272
-	 * Delete object in database
7273
-	 *
7274
-	 * @param 	User 	$user       			User that deletes
7275
-	 * @param 	bool 	$notrigger  			false=launch triggers after, true=disable triggers
7276
-	 * @param	int		$forcechilddeletion		0=no, 1=Force deletion of children
7277
-	 * @return 	int             				<=0 if KO, >0 if OK
7278
-	 */
7279
-	public function deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
7280
-	{
7281
-		$error=0;
7282
-
7283
-		$this->db->begin();
7284
-
7285
-		if ($forcechilddeletion)
7286
-		{
7287
-			foreach($this->childtables as $table)
7288
-			{
7289
-				$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
7290
-				$resql = $this->db->query($sql);
7291
-				if (! $resql)
7292
-				{
7293
-					$this->error=$this->db->lasterror();
7294
-					$this->errors[]=$this->error;
7295
-					$this->db->rollback();
7296
-					return -1;
7297
-				}
7298
-			}
7299
-		}
7300
-		elseif (! empty($this->fk_element) && ! empty($this->childtables))	// If object has childs linked with a foreign key field, we check all child tables.
7301
-		{
7302
-			$objectisused = $this->isObjectUsed($this->id);
7303
-			if (! empty($objectisused))
7304
-			{
7305
-				dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
7306
-				$this->error='ErrorRecordHasChildren';
7307
-				$this->errors[]=$this->error;
7308
-				$this->db->rollback();
7309
-				return 0;
7310
-			}
7311
-		}
7312
-
7313
-		if (! $error) {
7314
-			if (! $notrigger) {
7315
-				// Call triggers
7316
-				$result=$this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
7317
-				if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
7318
-				// End call triggers
7319
-			}
7320
-		}
7321
-
7322
-		if (! $error && ! empty($this->isextrafieldmanaged))
7323
-		{
7324
-			$sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element."_extrafields";
7325
-			$sql.= " WHERE fk_object=" . $this->id;
7326
-
7327
-			$resql = $this->db->query($sql);
7328
-			if (! $resql)
7329
-			{
7330
-				$this->errors[] = $this->db->lasterror();
7331
-				$error++;
7332
-			}
7333
-		}
7372
+    /**
7373
+     * Load comments linked with current task
7374
+     *	@return boolean	1 if ok
7375
+     */
7376
+    public function fetchComments()
7377
+    {
7378
+        require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
7334 7379
 
7335
-		if (! $error)
7336
-		{
7337
-			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
7380
+        $comment = new Comment($this->db);
7381
+        $result=$comment->fetchAllFor($this->element, $this->id);
7382
+        if ($result<0) {
7383
+            $this->errors=array_merge($this->errors, $comment->errors);
7384
+            return -1;
7385
+        } else {
7386
+            $this->comments = $comment->comments;
7387
+        }
7388
+        return count($this->comments);
7389
+    }
7338 7390
 
7339
-			$res = $this->db->query($sql);
7340
-			if($res===false) {
7341
-				$error++;
7342
-				$this->errors[] = $this->db->lasterror();
7343
-			}
7344
-		}
7345
-
7346
-		// Commit or rollback
7347
-		if ($error) {
7348
-			$this->db->rollback();
7349
-			return -1;
7350
-		} else {
7351
-			$this->db->commit();
7352
-			return 1;
7353
-		}
7354
-	}
7355
-
7356
-	/**
7357
-	 * Initialise object with example values
7358
-	 * Id must be 0 if object instance is a specimen
7359
-	 *
7360
-	 * @return void
7361
-	 */
7362
-	public function initAsSpecimenCommon()
7363
-	{
7364
-		$this->id = 0;
7365
-
7366
-		// TODO...
7367
-	}
7368
-
7369
-
7370
-	/* Part for comments */
7371
-
7372
-	/**
7373
-	 * Load comments linked with current task
7374
-	 *	@return boolean	1 if ok
7375
-	 */
7376
-	public function fetchComments()
7377
-	{
7378
-		require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
7379
-
7380
-		$comment = new Comment($this->db);
7381
-		$result=$comment->fetchAllFor($this->element, $this->id);
7382
-		if ($result<0) {
7383
-			$this->errors=array_merge($this->errors, $comment->errors);
7384
-			return -1;
7385
-		} else {
7386
-			$this->comments = $comment->comments;
7387
-		}
7388
-		return count($this->comments);
7389
-	}
7390
-
7391
-	/**
7392
-	 * Return nb comments already posted
7393
-	 *
7394
-	 * @return int
7395
-	 */
7396
-	public function getNbComments()
7397
-	{
7398
-		return count($this->comments);
7399
-	}
7391
+    /**
7392
+     * Return nb comments already posted
7393
+     *
7394
+     * @return int
7395
+     */
7396
+    public function getNbComments()
7397
+    {
7398
+        return count($this->comments);
7399
+    }
7400 7400
 
7401 7401
     /**
7402 7402
      * Trim object parameters
Please login to merge, or discard this patch.