0596e2b2e2838d6c004ea677154dafc5a9ec938e
[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, $early_discount=0, $early_days=0)
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, $early_discount, $early_days);
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, $early_discount=0, $early_days=0)
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, $early_discount, $early_days);
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, $early_discount=0, $early_days=0)
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, $early_discount, $early_days);
94                         }
95                 }
96         return $this->add_item($type, $type_no, $date_, $due_date, 
97                 $amount, $amount_allocated, $current_allocated, $ref, $early_discount, $early_days);
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,                               // this allocation
156                                         $myrow["reference"], $myrow["early_discount"], $myrow["early_days"]);
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"], $myrow["early_discount"], $myrow["early_days"]);
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
218                                 // Exchange Variations Joe Hunt 2008-09-20 ////////////////////
219                                 if ($alloc_item->type != ST_SALESORDER && !@$no_exchange_variations
220                                         && $alloc_item->type != ST_PURCHORDER && $alloc_item->type != ST_JOURNAL && $this->type != ST_JOURNAL)
221                                         exchange_variation($this->type, $this->trans_no,
222                                                 $alloc_item->type, $alloc_item->type_no, $this->date_,
223                                                 $amount, $this->person_type);
224
225                                 //////////////////////////////////////////////////////////////
226                                 $total_allocated += $alloc_item->current_allocated;
227                         }
228
229                 }  /*end of the loop through the array of allocations made */
230                 if ($this->person_type == PT_SUPPLIER)
231                         update_supp_trans_allocation($this->type, $this->trans_no, $this->person_id);
232                 else
233                         update_debtor_trans_allocation($this->type,     $this->trans_no, $this->person_id);
234
235                 commit_transaction();
236
237         }
238
239
240
241 //-----------------------------------------------------------------------------------
242
243 class allocation_item 
244 {
245
246         var $type;
247         var $type_no;
248         
249         var $date_;
250         var $due_date;
251         
252         var $amount_allocated;
253         var $amount;
254         var $ref;
255         
256         var $current_allocated;
257         var $early_discount;    // nominal early payment discount according to payment terms
258         var $early_days;        // days for early payment
259         
260         function __construct($type, $type_no, $date_, $due_date, $amount, 
261                 $amount_allocated, $current_allocated, $ref, $early_discount=0, $early_days=0)
262         {
263
264                 $this->type = $type;
265                 $this->type_no = $type_no;
266
267                 $this->ref = $ref;
268
269                 $this->date_ = $date_;
270                 $this->due_date = $due_date;
271                 
272                 $this->amount = $amount;
273                 $this->amount_allocated = $amount_allocated;
274                 $this->current_allocated = $current_allocated;
275                 $this->early_discount = $early_discount;
276                 $this->early_days = $early_days;
277         }
278 }
279
280 //--------------------------------------------------------------------------------
281
282 function show_allocatable($reallocation) {
283
284         global $systypes_array;
285         
286     $k = $total_allocated = $total_discount = 0;
287
288         $cart = $_SESSION['alloc'];
289         $supp_ref = in_array($cart->type, array(ST_SUPPCREDIT, ST_SUPPAYMENT, ST_BANKPAYMENT));
290
291         if (count($cart->allocs)) 
292         {
293                 display_heading(sprintf(_("Allocated amounts in %s:"), $cart->person_curr));
294                 start_table(TABLESTYLE, "width='60%'");
295                 $th = array(_("Transaction Type"), _("#"), $supp_ref ? _("Supplier Ref"): _("Ref"), _("Date"), _("Due Date"), _("Amount"),
296                         _("Other Allocations"), _("Left to Allocate"), _("Early Payment Discount"), _("This Allocation"),'','');
297
298                 table_header($th);
299                 $doc_ids = array();
300                 foreach ($cart->allocs as $id => $alloc_item)
301                 {
302                         $doc_ids[] = $id;
303                         $early_discount = $alloc_item->early_discount != 0 && !date1_greater_date2($cart->date_,  add_days($alloc_item->date_, $alloc_item->early_days));
304                                 alt_table_row_color($k);
305                         label_cell($systypes_array[$alloc_item->type]);
306                                 label_cell(get_trans_view_str($alloc_item->type, $alloc_item->type_no), "nowrap align='right'");
307                                 label_cell($alloc_item->ref);
308                         label_cell($alloc_item->date_, "align=right");
309                         label_cell($alloc_item->due_date, "align=right");
310                         amount_cell(abs($alloc_item->amount));
311                                 amount_cell($alloc_item->amount_allocated);
312
313                         $_POST['amount' . $id] = price_format($alloc_item->current_allocated);
314                         
315                         $un_allocated = round((abs($alloc_item->amount) - $alloc_item->amount_allocated), 6);
316                         amount_cell($un_allocated, false,'', 'maxval'.$id);
317                         if ($early_discount) {
318                                 $discount = price_format($alloc_item->early_discount*abs($alloc_item->amount));
319
320                                 if ($reallocation)
321                                                 label_cell($discount, 'align=center');
322                                         else
323                                                 check_cells(null, 'early_disc'.$id, $discount, false, false, 'align=center');
324                                         $total_discount += $discount;
325                                 } else {
326                                         label_cell(_("N/A"), 'align=center'); hidden('early_disc'.$id, 0);
327                                 }
328                         amount_cells(null, "amount" . $id);
329                                 if (0) {
330                                         label_cells('', '', '', 'colspan=2');
331                                 } else {
332                                         label_cell("<a href='#' name=Alloc$id onclick='allocate_all(this.name.substr(5));return true;'>"
333                                                  . _("All") . "</a>");
334                                         label_cell("<a href='#' name=DeAll$id onclick='allocate_none(this.name.substr(5));return true;'>"
335                                                  . _("None") . "</a>".hidden("un_allocated" . $id, 
336                                                  price_format($un_allocated), false));
337                                 }
338                                 end_row();
339
340                         $total_allocated += input_num('amount' . $id);
341                 }
342                 add_js_source('var docs=['.implode(',', $doc_ids).']');
343                 if ($reallocation) {
344                 label_row(_("Total Allocated"), price_format($total_allocated),
345                         "colspan=9 align=right", "align=right id='total_allocated'", 3);
346
347                 label_row(_("Total Discount"), price_format($total_discount),
348                         "colspan=9 align=right", "align=right id='total_discount'", 3);
349
350                         $amount = abs($cart->amount);
351
352                         if (floatcmp($amount, $total_allocated) < 0)
353                 {
354                         $font1 = "<font color=red>";
355                         $font2 = "</font>";
356             }
357                 else
358                         $font1 = $font2 = "";
359                         $left_to_allocate = price_format($amount - $total_allocated);
360                 label_row(_("Left to Allocate"), $font1 . $left_to_allocate . $font2, 
361                                 "colspan=9 align=right", "nowrap align=right id='left_to_allocate'",
362                                  3);
363                 }
364                 end_table(1);
365         }
366         hidden('TotalNumberOfAllocs', count($cart->allocs));
367 }
368 //--------------------------------------------------------------------------------
369
370 function check_allocations()
371 {
372         global $SysPrefs;
373
374         $total_allocated = 0;
375
376         for ($counter = 0; $counter < get_post("TotalNumberOfAllocs"); $counter++)
377         {
378                 if (!isset($_POST['amount'.$counter])) continue;
379                 if (!check_num('amount' . $counter, 0))
380                 {
381                         display_error(_("The entry for one or more amounts is invalid or negative."));
382                         set_focus('amount'.$counter);
383                         return false;
384                  }
385
386                   /* Now check to see that the AllocAmt is no greater than the
387                  amount left to be allocated against the transaction under review;
388                  skip check if no allocation is set to avoid deadlock on mistakenly overallocated transactions*/
389                  $allocated = input_num('amount' . $counter);
390                  if ($allocated && ($allocated > input_num('un_allocated' . $counter)))
391                  {
392                         display_error(_("At least one transaction is overallocated."));
393                         set_focus('amount'.$counter);
394                         return false;
395                  }
396
397                  $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
398
399                  $total_allocated += input_num('amount' . $counter);
400         }
401
402         $amount = abs($_SESSION['alloc']->amount);
403
404         if ($total_allocated - ($amount + input_num('discount'))  > $SysPrefs->allocation_settled_allowance())
405         {
406                 display_error(_("These allocations cannot be processed because the amount allocated is more than the total amount left to allocate."));
407                 return false;
408         }
409
410         return true;
411 }
412
413 //----------------------------------------------------------------------------------------
414 //
415 //      Returns sales or purchase invoice allocations to be reallocated after invoice edition.
416 //
417 function get_inv_allocations($trans_no, $trans_type, $person_id)
418 {
419         $allocs = array();
420         if ($trans_type == ST_SUPPINVOICE || $trans_type == ST_SUPPCREDIT)
421                 $result = get_allocatable_from_supp_transactions($person_id, $trans_no, $trans_type);
422         else
423                 $result = get_allocatable_from_cust_transactions($person_id, $trans_no, $trans_type);
424
425         while($dat = db_fetch($result))
426         {
427                 $allocs[] = array('type'=> $dat['type'], 'trans_no'=> $dat['trans_no'], 'amount'=>$dat['alloc']);
428         }
429         return $allocs;
430 }
431