receipts 1.1.2 → 2.1.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/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
|

|
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
|
+

|
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
|