invoice_printer 1.2.0 → 1.3.0
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/.gitignore +0 -1
- data/Gemfile +6 -0
- data/Gemfile.lock +63 -0
- data/README.md +1 -3
- data/benchmarks/render.yml +44 -0
- data/bin/invoice_printer +29 -18
- data/docs/COMMAND_LINE.md +5 -3
- data/docs/LIBRARY.md +8 -1
- data/docs/SERVER.md +6 -2
- data/examples/complex_invoice.rb +8 -1
- data/examples/czech_invoice_long.rb +92 -0
- data/lib/invoice_printer.rb +6 -2
- data/lib/invoice_printer/document/item.rb +6 -0
- data/lib/invoice_printer/pdf_document.rb +31 -6
- data/lib/invoice_printer/server.rb +23 -15
- data/lib/invoice_printer/version.rb +1 -1
- data/test/api_test.rb +1 -1
- data/test/cli_test.rb +42 -8
- data/test/inputs_test.rb +0 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 327ecc31f48a1b7a25b413c9423c47ce335c1a1b
|
4
|
+
data.tar.gz: bb454cbed1da8260b3f73c8f6ede4db3328326e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1aa09af511628b9dba7d3d5aa3497c33f5927c0d96b55964b5cf0a9b24219582dde29e3a8d8855fc8806ab99c77b4e69f4f34ad0cda1e52a520d8a3a5f362c95
|
7
|
+
data.tar.gz: 8de19983830a6263597e6799fd6c7738c81b5ee1b73dff6e44a504ef5b80c093da19eb7cbf25f73161987a9388314972df12f9e80e43f6f69be4a81aeecadbaa
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
invoice_printer (1.2.0)
|
5
|
+
json (~> 2.1)
|
6
|
+
prawn (= 2.1.0)
|
7
|
+
prawn-layout (= 0.8.4)
|
8
|
+
puma (~> 3.9)
|
9
|
+
roda (= 3.5.0)
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
Ascii85 (1.0.3)
|
15
|
+
afm (0.2.2)
|
16
|
+
benchmark_driver (0.14.11)
|
17
|
+
benchmark_driver-output-gruff (0.3.1)
|
18
|
+
benchmark_driver (>= 0.12.0)
|
19
|
+
gruff
|
20
|
+
gruff (0.7.0)
|
21
|
+
rmagick (~> 2.13, >= 2.13.4)
|
22
|
+
hashery (2.1.2)
|
23
|
+
json (2.1.0)
|
24
|
+
minitest (5.11.3)
|
25
|
+
pdf-core (0.6.1)
|
26
|
+
pdf-inspector (1.3.0)
|
27
|
+
pdf-reader (>= 1.0, < 3.0.a)
|
28
|
+
pdf-reader (2.2.0)
|
29
|
+
Ascii85 (~> 1.0.0)
|
30
|
+
afm (~> 0.2.1)
|
31
|
+
hashery (~> 2.0)
|
32
|
+
ruby-rc4
|
33
|
+
ttfunk
|
34
|
+
prawn (2.1.0)
|
35
|
+
pdf-core (~> 0.6.1)
|
36
|
+
ttfunk (~> 1.4.0)
|
37
|
+
prawn-layout (0.8.4)
|
38
|
+
puma (3.12.0)
|
39
|
+
rack (2.0.6)
|
40
|
+
rack-test (1.1.0)
|
41
|
+
rack (>= 1.0, < 3)
|
42
|
+
rake (10.5.0)
|
43
|
+
rmagick (2.16.0)
|
44
|
+
roda (3.5.0)
|
45
|
+
rack
|
46
|
+
ruby-rc4 (0.1.5)
|
47
|
+
ttfunk (1.4.0)
|
48
|
+
|
49
|
+
PLATFORMS
|
50
|
+
ruby
|
51
|
+
|
52
|
+
DEPENDENCIES
|
53
|
+
benchmark_driver (= 0.14.11)
|
54
|
+
benchmark_driver-output-gruff
|
55
|
+
bundler (~> 1.7)
|
56
|
+
invoice_printer!
|
57
|
+
minitest
|
58
|
+
pdf-inspector
|
59
|
+
rack-test
|
60
|
+
rake (~> 10.0)
|
61
|
+
|
62
|
+
BUNDLED WITH
|
63
|
+
1.16.1
|
data/README.md
CHANGED
@@ -48,6 +48,4 @@ See more usecases in the `examples/` directory.
|
|
48
48
|
|
49
49
|
## Copyright
|
50
50
|
|
51
|
-
Copyright 2015-
|
52
|
-
|
53
|
-
Originally extracted from and created for an open source single-entry invoicing app [InvoiceBar](https://github.com/strzibny/invoicebar).
|
51
|
+
Copyright 2015-2019 © [Josef Strzibny](http://strzibny.name/). MIT licensed.
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Run the benchmark with benchmark_driver as:
|
2
|
+
#
|
3
|
+
# $ benchmark-driver benchmarks/render.yml --output gruff --runner ips -e '/path/to/bin/ruby;/path/to/bin/ruby-2.6.0 --jit'
|
4
|
+
# $ benchmark-driver benchmarks/render.yml --output compare --runner memory -e '/path/to/bin/ruby;/path/to/bin/ruby-2.6.0 --jit--jit'
|
5
|
+
loop_count: 10000
|
6
|
+
prelude: |
|
7
|
+
lib = File.expand_path('../lib', __FILE__)
|
8
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
9
|
+
require 'invoice_printer'
|
10
|
+
|
11
|
+
item = InvoicePrinter::Document::Item.new(
|
12
|
+
name: 'Programming',
|
13
|
+
quantity: '10',
|
14
|
+
unit: 'hr',
|
15
|
+
price: '$ 90',
|
16
|
+
amount: '$ 900'
|
17
|
+
)
|
18
|
+
|
19
|
+
invoice = InvoicePrinter::Document.new(
|
20
|
+
number: 'NO. 198900000001',
|
21
|
+
provider_name: 'John White',
|
22
|
+
provider_street: '5th Avenue',
|
23
|
+
provider_street_number: '1',
|
24
|
+
provider_postcode: '747 05',
|
25
|
+
provider_city: 'NYC',
|
26
|
+
purchaser_name: 'Will Black',
|
27
|
+
purchaser_street: '7th Avenue',
|
28
|
+
purchaser_street_number: '1',
|
29
|
+
purchaser_postcode: '747 70',
|
30
|
+
purchaser_city: 'NYC',
|
31
|
+
issue_date: '05/03/2016',
|
32
|
+
due_date: '19/03/2016',
|
33
|
+
total: '$ 900',
|
34
|
+
bank_account_number: '156546546465',
|
35
|
+
items: [item],
|
36
|
+
note: 'This is a note at the end.'
|
37
|
+
)
|
38
|
+
benchmark:
|
39
|
+
render: |
|
40
|
+
InvoicePrinter.print(
|
41
|
+
document: invoice,
|
42
|
+
file_name: 'simple_invoice_a4.pdf',
|
43
|
+
page_size: :a4
|
44
|
+
)
|
data/bin/invoice_printer
CHANGED
@@ -16,11 +16,12 @@ def show_help
|
|
16
16
|
|
17
17
|
Options:
|
18
18
|
|
19
|
-
-l, --labels labels as JSON
|
20
19
|
-d, --document document as JSON
|
20
|
+
-l, --labels labels as JSON
|
21
|
+
--font path to font
|
21
22
|
-s, --stamp path to stamp
|
22
23
|
--logo path to logotype
|
23
|
-
|
24
|
+
--background path to background image
|
24
25
|
--page-size letter or a4 (letter is the default)
|
25
26
|
-f, --filename output path
|
26
27
|
-r, --render directly render PDF stream (filename option will be ignored)
|
@@ -33,12 +34,16 @@ end
|
|
33
34
|
options = {}
|
34
35
|
|
35
36
|
parser = OptionParser.new do|opts|
|
37
|
+
opts.on('-d', '--document JSON') do |json|
|
38
|
+
options[:document] = json
|
39
|
+
end
|
40
|
+
|
36
41
|
opts.on('-l', '--labels JSON') do |json|
|
37
42
|
options[:labels] = json
|
38
43
|
end
|
39
44
|
|
40
|
-
opts.on('
|
41
|
-
options[:
|
45
|
+
opts.on('--font PATH') do |path|
|
46
|
+
options[:font] = path
|
42
47
|
end
|
43
48
|
|
44
49
|
opts.on('-s', '--stamp PATH') do |path|
|
@@ -49,8 +54,8 @@ parser = OptionParser.new do|opts|
|
|
49
54
|
options[:logo] = path
|
50
55
|
end
|
51
56
|
|
52
|
-
opts.on('--
|
53
|
-
options[:
|
57
|
+
opts.on('--background PATH') do |path|
|
58
|
+
options[:background] = path
|
54
59
|
end
|
55
60
|
|
56
61
|
opts.on('--page-size OPTION') do |option|
|
@@ -89,8 +94,10 @@ begin
|
|
89
94
|
raise '--document not provided' unless options[:document]
|
90
95
|
|
91
96
|
begin
|
92
|
-
json = JSON.parse
|
97
|
+
json = JSON.parse(options[:document])
|
93
98
|
document = InvoicePrinter::Document.from_json(json)
|
99
|
+
labels = options[:labels] ? JSON.parse(options[:labels]) : {}
|
100
|
+
|
94
101
|
rescue => e
|
95
102
|
STDERR.puts "ERROR: parsing JSON failed. Invalid JSON?"
|
96
103
|
|
@@ -105,11 +112,13 @@ begin
|
|
105
112
|
|
106
113
|
if options[:render]
|
107
114
|
stream = InvoicePrinter.render(
|
108
|
-
document:
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
115
|
+
document: document,
|
116
|
+
labels: labels,
|
117
|
+
font: options[:font],
|
118
|
+
stamp: options[:stamp],
|
119
|
+
logo: options[:logo],
|
120
|
+
background: options[:background],
|
121
|
+
page_size: options[:page_size]
|
113
122
|
)
|
114
123
|
|
115
124
|
puts stream
|
@@ -117,12 +126,14 @@ begin
|
|
117
126
|
raise '--filename not provided. Use --render if you with to render to STDOUT.' unless options[:filename]
|
118
127
|
|
119
128
|
InvoicePrinter.print(
|
120
|
-
document:
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
129
|
+
document: document,
|
130
|
+
labels: labels,
|
131
|
+
font: options[:font],
|
132
|
+
stamp: options[:stamp],
|
133
|
+
logo: options[:logo],
|
134
|
+
file_name: options[:filename],
|
135
|
+
background: options[:background],
|
136
|
+
page_size: options[:page_size]
|
126
137
|
)
|
127
138
|
end
|
128
139
|
rescue => e
|
data/docs/COMMAND_LINE.md
CHANGED
@@ -10,12 +10,14 @@ Usage: invoice_printer [options]
|
|
10
10
|
|
11
11
|
Options:
|
12
12
|
|
13
|
-
-l, --labels labels as JSON
|
14
13
|
-d, --document document as JSON
|
14
|
+
-l, --labels labels as JSON
|
15
|
+
--font path to font
|
15
16
|
-s, --stamp path to stamp
|
16
17
|
--logo path to logotype
|
17
|
-
|
18
|
-
--
|
18
|
+
--background path to background image
|
19
|
+
--page-size letter or a4 (letter is the default)
|
19
20
|
-f, --filename output path
|
20
21
|
-r, --render directly render PDF stream (filename option will be ignored)
|
22
|
+
|
21
23
|
```
|
data/docs/LIBRARY.md
CHANGED
@@ -73,6 +73,10 @@ invoice = InvoicePrinter::Document.new(
|
|
73
73
|
)
|
74
74
|
```
|
75
75
|
|
76
|
+
**Note**: There is `variable` field that can be used for any
|
77
|
+
extra column. `tax2` and `tax3` for more complex taxes are
|
78
|
+
available as well.
|
79
|
+
|
76
80
|
### Ruby on Rails
|
77
81
|
|
78
82
|
If you want to use InvoicePrinter for printing PDF documents directly from Rails
|
@@ -170,6 +174,7 @@ InvoicePrinter.labels = {
|
|
170
174
|
issue_date: 'Issue date',
|
171
175
|
due_date: 'Due date',
|
172
176
|
item: 'Item',
|
177
|
+
variable: '',
|
173
178
|
quantity: 'Quantity',
|
174
179
|
unit: 'Unit',
|
175
180
|
price_per_item: 'Price per item',
|
@@ -181,6 +186,7 @@ InvoicePrinter.labels = {
|
|
181
186
|
total: 'Total'
|
182
187
|
}
|
183
188
|
```
|
189
|
+
**Note:** `variable` fields lack default label. You should provide one.
|
184
190
|
|
185
191
|
You can also use sublabels feature to provide the document in two languages:
|
186
192
|
|
@@ -201,6 +207,7 @@ sublabels = {
|
|
201
207
|
issue_date: 'Datum vydání',
|
202
208
|
due_date: 'Datum splatnosti',
|
203
209
|
item: 'Položka',
|
210
|
+
variable: '',
|
204
211
|
quantity: 'Počet',
|
205
212
|
unit: 'MJ',
|
206
213
|
price_per_item: 'Cena za položku',
|
@@ -215,7 +222,7 @@ labels.merge!({ sublabels: sublabels })
|
|
215
222
|
...
|
216
223
|
```
|
217
224
|
|
218
|
-
Now the document will have
|
225
|
+
Now the document will have little sublabels next to the original labels in Czech.
|
219
226
|
|
220
227
|
### Font
|
221
228
|
|
data/docs/SERVER.md
CHANGED
@@ -46,9 +46,11 @@ Options:
|
|
46
46
|
|
47
47
|
- `document` - JSON representation of the document
|
48
48
|
- `labels` - JSON for labels
|
49
|
+
- `font` - path to font file
|
49
50
|
- `stamp` - path to stamp file
|
50
51
|
- `logo` - path to logotype file
|
51
|
-
- `
|
52
|
+
- `background` - path to background file
|
53
|
+
- `page_size` - letter or A4 page size
|
52
54
|
|
53
55
|
On success a `200` response is returned:
|
54
56
|
|
@@ -84,9 +86,11 @@ Options:
|
|
84
86
|
|
85
87
|
- `document` - JSON representation of the document
|
86
88
|
- `labels` - JSON for labels
|
89
|
+
- `font` - path to font file
|
87
90
|
- `stamp` - path to stamp file
|
88
91
|
- `logo` - path to logotype file
|
89
|
-
- `
|
92
|
+
- `background` - path to background file
|
93
|
+
- `page_size` - letter or A4 page size
|
90
94
|
- `filename` - path for saving the generated output PDF
|
91
95
|
|
92
96
|
On success a `200` response is returned:
|
data/examples/complex_invoice.rb
CHANGED
@@ -6,11 +6,16 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
6
|
require 'invoice_printer'
|
7
7
|
|
8
8
|
labels = {
|
9
|
-
|
9
|
+
variable: 'Date',
|
10
|
+
tax: '10% VAT',
|
11
|
+
sublabels: {
|
12
|
+
variable: 'of work done'
|
13
|
+
}
|
10
14
|
}
|
11
15
|
|
12
16
|
item = InvoicePrinter::Document::Item.new(
|
13
17
|
name: 'Programming',
|
18
|
+
variable: 'June 2018',
|
14
19
|
quantity: '10',
|
15
20
|
unit: 'hr',
|
16
21
|
price: '$ 60',
|
@@ -20,6 +25,7 @@ item = InvoicePrinter::Document::Item.new(
|
|
20
25
|
|
21
26
|
item2 = InvoicePrinter::Document::Item.new(
|
22
27
|
name: 'Consulting',
|
28
|
+
variable: 'July 2018',
|
23
29
|
quantity: '10',
|
24
30
|
unit: 'hr',
|
25
31
|
price: '$ 30',
|
@@ -29,6 +35,7 @@ item2 = InvoicePrinter::Document::Item.new(
|
|
29
35
|
|
30
36
|
item3 = InvoicePrinter::Document::Item.new(
|
31
37
|
name: 'Support',
|
38
|
+
variable: 'September 2018',
|
32
39
|
quantity: '20',
|
33
40
|
unit: 'hr',
|
34
41
|
price: '$ 15',
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This is an example of a Czech invoice.
|
3
|
+
#
|
4
|
+
# Due to the special characters it requires Overpass-Regular.ttf font to be
|
5
|
+
# present in this directory.
|
6
|
+
|
7
|
+
lib = File.expand_path('../../lib', __FILE__)
|
8
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
9
|
+
require 'invoice_printer'
|
10
|
+
|
11
|
+
labels = {
|
12
|
+
name: 'Faktura',
|
13
|
+
provider: 'Prodejce',
|
14
|
+
purchaser: 'Kupující',
|
15
|
+
tax_id: 'IČ',
|
16
|
+
tax_id2: 'DIČ',
|
17
|
+
payment: 'Forma úhrady',
|
18
|
+
payment_by_transfer: 'Platba na následující účet:',
|
19
|
+
account_number: 'Číslo účtu',
|
20
|
+
issue_date: 'Datum vydání',
|
21
|
+
due_date: 'Datum splatnosti',
|
22
|
+
item: 'Položka',
|
23
|
+
quantity: 'Počet',
|
24
|
+
unit: 'MJ',
|
25
|
+
price_per_item: 'Cena za položku',
|
26
|
+
amount: 'Celkem bez daně',
|
27
|
+
subtotal: 'Cena bez daně',
|
28
|
+
tax: 'DPH 21 %',
|
29
|
+
total: 'Celkem'
|
30
|
+
}
|
31
|
+
|
32
|
+
first_item = InvoicePrinter::Document::Item.new(
|
33
|
+
name: 'Konzultace',
|
34
|
+
quantity: '2',
|
35
|
+
unit: 'hod',
|
36
|
+
price: 'Kč 500',
|
37
|
+
amount: 'Kč 1.000'
|
38
|
+
)
|
39
|
+
|
40
|
+
second_item = InvoicePrinter::Document::Item.new(
|
41
|
+
name: 'Programování',
|
42
|
+
quantity: '10',
|
43
|
+
unit: 'hod',
|
44
|
+
price: 'Kč 900',
|
45
|
+
amount: 'Kč 9.000'
|
46
|
+
)
|
47
|
+
|
48
|
+
invoice = InvoicePrinter::Document.new(
|
49
|
+
number: 'č. 198900000001',
|
50
|
+
provider_name: 'Petr Nový',
|
51
|
+
provider_tax_id: '56565656',
|
52
|
+
provider_tax_id2: 'CZ56565656',
|
53
|
+
provider_street: 'Rolnická',
|
54
|
+
provider_street_number: '1',
|
55
|
+
provider_postcode: '747 05',
|
56
|
+
provider_city: 'Opava',
|
57
|
+
provider_city_part: 'Kateřinky',
|
58
|
+
purchaser_name: 'Adam Černý',
|
59
|
+
purchaser_street: 'Ostravská',
|
60
|
+
purchaser_street_number: '1',
|
61
|
+
purchaser_postcode: '747 70',
|
62
|
+
purchaser_city: 'Opava',
|
63
|
+
purchaser_tax_id: '56565656',
|
64
|
+
purchaser_tax_id2: 'CZ56565656',
|
65
|
+
issue_date: '05/03/2016',
|
66
|
+
due_date: '19/03/2016',
|
67
|
+
subtotal: 'Kč 10.000',
|
68
|
+
tax: 'Kč 2.100',
|
69
|
+
total: 'Kč 12.100,-',
|
70
|
+
bank_account_number: '156546546465',
|
71
|
+
account_iban: 'IBAN464545645',
|
72
|
+
account_swift: 'SWIFT5456',
|
73
|
+
items: [first_item, second_item] * 20,
|
74
|
+
note: 'Osoba je zapsána v živnostenském rejstříku.'
|
75
|
+
)
|
76
|
+
|
77
|
+
InvoicePrinter.print(
|
78
|
+
document: invoice,
|
79
|
+
labels: labels,
|
80
|
+
font: File.expand_path('../Overpass-Regular.ttf', __FILE__),
|
81
|
+
logo: 'prawn.png',
|
82
|
+
file_name: 'czech_invoice_long.pdf'
|
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_long_a4.pdf',
|
91
|
+
page_size: :a4
|
92
|
+
)
|
data/lib/invoice_printer.rb
CHANGED
@@ -62,12 +62,14 @@ module InvoicePrinter
|
|
62
62
|
# Print the given InvoicePrinter::Document to PDF file named +file_name+
|
63
63
|
#
|
64
64
|
# document - InvoicePrinter::Document object
|
65
|
-
# file_name - output file
|
66
65
|
# labels - labels to override
|
67
66
|
# font - font file to use
|
68
67
|
# stamp - stamp & signature (image)
|
69
68
|
# logo - logotype (image)
|
70
|
-
|
69
|
+
# background - background (image)
|
70
|
+
# page_size - :letter or :a4
|
71
|
+
# file_name - output file
|
72
|
+
def self.print(document:, labels: {}, font: nil, stamp: nil, logo: nil, background: nil, page_size: :letter, file_name:)
|
71
73
|
PDFDocument.new(
|
72
74
|
document: document,
|
73
75
|
labels: labels,
|
@@ -86,6 +88,8 @@ module InvoicePrinter
|
|
86
88
|
# font - font file to use
|
87
89
|
# stamp - stamp & signature (image)
|
88
90
|
# logo - logotype (image)
|
91
|
+
# background - background (image)
|
92
|
+
# page_size - :letter or :a4
|
89
93
|
def self.render(document:, labels: {}, font: nil, stamp: nil, logo: nil, background: nil, page_size: :letter)
|
90
94
|
PDFDocument.new(
|
91
95
|
document: document,
|
@@ -6,6 +6,7 @@ module InvoicePrinter
|
|
6
6
|
#
|
7
7
|
# item = InvoicePrinter::Document::Item.new(
|
8
8
|
# name: 'UX consultation',
|
9
|
+
# variable: 'June 2008',
|
9
10
|
# quantity: '4',
|
10
11
|
# unit: 'hours',
|
11
12
|
# price: '$ 25',
|
@@ -17,6 +18,7 @@ module InvoicePrinter
|
|
17
18
|
# but this is not enforced.
|
18
19
|
class Item
|
19
20
|
attr_reader :name,
|
21
|
+
:variable, # for anything required
|
20
22
|
:quantity,
|
21
23
|
:unit,
|
22
24
|
:price,
|
@@ -29,6 +31,7 @@ module InvoicePrinter
|
|
29
31
|
def from_json(json)
|
30
32
|
new(
|
31
33
|
name: json['name'],
|
34
|
+
variable: json['variable'],
|
32
35
|
quantity: json['quantity'],
|
33
36
|
unit: json['unit'],
|
34
37
|
price: json['price'],
|
@@ -41,6 +44,7 @@ module InvoicePrinter
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def initialize(name: nil,
|
47
|
+
variable: nil,
|
44
48
|
quantity: nil,
|
45
49
|
unit: nil,
|
46
50
|
price: nil,
|
@@ -50,6 +54,7 @@ module InvoicePrinter
|
|
50
54
|
amount: nil)
|
51
55
|
|
52
56
|
@name = String(name)
|
57
|
+
@variable = String(variable)
|
53
58
|
@quantity = String(quantity)
|
54
59
|
@unit = String(unit)
|
55
60
|
@price = String(price)
|
@@ -62,6 +67,7 @@ module InvoicePrinter
|
|
62
67
|
def to_h
|
63
68
|
{
|
64
69
|
'name': @name,
|
70
|
+
'variable': @variable,
|
65
71
|
'quantity': @quantity,
|
66
72
|
'unit': @unit,
|
67
73
|
'price': @price,
|
@@ -37,6 +37,7 @@ module InvoicePrinter
|
|
37
37
|
issue_date: 'Issue date',
|
38
38
|
due_date: 'Due date',
|
39
39
|
item: 'Item',
|
40
|
+
variable: '',
|
40
41
|
quantity: 'Quantity',
|
41
42
|
unit: 'Unit',
|
42
43
|
price_per_item: 'Price per item',
|
@@ -46,7 +47,7 @@ module InvoicePrinter
|
|
46
47
|
amount: 'Amount',
|
47
48
|
subtotal: 'Subtotal',
|
48
49
|
total: 'Total',
|
49
|
-
sublabels: {}
|
50
|
+
sublabels: {}
|
50
51
|
}
|
51
52
|
|
52
53
|
PageSize = Struct.new(:name, :width, :height)
|
@@ -66,12 +67,12 @@ module InvoicePrinter
|
|
66
67
|
|
67
68
|
def initialize(document: Document.new, labels: {}, font: nil, stamp: nil, logo: nil, background: nil, page_size: :letter)
|
68
69
|
@document = document
|
69
|
-
@labels =
|
70
|
-
@page_size = PAGE_SIZES[page_size.to_sym]
|
71
|
-
@pdf = Prawn::Document.new(background: background, page_size: @page_size.name)
|
70
|
+
@labels = merge_custom_labels(labels)
|
72
71
|
@font = font
|
73
72
|
@stamp = stamp
|
74
73
|
@logo = logo
|
74
|
+
@page_size = page_size ? PAGE_SIZES[page_size.to_sym] : PAGE_SIZES[:letter]
|
75
|
+
@pdf = Prawn::Document.new(background: background, page_size: @page_size.name)
|
75
76
|
|
76
77
|
raise InvalidInput, 'document is not a type of InvoicePrinter::Document' \
|
77
78
|
unless @document.is_a?(InvoicePrinter::Document)
|
@@ -602,8 +603,9 @@ module InvoicePrinter
|
|
602
603
|
# |x | 2| hr| $2| $1| $4|
|
603
604
|
# =================================================================
|
604
605
|
#
|
606
|
+
# variable (2nd position), tax2 and tax3 (after tax) fields can be added
|
607
|
+
# as well if necessary. variable does not come with any default label.
|
605
608
|
# If a specific column miss data, it's omittted.
|
606
|
-
# tax2 and tax3 fields can be added as well if necessary.
|
607
609
|
#
|
608
610
|
# Using sublabels one can change the table to look as:
|
609
611
|
#
|
@@ -632,7 +634,8 @@ module InvoicePrinter
|
|
632
634
|
4 => :right,
|
633
635
|
5 => :right,
|
634
636
|
6 => :right,
|
635
|
-
7 => :right
|
637
|
+
7 => :right,
|
638
|
+
8 => :right
|
636
639
|
},
|
637
640
|
font_size: 10
|
638
641
|
}
|
@@ -645,6 +648,7 @@ module InvoicePrinter
|
|
645
648
|
items_params = {}
|
646
649
|
@document.items.each do |item|
|
647
650
|
items_params[:names] = true unless item.name.empty?
|
651
|
+
items_params[:variables] = true unless item.variable.empty?
|
648
652
|
items_params[:quantities] = true unless item.quantity.empty?
|
649
653
|
items_params[:units] = true unless item.unit.empty?
|
650
654
|
items_params[:prices] = true unless item.price.empty?
|
@@ -661,6 +665,7 @@ module InvoicePrinter
|
|
661
665
|
@document.items.map do |item|
|
662
666
|
line = []
|
663
667
|
line << item.name if items_params[:names]
|
668
|
+
line << item.variable if items_params[:variables]
|
664
669
|
line << item.quantity if items_params[:quantities]
|
665
670
|
line << item.unit if items_params[:units]
|
666
671
|
line << item.price if items_params[:prices]
|
@@ -676,6 +681,7 @@ module InvoicePrinter
|
|
676
681
|
def build_items_header(items_params)
|
677
682
|
headers = []
|
678
683
|
headers << { text: label_with_sublabel(:item) } if items_params[:names]
|
684
|
+
headers << { text: label_with_sublabel(:variable) } if items_params[:variables]
|
679
685
|
headers << { text: label_with_sublabel(:quantity) } if items_params[:quantities]
|
680
686
|
headers << { text: label_with_sublabel(:unit) } if items_params[:units]
|
681
687
|
headers << { text: label_with_sublabel(:price_per_item) } if items_params[:prices]
|
@@ -831,5 +837,24 @@ module InvoicePrinter
|
|
831
837
|
width_ratio = value / PAGE_SIZES[:letter].height
|
832
838
|
width_ratio * @page_size.height
|
833
839
|
end
|
840
|
+
|
841
|
+
def merge_custom_labels(labels = {})
|
842
|
+
custom_labels = if labels
|
843
|
+
hash_keys_to_symbols(labels)
|
844
|
+
else
|
845
|
+
{}
|
846
|
+
end
|
847
|
+
|
848
|
+
PDFDocument.labels.merge(custom_labels)
|
849
|
+
end
|
850
|
+
|
851
|
+
def hash_keys_to_symbols(value)
|
852
|
+
return value unless value.is_a? Hash
|
853
|
+
|
854
|
+
value.inject({}) do |memo, (k, v)|
|
855
|
+
memo[k.to_sym] = hash_keys_to_symbols(v)
|
856
|
+
memo
|
857
|
+
end
|
858
|
+
end
|
834
859
|
end
|
835
860
|
end
|
@@ -16,11 +16,13 @@ class InvoicePrinter::Server < Roda
|
|
16
16
|
r.halt
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
document = params[:document]
|
20
|
+
labels = params[:labels]
|
21
|
+
font = params[:font]
|
22
|
+
stamp = params[:stamp]
|
23
|
+
logo = params[:logo]
|
24
|
+
background = params[:background]
|
25
|
+
page_size = params[:page_size]
|
24
26
|
else
|
25
27
|
response.status = 400
|
26
28
|
response.write({ result: 'error', error: 'No JSON. Did you set Content-Type to application/json?' }.to_json)
|
@@ -46,11 +48,14 @@ class InvoicePrinter::Server < Roda
|
|
46
48
|
|
47
49
|
begin
|
48
50
|
InvoicePrinter.print(
|
49
|
-
document:
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
document: document,
|
52
|
+
labels: labels,
|
53
|
+
font: font,
|
54
|
+
stamp: stamp,
|
55
|
+
logo: logo,
|
56
|
+
background: background,
|
57
|
+
page_size: page_size,
|
58
|
+
file_name: filename
|
54
59
|
)
|
55
60
|
|
56
61
|
{ result: 'ok', path: filename }.to_json
|
@@ -65,10 +70,13 @@ class InvoicePrinter::Server < Roda
|
|
65
70
|
r.post 'render' do
|
66
71
|
begin
|
67
72
|
stream = InvoicePrinter.render(
|
68
|
-
document:
|
69
|
-
|
70
|
-
|
71
|
-
|
73
|
+
document: document,
|
74
|
+
labels: labels,
|
75
|
+
font: font,
|
76
|
+
stamp: stamp,
|
77
|
+
logo: logo,
|
78
|
+
background: background,
|
79
|
+
page_size: page_size
|
72
80
|
)
|
73
81
|
|
74
82
|
{ result: 'ok', data: Base64.encode64(stream) }.to_json
|
@@ -79,4 +87,4 @@ class InvoicePrinter::Server < Roda
|
|
79
87
|
end
|
80
88
|
end
|
81
89
|
end
|
82
|
-
end
|
90
|
+
end
|
data/test/api_test.rb
CHANGED
data/test/cli_test.rb
CHANGED
@@ -15,7 +15,12 @@ class CLITest < Minitest::Test
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_print_to_file
|
18
|
-
command = "bin/invoice_printer
|
18
|
+
command = "bin/invoice_printer " +
|
19
|
+
"--document '#{@invoice_as_json}' " +
|
20
|
+
"--labels '#{labels_hash.to_json}' " +
|
21
|
+
"--logo '#{logo_path}' " +
|
22
|
+
"--background '#{background_path}' " +
|
23
|
+
"--filename #{@output_path}"
|
19
24
|
|
20
25
|
exit_status, command_stdout, command_stderr = nil
|
21
26
|
|
@@ -33,9 +38,12 @@ class CLITest < Minitest::Test
|
|
33
38
|
expected_pdf_path = "#{@output_path}.expected_letter"
|
34
39
|
|
35
40
|
InvoicePrinter.print(
|
36
|
-
document:
|
37
|
-
|
38
|
-
|
41
|
+
document: @invoice,
|
42
|
+
labels: labels_hash,
|
43
|
+
logo: logo_path,
|
44
|
+
background: background_path,
|
45
|
+
page_size: :letter,
|
46
|
+
file_name: expected_pdf_path
|
39
47
|
)
|
40
48
|
|
41
49
|
similarity = (File.read(expected_pdf_path) == File.read(@output_path))
|
@@ -45,7 +53,13 @@ class CLITest < Minitest::Test
|
|
45
53
|
end
|
46
54
|
|
47
55
|
def test_print_to_file_a4
|
48
|
-
command = "bin/invoice_printer
|
56
|
+
command = "bin/invoice_printer " +
|
57
|
+
"--document '#{@invoice_as_json}' " +
|
58
|
+
"--labels '#{labels_hash.to_json}' " +
|
59
|
+
"--logo '#{logo_path}' " +
|
60
|
+
"--background '#{background_path}' " +
|
61
|
+
"--page-size a4 " +
|
62
|
+
"--filename #{@output_path}"
|
49
63
|
|
50
64
|
exit_status, command_stdout, command_stderr = nil
|
51
65
|
|
@@ -63,9 +77,12 @@ class CLITest < Minitest::Test
|
|
63
77
|
expected_pdf_path = "#{@output_path}.expected_a4"
|
64
78
|
|
65
79
|
InvoicePrinter.print(
|
66
|
-
document:
|
67
|
-
|
68
|
-
|
80
|
+
document: @invoice,
|
81
|
+
labels: labels_hash,
|
82
|
+
logo: logo_path,
|
83
|
+
background: background_path,
|
84
|
+
page_size: :a4,
|
85
|
+
file_name: expected_pdf_path
|
69
86
|
)
|
70
87
|
|
71
88
|
similarity = (File.read(expected_pdf_path) == File.read(@output_path))
|
@@ -73,4 +90,21 @@ class CLITest < Minitest::Test
|
|
73
90
|
|
74
91
|
File.unlink expected_pdf_path
|
75
92
|
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def labels_hash
|
97
|
+
sublabels = { sublabels: { name: 'Sublabel name' } }
|
98
|
+
|
99
|
+
{ provider: 'Default Provider',
|
100
|
+
purchaser: 'Default Purchaser' }.merge!(sublabels)
|
101
|
+
end
|
102
|
+
|
103
|
+
def logo_path
|
104
|
+
File.expand_path('../../examples/logo.png', __FILE__)
|
105
|
+
end
|
106
|
+
|
107
|
+
def background_path
|
108
|
+
File.expand_path('../../examples/background.png', __FILE__)
|
109
|
+
end
|
76
110
|
end
|
data/test/inputs_test.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: invoice_printer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josef Strzibny
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -119,10 +119,12 @@ extra_rdoc_files: []
|
|
119
119
|
files:
|
120
120
|
- ".gitignore"
|
121
121
|
- Gemfile
|
122
|
+
- Gemfile.lock
|
122
123
|
- LICENSE.txt
|
123
124
|
- README.md
|
124
125
|
- Rakefile
|
125
126
|
- assets/logo.png
|
127
|
+
- benchmarks/render.yml
|
126
128
|
- bin/invoice_printer
|
127
129
|
- bin/invoice_printer_server
|
128
130
|
- docs/COMMAND_LINE.md
|
@@ -133,6 +135,7 @@ files:
|
|
133
135
|
- examples/clients/node.js
|
134
136
|
- examples/complex_invoice.rb
|
135
137
|
- examples/czech_invoice.rb
|
138
|
+
- examples/czech_invoice_long.rb
|
136
139
|
- examples/international_invoice.rb
|
137
140
|
- examples/logo.png
|
138
141
|
- examples/long_invoice.rb
|