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 ***********************************************************************/
13 Class for supplier/customer payment/credit allocations edition
16 //-----------------------------------------------------------------------------------
24 var $person_name = '';
25 var $person_type; // PT_SUPPLIER/PT_CUSTOMER
28 var $amount = 0; /*Total amount of the transaction in FX */
31 var $allocs; /*array of transactions allocated to */
33 function __construct($type, $trans_no, $person_id = null, $person_type_id=null)
35 $this->allocs = array();
37 $this->trans_no = $trans_no;
40 $this->set_person($person_id, $person_type_id);
42 $this->read($type, $trans_no, $person_id, $person_type_id); // read payment or credit
45 function set_person($person_id, $person_type)
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;
54 function add_item($type, $type_no, $date_, $due_date, $amount, $amount_allocated,
55 $current_allocated, $ref, $early_discount=0, $early_days=0)
57 if (floatcmp($amount, 0))
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);
69 function update_item($index, $type, $type_no, $date_, $due_date,
70 $amount, $amount_allocated, $current_allocated, $ref, $early_discount=0, $early_days=0)
72 if (floatcmp($amount, 0))
74 $this->allocs[$index] = new allocation_item($type, $type_no,
75 $date_, $due_date, $amount, $amount_allocated, $current_allocated, $ref, $early_discount, $early_days);
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)
87 for ($i = 0; $i < count($this->allocs); $i++)
89 $item = $this->allocs[$i];
90 if (($item->type == $type) && ($item->type_no == $type_no))
92 return $this->update_item($i, $type, $type_no, $date_, $due_date,
93 $amount, $amount_allocated, $current_allocated, $ref, $early_discount, $early_days);
96 return $this->add_item($type, $type_no, $date_, $due_date,
97 $amount, $amount_allocated, $current_allocated, $ref, $early_discount, $early_days);
101 // Read payment or credit current/available allocations to cart.
103 // FIXME - read all transactions below twice seems to be suboptimal
105 function read($type = null, $trans_no = 0, $person_id=null, $person_type_id=null)
107 if ($type !== null) { // otherwise re-read
109 $trans_no = $this->trans_no;
111 if (isset($person_type_id))
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)))
118 $bank_trans = db_fetch(get_bank_trans($type, $trans_no));
119 $this->person_type = $bank_trans['person_type_id'];
121 $this->person_type = in_array($type, array(ST_SUPPCREDIT, ST_SUPPAYMENT)) ? PT_SUPPLIER : PT_CUSTOMER;
125 $trans = $this->person_type == PT_SUPPLIER ? get_supp_trans($trans_no, $type, $person_id)
126 : get_customer_trans($trans_no, $type, $person_id);
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"];
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 */
141 $this->allocs = array();
142 if ($this->person_id)
144 if ($this->person_type==PT_SUPPLIER)
145 $trans_items = get_allocatable_to_supp_transactions($this->person_id);
147 $trans_items = get_allocatable_to_cust_transactions($this->person_id);
148 while ($myrow = db_fetch($trans_items))
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"]);
159 if ($this->trans_no == 0) return; // this is new payment
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 */
165 if ($this->person_type==PT_SUPPLIER)
166 $trans_items = get_allocatable_to_supp_transactions($this->person_id,
167 $this->trans_no, $this->type);
169 $trans_items = get_allocatable_to_cust_transactions($this->person_id,
170 $this->trans_no, $this->type);
172 while ($myrow = db_fetch($trans_items))
174 $this->add_or_update_item ($myrow["type"], $myrow["trans_no"],
175 sql2date($myrow["tran_date"]),
176 sql2date($myrow["due_date"]),
178 $myrow["alloc"] - $myrow["amt"], $myrow["amt"], $myrow["reference"], $myrow["early_discount"], $myrow["early_days"]);
182 // Update allocations in database.
186 global $no_exchange_variations;
190 if ($this->person_type == PT_SUPPLIER)
191 clear_supp_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
193 clear_cust_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
195 // now add the new allocations
196 $total_allocated = 0;
197 $dec = user_price_dec();
198 foreach ($this->allocs as $alloc_item)
200 if ($alloc_item->current_allocated > 0)
202 $amount = round($alloc_item->current_allocated, $dec);
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_);
209 update_supp_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
211 add_cust_allocation($amount,
212 $this->type, $this->trans_no,
213 $alloc_item->type, $alloc_item->type_no, $this->person_id, $this->date_);
215 update_debtor_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
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);
225 //////////////////////////////////////////////////////////////
226 $total_allocated += $alloc_item->current_allocated;
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);
233 update_debtor_trans_allocation($this->type, $this->trans_no, $this->person_id);
235 commit_transaction();
241 //-----------------------------------------------------------------------------------
243 class allocation_item
252 var $amount_allocated;
256 var $current_allocated;
257 var $early_discount; // nominal early payment discount according to payment terms
258 var $early_days; // days for early payment
260 function __construct($type, $type_no, $date_, $due_date, $amount,
261 $amount_allocated, $current_allocated, $ref, $early_discount=0, $early_days=0)
265 $this->type_no = $type_no;
269 $this->date_ = $date_;
270 $this->due_date = $due_date;
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;
280 //--------------------------------------------------------------------------------
282 function show_allocatable($reallocation) {
284 global $systypes_array;
286 $k = $total_allocated = $total_discount = 0;
288 $cart = $_SESSION['alloc'];
289 $supp_ref = in_array($cart->type, array(ST_SUPPCREDIT, ST_SUPPAYMENT, ST_BANKPAYMENT));
291 if (count($cart->allocs))
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"),'','');
300 foreach ($cart->allocs as $id => $alloc_item)
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);
313 $_POST['amount' . $id] = price_format($alloc_item->current_allocated);
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));
321 label_cell($discount, 'align=center');
323 check_cells(null, 'early_disc'.$id, $discount, false, false, 'align=center');
324 $total_discount += $discount;
326 label_cell(_("N/A"), 'align=center'); hidden('early_disc'.$id, 0);
328 amount_cells(null, "amount" . $id);
330 label_cells('', '', '', 'colspan=2');
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));
340 $total_allocated += input_num('amount' . $id);
342 add_js_source('var docs=['.implode(',', $doc_ids).']');
344 label_row(_("Total Allocated"), price_format($total_allocated),
345 "colspan=9 align=right", "align=right id='total_allocated'", 3);
347 label_row(_("Total Discount"), price_format($total_discount),
348 "colspan=9 align=right", "align=right id='total_discount'", 3);
350 $amount = abs($cart->amount);
352 if (floatcmp($amount, $total_allocated) < 0)
354 $font1 = "<font color=red>";
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'",
366 hidden('TotalNumberOfAllocs', count($cart->allocs));
368 //--------------------------------------------------------------------------------
370 function check_allocations()
374 $total_allocated = 0;
376 for ($counter = 0; $counter < get_post("TotalNumberOfAllocs"); $counter++)
378 if (!isset($_POST['amount'.$counter])) continue;
379 if (!check_num('amount' . $counter, 0))
381 display_error(_("The entry for one or more amounts is invalid or negative."));
382 set_focus('amount'.$counter);
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)))
392 display_error(_("At least one transaction is overallocated."));
393 set_focus('amount'.$counter);
397 $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
399 $total_allocated += input_num('amount' . $counter);
402 $amount = abs($_SESSION['alloc']->amount);
404 if ($total_allocated - ($amount + input_num('discount')) > $SysPrefs->allocation_settled_allowance())
406 display_error(_("These allocations cannot be processed because the amount allocated is more than the total amount left to allocate."));
413 //----------------------------------------------------------------------------------------
415 // Returns sales or purchase invoice allocations to be reallocated after invoice edition.
417 function get_inv_allocations($trans_no, $trans_type, $person_id)
420 if ($trans_type == ST_SUPPINVOICE || $trans_type == ST_SUPPCREDIT)
421 $result = get_allocatable_from_supp_transactions($person_id, $trans_no, $trans_type);
423 $result = get_allocatable_from_cust_transactions($person_id, $trans_no, $trans_type);
425 while($dat = db_fetch($result))
427 $allocs[] = array('type'=> $dat['type'], 'trans_no'=> $dat['trans_no'], 'amount'=>$dat['alloc']);