Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Monday 13 January 2014

Updated Supplier Payments in KwaMoja

One frequently asked questions we get on the KwaMoja project is why you cannot just select the supplier items you want to pay, when you are making a payment to a supplier.

Well now you can:
As can be seen in the above screenshot, all open items are shown for the supplier. Checking those items adds it to the amount to be paid. If it is a credit or old payment, then the amounts will be deducted instead of added.

If the item is unchecked then the amount is deducted.

Once you hit the "Accept and Process Payment" button then the payment is posted and the items are allocated against this payment.

Monday 30 September 2013

What KwaMoja is and what KwaMoja is not

This is a post about what exactly KwaMoja is, and why we are doing it, and also to explain properly what it is not.

Origins

KwaMoja has its origins in some lectures I did at a community college in Kenya. The basis of the lectures was extending the webERP software in various directions. After the lectures some of the students wanted to carry on the work. Due to the unfriendly and often abusive style of management at webERP and I suggested they speak to Munir Patel who had previously talked to me about doing an African/Indian based fork of webERP, and so KwaMoja was born.

When it became obvious that certain people were intent on destroying the project for their own selfish ends, I got involved more deeply.

Vision

Our vision at KwaMoja is to make African businesses more competitive with those in the west. In the Eighties, western businesses went through a revolution, lowering inventory costs, improving efficiencies, improving quality, and raising profits for their shareholders. Central to this revolution was the rolling out of ERP software. Very few businesses in the west do not now take advantage of ERP. We believe that KwaMoja is that ERP software for African businesses.

Free Software

KwaMoja is Free software. Now free in this sense means that KwaMoja code comes to you free of restrictions. You can do whatever you like with it, so long as you maintain those freedoms. When you buy software from Apple for instance, the software comes with many restrictions as to what you can and cannot do with it. You can only use it to do what some people in grey suits at Apple's HQ want you to do with it. Free software does not necessarily mean that you cannot charge people for the software, anybody who wants to charge for KwaMoja can do, and anybody who wishes to give it away free can also do that.

Where is the money?

KwaMoja itself has no funding. It is not a company, we have no income, and we have no employees. It is made possible by donations of time and money from those of us involved. However we are not super rich philanthropists. Far from it, and we have had to earn money elsewhere while getting the project ready for release. There is no money to pay web designers, programmers etc.

Many people and companies in the west have become wealthy by being consultants in the implementation of ERP systems. They have no financial interest in the software themselves. They merely work on the implementation side, and people like SAP get the software fees themselves. These fees are often prohibitive to African companies. Our aim at KwaMoja is to build an ecosystem so that many people can get rich on the back of the KwaMoja project. Our aim is to get as many people as possible selling consultancy services around this project.

Consultants who work for $250 a day are considered very cheap, even in African countries. The rewards can be huge.

Why would someone need consultancy?

According to research at least 65% of ERP implementations fail. Failure can often have disastrous implications for a business, whilst success can bring huge benefits to a business. Employing good experienced consultants is often the difference between success and failure. This is why western businesses will always pay for consultancy.

Why would I want to donate my time and skills?

Good question. As I have said above we are none of us rich philanthropists, and we are all seeking to make some money in the end. Imagine you are a company that is looking for consultancy support for it's implementation . Would you choose a consultant who has been actively involved in the building of the software, or someone who has done nothing for it? My guess is that those who contribute the most will earn the most as consultants. You would also gain a global audience to showcase your skills.

Is it just for Africa?

No, definitely not. This software can be used anywhere in the world.

Conclusion

So there it is, that is what KwaMoja is. Sound interesting? Then join us, there is always tons to do.

Saturday 28 September 2013

The "illegal characters" debate

This page is written in response to the lies tha +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 response to this blog post of mine, +Phil Daintree has posted this to the mailing list. I tried to post a reply to the mailing list, but it was rejected, so I publish it here in the hope it is useful to the debate:

"Phil,

It seems to me that in your attempts to control what characters the user can use you have lost sight of why you are trying to do it. The point of an ERP is to help the user do what is best for their business rather than to tell them what you think is best for their business.

For instance you have recently put a check on what characters can be used in the users company name. Why should it be up to you to decide how a user should spell their company name? What you should be doing is trying these characters in the company name, and then looking at all the places in the code where that field is used, and seeing if these characters cause a problem. I suspect they don't cause any issues, but if they do you should be trying to resolve those issues, not just banning the characters.

Likewise with item codes. The code 3.5K/V-5"x3" would be a perfectly legitimate code for a user to want to use, but would fail the new illegal character tests on several grounds. Yes a user could work around this problem, but why should they when most other ERP packages will allow it?

It seems to me that this desire of yours to control everyone and everything is pulling webERP down. Let us free the users up to do what is best for them, instead of all the time telling them what you think is best for them.

Thanks
Tim"

Friday 6 September 2013

Introducing the KwaMoja dashboard

+KwaMoja now has a customisable dashboard.



This code has been kindly donated to +KwaMoja by +Mythri Hegde of Netelity and adds considerably to the usability of +KwaMoja.

We can produce any number of small reports to show in the dashboard. +Mythri Hegde has already contributed nine, and they look like this:

Each of these mini reports has a security token assigned to it. The user can only add the report to their dashboard if they have that security token. See here for more details of security tokens.

All a user has to do to remove it from their dashboard is co click on the X in the top right hand corner of the report.

At the bottom of the dashboard is a selection box
Clicking on this drops down a list of all those reports that a user can add to their dashboard, as follows:
Choosing from here adds the report to the users dashboard. The screen shot above shows 6 reports, but the user can have any number of reports showing depending on what they want.

Many thanks to +Mythri Hegde for her hard work and for contributing the code back to the project.

See the dashboard in operation by going to the KwaMoja demo at http://www.kwamoja.com/demo

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.

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 8 July 2013

Sorting html tables

I have just committed the code to sort HTML tables. So far this applies to only certain scripts, but to add it to any is easy, and should be done in all by tomorrow.

Here is the result of searching for stock items in SelectProduct.php


Now by clicking at the top of the Description column the table is automatically sorted and shows
and the items are sorted by item description. Clicking on that column again shows:

and the order of the items is reversed. This also holds true for the code column. I have also done the SelectCustomer.php script and the SelectSupplier.php script.

All this is done with one simple JavaScript function and minimal changes to the scripts.

This and the changes committed by Fahad earlier today means a huge leap forward for usability in KwaMoja. Hope you enjoy this new code.

Tuesday 25 June 2013

Lot's of great new functionality

KwaMoja has just gained lots of new functionality.

We now have a maintenance module for fixed assets. This allows you to set specific maintenance tasks for individual fixed assets, set up a schedule for these tasks to be done, and to email the person responsible with reminders when the tasks are becoming due.

Electronic bank statements in the MT940 format can now be imported and analysed directly into the KwaMoja general ledger.

Many thanks must go to Phil Daintree for this excellent contribution.

Phil has also setup a demo mode, which will stop whoever it is with an IP address in Wellington, New Zealand that has too much time on their hands and too little to occupy themselves. This person keeps trying to come up with new ways to wreck our demo. Thanks Phil for helping to stop them.

Also KwaMoja now links to Phil's new web shop, and it can be administered directly through KwaMoja. This web shop is commercial software, and not open source, so it is hard for us to test and verify, so caution  needs to be observed on the security side of this as it stores information about your customers payment methods. Nevertheless a great contribution, thanks Phil.

We now also have the ability to import general ledger journals, payments, and receipts directly from a csv file.

Also there is now a flag setting in the user setup that can restrict a user to only access their default location. Thanks Jerry for sponsoring this work.

All this new functionality can be tried out at the KwaMoja demo.




Wednesday 22 May 2013

New Work Order Entry script

I have never liked the old method of entering work orders in webERP and now I am no longer allowed to help people on the mailing lists and forums I have more time to devote to correcting it.

The old script didn't follow the normal webERP conventions, for instance it allocated a work order number, as soon as user clicked on that menu option, even if they didn't go on to enter an order, and this meant tracking order numbers was impossible. Sales and purchase orders both use a class to hold the order details in but Work orders didn't. The code was messy, and didn't abide by the coding guidelines.

In fact I am surprised it was ever allowed to corrupt the code base. I have just uploaded a revised script, which has more functionality, but in a lot less code. "Do more with less" is a good motto for these types of things.

Unfortunately I am not allowed to push this new functionality to the webERP svn repository, so you need to get it from here https://github.com/timschofield/KwaMoja/archive/develop.zip if you wish to test it. Hopefully someone who is still allowed to commit to svn will push it to webERP.

Any issues or bugs please let me know.




Tuesday 14 May 2013

ABC analysis of stock items.

One of the most important methods of keeping accurate stock levels - though by no means the most widely used, especially in Africa - is cyclical stock checking. That is the constant checking of the stock throughout a financial period, rather than leaving stock checks to the period end.

This has two big advantages in the control of an organisations stock. Firstly the stock levels are kept in a much more accurate state as they are checked more frequently, and secondly any variances are far easier to track down if the period between checking the item is much shorter.

A necessary prerequisite of cyclical stock checking is an ABC analysis of the organisations stock. By ABC analysis I mean to rank the items by:

A - The most important items to the organisation
B - Important to the organisation but not critical
C - Slow moving or non important stock items.

At the moment we have no ABC analysis. This is a proposal to rectify that.

ABC classification is a way of grouping your stock items. There are a few different ways to set up an ABC Ranking, such as Velocity (times sold), Quantity sold/Consumed or by Margin. But the most common method is the Annual Sales Volume ranking. This method will allow you to identify the small number of items that usually account for most of your sales value (think 80/20 rule).

My plan is to first implement Annual Sales Volume Ranking method, but to do it in such a way as to make adding other methods easy in the future. To do this I propose to setup a table to hold the methods to be used. This table will have the following structure:

CREATE TABLE `abcmethods` (
     `methodid` TINYINT NOT NULL DEFAULT 0,
     `methodname` VARCHAR(40) NOT NULL DEFAULT '',
     PRIMARY KEY (`methodid`)
);

Initially this will have just one record in, 
methodid=>0 
methodname=>Annual Sales Volume Ranking

The next table will contain the groups that are being used. This will specify the criteria used. Each method can have several groups. I propose the following table:

CREATE TABLE `abcgroups`(
     `groupid` INT(11) NOT NULL DEFAULT 0,
     `groupname` VARCHAR(40) NOT NULL DEFAULT '',
     `methodid` TINYINT NOT NULL DEFAULT 0,
     `apercentage` TINYINT NOT NULL DEFAULT 0,
     `bpercentage` TINYINT NOT NULL DEFAULT 0,
     `cpercentage` TINYINT NOT NULL DEFAULT 0,
     `zerousage` CHAR(1) NOT NULL DEFAULT 'D',
     `months` TINYINT NOT NULL DEFAULT 12,
     PRIMARY KEY (`groupid`),
     CONSTRAINT `abcgroups_ibfk_1` FOREIGN KEY (`methodid`) REFERENCES `abcmethods` (`methodid`) 
);

The zerousage field is intended to hold the category into which items that have no usage at all should be put. This would normally be C or D. The months field is the number of prior months movements that should be analysed.
Finally I propose a separate table to hold the ABC category for each item and group. This would look like:

CREATE TABLE `abcstock` (
     `groupid` INT(11) NOT NULL DEFAULT 0,
     `stockid` VARCHAR(20) NOT NULL DEFAULT '',
     `abccategory` CHAR(1) NOT NULL DEFAULT 'C',
     PRIMARY KEY (`groupid`, `stockid`), 
     CONSTRAINT `abcstock_ibfk_1` FOREIGN KEY (`groupid`) REFERENCES `abcgroups` (`groupid`),
     CONSTRAINT `abcstock_ibfk_2` FOREIGN KEY (`stockid`) REFERENCES `stockmaster` (`stockid`) 
);   

Using these three tables should provide for a very flexible system, easily changed in the future.

The function to actually assign the categories would work something like this:


1.  Calculate the 12 month value usage for all of the stock items.
2.  Rank the items in descending order by value.
3.  The "A" items are the top 80%.
4.  The "B" items make up the next 15%.
5The "C" items are the remaining items that have any usage in the period being looked at.
6.  Label zero-usage items as "D".


Comments and constructive (yes Phil I am looking at you!) criticisms would be very much appreciated.







Wednesday 8 May 2013

The anatomy of a KwaMoja plugin

As promised in my previous post regarding KwaMoja plugins, here is a tutorial on constructing a plugin.

Each plugin is a zip file. For this tutorial we will use the demo plugin that is available from http://www.kwamoja.com/demo_plugin.zip.

This demo takes the form of a standard "Hello World" type application, as used in most programming tutorials. The plugin will present the user with a text box asking for the users name. If that user has not used the plugin before from that IP address they will get a message saying "Hello John" or "Hello Jane" or whatever his or her name is. If they are returning, they will get a welcome back message.

If you unzip this file you will see it contains 5 files:

HelloWorld.php
HelloWorldDB.php
HelloWorldDBRemoval.php
HelloWorldLinks.txt
summary.xml

ALL KwaMoja plugins must have a summary.xml file. This is the file that tells KwaMoja how to install the plugin. Here is the xml contained in this examples summary.xml:

<plugin>
    <name>Demo Plugin</name>
    <installed>0</installed>
    <license>gpl</license>
    <scripts>
        <script>
            <name>HelloWorld.php</name>
            <pagesecurity>1</pagesecurity>
        </script>
    </scripts>
    <menulinks>HelloWorldLinks.txt</menulinks>
    <dbupdates>HelloWorldDB.php</dbupdates>
    <dbremoval>HelloWorldDBRemoval.php</dbremoval>
</plugin>


The first attribute is a descriptive name for the plugin. I have just called this one "Demo Plugin" .

The next attribute shows whether the plugin has been installed. KwaMoja will automatically set this to 1 when the plugin is installed. This should be set to zero when writing the plugin, and then never manually changed.

Then there follows the name of the license that this plugin is covered by. In this case, the GPL.

Next is an array of scripts that will be installed and a security token for each script. HelloWorld.php is a KwaMoja script. The plugin can contain any number of these scripts. In this simple example we only need the one, and I have given it a security token of one meaning any user can use it. I will go through this script and how to write a KwaMoja script in my next post.

The next attribute is called "menulinks" and contains the name of the file containing details of what new menu items are required to be added, and if any new modules are to be added to the menus. The full contents of this file for our example are:

$ModuleLink[] = 'Test';
$ReportList['Test'] = 'test';
$ModuleList[] = 'Test';

$MenuItems['Test']['Transactions']['Caption'][] = 'Hello World';
$MenuItems['Test']['Transactions']['URL'][] = '/HelloWorld.php';


The first three items on the list will setup a new module called "Test" to appear on the main menu. This is not obligatory. For instance if your plugin fits best in the Manufacturing module, then there is no need to have these lines. The next two lines are necessary for every script that you want the user to have access too via the main menu. These take the form of $MenuItems[Module][Section][Caption] and $MenuItems[Module][Section][URL] where "Module" is the name of the module, either one that you have just created or from the following list:

'orders', 

'AR', 
'AP', 
'PO', 
'stock', 
'manuf',  
'GL', 
'FA', 
'PC', 
'system', 
'Utilities'

The section is either "Transactions", "Reports", or "Maintenance" and determines whether the item appears in the left, centre, or right section of the menu. The Caption is the string that will appear in the menu, and the URL is the path to that menu, which must include a "/" at the beginning, as in "/HelloWorld.php".

Penultimately there is an attribute called dbupdates. This provides a link to a file containing the following:

<?php
CreateTable('helloworld',
"CREATE TABLE helloworld (
ipaddress char(13),
name varchar(30),
PRIMARY KEY  (`ipaddress`, `name`)
)", $db);

$SQL = "UPDATE www_users SET modulesallowed=modulesallowed+'1, WHERE userid='" . $_SESSION['UserID'] . "'";
executeSQL($SQL, $db);
?>


These are commands used to make any necessary changes to the database for your plugin to work. If it doesn't need any changes there should still be a file that just contains the <?php ?> tags in it. KwaMoja comes with an api to allow you to access the database, and facilitates the use of different DBMS's. You can find the functions here. The first call is to the CreateTable() function. Secondly there is some sql to update the users modulesallowed field and finally there is a call to the executeSQL() function to run this SQL.


The final attribute in the summary.xml file is called dbremoval. This is a reference to the file that contains the commands required to remove the database changes when the user uninstalls the plugin. Our demo file contains the following:

<?php
DropTable('helloworld', 'name', $db);

$SQL = "SELECT modulesallowed FROM www_users WHERE userid='" . $_SESSION['UserID'] . "'";
$Result = DB_query($SQL, $db);
$MyRow = DB_fetch_array($Result);

$SQL = "UPDATE www_users SET modulesallowed='" . substr($MyRow['modulesallowed'], 0, strlen($MyRow['modulesallowed'])-2) . "' WHERE userid='" . $_SESSION['UserID'] . "'";
executeSQL($SQL, $db);

?>
As you can see above, both the dbupdates and the dbremoval files can contain php code to verify information before updating the database.




So that is a simple KwaMoja plugin. This zip file can be imported into KwaMoja, and you can see and use this plugin directly from the menu.
The next post will cover actually writing the script.


Sunday 5 May 2013

KwaMoja now has a plugin architecture!

Yesterday Munir blogged about the new KwaMoja plugin architecture. As he says in his post this will enable both open source and commercial plugins to be written for KwaMoja. The code with this system in can be found here, and a demo plugin can be downloaded from here.

This is an exciting new development as it means that specialist areas, such as health and education, to name but two, can now have full KwaMoja support. Previously code for this sort of area would not have been part of the main line, as it would be too specific to be included.

It also provides an opportunity for third party developers to make money from the KwaMoja ecosystem.

KwaMoja plugins come as zip files, they must be uploaded to the KwaMoja installation from the browser interface, and then unpacked and installed via the same interface.

There are three scripts that have been added to KwaMoja: PluginUpload.php to load the plugin to the installation, PluginInstall.php to unpack and install the plugin and PluginUnInstall.php to remove the plugin.

Later today I hope to post a tutorial on how to create your own plugins.

This code could be adapted to work in webERP if Phil Daintree wanted to work with us on doing that. From his recent behaviour I suspect he wont.

Thursday 2 May 2013

KwaMoja/webERP security

This question came up recently on the webERP forum:

I'm new to webERP and naturally have some questions. I've created a role called "Inventory" in Access Permission Maintenance, then a user for this role and limited access to just "Display Inventory" module in User Maintenance. But I'd like to further limit access so my inventory user can NOT see pages in this range:

• Inventory Valuation Report
...
• List Negative Stocks
• Stock Transfer Note[/align]

The webERP Manual is vague on this. Is there a doc I can read to find out can I restrict a user, for example, to just Inventory.Maintenance.View or Update Prices Based On Costs?


Unfortunately the advice the poster was given was complex and not really correct, and the administrator of the forum has blocked me from helping people there. However, there is a much simpler answer which doesn't involve setting up phantom security tokens, and other complexities. This is to go to each of the reports they want removed in the "Page Security Settings" option in the setup module, and from there just give it a security token of a higher level than the inventory user. For instance set it to "General Ledger Reports/Inquiries" which makes more sense for something like an Inventory Valuation report. Then the report will be gone from the users screen the next time they log in. It's as simple as that!

However that led me to thinking that a lot of people (including it seems the current webERP project developer) who don't really understand the security system within KwaMoja/webERP so I thought it might be good to explain how it works.

Every user has a security role. These roles are meant to mirror their real life roles. So for instance we may have a role of an inventory clerk, and a role of an accountant. There can be any number of inventory clerks, and any number of accountants, all having the same role. As many roles as are wanted can be created.

Each role is given a number of security tokens. Each of these tokens permits the user with that role to perform different functions. There are a number of predefined tokens:


0 Main Index Page

1 Order Entry/Inquiries customer access only

2 Basic Reports and Inquiries with selection options

3 Credit notes and AR management

4 Purchasing data/PO Entry/Reorder Levels

5 Accounts Payable

6 Petty Cash

7 Bank Reconciliations

8 General ledger reports/inquiries

9 Supplier centre - Supplier access only

10 General Ledger Maintenance, stock valuation & Configuration

11 Inventory Management and Pricing

15
User Management and System Administration

When a user tries to access a function, the security token for that functionality is looked up in a database table called scripts and it is then compared with the array of security tokens that is owned by the role allotted to that user. If the token is in that array, the functionality can be accessed, if not, then access is denied.

This lookup is also performed when displaying the menus, and if the security token is not there, the menu option will not be displayed.

This system is simple elegant and flexible. It can be made as simple or as complex as an organisation requires. For instance a one person business only needs one security token, and one role, whereas a large business with hundreds of employees will have a very complex structure.

I hope this helps provide some insight into how the system works.