webtranslateit-payday 2.1.0 → 2.1.1
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 +6 -0
- data/lib/payday/config.rb +7 -0
- data/lib/payday/invoice.rb +17 -29
- data/lib/payday/invoice_presenter.rb +2 -4
- data/lib/payday/invoiceable.rb +18 -26
- data/lib/payday/line_item.rb +13 -11
- data/lib/payday/pdf_renderer.rb +2 -15
- data/payday.gemspec +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e997f24c5936a24f8deb333262cdfb74ecf989ddd229052797211579748a8f8c
|
|
4
|
+
data.tar.gz: af5120a7dd9edd4a7cf8e980d5090f2c20a2d1e0c32d654aa711c93b777cc429
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 73f336ccbb26a4e1ab7b98ca3f95cd407e5730d7508884c361049e1ad1120b6a09f02609f44f5edb4e492d5dff0563c3021c16fc72e67756926c50463308875e
|
|
7
|
+
data.tar.gz: ab853437c9e8d4bcdb647bde8535e0d0a5e18a9e93e5d132be4cebaccec844a5e649915123ac4216d50f832c79604905a3d7eb755b5e5dbae59cfa957d749126
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 2.1.1 (2026-11-08)
|
|
4
|
+
|
|
5
|
+
* An invoice that defines `date_format` now has its dates formatted with it, the way it could already override `currency`, `invoice_logo`, `page_size`, `company_name` and `company_details`. Every one of those is read through the new `Config#value_for(invoice, property)` (#319, #321). `Payday::Invoice` has no `date_format`, so its output is unchanged.
|
|
6
|
+
* Internal refactoring, no behaviour change: `Invoice#initialize` assigns its options from one list (#314); the invoice rate setters and the `LineItem` setters each share one decimal conversion (#315, #316); `Invoiceable#tax`, `#shipping` and `#retention` are one-liners (#315); `overdue?` reads through `Invoiceable.past?` (#317); `each_detail` is tidied (#318); and two doc comments no longer need RuboCop line-length directives (#320).
|
|
7
|
+
* The code satisfies RuboCop 1.90's `Style/DirectiveScope` cop, the linter runs on Ruby 4.0, and guard and pry are gone from the development setup.
|
|
8
|
+
|
|
3
9
|
## 2.1.0 (2026-08-27)
|
|
4
10
|
|
|
5
11
|
* Add `Payday::Appendix`, for compiling pages of your own onto the end of an invoice. `render_pdf`, `render_pdf_to_file`, `PdfRenderer.render` and `PdfRenderer.render_to_file` all take an `appendix:` keyword; every signature stays backward compatible and an invoice rendered without one is byte-for-byte what it was.
|
data/lib/payday/config.rb
CHANGED
|
@@ -18,6 +18,13 @@ module Payday
|
|
|
18
18
|
@default ||= new
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
# The invoice's own value for +property+ when it defines one and it is set, otherwise this
|
|
22
|
+
# config's. Every setting an invoice can override is read through here.
|
|
23
|
+
def value_for(invoice, property)
|
|
24
|
+
value = invoice.public_send(property) if invoice.respond_to?(property)
|
|
25
|
+
value || public_send(property)
|
|
26
|
+
end
|
|
27
|
+
|
|
21
28
|
# Internal: Resets a config object back to its default settings.
|
|
22
29
|
#
|
|
23
30
|
# Primarily intended for use in our tests.
|
data/lib/payday/invoice.rb
CHANGED
|
@@ -13,49 +13,30 @@ module Payday
|
|
|
13
13
|
|
|
14
14
|
attr_reader :tax_rate, :shipping_rate, :retention_rate
|
|
15
15
|
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
self.notes = options[:notes] || nil
|
|
16
|
+
# Options assigned as given; a missing one is nil, which the rate setters turn into 0.
|
|
17
|
+
OPTIONS = %i[invoice_number bill_to ship_to notes shipping_rate shipping_description tax_rate
|
|
18
|
+
tax_description retention_rate retention_description due_at paid_at refunded_at
|
|
19
|
+
currency invoice_date qr_code].freeze
|
|
20
|
+
|
|
21
|
+
def initialize(options = {})
|
|
22
|
+
OPTIONS.each { |name| public_send(:"#{name}=", options[name]) }
|
|
24
23
|
self.line_items = options[:line_items] || []
|
|
25
|
-
self.shipping_rate = options[:shipping_rate] || nil
|
|
26
|
-
self.shipping_description = options[:shipping_description] || nil
|
|
27
|
-
self.tax_rate = options[:tax_rate] || nil
|
|
28
|
-
self.tax_description = options[:tax_description] || nil
|
|
29
|
-
self.retention_rate = options[:retention_rate] || nil
|
|
30
|
-
self.retention_description = options[:retention_description] || nil
|
|
31
|
-
self.due_at = options[:due_at] || nil
|
|
32
|
-
self.paid_at = options[:paid_at] || nil
|
|
33
|
-
self.refunded_at = options[:refunded_at] || nil
|
|
34
|
-
self.currency = options[:currency] || nil
|
|
35
24
|
self.invoice_details = options[:invoice_details] || []
|
|
36
|
-
self.invoice_date = options[:invoice_date] || nil
|
|
37
|
-
self.qr_code = options[:qr_code] || nil
|
|
38
25
|
end
|
|
39
|
-
# rubocop:enable Metrics/AbcSize
|
|
40
|
-
# rubocop:enable Metrics/MethodLength
|
|
41
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
42
26
|
|
|
43
27
|
# The tax rate that we're applying, as a BigDecimal
|
|
44
28
|
def tax_rate=(value)
|
|
45
|
-
|
|
46
|
-
@tax_rate = BigDecimal(value.to_s)
|
|
29
|
+
@tax_rate = to_rate(value)
|
|
47
30
|
end
|
|
48
31
|
|
|
49
32
|
# Shipping rate
|
|
50
33
|
def shipping_rate=(value)
|
|
51
|
-
|
|
52
|
-
@shipping_rate = BigDecimal(value.to_s)
|
|
34
|
+
@shipping_rate = to_rate(value)
|
|
53
35
|
end
|
|
54
36
|
|
|
55
37
|
# Retention rate (e.g. IRPF). Applied as a percentage of the subtotal and deducted from the total.
|
|
56
38
|
def retention_rate=(value)
|
|
57
|
-
|
|
58
|
-
@retention_rate = BigDecimal(value.to_s)
|
|
39
|
+
@retention_rate = to_rate(value)
|
|
59
40
|
end
|
|
60
41
|
|
|
61
42
|
# Adds a line item
|
|
@@ -63,6 +44,13 @@ module Payday
|
|
|
63
44
|
line_items << Payday::LineItem.new(options)
|
|
64
45
|
end
|
|
65
46
|
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# A blank rate (nil, "", " ") is 0.
|
|
50
|
+
def to_rate(value)
|
|
51
|
+
BigDecimal(value.to_s.presence || 0)
|
|
52
|
+
end
|
|
53
|
+
|
|
66
54
|
end
|
|
67
55
|
|
|
68
56
|
end
|
|
@@ -144,7 +144,7 @@ module Payday
|
|
|
144
144
|
def date(value)
|
|
145
145
|
return value.to_s unless value.is_a?(Date) || value.is_a?(Time)
|
|
146
146
|
|
|
147
|
-
value.strftime(
|
|
147
|
+
value.strftime(setting(:date_format))
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
def t(key, default)
|
|
@@ -152,9 +152,7 @@ module Payday
|
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
def setting(property)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
Payday::Config.default.send(property)
|
|
155
|
+
Payday::Config.default.value_for(@invoice, property)
|
|
158
156
|
end
|
|
159
157
|
|
|
160
158
|
end
|
data/lib/payday/invoiceable.rb
CHANGED
|
@@ -12,10 +12,8 @@
|
|
|
12
12
|
# when generating an invoice. We include a simple tax method that calculates tax, but it's probably wiser
|
|
13
13
|
# to override this in your class (our calculated tax won't be stored to a database by default, for example).
|
|
14
14
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# rubocop:enable Layout/LineLength
|
|
18
|
-
# paid dates, and refunded dates, as well as stamps showing if the invoice is paid or due.
|
|
15
|
+
# If the +due_at+, +paid_at+, and +refunded_at+ methods are available, {Payday::Invoiceable} will use them
|
|
16
|
+
# to show due dates, paid dates, and refunded dates, as well as stamps showing if the invoice is paid or due.
|
|
19
17
|
module Payday
|
|
20
18
|
|
|
21
19
|
module Invoiceable
|
|
@@ -32,31 +30,19 @@ module Payday
|
|
|
32
30
|
|
|
33
31
|
# The tax for this invoice, as a BigDecimal
|
|
34
32
|
def tax
|
|
35
|
-
|
|
36
|
-
subtotal * tax_rate / 100
|
|
37
|
-
else
|
|
38
|
-
0
|
|
39
|
-
end
|
|
33
|
+
defined?(tax_rate) ? subtotal * tax_rate / 100 : 0
|
|
40
34
|
end
|
|
41
35
|
|
|
42
36
|
# TODO: Add a per weight unit shipping cost
|
|
43
37
|
# Calculates the shipping
|
|
44
38
|
def shipping
|
|
45
|
-
|
|
46
|
-
shipping_rate
|
|
47
|
-
else
|
|
48
|
-
0
|
|
49
|
-
end
|
|
39
|
+
defined?(shipping_rate) ? shipping_rate : 0
|
|
50
40
|
end
|
|
51
41
|
|
|
52
42
|
# Calculates the retention (e.g. IRPF) as a percentage of the subtotal.
|
|
53
43
|
# This is a post-tax deduction subtracted from the total.
|
|
54
44
|
def retention
|
|
55
|
-
|
|
56
|
-
subtotal * retention_rate / 100
|
|
57
|
-
else
|
|
58
|
-
0
|
|
59
|
-
end
|
|
45
|
+
defined?(retention_rate) ? subtotal * retention_rate / 100 : 0
|
|
60
46
|
end
|
|
61
47
|
|
|
62
48
|
# Calculates the total for this invoice.
|
|
@@ -65,9 +51,17 @@ module Payday
|
|
|
65
51
|
end
|
|
66
52
|
|
|
67
53
|
def overdue?
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
54
|
+
defined?(due_at) && Invoiceable.past?(due_at) && !paid_at
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Whether a Date is before today or a Time before now. A module function rather than a
|
|
58
|
+
# private method, so it does not land in the classes that include Invoiceable.
|
|
59
|
+
def self.past?(moment)
|
|
60
|
+
case moment
|
|
61
|
+
when Date then moment < Date.today
|
|
62
|
+
when Time then moment < Time.now
|
|
63
|
+
else false
|
|
64
|
+
end
|
|
71
65
|
end
|
|
72
66
|
|
|
73
67
|
def refunded?
|
|
@@ -92,11 +86,9 @@ module Payday
|
|
|
92
86
|
# Iterates through the details on this invoiceable. The block given should accept
|
|
93
87
|
# two parameters, the detail name and the actual detail value.
|
|
94
88
|
def each_detail
|
|
95
|
-
return
|
|
89
|
+
return unless defined?(invoice_details)
|
|
96
90
|
|
|
97
|
-
invoice_details.each
|
|
98
|
-
yield(detail[0], detail[1])
|
|
99
|
-
end
|
|
91
|
+
invoice_details.each { |detail| yield detail[0], detail[1] }
|
|
100
92
|
end
|
|
101
93
|
|
|
102
94
|
end
|
data/lib/payday/line_item.rb
CHANGED
|
@@ -4,11 +4,9 @@ module Payday
|
|
|
4
4
|
|
|
5
5
|
# Represents a line item in an invoice.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# them your invoice math could get pretty messed up. It's recommended that both values be set to +BigDecimal+ values.
|
|
11
|
-
# Otherwise, we'll do our best to convert the set values to a +BigDecimal+.
|
|
7
|
+
# +quantity+ and +price+ are written to be pretty picky, primarily because if we're not picky about what
|
|
8
|
+
# values are set to them your invoice math could get pretty messed up. It's recommended that both values be
|
|
9
|
+
# set to +BigDecimal+ values. Otherwise, we'll do our best to convert the set values to a +BigDecimal+.
|
|
12
10
|
class LineItem
|
|
13
11
|
|
|
14
12
|
include LineItemable
|
|
@@ -31,20 +29,24 @@ module Payday
|
|
|
31
29
|
|
|
32
30
|
# Sets the quantity of this {LineItem}
|
|
33
31
|
def quantity=(value)
|
|
34
|
-
|
|
35
|
-
@quantity = BigDecimal(value.to_s)
|
|
32
|
+
@quantity = to_decimal(value)
|
|
36
33
|
end
|
|
37
34
|
|
|
38
35
|
# Sets the price for this {LineItem}
|
|
39
36
|
def price=(value)
|
|
40
|
-
|
|
41
|
-
@price = BigDecimal(value.to_s)
|
|
37
|
+
@price = to_decimal(value)
|
|
42
38
|
end
|
|
43
39
|
|
|
44
40
|
# Sets the predefined_amount for this {LineItem}
|
|
45
41
|
def predefined_amount=(value)
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
@predefined_amount = to_decimal(value)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# A blank value (nil, "", " ") is 0.
|
|
48
|
+
def to_decimal(value)
|
|
49
|
+
BigDecimal(value.to_s.presence || 0)
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
end
|
data/lib/payday/pdf_renderer.rb
CHANGED
|
@@ -27,19 +27,12 @@ module Payday
|
|
|
27
27
|
def self.number_to_currency(number, invoice)
|
|
28
28
|
Money.locale_backend = :currency
|
|
29
29
|
Money.rounding_mode = BigDecimal::ROUND_HALF_UP
|
|
30
|
-
currency = Money::Currency.wrap(
|
|
30
|
+
currency = Money::Currency.wrap(Payday::Config.default.value_for(invoice, :currency))
|
|
31
31
|
number *= currency.subunit_to_unit
|
|
32
32
|
number = number.round unless Money.default_infinite_precision
|
|
33
33
|
Money.new(number, currency).format
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def self.currency_for(invoice)
|
|
37
|
-
return invoice.currency if invoice.respond_to?(:currency) && invoice.currency
|
|
38
|
-
|
|
39
|
-
Payday::Config.default.currency
|
|
40
|
-
end
|
|
41
|
-
private_class_method :currency_for
|
|
42
|
-
|
|
43
36
|
def initialize(invoice, appendix: nil)
|
|
44
37
|
@invoice = invoice
|
|
45
38
|
@appendix = Appendix.wrap(appendix)
|
|
@@ -95,7 +88,7 @@ module Payday
|
|
|
95
88
|
|
|
96
89
|
# Config#invoice_logo is either a path or a {filename:, size: "WxH"} Hash.
|
|
97
90
|
def logo
|
|
98
|
-
setting = invoice_logo
|
|
91
|
+
setting = Payday::Config.default.value_for(@invoice, :invoice_logo)
|
|
99
92
|
return nil if setting.nil?
|
|
100
93
|
|
|
101
94
|
path, width, height = logo_parts(setting)
|
|
@@ -104,12 +97,6 @@ module Payday
|
|
|
104
97
|
{name: name, width: width, height: height}
|
|
105
98
|
end
|
|
106
99
|
|
|
107
|
-
def invoice_logo
|
|
108
|
-
return @invoice.invoice_logo if @invoice.respond_to?(:invoice_logo) && @invoice.invoice_logo
|
|
109
|
-
|
|
110
|
-
Payday::Config.default.invoice_logo
|
|
111
|
-
end
|
|
112
|
-
|
|
113
100
|
def logo_parts(setting)
|
|
114
101
|
return [setting, nil, nil] unless setting.is_a?(Hash)
|
|
115
102
|
|
data/payday.gemspec
CHANGED