SlideShare ist ein Scribd-Unternehmen logo
1 von 95
Downloaden Sie, um offline zu lesen
Sales & payments
   in your app
      Martin Kleppmann
         http://go-test.it
  http://yes-no-cancel.co.uk
  http://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 need
more
of
these
We need
more
of
these

 £
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
Framework
So… 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 money
Enter the Ruby Invoicing Framework RubyGem, or invoicing gem for short. Itʼs a collection
Ruby Invoicing
Framework                                                                      ic in g/
                                                              /invo
                                                            m
So… youʼve spent many nights developing your awesome application. Itʼs coming together


                                                 co
nicely, and youʼve showed it to your friends, who got very excited about it too. In fact,


                                            u b.
people love your app so much that they are willing to pay you money to use it. Great news!



                    t.gith
Keeping it simple, you start taking payments trough PayPal or even accept cheques


                   p
through the post. Later you maybe integrate with the API of a more flexible credit card


                //e
handling provider. Money is coming in – even better news!


       tt    p:
The problems become apparent when you try to turn your app into a business. Suddenly

     h
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 money
Enter the Ruby Invoicing Framework RubyGem, or invoicing gem for short. Itʼs a collection
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
  end
end
Ledger items
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”
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'
  end
end
Fine, but
so 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
  end
end
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
  end
end
Best practices
Your investors will
       want to see
    your accounts
What accountants
         need to know
•   Exact dates and periods of
    invoices & payments
•   Reconciling bank statements
• Details of VAT & other tax
http://www.flickr.com/photos/8704943@N07/3491779722/
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
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
Invoice     (“bill”)


    ≠
Payment    (“receipt”)
Invoice ≠ Payment
 Your Sales Account



 Customer Account



 Your Bank Account
Invoice ≠ Payment
       Your Sales Account
–
    Invoice
+
       Customer Account



       Your Bank Account
Invoice ≠ Payment
       Your Sales Account
–
    Invoice
+
       Customer Account
                            –
                 Payment
                            +
       Your Bank Account
Account statement
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




Charges not yet invoiced
Description                                                          Amount

Pay As You Go charges so far this month                                  £23.45
Account statement                                     Invoice

No.      Date         Description
                                                           Credit Note
                                                                  Amount

100      2009-06-01   Subscription for June                          Payment
                                                                        £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



                            Invoice
Charges not yet invoiced
                       (with status=open)
Description                                                           Amount

Pay As You Go charges so far this month                                   £23.45
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])
  end
end
Thomas Hawk, http://www.flickr.com/photos/thomashawk/2317826708/
VAT
Your Sales Account      VAT Account



     Customer Account



           Your Bank Account
Your Sales Account          VAT Account
               –        –
     Invoice
               +
     Customer Account



           Your Bank Account
Your Sales Account           VAT Account
               –         –
     Invoice
               +
     Customer Account
                     –
         Payment
                     +
           Your Bank Account
Your Sales Account           VAT Account
               –         –          +
     Invoice
               +                        VAT
     Customer Account                   Return
                     –
         Payment
                     +              –
           Your Bank Account
Urgh.
(And we’ve not even started talking about EU
           VAT regulations yet.)
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
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
# New in invoicing gem version 0.3
class MyProduct < ActiveRecord::Base
  acts_as_taxable :price,
    :tax_logic => Invoicing::Countries::UK::VAT.new
end

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

p.price_taxed = 23.00
p.price.to_s
# => "20.0"
Paolo Màrgari, http://www.flickr.com/photos/paolomargari/3050305454/
Overview of your
   customers
Sales and purchases ledger

                                                    Sale         Purchase
Name            Currency   Sales        Purchases                            Balance
                                                    receipts     payments

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}"
    # ...
  end
end
What else?
Martin Kleppmann, http://www.flickr.com/photos/martinkleppmann/3131198770/
¥1,300




Martin Kleppmann, http://www.flickr.com/photos/martinkleppmann/3131198770/
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"
Johnny Vulkan, http://www.flickr.com/photos/26614375@N00/381941029/
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
  end
end
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
  end
end
Displaying an invoice
  class BillingController < ApplicationController
    def document
      @document = Billing::LedgerItem.find(params[:id])


              UBL? WTF?
      respond_to do |format|
        format.html {
        render :text => @document.render_html,
               :layout => true
      }
      format.xml {
        render :xml => @document.render_ubl
      }
    end
  end
end
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>
Designing XML Languages is
hard. It’s boring, political, time-
  consuming, unglamorous,
         irritating work.




  @timbray: “Don’t Invent XML Languages”
  http://tr.im/NoNewXML
Interoperability
OAccounts
Sage         Invoicing gem


MYOB         Payment provider


KashFlow     Shopping cart


Xero         Custom reporting
OAccounts
Sage                     Invoicing gem


MYOB                     Payment provider
             OAccounts
KashFlow                 Shopping cart


Xero                     Custom reporting
OAccounts
          =
 UBL + XBRL-GL + REST +
      Conventions +
    Documentation +
     Collaboration +
Open source implementation
OAccounts
           =
 UBL + XBRL-GL ts. REST+ or g/ +
       Conventionsco un +
          ://o ac
    ht tp
     Documentation +
      Collaboration +
Open source implementation
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/
Thank you!


    Martin Kleppmann
       http://go-test.it
http://yes-no-cancel.co.uk
http://twitter.com/martinkl

Weitere ähnliche Inhalte

Ähnlich wie Invoicing Gem - Sales & Payments In Your App

Monetizing your apps with PayPal API:s
Monetizing your apps with PayPal API:sMonetizing your apps with PayPal API:s
Monetizing your apps with PayPal API:sDisruptive Code
 
Dynamics AX 2009 finanace training
Dynamics AX 2009 finanace trainingDynamics AX 2009 finanace training
Dynamics AX 2009 finanace trainingOutsourceAX
 
Subscribed 2017: Unlocking The Full Functionality Of Zuora’s Billing & Paymen...
Subscribed 2017: Unlocking The Full Functionality Of Zuora’s Billing & Paymen...Subscribed 2017: Unlocking The Full Functionality Of Zuora’s Billing & Paymen...
Subscribed 2017: Unlocking The Full Functionality Of Zuora’s Billing & Paymen...Zuora, Inc.
 
Chargeback Mangement Solutions for Merchants
Chargeback Mangement Solutions for MerchantsChargeback Mangement Solutions for Merchants
Chargeback Mangement Solutions for MerchantsChargeback
 
Startup Highway Workshop
Startup Highway WorkshopStartup Highway Workshop
Startup Highway WorkshopPayPal
 
Business model banking distruptor
Business model banking distruptorBusiness model banking distruptor
Business model banking distruptordesigner DATA
 
Bkper double entry accounts explained
Bkper double entry accounts explainedBkper double entry accounts explained
Bkper double entry accounts explainedJacob van den Berg
 
Bank reconciliation in odoo 12
Bank reconciliation in odoo 12Bank reconciliation in odoo 12
Bank reconciliation in odoo 12Celine George
 
Processing standard invoices and verify supplier balances
Processing standard invoices and verify supplier balancesProcessing standard invoices and verify supplier balances
Processing standard invoices and verify supplier balancesoraerptraining
 
2015 Inspire Tour: Business Starts with Getting Paid
2015 Inspire Tour: Business Starts with Getting Paid2015 Inspire Tour: Business Starts with Getting Paid
2015 Inspire Tour: Business Starts with Getting PaidJeremy Ploessel
 
Debits And Credits
Debits And CreditsDebits And Credits
Debits And CreditsMang Engkus
 
Odoo - Easily Reconcile your Invoices & Payments with the Brand New Bank Stat...
Odoo - Easily Reconcile your Invoices & Payments with the Brand New Bank Stat...Odoo - Easily Reconcile your Invoices & Payments with the Brand New Bank Stat...
Odoo - Easily Reconcile your Invoices & Payments with the Brand New Bank Stat...Odoo
 
Active Merchant
Active MerchantActive Merchant
Active MerchantJohn Ward
 
Cash Receipts in SAP ERP
Cash Receipts in SAP ERPCash Receipts in SAP ERP
Cash Receipts in SAP ERPBill Hanna, CPA
 
Integration of payment gateways using Paypal account
Integration of payment gateways using Paypal account Integration of payment gateways using Paypal account
Integration of payment gateways using Paypal account Phenom People
 
EBOOK FOR RUNNING CPI - USING GOOGLE ADWORDS
EBOOK FOR RUNNING CPI - USING GOOGLE ADWORDS EBOOK FOR RUNNING CPI - USING GOOGLE ADWORDS
EBOOK FOR RUNNING CPI - USING GOOGLE ADWORDS QuynhDaoFtu
 
Vat mass validation services
Vat mass validation servicesVat mass validation services
Vat mass validation servicesVatABConsulting
 

Ähnlich wie Invoicing Gem - Sales & Payments In Your App (20)

Monetizing your apps with PayPal API:s
Monetizing your apps with PayPal API:sMonetizing your apps with PayPal API:s
Monetizing your apps with PayPal API:s
 
Dynamics AX 2009 finanace training
Dynamics AX 2009 finanace trainingDynamics AX 2009 finanace training
Dynamics AX 2009 finanace training
 
Subscribed 2017: Unlocking The Full Functionality Of Zuora’s Billing & Paymen...
Subscribed 2017: Unlocking The Full Functionality Of Zuora’s Billing & Paymen...Subscribed 2017: Unlocking The Full Functionality Of Zuora’s Billing & Paymen...
Subscribed 2017: Unlocking The Full Functionality Of Zuora’s Billing & Paymen...
 
Chargeback Mangement Solutions for Merchants
Chargeback Mangement Solutions for MerchantsChargeback Mangement Solutions for Merchants
Chargeback Mangement Solutions for Merchants
 
Startup Highway Workshop
Startup Highway WorkshopStartup Highway Workshop
Startup Highway Workshop
 
Business model banking distruptor
Business model banking distruptorBusiness model banking distruptor
Business model banking distruptor
 
Bkper double entry accounts explained
Bkper double entry accounts explainedBkper double entry accounts explained
Bkper double entry accounts explained
 
Bank reconciliation in odoo 12
Bank reconciliation in odoo 12Bank reconciliation in odoo 12
Bank reconciliation in odoo 12
 
Processing standard invoices and verify supplier balances
Processing standard invoices and verify supplier balancesProcessing standard invoices and verify supplier balances
Processing standard invoices and verify supplier balances
 
2015 Inspire Tour: Business Starts with Getting Paid
2015 Inspire Tour: Business Starts with Getting Paid2015 Inspire Tour: Business Starts with Getting Paid
2015 Inspire Tour: Business Starts with Getting Paid
 
Debits And Credits
Debits And CreditsDebits And Credits
Debits And Credits
 
FI-Bank.ppt
FI-Bank.pptFI-Bank.ppt
FI-Bank.ppt
 
Odoo - Easily Reconcile your Invoices & Payments with the Brand New Bank Stat...
Odoo - Easily Reconcile your Invoices & Payments with the Brand New Bank Stat...Odoo - Easily Reconcile your Invoices & Payments with the Brand New Bank Stat...
Odoo - Easily Reconcile your Invoices & Payments with the Brand New Bank Stat...
 
Active Merchant
Active MerchantActive Merchant
Active Merchant
 
Cash Receipts in SAP ERP
Cash Receipts in SAP ERPCash Receipts in SAP ERP
Cash Receipts in SAP ERP
 
Integration of payment gateways using Paypal account
Integration of payment gateways using Paypal account Integration of payment gateways using Paypal account
Integration of payment gateways using Paypal account
 
Chap020
Chap020Chap020
Chap020
 
Acceptance Test Driven Development
Acceptance Test Driven DevelopmentAcceptance Test Driven Development
Acceptance Test Driven Development
 
EBOOK FOR RUNNING CPI - USING GOOGLE ADWORDS
EBOOK FOR RUNNING CPI - USING GOOGLE ADWORDS EBOOK FOR RUNNING CPI - USING GOOGLE ADWORDS
EBOOK FOR RUNNING CPI - USING GOOGLE ADWORDS
 
Vat mass validation services
Vat mass validation servicesVat mass validation services
Vat mass validation services
 

Kürzlich hochgeladen

Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfSeasiaInfotech2
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesZilliz
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 

Kürzlich hochgeladen (20)

Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
The Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdfThe Future of Software Development - Devin AI Innovative Approach.pdf
The Future of Software Development - Devin AI Innovative Approach.pdf
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Vector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector DatabasesVector Databases 101 - An introduction to the world of Vector Databases
Vector Databases 101 - An introduction to the world of Vector Databases
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 

Invoicing Gem - Sales & Payments In Your App

  • 1. Sales & payments in your app Martin Kleppmann http://go-test.it http://yes-no-cancel.co.uk http://twitter.com/martinkl
  • 2.
  • 3. Automated Cross-Browser Functional Testing “Selenium in the cloud”
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14. You should be making one of these
  • 17. Your £1m business e.g. £42/mon × 12 mon × 2,000 customers
  • 18. Your £1m business e.g. £42/mon × 12 mon × 2,000 customers Automation!
  • 19. B2B
  • 20. Ruby Invoicing Framework So… 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 money Enter the Ruby Invoicing Framework RubyGem, or invoicing gem for short. Itʼs a collection
  • 21. Ruby Invoicing Framework ic in g/ /invo m So… youʼve spent many nights developing your awesome application. Itʼs coming together co nicely, and youʼve showed it to your friends, who got very excited about it too. In fact, u b. people love your app so much that they are willing to pay you money to use it. Great news! t.gith Keeping it simple, you start taking payments trough PayPal or even accept cheques p through the post. Later you maybe integrate with the API of a more flexible credit card //e handling provider. Money is coming in – even better news! tt p: The problems become apparent when you try to turn your app into a business. Suddenly h 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 money Enter the Ruby Invoicing Framework RubyGem, or invoicing gem for short. Itʼs a collection
  • 23.
  • 25. A solid foundation for building commercial web apps
  • 28. Jargon (as far as we can avoid it)
  • 29. Installing $ gem install invoicing invoicing_generator $ script/generate invoicing_ledger billing --currency=GBP $ rake db:migrate
  • 30. 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 end end
  • 32. Ledger items • Invoice: “you owe us money”
  • 33. Ledger items • Invoice: “you owe us money” • Credit Note: “oops, billed you too much”
  • 34. Ledger items • Invoice: “you owe us money” • Credit Note: “oops, billed you too much” • Payment: “thanks for the cash”
  • 35. 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' end end
  • 39.
  • 40. 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 end end
  • 41. 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 end end
  • 43. Your investors will want to see your accounts
  • 44. What accountants need to know • Exact dates and periods of invoices & payments • Reconciling bank statements • Details of VAT & other tax
  • 46. 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
  • 47. 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
  • 48. Invoice (“bill”) ≠ Payment (“receipt”)
  • 49. Invoice ≠ Payment Your Sales Account Customer Account Your Bank Account
  • 50. Invoice ≠ Payment Your Sales Account – Invoice + Customer Account Your Bank Account
  • 51. Invoice ≠ Payment Your Sales Account – Invoice + Customer Account – Payment + Your Bank Account
  • 52. Account statement 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 Charges not yet invoiced Description Amount Pay As You Go charges so far this month £23.45
  • 53. Account statement Invoice No. Date Description Credit Note Amount 100 2009-06-01 Subscription for June Payment £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 Invoice Charges not yet invoiced (with status=open) Description Amount Pay As You Go charges so far this month £23.45
  • 54. 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]) end end
  • 56. VAT
  • 57. Your Sales Account VAT Account Customer Account Your Bank Account
  • 58. Your Sales Account VAT Account – – Invoice + Customer Account Your Bank Account
  • 59. Your Sales Account VAT Account – – Invoice + Customer Account – Payment + Your Bank Account
  • 60. Your Sales Account VAT Account – – + Invoice + VAT Customer Account Return – Payment + – Your Bank Account
  • 61. Urgh. (And we’ve not even started talking about EU VAT regulations yet.)
  • 62. 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
  • 63. 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
  • 64. 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
  • 65. VAT made easy # New in invoicing gem version 0.3 class MyProduct < ActiveRecord::Base acts_as_taxable :price, :tax_logic => Invoicing::Countries::UK::VAT.new end p = MyProduct.new :price => 10 p.price_with_tax_info # => "£11.50 (inc. VAT)" p.price_taxed = 23.00 p.price.to_s # => "20.0"
  • 67. Overview of your customers
  • 68. Sales and purchases ledger Sale Purchase Name Currency Sales Purchases Balance receipts payments 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
  • 69. 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}" # ... end end
  • 74. 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
  • 76. 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"
  • 80. 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 end end
  • 81. 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 end end
  • 82. Displaying an invoice class BillingController < ApplicationController def document @document = Billing::LedgerItem.find(params[:id]) UBL? WTF? respond_to do |format| format.html { render :text => @document.render_html, :layout => true } format.xml { render :xml => @document.render_ubl } end end end
  • 83. UBL = “Universal Business Language” OASIS Open Standard
  • 84. 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
  • 85. 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> ...
  • 86. 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>
  • 87. 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>
  • 88. Designing XML Languages is hard. It’s boring, political, time- consuming, unglamorous, irritating work. @timbray: “Don’t Invent XML Languages” http://tr.im/NoNewXML
  • 90. OAccounts Sage Invoicing gem MYOB Payment provider KashFlow Shopping cart Xero Custom reporting
  • 91. OAccounts Sage Invoicing gem MYOB Payment provider OAccounts KashFlow Shopping cart Xero Custom reporting
  • 92. OAccounts = UBL + XBRL-GL + REST + Conventions + Documentation + Collaboration + Open source implementation
  • 93. OAccounts = UBL + XBRL-GL ts. REST+ or g/ + Conventionsco un + ://o ac ht tp Documentation + Collaboration + Open source implementation
  • 94. 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/
  • 95. Thank you! Martin Kleppmann http://go-test.it http://yes-no-cancel.co.uk http://twitter.com/martinkl