22bb1e1caef6596b27251254f52b4bd789c2fdf5
[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 __construct($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 __construct($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 = $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                 display_heading(sprintf(_("Allocated amounts in %s:"), $cart->person_curr));
289                 start_table(TABLESTYLE, "width='60%'");
290                 $th = array(_("Transaction Type"), _("#"), $supp_ref ? _("Supplier Ref"): _("Ref"), _("Date"), _("Due Date"), _("Amount"),
291                         _("Other Allocations"), _("Left to Allocate"), _("This Allocation"),'','');
292
293                 table_header($th);
294
295                 foreach ($cart->allocs as $id => $alloc_item)
296                 {
297                     if (floatcmp(abs($alloc_item->amount), $alloc_item->amount_allocated))
298                     {
299                                 alt_table_row_color($k);
300                         label_cell($systypes_array[$alloc_item->type]);
301                                 label_cell(get_trans_view_str($alloc_item->type, $alloc_item->type_no), "nowrap align='right'");
302                                 label_cell($alloc_item->ref);
303                         label_cell($alloc_item->date_, "align=right");
304                         label_cell($alloc_item->due_date, "align=right");
305                         amount_cell(abs($alloc_item->amount));
306                                 amount_cell($alloc_item->amount_allocated);
307
308                         $_POST['amount' . $id] = price_format($alloc_item->current_allocated);
309
310                         $un_allocated = round((abs($alloc_item->amount) - $alloc_item->amount_allocated), 6);
311                         amount_cell($un_allocated, false,'', 'maxval'.$id);
312                         amount_cells(null, "amount" . $id);//, input_num('amount' . $id));
313                                 label_cell("<a href='javascript:void(0)' name=Alloc$id onclick='allocate_all(this.name.substr(5));return true;'>"
314                                          . _("All") . "</a>");
315                                 label_cell("<a href='javascript:void(0)' name=DeAll$id onclick='allocate_none(this.name.substr(5));return true;'>"
316                                          . _("None") . "</a>".hidden("un_allocated" . $id, 
317                                          price_format($un_allocated), false));
318                                 end_row();
319
320                         $total_allocated += input_num('amount' . $id);
321                         }
322                 }
323                 if ($show_totals) {
324                 label_row(_("Total Allocated"), price_format($total_allocated),
325                         "colspan=8 align=right", "align=right id='total_allocated'", 3);
326
327                         $amount = abs($cart->amount);
328
329                         if (floatcmp($amount, $total_allocated) < 0)
330                 {
331                         $font1 = "<font color=red>";
332                         $font2 = "</font>";
333             }
334                 else
335                         $font1 = $font2 = "";
336                         $left_to_allocate = price_format($amount - $total_allocated);
337                 label_row(_("Left to Allocate"), $font1 . $left_to_allocate . $font2, 
338                                 "colspan=8 align=right", "nowrap align=right id='left_to_allocate'",
339                                  3);
340                 }
341                 end_table(1);
342         }
343         hidden('TotalNumberOfAllocs', count($cart->allocs));
344 }
345 //--------------------------------------------------------------------------------
346
347 function check_allocations()
348 {
349         global $SysPrefs;
350
351         $total_allocated = 0;
352
353         for ($counter = 0; $counter < get_post("TotalNumberOfAllocs"); $counter++)
354         {
355                 if (!isset($_POST['amount'.$counter])) continue;
356                 if (!check_num('amount' . $counter, 0))
357                 {
358                         display_error(_("The entry for one or more amounts is invalid or negative."));
359                         set_focus('amount'.$counter);
360                         return false;
361                  }
362
363                   /* Now check to see that the AllocAmt is no greater than the
364                  amount left to be allocated against the transaction under review;
365                  skip check if no allocation is set to avoid deadlock on mistakenly overallocated transactions*/
366                  $allocated = input_num('amount' . $counter);
367                  if ($allocated && ($allocated > input_num('un_allocated' . $counter)))
368                  {
369                         display_error(_("At least one transaction is overallocated."));
370                         set_focus('amount'.$counter);
371                         return false;
372                  }
373
374                 if ($_SESSION['alloc']->person_type == PT_CUSTOMER) {
375                         if ($_SESSION['alloc']->allocs[$counter]->type == ST_SALESORDER)
376                                 $trans = get_sales_order_header($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
377                         else
378                                 $trans = get_customer_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
379
380                         if ($trans['debtor_no'] != $_SESSION['alloc']->person_id) {
381                                 display_error(_("Allocated transaction allocated is not related to company selected."));
382                                 set_focus('amount'.$counter);
383                                 return false;
384                         }
385                 } elseif ($_SESSION['alloc']->person_type == PT_SUPPLIER) {
386                         $trans = get_supp_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
387                         if ($trans['supplier_id'] != $_SESSION['alloc']->person_id) {
388                                 display_error(_("Allocated transaction allocated is not related to company selected."));
389                                 set_focus('amount'.$counter);
390                                 return false;
391                         }
392                 }
393
394                 $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
395
396                 $total_allocated += input_num('amount' . $counter);
397         }
398
399         $amount = abs($_SESSION['alloc']->amount);
400
401         if ($total_allocated - ($amount + input_num('discount'))  > $SysPrefs->allocation_settled_allowance())
402         {
403                 display_error(_("These allocations cannot be processed because the amount allocated is more than the total amount left to allocate."));
404                 return false;
405         }
406
407         return true;
408 }
409
410 //----------------------------------------------------------------------------------------
411 //
412 //      Returns sales or purchase invoice allocations to be reallocated after invoice edition.
413 //
414 function get_inv_allocations($trans_no, $trans_type, $person_id)
415 {
416         $allocs = array();
417         if ($trans_type == ST_SUPPINVOICE || $trans_type == ST_SUPPCREDIT)
418                 $result = get_allocatable_from_supp_transactions($person_id, $trans_no, $trans_type);
419         else
420                 $result = get_allocatable_from_cust_transactions($person_id, $trans_no, $trans_type);
421
422         while($dat = db_fetch($result))
423         {
424                 $allocs[] = array('type'=> $dat['type'], 'trans_no'=> $dat['trans_no'], 'amount'=>$dat['alloc']);
425         }
426         return $allocs;
427 }