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)
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);
69 function update_item($index, $type, $type_no, $date_, $due_date,
70 $amount, $amount_allocated, $current_allocated, $ref)
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);
84 function add_or_update_item($type, $type_no, $date_, $due_date,
85 $amount, $amount_allocated, $current_allocated, $ref)
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);
96 return $this->add_item($type, $type_no, $date_, $due_date,
97 $amount, $amount_allocated, $current_allocated, $ref);
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
156 $myrow["reference"]); // this allocation
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 $amount = $this->amount;
173 while ($myrow = db_fetch($trans_items))
175 $amount -= $myrow["Total"];
176 $this->add_or_update_item ($myrow["type"], $myrow["trans_no"],
177 sql2date($myrow["tran_date"]),
178 sql2date($myrow["due_date"]),
180 $myrow["alloc"] - $myrow["amt"], $myrow["amt"], $myrow["reference"]);
183 /* Finally, if there any unallocated money remaining, assign the unallocated portion to
184 the earliest transactions. This makes the javascript All/None keys consistent
185 with the transaction amount. */
188 foreach ($this->allocs as $alloc_item) {
189 $allocatable = $alloc_item->amount - $alloc_item->amount_allocated;
190 if ($allocatable > 0) {
191 if ($amount >= $allocatable) {
192 $alloc_item->current_allocated = $allocatable;
193 $amount -= $allocatable;
195 $alloc_item->current_allocated += $amount;
203 // Update allocations in database.
207 global $no_exchange_variations;
211 if ($this->person_type == PT_SUPPLIER)
212 clear_supp_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
214 clear_cust_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
216 // now add the new allocations
217 $total_allocated = 0;
218 $dec = user_price_dec();
219 foreach ($this->allocs as $alloc_item)
221 if ($alloc_item->current_allocated > 0)
223 $amount = round($alloc_item->current_allocated, $dec);
225 if ($this->person_type == PT_SUPPLIER) {
226 add_supp_allocation($amount,
227 $this->type, $this->trans_no,
228 $alloc_item->type, $alloc_item->type_no, $this->person_id, $this->date_);
230 update_supp_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
232 add_cust_allocation($amount,
233 $this->type, $this->trans_no,
234 $alloc_item->type, $alloc_item->type_no, $this->person_id, $this->date_);
236 update_debtor_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
238 // Exchange Variations Joe Hunt 2008-09-20 ////////////////////
239 if ($alloc_item->type != ST_SALESORDER && !@$no_exchange_variations
240 && $alloc_item->type != ST_PURCHORDER && $alloc_item->type != ST_JOURNAL && $this->type != ST_JOURNAL)
241 exchange_variation($this->type, $this->trans_no,
242 $alloc_item->type, $alloc_item->type_no, $this->date_,
243 $amount, $this->person_type);
245 //////////////////////////////////////////////////////////////
246 $total_allocated += $alloc_item->current_allocated;
249 } /*end of the loop through the array of allocations made */
250 if ($this->person_type == PT_SUPPLIER)
251 update_supp_trans_allocation($this->type, $this->trans_no, $this->person_id);
253 update_debtor_trans_allocation($this->type, $this->trans_no, $this->person_id);
255 commit_transaction();
261 //-----------------------------------------------------------------------------------
263 class allocation_item
272 var $amount_allocated;
276 var $current_allocated;
278 function __construct($type, $type_no, $date_, $due_date, $amount,
279 $amount_allocated, $current_allocated, $ref)
283 $this->type_no = $type_no;
287 $this->date_ = $date_;
288 $this->due_date = $due_date;
290 $this->amount = $amount;
291 $this->amount_allocated = $amount_allocated;
292 $this->current_allocated = $current_allocated;
296 //--------------------------------------------------------------------------------
298 function show_allocatable($show_totals) {
300 global $systypes_array;
302 $k = $total_allocated = 0;
304 $cart = $_SESSION['alloc'];
305 $supp_ref = in_array($cart->type, array(ST_SUPPCREDIT, ST_SUPPAYMENT, ST_BANKPAYMENT));
307 if (count($cart->allocs))
309 display_heading(sprintf(_("Allocated amounts in %s:"), $cart->person_curr));
310 start_table(TABLESTYLE, "width='60%'");
311 $th = array(_("Transaction Type"), _("#"), $supp_ref ? _("Supplier Ref"): _("Ref"), _("Date"), _("Due Date"), _("Amount"),
312 _("Other Allocations"), _("Left to Allocate"), _("This Allocation"),'','');
316 foreach ($cart->allocs as $id => $alloc_item)
318 if (floatcmp(abs($alloc_item->amount), $alloc_item->amount_allocated))
320 alt_table_row_color($k);
321 label_cell($systypes_array[$alloc_item->type]);
322 label_cell(get_trans_view_str($alloc_item->type, $alloc_item->type_no), "nowrap align='right'");
323 label_cell($alloc_item->ref);
324 label_cell($alloc_item->date_, "align=right");
325 label_cell($alloc_item->due_date, "align=right");
326 amount_cell(abs($alloc_item->amount));
327 amount_cell($alloc_item->amount_allocated);
329 $_POST['amount' . $id] = price_format($alloc_item->current_allocated);
331 $un_allocated = round((abs($alloc_item->amount) - $alloc_item->amount_allocated), 6);
332 amount_cell($un_allocated, false,'', 'maxval'.$id);
333 amount_cells(null, "amount" . $id);//, input_num('amount' . $id));
334 label_cell("<a href='javascript:void(0)' name=Alloc$id onclick='allocate_all(this.name.substr(5));return true;'>"
335 . _("All") . "</a>");
336 label_cell("<a href='javascript:void(0)' name=DeAll$id onclick='allocate_none(this.name.substr(5));return true;'>"
337 . _("None") . "</a>".hidden("un_allocated" . $id,
338 price_format($un_allocated), false));
341 $total_allocated += input_num('amount' . $id);
345 label_row(_("Total Allocated"), price_format($total_allocated),
346 "colspan=8 align=right", "align=right id='total_allocated'", 3);
348 $amount = abs($cart->amount);
350 if (floatcmp($amount, $total_allocated) < 0)
352 $font1 = "<font color=red>";
356 $font1 = $font2 = "";
357 $left_to_allocate = price_format($amount - $total_allocated);
358 label_row(_("Left to Allocate"), $font1 . $left_to_allocate . $font2,
359 "colspan=8 align=right", "nowrap align=right id='left_to_allocate'",
364 hidden('TotalNumberOfAllocs', count($cart->allocs));
366 //--------------------------------------------------------------------------------
368 function check_allocations()
372 $total_allocated = 0;
374 for ($counter = 0; $counter < get_post("TotalNumberOfAllocs"); $counter++)
376 if (!isset($_POST['amount'.$counter])) continue;
377 if (!check_num('amount' . $counter, 0))
379 display_error(_("The entry for one or more amounts is invalid or negative."));
380 set_focus('amount'.$counter);
384 /* Now check to see that the AllocAmt is no greater than the
385 amount left to be allocated against the transaction under review;
386 skip check if no allocation is set to avoid deadlock on mistakenly overallocated transactions*/
387 $allocated = input_num('amount' . $counter);
388 if ($allocated && ($allocated > input_num('un_allocated' . $counter)))
390 display_error(_("At least one transaction is overallocated."));
391 set_focus('amount'.$counter);
395 if ($_SESSION['alloc']->person_type == PT_CUSTOMER) {
396 if ($_SESSION['alloc']->allocs[$counter]->type == ST_SALESORDER)
397 $trans = get_sales_order_header($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
399 $trans = get_customer_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type,$_SESSION['alloc']->person_id);
401 if ($trans['debtor_no'] != $_SESSION['alloc']->person_id) {
402 display_error(_("Allocated transaction allocated is not related to company selected."));
403 set_focus('amount'.$counter);
406 } elseif ($_SESSION['alloc']->person_type == PT_SUPPLIER) {
407 if ($_SESSION['alloc']->allocs[$counter]->type == ST_PURCHORDER)
408 $trans = get_po($_SESSION['alloc']->allocs[$counter]->type_no);
410 $trans = get_supp_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type, $_SESSION['alloc']->person_id);
411 if ($trans['supplier_id'] != $_SESSION['alloc']->person_id) {
412 display_error(_("Allocated transaction allocated is not related to company selected."));
413 set_focus('amount'.$counter);
418 $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
420 $total_allocated += input_num('amount' . $counter);
423 $amount = abs($_SESSION['alloc']->amount);
425 if ($total_allocated - ($amount + input_num('discount')) > $SysPrefs->allocation_settled_allowance())
427 display_error(_("These allocations cannot be processed because the amount allocated is more than the total amount left to allocate."));
434 //----------------------------------------------------------------------------------------
436 // Returns sales or purchase invoice allocations to be reallocated after invoice edition.
438 function get_inv_allocations($trans_no, $trans_type, $person_id)
441 if ($trans_type == ST_SUPPINVOICE || $trans_type == ST_SUPPCREDIT)
442 $result = get_allocatable_from_supp_transactions($person_id, $trans_no, $trans_type);
444 $result = get_allocatable_from_cust_transactions($person_id, $trans_no, $trans_type);
446 while($dat = db_fetch($result))
448 $allocs[] = array('type'=> $dat['type'], 'trans_no'=> $dat['trans_no'], 'amount'=>$dat['alloc']);