Invoicing Gem - Sales & Payments In Your App

Post on 06-May-2015

2.316 views 1 download

Tags:

description

Presentation given by Martin Kleppmann at Rails Underground in London on 24 July 2009. Gives an overview of how to develop a commercial B2B SaaS web app using Rails or other Ruby web frameworks, including best practices of structuring accouting data.

Transcript of Invoicing Gem - Sales & Payments In Your App

Sales & paymentsin your app

Martin Kleppmannhttp://go-test.it

http://yes-no-cancel.co.ukhttp://twitter.com/martinkl

Automated Cross-Browser Functional Testing

“Selenium in the cloud”

http://go-test.it

SaaS

Revenue model

You should be making one of

these

We needmoreofthese

We needmoreofthese

£

Your £1m business

e.g. £42/mon × 12 mon × 2,000 customers

Your £1m business

e.g. £42/mon × 12 mon × 2,000 customers

Automation!

B2B

Ruby Invoicing FrameworkSo… youʼve spent many nights developing your awesome application. Itʼs coming together nicely, and youʼve showed it to your friends, who got very excited about it too. In fact, people love your app so much that they are willing to pay you money to use it. Great news!Keeping it simple, you start taking payments trough PayPal or even accept cheques through the post. Later you maybe integrate with the API of a more flexible credit card handling provider. Money is coming in – even better news!The problems become apparent when you try to turn your app into a business. Suddenly everything becomes a lot more complicated. You need to start thinking about ugly things like tax and you need to pay an accountant to sort out the paperwork for you. You need to start bookkeeping, a prospect which gives you the shivers. Maybe some of your customers are awkward, accepting billing only in their own currency or requiring a special tax status. Itʼs all a bit of a mess, and as you grudgingly start ploughing through the Wikipedia page on “credits and debits”, you wish that you could just get the money and leave it at that.

The missing link between your app and the moneyEnter the Ruby Invoicing Framework RubyGem, or invoicing gem for short. Itʼs a collection of tools which provide the basic mechanisms for supporting financial transactions within your own application.

Ruby Invoicing FrameworkSo… youʼve spent many nights developing your awesome application. Itʼs coming together nicely, and youʼve showed it to your friends, who got very excited about it too. In fact, people love your app so much that they are willing to pay you money to use it. Great news!Keeping it simple, you start taking payments trough PayPal or even accept cheques through the post. Later you maybe integrate with the API of a more flexible credit card handling provider. Money is coming in – even better news!The problems become apparent when you try to turn your app into a business. Suddenly everything becomes a lot more complicated. You need to start thinking about ugly things like tax and you need to pay an accountant to sort out the paperwork for you. You need to start bookkeeping, a prospect which gives you the shivers. Maybe some of your customers are awkward, accepting billing only in their own currency or requiring a special tax status. Itʼs all a bit of a mess, and as you grudgingly start ploughing through the Wikipedia page on “credits and debits”, you wish that you could just get the money and leave it at that.

The missing link between your app and the moneyEnter the Ruby Invoicing Framework RubyGem, or invoicing gem for short. Itʼs a collection of tools which provide the basic mechanisms for supporting financial transactions within your own application.

http://ept.github.com/invoicing/

Richard Messenger, http://www.flickr.com/photos/richardmessenger/2626927255/

Production use

A solid foundation for building commercial

web apps

I’m not an accountant

Jargon

Jargon(as far as we can avoid it)

Installing

$ gem install invoicing invoicing_generator

$ script/generate invoicing_ledger billing \ --currency=GBP

$ rake db:migrate

Model classes

module Billing class Invoice < LedgerItem acts_as_invoice end class CreditNote < LedgerItem acts_as_credit_note end class Payment < LedgerItem acts_as_payment endend

Ledger items

•Invoice:“you owe us money”

Ledger items

•Invoice:“you owe us money”

•Credit Note:“oops, billed you too much”

Ledger items

•Invoice:“you owe us money”

•Credit Note:“oops, billed you too much”

•Payment:“thanks for the cash”

Ledger items

acts_as_ledger_item

module Billing class LedgerItem < ActiveRecord::Base acts_as_ledger_item has_many :line_items, :class_name => 'Billing::LineItem' belongs_to :sender, :class_name => 'Company' belongs_to :recipient, :class_name => 'Company' endend

Fine, butso what?

http://www.flickr.com/photos/26614375@N00/381941029/

Avoid writing boring code

Displaying an invoice

class BillingController < ApplicationController def document @document = Billing::LedgerItem.find(params[:id]) respond_to do |format| format.html { render :text => @document.render_html, :layout => true } format.xml { render :xml => @document.render_ubl } end endend

Displaying an invoice

class BillingController < ApplicationController def document @document = Billing::LedgerItem.find(params[:id]) respond_to do |format| format.html { render :text => @document.render_html, :layout => true } format.xml { render :xml => @document.render_ubl } end endend

Best practices

Your investors willwant to see

your accounts

•Exact dates and periods of invoices & payments

•Reconciling bank statements

•Details of VAT & other tax

What accountants need to know

http

://w

ww

.flic

kr.c

om/p

hoto

s/87

0494

3@N

07/3

4917

7972

2/

create_table "ledger_items" do |t| t.string "type" t.integer "sender_id" t.integer "recipient_id" t.string "currency", :default => "GBP", :null => false t.decimal "total_amount", :precision => 20, :scale => 4 t.decimal "tax_amount", :precision => 20, :scale => 4 t.string "status", :limit => 20 t.string "description" t.datetime "issue_date" t.datetime "period_start" t.datetime "period_end" t.datetime "created_at" t.datetime "updated_at"end

Dates & periods

create_table "ledger_items" do |t| t.string "type" t.integer "sender_id" t.integer "recipient_id" t.string "currency", :default => "GBP", :null => false t.decimal "total_amount", :precision => 20, :scale => 4 t.decimal "tax_amount", :precision => 20, :scale => 4 t.string "status", :limit => 20 t.string "description" t.datetime "issue_date" t.datetime "period_start" t.datetime "period_end" t.datetime "created_at" t.datetime "updated_at"end

Dates & periods

Invoice≠

Payment

(“bill”)

(“receipt”)

Your Sales Account

Customer Account

Your Bank Account

Invoice ≠ Payment

Your Sales Account

Customer Account

Your Bank Account

Invoice

+

Invoice ≠ Payment

Your Sales Account

Customer Account

Your Bank Account

Invoice

+

Payment

+

Invoice ≠ Payment

Charges not yet invoiced

Description Amount

Pay As You Go charges so far this month £23.45

No. Date Description Amount

100 2009-06-01 Subscription for June £115.00

101 2009-06-24 Referral fee – thanks for inviting 3 friends –£10.00

102 2009-06-30 Credit card payment including PAYG credit –£200.00

Current account balance (GBP) –£75.00

Account statement

Charges not yet invoiced

Description Amount

Pay As You Go charges so far this month £23.45

No. Date Description Amount

100 2009-06-01 Subscription for June £115.00

101 2009-06-24 Referral fee – thanks for inviting 3 friends –£10.00

102 2009-06-30 Credit card payment including PAYG credit –£200.00

Current account balance (GBP) –£75.00

Account statement Invoice

Credit Note

Payment

Invoice(with status=open)

Account statement

class BillingController < ApplicationController def statement scope = Billing::LedgerItem. exclude_empty_invoices. sent_or_received_by(params[:id]). sorted(:issue_date)

@in_effect = scope.in_effect.all @open_or_pending = scope.open_or_pending.all @summary = Billing::LedgerItem.account_summary( params[:id]) endend

Thomas Hawk, http://www.flickr.com/photos/thomashawk/2317826708/

VAT

Your Sales Account

Customer Account

Your Bank Account

VAT Account

Your Sales Account

Customer Account

Your Bank Account

VAT Account

Invoice

+

Your Sales Account

Customer Account

Your Bank Account

VAT Account

Payment

+

Invoice

+

Your Sales Account

Customer Account

Your Bank Account

VAT Account

Payment

+

Invoice

+

+

VATReturn

Urgh.(And we’ve not even started talking about EU

VAT regulations yet.)

create_table "ledger_items" do |t| t.string "type" t.integer "sender_id" t.integer "recipient_id" t.string "currency", :default => "GBP", :null => false t.decimal "total_amount", :precision => 20, :scale => 4 t.decimal "tax_amount", :precision => 20, :scale => 4 t.string "status", :limit => 20 t.string "description" t.datetime "issue_date" t.datetime "period_start" t.datetime "period_end" t.datetime "created_at" t.datetime "updated_at"end

VAT made easy

create_table "ledger_items" do |t| t.string "type" t.integer "sender_id" t.integer "recipient_id" t.string "currency", :default => "GBP", :null => false t.decimal "total_amount", :precision => 20, :scale => 4 t.decimal "tax_amount", :precision => 20, :scale => 4 t.string "status", :limit => 20 t.string "description" t.datetime "issue_date" t.datetime "period_start" t.datetime "period_end" t.datetime "created_at" t.datetime "updated_at"end

VAT made easy

create_table "ledger_items" do |t| t.string "type" t.integer "sender_id" t.integer "recipient_id" t.string "currency", :default => "GBP", :null => false t.decimal "total_amount", :precision => 20, :scale => 4 t.decimal "tax_amount", :precision => 20, :scale => 4 t.string "status", :limit => 20 t.string "description" t.datetime "issue_date" t.datetime "period_start" t.datetime "period_end" t.datetime "created_at" t.datetime "updated_at"end

VAT made easy

VAT made easy

# New in invoicing gem version 0.3class MyProduct < ActiveRecord::Base acts_as_taxable :price, :tax_logic => Invoicing::Countries::UK::VAT.newend

p = MyProduct.new :price => 10p.price_with_tax_info# => "£11.50 (inc. VAT)"

p.price_taxed = 23.00p.price.to_s# => "20.0"

Paolo Màrgari, http://www.flickr.com/photos/paolomargari/3050305454/

Overview of your customers

Sales and purchases ledger

Name Currency Sales Purchases Salereceipts

Purchasepayments Balance

A. N. Other GBP £400.00 £0.00 £400.00 £0.00 £0.00

Some Customer GBP £0.00 £395.00 £0.00 £250.00 –£145.00

Some Customer USD $2,782.31 $0.00 $2,160.61 $0.00 $621.70

Widgets Ltd GBP £229.63 £12.00 £300.00 £0.00 £82.37

Sales/purchase ledger

@summaries = Billing::LedgerItem.account_summaries(params[:id])

@summaries.each_pair do |id, by_currency| by_currency.each_pair do |currency, data| puts "Sales: #{data.sales_formatted}" puts "Purchases: #{data.purchases_formatted}" # ... endend

What else?

Mar

tin K

lepp

man

n, h

ttp:

//ww

w.fl

ickr

.com

/pho

tos/

mar

tinkl

eppm

ann/

3131

1987

70/

Mar

tin K

lepp

man

n, h

ttp:

//ww

w.fl

ickr

.com

/pho

tos/

mar

tinkl

eppm

ann/

3131

1987

70/

¥1,300

Currency formatting

Currency formatting

inv = Billing::MyInvoice.new :currency => 'JPY'nov = Billing::LineItem.new( :description => 'November charge', :net_amount => 10, :tax_point => '2008-11-30')inv.line_items << nov; inv.save!

nov.amount_formatted# => "¥10"inv.currency = 'GBP'nov.amount_formatted

Nothing is constant

VAT change 1/12/09

dec = Billing::LineItem.new( :description => 'December charge', :net_amount => 10, :tax_point => '2008-12-01')inv.line_items << dec

nov.amount_taxed_formatted# => "£11.75"dec.amount_taxed_formatted# => "£11.50"

John

ny V

ulka

n, h

ttp:

//ww

w.fl

ickr

.com

/pho

tos/

2661

4375

@N

00/3

8194

1029

/

Integrating with payment gateways

⇒ http://activemerchant.org

Open standards

Displaying an invoice

class BillingController < ApplicationController def document @document = Billing::LedgerItem.find(params[:id]) respond_to do |format| format.html {

render :text => @document.render_html, :layout => true } format.xml { render :xml => @document.render_ubl } end endend

Displaying an invoice

class BillingController < ApplicationController def document @document = Billing::LedgerItem.find(params[:id]) respond_to do |format| format.html {

render :text => @document.render_html, :layout => true } format.xml { render :xml => @document.render_ubl } end endend

Displaying an invoice

class BillingController < ApplicationController def document @document = Billing::LedgerItem.find(params[:id]) respond_to do |format| format.html {

render :text => @document.render_html, :layout => true } format.xml { render :xml => @document.render_ubl } end endend

UBL? WTF?

UBL = “Universal Business Language”

OASIS Open Standard

UBL: If you’re working with invoices and purchase orders

and that kind of stuff (and who isn’t?) [...] Look no further.

@timbray: “Don’t Invent XML Languages”http://tr.im/NoNewXML

UBL Example (1/3)

<ubl:Invoice xmlns:ubl="..." xmlns:cbc="..." xmlns:cac="..."> <cbc:ID>1</cbc:ID> <cbc:IssueDate>2008-06-30</cbc:IssueDate> <cac:InvoicePeriod> <cbc:StartDate>2008-06-01</cbc:StartDate> <cbc:EndDate>2008-07-01</cbc:EndDate> </cac:InvoicePeriod> <cac:AccountingSupplierParty> ... </cac:AccountingSupplierParty> <cac:AccountingCustomerParty> ... </cac:AccountingCustomerParty>...

UBL Example (2/3)

<cac:TaxTotal> <cbc:TaxAmount currencyID="GBP"> 15.00 </cbc:TaxAmount> </cac:TaxTotal> <cac:LegalMonetaryTotal> <cbc:TaxExclusiveAmount currencyID="GBP"> 100.00 </cbc:TaxExclusiveAmount> <cbc:PayableAmount currencyID="GBP"> 115.00 </cbc:PayableAmount> </cac:LegalMonetaryTotal>

UBL Example (3/3)

<cac:InvoiceLine> <cbc:ID>42</cbc:ID> <cbc:LineExtensionAmount currencyID="GBP"> 100.00 </cbc:LineExtensionAmount> <cac:Item> <cbc:Description> Subscription for my fantastic app </cbc:Description> </cac:Item> </cac:InvoiceLine></ubl:Invoice>

@timbray: “Don’t Invent XML Languages”http://tr.im/NoNewXML

Designing XML Languages is hard. It’s boring, political, time-

consuming, unglamorous, irritating work.

Interoperability

OAccountsSage

MYOB

KashFlow

Xero

Invoicing gem

Payment provider

Shopping cart

Custom reporting

OAccountsSage

MYOB

KashFlow

Xero

Invoicing gem

Payment provider

Shopping cart

Custom reporting

OAccounts

OAccounts=

UBL + XBRL-GL + REST + Conventions +

Documentation +Collaboration +

Open source implementation

OAccounts=

UBL + XBRL-GL + REST + Conventions +

Documentation +Collaboration +

Open source implementation

http://oaccounts.org/

Image credits

Screenshots of:http://basecamphq.com/signup, http://freshbooks.com/pricing.php, http://fogcreek.com/FogBugz/,http://github.com/plans, http://lessaccounting.com/pricing, http://freeagentcentral.com/pricing, http://huddle.net/huddle-price-plans/, http://twitter.com/bensummers/status/1199722134,http://oneis.co.uk/, http://bidforwine.co.uk

Building site: Richard Messenger, http://www.flickr.com/photos/richardmessenger/2626927255/

Tim Bray: http://en.wikipedia.org/wiki/File:Tim_Bray.jpg

Pile of money: Johnny Vulkan, http://www.flickr.com/photos/26614375@N00/381941029/

Astronomical clock: magro_kr, http://www.flickr.com/photos/8704943@N07/3491779722/

Tax Service: Thomas Hawk, http://www.flickr.com/photos/thomashawk/2317826708/

Man in a shop: Paolo Màrgari, http://www.flickr.com/photos/paolomargari/3050305454/

Hands full of money: Marshall Astor, http://www.flickr.com/photos/lifeontheedge/2672465894/

Martin Kleppmannhttp://go-test.it

http://yes-no-cancel.co.ukhttp://twitter.com/martinkl

Thank you!