Thursday, April 30, 2009

Updated quote interface

I've spent some time on the quote interface trying to weigh off a very simple interface (such that first time users aren't turned off) against the flexibility of a system which caters for different types of business.

Oracle, for example, has an interface which is far too complex with too many options. Most online invoicing systems aren't going to be viable most of the time because they are just too simple.

The way I have tried to get around this is with context-sensitive tabs. You see the basics by default, but you can click a tab to see more. When you are editing a quote header, there is one set of tabs, and when you are editing the lines, the tabs change.

All best explained in the video below.

Thursday, April 23, 2009

Making PDFs using PHP

Having spent several days using a HTML->PDF conversion program (DOMPDF), I've become disappointed with the original conversion software. Part of the reason was the the code was abstracted beyond belief. Every line of code seemed to make a reference to a class whose name was calculated then dynamically loaded; every function referred to another function in the same class which contained only one line setting a protected object in the class.
 
The code was also unneccessarily padded out with comments like the one below making a thousand lines into five thousand:
 

  /**
   * a record of the current font
   */
  public  $currentFont = '';
 
and here's the bit which really upset me:
 
function DOMPDF_autoload($class) {
  $filename = mb_strtolower($class) . ".cls.php";
  require_once(DOMPDF_INC_DIR . "/$filename");
}
 
if ( !function_exists("__autoload") ) {
  /**
   * Default __autoload() function
   *
   * @param string $class
   */
  function __autoload($class) {
    DOMPDF_autoload($class);
  }
}
 
What the above code does is to hunt through an (abstracted) directory for a file of a particular description whenever a class or function is called which isn't in the already loaded code. Not only that, but the whole thing has been abstracted to a further external function. Try debugging that !
 
So in summary, I couldn't really tell what was going on. More importantly, the code has some serious security vulnerabilities, didn't convert images accurately, and was unnecessarily slow. In fact, the DOMPDF code represents everything I hate about obsessively written abstracted software.
 
Predictably, the set of classes didn't actually create PDFS. It was itself an abstraction of a beautiful set of code written right here in New Zealand, which also has a concisely written manual. http://www.ros.co.nz/pdf/readme.pdf. Like all good code, it isn't loosely coupled, everything works the way you expect first time, and you can quickly find out what is going on.
 
To run it, you load a single file containing a single class, instantiate the class and call the methods laid out in the manual. Like this:
 
require_once("class.ezpdf.php");
$pdf =& new Cezpdf();
 
Add an image like this:
 
$pdf->addJpegFromFile('COLDHOT2.JPG',150,650,100);
Add a table like this:
 
$dblink = getconn();
$results = "";
$stmt = "select itemid,name from gst";
$result = mysqli_query($dblink,$stmt);
if ($result) {
 while($row = mysqli_fetch_array($result,MYSQLI_ASSOC)){
  $results[]=$row;
 }
}
$data = $results;
mysqli_close($dblink);
$pdf->ezTable($data,'',$title,$opts);
Add a block of text on the page like this:
 
$opts = array('shaded'=>0);$opts['showHeadings'] = 0;
$opts['yPos'] = 400; $opts['xPos'] = 200;
$opts['fontSize'] = 11; $opts['showLines'] = 0;
$data = array(array('aaa'=>"Is this a\nmulti line problem"));
$pdf->ezTable($data,'',$title,$opts);
File it to disk like this:
 
$pdfcode = $pdf->ezOutput();
$fp=fopen('findthis.pdf','wb');
fwrite($fp,$pdfcode);
fclose($fp);

Or push it to the browser like this:
 
$pdf->ezStream();
The big problem is that I now have two output types. Firstly, an html representation of a document, and secondly a PDF representation. It is certainly possible that the two will diverge over time so that what is printed on the user's printer is different from what is PDF'ed up and sent to a trading partner. In my opinion, this is a small price to pay for a much faster, more reliable, safer and better looking result. DOMPDF is now officially scrapped!

Wednesday, April 22, 2009

Printing

The previous post brings me on to the general area of printing.

To print a document which has just been created

For example a delivery note. Typically, this can be done via a browser as suggested in the previous post. It isn't possible to override the local user's browser setting, however they can change their own settings so that page headers are not printing, shading and images are printed, and even that they are not prompted for permission to print.

To print a batch, or print on someone else's printer

For example a batch of customer statements, or a batch of customer invoices, 100 sites worth of P&Ls etc. The print run might take a long time to prepare, which would result in a browser timeout. In this context, we are not going to be printing via the user's browser.

  • Have network access from a central print server to the client's printer. The job can then be run invisibly in the background, and the print can spool when the print is ready. A fairly simple print spooler can be run on a standalone linux/windows machine.
  • Emailing the user with the results of the print. The user can then open the document, print at their convenience, or depending on the format, cut/paste and analyse the results.
  • Emailing the user with a link to the results of the print. As above, other than that some central storage would be required, along with some sort of job schedule listing.
  • Introducing a job scheduler or concurrent process manager, and running the job in the background.
Pretty much all of the above need the ability for jobs to run in the background. This potentially opens up the complex area of job scheduling, including precedents, parallelisation, the ability to classify jobs by user, view and cancel jobs, and user time scheduling limitations, recurring jobs etc.
 
For the practical purpose of this application, I'm going to avoid batch processing entirely. Everything is going to go via the user's browser or otherwise complete while the user has their browser open, and applications will be written with this in mind. When timeouts eventually occur, clients will be given the option of running the application on a dedicated server, or will be of the size where they can afford some programming expertise to meet their requirements.

Using CSS to differentiate between printable and unprintable elements

As long as the browser is CSS2.1 compliant (IE7 and Chrome are fine), it is possible to have a web page looking quite differently when printed from the way it appears on screen.

Doing this is fairly simple - each CSS element can be enclosed in a media envelope to determine how the html is interpreted when printed, or on screen.

So for example we have:

 

@media screen {

.bt {
    background: transparent url('button.gif') scroll top right;
    font: normal 12px arial, sans-serif;
    height: 24px;
    margin-right: 6px;
    text-decoration: none;
    top-border:none;
    bottom-border:none;
}
}

@media print {
   .bt {display: none;}
}

<input name=sub class=bt type=submit value='Click to Continue'>

In the above, buttons aren't printed, but are shown on screen. The principle is ideal for web pages with drop down menus which are usually meaningless when printed. To convert any existing CSS, simply enclose it in @media screen brackets, then create a new @media print section for each css indicator.

For print-only html pages, the reverse can be done. i.e. the page is not visible to the user unless they print it.

 

 

 

Tuesday, April 21, 2009

PDF Invoice / Quote

It took some time to find CSS which rendered properly in cross-browser and pdf scenearios, and I've arrived at the source below.

 

<html>
<head>
<style>
td.c1 {font-family:Helvetica;font-size:50px;font-weight:bolder;color:#c0c0c0;}
td.c2 {font-family:Helvetica;font-size:13px;}
td.c3 {font-family:Helvetica;font-size:12px;}
td.c4 {font-family:Helvetica;font-size:12px;border-bottom:solid 1px gray; border-left:solid 1px gray; }
td.c5 {font-family:Helvetica;font-size:12px;border-bottom:solid 1px gray; border-right:solid 1px gray; border-left:solid 1px gray}
th.h1 {font-family:Helvetica;font-size:12px;border-bottom:solid 1px gray; border-left:solid 1px gray; border-top:solid 1px gray}
th.h2 {font-family:Helvetica;font-size:12px;border-bottom:solid 1px gray; border-left:solid 1px gray; border-right:solid 1px gray; border-top:solid 1px gray}
table {margin-left:50px;margin-top:10px;margin-right:10px;}
</style>
</head>
<body>
<table width=90%>
<tr><td width=15%>
<img src="coldhot2.jpg" width=100px>
</td>
<td class=c2 valign=top><b>PickMaster</b><br>
 37 Isobel Road<br>
 Greenhithe<br>
 Auckland<br>
 0632
</td>
<td valign=top align=right class=c1>
Invoice
</td>
</tr>
</table>

<table width=90%>
 <tr>
  <td width=50%></td>
  <td valign=top align=right class=c3>Invoice Number</td>
  <td valign=top  class=c3>10302</td>
 </tr>
 <tr>
  <td width=50%></td>
  <td valign=top align=right class=c3>Tax Date</td>
  <td valign=top class=c3>12 Dec 2009</td>
 </tr>
 <tr>
  <td width=50%></td>
  <td valign=top align=right class=c3>Order Number</td>
  <td valign=top class=c3>Customer Order Number Goes Here</td>
 </tr>
</table>
<br>
<br>
<table width=60%>
<tr>
<td class=c3 width=30%>Customer Addy<br>
Address Line 1<br>
Address Line 2<br>
Address Line 3<br>
Post Code
</td>
<td class=c3 width=30%>Customer Addy<br>
Address Line 1<br>
Address Line 2<br>
Address Line 3<br>
Post Code
</td>
</tr>
</table>
<br>
<br>

<table width=90% style=" border: 0pt none black;" cellspacing=0; >
<tr>
 <th valign=top class=h1 align=left width=15%>Code</th>
 <th valign=top class=h1 align=left width=60%>Description</th>
 <th valign=top class=h1 align=right width=15%>Qty</th>
 <th valign=top class=h2 align=right width=10%>Price</th>
</tr>
<tr>
 <td valign=top class=c4>ActualCode</td>
 <td valign=top class=c4>Full Textual Description Description</td>
 <td valign=top class=c4 align=right>14</td>
 <td valign=top class=c5 align=right>23.00</td>
</tr>
<tr>
 <td valign=top class=c4>ActualCode</td>
 <td valign=top class=c4>Full Textual Description Description</td>
 <td valign=top class=c4 align=right>14</td>
 <td valign=top class=c5 align=right>23.00</td>
</tr>
<tr>
 <td></td>
 <td></td>
 <td align=right valign=top class=c4>Total</td>
 <td valign=top class=c5 align=right>23.00</td>
</tr>
<tr>
 <td></td>
 <td></td>
 <td align=right valign=top class=c4>GST</td>
 <td valign=top class=c5 align=right>2.30</td>
</tr>
<tr>
 <td></td>
 <td></td>
 <td align=right valign=top class=c4>Total</td>
 <td valign=top class=c5 align=right>25.00</td>
</tr>
</table>
</body> </html>

Tuesday, April 14, 2009

Search Algorithms

I also went through all of the searching algorithms.

  • Limits results within master class
  • Lightning fast on almost any size table
  • Allows for selection of 'none of the above'
  • Fixed bug on where multi-page select lists used in conjunction with a change of search criteria
  • Cached results for further speed enhancements
 

Rendering in pdf

First off, I should point out that I *hate* PDFs.
 
When the format was first launched, I remember a review of it, and the article posed the question "What is the point?" It was inconclusive, and I have still not worked out what the point of a PDF is. Does it stop the receiver from editing the document? - No. Is it easier to transmit ? - No. Is it easier to write software for ? - Definitely No. Is it in some way more secure? - No. Is it easier to read ? - No, more difficult. Is it more portable than HTML? - No.
 
Notwithstanding, people expect to be able to transmit documents as PDFs. Adobe have done a wonderful marketing job, and it isn't my place to unilaterally open everyones' eyes to the pointlessness of their proprietary document format.
 
The solution? The applications produce html (probably from within the database), then have the facility to convert it to pdf at application level. The user can then transmit the PDF as an email attachment or file it somewhere as they see fit. Production of XML, direct import into google apps and other machine-readable formats will follow later.
 
To perform the translation, I am using dompdf v 0.5.1. This is a free, downloadable set of classes specifically for converting html to pdf, it is all done natively in php and requires no further downloads. It has limited font support (suggest stick with Helvetica), but understands CSS, tables and graphics, and even some support for inline scripting.
 
Here's an example bit of conversion code:
 
<?php
require_once("dompdf_config.inc.php");
$dompdf = new DOMPDF();
$dompdf->load_html_file("test.htm");
$dompdf->render();
$dompdf->stream("sample".time().".pdf");
?>
 
And it seems pretty happy to convert something like this:
 
<html>
<head>
<style>
td {font-family:Helvetica;font-size:10px;background-color:#ffffff;border-style:solid;border:thin solid #c0c0c0}
th {font-family:Helvetica;font-size:30px;background-color:#e0e0e0;border:thin solid #c0c0c0}
table {border:none;border-spacing:0;border-collapse:collapse;}
h1 {font-family:Helvetica;font-size:20px}
</style>
</head>
<h1>A Heading</h1>
<table width="100%">
  <tr>
    <tH>a</tH>
    <tH>b</tH>
    <tH>c</tH>
  </tr>
  <tr>
    <td>d</td>
    <td>e</td>
    <td>f</td>
  </tr>
</table>
<img src=".\coldhot.jpg">
</html>
 
Here's some more documentation.
 

Monday, April 13, 2009

Sales Order Interface

The sales order input program is probably the most important one for a number of reasons:

  • Strategically, it is close to the customer. In a telereactive sales environment, it gives the customer an impression of the business.
  • It should be the thing you do most often in your business - invoicing, quoting and raising orders.


The quote program is mostly written in principle. Further enhancements:

  • Ability to enter 'Manual' lines, i.e. not affecting stock, and not in the product file.
  • Ability to enter 'Special' lines, i.e. a physical product, so purchase order needed.
  • A 'type' grid for quotes, invoices, credits, collections, orders, directs, credits (no stock), cash type sales. Conditional conversion between types.
  • Calculation of customer selling prices, incorporating discount grid.
  • Packs and kits.
  • Credit control / order hold.
  • Printing / transmission.


Consequently, and with the above goals, it is the quotes (and order processing) routines I am concentrating on this week.




Thursday, April 9, 2009

Disabling form resubmission

When the user of a web site clicks on the submit button, the page is wrapped up and sent to the web server for processing. Depending on the speed of the connection and the web server, the user may click submit twice.

When this happens, the web server will receive exactly the same page twice, and will need to decide what to do. Depending on the complexity of the server-side application, you may need to cater for many different situations in a complex application.

It is much simpler to reduce the number of duplicated submissions (many users still think double-clicking is the right thing to do!), and to generate a general error. So how do you prevent form resubmission?

Answer : like this:

<%@ page contentType="text/html;charset=windows-1252"%>
Disabling form resubmission
<HTML>
<HEAD>
<SCRIPT language="JavaScript">
function submitform()
{
  document.myform.getme.disabled = true ;
}
</SCRIPT>
</head>
<form name="myform" onsubmit = "return false">
Search: <input type='text' name='query'>
<input name=getme type=submit value=test onclick="submitform()">
</form&gt;
</html>

Google Apps



Every small business has the same problem.

If you are a builder, a taxi driver, an electrician, an IT contractor, a masseur, cleaner or whatever, people want quotes and they want invoices. You need to keep records for tax and GST, and you need to keep details of who owes you money.

So what do you do?

Go out and buy MYOB or Sage / some other small accounting package, or you try to keep paper records using MS Word/Excel.

Both options are full of problems:

  •  If you set up a small accounting package, you need to spend some cash.
  •  You spend ages setting the software up and it's complex. You then need to keep installing updates.
  • When your hard disk fails, you lose your records. Either that, or you need to be good at backup and restore.
  • You need to be at your computer to create the quotes, or you write them out by hand while on site, and then need to rekey them as invoices later on, hoping you don't make any mistakes.
  • If you use Word, you keep saving your invoices and deleting the old copies. You also can't keep track of who owes you money.
  • Importantly, the setup won't grow with your business. As soon as you have more employees, need to open another site, employ an accountant or expand, you need to change software. Then before you know it you need to employ someone to look after the application.

So what's the solution ?

Googapps online invoicing. You don't need to worry about backup, keeping up to date with the latest GST legislation, updating software or expansion. Furthermore, because the software is online, you can create quotes and invoices from any computer with a browser and internet connectivity - even your phone.

On site with your customer and want to see how much they owe? Use your phone to access their account, then email them the details. Better still, if they are a googapps user too, they can import the invoices directly into their own googapps account.

Computer hard drive crashes? Get a new computer, borrow someone else's or use your phone. It won't affect your business.