Passed
Push — EXTRACT_CLASSES ( ae6b5c...83d77a )
by Rafael
60:14 queued 23:58
created

FormListWebPortal   F

Complexity

Total Complexity 157

Size/Duplication

Total Lines 790
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 435
dl 0
loc 790
rs 2
c 0
b 0
f 0
wmc 157

5 Methods

Rating   Name   Duplication   Size   Complexity  
F init() 0 85 18
F elementList() 0 532 119
C generatePageListNav() 0 42 13
A doActions() 0 15 6
A __construct() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like FormListWebPortal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FormListWebPortal, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* Copyright (C) 2023-2024  Laurent Destailleur         <[email protected]>
4
 * Copyright (C) 2023-2024	Lionel Vessiller		    <[email protected]>
5
 * Copyright (C) 2023-2024	Patrice Andreani		    <[email protected]>
6
 * Copyright (C) 2024       Frédéric France             <[email protected]>
7
 * Copyright (C) 2024		MDW							<[email protected]>
8
 * Copyright (C) 2024       Rafael San José             <[email protected]>
9
 *
10
 * This program is free software; you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation; either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23
24
namespace Dolibarr\Code\WebPortal\Classes;
25
26
use DoliDB;
27
28
/**
29
 * \file       htdocs/webportal/class/html.formlistwebportal.class.php
30
 * \ingroup    webportal
31
 * \brief      File of class with all html predefined components for WebPortal
32
 */
33
34
use Dolibarr\Code\Societe\Classes\Societe;
35
require_once constant('DOL_DOCUMENT_ROOT') . '/core/class/discount.class.php';
36
require_once constant('DOL_DOCUMENT_ROOT') . '/webportal/class/html.formwebportal.class.php';
37
38
/**
39
 *    Class to manage generation of HTML components
40
 *    Only common components for WebPortal must be here.
41
 *
42
 */
43
class FormListWebPortal
44
{
45
    /**
46
     * @var string Action
47
     */
48
    public $action = '';
49
50
    /**
51
     * @var DoliDB Database
52
     */
53
    public $db;
54
55
    /**
56
     * @var Form  Instance of the Form
57
     */
58
    public $form;
59
60
    /**
61
     * @var CommonObject Object
62
     */
63
    public $object;
64
65
    /**
66
     * @var int Limit (-1 to get limit from conf, 0 no limit, or Nb to show)
67
     */
68
    public $limit = -1;
69
70
    /**
71
     * @var int Page (1 by default)
72
     */
73
    public $page = 1;
74
75
    /**
76
     * @var string Sort field
77
     */
78
    public $sortfield = '';
79
80
    /**
81
     * @var string Sort order
82
     */
83
    public $sortorder = '';
84
85
    /**
86
     * @var string Title key to translate
87
     */
88
    public $titleKey = '';
89
90
    /**
91
     * @var string Title desc key to translate
92
     */
93
    public $titleDescKey = '';
94
95
    /**
96
     * @var string Page context
97
     */
98
    public $contextpage = '';
99
100
    /**
101
     * @var array Search filters
102
     */
103
    public $search = array();
104
105
    /**
106
     * @var array Array of fields
107
     */
108
    public $arrayfields = array();
109
110
    /**
111
     * @var array Company static list (cache)
112
     */
113
    public $companyStaticList = array();
114
115
116
    /**
117
     * Constructor
118
     *
119
     * @param DoliDB $db Database handler
120
     */
121
    public function __construct($db)
122
    {
123
        $this->db = $db;
124
        $this->form = new FormWebPortal($this->db);
125
    }
126
127
    /**
128
     * Init
129
     *
130
     * @param   string      $elementEn      Element (english) : "propal", "order", "invoice"
131
     * @return  void
132
     */
133
    public function init($elementEn)
134
    {
135
        // keep compatibility
136
        if ($elementEn == 'commande') {
137
            $elementEn = 'order';
138
        } elseif ($elementEn == 'facture') {
139
            $elementEn = 'invoice';
140
        }
141
142
        // load module libraries
143
        dol_include_once('/webportal/class/webportal' . $elementEn . '.class.php');
144
145
        // Initialize technical objects
146
        $objectclass = 'WebPortal' . ucfirst($elementEn);
147
        $object = new $objectclass($this->db);
148
149
        // set form list
150
        $this->action = GETPOST('action', 'aZ09');
0 ignored issues
show
Documentation Bug introduced by
It seems like GETPOST('action', 'aZ09') can also be of type array or array or array. However, the property $action is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
151
        $this->object = $object;
152
        $this->limit = GETPOSTISSET('limit') ? GETPOSTINT('limit') : -1;
153
        $this->sortfield = GETPOST('sortfield', 'aZ09comma');
0 ignored issues
show
Documentation Bug introduced by
It seems like GETPOST('sortfield', 'aZ09comma') can also be of type array or array or array. However, the property $sortfield is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
154
        $this->sortorder = GETPOST('sortorder', 'aZ09comma');
0 ignored issues
show
Documentation Bug introduced by
It seems like GETPOST('sortorder', 'aZ09comma') can also be of type array or array or array. However, the property $sortorder is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
155
        $this->page = GETPOSTISSET('page') ? GETPOSTINT('page') : 1;
156
        $this->titleKey = $objectclass . 'ListTitle';
157
158
        // Initialize array of search criteria
159
        //$search_all = GETPOST('search_all', 'alphanohtml');
160
        $search = array();
161
        foreach ($object->fields as $key => $val) {
162
            if (GETPOST('search_' . $key, 'alpha') !== '') {
163
                $search[$key] = GETPOST('search_' . $key, 'alpha');
164
            }
165
            if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
166
                $postDateStart = GETPOST('search_' . $key . '_dtstart', 'alphanohtml');
167
                $postDateEnd = GETPOST('search_' . $key . '_dtend', 'alphanohtml');
168
                // extract date YYYY-MM-DD for year, month and day
169
                $dateStartArr = explode('-', $postDateStart);
170
                $dateEndArr = explode('-', $postDateEnd);
171
                if (count($dateStartArr) == 3) {
172
                    $dateStartYear = (int) $dateStartArr[0];
173
                    $dateStartMonth = (int) $dateStartArr[1];
174
                    $dateStartDay = (int) $dateStartArr[2];
175
                    $search[$key . '_dtstart'] = dol_mktime(0, 0, 0, $dateStartMonth, $dateStartDay, $dateStartYear);
176
                }
177
                if (count($dateEndArr) == 3) {
178
                    $dateEndYear = (int) $dateEndArr[0];
179
                    $dateEndMonth = (int) $dateEndArr[1];
180
                    $dateEndDay = (int) $dateEndArr[2];
181
                    $search[$key . '_dtend'] = dol_mktime(23, 59, 59, $dateEndMonth, $dateEndDay, $dateEndYear);
182
                }
183
            }
184
        }
185
        $this->search = $search;
186
187
        // List of fields to search into when doing a "search in all"
188
        //$fieldstosearchall = array();
189
190
        // Definition of array of fields for columns
191
        $arrayfields = array();
192
        foreach ($object->fields as $key => $val) {
193
            // If $val['visible']==0, then we never show the field
194
            if (!empty($val['visible'])) {
195
                $visible = (int) dol_eval($val['visible'], 1);
196
                $arrayfields['t.' . $key] = array(
197
                    'label' => $val['label'],
198
                    'checked' => (($visible < 0) ? 0 : 1),
199
                    'enabled' => (abs($visible) != 3 && (int) dol_eval($val['enabled'], 1)),
200
                    'position' => $val['position'],
201
                    'help' => isset($val['help']) ? $val['help'] : ''
202
                );
203
            }
204
        }
205
        if ($elementEn == 'invoice') {
206
            $arrayfields['remain_to_pay'] = array('type' => 'price', 'label' => 'RemainderToPay', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10000, 'help' => '',);
207
        }
208
        $arrayfields['download_link'] = array('label' => 'File', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10001, 'help' => '',);
209
        if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) {
210
            $arrayfields['signature_link'] = array('label' => 'Signature', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10002, 'help' => '',);
211
        }
212
213
        $object->fields = dol_sort_array($object->fields, 'position');
214
        //$arrayfields['anotherfield'] = array('type'=>'integer', 'label'=>'AnotherField', 'checked'=>1, 'enabled'=>1, 'position'=>90, 'csslist'=>'right');
215
        $arrayfields = dol_sort_array($arrayfields, 'position');
216
217
        $this->arrayfields = $arrayfields;
218
    }
219
220
    /**
221
     * Do actions
222
     *
223
     * @return  void
224
     */
225
    public function doActions()
226
    {
227
        $object = $this->object;
228
        $search = $this->search;
229
230
        // Purge search criteria
231
        if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers
232
            foreach ($object->fields as $key => $val) {
233
                $search[$key] = '';
234
                if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
235
                    $search[$key . '_dtstart'] = '';
236
                    $search[$key . '_dtend'] = '';
237
                }
238
            }
239
            $this->search = $search;
240
        }
241
    }
242
243
    /**
244
     * List for an element in the page context
245
     *
246
     * @param   Context     $context        Context object
247
     * @return  string      Html output
248
     */
249
    public function elementList($context)
250
    {
251
        global $conf, $hookmanager, $langs;
252
253
        $html = '';
254
255
        // initialize
256
        $action = $this->action;
257
        $object = $this->object;
258
        $limit = $this->limit;
259
        $page = $this->page;
260
        $sortfield = $this->sortfield;
261
        $sortorder = $this->sortorder;
262
        $titleKey = $this->titleKey;
263
        $contextpage = $this->contextpage;
264
        $search = $this->search;
265
        $arrayfields = $this->arrayfields;
266
        $elementEn = $object->element;
267
        if ($object->element == 'commande') {
268
            $elementEn = 'order';
269
        } elseif ($object->element == 'facture') {
270
            $elementEn = 'invoice';
271
        }
272
273
        // specific for invoice and remain to pay
274
        $discount = null;
275
        if ($elementEn == 'invoice') {
276
            $discount = new DiscountAbsolute($this->db);
277
        }
278
279
        // empty value for select
280
        $emptyValueKey = ($elementEn == 'order' ? -5 : -1);
281
282
        if ($limit < 0) {
283
            $limit = $conf->liste_limit;
284
        }
285
        if ($page <= 0) {
286
            $page = 1;
287
        }
288
        $offset = $limit * ($page - 1);
289
        if (!$sortfield) {
290
            reset($object->fields); // Reset is required to avoid key() to return null.
291
            $sortfield = 't.' . key($object->fields); // Set here default search field. By default 1st field in definition.
292
        }
293
        if (!$sortorder) {
294
            $sortorder = 'DESC';
295
        }
296
297
        $socid = (int) $context->logged_thirdparty->id;
298
299
        // Build and execute select
300
        // --------------------------------------------------------------------
301
        $sql = "SELECT ";
302
        $sql .= $object->getFieldList('t');
303
        $sql .= ", t.entity as element_entity";
304
        // Add fields from hooks
305
        $parameters = array();
306
        $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
307
        $sql .= $hookmanager->resPrint;
308
        $sql = preg_replace('/,\s*$/', '', $sql);
309
310
        $sqlfields = $sql; // $sql fields to remove for count total
311
312
        $sql .= " FROM " . $this->db->prefix() . $object->table_element . " as t";
313
        // Add table from hooks
314
        $parameters = array();
315
        $reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
316
        $sql .= $hookmanager->resPrint;
317
        if ($object->ismultientitymanaged == 1) {
318
            $sql .= " WHERE t.entity IN (" . getEntity($object->element, (GETPOSTINT('search_current_entity') ? 0 : 1)) . ")";
319
        } else {
320
            $sql .= " WHERE 1 = 1";
321
        }
322
        // filter on logged third-party
323
        $sql .= " AND t.fk_soc = " . ((int) $socid);
324
        // discard record with status draft
325
        $sql .= " AND t.fk_statut <> 0";
326
327
        foreach ($search as $key => $val) {
328
            if (array_key_exists($key, $object->fields)) {
329
                if (($key == 'status' || $key == 'fk_statut') && $search[$key] == $emptyValueKey) {
330
                    continue;
331
                }
332
                $mode_search = (($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key])) ? 1 : 0);
333
                if ((strpos($object->fields[$key]['type'], 'integer:') === 0) || (strpos($object->fields[$key]['type'], 'sellist:') === 0) || !empty($object->fields[$key]['arrayofkeyval'])) {
334
                    if ($search[$key] == "$emptyValueKey" || ($search[$key] === '0' && (empty($object->fields[$key]['arrayofkeyval']) || !array_key_exists('0', $object->fields[$key]['arrayofkeyval'])))) {
335
                        $search[$key] = '';
336
                    }
337
                    $mode_search = 2;
338
                }
339
                if ($search[$key] != '') {
340
                    $sql .= natural_search("t." . $this->db->escape($key), $search[$key], (($key == 'status' || $key == 'fk_statut') ? ($search[$key] < 0 ? 1 : 2) : $mode_search));
341
                }
342
            } else {
343
                if (preg_match('/(_dtstart|_dtend)$/', $key) && $search[$key] != '') {
344
                    $columnName = preg_replace('/(_dtstart|_dtend)$/', '', $key);
345
                    if (preg_match('/^(date|timestamp|datetime)/', $object->fields[$columnName]['type'])) {
346
                        if (preg_match('/_dtstart$/', $key)) {
347
                            $sql .= " AND t." . $this->db->escape($columnName) . " >= '" . $this->db->idate($search[$key]) . "'";
348
                        }
349
                        if (preg_match('/_dtend$/', $key)) {
350
                            $sql .= " AND t." . $this->db->escape($columnName) . " <= '" . $this->db->idate($search[$key]) . "'";
351
                        }
352
                    }
353
                }
354
            }
355
        }
356
        //if ($search_all) {
357
        //    $sql .= natural_search(array_keys($fieldstosearchall), $search_all);
358
        //}
359
        // Add where from hooks
360
        $parameters = array();
361
        $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
362
        $sql .= $hookmanager->resPrint;
363
364
        // Count total nb of records
365
        $nbtotalofrecords = 0;
366
        if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
367
            /* The fast and low memory method to get and count full list converts the sql into a sql count */
368
            $sqlforcount = preg_replace('/^' . preg_quote($sqlfields, '/') . '/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
369
            $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
370
            $resql = $this->db->query($sqlforcount);
371
            if ($resql) {
372
                $objforcount = $this->db->fetch_object($resql);
373
                $nbtotalofrecords = (int) $objforcount->nbtotalofrecords;
374
            } else {
375
                dol_print_error($this->db);
376
            }
377
378
            if ($offset > $nbtotalofrecords) {    // if total resultset is smaller than the paging size (filtering), goto and load page 1
379
                $page = 1;
380
                $offset = 0;
381
            }
382
383
            $this->db->free($resql);
384
        }
385
386
        // Complete request and execute it with limit
387
        $sql .= $this->db->order($sortfield, $sortorder);
388
        if ($limit) {
389
            $sql .= $this->db->plimit($limit, $offset);
390
        }
391
392
        $resql = $this->db->query($sql);
393
        if (!$resql) {
394
            dol_print_error($this->db);
395
            return '';
396
        }
397
398
        $num = $this->db->num_rows($resql);
399
        if ($limit > 0) {
400
            $nbpages = ceil($nbtotalofrecords / $limit);
401
        }
402
        if ($nbpages <= 0) {
403
            $nbpages = 1;
404
        }
405
406
        // make array[sort field => sort order] for this list
407
        $sortList = array();
408
        $sortFieldList = explode(",", $sortfield);
409
        $sortOrderList = explode(",", $sortorder);
410
        $sortFieldIndex = 0;
411
        if (!empty($sortFieldList)) {
412
            foreach ($sortFieldList as $sortField) {
413
                if (isset($sortOrderList[$sortFieldIndex])) {
414
                    $sortList[$sortField] = $sortOrderList[$sortFieldIndex];
415
                }
416
                $sortFieldIndex++;
417
            }
418
        }
419
420
        $param = '';
421
        $param .= '&contextpage=' . urlencode($contextpage);
422
        $param .= '&limit=' . $limit;
423
        foreach ($search as $key => $val) {
424
            if (is_array($search[$key])) {
425
                foreach ($search[$key] as $skey) {
426
                    if ($skey != '') {
427
                        $param .= '&search_' . $key . '[]=' . urlencode($skey);
428
                    }
429
                }
430
            } elseif (preg_match('/(_dtstart|_dtend)$/', $key) && !empty($val)) {
431
                $param .= '&search_' . $key . 'month=' . (GETPOSTINT('search_' . $key . 'month'));
432
                $param .= '&search_' . $key . 'day=' . (GETPOSTINT('search_' . $key . 'day'));
433
                $param .= '&search_' . $key . 'year=' . (GETPOSTINT('search_' . $key . 'year'));
434
            } elseif ($search[$key] != '') {
435
                $param .= '&search_' . $key . '=' . urlencode($search[$key]);
436
            }
437
        }
438
        // Add $param from hooks
439
        $parameters = array();
440
        $reshook = $hookmanager->executeHooks('printFieldListSearchParam', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
441
        $param .= $hookmanager->resPrint;
442
443
        $url_file = $context->getControllerUrl($context->controller);
444
        $html .= '<form method="POST" id="searchFormList" action="' . $url_file . '">' . "\n";
445
        $html .= $context->getFormToken();
446
        $html .= '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
447
        $html .= '<input type="hidden" name="action" value="list">';
448
        $html .= '<input type="hidden" name="sortfield" value="' . $sortfield . '">';
449
        $html .= '<input type="hidden" name="sortorder" value="' . $sortorder . '">';
450
        $html .= '<input type="hidden" name="page" value="' . $page . '">';
451
        $html .= '<input type="hidden" name="contextpage" value="' . $contextpage . '">';
452
453
        // pagination
454
        $pagination_param = $param . '&sortfield=' . $sortfield . '&sortorder=' . $sortorder;
455
        $html .= '<nav id="webportal-' . $elementEn . '-pagination">';
456
        $html .= '<ul>';
457
        $html .= '<li><strong>' . $langs->trans($titleKey) . '</strong> (' . $nbtotalofrecords . ')</li>';
458
        $html .= '</ul>';
459
460
        /* Generate pagination list */
461
        $html .= static::generatePageListNav($url_file . $pagination_param, $nbpages, $page);
462
463
        $html .= '</nav>';
464
465
        // table with search filters and column titles
466
        $html .= '<table id="webportal-' . $elementEn . '-list" responsive="scroll" role="grid">';
467
        // title and desc for table
468
        //if ($titleKey != '') {
469
        //    $html .= '<caption id="table-collapse-responsive">';
470
        //    $html .= $langs->trans($titleKey) . '<br/>';
471
        //    if ($titleDescKey != '') {
472
        //        $html .= '<small>' . $langs->trans($titleDescKey) . '</small>';
473
        //    }
474
        //    $html .= '</caption>';
475
        //}
476
477
        $html .= '<thead>';
478
479
        // Fields title search
480
        // --------------------------------------------------------------------
481
        $html .= '<tr role="search-row">';
482
        // Action column
483
        // if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
484
        $html .= '<td data-col="row-checkbox" >';
485
        $html .= '	<button class="btn-filter-icon btn-search-filters-icon" type="submit" name="button_search_x" value="x" aria-label="' . dol_escape_htmltag($langs->trans('Search')) . '" ></button>';
486
        $html .= '	<button class="btn-filter-icon btn-remove-search-filters-icon" type="submit" name="button_removefilter_x" value="x" aria-label="' . dol_escape_htmltag($langs->trans('RemoveSearchFilters')) . '"></button>';
487
        $html .= '</td>';
488
        // }
489
        foreach ($object->fields as $key => $val) {
490
            if (!empty($arrayfields['t.' . $key]['checked'])) {
491
                $html .= '<td data-label="' . $arrayfields['t.' . $key]['label'] . '" data-col="' . dol_escape_htmltag($key) . '" >';
492
                if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
493
                    $html .= $this->form->selectarray('search_' . $key, $val['arrayofkeyval'], (isset($search[$key]) ? $search[$key] : ''), $val['notnull'], 0, 0, '', 1, 0, 0, '', '');
494
                } elseif (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
495
                    $postDateStart = GETPOST('search_' . $key . '_dtstart', 'alphanohtml');
496
                    $postDateEnd = GETPOST('search_' . $key . '_dtend', 'alphanohtml');
497
498
                    $html .= '<div class="grid">';
499
                    $html .= $this->form->inputDate('search_' . $key . '_dtstart', $postDateStart ? $postDateStart : '', $langs->trans('From'));
500
                    $html .= '</div>';
501
                    $html .= '<div class="grid">';
502
                    $html .= $this->form->inputDate('search_' . $key . '_dtend', $postDateEnd ? $postDateEnd : '', $langs->trans('to'));
503
                    $html .= '</div>';
504
                } else {
505
                    $html .= '<input type="text" name="search_' . $key . '" value="' . dol_escape_htmltag(isset($search[$key]) ? $search[$key] : '') . '">';
506
                }
507
                $html .= '</td>';
508
            }
509
        }
510
        // Fields from hook
511
        $parameters = array('arrayfields' => $arrayfields);
512
        $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
513
        $html .= $hookmanager->resPrint;
514
        // Remain to pay
515
        if (!empty($arrayfields['remain_to_pay']['checked'])) {
516
            $html .= '<td data-label="' . $arrayfields['remain_to_pay']['label'] . '">';
517
            $html .= '</td>';
518
        }
519
        // Download link
520
        if (!empty($arrayfields['download_link']['checked'])) {
521
            $html .= '<td data-label="' . $arrayfields['download_link']['label'] . '">';
522
            $html .= '</td>';
523
        }
524
        $html .= '</tr>';
525
        // Signature link
526
        if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) {
527
            if (!empty($arrayfields['signature_link']['checked'])) {
528
                $html .= '<td data-label="' . $arrayfields['signature_link']['label'] . '">';
529
                $html .= '</td>';
530
            }
531
        }
532
533
        $totalarray = array();
534
        $totalarray['nbfield'] = 0;
535
536
        // Fields title label
537
        // --------------------------------------------------------------------
538
        $html .= '<tr>';
539
        // Action column
540
        // if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
541
        $html .= '<th  data-col="row-checkbox"  ></th>';
542
        $totalarray['nbfield']++;
543
        // }
544
        foreach ($object->fields as $key => $val) {
545
            $tableKey = 't.' . $key;
546
            if (!empty($arrayfields[$tableKey]['checked'])) {
547
                $tableOrder = '';
548
                if (array_key_exists($tableKey, $sortList)) {
549
                    $tableOrder = strtolower($sortList[$tableKey]);
550
                }
551
                $url_param = $url_file . '&sortfield=' . $tableKey . '&sortorder=' . ($tableOrder == 'desc' ? 'asc' : 'desc') . $param;
552
                $html .= '<th data-col="' . dol_escape_htmltag($key) . '"  scope="col"' . ($tableOrder != '' ? ' table-order="' . $tableOrder . '"' : '') . '>';
553
                $html .= '<a href="' . $url_param . '">';
554
                $html .= $langs->trans($arrayfields['t.' . $key]['label']);
555
                $html .= '</a>';
556
                $html .= '</th>';
557
                $totalarray['nbfield']++;
558
            }
559
        }
560
        // Remain to pay
561
        if (!empty($arrayfields['remain_to_pay']['checked'])) {
562
            $html .= '<th scope="col">';
563
            $html .= $langs->trans($arrayfields['remain_to_pay']['label']);
564
            $html .= '</th>';
565
            $totalarray['nbfield']++;
566
        }
567
        // Download link
568
        if (!empty($arrayfields['download_link']['checked'])) {
569
            $html .= '<th scope="col">';
570
            $html .= $langs->trans($arrayfields['download_link']['label']);
571
            $html .= '</th>';
572
            $totalarray['nbfield']++;
573
        }
574
        // Signature link
575
        if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) {
576
            if (!empty($arrayfields['signature_link']['checked'])) {
577
                $html .= '<th scope="col">';
578
                $html .= $langs->trans($arrayfields['signature_link']['label']);
579
                $html .= '</th>';
580
                $totalarray['nbfield']++;
581
            }
582
        }
583
584
        // Hook fields
585
        $parameters = array('arrayfields' => $arrayfields, 'sortfield' => $sortfield, 'sortorder' => $sortorder, 'totalarray' => &$totalarray);
586
        $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
587
        $html .= $hookmanager->resPrint;
588
        $html .= '</tr>';
589
590
        $html .= '</thead>';
591
592
        $html .= '<tbody>';
593
594
        // Store company
595
        $idCompany = (int) $socid;
596
        if (!isset($this->companyStaticList[$socid])) {
597
            $companyStatic = new Societe($this->db);
598
            $companyStatic->fetch($idCompany);
599
            $this->companyStaticList[$idCompany] = $companyStatic;
600
        }
601
        $companyStatic = $this->companyStaticList[$socid];
602
603
        // Loop on record
604
        // --------------------------------------------------------------------
605
        $i = 0;
606
        $totalarray = [
607
            'nbfield' => 0,
608
            'totalizable' => [],
609
        ];
610
        $imaxinloop = ($limit ? min($num, $limit) : $num);
611
        while ($i < $imaxinloop) {
612
            $obj = $this->db->fetch_object($resql);
613
            if (empty($obj)) {
614
                break; // Should not happen
615
            }
616
617
            // Store properties in $object
618
            $object->setVarsFromFetchObj($obj);
619
620
            // specific to get invoice status (depends on payment)
621
            $payment = -1;
622
            if ($elementEn == 'invoice') {
623
                // paid sum
624
                $payment = $object->getSommePaiement();
625
                $totalcreditnotes = $object->getSumCreditNotesUsed();
626
                $totaldeposits = $object->getSumDepositsUsed();
627
628
                // remain to pay
629
                $totalpay = $payment + $totalcreditnotes + $totaldeposits;
630
                $remaintopay = price2num($object->total_ttc - $totalpay);
631
                if ($object->status == Facture::STATUS_CLOSED && $object->close_code == 'discount_vat') {        // If invoice closed with discount for anticipated payment
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\WebPortal\Classes\Facture was not found. Did you mean Facture? If so, make sure to prefix the type with \.
Loading history...
632
                    $remaintopay = 0;
633
                }
634
                if ($object->type == Facture::TYPE_CREDIT_NOTE && $obj->paye == 1 && $discount) {
635
                    $remaincreditnote = $discount->getAvailableDiscounts($companyStatic, '', 'rc.fk_facture_source=' . $object->id);
636
                    $remaintopay = -$remaincreditnote;
637
                }
638
            }
639
640
            // Show line of result
641
            $html .= '<tr data-rowid="' . $object->id . '">';
642
            // if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
643
            $html .= '<td class="nowraponall">';
644
            $html .= '</td>';
645
            if (!$i) {
646
                $totalarray['nbfield']++;
647
            }
648
            // }
649
            foreach ($object->fields as $key => $val) {
650
                if (!empty($arrayfields['t.' . $key]['checked'])) {
651
                    $html .= '<td class="nowraponall" data-label="' . $arrayfields['t.' . $key]['label'] . '">';
652
                    if ($key == 'status' || $key == 'fk_statut') {
653
                        if ($elementEn == 'invoice') {
654
                            // specific to get invoice status (depends on payment)
655
                            $html .= $object->getLibStatut(5, $payment);
656
                        } else {
657
                            $html .= $object->getLibStatut(5);
658
                        }
659
                    } elseif ($key == 'rowid') {
660
                        $html .= $this->form->showOutputFieldForObject($object, $val, $key, $object->id, '');
661
                    } else {
662
                        $html .= $this->form->showOutputFieldForObject($object, $val, $key, $object->$key, '');
663
                    }
664
                    $html .= '</td>';
665
666
667
                    if (!$i) {
668
                        $totalarray['nbfield']++;
669
                    }
670
                    if (!empty($val['isameasure']) && $val['isameasure'] == 1) {
671
                        if (!$i) {
672
                            $totalarray['pos'][$totalarray['nbfield']] = 't.' . $key;
673
                        }
674
                        if (!isset($totalarray['val'])) {
675
                            $totalarray['val'] = array();
676
                        }
677
                        if (!isset($totalarray['val']['t.' . $key])) {
678
                            $totalarray['val']['t.' . $key] = 0;
679
                        }
680
                        $totalarray['val']['t.' . $key] += $object->$key;
681
                    }
682
                }
683
            }
684
            // Remain to pay
685
            if (!empty($arrayfields['remain_to_pay']['checked'])) {
686
                $html .= '<td class="nowraponall" data-label="' . $arrayfields['remain_to_pay']['label'] . '">';
687
                $html .= $this->form->showOutputFieldForObject($object, $arrayfields['remain_to_pay'], 'remain_to_pay', $remaintopay, '');
688
                //$html .= price($remaintopay);
689
                $html .= '</td>';
690
                if (!$i) {
691
                    $totalarray['nbfield']++;
692
                }
693
            }
694
            // Download link
695
            if (!empty($arrayfields['download_link']['checked'])) {
696
                $element = $object->element;
697
                $html .= '<td class="nowraponall" data-label="' . $arrayfields['download_link']['label'] . '">';
698
                $filename = dol_sanitizeFileName($obj->ref);
699
                $filedir = $conf->{$element}->multidir_output[$obj->element_entity] . '/' . dol_sanitizeFileName($obj->ref);
700
                $html .= $this->form->getDocumentsLink($element, $filename, $filedir);
701
                $html .= '</td>';
702
                if (!$i) {
703
                    $totalarray['nbfield']++;
704
                }
705
            }
706
            // Signature link
707
            if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) {
708
                if (!empty($arrayfields['signature_link']['checked'])) {
709
                    $html .= '<td class="nowraponall" data-label="' . $arrayfields['signature_link']['label'] . '">';
710
                    if ($object->fk_statut == Propal::STATUS_VALIDATED) {
711
                        $html .= $this->form->getSignatureLink('proposal', $object);
712
                    }
713
                    $html .= '</td>';
714
                    if (!$i) {
715
                        $totalarray['nbfield']++;
716
                    }
717
                }
718
            }
719
            // Fields from hook
720
            $parameters = array('arrayfields' => $arrayfields, 'object' => $object, 'obj' => $obj, 'i' => $i, 'totalarray' => &$totalarray);
721
            $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
722
            $html .= $hookmanager->resPrint;
723
724
            $html .= '</tr>';
725
726
            $i++;
727
        }
728
729
        // Move fields of totalizable into the common array pos and val
730
        if (!empty($totalarray['totalizable']) && is_array($totalarray['totalizable'])) {
731
            foreach ($totalarray['totalizable'] as $keytotalizable => $valtotalizable) {
732
                $totalarray['pos'][$valtotalizable['pos']] = $keytotalizable;
733
                $totalarray['val'][$keytotalizable] = isset($valtotalizable['total']) ? $valtotalizable['total'] : 0;
734
            }
735
        }
736
        // Show total line
737
        if (isset($totalarray['pos'])) {
738
            $html .= '<tr>';
739
            $i = 0;
740
            while ($i < $totalarray['nbfield']) {
741
                $i++;
742
                if (!empty($totalarray['pos'][$i])) {
743
                    $html .= '<td class="nowraponall essai">';
744
                    $html .= price(!empty($totalarray['val'][$totalarray['pos'][$i]]) ? $totalarray['val'][$totalarray['pos'][$i]] : 0);
745
                    $html .= '</td>';
746
                } else {
747
                    if ($i == 1) {
748
                        $html .= '<td>' . $langs->trans("Total") . '</td>';
749
                    } else {
750
                        $html .= '<td></td>';
751
                    }
752
                }
753
            }
754
            $html .= '</tr>';
755
        }
756
757
        // If no record found
758
        if ($num == 0) {
759
            $colspan = 1;
760
            foreach ($arrayfields as $key => $val) {
761
                if (!empty($val['checked'])) {
762
                    $colspan++;
763
                }
764
            }
765
            $html .= '<tr><td colspan="' . $colspan . '"><span class="opacitymedium">' . $langs->trans("NoRecordFound") . '</span></td></tr>';
766
        }
767
768
        $html .= '</tbody>';
769
770
        $this->db->free($resql);
771
772
        $parameters = array('arrayfields' => $arrayfields, 'sql' => $sql);
773
        $reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
774
        $html .= $hookmanager->resPrint;
775
776
        $html .= '</table>';
777
778
        $html .= '</form>';
779
780
        return $html;
781
    }
782
783
    /**
784
     * Generate with pagination navigaion
785
     *
786
     * @param   string  $url            Url of current page
787
     * @param   int     $nbPages        Total of pages results
788
     * @param   int     $currentPage    Number of current page
789
     * @return  string
790
     */
791
    public static function generatePageListNav(string $url, int $nbPages, int $currentPage)
792
    {
793
        global $langs;
794
795
        // Return nothing (no navigation bar), if there is only 1 page.
796
        if ($nbPages <= 1) {
797
            return '';
798
        }
799
800
        $pSep = strpos($url, '?') === false ? '?' : '&amp;';
801
802
        $html = '<ul class="pages-nav-list">';
803
804
        if ($currentPage > 1) {
805
            $html .= '<li><a class="pages-nav-list__icon --prev" aria-label="' . dol_escape_htmltag($langs->trans('AriaPrevPage')) . '" href="' . $url . $pSep . 'page=' . ($currentPage - 1) . '" ' . ($currentPage <= 1 ? ' disabled' : '') . '></a></li>';
806
        }
807
808
        $maxPaginItem = min($nbPages, 5);
809
        $minPageNum = max(1, $currentPage - 3);
810
        $maxPageNum = min($nbPages, $currentPage + 3);
811
812
        if ($minPageNum > 1) {
813
            $html .= '<li><a class="pages-nav-list__link ' . ($currentPage == 1 ? '--active' : '') . '" aria-label="' . dol_escape_htmltag($langs->trans('AriaPageX', 1)) . '" href="' . $url . $pSep . 'page=1" >1</a></li>';
814
            $html .= '<li>&hellip;</li>';
815
        }
816
817
        for ($p = $minPageNum; $p <= $maxPageNum; $p++) {
818
            $html .= '<li><a class="pages-nav-list__link ' . ($currentPage === $p ? '--active' : '') . '" aria-label="' . dol_escape_htmltag($langs->trans('AriaPageX', $p)) . '"  href="' . $url . $pSep . 'page=' . $p . '">' . $p . '</a></li>';
819
        }
820
821
        if ($maxPaginItem < $nbPages) {
822
            $html .= '<li>&hellip;</li>';
823
            $html .= '<li><a class="pages-nav-list__link ' . ($currentPage == $nbPages ? '--active' : '') . '" aria-label="' . dol_escape_htmltag($langs->trans('AriaPageX', $nbPages)) . '" href="' . $url . $pSep . 'page=' . $nbPages . '">' . $nbPages . '</a></li>';
824
        }
825
826
        if ($currentPage < $nbPages) {
827
            $html .= '<li><a class="pages-nav-list__icon --next" aria-label="' . dol_escape_htmltag($langs->trans('AriaNextPage')) . '" href="' . $url . $pSep . 'page=' . ($currentPage + 1) . '" ' . ($currentPage >= $nbPages ? ' disabled' : '') . '></a></li>';
828
        }
829
830
        $html .= '</ul>';
831
832
        return $html;
833
    }
834
}
835