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 */
32 var $allocs; /*array of transactions allocated to */
34 function __construct($type, $trans_no, $person_id = null, $person_type_id=null)
36 $this->allocs = array();
38 $this->trans_no = $trans_no;
41 $this->set_person($person_id, $person_type_id);
43 $this->read($type, $trans_no, $person_id, $person_type_id); // read payment or credit
46 function set_person($person_id, $person_type)
48 $this->person_id = $person_id;
49 $this->person_type = $person_type;
50 $this->person_curr = $person_type == PT_SUPPLIER ?
51 get_supplier_currency($person_id) : get_customer_currency($person_id);
52 return $this->person_curr;
55 function add_item($type, $type_no, $date_, $due_date, $amount, $amount_allocated,
56 $current_allocated, $ref)
58 if (floatcmp($amount, 0))
60 $this->allocs[count($this->allocs)] = new allocation_item($type, $type_no,
61 $date_, $due_date, $amount, $amount_allocated, $current_allocated, $ref);
70 function update_item($index, $type, $type_no, $date_, $due_date,
71 $amount, $amount_allocated, $current_allocated, $ref)
73 if (floatcmp($amount, 0))
75 $this->allocs[$index] = new allocation_item($type, $type_no,
76 $date_, $due_date, $amount, $amount_allocated, $current_allocated, $ref);
85 function add_or_update_item($type, $type_no, $date_, $due_date,
86 $amount, $amount_allocated, $current_allocated, $ref)
88 for ($i = 0; $i < count($this->allocs); $i++)
90 $item = $this->allocs[$i];
91 if (($item->type == $type) && ($item->type_no == $type_no))
93 return $this->update_item($i, $type, $type_no, $date_, $due_date,
94 $amount, $amount_allocated, $current_allocated, $ref);
97 return $this->add_item($type, $type_no, $date_, $due_date,
98 $amount, $amount_allocated, $current_allocated, $ref);
102 // Read payment or credit current/available allocations to cart.
104 // FIXME - read all transactions below twice seems to be suboptimal
106 function read($type = null, $trans_no = 0, $person_id=null, $person_type_id=null)
108 if ($type !== null) { // otherwise re-read
110 $trans_no = $this->trans_no;
112 if (isset($person_type_id))
114 $this->person_type = $person_type_id;
115 $this->person_id = $person_id;
116 } else { // guess person_type_id
117 if (in_array($type, array(ST_BANKPAYMENT, ST_BANKDEPOSIT)))
119 $bank_trans = db_fetch(get_bank_trans($type, $trans_no));
120 $this->person_type = $bank_trans['person_type_id'];
122 $this->person_type = in_array($type, array(ST_SUPPCREDIT, ST_SUPPAYMENT)) ? PT_SUPPLIER : PT_CUSTOMER;
126 $trans = $this->person_type == PT_SUPPLIER ? get_supp_trans($trans_no, $type, $person_id)
127 : get_customer_trans($trans_no, $type, $person_id);
129 $this->person_id = $trans[$this->person_type == PT_SUPPLIER ? 'supplier_id':'debtor_no'];
130 $this->person_name = $trans[$this->person_type == PT_SUPPLIER ? "supplier_name":"DebtorName"];
131 $this->date_ = sql2date($trans["tran_date"]);
132 $this->person_curr = $trans['curr_code'];
133 $this->currency = isset($trans['bank_curr_code']) ? $trans['bank_curr_code'] : $trans['curr_code'];
134 $this->bank_amount = isset($trans["bank_amount"]) ? $trans["bank_amount"] : $trans["Total"]; // not set for journal entry
135 $this->amount = $trans["Total"];
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 */
142 $this->allocs = array();
143 if ($this->person_id)
145 if ($this->person_type==PT_SUPPLIER)
146 $trans_items = get_allocatable_to_supp_transactions($this->person_id);
148 $trans_items = get_allocatable_to_cust_transactions($this->person_id);
149 while ($myrow = db_fetch($trans_items))
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
157 $myrow["reference"]); // this allocation
160 if ($this->trans_no == 0) return; // this is new payment
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 */
166 if ($this->person_type==PT_SUPPLIER)
167 $trans_items = get_allocatable_to_supp_transactions($this->person_id,
168 $this->trans_no, $this->type);
170 $trans_items = get_allocatable_to_cust_transactions($this->person_id,
171 $this->trans_no, $this->type);
173 $amount = $this->amount;
174 while ($myrow = db_fetch($trans_items))
176 $amount -= $myrow["Total"];
177 $this->add_or_update_item ($myrow["type"], $myrow["trans_no"],
178 sql2date($myrow["tran_date"]),
179 sql2date($myrow["due_date"]),
181 $myrow["alloc"] - $myrow["amt"], $myrow["amt"], $myrow["reference"]);
184 /* Finally, if there any unallocated money remaining, assign the unallocated portion to
185 the earliest transactions. This makes the javascript All/None keys consistent
186 with the transaction amount. */
189 foreach ($this->allocs as $alloc_item) {
190 $allocatable = $alloc_item->amount - $alloc_item->amount_allocated;
191 if ($allocatable > 0) {
192 if ($amount >= $allocatable) {
193 $alloc_item->current_allocated = $allocatable;
194 $amount -= $allocatable;
196 $alloc_item->current_allocated += $amount;
204 // Update allocations in database.
208 global $no_exchange_variations;
212 if ($this->person_type == PT_SUPPLIER)
213 clear_supp_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
215 clear_cust_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
217 // now add the new allocations
218 $total_allocated = 0;
219 $dec = user_price_dec();
220 foreach ($this->allocs as $alloc_item)
222 if ($alloc_item->current_allocated > 0)
224 $amount = round($alloc_item->current_allocated, $dec);
226 if ($this->person_type == PT_SUPPLIER) {
227 add_supp_allocation($amount,
228 $this->type, $this->trans_no,
229 $alloc_item->type, $alloc_item->type_no, $this->person_id, $this->date_);
231 update_supp_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
233 add_cust_allocation($amount,
234 $this->type, $this->trans_no,
235 $alloc_item->type, $alloc_item->type_no, $this->person_id, $this->date_);
237 update_debtor_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
239 // Exchange Variations Joe Hunt 2008-09-20 ////////////////////
240 if ($alloc_item->type != ST_SALESORDER && !@$no_exchange_variations
241 && $alloc_item->type != ST_PURCHORDER && $alloc_item->type != ST_JOURNAL && $this->type != ST_JOURNAL)
242 exchange_variation($this->type, $this->trans_no,
243 $alloc_item->type, $alloc_item->type_no, $this->date_,
244 $amount, $this->person_type);
246 //////////////////////////////////////////////////////////////
247 $total_allocated += $alloc_item->current_allocated;
250 } /*end of the loop through the array of allocations made */
251 if ($this->person_type == PT_SUPPLIER)
252 update_supp_trans_allocation($this->type, $this->trans_no, $this->person_id);
254 update_debtor_trans_allocation($this->type, $this->trans_no, $this->person_id);
256 commit_transaction();
262 //-----------------------------------------------------------------------------------
264 class allocation_item
273 var $amount_allocated;
277 var $current_allocated;
279 function __construct($type, $type_no, $date_, $due_date, $amount,
280 $amount_allocated, $current_allocated, $ref)
284 $this->type_no = $type_no;
288 $this->date_ = $date_;
289 $this->due_date = $due_date;
291 $this->amount = $amount;
292 $this->amount_allocated = $amount_allocated;
293 $this->current_allocated = $current_allocated;
297 //--------------------------------------------------------------------------------
299 function show_allocatable($show_totals) {
301 global $systypes_array;
303 $k = $total_allocated = 0;
305 $cart = $_SESSION['alloc'];
306 $supp_ref = in_array($cart->type, array(ST_SUPPCREDIT, ST_SUPPAYMENT, ST_BANKPAYMENT));
308 if (count($cart->allocs))
310 display_heading(sprintf(_("Allocated amounts in %s:"), $cart->person_curr));
311 start_table(TABLESTYLE, "width='60%'");
312 $th = array(_("Transaction Type"), _("#"), $supp_ref ? _("Supplier Ref"): _("Ref"), _("Date"), _("Due Date"), _("Amount"),
313 _("Other Allocations"), _("Left to Allocate"), _("This Allocation"),'','');
317 foreach ($cart->allocs as $id => $alloc_item)
319 if (floatcmp(abs($alloc_item->amount), $alloc_item->amount_allocated))
321 alt_table_row_color($k);
322 label_cell($systypes_array[$alloc_item->type]);
323 label_cell(get_trans_view_str($alloc_item->type, $alloc_item->type_no), "nowrap align='right'");
324 label_cell($alloc_item->ref);
325 label_cell($alloc_item->date_, "align=right");
326 label_cell($alloc_item->due_date, "align=right");
327 amount_cell(abs($alloc_item->amount));
328 amount_cell($alloc_item->amount_allocated);
330 $_POST['amount' . $id] = price_format($alloc_item->current_allocated);
332 $un_allocated = round((abs($alloc_item->amount) - $alloc_item->amount_allocated), 6);
333 amount_cell($un_allocated, false,'', 'maxval'.$id);
334 amount_cells(null, "amount" . $id);//, input_num('amount' . $id));
335 label_cell("<a href='javascript:void(0)' name=Alloc$id onclick='allocate_all(this.name.substr(5));return true;'>"
336 . _("All") . "</a>");
337 label_cell("<a href='javascript:void(0)' name=DeAll$id onclick='allocate_none(this.name.substr(5));return true;'>"
338 . _("None") . "</a>".hidden("un_allocated" . $id,
339 price_format($un_allocated), false));
342 $total_allocated += input_num('amount' . $id);
346 label_row(_("Total Allocated"), price_format($total_allocated),
347 "colspan=8 align=right", "align=right id='total_allocated'", 3);
349 $amount = abs($cart->amount);
351 if (floatcmp($amount, $total_allocated) < 0)
353 $font1 = "<font color=red>";
357 $font1 = $font2 = "";
358 $left_to_allocate = price_format($amount - $total_allocated);
359 label_row(_("Left to Allocate"), $font1 . $left_to_allocate . $font2,
360 "colspan=8 align=right", "nowrap align=right id='left_to_allocate'",
365 hidden('TotalNumberOfAllocs', count($cart->allocs));
367 //--------------------------------------------------------------------------------
369 function check_allocations()
373 $total_allocated = 0;
375 for ($counter = 0; $counter < get_post("TotalNumberOfAllocs"); $counter++)
377 if (!isset($_POST['amount'.$counter])) continue;
378 if (!check_num('amount' . $counter, 0))
380 display_error(_("The entry for one or more amounts is invalid or negative."));
381 set_focus('amount'.$counter);
385 /* Now check to see that the AllocAmt is no greater than the
386 amount left to be allocated against the transaction under review;
387 skip check if no allocation is set to avoid deadlock on mistakenly overallocated transactions*/
388 $allocated = input_num('amount' . $counter);
389 if ($allocated && ($allocated > input_num('un_allocated' . $counter)))
391 display_error(_("At least one transaction is overallocated."));
392 set_focus('amount'.$counter);
396 if ($_SESSION['alloc']->person_type == PT_CUSTOMER) {
397 if ($_SESSION['alloc']->allocs[$counter]->type == ST_SALESORDER)
398 $trans = get_sales_order_header($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
400 $trans = get_customer_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type,$_SESSION['alloc']->person_id);
402 if ($trans['debtor_no'] != $_SESSION['alloc']->person_id) {
403 display_error(_("Allocated transaction allocated is not related to company selected."));
404 set_focus('amount'.$counter);
407 } elseif ($_SESSION['alloc']->person_type == PT_SUPPLIER) {
408 if ($_SESSION['alloc']->allocs[$counter]->type == ST_PURCHORDER)
409 $trans = get_po($_SESSION['alloc']->allocs[$counter]->type_no);
411 $trans = get_supp_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type, $_SESSION['alloc']->person_id);
412 if ($trans['supplier_id'] != $_SESSION['alloc']->person_id) {
413 display_error(_("Allocated transaction allocated is not related to company selected."));
414 set_focus('amount'.$counter);
419 $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
421 $total_allocated += input_num('amount' . $counter);
424 $amount = abs($_SESSION['alloc']->amount);
426 if ($total_allocated - ($amount + input_num('discount')) > $SysPrefs->allocation_settled_allowance())
428 display_error(_("These allocations cannot be processed because the amount allocated is more than the total amount left to allocate."));
435 //----------------------------------------------------------------------------------------
437 // Returns sales or purchase invoice allocations to be reallocated after invoice edition.
439 function get_inv_allocations($trans_no, $trans_type, $person_id)
442 if ($trans_type == ST_SUPPINVOICE || $trans_type == ST_SUPPCREDIT)
443 $result = get_allocatable_from_supp_transactions($person_id, $trans_no, $trans_type);
445 $result = get_allocatable_from_cust_transactions($person_id, $trans_no, $trans_type);
447 while($dat = db_fetch($result))
449 $allocs[] = array('type'=> $dat['type'], 'trans_no'=> $dat['trans_no'], 'amount'=>$dat['alloc']);