Module: InvoiceGenerator

Extended by:
InvoiceGenerator
Included in:
InvoiceGenerator
Defined in:
lib/invoicegenerator/version.rb,
lib/invoicegenerator/invoicegenerator.rb

Constant Summary collapse

VERSION =
'1.0.2'

Instance Method Summary collapse

Instance Method Details

#add_extra_options(opts) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/invoicegenerator/invoicegenerator.rb', line 97

def add_extra_options(opts)
  today = Date.today
  opts[:month] = today.strftime('%B')
  opts[:past_month] = (today << 1).strftime('%B')
  opts[:year] = today.strftime('%Y')

  raw_balance = opts[:items].inject(0) do |balance, item|
    balance + (item[2] * item[1])
  end
  opts[:balance] = Money.new(raw_balance, opts[:currency]).format
end

#fix_date_options(opts) ⇒ Object



91
92
93
94
95
# File 'lib/invoicegenerator/invoicegenerator.rb', line 91

def fix_date_options(opts)
  map_date_fields(opts) do |value|
    value.is_a?(Date) ? value : Date.parse(value)
  end
end

#format_items(opts) ⇒ Object



109
110
111
112
113
114
115
116
117
118
# File 'lib/invoicegenerator/invoicegenerator.rb', line 109

def format_items(opts)
  opts[:items].map! do |i|
    fail 'Items must have 3 values' if i.size != 3

    i[3] = Money.new(i[2] * i[1], opts[:currency]).format
    i[2] = Money.new(i[2], opts[:currency]).format
    "<tr><td>#{i.join('</td><td>')}</td></tr>"
  end
  opts[:items] = opts[:items].join
end

#generate_html(opts) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
# File 'lib/invoicegenerator/invoicegenerator.rb', line 124

def generate_html(opts)
  map_date_fields(opts) do |value|
    value.strftime('%B %-d, %Y')
  end

  html = File.read(opts[:template] || template_path)
  opts.each do |opt, value|
    html.gsub!("%#{opt}%", value.to_s)
  end
  html
end

#mainObject



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/invoicegenerator/invoicegenerator.rb', line 136

def main
  opts = read_params
  show_yml_example_and_exit if opts[:'show-yml-example_given']
  show_template_example_and_exit if opts[:'show-template-example_given']

  read_yml(opts)
  fix_date_options(opts)
  add_extra_options(opts)
  format_items(opts)
  html = generate_html(opts)

  kit = PDFKit.new(html)
  name = "invoice-#{opts[:number]}.pdf"
  puts "Generating #{name}..."
  kit.to_file(name)
rescue Errno::ENOENT => e
  abort e.message
rescue RuntimeError => e
  abort e.message
end

#map_date_fields(opts) ⇒ Object



48
49
50
51
52
# File 'lib/invoicegenerator/invoicegenerator.rb', line 48

def map_date_fields(opts)
  [:date, 'due-date'].each do |i|
    opts[i] = yield(opts[i])
  end
end

#read_paramsObject



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/invoicegenerator/invoicegenerator.rb', line 54

def read_params
  Optimist.options do
    opt :client,            'Contents of client field.', type: :string
    opt :currency,          'Currency used.', type: :string, default: 'USD'
    opt :date,              'Invoice date.', type: :date, default: Date.today
    opt 'due-date',         'Due date.', type: :date, default: (Date.today + 15)
    opt :from,              'Contents of from field.', type: :string
    opt :header,            'Contents of the header.', type: :string, default: 'Invoice'
    opt :notes,             'Contents of notes field.', type: :string
    opt :number,            'Invoice number.', type: :string
    opt 'show-yml-example', 'Show an example of a YML file that can be used by this script.'
    opt 'show-template-example', 'Show an example of a HTML template.'
    opt :stdin,             'Read YML file from STDIN.'
    opt :template,          'HTML template to use', type: :string
    opt :yml,               'YML file with values for parameters not given into command line.', default: 'invoice.yml'
  end
end

#read_yml(opts) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/invoicegenerator/invoicegenerator.rb', line 72

def read_yml(opts)
  if opts[:stdin]
    data = StringIO.new
    data << STDIN.read until STDIN.eof?
    yaml = data.string
  else
    yaml = File.read(opts[:yml])
  end
  YAML.safe_load(yaml).inject(opts) do |memo, item|
    memo[item[0].to_sym] = item[1]
    memo
  end
  fail('Items not in the right format, something is missing.') unless opts[:items].is_a?(Array)

  opts
rescue Errno::ENOENT
  raise "YML file #{opts[:yml]} not found or can't be read."
end

#show_template_example_and_exitObject



43
44
45
46
# File 'lib/invoicegenerator/invoicegenerator.rb', line 43

def show_template_example_and_exit
  puts File.read(template_path)
  exit
end

#show_yml_example_and_exitObject



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/invoicegenerator/invoicegenerator.rb', line 14

def show_yml_example_and_exit
  puts <<eot
from: |
  My multiline name
  Here's a second line
client: |
  My multiline client
  Hey ho, second line here
number: 2019-123
notes: |
  If all your data are always the same, just the invoice number changes,
  save the the static data in a yml and pass the invoice number on command line
  by using (--number).

  Note that the date is always default to today, and the due-date to today + 15
items:
  -
    - Nice item for %past_month% %year%
    - 1
    - 12334
  -
    - Other item, for %month%
    - 0.5
    - 100000
currency: GBP
eot
  exit
end

#template_pathObject



120
121
122
# File 'lib/invoicegenerator/invoicegenerator.rb', line 120

def template_path
  File.join(File.dirname(__FILE__), 'invoicegenerator.html')
end