Skip to content

Essential Invoicing Software Features

An invoicing software package needs to have a few essential features before it can truly be said to be more useful than manual invoicing, e.g. using a book of bills or simply using Excel invoice templates. Here’s a list of the top 5 essential attributes you need to look for in even the most basic and easy invoice software:-

1. A selection of professional invoice templates

To be considered professional, any invoice package needs to include a set of clean, professional invoice templates. The templates need to be neatly laid out and easy to read. The templates also need to let you add your business logo and contact details including payment instructions.

2. The package needs to let you easily edit the templates

Just letting you add your logo to a template is not enough. Any decent invoicing package will need to let you make edits to its templates. You may need different templates for specific customers for example or you may need to rearrange items and show extra information on your invoices, e.g. if you want to publicise special deals or offers from your business.

3. You need to be able to track payments

It goes without saying that the main reason for invoicing is to get paid. Make sure any software you purchase lets you easily record payments as well as show reports of who owes money and has overdue invoices.

4. Automates as much as possible of repeat invoicing

The better invoicing software supports recurring or automatic invoicing. You should be able to set up an initial invoice or transaction then have the package automatically raise future invoices on a schedule you set, e.g. once a month for 12 months.

5. Handles other documents, especially quotes

Any decent invoice software should also handle other documents associated with sales from your business. The most obvious is that and package capable of producing invoices should also be a good quote software too as quoting and invoicing go hand in hand with most businesses. For example, many tradesmen such as electricians and builders often produce quotes for work. They may need to track which quotes have been accepted/ rejected then raise invoices when work is completed and due to be charged.

For more information on invoicing, please visit http://www.sliqtools.co.uk/

Balance Guide v1.6 iPhone App

What’s new in the latest version of Balance Guide ?

The multi-account feature has been extended further and now includes transfers between accounts. To provide maximum flexibly, the transfer feature allows you enter a different description and date, catering for differences in clearing.

Also a backup and restore feature via Dropbox

Balance Guide iPhone app

My security in-security obesession for the year

We’ll our kaspersky internet security renewal was coming up so we upgraded. Been good this year, with other products we normally go for something different but have no complaints.

So had a look to see what I could get for my mac mini and they don’t have a internet security package, but they have AV so I bought that too :) also turned on the snow leopard firewall.

Phew…

by JM

iPhone, why I moved from FMDB to direct Sqlite3

I’m still quite disappointed, FMDB was easy to implement and only a few lines of code to do almost anything. I’ve even posted some example code on my blog. But when I sat and thought about, I realised I’d wasted two days (in total) trying figure out why sql wasn’t working.

There must be some problems in the libraries as the sql was working but failed on simple sql statements.

So today I’ve decided to use Sqlite3 directly, which has taken me have a day and I have all the previous code replaced and working. Yes, it takes more code and is more difficult to implement.

by JM

iPhone, how to insert a record using fmdb

One of the easiest ways to perform transactions with a database with the iPhone is with the fmdb libraries. Its a few lines of code to do almost anything.

This is the primary documentation for fmdb http://gusmueller.com/blog/archives/2005/3/22.html

It’s pretty good and straight forward. But, I’ve had some problems today trying to insert a record. None of the usage code I found on the web seemed to be any different to the code I was using.

However, after inserting one field at a time I found my problem was due to using an integer where I needed to use NSNumber. So I thought I’d post the code I arrived at, maybe it will help someone else.

NSMutableString *strAmount = [NSMutableString stringWithString:txtAmount.text];
[strAmount replaceCharactersInRange: [strAmount rangeOfString: strCurrencySymbol]  withString:@""];

NSString *strRecurrance = [self.pickerRecurranceData objectAtIndex:intSelRecurrenceRow];
NSArray *parts = [strRecurrance componentsSeparatedByString: @" "];

NSNumber *numInterval = [NSNumber numberWithInt:[[parts objectAtIndex:0] intValue]];
NSString *strIntervalType = [parts objectAtIndex:1];

// string to date
NSString *myDateString = btnStartDate.titleLabel.text;
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setDateFormat:@"dd MM yyyy"];
NSDate *datStartDate = [[NSDate alloc] init];
datStartDate = [dateFormatter dateFromString:myDateString];

NSString *strDesc = txtDescription.text;


FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
if (![db open]) {
NSLog(@"Could not open db.");
}

if (![db tableExists:@"mytable"]) {
[db executeUpdate:@"CREATE TABLE 'mytable' ('pid' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,'desc' VARCHAR,'sdate' DATETIME,'interval' INTEGER,'itype' VARCHAR,'price' DOUBLE);", nil];
}

[db beginTransaction];


[db executeUpdate:@"INSERT INTO mytable (desc,sdate,interval,itype,price) VALUES (?,?,?,?,?);",
strDesc, datStartDate, numInterval, strIntervalType, strAmount, nil];

//NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);

[db commit];
[db close];

by JM

How to add a decimal point or a done button to the iphone number pad

Following on from our blog post about how to add the done button to the iphone number pad, here’s how upgrade that code to add a decimal point as well, well not at the same time :)

How to add your own Done button to the iPhone numeric keypad

Ok, first of all if you haven’t done so already, get the code working posted in the done blog post.

Now open up your NumberKeypadModController.h file and add the following to the top of the file below your foundation import.

#define KeyPadTypeDoneButton 1
#define KeyPadTypeDecimalPoint 2

Add this to the interface.

int intKeyPadType;

And this under properties

- (void)setKeyPadType:(int)value;
-(id)initWithKeyPadType: (int)value;

Now open your NumberKeypadModController.m file and add.

-(id)initWithKeyPadType: (int)value
{
[self setKeyPadType:value];
//self = [super init];
self = [self init];
if( self != nil )

{
//self.intKeyPadType = value;

}
return self;

}

- (void)setKeyPadType:(int)value {
intKeyPadType = value;
NSLog(@”key=%i”, intKeyPadType);
}

Now where you see the line containing @”Done”, replace that with ..

if (intKeyPadType == KeyPadTypeDecimalPoint ) {
doneButton.titleLabel.font = [UIFont systemFontOfSize:35];
[doneButton setTitle:@"." forState:UIControlStateNormal];
} else if (intKeyPadType == KeyPadTypeDoneButton ) {
doneButton.titleLabel.font = [UIFont boldSystemFontOfSize:18];
[doneButton setTitle:@"DONE" forState:UIControlStateNormal];
}

Also replace the following functions

- (void)textFieldShouldEndEditing:(UITextField *)textField {
if (textField.keyboardType != UIKeyboardTypeNumberPad)
{
doneButtonShownRecently = YES;
if (intKeyPadType == KeyPadTypeDoneButton ) {
[self performSelector:@selector(considerDoneButtonReallyHidden) withObject:nil afterDelay:SLIDE_OUT_ANIMATION_DURATION];
}
return;
}
[self removeDoneFromKeyboard];
}

- (void) donePressed {

if (intKeyPadType == KeyPadTypeDecimalPoint ) {
NSString *currentText = currentTextField.text;
if ([currentText rangeOfString:@"." options:NSBackwardsSearch].length == 0) {
currentTextField.text = [currentTextField.text stringByAppendingString:@"."];
}else {
//alreay has a decimal point
}
} else if (intKeyPadType == KeyPadTypeDoneButton ) {
[self.currentTextField resignFirstResponder];
}
if ([delegate respondsToSelector:@selector(donePressed:)])
[delegate performSelector:@selector(donePressed:) withObject:self.currentTextField];
}

At this point, I’d like to cite and give credit to DevUp for the decimal point formatting code in the donePressed function above.
http://blog.devedup.com/index.php/2010/03/13/iphone-number-pad-with-a-decimal-point/

Now in you useage code replace the following code..

int intCurrentKeyPad;

- (void)viewDidLoad {
[super viewDidLoad];
//intCurrentKeyPad = KeyPadTypeDoneButton;
intCurrentKeyPad = KeyPadTypeDecimalPoint;

self.numberKeyPadModController = [[[NumberKeypadModController alloc] initWithKeyPadType:intCurrentKeyPad] autorelease];
//self.numberKeyPadModController = [[[NumberKeypadModController alloc] init] autorelease];
numberKeyPadModController.delegate = self;

[numberKeyPadModController setKeyPadType:intCurrentKeyPad];

if (intCurrentKeyPad == KeyPadTypeDoneButton ) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(doneButton:) name:@”DoneButtonPressed” object:nil];
}

textFieldRow1.delegate = self;
}

- (void)doneButtonPressed:(UITextField*)sender{
if (intCurrentKeyPad == KeyPadTypeDoneButton ) {
if ([textFieldRow1 isEditing]) {
[textFieldRow1 resignFirstResponder];
}
}
}

- (void) donePressed:(id)sender {
if (intCurrentKeyPad == KeyPadTypeDoneButton ) {
UIView* firstResponder = [self.view findFirstResponder];
[firstResponder resignFirstResponder];
}
}

Ok I hope this helps you out!

Feel free to make a comment about this article, let me know if you have any problems with it.

by Jules.

How to add your own Done button to the iPhone numeric keypad

Last revised 22nd September 2010 – as I missed some code from this article.

There are several issues which you must be aware of when working around the missing done button on the iPhone numeric keypad. Use of private API’s is frowned upon by apple and they can reject your application. Also a lot of the solutions out there don’t work very well for OS4. This solution works by drawing a button on top of the keypad and animating it off the screen when required. It also draws the text so you don’t have to rely on images.

Firstly add a header file call it something like NumberKeypadModController.h also create an M file. Add the follow code to your H file.

#import <Foundation/Foundation.h>

@protocol NumberKeypadModControllerDelegate <NSObject>

- (void) donePressed:(id)sender;

@end

/**
*    The class used to create the keypad
*/
@interface NumberKeypadModController : NSObject
{
NSTimer *fixKeyboardTimer;
UIButton *doneButton;
UITextField *currentTextField;
id<NumberKeypadModControllerDelegate> delegate;
int        showHideCounter;
BOOL    doneButtonShown;
BOOL    doneButtonShownRecently;
}

@property (nonatomic, retain) NSTimer *fixKeyboardTimer;

@property (nonatomic, retain) UIButton *doneButton;
@property (nonatomic, retain) UITextField *currentTextField;

@property (nonatomic, retain) id<NumberKeypadModControllerDelegate> delegate;

- (void)textFieldShouldBeginEditing:(UITextField *)textField;
- (void)textFieldDidBeginEditing:(UITextField *)textField;
- (void)textFieldShouldEndEditing:(UITextField *)textField;

- (void)resignedResponderWithView:(UIView*)textField;

@end

And your M file

#import “NumberKeypadModController.h”

@interface NumberKeypadModController (PrivateMethods)
- (void) addDoneToKeyboard;
- (void) removeDoneFromKeyboard;
- (void) donePressed;
@end

#define PRE_SLIDING_DELAY_DURATION        0.1
#define SLIDE_IN_ANIMATION_DURATION    0.2
#define SLIDE_OUT_ANIMATION_DURATION    0.15

#define USE_TEXTURED_BUTTON        0

@implementation NumberKeypadModController

@synthesize fixKeyboardTimer;

@synthesize doneButton;
@synthesize currentTextField;

@synthesize delegate;

- (id)init {
if (self = [super init]) {
self.fixKeyboardTimer = nil;
#if (USE_TEXTURED_BUTTON != 0)
self.doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
doneButton.frame = CGRectMake(0, 480, 105, 53);
if ([[[UIDevice currentDevice] systemVersion] hasPrefix:@”3″]) {
doneButton.frame = CGRectMake(0, 480, 105, 53);
[doneButton setImage:[UIImage imageNamed:@"DoneUp3.png"] forState:UIControlStateNormal];
[doneButton setImage:[UIImage imageNamed:@"DoneDown3.png"] forState:UIControlStateHighlighted];
} else {
[doneButton setImage:[UIImage imageNamed:@"DoneUp.png"] forState:UIControlStateNormal];
[doneButton setImage:[UIImage imageNamed:@"DoneDown.png"] forState:UIControlStateHighlighted];
}
#else
self.doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
doneButton.frame = CGRectMake(0, 480, 105, 53);
if ([[[UIDevice currentDevice] systemVersion] hasPrefix:@”3″])
doneButton.frame = CGRectMake(0, 480, 105, 53);
doneButton.titleLabel.font = [UIFont boldSystemFontOfSize:18];
[doneButton setTitleColor:[UIColor colorWithRed:60.0f/255.0f green:64.0f/255.0f blue:73.0f/255.0f alpha:1.0] forState:UIControlStateNormal];
[doneButton setBackgroundImage:[UIImage imageNamed:@"doneKeyDownBackground.png"] forState:UIControlStateHighlighted];
[doneButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[doneButton setTitle:@"DONE" forState:UIControlStateNormal];
#endif

[doneButton addTarget:self action:@selector(donePressed) forControlEvents:UIControlEventTouchUpInside];

self.currentTextField = nil;
self.delegate = nil;

showHideCounter = 0;
doneButtonShown = NO;
}
return self;
}

- (void)textFieldShouldBeginEditing:(UITextField *)textField {
if (textField.keyboardType != UIKeyboardTypeNumberPad)
{
doneButton.hidden = YES;
doneButtonShownRecently = YES;
}
}

- (void)textFieldDidBeginEditing:(UITextField *)textField {
if (textField.keyboardType != UIKeyboardTypeNumberPad)
return;

//    NSLog(@”textFieldDidBeginEditing”);
self.currentTextField = textField; //Save reference to current textfield being edited

if (!doneButtonShownRecently)
{
if (fixKeyboardTimer)
[fixKeyboardTimer invalidate];
self.fixKeyboardTimer = [NSTimer timerWithTimeInterval:PRE_SLIDING_DELAY_DURATION target:self
selector:@selector(addDoneToKeyboard) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:fixKeyboardTimer forMode:NSDefaultRunLoopMode];
} else
{
[doneButton removeFromSuperview];
[self addDoneToKeyboard];
}
}

- (void)textFieldShouldEndEditing:(UITextField *)textField {
if (textField.keyboardType != UIKeyboardTypeNumberPad)
{
doneButtonShownRecently = YES;
[self performSelector:@selector(considerDoneButtonReallyHidden) withObject:nil afterDelay:SLIDE_OUT_ANIMATION_DURATION];
return;
}
[self removeDoneFromKeyboard];
}

- (void) textFieldDidEndEditing:(UITextField *)textField {
}

- (void)resignedResponderWithView:(UIView*)textField {
//    NSLog(@”resignedResponderWithView”);
[self removeDoneFromKeyboard];
}

- (void) addDoneToKeyboard {
//    NSLog(@”addDoneToKeyboard”);

doneButton.hidden =  NO;

//Add a button to the top, above all windows
NSArray *allWindows = [[UIApplication sharedApplication] windows];
NSUInteger topWindowIndex = [allWindows count] – 1;
UIWindow *topWindow = [allWindows objectAtIndex:topWindowIndex];

// check if top window is of keypad or else
NSString *topViewClassName = [NSString stringWithFormat:@”%@”, [topWindow class]];
while (![topViewClassName isEqualToString:@"UITextEffectsWindow"] ) {
–topWindowIndex;

if(topWindowIndex < 0)
break;

topWindow = [allWindows objectAtIndex:topWindowIndex];
topViewClassName = [NSString stringWithFormat:@”%@”, [topWindow class]];
}

if(topWindowIndex < 0) {
topWindowIndex = [allWindows count] – 1;
topWindow = [allWindows objectAtIndex:topWindowIndex];
}

if (doneButton.superview)
[doneButton removeFromSuperview];

[topWindow addSubview:doneButton];

if (!doneButtonShownRecently) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:SLIDE_IN_ANIMATION_DURATION];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
doneButton.frame = CGRectMake(0, 480-53, doneButton.frame.size.width, 53);
[UIView commitAnimations];
} else {
doneButton.frame = CGRectMake(0, 427, doneButton.frame.size.width, 53);
}

doneButtonShown = YES;
}

- (void) removeDoneFromKeyboard {
//    NSLog(@”removeDoneFromKeyboard”);

[fixKeyboardTimer invalidate];
self.fixKeyboardTimer = nil;

if (!doneButtonShownRecently) {
[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:SLIDE_OUT_ANIMATION_DURATION];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
doneButton.frame = CGRectMake(0, 480, doneButton.frame.size.width, 53);
[UIView commitAnimations];
} else {
doneButton.frame = CGRectMake(0, 480, doneButton.frame.size.width, 53);
}

doneButtonShown = NO;
doneButtonShownRecently = YES;
[self performSelector:@selector(considerDoneButtonReallyHidden) withObject:nil afterDelay:SLIDE_OUT_ANIMATION_DURATION];
}

- (void)considerDoneButtonReallyHidden {
//    NSLog(@”considerDoneButtonReallyHidden”);
doneButtonShownRecently = NO;
}

- (void) donePressed {
[self.currentTextField resignFirstResponder];
if ([delegate respondsToSelector:@selector(donePressed:)])
[delegate performSelector:@selector(donePressed:) withObject:self.currentTextField];
}

- (void) dealloc {
[fixKeyboardTimer release];
[doneButton removeFromSuperview];
[doneButton release];
[delegate release];
[super dealloc];
}

@end

Now to your view header file add an import to NumberKeypadModController.h

Now its imple matter of adding a delegate to your interface and a property on the view your going to use the code. In your viewdidload add this code.

@interface YourViewController : UIViewController <UITextFieldDelegate, NumberKeypadModControllerDelegate> {

NumberKeypadModController *numberKeyPadModController;
IBOutlet UITextField *textFieldRow1;
}

@property(nonatomic,retain) NumberKeypadModController *numberKeyPadModController;
@property(nonatomic,retain) UITextField *textFieldRow1;

@end

And your m file.

#import “YourViewController.h”
#import “NumberKeypadModController.h”

@interface UIView (FindAndResignFirstResponder)
- (UIView*)findFirstResponder;
@end
@implementation UIView (FindAndResignFirstResponder)
- (UIView*)findFirstResponder {
if (self.isFirstResponder) {
[self resignFirstResponder];
return self;
}
for (UIView *subView in self.subviews) {
if ([subView findFirstResponder])
return subView;
}
return nil;
}
@end

@implementation YourViewController

@synthesize numberKeyPadModController;
@synthesize textFieldRow1;

- (void)viewDidLoad {
[super viewDidLoad];

self.numberKeyPadModController = [[[NumberKeypadModController alloc] init] autorelease];
numberKeyPadModController.delegate = self;

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(doneButton:) name:@”DoneButtonPressed” object:nil];

textFieldRow1.delegate = self;
}

- (void)doneButtonPressed:(UITextField*)sender{
if ([textFieldRow1 isEditing]) {
[textFieldRow1 resignFirstResponder];
}
}

- (void) donePressed:(id)sender {
UIView* firstResponder = [self.view findFirstResponder];
[firstResponder resignFirstResponder];
}

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
[numberKeyPadModController textFieldShouldBeginEditing:textField];

return YES;
}

- (void) textFieldDidBeginEditing:(UITextField *)textField {
[numberKeyPadModController textFieldDidBeginEditing:textField];
}

- (BOOL) textFieldShouldEndEditing:(UITextField *)textField {
[numberKeyPadModController textFieldShouldEndEditing:textField];
return YES;
}

-(void)touchesBegan :(NSSet *)touches withEvent:(UIEvent *)event {
UIView* firstResponder = [self.view findFirstResponder];
[firstResponder resignFirstResponder];
[numberKeyPadModController resignedResponderWithView:firstResponder];

[super touchesBegan:touches withEvent:event];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}

- (void)dealloc {
[super dealloc];
}

@end

Now in interface builder add a UITextField and assign the outlet, save and run.

I`m currently looking at making a decimal point version of this. I have seen others of the web, but the animation isn`t as good as mine. ;)

If you find this code helpful please consider buying a copy of our app. Suggestions and feedback our also welcome.

iTunes page Speaking Times Table iPhone App

Speaking Times Tables - Available on the App Store

I’d be happy to talk further about the code, please leave your comments below, you’ll notice from our app that we also use a bar above the keypad, which I haven’t included in the above code.

ALSO LET ME KNOW IF YOU HAVE ANY PROBLEMS WITH THIS CODE !!

by JM

How to, link my bug reports submissions into my ticket system?

Several years ago while my software wasn’t selling very well, I started to become very paranoid that my poor sales was due to bugs in my program. Bug that were hardly every reproduceable.  Over a year ago, I got some mturks to pinpoint some bugs. Which I believe, led to some of the major bugs being fixed.

However, I still get lots of bug reports where I can’t proceed any further. Theres two issues here. Firstly, that my bug reporting is super sensitive and produces reports when there isn’t actually a bug, although I’m not 100% why I get some of these reports.

Secondly, I’m wondering if I linked the reporting into my new ticket system and showed that I’m taking the problem seriously and asked for more information via the ticket system, whether people might provide more information.

by JM

Tip for PAD file Submission

Quick Tip: To make the most of a PAD file submission, you need to make sure your up to date listing gets shown on the front page of the software download site where it gets most exposure. As a side effect this probably means most link juice flows through your listing and back to your site via any links you get from the download site too :)

When you submit, make sure your software release date in the PAD file is the date of the day you actually do the submission. A lot of sites show recent submissions based on the release date in the PAD file rather than the actual date of submission, so submitting on the day of release maximises the chances of front page exposure.

Tagged

Goodbye to MDBSecure

After much discussion  over the last year or two, I finally took the difficult decision to pull the plug on MDBSecure. I’ve just been removing the last of the web pages and email addresses etc.

I first started development on the product back in 2005. The code to create an MS Access database which had security turned on is / was part of another product of mine. It made sense to put the functionality into a separate product.

Since then, I’ve improved the program and added extra functionality and a new website. However, as Microsoft drastically changed MS Access with version 2007 and added security, it was impossible to upgrade MDBSecure to cater for this new version.

Since, MDBSecure has become less popular as people are using the newer version of MS Access.

So it wasn’t worth the effort any more.

By JM