invoice_printer 1.1.0 → 1.2.0.alpha1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7267267a6ecf63b5859b996d9e559f15a47c1c99
4
- data.tar.gz: 0526101ea5a21143caca53fa8e6d6cde1a6a1907
3
+ metadata.gz: 8abb69910f861a930c17f4a59b2f00c5c7530556
4
+ data.tar.gz: b4b108dc6b68db259a24a9fca49db4eb78ab0a9b
5
5
  SHA512:
6
- metadata.gz: cf3f24eb77ac24d9fad50aff96b215289088f4a1d56f5d7aa32d29bbfeaf527301b46275853538da488dfc02eb925b3fc5d8fd4da59cdb0fd06049cdc8cd6ca5
7
- data.tar.gz: 9746c2ce166bbdbe5b81668345fde6ade8897c76872e175849e70be9f69f4a9c630ddf5933bb17cee4412cc14bb6527117f2e8f8a0366a42bd6463bed6ce2a86
6
+ metadata.gz: 126cddc2f3e5cf893cf94f18abdd2b7966399f993f0ff29826adff0d1b77f26ea08c8de3feb6b036c4c5b12027ebbd90b41ef54f3b1b9fecc6e80bc88c9a530c
7
+ data.tar.gz: db9b42fb49fa1c303c0f6a6abeb1408cd2940960efc529107372f9581d21248eb982ce1f3eaf4e4bee23ce6d3bb759972aeabfd3f4611aec42cd295d961968e2
data/Gemfile CHANGED
@@ -6,4 +6,5 @@ gemspec
6
6
  group :test do
7
7
  gem 'pdf-inspector'
8
8
  gem 'minitest'
9
+ gem 'rack-test', require: 'rack/test'
9
10
  end
data/README.md CHANGED
@@ -1,12 +1,22 @@
1
- <a href="http://strzibny.github.io/invoice_printer/">
2
- <img src="./docs/web/logo.png" width="300" />
3
- </a>
1
+ <img src="./assets/logo.png" width="300" />
4
2
 
5
3
  &nbsp;
6
4
 
7
- Super simple PDF invoicing in pure Ruby
5
+ **Super simple PDF invoicing.** InvoicePrinter is a server, command line program and pure Ruby library to generate PDF invoices in no time. You can use Ruby or JSON as the invoice representation to build the final PDF.
8
6
 
9
- InvoicePrinter is a Ruby library and a command line program. You can use Ruby or JSON to build the final PDF.
7
+ ## Philosophy
8
+
9
+ - **Simple**, no styling required, no calculation, no money formatting (bring your own)
10
+ - **Pure Ruby**, no dependency on system libraries or browsers
11
+ - **Fast**, so you can render invoice on the fly during request
12
+
13
+ ## Examples
14
+
15
+ | Simple invoice |
16
+ | -------------- |
17
+ | <a href="https://github.com/strzibny/invoice_printer/raw/master/examples/promo.pdf"><img src="./examples/picture.jpg" width="180" /></a>|
18
+
19
+ See more usecases in the `examples/` directory.
10
20
 
11
21
  ## Features
12
22
 
@@ -26,296 +36,18 @@ InvoicePrinter is a Ruby library and a command line program. You can use Ruby or
26
36
  - Note
27
37
  - JSON format
28
38
  - CLI
39
+ - Server
29
40
  - Well tested
30
41
 
31
- ## Example
32
-
33
- | Simple invoice |
34
- | -------------- |
35
- | <a href="https://github.com/strzibny/invoice_printer/raw/master/examples/promo.pdf"><img src="./examples/picture.jpg" width="180" /></a>|
36
-
37
- See more usecases in the `examples/` directory.
38
-
39
- ## Installation
40
-
41
- Add this line to your application's Gemfile:
42
-
43
- ```ruby
44
- gem 'invoice_printer'
45
- ```
46
-
47
- And then execute:
48
-
49
- $ bundle
50
-
51
- Or install it yourself as:
52
-
53
- $ gem install invoice_printer
54
-
55
- ## Usage
56
-
57
- The simplest way how to create your invoice PDF is to create an invoice object
58
- and pass it to printer:
59
-
60
- ```ruby
61
- item = InvoicePrinter::Document::Item.new(
62
- ...
63
- )
64
-
65
- invoice = InvoicePrinter::Document.new(
66
- ...
67
- items: [item, ...]
68
- )
69
-
70
- InvoicePrinter.print(
71
- document: invoice,
72
- file_name: 'invoice.pdf'
73
- )
74
-
75
- # Or render PDF directly
76
- InvoicePrinter.render(
77
- document: invoice
78
- )
79
- ```
80
-
81
- Here is an full example for creating the document object:
82
-
83
- ```ruby
84
- item = InvoicePrinter::Document::Item.new(
85
- name: 'Web consultation',
86
- quantity: nil,
87
- unit: 'hours',
88
- price: '$ 25',
89
- tax: '$ 1',
90
- amount: '$ 100'
91
- )
92
-
93
- invoice = InvoicePrinter::Document.new(
94
- number: '201604030001',
95
- provider_name: 'Business s.r.o.',
96
- provider_tax_id: '56565656',
97
- provider_tax_id2: '465454',
98
- provider_street: 'Rolnicka',
99
- provider_street_number: '1',
100
- provider_postcode: '747 05',
101
- provider_city: 'Opava',
102
- provider_city_part: 'Katerinky',
103
- provider_extra_address_line: 'Czech Republic',
104
- purchaser_name: 'Adam',
105
- purchaser_tax_id: '',
106
- purchaser_tax_id2: '',
107
- purchaser_street: 'Ostravska',
108
- purchaser_street_number: '1',
109
- purchaser_postcode: '747 70',
110
- purchaser_city: 'Opava',
111
- purchaser_city_part: '',
112
- purchaser_extra_address_line: '',
113
- issue_date: '19/03/3939',
114
- due_date: '19/03/3939',
115
- subtotal: '175',
116
- tax: '5',
117
- tax2: '10',
118
- tax3: '20',
119
- total: '$ 200',
120
- bank_account_number: '156546546465',
121
- account_iban: 'IBAN464545645',
122
- account_swift: 'SWIFT5456',
123
- items: [item],
124
- note: 'A note...'
125
- )
126
- ```
127
-
128
- ### Ruby on Rails
129
-
130
- If you want to use InvoicePrinter for printing PDF documents directly from Rails
131
- actions, you can:
132
-
133
- ```ruby
134
- # GET /invoices/1
135
- def show
136
- invoice = InvoicePrinter::Document.new(...)
137
-
138
- respond_to do |format|
139
- format.pdf {
140
- @pdf = InvoicePrinter.render(
141
- document: invoice
142
- )
143
- send_data @pdf, type: 'application/pdf', disposition: 'inline'
144
- }
145
- end
146
- end
147
- ```
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
-
188
- ## Customization
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
-
206
- ### Localization
207
-
208
- To localize your documents you can set both global defaults or instance
209
- overrides:
210
-
211
- ```ruby
212
- InvoicePrinter.labels = {
213
- provider: 'Dodavatel'
214
- }
215
-
216
- labels = {
217
- purchaser: 'Customer'
218
- }
219
-
220
- InvoicePrinter.print(
221
- document: invoice,
222
- labels: labels,
223
- file_name: 'invoice.pdf'
224
- )
225
- ```
226
-
227
- Here is the full list of labels to configure. You can paste and edit this block
228
- to `initializers/invoice_printer.rb` if you are using Rails.
229
-
230
- ```ruby
231
- InvoicePrinter.labels = {
232
- name: 'Invoice',
233
- provider: 'Provider',
234
- purchaser: 'Purchaser',
235
- tax_id: 'Identification number',
236
- tax_id2: 'Identification number',
237
- payment: 'Payment',
238
- payment_by_transfer: 'Payment by bank transfer on the account below:',
239
- payment_in_cash: 'Payment in cash',
240
- account_number: 'Account NO',
241
- swift: 'SWIFT',
242
- iban: 'IBAN',
243
- issue_date: 'Issue date',
244
- due_date: 'Due date',
245
- item: 'Item',
246
- quantity: 'Quantity',
247
- unit: 'Unit',
248
- price_per_item: 'Price per item',
249
- amount: 'Amount',
250
- tax: 'Tax',
251
- tax2: 'Tax 2',
252
- tax3: 'Tax 3',
253
- subtotal: 'Subtotal',
254
- total: 'Total'
255
- }
256
- ```
257
-
258
- You can also use sublabels feature to provide the document in two languages:
259
-
260
- ```ruby
261
- labels = {
262
- ...
263
- }
264
-
265
- sublabels = {
266
- name: 'Faktura',
267
- provider: 'Prodejce',
268
- purchaser: 'Kupující',
269
- tax_id: 'IČ',
270
- tax_id2: 'DIČ',
271
- payment: 'Forma úhrady',
272
- payment_by_transfer: 'Platba na následující účet:',
273
- account_number: 'Číslo účtu',
274
- issue_date: 'Datum vydání',
275
- due_date: 'Datum splatnosti',
276
- item: 'Položka',
277
- quantity: 'Počet',
278
- unit: 'MJ',
279
- price_per_item: 'Cena za položku',
280
- amount: 'Celkem bez daně',
281
- subtotal: 'Cena bez daně',
282
- tax: 'DPH 21 %',
283
- total: 'Celkem'
284
- }
285
-
286
- labels.merge!({ sublabels: sublabels })
287
-
288
- ...
289
- ```
290
-
291
- Now the document will have a little sublabels next to the original labels in Czech.
292
-
293
- ### Font
294
-
295
- To support specific characters you might need to specify a TTF font to be used:
296
-
297
- ``` ruby
298
- InvoicePrinter.print(
299
- ...
300
- font: File.expand_path('../Overpass-Regular.ttf', __FILE__)
301
- )
302
- ```
303
-
304
- We recommend you DejaVuSans and Overpass fonts.
305
-
306
- ### Background
307
-
308
- To include a background image you might need to create the file according to the size and resolution to be used (see: [examples/background.png](https://github.com/strzibny/invoice_printer/blob/master/examples/background.png)):
42
+ ## Documentation
309
43
 
310
- ``` ruby
311
- InvoicePrinter.print(
312
- ...
313
- background: File.expand_path('../background.png', __FILE__)
314
- )
315
- ```
44
+ - [Installation](./docs/INSTALLATION.md)
45
+ - [Ruby library](./docs/LIBRARY.md)
46
+ - [Server](./docs/SERVER.md)
47
+ - [Command line](./docs/COMMAND_LINE.md)
316
48
 
317
49
  ## Copyright
318
50
 
319
- Copyright 2015-2017 &copy; [Josef Strzibny](http://strzibny.name/). MIT licensed.
51
+ Copyright 2015-2018 &copy; [Josef Strzibny](http://strzibny.name/). MIT licensed.
320
52
 
321
53
  Originally extracted from and created for an open source single-entry invoicing app [InvoiceBar](https://github.com/strzibny/invoicebar).
Binary file
@@ -2,7 +2,6 @@
2
2
  $LOAD_PATH << File.expand_path('lib')
3
3
 
4
4
  require 'optparse'
5
- require 'json'
6
5
  require 'invoice_printer'
7
6
 
8
7
  def show_version
@@ -34,8 +33,6 @@ end
34
33
  options = {}
35
34
 
36
35
  parser = OptionParser.new do|opts|
37
- opts.banner = "Usage: invoice_printer [options]"
38
-
39
36
  opts.on('-l', '--labels JSON') do |json|
40
37
  options[:labels] = json
41
38
  end
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.expand_path('lib')
3
+
4
+ require 'optparse'
5
+ require 'rack/handler/puma'
6
+ require 'invoice_printer/server'
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_server [options]
17
+
18
+ Options:
19
+
20
+ -h, --host hostname to listen on (default is 0.0.0.0)
21
+ -p, --port port to listen on (default is 9393)
22
+ -w, --workers number of Puma workers (default is 2)
23
+
24
+ HELP
25
+
26
+ exit 0
27
+ end
28
+
29
+ options = {}
30
+
31
+ parser = OptionParser.new do|opts|
32
+ opts.on('-h', '--hostname ADDRESS') do |address|
33
+ options[:Host] = address
34
+ end
35
+
36
+ opts.on('-p', '--port NUMBER') do |number|
37
+ options[:Port] = number
38
+ end
39
+
40
+ opts.on('-w', '--workers NUMBER') do |number|
41
+ options[:workers] = number.to_i
42
+ end
43
+
44
+ opts.on('--debug') do
45
+ options[:debug] = true
46
+ end
47
+
48
+ opts.on('-h', '--help') do
49
+ show_help
50
+ end
51
+ end
52
+
53
+ parser.parse!
54
+
55
+ puma_options = { :Host => '0.0.0.0', :Port => 9393, :workers => 2 }.merge(options)
56
+
57
+ begin
58
+ puts 'Starting InvoicePrinter Server...'
59
+ Rack::Handler::Puma.run(InvoicePrinter::Server.freeze.app, puma_options)
60
+ rescue => e
61
+ STDERR.puts "ERROR: #{e.message}"
62
+
63
+ if options[:debug]
64
+ STDERR.puts
65
+ STDERR.puts e.backtrace
66
+ end
67
+
68
+ exit 1
69
+ end
@@ -0,0 +1,21 @@
1
+ # InvoicePrinter CLI
2
+
3
+ InvoicePrinter ships with a command line executable called `invoice_printer`.
4
+
5
+ It supports all features except it only accepts JSON as an input.
6
+
7
+ ```
8
+ $ invoice_printer --help
9
+ Usage: invoice_printer [options]
10
+
11
+ Options:
12
+
13
+ -l, --labels labels as JSON
14
+ -d, --document document as JSON
15
+ -s, --stamp path to stamp
16
+ --logo path to logotype
17
+ --font path to font
18
+ --page_size letter or a4 (letter is the default)
19
+ -f, --filename output path
20
+ -r, --render directly render PDF stream (filename option will be ignored)
21
+ ```
@@ -0,0 +1,22 @@
1
+ # Installation
2
+
3
+ ## Via RubyGems
4
+
5
+ This requires Ruby to be installed.
6
+
7
+ Then install the gem as:
8
+
9
+ $ gem install invoice_printer
10
+
11
+
12
+ ### With Bundler
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'invoice_printer'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ $ bundle
@@ -0,0 +1,242 @@
1
+ # InvoicePrinter Library
2
+
3
+ ## Usage
4
+
5
+ The simplest way how to create your invoice PDF is to create an invoice object
6
+ and pass it to printer:
7
+
8
+ ```ruby
9
+ item = InvoicePrinter::Document::Item.new(
10
+ ...
11
+ )
12
+
13
+ invoice = InvoicePrinter::Document.new(
14
+ ...
15
+ items: [item, ...]
16
+ )
17
+
18
+ InvoicePrinter.print(
19
+ document: invoice,
20
+ file_name: 'invoice.pdf'
21
+ )
22
+
23
+ # Or render PDF directly
24
+ InvoicePrinter.render(
25
+ document: invoice
26
+ )
27
+ ```
28
+
29
+ Here is an full example for creating the document object:
30
+
31
+ ```ruby
32
+ item = InvoicePrinter::Document::Item.new(
33
+ name: 'Web consultation',
34
+ quantity: nil,
35
+ unit: 'hours',
36
+ price: '$ 25',
37
+ tax: '$ 1',
38
+ amount: '$ 100'
39
+ )
40
+
41
+ invoice = InvoicePrinter::Document.new(
42
+ number: '201604030001',
43
+ provider_name: 'Business s.r.o.',
44
+ provider_tax_id: '56565656',
45
+ provider_tax_id2: '465454',
46
+ provider_street: 'Rolnicka',
47
+ provider_street_number: '1',
48
+ provider_postcode: '747 05',
49
+ provider_city: 'Opava',
50
+ provider_city_part: 'Katerinky',
51
+ provider_extra_address_line: 'Czech Republic',
52
+ purchaser_name: 'Adam',
53
+ purchaser_tax_id: '',
54
+ purchaser_tax_id2: '',
55
+ purchaser_street: 'Ostravska',
56
+ purchaser_street_number: '1',
57
+ purchaser_postcode: '747 70',
58
+ purchaser_city: 'Opava',
59
+ purchaser_city_part: '',
60
+ purchaser_extra_address_line: '',
61
+ issue_date: '19/03/3939',
62
+ due_date: '19/03/3939',
63
+ subtotal: '175',
64
+ tax: '5',
65
+ tax2: '10',
66
+ tax3: '20',
67
+ total: '$ 200',
68
+ bank_account_number: '156546546465',
69
+ account_iban: 'IBAN464545645',
70
+ account_swift: 'SWIFT5456',
71
+ items: [item],
72
+ note: 'A note...'
73
+ )
74
+ ```
75
+
76
+ ### Ruby on Rails
77
+
78
+ If you want to use InvoicePrinter for printing PDF documents directly from Rails
79
+ actions, you can:
80
+
81
+ ```ruby
82
+ # GET /invoices/1
83
+ def show
84
+ invoice = InvoicePrinter::Document.new(...)
85
+
86
+ respond_to do |format|
87
+ format.pdf {
88
+ @pdf = InvoicePrinter.render(
89
+ document: invoice
90
+ )
91
+ send_data @pdf, type: 'application/pdf', disposition: 'inline'
92
+ }
93
+ end
94
+ end
95
+ ```
96
+
97
+ ### JSON format
98
+
99
+ JSON format is supported via `from_json` method. JSON itself mimicks the original Ruby objects:
100
+
101
+ ```ruby
102
+ json = InvoicePrinter::Document.new(...).to_json
103
+ document = InvoicePrinter::Document.from_json(json)
104
+
105
+
106
+ InvoicePrinter.print(
107
+ document: document,
108
+ ...
109
+ )
110
+
111
+ ```
112
+
113
+
114
+
115
+ ## Customization
116
+
117
+ ### Page size
118
+
119
+ Both A4 and US letter is supported. Just pass `page_size` as an argument to `print` or `render` methods:
120
+
121
+ ```ruby
122
+ InvoicePrinter.print(
123
+ document: invoice,
124
+ labels: labels,
125
+ page_size: :a4,
126
+ file_name: 'invoice.pdf'
127
+ )
128
+ ```
129
+
130
+ `:letter` is the default.
131
+
132
+
133
+ ### Localization
134
+
135
+ To localize your documents you can set both global defaults or instance
136
+ overrides:
137
+
138
+ ```ruby
139
+ InvoicePrinter.labels = {
140
+ provider: 'Dodavatel'
141
+ }
142
+
143
+ labels = {
144
+ purchaser: 'Customer'
145
+ }
146
+
147
+ InvoicePrinter.print(
148
+ document: invoice,
149
+ labels: labels,
150
+ file_name: 'invoice.pdf'
151
+ )
152
+ ```
153
+
154
+ Here is the full list of labels to configure. You can paste and edit this block
155
+ to `initializers/invoice_printer.rb` if you are using Rails.
156
+
157
+ ```ruby
158
+ InvoicePrinter.labels = {
159
+ name: 'Invoice',
160
+ provider: 'Provider',
161
+ purchaser: 'Purchaser',
162
+ tax_id: 'Identification number',
163
+ tax_id2: 'Identification number',
164
+ payment: 'Payment',
165
+ payment_by_transfer: 'Payment by bank transfer on the account below:',
166
+ payment_in_cash: 'Payment in cash',
167
+ account_number: 'Account NO',
168
+ swift: 'SWIFT',
169
+ iban: 'IBAN',
170
+ issue_date: 'Issue date',
171
+ due_date: 'Due date',
172
+ item: 'Item',
173
+ quantity: 'Quantity',
174
+ unit: 'Unit',
175
+ price_per_item: 'Price per item',
176
+ amount: 'Amount',
177
+ tax: 'Tax',
178
+ tax2: 'Tax 2',
179
+ tax3: 'Tax 3',
180
+ subtotal: 'Subtotal',
181
+ total: 'Total'
182
+ }
183
+ ```
184
+
185
+ You can also use sublabels feature to provide the document in two languages:
186
+
187
+ ```ruby
188
+ labels = {
189
+ ...
190
+ }
191
+
192
+ sublabels = {
193
+ name: 'Faktura',
194
+ provider: 'Prodejce',
195
+ purchaser: 'Kupující',
196
+ tax_id: 'IČ',
197
+ tax_id2: 'DIČ',
198
+ payment: 'Forma úhrady',
199
+ payment_by_transfer: 'Platba na následující účet:',
200
+ account_number: 'Číslo účtu',
201
+ issue_date: 'Datum vydání',
202
+ due_date: 'Datum splatnosti',
203
+ item: 'Položka',
204
+ quantity: 'Počet',
205
+ unit: 'MJ',
206
+ price_per_item: 'Cena za položku',
207
+ amount: 'Celkem bez daně',
208
+ subtotal: 'Cena bez daně',
209
+ tax: 'DPH 21 %',
210
+ total: 'Celkem'
211
+ }
212
+
213
+ labels.merge!({ sublabels: sublabels })
214
+
215
+ ...
216
+ ```
217
+
218
+ Now the document will have a little sublabels next to the original labels in Czech.
219
+
220
+ ### Font
221
+
222
+ To support specific characters you might need to specify a TTF font to be used:
223
+
224
+ ``` ruby
225
+ InvoicePrinter.print(
226
+ ...
227
+ font: File.expand_path('../Overpass-Regular.ttf', __FILE__)
228
+ )
229
+ ```
230
+
231
+ We recommend you DejaVuSans and Overpass fonts.
232
+
233
+ ### Background
234
+
235
+ To include a background image you might need to create the file according to the size and resolution to be used (see: [examples/background.png](https://github.com/strzibny/invoice_printer/blob/master/examples/background.png)):
236
+
237
+ ``` ruby
238
+ InvoicePrinter.print(
239
+ ...
240
+ background: File.expand_path('../background.png', __FILE__)
241
+ )
242
+ ```
@@ -0,0 +1,96 @@
1
+ # InvoicePrinter Server
2
+
3
+ InvoicePrinter contains a built in server that can be run from a command line with `invoice_printer_server`.
4
+
5
+ Apart from this you can also manually mount the server inside of your Rack application.
6
+
7
+ ## Running the server
8
+
9
+ ### From command line
10
+
11
+ Once installed, InvoicePrinter provides `invoice_printer_server` executable that starts the Puma server:
12
+
13
+ ```bash
14
+ invoice_printer_server -h 0.0.0.0 -p 5000
15
+ ```
16
+
17
+ `-h` defines a host and `-p` defines a port. For help you can run `--help`.
18
+
19
+ By default server binds to `0.0.0.0:9393`.
20
+
21
+ ### As mountable Rack app
22
+
23
+ If you want you can always run the server from your custom program or mount it directly from a Rack app.
24
+
25
+ `InvoicePrinter::Server` is a Rack app as any other. Example:
26
+
27
+ ```ruby
28
+ require 'rack/handler/puma'
29
+ require 'invoice_printer/server'
30
+
31
+ Rack::Handler::Puma.run InvoicePrinter::Server.freeze.app
32
+ ```
33
+
34
+ ## Available API
35
+
36
+ Endpoints accept similar arguments as the corresponding methods to `InvoicePrinter`. `render` is used for directly getting the PDF output whereas `print` would accept `filename` option and save the document to that
37
+ file.
38
+
39
+ A content type is always `application/json` both for requests and responses.
40
+
41
+ ### `POST /render`
42
+
43
+ Directly render PDF data.
44
+
45
+ Options:
46
+
47
+ - `document` - JSON representation of the document
48
+ - `labels` - JSON for labels
49
+ - `stamp` - path to stamp file
50
+ - `logo` - path to logotype file
51
+ - `font` - path to font file
52
+
53
+ On success a `200` response is returned:
54
+
55
+ ```json
56
+ { "result": "ok", "data": "base64 encoded PDF document" }
57
+ ```
58
+
59
+ On error a `400` response is returned:
60
+
61
+ ```json
62
+ { "result": "error", "error": "error description" }
63
+ ```
64
+
65
+ #### Example
66
+
67
+ Example of calling the API to render a document using `curl`:
68
+
69
+ ```
70
+ $ curl -X POST http://0.0.0.0:9393/render -H "Content-Type: application/json" --data '{"document":{"number":"c. 198900000001","provider_name":"Petr Novy","provider_tax_id":"56565656","provider_tax_id2":"","provider_street":"Rolnicka","provider_street_number":"1","provider_postcode":"747 05","provider_city":"Opava","provider_city_part":"Katerinky","provider_extra_address_line":"","purchaser_name":"Adam Cerny","purchaser_tax_id":"","purchaser_tax_id2":"","purchaser_street":"Ostravska","purchaser_street_number":"1","purchaser_postcode":"747 70","purchaser_city":"Opava","purchaser_city_part":"","purchaser_extra_address_line":"","issue_date":"05/03/2016","due_date":"19/03/2016","subtotal":"Kc 10.000","tax":"Kc 2.100","tax2":"","tax3":"","total":"Kc 12.100,-","bank_account_number":"156546546465","account_iban":"IBAN464545645","account_swift":"SWIFT5456","items":[{"name":"Konzultace","quantity":"2","unit":"hod","price":"Kc 500","tax":"","tax2":"","tax3":"","amount":"Kc 1.000"},{"name":"Programovani","quantity":"10","unit":"hod","price":"Kc 900","tax":"","tax2":"","tax3":"","amount":"Kc 9.000"}],"note":"Osoba je zapsána v zivnostenském rejstríku."}}'
71
+ ```
72
+
73
+ ### `POST /print`
74
+
75
+ Print resulting document to a file.
76
+
77
+ Options:
78
+
79
+ - `document` - JSON representation of the document
80
+ - `labels` - JSON for labels
81
+ - `stamp` - path to stamp file
82
+ - `logo` - path to logotype file
83
+ - `font` - path to font file
84
+ - `filename` - path for saving the generated output PDF
85
+
86
+ On success a `200` response is returned:
87
+
88
+ ```json
89
+ { "result": "ok", "path": "/path/basically/what/was/sent/as/filepath" }
90
+ ```
91
+
92
+ On error a `400` response is returned:
93
+
94
+ ```json
95
+ { "result": "error", "error": "error description" }
96
+ ```
@@ -15,7 +15,6 @@ Gem::Specification.new do |spec|
15
15
  # Remove .pdf files as they take a lot of space
16
16
  package_files = `git ls-files -z`.split("\x0")
17
17
  .reject{ |file| file.match /.*\.pdf/ }
18
- .reject{ |file| file.match /docs\/.*/ }
19
18
 
20
19
  spec.files = package_files
21
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
@@ -24,6 +23,8 @@ Gem::Specification.new do |spec|
24
23
  spec.bindir = 'bin'
25
24
 
26
25
  spec.add_dependency 'json', '~> 2.1'
26
+ spec.add_dependency 'roda', '3.5.0'
27
+ spec.add_dependency 'puma', '~> 3.9'
27
28
  spec.add_dependency 'prawn', '2.1.0'
28
29
  spec.add_dependency 'prawn-layout', '0.8.4'
29
30
 
@@ -1,7 +1,12 @@
1
+ require 'json'
1
2
  require 'invoice_printer/version'
3
+ require 'invoice_printer/document'
2
4
  require 'invoice_printer/document/item'
3
5
  require 'invoice_printer/pdf_document'
4
6
 
7
+ # Skip warning for not specifying TTF font
8
+ Prawn::Font::AFM.hide_m17n_warning = true
9
+
5
10
  # Create PDF versions of invoices or receipts using Prawn
6
11
  #
7
12
  # Example:
@@ -1,5 +1,3 @@
1
- require 'json'
2
-
3
1
  module InvoicePrinter
4
2
  # Invoice and receipt representation
5
3
  #
@@ -1,5 +1,3 @@
1
- require 'invoice_printer/document'
2
-
3
1
  module InvoicePrinter
4
2
  class Document
5
3
  # Line items for InvoicePrinter::Document
@@ -0,0 +1,82 @@
1
+ require 'base64'
2
+ require 'roda'
3
+ require 'invoice_printer'
4
+
5
+ class InvoicePrinter::Server < Roda
6
+ route do |r|
7
+ response['Content-Type'] = 'application/json'
8
+
9
+ # Assign params
10
+ if r.content_type == 'application/json'
11
+ begin
12
+ params = JSON.parse(r.body.read, symbolize_names: true)
13
+ rescue => e
14
+ response.status = 400
15
+ response.write({ result: 'error', error: 'Invalid JSON.' }.to_json)
16
+ r.halt
17
+ end
18
+
19
+ labels = params[:labels]
20
+ document = params[:document]
21
+ stamp = params[:stamp]
22
+ logo = params[:logo]
23
+ font = params[:font]
24
+ else
25
+ response.status = 400
26
+ response.write({ result: 'error', error: 'No JSON. Did you set Content-Type to application/json?' }.to_json)
27
+ r.halt
28
+ end
29
+
30
+ # Initialize InvoicePrinter::Document from given JSON
31
+ begin
32
+ document[:items] ||= []
33
+ items = document[:items].map { |item| InvoicePrinter::Document::Item.new(**item) }
34
+ document[:items] = items
35
+
36
+ document = InvoicePrinter::Document.new(**document)
37
+ rescue => e
38
+ response.status = 400
39
+ response.write({ result: 'error', error: 'Invalid JSON document.' }.to_json)
40
+ r.halt
41
+ end
42
+
43
+ # POST /print
44
+ r.post 'print' do
45
+ filename = params[:filename] || 'document.pdf'
46
+
47
+ begin
48
+ InvoicePrinter.print(
49
+ document: document,
50
+ font: font,
51
+ stamp: stamp,
52
+ logo: logo,
53
+ file_name: filename
54
+ )
55
+
56
+ { result: 'ok', path: filename }.to_json
57
+ rescue => e
58
+ response.status = 400
59
+ response.write({ result: 'error', error: e.message }.to_json)
60
+ r.halt
61
+ end
62
+ end
63
+
64
+ # POST /render
65
+ r.post 'render' do
66
+ begin
67
+ stream = InvoicePrinter.render(
68
+ document: document,
69
+ font: font,
70
+ stamp: stamp,
71
+ logo: logo
72
+ )
73
+
74
+ { result: 'ok', data: Base64.encode64(stream) }.to_json
75
+ rescue => e
76
+ response.status = 400
77
+ response.write({ result: 'error', error: e.message }.to_json)
78
+ r.halt
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,3 +1,3 @@
1
1
  module InvoicePrinter
2
- VERSION = '1.1.0'
2
+ VERSION = '1.2.0.alpha1'
3
3
  end
@@ -0,0 +1,78 @@
1
+ require 'test_helper'
2
+ require 'invoice_printer/server'
3
+
4
+ class ApiTest < Minitest::Test
5
+ include Rack::Test::Methods
6
+ include InvoicePrinterHelpers
7
+
8
+ def app
9
+ InvoicePrinter::Server.freeze.app
10
+ end
11
+
12
+ def setup
13
+ @test_dir = File.absolute_path('./tmp/invoice_printer_api')
14
+ FileUtils.mkdir_p @test_dir
15
+ end
16
+
17
+ def teardown
18
+ FileUtils.rm_rf @test_dir if File.exists?(@test_dir)
19
+ end
20
+
21
+ # Test POST /print
22
+
23
+ def test_print_with_invalid_json
24
+ header 'Content-Type', 'application/json'
25
+ post '/print', nil
26
+
27
+ body = JSON.parse last_response.body
28
+
29
+ assert !last_response.ok?
30
+ assert_equal body, { 'result' => 'error', 'error' => 'Invalid JSON.' }
31
+ end
32
+
33
+ def test_print_with_valid_document
34
+ invoice = InvoicePrinter::Document.new(default_document_params)
35
+
36
+ json = {
37
+ 'document' => invoice.to_h,
38
+ 'filename' => "#{@test_dir}/test"
39
+ }.to_json
40
+
41
+ header 'Content-Type', 'application/json'
42
+ post '/print', json
43
+
44
+ body = JSON.parse last_response.body
45
+
46
+ assert last_response.ok?
47
+ assert_equal body, { 'result' => 'ok', 'path' => "#{@test_dir}/test" }
48
+ end
49
+
50
+ # Test POST /render
51
+
52
+ def test_render_with_invalid_json
53
+ header 'Content-Type', 'application/json'
54
+ post '/render', nil
55
+
56
+ body = JSON.parse last_response.body
57
+
58
+ assert !last_response.ok?
59
+ assert_equal body, { 'result' => 'error', 'error' => 'Invalid JSON.' }
60
+ end
61
+
62
+ def test_render_with_valid_document
63
+ invoice = InvoicePrinter::Document.new(default_document_params)
64
+ output = InvoicePrinter.render(document: invoice)
65
+
66
+ json = {
67
+ 'document' => invoice.to_h
68
+ }.to_json
69
+
70
+ header 'Content-Type', 'application/json'
71
+ post '/render', json
72
+
73
+ body = JSON.parse last_response.body
74
+
75
+ assert last_response.ok?
76
+ assert_equal body, { 'result' => 'ok', 'data' => Base64.encode64(output) }
77
+ end
78
+ end
@@ -1,4 +1,6 @@
1
+ require 'json'
1
2
  require 'pdf/inspector'
3
+ require 'rack/test'
2
4
 
3
5
  lib = File.expand_path('../../lib', __FILE__)
4
6
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
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.1.0
4
+ version: 1.2.0.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josef Strzibny
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-31 00:00:00.000000000 Z
11
+ date: 2018-03-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: roda
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 3.5.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 3.5.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: puma
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.9'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.9'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: prawn
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -85,6 +113,7 @@ email:
85
113
  - strzibny@strzibny.name
86
114
  executables:
87
115
  - invoice_printer
116
+ - invoice_printer_server
88
117
  extensions: []
89
118
  extra_rdoc_files: []
90
119
  files:
@@ -93,7 +122,13 @@ files:
93
122
  - LICENSE.txt
94
123
  - README.md
95
124
  - Rakefile
125
+ - assets/logo.png
96
126
  - bin/invoice_printer
127
+ - bin/invoice_printer_server
128
+ - docs/COMMAND_LINE.md
129
+ - docs/INSTALLATION.md
130
+ - docs/LIBRARY.md
131
+ - docs/SERVER.md
97
132
  - examples/background.png
98
133
  - examples/complex_invoice.rb
99
134
  - examples/czech_invoice.rb
@@ -110,7 +145,9 @@ files:
110
145
  - lib/invoice_printer/document.rb
111
146
  - lib/invoice_printer/document/item.rb
112
147
  - lib/invoice_printer/pdf_document.rb
148
+ - lib/invoice_printer/server.rb
113
149
  - lib/invoice_printer/version.rb
150
+ - test/api_test.rb
114
151
  - test/background_test.rb
115
152
  - test/cli_test.rb
116
153
  - test/dates_box_test.rb
@@ -139,16 +176,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
139
176
  version: '0'
140
177
  required_rubygems_version: !ruby/object:Gem::Requirement
141
178
  requirements:
142
- - - ">="
179
+ - - ">"
143
180
  - !ruby/object:Gem::Version
144
- version: '0'
181
+ version: 1.3.1
145
182
  requirements: []
146
183
  rubyforge_project:
147
- rubygems_version: 2.6.13
184
+ rubygems_version: 2.6.14
148
185
  signing_key:
149
186
  specification_version: 4
150
187
  summary: Super simple PDF invoicing in pure Ruby
151
188
  test_files:
189
+ - test/api_test.rb
152
190
  - test/background_test.rb
153
191
  - test/cli_test.rb
154
192
  - test/dates_box_test.rb