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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4cb98acc78809a09e4945f8d8525aee1bbafd3ed17a45234be6fb0a8836f3135
4
- data.tar.gz: b3379b263fb55f5d7fdb8ec48d66924e0f0b1110325b43e15bbd0a764fbc6154
3
+ metadata.gz: e997f24c5936a24f8deb333262cdfb74ecf989ddd229052797211579748a8f8c
4
+ data.tar.gz: af5120a7dd9edd4a7cf8e980d5090f2c20a2d1e0c32d654aa711c93b777cc429
5
5
  SHA512:
6
- metadata.gz: c0473911f660484293361d1fbfb7020efed4614c06dd89b000fc7995f90e52b995c763781f175609578fd42090923ed9969baab0d4bffb85140f887356964690
7
- data.tar.gz: 0a401edbb42eb073bf35602caaf6c2b7b762450dff19a04f2a8ee4d3ae8aca0ecf2ceab4485300a0647a2005b22116edd2cd1eba0be60a07117f5d44e5637b39
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.
@@ -13,49 +13,30 @@ module Payday
13
13
 
14
14
  attr_reader :tax_rate, :shipping_rate, :retention_rate
15
15
 
16
- # rubocop:todo Metrics/PerceivedComplexity
17
- # rubocop:todo Metrics/MethodLength
18
- # rubocop:todo Metrics/AbcSize
19
- def initialize(options = {}) # rubocop:todo Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
20
- self.invoice_number = options[:invoice_number] || nil
21
- self.bill_to = options[:bill_to] || nil
22
- self.ship_to = options[:ship_to] || nil
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
- value = 0 if value.to_s.blank?
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
- value = 0 if value.to_s.blank?
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
- value = 0 if value.to_s.blank?
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(Payday::Config.default.date_format)
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
- return @invoice.send(property) if @invoice.respond_to?(property) && @invoice.send(property)
156
-
157
- Payday::Config.default.send(property)
155
+ Payday::Config.default.value_for(@invoice, property)
158
156
  end
159
157
 
160
158
  end
@@ -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
- # rubocop:todo Layout/LineLength
16
- # If the +due_at+, +paid_at+, and +refunded_at+ methods are available, {Payday::Invoiceable} will use them to show due dates,
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
- if defined?(tax_rate)
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
- if defined?(shipping_rate)
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
- if defined?(retention_rate)
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
- # rubocop:todo Layout/LineLength
69
- defined?(due_at) && ((due_at.is_a?(Date) && due_at < Date.today) || (due_at.is_a?(Time) && due_at < Time.now)) && !paid_at
70
- # rubocop:enable Layout/LineLength
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 if defined?(invoice_details).nil?
89
+ return unless defined?(invoice_details)
96
90
 
97
- invoice_details.each do |detail|
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
@@ -4,11 +4,9 @@ module Payday
4
4
 
5
5
  # Represents a line item in an invoice.
6
6
  #
7
- # rubocop:todo Layout/LineLength
8
- # +quantity+ and +price+ are written to be pretty picky, primarily because if we're not picky about what values are set to
9
- # rubocop:enable Layout/LineLength
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
- value = 0 if value.to_s.blank?
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
- value = 0 if value.to_s.blank?
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
- value = 0 if value.to_s.blank?
47
- @predefined_amount = BigDecimal(value.to_s)
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
@@ -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(currency_for(invoice))
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'webtranslateit-payday'
5
- s.version = '2.1.0'
5
+ s.version = '2.1.1'
6
6
  s.required_ruby_version = '>= 3.2'
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ['Alan Johnson', 'Edouard Briere']
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webtranslateit-payday
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alan Johnson