Merged changes form stabel branch up to the current state (2.3.22+).
[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                         } else
136                                 $this->date_ = Today();
137                 }
138         /* Now populate the array of possible (and previous actual) allocations 
139                 for this customer/supplier. First get the transactions that have 
140                 outstanding balances ie Total-alloc >0 */
141
142                 $this->allocs = array();
143                 if ($this->person_id)
144                 {
145                         if ($this->person_type==PT_SUPPLIER)
146                                 $trans_items = get_allocatable_to_supp_transactions($this->person_id);
147                         else
148                                 $trans_items = get_allocatable_to_cust_transactions($this->person_id);
149                         while ($myrow = db_fetch($trans_items))
150                         {
151                                 $this->add_item($myrow["type"], $myrow["trans_no"],
152                                         sql2date($myrow["tran_date"]),
153                                         sql2date($myrow["due_date"]),
154                                         $myrow["Total"], // trans total
155                                         $myrow["alloc"], // trans total allocated
156                                         0,
157                                         $myrow["reference"]); // this allocation
158                         }
159                 }
160                 if ($this->trans_no == 0) return; // this is new payment
161
162         /* Now get trans that might have previously been allocated to by this trans
163         NB existing entries where still some of the trans outstanding entered from
164         above logic will be overwritten with the prev alloc detail below */
165
166                 if ($this->person_type==PT_SUPPLIER)
167                         $trans_items = get_allocatable_to_supp_transactions($this->person_id, 
168                                 $this->trans_no, $this->type);
169                 else
170                         $trans_items = get_allocatable_to_cust_transactions($this->person_id, 
171                                 $this->trans_no, $this->type);
172
173                 while ($myrow = db_fetch($trans_items))
174                 {
175 //_vd($myrow);
176                         $this->add_or_update_item ($myrow["type"], $myrow["trans_no"],
177                                 sql2date($myrow["tran_date"]),
178                                 sql2date($myrow["due_date"]),
179                                 $myrow["Total"],
180                                 $myrow["alloc"] - $myrow["amt"], $myrow["amt"], $myrow["reference"]);
181                 }
182         }
183         //
184         //      Update allocations in database.
185         //
186         function write()
187         {
188                 begin_transaction();
189
190                 if ($this->person_type == PT_SUPPLIER)
191                         clear_supp_alloctions($this->type, $this->trans_no, $this->date_);
192                 else
193                         clear_cust_alloctions($this->type, $this->trans_no);
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->date_);
208
209                                         update_supp_trans_allocation($alloc_item->type, $alloc_item->type_no);
210                                 } else {
211                                         add_cust_allocation($amount,
212                                                 $this->type, $this->trans_no,
213                                         $alloc_item->type, $alloc_item->type_no, $this->date_);
214
215                                         update_debtor_trans_allocation($alloc_item->type, $alloc_item->type_no);
216                                 }
217                                 // Exchange Variations Joe Hunt 2008-09-20 ////////////////////
218                                 if (!in_array($alloc_item->type, array(ST_SALESQUOTE, ST_SALESORDER, ST_PURCHORDER)))
219                                  exchange_variation($this->type, $this->trans_no,
220                                         $alloc_item->type, $alloc_item->type_no, $this->date_,
221                                         $amount, $this->person_type);
222
223                                 //////////////////////////////////////////////////////////////
224                                 $total_allocated += $alloc_item->current_allocated;
225                         }
226
227                 }  /*end of the loop through the array of allocations made */
228                 if ($this->person_type == PT_SUPPLIER)
229                         update_supp_trans_allocation($this->type, $this->trans_no);
230                 else
231                         update_debtor_trans_allocation($this->type,     $this->trans_no);
232
233                 commit_transaction();
234
235         }
236
237
238
239 //-----------------------------------------------------------------------------------
240
241 class allocation_item 
242 {
243
244         var $type;
245         var $type_no;
246         
247         var $date_;
248         var $due_date;
249         
250         var $amount_allocated;
251         var $amount;
252         var $ref;
253         
254         var $current_allocated;
255         
256         function allocation_item ($type, $type_no, $date_, $due_date, $amount, 
257                 $amount_allocated, $current_allocated, $ref)
258         {
259
260                 $this->type = $type;
261                 $this->type_no = $type_no;
262
263                 $this->ref = $ref;
264
265                 $this->date_ = $date_;
266                 $this->due_date = $due_date;
267                 
268                 $this->amount = $amount;
269                 $this->amount_allocated = $amount_allocated;
270                 $this->current_allocated = $current_allocated;
271         }
272 }
273
274 //--------------------------------------------------------------------------------
275
276 function show_allocatable($show_totals) {
277
278         global $systypes_array;
279         
280     $k = $counter = $total_allocated = 0;
281
282         $cart = $_SESSION['alloc'];
283         $supp_ref = in_array($cart->type, array(ST_SUPPCREDIT, ST_SUPPAYMENT, ST_BANKPAYMENT));
284
285         if (count($cart->allocs)) 
286         {
287                 if ($cart->currency != $cart->person_curr)
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));
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='#' name=Alloc$id onclick='allocate_all(this.name.substr(5));return true;'>"
314                                          . _("All") . "</a>");
315                                 label_cell("<a href='#' 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 = $_SESSION['alloc']->amount;
328
329                         if ($_SESSION['alloc']->type == ST_SUPPCREDIT
330                                 || $_SESSION['alloc']->type == ST_SUPPAYMENT
331                                 ||  $_SESSION['alloc']->type == ST_BANKPAYMENT)
332                                 $amount = -$amount;
333 */
334                         $amount = abs($cart->amount);
335
336                         if (floatcmp($amount, $total_allocated) < 0)
337                 {
338                         $font1 = "<font color=red>";
339                         $font2 = "</font>";
340             }
341                 else
342                         $font1 = $font2 = "";
343                         $left_to_allocate = price_format($amount - $total_allocated);
344                 label_row(_("Left to Allocate"), $font1 . $left_to_allocate . $font2, 
345                                 "colspan=8 align=right", "nowrap align=right id='left_to_allocate'",
346                                  3);
347                 }
348                 end_table(1);
349         }
350         hidden('TotalNumberOfAllocs', count($cart->allocs));
351 }
352 //--------------------------------------------------------------------------------
353
354 function check_allocations()
355 {
356         global $SysPrefs;
357
358         $total_allocated = 0;
359
360         for ($counter = 0; $counter < get_post("TotalNumberOfAllocs"); $counter++)
361         {
362                 if (!isset($_POST['amount'.$counter])) continue;
363
364                 if (!check_num('amount' . $counter, 0))
365                 {
366                         display_error(_("The entry for one or more amounts is invalid or negative."));
367                         set_focus('amount'.$counter);
368                         return false;
369                  }
370
371                   /* Now check to see that the AllocAmt is no greater than the
372                  amount left to be allocated against the transaction under review;
373                  skip check if no allocation is set to avoid deadlock on mistakenly overallocated transactions*/
374                  $allocated = input_num('amount' . $counter);
375                  if ($allocated && ($allocated > input_num('un_allocated' . $counter)))
376                  {
377                         display_error(_("At least one transaction is overallocated."));
378                         set_focus('amount'.$counter);
379                         return false;
380                  }
381
382                  $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
383
384                  $total_allocated += input_num('amount' . $counter);
385         }
386 /*
387         $amount = $_SESSION['alloc']->amount;
388
389         if (in_array($_SESSION['alloc']->type, array(ST_BANKPAYMENT, ST_SUPPCREDIT, ST_SUPPAYMENT)))
390                 $amount = -$amount;
391 */
392         $amount = abs($_SESSION['alloc']->amount);
393
394         if ($total_allocated - ($amount + input_num('discount'))  > $SysPrefs->allocation_settled_allowance())
395         {
396                 display_error(_("These allocations cannot be processed because the amount allocated is more than the total amount left to allocate."));
397                 return false;
398         }
399
400         return true;
401 }