Merged last changes from stable.
[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 = @$trans["bank_amount"];
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 //_vd($myrow);
175                         $this->add_or_update_item ($myrow["type"], $myrow["trans_no"],
176                                 sql2date($myrow["tran_date"]),
177                                 sql2date($myrow["due_date"]),
178                                 $myrow["Total"],
179                                 $myrow["alloc"] - $myrow["amt"], $myrow["amt"], $myrow["reference"]);
180                 }
181         }
182         //
183         //      Update allocations in database.
184         //
185         function write()
186         {
187                 begin_transaction();
188
189                 if ($this->person_type == PT_SUPPLIER)
190                         clear_supp_alloctions($this->type, $this->trans_no, $this->date_);
191                 else
192                         clear_cust_alloctions($this->type, $this->trans_no);
193
194                 // now add the new allocations
195                 $total_allocated = 0;
196                 $dec = user_price_dec();
197                 foreach ($this->allocs as $alloc_item)
198                 {
199                         if ($alloc_item->current_allocated > 0)
200                         {
201                                 $amount = round($alloc_item->current_allocated, $dec);
202
203                                 if ($this->person_type == PT_SUPPLIER) {
204                                         add_supp_allocation($amount,
205                                                 $this->type, $this->trans_no,
206                                         $alloc_item->type, $alloc_item->type_no, $this->date_);
207
208                                         update_supp_trans_allocation($alloc_item->type, $alloc_item->type_no);
209                                 } else {
210                                         add_cust_allocation($amount,
211                                                 $this->type, $this->trans_no,
212                                         $alloc_item->type, $alloc_item->type_no, $this->date_);
213
214                                         update_debtor_trans_allocation($alloc_item->type, $alloc_item->type_no);
215                                 }
216                                 // Exchange Variations Joe Hunt 2008-09-20 ////////////////////
217                                 if (!in_array($alloc_item->type, array(ST_SALESQUOTE, ST_SALESORDER, ST_PURCHORDER)))
218                                  exchange_variation($this->type, $this->trans_no,
219                                         $alloc_item->type, $alloc_item->type_no, $this->date_,
220                                         $amount, $this->person_type);
221
222                                 //////////////////////////////////////////////////////////////
223                                 $total_allocated += $alloc_item->current_allocated;
224                         }
225
226                 }  /*end of the loop through the array of allocations made */
227                 if ($this->person_type == PT_SUPPLIER)
228                         update_supp_trans_allocation($this->type, $this->trans_no);
229                 else
230                         update_debtor_trans_allocation($this->type,     $this->trans_no);
231
232                 commit_transaction();
233
234         }
235
236
237
238 //-----------------------------------------------------------------------------------
239
240 class allocation_item 
241 {
242
243         var $type;
244         var $type_no;
245         
246         var $date_;
247         var $due_date;
248         
249         var $amount_allocated;
250         var $amount;
251         var $ref;
252         
253         var $current_allocated;
254         
255         function allocation_item ($type, $type_no, $date_, $due_date, $amount, 
256                 $amount_allocated, $current_allocated, $ref)
257         {
258
259                 $this->type = $type;
260                 $this->type_no = $type_no;
261
262                 $this->ref = $ref;
263
264                 $this->date_ = $date_;
265                 $this->due_date = $due_date;
266                 
267                 $this->amount = $amount;
268                 $this->amount_allocated = $amount_allocated;
269                 $this->current_allocated = $current_allocated;
270         }
271 }
272
273 //--------------------------------------------------------------------------------
274
275 function show_allocatable($show_totals) {
276
277         global $systypes_array;
278         
279     $k = $counter = $total_allocated = 0;
280
281         $cart = $_SESSION['alloc'];
282         $supp_ref = in_array($cart->type, array(ST_SUPPCREDIT, ST_SUPPAYMENT, ST_BANKPAYMENT));
283
284         if (count($cart->allocs)) 
285         {
286                 if ($cart->currency != $cart->person_curr)
287                         display_heading(sprintf(_("Allocated amounts in %s:"), $cart->person_curr));
288                 start_table(TABLESTYLE, "width=60%");
289                 $th = array(_("Transaction Type"), _("#"), $supp_ref ? _("Supplier Ref"): _("Ref"), _("Date"), _("Due Date"), _("Amount"),
290                         _("Other Allocations"), _("Left to Allocate"), _("This Allocation"),'','');
291
292                 table_header($th);
293
294                 foreach ($cart->allocs as $id => $alloc_item)
295                 {
296                     if (floatcmp(abs($alloc_item->amount), $alloc_item->amount_allocated))
297                     {
298                                 alt_table_row_color($k);
299                         label_cell($systypes_array[$alloc_item->type]);
300                                 label_cell(get_trans_view_str($alloc_item->type, $alloc_item->type_no));
301                                 label_cell($alloc_item->ref);
302                         label_cell($alloc_item->date_, "align=right");
303                         label_cell($alloc_item->due_date, "align=right");
304                         amount_cell(abs($alloc_item->amount));
305                                 amount_cell($alloc_item->amount_allocated);
306
307                         $_POST['amount' . $id] = price_format($alloc_item->current_allocated);
308
309                         $un_allocated = round((abs($alloc_item->amount) - $alloc_item->amount_allocated), 6);
310                         amount_cell($un_allocated, false,'', 'maxval'.$id);
311                         amount_cells(null, "amount" . $id);//, input_num('amount' . $id));
312                                 label_cell("<a href='#' name=Alloc$id onclick='allocate_all(this.name.substr(5));return true;'>"
313                                          . _("All") . "</a>");
314                                 label_cell("<a href='#' name=DeAll$id onclick='allocate_none(this.name.substr(5));return true;'>"
315                                          . _("None") . "</a>".hidden("un_allocated" . $id, 
316                                          price_format($un_allocated), false));
317                                 end_row();
318
319                         $total_allocated += input_num('amount' . $id);
320                         }
321                 }
322                 if ($show_totals) {
323                 label_row(_("Total Allocated"), price_format($total_allocated),
324                         "colspan=8 align=right", "align=right id='total_allocated'", 3);
325 /*
326                         $amount = $_SESSION['alloc']->amount;
327
328                         if ($_SESSION['alloc']->type == ST_SUPPCREDIT
329                                 || $_SESSION['alloc']->type == ST_SUPPAYMENT
330                                 ||  $_SESSION['alloc']->type == ST_BANKPAYMENT)
331                                 $amount = -$amount;
332 */
333                         $amount = abs($cart->amount);
334
335                         if (floatcmp($amount, $total_allocated) < 0)
336                 {
337                         $font1 = "<font color=red>";
338                         $font2 = "</font>";
339             }
340                 else
341                         $font1 = $font2 = "";
342                         $left_to_allocate = price_format($amount - $total_allocated);
343                 label_row(_("Left to Allocate"), $font1 . $left_to_allocate . $font2, 
344                                 "colspan=8 align=right", "nowrap align=right id='left_to_allocate'",
345                                  3);
346                 }
347                 end_table(1);
348         }
349         hidden('TotalNumberOfAllocs', count($cart->allocs));
350 }
351 //--------------------------------------------------------------------------------
352
353 function check_allocations()
354 {
355         global $SysPrefs;
356
357         $total_allocated = 0;
358
359         for ($counter = 0; $counter < get_post("TotalNumberOfAllocs"); $counter++)
360         {
361                 if (!isset($_POST['amount'.$counter])) continue;
362
363                 if (!check_num('amount' . $counter, 0))
364                 {
365                         display_error(_("The entry for one or more amounts is invalid or negative."));
366                         set_focus('amount'.$counter);
367                         return false;
368                  }
369
370                   /* Now check to see that the AllocAmt is no greater than the
371                  amount left to be allocated against the transaction under review;
372                  skip check if no allocation is set to avoid deadlock on mistakenly overallocated transactions*/
373                  $allocated = input_num('amount' . $counter);
374                  if ($allocated && ($allocated > input_num('un_allocated' . $counter)))
375                  {
376                         display_error(_("At least one transaction is overallocated."));
377                         set_focus('amount'.$counter);
378                         return false;
379                  }
380
381                  $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
382
383                  $total_allocated += input_num('amount' . $counter);
384         }
385 /*
386         $amount = $_SESSION['alloc']->amount;
387
388         if (in_array($_SESSION['alloc']->type, array(ST_BANKPAYMENT, ST_SUPPCREDIT, ST_SUPPAYMENT)))
389                 $amount = -$amount;
390 */
391         $amount = abs($_SESSION['alloc']->amount);
392
393         if ($total_allocated - ($amount + input_num('discount'))  > $SysPrefs->allocation_settled_allowance())
394         {
395                 display_error(_("These allocations cannot be processed because the amount allocated is more than the total amount left to allocate."));
396                 return false;
397         }
398
399         return true;
400 }