|
# coding: utf-8 |
|
$:.unshift '/home/arne/github/kramdown/lib' |
|
|
|
require 'fileutils' |
|
require 'prawn' |
|
require 'kramdown' |
|
require 'json' |
|
|
|
CURRENCIES = { |
|
'EUR' => '€ %.2f', |
|
'USD' => 'USD %.2f' |
|
} |
|
|
|
PAYMENTS = { |
|
'bank' => 'Please make payment directly to the account of Arne Brasseur : |
|
|
|
Arne Brasseur |
|
IBAN: DE67100110012625668716 |
|
BIC: NTSBDEB1XXX', |
|
'paypal' => 'Please make payment through PayPal to the account **arne@arnebrasseur.net**.' |
|
} |
|
|
|
DEFAULTS = { |
|
'currency' => 'EUR', |
|
'payment' => 'bank', |
|
'region' => 'EU', |
|
'pre_message' => '', |
|
'description' => "Software development" |
|
} |
|
|
|
class Prawn::Document |
|
alias current_page page # :( |
|
end |
|
|
|
TEMPLATE = File.read('template.md') |
|
|
|
class Invoice |
|
def self.from_file(infile) |
|
new(DEFAULTS.merge(JSON.parse(File.read(infile)))) |
|
end |
|
|
|
def initialize(data) |
|
data.each do |k,v| |
|
define_singleton_method(k){v} |
|
end |
|
end |
|
|
|
def total |
|
if respond_to?(:days) && respond_to?(:day_price) |
|
days.to_f * day_price.to_f |
|
elsif respond_to?(:hours) && respond_to?(:hour_price) |
|
hrs = hours |
|
if respond_to?(:minutes) |
|
hrs += (minutes.to_f/60) |
|
end |
|
hrs.to_f * hour_price.to_f |
|
else |
|
line_items.map(&:last).map(&:to_f).inject(:+) |
|
end |
|
end |
|
|
|
def fmt_month |
|
%w[ |
|
January February March |
|
April May June |
|
July August September |
|
Oktober November December |
|
][month.to_i - 1] |
|
end |
|
|
|
def payment_info |
|
PAYMENTS[payment] |
|
end |
|
|
|
def fmt_price(price) |
|
CURRENCIES[currency.upcase] % Float(price) |
|
end |
|
|
|
def customer_vat_nr |
|
"VAT no.: #{customer_vat}" if respond_to?(:customer_vat) |
|
end |
|
|
|
def full_invoice_number |
|
if invoice_number =~ /^\d+$/ |
|
"#{year}/#{invoice_number}" |
|
else |
|
invoice_number |
|
end |
|
end |
|
|
|
unless respond_to?(:post_message) |
|
def post_message |
|
"Reverse charge method; customer is responsible for the VAT." if region == 'EU' |
|
end |
|
end |
|
|
|
def table(data) |
|
data.map do |line| |
|
('| ' + line.join(' | ') + ' |').gsub(/(\d) \|/, '\1|') |
|
end.join("\n") |
|
end |
|
|
|
def bold(str) |
|
'**' + str.to_s + '**' |
|
end |
|
|
|
def fmt_hours |
|
if respond_to?(:minutes) |
|
"%d:%02d hours" % [hours, minutes] |
|
else |
|
"%d hours" % [hours] |
|
end |
|
end |
|
|
|
def vat |
|
if region == "DE" |
|
total * 0.19 |
|
else |
|
0 |
|
end |
|
end |
|
|
|
def grand_total |
|
if region == "DE" |
|
total * 1.19 |
|
else |
|
total |
|
end |
|
end |
|
|
|
def line_items_table |
|
lines = if respond_to?(:days) |
|
tbl = [ [ description , "#{days} days", "#{fmt_price day_price} / day" , fmt_price(total) ] ] |
|
if region == "DE" |
|
tbl << [nil, nil, "VAT 19%", fmt_price(total * 0.19)] |
|
tbl << [nil, nil, "Total", fmt_price(total * 1.19)] |
|
else |
|
tbl << [nil, nil, "Total", fmt_price(total)] |
|
end |
|
tbl |
|
elsif respond_to?(:hours) |
|
tbl = [ [ description , fmt_hours, "#{fmt_price hour_price} / hour" , fmt_price(total) ] ] |
|
if region == "DE" |
|
tbl << [nil, nil, "VAT 19%", fmt_price(total * 0.19)] |
|
tbl << [nil, nil, "Total", fmt_price(total * 1.19)] |
|
else |
|
tbl << [nil, nil, "Total", fmt_price(total)] |
|
end |
|
tbl |
|
else |
|
tbl = line_items.map do |(desc, price)| |
|
[ desc, fmt_price(price) ] |
|
end |
|
if region == "DE" |
|
tbl << ["VAT 19%", fmt_price(total * 0.19)] |
|
tbl << ["Total", bold(fmt_price(total * 1.19))] |
|
else |
|
tbl << ["Total", bold(fmt_price(total))] |
|
end |
|
tbl |
|
end |
|
table(lines) |
|
end |
|
|
|
def call(outfile) |
|
markdown = TEMPLATE.gsub(/\{\{[^\}]+\}\}/) do |token| |
|
token = token[2...-2] |
|
self.send(token) |
|
end |
|
|
|
doc = Kramdown::Document.new(markdown, input: 'GFM') |
|
|
|
puts outfile |
|
|
|
File.write(outfile, doc.to_pdf) |
|
dir = "/home/arne/Doku/DE/#{year}#{month}" |
|
FileUtils.mkdir_p(dir) |
|
FileUtils.cp(outfile, dir) |
|
end |
|
end |