Issues (2811)

public/htdocs/margin/customerMargins.php (1 issue)

1
<?php
2
3
/* Copyright (C) 2012-2013  Christophe Battarel         <[email protected]>
4
 * Copyright (C) 2014		Ferran Marcet		        <[email protected]>
5
 * Copyright (C) 2024       Rafael San José             <[email protected]>
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
use Dolibarr\Code\Compta\Classes\Facture;
22
use Dolibarr\Code\Core\Classes\Form;
23
use Dolibarr\Code\Societe\Classes\Societe;
24
use Dolibarr\Lib\ViewMain;
25
26
/**
27
 *  \file       htdocs/margin/customerMargins.php
28
 *  \ingroup    margin
29
 *  \brief      Page des marges par client
30
 */
31
32
// Load Dolibarr environment
33
require constant('DOL_DOCUMENT_ROOT') . '/main.inc.php';
34
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/company.lib.php';
35
require_once constant('DOL_DOCUMENT_ROOT') . '/margin/lib/margins.lib.php';
36
37
// Load translation files required by the page
38
$langs->loadLangs(array('companies', 'bills', 'products', 'margins'));
39
40
// Load variable for pagination
41
$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
42
$sortfield = GETPOST('sortfield', 'aZ09comma');
43
$sortorder = GETPOST('sortorder', 'aZ09comma');
44
$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
45
if (empty($page) || $page == -1) {
46
    $page = 0;
47
}     // If $page is not defined, or '' or -1
48
$offset = $limit * $page;
49
$pageprev = $page - 1;
50
$pagenext = $page + 1;
51
if (!$sortfield) {
52
    $sortfield = "s.nom"; // Set here default search field
53
}
54
if (!$sortorder) {
55
    $sortorder = "ASC";
56
}
57
58
$startdate = $enddate = '';
59
if (GETPOST('startdatemonth')) {
60
    $startdate = dol_mktime(0, 0, 0, GETPOSTINT('startdatemonth'), GETPOSTINT('startdateday'), GETPOSTINT('startdateyear'));
61
}
62
if (GETPOST('enddatemonth')) {
63
    $enddate = dol_mktime(23, 59, 59, GETPOSTINT('enddatemonth'), GETPOSTINT('enddateday'), GETPOST('enddateyear'));
64
}
65
66
// Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
67
$object = new Societe($db);
68
$hookmanager->initHooks(array('margincustomerlist'));
69
70
// Security check
71
$socid = GETPOSTINT('socid');
72
$TSelectedProducts = GETPOST('products', 'array');
73
$TSelectedCats = GETPOST('categories', 'array');
74
75
if (!empty($user->socid)) {
76
    $socid = $user->socid;
77
}
78
$result = restrictedArea($user, 'societe', '', '');
79
$result = restrictedArea($user, 'margins');
80
81
/*
82
 * View
83
 */
84
85
$companystatic = new Societe($db);
86
$invoicestatic = new Facture($db);
87
88
$form = new Form($db);
89
90
ViewMain::llxHeader('', $langs->trans("Margins") . ' - ' . $langs->trans("Clients"));
91
92
$text = $langs->trans("Margins");
93
//print load_fiche_titre($text);
94
95
// Show tabs
96
$head = marges_prepare_head();
97
98
$titre = $langs->trans("Margins");
99
$picto = 'margin';
100
101
print '<form method="post" name="sel" action="' . $_SERVER['PHP_SELF'] . '">';
102
print '<input type="hidden" name="token" value="' . newToken() . '">';
103
104
print dol_get_fiche_head($head, 'customerMargins', $titre, 0, $picto);
105
106
print '<table class="border centpercent">';
107
108
$client = false;
109
if ($socid > 0) {
110
    $soc = new Societe($db);
111
    $soc->fetch($socid);
112
113
    if ($soc->client) {
114
        print '<tr><td class="titlefield">' . $langs->trans('ThirdPartyName') . '</td>';
115
        print '<td class="maxwidthonsmartphone" colspan="4">';
116
        $filter = '(client:IN:1,3)';
117
        print img_picto('', 'company') . $form->select_company($socid, 'socid', $filter, 1, 0, 0);
118
        print '</td></tr>';
119
120
        $client = true;
121
        if (!$sortorder) {
122
            $sortorder = "DESC";
123
        }
124
        if (!$sortfield) {
125
            $sortfield = "f.datef";
126
        }
127
    }
128
} else {
129
    print '<tr><td class="titlefield">' . $langs->trans('ThirdPartyName') . '</td>';
130
    print '<td class="maxwidthonsmartphone" colspan="4">';
131
    print img_picto('', 'company') . $form->select_company(null, 'socid', '((client:=:1) OR (client:=:3))', 1, 0, 0);
132
    print '</td></tr>';
133
}
134
135
$sortfield = GETPOST('sortfield', 'aZ09comma');
136
$sortorder = GETPOST('sortorder', 'aZ09comma');
137
if (!$sortorder) {
138
    $sortorder = "ASC";
139
}
140
if (!$sortfield) {
141
    if ($client) {
142
        $sortfield = "f.datef";
143
        $sortorder = "DESC";
144
    } else {
145
        $sortfield = "s.nom";
146
        $sortorder = "ASC";
147
    }
148
}
149
150
// Products
151
$TRes = $form->select_produits_list('', '', '', '', 0, '', 1, 2, 1, 0, '', 1);
152
153
$TProducts = array();
154
foreach ($TRes as $prod) {
155
    $TProducts[$prod['key']] = $prod['label'];
156
}
157
158
print '<tr><td class="titlefield">' . $langs->trans('ProductOrService') . '</td>';
159
print '<td class="maxwidthonsmartpone" colspan="4">';
160
print img_picto('', 'product') . $form->multiselectarray('products', $TProducts, $TSelectedProducts, 0, 0, 'minwidth500');
161
print '</td></tr>';
162
163
// Categories
164
$TCats = $form->select_all_categories('product', array(), '', 64, 0, 3);
165
166
print '<tr>';
167
print '<td class="titlefield">' . $langs->trans('Category') . '</td>';
168
print '<td class="maxwidthonsmartphone" colspan="4">';
169
print img_picto('', 'category', 'class="pictofixedwidth"') . $form->multiselectarray('categories', $TCats, $TSelectedCats, 0, 0, 'quatrevingtpercent widthcentpercentminusx');
170
print '</td>';
171
print '</tr>';
172
173
// Start date
174
print '<td>' . $langs->trans('DateStart') . ' (' . $langs->trans("DateValidation") . ')</td>';
175
print '<td>';
176
print $form->selectDate($startdate, 'startdate', 0, 0, 1, "sel", 1, 1);
177
print '</td>';
178
print '<td>' . $langs->trans('DateEnd') . ' (' . $langs->trans("DateValidation") . ')</td>';
179
print '<td>';
180
print $form->selectDate($enddate, 'enddate', 0, 0, 1, "sel", 1, 1);
181
print '</td>';
182
print '<td style="text-align: center;">';
183
print '<input type="submit" class="button" value="' . dol_escape_htmltag($langs->trans('Refresh')) . '" />';
184
print '</td></tr>';
185
186
print "</table>";
187
188
print '<br>';
189
190
print '<table class="border centpercent">';
191
192
// Total Margin
193
print '<tr><td class="titlefield">' . $langs->trans("TotalMargin") . '</td><td colspan="4">';
194
print '<span id="totalMargin" class="amount"></span> <span class="amount">' . $langs->getCurrencySymbol($conf->currency) . '</span>'; // set by jquery (see below)
195
print '</td></tr>';
196
197
// Margin Rate
198
if (getDolGlobalString('DISPLAY_MARGIN_RATES')) {
199
    print '<tr><td>' . $langs->trans("MarginRate") . '</td><td colspan="4">';
200
    print '<span id="marginRate"></span>'; // set by jquery (see below)
201
    print '</td></tr>';
202
}
203
204
// Mark Rate
205
if (getDolGlobalString('DISPLAY_MARK_RATES')) {
206
    print '<tr><td>' . $langs->trans("MarkRate") . '</td><td colspan="4">';
207
    print '<span id="markRate"></span>'; // set by jquery (see below)
208
    print '</td></tr>';
209
}
210
211
print "</table>";
212
213
print dol_get_fiche_end();
214
215
print '</form>';
216
217
$invoice_status_except_list = array(Facture::STATUS_DRAFT, Facture::STATUS_ABANDONED);
218
219
$sql = "SELECT";
220
$sql .= " s.rowid as socid, s.nom as name, s.code_client, s.client,";
221
if ($client) {
222
    $sql .= " f.rowid as facid, f.ref, f.total_ht, f.datef, f.paye, f.type, f.fk_statut as statut,";
223
}
224
$sql .= " sum(d.total_ht) as selling_price,";
225
// Note: qty and buy_price_ht is always positive (if not, your database may be corrupted, you can update this)
226
227
$sql .= " sum(" . $db->ifsql('(d.total_ht < 0 OR (d.total_ht = 0 AND f.type = 2))', '-1 * d.qty * d.buy_price_ht * (d.situation_percent / 100)', 'd.qty * d.buy_price_ht * (d.situation_percent / 100)') . ") as buying_price,";
228
$sql .= " sum(" . $db->ifsql('(d.total_ht < 0 OR (d.total_ht = 0 AND f.type = 2))', '-1 * (abs(d.total_ht) - (d.buy_price_ht * d.qty * (d.situation_percent / 100)))', 'd.total_ht - (d.buy_price_ht * d.qty * (d.situation_percent / 100))') . ") as marge";
229
230
$sql .= " FROM " . MAIN_DB_PREFIX . "societe as s";
231
$sql .= ", " . MAIN_DB_PREFIX . "facture as f";
232
$sql .= ", " . MAIN_DB_PREFIX . "facturedet as d";
233
if (!empty($TSelectedCats)) {
234
    $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'categorie_product as cp ON cp.fk_product=d.fk_product';
235
}
236
237
if (!$user->hasRight('societe', 'client', 'voir')) {
238
    $sql .= ", " . MAIN_DB_PREFIX . "societe_commerciaux as sc";
239
}
240
$sql .= " WHERE f.fk_soc = s.rowid";
241
if ($socid > 0) {
242
    $sql .= ' AND s.rowid = ' . ((int)$socid);
243
}
244
if (!$user->hasRight('societe', 'client', 'voir')) {
245
    $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = " . ((int)$user->id);
246
}
247
$sql .= " AND f.fk_statut NOT IN (" . $db->sanitize(implode(', ', $invoice_status_except_list)) . ")";
248
$sql .= ' AND s.entity IN (' . getEntity('societe') . ')';
249
$sql .= ' AND f.entity IN (' . getEntity('invoice') . ')';
250
$sql .= " AND d.fk_facture = f.rowid";
251
$sql .= " AND (d.product_type = 0 OR d.product_type = 1)";
252
if (!empty($TSelectedProducts)) {
253
    $sql .= ' AND d.fk_product IN (' . $db->sanitize(implode(',', $TSelectedProducts)) . ')';
254
}
255
if (!empty($TSelectedCats)) {
256
    $sql .= ' AND cp.fk_categorie IN (' . $db->sanitize(implode(',', $TSelectedCats)) . ')';
257
}
258
if (!empty($startdate)) {
259
    $sql .= " AND f.datef >= '" . $db->idate($startdate) . "'";
260
}
261
if (!empty($enddate)) {
262
    $sql .= " AND f.datef <= '" . $db->idate($enddate) . "'";
263
}
264
$sql .= " AND d.buy_price_ht IS NOT NULL";
265
// We should not use this here. Option ForceBuyingPriceIfNull should have effect only when inserting data. Once data is recorded, it must be used as it is for report.
266
// We keep it with value ForceBuyingPriceIfNull = 2 for retroactive effect but results are unpredictable.
267
if (getDolGlobalInt('ForceBuyingPriceIfNull') == 2) {
268
    $sql .= " AND d.buy_price_ht <> 0";
269
}
270
if ($client) {
271
    $sql .= " GROUP BY s.rowid, s.nom, s.code_client, s.client, f.rowid, f.ref, f.total_ht, f.datef, f.paye, f.type, f.fk_statut";
272
} else {
273
    $sql .= " GROUP BY s.rowid, s.nom, s.code_client, s.client";
274
}
275
$sql .= $db->order($sortfield, $sortorder);
276
// TODO: calculate total to display then restore pagination
277
//$sql.= $db->plimit($conf->liste_limit +1, $offset);
278
279
$param = '&socid=' . ((int)$socid);
280
if (GETPOSTINT('startdatemonth')) {
281
    $param .= '&startdateyear=' . GETPOSTINT('startdateyear');
282
    $param .= '&startdatemonth=' . GETPOSTINT('startdatemonth');
283
    $param .= '&startdateday=' . GETPOSTINT('startdateday');
284
}
285
if (GETPOSTINT('enddatemonth')) {
286
    $param .= '&enddateyear=' . GETPOSTINT('enddateyear');
287
    $param .= '&enddatemonth=' . GETPOSTINT('enddatemonth');
288
    $param .= '&enddateday=' . GETPOSTINT('enddateday');
289
}
290
$listofproducts = GETPOST('products', 'array:int');
291
if (is_array($listofproducts)) {
292
    foreach ($listofproducts as $val) {
293
        $param .= '&products[]=' . $val;
294
    }
295
}
296
$listofcateg = GETPOST('categories', 'array:int');
297
if (is_array($listofcateg)) {
298
    foreach ($listofcateg as $val) {
299
        $param .= '&categories[]=' . $val;
300
    }
301
}
302
303
dol_syslog('margin::customerMargins.php', LOG_DEBUG);
304
$result = $db->query($sql);
305
if ($result) {
306
    $num = $db->num_rows($result);
307
308
    print '<br>';
309
    // @phan-suppress-next-line PhanPluginSuspiciousParamPosition, PhanPluginSuspiciousParamOrder
310
    print_barre_liste($langs->trans("MarginDetails"), $page, $_SERVER["PHP_SELF"], "", $sortfield, $sortorder, '', $num, $num, '', 0, '', '', 0, 1);
311
312
    if (getDolGlobalString('MARGIN_TYPE') == "1") {
313
        $labelcostprice = 'BuyingPrice';
314
    } else { // value is 'costprice' or 'pmp'
315
        $labelcostprice = 'CostPrice';
316
    }
317
318
    $moreforfilter = '';
319
320
    $i = 0;
321
    print '<div class="div-table-responsive">';
322
    print '<table class="tagtable liste' . ($moreforfilter ? " listwithfilterbefore" : "") . '">' . "\n";
323
324
    print '<tr class="liste_titre">';
325
    if (!empty($client)) {
326
        print_liste_field_titre("Invoice", $_SERVER["PHP_SELF"], "f.ref", "", $param, '', $sortfield, $sortorder);
327
        print_liste_field_titre("DateInvoice", $_SERVER["PHP_SELF"], "f.datef", "", $param, 'align="center"', $sortfield, $sortorder);
328
    } else {
329
        print_liste_field_titre("Customer", $_SERVER["PHP_SELF"], "s.nom", "", $param, '', $sortfield, $sortorder);
330
    }
331
    print_liste_field_titre("SellingPrice", $_SERVER["PHP_SELF"], "selling_price", "", $param, 'align="right"', $sortfield, $sortorder);
332
    print_liste_field_titre($labelcostprice, $_SERVER["PHP_SELF"], "buying_price", "", $param, 'align="right"', $sortfield, $sortorder);
333
    print_liste_field_titre("Margin", $_SERVER["PHP_SELF"], "marge", "", $param, 'align="right"', $sortfield, $sortorder);
334
    if (getDolGlobalString('DISPLAY_MARGIN_RATES')) {
335
        print_liste_field_titre("MarginRate", $_SERVER["PHP_SELF"], "", "", $param, 'align="right"', $sortfield, $sortorder);
336
    }
337
    if (getDolGlobalString('DISPLAY_MARK_RATES')) {
338
        print_liste_field_titre("MarkRate", $_SERVER["PHP_SELF"], "", "", $param, 'align="right"', $sortfield, $sortorder);
339
    }
340
    print "</tr>\n";
341
342
    $cumul_achat = 0;
343
    $cumul_vente = 0;
344
345
    if ($num > 0) {
346
        while ($i < $num /*&& $i < $conf->liste_limit*/) {
347
            $objp = $db->fetch_object($result);
348
349
            $pa = $objp->buying_price;
350
            $pv = $objp->selling_price;
351
            $marge = $objp->marge;
352
353
            if ($marge < 0) {
354
                $marginRate = ($pa != 0) ? -1 * (100 * $marge / $pa) : '';
355
                $markRate = ($pv != 0) ? -1 * (100 * $marge / $pv) : '';
356
            } else {
357
                $marginRate = ($pa != 0) ? (100 * $marge / $pa) : '';
358
                $markRate = ($pv != 0) ? (100 * $marge / $pv) : '';
359
            }
360
361
            print '<tr class="oddeven">';
362
            if ($client) {
363
                $invoicestatic->id = $objp->facid;
364
                $invoicestatic->ref = $objp->ref;
365
                $invoicestatic->statut = $objp->statut;
0 ignored issues
show
Deprecated Code introduced by
The property Dolibarr\Core\Base\CommonObject::$statut has been deprecated: Use $status instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

365
                /** @scrutinizer ignore-deprecated */ $invoicestatic->statut = $objp->statut;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
366
                $invoicestatic->type = $objp->type;
367
368
                print '<td>';
369
                print $invoicestatic->getNomUrl(1);
370
                print '</td>';
371
                print '<td class="center">';
372
                print dol_print_date($db->jdate($objp->datef), 'day') . '</td>';
373
            } else {
374
                $companystatic->id = $objp->socid;
375
                $companystatic->name = $objp->name;
376
                $companystatic->client = $objp->client;
377
378
                print '<td>' . $companystatic->getNomUrl(1, 'margin') . '</td>';
379
            }
380
381
            print '<td class="nowrap right"><span class="amount">' . price(price2num($pv, 'MT')) . '</span></td>';
382
            print '<td class="nowrap right"><span class="amount">' . price(price2num($pa, 'MT')) . '</span></td>';
383
            print '<td class="nowrap right"><span class="amount">' . price(price2num($marge, 'MT')) . '</span></td>';
384
            if (getDolGlobalString('DISPLAY_MARGIN_RATES')) {
385
                print '<td class="nowrap right">' . (($marginRate === '') ? 'n/a' : price(price2num($marginRate, 'MT')) . "%") . '</td>';
386
            }
387
            if (getDolGlobalString('DISPLAY_MARK_RATES')) {
388
                print '<td class="nowrap right">' . (($markRate === '') ? 'n/a' : price(price2num($markRate, 'MT')) . "%") . '</td>';
389
            }
390
            print "</tr>\n";
391
392
            $i++;
393
            $cumul_achat += $objp->buying_price;
394
            $cumul_vente += $objp->selling_price;
395
        }
396
    }
397
398
    // affichage totaux marges
399
400
    $totalMargin = $cumul_vente - $cumul_achat;
401
    /*if ($totalMargin < 0)
402
    {
403
        $marginRate = ($cumul_achat != 0)?-1*(100 * $totalMargin / $cumul_achat):'';
404
        $markRate = ($cumul_vente != 0)?-1*(100 * $totalMargin / $cumul_vente):'';
405
    }
406
    else
407
    {*/
408
    $marginRate = ($cumul_achat != 0) ? (100 * $totalMargin / $cumul_achat) : '';
409
    $markRate = ($cumul_vente != 0) ? (100 * $totalMargin / $cumul_vente) : '';
410
    //}
411
412
    print '<tr class="liste_total">';
413
    if ($client) {
414
        print '<td colspan="2">';
415
    } else {
416
        print '<td>';
417
    }
418
    print $langs->trans('TotalMargin') . "</td>";
419
    print '<td class="nowrap right">' . price(price2num($cumul_vente, 'MT')) . '</td>';
420
    print '<td class="nowrap right">' . price(price2num($cumul_achat, 'MT')) . '</td>';
421
    print '<td class="nowrap right">' . price(price2num($totalMargin, 'MT')) . '</td>';
422
    if (getDolGlobalString('DISPLAY_MARGIN_RATES')) {
423
        print '<td class="nowrap right">' . (($marginRate === '') ? 'n/a' : price(price2num($marginRate, 'MT')) . "%") . '</td>';
424
    }
425
    if (getDolGlobalString('DISPLAY_MARK_RATES')) {
426
        print '<td class="nowrap right">' . (($markRate === '') ? 'n/a' : price(price2num($markRate, 'MT')) . "%") . '</td>';
427
    }
428
    print '</tr>';
429
430
    print '</table>';
431
    print '</div>';
432
} else {
433
    dol_print_error($db);
434
}
435
$db->free($result);
436
437
print '<script type="text/javascript">
438
$(document).ready(function() {
439
	console.log("Init some values");
440
	$("#totalMargin").html("' . price(price2num($totalMargin, 'MT')) . '");
441
	$("#marginRate").html("' . (($marginRate === '') ? 'n/a' : price(price2num($marginRate, 'MT')) . "%") . '");
442
	$("#markRate").html("' . (($markRate === '') ? 'n/a' : price(price2num($markRate, 'MT')) . "%") . '");
443
});
444
</script>
445
';
446
447
// End of page
448
ViewMain::llxFooter();
449
$db->close();
450