Invoice Ninja - Laravel-powered solution for better invoicing

We continue the series of reviews for Laravel-based projects and apps. Today we have Invoice Ninja - a really powerful tool for, you guessed it, invoicing. It really impressed me, let me tell you how.
Other reviews in this series:
  1. FlarePoint: Laravel-based free CRM
  2. Invoice Ninja – Laravel-powered solution for better invoicing
  3. Attendize – event tickets selling system based on Laravel 5
  4. Ribbbon – project management system on Laravel 5.1 and Vue.js
  5. Faveo: impressive helpdesk system built on Laravel
  6. Confomo: Laravel-based website to meet Twitter friends at conferences

First things first - Invoice Ninja has a really interesting business model. They have two different versions - hosted and self-hosted. In other words, you can register at invoiceninja.com and use their online version, paying for premium services, or you can download the code for free (Laravel 5.2) and install it on your server for free. It reminds me of another tool that has hosted and self-hosted open-source version - it's analytics app called Piwik. Anyway, we will be talking about self-hosted version here - we're more interested in Laravel code here. So let's begin.

Installation

Interestingly, though the code is available on Github, in their official documentation in Readme they recommend not to clone repository code, but to download ZIP file. And it's actually quite heavy - 91 MB as ZIP and 247 MB unpacked. invoice ninja archive Another interesting thing is that ZIP archive comes with already included vendor folder in it, so after un-archiving you don't need to run composer install. Instead, when you launch your homepage, you get a CMS-like installation page and you fill in your credentials. invoice ninja install Installation process went well, although I was waiting for it to finish quite long - around 1 minute my Chrome tab looked like this: screen-shot-2016-11-06-at-10-04-11-pm But after the process finished, I've got a clean and nice login screen. invoice ninja login screen All in all, I like the installation and information is prepared well-enough to install self-hosted version pretty smoothly.

Usage

Ok, let's go on and log in. After logging in, I got into a dashboard. Empty, of course, but quite impressive. invoice ninja dashboard Ok, so what should I do now - probably, create a new client first? Here's how it looks: invoice ninja create new client From this screen, and also from the amount of menu items on the left, I got an impression how well-thought the process actually is. Creators thought of the case that client might have more than one contact person. Looking further, I've noticed that such details are all over the Invoice Ninja. Well done, guys. After creating a client, what do we do next? Of course, create an invoice for that client. invoice ninja new invoice The form is pretty simple, but at the same time might be quite complicated - depending on what you actually need: to issue a simple invoice or to customize it quite a bit. What I loved that while I am filling in the details, on the bottom there is a "preview" mode which is refreshed on every change of any field. Impressive. invoice ninja preview You can also choose a design theme for your invoices. In online hosted version, this is one of the "premium" features - choose more complex invoice design and add your own logo there. invoice ninja choose theme To be honest, I didn't dig deeper into features, but first impression is really really solid. You can feel that Invoice Ninja has been on the market already for a couple years, and it's a pretty polished and serious product - quite a competitor to other more expensive enterprise invoicing tools out there.

Laravel code

Ok, and finally - what's under the hood? How good/complex is Laravel code, is it easy to customize? I usually start judging the quality from routes file - in this case it's Laravel 5.2 so it's app/Http/routes.php. And it's actually HUGE:
// Application setup
Route::get('/setup', 'AppController@showSetup');
Route::post('/setup', 'AppController@doSetup');
Route::get('/install', 'AppController@install');
Route::get('/update', 'AppController@update');

// Public pages
Route::get('/', 'HomeController@showIndex');
Route::get('/terms', 'HomeController@showTerms');
Route::get('/log_error', 'HomeController@logError');
Route::get('/invoice_now', 'HomeController@invoiceNow');
Route::get('/keep_alive', 'HomeController@keepAlive');
Route::post('/get_started', 'AccountController@getStarted');

// Client visible pages
Route::group(['middleware' => 'auth:client'], function() {
    Route::get('view/{invitation_key}', 'ClientPortalController@view');
    Route::get('download/{invitation_key}', 'ClientPortalController@download');
    Route::get('view', 'HomeController@viewLogo');
    Route::get('approve/{invitation_key}', 'QuoteController@approve');
// ...
Total 350 lines of code in routes. And I believe it's not really too much - the application is that big, seriously. What surprised me there is that in routes file there are constants defined at the end:
if (!defined('CONTACT_EMAIL')) {
    define('CONTACT_EMAIL', Config::get('mail.from.address'));
    define('CONTACT_NAME', Config::get('mail.from.name'));
    define('SITE_URL', Config::get('app.url'));

    define('ENV_DEVELOPMENT', 'local');
    define('ENV_STAGING', 'staging');

    define('RECENTLY_VIEWED', 'recent_history');
// ...
In fact, another 500 lines of code for constants. I probably wouldn't put it in routes file, but as long as it works... there might have been some logic in that. Also at the very end routes contains a few functions. Yes, functions, just like that. Go figure.
    function uctrans($text)
    {
        return ucwords(trans($text));
    }

    // optional trans: only return the string if it's translated
    function otrans($text)
    {
        $locale = Session::get(SESSION_LOCALE);

        if ($locale == 'en') {
            return trans($text);
        } else {
            $string = trans($text);
            $english = trans($text, [], 'en');
            return $string != $english ? $string : '';
        }
    }
Now, let's take a look at one of the controllers - for example, ClientController.php:
    protected $clientService;
    protected $clientRepo;
    protected $entityType = ENTITY_CLIENT;

    public function __construct(ClientRepository $clientRepo, ClientService $clientService)
    {
        //parent::__construct();

        $this->clientRepo = $clientRepo;
        $this->clientService = $clientService;
    }

    /**
     * Display a listing of the resource.
     *
     * @return Response
     */
    public function index()
    {
        return View::make('list', [
            'entityType' => ENTITY_CLIENT,
            'title' => trans('texts.clients'),
            'sortCol' => '4',
            'columns' => Utils::trans([
              'checkbox',
              'client',
              'contact',
              'email',
              'date_created',
              'last_login',
              'balance',
              ''
            ]),
        ]);
    }
Services and Repositories. Good. So should be a good flexible architecture underneath. View::make() syntax looks sooo Laravel 4, but it's pretty understandable because Invoice Ninja is a couple years old, so I guess the upgrades to newer Laravel versions ended up pretty "hacky". Also there's one list.blade.php for everything - might sound convenient, depends on a developer style, probably. All in all, the code seems a little questionable - although it works well and should be easy to maintain and customize. Also, I can say kind words about their creators involvement in the community and being open - they have open Trello board with roadmap, and also discuss their plans on Github. So, in conclusion, if you want to trust your invoices for a tool based on Laravel, Invoice Ninja is a perfect and solid choice.
Other reviews in this series:
  1. FlarePoint: Laravel-based free CRM
  2. Invoice Ninja – Laravel-powered solution for better invoicing
  3. Attendize – event tickets selling system based on Laravel 5
  4. Ribbbon – project management system on Laravel 5.1 and Vue.js
  5. Faveo: impressive helpdesk system built on Laravel
  6. Confomo: Laravel-based website to meet Twitter friends at conferences

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 59 courses (1056 lessons, total 44 h 09 min)
  • 78 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent Premium Tutorials