dolibarr  16.0.1
agentMargins.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  * Copyright (C) 2015 Marcos GarcĂ­a <marcosgdf@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
26 require '../main.inc.php';
27 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
28 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
29 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
30 require_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 $mesg = '';
36 
37 // Load variable for pagination
38 $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit;
39 $sortfield = GETPOST('sortfield', 'aZ09comma');
40 $sortorder = GETPOST('sortorder', 'aZ09comma');
41 $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int');
42 if (empty($page) || $page == -1) {
43  $page = 0;
44 } // If $page is not defined, or '' or -1
45 $offset = $limit * $page;
46 $pageprev = $page - 1;
47 $pagenext = $page + 1;
48 if (!$sortorder) {
49  $sortorder = "ASC";
50 }
51 if (!$sortfield) {
52  if ($agentid > 0) {
53  $sortfield = "s.nom";
54  } else {
55  $sortfield = "u.lastname";
56  }
57 }
58 
59 $startdate = $enddate = '';
60 
61 $startdateday = GETPOST('startdateday', 'int');
62 $startdatemonth = GETPOST('startdatemonth', 'int');
63 $startdateyear = GETPOST('startdateyear', 'int');
64 $enddateday = GETPOST('enddateday', 'int');
65 $enddatemonth = GETPOST('enddatemonth', 'int');
66 $enddateyear = GETPOST('enddateyear', 'int');
67 
68 if (!empty($startdatemonth)) {
69  $startdate = dol_mktime(0, 0, 0, $startdatemonth, $startdateday, $startdateyear);
70 }
71 if (!empty($enddatemonth)) {
72  $enddate = dol_mktime(23, 59, 59, $enddatemonth, $enddateday, $enddateyear);
73 }
74 
75 // Security check
76 if ($user->rights->margins->read->all) {
77  $agentid = GETPOST('agentid', 'int');
78 } else {
79  $agentid = $user->id;
80 }
81 $result = restrictedArea($user, 'margins');
82 
83 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
84 $object = new User($db);
85 $hookmanager->initHooks(array('marginagentlist'));
86 
87 /*
88  * Actions
89  */
90 
91 // None
92 
93 
94 
95 /*
96  * View
97  */
98 
99 $userstatic = new User($db);
100 $companystatic = new Societe($db);
101 $invoicestatic = new Facture($db);
102 
103 $form = new Form($db);
104 
105 llxHeader('', $langs->trans("Margins").' - '.$langs->trans("Agents"));
106 
107 $text = $langs->trans("Margins");
108 //print load_fiche_titre($text);
109 
110 // Show tabs
111 $head = marges_prepare_head($user);
112 $titre = $langs->trans("Margins");
113 $picto = 'margin';
114 
115 print '<form method="post" name="sel" action="'.$_SERVER['PHP_SELF'].'">';
116 print '<input type="hidden" name="token" value="'.newToken().'">';
117 
118 print dol_get_fiche_head($head, 'agentMargins', $titre, 0, $picto);
119 
120 print '<table class="border centpercent">';
121 
122 print '<tr><td class="titlefield">'.$langs->trans('ContactOfInvoice').'</td>';
123 print '<td class="maxwidthonsmartphone" colspan="4">';
124 print img_picto('', 'user').$form->select_dolusers($agentid, 'agentid', 1, '', $user->rights->margins->read->all ? 0 : 1, '', '', 0, 0, 0, '', 0, '', 'maxwidth300');
125 print '</td></tr>';
126 
127 // Start date
128 print '<td>'.$langs->trans('DateStart').' ('.$langs->trans("DateValidation").')</td>';
129 print '<td>';
130 print $form->selectDate($startdate, 'startdate', '', '', 1, "sel", 1, 1);
131 print '</td>';
132 print '<td>'.$langs->trans('DateEnd').' ('.$langs->trans("DateValidation").')</td>';
133 print '<td>';
134 print $form->selectDate($enddate, 'enddate', '', '', 1, "sel", 1, 1);
135 print '</td>';
136 print '<td style="text-align: center;">';
137 print '<input type="submit" class="button" value="'.dol_escape_htmltag($langs->trans('Refresh')).'" />';
138 print '</td></tr>';
139 print "</table>";
140 
141 print dol_get_fiche_end();
142 
143 print '</form>';
144 
145 $invoice_status_except_list = array(Facture::STATUS_DRAFT, Facture::STATUS_ABANDONED);
146 
147 $sql = "SELECT";
148 $sql .= " s.rowid as socid, s.nom as name, s.code_client, s.client,";
149 $sql .= " u.rowid as agent, u.login, u.lastname, u.firstname,";
150 $sql .= " sum(d.total_ht) as selling_price,";
151 // Note: qty and buy_price_ht is always positive (if not your database may be corrupted, you can update this)
152 $sql .= " sum(".$db->ifsql('d.total_ht < 0', 'd.qty * d.buy_price_ht * -1 * (d.situation_percent / 100)', 'd.qty * d.buy_price_ht * (d.situation_percent / 100)').") as buying_price,";
153 $sql .= " sum(".$db->ifsql('d.total_ht < 0', '-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";
154 $sql .= " FROM ".MAIN_DB_PREFIX."societe as s";
155 $sql .= ", ".MAIN_DB_PREFIX."facture as f";
156 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."element_contact e ON e.element_id = f.rowid and e.statut = 4 and e.fk_c_type_contact = ".(empty($conf->global->AGENT_CONTACT_TYPE) ?-1 : $conf->global->AGENT_CONTACT_TYPE);
157 $sql .= ", ".MAIN_DB_PREFIX."facturedet as d";
158 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
159 $sql .= ", ".MAIN_DB_PREFIX."user as u";
160 $sql .= " WHERE f.fk_soc = s.rowid";
161 $sql .= ' AND f.entity IN ('.getEntity('invoice').')';
162 $sql .= " AND sc.fk_soc = f.fk_soc";
163 $sql .= " AND (d.product_type = 0 OR d.product_type = 1)";
164 if (!empty($conf->global->AGENT_CONTACT_TYPE)) {
165  $sql .= " AND ((e.fk_socpeople IS NULL AND sc.fk_user = u.rowid) OR (e.fk_socpeople IS NOT NULL AND e.fk_socpeople = u.rowid))";
166 } else {
167  $sql .= " AND sc.fk_user = u.rowid";
168 }
169 $sql .= " AND f.fk_statut NOT IN (".$db->sanitize(implode(', ', $invoice_status_except_list)).")";
170 $sql .= ' AND s.entity IN ('.getEntity('societe').')';
171 $sql .= " AND d.fk_facture = f.rowid";
172 if ($agentid > 0) {
173  if (!empty($conf->global->AGENT_CONTACT_TYPE)) {
174  $sql .= " AND ((e.fk_socpeople IS NULL AND sc.fk_user = ".((int) $agentid).") OR (e.fk_socpeople IS NOT NULL AND e.fk_socpeople = ".((int) $agentid)."))";
175  } else {
176  $sql .= " AND sc.fk_user = ".((int) $agentid);
177  }
178 }
179 if (!empty($startdate)) {
180  $sql .= " AND f.datef >= '".$db->idate($startdate)."'";
181 }
182 if (!empty($enddate)) {
183  $sql .= " AND f.datef <= '".$db->idate($enddate)."'";
184 }
185 $sql .= " AND d.buy_price_ht IS NOT NULL";
186 // 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.
187 // We keep it with value ForceBuyingPriceIfNull = 2 for retroactive effect but results are unpredicable.
188 if (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 2) {
189  $sql .= " AND d.buy_price_ht <> 0";
190 }
191 //if ($agentid > 0) $sql.= " GROUP BY s.rowid, s.nom, s.code_client, s.client, u.rowid, u.login, u.lastname, u.firstname";
192 //else $sql.= " GROUP BY u.rowid, u.login, u.lastname, u.firstname";
193 $sql .= " GROUP BY s.rowid, s.nom, s.code_client, s.client, u.rowid, u.login, u.lastname, u.firstname";
194 $sql .= $db->order($sortfield, $sortorder);
195 // TODO: calculate total to display then restore pagination
196 //$sql.= $db->plimit($conf->liste_limit +1, $offset);
197 
198 
199 print '<br>';
200 print '<span class="opacitymedium">'.$langs->trans("MarginPerSaleRepresentativeWarning").'</span><br>';
201 
202 $param = '';
203 if (!empty($agentid)) {
204  $param .= "&amp;agentid=".urlencode($agentid);
205 }
206 if (!empty($startdateday)) {
207  $param .= "&amp;startdateday=".urlencode($startdateday);
208 }
209 if (!empty($startdatemonth)) {
210  $param .= "&amp;startdatemonth=".urlencode($startdatemonth);
211 }
212 if (!empty($startdateyear)) {
213  $param .= "&amp;startdateyear=".urlencode($startdateyear);
214 }
215 if (!empty($enddateday)) {
216  $param .= "&amp;enddateday=".urlencode($enddateday);
217 }
218 if (!empty($enddatemonth)) {
219  $param .= "&amp;enddatemonth=".urlencode($enddatemonth);
220 }
221 if (!empty($enddateyear)) {
222  $param .= "&amp;enddateyear=".urlencode($enddateyear);
223 }
224 
225 
226 dol_syslog('margin::agentMargins.php', LOG_DEBUG);
227 $result = $db->query($sql);
228 if ($result) {
229  $num = $db->num_rows($result);
230 
231  print '<br>';
232  print_barre_liste($langs->trans("MarginDetails"), $page, $_SERVER["PHP_SELF"], "", $sortfield, $sortorder, '', $num, $num, '', 0, '', '', 0, 1);
233 
234  if ($conf->global->MARGIN_TYPE == "1") {
235  $labelcostprice = 'BuyingPrice';
236  } else { // value is 'costprice' or 'pmp'
237  $labelcostprice = 'CostPrice';
238  }
239 
240  $moreforfilter = '';
241 
242  $i = 0;
243  print '<div class="div-table-responsive">';
244  print '<table class="tagtable liste'.($moreforfilter ? " listwithfilterbefore" : "").'">'."\n";
245 
246  print '<tr class="liste_titre">';
247  if ($agentid > 0) {
248  print_liste_field_titre("Customer", $_SERVER["PHP_SELF"], "s.nom", "", $param, '', $sortfield, $sortorder);
249  } else {
250  print_liste_field_titre("SalesRepresentative", $_SERVER["PHP_SELF"], "u.lastname", "", $param, '', $sortfield, $sortorder);
251  }
252 
253  print_liste_field_titre("SellingPrice", $_SERVER["PHP_SELF"], "selling_price", "", $param, '', $sortfield, $sortorder, 'right ');
254  print_liste_field_titre($labelcostprice, $_SERVER["PHP_SELF"], "buying_price", "", $param, '', $sortfield, $sortorder, 'right ');
255  print_liste_field_titre("Margin", $_SERVER["PHP_SELF"], "marge", "", $param, '', $sortfield, $sortorder, 'right ');
256  if (!empty($conf->global->DISPLAY_MARGIN_RATES)) {
257  print_liste_field_titre("MarginRate", $_SERVER["PHP_SELF"], "", "", $param, '', $sortfield, $sortorder, 'right ');
258  }
259  if (!empty($conf->global->DISPLAY_MARK_RATES)) {
260  print_liste_field_titre("MarkRate", $_SERVER["PHP_SELF"], "", "", $param, '', $sortfield, $sortorder, 'right ');
261  }
262  print "</tr>\n";
263 
264  if ($num > 0) {
265  $group_list = array();
266  while ($objp = $db->fetch_object($result)) {
267  if ($agentid > 0) {
268  $group_id = $objp->socid;
269  } else {
270  $group_id = $objp->agent;
271  }
272 
273  if (!isset($group_list[$group_id])) {
274  if ($agentid > 0) {
275  $group_name = $objp->name;
276  $companystatic->id = $objp->socid;
277  $companystatic->name = $objp->name;
278  $companystatic->client = $objp->client;
279  $group_htmlname = $companystatic->getNomUrl(1, 'customer');
280  } else {
281  $group_name = $objp->lastname;
282  $userstatic->fetch($objp->agent);
283  $group_htmlname = $userstatic->getFullName($langs, 0, 0, 0);
284  }
285  $group_list[$group_id] = array('name' => $group_name, 'htmlname' => $group_htmlname, 'selling_price' => 0, 'buying_price' => 0, 'marge' => 0);
286  }
287 
288  $seller_nb = 1;
289  if ($objp->socid > 0) {
290  // sql nb sellers
291  $sql_seller = "SELECT COUNT(sc.rowid) as nb";
292  $sql_seller .= " FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc";
293  $sql_seller .= " WHERE sc.fk_soc = ".((int) $objp->socid);
294  $sql_seller .= " LIMIT 1";
295 
296  $resql_seller = $db->query($sql_seller);
297  if (!$resql_seller) {
298  dol_print_error($db);
299  } else {
300  if ($obj_seller = $db->fetch_object($resql_seller)) {
301  if ($obj_seller->nb > 0) {
302  $seller_nb = $obj_seller->nb;
303  }
304  }
305  }
306  }
307 
308  $group_list[$group_id]['selling_price'] += $objp->selling_price / $seller_nb;
309  $group_list[$group_id]['buying_price'] += $objp->buying_price / $seller_nb;
310  $group_list[$group_id]['marge'] += $objp->marge / $seller_nb;
311  }
312 
313  // sort group array by sortfield
314  if ($sortfield == 'u.lastname' || $sortfield == 's.nom') {
315  $sortfield = 'name';
316  }
317  $group_list = dol_sort_array($group_list, $sortfield, $sortorder);
318 
319  foreach ($group_list as $group_id => $group_array) {
320  $pa = $group_array['buying_price'];
321  $pv = $group_array['selling_price'];
322  $marge = $group_array['marge'];
323 
324  $marginRate = ($pa != 0) ? (100 * $marge / $pa) : '';
325  $markRate = ($pv != 0) ? (100 * $marge / $pv) : '';
326 
327  print '<tr class="oddeven">';
328  print "<td>".$group_array['htmlname']."</td>\n";
329  print '<td class="nowrap right"><span class="amount">'.price(price2num($pv, 'MT')).'</span></td>';
330  print '<td class="nowrap right"><span class="amount">'.price(price2num($pa, 'MT')).'</span></td>';
331  print '<td class="nowrap right"><span class="amount">'.price(price2num($marge, 'MT')).'</span></td>';
332  if (!empty($conf->global->DISPLAY_MARGIN_RATES)) {
333  print '<td class="nowrap right">'.(($marginRate === '') ? 'n/a' : price(price2num($marginRate, 'MT'))."%").'</td>';
334  }
335  if (!empty($conf->global->DISPLAY_MARK_RATES)) {
336  print '<td class="nowrap right">'.(($markRate === '') ? 'n/a' : price(price2num($markRate, 'MT'))."%").'</td>';
337  }
338  print "</tr>\n";
339 
340  $i++;
341  $cumul_achat += $pa;
342  $cumul_vente += $pv;
343  }
344  }
345 
346  // Show total margin
347  $totalMargin = $cumul_vente - $cumul_achat;
348 
349  $marginRate = ($cumul_achat != 0) ? (100 * $totalMargin / $cumul_achat) : '';
350  $markRate = ($cumul_vente != 0) ? (100 * $totalMargin / $cumul_vente) : '';
351 
352  print '<tr class="liste_total">';
353  print '<td>';
354  print $langs->trans('TotalMargin')."</td>";
355  print '<td class="nowrap right">'.price(price2num($cumul_vente, 'MT')).'</td>';
356  print '<td class="nowrap right">'.price(price2num($cumul_achat, 'MT')).'</td>';
357  print '<td class="nowrap right">'.price(price2num($totalMargin, 'MT')).'</td>';
358  if (!empty($conf->global->DISPLAY_MARGIN_RATES)) {
359  print '<td class="nowrap right">'.(($marginRate === '') ? 'n/a' : price(price2num($marginRate, 'MT'))."%").'</td>';
360  }
361  if (!empty($conf->global->DISPLAY_MARK_RATES)) {
362  print '<td class="nowrap right">'.(($markRate === '') ? 'n/a' : price(price2num($markRate, 'MT'))."%").'</td>';
363  }
364  print '</tr>';
365 
366  print '</table>';
367  print '</div>';
368 } else {
369  dol_print_error($db);
370 }
371 $db->free($result);
372 
373 print "\n".'<script type="text/javascript">
374 $(document).ready(function() {
375  console.log("Init some values");
376  $("#totalMargin").html("'.price(price2num($totalMargin, 'MT')).'");
377  $("#marginRate").html("'.(($marginRate === '') ? 'n/a' : price(price2num($marginRate, 'MT'))."%").'");
378  $("#markRate").html("'.(($markRate === '') ? 'n/a' : price(price2num($markRate, 'MT'))."%").'");
379 });
380 </script>'."\n";
381 
382 // End of page
383 llxFooter();
384 $db->close();
GETPOST($paramname, $check= 'alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
if($cancel &&!$id) if($action== 'add'&&!$cancel) if($action== 'delete') if($id) $form
Actions.
Definition: card.php:142
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm= 'auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
Class to manage Dolibarr users.
Definition: user.class.php:44
if(!defined('NOREQUIRESOC')) if(!defined('NOREQUIRETRAN')) if(!defined('NOCSRFCHECK')) if(!defined('NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) llxHeader()
Empty header.
Definition: wrapper.php:59
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...
print_barre_liste($titre, $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.
Class to manage generation of HTML components Only common components must be here.
marges_prepare_head()
Return array of tabs to used on pages for third parties cards.
Definition: margins.lib.php:59
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form...
Class to manage third parties objects (customers, suppliers, prospects...)
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt= '', $morecss= '', $marginleftonlyshort=2)
Show picto whatever it&#39;s its name (generic function)
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
const STATUS_DRAFT
Draft status.
restrictedArea($user, $features, $objectid=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.
const STATUS_ABANDONED
Classified abandoned and no payment done.
dol_sort_array(&$array, $index, $order= 'asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by second index function, which produces ascending (default) or descending output...
dol_get_fiche_head($links=array(), $active= '', $title= '', $notab=0, $picto= '', $pictoisfullpath=0, $morehtmlright= '', $morecss= '', $limittoshow=0, $moretabssuffix= '')
Show tabs of a record.
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_get_fiche_end($notab=0)
Return tab footer of a card.
Class to manage invoices.
llxFooter()
Empty footer.
Definition: wrapper.php:73