Merged changes from master branch up to current state.
[fa-stable.git] / includes / ui / allocation_cart.inc
1 <?php
2 /**********************************************************************
3     Copyright (C) FrontAccounting, LLC.
4         Released under the terms of the GNU General Public License, GPL, 
5         as published by the Free Software Foundation, either version 3 
6         of the License, or (at your option) any later version.
7     This program is distributed in the hope that it will be useful,
8     but WITHOUT ANY WARRANTY; without even the implied warranty of
9     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
10     See the License here <http://www.gnu.org/licenses/gpl-3.0.html>.
11 ***********************************************************************/
12 /*
13         Class for supplier/customer payment/credit allocations edition
14         and related helpers.
15 */
16 //-----------------------------------------------------------------------------------
17
18 class allocation 
19 {
20
21         var $trans_no; 
22         var $type;
23         var $person_id = '';
24         var $person_name = '';
25         var $person_type;       // PT_SUPPLIER/PT_CUSTOMER
26         var $person_curr;
27         var $date_;
28         var $amount = 0; /*Total amount of the transaction in FX */
29         var $currency;
30
31         var $allocs; /*array of transactions allocated to */
32
33         function allocation($type, $trans_no, $person_id = null, $person_type_id=null)
34         {
35                 $this->allocs = array();
36                 
37                 $this->trans_no = $trans_no;
38                 $this->type = $type;
39                 if ($person_id)
40                         $this->set_person($person_id, $person_type_id);
41
42                 $this->read($type, $trans_no, $person_id, $person_type_id); // read payment or credit
43         }
44
45         function set_person($person_id, $person_type)
46         {
47                 $this->person_id = $person_id;
48                 $this->person_type = $person_type;
49                 $this->person_curr = $person_type == PT_SUPPLIER ?
50                         get_supplier_currency($person_id) : get_customer_currency($person_id);
51                 return $this->person_curr;
52         }
53
54         function add_item($type, $type_no, $date_, $due_date, $amount, $amount_allocated, 
55                 $current_allocated, $ref)
56         {
57                 if (floatcmp($amount, 0))
58                 {
59                         $this->allocs[count($this->allocs)] = new allocation_item($type, $type_no, 
60                                 $date_, $due_date, $amount, $amount_allocated, $current_allocated, $ref);
61                         return true;
62                 } 
63                 else 
64                 {
65                         return false;
66                 }
67         }
68         
69         function update_item($index, $type, $type_no, $date_, $due_date, 
70                 $amount, $amount_allocated, $current_allocated, $ref)
71         {
72                 if (floatcmp($amount, 0))
73                 {
74                         $this->allocs[$index] = new allocation_item($type, $type_no, 
75                                 $date_, $due_date, $amount, $amount_allocated, $current_allocated, $ref);
76                         return true;
77                 } 
78                 else 
79                 {
80                         return false;
81                 }
82         }
83         
84         function add_or_update_item($type, $type_no, $date_, $due_date, 
85                 $amount, $amount_allocated, $current_allocated, $ref)
86         {
87                 for ($i = 0; $i < count($this->allocs); $i++) 
88                 {
89                         $item = $this->allocs[$i];
90                         if (($item->type == $type) && ($item->type_no == $type_no)) 
91                         {
92                                 return $this->update_item($i, $type, $type_no, $date_, $due_date, 
93                                         $amount, $amount_allocated, $current_allocated, $ref);
94                         }
95                 }
96         return $this->add_item($type, $type_no, $date_, $due_date, 
97                 $amount, $amount_allocated, $current_allocated, $ref);
98         }
99
100         //
101         //      Read payment or credit current/available allocations to cart.
102         //
103         // FIXME - read all transactions below twice seems to be suboptimal
104         //
105         function read($type = null, $trans_no = 0, $person_id=null, $person_type_id=null)
106         {
107                 if ($type !== null) {   // otherwise re-read
108                         $type = $this->type;
109                         $trans_no = $this->trans_no;
110
111                         if (isset($person_type_id))
112                         {
113                                 $this->person_type = $person_type_id;
114                                 $this->person_id = $person_id;
115                         } else { // guess person_type_id
116                                 if (in_array($type, array(ST_BANKPAYMENT, ST_BANKDEPOSIT)))
117                                 {
118                                         $bank_trans = db_fetch(get_bank_trans($type, $trans_no));
119                                         $this->person_type = $bank_trans['person_type_id'];
120                                 } else
121                                         $this->person_type = in_array($type, array(ST_SUPPCREDIT, ST_SUPPAYMENT)) ? PT_SUPPLIER : PT_CUSTOMER;
122                         }
123
124                         if ($trans_no) {
125                                 $trans = $this->person_type == PT_SUPPLIER ? get_supp_trans($trans_no, $type, $person_id)
126                                         : get_customer_trans($trans_no, $type, $person_id);
127
128                                 $this->person_id = $trans[$this->person_type == PT_SUPPLIER ? 'supplier_id':'debtor_no'];
129                                 $this->person_name = $trans[$this->person_type == PT_SUPPLIER ? "supplier_name":"DebtorName"];
130                                 $this->date_ = sql2date($trans["tran_date"]);
131                                 $this->person_curr = $trans['curr_code'];
132                                 $this->currency = isset($trans['bank_curr_code']) ? $trans['bank_curr_code'] : $trans['curr_code'];
133                                 $this->bank_amount = isset($trans["bank_amount"]) ? $trans["bank_amount"] : $trans["Total"]; // not set for journal entry
134                                 $this->amount = $trans["Total"];
135                         }
136                 }
137         /* Now populate the array of possible (and previous actual) allocations 
138                 for this customer/supplier. First get the transactions that have 
139                 outstanding balances ie Total-alloc >0 */
140
141                 $this->allocs = array();
142                 if ($this->person_id)
143                 {
144                         if ($this->person_type==PT_SUPPLIER)
145                                 $trans_items = get_allocatable_to_supp_transactions($this->person_id);
146                         else
147                                 $trans_items = get_allocatable_to_cust_transactions($this->person_id);
148                         while ($myrow = db_fetch($trans_items))
149                         {
150                                 $this->add_item($myrow["type"], $myrow["trans_no"],
151                                         sql2date($myrow["tran_date"]),
152                                         sql2date($myrow["due_date"]),
153                                         $myrow["Total"], // trans total
154                                         $myrow["alloc"], // trans total allocated
155                                         0,
156                                         $myrow["reference"]); // this allocation
157                         }
158                 }
159                 if ($this->trans_no == 0) return; // this is new payment
160
161         /* Now get trans that might have previously been allocated to by this trans
162         NB existing entries where still some of the trans outstanding entered from
163         above logic will be overwritten with the prev alloc detail below */
164
165                 if ($this->person_type==PT_SUPPLIER)
166                         $trans_items = get_allocatable_to_supp_transactions($this->person_id, 
167                                 $this->trans_no, $this->type);
168                 else
169                         $trans_items = get_allocatable_to_cust_transactions($this->person_id, 
170                                 $this->trans_no, $this->type);
171
172                 while ($myrow = db_fetch($trans_items))
173                 {
174                         $this->add_or_update_item ($myrow["type"], $myrow["trans_no"],
175                                 sql2date($myrow["tran_date"]),
176                                 sql2date($myrow["due_date"]),
177                                 $myrow["Total"],
178                                 $myrow["alloc"] - $myrow["amt"], $myrow["amt"], $myrow["reference"]);
179                 }
180         }
181         //
182         //      Update allocations in database.
183         //
184         function write()
185         {
186                 global  $no_exchange_variations;
187
188                 begin_transaction();
189
190                 if ($this->person_type == PT_SUPPLIER)
191                         clear_supp_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
192                 else
193                         clear_cust_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
194
195                 // now add the new allocations
196                 $total_allocated = 0;
197                 $dec = user_price_dec();
198                 foreach ($this->allocs as $alloc_item)
199                 {
200                         if ($alloc_item->current_allocated > 0)
201                         {
202                                 $amount = round($alloc_item->current_allocated, $dec);
203
204                                 if ($this->person_type == PT_SUPPLIER) {
205                                         add_supp_allocation($amount,
206                                                 $this->type, $this->trans_no,
207                                         $alloc_item->type, $alloc_item->type_no, $this->person_id, $this->date_);
208
209                                         update_supp_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
210                                 } else {
211                                         add_cust_allocation($amount,
212                                                 $this->type, $this->trans_no,
213                                         $alloc_item->type, $alloc_item->type_no, $this->person_id, $this->date_);
214
215                                         update_debtor_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
216                                 }
217                                 // Exchange Variations Joe Hunt 2008-09-20 ////////////////////
218                                 if ($alloc_item->type != ST_SALESORDER && !@$no_exchange_variations
219                                         && $alloc_item->type != ST_PURCHORDER && $alloc_item->type != ST_JOURNAL && $this->type != ST_JOURNAL)
220                                         exchange_variation($this->type, $this->trans_no,
221                                                 $alloc_item->type, $alloc_item->type_no, $this->date_,
222                                                 $amount, $this->person_type);
223
224                                 //////////////////////////////////////////////////////////////
225                                 $total_allocated += $alloc_item->current_allocated;
226                         }
227
228                 }  /*end of the loop through the array of allocations made */
229                 if ($this->person_type == PT_SUPPLIER)
230                         update_supp_trans_allocation($this->type, $this->trans_no, $this->person_id);
231                 else
232                         update_debtor_trans_allocation($this->type,     $this->trans_no, $this->person_id);
233
234                 commit_transaction();
235
236         }
237
238
239
240 //-----------------------------------------------------------------------------------
241
242 class allocation_item 
243 {
244
245         var $type;
246         var $type_no;
247         
248         var $date_;
249         var $due_date;
250         
251         var $amount_allocated;
252         var $amount;
253         var $ref;
254         
255         var $current_allocated;
256         
257         function allocation_item ($type, $type_no, $date_, $due_date, $amount, 
258                 $amount_allocated, $current_allocated, $ref)
259         {
260
261                 $this->type = $type;
262                 $this->type_no = $type_no;
263
264                 $this->ref = $ref;
265
266                 $this->date_ = $date_;
267                 $this->due_date = $due_date;
268                 
269                 $this->amount = $amount;
270                 $this->amount_allocated = $amount_allocated;
271                 $this->current_allocated = $current_allocated;
272         }
273 }
274
275 //--------------------------------------------------------------------------------
276
277 function show_allocatable($show_totals) {
278
279         global $systypes_array;
280         
281     $k = $counter = $total_allocated = 0;
282
283         $cart = $_SESSION['alloc'];
284         $supp_ref = in_array($cart->type, array(ST_SUPPCREDIT, ST_SUPPAYMENT, ST_BANKPAYMENT));
285
286         if (count($cart->allocs)) 
287         {
288 //              if ($cart->currency != $cart->person_curr)
289                         display_heading(sprintf(_("Allocated amounts in %s:"), $cart->person_curr));
290                 start_table(TABLESTYLE, "width='60%'");
291                 $th = array(_("Transaction Type"), _("#"), $supp_ref ? _("Supplier Ref"): _("Ref"), _("Date"), _("Due Date"), _("Amount"),
292                         _("Other Allocations"), _("Left to Allocate"), _("This Allocation"),'','');
293
294                 table_header($th);
295
296                 foreach ($cart->allocs as $id => $alloc_item)
297                 {
298                     if (floatcmp(abs($alloc_item->amount), $alloc_item->amount_allocated))
299                     {
300                                 alt_table_row_color($k);
301                         label_cell($systypes_array[$alloc_item->type]);
302                                 label_cell(get_trans_view_str($alloc_item->type, $alloc_item->type_no));
303                                 label_cell($alloc_item->ref);
304                         label_cell($alloc_item->date_, "align=right");
305                         label_cell($alloc_item->due_date, "align=right");
306                         amount_cell(abs($alloc_item->amount));
307                                 amount_cell($alloc_item->amount_allocated);
308
309                         $_POST['amount' . $id] = price_format($alloc_item->current_allocated);
310
311                         $un_allocated = round((abs($alloc_item->amount) - $alloc_item->amount_allocated), 6);
312                         amount_cell($un_allocated, false,'', 'maxval'.$id);
313                         amount_cells(null, "amount" . $id);//, input_num('amount' . $id));
314                                 label_cell("<a href='#' name=Alloc$id onclick='allocate_all(this.name.substr(5));return true;'>"
315                                          . _("All") . "</a>");
316                                 label_cell("<a href='#' name=DeAll$id onclick='allocate_none(this.name.substr(5));return true;'>"
317                                          . _("None") . "</a>".hidden("un_allocated" . $id, 
318                                          price_format($un_allocated), false));
319                                 end_row();
320
321                         $total_allocated += input_num('amount' . $id);
322                         }
323                 }
324                 if ($show_totals) {
325                 label_row(_("Total Allocated"), price_format($total_allocated),
326                         "colspan=8 align=right", "align=right id='total_allocated'", 3);
327 /*
328                         $amount = $_SESSION['alloc']->amount;
329
330                         if ($_SESSION['alloc']->type == ST_SUPPCREDIT
331                                 || $_SESSION['alloc']->type == ST_SUPPAYMENT
332                                 ||  $_SESSION['alloc']->type == ST_BANKPAYMENT || 
333                                 ($_SESSION['alloc']->type == ST_JOURNAL && $_SESSION['alloc']->person_type == PT_SUPPLIER))
334                                 $amount = -$amount;
335 */
336                         $amount = abs($cart->amount);
337
338                         if (floatcmp($amount, $total_allocated) < 0)
339                 {
340                         $font1 = "<font color=red>";
341                         $font2 = "</font>";
342             }
343                 else
344                         $font1 = $font2 = "";
345                         $left_to_allocate = price_format($amount - $total_allocated);
346                 label_row(_("Left to Allocate"), $font1 . $left_to_allocate . $font2, 
347                                 "colspan=8 align=right", "nowrap align=right id='left_to_allocate'",
348                                  3);
349                 }
350                 end_table(1);
351         }
352         hidden('TotalNumberOfAllocs', count($cart->allocs));
353 }
354 //--------------------------------------------------------------------------------
355
356 function check_allocations()
357 {
358         global $SysPrefs;
359
360         $total_allocated = 0;
361
362         for ($counter = 0; $counter < get_post("TotalNumberOfAllocs"); $counter++)
363         {
364                 if (!isset($_POST['amount'.$counter])) continue;
365                 if (!check_num('amount' . $counter, 0))
366                 {
367                         display_error(_("The entry for one or more amounts is invalid or negative."));
368                         set_focus('amount'.$counter);
369                         return false;
370                  }
371
372                   /* Now check to see that the AllocAmt is no greater than the
373                  amount left to be allocated against the transaction under review;
374                  skip check if no allocation is set to avoid deadlock on mistakenly overallocated transactions*/
375                  $allocated = input_num('amount' . $counter);
376                  if ($allocated && ($allocated > input_num('un_allocated' . $counter)))
377                  {
378                         display_error(_("At least one transaction is overallocated."));
379                         set_focus('amount'.$counter);
380                         return false;
381                  }
382
383                  $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
384
385                  $total_allocated += input_num('amount' . $counter);
386         }
387 /*
388         $amount = $_SESSION['alloc']->amount;
389
390         if (in_array($_SESSION['alloc']->type, array(ST_BANKPAYMENT, ST_SUPPCREDIT, ST_SUPPAYMENT)) || 
391                 ($_SESSION['alloc']->type==ST_JOURNAL && $_SESSION['alloc']->person_type == PT_SUPPLIER))
392                 $amount = -$amount;
393 */
394         $amount = abs($_SESSION['alloc']->amount);
395
396         if ($total_allocated - ($amount + input_num('discount'))  > $SysPrefs->allocation_settled_allowance())
397         {
398                 display_error(_("These allocations cannot be processed because the amount allocated is more than the total amount left to allocate."));
399                 return false;
400         }
401
402         return true;
403 }
404
405 //----------------------------------------------------------------------------------------
406 //
407 //      Returns sales or purchase invoice allocations to be reallocated after invoice edition.
408 //
409 function get_inv_allocations($trans_no, $trans_type, $person_id)
410 {
411         $allocs = array();
412         if ($trans_type == ST_SUPPINVOICE || $trans_type == ST_SUPPCREDIT)
413                 $result = get_allocatable_from_supp_transactions($person_id, $trans_no, $trans_type);
414         else
415                 $result = get_allocatable_from_cust_transactions($person_id, $trans_no, $trans_type);
416
417         while($dat = db_fetch($result))
418         {
419                 $allocs[] = array('type'=> $dat['type'], 'trans_no'=> $dat['trans_no'], 'amount'=>$dat['alloc']);
420         }
421         return $allocs;
422 }