invoice_printer 1.0.0 → 1.1.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +62 -5
- data/bin/invoice_printer +140 -0
- data/examples/complex_invoice.rb +11 -1
- data/examples/czech_invoice.rb +12 -0
- data/examples/international_invoice.rb +9 -0
- data/examples/long_invoice.rb +9 -1
- data/examples/promo.rb +9 -0
- data/examples/simple_invoice.rb +7 -0
- data/invoice_printer.gemspec +1 -0
- data/lib/invoice_printer/document/item.rb +37 -21
- data/lib/invoice_printer/document.rb +130 -90
- data/lib/invoice_printer/pdf_document.rb +153 -108
- data/lib/invoice_printer/version.rb +1 -1
- data/lib/invoice_printer.rb +6 -4
- data/test/cli_test.rb +76 -0
- data/test/examples_test.rb +5 -0
- data/test/inputs_test.rb +9 -0
- data/test/invoice_printer_test.rb +13 -2
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bbd94bf5220a80cfe9001aaa3f4de3280a55341b
|
4
|
+
data.tar.gz: e008c681ad05e8af4e21cd434c1d3646d954b3ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0382b87d579e861ef8c22cb2f676b065f4eec8fa21223af9822a601ffc8ffba3aa90c1259a1bc528697b472139b01a55bee7876e3e57e005c760c84ed65b9ef
|
7
|
+
data.tar.gz: de82e8d3c3c1d40602b682240f38985564fa8b05dd77a997b3fa29ee107c6968902f7a2e229e097bc6da4b1fc05ff9eba1558ed2afb8be793233007f8b98273e
|
data/README.md
CHANGED
@@ -4,13 +4,13 @@
|
|
4
4
|
|
5
5
|
|
6
6
|
|
7
|
-
Super simple PDF invoicing in pure Ruby
|
7
|
+
Super simple PDF invoicing in pure Ruby
|
8
8
|
|
9
|
-
InvoicePrinter
|
10
|
-
designed only to provide an interface to build the PDF version of these documents.
|
9
|
+
InvoicePrinter is a Ruby library and a command line program. You can use Ruby or JSON to build the final PDF.
|
11
10
|
|
12
11
|
## Features
|
13
12
|
|
13
|
+
- A4 and US letter paper size
|
14
14
|
- Invoice/document name and number
|
15
15
|
- Purchaser and provider boxes with addresses and identificaton numbers
|
16
16
|
- Payment method box showing banking details including SWIFT and IBAN fields
|
@@ -24,6 +24,8 @@ designed only to provide an interface to build the PDF version of these document
|
|
24
24
|
- Background (as image)
|
25
25
|
- Stamp & signature (as image)
|
26
26
|
- Note
|
27
|
+
- JSON format
|
28
|
+
- CLI
|
27
29
|
- Well tested
|
28
30
|
|
29
31
|
## Example
|
@@ -144,8 +146,63 @@ def show
|
|
144
146
|
end
|
145
147
|
```
|
146
148
|
|
149
|
+
### JSON format
|
150
|
+
|
151
|
+
JSON format is supported via `from_json` method. JSON itself mimicks the original Ruby objects:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
json = InvoicePrinter::Document.new(...).to_json
|
155
|
+
document = InvoicePrinter::Document.from_json(json)
|
156
|
+
|
157
|
+
|
158
|
+
InvoicePrinter.print(
|
159
|
+
document: document,
|
160
|
+
...
|
161
|
+
)
|
162
|
+
|
163
|
+
```
|
164
|
+
|
165
|
+
## CLI
|
166
|
+
|
167
|
+
InvoicePrinter ships with a command line executable called `invoice_printer`.
|
168
|
+
|
169
|
+
It supports all features except it only accepts JSON as an input.
|
170
|
+
|
171
|
+
```
|
172
|
+
$ invoice_printer --help
|
173
|
+
Usage: invoice_printer [options]
|
174
|
+
|
175
|
+
Options:
|
176
|
+
|
177
|
+
-l, --labels labels as JSON
|
178
|
+
-d, --document document as JSON
|
179
|
+
-s, --stamp path to stamp
|
180
|
+
--logo path to logotype
|
181
|
+
--font path to font
|
182
|
+
--page_size letter or a4 (letter is the default)
|
183
|
+
-f, --filename output path
|
184
|
+
-r, --render directly render PDF stream (filename option will be ignored)
|
185
|
+
```
|
186
|
+
|
187
|
+
|
147
188
|
## Customization
|
148
189
|
|
190
|
+
### Page size
|
191
|
+
|
192
|
+
Both A4 and US letter is supported. Just pass `page_size` as an argument to `print` or `render` methods:
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
InvoicePrinter.print(
|
196
|
+
document: invoice,
|
197
|
+
labels: labels,
|
198
|
+
page_size: :a4,
|
199
|
+
file_name: 'invoice.pdf'
|
200
|
+
)
|
201
|
+
```
|
202
|
+
|
203
|
+
`:letter` is the default.
|
204
|
+
|
205
|
+
|
149
206
|
### Localization
|
150
207
|
|
151
208
|
To localize your documents you can set both global defaults or instance
|
@@ -172,7 +229,7 @@ to `initializers/invoice_printer.rb` if you are using Rails.
|
|
172
229
|
|
173
230
|
```ruby
|
174
231
|
InvoicePrinter.labels = {
|
175
|
-
name: 'Invoice'
|
232
|
+
name: 'Invoice',
|
176
233
|
provider: 'Provider',
|
177
234
|
purchaser: 'Purchaser',
|
178
235
|
tax_id: 'Identification number',
|
@@ -193,7 +250,7 @@ InvoicePrinter.labels = {
|
|
193
250
|
tax: 'Tax',
|
194
251
|
tax2: 'Tax 2',
|
195
252
|
tax3: 'Tax 3',
|
196
|
-
subtotal: 'Subtotal'
|
253
|
+
subtotal: 'Subtotal',
|
197
254
|
total: 'Total'
|
198
255
|
}
|
199
256
|
```
|
data/bin/invoice_printer
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH << File.expand_path('lib')
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'json'
|
6
|
+
require 'invoice_printer'
|
7
|
+
|
8
|
+
def show_version
|
9
|
+
puts "InvoicePrinter v#{InvoicePrinter::VERSION}"
|
10
|
+
|
11
|
+
exit 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def show_help
|
15
|
+
puts <<~HELP
|
16
|
+
Usage: invoice_printer [options]
|
17
|
+
|
18
|
+
Options:
|
19
|
+
|
20
|
+
-l, --labels labels as JSON
|
21
|
+
-d, --document document as JSON
|
22
|
+
-s, --stamp path to stamp
|
23
|
+
--logo path to logotype
|
24
|
+
--font path to font
|
25
|
+
--page-size letter or a4 (letter is the default)
|
26
|
+
-f, --filename output path
|
27
|
+
-r, --render directly render PDF stream (filename option will be ignored)
|
28
|
+
|
29
|
+
HELP
|
30
|
+
|
31
|
+
exit 0
|
32
|
+
end
|
33
|
+
|
34
|
+
options = {}
|
35
|
+
|
36
|
+
parser = OptionParser.new do|opts|
|
37
|
+
opts.banner = "Usage: invoice_printer [options]"
|
38
|
+
|
39
|
+
opts.on('-l', '--labels JSON') do |json|
|
40
|
+
options[:labels] = json
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on('-d', '--document JSON') do |json|
|
44
|
+
options[:document] = json
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-s', '--stamp PATH') do |path|
|
48
|
+
options[:stamp] = path
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('--logo PATH') do |path|
|
52
|
+
options[:logo] = path
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('--font PATH') do |path|
|
56
|
+
options[:font] = path
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('--page-size OPTION') do |option|
|
60
|
+
options[:page_size] = option.to_sym
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on('-f', '--filename PATH') do |path|
|
64
|
+
options[:filename] = path
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on('-r', '--render') do
|
68
|
+
options[:render] = true
|
69
|
+
end
|
70
|
+
|
71
|
+
opts.on('-v', '--version') do
|
72
|
+
show_version
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on('--debug') do
|
76
|
+
options[:debug] = true
|
77
|
+
end
|
78
|
+
|
79
|
+
opts.on('-h', '--help') do
|
80
|
+
show_help
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
parser.parse!
|
86
|
+
|
87
|
+
show_help if options.empty?
|
88
|
+
|
89
|
+
# Defaults
|
90
|
+
options[:page_size] ||= :letter
|
91
|
+
|
92
|
+
raise '--document not provided' unless options[:document]
|
93
|
+
|
94
|
+
begin
|
95
|
+
json = JSON.parse options[:document]
|
96
|
+
document = InvoicePrinter::Document.from_json(json)
|
97
|
+
rescue => e
|
98
|
+
STDERR.puts "ERROR: parsing JSON failed. Invalid JSON?"
|
99
|
+
|
100
|
+
if options[:debug]
|
101
|
+
STDERR.puts
|
102
|
+
STDERR.puts e.message
|
103
|
+
STDERR.puts e.backtrace
|
104
|
+
end
|
105
|
+
|
106
|
+
exit 1
|
107
|
+
end
|
108
|
+
|
109
|
+
if options[:render]
|
110
|
+
stream = InvoicePrinter.render(
|
111
|
+
document: document,
|
112
|
+
font: options[:font],
|
113
|
+
stamp: options[:stamp],
|
114
|
+
logo: options[:logo],
|
115
|
+
page_size: options[:page_size]
|
116
|
+
)
|
117
|
+
|
118
|
+
puts stream
|
119
|
+
else
|
120
|
+
raise '--filename not provided. Use --render if you with to render to STDOUT.' unless options[:filename]
|
121
|
+
|
122
|
+
InvoicePrinter.print(
|
123
|
+
document: document,
|
124
|
+
font: options[:font],
|
125
|
+
stamp: options[:stamp],
|
126
|
+
logo: options[:logo],
|
127
|
+
file_name: options[:filename],
|
128
|
+
page_size: options[:page_size]
|
129
|
+
)
|
130
|
+
end
|
131
|
+
rescue => e
|
132
|
+
STDERR.puts "ERROR: #{e.message}"
|
133
|
+
|
134
|
+
if options[:debug]
|
135
|
+
STDERR.puts
|
136
|
+
STDERR.puts e.backtrace
|
137
|
+
end
|
138
|
+
|
139
|
+
exit 1
|
140
|
+
end
|
data/examples/complex_invoice.rb
CHANGED
@@ -19,7 +19,7 @@ item = InvoicePrinter::Document::Item.new(
|
|
19
19
|
)
|
20
20
|
|
21
21
|
item2 = InvoicePrinter::Document::Item.new(
|
22
|
-
name: '
|
22
|
+
name: 'Consulting',
|
23
23
|
quantity: '10',
|
24
24
|
unit: 'hr',
|
25
25
|
price: '$ 30',
|
@@ -66,3 +66,13 @@ InvoicePrinter.print(
|
|
66
66
|
file_name: 'complex_invoice.pdf',
|
67
67
|
background: 'background.png'
|
68
68
|
)
|
69
|
+
|
70
|
+
InvoicePrinter.print(
|
71
|
+
document: invoice,
|
72
|
+
labels: labels,
|
73
|
+
stamp: 'stamp.png',
|
74
|
+
logo: 'prawn.png',
|
75
|
+
file_name: 'complex_invoice_a4.pdf',
|
76
|
+
background: 'background.png',
|
77
|
+
page_size: :a4
|
78
|
+
)
|
data/examples/czech_invoice.rb
CHANGED
@@ -49,6 +49,7 @@ invoice = InvoicePrinter::Document.new(
|
|
49
49
|
number: 'č. 198900000001',
|
50
50
|
provider_name: 'Petr Nový',
|
51
51
|
provider_tax_id: '56565656',
|
52
|
+
provider_tax_id2: 'CZ56565656',
|
52
53
|
provider_street: 'Rolnická',
|
53
54
|
provider_street_number: '1',
|
54
55
|
provider_postcode: '747 05',
|
@@ -59,6 +60,8 @@ invoice = InvoicePrinter::Document.new(
|
|
59
60
|
purchaser_street_number: '1',
|
60
61
|
purchaser_postcode: '747 70',
|
61
62
|
purchaser_city: 'Opava',
|
63
|
+
purchaser_tax_id: '56565656',
|
64
|
+
purchaser_tax_id2: 'CZ56565656',
|
62
65
|
issue_date: '05/03/2016',
|
63
66
|
due_date: '19/03/2016',
|
64
67
|
subtotal: 'Kč 10.000',
|
@@ -78,3 +81,12 @@ InvoicePrinter.print(
|
|
78
81
|
logo: 'prawn.png',
|
79
82
|
file_name: 'czech_invoice.pdf'
|
80
83
|
)
|
84
|
+
|
85
|
+
InvoicePrinter.print(
|
86
|
+
document: invoice,
|
87
|
+
labels: labels,
|
88
|
+
font: File.expand_path('../Overpass-Regular.ttf', __FILE__),
|
89
|
+
logo: 'prawn.png',
|
90
|
+
file_name: 'czech_invoice_a4.pdf',
|
91
|
+
page_size: :a4
|
92
|
+
)
|
@@ -82,3 +82,12 @@ InvoicePrinter.print(
|
|
82
82
|
logo: 'prawn.png',
|
83
83
|
file_name: 'international_invoice.pdf'
|
84
84
|
)
|
85
|
+
|
86
|
+
InvoicePrinter.print(
|
87
|
+
document: invoice,
|
88
|
+
labels: labels,
|
89
|
+
font: File.expand_path('../Overpass-Regular.ttf', __FILE__),
|
90
|
+
logo: 'prawn.png',
|
91
|
+
file_name: 'international_invoice_a4.pdf',
|
92
|
+
page_size: :a4
|
93
|
+
)
|
data/examples/long_invoice.rb
CHANGED
@@ -19,7 +19,7 @@ item = InvoicePrinter::Document::Item.new(
|
|
19
19
|
)
|
20
20
|
|
21
21
|
item2 = InvoicePrinter::Document::Item.new(
|
22
|
-
name: '
|
22
|
+
name: 'Consulting',
|
23
23
|
quantity: '10',
|
24
24
|
unit: 'hr',
|
25
25
|
price: '$ 30',
|
@@ -64,5 +64,13 @@ InvoicePrinter.print(
|
|
64
64
|
stamp: 'stamp.png',
|
65
65
|
logo: 'prawn.png',
|
66
66
|
file_name: 'long_invoice.pdf'
|
67
|
+
)
|
67
68
|
|
69
|
+
InvoicePrinter.print(
|
70
|
+
document: invoice,
|
71
|
+
labels: labels,
|
72
|
+
stamp: 'stamp.png',
|
73
|
+
logo: 'prawn.png',
|
74
|
+
file_name: 'long_invoice_a4.pdf',
|
75
|
+
page_size: :a4
|
68
76
|
)
|
data/examples/promo.rb
CHANGED
@@ -82,3 +82,12 @@ InvoicePrinter.print(
|
|
82
82
|
logo: 'logo.png',
|
83
83
|
file_name: 'promo.pdf'
|
84
84
|
)
|
85
|
+
|
86
|
+
InvoicePrinter.print(
|
87
|
+
document: invoice,
|
88
|
+
labels: labels,
|
89
|
+
font: File.expand_path('../Overpass-Regular.ttf', __FILE__),
|
90
|
+
logo: 'logo.png',
|
91
|
+
file_name: 'promo_a4.pdf',
|
92
|
+
page_size: :a4
|
93
|
+
)
|
data/examples/simple_invoice.rb
CHANGED
data/invoice_printer.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
23
|
spec.require_paths = ['lib']
|
24
|
+
spec.bindir = 'bin'
|
24
25
|
|
25
26
|
spec.add_dependency 'json', '~> 2.1'
|
26
27
|
spec.add_dependency 'prawn', '2.1.0'
|
@@ -27,34 +27,50 @@ module InvoicePrinter
|
|
27
27
|
:tax3,
|
28
28
|
:amount
|
29
29
|
|
30
|
-
|
30
|
+
class << self
|
31
|
+
def from_json(json)
|
32
|
+
new(
|
33
|
+
name: json['name'],
|
34
|
+
quantity: json['quantity'],
|
35
|
+
unit: json['unit'],
|
36
|
+
price: json['price'],
|
37
|
+
tax: json['tax'],
|
38
|
+
tax2: json['tax2'],
|
39
|
+
tax3: json['tax3'],
|
40
|
+
amount: json['amount']
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(name: nil,
|
31
46
|
quantity: nil,
|
32
|
-
unit:
|
33
|
-
price:
|
34
|
-
tax:
|
35
|
-
tax2:
|
36
|
-
tax3:
|
37
|
-
amount:
|
38
|
-
|
47
|
+
unit: nil,
|
48
|
+
price: nil,
|
49
|
+
tax: nil,
|
50
|
+
tax2: nil,
|
51
|
+
tax3: nil,
|
52
|
+
amount: nil)
|
53
|
+
|
54
|
+
@name = String(name)
|
39
55
|
@quantity = String(quantity)
|
40
|
-
@unit
|
41
|
-
@price
|
42
|
-
@tax
|
43
|
-
@tax2
|
44
|
-
@tax3
|
45
|
-
@amount
|
56
|
+
@unit = String(unit)
|
57
|
+
@price = String(price)
|
58
|
+
@tax = String(tax)
|
59
|
+
@tax2 = String(tax2)
|
60
|
+
@tax3 = String(tax3)
|
61
|
+
@amount = String(amount)
|
46
62
|
end
|
47
63
|
|
48
64
|
def to_h
|
49
65
|
{
|
50
|
-
'name':
|
66
|
+
'name': @name,
|
51
67
|
'quantity': @quantity,
|
52
|
-
'unit':
|
53
|
-
'price':
|
54
|
-
'tax':
|
55
|
-
'tax2':
|
56
|
-
'tax3':
|
57
|
-
'amount':
|
68
|
+
'unit': @unit,
|
69
|
+
'price': @price,
|
70
|
+
'tax': @tax,
|
71
|
+
'tax2': @tax2,
|
72
|
+
'tax3': @tax3,
|
73
|
+
'amount': @amount,
|
58
74
|
}
|
59
75
|
end
|
60
76
|
|