receipts 1.1.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/Gemfile +5 -0
- data/README.md +197 -181
- data/Rakefile +65 -46
- data/examples/{gorails.png → images/logo.png} +0 -0
- data/examples/images/options.jpg +0 -0
- data/examples/invoice.pdf +0 -0
- data/examples/receipt.pdf +0 -0
- data/examples/statement.pdf +0 -0
- data/lib/receipts/base.rb +102 -0
- data/lib/receipts/invoice.rb +1 -87
- data/lib/receipts/receipt.rb +1 -66
- data/lib/receipts/statement.rb +1 -84
- data/lib/receipts/version.rb +1 -1
- data/lib/receipts.rb +12 -15
- data/receipts.gemspec +18 -13
- metadata +13 -86
- data/.github/FUNDING.yml +0 -12
- data/.github/workflows/ci.yml +0 -36
- data/.gitignore +0 -15
- data/.travis.yml +0 -4
- data/spec/receipts_spec.rb +0 -18
- data/spec/spec_helper.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a0b0deba0350c50dd06db780729513d9c65d020c9d625b3c61c174078738ef44
|
4
|
+
data.tar.gz: 6b508e3232031df8d6952b9c2da80d5910022b2b53ba29e54f84080c12d522da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f734ad542b0405e91370fd960ab93283bd493cdd0523900b6f97a26f91bc8a57834512c7778c551af55e1208c232020743d39b73428bafe118679439838479f0
|
7
|
+
data.tar.gz: baca857094d744fa1ff47617940a9a665df5fc7da11ebc25e4ac180b9a324b1f00f82f85cfbba09952708e596cbdbe425d73abfab4371f319e7882e3d8dac59e
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
### Unreleased
|
2
2
|
|
3
|
-
### 1.
|
3
|
+
### 2.1.0
|
4
|
+
|
5
|
+
* Add `Receipts.default_font` - @excid3
|
6
|
+
|
7
|
+
### 2.0.0
|
8
|
+
|
9
|
+
* New, consistent layouts between Receipts, Invoices, and Statements - @excid3
|
10
|
+
* PDFs can now be completely customized - @excid3
|
11
|
+
* Add line_items to Receipts - @excid3
|
12
|
+
|
13
|
+
### 1.1.2
|
4
14
|
|
5
15
|
* Update design to give more room for longer product names, addresses, etc - @excid3
|
6
16
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
![travisci](https://api.travis-ci.org/excid3/receipts.svg)
|
2
2
|
|
3
|
-
# Receipts
|
3
|
+
# Receipts Gem
|
4
4
|
|
5
|
-
Receipts for your Rails application that works with any payment provider.
|
5
|
+
Receipts, Invoices, and Statements for your Rails application that works with any payment provider. Receipts uses Prawn to generate the PDFs.
|
6
6
|
|
7
|
-
Check out the [example
|
7
|
+
Check out the [example PDFs](https://github.com/excid3/receipts/blob/master/examples/).
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
@@ -24,112 +24,111 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
## Usage
|
26
26
|
|
27
|
-
|
28
|
-
model that stores your transaction details. In this example our
|
29
|
-
application has a model named `Charge` that we will use.
|
27
|
+
To generate a Receipt, Invoice, or Statement, create an instance and provide content to render:
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
```ruby
|
30
|
+
r = Receipts::Receipt.new(
|
31
|
+
details: [
|
32
|
+
["Receipt Number", "123"],
|
33
|
+
["Date paid", Date.today],
|
34
|
+
["Payment method", "ACH super long super long super long super long super long"]
|
35
|
+
],
|
36
|
+
company: {
|
37
|
+
name: "Example, LLC",
|
38
|
+
address: "123 Fake Street\nNew York City, NY 10012",
|
39
|
+
email: "support@example.com",
|
40
|
+
logo: File.expand_path("./examples/images/logo.png")
|
41
|
+
},
|
42
|
+
recipient: [
|
43
|
+
"Customer",
|
44
|
+
"Their Address",
|
45
|
+
"City, State Zipcode",
|
46
|
+
nil,
|
47
|
+
"customer@example.org"
|
48
|
+
],
|
49
|
+
line_items: [
|
50
|
+
["<b>Item</b>", "<b>Unit Cost</b>", "<b>Quantity</b>", "<b>Amount</b>"],
|
51
|
+
["Subscription", "$19.00", "1", "$19.00"],
|
52
|
+
[nil, nil, "Subtotal", "$19.00"],
|
53
|
+
[nil, nil, "Tax", "$1.12"],
|
54
|
+
[nil, nil, "Total", "$20.12"],
|
55
|
+
[nil, nil, "<b>Amount paid</b>", "$20.12"],
|
56
|
+
[nil, nil, "Refunded on #{Date.today}", "$5.00"]
|
57
|
+
],
|
58
|
+
footer: "Thanks for your business. Please contact us if you have any questions."
|
59
|
+
)
|
60
|
+
|
61
|
+
# Returns a string of the raw PDF
|
62
|
+
r.render
|
63
|
+
|
64
|
+
# Writes the PDF to disk
|
65
|
+
r.render_file "examples/receipt.pdf"
|
66
|
+
```
|
67
|
+
|
68
|
+
### Configuration
|
34
69
|
|
35
|
-
|
36
|
-
[GoRails Episode #51](https://gorails.com/episodes/pdf-receipts)
|
70
|
+
You can specify the default font for all PDFs by defining the following in an initializer:
|
37
71
|
|
38
72
|
```ruby
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# id :integer not null, primary key
|
44
|
-
# user_id :integer
|
45
|
-
# stripe_id :string(255)
|
46
|
-
# amount :integer
|
47
|
-
# card_last4 :string(255)
|
48
|
-
# card_type :string(255)
|
49
|
-
# card_exp_month :integer
|
50
|
-
# card_exp_year :integer
|
51
|
-
# uuid :string
|
52
|
-
# created_at :datetime
|
53
|
-
# updated_at :datetime
|
54
|
-
#
|
55
|
-
|
56
|
-
class Charge < ActiveRecord::Base
|
57
|
-
belongs_to :user
|
58
|
-
validates :stripe_id, uniqueness: true
|
59
|
-
|
60
|
-
def receipt
|
61
|
-
Receipts::Receipt.new(
|
62
|
-
id: id,
|
63
|
-
subheading: "RECEIPT FOR CHARGE #%{id}",
|
64
|
-
product: "GoRails",
|
65
|
-
company: {
|
66
|
-
name: "GoRails, LLC.",
|
67
|
-
address: "123 Fake Street\nNew York City, NY 10012",
|
68
|
-
email: "support@example.com",
|
69
|
-
logo: Rails.root.join("app/assets/images/logo.png")
|
70
|
-
},
|
71
|
-
line_items: [
|
72
|
-
["Date", created_at.to_s],
|
73
|
-
["Account Billed", "#{user.name} (#{user.email})"],
|
74
|
-
["Product", "GoRails"],
|
75
|
-
["Amount", "$#{amount / 100}.00"],
|
76
|
-
["Charged to", "#{card_type} (**** **** **** #{card_last4})"],
|
77
|
-
["Transaction ID", uuid]
|
78
|
-
],
|
79
|
-
font: {
|
80
|
-
bold: Rails.root.join('app/assets/fonts/tradegothic/TradeGothic-Bold.ttf'),
|
81
|
-
normal: Rails.root.join('app/assets/fonts/tradegothic/TradeGothic.ttf'),
|
82
|
-
}
|
83
|
-
)
|
84
|
-
end
|
85
|
-
end
|
73
|
+
Receipts.default_font = {
|
74
|
+
bold: Rails.root.join('app/assets/fonts/tradegothic/TradeGothic-Bold.ttf'),
|
75
|
+
normal: Rails.root.join('app/assets/fonts/tradegothic/TradeGothic.ttf'),
|
76
|
+
}
|
86
77
|
```
|
87
78
|
|
88
|
-
Update the options for the receipt according to the data you want to
|
89
|
-
display.
|
90
79
|
|
91
|
-
|
80
|
+
### Options
|
92
81
|
|
93
|
-
|
82
|
+
You can pass the following options to generate a PDF:
|
94
83
|
|
95
|
-
|
84
|
+
* `recipient` - Array of customer details to include. Typically, this is name, address, email, VAT ID, etc.
|
96
85
|
|
97
|
-
* `
|
86
|
+
* `company` - Hash of your company details
|
98
87
|
|
99
|
-
|
88
|
+
* `name` - Company name
|
100
89
|
|
101
|
-
* `
|
90
|
+
* `address` - Company address
|
102
91
|
|
103
|
-
|
92
|
+
* `email` - Company support email address
|
104
93
|
|
105
|
-
* `
|
106
|
-
* `address` - **Required**
|
107
|
-
* `email` - **Required**
|
108
|
-
* `line_items` - **Required**
|
94
|
+
* `phone` - Company phone number - _Optional_
|
109
95
|
|
110
|
-
|
96
|
+
* `logo` - Logo to be displayed on the receipt - _Optional_
|
97
|
+
This can be either a Path, File, StringIO, or URL. Here are a few examples:
|
111
98
|
|
112
|
-
|
99
|
+
```ruby
|
100
|
+
logo: Rails.root.join("app/assets/images/logo.png")
|
101
|
+
logo: File.expand_path("./logo.png")
|
102
|
+
logo: File.open("app/assets/images/logo.png", "rb")
|
103
|
+
logo: "https://www.ruby-lang.org/images/header-ruby-logo@2x.png" # Downloaded with OpenURI
|
104
|
+
```
|
113
105
|
|
114
|
-
|
106
|
+
* `details` - Array of details about the Receipt, Invoice, Statement. Typically, this is receipt numbers, issue date, due date, status, etc.
|
115
107
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
108
|
+
* `line_items` - Array of line items to be displayed in table format.
|
109
|
+
|
110
|
+
* `footer` - String for a message at the bottom of the PDF.
|
111
|
+
|
112
|
+
* `font` - Hash of paths to font files - _Optional_
|
121
113
|
|
122
|
-
|
114
|
+
```ruby
|
115
|
+
font: {
|
116
|
+
bold: Rails.root.join('app/assets/fonts/tradegothic/TradeGothic-Bold.ttf'),
|
117
|
+
normal: Rails.root.join('app/assets/fonts/tradegothic/TradeGothic.ttf'),
|
118
|
+
}
|
119
|
+
```
|
123
120
|
|
124
|
-
|
121
|
+
Here's an example of where each option is displayed.
|
125
122
|
|
126
|
-
|
123
|
+
![options](examples/images/options.jpg)
|
127
124
|
|
128
|
-
|
125
|
+
### Formatting
|
129
126
|
|
130
|
-
|
127
|
+
`details` and `line_items` allow inline formatting with Prawn. This allows you to use HTML tags to format text: `<b>` `<i>` `<u>` `<strikethrough>` `<sub>` `<sup>` `<font>` `<color>` `<link>`
|
131
128
|
|
132
|
-
|
129
|
+
See [the Prawn docs](https://prawnpdf.org/api-docs/2.3.0/Prawn/Text.html#text-instance_method) for more information.
|
130
|
+
|
131
|
+
### Internationalization (I18n)
|
133
132
|
|
134
133
|
You can use `I18n.t` when rendering your receipts to internationalize them.
|
135
134
|
|
@@ -141,21 +140,53 @@ line_items: [
|
|
141
140
|
]
|
142
141
|
```
|
143
142
|
|
144
|
-
|
143
|
+
### Custom PDF Content
|
145
144
|
|
146
|
-
|
147
|
-
you visit it with the PDF format, it calls the `receipt` method that we
|
148
|
-
just created on the `Charge` model.
|
145
|
+
You can change the entire PDF content by instantiating an Receipts object without any options.
|
149
146
|
|
150
|
-
|
151
|
-
|
147
|
+
```ruby
|
148
|
+
receipt = Receipts::Receipt.new # creates an empty PDF
|
149
|
+
```
|
152
150
|
|
153
|
-
|
151
|
+
Each Receipts object inherits from Prawn::Document. This allows you to choose what is rendered and include any custom Prawn content you like.
|
154
152
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
153
|
+
```ruby
|
154
|
+
receipt.text("hello world")
|
155
|
+
```
|
156
|
+
|
157
|
+
You can also use the Receipts helpers in your custom PDFs at the current cursor position.
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
receipt.text("Custom header")
|
161
|
+
receipt.render_line_items([
|
162
|
+
["my line items"]
|
163
|
+
])
|
164
|
+
receipt.render_footer("This is a custom footer using the Receipts helper")
|
165
|
+
```
|
166
|
+
|
167
|
+
### Rendering PDFs
|
168
|
+
|
169
|
+
To render a PDF in memory, use `render`. This is recommended for serving PDFs in your Rails controllers.
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
receipt.render
|
173
|
+
```
|
174
|
+
|
175
|
+
To render a PDF to disk, use `render_file`:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
receipt.render_file "receipt.pdf"
|
179
|
+
```
|
180
|
+
|
181
|
+
## Rendering PDFs in Rails controller actions
|
182
|
+
|
183
|
+
Here's an example Rails controller action you can use for serving PDFs. We'll first look up the database record for the `Charge` we want to render a receipt for.
|
184
|
+
|
185
|
+
The `Charge` model has a `receipt` method that returns a `Receipts::Receipt` instance with all the receipt data filled out.
|
186
|
+
|
187
|
+
Then we can `render` to generate the PDF in memory. This produces a String with the raw PDF data in it.
|
188
|
+
|
189
|
+
Using `send_data` from Rails, we can send the PDF contents and provide the browser with a recommended filename, content type and disposition.
|
159
190
|
|
160
191
|
```ruby
|
161
192
|
class ChargesController < ApplicationController
|
@@ -164,12 +195,7 @@ class ChargesController < ApplicationController
|
|
164
195
|
|
165
196
|
def show
|
166
197
|
respond_to do |format|
|
167
|
-
format.pdf {
|
168
|
-
send_data @charge.receipt.render,
|
169
|
-
filename: "#{@charge.created_at.strftime("%Y-%m-%d")}-gorails-receipt.pdf",
|
170
|
-
type: "application/pdf",
|
171
|
-
disposition: :inline
|
172
|
-
}
|
198
|
+
format.pdf { send_pdf }
|
173
199
|
end
|
174
200
|
end
|
175
201
|
|
@@ -178,13 +204,18 @@ class ChargesController < ApplicationController
|
|
178
204
|
def set_charge
|
179
205
|
@charge = current_user.charges.find(params[:id])
|
180
206
|
end
|
207
|
+
|
208
|
+
def send_pdf
|
209
|
+
# Render the PDF in memory and send as the response
|
210
|
+
send_data @charge.receipt.render,
|
211
|
+
filename: "#{@charge.created_at.strftime("%Y-%m-%d")}-gorails-receipt.pdf",
|
212
|
+
type: "application/pdf",
|
213
|
+
disposition: :inline # or :attachment to download
|
214
|
+
end
|
181
215
|
end
|
182
216
|
```
|
183
217
|
|
184
|
-
|
185
|
-
`pdf` and you're good to go.
|
186
|
-
|
187
|
-
For example:
|
218
|
+
Then, just `link_to` to your charge with the format of `pdf`. For example:
|
188
219
|
|
189
220
|
```ruby
|
190
221
|
# config/routes.rb
|
@@ -192,93 +223,78 @@ resources :charges
|
|
192
223
|
```
|
193
224
|
|
194
225
|
```erb
|
195
|
-
<%= link_to "
|
226
|
+
<%= link_to "View Receipt", charge_path(@charge, format: :pdf) %>
|
196
227
|
```
|
197
228
|
|
198
229
|
## Invoices
|
199
230
|
|
200
|
-
Invoices follow the exact same set of steps as above
|
201
|
-
|
202
|
-
* `issue_date` - Date the invoice was issued
|
203
|
-
|
204
|
-
* `due_date` - Date the invoice payment is due
|
205
|
-
|
206
|
-
* `status` - A status for the invoice (Pending, Paid, etc)
|
207
|
-
|
208
|
-
* `bill_to` - A string or Array of lines with billing details
|
209
|
-
|
210
|
-
You can also use line_items to flexibly generate and display the table with items in it, including subtotal, taxes, and total amount.
|
231
|
+
Invoices follow the exact same set of steps as above. You'll simply want to modify the `details` to include other information for the Invoice such as the Issue Date, Due Date, etc.
|
211
232
|
|
212
233
|
```ruby
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
],
|
238
|
-
|
234
|
+
Receipts::Invoice.new(
|
235
|
+
details: [
|
236
|
+
["Invoice Number", "123"],
|
237
|
+
["Issue Date", Date.today.strftime("%B %d, %Y")],
|
238
|
+
["Due Date", Date.today.strftime("%B %d, %Y")],
|
239
|
+
["Status", "<b><color rgb='#5eba7d'>PAID</color></b>"]
|
240
|
+
],
|
241
|
+
recipient: [
|
242
|
+
"<b>Bill To</b>",
|
243
|
+
"Customer",
|
244
|
+
"Address",
|
245
|
+
"City, State Zipcode",
|
246
|
+
"customer@example.org"
|
247
|
+
],
|
248
|
+
company: {
|
249
|
+
name: "Example, LLC",
|
250
|
+
address: "123 Fake Street\nNew York City, NY 10012",
|
251
|
+
phone: "(555) 867-5309",
|
252
|
+
email: "support@example.com",
|
253
|
+
logo: File.expand_path("./examples/images/logo.png")
|
254
|
+
},
|
255
|
+
line_items: [
|
256
|
+
["<b>Item</b>", "<b>Unit Cost</b>", "<b>Quantity</b>", "<b>Amount</b>"],
|
257
|
+
["Subscription", "$19.00", "1", "$19.00"],
|
258
|
+
[nil, nil, "Subtotal", "$19.00"],
|
259
|
+
[nil, nil, "Tax Rate", "0%"],
|
260
|
+
[nil, nil, "Amount Due", "$19.00"]
|
261
|
+
]
|
262
|
+
)
|
239
263
|
```
|
240
264
|
|
241
265
|
## Statements
|
242
266
|
|
243
|
-
Statements follow the exact same set of steps as
|
244
|
-
|
245
|
-
* `issue_date` - Date the invoice was issued
|
246
|
-
|
247
|
-
* `start_date` - The start date of the statement period
|
248
|
-
|
249
|
-
* `start_date` - The end date of the statement period
|
250
|
-
|
251
|
-
* `bill_to` - A string or Array of lines with account details
|
252
|
-
|
253
|
-
You can also use line_items to flexibly generate and display the table with items in it, including subtotal, taxes, and total amount.
|
267
|
+
Statements follow the exact same set of steps as above. You'll simply want to modify the `details` to include other information for the Invoice such as the Issue Date, Start and End Dates, etc.
|
254
268
|
|
255
269
|
```ruby
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
],
|
281
|
-
|
270
|
+
Receipts::Statement.new(
|
271
|
+
details: [
|
272
|
+
["Statement Number", "123"],
|
273
|
+
["Issue Date", Date.today.strftime("%B %d, %Y")],
|
274
|
+
["Period", "#{(Date.today - 30).strftime("%B %d, %Y")} - #{Date.today.strftime("%B %d, %Y")}"]
|
275
|
+
],
|
276
|
+
recipient: [
|
277
|
+
"<b>Bill To</b>",
|
278
|
+
"Customer",
|
279
|
+
"Address",
|
280
|
+
"City, State Zipcode",
|
281
|
+
"customer@example.org"
|
282
|
+
],
|
283
|
+
company: {
|
284
|
+
name: "Example, LLC",
|
285
|
+
address: "123 Fake Street\nNew York City, NY 10012",
|
286
|
+
email: "support@example.com",
|
287
|
+
phone: "(555) 867-5309",
|
288
|
+
logo: File.expand_path("./examples/images/logo.png")
|
289
|
+
},
|
290
|
+
line_items: [
|
291
|
+
["<b>Item</b>", "<b>Unit Cost</b>", "<b>Quantity</b>", "<b>Amount</b>"],
|
292
|
+
["Subscription", "$19.00", "1", "$19.00"],
|
293
|
+
[nil, nil, "Subtotal", "$19.00"],
|
294
|
+
[nil, nil, "Tax Rate", "0%"],
|
295
|
+
[nil, nil, "Total", "$19.00"]
|
296
|
+
]
|
297
|
+
)
|
282
298
|
```
|
283
299
|
|
284
300
|
## Contributing
|