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