webtranslateit-payday 1.7.2 → 1.7.3
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 +4 -0
- data/Gemfile.lock +29 -24
- data/config/locales/payday.de.yml +1 -0
- data/config/locales/payday.en.yml +1 -0
- data/config/locales/payday.es.yml +1 -0
- data/config/locales/payday.fr.yml +1 -0
- data/config/locales/payday.nl.yml +1 -0
- data/config/locales/payday.zh-CN.yml +1 -0
- data/lib/generators/payday/setup/templates/migration.rb +3 -0
- data/lib/payday/invoice.rb +11 -3
- data/lib/payday/invoiceable.rb +11 -1
- data/lib/payday/pdf_renderer.rb +18 -1
- data/payday.gemspec +1 -1
- data/spec/invoice_spec.rb +35 -0
- 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: 79e897436dac18ec0146ca7c556d2a1fd9a614fdc78b3b2fe4da6cff769184a5
|
|
4
|
+
data.tar.gz: 77f740456d34e88037867ca6890110bb39014073d476fedffed124fd698a844f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3253e3a0d51b8125df8fb0ee6341c18c320af4dbec9361376437082fc101b7552f8d25619f6d5ab79f914a2745c9a25a8952acce2a7cdeddbc71feeb7032afdf
|
|
7
|
+
data.tar.gz: 6af84ba49bb0171f4c969a13707b743e400694e897bdbf27ce494b2e1d18b7570ccee60da7c90789941c5e1669e82bde3d40b44b6088c772b3064aa30dd1bd91
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 1.7.3 (2026-02-16)
|
|
4
|
+
|
|
5
|
+
* Add retention support (e.g. Spanish IRPF). Set `retention_rate` on an invoice to apply a post-tax deduction calculated as a percentage of the subtotal. The retention amount is displayed as a negative line in the PDF and subtracted from the total. Customize the label with `retention_description` or via the `payday.invoice.retention` i18n key.
|
|
6
|
+
|
|
3
7
|
## 1.7.1 (2025-12-01)
|
|
4
8
|
|
|
5
9
|
* Relax dependency on rqrcode.
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
webtranslateit-payday (1.7.
|
|
4
|
+
webtranslateit-payday (1.7.3)
|
|
5
5
|
activesupport (>= 7, < 9)
|
|
6
6
|
i18n (~> 1.12, < 2)
|
|
7
7
|
money (>= 6.16, < 8.0)
|
|
@@ -34,24 +34,24 @@ GEM
|
|
|
34
34
|
bigdecimal (3.3.1)
|
|
35
35
|
chunky_png (1.4.0)
|
|
36
36
|
coderay (1.1.3)
|
|
37
|
-
concurrent-ruby (1.3.
|
|
37
|
+
concurrent-ruby (1.3.6)
|
|
38
38
|
connection_pool (2.5.4)
|
|
39
39
|
css_parser (1.21.1)
|
|
40
40
|
addressable
|
|
41
41
|
diff-lcs (1.6.2)
|
|
42
42
|
drb (2.2.3)
|
|
43
|
-
ffi (1.17.
|
|
44
|
-
ffi (1.17.
|
|
45
|
-
ffi (1.17.
|
|
46
|
-
formatador (1.
|
|
47
|
-
|
|
43
|
+
ffi (1.17.3-arm64-darwin)
|
|
44
|
+
ffi (1.17.3-x86_64-darwin)
|
|
45
|
+
ffi (1.17.3-x86_64-linux-gnu)
|
|
46
|
+
formatador (1.2.3)
|
|
47
|
+
reline
|
|
48
|
+
guard (2.20.1)
|
|
48
49
|
formatador (>= 0.2.4)
|
|
49
50
|
listen (>= 2.7, < 4.0)
|
|
50
51
|
logger (~> 1.6)
|
|
51
52
|
lumberjack (>= 1.0.12, < 2.0)
|
|
52
53
|
nenv (~> 0.1)
|
|
53
54
|
notiffany (~> 0.0)
|
|
54
|
-
ostruct (~> 0.6)
|
|
55
55
|
pry (>= 0.13.0)
|
|
56
56
|
shellany (~> 0.0)
|
|
57
57
|
thor (>= 0.18.1)
|
|
@@ -63,29 +63,30 @@ GEM
|
|
|
63
63
|
guard-rubocop (1.5.0)
|
|
64
64
|
guard (~> 2.0)
|
|
65
65
|
rubocop (< 2.0)
|
|
66
|
-
i18n (1.14.
|
|
66
|
+
i18n (1.14.8)
|
|
67
67
|
concurrent-ruby (~> 1.0)
|
|
68
|
-
|
|
68
|
+
io-console (0.8.2)
|
|
69
|
+
json (2.18.1)
|
|
69
70
|
language_server-protocol (3.17.0.5)
|
|
70
71
|
lint_roller (1.1.0)
|
|
71
|
-
listen (3.
|
|
72
|
+
listen (3.10.0)
|
|
73
|
+
logger
|
|
72
74
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
73
75
|
rb-inotify (~> 0.9, >= 0.9.10)
|
|
74
76
|
logger (1.7.0)
|
|
75
|
-
lumberjack (1.2
|
|
77
|
+
lumberjack (1.4.2)
|
|
76
78
|
matrix (0.4.3)
|
|
77
79
|
method_source (1.1.0)
|
|
78
80
|
minitest (5.26.0)
|
|
79
|
-
money (7.0.
|
|
81
|
+
money (7.0.2)
|
|
80
82
|
bigdecimal
|
|
81
83
|
i18n (~> 1.9)
|
|
82
84
|
nenv (0.3.0)
|
|
83
85
|
notiffany (0.1.3)
|
|
84
86
|
nenv (~> 0.1)
|
|
85
87
|
shellany (~> 0.0)
|
|
86
|
-
ostruct (0.6.1)
|
|
87
88
|
parallel (1.27.0)
|
|
88
|
-
parser (3.3.10.
|
|
89
|
+
parser (3.3.10.1)
|
|
89
90
|
ast (~> 2.4.1)
|
|
90
91
|
racc
|
|
91
92
|
pdf-core (0.10.0)
|
|
@@ -100,10 +101,11 @@ GEM
|
|
|
100
101
|
rexml (>= 3.4.2, < 4)
|
|
101
102
|
prawn-table (0.2.2)
|
|
102
103
|
prawn (>= 1.3.0, < 3.0.0)
|
|
103
|
-
prism (1.
|
|
104
|
-
pry (0.
|
|
104
|
+
prism (1.9.0)
|
|
105
|
+
pry (0.16.0)
|
|
105
106
|
coderay (~> 1.1)
|
|
106
107
|
method_source (~> 1.0)
|
|
108
|
+
reline (>= 0.6.0)
|
|
107
109
|
public_suffix (6.0.2)
|
|
108
110
|
racc (1.8.1)
|
|
109
111
|
rainbow (3.1.1)
|
|
@@ -111,6 +113,8 @@ GEM
|
|
|
111
113
|
rb-inotify (0.11.1)
|
|
112
114
|
ffi (~> 1.0)
|
|
113
115
|
regexp_parser (2.11.3)
|
|
116
|
+
reline (0.6.3)
|
|
117
|
+
io-console (~> 0.5)
|
|
114
118
|
rexml (3.4.4)
|
|
115
119
|
rqrcode (3.1.1)
|
|
116
120
|
chunky_png (~> 1.0)
|
|
@@ -129,7 +133,7 @@ GEM
|
|
|
129
133
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
130
134
|
rspec-support (~> 3.13.0)
|
|
131
135
|
rspec-support (3.13.6)
|
|
132
|
-
rubocop (1.
|
|
136
|
+
rubocop (1.84.2)
|
|
133
137
|
json (~> 2.3)
|
|
134
138
|
language_server-protocol (~> 3.17.0.2)
|
|
135
139
|
lint_roller (~> 1.1.0)
|
|
@@ -137,30 +141,30 @@ GEM
|
|
|
137
141
|
parser (>= 3.3.0.2)
|
|
138
142
|
rainbow (>= 2.2.2, < 4.0)
|
|
139
143
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
140
|
-
rubocop-ast (>= 1.
|
|
144
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
141
145
|
ruby-progressbar (~> 1.7)
|
|
142
146
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
143
|
-
rubocop-ast (1.
|
|
147
|
+
rubocop-ast (1.49.0)
|
|
144
148
|
parser (>= 3.3.7.2)
|
|
145
|
-
prism (~> 1.
|
|
149
|
+
prism (~> 1.7)
|
|
146
150
|
rubocop-performance (1.26.1)
|
|
147
151
|
lint_roller (~> 1.1)
|
|
148
152
|
rubocop (>= 1.75.0, < 2.0)
|
|
149
153
|
rubocop-ast (>= 1.47.1, < 2.0)
|
|
150
|
-
rubocop-rspec (3.
|
|
154
|
+
rubocop-rspec (3.9.0)
|
|
151
155
|
lint_roller (~> 1.1)
|
|
152
156
|
rubocop (~> 1.81)
|
|
153
157
|
ruby-progressbar (1.13.0)
|
|
154
158
|
securerandom (0.4.1)
|
|
155
159
|
shellany (0.0.1)
|
|
156
|
-
thor (1.
|
|
160
|
+
thor (1.5.0)
|
|
157
161
|
ttfunk (1.8.0)
|
|
158
162
|
bigdecimal (~> 3.1)
|
|
159
163
|
tzinfo (2.0.6)
|
|
160
164
|
concurrent-ruby (~> 1.0)
|
|
161
165
|
unicode-display_width (3.2.0)
|
|
162
166
|
unicode-emoji (~> 4.1)
|
|
163
|
-
unicode-emoji (4.
|
|
167
|
+
unicode-emoji (4.2.0)
|
|
164
168
|
zeitwerk (2.7.1)
|
|
165
169
|
|
|
166
170
|
PLATFORMS
|
|
@@ -168,6 +172,7 @@ PLATFORMS
|
|
|
168
172
|
arm64-darwin-22
|
|
169
173
|
arm64-darwin-23
|
|
170
174
|
arm64-darwin-24
|
|
175
|
+
arm64-darwin-25
|
|
171
176
|
x86_64-darwin-21
|
|
172
177
|
x86_64-linux
|
|
173
178
|
|
|
@@ -4,6 +4,9 @@ class CreatePaydayTables < ActiveRecord::Migration
|
|
|
4
4
|
# invoices will work without anything but bill_to, but there are quite a few options for the fields you can save, like ship_to
|
|
5
5
|
# due_at, refunded_at, and paid_at
|
|
6
6
|
t.string :bill_to
|
|
7
|
+
t.decimal :tax_rate
|
|
8
|
+
t.decimal :shipping_rate
|
|
9
|
+
t.decimal :retention_rate
|
|
7
10
|
|
|
8
11
|
t.timestamps
|
|
9
12
|
end
|
data/lib/payday/invoice.rb
CHANGED
|
@@ -8,10 +8,10 @@ module Payday
|
|
|
8
8
|
include Payday::Invoiceable
|
|
9
9
|
|
|
10
10
|
attr_accessor :invoice_number, :bill_to, :ship_to, :notes, :line_items, :shipping_description,
|
|
11
|
-
:tax_description, :due_at, :paid_at, :refunded_at, :currency,
|
|
12
|
-
:qr_code
|
|
11
|
+
:tax_description, :retention_description, :due_at, :paid_at, :refunded_at, :currency,
|
|
12
|
+
:invoice_details, :invoice_date, :qr_code
|
|
13
13
|
|
|
14
|
-
attr_reader :tax_rate, :shipping_rate
|
|
14
|
+
attr_reader :tax_rate, :shipping_rate, :retention_rate
|
|
15
15
|
|
|
16
16
|
# rubocop:todo Metrics/PerceivedComplexity
|
|
17
17
|
# rubocop:todo Metrics/MethodLength
|
|
@@ -26,6 +26,8 @@ module Payday
|
|
|
26
26
|
self.shipping_description = options[:shipping_description] || nil
|
|
27
27
|
self.tax_rate = options[:tax_rate] || nil
|
|
28
28
|
self.tax_description = options[:tax_description] || nil
|
|
29
|
+
self.retention_rate = options[:retention_rate] || nil
|
|
30
|
+
self.retention_description = options[:retention_description] || nil
|
|
29
31
|
self.due_at = options[:due_at] || nil
|
|
30
32
|
self.paid_at = options[:paid_at] || nil
|
|
31
33
|
self.refunded_at = options[:refunded_at] || nil
|
|
@@ -50,6 +52,12 @@ module Payday
|
|
|
50
52
|
@shipping_rate = BigDecimal(value.to_s)
|
|
51
53
|
end
|
|
52
54
|
|
|
55
|
+
# Retention rate (e.g. IRPF). Applied as a percentage of the subtotal and deducted from the total.
|
|
56
|
+
def retention_rate=(value)
|
|
57
|
+
value = 0 if value.to_s.blank?
|
|
58
|
+
@retention_rate = BigDecimal(value.to_s)
|
|
59
|
+
end
|
|
60
|
+
|
|
53
61
|
# Adds a line item
|
|
54
62
|
def add_line_item(options = {})
|
|
55
63
|
line_items << Payday::LineItem.new(options)
|
data/lib/payday/invoiceable.rb
CHANGED
|
@@ -49,9 +49,19 @@ module Payday
|
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# Calculates the retention (e.g. IRPF) as a percentage of the subtotal.
|
|
53
|
+
# This is a post-tax deduction subtracted from the total.
|
|
54
|
+
def retention
|
|
55
|
+
if defined?(retention_rate)
|
|
56
|
+
subtotal * retention_rate / 100
|
|
57
|
+
else
|
|
58
|
+
0
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
52
62
|
# Calculates the total for this invoice.
|
|
53
63
|
def total
|
|
54
|
-
subtotal + tax + shipping
|
|
64
|
+
subtotal + tax + shipping - retention
|
|
55
65
|
end
|
|
56
66
|
|
|
57
67
|
def overdue?
|
data/lib/payday/pdf_renderer.rb
CHANGED
|
@@ -223,8 +223,9 @@ module Payday
|
|
|
223
223
|
end
|
|
224
224
|
# rubocop:enable Metrics/MethodLength
|
|
225
225
|
|
|
226
|
+
# rubocop:todo Metrics/PerceivedComplexity
|
|
226
227
|
# rubocop:todo Metrics/MethodLength
|
|
227
|
-
def self.totals_lines(invoice, pdf) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
|
|
228
|
+
def self.totals_lines(invoice, pdf) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
228
229
|
table_data = []
|
|
229
230
|
table_data << [
|
|
230
231
|
bold_cell(pdf, I18n.t('payday.invoice.subtotal', default: 'Subtotal:')),
|
|
@@ -255,6 +256,21 @@ module Payday
|
|
|
255
256
|
align: :right)
|
|
256
257
|
]
|
|
257
258
|
end
|
|
259
|
+
|
|
260
|
+
if invoice.retention_rate.positive?
|
|
261
|
+
retention_description = if invoice.retention_description.nil?
|
|
262
|
+
I18n.t('payday.invoice.retention', default: 'Retention:')
|
|
263
|
+
else
|
|
264
|
+
invoice.retention_description
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
table_data << [
|
|
268
|
+
bold_cell(pdf, retention_description),
|
|
269
|
+
cell(pdf, number_to_currency(-invoice.retention, invoice),
|
|
270
|
+
align: :right)
|
|
271
|
+
]
|
|
272
|
+
end
|
|
273
|
+
|
|
258
274
|
table_data << [
|
|
259
275
|
bold_cell(pdf, I18n.t('payday.invoice.total', default: 'Total:'),
|
|
260
276
|
size: 12),
|
|
@@ -268,6 +284,7 @@ module Payday
|
|
|
268
284
|
end
|
|
269
285
|
end
|
|
270
286
|
# rubocop:enable Metrics/MethodLength
|
|
287
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
271
288
|
|
|
272
289
|
def self.notes(invoice, pdf) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
|
|
273
290
|
return unless defined?(invoice.notes) && invoice.notes
|
data/payday.gemspec
CHANGED
data/spec/invoice_spec.rb
CHANGED
|
@@ -75,6 +75,41 @@ module Payday # rubocop:todo Metrics/ModuleLength
|
|
|
75
75
|
expect(i.total).to eq(BigDecimal(1243))
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
+
it 'returns zero retention when no retention_rate is set' do
|
|
79
|
+
i = described_class.new
|
|
80
|
+
i.line_items << LineItem.new(price: 100, quantity: 1, description: 'Service')
|
|
81
|
+
|
|
82
|
+
expect(i.retention).to eq(BigDecimal(0))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'calculates retention correctly as a percentage of the subtotal' do
|
|
86
|
+
i = described_class.new(retention_rate: 15.0)
|
|
87
|
+
i.line_items << LineItem.new(price: 200, quantity: 5, description: 'Service')
|
|
88
|
+
|
|
89
|
+
expect(i.retention).to eq(BigDecimal(150))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# rubocop:todo RSpec/MultipleExpectations
|
|
93
|
+
it 'calculates total with retention deducted (IRPF example: 500 + 21% VAT - 19% IRPF = 510)' do # rubocop:todo RSpec/ExampleLength, RSpec/MultipleExpectations
|
|
94
|
+
# rubocop:enable RSpec/MultipleExpectations
|
|
95
|
+
i = described_class.new(tax_rate: 21.0, retention_rate: 19.0)
|
|
96
|
+
i.line_items << LineItem.new(price: 500, quantity: 1, description: 'Consulting')
|
|
97
|
+
|
|
98
|
+
expect(i.subtotal).to eq(BigDecimal(500))
|
|
99
|
+
expect(i.tax).to eq(BigDecimal(105))
|
|
100
|
+
expect(i.retention).to eq(BigDecimal(95))
|
|
101
|
+
expect(i.total).to eq(BigDecimal(510))
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'calculates total with tax, shipping, and retention combined' do
|
|
105
|
+
i = described_class.new(tax_rate: 10.0, shipping_rate: 20, retention_rate: 5.0)
|
|
106
|
+
i.line_items << LineItem.new(price: 100, quantity: 10, description: 'Widgets')
|
|
107
|
+
|
|
108
|
+
# subtotal: 1000, tax: 100, shipping: 20, retention: 50
|
|
109
|
+
# total: 1000 + 100 + 20 - 50 = 1070
|
|
110
|
+
expect(i.total).to eq(BigDecimal(1070))
|
|
111
|
+
end
|
|
112
|
+
|
|
78
113
|
it "is overdue when it's past date and unpaid" do
|
|
79
114
|
i = described_class.new(due_at: Date.today - 1)
|
|
80
115
|
expect(i.overdue?).to be(true)
|