Topic: Customer Payment allocation error when customer invoices fail to load

In a situation where the user's internet is not stable and the user is trying to post a customer payment, and changes the customer from customer list but the system fails to populate customer invoices for the selected customer. Since the customer selected and invoices do not match, the system will allocate the payment wrongly i.e customer payment to the right customer but wrong invoices. This has happened to our organisation already.

To solve this, I would like to change the check_allocations() function in frontaccounting\includes\ui\allocation_cart.inc as follows:

=>check that all allocations match the customer selected

NB: I would like this fix to be included in the next release.

/************************Add this code to the function:***********************/

if($_SESSION['alloc']->person_type == PT_CUSTOMER){
                     if($_SESSION['alloc']->allocs[$counter]->type  == ST_SALESINVOICE)
                        $trans = get_customer_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
                     else if ($_SESSION['alloc']->allocs[$counter]->type == ST_SALESORDER)
                         $trans = get_sales_order_header($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
                     
                    if($trans['debtor_no'] != $_SESSION['alloc']->person_id){
                       display_error(_("Cannot allocate to different persons."));
                       set_focus('amount'.$counter);
                       return false;
                    }
                 }

/*************************finally the function will be:************************/

function check_allocations()
{
    global $SysPrefs;

    $total_allocated = 0;

    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."));
            set_focus('amount'.$counter);
            return false;
         }

          /* 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)))
         {
            display_error(_("At least one transaction is overallocated."));
            set_focus('amount'.$counter);
            return false;
         }
                 
                 if($_SESSION['alloc']->person_type == PT_CUSTOMER){
                     if($_SESSION['alloc']->allocs[$counter]->type  == ST_SALESINVOICE)
                        $trans = get_customer_trans($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
                     else if ($_SESSION['alloc']->allocs[$counter]->type == ST_SALESORDER)
                         $trans = get_sales_order_header($_SESSION['alloc']->allocs[$counter]->type_no, $_SESSION['alloc']->allocs[$counter]->type);
                     
                    if($trans['debtor_no'] != $_SESSION['alloc']->person_id){
                       display_error(_("Cannot allocate to different persons."));
                       set_focus('amount'.$counter);
                       return false;
                    }
                 }

         $_SESSION['alloc']->allocs[$counter]->current_allocated = input_num('amount' . $counter);

         $total_allocated += input_num('amount' . $counter);
    }

    $amount = abs($_SESSION['alloc']->amount);

    if ($total_allocated - ($amount + input_num('discount') + input_num('whtax'))  > $SysPrefs->allocation_settled_allowance())
    {
        display_error(_("These allocations cannot be processed because the amount allocated is more than the total amount left to allocate."));
        return false;
    }

    return true;
}

Re: Customer Payment allocation error when customer invoices fail to load

There would likely be many areas in FA that are adversely affected by ajax timeouts caused by physical networking problems.  Trying to identify them and handle them in code would not be the best approach.  My recommendation would be changing the ajax timeout function in utils.js to something like:

                if(retry)
                    JsHttpRequest._request(trigger, form, tout, retry-1);
                else location.reload(true);

So on error, ajax would simply try to reload the page from the server.  If the network was still down, the browser clears the page and displays something like "cannot load page from server".  Thus it becomes impossible for the user to continue on with a loaded FA page that did not successfully run the ajax code.  This would prevent the problem that your site is experiencing.

The current FA approach to an ajax timeout is to display the warning sign, but this assumes that the internet is working, because if it is not, it may not be able to load the warning sign from the server if it is not already in cache.

Re: Customer Payment allocation error when customer invoices fail to load

Thanks @Braath Waate for your reply..
I have a concern with your solution. If a reload is done to the page, the user might lose the already entered data like in cases of "Direct sales Invoice". This might be frustrating to the user especially if a lot of data was entered.

Re: Customer Payment allocation error when customer invoices fail to load

I agree that the loss of data is frustrating, but this might be the only easy way to assure data integrity.  The code assumes that the ajax operation will complete successfully.  For a list, the list is already updated on the browser page and then the code requests the ajax update. If the ajax operation does not occur successfully, the page will have inconsistent data.

In the example of the customer payments screen, if the customer is updated and ajax fails, the branch will be wrong as well as the allocations and who knows what else?  Perhaps the customer has multiple branches, and the branch is updated and ajax fails.  Then the allocations would be assigned to the correct customer, but what effect will having the wrong branch have on the code? What if the bank account is changed, but ajax fails, and now the currency calculations are wrong?

There might be some easy way to cache the page before ajax is called and then restore the page if it fails, but that is beyond my knowledge.

Re: Customer Payment allocation error when customer invoices fail to load

Upon much thought and investigation, i fount that FA sets "_last" attribute to be the last selected Index of a select element. We can utilize this attribute on the ajax timeout function to reverse the element to the previous selectedIndex and also alert the user of this. See below:

 if(retry){
                    JsHttpRequest._request(trigger, form, tout, retry-1);
           } else {
                    var name = submitObj.getAttribute('name');
                    var SelectElement = document.getElementsByName(name.substring(1,name.length - 7))[0];
                    SelectElement.selectedIndex = SelectElement.getAttribute('_last');
                      alert("Server did not respond successfully.Selected value was not changed");
           }

After doing this, we will need to make sure that the "_last" attribute is only changed after completion of a request and not before the request. Also, only when "select_on_submit" is true. To do this, we change  "e.onchange" option of "_set_combo_select(e)" function in "inserts.js" as follows:

e.onchange = function() {
            var s = this;
                        var sname = '_'+s.name+'_update';
                        var update = document.getElementsByName(sname)[0];
                        if(!update) {
                            this.setAttribute('_last', this.selectedIndex);
                        }
            
            if(string_contains(s.className, 'combo') || string_contains(this.className, 'combo3'))
                _update_box(s);
            if(s.selectedIndex>=0) {
                 if(update) {
                        JsHttpRequest.request(update);
                }
            }
            return true;
        }

Lastly, change "_last" attribute if there is a successful response. In "utils.js" :

 if (result) {
if (trigger.tagName=='SELECT') {
    var name = submitObj.getAttribute('name');
    var SelectElement = document.getElementsByName(name.substring(1,name.length - 7)[0]);
    SelectElement.setAttribute('_last', SelectElement.selectedIndex);
}...

Please let me know if this approach is acceptable and helpful.
Thank you.

Re: Customer Payment allocation error when customer invoices fail to load

Your approach is promising.  This should work in theory for lists which are the most frequent occurrence of ajax sync, so perhaps this approach could fix the lions share of the problem.  However, ajax sync can be called for other data types, such as dates, so it is not a complete solution.

I don't have the ability to test this, but it seems like it does close one avenue of data corruption in FA in a more user friendly way than just simply reloading the page as I proposed.

Re: Customer Payment allocation error when customer invoices fail to load

Thanks to your direction, i was able to note that all inputs that require ajax sync have a previous value attached to them. e.g. the date inputs have  "_last_val" attribute. I will use this like in the solution above.

I would like to follow this to the end and make sure a proper solution is found.
After I go on this journey successfully, what are the steps if i needed such and more fixes incorporated in the main project?

Re: Customer Payment allocation error when customer invoices fail to load

@joe: ripe enough to incorporate into the core?

Re: Customer Payment allocation error when customer invoices fail to load

Have asked Janusz to look into this.

Joe

Re: Customer Payment allocation error when customer invoices fail to load

FA indeed needs better mechanism to avoid problems with ajax timeout, but the javascript code is complex, so it wll need some time to adopt. For now I have included in repo proposed additional checks for payment allocations (for both supplier and customer).

If you have any ready bugfix  or complete improvement suitable for most FA users you can send it either to contributions mailbox or via mantis, but better is approach seems to be presenting proposed changes on forum. This allows wider change review and usually helps make changes mature enough to be integrated into main code.

Janusz

Re: Customer Payment allocation error when customer invoices fail to load

Thank you for this inclusion...