Sunday 25 August 2013

Using regex patterns for verifying input

We have recently been using HTML 5 and JavaScript to do form verification, as Fahad blogged about here

I have just added pattern matching to this for email and telephone number verification. Thanks to +Exson Qu  for the regex ju-ju. 

HTML5 allows form elements of type "tel" and "email". We want to add in a regular expression to each of these types to verify the element as it is typed. The regular expressions we have used are "[0-9 +s()]*" for the telephone numbers, and "^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$" for email addresses. Neither of these are 100% as it is not possible to deal with every method in the world of writing telephone numbers, and every method of constructing emails addresses, however these will do a good job on most.

It is feasible to add html in the form:

<input type="tel" name="Telephone" pattern="[0-9 +s()]*" value="" />

However this would need to be done wherever there is a telephone or fax number to be entered into KwaMoja. Then if we decide to improve the regex we would need to go through all these entries again. This can get messy.

It would be better to do this via JavaScript. We have a function called initial() that gets called when the page has been loaded. This function iterates through all the form elements in the page that has just been loaded and appends the regex pattern any with a type "tel" or a type "email". Here is the function:

function initial() {
    var n = document.getElementsByTagName("input");
    for (i = 0; i < n.length; i++) {
        if (n[i].type == "tel") n[i].pattern = "[0-9 +s()]*";
        if (n[i].type == "email") n[i].pattern = "^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$";
    }
}


We also use this function to assign other properties to form elements.

Saturday 17 August 2013

Warehouse management - Part 1

I have been giving a lot of thought recently to how we should implement warehouse management. Just using row/bay/bin is too restrictive. 

If we have a warehouse structured like this :


it will not fit well to a rigid system of warehouse management.
 

What I propose a system where we have just one table for areas that can contain stock items. The best name would be "Locations" but that has already been taken, so I will for the moment call them "Containers". All containers can have parent containers. The above warehouse now looks like this:

As you can see the warehouse now can be modeled as a tree diagram. Each container can be set as to whether it can contain items, or just act as a container for child containers.







Each container has details of its physical position in the warehouse, and it's size. Also properties as to what it can contain (refrigeration unit for instance). Whether the contents are liquid etc.


Starting to implement this is easy, as each warehouse can be defined as a container. We just need a new table to hold the container information.

Monday 5 August 2013

How to sort HTML tables in JavaScript

A short while ago I blogged that KwaMoja now has the facility to sort HTML tables in JavaScript. I have had numerous requests to explain more about how this is done, so here is a brief tutorial on achieving this.

The table has to be constructed in a specific way. The area of the table to be sorted has to contain one row of <th> cells followed by any number of <td> rows.

So the following HTML would be suitable:

<tr>
        <th>Header1</th>
        <th>Header2</th>
</tr>
<tr>
        <td>Data11</td>
        <td>Data12</td>
</tr>
<tr>
        <td>Data21</td>
        <td>Data22</td>
</tr>

which will look like this:


Header1 Header2
Data11 Data12
Data21 Data22

This area to be sorted must be completely contained within a pair of tags, for instance between a <table> and a </table>. If you require other things in the table, then I generally use a <tbody> and a </tbody> to surround the area to be sorted.

The JavaScript function starts with the following:



1 function SortSelect(selElem) {
2    table=selElem.parentNode.parentNode;
3    headerRow = table.rows[0];


The function is called by clicking on the <th></th> element of the column that we wish to sort by. Thus the parameter "selElem" will be that cell. The parent of that cell will be the row (<tr>) containing the header, and the parent of the row will be the element that contains the whole of the sortable table.

Thus line 2 will give us the entire table that we wish to sort. Line 3 assigns the first row of that table to the variable "headerRow"

4    columnText=selElem.innerHTML;
5    for (var j = 0, col; col = headerRow.cells[j]; j++) {
6        if (
headerRow.cells[j].innerHTML==columnText) {
7            columnNumber=j;

8    }

This section finds the column number that is to be sorted on. Line 4 finds the column header, and then lines 5 to 8 loop through the columns until the correct one is found.

09    var tmpArray = new Array();
10    for (var i = 1, row; row = table.rows[i]; i++) {
11        var rowArray = new Array();
12        for (var j = 0, col; col = row.cells[j]; j++) {
13            if (row.cells[j].tagName == 'TD' ) {
14                rowArray[j]=row.cells[j].innerHTML;
15            }
16        }
17        tmpArray[i]=rowArray;
18    }

 
This section gets the information from each cell, and feeds it into a JavaScript array object before sorting it. Line 9 creates the array object then we cycle through each row (starting at the second line, as the first contains the headings), creating an array object for each row, and finally line 17 adds that row array to the main array.

19    tmpArray.sort(
20        function(a,b) {
21                if (columnClass=="number") {
22                    return parseFloat(a[columnNumber])-parseFloat(b[columnNumber]);
23                } else if (columnClass=="date") {
24                    da=new Date(a[columnNumber]);
25                    db=new Date(b[columnNumber]);
26                    return da>db;
27                } else {
28                    return a[columnNumber].localeCompare(b[columnNumber])

29                }
30        }
31    );
 

This section performs a standard JavaScript 3d array sorting routine. It will sort by number, alphabetic, and date. Other sorting routines can be included as needed.

Finally we need to feed the sorted array back into the html table as here

32    for (var i = 0, row; row = table.rows[i+1]; i++) {
33        var rowArray = new Array();
34        rowArray=tmpArray[i];
35        for (var j = 0, col; col = row.cells[j]; j++) {
36            if (row.cells[j].tagName == 'TD' ) {
37                row.cells[j].innerHTML=rowArray[j];
38            }
39        }
40    }
 

and we have fully sorted the HTML table in 40 lines of JavaScript. I have added in some nice formatting, and cursors in, and get the following full function:

function SortSelect(selElem) {
    var tmpArray = new Array();
    th=document.getElementById("Theme").value;
    columnText=selElem.innerHTML;
    table=selElem.parentNode.parentNode;
    row = table.rows[0];
    for (var j = 0, col; col = row.cells[j]; j++) {
        if (row.cells[j].innerHTML==columnText) {
            columnNumber=j;
            s=getComputedStyle(row.cells[j], null);
            if (s.cursor=="s-resize") {
                row.cells[j].style.cursor="n-resize";
                row.cells[j].style.backgroundImage="url('css/"+th+"/images/descending.png')";
                row.cells[j].style.backgroundPosition="right center";
                row.cells[j].style.backgroundRepeat="no-repeat";
                row.cells[j].style.backgroundSize="12px";
                direction="a";
            } else {
                row.cells[j].style.cursor="s-resize";
                row.cells[j].style.backgroundImage="url('css/"+th+"/images/ascending.png')";
                row.cells[j].style.backgroundPosition="right center";
                row.cells[j].style.backgroundRepeat="no-repeat";
                row.cells[j].style.backgroundSize="12px";
                direction="d";
            }
        }
    }
    for (var i = 1, row; row = table.rows[i]; i++) {
        var rowArray = new Array();
        for (var j = 0, col; col = row.cells[j]; j++) {
            if (row.cells[j].tagName == 'TD' ) {
                rowArray[j]=row.cells[j].innerHTML;
                columnClass=row.cells[columnNumber].className;
            }
        }
        tmpArray[i]=rowArray;
    }
    tmpArray.sort(
        function(a,b) {
            if (direction=="a") {
                if (columnClass=="number") {
                    return parseFloat(a[columnNumber])-parseFloat(b[columnNumber]);
                } else if (columnClass=="date") {
                    da=new Date(a[columnNumber]);
                    db=new Date(b[columnNumber]);
                    return da>db;
                } else {
                    return a[columnNumber].localeCompare(b[columnNumber])
                }
            } else {
                if (columnClass=="number") {
                    return parseFloat(b[columnNumber])-parseFloat(a[columnNumber]);
                } else if (columnClass=="date") {
                    da=new Date(a[columnNumber]);
                    db=new Date(b[columnNumber]);
                    return da<=db;
                } else {
                    return b[columnNumber].localeCompare(a[columnNumber])
                }
            }
        }
    );
    for (var i = 0, row; row = table.rows[i+1]; i++) {
        var rowArray = new Array();
        rowArray=tmpArray[i];
        for (var j = 0, col; col = row.cells[j]; j++) {
            if (row.cells[j].tagName == 'TD' ) {
                row.cells[j].innerHTML=rowArray[j];
            }
        }
    }
    return;
}


This is called by including this inline onclick handler

 onclick="SortSelect(this)"

I hope this explanation is useful to some people.

Monday 29 July 2013

Is it that Phil lies, or he just cannot count past one?

This page is written in response to the lies that +Phil Daintree has written about me, and spread on the internet. Despite years of searching he has been unable to find anything I have written that is untrue, and he has had to resort to vague generalities, faked emails, and badly fabricated screenshots (you can see the joins if you zoom in using any bit mapped image editor). +Phil Daintree is welcome to make any comments to these pages, as he has done in the past. If I agree with what he says I will amend my writings, if I do not agree I have allowed his comments to stand next to mine so that people can make their own judgements. I have every confidence in the intelligence of readers to make a sensible judgement based on the facts. +Phil Daintree will not allow me the right of reply to any of the lies he has told about me. It seems to me significant that he realises that if people see both sides of the argument they will see through his lies.

Recently a long term webERP user and advocate wrote to +Phil Daintree saying he should stand down as admin of the project because the way he is running the project is destroying it.

In his reply to that user he says "I have only had to exclude one person".

Not so Phil, I have an email from you where you state that you "excluded" Steve Kaill, one of the earlier webERP developers, as can be seen from the change log, and a long term helper on the mailing list as a search of the archives can demonstrate.

I have an email from Phil where he tells me that he "excluded" Danie Brink. Another one of the early developers of webERP who he decided to force out of the project. Again a search of both the change logs and the mailing list archives will show the amount of his contribution. Amusingly given Phil's recent behaviour one of his complaints against Danie was that Phil claimed he had written some addons to webERP that he had not given back.

I also have three other emails with details of people Phil has "excluded" from the project, who are prepared to be named if he disputes this fact.

Interestingly enough one of the complaints about Phil that "forced" him to exclude these people is his claim to own the copyright of all the code.

So there you have it. Phil added these up and came to the total of one.

So is it that Phil can only count as far as one, or is this yet another of his lies to add to the long history of lies that he has told this project?

Sunday 28 July 2013

Customer Relationship Management

I am starting to code up CRM in @KwaMoja. In the past CRM has largely been ignored in webERP.

For this I have firstly added the ability to have sub areas. So for instance we could have the following setup:

                                             East African Community
                                                                |
                                                                |
                         ________________________|
                         |                         |             
                     Kenya               Uganda           
                         |
   ______________|____________
   |               |              |          |
             Coastal   Central
                   |
           Mombasa

As you can see its now possible to have multilayer sales regions. 

Next I have added the sales area to the sales person record. So now a sales person can be allocated to a particular area. In the above diagram our sales person can be allocated to any of the areas. So they could cover the whole of Kenya, or they could just cover the area of Mombasa.

Also I have added a flag to the sales person record to record whether they are an area manager. Each Area can have only one area manager. 

So it is possible to have a regional manager for Kenya, and also an area manager for each of the regions, Coastal, Central, etc.

This has already been coded and committed to the development code. Now we need to look at configuring the sales cycle.

Saturday 27 July 2013

When censorsip goes mad

This page is written in response to the lies that +Phil Daintree has written about me, and spread on the internet. Despite years of searching he has been unable to find anything I have written that is untrue, and he has had to resort to vague generalities, faked emails, and badly fabricated screenshots (you can see the joins if you zoom in using any bit mapped image editor). +Phil Daintree is welcome to make any comments to these pages, as he has done in the past. If I agree with what he says I will amend my writings, if I do not agree I have allowed his comments to stand next to mine so that people can make their own judgements. I have every confidence in the intelligence of readers to make a sensible judgement based on the facts. +Phil Daintree will not allow me the right of reply to any of the lies he has told about me. It seems to me significant that he realises that if people see both sides of the argument they will see through his lies.

In a recent discussion on the webERP mailing list +Phil Daintree was writing about the new confirm boxes I did for +KwaMoja . This is the email he wrote:
Tim has also made some improvements to the confirm boxes but probably
unnecessary really, especially since it adds significantly to the size
of the file. 
 I then wrote back:
It only adds 996 bytes to the file, and this could be shortened further with the use of smaller variable names. To my mind they give a more consistent and professional feel across all browsers.
Phil refused to allow this email on the mailing list (for proof of this see previous blog entries), but I posted it to the nabble forums anyway as I thought it might be interesting to people. I then went away and played with this JavaScript a bit more, and managed to reduce the size of this function a bit more, so I posted the following:
Just for interest I reduced this to 921 bytes (0.899Kb) by using shorter variable names.
I tried this to the mailing list, but as usual it was rejected, so I posted it to the nabble forums. To my surprise it got deleted from there. I re-posted it and it got deleted from there again. It has now been deleted seven times by +Phil Daintree or one of his cohorts.

Now can anybody work out why a short post about reducing the size of a JavaScript function to 922 bytes should be considered so offensive that the readers of the nabble forums and the mailing lists need to be protected from reading it?
The only thing that I can imagine is that a recurring theme in his hate pages about me, is how much better a programmer he is than me, and maybe he thinks this email doesn't help that claim. I can't think of another reason.

Philippians 4:8

Amendment 3/1/2014:  I have since been informed  by +Exson Qu and +Phil Daintree that the reasons for deleting the above postings was that commenting on the length of a JavaScript function constituted a personal attack on +Phil Daintree. I have asked for clarification from either +Exson Qu or +Phil Daintree as to why this was, but have received no reply. My best guess is that +Phil Daintree had already said that including the function "adds significantly to the size of the file", and so my pointing out that it was actually quite small was contradicting him and so constituted a personal attack. Personally I think it constitutes valid technical discussion but I include it here as I have always said I am happy to put both sides of the discussion forward and allow readers to make their own minds up.











 

Thursday 25 July 2013

Phil's morals reach new low

This page is written in response to the lies that +Phil Daintree  has written about me, and spread on the internet. Despite years of searching he has been unable to find anything I have written that is untrue, and he has had to resort to vague generalities, faked emails, and badly fabricated screenshots (you can see the joins if you zoom in using any bit mapped image editor). +Phil Daintree is welcome to make any comments to these pages, as he has done in the past. If I agree with what he says I will amend my writings, if I do not agree I have allowed his comments to stand next to mine so that people can make their own judgements. I have every confidence in the intelligence of readers to make a sensible judgement based on the facts. +Phil Daintree will not allow me the right of reply to any of the lies he has told about me. It seems to me significant that he realises that if people see both sides of the argument they will see through his lies.

It appears that +Phil Daintree has nothing better to do with his time and money than to spend it delving into my private affairs in a desperate attempt to find an example of somewhere I had said something untrue.

This week he sent an email to the webERP mailing list that was faked to appear to come from my email address. This email made some claims about me that were completely untrue. I sent three emails to the list that proved that the accusations were false. Like all my emails to the lists they were rejected. If there is anybody out there who still doesn't believe that my emails are rejected, here is the full rejection including the headers:

Delivered-To: tim.schofield1960@gmail.com Received: by 10.205.22.136 with SMTP id qw8csp104466bkb; Wed, 24 Jul 2013 13:40:56 -0700 (PDT) X-Received: by 10.182.43.230 with SMTP id z6mr31561268obl.82.1374698455868; Wed, 24 Jul 2013 13:40:55 -0700 (PDT) Return-Path: <web-erp-developers-bounces@lists.sourceforge.net> Received: from lists.sourceforge.net (lists.sourceforge.net. [216.34.181.88]) by mx.google.com with ESMTPS id fg9si22656181obc.146.2013.07.24.13.40.55 for <tim.schofield1960@gmail.com> (version=TLSv1 cipher=RC4-SHA bits=128/128); Wed, 24 Jul 2013 13:40:55 -0700 (PDT) Received-SPF: pass (google.com: domain of web-erp-developers-bounces@lists.MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Subject: Request to mailing list Web-erp-developers rejected From: web-erp-developers-bounces@lists.sourceforge.net To: tim.schofield1960@gmail.com Message-ID: <mailman.0.1374698451.16766.web-erp-developers@lists.sourceforge.net> Date: Wed, 24 Jul 2013 20:40:51 +0000 Precedence: bulk X-BeenThere: web-erp-developers@lists.sourceforge.net X-Mailman-Version: 2.1.9 List-Id: webERP Developers <web-erp-developers.lists.sourceforge.net> X-List-Administrivia: yes Errors-To: web-erp-developers-bounces@lists.sourceforge.net Your request to the Web-erp-developers mailing list Posting of your message titled "Re: [WebERP-developers] Testing Tims weberpafrica.com email" has been rejected by the list moderator. The moderator gave the following reason for rejecting your request: "No reason given" Any questions or comments should be directed to the list administrator at: web-erp-developers-owner@lists.sourceforge.net 


Oh, and just in case there is anybody out there who still believe in Phil's lie about me only being moderated on te forums, here is your proof:


Having failed to get my refutation on the mailing list I tried sending them to the nabble forum. Phil then removed my postings to that forum as well. So he is happy to post lies about me to the mailing lists, but removes my refutations. I have known Phil a long time, and suffered greatly from his hate campaign against me, but even I was surprised by just how low he has now sunk.


This isn't new, I am not the first to suffer in this way. Phil forced out many of the earlier developers without whom webERP would never have existed. People such as +Steve Kaill and Danie Brink who did so much of the early work (if you don't believe me just Google their names along with webERP, or simply check out the change log distributed with webERP) were forced out of the project back in 2007.


If this is the way that +Phil Daintree keeps on treating developers then the project will die, new developers wont want to be treated liked this. 

The Lord will judge Phil for his lies and his deceit, but I am sure that the webERP community will judge him for the harm he is doing to our project.