dolibarr 21.0.0-alpha
customerMargins.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
3 * Copyright (C) 2014 Ferran Marcet <fmarcet@2byte.es>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
25// Load Dolibarr environment
26require '../main.inc.php';
27require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
28require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
29require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
30require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
31
32// Load translation files required by the page
33$langs->loadLangs(array('companies', 'bills', 'products', 'margins'));
34
35// Load variable for pagination
36$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
37$sortfield = GETPOST('sortfield', 'aZ09comma');
38$sortorder = GETPOST('sortorder', 'aZ09comma');
39$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
40if (empty($page) || $page == -1) {
41 $page = 0;
42} // If $page is not defined, or '' or -1
43$offset = $limit * $page;
44$pageprev = $page - 1;
45$pagenext = $page + 1;
46if (!$sortfield) {
47 $sortfield = "s.nom"; // Set here default search field
48}
49if (!$sortorder) {
50 $sortorder = "ASC";
51}
52
53$startdate = $enddate = '';
54if (GETPOST('startdatemonth')) {
55 $startdate = dol_mktime(0, 0, 0, GETPOSTINT('startdatemonth'), GETPOSTINT('startdateday'), GETPOSTINT('startdateyear'));
56}
57if (GETPOST('enddatemonth')) {
58 $enddate = dol_mktime(23, 59, 59, GETPOSTINT('enddatemonth'), GETPOSTINT('enddateday'), GETPOST('enddateyear'));
59}
60
61// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
62$object = new Societe($db);
63$hookmanager->initHooks(array('margincustomerlist'));
64
65// Security check
66$socid = GETPOSTINT('socid');
67$TSelectedProducts = GETPOST('products', 'array');
68$TSelectedCats = GETPOST('categories', 'array');
69
70if (!empty($user->socid)) {
71 $socid = $user->socid;
72}
73$result = restrictedArea($user, 'societe', '', '');
74$result = restrictedArea($user, 'margins');
75
76
77/*
78 * View
79 */
80
81$companystatic = new Societe($db);
82$invoicestatic = new Facture($db);
83
84$form = new Form($db);
85
86llxHeader('', $langs->trans("Margins").' - '.$langs->trans("Clients"), '', '', 0, 0, '', '', '', 'mod-margin page-customermargins');
87
88$text = $langs->trans("Margins");
89//print load_fiche_titre($text);
90
91// Show tabs
92$head = marges_prepare_head();
93
94$titre = $langs->trans("Margins");
95$picto = 'margin';
96
97
98print '<form method="post" name="sel" action="'.$_SERVER['PHP_SELF'].'">';
99print '<input type="hidden" name="token" value="'.newToken().'">';
100
101print dol_get_fiche_head($head, 'customerMargins', $titre, 0, $picto);
102
103print '<table class="border centpercent">';
104
105$client = false;
106if ($socid > 0) {
107 $soc = new Societe($db);
108 $soc->fetch($socid);
109
110 if ($soc->client) {
111 print '<tr><td class="titlefield">'.$langs->trans('ThirdPartyName').'</td>';
112 print '<td class="maxwidthonsmartphone" colspan="4">';
113 $filter = '(client:IN:1,3)';
114 print img_picto('', 'company').$form->select_company($socid, 'socid', $filter, 1, 0, 0);
115 print '</td></tr>';
116
117 $client = true;
118 if (!$sortorder) {
119 $sortorder = "DESC";
120 }
121 if (!$sortfield) {
122 $sortfield = "f.datef";
123 }
124 }
125} else {
126 print '<tr><td class="titlefield">'.$langs->trans('ThirdPartyName').'</td>';
127 print '<td class="maxwidthonsmartphone" colspan="4">';
128 print img_picto('', 'company').$form->select_company(null, 'socid', '((client:=:1) OR (client:=:3))', 1, 0, 0);
129 print '</td></tr>';
130}
131
132$sortfield = GETPOST('sortfield', 'aZ09comma');
133$sortorder = GETPOST('sortorder', 'aZ09comma');
134if (!$sortorder) {
135 $sortorder = "ASC";
136}
137if (!$sortfield) {
138 if ($client) {
139 $sortfield = "f.datef";
140 $sortorder = "DESC";
141 } else {
142 $sortfield = "s.nom";
143 $sortorder = "ASC";
144 }
145}
146
147// Products
148$TRes = $form->select_produits_list('', '', '', '', 0, '', 1, 2, 1, 0, '', 1);
149
150$TProducts = array();
151foreach ($TRes as $prod) {
152 $TProducts[$prod['key']] = $prod['label'];
153}
154
155print '<tr><td class="titlefield">'.$langs->trans('ProductOrService').'</td>';
156print '<td class="maxwidthonsmartpone" colspan="4">';
157print img_picto('', 'product').$form->multiselectarray('products', $TProducts, $TSelectedProducts, 0, 0, 'minwidth500');
158print '</td></tr>';
159
160// Categories
161$TCats = $form->select_all_categories('product', array(), '', 64, 0, 3);
162
163print '<tr>';
164print '<td class="titlefield">'.$langs->trans('Category').'</td>';
165print '<td class="maxwidthonsmartphone" colspan="4">';
166print img_picto('', 'category', 'class="pictofixedwidth"').$form->multiselectarray('categories', $TCats, $TSelectedCats, 0, 0, 'quatrevingtpercent widthcentpercentminusx');
167print '</td>';
168print '</tr>';
169
170// Start date
171print '<td>'.$langs->trans('DateStart').' ('.$langs->trans("DateValidation").')</td>';
172print '<td>';
173print $form->selectDate($startdate, 'startdate', 0, 0, 1, "sel", 1, 1);
174print '</td>';
175print '<td>'.$langs->trans('DateEnd').' ('.$langs->trans("DateValidation").')</td>';
176print '<td>';
177print $form->selectDate($enddate, 'enddate', 0, 0, 1, "sel", 1, 1);
178print '</td>';
179print '<td style="text-align: center;">';
180print '<input type="submit" class="button" value="'.dol_escape_htmltag($langs->trans('Refresh')).'" />';
181print '</td></tr>';
182
183print "</table>";
184
185print '<br>';
186
187print '<table class="border centpercent">';
188
189// Total Margin
190print '<tr><td class="titlefield">'.$langs->trans("TotalMargin").'</td><td colspan="4">';
191print '<span id="totalMargin" class="amount"></span> <span class="amount">'.$langs->getCurrencySymbol($conf->currency).'</span>'; // set by jquery (see below)
192print '</td></tr>';
193
194// Margin Rate
195if (getDolGlobalString('DISPLAY_MARGIN_RATES')) {
196 print '<tr><td>'.$langs->trans("MarginRate").'</td><td colspan="4">';
197 print '<span id="marginRate"></span>'; // set by jquery (see below)
198 print '</td></tr>';
199}
200
201// Mark Rate
202if (getDolGlobalString('DISPLAY_MARK_RATES')) {
203 print '<tr><td>'.$langs->trans("MarkRate").'</td><td colspan="4">';
204 print '<span id="markRate"></span>'; // set by jquery (see below)
205 print '</td></tr>';
206}
207
208print "</table>";
209
210print dol_get_fiche_end();
211
212print '</form>';
213
214$invoice_status_except_list = array(Facture::STATUS_DRAFT, Facture::STATUS_ABANDONED);
215
216$sql = "SELECT";
217$sql .= " s.rowid as socid, s.nom as name, s.code_client, s.client,";
218if ($client) {
219 $sql .= " f.rowid as facid, f.ref, f.total_ht, f.datef, f.paye, f.type, f.fk_statut as statut,";
220}
221$sql .= " sum(d.total_ht) as selling_price,";
222// Note: qty and buy_price_ht is always positive (if not, your database may be corrupted, you can update this)
223
224$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,";
225$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";
226
227$sql .= " FROM ".MAIN_DB_PREFIX."societe as s";
228$sql .= ", ".MAIN_DB_PREFIX."facture as f";
229$sql .= ", ".MAIN_DB_PREFIX."facturedet as d";
230if (!empty($TSelectedCats)) {
231 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_product as cp ON cp.fk_product=d.fk_product';
232}
233
234if (!$user->hasRight('societe', 'client', 'voir')) {
235 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
236}
237$sql .= " WHERE f.fk_soc = s.rowid";
238if ($socid > 0) {
239 $sql .= ' AND s.rowid = '.((int) $socid);
240}
241if (!$user->hasRight('societe', 'client', 'voir')) {
242 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
243}
244$sql .= " AND f.fk_statut NOT IN (".$db->sanitize(implode(', ', $invoice_status_except_list)).")";
245$sql .= ' AND s.entity IN ('.getEntity('societe').')';
246$sql .= ' AND f.entity IN ('.getEntity('invoice').')';
247$sql .= " AND d.fk_facture = f.rowid";
248$sql .= " AND (d.product_type = 0 OR d.product_type = 1)";
249if (!empty($TSelectedProducts)) {
250 $sql .= ' AND d.fk_product IN ('.$db->sanitize(implode(',', $TSelectedProducts)).')';
251}
252if (!empty($TSelectedCats)) {
253 $sql .= ' AND cp.fk_categorie IN ('.$db->sanitize(implode(',', $TSelectedCats)).')';
254}
255if (!empty($startdate)) {
256 $sql .= " AND f.datef >= '".$db->idate($startdate)."'";
257}
258if (!empty($enddate)) {
259 $sql .= " AND f.datef <= '".$db->idate($enddate)."'";
260}
261$sql .= " AND d.buy_price_ht IS NOT NULL";
262// 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.
263// We keep it with value ForceBuyingPriceIfNull = 2 for retroactive effect but results are unpredictable.
264if (getDolGlobalInt('ForceBuyingPriceIfNull') == 2) {
265 $sql .= " AND d.buy_price_ht <> 0";
266}
267if ($client) {
268 $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";
269} else {
270 $sql .= " GROUP BY s.rowid, s.nom, s.code_client, s.client";
271}
272$sql .= $db->order($sortfield, $sortorder);
273// TODO: calculate total to display then restore pagination
274//$sql.= $db->plimit($conf->liste_limit +1, $offset);
275
276$param = '&socid='.((int) $socid);
277if (GETPOSTINT('startdatemonth')) {
278 $param .= '&startdateyear='.GETPOSTINT('startdateyear');
279 $param .= '&startdatemonth='.GETPOSTINT('startdatemonth');
280 $param .= '&startdateday='.GETPOSTINT('startdateday');
281}
282if (GETPOSTINT('enddatemonth')) {
283 $param .= '&enddateyear='.GETPOSTINT('enddateyear');
284 $param .= '&enddatemonth='.GETPOSTINT('enddatemonth');
285 $param .= '&enddateday='.GETPOSTINT('enddateday');
286}
287$listofproducts = GETPOST('products', 'array:int');
288if (is_array($listofproducts)) {
289 foreach ($listofproducts as $val) {
290 $param .= '&products[]='.$val;
291 }
292}
293$listofcateg = GETPOST('categories', 'array:int');
294if (is_array($listofcateg)) {
295 foreach ($listofcateg as $val) {
296 $param .= '&categories[]='.$val;
297 }
298}
299
300$totalMargin = 0;
301$marginRate = '';
302$markRate = '';
303dol_syslog('margin::customerMargins.php', LOG_DEBUG);
304$result = $db->query($sql);
305if ($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;
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
437print '<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
448llxFooter();
449$db->close();
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:70
Class to manage invoices.
const STATUS_DRAFT
Draft status.
const STATUS_ABANDONED
Classified abandoned and no payment done.
Class to manage generation of HTML components Only common components must be here.
Class to manage third parties objects (customers, suppliers, prospects...)
llxFooter()
Footer empty.
Definition document.php:107
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $hideselectlimit=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
marges_prepare_head()
Return array of tabs to used on pages for third parties cards.
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.