Customer Payments, Payments to Supplier: additional checks to avoid data inconsistenc...
[fa-stable.git] / includes / ui / allocation_cart.inc
index bd84f5cc7533b738238d3d1d8694b7beddfb770d..22bb1e1caef6596b27251254f52b4bd789c2fdf5 100644 (file)
@@ -22,28 +22,42 @@ class allocation
        var $type;
        var $person_id = '';
        var $person_name = '';
-       var $person_type;
+       var $person_type;       // PT_SUPPLIER/PT_CUSTOMER
+       var $person_curr;
        var $date_;
        var $amount = 0; /*Total amount of the transaction in FX */
-       
-       var $allocs; /*array of transactions allocated to */    
+       var $currency;
+
+       var $allocs; /*array of transactions allocated to */
 
-       function allocation($type, $trans_no)
+       function __construct($type, $trans_no, $person_id = null, $person_type_id=null)
        {
                $this->allocs = array();
                
                $this->trans_no = $trans_no;
                $this->type = $type;
-               $this->read(); // read payment or credit
+               if ($person_id)
+                       $this->set_person($person_id, $person_type_id);
+
+               $this->read($type, $trans_no, $person_id, $person_type_id); // read payment or credit
+       }
+
+       function set_person($person_id, $person_type)
+       {
+               $this->person_id = $person_id;
+               $this->person_type = $person_type;
+               $this->person_curr = $person_type == PT_SUPPLIER ?
+                       get_supplier_currency($person_id) : get_customer_currency($person_id);
+               return $this->person_curr;
        }
 
        function add_item($type, $type_no, $date_, $due_date, $amount, $amount_allocated, 
-               $current_allocated)
+               $current_allocated, $ref)
        {
-               if ($amount > 0)
+               if (floatcmp($amount, 0))
                {
                        $this->allocs[count($this->allocs)] = new allocation_item($type, $type_no, 
-                               $date_, $due_date, $amount, $amount_allocated, $current_allocated);
+                               $date_, $due_date, $amount, $amount_allocated, $current_allocated, $ref);
                        return true;
                } 
                else 
@@ -53,22 +67,22 @@ class allocation
        }
        
        function update_item($index, $type, $type_no, $date_, $due_date, 
-               $amount, $amount_allocated, $current_allocated)
+               $amount, $amount_allocated, $current_allocated, $ref)
        {
-               if ($amount > 0)
+               if (floatcmp($amount, 0))
                {
                        $this->allocs[$index] = new allocation_item($type, $type_no, 
-                               $date_, $due_date, $amount, $amount_allocated, $current_allocated);
+                               $date_, $due_date, $amount, $amount_allocated, $current_allocated, $ref);
                        return true;
                } 
                else 
                {
                        return false;
                }
-       }       
+       }
        
        function add_or_update_item($type, $type_no, $date_, $due_date, 
-               $amount, $amount_allocated, $current_allocated)
+               $amount, $amount_allocated, $current_allocated, $ref)
        {
                for ($i = 0; $i < count($this->allocs); $i++) 
                {
@@ -76,82 +90,92 @@ class allocation
                        if (($item->type == $type) && ($item->type_no == $type_no)) 
                        {
                                return $this->update_item($i, $type, $type_no, $date_, $due_date, 
-                                       $amount, $amount_allocated, $current_allocated);
-                       }  
+                                       $amount, $amount_allocated, $current_allocated, $ref);
+                       }
                }
         return $this->add_item($type, $type_no, $date_, $due_date, 
-               $amount, $amount_allocated, $current_allocated);
-       }                                               
+               $amount, $amount_allocated, $current_allocated, $ref);
+       }
 
        //
        //      Read payment or credit current/available allocations to cart.
        //
-       function read($type = null, $trans_no = 0) 
+       // FIXME - read all transactions below twice seems to be suboptimal
+       //
+       function read($type = null, $trans_no = 0, $person_id=null, $person_type_id=null)
        {
-               if ($type == null) {    // re-read
+               if ($type !== null) {   // otherwise re-read
                        $type = $this->type;
                        $trans_no = $this->trans_no;
-               }
-               if ($type == ST_BANKPAYMENT || $type == ST_BANKDEPOSIT) {
-                       $bank_trans = db_fetch(get_bank_trans($type, $trans_no));
-                       $this->person_type = $bank_trans['person_type_id'] == PT_SUPPLIER;
-               } else
-                       $this->person_type = $type == ST_SUPPCREDIT || $type == ST_SUPPAYMENT;
-               $this->allocs = array();
 
-               if ($trans_no) {
-                       $trans = $this->person_type ? get_supp_trans($trans_no, $type) 
-                               : get_customer_trans($trans_no, $type);
+                       if (isset($person_type_id))
+                       {
+                               $this->person_type = $person_type_id;
+                               $this->person_id = $person_id;
+                       } else { // guess person_type_id
+                               if (in_array($type, array(ST_BANKPAYMENT, ST_BANKDEPOSIT)))
+                               {
+                                       $bank_trans = db_fetch(get_bank_trans($type, $trans_no));
+                                       $this->person_type = $bank_trans['person_type_id'];
+                               } else
+                                       $this->person_type = in_array($type, array(ST_SUPPCREDIT, ST_SUPPAYMENT)) ? PT_SUPPLIER : PT_CUSTOMER;
+                       }
 
-                       $this->person_id = $trans[$this->person_type ? 'supplier_id':'debtor_no'];
-                       $this->person_name = $trans[$this->person_type ? "supplier_name":"DebtorName"];
-                       $this->amount = $trans["Total"];
-                       $this->date_ = sql2date($trans["tran_date"]);
-               } 
-               else {
-                       $this->person_id = get_post($this->person_type ? 'supplier_id':'customer_id');
-                       $this->date_ = get_post($this->person_type ? 'DatePaid':'DateBanked', Today());
+                       if ($trans_no) {
+                               $trans = $this->person_type == PT_SUPPLIER ? get_supp_trans($trans_no, $type, $person_id)
+                                       : get_customer_trans($trans_no, $type, $person_id);
+
+                               $this->person_id = $trans[$this->person_type == PT_SUPPLIER ? 'supplier_id':'debtor_no'];
+                               $this->person_name = $trans[$this->person_type == PT_SUPPLIER ? "supplier_name":"DebtorName"];
+                               $this->date_ = sql2date($trans["tran_date"]);
+                               $this->person_curr = $trans['curr_code'];
+                               $this->currency = isset($trans['bank_curr_code']) ? $trans['bank_curr_code'] : $trans['curr_code'];
+                               $this->bank_amount = isset($trans["bank_amount"]) ? $trans["bank_amount"] : $trans["Total"]; // not set for journal entry
+                               $this->amount = $trans["Total"];
+                       }
                }
-
        /* Now populate the array of possible (and previous actual) allocations 
                for this customer/supplier. First get the transactions that have 
                outstanding balances ie Total-alloc >0 */
 
-               if ($this->person_type)
-                       $trans_items = get_allocatable_to_supp_transactions($this->person_id);
-               else
-                       $trans_items = get_allocatable_to_cust_transactions($this->person_id);
-
-               while ($myrow = db_fetch($trans_items))
+               $this->allocs = array();
+               if ($this->person_id)
                {
-                       $this->add_item($myrow["type"], $myrow["trans_no"],
-                               sql2date($myrow["tran_date"]),
-                               sql2date($myrow["due_date"]),
-                               $myrow["Total"], // trans total
-                               $myrow["alloc"], // trans total allocated
-                               0); // this allocation
+                       if ($this->person_type==PT_SUPPLIER)
+                               $trans_items = get_allocatable_to_supp_transactions($this->person_id);
+                       else
+                               $trans_items = get_allocatable_to_cust_transactions($this->person_id);
+                       while ($myrow = db_fetch($trans_items))
+                       {
+                               $this->add_item($myrow["type"], $myrow["trans_no"],
+                                       sql2date($myrow["tran_date"]),
+                                       sql2date($myrow["due_date"]),
+                                       $myrow["Total"], // trans total
+                                       $myrow["alloc"], // trans total allocated
+                                       0,
+                                       $myrow["reference"]); // this allocation
+                       }
                }
-
-               if ($trans_no == 0) return; // this is new payment
+               if ($this->trans_no == 0) return; // this is new payment
 
        /* Now get trans that might have previously been allocated to by this trans
        NB existing entries where still some of the trans outstanding entered from
        above logic will be overwritten with the prev alloc detail below */
 
-               if ($this->person_type)
+               if ($this->person_type==PT_SUPPLIER)
                        $trans_items = get_allocatable_to_supp_transactions($this->person_id, 
-                               $trans_no, $type);
+                               $this->trans_no, $this->type);
                else
                        $trans_items = get_allocatable_to_cust_transactions($this->person_id, 
-                               $trans_no, $type);
-       
+                               $this->trans_no, $this->type);
+
                while ($myrow = db_fetch($trans_items))
                {
                        $this->add_or_update_item ($myrow["type"], $myrow["trans_no"],
                                sql2date($myrow["tran_date"]),
                                sql2date($myrow["due_date"]),
                                $myrow["Total"],
-                               $myrow["alloc"] - $myrow["amt"], $myrow["amt"]);
+                               $myrow["alloc"] - $myrow["amt"], $myrow["amt"], $myrow["reference"]);
                }
        }
        //
@@ -159,54 +183,54 @@ class allocation
        //
        function write()
        {
+               global  $no_exchange_variations;
+
                begin_transaction();
 
-               if ($this->person_type)
-                       clear_supp_alloctions($this->type, $this->trans_no, $this->date_);
+               if ($this->person_type == PT_SUPPLIER)
+                       clear_supp_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
                else
-                       clear_cust_alloctions($this->type, $this->trans_no, $this->date_);
+                       clear_cust_alloctions($this->type, $this->trans_no, $this->person_id, $this->date_);
 
                // now add the new allocations
                $total_allocated = 0;
+               $dec = user_price_dec();
                foreach ($this->allocs as $alloc_item)
                {
                        if ($alloc_item->current_allocated > 0)
                        {
-                               if ($this->person_type) {
-                                       add_supp_allocation($alloc_item->current_allocated,
+                               $amount = round($alloc_item->current_allocated, $dec);
+
+                               if ($this->person_type == PT_SUPPLIER) {
+                                       add_supp_allocation($amount,
                                                $this->type, $this->trans_no,
-                                       $alloc_item->type, $alloc_item->type_no, $this->date_);
+                                       $alloc_item->type, $alloc_item->type_no, $this->person_id, $this->date_);
 
-                                       update_supp_trans_allocation($alloc_item->type, 
-                                               $alloc_item->type_no, $alloc_item->current_allocated);
+                                       update_supp_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
                                } else {
-                                       add_cust_allocation($alloc_item->current_allocated,
+                                       add_cust_allocation($amount,
                                                $this->type, $this->trans_no,
-                                       $alloc_item->type, $alloc_item->type_no, $this->date_);
-                       
-                                       update_debtor_trans_allocation($alloc_item->type, 
-                                               $alloc_item->type_no, $alloc_item->current_allocated);
+                                       $alloc_item->type, $alloc_item->type_no, $this->person_id, $this->date_);
+
+                                       update_debtor_trans_allocation($alloc_item->type, $alloc_item->type_no, $this->person_id);
                                }
                                // Exchange Variations Joe Hunt 2008-09-20 ////////////////////
-
-                               exchange_variation($this->type, $this->trans_no,
-                                       $alloc_item->type, $alloc_item->type_no, $this->date_,
-                                       $alloc_item->current_allocated,
-                                       $this->person_type ? PT_SUPPLIER : PT_CUSTOMER);
-                               
+                               if ($alloc_item->type != ST_SALESORDER && !@$no_exchange_variations
+                                       && $alloc_item->type != ST_PURCHORDER && $alloc_item->type != ST_JOURNAL && $this->type != ST_JOURNAL)
+                                       exchange_variation($this->type, $this->trans_no,
+                                               $alloc_item->type, $alloc_item->type_no, $this->date_,
+                                               $amount, $this->person_type);
 
                                //////////////////////////////////////////////////////////////
                                $total_allocated += $alloc_item->current_allocated;
                        }
 
                }  /*end of the loop through the array of allocations made */
-               if ($this->person_type)
-                       update_supp_trans_allocation($this->type, $this->trans_no, 
-                               $total_allocated);
+               if ($this->person_type == PT_SUPPLIER)
+                       update_supp_trans_allocation($this->type, $this->trans_no, $this->person_id);
                else
-                       update_debtor_trans_allocation($this->type,     $this->trans_no, 
-                               $total_allocated);
-       
+                       update_debtor_trans_allocation($this->type,     $this->trans_no, $this->person_id);
+
                commit_transaction();
 
        }
@@ -226,16 +250,19 @@ class allocation_item
        
        var $amount_allocated;
        var $amount;
+       var $ref;
        
        var $current_allocated;
        
-       function allocation_item ($type, $type_no, $date_, $due_date, $amount, 
-               $amount_allocated, $current_allocated)
+       function __construct($type, $type_no, $date_, $due_date, $amount, 
+               $amount_allocated, $current_allocated, $ref)
        {
 
                $this->type = $type;
                $this->type_no = $type_no;
-               
+
+               $this->ref = $ref;
+
                $this->date_ = $date_;
                $this->due_date = $due_date;
                
@@ -249,53 +276,57 @@ class allocation_item
 
 function show_allocatable($show_totals) {
 
-       global $table_style, $systypes_array;
+       global $systypes_array;
        
-    $k = $counter = $total_allocated = 0;
+    $k = $total_allocated = 0;
+
+       $cart = $_SESSION['alloc'];
+       $supp_ref = in_array($cart->type, array(ST_SUPPCREDIT, ST_SUPPAYMENT, ST_BANKPAYMENT));
 
-       if (count($_SESSION['alloc']->allocs)) 
+       if (count($cart->allocs)) 
        {
-               start_table("$table_style width=60%");
-               $th = array(_("Transaction Type"), _("#"), _("Date"), _("Due Date"), _("Amount"),
-                       _("Other Allocations"), _("This Allocation"), _("Left to Allocate"),'','');
+               display_heading(sprintf(_("Allocated amounts in %s:"), $cart->person_curr));
+               start_table(TABLESTYLE, "width='60%'");
+               $th = array(_("Transaction Type"), _("#"), $supp_ref ? _("Supplier Ref"): _("Ref"), _("Date"), _("Due Date"), _("Amount"),
+                       _("Other Allocations"), _("Left to Allocate"), _("This Allocation"),'','');
+
                table_header($th);
 
-               foreach ($_SESSION['alloc']->allocs as $alloc_item)
-           {
-                       alt_table_row_color($k);
-               label_cell($systypes_array[$alloc_item->type]);
-                       label_cell(get_trans_view_str($alloc_item->type, $alloc_item->type_no));
-               label_cell($alloc_item->date_, "align=right");
-               label_cell($alloc_item->due_date, "align=right");
-               amount_cell($alloc_item->amount);
-                       amount_cell($alloc_item->amount_allocated);
-
-               $_POST['amount' . $counter] = price_format($alloc_item->current_allocated);
-               amount_cells(null, "amount" . $counter, price_format('amount' . $counter));
-
-               $un_allocated = round($alloc_item->amount - $alloc_item->amount_allocated, 6);
-               amount_cell($un_allocated);
-                       label_cell("<a href='#' name=Alloc$counter onclick='allocate_all(this.name.substr(5));return true;'>"
-                                . _("All") . "</a>");
-                       label_cell("<a href='#' name=DeAll$counter onclick='allocate_none(this.name.substr(5));return true;'>"
-                                . _("None") . "</a>".hidden("un_allocated" . $counter, 
-                                price_format($un_allocated), false));
-                       end_row();
-
-               $total_allocated += input_num('amount' . $counter);
-                   $counter++;
-               }
+               foreach ($cart->allocs as $id => $alloc_item)
+               {
+                   if (floatcmp(abs($alloc_item->amount), $alloc_item->amount_allocated))
+                   {
+                               alt_table_row_color($k);
+                       label_cell($systypes_array[$alloc_item->type]);
+                               label_cell(get_trans_view_str($alloc_item->type, $alloc_item->type_no), "nowrap align='right'");
+                               label_cell($alloc_item->ref);
+                       label_cell($alloc_item->date_, "align=right");
+                       label_cell($alloc_item->due_date, "align=right");
+                       amount_cell(abs($alloc_item->amount));
+                               amount_cell($alloc_item->amount_allocated);
+
+                       $_POST['amount' . $id] = price_format($alloc_item->current_allocated);
+
+                       $un_allocated = round((abs($alloc_item->amount) - $alloc_item->amount_allocated), 6);
+                       amount_cell($un_allocated, false,'', 'maxval'.$id);
+                       amount_cells(null, "amount" . $id);//, input_num('amount' . $id));
+                               label_cell("<a href='javascript:void(0)' name=Alloc$id onclick='allocate_all(this.name.substr(5));return true;'>"
+                                        . _("All") . "</a>");
+                               label_cell("<a href='javascript:void(0)' name=DeAll$id onclick='allocate_none(this.name.substr(5));return true;'>"
+                                        . _("None") . "</a>".hidden("un_allocated" . $id, 
+                                        price_format($un_allocated), false));
+                               end_row();
+
+                       $total_allocated += input_num('amount' . $id);
+                       }
+               }
                if ($show_totals) {
                label_row(_("Total Allocated"), price_format($total_allocated),
-                       "colspan=6 align=right", "align=right id='total_allocated'", 3);
-                       $amount = $_SESSION['alloc']->amount;
-
-                       if ($_SESSION['alloc']->type == ST_SUPPCREDIT
-                               || $_SESSION['alloc']->type == ST_SUPPAYMENT
-                               ||  $_SESSION['alloc']->type == ST_BANKPAYMENT)
-                               $amount = -$amount;
-                       
-                       if ($amount - $total_allocated < 0)
+                       "colspan=8 align=right", "align=right id='total_allocated'", 3);
+
+                       $amount = abs($cart->amount);
+
+                       if (floatcmp($amount, $total_allocated) < 0)
                {
                        $font1 = "<font color=red>";
                        $font2 = "</font>";
@@ -304,12 +335,12 @@ function show_allocatable($show_totals) {
                        $font1 = $font2 = "";
                        $left_to_allocate = price_format($amount - $total_allocated);
                label_row(_("Left to Allocate"), $font1 . $left_to_allocate . $font2, 
-                               "colspan=6 align=right", "nowrap align=right id='left_to_allocate'",
+                               "colspan=8 align=right", "nowrap align=right id='left_to_allocate'",
                                 3);
                }
                end_table(1);
        }
-       hidden('TotalNumberOfAllocs', $counter);
+       hidden('TotalNumberOfAllocs', count($cart->allocs));
 }
 //--------------------------------------------------------------------------------
 
@@ -319,8 +350,9 @@ function check_allocations()
 
        $total_allocated = 0;
 
-       for ($counter = 0; $counter < $_POST["TotalNumberOfAllocs"]; $counter++)
+       for ($counter = 0; $counter < get_post("TotalNumberOfAllocs"); $counter++)
        {
+               if (!isset($_POST['amount'.$counter])) continue;
                if (!check_num('amount' . $counter, 0))
                {
                        display_error(_("The entry for one or more amounts is invalid or negative."));
@@ -328,23 +360,43 @@ function check_allocations()
                        return false;
                 }
 
-                 /*Now check to see that the AllocAmt is no greater than the
-                amount left to be allocated against the transaction under review */
-                if (input_num('amount' . $counter) > get_post('un_allocated' . $counter))
+                 /* Now check to see that the AllocAmt is no greater than the
+                amount left to be allocated against the transaction under review;
+                skip check if no allocation is set to avoid deadlock on mistakenly overallocated transactions*/
+                $allocated = input_num('amount' . $counter);
+                if ($allocated && ($allocated > input_num('un_allocated' . $counter)))
                 {
-                    //$_POST['amount' . $counter] = $_POST['un_allocated' . $counter];
+                       display_error(_("At least one transaction is overallocated."));
+                       set_focus('amount'.$counter);
+                       return false;
                 }
 
-                $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
+               if ($_SESSION['alloc']->person_type == PT_CUSTOMER) {
+                       if ($_SESSION['alloc']->allocs[$counter]->type == ST_SALESORDER)
+                               $trans = get_sales_order_header($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
+                       else
+                               $trans = get_customer_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
 
-                $total_allocated += input_num('amount' . $counter);
-       }
+                       if ($trans['debtor_no'] != $_SESSION['alloc']->person_id) {
+                               display_error(_("Allocated transaction allocated is not related to company selected."));
+                               set_focus('amount'.$counter);
+                               return false;
+                       }
+               } elseif ($_SESSION['alloc']->person_type == PT_SUPPLIER) {
+                       $trans = get_supp_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
+                       if ($trans['supplier_id'] != $_SESSION['alloc']->person_id) {
+                               display_error(_("Allocated transaction allocated is not related to company selected."));
+                               set_focus('amount'.$counter);
+                               return false;
+                       }
+               }
 
-       $amount = $_SESSION['alloc']->amount;
-       
+               $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);
 
-       if (in_array($_SESSION['alloc']->type, array(ST_BANKPAYMENT, ST_SUPPCREDIT, ST_SUPPAYMENT)))
-               $amount = -$amount;
+               $total_allocated += input_num('amount' . $counter);
+       }
+
+       $amount = abs($_SESSION['alloc']->amount);
 
        if ($total_allocated - ($amount + input_num('discount'))  > $SysPrefs->allocation_settled_allowance())
        {
@@ -355,4 +407,21 @@ function check_allocations()
        return true;
 }
 
-?>
+//----------------------------------------------------------------------------------------
+//
+//     Returns sales or purchase invoice allocations to be reallocated after invoice edition.
+//
+function get_inv_allocations($trans_no, $trans_type, $person_id)
+{
+       $allocs = array();
+       if ($trans_type == ST_SUPPINVOICE || $trans_type == ST_SUPPCREDIT)
+               $result = get_allocatable_from_supp_transactions($person_id, $trans_no, $trans_type);
+       else
+               $result = get_allocatable_from_cust_transactions($person_id, $trans_no, $trans_type);
+
+       while($dat = db_fetch($result))
+       {
+               $allocs[] = array('type'=> $dat['type'], 'trans_no'=> $dat['trans_no'], 'amount'=>$dat['alloc']);
+       }
+       return $allocs;
+}